Releases: hiroshiyui/5thPlanet
v0.6.0
Reworks the SH-2 on-chip FRT/WDT timers and interrupt recalc to Mednafen's
lazy/event-scheduled model, fixes a regression that black-screened Doukyuusei
if, and corrects the FRT prescaler. Save-state format break: v9 → v10 —
existing .sav/.state files (and cached bench snapshots) are rejected and
must be recreated. Both playable games (Virtua Fighter 2, Doukyuusei if)
remain fully playable (user-verified); the bios_boot golden hash is unchanged.
Fixed
- Doukyuusei
ifblack screen (regression since v0.4.0). A mid-batch SMPC
command dispatch (smpc.has_pending()breaking the SH-2 batch) re-anchored
run_frame's event-clamped batch grid mid-frame, so VDP2 stopped compositing
while the CPU ran normally — the game booted to an all-black framebuffer. The
batch break is removed; SMPC commands drain at the batch boundary as before.
Found by bisecting from the last working tag with a headless render probe. - FRT prescaler mapping. TCR CKS1-0 now decodes to φ/8, φ/32, φ/128,
external clock (the SH7604 mapping; Mednafen + Yabause agree) instead of the
shifted φ/1, φ/8, φ/32, φ/128 — the FRC no longer runs 4–8× too fast, and the
external-clock setting (CKS=3) freezes the counter rather than ticking it.
Changed
- On-chip FRT/WDT timers + INTC are now event-driven (advances roadmap M13
A1). The per-instructionadvance_timers(FRC/WTCNT tick) and
refresh_interrupts(INTC re-arm) are gone: the timers materialize lazily
from the elapsed-cycle delta and are scheduled by a next-event timestamp, and
the INTC is recomputed only when an input changes (timer events, on-chip
register writes, DMAC transfer-end, FTI capture). Bit-identical to the prior
model (ported in four golden-invariant stages); removes ~10 percentage points
of per-instruction timer/interrupt overhead in poll-heavy scenes. - Save-state format v9 → v10 — the FRT/WDT rework dropped the per-cycle
prescaler accumulators and added the timer epoch/next-event state. Old saves
are rejected by the version check. - Minor allocation/CPU tidy-ups (all bit-identical): cache the FRT prescaler
decode, skip a no-op cache-LRU rotate, dedup a WDT interrupt read, cache the
SAT_FTILOG/SAT_VDP1LOGdebug-env flags behindOnceLock, and skip the OSD
context clones while the menu is closed.
Added
- Game-render goldens.
doukyuusei_renders_non_blackand
vf2_renders_non_blackboot each game and assert the master runs game code
(HWRAM) and VDP2 composites a non-black frame — the game-level analogue of the
BIOS-splash golden, closing the gap that let the black-screen regression
through (the BIOS golden never exercised a game's render path). - SMPC batch-break regression guard (
pending_smpc_command_does_not_break_the_batch)
— a CI-runnable synthetic test that fails if the mid-batch dispatch is reintroduced.
v0.5.0
Grows the sdbg headless debugger into a full trace-diff workbench, and adds a
reference/developer-tooling documentation layer. No change to emulator runtime
behaviour — the bios_boot golden hash and the save-state format (v9) are
unchanged, so existing save states still load.
Added
sdbgreference master-PC trace-diff (tdiff).tdiff <ref> [frames]
runs ours through the real full-system path and compares the loop-collapsed
master PC stream against a MednafenSS_PCTRACEdump, stopping at the first
divergent PC with a both-sides context window. On a divergence it then
rewinds to a pre-trace snapshot and re-runs to a breakpoint there, printing
full registers + the stack call-chain and parking the machine at the
divergence. Knobs:TDIFF_ADD(Mednafen fetch-PC offset),PCTRACE_LO/HI.
This hosts the project's primary debugging methodology (the LLE↔Mednafen
trace-diff) inside the REPL instead of hand-diffing two trace files.sdbgmultiple breakpoints + symbols. Several register-guarded master /
slave breakpoints at once (b <addr> [ri v]adds,blists,bd <id|*>
deletes), honoured by bothcandfc. A symbol table (sym,syms <file>,
--syms=<file>) resolves names anywhere an address is expected and annotates
output (name+0xNN) in disassembly, breakpoint hits, and the call-chain.- Core support:
Sh2Entitynow holds a set of breakpoints andBpHit
carries the firing PC (Saturn::set_master_bps/set_slave_bps). Debug-only
and#[serde(skip)], so the golden and save-state format are unaffected.
Changed
- Documentation layer. Added a Developer tools catalog and a
References section (authoritative SEGA/Hitachi/Motorola manuals + the
behavioural oracles, with verified download locations) toCLAUDE.md; merged
bootstrapping.mdintosystem-architecture.md§9; recorded four ADRs
(0015 CD-block HLE, 0016 master-leads-slave stepping, 0017 reference-oracle
policy, 0018 save-state design); and recorded the M12 #9 cycle-accuracy
residual's cross-emulator corroboration (the post-spin-up seek is Mednafen-only;
MAME and Yabause match ours — left as-is per the stop rule).
v0.4.1
A documentation and internal-cleanup release — no behavioural change. The
save-state format (v9) and the bios_boot golden hash are both unchanged, so
existing save states still load.
Changed
- NBG renderer cleanup. Hoisted the VDP2 NBG per-dot register decode (mosaic
MZCTL, line-scrollSCRCTL/LSTAn, vertical cell-scroll table addressing)
out of the per-dot path into the once-per-frameNbgCtx, matching the existing
FrameCtxhoist pattern. Bit-identical (golden + all 597 saturn tests
unchanged); a clarity/redundancy cleanup, not a measurable perf win on the
benched scenes (profiling confirmednbg_layer's cost is memory-bound
sampling, not register decode). - Documentation overhaul.
- Merged
bootstrapping.mdintosystem-architecture.mdas a new §9, so the
reset→splash→game-boot sequence lives beside the chip→module map; refreshed
its status to M11-complete. - Synced
system-architecture.mdto the current M11/M12/M13 reality — the M12
BSC bus-timing model, CD-DA via the SCSP EXTS inputs, Shuttle Mouse support,
the render-pipeline worker / audio-paced loop, and corrected test counts. - Added module/struct/function
///doc comments across every crate
(saturn,sh2,m68k,scu_dsp,physdisc,jupiter) via a
docs-engineering source-comment audit; the skill now enforces this.
- Merged
- Four retroactive ADRs recording settled, load-bearing decisions that the
code already relied on: the CD-block HLE exception (0015), master-leads-slave
SH-2 stepping (0016), the reference-oracle policy (0017), and the save-state
design (0018).
v0.4.0
Adds the OSD disc-image browser and fixes VF2 input lag, plus an M13 A1
timing-accuracy refinement and a documentation overhaul. Backwards-compatible:
the save-state format (v9) and the bios_boot golden hash are both unchanged,
so existing save states still load.
Added
- Disc-image browser in the OSD. Esc → Load Disc… opens a filesystem
browser: navigate directories and pick a.cue/.iso/.ccd; selecting
an image loads it and power-cycles to boot the game. The menu stays core-free
andfs-free (the frontend supplies the directory listing); a scrolling
viewport handles large directories. - Raster batch-drain jitter probe (M13 A1, dev instrumentation) — an
observer-only check confirming VCNT/TVSTAT reads are never stale, the evidence
that the HBlank clamp edge can stay deferred.
Changed
- SMPC command dispatch is now an exact mid-batch event (M13 A1). A queued
command breaks the scheduler batch so it dispatches within one instruction of
the COMREG write, matching the LLE reference. Golden-safe — a timing-fidelity
improvement, not a behaviour regression. - Documentation overhaul. Normalised all roadmap tables to one format;
consolidated Tier G (residual reference-audit items) with status markers;
retired the point-in-time MAME/Mednafen cross-reference audits, folding their
durable residue intobootstrapping.md§C.1 and roadmap Tier G; added an
Enhancements section (EN1: GLSL shader presets); and synced the disc
browser into the feature docs.
Fixed
- Input lag / "not steady 60 fps" in VF2. The emu-thread frame pacer was
chronically collapsing ~1/3 of rendered frames ("run 2, show 1") because it
chased an audio-reserve target it structurally couldn't reach. It now renders
every game-frame in normal play, collapsing only when the audio reserve
has genuinely drained toward an under-run — smoother motion and lower input
latency. TheSAT_MAX_BURSTenv var becomes the catch-up ceiling (default 2;
=1disables catch-up).
v0.3.1
A small, behaviour-preserving release: a save/load latency optimization plus documentation sync. No emulation behaviour changes — the boot golden hash and the save-state format (v9) are unchanged, so existing save states still load.
Changed
- Disc fingerprint is cached at construction instead of re-hashed on every
save_state/load_state. The save-state media-identity check ran a full FNV-1a over the entire disc image (a ~1.5–1.7 s stall per quicksave and per quickload on a 600–700 MB game image); the hash is now computed once when the disc is inserted and stored in a field, so quicksaves and quickloads are effectively instant. The hash value is unchanged — bit-identical media identity, boot golden and save-state round-trip unaffected.
v0.3.0
Milestone 12 (whole-system cycle accuracy) is complete. The three
remaining timing models landed — the VDP1 draw-duration walk, the exact
B-bus deferred-write serialization, and the SCU A/B/C-bus DMA arbitration —
bringing the whole-system BGM-phase metric from a +182 seq-tick divergence
to within ~1% of the LLE reference (4450 vs 4497). Virtua Fighter 2 and
Doukyuusei if remain fully playable (user-verified under the new timing).
Save states: format v9 (older states are rejected, not migrated).
Added
- M12 task #6 — VDP1 draw-duration model: a Mednafen-faithful
draw-cycle walk (vdp1/timing.rs) runs alongside the instant
rasterisation and sizes every plot — per-command fetch/setup, per-span
clip costs, per-pixel charges with anti-aliasing and the leave-clip early
exit, and the fractional refresh-overhead scaling.EDSR.CEFand the
sprite-draw-end interrupt now land when the reference's do (the BIOS
CD-player panel draw spans ~258k cycles ≈ half a frame, matching the
oracle within 0.3%). The VDP1 clip/local registers are modelled as the
persistent hardware state they are, serialized with the machine. - M12 task #8 residual — exact B-bus deferred-write serialization: an
SH-2 B-bus write hands off in +2 CPU cycles and posts its device-side
completion (SCSP +17/+13, VDP1 +9/+1, VDP2 +3/+1 per 16-bit half) on a
separate timeline that only the next B-bus access waits out; B-bus reads
are always two 16-bit halves (VDP1/VDP2 reads were undercharged by half). - M12 task #8 residual — SCU A/B/C-bus DMA arbitration: DMA-timeline
costs corrected to the reference's per-access values (B-bus VDP1/VDP2 1,
SCSP 13 per 16-bit access; C-bus SDRAM read 6 per word, write free — the
old flat values overpriced DMA writes up to 11×), and a C-bus-endpoint
transfer now halts both SH-2s for its paced duration (the SCU owns
the CPU bus) while a pure A↔B transfer halts neither. This was the
dominant lever for the M12 phase residual.
Changed
- The SH-2↔VDP1 RW draw-slowdown is now opt-in (
set_rw_slowdown,
default off), matching the reference where it is a per-game hack
(applied to e.g. Virtua Fighter 1 — not VF2, not the BIOS). - Save-state format bumped to v9 (
Vdp1draw-timing state +
BusTiming::bbus_write_finish); older states are rejected on load. - The M12 residual (~1%) is documented and closed under the stop rule: a
discrete ~14-frame recognition-handshake offset plus a 68k-gated mailbox
poll loop — oracle-approximation territory, recorded as follow-up
threads in the roadmap.
v0.2.0
Milestone 9 (frontend OSD) is complete and Milestone 12 task #8 (the
per-access bus-timing model) landed; a chain of user-verified accuracy fixes
followed — VF2's "phantom ring-out" floor displacement, an audio-pacing
starvation, and a CD report-tearing bug found booting Panzer Dragoon Zwei.
Save states: format v8 (older states are rejected, not migrated).
Added
- M9 complete — frontend OSD: a persisted config file
($XDG_CONFIG_HOME/5thplanet/jupiter.toml: scale, fullscreen, region,
cartridge, keymap; CLI flag > config > autodetect), a Settings →
Controller screen with press-to-bind keyboard remapping (Reset Defaults
included), and a Settings → BIOS screen that power-cycles into any
sibling 512-KiB image, re-keying the save files. - Game-controller support: hot-plug SDL GameController (XInput/evdev)
merged into the port-1 pad with a fixed Xbox-style mapping
(A/B/C = X/A/B, X/Y/Z = Y/LB/RB, L/R = triggers, D-pad or left stick),
plus controller navigation of the OSD. - M12 task #8 — per-access BSC bus-timing model (a faithful Mednafen
port, implemented bus-side): CS0 as a 16-bit bus with per-transaction
costs, CS3 SDRAM read/write with the array-busy window, cache-line fills
as one burst (AccessKind::LineFill), the SH-2 write buffer, bus
turnaround, A-bus cartridge cost from live ASR0, and a shared bus
timestamp giving CPU↔CPU arbitration. Thebios_bootgolden was
unchanged; the BGM-phase metric moved toward the reference.
Fixed
- VDP2 rotation — the VF2 "phantom ring-out": in 640/704-dot hi-res
modes the rotation layer renders at normal dot resolution (each rotation
dot spans two display dots); ours stepped per display dot, compressing
the floor 2× toward screen-left. Also a one-bit sign-mask typo that
corrupted rotation viewpoint X values in [4096, 8191]. - Audio pacing: the SCSP is fed the master's actual cycle advance (the
batch-edge overshoot was silently dropped — fights starved the audio
reserve and felt slowed), delivered in fixed 256-cycle chunks so the
Timer-B rate stays locked to the reference (88.000 samples/tick); SCSP
output is resampled when the audio device opens at a non-44.1 kHz rate. - CD-block report tearing: the periodic status report now holds until
the host consumes it by reading CR4 (Mednafen'sResultsReadgate) —
previously it recomposed per sector during playback and could tear a
reader's CR1..CR4 sequence (found via Panzer Dragoon Zwei's BIOS
disc-validity check).
Changed
- Save-state format v6→v8 across the bus-timing,
ResultsRead, and
SCSP-feed changes; older states are rejected with a clear error. - Debug tooling: CD command-log ring widened to 8192 entries; the VF2
PC-trace and disc-content probes are now CUE-parameterized.
v0.1.0
First release. 5thPlanet is an accuracy-first SEGA Saturn emulator in Rust:
two commercial games are fully playable — Virtua Fighter 2 (steady 60 fps:
title → menus → character select → full 3D fights to the K.O. screen, with CD-DA
BGM and in-fight SFX at the hardware mix balance) and Doukyuusei if (640×224
hi-res, graphics, SFX, and voices all working) — on the pure real-BIOS LLE path,
with no game-specific hacks. Milestones M1–M8, M10, and M11 are complete;
1058 workspace tests, ~85 % line coverage.
Added
- SH-2 (SH7604) ×2 core (
sh2crate,no_std): full ~142-op ISA, 5-stage
pipeline cycle model (load-use/address-gen interlocks, multiply/divide
latency), delay-slot machinery, 4 KiB 4-way cache (write-through, associative
purge), exceptions/interrupts, and on-chip peripherals (INTC, DIVU, FRT, WDT,
working DMAC, BSC master/slave bit). - Saturn system layer (
saturncrate): typed-region bus with per-region
wait states (Mednafen-derived B-bus/A-bus costs), dual SH-2 in
master-leads-slave interleave with per-instruction SCU interrupt sampling,
and a deterministic event-driven scheduler clamped to peripheral-event edges. - SCU: 3 DMA channels (direct + indirect, strides, hardware start factors,
cycle-stealing, 27-bit address folding), interrupt aggregation with fixed
vectors, Timer 0/1, and the embedded SCU-DSP (scu_dspcrate, full VLIW
core). - SMPC: command set with hardware codes, staged INTBACK (status +
peripheral phases) at reconciled busy timings, live RTC, region selection,
and per-port peripheral selection — digital pad and Shuttle Mouse. - VDP1: full command-list plotter (all primitives and colour modes,
gouraud, mesh, clipping), TVM 8bpp framebuffer + DIE/DIL double-interlace
plotting, framebuffer erase/swap (FBCR), cycle-accurate draw-end IRQ. - VDP2: multi-layer compositor — NBG0–3 + RBG0/1 rotation (per-dot
coefficients, RDBS bank grants, RPMD 0–3) + the VDP1 sprite layer, with
colour calculation (incl. extended 3-layer), windows (rect/per-line/sprite),
special priority/colour-calc, colour offset, mosaic, line/back screens,
per-line scroll/zoom, vertical cell scroll, VRAM cycle-pattern fetch gating,
CRAM modes, and hi-res output (320/352/640/704 × 224/240/256, ×2 interlace). - SCSP + hosted MC68EC000 (
m68kcrate, full ISA + exceptions):
32-slot PCM/FM engine (ADSR EG, LFO, slot-to-slot FM, slot monitor), 128-step
effect DSP, CD-DA through the EXTS inputs at the game-programmed mix, MVOL,
44.1 kHz stereo output. - CD-block (HLE): host command interface, 200-block buffer pool with 24
filters/partitions, Mednafen-faithful drive-phase model (BUSY/SEEK/PLAY,
recognition spin-up, periodic reports), 75 Hz read pump with read-ahead,
data transfer (FIFO + SCU-DMA port), ISO9660 filesystem, disc
authentication, and CD-DA playback (FAD and track/index Play forms, 1×
audio pacing, byte-swapped-rip detection). - Disc sources: ISO / CUE-BIN / CCD-IMG image loaders and live optical
drives via the feature-gatedphysdisccrate (libcdio, ADR-0009). - Cartridge slot: Extension DRAM (1 MB / 4 MB), battery backup-RAM, and
game-ROM carts with the probed cart-ID byte. - Save states: full deterministic machine snapshot/restore (bincode,
versioned header, external media referenced by fingerprint, not embedded). - Internal backup RAM: hardware-faithful odd-byte packing, pre-formatted,
persisted to a host.bupfile. - SDL2 frontend (
jupiter): audio-paced main loop with a render-pipeline
worker thread, scanline-band parallel compositing, in-window OSD menu
(save/load slots, reset, disc eject/insert, graphics/region/cartridge
settings), save-state hotkeys, mouse capture (F10), and headless mode. - Interactive debugger (
sdbg): gdb-style REPL — master/slave
register-guarded breakpoints, bus-level memory probe, watchpoints, SH-2 +
68k disassembly and PC traces, HIRQ-edge trace, CD-block/SCSP state,
save-state rewind. - Cycle-accuracy infrastructure: PC-trace-diff and signal-scope harnesses
against the MAME/Mednafen references, ROM regression goldens, and the
committedbios_bootsplash golden.