Files
oot/rsp/rspboot_ap.s
T
Tharo da4a1f661c [rsp] Introduce microcode build process, disassemble and build both rspboot versions (#2764)
* [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
2026-08-02 16:27:33 +02:00

154 lines
5.0 KiB
ArmAsm

/**
* @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