; This is the trojan program we send to the DSP from DSPSpy to figure it out. REGS_BASE: equ 0x0f80 MEM_HI: equ 0x0f7E MEM_LO: equ 0x0f7F ; ; CODE STARTS HERE. ; Interrupt vectors 8 vectors, 2 opcodes each jmp irq0 jmp irq1 jmp irq2 jmp irq3 jmp irq4 jmp irq5 jmp irq6 jmp irq7 ; Main code at 0x10 sbset #0x02 sbset #0x03 sbclr #0x04 sbset #0x05 sbset #0x06 s16 lri $CR, #0x00ff ; Why do we have a main label here? main: clr $acc1 clr $acc0 ; get address of memory dump and copy it to DRAM call wait_for_dsp_mbox si @DMBH, #0x8888 si @DMBL, #0xdead si @DIRQ, #0x0001 call wait_for_cpu_mbox lrs $ac0.m, @CMBL andi $ac1.m, #0x7fff sr @MEM_HI, $ac1.m sr @MEM_LO, $ac0.m lri $ax0.l, #0 lri $ax1.l, #0 ;(DSP_CR_IMEM | DSP_CR_TO_CPU) lri $ax0.h, #0x2000 lr $ac0.l, @MEM_HI lr $ac0.m, @MEM_LO call do_dma ; get address of registers and DMA them to ram call wait_for_dsp_mbox si @DMBH, #0x8888 si @DMBL, #0xbeef si @DIRQ, #0x0001 call wait_for_cpu_mbox lrs $ac0.m, @CMBL andi $ac1.m, #0x7fff sr @MEM_HI, $ac1.m sr @MEM_LO, $ac0.m lri $ax0.l, #REGS_BASE lri $ax1.l, #0 ;(DSP_CR_IMEM | DSP_CR_TO_CPU) lri $ax0.h, #0x80 lr $ac0.l, @MEM_HI lr $ac0.m, @MEM_LO call do_dma ; Read in all the registers from RAM lri $ar0, #REGS_BASE+1 lrri $ar1, @$ar0 lrri $ar2, @$ar0 lrri $ar3, @$ar0 lrri $ix0, @$ar0 lrri $ix1, @$ar0 lrri $ix2, @$ar0 lrri $ix3, @$ar0 lrri $wr0, @$ar0 lrri $wr1, @$ar0 lrri $wr2, @$ar0 lrri $wr3, @$ar0 lrri $st0, @$ar0 lrri $st1, @$ar0 lrri $st2, @$ar0 lrri $st3, @$ar0 lrri $ac0.h, @$ar0 lrri $ac1.h, @$ar0 lrri $cr, @$ar0 lrri $sr, @$ar0 lrri $prod.l, @$ar0 lrri $prod.m1, @$ar0 lrri $prod.h, @$ar0 lrri $prod.m2, @$ar0 lrri $ax0.l, @$ar0 lrri $ax1.l, @$ar0 lrri $ax0.h, @$ar0 lrri $ax1.h, @$ar0 lrri $ac0.l, @$ar0 lrri $ac1.l, @$ar0 lrri $ac0.m, @$ar0 lrri $ac1.m, @$ar0 lr $ar0, @REGS_BASE jmp start_of_test ; This is where we jump when we're done testing, see above. end_of_test: nop nop nop nop nop nop nop ; We just fall into a loop, playing dead until someone resets the DSP. dead_loop: jmp dead_loop ; Utility function to do DMA. ; ac0.l:ac0.m - external address. ; ax0.l - address in DSP do_dma: sr @DSMAH, $ac0.l sr @DSMAL, $ac0.m sr @DSPA, $ax0.l sr @DSCR, $ax1.l sr @DSBL, $ax0.h ; This kicks off the DMA. ; Waits for said DMA to complete by watching a bit in DSCR. wait_dma: lrs $ac1.m, @DSCR andcf $ac1.m, #0x0004 jlz wait_dma ret ; This waits for a mail to arrive in the DSP in-mailbox. wait_for_dsp_mbox: lrs $ac1.m, @DMBH andcf $ac1.m, #0x8000 jlz wait_for_dsp_mbox ret ; This waits for the CPU to grab a mail that we just sent from the DSP. wait_for_cpu_mbox: lrs $ac1.m, @CMBH andcf $ac1.m, #0x8000 jlnz wait_for_cpu_mbox ret ; IRQ handlers. Not entirely sure what good they do currently. irq0: lri $ac0.m, #0x0000 jmp irq irq1: lri $ac0.m, #0x0001 jmp irq irq2: lri $ac0.m, #0x0002 jmp irq irq3: lri $ac0.m, #0x0003 jmp irq irq4: lri $ac0.m, #0x0004 jmp irq irq5: ; No idea what this code is doing. s16 mrr $st1, $ac0.l mrr $st1, $ac0.m clr $acc0 mrr $ac0.m, $st1 mrr $ac0.l, $st1 nop ; Or why there's a nop sled here. nop nop nop nop nop rti lri $ac0.m, #0x0005 jmp irq irq6: lri $ac0.m, #0x0006 jmp irq irq7: lri $ac0.m, #0x0007 jmp irq irq: lrs $ac1.m, @DMBH andcf $ac1.m, #0x8000 jlz irq si @DMBH, #0x8bad ;sr @DMBL, $wr3 ; ??? sr @DMBL, $ac0.m ; Exception number si @DIRQ, #0x0001 halt ; Through some magic this allows us to properly ack the exception in dspspy ;rti ; allow dumping of ucodes which cause exceptions...probably not safe at all ; DMA:s the current state of the registers back to the PowerPC. To do this, ; it must write the contents of all regs to DRAM. ; Unfortunately, this loop uses ar0 so it's best to use AR1 and friends for testing ; when messing with indexing. send_back: ; make state safe. set16 ; store registers to reg table sr @REGS_BASE, $ar0 lri $ar0, #(REGS_BASE + 1) srri @$ar0, $ar1 srri @$ar0, $ar2 srri @$ar0, $ar3 srri @$ar0, $ix0 srri @$ar0, $ix1 srri @$ar0, $ix2 srri @$ar0, $ix3 srri @$ar0, $wr0 srri @$ar0, $wr1 srri @$ar0, $wr2 srri @$ar0, $wr3 srri @$ar0, $st0 srri @$ar0, $st1 srri @$ar0, $st2 srri @$ar0, $st3 srri @$ar0, $ac0.h srri @$ar0, $ac1.h srri @$ar0, $cr srri @$ar0, $sr srri @$ar0, $prod.l srri @$ar0, $prod.m1 srri @$ar0, $prod.h srri @$ar0, $prod.m2 srri @$ar0, $ax0.l srri @$ar0, $ax1.l srri @$ar0, $ax0.h srri @$ar0, $ax1.h srri @$ar0, $ac0.l srri @$ar0, $ac1.l srri @$ar0, $ac0.m srri @$ar0, $ac1.m ; Regs are stored. Prepare DMA. lri $ax0.l, #0x0000 lri $ax1.l, #1 ;(DSP_CR_IMEM | DSP_CR_TO_CPU) lri $ax0.h, #0x200 lr $ac0.l, @MEM_HI lr $ac0.m, @MEM_LO lri $ar1, #8+8 ; Now, why are we looping here? bloop $ar1, dma_copy call do_dma addi $ac0.m, #0x200 mrr $ac1.m, $ax0.l addi $ac1.m, #0x100 mrr $ax0.l, $ac1.m nop dma_copy: nop ; Wait for the CPU to send us a mail. call wait_for_dsp_mbox si @DMBH, #0x8888 si @DMBL, #0xfeeb si @DIRQ, #0x0001 ; wait for the CPU to recieve our response before we execute the next op call wait_for_cpu_mbox lrs $ac0.m, @CMBL andi $ac1.m, #0x7fff ; Restore all regs again so we're ready to execute another op. lri $ar0, #REGS_BASE+1 lrri $ar1, @$ar0 lrri $ar2, @$ar0 lrri $ar3, @$ar0 lrri $ix0, @$ar0 lrri $ix1, @$ar0 lrri $ix2, @$ar0 lrri $ix3, @$ar0 lrri $wr0, @$ar0 lrri $wr1, @$ar0 lrri $wr2, @$ar0 lrri $wr3, @$ar0 lrri $st0, @$ar0 lrri $st1, @$ar0 lrri $st2, @$ar0 lrri $st3, @$ar0 lrri $ac0.h, @$ar0 lrri $ac1.h, @$ar0 lrri $cr, @$ar0 lrri $sr, @$ar0 lrri $prod.l, @$ar0 lrri $prod.m1, @$ar0 lrri $prod.h, @$ar0 lrri $prod.m2, @$ar0 lrri $ax0.l, @$ar0 lrri $ax1.l, @$ar0 lrri $ax0.h, @$ar0 lrri $ax1.h, @$ar0 lrri $ac0.l, @$ar0 lrri $ac1.l, @$ar0 lrri $ac0.m, @$ar0 lrri $ac1.m, @$ar0 lr $ar0, @REGS_BASE ret ; from send_back ; If you are in set40 mode, use this instead of send_back if you want to stay ; in set40 mode. send_back_40: set16 call send_back set40 ret ; This one's odd. Doesn't look like it should work since it uses ac0.m but ; increments acm0... (acc0) ; -- It's ok as addis adds short immediate to mid accumulator $acD.hm dump_memory: lri $ar2, #0x0000 lri $ac0.m, #0x1000 lri $ar1, #0x1000 bloop $ar1, _fill_loop2 mrr $ar3, $ac0.m nx'ld : $ax0.h, $ax1.h, @$ar0 mrr $ac1.m, $ar0 mrr $ar0, $ar2 srri @$ar0, $ax1.h mrr $ar2, $ar0 mrr $ar0, $ac1.m addis $ac0.m, #0x1 _fill_loop2: nop ret ; from dump_memory ; Obviously this must be included directly before your test code start_of_test: