From 702bb601fb5712e2ae962a34b89204c646fe98f5 Mon Sep 17 00:00:00 2001 From: lameguy64 Date: Sat, 4 May 2024 18:55:05 +0800 Subject: Included PSn00bDBG-mk2 monitor and test utility --- indev/psn00bdbg-mk2/doc/commands.txt | 321 ++++++ indev/psn00bdbg-mk2/doc/overview.txt | 140 +++ indev/psn00bdbg-mk2/logo.svg | 135 +++ indev/psn00bdbg-mk2/monitor/build.sh | 6 + indev/psn00bdbg-mk2/monitor/cmdefs.inc | 18 + indev/psn00bdbg-mk2/monitor/makefile | 10 + indev/psn00bdbg-mk2/monitor/monitor.asm | 1343 ++++++++++++++++++++++ indev/psn00bdbg-mk2/monitor/sdkinst.s | 65 ++ indev/psn00bdbg-mk2/monitor/sio.inc | 363 ++++++ indev/psn00bdbg-mk2/monitor/stubinst.s | 185 +++ indev/psn00bdbg-mk2/testutil/cmdefs.h | 36 + indev/psn00bdbg-mk2/testutil/comms.c | 817 +++++++++++++ indev/psn00bdbg-mk2/testutil/exec.c | 413 +++++++ indev/psn00bdbg-mk2/testutil/exec.h | 24 + indev/psn00bdbg-mk2/testutil/main.c | 131 +++ indev/psn00bdbg-mk2/testutil/main.h | 38 + indev/psn00bdbg-mk2/testutil/makefile | 31 + indev/psn00bdbg-mk2/testutil/makefile-linux | 11 + indev/psn00bdbg-mk2/testutil/mips_disassembler.c | 663 +++++++++++ indev/psn00bdbg-mk2/testutil/mips_disassembler.h | 10 + indev/psn00bdbg-mk2/testutil/prompt.c | 518 +++++++++ indev/psn00bdbg-mk2/testutil/pstypes.h | 81 ++ indev/psn00bdbg-mk2/testutil/serial.c | 273 +++++ 23 files changed, 5632 insertions(+) create mode 100644 indev/psn00bdbg-mk2/doc/commands.txt create mode 100644 indev/psn00bdbg-mk2/doc/overview.txt create mode 100644 indev/psn00bdbg-mk2/logo.svg create mode 100755 indev/psn00bdbg-mk2/monitor/build.sh create mode 100644 indev/psn00bdbg-mk2/monitor/cmdefs.inc create mode 100644 indev/psn00bdbg-mk2/monitor/makefile create mode 100644 indev/psn00bdbg-mk2/monitor/monitor.asm create mode 100644 indev/psn00bdbg-mk2/monitor/sdkinst.s create mode 100644 indev/psn00bdbg-mk2/monitor/sio.inc create mode 100644 indev/psn00bdbg-mk2/monitor/stubinst.s create mode 100644 indev/psn00bdbg-mk2/testutil/cmdefs.h create mode 100644 indev/psn00bdbg-mk2/testutil/comms.c create mode 100644 indev/psn00bdbg-mk2/testutil/exec.c create mode 100644 indev/psn00bdbg-mk2/testutil/exec.h create mode 100644 indev/psn00bdbg-mk2/testutil/main.c create mode 100644 indev/psn00bdbg-mk2/testutil/main.h create mode 100644 indev/psn00bdbg-mk2/testutil/makefile create mode 100644 indev/psn00bdbg-mk2/testutil/makefile-linux create mode 100644 indev/psn00bdbg-mk2/testutil/mips_disassembler.c create mode 100644 indev/psn00bdbg-mk2/testutil/mips_disassembler.h create mode 100644 indev/psn00bdbg-mk2/testutil/prompt.c create mode 100644 indev/psn00bdbg-mk2/testutil/pstypes.h create mode 100644 indev/psn00bdbg-mk2/testutil/serial.c diff --git a/indev/psn00bdbg-mk2/doc/commands.txt b/indev/psn00bdbg-mk2/doc/commands.txt new file mode 100644 index 0000000..8169007 --- /dev/null +++ b/indev/psn00bdbg-mk2/doc/commands.txt @@ -0,0 +1,321 @@ +== Debugger commands == + + CMD_DB_GETSTAT (D0h) - Get target status + + Get current status of the target. Issue this command at regular + intervals to monitor for breakpoints or exceptions during program + execution. + + Arguments: + + None. + + Response: + + [u8] Status byte. + 0 - Program stopped + 1 - Breakpoint hit/trace complete + 2 - Unhandled exception (see CMD_DB_GETREGS to + determine cause of exception via CAUSE) + 3 - Running + + + CMD_DB_GETINFO (D1h) - Get debug monitor info string + + Obtain information string of the debug monitor. Normally the + response would be 'PSnDBmk2'. + + Arguments: + + None. + + Response: + + [...] Zero terminated string. Transmission ends when a zero + character has been received. + + + CMD_DB_SETEXEC (D2h) - Execution control + + Control program execution on the target. + + Arguments: + + [u8] Set execution. + 0 - stop target + 1 - step single instruction + 2 - resume execution + + Response: + + [u8] New target status (see CMD_DB_GETSTAT). + + + CMD_DB_RUNTO (D3h) - Run to address + + Resume execution up to the specified program counter address. If the + trace bit is set, and a non-sequential instruction was encountered + (ie. jump or branch instruction) before the target address is reached, + the monitor will break on the destination address of the non-sequential + instruction instead. Intended for run-to cursor and source level trace + operations- so the destination of a function call, conditional line or + a loop while tracing can be determined. + + If a previously defined breakpoint or hard breakpoint (hard-coded break + instruction) was encountered before the target address is reached, the + monitor will break on the breakpoints instead. + + This command automatically resumes execution if the target was in a + stopped state. + + Arguments: + + [u8] Flags. + bit 0 : Trace enable + bit 1-7 : Reserved + + [u32] Target program counter address. + + Response: + + [u8] New execution status (see D0). + + + CMD_DB_SETBRK (D4h) - Set program breakpoint + + Defines a program breakpoint on the specified address. Up to 32 program + breakpoints can be set at a time. + + Breakpoints can only be set to writable memory regions as the + breakpoint mechanism works by patching break instructions into the + program text- a modification that is automatically undone and redone + between monitor interactions to appear invisible to the debugging host. + The patched instruction is also undone and immediately redone when + resuming from a breakpoint so program execution continues normally + between each break iteration. + + FlushCache() is called to clear the instruction cache whenever + breakpoints get patched and unpatched. + + Avoid placing a program breakpoint inside the delay slot of a + non-sequential instruction such as a jump or branch instruction, + otherwise the monitor will not be able to trace or resume execution + from the branch instruction. + + Setting a breakpoint to an already existing breakpoint will update + the existing breakpoint with the specified flags and target count, + as well as clearing the breakpoint counter. + + Arguments: + + [u32] Breakpoint address. + [u16] Flags + Bits 0-1 : Breakpoint mode + 0 - No break, but increment counter. + 1 - Stop execution when encountered. + 2 - Break when counter reaches target value. + 3 - Reserved. + Bits 2-15 : Reserved + [u16] Target Count + + Response: + + [u8] Result byte + 0 - Invalid address + 1 - Breakpoint set + 2 - Breakpoint updated + + + + CMD_DB_CLRBRK (D5h) - Clear program breakpoints + + Clears all previously defined program breakpoints. + + Arguments: + + none + + Response: + + [u8] Status byte (acknowledge) + + + CMD_DB_GETREGS (D6h) - Get registers + + Obtain all 32 general purpose register values and some special + registers- such as the hi/lo registers, EPC (program counter), CAUSE, + BADVADDR, JUMPDEST and DCIC. Two op-codes from EPC are also retrieved + totalling about 42 32-bit register words. + + The value of register k0 is superfluous for it is used immediately by + the jump routines of the exception vectors. Software is not meant to + use the k0/k1 registers as it is used by the kernel's jump vector. + + Refer to the LSI L64360 datasheet for details on cop0 registers SR, + CAUSE, BADVADDR, JUMPDEST, and DCIC. + + Arguments: + + None. + + Response: + + [u32] 42 Processor register values in the following order: + + r0,at,v0,v1,a0,a1,a2,a3, + t0,t1,t2,t3,t4,t5,t6,t7, + s0,s1,s2,s3,s4,s5,s6,s7, + t8,t9,k0,k1,gp,sp,fp,ra, + EPC,hi,lo, + SR,CAUSE,BADVADDR,JUMPDEST,DCIC + opcode(EPC+0),opcode(EPC+4) + + + CMD_DB_MEM (D7h) - Read/write n bytes from address + + Reads or writes n bytes of memory to or from the specified address. + + Arguments: + + [u8] Operation + 0 - read + 1 - write + [u32] Read source address/write destination address + [u32] Number of bytes + + [u8] Write data of n bytes (only when writing to memory) + + Response (only when reading from memory): + + [...] Bytes read of specified length. + + + CMD_DB_WORD (D8h) - Read/write word + + Reads or writes a word from memory. + + The specified memory address must be aligned to two or four bytes + when performing a 16-bit word or 32-bit memory operation respectively. + Failing to do so will cause an in-monitor exception and crash the + target. + + Arguments: + + [u8] Operation + 0 - read + 1 - write + [u8] Word size (0 - [u8], 1 - [u16], 2 - [u32]). + [u32] Memory address. + + [u8/u16/u32] Word value to write (only when writing to memory) + + Response (only when reading from memory): + + [u32] Word value read (always 32-bit regardless of word size). + + + CMD_DB_GETBRK (D9h) - Get all breakpoints + + Returns an array of currently set breakpoints with their flag settings + and counter values. + + Arguments: + + None. + + Response: + + [u32] Breakpoint address. + [u16] Breakpoint flag + [u16] Breakpoint counter target value. + [u16] Breakpoint counter. + ... + + The list is terminated with a breakpoint of address FFFFFFFFh. + + + CMD_DB_SETREG (DAh) - Set register + + Sets any of the register values of the running target. + + This command, coupled with CMD_DB_MEM, can be used to upload and + execute a PS-EXE without a loader as long as the monitor program + is installed and responsive. To do this, stop the target with + CMD_DB_SETEXEC, upload the program text to its target address + using CMD_DB_MEM, then use CMD_DB_SETREG to reset the stack pointer + (sp) to the initial stacktop (801FFFF0h) and PC to the program text's + entrypoint (pc0), clear bits 0-5 of the SR register then execute the + program by resuming with CMD_DB_SETEXEC. + + Arguments: + + [u8] Register number + 0 - r0* 1 - at 2 - v0 3 - v1 + 4 - a0 5 - a1 6 - a2 7 - a3 + 8 - t0 9 - t1 10 - t2 11 - t3 + 12 - t4 13 - t5 14 - t6 15 - t7 + 16 - s0 17 - s1 18 - s2 19 - s3 + 20 - s4 21 - s5 22 - s6 23 - s7 + 24 - t8 25 - t9 26 - k0 27 - k1 + 28 - gp 29 - sp 30 - fp 31 - ra + 32 - EPC 33 - hi 34 - lo 35 - SR + + * Ignored + + [u32] Register value + + Response: + + [u8] 0 - Response byte + + + CMD_DB_MEMBRK (DBh) - Set data access breakpoint registers + + Set breakpoint registers for memory access operations. Only a single + breakpoint address or a range of addresses with the compare mask can + be set with this command. + + The mask register is used to mask which bits between the address of + a memory access operation and the memory breakpoint address are + compared. For example, a mask value of FF000000h compares bits + 31-24 of the addresses- the remaining bits are ignored. + + To deactivate data access breakpoint, simply set the address, mask + and flags to zero. + + Arguments: + + [u32] Memory breakpoint address + [u32] Memory breakpoint compare mask + [u8] Breakpoint flags + Bit 0: Break on memory read + Bit 1: Break on memory write + Bit 2-7: Reserved + + Response: + [u8] 0 - Response byte + + + CMD_REBOOT (A0h) - Reboot console + + While not necessarily a debug command, it is a convenience command + that allows remote reboot of the target. Soft reboot is performed by + simply jumping to the base address of the BIOS ROM segment, which is + equivalent to pressing the reset button on the console. + + + Command numbers DC-DFh are not yet defined and are reserved. + + +== Multiple Program Breakpoints Implementation == + + Because the hardware only supports one software breakpoint at a time, + performing rapid breakpoint cycles on every non-sequential instruction + via the trace bit in DCIC would incur a massive performance penalty and + potentially introduce system instability. Multiple software breakpoints + are instead implemented by patching break instructions to specified + breakpoint locations and the patched instructions are undone when the + original instruction needs to be executed (ie. resume program from break + address). This limits breakpoints to code residing in RAM, but this + method does not incur a performance penalty that can break speed + sensitive code. diff --git a/indev/psn00bdbg-mk2/doc/overview.txt b/indev/psn00bdbg-mk2/doc/overview.txt new file mode 100644 index 0000000..867b610 --- /dev/null +++ b/indev/psn00bdbg-mk2/doc/overview.txt @@ -0,0 +1,140 @@ +== Summary == + + The new PSn00bDBG-Mk2 monitor is a significant revision of the + original, mostly proof-of-concept version from 2018. Written from the + groud-up, such improvements include: + + * Abandoned event-based messages system in favor of a much simpler + command-acknowledge protocol and polling-based status monitoring. + + * Communication functions are consolidated to a single include file, + giving provision to replace communication routines by swapping out + this include file. + + * Improved instruction step operation, with correct following of + branching and non-sequential instructions. + + * Enhanced run-to-address with optional break on branching or non- + sequential instructions- ideal for source-level trace operations. + + * Support for up to 32 program breakpoints with counters. + + * Block memory read and write- can be used to upload program text + directly through the debug monitor (similar to that of official + development systems). + + * Utilizes kernel stack instead of user stack- preserving the latter + during debugging operations. + + The new monitor is also better integrated into the kernel space- + taking advantage of TCB structures and hooking to more appropriate vectors + of both BIOS and kernel, reducing code size and improving system stability + when compared to the old debugger. The pollhost() macro (break 1024) is + also supported, and can be used to keep the debug monitor serviced for + instances where interrupts are disabled. + + Unlike the old proof-of-concept debugger, a specifically written debugger + is not provided. Instead this debug monitor is to be used with GNU GDB as + the debugger, with a simple protocol middle-man that serves to translate + between the GDB remote debugging protocol and PSn00bDBG-Mk2 command set. + + +== Installation and Operation == + + Three files are provided after assembling the debug monitor; patchcode.bin, + patchinst.bin and patch.bin. The former file is the assembled debug monitor, + while the latter file is a small software routine whose purpose is to + install and patch the debug monitor (patchcode.bin) into the kernel. + Finally, patch.bin are the two files combined, with patchcode.bin conca- + tenated over patchinst.bin as a payload. + + The patch.bin file is intended for use with LITELOAD and n00bROM's 'Patch + Binary' loader- this function simply downloads the binary file to a fixed + address (80010000h), flush the instruction cache with FlushCache() and calls + it as a function routine. The binary would perform a call-return jump + to return to the loader after performing kernel patches, so LITELOAD or + n00bROM can resume for downloading a PS-EXE next. + + The monitor code resides in the reserved kernel memory region between + address 8000C000h to 8000DFFFh (8KBytes) and is generally unused by + most if not all software titles, with exception of unlicensed CD + based cheat software such as Gameshark CD. + + Once copied, the monitor is patched into the kernel with the following + operations: + + * Exception vector on 80000080h is modified to preserved the contents + of the cop0 DCIC register to a predetermined address, before the jump + to the BIOS exception hander with DCIC cleared. + + * The debug exception vector at 80000040h is patched with a jump + to the debug monitor for trapping exceptions pertaining to debugging + (ie. trace, break on address). The routine also preserves k0, k1 and + cop0 DCIC registers to predetermined addresses for use by the monitor + later. + + * The monitor is hooked to BIOS functions ReturnFromException() (B(57h)) + and SysErrUnresolvedException() (A(40h)) by patching the BIOS function + tables. The original address of ReturnFromException() is preserved in + the monitor for use as a return address after it has finished servicing + itself for pending commands. + + Since the debug monitor is hooked to the exception handling subsystem, the + monitor operates like a background task over the current program and can + carry out debug operations completely transparently to the main program. + By design, the monitor depends on constant interrupts (such as Vblank) to + be able to service incoming commands- so when a program disables interrupts + and enters an infinite loop, the debugger is no longer able to respond to + commands. + + To mitigate this a special break instruction provided in the pollhost() + macro (break 1024) can be used inside loops suspected for locking up the + program. The debug monitor will catch the breakpoint exception of this + instruction and ignore it by resuming execution, but this will allow the + monitor to service itself without interrupts via break exceptions. + + Alternatively setting a program breakpoint that only counts iterations + can be used over pollhost() to keep the debug monitor serviced. + + +== Improved Single Step == + + The old debugger implemented instruction step by simply setting the Program + Break address from the Program Counter address, adjusted to be just an inst- + ruction ahead of the current Program Counter. While the same method is also + employed in this new debugger for instruction step, this method does not + work directly for non-sequential, or branching, instructions. + + The old debugger would inefficiently decipher the non-sequential instruction + and manually determine whether a branch is taken or not just to predict where + the next program counter is going to be on- despite cop0 providing debug + registers specifically for this purpose. This method, of course, is + inefficient, and wastes a lot of code space just for the branch interpreter. + + The new PSn00bDBG-Mk2 debugger makes use of the debug registers and eliminates + the need for code to interpret non-sequential instructions, freeing up code + space for other features. + + +== Multiple Program Breakpoints == + + PSn00bDBG-Mk2 supports up to 32 simultaneous program breakpoints with + execution counters and optional conditions. These breakpoints are + implemented by patching break instructions to designated points of the + program text- not without backing up the original instruction of + course. + + When one of these breakpoints is encountered by the Program Counter, a break + exception occurs and the debug monitor halts program execution. Before debu + operations are carried out the break instruction is restored to the original + instruction, both so the breakpoint patches would not be visible from the + debugger's perspective, but also to restore the original program logic when + program execution is resumed from a break. The patches are re-applied when + resuming execution, and is done in such a way that the patches are performed + transparently from the debugging host. + + This allows the debugger to support up to 32 program breakpoints with + virtually zero impact on processing performance. However, because it + relies on patching the program text to place break instructions, this does + not work on code located outside of RAM- such as the BIOS ROM or an + expansion cartridge ROM. diff --git a/indev/psn00bdbg-mk2/logo.svg b/indev/psn00bdbg-mk2/logo.svg new file mode 100644 index 0000000..a24a872 --- /dev/null +++ b/indev/psn00bdbg-mk2/logo.svg @@ -0,0 +1,135 @@ + + + + + PSn00bDBG mk2 Logo + + + + + + image/svg+xml + + PSn00bDBG mk2 Logo + + + Lameguy64 + + + + + PSn00bSDK Project / Meido-Tek Productions + + + + + + + + + + + + DBG + mk2 + + diff --git a/indev/psn00bdbg-mk2/monitor/build.sh b/indev/psn00bdbg-mk2/monitor/build.sh new file mode 100755 index 0000000..1ba1fe0 --- /dev/null +++ b/indev/psn00bdbg-mk2/monitor/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +armips -temp monitor.lst monitor.asm + +cat patchinst.bin >patch.bin +cat patchcode.bin >>patch.bin diff --git a/indev/psn00bdbg-mk2/monitor/cmdefs.inc b/indev/psn00bdbg-mk2/monitor/cmdefs.inc new file mode 100644 index 0000000..f32d334 --- /dev/null +++ b/indev/psn00bdbg-mk2/monitor/cmdefs.inc @@ -0,0 +1,18 @@ +CMD_REBOOT equ 0xA0 + +; **************************** +; Debug monitor commands +; (see commands.txt) +; **************************** + +CMD_DB_GETSTAT equ 0xD0 ; Get status about the debug monitor +CMD_DB_GETINFO equ 0xD1 ; Get info about debug monitor +CMD_DB_SETEXEC equ 0xD2 ; Set program execution mode +CMD_DB_RUNTO equ 0xD3 ; Run or trace to specified address +CMD_DB_SETBRK equ 0xD4 ; Set a program breakpoint +CMD_DB_CLRBRK equ 0xD5 ; Clear program breakpoints +CMD_DB_GETREGS equ 0xD6 ; Get processor registers (including debug regs) +CMD_DB_MEM equ 0xD7 ; Get region of memory +CMD_DB_WORD equ 0xD8 ; Get word of memory +CMD_DB_GETBRK equ 0xD9 ; Get currently set breakpoints +CMD_DB_MEMBRK equ 0xDB ; Set memory access breakpoint registers diff --git a/indev/psn00bdbg-mk2/monitor/makefile b/indev/psn00bdbg-mk2/monitor/makefile new file mode 100644 index 0000000..077fc55 --- /dev/null +++ b/indev/psn00bdbg-mk2/monitor/makefile @@ -0,0 +1,10 @@ +patch.bin: patchinst.bin + copy /B patchinst.bin+patchcode.bin patch.bin + +patchinst.bin: monitor.asm cmdefs.inc sio.inc + armips -temp monitor.lst monitor.asm + +clean: .SYMBOLIC + del patch.bin + del patchinst.bin + del patchcode.bin 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 diff --git a/indev/psn00bdbg-mk2/monitor/sdkinst.s b/indev/psn00bdbg-mk2/monitor/sdkinst.s new file mode 100644 index 0000000..6d20624 --- /dev/null +++ b/indev/psn00bdbg-mk2/monitor/sdkinst.s @@ -0,0 +1,65 @@ +# +# To use this installer properly, the program text must be compiled to +# load at a higher address such as 0x80012000. +# +# Then call the function early in your program: +# +# void install_monitor(void); +# +# ... +# +# int main(int argc, const char *argv[]) +# { +# ResetGraph(0); +# +# EnterCriticalSection(); +# install_monitor(); +# ExitCriticalSection(); +# ... +# +.set noreorder + +.section .text + +.global _install_monitor +.type _install_monitor, @function +_install_monitor: # = Patch installer routine + + addiu $sp, -4 + sw $ra, 0($sp) + + la $a0, debug_payload + la $a1, debug_payload_end + la $a2, debug_payload + subu $a1, $a2 + jal .Lcopymem + lui $a2, 0xA001 + + lui $a0, 0x8001 + jalr $a0 + nop + + lw $ra, 0($sp) + addiu $sp, 4 + jr $ra + nop + + .Lcopymem: # installer's copy routine + + addiu $a1, -4 + lw $v0, 0($a0) + addiu $a0, 4 + sw $v0, 0($a2) + bgtz $a1, .Lcopymem + addiu $a2, 4 + jr $ra + nop + + +.section .data + +debug_payload: + + .incbin "patch.bin" + +debug_payload_end: diff --git a/indev/psn00bdbg-mk2/monitor/sio.inc b/indev/psn00bdbg-mk2/monitor/sio.inc new file mode 100644 index 0000000..90c3760 --- /dev/null +++ b/indev/psn00bdbg-mk2/monitor/sio.inc @@ -0,0 +1,363 @@ +; Experimental PSn00bDEBUG Mk2 debug monitor by John Wilbert Villamor +; Copyright 2020 Meido-Tek Productions (Lameguy64) +; +; These are the communication routines for the monitor. This file can be +; swapped out with a different set of routines to adapt it for other +; interfaces. + +SIO_TXRX_REG_A equ 0x1F801050 ; Serial I/O port absolute addresses +SIO_MODE_REG_A equ 0x1F801058 +SIO_CTRL_REG_A equ 0x1F80105A +SIO_BAUD_REG_A equ 0x1F80105E +SIO_STAT_REG_A equ 0x1F801054 + +SIO_BAUD equ 0x12 ; Baud rate constant + +; = Function definitions of communication routines +; +comm_ReadByte equ sioReadByte +comm_ReadReg equ sioReadReg +comm_BlockRead equ sioBlockRead +comm_SendByte equ sioSendByte +comm_SendReg equ sioSendReg +comm_BlockSend equ sioBlockSend +comm_SendStr equ sioSendStr +comm_DisableListen equ sioDisableListen +comm_EnableListen equ sioEnableListen +comm_GetStatus equ sioGetStatus + +; sioReadByte - Read a byte from the serial port. +; +; Arguments: none +; +; Destroys: v0, v1 +; +; Returns: v0 - byte read +; +sioReadByte: + la v1, SIO_CTRL_REG_A ; Turn on RTS + lhu v0, 0(v1) + nop + ori v0, 0x22 + sh v0, 0(v1) + @@rx_wait: + la v0, SIO_STAT_REG_A ; Read status register + lw v0, 0(v0) + nop + andi v0, 0x80 ; Check DSR status + beqz v0, @@cancel + addi v0, r0, -1 + la v0, SIO_STAT_REG_A ; Read value register + lw v0, 0(v0) + nop + andi v0, 0x2 + beqz v0, @@rx_wait + nop + la v0, SIO_TXRX_REG_A + lbu v0, 0(v0) + @@cancel: + addiu sp, -4 + sw v0, 0(sp) + la v1, SIO_CTRL_REG_A ; Turn off RTS + lhu v0, 0(v1) + nop + xori v0, 0x20 + sh v0, 0(v1) + lw v0, 0(sp) + jr ra + addiu sp, 4 + ; sioReadByte + + +; sioSendByte - Send a byte to the serial port +; +; Input: a0 - byte to send to serial. +; +; Destroys: v0 +; +; Returns: 0 on success, -1 on drop +; +sioSendByte: + @@cts_wait: ; Wait for Clear-to-Send signal + la v0, SIO_STAT_REG_A ; Check DSR line + lhu v0, 0(v0) + nop + andi v0, 0x80 + beqz v0, @@cancel + addi v0, r0, -1 + la v0, SIO_STAT_REG_A ; Wait for CTS + lhu v0, 0(v0) + nop + andi v0, 0x100 + beqz v0, @@cts_wait + nop + @@tx_ready: ; Wait for TX to become ready + la v0, SIO_STAT_REG_A ; Check DSR line + lhu v0, 0(v0) + nop + andi v0, 0x80 + beqz v0, @@cancel + addi v0, r0, -1 + la v0, SIO_STAT_REG_A + lhu v0, 0(v0) + nop + andi v0, 0x1 + beqz v0, @@tx_ready + nop + la v0, SIO_TXRX_REG_A ; Send byte + sb a0, 0(v0) + @@tx_done: ; Wait for TX to finish + la v0, SIO_STAT_REG_A ; Check DSR line + lhu v0, 0(v0) + nop + andi v0, 0x80 + beqz v0, @@cancel + addi v0, r0, -1 + la v0, SIO_STAT_REG_A + lhu v0, 0(v0) + nop + andi v0, 0x4 + beqz v0, @@tx_done + nop + move v0, r0 + @@cancel: + jr ra ; Return + nop + ; sioSendByte + + +; sioBlockSend - Send a block of data to the serial port +; +; Arguments: +; a0 - Pointer to start sending data from +; a1 - Number of bytes to send +; +sioBlockSend: + addiu sp, -4 + sw ra, 0(sp) + move a2, a0 + @@sendloop: + lbu a0, 0(a2) ; Read and send a byte + jal sioSendByte + addiu a2, 1 + beq v0, -1, @@cancel ; Cancel if connection dropped + addiu a1, -1 + bgtz a1, @@sendloop + nop + @@cancel: + lw ra, 0(sp) + addiu sp, 4 + jr ra + nop + ; sioBlockSend + + +; sioBlockRead - Read a block of data from the serial port +; +; Arguments: +; a0 - Pointer to store read data to +; a1 - Number of bytes to read +; +sioBlockRead: + addiu sp, -4 + sw ra, 0(sp) + @@readloop: + jal sioReadByte + nop + beq v0, -1, @@cancel ; Check if comms got terminated + addiu a1, -1 + sb v0, 0(a0) + bgtz a1, @@readloop + addiu a0, 1 + @@cancel: + lw ra, 0(sp) + addiu sp, 4 + jr ra + nop + ; sioBlockRead + + +; sioSendStr - Send a string to serial port +; +; Arguments: a0 - pointer to string +; +; Destroys: a0 and a1 +; +; Returns: none +; +sioSendStr: + addiu sp, -4 + sw ra, 0(sp) + move a1, a0 + @@send_loop: + lbu a0, 0(a1) ; Get byte + nop + jal sioSendByte ; Send byte + addiu a1, 1 + bltz v0, @@send_end ; End transmission if DSR was dropped + nop + bnez a0, @@send_loop ; Loop until terminator is sent + nop + @@send_end: + lw ra, 0(sp) + addiu sp, 4 + jr ra + nop + ; sioSendStr + + +; sioSendStr - Send 4 bytes of a 32-bit register +; +; Input: a0 - Value to send. +; +; Return: none. +; +sioSendReg: + addiu sp, -24 + sw ra, 0(sp) + sw v0, 4(sp) + sw v1, 8(sp) + sw a0, 12(sp) + sw a1, 16(sp) + sw a2, 20(sp) + move a1, zero + move a2, a0 + @@write_loop: + andi a0, a2, 0xFF + jal sioSendByte + srl a2, 8 + bltz v0, @@cancel + addiu v0, r0, -1 + li at, 3 + bne a1, at, @@write_loop + addiu a1, 1 + @@cancel: + lw ra, 0(sp) ; Restore stack and return + lw v0, 4(sp) + lw v1, 8(sp) + lw a0, 12(sp) + lw a1, 16(sp) + lw a2, 20(sp) + jr ra + addiu sp, 24 + ; sioSendReg + + +; sioReadReg - Receives 4 bytes to a register +; +; Arguments: none. +; +; Return: v0 - Received word. +; +sioReadReg: + addiu sp, -16 + sw ra, 0(sp) ; Save stuff to stack + sw a0, 4(sp) + sw a1, 8(sp) + sw a2, 12(sp) + move a0, r0 ; Clear registers + move a1, r0 + move a2, r0 + @@loop: + jal sioReadByte ; Receive a byte + nop + bltz v0, @@cancel ; Cancel if return value is negative + sllv v0, a0 ; Shift return value to its respective + addiu a0, 8 ; bit location in the register + slti at, a0, 32 + bnez at, @@loop + or a2, v0 ; Merge it to the register + b @@done + move v0, a2 + @@cancel: + addiu v0, r0, -1 ; Set return value to -1 if canceled + @@done: + lw ra, 0(sp) + lw a0, 4(sp) + lw a1, 8(sp) + lw a2, 12(sp) + jr ra + addiu sp, 16 + ; sioReadReg + + +; sioEnableListen - Enable asynchronous listening of the serial port +; +; This function simply enables SIO interrupts and sets RTS and DTR high to +; allow debug commands to be issued to the target console. This is executed +; on every interrupt to make sure the SIO interface can receive comamnds at +; any time, and when the debug monitor leaves query mode. +; +; The monitor leaves query mode with the following commands: +; - When the monitor completes a command and resumes execution (if exec +; mode is set to run). +; - When the monitor is instructed to continue execution from a paused state. +; - When a trace or run-to operation is issued while the target is stopped. +; +; Arguments: none +; +; Destroys: v0 and v1 +; +; Returns: none +; +sioEnableListen: + la v1, SIO_CTRL_REG_A ; Enable RTS and DTR so PC can send + lhu v0, 0(v1) + nop + ori v0, 0x22 + sh v0, 0(v1) + jr ra + nop + ; sioEnableListen + + +; sioDisableListen - Disable asynchronous listening of the serial port +; +; This function simply disables SIO interrupts and sets RTS and DTR low +; to allow handshaking. This is executed when the debug monitor enters query +; mode, when it has received a command, told to stop the target or when a +; break/trace/unhandled exception occurs. +; +; Arguments: none +; +; Destroys: v0 and v1 +; +; Returns: none +; +sioDisableListen: + la v1, SIO_CTRL_REG_A + lhu v0, 0(v1) + nop + andi v0, 0xF ; Disable interrupts and RTS + ori v0, 0x10 ; Acknowledge serial just in case + sh v0, 0(v1) + jr ra + nop + ; sioDisableListen + + +; sioGetStatus - Get status if there's any pending data in the serial port +; +; This function checks if the SIO interface has connection with the host +; system (when CTS is set) and if there's data pending in the RX buffer of +; the SIO interface. This function is executed on every interrupt, to check +; for any commands pending in the SIO interface. +; +; Arguments: none +; Destroys: v0 and v1 +; Returns: Zero if no data pending, non-zero if there's data pending. +; +sioGetStatus: + la v0, SIO_STAT_REG_A ; Check serial status + lhu v0, 0(v0) + nop + andi v1, v0, 0x80 ; Check if CTS is set + beqz v1, @@nodata + andi v0, 0x2 + beqz v0, @@nodata ; Check if rx buffer is filled + nop + jr ra + li v0, 1 + @@nodata: + jr ra + move v0, r0 diff --git a/indev/psn00bdbg-mk2/monitor/stubinst.s b/indev/psn00bdbg-mk2/monitor/stubinst.s new file mode 100644 index 0000000..54bc8b9 --- /dev/null +++ b/indev/psn00bdbg-mk2/monitor/stubinst.s @@ -0,0 +1,185 @@ +# +# C-callable and linkable version of 'patchinst' portion of monitor.asm +# +# Assemble with: +# mipsel-none-elf-gcc -march=r3000 -c stubinst.s -o stubinst.o +# +# Then call the function early in your program: +# +# void mk2_InstallMonitor(void); +# +# ... +# +# int main(int argc, const char *argv[]) +# { +# ResetGraph(0); +# +# EnterCriticalSection(); +# mk2_InstallMonitor(); +# ExitCriticalSection(); +# ... +# +.set noreorder + +# +# These constants must reflect those in monitor.asm +# +.set MONADDR, 0xC000 +.set MAX_BREAK, 32 + +.set SAVE_mode, 0x30 +.set SAVE_tmode, 0x31 +.set SAVE_k0, 0x34 +.set SAVE_k1, 0x38 +.set SAVE_dcic, 0x3C + +.set DB_BRK_FLAG, 0 +.set DB_BRK_ADDR, 4 +.set DB_BRK_INST, 8 +.set DB_BRK_LCNT, 12 +.set DB_BRK_HCNT, 14 +.set DB_BRK_LEN, 16 + +.set DCIC, $7 + +.section .text + +.global _mk2_InstallMonitor +.type _mk2_InstallMonitor, @function +_mk2_InstallMonitor: + + addiu $sp, -4 + sw $ra, 0($sp) + li $a0, .Lbreakhook # Install breakpoint vector hook + li $a1, (.Lbreakhook_end-.Lbreakhook)+4 + li $a2, 0xA0000040 + jal .Lcopymem + nop + li $v0, 0x200 # Hook monitor entrypoint to + li $v1, 0x40 # SysErrUnresolvedException() slot + sll $v1, 2 + addu $v0, $v1 + la $v1, .Lpayload # Get entrypoint address + lw $v1, 0($v1) + nop + sw $v1, 0($v0) + addiu $t2, $0, 0xB0 # GetB0Table() + jalr $t2 + addiu $t1, $0, 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, .Lpayload + lw $v1, 0($v1) + nop + beq $v0, $v1, .Lnoinstall # Don't install if hooked already + nop + addiu $sp, -4 + sw $a0, 0($sp) + li $a0, .Lpayload # Load monitor code into target + li $a1, (.Lpayload_end-.Lpayload)+4 # address + lui $a2, 0xA000 # write via uncached segment + jal .Lcopymem + ori $a2, MONADDR + lw $a0, 0($sp) + addiu $sp, 4 + lw $v0, 0($a0) + la $v1, .Lpayload + lw $v1, 4($v1) + nop + sw $v0, 0($v1) + la $v0, .Lpayload # Set new address to table + lw $v0, 0($v0) + nop + sw $v0, 0($a0) + li $a0, 0x80 # Move existing exception vector + li $a2, 0x90 # jump to prepend patch + jal .Lcopymem + li $a1, 16 + la $a0, .Lexceptpatch # Patch the exception vector for + li $a2, 0x80 # trace to work properly + jal .Lcopymem + li $a1, 16 + addiu $t2, $0, 0xA0 # FlushCache() just to make sure + jalr $t2 + addiu $t1, $0, 0x44 + jal .Linit_breakpoints + nop + .Lnoinstall: + lw $ra, 0($sp) # Return to caller, debugger + addiu $sp, 4 # already installed + jr $ra + nop + + # patchinst + +# +# = installer's copy routine +# +.Lcopymem: + addiu $a1, -4 + lw $v0, 0($a0) + addiu $a0, 4 + sw $v0, 0($a2) + bgtz $a1, .Lcopymem + addiu $a2, 4 + jr $ra + nop + + # copymem + +# +# = Initializes breakpoint list +# +.Linit_breakpoints: + la $a2, .Lpayload # Get address of breakpoint table + lw $a2, 8($a2) + li $a3, MAX_BREAK + addiu $v0, $0, -1 + .Lclear_loop: + sw $v0, DB_BRK_INST($a2) + sw $v0, DB_BRK_ADDR($a2) + sw $0 , DB_BRK_FLAG($a2) + addiu $a3, -1 + bnez $a3, .Lclear_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 +# +.Lbreakhook: + sw $k0, SAVE_k0($0) # Save K0 and K1 registers + mfc0 $k0, DCIC # Save DCIC + sw $k1, SAVE_k1($0) + mtc0 $0, DCIC # Clear DCIC in case trace is still + sw $k0, SAVE_dcic($0) # effective + la $k0, breakhandler # Jump to break vector handler + jr $k0 + nop +.Lbreakhook_end: + +# +# = Exception vector patch +# +# This is prepended to the exception vector jump at address 80h +# +.Lexceptpatch: + mfc0 $k0, DCIC # backup DCIC value + nop + mtc0 $0 , DCIC # clear DCIC + sw $k0, SAVE_dcic($0) + +# +# = Payload data +# +.section .data + +.Lpayload: + .incbin "patchcode.bin" +.Lpayload_end: diff --git a/indev/psn00bdbg-mk2/testutil/cmdefs.h b/indev/psn00bdbg-mk2/testutil/cmdefs.h new file mode 100644 index 0000000..82e57e9 --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/cmdefs.h @@ -0,0 +1,36 @@ +#ifndef _CMDEFS_H +#define _CMDEFS_H + +/* status values */ + +#define DB_STAT_STOP 0 /* target is stopped */ +#define DB_STAT_BREAK 1 /* breakpoint (not by break opcodes ) */ +#define DB_STAT_EXCEPT 2 /* unhandled exception */ +#define DB_STAT_RUN 3 /* target is running */ + +/* general commands */ + +#define CMD_REBOOT 0xA0 /* soft reboot console */ + +/* debug and execution commands */ + +#define CMD_DB_GETSTAT 0xD0 /* get target status */ +#define CMD_DB_GETINFO 0xD1 /* get debug monitor info */ +#define CMD_DB_SETEXEC 0xD2 /* execution control */ +#define CMD_DB_RUNTO 0xD3 /* run to address */ +#define CMD_DB_SETBRK 0xD4 /* Set a program breakpoint */ +#define CMD_DB_CLRBRK 0xD5 /* Clear all program breakpoints */ +#define CMD_DB_GETREGS 0xD6 /* get registers */ +#define CMD_DB_GETMEM 0xD7 /* get memory */ +#define CMD_DB_WORD 0xD8 /* get a word value */ +#define CMD_DB_GETBRK 0xD9 /* get current breakpoints */ +#define CMD_DB_SETREG 0xDA /* set processor registers */ +#define CMD_DB_SETBDREG 0xDB /* set data breakpoint registers */ + +/* exec control values for CMD_DB_SETEXEC */ + +#define CMD_EXEC_STOP 0 +#define CMD_EXEC_STEP 1 +#define CMD_EXEC_RESUME 2 + +#endif /* _CMDEFS_H */ diff --git a/indev/psn00bdbg-mk2/testutil/comms.c b/indev/psn00bdbg-mk2/testutil/comms.c new file mode 100644 index 0000000..46ec1f3 --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/comms.c @@ -0,0 +1,817 @@ +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif /* _WIN32 */ +#include "cmdefs.h" +#include "main.h" +#include "mips_disassembler.h" + +#ifdef _WIN32 /* platform specific macros */ + +#define SIO_DELAY() ( Sleep( 10 ) ) + +#else + +#define SIO_DELAY() ( usleep( 10000 ) ) + +#endif /* _WIN32 */ + +static const char *msg_sendfail = "Failed to send command.\n"; +static const char *msg_recfail = "Error on receive.\n"; +static const char *msg_noack = "No acknowledgement from target.\n"; + +static char *regnames_param[] = +{ + "r0", "at", "v0", "v1", + "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", + "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", + "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", + "gp", "sp", "fp", "ra", + "pc", "hi", "lo", "sr" +}; + +int dbGetInfo( void ) +{ + int i; + + if( commWriteByte( CMD_DB_GETINFO ) > 0 ) + { + printf( "Debug monitor info string: " ); + while( i = commReadByte() ) + { + if( i < 0 ) + { + puts( msg_recfail ); + return( 1 ); + } + putchar( i ); + } + putchar( '\n' ); + } + else + { + puts( msg_sendfail ); + return( 1 ); + } + + return( 0 ); + +} /* dbGetInfo */ + +void dbGetStatus( void ) +{ + int i; + + if( commWriteByte( CMD_DB_GETSTAT ) > 0 ) + { + if( (i = commReadByte()) < 0 ) + { + puts( msg_recfail ); + } + else + { + printf( "Target status: %d (", i ); + + switch( i ) + { + case DB_STAT_STOP: + printf( "stopped" ); + break; + case DB_STAT_BREAK: + printf( "breakpoint" ); + break; + case DB_STAT_EXCEPT: + printf( "unhandled exception" ); + break; + case DB_STAT_RUN: + printf( "running" ); + break; + default: + printf( "undefined" ); + } + printf( ")\n" ); + } + } + else + { + puts( msg_sendfail ); + } + +} /* dbGetStatus */ + +int dbGetStatusPoll(void) +{ + int i; + + if( commWriteByte( CMD_DB_GETSTAT ) > 0 ) + { + if( (i = commReadByte()) < 0 ) + { + return(-1); + } + } + else + { + return(-1); + } + + return(i); + +} /* dbGetStatus */ + +int dbSetExec( int exec ) +{ + if( commWriteByte( CMD_DB_SETEXEC ) < 0 ) /* send command */ + { + puts( msg_sendfail ); + return(1); + } + + if( commWriteByte( exec) < 0 ) /* send exec value */ + { + puts( msg_sendfail ); + return(1); + } + + if( commReadByte() < 0 ) /* check for acknowledgment */ + { + puts( msg_noack ); + return(1); + } + + return(0); + +} /* dbSetExec */ + +void dbRunTo(unsigned int addr, int flag) +{ + if( commWriteByte(CMD_DB_RUNTO) < 0 ) /* send command */ + { + puts( msg_sendfail ); + return; + } + + if( commWriteByte(flag) < 0 ) /* send flag */ + { + puts( msg_sendfail ); + return; + } + + if( commWriteBytes( &addr, 4 ) < 4 ) /* send runto address */ + { + puts( msg_sendfail ); + return; + } + + if( commReadByte() < 0 ) /* check for acknowledgment */ + { + puts( msg_noack ); + return; + } + +} /* dbRunTo */ + +void dbSetBreak(unsigned int addr, unsigned int flag, int count) +{ + int ret; + + if( commWriteByte(CMD_DB_SETBRK) < 0 ) /* send command */ + { + puts(msg_sendfail); + return; + } + + if( commWriteBytes(&addr, 4) < 4 ) /* send breakpoint address */ + { + puts(msg_sendfail); + return; + } + + SIO_DELAY(); /* delay otherwise last byte doesn't get sent somehow */ + + ret = (flag&0xFF) | (count<<16); /* send flag value */ + if( commWriteBytes(&ret, 4) < 4 ) + { + puts(msg_sendfail); + return; + } + + SIO_DELAY(); + + if( (ret = commReadByte()) < 0 ) /* check for acknowledgment */ + { + puts( msg_noack ); + return; + } + + if( ret == 0 ) + { + printf("Bad breakpoint address.\n"); + } + else if( ret == 1 ) + { + printf("Breakpoint set/update.\n"); + } + else + { + printf("Unknown response value: %d\n", ret); + } + +} /* dbSetBreak */ + +void dbClearBreaks() +{ + int ret; + + if( commWriteByte(CMD_DB_CLRBRK) < 0 ) /* send command */ + { + puts(msg_sendfail); + return; + } + + if( (ret = commReadByte()) < 0 ) /* check for acknowledgment */ + { + puts( msg_noack ); + return; + } + +} /* dbClearBreaks */ + +void dbReboot( void ) +{ + if( commWriteByte( CMD_REBOOT ) < 0 ) /* send command */ + { + puts( msg_sendfail ); + return; + } + +} /* dbReboot */ + +void dbUploadMemFile( unsigned int addr, const char *infile, size_t len ) +{ + FILE *fp; + int i,progress,olen; + unsigned char buffer[100]; + size_t file_sz; + + fp = fopen( infile, "rb" ); + + if( !fp ) + { + printf( "File not found.\n" ); + return; + } + + fseek( fp, 0, SEEK_END ); /* determine file size */ + file_sz = ftell( fp ); + fseek( fp, 0, SEEK_SET ); + + if( len > file_sz ) + len = file_sz; + + if( commWriteByte( CMD_DB_GETMEM ) < 0 ) /* send getmem command */ + { + puts( msg_sendfail ); + fclose( fp ); + return; + } + + if( commWriteByte( 1 ) < 1 ) /* send operation */ + { + puts( msg_sendfail ); + fclose( fp ); + return; + } + + SIO_DELAY(); + + if( commWriteBytes( &addr, 4 ) < 4 ) /* send write address */ + { + puts( msg_sendfail ); + fclose( fp ); + return; + } + + SIO_DELAY(); + + if( commWriteBytes( &len, 4 ) < 4 ) /* send write length */ + { + puts( msg_sendfail ); + fclose( fp ); + return; + } + + putchar( ' ' ); /* draw a progress bar */ + for( i=0; i<70; i++ ) + { + putchar( '.' ); + } + putchar( ']' ); + putchar( '\r' ); + putchar( '[' ); + fflush( stdout ); + + progress = 0; + olen = len; + + while( len > 0 ) /* send the incoming bytes */ + { + i = len; + if( i > 10 ) + i = 10; + + i = fread( buffer, 1, i, fp ); + if( i <= 0 ) + { + break; + } + + if( ( i = commWriteBytes( buffer, i ) ) < 0 ) + { + puts( msg_recfail ); + break; + } + len -= i; + + i = (70*((1024*((olen-len)>>2))/ /* draw out progress */ + (olen>>2)))/1024; + if( i > progress ) + { + progress = i; + putchar( '#' ); + fflush( stdout ); + } + } + + for( i=0; i<(70-progress); i++ ) + putchar( '#' ); + + putchar( '\n' ); + + fclose( fp ); + +} /* dbUploadMemFile */ + +int dbUploadMem( unsigned int addr, char *buff, size_t len ) +{ + int i,progress,olen; + + if( commWriteByte( CMD_DB_GETMEM ) < 0 ) /* send getmem command */ + { + puts( msg_sendfail ); + return(1); + } + + if( commWriteByte( 1 ) < 1 ) /* send operation */ + { + puts( msg_sendfail ); + return(1); + } + + SIO_DELAY(); + + if( commWriteBytes( &addr, 4 ) < 4 ) /* send write address */ + { + puts( msg_sendfail ); + return(1); + } + + SIO_DELAY(); + + if( commWriteBytes( &len, 4 ) < 4 ) /* send write length */ + { + puts( msg_sendfail ); + return(1); + } + + putchar( ' ' ); /* draw a progress bar */ + for( i=0; i<70; i++ ) + { + putchar( '.' ); + } + putchar( ']' ); + putchar( '\r' ); + putchar( '[' ); + fflush( stdout ); + + progress = 0; + olen = len; + + while( len > 0 ) /* send the incoming bytes */ + { + i = len; + if( i > 10 ) + i = 10; + + if( ( i = commWriteBytes( buff, i ) ) < 0 ) + { + puts( msg_recfail ); + break; + } + len -= i; + buff += i; + + i = (70*((1024*((olen-len)>>2))/ /* draw out progress */ + (olen>>2)))/1024; + if( i > progress ) + { + progress = i; + putchar( '#' ); + fflush( stdout ); + } + } + + for( i=0; i<(70-progress); i++ ) + putchar( '#' ); + + putchar( '\n' ); + + return(0); + +} /* dbUploadMem */ + +void dbGetMem( unsigned int addr, int len, const char *outfile ) +{ + FILE *fp; + int i,progress,olen; + unsigned char buffer[100]; + + fp = fopen( outfile, "wb" ); + + if( !fp ) + { + printf( "Cannot create output file.\n" ); + return; + } + + if( commWriteByte( CMD_DB_GETMEM ) < 0 ) /* send getmem command */ + { + puts( msg_sendfail ); + fclose( fp ); + return; + } + + if( commWriteByte( 0 ) < 1 ) /* send operation */ + { + puts( msg_sendfail ); + fclose( fp ); + return; + } + + SIO_DELAY(); + + if( commWriteBytes( &addr, 4 ) < 4 ) /* send read address */ + { + puts( msg_sendfail ); + fclose( fp ); + return; + } + + SIO_DELAY(); + + if( commWriteBytes( &len, 4 ) < 4 ) /* send read length */ + { + puts( msg_sendfail ); + fclose( fp ); + return; + } + + putchar( ' ' ); /* draw a progress bar */ + for( i=0; i<70; i++ ) + { + putchar( '.' ); + } + putchar( ']' ); + putchar( '\r' ); + putchar( '[' ); + fflush( stdout ); + + progress = 0; + olen = len; + + while( len > 0 ) /* receive the incoming bytes */ + { + i = len; + if( i > 10 ) + i = 10; + + if( ( i = commReadBytes( buffer, i ) ) < 0 ) + { + puts( msg_recfail ); + break; + } + + fwrite( buffer, 1, i, fp ); + len -= i; + + i = (70*((1024*((olen-len)>>2))/ /* draw out progress */ + (olen>>2)))/1024; + if( i > progress ) + { + progress = i; + putchar( '#' ); + fflush( stdout ); + } + } + + for( i=0; i<(70-progress); i++ ) + putchar( '#' ); + + putchar( '\n' ); + + fclose( fp ); + +} /* dbGetMem */ + +unsigned int dbReadValue( int wordsz, unsigned int addr ) +{ + unsigned int val; + + if( commWriteByte( CMD_DB_WORD ) < 0 ) /* send value command */ + { + puts( msg_sendfail ); + return( 0 ); + } + + if( commWriteByte( 0 ) < 0 ) /* perform read operation */ + { + puts( msg_sendfail ); + return( 0 ); + } + + if( commWriteByte( wordsz ) < 0 ) /* send word size */ + { + puts( msg_sendfail ); + return( 0 ); + } + + if( commWriteBytes( &addr, 4 ) < 4 ) /* send address */ + { + puts( msg_sendfail ); + return( 0 ); + } + + if( commReadBytes( &val, 4 ) < 4 ) /* retrieve value */ + { + puts( msg_recfail ); + return( 0 ); + } + + return( val ); + +} /* dbReadValue */ + +void dbWriteValue( int wordsz, unsigned int addr, unsigned int val ) +{ + if( commWriteByte( CMD_DB_WORD ) < 0 ) /* send value command */ + { + puts( msg_sendfail ); + return; + } + + if( commWriteByte( 1 ) < 0 ) /* perform write operation */ + { + puts( msg_sendfail ); + return; + } + + if( commWriteByte( wordsz ) < 0 ) /* send word size */ + { + puts( msg_sendfail ); + return; + } + + if( commWriteBytes( &addr, 4 ) < 4 ) /* send address */ + { + puts( msg_sendfail ); + return; + } + + SIO_DELAY(); + + if( commWriteBytes( &val, 4 ) < 4 ) /* send value */ + { + puts( msg_sendfail ); + return; + } + + if( commReadByte() < 0 ) + { + puts( msg_noack ); + } + +} /* dbWriteValue */ + +static const char *regnames[] = +{ + "r0=","at=","v0=","v1=", + "a0=","a1=","a2=","a3=", + "t0=","t1=","t2=","t3=", + "t4=","t5=","t6=","t7=", + "s0=","s1=","s2=","s3=", + "s4=","s5=","s6=","s7=", + "t8=","t9=","k0=","k1=", + "gp=","sp=","fp=","ra=", + "pc=","hi=","lo=" +}; + +static const char *cop0names[] = +{ + "SR =", + "CAUSE =", + "BADVADDR =", + "JUMPDEST =", + "DCIC =", + "OPCODE[0] =", + "OPCODE[1] =" +}; + +void dbGetRegs( void ) +{ + int i; + unsigned int regval; + unsigned int pc; + char textbuff[32]; + + if( commWriteByte( CMD_DB_GETREGS ) < 0 ) /* send getmem command */ + { + puts( msg_sendfail ); + return; + } + + for( i=0; i<35; i++ ) + { + if( commReadBytes( ®val, 4 ) < 4 ) + { + puts( msg_recfail ); + return; + } + + printf( "%s%08X ", regnames[i], regval ); + if( (i&0x3) == 3 ) + printf( "\n" ); + + if( i == 32 ) + pc = regval; + } + + printf( "\n\n" ); + + for( i=0; i<7; i++ ) + { + if( commReadBytes( ®val, 4 ) < 4 ) + { + puts( msg_recfail ); + return; + } + printf( "%s%08X", cop0names[i], regval ); + if( i < 5 ) + putchar( '\n' ); + if( i >= 5 ) + { + mips_Decode( regval, pc+((i>5)?4:0), textbuff, 0 ); + printf( " %s\n", textbuff ); + } + } + + printf( "\n" ); + +} /* dbGetRegs */ + +void dbGetBreaks(void) +{ + unsigned int regval; + + if( commWriteByte(CMD_DB_GETBRK) < 0 ) /* send get breaks command */ + { + puts(msg_sendfail); + return; + } + + while(1) + { + if( commReadBytes(®val, 4) < 4 ) + { + puts(msg_recfail); + return; + } + + if( regval == 0xFFFFFFFF ) + { + break; + } + + printf("PC=%08X ", regval); + + if( commReadBytes(®val, 4) < 4 ) + { + puts(msg_recfail); + return; + } + + printf("FLAG=%08X ", regval); + + if( commReadBytes(®val, 4) < 4 ) + { + puts(msg_recfail); + return; + } + + printf("CNT=%08X\n", regval); + } + +} /* dbGetBreaks */ + +int dbSetReg(const char *regname, unsigned int value) +{ + int regnum; + int found; + + found = 0; + for(regnum=0; regnum<36; regnum++) + { + if( strcmp(regname, regnames_param[regnum]) == 0 ) + { + found = 1; + break; + } + } + + if( found == 0 ) + { + printf("Invalid register name.\n"); + return(1); + } + + if( commWriteByte(CMD_DB_SETREG) < 0 ) /* send set register command */ + { + puts(msg_sendfail); + return(1); + } + + if( commWriteByte(regnum) < 0 ) /* send register byte */ + { + puts(msg_sendfail); + return(1); + } + + if( commWriteBytes(&value, 4) < 4 ) /* send new register value */ + { + puts(msg_sendfail); + return(1); + } + + SIO_DELAY(); + + if( commReadByte() < 0 ) + { + puts(msg_noack); + } + + return(0); + +} /* dbSetReg */ + +int dbSetBDregs(unsigned int addr, unsigned int mask, unsigned char flags) +{ + /* send set data break command */ + if( commWriteByte(CMD_DB_SETBDREG) < 0 ) + { + puts(msg_sendfail); + return 1; + } + + if( commWriteBytes(&addr, 4) < 4 ) + { + puts(msg_sendfail); + return 1; + } + + if( commWriteBytes(&mask, 4) < 4 ) + { + puts(msg_sendfail); + return 1; + } + + if( commWriteByte(flags) < 0 ) + { + puts(msg_sendfail); + return 1; + } + + SIO_DELAY(); + + if( commReadByte() < 0 ) + { + puts(msg_noack); + } + + return 0; + +} /* dbSetDBregs */ diff --git a/indev/psn00bdbg-mk2/testutil/exec.c b/indev/psn00bdbg-mk2/testutil/exec.c new file mode 100644 index 0000000..bcd91ef --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/exec.c @@ -0,0 +1,413 @@ +#include +#include +#include +#include "exec.h" + +#define MAX_prg_entry_count 128 + +#pragma pack(push, 1) + +typedef struct _ELF_HEADER { + + unsigned int magic; // 0-3 + unsigned char word_size; // 4 + unsigned char endianness; // 5 + unsigned char elf_version; // 6 + unsigned char os_abi; // 7 + unsigned int unused[2]; // 8-15 + + unsigned short type; // 16-17 + unsigned short instr_set; // 18-19 + unsigned int elf_version2; // 20-23 + + unsigned int prg_entry_addr; // 24-27 + unsigned int prg_head_pos; // 28-31 + unsigned int sec_head_pos; // 32-35 + unsigned int flags; // 36-39 + unsigned short head_size; // 40-41 + unsigned short prg_entry_size; // 42-23 + unsigned short prg_entry_count; // 44-45 + unsigned short sec_entry_size; // 46-47 + unsigned short sec_entry_count; // 48-49 + unsigned short sec_names_index; // 50-51 + +} ELF_HEADER; + +typedef struct _PRG_HEADER { + unsigned int seg_type; + unsigned int p_offset; + unsigned int p_vaddr; + unsigned int undefined; + unsigned int p_filesz; + unsigned int p_memsz; + unsigned int flags; + unsigned int alignment; +} PRG_HEADER; + +#pragma pack(pop) + +typedef struct _PSEXE { + char header[8]; + char pad[8]; + EXEC params; + char license[64]; + char pad2[1908]; +} PSEXE; + +typedef struct _EXEPARAM { + EXEC params; + unsigned int crc32; + unsigned int flags; +} EXEPARAM; + +typedef struct _BINPARAM { + int size; + unsigned int addr; + unsigned int crc32; +} BINPARAM; + + +void* loadELF(FILE* fp, EXEC* param) +{ + int i; + ELF_HEADER head; + PRG_HEADER prg_heads[MAX_prg_entry_count]; + unsigned int exe_taddr = 0xffffffff; + unsigned int exe_haddr = 0; + unsigned int exe_tsize = 0; + unsigned char* binary; + size_t read_n; + + fseek(fp, 0, SEEK_SET); + read_n = fread(&head, 1, sizeof(head), fp); + + // Check header + if( head.magic != 0x464c457f ) { + + printf("File is neither a PS-EXE, CPE or ELF binary.\n"); + return NULL; + + } + + if( head.type != 2 ) { + + printf("Only executable ELF files are supported.\n"); + return NULL; + + } + + if( head.instr_set != 8 ) { + + printf("ELF file is not a MIPS binary.\n"); + return NULL; + + } + + if( head.word_size != 1 ) { + + printf("Only 32-bit ELF files are supported.\n"); + return NULL; + + } + + if( head.endianness != 1 ) { + + printf("Only little endian ELF files are supported.\n"); + return NULL; + + } + + + // Load program headers and determine binary size and load address + + fseek(fp, head.prg_head_pos, SEEK_SET); + for( i=0; i exe_haddr ) { + exe_haddr = prg_heads[i].p_vaddr; + } + + } + + exe_tsize = (exe_haddr-exe_taddr); + exe_tsize += prg_heads[head.prg_entry_count-1].p_filesz; + + + // Check if load address is appropriate in main RAM locations + if( ( ( exe_taddr>>24 ) == 0x0 ) || ( ( exe_taddr>>24 ) == 0x80 ) || + ( ( exe_taddr>>24 ) == 0xA0 ) ) { + + if( ( exe_taddr&0x00ffffff ) < 65536 ) { + + printf( "WARNING: Program text address overlaps kernel area!\n" ); + + } + + } + + + // Pad out the size to multiples of 2KB + exe_tsize = 2048*((exe_tsize+2047)/2048); + + // Load the binary data + binary = (unsigned char*)malloc( exe_tsize ); + memset( binary, 0x0, exe_tsize ); + + for( i=0; ipc0 = head.prg_entry_addr; + param->t_addr = exe_taddr; + param->t_size = exe_tsize; + + return binary; + +} + +void* loadCPE(FILE* fp, EXEC* param) { + + int i, val, exe_size = 0; + unsigned int uv, exe_entry = 0; + unsigned int *addr_list = NULL; + int addr_list_len = 0; + char* exe_buff; + unsigned int addr_upper=0; + unsigned int addr_lower=0; + size_t read_n; + + // Check CPE magic word + fseek(fp, 0, SEEK_SET); + read_n = fread(&val, 1, 4, fp); + + if( val != 0x01455043 ) + { + return NULL; + } + + // Clear EXEC parameters + memset(param, 0x0, sizeof(EXEC)); + + // Begin parsing CPE data + val = 0; + read_n = fread(&val, 1, 1, fp); + + while( val ) + { + switch( val ) + { + case 0x1: // Binary chunk + + read_n = fread(&uv, 1, 4, fp); + read_n = fread(&val, 1, 4, fp); + + addr_list_len++; + addr_list = (unsigned int*)realloc(addr_list, + sizeof(unsigned int)*addr_list_len); + + addr_list[addr_list_len-1] = uv; + exe_size += val; + + fseek(fp, val, SEEK_CUR); + break; + + case 0x3: // Set register, ignored + + val = 0; + read_n = fread(&val, 1, 2, fp); + + if( val != 0x90 ) + { + printf("Warning: Unknown SETREG code: %d\n", val); + } + + read_n = fread(&exe_entry, 1, 4, fp); + break; + + case 0x8: // Select unit, ignored + + val = 0; + read_n = fread(&val, 1, 1, fp); + break; + + default: + + printf("Unknown chunk found: %d\n", val); + free(addr_list); + return NULL; + + } + + // Read next code + val = 0; + read_n = fread(&val, 1, 1, fp); + + } + + // Begin loading the executable data + + // Find the highest chunk address + for(i=0; i addr_upper ) { + + addr_upper = addr_list[i]; + + } + + } + + // Find the lowest chunk address + addr_lower = addr_upper; + + for(i=0; ipc0 = exe_entry; + param->t_addr = addr_lower; + param->t_size = exe_size; + + return exe_buff; + +} + +void *loadExecutable(const char* exefile, EXEC *param) { + + PSEXE exe; + unsigned char* buffer; + + FILE* fp = fopen(exefile, "rb"); + + if( fp == NULL ) + { + printf("File not found.\n"); + return(NULL); + } + + // Load PS-EXE header + if( fread(&exe, 1, sizeof(PSEXE), fp) != sizeof(PSEXE) ) + { + printf("Read error or invalid file.\n"); + fclose(fp); + return(NULL); + } + + // Check file ID + if( strcmp(exe.header, "PS-X EXE") ) { + + // If not PS-EXE, try loading it as CPE + buffer = (unsigned char*)loadCPE(fp, param); + + if( buffer == NULL ) { + + // If not CPE, try loading it as ELF + buffer = (unsigned char*)loadELF(fp, param); + + if( buffer == NULL ) { + + fclose(fp); + return(NULL); + + } + + } + + } else { + + // Load PS-EXE body, simple + buffer = (unsigned char*)malloc(param->t_size); + + if( fread(buffer, 1, param->t_size, fp) != param->t_size ) { + + printf("Incomplete file or read error.\n"); + free(buffer); + fclose(fp); + + return(NULL); + + } + + memcpy(param, &exe.params, sizeof(EXEC)); + + } + + // Done with the file + fclose(fp); + + return(buffer); +} diff --git a/indev/psn00bdbg-mk2/testutil/exec.h b/indev/psn00bdbg-mk2/testutil/exec.h new file mode 100644 index 0000000..24688c0 --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/exec.h @@ -0,0 +1,24 @@ +#ifndef _EXEC_H +#define _EXEC_H + +typedef struct _EXEC { + unsigned int pc0; + unsigned int gp0; + unsigned int t_addr; + unsigned int t_size; + unsigned int d_addr; + unsigned int d_size; + unsigned int b_addr; + unsigned int b_size; + unsigned int sp_addr; + unsigned int sp_size; + unsigned int sp; + unsigned int fp; + unsigned int gp; + unsigned int ret; + unsigned int base; +} EXEC; + +void *loadExecutable(const char* exefile, EXEC *param); + +#endif /* _EXEC_H */ diff --git a/indev/psn00bdbg-mk2/testutil/main.c b/indev/psn00bdbg-mk2/testutil/main.c new file mode 100644 index 0000000..bb17731 --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/main.c @@ -0,0 +1,131 @@ +#include +#include +#ifdef _WIN32 +#include +#include +#include +#else +#include +#endif /* _WIN32 */ +#include "exec.h" +#include "cmdefs.h" +#include "main.h" + +#ifdef _WIN32 +const char *com_port = "COM1"; +#define delay(t) Sleep(t) +#else +const char *com_port = "/dev/ttyUSB0"; +#define delay(t) usleep(1000*t); +#endif +int com_baud = 115200; + +static const char *msg_minargs = "Not enough arguments.\n"; + +int UploadExec(const char *exefile) +{ + EXEC exec; + char *exebuff; + + /* load the PS-EXE */ + printf("Loading executable file... "); + fflush(stdout); + exebuff = loadExecutable(exefile, &exec); + if( !exebuff ) + return(1); + printf("Done.\n"); + + /* stop the target */ + printf("Target stop... "); + fflush(stdout); + if( dbSetExec(CMD_EXEC_STOP) ) + return(1); + delay(100); + printf("Done.\n"); + + /* modify target's registers for new program execution */ + printf("Setup target (pc0=%08X)... ", exec.pc0); + fflush(stdout); + delay(100); + if( dbSetReg("pc", exec.pc0) ) + return(1); + delay(100); + if( dbSetReg("sr", 0x00000400) ) + return(1); + delay(100); + if( dbSetReg("sp", 0x801FFFF0) ) + return(1); + delay(100); + printf("Done.\n"); + + /* upload program text */ + printf("Program upload (t_addr=%08X)...\n", exec.t_addr); + if( dbUploadMem(exec.t_addr, exebuff, exec.t_size) ) + return(1); + + printf("Target ready.\n"); + + return(0); + +} /* UploadExec */ + +int main( int argc, const char *argv[] ) +{ + int i; + const char *psexe; + + printf( "PSn00bDBG-mk2 Monitor Test Utility\n\n" ); + + psexe = NULL; + for( i=1; i - Specify serial port (default: COM1)\n" ); + printf( " /? or /h - Show help\n\n" ); + return( 0 ); + } + else + { + psexe = argv[i]; + } + } + + if( commOpen( com_port, com_baud ) ) + return( 1 ); + + if( dbGetInfo() ) + { + printf( "Unable to confirm connection with debug monitor.\n" ); + return( 1 ); + } + + /* perform PS-EXE upload */ + if( psexe ) + { + UploadExec(psexe); + promptMode(); + } + else + { + promptMode(); + } + + commClose(); + + return( 0 ); + +} /* main */ diff --git a/indev/psn00bdbg-mk2/testutil/main.h b/indev/psn00bdbg-mk2/testutil/main.h new file mode 100644 index 0000000..87f1ce4 --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/main.h @@ -0,0 +1,38 @@ +#ifndef _MAIN_H +#define _MAIN_H + +/* globals */ +extern const char *com_port; +extern int com_baud; + +/* comms.c */ +int dbGetInfo( void ); +void dbGetStatus( void ); +int dbGetStatusPoll(void); +int dbSetExec( int exec ); +void dbRunTo(unsigned int addr, int flag); +void dbSetBreak(unsigned int addr, unsigned int flag, int count); +void dbClearBreaks(void); +void dbGetBreaks(void); +void dbGetRegs( void ); +int dbSetReg(const char *regname, unsigned int value); +void dbReboot( void ); +void dbGetMem( unsigned int addr, int len, const char *outfile ); +int dbUploadMem( unsigned int addr, char *buff, size_t len ); +void dbUploadMemFile( unsigned int addr, const char *infile, size_t len ); +unsigned int dbReadValue( int wordsz, unsigned int addr ); +void dbWriteValue( int wordsz, unsigned int addr, unsigned int val ); + +/* prompt.c */ +void promptMode( void ); + +/* serial.c */ +int commOpen( const char *port, int baud ); +int commWriteByte( int value ); +int commWriteBytes( void *buff, int len ); +int commReadByte( void ); +int commReadBytes( void *buff, int len ); +int commPendingBytes(void); +void commClose( void ); + +#endif /* _MAIN_H */ diff --git a/indev/psn00bdbg-mk2/testutil/makefile b/indev/psn00bdbg-mk2/testutil/makefile new file mode 100644 index 0000000..be3fbe1 --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/makefile @@ -0,0 +1,31 @@ +CC = wcc386 +LNK = wlink + +CFLAGS = -q -bt=nt -w3 -d2 -odhl + +all : psdbtest.exe + +psdbtest.exe : main.obj prompt.obj comms.obj serial.obj mips_disassembler.obj exec.obj + wlink name psdbtest system nt f main,prompt,comms,serial,mips_disassembler,exec + +main.obj : main.c main.h cmdefs.h + $(CC) $(CFLAGS) main.c + +comms.obj : comms.c main.h cmdefs.h + $(CC) $(CFLAGS) comms.c + +prompt.obj : prompt.c main.h cmdefs.h + $(CC) $(CFLAGS) prompt.c + +serial.obj : serial.c + $(CC) $(CFLAGS) serial.c + +mips_disassembler.obj : mips_disassembler.c mips_disassembler.h + $(CC) $(CFLAGS) mips_disassembler.c + +exec.obj : exec.c exec.h + $(CC) $(CFLAGS) exec.c + +clean : .SYMBOLIC + del psdbtest.exe + del *.obj \ No newline at end of file diff --git a/indev/psn00bdbg-mk2/testutil/makefile-linux b/indev/psn00bdbg-mk2/testutil/makefile-linux new file mode 100644 index 0000000..83611d1 --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/makefile-linux @@ -0,0 +1,11 @@ +OFILES = main.o exec.o prompt.o comms.o serial.o mips_disassembler.o + +CFLAGS = -g -O2 + +CC = gcc + +all: $(OFILES) + $(CC) $(CFLAGS) $(OFILES) -o psdbtest + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/indev/psn00bdbg-mk2/testutil/mips_disassembler.c b/indev/psn00bdbg-mk2/testutil/mips_disassembler.c new file mode 100644 index 0000000..c7effb7 --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/mips_disassembler.c @@ -0,0 +1,663 @@ +#include +#include +#include +#include +#include "mips_disassembler.h" +#include "pstypes.h" + +#define OPCODE1(p) ( (p>>26)&0x3F ) +#define OPCODE2(p) ( p&0x3F ) +#define OPREGS(p) ( (p>>21)&0x1f ) +#define OPREGT(p) ( (p>>16)&0x1f ) +#define OPREGD(p) ( (p>>11)&0x1f ) + +#define OPIMM5(p) ( (p>>6)&0x1f ) +#define OPIMM16(p) ( p&0xffff ) +#define OPIMM20(p) ( (p>>6)&0x1FFFFF ) +#define OPIMM25(p) ( p&0x1FFFFFF ) +#define OPIMM26(p) ( p&0x3FFFFFF ) +#define EXTEND26(p) (((int)(p<<6))>>6) +#define EXTEND16(p) (((int)(p<<16))>>16) + +static const char* regs[] = { + "r0","at","v0","v1", + "a0","a1","a2","a3", + "t0","t1","t2","t3", + "t4","t5","t6","t7", + "s0","s1","s2","s3", + "s4","s5","s6","s7", + "t8","t9","k0","k1", + "gp","sp","fp","ra" +}; + +static void Decode_cop(unsigned int opcode, char* output) { + + int copnum = OPCODE1(opcode)&0x3; + + const char* cop0_nick[32] = { + "N/A","N/A","N/A","BPC", // r0-r3 + "N/A","BDA","JUMPDEST","DCIC", // r4-r7 + "BadVaddr","BDAM","N/A","BPCM", // r8-r11 + "SR","CAUSE","EPC","PRID", // r12-r15 + "N/A","N/A","N/A","N/A", + "N/A","N/A","N/A","N/A", + "N/A","N/A","N/A","N/A", + "N/A","N/A","N/A","N/A", + }; + + const char* cop2_data_nick[32] = { + "VXY0","VZ0","VXY1","VZ1", // r0-r3 + "VXY2","VZ2","RGBC","OTZ", // r4-r7 + "IR0","IR1","IR2","IR3", // r8-r11 + "SXY0","SXY1","SXY2","SXYP", // r12-r15 + "SZ0","SZ1","SZ2","SZ3", + "RGB0","RGB1","RGB2","RES1", + "MAC0","MAC1","MAC2","MAC3", + "IRGB","ORGB","LZCS","LZCR", + }; + + const char* cop2_ctrl_nick[32] = { + "RT11","RT12","RT13","RT21", // r0-r3 + "RT22","RT23","RT31","RT32", // r4-r7 + "RT33","TRX","TRY","TRZ", // r8-r11 + "LR1","LR2","LR3","LG1", // r12-r15 + "LG2","LG3","LB1","LB2", + "LB3","RFC","GFC","BFC", + "OFX","OFY","H","DQA", + "DQB","ZSF3","ZSF4","FLAG", + }; + + char temp[16]; + + if( (OPCODE1(opcode)&0x20) == 0 ) { + + switch(OPREGS(opcode)) { + case 0x0: // mfc? + sprintf( output, "mfc%d %s, r%d", copnum, + regs[OPREGT(opcode)], + OPREGD(opcode) ); + + if ( copnum == 0 ) { + + sprintf( temp, " (%s)", cop0_nick[OPREGD(opcode)] ); + strcat( output, temp ); + + } else if ( copnum == 2 ) { + + sprintf( temp, " (%s)", cop2_data_nick[OPREGD(opcode)] ); + strcat( output, temp ); + + } + + break; + case 0x2: // cfc? + + sprintf( output, "cfc%d %s, r%d", copnum, + regs[OPREGT(opcode)], + OPREGD(opcode) ); + + if ( copnum == 2 ) { + + sprintf( temp, " (%s)", cop2_ctrl_nick[OPREGD(opcode)] ); + strcat( output, temp ); + + } + + break; + case 0x4: // mtc? + + sprintf( output, "mtc%d %s, r%d", copnum, + regs[OPREGT(opcode)], + OPREGD(opcode) ); + + if ( copnum == 0 ) { + + sprintf( temp, " (%s)", cop0_nick[OPREGD(opcode)] ); + strcat( output, temp ); + + } else if ( copnum == 2 ) { + + sprintf( temp, " (%s)", cop2_data_nick[OPREGD(opcode)] ); + strcat( output, temp ); + + } + + break; + case 0x6: // ctc? + + sprintf( output, "ctc%d %s, r%d", copnum, + regs[OPREGT(opcode)], OPREGT(opcode) ); + + if ( copnum == 2 ) + { + sprintf( temp, " (%s)", cop2_ctrl_nick[OPREGT(opcode)] ); + strcat( output, temp ); + } + + break; + + case 0x8: // bc?f bc?t + if( OPREGT(opcode) == 1 ) + { + sprintf( output, "bc%dt $%X", copnum, + OPIMM16(opcode) ); + } + else + { + sprintf( output, "bc%df $%X", copnum, + OPIMM16(opcode) ); + } + break; + + default: // cop? + + //strcpy( output, "cp" ); + //sprintf( output, "%x", OPREGS(opcode) ); + + if( OPREGS(opcode)&0x10 ) + { + sprintf( output, "cop%d $%X", copnum, OPIMM25(opcode) ); + } + break; + } + + } else { + + if( (OPCODE1(opcode)&0x8) == 0 ) { + + sprintf( output, "lwc%d r%d,%d(%s)", copnum, + OPREGT(opcode), EXTEND16(OPIMM16(opcode)), + regs[OPREGS(opcode)] ); + + if ( copnum == 0 ) { + + sprintf( temp, " (%s)", cop0_nick[OPREGT(opcode)] ); + strcat( output, temp ); + + } else if ( copnum == 2 ) { + + sprintf( temp, " (%s)", cop2_data_nick[OPREGT(opcode)] ); + strcat( output, temp ); + + } + + } else { + + sprintf( output, "swc%d r%d,%d(%s)", copnum, + OPREGT(opcode), EXTEND16(OPIMM16(opcode)), + regs[OPREGS(opcode)] ); + + if ( copnum == 0 ) { + + sprintf( temp, " (%s)", cop0_nick[OPREGT(opcode)] ); + strcat( output, temp ); + + } else if ( copnum == 2 ) { + + sprintf( temp, " (%s)", cop2_data_nick[OPREGT(opcode)] ); + strcat( output, temp ); + + } + + } + + } + +} + +void mips_Decode(unsigned int opcode, unsigned int addr, char* output, int arrows) { + + char temp[32]; + int immval; + + const char* pri_op[] = { + NULL ,NULL ,"j " ,"jal ",//4 + "beq " ,"bne " ,"blez " ,"bgtz ",//8 + "addi " ,"addiu " ,"slti " ,"sltiu ",//12 + "andi " ,"ori " ,"xori " ,"lui ",//16 + "cop0 " ,"cop1 " ,"cop2 " ,"cop3 ",//24 + NULL , NULL ,NULL , NULL, //32 + NULL , NULL ,NULL , NULL, //36 + NULL , NULL ,NULL , NULL, //40 + "lb " ,"lh " ,"lwl " ,"lw ",//44 + "lbu " ,"lhu " ,"lwr " ,NULL, //48 + "sb " ,"sh " ,"swl " ,"sw ", + NULL ,NULL ,"swr " ,NULL, + }; + + const char* sc_op[] = { + "sll " ,NULL ,"srl " ,"sra " , //0 + "sllv " ,NULL ,"srlv " ,"srav " , //4 + "jr " ,"jalr " ,NULL ,NULL , //8 + "syscall " ,"break " ,NULL ,NULL , //12 + "mfhi " ,"mthi " ,"mflo " ,"mtlo " , //16 + NULL ,NULL ,NULL ,NULL , //20 + "mult " ,"multu " ,"div " ,"divu " , //24 + NULL ,NULL ,NULL ,NULL , //28 + "add " ,"addu " ,"sub " ,"subu " , //32 + "and " ,"or " ,"xor " ,"nor " , //36 + NULL ,NULL ,"slt " ,"sltu " , //38 + }; + + strcpy( output, "???" ); + + if( opcode ) { + + // Secondary codes + if( OPCODE1(opcode) == 0 ) { + + switch( OPCODE2(opcode) ) { + case 0x0: // sll, srl, sra + case 0x2: + case 0x3: + strcpy( output, sc_op[OPCODE2(opcode)] ); + sprintf( temp, "%s, %s, %d", + regs[OPREGD(opcode)], regs[OPREGT(opcode)], OPIMM5(opcode) ); + strcat( output, temp ); + break; + case 0x4: // sllv, srlv, srav + case 0x6: + case 0x7: + strcpy( output, sc_op[OPCODE2(opcode)] ); + sprintf( temp, "%s, %s, %s", regs[OPREGT(opcode)], + regs[OPREGD(opcode)], regs[OPREGS(opcode)] ); + strcat( output, temp ); + break; + case 0x8: // jr + strcpy( output, sc_op[OPCODE2(opcode)] ); + strcat( output, regs[OPREGS(opcode)] ); + break; + case 0x9: // jalr + strcpy( output, sc_op[OPCODE2(opcode)] ); + sprintf( temp, "%s, %s", regs[OPREGD(opcode)], + regs[OPREGS(opcode)] ); + strcat( output, temp ); + break; + case 0xc: // syscall, break + case 0xd: + strcpy( output, sc_op[OPCODE2(opcode)] ); + sprintf( temp, "$%x", OPIMM20(opcode) ); + strcat( output, temp ); + break; + case 0x10: // mfhi, mflo + case 0x12: + strcpy( output, sc_op[OPCODE2(opcode)] ); + strcat( output, regs[OPREGD(opcode)] ); + break; + case 0x11: // mthi, mtlo + case 0x13: + strcpy( output, sc_op[OPCODE2(opcode)] ); + strcat( output, regs[OPREGS(opcode)] ); + break; + case 0x18: // mult, multu, div, divu + case 0x19: + case 0x1a: + case 0x1b: + strcpy( output, sc_op[OPCODE2(opcode)] ); + sprintf( temp, "%s, %s", regs[OPREGS(opcode)], + regs[OPREGT(opcode)] ); + strcat( output, temp ); + break; + case 0x2a: // slt, sltu + case 0x2b: + strcpy( output, sc_op[OPCODE2(opcode)] ); + sprintf( temp, "%s, %s, %s", regs[OPREGD(opcode)], + regs[OPREGS(opcode)], regs[OPREGT(opcode)] ); + strcat( output, temp ); + break; + default: + + if( ( OPCODE2(opcode) >= 0x20 ) && ( OPCODE2(opcode) <= 0x27 ) ) { + strcpy( output, sc_op[OPCODE2(opcode)] ); + sprintf( temp, "%s, %s, %s", regs[OPREGD(opcode)], + regs[OPREGT(opcode)], regs[OPREGS(opcode)] ); + strcat( output, temp ); + } + + break; + } + + // Primary + } else { + + if( OPCODE1(opcode) == 0x1 ) { // Branches + + switch((opcode>>16)&0x1f) { + case 0x0: + strcpy(output, "bltz "); + break; + case 0x1: + strcpy(output, "bgez "); + break; + case 0x10: + strcpy(output, "bltzal "); + break; + case 0x11: + strcpy(output, "bgtzal "); + break; + } + + sprintf( temp, "%s,$%08X ", regs[OPREGS(opcode)], + addr+4+(EXTEND16(OPIMM16(opcode))*4) ); + strcat( output, temp ); + + if( arrows ) { + if ( addr+4+(EXTEND16(OPIMM16(opcode))*4) > addr ) { + strcat( output, "▼" ); + } else { + strcat( output, "▲" ); + } + } + + } else if( ( OPCODE1(opcode) == 0x2 ) || ( OPCODE1(opcode) == 0x3 ) ) { // j, jal + + unsigned int dest = (OPIMM26(opcode)*4)|(addr&0xf0000000); + + strcpy( output, pri_op[OPCODE1(opcode)] ); + sprintf( temp, "$%08X ", dest ); + strcat( output, temp ); + + if( arrows ) { + if ( dest > addr ) { + strcat( output, "▼" ); + } else { + strcat( output, "▲" ); + } + } + } else if( ( OPCODE1(opcode) >= 0x8 ) && + ( OPCODE1(opcode) <= 0x9 ) ) { + + strcpy( output, pri_op[OPCODE1(opcode)] ); + + sprintf( temp, "%s, %s, %d", regs[OPREGT(opcode)], + regs[OPREGS(opcode)], EXTEND16(OPIMM16(opcode)) ); + strcat( output, temp ); + + } else if( ( OPCODE1(opcode) >= 0xa ) && + ( OPCODE1(opcode) <= 0xe ) ) { + + strcpy( output, pri_op[OPCODE1(opcode)] ); + + sprintf( temp, "%s, %s, $%lX", regs[OPREGT(opcode)], + regs[OPREGS(opcode)], labs( OPIMM16(opcode) ) ); + strcat( output, temp ); + + } else if( OPCODE1(opcode) == 0xf ) { + + strcpy( output, pri_op[OPCODE1(opcode)] ); + sprintf( temp, "%s, $%X", + regs[OPREGT(opcode)], OPIMM16(opcode) ); + strcat( output, temp ); + + } else if( ( OPCODE1(opcode) >= 0x20 ) && + ( OPCODE1(opcode) <= 0x2e ) ) { + + switch( OPCODE1(opcode) ) { + case 0x27: + case 0x2c: + case 0x2d: + return; + } + + strcpy( output, pri_op[OPCODE1(opcode)] ); + sprintf( temp, "%s,", + regs[OPREGT(opcode)] ); + strcat( output, temp ); + + immval = EXTEND16(OPIMM16(opcode)); + sprintf( temp, "$%lX", labs( immval ) ); + if( immval < 0 ) { + strcat( output, "-" ); + } else { + strcat( output, " " ); + } + strcat( output, temp ); + + sprintf( temp, "(%s)", regs[OPREGS(opcode)] ); + strcat( output, temp ); + + } else if ( OPCODE1(opcode)&0x10 ) { + + Decode_cop( opcode, output ); + + } else { + + switch( OPCODE1(opcode) ) { + case 0x4: + case 0x5: + strcpy( output, pri_op[OPCODE1(opcode)] ); + sprintf( temp, "%s, %s, $%08X ", + regs[OPREGS(opcode)], + regs[OPREGT(opcode)], + addr+4+(EXTEND16(OPIMM16(opcode))*4) ); + strcat( output, temp ); + + if( arrows ) { + if ( addr+4+(EXTEND16(OPIMM16(opcode))*4) > addr ) { + strcat( output, "▼" ); + } else { + strcat( output, "▲" ); + } + } + + break; + case 0x6: + case 0x7: + strcpy( output, pri_op[OPCODE1(opcode)] ); + sprintf( temp, "%s, $%08X ", + regs[OPREGS(opcode)], + addr+4+(EXTEND16(OPIMM16(opcode))*4) ); + strcat( output, temp ); + + if( arrows ) { + if ( addr+4+(EXTEND16(OPIMM16(opcode))*4) > addr ) { + strcat( output, "▼" ); + } else { + strcat( output, "▲" ); + } + } + + break; + } + + } + + } + + } else { + + strcpy( output, "nop" ); + + } + +} + +unsigned int mips_GetNextPc(unsigned int *regs, int stepover) { + + unsigned int dest = regs[PS_REG_epc]+4; + unsigned int opcode = regs[PS_REG_opcode]; + unsigned int rv,rt; + + if( OPCODE1(opcode) == 0 ) { + + if( ( OPCODE2(opcode) == 8 )||( OPCODE2(opcode) == 9 ) ) { // jr, jalr + + if( ( OPCODE2(opcode) == 9 ) && ( stepover ) ) { + + dest += 4; + + } else { + + if( OPREGS(opcode) == 0 ) + dest = 0; + else + dest = regs[OPREGS(opcode)-1]; + + } + + } + + } else if( OPCODE1(opcode) == 1 ) { + + if( OPREGS(opcode) == 0 ) + rv = 0; + else + rv = regs[OPREGS(opcode)-1]; + + switch(OPREGT(opcode)) { + case 0x0: // bltz + if ( (((int)0)|rv) < 0 ) { + dest = regs[PS_REG_epc]+4+(EXTEND16(OPIMM16(opcode))*4); + } else { + dest += 4; + } + break; + case 0x1: // bgez + if ( (((int)0)|rv) >= 0 ) { + dest = regs[PS_REG_epc]+4+(EXTEND16(OPIMM16(opcode))*4); + } else { + dest += 4; + } + break; + case 0x8: // bltzal + if ( (((int)0)|rv) < 0 ) { + dest = regs[PS_REG_epc]+4+(EXTEND16(OPIMM16(opcode))*4); + } else { + dest += 4; + } + break; + case 0x9: // bgezal + if ( (((int)0)|rv) >= 0 ) { + dest = regs[PS_REG_epc]+4+(EXTEND16(OPIMM16(opcode))*4); + } else { + dest += 4; + } + break; + } + + } else if( ( OPCODE1(opcode) == 2 ) || ( OPCODE1(opcode) == 3 ) ) { // j,jal + + if( ( OPCODE1(opcode) == 3 ) && ( stepover ) ) { + + dest += 4; + + } else { + + dest = (regs[PS_REG_epc]&0xf0000000)|(OPIMM26(opcode)*4); + + } + + } else if( OPCODE1(opcode) == 4 ) { // beq + + if( OPREGS(opcode) == 0 ) { + rv = 0; + } else { + rv = regs[OPREGS(opcode)-1]; + } + + if( OPREGT(opcode) == 0 ) { + rt = 0; + } else { + rt = regs[OPREGT(opcode)-1]; + } + + if( rv == rt ) { + + dest = regs[PS_REG_epc]+4+(EXTEND16(OPIMM16(opcode))*4); + + } else { + + dest += 4; + + } + + } else if( OPCODE1(opcode) == 5 ) { // bne + + if( OPREGS(opcode) == 0 ) { + rv = 0; + } else { + rv = regs[OPREGS(opcode)-1]; + } + + if( OPREGT(opcode) == 0 ) { + rt = 0; + } else { + rt = regs[OPREGT(opcode)-1]; + } + + if( rv != rt ) { + + dest = regs[PS_REG_epc]+4+(EXTEND16(OPIMM16(opcode))*4); + + } else { + + dest += 4; + + } + + } else if( OPCODE1(opcode) == 6 ) { // blez + + if( OPREGS(opcode) == 0 ) { + rv = 0; + } else { + rv = regs[OPREGS(opcode)-1]; + } + + if( (((int)0)|rv) <= 0 ) { + + dest = regs[PS_REG_epc]+4+(EXTEND16(OPIMM16(opcode))*4); + + } else { + + dest += 4; + + } + + } else if( OPCODE1(opcode) == 7 ) { // bgtz + + if( OPREGS(opcode) == 0 ) { + rv = 0; + } else { + rv = regs[OPREGS(opcode)-1]; + } + + if( (((int)0)|rv) > 0 ) { + + dest = regs[PS_REG_epc]+4+(EXTEND16(OPIMM16(opcode))*4); + + } else { + + dest += 4; + + } + + } + + return dest; +} + +unsigned int mips_GetJumpAddr(unsigned int addr, unsigned int opcode) { + + if( OPCODE1(opcode) == 1 ) { + + switch(OPREGT(opcode)) { + case 0x0: // bltz + case 0x1: // bgez + case 0x8: // bltzal + case 0x9: // bgezal + return addr+4+(EXTEND16(OPIMM16(opcode))*4); + } + + } else if( ( OPCODE1(opcode) == 2 ) || ( OPCODE1(opcode) == 3 ) ) { // j,jal + + return (addr&0xf0000000)|(OPIMM26(opcode)*4); + + } else if( ( OPCODE1(opcode) >= 4 ) && ( OPCODE1(opcode) <= 7 ) ) { // beq + + return addr+4+(EXTEND16(OPIMM16(opcode))*4); + + } + + return 0; + +} diff --git a/indev/psn00bdbg-mk2/testutil/mips_disassembler.h b/indev/psn00bdbg-mk2/testutil/mips_disassembler.h new file mode 100644 index 0000000..41e604f --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/mips_disassembler.h @@ -0,0 +1,10 @@ +#ifndef DISASSEMBLER_H +#define DISASSEMBLER_H + +void mips_Decode(unsigned int opcode, unsigned int addr, char* output, int arrows); + +unsigned int mips_GetNextPc(unsigned int *regs, int stepover); +unsigned int mips_GetJumpAddr(unsigned int addr, unsigned int opcode); + +#endif /* DISASSEMBLER_H */ + diff --git a/indev/psn00bdbg-mk2/testutil/prompt.c b/indev/psn00bdbg-mk2/testutil/prompt.c new file mode 100644 index 0000000..e51e203 --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/prompt.c @@ -0,0 +1,518 @@ +#include +#include +#include + +#ifdef _WIN32 + +#include +#include + +#else + +#include +#include +#include +#include + +#endif /* _WIN32 */ + +#include "cmdefs.h" +#include "main.h" + +static int prompt_quit; +int UploadExec(const char *exefile); + +#ifdef _WIN32 + +#define _kbhit kbhit + +#else + +struct termios orig_term; + +void enable_raw_mode() +{ + struct termios term; + tcgetattr(0, &orig_term); + + term = orig_term; + term.c_lflag &= ~(ICANON | ECHO); // Disable echo as well + tcsetattr(0, TCSANOW, &term); +} + +void disable_raw_mode() +{ + tcsetattr(0, TCSANOW, &orig_term); +} + +int _kbhit() +{ + struct timeval tv = { 0L, 0L }; + + fd_set fds; + FD_ZERO(&fds); + FD_SET(0, &fds); + + return select(1, &fds, NULL, NULL, &tv); + +} /* _kbhit */ + +void term_func(int signum) +{ + disable_raw_mode(); + exit( 0 ); + +} /* term_func */ + +#endif /* _WIN32 */ + +static void promptHelp( void ) +{ + printf("? View help\n"); + printf("up Upload memory from \n"); + printf("down Download memory to \n"); + printf("rv <8|16|32> Read a value from \n"); + printf("wv <8|16|32> Write a value to \n"); + printf("sb [count] Set program breakpoint at \n"); + printf("cb Clear all breakpoints\n"); + printf("lb List breakpoints\n"); + printf("sr Set register value\n"); + printf("stop Stop target\n"); + printf("cont Resume target\n"); + printf("stat Show target status\n"); + printf("r / regs Show registers\n"); + printf("t / trace Trace\n"); + printf("rt / runto Run until \n"); + printf("tt / traceto Run until or jump/branch\n"); + printf("reboot Reboot target and quit\n"); + printf("q / quit Quit\n\n"); + printf("Commands can be stacked as one keystroke like so:\n"); + printf("trace regs - Performs a trace operation then shows registers\n\n"); + +} /* promptHelp */ + +static char *getarg( const char *msg ) +{ + char *arg; + + arg = strtok( NULL, " " ); + + if( !arg ) + { + printf( "Missing %s argument.\n", msg ); + return( NULL ); + } + + return( arg ); + +} /* getarg */ + +void promptMode( void ) +{ + int inkey; + int bytebuff; + char inbuff[100]; + int inpos; + int inlen; + char *inptr; + char *arg; + + unsigned int addr; + int len; + const char *file; + + int wordsz; + unsigned int value; + + int firsttime; + int pollcount; + int laststat; + int pollstat; + +#ifdef _WIN32 +#else + struct sigaction st; +#endif /* _WIN32 */ + + printf( "Enter ? for list of directives.\n\n" ); + + prompt_quit = 0; + +#ifdef _WIN32 +#else + st.sa_handler = term_func; + sigemptyset( &st.sa_mask ); + st.sa_flags = SA_RESTART; + sigaction( SIGINT, &st, NULL ); + + enable_raw_mode(); +#endif /* _WIN32 */ + + if( (laststat = dbGetStatusPoll()) < 0 ) + { + printf("Error polling target status.\n"); + laststat = 0; + } + + while( !prompt_quit ) + { + memset( inbuff, 0, 100 ); +#if 1//def _WIN32 + //gets( inbuff ); +#else + fgets( inbuff, 100, stdin ); + + while( arg = strchr( inbuff, '\n' ) ) /* remove newlines from input */ + { + *arg = 0; + } +#endif + + printf("nDB>"); + inpos = 0; + fflush(stdout); + firsttime = 1; + pollcount = 0; + while(1) + { + if( commPendingBytes() > 0 ) + { + /* routine that simply flushes the serial interface */ + bytebuff = commReadByte(); + printf("\rReceived byte: %02x\n", bytebuff); + + printf("nDB>%s", inbuff); + fflush(stdout); + pollcount = 0; + } + + if( _kbhit() ) + { +#ifdef _WIN32 + inkey = getch(); + if( inkey == 13 ) +#else + inkey = getchar(); + if( inkey == '\n' ) +#endif + { + fputc('\n', stdout); + fflush(stdout); + break; + } +#ifdef _WIN32 + else if( inkey == 8 ) +#else + else if( inkey == 127 ) /* linux backspace */ +#endif /* _WIN32 */ + { + if( inpos > 0 ) + { + fputc('\b', stdout); + fputc(' ', stdout); + fputc('\b', stdout); + fflush(stdout); + inpos--; + inbuff[inpos] = 0; + continue; + } + } + else + { + if( inpos < 100 ) + { + fputc(inkey, stdout); + fflush(stdout); + inbuff[inpos] = inkey; + inpos++; + inbuff[inpos] = 0; + continue; + } + } + } +#ifdef _WIN32 + Sleep(10); +#else + usleep(1000); +#endif /* _WIN32 */ + + pollcount++; + if( pollcount > 10 ) + { + if( (pollstat = dbGetStatusPoll()) < 0 ) + { + printf("Error polling target status.\n"); + } + else + { + if( pollstat != laststat ) + { + laststat = pollstat; + switch(pollstat) + { + case DB_STAT_STOP: + printf("\rTarget stopped.\n"); + break; + case DB_STAT_BREAK: + printf("\rBreakpoint/trace complete.\n"); + break; + case DB_STAT_EXCEPT: + printf("\rUnhandled exception.\n"); + break; + default: + printf("\rUndefined target state (%d).\n", pollstat); + } + dbGetRegs(); + printf("nDB>%s", inbuff); + fflush(stdout); + } + } + pollcount = 0; + } + } + + if( ( inlen = strlen( inbuff ) ) == 0 ) + continue; + + inptr = inbuff; + while( arg = strtok( inptr, " " ) ) + { + inptr = NULL; + + /* quit parsing when end of string is reached */ + + if( strcasecmp( "?", arg ) == 0 ) + { + promptHelp(); + } + else if( strcasecmp( "rv", arg ) == 0 ) /* read value */ + { + if( !(arg = getarg( "word size" ) ) ) /* get word size arg */ + break; + sscanf( arg, "%d", &wordsz ); + + if( !(arg = getarg( "address" ) ) ) /* get address arg */ + break; + sscanf( arg, "%x", &addr ); + + switch( wordsz ) + { + case 8: + wordsz = 0; + break; + case 16: + wordsz = 1; + break; + case 32: + wordsz = 2; + break; + default: + printf( "Unknown word size: %d\n", wordsz ); + wordsz = -1; + } + + if( wordsz >= 0 ) + { + value = dbReadValue( wordsz, addr ); + switch( wordsz ) + { + case 0: + printf( "%08X=%02X\n", addr, value ); + break; + case 1: + printf( "%08X=%04X\n", addr, value ); + break; + case 2: + printf( "%08X=%08X\n", addr, value ); + break; + } + } + } + else if( strcasecmp( "wv", arg ) == 0 ) /* read value */ + { + if( !(arg = getarg( "word size" ) ) ) /* get word size arg */ + break; + sscanf( arg, "%d", &wordsz ); + + if( !(arg = getarg( "address" ) ) ) /* get address arg */ + break; + sscanf( arg, "%x", &addr ); + + if( !(arg = getarg( "value" ) ) ) /* get value */ + break; + sscanf( arg, "%x", &value ); + + switch( wordsz ) + { + case 8: + wordsz = 0; + break; + case 16: + wordsz = 1; + break; + case 32: + wordsz = 2; + break; + default: + printf( "Unknown word size: %d\n", wordsz ); + wordsz = -1; + } + + dbWriteValue( wordsz, addr, value ); + } + else if( strcasecmp( "exe", arg ) == 0 ) /* upload executable */ + { + if( !(file = getarg( "file name" ) ) ) /* get file arg */ + break; + + UploadExec(file); + + laststat = DB_STAT_STOP; + } + else if( strcasecmp( "up", arg ) == 0 ) /* upload memory */ + { + if( !(arg = getarg( "address" ) ) ) /* get address arg */ + break; + sscanf( arg, "%x", &addr ); + + if( !(arg = getarg( "length" ) ) ) /* get length arg */ + break; + sscanf( arg, "%d", &len ); + + if( !(file = getarg( "file name" ) ) ) /* get file arg */ + break; + + printf( "Performing memory upload of %d byte(s) to %08X from %s\n", + len, addr, file ); + + dbUploadMemFile( addr, file, len ); + } + else if( strcasecmp( "down", arg ) == 0 ) /* download memory */ + { + if( !(arg = getarg( "address" ) ) ) /* get address arg */ + break; + sscanf( arg, "%x", &addr ); + + if( !(arg = getarg( "length" ) ) ) /* get length arg */ + break; + sscanf( arg, "%d", &len ); + + if( !(file = getarg( "file name" ) ) ) /* get file arg */ + break; + + printf( "Performing memory dump of %d byte(s) from %08X to %s\n", + len, addr, file ); + + dbGetMem( addr, len, file ); + } + else if( strcasecmp("sb", arg) == 0 ) /* set breakpoint */ + { + if( !(arg = getarg("address") ) ) /* get address arg */ + break; + sscanf(arg, "%x", &addr); + if( !(arg = getarg("flag") ) ) /* get flag arg */ + break; + sscanf(arg, "%d", &value); + if( value == 2 ) + { + if( !(arg = getarg("count") ) ) /* get flag arg */ + break; + sscanf(arg, "%d", &len); + } + else + { + len = 0; + } + dbSetBreak(addr, value, len); + } + else if( strcasecmp("cb", arg) == 0 ) /* clear breakpoints */ + { + dbClearBreaks(); + } + else if( strcasecmp("lb", arg) == 0 ) + { + dbGetBreaks(); + } + else if( strcasecmp( "stat", arg ) == 0 ) /* get target status */ + { + dbGetStatus(); + } + else if( strcasecmp( "stop", arg ) == 0 ) /* stop target */ + { + dbSetExec( CMD_EXEC_STOP ); + } + else if( strcasecmp( "cont", arg ) == 0 ) /* continue target */ + { + dbSetExec( CMD_EXEC_RESUME ); + laststat = DB_STAT_RUN; + } + else if( ( strcasecmp( "t", arg ) == 0 ) || + ( strcasecmp( "trace", arg ) == 0 ) ) + { + dbSetExec( CMD_EXEC_STEP ); + laststat = DB_STAT_RUN; + } + else if( ( strcasecmp( "rt", arg ) == 0 ) || /* runto address */ + ( strcasecmp( "runto", arg ) == 0 ) ) + { + if( !(arg = getarg( "program address" ) ) ) /* get address arg */ + break; + sscanf( arg, "%x", &addr ); + dbRunTo(addr, 0); + laststat = DB_STAT_RUN; + } + else if( ( strcasecmp( "tt", arg ) == 0 ) || /* traceto address */ + ( strcasecmp( "traceto", arg ) == 0 ) ) + { + if( !(arg = getarg( "program address" ) ) ) /* get address arg */ + break; + sscanf( arg, "%x", &addr ); + dbRunTo(addr, 1); + laststat = DB_STAT_RUN; + } + else if( ( strcasecmp( "r", arg ) == 0 ) || /* get registers */ + ( strcasecmp( "regs", arg ) == 0 ) ) + { + dbGetRegs(); + } + else if( strcasecmp("sr", arg) == 0 ) /* set register value */ + { + if( !(arg = getarg("register") ) ) /* get address arg */ + break; + file = arg; + if( !(arg = getarg("value") ) ) /* get flag arg */ + break; + sscanf(arg, "%x", &value); + dbSetReg(file, value); + } + else if( strcasecmp( "reboot", arg ) == 0 ) + { + dbReboot(); + prompt_quit = 1; + break; + } + else if( ( strcasecmp( "q", arg ) == 0 ) || /* quit program */ + ( strcasecmp( "quit", arg ) == 0 ) ) + { + prompt_quit = 1; + break; + } + else + { + printf( "Unknown directive: %s\n", arg ); + } + /* delay before executing next directive */ +#ifdef _WIN32 + Sleep(100); +#else + usleep(1000); +#endif /* _WIN32 */ + } + } + +#ifdef _WIN32 +#else + disable_raw_mode(); +#endif /* _WIN32 */ + +} /* promptMode */ diff --git a/indev/psn00bdbg-mk2/testutil/pstypes.h b/indev/psn00bdbg-mk2/testutil/pstypes.h new file mode 100644 index 0000000..5767cbd --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/pstypes.h @@ -0,0 +1,81 @@ +#ifndef PSTYPES_H +#define PSTYPES_H + +// CPU +#define PS_REG_at 0 +#define PS_REG_v0 1 +#define PS_REG_v1 2 +#define PS_REG_a0 3 +#define PS_REG_a1 4 +#define PS_REG_a2 5 +#define PS_REG_a3 6 +#define PS_REG_t0 7 +#define PS_REG_t1 8 +#define PS_REG_t2 9 +#define PS_REG_t3 10 +#define PS_REG_t4 11 +#define PS_REG_t5 12 +#define PS_REG_t6 13 +#define PS_REG_t7 14 +#define PS_REG_s0 15 +#define PS_REG_s1 16 +#define PS_REG_s2 17 +#define PS_REG_s3 18 +#define PS_REG_s4 19 +#define PS_REG_s5 20 +#define PS_REG_s6 21 +#define PS_REG_s7 22 +#define PS_REG_t8 23 +#define PS_REG_t9 24 +#define PS_REG_k0 25 +#define PS_REG_k1 26 +#define PS_REG_gp 27 +#define PS_REG_sp 28 +#define PS_REG_fp 29 +#define PS_REG_ra 30 +#define PS_REG_lo 31 +#define PS_REG_hi 32 + +// cop0 +#define PS_REG_epc 33 +#define PS_REG_cause 34 +#define PS_REG_status 35 +#define PS_REG_baddr 36 +#define PS_REG_tar 37 +#define PS_REG_dcic 38 +#define PS_REG_opcode 39 + +typedef struct { + unsigned int pc0; + unsigned int gp0; + unsigned int t_addr; + unsigned int t_size; + unsigned int d_addr; + unsigned int d_size; + unsigned int b_addr; + unsigned int b_size; + unsigned int sp_addr; + unsigned int sp_size; + unsigned int sp; + unsigned int fp; + unsigned int gp; + unsigned int ret; + unsigned int base; +} EXEC; + +typedef struct { + char header[8]; + char pad[8]; + EXEC params; + char license[64]; + char pad2[1908]; +} PSEXE; + +typedef struct { + EXEC params; + unsigned int crc32; + unsigned int flags; +} EXEPARAM; + +#endif /* PSTYPES_H */ + diff --git a/indev/psn00bdbg-mk2/testutil/serial.c b/indev/psn00bdbg-mk2/testutil/serial.c new file mode 100644 index 0000000..445a6de --- /dev/null +++ b/indev/psn00bdbg-mk2/testutil/serial.c @@ -0,0 +1,273 @@ +#include +#include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +#endif /* _WIN32 */ + +#ifdef _WIN32 +HANDLE hComm; +#else +int hComm; +#endif /* _WIN32 */ + +int commOpen( const char *port, int baud ) +{ + +#ifdef _WIN32 + + DCB dcbParams; + COMMTIMEOUTS timeouts; + + hComm = CreateFile( port, /* open serial port */ + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + if( hComm == INVALID_HANDLE_VALUE ) + { + printf( "Cannot open port %s.\n", port ); + return( 1 ); + } + + if( !GetCommState( hComm, &dcbParams ) ) /* get DCB parameters */ + { + CloseHandle( hComm ); + printf( "Cannot get comm state.\n" ); + return( 2 ); + } + + dcbParams.BaudRate = baud; /* adjust DCB parameters */ + dcbParams.ByteSize = 8; + dcbParams.StopBits = ONESTOPBIT; + dcbParams.Parity = NOPARITY; + dcbParams.fOutxCtsFlow = TRUE; + dcbParams.fOutxDsrFlow = TRUE; + dcbParams.fDtrControl = DTR_CONTROL_ENABLE; + dcbParams.fDsrSensitivity = FALSE; + dcbParams.fOutX = FALSE; + dcbParams.fInX = FALSE; + dcbParams.fErrorChar = FALSE; + dcbParams.fNull = FALSE; + dcbParams.fRtsControl = RTS_CONTROL_ENABLE;//RTS_CONTROL_HANDSHAKE; + dcbParams.fAbortOnError = FALSE; + + if( !SetCommState( hComm, &dcbParams ) ) /* apply DCB parameters */ + { + CloseHandle( hComm ); + printf( "Cannot set desired comm state.\n" ); + return( 3 ); + } + + timeouts.ReadIntervalTimeout = 100; + timeouts.ReadTotalTimeoutConstant = 50; + timeouts.ReadTotalTimeoutMultiplier = 10; + timeouts.WriteTotalTimeoutConstant = 50; + timeouts.WriteTotalTimeoutMultiplier = 10; + + if( !SetCommTimeouts( hComm, &timeouts ) ) + { + CloseHandle( hComm ); + printf( "Cannot set desired timeouts.\n" ); + return( 4 ); + } + + Sleep( 50 ); /* delay for the PS1 to receive the DSR signal */ + +#else + + struct termios tty; + + memset( &tty, 0, sizeof(struct termios) ); + + hComm = open( port, O_RDWR|O_NOCTTY ); + + if( hComm == -1 ) + { + printf( "Cannot open serial device %s.\n", port ); + return( 1 ); + } + + cfsetspeed( &tty, (speed_t)baud ); + + tty.c_cflag &= ~PARENB; + tty.c_cflag &= ~CSTOPB; + tty.c_cflag &= ~CSIZE; + tty.c_cflag |= (CS8 | CREAD | CLOCAL); + //tty.c_cflag |= (CS8 | CREAD | CLOCAL | CRTSCTS); + + tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + + tty.c_iflag &= ~(IXON | IXOFF | IXANY); + tty.c_iflag |= IGNPAR; + + tty.c_oflag &= ~OPOST; + + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 5; + + if ( tcsetattr( hComm, TCSANOW, &tty ) != 0 ) + { + printf( "Cannot set desired comms parameters.\n" ); + return( 3 ); + } + +#endif /* _WIN32 */ + + return( 0 ); + +} /* commOpen */ + +int commWriteByte( int value ) +{ +#ifdef _WIN32 + + DWORD nwritten; + + if( !WriteFile( hComm, (LPCVOID)&value, 1, &nwritten, NULL ) ) + { + return( -1 ); + } + + if( nwritten == 0 ) + { + return( -1 ); + } + +#else + + if( write( hComm, &value, 1 ) < 1 ) + { + return( -1 ); + } + +#endif /* _WIN32 */ + + return( 1 ); + +} /* commWriteByte */ + +int commWriteBytes( void *buff, int len ) +{ +#ifdef _WIN32 + + DWORD nwritten; + + if( !WriteFile( hComm, (LPCVOID)buff, len, &nwritten, NULL ) ) + { + return( -1 ); + } + + return( nwritten ); + +#else + + int nwritten; + + nwritten = write( hComm, buff, len ); + + return( nwritten ); + +#endif /* _WIN32 */ + +} /* commWriteBytes */ + +int commReadByte( void ) +{ + int recval; + +#ifdef _WIN32 + + DWORD nread; + + recval = 0; + if( !ReadFile(hComm, (LPVOID)&recval, 1, &nread, NULL) ) + { + return(-1); + } + + if( nread == 0 ) + { + return(-1); + } + +#else + + recval = 0; + if( read(hComm, &recval, 1) < 1 ) + return(-1); + +#endif /* _WIN32 */ + + return( recval ); + +} /* commReadByte */ + +int commPendingBytes(void) +{ +#ifdef _WIN32 + + DWORD dwErrorFlags; + COMSTAT ComStat; + + ClearCommError(hComm, &dwErrorFlags, &ComStat); + + return( (int)ComStat.cbInQue ); + +#else + + int bytes; + ioctl(hComm, FIONREAD, &bytes); + + return( bytes ); + +#endif /* _WIN32 */ + +} /* commPendingBytes */ + +int commReadBytes( void *buff, int len ) +{ +#ifdef _WIN32 + + DWORD nread; + + if( !ReadFile( hComm, (LPVOID)buff, len, &nread, NULL ) ) + { + return( -1 ); + } + +#else + + int nread; + + nread = read( hComm, buff, len ); + +#endif /* _WIN32 */ + + return( nread ); + +} /* commReadBytes */ + +void commClose( void ) +{ +#ifdef _WIN32 + + Sleep( 50 ); + + CloseHandle( hComm ); + +#else + + close( hComm ); + +#endif /* _WIN32 */ + +} /* commClose */ -- cgit v1.2.3