Merge remote-tracking branch 'origin/master' into landtmp/493

This commit is contained in:
Matthew Stanley
2026-10-05 13:23:22 -07:00
13 changed files with 638 additions and 5 deletions
+84 -3
View File
@@ -27,6 +27,9 @@
#include "overlay_backend.h"
#include "cpu_state.h"
#include "pgxp.h"
#ifndef PSX_NO_DEBUG_TOOLS
#include "psx_disasm.h"
#endif
#include "dma.h"
#include "gpu.h"
#include "gpu_render.h" /* gr_scale + gr_render_display_hires (screenshot_hires) */
@@ -5185,6 +5188,60 @@ static void handle_get_registers(int id, const char *json)
free(buf);
}
/* disasm: disassemble guest instructions. {"cmd":"disasm","addr":"0x8001121C",
* "count":32}. Reads live guest memory, so it works on EXE code and on
* RAM-installed/overlay code alike. Uses the recompiler's MIPS decoder.
* Reads go through psx_peek_word (main RAM / scratchpad / BIOS ROM only, no
* device reads, no read hooks); a word outside those comes back as
* {"word":null,"text":"<unreadable>"}. Compiled only with the debug tools,
* like the sources it needs (runtime.cmake). */
#ifndef PSX_NO_DEBUG_TOOLS
static void handle_disasm(int id, const char *json)
{
char addr_str[32];
if (!json_get_str(json, "addr", addr_str, sizeof(addr_str))) {
send_err(id, "missing addr"); return;
}
const char *hex = addr_str;
if (hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X')) hex += 2;
char *hex_end = NULL;
unsigned long long addr_val = strtoull(hex, &hex_end, 16);
if (hex_end == hex || *hex_end != '\0' || addr_val > 0xFFFFFFFFull) {
send_err(id, "bad addr"); return;
}
uint32_t addr = (uint32_t)addr_val & ~3u;
int count = json_get_int(json, "count", 16);
if (count < 1) count = 1;
if (count > 256) count = 256;
size_t bufsz = 512u + (size_t)count * 160u;
char *out = (char *)malloc(bufsz);
if (!out) { send_err(id, "oom"); return; }
int pos = snprintf(out, bufsz,
"{\"id\":%d,\"ok\":true,\"addr\":\"0x%08X\",\"count\":%d,\"lines\":[",
id, addr, count);
char text[128];
for (int i = 0; i < count; i++) {
uint32_t a = addr + (uint32_t)i * 4u;
uint32_t w = 0;
if (!psx_peek_word(a, &w)) {
pos += snprintf(out + pos, bufsz - (size_t)pos,
"%s{\"addr\":\"0x%08X\",\"word\":null,\"text\":\"<unreadable>\"}",
i ? "," : "", a);
continue;
}
text[0] = '\0';
(void)psx_disasm_one(w, a, text, (int)sizeof(text));
pos += snprintf(out + pos, bufsz - (size_t)pos,
"%s{\"addr\":\"0x%08X\",\"word\":\"0x%08X\",\"text\":\"%s\"}",
i ? "," : "", a, w, text);
}
pos += snprintf(out + pos, bufsz - (size_t)pos, "]}");
debug_server_send_line(out);
free(out);
}
#endif /* !PSX_NO_DEBUG_TOOLS */
static void handle_read_ram(int id, const char *json)
{
char addr_str[32];
@@ -10073,7 +10130,7 @@ static void wtrace_fill_entry(WriteTraceEntry *e, uint64_t seq,
* instead of the stale last-CPU-store PC (which is meaningless mid-DMA). */
if (g_dma_exec_depth > 0) {
e->dma_ch = (int8_t)g_dma_cur_ch;
if (g_dma_initiator_pc) e->pc = g_dma_initiator_pc;
e->pc = g_dma_initiator_pc; /* 0 = unknown; never a stale CPU store PC */
} else {
e->dma_ch = -1;
}
@@ -11680,6 +11737,22 @@ static void handle_wtrace_dump(int id, const char *json)
if (json_get_str(json, "addr_hi", hi_str, sizeof(hi_str)))
filter_hi = hex_to_u32(hi_str) & 0x1FFFFFFFu;
/* Filter the recorded producer PC, segment-masked like the address filter
* (KSEG0/KSEG1/KUSEG alias). DMA entries use the initiator PC stored by
* wtrace_fill_entry; an unknown (0) initiator matches only when no PC
* bound is given. Post-hoc only: recording, fingerprints and guest
* execution are unchanged. pc_lo/pc_hi echo the raw request. */
uint32_t pc_lo = 0, pc_hi = 0xFFFFFFFFu;
int pc_filtered = 0;
/* Default upper bound sits above every masked PC so no entry is excluded. */
uint32_t pc_lo_m = 0, pc_hi_m = 0x20000000u;
if (json_get_str(json, "pc_lo", lo_str, sizeof(lo_str))) {
pc_lo = hex_to_u32(lo_str); pc_lo_m = pc_lo & 0x1FFFFFFFu; pc_filtered = 1;
}
if (json_get_str(json, "pc_hi", hi_str, sizeof(hi_str))) {
pc_hi = hex_to_u32(hi_str); pc_hi_m = pc_hi & 0x1FFFFFFFu; pc_filtered = 1;
}
/* Optional frame-window filter — the "query the ring for the window of
* interest" primitive. Lets a caller reach entries in the MIDDLE of a deep,
* high-traffic ring (which oldest-N / newest-N paging cannot). -1 = unbounded. */
@@ -11703,8 +11776,9 @@ static void handle_wtrace_dump(int id, const char *json)
size_t pos = 0;
uint32_t emitted = 0;
pos += snprintf(buf + pos, BUF_SZ - pos,
"{\"id\":%d,\"ok\":true,\"total\":%llu,\"available\":%u,\"entries\":[",
id, (unsigned long long)total, avail);
"{\"id\":%d,\"ok\":true,\"total\":%llu,\"available\":%u,"
"\"pc_lo\":\"0x%08X\",\"pc_hi\":\"0x%08X\",\"entries\":[",
id, (unsigned long long)total, avail, pc_lo, pc_hi);
for (uint32_t i = 0; i < avail && emitted < MAX_OUT && pos < BUF_SZ - 256; i++) {
uint32_t idx;
if (newest_first) {
@@ -11715,6 +11789,10 @@ static void handle_wtrace_dump(int id, const char *json)
}
WriteTraceEntry *e = &s_wtrace[idx];
if (e->addr < filter_lo || e->addr >= filter_hi) continue;
if (pc_filtered) {
uint32_t epc = e->pc & 0x1FFFFFFFu;
if (e->pc == 0 || epc < pc_lo_m || epc >= pc_hi_m) continue;
}
if (frame_lo >= 0 && (int)e->frame < frame_lo) continue;
if (frame_hi >= 0 && (int)e->frame > frame_hi) continue;
pos += snprintf(buf + pos, BUF_SZ - pos,
@@ -14789,6 +14867,9 @@ static const CmdEntry s_commands[] = {
{ "c0_history", handle_c0_history },
{ "capture_quads", handle_capture_quads },
{ "get_quads", handle_get_quads },
#ifndef PSX_NO_DEBUG_TOOLS
{ "disasm", handle_disasm },
#endif
{ "gte_state", handle_gte_state },
{ "gte_ring_dump", handle_gte_ring_dump },
{ "gte_intpl_dump", handle_gte_intpl_dump },