diff options
| author | lameguy64 <lameguy64@github.com> | 2024-05-04 18:55:05 +0800 |
|---|---|---|
| committer | lameguy64 <lameguy64@github.com> | 2024-05-04 18:55:05 +0800 |
| commit | 702bb601fb5712e2ae962a34b89204c646fe98f5 (patch) | |
| tree | 765f400d1535d37c5ff4df61d01229a7dc7d8207 /indev/psn00bdbg-mk2/monitor/monitor.asm | |
| parent | 00abe5963fbead092f91935b90390aa5a9111c43 (diff) | |
| download | psn00bsdk-702bb601fb5712e2ae962a34b89204c646fe98f5.tar.gz | |
Included PSn00bDBG-mk2 monitor and test utility
Diffstat (limited to 'indev/psn00bdbg-mk2/monitor/monitor.asm')
| -rw-r--r-- | indev/psn00bdbg-mk2/monitor/monitor.asm | 1343 |
1 files changed, 1343 insertions, 0 deletions
diff --git a/indev/psn00bdbg-mk2/monitor/monitor.asm b/indev/psn00bdbg-mk2/monitor/monitor.asm new file mode 100644 index 0000000..78b2330 --- /dev/null +++ b/indev/psn00bdbg-mk2/monitor/monitor.asm @@ -0,0 +1,1343 @@ +;
+; The debug monitor consists of two parts; the installer binary (assembled as
+; patchinst.bin) and the monitor binary (assembled as patchcode.bin). The
+; purpose of the installer binary is to patch the debug monitor into the kernel
+; space starting at address C000h. The monitor's entrypoints are then hooked to
+; BIOS functions A(40h) (SystemErrorUnresolvedException) and B(17h)
+; (ReturnFromException), the latter of which is required so the debug monitor
+; can poll for any commands during any IRQ exception as opposed to polling the
+; debug monitor manually with a special break instruction (break 1024, defined
+; in pollhost()). This allows the debug monitor to stop program execution at any
+; point in the program, provided interrupts are not disabled.
+;
+
+.psx
+
+.include "cmdefs.inc"
+
+; ****************************
+; Register constants
+; ****************************
+
+BPC equ $3 ; cop0 register definitions
+BDA equ $5
+JUMPDEST equ $6
+DCIC equ $7
+BADVADDR equ $8
+BDAM equ $9
+BPCM equ $11
+SR equ $12
+CAUSE equ $13
+EPC equ $14
+PRID equ $15
+
+; ****************************
+; Constants
+; ****************************
+
+PCB_addr equ 0x108(r0) ; Pointer to main/current TCB
+
+TCB_status equ 0 ; TCB block
+TCB_reserve equ 4
+TCB_r0 equ 8
+TCB_at equ 12
+TCB_v0 equ 16
+TCB_v1 equ 20
+TCB_a0 equ 24
+TCB_a1 equ 28
+TCB_a2 equ 32
+TCB_a3 equ 36
+TCB_t0 equ 40
+TCB_t1 equ 44
+TCB_t2 equ 48
+TCB_t3 equ 52
+TCB_t4 equ 56
+TCB_t5 equ 60
+TCB_t6 equ 64
+TCB_t7 equ 68
+TCB_s0 equ 72
+TCB_s1 equ 76
+TCB_s2 equ 80
+TCB_s3 equ 84
+TCB_s4 equ 88
+TCB_s5 equ 92
+TCB_s6 equ 96
+TCB_s7 equ 100
+TCB_t8 equ 104
+TCB_t9 equ 108
+TCB_k0 equ 112
+TCB_k1 equ 116
+TCB_gp equ 120
+TCB_sp equ 124
+TCB_fp equ 128
+TCB_ra equ 132
+TCB_epc equ 136
+TCB_hi equ 140
+TCB_lo equ 144
+TCB_sr equ 148
+TCB_cause equ 152
+
+MONADDR equ 0xC000 ; Monitor install address
+
+SAVE_mode equ 0x30(r0) ; Saved variables
+SAVE_tmode equ 0x31(r0) ; These are stored from address 0
+SAVE_k0 equ 0x34(r0)
+SAVE_k1 equ 0x38(r0)
+SAVE_dcic equ 0x3C(r0)
+
+; ****************************
+; Debug monitor status values
+; ****************************
+
+DB_STAT_STOP equ 0
+DB_STAT_BREAK equ 1
+DB_STAT_EXCEPT equ 2
+DB_STAT_RUN equ 3
+
+; ****************************
+; Program Breakpoint entry (16 bytes)
+; ****************************
+
+MAX_BREAK equ 32
+
+DB_BRK_FLAG equ 0 ; Breakpoint flags
+DB_BRK_ADDR equ 4 ; Breakpoint address
+DB_BRK_INST equ 8 ; Breakpoint original opcode
+DB_BRK_LCNT equ 12 ; Breakpoint counters
+DB_BRK_HCNT equ 14
+DB_BRK_LEN equ 16 ; Breakpoint entry length
+
+; ****************************
+; Macros
+; ****************************
+
+.macro EnterCriticalSection
+ addiu a0, r0, 0x1
+ syscall 0
+.endmacro
+
+.macro ExitCriticalSection
+ addiu a0, r0, 0x2
+ syscall 0
+.endmacro
+
+.macro bios_rfe ; Jumps to real ReturnFromException()
+ la v0, rfe_ptr
+ lw v0, 0(v0)
+ nop
+ jr v0
+ nop
+.endmacro
+
+.macro rfe ; RFE opcode workaround for armips
+ .word 0x42000010 ; (for the version I'm stuck with)
+.endmacro
+
+; ****************************
+; Monitor installer binary
+; ****************************
+
+.create "patchinst.bin", 0x80010000
+
+; workaround notes for getting trace to operate properly...
+;
+; Modify exception handler jump at address 0x80 from:
+;
+; lui k0, 0
+; addiu k0, 0xC80
+; jr k0
+; nop
+;
+; To the following routine:
+;
+; mfc0 k0, DCIC ; backup DCIC value to a known, unused address
+; nop
+; sw k0, SAVE_dcic
+; mtc0 r0, DCIC ; deactivate debug flags by clearing DCIC
+; lui k0, 0
+; addiu k0, 0xC80
+; jr k0
+; nop
+;
+; This debug monitor is not really meant to debug commercial games that don't
+; play well with kernel registers (k0/k1) being modified by exception handlers.
+; Though games are not supposed to touch the kernel registers in the first
+; place albeit FlushCache() uses the two registers.
+
+;
+; = Patch installer routine
+;
+;
+patchinst:
+ addiu sp, -4
+ sw ra, 0(sp)
+ la a0, breakhook ; Install breakpoint vector hook
+ la a1, (breakhook_end-breakhook)+4
+ li a2, 0xA0000040
+ jal copymem
+ nop
+ li v0, 0x200 ; Hook monitor entrypoint to
+ li v1, 0x40 ; SysErrUnresolvedException() slot
+ sll v1, 2
+ addu v0, v1
+ la v1, monitor_entry
+ sw v1, 0(v0)
+ addiu t2, r0, 0xB0 ; GetB0Table()
+ jalr t2
+ addiu t1, r0, 0x57
+ li a0, 0x17 ; Get pointer of ReturnFromException()
+ sll a0, 2
+ addu a0, v0
+ lw v0, 0(a0) ; Save original address for later
+ la v1, monitor_entry
+ beq v0, v1, @@noinstall ; Don't install if hooked already
+ nop
+ addiu sp, -4
+ sw a0, 0(sp)
+ la a0, payload ; Load monitor code into target
+ la a1, (monitor_end-monitor)+4 ; address
+ lui a2, 0xA000 ; write via uncached segment
+ jal copymem
+ ori a2, MONADDR
+ lw a0, 0(sp)
+ addiu sp, 4
+ lw v0, 0(a0)
+ la v1, rfe_ptr
+ sw v0, 0(v1)
+ la v0, monitor_entry ; Set new address to table
+ sw v0, 0(a0)
+ li a0, 0x80 ; Move existing exception vector
+ li a2, 0x90 ; jump to prepend patch
+ jal copymem
+ li a1, 16
+ la a0, exceptpatch ; Patch the exception vector for
+ li a2, 0x80 ; trace to work properly
+ jal copymem
+ li a1, 16
+ addiu t2, r0, 0xA0 ; FlushCache() just to make sure
+ jalr t2
+ addiu t1, r0, 0x44
+ jal init_breakpoints
+ nop
+ @@noinstall:
+ lw ra, 0(sp) ; Return to caller, debugger
+ addiu sp, 4 ; already installed
+ jr ra
+ nop
+
+ ; patchinst
+
+;
+; = installer's copy routine
+;
+;
+;
+copymem:
+ subiu a1, 4
+ lw v0, 0(a0)
+ addiu a0, 4
+ sw v0, 0(a2)
+ bgtz a1, copymem
+ addiu a2, 4
+ jr ra
+ nop
+
+ ; copymem
+
+;
+; = Initializes breakpoint list
+;
+init_breakpoints:
+ la a2, break_entries
+ li a3, MAX_BREAK
+ addiu v0, r0, -1
+ @@clear_loop:
+ sw v0, DB_BRK_INST(a2)
+ sw v0, DB_BRK_ADDR(a2)
+ sw r0, DB_BRK_FLAG(a2)
+ addiu a3, -1
+ bnez a3, @@clear_loop
+ addiu a2, DB_BRK_LEN
+ jr ra
+ nop
+
+ ; init_breakpoints
+
+;
+; = Break vector hook code
+;
+; This is copied to the breakpoint exception vector at address 40h
+;
+breakhook:
+ sw k0, SAVE_k0 ; Save K0 and K1 registers
+ mfc0 k0, DCIC ; Save DCIC
+ sw k1, SAVE_k1
+ mtc0 r0, DCIC ; Clear DCIC in case trace is still
+ sw k0, SAVE_dcic ; effective
+ la k0, breakhandler ; Jump to break vector handler
+ jr k0
+ nop
+breakhook_end:
+
+;
+; = Exception vector patch
+;
+; This is prepended to the exception vector jump at address 80h
+;
+exceptpatch:
+ mfc0 k0, DCIC ; backup DCIC value
+ nop
+ mtc0 r0, DCIC ; clear DCIC
+ sw k0, SAVE_dcic
+payload: ; patchinst.bin must be appended
+ ; with patchcode.bin after assembly
+.close ; patchinst.bin
+
+
+;
+; = The actual debug monitor itself
+;
+;
+.create "patchcode.bin", MONADDR ; The debug monitor program itself
+
+monitor:
+
+ dw monitor_entry ; Pointers used by stub installer
+ dw rfe_ptr
+ dw break_entries
+ nop
+
+str_dbinfo: .asciiz "PSnDBmk2" ; Debug monitor identifier (at
+ .align 4 ; start so it can be seen easily)
+
+;
+; = Include comms routines
+;
+;.include "comms.inc"
+.include "sio.inc"
+
+;
+; = Main exception handler
+;
+; When BIOS function ReturnFromException() or SysErrorUnresolvedException()
+; is called, this routine checks for unresolved exceptions and any pending
+; monitor commands to process before actually returning from exception.
+;
+; In other words, this routine is executed on every interrupt. Thus, allowing
+; the debug monitor to operate like a background task.
+;
+monitor_entry:
+ lw k0, PCB_addr ; Retrieve address of current TCB
+ nop
+ lw k0, 0(k0) ; Load the address
+ nop
+ lw v0, TCB_cause(k0) ; Get cause register value from TCB
+ nop
+ srl v0, 2
+ andi v0, 0x1F
+ beq v0, 0x8, @@return ; Ignore syscall exceptions
+ nop
+ beq v0, 0x9, @@checkbrk ; Check opcode if break instruction
+ nop
+ bnez v0, dbexception ; Any other cause other than INTs
+ nop ; is an unhandled exception
+ addiu sp, -4
+ sw ra, 0(sp)
+ jal comm_EnableListen ; Enable listening of the comms
+ nop ; interface
+ jal comm_GetStatus ; Get comms status
+ nop
+ lw ra, 0(sp)
+ addiu sp, 4
+ bnez v0, commcmd ; If comm_GetStatus returns pending
+ nop ; data (non-zero) then query command
+ @@return:
+ lw v0, SAVE_dcic ; Resume program execution
+ nop
+ mtc0 v0, DCIC
+ bios_rfe ; Call actual ReturnFromException()
+ @@checkbrk: ; Breakpoint check routine
+ lw v0, TCB_epc(k0) ; Get PC address
+ nop
+ lw v1, 0(v0) ; Get opcode
+ nop
+ srl v1, 6 ; Check if break 1024 opcode
+ andi v1, 0xFFFF ; (pollhost)
+ beq v1, 1024, @@is_pollhost
+ nop
+ beq v1, 512, breakexception ; Check if break 512 opcode
+ nop ; (soft breakpoint)
+ b dbexception
+ nop
+ @@is_pollhost:
+ addiu v0, 4
+ sw v0, TCB_epc(k0) ; Set adjusted PC address to skip
+ b @@return ; break instruction
+ nop
+
+ ; monitor_entry
+
+;
+; = Breakpoint exception handler
+;
+; This routine is only executed when a COP0 debug exception occurs and the
+; processor jumps to the debug exception vector on address 040h. The breakpoints
+; here are not to be confused with breakpoint instructions, which still vectors
+; to the standard exception vector at address 080h.
+;
+breakhandler:
+ lw k0, PCB_addr ; Obtain TCB address
+ nop
+ lw k0, 0(k0)
+ nop
+ sw ra, TCB_ra(k0) ; Save some register values to TCB
+ sw v0, TCB_v0(k0)
+ lw v0, SAVE_dcic ; Obtain DCIC register value
+ sw v1, TCB_v1(k0)
+ andi v0, 0x20 ; Mask DCIC for trace condition
+ bnez v0, @@istrace ; Check if trace condition
+ nop
+ mfc0 v0, CAUSE ; Check if break on branch delay
+ lui v1, 0x8000
+ and v0, v1
+ beqz v0, @@nobd ; If not then take it as a runto
+ nop ; or trace completion
+ mfc0 v0, CAUSE ; Check if branch was taken
+ lui v1, 0x4000 ; (matched condition)
+ and v0, v1
+ beqz v0, @@notake
+ nop
+ mfc0 v0, JUMPDEST ; If taken, set JUMPDEST as program
+ nop ; breakpoint address
+ mtc0 v0, BPC
+ b @@destbreak ; Resume execution with break
+ nop
+ @@notake: ; If branch is not taken...
+ mfc0 v0, EPC ; (note: EPC still points to the
+ nop ; branch instruction even if
+ addiu v0, 8 ; breakpoint was in the delay slot,
+ mtc0 v0, BPC ; so must increment address by 8)
+ b @@destbreak ; Resume execution with break
+ nop
+ @@nobd:
+ la v0, db_step
+ lbu v1, 0(v0)
+ nop
+ bnez v1, @@stepbreak
+ nop
+ la v1, db_brkpass ; Obtain breakpoint pass status byte
+ lbu v1, 0(v1)
+ nop
+ bnez v1, @@is_brkpass ; Branch to pass routine if set
+ nop
+ @@stepbreak:
+ sb r0, 0(v0) ; Clear step flag
+ sw at, TCB_at(k0) ; Simulate kernel saving
+ sw a0, TCB_a0(k0) ; registers to current TCB
+ sw a1, TCB_a1(k0)
+ sw a2, TCB_a2(k0)
+ sw a3, TCB_a3(k0)
+ sw t0, TCB_t0(k0)
+ sw t1, TCB_t1(k0)
+ sw t2, TCB_t2(k0)
+ sw t3, TCB_t3(k0)
+ sw t4, TCB_t4(k0)
+ sw t5, TCB_t5(k0)
+ sw t6, TCB_t6(k0)
+ sw t7, TCB_t7(k0)
+ sw s0, TCB_s0(k0)
+ sw s1, TCB_s1(k0)
+ sw s2, TCB_s2(k0)
+ sw s3, TCB_s3(k0)
+ sw s4, TCB_s4(k0)
+ sw s5, TCB_s5(k0)
+ sw s6, TCB_s6(k0)
+ sw s7, TCB_s7(k0)
+ sw t8, TCB_t8(k0)
+ sw t9, TCB_t9(k0)
+ lw v0, SAVE_k0
+ lw v1, SAVE_k1
+ sw v0, TCB_k0(k0)
+ sw v1, TCB_k1(k0)
+ sw gp, TCB_gp(k0)
+ sw sp, TCB_sp(k0)
+ sw fp, TCB_fp(k0)
+ mfc0 v0, EPC
+ mfc0 v1, SR
+ sw v0, TCB_epc(k0)
+ mfc0 v0, CAUSE
+ sw v1, TCB_sr(k0)
+ sw v0, TCB_cause(k0)
+ mfhi v0
+ mflo v1
+ sw v0, TCB_hi(k0)
+ sw v1, TCB_lo(k0)
+ la v1, db_state ; Update execution status
+ li v0, DB_STAT_BREAK
+ sb v0, 0(v1)
+ jal comm_DisableListen ; Adjust comms before debug mode
+ nop
+ jal unset_breakpoints ; Undo breakpoint patches if any
+ nop
+ la v0, db_runto ; Clear runto status
+ sb r0, 0(v0)
+ b cmdstart ; Branch to command query loop
+ nop
+ @@istrace: ; For break on jump/branch conditions
+ mfc0 v1, EPC ; Get exception address
+ lui v0, 0xA180 ; Enable software breakpoint
+ addiu v1, 4 ; Adjust to breakpoint to delay slot
+ mtc0 v1, BPC ; Set as program break address
+ b breakhandler_ret ; Resume execution
+ sw v0, SAVE_dcic
+ @@destbreak: ; For jump/branch destination break
+ lui v0, 0xA180 ; Enable software breakpoint
+ b breakhandler_ret ; Resume execution
+ sw v0, SAVE_dcic
+ @@is_brkpass: ; For breakpoint pass condition
+ jal set_breakpoints ; Apply breakpoints again
+ nop
+ addiu sp, -4
+ sw k0, 0(sp)
+ sw at, TCB_at(k0) ; Save a bunch of registers as
+ sw t0, TCB_t0(k0) ; FlushCache() uses them
+ sw t1, TCB_t1(k0)
+ sw t2, TCB_t2(k0)
+ sw t3, TCB_t3(k0)
+ sw t4, TCB_t4(k0)
+ addiu t2, r0, 0xA0 ; FlushCache()
+ jalr t2
+ addiu t1, r0, 0x44
+ lw k0, 0(sp)
+ addiu sp, 4
+ lw at, TCB_at(k0)
+ lw t0, TCB_t0(k0)
+ lw t1, TCB_t1(k0)
+ lw t2, TCB_t2(k0)
+ lw t3, TCB_t3(k0)
+ lw t4, TCB_t4(k0)
+ la v0, db_runto ; Get runto state
+ lbu v0, 0(v0)
+ sw r0, SAVE_dcic ; Clear DCIC if no runto condition
+ beqz v0, @@no_runto ; is active
+ nop
+ la v0, runto_addr ; Set runto parameters
+ lw v0, 0(v0)
+ la v1, runto_dcic
+ lw v1, 0(v1)
+ nop ; arsemips weirdness, complains about load delay here
+ mtc0 v0, BPC
+ sw v1, SAVE_dcic
+ @@no_runto:
+ la v0, db_brkpass ; Clear breakpass flag
+ sb r0, 0(v0)
+ b breakhandler_ret ; Resume execution
+ nop
+
+ ; breakhandler
+
+;
+; = Break handler return routine
+;
+; Used to resume program execution from the breakpoint exception vector.
+;
+breakhandler_ret:
+ lw v0, TCB_v0(k0)
+ lw v1, TCB_v1(k0)
+ lw ra, TCB_ra(k0) ; Restore return address
+ lw k0, SAVE_dcic ; Restore DCIC
+ lw k1, SAVE_k1 ; Restore k1 (just in case)
+ mtc0 k0, DCIC
+ nop
+ mfc0 k0, EPC ; Return from exception
+ nop
+ jr k0
+ rfe
+ nop
+
+ ; breakhandler_ret
+
+;
+; = On command receive routine
+;
+; This routine is intended to be jumped to from monitor_entry.
+;
+commcmd:
+ jal unset_breakpoints ; Undo patched breakpoints if any
+ nop
+ jal comm_ReadByte
+ nop
+ move a0, v0
+ jal comm_DisableListen ; Adjust comms before debug mode
+ nop
+ lui sp,0x8001 ; Put stack to kernel memory space
+ addiu sp, -4
+ b querycmd
+ move v0, a0
+ ; commcmd
+
+;
+; = Breakpoint exception routine
+;
+breakexception:
+ lw a0, TCB_epc(k0) ; Get breakpoint flags
+ jal getbreakaddr
+ nop
+ bltz v0, @@unknown_break
+ nop
+ lhu v1, DB_BRK_LCNT(v0) ; Increment counter of breakpoint
+ nop
+ addiu v1, 1
+ sh v1, DB_BRK_LCNT(v0) ; Store updated value
+ lw v1, 0(v0)
+ nop
+ andi v1, 0x3 ; Mask break mode bits
+ beqz v1, @@do_resume ; Resume execution if disabled
+ nop
+ beq v1, 2, @@counter_break ; continue until counter reached
+ nop
+ @@unknown_break:
+ la v1, db_state ; Set monitor state that a
+ li v0, DB_STAT_BREAK ; program breakpoint occurred
+ sb v0, 0(v1)
+ b dbbreak
+ nop
+ @@counter_break: ; Counter break
+ addiu sp, -4
+ sw v0, 0(sp)
+ lhu v1, DB_BRK_LCNT(v0)
+ lhu v0, DB_BRK_FLAG+2(v0) ; Retrieve counter target
+ nop
+ blt v1, v0, @@do_resume ; Resume if counter not met
+ nop
+ lw v0, 0(sp) ; Clear the counter
+ addiu sp, 4
+ sh r0, DB_BRK_LCNT(v0)
+ b @@unknown_break ; Branch to trigger breakpoint
+ nop
+ @@do_resume:
+ jal unset_breakpoints
+ addiu sp, 4
+ b resume
+ nop
+
+ ; breakexception
+
+;
+; = Enter debug exception routine
+;
+dbexception:
+ la v1, db_state ; Set monitor state that a
+ li v0, DB_STAT_EXCEPT ; program exception occurred
+ sb v0, 0(v1)
+
+dbbreak:
+ jal comm_DisableListen ; Disable comms listen mode
+ nop
+ jal unset_breakpoints ; Undo breakpoints
+ nop
+
+ ; dbexception
+
+;
+; = Monitor command loop
+;
+cmdstart:
+ lui sp,0x8001 ; Put stack to kernel memory space
+ addiu sp, -4
+cmdloop:
+ jal comm_ReadByte
+ nop
+ bltz v0, cmdloop ; Keep repeating if connection
+ nop ; is in a dropped state
+
+;
+; = Query monitor commands (see commands.txt)
+;
+querycmd:
+ bne v0, CMD_REBOOT, @@no_reboot ; Check if reset command
+ nop
+ j reboot
+ nop
+ @@no_reboot:
+ blt v0, 0xD0, @@cmdend ; Ignore command bytes that are
+ nop ; out of range
+ bgt v0, 0xDB, @@cmdend
+ nop
+ subiu v0, 0xD0 ; Adjust command byte
+ sll v0, 2 ; Multiply by four
+ la v1, cmd_table ; Apply command value offset
+ addu v0, v1
+ lw a0, 0(v0) ; Load function address
+ nop
+ jalr a0 ; Jump to it
+ nop
+ @@cmdend:
+ la v1, db_state ; if status is 4 (running),
+ lbu v0, 0(v1) ; return from exception
+ nop
+ beq v0, DB_STAT_RUN, resume
+ nop
+ b cmdloop
+ nop
+
+cmd_table:
+ dw cmd_getstatus ; D0
+ dw cmd_info ; D1
+ dw cmd_setexec ; D2
+ dw cmd_runto ; D3
+ dw cmd_setbrk ; D4
+ dw cmd_clrbrk ; D5
+ dw cmd_getregs ; D6
+ dw cmd_mem ; D7
+ dw cmd_word ; D8
+ dw cmd_getbrk ; D9
+ dw cmd_setreg ; DA
+ dw cmd_setdbrk ; DB
+
+resume: ; Resume execution
+ addiu v1, r0, -1
+ mtc0 v1, BPCM
+ jal set_breakpoints ; Apply the breakpoints
+ nop
+ bnez v0, @@place_bpc ; If EPC is on a breakpoint,
+ nop ; place a break address to step
+ la v0, db_runto ; If EPC is not on breakpoint
+ lbu v0, 0(v0) ; check if a runto operation is
+ nop ; in progress
+ beqz v0, @@cont_resume ; skip if a runto is not set
+ nop
+ la a0, runto_dcic ; set runto as software break
+ lw v0, 0(a0)
+ la a0, runto_addr
+ lw v1, 0(a0)
+ sw v0, SAVE_dcic
+ mtc0 v1, BPC
+ @@cont_resume:
+ addiu t2, r0, 0xA0 ; FlushCache()
+ jalr t2
+ addiu t1, r0, 0x44
+ addiu sp, -4
+ sw ra, 0(sp)
+ jal comm_EnableListen ; Enable listening of the comms
+ nop ; interface
+ lw ra, 0(sp)
+ addiu sp, 4
+ lw v0, SAVE_dcic ; Apply DCIC settings
+ nop
+ mtc0 v0, DCIC
+ bios_rfe ; Return from exception
+ @@place_bpc:
+ mfc0 v0, EPC ; Set break address to the
+ lui v1, 0xA180 ; opcode following EPC
+ addiu v0, 4
+ mtc0 v0, BPC
+ sw v1, SAVE_dcic
+ li v0, 1 ; Set that a breakpoint pass is
+ la v1, db_brkpass ; in progress
+ sb v0, 0(v1)
+ b @@cont_resume ; Continue resume sequence
+ nop
+
+;
+; = Performs a soft reboot
+;
+reboot:
+ li v1, 0x1F801814 ; Blank video output
+ li v0, 0x03000001
+ sw v0, 0(v1)
+ ;la v1, SIO_CTRL_REG_A ; Reset serial before reboot otherwise
+ ;li v0, 0x40 ; serial will stop listening properly
+ ;sh v0, 0(v1)
+ lui v0, 0xBFC0 ; Jump to BIOS ROM
+ jr v0
+ nop
+
+ ; reboot
+
+;
+; = Queries info command
+;
+cmd_info:
+ addiu sp, -4
+ sw ra, 0(sp)
+ la a0, str_dbinfo ; Send info string
+ jal comm_SendStr
+ nop
+ lw ra, 0(sp)
+ addiu sp, 4
+ jr ra
+ nop
+
+ ; cmd_info
+
+;
+; = Queries get status
+;
+cmd_getstatus:
+ addiu sp, -4
+ sw ra, 0(sp)
+ la a0, db_state ; Send status byte
+ jal comm_SendByte
+ lbu a0, 0(a0)
+ lw ra, 0(sp)
+ addiu sp, 4
+ jr ra
+ nop
+
+ ; cmd_getstatus
+
+;
+; = Queries set exec command
+;
+cmd_setexec:
+ addiu sp, -4
+ sw ra, 0(sp)
+ jal comm_ReadByte
+ nop
+ beq v0, 0, @@setstate ; Program stop
+ li v1, DB_STAT_STOP
+ beq v0, 1, @@setstep ; Program step
+ nop
+ sw r0, SAVE_dcic
+ beq v0, 2, @@setstate ; Program resume
+ li v1, DB_STAT_RUN
+ b @@return ; ignore any unknown value
+ nop
+ @@setstep:
+ lw v0, TCB_epc(k0) ; Set breakpoint for single step
+ nop
+ addiu v0, 4
+ mtc0 v0, BPC
+ lui v0, 0xA180 ; Enable program break
+ sw v0, SAVE_dcic
+ la a0, db_step ; Set step flag
+ li v0, 1 ; when starting from a breakpoint
+ sb v0, 0(a0)
+ b @@setstate ; Proceed to run state
+ li v1, DB_STAT_RUN
+ @@setstate:
+ move v0, v1
+ la v1, db_state ; Set new state
+ sb v0, 0(v1)
+ @@return:
+ la a0, db_state ; Send acknowledgement byte
+ jal comm_SendByte
+ lbu a0, 0(a0)
+ lw ra, 0(sp)
+ addiu sp, 4
+ jr ra
+ nop
+
+ ; cmd_setexec
+
+
+;
+; = Run to specified address
+;
+cmd_runto:
+ addiu sp, -4
+ sw ra, 0(sp)
+ jal comm_ReadByte ; Get flags byte
+ nop
+ jal comm_ReadReg ; Get target address
+ move a0, v0
+ andi a0, 0x1 ; Check if trace bit is set
+ bnez a0, @@dotrace
+ lui v1, 0xB180 ; Enable program break with trace
+ lui v1, 0xA180 ; Enable program break
+ @@dotrace:
+ la a0, runto_addr ; Store run-to address in case of
+ sw v0, 0(a0) ; breakpoint resume
+ la a0, runto_dcic ; Store DCIC settings in case of
+ sw v1, 0(a0) ; breakpoint resume
+ li v0, 1 ; Set flag that a runto operation
+ la a0, db_runto ; is in progress
+ sb v0, 0(a0)
+ li v0, DB_STAT_RUN ; Set to run mode
+ la v1, db_state
+ sb v0, 0(v1)
+ jal comm_SendByte ; Send response byte
+ move a0, v0
+ lw ra, 0(sp)
+ addiu sp, 4
+ jr ra
+ nop
+
+ ; cmd_runto
+
+;
+; = Set program breakpoint
+;
+cmd_setbrk:
+ addiu sp, -4
+ sw ra, 0(sp)
+ jal comm_ReadReg ; Get breakpoint address
+ nop
+ jal comm_ReadReg ; Get flags value
+ move a0, v0
+ move a1, v0
+ move v0, a0
+ andi v0, 0x3 ; Reject address if not aligned
+ bnez v0, @@brk_badaddr
+ nop
+ la a2, break_entries ; Get address to breakpoint list
+ li a3, MAX_BREAK
+ addiu v1, r0, -1
+ @@brk_scan:
+ lw v0, DB_BRK_ADDR(a2)
+ addiu a3, -1
+ beq v0, a0, @@brk_update ; Branch to entry update if
+ nop ; breakpoint already set
+ bne v0, -1, @@brk_next ; Advance if entry not unset
+ nop
+ sw a0, DB_BRK_ADDR(a2) ; Complete the breakpoint entry
+ sw a1, DB_BRK_FLAG(a2)
+ sh r0, DB_BRK_LCNT(a2) ; Reset counter
+ b @@brk_set
+ nop
+ @@brk_next:
+ bnez a3, @@brk_scan
+ addiu a2, DB_BRK_LEN ; Advance to next breakpoint entry
+ jal comm_SendByte ; Send a zero when no breakpoint
+ move a0, r0 ; slots available
+ b @@brk_exit
+ nop
+ @@brk_set: ; Breakpoint was set/updated,
+ jal comm_SendByte ; respond with 1
+ li a0, 1
+ @@brk_exit:
+ lw ra, 0(sp)
+ addiu sp, 4
+ jr ra
+ nop
+ @@brk_badaddr: ; Send a bad address byte
+ jal comm_SendByte
+ move a0, r0
+ b @@brk_exit
+ nop
+ @@brk_update:
+ sw a1, DB_BRK_FLAG(a2)
+ sh r0, DB_BRK_LCNT(a2) ; Reset counter
+ jal comm_SendByte ; respond with 1
+ li a0, 2
+ b @@brk_exit
+ nop
+
+ ; cmd_setbrk
+
+;
+; = Clear program breakpoints
+;
+cmd_clrbrk:
+ addiu sp, -4
+ sw ra, 0(sp)
+ la a2, break_entries
+ li a3, MAX_BREAK
+ addiu v0, r0, -1
+ @@clear_loop:
+ sw v0, DB_BRK_INST(a2)
+ sw v0, DB_BRK_ADDR(a2)
+ sw r0, DB_BRK_FLAG(a2)
+ addiu a3, -1
+ bnez a3, @@clear_loop
+ addiu a2, DB_BRK_LEN
+ jal comm_SendByte
+ li a0, 1
+ lw ra, 0(sp)
+ addiu sp, 4
+ jr ra
+ nop
+
+ ; cmd_clrbrk
+
+;
+; = Do memory I/O operation
+;
+cmd_word:
+ addiu sp, -4
+ sw ra, 0(sp)
+ jal comm_ReadByte ; Get type of operation
+ nop
+ beq v0, 1, @@dowrite
+ nop
+ beq v0, -1, @@cancel
+ nop
+ jal comm_ReadByte ; Get word type
+ nop
+ beq v0, -1, @@cancel
+ nop
+ jal comm_ReadReg ; Get read address
+ move a0, v0
+ beq v0, -1, @@cancel
+ nop
+ beq a0, 2, @@rw32
+ nop
+ beq a0, 1, @@rw16
+ nop
+ jal comm_SendReg ; Do byte read
+ lbu a0, 0(v0)
+ b @@cancel
+ nop
+ @@rw16:
+ jal comm_SendReg ; Do halfword read
+ lhu a0, 0(v0)
+ b @@cancel
+ nop
+ @@rw32:
+ jal comm_SendReg ; Do word read
+ lw a0, 0(v0)
+ @@cancel:
+ lw ra, 0(sp)
+ addiu sp, 4
+ jr ra
+ nop
+ @@dowrite:
+ jal comm_ReadByte ; Get word type
+ nop
+ beq v0, -1, @@cancel
+ nop
+ jal comm_ReadReg ; Get write address
+ move a1, v0
+ beq v0, -1, @@cancel
+ nop
+ jal comm_ReadReg ; Get write value
+ move a0, v0
+ beq v0, -1, @@cancel
+ nop
+ beq a1, 2, @@ww32
+ nop
+ beq a1, 1, @@ww16
+ nop
+ b @@done ; Write byte value
+ sb v0, 0(a0)
+ @@ww16:
+ b @@done ; Write halfword value
+ sh v0, 0(a0)
+ @@ww32:
+ sw v0, 0(a0) ; Write word value
+ @@done:
+ la a0, db_state ; Send acknowledge byte
+ jal comm_SendByte
+ lbu a0, 0(a0)
+ b @@cancel
+ nop
+
+ ; cmd_word
+
+cmd_getregs:
+ addiu sp, -4
+ sw ra, 0(sp)
+ addiu a1, k0, TCB_r0 ; Send registers 0-34 from TCB
+ addi a2, r0, 36 ; registers 32-34 are EPC,hi,lo
+ @@regloop:
+ lw a0, 0(a1)
+ jal comm_SendReg
+ addi a2, -1
+ bltz v0, @@cancel
+ nop
+ bgtz a2, @@regloop
+ addiu a1, 4
+ lw a0, TCB_cause(k0) ; Send CAUSE register value
+ jal comm_SendReg
+ nop
+ bltz v0, @@cancel
+ nop
+ mfc0 a0, BADVADDR ; Send bad address
+ jal comm_SendReg
+ nop
+ bltz v0, @@cancel
+ nop
+ mfc0 a0, JUMPDEST ; Send JUMPDEST
+ jal comm_SendReg
+ nop
+ bltz v0, @@cancel
+ nop
+ lw a0, SAVE_dcic ; Send last DCIC
+ jal comm_SendReg
+ nop
+ bltz v0, @@cancel
+ nop
+ lw a0, TCB_epc(k0) ; Send opcode
+ nop
+ move v0, a0 ; Don't try to read opcode
+ andi v0, 0x3 ; if address is not aligned or
+ bnez v0, @@skipload ; the debug monitor will crash
+ nop
+ lw a0, 0(a0)
+ @@skipload:
+ jal comm_SendReg
+ nop
+ lw a0, TCB_epc(k0) ; Send opcode following it
+ nop
+ addiu a0, 4
+ move v0, a0 ; Don't try to read opcode
+ andi v0, 0x3 ; if address is not aligned or
+ bnez v0, @@skipload2 ; the debug monitor will crash
+ nop
+ lw a0, 0(a0)
+ @@skipload2:
+ jal comm_SendReg
+ nop
+ @@cancel:
+ lw ra, 0(sp)
+ addiu sp, 4
+ jr ra
+ nop
+
+ ; cmd_getregs
+
+
+cmd_mem: ; = Queries download/upload memory
+ addiu sp, -4
+ sw ra, 0(sp)
+ jal comm_ReadByte ; Get type of operation
+ nop
+ beq v0, 1, @@dowrite
+ nop
+ jal comm_ReadReg ; Get source address value
+ nop
+ beq v0, -1, @@cancel
+ move a0, v0
+ jal comm_ReadReg ; Get number of bytes to get
+ nop
+ beq v0, -1, @@cancel
+ move a1, v0
+ jal comm_BlockSend
+ nop
+ b @@cancel
+ nop
+ @@dowrite:
+ jal comm_ReadReg ; Get target address value
+ nop
+ beq v0, -1, @@cancel
+ move a0, v0
+ jal comm_ReadReg ; Get number of bytes to upload
+ nop
+ beq v0, -1, @@cancel
+ move a1, v0
+ jal comm_BlockRead ; Download block of data
+ nop
+ @@cancel:
+ lw ra, 0(sp)
+ addiu sp, 4
+ jr ra
+ nop
+ ; cmd_mem
+
+
+cmd_getbrk:
+ addiu sp, -4
+ sw ra, 0(sp)
+ la a1, break_entries
+ li a2, MAX_BREAK
+ @@get_bloop:
+ lw a0, DB_BRK_ADDR(a1)
+ addiu v0, r0, -1
+ beq a0, v0, @@end_bloop
+ nop
+ lw a0, DB_BRK_ADDR(a1)
+ jal comm_SendReg
+ nop
+ lw a0, DB_BRK_FLAG(a1)
+ jal comm_SendReg
+ nop
+ lw a0, DB_BRK_LCNT(a1)
+ jal comm_SendReg
+ nop
+ addiu a2, -1
+ bnez a2, @@get_bloop
+ addiu a1, DB_BRK_LEN
+ @@end_bloop:
+ jal comm_SendReg ; Send terminator word
+ addiu a0, r0, -1
+ lw ra, 0(sp)
+ addiu sp, 4
+ jr ra
+ nop
+ ; cmd_getbrk
+
+
+cmd_setreg:
+ addiu sp, -4
+ sw ra, 0(sp)
+ jal comm_ReadByte ; Get register number
+ nop
+ beq v0, -1, @@cancel
+ nop
+ move a1, v0
+ jal comm_ReadReg ; Get new value
+ nop
+ beq v0, -1, @@cancel
+ nop
+ beqz a1, @@skip ; Ignore register 0 changes
+ nop
+ lw k0, PCB_addr ; Retrieve address of current TCB
+ sll a1, 2 ; Multiply reg number by 4
+ lw k0, 0(k0) ; Load the address
+ move a0, v0
+ addiu a1, TCB_r0
+ addu v0, k0, a1 ; k0 must point to TCB
+ sw a0, 0(v0) ; Set new register value
+ @@skip:
+ jal comm_SendByte ; Send acknowledge byte
+ move a0, r0
+ @@cancel:
+ lw ra, 0(sp)
+ addiu sp, 4
+ jr ra
+ nop
+ ; cmd_setreg
+
+
+cmd_setdbrk:
+ addiu sp,-4
+ sw ra,0(sp)
+ jal comm_ReadReg ; Get memory break address
+ nop
+ mtc0 v0,BDA ; Set as data break address
+ jal comm_ReadReg ; Get memory compare mask
+ nop
+ mtc0 v0,BDAM ; Set as data break mask
+ jal comm_ReadByte ; Get memory break flags
+ sb v0,db_dbrkflags
+ jal comm_SendByte ; Send acknowledge byte
+ move a0, r0
+ lw ra,0(sp)
+ addiu sp,4
+ jr ra
+ nop
+ ; cmd_setmembrk
+
+
+getbreakaddr:
+ la a1, break_entries
+ li a2, MAX_BREAK
+ @@flag_loop:
+ lw v0, DB_BRK_ADDR(a1)
+ addiu a2, -1
+ beq v0, a0, @@flag_found
+ nop
+ bnez a2, @@flag_loop
+ addiu a1, DB_BRK_LEN
+ jr ra
+ addiu v0, r0, -1
+ @@flag_found:
+ jr ra
+ move v0, a1
+
+ ; getbreakflags
+
+;
+; = Backs up original opcodes and places break opcodes
+;
+; Destroys: v1
+; Returns: Non-zero if EPC was on a breakpoint (step required)
+;
+set_breakpoints:
+ addiu sp, -16
+ sw a0, 0(sp)
+ sw a1, 4(sp)
+ sw a2, 8(sp)
+ sw a3, 12(sp)
+ la a2, break_entries
+ li a3, MAX_BREAK
+ move a1, r0
+ @@set_loop:
+ lw v1, DB_BRK_ADDR(a2) ; Test if breakpoint is active
+ addiu v0, r0, -1
+ beq v0, v1, @@set_cont ; Skip if not set
+ nop
+ mfc0 v1, EPC
+ lw a0, DB_BRK_ADDR(a2) ; Don't patch break if on EPC
+ nop
+ beq v1, a0, @@on_epc
+ nop
+ lw v1, DB_BRK_INST(a2) ; Check if address already patched
+ addiu v0, r0, -1
+ bne v0, v1, @@set_cont ; Skip if already set
+ nop
+ la v1, break_inst ; Insert the break instruction
+ lw v1, 0(v1)
+ lw v0, 0(a0)
+ sw v1, 0(a0)
+ sw v0, DB_BRK_INST(a2)
+ b @@set_cont ; Continue to next iteration
+ nop
+ @@on_epc:
+ addiu a1, 1 ; Set as flag that EPC points to a
+ @@set_cont: ; breakpoint
+ addiu a3, -1
+ bnez a3, @@set_loop
+ addiu a2, DB_BRK_LEN
+ move v0, a1
+ lw a0, 0(sp)
+ lw a1, 4(sp)
+ lw a2, 8(sp)
+ lw a3, 12(sp)
+ jr ra
+ addiu sp, 16
+
+ ; set_breakpoints
+
+;
+; = Undoes any patched break opcodes
+;
+; Destroys: v0, v1
+; Returns: none
+;
+unset_breakpoints:
+ addiu sp, -12
+ sw a0, 0(sp)
+ sw a2, 4(sp)
+ sw a3, 8(sp)
+ la a2, break_entries
+ li a3, MAX_BREAK
+ @@set_loop:
+ lw v1, DB_BRK_ADDR(a2) ; Test if breakpoint enabled
+ addiu v0, r0, -1
+ beq v0, v1, @@set_cont
+ nop
+ lw v1, DB_BRK_INST(a2) ; Check if address already unpatched
+ lw a0, DB_BRK_ADDR(a2)
+ beq v0, v1, @@set_cont
+ nop
+ sw v1, 0(a0)
+ sw v0, DB_BRK_INST(a2)
+ @@set_cont:
+ addiu a3, -1
+ bnez a3, @@set_loop
+ addiu a2, 16
+ lw a0, 0(sp)
+ lw a2, 4(sp)
+ lw a3, 8(sp)
+ jr ra
+ addiu sp, 12
+
+ ; unset_breakpoints
+
+;
+; = Variables area
+;
+
+break_inst: break 512 ; Break opcode to use with soft
+ ; program breakpoints
+
+rfe_ptr: dw 0 ; Original ReturnFromException()
+ ; function address
+
+runto_addr: dw 0 ; Target address of run-to
+runto_dcic: dw 0 ; DCIC settings for run-to
+
+db_state: db DB_STAT_RUN ; Monitor execution state
+db_runto: db 0 ; Monitor run-to state
+db_step: db 0 ; Program step on-going
+db_brkpass: db 0 ; Break pass flag
+
+db_dbrkflags: db 0 ; Memory breakpoint flags
+
+.align 4
+break_entries:
+
+monitor_end:
+.close
|
