112 lines
8.2 KiB
Markdown
112 lines
8.2 KiB
Markdown
# Direct Sound subset
|
|
|
|
The core models timer-driven Direct Sound A/B playback and a bounded sound-DMA configuration.
|
|
It does not produce desktop audio. Programmable sound generator (PSG) synthesis remains unsupported.
|
|
`Memory::audio_level()` exposes the current digital stereo level for deterministic inspection, not a continuous sample stream.
|
|
|
|
## Registers and inactive PSG state
|
|
|
|
| Address | Behavior |
|
|
| --- | --- |
|
|
| `0x04000060..0x04000081` | Idle PSG configuration and documented read masks; writes are ignored while master sound is disabled |
|
|
| `0x04000082` (`SOUNDCNT_H`) | Mixing, routing, timer selection, and FIFO reset strobes; readback mask `0x770f` |
|
|
| `0x04000084` (`SOUNDCNT_X`) | Master enable in bit 7; read-only PSG activity flags remain zero |
|
|
| `0x04000088` (`SOUNDBIAS`) | Bias/resolution configuration with mask `0xc3fe` |
|
|
| `0x04000090..0x0400009f` | CPU wave RAM window, opposite the bank selected by `SOUND3CNT_L` bit 6 |
|
|
| `0x040000a0..0x040000a7` | FIFO A/B writes with byte, halfword, and word access boundaries preserved |
|
|
|
|
Unused mapped register bytes read zero and ignore writes.
|
|
FIFO reads and the `0x0400008c..0x0400008f` gap remain explicit unmapped-access errors; no FIFO open-bus value is invented.
|
|
Raw memory starts with zero sound state. RegisterRamReset bit 6 sets the functional bias default to `0x0200`.
|
|
|
|
Master disable clears PSG configuration and the FIFO queues, but retains wave RAM and Direct Sound routing/bias.
|
|
The CPU then accesses wave bank 1. Idle bank selection can expose bank 0 after master enable.
|
|
The original BIOS reset clears bank 1 and both FIFO queues. It retains bank 0 and any separate in-flight playback word.
|
|
This is a functional reset subset, not a claim of complete firmware ordering or sound-reset equivalence.
|
|
|
|
An enabled PSG channel trigger returns `MemoryError::UnsupportedIo` with the exact address and value.
|
|
This applies even when the configured channel would be inaudible. Disabled PSG trigger writes are ignored.
|
|
Length, envelope, sweep, frequency, and wave-bank playback clocks are not implemented.
|
|
Do not interpret successful idle configuration writes as PSG synthesis.
|
|
|
|
## FIFO and timer behavior
|
|
|
|
Each channel has a seven-word queue, a separate four-byte playback word, and a held signed sample.
|
|
Timer 0 or timer 1 overflow clocks the selected channel while master sound is enabled.
|
|
Timer 1 cascade overflows work. Timer IRQ enable and stereo routing do not gate sample consumption.
|
|
Timers 2 and 3 cannot clock Direct Sound.
|
|
|
|
On each selected overflow:
|
|
|
|
1. Request sound DMA if the queue contains at most three words, before loading a playback word.
|
|
2. Load a queued word if the playback word is empty.
|
|
3. Latch the next signed eight-bit sample in little-endian order.
|
|
|
|
The held sample remains unchanged between overflows. An empty playback word supplies zero on the next overflow.
|
|
This underflow policy follows both consulted emulator implementations; the cited hardware report leaves it uncertain.
|
|
A bulk advance consumes all available samples and then reaches zero without looping over every empty overflow.
|
|
|
|
A partial FIFO write updates the addressed lanes of the next queue slot and enqueues that whole word.
|
|
Other lanes retain that slot's previous bytes. A write to a full queue clears the queue.
|
|
Reset strobes clear queues without changing the held sample or remaining playback bytes.
|
|
Queue clearing also zeros stored queue slots in this nominal model.
|
|
Exact stale-lane behavior across overflow/reset remains unverified on hardware.
|
|
|
|
HALT continues timer/audio progress. STOP freezes it.
|
|
Master disable stops sample consumption; route disable only mutes the mixer path.
|
|
|
|
## Sound DMA
|
|
|
|
Special timing supports DMA1 to FIFO A and DMA2 to FIFO B.
|
|
Each request transfers four 32-bit words with a fixed destination, regardless of programmed count, width, or destination mode.
|
|
Source address control, channel priority, repeat enable, completion IRQ, and retained DMA data still apply.
|
|
Without repeat, the channel disables after the fourth word. With repeat, it waits for another timer request.
|
|
|
|
Other channel/FIFO pairings and non-FIFO destinations remain explicit `DmaError::UnsupportedControl` cases.
|
|
The references disagree about request routing outside the supported pairing; hardware reports permit non-FIFO destinations.
|
|
The core does not silently guess those configurations. Video capture and Game Pak DRQ remain unsupported.
|
|
|
|
Requests become visible at successful machine-step boundaries. Active DMA blocks ignore further requests.
|
|
Device-only bulk advances coalesce unserviced requests; they do not execute or replay DMA blocks.
|
|
Sub-instruction DMA arbitration and exact refill latency are not modeled.
|
|
|
|
## Mixing and timing ownership
|
|
|
|
Each routed sample contributes `sample * 2` at 50% volume or `sample * 4` at 100% volume.
|
|
The mixer adds the ten-bit bias, clips each side to `0..1023`, and subtracts 512.
|
|
`StereoLevel` therefore contains signed levels in `-512..511`. Master disable returns zero.
|
|
Bias resolution bits retain their values but do not yet drive pulse-width modulation (PWM) sampling or quantization.
|
|
There is no resampling, analog filtering, sample queue, or host output backend.
|
|
Polling once per video frame will not reconstruct audio.
|
|
|
|
Timer and sound state share the staged CPU/DMA clock.
|
|
Reads and writes observe their nominal bus-completion phase, after earlier timer overflows.
|
|
Successful steps commit once. Failed steps discard FIFO consumption, held samples, register writes, and refill requests.
|
|
Block-store validation simulates sound enable/disable in a temporary state before committing any RAM or I/O writes.
|
|
Other devices retain their existing scheduling limits.
|
|
|
|
## Validation and remaining work
|
|
|
|
Original tests cover signed samples, byte order, queue capacity, partial writes, resets, underflow, stereo routing, and clipping.
|
|
They also cover cascaded timers, DMA block/repeat behavior, source progress, completion IRQs, HALT/STOP, and clock batching.
|
|
CPU/DMA failure tests verify transactional rollback and sound writes at bus completion.
|
|
BIOS tests cover disabled FIFO reset and the supported wave-bank policy.
|
|
|
|
These tests validate the documented nominal model, not hardware audio fidelity.
|
|
Next work: PSG clocks and synthesis, a timestamped or fixed-rate output stream, PWM/mixer sampling, and a Darwin host backend.
|
|
The local Emerald startup now passes master enable and stops at a PSG channel 1 trigger.
|
|
See [the local runtime result](../research/emerald-reset.md).
|
|
|
|
## References
|
|
|
|
- [GBATEK Direct Sound](https://problemkaputt.de/gbatek-gba-sound-channel-a-and-b-dma-sound.htm): registers, timer selection, signed samples, and DMA blocks.
|
|
- [GBATEK sound control](https://problemkaputt.de/gbatek-gba-sound-control-registers.htm): master enable, read masks, reset strobes, routing, and bias.
|
|
- [GBATEK mirror](https://mgba-emu.github.io/gbatek/): wave-bank access and register layout.
|
|
- [gbadev Direct Sound overview](https://gbadev.net/gbadoc/audio/directsound.html): introductory FIFO/timer/DMA behavior; not authority for queue edge cases.
|
|
- [Gericom's hardware findings, mGBA issue 1847](https://github.com/mgba-emu/mgba/issues/1847): seven-word queue, separate playback buffer, request order, partial writes, and overflow observations.
|
|
- [mGBA audio at 3a5e34be](https://github.com/mgba-emu/mgba/blob/3a5e34be33dc7f8f707e5bc9db69e8a430046f21/src/gba/audio.c): playback, reset, mixing, and destination-based request routing comparison.
|
|
- [NanoBoyAdvance APU at 55b5cf0a](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/hw/apu/apu.cc): master gating, playback word, underflow, and mixing.
|
|
- [NanoBoyAdvance FIFO](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/hw/apu/channel/fifo.hh), [registers](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/hw/apu/registers.cc), and [DMA](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/hw/dma/dma.cc): queue/reset policy and channel-based request routing comparison.
|
|
- [mGBA BIOS at 3a5e34be](https://github.com/mgba-emu/mgba/blob/3a5e34be33dc7f8f707e5bc9db69e8a430046f21/src/gba/bios.c): functional reset defaults.
|
|
|
|
The Rust implementation and regression programs are original. No upstream implementation or external test ROM was copied into the repository.
|