← Blog

sigilOS Retro: Sega 32X — System #44

June 22, 2026 · sigil-retropie · Sigil-Docs
retropie sega-32x sh-2 0.6.0

Sega 32X is system #44 in sigilOS RetroPie. The 32X was an add-on for the Sega Genesis that added a pair of Hitachi SH-2 processors and a 320×224 ARGB framebuffer — more CPU and a cleaner colour space than the base console. The sigilOS core reuses the SH-2 engine from the Saturn (#43) via first-def-wins override, adds the 32X memory map, and renders packed RGB555 big-endian pixels to the ARGB framebuffer. uart=51 PASS.

sigilOS retro emulation running a game
sigilOS retro emulation — Sega 32X system #44, 320×224 RGB555 framebuffer
SystemSega 32X (Mega Drive add-on) CPUHitachi SH-2 (reused from Saturn core) Resolution320 × 224 ARGB Frame VRAM$04000000–$0403FFFF (143360 B packed RGB555 big-endian) ROM64KB boot ROM @ $00000000 WRAM256KB @ $06000000–$0603FFFF Steps/frame38,333 SH-2 instructions ROM cap64KB (SECURE: EL0 cannot exceed cap) Testuart=51 PASS Launcher countrp_count 43 → 44

First-def-wins SH-2 reuse

The Saturn and the 32X both use the Hitachi SH-2. Rather than duplicating the core, s32x.sg is linked before sh2.sg. The Sigil linker's first-def-wins rule means any function defined in s32x.sg takes precedence over the same symbol in sh2.sg. For the 32X, only the memory bus seam needs to differ — sh2_bus_read and sh2_bus_write in s32x.sg route to the 32X memory map (ROM, WRAM, frame VRAM). Everything else — the SH-2 instruction decoder, delay-slot handling, register file — comes from the shared sh2.sg unmodified.

This is the same pattern used for MSX Turbo-R (#42), which overrode the Z80 bus seam with an R800 core, and for CPS-1 (#37), which reused the 68000 core entirely. One CPU, multiple systems.


Memory map

Address rangeRegionSize
$00000000–$0000FFFFBoot ROM64 KB
$04000000–$0403FFFFFrame VRAM (RGB555 big-endian)143,360 B
$06000000–$0603FFFFWRAM (work RAM)256 KB

The frame VRAM layout stores one 16-bit RGB555 pixel per 2 bytes, big-endian, for a 320×224 framebuffer. Each pixel: bits [14:10] = R, [9:5] = G, [4:0] = B. Bit 15 is unused (high bit of the big-endian word).


RGB555 → ARGB conversion

s32x_rgb555(px) extracts the three 5-bit channels and scales each to 8 bits by shifting left 3 and ORing with the top bits — the standard 5-to-8 expansion:

fn s32x_rgb555(px: Int) -> Int {
  let r5 = (px >> 10) & 31
  let g5 = (px >>  5) & 31
  let b5 =  px        & 31
  let r8 = (r5 << 3) | (r5 >> 2)
  let g8 = (g5 << 3) | (g5 >> 2)
  let b8 = (b5 << 3) | (b5 >> 2)
  return (255 << 24) | (r8 << 16) | (g8 << 8) | b8
}

The 5-to-8 expansion via (ch << 3) | (ch >> 2) maps 0→0 and 31→255 exactly, with a smooth ramp across the middle — identical to the Saturn's VDP2 backdrop expansion.

s32x_render

s32x_render(fvram, fb) walks the 71,680-pixel frame VRAM (320 × 224) and converts each packed RGB555 big-endian word to ARGB, writing into the output framebuffer. The big-endian read swaps bytes before extracting channels.


Test coverage

TestChecksResult
WRAM bus r/wWrite and read back from $06000000PASS
Frame VRAM r/wWrite and read back from $04000000PASS
RGB555 decodes32x_rgb555 oracle for R=31/G=0/B=0 → ARGBPASS
s32x_render pixelSingle pixel round-trip through render pipelinePASS
sh2_reset from ROMReset vector loads PC and SP from boot ROMPASS
MOV #99,R2 stepSingle SH-2 instruction executes correctlyPASS