TRS-80 Utility - TBUG Z-80 Monitor and Debugging Aid (26-2001) for the Model I LEVEL II - Disassembled

Page Customization

Page Index

Introduction/Summary:

TBUG Z-80 Monitor and Debugging Aid Disassembly - LEVEL II (Model I)

TBUG (Radio Shack catalog number 26-2001) is a compact machine-language monitor for the TRS-80. It gives the user direct access to the Z80 CPU so that machine-language programs can be entered, examined, modified, debugged with breakpoints and register displays, and saved to or loaded from cassette tape. This disassembly documents the LEVEL II version, which loads into memory from 4380H through 497FH. User programs therefore live from 4980H upward, and the monitor presets the user Stack Pointer to 4980H.

The monitor presents a live keyboard driven by a single-character command dispatcher. When the # prompt is shown, the dispatcher at 43E2H reads one key and branches to the handler for that command. The supported commands are Memory (M), Jump (J), Breakpoint (B), Fix (F), Go (G), Register (R), Punch (P), and Load (L). TBUG uses only the first 16 columns of the 64 by 16 video screen; all output cursor management is done through a private cursor pointer kept at 483DH rather than through the ROM display driver.

A central feature is the Register Save Area at 4825H through 483CH. When a user program hits a breakpoint the entire register file (including the alternate set, IX, IY, SP and PC) is pushed into this area; the Register command displays it, and the Jump and Go commands reload the CPU from it before transferring control. This lets the user inspect and edit register contents between runs by simply editing memory in the save area.

Program Origin and Entry

The assembled origin is 4380H and the tape entry point (the END address) is 43A0H. Entry at 43A0H sets the stack to 4980H and falls into the cold-start display setup at 43D3H. The breakpoint trap vector lives at 44B7H: a breakpoint is planted in user code as a three-byte CALL 44B7H sequence (opcode CDH, then 80H 43H little-endian for 4380H+... resolved to the trap), so that when the user program reaches the planted instruction it calls into TBUG and the full register state is captured.

Variables and Work Area (Register Save Area and Scratch)

Address RangePurpose
4825H-482CH
(8 bytes)
Alternate register set save area: F' (4825H), A' (4826H), C' (4827H), B' (4828H), E' (4829H), D' (482AH), L' (482BH), H' (482CH). Populated by the breakpoint trap and reloaded before Go/Jump.
482DH-4838H
(12 bytes)
Main register set save area: F (482DH), A (482EH), C (482FH), B (4830H), E (4831H), D (4832H), L (4833H), H (4834H), IX lsb (4835H), IX msb (4836H), IY lsb (4837H), IY msb (4838H).
4839H-483AH
(2 bytes)
Saved user Stack Pointer (SP lsb at 4839H, SP msb at 483AH). Also used as a temporary SP holding cell during command entry.
483BH-483CH
(2 bytes)
Saved user Program Counter (PC lsb at 483BH, PC msb at 483CH). The Go command resumes execution here; the Fix command restores saved breakpoint bytes to this address.
483DH-483EH
(2 bytes)
Current display cursor pointer into video RAM (3C00H-3FFFH). Advanced by the character output routine at 457BH and reset to 3C00H at cold start.
483FH-4840H
(2 bytes)
Memory command working address (483FH) and the value byte just entered (4840H). IX is loaded from 483FH to point at the location being examined or modified.
4841H-4846H
(6 bytes)
Punch command parameters: start address (4841H lsb, 4842H msb), end address (4843H lsb, 4844H msb), and entry point (4845H lsb, 4846H msb).
4847H-484CH
(6 bytes)
Punch file name buffer, six characters padded with blanks (20H).
484DH
(1 byte)
Keyboard debounce holding cell. Stores the last accepted key code so the scanner at 45C8H requires the key to be released and re-pressed before repeating.
484EH
(1 byte)
Cassette port (FFH) output image. The latch is write-only, so TBUG keeps a shadow copy here and masks it (AND H / OR L) before every OUT (0FFH),A.
484FH-4851H
(3 bytes)
Breakpoint save cells. Holds the three original user bytes that the Breakpoint command overwrote with the CALL trap, so the Fix command can restore them.

Major Routines

AddressRoutineDescription and Entry/Exit
43A0HCold Start / Warm Restart
Entry point from tape (END 43A0H). Sets SP to 4980H and joins the screen-clear and command loop. The shorter entry at 43ABH is used after saving the caller SP.
43E2HCommand Dispatcher
Prints the # prompt, reads one key via 45C8H, and compares against the eight command letters, jumping to the matching handler. Unknown keys loop back.
44B7HBreakpoint Trap / Register Save
Reached by the planted CALL in user code. Saves every register into the save area (4825H-483CH), then returns to the command loop. On exit the user CPU state is preserved for inspection.
450FHDisplay Byte As Hex
Entry: IX points at the byte. Prints the two hex digits of (IX+00H) to the screen using the cursor pointer at 483DH.
4522HPrint One Hex Nibble
Entry: A holds a value 00H-0FH. Converts to an ASCII hex digit (0-9, A-F) and writes it to video at the cursor, then advances the cursor.
453CHNewline / Scroll
Advances the cursor to the start of the next 16-column line, scrolling the 16-column window up by one line when the bottom is reached and blanking the freed line.
457BHAdvance Cursor
Increments the cursor pointer at 483DH and wraps it within the video region (masking to keep the high byte in the 3CH-3FH range).
4589HInput Two Hex Digits To Byte
Reads two hex characters from the keyboard, echoes each, and returns the combined 8-bit value in A.
45A4HGet Hex Digit
Reads one key, validates it as a hex digit (0-9 or A-F), converts to 0-0FH, and returns it in A. The X key aborts to the command loop.
45C8HDebounced Key Scan
Waits for a fresh keypress using the debounce cell at 484DH and a settle delay, and returns the ASCII code in A.
45E9HRaw Keyboard Decode
Scans the memory-mapped keyboard matrix at 3801H-3880H and returns the ASCII code of the pressed key, or FFH if none.
4643HLoad Command
Reads a SYSTEM-format tape from the cassette port, honoring the A5H sync, 55H name header, 3CH data blocks, and 78H entry-point trailer, storing bytes to memory and checking the block checksum.
46A5HPunch Command
Prompts for the six-character file name, then writes a SYSTEM-format tape (leader, A5H sync, 55H name header, 3CH data blocks with checksum, and 78H entry trailer) for the memory range in the Punch parameter cells.
4762HRead One Cassette Bit
Uses the alternate register set. Waits for the sync pulse on port FFH, delays, samples the data pulse, and shifts the bit into A. Called eight times per byte by 4782H.
478CHWrite One Cassette Byte
Uses the alternate register set. Serializes the byte in A most-significant-bit first, driving the cassette port through the pulse routine at 47A6H.
47D2HCassette Port Output
Masks the shadow image at 484EH with H (AND) and L (OR), writes the result to port FFH, and refreshes the shadow. Entry points at 47C5H/47CAH/47CFH preload HL for specific bit patterns.

Cross-Reference Notes

TBUG is self-contained and calls no LEVEL II ROM routines during normal monitor operation: it drives the keyboard matrix, the video RAM, and the cassette port directly. The one external interface is the cassette port at FFH (see Model I hardware notes). The breakpoint mechanism links back into the monitor from arbitrary user code via the planted CALL 44B7H sequence. The tape format produced by the Punch command at 46A5H is the standard Model I SYSTEM tape format, so tapes written by TBUG can be reloaded by the LEVEL II BASIC SYSTEM command as well as by TBUG's own Load command at 4643H.

Disassembly:

4380H - Breakpoint Return Stub and Command Restart

This short block is the front door of the monitor. It contains the code that a planted breakpoint returns through, the Go/Jump re-entry helpers, and the common restart path that clears the screen and drops into the command loop. The tape entry point (43A0H) also lives here.

4380
LD (4839H),BC ED 43 39 48
Store Register Pair BC (the caller's BC at the moment the planted CALL fired) to the SP-save cell at 4839H so it can be recovered. This is the byte the breakpoint trap CALL lands on.
4384
POP BC C1
Restore Register Pair BC from the stack. The CALL that entered here pushed a return address; that value is pulled into BC so it can be used as the user Program Counter.
4385
DEC BC 0B
DECrement Register Pair BC by 1. The pushed return address points just past the planted CALL; three decrements walk it back to the first byte of the breakpoint so the saved PC is the breakpoint address itself.
4386
DEC BC 0B
DECrement Register Pair BC by 1 (second of three) to back up over the breakpoint CALL bytes.
4387
DEC BC 0B
DECrement Register Pair BC by 1 (third of three). BC now holds the address of the planted breakpoint, which is the true user PC.
4388
LD (483BH),BC ED 43 3B 48
Store Register Pair BC (the reconstructed user PC) to the PC-save cell at 483BH-483CH in the Register Save Area.
438C
LD BC,(4839H) ED 4B 39 48
Fetch the original user BC (saved at 4380H into 4839H) back into Register Pair BC so the user's real BC value is preserved for the save area.
4390
JUMP to 43ABH to save the remaining registers into the Register Save Area now that PC and BC are recorded.
4393
Go Command Handler
GOSUB to 4532H to emit a newline so command output starts on a fresh line. Reached when the user presses G.
4396
LD SP,(4839H) ED 7B 39 48
Load the Stack Pointer from the saved user SP at 4839H-483AH, restoring the user program's stack before resuming it.
439A
LD HL,(483BH) 2A 3B 48
Load Register Pair HL with the saved user PC from 483BH-483CH so it can be used as the resume address.
439D
JUMP to 44B7H, the shared register-reload path, which restores all registers from the save area and then transfers control to the user PC held in HL.
43A0
LD SP,4980H 31 80 49
Cold Start Entry Point
Load the Stack Pointer with 4980H, the top of TBUG's private stack and the default user stack. This is the tape entry point (END 43A0H).
43A3
Relative JUMP to 43ABH to run the register-save housekeeping and then reach the command loop.
43A5
LD SP,4980H 31 80 49
Command Loop Restart
Load the Stack Pointer with 4980H. Command handlers jump here when finished to reset the stack and fetch the next command.
43A8
JUMP to 43D3H to print the prompt and read the next command, skipping the one-time screen clear done only at cold start.
43AB
DI F3
Register Save Setup
Disable maskable interrupts so the register capture and stack switch below cannot be disturbed.
43AC
LD (4839H),SP ED 73 39 48
Store the current Stack Pointer to the saved-SP cell at 4839H-483AH, recording where the user (or caller) stack was.
43B0
LD SP,4839H 31 39 48
Load the Stack Pointer with 4839H so that the following PUSH instructions write the registers downward into the Register Save Area (which ends just below this address).
43B3
PUSH IY FD E5
Save Register Pair IY onto the (redirected) stack, placing IY msb/lsb into 4838H-4837H of the save area.
43B5
PUSH IX DD E5
Save Register Pair IX into 4836H-4835H of the save area.
43B7
PUSH HL E5
Save Register Pair HL into 4834H-4833H (H then L) of the save area.
43B8
PUSH DE D5
Save Register Pair DE into 4832H-4831H of the save area.
43B9
PUSH BC C5
Save Register Pair BC into 4830H-482FH of the save area.
43BA
PUSH AF F5
Save Register Pair AF into 482EH-482DH (A then F) of the save area.
43BB
EX AF,AF' 08
Exchange AF with the alternate AF' so the alternate accumulator and flags can be captured next.
43BC
EXX D9
Switch to the alternate register set (BC', DE', HL') so those can be saved. The alternate set is now active.
43BD
PUSH HL E5
Save Register Pair HL' (alternate) into 482CH-482BH of the save area.
43BE
PUSH DE D5
Save Register Pair DE' (alternate) into 482AH-4829H of the save area.
43BF
PUSH BC C5
Save Register Pair BC' (alternate) into 4828H-4827H of the save area.
43C0
PUSH AF F5
Save Register Pair AF' (alternate A and flags) into 4826H-4825H, completing the full register capture.
43C1
LD SP,4980H 31 80 49
Restore the Stack Pointer to TBUG's own stack top at 4980H now that the save area is fully populated.
43C4
LD B,10H 06 10
Load Register B with 10H (16) as the loop count for clearing the 16-line display window.
43C6
PUSH BC C5
Loop Start
Save Register Pair BC (the line counter) so the newline routine, which uses BC, does not disturb it.
43C7
GOSUB to 453CH to advance to and blank the next display line, clearing one row of the 16-column window per pass.
43CA
POP BC C1
Restore Register Pair BC (the line counter) after the newline routine returns.
43CB
DECrement Register B and LOOP BACK to 43C6H if not zero, clearing all 16 lines. Loop End
43CD
LD HL,3C00H 21 00 3C
Load Register Pair HL with 3C00H, the top-left corner of video RAM, to reset the display cursor to home.
43D0
LD (483DH),HL 22 3D 48
Store 3C00H to the cursor pointer at 483DH-483EH, homing the output cursor.
43D3
Prompt And Read Command
GOSUB to 453CH to move to a fresh line before printing the prompt.
43D6
LD A,00H 3E 00
Load Register A with 00H, the initial cassette port image, to place the cassette hardware in a known idle state.
43D8
LD (484EH),A 32 4E 48
Store 00H to the cassette port shadow image at 484EH so later masked writes start from a clean value.
43DB
OUT (0FFH),A D3 FF
Send 00H to cassette port FFH, turning off the cassette motor relay and clearing the data line.
43DD
LD A,23H 3E 23
Load Register A with 23H, the ASCII code for the # prompt character.
43DF
GOSUB to 4532H to display the # prompt at the cursor and advance.
43E2
Command Dispatcher
GOSUB to 45C8H to wait for a debounced keypress; the command letter is returned in Register A.
43E5
CP 46H FE 46
Compare Register A against 46H (ASCII F). If Register A equals 46H, the Z FLAG is set; otherwise the NZ FLAG is set.
43E7
If the Z FLAG is set (key was F), JUMP to 480DH to run the Fix command.
43EA
CP 42H FE 42
Compare Register A against 42H (ASCII B). If equal, the Z FLAG is set.
43EC
If the Z FLAG is set (key was B), JUMP to 47DFH to run the Breakpoint command.
43EF
CP 4DH FE 4D
Compare Register A against 4DH (ASCII M). If equal, the Z FLAG is set.
43F1
If the Z FLAG is set (key was M), relative JUMP to 440DH to run the Memory command.
43F3
CP 4AH FE 4A
Compare Register A against 4AH (ASCII J). If equal, the Z FLAG is set.
43F5
If the Z FLAG is set (key was J), JUMP to 44A5H to run the Jump command.
43F8
CP 52H FE 52
Compare Register A against 52H (ASCII R). If equal, the Z FLAG is set.
43FA
If the Z FLAG is set (key was R), relative JUMP to 4455H to run the Register display command.
43FC
CP 47H FE 47
Compare Register A against 47H (ASCII G). If equal, the Z FLAG is set.
43FE
If the Z FLAG is set (key was G), JUMP to 4393H to run the Go command.
4401
CP 50H FE 50
Compare Register A against 50H (ASCII P). If equal, the Z FLAG is set.
4403
If the Z FLAG is set (key was P), JUMP to 44D3H to run the Punch command.
4406
CP 4CH FE 4C
Compare Register A against 4CH (ASCII L). If equal, the Z FLAG is set.
4408
If the Z FLAG is set (key was L), JUMP to 4506H to run the Load command.
440B
The key matched no command; LOOP BACK to 43E2H to read another key.

440DH - Memory Command

The Memory command examines and edits RAM or ROM one byte at a time. It reads a four-digit address, displays that byte, and then repeatedly shows a location and accepts either two hex digits (to change it and advance) or ENTER (to advance unchanged). Pressing X aborts back to the command loop.

440D
GOSUB to 4532H to display the M just typed and advance the cursor.
4410
GOSUB to 4589H to read the two high hex digits (msb) of the address from the keyboard, returned in Register A.
4413
LD (4840H),A 32 40 48
Store the address msb (Register A) temporarily to 4840H, the working value cell.
4416
GOSUB to 4589H to read the two low hex digits (lsb) of the address, returned in Register A.
4419
LD (483FH),A 32 3F 48
Store the address lsb (Register A) to 483FH. Together with the msb it forms the Memory working address at 483FH-4840H.
441C
Loop Start
GOSUB to 457BH to advance the display cursor, spacing the output.
441F
LD IX,(483FH) DD 2A 3F 48
Load Register Pair IX with the current working address from 483FH-4840H so IX points at the memory byte to display.
4423
GOSUB to 450FH to display the byte at (IX) as two hex digits on the screen.
4426
GOSUB to 457BH to advance the cursor, leaving a space after the displayed byte.
4429
GOSUB to 45C8H to read the user's next keystroke into Register A (a hex digit, ENTER, or the X abort handled inside the scan).
442C
CP 0DH FE 0D
Compare Register A against 0DH (ENTER). If Register A equals 0DH, the Z FLAG is set, meaning the user wants to leave this byte unchanged.
442E
If the Z FLAG is set (ENTER pressed), relative JUMP to 443EH to advance to the next address without modifying this one.
4430
LD HL,443BH 21 3B 44
Load Register Pair HL with 443BH, the address of the store-and-continue code, to be used as a manufactured return address.
4433
PUSH HL E5
PUSH 443BH onto the stack so that the routine entered below will RET here after processing the first hex digit.
4434
LD HL,458CH 21 8C 45
Load Register Pair HL with 458CH, an internal entry point of the two-digit input routine that treats the key already in Register A as the first digit.
4437
PUSH HL E5
PUSH 458CH onto the stack as the entry target.
4438
JUMP to 45A7H to validate and convert the already-typed key as the first hex nibble; the two stacked addresses cause it to fold in a second digit and then return to 443BH with the assembled byte in Register A.
443B
LD (IX+00H),A DD 77 00
Store the newly entered byte (Register A) into the memory location addressed by IX, modifying the examined location.
443E
INC IX DD 23
INCrement Register Pair IX by 1 to point at the next memory location.
4440
LD (483FH),IX DD 22 3F 48
Store the advanced address (IX) back to the working address cell at 483FH-4840H so the loop tracks the current location.
4444
GOSUB to 453CH to move to the next display line for the new address.
4447
LD IX,4840H DD 21 40 48
Load Register Pair IX with 4840H, the address msb cell, to display the new address high byte.
444B
GOSUB to 450FH to display the address msb (byte at 4840H) as two hex digits.
444E
DEC IX DD 2B
DECrement Register Pair IX by 1 so it points at 483FH, the address lsb cell.
4450
GOSUB to 450FH to display the address lsb (byte at 483FH), completing the four-digit address readout.
4453
LOOP BACK to 441CH to display the byte at the new address and await the next keystroke. Loop End

4455H - Register Command

The Register command displays the contents of the Register Save Area in six labeled pairs of rows. It walks the save area in three-byte groups, printing pairs of bytes with spacing so the user can read the saved register file, then returns to the command loop.

4455
LD IX,4825H DD 21 25 48
Load Register Pair IX with 4825H, the base of the Register Save Area (the alternate flags F'), so the display walks forward through all saved registers.
4459
LD B,03H 06 03
Load Register B with 03H as the outer loop count, producing three printed lines of register pairs.
445B
PUSH BC C5
Loop Start
Save Register Pair BC (the outer line counter) across the calls that follow.
445C
GOSUB to 4483H to print one pair of register bytes (four hex digits) from the save area with internal spacing.
445F
PUSH IX DD E5
Save Register Pair IX (the save-area walk pointer) before the newline routine, which does not preserve it.
4461
GOSUB to 453CH to advance to the next display line.
4464
POP IX DD E1
Restore Register Pair IX (the walk pointer) after the newline.
4466
LD A,20H 3E 20
Load Register A with 20H (ASCII space) to separate the two register pairs printed on each line.
4468
GOSUB to 4532H to print the space at the cursor.
446B
GOSUB to 4483H to print the second register pair (four hex digits) on this line.
446E
PUSH IX DD E5
Save Register Pair IX before the two newline calls that follow.
4470
GOSUB to 453CH to advance one display line.
4473
GOSUB to 453CH again, leaving a blank line between register-pair rows for readability.
4476
POP IX DD E1
Restore Register Pair IX (the walk pointer).
4478
LD A,20H 3E 20
Load Register A with 20H (space) for the leading indent of the next line.
447A
GOSUB to 4532H to print the leading space.
447D
POP BC C1
Restore Register Pair BC (the outer line counter).
447E
DECrement Register B and LOOP BACK to 445BH if not zero, producing the remaining register-pair lines. Loop End
4480
JUMP to 43A5H to return to the command loop.

4483H - Display Register Pair Subroutine

Helper for the Register command. Given IX pointing into the save area, this prints one register pair as four hex digits in msb,lsb order with a trailing space, then advances IX past the two bytes so successive calls walk the save area.

4483
INC IX DD 23
INCrement Register Pair IX by 1 so it points at the high byte of the pair (the save area stores lsb first, msb second).
4485
GOSUB to 450FH to display the high byte (msb) at (IX) as two hex digits.
4488
DEC IX DD 2B
DECrement Register Pair IX by 1 back to the low byte of the pair.
448A
GOSUB to 450FH to display the low byte (lsb) at (IX) as two hex digits.
448D
INC IX DD 23
INCrement Register Pair IX by 1 to move past the low byte.
448F
INC IX DD 23
INCrement Register Pair IX by 1 again so IX now points at the next register pair for the following call.
4491
LD A,20H 3E 20
Load Register A with 20H (space) to separate this pair from the next output.
4493
GOSUB to 4532H to print the space.
4496
INC IX DD 23
INCrement Register Pair IX by 1 (this routine is also entered to display the second pair, and the extra advances skip the correct number of save-area bytes between the grouped pairs).
4498
GOSUB to 450FH to display the high byte of the second pair as two hex digits.
449B
DEC IX DD 2B
DECrement Register Pair IX by 1 to the low byte of the second pair.
449D
GOSUB to 450FH to display the low byte of the second pair.
44A0
INC IX DD 23
INCrement Register Pair IX by 1 past the low byte.
44A2
INC IX DD 23
INCrement Register Pair IX by 1 so IX points past both displayed pairs, ready for the next group.
44A4
RET C9
Return to the caller with IX advanced past the displayed register bytes.

44A5H - Jump Command

The Jump command reads a four-digit target address, stores it as the user Program Counter in the save area, and then falls into the register-reload path so the CPU is loaded from the save area before execution begins at the requested address.

44A5
GOSUB to 4532H to echo the J command letter and advance the cursor.
44A8
GOSUB to 4589H to read the two high hex digits (msb) of the jump address into Register A.
44AB
LD (483CH),A 32 3C 48
Store the address msb (Register A) to 483CH, the PC msb cell in the Register Save Area.
44AE
GOSUB to 4589H to read the two low hex digits (lsb) of the jump address into Register A.
44B1
LD (483BH),A 32 3B 48
Store the address lsb (Register A) to 483BH, the PC lsb cell, completing the user PC.
44B4
JUMP to 4396H (inside the Go path) to reload the user SP and PC and transfer control to the requested address.

44B7H - Register Reload and Resume

This is the shared exit path used by Go and Jump. It saves TBUG's stack, switches to the Register Save Area as a stack, pops the entire register file back into the CPU (including the alternate set, IX, and IY), restores the user stack, and returns to the user Program Counter placed on the stack by the caller.

44B7
PUSH HL E5
PUSH Register Pair HL (holding the user PC, set by the Go path at 439AH) onto the current stack so that the final RET will jump to it.
44B8
LD (4839H),SP ED 73 39 48
Store the current Stack Pointer to 4839H so the resume RET address (the user PC just pushed) can be recovered after the stack is switched.
44BC
DI F3
Disable interrupts so the register reload and stack switch cannot be interrupted.
44BD
LD SP,4825H 31 25 48
Load the Stack Pointer with 4825H, the base of the Register Save Area, so the following POP instructions pull the saved registers back into the CPU in ascending order.
44C0
POP AF F1
Restore Register Pair AF' (alternate) from 4825H-4826H. This is loaded while the alternate set will be selected below.
44C1
POP BC C1
Restore Register Pair BC' (alternate) from 4827H-4828H.
44C2
POP DE D1
Restore Register Pair DE' (alternate) from 4829H-482AH.
44C3
POP HL E1
Restore Register Pair HL' (alternate) from 482BH-482CH.
44C4
EX AF,AF' 08
Exchange AF with AF' so the alternate accumulator and flags just loaded become the alternate set, and the main AF slot is ready to receive the main value.
44C5
EXX D9
Switch back to the main register set, so the alternate values loaded above are parked in the alternate set and the next POPs fill the main registers.
44C6
POP AF F1
Restore Register Pair AF (main) from 482DH-482EH.
44C7
POP BC C1
Restore Register Pair BC (main) from 482FH-4830H.
44C8
POP DE D1
Restore Register Pair DE (main) from 4831H-4832H.
44C9
POP HL E1
Restore Register Pair HL (main) from 4833H-4834H.
44CA
POP IX DD E1
Restore Register Pair IX from 4835H-4836H.
44CC
POP IY FD E1
Restore Register Pair IY from 4837H-4838H. All registers are now reloaded from the save area.
44CE
LD SP,(4839H) ED 7B 39 48
Load the Stack Pointer from 4839H, restoring the stack that holds the pushed user PC as the pending return address.
44D2
RET C9
Return, popping the user PC into the Program Counter and transferring control to the user program with the full register state restored.

44D3H - Punch Command Parameter Entry

The Punch command dumps a block of memory to a SYSTEM-format cassette tape. This entry code reads the three four-digit addresses (start, end, and entry point) into the Punch parameter cells, then calls the tape-writer engine and returns to the command loop.

44D3
GOSUB to 4532H to echo the P command letter and advance the cursor.
44D6
GOSUB to 4589H to read the two high digits (msb) of the start address into Register A.
44D9
LD (4842H),A 32 42 48
Store the start-address msb (Register A) to 4842H, the start msb parameter cell.
44DC
GOSUB to 4589H to read the two low digits (lsb) of the start address into Register A.
44DF
LD (4841H),A 32 41 48
Store the start-address lsb (Register A) to 4841H. The start address now occupies 4841H-4842H.
44E2
GOSUB to 457BH to advance the cursor, separating the start and end address fields on screen.
44E5
GOSUB to 4589H to read the two high digits (msb) of the end address into Register A.
44E8
LD (4844H),A 32 44 48
Store the end-address msb (Register A) to 4844H, the end msb parameter cell.
44EB
GOSUB to 4589H to read the two low digits (lsb) of the end address into Register A.
44EE
LD (4843H),A 32 43 48
Store the end-address lsb (Register A) to 4843H. The end address now occupies 4843H-4844H.
44F1
GOSUB to 457BH to advance the cursor before the entry-point field.
44F4
GOSUB to 4589H to read the two high digits (msb) of the entry point into Register A.
44F7
LD (4846H),A 32 46 48
Store the entry-point msb (Register A) to 4846H, the entry msb parameter cell.
44FA
GOSUB to 4589H to read the two low digits (lsb) of the entry point into Register A.
44FD
LD (4845H),A 32 45 48
Store the entry-point lsb (Register A) to 4845H. The entry point now occupies 4845H-4846H.
4500
GOSUB to 46A5H, the Punch engine, which reads the file name and writes the tape for the parameters just captured.
4503
JUMP to 43A5H to return to the command loop once the tape has been written.

4506H - Load Command

The Load command reads a previously recorded SYSTEM-format tape into memory. It echoes the command letter, invokes the tape reader engine, and returns to the command loop.

4506
GOSUB to 4532H to echo the L command letter and advance the cursor.
4509
GOSUB to 4643H, the tape reader engine, which loads the SYSTEM tape into memory and verifies checksums.
450C
JUMP to 43A5H to return to the command loop after loading.

450FH - Display Byte As Two Hex Digits

Given IX pointing at a byte, this splits the byte into its high and low nibbles and prints each as an ASCII hex digit at the current display cursor. It is the core of every hex value shown on screen by the Memory and Register commands.

450F
LD A,(IX+00H) DD 7E 00
Load Register A with the byte at the location pointed to by IX (the value to display).
4512
SRL A CB 3F
Shift Register A right logically: move the high nibble toward the low nibble (first of four shifts, bit 0 discarded).
4514
SRL A CB 3F
Shift Register A right logically (second of four).
4516
SRL A CB 3F
Shift Register A right logically (third of four).
4518
SRL A CB 3F
Shift Register A right logically (fourth of four). Register A now holds the original high nibble in its low four bits.
451A
GOSUB to 4522H to convert the high nibble (0-0FH) to an ASCII hex digit and print it.
451D
LD A,(IX+00H) DD 7E 00
Reload Register A with the same byte at (IX) to process its low nibble.
4520
AND 0FH E6 0F
Mask Register A with 0FH, isolating the low nibble by clearing the high four bits.

4522H - Print One Hex Nibble

Converts a 4-bit value in Register A (0-0FH) into its ASCII hex character (0-9, A-F) and writes it to video RAM at the current cursor. This routine falls through into the cursor-advance helper so the cursor moves after the digit is written.

4522
LD HL,(483DH) 2A 3D 48
Load Register Pair HL with the current cursor pointer from 483DH-483EH, which points into video RAM (3C00H-3FFFH).
4525
ADD A,30H C6 30
ADD 30H to Register A, converting a binary digit 0-9 to ASCII 0-9 (this is corrected for A-F below).
4527
CP 3AH FE 3A
Compare Register A against 3AH (the character just past ASCII 9). If Register A is less than 3AH, the value was a decimal digit 0-9; if 3AH or greater it needs the letter adjustment.
4529
If the result is negative (Register A below 3AH, so a 0-9 digit), JUMP to 452EH to store the character as-is.
452C
ADD A,07H C6 07
ADD 07H to Register A to skip the seven ASCII punctuation characters between 9 and A, converting nibble values 0AH-0FH into ASCII A-F.
452E
LD (HL),A 77
Store the ASCII hex character (Register A) to video RAM at the cursor (HL), making the digit appear on screen.
452F
JUMP to 457BH to advance the cursor past the character just written and return to the caller.

4532H - Print Character And Advance Twice

Displays the character in Register A at the cursor, then advances the cursor twice, leaving a blank between output characters for the monitor's spaced hex readouts.

4532
LD HL,(483DH) 2A 3D 48
Load Register Pair HL with the current cursor pointer from 483DH-483EH.
4535
LD (HL),A 77
Store the character (Register A) to video RAM at the cursor, displaying it.
4536
GOSUB to 457BH to advance the cursor by one position past the character.
4539
JUMP to 457BH to advance the cursor a second time (adding the spacing) and return to the caller.

453CH - Newline With Scroll

Advances the cursor to the beginning of the next 16-column line within the monitor's left-hand output window. When the cursor reaches the bottom of the screen it scrolls the 16-column window up by one line and blanks the vacated bottom line, so TBUG output stays inside the first 16 columns.

453C
Loop Start
GOSUB to 457BH to advance the cursor by one position.
453F
LD A,L 7D
Load Register A with the low byte of the cursor pointer (L) to test the column within the current row.
4540
AND 3FH E6 3F
Mask Register A with 3FH, isolating the column offset (0-63) of the cursor within its 64-column video row.
4542
If the NZ FLAG is set (column offset not zero, so not at the start of a row), LOOP BACK to 453CH to keep advancing until the cursor reaches column 0 of the next row. Loop End
4544
LD A,H 7C
Load Register A with the high byte of the cursor pointer (H) to test whether the cursor has moved past the bottom of the screen.
4545
AND 03H E6 03
Mask Register A with 03H, isolating the two high bits of the video address that count 1K of screen (3C00H-3FFFH spans 3CH-3FH).
4547
CP 00H FE 00
Compare Register A against 00H. If the masked high bits are zero the cursor has wrapped past 3FFFH back toward 3C00H, meaning the bottom of the screen was passed.
4549
If the NZ FLAG is set (still within screen memory), relative JUMP to 456FH to simply blank the new line without scrolling.
454B
LD A,L 7D
Load Register A with the cursor low byte (L) for a second wrap test on the low address bits.
454C
CP 00H FE 00
Compare Register A against 00H to confirm the cursor is exactly at the wrap boundary (low byte zero as well).
454E
If the NZ FLAG is set (not exactly at the boundary), relative JUMP to 456FH to blank the new line without scrolling.
4550
LD IX,(483DH) DD 2A 3D 48
Load Register Pair IX with the cursor pointer from 483DH so the scroll routine can copy screen rows using indexed addressing.
4554
LD DE,0030H 11 30 00
Load Register Pair DE with 0030H (48). This is the stride added after each 16-column row to skip the 48 columns of each 64-column video line that TBUG does not use.
4557
LD B,0FH 06 0F
Load Register B with 0FH (15) as the outer loop count: 15 rows are copied upward to scroll the 16-line window.
4559
PUSH BC C5
Loop Start
Save Register Pair BC (the outer row counter) before reusing B for the inner byte loop.
455A
LD B,10H 06 10
Load Register B with 10H (16) as the inner loop count, one per column of a 16-column row.
455C
LD A,(IX+40H) DD 7E 40
Loop Start
Load Register A with the byte one full video row below the current position ((IX+40H), where 40H is 64 columns), the source of the upward copy.
455F
LD (IX+00H),A DD 77 00
Store that byte to the current row position (IX+00H), moving the lower line's content up by one line.
4562
INC IX DD 23
INCrement Register Pair IX by 1 to the next column in the row.
4564
DECrement Register B and LOOP BACK to 455CH if not zero, copying all 16 columns of this row. Loop End
4566
ADD IX,DE DD 19
ADD 0030H (DE) to Register Pair IX, skipping the 48 unused columns so IX lands at the start of the next 16-column row.
4568
POP BC C1
Restore Register Pair BC (the outer row counter).
4569
DECrement Register B and LOOP BACK to 4559H if not zero, scrolling all 15 rows upward. Loop End
456B
LD (483DH),IX DD 22 3D 48
Store the advanced IX (now pointing at the last, vacated line) back to the cursor pointer at 483DH so the freed line becomes the new output line.
456F
LD HL,(483DH) 2A 3D 48
Load Register Pair HL with the cursor pointer from 483DH to blank the current 16-column line.
4572
LD B,10H 06 10
Load Register B with 10H (16), the number of columns to blank.
4574
LD A,20H 3E 20
Load Register A with 20H (ASCII space), the blanking character.
4576
LD (HL),A 77
Loop Start
Store a space (Register A) to video RAM at (HL), clearing one column.
4577
INC HL 23
INCrement Register Pair HL by 1 to the next column.
4578
DECrement Register B and LOOP BACK to 4576H if not zero, blanking all 16 columns of the line. Loop End
457A
RET C9
Return to the caller with the cursor at the start of a fresh, blank output line.

457BH - Advance Cursor

Increments the display cursor by one position and keeps it inside the video RAM region by forcing the high byte back into the 3CH-3FH range, so the cursor never leaves the screen.

457B
LD HL,(483DH) 2A 3D 48
Load Register Pair HL with the cursor pointer from 483DH-483EH.
457E
INC HL 23
INCrement Register Pair HL by 1 to the next screen position.
457F
LD A,H 7C
Load Register A with the cursor high byte (H) to constrain it to the video address range.
4580
AND 03H E6 03
Mask Register A with 03H, keeping only the two low bits of the high byte (the 1K offset within the 3C00H-3FFFH screen).
4582
OR 3CH F6 3C
OR 3CH into Register A, forcing the high byte back to the 3CH-3FH range so the pointer always addresses video RAM (wrapping at the screen boundary).
4584
LD H,A 67
Load the corrected high byte (Register A) back into H.
4585
LD (483DH),HL 22 3D 48
Store the advanced, range-limited cursor pointer back to 483DH-483EH.
4588
RET C9
Return to the caller with the cursor advanced.

4589H - Input Two Hex Digits To A Byte

Reads two hex characters from the keyboard, echoing each as it is entered, and combines them into a single 8-bit value returned in Register A. The first digit becomes the high nibble and the second the low nibble. The internal entry at 458CH is used by the Memory command when the first key has already been read.

4589
GOSUB to 45A4H to read and validate the first hex digit (0-0FH), returned in Register A.
458C
LD B,A 47
Load Register B with the first nibble (Register A) to hold it while the digit is echoed and the second digit is read.
458D
PUSH BC C5
Save Register Pair BC (holding the first nibble in B) across the echo call, which uses BC.
458E
GOSUB to 4522H to echo the first hex digit to the screen (Register A still holds it).
4591
GOSUB to 45A4H to read and validate the second hex digit (0-0FH) into Register A.
4594
PUSH AF F5
Save Register Pair AF (the second nibble in A) across the echo call below.
4595
GOSUB to 4522H to echo the second hex digit to the screen.
4598
POP BC C1
Restore into Register Pair BC; the second nibble that was pushed as AF is popped into BC so that Register B now holds the second (low) nibble.
4599
POP AF F1
Restore into Register Pair AF; the first nibble that was pushed as BC is popped into AF so that Register A now holds the first (high) nibble.
459A
SLA A CB 27
Shift Left Arithmetic on Register A: move the high nibble one bit toward the top of the byte (first of four shifts).
459C
SLA A CB 27
Shift Left Arithmetic on Register A (second of four).
459E
SLA A CB 27
Shift Left Arithmetic on Register A (third of four).
45A0
SLA A CB 27
Shift Left Arithmetic on Register A (fourth of four). The first nibble now occupies the high four bits of Register A.
45A2
OR B B0
OR Register B (the low nibble) into Register A, merging the two nibbles into the complete 8-bit value.
45A3
RET C9
Return to the caller with the assembled byte in Register A.

45A4H - Get And Validate One Hex Digit

Reads a single key, accepts only valid hexadecimal characters (0-9 or A-F), and returns the 4-bit value in Register A. Non-hex keys are ignored (the routine loops), and the X key aborts the current command back to the command loop.

45A4
Loop Start
GOSUB to 45C8H to read one debounced keypress into Register A.
45A7
CP 41H FE 41
Compare Register A against 41H (ASCII A). If Register A is below 41H it may be a decimal digit; if 41H or above it may be a letter A-F.
45A9
If the result is negative (Register A below 41H), JUMP to 45B4H to handle it as a possible decimal digit 0-9.
45AC
CP 47H FE 47
Compare Register A against 47H (the character just past ASCII F). This tests the upper bound of the A-F range.
45AE
If the result is positive or zero (Register A is 47H or higher, so beyond F), JUMP to 45C1H to reject it (and check for the X abort key).
45B1
SUB 37H D6 37
SUBtract 37H from Register A, converting ASCII letters A-F (41H-46H) to their nibble values 0AH-0FH.
45B3
RET C9
Return with the valid hex nibble (0AH-0FH) in Register A.
45B4
CP 3AH FE 3A
Compare Register A against 3AH (the character just past ASCII 9), the upper bound of the digit range.
45B6
If the result is positive or zero (Register A is 3AH or higher, a non-digit below A), JUMP back to 45A4H to reject it and read another key.
45B9
CP 30H FE 30
Compare Register A against 30H (ASCII 0), the lower bound of the digit range.
45BB
If the result is negative (Register A below 30H, below ASCII 0), JUMP back to 45A4H to reject it and read another key.
45BE
SUB 30H D6 30
SUBtract 30H from Register A, converting ASCII digits 0-9 (30H-39H) to nibble values 00H-09H.
45C0
RET C9
Return with the valid hex nibble (00H-09H) in Register A.
45C1
CP 58H FE 58
Compare Register A against 58H (ASCII X), the abort key. If Register A equals 58H, the Z FLAG is set.
45C3
If the Z FLAG is set (X pressed), JUMP to 43A5H to abort the current command and return to the command loop.
45C6
The key was not a valid hex digit and not X; LOOP BACK to 45A4H to read another key. Loop End

45C8H - Debounced Key Scan

Waits for a fresh keypress. It uses the last-key cell at 484DH to require that a key be released (or a different key pressed) before accepting a repeat, and inserts a settle delay to debounce the contacts. The accepted ASCII code is returned in Register A.

45C8
LD A,(484DH) 3A 4D 48
Load Register A with the last accepted key code from the debounce cell at 484DH.
45CB
LD E,A 5F
Load Register E with the previous key code so it can be compared against the current keyboard reading.
45CC
Loop Start
GOSUB to 45E9H to scan the keyboard matrix; Register A returns the pressed key's ASCII code, or FFH if no key is down.
45CF
CP E BB
Compare Register A against Register E (the previous key). If they are equal, the Z FLAG is set, meaning the same key is still held.
45D0
If the Z FLAG is set (same key still down or still no key), LOOP BACK to 45CCH to keep scanning until the reading changes. Loop End
45D2
LD E,A 5F
Load Register E with the newly changed key reading to use as the reference for the debounce confirmation below.
45D3
LD B,0D4H 06 D4
Loop Start
Load Register B with 0D4H (212) as the settle delay count for debouncing.
45D5
PUSH BC C5
Loop Start
Save Register Pair BC. This tight push/pop pair pads out the delay timing.
45D6
POP BC C1
Restore Register Pair BC, completing one delay iteration's worth of cycles.
45D7
PUSH BC C5
Save Register Pair BC again to extend the settle delay.
45D8
POP BC C1
Restore Register Pair BC.
45D9
DECrement Register B and LOOP BACK to 45D5H if not zero, running the full settle delay. Loop End
45DB
GOSUB to 45E9H to re-scan the keyboard after the delay, confirming the keypress.
45DE
CP E BB
Compare Register A against Register E (the key read before the delay). If they match, the same key is stably pressed.
45DF
If the NZ FLAG is set (the reading changed during the delay, indicating a bounce), LOOP BACK to 45D3H to re-run the settle delay. Loop End
45E1
CP 0FFH FE FF
Compare Register A against 0FFH (the no-key indicator). If equal, the Z FLAG is set, meaning no key is actually pressed.
45E3
If the Z FLAG is set (no key), LOOP BACK to 45CCH to wait for a real keypress.
45E5
LD (484DH),A 32 4D 48
Store the accepted key code (Register A) to the debounce cell at 484DH as the new last-key value.
45E8
RET C9
Return to the caller with the debounced key code in Register A.

45E9H - Raw Keyboard Matrix Decode

Scans the Model I memory-mapped keyboard matrix at 3801H-3880H, which is read active-high per bit within each row address. It locates the first pressed key, computes a position code, and translates it to ASCII (with special handling for the shift row at 3880H). Returns the ASCII code in Register A, or FFH if no key is pressed.

45E9
XOR A AF
Set Register A to ZERO and clear all flags. This seeds the running key-position counter kept in the alternate accumulator.
45EA
LD HL,3801H 21 01 38
Load Register Pair HL with 3801H, the first keyboard matrix row address, to begin scanning the rows.
45ED
EX AF,AF' 08
Loop Start
Exchange AF with AF' so the position counter (kept in the alternate accumulator between iterations) becomes active in Register A.
45EE
LD A,(HL) 7E
Load Register A with the keyboard row byte at (HL). Each set bit corresponds to a pressed key in that matrix row.
45EF
CP 00H FE 00
Compare Register A against 00H. If the row byte is zero no key is down in this row, and the Z FLAG is set.
45F1
If the NZ FLAG is set (at least one key is pressed in this row), relative JUMP to 4600H to identify which key.
45F3
SLA L CB 25
Shift Left Arithmetic on Register L, doubling the low byte of the row address to step to the next matrix row (rows are at 3801H, 3802H, 3804H, 3808H, and so on).
45F5
EX AF,AF' 08
Exchange AF with AF' to bring the position counter back into Register A for updating.
45F6
ADD A,08H C6 08
ADD 08H to Register A, advancing the key-position counter by eight (one row equals eight key positions).
45F8
CP 37H FE 37
Compare Register A against 37H, the position count that marks the end of the scanned key rows.
45FA
If the result is negative (counter still below 37H, more rows to scan), JUMP back to 45EDH to test the next row. Loop End
45FD
LD A,0FFH 3E FF
All rows scanned with no key found; load Register A with 0FFH to signal no keypress.
45FF
RET C9
Return with FFH in Register A indicating no key is pressed.
4600
LD C,A 4F
Load Register C with the row byte (the pressed-key bit pattern) so its set bit can be located.
4601
EX AF,AF' 08
Exchange AF with AF' to bring the position counter (the base index for this row) into Register A.
4602
INC A 3C
Loop Start
INCrement Register A by 1, advancing the position counter for each bit position tested within the row.
4603
SRL C CB 39
Shift Register C right logically, moving the next matrix bit into the carry flag for testing.
4605
If the NO CARRY FLAG is set (the tested bit was 0, key not pressed), LOOP BACK to 4602H to test the next bit. Loop End
4607
DEC A 3D
DECrement Register A by 1 to correct the position counter, since the increment above ran one past the found bit.
4608
CP 30H FE 30
Compare Register A against 30H. Position codes of 30H or above map to the digit and symbol rows and are handled by a translation table; lower codes are letters.
460A
If the result is positive or zero (position 30H or above), JUMP to 462FH to translate via the symbol lookup table.
460D
CP 20H FE 20
Compare Register A against 20H. Positions in 20H-2FH are the letter block that needs an offset; below 20H is the first letter block.
460F
If the result is negative (position below 20H), JUMP to 462BH to add the base offset for the first letter group.
4612
ADD A,10H C6 10
ADD 10H to Register A, shifting the position code into the ASCII letter range for the second letter group.
4614
LD HL,3880H 21 80 38
Load Register Pair HL with 3880H, the shift-key row of the keyboard matrix, to test whether shift is held.
4617
SRL (HL) CB 3E
Shift the shift-row byte at (HL) right logically, moving the shift-key bit into the carry flag.
4619
RET NC D0
If the NO CARRY FLAG is set (shift key not pressed), Return with the unshifted ASCII character in Register A.
461A
CP 40H FE 40
Shift is held. Compare Register A against 40H (ASCII @) to decide how to apply the shift transformation to this character.
461C
If the result is negative (character below 40H), JUMP to 4625H to apply the shift adjustment for that range.
461F
CP 60H FE 60
Compare Register A against 60H (just past the uppercase letters). This bounds the letter range for shift handling.
4621
RET P F0
If the result is positive or zero (character 60H or above), Return with the character unchanged in Register A.
4622
AND 3FH E6 3F
Mask Register A with 3FH to fold a shifted letter into the correct code, then fall through to return.
4624
RET C9
Return with the shifted character in Register A.
4625
CP 30H FE 30
Compare Register A against 30H to check whether the shifted character is in the range needing a mask adjustment.
4627
RET M F8
If the result is negative (below 30H), Return with the character unchanged in Register A.
4628
AND 0EFH E6 EF
Mask Register A with 0EFH (clearing bit 4) to apply the shift transformation for digit-row symbols, then fall through to return.
462A
RET C9
Return with the shift-adjusted character in Register A.
462B
ADD A,40H C6 40
ADD 40H to Register A, mapping the first letter-block position codes up into the ASCII uppercase-letter range.
462D
JUMP to 4614H to apply shift handling to the resulting letter.
462F
LD HL,463BH 21 3B 46
Load Register Pair HL with 463BH, the base of the symbol/digit translation table, to look up the character for a position of 30H or higher.
4632
SUB 30H D6 30
SUBtract 30H from Register A to form a zero-based index into the translation table.
4634
LD B,00H 06 00
Load Register B with 00H to form the high byte of a 16-bit table offset.
4636
LD C,A 4F
Load Register C with the index (Register A), so BC holds the table offset.
4637
ADD HL,BC 09
ADD the offset (BC) to the table base (HL), so HL points at the translation table entry for this key.
4638
LD A,(HL) 7E
Load Register A with the ASCII character from the translation table entry.
4639
JUMP to 4614H to apply shift handling to the looked-up symbol.

463BH - Keyboard Symbol Translation Table

This is the lookup table used by the keyboard decoder at 462FH for the digit and symbol rows (position codes 30H and above). It is non-executable data; the disassembler misread the bytes as instructions, so they are shown here as their raw values. The bytes are the ASCII characters for the shifted/unshifted symbol keys.

463B
DEFB 0DH 0D
Symbol Table
Table entry byte (0DH). Data consumed by the decoder at 4638H, not executed.
463C
DEFB 63H 63
Table entry byte (63H). Data.
463D
DEFB 62H 62
Table entry byte (62H). Data.
463E
DEFB 5BH 5B
Table entry byte (5BH). Data.
463F
DEFB 5CH 5C
Table entry byte (5CH). Data.
4640
DEFB 5DH 5D
Table entry byte (5DH). Data.
4641
DEFB 5EH 5E
Table entry byte (5EH). Data.
4642
DEFB 20H 20
Table entry byte (20H, ASCII space). Data. This is the last table entry; the byte at 4643H begins the Load command code.

4643H - Load Command Engine

Reads a SYSTEM-format tape from the cassette port into memory. It waits for the A5H sync byte, then the 55H file-name header, skips the six-byte name, and processes 3CH data blocks: each block gives a byte count and a load address, and the data bytes are stored to memory while a running checksum is accumulated. A 78H trailer marks the entry point and ends the load. Byte 3FCCH is used as a screen flag cell during loading.

4643
GOSUB to 47CAH to set the cassette read latch (HL preloaded with FF04H) and turn on the cassette motor for reading.
4646
Loop Start
GOSUB to 4762H to read one bit-assembled byte from the tape into Register A while hunting for the sync byte.
4649
CP A5H FE A5
Compare Register A against A5H, the tape synchronization byte. If Register A equals A5H the Z FLAG is set, meaning sync was found.
464B
If the NZ FLAG is set (byte was not the sync byte), LOOP BACK to 4646H to keep reading until A5H appears. Loop End
464D
LD A,2AH 3E 2A
Load Register A with 2AH (ASCII asterisk), the loading-activity indicator.
464F
LD (3FCCH),A 32 CC 3F
Store the asterisk to video RAM at 3FCCH (lower-left area of the screen), showing the user that loading has begun.
4652
Loop Start
GOSUB to 4782H to read one full byte (eight bits) from the tape into Register A while looking for the name header.
4655
CP 55H FE 55
Compare Register A against 55H, the file-name header code. If equal, the Z FLAG is set.
4657
If the NZ FLAG is set (not the name header yet), LOOP BACK to 4652H to keep reading. Loop End
4659
LD B,06H 06 06
Load Register B with 06H, the number of file-name bytes to skip.
465B
Loop Start
GOSUB to 4782H to read one byte of the six-character file name (which is discarded on load).
465E
DECrement Register B and LOOP BACK to 465BH if not zero, skipping all six name bytes. Loop End
4660
LD A,(3FCCH) 3A CC 3F
Block Loop Start
Load Register A with the current activity-indicator character from 3FCCH to toggle it between data blocks.
4663
XOR 0AH EE 0A
Exclusive-OR Register A with 0AH, flipping the character between the asterisk and a blank so the on-screen indicator blinks as blocks load.
4665
LD (3FCCH),A 32 CC 3F
Store the toggled indicator back to video RAM at 3FCCH.
4668
GOSUB to 4782H to read the next control byte from the tape into Register A.
466B
CP 78H FE 78
Compare Register A against 78H, the entry-point trailer code. If equal, the Z FLAG is set, marking the end of the tape data.
466D
If the Z FLAG is set (entry-point trailer reached), relative JUMP to 4695H to read the entry address and finish.
466F
CP 3CH FE 3C
Compare Register A against 3CH, the data-block header code. If equal, the Z FLAG is set, meaning a data block follows.
4671
If the NZ FLAG is set (neither trailer nor data header), LOOP BACK to 4668H to resynchronize on the next control byte.
4673
GOSUB to 4782H to read the block byte count into Register A (a count of 00H represents a full 256-byte block).
4676
LD B,A 47
Load Register B with the block byte count to use as the data loop counter.
4677
GOSUB to 469CH to read the two-byte block load address from the tape into Register Pair HL.
467A
ADD A,L 85
ADD Register L to Register A. The address low byte seeds the running block checksum (Register A holds the address msb returned by 469CH).
467B
LD C,A 4F
Load Register C with the seeded checksum value so it accumulates as each data byte is read.
467C
Loop Start
GOSUB to 4782H to read one data byte from the tape into Register A.
467F
LD (HL),A 77
Store the data byte (Register A) to memory at the current load address (HL).
4680
INC HL 23
INCrement Register Pair HL by 1 to the next load address.
4681
ADD A,C 81
ADD Register C (the running checksum) to Register A (the byte just stored), forming the updated checksum.
4682
LD C,A 4F
Load Register C with the updated checksum for the next iteration.
4683
DECrement Register B and LOOP BACK to 467CH if not zero, loading every byte in the block. Loop End
4685
GOSUB to 4782H to read the block's stored checksum byte into Register A.
4688
CP C B9
Compare Register A (the tape checksum) against Register C (the computed checksum). If they match, the Z FLAG is set, confirming a good block.
4689
If the Z FLAG is set (checksum matched), LOOP BACK to 4660H to process the next data block. Block Loop End
468B
LD IY,(483DH) FD 2A 3D 48
Checksum failed. Load Register Pair IY with the display cursor pointer from 483DH so an error marker can be written to the screen.
468F
LD (IY+00H),45H FD 36 00 45
Store 45H (ASCII E) to the screen at the cursor (IY), flagging a checksum error to the user.
4693
LOOP BACK to 4668H to continue scanning for control bytes despite the error.
4695
Load Complete
GOSUB to 469CH to read the two-byte program entry-point address from the tape into HL (recorded but not auto-executed).
4698
GOSUB to 47C5H to turn off the cassette (HL preloaded with FB00H clears the motor bit).
469B
RET C9
Return to the Load command handler, which then returns to the command loop.

469CH - Read Two-Byte Address From Tape

Reads a little-endian 16-bit address from the tape into Register Pair HL (low byte first into L, then high byte into H). Register A is left holding the high byte so the caller can seed a checksum.

469C
GOSUB to 4782H to read the address low byte from the tape into Register A.
469F
LD L,A 6F
Load Register L with the address low byte.
46A0
GOSUB to 4782H to read the address high byte from the tape into Register A.
46A3
LD H,A 67
Load Register H with the address high byte, completing the 16-bit address in HL (with A still holding the high byte).
46A4
RET C9
Return with the address in HL and the high byte in Register A.

46A5H - Punch Command Engine

Prompts for a six-character file name, then writes a SYSTEM-format tape for the memory range in the Punch parameter cells (4841H-4846H). It emits a leader of zero bytes, the A5H sync, the 55H name header and padded name, then 256-byte data blocks each with a 3CH header, byte count, load address and checksum, and finally a 78H trailer with the entry-point address.

46A5
LD HL,4847H 21 47 48
Load Register Pair HL with 4847H, the base of the file-name buffer, to pre-fill it with blanks.
46A8
LD B,06H 06 06
Load Register B with 06H, the six-character length of the name buffer.
46AA
LD (HL),20H 36 20
Loop Start
Store 20H (ASCII space) into the name buffer at (HL), initializing each position to blank.
46AC
INC HL 23
INCrement Register Pair HL by 1 to the next name-buffer byte.
46AD
DECrement Register B and LOOP BACK to 46AAH if not zero, blanking all six name positions. Loop End
46AF
GOSUB to 457BH to advance the display cursor before echoing the name entry.
46B2
LD IY,4847H FD 21 47 48
Load Register Pair IY with 4847H so the entered name characters can be stored into the buffer.
46B6
LD B,06H 06 06
Load Register B with 06H as the count of name characters to accept.
46B8
PUSH BC C5
Loop Start
Save Register Pair BC (the name-length counter) across the key input call.
46B9
GOSUB to 45C8H to read one debounced keypress for the file name into Register A.
46BC
CP 62H FE 62
Compare Register A against 62H. This key value (the BREAK-derived code) requests cancellation of the Punch command.
46BE
If the Z FLAG is set (cancel key pressed), JUMP to 475CH to discard the saved counter and abort the Punch command.
46C1
CP 0DH FE 0D
Compare Register A against 0DH (ENTER). Pressing ENTER ends a name shorter than six characters and starts the write.
46C3
If the Z FLAG is set (ENTER pressed), JUMP to 475EH to discard the counter and begin writing the tape with the name entered so far.
46C6
CP 20H FE 20
Compare Register A against 20H (space). Control characters below 20H are rejected as invalid name characters.
46C8
If the CARRY FLAG is set (Register A below 20H, a control character), LOOP BACK to 46B9H to read another key.
46CA
LD HL,(483DH) 2A 3D 48
Load Register Pair HL with the display cursor pointer from 483DH to echo the accepted name character.
46CD
LD (HL),A 77
Store the character (Register A) to video RAM at the cursor, echoing it.
46CE
LD (IY+00H),A FD 77 00
Store the character (Register A) into the name buffer at (IY).
46D1
INC IY FD 23
INCrement Register Pair IY by 1 to the next name-buffer position.
46D3
GOSUB to 457BH to advance the display cursor past the echoed character.
46D6
POP BC C1
Restore Register Pair BC (the name-length counter).
46D7
DECrement Register B and LOOP BACK to 46B8H if not zero, accepting up to six name characters. When B reaches zero the write begins automatically. Loop End
46D9
LD IY,4847H FD 21 47 48
Begin Tape Write
Load Register Pair IY with 4847H, the name buffer base, to write the file name onto the tape.
46DD
GOSUB to 47CAH to set the cassette port for output and start the motor.
46E0
LD HL,(4843H) 2A 43 48
Load Register Pair HL with the end address from the Punch parameter cells (4843H-4844H).
46E3
LD DE,(4841H) ED 5B 41 48
Load Register Pair DE with the start address from the Punch parameter cells (4841H-4842H). DE is the running write pointer.
46E7
XOR A AF
Set Register A to ZERO and clear the carry flag in preparation for the length subtraction.
46E8
SBC HL,DE ED 52
SUBtract DE (start) from HL (end) with the cleared carry, giving the block length minus one in HL.
46EA
INC HL 23
INCrement Register Pair HL by 1 so HL holds the total number of bytes to write (inclusive of both endpoints).
46EB
LD A,FFH 3E FF
Load Register A with FFH as the leader byte count image (the leader is written as a run of zero bytes below).
46ED
EX AF,AF' 08
Loop Start
Exchange AF with AF' to keep the leader counter in the alternate accumulator while the main A is used to write.
46EE
XOR A AF
Set Register A to ZERO, the value of each leader byte.
46EF
GOSUB to 478CH to write one zero leader byte to the tape.
46F2
EX AF,AF' 08
Exchange AF with AF' to bring the leader counter back into Register A.
46F3
DEC A 3D
DECrement Register A by 1, counting down the leader bytes.
46F4
If the NZ FLAG is set (more leader bytes to write), LOOP BACK to 46EDH. Loop End
46F6
LD A,A5H 3E A5
Load Register A with A5H, the tape synchronization byte.
46F8
GOSUB to 478CH to write the A5H sync byte to the tape.
46FB
LD A,55H 3E 55
Load Register A with 55H, the file-name header code.
46FD
GOSUB to 478CH to write the 55H name header to the tape.
4700
LD B,06H 06 06
Load Register B with 06H, the number of file-name bytes to write.
4702
LD A,(IY+00H) FD 7E 00
Loop Start
Load Register A with a file-name character from the buffer at (IY).
4705
GOSUB to 478CH to write the name character to the tape.
4708
INC IY FD 23
INCrement Register Pair IY by 1 to the next name character.
470A
DECrement Register B and LOOP BACK to 4702H if not zero, writing all six name bytes. Loop End
470C
DEC H 25
Block Loop Start
DECrement Register H by 1. HL holds the remaining byte count; decrementing the high byte tests whether at least one more full 256-byte block remains.
470D
If the result is negative (fewer than 256 bytes remain), JUMP to 471EH to write the final partial block.
4710
LD A,3CH 3E 3C
Load Register A with 3CH, the data-block header code.
4712
GOSUB to 478CH to write the 3CH data-block header.
4715
XOR A AF
Set Register A to ZERO. A count byte of 00H denotes a full 256-byte block in the SYSTEM format.
4716
GOSUB to 478CH to write the block byte count (00H = 256).
4719
GOSUB to 4743H to write the block's load address and its 256 data bytes with checksum.
471C
LOOP BACK to 470CH to test for and write further full blocks. Block Loop End
471E
XOR A AF
Final Partial Block
Set Register A to ZERO to compare against the remaining low-byte count in Register L.
471F
CP L BD
Compare Register A (zero) against Register L (the remaining byte count). If they are equal there are no leftover bytes, and the Z FLAG is set.
4720
If the Z FLAG is set (no remaining bytes), relative JUMP to 472EH to write the entry-point trailer.
4722
LD A,3CH 3E 3C
Load Register A with 3CH, the data-block header code, for the final partial block.
4724
GOSUB to 478CH to write the 3CH header for the partial block.
4727
LD A,L 7D
Load Register A with the remaining byte count (Register L) as the partial block's length.
4728
GOSUB to 478CH to write the partial block byte count.
472B
GOSUB to 4743H to write the load address and the remaining data bytes with checksum.
472E
LD A,78H 3E 78
Load Register A with 78H, the entry-point trailer code.
4730
GOSUB to 478CH to write the 78H entry-point header.
4733
LD A,(4845H) 3A 45 48
Load Register A with the entry-point low byte from 4845H.
4736
GOSUB to 478CH to write the entry-point low byte.
4739
LD A,(4846H) 3A 46 48
Load Register A with the entry-point high byte from 4846H.
473C
GOSUB to 478CH to write the entry-point high byte.
473F
GOSUB to 47C5H to turn off the cassette motor now that the tape image is complete.
4742
RET C9
Return to the Punch command handler, which returns to the command loop.

4743H - Write Data Block Body And Checksum

Writes one block's two-byte load address followed by its data bytes, accumulating a checksum over the address and data, and finally writes the one-byte checksum. The data pointer is Register Pair DE and the byte count is Register B.

4743
LD B,A 47
Load Register B with the block byte count (passed in Register A by the caller) as the data loop counter.
4744
LD A,E 7B
Load Register A with the load-address low byte (Register E) to write it and seed the checksum.
4745
GOSUB to 478CH to write the load-address low byte to the tape.
4748
LD A,D 7A
Load Register A with the load-address high byte (Register D).
4749
GOSUB to 478CH to write the load-address high byte to the tape.
474C
ADD A,E 83
ADD Register E (address low byte) to Register A (address high byte) to seed the running checksum.
474D
LD C,A 4F
Load Register C with the seed checksum value for accumulation over the data bytes.
474E
LD A,(DE) 1A
Loop Start
Load Register A with the next memory data byte pointed to by DE.
474F
GOSUB to 478CH to write the data byte to the tape.
4752
ADD A,C 81
ADD Register C (the running checksum) to Register A (the byte just written), forming the updated checksum.
4753
LD C,A 4F
Load Register C with the updated checksum for the next iteration.
4754
INC DE 13
INCrement Register Pair DE by 1 to the next source memory byte.
4755
DECrement Register B and LOOP BACK to 474EH if not zero, writing every data byte in the block. Loop End
4757
LD A,C 79
Load Register A with the final checksum (Register C) to write it to the tape.
4758
GOSUB to 478CH to write the block checksum byte.
475B
RET C9
Return to the Punch engine with DE advanced past the written block.

475CH - Punch Cancel And Resume Handlers

Two short exits used during file-name entry. The cancel path (475CH) discards the saved counter and returns to the command loop; the ENTER path (475EH) discards the counter and jumps into the tape-write sequence to punch the block using the name entered so far.

475C
POP BC C1
Cancel Path
Restore (and discard) the name-length counter Register Pair BC that was pushed during name entry, balancing the stack.
475D
RET C9
Return to the Punch command handler (which returns to the command loop), aborting the Punch.
475E
POP BC C1
Enter Path
Restore (and discard) the saved counter Register Pair BC, balancing the stack before writing.
475F
JUMP to 46D9H to begin writing the tape with the file name entered so far.

4762H - Read One Cassette Bit

Reads a single bit from the cassette port at FFH. It uses the alternate register set, waits for the sync pulse, delays, samples the data pulse, and rotates the bit into the accumulator. The Model I cassette interface presents data on the port bits, and the routine clears the read latch between samples via the port helper at 47CFH.

4762
EXX D9
Switch to the alternate register set so the bit-timing loop does not disturb the caller's main registers. The alternate set is now active.
4763
EX AF,AF' 08
Exchange AF with AF' so the alternate accumulator and flags are used for the bit assembly.
4764
IN A,(0FFH) DB FF
Loop Start
Read the cassette port FFH into Register A to test for the leading sync pulse of the bit cell.
4766
RLA 17
Rotate Register A left through carry, moving the high data bit of the port into the carry flag for testing.
4767
If the NO CARRY FLAG is set (no sync pulse yet), LOOP BACK to 4764H to keep polling until the pulse arrives. Loop End
4769
LD B,41H 06 41
Load Register B with 41H as the delay count that positions the sample point within the bit cell.
476B
DECrement Register B and LOOP BACK to itself until zero, forming the timing delay to the data-sample point.
476D
GOSUB to 47CFH to clear the cassette read latch (HL preloaded with FF00H) so the next pulse can be detected.
4770
LD B,76H 06 76
Load Register B with 76H as a second delay count before sampling the data bit.
4772
DECrement Register B and LOOP BACK to itself until zero, completing the pre-sample delay.
4774
IN A,(0FFH) DB FF
Read the cassette port FFH into Register A to capture the data bit value at the sample point.
4776
LD B,A 47
Load Register B with the sampled port value so the data bit can be rotated out of it.
4777
EX AF,AF' 08
Exchange AF with AF' to bring the partially assembled byte back into Register A.
4778
RL B CB 10
Rotate Register B left through carry, moving the sampled data bit (bit 7 of the port reading) into the carry flag.
477A
RLA 17
Rotate Register A left through carry, shifting the new data bit from carry into the low bit of the assembled byte.
477B
PUSH AF F5
Save Register Pair AF (the assembled byte and flags) across the latch-clear call.
477C
GOSUB to 47CFH to clear the read latch again in readiness for the next bit.
477F
POP AF F1
Restore Register Pair AF (the assembled byte).
4780
EXX D9
Switch back to the main register set, restoring the caller's registers.
4781
RET C9
Return to the caller with the newly read bit shifted into the assembled byte (held in the alternate accumulator across calls).

4782H - Read One Cassette Byte

Reads a full byte from the tape by calling the single-bit reader eight times. The bits accumulate in the alternate accumulator, and the completed byte is returned in Register A.

4782
PUSH BC C5
Save Register Pair BC so the caller's BC is preserved across the eight-bit read loop.
4783
LD B,08H 06 08
Load Register B with 08H, the number of bits in a byte, as the loop count.
4785
Loop Start
GOSUB to 4762H to read one bit and shift it into the assembled byte.
4788
DECrement Register B and LOOP BACK to 4785H if not zero, reading all eight bits. Loop End
478A
POP BC C1
Restore Register Pair BC.
478B
RET C9
Return with the completed byte in Register A.

478CH - Write One Cassette Byte

Writes a single byte to the tape most-significant-bit first. It uses the alternate register set, and for each bit it emits a clock pulse and, if the bit is a one, a data pulse, using the pulse generator at 47A6H. The byte to write is passed in Register A.

478C
EXX D9
Switch to the alternate register set so the bit-serialization loop does not disturb the caller's registers.
478D
LD C,08H 0E 08
Load Register C with 08H, the number of bits to write.
478F
LD D,A 57
Load Register D with the byte to write so its bits can be rotated out one at a time.
4790
Loop Start
GOSUB to 47A6H to emit the clock pulse that begins every bit cell.
4793
RLC D CB 02
Rotate Register D left circularly, moving the next bit to write (most significant first) into the carry flag.
4795
If the NO CARRY FLAG is set (the bit is a zero), relative JUMP to 47A0H to insert the zero-bit delay without a data pulse.
4797
The bit is a one; GOSUB to 47A6H to emit the data pulse that represents a one bit.
479A
DEC C 0D
Bit Done
DECrement Register C by 1, counting down the bits written.
479B
If the NZ FLAG is set (more bits to write), LOOP BACK to 4790H to write the next bit. Loop End
479D
LD A,D 7A
Load Register A with Register D (the fully rotated byte, restored to its original value after eight circular rotates) as the routine's return value.
479E
EXX D9
Switch back to the main register set, restoring the caller's registers.
479F
RET C9
Return to the caller with the byte written to tape.
47A0
LD B,87H 06 87
Zero-Bit Delay
Load Register B with 87H as the delay count that fills the bit cell when writing a zero (no data pulse).
47A2
DECrement Register B and LOOP BACK to itself until zero, forming the zero-bit timing delay.
47A4
JUMP to 479AH to rejoin the bit loop after the zero-bit delay.

47A6H - Cassette Pulse Generator

Produces one cassette write pulse by driving the port through its rising and falling levels with calibrated delays. It writes the three level patterns (FC01H, FC02H, FC00H masks) via the port output helper at 47D2H, each followed by a timing delay, generating the waveform recorded on tape.

47A6
LD HL,0FC01H 21 01 FC
Load Register Pair HL with FC01H, the AND/OR mask pair that drives the first level of the pulse (H is the AND mask FCH, L is the OR mask 01H).
47A9
GOSUB to 47D2H to output the masked value to the cassette port, setting the first pulse level.
47AC
LD B,0BH 06 0B
Load Register B with 0BH as the delay count for the first pulse level.
47AE
DECrement Register B and LOOP BACK to itself until zero, holding the first level for its timed duration.
47B0
LD HL,0FC02H 21 02 FC
Load Register Pair HL with FC02H, the mask pair for the second pulse level (OR mask 02H).
47B3
GOSUB to 47D2H to output the second pulse level to the cassette port.
47B6
LD B,0BH 06 0B
Load Register B with 0BH as the delay count for the second pulse level.
47B8
DECrement Register B and LOOP BACK to itself until zero, holding the second level.
47BA
LD HL,0FC00H 21 00 FC
Load Register Pair HL with FC00H, the mask pair that returns the port to its rest level (OR mask 00H).
47BD
GOSUB to 47D2H to output the rest level, completing the pulse.
47C0
LD B,5CH 06 5C
Load Register B with 5CH as the inter-pulse delay count.
47C2
DECrement Register B and LOOP BACK to itself until zero, spacing this pulse from the next.
47C4
RET C9
Return to the caller with one pulse generated.

47C5H - Cassette Port Control Entries

Three short entry points that preload Register Pair HL with a specific AND/OR mask pair and fall into the port output routine. 47C5H turns the cassette off, 47CAH enables the read latch and motor, and 47CFH clears the read latch. Each sets HL then reaches the common output code at 47D2H.

47C5
LD HL,0FB00H 21 00 FB
Cassette Off
Load Register Pair HL with FB00H (AND mask FBH clears the motor bit, OR mask 00H) to turn the cassette off.
47C8
JUMP to 47D2H to apply the mask and write it to the cassette port.
47CA
LD HL,0FF04H 21 04 FF
Cassette On / Read Latch
Load Register Pair HL with FF04H (AND mask FFH preserves all bits, OR mask 04H sets the motor/read bit) to enable the cassette.
47CD
JUMP to 47D2H to apply the mask and write it to the cassette port.
47CF
LD HL,0FF00H 21 00 FF
Clear Read Latch
Load Register Pair HL with FF00H (AND mask FFH, OR mask 00H) to clear the read latch without changing other bits, then fall through into the output routine.

47D2H - Cassette Port Output

The single point through which all cassette port writes pass. Because port FFH is write-only, TBUG keeps a shadow image at 484EH; this routine masks that image with H (AND) and L (OR), writes the result to the port, and refreshes the shadow, then tests bit 2 (the read/data bit) for the caller.

47D2
LD A,(484EH) 3A 4E 48
Load Register A with the cassette port shadow image from 484EH, the last value written to port FFH.
47D5
AND H A4
AND Register A with Register H (the AND mask), clearing the bits the caller wants forced to zero.
47D6
OR L B5
OR Register A with Register L (the OR mask), setting the bits the caller wants forced to one.
47D7
OUT (0FFH),A D3 FF
Send the masked value in Register A to cassette port FFH, driving the cassette hardware (motor relay on bit 2 and the data line).
47D9
LD (484EH),A 32 4E 48
Store the value just written back to the shadow image at 484EH so the next masked write starts from the current state.
47DC
BIT 2,A CB 57
Test bit 2 of Register A (the cassette motor/data bit), setting the Z FLAG according to its state for any caller that inspects it.
47DE
RET C9
Return to the caller with the port updated and the shadow image refreshed.

47DFH - Breakpoint Command

The Breakpoint command reads a target address, saves the three original bytes at that address into the breakpoint save cells, and overwrites them with a three-byte CALL to the register-save trap. When the user program later reaches that address it calls into TBUG, which captures the registers. Only one breakpoint can be active because a single save area is used.

47DF
GOSUB to 4532H to echo the B command letter and advance the cursor.
47E2
GOSUB to 4589H to read the two high hex digits (msb) of the breakpoint address into Register A.
47E5
LD (4850H),A 32 50 48
Store the address msb (Register A) to 4850H, forming the high byte of the breakpoint address held at 484FH-4850H.
47E8
GOSUB to 4589H to read the two low hex digits (lsb) of the breakpoint address into Register A.
47EB
LD (484FH),A 32 4F 48
Store the address lsb (Register A) to 484FH. The breakpoint target address now occupies 484FH-4850H.
47EE
LD HL,(484FH) 2A 4F 48
Load Register Pair HL with the breakpoint target address from 484FH-4850H so the original bytes there can be read.
47F1
PUSH HL E5
Save Register Pair HL (the target address) on the stack for reuse after the save loop.
47F2
LD B,03H 06 03
Load Register B with 03H, the number of original bytes to preserve (the size of the CALL trap that will replace them).
47F4
LD IY,484FH FD 21 4F 48
Load Register Pair IY with 484FH, the base of the breakpoint save cells, as the destination for the preserved bytes.
47F8
LD A,(HL) 7E
Loop Start
Load Register A with an original byte from the user code at the breakpoint address (HL).
47F9
LD (IY+00H),A FD 77 00
Store the original byte (Register A) into the breakpoint save cell at (IY) so the Fix command can restore it later.
47FC
INC IY FD 23
INCrement Register Pair IY by 1 to the next save cell.
47FE
INC HL 23
INCrement Register Pair HL by 1 to the next byte of user code.
47FF
DECrement Register B and LOOP BACK to 47F8H if not zero, saving all three original bytes. Loop End
4801
POP HL E1
Restore Register Pair HL to the breakpoint target address so the trap CALL can be planted.
4802
LD (HL),0CDH 36 CD
Self-Modifying Code
Store CDH (the opcode for CALL) into the user code at the breakpoint address, overwriting the first original byte. This begins the planted trap that diverts execution into TBUG.
4804
INC HL 23
INCrement Register Pair HL by 1 to the second byte of the planted CALL.
4805
LD (HL),80H 36 80
Self-Modifying Code
Store 80H, the low byte of the trap address 4380H, into the user code. The full planted instruction becomes CALL 4380H, the breakpoint return stub that reconstructs the user PC and saves registers.
4807
INC HL 23
INCrement Register Pair HL by 1 to the third byte of the planted CALL.
4808
LD (HL),43H 36 43
Self-Modifying Code
Store 43H, the high byte of the trap address 4380H, completing the planted CALL 4380H at the breakpoint location.
480A
JUMP to 43A5H to return to the command loop with the breakpoint now armed.

480DH - Fix Command

The Fix command undoes a breakpoint after it has been hit. It copies the three saved original bytes back to the location held in the user Program Counter, restoring the instruction the breakpoint replaced so that execution can be resumed cleanly with a Go or Jump.

480D
GOSUB to 4532H to echo the F command letter and advance the cursor.
4810
LD HL,(483BH) 2A 3B 48
Load Register Pair HL with the saved user Program Counter from 483BH-483CH, which is where the breakpoint was hit and where the original bytes must be restored.
4813
LD IY,484FH FD 21 4F 48
Load Register Pair IY with 484FH, the base of the breakpoint save cells holding the three original bytes.
4817
LD B,03H 06 03
Load Register B with 03H, the number of bytes to restore.
4819
LD A,(IY+00H) FD 7E 00
Loop Start
Load Register A with a saved original byte from the breakpoint save cell at (IY).
481C
LD (HL),A 77
Store the original byte (Register A) back into the user code at (HL), undoing one byte of the planted CALL.
481D
INC HL 23
INCrement Register Pair HL by 1 to the next user code byte.
481E
INC IY FD 23
INCrement Register Pair IY by 1 to the next save cell.
4820
DECrement Register B and LOOP BACK to 4819H if not zero, restoring all three original bytes. Loop End
4822
JUMP to 43A5H to return to the command loop with the user code restored and ready to resume.