mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
PGXP: shift the SXY FIFO shadows on an SXYP write
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+8
-12
@@ -2138,10 +2138,10 @@ extern "C" void gte_write_data(CPUState* cpu, uint8_t reg, uint32_t val) {
|
||||
cpu->gte_data[29] = packed;
|
||||
}
|
||||
break;
|
||||
/* Guest writes to the SXY FIFO drop the affected register shadows:
|
||||
* we never model FIFO side effects on shadows — the shifted regs 12/13
|
||||
* now describe words their shadows no longer match, and validation
|
||||
* drops those on next use. */
|
||||
/* Guest writes to the SXY FIFO: regs 12/13 overwrite their shadow, a
|
||||
* reg 14 write overwrites SXY2 and its SXYP mirror, and a reg 15
|
||||
* write is the FIFO push, which pgxp_gte_reg_written applies to the
|
||||
* shadows exactly as the registers move. */
|
||||
case 12: case 13:
|
||||
cpu->gte_data[reg] = val;
|
||||
if (!PSXRecomp::GTE::s_gte_replay_sandbox)
|
||||
@@ -2150,20 +2150,16 @@ extern "C" void gte_write_data(CPUState* cpu, uint8_t reg, uint32_t val) {
|
||||
case 14:
|
||||
cpu->gte_data[14] = val;
|
||||
cpu->gte_data[15] = val;
|
||||
if (!PSXRecomp::GTE::s_gte_replay_sandbox) {
|
||||
pgxp_gte_reg_written(14, val);
|
||||
pgxp_gte_reg_written(15, val);
|
||||
}
|
||||
if (!PSXRecomp::GTE::s_gte_replay_sandbox)
|
||||
pgxp_gte_reg_written(14, val); /* resets the SXYP mirror too */
|
||||
break;
|
||||
case 15:
|
||||
cpu->gte_data[12] = cpu->gte_data[13];
|
||||
cpu->gte_data[13] = cpu->gte_data[14];
|
||||
cpu->gte_data[14] = val;
|
||||
cpu->gte_data[15] = val;
|
||||
if (!PSXRecomp::GTE::s_gte_replay_sandbox) {
|
||||
pgxp_gte_reg_written(14, val);
|
||||
pgxp_gte_reg_written(15, val);
|
||||
}
|
||||
if (!PSXRecomp::GTE::s_gte_replay_sandbox)
|
||||
pgxp_gte_reg_written(15, val); /* FIFO push on the shadows */
|
||||
break;
|
||||
case 28: {
|
||||
cpu->gte_data[9] = (val & 0x1Fu) << 7;
|
||||
|
||||
+23
-3
@@ -206,6 +206,13 @@ static inline int32_t f_simm(uint32_t i) { return (int32_t)(int16_t)(i & 0xFFF
|
||||
/* Memory-mode hooks: loads / stores */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* SXY2 and SXYP are one register seen at two addresses: whichever the hook
|
||||
* just filled, the other describes the same word. */
|
||||
static inline void gte_sxy2_mirror(uint32_t reg) {
|
||||
if (reg == 14) s_gte[15] = s_gte[14];
|
||||
else if (reg == 15) s_gte[14] = s_gte[15];
|
||||
}
|
||||
|
||||
extern "C" void psx_pgxp_load(struct CPUState *cpu, uint32_t instr,
|
||||
uint32_t addr, uint32_t value) {
|
||||
(void)cpu;
|
||||
@@ -323,6 +330,7 @@ extern "C" void psx_pgxp_cop2(struct CPUState *cpu, uint32_t instr,
|
||||
} else {
|
||||
pv_reset(dst, value);
|
||||
}
|
||||
gte_sxy2_mirror(f_rt(instr));
|
||||
return;
|
||||
}
|
||||
case 0x3A: { /* SWC2: [addr] <- gte[rt] */
|
||||
@@ -360,9 +368,9 @@ extern "C" void psx_pgxp_cop2(struct CPUState *cpu, uint32_t instr,
|
||||
} else {
|
||||
pv_reset(dst, value);
|
||||
}
|
||||
/* An SXYP write (rd==15) shifts the hardware FIFO; we do not
|
||||
* model it — regs 12..14 now describe words they no longer
|
||||
* match, and validation drops them on next use. */
|
||||
/* An SXYP write (rd==15) already shifted the FIFO shadows
|
||||
* (pgxp_gte_reg_written, from the GTE register write). */
|
||||
gte_sxy2_mirror(f_rd(instr));
|
||||
return;
|
||||
}
|
||||
case 0x02: { /* CFC2: control regs carry no
|
||||
@@ -727,8 +735,20 @@ extern "C" void pgxp_gte_reg_written(int reg, uint32_t value) {
|
||||
* machine state back, so its writes must not stick to the shadows). */
|
||||
if (s_suppress != 0) return;
|
||||
if (reg < 0 || reg > 31) return;
|
||||
if (reg == 15) {
|
||||
/* SXYP is a FIFO push: SXY1 moves to SXY0, SXY2 to SXY1, the value
|
||||
* lands in SXY2 (psx-spx GTE "SXYP"). Engines draw quads from a
|
||||
* projected-vertex table this way - load three corners, NCLIP, push
|
||||
* the fourth through SXYP, NCLIP, store SXY0..2 into the packet - so
|
||||
* the shadows shift with the registers. The MTC2 / LWC2 hook then
|
||||
* copies the source shadow into the pushed slot. */
|
||||
s_gte[12] = s_gte[13];
|
||||
s_gte[13] = s_gte[14];
|
||||
reg = 14;
|
||||
}
|
||||
pv_kill(&s_gte[reg]);
|
||||
pv_reset(&s_gte[reg], value);
|
||||
gte_sxy2_mirror(reg); /* SXY2 and SXYP: one register */
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
@@ -259,6 +259,38 @@ int main(void) {
|
||||
}
|
||||
pgxp_set_cpu_mode(0);
|
||||
|
||||
/* --- quad from a projected-vertex table (Spider-Man 0x8007C5D4):
|
||||
* lwc2 SXY0..2 <- table; NCLIP; lwc2 SXYP <- table; NCLIP;
|
||||
* swc2 SXY0..2 -> packet
|
||||
* The SXYP write pushes the FIFO; the stored words are corners 1..3. --- */
|
||||
{
|
||||
const uint32_t TABLE = 0x80180100u, PKT = 0x80180200u;
|
||||
uint32_t words[4];
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
words[i] = ((80u + i) << 16) | (160u + i);
|
||||
pgxp_gte_push_sxy((int32_t)((160 + i) << 16) | 0x8000,
|
||||
(int32_t)((80 + i) << 16) | 0x4000,
|
||||
(uint16_t)(100 + i), words[i]);
|
||||
psx_pgxp_cop2(nullptr, SWC2(14), words[i], TABLE + i * 4u);
|
||||
}
|
||||
for (uint32_t i = 0; i < 3; i++) { /* lwc2 SXY0..2 */
|
||||
pgxp_gte_reg_written((int)(12 + i), words[i]);
|
||||
psx_pgxp_cop2(nullptr, enc_i(0x32, 1, 12 + i, 0), words[i],
|
||||
TABLE + i * 4u);
|
||||
}
|
||||
pgxp_gte_reg_written(15, words[3]); /* lwc2 SXYP: push */
|
||||
psx_pgxp_cop2(nullptr, enc_i(0x32, 1, 15, 0), words[3], TABLE + 12u);
|
||||
for (uint32_t i = 0; i < 3; i++)
|
||||
psx_pgxp_cop2(nullptr, SWC2(12 + i), words[i + 1], PKT + i * 4u);
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
int32_t x, y; uint16_t z;
|
||||
CHECK(lookup(PKT + i * 4u, words[i + 1], 161 + (int32_t)i,
|
||||
81 + (int32_t)i, &x, &y, &z) == PGXP_SRC_DATAFLOW);
|
||||
CHECK(x == ((int32_t)((161 + i) << 16) | 0x8000));
|
||||
CHECK(z == 101 + i);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- in-place ops read their source before the destination is reset --- */
|
||||
pgxp_set_cpu_mode(1);
|
||||
produce_at(ADDR_A);
|
||||
|
||||
Reference in New Issue
Block a user