AmigaChromeA new chapter for the Amiga

API docs / Autodocs / acahi.doc

API · Autodoc

ACAHI sound board autodoc

The ACAHI sound board: registers, the stream, the voices, the timer, interrupts

acahi/--background--

   PURPOSE
        ACAHI is AmigaChrome's sound board for AHI. Its driver,
        acaudio.audio, gives AHI somewhere to play that is not Paula: the
        board's sound goes to the PC's speakers through Cradle's mixer,
        beside Paula's, which is unchanged. This file documents the
        board's registers, protocol acahi-v1. Constants are in
        common/protocol/acahi.h; the driver is documented in acaudio.doc.

   BOARD
        Zorro II, 64 KB of registers, no ROM. Manufacturer Dalsin
        (0xDA15, 55829), product 4; drivers should also look for
        manufacturer 2011, product 4. The board is the last one in the
        AutoConfig chain.

        All registers are 32-bit, big-endian, at the offsets below from
        the board's base. Write them with longword moves only; byte and
        word writes are ignored. Reads of any width work. Unknown
        registers read 0 and ignore writes.

            0x0000  ID            reads 0x41434148 ("ACAH")
            0x0004  VERSION       1
            0x0008  CAPS          ACAHI_CAP_* (PLAYBACK, S16, STEREO,
                                  IRQ, QUEUE, LATENCY, S32, VOICES,
                                  TIMER, LEVELS)
            0x000c  STATUS        HOST 1, STREAM 2, VOICES 4
            0x0010  CONTROL       STREAM 1, VOICES 2; RESET 0x80000000
            0x0014  RATE          the stream's frames a second
            0x0018  FORMAT        the stream's sample type
            0x001c  OUTRATE       44100
            0x0020  PB_ADDR       stream buffer: address
            0x0024  PB_FRAMES     stream buffer: frames
            0x0028  PB_SEQ        stream buffer: sequence number
            0x002c  PB_SUBMIT     write: queue it; read: the result
            0x0030  PB_DONE       the last sequence number played out
            0x0034  PB_QUEUED     stream frames not yet played
            0x0038  PB_FREE       buffers that can still be submitted
            0x003c  LATENCY       the queue in output frames
            0x0040  INTREQ        DONE 1, VOICE 2, TIMER 4, UNDERRUN 8
            0x0044  INTENA        the INTREQ bits that raise INT2
            0x0048  INTACK        write 1 bits to clear INTREQ's
            0x004c  UNDERRUNS     times the running stream ran dry
            0x0050  ERRORS        refused submits, sounds and commands
            0x0054  FRAME         output frames since reset
            0x0060  TIMER_FREQ    ticks a second, 16.16; 0 off
            0x0064  TIMER_TICKS   ticks since TIMER_FREQ was written
            0x0070  VOICES        32
            0x0074  VOICE_EVENTS  a bit a voice; write 1 bits to clear
            0x0078  MASTER        the voices' gain, 16.16
            0x0080  SND_ID        sound slot, 0-1023
            0x0084  SND_TYPE      AHI sample type
            0x0088  SND_ADDR      where its frames are
            0x008c  SND_LEN       how many frames
            0x0090  SND_CMD       write: LOAD 1, UNLOAD 2; read: result
            0x00a0  LEVELS        the machine's volume, Paula's and AHI's
                                  levels, mute
            0x00a4  LEVEL_SEQ     counts changes to LEVELS
            0x0100  voice 0's block; voice n at 0x100 + n * 0x20

   TWO SOURCES
        The board plays two things, summed into one 44.1 kHz stereo
        output:

        The STREAM is sound AHI has already mixed. The driver submits
        buffers; the board copies each one in at once, converts it to
        16-bit stereo and plays it at its own rate, resampled. A buffer
        is DONE when its last frame has played.

        The VOICES are 32 channels the board mixes itself, from sounds
        that stay in Amiga RAM. The driver says which sound, which part,
        how fast and how loud, with AHI's rules for what happens when.
        The 68k mixes nothing.

   CLOCK
        Everything runs on the machine's audio clock, which follows the
        CPU's emulated clock at 44,100 frames an emulated second. When
        the Amiga is paused or slowed, its sound is too; in hyper pace a
        stream buffer still takes its playing time to become DONE.
        FRAME reads the clock.

   OUTPUT
        On AmigaChrome's native runtime the output goes to a ring beside
        Paula's (PORT.ahi beside PORT.aud), frame for frame with it.
        STATUS bit HOST says that ring exists; without it the board
        works but nobody hears it.

   INTERRUPTS
        The board holds INT2 (PORTS) while INTREQ & INTENA is not 0.
        Share the line: an interrupt server should return 0 so that the
        servers after it run too. See ACAHI_REG_INTREQ.

   SEE ALSO
        acaudio.doc, common/protocol/acahi.h,
        docs/architecture/ACAHI_MODERN_AUDIO_SUBSYSTEM_SPEC.md

acahi/ACAHI_REG_CONTROL

   REGISTER
        0x0010 CONTROL, read and write.

   FUNCTION
        Starts and stops the two sources, and resets the board.

            ACAHI_CTL_STREAM   1           play the stream's queue
            ACAHI_CTL_VOICES   2           play the voices
            ACAHI_CTL_RESET    0x80000000  a command, never read back

        Write the bits wanted; a bit written 0 stops its source where it
        is. Stopping the stream keeps its queue; starting it again goes
        on from the same frame.

        RESET stops both sources, drops the stream's queue and every
        sound, turns the timer off, clears INTREQ, INTENA, VOICE_EVENTS,
        UNDERRUNS and ERRORS, and sets MASTER to 0x10000. Voices go
        silent. RATE goes back to 44100 and FORMAT to 3 (AHIST_S16S);
        each voice's staged values to silence, full volume and the
        centre; FRAME to 0. The board stays where AutoConfig placed it.

   STATUS
        0x000c STATUS, read only, says what is happening:

            ACAHI_ST_HOST      1   the output reaches the PC
            ACAHI_ST_STREAM    2   the stream is playing
            ACAHI_ST_VOICES    4   the voices are playing

   NOTES
        The machine's own reset (RESET instruction, reboot) resets the
        board too.

acahi/ACAHI_REG_PB_SUBMIT

   REGISTER
        0x002c PB_SUBMIT. Write: queue a stream buffer. Read: the result
        of the last write.

   FUNCTION
        Copies a buffer of frames from Amiga RAM into the stream's
        queue. Set these first:

            RATE       0x0014  4000 to 96000 frames a second
            FORMAT     0x0018  AHI's sample type:
                                1  AHIST_M16S  16-bit mono
                                3  AHIST_S16S  16-bit stereo
                                8  AHIST_M32S  32-bit mono
                               10  AHIST_S32S  32-bit stereo
            PB_ADDR    0x0020  the buffer, in Chip or Fast RAM
            PB_FRAMES  0x0024  1 to 65536
            PB_SEQ     0x0028  any number; PB_DONE reports it later

        then write anything to PB_SUBMIT. The copy is made during the
        write, so the buffer can be mixed into again at once. The board
        plays 32-bit samples' top 16 bits.

        Up to 8 buffers can be queued. RATE and FORMAT are those of the
        buffer when it is submitted; keep them the same while the stream
        plays.

   RESULT
        Read PB_SUBMIT after the write:

            ACAHI_PB_OK       0  queued
            ACAHI_PB_FULL     1  8 buffers queued already
            ACAHI_PB_BADADDR  2  the buffer is not all in Amiga RAM
            ACAHI_PB_BADLEN   3  PB_FRAMES is 0 or above 65536
            ACAHI_PB_BADFMT   4  RATE or FORMAT is not one above

        A refused buffer changes nothing else and adds one to ERRORS.

   SEE ALSO
        ACAHI_REG_PB_DONE

acahi/ACAHI_REG_PB_DONE

   REGISTER
        0x0030 PB_DONE, read only. And the queue's other registers:

            0x0034  PB_QUEUED  stream frames queued, not yet played
            0x0038  PB_FREE    buffers that can still be submitted, 0-8
            0x003c  LATENCY    PB_QUEUED in output (44.1 kHz) frames
            0x004c  UNDERRUNS  times the running stream ran dry

   FUNCTION
        PB_DONE is the PB_SEQ of the last buffer whose last frame has
        played. When one or more buffers finish, the board raises
        ACAHI_INT_DONE.

        A driver keeps the stream fed by numbering its buffers 1, 2, 3
        and so on, and submitting another while (sequence - PB_DONE) is
        below the depth it wants. Three buffers of a fiftieth of a
        second each are enough on AmigaChrome.

        When the stream is playing and the queue runs dry, the board
        plays silence, adds one to UNDERRUNS and raises
        ACAHI_INT_UNDERRUN, once until the next submit.

   NOTES
        Submit before setting ACAHI_CTL_STREAM, so the first frames are
        there when it starts: starting with an empty queue is not counted
        as an underrun.

acahi/ACAHI_REG_INTREQ

   REGISTER
        0x0040 INTREQ, read only; 0x0044 INTENA, read and write;
        0x0048 INTACK, write only.

   FUNCTION
        INTREQ holds the board's interrupt requests:

            ACAHI_INT_DONE      1  a stream buffer played to its end
            ACAHI_INT_VOICE     2  VOICE_EVENTS has a bit set
            ACAHI_INT_TIMER     4  the timer ticked
            ACAHI_INT_UNDERRUN  8  the running stream ran dry

        The board holds INT2 while INTREQ & INTENA is not 0. Write the
        bits wanted to INTENA. Write 1 bits to INTACK to clear them from
        INTREQ.

        An interrupt server reads INTREQ, returns 0 at once if
        (INTREQ & INTENA) is 0 (another board's interrupt), and
        otherwise acknowledges what it read before acting on it:

            bits = INTREQ & INTENA
            if (!bits) return 0
            INTACK = bits
            if (bits & ACAHI_INT_VOICE) {
                events = VOICE_EVENTS
                VOICE_EVENTS = events          (clears those bits)
                ...
            }
            return 0

   NOTES
        Requests raised while INTENA is 0 stay in INTREQ, and raise INT2
        when INTENA is written again. acaudio.audio's AHIsub_Disable and
        AHIsub_Enable work this way.

acahi/ACAHI_REG_TIMER_FREQ

   REGISTER
        0x0060 TIMER_FREQ, read and write; 0x0064 TIMER_TICKS, read only.

   FUNCTION
        A timer on the audio clock, for AHI's PlayerFunc when the board
        mixes the voices. TIMER_FREQ is ticks a second, 16.16 fixed
        point (AHI's Fixed); 50 << 16 is 50 Hz. 0 turns it off.

        Writing TIMER_FREQ sets TIMER_TICKS to 0 and starts a new
        period. Each tick adds one to TIMER_TICKS and raises
        ACAHI_INT_TIMER. If the interrupt has waited (INTENA off for a
        while), TIMER_TICKS tells how many ticks went by.

        The fastest the timer runs is a tick every 11 output frames,
        about 4 kHz.

   NOTES
        The timer runs whether the voices play or not.

acahi/ACAHI_REG_SND_CMD

   REGISTER
        0x0090 SND_CMD. Write: load or unload a sound. Read: the result
        of the last sound or voice command.

   FUNCTION
        A sound is a run of frames in Amiga RAM that the voices can
        play. The board keeps where it is, not a copy, so a program can
        change the frames while they play (AHI's dynamic sounds). Set
        these first:

            SND_ID    0x0080  the slot, 0 to 1023 (AHI's sound number)
            SND_TYPE  0x0084  AHI's sample type:
                               0  AHIST_M8S   8-bit mono
                               1  AHIST_M16S  16-bit mono
                               2  AHIST_S8S   8-bit stereo
                               3  AHIST_S16S  16-bit stereo
                               8  AHIST_M32S  32-bit mono
                              10  AHIST_S32S  32-bit stereo
            SND_ADDR  0x0088  the first frame
            SND_LEN   0x008c  frames

        then write ACAHI_SND_LOAD (1) to SND_CMD. Loading over a slot in
        use replaces it.

        SND_ADDR 0 with SND_LEN 0xFFFFFFFF is AHI's "all of memory"
        sound: a voice's OFFSET is then the frame's address divided by
        the frame size, and each segment is checked as it starts; one
        that is not all RAM leaves the voice silent and adds one to
        ERRORS. ACAHI_SND_UNLOAD (2) empties SND_ID's slot, and
        silences every voice playing it or waiting to.

   RESULT
            ACAHI_RES_OK        0
            ACAHI_RES_BADSLOT   1  SND_ID above 1023
            ACAHI_RES_BADTYPE   2  SND_TYPE not one above
            ACAHI_RES_BADADDR   3  SND_LEN is 0, or the frames are not
                                   all in Amiga RAM

        A refused command changes nothing and adds one to ERRORS.

   SEE ALSO
        ACAHI_V_CMD

acahi/ACAHI_V_CMD

   REGISTER
        Voice n's block is at 0x0100 + n * 0x20, n from 0 to 31:

            +0x00  SOUND   staged: a slot, or 0xFFFF for silence
            +0x04  OFFSET  staged: the first frame
            +0x08  LENGTH  staged: frames, signed
            +0x0c  FREQ    staged: frames a second
            +0x10  VOL     staged: 16.16, signed
            +0x14  PAN     staged: 16.16
            +0x18  CMD     write: apply what is staged; read: bit 0
                           playing
            +0x1c  POS     read: the frame of the sound playing now

   FUNCTION
        A voice plays a segment of a sound over and over: OFFSET is its
        first frame and LENGTH its frames. A LENGTH of 0 is the rest of
        the sound; below 0, it plays backwards from OFFSET. FREQ is
        frames a second (0xFFFFFFFF: the output rate, 44100); 0 pauses
        the voice where it is, silent and with no events, until another
        FREQ. VOL is 16.16 (0x10000 full, below 0 inverted, at most
        4.0). PAN is 0 left, 0x8000 centre, 0x10000 right, and a
        negative PAN (AHI's surround) plays as its absolute value; for a
        stereo sound it is a balance.

        Write the values, then write CMD with what to apply:

            ACAHI_VC_SOUND  1      SOUND, OFFSET and LENGTH
            ACAHI_VC_FREQ   2      FREQ
            ACAHI_VC_VOL    4      VOL and PAN
            ACAHI_VC_IMM    0x100  now

        These are AHI's rules. With ACAHI_VC_IMM the change is heard at
        once; a new sound starts from its first frame. Without it, the
        change waits until the segment playing reaches its end, and the
        next segment starts with it. A voice that is silent takes every
        change at once.

        When a segment ends the next one starts on the same frame, with
        nothing lost, and the board sets the voice's bit in VOICE_EVENTS
        (0x0074) and raises ACAHI_INT_VOICE. This is AHI's SoundFunc:
        the moment to set the sound after next. With nothing new set,
        the same segment plays again.

        SOUND 0xFFFF (AHI_NOSOUND) with ACAHI_VC_IMM silences the voice
        now; without, at the end of the segment playing.

        The voices are mixed with linear interpolation, then scaled by
        MASTER (0x0078, 16.16, 0x10000 unity, at most 16.0). AHI's
        drivers set it to the master volume times 2 / channels when
        there are more than two channels.

   RESULT
        Read SND_CMD (0x0090) after the write:

            ACAHI_RES_OK        0
            ACAHI_RES_BADSLOT   1  SOUND is not a loaded slot
            ACAHI_RES_BADRANGE  4  OFFSET or LENGTH reach outside it

        A refused command changes nothing, not even the frequency or
        volume written with it, and adds one to ERRORS.

   NOTES
        Voices play only while CONTROL has ACAHI_CTL_VOICES. Clearing it
        pauses them where they are.

   SEE ALSO
        ACAHI_REG_SND_CMD, ACAHI_REG_INTREQ, acaudio.doc

acahi/ACAHI_REG_LEVELS

   REGISTER
        0x00a0 LEVELS, read and write.
        0x00a4 LEVEL_SEQ, read.

   FUNCTION
        The machine's own sound levels, as the PC's mixer applies them:
        the volume of everything the machine plays, Paula's level, AHI's
        level (this board's sound) and mute. Sound prefs and the menu
        bar's speaker set them; so does the AmigaChrome mixer page on the
        PC, and either side sees the other's change at once. (Sound prefs
        and the speaker are designed, not built yet; ACAHIInfo shows the
        levels.)

            bits 0-7    volume, percent (0-100)
            bits 8-15   Paula's level, percent
            bits 16-23  AHI's level, percent
            bit 24      ACAHI_LEVEL_MUTE: nothing is heard

        The PC plays Paula's sound at volume x Paula's level and the
        board's at volume x AHI's level, each after the mixer page's own
        slider for it. Percentages above 100 are taken as 100; other bits
        are ignored. A write sets all four, so read, change and write.

        LEVEL_SEQ counts changes from either side. A program showing the
        levels (the menu bar's speaker) can read it now and then and read
        LEVELS again when it has moved.

        The levels start at ACAHI_LEVELS_DEFAULT (100%, 100%, 100%, not
        muted) when the instance starts, and a reset keeps them, as it
        would a volume knob. Sound prefs will set the saved ones at boot.

   NOTES
        On AmigaChrome's native runtime the two words are bytes 24-31 of
        Paula's ring header (PORT.aud): LEVELS with bit 31 set, then
        LEVEL_SEQ. A ring with 0 there has no levels (an older runtime).

        A real Amiga has no ACAHI board and no such register: there the
        volume is AHI's own, per unit, and Paula's level is the speakers'
        knob. Check CAPS for ACAHI_CAP_LEVELS before using them.

   SEE ALSO
        ACAHI_REG_CAPS, docs/howto/08-ahi-sound.md