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

Page Customization

Introduction/Summary

NEWDOS/80 v2.0 SYS16/SYS Disassembly - PDRIVE (Model I)

SYS16/SYS finishes the PDRIVE command: it changes, copies and shows the ten drive records in a diskette's system data sector and computes their derived bytes. It loads into the overlay area at 4D00H-51E7H (1,256 bytes in five load records, transfer address 4D00H). SYS7 parses PDRIVE password:d, checks the diskette's password (when option AA is on) and calls SYS16 with RST 28H code 32H (function 1 of directory slot 12H), Register B = the drive and HL = the rest of the command line.

The page shows every byte of the file once, in address order.

Parameters (from the code)

KeywordRecord byteValue
TI=+13, +14Letters A-P, one bit each. Exactly one of A (standard), B (Omikron), C (Percom), D (Radio Shack doubler), E (LNW) is required; F, G, N, O, P are not allowed; H, I, J, K, L, M set flags.
TD=+15A letter A-H: bit 0 = 8-inch, bit 1 = two sides, bit 2 = double density.
TC=, SPT=, TSR=, GPL=, DDSL=, DDGA=+3, +4, +12, +5, +8, +9Numbers 0-255 (decimal, or hex with H): tracks, sectors per track (at most 10/17/20/34/18/30/36/60 for TD A-H), step rate (0-3), granules per lump (1-8), the directory's lump and its size in granules (2-6).

PDRIVE d shows the table; PDRIVE d,n,keyword=value,... changes record n; PDRIVE d,n=m copies record m to n. For drive 0 the first parameter may be A (after writing, the records of the drives in use, AL of them, are also put into SYS0's in-memory table at 4371H) or B (only the in-memory table; the diskette is not written). Keywords LLD= and NLD= are in the file after the table's end marker and cannot be used.

Each record then gets +0 (the directory's lump = DDSL), +1 (the number of lumps, at most 192), +2 (TSR, the interface in bits 2-4, TI M, K, H), +6 (the byte for the FDC's sector register at select, the same for all records: 00H, 80H/C0H for Omikron or LNW, 80H/A0H for the Radio Shack doubler) and +7 (the disk I/O flags). A record with an impossible combination shows *** ERROR ***; if record 0 is unusable, or two interface kinds are used, PDRIVE ends through SYS0 4030H after the table is shown (the sector has been written already).

Variables

Locations inside SYS16 whose contents change while it runs:

Address RangePurpose
4DE2H-4DE8H
7 bytes
The sector write (CALL 4FD2H, CALL 443CH, RET NZ); option B replaces it with NOPs (51D9H).
4E05H
1 byte
The A/B option of drive 0: 00H in the file, 41H after A or B (operand of 4E04H; written at 51C2H, 51CFH, cleared at 4FF8H).
4EB1H
1 byte
The record being worked on, 0-9 (operand of 4EB0H; written at 4D1CH, 4DA5H and 4E25H).
511DH-512AH
14 bytes
FCB for the PDRIVE sector: the drive at 5123H (written at 4D08H), NEXT at 5127H (sector 2, written at 4FD4H).

Locations outside SYS16 that it reads or writes:

Address RangePurpose
4200H-429FH
160 bytes
The ten 16-byte PDRIVE records in the sector buffer (record n at 4200H + 16 n).
42A0H
1 byte
The SYSTEM option AL in the same sector: the number of drives (4E0EH, 4E31H).
4371H-4398H
40 bytes
SYS0's in-memory PDRIVE table, 10 bytes per drive, updated for the drives in use with option A or B (4E0BH).

Disassembly

4D00H - PDRIVE

SYS7 handles the start of PDRIVE password:d[,parameters]: it selects drive d, checks the diskette's password (when option AA is on) and calls SYS16 with RST 28H code 32H (function 1 of directory slot 12H), Register B = the drive and Register Pair HL = the rest of the command line (SYS7 4D65H). SYS16 reads the drive's system data sector (sector 2, the PDRIVE sector) into 4200H through its own FCB at 511DH, changes one record, recomputes every record's derived bytes, writes the sector back and shows the table. The forms, from the code: PDRIVE d (show), PDRIVE d,n,keyword=value[,...] (change record n, 0-9), PDRIVE d,n=m (copy record m to n), and for drive 0 a first parameter A (also copy the records of the drives in use to SYS0's in-memory table at 4371H) or B (only in memory, the diskette is not written). Each record is 16 bytes at 4200H + 16 times its number.

4D00
CP 32H FE 32
Compare Register A (the RST 28H code) against 32H, the only code SYS16 handles.
4D02
LD A,2AH 3E 2A
Load Register A with 2AH, the DOS error code ILLEGAL DOS FUNCTION. LD does not change the flags.
4D04
If the NZ FLAG is set (another code), JUMP to 504BH, which leaves through SYS0's error exit with Register A = 2AH.
4D07
LD A,B 78
Load Register A with Register B, the drive number from SYS7.
4D08
LD (5123H),A 32 23 51
Self-Modifying Code
Store Register A (the drive) at 5123H, byte 6 of the FCB at 511DH (the drive).
4D0B
GOSUB to 4FD2H, which sets the FCB's NEXT to sector 2 and returns Register Pair DE = 511DH, the FCB.
4D0E
GOSUB to SYS0 4436H, which reads the sector at NEXT (the PDRIVE sector) into the buffer at 4200H; the NZ FLAG is set on an error, with the code in Register A.
4D11
RET NZ C0
If the NZ FLAG is set (a read error), RETURN with it.
4D12
GOSUB to 4FDBH, which checks the separator at Register Pair HL (CARRY for a bad one, Z at the end) and, the first time for drive 0, handles the A and B options.
4D15
RET C D8
If the CARRY FLAG is set (a bad separator), RETURN with it (error 34H).
4D16
If the Z FLAG is set (no parameters), JUMP to 4DE9H to show the table.
4D19
GOSUB to 4D5EH, which reads the record number at Register Pair HL into Register A (0-9, else error 20H).
4D1C
LD (4EB1H),A 32 B1 4E
Self-Modifying Code
Store Register A (the record number) at 4EB1H, the operand of the LD A,FFH at 4EB0H, which selects the record.
4D1F
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the character after the record number.
4D20
CP 3DH FE 3D
Compare Register A against 3DH, an equals sign: n=m copies record m.
4D22
If the NZ FLAG is set (no =), JUMP to 4D3FH for keywords.
4D24
INC HL 23
Increment Register Pair HL past the = to the source record's number.
4D25
GOSUB to 4D5EH, which reads the number of the record to copy from into Register A (0-9).
4D28
RLCA 07
Rotate Register A left: the source record's number times 2.
4D29
RLCA 07
Rotate Register A left: the source record's number times 4.
4D2A
RLCA 07
Rotate Register A left: the source record's number times 8.
4D2B
RLCA 07
Rotate Register A left: times 16, the source record's offset in the sector.
4D2C
PUSH HL E5
Save Register Pair HL (the command line) on the stack.
4D2D
LD H,42H 26 42
Load Register H with 42H, the high byte of the sector buffer.
4D2F
LD L,A 6F
Load Register L with Register A. Register Pair HL = 4200H + 16 times the source record: its first byte.
4D30
XOR A AF
Load Register A with 00H (XOR A with itself), the offset of the record's first byte.
4D31
GOSUB to 4EA9H, which returns Index Register IX = 4200H + 16 times the target record (4EB1H) + Register A (0): the target record.
4D34
PUSH IX DD E5
Push Index Register IX (the target record) on the stack.
4D36
POP DE D1
Restore Register Pair DE from the stack: DE = the target record.
4D37
LD BC,0010H 01 10 00
Load Register Pair BC with 0010H (16), the length of a record.
4D3A
LDIR ED B0
LDIR: copy BC bytes (16) from Register Pair HL (the source record) to Register Pair DE (the target record).
4D3C
POP HL E1
Restore Register Pair HL from the stack: the command line.
4D3D
JUMP to 4D9EH for the next parameter at Register Pair HL.

KEYWORD LOOP
Register Pair HL = the command line; the record being changed is the one in 4EB1H.

4D3F
GOSUB to 4FDBH, which checks the separator at Register Pair HL: CARRY for a bad one, Z at the end.
4D42
RET C D8
If the CARRY FLAG is set (a bad separator), RETURN with it (error 34H).
4D43
If the Z FLAG is set (nothing follows), JUMP to 4DE9H to show the table.
4D46
LD BC,512BH 01 2B 51
Load Register Pair BC with 512BH, the keyword table (TI=, TD=, TC=, SPT=, TSR=, GPL=, DDSL=, DDGA=).
4D49
LD D,01H 16 01
Load Register D with 01H: one attribute byte follows each keyword in the table.
4D4B
GOSUB to 5026H, which finds the keyword at Register Pair HL in the table (error 34H if it is not there) and returns Register Pair BC at its attribute byte, HL past the keyword and its =.
4D4E
GOSUB to 4EA6H, which returns Index Register IX = the field of the record for this keyword (the attribute's low four bits give its offset).
4D51
LD A,(BC) 0A
Load Register A with the byte at Register Pair BC, the keyword's attribute.
4D52
BIT 7,A CB 7F
Test bit 7 of Register A: set for a keyword that takes a number.
4D54
If the Z FLAG is set (TI= or TD=), JUMP to 4D69H.
4D56
GOSUB to 503DH, which reads a number at Register Pair HL into Register A (0-255, else error 2FH).
4D59
JUMP to 4D9BH to store Register A in the field at Index Register IX.
4D5B
JUMP to 5049H to leave with error 2FH, BAD PARAMETER(S).
4D5E
GOSUB to 503DH, which reads a number at Register Pair HL into Register A (0-255).
4D61
CP 0AH FE 0A
Compare Register A (the number) against 0AH (10): the CARRY FLAG is set for 0-9.
4D63
RET C D8
If the CARRY FLAG is set (a record number 0-9), RETURN with it in Register A.
4D64
LD A,20H 3E 20
Load Register A with 20H, the DOS error code ILLEGAL OR MISSING DRIVE #.
4D66
JUMP to 504BH to leave with the error in Register A.
4D69
BIT 6,A CB 77
Test bit 6 of Register A (the attribute): set for TI=, a list of letters.
4D6B
If the Z FLAG is set (TD=, one letter), JUMP to 4D93H.
4D6D
XOR A AF
Load Register A with 00H (XOR A with itself).
4D6E
LD (IX+00H),A DD 77 00
Store Register A (00H) in the byte at Index Register IX + 00H (record byte +13): no letters A-H yet.
4D71
LD (IX+01H),A DD 77 01
Store Register A (00H) in the byte at Index Register IX + 01H (record byte +14): no letters I-P yet.

TI LETTER LOOP
Register Pair HL = the command line at the next letter; each letter A-P sets one bit of record bytes +13 (A-H) and +14 (I-P).

4D74
PUSH IX DD E5
Push Index Register IX (record byte +13) on the stack.
4D76
POP DE D1
Restore Register Pair DE from the stack: DE = record byte +13, the letters A-H.
4D77
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the next letter.
4D78
SUB 41H D6 41
Subtract 41H (the letter A) from Register A: A = 0 ... P = 15.
4D7A
CP 08H FE 08
Compare Register A against 08H: the CARRY FLAG is set for A to H.
4D7C
If the CARRY FLAG is set (A-H, a bit of +13), JUMP to 4D85H.
4D7E
INC DE 13
Increment Register Pair DE to record byte +14, the letters I-P.
4D7F
SUB 08H D6 08
Subtract 08H from Register A: I = 0 ... P = 7.
4D81
CP 08H FE 08
Compare Register A against 08H: the CARRY FLAG is set for I to P.
4D83
If the NO CARRY FLAG is set (not a letter A-P), JUMP to 4D9EH: the list has ended.
4D85
LD B,A 47
Load Register B with Register A, the letter's bit number (0-7).
4D86
LD A,80H 3E 80
Load Register A with 80H, bit 7, which the loop rotates into place.
4D88
INC B 04
Increment Register B, so the loop runs bit number plus 1 times.
4D89
RLCA 07
Rotate Register A left: the bit moves one place (80H becomes 01H first).
4D8A
Decrement Register B and LOOP back to 4D89H: Register A = the letter's bit.
4D8C
EX DE,HL EB
Exchange Register Pairs DE and HL: HL = the record byte, DE = the command line.
4D8D
OR (HL) B6
OR Register A (the letter's bit) with the byte at Register Pair HL, the letters so far.
4D8E
LD (HL),A 77
Store Register A at Register Pair HL: the letter is added.
4D8F
EX DE,HL EB
Exchange Register Pairs DE and HL: HL = the command line again.
4D90
INC HL 23
Increment Register Pair HL to the next letter.
4D91
JUMP to 4D74H for the next letter at Register Pair HL.
4D93
LD A,(HL) 7E
TD=: load Register A with the byte at Register Pair HL, the letter.
4D94
SUB 41H D6 41
Subtract 41H (the letter A) from Register A: A = 0 ... Z = 25.
4D96
CP 1AH FE 1A
Compare Register A against 1AH (26): the NO CARRY FLAG is set if it is not a letter.
4D98
INC HL 23
Increment Register Pair HL past it (the flags are not changed).
4D99
If the NO CARRY FLAG is set (not a letter), JUMP to 4D5BH for error 2FH.
4D9B
LD (IX+00H),A DD 77 00
Store Register A (the value, a number or TD's letter number) in the field at Index Register IX + 00H.
4D9E
GOSUB to 4FDBH, which checks the separator at Register Pair HL: CARRY for a bad one, Z at the end.
4DA1
RET C D8
If the CARRY FLAG is set (a bad separator), RETURN with it (error 34H).
4DA2
If the NZ FLAG is set (another keyword follows), LOOP back to 4D46H.
4DA4
XOR A AF
The end of the command: load Register A with 00H (XOR A with itself), record 0.

RECOMPUTE ALL TEN RECORDS
Register A = the record number, kept on the stack while 4EBFH works.

4DA5
LD (4EB1H),A 32 B1 4E
Self-Modifying Code
Store Register A (the record number) at 4EB1H, the operand of 4EB0H.
4DA8
PUSH AF F5
Save Register Pair AF (Register A = the record number) on the stack.
4DA9
GOSUB to 4EBFH, which computes the record's derived bytes (+0, +1, +2, +7) from its typed values.
4DAC
POP AF F1
Restore Register Pair AF from the stack: Register A = the record number.
4DAD
INC A 3C
Increment Register A, the next record number to recompute.
4DAE
CP 0AH FE 0A
Compare Register A against 0AH (10): the Z FLAG is set after record 9.
4DB0
If the NZ FLAG is set (records remain), LOOP back to 4DA5H.
4DB2
GOSUB to 4FFBH, which returns Register C = the interface letters B-G used by all ten records together, and makes record 0 unusable (the NO CARRY FLAG) if two or more kinds are used.
4DB5
LD IX,4200H DD 21 00 42
Load Index Register IX with 4200H, record 0 in the sector buffer.
4DB9
LD B,0AH 06 0A
Load Register B with 0AH (10), the records to do.

SELECT BYTE LOOP
Index Register IX = the record, Register B = the records left, Register C = the interface letters in use.

4DBB
LD E,(IX+07H) DD 5E 07
Load Register E with the byte at Index Register IX + 07H, the record's disk I/O flags (bit 0 double density, bit 7 8-inch).
4DBE
LD D,00H 16 00
Load Register D with 00H, the byte for the floppy controller's sector register at drive select (+6) when no special interface is used.
4DC0
LD A,C 79
Load Register A with Register C, the interface letters in use.
4DC1
AND 12H E6 12
AND Register A with 12H, keeping TI B (Omikron, bit 1) and TI E (LNW, bit 4).
4DC3
If the Z FLAG is set (neither), JUMP to 4DCFH.
4DC5
LD D,80H 16 80
Load Register D with 80H, the select byte for Omikron or LNW.
4DC7
BIT 7,E CB 7B
Test bit 7 of Register E, the record's 8-inch flag.
4DC9
If the Z FLAG is set (5-inch), JUMP to 4DD8H with Register D = 80H.
4DCB
LD D,C0H 16 C0
Load Register D with C0H, the select byte for an 8-inch drive on those interfaces.
4DCD
JUMP to 4DD8H to store Register D (C0H) in the record.
4DCF
BIT 3,C CB 59
Test bit 3 of Register C: TI D, the Radio Shack doubler.
4DD1
If the Z FLAG is set (no doubler), JUMP to 4DD8H with Register D = 00H.
4DD3
LD D,80H 16 80
Load Register D with 80H, the select byte for a double density drive on the doubler.
4DD5
GOSUB to 5195H, which changes Register D to A0H if the record is single density (Register E bit 0 clear).
4DD8
LD (IX+06H),D DD 72 06
Store Register D (the select byte) in the byte at Index Register IX + 06H, record byte +6.
4DDB
LD DE,0010H 11 10 00
Load Register Pair DE with 0010H (16), the length of a record.
4DDE
ADD IX,DE DD 19
Add Register Pair DE to Index Register IX: IX = the next record.
4DE0
Decrement Register B and LOOP back to 4DBBH for all ten records.
4DE2
GOSUB to 4FD2H, which sets NEXT to sector 2 and returns Register Pair DE = the FCB. (Option B puts 00H, NOP, into 4DE2H-4DE8H, so the sector is not written.)
4DE5
GOSUB to SYS0 443CH, which writes the buffer at 4200H as the PDRIVE sector and reads it back to verify; the NZ FLAG is set on an error.
4DE8
RET NZ C0
If the NZ FLAG is set (a write error), RETURN with it; otherwise execution goes on to show the table.

4DE9H - Show the Table

One line per record 0-9: its number (with * for the drives in use, below option AL), then every keyword with its value; an unusable record ends with *** ERROR ***. Then, with option A or B, SYS0's in-memory table is updated.

4DE9
XOR A AF
Load Register A with 00H (XOR A with itself), record 0.
4DEA
PUSH AF F5
Save Register Pair AF (Register A = the record number) on the stack.
4DEB
GOSUB to 4E25H, which shows the record whose number is in Register A.
4DEE
POP AF F1
Restore Register Pair AF from the stack: Register A = the record number.
4DEF
INC A 3C
Increment Register A, the next record number to show.
4DF0
CP 0AH FE 0A
Compare Register A against 0AH (10): the CARRY FLAG is set while records remain.
4DF2
If the CARRY FLAG is set, LOOP back to 4DEAH.
4DF4
GOSUB to 4FFBH: the NO CARRY FLAG is set if two or more interface kinds are used (record 0 is then made unusable).
4DF7
LD HL,50EBH 21 EB 50
Load Register Pair HL with 50EBH, the message TI= SPEC BETWEEN DRIVES INCOMPATIBLE. LD does not change the flags.
4DFA
If the NO CARRY FLAG is set, GOSUB to SYS0 4467H, which displays the message at Register Pair HL.
4DFD
LD A,(4202H) 3A 02 42
Load Register A with the byte at 4202H, byte +2 of record 0 (its interface bits; 0 when unusable).
4E00
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if record 0 is unusable.
4E01
If the Z FLAG is set, JUMP to SYS0 4030H, which ends the command with the error already displayed.
4E04
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the A/B option; the operand 4E05H is 00H in the file, 41H (A) after option A or B (stored at 51C2H and 51CFH).
4E06
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set without the option.
4E07
RET Z C8
If the Z FLAG is set (no A or B), RETURN: done.
4E08
LD HL,4200H 21 00 42
Load Register Pair HL with 4200H, record 0 in the sector buffer.
4E0B
LD DE,4371H 11 71 43
Load Register Pair DE with 4371H, SYS0's in-memory PDRIVE table (10 bytes per drive).
4E0E
LD A,(42A0H) 3A A0 42
Load Register A with the byte at 42A0H, the SYSTEM option AL in the system data sector: the number of drives.
4E11
DEC A 3D
Decrement Register A, the number of drives (AL), so 1-4 become 0-3.
4E12
CP 04H FE 04
Compare Register A against 04H: the CARRY FLAG is set for 1-4 drives.
4E14
If the CARRY FLAG is set (1-4 drives), JUMP to 4E18H with Register A = 0-3.
4E16
LD A,03H 3E 03
Load Register A with 03H: any other number counts as 4 drives.
4E18
INC A 3C
Increment Register A: Register A = 1-4, the records to copy.

COPY LOOP
Register Pair HL = the record in the sector, Register Pair DE = SYS0's table, Register A = the records left.

4E19
LD BC,000AH 01 0A 00
Load Register Pair BC with 000AH (10), the bytes of a record that SYS0 keeps.
4E1C
LDIR ED B0
LDIR: copy BC bytes (10) from Register Pair HL (the record) to Register Pair DE (SYS0's table).
4E1E
LD C,06H 0E 06
Load Register C with 06H: Register Pair BC = 6 (Register B is 00H after LDIR).
4E20
ADD HL,BC 09
Add Register Pair BC (6) to Register Pair HL, skipping the rest of the 16-byte record.
4E21
DEC A 3D
Decrement Register A, the records left to copy to SYS0's table.
4E22
If the NZ FLAG is set (records left), LOOP back to 4E19H.
4E24
RET C9
RETURN with SYS0's table updated for the drives in use.
4E25
LD (4EB1H),A 32 B1 4E
Self-Modifying Code
Store Register A (the record number) at 4EB1H, the operand of 4EB0H.
4E28
PUSH AF F5
Save Register Pair AF (Register A = the record number) on the stack.
4E29
ADD A,30H C6 30
Add 30H to Register A: the record number as a digit character.
4E2B
GOSUB to 501EH, which shows the character in Register A.
4E2E
LD B,03H 06 03
Load Register B with 03H, the columns to fill after the number.
4E30
POP AF F1
Restore Register Pair AF from the stack: Register A = the record number.
4E31
LD HL,42A0H 21 A0 42
Load Register Pair HL with 42A0H, the SYSTEM option AL in the sector: the number of drives.
4E34
CP (HL) BE
Compare Register A (the record number) with the byte at Register Pair HL (AL): the CARRY FLAG is set for a drive in use.
4E35
If the NO CARRY FLAG is set (not in use), JUMP to 4E3DH.
4E37
DEC B 05
Decrement Register B: one column is taken by the *.
4E38
LD A,2AH 3E 2A
Load Register A with 2AH, an asterisk marking a drive in use.
4E3A
GOSUB to 501EH to show the asterisk in Register A.
4E3D
LD A,20H 3E 20
Load Register A with 20H, a space to fill a column after the record number.
4E3F
GOSUB to 501EH to show the space in Register A.
4E42
Decrement Register B and LOOP back to 4E3DH until the columns are filled.
4E44
LD BC,512BH 01 2B 51
Load Register Pair BC with 512BH, the keyword table.

KEYWORD DISPLAY LOOP
Register Pair BC walks the keyword table: the keyword's characters, 00H, the attribute byte.

4E47
LD A,(BC) 0A
Load Register A with the byte at Register Pair BC, a character of the keyword (00H at its end).
4E48
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set at the 00H.
4E49
INC BC 03
Increment Register Pair BC (the flags are not changed).
4E4A
If the NZ FLAG is set (a character), GOSUB to 501EH to show it.
4E4D
If the NZ FLAG is set, LOOP back to 4E47H; after the 00H, Register Pair BC points at the attribute byte.
4E4F
GOSUB to 4EA6H, which returns Index Register IX = the record's field for this keyword.
4E52
LD A,(BC) 0A
Load Register A with the byte at Register Pair BC, the keyword's attribute.
4E53
INC BC 03
Increment Register Pair BC to the next keyword.
4E54
LD E,(IX+00H) DD 5E 00
Load Register E with the byte at Index Register IX + 00H, the field's value.
4E57
LD D,(IX+01H) DD 56 01
Load Register D with the byte at Index Register IX + 01H, the byte after the field (the letters I-P for TI=).
4E5A
PUSH BC C5
Save Register Pair BC (the table pointer) on the stack.
4E5B
BIT 7,A CB 7F
Test bit 7 of Register A (the attribute): set for a number.
4E5D
If the NZ FLAG is set (a number), JUMP to 4E7CH.
4E5F
BIT 6,A CB 77
Test bit 6 of Register A: set for TI=, a list of letters.
4E61
If the NZ FLAG is set (TI=, a list of letters), JUMP to 4E6BH.
4E63
LD A,E 7B
TD=: load Register A with Register E, the letter's number.
4E64
ADD A,41H C6 41
Add 41H (the letter A) to Register A: TD's letter.
4E66
GOSUB to 501EH to show the letter in Register A.
4E69
JUMP to 4E81H for the next keyword, with the table pointer on the stack.
4E6B
LD B,10H 06 10
TI=: load Register B with 10H (16), the letters A-P to test.
4E6D
LD C,41H 0E 41
Load Register C with 41H, the letter A, the first letter.
4E6F
RR D CB 1A
Rotate Register D right through the CARRY FLAG (the letters I-P, high byte).
4E71
RR E CB 1B
Rotate Register E right through the CARRY FLAG: the next letter's bit falls into the CARRY FLAG.
4E73
LD A,C 79
Load Register A with Register C, the current letter.
4E74
INC C 0C
Increment Register C to the next letter (the CARRY FLAG is not changed).
4E75
If the CARRY FLAG is set (the letter is in the list), GOSUB to 501EH to show the letter in Register A.
4E78
Decrement Register B and LOOP back to 4E6FH for all 16 letters.
4E7A
JUMP to 4E81H for the next keyword, with the table pointer on the stack.
4E7C
LD D,00H 16 00
A number: load Register D with 00H, so Register Pair DE = the field's value (Register E).
4E7E
GOSUB to 50A8H, which shows Register Pair DE in decimal.
4E81
POP BC C1
Restore Register Pair BC from the stack: the table pointer at the next keyword.
4E82
LD A,(BC) 0A
Load Register A with the byte at Register Pair BC, the next keyword's first byte (00H at the end of the table).
4E83
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set at the end.
4E84
If the Z FLAG is set (all keywords shown), JUMP to 4E8DH.
4E86
LD A,2CH 3E 2C
Load Register A with 2CH, a comma between the keywords.
4E88
GOSUB to 501EH to show the comma in Register A.
4E8B
JUMP to 4E47H for the next keyword at Register Pair BC.
4E8D
GOSUB to 4EBFH, which checks the record (and computes its derived bytes); Index Register IX returns at the record.
4E90
LD A,(IX+02H) DD 7E 02
Load Register A with the byte at Index Register IX + 02H, record byte +2.
4E93
AND 1CH E6 1C
AND Register A with 1CH, keeping the interface bits 2-4 (0 when the record is unusable).
4E95
If the NZ FLAG is set (an interface, the record is usable), JUMP to 4EA1H.
4E97
LD HL,50DAH 21 DA 50
Load Register Pair HL with 50DAH, the message *** ERROR ***.
4E9A
GOSUB to SYS0 4467H, which displays the message at Register Pair HL.
4E9D
XOR A AF
Load Register A with 00H (XOR A with itself).
4E9E
LD (4202H),A 32 02 42
Store Register A (00H) at 4202H, byte +2 of record 0: any unusable record makes record 0 unusable, which stops the command at 4E01H.
4EA1
LD A,0DH 3E 0D
Load Register A with 0DH, a carriage return, ending the record's line.
4EA3
JUMP to 501EH to show the carriage return in Register A and return.

4EA6H - The Field of a Record

4EA6H: Register A = the attribute at Register Pair BC; its low four bits are the field's offset in the record. 4EA9H: Index Register IX = 4200H + 16 times the record number (operand 4EB1H) + the offset in Register A. Register Pairs HL and DE are kept.

4EA6
LD A,(BC) 0A
Load Register A with the byte at Register Pair BC, the keyword's attribute.
4EA7
AND 0FH E6 0F
AND Register A with 0FH, keeping the field's offset in the record (0-15).
4EA9
PUSH HL E5
Save Register Pair HL on the stack; it is restored at 4EBDH.
4EAA
PUSH DE D5
Save Register Pair DE on the stack; it is restored at 4EBCH.
4EAB
LD HL,4200H 21 00 42
Load Register Pair HL with 4200H, record 0 in the sector buffer.
4EAE
LD D,L 55
Load Register D with Register L (00H), the high byte for the offset.
4EAF
LD E,A 5F
Load Register E with Register A, the field's offset.
4EB0
LD A,FFH 3E FF
Self-Modifying Code
Load Register A with the record number; the operand 4EB1H (FFH in the file) is stored at 4D1CH, 4DA5H and 4E25H.
4EB2
RLCA 07
Rotate Register A left: the record number times 2.
4EB3
RLCA 07
Rotate Register A left: the record number times 4.
4EB4
RLCA 07
Rotate Register A left: the record number times 8.
4EB5
RLCA 07
Rotate Register A left: times 16, the record's offset in the sector.
4EB6
ADD A,E 83
Add Register E (the field's offset) to Register A.
4EB7
LD E,A 5F
Load Register E with Register A: Register Pair DE = the field's offset in the sector.
4EB8
ADD HL,DE 19
Add Register Pair DE to Register Pair HL (4200H): HL = the field's address.
4EB9
PUSH HL E5
Push Register Pair HL (the field's address) on the stack.
4EBA
POP IX DD E1
Pop Index Register IX from the stack: IX = the field's address.
4EBC
POP DE D1
Restore Register Pair DE from the stack (saved at 4EAAH).
4EBD
POP HL E1
Restore Register Pair HL from the stack (saved at 4EA9H).
4EBE
RET C9
RETURN with Index Register IX = the field's address in the sector.

4EBFH - Compute a Record's Derived Bytes

From the typed values (TC at +3, SPT +4, GPL +5, DDSL +8, DDGA +9, TSR +12, TI +13/+14, TD +15) the record gets +0 (the lump where the directory starts), +1 (the number of lumps), +2 (TSR, the interface, TI M, K and H) and +7 (the disk I/O flags). Any value out of range returns early with +2's interface bits clear, which marks the record unusable. TD letters: bit 0 = 8-inch, bit 1 = two sides, bit 2 = double density. The record number is in 4EB1H.

4EBF
XOR A AF
Load Register A with 00H (XOR A with itself), the offset of the record's first byte.
4EC0
GOSUB to 4EA9H, which returns Index Register IX = the record.
4EC3
LD A,(IX+02H) DD 7E 02
Load Register A with the byte at Index Register IX + 02H, record byte +2.
4EC6
AND E3H E6 E3
AND Register A with E3H, clearing the interface bits 2-4: the record counts as unusable until the end.
4EC8
LD (IX+02H),A DD 77 02
Store Register A in the byte at Index Register IX + 02H.
4ECB
LD A,(IX+04H) DD 7E 04
Load Register A with the byte at Index Register IX + 04H, SPT (sectors per track).
4ECE
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set for 0.
4ECF
LD C,A 4F
Load Register C with Register A, SPT. LD does not change the flags.
4ED0
RET Z C8
If the Z FLAG is set (SPT = 0), RETURN: the record stays unusable.
4ED1
LD A,(IX+0FH) DD 7E 0F
Load Register A with the byte at Index Register IX + 0FH, TD (the letter's number).
4ED4
CP 08H FE 08
Compare Register A against 08H: the CARRY FLAG is set for A to H.
4ED6
LD E,A 5F
Load Register E with Register A, TD. LD does not change the flags.
4ED7
LD D,00H 16 00
Load Register D with 00H, so Register Pair DE = TD as an index.
4ED9
If the NO CARRY FLAG is set (TD above H), JUMP to 4EE2H; 4F7EH rejects it later.
4EDB
LD HL,5115H 21 15 51
Load Register Pair HL with 5115H, the table of the most sectors per track for TD A-H.
4EDE
ADD HL,DE 19
Add Register Pair DE (TD) to Register Pair HL: HL = TD's entry.
4EDF
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the most sectors per track for this TD.
4EE0
CP C B9
Compare Register A (the most) with Register C (SPT): the CARRY FLAG is set if SPT is higher.
4EE1
RET C D8
If the CARRY FLAG is set (too many sectors), RETURN: unusable.
4EE2
LD L,C 69
Load Register L with Register C, SPT, for the multiply by TC.
4EE3
LD A,(IX+03H) DD 7E 03
Load Register A with the byte at Index Register IX + 03H, TC (the number of tracks).
4EE6
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set for 0.
4EE7
RET Z C8
If the Z FLAG is set (TC = 0), RETURN: unusable.
4EE8
GOSUB to SYS0 4C92H, which multiplies Register L (SPT) by Register A (TC): HL = the sectors on the diskette.
4EEB
BIT 4,(IX+0EH) DD CB 0E 66
Test bit 4 of the byte at Index Register IX + 0EH, record byte +14: TI M.
4EEF
LD A,05H 3E 05
Load Register A with 05H, five sectors per granule. LD does not change the flags.
4EF1
NOP 00
NOP: no operation (a byte left in the file); execution goes on to 4EF2H.
4EF2
If the NZ FLAG is set (TI M), GOSUB to 518DH, which makes Register A = 3 sectors per granule when TD is double density.
4EF5
GOSUB to SYS0 4CB4H, which divides Register Pair HL (the sectors) by Register A: HL = the granules on the diskette.
4EF8
LD A,(IX+09H) DD 7E 09
Load Register A with the byte at Index Register IX + 09H, DDGA (the directory's size in granules).
4EFB
CP 02H FE 02
Compare Register A against 02H: the CARRY FLAG is set for 0 or 1.
4EFD
LD B,A 47
Load Register B with Register A, DDGA. LD does not change the flags.
4EFE
RET C D8
If the CARRY FLAG is set (fewer than 2), RETURN: unusable.
4EFF
CP 07H FE 07
Compare Register A (DDGA) against 07H: the NO CARRY FLAG is set for 7 or more.
4F01
RET NC D0
If the NO CARRY FLAG is set, RETURN: unusable.
4F02
EX DE,HL EB
Exchange Register Pairs DE and HL: DE = the granules on the diskette.
4F03
LD L,(IX+08H) DD 6E 08
Load Register L with the byte at Index Register IX + 08H, DDSL (the lump where the directory starts).
4F06
LD (IX+00H),L DD 75 00
Store Register L (DDSL) in the byte at Index Register IX + 00H, record byte +0.
4F09
LD A,(IX+05H) DD 7E 05
Load Register A with the byte at Index Register IX + 05H, GPL (granules per lump).
4F0C
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set for 0.
4F0D
LD C,A 4F
Load Register C with Register A, GPL. LD does not change the flags.
4F0E
RET Z C8
If the Z FLAG is set (GPL = 0), RETURN: unusable.
4F0F
CP 09H FE 09
Compare Register A (GPL) against 09H: the NO CARRY FLAG is set for 9 or more.
4F11
RET NC D0
If the NO CARRY FLAG is set, RETURN: unusable.
4F12
GOSUB to SYS0 4C92H, which multiplies Register L (DDSL) by Register A (GPL): HL = the directory's first granule.
4F15
INC HL 23
Increment Register Pair HL, counting one granule of the directory.
4F16
Decrement Register B (DDGA) and LOOP back to 4F15H: HL = the first granule after the directory.
4F18
EX DE,HL EB
Exchange Register Pairs DE and HL: DE = the granule after the directory, HL = the granules on the diskette.
4F19
OR A B7
OR Register A with itself to clear the CARRY FLAG for the subtraction.
4F1A
SBC HL,DE ED 52
Subtract Register Pair DE from Register Pair HL: the CARRY FLAG is set if the directory runs past the end of the diskette.
4F1C
RET C D8
If the CARRY FLAG is set (the directory does not fit), RETURN: unusable.
4F1D
ADD HL,DE 19
Add Register Pair DE back: HL = the granules on the diskette again.
4F1E
LD A,C 79
Load Register A with Register C, GPL (granules per lump), the divisor.
4F1F
GOSUB to SYS0 4CB4H, which divides Register Pair HL (the granules) by Register A (GPL): HL = the whole lumps, Register A = the granules left over, with the Z FLAG set if there are none.
4F22
If the Z FLAG is set (no part lump), JUMP to 4F25H.
4F24
INC HL 23
Increment Register Pair HL: a part lump counts as a lump.
4F25
LD (IX+01H),L DD 75 01
Store Register L (the number of lumps) in the byte at Index Register IX + 01H, record byte +1.
4F28
DEC HL 2B
Decrement Register Pair HL: the number of lumps minus 1.
4F29
LD DE,00C0H 11 C0 00
Load Register Pair DE with 00C0H (192), the most lumps a GAT can hold.
4F2C
OR A B7
OR Register A with itself to clear the CARRY FLAG for the subtraction.
4F2D
SBC HL,DE ED 52
Subtract Register Pair DE from Register Pair HL: the NO CARRY FLAG is set for more than 192 lumps.
4F2F
RET NC D0
If the NO CARRY FLAG is set, RETURN: unusable.
4F30
LD C,(IX+0DH) DD 4E 0D
Load Register C with the byte at Index Register IX + 0DH, TI letters A-H (record byte +13).
4F33
LD B,(IX+0EH) DD 46 0E
Load Register B with the byte at Index Register IX + 0EH, TI letters I-P (record byte +14).
4F36
LD A,C 79
Load Register A with Register C, the letters A-H.
4F37
AND 7FH E6 7F
AND Register A with 7FH, leaving out H, which is not an interface.
4F39
LD L,A 6F
Load Register L with Register A, the interface letters A-G.
4F3A
LD A,B 78
Load Register A with Register B, the letters I-P.
4F3B
AND 00H E6 00
AND Register A with 00H: none of I-P is an interface, so Register A = 0.
4F3D
LD H,A 67
Load Register H with Register A (00H). Register Pair HL = the interface letters as 16 bits.
4F3E
LD D,10H 16 10
Load Register D with 10H (16), the bits to count.
4F40
XOR A AF
Load Register A with 00H (XOR A with itself), the count of interface letters.

COUNT THE INTERFACE LETTERS
Register Pair HL is shifted left 16 times; Register A counts the bits that come out.

4F41
ADD HL,HL 29
Add Register Pair HL to itself: the top bit goes into the CARRY FLAG.
4F42
ADC A,00H CE 00
Add 00H with the CARRY FLAG to Register A, counting the bit.
4F44
DEC D 15
Decrement Register D, the bits of Register Pair HL left to count.
4F45
If the NZ FLAG is set (bits left), LOOP back to 4F41H.
4F47
DEC A 3D
Decrement Register A: the Z FLAG is set if exactly one of A-G was given.
4F48
RET NZ C0
If the NZ FLAG is set (none, or more than one), RETURN: unusable.
4F49
LD A,C 79
Load Register A with Register C, the letters A-H.
4F4A
AND 60H E6 60
AND Register A with 60H, keeping F and G, which have no interface.
4F4C
RET NZ C0
If the NZ FLAG is set (F or G), RETURN: unusable.
4F4D
LD A,B 78
Load Register A with Register B, the letters I-P.
4F4E
AND E0H E6 E0
AND Register A with E0H, keeping N, O and P, which have no meaning.
4F50
RET NZ C0
If the NZ FLAG is set (N, O or P given), RETURN: unusable.
4F51
LD D,14H 16 14
Load Register D with 14H, byte +2's value for interface 5 (TI E, LNW).
4F53
LD L,(IX+0FH) DD 6E 0F
Load Register L with the byte at Index Register IX + 0FH, TD (bit 0 8-inch, bit 1 two sides, bit 2 double density).
4F56
BIT 4,C CB 61
Test bit 4 of Register C, the TI letters A-H: TI E (LNW).
4F58
If the NZ FLAG is set (TI E, which allows every TD), JUMP to 4F7DH.
4F5A
LD D,08H 16 08
Load Register D with 08H, byte +2's value for interface 2 (TI D, the Radio Shack doubler).
4F5C
BIT 3,C CB 59
Test bit 3 of Register C, the TI letters A-H: TI D (the doubler).
4F5E
If the NZ FLAG is set (TI D), JUMP to 4F70H: no 8-inch drives.
4F60
BIT 1,C CB 49
Test bit 1 of Register C, the TI letters A-H: TI B (Omikron).
4F62
If the Z FLAG is set (not TI B), JUMP to 4F6AH.
4F64
LD D,0CH 16 0C
Load Register D with 0CH, byte +2's value for interface 3 (TI B, Omikron).
4F66
BIT 2,L CB 55
Test bit 2 of Register L, TD's double density bit.
4F68
JUMP to 4F7CH, where the NZ FLAG (double density) makes the record unusable.
4F6A
BIT 2,C CB 51
Test bit 2 of Register C, the TI letters A-H: TI C (Percom).
4F6C
If the Z FLAG is set (not TI C), JUMP to 4F74H.
4F6E
LD D,10H 16 10
Load Register D with 10H, byte +2's value for interface 4 (TI C, Percom).
4F70
BIT 0,L CB 45
Test bit 0 of Register L, TD's 8-inch bit, which TI C and TI D cannot do.
4F72
JUMP to 4F7CH, where the NZ FLAG (8-inch) makes the record unusable.
4F74
BIT 0,C CB 41
Test bit 0 of Register C, the TI letters A-H: TI A (standard).
4F76
RET Z C8
If the Z FLAG is set (no known interface letter), RETURN: unusable.
4F77
LD D,04H 16 04
Load Register D with 04H, byte +2's value for interface 1 (TI A, the standard controller).
4F79
LD A,L 7D
Load Register A with Register L, TD (bit 0 8-inch, bit 2 double density).
4F7A
AND 05H E6 05
AND Register A with 05H, keeping the 8-inch and double density bits, which TI A cannot do.
4F7C
RET NZ C0
If the NZ FLAG is set (TD not allowed on this interface), RETURN: unusable.
4F7D
LD A,L 7D
Load Register A with Register L, TD, for the range check.
4F7E
CP 08H FE 08
Compare Register A against 08H: the NO CARRY FLAG is set for TD above H.
4F80
RET NC D0
If the NO CARRY FLAG is set, RETURN: unusable.
4F81
LD E,00H 1E 00
Load Register E with 00H, the disk I/O flags being built.
4F83
BIT 2,L CB 55
Test bit 2 of Register L, TD's double density bit.
4F85
If the Z FLAG is set (single density), JUMP to 4F89H.
4F87
SET 0,E CB C3
Set bit 0 of Register E: flags bit 0, double density.
4F89
BIT 0,L CB 45
Test bit 0 of Register L, TD's 8-inch bit, for the disk I/O flags.
4F8B
If the Z FLAG is set (5-inch), JUMP to 4F8FH.
4F8D
SET 7,E CB FB
Set bit 7 of Register E: flags bit 7, 8-inch.
4F8F
BIT 1,L CB 4D
Test bit 1 of Register L, TD's two-sides bit.
4F91
If the Z FLAG is set (one side), JUMP to 4F95H.
4F93
SET 6,E CB F3
Set bit 6 of Register E: flags bit 6, two sides.
4F95
LD A,(IX+0CH) DD 7E 0C
Load Register A with the byte at Index Register IX + 0CH, TSR (the step rate, 0-3).
4F98
CP 04H FE 04
Compare Register A against 04H: the NO CARRY FLAG is set for 4 or more.
4F9A
RET NC D0
If the NO CARRY FLAG is set, RETURN: unusable.
4F9B
OR D B2
OR Register A (TSR) with Register D (the interface value).
4F9C
LD D,A 57
Load Register D with Register A: byte +2 so far (TSR in bits 0-1, the interface in bits 2-4).
4F9D
BIT 4,B CB 60
Test bit 4 of Register B, the TI letters I-P: TI M.
4F9F
If the Z FLAG is set (no TI M), JUMP to 4FABH.
4FA1
SET 5,D CB EA
Set bit 5 of Register D: byte +2 bit 5, TI M.
4FA3
BIT 0,E CB 43
Test bit 0 of Register E, the double density flag.
4FA5
LD A,06H 3E 06
Load Register A with 06H, the GPL that TI M with double density needs. LD does not change the flags.
4FA7
If the NZ FLAG is set (double density), GOSUB to 5187H, which sets flags bit 4 (sectors count from 1) and compares GPL with 6: the Z FLAG is set if GPL is 6.
4FAA
RET NZ C0
If the NZ FLAG is set (GPL is not 6), RETURN: unusable.
4FAB
BIT 3,B CB 58
Test bit 3 of Register B, the TI letters I-P: TI L.
4FAD
If the Z FLAG is set (no TI L), JUMP to 4FB1H.
4FAF
SET 2,E CB D3
Set bit 2 of Register E: flags bit 2, double step.
4FB1
BIT 7,C CB 79
Test bit 7 of Register C, the TI letters A-H: TI H.
4FB3
If the Z FLAG is set (no TI H), JUMP to 4FB7H.
4FB5
SET 7,D CB FA
Set bit 7 of Register D: byte +2 bit 7, TI H.
4FB7
BIT 1,B CB 48
Test bit 1 of Register B, the TI letters I-P: TI J.
4FB9
If the Z FLAG is set (no TI J), JUMP to 4FBDH.
4FBB
SET 1,E CB CB
Set bit 1 of Register E: flags bit 1, tracks count from 1.
4FBD
BIT 0,B CB 40
Test bit 0 of Register B, the TI letters I-P: TI I.
4FBF
If the Z FLAG is set (no TI I), JUMP to 4FC3H.
4FC1
SET 4,E CB E3
Set bit 4 of Register E: flags bit 4, sectors count from 1.
4FC3
BIT 2,B CB 50
Test bit 2 of Register B, the TI letters I-P: TI K.
4FC5
If the Z FLAG is set (no TI K), JUMP to 4FCBH.
4FC7
SET 6,D CB F2
Set bit 6 of Register D: byte +2 bit 6, TI K.
4FC9
SET 1,E CB CB
Set bit 1 of Register E: flags bit 1, tracks count from 1.
4FCB
LD (IX+07H),E DD 73 07
Store Register E (the disk I/O flags) in the byte at Index Register IX + 07H, record byte +7.
4FCE
LD (IX+02H),D DD 72 02
Store Register D (byte +2 with the interface bits: the record is usable) in the byte at Index Register IX + 02H.
4FD1
RET C9
RETURN with the record's derived bytes complete.

4FD2H - Helpers

4FD2H points the FCB at sector 2. 4FDBH reads a separator and, the first time for drive 0, handles the A and B options (51C0H). 4FFBH checks that at most one interface kind is used. 501EH shows a character. 5026H looks up a keyword.

4FD2
LD A,02H 3E 02
Load Register A with 02H, the PDRIVE sector's number.
4FD4
LD (5127H),A 32 27 51
Self-Modifying Code
Store Register A (2) at 5127H, the low byte of NEXT (FCB + 10) of the FCB at 511DH.
4FD7
LD DE,511DH 11 1D 51
Load Register Pair DE with 511DH, the FCB for the PDRIVE sector.
4FDA
RET C9
RETURN with Register Pair DE = the FCB and NEXT = sector 2.
4FDB
GOSUB to SYS0 4CD5H, which checks the separator at Register Pair HL: the CARRY FLAG is set for a bad one, the Z FLAG at the end of the command, otherwise HL moves to the next item.
4FDE
RET C D8
If the CARRY FLAG is set (a bad separator), RETURN with it.
4FDF
RET Z C8
If the Z FLAG is set (the end of the command), RETURN with Z.
4FE0
LD A,(5123H) 3A 23 51
Load Register A with the byte at 5123H, the drive in the FCB.
4FE3
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set for drive 0.
4FE4
RET NZ C0
If the NZ FLAG is set (drives 1-9), RETURN with NZ: a parameter follows.
4FE5
LD DE,4E05H 11 05 4E
Load Register Pair DE with 4E05H, the A/B option byte (the operand of 4E04H).
4FE8
LD A,(DE) 1A
Load Register A with the byte at Register Pair DE, the A/B option.
4FE9
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if it is not set yet.
4FEA
RET NZ C0
If the NZ FLAG is set (the option was already handled), RETURN with NZ.
4FEB
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the parameter's first character.
4FEC
JUMP to 51C0H to look for A or B in Register A.
4FEF-4FF0
DEFB 00H, 00H 00 00
Two 00H bytes between the JP at 4FECH and 4FF1H; no code jumps, calls or reads here.
4FF1
INC HL 23
Increment Register Pair HL past the A (or the B turned into an A).
4FF2
GOSUB to SYS0 4CD5H, which checks what follows at Register Pair HL: the NO CARRY FLAG for a separator or the end.
4FF5
RET NC D0
If the NO CARRY FLAG is set (the A stood alone), RETURN with the flags from 4CD5H (Z at the end, NZ if a parameter follows).
4FF6
DEC HL 2B
Otherwise the letter began a word, not the option: decrement Register Pair HL back to it.
4FF7
XOR A AF
Load Register A with 00H (XOR A with itself).
4FF8
LD (DE),A 12
Store Register A (00H) at Register Pair DE (4E05H): no A/B option.
4FF9
INC A 3C
Increment Register A to 01H, which clears the Z FLAG: a parameter follows.
4FFA
RET C9
RETURN with NZ and Register Pair HL at the parameter.
4FFB
LD HL,420DH 21 0D 42
Load Register Pair HL with 420DH, record 0's TI letters A-H (record byte +13).
4FFE
LD BC,0A00H 01 00 0A
Load Register Pair BC with 0A00H: Register B = 10 records, Register C = 00H, the letters collected.
5001
LD DE,0010H 11 10 00
Load Register Pair DE with 0010H (16), the length of a record.

COLLECT THE INTERFACE LETTERS
Register Pair HL = the record's TI byte, Register C = the letters B-G of all records so far.

5004
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, this record's TI letters A-H.
5005
AND 7EH E6 7E
AND Register A with 7EH, keeping B-G (A, the standard controller, mixes with any; H is no interface).
5007
OR C B1
OR Register A with Register C, the letters so far.
5008
LD C,A 4F
Load Register C with Register A, the letters of all records so far.
5009
ADD HL,DE 19
Add Register Pair DE (16) to Register Pair HL: HL = the next record's TI byte.
500A
Decrement Register B and LOOP back to 5004H for all ten records.
500C
LD B,08H 06 08
Load Register B with 08H, the bits of Register C to count.
500E
XOR A AF
Load Register A with 00H (XOR A with itself), the count of interface kinds.
500F
RLC C CB 01
Rotate Register C left: its top bit goes into the CARRY FLAG.
5011
If the NO CARRY FLAG is set (the bit was clear), JUMP to 5014H.
5013
INC A 3C
Increment Register A, counting an interface kind.
5014
Decrement Register B and LOOP back to 500FH; after 8 rotations Register C holds the letters again.
5016
CP 02H FE 02
Compare Register A (the kinds) against 02H: the CARRY FLAG is set for 0 or 1.
5018
RET C D8
If the CARRY FLAG is set (at most one kind), RETURN with CARRY.
5019
XOR A AF
Load Register A with 00H (XOR A with itself).
501A
LD (4202H),A 32 02 42
Store Register A (00H) at 4202H, byte +2 of record 0: record 0 is unusable.
501D
RET C9
RETURN with the NO CARRY FLAG: two or more interface kinds.
501E
PUSH DE D5
Save Register Pair DE on the stack; the ROM's display routine changes it.
501F
PUSH AF F5
Save Register Pair AF (Register A = the character to show) on the stack.
5020
GOSUB to the ROM's display routine at 0033H, which shows the character in Register A on the screen.
5023
POP AF F1
Restore Register Pair AF from the stack: Register A = the character.
5024
POP DE D1
Restore Register Pair DE from the stack, as it was before the display call.
5025
RET C9
RETURN with Register Pairs AF and DE unchanged.

KEYWORD LOOKUP
Register Pair BC = the table entry, Register Pair HL = the command line, Register D = the bytes after each keyword's 00H.

5026
GOSUB to SYS0 4CC5H, which compares the keyword at Register Pair BC with the text at Register Pair HL: the Z FLAG is set on a match, with BC at the attribute byte and HL past the keyword.
5029
RET Z C8
If the Z FLAG is set (found), RETURN with Register Pair BC at the attribute byte.
502A
LD A,(BC) 0A
Load Register A with the byte at Register Pair BC, a byte of the keyword.
502B
CP 00H FE 00
Compare Register A against 00H, the end of the keyword.
502D
INC BC 03
Increment Register Pair BC (the flags are not changed).
502E
If the NZ FLAG is set (not the end), LOOP back to 502AH.
5030
LD E,D 5A
Load Register E with Register D, the bytes after the keyword's 00H (1, the attribute).
5031
INC BC 03
Increment Register Pair BC over one of the bytes after the keyword's 00H.
5032
DEC E 1D
Decrement Register E, the bytes left to skip.
5033
If the NZ FLAG is set (bytes left), LOOP back to 5031H.
5035
LD A,(BC) 0A
Load Register A with the byte at Register Pair BC, the next keyword's first byte (00H at the end of the table).
5036
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set at the end.
5037
If the NZ FLAG is set (another keyword), LOOP back to 5026H.
5039
LD A,34H 3E 34
Load Register A with 34H, the DOS error code ILLEGAL KEYWORD OR SEPARATOR OR TERMINATOR.
503B
JUMP to 504BH to leave with the error in Register A.

503DH - Read a Number

A decimal number, or a hexadecimal one ended by H (the same routine as in SYS7, SYS14 and SYS17). 503DH also requires it to be 0-255 and returns it in Register A. A bad number is error 2FH BAD PARAMETER(S). Register Pair HL is the command line.

503D
GOSUB to 504EH, which reads the number at Register Pair HL into Register Pair DE.
5040
JUMP to 5045H to check that it fits in one byte.
5042
GOSUB to 506CH; no code jumps or calls here, so this instruction is never executed.
5045
LD A,D 7A
Load Register A with Register D, the high byte of the number.
5046
OR A B7
OR Register A with itself to set the flags: the Z FLAG is set if it is 0.
5047
LD A,E 7B
Load Register A with Register E, the low byte of the number. LD does not change the flags.
5048
RET Z C8
If the Z FLAG is set (0-255), RETURN with the number in Register A.
5049
LD A,2FH 3E 2F
Load Register A with 2FH, the DOS error code BAD PARAMETER(S).
504B
JUMP to SYS0 4409H, the DOS error exit, with the error code in Register A.
504E
PUSH HL E5
Save Register Pair HL (the start of the number) on the stack.
504F
GOSUB to 5071H, which reads decimal digits at Register Pair HL into Register Pair DE; HL is left on the first character that is not a digit.
5052
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the character after the digits.
5053
SUB 41H D6 41
Subtract 41H (the letter A) from Register A, the character after the digits.
5055
CP 08H FE 08
Compare Register A against 08H: the CARRY FLAG is set for A to H, a hexadecimal digit or the H that ends one.
5057
If the NO CARRY FLAG is set (a decimal number), JUMP to 5066H.
5059
POP HL E1
Restore Register Pair HL from the stack: back to the start of the number.
505A
LD B,01H 06 01
Load Register B with 01H: bit 0 set, read hexadecimal digits.
505C
PUSH HL E5
Save Register Pair HL (the start of the number) on the stack again, to be discarded at 5068H.
505D
GOSUB to 5073H, which reads hexadecimal digits at Register Pair HL into Register Pair DE.
5060
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the character after the digits.
5061
CP 48H FE 48
Compare Register A against 48H, the letter H.
5063
INC HL 23
Increment Register Pair HL past it (the flags are not changed).
5064
If the NZ FLAG is set (no H), JUMP to 5049H for error 2FH.
5066
BIT 1,B CB 48
Test bit 1 of Register B: set once a digit has been read.
5068
POP BC C1
Restore Register Pair BC from the stack, discarding the saved start of the number.
5069
RET NZ C0
If the NZ FLAG is set (a digit was read), RETURN with the number in Register Pair DE.
506A
JUMP to 5049H for error 2FH: no digit was read (Register B bit 1 clear).
506C
PUSH HL E5
Save Register Pair HL on the stack. Only 5042H, itself never executed, leads here.
506D
LD DE,5066H 11 66 50
Load Register Pair DE with 5066H, the digit check.
5070
PUSH DE D5
Push Register Pair DE (5066H) as a return address.
5071
LD B,00H 06 00
Load Register B with 00H: decimal digits, none read yet.
5073
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, the number so far.

DIGIT LOOP
Register Pair HL = the command line, Register Pair DE = the number so far, Register B = the flags (bit 0 hexadecimal, bit 1 a digit read).

5076
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the next character.
5077
SUB 30H D6 30
Subtract 30H (the digit 0) from Register A, the character: 0-9 become 00H-09H.
5079
CP 0AH FE 0A
Compare Register A against 0AH: the CARRY FLAG is set for 0-9.
507B
If the CARRY FLAG is set (a digit 0-9), JUMP to 5087H with its value in Register A.
507D
BIT 0,B CB 40
Test bit 0 of Register B: set when reading hexadecimal digits.
507F
RET Z C8
If the Z FLAG is set (decimal), RETURN: the number ends here.
5080
SUB 11H D6 11
Subtract 11H more from Register A: the letters A-F become 00H-05H.
5082
CP 06H FE 06
Compare Register A against 06H: the NO CARRY FLAG is set if it is not A-F.
5084
RET NC D0
If the NO CARRY FLAG is set, RETURN: the number ends here.
5085
ADD A,0AH C6 0A
Add 0AH to Register A: the letters A-F become their values 10-15.
5087
PUSH HL E5
Save Register Pair HL (the command line) on the stack.
5088
LD H,D 62
Load Register H with Register D, the high byte of the number so far.
5089
LD L,E 6B
Load Register L with Register E. Register Pair HL = the number so far (Register Pair DE keeps it too, for times 5).
508A
LD C,A 4F
Load Register C with Register A, the new digit's value.
508B
XOR A AF
Load Register A with 00H (XOR A with itself): Register A collects any overflow.
508C
SET 1,B CB C8
Set bit 1 of Register B: a digit has been read.
508E
ADD HL,HL 29
Add Register Pair HL (the number so far) to itself: times 2.
508F
ADC A,A 8F
Add Register A to itself with the CARRY FLAG: an overflow makes Register A non-zero.
5090
ADD HL,HL 29
Add Register Pair HL to itself: the number so far times 4.
5091
ADC A,A 8F
Add Register A to itself with the CARRY FLAG, collecting any overflow.
5092
BIT 0,B CB 40
Test bit 0 of Register B: set for hexadecimal.
5094
If the Z FLAG is set (decimal), JUMP to 5099H.
5096
ADD HL,HL 29
Hexadecimal: add Register Pair HL to itself, times 8.
5097
JUMP to 509AH with Register Pair HL = 8 times the number (hexadecimal).
5099
ADD HL,DE 19
Decimal: add Register Pair DE (the number) to Register Pair HL (4 times it): times 5.
509A
ADC A,A 8F
Add Register A to itself with the CARRY FLAG, collecting any overflow.
509B
ADD HL,HL 29
Add Register Pair HL to itself: times 16 (hexadecimal) or 10 (decimal).
509C
ADC A,A 8F
Add Register A to itself with the CARRY FLAG, collecting any overflow.
509D
LD E,C 59
Load Register E with Register C, the new digit's value.
509E
LD D,00H 16 00
Load Register D with 00H, so Register Pair DE = the digit.
50A0
ADD HL,DE 19
Add Register Pair DE (the digit) to Register Pair HL.
50A1
ADC A,A 8F
Add Register A to itself with the CARRY FLAG: Register A is non-zero (NZ) if the number passed FFFFH at any step.
50A2
EX DE,HL EB
Exchange Register Pairs DE and HL: DE = the new number.
50A3
POP HL E1
Restore Register Pair HL from the stack: the command line at the digit.
50A4
RET NZ C0
If the NZ FLAG is set (the number passed FFFFH), RETURN early with Register Pair HL on the digit; no error is given for it here.
50A5
INC HL 23
Increment Register Pair HL to the next character.
50A6
LOOP back to 5076H for the next digit at Register Pair HL.

50A8H - Show a Number in Decimal

Shows Register Pair DE in decimal without leading zeros (the same routine as SYS17 4EDEH), using the negative powers of ten at 50D2H.

50A8
LD BC,0400H 01 00 04
Load Register Pair BC with 0400H: Register B = 4 powers of ten, Register C = 0 (no digit shown yet).
50AB
LD HL,50D2H 21 D2 50
Load Register Pair HL with 50D2H, the table of negative powers of ten.

DIGIT LOOP
Register Pair DE = the part of the number not shown yet, Register Pair HL = the next power in the table.

50AE
PUSH BC C5
Save Register Pair BC (the counts) on the stack.
50AF
LD C,(HL) 4E
Load Register C with the byte at Register Pair HL, the low byte of the negative power of ten.
50B0
INC HL 23
Increment Register Pair HL to the high byte of the negative power of ten.
50B1
LD B,(HL) 46
Load Register B with the byte at Register Pair HL. Register Pair BC = minus the power of ten.
50B2
INC HL 23
Increment Register Pair HL to the next power in the table.
50B3
EX DE,HL EB
Exchange Register Pairs DE and HL: HL = the number, DE = the table pointer.
50B4
LD A,2FH 3E 2F
Load Register A with 2FH, one below the character 0.
50B6
INC A 3C
Increment Register A, counting how often the power goes into the number.
50B7
ADD HL,BC 09
Add Register Pair BC (minus the power) to Register Pair HL: subtract the power; the CARRY FLAG is set while the result is not negative.
50B8
If the CARRY FLAG is set (it went), LOOP back to 50B6H.
50BA
SBC HL,BC ED 42
Subtract Register Pair BC with the CARRY FLAG (clear here) from Register Pair HL: add the power back once.
50BC
POP BC C1
Restore Register Pair BC from the stack: the counts.
50BD
EX DE,HL EB
Exchange Register Pairs DE and HL: DE = the rest of the number, HL = the table pointer.
50BE
CP 30H FE 30
Compare Register A (the digit character) against 30H, the digit 0.
50C0
If the NZ FLAG is set (not 0), JUMP to 50C6H to show it.
50C2
INC C 0C
Increment Register C (the digits shown so far)...
50C3
DEC C 0D
...and decrement Register C again: the Z FLAG is set if no digit has been shown yet (Register C = 0).
50C4
If the Z FLAG is set (a leading 0), JUMP to 50CAH without showing it.
50C6
INC C 0C
Increment Register C: a digit has been shown.
50C7
GOSUB to 501EH to show the digit in Register A.
50CA
Decrement Register B (the powers left) and LOOP back to 50AEH.
50CC
LD A,E 7B
Load Register A with Register E, the units left in the number (0-9).
50CD
ADD A,30H C6 30
Add 30H to Register A: the units digit, always shown.
50CF
JUMP to 501EH to show the units digit in Register A and return.
50D2-50D9
DEFW D8F0H, FC18H, FF9CH, FFF6H F0 D8 18 FC 9C FF F6 FF
DEFW D8F0H, FC18H, FF9CH, FFF6H: the powers of ten 10000, 1000, 100 and 10 as negative numbers, added at 50B7H.

50DAH - Messages and Tables

Data, not code, apart from three small routines at 5187H-519AH that 4EF2H, 4FA7H and 4DD5H call.

50DA-50EA
DEFM " *** ERROR ***", 03H 20 20 20 2A 2A 2A 20 45 52 52 4F 52 20 2A 2A 2A 03
The message *** ERROR *** shown after a record that cannot be used (displayed at 4E9AH through SYS0 4467H).
50EB-5114
DEFM "**** TI= SPEC BETWEEN DRIVES INCOMPATIBLE", 0DH 2A 2A 2A 2A 20 54 49 3D 20 53 50 45 43 20 42 45 54 57 45 45 4E 20 44 52 49 56 45 53 20 49 4E 43 4F 4D 50 41 54 49 42 4C 45 0D
The message TI= SPEC BETWEEN DRIVES INCOMPATIBLE, shown when two or more interface kinds are used (4DFAH).
5115-511C
DEFB 0AH, 11H, 14H, 22H, 12H, 1EH, 24H, 3CH 0A 11 14 22 12 1E 24 3C
The most sectors per track for TD A-H, one byte each: 10, 17, 20, 34, 18, 30, 36, 60 (read at 4EDFH).
511D-512A
DEFB 82H, 60H, 00H, 00H, 42H, 00H, 00H, FFH, 00H, 00H, 02H, 00H, FFH, FFH 82 60 00 00 42 00 00 FF 00 00 02 00 FF FF
The FCB for the PDRIVE sector, 14 bytes: open, NEXT/EOF counted in sectors of the diskette (82H), buffer 4200H, the drive at 5123H (byte 6, stored at 4D08H), NEXT = sector 2 at 5127H (stored at 4FD4H), EOF FFFFH.
512B-512F
DEFM "TI=", 00H, "M" 54 49 3D 00 4D
Keyword TI= and its 00H end, then attribute 4DH: bit 6 (a list of letters), field offset 13.
5130-5134
DEFM "TD=", 00H, 0FH 54 44 3D 00 0F
Keyword TD= and its 00H end, then attribute 0FH: one letter, field offset 15.
5135-5139
DEFM "TC=", 00H, 83H 54 43 3D 00 83
Keyword TC= and its 00H end, then attribute 83H: bit 7 (a number), field offset 3.
513A-513F
DEFM "SPT=", 00H, 84H 53 50 54 3D 00 84
Keyword SPT= and its 00H end, then attribute 84H: a number, field offset 4.
5140-5145
DEFM "TSR=", 00H, 8CH 54 53 52 3D 00 8C
Keyword TSR= and its 00H end, then attribute 8CH: a number, field offset 12.
5146-514B
DEFM "GPL=", 00H, 85H 47 50 4C 3D 00 85
Keyword GPL= and its 00H end, then attribute 85H: a number, field offset 5.
514C-5152
DEFM "DDSL=", 00H, 88H 44 44 53 4C 3D 00 88
Keyword DDSL= and its 00H end, then attribute 88H: a number, field offset 8.
5153-5159
DEFM "DDGA=", 00H, 89H 44 44 47 41 3D 00 89
Keyword DDGA= and its 00H end, then attribute 89H: a number, field offset 9.
515A-5179
NOP x 32 00 x 32
The 00H at 515AH ends the keyword table (tested at 4E83H and 5036H); zero bytes follow up to 5179H.
517A-517F
DEFM "LLD=", 00H, 8AH 4C 4C 44 3D 00 8A
Keyword LLD= with attribute 8AH (a number, offset 10). Never reached: the table ends at 515AH, so PDRIVE does not accept or show it.
5180-5185
DEFM "NLD=", 00H, 8BH 4E 4C 44 3D 00 8B
Keyword NLD= with attribute 8BH (a number, offset 11). Never reached either, for the same reason.
5186
DEFB 00H 00
A 00H byte after NLD=, which would end the table if 515AH did not end it first.
5187
SET 4,E CB E3
Set bit 4 of Register E, the disk I/O flags: sectors count from 1 (TI M with double density, called from 4FA7H).
5189
CP (IX+05H) DD BE 05
Compare Register A (6) with the byte at Index Register IX + 05H, GPL: the Z FLAG is set if GPL is 6.
518C
RET C9
RETURN with the Z FLAG set if GPL is 6, which 4FAAH tests.
518D
BIT 2,(IX+0FH) DD CB 0F 56
Test bit 2 of the byte at Index Register IX + 0FH, TD's double density bit (TI M, called from 4EF2H).
5191
RET Z C8
If the Z FLAG is set (single density), RETURN with Register A = 5 sectors per granule.
5192
LD A,03H 3E 03
Load Register A with 03H: TI M with double density has 3 sectors per granule.
5194
RET C9
RETURN with Register A = 3 sectors per granule.
5195
BIT 0,E CB 43
Test bit 0 of Register E, the double density flag of the record (called from 4DD5H for the doubler).
5197
RET NZ C0
If the NZ FLAG is set (double density), RETURN with Register D = 80H.
5198
LD D,A0H 16 A0
Load Register D with A0H, the select byte for a single density drive on the Radio Shack doubler.
519A
RET C9
RETURN with Register D = A0H, the single density select byte.
519B-51BF
NOP x 37 00 x 37
Zero bytes between the routines and 51C0H; no code jumps, calls or reads here.

51C0H - The A and B Options of Drive 0

Reached from 4FECH for the first parameter of PDRIVE 0, with Register A = its first character, Register Pair DE = 4E05H (the option byte) and Register Pair HL at the character. A: after the sector is written, the records of the drives in use are also copied to SYS0's in-memory table (4371H). B: the same, but the diskette is not written: 4DE2H-4DE8H (the write) are replaced by NOPs and the B in the command line becomes an A.

51C0
CP 41H FE 41
Compare Register A (the parameter's first character) against 41H, the letter A.
51C2
LD (DE),A 12
Self-Modifying Code
Store Register A (the character) at Register Pair DE, 4E05H, the operand of 4E04H (4FF7H clears it again if this is not the option). LD does not change the flags.
51C3
If the NZ FLAG is set (not A), JUMP to 51C8H.
51C5
JUMP to 4FF1H, which checks that the A stands alone.
51C8
CP 42H FE 42
Compare Register A (the character) against 42H, the letter B.
51CA
If the NZ FLAG is set (neither A nor B), JUMP to 4FF7H, which clears the option: an ordinary parameter.
51CD
LD A,41H 3E 41
Load Register A with 41H, the letter A, which option B is turned into.
51CF
LD (DE),A 12
Self-Modifying Code
Store Register A (41H) at Register Pair DE, 4E05H: the option is set.
51D0
LD (HL),A 77
Store Register A (41H) at Register Pair HL, turning the B in the command line into an A.
51D1
PUSH BC C5
Save Register Pair BC on the stack; it is restored at 51DEH.
51D2
PUSH HL E5
Save Register Pair HL (the command line) on the stack.
51D3
LD BC,0700H 01 00 07
Load Register Pair BC with 0700H: Register B = 7 bytes, Register C = 00H, the NOP opcode.
51D6
LD HL,4DE2H 21 E2 4D
Load Register Pair HL with 4DE2H, the start of the sector write (CALL 4FD2H, CALL 443CH, RET NZ).
51D9
LD (HL),C 71
Self-Modifying Code
Store Register C (00H, NOP) at Register Pair HL, over a byte of the write at 4DE2H-4DE8H.
51DA
INC HL 23
Increment Register Pair HL to the next byte of the write.
51DB
Decrement Register B and LOOP back to 51D9H for all 7 bytes.
51DD
POP HL E1
Restore Register Pair HL from the stack: the command line.
51DE
POP BC C1
Restore Register Pair BC from the stack (saved at 51D1H).
51DF
JUMP to 4FF1H, which checks that the option letter stands alone.
51E2-51E7
NOP x 6 00 x 6
Six zero bytes that fill the end of the file (up to 51E7H). No code jumps, calls or reads here.