TRS-80 DOS - NEWDOS/80 v2.0 for the Model III - SYS3/SYS Disassembled

Page Customization

Introduction / Summary

SYS3/SYS — NEWDOS/80 v2.0 System Utility Functions Overlay (Model III)

SYS3 is a system overlay file for NEWDOS/80 v2.0 on the TRS-80 Model III. It loads at address 4D00H and occupies memory through 51DFH (1,248 bytes). SYS3 is loaded into RAM on demand when one of its functions is invoked via the RST 28H supervisor call mechanism. Because SYS3 overlays the same memory region each time it is loaded, it shares address space with other overlay modules (SYS4, SYS5, etc.), only one of which can be resident at a time.

SYS3 provides seven primary function handlers, dispatched by the function code in Register A:

Function CodeCommandDescription
25HFREECalculate and display free disk space; verify and repair the Granule Allocation Table (GAT)
45HPURGEDelete files matching a filespec; update GAT to release allocated granules
65HSETHOOKInsert a hook entry into the linked list at 4584H (interrupt vector chain)
85HRELHOOKRemove a hook entry from the linked list at 4584H
A5HDEBUG/DUMPDump memory contents to the printer; non-printable characters shown as periods
C5HKILL (access)Validate file access for the KILL command; return error 25H if not found
E5HSystem ConfigSystem configuration dispatcher with 10 sub-functions (C=1 through C=10)

The E5H function is itself a sub-dispatcher, routing to one of 10 sub-functions based on the value in Register C:

C ValueSub-function
1SCREEN mode toggle (bit 0 of 401CH)
2Display configuration (calls ROM 02A1H or 0298H)
3FORMS mode toggle (bit 7 of 4289H)
4SKIP mode configuration (self-modifying code at 4A2DH-4A2FH)
5DEBUG hook enable/disable (self-modifying code at 4478H-447AH)
6KILL interactive file deletion (with Y/N/Q prompt, /ext filter, USR option)
7File position/EOF update
8(Sub-functions 8-10 are dispatched via the jump table at 4D1FH-4D24H)
9(see above)
10TRACE mode toggle (bit 0 of 4019H)

Key Technical Features:

Self-Modifying Code: SYS3 makes extensive use of self-modifying code. Five locations are dynamically patched at runtime: 4EA1H (directory entry position), 4FB8H (FREE/PURGE mode flag), 5130H (KILL directory scan limit), 4A2DH-4A2FH (SKIP mode opcodes), and 4478H-447AH (DEBUG hook vector).

Bitmap Overlay: The FREE and PURGE commands build a 192-byte granule allocation bitmap at 4D00H-4DBFH, deliberately overwriting SYS3's own function dispatcher code. This is safe because the dispatcher has already completed execution before the bitmap is constructed. An additional 32-byte workspace at 4DC0H-4DDFH tracks granule changes during PURGE.

Hook Chain: The SETHOOK and RELHOOK functions manage a singly-linked list rooted at 4584H. Each hook entry begins with a 2-byte "next" pointer. Hooks are inserted at the head and removed by walking the chain. Interrupts are disabled (DI) during hook insertion to prevent corruption.

No FDC Operations: Unlike other SYS overlays, SYS3 does not directly access the floppy disk controller (ports F0H-F4H). All disk I/O is delegated to SYS0 routines (48AFH, 48C4H, 48D4H, 48DBH, 4C57H) which handle the hardware interface.

Variables

Complete table of all memory locations referenced by SYS3, including system variables, self-modifying code locations, scratch buffers, and data areas. Locations marked [SMC] are self-modifying code targets — their values are patched at runtime.

AddressSizeNameDescription
3840H1KBD_ROW6Keyboard Row 6 memory-mapped I/O (ENTER, CLEAR, BREAK, arrow keys, SPACE). Bit 2 = BREAK key (active low).
4019H1TRACE_FLAGTRACE mode flag. Bit 0: 1=enabled, 0=disabled. Toggled by E5H sub-function C=10.
401CH1SCREEN_FLAGSCREEN mode flag. Bit 0: 1=enabled, 0=disabled. Toggled by E5H sub-function C=1.
427EH1CURR_DRIVECurrent drive number (binary 0-3). Read by filename display routine at 4F94H.
4288H1SYS_STATUSSystem status flag. Cleared (00H) by FREE command at 4E77H.
4289H1FORMS_FLAGFORMS mode flag. Bit 7: 1=enabled, 0=disabled. Toggled by E5H sub-function C=3.
428BH1SYS_CTRLSystem control flags. Bit 2: when set, suppresses filename display during FREE/PURGE.
4290H1DUMP_LIMITUpper character code limit for DEBUG/DUMP display. Characters above this value are replaced with periods.
431FH1DIR_MAX_SECMaximum sector number for the directory track. Used by KILL at 508BH to calculate scan limit.
4478H1DEBUG_HOOK [SMC]Debug hook instruction byte. C3H = JP (hook enabled), C9H = RET (hook disabled). Written by E5H sub-function C=5.
4479H-447AH2DEBUG_ADDR [SMC]Debug hook target address. Set to 4546H when enabled. Follows the C3H opcode at 4478H.
4480H-449FH32FCB_TEMPLATEFCB template/buffer for file operations. Used by KILL at 5113H as the deletion FCB.
4487H1KILL_ENTRY_IDCombined sector+position identifier for the current KILL directory entry. Written at 50CEH.
4584H-4585H2HOOK_CHAINHook chain head pointer. Points to the first entry in the singly-linked list of installed hooks, or 0000H if the chain is empty.
4A2DH1SKIP_BYTE1 [SMC]SKIP mode self-modifying code byte 1. Set to DDH (IX prefix) for SKIP=Y or 00H (NOP) for SKIP=N.
4A2EH-4A2FH2SKIP_BYTE23 [SMC]SKIP mode self-modifying code bytes 2-3. Set to 7E01H (LD A,(IX+01H)) for SKIP=Y or 3EFFH (LD A,FFH) for SKIP=N.
4D00H-4DBFH192BITMAP_BUFGranule allocation bitmap buffer (C0H bytes, 1 bit per granule). Initialized to FFH at 4E7EH, then bits are cleared for each allocated granule. Overlays the SYS3 dispatcher code.
4DC0H-4DDFH32TRACK_BUFGranule tracking area (20H bytes). Initialized to 00H. Bits are set to track freed granules during PURGE. Applied to GAT sector 1 at 4F43H.
4EA1H1DIR_ENTRY_POS [SMC]Low byte of the current directory entry's buffer position. Written at 4E36H/4EB3H, read back at 4EA0H/4FC2H.
4FB8H1MODE_FLAG [SMC]Operation mode flag. 00H=FREE (count only), 01H=PURGE (delete files). Written at 4DFCH/4E06H, read at 4E8DH/4EB9H/4EFFH/4FB7H.
5130H1KILL_LIMIT [SMC]End sector limit for KILL directory scan. Computed at 5090H as (DIR_MAX_SEC + 08H). The CP operand at 512FH.
5140H1KILL_OPTIONSKILL options flags. Bit 0: extension filter active. Bit 1: USR mode (include system files).
5141H-5143H3KILL_EXT_FILTERExtension filter buffer for KILL /ext option (3 ASCII characters).
5144H-515EH27KILL_PROMPTPrompt string: 10 leading spaces + "KILL IT? (Y/N/Q)" + 2 trailing spaces + 03H (ETX terminator).

Memory Map

Physical layout of SYS3 in memory from 4D00H through 51DFH. The overlay is divided into code regions (the dispatcher, function handlers, subroutines), data regions (prompt strings), and padding. Note that the bitmap buffer at 4D00H-4DDFH overwrites the dispatcher and early code during FREE/PURGE execution.

Address RangeSizeRegionDescription
4D00H-4D42H67DispatcherMain function dispatcher (7 primary functions) and E5H sub-function dispatcher (10 sub-functions via jump table at 4D1FH). Also: BITMAP_BUF — overwritten during FREE/PURGE.
4D43H-4D46H4Error ReturnInvalid function handler: loads error 2AH and returns.
4D47H-4D54H14SETHOOKInsert hook into chain at 4584H with DI/EI protection.
4D55H-4D6FH27RELHOOKRemove hook from chain by walking linked list.
4D70H-4D7AH11Display ConfigE5H/C=2: Call ROM 02A1H (configure) or 0298H (reset) based on N/Y parameter.
4D7BH-4D87H13TRACE ToggleE5H/C=10: Toggle bit 0 of TRACE_FLAG at 4019H.
4D88H-4D94H13SCREEN ToggleE5H/C=1: Toggle bit 0 of SCREEN_FLAG at 401CH.
4D95H-4DA1H13FORMS ToggleE5H/C=3: Toggle bit 7 of FORMS_FLAG at 4289H.
4DA2H-4DB7H22SKIP ConfigE5H/C=4: Patch self-modifying code at 4A2DH-4A2FH for SKIP=Y (DDH 7EH 01H) or SKIP=N (00H 3EH FFH).
4DB8H-4DCBH20DEBUG ConfigE5H/C=5: Patch debug hook at 4478H to C3H/4546H (enabled) or C9H (disabled).
4DCCH-4DE5H26N/Y ParserParse N (4EH) or Y (59H) from command line. Returns Z for N, NZ for Y.
4DE6H-4E01H28PURGE HandlerFunction 45H: Parse filespec, validate, set PURGE mode (01H at 4FB8H), enter scanning loop.
4E02H-4E9DH156FREE HandlerFunction 25H: Open directory, scan entries, count granules from extent tables, initialize and populate allocation bitmap at 4D00H-4DDFH.
4E9EH-4F0CH111Bitmap ProcessingProcess file extents into allocation bitmap. Extract granule numbers, calculate byte/bit positions, clear bits. Handle FREE vs PURGE mode differences.
4F0DH-4F59H77GAT VerificationRead disk GAT, AND with computed bitmap, write back merged result. Scan secondary workspace and apply freed granule data to GAT sector 1.
4F5AH-4FC1H104Filename DisplayBuild display string from directory entry (FILENAME/EXT:d), convert drive number to ASCII via division, output to screen.
4FC2H-4FE9H40Granule TrackingMark granule in secondary tracking area at 4DC0H-4DDFH. Calculates byte offset and bit position, sets the corresponding bit.
4FEAH-4FF4H11Directory OpenHelper routine to open directory file. Pushes return addresses E507H and 497CH, then jumps to 4A07H.
4FF5H-502CH56Position UpdateE5H/C=7: Validate file, check access mode, update EOF sector count and extent position in directory entry.
502DH-5039H13KILL AccessFunction C5H: Parse filespec, open directory, return error 25H if file not found.
503AH-5135H252KILL InteractiveE5H/C=6: Parse /ext and USR options, scan directory entries, display filenames, prompt Y/N/Q for each match, delete confirmed files via PURGE entry.
5136H-513FH10Display CharsSubroutine: display B characters from (HL), skipping spaces.
5140H-515EH31Data AreaOption flags byte (5140H), extension buffer (5141H-5143H), and prompt string " KILL IT? (Y/N/Q) " + 03H terminator.
515FH-518EH48DEBUG/DUMPFunction A5H: Dump memory to printer starting at 3C00H (video RAM). Replace non-printable chars with periods. Check BREAK key to continue.
518FH-51DFH81NOP Padding81 bytes of 00H (NOP) padding. Unused space filling the remainder of the overlay's disk allocation.

Major Routines

All routines within SYS3 (internal) and all external routines called by SYS3 (SYS0 and ROM). Internal routine addresses serve as hyperlink targets within the disassembly. External routines are listed for reference; their code resides in other overlays or ROM.

Internal Routines (within SYS3: 4D00H-51DFH)

AddressRoutineDescription
4D00HDISPATCHERMain function dispatcher. Routes function code in Register A to 7 primary handlers (25H/45H/65H/85H/A5H/C5H/E5H) via computed branch. E5H further routes to 10 sub-functions via C register and jump table at 4D1FH.
4D43HINVALID_FUNCError return for invalid function codes. Loads A with 2AH (general error) and returns with NZ.
4D47HSETHOOKFunction 65H: Insert a hook at the head of the chain at 4584H. Disables interrupts during insertion. DE = address of new hook entry. The first 2 bytes of the entry become the "next" pointer.
4D55HRELHOOKFunction 85H: Remove a hook from the chain at 4584H. DE = address of hook to remove. Walks the linked list to find the predecessor, then unlinks the target by copying its "next" pointer to the predecessor.
4D70HDISPLAY_CFGE5H/C=2: Configure display. Parses N/Y parameter; N calls ROM 02A1H, Y calls ROM 0298H.
4D7BHTRACE_TOGGLEE5H/C=10: Toggle TRACE mode. Parses N/Y; sets or clears bit 0 of 4019H.
4D88HSCREEN_TOGGLEE5H/C=1: Toggle SCREEN mode. Parses N/Y; sets or clears bit 0 of 401CH.
4D95HFORMS_TOGGLEE5H/C=3: Toggle FORMS mode. Parses N/Y; sets or clears bit 7 of 4289H.
4DA2HSKIP_CONFIGE5H/C=4: Configure SKIP mode. Parses N/Y; patches 3 bytes of self-modifying code at 4A2DH-4A2FH.
4DB8HDEBUG_CONFIGE5H/C=5: Configure DEBUG hook. Parses N/Y; patches 4478H to C3H+addr (JP enabled) or C9H (RET disabled).
4DCCHPARSE_NYParse N/Y parameter from command line. Skips whitespace, reads character. Returns Z flag for N (4EH), NZ for Y (59H). Error if neither.
4DE6HPURGE_HANDLERFunction 45H: Parse filespec, validate wildcards, set mode 01H, enter directory scan loop.
4DF4HPURGE_CONTINUEPURGE loop continuation. Re-parses filespec, re-initializes BC=0, sets mode 01H. Also called by KILL at 5119H to perform actual file deletion.
4E02HFREE_HANDLERFunction 25H: Set mode 00H, open directory, read sectors, scan entries for active files (status AND 90H = 10H), count granules from extent tables, build bitmap, verify GAT.
4E9EHBITMAP_PROCESSWalk extent table entries and clear corresponding bits in allocation bitmap at 4D00H. Extract granule number from extent bytes, divide by sectors-per-granule (IY+05H), compute byte offset and bit mask.
4E59HDISK_POS_OPPerform disk seek/position operation. Loads A from D, calls 48D4H, checks status.
4E5FHFLUSH_DIRWrite directory sector buffer to disk via 48C4H, then check status and continue.
4F0DHGAT_VERIFYRead disk GAT (sector 0), AND each byte with bitmap, write back. Then scan secondary workspace (4DC0H-4DDFH) and apply freed granule bits to GAT sector 1.
4F5AHFILENAME_DISPLAYBuild filename string (up to 8 chars + /ext + :d) from directory entry, convert drive number to ASCII, display on screen. Checks bit 2 of 428BH to suppress output.
4FC2HMARK_GRANULESet a bit in the secondary tracking area (4DC0H-4DDFH) for a freed granule. Used during PURGE. Also clears bit 4 of entry status byte (deactivates file).
4FEAHOPEN_DIRHelper to open directory file. Pushes return addresses E507H and 497CH onto stack, then jumps to 4A07H.
4FF5HPOS_UPDATEE5H/C=7: Update file position/EOF markers. Validates FCB, checks access mode < 5, updates EOF sector count and extent position fields in directory entry, writes back if changed.
502DHKILL_ACCESSFunction C5H: Parse filespec, open directory. Return success (A=00H) if found, error 25H if not.
503AHKILL_INTERACTIVEE5H/C=6: Interactive KILL. Parses /ext filter and USR keyword. Scans directory, checks active files (AND 90H = 10H), filters by system flags and extension, displays "FILENAME/EXT KILL IT? (Y/N/Q)", processes Y (delete via 4DF4H), N (skip), Q (quit).
5136HDISPLAY_CHARSDisplay B characters from buffer at (HL), skipping spaces (20H). Calls ROM 0033H for each non-space character.
515FHDEBUG_DUMPFunction A5H: Memory dump to printer. Starts at 3C00H (video RAM), outputs via ROM 003BH. Replaces bytes < 20H or > DUMP_LIMIT (4290H) with periods. Checks BREAK key (3840H bit 2). Stops at 16K boundary.

External Routines Called by SYS3

AddressModuleRoutineDescription
0033HROMDISP_CHARDisplay character in A at cursor position and advance cursor.
003BHROMPRINT_CHARSend character in A to the printer (port F8H).
0049HROMSCAN_KBDScan keyboard, return character in A (0 if no key pressed).
0298HROMDISP_RESETReset display configuration (called when parameter = Y).
02A1HROMDISP_CONFIGConfigure display mode (called when parameter = N).
4409HSYS0DOS_ERRORDOS error handler.
4467HSYS0PRINT_STRINGDisplay string pointed to by HL (ETX-terminated).
4791HSYS0SETUP_DIRSet up drive and directory parameters for file searching.
48AFHSYS0READ_SECTORRead a disk sector. A = sector number. Returns HL pointing to buffer.
48C4HSYS0WRITE_DIR_SECWrite directory sector buffer back to disk (flush changes).
48D4HSYS0DISK_POSPerform disk seek/position operation with value in A.
48DBHSYS0READ_DIR_SECRead directory sector. C = sector number. Returns HL pointing to entry.
48F0HSYS0VALIDATE_FCBValidate FCB/filespec. Check wildcards and file state.
4925HSYS0PARSE_FSPECParse filespec from command line. Sets IX to FCB, HL to command position.
495EHSYS0SEARCH_FILESSearch for matching files and prepare for iteration through directory entries.
4972HSYS0NEXT_DIR_ENTRYAdvance to the next directory entry during scanning.
4A07HSYS0OPEN_DIR_FILEPerform the directory file open operation.
4C57HSYS0READ_DIR_BUFRead a directory sector into the buffer.
4C7AHSYS0SKIP_SPACESkip whitespace and delimiters in command buffer. Z = end of line.

Disassembly

4D00H - SYS3 Main Function Dispatcher

SYS3 is the third overlay module of NEWDOS/80 v2.0 for the TRS-80 Model III. It is loaded into RAM at 4D00H when any of its functions are invoked via RST 28H. Register A contains the function code on entry. SYS3 handles: FREE (25H), PURGE (45H), SETHOOK (65H), RELHOOK (85H), DEBUG/DUMP (A5H), KILL (C5H), and system configuration sub-functions (E5H with sub-function number in Register C).

[FUNCTION DISPATCHER] On entry, Register A contains the SVC function code. This dispatcher compares against each valid function code and routes to the appropriate handler. The function codes all have low 5 bits = 00101 (binary), identifying SYS3 as directory sector 7, function 0. The upper 3 bits encode the specific function within SYS3.

4D00
CP 25H FE 25
Compare Register A (the function code) against 25H (the FREE command function code). If Register A equals 25H, the Z FLAG is set; otherwise the NZ FLAG is set.
4D02
If the Z FLAG has been set (function code = 25H), JUMP to 4E02H to handle the FREE command — calculate and display free disk space.
4D05
CP 45H FE 45
Compare Register A against 45H (the PURGE command function code). If Register A equals 45H, the Z FLAG is set; otherwise the NZ FLAG is set.
4D07
If the Z FLAG has been set (function code = 45H), JUMP to 4DE6H to handle the PURGE command — delete files matching a filespec.
4D0A
CP 65H FE 65
Compare Register A against 65H (the SETHOOK function code). If Register A equals 65H, the Z FLAG is set; otherwise the NZ FLAG is set.
4D0C
If the Z FLAG has been set (function code = 65H), JUMP to 4D47H to handle SETHOOK — insert a new hook into the system hook chain.
4D0E
CP 85H FE 85
Compare Register A against 85H (the RELHOOK function code). If Register A equals 85H, the Z FLAG is set; otherwise the NZ FLAG is set.
4D10
If the Z FLAG has been set (function code = 85H), JUMP to 4D55H to handle RELHOOK — remove a hook from the system hook chain.
4D12
CP A5H FE A5
Compare Register A against A5H (the DEBUG/DUMP function code). If Register A equals A5H, the Z FLAG is set; otherwise the NZ FLAG is set.
4D14
If the Z FLAG has been set (function code = A5H), JUMP to 515FH to handle DEBUG/DUMP — display memory contents on screen.
4D17
CP C5H FE C5
Compare Register A against C5H (the KILL command function code). If Register A equals C5H, the Z FLAG is set; otherwise the NZ FLAG is set.
4D19
If the Z FLAG has been set (function code = C5H), JUMP to 502DH to handle the KILL command — check file access validity.
4D1C
CP E5H FE E5
Compare Register A against E5H (the system configuration function code). If Register A equals E5H, the Z FLAG is set; otherwise the NZ FLAG is set.
4D1E
If the NZ FLAG has been set (function code does not match any valid SYS3 function), JUMP to 4D43H to return error code 2AH (general/invalid function error).

[E5H SUB-FUNCTION DISPATCHER] Function code E5H has multiple sub-functions. Register C contains the sub-function number (1 through 10). Each DEC C instruction tests for the next sub-function number by counting down from the value in C. When C reaches zero, the Z FLAG is set and execution branches to the matching handler.

4D20
DEC C 0D
DECrement Register C (the sub-function number) by 1. If C was 1, it becomes 0 and the Z FLAG is set (sub-function 1 = SCREEN mode toggle).
4D21
If the Z FLAG has been set (C was 1), JUMP to 4D88H to toggle the SCREEN mode flag at 401CH.
4D24
DEC C 0D
DECrement Register C by 1. If C was 2, it becomes 0 and the Z FLAG is set (sub-function 2 = display configuration).
4D25
If the Z FLAG has been set (C was 2), JUMP to 4D70H to handle display configuration (calls ROM video routines).
4D27
DEC C 0D
DECrement Register C by 1. If C was 3, it becomes 0 and the Z FLAG is set (sub-function 3 = FORMS mode toggle).
4D28
If the Z FLAG has been set (C was 3), JUMP to 4D95H to toggle the FORMS mode flag (bit 7 of 4289H).
4D2B
DEC C 0D
DECrement Register C by 1. If C was 4, it becomes 0 and the Z FLAG is set (sub-function 4 = SKIP mode configuration).
4D2C
If the Z FLAG has been set (C was 4), JUMP to 4DA2H to configure SKIP mode (self-modifying code at 4A2DH-4A2FH).
4D2F
DEC C 0D
DECrement Register C by 1. If C was 5, it becomes 0 and the Z FLAG is set (sub-function 5 = DEBUG hook enable/disable).
4D30
If the Z FLAG has been set (C was 5), JUMP to 4DB8H to enable or disable the DEBUG hook (writes JP or RET at 4478H).
4D33
DEC C 0D
DECrement Register C by 1. If C was 6, it becomes 0 and the Z FLAG is set (sub-function 6 = KILL interactive file deletion).
4D34
If the Z FLAG has been set (C was 6), JUMP to 503AH to handle KILL — interactive file deletion with confirmation prompts.
4D37
DEC C 0D
DECrement Register C by 1. If C was 7, it becomes 0 and the Z FLAG is set (sub-function 7 = file position/EOF update).
4D38
If the Z FLAG has been set (C was 7), JUMP to 4FF5H to handle file position and EOF marker update.
4D3B
DEC C 0D
DECrement Register C by 1. If C was 8, it becomes 0 and the Z FLAG is set (sub-function 8 = invalid/error).
4D3C
If the Z FLAG has been set (C was 8), JUMP to 4D43H to return error code 2AH — sub-function 8 is not valid.
4D3F
DEC C 0D
DECrement Register C by 1. If C was 9, it becomes 0 and the Z FLAG is set (sub-function 9 is not valid, falls through).
4D40
If the Z FLAG has been set (C was 9 — actually sub-function 10 after the DEC), JUMP to 4D7BH to toggle the TRACE mode flag (bit 0 of 4019H).

4D43H - Invalid Function Error Return

Returns error code 2AH (general error) to the caller when an invalid or unrecognized function code is provided to SYS3. Sets the NZ FLAG to signal error.

4D43
LD A,2AH 3E 2A
Load Register A with 2AH — error code 2AH (general error / invalid function).
4D45
OR A B7
OR Register A with itself. Since A = 2AH (non-zero), this sets the NZ FLAG to signal an error condition to the caller.
4D46
RET C9
RETURN to caller with A = 2AH (error code) and NZ FLAG set (error).

4D47H - SETHOOK: Insert Hook Into System Hook Chain (Function 65H)

Inserts a new hook entry into the system hook chain. The hook chain is a singly-linked list anchored at 4584H (a 2-byte pointer to the head of the chain). On entry, Register Pair DE contains the address of the new hook entry to insert. The hook entry structure is: bytes 0-1 hold the "next" link pointer, which will be filled in by this routine. The new hook is inserted at the head of the chain. Interrupts are disabled during the modification to prevent corruption of the linked list.

4D47
DI F3
Disable Interrupts to prevent any interrupt handler from traversing the hook chain while it is being modified.
4D48
LD HL,(4584H) 2A 84 45
Load Register Pair HL with the current head-of-chain pointer from 4584H (the hook chain anchor). HL now points to the first hook entry in the chain (or 0000H if chain is empty).
4D4B
LD (4584H),DE ED 53 84 45
Store Register Pair DE (the address of the new hook entry) into 4584H, making the new entry the head of the hook chain.
4D4F
EX DE,HL EB
Exchange DE and HL. Now HL = address of new hook entry, DE = old head-of-chain pointer (the entry that was previously first).
4D50
LD (HL),E 73
Store the low byte of the old head pointer (Register E) into byte 0 of the new hook entry. This sets the "next" link of the new entry to point to the old first entry.
4D51
INC HL 23
INCrement HL to point to byte 1 of the new hook entry.
4D52
LD (HL),D 72
Store the high byte of the old head pointer (Register D) into byte 1 of the new hook entry. The new entry's "next" link now points to the previous head of chain.
4D53
EI FB
Enable Interrupts — the chain modification is complete and the list is consistent again.
4D54
RET C9
RETURN to caller. The hook chain now has the new entry at its head.

4D55H - RELHOOK: Remove Hook From System Hook Chain (Function 85H)

Removes a specific hook entry from the system hook chain. On entry, Register Pair DE contains the address of the hook entry to remove. The routine walks the linked list starting from the anchor at 4584H, comparing each entry's address against DE. When found, it unlinks the entry by updating the previous entry's "next" pointer to skip over the removed entry. Interrupts are disabled during list traversal and modification.

4D55
LD B,D 42
Copy the high byte of the target hook address from Register D to Register B. Register Pair BC will hold the address to search for.
4D56
LD C,E 4B
Copy the low byte of the target hook address from Register E to Register C. Now BC = address of the hook entry to remove.
4D57
LD HL,4584H 21 84 45
Point Register Pair HL to 4584H — the hook chain anchor (contains the pointer to the first hook entry in the chain).
4D5A
DI F3
Disable Interrupts to prevent corruption while traversing and modifying the linked list.

[LOOP START - Walk hook chain] This loop traverses the linked list. HL points to the "next" pointer field that will be updated if the current entry matches. DE receives the address of the current entry being examined.

4D5B
LD E,(HL) 5E
Load the low byte of the current entry's address from the location pointed to by HL into Register E.
4D5C
INC HL 23
INCrement HL to point to the high byte of the current entry's address.
4D5D
LD D,(HL) 56
Load the high byte of the current entry's address into Register D. Now DE = address of the current hook entry.
4D5E
EX DE,HL EB
Exchange DE and HL. Now HL = current hook entry address, DE = pointer to the "next" field that was just read.
4D5F
LD A,H 7C
Load Register A with the high byte of the current entry address (Register H).
4D60
OR L B5
OR Register A with the low byte (Register L). If HL = 0000H (end of chain), the Z FLAG is set.
4D61
If the Z FLAG has been set (reached end of chain without finding the target), JUMP to 4D53H to re-enable interrupts and return. The hook was not found in the chain.
4D63
OR A B7
Clear the CARRY FLAG (OR A always clears carry) to prepare for the SBC comparison below.
4D64
SBC HL,BC ED 42
SUBtract Register Pair BC (the target address to find) from HL (the current entry address) with borrow. If HL = BC, the result is zero and the Z FLAG is set.
4D66
ADD HL,BC 09
ADD BC back to HL to restore the original value of HL (the current entry address). This undoes the SBC without affecting the Z FLAG from the comparison.
4D67
If the NZ FLAG has been set (current entry does not match target), JUMP back to 4D5BH to continue walking the chain. HL now points to the current entry, and its "next" field will be read next iteration.

[MATCH FOUND - Unlink entry] The current entry (at HL) matches the target (BC). DE points to the "next" pointer field in the previous entry (or the anchor at 4584H). We need to copy the current entry's "next" pointer into the previous entry's "next" pointer, effectively unlinking the current entry from the chain.

4D69
DEC DE 1B
DECrement DE to point back to the low byte of the previous entry's "next" field (DE was left pointing one byte past it after the earlier INC HL / EX DE,HL sequence).
4D6A
LD A,(HL) 7E
Load Register A with the low byte of the current (matched) entry's "next" pointer — this is the entry that will follow after removal.
4D6B
INC HL 23
INCrement HL to point to the high byte of the current entry's "next" pointer.
4D6C
LD H,(HL) 66
Load Register H with the high byte of the current entry's "next" pointer.
4D6D
LD L,A 6F
Load Register L with the low byte (from Register A). Now HL = the "next" pointer from the removed entry (the entry that should follow the previous entry).
4D6E
JUMP to 4D4FH which will exchange DE/HL and store the new "next" pointer into the previous entry's link field, then re-enable interrupts and return. This completes the unlinking operation.

[LOOP END - Walk hook chain]

4D70H - Display Configuration Handler (E5H Sub-function 2)

Handles the display configuration sub-function. Parses an N/Y parameter from the command line, and if Y was specified, calls ROM routine 02A1H to configure the display. If N was specified, calls ROM routine 0298H to reset the display. Returns with A=00H and Z FLAG set (success).

4D70
GOSUB to 4DCCH to parse the N/Y parameter from the command line. On return, Z FLAG is set if N was specified, NZ FLAG is set if Y was specified.
4D73
If the Z FLAG has been set (N was specified — enable the feature), JUMP to ROM routine at 02A1H to configure the display output. Execution returns directly from the ROM routine.
4D76
GOSUB to ROM routine at 0298H to reset/disable the display configuration (Y was specified — disable the feature).
4D79
XOR A AF
XOR Register A with itself, setting A to 00H and the Z FLAG (success — no error).
4D7A
RET C9
RETURN to caller with A = 00H and Z FLAG set (success).

4D7BH - TRACE Mode Toggle (E5H Sub-function 10)

Toggles the TRACE mode flag at memory location 4019H bit 0. When TRACE is enabled (bit 0 = 1), execution tracing is active. Parses N/Y from the command line: N enables TRACE (sets bit 0), Y disables TRACE (clears bit 0).

4D7B
GOSUB to 4DCCH to parse the N/Y parameter from the command line. On return, Z FLAG set = N, NZ FLAG set = Y.
4D7E
LD HL,4019H 21 19 40
Point Register Pair HL to 4019H — the system flags byte containing the TRACE mode flag in bit 0.
4D81
SET 0,(HL) CB C6
SET bit 0 of the byte at (HL), enabling TRACE mode. This is done unconditionally first; if Y was specified, it will be cleared below.
4D83
RET Z C8
If the Z FLAG has been set (N was specified), RETURN immediately. TRACE mode is now enabled (bit 0 = 1).
4D84
RES 0,(HL) CB 86
RESset bit 0 of the byte at (HL), disabling TRACE mode (Y was specified — disable).
4D86
XOR A AF
XOR Register A with itself, setting A to 00H and the Z FLAG (success — no error).
4D87
RET C9
RETURN to caller with A = 00H and Z FLAG set (success). TRACE mode is now disabled.

4D88H - SCREEN Mode Toggle (E5H Sub-function 1)

Toggles the SCREEN mode flag at memory location 401CH bit 0. When SCREEN mode is enabled (bit 0 = 1), video display output is active. Parses N/Y from the command line: N enables SCREEN output (sets bit 0), Y disables SCREEN output (clears bit 0).

4D88
GOSUB to 4DCCH to parse the N/Y parameter from the command line. On return, Z FLAG set = N, NZ FLAG set = Y.
4D8B
LD HL,401CH 21 1C 40
Point Register Pair HL to 401CH — the system flags byte containing the SCREEN mode flag in bit 0.
4D8E
SET 0,(HL) CB C6
SET bit 0 of the byte at (HL), enabling SCREEN output. Done unconditionally first.
4D90
RET Z C8
If the Z FLAG has been set (N was specified), RETURN immediately. SCREEN output is now enabled (bit 0 = 1).
4D91
RES 0,(HL) CB 86
RESset bit 0 of the byte at (HL), disabling SCREEN output (Y was specified — disable).
4D93
XOR A AF
XOR Register A with itself, setting A to 00H and the Z FLAG (success — no error).
4D94
RET C9
RETURN to caller with A = 00H and Z FLAG set (success). SCREEN output is now disabled.

4D95H - FORMS Mode Toggle (E5H Sub-function 3)

Toggles the FORMS mode flag at memory location 4289H bit 7. When FORMS mode is enabled (bit 7 = 0), printer form-feed formatting is active. Parses N/Y: N enables FORMS (clears bit 7), Y disables FORMS (sets bit 7).

4D95
GOSUB to 4DCCH to parse the N/Y parameter from the command line. On return, Z FLAG set = N, NZ FLAG set = Y.
4D98
LD HL,4289H 21 89 42
Point Register Pair HL to 4289H — the system flags byte containing the FORMS mode flag in bit 7.
4D9B
RES 7,(HL) CB BE
RESset bit 7 of the byte at (HL), enabling FORMS mode (bit 7 = 0 means forms enabled). Done unconditionally first.
4D9D
RET Z C8
If the Z FLAG has been set (N was specified), RETURN immediately. FORMS mode is now enabled (bit 7 = 0).
4D9E
SET 7,(HL) CB FE
SET bit 7 of the byte at (HL), disabling FORMS mode (Y was specified — disable forms).
4DA0
XOR A AF
XOR Register A with itself, setting A to 00H and the Z FLAG (success).
4DA1
RET C9
RETURN to caller with A = 00H and Z FLAG set (success). FORMS mode is now disabled.

4DA2H - SKIP Mode Configuration (E5H Sub-function 4)

Configures the SKIP mode by writing self-modifying code at 4A2DH-4A2FH. When enabled (N), writes DDH at 4A2DH and 017EH at 4A2EH-4A2FH, which forms the instruction LD A,(IX+7EH). When disabled (Y), writes 00H at 4A2DH and FF3EH at 4A2EH-4A2FH, which forms the instruction NOP followed by LD A,FFH. [SELF-MODIFYING CODE]

4DA2
GOSUB to 4DCCH to parse the N/Y parameter from the command line. On return, Z FLAG set = N, NZ FLAG set = Y.
4DA5
LD A,DDH 3E DD
Load Register A with DDH — the IX prefix opcode byte, used when SKIP mode is enabled.
4DA7
LD HL,017EH 21 7E 01
Load Register Pair HL with 017EH — the operand bytes for LD A,(IX+7EH) when SKIP mode is enabled. Combined with DDH prefix, this forms the complete instruction.
4DAA
If the Z FLAG has been set (N was specified — enable SKIP), JUMP to 4DB0H to write the DDH/017EH values.
4DAC
XOR A AF
XOR Register A with itself, setting A to 00H — a NOP instruction byte, used when SKIP mode is disabled.
4DAD
LD HL,FF3EH 21 3E FF
Load Register Pair HL with FF3EH — the bytes for LD A,FFH when SKIP mode is disabled. Combined with the 00H (NOP), this forms NOP + LD A,FFH.
4DB0
LD (4A2DH),A 32 2D 4A
Store Register A into 4A2DH — write the first byte of the self-modifying code (DDH or 00H). [SELF-MODIFYING CODE]
4DB3
LD (4A2EH),HL 22 2E 4A
Store Register Pair HL into 4A2EH-4A2FH — write the remaining bytes of the self-modifying code (7E 01H or 3E FFH). [SELF-MODIFYING CODE]
4DB6
XOR A AF
XOR Register A with itself, setting A to 00H and the Z FLAG (success).
4DB7
RET C9
RETURN to caller with A = 00H and Z FLAG set (success). SKIP mode is now configured.

4DB8H - DEBUG Hook Enable/Disable (E5H Sub-function 5)

Enables or disables the DEBUG hook by writing self-modifying code at 4478H. When enabled (N), writes JP 4546H (C3H followed by address 4546H) at 4478H, causing the debug hook to be active. When disabled (Y), writes RET (C9H) at 4478H, causing the debug hook to immediately return without doing anything. [SELF-MODIFYING CODE]

4DB8
GOSUB to 4DCCH to parse the N/Y parameter from the command line. On return, Z FLAG set = N, NZ FLAG set = Y.
4DBB
LD HL,4546H 21 46 45
Load Register Pair HL with 4546H — the address of the DEBUG handler routine, used as the JP target when debug is enabled.
4DBE
LD (4479H),HL 22 79 44
Store Register Pair HL (4546H) into 4479H-447AH — the JP operand address at 4478H+1 and 4478H+2. This sets the jump target regardless of enable/disable. [SELF-MODIFYING CODE]
4DC1
LD A,C3H 3E C3
Load Register A with C3H — the opcode for JP instruction, used when DEBUG hook is enabled.
4DC3
If the Z FLAG has been set (N was specified — enable DEBUG), JUMP to 4DC7H to write the C3H (JP) opcode.
4DC5
LD A,C9H 3E C9
Load Register A with C9H — the opcode for RET instruction, used when DEBUG hook is disabled (Y was specified).
4DC7
LD (4478H),A 32 78 44
Store Register A into 4478H — write either C3H (JP, debug enabled) or C9H (RET, debug disabled) as the first byte of the hook routine. [SELF-MODIFYING CODE]
4DCA
XOR A AF
XOR Register A with itself, setting A to 00H and the Z FLAG (success).
4DCB
RET C9
RETURN to caller with A = 00H and Z FLAG set (success). DEBUG hook is now configured.

4DCCH - Parse N/Y Parameter From Command Line

Parses a single character (N or Y) from the command line pointed to by HL. First calls the inner parser at 4DD1H, then if neither N nor Y was found, skips whitespace and checks for end-of-line. Returns Z FLAG for N, NZ FLAG for Y. If the character is not N, Y, or end-of-line, jumps to the error handler at 4409H.

4DCC
GOSUB to 4DD1H to attempt to parse N or Y from the current command line position.
4DCF
JUMP to 4DDDH to skip whitespace and verify end-of-line (handles the case where the call to 4DD1H did not find N or Y at the initial position).

4DD1H - Inner N/Y Character Parser

Reads a single character from (HL), subtracts 4EH (ASCII N), and checks if the result is 00H (N) or 0BH (Y, since 59H - 4EH = 0BH). On match, advances HL past the character and returns with B holding the result (0 for N, 0BH for Y). On mismatch, restores HL and falls through to whitespace/end-of-line checking.

4DD1
LD A,(HL) 7E
Fetch the current character from the command line buffer pointed to by HL into Register A.
4DD2
SUB 4EH D6 4E
SUBtract 4EH (ASCII N) from Register A. If the character was N (4EH), A becomes 00H and the Z FLAG is set. If the character was Y (59H), A becomes 0BH.
4DD4
LD B,A 47
Save the result into Register B (00H if N, 0BH if Y, or other value if neither).
4DD5
INC HL 23
INCrement HL to advance past the character that was just read.
4DD6
If the Z FLAG has been set (character was N), JUMP to 4DE3H to return with B = 00H.
4DD8
CP 0BH FE 0B
Compare Register A (the subtraction result) against 0BH. If A = 0BH (original character was Y, since 59H - 4EH = 0BH), the Z FLAG is set.
4DDA
If the Z FLAG has been set (character was Y), JUMP to 4DE3H to return with B = 0BH (non-zero = Y).
4DDC
DEC HL 2B
DECrement HL to restore the pointer to the character that was not N or Y (undo the INC HL at 4DD5H).
4DDD
GOSUB to SYS0 routine at 4C7AH to skip whitespace and delimiters in the command line buffer. Returns Z FLAG set if end-of-line (0DH carriage return) is reached, NZ if a non-whitespace character remains.
4DE0
If the NZ FLAG has been set (unexpected characters remain on the command line — syntax error), JUMP to 4409H — the DOS error handler — to report a parse error.
4DE3
LD A,B 78
Load Register A with the saved result from Register B (00H for N, 0BH for Y).
4DE4
OR A B7
OR Register A with itself. Sets the Z FLAG if A = 00H (N was specified), sets the NZ FLAG if A = 0BH (Y was specified).
4DE5
RET C9
RETURN to caller. Z FLAG indicates N was specified, NZ FLAG indicates Y was specified.

4DE6H - PURGE Command Handler (Function 45H)

The PURGE command deletes files matching a filespec. It first parses the filespec, validates it for wildcards, sets up the PURGE operation mode, and then enters the directory scanning loop shared with the FREE command.

4DE6
GOSUB to SYS0 routine at 4925H to parse the filespec from the command line. Sets up IX to point to the FCB and HL to the command line position.
4DE9
GOSUB to SYS0 routine at 48F0H to validate the parsed filespec and check for wildcards.
4DEC
RET NZ C0
If the NZ FLAG has been set (filespec validation failed), RETURN immediately with the error code in Register A.
4DED
LD B,02H 06 02
Load Register B with 02H — a flag indicating PURGE mode (delete operation) passed to the file matching routine.
4DEF
GOSUB to SYS0 routine at 495EH to search for matching files and prepare for iteration through directory entries.
4DF2
JUMP to 4DF7H to begin the PURGE directory scanning loop.

4DF4H - PURGE Loop Continuation Entry

Re-entry point for the PURGE loop when continuing to delete additional files. The filespec is re-parsed because the directory may have changed.

4DF4
GOSUB to SYS0 routine at 4925H to re-parse the filespec from the command line.
4DF7
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H — initialize the granule counter to zero.
4DFA
LD A,01H 3E 01
Load Register A with 01H — PURGE mode flag (01H = PURGE active, 00H = FREE mode).
4DFC
LD (4FB8H),A 32 B8 4F
Store Register A (01H) into 4FB8H — the operation mode flag checked throughout the FREE/PURGE code.
4DFF
PUSH BC C5
Save Register Pair BC (zero granule counter) onto the stack.
4E00
JUMP to 4E27H to begin directory scanning.

4E02H - FREE Command Handler (Function 25H)

The FREE command calculates and displays free space on a disk. It opens the directory, scans all entries, counts allocated granules by examining extent tables, builds a granule allocation bitmap at 4D00H-4DBFH (overwriting SYS3's own dispatcher code which has already executed), and reports remaining free space. The bitmap is also used to verify and optionally repair the Granule Allocation Table (GAT).

4E02
GOSUB to SYS0 routine at 4925H to parse the drive specification from the command line.
4E05
XOR A AF
XOR Register A with itself, setting A to 00H — the FREE mode flag.
4E06
LD (4FB8H),A 32 B8 4F
Store 00H into 4FB8H — set the operation mode flag to FREE (count only, no deletion).
4E09
GOSUB to 4FEAH to open the directory file for reading. Returns CARRY set if directory opened successfully with entries, NO CARRY if empty or error.
4E0C
If the NO CARRY FLAG has been set (directory open failed or is empty), JUMP to 4F5AH to display results with zero files.
4E0F
BIT 6,(IX+02H) DD CB 02 76
Test bit 6 of FCB offset 02H (IX+02H). Bit 6 = alternate position valid flag. Checks if the directory file position was properly established.
4E13
If the NZ FLAG has been set (bit 6 set — alternate position active), JUMP to 4F5AH to display results.
4E16
PUSH HL E5
Save Register Pair HL (command line pointer) onto the stack.
4E17
LD A,(IX+08H) DD 7E 08
Load Register A with FCB offset 08H — the alternate position word high byte, indicating the directory sector position.
4E1A
OR A B7
OR Register A with itself to test if the alternate position high byte is zero.
4E1B
If the Z FLAG has been set (position high byte is zero — start of directory), JUMP to 4E1EH to skip the NOP.
4E1D
NOP 00
No operation — placeholder, skipped when alternate position is zero.
4E1E
EX DE,HL EB
Exchange DE and HL. HL now points to the directory sector buffer, DE holds the former HL.
4E1F
GOSUB to SYS0 routine at 4C57H to read a directory sector into the buffer.
4E22
OR A B7
OR Register A with itself to test the return status. Z FLAG set = success.
4E23
If the Z FLAG has been set (read succeeded), JUMP to 4E26H.
4E25
INC HL 23
INCrement HL — adjust pointer past error condition.
4E26
EX (SP),HL E3
Exchange HL with the top of stack. Swaps the command line pointer with the directory buffer pointer so both are available.

[MAIN DIRECTORY SCANNING LOOP] Processes each directory entry. D = position tracking (from IX+07H). BC = accumulated granule count. E = comparison mask (10H = active file). HL = current entry in buffer.

4E27
LD D,(IX+07H) DD 56 07
Load Register D with FCB offset 07H — the current position byte 2 (alternate sector offset), tracking which directory entry is being processed.
4E2A
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H — initialize the per-entry granule counter.
4E2D
LD E,10H 1E 10
Load Register E with 10H — mask for testing the directory entry status. AND 90H = 10H means bit 4 set, bit 7 clear = active file.

[LOOP START - Scan directory entries]

4E2F
LD A,(HL) 7E
Fetch the status byte of the current directory entry from (HL) into Register A.
4E30
AND 90H E6 90
AND Register A with 90H to isolate bits 7 and 4. Active file = 10H.
4E32
CP E BB
Compare Register A against Register E (10H). Z FLAG set = active file entry.
4E33
If the NZ FLAG has been set (not an active file entry), JUMP to 4E52H to skip to the next entry.

[ACTIVE FILE FOUND] Save the entry's buffer position at 4EA1H, advance to the extent table at offset +16H, and count allocated granules.

4E35
LD A,L 7D
Load Register A with the low byte of the directory entry address in the buffer.
4E36
LD (4EA1H),A 32 A1 4E
Store the entry's buffer position into 4EA1H. [SELF-MODIFYING CODE] This value is read back at 4EA0H and 4FC2H.
4E39
ADD 16H C6 16
ADD 16H (22 decimal) to Register A — advance to offset 16H, the start of the extent table (granule allocation records).
4E3B
LD L,A 6F
Load Register L with the result, pointing HL to the extent table.
4E3C
PUSH DE D5
Save Register Pair DE (D = position tracking, E = 10H mask) onto the stack.
4E3D
LD E,05H 1E 05
Load Register E with 05H — the extent table has up to 5 entries per directory entry.

[LOOP START - Count granules in extents] Each extent = 2 bytes. First byte's high 3 bits + second byte = starting granule. First byte's low 5 bits = granule count - 1. Byte >= FEH = end of extents.

4E3F
LD A,(HL) 7E
Fetch the first byte of the current extent entry.
4E40
CP FEH FE FE
Compare against FEH. If A >= FEH (FEH = end-of-file, FFH = unused), CARRY is clear.
4E42
INC HL 23
INCrement HL to the second byte of the extent entry.
4E43
If the NO CARRY FLAG has been set (extent byte >= FEH), JUMP to 4E64H for end-of-extents processing.
4E45
LD A,(HL) 7E
Fetch the second byte of the extent entry.
4E46
AND 1FH E6 1F
AND with 1FH to isolate the low 5 bits — the granule count minus 1.
4E48
INC HL 23
INCrement HL to advance to the next extent entry.
4E49
INC A 3C
INCrement A to convert from "count - 1" to actual granule count.
4E4A
ADD A,C 81
ADD the granule count to the low byte of the running total (Register C).
4E4B
LD C,A 4F
Store the updated low byte into Register C.
4E4C
If the NO CARRY FLAG has been set (no overflow), JUMP to 4E4FH.
4E4E
INC B 04
INCrement Register B (high byte) to account for carry from low byte addition.
4E4F
DEC E 1D
DECrement the extent counter (Register E).
4E50
If the NZ FLAG has been set (more extents remain), JUMP back to 4E3FH.

[LOOP END - Count granules in extents]

4E52
LD A,2CH 3E 2C
Load Register A with 2CH — error code 2CH (directory full / end of directory). Signals end of scanning.
4E54
OR A B7
OR Register A with itself. A = 2CH (non-zero) sets the NZ FLAG.
4E55
RET Z C8
RET if Z — defensive check; Z is never set here (A = 2CH).
4E56
JUMP to SYS0 routine at 4972H to advance to the next directory entry and continue scanning.
4E59
LD A,D 7A
Load Register A with the position tracking value from Register D.
4E5A
GOSUB to SYS0 routine at 48D4H to perform a disk seek/position operation.
4E5D
JUMP to 4E55H to check status and continue.
4E5F
GOSUB to SYS0 routine at 48C4H to write the directory sector buffer back to disk (flush changes).
4E62
JUMP to 4E55H to check status and continue.

[END-OF-EXTENTS HANDLER] Reached when extent byte >= FEH. FEH = end-of-file marker; FFH = unused extent slot.

4E64
If NZ (extent byte = FFH, unused slot), JUMP to 4E72H for end-of-entry processing.
4E66
DEC E 1D
DECrement the extent counter E.
4E67
POP DE D1
Restore DE (D = position tracking, E = mask) from the stack.
4E68
If NZ (more extents existed but FEH found), JUMP to 4E52H to advance to next entry.
4E6A
LD D,(HL) 56
Load Register D with the byte at (HL) — sectors used in the last granule, following the FEH marker.
4E6B
LD E,90H 1E 90
Load Register E with 90H — flag for end-of-file granule operation.
4E6D
GOSUB to 4E59H to perform disk operation with last granule position.
4E70
JUMP back to 4E2FH to continue scanning next directory entry.
4E72
POP DE D1
Restore DE from stack.
4E73
EX (SP),HL E3
Exchange HL with top of stack — swap directory buffer pointer with saved command line pointer.
4E74
XOR A AF
Set A to 00H and clear CARRY.
4E75
SBC HL,BC ED 42
SUBtract the accumulated granule count (BC) from HL for the free space calculation.
4E77
LD (4288H),A 32 88 42
Store 00H into 4288H — clear a system flag byte.
4E7A
PUSH HL E5
Save the free space value onto the stack.

[INITIALIZE ALLOCATION BITMAP] Fill 192 bytes (C0H) at 4D00H-4DBFH with FFH (all granules marked "allocated"), then fill 32 bytes (20H) at 4DC0H-4DDFH with 00H (workspace). This overwrites SYS3's own dispatcher code, which has already executed.

4E7E
LD HL,4D00H 21 00 4D
Point HL to 4D00H — the start of the allocation bitmap area.
4E80
LD B,C0H 06 C0
Load B with C0H (192) — bytes to fill with FFH.
4E80
LD (HL),FFH 36 FF
Store FFH at (HL) — mark all 8 granules in this byte as "allocated."
4E82
INC HL 23
INCrement HL to next bitmap byte.
4E83
DECrement B and loop back to 4E80H if not zero.
4E85
LD B,20H 06 20
Load B with 20H (32) — bytes to clear with 00H.
4E87
LD (HL),A 77
Store A (00H) at (HL).
4E88
INC HL 23
INCrement HL.
4E89
DECrement B and loop back to 4E87H if not zero.
4E8B
POP BC C1
Restore BC (adjusted free space value) from the stack.
4E8C
POP HL E1
Restore HL (directory buffer position) from the stack.
4E8D
LD A,(4FB8H) 3A B8 4F
Fetch operation mode flag from 4FB8H. 00H = FREE, 01H = PURGE.
4E90
OR A B7
Test the mode flag. Z = FREE, NZ = PURGE.
4E91
If NZ (PURGE mode), JUMP to 4E9EH to skip the free space comparison.
4E93
LD A,B 78
Load A with the high byte of the granule count.
4E94
CP 80H FE 80
Compare against 80H. If A >= 80H, NO CARRY is set.
4E96
PUSH HL E5
Save HL.
4E97
If NC (count >= 80H), GOSUB to 48C4H to flush directory buffer to disk.
4E9A
POP HL E1
Restore HL.
4E9B
If NZ (disk write error), JUMP to 4F0DH for error handling.

4E9EH - Process File Extents Into Allocation Bitmap

Walks through a directory entry's extent table and clears bits in the allocation bitmap at 4D00H-4DBFH for each allocated granule. Called after the bitmap has been initialized to all-FFH. The entry's low buffer address was saved at 4EA1H. In PURGE mode, also marks entries for deletion.

4E9E
DEC L 2D
DECrement Register L to back up the pointer within the directory entry buffer.
4E9F
DEC L 2D
DECrement Register L again — HL now points to the correct starting position in the extent table area.
4EA0
LD E,00H 1E 00
Load Register E with 00H — the directory entry buffer position. [SELF-MODIFYING CODE target] The 00H byte at address 4EA1H is overwritten at runtime with the actual entry position (see 4E36H and 4EB3H).

[LOOP START - Walk extent table for bitmap update] Register E holds the base address (low byte) of the current directory entry. Register L tracks the current position within the extent data.

4EA2
LD A,E 7B
Load Register A with Register E (the directory entry buffer base position).
4EA3
ADD 16H C6 16
ADD 16H to Register A — calculate the address of the extent table (offset 16H from entry start).
4EA5
CP L BD
Compare Register A against Register L (current position). If A < L, the current position is within or past the extent table.
4EA6
If the CARRY FLAG has been set (extent table start < current position — we are at/past the extent data), JUMP to 4EC5H to process the extent data.
4EA8
LD L,E 6B
Load Register L with E — set HL to point to the directory entry's status byte (base of entry).
4EA9
BIT 7,(HL) CB 7E
Test bit 7 of the status byte at (HL). Bit 7 set = entry is deleted or marked for deletion.
4EAB
If the Z FLAG has been set (bit 7 clear — entry is NOT deleted), JUMP to 4EFFH to finalize this file's processing.
4EAD
GOSUB to 4FC2H to mark the current file's granules in the secondary tracking area at 4DC0H-4DDFH.
4EB0
GOSUB to 4E59H to perform the disk position operation using the value in Register D.
4EB3
LD (4EA1H),A 32 A1 4E
Store Register A (updated position) into 4EA1H — update the self-modifying code with the new entry buffer position. [SELF-MODIFYING CODE]
4EB6
ADD 1FH C6 1F
ADD 1FH to Register A — advance to the last byte of the current directory entry (each entry is 32 bytes).
4EB8
LD L,A 6F
Load Register L with the result, pointing HL to the end of the current directory entry.
4EB9
LD A,(4FB8H) 3A B8 4F
Fetch the operation mode flag from 4FB8H. 00H = FREE, 01H = PURGE.
4EBC
OR A B7
Test the mode flag. Z = FREE, NZ = PURGE.
4EBD
If NZ (PURGE mode), JUMP back to 4E9EH to continue processing additional extents.

[FREE MODE ONLY] In FREE mode (A was 00H), DEC A produces FFH which is written to the last two bytes of the entry to mark deletion status in the buffer copy.

4EBF
DEC A 3D
DECrement Register A (from 00H to FFH).
4EC0
LD (HL),A 77
Store FFH at (HL) — write deletion marker at end of entry.
4EC1
DEC HL 2B
DECrement HL to the previous byte.
4EC2
LD (HL),A 77
Store FFH at this position — second deletion marker byte.
4EC3
JUMP back to 4E9FH to continue the extent processing loop.

[EXTENT DATA PROCESSING] Extract the granule number and count from the extent entry, then clear the corresponding bit in the allocation bitmap at 4D00H.

4EC5
LD E,(HL) 5E
Load Register E with the extent byte at (HL) — combined granule/count data.
4EC6
LD A,B 78
Load A with B (high byte of granule counter).
4EC7
OR C B1
OR with C. If BC = 0 (no more granules), Z is set.
4EC8
If Z (no more granules to process), JUMP to 4EFFH to finalize.
4ECA
PUSH DE D5
Save DE (E = extent byte).
4ECB
PUSH HL E5
Save HL (buffer position).
4ECC
LD D,(HL) 56
Load D with the extent byte at (HL).
4ECD
DEC (HL) 35
DECrement the extent byte in the buffer — reduce granule count.
4ECE
DEC HL 2B
DECrement HL to previous byte (starting granule number).
4ECF
LD E,(HL) 5E
Load E with the starting granule number byte.
4ED0
DEC E 1D
DECrement E — convert from 1-based to 0-based granule index.
4ED1
PUSH BC C5
Save BC (granule counter).

[CALCULATE BITMAP BYTE AND BIT POSITION] The granule number (encoded in D and E) is converted to a byte offset and bit position within the 192-byte bitmap. D's high 3 bits combined with D's low 5 bits form the sector number, which is divided by sectors-per-granule (from IY+05H) to get the actual granule number.

4ED2
LD A,D 7A
Load A with D (extent byte).
4ED3
AND 1FH E6 1F
Isolate low 5 bits of D.
4ED5
LD B,A 47
Save isolated value in B.
4ED6
PUSH AF F5
Save AF.
4ED7
LD A,D 7A
Load A with D again.
4ED8
RLCA 07
Rotate A Left — shift high bits down (first rotation).
4ED9
RLCA 07
Rotate A Left (second rotation).
4EDA
RLCA 07
Rotate A Left (third rotation). Original bits 7-5 are now in bits 2-0.
4EDB
AND 07H E6 07
Isolate bottom 3 bits (original high 3 bits of D).
4EDD
ADD A,B 80
ADD B (low 5 bits) to A (shifted high 3 bits) — combined sector number.
4EDE
LD B,(IY+05H) FD 46 05
Load B with sectors per granule from the drive parameter block at IY+05H.

[LOOP START - Divide by sectors-per-granule]

4EE1
INC E 1C
INCrement E — count one more granule.
4EE2
SUB B 90
SUBtract sectors-per-granule (B) from A.
4EE3
If NC (result >= 0), loop back to 4EE1H.

[LOOP END] E = granule number, A = negative remainder.

4EE5
ADD A,B 80
ADD B back to get positive remainder (bit position within byte).
4EE6
LD B,A 47
Save bit position in B.
4EE7
LD A,7FH 3E 7F
Load A with 7FH (01111111) — mask template with one bit clear.
4EE9
LD D,00H 16 00
Clear D for byte offset calculation.
4EEB
INC B 04
INCrement B for rotation count.

[LOOP START - Create bit mask]

4EEC
RLCA 07
Rotate A Left — shift the clear bit one position.
4EED
DECrement B and loop back to 4EECH if not zero.

[LOOP END] A = mask with exactly one bit cleared at the granule's position.

4EEF
LD HL,4D00H 21 00 4D
Point HL to 4D00H — base of allocation bitmap.
4EF2
ADD HL,DE 19
ADD byte offset (DE) to HL — point to the specific bitmap byte.
4EF3
AND (HL) A6
AND the mask with (HL) — clear the bit for this granule, marking it as "allocated by a file."
4EF4
LD (HL),A 77
Store updated bitmap byte back.
4EF5
POP AF F1
Restore AF.
4EF6
POP BC C1
Restore BC (granule counter).
4EF7
POP HL E1
Restore HL (buffer position).
4EF8
POP DE D1
Restore DE.
4EF9
INC BC 03
INCrement BC — one more granule processed.
4EFA
If NZ (INC BC did not wrap to 0000H — more granules), JUMP back to 4EC6H to process next extent.
4EFC
LD (HL),E 73
Store E at (HL) — save the final extent byte value.
4EFD
JUMP back to 4EB9H to check the mode flag and continue processing.

[FINALIZE FILE PROCESSING] All extents for this file have been processed. Check mode and proceed to results display or continue scanning.

4EFF
LD A,(4FB8H) 3A B8 4F
Fetch operation mode flag from 4FB8H. 00H = FREE, 01H = PURGE.
4F02
OR A B7
Test mode flag.
4F03
If Z (FREE mode), JUMP to 4F0AH.
4F05
GOSUB to 4FC2H to mark granules in tracking area (PURGE mode final update).
4F08
JUMP to 4F0DH to proceed to results display.
4F0A
GOSUB to 4E5FH to flush the directory sector to disk (FREE mode write-back).

4F0DH - GAT Verification and Bitmap Comparison

After all directory entries have been processed and the allocation bitmap at 4D00H-4DBFH is complete, this routine reads the disk's Granule Allocation Table (GAT) and compares it against the computed bitmap. If discrepancies are found, the GAT is updated. The bitmap is then used to count remaining free granules. The GAT is stored in the first sector of the directory track.

4F0D
GOSUB to 4F5AH to display the filename being processed (builds the display string from the directory entry and FCB data).
4F10
LD HL,4D00H 21 00 4D
Point Register Pair HL to 4D00H — the base of the computed allocation bitmap.
4F13
LD B,C0H 06 C0
Load Register B with C0H (192) — the number of bitmap bytes to scan for free granules.

[LOOP START - Scan bitmap for non-FFH bytes] Search through the bitmap for any byte that is not FFH. A byte that is not FFH contains at least one bit that was cleared, indicating an allocated granule. Actually, after the bitmap inversion logic, we are looking for bytes where some bits remain set (= free granules).

4F15
LD A,(HL) 7E
Fetch the current bitmap byte from (HL) into Register A.
4F16
INC A 3C
INCrement A. If A was FFH (all bits set = all granules allocated in this byte), it wraps to 00H and Z is set. If A was anything else, Z is clear.
4F17
INC HL 23
INCrement HL to the next bitmap byte.
4F18
If NZ (byte was not FFH — contains at least one free granule), JUMP to 4F1EH to process this bitmap section.
4F1A
DECrement B and loop back to 4F15H if not zero — continue scanning.
4F1C
JUMP to 4F33H — all bitmap bytes were FFH, meaning no free granules remain. Skip to the secondary workspace scan.

[LOOP END - Scan bitmap] Found a bitmap section with free granules. Now read the actual GAT from disk and compare/merge with the computed bitmap.

4F1E
XOR A AF
Set A to 00H.
4F1F
GOSUB to SYS0 routine at 48AFH to read a disk sector. A = 00H specifies the GAT sector (sector 0 of the directory track). On return, HL points to the GAT data in the buffer.
4F22
RET NZ C0
If NZ (read error), RETURN with error code.

[MERGE BITMAP WITH GAT] AND each byte of the computed bitmap with the corresponding GAT byte. This ensures that granules marked as allocated in the bitmap are also marked in the GAT. The result is written back to the GAT buffer.

4F23
LD DE,4D00H 11 00 4D
Point DE to 4D00H — the base of the computed allocation bitmap.
4F26
LD B,C0H 06 C0
Load B with C0H (192) — bytes to merge.

[LOOP START - AND bitmap with GAT]

4F28
LD A,(DE) 1A
Fetch the computed bitmap byte from (DE).
4F29
AND (HL) A6
AND the bitmap byte with the GAT byte at (HL). This preserves only bits that are set in BOTH — a granule is free only if both the computed bitmap and the existing GAT agree it is free.
4F2A
LD (HL),A 77
Store the merged result back into the GAT buffer at (HL).
4F2B
INC DE 13
INCrement DE to next bitmap byte.
4F2C
INC HL 23
INCrement HL to next GAT byte.
4F2D
DECrement B and loop back to 4F28H if not zero.

[LOOP END - AND bitmap with GAT] GAT buffer now contains the merged allocation data. Write it back to disk.

4F2F
GOSUB to 4E5FH to write the updated GAT sector back to disk.
4F32
EX DE,HL EB
Exchange DE and HL — HL now points past the bitmap area, which is the start of the secondary workspace at 4DC0H.

[SCAN SECONDARY WORKSPACE] Check the 32-byte workspace at 4DC0H-4DDFH for any non-zero bytes. Non-zero bytes indicate granules that need additional processing (extent tracking data).

4F33
LD B,20H 06 20
Load B with 20H (32) — bytes to scan in the secondary workspace.

[LOOP START - Scan workspace]

4F35
LD A,(HL) 7E
Fetch the current workspace byte from (HL).
4F36
OR A B7
Test if the byte is zero.
4F37
INC HL 23
INCrement HL to next byte.
4F38
If NZ (non-zero byte found), JUMP to 4F3DH to process it.
4F3A
DECrement B and loop back to 4F35H if not zero.
4F3C
RET C9
RETURN — all workspace bytes were zero, no additional processing needed. FREE/PURGE operation is complete.

[LOOP END - Scan workspace] Found a non-zero byte in the workspace. Read the GAT sector and apply the tracking data.

4F3D
LD A,01H 3E 01
Load A with 01H — sector 1 identifier for reading the second directory/GAT sector.
4F3F
GOSUB to 48AFH to read sector 1 from the directory track into the buffer.
4F42
RET NZ C0
If NZ (read error), RETURN with error code.

[APPLY WORKSPACE TO GAT SECTOR 1] Process the workspace data and OR each byte's bits into the GAT sector. The workspace at 4DC0H-4DDFH tracks which granules were freed during PURGE and need their GAT bits set.

4F43
LD DE,4DBFH 11 BF 4D
Point DE to 4DBFH — the last byte of the primary bitmap (one before the workspace start). DE will be incremented before first use.
4F46
LD C,20H 0E 20
Load C with 20H (32) — outer loop counter for workspace bytes.

[LOOP START - Process workspace bytes] Each workspace byte contains 8 bits, each representing one granule tracking flag.

4F48
INC DE 13
INCrement DE to the next workspace byte.
4F49
LD B,08H 06 08
Load B with 08H — inner loop counter for 8 bits per byte.

[LOOP START - Process bits within workspace byte]

4F4B
LD A,(DE) 1A
Fetch the workspace byte from (DE).
4F4C
RLCA 07
Rotate A Left — shift the high bit into the CARRY FLAG.
4F4D
If NC (bit was 0 — no granule to update), JUMP to 4F51H to skip this bit.
4F4F
LD (HL),00H 36 00
Store 00H at (HL) — clear the GAT entry for this granule (mark it as free/available).
4F51
INC HL 23
INCrement HL to next GAT byte.
4F52
DECrement B and loop back to 4F4CH if not zero (process next bit).

[LOOP END - Process bits]

4F54
DEC C 0D
DECrement C (outer loop counter).
4F55
If NZ (more workspace bytes to process), JUMP back to 4F48H.

[LOOP END - Process workspace bytes] All workspace data applied. Write the updated GAT sector back to disk.

4F57
JUMP to 4E5FH to write the updated GAT sector 1 back to disk. Execution returns from there via the directory continuation logic.

4F5AH - Build and Display Filename String

Builds a display string from the current directory entry's filename and extension fields, appends the drive number, and displays it on screen. The string is built in the area pointed to by IX (the FCB). The format is "FILENAME/EXT:d" where d is the drive number. Checks system flags first to determine if display should be suppressed.

4F5A
LD A,(428BH) 3A 8B 42
Fetch the system status flags byte from 428BH into Register A.
4F5D
BIT 2,A CB 57
Test bit 2 of the system flags. Bit 2 set = suppress display output.
4F5F
If NZ (bit 2 set — display suppressed), JUMP to 4FC0H to return without displaying.
4F61
LD A,(IX+07H) DD 7E 07
Load Register A with FCB offset 07H — the position byte used to locate the directory entry.
4F64
GOSUB to 4E5AH (which calls 48D4H) to perform a disk position operation to read the correct directory sector containing the file's entry.
4F67
EX DE,HL EB
Exchange DE and HL. HL now points to the directory entry in the sector buffer.
4F68
PUSH IX DD E5
Save IX (FCB pointer) onto the stack.
4F6A
POP HL E1
Pop the saved IX value into HL. HL now points to the FCB — this is the destination for building the display string.

[COPY FILENAME] Copy up to 8 characters of the filename from the directory entry (at DE+05H) to the display buffer (at HL). Skip spaces (20H) in the filename — they represent padding.

4F6B
LD B,08H 06 08
Load B with 08H — maximum 8 characters in a filename.
4F6D
PUSH HL E5
Save HL (display buffer pointer) for later restoration.
4F6E
INC DE 13
INCrement DE to advance past the directory entry's status byte.
4F6F
INC DE 13
INCrement DE again.
4F70
INC DE 13
INCrement DE — advance to offset +03H.
4F71
INC DE 13
INCrement DE — advance to offset +04H.
4F72
INC DE 13
INCrement DE — advance to offset +05H, the start of the 8-character filename field in the directory entry.

[LOOP START - Copy filename characters]

4F73
LD A,(DE) 1A
Fetch the next filename character from the directory entry at (DE).
4F74
CP 20H FE 20
Compare against 20H (ASCII space). If the character is a space (padding), Z is set.
4F76
If Z (space — skip padding), JUMP to 4F7AH to skip this character.
4F78
LD (HL),A 77
Store the filename character at (HL) in the display buffer.
4F79
INC HL 23
INCrement HL to next position in display buffer.
4F7A
DECrement B and loop back to 4F72H if not zero — process next filename character.

[LOOP END - Copy filename] Now check the extension field. If the extension is all spaces, skip the slash separator. Otherwise, append "/EXT".

4F7C
INC DE 13
INCrement DE to advance past the filename to the 3-character extension field (offset +0DH in directory entry).
4F7D
LD A,(DE) 1A
Fetch the first extension character.
4F7E
CP 20H FE 20
Compare against space. If the first extension character is a space, the extension is blank.
4F80
If Z (extension is blank), JUMP to 4F91H to skip the extension and go directly to the colon/drive number.
4F82
LD (HL),2FH 36 2F
Store 2FH (ASCII /) at (HL) — the slash separator between filename and extension.
4F84
LD B,03H 06 03
Load B with 03H — maximum 3 characters in the extension.

[LOOP START - Copy extension characters]

4F86
INC HL 23
INCrement HL to next display buffer position.
4F87
LD A,(DE) 1A
Fetch the next extension character from (DE).
4F88
CP 20H FE 20
Compare against space.
4F8A
INC DE 13
INCrement DE to next source character.
4F8B
If Z (space found — end of extension), JUMP to 4F91H.
4F8D
LD (HL),A 77
Store the extension character at (HL).
4F8E
INC HL 23
INCrement HL.
4F8F
DECrement B and loop back to 4F87H if not zero.

[LOOP END - Copy extension] Now append ":d" where d is the ASCII drive number.

4F91
LD (HL),3AH 36 3A
Store 3AH (ASCII :) at (HL) — the colon separator before the drive number.
4F93
INC HL 23
INCrement HL to the drive number position.
4F94
LD A,(427EH) 3A 7E 42
Fetch the current drive number from 427EH into Register A (binary value 0-3).

[CONVERT DRIVE NUMBER TO ASCII] The drive number (0-3) needs to be converted to its ASCII representation ('0'-'3'). This uses a division loop with 100 (64H) and 10 (0AH) to produce the hundreds, tens, and units digits. For drive numbers 0-3, the hundreds and tens will always be '0' and suppressed.

4F97
LD BC,0264H 01 64 02
Load BC with 0264H — B = 02H (outer loop count for hundreds then tens), C = 64H (100 decimal, the first divisor).
4F9A
LD D,2FH 16 2F
Load D with 2FH (ASCII '0' minus 1). D will be incremented before each subtraction, effectively counting the quotient digit as an ASCII character.

[LOOP START - Division by repeated subtraction]

4F9C
INC D 14
INCrement D — advance the ASCII digit character.
4F9D
SUB C 91
SUBtract the divisor (C) from Register A.
4F9E
If NC (result >= 0), loop back to 4F9CH to subtract again.

[LOOP END - Division] D = ASCII digit for current place value. A = negative remainder; add C back to get positive remainder for next digit.

4FA0
ADD A,C 81
ADD C back to A to restore the positive remainder.
4FA1
LD E,A 5F
Save the remainder in E.
4FA2
LD A,D 7A
Load A with D (the ASCII digit character).
4FA3
CP 30H FE 30
Compare against 30H (ASCII 0). If the digit is '0' (leading zero), Z is set.
4FA5
If Z (leading zero), JUMP to 4FA9H to suppress the leading zero (do not store it).
4FA7
LD (HL),A 77
Store the non-zero digit character at (HL) in the display buffer.
4FA8
INC HL 23
INCrement HL to next buffer position.
4FA9
LD A,E 7B
Restore the remainder from E into A for the next division pass.
4FAA
LD C,0AH 0E 0A
Load C with 0AH (10 decimal) — the divisor for the tens place.
4FAC
DECrement B and loop back to 4F9AH if not zero — process the tens digit.

After the loop, A contains the units digit (0-9). Convert it to ASCII by adding 30H and store it.

4FAE
ADD 30H C6 30
ADD 30H to Register A — convert binary digit (0-9) to ASCII character ('0'-'9').
4FB0
LD (HL),A 77
Store the units digit at (HL).
4FB1
INC HL 23
INCrement HL.
4FB2
LD (HL),03H 36 03
Store 03H at (HL) — the end-of-string terminator (ETX, End of Text).
4FB4
POP HL E1
Restore HL (the start of the display buffer, saved at 4F6DH).

[DISPLAY THE STRING] Display the constructed filename string. Uses a counter mechanism at 4FB7H-4FB9H to control display behavior. [SELF-MODIFYING CODE]

4FB5
LD B,20H 06 20
Load B with 20H (32) — maximum characters to display.
4FB7
LD A,00H 3E 00
Load Register A with 00H. [SELF-MODIFYING CODE target] The 00H at address 4FB8H is overwritten with the operation mode flag (00H = FREE, 01H = PURGE) at runtime.
4FB9
DEC A 3D
DECrement A. If mode was 00H (FREE), A becomes FFH and NZ is set. If mode was 01H (PURGE), A becomes 00H and Z is set.
4FBA
If NZ (FREE mode — A was 00H decremented to FFH), JUMP to 4FC0H to skip display and return.

[LOOP START - Display characters] PURGE mode: display each character of the filename string by writing it to the display buffer at (HL).

4FBC
LD (HL),A 77
Store A (00H) at (HL) — clear this character position (blank fill).
4FBD
INC HL 23
INCrement HL to next position.
4FBE
DECrement B and loop back to 4FBCH if not zero.

[LOOP END - Display characters]

4FC0
XOR A AF
Set A to 00H and Z FLAG (success).
4FC1
RET C9
RETURN with A = 00H and Z FLAG set (success).

4FC2H - Mark Granule in Secondary Tracking Area

Marks a granule as "processed" in the secondary tracking area at 4DC0H-4DDFH. Reads the directory entry position from the self-modifying code at 4EA1H, extracts the extent data, and sets the corresponding bit in the tracking area. This is used during PURGE operations to track which granules have been freed.

4FC2
LD A,(4EA1H) 3A A1 4E
Fetch the directory entry buffer position from 4EA1H (the self-modifying code location) into Register A.
4FC5
LD L,A 6F
Load Register L with A — set HL to point to the directory entry's status byte.
4FC6
RES 4,(HL) CB A6
RESset bit 4 of the status byte at (HL) — clear the "active file" flag, marking this entry as processed/deleted.
4FC8
LD E,D 5A
Copy Register D (the extent tracking value) into Register E.
4FC9
INC HL 23
INCrement HL to the next byte in the directory entry.
4FCA
LD D,(HL) 56
Load Register D with the byte at (HL) — additional extent position data.
4FCB
GOSUB to 4E5FH to flush the directory buffer to disk (write back the modified status byte).

[CALCULATE BIT POSITION IN TRACKING AREA] Convert the extent data in E into a byte offset and bit position within the 32-byte tracking area at 4DC0H.

4FCE
LD A,E 7B
Load A with E (extent tracking value).
4FCF
RRCA 0F
Rotate A Right — shift bits 2-0 toward upper positions (first rotation).
4FD0
RRCA 0F
Rotate A Right (second rotation).
4FD1
RRCA 0F
Rotate A Right (third rotation). After three right rotations, the original bits 7-3 of E are now in bits 4-0, giving the byte offset (E ÷ 8).
4FD2
AND 1FH E6 1F
AND with 1FH to isolate the byte offset (0-31 within the 32-byte area).
4FD4
PUSH BC C5
Save BC.
4FD5
LD HL,4DC0H 21 C0 4D
Point HL to 4DC0H — the base of the secondary tracking area.
4FD8
LD C,A 4F
Copy the byte offset into C.
4FD9
LD B,00H 06 00
Clear B. BC = byte offset.
4FDB
ADD HL,BC 09
ADD the offset to HL — point to the specific tracking byte.
4FDC
LD A,E 7B
Load A with E (original extent value).
4FDD
AND 07H E6 07
AND with 07H to isolate the bit position (E mod 8).
4FDF
LD B,A 47
Save bit position in B.
4FE0
LD A,01H 3E 01
Load A with 01H — start with bit 0 set.
4FE2
INC B 04
INCrement B for rotation count.

[LOOP START - Position bit mask]

4FE3
RRCA 0F
Rotate A Right — shift the set bit one position.
4FE4
DECrement B and loop back to 4FE3H if not zero.

[LOOP END] A now has the single bit set at the correct position.

4FE6
OR (HL) B6
OR Register A with the tracking byte at (HL) — set the bit for this granule.
4FE7
LD (HL),A 77
Store the updated tracking byte back.
4FE8
POP BC C1
Restore BC.
4FE9
RET C9
RETURN to caller.

4FEAH - Open Directory File for Reading

Sets up the stack with return addresses and jumps to a SYS0 routine to open the directory file. Pushes two return addresses onto the stack before jumping: E507H (a post-processing routine) and 497CH (the directory open continuation). Then jumps to 4A07H which performs the actual directory file open.

4FEA
LD HL,E507H 21 07 E5
Load HL with E507H — the address of a post-processing return point.
4FED
PUSH HL E5
Push E507H onto the stack as the outermost return address.
4FEE
LD HL,497CH 21 7C 49
Load HL with 497CH — the address of the directory open continuation routine in SYS0.
4FF1
PUSH HL E5
Push 497CH onto the stack as the immediate return address.
4FF2
JUMP to SYS0 routine at 4A07H to perform the directory file open operation. When 4A07H executes its RET, execution continues at 497CH (the first stacked address), and when that returns, execution continues at E507H.

4FF5H - File Position and EOF Marker Update (E5H Sub-function 7)

Updates a file's position and EOF marker information. Validates the file is open, checks its access mode, and reads/modifies the position fields in the FCB and directory entry. This is used for file truncation and position management operations.

4FF5
GOSUB to SYS0 routine at 48F0H to validate the FCB and check the file is properly open.
4FF8
If NZ (validation error), JUMP to 4E56H to continue with the directory scanning (returns with error).
4FFB
LD A,(IX+01H) DD 7E 01
Load Register A with FCB offset 01H — the additional flags byte. Bits 0-2 contain the file access mode.
4FFE
AND 07H E6 07
AND with 07H to isolate the access mode bits (0-7).
5000
CP 05H FE 05
Compare against 05H. If A >= 05H (access mode is 5 or higher), NC is set.
5002
RET NC D0
If NC (access mode >= 5 — not a valid mode for position update), RETURN immediately.
5003
LD E,(IX+0CH) DD 5E 0C
Load Register E with FCB offset 0CH — the low byte of the file's current sector position.
5006
LD D,(IX+0DH) DD 56 0D
Load Register D with FCB offset 0DH — the high byte of the file's current sector position. DE now holds the 16-bit sector position.
5009
LD A,(IX+08H) DD 7E 08
Load Register A with FCB offset 08H — the alternate position high byte.
500C
OR A B7
Test if the alternate position is zero.
500D
If Z (alternate position is zero), JUMP to 5010H to skip the DE increment.
500F
INC DE 13
INCrement DE — adjust the sector position when the alternate position is non-zero.
5010
PUSH HL E5
Save HL (directory entry pointer).
5011
INC L 2C
INCrement L to advance to the next field in the directory entry.
5012
INC L 2C
INCrement L again.
5013
INC L 2C
INCrement L — HL now points to offset +03H of the directory entry (the EOF sector count field).
5014
LD C,(HL) 4E
Load C with the current EOF sector count at (HL).
5015
LD (HL),A 77
Store the new value (from A — the alternate position byte) at (HL), updating the EOF sector count.
5016
SUB C 91
SUBtract the old value (C) from the new value (A). Z is set if they are equal.
5017
LD BC,0011H 01 11 00
Load BC with 0011H — an offset of 17 bytes to advance to the extent position fields.
501A
ADD HL,BC 09
ADD 11H to HL — advance to the extent position field (offset +14H from the entry start).
501B
LD C,(HL) 4E
Load C with the low byte of the current extent position.
501C
INC HL 23
INCrement HL to the high byte.
501D
LD B,(HL) 46
Load B with the high byte of the current extent position. BC = old extent position.
501E
LD (HL),D 72
Store the new high byte (D) at (HL).
501F
DEC HL 2B
DECrement HL back to the low byte.
5020
LD (HL),E 73
Store the new low byte (E) at (HL). The extent position is now updated to DE.
5021
If NZ (the old and new EOF sector counts differed), JUMP to 5027H to skip the comparison and just write back.

The EOF counts matched. Check if the extent positions also match (DE == old BC). If they match, no disk write is needed.

5023
LD H,D 62
Copy D to H.
5024
LD L,E 6B
Copy E to L. HL = DE = new extent position.
5025
SBC HL,BC ED 42
SUBtract old position (BC) from new position (HL). If equal, Z is set.
5027
If NZ (positions differ — directory entry was modified), GOSUB to 4E5FH to write the updated directory sector back to disk.
502A
POP HL E1
Restore HL.
502B
SCF 37
Set the CARRY FLAG to signal success to the caller.
502C
RET C9
RETURN with CARRY set (success).

502DH - KILL Command Access Check (Function C5H)

Validates whether a file can be accessed for the KILL command.

502D
GOSUB to SYS0 routine at 4925H to parse the filespec from the command line.
5030
GOSUB to 4FEAH to open the directory file for reading.
5033
If CARRY (file found), JUMP to 4FC0H to return success (A=00H, Z set).
5036
LD A,25H 3E 25
Load A with 25H — error code (access denied).
5038
OR A B7
Set NZ FLAG (error).
5039
RET C9
RETURN with error 25H.

503AH - KILL Interactive File Deletion (E5H Sub-function 6)

Interactive file deletion with user confirmation. Parses the command line for optional qualifiers: /ext to filter by extension and USR to include system files. For each matching file, prompts "KILL IT? (Y/N/Q)".

503A
LD A,B 78
Load A with B (initial search parameter).
503B
LD C,00H 0E 00
Load C with 00H — initialize option flags (no ext filter, no USR).
503D
GOSUB to SYS0 at 4791H to set up drive/directory parameters.
5040
RET NZ C0
If NZ (setup error), RETURN.
5041
GOSUB to 4C7AH to skip whitespace. Z = end of line.
5044
RET C D8
If CARRY (parse error), RETURN.
5045
If Z (end of line), JUMP to 5081H to begin file matching.
5047
LD A,(HL) 7E
Fetch next command line character.
5048
CP 2FH FE 2F
Compare against 2FH (ASCII /) for extension filter.
504A
If NZ (not slash), JUMP to 5069H to check for "USR".
504C
SET 0,C CB C1
SET bit 0 of C — enable extension filter flag.
504E
INC HL 23
INCrement HL past the slash.
504F
LD DE,5140H 11 40 51
Point DE to 5140H — the option flags / extension buffer.
5052
LD B,03H 06 03
Load B with 03H — max 3 extension chars.
5054
LD A,(HL) 7E
Fetch next character.
5055
SUB 30H D6 30
SUBtract 30H — convert digit chars to 0-9.
5057
CP 0AH FE 0A
If A < 0AH, it was a digit. CARRY set.
5059
If CARRY (digit), JUMP to 5061H to store it.
505B
SUB 11H D6 11
SUBtract 11H — converts 'A' to 00H.
505D
CP 1AH FE 1A
If A < 1AH, it was a letter. CARRY set.
505F
If NC (not letter/digit), JUMP to 5041H.
5061
INC DE 13
INCrement DE to next buffer position.
5062
LD A,(HL) 7E
Re-fetch the original character.
5063
LD (DE),A 12
Store character in extension buffer.
5064
INC HL 23
INCrement HL.
5065
DECrement B and loop if not zero.
5067
JUMP back to 5041H to continue parsing.
5069
CP 55H FE 55
Compare A against 55H (ASCII U).
506B
If NZ (not 'U'), JUMP to 5041H.
506D
LD D,H 54
Copy H to D.
506E
LD E,L 5D
Copy L to E. DE = current position.
506F
INC DE 13
INCrement DE past 'U'.
5070
LD A,(DE) 1A
Fetch next character.
5071
CP 53H FE 53
Compare against 53H (ASCII S).
5073
INC DE 13
INCrement DE.
5074
If NZ (not 'S'), JUMP to 5041H.
5076
LD A,(DE) 1A
Fetch next character.
5077
CP 52H FE 52
Compare against 52H (ASCII R).
5079
INC DE 13
INCrement DE.
507A
If NZ (not 'R'), JUMP to 5041H.
507C
SET 1,C CB C9
SET bit 1 of C — enable USR mode (include system files).
507E
EX DE,HL EB
HL = position past "USR".
507F
JUMP back to 5041H.

5081H - KILL: Begin File Matching and Deletion Loop

Stores option flags, reads the first directory sector, sets up the upper limit for scanning, and iterates through directory entries checking each against the match criteria.

5081
LD A,C 79
Load A with option flags from C.
5082
LD (5140H),A 32 40 51
Store option flags at 5140H.
5085
LD A,01H 3E 01
Load A with 01H — first directory data sector.
5087
GOSUB to 48AFH to read directory sector 1 into buffer.
508A
RET NZ C0
If NZ (read error), RETURN.
508B
LD A,(431FH) 3A 1F 43
Fetch max directory sector number from 431FH.
508E
ADD 08H C6 08
ADD 08H — calculate upper scan limit.
5090
LD (5130H),A 32 30 51
Store at 5130H. [SELF-MODIFYING CODE] This is the CP operand at 512FH.
5093
XOR A AF
Set A to 00H.
5094
LD C,A 4F
Load C with A — current sector/entry counter.
5095
GOSUB to 48DBH to read directory sector C into buffer. HL = entry pointer.
5098
RET NZ C0
If NZ (error), RETURN.
5099
LD A,(HL) 7E
Fetch directory entry status byte.
509A
AND 90H E6 90
Isolate bits 7 and 4.
509C
CP 10H FE 10
Compare against 10H (active file).
509E
If NZ (not active), JUMP to 5124H to advance.
50A1
LD A,(HL) 7E
Re-fetch status byte.
50A2
AND 48H E6 48
Isolate system file flags (bits 6,3).
50A4
LD DE,5140H 11 40 51
Point DE to option flags at 5140H.
50A7
LD A,(DE) 1A
Fetch option flags.
50A8
If Z (not system file), JUMP to 50AEH.
50AA
BIT 1,A CB 4F
Test USR mode flag (bit 1).
50AC
If NZ (system file but USR not enabled), JUMP to 5124H to skip.
50AE
BIT 0,A CB 47
Test extension filter flag (bit 0).
50B0
If Z (no ext filter), JUMP to 50C4H.
50B2
PUSH HL E5
Save HL.
50B3
LD A,L 7D
Load A with L.
50B4
ADD 0DH C6 0D
ADD 0DH — point to extension field.
50B6
LD L,A 6F
Update L.
50B7
LD B,03H 06 03
Load B with 03H — 3 chars to compare.
50B9
INC DE 13
INCrement DE to next filter char.
50BA
LD A,(DE) 1A
Fetch filter character.
50BB
CP (HL) BE
Compare against file extension char.
50BC
INC HL 23
INCrement HL.
50BD
If NZ (mismatch), break out.
50BF
Loop for next char.
50C1
POP HL E1
Restore HL.
50C2
If NZ (ext mismatch), JUMP to 5124H.
50C4
LD A,C 79
Load A with counter C.
50C5
CP 02H FE 02
Compare against 02H.
50C7
LD A,L 7D
Load A with entry buffer position.
50C8
If NC (counter >= 2), JUMP to 50CDH.
50CA
OR A B7
Test if entry position is zero.
50CB
If Z (skip GAT/HIT at start), JUMP to 5124H.
50CD
ADD A,C 81
ADD counter to position — combined entry ID.
50CE
LD (4487H),A 32 87 44
Store at 4487H — save entry position for PURGE.
50D1
PUSH HL E5
Save HL.
50D2
LD A,L 7D
Load A with L.
50D3
ADD 05H C6 05
ADD 05H — point to filename field.
50D5
LD L,A 6F
Update L.
50D6
LD B,08H 06 08
Load B with 08H — 8 filename chars.
50D8
GOSUB to 5136H to display B characters from (HL).
50DB
LD A,(HL) 7E
Fetch first extension char.
50DC
CP 20H FE 20
Compare against space.
50DE
If Z (blank ext), skip extension display.
50E0
LD A,2FH 3E 2F
Load A with 2FH (ASCII /).
50E2
GOSUB to ROM 0033H to display the slash.
50E5
LD B,03H 06 03
Load B with 03H — 3 extension chars.
50E7
GOSUB to 5136H to display extension.

[DISPLAY PROMPT] Show the "KILL IT? (Y/N/Q)" prompt string from 5144H, then wait for a keypress.

50EA
LD HL,5144H 21 44 51
Point HL to 5144H — the prompt string " KILL IT? (Y/N/Q)".
50ED
GOSUB to SYS0 routine at 4467H to display the string pointed to by HL.

[WAIT FOR KEYPRESS] Scan keyboard for Q (quit), N (skip), or Y (delete). Ignore other keys.

50F0
GOSUB to ROM routine at 0049H to scan the keyboard. Returns A = character pressed (0 if none).
50F3
RES 5,A CB AF
RESset bit 5 of A — convert lowercase to uppercase (forces bit 5 clear).
50F5
CP 51H FE 51
Compare against 51H (ASCII Q for Quit).
50F7
If Z (Q pressed), JUMP to 5101H to echo and handle response.
50F9
CP 4EH FE 4E
Compare against 4EH (ASCII N for No/skip).
50FB
If Z (N pressed), JUMP to 5101H.
50FD
CP 59H FE 59
Compare against 59H (ASCII Y for Yes/delete).
50FF
If NZ (not Q, N, or Y), JUMP back to 50F0H to wait for valid key.

[ECHO RESPONSE AND PROCESS]

5101
PUSH AF F5
Save the keypress character.
5102
GOSUB to ROM 0033H to display the pressed key on screen.
5105
LD A,0DH 3E 0D
Load A with 0DH (carriage return).
5107
GOSUB to ROM 0033H to output carriage return (new line).
510A
POP AF F1
Restore the keypress character.
510B
POP HL E1
Restore HL (directory entry pointer saved at 50D1H).
510C
CP 51H FE 51
Compare against Q (Quit).
510E
RET Z C8
If Z (Q pressed), RETURN — quit the KILL operation entirely.
510F
CP 4EH FE 4E
Compare against N (No/skip).
5111
If Z (N pressed), JUMP to 5124H to skip this file and advance.

[DELETE THE FILE] Y was pressed. Set up the FCB for deletion and call the PURGE entry point.

5113
LD DE,4480H 11 80 44
Point DE to 4480H — the default FCB / filename buffer.
5116
LD A,80H 3E 80
Load A with 80H — set bit 7 to mark the FCB for deletion.
5118
LD (DE),A 12
Store 80H at (DE) — mark the FCB's first byte with the deletion flag.
5119
GOSUB to 4DF4H — the PURGE loop continuation entry — to actually delete the file from the directory and update the GAT.
511C
RET NZ C0
If NZ (deletion error), RETURN with error code.

[ADVANCE TO NEXT ENTRY] Move to the next directory entry within the current sector, or advance to the next sector.

511D
PUSH HL E5
Save HL.
511E
LD A,C 79
Load A with current sector counter C.
511F
GOSUB to 48DBH to re-read directory sector C.
5122
POP HL E1
Restore HL.
5123
RET NZ C0
If NZ (error), RETURN.
5124
LD A,L 7D
Load A with L (current entry buffer position low byte).
5125
AND E0H E6 E0
AND with E0H — mask to the 32-byte entry boundary (clear low 5 bits).
5127
ADD 20H C6 20
ADD 20H (32) — advance to the next directory entry. If this wraps past FFH, CARRY is set (need next sector).
5129
LD L,A 6F
Update L with new entry position.
512A
If NC (still within current sector), JUMP to 5099H to process next entry.
512D
INC C 0C
INCrement C — advance to the next directory sector.
512E
LD A,C 79
Load A with the new sector counter.
512F
CP 00H FE 00
Compare against 00H. [SELF-MODIFYING CODE] The 00H at address 5130H is overwritten at 5090H with the upper sector limit. If C reaches the limit, CARRY is set.
5131
If CARRY (more sectors remain), JUMP to 5094H to read the next sector.
5134
XOR A AF
Set A to 00H and Z FLAG (success — all entries processed).
5135
RET C9
RETURN with A = 00H and Z FLAG set (KILL operation complete).

5136H - Display Characters From Buffer (Skip Spaces)

Displays B characters from the buffer at (HL), advancing HL after each character. Characters equal to 20H (space) are skipped and not displayed. All other characters are output via ROM routine 0033H.

5136
LD A,(HL) 7E
Fetch the next character from the buffer at (HL) into Register A.
5137
CP 20H FE 20
Compare against 20H (ASCII space). If the character is a space, Z is set.
5139
INC HL 23
INCrement HL to the next character in the buffer.
513A
If the NZ FLAG has been set (character is not a space), GOSUB to ROM routine at 0033H to display the character in Register A on screen.
513D
DECrement B and loop back to 5136H if not zero — process next character.
513F
RET C9
RETURN to caller. HL points past the last character processed.

5140H - Option Flags and Prompt String Data Area

This area serves dual purpose: at 5140H is the option flags byte (written at runtime by the KILL command at 5082H), followed by the extension filter buffer at 5141H-5143H, and at 5144H begins the "KILL IT?" prompt string. The data decodes as ASCII text: " KILL IT? (Y/N/Q) " followed by an ETX (03H) terminator.

5140
NOP 00
Option flags byte (overwritten at runtime). Initial value 00H = no flags set.
5141
DEFM 20 20 20 20 20 20 20 20 20 20
10 spaces (20H) — padding before the prompt text. Extension filter buffer occupies 5141H-5143H within this space.
514B
DEFM 4B 49 4C 4C 20 49 54 3F 20 28
ASCII text: "KILL IT? ("
5155
DEFM 59 2F 4E 2F 51 29 20 20
ASCII text: "Y/N/Q) "
515D
DEFB 20
Space (20H).
515E
DEFB 03
03H — ETX (End of Text) string terminator.

515FH - DEBUG/DUMP Screen Memory Display (Function A5H)

Displays the contents of memory on screen in a readable format. Non-printable characters (below 20H) are replaced with periods (2EH). Characters above the current display threshold (stored at 4290H) are also replaced with periods. The routine continuously dumps memory starting at 3C00H (video RAM) or 4000H (start of user RAM), checking for the BREAK key to stop. Before each line, if the current column is 0, a carriage return is output. The routine reads memory byte by byte, filters through the printability check, and outputs via ROM routine 003BH (printer output).

515F
LD HL,3C00H 21 00 3C
Point Register Pair HL to 3C00H — the start of the TRS-80 Model III video RAM (1024 bytes, 64 columns × 16 rows).

[MAIN DUMP LOOP] Check for BREAK key, then display the byte at (HL). If BREAK is pressed, switch to dumping from 4000H (start of user RAM) instead.

5162
LD A,(3840H) 3A 40 38
Read the keyboard row at 3840H — this is Row 6 which contains ENTER, CLEAR, BREAK, UP, DOWN, LEFT, RIGHT, SPACE keys.
5165
AND 04H E6 04
AND with 04H to isolate bit 2 — the BREAK key. Active LOW means bit 2 = 0 when BREAK is pressed.
5167
If the Z FLAG has been set (bit 2 = 0 — BREAK key IS pressed), JUMP to 516EH to continue dumping memory.
5169
LD HL,4000H 21 00 40
Point HL to 4000H — start of user RAM. BREAK not pressed, so reset the dump address to the beginning of user RAM.
516C
JUMP back to 5162H to check BREAK again and continue the loop.

[BREAK DETECTED — CONTINUE DUMP] Output a carriage return if at column 0, then display memory bytes.

516E
LD A,L 7D
Load Register A with the low byte of the current memory address (Register L).
516F
AND 3FH E6 3F
AND with 3FH — isolate the column position (bits 0-5, since each row is 64 bytes).
5171
LD A,0DH 3E 0D
Load A with 0DH (carriage return character).
5173
If the Z FLAG was set by the AND (column = 0, start of a new row), GOSUB to ROM routine at 003BH to output a carriage return to the printer.
5176
LD A,H 7C
Load A with the high byte of the current memory address (Register H).
5177
AND 3FH E6 3F
AND with 3FH. If H AND 3FH = 0 (address is at a 16K boundary, such as wrapping past FFFFH back to 0000H or reaching 4000H/8000H/C000H), Z is set.
5179
RET Z C8
If Z (reached a 16K boundary — end of dump region), RETURN to caller. This terminates the dump when the address wraps or reaches a boundary.
517A
LD A,(HL) 7E
Fetch the byte from memory at the current address (HL) into Register A.
517B
CP 20H FE 20
Compare against 20H (space — the lowest printable ASCII character). If A < 20H, CARRY is set (non-printable control character).
517D
INC HL 23
INCrement HL to advance to the next memory address.
517E
If CARRY (byte < 20H — non-printable), JUMP to 5188H to replace it with a period.

The byte is >= 20H (printable range). Now check if it exceeds the upper display threshold stored at 4290H. Characters above this threshold are also replaced with periods.

5180
LD B,A 47
Save the byte in Register B.
5181
LD A,(4290H) 3A 90 42
Fetch the upper display threshold from 4290H into Register A. Characters with values above this threshold are considered non-displayable.
5184
CP B B8
Compare the threshold (A) against the byte value (B). If threshold < byte value, CARRY is set (byte exceeds threshold).
5185
LD A,B 78
Restore the original byte value from B into A.
5186
If NC (byte <= threshold — printable), JUMP to 518AH to output the character as-is.
5188
LD A,2EH 3E 2E
Load A with 2EH (ASCII . period) — replacement character for non-printable bytes.
518A
GOSUB to ROM routine at 003BH to output the character in Register A to the printer.
518D
JUMP back to 5162H to continue the dump loop.

518FH - NOP Padding (End of SYS3 Code)

NOP padding bytes filling the remainder of the SYS3 overlay space from 518FH through 51DFH. This unused space (81 bytes of 00H) pads the file to its full allocated size on disk.

518F-51DF
NOP (×81) 00
81 bytes of NOP (00H) padding. This fills the remainder of the SYS3 overlay from 518FH through 51DFH. No executable code — unused space reserved for the overlay file's disk allocation.