diff options
| author | Tharo <tharo10600@gmail.com> | 2026-08-02 15:27:33 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-02 16:27:33 +0200 |
| commit | da4a1f661cc87c8a31b65310f8940a5c5f0927d6 (patch) | |
| tree | 3ec5f051015b043c943f47736c37aaf1476fc522 /rsp/rspboot_ap.s | |
| parent | 32b88f90a86a024dae0af0dc28a2c5e023fd193a (diff) | |
[rsp] Introduce microcode build process, disassemble and build both rspboot versions (#2764)HEADmain
* [rsp] Introduce microcode build process, disassemble and build both rspboot versions
* Suggested changes
* Check-in tool for generating armips.cpp from upstream sources, regen armips.cpp against a newer commit
* License armips_gen.py, add notice that armips.cpp is automatically generated
* Qualify comment with X105
* Clarify the assumed purpose of rspboot releasing SP_SEMAPHORE
Diffstat (limited to 'rsp/rspboot_ap.s')
| -rw-r--r-- | rsp/rspboot_ap.s | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/rsp/rspboot_ap.s b/rsp/rspboot_ap.s new file mode 100644 index 000000000..d3708c2fa --- /dev/null +++ b/rsp/rspboot_ap.s @@ -0,0 +1,153 @@ +/** + * @file rspboot_ap.s + * + * This RSP microcode program acts as a loader for other microcodes. This ucode: + * - (Optionally) Waits for the RDP to go idle + * - Loads a ucode's .data section to the start of DMEM + * - Loads a ucode's .text section to RSPBOOT_ENTRYPOINT + * - Jumps to the loaded ucode + * + * Along the way it also checks whether the task scheduler running on the CPU has + * requested a yield and, if so, halts the RSP. + * + * This is a special variant of rspboot that is designed to work alongside IPL3 X105 + * and the cic6105 microcode to attempt to detect whether the running game is a pirated + * copy. If it is so determined (in the antipiracy_test routine) cic6105 will not set + * signal 7 in SP_STATUS, causing subsequent runs of rspboot to corrupt random regions + * of memory (see the dpclock_corrupt routine) + */ +.rsp +#include "rcp.h" +#include "sptask.h" +#include "rspboot.h" + +#define OSTASK_FIELD(field) (-0x40 + OS_TASK_OFF_##field)($zero) + +.create CODE_FILE, IMEM_START + +entry: + // This jump is overwritten once rspboot runs for the first time (see set_status_and_patch below) + j antipiracy_test + lw $17, OSTASK_FIELD(FLAGS) + +load_ucode_text_and_enter: + li $ra, RSPBOOT_ENTRYPOINT + li $1, (IMEM_SIZE - (RSPBOOT_ENTRYPOINT - IMEM_START))-1 + mtc0 $16, SP_DRAM_ADDR + mtc0 $ra, SP_MEM_ADDR + mtc0 $1, SP_RD_LEN +@@while_dma_busy: + mfc0 $1, SP_DMA_BUSY + bnez $1, @@while_dma_busy +check_yield: + mfc0 $1, SP_STATUS + andi $1, $1, SP_STATUS_YIELD + bnez $1, yield_break + li $1, OSTASK_ADDR // $1 is expected to contain the OSTask pointer by many ucodes + jr $ra + +yield_break: + li $17, (SP_SET_SIG2 | SP_SET_SIG1 | SP_CLR_SIG0) + mtc0 $17, SP_STATUS + break + +antipiracy_test: + // Checks that $4, $5, $6 and $11 all have expected register values set by IPL3 X105 + lui $1, (SP_CLR_SIG7 >> 16) + li $3, 0x3D8 + bne $11, $3, set_status_and_patch + addi $16, $zero, entry_failure + lui $3, 0x3A0 + ori $3, $3, 0x4820 + bne $4, $3, set_status_and_patch + lui $3, 0x2529 + ori $3, $3, 0x4 + bne $6, $3, set_status_and_patch + srl $3, $5, 12 + addi $2, $3, -76 + bltz $2, set_status_and_patch + addi $2, $3, -79 + bgtz $2, set_status_and_patch + vxor $v13, $v13, $v13[1q] + vaddc $v13, $v13, $v13[2h] + vnxor $v13, $v13, $v13[4] + mfc2 $3, $v13[0] + xori $3, $3, 0x1234 // $3 is calculated and then never used before it is repurposed + nop + lui $1, (SP_SET_SIG7 >> 16) + addi $16, $zero, entry_success +set_status_and_patch: + mtc0 $1, SP_STATUS + lw $1, OSTASK_FIELD(UBOOT) + // fallthrough to dma write 8 bytes, patches the first 8 bytes of the ucode in dram + // with a new jump target that avoids the DPC_CLOCK code if SP_STATUS_SIG7 is set + +dma_write_8: +@@while_dma_full: + mfc0 $3, SP_DMA_FULL + bne $3, $3, @@while_dma_full + nop + mtc0 $16, SP_MEM_ADDR + mtc0 $1, SP_DRAM_ADDR + j dp_wait + mtc0 $zero, SP_WR_LEN // 0 = 8-byte write to RDRAM + +.align 8 +entry_success: + // antipiracy check passed, rspboot is patched to check signal 7 instead of performing the check again + j check_sig7 + lw $17, OSTASK_FIELD(FLAGS) + +.align 8 +entry_failure: + // antipiracy check failed, rspboot is patched to run the failure codepath until reset + j dpclock_corrupt + lw $17, OSTASK_FIELD(FLAGS) + +check_sig7: // if signal 7 is set, skip DPC_CLOCK sampling + corruption + mfc0 $16, SP_STATUS + andi $16, $16, SP_STATUS_SIG7 + bnez $16, dp_wait +dpclock_corrupt: + mfc0 $16, DPC_CLOCK + andi $1, $16, 0x1FFF + bnez $1, @@stepover // if the 13 low-order bits of the sampled clock are 0, set DPC_END to DPC_CLOCK + andi $1, $16, 0x7F + mtc0 $16, DPC_END +@@stepover: + beqz $1, dma_write_8 // if the 7 low-order bits of the sampled clock are 0, corrupt 8 random bytes of DRAM + srl $1, $16, 2 +dp_wait: + sll $17, $17, (32 - OS_TASK_DP_WAIT) + bgezal $17, load_ucode_data + lw $16, OSTASK_FIELD(UDATA_SZ) + mfc0 $1, DPC_STATUS + andi $1, $1, DPC_STATUS_DMA_BUSY + bnez $1, check_yield // yield check while waiting for RDP Command FIFO to become idle + +load_ucode_data: +@@while_dma_full: + mfc0 $1, SP_DMA_FULL + bnez $1, @@while_dma_full + lw $1, OSTASK_FIELD(UDATA) + addi $16, $16, -1 + mtc0 $zero, SP_MEM_ADDR + mtc0 $1, SP_DRAM_ADDR + mtc0 $16, SP_RD_LEN +@@while_dma_busy: + mfc0 $1, SP_DMA_BUSY + bnez $1, @@while_dma_busy + li $ra, load_ucode_text_and_enter + mtc0 $zero, SP_SEMAPHORE // Release the semaphore just in case a prior task or the CPU failed to release it + vxor $v16, $v16, $v16 // set $v16 to 0, some F3DZEX versions rely on this being set to function properly + sw $4, OSTASK_FIELD(UBOOT) + j check_yield + lw $16, OSTASK_FIELD(UCODE) + +.align 0x10 + +.if . > IMEM_END + .error "Not enough room in IMEM" +.endif + +.close |
