aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlameguy64 <lameguy64@github.com>2024-05-04 18:55:05 +0800
committerlameguy64 <lameguy64@github.com>2024-05-04 18:55:05 +0800
commit702bb601fb5712e2ae962a34b89204c646fe98f5 (patch)
tree765f400d1535d37c5ff4df61d01229a7dc7d8207
parent00abe5963fbead092f91935b90390aa5a9111c43 (diff)
downloadpsn00bsdk-702bb601fb5712e2ae962a34b89204c646fe98f5.tar.gz
Included PSn00bDBG-mk2 monitor and test utility
-rw-r--r--indev/psn00bdbg-mk2/doc/commands.txt321
-rw-r--r--indev/psn00bdbg-mk2/doc/overview.txt140
-rw-r--r--indev/psn00bdbg-mk2/logo.svg135
-rwxr-xr-xindev/psn00bdbg-mk2/monitor/build.sh6
-rw-r--r--indev/psn00bdbg-mk2/monitor/cmdefs.inc18
-rw-r--r--indev/psn00bdbg-mk2/monitor/makefile10
-rw-r--r--indev/psn00bdbg-mk2/monitor/monitor.asm1343
-rw-r--r--indev/psn00bdbg-mk2/monitor/sdkinst.s65
-rw-r--r--indev/psn00bdbg-mk2/monitor/sio.inc363
-rw-r--r--indev/psn00bdbg-mk2/monitor/stubinst.s185
-rw-r--r--indev/psn00bdbg-mk2/testutil/cmdefs.h36
-rw-r--r--indev/psn00bdbg-mk2/testutil/comms.c817
-rw-r--r--indev/psn00bdbg-mk2/testutil/exec.c413
-rw-r--r--indev/psn00bdbg-mk2/testutil/exec.h24
-rw-r--r--indev/psn00bdbg-mk2/testutil/main.c131
-rw-r--r--indev/psn00bdbg-mk2/testutil/main.h38
-rw-r--r--indev/psn00bdbg-mk2/testutil/makefile31
-rw-r--r--indev/psn00bdbg-mk2/testutil/makefile-linux11
-rw-r--r--indev/psn00bdbg-mk2/testutil/mips_disassembler.c663
-rw-r--r--indev/psn00bdbg-mk2/testutil/mips_disassembler.h10
-rw-r--r--indev/psn00bdbg-mk2/testutil/prompt.c518
-rw-r--r--indev/psn00bdbg-mk2/testutil/pstypes.h81
-rw-r--r--indev/psn00bdbg-mk2/testutil/serial.c273
23 files changed, 5632 insertions, 0 deletions
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 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="480"
+ height="160"
+ viewBox="0 0 127 42.333348"
+ version="1.1"
+ id="svg4999"
+ inkscape:version="0.92.1 r15371"
+ sodipodi:docname="logo.svg"
+ inkscape:export-filename="C:\Users\Lameguy64\Desktop\Projects\psdebug\icons\psdebug-banner.png"
+ inkscape:export-xdpi="108.7"
+ inkscape:export-ydpi="108.7">
+ <title
+ id="title4511">PSn00bDBG mk2 Logo</title>
+ <defs
+ id="defs4993" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.6409037"
+ inkscape:cx="248.98252"
+ inkscape:cy="90.068038"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-intersection-paths="false"
+ inkscape:object-paths="true"
+ inkscape:snap-bbox="true"
+ inkscape:snap-center="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-nodes="true"
+ inkscape:snap-global="true"
+ inkscape:window-width="1440"
+ inkscape:window-height="848"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ units="px"
+ inkscape:lockguides="false" />
+ <metadata
+ id="metadata4996">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title>PSn00bDBG mk2 Logo</dc:title>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Lameguy64</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title>PSn00bSDK Project / Meido-Tek Productions</dc:title>
+ </cc:Agent>
+ </dc:rights>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-254.66652)">
+ <path
+ style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.97349286;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 57.179711,259.17821 v 17.18692 h 5.773631 c -0.396902,-2.52219 -1.516281,-11.47627 2.677532,-11.43765 4.154274,0.0381 2.825972,8.818 2.338686,11.43765 h 5.726019 c 0,-2.5475 0.612565,-10.89684 -1.883577,-14.15061 -2.122953,-2.76729 -7.048341,-2.41673 -9.502018,-1.00656 v -2.02975 z"
+ id="path5596"
+ inkscape:connector-curvature="0" />
+ <path
+ style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.97349286;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 82.828855,255.71895 c -10.353164,0.25013 -10.721078,21.06797 0.714234,20.66283 10.000569,-0.35432 8.896917,-20.89502 -0.714234,-20.66283 z m -1.383056,5.71268 c -1.165387,3.71776 1.988956,7.56732 5.10259,5.73157 0,0 -0.09008,3.35969 -2.71075,4.0174 -5.560412,1.39549 -6.092866,-7.18149 -2.39184,-9.74897 z"
+ id="path5600"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="sssccsc" />
+ <path
+ style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.97349286;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 114.82523,255.43876 -6.43139,1.1339 0.7563,19.49459 4.79365,0.0898 0.0665,-1.56688 c 0,0 0.84813,1.16403 3.59108,1.45064 9.30033,0.97173 8.87091,-14.3259 1.34541,-14.73418 -2.83582,-0.15386 -4.43043,1.4041 -4.43043,1.4041 z m 2.46382,10.23949 c 3.64531,-0.38951 4.6306,5.87108 0.65,6.49892 -3.75349,0.59205 -4.99839,-6.03428 -0.65,-6.49892 z"
+ id="path5612"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccssccsss" />
+ <path
+ sodipodi:nodetypes="ssscccsc"
+ inkscape:connector-curvature="0"
+ id="path908"
+ d="m 99.804384,255.71668 c -10.133001,-0.0773 -10.593527,20.63351 0.262436,20.66506 10.26945,0.0298 9.09211,-20.59369 -0.262436,-20.66506 z m 0.556956,15.46392 c -0.008,9.6e-4 -0.0138,0.002 -0.021,0.004 2.35422,-2.63067 -1.289504,-8.16996 -4.519013,-6.33394 0,0 1.131029,-4.35205 4.012975,-4.09159 4.446218,0.40182 4.327948,9.1393 0.527048,10.42125 z"
+ style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.97349286;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path914"
+ d="m 26.876314,267.84943 c -0.07198,3.84917 -0.663735,8.22997 -3.673281,10.94853 -2.089478,1.66049 -4.851082,0.94991 -7.306542,1.10553 H 11.04198 v -3.42363 c 2.156559,-0.0653 4.33832,0.13885 6.477279,-0.11786 2.536377,-0.98515 3.091955,-4.09776 3.4593,-6.48869 0.300636,-3.36989 0.07856,-7.12599 -2.014955,-9.92767 -1.357242,-1.62658 -3.597582,-0.93352 -5.431053,-1.08326 H 8.6169014 v 37.37473 H 2.4115531 v -40.79837 c 5.9979405,0.0129 11.9968419,-0.0238 17.9941699,0.0171 3.625596,0.34104 5.274335,4.24515 5.941221,7.34963 0.369793,1.65348 0.52937,3.01218 0.52937,5.04391 z m 26.781736,16.11965 c -0.08017,3.9379 -0.873268,8.36119 -3.922921,11.12682 -2.089008,1.69992 -4.872113,0.98781 -7.342206,1.14121 H 31.547042 v -3.42363 c 4.070829,-0.0292 8.14743,0.0592 12.21455,-0.0456 2.781143,-0.57546 3.669767,-3.80823 4.021548,-6.25301 0.166648,-2.54728 0.292442,-5.57249 -1.565266,-7.57307 -2.159164,-1.74308 -5.042366,-1.02412 -7.593577,-1.17799 -2.19456,0.14293 -4.637761,-0.23372 -5.967246,-2.20664 -3.132732,-4.47482 -3.210319,-10.62153 -1.220061,-15.57718 0.94516,-2.55433 3.288885,-4.87136 6.197494,-4.54117 h 12.956554 v 3.42364 c -3.583967,0.0246 -7.172385,-0.0503 -10.753483,0.039 -2.74647,0.48591 -3.651617,3.67317 -3.835713,6.07941 -0.145669,2.69978 0.298311,6.06194 2.828236,7.58171 1.744887,0.64676 3.664647,0.23375 5.491578,0.35107 3.519412,0 4.7894,-0.0107 6.359655,1.56024 2.176061,2.17605 2.976739,6.12884 2.976739,9.49523 z"
+ style="font-style:normal;font-weight:normal;font-size:33.2859726px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.7429775"
+ sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.93305016px;line-height:1.25;font-family:'Bolt Bd BT';-inkscape-font-specification:'Bolt Bd BT';text-align:center;text-anchor:middle;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:14.51404381;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
+ x="56.093197"
+ y="296.23712"
+ id="text4502"><tspan
+ sodipodi:role="line"
+ id="tspan4500"
+ x="56.093197"
+ y="296.23712"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:24.19007301px;font-family:'Bolt Bd BT';-inkscape-font-specification:'Bolt Bd BT';text-align:start;text-anchor:start;fill:#000000;stroke-width:14.51404381">DBG</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.83296871px;line-height:1.25;font-family:'Bolt Bd BT';-inkscape-font-specification:'Bolt Bd BT';text-align:start;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.69978142;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
+ x="277.81357"
+ y="-116.16591"
+ id="text4512"
+ transform="rotate(90)"><tspan
+ sodipodi:role="line"
+ id="tspan4510"
+ x="277.81357"
+ y="-116.16591"
+ style="stroke-width:4.69978142">mk2</tspan></text>
+ </g>
+</svg>
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 <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#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( &regval, 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( &regval, 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(&regval, 4) < 4 )
+ {
+ puts(msg_recfail);
+ return;
+ }
+
+ if( regval == 0xFFFFFFFF )
+ {
+ break;
+ }
+
+ printf("PC=%08X ", regval);
+
+ if( commReadBytes(&regval, 4) < 4 )
+ {
+ puts(msg_recfail);
+ return;
+ }
+
+ printf("FLAG=%08X ", regval);
+
+ if( commReadBytes(&regval, 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 <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#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<head.prg_entry_count; i++ ) {
+
+ read_n = fread( &prg_heads[i], 1, sizeof(PRG_HEADER), fp );
+
+ if( prg_heads[i].flags == 4 ) {
+ continue;
+ }
+
+ if( prg_heads[i].p_vaddr < exe_taddr ) {
+ exe_taddr = prg_heads[i].p_vaddr;
+ }
+
+ if( prg_heads[i].p_vaddr > 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; i<head.prg_entry_count; i++ ) {
+
+ if( prg_heads[i].flags == 4 ) {
+ continue;
+ }
+
+ fseek( fp, prg_heads[i].p_offset, SEEK_SET );
+ read_n = fread( &binary[(int)(prg_heads[i].p_vaddr-exe_taddr)],
+ 1, prg_heads[i].p_filesz, fp );
+
+ }
+
+ memset(param, 0, sizeof(EXEC));
+ param->pc0 = 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_list_len; i++) {
+
+ if( addr_list[i] > addr_upper ) {
+
+ addr_upper = addr_list[i];
+
+ }
+
+ }
+
+ // Find the lowest chunk address
+ addr_lower = addr_upper;
+
+ for(i=0; i<addr_list_len; i++) {
+
+ if ( addr_list[i] < addr_lower ) {
+
+ addr_lower = addr_list[i];
+
+ }
+
+ }
+
+ // Pad executable size to multiples of 2KB and allocate
+ exe_size = 2048*((exe_size+2047)/2048);
+
+ exe_buff = (char*)malloc(exe_size);
+ memset(exe_buff, 0x0, exe_size);
+
+ // Load binary chunks
+ val = 0;
+ fseek(fp, 4, SEEK_SET);
+ 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);
+
+ read_n = fread(exe_buff+(uv-addr_lower), 1, val, fp);
+
+ break;
+
+ case 0x3: // Set register (skipped)
+
+ val = 0;
+ read_n = fread(&val, 1, 2, fp);
+
+ if( val == 0x90 ) {
+
+ fseek(fp, 4, SEEK_CUR);
+
+ }
+
+ break;
+
+ case 0x8: // Select unit (skipped)
+
+ fseek(fp, 1, SEEK_CUR);
+
+ break;
+ }
+
+ val = 0;
+ read_n = fread(&val, 1, 1, fp);
+
+ }
+
+ // Set program text and entrypoint
+ param->pc0 = 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 <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <conio.h>
+#include <windows.h>
+#include <winbase.h>
+#else
+#include <unistd.h>
+#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<argc; i++ )
+ {
+ if( strcasecmp( "/p", argv[i] ) == 0 ) /* specify serial port */
+ {
+ if( ( argc-i ) < 2 )
+ {
+ puts( msg_minargs );
+ return( 1 );
+ }
+ i++;
+ com_port = argv[i];
+ }
+ else if( ( strcasecmp( "/?", argv[i] ) == 0 ) ||
+ ( strcasecmp( "/h", argv[i] ) == 0 ) )
+ {
+ printf( "%s [params] [exe]\n\n", argv[0] );
+ printf( "Parameters:\n" );
+ printf( " /p <COMx> - 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 <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <conio.h>
+
+#else
+
+#include <sys/time.h>
+#include <unistd.h>
+#include <termios.h>
+#include <signal.h>
+
+#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 <addr> <len> <file> Upload memory from <file>\n");
+ printf("down <addr> <len> <file> Download memory to <file>\n");
+ printf("rv <8|16|32> <addr> Read a value from <addr>\n");
+ printf("wv <8|16|32> <addr> <val> Write a value to <addr>\n");
+ printf("sb <addr> <mode> [count] Set program breakpoint at <addr>\n");
+ printf("cb Clear all breakpoints\n");
+ printf("lb List breakpoints\n");
+ printf("sr <regname> <value> 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 <addr> Run until <addr>\n");
+ printf("tt / traceto <addr> Run until <addr> 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 <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <windows.h>
+#include <winbase.h>
+#else
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+#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 */