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

Page Customization

Introduction/Summary

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

SYS21/SYS carries out three Disk BASIC commands. It loads 4D00H-51E7H in five load records (transfer address 4D00H) into the overlay area 4D00H-51FFH; the code ends at 51E1H and 51E2H-51E7H are 6 bytes of 00H. BASIC/CMD runs it with RST 28H, not through its loader at 576CH: code 57H from 57B0H for CMD"F=ERASE" and CMD"F=KEEP" (57AAH and 57AEH, found by SYS20/SYS in BASIC/CMD's table at 592BH, with Register B = 28H or 20H), and code 37H from 57B3H for CMD"O" (from the letter commands at 57DDH). 4D00H drops the dispatcher's return address 4BC5H, so the commands return straight to the ROM's statement loop. The page shows every byte of the file once, in address order.

  • CMD"F=ERASE",name,name... deletes the named variables; CMD"F=KEEP",name,name... deletes all others. A name is a simple variable or an array written with parentheses. Each one found is marked with bit 4 of its type byte (4D5BH); 4D67H then copies the kept entries down and stores the new 40FBH and 40FDH. User functions are always kept. A comma at the end of a line continues the list on the next line (BASIC 5D78H); empty lines and REM lines in between are passed over. BASIC's statement-end CALL at 6426H points at 4D67H while the list is read, so an error still closes up the tables.
  • CMD"O",n,[*]array(start)[,[-]array(start)...] sorts n elements (n = 0: the rest of the first array) of up to nine arrays, from the same start subscript, as the columns of one table. The first array is the first key, each later one breaks the ties left; a minus sign makes a key descending; a string array may be followed by (position,length). With an asterisk the first array must be an integer array: it is filled with the element numbers start to start + n - 1 and only it is rearranged, by the keys after it (an index sort). The sort is a merge sort in place, with the buffer 5200H-56FFH (BASIC/CMD's overlay area, so 576EH is cleared) and the blocks at 4300H; 4288H is cleared so SYS21/SYS is loaded again next time, because the sort changes operands in its own code.

The Array Blocks at 4300H

4DBAH builds nine blocks of 17H (23) bytes at 4300H-43CEH in SYS0/SYS's sector buffer, numbered 1-9; 43CFH-43D0H (block number 0, type 0) end the table. With an index sort block 1 is the index array and the keys start at block 2 (4317H).

BytesContents
+00HThe block number, 1-9.
+01HThe element type, which is also its length (2, 3, 4 or 8); 0 = block not used.
+02H, +03HFor a string key: the first character compared (1) and the number of characters (255).
+04HBit 7 a descending key (BASIC 65B4H); bit 6 the same array as an earlier block, whose data is moved only once (65B9H).
+05H-+06HThe address of the array's element 0.
+07H-+08HThe address of the first element sorted.
+09H-+0AH, +0BH-+0CHRun A's current element and its end.
+0DH-+0EH, +0FH-+10HRun B's current element and its end, which is where the next pair starts.
+11H-+12H, +13H-+14HThe array's part of the buffer: its start and the next free byte.
+15H-+16HWhere the buffer is copied back to (run A's position when run B's first element was taken); a high byte of 00H means the buffer is not in use.

Variables

ROM Addresses

Address
Size
Contents
40AFH
1 byte
Value Type
The type PTRGET found, also the element length (4E15H).
40F9H-40FAH
2 bytes
VARTAB
The start of the simple variables (4D4DH, 4D67H).
40FBH-40FCH
2 bytes
ARYTAB
The start of the arrays (4D2EH, 4D6DH, 4D94H, 4E26H).
40FDH-40FEH
2 bytes
STREND
The end of the arrays (4D3BH, 4D99H, 4DB3H, 4E29H).
4101H-411AH
26 bytes
DEF Types
Read by BASIC 5BB4H for a name without a suffix.

DOS Addresses

Address
Size
Contents
4288H
1 byte
Overlay in 4D00H
The directory slot of the overlay in 4D00H-51FFH; cleared at 4DBBH.
4300H-43D0H
209 bytes
Array Blocks
Nine 17H-byte blocks and the end mark, in the sector buffer 4300H-43FFH (4DC2H).

BASIC/CMD Addresses

Address
Size
Contents
576EH
1 byte
Overlay in 5200H
Cleared at 4DBEH: the buffer 5200H-56FFH overwrites BASIC's overlay.
57B8H-57B9H
2 bytes
Next Line Number
Stored by BASIC 5D78H (4D21H).
5BFEH, 5C03H, 5C09H
3 bytes
Name Compare Operands
Stored by BASIC 5BB4H, compared by 5BFAH.
5200H-56FFH
1280 bytes
Sort Buffer
BASIC/CMD's overlay area (4F03H, 4F0DH).
6427H-6428H
2 bytes
Statement-End CALL
4D67H while ERASE or KEEP runs (4D15H); 6496H again from 6497H.

Operands Changed at Run Time

Address
Size
Contents
4D8DH
1 byte
ERASE or KEEP
The opcode at 4D8DH: 28H (JR Z) or 20H (JR NZ), stored at 4D0FH.
4EECH-4EEDH
2 bytes
First Key Start
Operand of 4EEBH, stored at 4E6EH, read at 4E73H.
4F02H
1 byte
Row Length
Operand of 4F01H, added up at 4E81H through BASIC 65EBH.
4F27H-4F28H
2 bytes
n
Operand of 4F26H, stored at 4DEBH and 4EA4H, read at 4E96H and 4EF1H.
4F39H-4F3AH
2 bytes
Run Length
Operand of 4F38H, stored at 4F23H.
4F40H-4F41H
2 bytes
Elements Left
Operand of 4F3FH, stored at 4F29H and 4F57H.
507EH-507FH
2 bytes
Buffer Count
Operand of 507DH, stored at 4F19H, 5081H and 508AH.
5088H-5089H
2 bytes
Buffer Rows
Operand of 5087H, stored at 4F09H, read at 4F16H.
50FFH-5100H
2 bytes
Step Routine
Operand of the CALL at 50FEH, stored at 50EEH.
5102H
1 byte
Index Sort
Operand of 5101H: 01H for an index sort (4E04H; read at 4E5EH, 4ED6H, 5096H and by BASIC 65BCH).
5124H-5125H
2 bytes
Run A Count
Operand of 5123H, stored at 4F3BH and 506AH.
5145H-5146H
2 bytes
Run B Count
Operand of 5144H, stored at 4F53H and 5047H.

Outside Routines

WhereAddresses
ROM1997H (SNERR), 1E46H (INTID2), 1E4AH (FCERR), 260DH (PTRGET), 2B1CH (GETBYT); RST 08H, RST 10H, RST 18H.
BASIC/CMD586EH (POP HL / RET), 5BB4H and 5BB6H (read a name), 5BFAH (compare), 5C0DH and 5C18H (next entry), 5C21H (pass a subscript), 5D56H (divide HL by A), 5D78H (next line), 5DEFH (DOS error), 6497H, 65B4H, 65B9H, 65EBH, 65F4H, 65FFH, 6608H.

Disassembly

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

The SYS0/SYS overlay dispatcher loads SYS21/SYS 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 4BC5H, the SYS0/SYS dispatcher's return address (its code there would clear bit 6 of 4289H); this skips its first byte.
4D01
INC SP 33
Increment the Stack Pointer SP again: 4BC5H is dropped. The dispatcher has already dropped the return address of BASIC's RST 28H (57B2H or 57B5H), so a RET here goes to the caller of BASIC's CMD code, the ROM's statement loop.
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
If the NZ FLAG is set (the code was neither 37H nor 57H), JUMP to BASIC 5DEFH, 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 (6427H),BC ED 43 27 64
Store Register Pair BC (4D67H) at 6427H, the operand of BASIC/CMD's CALL 6496H at 6426H. That CALL sits in BASIC's statement-end clean-up at 6423H, which also runs when an error ends the statement; 6496H 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 => 2CH CF 2C
RST 08H with the byte 2CH: the character at Register Pair HL (BASIC's text pointer) must be a comma before the next name, else the ROM gives SYNTAX ERROR; RST 08H then steps Register Pair HL to the next character and loads it into Register A, with the Z FLAG set at the end of the line or statement.
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
The comma ended the line (or statement): GOSUB to BASIC 5D78H. It requires the rest of the line to be empty, colons or a remark (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/SYS'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; 5D78H 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 5BB6H 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 5BFEH, 5C03H and 5C09H of BASIC's name-compare routine 5BFAH. 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 5BB6H 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 5C21H, 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
GOSUB to BASIC 5BFAH, which compares the entry at Register Pair HL (its type byte, second character and first character) with the name read by 5BB6H. 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 5C18H, 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
GOSUB to BASIC 5BFAH to compare the variable at Register Pair HL with the name read by 5BB6H. The Z FLAG is set on a match.
4D56
If the NZ FLAG is set (not this variable), GOSUB to BASIC 5C0DH, 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,586EH 21 6E 58
Load Register Pair HL with 586EH, the address of a POP HL / RET in BASIC/CMD.
4D66
PUSH HL E5
Push Register Pair HL (586EH) as a return address. The close-up below ends in a RET (at BASIC 6497H), which goes to BASIC 586EH; 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 6426H, set at 4D15H) when an error ends the statement. It ends by jumping to BASIC 6497H, which puts that CALL back to 6496H.

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 Register Pair 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
JUMP to BASIC 6497H, which stores 6496H back at 6427H (so BASIC's statement-end CALL at 6426H does nothing again) and returns: to BASIC 586EH (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,[asterisk]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 an asterisk 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 4300H, one per array; the table "The Array Blocks at 4300H" 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 (4288H),A 32 88 42
Store Register A (00H) at 4288H, the SYS0/SYS byte that holds the directory slot of the overlay now in 4D00H-51FFH. With it cleared SYS0/SYS treats the overlay area as empty and reloads SYS21/SYS 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/SYS, SYS19/SYS or SYS20/SYS) 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,4300H 21 00 43
Load Register Pair HL with 4300H, the address of the first array block, in SYS0/SYS's sector buffer 4300H-43FFH, which is 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 (43CFH): byte +00H of a tenth block, block number 0, which ends the table.
4DDF
INC HL 23
Increment Register Pair HL from 43CFH to 43D0H, byte +01H of the tenth block.
4DE0
LD (HL),D 72
Store Register D (00H) at Register Pair HL (43D0H): 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 => 2CH CF 2C
RST 08H with the byte 2CH: the character at Register Pair HL (BASIC's text after the CMD"O" string) must be a comma before the count n, else the ROM gives SYNTAX ERROR; RST 08H then steps Register Pair HL to the next character and loads it into Register A, with the Z FLAG set at the end of the line or statement.
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,4300H DD 21 00 43
Load Index Register IX with 4300H, 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 => 2CH CF 2C
RST 08H with the byte 2CH: the character at Register Pair HL (BASIC's text) must be a comma before the next array item, else the ROM gives SYNTAX ERROR; RST 08H then steps Register Pair HL to the next character and loads it into Register A, with the Z FLAG set at the end of the line or statement.
4DF8
CP CFH FE CF
Compare Register A (the first character of the array item) against CFH, the BASIC token for the asterisk (multiply), which marks the index sort.
4DFA
If the NZ FLAG is set (no asterisk), 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 asterisk is only allowed before the first array.
4E01
If the NZ FLAG is set (the asterisk 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 asterisk 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
GOSUB to BASIC 65B4H, 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 asterisk 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
GOSUB to BASIC 5BB4H, which steps Register Pair HL back one character, fetches the name again and reads it as 5BB6H does, storing the type and name characters into the compare operands of BASIC's name-compare routine 5BFAH.
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 5C18H, 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
GOSUB to BASIC 5BFAH 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 5D56H, 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
Load Register A with the byte at 5102H, the index-sort flag: 01H if the first array had an asterisk, 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
GOSUB to BASIC 65B9H, 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
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
GOSUB to BASIC 65EBH, 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 5D56H, 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
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 => 2CH CF 2C
RST 08H with the byte 2CH: the character at Register Pair HL (BASIC's text after the start position) must be a comma before the compare length, else the ROM gives SYNTAX ERROR; RST 08H then steps Register Pair HL to the next character and loads it into Register A, with the Z FLAG set at the end of the line or statement.
4EC3
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 => 29H CF 29
RST 08H with the byte 29H: the character at Register Pair HL (BASIC's text after the compare length) must be a closing parenthesis, else the ROM gives SYNTAX ERROR; RST 08H then steps Register Pair HL to the next character and loads it into Register A, with the Z FLAG set at the end of the line or statement.
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
Load Register A with the byte at 5102H, the index-sort flag (01H if the first array had an asterisk).
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,(4301H) 3A 01 43
Load Register A with the byte at 4301H, 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,(4318H) 3A 18 43
Load Register A with the byte at 4318H, 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,(4307H) 2A 07 43
Load Register Pair HL with the 16-bit value at 4307H, 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 65EBH, 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 5D56H, 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
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 with Register Pair HL = BASIC's text pointer to the ROM's statement loop, the caller of BASIC's CMD link (the dispatcher's 4BC5H was dropped at 4D00H, the RST 28H's return by the dispatcher).
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,4300H DD 21 00 43
Load Index Register IX with 4300H, 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 4317H (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 4317H, 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
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,4300H 21 00 43
Load Register Pair HL with 4300H, 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,4317H DD 21 17 43
Load Index Register IX with 4317H, 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,4317H DD 21 17 43
Load Index Register IX with 4317H, block 2, so that the compare loop at 4F6DH starts with the first key.
50D6
RET C9
RETURN with Index Register IX at 4317H (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 65F4H 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
GOSUB to BASIC 65F4H, 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 Register Pair 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,4300H DD 21 00 43
Load Index Register IX with 4300H, 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
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
JUMP to BASIC 65FFH, 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 6608H stores the high byte.
51A9
GOSUB to BASIC 6608H, 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 Register Pair 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.
51E2-51E7
DEFB 00H x 6 00 x 6
6 bytes of 00H to the end of the file (51E7H). Nothing in SYS21/SYS reads or writes them.