TRS-80 DOS - VTOS 4.0.2 for the Model I - SYS5/SYS Disassembled

Page Customization

Introduction:

VTOS 4.0.2 SYS5/SYS Disassembly - DEBUG Monitor (Model I)

SYS5/SYS is the DEBUG monitor overlay for VTOS 4.0 on the TRS-80 Model I. It is a complete interactive machine-language debugger packed into a single 1024-byte overlay slot (4E00H-51FFH). Despite its compact size, SYS5 provides a full set of debugging capabilities: register display with flags breakdown, hex/ASCII memory dump, memory modification, register modification, single-step execution with automatic breakpoint management, and a Z80 instruction decoder that handles prefixed opcodes and branch target calculation.

SYS5 is loaded into the overlay area at 4E00H-51FFH by the RST 28H overlay dispatcher in SYS0 when the user issues the DEBUG command (handled by SYS6 DUMP, IDAM 14H, which sets 430FH bit 7 and patches the RST 30H vector at 400FH). Once the debugger is active, execution enters at 4E00H whenever an RST 30H instruction (F7H) is encountered - either from a user-set breakpoint or from the BREAK key handler at 4315H. The entry code saves all 12 Z80 register pairs (including alternate registers) to a 24-byte save area at 4065H, captures the stack pointer, and enters the interactive command loop.

The breakpoint system uses RST 30H (opcode F7H) as the breakpoint marker. When a breakpoint is set, the original byte at the target address is saved in a 2-slot table at 4062H, and F7H is written in its place. A read-back verify (CP A,(HL) after writing) protects against setting breakpoints in ROM or other non-writable memory. The Go command restores all registers from the save area using a symmetric POP sequence with EX AF,AF'/EXX switches, then transfers control to the user program via a PUSH-saved-PC / RET pattern. The instruction decoder at 505DH uses a compact 3-byte classification table (mask/match/type) to determine instruction length and operand mode, supporting automatic dual breakpoints for branch instructions so execution is caught regardless of whether a branch is taken.

DEBUG Commands

KeyCommandDescription
GGoSet up to 2 breakpoints, patch BREAK key handler, restore all registers, and resume user program execution at saved PC (or entered address)
SStepToggle single-step mode (stores command character to 405EH as non-zero flag)
XExit StepClear single-step mode (XOR A clears 405EH to zero, falls through to S handler)
DDisplaySet memory dump start address (reads hex address via 51A3H, stores to 4063H)
;Scroll ForwardAdvance display address by 64 bytes (0040H), or 256 bytes (0100H) in step mode
-Scroll BackwardMove display address back by 64 bytes (FFC0H = -64)
MModify MemoryInteractive byte-by-byte memory editor: displays current value, accepts new hex value, advances to next address
RRegister ModifyChange a saved register value: reads 2-3 character register name, searches name table, reads new 16-bit value
A/HSet Address ModeStore command character to 405DH (controls ASCII column display in memory dump)
CContinueDecode instruction at PC, set sequential + branch breakpoints, resume execution (catches both taken and not-taken branches)
IInstruction StepSame as C but executes the instruction (single-step through code including subroutine calls)
UUser WaitWait for any keypress while continuously refreshing register display

SYS0 Variables Used by SYS5

Address RangePurpose
4020H - 4021H
(2 bytes)
Video RAM cursor position (written for screen output positioning)
405DH
(1 byte)
Command character / address mode storage (41H='A' enables ASCII dump column)
405EH
(1 byte)
Single-step mode flag (00H=normal display, non-zero=compact step display)
4060H - 4061H
(2 bytes)
Memory modify address / PC display tracking (highlighted in memory dump)
4062H - 4067H
(6 bytes)
Breakpoint save area (2 slots x 3 bytes: saved-byte, address-lo, address-hi)
4063H - 4064H
(2 bytes)
Display address for memory dump / scroll commands
4065H - 407CH
(24 bytes)
Register save area (12 register pairs: AF, BC, DE, HL, AF', BC', DE', HL', IX, IY, SP-AF, SP-HL in stack order)
406BH - 406CH
(2 bytes)
Saved HL register (within save area, restored last during Go)
4075H - 4076H
(2 bytes)
Saved IX register (within save area, used by instruction decoder)
4077H - 4078H
(2 bytes)
Saved IY register (within save area, used by instruction decoder)
4079H - 407AH
(2 bytes)
Saved user stack pointer (captured after LDIR register save)
407BH - 407CH
(2 bytes)
Saved program counter / resume address (adjusted for F7H breakpoint)
4315H
(1 byte)
SYS0 BREAK key handler opcode (patched to C3H=JP by Go command for debug re-entry)
4316H - 4317H
(2 bytes)
BREAK key handler target address (set to 400FH by Go command)

Major Routines

AddressName / Description
4E00HDEBUG Entry Point
Save all registers to 4065H, capture SP, check for F7H breakpoint at saved PC
4E2FHBreakpoint Cleanup
Scan 2 breakpoint slots at 405DH, restore original bytes if F7H still present
4E4BHCommand Loop
Restore SP, display registers, position cursor, read key, dispatch command
4ECFHRegister Dump Display
Clear screen, display all 12 register pairs with flags breakdown
4F2FHMemory Dump
Display 4 or 16 lines of hex dump at display address (4063H)
4F80HGo Command
Read breakpoint addresses, insert F7H markers, restore registers, resume execution
4FCAHInsert Breakpoint
Save original byte, write F7H, verify write succeeded (ROM protection)
4FDBHModify Memory
Interactive byte editor with current value display and hex input
5011HRegister Modify
Search register name table, calculate save area offset, store new value
505DHInstruction Decode
Classify opcode via table lookup, set sequential and branch breakpoints
518AHKeyboard Input
Wait for keypress with echo, return flags for separator/Enter detection
51A3HRead Hex Number
Build 16-bit value from hex digit input using shift-left-4-and-OR algorithm
51BFHHex Digit Converter
Convert ASCII character to hex nibble (0-15) via SUB/ADD chain
51D4HDisplay HL as Hex
Output HL as 4-digit hex via two calls to 51D9H
51D9HHex Byte Output
Output byte in A as 2-digit hex (high nibble first)
51E2HNibble to ASCII
Convert low 4 bits of A to ASCII hex digit and output via ROM 0033H

Cross-Reference Notes

SYS5 calls three ROM routines: 002BH (wait for keypress), 0033H (character output), and 0049H (keyboard scan). It uses SYS0 entry point 400FH (RST 30H handler / DEBUG entry vector) as the BREAK key re-entry target, patching 4315H and 4316H-4317H in the SYS0 resident area. The RST 28H instruction at 4E97H dispatches unrecognized commands back to the overlay loader. SYS5 is loaded by the SYS6 DUMP handler (IDAM 14H) which sets 430FH bit 7 and patches the RST 30H vector to enable debug mode.

Disassembly:

4E00H - DEBUG Entry Point and Register Save

Entry point for the DEBUG monitor. Reached via RST 30H (F7H breakpoint opcode or BREAK key handler). Saves all 12 Z80 register pairs (primary and alternate) to the 24-byte save area at 4065H using a PUSH-all / LDIR pattern, captures the stack pointer, and checks whether entry was via an F7H breakpoint to adjust the saved PC.

Register Save Sequence
The entry code saves all Z80 registers in a specific order. Two POP AF instructions discard the return addresses pushed by RST 30H (the RST vector itself and the overlay dispatcher). Then all 12 register pairs are pushed in order: AF, IY, IX, then EX AF,AF'/EXX to switch to alternate registers, push HL', DE', BC', AF', then EX AF,AF'/EXX back to primary registers, push HL, DE, BC, AF. This creates a 24-byte block on the stack which is then copied to the save area at 4065H via LDIR.

4E00
POP AF F1
Discard the first return address from the stack. When RST 30H executes, the Z80 pushes the PC onto the stack. The overlay dispatcher at SYS0 adds a second return address. This POP removes one of those stacked addresses.
4E01
POP AF F1
Discard the second return address from the stack. Register A is overwritten but will be restored from the user's saved AF on the stack below. After these two POPs, the stack points to the user program's state.
4E02
PUSH AF F5
Save the primary AF register pair (Accumulator and Flags) onto the stack. This is the first of 12 register pair saves.
4E03
PUSH IY FD E5
Save Register Pair IY onto the stack.
4E05
PUSH IX DD E5
Save Register Pair IX onto the stack.
4E07
EX AF,AF' 08
Switch to the alternate AF register. The primary AF has already been pushed; now the alternate set needs to be saved.
4E08
EXX D9
Switch to the alternate register set (BC', DE', HL'). Combined with EX AF,AF' above, all registers now refer to the alternate bank.
4E09
PUSH HL E5
Save Register Pair HL' (alternate HL) onto the stack.
4E0A
PUSH DE D5
Save Register Pair DE' (alternate DE) onto the stack.
4E0B
PUSH BC C5
Save Register Pair BC' (alternate BC) onto the stack.
4E0C
PUSH AF F5
Save Register Pair AF' (alternate AF) onto the stack.
4E0D
EX AF,AF' 08
Switch back to the primary AF register.
4E0E
EXX D9
Switch back to the primary register set (BC, DE, HL). All alternate registers have been pushed; primary registers are now active again.
4E0F
PUSH HL E5
Save Register Pair HL (primary) onto the stack.
4E10
PUSH DE D5
Save Register Pair DE (primary) onto the stack.
4E11
PUSH BC C5
Save Register Pair BC (primary) onto the stack.
4E12
PUSH AF F5
Save Register Pair AF (primary) onto the stack again. This is a duplicate of the AF pushed at 4E02H, providing the outermost AF entry in the save area. The 24 bytes on the stack now represent all 12 register pairs.

Block Copy to Save Area
The 24 bytes just pushed onto the stack are now copied via LDIR to the register save area at 4065H. HL is set to the current SP (the source), DE points to 4065H (the destination), and BC=0018H (24 bytes). After the copy, HL points past the 24-byte block on the stack, which is used as the new debugger stack pointer - this prevents the debugger's own stack operations from corrupting the saved user stack.

4E13
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H. This is preparation for the ADD HL,SP instruction that follows, which captures the current stack pointer value into HL.
4E16
ADD HL,SP 39
ADD the Stack Pointer to Register Pair HL. Since HL was 0000H, this copies SP into HL. The Z80 has no direct LD HL,SP instruction, so ADD HL,SP with HL=0 is the standard idiom to read SP into a general-purpose register pair.
4E17
LD DE,4065H 11 65 40
Point Register Pair DE to 4065H, the register save area in the SYS0 work area. This is the destination for the LDIR block copy.
4E1A
LD BC,0018H 01 18 00
Load Register Pair BC with 0018H (24 decimal). This is the byte count for the LDIR copy - 12 register pairs x 2 bytes each = 24 bytes.
4E1D
LDIR ED B0
Block copy 24 bytes from the stack (source at HL) to the register save area at 4065H (destination at DE). After completion, HL points past the 24-byte register block on the stack, DE points to 407DH (past the save area), and BC = 0.
4E1F
LD (4079H),HL 22 79 40
Store Register Pair HL to the saved stack pointer location at 4079H. After the LDIR, HL points past the 24 pushed register bytes on the stack. This is the user program's original stack pointer value (the stack has been consumed by reading the 24 bytes), so it is the correct SP to restore when resuming the user program.
4E22
LD SP,HL F9
Set the Stack Pointer to HL. The debugger now uses the same location as its own stack. Since the 24 register bytes have been copied to the save area, the stack space they occupied is now free for debugger use.

Breakpoint Check
The debugger now checks whether entry was caused by an F7H (RST 30H) breakpoint. If so, the saved PC needs to be decremented by 1 to point back to the breakpoint address itself (since the Z80 increments PC past the RST instruction before pushing it). This adjusted PC is stored back so the Go command will resume at the correct address.

4E23
LD HL,(407BH) 2A 7B 40
Fetch the saved program counter from 407BH into Register Pair HL. This is the address the Z80 pushed onto the stack when RST 30H executed - it points to the byte AFTER the F7H breakpoint opcode.
4E26
DEC HL 2B
DECrement Register Pair HL by 1, pointing it back to the address where the F7H breakpoint opcode was placed.
4E27
LD A,(HL) 7E
Fetch the byte at the address pointed to by HL (one byte before the saved PC) into Register A. If this byte is F7H, it confirms entry was via a breakpoint.
4E28
CP A,0F7H FE F7
Compare Register A against F7H (RST 30H opcode). If Register A equals F7H, the Z FLAG is set, confirming breakpoint entry. If Register A does not equal F7H (e.g., entry was via the BREAK key handler), the NZ FLAG is set.
4E2A
If the NZ FLAG has been set (the byte before the saved PC is not F7H, meaning entry was NOT via a breakpoint), JUMP to 4E2FH to skip the PC adjustment and proceed directly to breakpoint cleanup.
4E2C
LD (407BH),HL 22 7B 40
Store the adjusted program counter (decremented HL) back to the saved PC location at 407BH. The saved PC now points to the F7H byte itself rather than past it, so when the user resumes execution, the original instruction (after restoring the saved byte) will execute from the correct address.

4E2FH - Breakpoint Cleanup and Restore

Scans both breakpoint save slots at 405DH. For each slot, if a breakpoint address was stored (non-zero), checks whether the F7H byte is still present at that address. If so, restores the original saved byte. Zeroes out each slot after processing to prevent stale breakpoints from persisting.

4E2F
LD HL,405DH 21 5D 40
Point Register Pair HL to 405DH, the start of the breakpoint save area. Each slot is 3 bytes: saved-byte, address-lo, address-hi. Two slots occupy 405DH-4062H.
4E32
LD B,02H 06 02
Load Register B with 02H (2 decimal). This is the loop counter for processing 2 breakpoint slots.
4E34
XOR A,A AF
Set Register A to ZERO and clear all flags. Register A = 00H will be used to zero out each breakpoint slot after restoring the saved byte.
Loop Start
Process one breakpoint slot per iteration. Register B counts down from 2 to 0.
4E35
LD C,02H 0E 02
Load Register C with 02H. This sub-counter is not used as a traditional loop - it serves to track position within the 3-byte slot structure during the address extraction below.
4E37
LD E,(HL) 5E
Fetch the saved byte from the current breakpoint slot into Register E. This is the original byte that was replaced by F7H when the breakpoint was set. HL points to offset +0 of the slot.
4E38
LD (HL),A 77
Store 00H (Register A, cleared above) to the saved-byte position, zeroing it out to mark this slot as empty.
4E39
INC HL 23
INCrement Register Pair HL to point to the address-lo byte of the breakpoint slot (offset +1).
4E3A
LD D,(HL) 56
Fetch the address low byte from the breakpoint slot into Register D. Combined with Register E (saved byte), DE now holds: D=address-lo, E=saved-byte.
4E3B
LD (HL),A 77
Store 00H to the address-lo position, zeroing it out.
4E3C
INC HL 23
INCrement Register Pair HL to point to the address-hi byte of the breakpoint slot (offset +2).
4E3D
LD A,E 7B
Load Register A with the saved byte value from Register E. This prepares for the OR test below to check if the breakpoint address is non-zero.
4E3E
OR A,D B2
OR Register A (address-hi or saved-byte) with Register D (address-lo). If both the saved-byte and address-lo are zero, the Z FLAG is set, indicating this slot was empty (no breakpoint was stored).
4E3F
If the Z FLAG has been set (both bytes are zero, meaning no breakpoint was stored in this slot), JUMP to 4E48H to skip restoration and advance to the next slot.
4E41
LD A,(DE) 1A
Fetch the byte currently at the breakpoint address (pointed to by DE) into Register A. If the breakpoint is still active, this byte will be F7H (RST 30H).
4E42
CP A,0F7H FE F7
Compare Register A against F7H. If the byte at the breakpoint address is still F7H, the Z FLAG is set - the breakpoint has not been overwritten by other code, and the original byte should be restored.
4E44
If the NZ FLAG has been set (the byte at the breakpoint address is no longer F7H - it was already restored or overwritten), JUMP to 4E48H to skip restoration.
4E46
LD A,(HL) 7E
Fetch the address-hi byte of the breakpoint slot into Register A. HL currently points to offset +2 of the slot. Wait - this reads the address-hi byte, but what was supposed to happen is restoring the saved byte at the breakpoint address. Looking at the slot structure: offset +0 = saved-byte (already read into E at 4E37H, then zeroed), offset +1 = address-lo (read into D at 4E3AH), offset +2 = address-hi. The original byte to restore is in the slot's +2 position (address-hi was the saved original byte in VTOS's slot layout). Register A now holds the saved original byte.
4E47
LD (DE),A 12
Store Register A (the saved original byte from slot offset +2) to the breakpoint address (pointed to by DE). This restores the original instruction byte that was replaced by F7H, removing the breakpoint.
4E48
INC HL 23
INCrement Register Pair HL to advance past this slot's address-hi byte, pointing to the start of the next breakpoint slot.
4E49
DECrement Register B and LOOP BACK to 4E34H if not zero. Register B started at 2 (two breakpoint slots). After processing both slots, B reaches zero and the loop exits, falling through to the command loop entry at 4E4BH.
Loop End

4E4BH - Command Loop Entry Point

Main command loop for the DEBUG monitor. Restores the debugger's stack pointer from 4079H, calls the register dump display, positions the cursor to the bottom of the screen, reads a keypress, and dispatches to the appropriate command handler. All command handlers return here (via the pushed return address 4E4BH) except for the Go command which transfers control directly to the user program.

4E4B
LD SP,(4079H) ED 7B 79 40
Restore the Stack Pointer from the saved value at 4079H. This ensures the debugger stack is reset to a clean state at the start of each command loop iteration, preventing stack drift from nested calls during command processing.
4E4F
GOSUB to 4ECFH to display the register dump. This shows all 12 register pairs with their current saved values, flags breakdown for AF and AF', and a 4-line memory dump at the display address. In step mode (405EH non-zero), a compact display is shown instead.
4E52
LD HL,3FC0H 21 C0 3F
Point Register Pair HL to 3FC0H, the start of the last line of the video RAM display (row 15, column 0 in the 64x16 screen). This positions the cursor at the bottom of the screen for command input.
4E55
LD (4020H),HL 22 20 40
Store Register Pair HL to the cursor position variable at 4020H. Setting the cursor position directly allows the debugger to place input prompts at the screen bottom without scrolling.
4E58
GOSUB to 518AH to read a keypress from the keyboard with echo. Returns the character in Register A. The CARRY FLAG is set if Enter was pressed, the Z FLAG is set if a separator (comma or space) was pressed.

Command Dispatch
The command character in Register A is compared against each valid command key. The comparisons are organized with the most critical command (Go) checked first via JP Z, followed by a series of JR Z short jumps for the remaining commands. Before the comparisons, the return address 4E4BH is pushed so that each command handler can simply RET to return to the command loop.

4E5B
CP A,47H FE 47
Compare Register A against 47H (ASCII: G). If Register A equals 47H, the Z FLAG is set for the Go command.
4E5D
If the Z FLAG has been set (command is G for Go), JUMP to 4F80H to execute the Go command. This uses JP instead of JR because the Go command does not return to the command loop - it transfers control to the user program.
4E60
LD HL,4E4BH 21 4B 4E
Point Register Pair HL to 4E4BH, the command loop entry point address.
4E63
PUSH HL E5
Save the command loop address (4E4BH) onto the stack. This serves as the return address for all command handlers - when they execute RET, they return to 4E4BH to restart the command loop.
4E64
CP A,53H FE 53
Compare Register A against 53H (ASCII: S). If Register A equals 53H, the Z FLAG is set for the Step command.
4E66
If the Z FLAG has been set (command is S for Step), JUMP to 4E9AH to toggle single-step mode.
4E68
CP A,3BH FE 3B
Compare Register A against 3BH (ASCII: ;). If Register A equals 3BH, the Z FLAG is set for the Scroll Forward command.
4E6A
If the Z FLAG has been set (command is ; for scroll forward), JUMP to 4EAEH to advance the display address.
4E6C
CP A,2DH FE 2D
Compare Register A against 2DH (ASCII: -). If Register A equals 2DH, the Z FLAG is set for the Scroll Backward command.
4E6E
If the Z FLAG has been set (command is - for scroll backward), JUMP to 4EC6H to move the display address back by 64 bytes.
4E70
CP A,41H FE 41
Compare Register A against 41H (ASCII: A). If Register A equals 41H, the Z FLAG is set for the Address mode / ASCII display command.
4E72
If the Z FLAG has been set (command is A), JUMP to 4ECBH to store the command character to 405DH, enabling ASCII column display in memory dumps.
4E74
CP A,43H FE 43
Compare Register A against 43H (ASCII: C). If Register A equals 43H, the Z FLAG is set for the Continue/decode command.
4E76
If the Z FLAG has been set (command is C for Continue), JUMP to 4E82H which leads to the instruction decoder at 505DH.
4E78
CP A,44H FE 44
Compare Register A against 44H (ASCII: D). If Register A equals 44H, the Z FLAG is set for the Display command.
4E7A
If the Z FLAG has been set (command is D for Display), JUMP to 4EA8H to read a hex address and set the display address.
4E7C
CP A,48H FE 48
Compare Register A against 48H (ASCII: H). If Register A equals 48H, the Z FLAG is set for the alternate address mode command.
4E7E
If the Z FLAG has been set (command is H), JUMP to 4ECBH to store 'H' to 405DH, selecting hex-only display mode (no ASCII column).
4E80
CP A,49H FE 49
Compare Register A against 49H (ASCII: I). If Register A equals 49H, the Z FLAG is set for the Instruction step command. The comparison at 4E74H checked for 'C' first; if not 'C', this checks for 'I' which shares the same handler.
4E82
If the Z FLAG has been set (command is I for Instruction step, or fell through from the C check at 4E76H), JUMP to 505DH to execute the instruction decoder. Both C and I reach 505DH but the saved command character distinguishes them inside the decoder (C sets breakpoints only; I also executes).
4E85
CP A,4DH FE 4D
Compare Register A against 4DH (ASCII: M). If Register A equals 4DH, the Z FLAG is set for the Modify Memory command.
4E87
If the Z FLAG has been set (command is M for Modify), JUMP to 4FDBH to enter the interactive memory modification handler.
4E8A
CP A,52H FE 52
Compare Register A against 52H (ASCII: R). If Register A equals 52H, the Z FLAG is set for the Register Modify command.
4E8C
If the Z FLAG has been set (command is R for Register modify), JUMP to 5011H to enter the register modification handler.
4E8F
CP A,55H FE 55
Compare Register A against 55H (ASCII: U). If Register A equals 55H, the Z FLAG is set for the User wait command.
4E91
If the Z FLAG has been set (command is U for User wait), JUMP to 4E9EH to enter the wait-for-keypress loop with continuous register display refresh.
4E93
CP A,58H FE 58
Compare Register A against 58H (ASCII: X). If Register A equals 58H, the Z FLAG is set for the Exit step mode command.
4E95
If the Z FLAG has been set (command is X for Exit step), JUMP to 4E99H to clear the step mode flag.

Unrecognized Command
If no command matched, the RST 28H at 4E97H invokes the overlay dispatcher, which returns to the DOS command loop. The RET at 4E98H is the standard RST 28H stub terminator. This exits the debugger and returns to VTOS READY.

4E97
RST 28H EF
Invoke the RST 28H overlay dispatcher. Since no SVC code was loaded into Register A before this call, the dispatcher interprets whatever value is in A from the last CP instruction (58H XORed result). This effectively exits the debugger by returning control to the SYS0 overlay system.
4E98
RET C9
Return. This RET is reached only if RST 28H returns (which it normally does not for an unrecognized SVC). It serves as a safety fallback to the command loop via the pushed 4E4BH address.

4E99H - Exit Step Mode (X) and Step Mode Toggle (S)

The X command clears the step mode flag by setting A to zero via XOR and falling through to the S handler. The S handler stores the current value of Register A to the step mode flag at 405EH. When called directly for S, Register A contains 53H ('S', non-zero = step mode active). When reached from X, Register A is 00H (step mode disabled).

4E99
XOR A,A AF
Set Register A to ZERO and clear all flags. This clears the step mode flag value to 00H before falling through to the LD instruction below.
4E9A
LD (405EH),A 32 5E 40
Store Register A to the single-step mode flag at 405EH. When reached from the X command, A=00H (step mode off). When reached from the S command dispatch at 4E66H, A=53H ('S', non-zero = step mode on). A non-zero value at 405EH tells the register dump display routine at 4ECFH to use compact step mode format instead of the full-screen display.
4E9D
RET C9
Return to the command loop at 4E4BH (via the pushed return address).

4E9EH - Wait for Keypress (U Command)

The U (User wait) command loops waiting for a keypress while continuously refreshing the register display. This allows the user to observe register values updated by interrupt-driven routines or DMA transfers while waiting. Each iteration calls ROM 002BH (wait for keypress), checks if a key was returned, and if not, refreshes the register dump and loops back.

4E9E
GOSUB to ROM routine at 002BH to wait for a keypress. Returns the character in Register A. If no key is pressed, returns A=00H.
4EA1
OR A,A B7
OR Register A with itself. This sets the Z FLAG if A=00H (no key pressed), NZ if a key was pressed.
4EA2
RET NZ C0
If the NZ FLAG has been set (a key was pressed), RETurn to the command loop at 4E4BH. The keypress is discarded - the U command only waits, it does not process the key.
4EA3
GOSUB to 4ECFH to refresh the register dump display. This allows the user to see any changes to memory or register values that might occur during interrupt processing.
4EA6
LOOP BACK to 4E9EH to poll for another keypress. This creates a continuous poll/display loop until a key is pressed.

4EA8H - Display Memory Command (D)

The D (Display) command reads a hex address from the keyboard and stores it as the new memory dump display address at 4063H. If no address is entered (just Enter or separator), the current display address is retained.

4EA8
GOSUB to 51A3H to read a hexadecimal number from keyboard input. Returns the 16-bit value in Register Pair HL. The Z FLAG is set if no digits were entered. The CARRY FLAG is set if Enter terminated input.
4EAB
RET Z C8
If the Z FLAG has been set (no hex digits were entered), RETurn to the command loop without changing the display address.
4EAC
JUMP to 4EC2H to store the entered address (in HL) to the display address variable at 4063H and return.

4EAEH - Scroll Forward (;) and Scroll Backward (-)

The semicolon command advances the memory dump display address by 64 bytes (0040H) in normal mode, or by 256 bytes (0100H) in step mode. The minus command moves the display address backward by 64 bytes (FFC0H = -64 in 16-bit two's complement).

4EAE
LD BC,0040H 01 40 00
Load Register Pair BC with 0040H (64 decimal). This is the default scroll-forward increment - 4 lines of 16 bytes each, matching the 4-line hex dump display in normal mode.
4EB1
LD HL,(4063H) 2A 63 40
Fetch the current display address from 4063H into Register Pair HL.
4EB4
LD A,(405EH) 3A 5E 40
Fetch the single-step mode flag from 405EH into Register A. If non-zero, step mode is active and a larger scroll increment is used.
4EB7
OR A,A B7
OR Register A with itself. Sets the Z FLAG if A=00H (normal mode), NZ if step mode is active.
4EB8
If the Z FLAG has been set (normal mode, not stepping), JUMP to 4EC1H to add the 64-byte increment and store the result.
4EBA
LD C,00H 0E 00
Load Register C with 00H. This changes BC from 0040H to 0000H - but the next instructions adjust B to create the step-mode increment of 0100H (256 bytes = 16 lines of 16 bytes).
4EBC
LD A,B 78
Load Register A with Register B (currently 00H from the LD BC,0040H - the high byte was zero).
4EBD
OR A,A B7
OR Register A with itself. Since B=00H, A=00H, and the Z FLAG is set.
4EBE
If the NZ FLAG has been set (B was non-zero), JUMP to 4EC1H. Since B=00H here, this branch is not taken.
4EC0
INC B 04
INCrement Register B from 00H to 01H. With C=00H, Register Pair BC is now 0100H (256 decimal). In step mode, the display scrolls by 256 bytes (one full page of the 16-line compact display).
4EC1
ADD HL,BC 09
ADD Register Pair BC (scroll increment) to Register Pair HL (current display address). The result is the new display address.
4EC2
LD (4063H),HL 22 63 40
Store the new display address in Register Pair HL to the display address variable at 4063H.
4EC5
RET C9
Return to the command loop at 4E4BH.
4EC6
LD BC,0FFC0H 01 C0 FF
Load Register Pair BC with FFC0H (-64 in 16-bit two's complement). This is the scroll-backward decrement, moving the display address back by 64 bytes (4 lines).
4EC9
JUMP to 4EB1H to load the current display address and add the negative offset, then store the result. The step-mode check will still execute but the negative value in BC will produce the correct backward scroll regardless.

4ECBH - Set Address Mode (A/H Command)

The A and H commands store their respective command character to the address mode variable at 405DH. When 405DH contains 41H ('A'), the memory dump display includes an ASCII column alongside the hex bytes. When it contains 48H ('H') or any other non-'A' value, only hex bytes are displayed.

4ECB
LD (405DH),A 32 5D 40
Store Register A (the command character: 41H for 'A' or 48H for 'H') to the address mode variable at 405DH. The memory dump routine at 5131H checks this value to decide whether to display an ASCII interpretation column.
4ECE
RET C9
Return to the command loop at 4E4BH.

4ECFH - Register Dump Display

Displays all 12 register pairs from the save area at 4065H with their current values. In normal mode (405EH=00H), clears the screen first and shows a full display with 4 lines of memory dump. In step mode (405EH non-zero), shows a compact 16-line memory dump without clearing the screen. For AF and AF' register pairs, a flags bit breakdown is displayed showing the state of each flag bit. For all other register pairs, a memory dump line is shown at the address contained in that register pair.

4ECF
LD A,(405EH) 3A 5E 40
Fetch the single-step mode flag from 405EH into Register A. A non-zero value indicates step mode is active.
4ED2
OR A,A B7
OR Register A with itself. Sets the Z FLAG if A=00H (normal mode), NZ if step mode is active.
4ED3
If the NZ FLAG has been set (step mode is active), JUMP to 4F45H to display the compact step-mode format instead of the full register dump.
4ED5
LD A,1CH 3E 1C
Load Register A with 1CH (28 decimal). This is the TRS-80 screen control code for "clear screen" - it clears the video RAM and homes the cursor.
4ED7
GOSUB to ROM routine at 0033H to output the clear-screen control code. The screen is now blank and the cursor is at the top-left (3C00H).
4EDA
LD HL,4065H 21 65 40
Point Register Pair HL to 4065H, the start of the register save area. This pointer will walk through the 24-byte save area, reading 2 bytes per register pair.
4EDD
PUSH HL E5
Save the save area pointer (4065H) onto the stack. The EX (SP),HL technique at 4EEDH will swap between the name table pointer and the save area pointer each iteration.
4EDE
LD HL,4F54H 21 54 4F
Point Register Pair HL to 4F54H, the register name display table. This table contains 12 entries of 3 bytes each: 2 ASCII name characters plus a separator character (space for primary registers, apostrophe for alternate registers).
4EE1
LD B,0CH 06 0C
Load Register B with 0CH (12 decimal). This is the loop counter for displaying 12 register pairs.
4EE3
JUMP to 4EEAH to begin the first register display iteration, skipping the carriage return that precedes subsequent iterations.
4EE5
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return). This moves the cursor to the beginning of the next line before displaying the next register pair.
4EE7
GOSUB to ROM routine at 0033H to output the carriage return character.
4EEA
GOSUB to 51F6H to print two characters from the name table. This subroutine reads and outputs two consecutive bytes from the address in HL (the register name, e.g., "AF", "BC", "DE"), advancing HL past them.
Loop Start
Display one register pair per iteration. Register B counts down from 12.
4EED
EX (SP),HL E3
Exchange HL with the value on top of the stack. Before this instruction, HL points into the name table (after the 2 name chars), and the stack holds the save area pointer. After this instruction, HL points into the save area and the name table pointer is on the stack. This swap technique avoids needing a separate register pair to track two pointers.
4EEE
LD E,(HL) 5E
Fetch the low byte of the current register pair from the save area into Register E. HL points to the low byte of the pair (e.g., the F byte of the AF pair at 4065H).
4EEF
INC HL 23
INCrement Register Pair HL to point to the high byte of the register pair in the save area.
4EF0
LD D,(HL) 56
Fetch the high byte of the current register pair from the save area into Register D. Register Pair DE now holds the complete 16-bit register value (D=high, E=low).
4EF1
INC HL 23
INCrement Register Pair HL to advance the save area pointer to the next register pair.
4EF2
PUSH HL E5
Save the updated save area pointer onto the stack (it replaces the previous one). The name table pointer is now buried one level deeper on the stack.
4EF3
EX DE,HL EB
Exchange Register Pairs DE and HL. Now HL holds the register value (for display) and DE is free.
4EF4
LD A,3DH 3E 3D
Load Register A with 3DH (ASCII: =). This is the separator between the register name and its value in the display format "AF= HH LL".
4EF6
GOSUB to ROM routine at 0033H to output the equals sign character.
4EF9
GOSUB to 51F2H to print a space character. This adds spacing between the equals sign and the hex value.
4EFC
LD A,H 7C
Load Register A with Register H, the high byte of the register pair value. For display as "HH LL", the high byte is printed first.
4EFD
GOSUB to 51EFH to display Register A as a 2-digit hex byte followed by a space. This outputs the high byte of the register pair.
4F00
LD A,L 7D
Load Register A with Register L, the low byte of the register pair value.
4F01
GOSUB to 51EFH to display Register A as a 2-digit hex byte followed by a space. This outputs the low byte of the register pair.

AF/AF' Detection
The code now checks whether the current register pair is AF or AF' to decide whether to display a flags bit breakdown. The detection uses a clever bitmask trick: (B AND 0BH) == 08H is true only when B=0CH (first iteration = AF) or B=08H (fifth iteration = AF'). The mask 0BH (binary 00001011) eliminates bit 2, making binary 1100 (0CH) and 1000 (08H) both produce 1000 (08H) after the AND. No other value of B in the range 01H-0CH satisfies this condition.

4F04
LD A,B 78
Load Register A with Register B, the current loop counter. B=0CH for AF (first pair), 0BH for BC, 0AH for DE, 09H for HL, 08H for AF', 07H for BC', etc.
4F05
AND A,0BH E6 0B
AND Register A with 0BH (binary 00001011). This masks out bit 2, leaving only bits 3, 1, and 0. For B=0CH: 0CH AND 0BH = 08H. For B=08H: 08H AND 0BH = 08H. All other values produce different results.
4F07
CP A,08H FE 08
Compare Register A against 08H. If the masked value equals 08H, the Z FLAG is set - this register pair is AF or AF' and should get a flags breakdown display.
4F09
If the NZ FLAG has been set (this register pair is NOT AF or AF'), JUMP to 4F27H to display a memory dump line at the register's address instead of a flags breakdown.

Flags Bit Breakdown Display
For AF and AF' register pairs, the flags byte (in Register L, the low byte of the pair) is displayed as a series of 8 flag letters from the "SZ1H1PNC" table at 4F78H. Each bit is tested via SLA C (shift left into carry), and the corresponding flag letter is displayed if the bit is set, or '-' if the bit is clear. The flags are processed from bit 7 (Sign) down to bit 0 (Carry).

4F0B
LD C,L 4D
Load Register C with Register L, the flags byte from the register pair. Register C will be shifted left bit by bit to test each flag.
4F0C
PUSH BC C5
Save Register Pair BC onto the stack. Register B (loop counter for the outer 12-pair loop) must be preserved while the inner flags loop uses B as its own counter.
4F0D
LD HL,4F78H 21 78 4F
Point Register Pair HL to 4F78H, the flags bit name table. This 8-byte table contains "SZ1H1PNC" representing Sign, Zero, fixed-1, Half-carry, fixed-1, Parity/Overflow, Subtract (N), and Carry.
4F10
LD B,08H 06 08
Load Register B with 08H (8 decimal). This is the loop counter for 8 flag bits.
4F12
SLA C CB 21
Shift Left Arithmetic Register C. The highest bit of C is shifted into the CARRY FLAG, and a 0 is shifted into bit 0. This extracts one flag bit per iteration, starting with bit 7 (Sign flag) and proceeding down to bit 0 (Carry flag).
Loop Start
Test and display one flag bit per iteration.
4F14
LD A,(HL) 7E
Fetch the flag letter from the name table into Register A. On the first iteration, this is 'S' (Sign); on the second, 'Z' (Zero); and so on through "SZ1H1PNC".
4F15
If the CARRY FLAG has been set (the current flag bit was 1), JUMP to 4F19H to display the flag letter as-is, indicating the flag is set.
4F17
LD A,2DH 3E 2D
Load Register A with 2DH (ASCII: -). The flag bit was 0 (not set), so a dash is displayed instead of the flag letter.
4F19
GOSUB to ROM routine at 0033H to output the flag letter or dash character.
4F1C
INC HL 23
INCrement Register Pair HL to advance to the next flag letter in the name table.
4F1D
DECrement Register B and LOOP BACK to 4F12H if not zero. After 8 iterations, all 8 flag bits have been displayed and the loop exits.
Loop End
4F1F
POP BC C1
Restore Register Pair BC from the stack. Register B is back to the outer loop counter (12-pair count), and C contains the shifted-out remains of the flags byte.
4F20
LD A,0ECH 3E EC
Load Register A with ECH (236 decimal). This is a TRS-80 block graphics character used as a visual separator between the flags breakdown and the next register pair's display line.
4F22
GOSUB to ROM routine at 0033H to output the block character separator.
4F25
JUMP to 4F2AH to rejoin the main register display loop, skipping the non-flags memory dump call.
4F27
GOSUB to 5131H to display a memory dump line at the address held in the current register pair. For non-AF register pairs, this shows the memory contents at the address the register points to, giving the user a quick view of what each pointer register references. HL contains the register pair value from the EX DE,HL at 4EF3H.
4F2A
POP HL E1
Restore Register Pair HL from the stack. This pops the save area pointer that was pushed at 4EF2H.
4F2B
EX (SP),HL E3
Exchange HL with the value on top of the stack. This swaps the save area pointer (now in HL after the POP) with the name table pointer (which was pushed by the original PUSH at 4EDDH and swapped into the stack at each 4EEDH). HL now holds the name table pointer for the next iteration.
4F2C
DECrement Register B and LOOP BACK to 4EE5H if not zero. After all 12 register pairs have been displayed, B reaches zero and the loop exits.
Loop End
4F2E
POP HL E1
Restore Register Pair HL from the stack. This pops the final save area pointer, cleaning up the stack after the register display loop.

Memory Dump Display
After the register pairs, the display shows 4 lines of hex memory dump at the current display address (4063H). Each line shows: a C7H line marker character, the 4-digit hex address, an "=>" separator, and 16 hex bytes. The display address advances by 16 bytes per line.

4F2F
LD HL,(4063H) 2A 63 40
Fetch the current memory dump display address from 4063H into Register Pair HL.
4F32
LD B,04H 06 04
Load Register B with 04H (4 decimal). This is the loop counter for displaying 4 lines of memory dump in normal (non-step) mode.
4F34
LD A,0C7H 3E C7
Load Register A with C7H (199 decimal). This is a TRS-80 block graphics character used as a line marker at the start of each memory dump line, visually distinguishing dump lines from register lines.
Loop Start
Display one memory dump line per iteration.
4F36
GOSUB to ROM routine at 0033H to output the C7H line marker character.
4F39
GOSUB to 51D4H to display Register Pair HL as a 4-digit hexadecimal address. This shows the starting address of the current dump line.
4F3C
GOSUB to 51F2H to print a space character after the address.
4F3F
GOSUB to 5131H to display one line of memory dump: "=>" separator followed by 16 hex bytes (with optional ASCII column if 405DH='A'). HL is advanced by 16 bytes to point to the next line's starting address.
4F42
DECrement Register B and LOOP BACK to 4F34H if not zero. After 4 lines, B reaches zero and the loop exits.
Loop End
4F44
RET C9
Return to the caller (the command loop at 4E4BH).

4F45H - Step Mode Compact Display

When single-step mode is active (405EH non-zero), this alternate display is used instead of the full register dump. It positions the cursor at 3BFFH (near the bottom of the screen), page-aligns the display address, and shows 16 lines of hex dump (256 bytes total) without clearing the screen. This allows the user to see both the instruction being stepped and the surrounding memory context.

4F45
LD HL,3BFFH 21 FF 3B
Point Register Pair HL to 3BFFH. This is one byte before the start of video RAM (3C00H). Setting the cursor here causes the next character output to scroll the screen or position at the very start of the display area.
4F48
LD (4020H),HL 22 20 40
Store Register Pair HL (3BFFH) to the cursor position variable at 4020H.
4F4B
LD HL,(4063H) 2A 63 40
Fetch the current memory dump display address from 4063H into Register Pair HL.
4F4E
LD L,00H 2E 00
Load Register L with 00H, page-aligning the display address. This forces the dump to start at a 256-byte page boundary (e.g., 5000H instead of 5037H), ensuring the display covers a clean address range.
4F50
LD B,10H 06 10
Load Register B with 10H (16 decimal). This is the loop counter for displaying 16 lines of memory dump (16 lines x 16 bytes = 256 bytes = one full page).
4F52
JUMP to 4F34H to reuse the memory dump display loop with 16 iterations instead of 4. The loop at 4F34H displays line marker, address, and 16 hex bytes per line, controlled by the B counter.

4F54H - Register Name Display Table

Data table containing the display names for all 12 register pairs. Each entry is 3 bytes: 2 ASCII characters for the register name, followed by a separator character. Primary registers use space (20H) as the separator, alternate registers use apostrophe (27H). The order matches the save area layout at 4065H: AF, BC, DE, HL, AF', BC', DE', HL', IX, IY, SP, PC.

4F54
DEFM "AF " 41 46 20
Register name "AF" with space separator. Primary AF register pair (Accumulator and Flags). Save area offset: 4065H-4066H.
4F57
DEFM "BC " 42 43 20
Register name "BC" with space separator. Primary BC register pair. Save area offset: 4067H-4068H.
4F5A
DEFM "DE " 44 45 20
Register name "DE" with space separator. Primary DE register pair. Save area offset: 4069H-406AH.
4F5D
DEFM "HL " 48 4C 20
Register name "HL" with space separator. Primary HL register pair. Save area offset: 406BH-406CH.
4F60
DEFM "AF'" 41 46 27
Register name "AF" with apostrophe separator. Alternate AF' register pair. Save area offset: 406DH-406EH.
4F63
DEFM "BC'" 42 43 27
Register name "BC" with apostrophe separator. Alternate BC' register pair. Save area offset: 406FH-4070H.
4F66
DEFM "DE'" 44 45 27
Register name "DE" with apostrophe separator. Alternate DE' register pair. Save area offset: 4071H-4072H.
4F69
DEFM "HL'" 48 4C 27
Register name "HL" with apostrophe separator. Alternate HL' register pair. Save area offset: 4073H-4074H.
4F6C
DEFM "IX " 49 58 20
Register name "IX" with space separator. Index register IX. Save area offset: 4075H-4076H.
4F6F
DEFM "IY " 49 59 20
Register name "IY" with space separator. Index register IY. Save area offset: 4077H-4078H.
4F72
DEFM "SP " 53 50 20
Register name "SP" with space separator. Stack Pointer. Save area offset: 4079H-407AH.
4F75
DEFM "PC " 50 43 20
Register name "PC" with space separator. Program Counter. Save area offset: 407BH-407CH.

4F78H - Flags Bit Name Table

8-byte data table used by the flags bit breakdown display at 4F0BH. Contains one character per flag bit, ordered from bit 7 (most significant) to bit 0 (least significant): S=Sign, Z=Zero, 1=fixed (always 1), H=Half-carry, 1=fixed (always 1), P=Parity/Overflow, N=Subtract, C=Carry.

4F78
DEFM "SZ1H1PNC" 53 5A 31 48 31 50 4E 43
Flags bit name string. Bit 7: S (Sign flag), Bit 6: Z (Zero flag), Bit 5: 1 (fixed bit, always set), Bit 4: H (Half-carry flag), Bit 3: 1 (fixed bit, always set), Bit 2: P (Parity/Overflow flag), Bit 1: N (Subtract flag, set after SUB/NEG/etc.), Bit 0: C (Carry flag).

4F80H - Go Command (G)

The Go command reads up to 2 breakpoint addresses from the keyboard, inserts F7H (RST 30H) breakpoint markers at those addresses (saving the original bytes), patches the BREAK key handler at 4315H-4317H to vector to 400FH (DEBUG re-entry), then restores all 12 register pairs from the save area and transfers control to the user program at the saved PC address.

4F80
LD B,02H 06 02
Load Register B with 02H (2 decimal). This is the counter for reading up to 2 breakpoint addresses.
4F82
LD DE,4062H 11 62 40
Point Register Pair DE to 4062H, the breakpoint save area. Each breakpoint slot stores the saved original byte and the breakpoint address (3 bytes per slot, stored in reverse: byte first, then address high, address low).
4F85
GOSUB to 51A3H to read a hexadecimal number from the keyboard. Returns the 16-bit value in Register Pair HL. The Z FLAG is set if no digits were entered. The CARRY FLAG is set if Enter terminated input.
4F88
If the Z FLAG has been set (no hex digits were entered for the first address), JUMP to 4F8DH to skip setting a new PC address. The current saved PC at 407BH will be used as-is.
4F8A
LD (407BH),HL 22 7B 40
Store the entered address in Register Pair HL to the saved program counter at 407BH. This sets the resume address for when the Go command transfers control to the user program.
4F8D
If the CARRY FLAG has been set (Enter was pressed, ending input), JUMP to 4F99H to skip breakpoint entry and proceed directly to patching the BREAK handler and restoring registers.
4F8F
GOSUB to 51A3H to read the next hexadecimal breakpoint address from keyboard input.
4F92
PUSH AF F5
Save the flags from the hex input routine onto the stack. The Z and CARRY flags indicate whether input was provided and how it was terminated.
4F93
If the NZ FLAG has been set (a hex address was entered), GOSUB to 4FCAH to insert a breakpoint at the address in HL. The CALL NZ conditional means the breakpoint insertion is skipped if no address was entered.
4F96
POP AF F1
Restore the flags from the stack.
4F97
DECrement Register B and LOOP BACK to 4F8DH if not zero. This allows entering a second breakpoint address. After 2 iterations (or if Enter was pressed), the loop exits.

Patch Break Key Handler
Before transferring control to the user program, the Go command patches the BREAK key handler at 4315H to create a JP 400FH instruction. This ensures that pressing BREAK during user program execution will re-enter the DEBUG monitor via the RST 30H vector at 400FH, providing a software interrupt mechanism independent of F7H breakpoints.

4F99
LD HL,400FH 21 0F 40
Point Register Pair HL to 400FH, the RST 30H handler address in SYS0. This is the DEBUG entry vector.
4F9C
LD (4316H),HL 22 16 43
Store Register Pair HL (400FH) to the BREAK key handler target address at 4316H-4317H. When 4315H contains C3H (JP), the ISR's BREAK key detection will execute JP 400FH to re-enter the debugger.
4F9F
LD A,0C3H 3E C3
Load Register A with C3H, the opcode for the JP (Jump) instruction.
4FA1
LD (4315H),A 32 15 43
Store C3H to the BREAK key handler opcode at 4315H. This completes the JP 400FH instruction at 4315H-4317H, arming the BREAK key to re-enter the debugger. The ISR at SYS0 checks this byte: when it contains C3H, the BREAK key path is active.

Register Restoration
The register restoration sequence reads the save area BACKWARD from 407AH (the end of the save area), pushing each 2-byte register pair value onto the stack. Then a symmetric POP sequence with EX AF,AF'/EXX switches restores all registers to their saved values. The final transfer to the user program uses PUSH HL (saved PC) / LD HL,(406BH) (restore final HL) / RET to jump to the user program's address.

4FA4
LD HL,407AH 21 7A 40
Point Register Pair HL to 407AH, the last byte of the save area (high byte of the SP pair, the 11th register pair in the save area). The backward read starts here and works down to 4065H.
4FA7
LD B,0BH 06 0B
Load Register B with 0BH (11 decimal). This is the loop counter for pushing 11 register pairs onto the stack. The 12th pair (AF at the beginning of the save area) will be handled by the POP AF at 4FB0H.
4FA9
LD D,(HL) 56
Fetch the high byte of the current register pair from the save area into Register D. HL points to the high byte (reading backward from the end).
Loop Start
Read one register pair from the save area and push it onto the stack.
4FAA
DEC HL 2B
DECrement Register Pair HL to point to the low byte of the current register pair.
4FAB
LD E,(HL) 5E
Fetch the low byte of the current register pair into Register E. Register Pair DE now holds the complete 16-bit value.
4FAC
DEC HL 2B
DECrement Register Pair HL to point to the high byte of the next register pair (moving backward through the save area).
4FAD
PUSH DE D5
Save the register pair value onto the stack. These pushed values will be popped in the correct order below to restore each register.
4FAE
DECrement Register B and LOOP BACK to 4FA9H if not zero. After 11 iterations, the stack contains (from top to bottom): AF, BC, DE, HL, AF', BC', DE', HL', IX, IY, SP-value. The 12th pair (the outer AF at 4065H-4066H) is at the top.
Loop End

POP Sequence
The stack now contains all 11 register pairs (AF was read first from the end, so it is on top). The POPs restore registers in the exact reverse of the save order: primary AF, BC, DE, HL first, then switch to alternates via EX AF,AF'/EXX, restore alternate AF', BC', DE', HL', switch back, restore IX, IY, and finally SP and PC.

4FB0
POP AF F1
Restore the primary AF register pair from the stack. This is the outermost AF that was saved first (at 4E12H) and read last (from the beginning of the save area).
4FB1
POP BC C1
Restore the primary BC register pair from the stack.
4FB2
POP DE D1
Restore the primary DE register pair from the stack.
4FB3
POP HL E1
Restore the primary HL register pair from the stack.
4FB4
EX AF,AF' 08
Switch to the alternate AF register. The primary registers have been restored; now the alternate set will be popped.
4FB5
EXX D9
Switch to the alternate register set (BC', DE', HL'). All register references now affect the alternate bank.
4FB6
POP AF F1
Restore the alternate AF' register pair from the stack.
4FB7
POP BC C1
Restore the alternate BC' register pair from the stack.
4FB8
POP DE D1
Restore the alternate DE' register pair from the stack.
4FB9
POP HL E1
Restore the alternate HL' register pair from the stack.
4FBA
EX AF,AF' 08
Switch back to the primary AF register.
4FBB
EXX D9
Switch back to the primary register set. All registers are now restored to their saved values except IX, IY, SP, HL, and PC.
4FBC
POP IX DD E1
Restore the IX index register from the stack.
4FBE
POP IY FD E1
Restore the IY index register from the stack.
4FC0
POP HL E1
Pop the saved SP value from the stack into HL. This is the user program's original stack pointer.
4FC1
LD SP,HL F9
Set the Stack Pointer to the user program's saved SP value. The debugger's stack is now gone - SP points to the user program's stack.
4FC2
LD HL,(407BH) 2A 7B 40
Fetch the saved program counter (resume address) from 407BH into Register Pair HL. This is the address where the user program will continue execution.
4FC5
PUSH HL E5
Save the resume address onto the user program's stack. The RET instruction at 4FC9H will pop this address and jump to it.
4FC6
LD HL,(406BH) 2A 6B 40
Fetch the saved HL register value from 406BH (the HL position in the save area). This is the final register restoration - HL could not be restored earlier because it was being used as a working register.
4FC9
RET C9
Return. Pops the resume address from the stack (pushed at 4FC5H) and jumps to it, transferring control to the user program with all registers restored to their saved values.

4FCAH - Insert Breakpoint

Inserts a breakpoint at the address in HL. Saves the original byte at (HL) into the breakpoint save area pointed to by DE, writes F7H (RST 30H) to that address, then performs a read-back verify to confirm the write succeeded. If the write failed (ROM or write-protected memory), jumps to the breakpoint cleanup routine at 4E2FH. Records the breakpoint address in the save area.

4FCA
LD A,(HL) 7E
Fetch the byte currently at the breakpoint address (pointed to by HL) into Register A. This is the original instruction byte that will be saved before being replaced by F7H.
4FCB
LD (DE),A 12
Store the original byte (Register A) to the breakpoint save area slot pointed to by DE. This saves the byte so it can be restored when the breakpoint is removed.
4FCC
DEC DE 1B
DECrement Register Pair DE. The save area is filled in reverse order (descending addresses).
4FCD
LD A,0F7H 3E F7
Load Register A with F7H, the RST 30H opcode. This is the breakpoint marker byte that will replace the original instruction.
4FCF
LD (HL),A 77
Store F7H (Register A) to the breakpoint address (pointed to by HL), overwriting the original instruction byte with the breakpoint opcode.
4FD0
CP A,(HL) BE
Compare Register A (F7H) with the byte now at the breakpoint address. If the write succeeded, the byte will be F7H and the Z FLAG is set. If the address is in ROM or write-protected memory, the byte will be unchanged and the NZ FLAG is set.
4FD1
If the NZ FLAG has been set (the write to the breakpoint address failed - the memory is read-only), JUMP to 4E2FH to clean up any previously set breakpoints and return to the command loop. This prevents the debugger from leaving stale breakpoint data when a write to ROM is attempted.
4FD4
LD A,H 7C
Load Register A with Register H, the high byte of the breakpoint address.
4FD5
LD (DE),A 12
Store the address high byte to the save area slot (DE points to the address-hi position after the DEC at 4FCCH).
4FD6
DEC DE 1B
DECrement Register Pair DE to point to the address-lo position in the save area.
4FD7
LD A,L 7D
Load Register A with Register L, the low byte of the breakpoint address.
4FD8
LD (DE),A 12
Store the address low byte to the save area slot.
4FD9
DEC DE 1B
DECrement Register Pair DE to point to the next save area slot's saved-byte position, ready for a potential second breakpoint.
4FDA
RET C9
Return to the caller (the Go command loop at 4F93H or the instruction decoder at 505DH).

4FDBH - Modify Memory (M Command)

Interactive memory modification command. Reads a starting hex address from the user, then enters a loop: displays the current address and byte value, prints a dash separator, reads a new hex value from the user, writes it to memory, and advances to the next address. If the user presses Enter without typing a value, the command exits. If no input is provided for the new value (separator only), the byte is left unchanged and the next address is shown. Entered from the command dispatcher at 4E87H when Register A = 4DH (ASCII M). Returns to the command loop at 4E4BH via the PUSH at 4E63H.

4FDB
LD HL,(4060H) 2A 60 40
Fetch the current memory modify/display address from 4060H (2-byte pointer used by both the M command and the PC-highlight display at 5165H) into Register Pair HL. On first entry this holds the last address used; on re-entry from the loop below at 500FH it holds the updated address.
4FDE
GOSUB to the hex number input routine at 51A3H to read a hex address from the keyboard. On entry, Register Pair HL holds the default address (current value of 4060H). On return: if the user typed a hex value, Register Pair HL holds the new address and the NZ FLAG is set. If the user pressed Enter without typing anything, the CARRY FLAG is set. If the user typed a separator (comma or space) without digits, the Z FLAG is set and HL is unchanged.

The hex input routine at 51A3H calls 518AH internally for each keypress. It builds a 16-bit value in HL by shifting left 4 bits and OR-ing each new nibble. If the user types a valid hex address, it replaces the default HL value entirely. If the user presses Enter immediately, the CARRY flag signals the M command to exit.

4FE1
LD (4060H),HL 22 60 40
Store the address in Register Pair HL (either user-entered or carried forward from the previous iteration) back to the memory modify pointer at 4060H. This updates the working address for the current iteration of the modify loop.
4FE4
RET C D8
If the CARRY FLAG is set (the user pressed Enter without entering a hex address), RETurn to the command loop at 4E4BH. This is the normal exit path when the user is finished modifying memory.
4FE5
PUSH HL E5
Save the current modify address (Register Pair HL) onto the stack. This saved copy will be restored at 5005H after the user enters a new value, to write the new byte to the correct address.
4FE6
GOSUB to the register dump display routine at 4ECFH. This refreshes the full register dump and memory display on screen so the user sees the updated state before modifying the next byte.
4FE9
LD HL,3F40H 21 40 3F
Point Register Pair HL to video RAM address 3F40H. This is the start of row 13 in the 64-column x 16-row video display (3C00H + 13 x 64 = 3F40H), positioning the cursor for the address display line during memory modification.
4FEC
LD (4020H),HL 22 20 40
Store the cursor position 3F40H to the video cursor variable at 4020H. This sets the output position so the next character output via ROM 0033H appears at row 13 of the screen.
4FEF
LD HL,(4060H) 2A 60 40
Reload the current modify address from the memory pointer at 4060H into Register Pair HL. This was just stored at 4FE1H and is needed to display the address and read the current byte.
4FF2
GOSUB to the 4-digit hex display routine at 51D4H. This prints the high byte of HL (the address high byte) followed by the low byte, displaying the full 4-digit hex address of the byte being modified (e.g., "4F3A"). Output goes to the video position set at 4FECH.
4FF5
PUSH HL E5
Save the modify address (Register Pair HL, still holding the address from 4060H) onto the stack temporarily. This is needed because the cursor repositioning below will overwrite HL.
4FF6
LD HL,3F80H 21 80 3F
Point Register Pair HL to video RAM address 3F80H. This is the start of row 14 (3C00H + 14 x 64 = 3F80H), one line below the address display. The current byte value and the input prompt will appear here.
4FF9
LD (4020H),HL 22 20 40
Store the new cursor position 3F80H to the video cursor variable at 4020H. Output will now appear on row 14 of the screen.
4FFC
POP HL E1
Restore the modify address from the stack back into Register Pair HL (saved at 4FF5H).
4FFD
GOSUB to the display-byte-from-memory routine at 51D0H. This reads the byte at the address in HL (i.e., the current byte at the modify address), displays it as 2-digit hex, and increments HL past it. The user now sees the current value of the byte at this address.
5000
LD A,2DH 3E 2D
Load Register A with 2DH (ASCII -). This dash character serves as a visual separator between the displayed current value and the input area where the user types the new value.
5002
GOSUB to the ROM character output routine at 0033H to display the dash separator. The screen now shows: address (row 13), then current-byte-dash (row 14), e.g., "3A-" with the cursor positioned after the dash awaiting user input.
5005
POP DE D1
Restore the original modify address from the stack into Register Pair DE. This was saved at 4FE5H before the screen refresh. DE now holds the address where the new byte value should be written.
5006
GOSUB to the hex number input routine at 51A3H to read the new byte value from the keyboard. On return: if the user typed a hex value, Register Pair HL holds the value (only low byte L is meaningful for a single byte) and the NZ FLAG is set. If the user pressed Enter, the CARRY FLAG is set. If the user typed a separator without digits, the Z FLAG is set.
5009
EX DE,HL EB
Exchange Register Pairs DE and HL. After this: HL = the original modify address (from DE), DE = the user-entered value (from HL). Register E now contains the new byte value to write.
500A
If the Z FLAG is set (the user typed a separator without entering any hex digits), JUMP forward to 500DH to skip the write. The byte at the current address is left unchanged and the loop continues to the next address.
500C
LD (HL),E 73
Write the new byte value (Register E, the low byte of the user's hex input) to the memory address pointed to by Register Pair HL. This is the actual memory modification.
500D
RET C D8
If the CARRY FLAG is set (the user pressed Enter at the hex input prompt), RETurn to the command loop at 4E4BH. This exits the modify command when the user presses Enter instead of typing a new value.
500E
INC HL 23
INCrement Register Pair HL by 1 to advance to the next memory address. The modify loop will display and allow editing of the byte at this next address.
500F
Loop Back
JUMP back to 4FE1H to store the new address to 4060H and continue the modify loop. The user can keep modifying consecutive bytes until pressing Enter to exit.

5011H - Register Modify (R Command)

Allows the user to modify the contents of any saved register pair in the register save area at 4065H. The user types a 2-character register name (e.g., AF, BC, HL, IX) optionally followed by a 1-character separator (space or apostrophe for alternate registers), then enters a new 16-bit hex value. The routine searches the register name table at 4F54H for a matching 3-byte entry, computes the offset into the 24-byte save area, clears the screen, reads the new value, and stores it. Entered from the command dispatcher at 4E8CH when Register A = 52H (ASCII R). Returns to the command loop at 4E4BH via the PUSH at 4E63H.

5011
GOSUB to the keyboard input with echo routine at 518AH to read the first character of the register name. On return: Register A holds the typed character (uppercase letter), the Z FLAG is set if the character is a separator (comma or space), and the CARRY FLAG is set if Enter was pressed.
5014
RET Z C8
If the Z FLAG is set (the user typed a separator instead of a register name character), RETurn to the command loop. This handles the case where the user presses space or comma without typing a register name.
5015
LD C,A 4F
Store the first character of the register name (e.g., A for AF, B for BC, H for HL) into Register C for the table search later at 502EH.
5016
GOSUB to the keyboard input routine at 518AH to read the second character of the register name (e.g., F for AF, C for BC, L for HL).
5019
RET Z C8
If the Z FLAG is set (separator typed instead of a second character), RETurn to the command loop. A single character is not a valid register name.
501A
LD D,A 57
Store the second character of the register name into Register D. Now Register C holds the first character and Register D holds the second character (e.g., C=41H 'A', D=46H 'F' for register pair AF).
501B
LD E,20H 1E 20
Load Register E with 20H (ASCII space). This is the default third-byte separator, matching primary register entries in the name table at 4F54H (e.g., "AF ", "BC ", "HL "). Alternate registers use 27H (apostrophe) as the separator (e.g., "AF'", "BC'").
501D
GOSUB to the keyboard input routine at 518AH to read the third character. This may be a separator (space/comma indicating the user is done typing the register name), an apostrophe (27H, distinguishing alternate registers like AF' from AF), or Enter (end of input).
5020
RET C D8
If the CARRY FLAG is set (Enter pressed), RETurn to the command loop. The user pressed Enter after only two characters without confirming, so no modification is performed.
5021
If the Z FLAG is set (the third character is a separator - space or comma), JUMP to 5029H to begin the table search. The default separator 20H (space) in Register E will match primary registers (AF, BC, DE, HL, IX, IY, SP, PC).
5023
LD E,A 5F
The third character was not a separator, so store it into Register E as the explicit separator byte. If the user typed an apostrophe (27H), Register E now holds 27H, which will match alternate register entries (AF', BC', DE', HL') in the name table.
5024
GOSUB to the keyboard input routine at 518AH to read the fourth character. After a 3-character register name like "AF'", the user must type a separator (space/comma) or press Enter to confirm.
5027
RET NZ C0
If the NZ FLAG is set (the fourth character is a printable character, not a separator), RETurn to the command loop. Too many characters were typed for a valid register name.
5028
RET C D8
If the CARRY FLAG is set (Enter pressed after the fourth character), RETurn to the command loop. No new value was provided to store.

Register Name Table Search
The code below searches the 12-entry register name table at 4F54H. Each entry is 3 bytes: two ASCII name characters plus a separator (20H = space for primary registers, 27H = apostrophe for alternates). The entries in order are: AF, BC, DE, HL, AF', BC', DE', HL', IX, IY, SP, PC. The search compares all 3 bytes (C, D, E) against each table entry to find the matching register pair.

5029
LD HL,4F54H 21 54 4F
Point Register Pair HL to the register name display table at 4F54H. This table contains 12 three-byte entries (36 bytes total), one per register pair, in the order they are stored in the save area at 4065H.
502C
LD B,0CH 06 0C
Load Register B with 0CH (12 decimal) as the loop counter. There are 12 register pair entries to search: AF, BC, DE, HL, AF', BC', DE', HL', IX, IY, SP, PC.

Loop Start
Search loop iterates through the 12 register name table entries. Register B counts down from 12 to 1. Register C holds the first user character, Register D the second, Register E the separator byte.

502E
LD A,(HL) 7E
Load Register A with the first byte of the current table entry (the first character of the register name, e.g., 41H = A for the AF entry).
502F
CP A,C B9
Compare Register A (first character from table) against Register C (first character typed by user). If they match, the Z FLAG is set.
5030
If the Z FLAG is set (first character matches), JUMP to 5038H to compare the second character. Otherwise fall through to advance to the next table entry.
5032
INC HL 23
INCrement HL to skip past byte 1 of the current 3-byte table entry (the second name character).
5033
INC HL 23
INCrement HL to skip past byte 2 of the current entry (the separator byte). HL now points to the start of the next 3-byte entry.
5034
INC HL 23
INCrement HL past byte 3 (or byte 1 of next entry depending on which mismatch path reached here). After this, HL points to the next table entry's first byte.
5035
DECrement Register B and LOOP BACK to 502EH if not zero. Continues searching the next table entry. If B reaches zero, all 12 entries were checked without a match.
5037
RET C9
RETurn to the command loop at 4E4BH. No matching register name was found in the table, so no modification is performed. The user typed an invalid register name.
5038
INC HL 23
INCrement HL to point to the second byte of the current table entry (the second character of the register name).
5039
LD A,(HL) 7E
Load Register A with the second character of the register name from the table (e.g., 46H = F for the AF entry).
503A
CP A,D BA
Compare Register A (second character from table) against Register D (second character typed by user). If they match, the Z FLAG is set.
503B
If the NZ FLAG is set (second character does not match), JUMP back to 5033H to skip the remaining bytes of this entry and advance to the next one. This handles cases like "AF" matching on 'A' but failing on 'F' vs 'C' (for an AC typo).
503D
INC HL 23
INCrement HL to point to the third byte of the current table entry (the separator character: 20H for primary or 27H for alternate registers).
503E
LD A,(HL) 7E
Load Register A with the separator byte from the table entry.
503F
CP A,E BB
Compare Register A (separator from table) against Register E (separator from user input: 20H for primary, 27H for alternate). If they match, this is the correct register pair.
5040
If the NZ FLAG is set (separator does not match), JUMP back to 5034H to advance past this entry and continue searching. This distinguishes AF (space) from AF' (apostrophe) - both share the same first two characters but differ in the third byte.

Match Found
All three bytes of the register name matched. Register B holds the remaining entry count (12 for the first entry AF, 11 for BC, etc.). The save area at 4065H stores registers in the same order as the table: AF at offset 0, BC at offset 2, DE at offset 4, etc. The code computes the byte offset into the save area using the formula: offset = (24 - 2*B) = 18H - B - B. Since each register pair occupies 2 bytes and the table counts down from 12, the first entry (B=12) gives offset 0 and the last (B=1) gives offset 22.

5042
LD A,18H 3E 18
Load Register A with 18H (24 decimal), the total size of the register save area in bytes. This is the starting value for computing the byte offset into the save area at 4065H.
5044
SUB A,B 90
SUBtract Register B (remaining count, 12 for entry 0, 11 for entry 1, etc.) from Register A. After this subtraction, A = 18H - B.
5045
SUB A,B 90
SUBtract Register B again from Register A. Now A = 18H - 2*B. For AF (B=0CH): A = 18H - 18H = 00H (offset 0). For BC (B=0BH): A = 18H - 16H = 02H (offset 2). For PC (B=01H): A = 18H - 02H = 16H (offset 22). Each register pair occupies 2 bytes in the save area, and this double subtraction computes the correct byte offset.
5046
LD C,A 4F
Transfer the computed offset from Register A to Register C (low byte of BC for the ADD below).
5047
LD B,00H 06 00
Load Register B with 00H to form the 16-bit offset in Register Pair BC. BC now holds the byte offset (0-22) into the register save area.
5049
LD HL,4065H 21 65 40
Point Register Pair HL to the base of the register save area at 4065H. This 24-byte block holds all saved register pairs in order: AF, BC, DE, HL, AF', BC', DE', HL', IX, IY, SP, PC (saved by LDIR at 4E1DH during DEBUG entry).
504C
ADD HL,BC 09
ADD the offset in Register Pair BC to the save area base in HL. Register Pair HL now points to the specific 2-byte register pair within the save area that the user wants to modify.
504D
PUSH HL E5
Save the pointer to the target register pair location onto the stack. This will be restored at 5053H as the destination for writing the new value.
504E
LD A,1EH 3E 1E
Load Register A with 1EH. This is the TRS-80 control code for "Home Cursor" (move cursor to top-left of screen and clear). This prepares the screen for the value entry prompt.
5050
GOSUB to the ROM character output routine at 0033H to send the home/clear code 1EH to the screen. The display is cleared so the user can type the new register value on a clean screen.
5053
POP DE D1
Restore the pointer to the target register pair location from the stack into Register Pair DE (saved at 504DH). DE now points to the 2-byte slot in the save area at 4065H where the new value will be stored.
5054
GOSUB to the hex number input routine at 51A3H to read the new 16-bit register value from the keyboard. On return, Register Pair HL holds the entered hex value (e.g., 4E00H). If the user pressed Enter without typing, the Z FLAG is set.
5057
RET Z C8
If the Z FLAG is set (the user pressed Enter or typed a separator without entering any hex digits), RETurn to the command loop without modifying the register. The save area is left unchanged.
5058
EX DE,HL EB
Exchange Register Pairs DE and HL. After this: HL = pointer to save area slot (from DE), DE = the new register value (from HL, the hex input result). Register E holds the low byte and Register D holds the high byte of the new value.
5059
LD (HL),E 73
Store the low byte of the new register value (Register E) to the save area slot pointed to by HL. The save area stores values in little-endian format (low byte first).
505A
INC HL 23
INCrement Register Pair HL to point to the high byte of the register pair slot in the save area.
505B
LD (HL),D 72
Store the high byte of the new register value (Register D) to the save area. The full 16-bit register value is now stored. When the Go (G) command restores registers at 4FA4H, this modified value will be loaded into the corresponding CPU register pair.
505C
RET C9
RETurn to the command loop at 4E4BH (the return address was pushed at 4E63H). The register has been modified in the save area and the next register dump display will show the new value.

505DH - Instruction Decode and Step/Continue (C and I Commands)

Decodes the Z80 instruction at the saved program counter (407BH) to determine its length and whether it is a branch instruction. For the C (Continue) command, a single breakpoint (F7H) is inserted at the next sequential instruction address. For the I (Instruction step) command, a second breakpoint is also inserted at the computed branch target address, ensuring the debugger regains control regardless of whether the branch is taken. The routine uses three opcode classification tables (50E0H for unprefixed, 510EH for ED-prefix, 5115H for DD/FD-prefix) to determine instruction length and operand addressing mode. Entry from the command dispatcher: C at 4E76H or I at 4E80H/4E82H, both reaching JP Z,505DH.

505D
PUSH AF F5
Save Register AF onto the stack. Register A holds the command character (43H = C or 49H = I). This saved value is retrieved at 50AFH to determine whether to insert a branch-target breakpoint (I command only) or just a sequential breakpoint (C command).
505E
LD DE,(407BH) ED 5B 7B 40
Load Register Pair DE with the saved program counter from 407BH. This is the address of the next instruction to be executed in the user program. The opcode at this address will be decoded to determine instruction length and branch behavior.
5062
LD A,(DE) 1A
Load Register A with the opcode byte at the saved PC address. This is the first byte of the instruction to be decoded.
5063
LD HL,5115H 21 15 51
Point Register Pair HL to the DD/FD-prefix opcode classification table at 5115H. This table is used for IX-indexed (DDH prefix) and IY-indexed (FDH prefix) instructions. The code checks below whether the opcode is actually a DD or FD prefix.
5066
CP A,0DDH FE DD
Compare Register A against 0DDH (the IX-prefix byte). If the opcode is DDH, the Z FLAG is set and the DD/FD table at 5115H is the correct classification table.
5068
If the Z FLAG is set (opcode is DDH, an IX-prefix instruction), JUMP to 5078H to read the second byte of the instruction and begin classification using the DD/FD table at 5115H.
506A
CP A,0FDH FE FD
Compare Register A against 0FDH (the IY-prefix byte). If the opcode is FDH, the Z FLAG is set and the same DD/FD table at 5115H applies.
506C
If the Z FLAG is set (opcode is FDH, an IY-prefix instruction), JUMP to 5078H to read the second byte and classify using the DD/FD table.
506E
LD HL,50E0H 21 E0 50
Point Register Pair HL to the unprefixed opcode classification table at 50E0H. This table covers all single-byte opcodes and multi-byte instructions without DD/FD/ED prefixes.
5071
CP A,0EDH FE ED
Compare Register A against 0EDH (the ED-prefix byte). ED-prefix instructions include block moves (LDIR, LDDR), block I/O, and extended register loads.
5073
If the NZ FLAG is set (opcode is NOT EDH), JUMP to 507BH to classify using the unprefixed table at 50E0H. Register A already holds the opcode byte, and HL points to the correct table.
5075
LD HL,510EH 21 0E 51
Point Register Pair HL to the ED-prefix opcode classification table at 510EH. This short table handles ED-prefix instructions like LD (nn),rr and RETN/RETI.
5078
INC DE 13
INCrement Register Pair DE to point past the prefix byte to the second byte of the instruction. For prefixed instructions (DD, FD, ED), the classification is based on the second byte, not the prefix itself.
5079
LD A,(DE) 1A
Load Register A with the second byte of the instruction (the byte after the DD/FD/ED prefix). This is the actual opcode that determines instruction behavior.
507A
DEC DE 1B
DECrement Register Pair DE back to the instruction start address. DE must point to the first byte of the complete instruction for the length calculation at 5090H.

Opcode Classification Loop
The loop below searches the selected classification table for a matching entry. Each table entry is 3 bytes: a mask byte, a match byte, and a type byte. The classification works by ANDing the opcode with the mask and comparing against the match value. If (opcode AND mask) == match, the entry describes this instruction. The type byte encodes both instruction length (bits 0-3) and operand addressing mode (bits 4-7). The loop terminates when a match is found or when the type byte of the next entry is less than 05H (a sentinel value marking end-of-table).

507B
LD C,A 4F
Copy the opcode byte from Register A to Register C. Register C holds the opcode throughout the classification loop so Register A can be used for masking and comparison.
507C
LD A,(HL) 7E
Load Register A with the mask byte from the current classification table entry. The mask isolates the significant bits of the opcode for this instruction class.
507D
AND A,C A1
AND the mask (Register A) with the opcode (Register C). This isolates only the bits that distinguish this instruction class from others.
507E
INC HL 23
INCrement HL to point to the match byte (second byte of the table entry).
507F
CP A,(HL) BE
Compare the masked opcode (Register A) against the match byte from the table. If they are equal, the Z FLAG is set and this table entry describes the instruction.
5080
INC HL 23
INCrement HL to point to the type byte (third byte of the table entry). Whether or not the match succeeded, HL now points to the type byte.
5081
If the Z FLAG is set (the masked opcode matched the match byte), JUMP to 5089H to extract the instruction length and operand mode from the type byte. This entry describes the current instruction.
5083
INC HL 23
INCrement HL past the type byte to the mask byte of the next table entry. This advances to the next 3-byte entry since no match was found.
5084
LD A,(HL) 7E
Load Register A with the mask byte of the next table entry. Before using it as a mask, the code checks whether this is actually a sentinel marking end-of-table.
5085
CP A,05H FE 05
Compare Register A against 05H. If the value is less than 05H, it is a sentinel marking the end of the table (the actual sentinel values are 01H for the unprefixed table and 02H for the ED and DD/FD tables). Valid mask bytes are always 05H or greater since they need to match meaningful opcode bit patterns.
5087
If the NO CARRY FLAG is set (the value is >= 05H, meaning this is a valid mask byte and not a sentinel), LOOP BACK to 507CH to test this next table entry against the opcode.

Classification Complete
Execution reaches 5089H either because a table entry matched (JR Z from 5081H) or because the table was exhausted (sentinel reached, fall through from 5087H). In the sentinel case, the type byte of the last entry before the sentinel serves as a default - typically a 1-byte instruction with no operand display (length=1 or 2, mode=0). The type byte at (HL) encodes: bits 0-3 = total instruction length in bytes (including prefix), bits 4-7 = operand addressing mode for branch target computation.

5089
LD A,(HL) 7E
Load Register A with the type byte from the matched (or default) classification table entry. This byte encodes both the instruction length and the operand addressing mode.
508A
LD B,A 47
Copy the full type byte to Register B for later mode extraction at 5099H. Register B preserves the complete type byte while Register A is used for length extraction.
508B
AND A,0FH E6 0F
Mask Register A with 0FH to isolate bits 0-3: the instruction length in bytes. For example, 03H = 3-byte instruction, 02H = 2-byte instruction, 04H = 4-byte instruction.
508D
LD L,A 6F
Store the instruction length into Register L (low byte of HL). This will be added to DE (instruction start address) to compute the next sequential instruction address.
508E
LD H,00H 26 00
Clear Register H to zero. Register Pair HL now holds the instruction length as a 16-bit value (0001H to 0004H).
5090
ADD HL,DE 19
ADD the instruction length (HL) to the instruction start address (DE). Register Pair HL now holds the address of the next sequential instruction - the address where execution will continue if no branch is taken.
5091
PUSH DE D1
Save the instruction start address (Register Pair DE) onto the stack. This is needed by the mode dispatch code below to read operand bytes from the instruction.
5092
LD DE,4062H 11 62 40
Point Register Pair DE to the breakpoint save area at 4062H. The breakpoint insertion routine at 4FCAH will store the original byte and address here before writing the F7H (RST 30H) breakpoint opcode.
5095
GOSUB to the breakpoint insertion routine at 4FCAH. This saves the original byte at the next sequential instruction address (HL) into the breakpoint save area at 4062H, then writes F7H (RST 30H breakpoint) at that address. If the address is in ROM or write-protected memory, the write-verify check at 4FD0H will detect the failure and jump to 4E2FH.
5098
POP HL E1
Restore the instruction start address from the stack into Register Pair HL (was DE, saved at 5091H, now popped into HL). HL now points to the first byte of the instruction being decoded.
5099
LD A,B 78
Load Register A with the full type byte (saved in Register B at 508AH). The high nibble (bits 4-7) encodes the operand addressing mode for branch target computation.
509A
AND A,0F0H E6 F0
Mask Register A with 0F0H to isolate bits 4-7: the operand addressing mode. The result is one of: 00H (no branch operand), 10H (HL indirect), 20H (IX/IY indirect), 30H (signed relative), 40H (absolute address), 50H (stack read for RET), 60H (conditional execute/step).
509C
If the Z FLAG is set (mode = 00H, no branch operand), JUMP to 50B4H. This instruction has no branch target, so only the sequential breakpoint (already inserted at 5095H) is needed. Execution continues to the register restore and Go sequence at 4FA4H.
509E
INC HL 23
INCrement Register Pair HL past the opcode byte to point to the operand field. For prefixed instructions, HL already points to the prefix, so this advances to the opcode byte after the prefix. The mode dispatch below reads operand bytes from this position.
509F
CP A,20H FE 20
Compare the mode value in Register A against 20H. If less than 20H (mode = 10H), the CARRY FLAG is set. Mode 10H = HL indirect: the branch target is the address held in the saved HL register (e.g., JP (HL)).
50A1
If the CARRY FLAG is set (mode = 10H, HL indirect), JUMP to 50D8H to load the branch target from the saved HL value at 406BH in the register save area.
50A3
If the Z FLAG is set (mode = 20H, IX/IY indirect), JUMP to 50CCH to load the branch target from the saved IX or IY register value. The specific register (IX at 4075H or IY at 4077H) is determined by bit 5 of the opcode.
50A5
CP A,40H FE 40
Compare the mode value against 40H. If less than 40H (mode = 30H), the CARRY FLAG is set. Mode 30H = signed relative branch: the operand is a signed 8-bit displacement (e.g., JR, DJNZ).
50A7
If the CARRY FLAG is set (mode = 30H, signed relative), JUMP to 50C0H to compute the branch target by adding the signed displacement to the instruction address.
50A9
If the Z FLAG is set (mode = 40H, absolute address), JUMP to 50BAH to read the 16-bit branch target address directly from the operand bytes (e.g., JP nn, CALL nn).
50AB
CP A,60H FE 60
Compare the mode value against 60H. If less than 60H (mode = 50H), the CARRY FLAG is set. Mode 50H = stack read: the branch target is read from the saved stack pointer (e.g., RET, RETN).
50AD
If the CARRY FLAG is set (mode = 50H, stack indirect for RET-class instructions), JUMP to 50B7H to read the return address from the saved user stack at (4079H).
50AF
POP AF F1
Restore the original command character from the stack into Register A (saved at 505DH). Register A now holds 43H (C) or 49H (I). Mode 60H applies to CALL and conditional CALL instructions, which need a second breakpoint only for the I (Instruction step) command.
50B0
CP A,49H FE 49
Compare Register A against 49H (ASCII I). If the command is I (Instruction step), the Z FLAG is set and a second breakpoint should be inserted at the branch target. If the command is C (Continue), only the sequential breakpoint is used.
50B2
If the Z FLAG is set (command is I), JUMP to 50BAH to read the absolute branch target and insert a second breakpoint. For CALL instructions during I-step, this ensures the debugger stops at the first instruction of the called routine.
50B4
JUMP to the register restoration and Go sequence at 4FA4H. This restores all saved register pairs from the save area at 4065H, restores the stack pointer, pushes the saved PC (407BH) as a return address, and RETs to the user program. The breakpoint(s) already inserted will trigger when execution reaches those addresses.

Mode 50H - Stack Indirect (RET-Class Instructions)
For RET and RETN instructions, the branch target is the return address currently on the user's stack. The saved stack pointer at 4079H points to the top of the user stack, and the 16-bit value there is the return address.

50B7
LD HL,(4079H) 2A 79 40
Load Register Pair HL with the saved user stack pointer from 4079H. This points to the top of the user's stack, where the return address is stored.
50BA
LD A,(HL) 7E
Load Register A with the low byte of the branch target address. For mode 40H (absolute address), HL points into the instruction's operand field. For mode 50H (stack read), HL points to the top of the user stack.
50BB
INC HL 23
INCrement HL to point to the high byte of the branch target address.
50BC
LD H,(HL) 66
Load Register H with the high byte of the branch target address from memory.
50BD
LD L,A 6F
Load Register L with the low byte of the branch target address (saved in Register A at 50BAH). Register Pair HL now holds the complete 16-bit branch target address.
50BE
JUMP to 50DBH to insert the second breakpoint at the branch target address in HL, then proceed to the Go sequence at 50B4H.

Mode 30H - Signed Relative Branch (JR, DJNZ)
The operand is a signed 8-bit displacement relative to the address AFTER the instruction. The branch target = instruction_address + instruction_length + displacement. Since HL was already incremented past the opcode at 509EH, HL now points to the displacement byte.

50C0
LD C,(HL) 4E
Load Register C with the signed displacement byte from the instruction operand. This is the relative offset used by JR, JR cc, and DJNZ instructions.
50C1
XOR A,A AF
Set Register A to zero and clear all flags. This prepares Register A for sign extension of the displacement byte.
50C2
BIT 7,C CB 79
Test bit 7 of the displacement byte in Register C. If bit 7 is set, the displacement is negative (the branch goes backward).
50C4
If the Z FLAG is set (bit 7 is clear, displacement is positive), JUMP to 50C7H. Register A remains 00H, giving a high byte of 00H for a forward branch offset.
50C6
CPL 2F
Complement Register A (flip all bits). Register A changes from 00H to FFH. This sign-extends the negative displacement: Register B will be FFH as the high byte, so BC forms a negative 16-bit offset when added to HL.
50C7
LD B,A 47
Load Register B with the sign-extension byte (00H for positive displacement, FFH for negative). Register Pair BC now holds the sign-extended 16-bit displacement.
50C8
INC HL 23
INCrement HL past the displacement byte. HL now points to the byte AFTER the relative branch instruction, which is the base address for the displacement calculation.
50C9
ADD HL,BC 09
ADD the sign-extended displacement (BC) to the post-instruction address (HL). Register Pair HL now holds the computed branch target address. For example, if the instruction is at 4E49H (JR -17H), HL = 4E4BH + FFE9H = 4E34H.
50CA
JUMP to 50DBH to insert the second breakpoint at the branch target address in HL.

Mode 20H - IX/IY Indirect (JP (IX) or JP (IY))
The branch target is the value currently held in the saved IX or IY register. Bit 5 of the opcode byte (in Register C) distinguishes IX (bit 5 clear, DDH prefix) from IY (bit 5 set, FDH prefix).

50CC
LD HL,(4075H) 2A 75 40
Load Register Pair HL with the saved IX value from offset +10H in the register save area (4065H + 10H = 4075H). This is the default assumption that the instruction uses IX.
50CF
BIT 5,C CB 69
Test bit 5 of the opcode byte in Register C. For DD-prefix instructions (IX), bit 5 of the opcode after DD is typically clear. For FD-prefix instructions (IY), bit 5 is set. This distinguishes JP (IX) from JP (IY).
50D1
If the Z FLAG is set (bit 5 is clear, IX instruction), JUMP to 50DBH. Register Pair HL already holds the saved IX value from 4075H.
50D3
LD HL,(4077H) 2A 77 40
Load Register Pair HL with the saved IY value from offset +12H in the register save area (4065H + 12H = 4077H). The instruction uses IY, not IX.
50D6
JUMP to 50DBH to insert the second breakpoint at the IY-indirect branch target.

Mode 10H - HL Indirect (JP (HL))
The branch target is the value currently held in the saved HL register pair.

50D8
LD HL,(406BH) 2A 6B 40
Load Register Pair HL with the saved HL value from offset +06H in the register save area (4065H + 06H = 406BH). This is the branch target for JP (HL).

Second Breakpoint Insertion
HL now holds the computed branch target address (from any of the mode handlers above). A second breakpoint is inserted at this address so the debugger will regain control whether the branch is taken or falls through.

50DB
GOSUB to the breakpoint insertion routine at 4FCAH. This saves the original byte at the branch target address (HL) into the second slot of the breakpoint save area at 4062H, then writes F7H (RST 30H breakpoint opcode) at that address. The first breakpoint (at the sequential next instruction) was already inserted at 5095H.
50DE
JUMP to 50B4H to execute JP 4FA4H - the register restoration and Go sequence. Both breakpoints are now in place and the user program will resume execution.

50E0H - Opcode Classification Table: Unprefixed Instructions

Data table used by the instruction decoder at 507CH. Contains 15 three-byte entries (mask, match, type) followed by a 01H sentinel. Each entry classifies a group of Z80 opcodes by their length and operand addressing mode. The type byte encodes: bits 0-3 = instruction length in bytes, bits 4-7 = operand mode (0=none, 1=HL-indirect, 2=IX/IY-indirect, 3=signed-relative, 4=absolute-address, 5=stack-read, 6=CALL/execute).

50E0
DEFB C7H,C0H,51H C7 C0 51
Entry 0: mask=C7H match=C0H type=51H. Matches conditional RET instructions (RET NZ, RET Z, RET NC, RET C, RET PO, RET PE, RET P, RET M). Length=1, mode=5 (stack read). The breakpoint target is read from the user stack.
50E3
DEFB FFH,C9H,51H FF C9 51
Entry 1: mask=FFH match=C9H type=51H. Matches unconditional RET. Length=1, mode=5 (stack read).
50E6
DEFB FFH,E9H,11H FF E9 11
Entry 2: mask=FFH match=E9H type=11H. Matches JP (HL). Length=1, mode=1 (HL-indirect). The branch target is the saved HL register value at 406BH.
50E9
DEFB CFH,01H,03H CF 01 03
Entry 3: mask=CFH match=01H type=03H. Matches 16-bit immediate loads: LD BC/DE/HL/SP,nnnn. Length=3, mode=0 (no branch).
50EC
DEFB E7H,22H,03H E7 22 03
Entry 4: mask=E7H match=22H type=03H. Matches LD (nn),HL / LD HL,(nn) / LD (nn),A / LD A,(nn). Length=3, mode=0 (no branch).
50EF
DEFB C7H,C2H,43H C7 C2 43
Entry 5: mask=C7H match=C2H type=43H. Matches conditional JP cc,nn instructions. Length=3, mode=4 (absolute address). Breakpoint placed at the target address.
50F2
DEFB FFH,C3H,43H FF C3 43
Entry 6: mask=FFH match=C3H type=43H. Matches unconditional JP nn. Length=3, mode=4 (absolute address).
50F5
DEFB C7H,C4H,63H C7 C4 63
Entry 7: mask=C7H match=C4H type=63H. Matches conditional CALL cc,nn instructions. Length=3, mode=6 (execute/step decision). For the I command, a breakpoint is placed at the call target; for C, only the sequential breakpoint is used.
50F8
DEFB FFH,CDH,63H FF CD 63
Entry 8: mask=FFH match=CDH type=63H. Matches unconditional CALL nn. Length=3, mode=6 (execute/step decision).
50FB
DEFB C7H,06H,02H C7 06 02
Entry 9: mask=C7H match=06H type=02H. Matches LD r,n (8-bit immediate loads). Length=2, mode=0 (no branch).
50FE
DEFB F7H,D3H,02H F7 D3 02
Entry 10: mask=F7H match=D3H type=02H. Matches OUT (n),A and IN A,(n). Length=2, mode=0 (no branch).
5101
DEFB C7H,C6H,02H C7 C6 02
Entry 11: mask=C7H match=C6H type=02H. Matches immediate ALU operations: ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,n. Length=2, mode=0 (no branch).
5104
DEFB FFH,CBH,02H FF CB 02
Entry 12: mask=FFH match=CBH type=02H. Matches CB-prefix bit operations (RLC, RRC, RL, RR, SLA, SRA, SRL, BIT, RES, SET). Length=2, mode=0 (no branch).
5107
DEFB F7H,10H,32H F7 10 32
Entry 13: mask=F7H match=10H type=32H. Matches DJNZ and JR (unconditional). Length=2, mode=3 (signed relative). Breakpoint placed at the computed branch target.
510A
DEFB E7H,20H,32H E7 20 32
Entry 14: mask=E7H match=20H type=32H. Matches conditional JR cc (JR NZ, JR Z, JR NC, JR C). Length=2, mode=3 (signed relative).
510D
DEFB 01H 01
Sentinel byte marking end of the unprefixed table. Value 01H is less than 05H, causing the classification loop at 5085H to stop. If no entry matched, the instruction is treated as a 1-byte opcode with no branch operand (the default behavior from the last entry's type byte).

510EH - Opcode Classification Table: ED-Prefix Instructions

Data table for ED-prefix instruction classification. Contains 2 three-byte entries followed by a 02H sentinel. Only instructions with branch targets or non-standard lengths need explicit entries; all other ED-prefix instructions default to 2 bytes with no branch.

510E
DEFB C7H,43H,04H C7 43 04
Entry 0: mask=C7H match=43H type=04H. Matches ED 43/4B/53/5B/63/6B/73/7B: the 4-byte LD (nn),rr and LD rr,(nn) instructions. Length=4 (including ED prefix), mode=0 (no branch).
5111
DEFB F7H,45H,52H F7 45 52
Entry 1: mask=F7H match=45H type=52H. Matches RETN (ED 45H) and RETI (ED 4DH). Length=2 (including ED prefix), mode=5 (stack read). The return address is read from the user stack.
5114
DEFB 02H 02
Sentinel byte marking end of the ED-prefix table. Default: all other ED-prefix instructions are 2 bytes with no branch operand.

5115H - Opcode Classification Table: DD/FD-Prefix Instructions

Data table for DD-prefix (IX) and FD-prefix (IY) instruction classification. Contains 9 three-byte entries followed by a 02H sentinel. Lengths include the DD/FD prefix byte.

5115
DEFB FEH,34H,03H FE 34 03
Entry 0: mask=FEH match=34H type=03H. Matches INC (IX+d) (DD 34) and DEC (IX+d) (DD 35). Length=3, mode=0 (no branch).
5118
DEFB C0H,40H,03H C0 40 03
Entry 1: mask=C0H match=40H type=03H. Matches LD r,(IX+d) and LD (IX+d),r instructions (DD 46-7E range). Length=3, mode=0 (no branch).
511B
DEFB C0H,80H,03H C0 80 03
Entry 2: mask=C0H match=80H type=03H. Matches ALU operations with (IX+d): ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,(IX+d). Length=3, mode=0 (no branch).
511E
DEFB FFH,21H,04H FF 21 04
Entry 3: mask=FFH match=21H type=04H. Matches LD IX,nnnn (DD 21). Length=4, mode=0 (no branch).
5121
DEFB FFH,22H,04H FF 22 04
Entry 4: mask=FFH match=22H type=04H. Matches LD (nn),IX (DD 22). Length=4, mode=0 (no branch).
5124
DEFB FFH,2AH,04H FF 2A 04
Entry 5: mask=FFH match=2AH type=04H. Matches LD IX,(nn) (DD 2A). Length=4, mode=0 (no branch).
5127
DEFB FFH,36H,04H FF 36 04
Entry 6: mask=FFH match=36H type=04H. Matches LD (IX+d),n (DD 36). Length=4, mode=0 (no branch).
512A
DEFB FFH,CBH,04H FF CB 04
Entry 7: mask=FFH match=CBH type=04H. Matches DD CB prefix (bit operations with IX+d displacement). Length=4, mode=0 (no branch).
512D
DEFB FFH,E9H,22H FF E9 22
Entry 8: mask=FFH match=E9H type=22H. Matches JP (IX) (DD E9) and JP (IY) (FD E9). Length=2, mode=2 (IX/IY-indirect). The branch target is the saved IX or IY register value.
5130
DEFB 02H 02
Sentinel byte marking end of the DD/FD-prefix table. Default: all other DD/FD-prefix instructions are 2 bytes with no branch operand.

5131H - Memory Dump Line Display

Displays a single line of memory dump at the address pointed to by a register pair. Prints "=>" as a prefix, then calls 51F2H for a space separator, enters a 16-byte hex dump loop with optional ASCII column display (when the address mode at 405DH is set to 41H = A), and PC-highlight check. Called by the register dump display at 4F27H to show the memory contents at each register pair's value, and by the memory dump loop at 4F34H for the hex dump area.

5131
PUSH BC C5
Save Register Pair BC onto the stack. Register B holds the outer loop counter from the caller (register dump or memory dump), which must be preserved across this subroutine.
5132
LD A,3DH 3E 3D
Load Register A with 3DH (ASCII =). This is the first character of the "=>" prefix displayed before each memory dump line.
5134
GOSUB to the ROM character output routine at 0033H to display the = character.
5137
LD A,3EH 3E 3E
Load Register A with 3EH (ASCII >). This is the second character of the "=>" prefix.
5139
GOSUB to ROM 0033H to display the > character. The screen now shows "=>" as the line prefix.
513C
GOSUB to the print-space routine at 51F2H to output a space character after the "=>" prefix, separating it from the hex data.
513F
LD B,10H 06 10
Load Register B with 10H (16 decimal) as the byte count for this dump line. Each line displays 16 bytes of memory in hexadecimal.

Loop Start
The loop below displays 16 bytes of hex data. For each byte: call 5165H for PC-highlight check and hex display, optionally display the ASCII equivalent (if address mode is A), and call 51D0H for non-ASCII mode display. Register B counts down from 16 to 0.

5141
GOSUB to the byte display with PC-highlight routine at 5165H. This compares the current address (HL) against the saved PC tracking address at 4060H: if they match, a 95H highlight character is displayed instead of a normal space before the hex byte. The byte at (HL) is then displayed as 2-digit hex. On return, HL is NOT incremented (5165H does not advance HL).
5144
LD A,(405DH) 3A 5D 40
Fetch the command mode byte from 405DH. This byte stores the last A or H command character: 41H (A) = ASCII display mode, 48H (H) = hex-only mode. The A/H commands at 4ECBH store the command character here.
5147
CP A,41H FE 41
Compare Register A against 41H (ASCII A). If equal, the Z FLAG is set and the memory dump should include an ASCII interpretation column alongside the hex display.
5149
If the NZ FLAG is set (mode is not A, so hex-only mode is active), JUMP to 515EH to display the byte in hex-only format via 51D0H and continue the loop.
514B
GOSUB to the print-space routine at 51F2H to output a space separator between the hex byte and its ASCII interpretation.
514E
LD A,(HL) 7E
Load Register A with the byte at the current memory address (HL). This is the same byte that was just displayed in hex; now it will be shown as its ASCII equivalent.
514F
CP A,20H FE 20
Compare Register A against 20H (ASCII space). If the byte value is less than 20H (a control character), the CARRY FLAG is set and the character is not printable.
5151
If the CARRY FLAG is set (byte < 20H, non-printable control character), JUMP to 5157H to display a dot (2EH) as a placeholder instead of the unprintable character.
5153
CP A,0C0H FE C0
Compare Register A against 0C0H. Byte values from C0H-FFH are TRS-80 block graphics characters that may not display meaningfully in a hex dump context. If the byte is below C0H, it is in the printable ASCII/semigraphics range (20H-BFH).
5155
If the CARRY FLAG is set (byte is in the range 20H-BFH, printable), JUMP to 5159H to display the actual ASCII character.
5157
LD A,2EH 3E 2E
Load Register A with 2EH (ASCII .). This dot character replaces non-printable bytes (below 20H or C0H and above) in the ASCII column of the memory dump.
5159
GOSUB to the ROM character output routine at 0033H to display the ASCII character (or dot placeholder) for the current byte.
515C
INC HL 23
INCrement Register Pair HL to advance to the next memory address for the dump.
515D
XOR A,A AF
Set Register A to zero. This clears the NZ flag so the CALL NZ at 515EH will NOT execute, skipping the hex-only display since the ASCII path already displayed the byte above.
515E
If the NZ FLAG is set (hex-only mode, not ASCII mode), GOSUB to 51D0H to display the byte at (HL) as 2-digit hex and INCrement HL. In ASCII mode, Register A was zeroed at 515DH so this call is skipped (the byte was already displayed in both hex and ASCII).
5161
DECrement Register B and LOOP BACK to 5141H if not zero. Continues displaying the next byte until all 16 bytes of the line have been shown.
5163
POP BC C1
Restore Register Pair BC from the stack (saved at 5131H). This restores the caller's loop counter in Register B.
5164
RET C9
RETurn to the caller (register dump at 4F27H or memory dump at 4F34H).

5165H - Byte Display with PC Highlight

Displays a single byte from memory with optional PC-position highlighting. Compares the current address (HL) against the memory modify/PC tracking address at 4060H. If they match, displays a 95H highlight marker (a TRS-80 semigraphics block character) before the byte; otherwise displays a normal space. Also inserts a midpoint space separator when Register B = 08H (after 8 bytes displayed). Called by the memory dump line loop at 5141H.

5165
LD DE,(4060H) ED 5B 60 40
Load Register Pair DE with the PC tracking address from 4060H. This is the address of the current instruction being examined (the saved PC or the memory modify address).
5169
INC DE 13
INCrement DE by 1. The comparison below uses SBC HL,DE, so incrementing DE first means the match occurs when HL = DE - 1 + 1 = DE_original. This adjusts for the fact that the display position should highlight the byte AT the PC address, not after it.
516A
PUSH HL E5
Save the current dump address (HL) onto the stack. The SBC HL,DE below is destructive and will change HL, so the original value must be preserved.
516B
XOR A,A AF
Set Register A to zero and clear the CARRY flag. The SBC instruction below subtracts with borrow, so the CARRY must be clear for an accurate 16-bit comparison.
516C
SBC HL,DE ED 52
SUBtract Register Pair DE (PC address + 1) from HL (current dump address) with borrow. If HL equals the PC address, the result is zero and the Z FLAG is set.
516E
LD A,95H 3E 95
Load Register A with 95H (a TRS-80 semigraphics block character). This is the visual highlight marker displayed before the byte at the PC address, making it easy to spot the current instruction in the hex dump.
5170
If the Z FLAG is set (the current dump address matches the PC address), JUMP to 5180H to display the 95H highlight character via ROM 0033H.
5172
GOSUB to 5184H. This checks whether Register B = 08H (the midpoint of a 16-byte line); if so, it outputs a space via 51F2H as a visual separator between the first and second groups of 8 bytes.
5175
INC HL 23
INCrement HL. At this point HL holds the result of SBC HL,DE (not the original address). This increment is used in the zero-test below to check if HL was -1 (i.e., the address was one byte BEFORE the PC), but the practical effect is to test whether the SBC result plus 1 equals zero.
5176
LD A,L 7D
Load Register A with the low byte of the adjusted SBC result.
5177
OR A,H B4
OR the high byte (H) into Register A. If the result is zero, the current address is one past the PC address (useful for highlighting the end of the current instruction).
5178
POP HL E1
Restore the original dump address from the stack into Register Pair HL (saved at 516AH).
5179
LD A,0AAH 3E AA
Load Register A with 0AAH (another TRS-80 semigraphics character). This is a secondary highlight marker displayed AFTER the byte at the PC address, forming a visual bracket around the highlighted byte (95H before, AAH after).
517B
If the Z FLAG is set (current address is one byte past the PC), JUMP to ROM 0033H to display the AAH closing highlight character and return. This marks the end of the PC-highlighted byte in the dump.
517E
JUMP to 5188H to print a space (normal separator) and continue. The current address is not adjacent to the PC, so no highlight marker is needed.
5180
GOSUB to ROM 0033H to display the 95H highlight character (loaded at 516EH). This marks the beginning of the PC-highlighted byte in the hex dump.
5183
POP HL E1
Restore the original dump address from the stack into Register Pair HL.
5184
LD A,B 78
Load Register A with the current byte count from Register B. This is checked for the midpoint separator.
5185
CP A,08H FE 08
Compare Register A against 08H (8 decimal). If B equals 08H, the display is at the midpoint of the 16-byte line (8 bytes displayed, 8 remaining).
5187
RET NZ C0
If the NZ FLAG is set (not at the midpoint), RETurn to the caller. No extra space is needed.
5188
JUMP to the print-space routine at 51F2H. At the midpoint (B=8), this inserts an extra space separator between the two groups of 8 hex bytes for readability. Execution returns to the caller after the space is printed.

518AH - Keyboard Input with Echo

Waits for a keypress, echoes printable characters to the screen, and returns the character in Register A with flags indicating the type of input. Control characters below 20H (except Enter at 0DH) are silently discarded. On return: Z FLAG set if character is a separator (space 20H or comma 2CH), CARRY FLAG set if Enter was pressed, NZ FLAG set for all other printable characters. Used throughout the DEBUG monitor for command input and hex value entry.

518A
PUSH DE D5
Save Register Pair DE onto the stack. DE is preserved across the keyboard polling loop since it may hold important context from the caller (e.g., breakpoint save area pointer).
518B
GOSUB to the ROM keyboard scan routine at 0049H. This non-blocking routine checks the keyboard and returns the pressed key in Register A, or 00H if no key is pressed.
518E
CP A,0DH FE 0D
Compare Register A against 0DH (ASCII Enter/carriage return). If Enter was pressed, the Z FLAG is set.
5190
If the Z FLAG is set (Enter pressed), JUMP to 51A0H to restore DE, set CARRY, and return. Enter signals end-of-input to the caller.
5192
CP A,20H FE 20
Compare Register A against 20H (ASCII space). If the character is below 20H (a control character other than Enter), the CARRY FLAG is set.
5194
If the CARRY FLAG is set (character < 20H, a non-Enter control character or no keypress), LOOP BACK to 518BH to poll the keyboard again. Control characters are silently discarded.
5196
GOSUB to ROM 0033H to echo the printable character (Register A) to the screen. The user sees the character they typed.
5199
POP DE D1
Restore Register Pair DE from the stack (saved at 518AH).
519A
CP A,2CH FE 2C
Compare Register A against 2CH (ASCII comma). If the character is a comma, the Z FLAG is set (separator detected).
519C
RET Z C8
If the Z FLAG is set (character is a comma), RETurn with Z set. The caller interprets Z as "separator" (end of current field, more input follows).
519D
CP A,20H FE 20
Compare Register A against 20H (ASCII space). If the character is a space, the Z FLAG is set (separator). For any other printable character (letters, digits, symbols), the NZ FLAG is set.
519F
RET C9
RETurn to the caller. Flags: Z = space (separator), NZ = printable character (letter/digit/symbol), CARRY is clear. Register A holds the character.
51A0
POP DE D1
Restore Register Pair DE from the stack (saved at 518AH).
51A1
SCF 37
Set the CARRY FLAG to indicate that Enter was pressed. The CARRY flag is the signal to callers that input is complete (no more data to read).
51A2
RET C9
RETurn to the caller with CARRY set (Enter pressed).

51A3H - Read Hex Number from Keyboard

Reads a multi-digit hexadecimal number from the keyboard, building the 16-bit result in Register Pair HL using the shift-left-4-and-OR algorithm. Each new hex digit shifts HL left by 4 bits (one nibble) and OR's the new digit into the low nibble. Returns the accumulated value in HL with flags indicating how input ended: NZ + no-CARRY = value entered followed by separator, Z = no digits entered (separator only), CARRY = Enter pressed with no input (handled at 51ADH by jumping to 4E4BH). Called by the M command (4FDBH), R command (5054H), and D command (4EA8H).

51A3
GOSUB to the keyboard input routine at 518AH to read the first character. On return: CARRY = Enter, Z = separator (space/comma), NZ = printable character.
51A6
RET Z C8
If the Z FLAG is set (the first character is a separator with no hex digits), RETurn immediately with Z set. The caller detects this as "no input provided" and HL is unchanged.
51A7
LD HL,0000H 21 00 00
Initialize Register Pair HL to 0000H. This is the accumulator for the hex value being built digit by digit.
51AA
GOSUB to the hex digit converter at 51BFH. On entry, Register A holds the typed character. On return: Register A holds the binary value 0-15 with CARRY clear if valid, or CARRY set if the character is not a valid hex digit (0-9, A-F).
51AD
If the CARRY FLAG is set (invalid hex character, including Enter which passed through from 518AH), JUMP to the command loop at 4E4BH. This aborts the hex input and returns to the DEBUG command prompt. For Enter pressed after the first character but before any digits, this effectively cancels the command.
51B0
ADD HL,HL 29
Shift Register Pair HL left by 1 bit (multiply by 2). This is the first of four shifts that together shift HL left by 4 bits (one nibble position).
51B1
ADD HL,HL 29
Shift HL left by 1 bit again (cumulative: x4).
51B2
ADD HL,HL 29
Shift HL left by 1 bit again (cumulative: x8).
51B3
ADD HL,HL 29
Shift HL left by 1 bit again (cumulative: x16). The previous value of HL has been shifted left by 4 bits, making room for the new digit in the low nibble.
51B4
OR A,L B5
OR Register A (the new hex digit value, 0-15) with Register L. This inserts the new digit into the low nibble of L, which was cleared to 0 by the four left-shifts above.
51B5
LD L,A 6F
Store the updated low byte back to Register L. HL now holds the accumulated hex value with the new digit appended.
51B6
GOSUB to the keyboard input routine at 518AH to read the next character. If the user types another hex digit, it will be accumulated in the next iteration. If a separator or Enter, input ends.
51B9
If the NZ FLAG is set (a printable character was typed, not a separator), LOOP BACK to 51AAH to convert the next hex digit and accumulate it. The loop continues until the user types a separator or Enter.

Input Complete
The user typed a separator (space or comma) after one or more hex digits. HL holds the accumulated hex value. The code below sets flags to indicate "value entered, terminated by separator" (NZ + no-CARRY). The RRA/ADC trick creates a deliberate non-zero result to set NZ.

51BB
RRA 1F
Rotate Register A right through carry. Register A holds 20H (space) or 2CH (comma) from the separator. RRA shifts bit 0 into CARRY and CARRY (which was clear from the CP in 518AH) into bit 7. Result: A = 10H or 16H, CARRY = 0.
51BC
ADC A,81H CE 81
ADD 81H to Register A with carry. Since CARRY is clear, this adds 81H: 10H + 81H = 91H, or 16H + 81H = 97H. The result is non-zero, setting the NZ FLAG. The CARRY from this addition may be set but is not tested by callers since the primary signal is NZ + the value in HL.
51BE
RET C9
RETurn to the caller. HL holds the entered hex value. NZ FLAG is set (value was entered). CARRY state is not meaningful. The caller uses NZ to confirm input was provided and HL as the value.

51BFH - Hex Digit Converter

Converts a single ASCII hex character (0-9, A-F) in Register A to its binary value (0-15). Uses a chain of SUB/ADD instructions that progressively narrow the valid range. Returns: CARRY clear and A = 0-15 if valid hex digit; CARRY set if invalid character. Called by the hex number input routine at 51AAH.

51BF
SUB A,30H D6 30
SUBtract 30H from Register A. Converts ASCII '0' (30H) to 0, '9' (39H) to 9. If the original character was below '0' (below 30H), the result underflows and the CARRY FLAG is set.
51C1
RET C D8
If the CARRY FLAG is set (character was below ASCII '0'), RETurn with CARRY set indicating invalid character.
51C2
ADD A,0E9H C6 E9
ADD 0E9H to Register A. For digits 0-9 (values 00H-09H after the SUB): result = E9H-F2H, no carry (values wrap within byte range). For characters above '9' (0AH+): A = 0AH + E9H = F3H for ':', up to 11H + E9H = FAH for 'A'. The character 'G' (17H + E9H = 00H) would produce carry. But first, values 0AH-10H (characters ':' through '@') also need to be rejected.
51C4
RET C D8
If the CARRY FLAG is set (character was in the gap between '9'+1 and some upper bound), RETurn with CARRY set indicating invalid character. This rejects characters above 'F' in the raw ASCII range.
51C5
ADD A,06H C6 06
ADD 06H to Register A. For digits 0-9 (values E9H-F2H): result = EFH-F8H. For letters A-F (values F3H-F8H): result = F9H-FEH. If the addition overflows (produces carry), the character is a valid digit 0-9.
51C7
If the CARRY FLAG is set (the character was a digit '0'-'9'), JUMP to 51CCH to make the final adjustment and return the value 0-9.
51C9
ADD A,07H C6 07
ADD 07H to Register A. This adjusts for the gap between '9' (39H) and 'A' (41H) in ASCII. For letters A-F (values F9H-FEH after previous ADD): result = 00H-05H with carry. For characters ':' through '@' (gap characters), this addition does not produce carry, and the value remains invalid.
51CB
RET C D8
If the CARRY FLAG is set (invalid gap character between '9' and 'A'), RETurn with CARRY set. This rejects ':', ';', '<', '=', '>', '?', '@'.
51CC
ADD A,0AH C6 0A
ADD 0AH to Register A as the final adjustment. For digits '0'-'9': the accumulated value after all operations becomes 00H-09H. For letters 'A'-'F': the value becomes 0AH-0FH. Register A now holds the binary value of the hex digit.
51CE
OR A,A B7
OR Register A with itself. This clears the CARRY FLAG (OR always clears carry) while leaving the value unchanged. CARRY clear signals a valid hex digit to the caller at 51AAH.
51CF
RET C9
RETurn to the caller. Register A holds 00H-0FH (the binary value of the hex digit). CARRY is clear (valid digit).

51D0H - Display Byte from Memory

Reads a byte from the address in HL, increments HL, and displays the byte as 2-digit hex via the hex byte output routine at 51D9H. Called by the memory modify command (4FFDH) and the memory dump loop (515EH).

51D0
LD A,(HL) 7E
Load Register A with the byte at the memory address pointed to by Register Pair HL.
51D1
INC HL 23
INCrement Register Pair HL to point to the next memory address. The caller's HL is advanced past the byte that was just read.
51D2
JUMP to the hex byte output routine at 51D9H to display the byte in Register A as 2-digit hexadecimal.

51D4H - Display HL as 4-Digit Hex

Displays the 16-bit value in Register Pair HL as a 4-digit hexadecimal number. Outputs the high byte (H) first, then the low byte (L), by calling the hex byte output routine at 51D9H twice. Called by the memory modify display (4FF2H) and the memory dump address prefix (4F39H).

51D4
LD A,H 7C
Load Register A with the high byte of Register Pair HL (the upper 2 hex digits of the 16-bit value).
51D5
GOSUB to the hex byte output routine at 51D9H to display the high byte as 2 hex digits. After return, execution falls through to display the low byte.
51D8
LD A,L 7D
Load Register A with the low byte of Register Pair HL (the lower 2 hex digits). Falls through to 51D9H to display it.

51D9H - Hex Byte Output

Displays the byte in Register A as a 2-digit hexadecimal number. Outputs the high nibble first (by rotating right 4 times), then the low nibble, using the nibble-to-ASCII converter at 51E2H. Called by 51D4H (4-digit hex), 51D0H (memory byte display), 51EFH (hex byte with space), and the register dump at 4EFFH-4F01H.

51D9
PUSH AF F5
Save Register AF onto the stack. The full byte value is preserved so the low nibble can be output after the high nibble.
51DA
RRA 1F
Rotate Register A right through carry. First of four rotations to shift the high nibble into the low nibble position.
51DB
RRA 1F
Rotate right again (2 of 4).
51DC
RRA 1F
Rotate right again (3 of 4).
51DD
RRA 1F
Rotate right again (4 of 4). The high nibble of the original byte is now in the low nibble position of Register A (bits 0-3). The CARRY state from the rotations is irrelevant since 51E2H masks with 0FH.
51DE
GOSUB to the nibble-to-ASCII converter at 51E2H to display the high nibble as a single hex character (0-9, A-F).
51E1
POP AF F1
Restore the original byte value from the stack into Register A. The low nibble is now in bits 0-3, ready for conversion. Falls through to 51E2H.

51E2H - Nibble to ASCII Converter and Output

Converts the low nibble of Register A (value 0-15) to an ASCII hex character ('0'-'9' or 'A'-'F') and outputs it via ROM 0033H. Entry point for both high and low nibble display from 51D9H.

51E2
AND A,0FH E6 0F
Mask Register A with 0FH to isolate the low nibble (bits 0-3), discarding any high nibble bits from the rotation at 51DAH-51DDH. Result: 00H-0FH.
51E4
ADD A,30H C6 30
ADD 30H to convert the binary value to ASCII. Values 0-9 become 30H-39H (ASCII '0'-'9'). Values 10-15 become 3AH-3FH (characters ':' through '?'), which are not correct hex letters yet.
51E6
CP A,3AH FE 3A
Compare Register A against 3AH (ASCII ':'). If A is below 3AH, the value was a digit 0-9 and the ASCII representation is already correct. If A is 3AH or above, the value was 10-15 and needs an additional offset to reach 'A'-'F'.
51E8
If the CARRY FLAG is set (A < 3AH, digit 0-9), JUMP to 51ECH to output the character directly. No further adjustment needed.
51EA
ADD A,07H C6 07
ADD 07H to Register A. This bridges the gap between ASCII '9' (39H) and 'A' (41H): 3AH + 07H = 41H ('A'), 3FH + 07H = 46H ('F'). Register A now holds the correct ASCII character for hex letters A-F.
51EC
JUMP to the ROM character output routine at 0033H to display the hex character in Register A. Using JP instead of CALL+RET saves one byte; the ROM routine returns directly to this routine's caller.

51EFH - Hex Byte Output with Trailing Space

Displays the byte in Register A as 2-digit hex, followed by a space. Convenience wrapper that calls 51D9H then falls through to 51F2H. Called by the register dump display at 4EFFH-4F01H to display each register byte with spacing.

51EF
GOSUB to the hex byte output routine at 51D9H to display the byte in Register A as 2-digit hex. Falls through to 51F2H to append a space.

51F2H - Print Space

Outputs a single space character (20H) via ROM 0033H. Used as a separator throughout the DEBUG display routines. Frequently reached via JR or fall-through from adjacent routines.

51F2
LD A,20H 3E 20
Load Register A with 20H (ASCII space character).
51F4
JUMP to 51ECH which executes JP 0033H to output the space character. The ROM routine returns directly to this routine's caller.

51F6H - Print Two Characters from Table

Reads and displays two consecutive bytes from the address in HL, advancing HL by 2. Used by the register dump at 4EEAH to print the 2-character register name from the name table at 4F54H (e.g., "AF", "BC", "HL").

51F6
GOSUB to the print-character-from-table routine at 51FCH to read and display the first byte at (HL), then increment HL.
51F9
GOSUB to 51FCH again to read and display the second byte at (HL), then increment HL. After two calls, HL has advanced by 2 and both characters have been displayed.

51FCH - Print Character from Table

Reads one byte from the address in HL, increments HL, and outputs the byte as a character via 51ECH (JP 0033H). The simplest output primitive: one byte from memory to screen.

51FC
LD A,(HL) 7E
Load Register A with the byte at the memory address pointed to by Register Pair HL. This is the character to be displayed.
51FD
INC HL 23
INCrement Register Pair HL to point to the next byte in the table.
51FE
JUMP to 51ECH which executes JP 0033H to output the character in Register A. The ROM routine returns directly to this routine's caller.