Files
psxrecomp/tools/find_const.py
T
Matthew Stanley 685687f36f Restructure duckstation as submodule + patch; salvage TCP docs + debug_client
Recovery + restructure after rm -rf destroyed duckstation/build/ earlier this
session. Adopts the nesrecomp pattern (see F:/Projects/nesrecomp): pristine
upstream as submodule, our changes as a patch file that gets auto-applied by a
setup script.

Submodule:
- duckstation/ now pins stenzek/duckstation@ffb33c281 (release-20260328 era)
- .gitmodules records the pin; .gitignore stops ignoring the submodule path but
  still ignores build/ and dep/prebuilt/ (the submodule's own .gitignore also
  handles these)

Patch + setup:
- tools/duckstation/psxrecomp_oracle.patch (1187 lines, 7 files) captures our
  TCP debug-server on port 4371 plus the unfinished pc_break family of commands
  (with the VirtualMemoryAddress compile fix)
- tools/duckstation/setup.sh clones submodule, fetches + verifies + extracts
  prebuilt Windows deps, rewrites stale absolute paths baked into the prebuilt
  CMake metadata, applies the oracle patch. Idempotent.
- tools/duckstation/build.sh configures CMake (VS 17 2022) + runs MSBuild, then
  provisions bin/ with portable marker, BIOS copy, and settings.ini post-fixes
- tools/duckstation/README.md documents the layout, the regeneration flow, and
  the known headless-boot configuration issue

TCP documentation (salvaged from v3):
- TCP_COMMANDS.md: full command table with native (4370) / duckstation (4371)
  / both columns, reflecting actual v4 implementation including wtrace_* and
  pc_break/pc_hit_last families
- tools/debug_client.py: unified CLI from v3 (ports 4370/4371 match v4)
- DEBUG.md: merged v3's END-TO-END COMPLETION RULE, Stale Hypothesis Rule,
  TOOLING RULE, and TOOL LIFECYCLE RULE sections into v4's existing loop

Phase 4 diagnostic artefacts from this session:
- generated/relocation_proofs/vblank_registrar/ — corrected VBlank-stall
  diagnosis. Prior handoff claimed the shell VBlank handler was never installed;
  live TCP inspection of both runtime and DuckStation oracle proved handler IS
  installed at 0x800DFEE0. Real gap is one level up in the kernel IRQ chain at
  *(0xA000E014).
- tools/duck_query*.py, duck_search.py, disasm_ram.py, scan_*.py — focused
  probes used to establish the above; reusable against either server

CLAUDE.md §2 and new §15 document the submodule-based oracle setup and the
"never edit submodule, regenerate patch instead" rule.

Known issues (tracked in tools/duckstation/README.md):
- Fresh settings.ini doesn't complete BIOS boot under -nogui; one-time launch
  with GUI is required to warm up the settings before headless mode works

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 01:34:27 -07:00

41 lines
1.6 KiB
Python

import struct, sys
with open('bios/SCPH1001.BIN','rb') as f: rom=f.read()
BASE=0xBFC00000
# Find lui reg, X followed by addiu reg, reg, Y where X<<16+signext(Y) == target
def find_const(target):
tgt_hi = (target >> 16) & 0xffff
tgt_lo_signed = target & 0xffff
# If low bit 15 set, lui needs hi+1
if tgt_lo_signed & 0x8000:
lui_hi = (tgt_hi + 1) & 0xffff
else:
lui_hi = tgt_hi
hits = []
for off in range(0, len(rom)-8, 4):
w0 = struct.unpack('<I', rom[off:off+4])[0]
op0 = (w0>>26)&0x3f
if op0 != 15 or (w0&0xffff) != lui_hi: continue
rt0 = (w0>>16)&0x1f
for d in range(1, 16):
noff = off + d*4
if noff+4>len(rom): break
w1 = struct.unpack('<I', rom[noff:noff+4])[0]
op1 = (w1>>26)&0x3f
if op1==9 and (w1&0xffff)==tgt_lo_signed:
rs1=(w1>>21)&0x1f; rt1=(w1>>16)&0x1f
if rs1==rt0 and rt1==rt0:
hits.append((BASE+off, BASE+noff, rt0, d))
break
if op1==13 and (w1&0xffff)==(target&0xffff): # ori variant
rs1=(w1>>21)&0x1f; rt1=(w1>>16)&0x1f
if rs1==rt0 and rt1==rt0 and (w0&0xffff)==tgt_hi:
hits.append((BASE+off, BASE+noff, rt0, d))
break
return hits
for t in [int(a,16) for a in sys.argv[1:]]:
hits = find_const(t)
print(f'--- const 0x{t:08X} synthesized by ({len(hits)} sites) ---')
for lui_pc, lo_pc, rt, d in hits[:20]:
print(f' lui@0x{lui_pc:08X} lo@0x{lo_pc:08X} reg={rt} dist={d}')