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

Page Customization

SYS20/SYS - NEWDOS/80 v2.0 BASIC Extension Overlay (Model III)

Program Overview
SYS20/SYS is one of the BASIC/CMD overlay modules for NEWDOS/80 v2.0 on the TRS-80 Model III. It occupies the shared overlay space at 5200H-56E7H and is loaded on demand when any of its resident statements or functions are invoked. SYS20 contains a diverse collection of BASIC statement handlers and functions that extend the ROM BASIC capabilities with disk I/O features.

The overlay provides seven distinct functional components: (1) the OPEN statement parameter decoder, which parses the five disk I/O modes (Input, Output, Extended, Random, and Direct access) and configures the FCB accordingly; (2) the LSET and RSET string assignment handlers for left-justified and right-justified field assignment within random-access file buffers; (3) the MID$ assignment statement (left-side MID$) for substring replacement within string variables; (4) the HEX$ function for converting numeric values to hexadecimal string representation; (5) the INSTR function for searching within strings; (6) the INPUT# statement handler for reading comma-separated fields from sequential disk files with full quote-delimited field support; and (7) a memory integrity checker that uses XOR-based fill/verify with self-relocating code to test RAM without destroying the overlay itself.

Like its companion overlays SYS18 and SYS19, SYS20 begins with a two-byte presence test stub at 5200H (CP A / RET) that returns with the Z flag set, allowing callers to detect whether the overlay is currently loaded. Only one overlay can occupy the 5200H-56E7H address range at a time; loading SYS20 overwrites any previously resident overlay (SYS18, SYS19, or SYS21).

The CMD“F=” keyword dispatcher at 52E2H handles the extended file command syntax, routing to subcommand handlers via a keyword table at 592BH (in the main BASIC/CMD module). This allows commands like CMD“F=S” (which routes to 4405H in SYS0) and CMD“F=xxx” for other file subcommands.

The memory integrity checker at 5223H-52E1H is a self-relocating routine: it copies itself from the overlay area into high memory (above 7100H), then tests the entire address range from 0000H through the top of memory using an XOR A5H fill-and-verify pattern. The XOR approach allows testing without permanently destroying the tested memory contents - each byte is XORed with A5H, then XORed again to restore the original value. The routine relocates to avoid testing memory that contains the test code itself.

Memory Map

Address ranges and functional areas within SYS20/SYS.

Address Range SizeFunction
5200H-5201H2Overlay presence test stub (CP A / RET)
5202H-5221H32Main entry: OPEN/CMD dispatcher with DOS-CALL (4419H) and error routing
5223H-5299H119Memory integrity checker: stack setup, XOR fill/verify, self-relocation to 7100H+, and post-test FCB restore via SVC
529AH-52E1H72Memory test subroutines: A5H fill loop, XOR cipher, page-boundary comparator, and verify loop
52E2H-5315H52CMD“F=” keyword dispatcher: routes to subcommand handlers via 592BH table lookup
5316H-5348H51NEXT/FOR stack frame handler: stack pointer relocation and variable passing (CMD“F”,NEXT)
534AH-545CH275LSET / RSET / MID$ (left-side) statement handlers: string field assignment for random-access buffers and substring replacement
545DH-5503H167HEX$ function (hex string conversion) and INSTR function (string search)
5504H-55D3H208OPEN statement parameter decoder: parses file mode (I/O/E/R/D), record length, and field width, stores to FCB work area at 5700H
55D4H-55E7H20INPUT# statement handler entry: guard flag save, expression type check, dispatch to field reader
55E8H-5606H31PRINT# / PRINT USING# extension: string expression evaluation with ROM PRINT dispatch
560EH-561FH18LINE INPUT# handler: file setup, FIELD evaluation, and ROM 2169H cleanup dispatch
5621H-56ADH141INPUT# field reader: CSV parser with quote-delimited field support, CR/LF handling, comma separation, and overflow detection
56AEH-56E7H58NOP padding (unused overlay space)

SYS20 - Variables and Self-Modifying Code

AddressInitial ValueWritten ByPurpose
5272H0000H5236HAvailable memory size (SP − string stack end), used by memory test to determine test range
5275HPart of LD HL,(5275H) at 5245HExternal / contextBase address for memory test source pointer calculation
5278HPart of LD BC,(5278H) at 522FHExternal / contextMemory test comparison base (overlay load address or similar)
528FH-5290H0000H5224HSaved SP value; restored after memory test completes at 528FH (operand of LD SP,0000H)
52C9H0000H522CHRelocated code destination address (SP − 192), base of the relocated test routine in high memory
5386H0000H5368H, 53AAHSaved HL pointer for LSET/RSET/MID$ target string descriptor address
5716H(FCB work area)5691HPosition parameter byte - set to 01H when INPUT# encounters an unquoted, non-comma, non-CR field terminator (signals partial record read)

Major Routine Reference

Entry points, subroutines, and dispatch targets within SYS20/SYS, with cross-references to callers and the BASIC/CMD jump table.

AddressNameDescription
5200HOverlay StubCP A / RET - presence test, returns Z flag set
5202HMain EntryOPEN/CMD dispatcher: POP BC, extract HL, check for ‘=’ (3DH), set 428AH bit 7, DOS-CALL 4419H, error routing
5223HMemory Test SetupSave SP, calculate relocated destination (SP − 192), compute available memory, check for out-of-memory
5251HMemory Test RelocatorCopy overlay code via LDIR to high memory, then jump to relocated copy
5271HRelocated Memory Test CoreZero-fill source, LDIR copy overlay to 5200H, jump to relocated 527FH
527FHPost-Relocation EntrySet SP to 43FCH, call XOR test, restore SP, check for errors
529AHMemory Fill (A5H)Fill 64 bytes at (52C9H) with A5H pattern
52A4HXOR Memory TestXOR-cipher 7100H range with relocated base, verify 64-byte integrity block
52C8HXOR Cipher EngineXOR each byte at (HL) with corresponding byte at (DE), page-boundary loop with AF′ counter
52E2HCMD“F=” DispatcherParse character after ‘=’: ‘S’ routes to 4405H, ‘F’ routes to keyword table at 592BH
5316HStack Frame HandlerFOR/NEXT stack cleanup: set return to 5346H, relocate SP below 40A0H, dispatch to 1D25H
5324HFOR Statement ExtensionSearch FOR stack via ROM 1936H, check for token 91H, ADD HL,BC+4 for stack frame skip
5334HVariable PassingEvaluate optional string expression, check token via ROM 1936H, dispatch to 1D25H
534AHLSET/RSET EntryEvaluate file number (5824H), push context, read string descriptor, check for ‘(’ (MID$ path)
535FHMID$ Assignment PathRST 08H ⇒ 28H, evaluate second file# (5831H), store descriptor, call ROM 2888H or 5CB7H
539EHLSET/RSET Common PathRST 08H ⇒ D5H, evaluate string via ROM 2337H, check end-of-statement, dispatch to ROM 2819H
53F2HINSTR FunctionRST 10H, evaluate string via ROM 2335H, RST 20H type check, search via ROM 2B1FH
5462HHEX$ Function EntryRST 10H, RST 08H ⇒ 28H, parse hex digits, accumulate in DE via shift-and-add
54C5HHex Digit ParserConvert ASCII hex digit to binary: 0-9 and A-F, with overflow check
5504HOPEN Mode DecoderEvaluate string expression (6190H), check single-character mode: I (0128H), O (8448H), E (8248H), R (80ECH), D (01ECH)
5531HOPEN Parameter StoreEX (SP),HL to save mode word, parse file number (62C0H), set up FCB (6087H), parse record length
55C9HOPEN FinalizeStore DE to 5702H (record offset), store BC to 5700H (FCB status), JP 2169H (ROM cleanup)
55D4HINPUT# EntrySave guard flag (6382H), RST 20H type check, set D=03H (string) or D=05H (numeric), dispatch to field reader
55E8HPRINT# ExtensionRST 08H ⇒ 89H (USING token), call ROM 2828H, check for ‘#’, dispatch to 560EH or ROM PRINT path
560EHLINE INPUT# HandlerCall 5F36H (file setup), 5E7BH (FIELD eval), push ROM 2169H return, dispatch to 5621H
5621HINPUT# Field ReaderCSV parser: read bytes via 60F2H, handle spaces (leading skip), quotes (toggle mode), CR/LF (record end), commas (field separator), with 256-byte buffer overflow detection
56A7HFile Read HelperCall 60F2H, RET Z on EOF, else POP AF and jump to field termination at 569AH

Disassembly

5200H - Overlay Presence Test Stub

Two-byte stub that allows callers to detect whether SYS20 is currently loaded in the overlay space. CALL 5200H returns with the Z flag set if SYS20 is resident.

5200
CP A BF
Compare Register A against itself. This always sets the Z FLAG (since any value equals itself) and clears the Carry flag. This is the first byte of the overlay presence test - callers execute CALL 5200H and then test the Z flag to determine if SYS20 is loaded.
5201
RET C9
Return to the caller. The Z flag is set (from the CP A above), so the caller knows SYS20 is resident. If the overlay were not loaded, whatever byte happened to be at 5200H would execute instead, and the Z flag test would fail.

5202H - Main Entry: OPEN / CMD Dispatcher

Primary entry point for SYS20. This routine handles the OPEN statement and CMD command dispatching. It receives context on the stack and in HL, checks whether the command includes an ‘=’ sign (for CMD“F=” syntax), and either dispatches to the CMD“F=” handler or executes the command via DOS-CALL (SVC 4419H).

On entry, the stack contains a return address pushed by the caller, and HL points into the command text being parsed. The routine saves the current position in DE, advances HL to check the next character, and branches based on whether it finds an ‘=’ sign.

5202
POP BC C1
Restore Register Pair BC from the stack. This pops the return address or context value that was pushed by the calling routine, freeing the stack for the upcoming DOS-CALL.
5203
LD D,H 54
Load Register D with the high byte of Register Pair HL (the current command text pointer). This saves the current parse position in DE for later use.
5204
LD E,L 5D
Load Register E with the low byte of Register Pair HL, completing the copy of the command text pointer into DE. Register Pair DE now holds the same address as HL.
5205
INC HL 23
INCrement Register Pair HL by 1, advancing the parse pointer past the current character to look at the next character in the command text.
5206
LD A,(HL) 7E
Fetch the next character from the command text (pointed to by HL) into Register A. This is checking for an ‘=’ sign that would indicate CMD“F=xxx” syntax.
5207
CP 3DH FE 3D
Compare Register A against 3DH (ASCII =). If Register A equals 3DH, the Z FLAG is set, indicating this is a CMD“F=” command; otherwise the NZ FLAG is set.
5209
If the Z FLAG has been set (the character is =, indicating CMD“F=xxx” syntax), JUMP to 52E2H to enter the CMD“F=” keyword dispatcher, which will parse the subcommand keyword and route to the appropriate handler.

Standard Command Path
If the character is not ‘=’, this is a regular DOS command. The routine sets bit 7 of 428AH (the DOS state flags) to indicate that a BASIC-initiated DOS command is in progress, executes the command via DOS-CALL at 4419H, then clears the bit and processes the return status.

520C
LD HL,428AH 21 8A 42
Point Register Pair HL to 428AH, the DOS state flags byte. Bit 7 of this byte is used to signal that a BASIC-initiated DOS command is active.
520F
SET 7,(HL) CB FE
SET bit 7 of the byte at (HL), which is 428AH. This marks the DOS state as “BASIC DOS command in progress” so that the DOS knows to return control to BASIC after the command completes rather than going to the standard DOS command loop.
5211
PUSH HL E5
Save Register Pair HL (pointing to 428AH) onto the stack. This is preserved so the routine can clear bit 7 after the DOS command returns.
5212
EX DE,HL EB
Exchange DE and HL. Register Pair HL now holds the saved command text pointer (from 5203H-5204H), and DE holds 428AH. The DOS-CALL at 4419H expects the command text address in HL.
5213
DOS-CALL. Execute a DOS command and return. GOSUB to 4419H with HL pointing to the command text. The DOS parses and executes the command, then returns with status: Z flag set = success (no error), C flag set = error code in A, NZ = warning/info code in A.
5216
POP HL E1
Restore Register Pair HL from the stack. HL now points back to 428AH (the DOS state flags byte saved at 5211H).
5217
RES 7,(HL) CB BE
RESset (clear) bit 7 of the byte at (HL), which is 428AH. This clears the “BASIC DOS command in progress” flag now that the DOS command has completed.
5219
POP HL E1
Restore Register Pair HL from the stack. This retrieves the original caller’s context (pushed before 5202H was entered).
521A
If the CARRY FLAG has been set (the DOS command returned with an error), JUMP to 5DC7H in BASIC/CMD, which is the error exit path that processes the DOS error code in Register A.
521D
RET Z C8
If the Z FLAG has been set (the DOS command completed successfully with no warnings), return to the caller. Normal successful completion.
521E
CP 38H FE 38
Compare Register A (the DOS return code) against 38H (decimal 56, which is the error code for “Illegal while in MINI-DOS”). If Register A equals 38H, the Z FLAG is set.
5220
If the NZ FLAG has been set (the return code is not 38H - it is some other non-zero, non-error status), JUMP to 5DEFH, the error translator in BASIC/CMD, which converts the DOS error code to a BASIC error number and raises the appropriate error.

If the return code is exactly 38H (“Illegal while in MINI-DOS”), execution falls through here. The code at 5222H (not shown - this is actually 5223H, the memory test setup) handles this case, which means the “Illegal in MINI-DOS” warning is silently ignored and processing continues.

5223H - Memory Integrity Checker Setup

Initializes the memory integrity test by saving the current stack pointer, calculating a safe relocation address in high memory (SP − 192 bytes), computing the available memory range to test, and checking that sufficient memory exists. The test routine will be copied to high memory via LDIR so it can test the overlay region without destroying itself.

On entry, HL contains the BASIC text pointer. The routine saves SP, then computes the destination address for the relocated test code as SP − C0H (192 decimal). The test will XOR every byte in the range 0000H through top-of-memory with A5H, verify integrity, then XOR again to restore. Because the test code itself resides in the overlay area (5200H-56E7H), it must first copy itself out of the way.

5223
PUSH HL E5
Save Register Pair HL (the BASIC text pointer) onto the stack, preserving the parse position for restoration after the memory test completes.
5224
LD (5290H),SP ED 73 90 52
Store the current Stack Pointer value to memory location 5290H. This saves SP so it can be restored after the memory test. 5290H is the operand of the LD SP,0000H instruction at 528FH - this is self-modyfing code that patches the restore instruction with the correct SP value.
5228
LD HL,FF40H 21 40 FF
Load Register Pair HL with FF40H. This is the negative offset −C0H (two’s complement of 192 decimal). Adding this to SP will compute SP − 192, which is the destination address where the test routine will be relocated.
522B
ADD HL,SP 39
ADD the Stack Pointer to HL. HL = SP + FF40H = SP − 192. This computes the relocation destination address, placing the test code 192 bytes below the current stack pointer to avoid collision during execution.
522C
LD (52C9H),HL 22 C9 52
Store the relocation destination address (SP − 192) to memory location 52C9H. this is self-modyfing code - 52C9H is referenced by LD HL,(52C9H) at 529AH and LD BC,(52C9H) at 52A7H, so subsequent code can access the relocated destination address.
522F
LD BC,(5278H) ED 4B 78 52
Fetch the value at memory location 5278H into Register Pair BC. This is the memory test comparison base address - it defines the lower boundary of the memory region being tested. The value at 5278H is set by the caller before invoking the memory test.
5233
OR A B7
OR Register A with itself to clear the Carry flag. This prepares for the SBC HL,BC subtraction at the next instruction, which requires Carry to be clear for a proper subtraction.
5234
SBC HL,BC ED 42
SUBtract with Carry Register Pair BC from HL. HL = (SP − 192) − (memory base). This computes the size of the available memory region between the test base and the relocation destination. The result represents how many bytes of memory can be tested.
5236
LD (5272H),HL 22 72 52
Store the available memory size to 5272H. this is self-modyfing code - 5272H is the operand of the LD HL,0000H instruction at 5271H in the relocated test core, telling it how many bytes of overlay data need to be zero-filled and then restored.
5239
PUSH HL E5
Save Register Pair HL (the available memory size) onto the stack for use in the upcoming boundary check.
523A
LD DE,(40FDH) ED 5B FD 40
Fetch the string stack pointer from ROM BASIC system variable at 40FDH into Register Pair DE. The string stack grows downward from its initial allocation, so 40FDH holds the current bottom of the string descriptor stack. The memory test must not overwrite this area.
523E
OR A B7
Clear the Carry flag in preparation for the following SBC instruction.
523F
SBC HL,DE ED 52
SUBtract the string stack pointer (DE) from the available memory size (HL). If the result is negative (Carry set), the available memory overlaps with the string stack, meaning there is not enough free memory to safely perform the test.
5241
POP DE D1
Restore Register Pair DE from the stack. DE now holds the available memory size (pushed at 5239H, but note the stack has been modified - this actually restores HL’s saved value).
5242
If the CARRY FLAG has been set (the memory test region would collide with the string stack - insufficient free memory), JUMP to ROM 197AH, the “Out of Memory” error handler (?OM ERROR). The memory test cannot proceed safely.

Second Boundary Check
The routine now performs an additional check to ensure that the overlay source region (starting at 5275H) plus the overlay size does not extend past the string stack pointer. This prevents the LDIR copy from reading beyond allocated memory.

5245
LD HL,(5275H) 2A 75 52
Fetch the overlay source base address from 5275H into Register Pair HL. This address marks the start of the data that will be preserved and restored after the memory test.
5248
PUSH HL E5
Save Register Pair HL (the source base address) onto the stack for later use during the LDIR copy.
5249
ADD HL,BC 09
ADD Register Pair BC (the memory test comparison base from 5278H) to HL. This computes the end address of the source region: source_base + test_range_size.
524A
DEC HL 2B
DECrement Register Pair HL by 1. HL now points to the last byte of the source region (end address − 1).
524B
OR A B7
Clear the Carry flag for the following SBC instruction.
524C
SBC HL,DE ED 52
SUBtract Register Pair DE (the string stack pointer from 40FDH) from HL (the last byte of the source region). If the result is non-negative (No Carry), the source region extends into or past the string stack - out of memory.
524E
If the NO CARRY FLAG has been set (the source region overlaps with the string stack), JUMP to ROM 197AH, the “Out of Memory” error handler. The test cannot proceed.

Memory Test Relocation
Both boundary checks have passed. The routine now copies the test code from the overlay area to high memory (the destination at 52C9H), then jumps to the relocated copy. The LDIR copies the code, and then the relocated version runs the actual XOR fill/verify test.

5251
POP HL E1
Restore Register Pair HL from the stack. HL now holds the source base address (pushed at 5248H) - this is the start of the code/data to be copied to the relocation destination.
5252
PUSH HL E5
Save Register Pair HL (source address) onto the stack again. It will be needed after the LDIR completes.
5253
PUSH DE D5
Save Register Pair DE (the string stack pointer) onto the stack.
5254
EX DE,HL EB
Exchange DE and HL. HL now holds the string stack pointer, and DE holds the source base address. This prepares for the byte count calculation.
5255
OR A B7
Clear the Carry flag for the SBC subtraction.
5256
SBC HL,DE ED 52
SUBtract DE (source base) from HL (string stack pointer). HL = bytes_available = string_stack − source_base. However, this is computing the size of the region to copy for relocation.
5258
LD DE,5271H 11 71 52
Load Register Pair DE with 5271H, the address of the “Relocated Memory Test Core” routine within the overlay. This is the start of the block of code that will be copied to high memory via LDIR. The code from 5271H onward is the actual test logic.
525B
ADD HL,DE 19
ADD DE (5271H) to HL (the byte count from the SBC). This adjusts the copy length to account for the offset from the start of the code block.
525C
POP DE D1
Restore Register Pair DE from the stack (the string stack pointer saved at 5253H). This is discarded - the next instruction refetches the needed value.
525D
Keep drives rotating. GOSUB to 4416H to issue a drive-activity pulse, preventing the floppy drives from spinning down during the potentially lengthy memory test.
5260
EX (SP),HL E3
Exchange HL with the value on top of the stack. HL receives the source base address (pushed at 5252H), and the stack now holds the adjusted copy length. This sets up HL as the LDIR source.
5261
LDIR ED B0
Block copy: copy BC bytes from (HL) to (DE), incrementing both HL and DE after each byte. This copies the memory test code from the overlay area (starting at the source base) to the relocation destination in high memory (52C9H area). After the copy, the test code exists in two places: the original overlay and the high-memory copy.
5263
POP HL E1
Restore Register Pair HL from the stack. HL now holds the adjusted copy length / entry point offset for the relocated code.
5264
LD SP,70FCH 31 FC 70
Set the Stack Pointer to 70FCH. This places the stack in a safe area below the relocated test code (which starts around 7100H area per the 52C9H calculation). The original stack is preserved via the self-modifying code at 528FH/5290H.
5267
PUSH HL E5
Save Register Pair HL (the relocated entry point) onto the new stack at 70FCH.
5268
GOSUB to 529AH to fill 64 bytes at the relocation destination (52C9H) with the A5H test pattern. This initializes the integrity verification block that will be checked after the memory test to confirm the test code was not corrupted.
526B
LD HL,4225H 21 25 42
Load Register Pair HL with 4225H. This is a DOS command string address that will be passed to DOS-CALL at 4419H. The string at 4225H contains a DOS command to reload the overlay or restore the system state after the memory test completes.
526E
JUMP to 4419H (DOS-CALL) to execute the DOS command at 4225H. This is a tail call - the routine does not return here. The DOS command restores the system state, reloads any necessary overlay, and returns control to the caller via the stack frame established earlier.

5271H - Relocated Memory Test Core

This block of code is copied via LDIR to high memory (the address stored at 52C9H) before execution. Once relocated, it zero-fills the original overlay space at 5200H, copies the overlay data back using LDIR, and then jumps to the post-relocation entry at 527FH. The LD HL,0000H at 5271H has its operand patched by the self-modifying code at 5236H to hold the available memory size.

When this code executes, it is running from its relocated position in high memory, not from 5271H. The addresses shown are the original overlay addresses; the actual execution addresses are offset by the relocation delta. The code zeros out the original overlay area, restores the overlay content from a saved copy, then transfers control to the post-test handler.

5271
LD HL,0000H 21 00 00
Load Register Pair HL with the available memory size. The operand 0000H is self-modifying code - patched at runtime by the instruction at 5236H [LD (5272H),HL] to hold the computed memory size value (SP − 192 − test base). This tells the LDIR how many bytes to copy.
5274
LD DE,5200H 11 00 52
Load Register Pair DE with 5200H, the start of the overlay address range. This is the destination for the LDIR block copy that will restore the overlay content after the memory test.
5277
LD BC,1F00H 01 00 1F
Load Register Pair BC with 1F00H (7936 decimal). This is the byte count for the LDIR copy. The value covers the range from 5200H to approximately 70FFH, which encompasses the entire overlay area plus the surrounding memory region that needs to be restored.
527A
LDIR ED B0
Block copy: copy BC bytes (1F00H = 7936) from source (HL, the memory size / source pointer) to destination (DE = 5200H), incrementing both pointers. This restores the overlay area and surrounding memory from the saved copy after the XOR memory test has completed. Every byte that was XOR’d with A5H is now restored to its original value.
527C
JUMP to 527FH, the post-relocation entry point. Now that the overlay area has been restored, execution can safely return to code in the 5200H range. This jump transfers control from the relocated copy in high memory back to the restored overlay code.

527FH - Post-Relocation Memory Test Entry

After the overlay code has been restored to its original location, this routine sets up a temporary stack, runs the XOR-based memory verification, checks for errors, restores the original stack pointer, and returns the test result to the caller.

At this point, the overlay area has been restored and execution is back in the normal 5200H address range. The routine sets SP to a safe temporary value (43FCH, within the DOS workspace), then calls the XOR verification to check whether any memory bit errors were detected during the test.

527F
LD SP,43FCH 31 FC 43
Set the Stack Pointer to 43FCH. This places the stack within the DOS workspace area (above the PDRIVE table at 4300H-433FH). A temporary stack is needed because the original stack was overwritten during the memory test.
5282
PUSH AF F5
Save Register Pair AF (flags and accumulator) onto the temporary stack. The flags contain status from the memory test restoration.
5283
GOSUB to 52A4H, the XOR memory test verification routine. This routine XOR-ciphers the memory range with the relocated base, then verifies the 64-byte integrity block at (52C9H). On return, the Z flag indicates success (all bytes verified) or failure (memory corruption detected).
5286
LD A,23H 3E 23
Load Register A with 23H (decimal 35). This is the NEWDOS/80 error code for a memory test failure. If the verification detected corruption, this error code will be passed to the DOS error handler.
5288
If the NZ FLAG has been set (the XOR verification at 52A4H detected a memory error - at least one byte did not verify correctly), JUMP to 4409H, the DOS Error Exit, with error code 23H in Register A. This reports the memory test failure to the user.
528B
Keep drives rotating. GOSUB to 4416H to pulse the floppy drive motors, preventing spin-down timeout after the potentially lengthy memory test.
528E
POP AF F1
Restore Register Pair AF from the stack (saved at 5282H). This recovers the original flags from before the verification call.
528F
LD SP,0000H 31 00 00
Restore the Stack Pointer. The operand 0000H is self-modifying code - patched by the instruction at 5224H (LD (5290H),SP) to hold the original SP value that was saved before the memory test began. This restores the caller’s stack frame.
5292
POP HL E1
Restore Register Pair HL from the now-restored stack. This retrieves the BASIC text pointer that was saved at 5223H (PUSH HL), restoring the parse context.
5293
If the CARRY FLAG has been set (an error condition was propagated from the memory test or restoration), JUMP to 5DC7H in BASIC/CMD, the error exit path.
5296
If the NZ FLAG has been set (a non-zero, non-carry warning code from the memory test), JUMP to 5DEFH, the error translator in BASIC/CMD, to convert and display the error.
5299
RET C9
Return to the caller. The memory test completed successfully with no errors detected. The Z flag is set, Register A is zero, and the original stack frame has been restored.

529AH - Memory Fill (A5H Pattern)

Fills 64 bytes starting at the address stored in 52C9H (the relocation destination) with the byte value A5H. This creates the integrity verification block that will be checked after the memory test to confirm the relocated code area was not corrupted.

529A
LD HL,(52C9H) 2A C9 52
Fetch the relocation destination address from 52C9H (set by the self-modifying code at 522CH) into Register Pair HL. This is the base address of the 64-byte integrity verification block in high memory.
529D
LD B,40H 06 40
Load Register B with 40H (decimal 64). This is the loop counter - 64 bytes will be filled with the A5H pattern.

Loop Start (64 Iterations)

529F
LD (HL),A5H 36 A5
Store the value A5H to the memory byte pointed to by HL. The pattern A5H (binary 10100101) is chosen because it alternates between set and clear bits, making it effective at detecting stuck-at-0 and stuck-at-1 memory faults.
52A1
INC HL 23
INCrement Register Pair HL by 1, advancing the pointer to the next byte in the fill area.
52A2
DECrement Register B and loop back to 529FH if B is not zero. This fills all 64 bytes with A5H.

Loop End

52A4H - XOR Memory Test Verification

Performs the XOR-based memory test by ciphering the memory range from 7100H (or the computed range) with the relocated code block, then verifies the 64-byte integrity block at (52C9H) by checking that each byte still contains A5H. Returns Z flag set if all bytes verified, NZ if corruption was detected.

52A4
LD DE,7100H 11 00 71
Load Register Pair DE with 7100H. This is the starting address for the XOR cipher operation - the test will XOR memory starting at 7100H against the block at (HL).
52A7
LD BC,(52C9H) ED 4B C9 52
Fetch the relocation destination address from 52C9H into Register Pair BC. BC serves as the upper boundary marker for the XOR cipher - when the source pointer reaches this address, the cipher loop stops.
52AB
PUSH BC C5
Save Register Pair BC (the relocation destination / boundary address) onto the stack for the verification phase that follows.
52AC
GOSUB to 52C8H, the XOR cipher engine. This routine XORs each byte at (HL) with the corresponding byte at (DE), cycling through 40H-byte (64-byte) pages with the AF′ register as a page counter. The XOR operation flips bits in memory, and a second pass of XOR with the same data restores the original values.
52AF
EX DE,HL EB
Exchange DE and HL. After the XOR cipher call, DE pointed to the next address after the cipher range. HL now holds that address, and DE is freed for the next step.
52B0
LD BC,0040H 01 40 00
Load Register Pair BC with 0040H (decimal 64). This is the size of the integrity verification block - 64 bytes that must be skipped before continuing the cipher to the end of memory.
52B3
ADD HL,BC 09
ADD BC (64) to HL. This advances the source pointer past the 64-byte integrity block, which must not be XOR’d (it contains the A5H verification pattern that will be checked later).
52B4
EX DE,HL EB
Exchange DE and HL. DE now holds the advanced pointer (past the integrity block), and HL is restored to its previous value.
52B5
LD BC,(40B1H) ED 4B B1 40
Fetch the top of BASIC memory from ROM system variable 40B1H into Register Pair BC. This is the highest usable memory address - the upper boundary for the second phase of the XOR cipher.
52B9
INC BC 03
INCrement Register Pair BC by 1. The XOR cipher tests bytes up to but not including the boundary, so incrementing ensures the byte at 40B1H itself is included in the test.
52BA
GOSUB to 52D7H, which is the mid-entry point of the XOR cipher engine. This continues the XOR cipher from the current DE position through to the top of memory (BC). This tests the memory above the integrity block.

Integrity Verification Phase
The XOR cipher has now been applied to all testable memory (below and above the integrity block). The next step verifies that the 64-byte integrity block still contains the A5H pattern. If any byte has changed, a memory fault has been detected.

52BD
POP HL E1
Restore Register Pair HL from the stack (saved at 52ABH). HL now points to the start of the 64-byte integrity verification block (the relocation destination address from 52C9H).
52BE
LD B,40H 06 40
Load Register B with 40H (decimal 64). This is the loop counter for verifying all 64 bytes of the integrity block.

Loop of 64 iterations Start

52C0
LD A,(HL) 7E
Fetch the byte at (HL) into Register A. This reads the current value of the integrity block byte, which should still be A5H if no memory corruption occurred.
52C1
XOR A5H EE A5
XOR Register A with A5H. If the byte is still A5H (uncorrupted), the result is 00H and the Z flag is set. If any bit has changed, the result is non-zero and the NZ flag is set, indicating memory corruption.
52C3
INC HL 23
INCrement Register Pair HL by 1, advancing to the next byte in the integrity block.
52C4
RET NZ C0
If the NZ FLAG has been set (the byte did not verify as A5H - memory corruption detected), return immediately to the caller with NZ set. The caller at 5288H will jump to the error handler.
52C5
DECrement Register B and loop back to 52C0H if B is not zero. Continue checking all 64 bytes.

Loop End

52C7
RET C9
Return to the caller. All 64 integrity bytes verified as A5H - no memory corruption detected. The Z flag is set (from the last successful XOR A5H comparison).

52C8H - XOR Cipher Engine

Core memory test routine that XORs each byte at (HL) with the corresponding byte at (DE), processing 64-byte pages at a time. The AF′ (alternate accumulator) register serves as the page counter. The cipher loop runs from the starting address (DE) up to but not including the boundary address in BC (high byte in B, low byte in C).

The XOR cipher is non-destructive when applied twice: XOR(XOR(x, key), key) = x. The first pass scrambles memory; the second pass restores it. By checking the integrity block between passes, the routine can detect single-bit memory faults.

Outer Loop Start

52C8
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H, the start of the memory address space. The cipher will XOR bytes starting from address 0000H.
52CB
LD A,40H 3E 40
Load Register A with 40H (decimal 64). This is the page size counter - each “page” is 64 bytes. This value will be saved in the alternate accumulator AF′.
52CD
EX AF,AF' 08
Switch to the alternate AF register set. The page counter (40H) is now stored in AF′, freeing Register A for the XOR operations. The primary AF register is now available for data manipulation.

Inner Loop Start

52CE
LD A,(DE) 1A
Fetch the byte at the address in DE (the cipher key source) into Register A. This reads one byte of the cipher key data.
52CF
XOR (HL) AE
XOR Register A with the byte at (HL). This combines the cipher key byte with the memory byte, producing the XOR result in A. On the first pass this scrambles the byte; on the second pass it restores the original value.
52D0
LD (HL),A 77
Store the XOR result back to memory at (HL), replacing the original byte with the ciphered value.
52D1
INC DE 13
INCrement Register Pair DE by 1, advancing the cipher key pointer to the next byte.
52D2
INC HL 23
INCrement Register Pair HL by 1, advancing the memory test pointer to the next byte.
52D3
LD A,D 7A
Load Register A with the high byte of DE (the cipher key pointer). This prepares for a boundary check against Register B.
52D4
CP B B8
Compare Register A (high byte of DE) against Register B (high byte of the boundary address in BC). If they match, the cipher has reached the boundary page and must also check the low byte.
52D5
If the Z FLAG has been set (the high bytes match - DE and BC are on the same page), JUMP forward to 52DDH to check the low byte for an exact boundary match.
52D7
EX AF,AF' 08
Switch back to the alternate AF register. Register A now contains the page counter (initially 40H). [PAGE COUNTER CHECK]
52D8
DEC A 3D
DECrement the page counter in Register A by 1. Each decrement represents one byte processed within the current 64-byte page.
52D9
If the NZ FLAG has been set (the page counter has not reached zero - more bytes remain in the current 64-byte page), JUMP back to 52CDH to process the next byte.

Inner Loop End

52DB
Unconditional JUMP back to 52C8H to start a new 64-byte page. The cipher key pointer (DE) wraps back to the beginning of the key, and the page counter is reset to 40H.

Outer Loop End - Continues until Boundary Reached

Boundary Check (Low Byte)
The high bytes of DE and BC match. Now the low bytes are compared to determine if the cipher has reached the exact boundary address.

52DD
LD A,E 7B
Load Register A with the low byte of DE (the cipher key pointer). This prepares for an exact boundary comparison against Register C.
52DE
CP C B9
Compare Register A (low byte of DE) against Register C (low byte of the boundary address). If they match, the cipher has reached the exact boundary and must stop.
52DF
If the NZ FLAG has been set (the low bytes do not match - the boundary has not been reached), JUMP back to 52D7H to continue the page counter decrement and keep processing. The high bytes matched but the low bytes differ, so the cipher is on the boundary page but has not reached the exact boundary byte.
52E1
RET C9
Return to the caller. The XOR cipher has reached the boundary address in BC. All bytes from the starting address through BC−1 have been XOR’d with the corresponding cipher key bytes.

52E2H - CMD“F=” Keyword Dispatcher

Handles the CMD“F=xxx” extended file command syntax. The character after the ‘=’ sign has been identified. If it is ‘S’ (53H), the command routes to 4405H (SYS0). If it is ‘F’ (46H), the routine performs a keyword table lookup against the CMD“F=” subcommand table at 592BH. Any other character produces an “Illegal Function Call” error.

On entry, HL points to the character after the ‘=’ sign (the first character of the subcommand keyword). DE points to the original command text position (before the ‘=’), which was saved at 5203H-5204H. The routine first checks for single-character commands (‘S’ and ‘F’), then falls through to the keyword table scanner for multi-character subcommands.

52E2
INC HL 23
INCrement Register Pair HL by 1, advancing past the ‘=’ sign to point to the first character of the subcommand keyword.
52E3
LD A,(DE) 1A
Fetch the character at DE (the original command text position saved at 5203H-5204H) into Register A. This is the command letter that precedes the ‘=’ sign.
52E4
CP 53H FE 53
Compare Register A against 53H (ASCII S). If this is CMD“S=”, the command routes to the ‘S’ subcommand handler.
52E6
If the Z FLAG has been set (the command letter is S, indicating CMD“F=S”), JUMP to 4405H in SYS0. This is the CMD“F=S” handler, which performs the ‘S’ subcommand (system configuration).
52E9
CP 46H FE 46
Compare Register A against 46H (ASCII F). If this is CMD“F=Fxxx”, the command routes to the keyword table lookup for file subcommands.
52EB
If the NZ FLAG has been set (the command letter is neither S nor F), JUMP to 5302H to raise an “Illegal Function Call” error. Only CMD“F=S” and CMD“F=Fxxx” are valid subcommand forms.

CMD“F=F” Keyword Table Lookup
The command letter is ‘F’. The routine now scans the keyword table at 592BH (in the main BASIC/CMD module) to find a matching subcommand keyword. The table format is: [length byte] [keyword chars] [handler address low] [handler address high] [00H terminator]. Register C is decremented by 1 to adjust for the keyword comparison logic.

52ED
LD DE,592BH 11 2B 59
Load Register Pair DE with 592BH, the address of the CMD“F=” subcommand keyword table in the main BASIC/CMD module. Each entry in this table contains a length byte, the keyword characters, and a 2-byte handler address.
52F0
DEC C 0D
DECrement Register C by 1. Register C holds a count or offset value from the caller; this adjustment aligns it with the keyword comparison logic that follows.

Loop Start - Keyboard Table Scan

52F1
PUSH HL E5
Save Register Pair HL (the pointer to the subcommand text in the user’s command) onto the stack. The comparison will advance HL, so the original position must be preserved for retry on mismatch.
52F2
LD A,(DE) 1A
Fetch the length byte of the current keyword table entry from (DE) into Register A. This byte indicates how many characters are in the keyword.
52F3
CP C B9
Compare the keyword length (Register A) against Register C (the adjusted character count). If they match, this entry has the same length as the user’s subcommand and is a candidate for character-by-character comparison.
52F4
LD B,A 47
Load Register B with the keyword length from Register A. B will serve as the character comparison loop counter.
52F5
INC DE 13
INCrement DE by 1, advancing the table pointer past the length byte to the first character of the keyword.
52F6
If the Z FLAG has been set (the keyword length matches the user’s subcommand length), JUMP to 5305H to begin the character-by-character comparison of the keyword against the user’s input.

Skip Non-Matching Entry
The length does not match. Skip past the keyword characters and the 2-byte handler address to reach the next table entry.

52F8
POP HL E1
Restore Register Pair HL from the stack (the saved subcommand text pointer from 52F1H). This resets the parse position for the next table entry comparison.

Skip Routine Loop Start

52F9
INC DE 13
INCrement DE by 1, skipping one byte of the keyword characters in the table entry.
52FA
DECrement Register B and loop back to 52F9H if B is not zero. This skips past all the keyword characters (B bytes).

Skip Routine Loop End

52FC
INC DE 13
INCrement DE by 1, skipping the first byte of the 2-byte handler address following the keyword.
52FD
INC DE 13
INCrement DE by 1, skipping the second byte of the handler address. DE now points to the next table entry.
52FE
LD A,(DE) 1A
Fetch the first byte of the next table entry. If this byte is 00H, it is the table terminator - no more entries to check.
52FF
OR A B7
OR Register A with itself to test for zero. If A is 00H (table terminator), the Z flag is set.
5300
If the NZ FLAG has been set (the table entry is not the terminator - more keywords to check), JUMP back to 52F1H to compare the next entry.

Keyboard Table Scan Loop End

5302
JUMP to ROM 1E4AH, the “Illegal Function Call” error handler. The subcommand keyword was not found in the table (table exhausted with no match), or the command letter was not ‘S’ or ‘F’.

Keyword Character Comparison
The keyword length matches. Now compare each character of the keyword against the user’s subcommand text, one byte at a time.

5305
LD A,(DE) 1A
Fetch the next character of the keyword from the table (pointed to by DE) into Register A.
5306
CP (HL) BE
Compare Register A (the keyword character from the table) against the byte at (HL) (the corresponding character from the user’s subcommand text). If they match, the Z flag is set.
5307
LD (DE),A 12
Store Register A back to (DE). This is a no-op for the table data (writing the same value back), but it ensures the table pointer is valid. This instruction may be a remnant of an earlier version or serves as a timing placeholder.
5308
INC HL 23
INCrement Register Pair HL by 1, advancing the user’s subcommand text pointer to the next character.
5309
If the NZ FLAG has been set (the characters do not match), JUMP to 52F8H to restore HL and skip to the next table entry. This keyword is not a match.
530B
INC DE 13
INCrement DE by 1, advancing the table pointer to the next keyword character.
530C
DECrement Register B and loop back to 5305H if B is not zero. Continue comparing characters.

Compare Loop End

Keyword Match Found
All characters matched. DE now points to the 2-byte handler address following the keyword in the table. Extract the address and dispatch to the handler.

530E
EX DE,HL EB
Exchange DE and HL. HL now points to the handler address in the table, and DE holds the advanced user text pointer.
530F
LD E,(HL) 5E
Fetch the low byte of the handler address from the table (pointed to by HL) into Register E.
5310
INC HL 23
INCrement HL by 1, advancing to the high byte of the handler address.
5311
LD D,(HL) 56
Fetch the high byte of the handler address from the table into Register D. Register Pair DE now holds the full 16-bit handler address for the matched subcommand.
5312
POP HL E1
Restore Register Pair HL from the stack. This discards the saved subcommand text pointer (from 52F1H) since the match has been found.
5313
POP HL E1
Restore Register Pair HL from the stack again. This retrieves the original parse context from the caller, positioned after the matched keyword.
5314
PUSH DE D5
Save the handler address (in DE) onto the stack. The following RET will pop this address and jump to it, effectively dispatching to the matched subcommand handler.
5315
RET C9
Return. This pops the handler address (pushed at 5314H) from the stack and jumps to it, dispatching execution to the CMD“F=xxx” subcommand handler. The stack trick (PUSH DE / RET) is a common Z80 pattern for computed dispatch.

5316H - NEXT/FOR Stack Frame Handler

Handles the CMD“F”,NEXT functionality by manipulating the stack to pass variable context through FOR/NEXT loops. The routine sets up a return address of 5346H on the stack, relocates the stack pointer below the BASIC program start (40A0H), and dispatches to ROM 1D25H for program execution with variable context preserved.

This routine is called when the program needs to transfer variable values across a CHAIN or overlay boundary. It manipulates the stack frame to ensure that the FOR/NEXT loop context and variable bindings survive the transition.

5316
EX DE,HL EB
Exchange DE and HL. DE held the previous parse context; HL now holds it, and DE is freed for loading the return address.
5317
LD HL,5346H 21 46 53
Load Register Pair HL with 5346H, the address of the stack cleanup and dispatch code. This will be placed on the stack as a return address so that when the called routine returns, execution continues at 5346H.
531A
PUSH HL E5
Save the return address 5346H onto the stack. When the following code returns, it will jump to 5346H.
531B
LD HL,(40A0H) 2A A0 40
Fetch the BASIC program start address from ROM system variable 40A0H into Register Pair HL. This is the address where the BASIC program text begins in memory. The stack will be relocated below this point.
531E
DEC HL 2B
DECrement Register Pair HL by 1. Move one byte below the BASIC program start to create a safe stack location that does not overlap with program text.
531F
DEC HL 2B
DECrement Register Pair HL by 1 again. The stack needs at least 2 bytes below the program start for the return address that was pushed at 531AH.
5320
POP AF F1
Restore Register Pair AF from the stack. This pops the return address 5346H that was just pushed at 531AH, temporarily holding it in AF.
5321
LD SP,HL F9
Set the Stack Pointer to the value in HL (two bytes below the BASIC program start at 40A0H). This relocates the stack to a safe area below the program text. WARNING: all previous stack content is now inaccessible.
5322
PUSH AF F5
Save Register Pair AF (holding the return address 5346H) onto the new stack. When the dispatched routine returns via RET, execution will continue at 5346H.
5323
RET C9
Return. This pops the value pushed at 5322H (the address 5346H encoded in AF) and “returns” to it. Execution continues at 5346H.

5324H - FOR Statement Extension

Extends the FOR statement handling by searching the stack for a FOR push (via ROM 1936H), verifying the presence of the expected token 91H (the FOR token in tokenized BASIC), and adjusting the stack by 4 bytes to skip the FOR loop context frame.

5324
EX (SP),HL E3
Exchange HL with the value on top of the stack. The caller’s return address is saved in HL, and the current HL value (the parse pointer) is placed on the stack.
5325
LD D,FFH 16 FF
Load Register D with FFH. This is passed to ROM 1936H as a search parameter, indicating a “search all FOR entries” mode (FFH means match any variable).
5327
GOSUB to ROM 1936H, the FOR stack search routine. This routine walks the stack looking for a FOR loop push frame. On return: if a matching FOR frame was found, NZ is set and HL points to the frame; if no frame was found, Z is set.
532A
CP 91H FE 91
Compare Register A against 91H. Token 91H is the FOR keyword token in NEWDOS/80 BASIC. The ROM 1936H routine returns the token byte from the stack frame in A; if this is not 91H, the stack frame is not a FOR loop.
532C
If the NZ FLAG has been set (the token is not 91H - the stack frame is not a FOR loop), JUMP to ROM 1EEAH, an error handler that raises the appropriate BASIC error (such as “NEXT without FOR”).
532F
LD BC,0004H 01 04 00
Load Register Pair BC with 0004H (decimal 4). This is the number of bytes to skip in the stack frame - the FOR loop context header is 4 bytes that must be bypassed to reach the variable data.
5332
Unconditional JUMP to 5343H to add BC (4) to HL and continue with the stack frame adjustment.

5334H - Variable Passing (String Expression Path)

Evaluates an optional string expression for variable passing, checks the next token via ROM 1936H, and dispatches to ROM 1D25H for program execution with the variable context intact. This routine handles the case where a string expression is provided as part of the CHAIN or variable-passing syntax.

5334
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H. DE is initialized to zero, which serves as a default or null value for the string expression result if no expression is present.
5337
DEC HL 2B
DECrement Register Pair HL by 1. This backs up the parse pointer by one byte so that RST 10H will re-fetch the current character (the character that might be the start of a string expression).
5338
RST 10H D7
Fetch the next non-space character from the BASIC text at (HL) into Register A, advancing HL past any spaces. The Carry flag is set if the character is a digit (30H-39H).
5339
If the NZ FLAG has been set (there is a non-null character - a string expression is present), GOSUB to ROM 260DH, the BASIC string expression evaluator. This evaluates the string expression and returns the result in the string descriptor.
533C
EX (SP),HL E3
Exchange HL with the value on top of the stack. The updated parse pointer is saved on the stack, and the previous stack value (a return address or context) is loaded into HL.
533D
GOSUB to ROM 1936H, the FOR stack search routine. This checks whether there are any remaining FOR loop frames on the stack that need to be processed.
5340
If the NZ FLAG has been set (a FOR stack frame was found when none was expected), JUMP to ROM 199DH, the “NEXT without FOR” error handler.
5343
ADD HL,BC 09
ADD Register Pair BC to HL. This adjusts the stack pointer / frame pointer by the skip count (either 4 from 532FH, or 0 from the fall-through). HL now points past the FOR loop context bytes.
5344
POP DE D1
Restore Register Pair DE from the stack. DE receives the parse pointer or expression result that was saved on the stack.
5345
LD SP,HL F9
Set the Stack Pointer to the adjusted value in HL. This removes the FOR loop frame from the stack by moving SP past it.
5346
EX DE,HL EB
Exchange DE and HL. HL now holds the parse pointer, and DE holds the adjusted stack value. This prepares HL for the ROM dispatch.
5347
RET C9
Return to the caller. With the stack cleaned up, HL pointing to the program text, execution continues via the return address on the stack. NOTE: Due to the stack frame at 531AH, this returns to 5346H in one call path, or to the original caller in another.
5348
JUMP to ROM 1D25H, the program execution entry with variable context preservation. This transfers control to the BASIC runtime, which will continue executing the program with the variable values that were passed through the stack frame manipulation. This is a tail call - execution does not return here.

534AH - LSET / RSET / MID$ Assignment Entry

Entry point for the LSET, RSET, and left-side MID$ assignment statements. LSET and RSET assign string values to random-access file buffer fields (left-justified or right-justified respectively). Left-side MID$ replaces a substring within an existing string variable. The routine evaluates the file number, reads the target string descriptor, and branches based on whether a ‘(’ follows (indicating MID$ assignment) or not (indicating LSET/RSET).

On entry, HL points to the BASIC program text at the statement being executed. The routine first evaluates the file number via 5824H, then examines the string descriptor to determine the target variable. If the byte following the descriptor is 28H (‘(’), this is a MID$(a$,n,m)=expr assignment. Otherwise, it falls through to the LSET/RSET common path.

534A
GOSUB to 5824H in BASIC/CMD, the file number evaluator. This routine parses the file number from the BASIC text, validates it, and returns with the FCB pointer set up in IX. Register A contains the file mode byte on return.
534D
PUSH AF F5
Save Register Pair AF (containing the file mode byte from 5824H) onto the stack. This will be used later to determine whether LSET or RSET behavior is needed.
534E
XOR A AF
Set Register A to zero and clear all flags. A=00H will serve as an initial flag value (no MID$ parameters set yet).
534F
PUSH AF F5
Save Register Pair AF (A=00H, Z flag set) onto the stack. This zero value marks that no MID$ start position has been evaluated yet.
5350
EX DE,HL EB
Exchange DE and HL. The parse pointer moves to DE, and HL is freed. DE now holds the address of the target variable’s string descriptor pointer.
5351
LD A,(HL) 7E
Fetch the low byte of the string descriptor address from (HL) into Register A. The string descriptor is a 3-byte structure: [length][address_low][address_high].
5352
INC HL 23
INCrement HL by 1, advancing to the high byte of the string descriptor address.
5353
LD H,(HL) 66
Fetch the high byte of the string descriptor address from (HL) into Register H.
5354
LD L,A 6F
Load Register L with Register A (the low byte). Register Pair HL now holds the full 16-bit string descriptor address.
5355
LD A,H 7C
Load Register A with the high byte of the string descriptor address (from HL). This prepares for a null-pointer check.
5356
OR L B5
OR Register A (high byte) with Register L (low byte). If both bytes are zero, the string descriptor address is 0000H (null pointer), and the Z flag is set.
5357
If the Z FLAG has been set (the string descriptor address is 0000H - the target variable has no string assigned), JUMP to 5DA0H in BASIC/CMD, the “Bad file mode” error handler. LSET/RSET/MID$ cannot assign to a null string descriptor.
535A
LD A,(DE) 1A
Fetch the next character from the BASIC text (pointed to by DE) into Register A. This checks whether the next character is ‘(’, which would indicate a MID$ assignment.
535B
CP 28H FE 28
Compare Register A against 28H (ASCII (). If it is an opening parenthesis, this is a MID$ left-side assignment (MID$(a$,start,length) = expr).
535D
If the NZ FLAG has been set (the character is not ( - this is LSET or RSET, not MID$), JUMP forward to 539EH to enter the LSET/RSET common path.

MID$ Assignment Path
The character is ‘(’, so this is a MID$(target$, start [,length]) = source$ assignment. The routine parses the start position and optional length parameters inside the parentheses.

535F
RST 08H ⇒ 28H CF 28
Verify and consume the expected token 28H (opening parenthesis). RST 08H checks that the current byte in the BASIC text matches the expected byte; if not, it raises a Syntax Error. HL is advanced past the token.
5361
If the Z FLAG has been set (the RST 08H matched and consumed the parenthesis; Z is set on match in this context), JUMP back to 5337H. This re-enters the variable passing code path - but this interpretation needs further analysis. Given the disassembly, this is the RST 08H operand byte forming a JR Z instruction only if the preceding RST 08H falls through with Z set.
5362
GOSUB to 5831H in BASIC/CMD, the alternate file number evaluator. This evaluates the second parameter (the start position for MID$) as a file number context value.
5365
POP BC C1
Restore Register Pair BC from the stack. BC receives the zero flag value that was pushed at 534FH.
5366
LD (5386H),HL 22 86 53
Store Register Pair HL to memory location 5386H. This saves the string descriptor pointer for the MID$ target variable. 5386H is self-modifying code - it is the operand of LD HL,(5386H) at 53E4H, allowing later code to retrieve this pointer.
5369
If the NZ FLAG has been set (the file number evaluation returned a non-zero result, indicating the start position was specified as a numeric expression), JUMP to 5374H to handle the numeric MID$ start position.
536B
PUSH DE D5
Save Register Pair DE onto the stack.
536C
PUSH AF F5
Save Register Pair AF onto the stack.
536D
PUSH BC C5
Save Register Pair BC onto the stack.
536E
PUSH DE D5
Save Register Pair DE onto the stack again. The stack is being set up with the parameters for ROM 2888H (the MID$ assignment helper).
536F
GOSUB to ROM 2888H, the MID$ assignment helper. This routine performs the actual substring replacement, copying source string bytes into the target string at the specified position and length.
5372
Unconditional JUMP to 5384H to continue with the post-assignment processing (popping parameters and checking for more assignments).

Numeric Start Position Path
The MID$ start position was specified as a numeric expression. The routine negates it (to create a negative stack offset), adjusts SP, and calls 5CB7H (the file status checker) to validate the operation.

5374
NEG ED 44
Negate Register A (two’s complement). The start position value is negated to compute a negative stack offset for the parameter frame.
5376
LD L,A 6F
Load Register L with the negated value from Register A.
5377
LD H,FFH 26 FF
Load Register H with FFH. Combined with L, HL forms a negative 16-bit offset (sign-extended from the 8-bit negated value).
5379
ADD HL,SP 39
ADD the Stack Pointer to HL. HL = SP + (negative offset) = SP − start_position. This reserves space on the stack for the MID$ parameter frame.
537A
LD SP,HL F9
Set the Stack Pointer to HL. The stack now has room for the parameter frame. WARNING: previous stack entries are preserved but the SP has moved down.
537B
NEG ED 44
Negate Register A again, restoring the original positive start position value.
537D
PUSH DE D5
Save Register Pair DE onto the stack.
537E
PUSH AF F5
Save Register Pair AF (the start position in A) onto the stack.
537F
PUSH BC C5
Save Register Pair BC onto the stack.
5380
PUSH DE D5
Save Register Pair DE onto the stack again. Same parameter setup as the Z path at 536BH-536EH.
5381
GOSUB to 5CB7H in BASIC/CMD, the file status check routine. This validates that the file is in the correct mode for the MID$ assignment operation and returns the buffer parameters.
5384
POP DE D1
Restore Register Pair DE from the stack.
5385
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H. The operand at 5386H is self-modifying code - it is overwritten by the instruction at 5366H or 53AAH with the saved string descriptor pointer for the MID$ target. At runtime, HL holds the target string descriptor address.
5388
EX (SP),HL E3
Exchange HL with the value on top of the stack. The string descriptor pointer is saved on the stack, and the previous stack top (a return address or parameter) is loaded into HL.
5389
RST 10H D7
Fetch the next non-space character from the BASIC text into Register A, advancing HL.
538A
GOSUB to ROM 1F2BH, a PRINT formatting routine. In this context, it processes the expression following the ‘=’ sign in the MID$ assignment.
538D
POP DE D1
Restore Register Pair DE from the stack.
538E
LD A,(DE) 1A
Fetch the next byte from the BASIC text at (DE) into Register A.
538F
CP (HL) BE
Compare Register A against the byte at (HL). This verifies that the source and target are compatible - typically checking that both are strings or both are numeric.
5390
If the NZ FLAG has been set (type mismatch between source and target), JUMP to ROM 1997H, the “Syntax Error” handler.
5393
EX DE,HL EB
Exchange DE and HL. HL now points to the source expression result, DE to the target.
5394
CP 29H FE 29
Compare Register A against 29H (ASCII )). Check whether the current character is a closing parenthesis, which would terminate the MID$ parameter list.
5396
If the Z FLAG has been set (the character is ), closing the MID$ parameter list), JUMP to 539CH to finalize the MID$ assignment.
5398
RST 08H ⇒ 2CH CF 2C
Verify and consume the expected token 2CH (comma). RST 08H checks that the current byte matches 2CH (ASCII ,); if not, it raises a Syntax Error. There is another parameter to parse after the comma.
539A
Unconditional JUMP back to 5361H to parse the next parameter (the length for MID$). This loops back to evaluate additional comma-separated values.
539C
INC DE 13
INCrement DE by 1, advancing past the closing parenthesis in the BASIC text.
539D
RST 10H D7
Fetch the next non-space character from the BASIC text at (HL) into Register A. This should be the ‘=’ sign of the assignment.

539EH - LSET / RSET Common Path

Common processing for LSET and RSET (and the final stage of MID$ assignment after the parameters have been parsed). Expects an ‘=’ sign followed by a string expression. Evaluates the source string via ROM 2337H, verifies end-of-statement, and dispatches to ROM 2819H for the actual string field assignment.

539E
RST 08H ⇒ D5H CF D5
Verify and consume the expected token D5H. In NEWDOS/80 BASIC tokenization, D5H is the ‘=’ assignment operator token (PUSH DE opcode used as inline comparison byte by RST 08H). If the current token does not match, a Syntax Error is raised.
53A0
PUSH DE D5
Save Register Pair DE onto the stack. DE holds the parse context that will be needed after the string expression evaluation.
53A1
GOSUB to ROM 2337H, the string expression evaluator. This evaluates the source string expression (the right-hand side of the LSET/RSET/MID$ assignment) and returns the string descriptor in the standard location.
53A4
DEC HL 2B
DECrement HL by 1. Back up the parse pointer so RST 10H will re-examine the current character to verify end-of-statement.
53A5
RST 10H D7
Fetch the next non-space character from the BASIC text. After the string expression, this should be a statement terminator (colon or end-of-line).
53A6
If the NZ FLAG has been set (the character is not a statement terminator - there is unexpected text after the expression), JUMP to ROM 1997H, the “Syntax Error” handler.
53A9
POP HL E1
Restore Register Pair HL from the stack. HL receives the parse context saved at 53A0H.
53AA
LD (5386H),HL 22 86 53
Store Register Pair HL to memory location 5386H. This updates the [SELF-MODIFYING CODE] target with the string descriptor pointer for the LSET/RSET operation. The LD HL,(5386H) at 53E4H will retrieve this value.
53AD
RST 20H E7
Evaluate the expression type. RST 20H returns: Z flag set if the last expression was a string, NZ if numeric. Carry set indicates single-precision, Minus indicates integer.
53AE
If the NZ FLAG has been set (the expression result is numeric, not a string), JUMP to 53E1H to handle the numeric assignment path (which will pop the flags and either recurse or dispatch).

String Expression Assignment
The source expression is a string. The routine now compares the source string length against the target field length and performs the LSET/RSET operation via ROM 2819H.

53B0
LD DE,(40B3H) ED 5B B3 40
Fetch the string accumulator pointer from ROM system variable 40B3H into Register Pair DE. 40B3H holds a pointer to the most recently computed string result (the source string for the assignment).
53B4
LD HL,(4121H) 2A 21 41
Fetch the value from ROM system variable 4121H (the single-precision accumulator) into Register Pair HL. In the string context, this holds the target field descriptor or length information.
53B7
DEC DE 1B
DECrement DE by 1, adjusting the string pointer to account for the descriptor header offset.
53B8
DEC DE 1B
DECrement DE by 1 again.
53B9
DEC DE 1B
DECrement DE by 1 a third time. DE now points 3 bytes before the string accumulator pointer, which positions it at the start of the 3-byte string descriptor (length, address_low, address_high).
53BA
RST 18H DF
Compare HL vs DE (16-bit comparison). RST 18H sets: Carry if HL < DE, Z if HL == DE, NZ+NC if HL > DE. This compares the target field value against the source string descriptor position.
53BB
If the Z FLAG has been set (HL equals DE - the target and source descriptors are the same), JUMP to 53C2H to handle the self-assignment case (assigning a field to itself).
53BD
GOSUB to ROM 2843H, the string assignment helper. This routine copies the source string data into the string space and updates the descriptor, preparing for the LSET/RSET field copy.
53C0
Unconditional JUMP to 53E1H to continue with the post-assignment processing.

Self-Assignment Handling
The source and target descriptors are the same. A direct copy of the string into string space is required to avoid aliasing issues (the source would be overwritten during copy).

53C2
LD DE,40D3H 11 D3 40
Load Register Pair DE with 40D3H. This is a ROM workspace address used as a temporary buffer for the string copy operation.
53C5
GOSUB to ROM 29F5H, the string space allocator. This allocates space in the string area for the copy and returns the allocated address.
53C8
LD (40B3H),HL 22 B3 40
Store the allocated string space address (returned by 29F5H in HL) to ROM system variable 40B3H, updating the string accumulator pointer to the new copy.
53CB
LD BC,0003H 01 03 00
Load Register Pair BC with 0003H (decimal 3). The string descriptor is 3 bytes long (length + 2-byte address).
53CE
LDIR ED B0
Block copy: copy 3 bytes from (HL) to (DE). This copies the string descriptor from the newly allocated space to the workspace buffer at 40D3H, completing the self-assignment copy setup.
53D0
Unconditional JUMP to 53E1H to continue with the post-assignment path.

Numeric Value Handling
The expression was numeric. The routine reads the stack-saved type byte, and if the type is 03H (integer or special numeric), routes to 53C5H for the string space allocator. Otherwise, it copies the numeric value via 5CB7H.

53D2
POP DE D1
Restore Register Pair DE from the stack.
53D3
CP 03H FE 03
Compare Register A against 03H. This checks the expression type: 03H indicates an integer or special numeric type that requires different handling.
53D5
If the Z FLAG has been set (the type is 03H), JUMP back to 53C5H to handle the value via the string space allocator path.
53D7
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H. This creates a zero value for the upcoming stack pointer calculation.
53DA
ADD HL,SP 39
ADD SP to HL. HL = SP + 0 = SP. This reads the current stack pointer value into HL without modifying it (a common Z80 idiom for reading SP).
53DB
EX DE,HL EB
Exchange DE and HL. DE now holds the current SP value, and HL holds the previous DE value.
53DC
GOSUB to 5CB7H in BASIC/CMD, the file status check routine. This validates the file/buffer state and copies the numeric value into the field buffer.
53DF
EX DE,HL EB
Exchange DE and HL. Restore the register pairing after the CALL.
53E0
LD SP,HL F9
Set the Stack Pointer to HL. This restores the stack to its previous position, cleaning up the parameter frame that was created for the numeric value handling.
53E1
POP AF F1
Restore Register Pair AF from the stack. Register A now holds the file mode byte or expression type that was pushed earlier (at 534DH or 534FH).
53E2
OR A B7
OR Register A with itself. This tests whether A is zero (no more assignments to process) or non-zero (more work remains).
53E3
If the NZ FLAG has been set (A is non-zero - there are more assignment operations to process in a multi-value context), JUMP back to 53D2H to process the next value.
53E5
RST 20H E7
Evaluate the expression type one more time. This final type check ensures the result is compatible with the target field.
53E6
If the Z FLAG has been set (the expression is a string type), GOSUB to ROM 29C2H, the string garbage collector. This reclaims unused string space after the assignment is complete.
53E9
LD HL,(5386H) 2A 86 53
Fetch the saved string descriptor pointer from 5386H (the [SELF-MODIFYING CODE] target set at 5366H or 53AAH) into Register Pair HL. This retrieves the target variable’s string descriptor.
53EC
DEC HL 2B
DECrement HL by 1, backing up one byte before the string descriptor address to position HL for the ROM dispatch.
53ED
RST 10H D7
Fetch the next non-space character from the BASIC text. This reads the byte at the adjusted position.
53EE
POP AF F1
Restore Register Pair AF from the stack. This retrieves the file mode byte (pushed at 534DH) which determines whether this is LSET or RSET.
53EF
JUMP to ROM 2819H, the LSET/RSET ROM handler. This routine performs the final field assignment: if the file mode indicates LSET, the source string is left-justified in the field; if RSET, it is right-justified. Padding with spaces fills any remaining bytes. This is a tail call.

534AH - LSET / RSET / MID$ Assignment Entry

Entry point for the LSET, RSET, and left-side MID$ assignment statements. LSET and RSET assign string values to random-access file buffer fields (left-justified or right-justified respectively). Left-side MID$ replaces a substring within an existing string variable. The routine evaluates the file number, reads the target string descriptor, and branches based on whether a ‘(’ follows (indicating MID$ assignment) or not (indicating LSET/RSET).

On entry, HL points to the BASIC program text at the statement being executed. The routine first evaluates the file number via 5824H, then examines the string descriptor to determine the target variable. If the byte following the descriptor is 28H (‘(’), this is a MID$(a$,n,m)=expr assignment. Otherwise, it falls through to the LSET/RSET common path.

534A
GOSUB to 5824H in BASIC/CMD, the file number evaluator. This routine parses the file number from the BASIC text, validates it, and returns with the FCB pointer set up in IX. Register A contains the file mode byte on return.
534D
PUSH AF F5
Save Register Pair AF (containing the file mode byte from 5824H) onto the stack. This will be used later to determine whether LSET or RSET behavior is needed.
534E
XOR A AF
Set Register A to zero and clear all flags. A=00H will serve as an initial flag value (no MID$ parameters set yet).
534F
PUSH AF F5
Save Register Pair AF (A=00H, Z flag set) onto the stack. This zero value marks that no MID$ start position has been evaluated yet.
5350
EX DE,HL EB
Exchange DE and HL. The parse pointer moves to DE, and HL is freed. DE now holds the address of the target variable’s string descriptor pointer.
5351
LD A,(HL) 7E
Fetch the low byte of the string descriptor address from (HL) into Register A. The string descriptor is a 3-byte structure: [length][address_low][address_high].
5352
INC HL 23
INCrement HL by 1, advancing to the high byte of the string descriptor address.
5353
LD H,(HL) 66
Fetch the high byte of the string descriptor address from (HL) into Register H.
5354
LD L,A 6F
Load Register L with Register A (the low byte). Register Pair HL now holds the full 16-bit string descriptor address.
5355
LD A,H 7C
Load Register A with the high byte of the string descriptor address (from HL). This prepares for a null-pointer check.
5356
OR L B5
OR Register A (high byte) with Register L (low byte). If both bytes are zero, the string descriptor address is 0000H (null pointer), and the Z flag is set.
5357
If the Z FLAG has been set (the string descriptor address is 0000H - the target variable has no string assigned), JUMP to 5DA0H in BASIC/CMD, the “Bad file mode” error handler. LSET/RSET/MID$ cannot assign to a null string descriptor.
535A
LD A,(DE) 1A
Fetch the next character from the BASIC text (pointed to by DE) into Register A. This checks whether the next character is ‘(’, which would indicate a MID$ assignment.
535B
CP 28H FE 28
Compare Register A against 28H (ASCII (). If it is an opening parenthesis, this is a MID$ left-side assignment (MID$(a$,start,length) = expr).
535D
If the NZ FLAG has been set (the character is not ( - this is LSET or RSET, not MID$), JUMP forward to 539EH to enter the LSET/RSET common path.

MID$ Assignment Path
The character is ‘(’, so this is a MID$(target$, start [,length]) = source$ assignment. The routine parses the start position and optional length parameters inside the parentheses.

535F
RST 08H ⇒ 28H CF 28
Verify and consume the expected token 28H (opening parenthesis). RST 08H checks that the current byte in the BASIC text matches the expected byte; if not, it raises a Syntax Error. HL is advanced past the token.
5361
If the Z FLAG has been set (the RST 08H matched and consumed the parenthesis; Z is set on match in this context), JUMP back to 5337H. This re-enters the variable passing code path - but this interpretation needs further analysis. Given the disassembly, this is the RST 08H operand byte forming a JR Z instruction only if the preceding RST 08H falls through with Z set.
5362
GOSUB to 5831H in BASIC/CMD, the alternate file number evaluator. This evaluates the second parameter (the start position for MID$) as a file number context value.
5365
POP BC C1
Restore Register Pair BC from the stack. BC receives the zero flag value that was pushed at 534FH.
5366
LD (5386H),HL 22 86 53
Store Register Pair HL to memory location 5386H. This saves the string descriptor pointer for the MID$ target variable. 5386H is self-modifying code - it is the operand of LD HL,(5386H) at 53E4H, allowing later code to retrieve this pointer.
5369
If the NZ FLAG has been set (the file number evaluation returned a non-zero result, indicating the start position was specified as a numeric expression), JUMP to 5374H to handle the numeric MID$ start position.
536B
PUSH DE D5
Save Register Pair DE onto the stack.
536C
PUSH AF F5
Save Register Pair AF onto the stack.
536D
PUSH BC C5
Save Register Pair BC onto the stack.
536E
PUSH DE D5
Save Register Pair DE onto the stack again. The stack is being set up with the parameters for ROM 2888H (the MID$ assignment helper).
536F
GOSUB to ROM 2888H, the MID$ assignment helper. This routine performs the actual substring replacement, copying source string bytes into the target string at the specified position and length.
5372
Unconditional JUMP to 5384H to continue with the post-assignment processing (popping parameters and checking for more assignments).

Numeric Start Position Path
The MID$ start position was specified as a numeric expression. The routine negates it (to create a negative stack offset), adjusts SP, and calls 5CB7H (the file status checker) to validate the operation.

5374
NEG ED 44
Negate Register A (two’s complement). The start position value is negated to compute a negative stack offset for the parameter frame.
5376
LD L,A 6F
Load Register L with the negated value from Register A.
5377
LD H,FFH 26 FF
Load Register H with FFH. Combined with L, HL forms a negative 16-bit offset (sign-extended from the 8-bit negated value).
5379
ADD HL,SP 39
ADD the Stack Pointer to HL. HL = SP + (negative offset) = SP − start_position. This reserves space on the stack for the MID$ parameter frame.
537A
LD SP,HL F9
Set the Stack Pointer to HL. The stack now has room for the parameter frame. WARNING: previous stack entries are preserved but the SP has moved down.
537B
NEG ED 44
Negate Register A again, restoring the original positive start position value.
537D
PUSH DE D5
Save Register Pair DE onto the stack.
537E
PUSH AF F5
Save Register Pair AF (the start position in A) onto the stack.
537F
PUSH BC C5
Save Register Pair BC onto the stack.
5380
PUSH DE D5
Save Register Pair DE onto the stack again. Same parameter setup as the Z path at 536BH-536EH.
5381
GOSUB to 5CB7H in BASIC/CMD, the file status check routine. This validates that the file is in the correct mode for the MID$ assignment operation and returns the buffer parameters.
5384
POP DE D1
Restore Register Pair DE from the stack.
5385
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H. The operand at 5386H is self-modifying code - it is overwritten by the instruction at 5366H or 53AAH with the saved string descriptor pointer for the MID$ target. At runtime, HL holds the target string descriptor address.
5388
EX (SP),HL E3
Exchange HL with the value on top of the stack. The string descriptor pointer is saved on the stack, and the previous stack top (a return address or parameter) is loaded into HL.
5389
RST 10H D7
Fetch the next non-space character from the BASIC text into Register A, advancing HL.
538A
GOSUB to ROM 1F2BH, a PRINT formatting routine. In this context, it processes the expression following the ‘=’ sign in the MID$ assignment.
538D
POP DE D1
Restore Register Pair DE from the stack.
538E
LD A,(DE) 1A
Fetch the next byte from the BASIC text at (DE) into Register A.
538F
CP (HL) BE
Compare Register A against the byte at (HL). This verifies that the source and target are compatible - typically checking that both are strings or both are numeric.
5390
If the NZ FLAG has been set (type mismatch between source and target), JUMP to ROM 1997H, the “Syntax Error” handler.
5393
EX DE,HL EB
Exchange DE and HL. HL now points to the source expression result, DE to the target.
5394
CP 29H FE 29
Compare Register A against 29H (ASCII )). Check whether the current character is a closing parenthesis, which would terminate the MID$ parameter list.
5396
If the Z FLAG has been set (the character is ), closing the MID$ parameter list), JUMP to 539CH to finalize the MID$ assignment.
5398
RST 08H ⇒ 2CH CF 2C
Verify and consume the expected token 2CH (comma). RST 08H checks that the current byte matches 2CH (ASCII ,); if not, it raises a Syntax Error. There is another parameter to parse after the comma.
539A
Unconditional JUMP back to 5361H to parse the next parameter (the length for MID$). This loops back to evaluate additional comma-separated values.
539C
INC DE 13
INCrement DE by 1, advancing past the closing parenthesis in the BASIC text.
539D
RST 10H D7
Fetch the next non-space character from the BASIC text at (HL) into Register A. This should be the ‘=’ sign of the assignment.

539EH - LSET / RSET Common Path

Common processing for LSET and RSET (and the final stage of MID$ assignment after the parameters have been parsed). Expects an ‘=’ sign followed by a string expression. Evaluates the source string via ROM 2337H, verifies end-of-statement, and dispatches to ROM 2819H for the actual string field assignment.

539E
RST 08H ⇒ D5H CF D5
Verify and consume the expected token D5H. In NEWDOS/80 BASIC tokenization, D5H is the ‘=’ assignment operator token (PUSH DE opcode used as inline comparison byte by RST 08H). If the current token does not match, a Syntax Error is raised.
53A0
PUSH DE D5
Save Register Pair DE onto the stack. DE holds the parse context that will be needed after the string expression evaluation.
53A1
GOSUB to ROM 2337H, the string expression evaluator. This evaluates the source string expression (the right-hand side of the LSET/RSET/MID$ assignment) and returns the string descriptor in the standard location.
53A4
DEC HL 2B
DECrement HL by 1. Back up the parse pointer so RST 10H will re-examine the current character to verify end-of-statement.
53A5
RST 10H D7
Fetch the next non-space character from the BASIC text. After the string expression, this should be a statement terminator (colon or end-of-line).
53A6
If the NZ FLAG has been set (the character is not a statement terminator - there is unexpected text after the expression), JUMP to ROM 1997H, the “Syntax Error” handler.
53A9
POP HL E1
Restore Register Pair HL from the stack. HL receives the parse context saved at 53A0H.
53AA
LD (5386H),HL 22 86 53
Store Register Pair HL to memory location 5386H. This updates the [SELF-MODIFYING CODE] target with the string descriptor pointer for the LSET/RSET operation. The LD HL,(5386H) at 53E4H will retrieve this value.
53AD
RST 20H E7
Evaluate the expression type. RST 20H returns: Z flag set if the last expression was a string, NZ if numeric. Carry set indicates single-precision, Minus indicates integer.
53AE
If the NZ FLAG has been set (the expression result is numeric, not a string), JUMP to 53E1H to handle the numeric assignment path (which will pop the flags and either recurse or dispatch).

String Expression Assignment
The source expression is a string. The routine now compares the source string length against the target field length and performs the LSET/RSET operation via ROM 2819H.

53B0
LD DE,(40B3H) ED 5B B3 40
Fetch the string accumulator pointer from ROM system variable 40B3H into Register Pair DE. 40B3H holds a pointer to the most recently computed string result (the source string for the assignment).
53B4
LD HL,(4121H) 2A 21 41
Fetch the value from ROM system variable 4121H (the single-precision accumulator) into Register Pair HL. In the string context, this holds the target field descriptor or length information.
53B7
DEC DE 1B
DECrement DE by 1, adjusting the string pointer to account for the descriptor header offset.
53B8
DEC DE 1B
DECrement DE by 1 again.
53B9
DEC DE 1B
DECrement DE by 1 a third time. DE now points 3 bytes before the string accumulator pointer, which positions it at the start of the 3-byte string descriptor (length, address_low, address_high).
53BA
RST 18H DF
Compare HL vs DE (16-bit comparison). RST 18H sets: Carry if HL < DE, Z if HL == DE, NZ+NC if HL > DE. This compares the target field value against the source string descriptor position.
53BB
If the Z FLAG has been set (HL equals DE - the target and source descriptors are the same), JUMP to 53C2H to handle the self-assignment case (assigning a field to itself).
53BD
GOSUB to ROM 2843H, the string assignment helper. This routine copies the source string data into the string space and updates the descriptor, preparing for the LSET/RSET field copy.
53C0
Unconditional JUMP to 53E1H to continue with the post-assignment processing.

Self-Assignment Handling
The source and target descriptors are the same. A direct copy of the string into string space is required to avoid aliasing issues (the source would be overwritten during copy).

53C2
LD DE,40D3H 11 D3 40
Load Register Pair DE with 40D3H. This is a ROM workspace address used as a temporary buffer for the string copy operation.
53C5
GOSUB to ROM 29F5H, the string space allocator. This allocates space in the string area for the copy and returns the allocated address.
53C8
LD (40B3H),HL 22 B3 40
Store the allocated string space address (returned by 29F5H in HL) to ROM system variable 40B3H, updating the string accumulator pointer to the new copy.
53CB
LD BC,0003H 01 03 00
Load Register Pair BC with 0003H (decimal 3). The string descriptor is 3 bytes long (length + 2-byte address).
53CE
LDIR ED B0
Block copy: copy 3 bytes from (HL) to (DE). This copies the string descriptor from the newly allocated space to the workspace buffer at 40D3H, completing the self-assignment copy setup.
53D0
Unconditional JUMP to 53E1H to continue with the post-assignment path.

Numeric Value Handling
The expression was numeric. The routine reads the stack-saved type byte, and if the type is 03H (integer or special numeric), routes to 53C5H for the string space allocator. Otherwise, it copies the numeric value via 5CB7H.

53D2
POP DE D1
Restore Register Pair DE from the stack.
53D3
CP 03H FE 03
Compare Register A against 03H. This checks the expression type: 03H indicates an integer or special numeric type that requires different handling.
53D5
If the Z FLAG has been set (the type is 03H), JUMP back to 53C5H to handle the value via the string space allocator path.
53D7
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H. This creates a zero value for the upcoming stack pointer calculation.
53DA
ADD HL,SP 39
ADD SP to HL. HL = SP + 0 = SP. This reads the current stack pointer value into HL without modifying it (a common Z80 idiom for reading SP).
53DB
EX DE,HL EB
Exchange DE and HL. DE now holds the current SP value, and HL holds the previous DE value.
53DC
GOSUB to 5CB7H in BASIC/CMD, the file status check routine. This validates the file/buffer state and copies the numeric value into the field buffer.
53DF
EX DE,HL EB
Exchange DE and HL. Restore the register pairing after the CALL.
53E0
LD SP,HL F9
Set the Stack Pointer to HL. This restores the stack to its previous position, cleaning up the parameter frame that was created for the numeric value handling.
53E1
POP AF F1
Restore Register Pair AF from the stack. Register A now holds the file mode byte or expression type that was pushed earlier (at 534DH or 534FH).
53E2
OR A B7
OR Register A with itself. This tests whether A is zero (no more assignments to process) or non-zero (more work remains).
53E3
If the NZ FLAG has been set (A is non-zero - there are more assignment operations to process in a multi-value context), JUMP back to 53D2H to process the next value.
53E5
RST 20H E7
Evaluate the expression type one more time. This final type check ensures the result is compatible with the target field.
53E6
If the Z FLAG has been set (the expression is a string type), GOSUB to ROM 29C2H, the string garbage collector. This reclaims unused string space after the assignment is complete.
53E9
LD HL,(5386H) 2A 86 53
Fetch the saved string descriptor pointer from 5386H (the [SELF-MODIFYING CODE] target set at 5366H or 53AAH) into Register Pair HL. This retrieves the target variable’s string descriptor.
53EC
DEC HL 2B
DECrement HL by 1, backing up one byte before the string descriptor address to position HL for the ROM dispatch.
53ED
RST 10H D7
Fetch the next non-space character from the BASIC text. This reads the byte at the adjusted position.
53EE
POP AF F1
Restore Register Pair AF from the stack. This retrieves the file mode byte (pushed at 534DH) which determines whether this is LSET or RSET.
53EF
JUMP to ROM 2819H, the LSET/RSET ROM handler. This routine performs the final field assignment: if the file mode indicates LSET, the source string is left-justified in the field; if RSET, it is right-justified. Padding with spaces fills any remaining bytes. This is a tail call.

53F2H - INSTR Function

Implements the INSTR(start, search$, target$) function, which returns the position of the first occurrence of search$ within target$ starting at position start. Evaluates the string arguments via ROM routines, performs the search via ROM 2B1FH, and returns the numeric result via ROM 27F8H.

The INSTR function syntax is: INSTR([start,] search$, target$). If the optional start position is omitted, the search begins at position 1. The routine evaluates the string expressions, pushes descriptors onto the stack, and calls the ROM search engine to find the match.

53F2
RST 10H D7
Fetch the next non-space character from the BASIC text at (HL), advancing past spaces. This reads the first argument of the INSTR function.
53F3
GOSUB to ROM 2335H, the string expression evaluator (alternate entry). This evaluates the first string argument and returns the descriptor in the standard string result area.
53F6
RST 20H E7
Evaluate the expression type. Z flag set = string, NZ = numeric. This checks whether the first argument was a string or a numeric start position.
53F7
LD A,01H 3E 01
Load Register A with 01H (decimal 1). This is the default start position - if no explicit start position was specified, the search begins at position 1 (the first character).
53F9
If the NZ FLAG has been set (the first argument was numeric, not a string - it is the optional start position), GOSUB to ROM 2B1FH, the INSTR search helper. This processes the numeric start position and prepares for the string search.
53FC
OR A B7
OR Register A with itself. This tests whether A is zero (which would indicate an invalid start position of 0).
53FD
If the Z FLAG has been set (the start position is 0, which is invalid since BASIC strings are 1-based), JUMP to ROM 1E4AH, the “Illegal Function Call” error handler.
5400
PUSH AF F5
Save Register Pair AF (the start position in A) onto the stack for use by the search routine.
5401
RST 20H E7
Evaluate the expression type again. This check determines whether the next argument is string or numeric.
5402
If the NZ FLAG has been set (the expression is numeric), GOSUB to 545DH which verifies a comma (RST 08H ⇒ 2CH) and evaluates the next string via ROM 2337H.
5405
GOSUB to ROM 0AF4H, the numeric conversion routine. This converts the search string length or position to binary form for the comparison.
5408
LD BC,(4121H) ED 4B 21 41
Fetch the value from the single-precision accumulator at ROM system variable 4121H into Register Pair BC. This holds the numeric result from the conversion.
540C
PUSH BC C5
Save Register Pair BC (the converted numeric value) onto the stack.
540D
GOSUB to 545DH to verify a comma and evaluate the next string expression via ROM 2337H. This evaluates the target string (the string to search within).
5410
RST 08H ⇒ 29H CF 29
Verify and consume the expected token 29H (closing parenthesis )). This terminates the INSTR argument list.
5412
EX (SP),HL E3
Exchange HL with the value on top of the stack. The parse pointer is saved on the stack, and the previously pushed BC value (the numeric parameter) is loaded into HL.
5413
PUSH HL E5
Save HL (the numeric parameter) onto the stack for the comparison routine.
5414
GOSUB to ROM 29D7H, the string descriptor push routine. This pushes the current string descriptor onto the string descriptor stack for the search operation.
5417
EX (SP),HL E3
Exchange HL with the stack top again. Swap the descriptor result with the numeric parameter.
5418
GOSUB to ROM 29DDH, the string descriptor read routine. This reads the string descriptor from the string stack, retrieving the string’s length and address for the comparison.
541B
POP DE D1
Restore Register Pair DE from the stack.
541C
POP BC C1
Restore Register Pair BC from the stack. BC holds the search string descriptor data.
541D
POP AF F1
Restore Register Pair AF from the stack. A holds the start position (pushed at 5400H).
541E
PUSH BC C5
Save Register Pair BC back onto the stack (reordering the parameter order for the search).
541F
PUSH AF F5
Save the start position back onto the stack.
5420
PUSH DE D5
Save Register Pair DE onto the stack. The stack now contains the parameters in the order needed by the search routine.

String Search Loop
The routine now reads the search string descriptor (B=length, DE=address), adjusts the start position (A) relative to the target string, and performs a byte-by-byte comparison. If a match is found, the position is returned; if the search string is longer than the remaining target, no match is possible.

5421
LD B,(HL) 46
Load Register B with the byte at (HL), which is the length of the target string (the string being searched within).
5422
INC HL 23
INCrement HL by 1, advancing to the address field of the target string descriptor.
5423
LD E,(HL) 5E
Fetch the low byte of the target string address into Register E.
5424
INC HL 23
INCrement HL by 1, advancing to the high byte of the target string address.
5425
LD D,(HL) 56
Fetch the high byte of the target string address into Register D. Register Pair DE now holds the address of the target string data.
5426
DEC A 3D
DECrement Register A by 1. The start position is 1-based in BASIC but 0-based for the search loop, so subtract 1 to convert.
5427
LD L,A 6F
Load Register L with the adjusted start position (0-based).
5428
LD H,00H 26 00
Load Register H with 00H. HL = 0-based start position as a 16-bit value.
542A
ADD HL,DE 19
ADD DE (target string address) to HL (start offset). HL now points to the byte within the target string where the search begins.
542B
EX (SP),HL E3
Exchange HL with the stack top. Save the search start pointer on the stack, and retrieve the search string descriptor.
542C
LD C,(HL) 4E
Load Register C with the byte at (HL), which is the length of the search string.
542D
INC HL 23
INCrement HL by 1.
542E
LD E,(HL) 5E
Fetch the low byte of the search string address into Register E.
542F
INC HL 23
INCrement HL by 1.
5430
LD D,(HL) 56
Fetch the high byte of the search string address into Register D. DE now points to the search string data. C holds its length.
5431
POP HL E1
Restore Register Pair HL from the stack. HL now holds the search start pointer within the target string.
5432
SUB B 90
SUBtract Register B (target string length) from Register A (start position). If A >= B, the start position is past the end of the target string, so no match is possible.
5433
If the NO CARRY FLAG has been set (the start position is at or past the end of the target string), JUMP to 5454H to return “not found” (position 0).
5435
NEG ED 44
Negate Register A. A was negative (start − target_length); negating gives the number of remaining characters available for searching.
5437
SUB C 91
SUBtract Register C (search string length) from Register A (remaining characters). If the remaining characters are fewer than the search string, no match is possible.
5438
LD B,A 47
Load Register B with Register A. B now holds the number of positions to try (remaining − search_length + 1).
5439
If the CARRY FLAG has been set (the search string is longer than the remaining target - no match possible), JUMP to 5454H to return “not found”.
543B
INC B 04
INCrement B by 1. Adjust the try count by 1 since the loop uses DJNZ (which pre-decrements).

Outer Search Loop Start

543C
PUSH HL E5
Save Register Pair HL (the current position in the target string) onto the stack. This is the comparison start point.
543D
PUSH DE D5
Save Register Pair DE (the search string address) onto the stack.
543E
PUSH BC C5
Save Register Pair BC (B=remaining tries, C=search length) onto the stack.
543F
INC C 0C
INCrement C by 1. Pre-adjust the search length counter for the comparison loop which uses DEC C first.

Inner Compare Routine Loop Start

5440
DEC C 0D
DECrement C by 1. If C reaches zero, all characters have matched.
5441
If the Z FLAG has been set (C is zero - all search characters have matched), JUMP to 5449H to report a successful match.
5443
LD A,(DE) 1A
Fetch the current byte from the search string (pointed to by DE) into Register A.
5444
CP (HL) BE
Compare the search string byte (A) against the target string byte at (HL).
5445
INC DE 13
INCrement DE by 1, advancing the search string pointer.
5446
INC HL 23
INCrement HL by 1, advancing the target string pointer.
5447
If the Z FLAG has been set (the bytes match), JUMP back to 5440H to compare the next byte.

Inner Compare Routine Loop END

5449
POP BC C1
Restore Register Pair BC from the stack (B=remaining tries, C=search length).
544A
POP DE D1
Restore Register Pair DE (the search string start address) from the stack.
544B
POP HL E1
Restore Register Pair HL (the target string position for this comparison attempt) from the stack.
544C
If the Z FLAG has been set (the entire search string was matched - from the JR Z at 5441H which set Z when C reached zero), JUMP to 5457H to compute and return the match position.
544E
POP AF F1
Restore Register Pair AF from the stack. This retrieves the original start position counter for the position calculation.
544F
INC A 3C
INCrement Register A by 1. Advance the match position counter for the next attempt.
5450
INC HL 23
INCrement HL by 1, advancing the target string position to try the next starting position.
5451
PUSH AF F5
Save the updated position counter back onto the stack.
5452
DECrement Register B and loop back to 543CH if B is not zero. Try the next position in the target string.

Outer Search Loop End

5454
POP AF F1
Restore Register Pair AF from the stack (discard the position counter).
5455
XOR A AF
Set Register A to zero. This is the “not found” return value - INSTR returns 0 when the search string is not found.
5456
PUSH AF F5
Save the zero result onto the stack for the return path at 5457H.
5457
POP AF F1
Restore Register Pair AF from the stack. Register A holds either the 1-based match position or 0 (not found).
5458
GOSUB to ROM 27F8H, the numeric result store routine. This converts the integer result in A to the BASIC numeric format and stores it in the accumulator for return to the calling expression.
545B
POP HL E1
Restore Register Pair HL from the stack. This retrieves the BASIC parse pointer.
545C
RET C9
Return to the caller. The INSTR function result is in the BASIC numeric accumulator.

545DH - Comma + String Expression Helper

Verifies a comma separator in the BASIC text, then evaluates the following string expression via ROM 2337H. Used by the INSTR function to parse its comma-separated arguments.

545D
RST 08H ⇒ 2CH CF 2C
Verify and consume the expected token 2CH (comma ,). If the current byte does not match, a Syntax Error is raised.
545F
JUMP to ROM 2337H, the string expression evaluator. This evaluates the string expression following the comma and returns the result in the standard string descriptor. This is a tail call.

5462H - HEX$ Function

Implements the HEX$(n) function, which converts a numeric value to its hexadecimal string representation. Parses the argument within parentheses, converts each hex digit using a shift-and-accumulate algorithm, and returns the result as a BASIC string. Handles both decimal digits (0-9) and hex letters (A-F) in the input, with overflow detection via the Carry flag.

The HEX$ function accepts a numeric argument and returns a string of hexadecimal digits. The parsing loop accumulates the hex value in DE by shifting left 4 bits (multiply by 16) for each new digit, then adding the digit value. The routine handles up to 4 hex digits (16-bit value in DE).

5462
RST 10H D7
Fetch the next non-space character from the BASIC text at (HL), advancing past spaces.
5463
RST 08H ⇒ 28H CF 28
Verify and consume the expected token 28H (opening parenthesis (). This begins the HEX$ argument list.
5465
This is the second byte of the RST 08H inline operand. In context, if the character matched the parenthesis (Z set), execution continues normally past the RST 08H. The JR Z encoding here is part of the RST 08H / DB mechanism.
5466
LD A,E 7B
Load Register A with Register E. This retrieves the low byte of the current parse pointer or expression state.
5467
LD E,(HL) 5E
Fetch the first character of the hex string argument from (HL) into Register E.
5468
PUSH HL E5
Save Register Pair HL (the parse pointer) onto the stack.
5469
PUSH DE D5
Save Register Pair DE onto the stack.
546A
EX DE,HL EB
Exchange DE and HL. HL now holds the value being constructed, DE holds the parse context.
546B
INC HL 23
INCrement HL by 1.
546C
LD E,(HL) 5E
Fetch the low byte of the string address from (HL) into Register E.
546D
INC HL 23
INCrement HL by 1.
546E
LD D,(HL) 56
Fetch the high byte of the string address into Register D. DE now points to the string data.
546F
LD HL,(40A0H) 2A A0 40
Fetch the BASIC program start address from ROM system variable 40A0H into HL. This is used as a comparison point - the string data address in DE is checked against the program area.
5472
RST 18H DF
Compare HL vs DE (16-bit). Carry set if HL < DE, Z if HL == DE. This checks whether the string data resides below the program start (in the workspace area).
5473
POP HL E1
Restore Register Pair HL from the stack.
5474
PUSH HL E5
Save HL back onto the stack (preserving it for later).
5475
If the NO CARRY FLAG has been set (the string address is at or above the program start), GOSUB to 5CC4H in BASIC/CMD, the numeric expression + file evaluation routine. This processes the numeric argument that was passed to HEX$.
5478
POP HL E1
Restore Register Pair HL from the stack.
5479
EX (SP),HL E3
Exchange HL with the stack top.
547A
RST 08H ⇒ 2CH CF 2C
Verify and consume a comma separator (2CH).
547C
GOSUB to ROM 2B1CH, the string evaluation routine. This evaluates the second argument string expression.
547F
OR A B7
OR Register A with itself. Test whether the result is zero (null or empty string).
5480
If the Z FLAG has been set (the result is zero/empty), JUMP to ROM 1E4AH, the “Illegal Function Call” error handler. An empty string is not valid for this operation.
5483
PUSH AF F5
Save Register Pair AF onto the stack.
5484
LD A,(HL) 7E
Fetch the next character from (HL) into Register A.
5485
CP 2CH FE 2C
Compare Register A against 2CH (comma ,). Check if there is another argument.
5487
LD A,FFH 3E FF
Load Register A with FFH. This is the default “all digits” flag (FFH = 255, meaning format the complete value with no digit limit).
5489
If the NZ FLAG has been set (no comma - no digit count argument), JUMP to 548FH to use the default FFH digit count.
548B
RST 10H D7
Fetch the next non-space character (skip the comma).
548C
GOSUB to ROM 2B1CH to evaluate the digit count argument. Register A returns with the numeric value.
548F
PUSH AF F5
Save the digit count (FFH default or the evaluated value) onto the stack.
5490
RST 08H ⇒ 29H CF 29
Verify and consume the closing parenthesis ).
5492
RST 08H ⇒ D5H CF D5
Verify and consume the ‘=’ assignment operator token (D5H).
5494
GOSUB to ROM 2337H, the string expression evaluator. Evaluate the source string for the HEX$ conversion.
5497
PUSH HL E5
Save the parse pointer onto the stack.
5498
GOSUB to ROM 29D7H, the string descriptor push routine.
549B
POP DE D1
Restore Register Pair DE from the stack.
549C
POP BC C1
Restore Register Pair BC from the stack. C holds the digit count.
549D
POP AF F1
Restore Register Pair AF from the stack.
549E
LD C,A 4F
Load Register C with Register A. C now holds the format parameter.
549F
EX DE,HL EB
Exchange DE and HL.
54A0
EX (SP),HL E3
Exchange HL with the stack top.
54A1
PUSH DE D5
Save DE onto the stack.
54A2
LD A,(HL) 7E
Fetch the target string descriptor length byte from (HL).
54A3
INC HL 23
INCrement HL by 1.
54A4
LD E,(HL) 5E
Fetch the low byte of the target string address.
54A5
INC HL 23
INCrement HL by 1.
54A6
LD D,(HL) 56
Fetch the high byte of the target string address. DE now points to the target string data.
54A7
LD L,C 69
Load Register L with Register C (the format/digit count).
54A8
LD H,00H 26 00
Load Register H with 00H. HL = digit count as a 16-bit value.
54AA
ADD HL,DE 19
ADD DE (target string address) to HL (digit count). HL now points to the byte just past where the last digit would be placed.
54AB
DEC HL 2B
DECrement HL by 1. HL now points to the last byte position in the target field.
54AC
SUB C 91
SUBtract Register C (format width) from Register A (target string length). This checks whether the target field is large enough for the formatted output.
54AD
If the CARRY FLAG has been set (the target string is too short for the requested format width), JUMP to ROM 1E4AH, the “Illegal Function Call” error.
54B0
INC A 3C
INCrement A by 1. Adjust the remaining length for the copy loop.
54B1
CP B B8
Compare Register A against Register B. Check against the source string length.
54B2
If the NO CARRY FLAG has been set (the target has enough room), JUMP to 54B5H to continue with the copy.
54B4
LD B,A 47
Load Register B with Register A. Clamp the copy count to the available target space.
54B5
EX (SP),HL E3
Exchange HL with the stack top.
54B6
LD A,(HL) 7E
Fetch the source string length from (HL).
54B7
INC HL 23
INCrement HL.
54B8
LD E,(HL) 5E
Fetch low byte of source address.
54B9
INC HL 23
INCrement HL.
54BA
LD D,(HL) 56
Fetch high byte of source address. DE points to source string data.
54BB
POP HL E1
Restore HL from stack.
54BC
CP B B8
Compare source length (A) against copy count (B).
54BD
If the CARRY FLAG has been set (source is shorter than copy count), JUMP to 54C0H to use the source length instead.
54BF
LD A,B 78
Load Register A with Register B. Use the copy count (clamped to target size) as the actual copy length.
54C0
GOSUB to 5CB7H in BASIC/CMD, the file status check / string copy routine. This performs the actual byte copy from the source string into the target field, with A specifying the number of bytes to copy.
54C3
POP HL E1
Restore Register Pair HL from the stack. HL receives the BASIC parse pointer.
54C4
RET C9
Return to the caller. The HEX$ conversion or string field operation is complete.

54C5H - Hexadecimal Digit Parser

Parses a hexadecimal string character by character, accumulating the binary value in Register Pair DE. Each hex digit (0-9, A-F) is converted to its 4-bit binary equivalent and shifted into DE. Supports the ‘H’ prefix for hex mode and the ‘O’ prefix for octal mode. The accumulation loop shifts DE left by 4 bits (multiply by 16) and adds the new digit.

54C5
RST 10H D7
Fetch the next non-space character from the BASIC text at (HL) into Register A.
54C6
LD C,A 4F
Load Register C with Register A (save the first character for later type detection).
54C7
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H. DE is the accumulator that will hold the binary value being built from hex digits. It starts at zero.

Digit Parse Loop Start

54CA
LD A,C 79
Load Register A with Register C (the current character).
54CB
CP 48H FE 48
Compare Register A against 48H (ASCII H). If the character is ‘H’, this indicates hexadecimal mode prefix.
54CD
If the NZ FLAG has been set (the character is not H), JUMP to 54F1H to check for the octal prefix ‘O’ or other non-hex characters.

Hex Digit Accumulation Loop
The ‘H’ prefix has been detected. The following code reads hex digits one at a time, converts each to binary (0-15), and accumulates the value in DE by shifting left 4 bits and adding.

54CF
RST 10H D7
Fetch the next non-space character (the first hex digit after ‘H’).
54D0
EX DE,HL EB
Exchange DE and HL. HL now holds the accumulator value, DE holds the parse pointer.
54D1
SUB 30H D6 30
SUBtract 30H from Register A. This converts ASCII digits ‘0’-‘9’ (30H-39H) to binary 0-9.
54D3
CP 0AH FE 0A
Compare the result against 0AH (decimal 10). If the converted value is less than 10, it was a decimal digit (0-9).
54D5
If the CARRY FLAG has been set (the value is 0-9, a valid decimal digit), JUMP to 54DFH to shift the accumulator and add this digit.
54D7
SUB 11H D6 11
SUBtract 11H from the result. This converts ASCII hex letters ‘A’-‘F’ (which after the initial SUB 30H are 11H-16H) to 0-5.
54D9
CP 06H FE 06
Compare the result against 06H. If the value is 0-5, it was a valid hex letter (A-F).
54DB
If the NO CARRY FLAG has been set (the value is 6 or greater - not a valid hex digit), JUMP to 54FFH. This is not a hex character; the hex number has ended.
54DD
ADD 0AH C6 0A
ADD 0AH (decimal 10) to Register A. This converts the 0-5 range back to 10-15 (the binary values for A-F).
54DF
ADD HL,HL 29
ADD HL to itself (shift left 1 bit, multiply by 2). This is the first of four left shifts to multiply the accumulator by 16.
54E0
If the CARRY FLAG has been set (overflow from the shift - the accumulator has exceeded 16 bits), JUMP to 54E9H to handle the overflow error.
54E2
ADD HL,HL 29
Shift left again (multiply by 4 total).
54E3
If overflow, JUMP to 54E9H.
54E5
ADD HL,HL 29
Shift left again (multiply by 8 total).
54E6
If overflow, JUMP to 54E9H.
54E8
ADD HL,HL 29
Shift left one final time (multiply by 16 total). The accumulator has been shifted left 4 bits, making room for the new hex digit in the low nibble.
54E9
If the CARRY FLAG has been set (the accumulator overflowed during any of the four shifts - the hex value exceeds 16 bits / FFFFH), JUMP to ROM 07B2H, the overflow error handler.
54EC
ADD A,L 85
ADD the new digit value (in A, range 0-15) to Register L (the low byte of the accumulator). This combines the shifted accumulator with the new digit.
54ED
LD L,A 6F
Load Register L with the result. The low byte of HL now includes the new hex digit.
54EE
EX DE,HL EB
Exchange DE and HL. DE now holds the updated accumulator, HL holds the parse pointer.

Digit Parse Loop End

54EF
Unconditional JUMP back to 54CAH to parse the next character.

Octal Prefix Check
The character was not ‘H’. Check for ‘O’ (octal mode) or treat as a regular numeric expression.

54F1
LD C,4FH 0E 4F
Load Register C with 4FH (ASCII O). This prepares for the octal prefix comparison.
54F3
CP C B9
Compare Register A (the current character) against Register C (4FH = O). Check for octal prefix.
54F4
If the Z FLAG has been set (the character is O for octal mode), JUMP to 54F7H to process octal digits.
54F6
DEC HL 2B
DECrement HL by 1. Back up the parse pointer so the character will be re-read as part of a standard numeric expression.
54F7
RST 10H D7
Fetch the next non-space character.
54F8
EX DE,HL EB
Exchange DE and HL. HL holds the accumulator (0000H initially), DE holds the parse pointer.
54F9
SUB 30H D6 30
SUBtract 30H from Register A, converting ASCII digit to binary.
54FB
CP 08H FE 08
Compare against 08H. For octal mode, valid digits are 0-7; for decimal mode, this continues to the multiply path at 54E2H.
54FD
If the CARRY FLAG has been set (the digit is 0-7, valid for octal), JUMP back to 54E2H. Note: for octal, only 3 left-shifts are needed (multiply by 8), and the entry at 54E2H performs shifts starting from the “multiply by 4” point (3 shifts total = multiply by 8). This elegantly reuses the hex shift code for octal conversion.
54FF
GOSUB to ROM 0A9AH, the 16-bit multiply routine (HL = HL × DE). This handles the case where the input is a decimal number rather than hex or octal - the parsed decimal value in HL is multiplied by DE for the final conversion.
5502
EX DE,HL EB
Exchange DE and HL. Move the result into DE.
5503
RET C9
Return to the caller. Register Pair DE holds the converted numeric value.

5504H - OPEN Statement Parameter Decoder

Decodes the mode parameter of the OPEN statement. Evaluates a single-character string expression to determine the file I/O mode: ‘I’ (Input), ‘O’ (Output), ‘E’ (Extended/append), ‘R’ (Random access), or ‘D’ (Direct access). Each mode maps to a specific 16-bit configuration word that encodes the FCB status flags and record length parameters. After determining the mode, the routine parses the file number, sets up the FCB, and optionally parses a record length and field width.

The OPEN statement syntax is: OPEN mode$, #filenum, filespec$ [,reclen]. The mode$ is a single-character string: “I” for input, “O” for output, “E” for extended (append), “R” for random access, “D” for direct access. Each mode sets different bits in the FCB status word and determines default record lengths.

5504
GOSUB to 6190H in BASIC/CMD, the string expression + descriptor evaluator. This evaluates the mode string expression and returns: Z flag set if the string is 1 byte long (expected for mode character), B = string length, HL = string descriptor address.
5507
If the NZ FLAG has been set (the string length is not 1 - the mode is not a single character), JUMP to 552EH to raise a “Bad file mode” error. The OPEN mode must be exactly one character.
5509
DEC B 05
DECrement Register B by 1. B held the string length (1); after decrement, B = 0. This confirms the string was exactly 1 byte.
550A
If the NZ FLAG has been set (B was not 1 after decrement - unexpected extra length), JUMP to 552EH for the error. This double-check ensures exactly one character.
550C
LD A,(HL) 7E
Fetch the single mode character from the string data (pointed to by HL) into Register A. This is the character ‘I’, ‘O’, ‘E’, ‘R’, or ‘D’.

Mode Character Cascade
The routine compares the mode character against each valid option and loads the corresponding configuration word into HL. The configuration word encodes: high byte = FCB status flags, low byte = default record length or buffer parameter.

550D
LD HL,0128H 21 28 01
Load Register Pair HL with 0128H. This is the configuration word for mode ‘I’ (Input): high byte 01H = read mode (bit 0 set), low byte 28H = default record/buffer size of 40 bytes.
5510
CP 49H FE 49
Compare Register A against 49H (ASCII I). Check for Input mode.
5512
If the Z FLAG has been set (the mode character is I for Input), JUMP to 5531H to store the configuration word and continue parsing.
5514
LD HL,8448H 21 48 84
Load Register Pair HL with 8448H. This is the configuration word for mode ‘O’ (Output): high byte 84H = write mode + error flag (bits 7,2 set), low byte 48H = default buffer size of 72 bytes.
5517
CP 4FH FE 4F
Compare Register A against 4FH (ASCII O). Check for Output mode.
5519
If the Z FLAG has been set (mode is O for Output), JUMP to 5531H.
551B
LD HL,8248H 21 48 82
Load Register Pair HL with 8248H. This is the configuration word for mode ‘E’ (Extended/append): high byte 82H = write mode + dirty flag (bits 7,1 set), low byte 48H = default buffer size of 72 bytes.
551E
CP 45H FE 45
Compare Register A against 45H (ASCII E). Check for Extended mode.
5520
If the Z FLAG has been set (mode is E for Extended), JUMP to 5531H.
5522
LD HL,80ECH 21 EC 80
Load Register Pair HL with 80ECH. This is the configuration word for mode ‘R’ (Random): high byte 80H = error/random flag (bit 7 set), low byte ECH = 236 bytes default record length.
5525
CP 52H FE 52
Compare Register A against 52H (ASCII R). Check for Random access mode.
5527
If the Z FLAG has been set (mode is R for Random), JUMP to 5531H.
5529
LD HL,01ECH 21 EC 01
Load Register Pair HL with 01ECH. This is the configuration word for mode ‘D’ (Direct): high byte 01H = read mode (bit 0 set), low byte ECH = 236 bytes default record length.
552C
CP 44H FE 44
Compare Register A against 44H (ASCII D). Check for Direct access mode.
552E
If the NZ FLAG has been set (the character does not match any valid mode letter - not I, O, E, R, or D), JUMP to 5DA9H in BASIC/CMD, the “Bad file mode” error handler.

Store Mode Configuration and Parse File Number
The mode character has been matched and the configuration word is in HL. The routine now pushes the configuration onto the stack (via EX (SP),HL), parses the comma separator and file number, sets up the FCB, and processes optional record length / field width parameters.

5531
EX (SP),HL E3
Exchange HL with the value on top of the stack. The mode configuration word is saved on the stack, and the previous stack value (the caller’s parse pointer) is loaded into HL. This effectively pushes the configuration for later retrieval.
5532
RST 08H ⇒ 2CH CF 2C
Verify and consume a comma separator (2CH) between the mode and file number. Raises Syntax Error if missing.
5534
GOSUB to 62C0H in BASIC/CMD, the file + comma + status routine. This evaluates the file number expression (after the ‘#’), validates it, checks the file status, and returns with IX pointing to the FCB and the file state configured.
5537
POP BC C1
Restore Register Pair BC from the stack. BC receives the mode configuration word (pushed via EX (SP),HL at 5531H): B = FCB status flags high byte, C = default record length / buffer parameter.
5538
GOSUB to 6087H in BASIC/CMD, the SVC setup routine. This initializes the FCB for the specified file mode, setting up the SVC parameter block and configuring the file access type based on the mode flags in BC.
553B
LD DE,0100H 11 00 01
Load Register Pair DE with 0100H (decimal 256). This is the maximum record length value - DE serves as a ceiling for the record length parameter if one is specified.
553E
GOSUB to 6368H in BASIC/CMD, the comma checker. This checks if there is a comma following the filespec (indicating an optional record length parameter). Returns Z if a comma was found and consumed, NZ if no comma (end of statement).
5541
If the Z FLAG has been set (no comma - no record length parameter specified), JUMP to 55C9H to finalize the OPEN with the default record length in DE.

Record Length Parameter Parsing
A comma was found, so there is a record length parameter. The routine evaluates the string expression to determine the record length type, then branches based on the result.

5544
PUSH BC C5
Save Register Pair BC (the mode configuration) onto the stack.
5545
GOSUB to 6190H, the string expression + descriptor evaluator. This evaluates the record length expression.
5548
If the NZ FLAG has been set (the expression was numeric, not a string), JUMP to 555AH to handle the numeric record length path.
554A
LD A,(HL) 7E
Fetch the first byte of the string result (the record length type character).
554B
INC HL 23
INCrement HL by 1.
554C
LD E,(HL) 5E
Fetch the low byte of the associated value.
554D
POP HL E1
Restore Register Pair HL from the stack.
554E
LD D,B 50
Load Register D with Register B. D receives the byte count.
554F
POP BC C1
Restore Register Pair BC from the stack (mode configuration).
5550
SET 7,C CB F9
SET bit 7 of Register C. This marks the mode configuration to indicate that a record type specifier was provided.
5552
CP 46H FE 46
Compare Register A against 46H (ASCII F). Check if the type specifier is ‘F’ (fixed-length records).
5554
If the NZ FLAG has been set (the character is not F), JUMP to 557DH to check for other record type specifiers (‘M’ for move mode).
5556
SET 1,C CB C9
SET bit 1 of Register C. For ‘F’ (fixed) mode, bit 1 indicates write access is enabled (the file is opened for both read and write in fixed-record mode).
5558
Unconditional JUMP to 5583H to continue processing the record type parameters (decrement D twice for the type byte overhead).

Numeric Record Length Path
The record length was specified as a numeric expression. Validate that a record type has been specified (bit 7 of C), then evaluate the numeric value.

555A
POP HL E1
Restore Register Pair HL from the stack.
555B
POP BC C1
Restore Register Pair BC (mode configuration) from the stack.
555C
BIT 7,C CB 79
Test bit 7 of Register C. Bit 7 indicates whether a record type specifier (‘F’, ‘M’, etc.) was previously set. If not, a numeric record length without a type specifier is a syntax error.
555E
If the Z FLAG has been set (bit 7 is clear - no record type was specified before the numeric length), JUMP to ROM 1997H, the “Syntax Error” handler.
5561
PUSH BC C5
Save Register Pair BC (mode config) onto the stack.
5562
PUSH HL E5
Save Register Pair HL (parse pointer) onto the stack.
5563
GOSUB to ROM 0A7FH, the numeric expression evaluator. This evaluates the record length expression and returns the result in the BASIC accumulator (HL = integer value).
5566
DEC HL 2B
DECrement HL by 1. Adjust for 0-based vs 1-based indexing.
5567
LD A,H 7C
Load Register A with the high byte of the record length result.
5568
OR A B7
OR A with itself. Test whether the high byte is non-zero (record length > 255).
5569
If the NZ FLAG has been set (record length > 255 - exceeds the 8-bit maximum), JUMP to 557AH to raise a “Record number overflow” error.
556B
INC HL 23
INCrement HL by 1 (undo the DEC at 5566H to restore the original value).
556C
LD A,L 7D
Load Register A with the low byte of the record length. A now holds the record length (1-255).
556D
OR A B7
OR A with itself. Test whether the record length is zero (which is invalid).
556E
If the Z FLAG has been set (record length is zero), JUMP to 55BFH to use the record length as-is (DE will be loaded from HL).
5570
INC (IX+11H) DD 34 11
INCrement the byte at IX+11H (an FCB field) by 1. IX+11H is the record count or access counter within the FCB. Incrementing it indicates that a record length parameter was explicitly provided.
5573
LD A,(649FH) 3A 9F 64
Fetch the verify flag from memory location 649FH in BASIC/CMD into Register A. 649FH is the verify-on-write flag, set during BASIC initialization based on the ‘V’ command-line option.
5576
CP 02H FE 02
Compare Register A against 02H. Check if the verify flag indicates “verify after every write” mode (value 02H).
5578
If the Z FLAG has been set (verify mode is 02H), JUMP to 55BFH to continue with the current record length.
557A
JUMP to 5DD6H in BASIC/CMD, the “Record number overflow” error handler. The record length exceeded 255 or was otherwise invalid.

‘M’ (Move) Mode Check
The record type specifier was not ‘F’. Check for ‘M’ (move mode) or raise an error.

557D
CP 4DH FE 4D
Compare Register A against 4DH (ASCII M). Check if the record type specifier is ‘M’ (move mode).
557F
If the NZ FLAG has been set (not ‘M’), JUMP to 5585H. If the character is not ‘F’ or ‘M’, it will be checked against other possible specifiers.
5581
SET 0,C CB C1
SET bit 0 of Register C. For ‘M’ (move) mode, bit 0 indicates read access is enabled.
5583
DEC D 15
DECrement Register D by 1. Account for the record type specifier byte consumed from the parameter string.
5584
DEC D 15
DECrement Register D by 1 again. Two bytes of overhead (type letter + separator) are subtracted from the remaining parameter count.
5585
If the NZ FLAG has been set (D is not zero after the two decrements - there are extra unexpected characters in the type specifier), JUMP to 5DCAH in BASIC/CMD, an error handler.
5588
RES 3,C CB 99
RESset (clear) bit 3 of Register C. Clear the buffer flag in the mode configuration.
558A
RES 2,C CB 91
RESset (clear) bit 2 of Register C. Clear the random access flag. These two clears reset the access mode bits before setting new ones based on the specifier.
558C
LD A,E 7B
Load Register A with Register E (the parameter value associated with the record type specifier).
558D
LD DE,7FFFH 11 FF 7F
Load Register Pair DE with 7FFFH (decimal 32767). This is the maximum field width for ‘I’ (Input) mode.
5590
CP 49H FE 49
Compare Register A against 49H (ASCII I). Check for Input field type.
5592
If the Z FLAG has been set (field type is I for Input), JUMP to 55C9H to finalize with DE=7FFFH as the maximum record/field size.
5594
LD DE,0FFFH 11 FF 0F
Load Register Pair DE with 0FFFH (decimal 4095). This is the maximum field width for fixed (‘F’) and update (‘U’) modes.
5597
BIT 1,C CB 49
Test bit 1 of Register C. Bit 1 was set for ‘F’ (fixed) mode at 5556H.
5599
If the NZ FLAG has been set (bit 1 is set - this is fixed-record mode), JUMP to 559FH to check for the ‘F’ field width specifier.
559B
CP 55H FE 55
Compare Register A against 55H (ASCII U). Check for Update field type.
559D
If the Z FLAG has been set (field type is U for Update), JUMP to 55A5H to set update mode flags.
559F
CP 46H FE 46
Compare Register A against 46H (ASCII F). Check for Fixed field type (in the context of record length parsing).
55A1
If the NZ FLAG has been set (not ‘F’ and not ‘U’ - unrecognized field type), JUMP back to 5585H to raise an error.
55A3
SET 2,C CB D1
SET bit 2 of Register C. Bit 2 marks random access mode in the FCB status flags.
55A5
SET 3,C CB D9
SET bit 3 of Register C. Bit 3 marks the buffer flag in the FCB status.
55A7
GOSUB to 6368H, the comma checker. Check if there is another comma (indicating a field width parameter follows).
55AA
If the Z FLAG has been set (no comma - no field width parameter), JUMP to 55C4H to check the access mode and finalize.
55AC
PUSH BC C5
Save Register Pair BC (mode config) onto the stack.
55AD
GOSUB to 6190H, evaluate the field width expression as a string.
55B0
If the Z FLAG has been set (the expression returned an empty/null string), JUMP to 5DD6H, the overflow error handler.
55B3
GOSUB to ROM 0A7FH, the numeric expression evaluator, to get the field width as a number.
55B6
LD A,H 7C
Load Register A with the high byte of the result.
55B7
CP 10H FE 10
Compare A against 10H (decimal 16). Check if the field width is 4096 or more (H ≥ 10H means HL ≥ 1000H).
55B9
If the NO CARRY FLAG has been set (field width ≥ 4096), JUMP to 5DD6H, the overflow error.
55BC
OR L B5
OR the high byte (A) with the low byte (L). Test whether the combined field width is zero.
55BD
If the Z FLAG has been set (field width is zero), JUMP back to 55B0H which routes to the overflow error. A field width of zero is invalid.
55BF
EX DE,HL EB
Exchange DE and HL. DE now holds the field width value, and HL is freed.
55C0
POP HL E1
Restore Register Pair HL from the stack.
55C1
POP BC C1
Restore Register Pair BC (mode configuration) from the stack.
55C2
Unconditional JUMP to 55C9H to finalize the OPEN with the specified field width in DE.
55C4
BIT 2,C CB 51
Test bit 2 of Register C (random access flag). If random access was specified but no field width was given, this is an error.
55C6
If the NZ FLAG has been set (random access mode was set but no field width was provided), JUMP to 5DD6H, the overflow error. Random access mode requires an explicit field width.

OPEN Finalization
Store the record offset (DE) and FCB status word (BC) to the FCB work area at 5700H, then dispatch to ROM 2169H for post-statement cleanup.

55C9
LD (5702H),DE ED 53 02 57
Store Register Pair DE to memory location 5702H (the FCB work area byte offset within record field). This records the record length / field width parameter for the opened file.
55CD
LD (5700H),BC ED 43 00 57
Store Register Pair BC to memory location 5700H (the FCB status word in the work area). This records the mode configuration flags (read/write/random/buffer bits) for the opened file.
55D1
JUMP to ROM 2169H, the post-statement cleanup routine. This completes the OPEN statement execution and returns to the BASIC command loop. This is a tail call.

55D4H - INPUT# Statement Handler Entry

Entry point for the INPUT# statement. Saves the guard flag state via 6382H, checks the expression type via RST 20H, and dispatches to the INPUT# field reader at 5621H with the appropriate type indicator in Register D (03H for string, 05H for numeric).

55D4
GOSUB to 6382H in BASIC/CMD, the guard flag save-and-clear routine. This saves the current guard flag state and clears it, ensuring that error handling during the INPUT# operation does not trigger the single-step guard mechanism.
55D7
POP AF F1
Restore Register Pair AF from the stack. This retrieves the caller’s context (the file status and parse state).
55D8
LD D,03H 16 03
Load Register D with 03H. This sets the default field type to 03H (string type). Register D serves as a type indicator that controls how the INPUT# field reader interprets the data it reads from the file.
55DA
RST 20H E7
Evaluate expression type. RST 20H returns with flags: Z = string, NZ = numeric. This checks whether the target variable (the variable being assigned by INPUT#) is a string or numeric type.
55DB
If the Z FLAG has been set (the target variable is a string type), JUMP forward to 55DFH, keeping D=03H (string mode).
55DD
LD D,05H 16 05
Load Register D with 05H. The target variable is numeric, so override the type indicator to 05H (numeric mode). In numeric mode, the field reader treats the data differently - leading spaces are significant and commas within quoted strings are handled differently.
55DF
PUSH AF F5
Save Register Pair AF (the expression type flags) onto the stack.
55E0
PUSH HL E5
Save Register Pair HL (the BASIC text pointer) onto the stack.
55E1
GOSUB to 5621H, the INPUT# field reader. This routine reads one comma-separated field from the disk file, handling quoted strings, CR/LF sequences, and leading space suppression. On return, the field data is in the line input buffer, and Z/NZ flags indicate success or EOF.
55E4
POP HL E1
Restore Register Pair HL (the BASIC text pointer) from the stack.
55E5
JUMP to ROM 2243H, the BASIC continuation routine. This processes the field data read by 5621H, assigns it to the target variable, and continues executing the INPUT# statement (which may have multiple variables separated by commas). This is a tail call.

55E8H - PRINT# / PRINT USING# Extension

Handles the PRINT# statement with optional USING format specifier. Checks for the USING keyword token (89H), evaluates the format string, then checks whether the output target is a file (‘#’) or the screen, and dispatches accordingly.

55E8
RST 08H ⇒ 89H CF 89
RST 08H followed by byte 89H. Verify that the current token is 89H, which is the BASIC token for the USING keyword. If the token does not match, a “Syntax Error” is raised. This confirms the statement is PRINT USING# (or PRINT#...USING).
55EA
GOSUB to ROM 2828H, the USING format handler. This evaluates the format string that follows the USING keyword and sets up the internal formatting state for subsequent PRINT output.
55ED
LD A,(HL) 7E
Fetch the current character from the BASIC text at (HL) into Register A. After the USING format string has been processed, this checks the next token to determine the output destination.
55EE
CP 23H FE 23
Compare Register A against 23H (ASCII #). If the character is ‘#’, the output is directed to a file (PRINT USING#n). If not, the output goes to the screen.
55F0
If the Z FLAG has been set (the character is #, indicating file output), JUMP forward to 560EH to handle the LINE INPUT# / file output path with file setup.

Screen Output Path
No ‘#’ was found, so the PRINT USING output goes to the screen. The routine evaluates the string expression to print, then dispatches to the ROM PRINT handler.

55F2
GOSUB to ROM 21CDH, the string expression evaluator. This evaluates the expression to be printed and prepares the string result.
55F5
GOSUB to 5E7BH in BASIC/CMD, the FIELD evaluator. This evaluates the field specification for the PRINT output.
55F8
PUSH HL E5
Save Register Pair HL (the BASIC text pointer) onto the stack.
55F9
PUSH DE D5
Save Register Pair DE onto the stack.
55FA
GOSUB to ROM 0361H. This is a ROM string/output routine that processes the formatted output for the PRINT USING statement.
55FD
If the CARRY FLAG has been set (the ROM routine indicated an error or special condition), JUMP forward to 5609H to handle the error exit path.
55FF
LD B,00H 06 00
Load Register B with 00H. This parameter is passed to ROM 2868H to indicate no special formatting flags.
5601
GOSUB to ROM 2868H, the numeric/string output routine. This outputs the formatted result to the screen using the USING format specification.
5604
POP HL E1
Restore Register Pair HL from the stack.
5605
XOR A AF
Set Register A to 00H and clear all flags. This sets up the success return status (Z flag set, A=0).
5606
JUMP to ROM 1F33H, the ROM PRINT handler. This continues the PRINT statement processing (handling additional items separated by semicolons or commas, or terminating the statement). This is a tail call.
5609
POP AF F1
Restore Register Pair AF from the stack, discarding the saved DE value.
560A
POP AF F1
Restore Register Pair AF again, discarding the saved HL value. Both stacked values are discarded because the error path does not need them.
560B
JUMP to ROM 1DBEH, the BASIC runtime continuation routine. This aborts the PRINT USING operation and returns to the BASIC command loop after the error condition from 0361H.

560EH - LINE INPUT# Handler

Handles the LINE INPUT# statement, which reads an entire line from a disk file into a string variable without any field parsing (no comma or quote interpretation). Sets up the file context via 5F36H, evaluates the FIELD expression via 5E7BH, pushes the ROM cleanup address 2169H as a return point, and dispatches to the INPUT# field reader at 5621H.

560E
GOSUB to 5F36H in BASIC/CMD, the file setup routine. This sets up the file channel for the # file number that follows, configuring IX to point to the correct FCB and establishing the I/O context.
5611
GOSUB to 5E7BH in BASIC/CMD, the FIELD evaluator. This evaluates the field specification for the LINE INPUT# operation.
5614
LD BC,2169H 01 69 21
Load Register Pair BC with 2169H, the address of ROM’s post-statement cleanup routine. This address will be pushed onto the stack as a return address.
5617
PUSH BC C5
Save 2169H onto the stack. When the field reader at 5621H returns and the subsequent code completes, this return address will be popped, causing execution to transfer to ROM 2169H for post-statement cleanup.
5618
PUSH HL E5
Save Register Pair HL (the BASIC text pointer) onto the stack.
5619
PUSH DE D5
Save Register Pair DE onto the stack.
561A
XOR A AF
Set Register A to 00H and clear all flags. A=0 is the initial state for the field reader (no pending character).
561B
LD D,A 57
Load Register D with 00H (from Register A). D=00H signals LINE INPUT# mode to the field reader - in this mode, the reader does not interpret commas as field separators or quotes as delimiters. It reads the entire line until CR/LF.
561C
GOSUB to 5621H, the INPUT# field reader. With D=00H (LINE INPUT# mode), the reader captures everything up to the next CR/LF without field parsing.
561F
JUMP back to 5604H (POP HL / XOR A / JP 1F33H) to restore HL, set the success status, and dispatch to the ROM PRINT handler for output completion.

5621H - INPUT# Field Reader (CSV Parser)

The core field reader for INPUT# and LINE INPUT# statements. Reads bytes one at a time from the disk file via 60F2H, parsing comma-separated values (CSV format) with full support for quoted fields, leading space suppression, CR/LF record terminators, and buffer overflow detection. Register D controls the parsing mode: D=00H for LINE INPUT# (raw mode, no field parsing), D=03H for string INPUT#, D=05H for numeric INPUT#. The reader uses a state machine encoded in the bit flags of Register D to track whether it is inside a quoted field (bit 3), has seen a leading space (bit 0), or has processed a field delimiter (bit 2).

Register D Bit Flags (Field Reader State)
Bit 0
Leading-space-seen flag. When set, leading spaces are skipped (for numeric mode). Cleared after the first non-space character.
Bit 1
Quote-field mode active. Set when the opening quote of a quoted field is processed.
Bit 2
Space-after-field flag. When set, trailing spaces between the field data and the delimiter are being consumed.
Bit 3
Inside-quoted-field flag. Set when a closing quote is encountered inside a quoted field, signaling that the next character determines whether the field ends (comma/CR) or the quote was doubled (another quote).

5621
PUSH AF F5
Save Register Pair AF (the initial state: A=0 for no pending character, or A=last character if re-entering) onto the stack.
5622
LD HL,(40A7H) 2A A7 40
Fetch the line input buffer pointer from ROM system variable at 40A7H into Register Pair HL. 40A7H holds the address of the BASIC line input buffer where the field data will be assembled character by character.
5625
LD B,FFH 06 FF
Load Register B with FFH (decimal 255). This is the maximum field length counter - the field reader will accept up to 255 characters before triggering an overflow condition. B is decremented for each character stored.
5627
LD C,B 48
Load Register C with the value of B (FFH). Register C serves as the “last character read” register, initialized to FFH (no valid last character).
5628
PUSH HL E5
Save Register Pair HL (the buffer pointer) onto the stack. This is the buffer start address, needed later to compute the field length.

Main Read Loop Start

5629
GOSUB to 60F2H in BASIC/CMD, the file read routine. This reads one byte from the currently open disk file into Register A. Returns Z if EOF, NZ with the byte in A otherwise.
562C
If the NZ FLAG has been set (the read returned an error or unexpected status - not a normal byte and not EOF), JUMP to 5DAFH in BASIC/CMD, the error handler.

Leading Space Check
The byte was read successfully (Z was set by 60F2H meaning a valid byte is in A). Check if it is a space (20H) and whether leading spaces should be suppressed (bit 0 of D).

562F
CP 20H FE 20
Compare Register A against 20H (ASCII space). If the byte is a space, the Z FLAG is set.
5631
If the NZ FLAG has been set (the byte is not a space), JUMP forward to 5637H to check for quote characters. Not a space, so no leading-space processing needed.
5633
BIT 0,D CB 42
Test bit 0 of Register D (the leading-space-seen flag). If bit 0 is set, leading spaces are being suppressed.
5635
If the NZ FLAG has been set (bit 0 is set - leading space suppression is active), JUMP back to 5629H to read the next byte, effectively skipping this leading space.

Quote Character Check
Check if the byte is a double-quote (22H). If so, toggle the quoted-field state in Register D.

5637
CP 22H FE 22
Compare Register A against 22H (ASCII double-quote "). If the byte is a quote, the Z FLAG is set.
5639
If the NZ FLAG has been set (the byte is not a quote), JUMP to 5643H to check for CR.
563B
BIT 1,D CB 4A
Test bit 1 of Register D (the quote-field mode flag). If bit 1 is already set, we are inside a quoted field and this quote is either a closing quote or a doubled quote (escaped quote).
563D
If the Z FLAG has been set (bit 1 is clear - not currently in a quoted field), JUMP to 5643H. The quote is an opening quote for a new quoted field, but the code processes it in the normal character path.
563F
SET 3,D CB DA
SET bit 3 of Register D. This sets the “inside-quoted-field closing-quote-seen” flag. The next character will determine whether this was a doubled quote (another 22H follows) or the end of the quoted field.
5641
JUMP to 5667H to save the current character to Register C and call 56A7H for the next byte lookahead.

Carriage Return Check
Check if the byte is a carriage return (0DH), which indicates end-of-record in the file.

5643
CP 0DH FE 0D
Compare Register A against 0DH (ASCII carriage return). If the byte is CR, the Z FLAG is set, indicating end-of-line in the file data.
5645
If the Z FLAG has been set (the byte is CR), JUMP to 5683H to handle the CR/LF end-of-record processing.

Space-After-Field Check
Check if the byte is a space when already in “trailing space” mode (bit 2 of D). If so, the space is consumed as a trailing delimiter.

5647
CP 20H FE 20
Compare Register A against 20H (ASCII space).
5649
If the NZ FLAG has been set (not a space), JUMP to 564FH to check for comma.
564B
BIT 2,D CB 52
Test bit 2 of Register D (the space-after-field flag). If set, trailing spaces after the field data are being consumed.
564D
If the NZ FLAG has been set (bit 2 is set - trailing space consumption mode), JUMP to 5673H to call 56A7H and continue consuming trailing spaces.

Comma Check (Field Separator)
Check for comma (2CH), which separates fields in CSV format.

564F
CP 2CH FE 2C
Compare Register A against 2CH (ASCII ,). If the byte is a comma, the Z FLAG is set, indicating a field separator.
5651
If the NZ FLAG has been set (the byte is not a comma), JUMP to 5657H to check for LF.
5653
BIT 0,D CB 42
Test bit 0 of Register D. In this context, bit 0 being set means the comma should terminate the field (we are not inside a quoted string where commas are literal).
5655
If the NZ FLAG has been set (bit 0 is set - comma is a field terminator), JUMP to 569AH to terminate the current field and return.

Line Feed Check
Check for LF (0AH), which typically follows CR in a CR/LF sequence.

5657
CP 0AH FE 0A
Compare Register A against 0AH (ASCII line feed). If the byte is LF, the Z FLAG is set.
5659
If the NZ FLAG has been set (the byte is not LF), JUMP to 565FH to handle it as a normal data character.
565B
BIT 0,D CB 42
Test bit 0 of Register D. If set, the LF acts as a record terminator (similar to comma).
565D
If the NZ FLAG has been set (bit 0 is set - LF terminates the field after a preceding CR), JUMP to 5667H to save the character and look ahead.

Normal Data Character
The byte is a regular data character (not a space, quote, CR, LF, or comma in terminating context). Store it in the buffer and continue.

565F
OR A B7
OR Register A with itself. Test whether A is 00H (null byte). If A is zero, the Z FLAG is set.
5660
If the Z FLAG has been set (the byte is a null 00H), JUMP to 5667H to skip the null without storing it. Null bytes are not stored in the field buffer.
5662
LD (HL),A 77
Store the data byte (Register A) into the buffer at (HL). This adds the character to the field being assembled.
5663
INC HL 23
INCrement Register Pair HL by 1, advancing the buffer write pointer to the next position.
5664
DEC B 05
DECrement Register B (the remaining buffer space counter) by 1.
5665
If the Z FLAG has been set (B has reached zero - the 255-character buffer is full), JUMP to 569AH to terminate the field. Buffer overflow - no more characters can be stored.
5667
LD C,A 4F
Load Register C with Register A, saving the current character as the “last character read” for lookahead comparisons.
5668
GOSUB to 56A7H, the file read helper. This reads the next byte from the file via 60F2H. On EOF (Z), it returns normally. On error, it pops the return address and jumps to the field termination at 569AH.
566B
BIT 3,D CB 5A
Test bit 3 of Register D (the “closing quote seen inside quoted field” flag). If set, the previous character was a quote inside a quoted field, and we need to check whether this character is another quote (doubled) or a field delimiter.
566D
If the Z FLAG has been set (bit 3 is clear - no pending closing quote), JUMP back to 5643H to continue normal character processing in the main loop. [MAIN READ LOOP END - loops back to character classification]
566F
CP 22H FE 22
Compare Register A against 22H (double-quote). If this character is also a quote, the pair forms a doubled/escaped quote, and a single quote should be stored in the field.
5671
If the NZ FLAG has been set (the character is NOT a quote - the previous quote was the closing quote of the field), JUMP back to 565FH to process this character as a normal post-field character (it will be the delimiter).

Trailing Space Consumption
After a non-quoted field value, consume any trailing spaces before the delimiter.

5673
GOSUB to 56A7H to read the next byte from the file.
5676
CP 20H FE 20
Compare Register A against 20H (space).
5678
LD C,A 4F
Load Register C with A (save the character as “last read”).
5679
If the Z FLAG has been set (the character is a space), JUMP back to 5673H to continue consuming trailing spaces. [SPACE CONSUMPTION LOOP]
567B
CP 2CH FE 2C
Compare Register A against 2CH (comma). If the first non-space character after the field is a comma, the field is properly terminated.
567D
If the Z FLAG has been set (comma found), JUMP to 569AH to terminate the field normally.
567F
CP 0DH FE 0D
Compare Register A against 0DH (carriage return). CR also terminates the field.
5681
If the NZ FLAG has been set (the character is neither comma nor CR - an unexpected character after the field), JUMP to 568FH to handle the unexpected delimiter by setting the position parameter and issuing a repositioning SVC.

CR/LF End-of-Record Handler
A carriage return was found. Check if the previous character was LF (forming a LF+CR sequence, which is unusual but handled), and then look ahead for the expected LF that follows CR.

5683
LD A,C 79
Load Register A with Register C (the last character that was read before the CR). This checks whether the previous character was a line feed.
5684
CP 0AH FE 0A
Compare Register A against 0AH (line feed). If the previous character was LF and the current is CR, this is a LF+CR sequence (reverse order).
5686
If the Z FLAG has been set (previous character was LF - this is a LF+CR sequence), JUMP back to 565BH to check bit 0 of D and handle the LF as a terminator.
5688
GOSUB to 56A7H to read the next byte (expecting LF after CR).
568B
CP 0AH FE 0A
Compare Register A against 0AH (line feed). After a CR, the expected next byte is LF.
568D
If the Z FLAG has been set (LF found - proper CR/LF sequence), JUMP to 569AH to terminate the field normally. The record is complete.

Unexpected Delimiter Handler
An unexpected character was found after the field data (not comma, CR, or LF). Set the position parameter flag at 5716H and issue a repositioning SVC to resync the file position.

568F
LD A,01H 3E 01
Load Register A with 01H. This value will be written to 5716H as a flag indicating that the file position needs adjustment.
5691
LD (5716H),A 32 16 57
Store 01H to memory location 5716H (the position parameter byte in the FCB work area). this is self-modyfing code relative to the FCB - setting 5716H to 01H signals to the file positioning logic that a partial record was read and the position needs adjustment.
5694
LD DE,4445H 11 45 44
Load Register Pair DE with 4445H. This is the SVC parameter for the file repositioning operation - the high byte 44H is the SVC function code, and 45H is the sub-parameter. SVC 4445H positions the FCB back by one record to account for the partial read.
5697
GOSUB to 621EH in BASIC/CMD, the common SVC dispatch routine. This executes the SVC operation specified in DE (4445H), repositioning the file to compensate for the extra byte that was read past the field boundary.

Field Termination
The field has been completely read (terminated by comma, CR/LF, buffer overflow, or unexpected delimiter). Null-terminate the buffer, restore the buffer start pointer, and return.

569A
LD B,00H 06 00
Load Register B with 00H. This is the null terminator byte that will be written to end the field string in the buffer.
569C
LD (HL),B 70
Store 00H to the buffer at (HL), null-terminating the field string. The field data in the buffer is now a proper null-terminated string.
569D
POP HL E1
Restore Register Pair HL from the stack (the buffer start address saved at 5628H). HL now points to the beginning of the assembled field string.
569E
POP AF F1
Restore Register Pair AF from the stack (the initial state saved at 5621H).
569F
DEC HL 2B
DECrement Register Pair HL by 1. This adjusts the buffer pointer back by one position for the upcoming ROM call, which expects HL to point one byte before the string data.
56A0
If the Z FLAG has been set (the initial A value was 00H, indicating this is a continuation entry from a prior field), JUMP to ROM 2868H, the numeric/string output routine, to process the field data directly.
56A3
RST 10H D7
Fetch the next non-space character from BASIC text. This re-syncs the parse pointer after the field read operation.
56A4
JUMP to ROM 0E6CH, the BASIC expression continuation routine. This continues processing the INPUT# statement (there may be additional variables to read into). This is a tail call.

56A7H - File Read Helper

Reads one byte from the currently open disk file via 60F2H. If the byte is successfully read and is not EOF (Z clear, meaning normal data), returns normally with the byte in Register A. If EOF is reached (Z set), returns with Z. On error, pops the return address from the stack and jumps directly to the field termination at 569AH, aborting the current field read.

56A7
GOSUB to 60F2H in BASIC/CMD, the file read routine. Reads one byte from the disk file. Returns Z if the read succeeded with a valid byte in A, NZ if EOF or error.
56AA
RET Z C8
If the Z FLAG has been set (byte successfully read), return to the caller with the byte in Register A. Normal successful read.
56AB
POP AF F1
Restore (discard) the return address from the stack. The caller’s return address is popped because we are aborting the current read operation and jumping directly to the field termination code.
56AC
JUMP to 569AH (field termination). This null-terminates whatever data has been collected so far in the buffer and returns the partial field to the caller. The EOF or error condition is handled by terminating the field early.

56AEH-56E7H - NOP Padding

Unused overlay space filled with NOP instructions. This padding occupies the remainder of the SYS20 overlay region from 56AEH through 56E7H (58 bytes).

56AE-56E7
NOP × 58 00 × 58
Reserved/unused area - 58 bytes of 00H (NOP instructions). This padding fills the remaining space in the SYS20 overlay region (5200H-56E7H). The overlay code ends at 56ADH; the remaining bytes are zero-filled and not executed.