mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
feat: 46 GTE library seeds + DuckStation oracle SW readback + debug tooling
Add 46 seeds for the libgte helper functions at BFC34E00-BFC36D00. These wrap individual GTE operations (RTPS, RTPT, NCLIP, MVMVA, etc.) and were never discovered because they're called via JALR function pointers, not direct JAL. Coverage: 52→114 gte_execute, 166→429 LWC2/SWC2. Emitted functions: 1178→1214. Seeds: 960→1006. Fix DuckStation oracle VRAM readback by setting UseSoftwareRendererForReadbacks=true in settings.ini. Add debug/analysis tools: GTE audit, VRAM stitching, rapid polling, GPU info, divergence comparison, jump table dumps. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/* AUTO-GENERATED by psxrecomp-bios --emit-full. DO NOT EDIT.
|
||||
*
|
||||
* BIOS SHA256: 71af94d1e47a68c11e8fdb9f8368040601514a42a5a399cda48c7d3bff1e99d3
|
||||
* Dispatch entries: 1174
|
||||
* Dispatch entries: 1215
|
||||
*/
|
||||
|
||||
#include "cpu_state.h"
|
||||
@@ -621,6 +621,7 @@ extern void func_1FC20F8C(CPUState* cpu);
|
||||
extern void func_1FC20FE8(CPUState* cpu);
|
||||
extern void func_1FC21044(CPUState* cpu);
|
||||
extern void func_1FC210C0(CPUState* cpu);
|
||||
extern void func_1FC210F0(CPUState* cpu);
|
||||
extern void func_1FC21128(CPUState* cpu);
|
||||
extern void func_1FC21184(CPUState* cpu);
|
||||
extern void func_1FC21200(CPUState* cpu);
|
||||
@@ -923,6 +924,7 @@ extern void func_1FC34AA0(CPUState* cpu);
|
||||
extern void func_1FC34AF0(CPUState* cpu);
|
||||
extern void func_1FC34B08(CPUState* cpu);
|
||||
extern void func_1FC34B58(CPUState* cpu);
|
||||
extern void func_1FC34E00(CPUState* cpu);
|
||||
extern void func_1FC34EC0(CPUState* cpu);
|
||||
extern void func_1FC34EC4(CPUState* cpu);
|
||||
extern void func_1FC35128(CPUState* cpu);
|
||||
@@ -959,9 +961,44 @@ extern void func_1FC35F38(CPUState* cpu);
|
||||
extern void func_1FC35F78(CPUState* cpu);
|
||||
extern void func_1FC35FA0(CPUState* cpu);
|
||||
extern void func_1FC35FD0(CPUState* cpu);
|
||||
extern void func_1FC36000(CPUState* cpu);
|
||||
extern void func_1FC36024(CPUState* cpu);
|
||||
extern void func_1FC36068(CPUState* cpu);
|
||||
extern void func_1FC36094(CPUState* cpu);
|
||||
extern void func_1FC360E0(CPUState* cpu);
|
||||
extern void func_1FC36104(CPUState* cpu);
|
||||
extern void func_1FC3614C(CPUState* cpu);
|
||||
extern void func_1FC36178(CPUState* cpu);
|
||||
extern void func_1FC361A0(CPUState* cpu);
|
||||
extern void func_1FC361CC(CPUState* cpu);
|
||||
extern void func_1FC361F4(CPUState* cpu);
|
||||
extern void func_1FC3621C(CPUState* cpu);
|
||||
extern void func_1FC36244(CPUState* cpu);
|
||||
extern void func_1FC3625C(CPUState* cpu);
|
||||
extern void func_1FC36280(CPUState* cpu);
|
||||
extern void func_1FC36294(CPUState* cpu);
|
||||
extern void func_1FC362BC(CPUState* cpu);
|
||||
extern void func_1FC362D0(CPUState* cpu);
|
||||
extern void func_1FC3632C(CPUState* cpu);
|
||||
extern void func_1FC36388(CPUState* cpu);
|
||||
extern void func_1FC363A0(CPUState* cpu);
|
||||
extern void func_1FC363F0(CPUState* cpu);
|
||||
extern void func_1FC36458(CPUState* cpu);
|
||||
extern void func_1FC364DC(CPUState* cpu);
|
||||
extern void func_1FC3653C(CPUState* cpu);
|
||||
extern void func_1FC365C4(CPUState* cpu);
|
||||
extern void func_1FC365F8(CPUState* cpu);
|
||||
extern void func_1FC36684(CPUState* cpu);
|
||||
extern void func_1FC36740(CPUState* cpu);
|
||||
extern void func_1FC367D4(CPUState* cpu);
|
||||
extern void func_1FC36880(CPUState* cpu);
|
||||
extern void func_1FC36940(CPUState* cpu);
|
||||
extern void func_1FC36998(CPUState* cpu);
|
||||
extern void func_1FC36A3C(CPUState* cpu);
|
||||
extern void func_1FC36B14(CPUState* cpu);
|
||||
extern void func_1FC36BEC(CPUState* cpu);
|
||||
extern void func_1FC36C60(CPUState* cpu);
|
||||
extern void func_1FC36CA4(CPUState* cpu);
|
||||
extern void func_1FC36CF0(CPUState* cpu);
|
||||
extern void func_1FC36D1C(CPUState* cpu);
|
||||
extern void func_1FC36D48(CPUState* cpu);
|
||||
@@ -1156,7 +1193,11 @@ extern void func_1FC41BA8(CPUState* cpu);
|
||||
extern void func_1FC41C04(CPUState* cpu);
|
||||
extern void func_1FC41C0C(CPUState* cpu);
|
||||
extern void func_1FC41C80(CPUState* cpu);
|
||||
extern void func_1FC41D18(CPUState* cpu);
|
||||
extern void func_1FC41DA4(CPUState* cpu);
|
||||
extern void func_1FC41DC8(CPUState* cpu);
|
||||
extern void func_1FC41DFC(CPUState* cpu);
|
||||
extern void func_1FC41E14(CPUState* cpu);
|
||||
extern void func_1FC41E30(CPUState* cpu);
|
||||
extern void func_1FC41ED8(CPUState* cpu);
|
||||
extern void func_1FC41F10(CPUState* cpu);
|
||||
@@ -1200,7 +1241,7 @@ typedef struct {
|
||||
PsxRecompFunc func;
|
||||
} DispatchEntry;
|
||||
|
||||
static const DispatchEntry dispatch_table[1174] = {
|
||||
static const DispatchEntry dispatch_table[1215] = {
|
||||
{ 0x00000500u, func_00000500 },
|
||||
{ 0x00000510u, func_00000510 },
|
||||
{ 0x00000520u, func_00000520 },
|
||||
@@ -1804,6 +1845,7 @@ static const DispatchEntry dispatch_table[1174] = {
|
||||
{ 0x1FC20FE8u, func_1FC20FE8 },
|
||||
{ 0x1FC21044u, func_1FC21044 },
|
||||
{ 0x1FC210C0u, func_1FC210C0 },
|
||||
{ 0x1FC210F0u, func_1FC210F0 },
|
||||
{ 0x1FC21128u, func_1FC21128 },
|
||||
{ 0x1FC21184u, func_1FC21184 },
|
||||
{ 0x1FC21200u, func_1FC21200 },
|
||||
@@ -2106,6 +2148,7 @@ static const DispatchEntry dispatch_table[1174] = {
|
||||
{ 0x1FC34AF0u, func_1FC34AF0 },
|
||||
{ 0x1FC34B08u, func_1FC34B08 },
|
||||
{ 0x1FC34B58u, func_1FC34B58 },
|
||||
{ 0x1FC34E00u, func_1FC34E00 },
|
||||
{ 0x1FC34EC0u, func_1FC34EC0 },
|
||||
{ 0x1FC34EC4u, func_1FC34EC4 },
|
||||
{ 0x1FC35128u, func_1FC35128 },
|
||||
@@ -2142,9 +2185,44 @@ static const DispatchEntry dispatch_table[1174] = {
|
||||
{ 0x1FC35F78u, func_1FC35F78 },
|
||||
{ 0x1FC35FA0u, func_1FC35FA0 },
|
||||
{ 0x1FC35FD0u, func_1FC35FD0 },
|
||||
{ 0x1FC36000u, func_1FC36000 },
|
||||
{ 0x1FC36024u, func_1FC36024 },
|
||||
{ 0x1FC36068u, func_1FC36068 },
|
||||
{ 0x1FC36094u, func_1FC36094 },
|
||||
{ 0x1FC360E0u, func_1FC360E0 },
|
||||
{ 0x1FC36104u, func_1FC36104 },
|
||||
{ 0x1FC3614Cu, func_1FC3614C },
|
||||
{ 0x1FC36178u, func_1FC36178 },
|
||||
{ 0x1FC361A0u, func_1FC361A0 },
|
||||
{ 0x1FC361CCu, func_1FC361CC },
|
||||
{ 0x1FC361F4u, func_1FC361F4 },
|
||||
{ 0x1FC3621Cu, func_1FC3621C },
|
||||
{ 0x1FC36244u, func_1FC36244 },
|
||||
{ 0x1FC3625Cu, func_1FC3625C },
|
||||
{ 0x1FC36280u, func_1FC36280 },
|
||||
{ 0x1FC36294u, func_1FC36294 },
|
||||
{ 0x1FC362BCu, func_1FC362BC },
|
||||
{ 0x1FC362D0u, func_1FC362D0 },
|
||||
{ 0x1FC3632Cu, func_1FC3632C },
|
||||
{ 0x1FC36388u, func_1FC36388 },
|
||||
{ 0x1FC363A0u, func_1FC363A0 },
|
||||
{ 0x1FC363F0u, func_1FC363F0 },
|
||||
{ 0x1FC36458u, func_1FC36458 },
|
||||
{ 0x1FC364DCu, func_1FC364DC },
|
||||
{ 0x1FC3653Cu, func_1FC3653C },
|
||||
{ 0x1FC365C4u, func_1FC365C4 },
|
||||
{ 0x1FC365F8u, func_1FC365F8 },
|
||||
{ 0x1FC36684u, func_1FC36684 },
|
||||
{ 0x1FC36740u, func_1FC36740 },
|
||||
{ 0x1FC367D4u, func_1FC367D4 },
|
||||
{ 0x1FC36880u, func_1FC36880 },
|
||||
{ 0x1FC36940u, func_1FC36940 },
|
||||
{ 0x1FC36998u, func_1FC36998 },
|
||||
{ 0x1FC36A3Cu, func_1FC36A3C },
|
||||
{ 0x1FC36B14u, func_1FC36B14 },
|
||||
{ 0x1FC36BECu, func_1FC36BEC },
|
||||
{ 0x1FC36C60u, func_1FC36C60 },
|
||||
{ 0x1FC36CA4u, func_1FC36CA4 },
|
||||
{ 0x1FC36CF0u, func_1FC36CF0 },
|
||||
{ 0x1FC36D1Cu, func_1FC36D1C },
|
||||
{ 0x1FC36D48u, func_1FC36D48 },
|
||||
@@ -2339,7 +2417,11 @@ static const DispatchEntry dispatch_table[1174] = {
|
||||
{ 0x1FC41C04u, func_1FC41C04 },
|
||||
{ 0x1FC41C0Cu, func_1FC41C0C },
|
||||
{ 0x1FC41C80u, func_1FC41C80 },
|
||||
{ 0x1FC41D18u, func_1FC41D18 },
|
||||
{ 0x1FC41DA4u, func_1FC41DA4 },
|
||||
{ 0x1FC41DC8u, func_1FC41DC8 },
|
||||
{ 0x1FC41DFCu, func_1FC41DFC },
|
||||
{ 0x1FC41E14u, func_1FC41E14 },
|
||||
{ 0x1FC41E30u, func_1FC41E30 },
|
||||
{ 0x1FC41ED8u, func_1FC41ED8 },
|
||||
{ 0x1FC41F10u, func_1FC41F10 },
|
||||
@@ -2395,7 +2477,7 @@ void psx_dispatch(CPUState* cpu, uint32_t addr) {
|
||||
for (;;) {
|
||||
cpu->pc = 0;
|
||||
uint32_t phys = normalize(addr);
|
||||
int lo = 0, hi = 1174 - 1;
|
||||
int lo = 0, hi = 1215 - 1;
|
||||
int found = 0;
|
||||
while (lo <= hi) {
|
||||
int mid = (lo + hi) / 2;
|
||||
|
||||
+4174
-2334
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,3 @@
|
||||
[
|
||||
{"address": "0xBFC02324", "reason": "addr=0xBFC0233C raw=0x44801000 opcode=0x11: top-level opcode 0x11 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC336F0", "reason": "addr=0xBFC3370C raw=0x48C8E800 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC33C98", "reason": "addr=0xBFC33C98 raw=0x4884F000 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC33CC0", "reason": "addr=0xBFC33CCC raw=0x012A4822 opcode=0x00: SPECIAL funct 0x22 not implemented in Phase 1a strict translator"},
|
||||
{"address": "0xBFC33F00", "reason": "addr=0xBFC33F0C raw=0x48C46800 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC33F20", "reason": "addr=0xBFC33F2C raw=0x48C4A800 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC33FB4", "reason": "addr=0xBFC33FBC raw=0x48C4C000 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC33FCC", "reason": "addr=0xBFC33FCC raw=0x48C4D000 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC33FD8", "reason": "addr=0xBFC33FD8 raw=0x48C4D800 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC33FE4", "reason": "addr=0xBFC33FE4 raw=0x48C4E000 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC35AD8", "reason": "addr=0xBFC35AEC raw=0x48C80000 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC35B08", "reason": "addr=0xBFC35B1C raw=0x48C84000 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC35B38", "reason": "addr=0xBFC35B4C raw=0x48C88000 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC35B68", "reason": "addr=0xBFC35B74 raw=0x48C82800 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC36068", "reason": "addr=0xBFC36068 raw=0xC8800000 opcode=0x32: top-level opcode 0x32 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC362D0", "reason": "addr=0xBFC362D0 raw=0x484D0000 opcode=0x12: top-level opcode 0x12 not implemented in Phase 1a"},
|
||||
{"address": "0xBFC36740", "reason": "addr=0xBFC36740 raw=0xC8800000 opcode=0x32: top-level opcode 0x32 not implemented in Phase 1a"}
|
||||
{"address": "0xBFC02324", "reason": "addr=0xBFC0233C raw=0x44801000 opcode=0x11: top-level opcode 0x11 not implemented in Phase 1a"}
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schema": "psxrecomp-v4 phase2 seeds",
|
||||
"source": "generated/ghidra_function_starts.json + phase1c seeds + dispatch_miss_seeds + preserved manual",
|
||||
"seed_count": 960,
|
||||
"source": "generated/ghidra_function_starts.json + phase1c seeds + dispatch_miss_seeds + preserved manual + gte_lib_seeds",
|
||||
"seed_count": 1006,
|
||||
"seeds": [
|
||||
{
|
||||
"address": "0xBFC00000",
|
||||
@@ -4802,6 +4802,190 @@
|
||||
"address": "0xBFC41E14",
|
||||
"label": "dispatch_miss_0xBFC41E14",
|
||||
"rationale": "Phase 4.5 dispatch miss from 3D rendering seeds"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC34E00",
|
||||
"name": "gte_lib_BFC34E00"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC3586C",
|
||||
"name": "gte_lib_BFC3586C"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC359B4",
|
||||
"name": "gte_lib_BFC359B4"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC35AD8",
|
||||
"name": "gte_lib_BFC35AD8"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC35B08",
|
||||
"name": "gte_lib_BFC35B08"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC35B38",
|
||||
"name": "gte_lib_BFC35B38"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC35B68",
|
||||
"name": "gte_lib_BFC35B68"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36000",
|
||||
"name": "gte_lib_BFC36000"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36024",
|
||||
"name": "gte_lib_BFC36024"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36068",
|
||||
"name": "gte_lib_BFC36068"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36094",
|
||||
"name": "gte_lib_BFC36094"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC360E0",
|
||||
"name": "gte_lib_BFC360E0"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36104",
|
||||
"name": "gte_lib_BFC36104"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC3614C",
|
||||
"name": "gte_lib_BFC3614C"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36178",
|
||||
"name": "gte_lib_BFC36178"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC361A0",
|
||||
"name": "gte_lib_BFC361A0"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC361CC",
|
||||
"name": "gte_lib_BFC361CC"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC361F4",
|
||||
"name": "gte_lib_BFC361F4"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC3621C",
|
||||
"name": "gte_lib_BFC3621C"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36244",
|
||||
"name": "gte_lib_BFC36244"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC3625C",
|
||||
"name": "gte_lib_BFC3625C"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36280",
|
||||
"name": "gte_lib_BFC36280"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36294",
|
||||
"name": "gte_lib_BFC36294"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC362BC",
|
||||
"name": "gte_lib_BFC362BC"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC362D0",
|
||||
"name": "gte_lib_BFC362D0"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC3632C",
|
||||
"name": "gte_lib_BFC3632C"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36388",
|
||||
"name": "gte_lib_BFC36388"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC363A0",
|
||||
"name": "gte_lib_BFC363A0"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC363F0",
|
||||
"name": "gte_lib_BFC363F0"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36458",
|
||||
"name": "gte_lib_BFC36458"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC364DC",
|
||||
"name": "gte_lib_BFC364DC"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC3653C",
|
||||
"name": "gte_lib_BFC3653C"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC365C4",
|
||||
"name": "gte_lib_BFC365C4"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC365F8",
|
||||
"name": "gte_lib_BFC365F8"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36684",
|
||||
"name": "gte_lib_BFC36684"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36740",
|
||||
"name": "gte_lib_BFC36740"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC367D4",
|
||||
"name": "gte_lib_BFC367D4"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36880",
|
||||
"name": "gte_lib_BFC36880"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36940",
|
||||
"name": "gte_lib_BFC36940"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36998",
|
||||
"name": "gte_lib_BFC36998"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36A3C",
|
||||
"name": "gte_lib_BFC36A3C"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36B14",
|
||||
"name": "gte_lib_BFC36B14"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36BEC",
|
||||
"name": "gte_lib_BFC36BEC"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36C60",
|
||||
"name": "gte_lib_BFC36C60"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36CA4",
|
||||
"name": "gte_lib_BFC36CA4"
|
||||
},
|
||||
{
|
||||
"address": "0xBFC36FF0",
|
||||
"name": "gte_lib_BFC36FF0"
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare two images and report pixel differences."""
|
||||
import sys
|
||||
from PIL import Image
|
||||
|
||||
a = Image.open(sys.argv[1])
|
||||
b = Image.open(sys.argv[2])
|
||||
diff = 0
|
||||
for y in range(a.height):
|
||||
for x in range(a.width):
|
||||
if a.getpixel((x, y)) != b.getpixel((x, y)):
|
||||
diff += 1
|
||||
print(f'Pixels different: {diff} out of {a.width * a.height}')
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Capture DuckStation oracle VRAM as screenshot via debug server."""
|
||||
import socket, json, sys
|
||||
from PIL import Image
|
||||
|
||||
port = int(sys.argv[1]) if len(sys.argv) > 1 else 4371
|
||||
out = sys.argv[2] if len(sys.argv) > 2 else "ds_screenshot.png"
|
||||
|
||||
s = socket.create_connection(('localhost', port))
|
||||
cmd = '{"cmd":"read_vram","x":0,"y":0,"w":640,"h":480}\n'
|
||||
s.sendall(cmd.encode())
|
||||
data = b''
|
||||
while True:
|
||||
chunk = s.recv(65536)
|
||||
if not chunk:
|
||||
break
|
||||
data += chunk
|
||||
if b'\n' in data:
|
||||
break
|
||||
s.close()
|
||||
|
||||
resp = json.loads(data.split(b'\n')[0])
|
||||
if not resp.get('ok'):
|
||||
print('Error:', resp)
|
||||
sys.exit(1)
|
||||
|
||||
pixels = bytes.fromhex(resp['pixels'])
|
||||
w, h = resp['w'], resp['h']
|
||||
img = Image.new('RGB', (w, h))
|
||||
for y in range(h):
|
||||
for x in range(w):
|
||||
off = (y * w + x) * 2
|
||||
if off + 1 >= len(pixels):
|
||||
break
|
||||
val = pixels[off] | (pixels[off + 1] << 8)
|
||||
r = (val & 0x1F) << 3
|
||||
g = ((val >> 5) & 0x1F) << 3
|
||||
b = ((val >> 10) & 0x1F) << 3
|
||||
img.putpixel((x, y), (r, g, b))
|
||||
|
||||
img.save(out)
|
||||
print(f'Saved {out} ({w}x{h})')
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Stitch DuckStation VRAM into a full screenshot using small tile reads."""
|
||||
import socket, json, sys
|
||||
from PIL import Image
|
||||
|
||||
port = int(sys.argv[1]) if len(sys.argv) > 1 else 4371
|
||||
out = sys.argv[2] if len(sys.argv) > 2 else "ds_screenshot.png"
|
||||
FULL_W, FULL_H = 320, 240
|
||||
TILE = 32
|
||||
|
||||
img = Image.new('RGB', (FULL_W, FULL_H))
|
||||
|
||||
for ty in range(0, FULL_H, TILE):
|
||||
for tx in range(0, FULL_W, TILE):
|
||||
tw = min(TILE, FULL_W - tx)
|
||||
th = min(TILE, FULL_H - ty)
|
||||
try:
|
||||
s = socket.create_connection(('localhost', port), timeout=5)
|
||||
cmd = f'{{"cmd":"read_vram","x":{tx},"y":{ty},"w":{tw},"h":{th}}}\n'
|
||||
s.sendall(cmd.encode())
|
||||
data = b''
|
||||
while True:
|
||||
chunk = s.recv(65536)
|
||||
if not chunk:
|
||||
break
|
||||
data += chunk
|
||||
if b'\n' in data:
|
||||
break
|
||||
s.close()
|
||||
resp = json.loads(data.split(b'\n')[0])
|
||||
if not resp.get('ok'):
|
||||
continue
|
||||
pixels = bytes.fromhex(resp['pixels'])
|
||||
rw, rh = resp['w'], resp['h']
|
||||
for py in range(rh):
|
||||
for px in range(rw):
|
||||
off = (py * rw + px) * 2
|
||||
if off + 1 >= len(pixels):
|
||||
break
|
||||
val = pixels[off] | (pixels[off + 1] << 8)
|
||||
r = (val & 0x1F) << 3
|
||||
g = ((val >> 5) & 0x1F) << 3
|
||||
b = ((val >> 10) & 0x1F) << 3
|
||||
img.putpixel((tx + px, ty + py), (r, g, b))
|
||||
except Exception as e:
|
||||
print(f'Tile ({tx},{ty}) failed: {e}', file=sys.stderr)
|
||||
|
||||
img.save(out)
|
||||
print(f'Saved {out} ({FULL_W}x{FULL_H})')
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Stitch VRAM tiles with configurable start position."""
|
||||
import socket, json, sys
|
||||
from PIL import Image
|
||||
|
||||
port = int(sys.argv[1]) if len(sys.argv) > 1 else 4371
|
||||
out = sys.argv[2] if len(sys.argv) > 2 else "vram_capture.png"
|
||||
START_X = int(sys.argv[3]) if len(sys.argv) > 3 else 0
|
||||
START_Y = int(sys.argv[4]) if len(sys.argv) > 4 else 0
|
||||
FULL_W = int(sys.argv[5]) if len(sys.argv) > 5 else 320
|
||||
FULL_H = int(sys.argv[6]) if len(sys.argv) > 6 else 240
|
||||
TILE = 32
|
||||
|
||||
img = Image.new('RGB', (FULL_W, FULL_H))
|
||||
|
||||
for ty in range(0, FULL_H, TILE):
|
||||
for tx in range(0, FULL_W, TILE):
|
||||
tw = min(TILE, FULL_W - tx)
|
||||
th = min(TILE, FULL_H - ty)
|
||||
try:
|
||||
s = socket.create_connection(('localhost', port), timeout=5)
|
||||
cmd = f'{{"cmd":"read_vram","x":{START_X+tx},"y":{START_Y+ty},"w":{tw},"h":{th}}}\n'
|
||||
s.sendall(cmd.encode())
|
||||
data = b''
|
||||
while b'\n' not in data:
|
||||
data += s.recv(65536)
|
||||
s.close()
|
||||
resp = json.loads(data.split(b'\n')[0])
|
||||
if not resp.get('ok'):
|
||||
continue
|
||||
pixels = bytes.fromhex(resp['pixels'])
|
||||
rw, rh = resp['w'], resp['h']
|
||||
for py in range(rh):
|
||||
for px in range(rw):
|
||||
off = (py * rw + px) * 2
|
||||
if off + 1 >= len(pixels):
|
||||
break
|
||||
val = pixels[off] | (pixels[off + 1] << 8)
|
||||
r = (val & 0x1F) << 3
|
||||
g = ((val >> 5) & 0x1F) << 3
|
||||
b = ((val >> 10) & 0x1F) << 3
|
||||
img.putpixel((tx + px, ty + py), (r, g, b))
|
||||
except Exception as e:
|
||||
print(f'Tile ({START_X+tx},{START_Y+ty}) failed: {e}', file=sys.stderr)
|
||||
|
||||
img.save(out)
|
||||
print(f'Saved {out} ({FULL_W}x{FULL_H} from VRAM {START_X},{START_Y})')
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Dump a jump table from PSX RAM via debug server."""
|
||||
import socket, json, struct, sys
|
||||
|
||||
port = int(sys.argv[1]) if len(sys.argv) > 1 else 4370
|
||||
addr = int(sys.argv[2], 16) if len(sys.argv) > 2 else 0x8005AD0C
|
||||
count = int(sys.argv[3]) if len(sys.argv) > 3 else 24
|
||||
base_state = int(sys.argv[4]) if len(sys.argv) > 4 else 2
|
||||
|
||||
s = socket.create_connection(('localhost', port), timeout=5)
|
||||
cmd = f'{{"cmd":"read_ram","addr":"0x{addr:08X}","len":{count*4}}}\n'
|
||||
s.sendall(cmd.encode())
|
||||
data = b''
|
||||
while b'\n' not in data:
|
||||
data += s.recv(65536)
|
||||
s.close()
|
||||
|
||||
resp = json.loads(data.split(b'\n')[0])
|
||||
raw = bytes.fromhex(resp['hex'])
|
||||
for i in range(count):
|
||||
val = struct.unpack_from('<I', raw, i * 4)[0]
|
||||
state = base_state + i
|
||||
print(f' state {state:2d} (idx {i:2d}): 0x{val:08X}')
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Find all JAL instructions targeting GTE library, with correct address decoding."""
|
||||
import struct
|
||||
from collections import defaultdict
|
||||
|
||||
ROM_PATH = "bios/SCPH1001.BIN"
|
||||
ROM_BASE = 0xBFC00000
|
||||
|
||||
CODE_REGIONS = [
|
||||
(0xBFC00000, 0xBFC0DC60),
|
||||
(0xBFC10000, 0xBFC16760),
|
||||
(0xBFC18000, 0xBFC42800),
|
||||
]
|
||||
|
||||
# GTE library ROM range
|
||||
GTE_ROM_LO = 0xBFC34E00
|
||||
GTE_ROM_HI = 0xBFC37000
|
||||
|
||||
# Corresponding RAM range (shell: ROM BFC18000 -> RAM 0x80030000)
|
||||
SHELL_ROM_BASE = 0xBFC18000
|
||||
SHELL_RAM_BASE = 0x80030000
|
||||
|
||||
def rom_to_ram(rom_addr):
|
||||
return SHELL_RAM_BASE + (rom_addr - SHELL_ROM_BASE)
|
||||
|
||||
GTE_RAM_LO = rom_to_ram(GTE_ROM_LO)
|
||||
GTE_RAM_HI = rom_to_ram(GTE_ROM_HI)
|
||||
|
||||
with open(ROM_PATH, "rb") as f:
|
||||
rom = f.read()
|
||||
|
||||
callers = []
|
||||
for lo, hi in CODE_REGIONS:
|
||||
for off in range(lo - ROM_BASE, hi - ROM_BASE, 4):
|
||||
word = struct.unpack_from("<I", rom, off)[0]
|
||||
opcode = (word >> 26) & 0x3F
|
||||
if opcode == 0x03: # JAL
|
||||
target26 = word & 0x03FFFFFF
|
||||
# Shell code runs from RAM, so PC[31:28] = 0x8
|
||||
# For kernel code running from ROM, PC[31:28] = 0xB
|
||||
# But kernel would call through RAM addresses too after shell copy
|
||||
# The recompiler uses 0x8 prefix for RAM targets
|
||||
target_ram = 0x80000000 | (target26 << 2)
|
||||
caller_rom = ROM_BASE + off
|
||||
|
||||
if GTE_RAM_LO <= target_ram < GTE_RAM_HI:
|
||||
target_rom = SHELL_ROM_BASE + (target_ram - SHELL_RAM_BASE)
|
||||
callers.append((caller_rom, target_ram, target_rom))
|
||||
|
||||
print(f"JAL calls to GTE library (RAM {GTE_RAM_LO:#x}-{GTE_RAM_HI:#x}):")
|
||||
print(f"Found: {len(callers)}")
|
||||
|
||||
by_target = defaultdict(list)
|
||||
for caller_rom, target_ram, target_rom in callers:
|
||||
by_target[target_rom].append(caller_rom)
|
||||
|
||||
for target in sorted(by_target):
|
||||
callers_list = by_target[target]
|
||||
ram = rom_to_ram(target)
|
||||
print(f"\n Target ROM 0x{target:08X} (RAM 0x{ram:08X}):")
|
||||
for c in callers_list:
|
||||
region = "kernel1" if c < 0xBFC10000 else ("kernel2" if c < 0xBFC18000 else "shell")
|
||||
# Find approximate function
|
||||
func_rom = (c & ~0xFFF) # rough 4KB alignment
|
||||
print(f" Called from 0x{c:08X} ({region})")
|
||||
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Find all function entry points in the GTE library region by scanning for jr $ra patterns."""
|
||||
import struct, json
|
||||
|
||||
ROM_PATH = "bios/SCPH1001.BIN"
|
||||
ROM_BASE = 0xBFC00000
|
||||
|
||||
# GTE library region (from the audit: BFC34F00 to BFC36D00)
|
||||
# Actually scan a wider range to catch everything
|
||||
SCAN_LO = 0xBFC34E00
|
||||
SCAN_HI = 0xBFC37000
|
||||
|
||||
with open(ROM_PATH, "rb") as f:
|
||||
rom = f.read()
|
||||
|
||||
# Find all jr $ra instructions in the scan range
|
||||
JR_RA = 0x03E00008
|
||||
NOP = 0x00000000
|
||||
|
||||
entries = []
|
||||
off_lo = SCAN_LO - ROM_BASE
|
||||
off_hi = SCAN_HI - ROM_BASE
|
||||
|
||||
# Strategy: after every "jr $ra; nop" pair, the next non-NOP word starts a new function
|
||||
i = off_lo
|
||||
while i < off_hi - 8:
|
||||
word = struct.unpack_from("<I", rom, i)[0]
|
||||
if word == JR_RA:
|
||||
# Found jr $ra at offset i. Delay slot is at i+4.
|
||||
delay = struct.unpack_from("<I", rom, i + 4)[0]
|
||||
# Next instruction after delay slot
|
||||
next_off = i + 8
|
||||
if next_off < off_hi:
|
||||
next_word = struct.unpack_from("<I", rom, next_off)[0]
|
||||
if next_word != NOP and next_word != JR_RA:
|
||||
entry_addr = ROM_BASE + next_off
|
||||
entries.append(entry_addr)
|
||||
i += 4
|
||||
|
||||
# Also check the very first instruction in the range as a potential entry
|
||||
first_word = struct.unpack_from("<I", rom, off_lo)[0]
|
||||
first_addr = ROM_BASE + off_lo
|
||||
if first_addr not in entries:
|
||||
entries.insert(0, first_addr)
|
||||
|
||||
# Deduplicate and sort
|
||||
entries = sorted(set(entries))
|
||||
|
||||
# Load existing seeds to check which are new
|
||||
existing_seeds = set()
|
||||
seed_file = "recompiler/seeds/phase2_ghidra_seeds.json"
|
||||
try:
|
||||
with open(seed_file) as f:
|
||||
data = json.load(f)
|
||||
for s in data.get("seeds", []):
|
||||
existing_seeds.add(int(s["address"], 16))
|
||||
except:
|
||||
pass
|
||||
|
||||
print(f"Found {len(entries)} potential function entries in {SCAN_LO:#010x}-{SCAN_HI:#010x}")
|
||||
new_count = 0
|
||||
for addr in entries:
|
||||
# Show the first few bytes for verification
|
||||
off = addr - ROM_BASE
|
||||
words = [struct.unpack_from("<I", rom, off + j*4)[0] for j in range(3)]
|
||||
mnemonics = []
|
||||
for w in words:
|
||||
op = (w >> 26) & 0x3F
|
||||
if op == 0x32:
|
||||
mnemonics.append("lwc2")
|
||||
elif op == 0x3A:
|
||||
mnemonics.append("swc2")
|
||||
elif op == 0x12:
|
||||
mnemonics.append("cop2")
|
||||
elif op == 0x09:
|
||||
mnemonics.append("addiu")
|
||||
elif op == 0x0F:
|
||||
mnemonics.append("lui")
|
||||
elif op == 0x00:
|
||||
func = w & 0x3F
|
||||
if func == 0x08:
|
||||
mnemonics.append("jr")
|
||||
elif func == 0x21:
|
||||
mnemonics.append("addu")
|
||||
elif func == 0x00 and w == 0:
|
||||
mnemonics.append("nop")
|
||||
else:
|
||||
mnemonics.append(f"r{func:#04x}")
|
||||
else:
|
||||
mnemonics.append(f"op{op:#04x}")
|
||||
|
||||
is_new = addr not in existing_seeds
|
||||
marker = "NEW" if is_new else "exists"
|
||||
if is_new:
|
||||
new_count += 1
|
||||
print(f" {addr:#010x}: {' '.join(mnemonics):30s} [{marker}]")
|
||||
|
||||
print(f"\n{new_count} new seeds to add")
|
||||
|
||||
# Output as seed entries
|
||||
if new_count > 0:
|
||||
print("\nSeed JSON entries:")
|
||||
for addr in entries:
|
||||
if addr not in existing_seeds:
|
||||
print(f' {{"address": "0x{addr:08X}", "name": "gte_lib_{addr:08X}"}}')
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check emitted function sizes vs expected ROM span."""
|
||||
import re, sys
|
||||
|
||||
with open('generated/SCPH1001_full.c') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
funcs = []
|
||||
for i, line in enumerate(lines, 1):
|
||||
m = re.match(r'^void func_1FC([0-9A-Fa-f]+)\(CPUState\* cpu\) \{', line)
|
||||
if m:
|
||||
addr = int(m.group(1), 16) + 0xBFC00000
|
||||
funcs.append((addr, i))
|
||||
|
||||
targets = [0xBFC34EC4, 0xBFC35FD0, 0xBFC36068, 0xBFC362D0, 0xBFC36740, 0xBFC36CF0]
|
||||
for t in targets:
|
||||
for i, (addr, lineno) in enumerate(funcs):
|
||||
if addr == t:
|
||||
next_line = funcs[i + 1][1] if i + 1 < len(funcs) else len(lines)
|
||||
code_lines = next_line - lineno
|
||||
rom_addrs = set()
|
||||
for li in range(lineno - 1, min(next_line - 1, len(lines))):
|
||||
m2 = re.search(r'0x(BFC[0-9A-Fa-f]{5}):', lines[li])
|
||||
if m2:
|
||||
rom_addrs.add(int(m2.group(1), 16))
|
||||
if rom_addrs:
|
||||
lo = min(rom_addrs)
|
||||
hi = max(rom_addrs)
|
||||
rom_bytes = hi - lo + 4
|
||||
else:
|
||||
lo = hi = rom_bytes = 0
|
||||
print(f'func_1FC{t - 0xBFC00000:05X}: {code_lines:4d} C lines, '
|
||||
f'ROM 0x{lo:08X}-0x{hi:08X} ({rom_bytes:5d} bytes = {rom_bytes // 4:4d} insns)')
|
||||
break
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Read GPU state from debug server."""
|
||||
import socket, json, sys
|
||||
|
||||
port = int(sys.argv[1]) if len(sys.argv) > 1 else 4370
|
||||
s = socket.create_connection(('localhost', port), timeout=5)
|
||||
s.sendall(b'{"cmd":"gpu_state"}\n')
|
||||
data = b''
|
||||
while b'\n' not in data:
|
||||
data += s.recv(4096)
|
||||
s.close()
|
||||
resp = json.loads(data.split(b'\n')[0])
|
||||
if resp.get('ok'):
|
||||
for k, v in sorted(resp.items()):
|
||||
if k not in ('ok', 'id'):
|
||||
print(f' {k}: {v}')
|
||||
else:
|
||||
print(f'Error: {resp}')
|
||||
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Audit GTE instructions: compare ROM contents vs generated code."""
|
||||
import struct, re, sys
|
||||
|
||||
ROM_PATH = "bios/SCPH1001.BIN"
|
||||
GEN_PATH = "generated/SCPH1001_full.c"
|
||||
ROM_BASE = 0xBFC00000
|
||||
|
||||
# 1. Scan ROM for all COP2 (GTE) instructions
|
||||
with open(ROM_PATH, "rb") as f:
|
||||
rom = f.read()
|
||||
|
||||
rom_gte = [] # (rom_addr, instruction_word)
|
||||
for off in range(0, len(rom), 4):
|
||||
word = struct.unpack_from("<I", rom, off)[0]
|
||||
opcode = (word >> 26) & 0x3F
|
||||
if opcode == 0x12: # COP2
|
||||
bit25 = (word >> 25) & 1
|
||||
if bit25 == 1: # GTE command (not MFC2/MTC2/CFC2/CTC2)
|
||||
rom_addr = ROM_BASE + off
|
||||
rom_gte.append((rom_addr, word))
|
||||
|
||||
# Also find LWC2/SWC2 (COP2 load/store) — opcodes 0x32 (LWC2) and 0x3A (SWC2)
|
||||
rom_lwc2_swc2 = []
|
||||
for off in range(0, len(rom), 4):
|
||||
word = struct.unpack_from("<I", rom, off)[0]
|
||||
opcode = (word >> 26) & 0x3F
|
||||
if opcode in (0x32, 0x3A): # LWC2 or SWC2
|
||||
rom_addr = ROM_BASE + off
|
||||
rom_lwc2_swc2.append((rom_addr, word))
|
||||
|
||||
# 2. Scan generated code for gte_execute addresses
|
||||
gen_gte_addrs = set()
|
||||
with open(GEN_PATH, "r") as f:
|
||||
for line in f:
|
||||
# Pattern: /* 0xBFCxxxxx: xxxxxxxx gte cmd ... */
|
||||
m = re.search(r'/\* 0x(BFC[0-9A-Fa-f]{5}):', line)
|
||||
if m and 'gte' in line.lower():
|
||||
gen_gte_addrs.add(int(m.group(1), 16))
|
||||
# Also match gte_execute calls with preceding comment
|
||||
if 'gte_execute' in line:
|
||||
# Find the ROM address from the comment above
|
||||
pass
|
||||
|
||||
# Better approach: find all ROM addresses that have gte_execute
|
||||
# by looking for the comment pattern before gte_execute
|
||||
gen_gte_addrs2 = set()
|
||||
with open(GEN_PATH, "r") as f:
|
||||
prev_addr = None
|
||||
for line in f:
|
||||
m = re.search(r'/\* 0x(BFC[0-9A-Fa-f]{5}):', line)
|
||||
if m:
|
||||
prev_addr = int(m.group(1), 16)
|
||||
if 'gte_execute' in line and prev_addr:
|
||||
gen_gte_addrs2.add(prev_addr)
|
||||
|
||||
# Also find all lwc2/swc2 in generated code
|
||||
gen_cop2_load_addrs = set()
|
||||
with open(GEN_PATH, "r") as f:
|
||||
for line in f:
|
||||
if 'lwc2' in line.lower() or 'swc2' in line.lower():
|
||||
m = re.search(r'/\* 0x(BFC[0-9A-Fa-f]{5}):', line)
|
||||
if m:
|
||||
gen_cop2_load_addrs.add(int(m.group(1), 16))
|
||||
|
||||
# 3. Compare
|
||||
print(f"ROM GTE commands (COP2 bit25=1): {len(rom_gte)}")
|
||||
print(f"ROM LWC2/SWC2 instructions: {len(rom_lwc2_swc2)}")
|
||||
print(f"Generated gte_execute calls: {len(gen_gte_addrs2)}")
|
||||
print(f"Generated lwc2/swc2 accesses: {len(gen_cop2_load_addrs)}")
|
||||
print()
|
||||
|
||||
# Missing GTE commands
|
||||
missing_gte = [(addr, word) for addr, word in rom_gte if addr not in gen_gte_addrs2]
|
||||
print(f"MISSING GTE commands: {len(missing_gte)}")
|
||||
|
||||
# Group by 256-byte region to show function clusters
|
||||
from collections import defaultdict
|
||||
regions = defaultdict(list)
|
||||
for addr, word in missing_gte:
|
||||
region = addr & ~0xFF
|
||||
func_code = (word & 0x3F)
|
||||
regions[region].append((addr, word, func_code))
|
||||
|
||||
print(f"Missing spread across {len(regions)} regions:")
|
||||
for region in sorted(regions):
|
||||
entries = regions[region]
|
||||
codes = [f"0x{e[2]:02X}" for e in entries]
|
||||
addrs = [f"0x{e[0]:08X}" for e in entries]
|
||||
print(f" 0x{region:08X}: {len(entries)} missing — ops: {', '.join(codes)}")
|
||||
for a, w, c in entries:
|
||||
print(f" 0x{a:08X}: 0x{w:08X} (op {c:#04x})")
|
||||
|
||||
# Missing LWC2/SWC2
|
||||
missing_loads = [(addr, word) for addr, word in rom_lwc2_swc2 if addr not in gen_cop2_load_addrs]
|
||||
print(f"\nMissing LWC2/SWC2: {len(missing_loads)} out of {len(rom_lwc2_swc2)}")
|
||||
if missing_loads:
|
||||
load_regions = defaultdict(list)
|
||||
for addr, word in missing_loads:
|
||||
load_regions[addr & ~0xFF].append(addr)
|
||||
for region in sorted(load_regions):
|
||||
print(f" 0x{region:08X}: {len(load_regions[region])} missing")
|
||||
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check which ROM functions in the GTE-heavy region are emitted."""
|
||||
import struct, re
|
||||
|
||||
ROM_PATH = "bios/SCPH1001.BIN"
|
||||
GEN_PATH = "generated/SCPH1001_full.c"
|
||||
ROM_BASE = 0xBFC00000
|
||||
|
||||
# Code regions from CLAUDE.md
|
||||
CODE_REGIONS = [
|
||||
(0xBFC00000, 0xBFC0DC60), # kernel 1
|
||||
(0xBFC10000, 0xBFC16760), # kernel 2
|
||||
(0xBFC18000, 0xBFC42800), # shell
|
||||
]
|
||||
|
||||
def in_code_region(addr):
|
||||
return any(lo <= addr < hi for lo, hi in CODE_REGIONS)
|
||||
|
||||
# Scan ROM for GTE in code regions only
|
||||
with open(ROM_PATH, "rb") as f:
|
||||
rom = f.read()
|
||||
|
||||
gte_in_code = []
|
||||
for off in range(0, len(rom), 4):
|
||||
word = struct.unpack_from("<I", rom, off)[0]
|
||||
opcode = (word >> 26) & 0x3F
|
||||
addr = ROM_BASE + off
|
||||
if not in_code_region(addr):
|
||||
continue
|
||||
if opcode == 0x12 and (word >> 25) & 1: # COP2 command
|
||||
gte_in_code.append((addr, word))
|
||||
elif opcode in (0x32, 0x3A): # LWC2/SWC2
|
||||
gte_in_code.append((addr, word))
|
||||
|
||||
# Find all emitted function ranges
|
||||
emitted = [] # (start_addr, end_addr_or_None)
|
||||
with open(GEN_PATH, "r") as f:
|
||||
for line in f:
|
||||
m = re.match(r'^void func_1FC([0-9A-Fa-f]+)\(CPUState\* cpu\) \{', line)
|
||||
if m:
|
||||
addr = 0xBFC00000 + int(m.group(1), 16)
|
||||
emitted.append(addr)
|
||||
|
||||
emitted.sort()
|
||||
emitted_set = set(emitted)
|
||||
|
||||
# Find all gte_execute ROM addresses
|
||||
gen_gte = set()
|
||||
with open(GEN_PATH, "r") as f:
|
||||
prev_addr = None
|
||||
for line in f:
|
||||
m = re.search(r'/\* 0x(BFC[0-9A-Fa-f]{5}):', line)
|
||||
if m:
|
||||
prev_addr = int(m.group(1), 16)
|
||||
if ('gte_execute' in line or 'gte_data' in line or 'gte_ctrl' in line) and prev_addr:
|
||||
gen_gte.add(prev_addr)
|
||||
|
||||
# Focus on BFC34F00-BFC36D00 region
|
||||
print("=== GTE instructions in BFC34F00-BFC36D00 ===")
|
||||
region_gte = [(a, w) for a, w in gte_in_code if 0xBFC34F00 <= a < 0xBFC36D00]
|
||||
print(f"Total in region: {len(region_gte)}")
|
||||
|
||||
missing = [(a, w) for a, w in region_gte if a not in gen_gte]
|
||||
present = [(a, w) for a, w in region_gte if a in gen_gte]
|
||||
print(f"Present in generated: {len(present)}")
|
||||
print(f"Missing: {len(missing)}")
|
||||
|
||||
# Check which emitted functions are in this range
|
||||
funcs_in_range = [a for a in emitted if 0xBFC34F00 <= a < 0xBFC36D00]
|
||||
print(f"\nEmitted functions in range: {len(funcs_in_range)}")
|
||||
for a in funcs_in_range:
|
||||
print(f" func_1FC{a-0xBFC00000:05X} (0x{a:08X})")
|
||||
|
||||
# For each missing GTE, find nearest emitted function
|
||||
print(f"\nMissing GTE instructions by function:")
|
||||
from collections import defaultdict
|
||||
by_func = defaultdict(list)
|
||||
for addr, word in missing:
|
||||
# Find the function this address belongs to
|
||||
best = None
|
||||
for fa in emitted:
|
||||
if fa <= addr:
|
||||
best = fa
|
||||
else:
|
||||
break
|
||||
by_func[best].append((addr, word))
|
||||
|
||||
for func_addr in sorted(by_func):
|
||||
entries = by_func[func_addr]
|
||||
if func_addr:
|
||||
name = f"func_1FC{func_addr-0xBFC00000:05X}"
|
||||
in_dispatch = func_addr in emitted_set
|
||||
else:
|
||||
name = "(no function found)"
|
||||
in_dispatch = False
|
||||
print(f" {name}: {len(entries)} missing GTE instructions")
|
||||
for a, w in entries:
|
||||
op = w & 0x3F
|
||||
opcode = (w >> 26) & 0x3F
|
||||
kind = "COP2cmd" if opcode == 0x12 else ("LWC2" if opcode == 0x32 else "SWC2")
|
||||
print(f" 0x{a:08X}: 0x{w:08X} ({kind} op=0x{op:02x})")
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rapidly poll a RAM address and report unique values seen."""
|
||||
import socket, json, struct, sys, time
|
||||
|
||||
port = int(sys.argv[1])
|
||||
addr = int(sys.argv[2], 16)
|
||||
duration = float(sys.argv[3]) if len(sys.argv) > 3 else 5.0
|
||||
label = sys.argv[4] if len(sys.argv) > 4 else "value"
|
||||
|
||||
seen = {}
|
||||
reads = 0
|
||||
start = time.time()
|
||||
|
||||
while time.time() - start < duration:
|
||||
try:
|
||||
s = socket.create_connection(('localhost', port), timeout=1)
|
||||
cmd = f'{{"cmd":"read_ram","addr":"0x{addr:08X}","len":4}}\n'
|
||||
s.sendall(cmd.encode())
|
||||
data = b''
|
||||
while b'\n' not in data:
|
||||
data += s.recv(4096)
|
||||
s.close()
|
||||
resp = json.loads(data.split(b'\n')[0])
|
||||
if resp.get('ok'):
|
||||
raw = bytes.fromhex(resp['hex'])
|
||||
val = struct.unpack_from('<I', raw, 0)[0]
|
||||
seen[val] = seen.get(val, 0) + 1
|
||||
reads += 1
|
||||
except:
|
||||
pass
|
||||
|
||||
elapsed = time.time() - start
|
||||
print(f'{label} at 0x{addr:08X}: {reads} reads in {elapsed:.1f}s')
|
||||
for val, count in sorted(seen.items()):
|
||||
print(f' 0x{val:08X} ({val:5d}): seen {count} times ({100*count/reads:.1f}%)')
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
import socket, json, struct, sys
|
||||
|
||||
port = int(sys.argv[1])
|
||||
addr = int(sys.argv[2], 16)
|
||||
count = int(sys.argv[3]) if len(sys.argv) > 3 else 4
|
||||
|
||||
s = socket.create_connection(('localhost', port), timeout=5)
|
||||
s.sendall(f'{{"cmd":"read_ram","addr":"0x{addr:08X}","len":{count*4}}}\n'.encode())
|
||||
data = b''
|
||||
while b'\n' not in data:
|
||||
data += s.recv(4096)
|
||||
s.close()
|
||||
d = json.loads(data.split(b'\n')[0])
|
||||
raw = bytes.fromhex(d['hex'])
|
||||
for i in range(count):
|
||||
v = struct.unpack_from('<I', raw, i * 4)[0]
|
||||
print(f' [{i}] 0x{v:08X}')
|
||||
Reference in New Issue
Block a user