TCP pgxp_shadow: read PGXP shadow slots by address or register

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthew Stanley
2026-10-02 11:57:55 -07:00
parent 36bea23bd2
commit ce22e54e89
3 changed files with 60 additions and 0 deletions
+8
View File
@@ -127,6 +127,14 @@ void pgxp_store_gte_reg(uint32_t addr, uint8_t reg);
int pgxp_load_precise_word(uint32_t addr, uint32_t packed,
int32_t *x16, int32_t *y16, uint16_t *z);
/* Debug read of one shadow slot (TCP pgxp_shadow). `space`: 0 = guest
* address (RAM / scratchpad), 1 = GPR index (32 = HI, 33 = LO), 2 = GTE data
* register. Returns 0 when the slot does not exist; *live says whether it
* belongs to the current generation. */
int pgxp_debug_shadow(int space, uint32_t key, int *live, uint32_t *value,
uint32_t *flags, int32_t *x16, int32_t *y16,
uint16_t *z);
/* --- test accessors (always compiled; trivial) ---------------------------- */
/* index 0..3 selects the SXY0..SXYP register shadow (GTE data regs 12..15). */
+35
View File
@@ -5319,6 +5319,40 @@ static void handle_geom_correction(int id, const char *json)
(unsigned long long)ps.word_no_z);
}
/* pgxp_shadow — read PGXP shadow slots: {"cmd":"pgxp_shadow","addr":A,
* "count":N} walks N guest words from A (RAM / scratchpad); "space":"gpr" or
* "gte" with "index"/"count" reads register shadows instead. Each slot: the
* word it describes, whether it is live this generation, the per-half/depth
* flags (1 = X, 2 = Y, 4 = Z) and the 16.16 positions. Pair it with read_ram
* to see whether a packet word still matches its shadow. */
static void handle_pgxp_shadow(int id, const char *json)
{
char sp[16] = "";
json_get_str(json, "space", sp, sizeof sp);
int space = strcmp(sp, "gpr") == 0 ? 1 : strcmp(sp, "gte") == 0 ? 2 : 0;
char addr_str[32] = "";
uint32_t key = space ? (uint32_t)json_get_int(json, "index", 0)
: (json_get_str(json, "addr", addr_str, sizeof addr_str)
? hex_to_u32(addr_str) : 0u);
int count = json_get_int(json, "count", 1);
if (count < 1) count = 1;
if (count > 256) count = 256;
static char buf[256 * 112 + 128];
size_t n = (size_t)snprintf(buf, sizeof buf, "{\"id\":%d,\"ok\":true,\"slots\":[", id);
for (int i = 0; i < count && n < sizeof buf - 128; i++) {
uint32_t k = space ? key + (uint32_t)i : key + (uint32_t)i * 4u;
int live = 0; uint32_t value = 0, flags = 0; int32_t x16 = 0, y16 = 0; uint16_t z = 0;
if (!pgxp_debug_shadow(space, k, &live, &value, &flags, &x16, &y16, &z))
break;
n += (size_t)snprintf(buf + n, sizeof buf - n,
"%s{\"key\":\"0x%08X\",\"live\":%d,\"value\":\"0x%08X\","
"\"flags\":%u,\"x16\":%d,\"y16\":%d,\"z\":%u}",
i ? "," : "", k, live, value, flags, x16, y16, z);
}
snprintf(buf + n, sizeof buf - n, "]}");
send_fmt("%s", buf);
}
/* pgxp — live-tune the value-propagation engine for one-toggle isolation runs
* without a rebuild: {"cmd":"pgxp","cpu_mode":0|1,"tolerance":F}. Fields are
* optional; the reply echoes the resulting state (same shape as
@@ -14367,6 +14401,7 @@ static const CmdEntry s_commands[] = {
{ "gpu_state", handle_gpu_state },
{ "geom_correction", handle_geom_correction },
{ "pgxp", handle_pgxp },
{ "pgxp_shadow", handle_pgxp_shadow },
{ "ws_aspect_cone_site", handle_ws_aspect_cone_site },
{ "ws_margin", handle_ws_margin },
{ "ws_hud_mode", handle_ws_hud_mode },
+17
View File
@@ -767,6 +767,23 @@ extern "C" void pgxp_store_gte_reg(uint32_t addr, uint8_t reg) {
*dst = *src;
}
extern "C" int pgxp_debug_shadow(int space, uint32_t key, int *live,
uint32_t *value, uint32_t *flags,
int32_t *x16, int32_t *y16, uint16_t *z) {
const PGXPValue *pv = nullptr;
if (space == 0) pv = pgxp_ptr(key);
else if (space == 1 && key < 34) pv = &s_gpr[key];
else if (space == 2 && key < 32) pv = &s_gte[key];
if (!pv) return 0;
*live = pv->gen == s_gen;
*value = pv->value;
*flags = pv->flags;
*x16 = pv->x16;
*y16 = pv->y16;
*z = pv->z;
return 1;
}
/* ------------------------------------------------------------------------- */
/* Test accessors */
/* ------------------------------------------------------------------------- */