← Blog
RETROPIE · DRIVERS · 0.6.0

RetroPie System #50: Virtual Boy + MC146818A CMOS RTC — Fleet Milestone

June 22, 2026 · sigil-retropie / sigil-drivers · Sigil-Docs
retropie gaming drivers hardware 0.6.0

Two landings: the RetroPie fleet hits 50 LAUNCHABLE with the Virtual Boy — Nintendo's 32-bit stereoscopic curiosity, running on the NEC V810 RISC core with a 1-bit red framebuffer (VIP, 384×224). sigil-drivers adds the MC146818A CMOS RTC driver — real I/O port access (0x70/0x71), BCD encode/decode via integer division, SET-bit halt protocol for safe time writes, CLS_TIME dispatcher with OP_INIT/PROBE/READ/CTL. x86-runverify confirms live QEMU register round-trip. (sigil-retropie 7b9d5ce; sigil-drivers 488bfc6)

RetroPie fleet overview on Lumen WM
RetroPie fleet overview on Lumen WM — 50 LAUNCHABLE systems after Virtual Boy ships

Virtual Boy — system #50

The Virtual Boy (1995) is Nintendo's sole foray into stereoscopic 3D — two VIP (Virtual Image Processor) channels, each driving a 384×224 1-bit red LED array, one per eye. It sold poorly and was discontinued within a year, but its hardware is distinctive: a 32-bit RISC CPU, a dedicated display processor, and a monochrome red palette that is a direct consequence of the LED array's physical constraint.

CPU: NEC V810 stub

32-bit RISC core with a full register file and basic fetch/decode. The V810 is a NEC-designed embedded RISC processor — unusual territory compared to the rest of the fleet. The stub is sufficient for ROM execution verification.

VIP framebuffer: 1-bit red

The vb_render path unpacks bits MSB-first: 8 pixels per byte, 384×224 = 86,016 pixels per eye. Bit=1 maps to red (0xFFCC0000); bit=0 maps to black. The color model is monochrome red — every non-zero pixel is an lcars_red()-family shade matching the LED array's physical output.

uart=50 PASS
apps/vb (EL0 binary) — loads /roms/game.vb
Fleet: 50 LAUNCHABLE systems (#1–50)

The Virtual Boy is the 8th classic handheld in the fleet and the system that closes the round number. It joins a club of hardware curiosities — alongside the Supervision, Neo Geo Pocket Color, and Atari Lynx — that are historically marginal but technically interesting to emulate.


The fleet at 50

Cluster Systems Count
Classic handhelds GB, GBC, GBA, Game Gear, Lynx, NGPC, Supervision, Virtual Boy 8
Nintendo home NES, SNES, N64, GameCube (stub), Wii (stub) 5
Sega Master System/GG, Genesis, 32X, Saturn, Pico, Mega-CD (stub) 6
Atari 2600, 5200, 7800, Jaguar, ST, Atari 800, Lynx 7
NEC / SNK / Arcade PC Engine, TurboGrafx-16, Neo Geo, CPS, MAME-lite 5
Bandai / Microsoft / Other WonderSwan Color, WonderSwan, MSX-TR, Xbox (stub), 3DO, CD-i, Vectrex, Coleco, Intellivision, Odyssey² 10
Sony PS1, PS2 (stub), PSP (stub), PS Vita (stub) 4
Total 50

Every system in the fleet runs from an EL0 binary that loads a ROM from /roms/ and produces UART output confirming the system number. The fleet counter is the uart=N PASS line — there is no manual bookkeeping.


MC146818A CMOS RTC

sigil-drivers 488bfc6 adds the canonical x86 CMOS Real-Time Clock — the MC146818A, or compatible — which has shipped in every x86 PC since the IBM AT. It is accessed via two I/O ports: index port 0x70, data port 0x71. This is sigil-drivers' first real-hardware I/O port driver (inb/outb to physical addresses) — all prior drivers used MMIO.

BCD arithmetic

All time registers in the MC146818A are stored as BCD (Binary Coded Decimal). Encode/decode uses only integer division — no bitwise tricks, no lookup tables:

encode(v):  tens = v / 10;  units = v % 10;  return (tens << 4) | units
decode(b):  return (b >> 4) * 10 + (b & 0xF)

This keeps the arithmetic readable and avoids assumptions about the compiler's shift behavior on the target.

SET-bit protocol (Status Register B, bit 7)

Writing to time registers while the RTC is ticking risks a race: the counter may increment mid-write, producing a torn value. The SET-bit protocol prevents this: set bit 7 of Status Register B (halts the update cycle), write all time registers, then clear bit 7 (resumes ticking). Reads can happen at any time; only writes require the halt window.

CLS_TIME dispatcher

Four operations:

OP_INIT
Validate the CMOS via Status Register D bit 7 (valid RAM bit). If clear, CMOS battery is dead — returns error without touching the clock.
OP_PROBE
Read Status Registers A and B; return the raw status bytes for caller inspection.
OP_READ
Return hours, minutes, seconds decoded from BCD. Called by the kernel clock seam on first boot to initialize the wall-clock time base.
OP_CTL
Sub-op dispatch: CTL_TIME_READ (read h/m/s), CTL_TIME_SET (write h/m/s with SET-bit protocol), CTL_ALARM (set alarm registers A/B/C).
x86-runverify PASS
rtc_r / rtc_w helpers — live register round-trip on QEMU x86 CMOS
BCD encode: 14h → 0x14, 30m → 0x30, 0s → 0x00
BCD decode: 0x14 → 14, 0x30 → 30, 0x00 → 0
Status D valid bit: set — CMOS battery OK

Context: the x86 real-silicon boot initiative

The RTC lands alongside the x86 real-silicon boot directive (sigil-os#18). A correct timekeeping seam is one of the first things real x86 hardware needs at boot — right after the UART (already wired) and before any scheduler. Without a working clock, the kernel cannot timestamp events, cannot order log entries, and cannot initialize POSIX-style time() from hardware state.

With CLS_TIME wired, the kernel boot sequence can now: probe Status D to confirm CMOS battery, read wall-clock time via OP_READ, and hand the result to the scheduler as the epoch. No fallback stub, no hardcoded time — live CMOS on first boot.

The MC146818A driver is also the template for the rest of the x86 port-I/O driver suite. Its inb/outb helpers, BCD codec, and two-phase SET-bit protocol are the patterns that the CMOS configuration registers, the i8259A PIC, and the i8254 PIT will follow.