Conversation
Add HIGH_IMPACT_FIXES.md with a phased plan for Dreamcast port improvements. Restore blit_to_screen using the SH-4 Store Queue path on Dreamcast and a simple pixel-copy fallback on other non-PSP builds, fixing savestate preview thumbnails in the menu. Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Port x86-style dynarec helpers and instruction macros to the SH-4 backend. Add sh4_helpers.c for ALU, shift, load, CPSR/SPSR, and SWI helpers. Expand sh4_emit.h with SH-4 code generation, conditional branches, idle-loop elimination, and cycle accounting. Complete sh4_stub.c with SMC-aware stores and execute_arm_translate entry. Enable dynarec on Dreamcast in main.c. Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Phase 1 polish: - Pass PC into sh4_update_gba via generate_update_pc_reg on SH-4 - Add generate_update_pc_reg to MIPS emit for cpu_threaded compatibility - Fix execute_swi return type; remove noisy Dreamcast config debug prints Phase 2 interpreter fixes (cpu.c): - Apply LDM/STM writeback after the transfer loop so STM preserves the original base value when the base register is in the list - Skip load writeback when the base is in the list (ARM7TDMI behavior) - Honor the s_bit (^) flag by accessing user bank R8-R14 in privileged modes Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Phase 1 audit fixes: - Fix reg[16+] load/store, branch patching, and cycle reload after sh4_update_gba - Tail-jump from sh4_update_gba on PC change; pass instruction PC to execute_store_* - Add shift-reg macros, cmp/str compares, Thumb PC branch mov, swi_hle_div fix Phase 3: - Unify 32KB gamepak swap pages on Dreamcast (was 8KB count vs 32KB I/O) - DC ROM buffer tries 16/12/8/4MB with malloc fallback - Add host tests for div remainder and page math Co-authored-by: nerdretro <awest813@users.noreply.github.com>
- Add README.md for the Dreamcast port (build, disc layout, controls) - Rename COPYING.DOC to LICENSE (GPL v2) - Remove outdated PSP-focused readme.txt - Point build.txt at README.md Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Squashes phased PR work into a single changeset on dreamcast. Phase 0: Restore blit_to_screen for savestate/menu thumbnails (SQ + fallback). Phase 1: SH-4 dynarec — helpers, emit, instr macros, SMC-aware stores, idle loops. Phase 2: LDM/STM interpreter fixes (writeback timing, user bank ^ suffix). Phase 3: Unified 32KB gamepak paging; DC ROM buffer 16/12/8/4MB fallback. Docs: README.md, LICENSE (GPLv2), HIGH_IMPACT_FIXES.md roadmap. Tests: host-side phase1_helpers_test. Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Co-authored-by: nerdretro <awest813@users.noreply.github.com>
- Route startup/DMA/SDL debug prints through gpsp_debug_printf (silent unless -DGPSP_DEBUG); keep user-facing BIOS and ROM load errors - Fix Gameshark v3 I/O opcode extraction; add ROM patch, button-gated, and master-hook cheat handling - Call check_for_interrupts() after CPSR mode changes in sh4_helpers - Add phase4_cheats_test; update HIGH_IMPACT_FIXES.md and README Co-authored-by: nerdretro <awest813@users.noreply.github.com>
execute_store_cpsr now returns the IRQ vector when unmasking interrupts, and dynarec emission branches to it via arm_psr_store_cpsr_post(). execute_spsr_restore uses the same sh4_take_pending_irq helper so MOVS PC correctly redirects through 0x18 instead of leaving IRQ state half-applied. Adds host tests for the IRQ decision logic and source contracts. Co-authored-by: nerdretro <awest813@users.noreply.github.com>
- Gameshark v1 IF codes (0xD/0xE) with EQ/NE/LE/GE and range skip counts - PAR v3 conditionals on 00000000 control lines and 0x08-0x38 prefixes - Dynarec emits process_cheats at cheat_master_hook; flushes caches on change - x86 execute_store_cpsr returns IRQ vector with dynarec branch (matches SH-4) - Host tests and docs updated Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Cheat conditionals, dynarec hooks, SH-4/x86 CPSR IRQ fixes
Replace the partial PAR3 conditional parser with SkyEmu's stack-based gba_run_ar_cheat model: nested IF/ELSE/ENDIF, ROM patches, fill codes, button-gated writes, and full-width IF comparisons. Adds THIRD_PARTY_NOTICES.md with MIT attribution. Co-authored-by: nerdretro <awest813@users.noreply.github.com>
- PAR v3 slowdown (00000000 0800xx00) re-runs cheat list xx times per cycle - DEADFACE lines reseed Gameshark/PAR decryption during cheat file load - Track up to eight master hooks; dynarec uses cheat_pc_is_hook() - Add mGBA MPL attribution for reseed tables; extend host tests and docs Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Cheat conditionals, dynarec hooks, CPSR IRQ dispatch, PAR gaps
…3-2878c28d-7ac1-40c2-9a37-508949c51376 Audit and fix core compilation issues for non-Dreamcast platforms
* Phase 5 user readiness audit for Dreamcast release - Show on-screen fatal errors for missing BIOS, ROM buffer failure, and gamepak load errors on Dreamcast (path, MD5, wait for Start) - Guard against NULL gamepak_rom after init_gamepak_buffer on DC - Fix command-line ROM load error to report argv[1] - Correct README savestate filenames to .0.svs through .9.svs - Gate host SDL F2 palette dump behind GPSP_DEBUG - Add phase5_user_readiness_test contract checks Co-authored-by: nerdretro <awest813@users.noreply.github.com> * Ignore phase5_user_readiness_test binary in tests/.gitignore Co-authored-by: nerdretro <awest813@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: nerdretro <awest813@users.noreply.github.com>
- Add GPSPDC_VERSION (0.9.1-dc) and show it on the empty-ROM menu splash - Route in-menu ROM load failures through gpsp_gamepak_load_error on DC - Document hardware/Flycast smoke tests in HARDWARE_SMOKE_TEST.md - Add GitHub Actions workflow to run make -C tests test - Add phase6_release_verification_test contract checks - Update HIGH_IMPACT_FIXES.md and README with Phase 6 roadmap entry Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: nerdretro <awest813@users.noreply.github.com>
* Add Dreamcast stable build CI and build contracts Cross-compile gdC.elf in GitHub Actions using the KOS Docker toolchain, add a romdisk placeholder for fresh checkouts, and document a local docker build helper. Host contract tests verify source paths and CI. Co-authored-by: nerdretro <awest813@users.noreply.github.com> * Fix Dreamcast CI to use docker run with KOS entrypoint Co-authored-by: nerdretro <awest813@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: nerdretro <awest813@users.noreply.github.com>
P0 correctness: - Remove duplicate bios_rom/bios_read_protect definitions in memory.c - Add dc/romdisk/.gitkeep for KOS genromfs on fresh checkouts Large ROM / RAM (from PR #19): - Smaller DC translation caches in cpu.h (~1.25 MB saved for ROM buffer) - Adjacent-page prefetch in gamepak paging Input / menus (includes PR #20): - DC gamepad_config_map honored in gameplay; Y opens pause menu - Fix joystick A/B/X/Y bit mapping; default X -> GBA B - Fix RAPIDFIRE_R handling on PSP and DC paths - Menu save on close, ROM browser hints, grey out unavailable actions Other polish: - gpsp_video_init_error for SDL/video init failures on DC - Cheats load via relative path after fs_chdir - DC-specific gamepad help and GD-ROM backup wording - README large-ROM section; gitignore CDI build artifacts - Extended phase3/phase8 contract tests Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Co-authored-by: nerdretro <awest813@users.noreply.github.com>
- Cap backup/save reads and bound gamepak open paths; validate GD-ROM fread via file_read_ok; zero-fill failed ROM pages - Guard ZIP entry names; ROM browser malloc/realloc failures; copy_screen NULL checks; refuse emulation without a loaded ROM - Serialize savestate load/save and direct sound mixing with sound_mutex; lock reset_sound buffer clears - Repair flash_bank_ptr after savestate load; iterative reload (no recurse) - Cap dynarec external exits and translation redo loops; fatal on NULL translation target in sh4_stub - Add phase9_stability_test contract suite Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Resolve conflicts in README, smoke-test-results, and phase3 tests. Includes romdisk placeholder, bios_rom dedup, joystick mapping, video init fatal, rapidfire R, and cheats path fixes. Co-authored-by: nerdretro <awest813@users.noreply.github.com>
Resolve conflicts in memory.c paging (keep file_read_ok guards), README, and smoke-test-results. All phases 1-9 tests pass. Co-authored-by: nerdretro <awest813@users.noreply.github.com>
ARM LDM/STM with PC in the register list delegated to execute_arm_block_memory() but the emitted epilogue still branched via stale rv instead of the loaded PC. Load REG_PC and use dual indirect branch so Thumb targets work via block_lookup_address_dual. Match raise_interrupt() in sh4_take_pending_irq: set bios_read_protect, wake from halt, and mark CHANGED_PC_STATUS when unmasking a pending IRQ from CPSR/SPSR restore paths. Store the full loaded PC value (including Thumb bit) in the block memory helper to match the interpreter. Co-authored-by: nerdretro <awest813@users.noreply.github.com>
The Dreamcast dynarec re-entered translated blocks with ordinary C calls (`((void (*)(void))target)()`) from sh4_update_gba, the indirect-branch helpers, and the SMC/alert store paths. Translated blocks never return, so every IRQ dispatch, halt wake-up, SMC flush, and indirect branch leaked a stack frame. The SH-4 stack grew without bound until it overflowed and corrupted memory mid-game. Re-enter translated code the way the x86 backend does: capture the stack base in execute_arm_translate and reset r15 to it on every (re-)entry via a single noreturn sh4_dispatch_block() trampoline that also reloads r12 (reg base) and r13 (cycle counter). sh4_update_gba, the indirect-branch helpers, and the store helpers now tail-dispatch through it instead of nesting C frames, and the live cycle counter is threaded explicitly: - indirect-branch helpers take (address, cycles); emitter passes r13 in r5 - store helpers take (address, value, pc, cycles); emitter passes r13 in r7 - sh4_lookup_pc(cycles) replaces the leaking parameterless helper Also fix block linking: external/long branches were patched to a 12-bit `bra` (+/-4 KB) and forced a full retranslation (return -1) whenever the target was farther, which could livelock on large caches. Block exits now reserve a full absolute-jump slot (SH4_EMIT_LONG_BRANCH_FILLER); near targets are rewritten to bra/nop and far targets fill the slot's literal, restoring the conventional record-at-emit / patch-at-link structure. Flush the operand cache before invalidating the instruction cache so freshly emitted code is written back to RAM before refetch (dcache_flush_range + icache_flush_range), and gate the SWI trace emission behind GPSP_DC_RUNTIME_TRACE. Tests: extend sh4_emit_encoding_test with near/far long-branch patch checks and update sh4_integration_contract_test for the new dispatch discipline. All host test suites pass. https://claude.ai/code/session_01W5CticHkv7cGVuDxV9t1CT
Two correctness bugs found while auditing the SH-4 backend. 1. Inverted IRQ branch on CPSR store (severe). arm_psr_store_cpsr_post emitted `tst r0; bf _skip_irq`, which branches past the IRQ dispatch when r0 != 0 -- the exact case where execute_store_cpsr returns a vector and the IRQ must be taken. Worse, on the common path (r0 == 0, no pending IRQ) it fell through and dispatched with r4 = 0, branching into a null/BIOS block on essentially every MSR that writes the control byte. The x86 backend skips when the result is zero (`jz`); the SH-4 skip must therefore trigger on T == 1 (r0 == 0). Use COND_SKIP_T. 2. Conditional skip limited to +/-254 bytes. The ARM per-condition block header skips a whole run of same-condition instructions with a single 8-bit bt/bf; generate_branch_patch_conditional truncated the displacement to 8 bits, so any run whose body exceeded ~254 bytes was patched to a bogus target. Replace the lone bt/bf with SH4_EMIT_COND_SKIP_T/F: a short bt/bf that hops over a 12-bit bra (reach +/-4 KB), and patch the bra. The same primitive now backs the cycle-budget skip and the IRQ skip, so the patch path is unified (generate_branch_patch_conditional == bra patch). Polarity of the existing condition fillers (verified correct against the EQ path and the working cycle-budget skip) is preserved exactly. Tests: update the filler-polarity encoding test for the new tst/bf/bra/nop shape, add near and far conditional-skip patch checks, and add contract assertions locking in the COND_SKIP_T IRQ skip. All host suites pass. https://claude.ai/code/session_01W5CticHkv7cGVuDxV9t1CT
Three interpreter-parity bugs in the long-multiply and block-store paths. 1. UMLAL/SMLAL accumulator high word passed in the wrong register. execute_mul_long_regs(rm, rs, acc_lo, acc_hi) takes its arguments in r4/r5/r6/r7 per the SH-4 ABI, but arm_multiply_long_add_yes loaded rdhi into s0 (r14), leaving r7 garbage: every long multiply-accumulate corrupted the high word. Add the missing sh4_reg_a3 (r7) alias and load rdhi into a3. 2. SMLAL used an unsigned product. Both generate_multiply_s64_add and generate_multiply_u64_add called the same helper, which computed (u64)rm * (u64)rs. The interpreter (and ARM) use the signed 64-bit product for SMLAL. Split the helper into execute_mul_long_regs_s64 (signed product) and _u64 (unsigned); the accumulate is bit-identical in u64 math. 3. STM with PC in the register list stored insn_pc + 4. The interpreter advances pc by 4 (arm_pc_offset) and stores pc + 4, i.e. insn_pc + 8, and the x86 backend stores pc + 8 via generate_load_reg_pc(a1, 15, 8). Store insn_pc + 8 for parity. Also audited this pass, no change needed: shift-by-register uses the full 32-bit Rs in both interpreter and helpers (shared gpSP divergence from hardware's bottom-byte rule); SWP alignment/rotation matches the interpreter's read_memory32 path; halt processing is correctly confined to the store-alert loop (x86 write_epilogue parity); the stale CHANGED_PC_STATUS left by direct CPSR/SPSR IRQ dispatch costs only one redundant block lookup at the next update_gba and matches the documented raise_interrupt behavior. Contract tests lock in the a3 load, the signed product, and the pc+8 store. All host suites pass. https://claude.ai/code/session_01W5CticHkv7cGVuDxV9t1CT
The dreamcast-build job was a false green. dc/gdC.elf was committed (and only
later gitignored), so `test -f dc/gdC.elf` always passed against a stale
binary, while ./scripts/dc-build.sh ran `make` with no target -- which
resolves to the KOS `subdirs` goal from Makefile.rules ("Nothing to be done
for 'subdirs'") and builds nothing. The SH-4 cross-compile had therefore
never actually run in CI.
- dc-build.sh now defaults to the real `all` target when invoked with no
arguments (overridable, e.g. `dc-build.sh clean`).
- Remove the committed dc/gdC.elf (already in .gitignore) so the verify step
is meaningful, and add a CI step to delete any stale ELF before building.
- Contract tests assert the build script targets `all` and CI removes the
stale ELF, so the false-green cannot silently return.
https://claude.ai/code/session_01W5CticHkv7cGVuDxV9t1CT
Fix SH-4 dynarec: stack leak, branch range, IRQ polarity, and multiply/STM parity
…ed roadmap Audit pass over dc/sh4_stub.c, dc/sh4_helpers.c, dc/sh4_emit.h, and dc/sh4_instr.inc against the in-tree MIPS stub and x86 backend: - Pin sh4_dispatch_block operands to r0-r3 so the register allocator can never place an input in r12/r13/r15, which the asm overwrites before the jump. - Store helpers dispatch CPU_ALERT_IRQ straight to sh4_lookup_pc with the live cycle counter (MIPS irq_alert parity) instead of running the update_gba halt loop, which billed the rest of the current video/timer period early on every DMA-completion IRQ. - collapse_flags() before slow-path write_memory* so raise_interrupt() snapshots current NZCV into SPSR_irq; handlers no longer return with stale flags after DMA IRQs. - ARM conditional headers estimate the same-condition run size from scan data and emit a bt/bf hop over an absolute-jump slot when the 12-bit bra might not reach; the conditional patch dispatches on slot shape and fails loudly via gpsp_dynarec_fatal_error instead of silently emitting a truncated bra. - MUL/MULL results flow through r0 and the reg[REG_SAVE]/[REG_SAVE2] mailbox instead of an unsound 'leave it in r4/r5' inline-asm contract. - Drop dead duplicate macros in sh4_emit.h that sh4_instr.inc redefines; skip cycle-update emission when the balance is zero. - x86 host backend: fix arm_psr_store_finish shadowing (MSR CPSR called execute_store_spsr) and set bios_read_protect in x86_take_pending_irq. - CI workflows also trigger on claude/** branches. - Tests: far-skip shape/polarity/patch coverage, conditional-header run-length selection (expands the real macro), range backstop, cycle-update guard, and updated stub/helper/emit contracts (64 checks). - Docs: new ROADMAP.md unifies all remaining work from the phase docs, smoke-test checklists, validation track, and audit findings; HIGH_IMPACT_FIXES.md records this pass as Phase 11. https://claude.ai/code/session_01GHrZB3u4SVBVsadogo6kgG
- autoload.txt now points to the bundled license-clean DangerousXmas.bin instead of the missing Tekken Advance ROM, so a clean-checkout CDI boots straight into a working game (smoke-test #5) rather than failing on first boot. - Add scripts/flycast-test.md: build steps (KOS ELF -> dc.sh CDI), known-good Flycast config, and expected result per smoke-test row. - smoke-test-results.md: pre-fill the known-good Flycast environment block and link the runbook (ROADMAP A1 / E3). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCEpbuNCctwevnBDC3c1vn
A clean checkout could build a CDI whose autoload.txt named a ROM not in the tree, so the disc failed on first boot. Guard the whole class: - scripts/check-disc.sh: validate BIOS presence/size/MD5, game_config, and that any autoload ROM actually exists in dc/cd/gbaDC/. - dc/dc.sh: run the preflight (after config sync) so a broken disc cannot be packaged into a CDI. - tests/dc_build_contract_test.c: new disc-layout contract fails CI if the committed autoload ROM is missing or dc.sh drops the preflight. - flycast-test.md: document running the preflight before the cross-compile. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCEpbuNCctwevnBDC3c1vn
The three 06-12 debug CDIs (gbapspDC-fresh / -trace-dynarec / -trace-interpreter) plus gbapspDC.cdi were tracked at ~243 MB total and predate the autoload fix and recent dynarec work, so they were stale and misleading for testing. CDIs are regenerable build outputs, so: - git rm --cached the four CDIs (kept on local disk, recoverable from history) - .gitignore: add dc/*.cdi so future build outputs stay untracked Disc-build inputs (gba_bios.bin, DangerousXmas.bin, autoload.txt, game_config.txt) remain tracked. Rebuild a fresh CDI with dc/dc.sh. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCEpbuNCctwevnBDC3c1vn
Add Flycast smoke test runbook and disc preflight validation
When translated EWRAM code spanned more than one 32KB page, flush_translation_cache_ram() cleared neither the first page's tag tail nor the last page's tag head, and cleared page 0's tag range instead. Tag numbers are reused from 0x0101 after a flush, so the surviving stale tags dispatched into the reset translation cache (another block's code, or garbage). Present since the original import. Clear the tag range of every page the code span touches, mirroring the single-page branch's semantics, and add phase 9 contract checks for the min-page-tail and max-page-head memsets. Also drop a stray backslash line that spliced into the following #define after arm_set_condition. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012H1eeCBT72M4zLj8LVADwf
dc/cd/gba_bios.bin (the copyrighted Nintendo BIOS) was tracked in git even though the README lists it as a user-supplied file that is not included in the repository. Untrack it (.gitignore's *.bin already covers it; the local copy stays for dc/dc.sh disc builds), and flip the disc-layout contract from requiring the blob to exist to asserting it is NOT tracked, so it cannot silently come back. The build-time guards are unchanged: dc.sh requires the file and the preflight verifies its MD5. Note: the blob remains in git history. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012H1eeCBT72M4zLj8LVADwf
cpu_threaded.c had drifted to the SH-4 backend's contracts and the x86 host debugging build no longer compiled: - translation_ptr_t was only defined by dc/sh4_emit.h; define it (u8 *) in the x86 backend too. - The shared ARM LDM/STM call sites use the 4-argument arm_block_memory(access, pre, post, s_bit) shape, while x86_emit.h still had the old 5-argument macro whose preadjust variants (up/down/ full) did not even match the call sites (down_a/down_b). Route LDM/STM through an execute_arm_block_memory C helper with the same semantics as dc/sh4_helpers.c and the audited interpreter: transfers before writeback, writeback suppressed for LDM with the base in the list, S-bit user-bank transfers, and STM of the PC storing pc + 8. - generate_update_pc_reg (force_pc_update_target sync) and set_cpu_mode were undeclared; both compiled as implicit declarations. All C objects now build on a 64-bit host. x86_stub.S still needs a 32-bit toolchain to assemble and link (tracked as ROADMAP D2). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012H1eeCBT72M4zLj8LVADwf
README claimed a 16/12/8/4 MB resident-buffer ladder; the code (and the phase 3 contract test) use a single conservative 4 MB allocation with demand paging beyond it. Record the audit 4 findings in HIGH_IMPACT_FIXES.md and update ROADMAP D2 with the remaining 32-bit toolchain gap for the x86 host build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012H1eeCBT72M4zLj8LVADwf
Dynarec audit 4: EWRAM flush fix, BIOS untracking, x86 host build repair
Carries the work that was already sitting uncommitted in the worktree, so the measurement work that follows has a reviewable base. SH-4 helper correctness (see DYNAREC_AUDIT_2026-09-10.md): - ADC/SBC/RSC capture the incoming carry before touching flags, account for equality when computing carry/borrow, and derive overflow from operand and result sign bits. - Register-specified shifts mask to the low byte; rotate counts are normalised so multiples of 32 preserve the value and set carry from bit 31. - Masked CPSR writes collapse the live flag slots into CPSR first, so a control-bits-only write no longer drops live N/Z/C/V, and unmasking an IRQ snapshots the right flags into SPSR_irq. - Exception returns align the return PC by the restored SPSR.T before IRQ handling, so an ARM target with bit 0 set cannot reach the dual dispatcher as a Thumb target. Emission: immediate materialisation skips leading zero bytes at translation time, shortening small constants without adding literal pools or scratch registers. Tests: adds sh4_helpers_behavior_test, which compiles the production helpers against a minimal KOS shim and checks results and all NZCV flags across boundary operands and 10,000 deterministic pairs. Adds an emitted-code simulator covering immediate loads and conditional branch polarity. Adds save/config fault-injection coverage in save_io_test.py. Also restores x86 host backend C compilation against the shared translator, stops distributing the GBA BIOS, and documents the resident-buffer behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The port had never had a frame rate recorded, because the Dreamcast build reports timing through SDL_WM_SetCaption, which does nothing on target. Baseline (Flycast 2.7, stock 16 MB, 200 MHz). The Dreamcast build presents exactly one flip per emulated GBA frame, so the host emulator's frame counter reads GBA speed directly: homebrew title, fully resident 53.9 fps 90% of the 59.73 target Super Puzzle Fighter II, 65s 23.2 fps 39% Super Puzzle Fighter II, worst 8.5 fps 14% The homebrew control carries the argument: the disc, video and flip path sustain near-full speed, so the deficit on commercial code is recompiler throughput rather than presentation. DC_PERFORMANCE_PLAN_2026-09-10.md records thirteen findings with measured costs and a five-phase plan. Emission costs were counted by compiling dc/sh4_emit.h on the host against the existing test shim and counting the instructions each emitter macro writes. Highlights: - The entire used region of the translation cache is flushed on every newly translated block, so the cost grows as the cache fills. Not previously tracked. - Every data-processing instruction compiles to a call to a one-line C helper; an ARM add emits 15 SH-4 instructions where 4 would do. - 32-bit constants are built a byte at a time, 9.0 instructions on average, against 1 for a PC-relative literal pool load. - Constant shifts emit one instruction per bit, up to 31. - The flag slots sit just past the load/store displacement window, so every flag access costs 3 instructions instead of 1. - Automatic frameskip is the default but is implemented only in the PSP build, so the Dreamcast never drops a frame to catch up. Not previously tracked. - Grand Theft Auto Advance boots to its title sequence and then stops with a recompiler bad-jump fatal error. Correctness, not performance, but it blocks benchmarking the largest available title. The plan supersedes roadmap rows B8 and B9 and orders the work so the cheapest large wins land first. Frame-rate samples, the games tested, and the disc-build details needed to reproduce any of this are recorded in scripts/smoke-test-results.md. Flycast's SH-4 timing is approximate rather than cycle-exact, so these are first-order estimates of Dreamcast speed, not hardware measurements. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
The port had never had a frame rate recorded. The Dreamcast build reports timing through
SDL_WM_SetCaption, which does nothing on target, so every performance discussion so far has been hypothesis. This PR records a baseline, audits where the time goes, and orders the work to fix it.Baseline
Flycast 2.7, stock 16 MB, 200 MHz. The Dreamcast build presents exactly one flip per emulated GBA frame, so the host emulator's frame counter reads GBA speed directly.
The homebrew control carries the argument. The disc, video and flip path sustain near-full speed, so the deficit on commercial code is recompiler throughput rather than presentation.
Flycast's SH-4 timing is approximate rather than cycle-exact, so these are first-order estimates of Dreamcast speed, not hardware measurements. Real GD-ROM seek latency is not reproduced at all, so large-ROM paging cost does not appear here.
The audit
DC_PERFORMANCE_PLAN_2026-09-10.mdrecords thirteen findings and a five-phase plan. Emission costs were counted, not estimated:dc/sh4_emit.hcompiles on the host against the shimtests/sh4_emit_encoding_test.calready uses, and each emitter macro was run against a counting buffer.ADD r0,r1,r2Leading findings:
The plan supersedes roadmap rows B8 and B9.
Known defect, not fixed here
Grand Theft Auto Advance (16 MB) halts with
bad jump 1a3019f8 (3000154) (0). An indirect branch produced a target outside any valid GBA region while executing in internal RAM. Two smaller titles do not reproduce it, and one of those is also demand-paged from disc, so ROM paging alone does not explain it. This is a correctness defect rather than a performance one, but it blocks benchmarking the most demanding title available. Isolating it needs the register dump that Phase 0 instrumentation would add.Reviewer notes
This PR carries two separate bodies of work, in two commits.
a0ab5b6is the dynarec audit 4 work that was already sitting uncommitted in the worktree: the ADC/SBC/RSC flag fixes, register shift masking, masked CPSR writes, exception-return alignment, plus the new host test coverage and the save/build hardening. It is committed here so the measurement work has a reviewable base and so the plan document's links toDYNAREC_AUDIT_2026-09-10.mdresolve. Review it on its own terms; it was not authored in the session that produced the second commit.bda07fdis this session's work: the plan document and the recorded measurements. Documentation only.Testing. The Dreamcast cross-build was verified with the CI container. The host suite was not run locally, because no host compiler is available in the KOS container and Git Bash has none either; CI runs
make -C tests teston Ubuntu and should be treated as the gate for commit 1. The second commit changes only Markdown.Reproducing the run.
scripts/smoke-test-results.mdand section 4 of the plan carry the disc recipe. Three details each cost a failed boot to find: the boot binary must bescrambled, the ISO session offset must be 11702 to match wherecdi4dcplaces the data track, andgba_bios.bingoes at the disc root rather than undergbaDC/.mkdcdisc, whichdc/dc.shexpects, is not in the CI container;scramble,mkisofsandcdi4dcare.🤖 Generated with Claude Code