mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
cf6ac8a580
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
89 lines
4.6 KiB
Python
89 lines
4.6 KiB
Python
"""Segment misses are recorded at dispatch and reach every surface.
|
|
|
|
docs/SEGMENT_AWARE_CODE.md §5.5 (§10 decision 2): a PC in the EXE's text whose
|
|
physical word has a compiled row only in another segment is a segment miss. It
|
|
runs interpreted through dirty_ram_dispatch_inner's clean-text-miss path and is
|
|
recorded, so the run names each (segment, entry) that needs a seed.
|
|
|
|
The classification and the ring are unit-tested (test_psx_segment_miss.c,
|
|
psx_segment_miss_note included). What a unit test cannot see is the wiring
|
|
inside the runtime, which only runs in a full build: deleting the dispatch
|
|
hook, the TCP command or a report field would leave every other test green
|
|
and make segment misses silent again. This guard pins that wiring and the
|
|
`segment_misses` row shape, whose `kind` tells a game segment miss from a
|
|
BIOS one (the compiled BIOS ran a window's home body for another segment's
|
|
alias PC; bios_segment_variants compiles and queries that emitted check).
|
|
"""
|
|
from pathlib import Path
|
|
import re
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
INTERP = (ROOT / 'runtime/src/dirty_ram_interp.c').read_text(encoding='utf-8')
|
|
DEBUG = (ROOT / 'runtime/src/debug_server.c').read_text(encoding='utf-8')
|
|
CRASH = (ROOT / 'runtime/src/crash_trace.c').read_text(encoding='utf-8')
|
|
CMAKE = (ROOT / 'runtime/runtime.cmake').read_text(encoding='utf-8')
|
|
|
|
|
|
def body(source, signature):
|
|
start = source.index(signature)
|
|
depth, pos = 0, source.index('{', start)
|
|
while True:
|
|
if source[pos] == '{':
|
|
depth += 1
|
|
elif source[pos] == '}':
|
|
depth -= 1
|
|
if depth == 0:
|
|
return source[start:pos]
|
|
pos += 1
|
|
|
|
|
|
def main():
|
|
assert 'runtime/src/psx_segment_miss.c' in CMAKE, 'the runtime does not link psx_segment_miss.c'
|
|
|
|
# The hook: every clean game-text miss goes to psx_segment_miss_note with
|
|
# the full PC, inside the game-dispatch block that sets the flag.
|
|
inner = body(INTERP, 'static int dirty_ram_dispatch_inner(CPUState* cpu, uint32_t addr, '
|
|
'uint32_t stop_addr) {')
|
|
block = inner[inner.index('#ifdef PSX_HAS_GAME_DISPATCH'):]
|
|
block = block[:block.index('#endif\n\n /* B-2')]
|
|
assert re.search(r'clean_game_text_miss = psx_game_address_in_text\(addr\) \? 1 : 0;', block), \
|
|
'a compiled-dispatch miss in game text no longer sets clean_game_text_miss'
|
|
# A forced span replay interprets compiled text on purpose: not a miss.
|
|
hook = re.search(r'if \(clean_game_text_miss && !forced\)\s*\(void\)psx_segment_miss_note\(addr, '
|
|
r'psx_game_is_function_entry,\s*cpu->gpr\[31\], cpu->gpr\[29\],', block)
|
|
assert hook, 'dirty_ram_dispatch_inner no longer notes segment misses on a clean-text miss'
|
|
assert block.index('psx_dispatch_game_compiled(cpu, addr)') < hook.start(), \
|
|
'the segment-miss note must follow the compiled-dispatch attempt'
|
|
|
|
# TCP: the command, and the counters on dispatch_stats and ping.
|
|
assert re.search(r'\{\s*"segment_misses",\s*handle_segment_misses\s*\}', DEBUG), \
|
|
'TCP segment_misses is not registered'
|
|
seg = body(DEBUG, 'static void handle_segment_misses(int id, const char *json)')
|
|
# The summary row shape (the seed tools read `pc` and `kind`: a "bios"
|
|
# row belongs in the BIOS seeds, not the game's).
|
|
assert '\\"summary\\":[' in seg and \
|
|
('{\\"pc\\":\\"0x%08X\\",\\"home\\":\\"0x%08X\\",\\"count\\":%llu,"\n'
|
|
' "\\"kind\\":\\"%s\\"}"') in seg and \
|
|
'psx_segment_miss_kind_name(kinds[i])' in seg, \
|
|
'segment_misses summary rows changed shape'
|
|
assert '\\"kind\\":\\"%s\\"}' in seg and 'psx_segment_miss_kind_name(e.kind)' in seg, \
|
|
'segment_misses tail entries lost their kind'
|
|
stats = body(DEBUG, 'static void handle_dispatch_stats(int id, const char *json)')
|
|
assert 'segment_miss_total' in stats and 'psx_segment_miss_total()' in stats
|
|
assert 'segment_miss_unique' in stats and 'psx_segment_miss_unique()' in stats
|
|
ping = body(DEBUG, 'static void handle_ping(int id, const char *json)')
|
|
assert 'segment_miss_total' in ping and 'psx_segment_miss_total()' in ping, \
|
|
'ping no longer surfaces segment misses'
|
|
|
|
# Exit report (psx_last_run_report.json).
|
|
dump = body(CRASH, 'void psx_crash_trace_dump(const char *reason, void *seh_info) {')
|
|
assert '\\"segment_misses\\"' in dump and 'psx_segment_miss_summary(' in dump, \
|
|
'the exit report lost its segment_misses section'
|
|
assert 'psx_segment_miss_kind_name(kinds[i])' in dump, \
|
|
'the exit report lost the segment-miss kind'
|
|
print('segment-miss wiring guards passed')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|