From 9e61e579c4ba1e5359d3de820abf89e3b800ded1 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Thu, 23 Apr 2026 13:48:43 -0700 Subject: [PATCH] fix: add SUB opcode to strict translator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SUB (SPECIAL funct 0x22) was the last non-FPU unsupported opcode. Treated identically to SUBU — PSX R3000A has no overflow trap in practice. Unblocks function at BFC33CC0. Emitted: 1086 (was 1085). Skipped: 1 (COP1/FPU only). Unsupported instructions: 2 (was 4). Co-Authored-By: Claude Opus 4.6 (1M context) --- generated/SCPH1001_full.c | 67 ++++++++++++++++++++++++++-- recompiler/src/strict_translator.cpp | 1 + 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/generated/SCPH1001_full.c b/generated/SCPH1001_full.c index 79ccdaf3..878f31fe 100644 --- a/generated/SCPH1001_full.c +++ b/generated/SCPH1001_full.c @@ -127938,6 +127938,69 @@ label_BFC33CBC: cpu->pc = 0xBFC33CC0u; return; /* fallthrough */ } +void func_1FC33CC0(CPUState* cpu) { +label_BFC33CC0: + psx_check_interrupts(cpu); + /* 0xBFC33CC0: 240AFFFE addiu $t2, $zero, -2 */ + cpu->gpr[10] = (uint32_t)((int32_t)cpu->gpr[0] + (-2)); + /* 0xBFC33CC4: 004A5024 and $t2, $v0, $t2 */ + cpu->gpr[10] = cpu->gpr[2] & cpu->gpr[10]; + /* 0xBFC33CC8: 2409001F addiu $t1, $zero, 31 */ + cpu->gpr[9] = (uint32_t)((int32_t)cpu->gpr[0] + (31)); + /* 0xBFC33CCC: 012A4822 subu $t1, $t1, $t2 */ + cpu->gpr[9] = cpu->gpr[9] - cpu->gpr[10]; + /* 0xBFC33CD0: 00094843 sra $t1, $t1, 1 */ + cpu->gpr[9] = (uint32_t)((int32_t)cpu->gpr[9] >> 1u); + /* 0xBFC33CD4: 214BFFE8 addi $t3, $t2, -24 */ + { int32_t psx_a = (int32_t)cpu->gpr[10]; int32_t psx_b = (-24); int64_t psx_r = (int64_t)psx_a + (int64_t)psx_b; if (psx_r < (int64_t)INT32_MIN || psx_r > (int64_t)INT32_MAX) { psx_arith_overflow(cpu); return; } cpu->gpr[11] = (uint32_t)(int32_t)psx_r; } + /* 0xBFC33CD8: 05600003 bltz $t3, 0xBFC33CE8 */ + uint32_t psx_brA_BFC33CD8 = cpu->gpr[11]; + int psx_taken_BFC33CD8 = ((int32_t)psx_brA_BFC33CD8 < 0); + /* 0xBFC33CDC: 00000000 nop */ + /* nop */ + if (psx_taken_BFC33CD8) goto label_BFC33CE8; +label_BFC33CE0: + psx_check_interrupts(cpu); + /* 0xBFC33CE0: 01646004 sllv $t4, $a0, $t3 */ + cpu->gpr[12] = cpu->gpr[4] << (cpu->gpr[11] & 0x1Fu); + /* 0xBFC33CE4: 10000003 beq $zero, $zero, 0xBFC33CF4 */ + uint32_t psx_brA_BFC33CE4 = cpu->gpr[0]; uint32_t psx_brB_BFC33CE4 = cpu->gpr[0]; + int psx_taken_BFC33CE4 = (psx_brA_BFC33CE4 == psx_brB_BFC33CE4); +label_BFC33CE8: + psx_check_interrupts(cpu); + /* 0xBFC33CE8: 240B0018 addiu $t3, $zero, 24 */ + cpu->gpr[11] = (uint32_t)((int32_t)cpu->gpr[0] + (24)); + if (psx_taken_BFC33CE4) goto label_BFC33CF4; +label_BFC33CEC: + psx_check_interrupts(cpu); + /* 0xBFC33CEC: 016A5822 subu $t3, $t3, $t2 */ + cpu->gpr[11] = cpu->gpr[11] - cpu->gpr[10]; + /* 0xBFC33CF0: 01646007 srav $t4, $a0, $t3 */ + cpu->gpr[12] = (uint32_t)((int32_t)cpu->gpr[4] >> (cpu->gpr[11] & 0x1Fu)); +label_BFC33CF4: + psx_check_interrupts(cpu); + /* 0xBFC33CF4: 218CFFC0 addi $t4, $t4, -64 */ + { int32_t psx_a = (int32_t)cpu->gpr[12]; int32_t psx_b = (-64); int64_t psx_r = (int64_t)psx_a + (int64_t)psx_b; if (psx_r < (int64_t)INT32_MIN || psx_r > (int64_t)INT32_MAX) { psx_arith_overflow(cpu); return; } cpu->gpr[12] = (uint32_t)(int32_t)psx_r; } + /* 0xBFC33CF8: 000C6040 sll $t4, $t4, 1 */ + cpu->gpr[12] = cpu->gpr[12] << 1u; + /* 0xBFC33CFC: 3C0D8008 lui $t5, 0x8008 */ + cpu->gpr[13] = 0x80080000u; + /* 0xBFC33D00: 01AC6821 addu $t5, $t5, $t4 */ + cpu->gpr[13] = cpu->gpr[13] + cpu->gpr[12]; + /* 0xBFC33D04: 85AD8544 lh $t5, -31420($t5) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[13] + (-31420)); if (psx_addr & 1u) { psx_unaligned_access(cpu, psx_addr, 0xBFC33D04u); return; } cpu->gpr[13] = (uint32_t)(int32_t)(int16_t)cpu->read_half(psx_addr); } + /* 0xBFC33D08: ACC90000 sw $t1, 0($a2) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[6] + (0)); if (psx_addr & 3u) { psx_unaligned_access(cpu, psx_addr, 0xBFC33D08u); return; } cpu->write_word(psx_addr, cpu->gpr[9]); } + /* 0xBFC33D0C: ACAD0000 sw $t5, 0($a1) */ + { uint32_t psx_addr = (uint32_t)((int32_t)cpu->gpr[5] + (0)); if (psx_addr & 3u) { psx_unaligned_access(cpu, psx_addr, 0xBFC33D0Cu); return; } cpu->write_word(psx_addr, cpu->gpr[13]); } + /* 0xBFC33D10: 24020001 addiu $v0, $zero, 1 */ + cpu->gpr[2] = (uint32_t)((int32_t)cpu->gpr[0] + (1)); + /* 0xBFC33D14: 03E00008 jr $ra */ + /* 0xBFC33D18: 00000000 nop */ + /* nop */ + return; +} + void func_1FC33F00(CPUState* cpu) { label_BFC33F00: psx_check_interrupts(cpu); @@ -162247,7 +162310,3 @@ void func_1FC02324(CPUState* cpu) { psx_unknown_dispatch(cpu, 0xBFC02324u, 0x1FC02324u); } -void func_1FC33CC0(CPUState* cpu) { - psx_unknown_dispatch(cpu, 0xBFC33CC0u, 0x1FC33CC0u); -} - diff --git a/recompiler/src/strict_translator.cpp b/recompiler/src/strict_translator.cpp index 0556e380..f8d54e38 100644 --- a/recompiler/src/strict_translator.cpp +++ b/recompiler/src/strict_translator.cpp @@ -367,6 +367,7 @@ TranslateResult StrictTranslator::translate(const PSXRecomp::DecodedInstruction& return r; } + case 0x22: // SUB rd, rs, rt -- same as SUBU on PSX (no overflow trap in recomp) case 0x23: { // SUBU rd, rs, rt -- unsigned wrap, no trap r.supported = true; r.c_code = emit_gpr_write(rd,