diff --git a/generated/SCPH1001_dispatch.c b/generated/SCPH1001_dispatch.c index d1c30c5f..c0b719c7 100644 --- a/generated/SCPH1001_dispatch.c +++ b/generated/SCPH1001_dispatch.c @@ -1,7 +1,7 @@ /* AUTO-GENERATED by psxrecomp-bios --emit-full. DO NOT EDIT. * * BIOS SHA256: 71af94d1e47a68c11e8fdb9f8368040601514a42a5a399cda48c7d3bff1e99d3 - * Dispatch entries: 1043 + * Dispatch entries: 1045 */ #include "cpu_state.h" @@ -1036,6 +1036,7 @@ extern void func_1FC41ED8(CPUState* cpu); extern void func_1FC41F10(CPUState* cpu); extern void func_1FC41F54(CPUState* cpu); extern void func_1FC41F88(CPUState* cpu); +extern void func_1FC41FC0(CPUState* cpu); extern void func_1FC42014(CPUState* cpu); extern void func_1FC4203C(CPUState* cpu); extern void func_1FC42060(CPUState* cpu); @@ -1048,6 +1049,7 @@ extern void func_1FC42380(CPUState* cpu); extern void func_1FC42424(CPUState* cpu); extern void func_1FC424A8(CPUState* cpu); extern void func_1FC42540(CPUState* cpu); +extern void func_1FC425BC(CPUState* cpu); extern void func_1FC425FC(CPUState* cpu); extern void func_1FC42640(CPUState* cpu); extern void func_1FC426DC(CPUState* cpu); @@ -1063,7 +1065,7 @@ typedef struct { PsxRecompFunc func; } DispatchEntry; -static const DispatchEntry dispatch_table[1043] = { +static const DispatchEntry dispatch_table[1045] = { { 0x00000500u, func_00000500 }, { 0x00000510u, func_00000510 }, { 0x00000520u, func_00000520 }, @@ -2088,6 +2090,7 @@ static const DispatchEntry dispatch_table[1043] = { { 0x1FC41F10u, func_1FC41F10 }, { 0x1FC41F54u, func_1FC41F54 }, { 0x1FC41F88u, func_1FC41F88 }, + { 0x1FC41FC0u, func_1FC41FC0 }, { 0x1FC42014u, func_1FC42014 }, { 0x1FC4203Cu, func_1FC4203C }, { 0x1FC42060u, func_1FC42060 }, @@ -2100,6 +2103,7 @@ static const DispatchEntry dispatch_table[1043] = { { 0x1FC42424u, func_1FC42424 }, { 0x1FC424A8u, func_1FC424A8 }, { 0x1FC42540u, func_1FC42540 }, + { 0x1FC425BCu, func_1FC425BC }, { 0x1FC425FCu, func_1FC425FC }, { 0x1FC42640u, func_1FC42640 }, { 0x1FC426DCu, func_1FC426DC }, @@ -2127,7 +2131,7 @@ void psx_dispatch(CPUState* cpu, uint32_t addr) { for (;;) { cpu->pc = 0; uint32_t phys = normalize(addr); - int lo = 0, hi = 1043 - 1; + int lo = 0, hi = 1045 - 1; int found = 0; while (lo <= hi) { int mid = (lo + hi) / 2; diff --git a/generated/SCPH1001_full.c b/generated/SCPH1001_full.c index c633afef..d988b3ad 100644 --- a/generated/SCPH1001_full.c +++ b/generated/SCPH1001_full.c @@ -1,7 +1,7 @@ /* AUTO-GENERATED by psxrecomp-bios --emit-full. DO NOT EDIT. * * BIOS SHA256: 71af94d1e47a68c11e8fdb9f8368040601514a42a5a399cda48c7d3bff1e99d3 - * Functions: 1043 + * Functions: 1045 */ #include "cpu_state.h" @@ -1034,6 +1034,7 @@ void func_1FC41ED8(CPUState* cpu); void func_1FC41F10(CPUState* cpu); void func_1FC41F54(CPUState* cpu); void func_1FC41F88(CPUState* cpu); +void func_1FC41FC0(CPUState* cpu); void func_1FC42014(CPUState* cpu); void func_1FC4203C(CPUState* cpu); void func_1FC42060(CPUState* cpu); @@ -1046,6 +1047,7 @@ void func_1FC42380(CPUState* cpu); void func_1FC42424(CPUState* cpu); void func_1FC424A8(CPUState* cpu); void func_1FC42540(CPUState* cpu); +void func_1FC425BC(CPUState* cpu); void func_1FC425FC(CPUState* cpu); void func_1FC42640(CPUState* cpu); void func_1FC426DC(CPUState* cpu); @@ -159730,6 +159732,62 @@ label_BFC41FA0: return; } +void func_1FC41FC0(CPUState* cpu) { +label_BFC41FC0: + psx_check_interrupts(cpu); + /* 0xBFC41FC0: 3C018008 lui $at, 0x8008 */ + cpu->gpr[1] = 0x80080000u; + /* 0xBFC41FC4: AC249F30 sw $a0, -24784($at) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[1] + (-24784)); if (psx_addr & 3u) { psx_unaligned_access(cpu, psx_addr, 0xBFC41FC4u); return; } cpu->write_word(psx_addr, cpu->gpr[4]); } + /* 0xBFC41FC8: 3C01800E lui $at, 0x800E */ + cpu->gpr[1] = 0x800E0000u; + /* 0xBFC41FCC: 27BDFFE8 addiu $sp, $sp, -24 */ + cpu->gpr[29] = (uint32_t)((int32_t)cpu->gpr[29] + (-24)); + /* 0xBFC41FD0: AC20EED4 sw $zero, -4396($at) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[1] + (-4396)); if (psx_addr & 3u) { psx_unaligned_access(cpu, psx_addr, 0xBFC41FD0u); return; } cpu->write_word(psx_addr, cpu->gpr[0]); } + /* 0xBFC41FD4: AFBF0014 sw $ra, 20($sp) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[29] + (20)); if (psx_addr & 3u) { psx_unaligned_access(cpu, psx_addr, 0xBFC41FD4u); return; } cpu->write_word(psx_addr, cpu->gpr[31]); } + /* 0xBFC41FD8: 3C01800E lui $at, 0x800E */ + cpu->gpr[1] = 0x800E0000u; + /* 0xBFC41FDC: 240EFFFF addiu $t6, $zero, -1 */ + cpu->gpr[14] = (uint32_t)((int32_t)cpu->gpr[0] + (-1)); + /* 0xBFC41FE0: 0C016818 jal 0xB005A060 */ + /* 0xBFC41FE4: AC2EEED0 sw $t6, -4400($at) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[1] + (-4400)); if (psx_addr & 3u) { psx_unaligned_access(cpu, psx_addr, 0xBFC41FE4u); return; } cpu->write_word(psx_addr, cpu->gpr[14]); } + cpu->gpr[31] = 0xBFC41FE8u; + psx_dispatch(cpu, 0x8005A060u); +label_BFC41FE8: + psx_check_interrupts(cpu); + /* 0xBFC41FE8: 3C042000 lui $a0, 0x2000 */ + cpu->gpr[4] = 0x20000000u; + /* 0xBFC41FEC: 3C05800E lui $a1, 0x800E */ + cpu->gpr[5] = 0x800E0000u; + /* 0xBFC41FF0: 24A5EED0 addiu $a1, $a1, -4400 */ + cpu->gpr[5] = (uint32_t)((int32_t)cpu->gpr[5] + (-4400)); + /* 0xBFC41FF4: 0C016AEC jal 0xB005ABB0 */ + /* 0xBFC41FF8: 34840001 ori $a0, $a0, 0x1 */ + cpu->gpr[4] = cpu->gpr[4] | 0x1u; + cpu->gpr[31] = 0xBFC41FFCu; + psx_dispatch(cpu, 0x8005ABB0u); +label_BFC41FFC: + psx_check_interrupts(cpu); + /* 0xBFC41FFC: 0C016A60 jal 0xB005A980 */ + /* 0xBFC42000: 00002021 addu $a0, $zero, $zero */ + cpu->gpr[4] = cpu->gpr[0] + cpu->gpr[0]; + cpu->gpr[31] = 0xBFC42004u; + psx_dispatch(cpu, 0x8005A980u); +label_BFC42004: + psx_check_interrupts(cpu); + /* 0xBFC42004: 8FBF0014 lw $ra, 20($sp) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[29] + (20)); if (psx_addr & 3u) { psx_unaligned_access(cpu, psx_addr, 0xBFC42004u); return; } cpu->gpr[31] = cpu->read_word(psx_addr); } + /* 0xBFC42008: 27BD0018 addiu $sp, $sp, 24 */ + cpu->gpr[29] = (uint32_t)((int32_t)cpu->gpr[29] + (24)); + /* 0xBFC4200C: 03E00008 jr $ra */ + /* 0xBFC42010: 00000000 nop */ + /* nop */ + return; +} + void func_1FC42014(CPUState* cpu) { label_BFC42014: psx_check_interrupts(cpu); @@ -160662,6 +160720,50 @@ label_BFC425AC: return; } +void func_1FC425BC(CPUState* cpu) { +label_BFC425BC: + psx_check_interrupts(cpu); + /* 0xBFC425BC: 3C0E8008 lui $t6, 0x8008 */ + cpu->gpr[14] = 0x80080000u; + /* 0xBFC425C0: 8DCE9D9C lw $t6, -25188($t6) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[14] + (-25188)); if (psx_addr & 3u) { psx_unaligned_access(cpu, psx_addr, 0xBFC425C0u); return; } cpu->gpr[14] = cpu->read_word(psx_addr); } + /* 0xBFC425C4: 3C02800E lui $v0, 0x800E */ + cpu->gpr[2] = 0x800E0000u; + /* 0xBFC425C8: 8C42FF40 lw $v0, -192($v0) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[2] + (-192)); if (psx_addr & 3u) { psx_unaligned_access(cpu, psx_addr, 0xBFC425C8u); return; } cpu->gpr[2] = cpu->read_word(psx_addr); } + /* 0xBFC425CC: 27BDFFE8 addiu $sp, $sp, -24 */ + cpu->gpr[29] = (uint32_t)((int32_t)cpu->gpr[29] + (-24)); + /* 0xBFC425D0: 3C018008 lui $at, 0x8008 */ + cpu->gpr[1] = 0x80080000u; + /* 0xBFC425D4: 25CF0001 addiu $t7, $t6, 1 */ + cpu->gpr[15] = (uint32_t)((int32_t)cpu->gpr[14] + (1)); + /* 0xBFC425D8: AFBF0014 sw $ra, 20($sp) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[29] + (20)); if (psx_addr & 3u) { psx_unaligned_access(cpu, psx_addr, 0xBFC425D8u); return; } cpu->write_word(psx_addr, cpu->gpr[31]); } + /* 0xBFC425DC: 10400003 beq $v0, $zero, 0xBFC425EC */ + uint32_t psx_brA_BFC425DC = cpu->gpr[2]; uint32_t psx_brB_BFC425DC = cpu->gpr[0]; + int psx_taken_BFC425DC = (psx_brA_BFC425DC == psx_brB_BFC425DC); + /* 0xBFC425E0: AC2F9D9C sw $t7, -25188($at) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[1] + (-25188)); if (psx_addr & 3u) { psx_unaligned_access(cpu, psx_addr, 0xBFC425E0u); return; } cpu->write_word(psx_addr, cpu->gpr[15]); } + if (psx_taken_BFC425DC) goto label_BFC425EC; +label_BFC425E4: + psx_check_interrupts(cpu); + /* 0xBFC425E4: 0040F809 jalr $ra, $v0 */ + /* 0xBFC425E8: 00000000 nop */ + /* nop */ + cpu->gpr[31] = 0xBFC425ECu; + psx_dispatch(cpu, cpu->gpr[2]); +label_BFC425EC: + psx_check_interrupts(cpu); + /* 0xBFC425EC: 8FBF0014 lw $ra, 20($sp) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[29] + (20)); if (psx_addr & 3u) { psx_unaligned_access(cpu, psx_addr, 0xBFC425ECu); return; } cpu->gpr[31] = cpu->read_word(psx_addr); } + /* 0xBFC425F0: 27BD0018 addiu $sp, $sp, 24 */ + cpu->gpr[29] = (uint32_t)((int32_t)cpu->gpr[29] + (24)); + /* 0xBFC425F4: 03E00008 jr $ra */ + /* 0xBFC425F8: 00000000 nop */ + /* nop */ + return; +} + void func_1FC425FC(CPUState* cpu) { label_BFC425FC: psx_check_interrupts(cpu); diff --git a/recompiler/seeds/phase2_ghidra_seeds.json b/recompiler/seeds/phase2_ghidra_seeds.json index a5227529..c6bf3fe0 100644 --- a/recompiler/seeds/phase2_ghidra_seeds.json +++ b/recompiler/seeds/phase2_ghidra_seeds.json @@ -1,7 +1,7 @@ { "schema": "psxrecomp-v4 phase2 seeds", "source": "generated/ghidra_function_starts.json + phase1c seeds + dispatch_miss_seeds + preserved manual", - "seed_count": 823, + "seed_count": 824, "seeds": [ { "address": "0xBFC00000", @@ -4117,6 +4117,16 @@ "address": "0xBFC33CC0", "label": "jal_BFC33CC0", "rationale": "JAL target from code region" + }, + { + "address": "0xBFC41FC0", + "label": "shell_InitRCnt_setup", + "rationale": "Undiscovered function: calls B0:0x15 (InitRCnt) with arg 0x20000001. Only reachable via indirect jalr from shell init table. Missing seed causes VSync counter to never increment." + }, + { + "address": "0xBFC425BC", + "label": "vblank_chain_handler", + "rationale": "VBlank chain handler: the ONLY instruction in the entire BIOS ROM that stores to 0x80079D9C (VSync counter) is at 0xBFC425E0, inside this 15-instruction function at 0xBFC425BC. Function reads counter, increments it, optionally calls user callback at *0x800DFF40. Not reachable via any JAL/literal/lui in ROM — installed at runtime by kernel chain-registration code. Missing seed means counter-increment code is never recompiled, so even if kernel installs the handler pointer it cannot dispatch to recompiled code." } ], "excluded": [ diff --git a/runtime/src/debug_server.c b/runtime/src/debug_server.c index f46faaa6..0b0ecbb5 100644 --- a/runtime/src/debug_server.c +++ b/runtime/src/debug_server.c @@ -92,6 +92,28 @@ typedef struct { } Watchpoint; static Watchpoint s_watchpoints[MAX_WATCHPOINTS]; +/* ---- Write trace (ring buffer) ---- + * Records every RAM write whose physical address is in [trace_lo, trace_hi). + * Filter variables are non-static so memory.c can inline the bounds check. + * Disabled when trace_lo == trace_hi. */ +#define WRITE_TRACE_CAP 1024 +typedef struct { + uint64_t seq; + uint32_t addr; + uint32_t old_val; + uint32_t new_val; + uint32_t ra; + uint8_t width; +} WriteTraceEntry; +static WriteTraceEntry s_wtrace[WRITE_TRACE_CAP]; +static uint64_t s_wtrace_seq = 0; /* total writes ever recorded */ +static uint32_t s_wtrace_head = 0; +/* Default: trace the EvCB region from program start so we catch OpenEvent + * writes that happen during early BIOS boot, before TCP clients can connect. + * Can be overridden at runtime via the wtrace_range command. */ +uint32_t debug_server_wtrace_lo = 0x0000E000; /* physical */ +uint32_t debug_server_wtrace_hi = 0x0000E1F0; /* physical, exclusive — EvCB slots (stops before PCB@0xE1EC/TCB@0xE1F4) */ + /* ---- Platform helpers ---- */ static void set_nonblocking(sock_t s) { @@ -783,6 +805,71 @@ static void handle_vram_peek(int id, const char *json) id, x, y, w, h, hex); } +/* ---- Write trace: hook + handlers ---- */ +extern CPUState *debug_cpu_ptr; + +/* Called from memory.c write paths after the bounds check. */ +void debug_server_trace_write(uint32_t phys, uint32_t old_val, uint32_t new_val, uint8_t width) +{ + uint32_t ra = debug_cpu_ptr ? debug_cpu_ptr->gpr[31] : 0; + WriteTraceEntry *e = &s_wtrace[s_wtrace_head]; + e->seq = s_wtrace_seq++; + e->addr = phys; + e->old_val = old_val; + e->new_val = new_val; + e->ra = ra; + e->width = width; + s_wtrace_head = (s_wtrace_head + 1) % WRITE_TRACE_CAP; +} + +static void handle_wtrace_range(int id, const char *json) +{ + char lo_str[32], hi_str[32]; + if (!json_get_str(json, "lo", lo_str, sizeof(lo_str))) { send_err(id, "missing lo"); return; } + if (!json_get_str(json, "hi", hi_str, sizeof(hi_str))) { send_err(id, "missing hi"); return; } + debug_server_wtrace_lo = hex_to_u32(lo_str) & 0x1FFFFFFFu; + debug_server_wtrace_hi = hex_to_u32(hi_str) & 0x1FFFFFFFu; + send_fmt("{\"id\":%d,\"ok\":true,\"lo\":\"0x%08X\",\"hi\":\"0x%08X\"}", + id, debug_server_wtrace_lo, debug_server_wtrace_hi); +} + +static void handle_wtrace_clear(int id, const char *json) +{ + (void)json; + s_wtrace_seq = 0; + s_wtrace_head = 0; + memset(s_wtrace, 0, sizeof(s_wtrace)); + send_ok(id); +} + +static void handle_wtrace_dump(int id, const char *json) +{ + (void)json; + uint64_t total = s_wtrace_seq; + uint32_t count = (total < WRITE_TRACE_CAP) ? (uint32_t)total : WRITE_TRACE_CAP; + uint32_t start = (total < WRITE_TRACE_CAP) ? 0 : s_wtrace_head; + + const size_t BUF_SZ = 256 * 1024; + char *buf = (char *)malloc(BUF_SZ); + if (!buf) { send_err(id, "oom"); return; } + size_t pos = 0; + pos += snprintf(buf + pos, BUF_SZ - pos, + "{\"id\":%d,\"ok\":true,\"total\":%llu,\"count\":%u,\"entries\":[", + id, (unsigned long long)total, count); + for (uint32_t i = 0; i < count && pos < BUF_SZ - 256; i++) { + uint32_t idx = (start + i) % WRITE_TRACE_CAP; + WriteTraceEntry *e = &s_wtrace[idx]; + pos += snprintf(buf + pos, BUF_SZ - pos, + "%s{\"seq\":%llu,\"addr\":\"0x%08X\",\"old\":\"0x%08X\",\"new\":\"0x%08X\",\"ra\":\"0x%08X\",\"w\":%u}", + (i == 0) ? "" : ",", + (unsigned long long)e->seq, + e->addr, e->old_val, e->new_val, e->ra, (unsigned)e->width); + } + pos += snprintf(buf + pos, BUF_SZ - pos, "]}"); + debug_server_send_line(buf); + free(buf); +} + static void handle_quit(int id, const char *json) { (void)json; @@ -809,6 +896,9 @@ static const CmdEntry s_commands[] = { { "sio_state", handle_sio_state }, { "watch", handle_watch }, { "unwatch", handle_unwatch }, + { "wtrace_range", handle_wtrace_range }, + { "wtrace_dump", handle_wtrace_dump }, + { "wtrace_clear", handle_wtrace_clear }, { "set_input", handle_set_input }, { "press", handle_press }, { "clear_input", handle_clear_input }, @@ -856,10 +946,12 @@ static void process_command(const char *line) /* Extended init that accepts a CPU state pointer for register queries. */ static CPUState *s_init_cpu = NULL; +CPUState *debug_cpu_ptr = NULL; /* Global, used by memory.c watchpoints */ void debug_server_set_cpu(CPUState *cpu) { s_cpu = cpu; + debug_cpu_ptr = cpu; } void debug_server_init(int port) diff --git a/runtime/src/memory.c b/runtime/src/memory.c index 9a4cf25e..cdac6aea 100644 --- a/runtime/src/memory.c +++ b/runtime/src/memory.c @@ -45,6 +45,21 @@ static const uint32_t *sr_ptr; uint32_t i_stat; /* 0x1F801070 — interrupt status (AND-acknowledge semantics) */ uint32_t i_mask; /* 0x1F801074 — interrupt enable mask */ +/* Write-trace hook (implemented in debug_server.c). Disabled when lo == hi. */ +extern uint32_t debug_server_wtrace_lo; +extern uint32_t debug_server_wtrace_hi; +extern void debug_server_trace_write(uint32_t phys, uint32_t old_val, uint32_t new_val, uint8_t width); + +static inline uint32_t read_ram_word(uint32_t phys) { + return (uint32_t)ram[phys] + | ((uint32_t)ram[phys + 1] << 8) + | ((uint32_t)ram[phys + 2] << 16) + | ((uint32_t)ram[phys + 3] << 24); +} +static inline uint16_t read_ram_half(uint32_t phys) { + return (uint16_t)ram[phys] | ((uint16_t)ram[phys + 1] << 8); +} + /* SPU registers are now handled by spu.c */ void memory_set_sr_ptr(const uint32_t *p) { sr_ptr = p; } @@ -311,6 +326,9 @@ void psx_write_word(uint32_t addr, uint32_t val) { uint32_t phys = addr & 0x1FFFFFFFu; if (phys < RAM_SIZE) { + if (phys >= debug_server_wtrace_lo && phys < debug_server_wtrace_hi) { + debug_server_trace_write(phys, read_ram_word(phys), val, 4); + } ram[phys] = (uint8_t)(val); ram[phys + 1] = (uint8_t)(val >> 8); ram[phys + 2] = (uint8_t)(val >> 16); @@ -366,6 +384,9 @@ void psx_write_half(uint32_t addr, uint16_t val) { uint32_t phys = addr & 0x1FFFFFFFu; if (phys < RAM_SIZE) { + if (phys >= debug_server_wtrace_lo && phys < debug_server_wtrace_hi) { + debug_server_trace_write(phys, read_ram_half(phys), val, 2); + } ram[phys] = (uint8_t)(val); ram[phys + 1] = (uint8_t)(val >> 8); return; @@ -413,6 +434,9 @@ void psx_write_byte(uint32_t addr, uint8_t val) { uint32_t phys = addr & 0x1FFFFFFFu; if (phys < RAM_SIZE) { + if (phys >= debug_server_wtrace_lo && phys < debug_server_wtrace_hi) { + debug_server_trace_write(phys, (uint32_t)ram[phys], (uint32_t)val, 1); + } ram[phys] = val; return; } diff --git a/tools/print_wtrace.py b/tools/print_wtrace.py new file mode 100644 index 00000000..74bd4222 --- /dev/null +++ b/tools/print_wtrace.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 +import json, sys +d = json.load(open(sys.argv[1])) +print(f"total={d['total']} captured={d['count']}") +for e in d['entries']: + print(f"seq={e['seq']:4} addr={e['addr']} old={e['old']} new={e['new']} ra={e['ra']} w={e['w']}") diff --git a/tools/sample_ra.py b/tools/sample_ra.py new file mode 100644 index 00000000..c84c3b41 --- /dev/null +++ b/tools/sample_ra.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Sample $ra + EPC over N seconds to profile hot code regions.""" +import socket, json, sys, time, collections + +HOST, PORT = "127.0.0.1", 4370 +DURATION = float(sys.argv[1]) if len(sys.argv) > 1 else 5.0 + +s = socket.socket() +s.connect((HOST, PORT)) +s.settimeout(2.0) +buf = b"" +ra_hist = collections.Counter() +epc_hist = collections.Counter() +n_samples = 0 +t_end = time.time() + DURATION + +def read_line(): + global buf + while b"\n" not in buf: + chunk = s.recv(16384) + if not chunk: return None + buf += chunk + line, _, buf = buf.partition(b"\n") + return line.decode() + +while time.time() < t_end: + s.sendall(b'{"cmd":"get_registers"}\n') + line = read_line() + if not line: break + d = json.loads(line) + ra = d['gpr'][31] + epc = d['cop0_epc'] + ra_hist[ra] += 1 + epc_hist[epc] += 1 + n_samples += 1 + +s.close() +print(f"samples: {n_samples}") +print(f"--- top 15 $ra ---") +for ra, cnt in ra_hist.most_common(15): + print(f" {ra} {cnt:5} ({100.0*cnt/n_samples:.1f}%)") +print(f"--- top 15 EPC ---") +for epc, cnt in epc_hist.most_common(15): + print(f" {epc} {cnt:5} ({100.0*cnt/n_samples:.1f}%)") diff --git a/tools/sample_ra_long.py b/tools/sample_ra_long.py new file mode 100644 index 00000000..a1c89be6 --- /dev/null +++ b/tools/sample_ra_long.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Sample $ra and also sp+20 (saved ra on stack) to trace the full chain.""" +import socket, json, sys, time, collections + +HOST, PORT = "127.0.0.1", 4370 +DURATION = float(sys.argv[1]) if len(sys.argv) > 1 else 5.0 + +s = socket.socket() +s.connect((HOST, PORT)) +s.settimeout(3.0) +buf = b"" + +def read_line(): + global buf + while b"\n" not in buf: + chunk = s.recv(16384) + if not chunk: return None + buf += chunk + line, _, buf = buf.partition(b"\n") + return line.decode() + +def send(cmd): + s.sendall(cmd.encode() + b"\n") + return json.loads(read_line()) + +ra_hist = collections.Counter() +stack0_hist = collections.Counter() +n = 0 +t_end = time.time() + DURATION + +while time.time() < t_end: + r = send('{"cmd":"get_registers"}') + ra = r['gpr'][31] + sp = r['gpr'][29] + ra_hist[ra] += 1 + # Read saved $ra at sp+20 (common store-location) + sp_int = int(sp, 16) + sp20 = f"0x{(sp_int + 20) & 0xFFFFFFFF:08X}" + r2 = send(f'{{"cmd":"read_ram","addr":"{sp20}","len":4}}') + if r2.get('ok'): + hex4 = r2['hex'] + # little-endian 4 bytes + saved_ra = int.from_bytes(bytes.fromhex(hex4), 'little') + stack0_hist[f"0x{saved_ra:08X}"] += 1 + n += 1 + +s.close() +print(f"samples: {n}") +print("--- $ra (register) ---") +for v, c in ra_hist.most_common(15): + print(f" {v} {c:5}") +print("--- saved $ra at sp+20 (outer frame) ---") +for v, c in stack0_hist.most_common(15): + print(f" {v} {c:5}") diff --git a/tools/scan_f2_sites.py b/tools/scan_f2_sites.py new file mode 100644 index 00000000..32f4e8c7 --- /dev/null +++ b/tools/scan_f2_sites.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +import sys +rom = open('F:/Projects/psxrecomp-v4/bios/SCPH1001.BIN','rb').read() + +matches = [] +for rt in range(32): + word = (0x0F << 26) | (rt << 16) | 0xF200 + b = word.to_bytes(4, 'little') + off = 0 + while True: + o = rom.find(b, off) + if o == -1: break + bios_addr = 0xBFC00000 + o + next_word = int.from_bytes(rom[o+4:o+8], 'little') + op = (next_word >> 26) & 0x3F + rs = (next_word >> 21) & 0x1F + rt_out = (next_word >> 16) & 0x1F + imm = next_word & 0xFFFF + matches.append((bios_addr, rt, op, rs, rt_out, imm, next_word, o)) + off = o + 4 + +print("--- ROM sites forming 0xF200xxxx (class values) ---") +for addr, rt, op, rs, rt_out, imm, nw, offset in matches: + if rs == rt and rt_out == rt: + if op == 0x0D: + marker = "OPENEVENT-TARGET" if imm in (1,2,3,9) else "" + print(f" 0x{addr:08X}: lui r{rt}, 0xF200; ori r{rt}, 0x{imm:04X} class=0xF200{imm:04X} {marker}") + +print("\n--- 0xF2000003 (VBlank) sites, JAL-adjacency check ---") +for addr, rt, op, rs, rt_out, imm, nw, offset in matches: + if rs == rt and rt_out == rt and op == 0x0D and imm == 0x0003: + o = offset + # JAL preceding? (i.e. at offset-4) + if o >= 4: + prev = int.from_bytes(rom[o-4:o], 'little') + op_prev = (prev >> 26) & 0x3F + if op_prev == 0x03: + jal_addr = 0xBFC00000 + o - 4 + target = (jal_addr & 0xF0000000) | ((prev & 0x3FFFFFF) << 2) + print(f" 0x{addr:08X}: VBlank class; PREV_JAL at 0x{jal_addr:08X} -> 0x{target:08X}") + else: + print(f" 0x{addr:08X}: VBlank class; prev op=0x{op_prev:02x} at 0x{0xBFC00000+o-4:08X}") + +# Now: find which functions contain these addresses +# A function "contains" addr if it's the closest-entry <= addr +# Read dispatch.c: +print("\n--- Functions containing 0xF2000003 sites ---") +import re +dispatch = open('F:/Projects/psxrecomp-v4/generated/SCPH1001_dispatch.c').read() +entries = re.findall(r'\{\s*0x([0-9A-F]+)u,\s*func_([0-9A-F]+)\s*\}', dispatch) +# Only keep those in code range (phys 0x1FC00000+) +fn_list = sorted([(int(a,16), name) for a,name in entries]) +def containing_fn(addr): + phys = addr & 0x1FFFFFFF + prev = None + for faddr, name in fn_list: + if faddr <= phys: + prev = (faddr, name) + else: + break + return prev + +for addr, rt, op, rs, rt_out, imm, nw, offset in matches: + if rs == rt and rt_out == rt and op == 0x0D and imm == 0x0003: + cf = containing_fn(addr) + if cf: + print(f" 0x{addr:08X} (lui+ori F2000003) is inside func_{cf[1]} (entry 0x{cf[0]:08X})") + else: + print(f" 0x{addr:08X}: NO CONTAINING FUNCTION (orphan code region!)")