mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
fix: DMA ch2 GPU→RAM now reads GPUREAD instead of writing zeros
The VRAM-to-RAM DMA path (channel 2, direction 0) was stubbed to fill the destination buffer with zeros. This broke all offscreen VRAM readback — the BIOS shell draws spiral/splash textures to an offscreen VRAM region, then DMA-transfers the rendered pixels back to RAM for later use as texture atlas entries. With the stub, all texture buffers stayed zero, producing invisible button backgrounds. Fix: call gpu_read_gpuread() for each DMA word instead of writing 0. Also includes: CDROM GetID two-phase response fix, GPU raw texture mode fix (opcode bit 0 instead of color==0x808080), debug server A0/C0 upload history commands with CPU context capture, and extended write trace ranges. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -133,6 +133,11 @@ int debug_server_get_input_override(void);
|
||||
/* Set the CPU state pointer for register queries. Call after CPU init. */
|
||||
void debug_server_set_cpu(CPUState *cpu);
|
||||
|
||||
/* Global CPU pointer — set by debug_server_set_cpu(). Available for
|
||||
* hardware simulation modules that need to read CPU state (e.g. GPU
|
||||
* capturing stack values during A0 uploads). */
|
||||
extern CPUState *debug_cpu_ptr;
|
||||
|
||||
void debug_server_send_line(const char *json);
|
||||
void debug_server_send_fmt(const char *fmt, ...);
|
||||
|
||||
|
||||
+10
-13
@@ -223,19 +223,15 @@ static void exec_command(uint8_t cmd) {
|
||||
break;
|
||||
|
||||
case 0x1A: /* GetID */
|
||||
if (!has_disc()) {
|
||||
response_push(stat_reg | CDSTAT_IDERROR);
|
||||
response_push(0x80); /* NOT_READY */
|
||||
set_irq(CDIRQ_ERROR);
|
||||
fire_cdrom_irq();
|
||||
} else {
|
||||
response_push(stat_reg);
|
||||
set_irq(CDIRQ_ACK);
|
||||
pending.cmd = 0x1A;
|
||||
pending.pending = 1;
|
||||
pending.delay = 30000;
|
||||
pending.phase = 1;
|
||||
}
|
||||
/* GetID always sends INT3 (ACK) first, then a pending
|
||||
* second response: INT2 (COMPLETE) with disc ID if present,
|
||||
* or INT5 (ERROR) if no disc / lid open. */
|
||||
response_push(stat_reg);
|
||||
set_irq(CDIRQ_ACK);
|
||||
pending.cmd = 0x1A;
|
||||
pending.pending = 1;
|
||||
pending.delay = 30000;
|
||||
pending.phase = 1;
|
||||
break;
|
||||
|
||||
case 0x1B: /* ReadS */
|
||||
@@ -356,6 +352,7 @@ static void process_pending(void) {
|
||||
case 0x1E: /* ReadTOC complete */
|
||||
response_push(stat_reg);
|
||||
set_irq(CDIRQ_COMPLETE);
|
||||
fire_cdrom_irq();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -449,6 +449,69 @@ static void handle_irq_state(int id, const char *json)
|
||||
/* GPU opcode counter — defined in gpu.c */
|
||||
extern uint32_t gpu_get_opcode_count(uint8_t op);
|
||||
|
||||
extern int gpu_get_a0_count(void);
|
||||
extern int gpu_get_a0_history(int index, int *x, int *y, int *w, int *h,
|
||||
uint32_t *fw0, uint32_t *fw1, int *wcount);
|
||||
extern int gpu_get_a0_extra(int index, uint32_t *func, uint32_t *sp, uint32_t *ra,
|
||||
uint32_t *s1, uint32_t *stack10);
|
||||
|
||||
static void handle_a0_history(int id, const char *json)
|
||||
{
|
||||
(void)json;
|
||||
int count = gpu_get_a0_count();
|
||||
/* Use dynamic allocation for large output */
|
||||
int bufsz = 65536;
|
||||
char *buf = (char*)malloc(bufsz);
|
||||
if (!buf) { send_fmt("{\"id\":%d,\"ok\":false,\"error\":\"OOM\"}", id); return; }
|
||||
int pos = snprintf(buf, bufsz, "{\"id\":%d,\"ok\":true,\"count\":%d,\"uploads\":[", id, count);
|
||||
for (int i = 0; i < count && pos < bufsz - 500; i++) {
|
||||
int x, y, w, h, wcount;
|
||||
uint32_t fw0, fw1, func, sp, ra, s1, stk[10];
|
||||
gpu_get_a0_history(i, &x, &y, &w, &h, &fw0, &fw1, &wcount);
|
||||
gpu_get_a0_extra(i, &func, &sp, &ra, &s1, stk);
|
||||
pos += snprintf(buf + pos, bufsz - pos,
|
||||
"%s{\"i\":%d,\"x\":%d,\"y\":%d,\"w\":%d,\"h\":%d,"
|
||||
"\"fw0\":\"0x%08X\",\"fw1\":\"0x%08X\",\"words\":%d,"
|
||||
"\"func\":\"0x%08X\",\"sp\":\"0x%08X\",\"ra\":\"0x%08X\","
|
||||
"\"s1\":\"0x%08X\","
|
||||
"\"stk\":[\"0x%08X\",\"0x%08X\",\"0x%08X\",\"0x%08X\","
|
||||
"\"0x%08X\",\"0x%08X\",\"0x%08X\",\"0x%08X\","
|
||||
"\"0x%08X\",\"0x%08X\"]}",
|
||||
i ? "," : "", i, x, y, w, h, fw0, fw1, wcount,
|
||||
func, sp, ra, s1,
|
||||
stk[0], stk[1], stk[2], stk[3], stk[4], stk[5], stk[6], stk[7],
|
||||
stk[8], stk[9]);
|
||||
}
|
||||
pos += snprintf(buf + pos, bufsz - pos, "]}");
|
||||
send_fmt("%s", buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
extern int gpu_get_c0_count(void);
|
||||
extern int gpu_get_c0_history(int index, int *x, int *y, int *w, int *h,
|
||||
uint32_t *func, uint32_t *sp, uint32_t *s1,
|
||||
uint32_t *fw0, uint32_t *fw1, int *rcount);
|
||||
|
||||
static void handle_c0_history(int id, const char *json)
|
||||
{
|
||||
(void)json;
|
||||
int count = gpu_get_c0_count();
|
||||
char buf[8192];
|
||||
int pos = snprintf(buf, sizeof(buf), "{\"id\":%d,\"ok\":true,\"count\":%d,\"reads\":[", id, count);
|
||||
for (int i = 0; i < count && pos < (int)sizeof(buf) - 300; i++) {
|
||||
int x, y, w, h, rcount;
|
||||
uint32_t func, sp, s1, fw0, fw1;
|
||||
gpu_get_c0_history(i, &x, &y, &w, &h, &func, &sp, &s1, &fw0, &fw1, &rcount);
|
||||
pos += snprintf(buf + pos, sizeof(buf) - pos,
|
||||
"%s{\"i\":%d,\"x\":%d,\"y\":%d,\"w\":%d,\"h\":%d,"
|
||||
"\"func\":\"0x%08X\",\"sp\":\"0x%08X\",\"s1\":\"0x%08X\","
|
||||
"\"fw0\":\"0x%08X\",\"fw1\":\"0x%08X\",\"reads\":%d}",
|
||||
i ? "," : "", i, x, y, w, h, func, sp, s1, fw0, fw1, rcount);
|
||||
}
|
||||
pos += snprintf(buf + pos, sizeof(buf) - pos, "]}");
|
||||
send_fmt("%s", buf);
|
||||
}
|
||||
|
||||
static void handle_gpu_opcodes(int id, const char *json)
|
||||
{
|
||||
(void)json;
|
||||
@@ -1328,6 +1391,8 @@ static const CmdEntry s_commands[] = {
|
||||
{ "screenshot", handle_screenshot },
|
||||
{ "screenshot_file", handle_screenshot_file },
|
||||
{ "gpu_opcodes", handle_gpu_opcodes },
|
||||
{ "a0_history", handle_a0_history },
|
||||
{ "c0_history", handle_c0_history },
|
||||
{ "capture_quads", handle_capture_quads },
|
||||
{ "get_quads", handle_get_quads },
|
||||
{ "gte_state", handle_gte_state },
|
||||
@@ -1414,12 +1479,19 @@ void debug_server_init(int port)
|
||||
s_wtrace_seq = 0;
|
||||
s_wtrace_head = 0;
|
||||
|
||||
/* Phase 4.5: watch EB4 area + DF8/DFC area. */
|
||||
/* Phase 4.5: watch EB4 area + DF8/DFC area + EvCB slot 1 status + state machine
|
||||
* + spiral texture buffer. */
|
||||
s_wtrace_ranges[0].lo = 0x00079EB0u;
|
||||
s_wtrace_ranges[0].hi = 0x00079EC0u;
|
||||
s_wtrace_ranges[1].lo = 0x00079DF0u;
|
||||
s_wtrace_ranges[1].hi = 0x00079E04u;
|
||||
s_wtrace_range_count = 2;
|
||||
s_wtrace_ranges[2].lo = 0x0000E044u; /* EvCB slot 1 (class+status+spec) */
|
||||
s_wtrace_ranges[2].hi = 0x0000E054u;
|
||||
s_wtrace_ranges[3].lo = 0x00066940u; /* shell state machine 0x80066940 */
|
||||
s_wtrace_ranges[3].hi = 0x00066954u;
|
||||
s_wtrace_ranges[4].lo = 0x001B6810u; /* spiral texture buffer (heap @ 0x801B6814) */
|
||||
s_wtrace_ranges[4].hi = 0x001B6830u;
|
||||
s_wtrace_range_count = 5;
|
||||
|
||||
/* Tier 1: heap-allocate MMIO trace ring buffer (2 MB). */
|
||||
if (!s_mmio_trace) {
|
||||
|
||||
+4
-6
@@ -80,11 +80,8 @@ static void execute_ch2_gpu(void) {
|
||||
uint32_t sync_mode = (chcr >> 9) & 3; /* 0=burst, 1=block, 2=linked-list */
|
||||
|
||||
if (direction == 0) {
|
||||
/* GPU → RAM (VRAM read) — not fully implemented.
|
||||
* Fill target RAM with zeros and complete the transfer so the
|
||||
* DMA interrupt fires and the handler chain can process it.
|
||||
* The BIOS uses this during display setup; the actual VRAM
|
||||
* contents aren't critical for boot progression. */
|
||||
/* GPU → RAM (VRAM read): read pixel data via GPUREAD.
|
||||
* A prior GP0(C0h) command must have set up the VRAM read region. */
|
||||
if (sync_mode == 1) {
|
||||
uint32_t block_size = channels[2].bcr & 0xFFFF;
|
||||
uint32_t block_count = (channels[2].bcr >> 16) & 0xFFFF;
|
||||
@@ -92,7 +89,8 @@ static void execute_ch2_gpu(void) {
|
||||
uint32_t addr = channels[2].madr & 0x1FFFFCu;
|
||||
int32_t addr_step = step ? -4 : 4;
|
||||
for (uint32_t i = 0; i < total_words; i++) {
|
||||
psx_write_word(addr, 0);
|
||||
uint32_t pixel_data = gpu_read_gpuread();
|
||||
psx_write_word(addr, pixel_data);
|
||||
addr = (addr + addr_step) & 0x1FFFFCu;
|
||||
}
|
||||
}
|
||||
|
||||
+152
-14
@@ -12,6 +12,8 @@
|
||||
|
||||
#include "gpu.h"
|
||||
#include "gpu_sw_renderer.h"
|
||||
#include "debug_server.h"
|
||||
#include "cpu_state.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -107,6 +109,17 @@ static uint32_t v_display_y2;
|
||||
/* GPUREAD latch (GP1(10h) get-info result, or VRAM read data) */
|
||||
static uint32_t gpuread_latch;
|
||||
|
||||
/* C0 (VRAM→CPU) capture slot — forward declaration for gpu_read_gpuread */
|
||||
#define C0_HISTORY_CAP_FWD 32
|
||||
static struct C0HistEntry {
|
||||
uint16_t x, y, w, h;
|
||||
uint32_t func_addr, sp_val, s1_val;
|
||||
uint32_t first_words[4];
|
||||
int read_count;
|
||||
} c0_history_fwd[C0_HISTORY_CAP_FWD];
|
||||
static int c0_history_count_fwd = 0;
|
||||
static int c0_capture_slot_fwd = -1;
|
||||
|
||||
/* Vblank presentation callback */
|
||||
static gpu_vblank_cb vblank_callback;
|
||||
|
||||
@@ -275,6 +288,15 @@ uint32_t gpu_read_gpuread(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Capture first words for C0 debug */
|
||||
if (c0_capture_slot_fwd >= 0 && c0_capture_slot_fwd < C0_HISTORY_CAP_FWD) {
|
||||
int rc = c0_history_fwd[c0_capture_slot_fwd].read_count++;
|
||||
if (rc < 4)
|
||||
c0_history_fwd[c0_capture_slot_fwd].first_words[rc] = value;
|
||||
if (!vram_read_active)
|
||||
c0_capture_slot_fwd = -1; /* transfer complete */
|
||||
}
|
||||
|
||||
gpuread_latch = value;
|
||||
return value;
|
||||
}
|
||||
@@ -536,13 +558,13 @@ static uint16_t current_texpage(void) {
|
||||
}
|
||||
|
||||
/* Helper: set up SW renderer state before a textured draw.
|
||||
* semi_trans: whether the primitive has semi-transparency enabled (from opcode bit) */
|
||||
static void setup_textured_draw(uint32_t color24, int semi_trans) {
|
||||
* semi_trans: whether the primitive has semi-transparency enabled (from opcode bit)
|
||||
* raw_texture: 1 if this is a raw-texture opcode (bit 0 of GP0 opcode set) */
|
||||
static void setup_textured_draw(uint32_t color24, int semi_trans, int raw_texture) {
|
||||
uint32_t r = (color24 >> 0) & 0xFF;
|
||||
uint32_t g = (color24 >> 8) & 0xFF;
|
||||
uint32_t b = (color24 >> 16) & 0xFF;
|
||||
int raw = (r == 0x80 && g == 0x80 && b == 0x80) ? 1 : 0;
|
||||
sw_set_color_modulation((int)r, (int)g, (int)b, raw);
|
||||
sw_set_color_modulation((int)r, (int)g, (int)b, raw_texture);
|
||||
sw_set_semi_transparency(semi_trans, (int)semi_transparency);
|
||||
}
|
||||
|
||||
@@ -550,6 +572,7 @@ static void setup_textured_draw(uint32_t color24, int semi_trans) {
|
||||
static void gp0_exec_textured_tri(void) {
|
||||
uint32_t color24 = gp0_cmd_buf[0] & 0xFFFFFFu;
|
||||
int semi_trans = (gp0_cmd_buf[0] >> 25) & 1;
|
||||
int raw_texture = (gp0_cmd_buf[0] >> 24) & 1;
|
||||
int32_t vx[3], vy[3];
|
||||
int u[3], v[3];
|
||||
/* Layout: color+v0, texcoord0+clut, v1, texcoord1+tpage, v2, texcoord2 */
|
||||
@@ -571,7 +594,7 @@ static void gp0_exec_textured_tri(void) {
|
||||
vy[i] += draw_offset_y;
|
||||
}
|
||||
|
||||
setup_textured_draw(color24, semi_trans);
|
||||
setup_textured_draw(color24, semi_trans, raw_texture);
|
||||
sw_draw_textured_triangle(vx[0], vy[0], u[0], v[0],
|
||||
vx[1], vy[1], u[1], v[1],
|
||||
vx[2], vy[2], u[2], v[2],
|
||||
@@ -582,6 +605,7 @@ static void gp0_exec_textured_tri(void) {
|
||||
static void gp0_exec_textured_quad(void) {
|
||||
uint32_t color24 = gp0_cmd_buf[0] & 0xFFFFFFu;
|
||||
int semi_trans = (gp0_cmd_buf[0] >> 25) & 1;
|
||||
int raw_texture = (gp0_cmd_buf[0] >> 24) & 1;
|
||||
int32_t vx[4], vy[4];
|
||||
int u[4], v[4];
|
||||
/* Layout: color+v0, tc0+clut, v1, tc1+tpage, v2, tc2, v3, tc3 */
|
||||
@@ -603,7 +627,7 @@ static void gp0_exec_textured_quad(void) {
|
||||
vy[i] += draw_offset_y;
|
||||
}
|
||||
|
||||
setup_textured_draw(color24, semi_trans);
|
||||
setup_textured_draw(color24, semi_trans, raw_texture);
|
||||
sw_draw_textured_triangle(vx[0], vy[0], u[0], v[0],
|
||||
vx[1], vy[1], u[1], v[1],
|
||||
vx[2], vy[2], u[2], v[2],
|
||||
@@ -618,6 +642,7 @@ static void gp0_exec_textured_quad(void) {
|
||||
static void gp0_exec_shaded_textured_tri(void) {
|
||||
uint32_t color24 = gp0_cmd_buf[0] & 0xFFFFFFu;
|
||||
int semi_trans = (gp0_cmd_buf[0] >> 25) & 1;
|
||||
int raw_texture = (gp0_cmd_buf[0] >> 24) & 1;
|
||||
int32_t vx[3], vy[3];
|
||||
int u[3], v[3];
|
||||
/* Layout: C0, V0, TC0+clut, C1, V1, TC1+tpage, C2, V2, TC2 */
|
||||
@@ -637,7 +662,7 @@ static void gp0_exec_shaded_textured_tri(void) {
|
||||
vy[i] += draw_offset_y;
|
||||
}
|
||||
|
||||
setup_textured_draw(color24, semi_trans);
|
||||
setup_textured_draw(color24, semi_trans, raw_texture);
|
||||
sw_draw_textured_triangle(vx[0], vy[0], u[0], v[0],
|
||||
vx[1], vy[1], u[1], v[1],
|
||||
vx[2], vy[2], u[2], v[2],
|
||||
@@ -648,6 +673,7 @@ static void gp0_exec_shaded_textured_tri(void) {
|
||||
static void gp0_exec_shaded_textured_quad(void) {
|
||||
uint32_t color24 = gp0_cmd_buf[0] & 0xFFFFFFu;
|
||||
int semi_trans = (gp0_cmd_buf[0] >> 25) & 1;
|
||||
int raw_texture = (gp0_cmd_buf[0] >> 24) & 1;
|
||||
int32_t vx[4], vy[4];
|
||||
int u[4], v[4];
|
||||
/* Layout: C0, V0, TC0+clut, C1, V1, TC1+tpage, C2, V2, TC2, C3, V3, TC3 */
|
||||
@@ -669,7 +695,7 @@ static void gp0_exec_shaded_textured_quad(void) {
|
||||
vy[i] += draw_offset_y;
|
||||
}
|
||||
|
||||
setup_textured_draw(color24, semi_trans);
|
||||
setup_textured_draw(color24, semi_trans, raw_texture);
|
||||
sw_draw_textured_triangle(vx[0], vy[0], u[0], v[0],
|
||||
vx[1], vy[1], u[1], v[1],
|
||||
vx[2], vy[2], u[2], v[2],
|
||||
@@ -726,6 +752,7 @@ static void gp0_exec_mono_rect(void) {
|
||||
static void gp0_exec_textured_rect(void) {
|
||||
uint32_t color24 = gp0_cmd_buf[0] & 0xFFFFFFu;
|
||||
int semi_trans = (gp0_cmd_buf[0] >> 25) & 1;
|
||||
int raw_texture = (gp0_cmd_buf[0] >> 24) & 1;
|
||||
int32_t x0, y0;
|
||||
parse_vertex(gp0_cmd_buf[1], &x0, &y0);
|
||||
x0 += draw_offset_x; y0 += draw_offset_y;
|
||||
@@ -739,7 +766,7 @@ static void gp0_exec_textured_rect(void) {
|
||||
if (w > 1023) w = 1023;
|
||||
if (h > 511) h = 511;
|
||||
|
||||
setup_textured_draw(color24, semi_trans);
|
||||
setup_textured_draw(color24, semi_trans, raw_texture);
|
||||
sw_draw_textured_rect(x0, y0, w, h, u0, v0, clut_x, clut_y, current_texpage());
|
||||
}
|
||||
|
||||
@@ -758,6 +785,7 @@ static void gp0_exec_mono_dot(void) {
|
||||
static void gp0_exec_textured_8x8(void) {
|
||||
uint32_t color24 = gp0_cmd_buf[0] & 0xFFFFFFu;
|
||||
int semi_trans = (gp0_cmd_buf[0] >> 25) & 1;
|
||||
int raw_texture = (gp0_cmd_buf[0] >> 24) & 1;
|
||||
int32_t x0, y0;
|
||||
parse_vertex(gp0_cmd_buf[1], &x0, &y0);
|
||||
x0 += draw_offset_x; y0 += draw_offset_y;
|
||||
@@ -767,7 +795,7 @@ static void gp0_exec_textured_8x8(void) {
|
||||
uint16_t clut_x = (clut & 0x3F) * 16;
|
||||
uint16_t clut_y = (clut >> 6) & 0x1FF;
|
||||
|
||||
setup_textured_draw(color24, semi_trans);
|
||||
setup_textured_draw(color24, semi_trans, raw_texture);
|
||||
sw_draw_textured_rect(x0, y0, 8, 8, u0, v0, clut_x, clut_y, current_texpage());
|
||||
}
|
||||
|
||||
@@ -786,6 +814,7 @@ static void gp0_exec_mono_8x8(void) {
|
||||
static void gp0_exec_textured_16x16(void) {
|
||||
uint32_t color24 = gp0_cmd_buf[0] & 0xFFFFFFu;
|
||||
int semi_trans = (gp0_cmd_buf[0] >> 25) & 1;
|
||||
int raw_texture = (gp0_cmd_buf[0] >> 24) & 1;
|
||||
int32_t x0, y0;
|
||||
parse_vertex(gp0_cmd_buf[1], &x0, &y0);
|
||||
x0 += draw_offset_x; y0 += draw_offset_y;
|
||||
@@ -795,7 +824,7 @@ static void gp0_exec_textured_16x16(void) {
|
||||
uint16_t clut_x = (clut & 0x3F) * 16;
|
||||
uint16_t clut_y = (clut >> 6) & 0x1FF;
|
||||
|
||||
setup_textured_draw(color24, semi_trans);
|
||||
setup_textured_draw(color24, semi_trans, raw_texture);
|
||||
sw_draw_textured_rect(x0, y0, 16, 16, u0, v0, clut_x, clut_y, current_texpage());
|
||||
}
|
||||
|
||||
@@ -900,6 +929,46 @@ static void gp0_exec_mask_bit(void) {
|
||||
sw_set_mask_bits((int)set_mask_bit, (int)check_mask_bit);
|
||||
}
|
||||
|
||||
/* A0 upload history for debug inspection */
|
||||
#define A0_HISTORY_CAP 128
|
||||
typedef struct {
|
||||
uint16_t x, y, w, h;
|
||||
uint32_t first_words[4];
|
||||
int word_count;
|
||||
uint32_t func_addr;
|
||||
uint32_t sp_val; /* CPU $sp at time of A0 header */
|
||||
uint32_t ra_val; /* CPU $ra register at time of A0 header */
|
||||
uint32_t s1_val; /* CPU $s1 = source data pointer in func_1FC38524 */
|
||||
uint32_t stack[10]; /* first 10 words from sp (sp+32=saved $s1, sp+36=saved $ra) */
|
||||
} A0HistEntry;
|
||||
static A0HistEntry a0_history[A0_HISTORY_CAP];
|
||||
static int a0_history_count = 0;
|
||||
static int a0_capture_slot = -1; /* currently capturing data words for this slot */
|
||||
|
||||
extern uint32_t psx_read_word(uint32_t addr);
|
||||
|
||||
int gpu_get_a0_history(int index, int *x, int *y, int *w, int *h,
|
||||
uint32_t *fw0, uint32_t *fw1, int *wcount) {
|
||||
if (index < 0 || index >= a0_history_count) return 0;
|
||||
*x = a0_history[index].x; *y = a0_history[index].y;
|
||||
*w = a0_history[index].w; *h = a0_history[index].h;
|
||||
*fw0 = a0_history[index].first_words[0];
|
||||
*fw1 = a0_history[index].first_words[1];
|
||||
*wcount = a0_history[index].word_count;
|
||||
return 1;
|
||||
}
|
||||
int gpu_get_a0_count(void) { return a0_history_count; }
|
||||
int gpu_get_a0_extra(int index, uint32_t *func, uint32_t *sp, uint32_t *ra,
|
||||
uint32_t *s1, uint32_t *stack10) {
|
||||
if (index < 0 || index >= a0_history_count) return 0;
|
||||
*func = a0_history[index].func_addr;
|
||||
*sp = a0_history[index].sp_val;
|
||||
*ra = a0_history[index].ra_val;
|
||||
*s1 = a0_history[index].s1_val;
|
||||
memcpy(stack10, a0_history[index].stack, 10 * sizeof(uint32_t));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void gp0_exec_cpu_to_vram(void) {
|
||||
/* 0xA0 — CPU→VRAM Copy (header: 3 words)
|
||||
* Word 0: command
|
||||
@@ -915,6 +984,34 @@ static void gp0_exec_cpu_to_vram(void) {
|
||||
vram_write_w = (w == 0) ? 0x400 : (uint16_t)w;
|
||||
vram_write_h = (h == 0) ? 0x200 : (uint16_t)h;
|
||||
|
||||
/* Record for debug */
|
||||
if (a0_history_count < A0_HISTORY_CAP) {
|
||||
int slot = a0_history_count++;
|
||||
a0_history[slot].x = vram_write_x;
|
||||
a0_history[slot].y = vram_write_y;
|
||||
a0_history[slot].w = vram_write_w;
|
||||
a0_history[slot].h = vram_write_h;
|
||||
a0_history[slot].word_count = 0;
|
||||
a0_history[slot].func_addr = g_debug_current_func_addr;
|
||||
memset(a0_history[slot].first_words, 0, sizeof(a0_history[slot].first_words));
|
||||
/* Capture CPU context for caller tracing.
|
||||
* func_1FC38524 (shell LoadImage) uses $s1 as source data pointer. */
|
||||
a0_history[slot].sp_val = 0;
|
||||
a0_history[slot].ra_val = 0;
|
||||
a0_history[slot].s1_val = 0;
|
||||
memset(a0_history[slot].stack, 0, sizeof(a0_history[slot].stack));
|
||||
if (debug_cpu_ptr) {
|
||||
uint32_t sp = debug_cpu_ptr->gpr[29];
|
||||
a0_history[slot].sp_val = sp;
|
||||
a0_history[slot].ra_val = debug_cpu_ptr->gpr[31];
|
||||
a0_history[slot].s1_val = debug_cpu_ptr->gpr[17]; /* $s1 */
|
||||
uint32_t sp_phys = sp & 0x1FFFFFu;
|
||||
for (int si = 0; si < 10 && sp_phys + (si + 1) * 4 <= 0x200000u; si++)
|
||||
a0_history[slot].stack[si] = psx_read_word(sp + si * 4);
|
||||
}
|
||||
a0_capture_slot = slot;
|
||||
}
|
||||
|
||||
vram_write_col = 0;
|
||||
vram_write_row = 0;
|
||||
|
||||
@@ -925,6 +1022,28 @@ static void gp0_exec_cpu_to_vram(void) {
|
||||
gp0_state = GP0_VRAM_WRITE;
|
||||
}
|
||||
|
||||
/* C0 (VRAM→CPU) history — uses c0_history_fwd declared at top of file */
|
||||
#define C0_HISTORY_CAP C0_HISTORY_CAP_FWD
|
||||
#define c0_history c0_history_fwd
|
||||
#define c0_history_count c0_history_count_fwd
|
||||
#define c0_capture_slot c0_capture_slot_fwd
|
||||
|
||||
int gpu_get_c0_count(void) { return c0_history_count; }
|
||||
int gpu_get_c0_history(int index, int *x, int *y, int *w, int *h,
|
||||
uint32_t *func, uint32_t *sp, uint32_t *s1,
|
||||
uint32_t *fw0, uint32_t *fw1, int *rcount) {
|
||||
if (index < 0 || index >= c0_history_count) return 0;
|
||||
*x = c0_history[index].x; *y = c0_history[index].y;
|
||||
*w = c0_history[index].w; *h = c0_history[index].h;
|
||||
*func = c0_history[index].func_addr;
|
||||
*sp = c0_history[index].sp_val;
|
||||
*s1 = c0_history[index].s1_val;
|
||||
*fw0 = c0_history[index].first_words[0];
|
||||
*fw1 = c0_history[index].first_words[1];
|
||||
*rcount = c0_history[index].read_count;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void gp0_exec_vram_to_cpu(void) {
|
||||
/* 0xC0 — VRAM→CPU Copy (3 words)
|
||||
* Word 1: source coords
|
||||
@@ -938,8 +1057,20 @@ static void gp0_exec_vram_to_cpu(void) {
|
||||
vram_read_w = (w == 0) ? 0x400 : (uint16_t)w;
|
||||
vram_read_h = (h == 0) ? 0x200 : (uint16_t)h;
|
||||
|
||||
/* DuckStation uses (val-1)&mask+1 for VRAM→CPU, but the effect
|
||||
* is identical: 0→max. We use ReplaceZero like CPU→VRAM. */
|
||||
/* Record for debug */
|
||||
if (c0_history_count < C0_HISTORY_CAP) {
|
||||
int slot = c0_history_count++;
|
||||
c0_history[slot].x = vram_read_x;
|
||||
c0_history[slot].y = vram_read_y;
|
||||
c0_history[slot].w = vram_read_w;
|
||||
c0_history[slot].h = vram_read_h;
|
||||
c0_history[slot].func_addr = g_debug_current_func_addr;
|
||||
c0_history[slot].sp_val = debug_cpu_ptr ? debug_cpu_ptr->gpr[29] : 0;
|
||||
c0_history[slot].s1_val = debug_cpu_ptr ? debug_cpu_ptr->gpr[17] : 0;
|
||||
memset(c0_history[slot].first_words, 0, sizeof(c0_history[slot].first_words));
|
||||
c0_history[slot].read_count = 0;
|
||||
c0_capture_slot = slot;
|
||||
}
|
||||
|
||||
vram_read_col = 0;
|
||||
vram_read_row = 0;
|
||||
@@ -1136,6 +1267,7 @@ static void gp0_execute_command(void) {
|
||||
/* 1x1 textured dot: cmd, vertex, texcoord+clut (no size word) */
|
||||
uint32_t color24 = gp0_cmd_buf[0] & 0xFFFFFFu;
|
||||
int semi_trans = (gp0_cmd_buf[0] >> 25) & 1;
|
||||
int raw_texture = (gp0_cmd_buf[0] >> 24) & 1;
|
||||
int32_t x0, y0;
|
||||
parse_vertex(gp0_cmd_buf[1], &x0, &y0);
|
||||
x0 += draw_offset_x; y0 += draw_offset_y;
|
||||
@@ -1144,7 +1276,7 @@ static void gp0_execute_command(void) {
|
||||
uint16_t clut = (uint16_t)(gp0_cmd_buf[2] >> 16);
|
||||
uint16_t clut_x = (clut & 0x3F) * 16;
|
||||
uint16_t clut_y = (clut >> 6) & 0x1FF;
|
||||
setup_textured_draw(color24, semi_trans);
|
||||
setup_textured_draw(color24, semi_trans, raw_texture);
|
||||
sw_draw_textured_rect(x0, y0, 1, 1, u0, v0, clut_x, clut_y, current_texpage());
|
||||
break;
|
||||
}
|
||||
@@ -1264,6 +1396,12 @@ void gpu_write_gp0(uint32_t val) {
|
||||
|
||||
/* State: consuming pixel data for CPU→VRAM transfer */
|
||||
if (gp0_state == GP0_VRAM_WRITE) {
|
||||
/* Capture first few data words for debug */
|
||||
if (a0_capture_slot >= 0 && a0_capture_slot < A0_HISTORY_CAP) {
|
||||
int wc = a0_history[a0_capture_slot].word_count++;
|
||||
if (wc < 4)
|
||||
a0_history[a0_capture_slot].first_words[wc] = val;
|
||||
}
|
||||
/* Each word contains two 16-bit pixels (low halfword first) */
|
||||
for (int i = 0; i < 2; i++) {
|
||||
uint16_t pixel = (uint16_t)(val >> (i * 16));
|
||||
|
||||
Reference in New Issue
Block a user