summaryrefslogtreecommitdiff
path: root/rsp/rspboot_ap.s
diff options
context:
space:
mode:
Diffstat (limited to 'rsp/rspboot_ap.s')
-rw-r--r--rsp/rspboot_ap.s153
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