cdirecomp's default is a byte-faithful CD-i: same OS, same device behavior, same output as CeDImu/real hardware. Enhancements (widescreen, higher internal resolution, faster load skips, input remaps) are allowed only under the rules below, which the sibling projects converged on. They exist so faithfulness and quality-of-life never get confused for one another.
Every enhancement is opt-in, off by default, and byte-identical to the faithful path when off. A standard build with all toggles off must produce the exact frames, audio, and RAM state CeDImu produces. If turning a feature off isn't byte-identical, it's a bug, not an enhancement.
The runner provides a generic, parameterised, inert-by-default capability (e.g.
"render N extra columns of MCD212 plane"). The per-game/per-OS config supplies
title-specific policy. Host preferences such as mouse capture and clock seeding
belong instead to persistent launcher/player configuration, never game.cfg.
The runner never hardcodes a title. (Mirrors segagenesisrecomp's g_game_spec /
g_game_layout split.)
Generated *.c is rebuilt from the recompiler and must never be edited (the
project's first law). Enhancements that need to touch recompiled code use a
post-regen, build-time injector: anchored-block patches or whole-function
override prologues that are idempotent and reversible, applied after emission.
Same as the recompiler discipline for everything else.
Enhancements gate on live OS/game state, not wall-clock. Anything that must stay canonical for correctness (a recorded boot trace, a regression smoke snapshot, an oracle diff) runs with enhancements OFF so it stays comparable.
DYUV/CLUT/RGB555 palettes, plane sizes, and audio rates are real constraints. An enhancement that widens a buffer must clamp in every consumer; never assume a wider intermediate silently survives a faithful stage.
When an enhancement changes emitted assets, ship the pristine faithful set and the enhanced set side by side. The faithful set is what the oracle diff uses.
Enhancement bugs are debugged exactly like faithful bugs: ring buffers, first divergence vs the faithful path, fix the class. No printf, no eyeballing.
The BIOS player shell is bootable, rendered, real-time paced, physically
controllable, and closed as a milestone. These first host-integration enhancements are implemented through
the durable per-user player.cfg contract. Enabled and disabled values pass
load/save round trips. A deterministic profile suppresses them for that run but
does not read, create, or overwrite the user's saved choices. The repository
does not yet contain a graphical launcher; its eventual UI uses this same
player-config file/API rather than inventing CLI-only state.
The opt-in persistent input.capture_mouse player preference uses the host
mouse as the CD-i relative pointing device while the SDL window has input
focus. It is saved in the user's player config so it survives future launches;
it is not a command-line-only or per-title option.
- On focus gain, hide the host cursor and enable SDL relative-mouse capture. On focus loss, shutdown, or capture disable, release capture and restore the host cursor immediately. Never trap a pointer in an unfocused window.
- Feed accumulated relative X/Y motion through a transport-neutral runtime ABI and the existing emulated-time, 25-ms IKAT packet path. Do not write the BIOS cursor position or guest RAM directly, and do not reduce mouse motion to the current keyboard acceleration steps.
- Map the primary host button to CD-i button 1 and the secondary host button to CD-i button 2; press and release transitions must both reach the guest.
- Preserve representable motion across IKAT packets (including clamping and carrying any remainder) so fast host movement is not silently discarded.
- With the option off, retain the current visible host cursor and keyboard/ controller behavior byte-for-byte. Headless, scripted-input, co-sim, and regression runs keep capture off.
Acceptance is met by CdiInputTest, PlayerConfigTest, the press/release
assertions in tools/shell_idle_smoke.py, and the end-to-end relative-motion
path in tools/bios_navigation_smoke.py (exact delta, enabled IRQ, guest drain,
visible cursor/framebuffer movement, STOP return, and RULE 0a cleanliness).
The opt-in persistent rtc.sync_host_on_startup player preference samples the
host's local civil date/time once per emulated startup and uses it to seed the
CD-i RTC before the first guest instruction. The player config persists the
preference; enabling it does not imply continuous synchronization.
- Synchronize only the DS1216 clock fields; preserve the faithful 32 KiB SRAM
persistence policy independently. Normal player runs use
nvram.binbesideplayer.cfg; deterministic profiles do not touch it. - After startup, advance solely from emulated SCC68070 cycles. Never poll or continuously correct against wall-clock time, so guest changes to the date, time, oscillator state, or hundredths remain authoritative at runtime.
- Use the portable host-local time APIs on Windows, macOS, and Linux, validate the value against the RTC's representable range, and fail clearly rather than wrap an invalid date.
- With the option off, keep the deterministic hardware-model
1989-01-01 00:00:00seed exactly. Co-sim, recorded traces, baseline smokes, and oracle comparisons keep synchronization off.
Acceptance is met by CdiNvramTest (faithful seed, valid/invalid host seed,
cycle rollover, SRAM independence, and no re-seed after a guest RTC write),
PlayerConfigTest, and tools/rtc_startup_smoke.py, which boots a real BIOS
with a temporary enabled persistent config and verifies one host-local sample,
the canonical shell STOP, and an unchanged config file.
Battery persistence itself is faithful hardware behavior, not an enhancement.
CdiNvramTest covers exact-size load/save, malformed-image rejection, atomic
replacement, and RTC independence. tools/bios_options_smoke.py proves a fresh
battery is initialized by the real BIOS, loaded on the next player boot, used
through Options/Storage/Exit, and ignored by deterministic execution.