IROM_BASE: equ 0x8000 lri $CR, #0x00ff lri $SR, #0x2000 si @DMBH, #0x8071 si @DMBL, #0xfeed mainloop: clr $ACC1 clr $ACC0 call wait_for_cpu_mbox+#IROM_BASE ;mmem-addr param1: lr $AC1.M, @CMBL lri $AC0.M, #0xa001 cmp jnz param2+#IROM_BASE call wait_for_cpu_mbox+#IROM_BASE lr $IX0, @CMBH lr $IX1, @CMBL jmp mainloop+#IROM_BASE ;iram-addr param2: lri $AC0.M, #0xc002 cmp jnz param3+#IROM_BASE call wait_for_cpu_mbox+#IROM_BASE lr $IX2, @CMBL jmp mainloop+#IROM_BASE ;iram-length param3: lri $AC0.M, #0xa002 cmp jnz param4+#IROM_BASE call wait_for_cpu_mbox+#IROM_BASE lr $IX3, @CMBL jmp mainloop+#IROM_BASE ;dram-length param4: lri $AC0.M, #0xb002 cmp jnz param5+#IROM_BASE call wait_for_cpu_mbox+#IROM_BASE lr $AX0.L, @CMBL jmp mainloop+#IROM_BASE ;iram-start-addr param5: lri $AC0.M, #0xd001 cmp jnz mainloop+#IROM_BASE call wait_for_cpu_mbox+#IROM_BASE lr $AR0, @CMBL ; skip the branch of bootucode that uses the AX registers jmp bootucode_ix+#IROM_BASE wait_dma: lrs $AC0.M, @DSCR andcf $AC0.M, #0x0004 jlz wait_dma+#IROM_BASE ret WARNPC 0x78 ORG 0x78 ; called by GBA ucode wait_for_cpu_mbox: lrs $AC0.M, @CMBH andcf $AC0.M, #0x8000 jlnz wait_for_cpu_mbox+#IROM_BASE ret WARNPC 0x7e ORG 0x7e ; called by GBA ucode wait_for_dsp_mbox: lrs $AC0.M, @DMBH andcf $AC0.M, #0x8000 jlz wait_for_dsp_mbox+#IROM_BASE ret WARNPC 0x8b ORG 0x8b ; called by GBA ucode dram_to_cpu: sr @DSMAH, $AX0.H sr @DSMAL, $AX0.L si @DSCR, #0x1 sr @DSPA, $AX1.H sr @DSBL, $AX1.L call wait_dma+#IROM_BASE ret WARNPC 0xb5 ORG 0xb5 bootucode: set16 clr $ACC0 mrr $AC0.M, $AX1.L andi $AC0.M, #0xffff jz bootucode_ix+#IROM_BASE WARNPC 0xbc ORG 0xbc ; called by GBA ucode bootucode_ax: lris $AC0.M, #0 srs @DSCR, $AC0.M sr @DSMAH, $AX0.H sr @DSMAL, $AX0.L sr @DSPA, $AX1.H sr @DSBL, $AX1.L call wait_dma+#IROM_BASE bootucode_ix: mrr $AC0.M, $IX3 andi $AC0.M, #0xffff jz bootucode_epilogue+#IROM_BASE lris $AC0.M, #0x2 srs @DSCR, $AC0.M sr @DSMAH, $IX0 sr @DSMAL, $IX1 sr @DSPA, $IX2 sr @DSBL, $IX3 call wait_dma+#IROM_BASE bootucode_epilogue: clr $ACC1 lr $AC1.M, @DSBL jmpr $AR0 WARNPC 0xe7 ORG 0xe7 ; Args: ; AR0 points to the 32 input 1 samples (s16) ; AR1 points to the volume data (init1, delta1, init2, delta2) ; AR2 points to the already mixed samples for output 1 (s32) ; AR3 points to where the output 1 should be stored (s32) ; IX0 points to the 32 input 2 samples (s16) ; IX1 points to where the output 2 should be stored (s32) ; ; Returns: ; AX0.L is the value of the last sample from input 1 ; AX1.H is the value of the last sample from input 2 ; ; for i = 0..31: ; ar3[i] = ((ar2[i] << 16) + ar0[i] * ar1[0]) >> 16 ; for i = 0..31: ; ix1[i] = ((ix1[i] << 16) + ix0[i] * ar1[2]) >> 16 ; ax0.l = ar0[31] * ar1[0] ; ax1.h = ix0[31] * ar1[2] mix_two_add: call mix_add+#IROM_BASE mrr $AR0, $IX0 mrr $AR2, $IX1 mrr $AR3, $IX1 mrr $IX0, $AX0.L call mix_add+#IROM_BASE mrr $AX1.H, $AX0.L mrr $AX0.L, $IX0 ret WARNPC 0x1f4 ORG 0x1f4 ; used by GBA ucode for joyboot length and is the end of some mixing function ; (for an example of hitting the full function, try running the main menu of ; Metroid Prime using the Nintendo DSP ROM). sub_81f4: asr16'ir $ACC1 : $AR1 clr's $ACC0 : @$AR3, $AC1.M ; AC1.M is always #0x0 here. ; necessary both to match register state of official ROM, and for the ; following mul. could also be mrr $AX1.H, $AC0.M (before clearing ACC0). mrr $AX1.H, $AX0.H ; make the product register match. mul's $AX1.L, $AX1.H : @$AR3, $AC1.L ret WARNPC 0x1f9 ORG 0x1f9 ; Args: ; AR0 points to the 32 input samples (s16) ; AR1 points to the volume data (init, delta) ; AR2 points to the already mixed samples (s32) ; AR3 points to where the output should be stored (s32) ; ; Returns: ; AX0.L is the value of the last sample ; AX1.H is the first address after the output ; ; for i = 0..31: ; ar3[i] = ((ar2[i] << 16) + ar0[i] * ar1[0]) >> 16 ; ax0.l = ar0[31] * ar1[0] ; ax1.h = ar3 + 32 // assuming ar3 is a s32 pointer mix_add: lrri $AX1.L, @$AR1 iar $AR1 bloopi #32, ____mix_add_end_loop+#IROM_BASE lrri $AC0.M, @$AR2 lrri $AC0.L, @$AR2 lsl16 $ACC0 lrri $AX0.H, @$AR0 mulx $AX0.H, $AX1.L addp $ACC0 asr16 $ACC0 srri @$AR3, $AC0.M ____mix_add_end_loop: srri @$AR3, $AC0.L movp $ACC0 mrr $AX0.L, $AC0.M mrr $AX1.H, $AR3 ret WARNPC 0x282 ORG 0x282 ; for i = 0..31: ; ar3[i] = ar1[0] + i * ar1[1] ; ar2[i] = ((ar2[i] << 16) + ar0[i] * ar3[i]) >> 16 ; ar3[i+32] = ar1[2] + i * ar1[3] ; ix1[i] = ((ix1[i] << 16) + ix0[i] * ar3[i+32]) >> 16 ; ax0.l = ar0[31] * ar3[31] ; ax1.h = ix0[31] * ar3[63] mix_two_add_ramp: call mix_add_ramp+#IROM_BASE mrr $AR0, $IX0 mrr $AR2, $IX1 mrr $IX1, $AX0.L call mix_add_ramp+#IROM_BASE mrr $AX1.H, $AX0.L mrr $AX0.L, $IX1 ret WARNPC 0x458 ORG 0x458 ; used by GBA ucode for joyboot length sub_8458: ; AC1.L after = AC1.M before + 7. this looks really stupid, but matches ; captured traces and seems to work. addis $AC1.M, #0x7 asr16 $ACC1 srri @$AR3, $AC1.M ; or just #0x0. srri @$AR3, $AC1.L ret WARNPC 0x45d ORG 0x45d ; for i = 0..31: ; ar3[i] = ar1[0] + i * ar1[1] ; ar2[i] = ((ar2[i] << 16) + ar0[i] * ar3[i]) >> 16 ; ax0.l = ar0[31] * ar3[31] mix_add_ramp: clr $ACC0 clr $ACC1 lrri $AC0.L, @$AR1 lrrd $AC1.L, @$AR1 mrr $IX2, $AR3 bloopi #32, ____mix_add_ramp_end_ramp+#IROM_BASE srri @$AR3, $AC0.L ____mix_add_ramp_end_ramp: add $ACC0, $ACC1 srri @$AR1, $AC0.L iar $AR1 mrr $IX3, $AR1 mrr $AR1, $IX2 mrr $AR3, $AR2 bloopi #32, ____mix_add_ramp_end_loop+#IROM_BASE lrri $AC0.M, @$AR2 lrri $AC0.L, @$AR2 lsl16 $ACC0 lrri $AX0.H, @$AR0 lrri $AX1.L, @$AR1 mulx $AX0.H, $AX1.L addp $ACC0 asr16 $ACC0 srri @$AR3, $AC0.M ____mix_add_ramp_end_loop: srri @$AR3, $AC0.L movp $ACC0 mrr $AX0.L, $AC0.M mrr $AR1, $IX3 mrr $AR3, $IX2 ret WARNPC 0x723 ORG 0x723 ; called by GBA ucode sub_8723: ; in GBA-HLE, the nonce challenge is XOR'd with 0x6f646573, which happens ; to match the values of the AX1.H register across these two calls. xorr $AC1.M, $AX1.H ; the value of @AR2 is always the same as AC1.M after srrd @$AR2, $AC1.M ret WARNPC 0x809 ORG 0x809 ; called by GBA ucode sub_8809: ; AR2 is the only addressing register that corresponds to the dmem writes ; could be AC1.L or AX0.L in the second call, but can't be AX0.L in the ; third call. srr @$AR2, $AC1.L ; AC1.M after calling always look like either AC1.M | AC0.M or ; AC1.M | AX0.H. TODO: Why pick AX0.H? orr $AC1.M, $AX0.H ; the second dmem write is incremented only in calls #3A and #3B. There, ; IX2 is the only register set to 1, and it's specifically set to 1 in the ; ucode. It's set to 0 in the first two calls. addarn $AR2, $IX2 ; obvious srri @$AR2, $AC1.M ret WARNPC 0x8e5 ORG 0x8e5 ; used by GBA ucode for challenge nonce, logo palette/speed, and joyboot length sub_88e5: dar $AR1 ; always gets decremented, no effect on rest of function lrri $AC1.M, @$AR2 lrrd $AC1.L, @$AR2 add $ACC0, $ACC1 ; signed addition orr $AC0.M, $AX0.H srri @$AR2, $AC0.M srr @$AR2, $AC0.L ret WARNPC 0x1000 ORG 0x1000