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

Page Customization

Introduction / Summary

SYS14/SYS is a DOS overlay module for NEWDOS/80 v2.0 on the TRS-80 Model III for CLEAR, CREATE, ERROR, LIST, PRINT, and ROUTE. It is loaded into the overlay area at 4D00H–51E7H when any of its commands are invoked. SYS14 handles six DOS library commands:

ERROR

Display the text of a DOS error message for a given error code.

ROUTE

Redirect a logical I/O device (Keyboard, Display, Printer, RS-232 In/Out) to a different physical device or a memory address. ROUTE,CLEAR resets all routes to defaults.

CLEAR

Clear all device routes, dequeue all user timer interrupt routines and *name routines, optionally reset HIMEM and zero a range of user memory.

CREATE

Pre-allocate a disk file with specified record length, record count, and auto-space-allocation/deallocation options.

LIST

Display a text file on the video display, with optional start line and line count.

PRINT

Send a text file to the printer (same as LIST but to printer).

SYS14 is dispatched via RST 28H (the SVC handler). On entry, Register A holds the SVC function code F0H, and Register C holds the sub-function number (1–7) identifying which command to execute. Register HL points to the command line text following the command keyword. Register IY is set to 4280H (the DOS system variable base) by the first instruction.

Variable and RAM Location Reference

The following RAM locations, system variables, and self-modifying code addresses are referenced throughout SYS14/SYS.

Variables and Storage:

AddressDescription
4280HDOS System Variable Base (pointed to by IY throughout SYS14).
428BHDOS control flags byte.
4290HHighest printable ASCII code for the current output device.
42C9HCurrent top-of-user-memory pointer (HIMEM related).
42D3HStart of the device route table in the DOS system area.
4A7EHBase of the timer/logical routine linked list.
4A81HPointer to the error message table (used by ERROR command).
4480HFCB (File Control Block) area used for file operations.
4483HDOS overlay entry point pointer (2 bytes).
448AHFile position pointer (2 bytes) for sector-level I/O.
448CHAnother file control pointer (2 bytes).
4488HFile attribute/flag byte.
4058HSystem variable: start line counter for LIST command.
4584HSystem variable: line count for LIST command.
4411HSystem variable: END address for CLEAR command memory zeroing.

Self-Modifying Code:

AddressDescription
4DA5HStores the AND mask byte from the ROUTE device flags during ROUTE command processing. Modified by the instruction at 4D61H.
4DCEHStores DE (the physical device handler address) during ROUTE command processing. Modified by the instruction at 4D64H. Two bytes (low, high).
4DD9HStores a data byte fetched from the device entry during ROUTE processing. Modified by the instruction at 4D6DH.
4DE2HStores DE (the target address for ROUTE MM= memory routing). Modified by the instruction at 4DABH. Two bytes (low, high).
4D34HPart of the cold-start/warm-start hook at 4D33H. Stores the jump target address for JP Z,0000H. When modified by the LIST/PRINT handler at 501AH, this changes the warm-start vector.
5182HStores a character code used by the LIST/PRINT output routine. Modified by the PRINT command handler at 50E7H to change the output destination from display (0DH) to printer (3BH).

Major Routine Reference

The following is a summary of the major routines within SYS14/SYS and their entry points.

Command Dispatch and Error Handling

AddressDescription
4D00HSYS14 Main Dispatcher: Sets IY=4280H, validates function code F0H, then dispatches to the appropriate command handler based on Register C sub-function number.
4D27HCold Start / Warm Start Hook: Saves registers and either cold-starts the system (JP Z,0000H) or invokes RST 28H for a DOS SVC call. Used by LIST/PRINT to handle warm-start during file output.
4D39HCLEAR Command Entry: Parses optional START=, END=, MEM= parameters, clears all routes, dequeues timer/logical routines, zeroes user memory, and resets HIMEM.
4D4DHERROR Command Entry: Parses error code from command line, looks up the error message in the error dictionary, and displays it.
4E01HERROR Sub-handler (DI + setup): Disables interrupts and sets up DE=0000H for the error lookup, then calls the linked-list scanner at 4E94H.
4E0AH–
4E53H
Error Message Display Loop: Walks the error message linked list, displays matching error text with " TO " separation for multiple entries.
4E54HCommon Exit: Re-enables interrupts and returns.
4E56HDevice Name Lookup: Searches the device name table at 4F53H for a match with DE (the device address), and displays the device name. If not found, displays "MM=" followed by the hex address.
4E94HLinked List Scanner: Walks a linked list starting at 4A7FH+2, looking for an entry matching DE. If found, unlinks it. Used for dequeuing timer/logical routines during ROUTE and CLEAR processing.
4EC6HCommand Keyword/Parameter Table Scanner: Scans the device/parameter name table starting at BC, with D data bytes per entry after each null terminator. Returns the matched entry's flag byte in A and handler address in DE.
4ED7HTable Entry Skipper Sub-routine: Called by 4EC6H to skip past null-terminated strings and count data fields in the lookup table.
4EE9HError Exit (Parse Error 34H): Loads A=34H and jumps to the DOS error handler at 4409H (via 4EEBH).
4EEBHError Exit (General): Re-enables interrupts and jumps to DOS error handler at 4409H with error code in A.
4EEFHParse Hex/Decimal Number: Parses a numeric value from the command line text pointed to by HL, returning the 16-bit result in DE. Supports both decimal and hexadecimal (if suffixed with 'H').
4F12HNumeric Digit Parser Sub-routine: Parses individual digits (decimal or hex depending on flag in B), accumulating a 16-bit result in DE via shift-and-add multiplication.
4F8EHROUTE Command Entry: Parses the ROUTE command parameters, looks up logical and physical device names, and installs or removes device routing in the DOS route table.
5071HCREATE Command Entry: Parses CREATE command parameters (LRL=, REC=, ASE=, ASC=), opens or creates the specified file, and sets its attributes.
50E5HPRINT Command Entry: Sets the output character to 3BH (printer line terminator) via self-modifying code, then falls through to the LIST handler.
50EAHLIST Command Entry: Parses optional start line and line count parameters, opens the specified file, and displays its contents line by line, with keyboard control for pausing and cancellation.
513AHLIST Character Fetch: Reads the next character from the keyboard row at 3840H, checking for BREAK and SHIFT keys to control listing flow.
516FHLIST Number Validator: Validates that parsed numeric parameters are non-zero and within range.
517DHCarriage Return Output: Sends a carriage return (0DH) to the display via ROM routine 0033H.

Disassembly

4D00H - SYS14 Main Function Dispatcher

This is the entry point for SYS14. On entry from the SVC handler (RST 28H), Register A contains the SVC function code and Register C contains the sub-function number identifying which command to execute. Register HL points to the command line text. The dispatcher first sets IY to the DOS system variable base at 4280H, then checks whether the function code in Register A is F0H. If not, it exits with error code 2AH (general error). If the code is F0H, it decrements Register C repeatedly to dispatch to the correct command handler: C=1 for ERROR, C=2 for ROUTE, C=3 for a simple POP AF/RET (placeholder), C=4 for CREATE, C=5 for LIST, C=6 for PRINT, and C=7 for CLEAR.

SYS14 ENTRY POINT
The SVC handler loads this overlay into RAM at 4D00H and transfers control here. Register A = F0H (the SYS14 function code), Register C = sub-function number (1–7), Register HL = pointer to remaining command line text.

4D00
LD IY,4280H FD 21 80 42
Point Index Register IY to 4280H, the DOS system variable base address. IY will remain pointed here throughout SYS14 execution, providing quick access to system variables via indexed addressing.
4D04
CP F0H FE F0
Compare Register A (the SVC function code passed by the SVC handler) against F0H, which is the function code assigned to SYS14. If Register A equals F0H, the Z FLAG is set; otherwise the NZ FLAG is set.
4D06
If the NZ FLAG has been set (function code is NOT F0H - this is not a valid SYS14 call), JUMP to 4D22H to load error code 2AH and exit with an error.
COMMAND DISPATCH TABLE
Register C contains the sub-function number. The dispatcher decrements C and tests for zero after each decrement to route to the correct command handler.
C=Meaning
C=1Error
C=2Route
C=3POP then RETurn
C=4Create
C=5List
C=6Print
C=7Clear
4D08
DEC C 0D
DECrement Register C by 1. If C was 1 (ERROR command), it becomes 0 and the Z FLAG is set.
4D09
If the Z FLAG has been set (C was 1 = ERROR command), JUMP to 4D4DH to handle the ERROR command - display the text of a DOS error message for the specified error code.
4D0C
DEC C 0D
DECrement Register C by 1. If C was 2 (ROUTE command), it becomes 0 and the Z FLAG is set.
4D0D
If the Z FLAG has been set (C was 2 = ROUTE command), JUMP to 4F8EH to handle the ROUTE command - redirect a logical I/O device to a physical device or memory address.
4D10
DEC C 0D
DECrement Register C by 1. If C was 3, it becomes 0 and the Z FLAG is set.
4D11
If the Z FLAG has been set (C was 3 = placeholder/unused sub-function), JUMP to 4D37H which simply executes POP AF followed by RET - a no-op exit that restores AF from the stack and returns to the caller.
4D13
DEC C 0D
DECrement Register C by 1. If C was 4 (CREATE command), it becomes 0 and the Z FLAG is set.
4D14
If the Z FLAG has been set (C was 4 = CREATE command), JUMP to 5071H to handle the CREATE command - pre-allocate a disk file with specified parameters.
4D17
DEC C 0D
DECrement Register C by 1. If C was 5 (LIST command), it becomes 0 and the Z FLAG is set.
4D18
If the Z FLAG has been set (C was 5 = LIST command), JUMP to 50EAH to handle the LIST command - display a text file on the screen with optional start line and line count.
4D1B
DEC C 0D
DECrement Register C by 1. If C was 6 (PRINT command), it becomes 0 and the Z FLAG is set.
4D1C
If the Z FLAG has been set (C was 6 = PRINT command), JUMP to 50E5H to handle the PRINT command - send a text file to the printer (identical to LIST but output goes to the printer instead of the display).
4D1F
DEC C 0D
DECrement Register C by 1. If C was 7 (CLEAR command), it becomes 0 and the Z FLAG is set.
4D20
If the Z FLAG has been set (C was 7 = CLEAR command), JUMP to 4D39H to handle the CLEAR command - clear all device routes, dequeue timer/logical routines, and optionally zero user memory.

INVALID SUB-FUNCTION
If none of the C values 1–7 matched, we fall through to the error exit at 4D22H.

4D22
LD A,2AH 3E 2A
Load Register A with 2AH (decimal 42), the DOS error code for "General Error". This error is returned when the SVC function code or sub-function number is not recognized.
4D24
JUMP to 4EEBH to re-enable interrupts and then jump to the DOS error handler at 4409H with the error code 2AH in Register A.

4D27H - Cold Start / Warm Start Hook

This routine serves as a hook that can either perform a cold start (jump to 0000H to reboot) or invoke an RST 28H SVC call. It saves the alternate register set and key parameters onto the stack. The JP Z,0000H instruction at 4D33H is self-modifying - the LIST/PRINT handler modifies the target address at 4D34H to redirect warm-start behavior during file output operations. When Register A is non-zero on entry (OR A sets NZ), it falls through to RST 28H for a normal SVC call. When Register A is zero (OR A sets Z), it jumps to the address stored at 4D34H (normally 0000H for a cold reboot).

4D27
XOR A AF
Set Register A to zero and clear all flags. This prepares for the OR A test at 4D32H.
4D28
EXX D9
Exchange the primary register set (BC, DE, HL) with the alternate register set (BC', DE', HL'). This saves the current BC, DE, HL values into the alternate set so they can be preserved.
4D29
LD BC,F003H 01 03 F0
Load Register Pair BC with F003H. B = F0H (the SYS14 function code for RST 28H dispatch), C = 03H (sub-function 3 = the POP AF/RET placeholder). This prepares parameters for a potential RST 28H SVC call.
4D2C
LD DE,4978H 11 78 49
Load Register Pair DE with 4978H, a DOS system routine address. This value will be pushed onto the stack as a return address for the SVC call.
4D2F
PUSH BC C5
Save Register Pair BC (F003H - the SVC function code and sub-function) onto the stack.
4D30
PUSH DE D5
Save Register Pair DE (4978H - the DOS system routine address) onto the stack.
4D31
EXX D9
Exchange back to the primary register set, restoring the original BC, DE, HL values that were saved into the alternate set at 4D28H.
4D32
OR A B7
OR Register A with itself. Since A was set to 0 at 4D27H, this sets the Z FLAG. However, if this routine is entered at 4D28H (skipping the XOR A), Register A retains its previous value and the Z FLAG reflects whether A is zero or not.
4D33
[SELF-MODIFYING CODE] If the Z FLAG is set (Register A = 0), JUMP to the address stored at 4D34H–4D35H. Normally this is 0000H (cold reboot). During LIST/PRINT operations, the code at 501AH modifies bytes 4D34H–4D35H to point to 4439H (the DOS sector write routine), redirecting this hook to write file data instead of rebooting.
4D36
RST 28H EF
If Register A was non-zero (NZ FLAG set at 4D32H), invoke RST 28H - the NEWDOS/80 SVC (Supervisor Call) handler. The parameters previously pushed onto the stack (BC=F003H, DE=4978H) will be used by the SVC dispatcher.

4D37H - Sub-Function 3 Placeholder (POP AF / RET)

This is the handler for sub-function C=3. It simply pops AF from the stack and returns. This serves as a placeholder or no-op entry point that discards a stacked value and returns to the caller.

4D37
POP AF F1
Restore Register Pair AF from the stack, discarding the top stack value (which was the return address or saved flags from the calling code).
4D38
RET C9
RETurn to the caller.

4D39H - CLEAR Command Entry Point

Syntax: CLEAR [,START=addr1] [,END=addr2] [,MEM=addr3]. Register HL points to the command line text following "CLEAR".

The CLEAR command performs:
  1. ROUTE,CLEAR to reset all device routes to defaults
  2. dequeue all user timer interrupt routines
  3. dequeue all *name routines
  4. optionally reset HIMEM
  5. optionally zero a range of user memory.
4D39
GOSUB to 4EEFH to parse a hexadecimal or decimal number from the command line text pointed to by Register HL. On return, DE contains the parsed numeric value (or 0 if no number was present), and HL has advanced past the parsed text.
4D3C
GOSUB to 4C7AH (in SYS0) to skip blanks and check for the next command-line delimiter. On return: Z FLAG set if end of parameters (carriage return), NZ FLAG set if more parameters follow, CARRY FLAG set if a parsing error occurred.
4D3F
RET NZ C0
If the NZ FLAG has been set (there are more parameters to process on the command line), RETurn to the caller. The CLEAR command with parameters is handled by a different path - the caller will re-invoke with proper parameter parsing.
4D40
EX DE,HL EB
Exchange DE and HL. Now HL holds the parsed numeric value (from the 4EEFH call), and DE holds the command line pointer.
4D41
LD DE,0040H 11 40 00
Load Register Pair DE with 0040H (decimal 64). This is a comparison value - the maximum valid value for this context.
4D44
RST 18H DF
Invoke RST 18H, the NEWDOS/80 16-bit compare routine. This compares HL against DE. If HL < DE, the CARRY FLAG is set. If HL >= DE, the NO CARRY FLAG is set.
4D45
If the NO CARRY FLAG is set (HL >= 0040H - the parsed value is too large), JUMP to 4F0DH to exit with error code 2FH (parameter out of range).
4D48
LD H,L 65
Copy the low byte of HL into the high byte. Since HL held the parsed value (which is less than 0040H and thus fits in L alone), this creates H = L, effectively H = the parsed value.
4D49
LD A,66H 3E 66
Load Register A with 66H (decimal 102). This is the SVC function code for a specific DOS function that will be invoked next.
4D4B
PUSH HL E5
Save Register Pair HL onto the stack (preserving the adjusted parameter value for use after the SVC call returns).
4D4C
RST 28H EF
Invoke RST 28H - the NEWDOS/80 SVC handler. Register A = 66H identifies the specific DOS function to execute. This performs the route-clearing and timer/logical routine dequeuing operations of the CLEAR command.

4D4DH - ERROR Command Handler

The ERROR command displays the text of a DOS error message for a given error code.

Syntax: ERROR,xx where xx is a decimal or hexadecimal error number.

On entry, Register HL points to the command line text containing the error code number. The handler disables interrupts, parses the command line to find the error code, looks up the error in the error message linked list, and displays the error text. If the error code matches an entry in the ROUTE device table, the associated device names are also displayed.

ERROR COMMAND ENTRY
Register HL points to the command line text after "ERROR". The first task is to disable interrupts (DI) since we will be walking system data structures.

4D4D
DI F3
Disable Interrupts to prevent any interrupt-driven code from modifying the error message data structures while we are reading them.
4D4E
LD A,(HL) 7E
Load Register A with the byte at the memory address pointed to by HL (the next character on the command line). This checks what follows the ERROR keyword.
4D4F
CP 0DH FE 0D
Compare Register A against 0DH (carriage return). If the command line ends here (no error code specified), the Z FLAG is set. The ERROR command with no argument displays all current error routes.
4D51
If the Z FLAG has been set (the command line contains just "ERROR" with no error code - a carriage return follows), JUMP to 4E0AH to display all current device routes by walking the error/route message list starting from the pointer at 4A81H.
4D54
GOSUB to 4EC6H to scan the device/parameter name table starting at 4F53H. This routine searches for a keyword match against the text at HL. On return: Register A contains the flag/type byte from the matched table entry, and DE contains the 2-byte handler/address value from the entry.
4D57
BIT 7,A CB 7F
Test bit 7 of Register A (the flag byte returned by the table scanner). Bit 7 indicates whether the matched device entry is a valid I/O device. If bit 7 is 0, the Z FLAG is set (not a valid device); if bit 7 is 1, the NZ FLAG is set (valid device found).
4D59
If the Z FLAG has been set (bit 7 of the flag byte is 0 - not a recognized device name), JUMP to 4EE9H to exit with error code 34H (parse error). The command line text did not match any known device or parameter name.
4D5C
BIT 4,A CB 67
Test bit 4 of Register A (the flag byte). Bit 4 distinguishes between different device entry types. If bit 4 is 0, the Z FLAG is set; if bit 4 is 1, the NZ FLAG is set.
4D5E
If the NZ FLAG has been set (bit 4 is 1 - this is a special device type, the "CLEAR" keyword), JUMP to 4DFCH to handle the ROUTE,CLEAR sub-command which clears all device routes.

ROUTE ASSIGNMENT SETUP
We have matched a valid logical device name. Now we save the device flag and address, then parse the physical device (the "TO" target) from the command line.

4D61
LD (4DA5H),A 32 A5 4D
[SELF-MODIFYING CODE] Store Register A (the flag byte of the matched logical device) into memory location 4DA5H. This saves the logical device's type flags for later use during route installation. The byte at 4DA5H is part of the AND instruction at 4DA4H.
4D64
LD (4DCEH),DE ED 53 CE 4D
[SELF-MODIFYING CODE] Store Register Pair DE (the handler address of the matched logical device) into memory locations 4DCEH–4DCFH. This saves the logical device's I/O handler vector address for later route table manipulation. The two bytes at 4DCEH are part of the LD DE,0000H instruction at 4DCDH.
4D68
PUSH HL E5
Save Register Pair HL (the current command line pointer, now past the logical device name) onto the stack.
4D69
GOSUB to 4E94H to walk the linked list starting at 4A7FH+2. This scans the timer/logical routine chain looking for an entry matching DE (the logical device's handler address). If a match is found, the entry is unlinked (dequeued) from the list. This removes any existing route for this logical device.
4D6C
LD A,(DE) 1A
Load Register A with the byte at the memory address pointed to by DE. DE was set by the 4E94H routine to point to a data field in the linked list entry. This fetches a control byte from the entry.
4D6D
LD (4DD9H),A 32 D9 4D
[SELF-MODIFYING CODE] Store Register A (the control byte from the linked list entry) into memory location 4DD9H. This saves the old route's control value. The byte at 4DD9H is part of the LD A,00H instruction at 4DD8H.
4D70
POP HL E1
Restore Register Pair HL from the stack (the command line pointer, past the logical device name).
4D71
GOSUB to 4C7AH (in SYS0) to skip blanks and check for the next command-line delimiter. On return: CARRY FLAG set if error, Z FLAG set if end of line (carriage return), NZ FLAG set if more text follows.
4D74
RET C D8
If the CARRY FLAG has been set (a parsing error occurred), RETurn to the caller with the error condition.
4D75
If the Z FLAG has been set (end of command line - no physical device specified after the logical device), JUMP to 4E54H to re-enable interrupts and return. This handles "ROUTE,KB" without a target, which clears the route for that logical device.

PARSE PHYSICAL DEVICE
More text follows on the command line. This should be the physical device name (the routing target). Parse it using the same device name table scanner.

4D78
GOSUB to 4EC6H to scan the device name table again, this time matching the physical device name on the command line. On return: Register A = flag byte of the matched device, DE = handler address of the physical device.
4D7B
BIT 6,A CB 77
Test bit 6 of Register A (the flag byte of the matched physical device). Bit 6 indicates whether the device is a valid routing target. If bit 6 is 0, the Z FLAG is set (not valid); if bit 6 is 1, the NZ FLAG is set (valid target).
4D7D
If the Z FLAG has been set (bit 6 is 0 - the physical device name is not a valid routing target), JUMP to 4EE9H to exit with error code 34H (parse error).
4D80
BIT 5,A CB 6F
Test bit 5 of Register A. Bit 5 distinguishes between a named device (bit 5 = 1, e.g. PR, DO, NL) and the MM= memory-address routing option (bit 5 = 0). If bit 5 is 0, the Z FLAG is set; if bit 5 is 1, the NZ FLAG is set.
4D82
If the Z FLAG has been set (bit 5 is 0 - this is the MM= memory address routing option), JUMP to 4DA0H to parse the memory address and set up memory-mapped routing.

NAMED PHYSICAL DEVICE
The physical device is a named device (PR, DO, NL, etc.). Now we need to validate the address and install the route in the route table.

4D84
GOSUB to 4EEFH to parse any additional numeric value from the command line (this handles cases where additional parameters follow the device name).
4D87
LD A,D 7A
Load Register A with the value of Register D (the high byte of the parsed address in DE). This checks whether the address is within a valid range.
4D88
CP 52H FE 52
Compare Register A (high byte of address) against 52H. Addresses must be below 5200H (the start of the user memory area) to be valid device handler addresses. If A < 52H, the CARRY FLAG is set.
4D8A
If the CARRY FLAG has been set (high byte < 52H - the address is below 5200H and is within the DOS/system area, not user memory), JUMP to 4F0DH to exit with error code 2FH (parameter out of range).
4D8D
PUSH HL E5
Save Register Pair HL (the command line pointer) onto the stack.
4D8E
PUSH DE D5
Save Register Pair DE (the physical device handler address) onto the stack.
4D8F
LD HL,0010H 21 00 10
Load Register Pair HL with 0010H (decimal 16). This is an offset that will be added to the device handler address to locate the route table entry.
4D92
ADD HL,DE 19
ADD Register Pair DE (the device handler address) to HL (0010H). HL now points to address + 16, which is the location within the device handler block where route linkage data is stored.
4D93
EX DE,HL EB
Exchange DE and HL. DE now holds the address+16 pointer, HL is free for further calculation.
4D94
LD HL,FFFAH 21 FA FF
Load Register Pair HL with FFFAH (decimal -6, or 65530 unsigned). This is an offset of -6 relative to DE.
4D97
ADD HL,DE 19
ADD DE to HL. HL = DE + FFFAH = DE - 6. This points to the route table entry that is 6 bytes before the linkage data at DE. This is the location where the back-pointer to the previous route is stored.
4D98
LD (HL),D 72
Store Register D (the high byte of the link pointer in DE) into the memory location pointed to by HL. This writes the high byte of the forward link address into the route table entry.
4D99
DEC HL 2B
DECrement HL by 1 to point to the preceding byte (the low byte position of the link pointer).
4D9A
LD (HL),E 73
Store Register E (the low byte of the link pointer in DE) into the memory location pointed to by HL. This writes the low byte of the forward link address, completing the 2-byte link pointer.
4D9B
DEC HL 2B
DECrement HL by 1 to point to the next field to fill.
4D9C
EX DE,HL EB
Exchange DE and HL. DE now points to the partially-filled route entry, HL holds the original link data pointer.
4D9D
POP HL E1
Restore Register Pair HL from the stack. This retrieves the physical device handler address that was pushed at 4D8EH. (Note: this is actually retrieving DE that was pushed, now into HL.)
4D9E
JUMP unconditionally to 4DABH to continue installing the route by storing the address and control data into the route table entry.

4DA0H - MM= Memory Address Route Handler

This section handles the ROUTE command's MM=nnnn option, which routes a logical device to a memory address. The output will be sent to the specified RAM location instead of a physical device. Register HL points to the command line text after "MM=".

4DA0
PUSH HL E5
Save Register Pair HL (the command line pointer, positioned after the MM= keyword) onto the stack.
4DA1
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H. This initializes HL to zero as a default/placeholder value before processing.
4DA4
AND 00H E6 00
[SELF-MODIFYING CODE] AND Register A with 00H. The immediate byte at 4DA5H was previously modified by the instruction at 4D61H to contain the logical device's flag byte. This AND operation masks Register A (the physical device flags) against the logical device flags, checking for compatibility between the logical and physical device types.
4DA6
AND 03H E6 03
AND Register A with 03H (binary 0000 0011). This isolates the lowest 2 bits of the masked result, which encode the device direction (bit 0 = input capable, bit 1 = output capable). The result must be non-zero for a valid route.
4DA8
If the Z FLAG has been set (the AND result is zero - the logical and physical device are incompatible, e.g. trying to route an input device to an output-only target), JUMP to 4F0DH to exit with error code 2FH (invalid parameter).

4DABH - Install Route in Route Table

This section installs the new device route into the DOS route table. DE contains the target device/address, and HL contains the route table slot address. The route table is a linked list structure starting at 42D3H in the DOS system area.

4DAB
LD (4DE2H),DE ED 53 E2 4D
[SELF-MODIFYING CODE] Store Register Pair DE (the physical device handler address or MM= memory address) into memory locations 4DE2H–4DE3H. This saves the target address. The two bytes at 4DE2H are part of the LD DE,0000H instruction at 4DE1H.
4DAF
LD A,H 7C
Load Register A with the value of Register H (the high byte of HL, which holds the route source address or zero).
4DB0
OR L B5
OR Register A with Register L. If HL = 0000H (no existing route entry was found - this is a new route that needs a slot allocated), the Z FLAG is set. If HL is non-zero (an existing route slot was found), the NZ FLAG is set.
4DB1
If the NZ FLAG has been set (HL is non-zero - an existing route table slot is available), JUMP to 4DCDH to write the route data into that existing slot.

ALLOCATE NEW ROUTE SLOT
HL = 0, meaning no existing slot was found. We need to find a free slot in the route table starting at 42D3H.

4DB3
LD HL,42D3H 21 D3 42
Load Register Pair HL with 42D3H, the start address of the DOS device route table in the system variable area.
4DB6
LD A,L 7D
Load Register A with the value of Register L (the low byte of the current route table pointer in HL). This is used for a range check.
4DB7
CP FDH FE FD
Compare Register A against FDH. The route table occupies addresses 42D3H through approximately 42FFH. If L reaches FDH, the table is full. If A < FDH, the CARRY FLAG is set (more room). If A >= FDH, the NO CARRY FLAG is set (table full).
4DB9
LD A,3BH 3E 3B
Load Register A with 3BH (decimal 59), the DOS error code for "Route Table Full" (or a related capacity error). This is pre-loaded in case the table is full.
4DBB
If the NO CARRY FLAG has been set (L >= FDH - the route table is full, no more slots available), JUMP to 4EEBH to exit with the error code 3BH in Register A.

SCAN FOR FREE SLOT
Walk through the route table entries, each 6 bytes long, looking for an unused slot (marked by both bytes of the link field being FFH).

4DBE
INC HL 23
INCrement HL by 1 to advance to the next byte in the route table.
4DBF
INC HL 23
INCrement HL by 1.
4DC0
INC HL 23
INCrement HL by 1. HL has now advanced 3 bytes past the start of the current route entry, pointing to the first byte of the "in-use" marker field.
4DC1
LD D,H 54
Copy Register H into Register D. DE will hold the address of this potential slot for later use.
4DC2
LD E,L 5D
Copy Register L into Register E. DE now holds the same address as HL (the potential free slot pointer).
4DC3
INC HL 23
INCrement HL by 1 to advance to the next field.
4DC4
INC HL 23
INCrement HL by 1.
4DC5
INC HL 23
INCrement HL by 1. HL now points 3 bytes ahead of DE, to the free-slot marker bytes.
4DC6
LD A,(HL) 7E
Load Register A with the byte at the memory address pointed to by HL. This is the first free-slot marker byte.
4DC7
INC HL 23
INCrement HL by 1 to point to the second marker byte.
4DC8
AND (HL) A6
AND Register A with the byte at (HL). If both marker bytes are FFH, A AND FFH = FFH. If either is not FFH, the result will differ from FFH.
4DC9
INC A 3C
INCrement Register A by 1. If A was FFH (both markers were FFH = slot is free), incrementing makes A = 00H and sets the Z FLAG. If A was anything else, the Z FLAG is not set.
4DCA
If the NZ FLAG has been set (A was not FFH - this slot is in use, not free), JUMP back to 4DB6H to check the table boundary and try the next slot.
4DCC
EX DE,HL EB
Exchange DE and HL. HL now holds the free slot address (saved in DE at 4DC1/4DC2), DE holds the scan pointer. A free slot has been found.

4DCDH - Write Route Data Into Table Slot

A route table slot has been found (either existing or newly allocated). HL points to the slot. Now we fill in the route data: the target device address, control byte, and link pointers to connect this entry into the timer/logical routine linked list at 4A7EH.

4DCD
LD DE,0000H 11 00 00
[SELF-MODIFYING CODE] Load Register Pair DE with the value stored at 4DCEH–4DCFH. These bytes were modified by the instruction at 4D64H to contain the logical device's handler address. DE now holds the original logical device address.
4DD0
PUSH HL E5
Save Register Pair HL (the route table slot pointer) onto the stack for later retrieval.
4DD1
LD (HL),E 73
Store Register E (the low byte of the logical device handler address) into the memory location pointed to by HL. This writes the first byte of the route entry - the low byte of the source device vector.
4DD2
INC HL 23
INCrement HL by 1 to advance to the next byte of the route table slot.
4DD3
LD (HL),D 72
Store Register D (the high byte of the logical device handler address) into the memory location pointed to by HL. This completes the 2-byte source device vector.
4DD4
INC HL 23
INCrement HL by 1 to advance to the control byte position.
4DD5
LD A,C0H 3E C0
Load Register A with C0H (binary 1100 0000). This is the route control byte indicating that both the route is active (bit 7) and the entry is valid (bit 6).
4DD7
LD (DE),A 12
Store Register A (C0H, the active route marker) into the memory location pointed to by DE (the logical device handler address). This marks the logical device as having an active route.
4DD8
LD A,00H 3E 00
[SELF-MODIFYING CODE] Load Register A with the value stored at 4DD9H. This byte was modified by the instruction at 4D6DH to contain the old route's control value. A now holds the previous route's saved control byte.
4DDA
LD (HL),A 77
Store Register A (the old route control byte) into the memory location pointed to by HL. This saves the previous control state in the route table entry, allowing it to be restored if the route is later cleared.
4DDB
INC HL 23
INCrement HL by 1 to advance to the next field of the route entry.
4DDC
XOR A AF
Set Register A to zero and clear all flags.
4DDD
LD (HL),A 77
Store zero into the current byte of the route entry (padding/reserved field).
4DDE
INC HL 23
INCrement HL by 1.
4DDF
LD (HL),A 77
Store zero into the next byte of the route entry (another padding/reserved field).
4DE0
INC HL 23
INCrement HL by 1 to advance to the target address field.
4DE1
LD DE,0000H 11 00 00
[SELF-MODIFYING CODE] Load Register Pair DE with the value stored at 4DE2H–4DE3H. These bytes were modified by the instruction at 4DABH to contain the target physical device address (or MM= memory address). DE now holds the routing target.
4DE4
LD (HL),E 73
Store Register E (the low byte of the target address) into the memory location pointed to by HL.
4DE5
INC HL 23
INCrement HL by 1.
4DE6
LD (HL),D 72
Store Register D (the high byte of the target address) into the memory location pointed to by HL. This completes the 2-byte target address in the route entry.

LINK ROUTE ENTRY INTO CHAIN
Now link this new route entry into the timer/logical routine linked list rooted at 4A7EH. Walk the list to find the end (a null link), then point the last entry's link to this new route entry.

4DE7
LD DE,4A7EH 11 7E 4A
Load Register Pair DE with 4A7EH, the base address of the timer/logical routine linked list. This is the starting point for walking the chain.
4DEA
EX DE,HL EB
Exchange DE and HL. HL = 4A7EH (or current chain pointer), DE = the route entry address.
4DEB
INC HL 23
INCrement HL by 1 to skip past the first byte of the chain entry.
4DEC
INC HL 23
INCrement HL by 1.
4DED
INC HL 23
INCrement HL by 1. HL now points to the "next" link field of the current chain entry.
4DEE
LD E,(HL) 5E
Load Register E with the low byte of the "next" link pointer from the chain entry.
4DEF
INC HL 23
INCrement HL by 1 to point to the high byte of the link.
4DF0
LD D,(HL) 56
Load Register D with the high byte of the "next" link pointer. DE now holds the address of the next entry in the chain.
4DF1
LD A,D 7A
Load Register A with Register D (the high byte of the next link pointer).
4DF2
OR E B3
OR Register A with Register E. If DE = 0000H (the next link is null - we have reached the end of the chain), the Z FLAG is set.
4DF3
If the NZ FLAG has been set (DE is non-zero - there is another entry in the chain), JUMP back to 4DEAH to advance to the next entry and continue walking the chain.

END OF CHAIN FOUND
DE = 0000H, meaning HL points to the last entry's link field. Now we patch this link to point to our new route entry.

4DF5
POP BC C1
Restore Register Pair BC from the stack. This retrieves the route table slot pointer that was pushed at 4DD0H. BC now points to the new route entry.
4DF6
LD (HL),B 70
Store Register B (the high byte of the new route entry address) into the memory location pointed to by HL. This patches the high byte of the last chain entry's link to point to the new route.
4DF7
DEC HL 2B
DECrement HL by 1 to point to the low byte of the link field.
4DF8
LD (HL),C 71
Store Register C (the low byte of the new route entry address) into the memory location pointed to by HL. This completes the link, connecting the new route entry to the end of the chain.
4DF9
JUMP to 4D70H to restore the command line pointer from the stack and check if there are more ROUTE parameters to process (handles multiple routes on one command line, e.g. "ROUTE,KB,DO,PR,NL").

4DFCH - ROUTE,CLEAR Sub-Command Handler

This handler processes the ROUTE,CLEAR sub-command (entered when bit 4 of the matched device flag byte is 1, indicating the "CLEAR" keyword was matched). It clears all existing device routes by dequeuing all route entries from the linked list.

4DFC
GOSUB to 4C7AH (in SYS0) to skip blanks and check for the next delimiter on the command line.
4DFF
If the NZ FLAG has been set (more text follows on the command line - unexpected for ROUTE,CLEAR), JUMP to 4E54H to re-enable interrupts and return. The extra text is ignored.
4E01
DI F3
Disable Interrupts to prevent any interrupt-driven code from modifying the linked list while we are clearing it.
4E02
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H. Passing DE = 0 to the linked list scanner at 4E94H causes it to dequeue ALL entries (rather than searching for a specific address match).
4E05
GOSUB to 4E94H to walk the timer/logical routine linked list and dequeue all entries (since DE = 0000H, every entry is removed).
4E08
JUMP unconditionally to 4E53H to clear Register A (XOR A) and then re-enable interrupts and return.

4E0AH - Display All Current Device Routes (ERROR with No Argument)

When the ERROR command is entered with no error code (just "ERROR" followed by a carriage return), this routine displays all current device routes. It walks the error/route message linked list starting from the pointer stored at 4A81H, displaying each route's logical device name, " TO " separator, and physical device name.

4E0A
LD HL,(4A81H) 2A 81 4A
Load Register Pair HL with the 16-bit value stored at memory locations 4A81H–4A82H. This is the pointer to the head of the error message / route display linked list.
4E0D
JUMP unconditionally to 4E4FH to check if HL is zero (empty list) and begin the display loop.

4E0FH - Route Display Loop Body

This is the main loop that iterates through route entries and displays their information. For each entry, it reads the source device address, looks up its name, displays " TO ", reads the target device address, looks up its name, then checks for the next link in the chain.

4E0F
LD E,(HL) 5E
Load Register E with the low byte of the source device address from the current linked list entry pointed to by HL.
4E10
INC HL 23
INCrement HL by 1 to point to the high byte of the source address.
4E11
LD D,(HL) 56
Load Register D with the high byte of the source device address. DE now holds the complete source device address for this route entry.
4E12
INC HL 23
INCrement HL by 1 to skip past the source address to the control byte.
4E13
INC HL 23
INCrement HL by 1 to skip the control byte. HL now points to the route data area.
4E14
PUSH DE D5
Save Register Pair DE (the source device address) onto the stack.
4E15
PUSH HL E5
Save Register Pair HL (the current position in the linked list) onto the stack.
4E16
GOSUB to 4E56H to look up and display the device name for the address in DE (the source device). This searches the device name table at 4F53H and displays the matching name (e.g., "KB", "DO", "PR") or "MM=nnnnH" if it is a memory-mapped route.
4E19
LD HL,4F4EH 21 4E 4F
Load Register Pair HL with 4F4EH, the address of the " TO " separator string in the string data area. This string is " TO " followed by a 03H terminator byte.
4E1C
GOSUB to 4467H (in SYS0), the DOS "send message to display" routine. HL points to the " TO " string, which is displayed on screen. The string is output until a 0DH (carriage return) or 03H (end of message) terminator is reached.
4E1F
POP HL E1
Restore Register Pair HL from the stack (the position in the linked list, pointing to the route data area).
4E20
PUSH HL E5
Save Register Pair HL onto the stack again (needed for later when we advance to the next entry).
4E21
INC HL 23
INCrement HL by 1 to skip past the first data byte.
4E22
INC HL 23
INCrement HL by 1. HL now points to the target device address field within the route entry.
4E23
LD E,(HL) 5E
Load Register E with the low byte of the target device address.
4E24
INC HL 23
INCrement HL by 1.
4E25
LD D,(HL) 56
Load Register D with the high byte of the target device address. DE now holds the complete target (physical) device address.
4E26
GOSUB to 4E56H to look up and display the device name for the target device address in DE.
4E29
POP HL E1
Restore Register Pair HL from the stack (the position in the linked list).
4E2A
POP DE D1
Restore Register Pair DE from the stack (the source device address that was pushed at 4E14H).

READ NEXT LINK
Now read the "next" link pointer from the current entry to advance to the next route in the chain. The link field is at the current position in HL.

4E2B
LD A,(HL) 7E
Load Register A with the low byte of the "next" link from the route entry.
4E2C
INC HL 23
INCrement HL by 1.
4E2D
LD H,(HL) 66
Load Register H with the high byte of the "next" link.
4E2E
LD L,A 6F
Load Register L with Register A (the low byte of the link). HL now holds the complete address of the next route entry in the chain.
4E2F
PUSH HL E5
Save Register Pair HL (the next entry address) onto the stack.
4E30
LD A,H 7C
Load Register A with Register H (the high byte of the next entry address).
4E31
OR L B5
OR Register A with Register L. If HL = 0000H (null link - end of chain), the Z FLAG is set.
4E32
JUMP unconditionally to 4E49H to output a carriage return and then check whether to continue the loop (based on the Z FLAG set by the OR at 4E31H).

DUPLICATE CHECK
Before displaying, check if this entry's source address matches the previous entry's source address (DE). If so, display a comma separator instead of a new line. This handles devices with multiple routes showing as "KB TO DO, PR" rather than separate lines.

4E34
LD A,(HL) 7E
Load Register A with the low byte of the source device address from the current entry.
4E35
CP E BB
Compare Register A against Register E (the low byte of the previous entry's source address). If they match, the Z FLAG is set.
4E36
INC HL 23
INCrement HL by 1 to point to the high byte.
4E37
If the NZ FLAG has been set (the low bytes do not match), JUMP to 4E3BH to skip the high byte comparison (they are different entries).
4E39
LD A,(HL) 7E
Load Register A with the high byte of the source device address.
4E3A
CP D BA
Compare Register A against Register D (the high byte of the previous source address). If both bytes match, the Z FLAG is set - same source device as the previous entry.
4E3B
INC HL 23
INCrement HL by 1.
4E3C
INC HL 23
INCrement HL by 1. HL now points past the header fields of the current entry.
4E3D
If the NZ FLAG has been set (different source device - not a duplicate), JUMP to 4E49H to output a carriage return and continue with the standard display loop.

SAME SOURCE - DISPLAY COMMA SEPARATOR
This entry has the same source device as the previous one. Display a comma separator and loop back to display the target device.

4E3F
POP AF F1
Discard the top of stack (the next link address that was pushed at 4E2FH). We will re-push after displaying the comma.
4E40
LD A,2CH 3E 2C
Load Register A with 2CH (ASCII comma ,). This separator will be displayed between multiple targets for the same source device.
4E42
PUSH HL E5
Save Register Pair HL (the current list position) onto the stack.
4E43
PUSH DE D5
Save Register Pair DE (the source device address) onto the stack.
4E44
GOSUB to ROM routine at 0033H to display the character in Register A (comma) at the current cursor position and advance the cursor.
4E47
JUMP back to 4E1FH to display the next target device for this same source (pop HL, push HL, advance to target address, look up name, display it).
4E49
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return). This outputs a new line to separate route display entries.
4E4B
GOSUB to ROM routine at 0033H to display the carriage return character, moving the cursor to the start of the next line.
4E4E
POP HL E1
Restore Register Pair HL from the stack (the next link address or the list head pointer).
4E4F
LD A,H 7C
Load Register A with Register H (the high byte of the link pointer).
4E50
OR L B5
OR Register A with Register L. If HL = 0000H (null pointer - list is empty or we have reached the end), the Z FLAG is set.
4E51
If the NZ FLAG has been set (HL is non-zero - more entries to display), JUMP back to 4E0FH to process and display the next route entry.
4E53
XOR A AF
Set Register A to zero and clear all flags. This indicates success (no error) to the caller.
4E54
EI FB
Re-enable Interrupts, which were disabled at the start of the ERROR/ROUTE processing.
4E55
RET C9
RETurn to the caller with Register A = 0 (success) and all flags clear.

4E56H - Lookup Device Name and Display It

This subroutine searches the device name table at 4F53H for the device whose DCB address matches the value in Register Pair DE. If found, it displays the device name. If not found, it displays the DCB address as a hex string using the format template at 4F86H ("MM="). On entry, DE holds the DCB address of the device to look up.

4E56
LD HL,4F53H 21 53 4F
Point Register Pair HL to 4F53H - the start of the device name table. This table contains NUL-terminated device name strings (KB, DO, PR, RI, RO, NL, CLEAR, MM=), each followed by 3 data bytes: a flags byte and a 2-byte DCB address.
4E59
PUSH HL E5
Save the current table position onto the stack. This will be used later to display the matched device name.

LOOP START - Skip Past Device Name
This inner loop advances HL past the NUL-terminated device name string to reach the 3 data bytes that follow it.

4E5A
LD A,(HL) 7E
Fetch the current byte from the device name table into Register A.
4E5B
OR A B7
Test Register A against itself. If A is 00H (NUL terminator), the Z FLAG is set, indicating the end of the device name string has been reached.
4E5C
INC HL 23
Advance HL to the next byte in the table.
4E5D
If the NZ FLAG is set (current byte was not NUL), JUMP back to 4E5AH to continue scanning through the name string. [LOOP END]

COMPARE DCB ADDRESS
HL now points to the first data byte after the NUL terminator. The next 2 bytes (at HL+1 and HL+2) contain the DCB address for this device in little-endian order. Compare against DE (the target DCB address).

4E5F
INC HL 23
Skip past the first data byte (the flags byte) to reach the low byte of the DCB address.
4E60
LD A,(HL) 7E
Fetch the low byte of this device's DCB address from the table into Register A.
4E61
CP E BB
Compare Register A (DCB address low byte from table) against Register E (target DCB address low byte). If they match, the Z FLAG is set.
4E62
INC HL 23
Advance HL to the high byte of the DCB address in the table.
4E63
If the NZ FLAG is set (low bytes do not match), JUMP to 4E67H to skip the high-byte comparison and check for more table entries.
4E65
LD A,(HL) 7E
Fetch the high byte of this device's DCB address from the table into Register A.
4E66
SUB D 92
SUBtract Register D (target DCB address high byte) from Register A. If they are equal, A becomes 00H and the Z FLAG is set, meaning both bytes match - this is the correct device.
4E67
If the Z FLAG is set (DCB address matches), JUMP to 4E74H to display this device's name. Register A is 00H (Z FLAG set from the successful SUB).

NO MATCH - Try Next Entry
The DCB address did not match. Advance past the remaining data byte to the next entry's name, and check if the table has ended.

4E69
INC HL 23
Advance HL past the high byte of the DCB address, now pointing to the start of the next table entry's name.
4E6A
POP AF F1
Discard the saved table position for the non-matching entry (the PUSH HL from 4E59H). This cleans the stack for the next iteration.
4E6B
LD A,(HL) 7E
Fetch the first byte of the next table entry's name into Register A.
4E6C
OR A B7
Test if A is 00H. If the Z FLAG is set, the table has ended (a NUL byte marks the end of the entire device table).
4E6D
If the NZ FLAG is set (there is another entry), JUMP back to 4E59H to save the new position and search this entry.

TABLE EXHAUSTED - No Match Found
The device was not found in the name table. Fall through to display it as "MM=nnnn" using the hex address format.

4E6F
LD HL,4F86H 21 86 4F
Point Register Pair HL to 4F86H - the "MM=" string in the device table. This will be displayed as the device name prefix before the hex address.
4E72
INC A 3C
INCrement Register A from 00H to 01H. This clears the Z FLAG (NZ), which signals the display routine at 4E74H that a hex address should be appended after displaying the name string.
4E73
PUSH HL E5
Save HL (pointing to "MM=" string at 4F86H) onto the stack. This replaces the table position that was popped at 4E6AH.

4E74H - Display Device Name String

Displays the NUL-terminated device name string pointed to by the stack value. On entry, the stack holds the pointer to the start of the device name. The Z FLAG state from the caller determines post-display behavior: Z FLAG set (A=00H) means a match was found and only the name is displayed; NZ FLAG set (A≠0) means no match was found, so after displaying the "MM=" prefix, the DCB address in DE is appended as a hex string.

4E74
POP HL E1
Restore Register Pair HL from the stack. HL now points to the start of the NUL-terminated device name string to display.
4E75
PUSH AF F5
Save the flags and Register A onto the stack. The Z FLAG state indicates whether a hex address needs to be appended after the name.
4E76
PUSH DE D5
Save Register Pair DE (the DCB address) onto the stack. This will be needed later if a hex address must be displayed.

LOOP START - Display Name Characters
Print each character of the device name until a NUL terminator is reached.

4E77
LD A,(HL) 7E
Fetch the current character from the device name string into Register A.
4E78
INC HL 23
Advance HL to the next character in the name string.
4E79
GOSUB to ROM routine at 0033H to display the character in Register A at the current cursor position and advance the cursor.
4E7C
LD A,(HL) 7E
Fetch the next character from the name string into Register A.
4E7D
OR A B7
Test if A is 00H (NUL terminator). If so, the Z FLAG is set.
4E7E
If the NZ FLAG is set (character is not NUL), JUMP back to 4E77H to display this character. [LOOP END]

NAME DISPLAYED - Check If Hex Address Needed
The full device name has been printed. Now check if we need to append a hex address (for the "MM=nnnn" case).

4E80
POP DE D1
Restore Register Pair DE (the DCB address) from the stack.
4E81
POP AF F1
Restore the flags from the stack. The Z FLAG state determines whether a hex address needs to be displayed.
4E82
OR A B7
Test Register A. If A is 00H (device was found in table), the Z FLAG is set. If A is non-zero (no match, MM= prefix was displayed), the NZ FLAG is set.
4E83
RET Z C8
If the Z FLAG is set (device name was found and displayed), RETURN to the caller. No hex address is needed.

DISPLAY HEX ADDRESS
No device name match was found. The "MM=" prefix has been displayed. Now display the DCB address in DE as a 4-digit hex number followed by "H".

4E84
LD HL,FFF8H 21 F8 FF
Load Register Pair HL with FFF8H (−8 in two's complement). This will be added to DE to calculate a display offset: DE − 8 gives the base address to show, since the DCB address stored in the ROUTE table is offset by 8 from the actual device vector address.
4E87
ADD HL,DE 19
ADD DE to HL. HL now contains DE − 8, the adjusted address to display.
4E88
EX DE,HL EB
Swap HL and DE. DE now holds the adjusted address (DE − 8) and HL is free.
4E89
LD HL,4F48H 21 48 4F
Point Register Pair HL to 4F48H - the hex format template string "0000H" followed by a 03H terminator. This 5-byte buffer will be overwritten with the actual hex digits of the address.
4E8C
PUSH HL E5
Save HL (pointing to format buffer at 4F48H) onto the stack. After the hex conversion fills the buffer, it will be displayed.
4E8D
GOSUB to SYS0 routine at 44D2H to convert the 16-bit value in DE into a 4-digit hex ASCII string and store it in the buffer pointed to by HL (4F48H). After this call, the buffer at 4F48H contains the hex representation of the adjusted address.
4E90
POP HL E1
Restore HL from the stack, pointing back to the hex string buffer at 4F48H.
4E91
JUMP to SYS0 routine at 4467H to display the NUL-terminated string at HL (the hex address string "nnnnH" at 4F48H). This routine returns to the caller of 4E56H after displaying the address.

4E94H - Traverse FCB Linked List and Process $FILPTR Entries

This subroutine walks the linked list of File Control Blocks (FCBs) starting from the head pointer at 4A7FH. For each FCB in the chain, if DE is non-zero, it checks whether the FCB's file pointer matches DE. If a match is found, it processes the entry by restoring a saved byte, marking the FCB entry as inactive (FFFFH), and relinking the list. If DE is 0000H, it unconditionally processes the first active entry. This is the core engine for the $FILPTR SVC which manages file pointer redirection.

4E94
LD HL,4A7FH 21 7F 4A
Point Register Pair HL to 4A7FH - this is the system variable that holds the head pointer of the $FILPTR linked list. The linked list chains together all active file pointer redirections.

LOOP START - Walk Linked List
Each entry in the linked list is 7 bytes: bytes 0-1 = pointer to next entry (or 0000H for end), byte 2 = the original file pointer byte to restore, bytes 3-4 = next-entry forward pointer (for relinking), bytes 5-6 unused. The traversal uses BC to track the previous entry's pointer location for relinking.

4E97
INC HL 23
Advance HL by 1 byte to position within the linked list node.
4E98
INC HL 23
Advance HL again. HL now points to the 2-byte forward pointer field of the current node.
4E99
LD B,H 44
Copy the high byte of HL into Register B. BC will hold the address of the current forward pointer, used for relinking if this entry is removed.
4E9A
LD C,L 4D
Copy the low byte of HL into Register C. BC now points to the current node's forward pointer field.
4E9B
LD A,(HL) 7E
Fetch the low byte of the forward pointer from the current node into Register A.
4E9C
INC HL 23
Advance HL to the high byte of the forward pointer.
4E9D
LD H,(HL) 66
Load the high byte of the forward pointer into Register H.
4E9E
LD L,A 6F
Load the low byte of the forward pointer (saved in A) into Register L. HL now contains the address of the next linked list entry.
4E9F
LD A,H 7C
Copy the high byte of the next entry address into Register A.
4EA0
OR L B5
OR Register L into Register A. If HL is 0000H (end of linked list), the Z FLAG is set.
4EA1
RET Z C8
If the Z FLAG is set (HL = 0000H, end of list reached), RETURN to the caller. No matching entry was found, or all entries have been processed.

CHECK MATCH CONDITION
HL points to a valid linked list entry. If DE is non-zero, compare the entry's file pointer against DE. If DE is zero, process this entry unconditionally.

4EA2
LD A,D 7A
Load the high byte of DE (the target file pointer to match) into Register A.
4EA3
OR E B3
OR Register E into Register A. If DE is 0000H, the Z FLAG is set (process unconditionally).
4EA4
LD A,(HL) 7E
Fetch the low byte of this entry's file pointer into Register A.
4EA5
INC HL 23
Advance HL to the high byte of the entry's file pointer.
4EA6
If the Z FLAG is set (DE was 0000H - unconditional mode), JUMP to 4EAFH to process this entry without comparing the file pointer.
4EA8
CP E BB
Compare Register A (entry's file pointer low byte) against Register E (target low byte).
4EA9
If the NZ FLAG is set (low bytes do not match), JUMP to 4EADH to skip the high byte comparison.
4EAB
LD A,(HL) 7E
Fetch the high byte of this entry's file pointer into Register A.
4EAC
CP D BA
Compare Register A (entry's file pointer high byte) against Register D (target high byte). If they match, the Z FLAG is set.
4EAD
If the NZ FLAG is set (file pointers do not match), JUMP back to 4E97H to advance to the next entry in the linked list.

MATCH FOUND - Process Entry
Either DE was 0000H (unconditional) or the file pointer matched. Now restore the original byte, mark this entry inactive (FFFFH), and relink the list to remove this entry.

4EAF
PUSH BC C5
Save BC (the address of the previous node's forward pointer field) onto the stack. This is needed to relink the list after removing this entry.
4EB0
DEC HL 2B
Move HL back to the low byte of this entry's file pointer.
4EB1
LD C,(HL) 4E
Load the low byte of the file pointer (the address where the original byte needs to be restored) into Register C.
4EB2
INC HL 23
Advance to the high byte of the file pointer.
4EB3
LD B,(HL) 46
Load the high byte of the file pointer into Register B. BC now holds the memory address where the original byte must be restored.
4EB4
INC HL 23
Advance HL to the saved original byte within this entry.
4EB5
LD A,(HL) 7E
Fetch the saved original byte into Register A. This is the byte that was replaced when the file pointer redirection was installed.
4EB6
LD (BC),A 02
Write the original byte back to the memory location in BC, restoring the file pointer to its original state before the redirection was installed.
4EB7
INC HL 23
Advance HL to the low byte of this entry's forward pointer (the next entry in the chain).
4EB8
LD C,(HL) 4E
Load the low byte of the forward pointer into Register C.
4EB9
LD A,FFH 3E FF
Load Register A with FFH. This will be used to mark the entry as inactive.
4EBB
LD (HL),A 77
Store FFH into the low byte of the forward pointer, marking the first half of the entry as inactive (FFFFH pattern).
4EBC
INC HL 23
Advance HL to the high byte of the forward pointer.
4EBD
LD B,(HL) 46
Load the high byte of the forward pointer into Register B. BC now holds the address of the next entry in the chain (after the one being removed).
4EBE
LD (HL),A 77
Store FFH into the high byte of the forward pointer. The removed entry's forward pointer is now FFFFH, indicating it is inactive/deallocated.
4EBF
POP HL E1
Restore HL from the stack. HL now points to the previous node's forward pointer field (saved as BC before the POP at 4EAFH).
4EC0
LD (HL),C 71
Store the low byte of the next entry's address (from Register C) into the previous node's forward pointer. This relinks the list to skip over the removed entry.
4EC1
INC HL 23
Advance HL to the high byte of the previous node's forward pointer.
4EC2
LD (HL),B 70
Store the high byte of the next entry's address into the previous node's forward pointer. The linked list is now relinked with the removed entry bypassed.
4EC3
DEC HL 2B
Move HL back to the low byte of the previous node's forward pointer, restoring the scanning position.
4EC4
JUMP back to 4E99H to continue scanning the rest of the linked list. More entries with the same file pointer might exist and need processing.

4EC6H - Parse Device Keyword from Command Line

This subroutine parses a device keyword (KB, DO, PR, RI, RO, NL, CLEAR, MM=) from the command line by searching the device name table at 4F53H. It uses the SYS0 keyword matching routine at 4C6AH with D=03H (3 data bytes per entry). On return, Register A contains the flags byte, and DE contains the DCB address associated with the matched device.

4EC6
LD BC,4F53H 01 53 4F
Point Register Pair BC to 4F53H - the start of the device name table. BC is used as the table base pointer by the keyword search routine.
4EC9
LD D,03H 16 03
Load Register D with 03H - the number of data bytes following each NUL-terminated keyword in the table. Each device entry has 3 data bytes: a flags/attribute byte, the DCB address low byte, and the DCB address high byte.
4ECB
GOSUB to 4ED7H to search the device name table for a keyword that matches the text at the current command line position (pointed to by HL).
4ECE
PUSH BC C5
Save Register Pair BC (which now points to the data bytes of the matched entry) onto the stack.
4ECF
EX (SP),HL E3
Exchange HL with the top of the stack. HL now points to the matched entry's data bytes (former BC), and the former HL (command line position) is saved on the stack.
4ED0
LD A,(HL) 7E
Fetch the first data byte (the device flags/attribute byte) from the matched entry into Register A.
4ED1
INC HL 23
Advance HL to the DCB address low byte.
4ED2
LD E,(HL) 5E
Load the DCB address low byte into Register E.
4ED3
INC HL 23
Advance HL to the DCB address high byte.
4ED4
LD D,(HL) 56
Load the DCB address high byte into Register D. DE now holds the full 16-bit DCB address for the matched device.
4ED5
POP HL E1
Restore HL from the stack. HL is now back to the command line position (advanced past the matched keyword by the search routine).
4ED6
RET C9
RETURN to the caller with: A = device flags byte, DE = DCB address, HL = command line position after the keyword.

4ED7H - Search Keyword Table with Variable-Length Data Fields

This subroutine searches a keyword table (pointed to by BC) for a keyword that matches the text at the current command line position (HL). Each table entry consists of a NUL-terminated keyword string followed by D data bytes. On match, BC points to the first data byte. If no match, execution falls through to the error handler at 4EE9H.

4ED7
GOSUB to SYS0 routine at 4C6AH - keyword matching. This compares the command line text at HL against the keyword at BC. If matched, the Z FLAG is set and HL is advanced past the keyword. If not matched, the NZ FLAG is set.
4EDA
RET Z C8
If the Z FLAG is set (keyword matched), RETURN to the caller. BC now points to the first data byte after the matched keyword's NUL terminator.

NO MATCH - Skip to Next Entry
The keyword did not match. Advance BC past the remaining NUL-terminated name bytes, then skip D data bytes to reach the next entry.

4EDB
LD A,(BC) 0A
Fetch the current byte from the keyword table (at BC) into Register A.
4EDC
OR A B7
Test if A is 00H (NUL terminator). If so, the Z FLAG is set - end of the unmatched keyword's name has been reached.
4EDD
INC BC 03
Advance BC to the next byte in the table.
4EDE
If the NZ FLAG is set (byte was not NUL), JUMP back to 4EDBH to continue scanning past the name. [LOOP END]

SKIP DATA BYTES
BC now points just past the NUL terminator. Advance BC by D bytes to skip past the data field for this entry.

4EE0
LD E,D 5A
Copy Register D (the number of data bytes per entry) into Register E. E is used as a countdown counter.
4EE1
INC BC 03
Advance BC past one data byte.
4EE2
DEC E 1D
DECrement the byte counter E. If E reaches zero, the Z FLAG is set - all data bytes have been skipped.
4EE3
If the NZ FLAG is set (more data bytes to skip), JUMP back to 4EE1H. [LOOP END]

CHECK FOR END OF TABLE
BC now points to the start of the next entry. Check if the table has ended.

4EE5
LD A,(BC) 0A
Fetch the first byte of the next entry's name into Register A.
4EE6
OR A B7
Test if A is 00H (NUL). If so, the Z FLAG is set - the table has ended with no match.
4EE7
If the NZ FLAG is set (another entry exists), JUMP back to 4ED7H to try matching the next keyword.

4EE9H - Error Exit: Parse Error (Error 34H)

Common error exit point that reports error code 34H (parse error / illegal parameter). All command parsing failures throughout SYS14 jump here.

4EE9
LD A,34H 3E 34
Load Register A with 34H - the NEWDOS/80 error code for "parse error" (decimal 52). This is used when a command parameter is invalid, a required keyword is missing, or a number is out of range.

4EEBH - Error Exit: Return Error to DOS

Common error return point. Re-enables interrupts and jumps to the SYS0 error handler at 4409H with the error code in Register A. All error exits in SYS14 converge here.

4EEB
EI FB
Re-enable maskable interrupts. Several routines in SYS14 disable interrupts (DI) during critical operations, and this ensures they are restored before returning to DOS.
4EEC
JUMP to SYS0 error handler at 4409H. Register A contains the error code. This routine displays the error message and returns to the DOS Ready prompt.

4EEFH - Parse Numeric Value from Command Line (Decimal or Hex)

This subroutine parses a numeric value from the command line text at HL. It first attempts to parse as decimal. If the number is followed by the letter H, it re-parses as hexadecimal. The parsed 16-bit value is returned in DE. On error (no digits found, overflow, or invalid character), execution jumps to the error handler at 4F0DH.

4EEF
PUSH HL E5
Save the current command line position onto the stack. If the initial decimal parse succeeds but is followed by H, HL must be restored to re-parse as hex.
4EF0
LD B,00H 06 00
Load Register B with 00H - this sets the initial mode to decimal parsing. Bit 0 of B controls the base: 0 = decimal (×10 accumulation), 1 = hexadecimal (×16 accumulation). Bit 1 of B is set after at least one valid digit has been parsed.
4EF2
GOSUB to 4F12H to parse a number in decimal mode. On return, DE contains the parsed value. If bit 1 of B is set, at least one digit was parsed.
4EF5
LD A,(HL) 7E
Fetch the character immediately following the parsed number into Register A. This checks if the number has an H suffix indicating hexadecimal.
4EF6
SUB 41H D6 41
SUBtract 41H (ASCII A) from Register A. This normalizes hex letter digits: A→0, B→1, ... F→5, G→6, H→7.
4EF8
CP 08H FE 08
Compare the result against 08H. Characters A through H produce values 0-7, so if the result is less than 8 (CARRY set), the trailing character is a valid hex letter, which means this number should be re-parsed as hex.
4EFA
If the NC FLAG is set (trailing character is not A-H), JUMP to 4F09H to validate the decimal result and return it. The number is confirmed as decimal.

HEX SUFFIX DETECTED
The trailing character is A through H, suggesting hex notation. Re-parse the entire number as hexadecimal by restoring HL and calling the parser with B bit 0 set.

4EFC
POP HL E1
Restore Register Pair HL from the stack (the original command line position saved at 4EEFH). This rewinds the parse position to re-read the number as hex.
4EFD
LD B,01H 06 01
Load Register B with 01H - set bit 0 to enable hexadecimal parsing mode (×16 accumulation, A-F digits accepted).
4EFF
PUSH HL E5
Save the restored command line position onto the stack again (for the POP at 4F0BH).
4F00
GOSUB to 4F12H to re-parse the number in hexadecimal mode. On return, DE contains the 16-bit hex value.
4F03
LD A,(HL) 7E
Fetch the character after the hex number into Register A. This should be H (48H) to confirm the hex suffix.
4F04
CP 48H FE 48
Compare Register A against 48H (ASCII H). If the character is H, the Z FLAG is set, confirming proper hex notation.
4F06
INC HL 23
Advance HL past the H suffix (or past whatever character is there, regardless of match).
4F07
If the NZ FLAG is set (character after hex digits was NOT H), JUMP to 4F0DH to report error 2FH (illegal parameter).

4F09H - Validate Parsed Number and Return

Validates that at least one digit was parsed (bit 1 of B is set) and returns the result in DE. Cleans the saved HL from the stack.

4F09
BIT 1,B CB 48
Test bit 1 of Register B. This bit is set by the number parser at 4F2BH after at least one valid digit has been processed. If bit 1 is clear (no digits parsed), the Z FLAG is set.
4F0B
POP BC C1
Pop the saved command line position into BC (discarding it - we no longer need to re-parse). This cleans the stack.
4F0C
RET NZ C0
If the NZ FLAG is set (at least one digit was parsed), RETURN to the caller with DE = the parsed 16-bit value and HL = command line position after the number.

4F0DH - Error Exit: Illegal Parameter (Error 2FH)

Error exit for invalid numeric parameters. Reports error code 2FH (illegal parameter value, decimal 47).

4F0D
LD A,2FH 3E 2F
Load Register A with 2FH - the NEWDOS/80 error code for "illegal parameter" (decimal 47). This is used when a numeric value is out of range, missing, or improperly formatted.
4F0F
JUMP to SYS0 error handler at 4409H with error code 2FH in Register A.

4F12H - Core Number Parser (Decimal or Hexadecimal Accumulator)

Core number parsing engine. Reads ASCII digit characters from the command line at HL and accumulates them into a 16-bit value in DE. Register B bit 0 controls the base: 0 = decimal (multiply by 10 and add digit), 1 = hexadecimal (multiply by 16 and add digit). Hex mode accepts digits 0-9 and letters A-F. Sets bit 1 of B after the first valid digit. Returns when a non-digit character is encountered, with DE holding the result and HL pointing to the first non-digit.

4F12
LD DE,0000H 11 00 00
Initialize the accumulator Register Pair DE to 0000H. The parsed numeric value will be built up in DE as digits are processed.

LOOP START - Digit Accumulation
Read the next character, convert it to a digit value (0-9 for decimal, 0-15 for hex), multiply DE by the base, and add the new digit.

4F15
LD A,(HL) 7E
Fetch the current character from the command line into Register A.
4F16
SUB 30H D6 30
SUBtract 30H (ASCII 0) from Register A. This converts ASCII digits '0'-'9' to values 0-9. If the character was below '0', A underflows (value ≥ 0AH).
4F18
CP 0AH FE 0A
Compare Register A against 0AH. If A is 0-9 (valid decimal digit), the CARRY FLAG is set. If A ≥ 0AH (not a decimal digit), the NC FLAG is set.
4F1A
If the CARRY FLAG is set (A is a valid digit 0-9), JUMP to 4F26H to accumulate this digit into DE.

NOT A DECIMAL DIGIT - Check for Hex Letter
The character is not 0-9. If we are in decimal mode (B bit 0 = 0), return. If hex mode, check for A-F.

4F1C
BIT 0,B CB 40
Test bit 0 of Register B. If clear (decimal mode), the Z FLAG is set - hex letters are not accepted.
4F1E
RET Z C8
If the Z FLAG is set (decimal mode), RETURN to the caller. The non-digit character ends the number. DE contains the accumulated value.
4F1F
SUB 11H D6 11
SUBtract 11H from Register A. After the earlier SUB 30H, the character value is (char − 30H). Now subtracting 11H gives (char − 41H). For 'A' (41H): result = 0. For 'F' (46H): result = 5.
4F21
CP 06H FE 06
Compare Register A against 06H. If A is 0-5 (hex letters A-F), the CARRY FLAG is set. If A ≥ 6 (character G or above), the NC FLAG is set.
4F23
RET NC D0
If the NC FLAG is set (character is not A-F), RETURN to the caller. The non-hex character ends the number.
4F24
ADD A,0AH C6 0A
ADD 0AH to Register A. This converts the 0-5 range back to 10-15 (0AH-0FH), the proper numeric values for hex digits A-F.

ACCUMULATE DIGIT
Register A holds the digit value (0-15). Now multiply DE by the base (10 for decimal, 16 for hex) and add the new digit.

4F26
PUSH HL E5
Save the command line position onto the stack. HL is needed as a scratch register for the multiplication.
4F27
LD H,D 62
Copy the current accumulated value's high byte (D) into H.
4F28
LD L,E 6B
Copy the current accumulated value's low byte (E) into L. HL now holds a copy of the current accumulator.
4F29
LD C,A 4F
Save the new digit value from Register A into Register C for later addition.
4F2A
XOR A AF
Set Register A to 00H. A is used as the overflow/high-byte accumulator during the multiplication. If A becomes non-zero after the shifts, the 16-bit result has overflowed.
4F2B
SET 1,B CB C8
SET bit 1 of Register B. This flag indicates that at least one valid digit has been parsed. It is checked at 4F09H to verify the number is not empty.

MULTIPLY BY BASE
For decimal mode (B bit 0 = 0): compute HL = HL × 10 = (HL × 4 + HL) × 2. For hex mode (B bit 0 = 1): compute HL = HL × 16 = HL shifted left 4 times. Register A tracks overflow beyond 16 bits.

4F2D
ADD HL,HL 29
Shift HL left by 1 (HL = HL × 2). This is the first step of the multiply-by-base operation.
4F2E
ADC A,A 8F
Rotate the carry from the ADD HL,HL into A. A tracks bits that overflow past 16 bits.
4F2F
ADD HL,HL 29
Shift HL left by 1 again (HL = original × 4).
4F30
ADC A,A 8F
Rotate carry into A (tracking overflow).
4F31
BIT 0,B CB 40
Test bit 0 of Register B. If set (hex mode), take the ×16 path (two more shifts). If clear (decimal mode), take the ×10 path (add original, then one more shift).
4F33
If the Z FLAG is set (decimal mode, B bit 0 = 0), JUMP to 4F38H to execute the ×10 path: add original value (from DE), then shift once more.

HEX PATH: ×16
HL currently = original × 4. Two more left shifts give HL = original × 16.

4F35
ADD HL,HL 29
Shift HL left (HL = original × 8).
4F36
JUMP to 4F39H to execute the final shift (HL × 2 = original × 16) and add the digit.

DECIMAL PATH: ×10
HL currently = original × 4. Add the original value to get ×5, then shift once to get ×10.

4F38
ADD HL,DE 19
ADD DE (the original accumulator value) to HL. HL now = original × 4 + original = original × 5.
4F39
ADC A,A 8F
Rotate carry into A (tracking overflow from the ADD).
4F3A
ADD HL,HL 29
Shift HL left by 1. For decimal: HL = original × 10. For hex: HL = original × 16.
4F3B
ADC A,A 8F
Rotate carry into A.

ADD NEW DIGIT
HL now contains (old value × base). Add the new digit value (saved in C) to complete the accumulation.

4F3C
LD E,C 59
Load the digit value (from Register C) into Register E.
4F3D
LD D,00H 16 00
Load Register D with 00H. DE now holds the digit value as a 16-bit number (00:digit).
4F3F
ADD HL,DE 19
ADD DE (the digit value) to HL. HL now = (old value × base) + new digit.
4F40
ADC A,A 8F
Rotate carry into A. If A is non-zero after this, the 16-bit result has overflowed (number exceeds FFFFH).
4F41
EX DE,HL EB
Swap HL and DE. DE now holds the updated accumulator value. HL is freed.
4F42
POP HL E1
Restore the command line position from the stack.
4F43
If the NZ FLAG is set (A was non-zero, meaning 16-bit overflow occurred), JUMP to 4F0DH to report error 2FH (illegal parameter - number too large).
4F45
INC HL 23
Advance HL to the next character in the command line.
4F46
JUMP back to 4F15H to read the next digit. [LOOP END]

4F48H - Hex Address Format Template

A 6-byte buffer used by the device name display routine at 4E84H. It is initialized with "0000H" followed by a 03H terminator. The SYS0 hex conversion routine at 44D2H overwrites the first 4 bytes with the actual hex digits of the address being displayed. The "H" at byte 4 remains as a hex suffix, and the 03H terminator marks end-of-string for the display routine at 4467H.

4F48-4F4C
DEFM "0000H"30 30 30 30 48
Hex address display template: four ASCII 0 digits (30H each), the H suffix (48H), and the 03H end-of-string terminator. The 4E8DH code calls 44D2H to overwrite bytes 4F48H-4F4BH with the actual hex digits.
4F4D
DEFB 03H 03
Hex address display template: four ASCII 0 digits (30H each), the H suffix (48H), and the 03H end-of-string terminator. The 4E8DH code calls 44D2H to overwrite bytes 4F48H-4F4BH with the actual hex digits.

4F4EH - " TO " String for ROUTE Display

A 5-byte string " TO " followed by a 03H terminator, used by the $FILPTR display routine at 4E16H-4E1CH when showing route assignments. The routine calls SYS0 string display at 4467H with this string to print the separator between source and destination device names.

4F4E-4F51
DEFM " TO "20 54 4F 20
ASCII string " TO " (space-T-O-space) followed by a 03H end-of-string terminator.
4F52
DEFB 03H 03
ASCII string " TO " (space-T-O-space) followed by a 03H end-of-string terminator.

4F53H - Device Name Table (ROUTE Command)

This data table contains the device names recognized by the ROUTE command. Each entry consists of a NUL-terminated device name string followed by 3 data bytes: a flags/attribute byte (returned in A), and a 2-byte DCB vector address in little-endian order (returned in DE). The table is terminated by a single 00H byte. The flags byte encodes device characteristics used by the ROUTE command dispatcher. The DCB vector addresses point to the system's device control block chain locations in SYS0 RAM.

DEVICE NAME TABLE
Used by the keyword parser at 4EC6H/4ED7H. The ROUTE command syntax is: ROUTE source TO destination. Valid device names are KB (Keyboard), DO (Display), PR (Printer), RI (RS-232 In), RO (RS-232 Out), NL (Null device), CLEAR (clear all routes), and MM=nnnn (memory-mapped address).

4F53-4F55
DEFM "KB" + 00H4B 42 00
Device KB (Keyboard) + NUL terminator.
4F56-4F58
DEFB C1H,15H,40H C1 15 40
Data: Flags=C1H, DCB address=4015H. The keyboard device vector is at SYS0 address 4015H.
4F59-4F5B
DEFM "DO" + 00H 44 4F 00
Device DO (Display Output) + NUL terminator.
4F5C-4F5E
DEFB C2H,1DH,40H C2 1D 40
Data: Flags=C2H, DCB address=401DH. The display device vector is at SYS0 address 401DH.
4F5F-4F61
DEFM "PR" + 00H 50 52 00
Device PR (Printer) + NUL terminator.
4F52-4F64
DEFB C2H,25H,40H C2 25 40
Data: Flags=C2H, DCB address=4025H. The printer device vector is at SYS0 address 4025H.
4F65-4F67
DEFM "RI" + 00H 52 49 00
Device RI (RS-232 Input) + NUL terminator.
4F68-4F6A
DEFB C1H,E5H,41H C1 E5 41
Data: Flags=C1H, DCB address=41E5H. The RS-232 input device vector is at SYS0 address 41E5H.
4F6B-4F70
DEFM "RO" DEFB 00H,C2H,EDH,41H 52 4F 00 C2 ED 41
Device RO (RS-232 Output). NUL terminator at 4F6DH. Data: flags=C2H, DCB address=41EDH. The RS-232 output device vector is at SYS0 address 41EDH.
4F71-4F76
DEFM "RC" DEFB 00H,C2H,F5H,41H 52 43 00 C2 F5 41
Device RC (RS-232 Control). NUL terminator at 4F73H. Data: flags=C2H, DCB address=41F5H. The RS-232 control device vector is at SYS0 address 41F5H.
4F77-4F7C
DEFM "NL" DEFB 00H,43H,A1H,4CH 4E 4C 00 43 A1 4C
Device NL (Null Device). NUL terminator at 4F79H. Data: flags=43H, DCB address=4CA1H. The null device vector is at SYS0 address 4CA1H. Routing to NL discards all output.
4F7D-4F85
DEFM "CLEAR" DEFB 00H,90H,00H,00H 43 4C 45 41 52 00 90 00 00
Special keyword CLEAR. NUL terminator at 4F82H. Data: flags=90H, DCB address=0000H. CLEAR is not a device - it is a command to remove all active ROUTE assignments. The flags byte 90H (bit 7 set, bit 4 set) triggers the special CLEAR logic in the ROUTE command handler.
4F86-4F8C
DEFM "MM=" DEFB 00H,60H,00H,00H 4D 4D 3D 00 60 00 00
Special keyword MM= (Memory-Mapped). NUL terminator at 4F89H. Data: flags=60H, DCB address=0000H. When MM= is specified, the user provides a hex address after the equals sign (e.g., MM=7000H). The flags byte 60H (bits 6 and 5 set) triggers the numeric address parsing logic.
4F8D
DEFB 00H 00
End-of-table marker. A NUL byte as the first byte of an entry signals the keyword search routine at 4EE5H that no more entries exist.

4F8EH - ROUTE Command Handler (Sub-function 2)

The ROUTE command handler. This is reached from the main dispatcher at 4D0DH when function code F0H sub-function 2 is specified. The ROUTE command reassigns I/O device vectors so that output intended for one device is redirected to another. Syntax: ROUTE source TO destination [,PP=Y/N] [,LRL=nnn] [,REC=nnn] [,MM=addr]. If no parameters are given (just ENTER), the current route assignments are displayed.

INITIALIZE PARAMETER ACCUMULATORS
BC is used to accumulate option flags as the command parameters are parsed. B holds one set of options, C holds another. Both start at 0000H.

4F8E
LD BC,0000H 01 00 00
Initialize Register Pair BC to 0000H. BC accumulates the parsed option flags: C holds the combined device attribute bits, B holds the record count parameter.
4F91
PUSH BC C5
Save the initial BC (0000H) onto the stack. This will be the "destination device attributes" accumulator.
4F92
PUSH BC C5
Save another copy of BC (0000H). This will be the "source device attributes" accumulator.

MAIN PARAMETER PARSING LOOP
Check if there are parameters on the command line. If the line ends (CR), jump to execute the ROUTE. If there is text, parse a parameter keyword and its value.

4F93
GOSUB to SYS0 routine at 4C7AH to skip whitespace on the command line and test the next character. On return: CARRY set = error, Z FLAG set = end of line (0DH), NZ = valid character found.
4F96
If the CARRY FLAG is set (parse error from 4C7AH), JUMP to 4EEBH to report the error.
4F99
If the Z FLAG is set (end of command line reached), JUMP to 4FD1H to execute the ROUTE command with the accumulated parameters.

PARSE PARAMETER KEYWORD
There is a parameter on the command line. Use the parameter keyword table at 5050H to identify which parameter it is.

4F9B
LD BC,5050H 01 50 50
Point Register Pair BC to 5050H - the parameter keyword table for the ROUTE/CREATE commands. This table contains keywords like LRL=, REC=, OASE=, OASC=, etc.
4F9E
GOSUB to 4EC9H (entry point of the keyword parser with D=03H already set from 4EC9H). This searches the parameter table at 5050H. On return: A = attribute byte, DE = associated address/value.
4FA1
PUSH DE D5
Save DE (the parameter's associated value from the table) onto the stack.
4FA2
LD D,A 57
Copy the parameter's attribute byte into Register D. The attribute byte encodes how the parameter value should be processed (Y/N toggle, numeric value, etc.).
4FA3
RET C9
RETURN. This is a computed return - the PUSH DE at 4FA1H pushed an address onto the stack, and this RET jumps to that address. The parameter's associated address acts as a handler routine for that specific parameter type.

4FA4H - Parse Y/N Parameter Value

Parses a Y or N response from the command line for toggle-type parameters. On entry, Register D contains a bit mask from the parameter table. Y sets the corresponding bits in the accumulated flags, N clears them. Returns to the main parameter loop at 4FCFH.

4FA4
LD E,00H 1E 00
Initialize Register E to 00H. E will hold 00H if Y is parsed, or FFH if N is parsed (inverted via DEC E).
4FA6
LD A,(HL) 7E
Fetch the current character from the command line into Register A.
4FA7
CP 59H FE 59
Compare Register A against 59H (ASCII Y). If the character is Y, the Z FLAG is set.
4FA9
INC HL 23
Advance HL past the Y/N character.
4FAA
If the Z FLAG is set (character was Y), JUMP to 4FB2H with E=00H to apply the "yes" logic.
4FAC
DEC E 1D
DECrement Register E from 00H to FFH. E = FFH is used as an AND mask below to clear bits when N is specified.
4FAD
CP 4EH FE 4E
Compare Register A against 4EH (ASCII N). If the character is N, the Z FLAG is set.
4FAF
If the NZ FLAG is set (character was neither Y nor N), JUMP to 4F0DH to report error 2FH (illegal parameter).

APPLY Y/N TO FLAG ACCUMULATOR
For Y: E=00H, so D AND E = 00H, and C OR 00H = C (flags unchanged - Y means "allow", the default). For N: E=FFH, so D AND FFH = D, and C OR D = C with bits set (N means "disallow", setting the restriction bit).

4FB2
LD A,D 7A
Load Register A with the parameter's bit mask (from Register D, set at 4FA2H from the table attribute byte).
4FB3
AND E A3
AND Register A with E. If Y was parsed (E=00H), A becomes 00H. If N was parsed (E=FFH), A retains the mask value from D.
4FB4
POP BC C1
Restore the current flag accumulator from the stack.
4FB5
OR C B1
OR Register A into Register C. If N was specified, the mask bits from D are now set in C. If Y was specified, C is unchanged.
4FB6
LD C,A 4F
Store the updated flags back into Register C.
4FB7
PUSH BC C5
Save the updated flag accumulator back onto the stack.
4FB8
JUMP to 4FCFH to return to the main parameter parsing loop.

4FBAH - Parse Numeric Parameter (LRL=nnn)

Parses a numeric value for the LRL (Logical Record Length) parameter. Decrements the parsed value by 1 (since NEWDOS/80 stores LRL as value − 1 internally), validates that the high byte is zero (value must be 1-256), and stores the result. Returns to the main parameter parsing loop.

4FBA
GOSUB to 4EEFH to parse a numeric value from the command line. On return, DE contains the parsed 16-bit value.
4FBD
DEC DE 1B
DECrement DE by 1. NEWDOS/80 stores the Logical Record Length internally as (LRL − 1), so a user-specified LRL of 256 is stored as 255 (FFH), and LRL of 1 is stored as 0.
4FBE
LD A,D 7A
Load the high byte of the adjusted value into Register A.
4FBF
OR A B7
Test if D is 00H. If the original LRL was 1-256, after decrementing, D must be 00H (result 0000H-00FFH). If D is non-zero, the value was too large.
4FC0
If the NZ FLAG is set (D ≠ 0, meaning LRL > 256 or LRL = 0 which decremented to FFFFH), JUMP to 4FAFH which leads to error 2FH (illegal parameter).
4FC2
POP BC C1
Restore the flag accumulator from the stack.
4FC3
INC E 1C
INCrement E by 1. This converts the internal (LRL − 1) value back to the original value for storage in the flag accumulator. Since NEWDOS/80 stores LRL − 1 in the byte, the INC restores it so the byte accurately represents the user's intent.
4FC4
LD B,E 43
Store the LRL value (from E) into Register B of the flag accumulator.
4FC5
PUSH BC C5
Save the updated flag accumulator (with LRL in B) back onto the stack.
4FC6
JUMP to 4FCFH to return to the main parameter parsing loop.

4FC8H - Parse Numeric Parameter (REC=nnn)

Parses a numeric value for the REC (Record Count) parameter. The parsed 16-bit value in DE is pushed onto the stack as an additional data item for the CREATE command execution.

4FC8
GOSUB to 4EEFH to parse a numeric value from the command line. On return, DE contains the parsed 16-bit record count.
4FCB
POP BC C1
Pop the current flag accumulator from the stack into BC.
4FCC
POP AF F1
Pop the previous stack frame (the source device attributes) into AF. This reorders the stack to insert the REC value.
4FCD
PUSH DE D5
Push DE (the parsed record count) onto the stack as a new data item.
4FCE
PUSH BC C5
Push the flag accumulator back onto the stack, restoring the expected stack layout with the record count inserted beneath it.

4FCFH - Return to Parameter Parsing Loop

Simple loop-back point. All parameter handlers (Y/N, LRL, REC) jump here after processing their value to continue parsing the next parameter on the command line.

4FCF
JUMP back to 4F93H to skip whitespace and parse the next parameter, or detect end-of-line and proceed to execute the command.

4FD1H - Execute CREATE Command (Process Parsed Parameters)

This is the CREATE command execution entry point, reached from the parameter parsing loop at 4F99H when end-of-line is detected. At this point, the stack contains the accumulated parameter flags (B = LRL value, C = ASE/ASC flags) and the record count (from REC=). The routine opens the specified file via SYS0 routines, applies the parsed parameters to the FCB, and writes the directory entry to disk.

RETRIEVE ACCUMULATED FLAGS
Pop the parameter flags to inspect them, then push them back. The stack holds [record count, flags] at this point.

4FD1
POP BC C1
Pop the accumulated parameter flags into BC. Register B holds the LRL value (0 = default 256), Register C holds the ASE/ASC flag bits.
4FD2
PUSH BC C5
Push BC back onto the stack — the flags will be needed after the file is opened.

OPEN THE FILE
Call SYS0 to open the file specified earlier on the command line. The file spec was parsed during the pre-parameter phase.

4FD3
LD A,44H 3E 44
Load Register A with 44H (ASCII D). This is the DOS function code for "open file for directory update" — a special mode used by CREATE to modify the file's directory attributes without reading or writing file data.
4FD5
LD DE,4480H 11 80 44
Point Register Pair DE to 4480H — the system FCB work area in SYS0 RAM. This is where the file's FCB will be built.
4FD8
GOSUB to 4D28H — the SYS14 initialization/file-open wrapper. This prepares the system state and calls the appropriate SYS0 file handling routine. On return, DE points to the FCB.
4FDB
PUSH DE D5
Save DE (pointing to the FCB) onto the stack. This will be transferred to IX for indexed access to FCB fields.
4FDC
POP IX DD E1
Pop the FCB address into Index Register IX. IX now provides indexed access to all fields within the File Control Block.
4FDE
RES 7,(IX+01H) DD CB 01 BE
RESET bit 7 of the FCB byte at offset +01H. Bit 7 of the second FCB byte is the "file is open" flag. Clearing it marks the file as closed, allowing CREATE to modify the directory entry attributes without conflicting with open-file protections.

SEARCH FOR FILE IN DIRECTORY
Look up the file's directory entry on disk to determine if it already exists.

4FE2
GOSUB to SYS0 routine at 48F0H — directory search. This scans the disk directory for the file specified in the FCB. On return: Z FLAG set = file found (HL points to directory entry), NZ = file not found (A = error code).
4FE5
If the NZ FLAG is set (file not found or directory error), JUMP to 4EEBH to report the error.

APPLY ASE/ASC FLAGS TO DIRECTORY ENTRY
The file was found. HL points to its directory entry. Apply the parsed ASE and ASC flags to the directory entry's attribute byte. The flags are encoded as bit 7 = ASE=N (auto-space-extend disabled) and bit 6 = ASC=N (auto-space-close disabled).

4FE8
POP BC C1
Pop the accumulated parameter flags back into BC. C holds the ASE/ASC bits (bit 7 = ASE, bit 6 = ASC), B holds the LRL value.
4FE9
LD A,C 79
Load the ASE/ASC flag byte from Register C into Register A.
4FEA
AND C0H E6 C0
AND Register A with C0H (binary 11000000). This isolates bits 7 and 6 — the ASE and ASC flags. All other bits are cleared.
4FEC
LD C,A 4F
Store the isolated ASE/ASC flag bits back into Register C.
4FED
INC HL 23
Advance HL to the attribute byte within the directory entry (offset +1 from the entry start).
4FEE
LD A,(HL) 7E
Fetch the current attribute byte from the directory entry into Register A.
4FEF
AND 1FH E6 1F
AND Register A with 1FH (binary 00011111). This preserves the lower 5 bits of the existing attribute byte and clears the ASE/ASC bits (7 and 6) and bit 5.
4FF1
OR C B1
OR the new ASE/ASC flags (from C) into Register A, combining them with the preserved attribute bits.
4FF2
LD (HL),A 77
Write the updated attribute byte back to the directory entry. The ASE and ASC flags are now set as specified by the CREATE command.

APPLY LRL VALUE
Write the Logical Record Length to the directory entry at offset +4.

4FF3
INC HL 23
Advance HL past offset +1 to +2 in the directory entry.
4FF4
INC HL 23
Advance HL to offset +3.
4FF5
INC HL 23
Advance HL to offset +4 — the LRL byte within the directory entry.
4FF6
LD (HL),B 70
Write the LRL value (from Register B) to the directory entry at offset +4. A value of 00H means LRL=256 (stored as 0 internally).

MARK FCB FOR WRITE-BACK
Set the FCB status to indicate the directory entry has been modified and needs to be written back to disk.

4FF7
LD (IX+02H),02H DD 36 02 00
Store 02H into the FCB at offset +02H. This sets the "directory modified" flag, indicating that the directory sector must be written back to disk when the file is closed.
4FFB
GOSUB to SYS0 routine at 48C4H — write the modified directory sector back to disk. On return: Z FLAG set = success, NZ = error with code in A.
4FFE
If the NZ FLAG is set (disk write error), JUMP to 4EEBH to report the error.

ALLOCATE INITIAL RECORDS
Pop the record count from the stack. If non-zero, allocate disk space for the specified number of records.

5001
POP HL E1
Pop the record count from the stack into HL. This is the value from the REC= parameter. If REC= was not specified, HL is 0000H (from the initial PUSH BC at 4F92H).
5002
LD A,H 7C
Load the high byte of the record count into Register A.
5003
OR L B5
OR Register L into A. If HL is 0000H (no REC= specified), the Z FLAG is set.
5004
If the Z FLAG is set (REC=0 or no REC= specified), JUMP to 5047H to skip record allocation and go directly to the final close/exit.

VALIDATE AND PREPARE FOR RECORD ALLOCATION
A non-zero REC count was specified. Prepare to allocate disk space by writing empty records.

5006
LD A,B 78
Load the LRL value from Register B into Register A.
5007
OR A B7
Test if LRL is 00H (which means LRL=256). If non-zero, the NZ FLAG is set.
5008
LD C,A 4F
Copy the LRL value into Register C.
5009
If the NZ FLAG is set (LRL ≠ 256), GOSUB to SYS0 routine at 4C42H to adjust the record size setting. Only called for non-default LRL values.
500C
LD A,C 79
Load the (possibly adjusted) status value from Register C back into Register A.
500D
OR A B7
Test if C is 00H. The Z/NZ state controls whether the allocation writes full or partial records.
500E
PUSH AF F5
Save AF (A = status, flags = Z/NZ) onto the stack for the final exit at 5046H.
500F
PUSH HL E5
Save HL (the record count) onto the stack.
5010
If the NZ FLAG is set (non-default LRL), JUMP to 5013H to skip the DEC HL.
5012
DEC HL 2B
DECrement HL by 1. For default LRL=256, adjust the count down by 1 because the first record is handled separately.

SET UP FILE POINTER AND WRITE ROUTINE
Configure the system pointers for writing empty records to the file.

5013
LD (448AH),HL 22 8A 44
Store HL (the adjusted record count) into the system variable at 448AH — the file pointer/record counter.
5016
PUSH HL E5
Save HL (record count) for the write loop counter.
5017
LD HL,4439H 21 39 44
Load HL with 4439H — the address of the SYS0 "write sector" routine.
501A
LD (4D34H),HL 22 34 4D
Store the write routine address at 4D34H — a self-modifying code location within the SYS14 processing loop. The loop CALLs the address stored here to perform the actual disk I/O. ⚠ SELF-MODIFYING CODE
501D
LD HL,428BH 21 8B 42
Point HL to 428BH — a system flags byte in the DOS variable area.
5020
SET 1,(HL) CB CE
SET bit 1 at 428BH. This enables "write mode", signaling the I/O subsystem that subsequent operations write data.
5022
GOSUB to 4D27H — the SYS14 initialization entry that sets up the I/O buffer and prepares for sequential record operations.
5025
RES 1,(HL) CB 8E
RESET bit 1 at 428BH. Clear the "write mode" flag now that initialization is complete.

ZERO THE I/O BUFFER
Fill the 256-byte buffer at 4D4DH with zeros to create empty records.

5027
LD HL,4D4DH 21 4D 4D
Point HL to 4D4DH — the start of the I/O buffer for sequential file writes.
502A
LD (4483H),HL 22 83 44
Store the buffer address at 4483H — the system variable for the current I/O buffer position.
502D
XOR A AF
Set Register A to 00H — the fill value.
502E
LD B,A 47
Load B with 00H. DJNZ loops 256 times when B starts at 0 (decrements before testing).
502F
LD (HL),A 77
Store 00H at the current buffer position.
5030
INC HL 23
Advance HL to the next buffer byte.
5031
DECrement B and JUMP to 502FH if non-zero. Loops 256 times, zeroing the entire buffer. [LOOP END]

WRITE EMPTY RECORDS LOOP
Write BC empty records to the file.

5033
POP BC C1
Restore BC from the stack — the record count to write.
5034
INC BC 03
INCrement BC by 1 to compensate for the pre-decrement in the loop.
5035
LD H,A 67
Load H with 00H (A is still 0 from XOR A).
5036
LD L,A 6F
Load L with 00H. HL = 0000H.
5037
LD (448AH),HL 22 8A 44
Reset the file pointer at 448AH to 0000H — start writing from the beginning of the file.
503A
DEC BC 0B
DECrement the record counter BC.
503B
GOSUB to SYS0 at 4439H — write one zeroed record to the file. Z FLAG set = success, NZ = error.
503E
If the NZ FLAG is set (disk write error), JUMP to 4EEBH to report the error.
5041
LD A,B 78
Load B (record counter high byte) into A.
5042
OR C B1
OR C into A. If BC = 0000H (all records written), Z FLAG is set.
5043
If NZ (more records to write), JUMP back to 503AH. [LOOP END]

ALL RECORDS WRITTEN
Clean Stack

5045
POP HL E1
Discard the saved record count (pushed at 500FH).
5046
POP AF F1
Restore AF (status/flags pushed at 500EH). A = LRL status value.

5047H - CREATE Command Finalize and Return

Final exit point for the CREATE command. Stores the final file pointer and status byte, then calls the SYS0 close/return routine to finalize the file and return to the DOS command prompt.

5047
LD (448CH),HL 22 8C 44
Store HL into the system variable at 448CH — the secondary file pointer. If REC= was specified, HL reflects the final record position; otherwise HL is 0000H.
504A
LD (4488H),A 32 88 44
Store Register A into 4488H — the file status byte recording the completion status.
504D
JUMP to SYS0 at 4428H — the common file close and return-to-DOS handler. Writes remaining buffered data, updates the directory, closes the file, and returns to the DOS Ready prompt.

5050H - ROUTE/CREATE Parameter Keyword Table

This data table contains the parameter keywords accepted by the ROUTE and CREATE commands. Each entry consists of a NUL-terminated keyword string followed by 3 data bytes: an attribute/mask byte and a 2-byte handler address. The attribute byte for Y/N parameters provides the bit mask used by the Y/N parser at 4FA4H. The handler address is the routine called to process that parameter's value. The table is terminated by a single 00H byte.

5050-5057
DEFM "LRL=" DEFB 00H,BAH,4FH 4C 52 4C 3D 00 00 BA 4F
Parameter LRL= (Logical Record Length). Attribute=00H, handler=4FBAH. Dispatches to the numeric parser at 4FBAH, which reads a decimal value 1-256 and stores it internally as (LRL − 1).
5058-505F
DEFM "REC=" DEFB 00H,C8H,4FH 52 45 43 3D 00 00 C8 4F
Parameter REC= (Record Count). Attribute=00H, handler=4FC8H. Dispatches to the numeric parser at 4FC8H, which reads a 16-bit record count for initial file space allocation.
5060-5067
DEFM "ASE=" DEFB 80H,A4H,4FH 41 53 45 3D 00 80 A4 4F
Parameter ASE= (Auto Space Extend). Attribute=80H (bit 7 mask), handler=4FA4H (Y/N parser). ASE=Y allows DOS to automatically allocate more disk space as the file grows; ASE=N sets bit 7 to disable this.
5068-506F
DEFM "ASC=" DEFB 40H,A4H,4FH 41 53 43 3D 00 40 A4 4F
Parameter ASC= (Auto Space Close). Attribute=40H (bit 6 mask), handler=4FA4H (Y/N parser). ASC=Y allows the close function to deallocate unused space; ASC=N sets bit 6 to prevent this.
5070
DEFB 00H 00
End-of-table marker. A NUL byte signals the keyword search routine at 4EE5H that no more entries exist.

5071H - CLEAR Command Handler (Sub-function 5)

The CLEAR command handler. This is reached from the main dispatcher at 4D19H when function code F0H sub-function 5 is specified. The CLEAR command clears user memory, routes, timer enqueues, and *name routines. Syntax: CLEAR [,START=addr1] [,END=addr2] [,MEM=addr3]. The START= and END= parameters specify the range of memory to zero. MEM= specifies the new high-memory boundary. If no parameters are given, default values are used. The parameters are parsed by matching against the keyword table at 50CAH and storing values into self-modifying code locations within the CLEAR execution logic.

CHECK FOR END OF LINE
If no parameters follow the CLEAR command, use default values.

5071
LD A,(HL) 7E
Fetch the current character from the command line into Register A. HL points to the first character after "CLEAR" on the command line.
5072
CP 0DH FE 0D
Compare Register A against 0DH (carriage return). If the command line ends immediately after CLEAR, no parameters were specified.
5074
If the Z FLAG is set (end of line), JUMP to 508BH to execute CLEAR with default values.

PARAMETER PARSING LOOP
Parse START=, END=, and MEM= parameters. Each parameter value is stored at the self-modifying code location specified in the keyword table.

5076
LD BC,50CAH 01 CA 50
Point Register Pair BC to 50CAH — the CLEAR parameter keyword table containing START=, END=, and MEM= entries.
5079
GOSUB to 4EC9H to search the keyword table for a match. On return: A = attribute byte, DE = storage address for this parameter's value.
507C
PUSH DE D5
Save DE (the storage address where the parsed value will be written) onto the stack.
507D
GOSUB to 4EEFH to parse a numeric value from the command line. On return, DE contains the parsed 16-bit value (the address specified after the = sign).
5080
EX (SP),HL E3
Exchange HL with the top of the stack. HL now holds the storage address (from the PUSH DE at 507CH). The command line position is saved on the stack.
5081
LD (HL),E 73
Store the low byte of the parsed value (Register E) into the storage location. This writes into the self-modifying code within the CLEAR execution logic. ⚠ SELF-MODIFYING CODE
5082
INC HL 23
Advance HL to the next byte of the storage location.
5083
LD (HL),D 72
Store the high byte of the parsed value (Register D) into the storage location. The full 16-bit parameter value is now written into the self-modifying instruction's operand field.
5084
POP HL E1
Restore HL (the command line position) from the stack.
5085
GOSUB to SYS0 at 4C7AH to skip whitespace and test the next character. CARRY = error, Z = end of line, NZ = more parameters.
5088
RET C D8
If the CARRY FLAG is set (parse error), RETURN to the error handler up the call chain.
5089
If the NZ FLAG is set (more parameters on the command line), JUMP back to 5076H to parse the next parameter. [LOOP END]

508BH - CLEAR Command Execution

Executes the CLEAR operation after all parameters have been parsed (or with defaults if none were specified). This routine: (1) calls 4E01H to display current settings, (2) initializes file and timer pointers to zero, (3) validates the START, END, and MEM addresses, (4) zeros the specified memory range, and (5) returns to DOS. Three instruction operands within this code are patched by the parameter parser at 5081H-5083H: the START address at 50B5H, the END address at 50AEH, and the MEM address at 509EH.

DISPLAY CURRENT SETTINGS AND RESET POINTERS

508B
GOSUB to 4E01H to display the current CLEAR settings and route assignments to the screen.
508E
LD HL,0000H 21 00 00
Load HL with 0000H.
5091
LD (4058H),HL 22 58 40
Store 0000H at 4058H — clear the file allocation pointer. This resets the file system's free-space tracking.
5094
LD HL,0000H 21 00 00
Load HL with 0000H again.
5097
LD (4584H),HL 22 84 45
Store 0000H at 4584H — clear the timer/enqueue pointer. This dequeues all active timer and *name routines.

VALIDATE AND SET MEM= BOUNDARY
Get the current memory pointer, compare against the MEM= value, and set the new memory ceiling. The MEM= value is stored at 509EH by the self-modifying parameter store.

509A
LD HL,(42C9H) 2A C9 42
Load HL from system variable 42C9H — the current user-memory pointer (top of available RAM).
509D
LD DE,FFFFH 11 FF FF
Load DE with FFFFH — the default MEM= value (meaning "no change"). NOTE: The two bytes at 509EH-509FH are the target of self-modifying code: if MEM= is specified on the command line, the parameter parser at 5081H stores the user's address here, replacing the FFFFH default. ⚠ SELF-MODIFYING CODE at 509EH
50A0
LD A,D 7A
Load the high byte of the MEM= value into Register A.
50A1
CP 70H FE 70
Compare against 70H. If D < 70H, the MEM address is below 7000H, which is too low (below the DOS/system area). CARRY is set if D < 70H.
50A3
If the CARRY FLAG is set (MEM= address below 7000H), JUMP to 4F0DH to report error 2FH (illegal parameter). Memory below 7000H is reserved for the operating system.
50A6
RST 18H DF
Call RST 18H — the ROM compare routine. Compares HL against DE. Sets CARRY if HL < DE.
50A7
If the CARRY FLAG is set (current memory pointer < MEM= value), JUMP to 50AAH to keep the current pointer (don't extend memory beyond what's available).
50A9
EX DE,HL EB
Swap DE and HL. HL now contains the MEM= value (the new memory ceiling), replacing the current pointer.
50AA
LD (4411H),HL 22 11 44
Store HL into 4411H — the system high-memory limit. This sets the new ceiling for user memory allocation.

VALIDATE END= ADDRESS
The END= value defaults to FFFFH. If specified, validate it against the current memory pointer.

50AD
LD DE,FFFFH 11 FF FF
Load DE with FFFFH — the default END= value. NOTE: Bytes 50AEH-50AFH are a self-modifying target: the parameter parser stores the user's END= address here if specified. ⚠ SELF-MODIFYING CODE at 50AEH
50B0
RST 18H DF
Call RST 18H — compare HL against DE. CARRY if HL < DE, NC if HL ≥ DE.
50B1
If the NC FLAG is set (current memory ≥ END= value), JUMP to 50B4H — the END value is within range, use it.
50B3
EX DE,HL EB
Swap DE and HL. HL now holds the END= address (clamped to available memory).

VALIDATE START= ADDRESS AND ZERO MEMORY
The START= value defaults to 5200H. Validate that START ≤ END, then fill the range with zeros.

50B4
LD HL,5200H 21 00 52
Load HL with 5200H — the default START= address. This is the base of user memory (just above the SYS14 overlay area). NOTE: Bytes 50B5H-50B6H are a self-modifying target: the parameter parser stores the user's START= address here if specified. ⚠ SELF-MODIFYING CODE at 50B5H
50B7
LD A,H 7C
Load the high byte of the START= address into Register A.
50B8
CP 52H FE 52
Compare against 52H. If the START address high byte is below 52H, the START address is within the SYS14 overlay area (below 5200H), which must not be overwritten.
50BA
If the CARRY FLAG is set (START < 5200H), JUMP to 4F0DH to report error 2FH (illegal parameter — cannot zero system memory).
50BD
DEC HL 2B
DECrement HL (START address) by 1 for the comparison below. RST 18H compares HL against DE, and we need to check if START − 1 < END (i.e., START ≤ END).
50BE
RST 18H DF
Call RST 18H — compare (START − 1) against DE (END address). CARRY if START − 1 < END (valid range), NC if START > END (invalid).
50BF
If the NC FLAG is set (START > END, invalid range), JUMP to 4F0DH to report error 2FH.

ZERO MEMORY LOOP
Fill memory from START to END with 00H bytes.

50C2
INC HL 23
Advance HL to the next memory address in the range to clear.
50C3
LD (HL),00H 36 00
Store 00H at the current memory address, zeroing it.
50C5
RST 18H DF
Call RST 18H — compare HL against DE (END address). CARRY if HL < DE (more bytes to clear).
50C6
If the CARRY FLAG is set (HL < END, more memory to zero), JUMP back to 50C2H. [LOOP END]
50C8
XOR A AF
Set Register A to 00H — clear the error code to indicate successful completion.
50C9
RET C9
RETURN to the caller with A = 00H (no error). The CLEAR command has completed successfully.

50CAH - CLEAR Parameter Keyword Table

This data table contains the parameter keywords accepted by the CLEAR command. Each entry consists of a NUL-terminated keyword string followed by 3 data bytes: an attribute byte (unused, always 00H) and a 2-byte storage address where the parsed parameter value will be written. These storage addresses point to self-modifying code locations within the CLEAR execution logic at 508BH-50C9H. The table is terminated by a single 00H byte.

50CA-50D3
DEFM "START=" DEFB 00H,B5H,50H 53 54 41 52 54 3D 00 00 B5 50
Parameter START=. Attribute=00H, storage=50B5H. When parsed, the user's start address is written to bytes 50B5H-50B6H, which are the operand of the LD HL,5200H instruction at 50B4H. This changes the default start address from 5200H to the user-specified value.
50D4-50DB
DEFM "END=" DEFB 00H,AEH,50H 45 4E 44 3D 00 00 AE 50
Parameter END=. Attribute=00H, storage=50AEH. When parsed, the user's end address is written to bytes 50AEH-50AFH, which are the operand of the LD DE,FFFFH instruction at 50ADH. This changes the default end address from FFFFH to the user-specified value.
50DC-50E3
DEFM "MEM=" DEFB 00H,9EH,50H 4D 45 4D 3D 00 00 9E 50
Parameter MEM=. Attribute=00H, storage=509EH. When parsed, the user's memory ceiling address is written to bytes 509EH-509FH, which are the operand of the LD DE,FFFFH instruction at 509DH. This changes the default MEM value from FFFFH to the user-specified boundary.
50E4
DEFB 00H 00
End-of-table marker. A NUL byte signals the keyword search routine that no more entries exist.

50E5H - $RAMDIR Command Handler (Sub-function 6)

The $RAMDIR command handler. This is reached from the main dispatcher when function code F0H sub-function 6 is specified. $RAMDIR is a system-level directory display command that reads the disk directory and displays it to the screen with filtering support. It sets up a semicolon terminator for output formatting, then invokes the display engine with keyboard interaction for browsing through directory entries. The routine supports BREAK key detection and handles both the ENTER and SHIFT+CLEAR key combinations for navigation.

INITIALIZE OUTPUT FORMAT
Set the output separator character to semicolon (3BH) by patching the character output routine.

50E5
LD A,3BH 3E 3B
Load Register A with 3BH (ASCII semicolon ;). This will be stored as the output field separator character for directory display formatting.
50E7
LD (5182H),A 32 82 51
Store the semicolon character at 5182H — a self-modifying code location within the character output subroutine at 517FH. This patches the LD A,0DH instruction at 5181H to become LD A,3BH, changing the output separator from carriage return to semicolon. ⚠ SELF-MODIFYING CODE at 5182H

SET UP DISPLAY PARAMETERS
Restore state from the stack and prepare the display engine.

50EA
POP AF F1
Restore AF from the stack (saved by the dispatcher). Contains status flags from the pre-processing phase.
50EB
POP HL E1
Restore HL from the stack. HL points to the current position in the command line or data buffer.
50EC
GOSUB to SYS0 at 4C7AH to skip whitespace and test the next character.
50EF
LD DE,0001H 11 01 00
Load DE with 0001H — the default starting record number (first directory entry).
50F2
If the NZ FLAG is set (parameter present on command line), GOSUB to 516FH to parse a numeric starting position. This allows the user to specify which directory entry to begin displaying from.
50F5
PUSH DE D5
Save DE (the starting record number) onto the stack.
50F6
LD DE,FFFFH 11 FF FF
Load DE with FFFFH — the default ending record number (display all entries).
50F9
If the NZ FLAG is set (another parameter present), GOSUB to 516FH to parse the ending record number.
50FC
If the NZ FLAG is set (additional unexpected parameters remain), JUMP to 517AH to report error 2FH (illegal parameter).
50FE
POP BC C1
Restore BC from the stack — the starting record number.

INITIALIZE DIRECTORY BUFFER
Set up the I/O buffer for reading directory sectors.

50FF
LD HL,4300H 21 00 43
Point HL to 4300H — a system buffer area used as the directory I/O buffer.
5102
LD (4483H),HL 22 83 44
Store the buffer address at 4483H — the system I/O buffer pointer.

SKIP TO STARTING ENTRY
If the starting record number is greater than 1, skip past that many directory entries without displaying them.

5105
DEC BC 0B
DECrement BC (the entries to skip). When BC reaches 0, we start displaying.
5106
LD A,B 78
Load B into A.
5107
OR C B1
OR C into A. If BC = 0, Z FLAG is set — done skipping.
5108
If Z (skipping done), JUMP to 5113H to begin the display loop.
510A
GOSUB to 513AH — read the next character from the directory. This advances through directory data without displaying it.
510D
CP 0DH FE 0D
Compare Register A against 0DH (carriage return). CR marks the end of a directory entry.
510F
If NZ (not end of entry), JUMP back to 510AH to continue reading this entry.
5111
JUMP back to 5105H to decrement the skip counter and check if more entries need to be skipped.

DISPLAY LOOP
Read and display directory entries, one character at a time. Check for user keyboard interaction after each entry.

5113
GOSUB to 513AH — read the next character from the directory buffer.
5116
GOSUB to 517FH — output the character in Register A to the display. This routine preserves DE and AF across the CALL 0033H ROM output.
5119
DEC C 0D
DECrement Register C. C is used as a line-length counter to track characters displayed on the current line.
511A
If the Z FLAG is set (C reached 0, a full entry has been displayed), JUMP to 5125H to check for user key presses.
511C
CP 0DH FE 0D
Compare A against 0DH (carriage return). CR marks the end of a directory entry.
511E
If NZ (not end of entry), JUMP back to 5113H to continue displaying characters.

END OF ENTRY
Decrement Count and Check for More

5120
DEC DE 1B
DECrement DE — the remaining entries to display counter.
5121
LD A,D 7A
Load D into A.
5122
OR E B3
OR E into A. If DE = 0, all requested entries have been displayed.
5123
If Z (no more entries to display), JUMP to 516AH to output a final carriage return and return.

CHECK KEYBOARD FOR USER INTERACTION
Scan the keyboard matrix at 3840H for special key combinations.

5125
LD C,00H 0E 00
Reset the line counter C to 00H for the next entry.
5127
LD A,(3840H) 3A 40 38
Read the keyboard matrix byte at 3840H. This memory-mapped location reflects the state of a specific row of keys on the TRS-80 keyboard.
512A
AND 48H E6 48
AND with 48H (binary 01001000). This tests bit 6 (ENTER key) and bit 3 (BREAK key). If neither key is pressed, the result is 00H.
512C
If Z (no special keys pressed), JUMP back to 5113H to continue displaying the next entry.

KEY DETECTED
Wait for Release, Then Check Which Key

512E
LD A,(3840H) 3A 40 38
Read the keyboard matrix again to wait for key release.
5131
AND 09H E6 09
AND with 09H (binary 00001001). This tests bit 3 (BREAK) and bit 0 (SHIFT). These keys stop or abort the display.
5133
If Z (keys still held), JUMP back to 512EH to keep waiting. This debounces the key press.
5135
RRCA 0F
Rotate Register A right through carry. Bit 0 (SHIFT state) moves into the CARRY FLAG.
5136
If the NC FLAG is set (SHIFT was not pressed — BREAK alone was pressed), JUMP to 516AH to abort the display and return.
5138
SHIFT was pressed (SHIFT+BREAK = continue after pause). JUMP back to 5113H to resume directory display.

513AH - Read Next Character from Directory Buffer

Reads the next character from the directory listing via the DOS character input routine. Handles BREAK key detection and filters control characters, replacing non-printable characters with a period. On return, Register A contains the displayable character.

513A
LD A,(3840H) 3A 40 38
Read the keyboard matrix at 3840H to check for BREAK key before reading the next character.
513D
AND 08H E6 08
AND with 08H to test bit 3 (BREAK key). If BREAK is pressed, the result is non-zero.
513F
If NZ (BREAK pressed), JUMP to 5169H to abort — pops the return address and exits the display loop.
5141
PUSH DE D5
Save DE (the entry counter) before the DOS call which may modify it.
5142
LD DE,4480H 11 80 44
Point DE to 4480H — the FCB work area, required as a parameter for the ROM character input routine.
5145
GOSUB to ROM routine at 0013H — read the next character from the current input device. On return: A = character, Z FLAG set = normal character, NZ = special condition.
5148
POP DE D1
Restore DE (the entry counter).
5149
If NZ (special condition from ROM input — typically end-of-file or error), JUMP to 5160H to handle the special case.

FILTER CHARACTER
Ensure the character is printable. Non-printable characters below space (20H) are replaced with a period (2EH), except for carriage return (0DH) which is passed through.

514B
AND 7FH E6 7F
AND with 7FH to strip the high bit (bit 7). This converts any extended ASCII to standard 7-bit ASCII.
514D
PUSH HL E5
Save HL before the comparison.
514E
LD HL,4290H 21 90 42
Point HL to 4290H — a system variable that holds the maximum printable character code (typically 7FH or less).
5151
CP (HL) BE
Compare Register A against the maximum printable character code at (4290H).
5152
POP HL E1
Restore HL.
5153
If CARRY (A < max printable), JUMP to 5157H to check the lower bound.
5155
If NZ (A > max printable), JUMP to 515DH to replace with period.
5157
CP 20H FE 20
Compare A against 20H (ASCII space). Characters ≥ 20H are printable.
5159
RET NC D0
If NC (A ≥ 20H, printable character), RETURN with the character in A.
515A
CP 0DH FE 0D
Compare A against 0DH (carriage return). CR is a control character but must be passed through as an end-of-entry marker.
515C
RET Z C8
If Z (character is CR), RETURN with A = 0DH.
515D
LD A,2EH 3E 2E
Load Register A with 2EH (ASCII period .). All non-printable, non-CR characters are replaced with a period for clean display.
515F
RET C9
RETURN with A = 2EH (period).

5160H - Handle Special Input Condition (EOF/Error)

Handles special conditions returned by the ROM character input at 0013H. Checks for end-of-file (1CH) and end-of-section (1DH) markers. End-of-file causes an abort (same as BREAK). End-of-section is an error. Other special codes also trigger an error.

5160
CP 1CH FE 1C
Compare A against 1CH — the end-of-file marker from the ROM input routine.
5162
If Z (end-of-file reached), JUMP to 5169H to abort the display — all directory data has been read.
5164
CP 1DH FE 1D
Compare Register A against 1DH (cursor-right control code). If Register A equals 1DH, the Z FLAG is set; otherwise the NZ FLAG is set.
5166
If NZ (Register A is neither 1CH nor 1DH — an invalid control character), JUMP to SYS0 error dispatcher at 4EEBH which loads error code 34H (Parse Error) and aborts.

If we reach 5169H, the character in Register A was either 1CH (cursor-left) or 1DH (cursor-right). Both are treated as end-of-input signals. The routine discards the AF that was saved on the stack before the input sub-call and falls through to the shared CR-output exit at 516AH.

5169
POP AF F1
Restore Register AF from the stack, discarding the character and flags that were saved before the input sub-call. This balances the stack frame before the exit.
516A
GOSUB to 517DH to output a Carriage Return (0DH) to the currently active output route, terminating the display line before returning to the caller.
516D
XOR A AF
Set Register A to zero and clear all flags. Register A = 00H signals a successful, clean exit from the CLEAR command input loop.
516E
RET C9
Return to caller with Register A = 00H indicating no error — normal exit from this input path.

516FH – CLEAR Command: Parameter Value Store with Bounds Check

Shared utility called after a hex address parameter has been parsed. Checks for carry (overflow) from the parser, then calls the SYS0 parameter-store routine at 4EEFH. On return, tests whether DE is zero and either loops back to the main CLEAR dispatch or issues an error.

516F
If CARRY is set (the hex parser detected an overflow or invalid value in DE), JUMP to SYS0 at 4EE9H which loads error code 34H (Parse Error) into Register A and jumps to the error dispatcher at 4409H.
5172
GOSUB to SYS0 routine at 4EEFH to store the 16-bit parsed address value currently in Register pair DE into the appropriate CLEAR command parameter slot.
5175
LD A,D 7A
Load Register A with the high byte of Register pair DE. Register DE holds the 16-bit address value that was just stored. This begins a zero-check on DE.
5176
OR E B3
Bitwise OR Register A (high byte of DE) with Register E (low byte of DE). If DE = 0000H, Register A becomes 00H and the Z FLAG is set. If DE is any non-zero address, the NZ FLAG is set.
5177
If NZ (Register pair DE is non-zero, meaning a valid address was successfully stored), JUMP to SYS0 at 4C7AH to continue processing the next CLEAR command parameter.
517A
If Z (Register pair DE was zero — no valid address was parsed), JUMP to SYS0 at 4F0DH to issue error code 2FH (invalid parameter) and abort the CLEAR command.

517DH – CLEAR Command: Output CARRIAGE RETURN Utility

A small shared subroutine that outputs a single Carriage Return character (0DH) to the currently active output route. Saves Register DE and Register AF around the ROM display call so the caller's values are fully preserved on return. Note: the address byte at 5182H is subject to self-modification by code at 50E7H.

517D
LD A,0DH 3E 0D
Load Register A with 0DH (ASCII Carriage Return). This is the character that will be sent to the output device to terminate the current display line.
517F
PUSH DE D5
Save Register pair DE onto the stack. Register DE currently holds the parsed address value or remaining character count used by the CLEAR command, which must be preserved across the ROM output call.
5180
PUSH AF F5
Save Register AF onto the stack. Register A holds 0DH (Carriage Return) and the current flags. The ROM character output routine at 0033H may alter both.
5181
GOSUB to ROM routine at 0033H to output the character in Register A (0DH = Carriage Return) to the display at the current cursor position and advance the cursor. [SELF-MODIFYING CODE] — The byte at address 5182H (the low byte of this CALL's target address) is overwritten by code at 50E7H with value 3BH, changing this CALL's target from 0033H to 003BH when that modification is active, redirecting output to an alternate ROM entry point.
5184
POP AF F1
Restore Register AF from the stack. Register A is restored to 0DH and the flags are restored to their state before the ROM call.
5185
POP DE D1
Restore Register pair DE from the stack. Register DE is restored to the parsed address value or character count that was saved before the ROM call.
5186
RET C9
Return to caller. The Carriage Return has been output to the display; Register DE and Register AF are fully restored to their pre-call values.

Addresses 5187H through 51E7H contain 97 consecutive NOP bytes (00H). These are unused padding bytes filling the remainder of the SYS5 overlay module to its fixed-length page boundary. No code executes in this region.

5187–51E7
NOP ×97 00
Unused padding bytes (all 00H = NOP). These fill the remainder of the SYS5 overlay module to its fixed 512-byte page boundary. No executable code exists in this range.