Driver architecture
Manatee is a tiny real-time kernel: one interrupt, one loop, a handful of engines and a single queue of MIDI-style events that ties them together. Everything fits in 33 KB of hand-written ARM assembly.
The engines
Owns AICA channels 0-47. Keeps full controller state for 8 ports × 16 MIDI channels, and turns note-on events into voices using tone banks. See The MIDI engine & voices.
Eight song players, one per MIDI port, each with its own tempo, volume, pan, pitch and priority, feeding events into the ring. See The sequencer.
Own AICA channels 48-63: eight one-shot ports for sound effects and voices, four stream ports for streamed music. See One-shots & PCM streams.
Loads DSP programs cleanly, applies output mixes and positional pan. See The DSP and effects.
Who owns which channel
Boot sequence
- Reset vectorSet the stack below the event ring, mask FIQ, mark the status word "initialising".
- SilenceMaster volume 0; key off all 64 channels with the fastest release.
- Clear and defaultZero the host area, event ring, port/channel/voice state and player state; give every MIDI channel power-on defaults (volume 100, expression 127, centred pan, the built-in tone bank); install the built-in banks (tone banks, drum kits, the "e-reverb" DSP program, a default output mix) in the bank table; mute and clear the DSP.
- Start the clockMaster volume up; drain the MIDI input; assign interrupt levels; start Timer A; enable its and MIDI-in's interrupts.
- GoWrite "SEGA" to the status word, unmask FIQ and enter the main loop forever.
The main loop
The loop never sleeps. On every pass it executes up to four queued events, then looks at the two flags the timer interrupt maintains:
Because the event executor runs on every pass, a note triggered by the sequencer in step 3 of a tick typically sounds within microseconds of that tick, not 4 ms later. Parameter changes that affect already sounding voices (a port volume fade, say) are applied in step 5 of the same tick.
The event ring: one language for everything
The key design decision in Manatee is that every musical action becomes a MIDI-style event in one ring of 1024 entries. Each event is a single 32-bit word:
- Producers: the sequencer (ports 0-7), the game directly (the "send MIDI message" host command) and the MIDI input (port 7).
- Consumer: the main loop, four events per pass.
- Priority travels with the event. A port's priority is stamped on every note it plays and becomes the voice's priority when it is allocated, so high-priority sounds can steal voices from music but not the other way round.
- Overflow: the sequencer checks for a full ring and spills into its own overflow queue; the other producers do not check.
Sound RAM layout
Written in assembly
Manatee was written by hand in ARM assembly (credits in the image name Sega's Digital Media group), and it shows in ways that matter when reading it:
- there is no calling convention: routines take values in whatever registers are convenient and sometimes return in two registers, or "return" to the instruction after the one they were called from to signal success;
- scratch variables live inside the code image, between routines;
- many routines share tails or fall into each other;
- jump tables of branch instructions implement every
switch.
The decompilation in this repository expresses all of that as ordinary C, preserving behaviour (including bugs), with a confidence rating per function. See Bugs, quirks & confidence.