TRS-80 DOS - LDOS 5.3.1 for the Model I - SYS5/SYS Disassembled

Page Customization

Summary:

LDOS 5.3.1 SYS5/SYS Disassembly - DEBUG Monitor (Model I)

SYS5/SYS is the LDOS 5.3.1 DEBUG overlay for the TRS-80 Model I. It is the interactive machine-language monitor that the operating system enters whenever the user issues the DEBUG command, whenever a planted breakpoint fires, or whenever a program transfers control to the DEBUG re-entry hook. DEBUG lets the user inspect and change the Z80 register set, examine and patch memory in hexadecimal or ASCII, set execution breakpoints, single-step or trace through code, and resume execution.

Like every other LDOS system overlay, SYS5/SYS is not resident. It is pulled in from disk by the resident RST 28H supervisor at SYSRES address 4BF5H, loaded into the overlay region beginning at 4E00H, and given control at its transfer address 4E00H. When DEBUG first gains control it takes a complete snapshot of the caller's Z80 registers into the resident DEBUG save area DBGSV$ (405DH-407CH), then runs its own command loop until the user resumes the program with the G (Go) command, at which point the saved registers are reloaded and control returns to the program under test.

The overlay reaches the interactive user only through the resident kernel. It calls SYSRES routines for none of its disk work (DEBUG performs no direct disk I/O once it is loaded); instead it drives the screen and keyboard through the Model I ROM (character output at 0033H, keyboard scan at 002BH and 0049H) and manipulates the resident video Device Control Block DODCB$ cursor field at 4020H to place its display. Breakpoints are implemented by overwriting the target instruction with an RST 30H opcode (F7H); when that RST fires it vectors through ROM 0030H to the resident hook at 400FH, which re-enters this overlay so the saved-register snapshot can be rebuilt.

Two regions of the overlay are pure data that the source-listing disassembler renders as meaningless instructions: the register/flag name table at 4F53H-4F7EH, and the three instruction-length/type lookup tables at 50E0H-5130H that drive the single-step next-address calculation. Both are documented here as data.

Program Variables and Work Areas (in resident SYSRES DBGSV$ and DODCB$)

DEBUG keeps all of its state in the resident SYSRES DEBUG save area DBGSV$, which begins at 405DH. The low six bytes are shared: on entry they hold the two-entry breakpoint table, but while the user is browsing interactively DEBUG reuses the same bytes as its display-mode flags. The register snapshot occupies the upper 24 bytes.

Address RangePurpose
405DH-405EH
2 bytes
Breakpoint 2 address (planted-breakpoint table). While browsing, reused as two one-byte flags: 405DH = memory-display mode (a = ASCII, h = hex); 405EH = screen layout flag (x = register+memory screen, value 0; s = full 16-line memory screen, non-zero).
405FH
1 byte
Breakpoint 2 saved original opcode byte (the byte that the F7H RST 30H overwrote, restored when DEBUG is re-entered).
4060H-4061H
2 bytes
Breakpoint 1 address. While browsing, 4060H is reused as the memory-modify cursor pointer used by the m command and the cursor block in the memory display.
4062H
1 byte
Breakpoint 1 saved original opcode byte. Also the base of DEBUG's small scratch/output area used during Go-parameter parsing and single-step decoding.
4063H-4064H
2 bytes
Memory-display window address: the first address shown in the scrolling memory dump, set by the d command and advanced by the ; and - scroll commands.
4065H-407CH
24 bytes
Register snapshot block, in the order AF, BC, DE, HL, AF', BC', DE', HL', IX, IY, SP, PC (each low byte first). 4079H-407AH is overwritten with the caller's stack pointer and 407BH-407CH holds the caller's program counter. The r command edits words in this block; the g command reloads the CPU from it.
4020H-4021H
2 bytes
Resident video DCB (DODCB$) cursor-position word. DEBUG stores a screen address here to steer the ROM character-output routine to a specific row before painting a display block.
4310H-4317H
8 bytes
Resident SYSRES hooks/state. 4310H holds the vector taken for any key that is not a recognized DEBUG command; 4315H-4317H is patched by the Go command with a JP 400FH to arm RST 30H breakpoint re-entry (400FH is the resident RST 30H hook).

Major Routines

AddressName and Purpose
4E00HDEBUG Entry - Full Register Save
Discards one stack level, pushes every Z80 register (both banks, IX, IY), copies the 24-byte snapshot to 4065H, records the caller's SP at 4079H and PC at 407BH.
4E23HBreakpoint PC Fix-up and Restore
If the byte just before the saved PC is an F7H (RST 30H) breakpoint, backs the PC up onto it; clears the two-entry breakpoint table and restores the original bytes it had overwritten.
4E49HMain Command Loop
Resets SP, repaints the display, reads one command key, and dispatches it. Command handlers return here to loop.
4ECFHScreen Refresh
Paints the register-and-memory screen (12 register lines, each with a decoded flag byte and the 16 memory bytes it points at) or, in s mode, a 16-line full-memory screen.
4F7FHGo Command
Reads an optional new PC and up to two breakpoint addresses, plants F7H RST 30H breakpoints, arms the re-entry hook, reloads the CPU from the snapshot, and transfers to the program.
4FDAHModify Memory Command
Reads a memory address and lets the user deposit new bytes at the memory-modify pointer 4060H.
5010HModify Register Command
Reads a two-character register name, finds its slot in the 4065H snapshot, and stores a new hex value there.
505EHSingle-Step Next-Address Decoder
Reads the opcode at the saved PC, uses the length/type tables to compute the address of the next instruction (following jumps, and stepping over or into calls), plants a temporary breakpoint there, and goes.
5164HMemory-Line Formatter
Emits one byte of the memory display in hex or ASCII, drawing the cursor block at the modify pointer and a mid-line separator.
5189HKeyboard Command Reader
Waits for a key, ignores control characters, folds letters to lowercase, echoes the key, and reports comma/space/ENTER delimiters.
51A4HHex Parameter Parser
Reads a hexadecimal value from the keyboard into HL and reports which delimiter ended it.
51D1HHexadecimal Output
Family of routines that print a byte or a register pair as ASCII hex digits via ROM 0033H, with optional trailing spaces.

Cross-Reference Notes

SYS5/SYS is entered by the resident RST 28H overlay loader at SYSRES 4BF5H and by the RST 30H breakpoint hook at 400FH. It reads and writes the resident DEBUG save area DBGSV$ (405DH), the video DCB cursor field 4020H, and the hooks/state bytes 4310H-4317H, all of which live in SYS0/SYS (SYSRES). It calls no other SYS overlay. Its only external calls are to the Model I ROM: 002BH (scan keyboard), 0033H (display character), and 0049H (wait for a key).

Disassembly:

4BC9H - Resident Load Block

A two-byte block deposited by the overlay loader into the SYSRES padding at 4BBCH-4BCAH, just ahead of the resident execute-drive-operation routine at 4BCBH. It is data, not executed on the DEBUG path. Each SYS overlay leaves its own two bytes here (SYS1 wrote F9H 03H, SYS2 wrote 00H 04H, SYS3 wrote A6H 02H, SYS4 wrote AFH 03H, SYS5 writes FFH 03H).

4BC9
DEFB FFH FF
Load Block Byte 1
Deposited into SYSRES 4BC9H by the loader as part of overlay housekeeping. Data only; never executed as an instruction on the DEBUG path.
4BCA
DEFB 03H 03
Load Block Byte 2
Second byte of the resident load block at SYSRES 4BCAH. Data only.

4E00H - DEBUG Entry: Full Register Save

The overlay transfer address. On entry the caller's registers are exactly as they were when DEBUG was invoked (via the RST 28H DEBUG request, or via an RST 30H breakpoint that vectored through the resident hook at 400FH). This block captures every Z80 register so the user can inspect and later restore them.

4E00
POP AF F1
Discard the top stack word (the overlay dispatcher's return address into SYSRES) into Register Pair AF, which is about to be overwritten anyway. This removes one return level so the word beneath it becomes the top of stack.
4E01
POP AF F1
Pop the next stack word into Register Pair AF. This word is the value DEBUG must keep on the caller's stack; it is re-pushed on the following line.
4E02
PUSH AF F5
Push that word back onto the stack. The net effect of the three preceding lines is to strip exactly one return address from the stack so that the remaining stack matches the state the caller was in.
4E03
PUSH IY FD E5
Save the caller's Index Register IY onto the stack as the first entry of the register snapshot being built.
4E05
PUSH IX DD E5
Save the caller's Index Register IX onto the stack.
4E07
EX AF,AF' 08
Exchange the main Register Pair AF with the alternate AF' so the alternate accumulator and flags can be saved next. The alternate set is now the active AF.
4E08
EXX D9
Exchange BC, DE, HL with their alternate set BC', DE', HL'. The alternate register set is now active so it can be pushed.
4E09
PUSH HL E5
Save the caller's alternate Register Pair HL' (currently active) onto the stack.
4E0A
PUSH DE D5
Save the caller's alternate Register Pair DE' onto the stack.
4E0B
PUSH BC C5
Save the caller's alternate Register Pair BC' onto the stack.
4E0C
PUSH AF F5
Save the caller's alternate Register Pair AF' (made active at 4E07H) onto the stack.
4E0D
EX AF,AF' 08
Swap AF back so the main accumulator and flags are active again.
4E0E
EXX D9
Swap BC, DE, HL back so the main register set is active again for saving.
4E0F
PUSH HL E5
Save the caller's main Register Pair HL onto the stack.
4E10
PUSH DE D5
Save the caller's main Register Pair DE onto the stack.
4E11
PUSH BC C5
Save the caller's main Register Pair BC onto the stack.
4E12
PUSH AF F5
Save the caller's main Register Pair AF onto the stack. The stack now holds, from the current top upward, AF, BC, DE, HL, AF', BC', DE', HL', IX, IY, plus the two words left below them.
4E13
LD HL,0000H 21 00 00
Load Register Pair HL with zero to prepare to read the stack pointer, which cannot be read directly.
4E16
ADD HL,SP 39
Add the Stack Pointer SP to the zeroed HL, so HL now holds the current SP, which points at the saved AF at the top of the register snapshot.
4E17
LD DE,4065H 11 65 40
Point Register Pair DE at 4065H, the start of the 24-byte register snapshot block in the resident DEBUG save area DBGSV$.
4E1A
LD BC,0018H 01 18 00
Load Register Pair BC with 24, the number of bytes to copy: ten register pairs (20 bytes) plus the caller's SP and PC words.
4E1D
LDIR ED B0
Block-copy 24 bytes from the stack (source HL) to the snapshot block at 4065H (destination DE), incrementing both. On completion HL points just past the snapshot on the stack, which is the caller's original stack pointer value.
4E1F
LD (4079H),HL 22 79 40
Store HL (the caller's original SP, computed by LDIR) into 4079H-407AH, the SP slot of the snapshot, overwriting the placeholder that LDIR just wrote there. The register display will show this as SP.
4E22
LD SP,HL F9
Set the Stack Pointer SP to the caller's original SP, unwinding DEBUG's own register-save pushes so the stack is back to the caller's state.

4E23H - Breakpoint PC Fix-up

DEBUG plants breakpoints as an RST 30H opcode (F7H). When one fires, the CPU has already advanced the program counter past the F7H byte, so the saved PC points one byte too high. This block detects that case and backs the PC up onto the breakpoint.

4E23
LD HL,(407BH) 2A 7B 40
Load Register Pair HL with the caller's saved program counter from 407BH-407CH, the PC slot of the snapshot.
4E26
DEC HL 2B
Decrement HL to point at the byte just before the saved PC, the location a breakpoint opcode would occupy if this entry came from an RST 30H.
4E27
LD A,(HL) 7E
Fetch that byte (the possible breakpoint opcode at PC minus one) into Register A.
4E28
CP 0F7H FE F7
Compare Register A against F7H, the opcode for RST 30H. If Register A equals F7H the Z FLAG is set, meaning this entry was caused by a planted breakpoint.
4E2A
If the NZ FLAG is set (the byte before the PC is not an F7H breakpoint), JUMP to 4E2FH to skip the PC correction and go straight to breakpoint-table cleanup.
4E2C
LD (407BH),HL 22 7B 40
Store the decremented HL back into the saved PC at 407BH-407CH, so the displayed PC points at the breakpoint instruction rather than one byte past it.

4E2FH - Clear Breakpoint Table and Restore Bytes

DEBUG keeps a two-entry breakpoint table in the resident save area at 405DH-4062H. Each entry is a two-byte address followed by the one original opcode byte that the F7H breakpoint overwrote. This loop restores those original bytes and zeroes the table so the same six bytes can be reused as display flags while the user browses.

4E2F
LD HL,405DH 21 5D 40
Point Register Pair HL at 405DH, the first byte of the breakpoint table in the resident DEBUG save area DBGSV$.
4E32
LD B,02H 06 02
Load Register B with 2, the number of breakpoint slots to process.
4E34
XOR A AF
Loop Start
Set Register A to zero and clear all flags; the zero will be written over the breakpoint table bytes to clear them.
4E35
LD E,(HL) 5E
Load Register E with the low byte of this slot's breakpoint address from the table at HL.
4E36
LD (HL),A 77
Store zero (Register A) over that table byte, clearing the low address byte so the slot is empty for reuse.
4E37
INC HL 23
Advance HL to the high byte of the breakpoint address.
4E38
LD D,(HL) 56
Load Register D with the high byte of this slot's breakpoint address, completing the address in Register Pair DE.
4E39
LD (HL),A 77
Store zero over the high address byte, clearing it.
4E3A
INC HL 23
Advance HL to this slot's saved original opcode byte.
4E3B
LD A,E 7B
Copy the low breakpoint address byte (Register E) into Register A to test whether the address is zero.
4E3C
OR D B2
OR in the high address byte (Register D). If the whole breakpoint address in DE was zero the Z FLAG is set, meaning this slot was unused.
4E3D
If the Z FLAG is set (the breakpoint address in DE is zero, so nothing was planted), JUMP to 4E46H to skip restoration for this slot.
4E3F
LD A,(DE) 1A
Fetch the current byte at the breakpoint address DE into Register A to confirm the breakpoint is still present.
4E40
CP 0F7H FE F7
Compare Register A against F7H (RST 30H). If it equals F7H the Z FLAG is set, confirming DEBUG's breakpoint opcode is still in place at DE.
4E42
If the NZ FLAG is set (the byte at DE is no longer F7H, so the program overwrote it), JUMP to 4E46H without restoring, since the original byte no longer belongs there.
4E44
LD A,(HL) 7E
Load Register A with the saved original opcode byte from the table (at HL), the byte the breakpoint had overwritten.
4E45
LD (DE),A 12
Store the saved original byte (Register A) back at the breakpoint address DE, removing the F7H breakpoint and healing the program.
4E46
INC HL 23
Advance HL past the saved-byte field to the start of the next breakpoint slot.
4E47
Decrement Register B and LOOP BACK to 4E34H to process the second breakpoint slot; fall through when both slots are done.

4E49H - Main Command Loop and Dispatch

The heart of DEBUG. It resets the stack, repaints the display, positions the cursor on the bottom screen line, reads one command key, and dispatches to the handler for that key. Every command handler returns here (its return address is pushed at 4E5EH) so DEBUG loops until the user issues the Go command.

4E49
LD SP,(4079H) ED 7B 79 40
Reset the Stack Pointer SP to the caller's saved SP held at 4079H-407AH. This discards any leftover working data so each command starts with a clean stack.
4E4D
GOSUB to 4ECFH to repaint the DEBUG screen (register lines and memory dump, or the full-memory screen).
4E50
LD HL,3FC0H 21 C0 3F
Load Register Pair HL with 3FC0H, the video RAM address of the bottom-left character cell, where the command prompt will appear.
4E53
LD (4020H),HL 22 20 40
Store 3FC0H into the resident video DCB (DODCB$) cursor field at 4020H-4021H so the next ROM character output lands on the bottom line.
4E56
GOSUB to 5189H to wait for and read one command key, returning its lowercased ASCII code in Register A.
4E59
CP 67H FE 67
Compare the command key (Register A) against 67H (ASCII g). If Register A equals 67H the Z FLAG is set.
4E5B
If the Z FLAG is set (the key was g), JUMP to 4F7FH, the Go command that resumes program execution. Go does not return to the loop.
4E5E
LD HL,4E49H 21 49 4E
Load Register Pair HL with 4E49H, the address of this command loop, to be used as the return address for the remaining command handlers.
4E61
PUSH HL E5
Push 4E49H onto the stack so that any command handler reached below can simply RET to loop back here.
4E62
CP 73H FE 73
Compare the command key (Register A) against 73H (ASCII s). If equal the Z FLAG is set.
4E64
If the Z FLAG is set (the key was s), JUMP to 4E9AH to select the full-screen memory display.
4E66
CP 3BH FE 3B
Compare the command key (Register A) against 3BH (ASCII ;). If equal the Z FLAG is set.
4E68
If the Z FLAG is set (the key was ;), JUMP to 4EAEH to scroll the memory display forward.
4E6A
CP 2DH FE 2D
Compare the command key (Register A) against 2DH (ASCII -). If equal the Z FLAG is set.
4E6C
If the Z FLAG is set (the key was -), JUMP to 4EC6H to scroll the memory display backward.
4E6E
CP 61H FE 61
Compare the command key (Register A) against 61H (ASCII a). If equal the Z FLAG is set.
4E70
If the Z FLAG is set (the key was a), JUMP to 4ECBH to select ASCII memory-display mode.
4E72
CP 63H FE 63
Compare the command key (Register A) against 63H (ASCII c). If equal the Z FLAG is set.
4E74
If the Z FLAG is set (the key was c), JUMP to 4E80H, which (with the Z FLAG still set from this compare) falls into the single-step call at 505EH to step over the current instruction.
4E76
CP 64H FE 64
Compare the command key (Register A) against 64H (ASCII d). If equal the Z FLAG is set.
4E78
If the Z FLAG is set (the key was d), JUMP to 4EA8H to set the memory-display window address.
4E7A
CP 68H FE 68
Compare the command key (Register A) against 68H (ASCII h). If equal the Z FLAG is set.
4E7C
If the Z FLAG is set (the key was h), JUMP to 4ECBH to select hexadecimal memory-display mode.
4E7E
CP 69H FE 69
Compare the command key (Register A) against 69H (ASCII i). If equal the Z FLAG is set.
4E80
If the Z FLAG is set (the key was i, or c arriving from 4E74H), JUMP to 505EH, the single-step next-address decoder. The c and i commands share this code and are told apart there by the saved command key.
4E83
CP 6DH FE 6D
Compare the command key (Register A) against 6DH (ASCII m). If equal the Z FLAG is set.
4E85
If the Z FLAG is set (the key was m), JUMP to 4FDAH, the modify-memory command.
4E88
CP 72H FE 72
Compare the command key (Register A) against 72H (ASCII r). If equal the Z FLAG is set.
4E8A
If the Z FLAG is set (the key was r), JUMP to 5010H, the modify-register command.
4E8D
CP 75H FE 75
Compare the command key (Register A) against 75H (ASCII u). If equal the Z FLAG is set.
4E8F
If the Z FLAG is set (the key was u), JUMP to 4E9EH, the continuous update mode.
4E91
CP 78H FE 78
Compare the command key (Register A) against 78H (ASCII x). If equal the Z FLAG is set.
4E93
If the Z FLAG is set (the key was x), JUMP to 4E99H to select the register-and-memory display.
4E95
LD HL,(4310H) 2A 10 43
The key matched no command. Load Register Pair HL with the vector stored at the resident hooks/state word 4310H, the address DEBUG uses for any unrecognized key.
4E98
JP (HL) E9
Jump to the address in HL taken from 4310H, handing an unrecognized key to the resident unrecognized-command vector.

4E99H - Display Mode Command Handlers

Short handlers for the display-control keys. The x and s keys set the screen-layout flag at 405EH; u enters a live update loop; d sets the memory window address; ; and - scroll it; a and h set the memory-display mode at 405DH. Each returns to the command loop at 4E49H pushed on the stack.

4E99
XOR A AF
Command x
Set Register A to zero. Zero selects the register-and-memory screen layout, stored next.
4E9A
LD (405EH),A 32 5E 40
Command s
Store Register A into the screen-layout flag at 405EH. Reached from x with A=0 (register+memory screen) or from s with A=73H (non-zero, full 16-line memory screen).
4E9D
RET C9
Return to the command loop at 4E49H (pushed at 4E61H), which repaints using the new layout flag.
4E9E
Command u
GOSUB to the Model I ROM keyboard scan at 002BH, which returns the ASCII of any key pressed in Register A, or zero if none.
4EA1
OR A B7
Test Register A. If a key was pressed (non-zero) the NZ FLAG is set; if no key was pressed (zero) the Z FLAG is set.
4EA2
RET NZ C0
If the NZ FLAG is set (a key was pressed) return to the command loop, ending the live update.
4EA3
GOSUB to 4ECFH to repaint the display, so values that change in memory or registers are shown live.
4EA6
LOOP BACK to 4E9EH to keep polling the keyboard and refreshing until a key is pressed.
4EA8
Command d
GOSUB to 51A4H to read a hexadecimal address from the keyboard into Register Pair HL.
4EAB
RET Z C8
If the Z FLAG is set (no address was entered, only a delimiter) return to the command loop, leaving the memory window unchanged.
4EAC
JUMP to 4EC2H to store the entered address (HL) as the new memory-display window.
4EAE
LD BC,0040H 01 40 00
Command ;
Load Register Pair BC with 40H (64), the forward scroll distance for the memory window in the register-screen layout.
4EB1
LD HL,(4063H) 2A 63 40
Load Register Pair HL with the current memory-display window address from 4063H-4064H.
4EB4
LD A,(405EH) 3A 5E 40
Load Register A with the screen-layout flag at 405EH to decide the scroll distance.
4EB7
OR A B7
Test the layout flag. If zero (register+memory screen) the Z FLAG is set; if non-zero (full-memory screen) the NZ FLAG is set.
4EB8
If the Z FLAG is set (register screen), JUMP to 4EC1H keeping the 40H scroll distance in BC.
4EBA
LD C,00H 0E 00
Full-memory screen: clear Register C so the scroll distance becomes a whole 256-byte page (adjusted next).
4EBC
LD A,B 78
Copy the high scroll byte (Register B) into Register A to test its sign for the backward-scroll case.
4EBD
OR A B7
Test Register B. For forward scroll B is 00H (Z FLAG set); for backward scroll (entered from 4EC6H) B is FFH (NZ FLAG set).
4EBE
If the NZ FLAG is set (backward scroll, BC already FF00H = minus 256), JUMP to 4EC1H to apply it.
4EC0
INC B 04
Forward full-memory scroll: increment Register B to make BC equal 0100H, a full 256-byte page forward.
4EC1
ADD HL,BC 09
Add the scroll distance BC to the window address HL, advancing (or, with a negative BC, retreating) the memory display.
4EC2
LD (4063H),HL 22 63 40
Store the new window address (HL) into 4063H-4064H. Also reached from the d command to set the window directly.
4EC5
RET C9
Return to the command loop at 4E49H, which repaints memory from the new window address.
4EC6
LD BC,0FFC0H 01 C0 FF
Command -
Load Register Pair BC with FFC0H (minus 64), the backward scroll distance for the register-screen layout.
4EC9
JUMP to 4EB1H to apply the backward scroll, sharing the forward-scroll code with a negative BC.
4ECB
LD (405DH),A 32 5D 40
Commands a and h
Store the command key (Register A) into the memory-display mode flag at 405DH. Register A holds 61H (a) for ASCII display or 68H (h) for hexadecimal display.
4ECE
RET C9
Return to the command loop at 4E49H, which repaints memory in the newly selected mode.

4ECFH - Screen Refresh: Register and Memory Display

Repaints the DEBUG screen. In the register layout it prints twelve lines, one per register pair (AF, BC, DE, HL, AF', BC', DE', HL', IX, IY, SP, PC), each showing the register value, and for AF and AF' a decoded flag field, followed by the sixteen memory bytes that register points at. If the full-memory flag at 405EH is set it hands off to 4F44H instead.

4ECF
LD A,(405EH) 3A 5E 40
Load Register A with the screen-layout flag at 405EH to choose which display to paint.
4ED2
OR A B7
Test the flag. Zero (Z FLAG set) selects the register+memory screen; non-zero (NZ FLAG set) selects the full-memory screen.
4ED3
If the NZ FLAG is set (full-memory layout selected by the s command), JUMP to 4F44H to paint sixteen memory lines only.
4ED5
LD A,1CH 3E 1C
Load Register A with 1CH, the TRS-80 control code that homes the cursor to the top-left of the screen.
4ED7
GOSUB to the Model I ROM character output at 0033H to home the cursor before painting the register lines.
4EDA
LD HL,4065H 21 65 40
Point Register Pair HL at 4065H, the start of the register snapshot block, to walk the saved register values.
4EDD
PUSH HL E5
Save the snapshot pointer on the stack; the loop alternates HL between the snapshot pointer and the name-table pointer using EX (SP),HL.
4EDE
LD HL,4F53H 21 53 4F
Point Register Pair HL at 4F53H, the start of the register-name table ("AF BC DE ...").
4EE1
LD B,0CH 06 0C
Load Register B with 12, the number of register pairs to display; B also indexes which register is current.
4EE3
JUMP into the loop body at 4EEAH, skipping the newline that separates lines (the first line needs no leading newline).
4EE5
LD A,0DH 3E 0D
Loop Start
Load Register A with 0DH (carriage return) to end the previous register line and start a new one.
4EE7
GOSUB to ROM 0033H to output the carriage return.
4EEA
GOSUB to 51F5H to display the two-character register name at HL (for example "AF"), advancing HL past it.
4EED
EX (SP),HL E3
Swap HL with the saved pointer on the stack: HL now holds the snapshot pointer, and the name-table pointer is parked on the stack.
4EEE
LD E,(HL) 5E
Load Register E with the low byte of this register's saved value from the snapshot at HL.
4EEF
INC HL 23
Advance the snapshot pointer to the high byte.
4EF0
LD D,(HL) 56
Load Register D with the high byte, completing the saved register value in Register Pair DE.
4EF1
INC HL 23
Advance the snapshot pointer to the next register's value.
4EF2
PUSH HL E5
Save the updated snapshot pointer on the stack for the next iteration.
4EF3
EX DE,HL EB
Move the saved register value into Register Pair HL so it can be printed and used as a memory address.
4EF4
LD A,3DH 3E 3D
Load Register A with 3DH (ASCII =) to separate the register name from its value.
4EF6
GOSUB to ROM 0033H to output the equals sign.
4EF9
GOSUB to 51F1H to output one space after the equals sign.
4EFC
LD A,H 7C
Load Register A with the high byte of the register value (Register H) to print it first.
4EFD
GOSUB to 51EEH to print the high byte as two hexadecimal digits followed by a space.
4F00
LD A,L 7D
Load Register A with the low byte of the register value (Register L).
4F01
GOSUB to 51EEH to print the low byte as two hexadecimal digits followed by a space, completing the register value display.
4F04
LD A,B 78
Copy the register index (Register B) into Register A to test whether this is a flag register (AF or AF').
4F05
AND 0BH E6 0B
Mask the index. The two flag registers are at indices 0CH (AF) and 08H (AF'); both give 08H under this mask, distinguishing them from the other ten registers.
4F07
CP 08H FE 08
Compare the masked index against 08H. If Register A equals 08H (this is AF or AF') the Z FLAG is set.
4F09
If the NZ FLAG is set (this is not a flag register), JUMP to 4F26H to skip the flag decode and show the pointed-at memory instead.
4F0B
LD C,L 4D
Load Register C with Register L, the low byte of the AF/AF' value, which is the flag byte F to be decoded bit by bit.
4F0C
PUSH BC C5
Save Register B (the register index) and Register C (the flag byte) so B survives the flag loop.
4F0D
LD HL,4F77H 21 77 4F
Point Register Pair HL at 4F77H, the eight-character flag-name table "SZ1H1PNC".
4F10
LD B,08H 06 08
Load Register B with 8, the number of flag bits to display, from bit 7 down to bit 0.
4F12
SLA C CB 21
Loop Start
Shift the flag byte (Register C) left, moving the next flag bit (bit 7 first) into the CARRY FLAG.
4F14
LD A,(HL) 7E
Load Register A with the name character for this flag position from the table at HL.
4F15
If the CARRY FLAG is set (this flag bit is 1), JUMP to 4F19H to print the flag's name letter.
4F17
LD A,2DH 3E 2D
The flag bit is 0: load Register A with 2DH (ASCII -) to print a dash in place of the letter.
4F19
GOSUB to ROM 0033H to display the flag letter (if set) or dash (if clear).
4F1C
INC HL 23
Advance HL to the next flag-name character.
4F1D
Decrement Register B and LOOP BACK to 4F12H until all eight flag bits have been shown.
4F1F
POP BC C1
Restore the register index into Register B (and the flag byte into C) saved at 4F0CH.
4F20
LD A,0ECH 3E EC
Load Register A with ECH, a graphics block character that closes the flag field on the line.
4F22
GOSUB to ROM 0033H to display the closing graphics character.
4F25
XOR A AF
Set Register A to zero, which sets the Z FLAG so the following conditional call is skipped for the flag registers (no pointed-at memory line is shown for AF or AF').
4F26
For the ten non-flag registers (NZ FLAG set from the 4F07H compare), GOSUB to 5131H to display the sixteen memory bytes at the address this register holds (still in Register Pair HL).
4F29
POP HL E1
Restore the snapshot pointer (saved at 4EF2H) into Register Pair HL.
4F2A
EX (SP),HL E3
Swap HL back with the name-table pointer on the stack, so HL again points into the register-name table for the next line.
4F2B
Decrement Register B and LOOP BACK to 4EE5H to paint the next register line until all twelve are done.
4F2D
POP HL E1
Discard the leftover pointer from the stack (balancing the PUSH at 4EDDH). Execution falls through into the memory-window painter below.

4F2EH - Memory-Window Painter (Register Screen)

The lower part of the register screen. After the twelve register lines, four lines of the memory window at 4063H are painted. The per-line loop at 4F33H is shared with the full-memory screen, which enters it with a line count of sixteen.

4F2E
LD HL,(4063H) 2A 63 40
Load Register Pair HL with the memory-display window address from 4063H-4064H, the first address of the memory block shown beneath the registers.
4F31
LD B,04H 06 04
Load Register B with 4, the number of memory lines to paint on the register screen.
4F33
LD A,0C7H 3E C7
Loop Start
Load Register A with C7H, a graphics character drawn at the start of each memory line as a left margin marker. Shared entry for the full-memory screen (4F51H).
4F35
GOSUB to ROM 0033H to display the left-margin graphics character.
4F38
GOSUB to 51D5H to display the current line address (Register Pair HL) as four hexadecimal digits.
4F3B
GOSUB to 51F1H to display one space between the address and the data bytes.
4F3E
GOSUB to 5131H to display the sixteen memory bytes starting at HL, advancing HL by sixteen.
4F41
Decrement Register B and LOOP BACK to 4F33H until all lines (four here, or sixteen from the full-memory screen) are painted.
4F43
RET C9
Return to the caller of the screen refresh (the command loop at 4E4DH or the update loop at 4EA3H).

4F44H - Full-Screen Memory Display

Reached when the full-memory layout is selected by the s command. It paints sixteen lines of memory, each showing an address and sixteen bytes, starting at the memory window address held at 4063H.

4F44
LD HL,3BFFH 21 FF 3B
Load Register Pair HL with 3BFFH, one cell before the top of the display region, so the first line begins at the top of the screen.
4F47
LD (4020H),HL 22 20 40
Store that position into the resident video DCB (DODCB$) cursor field at 4020H so the memory dump paints from the top.
4F4A
LD HL,(4063H) 2A 63 40
Load Register Pair HL with the memory-display window address from 4063H-4064H, the first address to show.
4F4D
LD L,00H 2E 00
Clear the low byte of the window address so the full-memory dump always starts on a 256-byte page boundary.
4F4F
LD B,10H 06 10
Load Register B with 16, the number of memory lines to paint (16 lines of 16 bytes fills one page).
4F51
JUMP to the shared memory-line painter at 4F33H, which loops B times drawing each address and its bytes.

4F53H - Register and Flag Name Table (Data)

Non-executable data. The register-name strings printed by the screen refresh (twelve three-character entries) followed by the eight flag-bit names decoded for the AF and AF' registers. The name for the two unused flag-register bits (bit 5 and bit 3) is the digit 1. The source-listing disassembler misreads this table as instructions.

4F53
DEFM "AF BC DE HL " 41 46 20 42 43 20 44 45 20 48 4C 20
Register-name strings for AF, BC, DE, and HL, each three characters (name plus a trailing space). Consumed two characters at a time by the display loop at 4EEAH.
4F5F
DEFM "AF'BC'DE'HL'" 41 46 27 42 43 27 44 45 27 48 4C 27
Register-name strings for the alternate set AF', BC', DE', and HL', each name terminated by an apostrophe as its third character.
4F6B
DEFM "IX IY SP PC " 49 58 20 49 59 20 53 50 20 50 43 20
Register-name strings for IX, IY, SP, and PC, completing the twelve entries displayed on the register screen.
4F77
DEFM "SZ1H1PNC" 53 5A 31 48 31 50 4E 43
The eight flag-bit names, from bit 7 to bit 0: Sign, Zero, unused (shown as 1), Half-carry, unused (shown as 1), Parity/overflow, Add/Subtract, Carry. Used by the flag decode loop at 4F0DH.

4F7FH - Go Command

Resumes the program under test. It reads an optional new program counter and up to two breakpoint addresses, plants F7H (RST 30H) breakpoints at them, arms the resident RST 30H re-entry hook, reloads every Z80 register from the snapshot, and transfers control to the saved program counter.

4F7F
LD B,02H 06 02
Load Register B with 2, the maximum number of breakpoint addresses the Go command will accept.
4F81
LD DE,4062H 11 62 40
Point Register Pair DE at 4062H, the breakpoint-table cursor. The store helper writes each breakpoint's saved byte and address here, decrementing DE through the table.
4F84
GOSUB to 51A4H to read the first hexadecimal parameter from the keyboard into Register Pair HL: an optional new program counter to resume at.
4F87
If the Z FLAG is set (no value was typed before the delimiter), JUMP to 4F8CH leaving the saved PC unchanged.
4F89
LD (407BH),HL 22 7B 40
Store the entered value (HL) into the saved program counter at 407BH-407CH, so execution resumes at the user-specified address.
4F8C
If the CARRY FLAG is set (the previous parameter was ended by ENTER, so there are no breakpoints to enter), JUMP to 4F98H to arm the run.
4F8E
GOSUB to 51A4H to read a breakpoint address from the keyboard into Register Pair HL.
4F91
PUSH AF F5
Save the delimiter status flags returned by 51A4H so they survive the breakpoint store.
4F92
If the NZ FLAG is set (a real address, not just a delimiter, was entered), GOSUB to 4FC9H to plant an F7H breakpoint at HL and record it in the table at DE.
4F95
POP AF F1
Restore the delimiter status flags saved at 4F91H.
4F96
Decrement Register B and LOOP BACK to 4F8CH to read a possible second breakpoint (or stop on an ENTER).
4F98
LD HL,400FH 21 0F 40
Load Register Pair HL with 400FH, the resident RST 30H hook address that re-enters this DEBUG overlay when a breakpoint fires.
4F9B
LD (4316H),HL 22 16 43
Store 400FH into 4316H-4317H, the address operand of the JP instruction being assembled in the resident hooks area.
4F9E
LD A,0C3H 3E C3
Load Register A with C3H, the Z80 opcode for an absolute JP instruction.
4FA0
LD (4315H),A 32 15 43
Store C3H at 4315H, completing a "JP 400FH" instruction at 4315H-4317H so RST 30H breakpoints re-enter DEBUG.

4FA3H - Register Restore and Transfer

Reloads the CPU from the 24-byte snapshot and jumps to the saved program counter. The eleven register-pair words are pushed onto the stack in reverse and popped back into every register (both banks, IX, IY, SP), then the saved PC is placed so the final RET transfers to it.

4FA3
LD HL,407AH 21 7A 40
Point Register Pair HL at 407AH, the high byte of the SP slot, the top of the snapshot to be pushed downward.
4FA6
LD B,0BH 06 0B
Load Register B with 11, the number of register-pair words in the snapshot (SP, IY, IX, HL', DE', BC', AF', HL, DE, BC, AF).
4FA8
LD D,(HL) 56
Loop Start
Load Register D with the high byte of the current snapshot word from HL.
4FA9
DEC HL 2B
Move HL down to the low byte of the current word.
4FAA
LD E,(HL) 5E
Load Register E with the low byte, completing the snapshot word in Register Pair DE.
4FAB
DEC HL 2B
Move HL down to the next snapshot word for the following iteration.
4FAC
PUSH DE D5
Push the snapshot word (Register Pair DE) onto the stack so the register POPs below can retrieve them in the correct order.
4FAD
Decrement Register B and LOOP BACK to 4FA8H until all eleven snapshot words are on the stack, with the AF word on top.
4FAF
POP AF F1
Pop the main Register Pair AF from the reversed snapshot on the stack.
4FB0
POP BC C1
Pop the main Register Pair BC.
4FB1
POP DE D1
Pop the main Register Pair DE.
4FB2
POP HL E1
Pop the main Register Pair HL (this main HL is reloaded a final time at 4FC5H after the PC is placed).
4FB3
EX AF,AF' 08
Switch to the alternate AF so the alternate set can be reloaded.
4FB4
EXX D9
Switch to the alternate BC, DE, HL set for reloading.
4FB5
POP AF F1
Pop the alternate Register Pair AF'.
4FB6
POP BC C1
Pop the alternate Register Pair BC'.
4FB7
POP DE D1
Pop the alternate Register Pair DE'.
4FB8
POP HL E1
Pop the alternate Register Pair HL'.
4FB9
EX AF,AF' 08
Switch AF back to the main accumulator and flags.
4FBA
EXX D9
Switch back to the main BC, DE, HL set; both banks now hold the caller's values.
4FBB
POP IX DD E1
Pop the caller's Index Register IX from the snapshot.
4FBD
POP IY FD E1
Pop the caller's Index Register IY.
4FBF
POP HL E1
Pop the caller's saved stack pointer value into Register Pair HL.
4FC0
LD SP,HL F9
Set the Stack Pointer SP to the caller's saved SP, so the program resumes with its own stack.
4FC1
LD HL,(407BH) 2A 7B 40
Load Register Pair HL with the saved program counter from 407BH-407CH.
4FC4
PUSH HL E5
Push the saved PC onto the caller's stack so the closing RET jumps to it.
4FC5
LD HL,(406BH) 2A 6B 40
Reload Register Pair HL with the caller's saved main HL value from 406BH-406CH, restoring the HL that was used as a scratch pointer during the restore.
4FC8
RET C9
Return to the address pushed at 4FC4H, transferring control to the caller's saved program counter with every register restored.

4FC9H - Breakpoint Store Helper

Plants one breakpoint. It saves the original opcode byte, writes an F7H (RST 30H) over it, verifies the write took (guarding against ROM or non-existent RAM), and records the address in the breakpoint table. Register Pair HL holds the breakpoint address; Register Pair DE points at the table slot and is decremented past the three bytes written.

4FC9
LD A,(HL) 7E
Load Register A with the original opcode byte currently at the breakpoint address HL.
4FCA
LD (DE),A 12
Store that original byte into the breakpoint table at DE so it can be restored later.
4FCB
DEC DE 1B
Move the table cursor DE down to the address-high field of this slot.
4FCC
LD A,0F7H 3E F7
Load Register A with F7H, the RST 30H opcode used as the breakpoint marker.
4FCE
LD (HL),A 77
Write F7H over the instruction at the breakpoint address HL, so execution reaching it traps into DEBUG.
4FCF
CP (HL) BE
Compare Register A (F7H) against the byte now at HL to verify the write succeeded. If they differ the NZ FLAG is set, meaning the address is ROM or non-existent RAM.
4FD0
If the NZ FLAG is set (the breakpoint would not stick), JUMP to 4E2FH to abort back to breakpoint cleanup and the command loop.
4FD3
LD A,H 7C
Load Register A with the high byte of the breakpoint address (Register H).
4FD4
LD (DE),A 12
Store the address high byte into the table slot at DE.
4FD5
DEC DE 1B
Move the table cursor DE down to the address-low field.
4FD6
LD A,L 7D
Load Register A with the low byte of the breakpoint address (Register L).
4FD7
LD (DE),A 12
Store the address low byte into the table slot at DE.
4FD8
DEC DE 1B
Move the table cursor DE down to the next breakpoint slot for a possible second breakpoint.
4FD9
RET C9
Return to the caller (the Go command at 4F92H or the single-step decoder at 5096H).

4FDAH - Modify Memory Command

The m command. It sets the memory-modify pointer at 4060H (optionally to a new address), then repeatedly shows the address and current byte on the screen, prompts with a dash, and stores a new hexadecimal byte if one is typed, advancing byte by byte until ENTER.

4FDA
LD HL,(4060H) 2A 60 40
Load Register Pair HL with the current memory-modify pointer from 4060H-4061H.
4FDD
GOSUB to 51A4H to read an optional new address into Register Pair HL. If only a delimiter is typed, HL keeps the old pointer.
4FE0
LD (4060H),HL 22 60 40
Store HL as the memory-modify pointer at 4060H-4061H. This is the loop point after each byte is advanced.
4FE3
RET C D8
If the CARRY FLAG is set (the value was ended by ENTER), return to the command loop, finishing the modify session.
4FE4
PUSH HL E5
Save the current pointer; it is recovered at 5004H as the store target after the display work.
4FE5
GOSUB to 4ECFH to repaint the screen so the memory display shows the cursor block at the current modify pointer.
4FE8
LD HL,3F40H 21 40 3F
Load Register Pair HL with 3F40H, a video RAM address near the bottom of the screen for the modify prompt.
4FEB
LD (4020H),HL 22 20 40
Store 3F40H into the video DCB cursor field at 4020H so the address prints on that line.
4FEE
LD HL,(4060H) 2A 60 40
Reload Register Pair HL with the modify pointer from 4060H to display it.
4FF1
GOSUB to 51D5H to display the pointer address (HL) as four hexadecimal digits.
4FF4
PUSH HL E5
Save the pointer address across the next cursor move.
4FF5
LD HL,3F80H 21 80 3F
Load Register Pair HL with 3F80H, the next screen line, for the current byte value.
4FF8
LD (4020H),HL 22 20 40
Store 3F80H into the video DCB cursor field at 4020H so the byte value prints on that line.
4FFB
POP HL E1
Recover the pointer address into Register Pair HL.
4FFC
GOSUB to 51D1H to display the byte currently at the pointer as two hexadecimal digits.
4FFF
LD A,2DH 3E 2D
Load Register A with 2DH (ASCII -), the prompt shown before the user types a replacement byte.
5001
GOSUB to ROM 0033H to display the dash prompt.
5004
POP DE D1
Recover the modify pointer (saved at 4FE4H) into Register Pair DE, the address where a new byte will be stored.
5005
GOSUB to 51A4H to read the replacement byte value from the keyboard into Register Pair HL.
5008
EX DE,HL EB
Swap so Register Pair HL is the store address (the pointer) and Register E holds the low byte of the entered value.
5009
If the Z FLAG is set (no value was typed, only a delimiter), JUMP to 500CH to leave the byte unchanged.
500B
LD (HL),E 73
Store the low byte of the entered value (Register E) at the pointer address HL, patching that memory byte.
500C
RET C D8
If the CARRY FLAG is set (the entry was ended by ENTER), return to the command loop, finishing the modify session.
500D
INC HL 23
Advance the pointer HL to the next memory byte.
500E
LOOP BACK to 4FE0H to store the advanced pointer and display and edit the next byte.

5010H - Modify Register Command

The r command. It reads a one or two character register name (with an optional apostrophe for the alternate set), locates the matching entry in the name table, computes that register's slot in the snapshot at 4065H, and stores a new hexadecimal value there. The change takes effect when the program is resumed with Go.

5010
GOSUB to 5189H to read the first character of the register name into Register A.
5013
RET Z C8
If the Z FLAG is set (a delimiter, no name) return to the command loop.
5014
RES 5,A CB AF
Clear bit 5 of Register A to fold the lowercased key back to uppercase, matching the uppercase names in the table.
5016
LD C,A 4F
Save the first name character in Register C for the table search.
5017
GOSUB to 5189H to read the second character of the register name into Register A.
501A
RET Z C8
If the Z FLAG is set (a delimiter, incomplete name) return to the command loop.
501B
RES 5,A CB AF
Fold the second character back to uppercase.
501D
LD D,A 57
Save the second name character in Register D for the table search.
501E
LD E,20H 1E 20
Preset Register E to 20H (space), the default third name character for the main register set.
5020
GOSUB to 5189H to read the next key, which may be a value delimiter or an apostrophe selecting the alternate register set.
5023
RET C D8
If the CARRY FLAG is set (ENTER) return to the command loop without editing.
5024
If the Z FLAG is set (a space or comma delimiter), JUMP to 502CH keeping the space as the third name character (a main-set register).
5026
LD E,A 5F
The key was an apostrophe (alternate-set marker); store it as the third name character in Register E.
5027
GOSUB to 5189H to read the delimiter that must follow the apostrophe.
502A
RET NZ C0
If the NZ FLAG is set (an unexpected non-delimiter character) return to the command loop, rejecting the name.
502B
RET C D8
If the CARRY FLAG is set (ENTER) return to the command loop.
502C
LD HL,4F53H 21 53 4F
Point Register Pair HL at 4F53H, the register-name table, to search for the entered name.
502F
LD B,0CH 06 0C
Load Register B with 12, the number of names in the table; B also yields the register's slot index once a match is found.
5031
LD A,(HL) 7E
Loop Start
Load Register A with the first character of the current table name at HL.
5032
INC HL 23
Advance HL to the second character of the name.
5033
CP C B9
Compare the table's first character against the entered first character (Register C). If equal the Z FLAG is set.
5034
If the Z FLAG is set (first character matches), JUMP to 503BH to check the remaining characters.
5036
INC HL 23
First character did not match: advance HL past the second character of this name.
5037
INC HL 23
Advance HL past the third character to the start of the next name.
5038
Decrement Register B and LOOP BACK to 5031H to test the next name.
503A
RET C9
No name matched; return to the command loop.
503B
LD A,(HL) 7E
Load Register A with the second character of the matched name at HL.
503C
CP D BA
Compare it against the entered second character (Register D). If equal the Z FLAG is set.
503D
If the NZ FLAG is set (second character differs), JUMP to 5036H to advance to the next name.
503F
INC HL 23
Advance HL to the third character of the name.
5040
LD A,(HL) 7E
Load Register A with the third character (a space or apostrophe).
5041
SUB E 93
Subtract the entered third character (Register E). If they match the result is zero and the Z FLAG is set.
5042
If the NZ FLAG is set (third character differs, wrong register bank), JUMP to 5037H to advance to the next name.
5044
LD D,A 57
The name fully matched. Register A is zero (from the successful subtraction); copy it into Register D to form the high byte of the slot offset.
5045
LD A,18H 3E 18
Load Register A with 18H (24), the size in bytes of the register snapshot, to convert the name index into a byte offset.
5047
SUB B 90
Subtract the remaining name count (Register B). Since B counts down from 12, this begins converting the index to an offset.
5048
SUB B 90
Subtract Register B again, so Register A holds 24 minus twice the count, which is the byte offset of the matched register within the snapshot (0 for AF, 2 for BC, and so on).
5049
LD E,A 5F
Move the byte offset into Register E, completing the offset in Register Pair DE.
504A
LD HL,4065H 21 65 40
Point Register Pair HL at 4065H, the base of the register snapshot.
504D
ADD HL,DE 19
Add the offset DE to the base so HL points at the matched register's two-byte slot in the snapshot.
504E
PUSH HL E5
Save the slot address across the value-entry prompt.
504F
LD A,1EH 3E 1E
Load Register A with 1EH, a TRS-80 cursor-control code, to position the cursor for the value entry.
5051
GOSUB to ROM 0033H to emit the cursor-control code.
5054
POP DE D1
Recover the register slot address into Register Pair DE.
5055
GOSUB to 51A4H to read the new register value from the keyboard into Register Pair HL.
5058
RET Z C8
If the Z FLAG is set (no value typed) return to the command loop, leaving the register unchanged.
5059
EX DE,HL EB
Swap so Register Pair HL is the slot address and Register Pair DE holds the new value.
505A
LD (HL),E 73
Store the low byte of the new value (Register E) into the register slot.
505B
INC HL 23
Advance HL to the high byte of the slot.
505C
LD (HL),D 72
Store the high byte of the new value (Register D), completing the register update in the snapshot.
505D
RET C9
Return to the command loop; the edited value is loaded into the CPU on the next Go.

505EH - Single-Step Next-Address Decoder

The c (step over) and i (step into) commands. It reads the opcode at the saved program counter, looks up its length and type in the tables at 50E0H-5130H, and plants a temporary breakpoint at the instruction that will execute next: always the sequential next instruction, and for branches also the branch target. It then transfers control so the program runs to the first breakpoint. The two commands differ only in how they treat a CALL: i breaks inside the called routine, c breaks after it returns.

505E
PUSH AF F5
Save the command key (Register A holds 63H for c or 69H for i) so it can be tested later to decide step-over versus step-into.
505F
LD DE,(407BH) ED 5B 7B 40
Load Register Pair DE with the saved program counter from 407BH-407CH, the address of the instruction about to be stepped.
5063
LD A,(DE) 1A
Fetch the opcode at the program counter (DE) into Register A to classify it.
5064
LD HL,5115H 21 15 51
Point Register Pair HL at 5115H, the length/type table for DD- and FD-prefixed (index register) instructions, assumed until the prefix is ruled out.
5067
CP 0DDH FE DD
Compare the opcode (Register A) against DDH, the IX-register prefix. If equal the Z FLAG is set.
5069
If the Z FLAG is set (a DD prefix), JUMP to 5079H to classify by the following byte using the DD/FD table.
506B
CP 0FDH FE FD
Compare the opcode against FDH, the IY-register prefix. If equal the Z FLAG is set.
506D
If the Z FLAG is set (an FD prefix), JUMP to 5079H to classify by the following byte using the DD/FD table.
506F
LD HL,50E0H 21 E0 50
Not an index prefix: point Register Pair HL at 50E0H, the length/type table for unprefixed opcodes.
5072
CP 0EDH FE ED
Compare the opcode against EDH, the extended-instruction prefix. If equal the Z FLAG is set.
5074
If the NZ FLAG is set (an ordinary unprefixed opcode), JUMP to 507CH to classify the opcode itself using the 50E0H table.
5076
LD HL,510EH 21 0E 51
An ED prefix: point Register Pair HL at 510EH, the length/type table for extended (ED-prefixed) instructions.
5079
INC DE 13
Advance DE to the second opcode byte (the byte after the DD, FD, or ED prefix).
507A
LD A,(DE) 1A
Fetch that second byte into Register A; it is the value used to classify a prefixed instruction.
507B
DEC DE 1B
Restore DE to point back at the prefix, so DE again equals the program counter.
507C
LD C,A 4F
Copy the classifying byte (the opcode, or the byte after a prefix) into Register C for the table-matching loop.
507D
LD A,(HL) 7E
Loop Start
Load Register A with the mask byte of the current table entry at HL.
507E
AND C A1
Mask the classifying byte (Register C) with the entry's mask, isolating the bits this entry cares about.
507F
INC HL 23
Advance HL to the entry's match byte.
5080
CP (HL) BE
Compare the masked value against the entry's match byte. If they are equal the Z FLAG is set, meaning this opcode belongs to this entry's group.
5081
INC HL 23
Advance HL to the entry's result byte (which encodes length in its low nibble and type in its high nibble).
5082
If the Z FLAG is set (this entry matched), JUMP to 508AH to use its result byte.
5084
INC HL 23
No match: advance HL to the mask byte of the next table entry.
5085
LD A,(HL) 7E
Load Register A with that next entry's mask byte to test for the table terminator.
5086
CP 05H FE 05
Compare the mask byte against 5. A real entry's mask is always 5 or greater; the single terminating byte at the table end is less than 5 and serves as the default result.
5088
If the NO CARRY FLAG is set (mask is 5 or more, a real entry), LOOP BACK to 507DH to test it; otherwise fall through with HL on the default byte.
508A
LD A,(HL) 7E
Load Register A with the result byte (a matched entry's result, or the table's default length byte).
508B
LD B,A 47
Keep the full result byte in Register B; its high nibble is the instruction type and its low nibble is the length.
508C
AND 0FH E6 0F
Mask Register A to the low nibble, the instruction length in bytes.
508E
LD L,A 6F
Put the length in Register L.
508F
LD H,00H 26 00
Clear Register H so Register Pair HL holds just the length.
5091
ADD HL,DE 19
Add the length to the program counter (DE), so HL now holds the address of the sequential next instruction.
5092
PUSH DE D5
Save the program counter (DE) while the breakpoint is planted.
5093
LD DE,4062H 11 62 40
Point Register Pair DE at 4062H, breakpoint slot 1 in the table, to record the step breakpoint.
5096
GOSUB to 4FC9H to plant an F7H breakpoint at the sequential next instruction (HL) and record it, so execution always stops after this instruction.
5099
POP HL E1
Recover the program counter into Register Pair HL to compute any branch target.
509A
LD A,B 78
Copy the result byte (Register B) into Register A to examine the instruction-type nibble.
509B
AND 0F0H E6 F0
Mask to the high nibble, the instruction type: 00H ordinary, 10H JP (HL), 20H JP (IX)/(IY), 30H relative jump, 40H absolute jump, 50H return, 60H call.
509D
If the Z FLAG is set (type 00H, an ordinary instruction with no branch), JUMP to 50B5H to run to the single breakpoint already planted.
509F
INC HL 23
Advance HL past the opcode to its operand, needed for computing jump and call targets.
50A0
CP 20H FE 20
Compare the type nibble against 20H to separate the register-indirect jumps from the address-operand branches.
50A2
If the CARRY FLAG is set (type 10H, JP (HL)), JUMP to 50D8H to take the target from the saved HL register.
50A4
If the Z FLAG is set (type 20H, JP (IX) or JP (IY)), JUMP to 50CDH to take the target from the saved index register.
50A6
CP 40H FE 40
Compare the type nibble against 40H to separate relative jumps from absolute jumps.
50A8
If the CARRY FLAG is set (type 30H, a relative JR or DJNZ), JUMP to 50C1H to compute the program-counter-relative target.
50AA
If the Z FLAG is set (type 40H, an absolute JP), JUMP to 50BBH to read the two-byte target operand.
50AC
CP 60H FE 60
Compare the type nibble against 60H to separate returns from calls.
50AE
If the CARRY FLAG is set (type 50H, a RET), JUMP to 50B8H to take the target from the top of the program's stack.
50B0
POP AF F1
Type is 60H (a CALL). Recover the command key (saved at 505EH) into Register A to decide step-over versus step-into.
50B1
CP 69H FE 69
Compare the command key against 69H (ASCII i). If equal the Z FLAG is set, meaning step-into.
50B3
If the Z FLAG is set (i, step-into), JUMP to 50BBH to also break at the call destination; otherwise (c, step-over) fall through to run only to the after-call breakpoint.
50B5
JUMP to the register-restore-and-transfer routine at 4FA3H, resuming the program so it runs until it hits a planted breakpoint.
50B8
LD HL,(4079H) 2A 79 40
Return target: load Register Pair HL with the program's saved stack pointer from 4079H, so the top of that stack can be read as the return address.
50BB
LD A,(HL) 7E
Load Register A with the low byte of the target address at HL (either the branch operand at PC+1, or the return address on the stack).
50BC
INC HL 23
Advance HL to the high byte of the target address.
50BD
LD H,(HL) 66
Load the high byte of the target into Register H.
50BE
LD L,A 6F
Move the saved low byte into Register L, so Register Pair HL now holds the full branch or return target.
50BF
JUMP to 50DBH to plant a breakpoint at this target.
50C1
LD C,(HL) 4E
Relative jump: load Register C with the signed displacement byte at PC+1 (HL).
50C2
XOR A AF
Clear Register A to build the high byte of the sign-extended displacement.
50C3
BIT 7,C CB 79
Test the sign bit of the displacement (Register C). If it is set the displacement is negative and the Z FLAG is cleared.
50C5
If the Z FLAG is set (displacement is positive), JUMP to 50C8H leaving the high byte zero.
50C7
CPL 2F
Displacement is negative: complement Register A to FFH so Register Pair BC sign-extends the displacement.
50C8
LD B,A 47
Store the sign-extension byte in Register B, forming the signed displacement in Register Pair BC.
50C9
INC HL 23
Advance HL to PC+2, the address the relative branch is measured from.
50CA
ADD HL,BC 09
Add the signed displacement (BC) to PC+2, giving the branch target in Register Pair HL.
50CB
JUMP to 50DBH to plant a breakpoint at the relative-jump target.
50CD
LD HL,(4075H) 2A 75 40
Index-register jump: load Register Pair HL with the saved IX register from 4075H as the default target.
50D0
BIT 5,C CB 69
Test bit 5 of the classifying byte (Register C) to select between the saved IX and IY as the jump target.
50D2
If the Z FLAG is set, JUMP to 50DBH to plant the breakpoint at the saved IX target.
50D4
LD HL,(4077H) 2A 77 40
Otherwise load Register Pair HL with the saved IY register from 4077H as the target.
50D7
LD IX,(406BH) DD 2A 6B 40
Overlapping Code
Entered here (with the DD prefix) this is a benign load of the saved HL value into IX on the index-jump path. Entered one byte later at 50D8H the same bytes read as LD HL,(406BH), which is the JP (HL) target handler below.
50D8
LD HL,(406BH) 2A 6B 40
JP (HL) target: load Register Pair HL with the program's saved HL register from 406BH, the destination of a JP (HL). These three bytes overlap the DD-prefixed instruction above.
50DB
GOSUB to 4FC9H to plant a second F7H breakpoint at the computed branch, call, or return target (HL), so a taken branch also stops.
50DE
JUMP to 50B5H to resume the program, which now stops at whichever breakpoint it reaches first.

50E0H - Instruction Length and Type Tables (Data)

Non-executable data used by the single-step decoder at 505EH. Three tables of three-byte entries (mask, match, result), each ending in a one-byte default less than 5. The result byte's low nibble is the instruction length in bytes and its high nibble is the branch type. The source-listing disassembler renders this region as meaningless instructions.

50E0
DEFB C7H,C0H,51H C7 C0 51
Unprefixed table. Opcodes matching mask C7H equal to C0H (the RET conditionals, C0H/C8H/D0H and so on): length 1, type 5 (return).
50E3
DEFB FFH,C9H,51H FF C9 51
Opcode C9H exactly (RET): length 1, type 5 (return).
50E6
DEFB FFH,E9H,11H FF E9 11
Opcode E9H exactly (JP (HL)): length 1, type 1 (register-indirect jump through HL).
50E9
DEFB CFH,01H,03H CF 01 03
Opcodes matching mask CFH equal to 01H (LD rr,nn for BC/DE/HL/SP): length 3, type 0 (no branch).
50EC
DEFB E7H,22H,03H E7 22 03
Opcodes matching mask E7H equal to 22H (LD (nn),HL, LD HL,(nn), LD (nn),A, LD A,(nn)): length 3, type 0.
50EF
DEFB C7H,C2H,43H C7 C2 43
Opcodes matching mask C7H equal to C2H (the conditional absolute jumps JP cc,nn): length 3, type 4 (absolute jump).
50F2
DEFB FFH,C3H,43H FF C3 43
Opcode C3H exactly (JP nn): length 3, type 4 (absolute jump).
50F5
DEFB C7H,C4H,63H C7 C4 63
Opcodes matching mask C7H equal to C4H (the conditional calls CALL cc,nn): length 3, type 6 (call).
50F8
DEFB FFH,CDH,63H FF CD 63
Opcode CDH exactly (CALL nn): length 3, type 6 (call).
50FB
DEFB C7H,06H,02H C7 06 02
Opcodes matching mask C7H equal to 06H (LD r,n immediate loads): length 2, type 0.
50FE
DEFB F7H,D3H,02H F7 D3 02
Opcodes matching mask F7H equal to D3H (OUT (n),A and IN A,(n)): length 2, type 0.
5101
DEFB C7H,C6H,02H C7 C6 02
Opcodes matching mask C7H equal to C6H (the immediate accumulator arithmetic and logic operations): length 2, type 0.
5104
DEFB FFH,CBH,02H FF CB 02
Opcode CBH exactly (the CB bit-operation prefix): length 2, type 0.
5107
DEFB F7H,10H,32H F7 10 32
Opcodes matching mask F7H equal to 10H (DJNZ e and JR e): length 2, type 3 (relative jump).
510A
DEFB E7H,20H,32H E7 20 32
Opcodes matching mask E7H equal to 20H (the conditional relative jumps JR cc,e): length 2, type 3 (relative jump).
510D
DEFB 01H 01
Unprefixed-table default: length 1, type 0. Any opcode not matched above (a single-byte instruction) falls through to this.
510E
DEFB C7H,43H,04H C7 43 04
ED table. Second bytes matching mask C7H equal to 43H (LD (nn),rr and LD rr,(nn), such as ED 43H): length 4, type 0.
5111
DEFB F7H,45H,52H F7 45 52
Second bytes matching mask F7H equal to 45H (RETN and RETI): length 2, type 5 (return).
5114
DEFB 02H 02
ED-table default: length 2, type 0, for all other extended instructions.
5115
DEFB FEH,34H,03H FE 34 03
DD/FD table. Second bytes matching mask FEH equal to 34H (INC (IX+d) and DEC (IX+d)): length 3, type 0.
5118
DEFB C0H,40H,03H C0 40 03
Second bytes matching mask C0H equal to 40H (the indexed LD r,(IX+d) and LD (IX+d),r group): length 3, type 0.
511B
DEFB C0H,80H,03H C0 80 03
Second bytes matching mask C0H equal to 80H (the indexed accumulator arithmetic with (IX+d)): length 3, type 0.
511E
DEFB FFH,21H,04H FF 21 04
Second byte 21H exactly (LD IX,nn): length 4, type 0.
5121
DEFB FFH,22H,04H FF 22 04
Second byte 22H exactly (LD (nn),IX): length 4, type 0.
5124
DEFB FFH,2AH,04H FF 2A 04
Second byte 2AH exactly (LD IX,(nn)): length 4, type 0.
5127
DEFB FFH,36H,04H FF 36 04
Second byte 36H exactly (LD (IX+d),n): length 4, type 0.
512A
DEFB FFH,CBH,04H FF CB 04
Second byte CBH exactly (the DDCB/FDCB indexed bit-operation prefix): length 4, type 0.
512D
DEFB FFH,E9H,22H FF E9 22
Second byte E9H exactly (JP (IX) or JP (IY)): length 2, type 2 (index-register jump).
5130
DEFB 02H 02
DD/FD-table default: length 2, type 0, for all other index-prefixed instructions.

5131H - Memory Line Display

Displays one line of sixteen memory bytes starting at the address in Register Pair HL, advancing HL by sixteen. Bytes are shown in hexadecimal, or as characters when the display mode at 405DH is ASCII. Called for each memory line and, on the register screen, for the memory each register points at.

5131
PUSH BC C5
Save Register B (a loop counter belonging to the caller) so the sixteen-byte counter used here does not disturb it.
5132
LD A,3DH 3E 3D
Load Register A with 3DH (ASCII =), the first character of the "=>" lead-in that precedes the bytes.
5134
GOSUB to ROM 0033H to display the equals sign.
5137
INC A 3C
Increment Register A from 3DH to 3EH (ASCII >) to complete the "=>" lead-in.
5138
GOSUB to ROM 0033H to display the greater-than sign.
513B
GOSUB to 51F1H to display one space after the lead-in.
513E
LD B,10H 06 10
Load Register B with 16, the number of bytes to display on this line.
5140
Loop Start
GOSUB to 5164H to draw the modify-cursor marker for this position (if any) and the mid-line separator at the eighth byte.
5143
LD A,(405DH) 3A 5D 40
Load Register A with the memory-display mode flag at 405DH to choose hexadecimal or ASCII output.
5146
CP 61H FE 61
Compare the mode against 61H (ASCII a). If equal the Z FLAG is set, selecting ASCII display.
5148
If the NZ FLAG is set (not ASCII mode, so hexadecimal), JUMP to 515DH to print this byte as two hex digits.
514A
ASCII mode: GOSUB to 51F1H to print a leading space so the characters are spaced across the line.
514D
LD A,(HL) 7E
Fetch the current memory byte at HL into Register A.
514E
CP 20H FE 20
Compare the byte against 20H (space). If the byte is below 20H the CARRY FLAG is set, marking it a non-printable control code.
5150
If the CARRY FLAG is set (control code), JUMP to 5156H to substitute a dot.
5152
CP 0C0H FE C0
Compare the byte against C0H. If below C0H the CARRY FLAG is set, marking it a printable character.
5154
If the CARRY FLAG is set (printable, in the range 20H to BFH), JUMP to 5158H to display the character as-is.
5156
LD A,2EH 3E 2E
Non-printable byte: load Register A with 2EH (ASCII .) to display a dot in its place.
5158
GOSUB to ROM 0033H to display the ASCII character (or the substituted dot).
515B
INC HL 23
Advance HL to the next memory byte.
515C
XOR A AF
Set Register A to zero, setting the Z FLAG so the hexadecimal call below is skipped in ASCII mode.
515D
In hexadecimal mode (NZ FLAG set from the 5146H compare), GOSUB to 51D1H to display the byte at HL as two hex digits and advance HL.
5160
Decrement Register B and LOOP BACK to 5140H until all sixteen bytes of the line are shown.
5162
POP BC C1
Restore the caller's Register B saved at 5131H.
5163
RET C9
Return to the caller (the register-screen loop at 4F26H or the memory-line painter at 4F3EH).

5164H - Memory Cursor Marker and Separator

Called once per byte before it is displayed. It draws a graphics block marker where the memory-modify pointer at 4060H falls within the line, and inserts a space between the two groups of eight bytes.

5164
LD DE,(4060H) ED 5B 60 40
Load Register Pair DE with the memory-modify pointer from 4060H-4061H, the address the cursor marks.
5168
INC DE 13
Advance DE to pointer-plus-one, the address that indicates the marker should be drawn just before the pointed-at byte.
5169
PUSH HL E5
Save the current display address (Register Pair HL) before it is used in a subtraction.
516A
XOR A AF
Clear Register A and the CARRY FLAG in preparation for the subtraction.
516B
SBC HL,DE ED 52
Subtract pointer-plus-one (DE) from the display address (HL). A zero result (Z FLAG set) means the display is one byte past the modify pointer.
516D
LD A,95H 3E 95
Load Register A with 95H, a graphics block character used as the after-pointer cursor marker.
516F
If the Z FLAG is set (display address is pointer-plus-one), JUMP to 517EH to draw the 95H marker.
5171
Otherwise GOSUB to 5182H to emit the mid-line separator space if this is the eighth byte.
5174
INC HL 23
Increment the difference in HL, so a zero now means the display address equals the modify pointer exactly.
5175
LD A,L 7D
Copy the low byte of the difference into Register A to test it against zero.
5176
OR H B4
OR in the high byte; if the difference is zero (display address equals the pointer) the Z FLAG is set.
5177
POP HL E1
Restore the display address into Register Pair HL.
5178
LD A,0AAH 3E AA
Load Register A with AAH, a graphics block character used as the at-pointer cursor marker.
517A
If the Z FLAG is set (display address equals the pointer), JUMP to 51EBH to display the AAH marker and return.
517C
Otherwise JUMP to 5186H to emit the mid-line separator space and return.
517E
Display the 95H after-pointer marker (Register A) via ROM 0033H.
5181
POP HL E1
Restore the display address into Register Pair HL, balancing the earlier save.
5182
LD A,B 78
Load Register A with the remaining byte count (Register B) to test for the line midpoint.
5183
CP 08H FE 08
Compare the count against 8. If eight bytes remain the Z FLAG is set, marking the middle of the sixteen-byte line.
5185
RET NZ C0
If the NZ FLAG is set (not the midpoint) return without emitting a separator.
5186
At the midpoint, JUMP to 51F1H to display one space that splits the line into two groups of eight, returning through 51F1H.
5188
NOP 00
A single padding byte; not reached in normal flow.

5189H - Keyboard Command Reader

Waits for a keystroke, discards control characters, folds letters to lowercase, echoes the accepted key, and reports the delimiter type. ENTER returns with the CARRY FLAG set; a comma or space returns with the Z FLAG set; any other key returns with neither, its code in Register A.

5189
PUSH DE D5
Save Register Pair DE, which the ROM keyboard routine disturbs.
518A
Loop Start
GOSUB to the Model I ROM keyboard routine at 0049H, which waits for a key and returns its ASCII code in Register A.
518D
CP 0DH FE 0D
Compare the key against 0DH (ENTER). If equal the Z FLAG is set.
518F
If the Z FLAG is set (ENTER), JUMP to 51A1H to return with the CARRY FLAG set.
5191
CP 20H FE 20
Compare the key against 20H (space). If the key is below 20H the CARRY FLAG is set, marking an unwanted control character.
5193
If the CARRY FLAG is set (a control character other than ENTER), LOOP BACK to 518AH to wait for another key.
5195
SET 5,A CB EF
Set bit 5 of Register A, folding an uppercase letter to lowercase so command comparisons can use lowercase codes.
5197
GOSUB to ROM 0033H to echo the accepted key to the screen.
519A
POP DE D1
Restore Register Pair DE saved on entry.
519B
CP 2CH FE 2C
Compare the key against 2CH (ASCII comma). If equal the Z FLAG is set, marking a parameter delimiter.
519D
RET Z C8
If the Z FLAG is set (comma) return, reporting a delimiter to the caller.
519E
CP 20H FE 20
Compare the key against 20H (space). If equal the Z FLAG is set, also a delimiter.
51A0
RET C9
Return with the flags from the space comparison: Z FLAG set for a space delimiter, clear for an ordinary character, whose code remains in Register A.
51A1
POP DE D1
ENTER path: restore Register Pair DE saved on entry.
51A2
SCF 37
Set the CARRY FLAG to signal that ENTER ended the input.
51A3
RET C9
Return with the CARRY FLAG set, telling the caller no more parameters follow.

51A4H - Hexadecimal Parameter Parser

Reads a hexadecimal number from the keyboard into Register Pair HL. It returns immediately with the Z FLAG set if only a delimiter was typed; otherwise it accumulates digits until a delimiter, then encodes that delimiter in the CARRY FLAG (set for ENTER) so callers know whether further parameters follow. An invalid hex digit aborts to the command loop.

51A4
GOSUB to 5189H to read the first character of the parameter.
51A7
RET Z C8
If the Z FLAG is set (only a delimiter, no value) return with the Z FLAG set so the caller keeps the previous value.
51A8
LD HL,0000H 21 00 00
Clear Register Pair HL to accumulate the hexadecimal value.
51AB
Loop Start
GOSUB to 51C0H to convert the character in Register A to a binary nibble, setting the CARRY FLAG if it is not a valid hex digit.
51AE
If the CARRY FLAG is set (an invalid hex digit), JUMP to the command loop at 4E49H, abandoning the parameter.
51B1
ADD HL,HL 29
Shift the accumulator HL left one bit (first of four) to make room for the new nibble.
51B2
ADD HL,HL 29
Shift HL left again.
51B3
ADD HL,HL 29
Shift HL left again.
51B4
ADD HL,HL 29
Shift HL left a fourth time, so the low nibble of HL is now clear for the new digit.
51B5
OR L B5
OR the new nibble (Register A) with the low byte of HL (Register L).
51B6
LD L,A 6F
Store the combined low byte back into Register L, appending the new digit to the value.
51B7
GOSUB to 5189H to read the next character.
51BA
If the NZ FLAG is set (another digit, not a delimiter), LOOP BACK to 51ABH to add it to the value.
51BC
RRA 1F
A delimiter ended the value; rotate the delimiter code and its carry status right to begin converting it into a return flag.
51BD
ADC A,81H CE 81
Add 81H with carry so that the CARRY FLAG on return is set when the value was ended by ENTER and clear when ended by a space or comma, telling callers whether more parameters follow.
51BF
RET C9
Return to the caller with the parsed value in Register Pair HL and the delimiter status in the flags.

51C0H - ASCII to Hexadecimal Digit

Converts one ASCII character in Register A to a binary value 0 through 15, returning with the CARRY FLAG set if the character is not a valid hexadecimal digit.

51C0
SUB 30H D6 30
Subtract 30H (ASCII 0) from the character. Digits 0 through 9 now hold 0 through 9; characters below 0 underflow and set the CARRY FLAG.
51C2
RET C D8
If the CARRY FLAG is set (character below 0, invalid) return reporting the error.
51C3
ADD A,0C9H C6 C9
Add C9H to bias the value so digits above 9 can be range-checked by the following carry tests.
51C5
RET C D8
If the CARRY FLAG is set (the character was too large to be a valid digit or letter) return reporting the error.
51C6
ADD A,06H C6 06
Add 06H to test whether the character falls in the gap between the digits and the letters A through F.
51C8
If the CARRY FLAG is set (the character is a letter A through F), JUMP to 51CDH to finish the conversion.
51CA
ADD A,27H C6 27
The character is a decimal digit: add 27H to undo the bias and leave the value 0 through 9.
51CC
RET C D8
If the CARRY FLAG is set here (a character in the invalid gap) return reporting the error.
51CD
ADD A,0AH C6 0A
Add 0AH to complete the conversion, so letters A through F yield 10 through 15 and digits yield 0 through 9.
51CF
OR A B7
Clear the CARRY FLAG to signal a valid digit in Register A.
51D0
RET C9
Return with the binary nibble in Register A and the CARRY FLAG clear.

51D1H - Hexadecimal Output Routines

A family of routines that print binary values as ASCII hexadecimal digits through ROM 0033H. 51D1H prints the byte at HL and advances; 51D5H prints Register Pair HL as four digits; 51DAH prints Register A as two digits; 51EEH and 51F1H add trailing spaces. The nibble-to-ASCII conversion uses the classic add-and-decimal-adjust technique.

51D1
LD A,(HL) 7E
Load Register A with the byte at HL to be printed in hexadecimal.
51D2
INC HL 23
Advance HL to the next byte, so repeated calls walk through memory.
51D3
JUMP to 51DAH to print Register A as two hexadecimal digits.
51D5
LD A,H 7C
Load Register A with the high byte of Register Pair HL to print the address high byte first.
51D6
GOSUB to 51DAH to print the high byte as two hexadecimal digits.
51D9
LD A,L 7D
Load Register A with the low byte of Register Pair HL, then fall into the two-digit printer to complete the four-digit address.
51DA
PUSH AF F5
Save the byte so both nibbles can be printed; this copy supplies the low nibble later.
51DB
RRA 1F
Rotate Register A right to move the high nibble toward the low nibble (first of four rotates).
51DC
RRA 1F
Rotate right again.
51DD
RRA 1F
Rotate right again.
51DE
RRA 1F
Rotate right a fourth time, so the high nibble is now in the low four bits of Register A.
51DF
GOSUB to 51E3H to convert and display the high nibble as a hex digit.
51E2
POP AF F1
Restore the original byte to print its low nibble.
51E3
AND 0FH E6 0F
Mask Register A to the low nibble, the digit value 0 through 15 to convert.
51E5
ADD A,90H C6 90
Add 90H as the first step of the add-and-decimal-adjust hex conversion.
51E7
DAA 27
Decimal-adjust Register A, producing a value whose low digit is the correct hex character offset and setting the carry for digits A through F.
51E8
ADC A,40H CE 40
Add 40H with carry, mapping the value into the ASCII ranges for 0 through 9 and A through F.
51EA
DAA 27
Decimal-adjust again to finish forming the ASCII hexadecimal character in Register A.
51EB
JUMP to ROM 0033H to display the character, returning through the ROM routine to the original caller.

51EEH - Hex-Plus-Space and Space Output

Convenience printers: 51EEH prints Register A as two hex digits followed by a space; 51F1H prints a single space. Both finish through the character output at 51EBH.

51EE
GOSUB to 51DAH to print Register A as two hexadecimal digits.
51F1
LD A,20H 3E 20
Load Register A with 20H (space); this is also the entry point for printing a lone space.
51F3
JUMP to 51EBH to display the space through ROM 0033H and return.

51F5H - Two-Character Display

Displays two consecutive characters from the string at Register Pair HL, advancing HL. Used to print each two-character register name on the register screen.

51F5
GOSUB to 51FBH to display the first character at HL and advance HL.
51F8
GOSUB to 51FBH again to display the second character at HL and advance HL.
51FB
LD A,(HL) 7E
Load Register A with the character at HL.
51FC
INC HL 23
Advance HL to the next character.
51FD
JUMP to 51EBH to display the character through ROM 0033H and return.