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

Page Customization

SYS21/SYS – CMD"O" Array Sort Command

SYS21 implements the CMD"O" array sort command for NEWDOS/80 v2.0 Disk BASIC on the TRS-80 Model III. This overlay provides a general-purpose in-memory sort that operates directly on BASIC array data, supporting both direct sorting (where array elements are physically rearranged) and indirect sorting (where only an integer index array is rearranged, leaving the source arrays untouched).

The sort supports up to 9 arrays as sort keys, with each key independently configurable for ascending or descending order. String arrays support optional substring field specification (x,y) to compare only a portion of each element. Numeric arrays support integer (2-byte sign-magnitude), single-precision (4-byte), and double-precision (8-byte) comparisons. The algorithm is a bubble sort variant that makes repeated passes through the data, comparing adjacent elements and swapping when out of order, until no more swaps are needed.

The overlay is loaded at 4D00H and entered via SVC 57H (RST 28H with function code 57H). On entry, Register A contains the SVC function code and Register B holds the caller's context byte. The first two instructions (INC SP / INC SP) discard the return address pushed by the RST 28H dispatcher, since SYS21 exits through the DOS error exit or by returning to BASIC directly.

Program Structure

The overlay is organized into the following functional sections:

Command Parser and Variable Table Builder

4D00H–4DB9H: Validates the SVC function code, parses the CMD"O" argument list, extracts array variable descriptors (type, address, element count, substring parameters), and builds the 9-entry array descriptor table at 4300H–43D0H. Handles both direct and indirect sort mode detection via the * prefix on the first array argument.

Array Descriptor Table Initialization

4DBAH–4DE1H: Clears the SVC result byte at 4288H and the BASIC/CMD communication byte at 576EH, then initializes all 9 array descriptor table entries at 4300H with sequential element counters and default values.

Sort Element Count and Array Validation

4DE2H–4ED4H: Parses the element count parameter n, evaluates each array variable expression, locates the array in the BASIC variable table, computes element addresses and REN offsets, validates that all arrays have sufficient elements, and stores substring field parameters for string arrays.

Indirect Sort Index Initialization

4ED5H–4F00H: For indirect sorts (Format 2), validates that the indirect array is an integer type, then fills it with sequential REN values (0, 1, 2, ..., n-1) corresponding to the elements in the sort arrays.

Sort Workspace Setup and Main Sort Loop

4F01H–5077H: Allocates workspace memory via the division routine at 5D56H, relocates the comparison and swap routines to the workspace area, and executes the bubble sort outer/inner loops. Each pass compares adjacent elements across all sort key levels, swapping when out of order. The outer loop repeats until a complete pass produces no swaps.

Comparison Engine

4F7CH–5039H: Dispatches to the appropriate comparison routine based on variable type: integer (sign-magnitude XOR 80H for unsigned comparison), string (with optional substring field extraction and length-aware byte-by-byte comparison), or floating-point (byte-by-byte from most significant to least significant).

Swap Dispatch and Element Swap Routines

503BH–51D4H: When a comparison determines elements are out of order, the swap routines exchange the elements in all participating arrays. For direct sorts, the actual array data is moved. For indirect sorts, only the integer index values are exchanged.

Utility Subroutines

50D7H–50EEH: Helper routines for element address calculation, block copy with boundary checking, and the array-walking dispatch loop that applies an operation to all active array descriptors.

NOP Padding

51E2H–51E7H: Unused area filled with 00H bytes to pad the overlay to its full allocated size.

Array Descriptor Table (4300H–43D0H)

The sort uses a 9-entry descriptor table at 4300H, with each entry occupying 17H (23) bytes. The table is indexed via the IX register, which is advanced by 17H to step from one entry to the next. Only entries with a non-zero type byte at IX+01H are active.

Offset SizeNameDescription
IX+00H1Entry CounterSequential entry number (1–9) assigned during initialization. Also used as the array slot identifier during parsing. Entry 1 is the first (or indirect) array; entry 2+ are sort key arrays.
IX+01H1Variable TypeBASIC variable type code from ROM (40AFH): 02H=integer, 03H=string, 04H=single-precision, 08H=double-precision. Zero means the entry is unused/inactive.
IX+02H1Substring StartFor string arrays: the 1-based starting character position for substring comparison (the x in (x,y)). Zero means compare from the beginning.
IX+03H1Substring LengthFor string arrays: the number of characters to compare (the y in (x,y)). Zero means compare all characters. FFH is the initialization sentinel.
IX+04H1Sort DirectionBit 7: set = descending sort order, clear = ascending sort order. Set by the - prefix on the array variable name via the 65B4H routine.
IX+05H-
IX+06H
2Array Base AddressStarting address of the first participating element within the BASIC array data area. Computed from the array's location in the variable table plus the starting REN offset.
IX+07H-
IX+08H
2Array Element AddressAddress of the array descriptor entry in the BASIC variable table. Used to compute element sizes and total element counts.
IX+09H-
IX+0AH
2Current Element 1 PointerWorking pointer to the first element of the current comparison pair during the sort. Updated during each sort pass.
IX+0BH-
IX+0CH
2Swap Source AddressUsed during element swaps to track the source block address.
IX+0DH-
IX+0EH
2Element SizeSize of each array element in bytes. For integer=2, string=3 (descriptor), single=4, double=8. Retrieved from the BASIC array header.
IX+0FH-
IX+10H
2Element OffsetByte offset of the starting element within the array data, relative to the array base. Used to compute absolute element addresses.
IX+11H-
IX+12H
2Current Element 2 PointerWorking pointer to the second element of the current comparison pair during the sort.
IX+13H-
IX+14H
2Swap Temp PointerAddress used during element swap operations for temporary storage or the destination of a block move.
IX+15H-
IX+16H
2Swap Destination AddressDestination address for element data during swap operations.

SYS21 - Variables and Self-Modifying Code

AddressDescription
4288HExternal Write
SVC result byte. Cleared to 00H at 4DBBH.
4300HTable
Array descriptor table base (9 entries × 23 bytes = 207 bytes, 4300H–43CEH, plus 2 sentinel bytes).
4D8DHSelf-Modifying
Operand of JR NZ at 4D8DH. Written at 4D0FH with Register B (caller context). Controls the variable table compaction branch.
4F27HVariable
Element count n (the number of elements participating in the sort). Written at 4DEBH, 4EA4H.
4F39HSelf-Modifying
Operand of LD HL at 4F38H. Holds the current pass swap counter. Written at 4F23H.
4F40HSelf-Modifying
Operand of LD DE at 4F3FH. Holds the second pass swap counter. Written at 4F29H.
5088HVariable
Workspace base address. Written at 4F09H.
5102HVariable
Indirect sort flag. 00H=direct sort, 01H=indirect sort. Written at 4E04H.
5124HSelf-Modifying
Second-direction pass counter. Written at 4F3BH, 506AH.
5145HSelf-Modifying
First-direction pass counter. Written at 4F53H, 5047H.
507EHSelf-Modifying
Partition boundary for pivot tracking. Written at 4F19H, 5081H, 508AH.
50FFHSelf-Modifying
Operand of CALL at 50FEH. Written at 50EEH. Holds the address of the per-array operation routine (comparison, swap, or address setup).
576EHExternal Write
BASIC/CMD SVC result byte. Cleared to 00H at 4DBEH.
6427HExternal Write
Reset vector. Written at 4D15H with 4D67H (the variable table compaction entry point).

Major Routine Reference

Entry points, subroutines, and dispatch targets within SYS21/SYS.

AddressDescription
4D00HSVC Entry Point
Entry from SVC 57H dispatcher. Validates function code 37H (CMD"O") or 57H (CMD"O" with indirect), discards return address, parses argument list.
4D67HVariable Table Compaction
Reset vector target. Walks the BASIC variable table (40F9H–40FBH) and string descriptor stack (40FDH), compacting entries by removing gaps left by array data movement.
4DBAHCMD"O" Main Entry
Initializes SVC result bytes, builds the 9-entry array descriptor table at 4300H, then parses the element count and array arguments.
4DEFHArray Argument Loop
Parses each comma-separated array variable in the CMD"O" argument list, evaluates the expression, locates the array, fills the descriptor table entry, and advances IX to the next entry.
4F01HSort Setup
Allocates workspace, relocates sort routines, initializes pass counters.
4F32HSort Outer Loop
Begins each pass of the bubble sort. Resets swap counters and enters the inner comparison loop.
4F63HSort Inner Loop
Walks IX through all active array descriptors, comparing element pairs and dispatching to swap routines when elements are out of order.
4F7CHComparison Dispatcher
Reads the variable type from IX+01H and dispatches to integer, string, or floating-point comparison.
50D7HElement Address Helper
Computes element addresses with boundary checking.
50EEHArray Walker Dispatch
Iterates through all active descriptors in the table, calling the routine whose address is stored at 50FFH for each one.
510CHElement Address Setup
Stores element pointer values into IX+11H/12H and IX+13H/14H for a given descriptor entry, then jumps to 65FFH for additional computation.
511DHREN-to-Address Converter
Converts a Relative Element Number to an absolute memory address using the element size from IX+0FH/10H and the base address from IX+05H/06H.
5153HForward Swap
Swaps element data in the forward (ascending) direction for all active arrays.
5174HReverse Swap
Swaps element data in the reverse (descending) direction for all active arrays.
5184HIndirect Index Update
For indirect sorts, updates the integer index array entries to reflect the swap.
51D5HPass Advance
Advances element pointers (IX+07H/08H) to the next pair for the next comparison iteration.

Cross-References to External Routines

AddressSourceDescription
1997HROMBASIC syntax error handler (?SN ERROR)
1E46HROMBASIC numeric expression evaluator (integer result in DE)
1E4AHROM"Illegal Function Call" error handler
260DHROMBASIC string expression evaluator
2B1CHROMNumeric expression evaluator (byte result in A)
5BB4HBASIC/CMDVariable type parser (returns type code)
5BB6HBASIC/CMDVariable type parser (alternate entry)
5BFAHBASIC/CMDLine type check (classifies current BASIC text position)
5C0DHBASIC/CMDLine entry length calculator
5C18HBASIC/CMDLine link follower (advance to next variable table entry)
5C21HBASIC/CMDParenthesis counter
5D56HBASIC/CMD16-bit division (HL = HL / DE, remainder in DE)
5D78HBASIC/CMDLine+Variable evaluator
5DEFHBASIC/CMDError translator (DOS error code in A)
65B4HBASIC/CMDSet bit 7 of IX+04H (mark descending sort)
65B9HBASIC/CMDPDRIVE comparator (validate array base addresses)
65EBHBASIC/CMDConditional store to 4F02H
65F4HBASIC/CMDConditional return + position check
65FFHBASIC/CMDConditional add loop
6497HBASIC/CMDSet reset vector
6608HBASIC/CMDFCB header + block copy

Disassembly

4D00H – SVC Entry Point and Function Code Validation

Entry point from the SVC 57H dispatcher. The first two instructions discard the return address that the RST 28H SVC mechanism pushed onto the stack, since SYS21 exits through BASIC's error handlers or the DOS exit rather than returning to the SVC dispatcher. Register A holds the SVC function code and Register B holds the caller's context byte passed through the SVC dispatch chain.

4D00
INC SP 33
INCrement the Stack Pointer by 1, discarding the low byte of the return address that the RST 28H SVC dispatcher pushed onto the stack.
4D01
INC SP 33
INCrement the Stack Pointer by 1 again, discarding the high byte of the return address. The stack now points to the caller's context frame above the SVC dispatch layer.
4D02
CP 37H FE 37
Compare Register A (the SVC function code) against 37H. SVC function code 37H corresponds to the CMD"O" command entry point. If Register A equals 37H, the Z FLAG is set; otherwise the NZ FLAG is set.
4D04
If the Z FLAG has been set (the SVC function code is 37H, meaning this is a CMD"O" sort command), JUMP to 4DBAH to initialize the array descriptor table and begin parsing the command arguments.
4D07
CP 57H FE 57
Compare Register A (the SVC function code) against 57H. SVC function code 57H is an alternate entry for CMD"O" used during the variable table compaction and cleanup phase. If Register A equals 57H, the Z FLAG is set.
4D09
LD A,2AH 3E 2A
Load Register A with 2AH (decimal 42, the DOS error code for a general error). This pre-loads the error code in case the function code validation fails at the next instruction.
4D0B
If the NZ FLAG has been set (the SVC function code is neither 37H nor 57H, meaning an unrecognized function code was passed), JUMP to 5DEFH, the Error translator in BASIC/CMD, with error code 2AH (general error) in Register A. This is a dead-end error exit.

At this point, the SVC function code has been validated as 57H — the variable table compaction entry. This path is taken when the reset vector at 6427H fires during or after the sort, triggering a cleanup pass through the BASIC variable and string descriptor tables. Register B holds the caller's context byte from the SVC dispatch chain.

4D0E
LD A,B 78
Load Register A with the value in Register B (the caller's context byte from the SVC dispatch chain). This byte controls the compaction branch at 4D8DH.
4D0F
LD (4D8DH),A 32 8D 4D
Store Register A (the caller's context byte) to memory location 4D8DH. [SELF-MODIFYING CODE
This overwrites the operand byte of the JR NZ instruction at 4D8CH, controlling whether the compaction routine at 4D8FH takes the short path (skip LDIR) or the full copy path. The initial value at 4D8DH is 02H (from the disassembly), but it is overwritten here with the runtime context byte.
4D12
LD BC,4D67H 01 67 4D
Point Register Pair BC to 4D67H, the address of the variable table compaction routine within this overlay. This address will be stored as the reset vector.
4D15
LD (6427H),BC ED 43 27 64
Store Register Pair BC (4D67H, the compaction routine address) to memory location 6427H, the reset vector in BASIC/CMD. This ensures that if an error or BREAK occurs during the sort, the variable table compaction routine will be invoked to clean up any partially-modified variable table entries.

4D19H – CMD"O" Argument Pre-Scanner

Before the main argument parsing begins, this section pre-scans the CMD"O" argument list to process any variable names and line references that precede the array variables. HL points to the current position in the BASIC program text. The pre-scanner handles the CMD"O",n,... portion where n is the element count and the comma-separated list may contain line references (via 5D78H) and NEXT tokens (93H).

4D19
DEC HL 2B
DECrement Register Pair HL by 1, backing up the BASIC text pointer by one byte so that RST 10H will re-fetch the current character. This is necessary because the SVC dispatcher may have advanced HL past the current token.
4D1A
RST 10H D7
Fetch the next non-space character from the BASIC program text at (HL) into Register A, advancing HL past any spaces. The CARRY FLAG is set if the character is a digit (30H–39H).
4D1B
If the Z FLAG has been set (the character at the current BASIC text position is a null byte 00H or end-of-statement marker, meaning there are no more arguments), JUMP forward to 4D62H to finalize the argument list and enter the variable table compaction phase.

OUTER LOOP START - Argument Scanner

4D1D
RST 08H ⇒ 2CH CF 2C
Call the RST 08H inline character match routine, comparing the current BASIC text character against 2CH (ASCII comma ,). If the character is a comma, execution continues at the next instruction; otherwise a syntax error is raised.
4D1F
If the NZ FLAG has been set (the character was not a comma — RST 08H sets NZ when the match fails but does not always raise an error), JUMP forward to 4D2BH to process the current argument as an array variable expression rather than a line reference.

INNER LOOP START — Line Reference Scanner
This loop processes comma-separated line references and NEXT tokens before the array variable arguments. Each iteration calls 5D78H to evaluate a line+variable reference, then checks for end-of-statement or another comma/NEXT token.

4D21
GOSUB to 5D78H, the Line+Variable evaluator in BASIC/CMD. This routine evaluates the expression at the current BASIC text position (HL) as a combined line number and variable reference, returning with HL advanced past the expression.
4D24
RST 10H D7
Fetch the next non-space character from the BASIC program text at (HL) into Register A.
4D25
If the Z FLAG has been set (the character is 00H, end-of-statement), JUMP back to 4D21H to process the next line reference. This handles the case where multiple line references are separated without explicit delimiters. [LOOP]
4D27
CP 93H FE 93
Compare Register A against 93H, the BASIC token for the NEXT keyword. If Register A equals 93H, the Z FLAG is set.
4D29
If the Z FLAG has been set (the current token is NEXT), JUMP back to 4D21H to continue the pre-scan loop, processing the NEXT keyword as part of the argument list context.

INNER LOOP END — Line Reference Scanner

4D2BH – Array Variable Locator

This section locates an array variable in the BASIC variable table. It calls the variable type parser at 5BB6H to identify the variable, then searches through the BASIC variable table (from 40FBH, the array end / variable top) and the string descriptor stack (from 40FDH) to find the matching array entry. The search walks backward through the linked variable entries, checking each one against the target variable name and type.

4D2B
GOSUB to 5BB6H, the Variable type parser (alternate entry) in BASIC/CMD. This routine parses the variable name and type suffix at the current BASIC text position (HL), returning the variable type code in the ROM variable at 40AFH and setting the Z FLAG if the variable is an array type, or NZ if it is a simple variable.
4D2E
LD DE,(40FBH) ED 5B FB 40
Fetch the array end pointer from memory location 40FBH (the top of the BASIC array/variable area) into Register Pair DE. This address marks the upper boundary of the variable table search.
4D32
If the NZ FLAG has been set (the variable parsed by 5BB6H is not an array type — it is a simple scalar variable), JUMP forward to 4D4CH to search the simple variable area instead of the array area.

The variable is an array type. Search the array variable area, which lies between 40F9H (string space start / array base) and 40FBH (array end). The search starts from 40FBH and walks backward through linked entries.

4D34
GOSUB to 5C21H, the Parenthesis counter in BASIC/CMD. This routine counts and validates the parenthesized subscript expression following the array variable name (e.g., the (0) in NM$(0)). Returns with CARRY set if a syntax error is detected.
4D37
If the CARRY FLAG has been set (a syntax error was detected in the parenthesized subscript expression), JUMP to the ROM BASIC syntax error handler at 1997H (?SN ERROR). This is a dead-end error exit.
4D3A
PUSH HL E5
Save Register Pair HL (the current BASIC text pointer, now positioned past the subscript expression) onto the stack for later restoration.
4D3B
LD HL,(40FDH) 2A FD 40
Fetch the string descriptor stack pointer from memory location 40FDH into Register Pair HL. This is the lower boundary of the array variable search area.
4D3E
EX DE,HL EB
Exchange Register Pairs DE and HL. Now HL holds the array end pointer (from 40FBH, loaded at 4D2EH) and DE holds the string descriptor stack pointer (from 40FDH). The search will walk from HL downward toward DE.

INNER LOOP START — Array Variable Search This loop walks through the BASIC variable table entries, comparing each entry against the target variable. HL points to the current entry being examined, DE points to the lower boundary.

4D3F
RST 18H DF
Compare Register Pair HL against Register Pair DE (16-bit comparison). The CARRY FLAG is set if HL < DE, the Z FLAG is set if HL equals DE. This checks whether the search has reached or passed the lower boundary of the variable table.
4D40
If the Z FLAG has been set (HL equals DE, meaning the search has exhausted the entire array variable area without finding a match), JUMP forward to 4D5DH to restore HL and return to the argument parser to continue with the next argument.
4D42
GOSUB to 5BFAH, the Line type check routine in BASIC/CMD. This routine examines the variable table entry at (HL) and determines whether it matches the target variable by name and type. Returns Z if the entry matches, NZ if it does not.
4D45
If the Z FLAG has been set (the current variable table entry matches the target array variable), JUMP forward to 4D5BH to mark this entry (SET bit 4) and continue processing.
4D47
GOSUB to 5C18H, the Line link follower in BASIC/CMD. This routine advances HL to the next variable table entry by following the link pointer at the current entry. HL is updated to point to the next entry in the chain.
4D4A
JUMP back unconditionally to 4D3FH to compare the next variable table entry against the target.

INNER LOOP END - Array Variable Search

4D4CH – Simple Variable Search Path

This section handles the case where the parsed variable is a simple (non-array) type. It searches the simple variable area between 40F9H (string space start) and 40FBH (array end), walking through entries and comparing each one against the target variable.

4D4C
PUSH HL E5
Save Register Pair HL (the current BASIC text pointer) onto the stack for later restoration.
4D4D
LD HL,(40F9H) 2A F9 40
Fetch the string space start / simple variable area base from memory location 40F9H into Register Pair HL. This is the lower boundary of the simple variable search.

INNER LOOP START — Simple Variable Search This loop mirrors the array search at 4D3FH but searches the simple variable area. DE holds 40FBH (from 4D2EH), the upper boundary.

4D50
RST 18H DF
Compare Register Pair HL against Register Pair DE (the array end pointer at 40FBH). The Z FLAG is set if HL equals DE (search exhausted).
4D51
If the Z FLAG has been set (the search has exhausted the simple variable area without finding a match), JUMP forward to 4D5DH to restore HL and continue parsing.
4D53
GOSUB to 5BFAH, the Line type check routine, to compare the current variable table entry at (HL) against the target variable name and type.
4D56
If the NZ FLAG has been set (the current entry does not match the target variable), GOSUB to 5C0DH, the Line entry length calculator in BASIC/CMD, to compute the size of the current entry so HL can be advanced past it.
4D59
If the NZ FLAG has been set (the entry did not match and HL has been advanced past it), JUMP back to 4D50H to continue searching the next entry.

INNER LOOP END — Simple Variable Search

4D5B
SET 4,(HL) CB E6
SET bit 4 of the byte at (HL), which is the first byte of the matched variable table entry. Bit 4 serves as a flag indicating that this variable has been claimed by the CMD"O" sort. This prevents the same variable from being matched twice during subsequent searches.
4D5D
POP HL E1
Restore Register Pair HL from the stack, recovering the BASIC text pointer that was saved at 4D3AH or 4D4CH.
4D5E
DEC HL 2B
DECrement Register Pair HL by 1, backing up the BASIC text pointer by one byte so that RST 10H will re-read the current character position.
4D5F
RST 10H D7
Fetch the next non-space character from the BASIC program text at (HL) into Register A.
4D60
If the NZ FLAG has been set (there are more characters in the argument list — the current character is not 00H / end-of-statement), JUMP back to 4D1DH to check for a comma and process the next argument.

OUTER LOOP END - Argument Scanner

4D62H – Variable Table Compaction Entry

This section is reached when the pre-scanner has exhausted all arguments (end-of-statement detected). It pushes the cleanup return address (586EH) and the compaction routine entry (4D67H) onto the stack, then falls through to the compaction code. The address 586EH is a routine in BASIC/CMD that serves as the post-sort cleanup handler, likely the single-digit parser or a nearby return point.

4D62
PUSH HL E5
Save Register Pair HL (the BASIC text pointer at end-of-statement) onto the stack. This will be restored by the cleanup handler after compaction completes.
4D63
LD HL,586EH 21 6E 58
Point Register Pair HL to 586EH, the address of the post-sort cleanup handler in BASIC/CMD (the single-digit parser entry point at 5870H minus 2, which is a return dispatch point). This address will be used as the return address after compaction.
4D66
PUSH HL E5
Save Register Pair HL (586EH, the cleanup return address) onto the stack. When the compaction routine at 4D67H executes its RET, it will return to 586EH.

4D67H – Variable Table Compaction Routine

This routine compacts the BASIC variable table by walking through all entries and removing any gaps created during the sort operation. It processes the array variable area (40F9H–40FBH) and the string descriptor stack (40FDH), moving entries downward to close gaps and updating the area boundary pointers. This routine is also installed as the reset vector at 6427H so that it is automatically invoked if an error or BREAK interrupts the sort.

4D67
LD HL,(40F9H) 2A F9 40
Fetch the string space start / variable area base address from memory location 40F9H into Register Pair HL. This is the starting point for the compaction walk through the variable table.
4D6A
LD D,H 54
Copy Register H to Register D, setting the high byte of DE equal to the high byte of HL. DE will track the destination (write) pointer during compaction, while HL tracks the source (read) pointer.
4D6B
LD E,L 5D
Copy Register L to Register E, completing the DE = HL copy. Now both HL (source) and DE (destination) point to the start of the variable area.
4D6C
PUSH DE D5
Save Register Pair DE (the destination pointer, initially equal to HL) onto the stack. This saved value will be used to compute the compaction delta later.
4D6D
LD DE,(40FBH) ED 5B FB 40
Fetch the array end / variable top pointer from memory location 40FBH into Register Pair DE. This marks the upper boundary of the variable area to be compacted.
4D71
RST 18H DF
Compare Register Pair HL against Register Pair DE (16-bit comparison). The Z FLAG is set if HL equals DE (all entries have been processed).
4D72
POP DE D1
Restore Register Pair DE from the stack, recovering the destination pointer saved at 4D6CH.
4D73
If the Z FLAG has been set (HL has reached the variable area upper boundary, meaning all entries in the array/variable area have been processed), JUMP forward to 4D94H to update the 40FBH pointer and continue with the string descriptor stack.

LOOP START — Variable Entry Compaction
This loop processes each variable table entry. For each entry, it reads the entry header to determine its size, checks bit 7 and bit 4 flags, and either skips the entry (if marked for removal) or copies it to the destination pointer (compacting it down).

4D75
LD BC,4D6CH 01 6C 4D
Point Register Pair BC to 4D6CH, the loop re-entry address. This address is pushed onto the stack so that the processing subroutine can RET back to the loop.
4D78
PUSH BC C5
Save Register Pair BC (4D6CH, the loop continuation address) onto the stack. This sets up the return-to-loop mechanism: the subroutine called below will RET to 4D6CH.
4D79
LD A,(HL) 7E
Fetch the first byte of the current variable table entry at (HL) into Register A. This byte contains the entry's type/flag bits, including bit 7 (deletion mark) and bit 4 (CMD"O" claim flag set at 4D5BH).
4D7A
AND 0FH E6 0F
Mask Register A with 0FH, isolating the low nibble (bits 3–0) which contains the variable type code. The upper nibble flags (bit 7 and bit 4) are discarded for the size calculation.
4D7C
ADD 03H C6 03
ADD 03H (decimal 3) to Register A (the variable type code). The type code represents the element size in bytes: type 02H (integer) = 2, type 03H (string) = 3, type 04H (single) = 4, type 08H (double) = 8. Adding 3 accounts for the 3-byte entry header (name bytes + type byte), giving the total entry size in Register A.
4D7E
LD C,A 4F
Copy Register A (the total entry size in bytes) to Register C, setting up the low byte of the byte count for a potential LDIR block copy.
4D7F
LD B,00H 06 00
Load Register B with 00H, clearing the high byte of BC. Register Pair BC now holds the total entry size (3 + type code) as a 16-bit value for LDIR.
4D81
INC HL 23
INCrement Register Pair HL by 1, advancing past the first byte of the entry header to point to the second header byte.
4D82
INC HL 23
INCrement Register Pair HL by 1 again, advancing to the third header byte (byte +2 of the entry).
4D83
BIT 7,(HL) CB 7E
Test bit 7 of the byte at (HL), which is byte +2 of the variable table entry. Bit 7 is the deletion/active flag. If bit 7 is set, the entry is marked for deletion (or is an array header that should be compacted differently).
4D85
DEC HL 2B
DECrement Register Pair HL by 1, backing up to byte +1 of the entry.
4D86
DEC HL 2B
DECrement Register Pair HL by 1 again, restoring HL to the start of the entry (byte +0).
4D87
If the NZ FLAG has been set (bit 7 of byte +2 is set, meaning this entry is marked for deletion or requires a full copy), JUMP forward to 4D91H to execute the LDIR block copy.
4D89
BIT 4,(HL) CB 66
Test bit 4 of the byte at (HL), which is byte +0 of the variable table entry. Bit 4 is the CMD"O" claim flag set at 4D5BH during the pre-scan. If bit 4 is set, this entry was claimed by CMD"O".
4D8B
RES 4,(HL) CB A6
Clear bit 4 of the byte at (HL), removing the CMD"O" claim flag. This restores the entry to its original state regardless of whether it will be compacted or skipped.
4D8D
If the NZ FLAG has been set (bit 4 was set before it was cleared — meaning this entry was claimed by CMD"O"), JUMP forward to 4D91H to execute the LDIR copy. [SELF-MODIFYING CODE
The operand byte 02H at address 4D8DH+1 is overwritten at 4D0FH with the caller's context byte from Register B. The runtime value of this operand controls the branch displacement, potentially redirecting this JR to a different target depending on the context byte.
4D8F
ADD HL,BC 09
ADD Register Pair BC (the entry size) to Register Pair HL, advancing the source pointer past the current entry. This skips the entry without copying it — it is being removed from the compacted table.
4D90
RET C9
RETurn to the caller. The return address on the stack is 4D6CH (pushed at 4D78H), so execution continues at the top of the compaction loop.
4D91
LDIR ED B0
Execute the LDIR block copy instruction: copy BC bytes from (HL) to (DE), incrementing both HL and DE after each byte, and decrementing BC until it reaches zero. This copies the current variable table entry from its source position to the destination (compacted) position, closing any gap left by previously skipped entries.
4D93
RET C9
RETurn to the caller at 4D6CH (the loop continuation address pushed at 4D78H) to process the next variable table entry.

LOOP END — Variable Entry Compaction

4D94H – Update Variable Pointers and Process String Descriptors

After compacting the array/variable area, this section updates the 40FBH pointer to reflect the new (smaller) variable area size, then performs the same compaction on the string descriptor stack between 40FBH and 40FDH.

4D94
LD (40FBH),DE ED 53 FB 40
Store Register Pair DE (the current destination pointer after compacting all variable entries) to memory location 40FBH, updating the array end / variable top pointer to reflect the smaller compacted variable table.
4D98
PUSH DE D5
Save Register Pair DE (the updated 40FBH value, which is now the lower boundary for the string descriptor stack walk) onto the stack.
4D99
LD DE,(40FDH) ED 5B FD 40
Fetch the string descriptor stack pointer from memory location 40FDH into Register Pair DE. This marks the upper boundary of the string descriptor area to be compacted.
4D9D
RST 18H DF
Compare Register Pair HL (the current source pointer, which now points to the first string descriptor entry) against Register Pair DE (the string descriptor stack top at 40FDH). The Z FLAG is set if they are equal (no string descriptors to process).
4D9E
POP DE D1
Restore Register Pair DE from the stack, recovering the destination pointer (the updated 40FBH value).
4D9F
If the Z FLAG has been set (HL equals the string descriptor stack top, meaning there are no string descriptors to compact), JUMP forward to 4DB3H to update the 40FDH pointer and finalize.

LOOP START — String Descriptor Compaction
This loop processes each string descriptor entry. Each descriptor is 5 bytes: 3 bytes of name/flags, then a 2-byte length, then the string data. The loop reads the length to determine the full entry size.

4DA1
PUSH HL E5
Save Register Pair HL (the current source pointer within the string descriptor area) onto the stack.
4DA2
INC HL 23
INCrement HL by 1, advancing to byte +1 of the string descriptor entry.
4DA3
INC HL 23
INCrement HL by 1, advancing to byte +2.
4DA4
INC HL 23
INCrement HL by 1, advancing to byte +3, which is the low byte of the entry's data length.
4DA5
LD C,(HL) 4E
Fetch the low byte of the entry's data length from (HL) into Register C.
4DA6
INC HL 23
INCrement HL by 1, advancing to byte +4, which is the high byte of the entry's data length.
4DA7
LD B,(HL) 46
Fetch the high byte of the entry's data length from (HL) into Register B. Register Pair BC now holds the data length of this string descriptor entry.
4DA8
INC BC 03
INCrement Register Pair BC by 1, adding 1 to the data length to account for the first header/name byte.
4DA9
INC BC 03
INCrement Register Pair BC by 1 again, adding another byte for the second header byte.
4DAA
INC BC 03
INCrement Register Pair BC by 1, adding the third header byte.
4DAB
INC BC 03
INCrement Register Pair BC by 1, adding the length-low byte itself to the total.
4DAC
INC BC 03
INCrement Register Pair BC by 1, adding the length-high byte. Register Pair BC now holds the total entry size: 5 header/length bytes plus the data length.
4DAD
POP HL E1
Restore Register Pair HL from the stack, recovering the pointer to the start of the current string descriptor entry.
4DAE
GOSUB to 4D89H, the entry copy/skip handler. This routine checks bit 4 (the CMD"O" claim flag) and either copies the entry via LDIR (if claimed) or skips it by advancing HL past it. On return, HL and DE are updated.
4DB1
JUMP back unconditionally to 4D98H to continue processing the next string descriptor entry.

LOOP END — String Descriptor Compaction

4DB3
LD (40FDH),DE ED 53 FD 40
Store Register Pair DE (the updated destination pointer after compacting all string descriptor entries) to memory location 40FDH, updating the string descriptor stack pointer to reflect the compacted stack.
4DB7
JUMP to 6497H, the Set reset vector routine in BASIC/CMD. This routine restores the reset vector to its default state, cleaning up after the sort's custom reset handler. Execution then returns via the address on the stack (either 586EH for the normal post-sort cleanup, or the error handler if the sort was interrupted).

4DBAH – CMD"O" Array Descriptor Table Initialization

This is the main CMD"O" entry point reached when SVC function code 37H is detected at 4D04H. It clears the SVC result byte at 4288H and the BASIC/CMD communication byte at 576EH, then initializes the 9-entry array descriptor table at 4300H. Each entry is 17H (23) bytes: a 2-byte sequential counter (1–9), a 01H marker byte, an FFH sentinel byte, and 13H (19) bytes of zeroes. The table occupies 4300H–43CEH with two final sentinel bytes at 43CFH–43D0H.

4DBA
XOR A AF
Set Register A to 00H by XORing it with itself, clearing all flags. Register A = 00H.
4DBB
LD (4288H),A 32 88 42
Store Register A (00H) to memory location 4288H, clearing the SVC result byte. This byte is part of the DOS state area and is cleared to indicate no error from the CMD"O" operation.
4DBE
LD (576EH),A 32 6E 57
Store Register A (00H) to memory location 576EH, clearing the BASIC/CMD SVC result communication byte. This byte is within the FCB/Runtime Work Area and is used to pass status between the SVC handler and BASIC.
4DC1
PUSH HL E5
Save Register Pair HL (the current BASIC text pointer, positioned at the CMD"O" arguments) onto the stack. It will be restored at 4DE1H after the table is initialized.
4DC2
LD HL,4300H 21 00 43
Point Register Pair HL to 4300H, the base address of the array descriptor table. The initialization loop will fill 9 entries starting at this address.
4DC5
LD C,09H 0E 09
Load Register C with 09H (decimal 9), the number of array descriptor table entries to initialize. This is the outer loop counter.
4DC7
LD DE,0000H 11 00 00
Point Register Pair DE to 0000H. Register E will be used as the sequential entry counter (incremented from 0 to 9), and Register D (00H) is used to zero-fill the trailing bytes of each entry.

LOOP START — Table Entry Initialization
This loop initializes each of the 9 array descriptor table entries at 4300H. Each entry is 23 bytes: [counter_low][counter_high][01H][FFH][19 × 00H].

4DCA
INC E 1C
INCrement Register E by 1, advancing the sequential entry counter. On the first iteration, E goes from 00H to 01H (entry 1).
4DCB
LD (HL),E 73
Store Register E (the sequential entry counter, 01H–09H) to memory at (HL), writing the low byte of the counter to offset +0 of the current table entry (IX+00H).
4DCC
INC HL 23
INCrement Register Pair HL by 1, advancing to offset +1 of the entry.
4DCD
LD (HL),D 72
Store Register D (00H) to memory at (HL), writing 00H to offset +1 of the entry (IX+01H, the variable type field). This marks the entry as inactive/unused.
4DCE
INC HL 23
INCrement Register Pair HL by 1, advancing to offset +2 of the entry.
4DCF
LD (HL),01H 36 01
Store the immediate value 01H to memory at (HL), writing 01H to offset +2 of the entry (IX+02H, the substring start position). The default value 01H means "start at the first character" for string comparisons.
4DD1
INC HL 23
INCrement Register Pair HL by 1, advancing to offset +3 of the entry.
4DD2
LD (HL),FFH 36 FF
Store the immediate value FFH to memory at (HL), writing FFH to offset +3 of the entry (IX+03H, the substring length). The sentinel value FFH means "compare all characters" — no substring length restriction.
4DD4
INC HL 23
INCrement Register Pair HL by 1, advancing to offset +4 of the entry.
4DD5
LD B,13H 06 13
Load Register B with 13H (decimal 19), the number of remaining bytes to zero-fill in the current entry. Each entry has 23 bytes total: 4 have been written (counter, type, substr start, substr length), leaving 19 to fill with 00H.

INNER LOOP — Zero Fill
This inner loop writes 19 bytes of 00H to the remaining fields of the current table entry (offsets +4 through +22).

4DD7
LD (HL),D 72
Store Register D (00H) to memory at (HL), zero-filling the current byte of the entry.
4DD8
INC HL 23
INCrement Register Pair HL by 1, advancing to the next byte.
4DD9
DECrement Register B (the inner zero-fill counter) by 1 and JUMP back to 4DD7H if B is not zero. This loops 19 times, filling offsets +4 through +22 with 00H. [INNER LOOP END]
4DDB
DEC C 0D
DECrement Register C (the outer entry counter) by 1. When C reaches zero, all 9 entries have been initialized.
4DDC
If the NZ FLAG has been set (Register C is not zero, meaning there are more entries to initialize), JUMP back to 4DCAH to begin the next entry.

LOOP END — Table Entry Initialization

All 9 entries have been initialized. HL now points to 43CFH, one byte past the last entry. Write two final sentinel zero bytes.

4DDE
LD (HL),D 72
Store Register D (00H) to memory at (HL), writing a 00H sentinel byte at 43CFH (the first byte past the last table entry).
4DDF
INC HL 23
INCrement Register Pair HL by 1, advancing to 43D0H.
4DE0
LD (HL),D 72
Store Register D (00H) to memory at (HL), writing a second 00H sentinel byte at 43D0H. These two zero bytes terminate the descriptor table, ensuring that any loop walking the table will stop when it encounters a zero type byte.
4DE1
POP HL E1
Restore Register Pair HL from the stack, recovering the BASIC text pointer that was saved at 4DC1H. HL is now positioned at the CMD"O" arguments in the BASIC program text.

4DE2H – Element Count Parser

This section parses the first argument of the CMD"O" command: the element count n, which specifies how many elements from each array participate in the sort. The comma before n is consumed by RST 08H, and n is evaluated as a numeric expression via ROM 1E46H, returning the result in Register Pair DE. The count is stored at 4F27H. IX is then initialized to point to the first array descriptor table entry at 4300H.

4DE2
RST 08H ⇒ 2CH CF 2C
Call the RST 08H inline character match routine, comparing the current BASIC text character against 2CH (ASCII comma ,). The comma separates the CMD"O" keyword from the element count n. If the match fails, a syntax error is raised.
4DE4
GOSUB to ROM routine 1E46H, the BASIC numeric expression evaluator that returns an integer result in Register Pair DE. This evaluates the element count expression n from the BASIC program text. On return, DE contains the integer value of n and HL points past the expression in the BASIC text.
4DE7
LD IX,4300H DD 21 00 43
Point Index Register IX to 4300H, the base address of the array descriptor table. IX will be used throughout the following code to access the fields of the current table entry.
4DEB
LD (4F27H),DE ED 53 27 4F
Store Register Pair DE (the element count n) to memory location 4F27H. This variable holds the number of elements participating in the sort and is referenced later during sort loop setup and array bounds validation. A value of 0000H has special meaning: it will be replaced with the number of remaining elements in the first specified array.

4DEFH – Array Argument Processing Loop

This is the main loop that processes each comma-separated array variable argument in the CMD"O" statement. For each argument, it validates the entry counter, checks for the * prefix (indirect sort), checks for the - prefix (descending order), evaluates the array expression, locates the array in the variable table, computes element addresses and sizes, validates bounds, and optionally parses the substring field specification (x,y) for string arrays. The loop advances IX by 17H (23 bytes) for each argument and continues until end-of-statement.

4DEF
LD A,(IX+00H) DD 7E 00
Fetch the entry counter from IX+00H (the sequential entry number, 01H–09H) into Register A. This counter was written during table initialization at 4DCAH. It identifies which table slot is being processed.
4DF2
OR A B7
OR Register A with itself. This sets the Z FLAG if Register A is 00H (which would mean the table has been exhausted — all 9 entries have been processed and the sentinel byte has been reached).
4DF3
If the Z FLAG has been set (the entry counter is 00H, meaning more than 9 array arguments were specified, exceeding the maximum), JUMP to the ROM BASIC syntax error handler at 1997H (?SN ERROR). The CMD"O" command supports a maximum of 9 arrays.
4DF6
RST 08H ⇒ 2CH CF 2C
Call the RST 08H inline character match routine, comparing the current BASIC text character against 2CH (ASCII comma ,). Each array argument is preceded by a comma. If the comma is not found, a syntax error is raised.
4DF8
CP CFH FE CF
Compare Register A (the character following the comma, returned by RST 08H) against CFH, the BASIC token for the * operator. The * prefix indicates an indirect sort (Format 2) where the preceding array is the index array. If Register A equals CFH, the Z FLAG is set.
4DFA
If the NZ FLAG has been set (the character is not *, meaning this is not an indirect sort specification), JUMP forward to 4E09H to check for the descending-order prefix - instead.

The * prefix was found. This is an indirect sort (Format 2). The current entry (pointed to by IX) must be the first array (entry counter = 01H), and it must be an integer array. Store the indirect flag and advance past the * token.

4DFC
LD A,(IX+00H) DD 7E 00
Fetch the entry counter from IX+00H into Register A. This checks which table slot is currently being processed.
4DFF
CP 01H FE 01
Compare Register A (the entry counter) against 01H. The * indirect prefix is only valid on the first array argument (entry 1). If Register A does not equal 01H, the NZ FLAG is set.
4E01
If the NZ FLAG has been set (the * prefix appeared on an array other than the first one, which is a syntax error), JUMP to the ROM BASIC syntax error handler at 1997H (?SN ERROR).
4E04
LD (5102H),A 32 02 51
Store Register A (01H, since we verified the entry counter is 01H) to memory location 5102H, the indirect sort flag. A non-zero value at 5102H indicates that this is an indirect sort (Format 2). The value 01H serves as both the flag and the entry number.
4E07
JUMP forward unconditionally to 4E10H to advance past the token via RST 10H and continue with the array expression evaluation.

No * prefix. Check for the - prefix which indicates descending sort order for this array.

4E09
CP CEH FE CE
Compare Register A (the current BASIC text character) against CEH, the BASIC token for the - (minus/negate) operator. The - prefix before an array variable name means sort that array in descending order.
4E0B
If the NZ FLAG has been set (the character is not -, meaning ascending sort order for this array), JUMP forward to 4E11H to evaluate the array expression without setting the descending flag.
4E0D
GOSUB to 65B4H, the Set bit 7 of IX+04H routine in BASIC/CMD. This sets bit 7 of the sort direction byte at IX+04H, marking this array for descending sort order.
4E10
RST 10H D7
Fetch the next non-space character from the BASIC program text at (HL) into Register A, advancing HL past any spaces. This moves past the * or - prefix token to reach the array variable name.

4E11H – Array Expression Evaluation and Variable Table Lookup

This section evaluates the array variable expression, locates the array in the BASIC variable table, and extracts key information (type, element address, array base address) into the current descriptor table entry at IX. The ROM string expression evaluator at 260DH is called to evaluate the array variable, returning the array's address in DE and its type in 40AFH. The code then searches the variable table to find the matching array entry and computes the element layout.

4E11
PUSH HL E5
Save Register Pair HL (the current BASIC text pointer, positioned at the array variable expression) onto the stack.
4E12
GOSUB to ROM routine 260DH, the BASIC string expression evaluator. This evaluates the array variable expression at the current BASIC text position, returning the variable's address in Register Pair DE and updating the variable type at ROM location 40AFH. HL is advanced past the expression.
4E15
LD A,(40AFH) 3A AF 40
Fetch the variable type code from memory location 40AFH (the ROM BASIC variable type indicator, set by 260DH) into Register A. Type codes: 02H=integer, 03H=string, 04H=single-precision, 08H=double-precision.
4E18
LD (IX+01H),A DD 77 01
Store Register A (the variable type code) to IX+01H, the type field of the current array descriptor table entry. This marks the entry as active and records what type of data this array contains.
4E1B
LD (IX+07H),E DD 73 07
Store Register E (the low byte of the variable's address returned by 260DH) to IX+07H, the low byte of the array element address field in the descriptor table.
4E1E
LD (IX+08H),D DD 72 08
Store Register D (the high byte of the variable's address) to IX+08H, the high byte of the array element address field.
4E21
EX (SP),HL E3
Exchange HL with the value on top of the stack. HL (the post-expression text pointer from 260DH) is saved to the stack, and the original BASIC text pointer (saved at 4E11H) is restored to HL. This allows the code to re-parse the variable name for the variable table search.
4E22
PUSH DE D5
Save Register Pair DE (the variable's address from 260DH) onto the stack. This address will be used later after the variable table search completes.
4E23
GOSUB to 5BB4H, the Variable type parser in BASIC/CMD. This re-parses the variable name at the original BASIC text position (HL) to identify the variable by name and type for the subsequent table search. The result sets up internal state for the 5BFAH comparison routine.

Now search the BASIC variable table for the array matching the parsed variable name. The search walks from 40FBH (array end) toward 40FDH (string stack pointer), comparing each entry against the target.

4E26
LD HL,(40FBH) 2A FB 40
Fetch the array end / variable top pointer from memory location 40FBH into Register Pair HL. This is the starting point for the array search (searching downward).
4E29
LD DE,(40FDH) ED 5B FD 40
Fetch the string descriptor stack pointer from memory location 40FDH into Register Pair DE. This is the lower boundary of the array search area.
4E2D
JUMP forward unconditionally to 4E32H to begin the search comparison, skipping the link-follow instruction on the first iteration.

LOOP START — Array Table Search
Walk through array variable entries, following link pointers to advance from one entry to the next.

4E2F
GOSUB to 5C18H, the Line link follower in BASIC/CMD, to advance HL to the next variable table entry.
4E32
RST 18H DF
Compare Register Pair HL against Register Pair DE (16-bit comparison). The Z FLAG is set if HL equals DE (search exhausted without finding the array).
4E33
If the Z FLAG has been set (the array was not found in the variable table — it was never dimensioned), JUMP to the ROM BASIC syntax error handler at 1997H (?SN ERROR).
4E36
GOSUB to 5BFAH, the Line type check routine, to compare the current variable table entry at (HL) against the target variable name and type. Returns Z if the entry matches.
4E39
If the NZ FLAG has been set (the current entry does not match the target array), JUMP back to 4E2FH to follow the link to the next entry and continue searching.

LOOP END — Array Table Search

4E3BH – Array Header Parsing and Element Address Computation

The matching array has been found in the variable table at address HL. This section now parses the array header to extract the dimension count, total element count, and element sizes. The BASIC array header format (starting at the matched entry) is: [name bytes][type byte][dim_count_low][dim_count_high][dim1_size_low][dim1_size_high]... for each dimension. The code walks through the dimension entries to compute the total number of elements and the starting element offset based on the subscript specified in the CMD"O" argument.

4E3B
INC HL 23
INCrement Register Pair HL by 1, advancing past byte +0 of the matched variable table entry (the first name/flag byte).
4E3C
INC HL 23
INCrement HL by 1, advancing past byte +1 (the second name byte).
4E3D
INC HL 23
INCrement HL by 1, advancing to byte +3 of the array entry. HL now points to the low byte of the array's total data length (the number of bytes of array data, not counting the header).
4E3E
LD E,(HL) 5E
Fetch the low byte of the array's total data length from (HL) into Register E.
4E3F
INC HL 23
INCrement HL by 1, advancing to the high byte of the array data length.
4E40
LD D,(HL) 56
Fetch the high byte of the array's total data length from (HL) into Register D. Register Pair DE now holds the total byte count of the array's data area.
4E41
DEC DE 1B
DECrement Register Pair DE by 1. This subtracts 1 from the total data length to account for the dimension count byte that is included in the BASIC array header but is not part of the actual element data.
4E42
INC HL 23
INCrement HL by 1, advancing to the dimension count byte of the array header.
4E43
LD A,(HL) 7E
Fetch the number of dimensions in the array from (HL) into Register A. For a one-dimensional array like A$(100), this value is 01H. For multi-dimensional arrays, it is 02H or more.

LOOP START — Dimension Walk
This loop iterates through each dimension entry in the array header. Each dimension entry is 2 bytes (the dimension size + 1). The loop subtracts the dimension entry sizes from DE and the dimension header overhead from DE, computing the total number of elements times the element size.

4E44
INC HL 23
INCrement HL by 1, advancing past the low byte of the current dimension's size entry.
4E45
INC HL 23
INCrement HL by 1, advancing past the high byte of the current dimension's size entry.
4E46
INC HL 23
INCrement HL by 1, positioning HL at the next dimension entry (or past the last one).
4E47
DEC DE 1B
DECrement Register Pair DE by 1, subtracting 1 byte of dimension header overhead from the remaining data length.
4E48
DEC DE 1B
DECrement Register Pair DE by 1 again, subtracting another byte of dimension header overhead (each dimension entry is 2 bytes).
4E49
DEC A 3D
DECrement Register A (the dimension counter) by 1. When A reaches zero, all dimensions have been processed.
4E4A
If the NZ FLAG has been set (Register A is not zero, meaning there are more dimensions to process), JUMP back to 4E45H to process the next dimension entry.

LOOP END — Dimension Walk
At this point, all dimensions have been processed. HL now points to the start of the actual array element data. DE holds the total size in bytes of the element data area (total data length minus dimension headers). The following instructions store this address as the array data base in the descriptor table.

4E4C
LD (IX+05H),L DD 75 05
Store Register L (the low byte of the array data base address) to IX+05H, the low byte of the array base address field in the descriptor table entry. This is the starting address of the array's element data in memory.
4E4F
LD (IX+06H),H DD 74 06
Store Register H (the high byte of the array data base address) to IX+06H, the high byte of the array base address field.
4E52
EX DE,HL EB
Exchange Register Pairs DE and HL. Now HL holds the total element data size (previously in DE) and DE holds the array data base address (previously in HL).
4E53
EX (SP),HL E3
Exchange HL with the value on top of the stack. HL (the element data size) is saved to the stack, and the variable address from 260DH (saved at 4E22H) is restored to HL. HL now holds the variable's return address from the ROM expression evaluator.
4E54
OR A B7
Clear the CARRY FLAG by ORing Register A with itself. This prepares for the SBC HL,DE subtraction at the next instruction, ensuring an accurate subtraction without borrow.
4E55
SBC HL,DE ED 52
SUBtract Register Pair DE (the array data base address) from Register Pair HL (the variable address from 260DH) with borrow. The result in HL is the byte offset from the array data base to the starting element specified in the CMD"O" subscript. This offset represents the REN position times the element size.

4E57H – Element Size Calculation and Bounds Validation

This section divides the byte offset (in HL) by the element size to compute the number of elements before the starting element (the REN offset), then validates that the array has enough elements for the sort. It also handles the special case where n was specified as 0, meaning "use all remaining elements from this array."

4E57
LD A,(IX+01H) DD 7E 01
Fetch the variable type code from IX+01H (02H=integer, 03H=string, 04H=single, 08H=double) into Register A. The type determines the element size for the division.
4E5A
PUSH AF F5
Save Register A (the variable type code) and the flags onto the stack. The type code will be needed later for the element size calculation.
4E5B
GOSUB to 5D56H, the 16-bit division routine in BASIC/CMD. This divides HL by the element size (type code value, where integer=2 bytes, string=3 bytes, single=4 bytes, double=8 bytes). On return, HL holds the quotient (the REN offset — number of elements before the starting element) and DE holds the remainder.

Now determine which descriptor entry to compare against. For indirect sorts (5102H non-zero), the first array (entry 1) is the index array, and entry 2 is the first sort key. For direct sorts, entry 1 is the first sort key.

4E5E
LD A,(5102H) 3A 02 51
Fetch the indirect sort flag from memory location 5102H into Register A. 00H = direct sort, 01H = indirect sort.
4E61
OR A B7
OR Register A with itself, setting the Z FLAG if the indirect sort flag is 00H (direct sort).
4E62
PUSH AF F5
Save Register A (the indirect sort flag) and the flags onto the stack. The Z/NZ state will be used later to select the correct comparison branch.
4E63
LD A,01H 3E 01
Load Register A with 01H, the entry number for the first array in a direct sort. This is the default comparison target entry number.
4E65
If the Z FLAG has been set (this is a direct sort, 5102H = 00H), JUMP forward to 4E68H, keeping A = 01H as the first sort key entry number.
4E67
INC A 3C
INCrement Register A from 01H to 02H. For indirect sorts, the first sort key is entry 2 (entry 1 is the index array), so the comparison target entry number is 02H.
4E68
LD C,(IX+00H) DD 4E 00
Fetch the current entry counter from IX+00H into Register C. This identifies which table entry is being processed (1–9).
4E6B
CP C B9
Compare Register A (01H or 02H, the first sort key entry number) against Register C (the current entry counter). If they are equal, this is the first sort key array and its REN offset will be stored as the reference for validating subsequent arrays.
4E6C
If the NZ FLAG has been set (the current entry is not the first sort key), JUMP forward to 4E71H to skip the reference REN storage.
4E6E
LD (4EECH),HL 22 EC 4E
Store Register Pair HL (the REN offset of the current array, which is the first sort key) to memory location 4EECH. This saves the reference REN offset that all subsequent arrays must match. [SELF-MODIFYING CODE
Location 4EECH is within the code area — it is the operand of the LD DE,(4EECH) instruction at 4E73H.
4E71
If the NO CARRY FLAG has been set (Register A >= Register C, meaning the current entry number is less than or equal to the first sort key entry number — this is the index array or the first sort key itself), JUMP forward to 4E7BH to skip the REN validation, since there is no previous reference to compare against.

This entry is a subsequent sort key (entry number greater than the first sort key). Validate that its REN offset matches the first sort key's REN offset, as required by CMD"O".

4E73
LD DE,(4EECH) ED 5B EC 4E
Fetch the reference REN offset from memory location 4EECH into Register Pair DE. This is the REN offset of the first sort key array, stored at 4E6EH.
4E77
RST 18H DF
Compare Register Pair HL (the current array's REN offset) against Register Pair DE (the reference REN offset from the first sort key). The Z FLAG is set if they are equal.
4E78
GOSUB to 65B9H, the PDRIVE comparator routine in BASIC/CMD. In this context, the routine validates the array base addresses. If the comparison at RST 18H showed a mismatch (NZ), this routine raises an error because all arrays must have matching starting REN values.
4E7B
POP AF F1
Restore Register A and flags from the stack. Register A holds the indirect sort flag (00H or 01H), and the Z FLAG reflects whether this is a direct sort (Z) or indirect sort (NZ).
4E7C
If the Z FLAG has been set (this is a direct sort), JUMP forward to 4E81H to store the element size information for this entry.
4E7E
DEC C 0D
DECrement Register C (the current entry counter) by 1. For indirect sorts, this adjusts the entry counter to check if the current entry is entry 1 (the index array). After DEC, C = 0 means the original entry was 1.
4E7F
If the NZ FLAG has been set (the entry counter after DEC is not zero — this is not the index array in an indirect sort), JUMP forward to 4E8AH to skip the element size storage (the index array gets different treatment).
4E81
LD A,(4F02H) 3A 02 4F
Fetch the value from memory location 4F02H into Register A. This location holds a previously computed element size factor or offset used in the conditional store operation.
4E84
ADD A,(IX+01H) DD 86 01
ADD the variable type code from IX+01H to Register A. This combines the element size factor from 4F02H with the current array's type code to produce the combined parameter for the 65EBH routine.
4E87
GOSUB to 65EBH, the Conditional store to 4F02H routine in BASIC/CMD. This routine conditionally stores the computed value (type code + size factor) to the 4F02H workspace variable, updating the element size tracking for the sort workspace allocation.
4E8A
POP AF F1
Restore Register A and flags from the stack. Register A holds the variable type code (02H, 03H, 04H, or 08H) saved at 4E5AH.
4E8B
EX (SP),HL E3
Exchange HL with the value on top of the stack. HL (the REN offset) goes to the stack, and the total element data size (saved at 4E53H) is restored to HL.
4E8C
GOSUB to 5D56H, the 16-bit division routine. This divides HL (the total element data size in bytes) by the element size (type code), yielding the total number of elements in the array in HL.
4E8F
POP BC C1
Restore Register Pair BC from the stack. BC now holds the REN offset (the number of elements before the starting element), which was saved by the EX (SP),HL at 4E8BH.
4E90
OR A B7
Clear the CARRY FLAG by ORing Register A with itself, preparing for the SBC subtraction.
4E91
SBC HL,BC ED 42
SUBtract Register Pair BC (the REN offset) from Register Pair HL (the total element count) with borrow. The result in HL is the number of elements remaining from the starting element to the end of the array. This is the maximum number of elements available for sorting.
4E93
If the CARRY FLAG has been set (the REN offset exceeds the total element count — the starting subscript is past the end of the array), JUMP to the ROM "Illegal Function Call" error handler at 1E4AH. This validates that the array has enough elements from the specified starting position.

4E96H – Element Count Auto-Calculation and Final Bounds Check

This section handles the special case where the element count n was specified as 0 in the CMD"O" statement. When n is 0, the number of elements to sort is automatically set to the number of remaining elements in the first specified array (from the starting subscript to the end). It then performs the final bounds validation: the remaining elements in the array must be greater than or equal to n.

4E96
LD DE,(4F27H) ED 5B 27 4F
Fetch the element count n from memory location 4F27H into Register Pair DE. This is the value specified in the CMD"O" statement, or 0000H if the user specified 0.
4E9A
LD A,(IX+00H) DD 7E 00
Fetch the current entry counter from IX+00H into Register A. This identifies which array is being processed.
4E9D
DEC A 3D
DECrement Register A by 1. If the original entry counter was 01H (the first array), A becomes 00H and the Z FLAG is set.
4E9E
If the NZ FLAG has been set (this is not the first array — the auto-calculation only applies to the first array), JUMP forward to 4EA9H to perform the standard bounds check.
4EA0
LD A,D 7A
Copy Register D (the high byte of the element count n) to Register A.
4EA1
OR E B3
OR Register A with Register E (the low byte of n). This tests whether DE is zero. If both D and E are zero, the Z FLAG is set, indicating n = 0.
4EA2
If the NZ FLAG has been set (n is not zero — a specific element count was given), JUMP forward to 4EA9H to perform the standard bounds check with the specified count.

The element count n is zero and this is the first array. Auto-calculate n as the number of remaining elements from the starting subscript to the end of the array (which is in HL from the SBC at 4E91H).

4EA4
LD (4F27H),HL 22 27 4F
Store Register Pair HL (the number of remaining elements in the first array) to memory location 4F27H, replacing the zero element count with the auto-calculated value. All subsequent arrays will be validated against this count.
4EA7
LD D,H 54
Copy Register H to Register D, setting DE = HL so that the bounds check below compares correctly.
4EA8
LD E,L 5D
Copy Register L to Register E, completing the DE = HL copy.
4EA9
RST 18H DF
Compare Register Pair HL (the number of remaining elements in the current array) against Register Pair DE (the required element count n). The CARRY FLAG is set if HL < DE (the array has fewer remaining elements than n requires).
4EAA
If the CARRY FLAG has been set (the array does not have enough elements from the starting position to satisfy the element count n), JUMP to the ROM "Illegal Function Call" error handler at 1E4AH. This enforces the CMD"O" requirement that all arrays must have at least n elements from the specified starting subscript.

4EADH – Substring Field Parser and Argument Loop Advance

After validating the element count, this section checks whether the current array is a string type (03H) and, if so, looks for an optional substring field specification (x,y) following the array variable. The substring start position x and length y are parsed via ROM 2B1CH and stored into IX+02H and IX+03H. Finally, IX is advanced by 17H to the next descriptor table entry, and the loop returns to 4DEFH if more arguments remain.

4EAD
POP HL E1
Restore Register Pair HL from the stack, recovering the BASIC text pointer that was positioned past the array expression by 260DH (saved via EX (SP),HL at 4E21H and 4E8BH).
4EAE
LD A,(IX+01H) DD 7E 01
Fetch the variable type code from IX+01H into Register A. The substring field specification is only valid for string arrays (type 03H).
4EB1
CP 03H FE 03
Compare Register A against 03H (the string type code). If Register A equals 03H, the Z FLAG is set.
4EB3
If the NZ FLAG has been set (this is not a string array — it is integer, single, or double), JUMP forward to 4ECBH to skip the substring field parsing and advance to the next argument.
4EB5
LD A,(HL) 7E
Fetch the current character from the BASIC text at (HL) into Register A. This checks whether a substring field specification (x,y) follows the array variable.
4EB6
CP 28H FE 28
Compare Register A against 28H (ASCII open parenthesis (). The substring field is introduced by an open parenthesis following the array variable's closing parenthesis. If Register A equals 28H, the Z FLAG is set.
4EB8
If the NZ FLAG has been set (no open parenthesis follows — no substring field specified), JUMP forward to 4ECBH to keep the default values (IX+02H = 01H, IX+03H = FFH, meaning compare all characters starting at position 1).
4EBA
RST 10H D7
Fetch the next non-space character from the BASIC program text, advancing HL past the open parenthesis.
4EBB
GOSUB to ROM routine 2B1CH, the numeric expression evaluator that returns a byte result in Register A. This evaluates the substring start position x — the 1-based character position where the comparison begins.
4EBE
LD (IX+02H),A DD 77 02
Store Register A (the substring start position x) to IX+02H, the substring start field of the descriptor table entry.
4EC1
RST 08H ⇒ 2CH CF 2C
Call the RST 08H inline character match routine, consuming the comma between x and y in the (x,y) specification.
4EC3
GOSUB to ROM routine 2B1CH to evaluate the substring length y — the number of characters to compare.
4EC6
LD (IX+03H),A DD 77 03
Store Register A (the substring length y) to IX+03H, the substring length field of the descriptor table entry.
4EC9
RST 08H ⇒ 29H CF 29
Call the RST 08H inline character match routine, consuming the closing parenthesis ) of the (x,y) specification. If the closing parenthesis is not found, a syntax error is raised.

Advance IX to the next descriptor table entry and check if there are more array arguments.

4ECB
LD BC,0017H 01 17 00
Load Register Pair BC with 0017H (decimal 23), the size of one array descriptor table entry. This is the stride for advancing IX to the next entry.
4ECE
ADD IX,BC DD 09
ADD Register Pair BC (0017H) to Index Register IX, advancing IX to point to the next array descriptor table entry.
4ED0
DEC HL 2B
DECrement Register Pair HL by 1, backing up the BASIC text pointer so that RST 10H will re-fetch the current character.
4ED1
RST 10H D7
Fetch the next non-space character from the BASIC program text at (HL) into Register A.
4ED2
If the NZ FLAG has been set (the current character is not 00H / end-of-statement, meaning there are more arguments), JUMP back to 4DEFH to process the next array argument. [LOOP — Array Argument Processing]

4ED5H – Indirect Sort Index Array Initialization

This section handles the setup specific to indirect sorts (Format 2). After all array arguments have been parsed, if the indirect sort flag at 5102H is set, the code validates that the first array (entry 1) is an integer type, then validates that the second array (entry 2) has a non-zero type (at least one sort key array was specified). Finally, it fills the index array with sequential REN values (0, 1, 2, ..., n-1) so that the sort can rearrange the indices rather than the data.

4ED5
PUSH HL E5
Save Register Pair HL (the BASIC text pointer at end-of-statement) onto the stack. This will be restored later when the sort completes.
4ED6
LD A,(5102H) 3A 02 51
Fetch the indirect sort flag from memory location 5102H into Register A. 00H = direct sort, 01H = indirect sort.
4ED9
OR A B7
OR Register A with itself, setting the Z FLAG if the indirect sort flag is 00H (direct sort).
4EDA
If the Z FLAG has been set (this is a direct sort — no index array to initialize), JUMP forward to 4F01H to proceed directly to the sort workspace setup.

This is an indirect sort. Validate the index array (entry 1 at 4300H) and the first sort key (entry 2 at 4317H).

4EDC
LD A,(4301H) 3A 01 43
Fetch the variable type code from memory location 4301H (IX+01H of entry 1, the index array) into Register A.
4EDF
CP 02H FE 02
Compare Register A against 02H (the integer type code). The indirect index array must be an integer array (as specified in the CMD"O" documentation).
4EE1
If the NZ FLAG has been set (the index array is not an integer type), JUMP to the ROM BASIC syntax error handler at 1997H (?SN ERROR). The index array must be integer type (%%).
4EE4
LD A,(4318H) 3A 18 43
Fetch the variable type code from memory location 4318H (IX+01H of entry 2, the first sort key array at offset 4300H + 17H + 01H = 4318H) into Register A.
4EE7
OR A B7
OR Register A with itself, setting the Z FLAG if the type code is 00H (no sort key array was specified — only the index array was given, with no data arrays to sort by).
4EE8
If the Z FLAG has been set (no sort key array was specified after the index array), JUMP to the ROM BASIC syntax error handler at 1997H (?SN ERROR). An indirect sort requires at least one sort key array (av2) in addition to the index array (iav1).

Both validations passed. Now fill the index array with sequential REN values starting from 0. The index array's element data starts at the address stored in entry 1's IX+07H/08H field (4307H/4308H). The loop writes n 2-byte integer values (0, 1, 2, ..., n-1) to the index array.

4EEB
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, the initial REN value. The first index entry will be 0.
4EEE
LD HL,(4307H) 2A 07 43
Fetch the index array's element base address from memory location 4307H (IX+07H of entry 1) into Register Pair HL. This is where the sequential REN values will be written.
4EF1
LD BC,(4F27H) ED 4B 27 4F
Fetch the element count n from memory location 4F27H into Register Pair BC. This is the number of REN values to write.
4EF5
JUMP forward unconditionally to 4EFDH to test the loop counter before the first write.

LOOP START — REN Value Fill
Write sequential 2-byte integer REN values to the index array.

4EF7
LD (HL),E 73
Store Register E (the low byte of the current REN value) to memory at (HL), writing the low byte of the integer to the index array.
4EF8
INC HL 23
INCrement HL by 1, advancing to the high byte position of the integer.
4EF9
LD (HL),D 72
Store Register D (the high byte of the current REN value) to memory at (HL), writing the high byte of the integer.
4EFA
DEC BC 0B
DECrement Register Pair BC (the remaining element count) by 1.
4EFB
INC HL 23
INCrement HL by 1, advancing to the next integer element position.
4EFC
INC DE 13
INCrement Register Pair DE by 1, advancing the REN counter to the next sequential value (0, 1, 2, 3, ...).
4EFD
LD A,B 78
Copy Register B (the high byte of the remaining count) to Register A.
4EFE
OR C B1
OR Register A with Register C (the low byte of the remaining count). If both B and C are zero, the Z FLAG is set indicating all n REN values have been written.
4EFF
If the NZ FLAG has been set (BC is not zero, meaning there are more REN values to write), JUMP back to 4EF7H to write the next value.

LOOP END — REN Value Fill

4F01H – Sort Workspace Allocation

This section allocates workspace memory for the sort operation. It divides the available memory (0500H bytes = 1280 bytes) by the element size to determine how many elements can be processed, stores the workspace base address, and then uses the Array Walker Dispatch routine at 50EEH to set up element address pointers for all active array descriptors. The routine at 510CH stores element pointers into each descriptor's IX+0DH/0EH and IX+11H/12H fields.

4F01
LD A,00H 3E 00
Load Register A with 00H. This is a placeholder value that may be used by the division routine to select the element size mode. SELF-MODIFYING CODE - The operand byte at 4F02H is written by the 65EBH conditional store routine during array parsing. At runtime, this byte holds the combined element size factor computed during the array argument loop.
4F03
LD HL,0500H 21 00 05
Load Register Pair HL with 0500H (decimal 1280), the workspace buffer size in bytes. This is the amount of memory allocated for the sort's working data.
4F06
GOSUB to 5D56H, the 16-bit division routine in BASIC/CMD. This divides HL (the workspace size 0500H) by the element size factor (loaded into the divisor from Register A and the internal state set by 65EBH). On return, HL holds the quotient (number of element slots available in the workspace) and DE holds the remainder.
4F09
LD (5088H),HL 22 88 50
Store Register Pair HL (the number of workspace element slots) to memory location 5088H, the workspace base address variable. This value is used later to reset the partition boundary.
4F0C
EX DE,HL EB
Exchange Register Pairs DE and HL. Now HL holds the remainder from the division (unused workspace bytes) and DE holds the workspace slot count.
4F0D
LD HL,5200H 21 00 52
Load Register Pair HL with 5200H. This is the address above the SYS21 overlay area where the sort's relocated comparison and swap routines will be placed (the overlay occupies 4D00H–51E7H, and 5200H is the next available area).
4F10
LD BC,510CH 01 0C 51
Load Register Pair BC with 510CH, the address of the element address setup routine. This routine will be called by the Array Walker Dispatch at 50EEH for each active descriptor entry, storing element pointer values into the IX+0DH/0EH and IX+11H/12H fields.
4F13
GOSUB to 50EEH, the Array Walker Dispatch routine. This stores the address 510CH at 50FFH and then iterates through all active descriptor table entries, calling the routine at 510CH for each entry that has a non-zero type byte at IX+01H. This initializes the element address pointers for all participating arrays.
4F16
LD HL,(5088H) 2A 88 50
Fetch the workspace slot count from memory location 5088H into Register Pair HL.
4F19
LD (507EH),HL 22 7E 50
Store Register Pair HL (the workspace slot count) to memory location 507EH, the partition boundary variable. [SELF-MODIFYING CODE - Location 507EH is the operand of the LD HL instruction at 507DH. This value tracks the partition boundary during the sort.

4F1CH – Sort Outer Loop Entry

This is the entry point for the sort's outer loop. The sort uses a bidirectional bubble sort (cocktail shaker sort) that alternates between forward and reverse passes. The outer loop initializes the swap counter to 1 (indicating "no swaps yet" in a flag sense), then enters the pass setup. The sort terminates when a complete forward+reverse pass pair produces no swaps.

4F1C
LD HL,0001H 21 01 00
Load Register Pair HL with 0001H, initializing the forward-pass swap counter to 1. A non-zero value means "there are still swaps to check." The actual swap count is tracked by decrementing this counter as swaps occur.
4F1F
JUMP forward unconditionally to 4F23H to store the swap counter and begin the pass.

This entry point at 4F21H is reached from the bottom of the sort loop when a pass completes and the swap counters need to be doubled and re-stored.

4F21
EX DE,HL EB
Exchange Register Pairs DE and HL, moving the swap counter from DE to HL for storage.
4F22
ADD HL,HL 29
ADD Register Pair HL to itself, doubling the swap counter value. This doubles the pass counter each time a swap occurs, effectively counting the number of swap events.
4F23
LD (4F39H),HL 22 39 4F
Store Register Pair HL (the forward-pass swap counter) to memory location 4F39H. SELF-MODIFYING CODE - Location 4F39H is the operand of the LD HL instruction at 4F38H, used to reload the swap counter at the start of each pass.
4F26
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, clearing the reverse-pass swap counter.
4F29
LD (4F40H),DE ED 53 40 4F
Store Register Pair DE (0000H) to memory location 4F40H, clearing the reverse-pass swap counter. SELF-MODIFYING CODE - Location 4F40H is the operand of the LD HL instruction at 4F3FH.
4F2D
RST 18H DF
Compare Register Pair HL (the forward-pass swap counter) against Register Pair DE (0000H, the just-cleared reverse counter). The CARRY FLAG is set if HL < DE. Since DE is 0, this comparison checks whether HL is zero (Z flag) or positive (no carry). The intent is to check whether any swaps occurred in the previous pass pair.
4F2E
If the CARRY FLAG has been set (HL < DE, which is not possible since DE=0 — this branch is for the re-entry path where HL and DE have different values from swap tracking), JUMP forward to 4F32H to begin the next sort pass.

Sort complete. No swaps occurred during the last pass pair, or the element count has been exhausted. Restore HL and return.

4F30
POP HL E1
Restore Register Pair HL from the stack, recovering the BASIC text pointer saved at 4ED5H.
4F31
RET C9
RETurn to the caller. The sort is complete. Execution returns to the BASIC interpreter with the arrays sorted.

4F32H – Sort Pass Setup

This section sets up a new sort pass by calling the Array Walker Dispatch to advance element pointers (via routine 51D5H), then loads the pass counters for the bidirectional comparison. The sort operates as a cocktail shaker sort: the first pass sweeps forward comparing adjacent elements, and the second pass sweeps in reverse. The counters at 5145H and 5124H track how many comparisons remain in each direction.

4F32
LD BC,51D5H 01 D5 51
Load Register Pair BC with 51D5H, the address of the pass advance routine. This routine advances element pointers (IX+07H/08H) to the next element pair for the next comparison iteration.
4F35
GOSUB to 50EEH, the Array Walker Dispatch, to call the 51D5H pass advance routine for all active descriptor entries. This initializes the element pointers for the next sweep.
4F38
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H. SELF-MODIFYING CODE - The operand at 4F39H is overwritten at 4F23H with the forward-pass swap counter. At runtime, this loads the current forward-pass counter into HL.
4F3B
LD (5124H),HL 22 24 51
Store Register Pair HL (the forward-pass counter) to memory location 5124H, the reverse-direction pass counter. [SELF-MODIFYING CODE
Location 5124H is the operand of the LD HL instruction at 5123H (within the relocated sort code).
4F3E
EX DE,HL EB
Exchange Register Pairs DE and HL, moving the forward counter to DE.
4F3F
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H. SELF-MODIFYING CODE - The operand at 4F40H is overwritten at 4F29H with the reverse-pass swap counter. At runtime, this loads the current reverse-pass counter.
4F42
OR A B7
Clear the CARRY FLAG, preparing for the SBC comparison.
4F43
SBC HL,DE ED 52
SUBtract Register Pair DE (the forward counter) from Register Pair HL (the reverse counter). If HL < DE, the CARRY FLAG is set. If HL = DE, the Z FLAG is set. This determines which direction needs more passes.
4F45
If the CARRY FLAG has been set (the reverse counter is less than the forward counter), JUMP back to 4F21H to adjust the counters and restart the pass.
4F47
If the Z FLAG has been set (the counters are equal), JUMP back to 4F21H to double and restart.
4F49
OR A B7
Clear the CARRY FLAG again for the next SBC.
4F4A
SBC HL,DE ED 52
SUBtract DE from HL a second time. This further computes the difference between the two pass counters, splitting the remaining work between the forward and reverse sweep directions.
4F4C
If the NO CARRY FLAG has been set (HL >= DE, meaning the split is valid), JUMP forward to 4F53H to store the split counters.
4F4E
ADD HL,DE 19
ADD Register Pair DE back to Register Pair HL, restoring the original value before the second SBC. This handles the case where the subtraction underflowed.
4F4F
EX DE,HL EB
Exchange DE and HL, swapping the counter values.
4F50
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H, zeroing one of the counters when the split puts all remaining work in one direction.
4F53
LD (5145H),DE ED 53 45 51
Store Register Pair DE to memory location 5145H, the forward-sweep pass counter. SELF-MODIFYING CODE - Location 5145H is the operand of the LD HL instruction at 5144H (within the sort code).
4F57
LD (4F40H),HL 22 40 4F
Store Register Pair HL to memory location 4F40H, updating the reverse-sweep pass counter for the next iteration.
4F5A
LD BC,511DH 01 1D 51
Load Register Pair BC with 511DH, the address of the REN-to-address converter routine. This routine converts element indices to absolute memory addresses using the element size and base address.
4F5D
GOSUB to 50EEH, the Array Walker Dispatch, to call the 511DH REN-to-address converter for all active descriptor entries, computing element addresses for the current pass.
4F60
GOSUB to 5093H, a setup routine that initializes the indirect index array pointers for the current pass (if this is an indirect sort) and prepares the comparison state.

4F63H – Sort Inner Loop (Comparison Dispatch)

This is the inner loop of the sort. It walks through all active array descriptors using IX, loading the element pointers from each descriptor and dispatching to the appropriate comparison routine based on the variable type. After comparing all sort key levels for the current element pair, the loop either advances to the next pair or triggers a swap.

4F63
LD IX,4300H DD 21 00 43
Point Index Register IX to 4300H, the base of the array descriptor table. This resets IX to the first entry for a new comparison pass across all sort levels.
4F67
LD BC,000DH 01 0D 00
Load Register Pair BC with 000DH (decimal 13). This value is passed to the 5096H routine as an offset parameter — it points to IX+0DH, the element size field within each descriptor entry.
4F6A
GOSUB to 5096H, the indirect index update routine. If the indirect sort flag at 5102H is non-zero, this routine reads the current index values from the index array and uses them to compute element addresses for all sort key arrays. For direct sorts (5102H = 00H), this routine returns immediately.

4F6DH – Element Pair Comparison Entry

For each active descriptor entry, this section loads the two element pointers from IX+09H/0AH and IX+0DH/0EH into DE and HL respectively, reads the variable type from IX+01H, checks the sort direction flag at IX+04H bit 7, and then dispatches to the type-specific comparison routine. The comparison result (Z, CARRY, or NZ with no CARRY) determines whether the elements are equal, in order, or out of order.

4F6D
LD E,(IX+09H) DD 5E 09
Fetch the low byte of the Current Element 1 Pointer from IX+09H into Register E.
4F70
LD D,(IX+0AH) DD 56 0A
Fetch the high byte of the Current Element 1 Pointer from IX+0AH into Register D. Register Pair DE now points to the first element of the comparison pair.
4F73
LD L,(IX+0DH) DD 6E 0D
Fetch the low byte of the Element Size/second element pointer from IX+0DH into Register L.
4F76
LD H,(IX+0EH) DD 66 0E
Fetch the high byte from IX+0EH into Register H. Register Pair HL now points to the second element of the comparison pair.
4F79
LD C,(IX+01H) DD 4E 01
Fetch the variable type code from IX+01H into Register C. Values: 02H=integer, 03H=string, 04H=single-precision, 08H=double-precision.
4F7C
BIT 7,(IX+04H) DD CB 04 7E
Test bit 7 of IX+04H, the sort direction flag. Bit 7 set = descending order, bit 7 clear = ascending order. The Z FLAG is set if bit 7 is clear (ascending).
4F80
If the Z FLAG has been set (ascending sort order), JUMP forward to 4F83H, keeping DE as element 1 and HL as element 2 (compare element 1 against element 2 in natural order).
4F82
EX DE,HL EB
Exchange Register Pairs DE and HL, swapping the two element pointers. For descending sort order, the comparison is reversed by swapping which element is "first" and which is "second." Now DE points to what was element 2 and HL points to what was element 1.
4F83
LD A,C 79
Copy Register C (the variable type code) to Register A for the type dispatch comparison.
4F84
SUB 02H D6 02
SUBtract 02H from Register A. After this: A=00H for integer (type 02H), A=01H for string (type 03H), A=02H for single (type 04H), A=06H for double (type 08H). The Z FLAG is set if A is 00H (integer type).
4F86
If the NZ FLAG has been set (the variable type is not integer), JUMP forward to 4F9DH to check for string or floating-point types.

4F88H – Integer Comparison (Sign-Magnitude XOR 80H)

This section compares two 16-bit signed integers. BASIC stores integers in sign-magnitude format where bit 15 is the sign bit. To compare signed integers correctly using unsigned subtraction, both values have their sign bits (bit 7 of the high byte) flipped using XOR 80H. This converts the sign-magnitude representation to an offset binary representation where the natural unsigned ordering matches the signed ordering: -32768 maps to 0000H, 0 maps to 8000H, and +32767 maps to FFFFH.

4F88
LD C,(HL) 4E
Fetch the low byte of element 2's integer value from (HL) into Register C.
4F89
INC HL 23
INCrement HL by 1, advancing to the high byte of element 2.
4F8A
LD B,(HL) 46
Fetch the high byte of element 2's integer value from (HL) into Register B. Register Pair BC now holds element 2's integer value.
4F8B
LD A,(DE) 1A
Fetch the low byte of element 1's integer value from (DE) into Register A.
4F8C
LD L,A 6F
Copy Register A (element 1's low byte) to Register L.
4F8D
INC DE 13
INCrement DE by 1, advancing to the high byte of element 1.
4F8E
LD A,(DE) 1A
Fetch the high byte of element 1's integer value from (DE) into Register A.
4F8F
LD H,A 67
Copy Register A (element 1's high byte) to Register H. Register Pair HL now holds element 1's integer value.

Flip the sign bits of both values using XOR 80H to convert from sign-magnitude to offset binary for correct signed comparison via unsigned subtraction.

4F90
LD A,B 78
Copy Register B (element 2's high byte) to Register A.
4F91
XOR 80H EE 80
XOR Register A with 80H, flipping bit 7 (the sign bit) of element 2's high byte. This converts the sign-magnitude representation to offset binary.
4F93
LD B,A 47
Copy the modified high byte back to Register B.
4F94
LD A,H 7C
Copy Register H (element 1's high byte) to Register A.
4F95
XOR 80H EE 80
XOR Register A with 80H, flipping bit 7 (the sign bit) of element 1's high byte.
4F97
LD H,A 67
Copy the modified high byte back to Register H.
4F98
OR A B7
Clear the CARRY FLAG, preparing for the SBC subtraction.
4F99
SBC HL,BC ED 42
SUBtract Register Pair BC (element 2, offset binary) from Register Pair HL (element 1, offset binary) with borrow. The result sets: Z FLAG if the integers are equal, CARRY FLAG if element 1 < element 2 (elements are already in ascending order), NO CARRY + NZ if element 1 > element 2 (elements are out of order and need swapping).
4F9B
JUMP forward unconditionally to 5039H, the comparison result dispatcher, to determine the next action based on the Z, CARRY, and NZ flags.

4F9DH – String Comparison Dispatcher

This section handles string and floating-point comparisons. After the integer check at 4F86H, Register A holds 01H for strings or 02H+ for floating-point. The string comparison is complex: it reads the string descriptors (length byte + 2-byte data pointer), resolves the substring field parameters from IX+02H/03H, and then performs a byte-by-byte comparison with length-awareness. The floating-point comparison falls through to a simple multi-byte backward comparison at 4FF7H.

4F9D
DEC A 3D
DECrement Register A by 1. After this: A=00H for string (originally 01H), A=01H for single (originally 02H), A=05H for double (originally 06H). The Z FLAG is set if A is 00H (string type).
4F9E
If the NZ FLAG has been set (the variable type is not string — it is single or double floating-point), JUMP forward to 4FF7H for the floating-point byte-by-byte comparison.

String comparison. First check if the substring length at IX+03H is non-zero (FFH default OR an explicit value). If IX+03H is zero, the string has no characters to compare and the result is "equal."

4FA0
OR (IX+03H) DD B6 03
OR Register A (currently 00H) with the value at IX+03H (the substring length). If IX+03H is also 00H, the result is 00H and the Z FLAG is set (no characters to compare, treat as equal).
4FA3
If the Z FLAG has been set (IX+03H = 00H, no substring length specified and A was already 0), JUMP forward to 4FD4H to skip the comparison and treat the elements as equal.

Read the string descriptors for both elements. Each string descriptor in BASIC is 3 bytes: [length_byte][address_low][address_high]. HL and DE point to the descriptors. B will hold element 1's string length (from DE), C will hold element 2's string length (from HL).

4FA5
LD A,(DE) 1A
Fetch the string length byte of element 1 from (DE) into Register A.
4FA6
LD B,A 47
Copy Register A (element 1's string length) to Register B.
4FA7
LD C,(HL) 4E
Fetch the string length byte of element 2 from (HL) into Register C.
4FA8
INC B 04
INCrement Register B by 1, adjusting element 1's string length to 1-based for the upcoming loop counter (the loop uses DEC B to count down, so incrementing here compensates for the initial decrement).
4FA9
INC DE 13
INCrement DE by 1, advancing past element 1's length byte to the string data address pointer.
4FAA
INC C 0C
INCrement Register C by 1, adjusting element 2's string length to 1-based.
4FAB
INC HL 23
INCrement HL by 1, advancing past element 2's length byte to the string data address pointer.

Read the 2-byte string data pointers from both descriptors, resolving to actual string data addresses.

4FAC
PUSH DE D5
Save Register Pair DE (pointing to element 1's data address in the descriptor) onto the stack.
4FAD
LD E,(HL) 5E
Fetch the low byte of element 2's string data address from (HL) into Register E.
4FAE
INC HL 23
INCrement HL by 1.
4FAF
LD D,(HL) 56
Fetch the high byte of element 2's string data address from (HL) into Register D. DE now points to element 2's actual string data.
4FB0
EX DE,HL EB
Exchange DE and HL. Now HL points to element 2's string data and DE holds the descriptor address.
4FB1
EX (SP),HL E3
Exchange HL with the value on the stack. Element 2's string data pointer goes to the stack, and element 1's descriptor address pointer comes back to HL.
4FB2
LD E,(HL) 5E
Fetch the low byte of element 1's string data address from (HL) into Register E.
4FB3
INC HL 23
INCrement HL by 1.
4FB4
LD D,(HL) 56
Fetch the high byte of element 1's string data address from (HL) into Register D. DE now points to element 1's actual string data.
4FB5
POP HL E1
Restore Register Pair HL from the stack, recovering element 2's string data pointer. Now DE points to element 1's string data and HL points to element 2's string data. B holds element 1's adjusted length, C holds element 2's adjusted length.

Check if a substring start position is specified (IX+02H). If non-zero, advance both pointers to the starting character position before beginning the comparison.

4FB6
LD A,(IX+02H) DD 7E 02
Fetch the substring start position from IX+02H into Register A. Zero means compare from the beginning; a non-zero value x means start at character position x.
4FB9
OR A B7
OR Register A with itself, testing whether the substring start position is zero.
4FBA
If the Z FLAG has been set (substring start position is zero — no substring offset, compare from the beginning), JUMP forward to 4FD4H to skip the substring positioning and go directly to the comparison result.

LOOP — Substring Offset Advance
Advance both string pointers by the substring start offset, counting down characters. If either string is shorter than the start position, the string is treated as empty for comparison purposes.

4FBC
DEC HL 2B
DECrement HL by 1, backing up element 2's pointer (this will be immediately re-advanced).
4FBD
DEC DE 1B
DECrement DE by 1, backing up element 1's pointer.
4FBE
INC HL 23
INCrement HL by 1, advancing element 2's pointer by one character.
4FBF
INC DE 13
INCrement DE by 1, advancing element 1's pointer by one character.
4FC0
DEC B 05
DECrement Register B (element 1's remaining length) by 1.
4FC1
If the NZ FLAG has been set (element 1 still has characters remaining), JUMP forward to 4FCBH to check element 2's length.

Element 1 has run out of characters during the substring offset advance.

4FC3
DEC C 0D
DECrement Register C (element 2's remaining length) by 1.
4FC4
If the Z FLAG has been set (element 2 has also run out of characters — both strings are too short for the substring start position), JUMP to 4FD4H. Both strings are effectively empty, so they are treated as equal.
4FC6
DEC A 3D
DECrement Register A (the remaining substring start offset) by 1.
4FC7
If the NZ FLAG has been set (the substring offset has not been fully consumed), JUMP back to 4FC3H to continue counting down element 2's length. Element 1 is already exhausted.
4FC9
JUMP forward to 500CH. Element 1 is shorter than the substring start position (treated as empty), while element 2 still has characters. Element 1 < element 2.
4FCB
DEC C 0D
DECrement Register C (element 2's remaining length) by 1.
4FCC
If the NZ FLAG has been set (element 2 still has characters remaining), JUMP forward to 4FD6H to continue the substring offset advance.

Element 2 has run out of characters during the offset advance, but element 1 has not.

4FCE
DEC A 3D
DECrement Register A (the remaining substring start offset) by 1.
4FCF
If the Z FLAG has been set (the substring offset has been fully consumed), JUMP forward to 5018H. Element 2 is exhausted but element 1 is not, meaning element 1 > element 2.
4FD1
DEC B 05
DECrement Register B (element 1's remaining length) by 1.
4FD2
If the NZ FLAG has been set (element 1 still has characters), JUMP back to 4FCEH to continue counting down the offset.

LOOP END — Substring Offset

4FD4
JUMP forward unconditionally to 5039H, the comparison result dispatcher. The current flags (Z/CARRY/NZ) reflect the comparison outcome.

Continue substring offset advance when both strings still have characters.

4FD6
DEC A 3D
DECrement Register A (remaining substring offset) by 1.
4FD7
If the NZ FLAG has been set (offset not yet consumed), JUMP back to 4FBEH to advance both pointers by one more character.

4FD9H – Substring Character-by-Character Comparison

After the substring offset advance, both pointers (HL and DE) are positioned at the starting character for comparison. The substring length from IX+03H limits how many characters are compared. B holds element 1's remaining character count, C holds element 2's remaining count. The comparison walks forward through both strings, comparing byte by byte.

4FD9
LD A,(IX+03H) DD 7E 03
Fetch the substring comparison length from IX+03H into Register A. This is the maximum number of characters to compare (the y parameter from the (x,y) field specification). FFH means compare all remaining characters.
4FDC
CP B B8
Compare Register A (substring length limit) against Register B (element 1's remaining characters). If A < B, element 1 has more characters than the limit, so B should be capped.
4FDD
If the NO CARRY FLAG has been set (A >= B, the limit is not less than element 1's length — no capping needed), JUMP forward to 4FE0H.
4FDF
LD B,A 47
Cap Register B at the substring length limit. Element 1 will only compare up to y characters.
4FE0
CP C B9
Compare Register A (substring length limit) against Register C (element 2's remaining characters).
4FE1
If the NO CARRY FLAG has been set (A >= C, no capping needed), JUMP forward to 4FE4H.
4FE3
LD C,A 4F
Cap Register C at the substring length limit. Element 2 will only compare up to y characters.

LOOP START — Character Comparison Compare element 1 and element 2 byte by byte, advancing through both strings simultaneously.

4FE4
LD A,(DE) 1A
Fetch the current character of element 1's string from (DE) into Register A.
4FE5
CP (HL) BE
Compare Register A (element 1's character) against the byte at (HL) (element 2's character). If they are equal, the Z FLAG is set. If A < (HL), the CARRY FLAG is set.
4FE6
INC DE 13
INCrement DE by 1, advancing element 1's pointer to the next character.
4FE7
INC HL 23
INCrement HL by 1, advancing element 2's pointer to the next character.
4FE8
If the NZ FLAG has been set (the characters differ — a definitive comparison result has been reached), JUMP forward to 5039H to process the result. [MISMATCH — comparison complete]
4FEA
DEC B 05
DECrement Register B (element 1's remaining character count) by 1.
4FEB
If the NZ FLAG has been set (element 1 still has characters to compare), JUMP forward to 4FF2H to check element 2.

Element 1 has run out of characters. Check element 2.

4FED
DEC C 0D
DECrement Register C (element 2's remaining character count) by 1.
4FEE
If the Z FLAG has been set (element 2 has also run out of characters — both strings are the same length and all compared characters matched), JUMP to 5039H. The strings are equal (Z FLAG set).
4FF0
JUMP to 500CH. Element 1 is exhausted but element 2 is not, so element 1 is shorter. A shorter string sorts before a longer one (element 1 < element 2).
4FF2
DEC C 0D
DECrement Register C (element 2's remaining character count) by 1.
4FF3
If the NZ FLAG has been set (element 2 still has characters), JUMP back to 4FE4H to compare the next character pair.

LOOP END — Character Comparison

4FF5
JUMP to 5018H. Element 2 is exhausted but element 1 is not, so element 2 is shorter. Element 1 > element 2.

4FF7H – Floating-Point Comparison (Multi-Byte Backward)

This section handles single-precision (4-byte) and double-precision (8-byte) floating-point comparisons. Floating-point values in BASIC ROM are stored in a format where the most significant byte determines the magnitude, so the comparison works by scanning from the last (most significant) byte backward to the first (least significant) byte. The first byte that differs determines the ordering. Special handling is required for the sign byte (bit 7 of the last byte) and for zero values.

4FF7
LD B,00H 06 00
Load Register B with 00H, clearing the high byte for the BC offset calculation. Register C still holds the variable type code (04H for single, 08H for double).
4FF9
DEC C 0D
DECrement Register C by 1. For single-precision (C was 04H), C becomes 03H. For double-precision (C was 08H), C becomes 07H. BC now holds the byte offset from the first byte to the last byte of the value.
4FFA
ADD HL,BC 09
ADD BC to HL, advancing element 2's pointer to the last (most significant) byte of the value.
4FFB
EX DE,HL EB
Exchange DE and HL. Now HL points to element 1's first byte and DE points to element 2's last byte.
4FFC
ADD HL,BC 09
ADD BC to HL, advancing element 1's pointer to its last (most significant) byte.
4FFD
EX DE,HL EB
Exchange DE and HL again. Now DE points to element 1's last byte and HL points to element 2's last byte. Both pointers are positioned at the most significant byte.

Check if element 1's most significant byte (the exponent/sign byte) is zero. A zero exponent means the floating-point value is zero.

4FFE
LD A,(DE) 1A
Fetch element 1's most significant byte (the exponent byte) from (DE) into Register A.
4FFF
OR A B7
OR Register A with itself, testing whether element 1's exponent is zero.
5000
DEC DE 1B
DECrement DE by 1, moving element 1's pointer to the next-to-last byte (the sign/mantissa byte).
5001
LD A,(HL) 7E
Fetch element 2's most significant byte (the exponent byte) from (HL) into Register A.
5002
DEC HL 2B
DECrement HL by 1, moving element 2's pointer to the next-to-last byte.
5003
If the NZ FLAG has been set (element 1's exponent was non-zero — element 1 is not zero), JUMP forward to 5010H to check element 2's exponent.

Element 1's exponent is zero (element 1 = 0.0). Check element 2.

5005
OR A B7
OR Register A (element 2's exponent) with itself, testing whether element 2 is also zero.
5006
If the Z FLAG has been set (both exponents are zero — both values are 0.0), JUMP to 5039H. They are equal.
5008
BIT 7,(HL) CB 7E
Test bit 7 of element 2's sign/mantissa byte at (HL). Bit 7 is the sign bit: 0 = positive, 1 = negative.
500A
If the NZ FLAG has been set (element 2 is negative), JUMP to 5018H. Element 1 is zero and element 2 is negative, so element 1 > element 2 (zero is greater than a negative number).
500C
OR A B7
Clear the CARRY FLAG (OR A sets it to 0).
500D
SCF 37
SET the CARRY FLAG. This explicitly sets CARRY to signal "element 1 < element 2" (the first element is less than the second, meaning they are already in ascending order).
500E
JUMP forward to 503BH, the swap dispatch, with CARRY set indicating element 1 < element 2.
5010
OR A B7
OR Register A (element 2's exponent) with itself, testing whether element 2 is zero.
5011
If the NZ FLAG has been set (element 2's exponent is also non-zero — neither value is zero), JUMP forward to 501CH for the full sign/magnitude comparison.

Element 2 is zero but element 1 is not. Check element 1's sign.

5013
EX DE,HL EB
Exchange DE and HL. Now HL points to element 1's sign/mantissa byte.
5014
BIT 7,(HL) CB 7E
Test bit 7 of element 1's sign byte. 0 = positive, 1 = negative.
5016
If the NZ FLAG has been set (element 1 is negative), JUMP to 500CH. Element 1 is negative and element 2 is zero, so element 1 < element 2. Set CARRY and go to swap dispatch.
5018
OR FFH F6 FF
OR Register A with FFH, setting A to FFH and clearing the Z FLAG (NZ). The CARRY FLAG is cleared by OR. This signals "element 1 > element 2" (NZ and no CARRY).
501A
JUMP forward to 5039H, the comparison result dispatcher, with NZ and no CARRY indicating element 1 > element 2.

501CH – Floating-Point Sign and Mantissa Comparison

Both floating-point values are non-zero. This section compares the sign bits first. If the signs differ, the result is immediately determined. If both signs are the same, the values are compared byte-by-byte from most significant to least significant. For negative numbers, the comparison is reversed (a larger absolute value is more negative, so it sorts earlier in ascending order).

501C
LD A,(DE) 1A
Fetch element 1's sign/mantissa byte from (DE) into Register A. This is the byte just below the exponent, containing the sign bit in bit 7.
501D
BIT 7,A CB 7F
Test bit 7 of Register A (element 1's sign bit). Z if positive, NZ if negative.
501F
INC DE 13
INCrement DE by 1, moving element 1's pointer back to the exponent (most significant) byte.
5020
If the Z FLAG has been set (element 1 is positive), JUMP forward to 502AH to check element 2's sign.

Element 1 is negative. Check element 2's sign.

5022
BIT 7,(HL) CB 7E
Test bit 7 of element 2's sign byte at (HL).
5024
INC HL 23
INCrement HL by 1, moving element 2's pointer back to the exponent byte.
5025
If the Z FLAG has been set (element 2 is positive while element 1 is negative), JUMP to 500CH. A negative number is less than a positive number: element 1 < element 2.
5027
EX DE,HL EB
Exchange DE and HL. Both values are negative, so the comparison must be reversed: for negative numbers, the value with the larger absolute magnitude is more negative (sorts first in ascending order). Swapping the pointers reverses the comparison direction.
5028
JUMP forward to 502FH to begin the byte-by-byte comparison with reversed pointers.
502A
BIT 7,(HL) CB 7E
Test bit 7 of element 2's sign byte at (HL). Element 1 is positive.
502C
INC HL 23
INCrement HL by 1, moving back to the exponent byte.
502D
If the NZ FLAG has been set (element 2 is negative while element 1 is positive), JUMP to 5018H. A positive number is greater than a negative number: element 1 > element 2.

Both values have the same sign. Compare byte-by-byte from most significant to least significant. B (loaded from C at 502FH) counts the remaining bytes.

502F
LD B,C 41
Copy Register C (the byte offset value, 03H for single or 07H for double) to Register B, setting up the byte comparison loop counter.
5030
INC B 04
INCrement Register B by 1, adjusting for the DJNZ loop (which decrements before testing). B now holds the number of bytes to compare (4 for single, 8 for double).

LOOP START — Byte-by-Byte FP Comparison]

5031
LD A,(DE) 1A
Fetch element 1's current byte from (DE) into Register A.
5032
CP (HL) BE
Compare Register A against element 2's current byte at (HL). Sets Z if equal, CARRY if A < (HL).
5033
DEC DE 1B
DECrement DE by 1, moving element 1's pointer to the next less significant byte.
5034
DEC HL 2B
DECrement HL by 1, moving element 2's pointer to the next less significant byte.
5035
If the NZ FLAG has been set (the bytes differ), JUMP to 5039H to process the comparison result. [MISMATCH]
5037
DECrement Register B by 1 and JUMP back to 5031H if B is not zero. This loops through all bytes of the floating-point value.

LOOP END — Byte-by-Byte FP Comparison

5039H – Comparison Result Dispatcher

This is the central comparison result dispatcher reached from all comparison types (integer, string, and floating-point). The flags encode the comparison result: Z = elements are equal (advance to next sort level), CARRY = element 1 < element 2 (already in order for ascending sort), NZ + no CARRY = element 1 > element 2 (out of order, need swap for ascending sort). For multi-level sorts, Z causes the loop to advance IX to the next descriptor entry and compare the next sort key.

5039
If the Z FLAG has been set (the elements are equal at this sort level), JUMP forward to 5054H to advance IX to the next descriptor entry and compare the next sort key level. If there are no more levels, the elements are fully equal and no swap is needed.
503B
If the CARRY FLAG has been set (element 1 < element 2 — elements are already in ascending order), JUMP forward to 5060H to perform the reverse-direction swap dispatch. In the cocktail shaker sort, a "less than" result during the forward pass means no forward swap is needed, but the reverse pass counter is updated.

NZ and no CARRY: element 1 > element 2. The elements are out of order for ascending sort. Perform a forward swap.

503D
LD BC,5153H 01 53 51
Load Register Pair BC with 5153H, the address of the forward swap routine. This routine exchanges element data in all participating arrays for a forward-direction swap.
5040
GOSUB to 50EEH, the Array Walker Dispatch, to call the 5153H forward swap routine for all active descriptor entries. This physically exchanges the data of the two elements in every participating array.
5043
LD HL,(5145H) 2A 45 51
Fetch the forward-direction pass counter from memory location 5145H into Register Pair HL. SELF-MODIFYING CODE - The operand at 5145H+1 is written at 4F53H.
5046
DEC HL 2B
DECrement Register Pair HL by 1, decrementing the forward pass counter. Each swap consumes one unit from the pass counter.
5047
LD (5145H),HL 22 45 51
Store the decremented forward pass counter back to 5145H.
504A
LD A,H 7C
Copy Register H (high byte of the pass counter) to Register A.
504B
OR L B5
OR Register A with Register L (low byte). If both are zero, the Z FLAG is set, indicating the forward pass is complete.
504C
If the Z FLAG has been set (the forward pass counter has reached zero), JUMP to 5077H to reset the partition boundary and start the reverse pass.
504E
GOSUB to 507DH, the partition boundary update routine. This adjusts the partition boundary tracking variable at 507EH.
5051
JUMP back to 4F63H, the sort inner loop, to compare the next element pair.

5054H – Multi-Level Sort: Advance to Next Key Level

When the current sort key level comparison results in equality (Z flag), the sort must check the next key level to break the tie. This section advances IX by 17H to the next descriptor table entry and checks if there is another active sort key. If there is, it loops back to the comparison dispatcher at 4F6DH. If there are no more levels, the elements are fully equal and no swap is performed.

5054
LD BC,0017H 01 17 00
Load Register Pair BC with 0017H (decimal 23), the array descriptor entry stride.
5057
ADD IX,BC DD 09
ADD BC to IX, advancing to the next descriptor table entry.
5059
LD A,(IX+01H) DD 7E 01
Fetch the variable type code from IX+01H of the next entry into Register A. A zero value means this entry is inactive (no more sort key levels).
505C
OR A B7
OR Register A with itself, testing whether the type code is zero.
505D
If the NZ FLAG has been set (the next entry has a non-zero type code — there is another sort key level), JUMP back to 4F6DH to load the element pointers for this level and compare. [MULTI-LEVEL SORT]

5060H – Reverse Direction Swap Dispatch

When the comparison shows element 1 < element 2 (CARRY set), the forward pass does not need a swap, but the reverse pass counter is updated. This section performs the reverse-direction swap and decrements the reverse pass counter.

5060
LD BC,5174H 01 74 51
Load Register Pair BC with 5174H, the address of the reverse swap routine.
5063
GOSUB to 50EEH, the Array Walker Dispatch, to call the 5174H reverse swap routine for all active descriptor entries.
5066
LD HL,(5124H) 2A 24 51
Fetch the reverse-direction pass counter from memory location 5124H into Register Pair HL.
5069
DEC HL 2B
DECrement the reverse pass counter by 1.
506A
LD (5124H),HL 22 24 51
Store the decremented reverse pass counter back to 5124H.
506D
LD A,H 7C
Copy Register H to Register A.
506E
OR L B5
OR Register A with Register L, testing whether the counter has reached zero.
506F
If the Z FLAG has been set (the reverse pass counter has reached zero), JUMP to 5077H to reset and start the next pass cycle.
5071
GOSUB to 507DH, the partition boundary update routine.
5074
JUMP back to 4F60H to continue the sort pass with the next element pair.

5077H – Pass Complete: Reset Partition and Restart

When a pass direction's counter reaches zero, this section resets the partition boundary and starts a new pass cycle from the outer loop.

5077
GOSUB to 5087H, the partition boundary reset routine. This resets the boundary tracker at 507EH to the workspace size and calls the Array Walker Dispatch to reinitialize the indirect index pointers.
507A
JUMP back to 4F38H, the sort pass setup, to begin a new pass cycle with updated counters.

507DH – Partition Boundary Tracker

This routine decrements the partition boundary counter at 507EH. The partition boundary tracks the current position within the sort pass. When the counter reaches zero (which wraps from 0000H to FFFFH), the routine returns with Z flag cleared via the OR test, allowing the caller to detect the boundary. The self-modifying operand at 507EH was initialized at 4F19H with the workspace slot count.

507D
LD HL,0000H 21 00 00
Load Register Pair HL with the partition boundary value. SELF-MODIFYING CODE - The operand at 507EH is overwritten at 4F19H, 5081H, and 508AH. At runtime, this loads the current partition boundary counter.
5080
DEC HL 2B
DECrement Register Pair HL by 1, advancing the partition boundary by one position.
5081
LD (507EH),HL 22 7E 50
Store the decremented partition boundary back to 507EH, updating the self-modifying operand for the next call.
5084
LD A,H 7C
Copy Register H (high byte of the counter) to Register A.
5085
OR L B5
OR Register A with Register L. If HL is 0000H (the counter wrapped from 0001H to 0000H), the Z FLAG is set.
5086
RET NZ C0
RETurn if the NZ FLAG has been set (the counter has not reached zero — still within the current partition). If the counter is zero, execution falls through to the reset routine at 5087H.

5087H – Partition Boundary Reset

This routine resets the partition boundary to the workspace size value from 5088H and reinitializes the indirect index array pointers by calling the Array Walker Dispatch with the 5184H indirect index update routine.

5087
LD HL,0000H 21 00 00
Load Register Pair HL with the workspace base value. SELF-MODIFYING CODE - The operand at 5088H is written at 4F09H with the workspace slot count. At runtime, this reloads the full partition size.
508A
LD (507EH),HL 22 7E 50
Store Register Pair HL to 507EH, resetting the partition boundary counter to the full workspace size.
508D
LD BC,5184H 01 84 51
Load Register Pair BC with 5184H, the address of the indirect index update routine.
5090
GOSUB to 50EEH, the Array Walker Dispatch, to call the 5184H indirect index update routine for all active descriptor entries. This reinitializes the swap source/destination pointers for the next partition.

5093H – Indirect Sort Index Resolver

This routine computes the actual element addresses for all sort key arrays based on the current index values in the indirect index array. For direct sorts (5102H = 00H), it returns immediately. For indirect sorts, it reads the current pair of index values from the index array, then walks through all sort key descriptor entries (starting at entry 2 / 4317H), computing the element address for each array as: base_address + (index_value × element_size). The computed addresses are written to the IX+09H/0AH and IX+0DH/0EH fields of each descriptor entry.

5093
LD BC,0009H 01 09 00
Load Register Pair BC with 0009H (decimal 9). This offset is used to index into the first descriptor entry at 4300H to read IX+09H, the current element 1 pointer field.
5096
LD A,(5102H) 3A 02 51
Fetch the indirect sort flag from memory location 5102H into Register A. 00H = direct sort, non-zero = indirect sort.
5099
OR A B7
OR Register A with itself, testing whether the indirect flag is zero.
509A
RET Z C8
RETurn if the Z FLAG has been set (this is a direct sort — no index resolution needed). For direct sorts, the element pointers are computed directly from the array base addresses.

This is an indirect sort. Read the current index values from the index array (entry 1 at 4300H) and use them to compute element addresses for all sort key arrays.

509B
LD HL,4300H 21 00 43
Point Register Pair HL to 4300H, the base of the descriptor table. This will be offset by BC to access the index array's element pointer field.
509E
ADD HL,BC 09
ADD Register Pair BC (0009H) to HL, computing 4309H — the address of the index array's IX+09H field (the current element 1 pointer).
509F
LD E,(HL) 5E
Fetch the low byte of the index array's element 1 pointer from (HL) into Register E.
50A0
INC HL 23
INCrement HL by 1.
50A1
LD D,(HL) 56
Fetch the high byte from (HL) into Register D. Register Pair DE now points to the current element in the index array.
50A2
EX DE,HL EB
Exchange DE and HL. Now HL points to the element in the index array, and DE holds the descriptor table address.
50A3
LD E,(HL) 5E
Fetch the low byte of the index value (the REN) from the index array element at (HL) into Register E.
50A4
INC HL 23
INCrement HL by 1.
50A5
LD D,(HL) 56
Fetch the high byte of the index value from (HL) into Register D. Register Pair DE now holds the REN (Relative Element Number) from the index array.
50A6
LD IX,4317H DD 21 17 43
Point Index Register IX to 4317H, the base of the second descriptor table entry (entry 2). Entry 1 is the index array; entries 2+ are the sort key arrays.

LOOP START — Index Resolution
For each active sort key descriptor, compute the element address as base + (REN × element_size) and store it in the element pointer fields.

50AA
PUSH IX DD E5
Save Index Register IX (the current descriptor entry address) onto the stack.
50AC
POP HL E1
Restore the value to HL (this is a standard IX-to-HL transfer pattern: PUSH IX / POP HL copies IX to HL).
50AD
ADD HL,BC 09
ADD Register Pair BC (the offset parameter, 0009H for IX+09H or 000DH for IX+0DH) to HL, computing the address of the target element pointer field within this descriptor entry.
50AE
PUSH BC C5
Save Register Pair BC (the offset parameter) onto the stack.
50AF
PUSH HL E5
Save Register Pair HL (the address of the element pointer field) onto the stack.
50B0
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H, initializing the accumulator for the REN × element_size multiplication.
50B3
LD B,(IX+01H) DD 46 01
Fetch the variable type code from IX+01H into Register B. This is the element size (02H for integer, 03H for string, 04H for single, 08H for double) which serves as the multiplication loop counter.

INNER LOOP — Multiply by repeated addition
Compute REN × element_size by adding DE (the REN value) to HL repeatedly, B times.

50B6
ADD HL,DE 19
ADD Register Pair DE (the REN value) to Register Pair HL. Each iteration adds one copy of the REN, effectively computing REN × B (the element size).
50B7
DECrement Register B by 1 and JUMP back to 50B6H if B is not zero. After B iterations, HL = REN × element_size. [INNER LOOP END]
50B9
LD C,(IX+05H) DD 4E 05
Fetch the low byte of the array base address from IX+05H into Register C.
50BC
LD B,(IX+06H) DD 46 06
Fetch the high byte of the array base address from IX+06H into Register B. Register Pair BC now holds the array's base element address.
50BF
ADD HL,BC 09
ADD Register Pair BC (the base address) to Register Pair HL (the byte offset REN × element_size). HL now holds the absolute memory address of the element at position REN within this array.
50C0
POP BC C1
Restore Register Pair BC from the stack. BC now holds the address of the element pointer field within the descriptor entry (saved at 50AFH).
50C1
LD A,L 7D
Copy Register L (the low byte of the computed element address) to Register A.
50C2
LD (BC),A 02
Store Register A (the low byte of the element address) to the memory location pointed to by BC (the element pointer field's low byte).
50C3
INC BC 03
INCrement BC by 1, advancing to the high byte of the element pointer field.
50C4
LD A,H 7C
Copy Register H (the high byte of the computed element address) to Register A.
50C5
LD (BC),A 02
Store Register A (the high byte) to the memory location pointed to by BC (the element pointer field's high byte).
50C6
LD BC,0017H 01 17 00
Load Register Pair BC with 0017H (decimal 23), the descriptor entry stride.
50C9
ADD IX,BC DD 09
ADD BC to IX, advancing to the next descriptor table entry.
50CB
POP BC C1
Restore Register Pair BC from the stack. BC holds the original offset parameter (0009H or 000DH) for the next iteration.
50CC
LD A,(IX+01H) DD 7E 01
Fetch the variable type code from IX+01H of the next entry into Register A.
50CF
OR A B7
OR Register A with itself, testing whether the next entry is active.
50D0
If the NZ FLAG has been set (the next entry has a non-zero type — it is active), JUMP back to 50AAH to process the next sort key array.

LOOP END — Index Resolution

50D2
LD IX,4317H DD 21 17 43
Point IX back to 4317H (entry 2), restoring IX to the first sort key entry for the caller.
50D6
RET C9
RETurn to the caller.

50D7H – Element Address Helper with Boundary Check

This helper routine checks IX+16H (the swap destination status flag). If IX+16H is zero, the routine simply adds BC to HL and returns — a basic address offset. If IX+16H is non-zero, it calls the 65F4H conditional return+position routine to perform a boundary-checked block copy using LDIR, updating the swap temp pointers at IX+13H/14H afterward.

50D7
LD A,(IX+16H) DD 7E 16
Fetch the swap destination status flag from IX+16H into Register A. Zero means no boundary-checked copy is needed.
50DA
OR A B7
OR Register A with itself, testing whether the flag is zero.
50DB
If the NZ FLAG has been set (the flag is non-zero — a boundary-checked copy is required), JUMP forward to 50DFH to perform the copy.
50DD
ADD HL,BC 09
ADD Register Pair BC to Register Pair HL, applying a simple address offset.
50DE
RET C9
RETurn to the caller with the adjusted address in HL.
50DF
GOSUB to 65F4H, the Conditional return + position check routine in BASIC/CMD. This routine performs a boundary-validated block copy.
50E2
LD D,(IX+14H) DD 56 14
Fetch the high byte of the swap temp pointer from IX+14H into Register D.
50E5
LDIR ED B0
Execute the LDIR block copy: copy BC bytes from (HL) to (DE), incrementing both HL and DE, decrementing BC until zero. This copies element data as part of the swap operation.
50E7
LD (IX+13H),E DD 73 13
Store Register E (the updated low byte of DE after LDIR) to IX+13H, updating the swap temp pointer's low byte.
50EA
LD (IX+14H),D DD 72 14
Store Register D (the updated high byte) to IX+14H, updating the swap temp pointer's high byte.
50ED
RET C9
RETurn to the caller.

50EEH – Array Walker Dispatch

This is a general-purpose dispatch routine that iterates through all active entries in the array descriptor table at 4300H. For each entry where IX+01H (the type code) is non-zero, it calls the routine whose address was stored at 50FFH. The routine address is passed in BC and stored via self-modifying code. The type code is loaded into C (with B=00H) before each call, making BC available to the called routine as the element size. The called routine can also access the current descriptor entry via IX.

50EE
LD (50FFH),BC ED 43 FF 50
Store Register Pair BC (the address of the routine to call for each entry) to memory location 50FFH. SELF-MODIFYING CODE - This overwrites the operand of the CALL instruction at 50FEH, setting the target address for the per-entry dispatch call.
50F2
LD IX,4300H DD 21 00 43
Point Index Register IX to 4300H, the base of the array descriptor table.

LOOP START — Table Walk]

50F6
LD A,(IX+01H) DD 7E 01
Fetch the variable type code from IX+01H of the current entry into Register A. Zero means the entry is inactive.
50F9
OR A B7
OR Register A with itself, setting the Z FLAG if the type code is zero.
50FA
RET Z C8
RETurn if the Z FLAG has been set (the entry is inactive — all active entries have been processed).
50FB
LD C,A 4F
Copy Register A (the type code / element size) to Register C.
50FC
LD B,00H 06 00
Load Register B with 00H. Register Pair BC now holds the element size as a 16-bit value (00:type_code).
50FE
CALL 0000H CD 00 00
GOSUB to the routine address stored at 50FFH. SELF-MODIFYING CODE - The operand bytes at 50FFH/5100H are overwritten at 50EEH with the actual routine address passed in BC by the caller. This calls the per-entry operation (e.g., 510CH for address setup, 511DH for REN conversion, 5153H for forward swap, 5174H for reverse swap, 51D5H for pass advance, 5184H for indirect update).
5101
LD A,00H 3E 00
Load Register A with 00H. SELF-MODIFYING CODE - The operand at 5102H is the indirect sort flag, written at 4E04H. At runtime, A is loaded with 00H (direct) or 01H (indirect).
5103
OR A B7
OR Register A with itself, testing the indirect sort flag.
5104
RET NZ C0
RETurn if the NZ FLAG has been set (this is an indirect sort). For indirect sorts, only the first descriptor entry (the index array) needs the per-entry operation; the sort key entries are handled separately by the index resolver at 5093H. Returning here skips the remaining entries.
5105
LD BC,0017H 01 17 00
Load Register Pair BC with 0017H (decimal 23), the descriptor entry stride.
5108
ADD IX,BC DD 09
ADD BC to IX, advancing to the next descriptor table entry.
510A
JUMP back unconditionally to 50F6H to process the next entry.

LOOP END — Table Walk

510CH – Element Address Setup Routine

This routine is called by the Array Walker Dispatch (50EEH) to initialize element pointer fields in a descriptor entry. It stores element addresses into IX+0DH/0EH and IX+11H/12H, then jumps to 65FFH (the Conditional add loop in BASIC/CMD) for additional address computation. The disassembler has incorrectly decoded the DD-prefixed IX instructions in this section — the corrected mnemonics are shown below.

DISASSEMBLER CORRECTION: The bytes at 510CH–511BH contain four DD-prefixed IX instructions whose offset bytes were misinterpreted as separate instructions. The raw bytes are: DD 75 11 DD 74 12 47 DD 75 13 DD 74 14 C3 FF 65 — decoding to: LD (IX+11H),L / LD (IX+12H),H / LD B,A / LD (IX+13H),L / LD (IX+14H),H / JP 65FFH.

510C
LD (IX+11H),L DD 75 11
Store Register L (the low byte of the element address) to IX+11H, the low byte of the current element 2 pointer field.
510F
LD (IX+12H),H DD 74 12
Store Register H (the high byte) to IX+12H, the high byte of the current element 2 pointer field.
5112
LD B,A 47
Copy Register A to Register B, saving the element type/size for subsequent use.
5113
LD (IX+13H),L DD 75 13
Store Register L to IX+13H, the low byte of the swap temp pointer field. This initializes the swap temp pointer to the same address as the element 2 pointer.
5116
LD (IX+14H),H DD 74 14
Store Register H to IX+14H, the high byte of the swap temp pointer field.
5119
JUMP to 65FFH, the Conditional add loop routine in BASIC/CMD, to perform additional element address computation and return to the Array Walker Dispatch loop.
511C
RET C9
RETurn. This is a secondary return point reached from within the 65FFH routine when the address computation is complete.

511DH – REN-to-Address Converter

This routine converts a Relative Element Number (REN) to an absolute memory address for the current descriptor entry. It computes: address = base_address + (REN × element_size) + element_offset. The routine uses self-modifying operands at 511EH/511FH and 5124H/5125H for the REN values, a multiply-by-repeated-addition loop, and stores the results into IX+09H/0AH (element 1 pointer) and IX+0DH/0EH (element 2 pointer). The disassembler has heavily garbled this section due to DD-prefixed IX instructions.

DISASSEMBLER CORRECTION: The bytes from 511DH through 5151H contain numerous DD-prefixed IX store instructions whose offset bytes were decoded as separate instructions. The corrected instruction stream is documented below with the proper IX offsets.

511D
LD B,A 47
Copy Register A (the type code / element size, passed in C by the Array Walker Dispatch and loaded to A by the caller) to Register B for use as the multiply loop counter.
511E
LD HL,0000H 21 00 00
Load Register Pair HL with the first element's REN value. SELF-MODIFYING CODE - The operand at 511FH is overwritten at 4F3BH and other locations with the current forward-pass REN counter. This provides the element index for the first element of the comparison pair.
5121
PUSH HL E5
Save Register Pair HL (the first element's REN) onto the stack for later use when computing the second element's address.
5122
PUSH BC C5
Save Register Pair BC (B = element size, C = type code) onto the stack.
5123
LD DE,0000H 11 00 00
Load Register Pair DE with the second element's REN value. SELF-MODIFYING CODE - The operand at 5124H is overwritten at 4F3BH and 506AH with the reverse-pass REN counter.

MULTIPLY LOOP — REN × element_size
Compute the byte offset by adding DE (element size) to HL repeatedly, B times.

5126
ADD HL,DE 19
ADD Register Pair DE to Register Pair HL.
5127
DECrement Register B by 1 and JUMP back to 5126H if B is not zero. After B iterations, HL = REN × element_size.
5129
LD E,(IX+0FH) DD 5E 0F
Fetch the low byte of the element offset from IX+0FH into Register E.
512C
LD D,(IX+10H) DD 56 10
Fetch the high byte of the element offset from IX+10H into Register D.
512F
LD (IX+09H),E DD 73 09
Store Register E to IX+09H, saving the element offset as the initial element 1 pointer (will be adjusted below).
5132
LD (IX+0AH),D DD 72 0A
Store Register D to IX+0AH.
5135
ADD HL,DE 19
ADD Register Pair DE (the element offset) to Register Pair HL (REN × element_size). HL now holds the absolute byte offset from the base of the array to the target element.
5136
LD (IX+0DH),L DD 75 0D
Store Register L (the low byte of the computed address) to IX+0DH.
5139
LD (IX+0EH),H DD 74 0E
Store Register H to IX+0EH. IX+0DH/0EH now holds the element 1 address.
513C
POP BC C1
Restore Register Pair BC from the stack (B = element size, C = type code).
513D
LD (IX+0BH),L DD 75 0B
Store Register L to IX+0BH, the low byte of the swap source address field.
5140
LD (IX+0CH),H DD 74 0C
Store Register H to IX+0CH, the high byte of the swap source address field.
5143
EX (SP),HL E3
Exchange HL with the value on the stack. HL receives the first element's REN (saved at 5121H), and the computed element 1 address goes to the stack.
5144
LD DE,0000H 11 00 00
Load Register Pair DE with the second element's index. SELF-MODIFYING CODE - The operand at 5145H is written at 4F53H and 5047H.

MULTIPLY LOOP 2
Compute the second element's byte offset.

5147
ADD HL,DE 19
ADD DE to HL.
5148
DECrement B and loop back if not zero.
514A
POP DE D1
Restore Register Pair DE from the stack (the first element's computed address).
514B
ADD HL,DE 19
ADD DE to HL, combining the second element's offset with the base to get the element 2 absolute address.
514C
LD (IX+0FH),L DD 75 0F
Store Register L to IX+0FH, the low byte of the element offset field (repurposed here to hold the element 2 address during the sort).
514F
LD (IX+10H),H DD 74 10
Store Register H to IX+10H.
5152
RET C9
RETurn to the Array Walker Dispatch loop.

5153H – Forward Swap Routine

This routine performs a forward-direction element swap for one descriptor entry. It reads the element pointers from IX+0DH/0EH, calls the element address helper at 50DFH to perform a boundary-checked copy, then stores the updated pointers. The disassembler has garbled the IX store instructions in this section.

5153
LD L,(IX+0DH) DD 6E 0D
Fetch the low byte of the element pointer from IX+0DH into Register L.
5156
LD H,(IX+0EH) DD 66 0E
Fetch the high byte from IX+0EH into Register H. HL now holds the element 1 pointer for the forward swap.
5159
GOSUB to 50DFH, the boundary-checked copy portion of the element address helper. This performs the block copy of element data as part of the swap.
515C
LD (IX+0DH),L DD 75 0D
Store the updated low byte of HL (after the copy operation) back to IX+0DH.
515F
LD (IX+0EH),H DD 74 0E
Store the updated high byte back to IX+0EH.

The following bytes contain another garbled DD-prefixed instruction. The raw bytes at 5162H are DD 7E 16 = LD A,(IX+16H), not the standalone LD A,(HL) that the disassembler shows.

5162
LD A,(IX+16H) DD 7E 16
Fetch the swap destination status flag from IX+16H into Register A. A non-zero value indicates that a swap destination address has been stored and a boundary-checked copy was initiated.
5165
OR A B7
OR Register A with itself, testing whether the swap destination flag is zero.
5164
RET NZ C0
RETurn if the NZ FLAG has been set (the byte is non-zero — the swap status check passed).
5167
LD E,(IX+09H) DD 5E 09
Fetch the low byte of the current element 1 pointer from IX+09H into Register E.
516A
LD D,(IX+0AH) DD 56 0A
Fetch the high byte from IX+0AH into Register D. DE now holds the element 1 address.
516D
LD (IX+15H),E DD 73 15
Store Register E to IX+15H, the low byte of the swap destination address.
5170
LD (IX+16H),D DD 72 16
Store Register D to IX+16H, the high byte of the swap destination address.
5173
RET C9
RETurn to the Array Walker Dispatch.

5174H – Reverse Swap Routine

This routine performs a reverse-direction element swap for one descriptor entry. It reads the element pointer from IX+09H/0AH, calls the element address helper at 50D7H, and stores the updated pointers.

5174
LD L,(IX+09H) DD 6E 09
Fetch the low byte of the element 1 pointer from IX+09H into Register L.
5177
LD H,(IX+0AH) DD 66 0A
Fetch the high byte from IX+0AH into Register H.
517A
GOSUB to 50D7H, the element address helper with boundary check.
517D
LD (IX+09H),L DD 75 09
Store the updated low byte back to IX+09H.
5180
LD (IX+0AH),H DD 74 0A
Store the updated high byte back to IX+0AH.
5183
RET C9
RETurn to the Array Walker Dispatch.

5184H – Indirect Index Update / Block Swap Routine

This routine handles the bulk element swap for a descriptor entry. It computes the difference between the swap source (IX+0BH/0CH) and the current element pointer (IX+09H/0AH) to determine the block size, then copies the element data from the source to the destination using the 6608H FCB header + block copy routine in BASIC/CMD. It also handles the secondary block from IX+13H/14H to IX+11H/12H via LDIR. After the swap, it writes a status marker to IX+16H.

5184
LD L,(IX+0BH) DD 6E 0B
Fetch the low byte of the swap source address from IX+0BH into Register L.
5187
LD H,(IX+0CH) DD 66 0C
Fetch the high byte from IX+0CH into Register H. HL now holds the swap source address.
518A
LD E,(IX+09H) DD 5E 09
Fetch the low byte of the current element 1 pointer from IX+09H into Register E.
518D
LD D,(IX+0AH) DD 56 0A
Fetch the high byte from IX+0AH into Register D. DE now holds the element 1 address.
5190
OR A B7
Clear the CARRY FLAG.
5191
SBC HL,DE ED 52
SUBtract DE (element 1 address) from HL (swap source address). The result is the byte count (block size) of the first swap block.
5193
LD B,H 44
Copy Register H to Register B.
5194
LD C,L 4D
Copy Register L to Register C. BC now holds the block size.
5195
ADD HL,DE 19
ADD DE back to HL, restoring HL to the swap source address.
5196
LD E,(IX+0DH) DD 5E 0D
Fetch the low byte of the element size / second element pointer from IX+0DH into Register E.
5199
LD D,(IX+0EH) DD 56 0E
Fetch the high byte from IX+0EH into Register D.
519C
LD (IX+0BH),E DD 73 0B
Store Register E to IX+0BH, updating the swap source address for the next pass.
519F
LD (IX+0CH),D DD 72 0C
Store Register D to IX+0CH.
51A2
EX DE,HL EB
Exchange DE and HL. Now HL holds the second element pointer and DE holds the swap source.
51A3
OR A B7
Clear the CARRY FLAG.
51A4
SBC HL,BC ED 42
SUBtract BC (the block size) from HL (the second element pointer), computing the destination address for the block copy.
51A6
LD (IX+09H),L DD 75 09
Store Register L (the low byte of the computed destination address from the SBC result) to IX+09H, updating the element 1 pointer low byte. The high byte (H) is passed to the 6608H routine in the register, which reads it directly.
51A9
GOSUB to 6608H, the FCB header + block copy routine in BASIC/CMD. This routine performs the actual block data copy from the source to the destination, moving BC bytes of element data. Register H (the high byte of the destination address) is still valid from the SBC HL,BC result.
51AC
If the NZ FLAG has been set (an error or special condition was detected during the copy), JUMP forward to 51D0H to clear the swap status flag and return.

The first block copy succeeded. Now handle the secondary block: copy data from the swap temp area (IX+13H/14H) to the element 2 position (IX+11H/12H).

51AE
NOP 00
No operation (padding byte).
51AF
LD L,(IX+13H) DD 6E 13
Fetch the low byte of the swap temp pointer from IX+13H into Register L.
51B2
LD H,(IX+14H) DD 66 14
Fetch the high byte from IX+14H into Register H. HL now points to the swap temp area.
51B5
LD E,(IX+11H) DD 5E 11
Fetch the low byte of the element 2 pointer from IX+11H into Register E.
51B8
LD D,(IX+12H) DD 56 12
Fetch the high byte from IX+12H into Register D. DE now holds the element 2 destination.
51BB
OR A B7
Clear the CARRY FLAG.
51BC
SBC HL,DE ED 52
SUBtract DE from HL, computing the byte count for the secondary block.
51BE
LD B,H 44
Copy Register H to Register B.
51BF
LD C,L 4D
Copy Register L to Register C. BC now holds the secondary block size.
51C0
RET Z C8
RETurn if the Z FLAG has been set (the block size is zero — no secondary copy needed).
51C1
EX DE,HL EB
Exchange DE and HL. Now HL holds the element 2 destination and DE holds the difference.
51C2
LD (IX+13H),L DD 75 13
Store Register L to IX+13H, updating the swap temp pointer low byte.
51C5
LD (IX+14H),H DD 74 14
Store Register H to IX+14H, updating the swap temp pointer high byte.
51C8
LD E,(IX+15H) DD 5E 15
Fetch the low byte of the swap destination from IX+15H into Register E.
51CB
LD D,(IX+16H) DD 56 16
Fetch the high byte from IX+16H into Register D. DE now holds the swap destination address.
51CE
LDIR ED B0
Execute the LDIR block copy: copy BC bytes from (HL) to (DE), incrementing both HL and DE, decrementing BC until zero. This completes the secondary block of the element swap.
51D0
LD (IX+16H),00H DD 36 16 00
Store the immediate value 00H to IX+16H, clearing the swap destination status flag. This marks the swap as complete for this descriptor entry. (The disassembler incorrectly showed the immediate value as 16H; the raw bytes DD 36 16 00 decode as: DD 36 = LD (IX+d),n opcode, 16 = offset 16H, 00 = immediate value 00H.)
51D4
RET C9
RETurn to the Array Walker Dispatch.

51D5H – Pass Advance Routine

This routine advances the element pointers for a descriptor entry to the next pair for the next comparison iteration. It copies IX+07H/08H (the array element address) to IX+0FH/10H (the element offset). The disassembler has garbled the DD-prefixed instructions.

51D5
LD L,(IX+07H) DD 6E 07
Fetch the low byte of the array element address from IX+07H into Register L.
51D8
LD H,(IX+08H) DD 66 08
Fetch the high byte from IX+08H into Register H.
51DB
LD (IX+0FH),L DD 75 0F
Store Register L to IX+0FH, the low byte of the element offset field.
51DE
LD (IX+10H),H DD 74 10
Store Register H to IX+10H, the high byte of the element offset field.
51E1
RET C9
RETurn to the Array Walker Dispatch.

51E2H – NOP Padding

Unused area filled with 00H bytes to pad the SYS21 overlay to its full allocated size of 4E8H bytes (4D00H–51E7H).

51E2–51E7
NOP × 6 00 × 6
Six bytes of NOP (00H) padding. This unused area fills the remainder of the SYS21 overlay from 51E2H to 51E7H, the end of the allocated overlay space.