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.mdacahi/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_DONEacahi/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_CMDacahi/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.docacahi/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