CHAPTER 01CPU Architecture — MIPS R3000A
CoreLSI CoreWare CW33300 (MIPS R3000A derivative) @ 33.8688 MHz.
Full MIPS I Instruction Set (every opcode)
Primary opcodes (bits 31-26)00h SPECIAL, 02h J, 03h JAL, 04h BEQ … 12h COP2 (GTE), 20h-26h loads, 28h-2Eh stores. SPECIAL funct (bits 5-0): 00h SLL, 08h JR, 18h MULT, 20h ADD, 24h AND, 2Ah SLT, etc. (full 64-entry table available).
32 General-Purpose Registers + HI/LO with standard ABI
$zero always 0 $at assembler temp $v0-$v1 return values
$a0-$a3- arguments
$t0-$t9 temps $s0-$s7 saved $k0-$k1 kernel $gp global pointer $sp stack (full descending) $fp frame pointer $ra return address
Load-Delay SlotsInstruction immediately after LW/LB/LH/MFC2/CFC2 sees old register value. Data arrives after the delay slot completes (critical for recomp hazard analysis).
Branch-Delay SlotsInstruction after J/JAL/Bxx always executes. On exception in delay slot: CAUSE.BD=1, EPC points to branch address. 5-Stage Pipeline: IF → ID → EX → MEM → WB. No interlocks.
Exception/Interrupt Handling (COP0)
SR (r12)IEc/KUc bits, Isc (cache isolation), Swc (cache swap), CU2 (GTE enable). Cause (r13): ExcCode, IP pending, BD flag. EPC (r14), BadVAddr (r8). RFE restores previous IE/KU bits.
Cache4 KB direct-mapped I-cache. 1 KB scratchpad at 1F800000h-1F8003FFh (used as fast data RAM when SR.Isc=1). Cache isolation mode routes loads/stores to scratchpad only.
GTE (Geometry Transformation Engine — COP2)
64 Data + 32 Control Registers (full list)
Data (0-31)V0-V2 vectors, RGBC, OTZ, IR0-IR3, SXY0-SXY2, SZ0-SZ3, RGB0-RGB2, MAC0-MAC3, IRGB/ORGB, LZCS/LZCR. Control (32-63): RT 3×3 matrix (1.3.12), TR/LLM/LCM translation matrices, BK/RBK/GBK/BBK, RFC/GFC/BFC far color, OFX/OFY (1.15.16), H (U16), DQA/DQB, ZSF3/ZSF4, FLAG (overflow/saturation bits).
Key Operations with Cycle Counts
RTPS15 cycles (perspective single) RTPT: 23 cycles (perspective triple) NCLIP: 8 cycles MVMVA (sf/mx/v/cv/lm): 8 cycles SQR: 5 cycles NCDS/NCDT/NCCS/NCCT: 19/44/17/39 cycles AVSZ3/AVSZ4: 5/6 cycles
MVMVA Full Pseudocode
MAC1-3 = (cv_translation + mx_matrix * v_vector) >> (sf ? 12 : 0) IR1-3 = saturate(MAC1-3, lm ? 0..7FFFh : -8000h..7FFFh) FLAG bits 30-12 updated on overflow/saturation
UNR Division Table (257 entries, verbatim for zero-jitter perspective)
FFh, FDh, FBh … 00h (full table embedded in REFORGE GTE header).
FLAG Saturation (cop2r63, 32 bits)
Bit 31Error summary 30-23: MAC overflow 22-20: IR1-IR3 saturation 19-17: RGB saturation 16: SZ3/OTZ saturation 15: RTPS divide overflow 14-13: SX2/SY2 saturation 12: IR0 saturation Full test vectors for every MVMVA variant (sf/mx/v/cv/lm) available.
Fixed-Point PrecisionMatrices 1.3.12 dominant. All math uses int64 accumulators + exact SAR + saturation. COP2 Instruction Encoding: 010010h + opcode in lower 25 bits.
CHAPTER 02GPU
1 MB VRAM (2 MB on v2 arcade boards via GP1(09h).bit0=1).
Command-Based Rendering (GP0 at 1F801810h)
20hFlat shaded triangle (cmd + color + 3× YX) 30h: Gouraud triangle 24h/2Ch: Textured quads (with UV + CLUT + page) Full hex packet examples:200000FF 00100020 00300040 00500060 ; flat triangle
2C808080- 00100020 00101010 00300040 … ; textured quad
GP1 Display Control (1F801814h)resolution, interlace, area, mode. Texture Formats: 4-bit CLUT (16 colors), 8-bit CLUT (256), 15-bit direct.
Semi-Transparency (GPUSTAT.5-6)
0: 0.5×B + 0.5×F 1: B + F 2: B − F 3: B + 0.25×F Dithering: 4×4 pattern (GP0(E1h).bit0). Render Area / Draw Offset: GP0(E3h-E5h). Display Resolutions: 256/320/368/512/640 × 240/480. GPUSTAT Register (1F801814h) full 32-bit layout for DMA direction, draw mode, texture page, busy flags, etc.
Revision Differences (v0 160-pin vs v2 208-pin)
Shadingv0 ((color/8)×texel)/2 vs v2 (color×texel)/16 GP0(03h) FIFO space taken only on v2 2 MB VRAM support on v2 (Y=512-1023 via GP1(09h))
Texture Cache (2 KB)mode-dependent blocks (4-bit=64×64 units). Eviction on same-entry conflict + VRAM 4-word fetch stall.
SPU (Sound Processing Unit) 24 ADPCM Voices, 512 KB sound RAM.
ADSR Envelope (32-bit register at 1F801C08h)
Full per-sample update with linear/exponential modes, shift/step, clamping (PhaseNegative snap to 0 on decrease).
Reverb Engine (22050 Hz, 112-cycle integration in voice loop)
Full comb + APF stages with registers at 1F801DC0h-DFFh. Buffer circular from mBASE to 7FFFEh with saturation and vIIR=-8000h negation bug. Noise Generator (global 16-bit LFSR parity) + pitch modulation (factor from previous voice output).
Gaussian Interpolation (512-entry table, 4-point)
Full verbatim table + math
$$\text{out} = \sum_{k=0}^{3} \text{gauss}[offset + i] \times \text{sample}_k \gg 15$$
768-Cycle Voice Loop (exact 44.1 kHz timing)
24 voices × 24 cycles + 32 cycles (CD/capture) + 112 cycles reverb + DMA4 preload stalls in unrelated slot. Registers ($1F801C00-$1F801DFF): voice volumes, pitch, start addr, ADSR, KON/KOFF, reverb enable, SPUCNT.
CHAPTER 03Memory Map
$00000000-$001FFFFF- 2 MB main RAM
$1F000000-$1F00FFFF- Expansion region 1
$1F800000-$1F8003FF- 1 KB scratchpad
$1F801000-$1F802FFF- I/O (GPU 1810/1814, SPU C00-DFF, CD 1800, MDEC 1820, DMA 1080-10F4, timers, joypads, serial)
$80000000-$9FFFFFFF- KSEG0 cached mirror
$A0000000-$BFFFFFFF- KSEG1 uncached mirror
$BFC00000-$BFC7FFFF- 512 KB BIOS ROM
CHAPTER 04DMA
7 Channels
0 MDECin, 1 MDECout, 2 GPU, 3 CD-ROM, 4 SPU, 5 PIO (expansion), 6 OTC (ordering table clear). Control Registers (per channel + DPCR/DICR).
Sync Modes
0 burst, 1 block (DREQ), 2 linked-list (GPU OT).
DMA6 OTC + ClearOTagR (reverse fill)
for (i = depth-1; i >= 0; i--) { OT[i] = (i == depth-1 ? 0x00FFFFFF : (OT[i+1] | (len << 24))); } DMA2 GPU Linked-List parser with header (len << 24 | next) + terminator 00FFFFFFh. CD-ROM XA Audio (Mode 2 Form 2 sectors, 924h-byte ADPCM blocks interleaved). Double-Speed (Setmode bit7=1) + DMA3 burst after INT1 (231 words, first word FFFFh quirk). Sony filesystem support via BIOS. MDEC
Hardware JPEG-like FMV Decompression
CommandsMDEC(1) decode, MDEC(2) load quant tables, MDEC(3) load scale table. Quantization tables (luminance/chrominance, game-loaded). IDCT scale table (64 signed 16-bit values, verbatim hex). Zigzag table (full 64 entries). rl_decode_block pseudocode + yuv-to-rgb for colored macroblocks. Pipeline: CD → DMA0 → MDEC → DMA1 → GPU.
CHAPTER 05I/O & Controllers
Joypad Serial Protocol (1F801040h SIO)01h address + 42h read command → ID 5Ah + buttons + DualShock analogs. DualShock Rumble (43h/4Dh config, 42h data bytes for M1/M2 motors). Vibration spin-down ~100-500 ms (M1 large motor). Memory Card Protocol (same bus): 81h 52h read / 57h write, 128-byte sectors, XOR checksum, /ACK per byte ≥2 µs. Multi-Tap IRQ10 shared line (resistor network) with long-response abort on empty Slot A. SIO1 Link Cable (1F801050h) for multiplayer (baud sync, null-modem).
Expansion Port Parallel I/O (1F801000h+) + DMA5. Timers 0-2 (1F801100h+): full MODE register with Hblank/Vblank sync modes, target/FFFF IRQ.
CHAPTER 06BIOS
A0h/B0h/C0h Function Tables (jump via syscall)
A(00h) open, A(02h) read, A(33h) malloc, A(3Fh) printf, A(4Ah) InitPad, etc. Full parameter layouts (R4-R7 + stack words for extra args) + caller/callee conventions. File system, controller/memory card routines, exception handlers, TCB/EvCB structures. Scheduler: cooperative priority-based thread switching via ChangeTh.
CHAPTER 07Recompilation Notes
Critical for Static Recompilation Engine
MIPS delay-slot hazard analysis + BD flag handling in basic blocks. GTE precision/jitter modeling (exact UNR table + FLAG saturation for all MVMVA variants). No Z-buffer → forced DMA6 OTC + linked-list DMA2 OT insertion. GPU packet variable-length parser + texture cache simulation pass (2 KB eviction logic). SPU 44.1 kHz timing-accurate host buffer (768-cycle voice loop with ADSR clamping, Gaussian 4-point interp, noise LFSR, reverb APF/comb, DMA4 preload stalls). MDEC pre-decode pipeline (rl_decode + IDCT + yuv-to-rgb to textures).
BIOS full syscall trampoline generator (A0/B0/C0 with param/stack validation). Timer/HBlank/VBlank interrupt hooks + cooperative scheduler simulation. Joypad SIO state machine + DualShock rumble timing + multi-tap IRQ10 interference handling. Memcard checksum/ACK state machine. SIO1 link cable emulation. PIO DMA5 expansion port hooks. GPU revision detection (v0/v2 shading + 2 MB VRAM + FIFO behavior). Full savestate delta compression (SPU voices, reverb buffer, GPU cache map, TCB/EvCB, timers).
CHAPTER 08I/O Dispatch Reference (Iteration 10 Synthesis)
GTE (COP2) — Geometry Transformation Engine
Data Registers (cop2r0–31):
| Register | Name | Type | Description |
|---|---|---|---|
| r0–r5 | V0/V1/V2 | S16 XYZ | Input vectors |
| r6 | RGBC | U8×4 | R/G/B + CODE |
| r7 | OTZ | U16 | Average Z (0..FFFFh saturated) |
| r8–r11 | IR0–IR3 | S16 | Intermediate results (1.3.12) |
| r12–r15 | SXY0–SXY3 | S16 | Screen coords (saturated -400h..+3FFh) |
| r16–r19 | SZ0–SZ3 | U16 | Z FIFO |
| r20–r22 | RGB0–RGB2 | U8×4 | Color FIFO |
| r24–r27 | MAC0–MAC3 | S32 | Accumulators |
| r28–r29 | IRGB/ORGB | 5:5:5 | RGB conversion |
| r30–r31 | LZCS/LZCR | S32/U32 | Leading-zero count input/result |
Control Registers (cop2r32–63):
| Register | Name | Type | Description |
|---|---|---|---|
| r32–r36 | RT11–RT33 | S16 | Rotation matrix (1.3.12) |
| r37–r39 | TRX/TRY/TRZ | S32 | Translation vector |
| r40–r44 | LLM | S16 | Light matrix |
| r45–r47 | RBK/GBK/BBK | S32 | Background color |
| r48–r52 | LCM | S16 | Light-color matrix |
| r53–r55 | RFC/GFC/BFC | S32 | Far color |
| r56–r57 | OFX/OFY | S32 | Screen offset (1.15.16) |
| r58 | H | U16 | Projection distance |
| r59–r60 | DQA/DQB | S16/S32 | Depth cue coefficients |
| r61–r62 | ZSF3/ZSF4 | S16 | Average Z scale factors |
| r63 | FLAG | U32 | Error bits (see below) |
FLAG register (r63) bit layout:
| Bit | Description |
|---|---|
| 31 | Error summary (any error) |
| 30–25 | MAC1–MAC3 overflow positive/negative |
| 24–22 | IR1–IR3 saturation |
| 21–19 | RGB R/G/B saturation |
| 18 | OTZ saturation |
| 17 | Division overflow |
| 16 | MAC0 overflow |
| 15 | SX2 saturation |
| 14 | SY2 saturation |
| 13 | IR0 saturation |
| 12 | SZ3 saturation |
**All GTE Commands (MIPS encodingbits 31–26=010010, bit 25=sf, bits 0–5=cmd):**
| Command | Cycles | Description |
|---|---|---|
| RTPS | 15 | Perspective transform single (+ UNR divide) |
| RTPT | 23 | Perspective transform triple |
| MVMVA | 8 | 48 matrix×vector variants (mx=0–3, v=0–3, cv=0–3) |
| NCLIP | 8 | Normal clip (MAC0 = cross product of SXY FIFO) |
| AVSZ3 | 5 | Average Z (3 values × ZSF3) |
| AVSZ4 | 6 | Average Z (4 values × ZSF4) |
| NCS/NCT | 14/30 | Normal color single/triple |
| NCDS/NCDT | 19/44 | Normal color depth-cue single/triple |
| NCCS/NCCT | 17/39 | Normal color color single/triple |
| SQR | 5 | Square of IR vector |
| OP | 6 | Outer product |
| GPF | 5 | General interpolation (far) |
| GPL | 5 | General interpolation (far + MAC) |
| DCPL | 8 | Depth-cue color light |
| DPCS/DPCT | 8/17 | Depth-cue single/triple |
| INTPL | 8 | Interpolation |
| CC | 11 | Color color |
| CDP | 13 | Color depth-cue |
MVMVA pseudocode: MAC1-3 = (cv_translation + mx_matrix × v_vector) >> (sf ? 12 : 0), then IR1-3 = saturate(MAC1-3) with lm flag controlling range.
UNR Divide Table (257 entries, used by RTPS/RTPT perspective projection)
0xFF, 0xFD, 0xFB, 0xF9, 0xF7, 0xF5, 0xF3, 0xF1, 0xEF, 0xEE, 0xEC, 0xEA, 0xE8, 0xE6, 0xE4, 0xE3, 0xE1, 0xDF, ... (continues to entry 0x100; exact hardware Newton-Raphson approximation).
Pipeline Stalls: CPU halts on next COP2 or MFC2/CFC2 while GTE is busy (4-stage: load 1–2, MAC 3–12, saturation/FLAG 1, writeback 1).
DMA Controller ($1F801080–$1F8010F8)
DPCR ($1F8010F0): 3-bit priority (0=highest) + enable per channel + CPU priority bits 28–30.
DICR ($1F8010F4): IRQ enables/flags/master. Write-1-to-clear flag bits 24–30. Bit 23 = force IRQ. Bit 31 = master IRQ flag (read-only, computed from enables × flags).
Per-Channel Registers (base + N×$10):
| Offset | Name | Description |
|---|---|---|
| +$00 | MADR | Memory address |
| +$04 | BCR | Block count/size (words per block, number of blocks) |
| +$08 | CHCR | Channel control: direction, memory step, sync mode 0/1/2, start/busy, chopping |
Channel Map:
| Ch | Name | Purpose |
|---|---|---|
| 0 | MDECin | MDEC input (compressed data) |
| 1 | MDECout | MDEC output (decoded pixels) |
| 2 | GPU | GPU command linked-list / image transfer |
| 3 | CDROM | CD-ROM data sector transfer |
| 4 | SPU | Sound RAM transfer |
| 5 | PIO | Expansion port parallel I/O |
| 6 | OTC | Ordering Table Clear (reverse fill) |
Ch2 Linked-List Walk (exact):
addr = MADR;
while (true) {
header = read32(addr); // [31:24]=word count, [23:0]=next pointer
if ((header & 0x00FFFFFF) == 0x00FFFFFF) break;
for (i = 1; i <= (header >> 24); i++)
GPU_GP0(read32(addr + i * 4));
addr = header & 0x00FFFFFF;
}Ch6 OTC Reverse: Backward write of linked-list pointers, fixed step -4, terminates with 0x00FFFFFF.
DMA Timing & Arbitration:
- Base: 1 clock/word (GPU/MDEC/OTC), 4 clocks/word (SPU)
- Hyper-page penalty: 17 clocks per 16 words + 1–2 refresh cycles per row boundary
- Priority: lowest channel number wins ties
- Chopping: DMA window (1<<N words) then CPU window (1<<M cycles)
- Contention: GPU+SPU simultaneous → 30–70% bandwidth loss in 3D scenes
Root Counters / Timers ($1F801100–$1F801130)
Timer 0/1/2 MODE register bits:
| Bit | Description |
|---|---|
| 0 | Sync enable |
| 1–2 | Sync mode (free run / reset at Hblank / reset at Vblank / pause during blank) |
| 3 | Reset counter on target match |
| 4 | IRQ on target match |
| 5 | IRQ on overflow ($FFFF) |
| 6 | Repeat IRQ (0=one-shot, 1=repeat) |
| 7 | IRQ toggle/pulse mode |
| 8 | Clock source (Timer 0: system/dotclock, Timer 1: system/Hblank, Timer 2: system/system÷8) |
| 10 | IRQ flag (read: 0=IRQ pending; write 0 to clear) |
| 11 | Target reached flag |
| 12 | Overflow reached flag |
Exact behavior: Writing MODE resets counter to 0. Target reset holds counter at 0000h for 2 cycles. Overflow wraps after 1 cycle at $FFFF. IRQ pulse vs toggle configurable via bit 7.
SPU ($1F801C00–$1F801E00) — 24 Voices
Per-Voice Registers ($1F801C00 + N×$10):
| Offset | Name | Description |
|---|---|---|
| +$00 | VOL_L | Volume left (fixed or sweep mode) |
| +$02 | VOL_R | Volume right (fixed or sweep mode) |
| +$04 | PITCH | Sample rate (0x1000 = 44.1 kHz base) |
| +$06 | START_ADDR | ADPCM start address (8-byte units in sound RAM) |
| +$08 | ADSR_LO | Attack/Decay (shift/step/mode) |
| +$0A | ADSR_HI | Sustain/Release (shift/step/mode/level) |
| +$0C | ADSR_VOL | Current envelope volume (read) |
| +$0E | REPEAT_ADDR | Loop address (8-byte units) |
ADSR Timing (at 44.1 kHz tick rate):
Cycles = 1 << max(0, shift - 11)
Step = stepValue << max(0, 11 - shift)Exponential increasewhen volume > 0x6000, cycle count ×4. Exponential decrease: step = step × level / 0x8000.
Noise Generator (global): 16-bit Galois LFSR (taps at bits 15/12/11/10, XOR into bit 0). Timer = 0x20000 >> shift. Immediate reset on shift change.
Global SPU Controls:
| Address | Name | Description |
|---|---|---|
| $1F801D80 | MAIN_VOL_L/R | Master volume left/right |
| $1F801D84 | REVERB_OUT_L/R | Reverb output volume |
| $1F801D88 | KON | Key On (24-bit, bits 0–23 = voices 0–23) |
| $1F801D8C | KOFF | Key Off (24-bit) |
| $1F801D90 | PMON | Pitch modulation enable (24-bit) |
| $1F801D94 | NON | Noise enable (24-bit) |
| $1F801D98 | EON | Reverb enable (24-bit) |
| $1F801D9C | ENDX | Voice end status flags (read-only, 24-bit) |
| $1F801DA2 | mBASE | Reverb work area start address |
| $1F801DA6 | IRQ_ADDR | SPU IRQ target address |
| $1F801DA8 | ADDR | Data transfer address |
| $1F801DAA | SPUCNT | SPU control: master enable, reverb, IRQ9, transfer mode, CD/ext enable |
| $1F801DAC | SPUSTAT | SPU status (read-only) |
| $1F801DAE | CD_VOL_L/R | CD audio volume |
| $1F801DB0 | EXT_VOL_L/R | External audio volume |
Reverb Canonical Presets (9 standard presets used by all games):
| Preset | Buffer Size | Description |
|---|---|---|
| Room | 0x26C0 bytes | Small room reflection |
| Studio Small | 0x1F40 bytes | Tight studio reverb |
| Studio Medium | 0x4840 bytes | Medium studio reverb |
| Studio Large | 0x6FE0 bytes | Large studio reverb |
| Hall | 0xADE0 bytes | Concert hall reverb |
| Half Echo | 0x3C00 bytes | Single delayed reflection |
| Space Echo | — | Extended echo effect |
| Echo | — | Standard echo |
| Cathedral | — | Large reverberant space |
Reverb Math (22050 Hz effective, every multiply SAR 15 + saturate -8000h..+7FFFh):
Lin = vLIN * inputL; Rin = vRIN * inputR;
[mLSAME] = saturate((Lin + [dLSAME] * vWALL - [mLSAME-2]) * vIIR / 8000h + [mLSAME-2]);
[mRSAME] = analogous;
// diff-side, comb, APF1, APF2 identical structure
OutputL = saturate(Lout * vLOUT) / 8000h;
BufferAddress = max(mBASE, (BufferAddress + 2) & 0x7FFFE);Pitch Modulation:
if (PMON bit set && voice > 0) {
factor = prev_voice_output + 0x8000; // sign-extend glitch
step = (pitch * factor) >> 15 & 0xFFFF;
}
if (voice == 0) step = pitch; // voice 0 cannot be modulated
step = min(step, 0x4000); // clamp to prevent aliasingCapture Buffers ($00000–$00FFF, 1 KB each): CD Left/Right + Voice 1 + Voice 3. Written every 44100 Hz tick. DMA-readable.
CD-ROM Controller ($1F801800–$1F801803)
INDEX port ($1F801800 bits 0–1): Selects register bank 0–3.
| Port | Bank 0 Read | Bank 0 Write | Bank 1 Read | Bank 1 Write |
|---|---|---|---|---|
| $1800 | Status | Index select | Status | Index select |
| $1801 | Response FIFO | Command | Response FIFO | — |
| $1802 | Data FIFO | Parameter FIFO | Data FIFO | INT enable |
| $1803 | INT enable+flag | INT flag (ack) | INT enable+flag | INT flag (ack) |
Key Commands:
| Cmd | Name | INT | Description |
|---|---|---|---|
| $01 | GetStat | INT3 | Return drive status byte |
| $02 | Setloc | INT3 | Set seek target (3 BCD bytes: MM:SS:FF) |
| $06 | ReadN | INT3→INT1 | Read with retry (ings/normal speed) |
| $08 | Stop | INT3 | Stop motor |
| $09 | Pause | INT3 | Pause reading |
| $15 | SeekL | INT3 | Seek (data mode) |
| $16 | SeekP | INT3 | Seek (audio mode) |
| $19 | Test | INT3 | Various test subfunctions |
| $1A | GetID | INT3→INT2 | Disc identification + region |
| $1B | ReadS | INT3→INT1 | Read without retry (ings/double speed) |
Interrupt Types: INT1 = data ready, INT2 = complete, INT3 = acknowledge, INT4 = data end, INT5 = error. Acknowledge via writing $07/$1F to INT flag register.
XA-ADPCM: Setfilter selects file/channel. Always $900 raw bytes per sector when enabled.
Seek Timing by PU revision:
- PU-7/8 (early): 400–800 ms spin-up, ±8 sector overshoot
- PU-16/18/20 (late): 200–500 ms spin-up, ±2–5 sector overshoot
MDEC ($1F801820–$1F801828)
Commands (written to $1F801820):
| Cmd | Description |
|---|---|
| 1 | Decode macroblock (bits specify depth 15/24-bit, signed/unsigned) |
| 2 | Set quantization table (64 bytes, luminance or chrominance) |
| 3 | Set IDCT scale table (64 signed 16-bit values) |
Status Register ($1F801824): Busy flag, input FIFO full/empty, output FIFO full/empty, remaining word count.
Decode Pipeline: RLE unpack → zigzag reorder → scale × quantize → IDCT (fast 80-multiply or full 1024-multiply) → YCbCr→RGB conversion (15-bit or 24-bit output, 16×16 macroblock reorder).
DMA Request Timing: DMA0 requests every cycle while input FIFO not full and not busy. DMA1 requests while output FIFO not empty (1–2 CPU cycle latency). DecDCTReset(1) clears busy flag and FIFOs (quant/IDCT tables unchanged).
DICR ($1F8010F4) — DMA Interrupt Control Register
Bit layout:
| Bits | Description |
|---|---|
| 0–5 | Unknown/unused |
| 6–14 | Not used |
| 15 | Bus error flag |
| 16–22 | IRQ enable per channel (0–6) |
| 23 | Force IRQ (master IRQ regardless of channel flags) |
| 24–30 | IRQ flags per channel (write-1-to-clear) |
| 31 | Master IRQ flag (read-only): = force OR (master_enable AND (enables AND flags) != 0) |
Complete I/O Register Dispatch Table
| Address | Name | R/W | Side Effects |
|---|---|---|---|
$1F801070 | I_STAT | R/W | Interrupt status. AND-acknowledge: new = old & written (writing 0 clears bit) |
$1F801074 | I_MASK | R/W | Interrupt enable mask (11 sources) |
$1F801040 | JOY_DATA | R/W | Joypad RX data / TX data. Default $FFFF (no buttons). Protocol: addr $01, cmd $42, ID $5A + button bytes |
$1F801044 | JOY_STAT | R | bit1: RX FIFO not empty, bit2: TX ready, bit7: /ACK level |
$1F801048 | JOY_MODE | R/W | Baudrate reload, character length, parity |
$1F80104A | JOY_CTRL | R/W | TX enable, select, ACK, reset, RX enable, IRQ enables |
$1F80104E | JOY_BAUD | R/W | Baudrate divisor |
$1F801810 | GP0 | W | GPU command FIFO — render commands ($20 flat tri, $30 gouraud, $24/$2C textured), VRAM transfer ($A0/$C0) |
$1F801814 | GP1/GPUSTAT | R/W | Read: GPU status (bit26=cmd ready, bit28=VRAM ready, bit27=DMA dir, bit25=DMA req, bit31=odd field). Write: GPU control (reset, display, DMA mode, area, resolution) |
$1F801820 | MDEC_CMD | W | MDEC command/data input |
$1F801824 | MDEC_STAT | R | MDEC status (busy, FIFO, remaining words) |
$1F8010F0 | DPCR | R/W | DMA channel priority (3-bit per ch) + enable bits |
$1F8010F4 | DICR | R/W | DMA interrupt control. Flags 24–30 are write-1-to-clear. Bit 23 = force IRQ. Bit 31 = master (read-only) |
$1F801100 | T0_COUNT | R/W | Timer 0 current value |
$1F801104 | T0_MODE | R/W | Timer 0 mode (write resets counter). Clock: system or dotclock |
$1F801108 | T0_TARGET | R/W | Timer 0 target value |
$1F801110 | T1_COUNT | R/W | Timer 1 current value |
$1F801114 | T1_MODE | R/W | Timer 1 mode. Clock: system or Hblank |
$1F801118 | T1_TARGET | R/W | Timer 1 target value |
$1F801120 | T2_COUNT | R/W | Timer 2 current value |
$1F801124 | T2_MODE | R/W | Timer 2 mode. Clock: system or system÷8 |
$1F801128 | T2_TARGET | R/W | Timer 2 target value |
$1F801800 | CDROM_STATUS | R | CD-ROM status + index select (W) |
$1F801801 | CDROM_CMD/RESP | R/W | Command (W bank 0) / Response FIFO (R) |
$1F801802 | CDROM_DATA/PARAM | R/W | Data FIFO (R) / Parameter FIFO (W bank 0) / INT enable (W bank 1) |
$1F801803 | CDROM_IRQ | R/W | INT enable+flag (R) / INT flag ack (W): write $07/$1F to acknowledge |
$1F801C00–$1F801DFF | SPU | R/W | 24 voice registers + global controls (see SPU section above) |
$1F801DAA | SPUCNT | R/W | SPU control: bit15=master, bit7=reverb, bit6=IRQ9 enable, bits5-4=transfer mode, bit0=CD enable |
$1F801DAC | SPUSTAT | R | SPU status (read-only mirror of control + DMA/IRQ state) |
x86-64 Dispatch Handler Stubs
// Conceptual dispatch for REFORGE PS1 I/O
void mmio_dispatch(uint32_t addr, uint32_t value, bool is_write) {
switch (addr) {
// Interrupts
case 0x1F801070:
if (is_write) i_stat_and_ack(value); // new = old & written
else return i_stat_read();
break;
case 0x1F801074:
if (is_write) i_mask_write(value);
else return i_mask_read();
break;
// GPU
case 0x1F801810: gp0_write(value); break;
case 0x1F801814:
if (is_write) gp1_write(value);
else return gpustat_read();
break;
// DMA
case 0x1F8010F0: dpcr_write(value); break;
case 0x1F8010F4:
if (is_write) dicr_w1c(value); // flags: current &= ~(written & flags_mask)
else return dicr_read();
break;
// Timers
case 0x1F801104: case 0x1F801114: case 0x1F801124:
timer_mode_write(addr, value); // resets counter to 0
break;
// Joypad
case 0x1F801040:
if (is_write) joy_tx(value);
else return joy_rx(); // default $FFFF
break;
// SPU
case 0x1F801DAA: spucnt_write(value); break;
// CD-ROM
case 0x1F801803:
if (is_write) cdrom_irq_ack(value); // write $07/$1F
break;
// MDEC
case 0x1F801820: mdec_cmd_write(value); break;
case 0x1F801824: return mdec_stat_read();
}
}Verification Notes
Test approach (mednafen/DuckStation trace comparison):
- I_STAT AND-acknowledge — set flags via hardware IRQ, write partial mask, verify only masked bits remain
- DICR write-1-to-clear — set channel flags, write selective clear, verify bit 31 master recomputation
- Timer MODE write — verify counter resets to 0, target-reached flag clears
- GTE RTPS — compare all 32 output registers + FLAG against known test vectors
- DMA Ch2 linked-list — walk test OT, verify GP0 receives correct word count and data
- SPU ADSR — verify envelope volume progression against cycle-accurate traces
Expected trace match: ≥99.9% for all MMIO addresses, <0.1% deviation on cycle-timing-dependent reads (GPU FIFO status during heavy rendering).