Skip to content
RetrogamingDeep Dive Published Updated 8 min readViews unavailable

Sega Saturn SCU DMA: Transfer Levels, Indirect Tables, and Bus Boundaries

Read the Saturn SCU DMA contract: three CPU transfer levels, direct and indirect modes, byte counts, address updates, triggers, and documented restrictions.

The Sega Saturn’s System Control Unit (SCU) is more than a helper that copies memory. It connects the CPU interface, A-Bus, and B-Bus, and contains DMA, interrupt, and DSP functions. The SCU DMA levels move data among system regions and can be started directly or from selected events. A transfer’s correctness depends on source and destination buses, byte-count interpretation, address-update settings, activation factor, and documented restrictions.

The primary reference is Sega’s SCU User’s Manual, third version, supplemented by Sega technical bulletins that correct or extend details such as transfer-count semantics. These are preserved scans of confidential developer documents, not current public product documentation. Because Sega itself issued corrections, use the manual and the applicable bulletin together rather than treating one table as timeless and complete.

The SCU coordinates buses and processors

The manual describes the SCU as containing CPU, A-Bus, and B-Bus interfaces. The A-Bus connects external devices such as ROM or CD hardware; the B-Bus connects devices including VDP1, VDP2, and SCSP. Three CPU-usable DMA levels are named 0, 1, and 2, and a separate DMA facility serves the SCU DSP. The registers let software define a read address, write address, transfer byte count, address-add behavior, authorization, and mode/start factor.

Do not collapse these into the SH-2 processor’s own DMA controller. The Saturn includes SH-2-side mechanisms as well as the SCU controller. A transfer triggered by SCU registers has different ownership and bus restrictions from a CPU instruction or a SH-2 DMAC operation. Debug output should identify the bus master and channel level for each read/write.

The three levels are not simply aliases. Their direct-mode count fields and behavior differ, and the manual and technical bulletins describe priority and concurrency constraints. Software that launches more than one channel must respect the controller’s arbitration and the relevant bus availability. A fast emulator should not run all transfers concurrently against one unprotected memory array.

Direct mode: one programmed range

In direct mode, a channel uses the programmed read and write addresses and transfer byte number for one activation. The source and destination can be configured to update by a selected amount after each unit. Address update is not an ornamental stride setting: it determines whether a peripheral data register remains fixed, an interleaved destination advances, or a buffer pointer traverses a region. The legal update choices depend on the channel and bus address class.

Setting a transfer byte count to zero is particularly dangerous if imported from a generic DMA API. Sega’s technical bulletins specify that zero represents the maximum count for the applicable setting, not an empty transfer. The maximum differs by level and mode. In direct mode, level 0 has a larger count range than levels 1 and 2. A boot-time copy routine that unintentionally writes zero can therefore overwrite far more memory than intended.

Before enabling a channel, initialize the complete register set and verify address alignment, count units, bus region, update amount, and start factor. If a peripheral requires a fixed destination register, select the documented no-increment behavior rather than relying on the address remaining constant by accident. After completion, inspect the channel status and end interrupt separately; a DMA end event is not the same as a CPU interrupt handler having already run.

Indirect mode: descriptors control many transfers

Indirect mode adds a table of transfer descriptors in memory. The channel reads descriptor fields that provide a transfer count and source/destination information, then proceeds to later descriptors according to the format and end condition. This is useful for scatter/gather-style data movement and for feeding video command/data structures without having the CPU program a new direct transfer for every segment.

Descriptor parsing must be byte-accurate. A wrong field width or end bit can turn a valid table into a transfer to an invalid bus address. Do not write an indirect-mode test that only checks the final memory range; include a table with two short entries that target separated destinations, then verify the order of descriptor fetches and data reads. Include the termination marker and a zero-count descriptor case explicitly.

Indirect mode count capacity is clarified by Sega’s supplemental bulletins. The direct-mode count field width for levels 1 and 2 does not imply the same maximum for indirect table entries. Copying the direct count mask into an indirect descriptor parser is a common conceptual mistake. Keep mode-specific count conversion in a dedicated routine and annotate it with the source document/version.

Activation factors connect DMA to hardware events

The SCU mode/start-factor register selects how a channel is activated. Depending on the selected mode, DMA can be initiated directly or associated with system events such as blanking or other interrupt-related conditions. This is how software schedules a transfer near display or peripheral deadlines without executing a CPU loop at each event.

An event-triggered transfer should be queued against the emulated Saturn timeline. If the trigger occurs while the channel is already active or while its target bus cannot accept the operation, the hardware’s documented behavior determines whether it waits, ignores, or reports an error. Avoid a generic host callback that completes the entire transfer before the rest of the current scanline executes. For raster-sensitive code, record the trigger cycle, bus owner, transfer progress, and DMA end event.

The Saturn also has separate interrupts for VBlank, HBlank, timer conditions, DMA completion, DSP completion, and other subsystems. An SCU DMA trigger and a CPU interrupt are separate paths even when both originate from a similar video event. Model the event once, fan it out to the appropriate SCU logic, then preserve their distinct masks, status, and completion behavior.

Restrictions are part of compatibility

Sega’s technical bulletins list restrictions that software must observe, including prohibited accesses or alignment/update combinations in specific A-Bus/B-Bus regions, limits on concurrent channel use, and rules around registers being modified during active DMA. Some revisions remove or change registers that appear in earlier drafts. The manual itself warns readers that it can contain errors and that later versions may incorporate changes.

This makes “helpfully accepting” every address a compatibility hazard. If an emulator allows an illegal transfer that the physical controller rejects, software may skip a workaround path and behave differently. Conversely, enforcing a draft restriction that a subsequent bulletin corrected can break valid software. Keep restrictions in a versioned hardware policy and cite the specific document that supports each rule.

Use the correct address view when accessing SCU registers. The manual discusses cache mapping and cache-through addressing. A debugger should show both the CPU-visible address and the canonical SCU register target when appropriate; otherwise a cache alias can make a valid write appear to miss the DMA controller.

A disciplined emulator model

Represent each CPU DMA level with register state, active/authorized state, selected mode, pending start request, transfer progress, and a bus interface. Model the DSP DMA separately because its data unit and memory addressing are different. The shared arbiter should mediate accesses to the A-Bus and B-Bus, and should serialize conflicting work according to documented priority and device timing.

A useful trace record includes:

master=SCU_DMA level=1 mode=indirect trigger=HBlankIn
descriptor=0x06001200 read=0x06008000 write=0x05C00000 count=0x40
read_step=12 bus=A-Bus->B-Bus status=active

The fields are an observability example, not a hardware-defined log format. For correctness, add register snapshots, cache-through/canonical addresses, address-update mode, bus wait state, and completion status. Do not emit logs from every byte in release builds; use bounded tracing and per-transfer summaries.

Verification and safe operating checks

Test direct transfers with byte counts 1, 2, the mode’s maximum-minus-one, and zero. Verify exact semantics for zero with the Sega technical bulletin in view. Exercise every legal address-update mode using both RAM-to-RAM and peripheral-register destinations. For indirect mode, test multiple descriptors, a terminating descriptor, and independent count/address fields. Then test blanking/timer activation with a transfer that is short enough to complete in the intended event window.

Negative tests should include prohibited address regions, a conflicting second channel, writes to active channel registers, cache aliases, and transfers whose destination is a device with side effects. Confirm whether the emulator rejects, stalls, or completes each case according to the cited manual/bulletin rather than a guessed generic policy. Run the same program on a physical Saturn or compare independent emulators when a specific behavior is disputed.

For end-to-end game tests, capture VDP1 command data or VDP2 tables before and after transfer, SCU DMA status, the end interrupt status, and the final raster. If a sprite disappears only when DMA is used, distinguish a bad descriptor, late activation, unsupported destination, and delayed CPU acknowledgement. That decomposition shortens debugging substantially.

Acceptance criteria

An SCU DMA implementation is ready when it reproduces direct and indirect transfer semantics, handles zero-count values per mode, preserves channel and bus ordering, supports event-start timing, and exposes documented restrictions. It should serialize pending DMA state for save states and keep CPU-DMA and DSP-DMA behavior distinct. Every undocumented or implementation-derived exception should be labeled with a confidence level and tested against hardware where possible.

The Saturn’s SCU is a coordination block, not a bulk-copy shortcut. Thinking in terms of bus interfaces, event sources, transfer descriptors, and completion signals reflects the actual hardware design and makes timing bugs reproducible instead of title-specific guesswork.

Related:

Sources:

Comments