TRS-80 DOS - LDOS 5.3.1 for the Model I - BASIC/OV3 Disassembled

Page Customization

Summary:

LDOS 5.3.1 BASIC/OV3 Disassembly - LBASIC Error Dictionary, Array Sort, Chained Load and Line Edit (Model I)

BASIC/OV3 is the one LBASIC overlay that has to be on the disk whenever DOS version 5 BASIC is running. The other three are loaded on demand by a single CMD statement and replace LBASIC's own image while they run; BASIC/OV3 is a subroutine library that LBASIC calls back into repeatedly, and the MISOSYS BASIC Reference Manual says plainly that it must always be present when programming in BASIC. It carries six services behind one entry point: the long error message dictionary that turns a Level II error number into English text, the translator that maps an LDOS error number onto a BASIC one, the CMD"O" string array sort, the machinery that keeps the variables alive across a chained load, and the two DOS version 5 single-key program line edit commands, M and C.

The overlay is a single LDOS load module of 1229 bytes: one type 1FH copyright record carrying "Copyright 1991 MISOSYS, Inc., All rights reserved", five type 01H load records and a type 02H transfer record. The image is contiguous at 4E00H-5281H, 1154 bytes with no gaps, and the transfer address is 4E00H. Alone among the four BASIC overlays it loads at 4E00H rather than 5200H, which is what lets it be a subroutine rather than a program: 4E00H-51FFH is the numbered SYS overlay area, free while a /CMD program is running, and BASIC/CMD has already emptied it by copying its two RAM hook images out to 4152H and 4000H during start-up. The top of the image, 5200H-5281H, does overlap BASIC/CMD's own banner, message and parameter table block, which is safe because none of that data is referenced once BASIC has started.

Entry is always at 4E00H with a FUNCTION SELECTOR in the alternate accumulator. The caller does LD A,n followed by EX AF,AF' before calling BASIC/CMD's loader at 54B6H, because the loader needs the primary accumulator for OVRLY$ and for the @LOAD error code. The first two instructions of the overlay write 01H into OVRLY$ at 430EH, and it is that byte the loader tests: when it already holds 01H the overlay is entered with no disk access at all. The overlay issues no RST 28H supervisor request of its own and calls no resident LDOS routine; every external call it makes is to a Level II ROM entry point, and the only LDOS cell it touches is OVRLY$.

The error dictionary is built the same way SYS4 builds the DOS error dictionary, and the resemblance is close enough to be deliberate. Messages are not stored as strings. A dictionary of seventy-seven English words holds each word once, with bit 7 set on its final character; an index table holds one entry per message, each entry a run of word numbers with bit 7 set on the last; and the expander walks both tables counting bit-7 bytes, so neither table needs offsets. Forty-nine messages are built from 428 bytes of dictionary and 127 bytes of index. The finished text is assembled into a buffer at 5414H, inside the block of BASIC/CMD start-up code that runs once and is never re-entered, which is the same space-reclaiming trick BASIC/CMD uses for its keyboard line buffer at 5309H and its DOS vector save area at 540AH.

File Format and Load Map

RecordContents
Type 1FH
49 bytes
Copyright comment record, "Copyright 1991 MISOSYS, Inc., All rights reserved". Not executable and not loaded.
Type 01H
5 records
Load records for 4E00H, 4F00H, 5000H and 5100H of 256 bytes each and 5200H of 130 bytes, giving a contiguous image at 4E00H-5281H, 1154 bytes with no gaps and no byte loaded twice.
Type 02H
2 bytes
Transfer address record, 4E00H. There are no type 07H patch records, so no LDOS /FIX patch has been applied to this copy.

Memory Map

Address RangePurpose
4E00H-4E1AH
27 bytes
Entry point, OVRLY$ marker and the six-way function selector dispatcher.
4E1BH-4E6AH
80 bytes
Selector 01H, expand a BASIC error number into its full message text.
4E6BH-4E7FH
21 bytes
Selector 02H, translate an LDOS error number into a BASIC error and enter the ROM error handler.
4E80H-4F18H
153 bytes
Selector 03H, the CMD"O" string array sort. Argument parsing at 4E80H, the gap loop at 4E95H, the comparison and exchange at 4EADH.
4F19H-4FD5H
189 bytes
Selector 04H, retain the variables across a chained load. Simple variables at 4F19H, arrays at 4F3FH, the string relocator at 4F6AH, the block park at 4F99H.
4FD6H-502CH
87 bytes
Selectors 05H and 06H, the M and C program line edit commands, sharing one body through the overlapping entry at 4FD6H and 4FD7H.
502DH-5056H
42 bytes
Data. LDOS error number to BASIC error value table, one byte per LDOS error number 0 to 41.
5057H-50D5H
127 bytes
Data. Message index table, 49 entries of word numbers with bit 7 set on the last of each.
50D6H-5281H
428 bytes
Data. Word dictionary, a marker byte followed by 77 words with bit 7 set on the final character of each.

Function Selectors

SelectorEntryCalled FromService
01H4E1BHBASIC/CMD 5A42HExpand A Basic Error Number
Register L holds the ROM error value, twice the documented BASIC error code. Returns Register Pair HL pointing at the assembled message text at 5414H, which the caller displays with ROM STROUT at 28A7H.
02H4E6BHBASIC/CMD 644FHTranslate An Ldos Error Number
Register L holds the LDOS error number, already masked to 0 to 63 by the CMD string shell. Saves it in 65F7H for CMD"E", looks the BASIC error value up in the table at 502DH and jumps to the ROM error entry at 19A2H. Does not return.
03H4E80HBASIC/CMD 5834HSort A String Array
CMD"O",numelem,firstelem. Register Pair HL points at the BASIC text after the closing quotation mark. A Shell sort with the gap starting at half the element count, exchanging three-byte string descriptors rather than string text.
04H4F19HBASIC/CMD 5EB0HRetain The Variables Across A Chained Load
Reached from RUN"filespec",V and LOAD"filespec",V. Voids DEF FN definitions, lifts string constants out of the program into string space, parks the whole variable block below the stack, lowers STKTOP to protect it, fills in the five operand cells 5F5EH, 5F65H, 5F71H, 5F7EH and 5F84H in BASIC/CMD and writes 0D6H to the chain state byte 65F4H.
05H4FD6HBASIC/CMD 6459HMove A Program Line
M n1,n2. Untokenizes line n1 into the line input buffer, re-enters it as line n2, then deletes line n1 by way of a temporary diversion of the Disk BASIC keyboard line input slot at 41AFH.
06H4FD7HBASIC/CMD 6459HCopy A Program Line
C n1,n2. The same body as selector 05H without the deletion. 4FD7H is the second byte of the instruction at 4FD6H, so one two-byte sequence provides both entries.

Self-Modifying Code and Work Cells

Address RangePurpose
4EA0H-4EA1H
2 bytes
Operand of the LD HL,0000H at 4E9FH. Written at 4E85H with the CMD"O" element count. Read once per sort pass to work out the number of comparison positions.
4EAEH-4EAFH
2 bytes
Operand of the LD HL,0000H at 4EADH. The CURRENT ELEMENT POSITION of the sort pass. Zeroed at 4EAAH, incremented at 4F0BH.
4EB4H-4EB5H
2 bytes
Operand of the LD HL,0000H at 4EB3H. The WORKING POSITION of the sift-down loop. Seeded from the current position at 4EB0H and stepped down one gap at a time at 4EFDH.
4EBBH-4EBCH
2 bytes
Operand of the LD DE,0000H at 4EBAH. The address of the first element's string descriptor, written at 4E8FH from the value ROM PTRGET returned for the firstelem argument.
4F0FH-4F10H
2 bytes
Operand of the LD DE,0000H at 4F0EH. The pass length, element count less gap, written at 4EA4H each time the gap is halved.
5023H-5024H
2 bytes
Operand of the LD DE,0000H at 5022H. The source line number of an M command, written at 4FE3H and read back only on the move path after the new line has been entered.
5414H
up to 30 bytes
Not in this overlay. The message assembly buffer, inside BASIC/CMD's once-only start-up code between the MEM range check and the sign-on banner. BASIC/CMD's filespec builder at 62A1H stages filenames in the same place; the two uses never overlap because a message is assembled and printed immediately.
65F7H
1 byte
Not in this overlay. BASIC/CMD's LAST DOS ERROR cell. Selector 02H at 4E6CH is its only writer; BASIC/CMD's CMD"E" handler at 584AH is its only reader.
65F4H
1 byte
Not in this overlay. BASIC/CMD's chain state byte. Selector 04H at 4FD2H is the only place the value 0D6H, meaning a chained load with variables is in progress, is ever written.
5F5EH, 5F65H, 5F71H, 5F7EH, 5F84H
2 bytes each
Not in this overlay. The five 16-bit operand cells in BASIC/CMD's chained-load fixup at 5F54H. All five ship as 0000H in the BASIC/CMD image and no instruction in BASIC/CMD writes any of them; selector 04H at 4F9CH, 4FBDH, 4FA4H, 4FC3H and 4FC9H is their only source.
41B0H-41B1H
2 bytes
Not in this overlay. The operand of the JP in the Disk BASIC vector table slot at 41AFH, the ROM keyboard line input exit. Selector 05H points it at 501AH at 5012H and puts BASIC/CMD's 5BD1H back at 501DH.

Major Routines

AddressName and Purpose
4E00HOverlay Entry And Selector Dispatcher
Entry: function selector in the alternate accumulator. Writes 01H to OVRLY$ 430EH, brings the selector into Register A with EX AF,AF' and dispatches on 01H to 06H by successive DEC A tests.
4E1BHExpand A Basic Error Number
Entry: Register L is the ROM error value. Exit: Register Pair HL points at the assembled text at 5414H, zero terminated, first letter capitalized. Registers A, B, DE and HL are used.
4E36HIndex Table Entry Skipper
Entry: Register B is the entry number, Register Pair HL is 5057H. Exit: Register Pair HL points at the first word number of the wanted message. Counts complete entries by their bit-7 terminators.
4E4AHDictionary Word Skipper
Entry: Register B is the word number, Register Pair HL is 50D6H. Exit: Register Pair HL points at the first character of the wanted word. The same counting trick as 4E36H.
4E6BHTranslate An Ldos Error Number
Entry: Register L is the LDOS error number. Records it in 65F7H, clamps it to 41, indexes the table at 502DH and jumps to ROM 19A2H with the BASIC error value in Register E. Does not return.
4E80HCmd O Argument Parser
Entry: Register Pair HL is at the BASIC text after the CMD string. Requires a comma, evaluates the element count through ROM INTID2 at 1E46H, requires a second comma and locates the first element through ROM PTRGET at 260DH, planting both results in self-modified operands.
4E95HSort Gap Loop
Halves the gap in Register Pair BC, works out the pass length and resets the element position. Returns to the caller of the CMD statement when the gap reaches zero.
4EADHSort Compare And Exchange
Compares the descriptors at the working position and one gap above it, character by character over the shorter length and then by length, and exchanges the three-byte descriptors if they are out of ascending order. An exchange sifts the element down another gap.
4F19HChained Load, Simple Variable Pass
Walks VARTAB to ARYTAB. Voids the definition pointer of every DEF FN entry and passes every string descriptor to 4F6AH.
4F3FHChained Load, Array Pass
Walks ARYTAB to STREND. Steps whole numeric arrays in one addition and passes every element of a string array to 4F6AH.
4F6AHMove One String Body Into String Space
Entry: Register Pair HL at a three-byte descriptor, Register Pair DE at the top of the region being scanned. Copies the text through ROM GETSPA at 28BFH and rewrites the descriptor only when the text lies between TXTTAB and that ceiling. Exit: Register Pair HL advanced by three, Register Pair DE unchanged.
4F99HPark The Variable Block
Moves VARTAB to STREND up to the stack pointer less 128 with LDDR, raises Out of memory through ROM 197AH if it will not fit, lowers STKTOP to protect it and fills in the five BASIC/CMD operand cells and the chain state byte.
4FD6HMove And Copy A Program Line
Parses two line numbers, requires the first to exist and the second not to, untokenizes the first through ROM BUFLIN at 2B7EH and re-enters it under the second number. The move path additionally deletes the original through ROM DELETE at 2BCAH.

Cross-Reference Notes

BASIC/OV3 is called only by BASIC/CMD, and always through BASIC/CMD's loader at 54B6H. The six call sites are 5A42H (long error messages, selector 01H), 644FH (DOS error translation, selector 02H), 5834H (CMD"O", selector 03H), 5EB0H (RUN and LOAD with the V option, selector 04H) and 6459H twice, through the one-letter abbreviation table at 656BH where M carries selector 05H and C carries 06H. Because selector 02H ends by jumping to the ROM error entry itself, the JP 19A2H at BASIC/CMD 6456H that follows that call is never reached.

The overlay makes no RST 28H supervisor request and calls no resident LDOS routine. OVRLY$ at 430EH is the only LDOS cell it touches, and it is written once, at 4E02H, as the resident marker BASIC/CMD's loader tests. Everything else it reads or writes below 4200H is a Level II BASIC RAM cell: STKTOP 40A0H, TXTTAB 40A4H, FRETOP 40D6H, VARTAB 40F9H, ARYTAB 40FBH, STREND 40FDH and the Disk BASIC vector table slot operand at 41B0H.

The Level II ROM entry points it uses are the error entries 1997H, 197AH, 19A2H, 1E4AH and 1ED9H, the line number parser LINSPC 1E4FH, the positive integer evaluator INTID2 1E46H, the line finder FNDLIN 1B2CH, the variable locator PTRGET 260DH, the string space allocator GETSPA 28BFH and the untokenizer BUFLIN 2B7EH, plus the RST 08H, RST 10H and RST 18H hooks at 4000H, 4003H and 4006H, which LBASIC has already pointed at the ROM or at its own replacements.

Two of the ROM entries are taken part way into a routine rather than at its front door, and both are worth knowing about in their own right. EDITRT at 2FEBH is the common exit of the EDIT command: it reloads Register Pair HL from BUFPNT at 40A7H, returns at once if the Z FLAG says there is no line number, and otherwise sets the CARRY FLAG to mark a line number as already collected and jumps to EDENT at 1A98H, which tokenizes the buffer and inserts it as the line numbered in Register Pair DE. That is the whole of how a line is entered without being typed. 2BCAH is three instructions into DELETE, past the range scan SCNLIN at 1B10H and the POP DE that follows it, and expects Register Pair BC pointing at the first line of the range with Register Pair DE holding the last line NUMBER of the range; because FNDLIN preserves Register Pair DE, one call to FNDLIN sets up both and produces a range of one line.

Three cells inside BASIC/CMD's image are written from here and nowhere else: the chain state byte 65F4H when it holds 0D6H, the last DOS error byte 65F7H, and the five operand cells of the chained-load fixup at 5F54H. The message assembly buffer at 5414H and the line input buffer reached through BUFPNT 40A7H are both shared with BASIC/CMD, and in both cases the sharing is safe because the BASIC/CMD code that occupies those addresses runs only once, during start-up.

Disassembly:

4E00H - Overlay Entry Point and Function Selector Dispatcher

BASIC/OV3 is a SUBROUTINE overlay, not a program. BASIC/CMD loads it at 4E00H through the loader at 54B6H and enters it with a RET, so the overlay is reached with BASIC/CMD's own return address on the stack and every selector path here ends by returning to it. The function selector is carried in the ALTERNATE accumulator because the loader needs the primary accumulator for OVRLY$ and for the @LOAD error code, so the first thing the dispatcher does after marking itself resident is EX AF,AF' to bring the selector into Register A. Six selectors are implemented.

4E00
LD A,01H 3E 01
Load Register A with 01H, the overlay number BASIC/CMD uses as its "BASIC/OV3 is already in memory" marker. This is the very first instruction executed after the overlay is loaded, so the marker is planted before any work is done.
4E02
LD (430EH),A 32 0E 43
Store the overlay number (Register A, 01H, just loaded) into OVRLY$ at 430EH, the resident LDOS cell that records which numbered SYS overlay currently occupies 4E00H. BASIC/CMD's loader at 54B6H reads this same byte and, when it holds 01H, jumps straight to 4E00H with no disk access at all, so writing it here is what makes every later call free.
4E05
EX AF,AF' 08
Exchange Register A and the FLAGS with the alternate set, bringing the FUNCTION SELECTOR into Register A. The caller loaded the selector with LD A,n followed by EX AF,AF' before calling BASIC/CMD's loader at 54B6H, precisely so that the primary Register A stayed free for OVRLY$ and for the @LOAD error return.
4E06
DEC A 3D
DECrement Register A (the function selector) by 1. If the selector was 01H, Register A becomes zero and the Z FLAG is set.
4E07
If the Z FLAG (Zero) has been set, the selector was 01H, so JUMP to 4E1BH to expand a BASIC error number into its full message text. Register L holds the error number on this path.
4E09
DEC A 3D
DECrement Register A (the function selector, already reduced by 1) by 1 more. If the selector was 02H, Register A becomes zero and the Z FLAG is set.
4E0A
If the Z FLAG (Zero) has been set, the selector was 02H, so JUMP to 4E6BH to translate an LDOS error number into a BASIC error. Register L holds the LDOS error number on this path.
4E0C
DEC A 3D
DECrement Register A (the function selector, already reduced by 2) by 1 more. If the selector was 03H, Register A becomes zero and the Z FLAG is set.
4E0D
If the Z FLAG (Zero) has been set, the selector was 03H, so JUMP to 4E80H to perform the CMD"O" string array sort. Register Pair HL points at the BASIC text just past the letter O on this path.
4E10
DEC A 3D
DECrement Register A (the function selector, already reduced by 3) by 1 more. If the selector was 04H, Register A becomes zero and the Z FLAG is set.
4E11
If the Z FLAG (Zero) has been set, the selector was 04H, so JUMP to 4F19H to retain the current variables across a chained load. This is the path taken by RUN"filespec",V and LOAD"filespec",V.
4E14
DEC A 3D
DECrement Register A (the function selector, already reduced by 4) by 1 more. If the selector was 05H, Register A becomes zero and the Z FLAG is set.
4E15
If the Z FLAG (Zero) has been set, the selector was 05H, so JUMP to 4FD6H, the MOVE entry of the two DOS version 5 single-key edit commands. M n1,n2 moves line n1 to become line n2.
4E18
Nothing matched 01H through 05H, so the selector is 06H and control JUMPs to 4FD7H, the COPY entry of the same pair of edit commands. C n1,n2 duplicates line n1 as line n2. 4FD7H is one byte into the instruction at 4FD6H, which is how one body serves both commands; see the debug note at 4FD6H.

Selector Summary
01H expand a BASIC error number (called from BASIC/CMD 5A42H, the long error message handler). 02H translate an LDOS error number (called from BASIC/CMD 644FH, the DOS error translator). 03H sort a string array (called from BASIC/CMD 5834H, the CMD"O" handler). 04H retain variables across a chained load (called from BASIC/CMD 5EB0H, the RUN"filespec",V and LOAD"filespec",V path). 05H move a program line and 06H copy a program line (both called from BASIC/CMD 6459H, the READY prompt line collector, through the abbreviation table at 656BH where the letters M and C carry the selector values 05H and 06H).

4E1BH - Selector 01H: Expand a BASIC Error Number into Message Text

Level II BASIC in ROM prints two-letter error abbreviations. LBASIC replaces that with full English text by hooking the ROM error path, and this routine is the dictionary lookup behind it. Register L arrives holding the ROM error value, which is twice the documented BASIC error code. The routine converts that value to an index, walks the message index table at 5057H to the matching entry, then walks the word dictionary at 50D6H once per word number in the entry, copying the words into a buffer with single spaces between them. It returns Register Pair HL pointing at the assembled text so the caller can hand it to the ROM string printer STROUT at 28A7H.

4E1B
LD A,L 7D
Load Register A with the error value from Register L. BASIC/CMD's long error message handler at 5A42H does LD L,E immediately before calling here, so this value is the ROM error number that every Level II error path leaves in Register E: 00H for Next without for, 02H for Syntax error, and so on in steps of two, with the LBASIC disk errors occupying 64H to 90H.
4E1C
LD HL,5057H 21 57 50
Point Register Pair HL at 5057H, the base of the message index table. This load is superseded by the identical load at 4E32H after the index arithmetic has finished; it is executed on every entry and its result is discarded.
4E1F
CP 91H FE 91
Compare Register A (the error value) against 91H (145). If Register A is below 91H the CARRY FLAG is set; if it is 91H or above the NO CARRY FLAG is set. 90H is the highest error value the dictionary covers, Protection has cleared memory.
4E21
If the NO CARRY FLAG has been set, the error value in Register A is 91H or above and therefore off the top of the dictionary, so JUMP to 4E2BH to substitute the fixed value 5AH, which resolves to the Unprintable error entry.
4E23
CP 63H FE 63
Compare Register A (the error value, known to be below 91H) against 63H (99). If Register A is below 63H the CARRY FLAG is set; if it is 63H or above the NO CARRY FLAG is set. 64H is the first LBASIC disk error value, Field overflow.
4E25
If the NO CARRY FLAG has been set, the error value in Register A is one of the LBASIC disk errors in the range 64H to 90H, so JUMP to 4E2DH to subtract the 34H bias that packs the disk errors onto the end of the index table.
4E27
CP 2FH FE 2F
Compare Register A (the error value, known to be below 63H) against 2FH (47). If Register A is below 2FH the CARRY FLAG is set; if it is 2FH or above the NO CARRY FLAG is set. 2EH is the highest of the ROM-range error values, the undefined user function error that BASIC/CMD raises from its own error code table at 642FH.
4E29
If the CARRY FLAG has been set, the error value in Register A is a ROM-range error below 2FH, so JUMP to 4E2FH and use it directly with no bias applied.
4E2B
LD A,5AH 3E 5A
Load Register A with 5AH, the substitute error value used for anything the dictionary does not cover. Reached either by falling through from 4E29H (the error value was between 2FH and 62H, a gap with no assigned messages) or by the jump at 4E21H (the error value was 91H or above). 5AH less the 34H bias subtracted next gives 26H, which resolves to the Unprintable error entry.
4E2D
SUB 34H D6 34
SUBtract 34H (52) from Register A. For an LBASIC disk error this compresses the range 64H to 90H down onto 30H to 5CH so that the disk messages can follow the ROM messages directly in the index table instead of leaving a fifty-entry hole.
4E2F
RRCA 0F
Rotate Register A right, which halves the biased error value because every error value is even. Register A now holds the message number counting from zero.
4E30
INC A 3C
INCrement Register A (the message number) by 1.
4E31
INC A 3C
INCrement Register A by 1 again. The two increments step over the two leading entries of the index table at 5057H: the one-byte terminator at 5057H that exists only so the skip loop below counts correctly, and the No error entry at 5058H, which is never selected because the arithmetic can never produce an index below two. Register A is now the entry number to fetch.
4E32
LD HL,5057H 21 57 50
Point Register Pair HL at 5057H, the base of the message index table, where every entry is a run of word numbers ended by a byte with bit 7 set.
4E35
LD B,A 47
Load Register B with the entry number from Register A, to be used as the count of entries to skip.
4E36
LD A,(HL) 7E
Loop Start
Fetch one byte of the message index table into Register A from the address in Register Pair HL, which walks forward from 5057H.
4E37
RLCA 07
Rotate Register A left, moving bit 7 of the index byte into the CARRY FLAG. Bit 7 marks the LAST word number of an entry, so the CARRY FLAG now says whether this byte ends the entry.
4E38
INC HL 23
INCrement Register Pair HL by 1 to step to the next byte of the index table.
4E39
If the NO CARRY FLAG has been set, bit 7 of the index byte was clear and the entry continues, so LOOP BACK to 4E36H to consume the rest of it.
4E3B
DEC B 05
DECrement Register B, the count of entries still to be skipped, by 1. One complete entry has just been consumed.
4E3C
If the NZ FLAG (Not Zero) has been set, Register B has not reached zero and more entries remain to be skipped, so LOOP BACK to 4E36H. Loop End
On exit Register Pair HL points at the first word number of the wanted message.
4E3E
LD DE,5414H 11 14 54
Point Register Pair DE at 5414H, the buffer the message text is assembled into. 5414H is inside BASIC/CMD's own image, in the block of once-only start-up code that runs between the MEM range check and the sign-on banner and is never re-entered after BASIC is running. BASIC/CMD's filespec builder at 62A1H stages filenames in the same buffer, and the two uses never overlap because a message is built and printed immediately.
4E41
PUSH DE D5
Save the buffer start address (Register Pair DE, 5414H) onto the stack. It is recovered at 4E67H so that the first character of the finished message can be capitalized, and it is the value returned to the caller in Register Pair HL.
4E42
LD A,(HL) 7E
Loop Start
Fetch the next WORD NUMBER of this message into Register A from the address in Register Pair HL, which points into the message index table entry selected above.
4E43
AND 7FH E6 7F
Mask off bit 7 of Register A. Bit 7 is the end-of-entry marker and is not part of the word number, so Register A is left holding a word number in the range 1 to 4DH.
4E45
LD B,A 47
Load Register B with the word number from Register A, to be used as the count of dictionary words to skip.
4E46
PUSH HL E5
Save the message index table pointer (Register Pair HL) onto the stack, because Register Pair HL is about to be re-used to walk the word dictionary. It is recovered at 4E5FH.
4E47
LD HL,50D6H 21 D6 50
Point Register Pair HL at 50D6H, the base of the word dictionary. The single byte at 50D6H has bit 7 set and holds no text; it exists purely so that the skip loop below counts complete words and word number 1 lands on the first real word, "next", at 50D7H.
4E4A
LD A,(HL) 7E
Loop Start
Fetch one dictionary character into Register A from the address in Register Pair HL.
4E4B
RLCA 07
Rotate Register A left, moving bit 7 of the character into the CARRY FLAG. Every word in the dictionary is stored in plain lowercase or uppercase ASCII with bit 7 set on its LAST character only, so the CARRY FLAG now says whether this character ends the word.
4E4C
INC HL 23
INCrement Register Pair HL by 1 to step to the next dictionary character.
4E4D
If the NO CARRY FLAG has been set, bit 7 of the character was clear and the word continues, so LOOP BACK to 4E4AH to skip the rest of it.
4E4F
DEC B 05
DECrement Register B, the count of dictionary words still to be skipped, by 1. One complete word has just been passed over.
4E50
If the NZ FLAG (Not Zero) has been set, Register B has not reached zero and more words remain to be skipped, so LOOP BACK to 4E4AH. Loop End
On exit Register Pair HL points at the first character of the wanted dictionary word.
4E52
LD A,(HL) 7E
Loop Start
Fetch one character of the wanted word into Register A from the address in Register Pair HL.
4E53
RLCA 07
Rotate Register A left, moving bit 7 of the character into the CARRY FLAG so that the end-of-word marker is captured before the character is cleaned up.
4E54
SRL A CB 3F
Shift Register A right one place with a zero shifted into bit 7. The bit shifted out into the CARRY FLAG is the same bit 7 that RLCA just captured, so the CARRY FLAG survives, and Register A is restored to the original character with bit 7 stripped. Two rotates in opposite directions therefore deliver both a clean ASCII character and its end-of-word flag.
4E56
LD (DE),A 12
Store the cleaned ASCII character (Register A) into the message buffer at the address in Register Pair DE, which walks forward from 5414H.
4E57
INC HL 23
INCrement Register Pair HL by 1 to step to the next character of the dictionary word.
4E58
INC DE 13
INCrement Register Pair DE by 1 to step to the next free byte of the message buffer at 5414H.
4E59
If the NO CARRY FLAG has been set, the character just copied did not carry the end-of-word marker, so LOOP BACK to 4E52H to copy the next one. Loop End
4E5B
LD A,20H 3E 20
Load Register A with 20H, the ASCII code for a SPACE. Every word is followed by one space, including the last, which is why the finished message always carries a single trailing blank.
4E5D
LD (DE),A 12
Store the space (Register A, 20H) into the message buffer at the address in Register Pair DE.
4E5E
INC DE 13
INCrement Register Pair DE by 1 to step past the space just written.
4E5F
POP HL E1
Restore Register Pair HL from the stack, recovering the message index table pointer saved at 4E46H. It points at the word number that was just expanded.
4E60
LD A,(HL) 7E
Fetch that same word number back into Register A so that its bit 7, the end-of-entry marker, can be tested.
4E61
INC HL 23
INCrement Register Pair HL by 1 so that it points at the NEXT word number of this message before the test is made.
4E62
RLCA 07
Rotate Register A left, moving bit 7 of the word number into the CARRY FLAG. Bit 7 set means the word just expanded was the last one in the message.
4E63
If the NO CARRY FLAG has been set, the message has more words, so LOOP BACK to 4E42H to expand the next one. Loop End
4E65
XOR A AF
Set Register A to ZERO and clear all flags, ready to terminate the assembled message.
4E66
LD (DE),A 12
Store the terminating 00H (Register A) into the message buffer at the address in Register Pair DE. The ROM string printer STROUT at 28A7H stops on this byte.
4E67
POP HL E1
Restore Register Pair HL from the stack, recovering the buffer start address 5414H saved at 4E41H. This is also the value the routine returns to its caller.
4E68
RES 5,(HL) CB AE
Clear bit 5 of the FIRST character of the assembled message, at 5414H. In ASCII bit 5 is the case bit, so clearing it converts a lowercase initial letter to uppercase and leaves an already uppercase letter alone. This is why the dictionary can store every word in lowercase and still produce "Next without for" and "Disk I/O error" with a capital first letter.
4E6A
RET C9
RETurn to the caller with Register Pair HL pointing at the finished message text at 5414H. BASIC/CMD's handler at 5A42H follows the call immediately with CALL 28A7H, the ROM string output routine, to display it.

4E6BH - Selector 02H: Translate an LDOS Error Number into a BASIC Error

When a disk operation fails, LDOS returns an error number of its own that has no meaning to Level II BASIC. This routine records that number for later inspection by CMD"E" and then converts it into a Level II error value through the 42-entry lookup table at 502DH, entering the ROM error handler with the result. It never returns.

4E6B
LD A,L 7D
Load Register A with the LDOS error number from Register L. BASIC/CMD's DOS error translator at 644FH does LD L,A immediately before setting the selector, and it in turn is reached from the CMD string shell at 55E8H, where the raw code returned by the DOS command has already been masked with AND 3FH to a value of 0 to 63.
4E6C
LD (65F7H),A 32 F7 65
Store the LDOS error number (Register A) into 65F7H, BASIC/CMD's LAST DOS ERROR cell. Nothing in BASIC/CMD ever writes this byte; this instruction is its only source. BASIC/CMD's CMD"E" handler at 584AH reads it back with LD A,(65F7H), sets bits 6 and 7 with OR 0C0H to ask for the full message text, and hands it to the resident @ERROR vector at 4409H, which is how CMD"E" reports the DOS reason behind a bare "Disk I/O error".
4E6F
LD HL,502DH 21 2D 50
Point Register Pair HL at 502DH, the base of the LDOS-error-number to BASIC-error-value table. The table holds one byte per LDOS error number, from 0 at 502DH to 41 at 5056H.
4E72
CP 29H FE 29
Compare Register A (the LDOS error number) against 29H (41), the highest number the table covers. If Register A is below 29H the CARRY FLAG is set; if it is 29H or above the NO CARRY FLAG is set.
4E74
If the CARRY FLAG has been set, the LDOS error number in Register A is 40 or less and indexes the table directly, so JUMP to 4E78H and use it as it stands.
4E76
LD A,29H 3E 29
Load Register A with 29H (41), clamping every LDOS error number of 41 or above to the last table entry. The mask applied at BASIC/CMD 55E6H can deliver values up to 63, and all of them land on 5056H, which holds 66H, the Internal error message.
4E78
LD C,A 4F
Load Register C with the clamped LDOS error number from Register A, the low half of the 16-bit table offset.
4E79
LD B,00H 06 00
Load Register B with 00H so that Register Pair BC holds the table offset as a 16-bit value with no high byte.
4E7B
ADD HL,BC 09
Add the offset (Register Pair BC, the LDOS error number) to Register Pair HL (502DH, the table base), so that Register Pair HL points at the table entry for this error.
4E7C
LD E,(HL) 5E
Fetch the BASIC error value from that table entry into Register E. The ROM error handler takes its error number in Register E, and the table stores the values already doubled in the ROM's own convention, so 72H here means the documented BASIC error code 57, Disk I/O error.
4E7D
JUMP to the Level II ROM error entry point at 19A2H with the BASIC error value in Register E. The ROM prints the error and reports the line number; it does not come back, so the JP at BASIC/CMD 6456H that follows the call into this overlay is never reached.

4E80H - Selector 03H: CMD"O" String Array Sort, Argument Parsing

CMD"O",numelem,firstelem sorts a singly dimensioned string array in place. The manual states that the sort starts at the element given and covers the number of elements given, and that the count must not run past the end of the array; nothing in this code checks that, so the caller carries the responsibility. BASIC/CMD reaches here with a JP rather than a CALL, from the CMD"O" stub at 5834H, so this routine's RET returns to whatever called the CMD statement handler. On entry Register Pair HL points at the BASIC text immediately after the closing quotation mark of the CMD string.

4E80
RST 08H => 2CH CF 2C
Call the ROM syntax checker through the RST 08H hook at 4000H, requiring the next character of the BASIC text at Register Pair HL to be the 2CH byte that follows, ASCII ,. Anything else raises a Syntax error. This is the comma between the closing quotation mark of CMD"O" and the element count.
4E82
GOSUB to ROM routine at 1E46H, the INTID2 entry of the positive-integer evaluator, which evaluates the expression at Register Pair HL and returns it as a 16-bit integer in Register Pair DE, raising an Illegal function call error for a negative result. This is numelem, the NUMBER OF ELEMENTS to sort. On return Register Pair HL points at the character that ended the expression.
4E85
LD (4EA0H),DE ED 53 A0 4E
Self-Modifying Code
Store the element count (Register Pair DE, just evaluated) into 4EA0H, which is the 16-bit operand of the LD HL,0000H instruction at 4E9FH. The listing shows the assembled 0000H; at run time that instruction loads Register Pair HL with the element count so the outer gap loop can work out how many comparison positions each pass has.
4E89
PUSH DE D5
Save the element count (Register Pair DE) onto the stack. It is recovered into Register Pair BC at 4E93H, where it becomes the starting value that is repeatedly halved to give the sort gap.
4E8A
RST 08H => 2CH CF 2C
Call the ROM syntax checker through the RST 08H hook at 4000H, requiring the next character of the BASIC text at Register Pair HL to be the 2CH byte that follows, ASCII ,. This is the comma between the element count and the first element.
4E8C
GOSUB to ROM routine at 260DH, PTRGET, which locates or creates the variable named at Register Pair HL and returns a pointer to its value in Register Pair DE. The argument is a subscripted string array element such as A$(10), so PTRGET returns the address of THAT ELEMENT'S three-byte string descriptor, which becomes the base of the region to be sorted. Entering PTRGET at 260DH rather than 260CH leaves DIMFLG at 40AEH clear, so an existing array is located rather than dimensioned.
4E8F
LD (4EBBH),DE ED 53 BB 4E
Self-Modifying Code
Store the address of the first element's string descriptor (Register Pair DE, returned by PTRGET) into 4EBBH, which is the 16-bit operand of the LD DE,0000H instruction at 4EBAH. The listing shows the assembled 0000H; at run time that instruction supplies the base address that every element offset is measured from.
4E93
POP BC C1
Restore Register Pair BC from the stack, recovering the element count saved at 4E89H. From here on Register Pair BC is the working gap of the sort and is halved on every pass.
4E94
PUSH HL E5
Save the BASIC text pointer (Register Pair HL, left by PTRGET pointing past the array reference) onto the stack. It is restored at 4E9DH when the sort finishes, so that the CMD statement can carry on parsing the line.

4E95H - CMD"O" Sort: Halve the Gap and Test for Completion

The sort is a Shell sort. The gap starts at half the element count and is halved on every pass; when it reaches zero the array is ordered and the routine returns. This block is the top of the outer loop, re-entered from 4F16H after every complete pass.

4E95
SRL B CB 38
Loop Start
Shift Register B, the high byte of the current gap in Register Pair BC, right one place with a zero shifted into bit 7. The bit shifted out goes to the CARRY FLAG so that it can be brought into the low byte next. On the first pass Register Pair BC holds the element count.
4E97
RR C CB 19
Rotate Register C, the low byte of the gap, right through the CARRY FLAG, bringing in the bit shifted out of Register B. Register Pair BC now holds the previous gap divided by two.
4E99
LD A,B 78
Load Register A with the high byte of the new gap (Register B) so that the whole pair can be tested for zero.
4E9A
OR C B1
OR Register A (the gap high byte) with Register C (the gap low byte). The Z FLAG is set only when the whole of Register Pair BC is zero, and the operation also clears the CARRY FLAG, which the SBC HL,BC at 4EA2H depends on.
4E9B
If the NZ FLAG (Not Zero) has been set, the gap in Register Pair BC is still at least one, so JUMP to 4E9FH to run another pass.
4E9D
POP HL E1
The gap has reached zero and the array is sorted. Restore Register Pair HL from the stack, recovering the BASIC text pointer saved at 4E94H so that the interpreter resumes where the CMD"O" statement ended.
4E9E
RET C9
RETurn. Because BASIC/CMD jumped to this overlay from 5834H rather than calling it, this RET returns to the caller of the CMD statement handler and the interpreter carries on with the next statement. Loop End
4E9F
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the ELEMENT COUNT. The operand at 4EA0H is shown as 0000H in the listing and is written at 4E85H with the value of numelem parsed from the CMD"O" statement.
4EA2
SBC HL,BC ED 42
Subtract the current gap (Register Pair BC) and the CARRY FLAG (cleared by the OR C at 4E9AH) from the element count in Register Pair HL. The result is the number of element positions this pass must visit, since positions beyond it have no partner a gap away.
4EA4
LD (4F0FH),HL 22 0F 4F
Self-Modifying Code
Store that pass length (Register Pair HL) into 4F0FH, the 16-bit operand of the LD DE,0000H instruction at 4F0EH. The listing shows the assembled 0000H; at run time that instruction reloads the pass length so the inner loop at 4F07H can tell when the pass is finished.
4EA7
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H, the starting element position for this pass.
4EAA
LD (4EAEH),HL 22 AE 4E
Self-Modifying Code
Store zero (Register Pair HL) into 4EAEH, the 16-bit operand of the LD HL,0000H instruction at 4EADH. That instruction is the head of the pass loop and 4EAEH is therefore the CURRENT ELEMENT POSITION counter, held inside the instruction that reads it rather than in a separate cell.

4EADH - CMD"O" Sort: Compare and Exchange

One position of one pass. The element at the current position and the element a gap above it are compared as strings, and if they are out of order their three-byte descriptors are exchanged and the comparison is repeated a gap lower down, which is what turns a plain gapped exchange into a proper Shell sort. Level II holds a string variable as a three-byte descriptor of length followed by text address, so exchanging descriptors reorders the array without moving any string text.

4EAD
LD HL,0000H 21 00 00
Loop Start
Self-Modifying Code
Load Register Pair HL with the CURRENT ELEMENT POSITION. The operand at 4EAEH is shown as 0000H in the listing, is zeroed at 4EAAH when the pass begins and is incremented at 4F07H after every position.
4EB0
LD (4EB4H),HL 22 B4 4E
Self-Modifying Code
Copy the current element position (Register Pair HL) into 4EB4H, the 16-bit operand of the LD HL,0000H instruction at 4EB3H. 4EB4H is the WORKING POSITION, which starts at the current position and is stepped down by one gap at a time at 4EFBH each time an exchange is made.
4EB3
LD HL,0000H 21 00 00
Loop Start
Self-Modifying Code
Load Register Pair HL with the WORKING POSITION from the operand at 4EB4H, shown as 0000H in the listing and maintained at 4EB0H and 4EFDH. This is the head of the sift-down loop.
4EB6
LD D,H 54
Copy the high byte of the working position (Register H) into Register D, the first half of taking a copy of Register Pair HL in Register Pair DE.
4EB7
LD E,L 5D
Copy the low byte of the working position (Register L) into Register E, so that Register Pair DE now also holds the working position.
4EB8
ADD HL,DE 19
Add the working position (Register Pair DE) to Register Pair HL, giving twice the working position.
4EB9
ADD HL,DE 19
Add the working position (Register Pair DE) to Register Pair HL again, giving three times the working position. Three is the size of a Level II string descriptor, so Register Pair HL now holds the BYTE OFFSET of the working element from the start of the sorted region.
4EBA
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the ADDRESS OF THE FIRST ELEMENT'S DESCRIPTOR. The operand at 4EBBH is shown as 0000H in the listing and is written at 4E8FH with the value PTRGET returned for the firstelem argument.
4EBD
ADD HL,DE 19
Add the base address (Register Pair DE) to the byte offset in Register Pair HL, so Register Pair HL now points at the three-byte descriptor of the WORKING element.
4EBE
LD D,H 54
Copy the high byte of that descriptor address (Register H) into Register D.
4EBF
LD E,L 5D
Copy the low byte of that descriptor address (Register L) into Register E, so Register Pair DE now points at the working element's descriptor and Register Pair HL is free to be advanced to its partner.
4EC0
ADD HL,BC 09
Add the gap (Register Pair BC) to Register Pair HL, the first of three additions that convert an element gap into a byte gap.
4EC1
ADD HL,BC 09
Add the gap (Register Pair BC) to Register Pair HL again.
4EC2
ADD HL,BC 09
Add the gap (Register Pair BC) to Register Pair HL a third time. Three times the gap is the byte distance between the two descriptors, so Register Pair HL now points at the descriptor of the PARTNER element a gap above the working one.
4EC3
PUSH BC C5
Save the gap (Register Pair BC) onto the stack, because Register Pair BC is about to be re-used to hold the comparison length and the saved flags. It is recovered at 4EFAH or 4F06H.
4EC4
PUSH HL E5
Save the partner element's descriptor address (Register Pair HL) onto the stack, for the exchange at 4EEAH or the discard at 4F05H.
4EC5
PUSH DE D5
Save the working element's descriptor address (Register Pair DE) onto the stack, for the exchange at 4EE9H or the discard at 4F04H.
4EC6
LD A,(DE) 1A
Fetch the LENGTH byte of the working element's string from the first byte of its descriptor, at the address in Register Pair DE, into Register A.
4EC7
CP (HL) BE
Compare Register A (the working string's length) against the length byte of the partner string, the first byte of the descriptor at the address in Register Pair HL. If the working string is shorter the CARRY FLAG is set; if it is the same length or longer the NO CARRY FLAG is set. The flags produced here are kept and used again at 4EE5H to break a tie between two strings with a common prefix.
4EC8
If the CARRY FLAG has been set, the working string is the shorter of the two, so Register A already holds the smaller length and the character comparison should stop there. JUMP to 4ECBH.
4ECA
LD A,(HL) 7E
The partner string is the shorter, so load Register A with ITS length byte from the descriptor at the address in Register Pair HL. Register A now holds the smaller of the two lengths either way.
4ECB
PUSH AF F5
Save Register A (the smaller of the two string lengths) and the FLAGS from the comparison at 4EC7H onto the stack.
4ECC
POP BC C1
Restore that stacked pair into Register Pair BC. Register B now holds the comparison length and REGISTER C HOLDS THE FLAGS BYTE, so the CARRY FLAG from the length comparison at 4EC7H is preserved as bit 0 of Register C and survives everything the character loop does. It is tested at 4EE5H.
4ECD
AND A A7
Test Register A, the comparison length, against itself to set the flags without altering it. The Z FLAG is set when the length is zero.
4ECE
If the Z FLAG (Zero) has been set, at least one of the two strings is empty and there are no characters to compare, so JUMP to 4EE5H to settle the order on length alone.
4ED0
INC DE 13
INCrement Register Pair DE by 1 so that it points at the two-byte TEXT ADDRESS field of the working element's descriptor, which follows the length byte.
4ED1
INC HL 23
INCrement Register Pair HL by 1 so that it points at the two-byte TEXT ADDRESS field of the partner element's descriptor.
4ED2
LD A,(HL) 7E
Fetch the low byte of the partner string's text address into Register A from the address in Register Pair HL.
4ED3
INC HL 23
INCrement Register Pair HL by 1 to reach the high byte of the partner string's text address.
4ED4
LD H,(HL) 66
Fetch the high byte of the partner string's text address into Register H.
4ED5
LD L,A 6F
Move the low byte held in Register A into Register L, so Register Pair HL now points at the partner string's TEXT rather than at its descriptor.
4ED6
EX DE,HL EB
Exchange Register Pair DE and Register Pair HL, so Register Pair DE now holds the partner string's text address and Register Pair HL points back at the working element's text address field.
4ED7
LD A,(HL) 7E
Fetch the low byte of the working string's text address into Register A from the address in Register Pair HL.
4ED8
INC HL 23
INCrement Register Pair HL by 1 to reach the high byte of the working string's text address.
4ED9
LD H,(HL) 66
Fetch the high byte of the working string's text address into Register H.
4EDA
LD L,A 6F
Move the low byte held in Register A into Register L. Register Pair HL now points at the WORKING string's text and Register Pair DE at the PARTNER string's text, with Register B holding the number of characters to compare.
4EDB
LD A,(DE) 1A
Loop Start
Fetch one character of the PARTNER string into Register A from the address in Register Pair DE.
4EDC
SUB (HL) 96
SUBtract the corresponding character of the WORKING string, at the address in Register Pair HL, from Register A. The CARRY FLAG is set when the partner character is the lower of the two, and the Z FLAG is set when they are equal.
4EDD
If the CARRY FLAG has been set, the partner string sorts BELOW the working string, so the two elements are out of ascending order. JUMP to 4EE9H to exchange them.
4EDF
If the NZ FLAG (Not Zero) has been set, the characters differ and the partner character is the higher, so the pair is already in ascending order. JUMP to 4F04H to discard the saved pointers and move on to the next element position.
4EE1
INC DE 13
The two characters were equal. INCrement Register Pair DE by 1 to step to the next character of the partner string.
4EE2
INC HL 23
INCrement Register Pair HL by 1 to step to the next character of the working string.
4EE3
DECrement Register B, the count of characters still to compare, and LOOP BACK to 4EDBH if it has not reached zero. Loop End
Falling out means the shorter string is a prefix of the longer one.
4EE5
BIT 0,C CB 41
Test bit 0 of Register C, which is the CARRY FLAG saved from the length comparison at 4EC7H by the PUSH AF and POP BC pair at 4ECBH. The bit is set when the WORKING string is the shorter of the two, which for equal prefixes means the pair is already in ascending order.
4EE7
If the NZ FLAG (Not Zero) has been set, the saved CARRY FLAG was set, the working string is the shorter and the pair is in order, so JUMP to 4F04H to discard the saved pointers and move on. Note that two strings of equal length with equal contents also take the exchange path, so the sort is not stable.
4EE9
POP DE D1
Restore Register Pair DE from the stack, recovering the WORKING element's descriptor address saved at 4EC5H.
4EEA
POP HL E1
Restore Register Pair HL from the stack, recovering the PARTNER element's descriptor address saved at 4EC4H.
4EEB
LD B,03H 06 03
Load Register B with 03H, the size of a Level II string descriptor in bytes: one length byte followed by a two-byte text address.
4EED
LD C,(HL) 4E
Loop Start
Fetch one byte of the partner element's descriptor, at the address in Register Pair HL, into Register C as temporary storage for the exchange.
4EEE
LD A,(DE) 1A
Fetch the corresponding byte of the working element's descriptor, at the address in Register Pair DE, into Register A.
4EEF
LD (HL),A 77
Store the working element's byte (Register A) into the partner element's descriptor at the address in Register Pair HL.
4EF0
LD A,C 79
Move the partner element's saved byte from Register C into Register A.
4EF1
LD (DE),A 12
Store the partner element's byte (Register A) into the working element's descriptor at the address in Register Pair DE. One byte of the exchange is complete; no string text has moved, only the descriptors.
4EF2
INC HL 23
INCrement Register Pair HL by 1 to step to the next byte of the partner element's descriptor.
4EF3
INC DE 13
INCrement Register Pair DE by 1 to step to the next byte of the working element's descriptor.
4EF4
DECrement Register B, the count of descriptor bytes still to exchange, and LOOP BACK to 4EEDH if it has not reached zero. Loop End
4EF6
LD HL,(4EB4H) 2A B4 4E
Load Register Pair HL with the WORKING POSITION from 4EB4H, the self-modified operand of the LD HL,0000H instruction at 4EB3H.
4EF9
XOR A AF
Set Register A to ZERO and clear all flags. The CARRY FLAG must be clear before the SBC that follows.
4EFA
POP BC C1
Restore Register Pair BC from the stack, recovering the sort gap saved at 4EC3H.
4EFB
SBC HL,BC ED 42
Subtract the gap (Register Pair BC) from the working position in Register Pair HL. An exchange has just been made, so the element that moved down must now be compared against the one a further gap below it. The CARRY FLAG is set if the result went negative, meaning there is no such element.
4EFD
LD (4EB4H),HL 22 B4 4E
Self-Modifying Code
Store the reduced working position (Register Pair HL) back into 4EB4H, the operand of the LD HL,0000H instruction at 4EB3H, so that the sift-down loop reads it on its next turn.
4F00
If the NO CARRY FLAG has been set, the working position is still zero or above, so LOOP BACK to 4EB3H to compare the exchanged element against the next one down. Loop End
4F02
The working position went below zero, so the sift-down is finished. JUMP to 4F07H to advance to the next element position. Register Pair BC already holds the gap, restored at 4EFAH, so the three POPs at 4F04H are skipped.
4F04
POP DE D1
No exchange was needed. Discard the saved working element descriptor address from the stack into Register Pair DE, undoing the PUSH at 4EC5H.
4F05
POP HL E1
Discard the saved partner element descriptor address from the stack into Register Pair HL, undoing the PUSH at 4EC4H.
4F06
POP BC C1
Restore Register Pair BC from the stack, recovering the sort gap saved at 4EC3H.
4F07
LD HL,(4EAEH) 2A AE 4E
Load Register Pair HL with the CURRENT ELEMENT POSITION from 4EAEH, the self-modified operand of the LD HL,0000H instruction at 4EADH.
4F0A
INC HL 23
INCrement Register Pair HL by 1 to advance to the next element position in this pass.
4F0B
LD (4EAEH),HL 22 AE 4E
Self-Modifying Code
Store the advanced element position (Register Pair HL) back into 4EAEH, the operand of the LD HL,0000H instruction at 4EADH, which is the head of the pass loop.
4F0E
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the PASS LENGTH, the element count less the gap. The operand at 4F0FH is shown as 0000H in the listing and is written at 4EA4H each time the gap is halved.
4F11
XOR A AF
Set Register A to ZERO and clear all flags, so that the CARRY FLAG is clear before the SBC that follows.
4F12
SBC HL,DE ED 52
Subtract the pass length (Register Pair DE) from the new element position in Register Pair HL. The CARRY FLAG is set while the position is still below the pass length.
4F14
If the CARRY FLAG has been set, this pass has positions left, so LOOP BACK to 4EADH to handle the next one. Loop End
4F16
The pass is complete, so JUMP to 4E95H to halve the gap and either start another pass or finish the sort.

4F19H - Selector 04H: Retain the Variables Across a Chained Load, Simple Variables

RUN"filespec",V and LOAD"filespec",V load a new program but keep the current variables. BASIC/CMD calls this selector from 5EB0H after the filespec has been parsed and BEFORE the ROM NEW at 5EC5H wipes the variable area, and everything the later fixup code at 5F54H needs is set up here, because nothing in BASIC/CMD writes any of it. The work is in three stages. First the simple variables are walked, function definitions are voided and any string whose text still lives inside the program is copied into string space so that it survives the load. Then the arrays are walked for the same reason. Finally the whole variable block is moved up under the stack and the five operand cells in BASIC/CMD are filled in. This block is the first stage.

4F19
LD HL,(40F9H) 2A F9 40
Load Register Pair HL with VARTAB from 40F9H, the Level II pointer to the start of the simple variable table, which is the first byte above the program text. This is the walk pointer for the simple variable scan.
4F1C
LD DE,(40FBH) ED 5B FB 40
Load Register Pair DE with ARYTAB from 40FBH, the Level II pointer to the start of the array table, which is the first byte above the simple variables. Register Pair DE serves two purposes here: it is the end-of-scan limit for the loop below, and it is the ceiling the string relocator at 4F6AH tests each string text address against.
4F20
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H so that the first pass through the loop advances the walk pointer by nothing. On every later turn Register C holds the size in bytes of the value just examined, taken from the variable's own type byte, and Register B stays at zero throughout, which is also what makes it usable as the zero written at 4F2FH and 4F31H.
4F23
ADD HL,BC 09
Loop Start
Add the size of the previous variable's value (Register Pair BC) to the walk pointer in Register Pair HL, so that Register Pair HL points at the next simple variable entry. A Level II simple variable is a type byte, two name characters and then the value, and the three header bytes are stepped over one at a time below.
4F24
RST 18H DF
Call the ROM comparison routine DCOMPR through the RST 18H hook at 4006H, which compares Register Pair HL (the walk pointer) with Register Pair DE (ARYTAB at 40FBH, the top of the simple variables). The Z FLAG is set when they are equal.
4F25
If the Z FLAG (Zero) has been set, the walk has reached ARYTAB and every simple variable has been dealt with, so JUMP to 4F3FH to start on the arrays.
4F27
LD C,(HL) 4E
Fetch the variable's TYPE byte, at the address in Register Pair HL, into Register C. Level II stores the value size directly in this byte: 02H integer, 03H string, 04H single precision, 08H double precision. Register C therefore becomes the advance applied at 4F23H on the next turn.
4F28
INC HL 23
INCrement Register Pair HL by 1, stepping past the type byte to the first character of the variable name.
4F29
INC HL 23
INCrement Register Pair HL by 1 again, so Register Pair HL points at the SECOND character of the variable name.
4F2A
BIT 7,(HL) CB 7E
Test bit 7 of the second name character at the address in Register Pair HL. Level II sets bit 7 of a name character to mark a DEF FN function, which keeps function names in a namespace of their own, so a set bit here means this entry is a function definition rather than an ordinary variable.
4F2C
INC HL 23
INCrement Register Pair HL by 1, stepping past the second name character to the first byte of the value.
4F2D
If the Z FLAG (Zero) has been set, bit 7 was clear and this is an ordinary variable, so JUMP to 4F35H to test whether it is a string.
4F2F
LD (HL),B 70
This entry is a DEF FN function. Store zero (Register B, still 00H from 4F20H) into the low byte of the definition pointer at the address in Register Pair HL. The function's definition is held as a pointer to the program text inside the function's own variable slot, and that text is about to be replaced, so the pointer must be voided. BASIC/CMD's FN evaluator at 5603H treats a zero pointer as undefined and raises the undefined user function error through its table entry at 642FH.
4F30
INC HL 23
INCrement Register Pair HL by 1 to reach the high byte of the definition pointer.
4F31
LD (HL),B 70
Store zero (Register B) into the high byte of the definition pointer at the address in Register Pair HL, completing the erasure of the definition.
4F32
DEC HL 2B
DECrement Register Pair HL by 1, putting the walk pointer back at the first byte of the value so that the advance at 4F23H measures from the right place.
4F33
LOOP BACK to 4F23H to advance past this entry's value and take the next simple variable.
4F35
LD A,C 79
Load Register A with the variable's type byte from Register C so that it can be tested.
4F36
CP 03H FE 03
Compare Register A (the type byte) against 03H, the Level II type code for a string. If Register A equals 03H, the Z FLAG is set; otherwise the NZ FLAG is set.
4F38
If the NZ FLAG (Not Zero) has been set, this is a numeric variable whose value needs no attention, so LOOP BACK to 4F23H to advance past it and take the next one.
4F3A
GOSUB to 4F6AH to examine this string variable's three-byte descriptor, at the address in Register Pair HL, and copy its text into string space if the text currently lives inside the program. The routine leaves Register Pair HL pointing at the byte after the descriptor, which is the next variable entry.
4F3D
LOOP BACK to 4F24H. The advance at 4F23H is deliberately skipped because 4F6AH has already stepped Register Pair HL over the three descriptor bytes. Loop End

4F3FH - Chained Load: Walk the Arrays

The array table runs from ARYTAB to STREND. Each array is a type byte, two name characters, a two-byte block length, a dimension count, one two-byte size per dimension and then the elements. Only string arrays need attention, and for those every element descriptor is passed to the same relocator the simple variables used.

4F3F
LD DE,(40FDH) ED 5B FD 40
Load Register Pair DE with STREND from 40FDH, the Level II pointer to the first free byte above every variable, which is the end of the array table. This is the end-of-scan limit. Register Pair HL still holds ARYTAB, left there by the simple variable loop when it fell out at 4F25H.
4F43
RST 18H DF
Loop Start
Call the ROM comparison routine DCOMPR through the RST 18H hook at 4006H, comparing Register Pair HL (the array walk pointer) with Register Pair DE (STREND at 40FDH). The Z FLAG is set when they are equal.
4F44
If the Z FLAG (Zero) has been set, the walk has reached STREND and every array has been dealt with, so JUMP to 4F99H to park the whole variable block under the stack.
4F46
LD A,(HL) 7E
Fetch the array's TYPE byte, at the address in Register Pair HL, into Register A. As for simple variables the value is 02H, 03H, 04H or 08H, and 03H means an array of strings.
4F47
INC HL 23
INCrement Register Pair HL by 1, stepping past the type byte to the first name character.
4F48
INC HL 23
INCrement Register Pair HL by 1, stepping past the first name character.
4F49
INC HL 23
INCrement Register Pair HL by 1, stepping past the second name character to the two-byte block length.
4F4A
LD C,(HL) 4E
Fetch the low byte of the array's BLOCK LENGTH, at the address in Register Pair HL, into Register C. The block length counts everything from the dimension count to the last element.
4F4B
INC HL 23
INCrement Register Pair HL by 1 to reach the high byte of the block length.
4F4C
LD B,(HL) 46
Fetch the high byte of the block length, at the address in Register Pair HL, into Register B. Register Pair BC now holds the whole block length.
4F4D
INC HL 23
INCrement Register Pair HL by 1, so Register Pair HL points at the DIMENSION COUNT, which is the first byte the block length covers.
4F4E
CP 03H FE 03
Compare Register A (the array's type byte) against 03H, the Level II type code for a string. If Register A equals 03H, the Z FLAG is set; otherwise the NZ FLAG is set.
4F50
If the Z FLAG (Zero) has been set, this is a string array and its elements must be examined, so JUMP to 4F55H.
4F52
ADD HL,BC 09
This is a numeric array, so add its block length (Register Pair BC) to Register Pair HL to step over the whole array in one move.
4F53
LOOP BACK to 4F43H to test the walk pointer against STREND and take the next array. Loop End
4F55
LD A,(HL) 7E
Fetch the DIMENSION COUNT, at the address in Register Pair HL, into Register A. Each dimension is described by a two-byte size that sits between the count and the first element.
4F56
PUSH DE D5
Save STREND (Register Pair DE) onto the stack, because Register Pair DE is about to be re-used to hold the end of this array's block. It is recovered at 4F67H.
4F57
PUSH HL E5
Save the address of the dimension count (Register Pair HL) onto the stack, so that it can be recovered after the end of the block has been worked out.
4F58
ADD HL,BC 09
Add the block length (Register Pair BC) to Register Pair HL, giving the address of the first byte PAST this array.
4F59
EX DE,HL EB
Exchange Register Pair DE and Register Pair HL, so Register Pair DE now holds the end-of-block address that the element loop tests against.
4F5A
POP HL E1
Restore Register Pair HL from the stack, recovering the address of the dimension count saved at 4F57H.
4F5B
INC HL 23
INCrement Register Pair HL by 1, stepping past the dimension count itself to the first dimension size word.
4F5C
RLCA 07
Rotate Register A left, doubling the dimension count because each dimension is described by a two-byte size. Register A now holds the number of BYTES the dimension table occupies.
4F5D
LD C,A 4F
Load Register C with that byte count from Register A, the low half of the offset.
4F5E
LD B,00H 06 00
Load Register B with 00H so that Register Pair BC holds the dimension table size as a 16-bit value. A Level II array is limited to 255 dimensions, so the doubled count always fits in one byte.
4F60
ADD HL,BC 09
Add the dimension table size (Register Pair BC) to Register Pair HL, so Register Pair HL now points at the FIRST ELEMENT of the string array, that is at its first three-byte descriptor.
4F61
Loop Start
GOSUB to 4F6AH to examine the string descriptor at the address in Register Pair HL and copy its text into string space if the text lives inside the program. The routine leaves Register Pair HL pointing at the next descriptor.
4F64
RST 18H DF
Call the ROM comparison routine DCOMPR through the RST 18H hook at 4006H, comparing Register Pair HL (the element pointer) with Register Pair DE (the end of this array's block, worked out at 4F58H). The Z FLAG is set when they are equal.
4F65
If the NZ FLAG (Not Zero) has been set, elements of this array remain, so LOOP BACK to 4F61H to take the next descriptor. Loop End
4F67
POP DE D1
Restore Register Pair DE from the stack, recovering STREND saved at 4F56H, so that the outer loop has its end-of-scan limit back.
4F68
LOOP BACK to 4F43H to test the walk pointer against STREND and take the next array. Register Pair HL is already at the first byte past this array because the element loop ran to the end of the block.

4F6AH - Chained Load: Move One String Body into String Space

A Level II string variable is a three-byte descriptor of length followed by text address, and the text itself may live in one of two places. A string assigned from a quoted constant points straight into the program text, which costs nothing while that program is loaded but is worthless once it has been replaced. A string built at run time already lives in string space at the top of memory and needs no attention. This routine tells the two apart by two address comparisons and copies only the first kind, rewriting the descriptor to point at the new copy. Entry is with Register Pair HL at the descriptor and Register Pair DE at the top of the region being scanned. Exit is always with Register Pair HL advanced by three, so the caller can walk straight on.

4F6A
PUSH DE D5
Save the caller's scan limit (Register Pair DE, either ARYTAB for the simple variable pass or the end of the current array block) onto the stack, so that it can be handed back unchanged at 4F92H or 4F97H.
4F6B
LD A,(HL) 7E
Fetch the string's LENGTH byte, the first byte of the descriptor at the address in Register Pair HL, into Register A.
4F6C
INC HL 23
INCrement Register Pair HL by 1 so that it points at the two-byte TEXT ADDRESS field of the descriptor.
4F6D
PUSH HL E5
Save the address of the text address field (Register Pair HL) onto the stack. It is needed at 4F8DH to write the new address back into the descriptor, and at 4F94H to work out where the next descriptor begins.
4F6E
AND A A7
Test Register A, the string length, against itself to set the flags without altering it. The Z FLAG is set when the string is empty.
4F6F
If the Z FLAG (Zero) has been set, the string has no text at all and there is nothing to move, so JUMP to 4F94H to tidy the stack and return.
4F71
LD C,A 4F
Load Register C with the string length from Register A, holding it while Register A is used for other work.
4F72
LD A,(HL) 7E
Fetch the low byte of the string's text address, at the address in Register Pair HL, into Register A.
4F73
INC HL 23
INCrement Register Pair HL by 1 to reach the high byte of the string's text address.
4F74
LD H,(HL) 66
Fetch the high byte of the string's text address into Register H.
4F75
LD L,A 6F
Move the low byte held in Register A into Register L, so Register Pair HL now points at the string's TEXT.
4F76
RST 18H DF
Call the ROM comparison routine DCOMPR through the RST 18H hook at 4006H, comparing Register Pair HL (the string's text address) with Register Pair DE (the caller's scan limit). The CARRY FLAG is set when the text address is the lower of the two.
4F77
If the NO CARRY FLAG has been set, the text lies at or above the top of the variable area, which means it is already in string space and will survive the load untouched, so JUMP to 4F94H and leave the descriptor alone.
4F79
LD DE,(40A4H) ED 5B A4 40
Load Register Pair DE with TXTTAB from 40A4H, the Level II pointer to the first byte of the program text, so that the lower end of the program can be tested as well.
4F7D
RST 18H DF
Call the ROM comparison routine DCOMPR through the RST 18H hook at 4006H, comparing Register Pair HL (the string's text address) with Register Pair DE (TXTTAB at 40A4H). The CARRY FLAG is set when the text address is below the start of the program.
4F7E
If the CARRY FLAG has been set, the text lies BELOW the program, which on this machine means it points into the keyboard line buffer or another transient area rather than into anything the load will disturb, so JUMP to 4F94H and leave the descriptor alone. Only text lying between TXTTAB and the top of the variable area, that is a constant embedded in the program itself, reaches the copy below.
4F80
LD A,C 79
Load Register A with the string length from Register C, because the ROM string space allocator takes its length in Register A.
4F81
PUSH HL E5
Save the string's current text address (Register Pair HL) onto the stack; it is the source address of the copy and is recovered at 4F88H.
4F82
GOSUB to ROM routine at 28BFH, GETSPA, which reserves Register A bytes in the string heap between STKTOP at 40A0H and FRETOP at 40D6H, running a garbage collection first if there is not enough room and raising an Out of string space error if that still does not free enough. It returns the address of the reserved space in Register Pair DE and leaves the length unchanged in Register A.
4F85
LD C,A 4F
Load Register C with the string length, still in Register A after the allocation, as the low half of the block move count.
4F86
LD B,00H 06 00
Load Register B with 00H so that Register Pair BC holds the length as a 16-bit count for LDIR. A Level II string cannot exceed 255 characters, so the high byte is always zero.
4F88
POP HL E1
Restore Register Pair HL from the stack, recovering the string's current text address saved at 4F81H, which is the source of the move.
4F89
PUSH DE D5
Save the newly allocated string space address (Register Pair DE) onto the stack, because LDIR advances Register Pair DE past the end of the copy and the ORIGINAL address is the one the descriptor must be given. It is recovered at 4F8CH.
4F8A
LDIR ED B0
Block move. The source is the string's text inside the program at Register Pair HL, the destination is the newly reserved string space at Register Pair DE, and the count in Register Pair BC is the string length. Addresses increment, so the text is copied forward. This is what lifts the constant out of the program before the program is thrown away.
4F8C
POP DE D1
Restore Register Pair DE from the stack, recovering the START of the newly allocated string space saved at 4F89H.
4F8D
POP HL E1
Restore Register Pair HL from the stack, recovering the address of the descriptor's text address field saved at 4F6DH.
4F8E
LD (HL),E 73
Store the low byte of the new text address (Register E) into the descriptor at the address in Register Pair HL.
4F8F
INC HL 23
INCrement Register Pair HL by 1 to reach the high byte of the descriptor's text address field.
4F90
LD (HL),D 72
Store the high byte of the new text address (Register D) into the descriptor at the address in Register Pair HL. The variable now points at a copy in string space instead of at the program.
4F91
INC HL 23
INCrement Register Pair HL by 1, leaving it on the byte after the descriptor, which is where the caller expects to carry on.
4F92
POP DE D1
Restore Register Pair DE from the stack, recovering the caller's scan limit saved at 4F6AH.
4F93
RET C9
RETurn to the caller with Register Pair HL three bytes past the descriptor it was given and Register Pair DE unchanged.
4F94
POP HL E1
This descriptor needed no work. Restore Register Pair HL from the stack, recovering the address of the text address field saved at 4F6DH.
4F95
INC HL 23
INCrement Register Pair HL by 1 to step over the low byte of the text address field.
4F96
INC HL 23
INCrement Register Pair HL by 1 to step over the high byte, leaving Register Pair HL on the byte after the descriptor exactly as the copy path does.
4F97
POP DE D1
Restore Register Pair DE from the stack, recovering the caller's scan limit saved at 4F6AH.
4F98
RET C9
RETurn to the caller with Register Pair HL three bytes past the descriptor and the descriptor itself unchanged.

4F99H - Chained Load: Park the Variable Block and Seed the BASIC/CMD Operand Cells

Every string that had to be rescued has been rescued, so the whole variable block from VARTAB to STREND can now be lifted out of the way and the load allowed to proceed. The block is moved to sit just below the stack, STKTOP is lowered to protect it, and the five 16-bit operand cells that BASIC/CMD's fixup code at 5F54H reads are filled in. Those five cells ship as 0000H in the BASIC/CMD image and NO instruction in BASIC/CMD ever writes them, so this is their only source. The chain-state byte 65F4H is set to 0D6H last, and that is likewise the only place in either module the value 0D6H is written.

4F99
LD HL,(40F9H) 2A F9 40
Load Register Pair HL with VARTAB from 40F9H, the Level II pointer to the start of the simple variable table, which is the bottom of the block about to be moved.
4F9C
LD (5F5EH),HL 22 5E 5F
Store VARTAB (Register Pair HL) into 5F5EH, which is the 16-bit operand of the LD DE,0000H instruction at BASIC/CMD 5F5DH. That instruction is the start of the fixup that runs after the new program has been loaded, and this is the OLD VARTAB it needs in order to work out how far the parked block must come back down.
4F9F
EX DE,HL EB
Exchange Register Pair DE and Register Pair HL. Register Pair DE arrived from the array loop holding STREND, so after the exchange Register Pair HL holds STREND, the top of the variable block, and Register Pair DE holds VARTAB, its bottom.
4FA0
XOR A AF
Set Register A to ZERO and clear all flags, so that the CARRY FLAG is clear before the SBC that follows.
4FA1
PUSH HL E5
Save STREND (Register Pair HL) onto the stack; it is the source end of the descending block move and is recovered at 4FAEH.
4FA2
SBC HL,DE ED 52
Subtract VARTAB (Register Pair DE) from STREND (Register Pair HL), giving the LENGTH IN BYTES of the whole variable block, simple variables and arrays together.
4FA4
LD (5F71H),HL 22 71 5F
Store that block length (Register Pair HL) into 5F71H, the 16-bit operand of the LD BC,0000H instruction at BASIC/CMD 5F70H, which is the count for the LDIR at 5F77H that moves the parked block back down once the new program is in place.
4FA7
LD B,H 44
Copy the high byte of the block length (Register H) into Register B, the first half of setting up the move count.
4FA8
LD C,L 4D
Copy the low byte of the block length (Register L) into Register C, so Register Pair BC now holds the byte count for the LDDR below.
4FA9
LD HL,0FF80H 21 80 FF
Load Register Pair HL with 0FF80H, which is minus 128 as a 16-bit two's complement value, ready to be added to the stack pointer.
4FAC
ADD HL,SP 39
Add the STACK POINTER to Register Pair HL, so Register Pair HL now holds the current stack pointer less 128. That 128-byte gap is the working headroom left for the stack itself while the loaded program is being read in, and its top is where the parked block will end.
4FAD
EX DE,HL EB
Exchange Register Pair DE and Register Pair HL, so Register Pair DE holds the destination end address, the stack pointer less 128, ready for the descending block move.
4FAE
POP HL E1
Restore Register Pair HL from the stack, recovering STREND saved at 4FA1H.
4FAF
DEC HL 2B
DECrement Register Pair HL by 1, so that Register Pair HL points at the LAST BYTE of the variable block rather than at the first byte above it. LDDR works downwards and needs the highest source byte.
4FB0
RST 18H DF
Call the ROM comparison routine DCOMPR through the RST 18H hook at 4006H, comparing Register Pair HL (the last byte of the variable block) with Register Pair DE (the stack pointer less 128). The CARRY FLAG is set when the variable block still ends below the destination, which is the safe case.
4FB1
If the NO CARRY FLAG has been set, the variable block already reaches the destination and there is no room to park it, so JUMP to the Level II ROM Out of memory error entry at 197AH. This is the check behind the manual's warning that a chained load with variables can fail for want of space.
4FB4
LD A,B 78
Load Register A with the high byte of the block length (Register B) so that the whole count can be tested for zero.
4FB5
OR C B1
OR Register A (the length high byte) with Register C (the length low byte). The Z FLAG is set only when the block length is zero, which happens when the program has no variables at all.
4FB6
If the Z FLAG (Zero) has been set, there is nothing to move, so JUMP to 4FBAH and skip the block move. LDDR with a count of zero would otherwise move 65536 bytes.
4FB8
LDDR ED B8
Block move. The source is the top of the variable block at Register Pair HL, the destination is the stack pointer less 128 at Register Pair DE, and the count in Register Pair BC is the block length. Addresses DECREMENT, so the block is copied downwards from its top, which is what allows source and destination to overlap safely when the destination is higher. On completion Register Pair DE points one byte BELOW the parked block, which is exactly the value STKTOP is given at 4FCCH.
4FBA
LD HL,(40FBH) 2A FB 40
Load Register Pair HL with ARYTAB from 40FBH, the Level II pointer to the start of the array table inside the block that has just been parked.
4FBD
LD (5F65H),HL 22 65 5F
Store ARYTAB (Register Pair HL) into 5F65H, the 16-bit operand of the LD HL,0000H instruction at BASIC/CMD 5F64H. The fixup at 5F54H adds the distance the block travelled to this OLD ARYTAB and writes the result back to 40FBH, which is how the array pointer is corrected after the block comes back down.
4FC0
LD HL,(40D6H) 2A D6 40
Load Register Pair HL with FRETOP from 40D6H, the Level II pointer to the top of the live string space, which must be preserved because the ROM NEW that follows this call would otherwise reset it.
4FC3
LD (5F7EH),HL 22 7E 5F
Store FRETOP (Register Pair HL) into 5F7EH, the 16-bit operand of the LD HL,0000H instruction at BASIC/CMD 5F7DH, which restores 40D6H once the load is complete so that the rescued string bodies are still accounted for.
4FC6
LD HL,(40A0H) 2A A0 40
Load Register Pair HL with STKTOP from 40A0H, the Level II pointer to the bottom of string space, taken BEFORE it is lowered below to protect the parked block.
4FC9
LD (5F84H),HL 22 84 5F
Store the original STKTOP (Register Pair HL) into 5F84H, the 16-bit operand of the LD HL,0000H instruction at BASIC/CMD 5F83H. That instruction restores 40A0H, then derives SAVSTK at 40E8H from it and loads the stack pointer itself, which is why the CALL 5F83H at BASIC/CMD 5A4AH never returns.
4FCC
LD (40A0H),DE ED 53 A0 40
Store the address one byte below the parked block (Register Pair DE, left there by the LDDR at 4FB8H) into STKTOP at 40A0H. Lowering STKTOP puts the parked variables above the string space ceiling, so nothing the loader or the ROM NEW does can reach them.
4FD0
LD A,0D6H 3E D6
Load Register A with 0D6H, the value that marks a chained load WITH VARIABLES in progress.
4FD2
LD (65F4H),A 32 F4 65
Store 0D6H (Register A) into 65F4H, BASIC/CMD's CHAIN STATE byte. The other values that cell takes are 00H for a plain LOAD or MERGE, 0FFH for RUN"filespec" and the option character when one was given. No instruction in BASIC/CMD writes 0D6H; this is its only source, and it is what BASIC/CMD tests at 5A43H, 5C28H, 5F4DH and 5FAAH to know that a parked variable block is waiting to be brought back down.
4FD5
RET C9
RETurn to BASIC/CMD at 5EB0H, which goes straight on to the ROM SCRATH routine that clears the program area and then loads the new program.

4FD6H - Selectors 05H and 06H: the M and C Program Line Edit Commands

The two single-key edit commands that DOS version 5 LBASIC keeps in this overlay. C n1,n2 duplicates line n1 as a new line n2, and M n1,n2 does the same and then deletes n1. Line n1 must exist and line n2 must not. BASIC/CMD's READY prompt line collector at 6459H recognises the letters through its abbreviation table at 656BH, where M carries the selector value 05H and C carries 06H, and the manual notes that both commands clear the variables and close open files, which is a consequence of the line being re-entered through the ordinary tokenizer. The two selectors share one body: the difference is a single flag pushed at 4FD8H and tested at 500DH. Both entries lean on the ROM twice over. The new line is entered by handing the untokenized text to EDITRT at 2FEBH, the exit the EDIT command itself uses, and for a move the old line is removed by jumping into the DELETE body at 2BCAH with a one-line range already set up. Neither borrowed entry point is the routine's own front door, which is why the register conditions are spelled out on each row.

4FD6
OR 0AFH F6 AF
ENTRY FOR SELECTOR 05H, the MOVE command. OR Register A with 0AFH, which leaves Register A non-zero whatever it held and therefore sets the NZ FLAG. The flag is the only thing that distinguishes a move from a copy.
4FD7
XOR A AF
ENTRY FOR SELECTOR 06H, the COPY command, reached by the JP at 4E18H. Set Register A to ZERO and clear all flags, which sets the Z FLAG.

Overlapping Entry Points
4FD6H and 4FD7H occupy the same two bytes, F6 AF. Entered at 4FD6H the pair is the single instruction OR 0AFH; entered one byte later at 4FD7H the AF byte is executed on its own as XOR A. Both entries then fall into the common body at 4FD8H, carrying the NZ FLAG for a move and the Z FLAG for a copy. The address 4FD7H therefore has two valid readings, as the operand of the instruction at 4FD6H and as an instruction in its own right, and the disassembly above shows both.

4FD8
PUSH AF F5
Save Register A and the FLAGS onto the stack, preserving the move-or-copy distinction across all of the parsing that follows. It is recovered at 500CH.
4FD9
RST 10H D7
Call the character fetcher through the RST 10H hook at 4003H, which advances Register Pair HL and returns the next character of the command line in Register A, skipping blanks. LBASIC points this hook at its own routine at BASIC/CMD 6579H rather than at the ROM CHRGTR, but for an ordinary character the behaviour is the ROM's. The Z FLAG is set at the end of the statement.
4FDA
If the Z FLAG (Zero) has been set, the command line ended with no line numbers at all, so JUMP to 502BH to raise a Syntax error.
4FDC
GOSUB to ROM routine at 1E4FH, LINSPC, the line number parser that also accepts a full stop as a shorthand for the current line. It converts the digits at Register Pair HL into a 16-bit line number in Register Pair DE. This is n1, the line to be copied or moved.
4FDF
LD A,D 7A
Load Register A with the high byte of the parsed line number (Register D) so that the whole pair can be tested for zero.
4FE0
OR E B3
OR Register A (the line number high byte) with Register E (the line number low byte). The Z FLAG is set only when the line number is zero, which LBASIC does not allow as a program line number.
4FE1
If the Z FLAG (Zero) has been set, the source line number was zero or absent, so JUMP to 502BH to raise a Syntax error.
4FE3
LD (5023H),DE ED 53 23 50
Self-Modifying Code
Store the SOURCE line number (Register Pair DE) into 5023H, the 16-bit operand of the LD DE,0000H instruction at 5022H. The listing shows the assembled 0000H. 5022H is only reached on the move path, where the source line has to be found a second time so that it can be deleted, and this is how its number is carried across the re-entry into the tokenizer.
4FE7
PUSH HL E5
Save the command line text pointer (Register Pair HL) onto the stack while Register Pair HL is used to build the address of the source line. It is swapped back at 4FF4H.
4FE8
GOSUB to ROM routine at 1B2CH, FNDLIN, which searches the program for the line whose number is in Register Pair DE. It returns with the CARRY FLAG set and Register Pair BC pointing at the line's link field when the line exists, and with the NO CARRY FLAG when it does not. Register Pair DE is preserved.
4FEB
If the NO CARRY FLAG has been set, the source line does not exist, so JUMP to the Level II ROM Undefined line number error entry at 1ED9H.
4FEE
LD H,B 60
Copy the high byte of the found line's address (Register B) into Register H.
4FEF
LD L,C 69
Copy the low byte of the found line's address (Register C) into Register L, so Register Pair HL now points at the source line's two-byte link field.
4FF0
INC HL 23
INCrement Register Pair HL by 1, stepping over the low byte of the link field.
4FF1
INC HL 23
INCrement Register Pair HL by 1, stepping over the high byte of the link field to reach the line number.
4FF2
INC HL 23
INCrement Register Pair HL by 1, stepping over the low byte of the line number.
4FF3
INC HL 23
INCrement Register Pair HL by 1, stepping over the high byte of the line number. Register Pair HL now points at the first byte of the source line's TOKENIZED BODY, which is what the untokenizer at 2B7EH expects.
4FF4
EX (SP),HL E3
Exchange Register Pair HL with the word on top of the stack. The body pointer just built goes onto the stack and the command line text pointer saved at 4FE7H comes back into Register Pair HL, so parsing can continue where it left off.
4FF5
RST 08H => 2CH CF 2C
Call the ROM syntax checker through the RST 08H hook at 4000H, requiring the next character of the command line at Register Pair HL to be the 2CH byte that follows, ASCII ,. This is the comma between the two line numbers.
4FF7
GOSUB to ROM routine at 1E4FH, LINSPC, to parse the second line number into Register Pair DE. This is n2, the line number to be created.
4FFA
LINSPC returns with the Z FLAG reflecting the character that ended the number. If the NZ FLAG (Not Zero) has been set there is text after the second line number, so JUMP to 502BH to raise a Syntax error.
4FFC
LD A,D 7A
Load Register A with the high byte of the destination line number (Register D) so that the whole pair can be tested for zero.
4FFD
OR E B3
OR Register A (the destination line number high byte) with Register E (its low byte). The Z FLAG is set only when the destination line number is zero, which is not a legal program line number.
4FFE
If the Z FLAG (Zero) has been set, the destination line number was zero, so JUMP to 502BH to raise a Syntax error.
5000
GOSUB to ROM routine at 1B2CH, FNDLIN, to search the program for the DESTINATION line number now in Register Pair DE. The CARRY FLAG comes back set if a line with that number already exists.
5003
POP HL E1
Restore Register Pair HL from the stack, recovering the pointer to the SOURCE line's tokenized body that was put there by the exchange at 4FF4H.
5004
If the CARRY FLAG has been set, a line with the destination number already exists, so JUMP to the Level II ROM Illegal function call error entry at 1E4AH. This is the error the manual describes for incorrect line numbers.
5007
PUSH DE D5
Save the destination line number (Register Pair DE) onto the stack, because the untokenizer is about to overwrite the line input buffer and Register Pair DE with it. It is recovered at 500BH.
5008
GOSUB to ROM routine at 2B7EH, BUFLIN, the untokenizer that LIST and EDIT use. On entry Register Pair HL points at the source line's tokenized body; it expands every keyword token back into text and writes the result into the line input buffer addressed by BUFPNT at 40A7H, which under LBASIC is BASIC/CMD's own 240-byte buffer at 5309H.
500B
POP DE D1
Restore Register Pair DE from the stack, recovering the destination line number saved at 5007H. The source line now exists as plain text in the line input buffer and only needs a new line number in front of it.
500C
POP AF F1
Restore Register A and the FLAGS from the stack, recovering the move-or-copy flag pushed at 4FD8H. The Z FLAG is set for a copy and the NZ FLAG for a move.
500D
If the Z FLAG (Zero) has been set this is a COPY, so nothing needs to happen after the new line has been entered and control JUMPs to 5015H, leaving the keyboard line input hook alone.
500F
LD HL,501AH 21 1A 50
This is a MOVE, so the source line must be deleted once the new one has been entered. Point Register Pair HL at 501AH, the continuation inside this overlay that performs the deletion.
5012
LD (41B0H),HL 22 B0 41
Store 501AH (Register Pair HL) into 41B0H, the 16-bit operand of the JP instruction in the Disk BASIC vector table slot at 41AFH. That slot is the ROM keyboard line input exit, taken from ROM 0368H, and LBASIC normally points it at BASIC/CMD's routine at 5BD1H. Diverting it to 501AH is how this overlay regains control immediately after the ROM has tokenized and inserted the new line.
5015
OR 0FFH F6 FF
Load Register A with 0FFH, which sets the NZ FLAG. This is the flag EDITRT tests one instruction after the entry taken below. The ROM reaches EDITRT from 2FE8H, where LD A,D followed by AND E and INC A leaves the Z FLAG set when the line number in Register Pair DE is FFFFH, meaning there is no line number and the text belongs to the command line collector. Forcing Register A non-zero here sets the NZ FLAG so that the RET Z at 2FEFH is NOT taken and the buffered text is entered into the program as a numbered line.
5017
JUMP to the Level II ROM at 2FEBH, EDITRT, the common exit of the EDIT command. It reloads Register Pair HL from BUFPNT at 40A7H, and because Register A is non-zero the RET Z at 2FEFH is passed over; it then sets the CARRY FLAG, which tells the insert code that a line number has already been collected, steps Register Pair HL back to the first byte of the buffer, pushes the flags and jumps to EDENT at 1A98H. EDENT tokenizes the buffer and inserts it into the program as the line whose number is in Register Pair DE. For a copy the job ends there; for a move the diverted vector at 41AFH brings control back to 501AH.
501A
LD HL,5BD1H 21 D1 5B
Move Continuation
Reached through the Disk BASIC keyboard line input slot at 41AFH after the ROM has entered the new line. Point Register Pair HL at 5BD1H, BASIC/CMD's own keyboard line input routine, so that the diverted vector can be put back.
501D
LD (41B0H),HL 22 B0 41
Store 5BD1H (Register Pair HL) into 41B0H, the operand of the JP in the Disk BASIC vector table slot at 41AFH, restoring the keyboard line input exit that was diverted at 5012H. From here on the interpreter behaves normally again.
5020
POP HL E1
Discard one word from the stack into Register Pair HL. Control arrived here through the Disk BASIC keyboard line input slot at 41AFH, which the ROM reaches from inside its own line collection loop, so that loop's working addresses are still stacked. They have to go, because this routine leaves through the ROM DELETE body, which replaces the top of the stack with FINI at 1AE8H and exits that way rather than returning to whatever called it.
5021
POP HL E1
Discard the second word from the stack into Register Pair HL, leaving the stack in the state the ROM DELETE body expects.
5022
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the SOURCE line number. The operand at 5023H is shown as 0000H in the listing and was written at 4FE3H with the first of the two line numbers parsed from the command.
5025
GOSUB to ROM routine at 1B2CH, FNDLIN, to locate the source line again. It returns Register Pair BC pointing at that line's link field and PRESERVES Register Pair DE, so the line number is still there afterwards. Those are exactly the two values the ROM DELETE routine holds at the point jumped to below.
5028
JUMP into the Level II ROM DELETE routine at 2BCAH, three instructions past its entry, skipping the range scan SCNLIN at 1B10H and the POP DE that follows it. 2BCAH is reached with Register Pair BC pointing at the first line of the range and Register Pair DE holding the LAST line number of the range, and the two values just set up make both of those the source line, so DELETE removes exactly one line. It stacks Register Pair BC twice for the pointer fixup, runs its own FNDLIN on Register Pair DE to find the end of the range, checks that the range start precedes its end, prints the READY message from 1929H through STROUT at 28A7H, plants FINI at 1AE8H as its return address and closes the gap by moving everything above the line down over it, updating VARTAB at 40F9H. The move is then complete and the interpreter is back at the READY prompt.
502B
Error Path
JUMP to the Level II ROM Syntax error entry at 1997H. Reached from 4FDAH, 4FE1H, 4FFAH and 4FFEH, that is whenever the command line does not consist of two non-zero line numbers separated by a comma.

502DH - Data: LDOS Error Number to BASIC Error Value Table

Forty-two bytes, one per LDOS error number from 0 at 502DH to 41 at 5056H. Each byte is the value the Level II ROM error handler expects in Register E, which is twice the documented BASIC error code. LDOS distinguishes far more failures than BASIC has messages for, so the table is heavily many-to-one: twenty-one different LDOS numbers all report Disk I/O error, which is exactly why the manual recommends CMD"E" for the real reason. Selector 02H at 4E6BH is the only reader.

502D
DEFB 19H 19
Entry for LDOS error number 0, no error. This byte is also the high byte of the operand of the JP 1997H instruction at 502BH, so the table shares its first entry with the code above it; see the debug note. The value 19H is not a valid BASIC error value and error number 0 never reaches this table, because BASIC/CMD's CMD string shell returns at 55DEH without calling the translator when the DOS command succeeded.

Overlapping Code And Data
The byte at 502DH has two valid readings. It is the high byte of the operand of the JP 1997H instruction at 502BH, the Syntax error exit of the M and C edit commands, and it is also entry 0 of the table below, the one addressed when the LDOS error number is zero. The address is therefore shown once at 502BH as part of the jump and once here as a table entry. The table base loaded at 4E6FH is 502DH, so the sharing is deliberate and saves one byte.

502E
DEFB 72H 72
Entry for LDOS error number 1, parity error during header read. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
502F
DEFB 72H 72
Entry for LDOS error number 2, seek error during read. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5030
DEFB 72H 72
Entry for LDOS error number 3, lost data during read. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5031
DEFB 72H 72
Entry for LDOS error number 4, parity error during read. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5032
DEFB 72H 72
Entry for LDOS error number 5, data record not found during read. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5033
DEFB 72H 72
Entry for LDOS error number 6, attempted to read system data record. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5034
DEFB 72H 72
Entry for LDOS error number 7, attempted to read locked or deleted data record. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5035
DEFB 80H 80
Entry for LDOS error number 8, device not available. Supplies Register E = 80H, the BASIC error value for code 64, Bad file name.
5036
DEFB 72H 72
Entry for LDOS error number 9, parity error during header write. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5037
DEFB 72H 72
Entry for LDOS error number 10, seek error during write. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5038
DEFB 72H 72
Entry for LDOS error number 11, lost data during write. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5039
DEFB 72H 72
Entry for LDOS error number 12, parity error during write. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
503A
DEFB 72H 72
Entry for LDOS error number 13, data record not found during write. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
503B
DEFB 72H 72
Entry for LDOS error number 14, write fault on disk drive. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
503C
DEFB 88H 88
Entry for LDOS error number 15, write protected disk. Supplies Register E = 88H, the BASIC error value for code 68, Disk write protection.
503D
DEFB 66H 66
Entry for LDOS error number 16, illegal logical file number. Supplies Register E = 66H, the BASIC error value for code 51, Internal error.
503E
DEFB 72H 72
Entry for LDOS error number 17, directory read error. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
503F
DEFB 72H 72
Entry for LDOS error number 18, directory write error. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5040
DEFB 80H 80
Entry for LDOS error number 19, illegal file name. Supplies Register E = 80H, the BASIC error value for code 64, Bad file name.
5041
DEFB 72H 72
Entry for LDOS error number 20, GAT read error. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5042
DEFB 72H 72
Entry for LDOS error number 21, GAT write error. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5043
DEFB 72H 72
Entry for LDOS error number 22, HIT read error. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5044
DEFB 72H 72
Entry for LDOS error number 23, HIT write error. Supplies Register E = 72H, the BASIC error value for code 57, Disk I/O error.
5045
DEFB 6AH 6A
Entry for LDOS error number 24, file not in directory. Supplies Register E = 6AH, the BASIC error value for code 53, File not found.
5046
DEFB 8AH 8A
Entry for LDOS error number 25, file access denied. Supplies Register E = 8AH, the BASIC error value for code 69, File access denied.
5047
DEFB 88H 88
Entry for LDOS error number 26, full or write protected disk. Supplies Register E = 88H, the BASIC error value for code 68, Disk write protection.
5048
DEFB 7AH 7A
Entry for LDOS error number 27, disk space full. Supplies Register E = 7AH, the BASIC error value for code 61, Disk full.
5049
DEFB 66H 66
Entry for LDOS error number 28, end of file encountered. Supplies Register E = 66H, the BASIC error value for code 51, Internal error.
504A
DEFB 66H 66
Entry for LDOS error number 29, record number out of range. Supplies Register E = 66H, the BASIC error value for code 51, Internal error.
504B
DEFB 7AH 7A
Entry for LDOS error number 30, directory full, can't extend file. Supplies Register E = 7AH, the BASIC error value for code 61, Disk full.
504C
DEFB 6AH 6A
Entry for LDOS error number 31, program not found. Supplies Register E = 6AH, the BASIC error value for code 53, File not found.
504D
DEFB 80H 80
Entry for LDOS error number 32, illegal drive number. Supplies Register E = 80H, the BASIC error value for code 64, Bad file name.
504E
DEFB 66H 66
Entry for LDOS error number 33, no device space available. Supplies Register E = 66H, the BASIC error value for code 51, Internal error.
504F
DEFB 66H 66
Entry for LDOS error number 34, load file format error. Supplies Register E = 66H, the BASIC error value for code 51, Internal error.
5050
DEFB 66H 66
Entry for LDOS error number 35, memory fault. Supplies Register E = 66H, the BASIC error value for code 51, Internal error.
5051
DEFB 66H 66
Entry for LDOS error number 36, attempted to load read only memory. Supplies Register E = 66H, the BASIC error value for code 51, Internal error.
5052
DEFB 8AH 8A
Entry for LDOS error number 37, illegal access attempted to protected file. Supplies Register E = 8AH, the BASIC error value for code 69, File access denied.
5053
DEFB 66H 66
Entry for LDOS error number 38, file not open. Supplies Register E = 66H, the BASIC error value for code 51, Internal error.
5054
DEFB 8AH 8A
Entry for LDOS error number 39, device in use. Supplies Register E = 8AH, the BASIC error value for code 69, File access denied.
5055
DEFB 8AH 8A
Entry for LDOS error number 40, protected system device. Supplies Register E = 8AH, the BASIC error value for code 69, File access denied.
5056
DEFB 66H 66
Entry for LDOS error number 41, unknown error code. Supplies Register E = 66H, the BASIC error value for code 51, Internal error. Every LDOS error number of 41 or above is clamped to this entry by the test at 4E72H, so the LDOS parameter error, number 44, is also reported here.

5057H - Data: Message Index Table

Forty-nine entries, 5057H to 50D5H. Each entry is a run of word numbers into the dictionary at 50D6H, with bit 7 set on the last of them, and the entries are laid out so that the entry number is the BASIC error code plus one for the ROM range and the code less twenty-four for the disk range. The expander at 4E36H reaches the entry it wants by counting bit-7 bytes, so the entries can be any length and no offset table is needed. This is the same three-table scheme SYS4 uses for the DOS error dictionary.

5057
DEFB D2H D2
End marker for entry 0. The byte has bit 7 set and holds no usable word number. It exists only so that the entry-skipping loop at 4E36H counts complete entries, in the same way the byte at 50D6H does for the word dictionary; the index arithmetic at 4E30H and 4E31H can never produce an entry number below 2, so this entry is never selected.
5058
DEFB 25H, 85H 25 85
Entry 1, "No error". Word numbers 37 (no), 5 (error). Reserved for BASIC error code 0 and never selected, because the two INC A instructions at 4E30H and 4E31H make 2 the lowest entry number the routine can produce.
505A
DEFB 01H, 02H, 83H 01 02 83
Entry 2, "Next without for". Word numbers 1 (next), 2 (without), 3 (for). Selected for error value 00H, BASIC error code 1.
505D
DEFB 04H, 85H 04 85
Entry 3, "Syntax error". Word numbers 4 (syntax), 5 (error). Selected for error value 02H, BASIC error code 2.
505F
DEFB 06H, 02H, 87H 06 02 87
Entry 4, "Return without GOSUB". Word numbers 6 (return), 2 (without), 7 (GOSUB). Selected for error value 04H, BASIC error code 3.
5062
DEFB 08H, 09H, 8AH 08 09 8A
Entry 5, "Out of data". Word numbers 8 (out), 9 (of), 10 (data). Selected for error value 06H, BASIC error code 4.
5065
DEFB 0BH, 0CH, 8DH 0B 0C 8D
Entry 6, "Illegal function call". Word numbers 11 (illegal), 12 (function), 13 (call). Selected for error value 08H, BASIC error code 5.
5068
DEFB 8EH 8E
Entry 7, "Overflow". Word numbers 14 (overflow). Selected for error value 0AH, BASIC error code 6.
5069
DEFB 08H, 09H, 8FH 08 09 8F
Entry 8, "Out of memory". Word numbers 8 (out), 9 (of), 15 (memory). Selected for error value 0CH, BASIC error code 7.
506C
DEFB 10H, 11H, 92H 10 11 92
Entry 9, "Undefined line number". Word numbers 16 (undefined), 17 (line), 18 (number). Selected for error value 0EH, BASIC error code 8.
506F
DEFB 13H, 08H, 09H, 94H 13 08 09 94
Entry 10, "Subscript out of range". Word numbers 19 (subscript), 8 (out), 9 (of), 20 (range). Selected for error value 10H, BASIC error code 9.
5073
DEFB 15H, 96H 15 96
Entry 11, "Redimensioned array". Word numbers 21 (redimensioned), 22 (array). Selected for error value 12H, BASIC error code 10.
5075
DEFB 17H, 18H, 99H 17 18 99
Entry 12, "Division by zero". Word numbers 23 (division), 24 (by), 25 (zero). Selected for error value 14H, BASIC error code 11.
5078
DEFB 0BH, 9AH 0B 9A
Entry 13, "Illegal direct". Word numbers 11 (illegal), 26 (direct). Selected for error value 16H, BASIC error code 12.
507A
DEFB 1BH, 9CH 1B 9C
Entry 14, "Type mismatch". Word numbers 27 (type), 28 (mismatch). Selected for error value 18H, BASIC error code 13.
507C
DEFB 08H, 09H, 1DH, 9EH 08 09 1D 9E
Entry 15, "Out of string space". Word numbers 8 (out), 9 (of), 29 (string), 30 (space). Selected for error value 1AH, BASIC error code 14.
5080
DEFB 1DH, 1FH, A0H 1D 1F A0
Entry 16, "String too long". Word numbers 29 (string), 31 (too), 32 (long). Selected for error value 1CH, BASIC error code 15.
5083
DEFB 1DH, 21H, 1FH, A2H 1D 21 1F A2
Entry 17, "String formula too complex". Word numbers 29 (string), 33 (formula), 31 (too), 34 (complex). Selected for error value 1EH, BASIC error code 16.
5087
DEFB 23H, A4H 23 A4
Entry 18, "Can't continue". Word numbers 35 (can't), 36 (continue). Selected for error value 20H, BASIC error code 17.
5089
DEFB 25H, A6H 25 A6
Entry 19, "No RESUME". Word numbers 37 (no), 38 (RESUME). Selected for error value 22H, BASIC error code 18.
508B
DEFB 26H, 02H, 85H 26 02 85
Entry 20, "RESUME without error". Word numbers 38 (RESUME), 2 (without), 5 (error). Selected for error value 24H, BASIC error code 19.
508E
DEFB 27H, 85H 27 85
Entry 21, "Unprintable error". Word numbers 39 (unprintable), 5 (error). Selected for ROM error value 26H, BASIC error code 20, and also for every value the dictionary does not cover, since 4E2BH substitutes 5AH for anything in the range 2FH to 62H or 91H and above.
5090
DEFB 28H, A9H 28 A9
Entry 22, "Missing operand". Word numbers 40 (missing), 41 (operand). Selected for error value 28H, BASIC error code 21.
5092
DEFB 2AH, 2BH, 8AH 2A 2B 8A
Entry 23, "Bad cassette data". Word numbers 42 (bad), 43 (cassette), 10 (data). Selected for error value 2AH, BASIC error code 22.
5095
DEFB ACH AC
Entry 24, "?". Word number 44 (?). A placeholder for a BASIC error code that DOS version 5 LBASIC does not assign: error value 2CH, code 23.
5096
DEFB 2DH, 2EH, 8CH 2D 2E 8C
Entry 25, "Undefined user function". Word numbers 45 (undefined), 46 (user), 12 (function). Selected for error value 2EH, BASIC error code 24.
5099
DEFB 2FH, 8EH 2F 8E
Entry 26, "FIELD overflow". Word numbers 47 (FIELD), 14 (overflow). Selected for error value 64H, BASIC error code 50.
509B
DEFB 30H, 85H 30 85
Entry 27, "Internal error". Word numbers 48 (internal), 5 (error). Selected for error value 66H, BASIC error code 51.
509D
DEFB 2AH, 31H, 92H 2A 31 92
Entry 28, "Bad file number". Word numbers 42 (bad), 49 (file), 18 (number). Selected for error value 68H, BASIC error code 52.
50A0
DEFB 31H, 32H, B3H 31 32 B3
Entry 29, "File not found". Word numbers 49 (file), 50 (not), 51 (found). Selected for error value 6AH, BASIC error code 53.
50A3
DEFB 2AH, 31H, B4H 2A 31 B4
Entry 30, "Bad file mode". Word numbers 42 (bad), 49 (file), 52 (mode). Selected for error value 6CH, BASIC error code 54.
50A6
DEFB 31H, 35H, B6H 31 35 B6
Entry 31, "File already open". Word numbers 49 (file), 53 (already), 54 (open). Selected for error value 6EH, BASIC error code 55.
50A9
DEFB ACH AC
Entry 32, "?". Word number 44 (?). A placeholder for a BASIC error code that DOS version 5 LBASIC does not assign: error value 70H, code 56.
50AA
DEFB 37H, 38H, 85H 37 38 85
Entry 33, "Disk I/O error". Word numbers 55 (disk), 56 (I/O), 5 (error). Selected for error value 72H, BASIC error code 57.
50AD
DEFB 31H, 35H, B9H 31 35 B9
Entry 34, "File already exists". Word numbers 49 (file), 53 (already), 57 (exists). Selected for error value 74H, BASIC error code 58.
50B0
DEFB ACH AC
Entry 35, "?". Word number 44 (?). A placeholder for a BASIC error code that DOS version 5 LBASIC does not assign: error value 76H, code 59.
50B1
DEFB ACH AC
Entry 36, "?". Word number 44 (?). A placeholder for a BASIC error code that DOS version 5 LBASIC does not assign: error value 78H, code 60.
50B2
DEFB 37H, BAH 37 BA
Entry 37, "Disk full". Word numbers 55 (disk), 58 (full). Selected for error value 7AH, BASIC error code 61.
50B4
DEFB 3BH, 3CH, BDH 3B 3C BD
Entry 38, "Input past end". Word numbers 59 (input), 60 (past), 61 (end). Selected for error value 7CH, BASIC error code 62.
50B7
DEFB 2AH, 3EH, 92H 2A 3E 92
Entry 39, "Bad record number". Word numbers 42 (bad), 62 (record), 18 (number). Selected for error value 7EH, BASIC error code 63.
50BA
DEFB 2AH, 31H, BFH 2A 31 BF
Entry 40, "Bad file name". Word numbers 42 (bad), 49 (file), 63 (name). Selected for error value 80H, BASIC error code 64.
50BD
DEFB 34H, 9CH 34 9C
Entry 41, "Mode mismatch". Word numbers 52 (mode), 28 (mismatch). Selected for error value 82H, BASIC error code 65.
50BF
DEFB 1AH, 40H, 41H, B1H 1A 40 41 B1
Entry 42, "Direct statement in file". Word numbers 26 (direct), 64 (statement), 65 (in), 49 (file). Selected for error value 84H, BASIC error code 66.
50C3
DEFB 1FH, 42H, C3H 1F 42 C3
Entry 43, "Too many files". Word numbers 31 (too), 66 (many), 67 (files). Selected for error value 86H, BASIC error code 67.
50C6
DEFB 37H, 44H, C5H 37 44 C5
Entry 44, "Disk write protection". Word numbers 55 (disk), 68 (write), 69 (protection). Selected for error value 88H, BASIC error code 68.
50C9
DEFB 31H, 46H, C7H 31 46 C7
Entry 45, "File access denied". Word numbers 49 (file), 70 (access), 71 (denied). Selected for error value 8AH, BASIC error code 69.
50CC
DEFB 48H, 31H, 85H 48 31 85
Entry 46, "Blocked file error". Word numbers 72 (blocked), 49 (file), 5 (error). Selected for error value 8CH, BASIC error code 70.
50CF
DEFB 49H, 4AH, CBH 49 4A CB
Entry 47, "System command aborted". Word numbers 73 (system), 74 (command), 75 (aborted). Selected for error value 8EH, BASIC error code 71.
50D2
DEFB 45H, 4CH, 4DH, 8FH 45 4C 4D 8F
Entry 48, "Protection has cleared memory". Word numbers 69 (protection), 76 (has), 77 (cleared), 15 (memory). Selected for error value 90H, BASIC error code 72.

50D6H - Data: Word Dictionary

Seventy-seven English words plus a leading marker byte, 50D6H to 5281H, and the last data in the file. Each word is stored once in plain ASCII with bit 7 set on its final character, and messages are built by naming the words rather than by repeating them: "out" and "of" are each stored once and used by four messages, "file" by nine. Case is stored as written, so GOSUB, RESUME and FIELD keep their capitals while everything else is lowercase and gets its initial capital from the RES 5,(HL) at 4E68H.

50D6
DEFB D2H D2
Word number 0, a single byte with bit 7 set and no text. It exists only so that the word-skipping loop at 4EA4H starts on a complete word boundary, which makes word number 1 land on the first real word at 50D7H. The value D2H is simply an end-of-word marker and is never copied to the buffer.
50D7
DEFM 'next'+80H 6E 65 78 F4
Word number 1, "next". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
50DB
DEFM 'without'+80H 77 69 74 68 6F 75 F4
Word number 2, "without". Bit 7 is set on the final character to mark the end of the word. Used by 3 messages.
50E2
DEFM 'for'+80H 66 6F F2
Word number 3, "for". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
50E5
DEFM 'syntax'+80H 73 79 6E 74 61 F8
Word number 4, "syntax". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
50EB
DEFM 'error'+80H 65 72 72 6F F2
Word number 5, "error". Bit 7 is set on the final character to mark the end of the word. Used by 7 messages.
50F0
DEFM 'return'+80H 72 65 74 75 72 EE
Word number 6, "return". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
50F6
DEFM 'GOSUB'+80H 47 4F 53 55 C2
Word number 7, "GOSUB". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
50FB
DEFM 'out'+80H 6F 75 F4
Word number 8, "out". Bit 7 is set on the final character to mark the end of the word. Used by 4 messages.
50FE
DEFM 'of'+80H 6F E6
Word number 9, "of". Bit 7 is set on the final character to mark the end of the word. Used by 4 messages.
5100
DEFM 'data'+80H 64 61 74 E1
Word number 10, "data". Bit 7 is set on the final character to mark the end of the word. Used by 2 messages.
5104
DEFM 'illegal'+80H 69 6C 6C 65 67 61 EC
Word number 11, "illegal". Bit 7 is set on the final character to mark the end of the word. Used by 2 messages.
510B
DEFM 'function'+80H 66 75 6E 63 74 69 6F EE
Word number 12, "function". Bit 7 is set on the final character to mark the end of the word. Used by 2 messages.
5113
DEFM 'call'+80H 63 61 6C EC
Word number 13, "call". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5117
DEFM 'overflow'+80H 6F 76 65 72 66 6C 6F F7
Word number 14, "overflow". Bit 7 is set on the final character to mark the end of the word. Used by 2 messages.
511F
DEFM 'memory'+80H 6D 65 6D 6F 72 F9
Word number 15, "memory". Bit 7 is set on the final character to mark the end of the word. Used by 2 messages.
5125
DEFM 'undefined'+80H 75 6E 64 65 66 69 6E 65 E4
Word number 16, "undefined". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
512E
DEFM 'line'+80H 6C 69 6E E5
Word number 17, "line". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5132
DEFM 'number'+80H 6E 75 6D 62 65 F2
Word number 18, "number". Bit 7 is set on the final character to mark the end of the word. Used by 3 messages.
5138
DEFM 'subscript'+80H 73 75 62 73 63 72 69 70 F4
Word number 19, "subscript". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5141
DEFM 'range'+80H 72 61 6E 67 E5
Word number 20, "range". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5146
DEFM 'redimensioned'+80H 72 65 64 69 6D 65 6E 73 69 6F 6E 65 E4
Word number 21, "redimensioned". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5153
DEFM 'array'+80H 61 72 72 61 F9
Word number 22, "array". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5158
DEFM 'division'+80H 64 69 76 69 73 69 6F EE
Word number 23, "division". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5160
DEFM 'by'+80H 62 F9
Word number 24, "by". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5162
DEFM 'zero'+80H 7A 65 72 EF
Word number 25, "zero". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5166
DEFM 'direct'+80H 64 69 72 65 63 F4
Word number 26, "direct". Bit 7 is set on the final character to mark the end of the word. Used by 2 messages.
516C
DEFM 'type'+80H 74 79 70 E5
Word number 27, "type". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5170
DEFM 'mismatch'+80H 6D 69 73 6D 61 74 63 E8
Word number 28, "mismatch". Bit 7 is set on the final character to mark the end of the word. Used by 2 messages.
5178
DEFM 'string'+80H 73 74 72 69 6E E7
Word number 29, "string". Bit 7 is set on the final character to mark the end of the word. Used by 3 messages.
517E
DEFM 'space'+80H 73 70 61 63 E5
Word number 30, "space". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5183
DEFM 'too'+80H 74 6F EF
Word number 31, "too". Bit 7 is set on the final character to mark the end of the word. Used by 3 messages.
5186
DEFM 'long'+80H 6C 6F 6E E7
Word number 32, "long". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
518A
DEFM 'formula'+80H 66 6F 72 6D 75 6C E1
Word number 33, "formula". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5191
DEFM 'complex'+80H 63 6F 6D 70 6C 65 F8
Word number 34, "complex". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5198
DEFM 'can't'+80H 63 61 6E 27 F4
Word number 35, "can't". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
519D
DEFM 'continue'+80H 63 6F 6E 74 69 6E 75 E5
Word number 36, "continue". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
51A5
DEFM 'no'+80H 6E EF
Word number 37, "no". Bit 7 is set on the final character to mark the end of the word. Used by 2 messages.
51A7
DEFM 'RESUME'+80H 52 45 53 55 4D C5
Word number 38, "RESUME". Bit 7 is set on the final character to mark the end of the word. Used by 2 messages.
51AD
DEFM 'unprintable'+80H 75 6E 70 72 69 6E 74 61 62 6C E5
Word number 39, "unprintable". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
51B8
DEFM 'missing'+80H 6D 69 73 73 69 6E E7
Word number 40, "missing". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
51BF
DEFM 'operand'+80H 6F 70 65 72 61 6E E4
Word number 41, "operand". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
51C6
DEFM 'bad'+80H 62 61 E4
Word number 42, "bad". Bit 7 is set on the final character to mark the end of the word. Used by 5 messages.
51C9
DEFM 'cassette'+80H 63 61 73 73 65 74 74 E5
Word number 43, "cassette". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
51D1
DEFM '?'+80H BF
Word number 44, "?". Bit 7 is set on the final character to mark the end of the word. Used by 4 messages.
51D2
DEFM 'undefined'+80H 75 6E 64 65 66 69 6E 65 E4
Word number 45, "undefined". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
51DB
DEFM 'user'+80H 75 73 65 F2
Word number 46, "user". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
51DF
DEFM 'FIELD'+80H 46 49 45 4C C4
Word number 47, "FIELD". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
51E4
DEFM 'internal'+80H 69 6E 74 65 72 6E 61 EC
Word number 48, "internal". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
51EC
DEFM 'file'+80H 66 69 6C E5
Word number 49, "file". Bit 7 is set on the final character to mark the end of the word. Used by 9 messages.
51F0
DEFM 'not'+80H 6E 6F F4
Word number 50, "not". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
51F3
DEFM 'found'+80H 66 6F 75 6E E4
Word number 51, "found". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
51F8
DEFM 'mode'+80H 6D 6F 64 E5
Word number 52, "mode". Bit 7 is set on the final character to mark the end of the word. Used by 2 messages.
51FC
DEFM 'already'+80H 61 6C 72 65 61 64 F9
Word number 53, "already". Bit 7 is set on the final character to mark the end of the word. Used by 2 messages.
5203
DEFM 'open'+80H 6F 70 65 EE
Word number 54, "open". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5207
DEFM 'disk'+80H 64 69 73 EB
Word number 55, "disk". Bit 7 is set on the final character to mark the end of the word. Used by 3 messages.
520B
DEFM 'I/O'+80H 49 2F CF
Word number 56, "I/O". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
520E
DEFM 'exists'+80H 65 78 69 73 74 F3
Word number 57, "exists". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5214
DEFM 'full'+80H 66 75 6C EC
Word number 58, "full". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5218
DEFM 'input'+80H 69 6E 70 75 F4
Word number 59, "input". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
521D
DEFM 'past'+80H 70 61 73 F4
Word number 60, "past". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5221
DEFM 'end'+80H 65 6E E4
Word number 61, "end". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5224
DEFM 'record'+80H 72 65 63 6F 72 E4
Word number 62, "record". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
522A
DEFM 'name'+80H 6E 61 6D E5
Word number 63, "name". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
522E
DEFM 'statement'+80H 73 74 61 74 65 6D 65 6E F4
Word number 64, "statement". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5237
DEFM 'in'+80H 69 EE
Word number 65, "in". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5239
DEFM 'many'+80H 6D 61 6E F9
Word number 66, "many". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
523D
DEFM 'files'+80H 66 69 6C 65 F3
Word number 67, "files". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5242
DEFM 'write'+80H 77 72 69 74 E5
Word number 68, "write". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5247
DEFM 'protection'+80H 70 72 6F 74 65 63 74 69 6F EE
Word number 69, "protection". Bit 7 is set on the final character to mark the end of the word. Used by 2 messages.
5251
DEFM 'access'+80H 61 63 63 65 73 F3
Word number 70, "access". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5257
DEFM 'denied'+80H 64 65 6E 69 65 E4
Word number 71, "denied". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
525D
DEFM 'blocked'+80H 62 6C 6F 63 6B 65 E4
Word number 72, "blocked". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5264
DEFM 'system'+80H 73 79 73 74 65 ED
Word number 73, "system". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
526A
DEFM 'command'+80H 63 6F 6D 6D 61 6E E4
Word number 74, "command". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5271
DEFM 'aborted'+80H 61 62 6F 72 74 65 E4
Word number 75, "aborted". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
5278
DEFM 'has'+80H 68 61 F3
Word number 76, "has". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.
527B
DEFM 'cleared'+80H 63 6C 65 61 72 65 E4
Word number 77, "cleared". Bit 7 is set on the final character to mark the end of the word. Used by 1 message.