mirror of
https://github.com/zeldaret/oot
synced 2026-08-04 17:10:18 -04:00
da4a1f661c
* [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
154 lines
5.0 KiB
ArmAsm
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
|