Skip to content
RetrogamingDeep Dive Published Updated 8 min readViews unavailable

Neo Geo YM2610 Sound CPU: Command Latches, ADPCM, and ROM Banks

Trace the Neo Geo Z80 sound command path into the YM2610, distinguishing FM and ADPCM engines, sample ROM mapping, and banked driver state.

Neo Geo audio is a small computer inside the console and arcade system, not a YM2610 directly controlled by the 68000. The main CPU sends commands through a sound communication latch; the Z80 sound CPU runs a driver and owns the YM2610 register interface. Cartridge hardware can also provide banked Z80 program data and sample regions. The boundary between command protocol, sound-CPU execution, chip registers, and ROM mapping is essential to reproduce music and effects correctly.

Yamaha’s YM2610, also called OPNB, combines several sound engines: four four-operator FM channels, a three-channel SSG tone generator, six ADPCM-A sample channels, and one ADPCM-B channel. The different engines do not share one generic sample format or trigger model. ADPCM-A is commonly used for short effects, while ADPCM-B can provide longer or streamed material, but a game driver decides its practical role.

The 68000-to-Z80 command path

The 68000 writes a command value to the sound interface and may request service from the Z80. The Z80 reads that command, executes the corresponding driver routine, and can write a response or acknowledgement. This is a producer-consumer protocol between processors. The latch has finite state; sending a new command before the previous value is consumed can overwrite or delay information depending on the hardware behavior.

A sound command is not itself an audio event. It may select a music sequence, trigger a sound effect, stop a voice, change a bank, or update volume. The Z80 program then performs a series of YM2610 register operations, each with bus timing and address/data sequencing. Logging only the command byte omits the point where an incorrect note or sample address is actually programmed.

The Z80 has its own small work RAM and a sound program address space. The Neo Geo cartridge’s banking logic can expose different portions of the audio ROM through selected windows. MAME’s driver and Neo Geo development code show that bank selection is a separate mapping operation. A driver that points to the correct logical sample number but has the wrong bank can read unrelated bytes and decode plausible but incorrect noise.

YM2610 engines and resource ownership

FM channels use operator parameters, frequency numbers, key state, envelope generators, and stereo/output controls. They are synthesized by the chip; they are not decoded from a sample ROM. A register write to one FM operator can alter a timbre without changing the channel’s pitch. Tests should validate address latch, data write, key-on, key-off, and timer/interrupt state rather than compare only the final waveform.

The SSG portion is a separate programmable tone/noise subsystem with its own registers and mixer controls. Treating it as a side effect of FM channel volume is incorrect. A driver can use SSG for simple tones, noise, or additional musical parts while FM runs independently. Keep all source levels separately observable before the chip-level mix.

ADPCM-A and ADPCM-B both use external sample data but have different channel counts, address/control state, and operating modes. The chip’s sample address outputs and board-level ROM mapping determine which bytes the decoder sees. Do not assume an ADPCM address is a flat index into the entire cartridge image; the host machine’s ROM region and banking hardware must resolve it.

ADPCM playback also has endpoint and loop semantics. The start/end registers and key-on/control behavior determine when a sample stops or loops. A common bug is to get the decoded nibble sequence right while rounding the end address or bank boundary incorrectly. Record the chip-visible address, resolved ROM offset, bank selection, nibble phase, predictor state, and channel status together.

Z80 scheduling and safe register writes

The Z80 driver may use short waits between YM2610 address and data writes because the chip has bus timing requirements. Removing those waits can produce failures in a fast emulator even if every write value is correct. Conversely, injecting one host sleep per register write is the wrong fix: it makes the emulated clock depend on wall-clock scheduling.

Advance the Z80, sound latch, YM2610, and sample ROM access on the same deterministic machine timeline. A Z80 interrupt or NMI should be modeled according to the latch and driver protocol, separately from the YM2610’s own timer and status flags. The sound CPU may be blocked, masked, or in a critical section when a new command arrives. Preserve pending latch state until the processor-visible read/acknowledgement transition occurs.

The chip’s timer flags can be used by drivers for sequencing or timing. They are not interchangeable with a host callback timer or the platform’s vertical blank interrupt. Keep YM2610 status, Z80 interrupt state, sound command pending state, and any driver-level queue separately inspectable.

A trace schema for audio failures

This Python record is a debugging schema, not a Neo Geo register definition:

from dataclasses import dataclass


@dataclass(frozen=True)
class SoundEvent:
    cycle: int
    actor: str
    operation: str
    address: int | None = None
    value: int | None = None
    bank: int | None = None


def check_event(event):
    if event.actor not in {"68000", "Z80", "YM2610", "ROM"}:
        raise ValueError("unknown sound-path actor")
    if event.cycle < 0:
        raise ValueError("cycle must be nonnegative")

Use actor labels to distinguish a command-latch write from a Z80 port transaction or a chip-internal sample fetch. A production trace should add the resolved ROM region, channel/engine, nibble or operator state, and a correlation id linking a CPU command to its resulting voice changes.

Reproducible verification

Start with sound CPU reset and command-latch tests: one command, delayed Z80 service, a second command while one is pending, acknowledgement, and interrupt masking. Confirm the sound ROM mapping before interpreting YM2610 output. Then test each engine independently: one FM note with a fixed patch, one SSG tone, one short ADPCM-A sample, and one ADPCM-B stream with a known endpoint.

For banked data, choose samples that cross each relevant window boundary and verify both the logical bank and physical ROM offset. Test bank changes while playback is stopped and, only if the hardware supports it, during active playback. Include missing or absent sample regions so the emulator’s behavior is deterministic and diagnosable rather than an out-of-range host read.

Capture register writes with cycle stamps and compare them against expected sound-driver behavior. Next compare decoded ADPCM state before mixing, then the YM2610 output, and finally the host audio stream. This staged comparison separates a wrong command, wrong driver sequence, bank error, chip synthesis difference, and host resampler issue.

Save states should contain sound latch values and pending flags, Z80 registers and interrupt state, bank selections, RAM, all YM2610 operator/envelope/timer/channel state, sample decoder predictors and positions, and pending bus events. A state captured during a long ADPCM sample must resume from the same bank, byte, nibble, and predictor value.

Acceptance criteria

A production-quality Neo Geo sound model keeps the 68000 command interface, Z80 program and banks, YM2610 engines, external sample ROM mapping, and final audio mix as distinct devices. It can trace a sound command into a specific driver action and chip state transition without relying on a waveform guess.

The reliable debugging question is not simply “does YM2610 sound work?” Ask which actor owned the last correct event: main CPU, latch, Z80, bank logic, chip register interface, sample ROM, or host mixer. That sequence is both the architecture and the test plan.

The sample path is a chip-plus-board contract

The YM2610’s ADPCM engines expose addresses to external sample memory, but a cartridge and board determine how those addresses map into physical V ROM data. Bank switching is therefore a system-level operation around a chip-level decoder. Keep a logical chip address, selected bank, resolved ROM address, and decoded sample state in debugger output. Showing only the final host pointer makes it impossible to tell whether an address calculation or a board mapping is wrong.

When a bank changes, define whether the change affects future fetches immediately, on a sample boundary, or only after the driver stops and restarts a voice, according to the relevant board and chip behavior. Never relocate an already decoded host sample by mutating a pointer behind the decoder. Preserve the guest-visible order of register programming, bank selection, and key-on, then build a minimal test that crosses one window boundary.

Command queue design for the main CPU

A game’s main CPU often has higher-level music and effect events to submit than the one-byte sound interface can represent at once. The Z80 driver may therefore maintain command state or a software queue in its own RAM. Do not confuse that driver queue with a hardware FIFO. If commands are serialized by software, the emulator should execute the actual Z80 program or a documented compatible implementation, preserving its acknowledgement and queue-drain policy.

This distinction matters when sounds are dropped under load. A queue overflow may occur in the driver’s software before the YM2610 sees any write. A chip-channel collision, by contrast, occurs after the driver has issued registers. Capture both the command stream and subsequent Z80-to-chip traffic. A diagnostic can then report whether the requested effect was never serviced, was serviced but replaced an active voice, or was rendered from the wrong sample bank.

Mix and capture evidence at each layer

For FM, SSG, ADPCM-A, and ADPCM-B, collect per-engine output before the chip’s final mix. Then record the mixed YM2610 result before any board amplifier model or host resampling. This gives four comparison levels: driver register stream, chip engine state, chip mix, and host output. A mismatch at each level implies a different class of defect.

Physical Neo Geo measurements should name MVS or AES board revision and the capture point. The same YM2610 core can be present while ROM banking or analog filtering differs around it. Where hardware access is unavailable, mark the result as code/manual-derived or cross-emulator agreement instead of describing it as a measurement from a console.

Related:

Sources:

Comments