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

Page Customization

Introduction/Summary

NEWDOS/80 v2.0 SYS21/SYS Disassembly - BASIC CMD"O" Array Sort, ERASE and KEEP (Model I)

SYS21/SYS is a BASIC overlay that loads into the DOS overlay area at 4D00H-51E7H (1,256 bytes in five load records, transfer address 4D00H); 51E2H-51E7H are unused zero bytes. It does two jobs for BASIC/CMD: it sorts arrays for CMD"O", and it deletes variables for CMD"F=ERASE" and CMD"F=KEEP". Several of the routines it calls lie in a patch area of BASIC/CMD (65E0H-6647H) that exists only for this overlay.

CMD"O",n,[*]array(start)[,[-]array(start)...] sorts n elements of one or more arrays, starting at the subscript given for each. The first array is the first sort key and each later array breaks ties left by the ones before it; a minus sign sorts that key in descending order; a string array may be followed by (position,length) to compare only part of each string. All the arrays are moved together, like the columns of one table. With * before the first array (which must then be an integer array) only that array is rearranged: it is filled with the element numbers and sorted by the keys that follow, which stay where they are. The sort is a merge sort that works in place, with a 1,280-byte buffer in the BASIC overlay area at 5200H-56FFH; it keeps equal elements in their original order.

CMD"F=ERASE",name,... deletes the named simple variables and arrays (an array is written with parentheses, A()); CMD"F=KEEP",name,... deletes all the others. User functions (DEF FN) are always kept.

The page shows every byte of the file once, in address order. Calls into BASIC/CMD are linked only where the BASIC page has a row with the right bytes at that address.

Entry Points

RST 28H codeFrom BASICStatement
37H57B3HCMD"O" (sort, 4DBAH).
57H, B = 28H57AAHCMD"F=ERASE" (4D0EH).
57H, B = 20H57AEHCMD"F=KEEP" (4D0EH).

Any other code returns DOS error 2AH through BASIC 5E16H. ERASE and KEEP are reached from SYS20's CMD"F=" table search, which finds their addresses in BASIC's table at 5952H.

Variables

Locations inside SYS21 whose contents change while it runs (operands written by the code itself):

Address RangePurpose
4D8DH
1 byte
Opcode of the keep test in the close-up: 28H (JR Z) for ERASE or 20H (JR NZ, the value in the file) for KEEP. Written at 4D0FH.
4EECH-4EEDH
2 bytes
Start subscript of the first sort key. Written at 4E6EH; read at 4E73H (all keys must start there) and at 4EEBH (first value put into the index array).
4F02H
1 byte
Length in bytes of one table row: the sum of the element lengths of the arrays that are moved. 00H in the file; added up at 4E81H through BASIC 6617H; read at 4F01H.
4F27H-4F28H
2 bytes
n, the number of elements to sort. Written at 4DEBH (and at 4EA4H when n was 0); read at 4E96H, 4EF1H and 4F26H.
4F39H-4F3AH
2 bytes
Run length of the current pass (1, 2, 4 ...). Written at 4F23H; read at 4F38H.
4F40H-4F41H
2 bytes
Elements not yet merged in the current pass. Written at 4F29H and 4F57H; read at 4F3FH.
507EH-507FH
2 bytes
Buffer count: rows that may still be taken before the buffer must be emptied. Written at 4F19H and 508AH; counted down at 507DH-5081H.
5088H-5089H
2 bytes
Rows the 1,280-byte buffer holds (1,280 divided by the row length). Written at 4F09H; read at 4F16H and 5087H.
50FFH-5100H
2 bytes
Address of the routine 50EEH calls for each array (510CH, 511DH, 5153H, 5174H, 5184H or 51D5H). 0000H in the file; written at 50EEH.
5102H
1 byte
Index-sort flag: 00H in the file, 01H when the first array had a *. Written at 4E04H; read at 4E5EH, 4ED6H, 5096H and 5101H.
5124H-5125H
2 bytes
Elements left in run A of the pair being merged. Written at 4F3BH; counted down at 5066H-506AH; read at 5123H.
5145H-5146H
2 bytes
Elements left in run B of the pair being merged. Written at 4F53H; counted down at 5043H-5047H; read at 5144H.

Locations outside SYS21 that it reads or writes:

Address RangePurpose
4101H-411AH
26 bytes
The ROM's DEFINT/DEFSNG/DEFDBL/DEFSTR table, one type byte per letter A-Z; read by BASIC 5BDDH for a name without a type suffix.
40AFH
1 byte
The ROM's type flag of the value or variable just handled: 02H integer, 03H string, 04H single, 08H double precision. Read at 4E15H after PTRGET.
40F9H-40FAH
2 bytes
The ROM's pointer to the start of the simple variable table. Read at 4D4DH and 4D67H.
40FBH-40FCH
2 bytes
The ROM's pointer to the start of the array table (the end of the simple variables). Read at 4D2EH, 4D6DH and 4E26H; written at 4D94H after the close-up.
40FDH-40FEH
2 bytes
The ROM's pointer to the end of the array table (the start of free memory). Read at 4D3BH, 4D99H and 4E29H; written at 4DB3H after the close-up.
4200H-42D0H
209 bytes
Nine 23-byte array blocks for CMD"O" (layout below) and the two zero bytes of a tenth block that end the table. Built at 4DBAH-4DE0H.
4317H
1 byte
SYS0's record of the overlay now in 4D00H (its directory slot). Cleared at 4DBBH so SYS21 is loaded fresh next time.
5200H-56FFH
1,280 bytes
BASIC's overlay area, used by the sort as its buffer.
576EH
1 byte
BASIC/CMD's record (operand of CP at 576DH) of the RST 28H code of the overlay now in 5200H. Cleared at 4DBEH because the sort overwrites that area.
57B8H-57B9H
2 bytes
BASIC/CMD's record of the line BASIC 5D9FH has stepped to, written when an ERASE/KEEP list continues on the next line.
644EH-644FH
2 bytes
Operand of the CALL at BASIC 644DH in BASIC's statement-end clean-up; normally 64BDH (a RET). Set to 4D67H at 4D15H so an error during ERASE/KEEP still closes up the tables; BASIC 64BEH puts back 64BDH.

The Array Blocks at 4200H

CMD"O" builds one 23-byte block per array at 4200H, 4217H, 422EH ... (nine blocks, then a block number 0 and type 0 at 42CFH-42D0H):

OffsetContents
+00HBlock number 1-9 (0 after the ninth).
+01HElement type, which is also the element length: 2 integer, 3 string, 4 single, 8 double precision; 0 = block not used.
+02H, +03HString keys: the character position where the compare starts and the number of characters compared (1 and FFH unless given).
+04HBit 7: descending key (-). Bit 6: the same array as an earlier key, so its data is not moved twice.
+05H/+06HAddress of the array's element 0.
+07H/+08HAddress of the first element sorted.
+09H/+0AH, +0BH/+0CHRun A: the current element and the end.
+0DH/+0EH, +0FH/+10HRun B: the current element and the end (which is also where the next pair starts).
+11H/+12H, +13H/+14HThis array's part of the buffer at 5200H: its start, and the pointer to the next free byte.
+15H/+16HWhere the buffer is copied back to; a high byte of 0 means the buffer is not in use.

Disassembly

4D00H - Entry Point: Select CMD"O", ERASE or KEEP

The SYS0 overlay dispatcher loads SYS21 into 4D00H-51E7H and calls 4D00H with Register A holding the RST 28H code that BASIC/CMD issued. Code 37H (function 1 of directory slot 17H) comes from BASIC 57B3H for CMD"O", the array sort. Code 57H (function 2 of slot 17H) comes from BASIC 57B0H for CMD"F=ERASE" (Register B = 28H, loaded at BASIC 57AAH) and CMD"F=KEEP" (Register B = 20H, loaded at BASIC 57AEH). In every case Register Pair HL holds BASIC's text pointer, pointing just past the CMD string in the program line.

4D00
INC SP 33
Increment the Stack Pointer SP by 1. The word on top of the stack is the return address into the SYS0 dispatcher; this is its first byte.
4D01
INC SP 33
Increment the Stack Pointer SP again. The SYS0 return address is now dropped, so a RET in this overlay returns straight to BASIC/CMD, just after its RST 28H.
4D02
CP 37H FE 37
Compare Register A (the RST 28H code from BASIC/CMD) against 37H, the code BASIC 57B3H issues for CMD"O". The Z FLAG is set if they match.
4D04
If the Z FLAG is set (Register A = 37H, CMD"O"), JUMP to 4DBAH to parse the array list and sort.
4D07
CP 57H FE 57
Compare Register A (the RST 28H code) against 57H, the code BASIC 57B0H issues for CMD"F=ERASE" and CMD"F=KEEP". The Z FLAG is set if they match.
4D09
LD A,2AH 3E 2A
Load Register A with 2AH, the DOS error code ILLEGAL DOS FUNCTION, in case the code was neither 37H nor 57H. LD does not change the flags, so the Z FLAG from the compare at 4D07H is kept.
4D0B
JP NZ,5E16H C2 16 5E
If the NZ FLAG is set (the code was neither 37H nor 57H), JUMP to BASIC 5E16H, which turns the DOS error code in Register A (2AH) into a BASIC error (DOS ERROR) and stops the statement.

4D0EH - CMD"F=ERASE" and CMD"F=KEEP": Mark the Named Variables

CMD"F=ERASE",name,name... deletes the named variables; CMD"F=KEEP",name,name... deletes every variable except the named ones. A name is a simple variable (A, B$, X%) or an array, written with parentheses (A(); anything may stand between them). Each variable found gets bit 4 of its type byte set as a mark; the close-up at 4D67H then copies the kept variables down over the deleted ones. User functions (DEF FN, whose entries have bit 7 set in the name's first character) are never deleted. A comma at the end of a program line continues the list on the next line; the rest of that line may be a REMark, and whole REM lines in between are skipped.

4D0E
LD A,B 78
Load Register A with Register B, the switch BASIC/CMD passed: 28H for ERASE (from BASIC 57AAH) or 20H for KEEP (from BASIC 57AEH). Both values are Z80 opcodes: 28H is JR Z and 20H is JR NZ.
4D0F
LD (4D8DH),A 32 8D 4D
Self-Modifying Code
Store Register A (28H for ERASE, 20H for KEEP) at 4D8DH, the opcode byte of the conditional jump in the keep-or-drop test at 4D89H. With JR Z an unmarked variable is kept (ERASE); with JR NZ a marked variable is kept (KEEP).
4D12
LD BC,4D67H 01 67 4D
Load Register Pair BC with 4D67H, the address of the close-up routine in this overlay.
4D15
LD (644EH),BC ED 43 4E 64
Store Register Pair BC (4D67H) at 644EH, the operand of BASIC/CMD's CALL 64BDH at 644DH. That CALL sits in BASIC's statement-end clean-up at 6445H, which also runs when an error ends the statement; 64BDH itself is only a RET. From now on the clean-up calls 4D67H, so if a name in the list causes an error the marks already set are still acted on and cleared.
4D19
DEC HL 2B
Decrement Register Pair HL (BASIC's text pointer) by one, so that the RST 10H that follows fetches the character HL was pointing at again.
4D1A
RST 10H D7
RST 10H (the ROM's CHRGET at 1D78H): increment Register Pair HL (the text pointer) and load Register A with the next non-blank character of the BASIC line. The Z FLAG is set if it is 00H (end of line) or a colon (end of statement).
4D1B
If the Z FLAG is set (the statement ends right after the CMD string, so there are no names), JUMP to 4D62H to run the close-up with nothing marked.

NAME LOOP
Each pass handles one name of the list. Register Pair HL is BASIC's text pointer throughout; it is saved on the stack while the variable tables are searched.

4D1D
RST 08H CF
RST 08H (the ROM's syntax check): the character at Register Pair HL (the text pointer) must equal the byte that follows this instruction (a comma); if it does not, the ROM reports SYNTAX ERROR. On a match RST 08H continues into RST 10H, so HL moves on and Register A holds the next character, with the Z FLAG set at the end of the line or statement.
4D1E
DEFB 2CH 2C
Data byte 2CH (a comma), the character the RST 08H at 4D1DH requires at the text pointer. RST 08H returns past this byte, to 4D1FH.
4D1F
If the NZ FLAG is set (a character follows the comma on this line, so it is a name), JUMP to 4D2BH to look the name up.
4D21
CALL 5D9FH CD 9F 5D
The comma ended the line (or statement): GOSUB to BASIC 5D9FH. It requires the rest of the line to be empty or a REM (anything else is SYNTAX ERROR), moves Register Pair HL to the next program line (SYNTAX ERROR if there is none), stores that line's number at 57B8H-57B9H (BASIC/CMD's record of the line it has stepped to, which SYS19's MERGE also reads), and returns with HL on the last byte of the line number.
4D24
RST 10H D7
RST 10H: increment Register Pair HL (the text pointer) and load Register A with the first non-blank character of the new line's text. The Z FLAG is set if the line is empty or starts with a colon.
4D25
If the Z FLAG is set (nothing on this line), LOOP back to 4D21H to step to the line after it.
4D27
CP 93H FE 93
Compare Register A (the first character of the line) against 93H, the BASIC token for REM.
4D29
If the Z FLAG is set (the line is a REM line), LOOP back to 4D21H; 5D9FH accepts the REM, skips the rest of the line and steps to the next one. Any other character is taken as the next name.
4D2B
GOSUB to BASIC 5BDDH with Register A holding the first letter of the name and Register Pair HL pointing at it. It reads the name (SYNTAX ERROR if it does not start with a letter), works out its type from a %, $, ! or # suffix or else from the DEFINT/DEFSTR table at 4101H-411AH, and stores the type, the second character and the first letter into the compare operands at 5C25H, 5C2AH and 5C30H of BASIC's name-compare routine 5C21H. It returns with Register A holding the character after the name and the Z FLAG set if that character is an opening parenthesis (an array).
4D2E
LD DE,(40FBH) ED 5B FB 40
Load Register Pair DE with the 16-bit value at 40FBH, the ROM's pointer to the start of the array table (which is also the end of the simple variable table). LD does not change the flags, so the Z FLAG from 5BDDH is kept.
4D32
If the NZ FLAG is set (no parenthesis follows, so the name is a simple variable), JUMP to 4D4CH to search the simple variables.
4D34
The name is an array: GOSUB to BASIC 5C48H, which moves Register Pair HL (the text pointer) past the parenthesised part, counting nested parentheses, and returns with Register A holding the character after the closing parenthesis. The CARRY FLAG is set if the statement ended before the parentheses closed.
4D37
If the CARRY FLAG is set (the parentheses were not closed), JUMP to the ROM's SNERR at 1997H to report SYNTAX ERROR.
4D3A
PUSH HL E5
Save Register Pair HL (the text pointer, just past the array name's parentheses) on the stack while the array table is searched.
4D3B
LD HL,(40FDH) 2A FD 40
Load Register Pair HL with the 16-bit value at 40FDH, the ROM's pointer to the end of the array table (the first byte of free memory).
4D3E
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the start of the array table (from 40FBH) and DE the end of the array table (from 40FDH).

ARRAY SEARCH LOOP
Register Pair HL walks from array to array; Register Pair DE holds the end of the array table.

4D3F
RST 18H DF
RST 18H (the ROM's 16-bit compare): compare Register Pair HL (the current array entry) with Register Pair DE (the end of the array table). The Z FLAG is set if they are equal.
4D40
If the Z FLAG is set (the end of the table is reached and the array does not exist), JUMP to 4D5DH; nothing is marked for this name.
4D42
CALL 5C21H CD 21 5C
GOSUB to BASIC 5C21H, which compares the entry at Register Pair HL (its type byte, second character and first character) with the name read by 5BDDH. It returns with the Z FLAG set on a match and HL unchanged.
4D45
If the Z FLAG is set (this array is the one named), JUMP to 4D5BH to mark it.
4D47
GOSUB to BASIC 5C3FH, which adds the array's length word (at entry +3) plus 5 to Register Pair HL, so HL points at the next array entry.
4D4A
LOOP back to 4D3FH to test the next array entry, now at Register Pair HL.
4D4C
PUSH HL E5
The name is a simple variable: save Register Pair HL (the text pointer, just past the name) on the stack while the simple variable table is searched.
4D4D
LD HL,(40F9H) 2A F9 40
Load Register Pair HL with the 16-bit value at 40F9H, the ROM's pointer to the start of the simple variable table. Register Pair DE still holds the start of the array table (from 40FBH), which is the end of the simple variables.

SIMPLE VARIABLE SEARCH LOOP
Register Pair HL walks from variable to variable; Register Pair DE holds the end of the simple variable table.

4D50
RST 18H DF
RST 18H: compare Register Pair HL (the current variable entry) with Register Pair DE (the end of the simple variable table). The Z FLAG is set if they are equal.
4D51
If the Z FLAG is set (the end is reached and the variable does not exist), JUMP to 4D5DH; nothing is marked.
4D53
CALL 5C21H CD 21 5C
GOSUB to BASIC 5C21H to compare the variable at Register Pair HL with the name read by 5BDDH. The Z FLAG is set on a match.
4D56
If the NZ FLAG is set (not this variable), GOSUB to BASIC 5C34H, which adds the entry length ((type AND 0FH) + 3) to Register Pair HL so it points at the next variable, and returns with the NZ FLAG set.
4D59
If the NZ FLAG is set (no match yet), LOOP back to 4D50H to test the next variable. On a match the Z FLAG is still set and execution falls through.
4D5B
SET 4,(HL) CB E6
Set bit 4 of the byte at Register Pair HL, the type byte of the variable or array just found. Bit 4 is the mark that the close-up at 4D89H tests.
4D5D
POP HL E1
Restore Register Pair HL from the stack: BASIC's text pointer, just past the name, saved at 4D3AH or 4D4CH.
4D5E
DEC HL 2B
Decrement Register Pair HL (the text pointer) so that the RST 10H that follows fetches the character after the name again.
4D5F
RST 10H D7
RST 10H: increment Register Pair HL and load Register A with the character after the name. The Z FLAG is set at the end of the line or statement.
4D60
If the NZ FLAG is set (more of the statement follows), LOOP back to 4D1DH, where a comma and the next name must follow.
4D62
PUSH HL E5
The list is finished: save Register Pair HL (BASIC's text pointer, at the end of the statement) on the stack.
4D63
LD HL,5881H 21 81 58
Load Register Pair HL with 5881H, the address of a POP HL / RET in BASIC/CMD.
4D66
PUSH HL E5
Push Register Pair HL (5881H) as a return address. The close-up below ends in a RET (at BASIC 64BEH), which goes to BASIC 5881H; that pops the text pointer saved at 4D62H into HL and returns to BASIC.

4D67H - Close Up the Variable Tables

Copies every simple variable and every array that is kept down over those that are dropped, clears the marks, and stores the new ends of the tables at 40FBH and 40FDH. Register Pair HL is the source (the entry being looked at) and Register Pair DE the destination (where the next kept entry goes). The test at 4D89H decides for each entry: for ERASE (JR Z at 4D8DH) an unmarked entry is kept, for KEEP (JR NZ) a marked one. User functions (bit 7 of the first name character) are always kept. This routine is also reached from BASIC's statement-end clean-up (CALL at 644DH, set at 4D15H) when an error ends the statement. It ends by jumping to BASIC 64BEH, which puts that CALL back to 64BDH.

4D67
LD HL,(40F9H) 2A F9 40
Load Register Pair HL with the 16-bit value at 40F9H, the ROM's pointer to the start of the simple variable table. HL is the source pointer.
4D6A
LD D,H 54
Load Register D with Register H (the high byte of the start of the simple variables).
4D6B
LD E,L 5D
Load Register E with Register L. Register Pair DE now also holds the start of the simple variable table: it is the destination pointer, where the first kept variable goes.

SIMPLE VARIABLE CLOSE-UP LOOP
Register Pair HL = the next variable to look at, Register Pair DE = where the next kept variable is copied to.

4D6C
PUSH DE D5
Save Register Pair DE (the destination pointer) on the stack, because DE is needed for the compare.
4D6D
LD DE,(40FBH) ED 5B FB 40
Load Register Pair DE with the 16-bit value at 40FBH, the ROM's pointer to the start of the array table, which is the end of the simple variables.
4D71
RST 18H DF
RST 18H: compare Register Pair HL (the source pointer) with Register Pair DE (the end of the simple variables). The Z FLAG is set if all simple variables are done.
4D72
POP DE D1
Restore Register Pair DE (the destination pointer) from the stack. POP does not change the flags.
4D73
If the Z FLAG is set (every simple variable is done), JUMP to 4D94H to store the new end and close up the arrays.
4D75
LD BC,4D6CH 01 6C 4D
Load Register Pair BC with 4D6CH, the top of this loop.
4D78
PUSH BC C5
Push Register Pair BC (4D6CH) as a return address, so the RET at 4D90H or 4D93H goes back to the top of the loop.
4D79
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the type byte of this variable (2 integer, 3 string, 4 single, 8 double precision in the low 4 bits; bit 4 is the mark).
4D7A
AND 0FH E6 0F
AND Register A with 0FH, keeping the type in the low 4 bits (the length of the value in bytes) and dropping the mark.
4D7C
ADD A,03H C6 03
Add 03H to Register A, for the type byte and the two name bytes. Register A now holds the length of the whole entry.
4D7E
LD C,A 4F
Load Register C with Register A, the length of the entry.
4D7F
LD B,00H 06 00
Load Register B with 00H, so Register Pair BC holds the entry length as a 16-bit count for LDIR or ADD.
4D81
INC HL 23
Increment Register Pair HL; it now points at the entry's second name character (entry +1).
4D82
INC HL 23
Increment Register Pair HL again; it now points at the entry's first name character (entry +2).
4D83
BIT 7,(HL) CB 7E
Test bit 7 of the byte at Register Pair HL, the first name character. The bit is set in the entries of user functions (DEF FN); the Z FLAG is clear if it is set.
4D85
DEC HL 2B
Decrement Register Pair HL back to entry +1, the second name character.
4D86
DEC HL 2B
Decrement Register Pair HL back to entry +0, the type byte. DEC of a register pair does not change the flags.
4D87
If the NZ FLAG is set (a user function), JUMP to 4D91H to keep it.
4D89
BIT 4,(HL) CB 66
Test bit 4 of the byte at Register Pair HL, the type byte of the entry (the mark set at 4D5BH). The Z FLAG is set if the entry is not marked. The array close-up also calls in here, from 4DAEH, with Register Pair BC holding the array's full length.
4D8B
RES 4,(HL) CB A6
Reset bit 4 of the byte at Register Pair HL, clearing the mark. RES does not change the flags, so the Z FLAG from the test is kept.
4D8D
Self-Modifying Code
This opcode is written at 4D0FH: JR Z (28H) for ERASE or JR NZ (20H) for KEEP; 20H is the value in the file. JR Z jumps for an unmarked entry, JR NZ for a marked one; either way the JUMP goes to 4D91H to keep the entry.
4D8F
ADD HL,BC 09
The entry is dropped: add Register Pair BC (the entry length) to Register Pair HL, so HL (the source pointer) skips it. Register Pair DE (the destination) does not move.
4D90
RET C9
RETURN to the address pushed at 4D78H (4D6CH, the next simple variable) or, when called from 4DAEH, to 4DB1H.
4D91
LDIR ED B0
The entry is kept: LDIR copies BC bytes (the whole entry) from Register Pair HL to Register Pair DE. Afterwards HL points at the next entry, DE just past the copy, and BC is 0.
4D93
RET C9
RETURN to 4D6CH (the next simple variable) or, when called from 4DAEH, to 4DB1H.
4D94
LD (40FBH),DE ED 53 FB 40
All simple variables are done: store Register Pair DE (the end of the kept simple variables) at 40FBH, the ROM's pointer to the start of the array table. The arrays are copied down to start there.

ARRAY CLOSE-UP LOOP
Register Pair HL = the next array to look at, Register Pair DE = where the next kept array is copied to.

4D98
PUSH DE D5
Save Register Pair DE (the destination pointer) on the stack, because DE is needed for the compare.
4D99
LD DE,(40FDH) ED 5B FD 40
Load Register Pair DE with the 16-bit value at 40FDH, the ROM's pointer to the end of the array table (still the old end).
4D9D
RST 18H DF
RST 18H: compare Register Pair HL (the source pointer) with Register Pair DE (the end of the array table). The Z FLAG is set if all arrays are done.
4D9E
POP DE D1
Restore Register Pair DE (the destination pointer) from the stack.
4D9F
If the Z FLAG is set (every array is done), JUMP to 4DB3H to store the new end of the arrays.
4DA1
PUSH HL E5
Save Register Pair HL (the start of this array entry) on the stack.
4DA2
INC HL 23
Increment Register Pair HL to entry +1 (the second name character).
4DA3
INC HL 23
Increment Register Pair HL to entry +2 (the first name character).
4DA4
INC HL 23
Increment Register Pair HL to entry +3, the low byte of the array's length word (the number of bytes that follow the length word).
4DA5
LD C,(HL) 4E
Load Register C with the byte at Register Pair HL, the low byte of the array's length.
4DA6
INC HL 23
Increment Register Pair HL to entry +4, the high byte of the length.
4DA7
LD B,(HL) 46
Load Register B with the byte at Register Pair HL. Register Pair BC now holds the array's length word.
4DA8
INC BC 03
Increment Register Pair BC (the length): +1 of 5, for the type byte.
4DA9
INC BC 03
Increment Register Pair BC: +2, for the second name character.
4DAA
INC BC 03
Increment Register Pair BC: +3, for the first name character.
4DAB
INC BC 03
Increment Register Pair BC: +4, for the low byte of the length word.
4DAC
INC BC 03
Increment Register Pair BC: +5, for the high byte. Register Pair BC now holds the length of the whole array entry.
4DAD
POP HL E1
Restore Register Pair HL from the stack: the start of this array entry (its type byte).
4DAE
GOSUB to 4D89H with Register Pair HL at the array's type byte, Register Pair DE at the destination and Register Pair BC holding the entry length: the array is kept (copied down to DE) or dropped (skipped), and its mark is cleared. HL returns pointing at the next array.
4DB1
LOOP back to 4D98H for the next array, now at Register Pair HL.
4DB3
LD (40FDH),DE ED 53 FD 40
Store Register Pair DE (the end of the kept arrays) at 40FDH, the ROM's pointer to the end of the array table (the start of free memory).
4DB7
JP 64BEH C3 BE 64
JUMP to BASIC 64BEH, which stores 64BDH back at 644EH (so BASIC's statement-end CALL at 644DH does nothing again) and returns: to BASIC 5881H (pushed at 4D66H) after ERASE or KEEP, or into BASIC's clean-up after an error.

4DBAH - CMD"O": Build the Array Blocks

CMD"O",n,[*]array(start)[,[-]array(start)...] sorts n elements of the arrays from the start subscript given (n = 0 sorts the rest of the first array). The first array is the first sort key; each array after it breaks ties left by the ones before. A minus sign before an array sorts on it in descending order. All arrays are moved together, as the columns of one table. With * before the first array, that array must be an integer array: it is filled with the element numbers of the keys (start to start+n-1) and only it is rearranged, by the keys that follow (an index sort; the keys stay where they are). A string array may be followed by (position,length) to compare only that part of each string. At most 9 arrays. The routine first builds nine 23-byte blocks at 4200H, one per array; the table "The Array Blocks at 4200H" at the top of the page gives their layout.

4DBA
XOR A AF
Load Register A with 00H (XOR A with itself). This also clears the CARRY FLAG.
4DBB
LD (4317H),A 32 17 43
Store Register A (00H) at 4317H, the SYS0 byte that holds the directory slot of the overlay now in 4D00H-51FFH. With it cleared SYS0 treats the overlay area as empty and reloads SYS21 from disk next time; needed because the sort changes operands inside this overlay's code.
4DBE
LD (576EH),A 32 6E 57
Store Register A (00H) at 576EH, the operand of the CP 00H at BASIC 576DH, which holds the RST 28H code of the BASIC overlay (SYS18, SYS19 or SYS20) now in 5200H. The sort uses 5200H-56FFH as its buffer, so BASIC must load its overlay again next time.
4DC1
PUSH HL E5
Save Register Pair HL (BASIC's text pointer, just past the CMD"O" string) on the stack while the blocks are built.
4DC2
LD HL,4200H 21 00 42
Load Register Pair HL with 4200H, the address of the first array block. (4200H-42FFH is the DOS sector buffer area, free while the sort runs.)
4DC5
LD C,09H 0E 09
Load Register C with 09H, the number of blocks to build.
4DC7
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H. Register E counts the block numbers (incremented before each block); Register D stays 00H and is the fill byte.

BLOCK BUILD LOOP
Register Pair HL = the next byte to write, Register C = the blocks left, Register E = the block number, Register D = 00H.

4DCA
INC E 1C
Increment Register E, the block number: 01H for the first block, up to 09H.
4DCB
LD (HL),E 73
Store Register E (the block number) at Register Pair HL, byte +00H of the block.
4DCC
INC HL 23
Increment Register Pair HL to byte +01H of the block.
4DCD
LD (HL),D 72
Store Register D (00H) at Register Pair HL: byte +01H, the element type, is 0 = block not used (yet).
4DCE
INC HL 23
Increment Register Pair HL to byte +02H of the block.
4DCF
LD (HL),01H 36 01
Store 01H at Register Pair HL: byte +02H, the character position where a string compare starts, defaults to 1.
4DD1
INC HL 23
Increment Register Pair HL to byte +03H of the block.
4DD2
LD (HL),FFH 36 FF
Store FFH at Register Pair HL: byte +03H, the number of string characters compared, defaults to 255 (the whole string).
4DD4
INC HL 23
Increment Register Pair HL to byte +04H of the block.
4DD5
LD B,13H 06 13
Load Register B with 13H (19), the number of bytes from +04H to +16H that are cleared.
4DD7
LD (HL),D 72
Store Register D (00H) at Register Pair HL, clearing one byte of the block.
4DD8
INC HL 23
Increment Register Pair HL to the next byte of the block.
4DD9
Decrement Register B (the bytes left to clear) and LOOP back to 4DD7H until it reaches zero.
4DDB
DEC C 0D
Decrement Register C, the number of blocks left to build.
4DDC
If the NZ FLAG is set (Register C is not zero, blocks remain), LOOP back to 4DCAH to build the next block.
4DDE
LD (HL),D 72
Store Register D (00H) at Register Pair HL (42CFH): byte +00H of a tenth block, block number 0, which ends the table.
4DDF
INC HL 23
Increment Register Pair HL from 42CFH to 42D0H, byte +01H of the tenth block.
4DE0
LD (HL),D 72
Store Register D (00H) at Register Pair HL (42D0H): byte +01H of the tenth block, type 0, so the loops that stop at a type of 0 stop here.
4DE1
POP HL E1
Restore Register Pair HL from the stack: BASIC's text pointer, just past the CMD"O" string.
4DE2
RST 08H CF
RST 08H: the character at Register Pair HL (the text pointer) must be the byte that follows (a comma), else SYNTAX ERROR. On a match RST 08H continues into RST 10H, so HL moves to the next character and Register A holds it.
4DE3
DEFB 2CH 2C
Data byte 2CH (a comma), the character the RST 08H at 4DE2H requires after CMD"O". RST 08H returns past this byte, to 4DE4H.
4DE4
GOSUB to the ROM's INTID2 at 1E46H, which evaluates the expression at Register Pair HL (the count n) and returns it as an integer in Register Pair DE; a negative value gives ILLEGAL FUNCTION CALL. HL is left on the character after the expression.
4DE7
LD IX,4200H DD 21 00 42
Load Index Register IX with 4200H, the first array block. IX points at the block for the array being parsed.
4DEB
LD (4F27H),DE ED 53 27 4F
Store Register Pair DE (n, the number of elements to sort) at 4F27H, the operand of the LD DE,nnnnH at 4F26H, where the sort and the checks below read it.

ARRAY LIST LOOP
One pass per array named. Index Register IX = the block for this array, Register Pair HL = BASIC's text pointer.

4DEF
LD A,(IX+00H) DD 7E 00
Load Register A with byte +00H of the block at Index Register IX, its block number (1-9, or 0 for the end marker after block 9).
4DF2
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if the block number is 0.
4DF3
If the Z FLAG is set (a tenth array was named), JUMP to the ROM's SNERR at 1997H to report SYNTAX ERROR.
4DF6
RST 08H CF
RST 08H: the character at Register Pair HL (the text pointer) must be the byte that follows (a comma), else SYNTAX ERROR. On a match RST 08H continues into RST 10H, so Register A holds the first character of the array item.
4DF7
DEFB 2CH 2C
Data byte 2CH (a comma), the character the RST 08H at 4DF6H requires before each array. RST 08H returns past this byte, to 4DF8H.
4DF8
CP CFH FE CF
Compare Register A (the first character of the array item) against CFH, the BASIC token for * (multiply), which marks the index sort.
4DFA
If the NZ FLAG is set (no *), JUMP to 4E09H to check for a minus sign.
4DFC
LD A,(IX+00H) DD 7E 00
Load Register A with byte +00H of the block at Index Register IX, the block number of this array.
4DFF
CP 01H FE 01
Compare Register A (the block number) against 01H: the * is only allowed before the first array.
4E01
If the NZ FLAG is set (the * is on a later array), JUMP to the ROM's SNERR at 1997H to report SYNTAX ERROR.
4E04
LD (5102H),A 32 02 51
Self-Modifying Code
Store Register A (01H) at 5102H, the operand of the LD A,00H at 5101H: the index-sort flag, 00H in the file, 01H from here on. The loops at 4E5EH, 4ED6H, 5096H and 5101H read it.
4E07
JUMP to 4E10H to step past the * and read the array name.
4E09
CP CEH FE CE
Compare Register A (the first character of the array item) against CEH, the BASIC token for - (minus), which marks a descending key.
4E0B
If the NZ FLAG is set (no minus sign), JUMP to 4E11H; Register Pair HL already points at the array name.
4E0D
CALL 65E0H CD E0 65
GOSUB to BASIC 65E0H, which sets bit 7 of byte +04H of the block at Index Register IX: this key is compared in descending order.
4E10
RST 10H D7
RST 10H: move Register Pair HL (the text pointer) past the * or minus sign to the array name; Register A holds its first character.
4E11
PUSH HL E5
Save Register Pair HL (the text pointer at the array name) on the stack; the name is read a second time at 4E23H.
4E12
GOSUB to the ROM's PTRGET at 260DH, which reads the array name and subscript at Register Pair HL and returns in Register Pair DE the address of that element (the first one to sort), with HL past the closing parenthesis.
4E15
LD A,(40AFH) 3A AF 40
Load Register A with the byte at 40AFH, the ROM's type flag for the variable PTRGET just found: 02H integer, 03H string, 04H single, 08H double precision. The value is also the length of one element in bytes.
4E18
LD (IX+01H),A DD 77 01
Store Register A (the type, which is also the element length) in byte +01H of the block at Index Register IX. A non-zero type marks the block as used.
4E1B
LD (IX+07H),E DD 73 07
Store Register E (the low byte of the first element's address) in byte +07H of the block at Index Register IX.
4E1E
LD (IX+08H),D DD 72 08
Store Register D (the high byte) in byte +08H of the block at Index Register IX. Bytes +07H/+08H of the block now hold the address of the first element to sort.
4E21
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (at the Stack Pointer SP): HL now holds the text pointer at the array name (saved at 4E11H), and the text pointer after the subscript is saved in its place.
4E22
PUSH DE D5
Save Register Pair DE (the first element's address) on the stack.
4E23
CALL 5BDBH CD DB 5B
GOSUB to BASIC 5BDBH, which steps Register Pair HL back one character, fetches the name again and reads it as 5BDDH does, storing the type and name characters into the compare operands of BASIC's name-compare routine 5C21H.
4E26
LD HL,(40FBH) 2A FB 40
Load Register Pair HL with the 16-bit value at 40FBH, the ROM's pointer to the start of the array table.
4E29
LD DE,(40FDH) ED 5B FD 40
Load Register Pair DE with the 16-bit value at 40FDH, the ROM's pointer to the end of the array table.
4E2D
JUMP to 4E32H to test the first array entry (Register Pair HL) against the end (Register Pair DE).

FIND THE ARRAY ENTRY
Register Pair HL walks from array to array; Register Pair DE holds the end of the array table.

4E2F
GOSUB to BASIC 5C3FH, which adds the array's length word (at entry +3) plus 5 to Register Pair HL, moving HL to the next array entry.
4E32
RST 18H DF
RST 18H: compare Register Pair HL (the current array entry) with Register Pair DE (the end of the array table). The Z FLAG is set if they are equal.
4E33
If the Z FLAG is set (no array of that name exists; PTRGET found a simple variable), JUMP to the ROM's SNERR at 1997H to report SYNTAX ERROR.
4E36
CALL 5C21H CD 21 5C
GOSUB to BASIC 5C21H to compare the array entry at Register Pair HL with the name. The Z FLAG is set on a match.
4E39
If the NZ FLAG is set (not this array), LOOP back to 4E2FH to step to the next one.
4E3B
INC HL 23
Increment Register Pair HL (the array entry) to entry +1, the second name character.
4E3C
INC HL 23
Increment Register Pair HL to entry +2, the first name character.
4E3D
INC HL 23
Increment Register Pair HL to entry +3, the low byte of the array's length word.
4E3E
LD E,(HL) 5E
Load Register E with the byte at Register Pair HL, the low byte of the length (the bytes from entry +5 to the end of the array).
4E3F
INC HL 23
Increment Register Pair HL to entry +4, the high byte of the length word.
4E40
LD D,(HL) 56
Load Register D with the byte at Register Pair HL, the high byte of the length. Register Pair DE holds the length word.
4E41
DEC DE 1B
Decrement Register Pair DE by one, for the dimension-count byte at entry +5; DE now counts the dimension words and the elements.
4E42
INC HL 23
Increment Register Pair HL to entry +5, the number of dimensions.
4E43
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the number of dimensions of the array.
4E44
INC HL 23
Increment Register Pair HL to entry +6, the first dimension word.

SKIP THE DIMENSION WORDS
Register A counts the dimensions; Register Pair HL steps over each 2-byte size and Register Pair DE loses 2 for each.

4E45
INC HL 23
Increment Register Pair HL, the first byte of this dimension word.
4E46
INC HL 23
Increment Register Pair HL past the second byte of this dimension word.
4E47
DEC DE 1B
Decrement Register Pair DE (the byte count) for the first byte of the dimension word.
4E48
DEC DE 1B
Decrement Register Pair DE for the second byte.
4E49
DEC A 3D
Decrement Register A, the dimensions still to skip.
4E4A
If the NZ FLAG is set (Register A is not zero), LOOP back to 4E45H. Afterwards Register Pair HL points at element 0 of the array and Register Pair DE holds the length of all its elements in bytes.
4E4C
LD (IX+05H),L DD 75 05
Store Register L (the low byte of element 0's address) in byte +05H of the block at Index Register IX.
4E4F
LD (IX+06H),H DD 74 06
Store Register H (the high byte) in byte +06H of the block at Index Register IX. Bytes +05H/+06H of the block now hold the address of the array's element 0.
4E52
EX DE,HL EB
Exchange Register Pairs DE and HL: HL now holds the length of the elements in bytes, DE the address of element 0.
4E53
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (at the Stack Pointer SP): HL now holds the first element's address (pushed at 4E22H), and the length of the elements is saved in its place.
4E54
OR A B7
OR Register A with itself; Register A is 00H after the loop, so this only clears the CARRY FLAG for the subtraction.
4E55
SBC HL,DE ED 52
Subtract Register Pair DE (element 0's address) from Register Pair HL (the first element's address): HL now holds the offset of the first element in bytes.
4E57
LD A,(IX+01H) DD 7E 01
Load Register A with byte +01H of the block at Index Register IX, the element length (2, 3, 4 or 8).
4E5A
PUSH AF F5
Save Register Pair AF: Register A (the element length) and the flags on the stack.
4E5B
GOSUB to BASIC 5D7DH, which divides Register Pair HL (the offset in bytes) by Register A (the element length): HL now holds the start subscript, the number of the first element to sort, counted from element 0.
4E5E
LD A,(5102H) 3A 02 51
Self-Modifying Code (operand of 5101H)
Load Register A with the byte at 5102H, the index-sort flag: 01H if the first array had a *, else 00H.
4E61
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if it is not an index sort.
4E62
PUSH AF F5
Save Register Pair AF: Register A (the index-sort flag) and its flags on the stack.
4E63
LD A,01H 3E 01
Load Register A with 01H, the block number of the first key when there is no index sort.
4E65
If the Z FLAG is set (not an index sort), JUMP to 4E68H with Register A = 1.
4E67
INC A 3C
Increment Register A to 02H: with an index sort block 1 is the index array and block 2 is the first key.
4E68
LD C,(IX+00H) DD 4E 00
Load Register C with byte +00H of the block at Index Register IX, this array's block number.
4E6B
CP C B9
Compare Register A (the first key's block number) with Register C (this block's number). The Z FLAG is set if this array is the first key; the CARRY FLAG is clear if this block comes no later than the first key.
4E6C
If the NZ FLAG is set (this is not the first key), JUMP to 4E71H.
4E6E
LD (4EECH),HL 22 EC 4E
Self-Modifying Code
Store Register Pair HL (the first key's start subscript) at 4EECH, the operand of the LD DE,0000H at 4EEBH. The later keys are checked against it, and the index sort fills its index array from it.
4E71
If the NO CARRY FLAG is set (this block is the first key, or the index array before it), JUMP to 4E7BH; the start check does not apply.
4E73
LD DE,(4EECH) ED 5B EC 4E
Self-Modifying Code
Load Register Pair DE with the 16-bit value at 4EECH, the first key's start subscript, stored at 4E6EH.
4E77
RST 18H DF
RST 18H: compare Register Pair HL (this key's start subscript) with Register Pair DE (the first key's). The Z FLAG is set if they are the same.
4E78
CALL 65E5H CD E5 65
GOSUB to BASIC 65E5H, which reports ILLEGAL FUNCTION CALL if the NZ FLAG is set (all keys must start at the same subscript), and otherwise compares byte +05H/+06H (element 0's address) of the block at Index Register IX with each earlier key's block; if one names the same array, it sets bit 6 of byte +04H, so that the array's data is moved only once.
4E7B
POP AF F1
Restore Register Pair AF: Register A (the index-sort flag) and its flags from the stack (pushed at 4E62H).
4E7C
If the Z FLAG is set (not an index sort; every array is moved), JUMP to 4E81H to add this array to the table row length.
4E7E
DEC C 0D
Index sort: decrement Register C, this block's number.
4E7F
If the NZ FLAG is set (Register C was not 1, so this is a key array, which is not moved), JUMP to 4E8AH, skipping the row length.
4E81
LD A,(4F02H) 3A 02 4F
Self-Modifying Code (operand of 4F01H)
Load Register A with the byte at 4F02H, the length in bytes of one table row counted so far (the operand of the LD A,00H at 4F01H; 00H in the file).
4E84
ADD A,(IX+01H) DD 86 01
Add byte +01H of the block at Index Register IX (this array's element length) to Register A.
4E87
CALL 6617H CD 17 66
GOSUB to BASIC 6617H, which stores Register A (the new row length) back at 4F02H unless bit 6 of byte +04H of the block at IX is set (the array is already counted as an earlier key).
4E8A
POP AF F1
Restore Register Pair AF from the stack: Register A = the element length (pushed at 4E5AH).
4E8B
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (at the Stack Pointer SP): HL now holds the length of all the array's elements in bytes (saved at 4E53H), and the start subscript is saved in its place.
4E8C
GOSUB to BASIC 5D7DH, which divides Register Pair HL by Register A (the element length): HL now holds the number of elements in the array.
4E8F
POP BC C1
Restore Register Pair BC from the stack: the start subscript.
4E90
OR A B7
OR Register A with itself; Register A is the element length (never 0), so this only clears the CARRY FLAG.
4E91
SBC HL,BC ED 42
Subtract Register Pair BC (the start subscript) from Register Pair HL (the number of elements): HL now holds the number of elements from the start subscript to the end of the array.
4E93
If the CARRY FLAG is set (the start lies past the end of the array), JUMP to the ROM's FCERR at 1E4AH to report ILLEGAL FUNCTION CALL.
4E96
LD DE,(4F27H) ED 5B 27 4F
Self-Modifying Code
Load Register Pair DE with the 16-bit value at 4F27H, n, the number of elements to sort, stored at 4DEBH (or at 4EA4H below).
4E9A
LD A,(IX+00H) DD 7E 00
Load Register A with byte +00H of the block at Index Register IX, this array's block number.
4E9D
DEC A 3D
Decrement Register A: the Z FLAG is set if this is block 1, the first array.
4E9E
If the NZ FLAG is set (not the first array), JUMP to 4EA9H.
4EA0
LD A,D 7A
Load Register A with Register D, the high byte of n.
4EA1
OR E B3
OR Register A with Register E, the low byte of n: the Z FLAG is set if n is 0.
4EA2
If the NZ FLAG is set (n was given), JUMP to 4EA9H.
4EA4
LD (4F27H),HL 22 27 4F
Self-Modifying Code
n = 0: store Register Pair HL (the elements from the start to the end of the first array) at 4F27H, the operand of 4F26H: that many elements are sorted.
4EA7
LD D,H 54
Load Register D with Register H, the high byte of the new n.
4EA8
LD E,L 5D
Load Register E with Register L. Register Pair DE now holds the new n as well.
4EA9
RST 18H DF
RST 18H: compare Register Pair HL (the elements available from the start) with Register Pair DE (n). The CARRY FLAG is set if fewer than n are available.
4EAA
If the CARRY FLAG is set (the array has fewer than n elements from its start), JUMP to the ROM's FCERR at 1E4AH to report ILLEGAL FUNCTION CALL.
4EAD
POP HL E1
Restore Register Pair HL from the stack: BASIC's text pointer after the array's subscript (saved at 4E21H).
4EAE
LD A,(IX+01H) DD 7E 01
Load Register A with byte +01H of the block at Index Register IX, the element type.
4EB1
CP 03H FE 03
Compare Register A (the element type) against 03H, a string array.
4EB3
If the NZ FLAG is set (not a string array), JUMP to 4ECBH.
4EB5
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the character after the subscript.
4EB6
CP 28H FE 28
Compare Register A against 28H, an opening parenthesis, which starts (position,length).
4EB8
If the NZ FLAG is set (no parenthesis), JUMP to 4ECBH; the defaults 1 and 255 stay.
4EBA
RST 10H D7
RST 10H: move Register Pair HL past the parenthesis; Register A holds the next character.
4EBB
CALL 2B1CH CD 1C 2B
GOSUB to the ROM's GETBYT at 2B1CH, which evaluates the expression at Register Pair HL and returns it as a byte (0-255) in Register A: the character position where the compare starts.
4EBE
LD (IX+02H),A DD 77 02
Store Register A (the start position) in byte +02H of the block at Index Register IX.
4EC1
RST 08H CF
RST 08H: the character at Register Pair HL must be the byte that follows (a comma), else SYNTAX ERROR; then RST 10H moves HL on and loads Register A.
4EC2
DEFB 2CH 2C
Data byte 2CH (a comma), the character the RST 08H at 4EC1H requires between the position and the length. RST 08H returns past this byte, to 4EC3H.
4EC3
CALL 2B1CH CD 1C 2B
GOSUB to the ROM's GETBYT at 2B1CH: Register A = the number of characters to compare.
4EC6
LD (IX+03H),A DD 77 03
Store Register A (the compare length) in byte +03H of the block at Index Register IX.
4EC9
RST 08H CF
RST 08H: the character at Register Pair HL must be the byte that follows (a closing parenthesis), else SYNTAX ERROR; then RST 10H moves HL on and loads Register A.
4ECA
DEFB 29H 29
Data byte 29H (a closing parenthesis), the character the RST 08H at 4EC9H requires. RST 08H returns past this byte, to 4ECBH.
4ECB
LD BC,0017H 01 17 00
Load Register Pair BC with 0017H (23), the length of one array block.
4ECE
ADD IX,BC DD 09
Add Register Pair BC (23) to Index Register IX: IX now points at the next array block.
4ED0
DEC HL 2B
Decrement Register Pair HL (the text pointer) so that the RST 10H that follows fetches the current character again.
4ED1
RST 10H D7
RST 10H: move Register Pair HL on and load Register A with the current character. The Z FLAG is set at the end of the line or statement.
4ED2
If the NZ FLAG is set (more of the statement follows), JUMP to 4DEFH, where a comma and the next array must follow.
4ED5
PUSH HL E5
Save Register Pair HL (BASIC's text pointer at the end of the statement) on the stack for the end of the sort.
4ED6
LD A,(5102H) 3A 02 51
Self-Modifying Code (operand of 5101H)
Load Register A with the byte at 5102H, the index-sort flag (01H if the first array had a *).
4ED9
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if it is not an index sort.
4EDA
If the Z FLAG is set (not an index sort), JUMP to 4F01H to start the sort.
4EDC
LD A,(4201H) 3A 01 42
Load Register A with the byte at 4201H, byte +01H of block 1: the element type of the index array.
4EDF
CP 02H FE 02
Compare Register A (the index array's element type) against 02H, integer.
4EE1
If the NZ FLAG is set (the index array is not an integer array), JUMP to the ROM's SNERR at 1997H to report SYNTAX ERROR.
4EE4
LD A,(4218H) 3A 18 42
Load Register A with the byte at 4218H, byte +01H of block 2: the element type of the first key (0 if no key was named).
4EE7
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if block 2 is not used.
4EE8
If the Z FLAG is set (an index array but no key), JUMP to the ROM's SNERR at 1997H to report SYNTAX ERROR.
4EEB
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the first key's start subscript, stored in this operand (4EECH) at 4E6EH. It is the first element number written into the index array (0000H in the file).
4EEE
LD HL,(4207H) 2A 07 42
Load Register Pair HL with the 16-bit value at 4207H, bytes +07H/+08H of block 1: the address of the index array's first element to fill.
4EF1
LD BC,(4F27H) ED 4B 27 4F
Load Register Pair BC with the 16-bit value at 4F27H, n, the number of elements to sort (the operand of 4F26H).
4EF5
JUMP to 4EFDH to test Register Pair BC for zero first.

FILL THE INDEX ARRAY
Register Pair HL = the next index element, Register Pair DE = the element number to store, Register Pair BC = the elements left.

4EF7
LD (HL),E 73
Store Register E (the low byte of the element number) at Register Pair HL, the low byte of this integer element.
4EF8
INC HL 23
Increment Register Pair HL to the high byte of the element.
4EF9
LD (HL),D 72
Store Register D (the high byte of the element number) at Register Pair HL.
4EFA
DEC BC 0B
Decrement Register Pair BC, the elements left to fill.
4EFB
INC HL 23
Increment Register Pair HL to the next integer element.
4EFC
INC DE 13
Increment Register Pair DE, the element number to store next.
4EFD
LD A,B 78
Load Register A with Register B, the high byte of the count of elements left.
4EFE
OR C B1
OR Register A with Register C, the low byte: the Z FLAG is set if Register Pair BC is zero.
4EFF
If the NZ FLAG is set (elements remain), LOOP back to 4EF7H.

4F01H - The Merge Sort

The elements are merged in passes: runs of 1, 2, 4 ... elements are merged pairwise into runs twice as long, until one run holds all n. Two neighbouring runs, A and B, are merged in place. While elements are taken from run A they stay where they are; once an element of run B has been taken, every element taken goes into a buffer at 5200H-56FFH (each moved array has its own part of it). When the buffer is full or a run is used up (5087H), the rest of run A is moved up to meet what is left of run B, and the buffer is copied back in front of it. The routines called through 50EEH do one step for every array that is moved; with the index sort that is only block 1, and 5093H/5096H find the key elements from the index values. For equal keys the element of run A is taken first, so the order of equal elements is kept.

4F01
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the length in bytes of one table row: the operand 4F02H (00H in the file) was added up at 4E81H through BASIC 6617H, one element length per moved array.
4F03
LD HL,0500H 21 00 05
Load Register Pair HL with 0500H (1,280), the size of the buffer at 5200H-56FFH in bytes.
4F06
GOSUB to BASIC 5D7DH, which divides Register Pair HL (1,280) by Register A (the row length): HL now holds the number of table rows the buffer can hold.
4F09
LD (5088H),HL 22 88 50
Self-Modifying Code
Store Register Pair HL (the rows the buffer holds) at 5088H, the operand of the LD HL,0000H at 5087H, which restarts the buffer count after each emptying.
4F0C
EX DE,HL EB
Exchange Register Pairs DE and HL: DE now holds the rows the buffer holds.
4F0D
LD HL,5200H 21 00 52
Load Register Pair HL with 5200H, the start of the buffer (the BASIC overlay area).
4F10
LD BC,510CH 01 0C 51
Load Register Pair BC with 510CH, the routine that gives one array its part of the buffer.
4F13
GOSUB to 50EEH, which calls 510CH with Index Register IX on each moved array's block: each gets the next Register Pair DE times element-length bytes of the buffer, starting at Register Pair HL (5200H).
4F16
LD HL,(5088H) 2A 88 50
Self-Modifying Code (operand of 5087H)
Load Register Pair HL with the 16-bit value at 5088H, the rows the buffer holds (stored at 4F09H).
4F19
LD (507EH),HL 22 7E 50
Self-Modifying Code
Store Register Pair HL at 507EH, the operand of the LD HL,0000H at 507DH: the buffer count, the rows that may still be taken before the buffer must be emptied.
4F1C
LD HL,0001H 21 01 00
Load Register Pair HL with 0001H, the run length of the first pass.
4F1F
JUMP to 4F23H to start the first pass with Register Pair HL = 1.

PASS LOOP
Each pass merges runs of the current run length into runs twice as long.

4F21
EX DE,HL EB
Exchange Register Pairs DE and HL: HL now holds the run length of the pass just finished (Register Pair DE held it).
4F22
ADD HL,HL 29
Add Register Pair HL to itself: HL now holds the run length doubled, for the next pass.
4F23
LD (4F39H),HL 22 39 4F
Self-Modifying Code
Store Register Pair HL (the run length) at 4F39H, the operand of the LD HL,0000H at 4F38H.
4F26
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with n, the number of elements to sort; the operand 4F27H was stored at 4DEBH (or 4EA4H when n was 0) (0000H in the file).
4F29
LD (4F40H),DE ED 53 40 4F
Self-Modifying Code
Store Register Pair DE (n) at 4F40H, the operand of the LD HL,0000H at 4F3FH: the elements not yet merged in this pass.
4F2D
RST 18H DF
RST 18H: compare Register Pair HL (the run length) with Register Pair DE (n). The CARRY FLAG is set if the run length is less than n.
4F2E
If the CARRY FLAG is set (more than one run, so a pass is needed), JUMP to 4F32H.
4F30
POP HL E1
The run length has reached n, so the elements are sorted: restore Register Pair HL from the stack, BASIC's text pointer at the end of the statement (saved at 4ED5H).
4F31
RET C9
RETURN to BASIC/CMD, just after its RST 28H at 57B5H (the SYS0 return was dropped at 4D00H).
4F32
LD BC,51D5H 01 D5 51
Load Register Pair BC with 51D5H, the routine that starts a pass.
4F35
GOSUB to 50EEH, which calls 51D5H for each moved array: byte +0FH/+10H of the block (the end of the last pair) is set to its first element to sort, so the first pair starts there.

PAIR LOOP
One pass of this loop sets up and merges one pair of runs.

4F38
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the run length of this pass; the operand 4F39H was stored at 4F23H (0000H in the file).
4F3B
LD (5124H),HL 22 24 51
Self-Modifying Code
Store Register Pair HL (the run length) at 5124H, the operand of the LD DE,0000H at 5123H: the count of elements in run A.
4F3E
EX DE,HL EB
Exchange Register Pairs DE and HL: DE now holds the run length.
4F3F
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the elements not yet merged in this pass; the operand 4F40H was stored at 4F29H or 4F57H (0000H in the file).
4F42
OR A B7
OR Register A with itself to clear the CARRY FLAG for the subtraction.
4F43
SBC HL,DE ED 52
Subtract Register Pair DE (the run length) from Register Pair HL (the elements left): HL = the elements left after run A.
4F45
If the CARRY FLAG is set (less than a whole run is left, and it is already sorted), JUMP to 4F21H: the pass is finished.
4F47
If the Z FLAG is set (exactly one run is left, with no partner), JUMP to 4F21H: the pass is finished.
4F49
OR A B7
OR Register A with itself to clear the CARRY FLAG for the subtraction.
4F4A
SBC HL,DE ED 52
Subtract Register Pair DE (the run length) from Register Pair HL again: HL = the elements left after a whole run B.
4F4C
If the NO CARRY FLAG is set (a whole run B is there), JUMP to 4F53H with Register Pair DE = the run length as run B's count.
4F4E
ADD HL,DE 19
Run B is shorter than a run: add Register Pair DE back, so HL holds the elements left after run A, which is run B's length.
4F4F
EX DE,HL EB
Exchange Register Pairs DE and HL: DE now holds run B's length.
4F50
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H: no elements are left after run B.
4F53
LD (5145H),DE ED 53 45 51
Self-Modifying Code
Store Register Pair DE (run B's element count) at 5145H, the operand of the LD DE,0000H at 5144H.
4F57
LD (4F40H),HL 22 40 4F
Self-Modifying Code
Store Register Pair HL (the elements left after this pair) at 4F40H, the operand of 4F3FH.
4F5A
LD BC,511DH 01 1D 51
Load Register Pair BC with 511DH, the routine that sets up a pair of runs.
4F5D
GOSUB to 50EEH, which calls 511DH for each moved array: the pointers and ends of run A and run B go into bytes +09H to +10H of its block.

COMPARE LOOP
Each pass compares the current elements of run A and run B key by key and takes the smaller one.

4F60
GOSUB to 5093H: with the index sort, the address of each key's element for run A's current index goes into byte +09H/+0AH of the key's block.
4F63
LD IX,4200H DD 21 00 42
Load Index Register IX with 4200H, block 1, the first key when there is no index sort.
4F67
LD BC,000DH 01 0D 00
Load Register Pair BC with 000DH, the offset of byte +0DH in a block, run B's element pointer.
4F6A
GOSUB to 5096H: with the index sort, the address of each key's element for run B's current index goes into byte +0DH/+0EH of the key's block, and Index Register IX returns at 4217H (block 2, the first key).

KEY LOOP
Index Register IX = the block of the key being compared.

4F6D
LD E,(IX+09H) DD 5E 09
Load Register E with byte +09H of the block at Index Register IX, the low byte of run A's current element address.
4F70
LD D,(IX+0AH) DD 56 0A
Load Register D with byte +0AH of the block at Index Register IX. Register Pair DE now points at run A's current element.
4F73
LD L,(IX+0DH) DD 6E 0D
Load Register L with byte +0DH of the block at Index Register IX, the low byte of run B's current element address.
4F76
LD H,(IX+0EH) DD 66 0E
Load Register H with byte +0EH of the block at Index Register IX. Register Pair HL now points at run B's current element.
4F79
LD C,(IX+01H) DD 4E 01
Load Register C with byte +01H of the block at Index Register IX, the key's element type (2, 3, 4 or 8), which is also its length.
4F7C
BIT 7,(IX+04H) DD CB 04 7E
Test bit 7 of byte +04H of the block at Index Register IX, set at 4E0DH for a descending key. The Z FLAG is set for an ascending key.
4F80
If the Z FLAG is set (ascending), JUMP to 4F83H.
4F82
EX DE,HL EB
Descending: exchange Register Pairs DE and HL, so DE points at run B's element and HL at run A's, and the compare below gives the opposite result.
4F83
LD A,C 79
Load Register A with Register C, the key's element type.
4F84
SUB 02H D6 02
Subtract 02H from Register A: the Z FLAG is set for an integer (type 2); Register A becomes 1 for a string, 2 for single, 6 for double precision.
4F86
If the NZ FLAG is set (not an integer), JUMP to 4F9DH.
4F88
LD C,(HL) 4E
Integer key: load Register C with the byte at Register Pair HL, the low byte of the second element's value.
4F89
INC HL 23
Increment Register Pair HL to the high byte of the second element.
4F8A
LD B,(HL) 46
Load Register B with the byte at Register Pair HL. Register Pair BC now holds the second element's value.
4F8B
LD A,(DE) 1A
Load Register A with the byte at Register Pair DE, the low byte of the first element's value.
4F8C
LD L,A 6F
Load Register L with Register A (the low byte of the first element's value).
4F8D
INC DE 13
Increment Register Pair DE to the high byte of the first element.
4F8E
LD A,(DE) 1A
Load Register A with the byte at Register Pair DE, the high byte of the first element's value.
4F8F
LD H,A 67
Load Register H with Register A. Register Pair HL now holds the first element's value.
4F90
LD A,B 78
Load Register A with Register B, the high byte of the second value.
4F91
XOR 80H EE 80
XOR Register A with 80H, flipping the sign bit: -32768..32767 becomes 0..65535 in the same order, so an unsigned subtraction compares them correctly.
4F93
LD B,A 47
Load Register B with Register A. Register Pair BC holds the second value with its sign bit flipped.
4F94
LD A,H 7C
Load Register A with Register H, the high byte of the first value.
4F95
XOR 80H EE 80
XOR Register A with 80H, flipping its sign bit too.
4F97
LD H,A 67
Load Register H with Register A. Register Pair HL holds the first value with its sign bit flipped.
4F98
OR A B7
OR Register A with itself to clear the CARRY FLAG for the subtraction.
4F99
SBC HL,BC ED 42
Subtract Register Pair BC from Register Pair HL: the CARRY FLAG is set if the first element is smaller, the Z FLAG if they are equal.
4F9B
JUMP to 4FD4H, which passes the result to 5039H.
4F9D
DEC A 3D
Decrement Register A (the type minus 2): the Z FLAG is set for a string (type 3).
4F9E
If the NZ FLAG is set (single or double precision), JUMP to 4FF7H.
4FA0
OR (IX+03H) DD B6 03
String key: OR Register A (00H) with byte +03H of the block at Index Register IX, the number of characters to compare. The Z FLAG is set if it is 0.
4FA3
If the Z FLAG is set (the compare length is 0), JUMP to 4FD4H with the Z FLAG set: this key counts as equal.
4FA5
LD A,(DE) 1A
Load Register A with the byte at Register Pair DE, the length of the first string (byte 0 of its string descriptor).
4FA6
LD B,A 47
Load Register B with Register A, the first string's length.
4FA7
LD C,(HL) 4E
Load Register C with the byte at Register Pair HL, the length of the second string.
4FA8
INC B 04
Increment Register B: the first string's length plus 1, used as a count that reaches 0 after its last character.
4FA9
INC DE 13
Increment Register Pair DE to byte 1 of the first string's descriptor (its text address).
4FAA
INC C 0C
Increment Register C: the second string's length plus 1.
4FAB
INC HL 23
Increment Register Pair HL to byte 1 of the second string's descriptor.
4FAC
PUSH DE D5
Save Register Pair DE (the pointer into the first descriptor) on the stack.
4FAD
LD E,(HL) 5E
Load Register E with the byte at Register Pair HL, the low byte of the second string's text address.
4FAE
INC HL 23
Increment Register Pair HL to the high byte of the address.
4FAF
LD D,(HL) 56
Load Register D with the byte at Register Pair HL. Register Pair DE now holds the second string's text address.
4FB0
EX DE,HL EB
Exchange Register Pairs DE and HL: HL holds the second string's text address.
4FB1
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (at the Stack Pointer SP): HL now points into the first string's descriptor, and the second string's text address is saved.
4FB2
LD E,(HL) 5E
Load Register E with the byte at Register Pair HL, the low byte of the first string's text address.
4FB3
INC HL 23
Increment Register Pair HL to the high byte of the address.
4FB4
LD D,(HL) 56
Load Register D with the byte at Register Pair HL. Register Pair DE now holds the first string's text address.
4FB5
POP HL E1
Restore Register Pair HL from the stack: the second string's text address.
4FB6
LD A,(IX+02H) DD 7E 02
Load Register A with byte +02H of the block at Index Register IX, the character position where the compare starts (1 unless given).
4FB9
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if the position is 0.
4FBA
If the Z FLAG is set (position 0), JUMP to 4FD4H with the Z FLAG set: this key counts as equal.
4FBC
DEC HL 2B
Decrement Register Pair HL (the second string's text), one before its first character, because the loop increments first.
4FBD
DEC DE 1B
Decrement Register Pair DE (the first string's text) the same way.

MOVE TO THE START POSITION
Register A counts down the position; Register B and Register C count down the characters left in the first and second string.

4FBE
INC HL 23
Increment Register Pair HL to the next character of the second string.
4FBF
INC DE 13
Increment Register Pair DE to the next character of the first string.
4FC0
DEC B 05
Decrement Register B, the characters left in the first string (plus 1).
4FC1
If the NZ FLAG is set (the first string still has characters), JUMP to 4FCBH.
4FC3
DEC C 0D
The first string ended: decrement Register C, the characters left in the second string.
4FC4
If the Z FLAG is set (the second string ended too), JUMP to 4FD4H with the Z FLAG set: equal.
4FC6
DEC A 3D
Decrement Register A, the character positions still to pass.
4FC7
If the NZ FLAG is set (the start position is not reached), LOOP back to 4FC3H.
4FC9
The second string has a character at the start position and the first has none: the first is smaller. JUMP to 500CH.
4FCB
DEC C 0D
Decrement Register C, the characters left in the second string.
4FCC
If the NZ FLAG is set (the second string still has characters), JUMP to 4FD6H.
4FCE
DEC A 3D
The second string ended: decrement Register A, the character positions still to pass.
4FCF
If the Z FLAG is set (the first string reaches the start position and the second does not), JUMP to 5018H: the first is larger.
4FD1
DEC B 05
Decrement Register B, the characters left in the first string.
4FD2
If the NZ FLAG is set (the first string still has characters), LOOP back to 4FCEH. If it also ends before the start position, the Z FLAG is set: equal.
4FD4
JUMP to 5039H with the result of this key in the flags (Z = equal, CARRY = the first element is smaller).
4FD6
DEC A 3D
Decrement Register A, the character positions still to pass.
4FD7
If the NZ FLAG is set (the start position is not reached), LOOP back to 4FBEH.
4FD9
LD A,(IX+03H) DD 7E 03
Both strings reach the start position: load Register A with byte +03H of the block at Index Register IX, the number of characters to compare.
4FDC
CP B B8
Compare Register A (the compare length) with Register B (the characters left in the first string, plus 1). The NO CARRY FLAG is set if the compare length is not smaller.
4FDD
If the NO CARRY FLAG is set, JUMP to 4FE0H, keeping Register B.
4FDF
LD B,A 47
Load Register B with Register A: only the compare length counts for the first string.
4FE0
CP C B9
Compare Register A (the compare length) with Register C (the characters left in the second string, plus 1).
4FE1
If the NO CARRY FLAG is set, JUMP to 4FE4H, keeping Register C.
4FE3
LD C,A 4F
Load Register C with Register A: only the compare length counts for the second string.

CHARACTER COMPARE LOOP
Register Pair DE = the first string's character, Register Pair HL = the second string's; Register B and Register C count down what is left of each.

4FE4
LD A,(DE) 1A
Load Register A with the byte at Register Pair DE, a character of the first string.
4FE5
CP (HL) BE
Compare Register A with the byte at Register Pair HL, the character of the second string: the Z FLAG is set if they are equal, the CARRY FLAG if the first is smaller.
4FE6
INC DE 13
Increment Register Pair DE to the next character of the first string (the flags are not changed).
4FE7
INC HL 23
Increment Register Pair HL to the next character of the second string.
4FE8
If the NZ FLAG is set (the characters differ), JUMP to 5039H with the result in the CARRY FLAG.
4FEA
DEC B 05
Decrement Register B, the characters left in the first string.
4FEB
If the NZ FLAG is set (the first string has more), JUMP to 4FF2H.
4FED
DEC C 0D
The first string ended: decrement Register C, the characters left in the second string.
4FEE
If the Z FLAG is set (both ended together), JUMP to 5039H with the Z FLAG set: equal.
4FF0
The first string is shorter: JUMP to 500CH, the first element is smaller.
4FF2
DEC C 0D
Decrement Register C, the characters left in the second string.
4FF3
If the NZ FLAG is set (the second string has more), LOOP back to 4FE4H.
4FF5
The second string is shorter: JUMP to 5018H, the first element is larger.
4FF7
LD B,00H 06 00
Single or double precision key: load Register B with 00H. Register C still holds the element length (4 or 8), so Register Pair BC = the length.
4FF9
DEC C 0D
Decrement Register C: Register Pair BC = the length minus 1, the offset of the exponent byte (the last byte of the value).
4FFA
ADD HL,BC 09
Add Register Pair BC to Register Pair HL: HL points at the second element's exponent.
4FFB
EX DE,HL EB
Exchange Register Pairs DE and HL: HL now points at the first element.
4FFC
ADD HL,BC 09
Add Register Pair BC to Register Pair HL: HL points at the first element's exponent.
4FFD
EX DE,HL EB
Exchange Register Pairs DE and HL back: DE points at the first element's exponent, HL at the second's.
4FFE
LD A,(DE) 1A
Load Register A with the byte at Register Pair DE, the first element's exponent (0 means the value is 0).
4FFF
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if the first value is 0.
5000
DEC DE 1B
Decrement Register Pair DE to the first element's top mantissa byte, which holds the sign in bit 7 (the flags are not changed).
5001
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the second element's exponent.
5002
DEC HL 2B
Decrement Register Pair HL to the second element's top mantissa byte (sign in bit 7).
5003
If the NZ FLAG is set (the first value is not 0), JUMP to 5010H.
5005
OR A B7
The first value is 0: OR Register A (the second element's exponent) with itself; the Z FLAG is set if the second value is 0 too.
5006
If the Z FLAG is set (both are 0), JUMP to 5039H with the Z FLAG set: equal.
5008
BIT 7,(HL) CB 7E
Test bit 7 of the byte at Register Pair HL, the second value's sign.
500A
If the NZ FLAG is set (the second value is negative, so 0 is larger), JUMP to 5018H.
500C
OR A B7
The first element is smaller: OR Register A with itself, which clears the CARRY FLAG. (The Z FLAG this leaves does not matter: 503BH tests only the CARRY FLAG.)
500D
SCF 37
Set the CARRY FLAG: CARRY means the first element is smaller.
500E
JUMP to 503BH with that result (CARRY: run A's element is smaller).
5010
OR A B7
The first value is not 0: OR Register A (the second element's exponent) with itself; the Z FLAG is set if the second value is 0.
5011
If the NZ FLAG is set (neither value is 0), JUMP to 501CH.
5013
EX DE,HL EB
The second value is 0: exchange Register Pairs DE and HL, so HL points at the first value's sign byte.
5014
BIT 7,(HL) CB 7E
Test bit 7 of the byte at Register Pair HL, the first value's sign.
5016
If the NZ FLAG is set (the first value is negative, less than 0), JUMP to 500CH: the first element is smaller.
5018
OR FFH F6 FF
The first element is larger: OR Register A with FFH, giving Register A = FFH, the NZ FLAG and no CARRY.
501A
JUMP to 5039H with that result (NZ and no CARRY: run A's element is larger).
501C
LD A,(DE) 1A
Neither value is 0: load Register A with the byte at Register Pair DE, the first value's sign byte.
501D
BIT 7,A CB 7F
Test bit 7 of Register A, the first value's sign.
501F
INC DE 13
Increment Register Pair DE back to the first value's exponent (the flags are not changed).
5020
If the Z FLAG is set (the first value is positive), JUMP to 502AH.
5022
BIT 7,(HL) CB 7E
The first value is negative: test bit 7 of the byte at Register Pair HL, the second value's sign.
5024
INC HL 23
Increment Register Pair HL back to the second value's exponent.
5025
If the Z FLAG is set (the second value is positive), JUMP to 500CH: the negative first value is smaller.
5027
EX DE,HL EB
Both are negative, where the larger magnitude is the smaller value: exchange Register Pairs DE and HL so the byte compare below runs the other way round.
5028
JUMP to 502FH to compare the bytes of the two values from the exponent down.
502A
BIT 7,(HL) CB 7E
The first value is positive: test bit 7 of the byte at Register Pair HL, the second value's sign.
502C
INC HL 23
Increment Register Pair HL back to the second value's exponent.
502D
If the NZ FLAG is set (the second value is negative), JUMP to 5018H: the first element is larger.
502F
LD B,C 41
Load Register B with Register C, the element length minus 1.
5030
INC B 04
Increment Register B: Register B = the element length, the bytes to compare.

BYTE COMPARE LOOP
Register Pair DE and Register Pair HL step down from the exponent through the mantissa; Register B counts the bytes.

5031
LD A,(DE) 1A
Load Register A with the byte at Register Pair DE, a byte of the first value (the exponent first).
5032
CP (HL) BE
Compare Register A with the byte at Register Pair HL, the same byte of the second value: the Z FLAG is set if equal, the CARRY FLAG if the first is smaller.
5033
DEC DE 1B
Decrement Register Pair DE to the next lower byte (the flags are not changed).
5034
DEC HL 2B
Decrement Register Pair HL to the next lower byte.
5035
If the NZ FLAG is set (the bytes differ), JUMP to 5039H with the result.
5037
Decrement Register B (the bytes left) and LOOP back to 5031H until it reaches zero; then the values are equal and the Z FLAG is set.
5039
If the Z FLAG is set (this key is equal), JUMP to 5054H to compare the next key.
503B
If the CARRY FLAG is set (run A's element is smaller), JUMP to 5060H to take it.
503D
LD BC,5153H 01 53 51
Run B's element is smaller: load Register Pair BC with 5153H, the routine that takes run B's element.
5040
GOSUB to 50EEH, which calls 5153H for each moved array: run B's element is copied into the buffer and run B's pointer advances.
5043
LD HL,(5145H) 2A 45 51
Load Register Pair HL with the 16-bit value at 5145H, the operand of 5144H: the elements left in run B.
5046
DEC HL 2B
Decrement Register Pair HL, one element of run B taken.
5047
LD (5145H),HL 22 45 51
Self-Modifying Code
Store Register Pair HL (the elements left in run B) back at 5145H.
504A
LD A,H 7C
Load Register A with Register H, the high byte of the elements left in run B.
504B
OR L B5
OR Register A with Register L, the low byte: the Z FLAG is set if run B is used up.
504C
If the Z FLAG is set (run B is used up), JUMP to 5077H to empty the buffer and go on with the next pair.
504E
GOSUB to 507DH to count one row against the buffer; if the buffer is full it is emptied.
5051
JUMP to 4F63H to compare the next pair of elements (only run B's element changed).
5054
LD BC,0017H 01 17 00
This key is equal: load Register Pair BC with 0017H (23), the length of one block.
5057
ADD IX,BC DD 09
Add Register Pair BC to Index Register IX: IX points at the next block, the next key.
5059
LD A,(IX+01H) DD 7E 01
Load Register A with byte +01H of the block at Index Register IX, its element type (0 if the block is not used).
505C
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if there are no more keys.
505D
If the NZ FLAG is set (another key follows), JUMP to 4F6DH to compare it.
5060
LD BC,5174H 01 74 51
Run A's element is smaller, or all keys are equal: load Register Pair BC with 5174H, the routine that takes run A's element.
5063
GOSUB to 50EEH, which calls 5174H for each moved array: run A's element stays in place (or goes into the buffer if it is in use) and run A's pointer advances.
5066
LD HL,(5124H) 2A 24 51
Load Register Pair HL with the 16-bit value at 5124H, the operand of 5123H: the elements left in run A.
5069
DEC HL 2B
Decrement Register Pair HL, one element of run A taken.
506A
LD (5124H),HL 22 24 51
Self-Modifying Code
Store Register Pair HL (the elements left in run A) back at 5124H.
506D
LD A,H 7C
Load Register A with Register H, the high byte of the elements left in run A.
506E
OR L B5
OR Register A with Register L, the low byte: the Z FLAG is set if run A is used up.
506F
If the Z FLAG is set (run A is used up), JUMP to 5077H to empty the buffer and go on with the next pair.
5071
GOSUB to 507DH to count one row against the buffer; if the buffer is full it is emptied.
5074
JUMP to 4F60H to find the new run A element (for the index sort) and compare again.
5077
A run is used up: GOSUB to 5087H, which restarts the buffer count and empties the buffer into place.
507A
JUMP to 4F38H to set up the next pair of runs of this pass.
507D
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the buffer count, the rows that may still be taken before the buffer must be emptied; the operand 507EH was stored at 4F19H or 508AH (0000H in the file).
5080
DEC HL 2B
Decrement Register Pair HL, the buffer count.
5081
LD (507EH),HL 22 7E 50
Self-Modifying Code
Store Register Pair HL (the buffer count) back at 507EH.
5084
LD A,H 7C
Load Register A with Register H, the high byte of the buffer count.
5085
OR L B5
OR Register A with Register L, the low byte: the Z FLAG is set if the count reached 0.
5086
RET NZ C0
If the NZ FLAG is set (room remains), RETURN.
5087
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the rows the buffer holds; the operand 5088H was stored at 4F09H (0000H in the file).
508A
LD (507EH),HL 22 7E 50
Self-Modifying Code
Store Register Pair HL at 507EH: the buffer count starts again.
508D
LD BC,5184H 01 84 51
Load Register Pair BC with 5184H, the routine that empties the buffer.
5090
GOSUB to 50EEH, which calls 5184H for each moved array: the rest of run A is moved up to run B's pointer and the buffer is copied back in front of it.

5093H - Index Sort: Find the Key Elements

Used only by the index sort (when the flag at 5102H is 01H). The index array's current element (run A's at +09H or run B's at +0DH in block 1) holds an element number; for every key block from block 2 on, the address of that element of the key array is worked out (element number times element length, plus the address of element 0 in +05H/+06H) and stored in the same field of the key's block. 5093H does it for run A (field +09H); 5096H for the field whose offset is in Register Pair BC. Without the index sort both return at once with Index Register IX unchanged; with it IX returns at 4217H, block 2, so that the compare starts with the first key.

5093
LD BC,0009H 01 09 00
Load Register Pair BC with 0009H, the offset of byte +09H in a block, run A's element pointer.
5096
LD A,(5102H) 3A 02 51
Self-Modifying Code (operand of 5101H)
Load Register A with the byte at 5102H, the index-sort flag stored at 4E04H: 01H for an index sort, 00H otherwise.
5099
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if it is not an index sort.
509A
RET Z C8
If the Z FLAG is set (not an index sort; the keys are the moved arrays themselves), RETURN.
509B
LD HL,4200H 21 00 42
Load Register Pair HL with 4200H, block 1, the index array's block.
509E
ADD HL,BC 09
Add Register Pair BC (the field offset, 09H or 0DH) to Register Pair HL: HL points at that field of block 1.
509F
LD E,(HL) 5E
Load Register E with the byte at Register Pair HL, the low byte of the pointer to the index array's current element.
50A0
INC HL 23
Increment Register Pair HL to the high byte of the pointer.
50A1
LD D,(HL) 56
Load Register D with the byte at Register Pair HL. Register Pair DE now points at the index array's current element.
50A2
EX DE,HL EB
Exchange Register Pairs DE and HL: HL points at the index array's current element.
50A3
LD E,(HL) 5E
Load Register E with the byte at Register Pair HL, the low byte of the element number stored in the index array.
50A4
INC HL 23
Increment Register Pair HL to the high byte of the element.
50A5
LD D,(HL) 56
Load Register D with the byte at Register Pair HL. Register Pair DE now holds the element number (the index value).
50A6
LD IX,4217H DD 21 17 42
Load Index Register IX with 4217H, block 2, the first key.

KEY BLOCK LOOP
Index Register IX = the key block, Register Pair DE = the element number, Register Pair BC = the field offset.

50AA
PUSH IX DD E5
Push Index Register IX (the key block's address) on the stack.
50AC
POP HL E1
Pop it into Register Pair HL: HL now holds the key block's address.
50AD
ADD HL,BC 09
Add Register Pair BC (the field offset) to Register Pair HL: HL points at the field (+09H or +0DH) of the key block.
50AE
PUSH BC C5
Save Register Pair BC (the field offset) on the stack.
50AF
PUSH HL E5
Save Register Pair HL (the field's address) on the stack.
50B0
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H, to add up element number times element length.
50B3
LD B,(IX+01H) DD 46 01
Load Register B with byte +01H of the block at Index Register IX, the key's element length (2, 3, 4 or 8), used as the multiply count.
50B6
ADD HL,DE 19
Add Register Pair DE (the element number) to Register Pair HL.
50B7
Decrement Register B and LOOP back to 50B6H until it reaches zero: HL = element number times element length, the offset of the element in bytes.
50B9
LD C,(IX+05H) DD 4E 05
Load Register C with byte +05H of the block at Index Register IX, the low byte of the key array's element 0 address.
50BC
LD B,(IX+06H) DD 46 06
Load Register B with byte +06H of the block at Index Register IX. Register Pair BC holds the address of the key array's element 0.
50BF
ADD HL,BC 09
Add Register Pair BC to Register Pair HL: HL now holds the address of the key element with that number.
50C0
POP BC C1
Restore Register Pair BC from the stack: the address of the field in the key block (pushed at 50AFH).
50C1
LD A,L 7D
Load Register A with Register L, the low byte of the key element's address.
50C2
LD (BC),A 02
Store Register A at the address in Register Pair BC, the low byte of the field.
50C3
INC BC 03
Increment Register Pair BC to the high byte of the field.
50C4
LD A,H 7C
Load Register A with Register H, the high byte of the key element's address.
50C5
LD (BC),A 02
Store Register A at the address in Register Pair BC. The key block's field now points at the key element.
50C6
LD BC,0017H 01 17 00
Load Register Pair BC with 0017H (23), the length of one block.
50C9
ADD IX,BC DD 09
Add Register Pair BC to Index Register IX: IX points at the next key block.
50CB
POP BC C1
Restore Register Pair BC from the stack: the field offset (09H or 0DH, pushed at 50AEH).
50CC
LD A,(IX+01H) DD 7E 01
Load Register A with byte +01H of the block at Index Register IX, its element type (0 if the block is not used).
50CF
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if there are no more keys.
50D0
If the NZ FLAG is set (another key block follows), LOOP back to 50AAH.
50D2
LD IX,4217H DD 21 17 42
Load Index Register IX with 4217H, block 2, so that the compare loop at 4F6DH starts with the first key.
50D6
RET C9
RETURN with Index Register IX at 4217H (block 2) and every key block's field pointing at its element.

50D7H - Take an Element: Leave It or Copy It to the Buffer

Called with Register Pair HL pointing at an element, Register Pair BC holding its length and Index Register IX on the array's block; returns with HL past the element. 50D7H copies the element to the buffer only when the buffer is in use (byte +16H of the block not 0); otherwise the element stays where it is. 50DFH always copies, except for an array whose data another block moves (bit 6 of +04H), where BASIC 6620H only steps HL past the element.

50D7
LD A,(IX+16H) DD 7E 16
Load Register A with byte +16H of the block at Index Register IX, the high byte of the copy-back address (0 while the buffer is not in use).
50DA
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if the buffer is not in use.
50DB
If the NZ FLAG is set (the buffer is in use, so every element taken goes into it), JUMP to 50DFH.
50DD
ADD HL,BC 09
The buffer is not in use: add Register Pair BC (the element length) to Register Pair HL, so HL points past the element, which stays in place.
50DE
RET C9
RETURN with Register Pair HL past the element.
50DF
CALL 6620H CD 20 66
GOSUB to BASIC 6620H, which loads Register E with byte +13H of the block at Index Register IX (the low byte of the buffer pointer). If bit 6 of byte +04H is set (another block moves this array), it drops this return address, adds Register Pair BC to Register Pair HL and returns straight to this routine's caller.
50E2
LD D,(IX+14H) DD 56 14
Load Register D with byte +14H of the block at Index Register IX. Register Pair DE now holds the buffer pointer, where the element goes.
50E5
LDIR ED B0
LDIR: copy BC bytes (the element) from Register Pair HL (the element) to Register Pair DE (the buffer). Afterwards HL points past the element, DE past the copy, and BC is 0.
50E7
LD (IX+13H),E DD 73 13
Store Register E (the low byte of the new buffer pointer) in byte +13H of the block at Index Register IX.
50EA
LD (IX+14H),D DD 72 14
Store Register D (the high byte) in byte +14H of the block at Index Register IX. The block's buffer pointer now points past the element just copied.
50ED
RET C9
RETURN with Register Pair HL past the element.

50EEH - Do One Step for Every Moved Array

Calls the routine whose address is in Register Pair BC once for every array block in use, from block 1 up, with Index Register IX pointing at the block and Register Pair BC holding the element length. With the index sort only block 1 (the index array) is moved, so the loop stops after it. Register Pairs DE and HL are passed through to the routine unchanged.

50EE
LD (50FFH),BC ED 43 FF 50
Self-Modifying Code
Store Register Pair BC (the routine's address: 510CH, 511DH, 5153H, 5174H, 5184H or 51D5H) at 50FFH, the operand of the CALL at 50FEH.
50F2
LD IX,4200H DD 21 00 42
Load Index Register IX with 4200H, block 1, the first array block.

BLOCK LOOP
Index Register IX = the block of the array being handled.

50F6
LD A,(IX+01H) DD 7E 01
Load Register A with byte +01H of the block at Index Register IX, the element type, which is also the element length (0 if the block is not used).
50F9
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if the block is not used.
50FA
RET Z C8
If the Z FLAG is set (no more arrays), RETURN.
50FB
LD C,A 4F
Load Register C with Register A, the element length.
50FC
LD B,00H 06 00
Load Register B with 00H, so Register Pair BC holds the element length as a 16-bit count.
50FE
CALL 0000H CD 00 00
Self-Modifying Code
GOSUB to the routine whose address was stored in this operand (50FFH) at 50EEH. The file holds 0000H here; the CALL is only executed after 50EEH has written the address.
5101
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the index-sort flag, the operand 5102H: 00H in the file, 01H once stored at 4E04H for an index sort.
5103
OR A B7
OR Register A with itself to set the flags: the NZ FLAG is set for an index sort.
5104
RET NZ C0
If the NZ FLAG is set (an index sort, where only block 1 is moved), RETURN after block 1.
5105
LD BC,0017H 01 17 00
Load Register Pair BC with 0017H (23), the length of one block.
5108
ADD IX,BC DD 09
Add Register Pair BC to Index Register IX: IX points at the next block.
510A
LOOP back to 50F6H for the next block, now at Index Register IX.

510CH - Give an Array Its Part of the Buffer

Called through 50EEH at 4F13H, with Register Pair HL = the first free byte of the buffer at 5200H, Register Pair DE = the rows the buffer holds, Register A and Register Pair BC = the element length, and Index Register IX on the array's block.

510C
LD (IX+11H),L DD 75 11
Store Register L (the low byte of this array's buffer start) in byte +11H of the block at Index Register IX.
510F
LD (IX+12H),H DD 74 12
Store Register H (the high byte) in byte +12H of the block at Index Register IX. Bytes +11H/+12H hold where this array's part of the buffer starts.
5112
LD B,A 47
Load Register B with Register A, the element length, used as the multiply count.
5113
LD (IX+13H),L DD 75 13
Store Register L in byte +13H of the block at Index Register IX: the low byte of the buffer pointer, which starts at the buffer start.
5116
LD (IX+14H),H DD 74 14
Store Register H in byte +14H of the block at Index Register IX, the high byte of the buffer pointer.
5119
JP 662BH C3 2B 66
JUMP to BASIC 662BH, which adds Register Pair DE (the rows) to Register Pair HL Register B times (the element length), so HL points past this array's part of the buffer for the next array, and returns to 50EEH's loop. If bit 6 of byte +04H is set (another block moves this array), it returns without adding: the array shares that block's part.
511C
RET C9
A RET that is never executed: 5119H jumps away, and no instruction jumps or calls here.

511DH - Set Up a Pair of Runs

Called through 50EEH at 4F5DH, with Register A and Register Pair BC = the element length and Index Register IX on the array's block. Run A starts at the end of the last pair (+0FH/+10H) and holds the count at 5124H elements; run B follows it and holds the count at 5145H. The block gets run A's pointer (+09H) and end (+0BH), and run B's pointer (+0DH) and end (+0FH), which is also where the next pair starts.

511D
LD B,A 47
Load Register B with Register A, the element length, used as the multiply count.
511E
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H, the start value of run A's length in bytes.
5121
PUSH HL E5
Push Register Pair HL (0000H) on the stack; it is exchanged for run B's start at 5143H.
5122
PUSH BC C5
Save Register Pair BC (Register B = the element length) on the stack.
5123
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the count of elements in run A; the operand 5124H was stored at 4F3BH (the run length) and counted down at 506AH (0000H in the file).
5126
ADD HL,DE 19
Add Register Pair DE (run A's count) to Register Pair HL.
5127
Decrement Register B and LOOP back to 5126H until it reaches zero: HL = run A's length in bytes.
5129
LD E,(IX+0FH) DD 5E 0F
Load Register E with byte +0FH of the block at Index Register IX, the low byte of the end of the last pair.
512C
LD D,(IX+10H) DD 56 10
Load Register D with byte +10H of the block at Index Register IX. Register Pair DE holds the end of the last pair, which is where run A starts.
512F
LD (IX+09H),E DD 73 09
Store Register E in byte +09H of the block at Index Register IX: the low byte of run A's pointer.
5132
LD (IX+0AH),D DD 72 0A
Store Register D in byte +0AH of the block at Index Register IX. Run A's pointer now holds run A's first element.
5135
ADD HL,DE 19
Add Register Pair DE (run A's start) to Register Pair HL (run A's length in bytes): HL = run B's start.
5136
LD (IX+0DH),L DD 75 0D
Store Register L in byte +0DH of the block at Index Register IX: the low byte of run B's pointer.
5139
LD (IX+0EH),H DD 74 0E
Store Register H in byte +0EH of the block at Index Register IX. Run B's pointer now holds run B's first element.
513C
POP BC C1
Restore Register Pair BC from the stack: Register B = the element length again.
513D
LD (IX+0BH),L DD 75 0B
Store Register L in byte +0BH of the block at Index Register IX: the low byte of run A's end (run A ends where run B starts).
5140
LD (IX+0CH),H DD 74 0C
Store Register H in byte +0CH of the block at Index Register IX, the high byte of run A's end.
5143
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (at the Stack Pointer SP): HL = 0000H (pushed at 5121H), and run B's start is saved.
5144
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the count of elements in run B; the operand 5145H was stored at 4F53H and counted down at 5047H (0000H in the file).
5147
ADD HL,DE 19
Add Register Pair DE (run B's count) to Register Pair HL.
5148
Decrement Register B and LOOP back to 5147H until it reaches zero: HL = run B's length in bytes.
514A
POP DE D1
Restore Register Pair DE from the stack: run B's start.
514B
ADD HL,DE 19
Add Register Pair DE to Register Pair HL: HL = run B's end, the first element after the pair.
514C
LD (IX+0FH),L DD 75 0F
Store Register L in byte +0FH of the block at Index Register IX: the low byte of run B's end.
514F
LD (IX+10H),H DD 74 10
Store Register H in byte +10H of the block at Index Register IX. Bytes +0FH/+10H now hold where the next pair starts.
5152
RET C9
RETURN to 50EEH's loop, which moves on to the next array block.

5153H - Take Run B's Element

Called through 50EEH at 5040H, with Register Pair BC = the element length and Index Register IX on the array's block. Run B's element always goes into the buffer. The first time in a pair, run A's current position is recorded at +15H/+16H as the place the buffer is copied back to.

5153
LD L,(IX+0DH) DD 6E 0D
Load Register L with byte +0DH of the block at Index Register IX, the low byte of run B's pointer.
5156
LD H,(IX+0EH) DD 66 0E
Load Register H with byte +0EH of the block at Index Register IX. Register Pair HL now points at run B's current element.
5159
GOSUB to 50DFH, which copies the element (Register Pair BC bytes at Register Pair HL) into the buffer and returns with HL past it.
515C
LD (IX+0DH),L DD 75 0D
Store Register L (the low byte of the next element of run B) in byte +0DH of the block at Index Register IX.
515F
LD (IX+0EH),H DD 74 0E
Store Register H in byte +0EH of the block at Index Register IX. Run B's pointer has moved on by one element.
5162
LD A,(IX+16H) DD 7E 16
Load Register A with byte +16H of the block at Index Register IX, the high byte of the copy-back address (0 while the buffer is not in use).
5165
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if the buffer was not in use.
5166
RET NZ C0
If the NZ FLAG is set (the buffer was already in use), RETURN.
5167
LD E,(IX+09H) DD 5E 09
Load Register E with byte +09H of the block at Index Register IX, the low byte of run A's pointer.
516A
LD D,(IX+0AH) DD 56 0A
Load Register D with byte +0AH of the block at Index Register IX. Register Pair DE holds run A's current position, where the buffered elements will go.
516D
LD (IX+15H),E DD 73 15
Store Register E in byte +15H of the block at Index Register IX, the low byte of the copy-back address.
5170
LD (IX+16H),D DD 72 16
Store Register D in byte +16H of the block at Index Register IX. The high byte is never 0, so the buffer now counts as in use.
5173
RET C9
RETURN to 50EEH's loop, which moves on to the next array block.

5174H - Take Run A's Element

Called through 50EEH at 5063H, with Register Pair BC = the element length and Index Register IX on the array's block. Run A's element stays where it is, or goes into the buffer if the buffer is in use, and run A's pointer moves on.

5174
LD L,(IX+09H) DD 6E 09
Load Register L with byte +09H of the block at Index Register IX, the low byte of run A's pointer.
5177
LD H,(IX+0AH) DD 66 0A
Load Register H with byte +0AH of the block at Index Register IX. Register Pair HL now points at run A's current element.
517A
GOSUB to 50D7H, which leaves the element in place or, when the buffer is in use, copies it to the buffer; it returns with Register Pair HL past the element.
517D
LD (IX+09H),L DD 75 09
Store Register L in byte +09H of the block at Index Register IX, the low byte of run A's new pointer.
5180
LD (IX+0AH),H DD 74 0A
Store Register H in byte +0AH of the block at Index Register IX. Run A's pointer has moved on by one element.
5183
RET C9
RETURN to 50EEH's loop, which moves on to the next array block.

5184H - Empty the Buffer

Called through 50EEH at 5090H, with Register Pair BC = the element length and Index Register IX on the array's block. The elements of run A not yet taken (from +09H to +0BH) are moved up so that they end just before run B's pointer; run B's pointer becomes run A's new end. Then the buffer is copied back to the copy-back address (+15H/+16H), which fills exactly the gap in front of the moved elements, and the buffer is marked not in use.

5184
LD L,(IX+0BH) DD 6E 0B
Load Register L with byte +0BH of the block at Index Register IX, the low byte of run A's end.
5187
LD H,(IX+0CH) DD 66 0C
Load Register H with byte +0CH of the block at Index Register IX. Register Pair HL holds run A's end.
518A
LD E,(IX+09H) DD 5E 09
Load Register E with byte +09H of the block at Index Register IX, the low byte of run A's pointer.
518D
LD D,(IX+0AH) DD 56 0A
Load Register D with byte +0AH of the block at Index Register IX. Register Pair DE holds run A's pointer (its first element not yet taken).
5190
OR A B7
OR Register A with itself to clear the CARRY FLAG for the subtraction.
5191
SBC HL,DE ED 52
Subtract Register Pair DE (run A's pointer) from Register Pair HL (run A's end): HL = the bytes of run A not yet taken.
5193
LD B,H 44
Load Register B with Register H, the high byte of that count.
5194
LD C,L 4D
Load Register C with Register L. Register Pair BC holds the bytes of run A not yet taken.
5195
ADD HL,DE 19
Add Register Pair DE back to Register Pair HL: HL = run A's end again.
5196
LD E,(IX+0DH) DD 5E 0D
Load Register E with byte +0DH of the block at Index Register IX, the low byte of run B's pointer.
5199
LD D,(IX+0EH) DD 56 0E
Load Register D with byte +0EH of the block at Index Register IX. Register Pair DE holds run B's pointer.
519C
LD (IX+0BH),E DD 73 0B
Store Register E in byte +0BH of the block at Index Register IX: run A's new end (low byte) is run B's pointer.
519F
LD (IX+0CH),D DD 72 0C
Store Register D in byte +0CH of the block at Index Register IX, the high byte of run A's new end.
51A2
EX DE,HL EB
Exchange Register Pairs DE and HL: HL = run B's pointer, DE = run A's old end.
51A3
OR A B7
OR Register A with itself to clear the CARRY FLAG for the subtraction.
51A4
SBC HL,BC ED 42
Subtract Register Pair BC (the bytes of run A not yet taken) from Register Pair HL (run B's pointer): HL = where the rest of run A starts after the move, run A's new pointer.
51A6
LD (IX+09H),L DD 75 09
Store Register L in byte +09H of the block at Index Register IX, the low byte of run A's new pointer; BASIC 6634H stores the high byte.
51A9
CALL 6634H CD 34 66
GOSUB to BASIC 6634H, which stores Register H in byte +0AH, then moves the BC bytes that end at run A's old end (Register Pair DE) up so that they end just before run B's pointer (LDDR), and returns with the Z FLAG set. If bit 6 of byte +04H is set (another block moves this array), it moves nothing and returns with the NZ FLAG set.
51AC
If the NZ FLAG is set (another block moves this array's data), JUMP to 51D0H; there is no buffer to copy back.
51AE
NOP 00
NOP: no operation; execution simply continues at 51AFH, which copies the buffer back.
51AF
LD L,(IX+13H) DD 6E 13
Load Register L with byte +13H of the block at Index Register IX, the low byte of the buffer pointer.
51B2
LD H,(IX+14H) DD 66 14
Load Register H with byte +14H of the block at Index Register IX. Register Pair HL holds the buffer pointer (the end of what is in the buffer).
51B5
LD E,(IX+11H) DD 5E 11
Load Register E with byte +11H of the block at Index Register IX, the low byte of this array's buffer start.
51B8
LD D,(IX+12H) DD 56 12
Load Register D with byte +12H of the block at Index Register IX. Register Pair DE holds the buffer start.
51BB
OR A B7
OR Register A with itself to clear the CARRY FLAG for the subtraction.
51BC
SBC HL,DE ED 52
Subtract Register Pair DE (the buffer start) from Register Pair HL (the buffer pointer): HL = the bytes in the buffer; the Z FLAG is set if it is empty.
51BE
LD B,H 44
Load Register B with Register H, the high byte of the bytes in the buffer.
51BF
LD C,L 4D
Load Register C with Register L. Register Pair BC holds the bytes in the buffer.
51C0
RET Z C8
If the Z FLAG is set (the buffer is empty), RETURN.
51C1
EX DE,HL EB
Exchange Register Pairs DE and HL: HL = the buffer start.
51C2
LD (IX+13H),L DD 75 13
Store Register L in byte +13H of the block at Index Register IX: the buffer pointer (low byte) is back at the buffer start.
51C5
LD (IX+14H),H DD 74 14
Store Register H in byte +14H of the block at Index Register IX, the high byte of the buffer pointer.
51C8
LD E,(IX+15H) DD 5E 15
Load Register E with byte +15H of the block at Index Register IX, the low byte of the copy-back address.
51CB
LD D,(IX+16H) DD 56 16
Load Register D with byte +16H of the block at Index Register IX. Register Pair DE holds the copy-back address (run A's pointer when the first element of run B was taken).
51CE
LDIR ED B0
LDIR: copy BC bytes (the buffer) from Register Pair HL (the buffer start) to Register Pair DE (the copy-back address), back into the array.
51D0
LD (IX+16H),00H DD 36 16 00
Store 00H in byte +16H of the block at Index Register IX: the high byte of the copy-back address is 0, the buffer is not in use.
51D4
RET C9
RETURN to 50EEH's loop, which moves on to the next array block.

51D5H - Start a Pass

Called through 50EEH at 4F35H with Index Register IX on the array's block: the first pair of runs of a pass starts at the first element sorted.

51D5
LD L,(IX+07H) DD 6E 07
Load Register L with byte +07H of the block at Index Register IX, the low byte of the address of the first element sorted (stored at 4E1BH).
51D8
LD H,(IX+08H) DD 66 08
Load Register H with byte +08H of the block at Index Register IX. Register Pair HL holds the first element sorted.
51DB
LD (IX+0FH),L DD 75 0F
Store Register L in byte +0FH of the block at Index Register IX, the low byte of the end of the last pair.
51DE
LD (IX+10H),H DD 74 10
Store Register H in byte +10H of the block at Index Register IX. The first pair of this pass starts at the first element sorted.
51E1
RET C9
RETURN to 50EEH's loop, which moves on to the next array block.

51E2H - Unused Bytes

51E2H-51E7H fill the last load record; nothing reads or executes them.

51E2-51E7
NOP x 6 00 x 6
Six 00H bytes that fill the end of the last load record. No code jumps, calls or reads here.