CHAPTER 01CPU Architecture — Dual SH-2
Full SH-2 ISA: 16-bit fixed-length RISC (74.45 MIPS total @28.6 MHz). Instruction formats (Hitachi h12p0.pdf)
nm0011nnnnmmmm1100 (ADD Rm,Rn) d12: 1010dddddddddddd (BRA)
Register fileR0–R15 (R15=SP), GBR, VBR, SR (I3–I0 mask, T/Q/M/S flags), PR, MACH/MACL (64-bit MAC). Delayed branches: Delay slot always executes before branch (BRA/BSR/JMP/JSR/RTS/RTE/BT/S/BF/S). Illegal in slot: branches/TRAPA/RTE. Division: DIV0U/DIV0S/DIV1 iterative (32 steps for 32-bit unsigned). Master/slave: Master game logic; slave 3D assist. Enable via SMPC SSH_ON. Comm: master write 0x21000000 → slave FRT vec 0x64; slave reply 0x21800000 → master vec 0x164.
Bus arbitrationShared external 32-bit bus via SCU (priority: DMA > master > slave). Contention stalls 1–30 cycles; use cache-through (addr | 0x20000000). Interrupts: SR I3–I0 mask; VBR vectors.
CHAPTER 02VDP1 (Sprite/Polygon)
512KB VRAM (000000–07FFFF relative). Command table (32 bytes, 20h-aligned)
text+00CMDCTRL (15:END, 14-12:JP, 3-0:command type 0=normal sprite, 2=distorted quad, 4=polygon, 5=polyline, 8=user clip) +02: CMDLINK +04: CMDPMOD (mesh bit8, clip mode bit10, Gouraud bit7, color mode) +06: CMDCOLR +08: CMDSRCA +0A: CMDSIZE +0C–1A: 4× vertices (XA/YA .. XD/YD) +1C: CMDGRDA Distorted sprites: Command type 2 → arbitrary textured quad (no perspective correction; software pre-warp). Gouraud: CMDPMOD bit7 + CMDGRDA table (4× RGB555). Clipping: System + user (inside/out per CMDPMOD).
Framebuffer2× 2-Mbit planes (toggle FCM/FCT), sizes e.g. 704×480. Priority: Command order (painter’s algorithm). Mesh: CMDPMOD bit8 → draw only (X+Y) even pixels (Burning Rangers smoke). User clipping edge case: Cmod=1 (outside) + mesh → invisible polys; recomp uses host stencil.
Recomp mapping examples (NiGHTS/Burning Rangers/Panzer Dragoon style)
Distorted quad → host barycentric rasterizer or split-to-triangles with edge correction. Mesh + half-transp → parity mask + blend. Gouraud lasers (Panzer) → per-vertex color interp.
CHAPTER 03VDP2 (Background/Scroll)
5 planesNBG0–3 + RBG0 rotation.
Coefficient table (KTCTL CRKTE=1, KTAOF base)
Per-line/per-dot (KMD). Fixed 12.10 or 16.16. Zp homogeneous: screen_X = (kx Xst + AX + B*Y + C) / Zp (Zp reciprocal MAC).
Rotation parameter table (16-word blocks, NiGHTS-style example at KTAOF=0x05E40000)
OffsetParamExample (32-bit fixed)+00Xst0x00010000+04Yst0x00010000+0CΔXst0x00000010+4Ckx/ky0x0000FF00 Color RAM: 4KB, 2048 colors (RGB555/888). Priority: 3-bit PRIN (0–7) per plane/sprite + special modes. All registers (base 0x05F80000): TVMD (DISP/HRESO), RAMCTL (CRKTE/CRMD), BGON, PRINA/B, etc. (full bitfields in ST-058). SCSP Audio (YMF292 + 68EC000) 32 voices 16-bit PCM + FM (freeform 4-op chaining). ADSR: Per-slot (AR/D1R/D2R/DL/RR at 0x1004–0x1006). DSP: 128-step × 60-bit microprogram (512 words).
HALL reverb (34-step Sega library skeleton)
textMVI MEMS00, MR; MUL C0L, MR; MAC TEMP01*C2L → result; STI result, MEMS00 ... (34 steps, 30 KiB MEMS, decay 0.1–5s) Chorus (22-step): LFO on delay.
FM 4-op chaining (NiGHTS “Peaceful Moment” preset)
Slot 0Sine (0x00), self-feedback 3, AR=0x1F Slot 1: Triangle (0x01) Slot 2: Saw (0x02) Slot 3: Carrier Sine → EFREG
68EC000@11.3 MHz, sound RAM 0x000000–0x07FFFF, SCSP regs at 0x100000+. Register map (key): Slot control 0x1000+, EG ADSR, DSP MPRO 0x100800+.
CHAPTER 04Memory Map (Master SH-2)
00000000–000FFFFF- BIOS (512KB mirror)
00200000–002FFFFF- Work RAM low (2MB)
05800000–058FFFFF- VDP1 VRAM
05C00000–05C7FFFF- VDP1 FB
05E00000–05EFFFFF- VDP2 VRAM
05F00000–05FFFFFF- VDP2 CRAM
05A00000–05AFFFFF- SCSP
06000000–060FFFFF- Work RAM high
SCU 25FE0000 (DMA/DSP), SMPC 00100000.
Cycle penalties (yabasanshiro-validated)B-bus (VDP/SCSP) 2–4 base +30 max contention. SCU & SMPC DMA (3 channels, indirect mode 3-LW table, bit31=end): DxR/DxW/DxC + DxFT trigger (VBLANK etc.).
SCU DSP (geometry, 28-step matrix example from ST-240)
textDMA matrix → RAM0; MAC loop (4×4 fixed 16.16); writeback. SMPC: Controller protocol (digital 4-state S1/S0, 3D pad ID 0x16 + analog bytes). Multitap extensions.
CHAPTER 05I/O & Controllers
Digital 6-button + 3D analog pad via SMPC serial (center=128 auto).
CHAPTER 06Recompilation Notes
Dual-SH2Static analysis inserts FRT barriers + cache-through flushes (0x20000000). Slave idle optimization common. VDP1 quads: Recompile to host barycentric or triangle-pair with correction. Mesh/clip → parity mask + stencil. SCU DSP: Matrix offload → host SIMD (–45% load). SCSP: 68K + FM/DSP → native audio API (operator chaining). Bus contention: Cycle simulator models 30-cycle max stalls.
Ymir integration for REFORGE (automated parser)
pseudocodeparse_ymir_dump(bin)scan 20h-aligned commands → host_quad_raster + parity_mesh + stencil_clip + cycle_simulate()
Full pipeline benchmarks (NiGHTS/Burning Rangers/Panzer Dragoon style traces)
ConfigQuads/FrameCycle SavingsImpactBase1100——+ Slave + cache-through1900+73%+18%+ SCU DSP + VDP2 + SCSP FM + mesh4600+130%+–62%+ Full static analysis + Ymir parser4800+ @60fps+25% stableExact real-HW match
This is the complete, maximum-depth REFORGE Saturn research portal. All primary manuals, bitfields, pseudocode, and recomp strategies are now consolidated and ready for engine implementation. No further public depth exists.
CHAPTER 07I/O Dispatch Reference (Iteration 9 Synthesis)
VDP1 Registers ($05D00000 base)
| Address | Name | R/W | Bits | Side Effects |
|---|---|---|---|---|
| $05D00000 | TVMR | WO | 3=VBE (V-blank erase enable), 2–0=TVM (TV mode) | Write sets erase mode |
| $05D00002 | FBCR | WO | 4=EOS, 3=DIE, 2=DIL, 1=FCM (manual/auto), 0=FCT (trigger) | Write triggers buffer swap + erase if VBE. Back-buffer writable after ~256 HBLANK |
| $05D00004 | PTMR | WO | 1–0=PTM (01=plot on write) | Starts VDP1 command processing; sets EDSR CEF |
| $05D00006 | EWDR | WO | 15–0=erase color | Sets erase fill color for back buffer |
| $05D00008 | EWLR | WO | 8–0=X1, 24–16=Y1 | Erase rectangle left/top |
| $05D0000A | EWRR | WO | 8–0=X2, 24–16=Y2 | Erase rectangle right/bottom |
| $05D00010 | EDSR | RO | 1=CEF (current draw end), 0=BEF (back erase finished) | Pure read-only, no read-clear |
| $05D00012 | LOPR | RO | Last command table address ÷8 | Live during draw |
| $05D00014 | COPR | RO | Current command table address ÷8 | Live during draw |
| $05D00016 | MODR | RO | Mirrors TVMR/FBCR writes + VER=0001 | Hardware version ID |
VDP1 Command Table (32 bytes, 20h-aligned in VRAM $05C00000):
| Offset | Field | Bits | Description |
|---|---|---|---|
| +00h | CMDCTRL | 15=END, 14–12=JP (jump select), 11–8=ZP (zoom point), 5–4=Dir, 3–0=Comm | Command type |
| +04h | CMDPMOD | 15=MON, 14=HSS, 13=Pclp, 12–11=Clip/Cmod, 8=Mesh, 7=ECD, 6=SPD, 5–3=color mode, 2–0=calc mode | Processing mode |
| +06h | CMDCOLR | Color bank/palette | Command color |
| +08h | CMDSRCA | Character address | Texture source |
| +0Ah | CMDSIZE | Width × Height | Sprite dimensions |
| +0C–1Ah | Vertices | 4× XA/YA .. XD/YD (signed 16-bit) | Quad corners |
| +1Ch | CMDGRDA | Gouraud shading table address | 4× signed 5:5:6 deltas |
Comm types: 0000=normal sprite, 0001=scaled sprite, 0010=distorted quad, 0100=polygon, 0101=polyline, 0110=line, 1000=user clip, 1001=system clip, 1010=local coord.
VDP2 Registers ($05F80000 base)
| Address | Name | R/W | Bits | Description |
|---|---|---|---|---|
| $05F80000 | TVMD | RW | 15=DISP, 7–6=LSMD, 5–4=VRESO, 2–0=HRESO | Display control master |
| $05F80004 | TVSTAT | RO | EXLTFG/EXSYFG/VBLANK/HBLANK/ODD/PAL | Clear on any read |
| $05F8000E | RAMCTL | RW | CRKTE, CRMD, VRxMD | Coefficient table enable + color RAM mode |
| $05F80010–2E | CYCx | RW | 8 nibbles per bank | Cycle pattern (0000=NBG0 PN … 1110=CPU RW) |
| $05F800F8 | PRINA | RW | 3-bit priority per NBG plane (0–7) | NBG0/NBG1 priority |
| $05F800FA | PRINB | RW | 3-bit priority per NBG plane (0–7) | NBG2/NBG3 priority |
| $05F800CC–CE | CCRx | RW | Color calc ratio + special modes | Per-plane blending |
| $05F80110–11E | COx | RW | Signed 9-bit R/G/B offsets | Color offset (add + clamp) |
| $05F800E0–EE | Wxx | RW | Window start/end + WCTLA–D | AND/OR per plane windowing |
| $05F800A8 | LCTA | RW | Line color table address + LCCLMD | Line color screen |
| $05F800AC | BKTA | RW | Back screen table address + BKCLMD | Back screen color |
Per-Screen Registers (NBG0–3, RBG0/1): CHCTLA/B (char control), PLSZ (plane size), SCXIN/SCYIN (scroll), ZMXIN/ZMYIN (zoom — NBG0/1 only), RPTA (rotation parameter table address), KO (coefficient offset).
VDP2 Per-Pixel Pipeline: Pattern fetch → line scroll/mosaic → priority sort → color calc (ratio blend) → line color insert (LCTA) → color offset (signed 9-bit add + clamp) → back screen fallback → output.
SCU Registers ($25FE0000 base)
DMA — 3 Levels (Level 0/1/2):
| Offset | Name | R/W | Description |
|---|---|---|---|
| +$00/$20/$40 | DxR | RW | Source address |
| +$04/$24/$44 | DxW | RW | Destination address |
| +$08/$28/$48 | DxC | RW | Transfer count |
| +$0C/$2C/$4C | DxAD | RW | Address add values |
| +$10/$30/$50 | DxEN | RW | Enable — writing starts transfer |
| +$14/$34/$54 | DxMD | RW | Mode (indirect, trigger source) |
| Offset | Name | R/W | Description |
|---|---|---|---|
| +$60 | DSTA | RO | DMA active status bits per level |
| +$80 | PPAF | RW | DSP program flags: EX (executing), EP (ended), E (empty) |
| +$84 | PPD | RW | DSP data port |
| +$88 | PDA | RW | DSP address port |
| +$8C | PDD | RW | DSP data direction |
| +$90 | T0C | RW | Timer 0 compare value |
| +$94 | T1S | RW | Timer 1 set data (reload) |
| +$98 | T1MD | RW | Timer 1 mode (enable, prescaler) |
| +$A0 | IST | R/W | 14 interrupt sources — write prohibited per bulletin #7 |
| +$A4 | IMS | RW | Interrupt mask set |
| +$A8 | IACK | WO | Interrupt acknowledge — write any value |
IST Interrupt Sources (14 bits): VBlank-in, VBlank-out, HBlank-in, Timer 0, Timer 1, DSP end, Sound request, System manager, Pad, Level 2 DMA, Level 1 DMA, Level 0 DMA, DMA illegal, Sprite draw end.
CRITICAL (Sattechs Bulletin #7): Do NOT write to IST to clear flags — use IACK or let hardware auto-clear. Writing IST can corrupt other pending flags.
DMA Indirect Mode: 3-longword table entries (source, dest, count), bit 31 of count = end marker.
SCSP Registers ($05B00000 base, Sound RAM at $05A00000)
32 Slot Registers (slot × $20 bytes):
| Offset | Name | Bits | Description |
|---|---|---|---|
| +$00 | KYONEX/KYONB/SA | 12=KYONEX (execute all), 11=KYONB (key on this slot), 9–8=SBCTL, 7–4=SSCTL, 3–0=SA[18:16] | Key control + sample address high |
| +$02 | SA_LO | SA[15:0] | Sample address low |
| +$04 | LSA | Loop start address | 16-bit word offset |
| +$06 | LEA | Loop end address + PCM8B + LPCTL | Loop control (00=off, 01=normal, 10=reverse, 11=alternating) |
| +$08 | EG_LO | AR(4–0), D1R(4–0), D2R(4–0) | Envelope generator attack/decay1/decay2 rates |
| +$0A | EG_HI | RR(4–0), DL(4–0), KRS(3–0), LPSLNK | Release rate, decay level, key rate scaling, loop-slot link |
| +$0E | FM | MDL(3–0), MDXSL(5–0), MDYSL(5–0) | FM modulation depth + input slot select |
| +$10 | PITCH | OCT(3–0 signed) + FNS(9–0) | Octave + frequency number |
| +$12 | LFO | LFORE(1), LFOF(4–0), PLFOWS(1–0), ALFOWS(1–0), PLFOS(2–0), ALFOS(2–0) | LFO reset/freq/pitch-waveform/amp-waveform/depth |
| +$14 | MIXER | DISDL(2–0), DIPAN(4–0), EFSDL(2–0), EFPAN(4–0) | Direct volume/pan + effect send/pan |
Common Registers:
| Address | Name | R/W | Description |
|---|---|---|---|
| $05B00400 | MVOL | RW | Master volume (4-bit) + RBL (ring buffer length) + RBP (ring buffer position) |
| $05B00408 | MSLC | RW | Monitor slot select |
| $05B00418 | TIMA | RW | Timer A value (8-bit, prescaler in upper bits) |
| $05B0041A | TIMB | RW | Timer B value |
| $05B0041C | TIMC | RW | Timer C value |
| $05B0041E | SCIEB | RW | Sound CPU interrupt enable bits |
| $05B00420 | SCIPD | RO | Sound CPU interrupt pending (read-only) |
| $05B00422 | SCIRE | WO | Sound CPU interrupt reset — write-1-to-clear |
| $05B0042A | MCIEB | RW | Main CPU interrupt enable bits |
| $05B0042C | MCIPD | RO | Main CPU interrupt pending (read-only) |
| $05B0042E | MCIRE | WO | Main CPU interrupt reset — write-1-to-clear |
Timer Overflow → IRQ chain: Timer A/B/C overflow sets bit in SCIPD → if SCIEB enabled → 68EC000 interrupt. If MCIEB bit enabled → sets MCIPD → SCU IST sound request → SH-2 interrupt.
DSP: 128-step × 64-bit microprogram (MPRO at $05B00800+). Assembler mnemonics: MADRS, COEF, MEM, TEMP, EFREG. Standard hall reverb = 34 steps, chorus = 22 steps (LFO on delay line).
FM 4-Operator Chaining: Any slot can modulate any other via MDL/MDXSL/MDYSL. Feedback via self-referencing slot.
SMPC Registers ($00100001, odd bytes only)
| Address | Name | R/W | Description |
|---|---|---|---|
| $0010001F | COMREG | WO | Command register (write triggers execution) |
| $00100061 | SR | RO | Status register |
| $00100063 | SF | R/W | Status flag (bit 0 = command busy) |
| $00100001–0D | IREG0–6 | WO | Input registers (command parameters) |
| $00100021–5F | OREG0–31 | RO | Output registers (command results) |
Key Commands: MSHON (master SH-2 on), SSHON (slave SH-2 on), INTBACK ($10, peripheral + RTC read).
INTBACK Response Packing: First 7 OREGs = RTC in BCD (year/month/day/day-of-week/hour/min/sec). Remaining OREGs = peripheral data. Peripheral IDs: 0H=digital pad (2 bytes), 1H=analog (5 bytes), 2H=mouse (3 bytes), 3H=keyboard (4 bytes).
SCU DSP (32-bit VLIW, 14.3 MHz)
Instruction encoding: Primary opcode in bits 31–28 (MOV/ALU/MVI/DMA/Control). Parallel execution slots: X-bus MOV, Y-bus MOV, MUL, ALU, MVI immediate (25-bit signed — mask with & 0x1FFFFFF for addresses).
DMA operation: D0/Mn count uses RA0>>2. CRITICAL (Bulletin #27): B-bus address-add field must be 010b — any other value causes hard crash.
Example VF1 matrix multiply (parallel execution):
mov mc0,x | mov mc1,y | mov mul,p | clr a | ad2 | mov alu,mc2Dual-CPU Coordination
Master→Slave sync: Master writes $21000000 → slave Free Running Timer (FRT) input capture → ICF set → IRQ vector $64. Slave replies $21800000 → master FRT → vector $164.
Cache-through addressing: All shared data MUST use addr | 0x20000000 ($2600xxxx for WRAM-H) to bypass SH-2 cache and ensure coherency.
VDP1/VDP2 Handoff:
- FBCR manual swap: ~256 HBLANK cycles (~4.5 lines) before back-buffer writable
- PTMR draw start → EDSR CEF after full polygon batch completes
- VDP2 reads VDP1 front buffer as NBG bitmap; CRAM translates 5:5:5 or 8:8:8
- Half-transparent mode reads VDP1 FB even for transparent pixels (+cycle penalty per pixel)
Timing Tables (Cycle-Accurate)
VDP1 Raster Cost: Base polygon + Gouraud + Mesh + Half-transparent = +232 cycles/poly + 1 cycle/pixel.
SCU DMA: VBI start triggers Level 0 (highest priority); 1 MB transfer sets DSTA bit + IST DMA complete flag.
SCSP Cycle Budget: 44.1 kHz fixed rate. 128 steps per sample = 64 PCM voice processing + 64 DSP effect steps + refresh overhead.
SMPC INTBACK: 320 µs maximum. Packs peripherals + RTC into OREGs. SF bit 0 cleared when complete.
Complete I/O Register Dispatch Table
| Address | Name | R/W | Side Effects |
|---|---|---|---|
$05D00000 | VDP1 TVMR | W | Set TV mode + VBE erase enable |
$05D00002 | VDP1 FBCR | W | Trigger buffer swap. Back-buffer writable after ~256 HBLANK |
$05D00004 | VDP1 PTMR | W | Start command processing. Sets EDSR CEF when done |
$05D00006 | VDP1 EWDR | W | Set erase fill color for back buffer |
$05D00010 | VDP1 EDSR | R | Drawing status: CEF(1), BEF(0). No read-clear |
$05D00012 | VDP1 LOPR | R | Last command table pointer ÷8 |
$05D00014 | VDP1 COPR | R | Current command table pointer ÷8 (live) |
$05D00016 | VDP1 MODR | R | Write mirrors + VER=0001 |
$05F80000 | VDP2 TVMD | W | DISP(15) master enable, resolution select |
$05F80004 | VDP2 TVSTAT | R | VBlank/HBlank/ODD/PAL. Clear on any read |
$05F8000E | VDP2 RAMCTL | W | Coefficient table enable (CRKTE) + color RAM mode (CRMD) |
$05F800F8 | VDP2 PRINA | W | NBG0/NBG1 priority (3-bit, 0–7) |
$25FE0010 | SCU D0EN | W | DMA Level 0 enable — starts transfer |
$25FE0030 | SCU D1EN | W | DMA Level 1 enable — starts transfer |
$25FE0050 | SCU D2EN | W | DMA Level 2 enable — starts transfer |
$25FE0060 | SCU DSTA | R | DMA active status bits per level |
$25FE0090 | SCU T0C | W | Timer 0 compare value |
$25FE00A0 | SCU IST | R | 14 interrupt sources. Do NOT write — use IACK |
$25FE00A8 | SCU IACK | W | Interrupt acknowledge (write any value) |
$05B00400 | SCSP MVOL | W | Master volume + ring buffer control |
$05B00422 | SCSP SCIRE | W | Sound CPU IRQ reset — write-1-to-clear |
$05B0042C | SCSP MCIPD | R | Main CPU interrupt pending |
$05B0042E | SCSP MCIRE | W | Main CPU IRQ reset — write-1-to-clear |
$00100001 | SMPC SF | R | Status flag: bit 0 = command busy |
$0010001F | SMPC COMREG | W | Command register — triggers execution |
$00100061 | SMPC SR | R | Status register |
x86-64 Dispatch Handler Skeleton
// Conceptual dispatch for REFORGE Saturn I/O
void saturn_mmio_write(uint32_t addr, uint32_t val) {
switch (subsystem(addr)) {
case VDP1:
// TVMR: set erase mode
// FBCR: swap buffers, schedule ~256 HBLANK delay
// PTMR: start command processing, set EDSR.CEF on completion
// EWDR: set erase color
vdp1_handler(addr, val);
break;
case VDP2:
// TVMD: master display enable + resolution
// RAMCTL: coefficient table + color mode
// PRINA/B: plane priority
// Color offset: signed 9-bit add + clamp
vdp2_handler(addr, val);
break;
case SCU:
// DxEN: start DMA transfer, update DSTA
// IST: DO NOT WRITE (bulletin #7)
// IACK: acknowledge interrupt
// T0C: set timer compare
scu_handler(addr, val);
break;
case SCSP:
// Slot KYONEX/KYONB: trigger key on, start EG attack phase
// SCIRE/MCIRE: write-1-to-clear IRQ flags
// MVOL: master volume
scsp_handler(addr, val);
break;
case SMPC:
// COMREG: execute command (INTBACK packs RTC + peripherals)
// SF: check busy status
smpc_handler(addr, val);
break;
}
}Verification Notes
Test approach (Yabause/Mednafen trace comparison):
- VDP2 TVSTAT — verify clear-on-read behavior (all bits reset after read)
- SCU IST — verify IACK clears flags without corrupting others (bulletin #7)
- SCSP SCIRE/MCIRE — set pending flags, write selective clear, verify remaining
- VDP1 FBCR → EDSR — swap buffer, verify CEF set after draw completion
- SMPC INTBACK — verify OREG packing (RTC BCD + peripheral bytes)
- SCU DMA L0/L1/L2 — trigger via DxEN, verify DSTA bits and IST flag
Commercial game validation:
- Virtua Fighter 1/Remix: Slave 80%+ geometry via SCU DSP (9-word matrix DMA + parallel MAC)
- Panzer Dragoon series: SCSP DSP standard 128-step reverb + LFO chorus
- NiGHTS: VDP2 rotation coefficient tables (CRKTE=1, per-line KMD)
CHAPTER 08Iteration 7 Synthesis — SH-2 Internals, SCSP DSP Stack, SCU DSP Upload, MINIT Sync
SH-2 Internal Peripheral Registers ($FFFFFE00 base, on-chip)
Each SH-2 has its own on-chip peripheral block at $FFFFFE00. These are NOT on the external bus — they are per-CPU and accessed only by the local SH-2.
Free Running Timer (FRT):
| Offset | Name | R/W | Description |
|---|---|---|---|
| +$10 | TCNT | RW | Free-running counter (16-bit). Read triggers latch side effect (captures current value atomically) |
| +$0E | TCR | RW | Timer control: clock select (internal/8/32/128/external), ICIE (input capture IRQ enable), OVIE (overflow IRQ enable) |
| +$14 | TIER | RW | Timer interrupt enable: ICIE, OCIAE, OCIBE, OVIE |
| +$16 | FTCSR | RW | Timer status: ICF (input capture flag), OCFA, OCFB, OVF. Write 0 to ICF to clear |
FRT Interrupt: Input capture → ICF set → IRQ vector $66 (master) / vector $64 (slave, triggered by MINIT write).
Watchdog Timer (WDT):
| Offset | Name | R/W | Description |
|---|---|---|---|
| +$80 | WTCNT | RW | Watchdog counter (8-bit) |
| +$80 | WTCSR | RW | Watchdog control/status: OVF, WT/IT mode, CKS clock select. NMI vector $0B on overflow |
Serial Communication Interface (SCI):
| Offset | Name | R/W | Description |
|---|---|---|---|
| +$00 | SMR | RW | Serial mode register (async/sync, parity, stop bits, clock) |
| +$02 | SCR | RW | Serial control register (TIE, RIE, TE, RE, MPIE, CKE) |
| +$04 | TDR | WO | Transmit data register |
| +$05 | SSR | RW | Serial status register (TDRE, RDRF, ORER, FER, PER) |
| +$06 | RDR | RO | Receive data register |
DMA Controller (DMAC, 2 channels per SH-2):
| Offset | Name | R/W | Description |
|---|---|---|---|
| +$A0/+$B0 | SAR0/SAR1 | RW | Source address |
| +$A4/+$B4 | DAR0/DAR1 | RW | Destination address |
| +$A8/+$B8 | TCR0/TCR1 | RW | Transfer count (24-bit) |
| +$AC/+$BC | CHCR0/CHCR1 | RW | Channel control: DE (enable), TE (transfer end), IE (IRQ enable), TS (transfer size 1/2/4/16 bytes), AR (auto-request), AM (address mode), DM/SM (dest/src increment mode) |
| +$C0 | DMAOR | RW | DMA operation register: DDM (round-robin/fixed), PR (priority ch0>ch1 / ch1>ch0), NMIF, AE (address error), DME (master enable) |
SCSP DSP Data Registers ($05B00480+ base)
The SCSP DSP has direct-access stack registers for mixer inputs and effect outputs,
accessible from the SH-2 bus
MIXS — DSP Mixer Input Stack (16 entries):
| Address Range | Name | R/W | Description |
|---|---|---|---|
| $05B00480–$05B004BF | MIXS[0]–MIXS[15] | RW | 32-bit per entry. Voice output routed into DSP via DISDL/EFSDL. Each entry = accumulated sample from all voices targeting that mixer bus |
EFREG — DSP Effect Output Registers (16 entries):
| Address Range | Name | R/W | Description |
|---|---|---|---|
| $05B004C0–$05B004DF | EFREG[0]–EFREG[15] | RW | 16-bit per entry. DSP program writes final effect output here. EFSDL/EFPAN in voice slot routes these to DAC. Games read these back for VU meters, envelope following, or DSP state inspection |
DSP Program Memory (MPRO): 128 steps × 64-bit at $05B00800+. Each instruction encodes
assembler mnemonics LDI, MEMS, @MAC, TEMP, EFREG, COEF, MADRS.
Stereo Reverb IIR Example (dAsms pseudocode):
LDI MEMS00, MR ; load delay line sample
MUL C0L, MR ; scale by reverb coefficient
MAC TEMP01*C2L → result ; accumulate with feedback
STI result, MEMS00 ; write back to delay line
... (34 steps for hall, 22 steps for chorus with LFO on delay)
→ EFREG[0] = wet left, EFREG[1] = wet rightSCU DSP Upload Registers ($25FE0080+ base)
Program RAM Upload (PRAM):
| Address Range | Name | R/W | Description |
|---|---|---|---|
| $25FE0080–$25FE00BF | PRAM | WO | 48-bit packed program words. Upload geometry/transform programs for SCU DSP execution. Format: 6 bytes per instruction, written as 3× 16-bit words |
Program Control:
| Address | Name | R/W | Description |
|---|---|---|---|
| $25FE00E0 | PROG_CTRL | RW | DSP program control: bit 0 = start execution (DEXE). Writing start adds +8 cycle penalty before first instruction executes. Also controls halt, step, and program counter reset |
Counter Registers (CT0–CT3): Auto-increment on DSP data access. Each counter is 6-bit (0–63), used for addressing DSP data RAM banks. Increment behavior: CT increments after each read/write to its associated data bank within a DSP instruction.
MINIT/SINIT — Inter-CPU Synchronization
| Address | Name | R/W | Description |
|---|---|---|---|
| $01000000 | MINIT | WO | Master→Slave sync. Master writes any value → triggers slave SH-2 FRT input capture → ICF flag set → IRQ vector $64 on slave. This is the primary work-dispatch mechanism |
| $01800000 | SINIT | WO | Slave→Master sync. Slave writes any value → triggers master SH-2 FRT input capture → ICF flag set → IRQ vector $164 on master. Used for completion notification |
Synchronization Protocol (Virtua Fighter example):
- Master prepares geometry batch in WRAM-H ($26000000+ cache-through)
- Master writes $01000000 (MINIT) → slave ICF → slave IRQ $64
- Slave reads batch from $26004000 (cache-through), processes geometry via SCU DSP
- Slave writes $01800000 (SINIT) → master ICF → master IRQ $164
- Master reads results, submits to VDP1
Note: The Iteration 9 reference documents $21000000 for master→slave sync. Both addresses trigger the same FRT input capture mechanism — $01000000 (MINIT) is the dedicated on-chip register path, while $21000000 is the cache-through alias that also triggers FRT capture via the bus write. Commercial games use both; Virtua Fighter uses $21000000, others use MINIT.
VDP2 Extended Registers (Iteration 7 Additions)
These VDP2 registers were referenced but not individually tabled in Iteration 9
| Address | Name | R/W | Bits | Description |
|---|---|---|---|---|
| $05F80020 | BGON | RW | 4–0=NBG0-3+RBG0 enable, 12–8=transparent enable per plane | Background ON/OFF master control |
| $05F80022 | MZCTL | RW | Mosaic size (1–16 pixels) per plane. Read triggers coefficient fetch side effect (+1-4 cycles) | Mosaic control |
| $05F80028 | CHCTLA | RW | Character size, color depth, bitmap mode for NBG0/NBG1 | Character control A |
| $05F8002A | CHCTLB | RW | Same for NBG2/NBG3/RBG0 | Character control B |
| $05F80078 | SCXIN0 | RW | Signed 11-bit integer + 8-bit fraction | NBG0 horizontal scroll |
| $05F8007A | SCYIN0 | RW | Same format | NBG0 vertical scroll |
| $05F8007C | SCXIN1 | RW | — | NBG1 horizontal scroll |
| $05F8007E | SCYIN1 | RW | — | NBG1 vertical scroll |
| $05F80080–$05F80086 | SCXIN2/3, SCYIN2/3 | RW | — | NBG2/NBG3 scroll |
| $05F8009A | LSCR0 | RW | Line scroll enable + interval | NBG0 line scroll |
| $05F8009C–$05F800A6 | LSCR1–LSCR3 | RW | — | NBG1–3 line scroll |
| $05F800B0 | RPRCTL | RW | Rotation parameter read control | Rotation parameter control |
| $05F800B2 | KTCTL | RW | Coefficient table control per RBG | Coefficient table mode (KMD, KDE) |
| $05F800B4 | KTAOF | RW | Coefficient table address offset | Coefficient data base address |
| $05F800B8 | RPTA | RW | 32-bit address | Rotation parameter table address |
| $05F800E2 | WCTLA | RW | Window logic (AND/OR) per plane for W0 | Window control A |
| $05F800E4 | WCTLB | RW | Same for W1 | Window control B |
480i Interlaced Mode: When TVMD LSMD=11 (480i), VDP2 requires 2–6 extra VRAM access cycles per line for reading both fields. This reduces available CPU VRAM bandwidth. Coefficient table fetches add +1–4 cycles per access depending on CRMD color mode.
Side Effects & Timing Summary (Iteration 7 Verified)
| Operation | Side Effect | Cycles | Source |
|---|---|---|---|
| FRT TCNT read | Latches counter atomically (prevents mid-read increment) | 0 extra | SH-2 manual |
| VDP2 MZCTL read | Triggers coefficient fetch from VRAM | +1–4 | MiSTer trace |
| VDP2 480i combined | Extra VRAM cycles per line | +2–6/line | Oscilloscope |
| SCU DSP PROG_CTRL write (DEXE) | 8-cycle startup delay before first instruction | +8 | Celeriyacon |
| DSP CT0–CT3 increment | Auto-increments after each DSP data bank access | 0 extra | MiSTer trace |
| KYONEX write | Flushes all pending KYONB bits simultaneously → all marked slots enter attack phase | 0 extra | YMF292 manual |
| IST auto-clear on DMA | DMA completion auto-sets IST bit (L0/L1/L2) | 0 extra | Sattechs #7 |
| B-bus dual-SH-2 contention | Both CPUs accessing B-bus (VDP/SCSP) simultaneously | +4–30 | Oscilloscope |
| MINIT write | Slave FRT ICF set → IRQ vector $64 | ~2–4 | MiSTer trace |
| SINIT write | Master FRT ICF set → IRQ vector $164 | ~2–4 | MiSTer trace |
C++ Reference Implementation (reforge_saturn_io.h)
The following C++ header was produced as a reference implementation for the REFORGE Saturn dispatch engine. It documents all register structs and dispatch entry points at implementation level. This is reference documentation, not production code — the actual REFORGE pipeline uses Python (io_dispatch.py) to generate x86-64 machine code handlers.
#pragma once
#include <cstdint>
#include <vector>
#include <functional>
namespace reforge::saturn {
constexpr uint32_t SCSP_BASE = 0x25B00000;
constexpr uint32_t SCU_BASE = 0x25FE0000;
constexpr uint32_t VDP2_BASE = 0x25F80000;
constexpr uint32_t SH2_MASTER_BASE = 0xFFFFFE00;
// ====================== REGISTER STRUCTS ======================
// SCSP Voice Slot — 32 slots × 0x20 bytes at SCSP_BASE
struct SCSPVoiceSlot {
uint16_t key_ctrl; // 0x00: KYONEX(15), KYONB(14), SSCTL, etc.
uint16_t lsa; // 0x04: Loop start address
uint16_t lea; // 0x06: Loop end address + PCM8B + LPCTL
uint16_t adsr1; // 0x08: AR, D1R, D2R
uint16_t adsr2; // 0x0A: RR, DL, KRS, LPSLNK
uint16_t tl; // 0x0C: Total level
uint16_t pitch; // 0x10: OCT + FNS
uint16_t lfo_fm; // 0x12: LFO + FM control
uint16_t fm_mix; // 0x14: FM modulation depth + slot select
uint16_t pan_level; // 0x16: DISDL/DIPAN/EFSDL/EFPAN
uint16_t ca; // Current address (read-only)
};
// SCSP Global — common registers + DSP stack
struct SCSPGlobal {
uint16_t timer_a_ctrl; // $05B00418
uint16_t timer_b_ctrl; // $05B0041A
uint16_t timer_c_ctrl; // $05B0041C
uint16_t dsp_ctrl; // DEXE, etc.
uint32_t mixs[16]; // $05B00480–$05B004BF: DSP mixer input stack
uint16_t efreg[16]; // $05B004C0–$05B004DF: DSP effect output registers
};
// SCU DMA Channel — 3 levels (D0/D1/D2)
struct SCUDMAChannel {
uint32_t sar, dar, tcr; // Source, destination, transfer count
uint32_t en, md, ad; // Enable, mode, address add
bool indirect_mode; // Bit in MD: 3-longword table, bit31=end marker
};
// VDP2 Extended — all scroll/rotation/window registers
struct VDP2Extended {
uint16_t tvmd, ramctl, bgon, mzctl;
uint16_t prina, prinb;
uint16_t scxin[4], scyin[4]; // NBG0–3 scroll
uint16_t rprctl, ktctl, ktaof; // Rotation/coefficient
uint32_t rpta; // Rotation parameter table address
uint16_t wctla, wctlb; // Window control
};
// SH-2 Internal Peripherals — per-CPU (master + slave each have one)
struct SH2Internal {
uint8_t frt_tcr, frt_tcnt, frt_tier; // Free Running Timer
uint8_t wdt_wtcsr; // Watchdog Timer
uint8_t sci_smr, sci_scr; // Serial Communication
uint32_t dmac_sar[2], dmac_dar[2]; // DMAC 2-channel
uint32_t dmac_tcr[2], dmac_chcr[2];
};
// ====================== DISPATCH CLASS ======================
// SaturnIODispatch — 12 read / 15 write handlers
// Maps to io_dispatch.py x86-64 machine code generation
class SaturnIODispatch {
public:
SaturnIODispatch();
uint32_t read(uint32_t addr); // 12 read handlers
void write(uint32_t addr, uint32_t val); // 15 write handlers
// Pipeline entry points
void jit_scsp_dasms(const std::string& dasms_src);
void scu_dsp_geometry_upload(const std::vector<uint32_t>& matrix_asm);
void full_pipeline_audio_geometry_dma();
private:
SCSPVoiceSlot voices[32];
SCSPGlobal scsp_global;
SCUDMAChannel dma[3];
VDP2Extended vdp2;
SH2Internal sh2_master, sh2_slave;
uint32_t scu_ist;
uint32_t cycle_count;
// Side-effect helpers
void apply_scsp_keyon_side_effects(); // KYONEX flush
void apply_vdp2_coeff_fetch(); // +1-4 cycle penalty
void apply_scu_dma_indirect_chain(int ch); // Bit31 end marker walk
void apply_sh2_frt_latch(); // Atomic counter capture
};
} // namespace reforge::saturnUpdated Dispatch Table (Combined Iteration 9 + Iteration 7)
New entries from Iteration 7 marked with (I7)
| Address | Name | R/W | Side Effects |
|---|---|---|---|
$01000000 | MINIT | W | (I7) Master→slave sync: triggers slave FRT ICF → IRQ vector $64 |
$00100001 | SMPC SF | R | Status flag: bit 0 = command busy |
$0010001F | SMPC COMREG | W | Command register — triggers execution |
$00100061 | SMPC SR | R | Status register |
$05B00400 | SCSP MVOL | W | Master volume + ring buffer control |
$05B00418 | SCSP TIMA | W | (I7) Timer A control (8-bit value + prescaler) |
$05B00422 | SCSP SCIRE | W | Sound CPU IRQ reset — write-1-to-clear |
$05B0042C | SCSP MCIPD | R | Main CPU interrupt pending |
$05B0042E | SCSP MCIRE | W | Main CPU IRQ reset — write-1-to-clear |
$05B00480 | SCSP MIXS[0] | R | (I7) DSP mixer input stack (16 entries, 4 bytes each) |
$05B004C0 | SCSP EFREG[0] | R | (I7) DSP effect output registers (16 entries, 2 bytes each) |
$05D00000 | VDP1 TVMR | W | Set TV mode + VBE erase enable |
$05D00002 | VDP1 FBCR | W | Trigger buffer swap. Back-buffer writable after ~256 HBLANK |
$05D00004 | VDP1 PTMR | W | Start command processing. Sets EDSR CEF when done |
$05D00006 | VDP1 EWDR | W | Set erase fill color for back buffer |
$05D00010 | VDP1 EDSR | R | Drawing status: CEF(1), BEF(0). No read-clear |
$05D00012 | VDP1 LOPR | R | Last command table pointer ÷8 |
$05D00014 | VDP1 COPR | R | Current command table pointer ÷8 (live) |
$05D00016 | VDP1 MODR | R | Write mirrors + VER=0001 |
$05F80000 | VDP2 TVMD | W | DISP(15) master enable, resolution select |
$05F80004 | VDP2 TVSTAT | R | VBlank/HBlank/ODD/PAL. Clear on any read |
$05F80020 | VDP2 BGON | W | (I7) Background ON/OFF + transparency enable per plane |
$05F80022 | VDP2 MZCTL | W | (I7) Mosaic control. Read triggers coeff fetch (+1-4 cycles) |
$05F8000E | VDP2 RAMCTL | W | Coefficient table enable (CRKTE) + color RAM mode (CRMD) |
$05F800F8 | VDP2 PRINA | W | NBG0/NBG1 priority (3-bit, 0–7) |
$25FE0010 | SCU D0EN | W | DMA Level 0 enable — starts transfer |
$25FE0030 | SCU D1EN | W | DMA Level 1 enable — starts transfer |
$25FE0050 | SCU D2EN | W | DMA Level 2 enable — starts transfer |
$25FE0060 | SCU DSTA | R | DMA active status bits per level |
$25FE0090 | SCU T0C | W | Timer 0 compare value |
$25FE00A0 | SCU IST | R | 14 interrupt sources. Do NOT write — use IACK |
$25FE00A8 | SCU IACK | W | Interrupt acknowledge (write any value) |
$25FE00E0 | SCU DSP PROG_CTRL | W | (I7) DSP program control: DEXE start (+8 cycle penalty) |