LDOS 5.3.1 for the Model III - SYS12/SYS Disassembled

Page Customization

Introduction/Summary

LDOS 5.3.1 SYS12/SYS Disassembly - @DODIR and @RAMDIR (Model III)

SYS12/SYS holds the two directory services that programs call: @DODIR (4419H, request AEH) shows the files of a drive on the screen or lists their directory records into a buffer, and @RAMDIR (4290H, request 9EH) puts a short record of each file, or of one file, or the space used and free, into a buffer. The manual says the MiniDOS filter uses them and that other programs that show a directory may. The supervisor dispatcher loads SYS12/SYS into the overlay area at 4E00H and calls it with the request code in Register A (4E00H).

The file is a load module of 839 bytes: a record of type 1FH with the text Copyright 1991 MISOSYS, Inc., All rights reserved, three load blocks for 4E00H-50FDH, a load block for the two bytes 4BC8H-4BC9H (02FEH, the 766 bytes that SYS12/SYS takes up in the overlay area) and the transfer address record for 4E00H.

The Calls

Both services check the drive number (0-7, else error 20H), enter the file prologue of SYS0/SYS (49F1H), which gives the caller back its Register Pairs BC, DE and HL and Index Register IX when the service returns, and test that the drive is there and turning (50AEH): its code table must start with a JP, and the index pulse must go off, on and off within 15 ticks of the clock (half a second), else error 08H. The results are in the buffer, Register A and the flags.

CallRegisters and result
@RAMDIR, C = FFHB = the drive, HL = a buffer of 4 bytes: the space used in K (+00H-+01H) and the space free in K (+02H-+03H) (4E1DH).
@RAMDIR, C = 00HA record of 22 bytes for every visible file (in use, primary, not a system file, not invisible), each followed by 2BH, which the next overwrites (4EA6H).
@RAMDIR, C = nThe record of directory record n less 1 (4E3AH): error 10H when it lies beyond the directory's sectors, 19H when the file is not visible.
@DODIR, B = 0-3C = the drive, HL = a buffer starting with a mask of three characters for the extension. Bit 0 of B: list 18 bytes of each file's directory record into the buffer, ended by FFH; clear: show each visible file as NAME/EXT:d on the screen, four to a line, fifteen lines to a screen. Bit 1: only the files whose extension matches the mask (a dollar sign matches any character) (4EE6H).
@DODIR, B = 04H20 bytes: the diskette's name and date (allocation table +D0H-+DFH), then its capacity and free space in K (4F1AH).
@DODIR, B above 04HError 1DH.

The record of @RAMDIR: +00H-+0EH the name as NAME/EXT:d padded with spaces, +0FH the protection level (bits 2-0 of the attributes), +10H the end of file byte, +11H the record length, +12H-+13H the ending record number, +14H-+15H the size in K (the ending record number divided by 4). The 18 bytes of @DODIR's buffer list are the directory record's +00H-+0FH (attributes, date, end of file byte, record length, name and extension) and +14H-+15H (the ending record number), without the password hashes; that list takes system and invisible files too.

The Walk through the Directory

The lists read the hash index table (sector 1 of the directory cylinder) into 5100H and take its bytes in the order of the directory: down each column (the records of one directory sector, 20H apart), then the next column, up to 32. A byte of 00H is a free record. A directory sector is read into 4300H only when it is not the one read last, kept in the operand at 4ECAH (@RAMDIR) or 4F5AH (@DODIR). These operands are FFH only in the file and are not set back by the services, so a second call while SYS12/SYS is still in the overlay area takes 4300H as the sector of the call before when the first record lies in it.

The free space comes from the granule allocation table (sector 0) at 5100H: the number of cylinder bytes counted is the byte at 51CCH (the table's +CCH) plus 35, and each byte's 0 bits are its free granules (5025H). The capacity is the cylinders times the sectors of a cylinder; both are turned from sectors into K as (sectors + 2) / 4 (5075H).

About This Page

The Model III file is the Model I file of the same release with the same code at the same addresses; its 16 different bytes: the page of the system sector buffer 43H (4ED6H, 4F65H; Model I 42H), CFCB$ 4485H and 4488H (4F00H, 4F94H, 4FCDH; Model I 4480H, 4483H), MULTEA 4B6BH (4E4DH, 5047H) and DIRCYL 4B64H (508DH, 50A1H; the Model I routines are one byte higher), @MULT 444EH (5075H; Model I 44C1H), TIMER$ 4288H (50D2H, 50ECH; Model I 4040H), ADD A,0FH at 50D5H, 15 ticks of the Model III clock (30 a second) for the half second index pulse test (Model I ADD A,14H, 20 of 40), and the overlay length block at 4BC8H-4BC9H (Model I 4BC9H-4BCAH).

Every byte of the file is shown, in address order: the load block at 4BC8H first, then 4E00H-50FDH. SYS12/SYS changes six bytes of its own while it runs, all operands (the table of Variables).

Memory Map

Load Blocks in File Order

The four load blocks of the file in the order the overlay loader (4C8CH) reads them, after the copyright record. The transfer address is 4E00H.

AddressContents
4E00H-4EFFH256 bytes
4F00H-4FFFH256 bytes
5000H-50FDH254 bytes
4BC8H-4BC9H2 bytes

Sections of the Disassembly

AddressContents
4BC8HThe Overlay Length (Data)
4E00HThe Two Requests, and @RAMDIR
4E3AH@RAMDIR: One File's Record
4EA6H@RAMDIR: Every Visible File
4EE6H@DODIR
4F2CH@DODIR: Go through the Files
4FC4HTest the Extension against the Mask
4FE2HWrite NAME/EXT:d
5025HThe Capacity and the Free Space in K
508BHRead the Allocation Table or the Hash Index Table
50AEHIs the Drive There and Turning?

Memory SYS12/SYS Uses

AddressContents
0033H, 0049H, 01C9HROM: display a character, wait for a key, clear the screen (the screen list).
4288HTIMER$, the deadline of the index pulse test (50D2H).
4300H-43FFHThe directory sectors read by DIRRD.
4290H, 4419H@RAMDIR and @DODIR, which issue requests 9EH and AEH.
4485H-4497HCFCB$: @DODIR keeps the mask at 4485H-4487H and builds each name at 4488H.
444EH, 4467H@MULT and @DSPLY.
4759H, 475EH, 478FH, 479CHTSTBSY, SEEK, GETDCT and DCTBYT.
49F1HThe file prologue: the caller's registers come back through 4A14H.
4B10H, 4B45H, 4B64H, 4B6BHDIRRD, RDSSEC, DIRCYL and MULTEA.
4BC8H-4BC9HThe length of the system file in the overlay area: 02FEH for SYS12/SYS.
4E00H-50FDHSYS12/SYS in the overlay area.
5100H-51FFHThe hash index table, or the granule allocation table (+CCH at 51CCH, the name and date at 51D0H-51DFH).

Variables

The entry points of SYS12/SYS, then the data of the file, the operands it changes while it runs, and the cells outside the file that it uses, by address.

Entry Points

AddressNameContents
4E00H@RAMDIRRequest 9EH from 4E0AH; request AEH goes to 4EE6H.
4E3AH@RAMDIR: one file's record; 4E77H builds the 22 bytes.
4EA6H@RAMDIR: every visible file.
4EE6H@DODIRRequest AEH.
4F2CH@DODIR: the walk and the handlers of each file.
4FC4HTest the extension against the mask.
4FE2HWrite NAME/EXT:d, 15 bytes and 03H.
5025HThe capacity and the free space in K; 5075H turns sectors into K.
508BHRead the allocation table into 5100H; 509CH the hash index table.
50AEHIs the drive there and turning? 50ECH reads the status.

Data in the File

Address
Size
Contents
4BC8H-4BC9H
2 bytes
DEFW 02FEH
4BC8H-4BC9H: 02FEH (766), the number of bytes from 4E00H that SYS12/SYS takes up (4E00H-50FDH). Loading SYS12/SYS puts it over the length the system file loaded before it left here.

Operands Changed While It Runs

Address
Size
Contents
4ECAH
1 byte
The operand of CP FFH at 4EC9H: the directory sector @RAMDIR read last into 4300H (4ECDH). FFH in the file only.
4F5AH
1 byte
The operand of CP FFH at 4F59H: the directory sector @DODIR read last into 4300H (4F5DH). FFH in the file only.
4F6CH
1 byte
The operand of LD A,00H at 4F6BH: @DODIR's function (4EF4H).
4FA0H
1 byte
The operand of LD A,00H at 4F9FH: the files still to show on the line (4F0BH, 4FA2H, 4FA8H).
4FB1H
1 byte
The operand of LD A,00H at 4FB0H: the lines still to show on the screen (4F10H, 4FB3H, 4FB9H).
5017H
1 byte
The operand of LD A,00H at 5016H: the drive's digit for NAME/EXT:d (50B1H).

Cells Outside the File That SYS12/SYS Uses

Address
Size
Contents
4288H
1 byte
TIMER$, 30 ticks a second: the half second deadline of the index pulse test.
4300H-43FFH
256 bytes
The directory sector read by DIRRD; the record at its offset is the file's.
4485H-4487H
3 bytes
CFCB$ +00H-+02H: @DODIR's extension mask, copied from the caller's buffer.
4488H-4497H
16 bytes
CFCB$ +03H-+12H: the name NAME/EXT:d and 03H built for the screen list.
5100H-51FFH
256 bytes
The hash index table (one byte per record, 00H free) or the granule allocation table (byte 5100H + c for cylinder c, bit n set while granule n is in use; +CCH the cylinders less 35; +D0H-+DFH the name and date).
IY +00H, +04H-+09HThe drive code table: +00H C3H for a drive that is there, +04H bit 5 two sides and bit 4 no index test, +05H the cylinder under the head, +06H the highest cylinder, +07H and +08H the layout, +09H the directory cylinder.

Disassembly

4BC8H - The Overlay Length (Data)

Every system file from SYS1/SYS to SYS12/SYS carries a load block for the two bytes 4BC8H-4BC9H in the resident area of SYS0/SYS. They hold the length of the file's part in the overlay area: for SYS12/SYS 02FEH, the 766 bytes 4E00H-50FDH. In the file this block is the last load block, after the three blocks of 4E00H-50FDH and before the transfer address record (4E00H). The two bytes are data, not instructions.

4BC8-4BC9
DEFW 02FEH FE 02
4BC8H-4BC9H: 02FEH (766), the number of bytes from 4E00H that SYS12/SYS takes up (4E00H-50FDH). Loading SYS12/SYS puts it over the length the system file loaded before it left here.

4E00H - The Two Requests, and @RAMDIR

The supervisor dispatcher loads SYS12/SYS into 4E00H and calls it with the request code in Register A. Bits 6-4 select the service: 20H, request AEH from @DODIR (4419H), goes to 4EE6H; 10H, request 9EH from @RAMDIR (4290H), is handled here; any other function only loads the file. @RAMDIR takes Register B = the drive (0-7, else error 20H), Register C = what is wanted and Register Pair HL = a buffer. Both services enter through the file prologue of SYS0/SYS at 49F1H, so their RET goes through 4A14H, which gives the caller back its Register Pairs BC, DE and HL and Index Register IX: the results are in the buffer, Register A and the flags. With Register C = FFH the buffer gets the space of the diskette in K (1024 bytes): the space used, then the space free (4E1DH). With Register C = 00H it gets a record of 22 bytes for each visible file (4EA6H); any other Register C names directory record C less 1, and the buffer gets that file's record (4E3AH).

4E00
AND 70H E6 70
Keep bits 6-4 of Register A, the request code (AND 70H): the function, 10H for request 9EH (@RAMDIR), 20H for request AEH (@DODIR).
4E02
CP 20H FE 20
Compare Register A (the function) against 20H, @DODIR.
4E04
If the Z FLAG is set (request AEH), JUMP to 4EE6H, @DODIR.
4E07
CP 10H FE 10
Compare Register A (the function) against 10H, @RAMDIR.
4E09
RET NZ C0
RETurn with the NZ FLAG for any other function: the request only loads SYS12/SYS.
4E0A
LD A,07H 3E 07
@RAMDIR: Load Register A with 07H, the highest drive number.
4E0C
CP B B8
Compare Register A (07H) against Register B (the drive): the CARRY FLAG when the drive is above 7.
4E0D
LD A,20H 3E 20
Load Register A with 20H, error 32: illegal drive number. LD does not change the flags.
4E0F
RET C D8
RETurn with the CARRY FLAG, the NZ FLAG and Register A = 20H when the drive is above 7.
4E10
GOSUB to 49F1H, the file prologue of SYS0/SYS without the open test: the caller's Register Pairs BC, DE and HL and Index Register IX are saved and put back by 4A14H when this service returns.
4E13
LD A,B 78
Copy Register B (the drive) into Register A, to swap Register B and Register C.
4E14
LD B,C 41
Copy Register C (what is wanted) into Register B.
4E15
LD C,A 4F
Copy Register A (the drive) into Register C: Register C = the drive, as the disk routines take it, Register B = what is wanted.
4E16
GOSUB to 50AEH to see that drive Register C is there and turning: the Z FLAG when it is, else the NZ FLAG with an error number.
4E19
RET NZ C0
RETurn if the NZ FLAG is set (the drive is not ready) with the error number in Register A.
4E1A
INC B 04
INCrement Register B (what is wanted): 00H with the Z FLAG for FFH.
4E1B
If the NZ FLAG is set (not FFH), JUMP to 4E3AH.
4E1D
PUSH HL E5
Save Register Pair HL (the buffer) to the stack.
4E1E
GOSUB to 5025H to store the diskette's capacity in K at the buffer +00H-+01H and its free space in K at +02H-+03H: Register Pair HL ends at +03H, Register Pair DE = the free space; the NZ FLAG when the allocation table could not be read.
4E21
If the NZ FLAG is set (the table could not be read), JUMP to 4E38H to return the error.
4E23
LD B,(HL) 46
Fetch the high byte of the free space at Register Pair HL (+03H) into Register B.
4E24
DEC HL 2B
DECrement Register Pair HL to the buffer +02H, the low byte of the free space.
4E25
LD C,(HL) 4E
Fetch the low byte of the free space at Register Pair HL into Register C: Register Pair BC = the free space in K.
4E26
DEC HL 2B
DECrement Register Pair HL to the buffer +01H, the high byte of the capacity.
4E27
LD A,(HL) 7E
Fetch the high byte of the capacity at Register Pair HL into Register A.
4E28
DEC HL 2B
DECrement Register Pair HL to the buffer +00H, the low byte of the capacity.
4E29
LD L,(HL) 6E
Fetch the low byte of the capacity at Register Pair HL into Register L.
4E2A
LD H,A 67
Copy Register A (the high byte of the capacity) into Register H: Register Pair HL = the capacity in K.
4E2B
SBC HL,DE ED 52
SUBtract Register Pair DE (the free space, from 5075H) from Register Pair HL (the capacity), the CARRY FLAG clear after the XOR A at 5089H: Register Pair HL = the space used in K.
4E2D
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the space used.
4E2E
POP HL E1
Restore Register Pair HL (the buffer) from the stack.
4E2F
LD (HL),E 73
Store Register E (the low byte of the space used) at Register Pair HL, the buffer +00H.
4E30
INC HL 23
INCrement Register Pair HL to the buffer +01H.
4E31
LD (HL),D 72
Store Register D (the high byte of the space used) at Register Pair HL.
4E32
INC HL 23
INCrement Register Pair HL to the buffer +02H.
4E33
LD (HL),C 71
Store Register C (the low byte of the free space) at Register Pair HL.
4E34
INC HL 23
INCrement Register Pair HL to the buffer +03H.
4E35
LD (HL),B 70
Store Register B (the high byte of the free space) at Register Pair HL.
4E36
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG: no error.
4E37
RET C9
RETurn through 4A14H with the Z FLAG: the buffer holds the space used and the space free.
4E38
POP HL E1
Restore Register Pair HL (the buffer) from the stack.
4E39
RET C9
RETurn through 4A14H with the NZ FLAG and Register A = 14H, GAT read error.

4E3AH - @RAMDIR: One File's Record

Register B = what was asked, plus one. 00H (now 01H) asks for every file (4EA6H); any other value is the number of a directory record plus one. The record's sector (bits 4-0 of its number) must lie on the directory cylinder below the sectors that hold records (the sectors of a cylinder less 2, for the allocation table and the hash index table), else error 10H; the record must be in use, primary, not a system file and not invisible (bits 7, 6, 4 and 3 of its attributes 10H only), else error 19H. The record built at 4E77H for the buffer is 22 bytes: +00H-+0EH the file's name as NAME/EXT:d, padded with spaces; +0FH its protection level (bits 2-0 of the attributes); +10H its end of file byte; +11H its record length; +12H-+13H its ending record number; +14H-+15H its size in K (that number divided by 4); then 2BH at +16H, which the next record overwrites.

4E3A
DEC B 05
DECrement Register B (what was asked, plus one) back to what was asked.
4E3B
If the Z FLAG is set (00H: every file), JUMP to 4EA6H.
4E3D
INC B 04
INCrement Register B: what was asked, the directory record's number plus one, is used as the record number.
4E3E
LD A,07H 3E 07
Load Register A with 07H, the offset of the sector layout in a drive code table.
4E40
GOSUB to 479CH (DCTBYT) to fetch byte +07H of drive Register C's code table into Register A: bits 4-0 the highest sector on a track, bits 7-5 the heads less one.
4E43
LD D,A 57
Copy Register A (byte +07H of the drive code table) into Register D.
4E44
AND 1FH E6 1F
Keep bits 4-0 of Register A (AND 1FH): the highest sector on a track.
4E46
LD E,A 5F
Copy Register A (the highest sector) into Register E.
4E47
INC E 1C
INCrement Register E: the sectors on a track.
4E48
XOR D AA
Exclusive OR Register D (byte +07H) into Register A (its bits 4-0): Register A = bits 7-5 alone, the heads less one.
4E49
RLCA 07
Rotate Register A left one bit (RLCA): first of three rotations that bring bits 7-5 to bits 2-0.
4E4A
RLCA 07
Rotate Register A left one bit (RLCA): second of the three rotations.
4E4B
RLCA 07
Rotate Register A left one bit (RLCA): bits 2-0 now hold the heads less one.
4E4C
INC A 3C
INCrement Register A (the heads less one): Register A = the number of heads.
4E4D
GOSUB to 4B6BH (MULTEA) to multiply Register A (the heads) by Register E (the sectors on a track): Register A = the sectors of a cylinder on one side.
4E50
LD E,A 5F
Copy Register A (the sectors on one side) into Register E.
4E51
LD A,04H 3E 04
Load Register A with 04H, the offset of the second drive specification byte in a drive code table.
4E53
GOSUB to 479CH (DCTBYT) to fetch byte +04H of drive Register C's code table into Register A.
4E56
BIT 5,A CB 6F
Test bit 5 of Register A, set when the drive has two sides.
4E58
LD A,E 7B
Copy Register E (the sectors on one side) into Register A. LD does not change the flags.
4E59
If the Z FLAG is set (one side), JUMP to 4E5CH.
4E5B
ADD A,A 87
ADD Register A to itself: the sectors of the cylinder on both sides.
4E5C
SUB 02H D6 02
SUBtract 02H from Register A: less the allocation table and the hash index table, the sectors that hold directory records.
4E5E
LD D,A 57
Copy Register A (the directory sectors) into Register D.
4E5F
LD A,B 78
Copy Register B (the record number) into Register A.
4E60
AND 1FH E6 1F
Keep bits 4-0 of Register A (AND 1FH): the record's directory sector less 2.
4E62
CP D BA
Compare Register A against Register D (the directory sectors): the CARRY FLAG when the record lies on the directory cylinder.
4E63
If the CARRY FLAG is set (it does), JUMP to 4E69H to read it.
4E65
LD A,10H 3E 10
Load Register A with 10H, error 16: illegal logical file number.
4E67
OR A B7
Set the flags from Register A (OR A): the NZ FLAG, Register A not being 00H.
4E68
RET C9
RETurn through 4A14H with the NZ FLAG and Register A = 10H.
4E69
PUSH HL E5
Save Register Pair HL (the buffer) to the stack.
4E6A
GOSUB to 4B10H (DIRRD) to read the directory sector of record Register B of drive Register C into 4300H: Register Pair HL = the record, or the NZ FLAG with Register A = 11H.
4E6D
POP DE D1
Restore Register Pair DE from the stack: Register Pair DE = the buffer. POP does not change the flags.
4E6E
RET NZ C0
RETurn through 4A14H if the NZ FLAG is set (the sector could not be read) with Register A = 11H, error 17: directory read error.
4E6F
LD A,(HL) 7E
Fetch the record's attributes at Register Pair HL (+00H) into Register A.
4E70
AND D8H E6 D8
Keep bits 7, 6, 4 and 3 of Register A (AND D8H): extension record, system file, in use, invisible.
4E72
XOR 10H EE 10
Exclusive OR 10H into Register A: 00H with the Z FLAG only for a primary record in use that is neither a system file nor invisible.
4E74
LD A,19H 3E 19
Load Register A with 19H, error 25: file access denied. LD does not change the flags.
4E76
RET NZ C0
RETurn through 4A14H with the NZ FLAG and Register A = 19H for any other record.
4E77
PUSH HL E5
Save Register Pair HL (the directory record) to the stack. 4EE0H calls here for each file.
4E78
GOSUB to 4FE2H to write the file's name as NAME/EXT:d and spaces, 15 bytes and 03H, at Register Pair DE: Register Pair DE ends at the 03H.
4E7B
POP HL E1
Restore Register Pair HL (the directory record, +00H) from the stack.
4E7C
LD A,(HL) 7E
Fetch the record's attributes at Register Pair HL into Register A.
4E7D
AND 07H E6 07
Keep bits 2-0 of Register A (AND 07H): the file's protection level.
4E7F
LD (DE),A 12
Store Register A (the protection level) at Register Pair DE, the buffer +0FH, over the 03H.
4E80
INC DE 13
INCrement Register Pair DE to the buffer +10H.
4E81
INC L 2C
INCrement Register L to the record's +01H (the record does not cross a page).
4E82
INC L 2C
INCrement Register L to the record's +02H, the rest of the date.
4E83
INC L 2C
INCrement Register L to +03H, the end of file byte.
4E84
LDI ED A0
Copy the end of file byte from Register Pair HL to the buffer at Register Pair DE (+10H), stepping both up and Register Pair BC down (LDI).
4E86
LDI ED A0
Copy the record length (+04H) from Register Pair HL to the buffer at Register Pair DE (+11H), stepping both up and Register Pair BC down (LDI).
4E88
LD A,L 7D
Copy Register L (the low byte of the record's +05H) into Register A.
4E89
ADD A,0FH C6 0F
ADD 0FH to Register A: the low byte of the record's +14H, the ending record number.
4E8B
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the record +14H.
4E8C
LD A,(HL) 7E
Fetch the low byte of the ending record number at Register Pair HL into Register A.
4E8D
LD (DE),A 12
Store Register A at Register Pair DE, the buffer +12H.
4E8E
INC HL 23
INCrement Register Pair HL to the record +15H.
4E8F
INC DE 13
INCrement Register Pair DE to the buffer +13H.
4E90
LD H,(HL) 66
Fetch the high byte of the ending record number at Register Pair HL into Register H.
4E91
LD L,A 6F
Copy Register A (the low byte) into Register L: Register Pair HL = the ending record number.
4E92
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the buffer +13H, Register Pair DE = the ending record number.
4E93
LD (HL),D 72
Store Register D (the high byte of the ending record number) at Register Pair HL, the buffer +13H.
4E94
INC HL 23
INCrement Register Pair HL to the buffer +14H.
4E95
SRL D CB 3A
Shift Register D right (SRL): the first of two halvings of Register Pair DE.
4E97
RR E CB 1B
Rotate Register E right through the CARRY FLAG (RR), taking the bit from Register D: Register Pair DE = the records divided by 2.
4E99
SRL D CB 3A
Shift Register D right (SRL): the second halving.
4E9B
RR E CB 1B
Rotate Register E right through the CARRY FLAG (RR): Register Pair DE = the records divided by 4, the size in K.
4E9D
LD (HL),E 73
Store Register E (the low byte of the size) at Register Pair HL, the buffer +14H.
4E9E
INC HL 23
INCrement Register Pair HL to the buffer +15H.
4E9F
LD (HL),D 72
Store Register D (the high byte of the size) at Register Pair HL.
4EA0
INC HL 23
INCrement Register Pair HL to the buffer +16H.
4EA1
LD (HL),2BH 36 2B
Store 2BH at Register Pair HL, the buffer +16H, after the record: the next record starts here and overwrites it.
4EA3
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the buffer +16H, where the next record goes.
4EA4
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG.
4EA5
RET C9
RETurn with the Z FLAG: to 4EE3H, or through 4A14H for one file.

4EA6H - @RAMDIR: Every Visible File

The hash index table (sector 1 of the directory cylinder) is read into 5100H, and its bytes are taken in the order of the directory: row by row down each column (L + 20H), then the next column, up to 32 columns; a byte of 00H is a free record. For each record in use its directory sector is read into 4300H, unless the sector number matches the one kept at 4ECAH, the operand of the CP at 4EC9H; a primary record in use that is neither a system file nor invisible gets its 22 byte record in the buffer (4E77H). The operand is FFH only in the file: when @RAMDIR is called again while SYS12/SYS is still in the overlay area, the first record whose sector equals the one kept is taken from 4300H without reading it. The record of the single file at 4E6AH does not change the operand.

4EA6
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the buffer, where the records go.
4EA7
GOSUB to 509CH to read the hash index table of drive Register C into 5100H-51FFH: Register Pair HL = 5100H, the Z FLAG on success.
4EAA
RET NZ C0
RETurn through 4A14H if the NZ FLAG is set (the table could not be read) with Register A = 16H, error 22: HIT read error.
4EAB
JUMP to 4EBEH to look at the first byte of the table, record 00H.
4EAD
POP BC C1
Restore Register Pair BC from the stack: Register B = the record just done, Register C = the drive. 4EDDH and 4EE4H loop back here.
4EAE
LD H,51H 26 51
Load Register H with 51H, the page of the hash index table at 5100H.
4EB0
LD L,B 68
Copy Register B (the record just done) into Register L: Register Pair HL = its byte in the table.
4EB1
LD A,L 7D
Copy Register L (the byte's place in the table) into Register A. 4EC0H comes here past a free record.
4EB2
ADD A,20H C6 20
ADD 20H to Register A: the same column of the next row, the next record of the same directory sector.
4EB4
LD L,A 6F
Copy Register A (the next row of the column) into Register L.
4EB5
If the NO CARRY FLAG is set (a row is left in the column), JUMP to 4EBEH to look at that byte.
4EB7
INC L 2C
INCrement Register L: after the carry, row 0 of the next column, the next directory sector.
4EB8
BIT 5,L CB 6D
Test bit 5 of Register L: set when the column is past the 32 the table holds.
4EBA
If the Z FLAG is set (a column is left), JUMP to 4EBEH.
4EBC
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG: every record is done.
4EBD
RET C9
RETurn through 4A14H with the Z FLAG and the records in the buffer, the last followed by 2BH.
4EBE
LD A,(HL) 7E
Fetch the record's byte of the hash index table at Register Pair HL into Register A.
4EBF
OR A B7
Set the flags from Register A (OR A): the Z FLAG for 00H, a free record.
4EC0
If the Z FLAG is set (a free record), LOOP BACK to 4EB1H for the next one.
4EC2
LD B,L 45
Copy Register L (the record's number) into Register B, for DIRRD.
4EC3
PUSH BC C5
Save Register Pair BC (Register B = the record, Register C = the drive) to the stack.
4EC4
LD A,L 7D
Copy Register L (the record's number) into Register A.
4EC5
AND E0H E6 E0
Keep bits 7-5 of Register A (AND E0H): the record's place in its directory sector, its offset there.
4EC7
LD L,A 6F
Copy Register A into Register L: the record's offset in the sector at 4300H.
4EC8
XOR B A8
Exclusive OR Register B (the record's number) into Register A: Register A = bits 4-0 alone, the record's directory sector less 2.
4EC9
CP FFH FE FF
Compare Register A against the sector already in 4300H, the operand at 4ECAH (FFH in the file) stored by 4ECDH.
4ECB
If the Z FLAG is set (the sector is already in 4300H), JUMP to 4ED6H.
4ECD
LD (4ECAH),A 32 CA 4E
Store Register A (the sector) at 4ECAH, the operand of the CP at 4EC9H.
4ED0
GOSUB to 4B10H (DIRRD) to read the directory sector of record Register B of drive Register C into 4300H: the NZ FLAG with Register A = 11H on failure.
4ED3
If the NZ FLAG is set (the sector could not be read), JUMP to 4FC2H, which drops the record from the stack and returns the error.
4ED6
LD H,43H 26 43
Load Register H with 43H: Register Pair HL = the record in the sector at 4300H.
4ED8
LD A,(HL) 7E
Fetch the record's attributes at Register Pair HL into Register A.
4ED9
AND D8H E6 D8
Keep bits 7, 6, 4 and 3 of Register A (AND D8H): extension record, system file, in use, invisible.
4EDB
XOR 10H EE 10
Exclusive OR 10H into Register A: 00H with the Z FLAG only for a primary record in use that is neither a system file nor invisible.
4EDD
If the NZ FLAG is set (any other record), LOOP BACK to 4EADH for the next record.
4EDF
PUSH HL E5
Save Register Pair HL (the record) to the stack.
4EE0
GOSUB to 4E77H to build the file's 22 byte record at Register Pair DE in the buffer: Register Pair DE moves on to the next place.
4EE3
POP HL E1
Restore Register Pair HL (the record) from the stack.
4EE4
LOOP BACK to 4EADH for the next record of the hash index table.

4EE6H - @DODIR

Request AEH, issued by @DODIR (4419H). Register C = the drive (0-7, else error 20H), Register B = the function, Register Pair HL = a buffer whose first three bytes are a mask for the extension. The function is kept at 4F6CH, the operand of LD A at 4F6BH. Function 04H puts the diskette's name and date (allocation table +D0H-+DFH) and its capacity and free space in K into 20 bytes of the buffer (4F1AH). Functions above 04H give error 1DH. Functions 00H-03H go through the files of the directory like @RAMDIR, from 4F2CH: bit 0 set puts 18 bytes of each file's directory record into the buffer (+00H-+0FH and +14H-+15H) and ends the list with FFH; bit 0 clear shows NAME/EXT:d of each visible file on the screen, four to a line, and waits for a key and clears the screen after 15 lines. Bit 1 set shows only the files whose extension matches the mask (4FC4H). The buffer list (bit 0) takes the system files and the invisible files too; the screen list does not.

4EE6
LD A,07H 3E 07
@DODIR: Load Register A with 07H, the highest drive number.
4EE8
CP C B9
Compare Register A (07H) against Register C (the drive): the CARRY FLAG when the drive is above 7.
4EE9
LD A,20H 3E 20
Load Register A with 20H, error 32: illegal drive number. LD does not change the flags.
4EEB
RET C D8
RETurn with the CARRY FLAG, the NZ FLAG and Register A = 20H when the drive is above 7.
4EEC
GOSUB to 49F1H, the file prologue of SYS0/SYS without the open test: the caller's Register Pairs BC, DE and HL and Index Register IX are saved and put back by 4A14H when this service returns.
4EEF
GOSUB to 50AEH to see that drive Register C is there and turning: the Z FLAG when it is, else the NZ FLAG with an error number.
4EF2
RET NZ C0
RETurn through 4A14H if the NZ FLAG is set (the drive is not ready) with the error number in Register A.
4EF3
LD A,B 78
Copy Register B (the function) into Register A.
4EF4
LD (4F6CH),A 32 6C 4F
Store Register A (the function) at 4F6CH, the operand of the LD A at 4F6BH, which the file loop reads.
4EF7
CP 04H FE 04
Compare Register A (the function) against 04H.
4EF9
If the Z FLAG is set (function 04H), JUMP to 4F1AH for the diskette's name, date and space.
4EFB
LD A,1DH 3E 1D
Load Register A with 1DH, error 29: record number out of range. LD does not change the flags.
4EFD
RET NC D0
RETurn through 4A14H with the NO CARRY FLAG (function above 04H) and Register A = 1DH.
4EFE
PUSH HL E5
Save Register Pair HL (the buffer, the mask) to the stack.
4EFF
PUSH BC C5
Save Register Pair BC (Register B = the function, Register C = the drive) to the stack.
4F00
LD DE,4485H 11 85 44
Point Register Pair DE to 4485H, CFCB$, where the mask is kept while the buffer is filled.
4F03
LD BC,0003H 01 03 00
Load Register Pair BC with 0003H, the three characters of the mask.
4F06
LDIR ED B0
Copy (DE) = (HL) for Register Pair BC bytes, HL and DE counting up (LDIR): the mask goes to 4485H-4487H.
4F08
POP BC C1
Restore Register Pair BC (Register B = the function, Register C = the drive) from the stack.
4F09
LD A,04H 3E 04
Load Register A with 04H, the files on one line of the screen.
4F0B
LD (4FA0H),A 32 A0 4F
Store Register A (04H) at 4FA0H, the operand of the LD A at 4F9FH: four files to the line.
4F0E
LD A,0FH 3E 0F
Load Register A with 0FH, the lines on one screen.
4F10
LD (4FB1H),A 32 B1 4F
Store Register A (0FH) at 4FB1H, the operand of the LD A at 4FB0H: fifteen lines to the screen.
4F13
GOSUB to 509CH to read the hash index table of drive Register C into 5100H-51FFH: Register Pair HL = 5100H, the Z FLAG on success.
4F16
POP DE D1
Restore Register Pair DE from the stack: the buffer as the caller gave it, where the records go (the mask is now at 4485H).
4F17
RET NZ C0
RETurn through 4A14H if the NZ FLAG is set (the table could not be read) with Register A = 16H.
4F18
JUMP to 4F4EH to look at the first byte of the table, record 00H.
4F1A
PUSH HL E5
Function 04H: save Register Pair HL (the buffer) to the stack.
4F1B
LD DE,0010H 11 10 00
Load Register Pair DE with 0010H, the 16 bytes of the name and date before the space.
4F1E
PUSH DE D5
Save Register Pair DE (0010H) to the stack: it becomes the count of the LDIR.
4F1F
ADD HL,DE 19
ADD Register Pair DE to Register Pair HL: Register Pair HL = the buffer +10H.
4F20
GOSUB to 5025H to store the capacity in K at the buffer +10H-+11H and the free space in K at +12H-+13H. Its result is not tested.
4F23
POP BC C1
Restore Register Pair BC from the stack: 0010H, the count.
4F24
POP DE D1
Restore Register Pair DE from the stack: the buffer.
4F25
LD HL,51D0H 21 D0 51
Point Register Pair HL to 51D0H, the allocation table +D0H read by 5025H: the diskette's name (eight characters) and date (eight characters).
4F28
LDIR ED B0
Copy (DE) = (HL) for Register Pair BC (16) bytes, HL and DE counting up (LDIR): the name and date go to the buffer +00H-+0FH.
4F2A
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG.
4F2B
RET C9
RETurn through 4A14H with the Z FLAG: the buffer holds 20 bytes.

4F2CH - @DODIR: Go through the Files

The same walk through the hash index table at 5100H as @RAMDIR's (4EADH), with the sector kept at 4F5AH, the operand of the CP at 4F59H (FFH only in the file, as at 4ECAH). For each record in use the address 4F2CH is pushed as the return address, so that the handler's RET, or a RET NZ for a file passed over, goes on to the next record. At the end the buffer list gets FFH, the screen list a new line.

4F2C
POP BC C1
Restore Register Pair BC from the stack: Register B = the record just done, Register C = the drive. Each record's handler returns here.
4F2D
LD H,51H 26 51
Load Register H with 51H, the page of the hash index table at 5100H.
4F2F
LD L,B 68
Copy Register B (the record just done) into Register L: Register Pair HL = its byte in the table.
4F30
LD A,L 7D
Copy Register L (the byte's place in the table) into Register A. 4F50H comes here past a free record.
4F31
ADD A,20H C6 20
ADD 20H to Register A: the same column of the next row, the next record of the same directory sector.
4F33
LD L,A 6F
Copy Register A (the next row of the column) into Register L.
4F34
If the NO CARRY FLAG is set (a row is left in the column), JUMP to 4F4EH to look at that byte.
4F36
INC L 2C
INCrement Register L: after the carry, row 0 of the next column.
4F37
BIT 5,L CB 6D
Test bit 5 of Register L: set when the column is past the 32 the table holds.
4F39
If the Z FLAG is set (a column is left), JUMP to 4F4EH.
4F3B
LD A,(4F6CH) 3A 6C 4F
Every record is done: fetch the function from 4F6CH into Register A.
4F3E
AND 01H E6 01
Keep bit 0 of Register A (AND 01H): set for the list in the buffer.
4F40
If the NZ FLAG is set (the buffer list), JUMP to 4F49H to end it with FFH.
4F42
LD A,0DH 3E 0D
Load Register A with 0DH, to end the last line of the screen list.
4F44
GOSUB to the ROM at 0033H to display Register A (0DH).
4F47
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG.
4F48
RET C9
RETurn through 4A14H with the Z FLAG: the list is shown.
4F49
LD A,FFH 3E FF
Load Register A with FFH, the end of the buffer list.
4F4B
LD (DE),A 12
Store Register A (FFH) at Register Pair DE, after the last record of the buffer.
4F4C
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG.
4F4D
RET C9
RETurn through 4A14H with the Z FLAG: the buffer holds the list.
4F4E
LD A,(HL) 7E
Fetch the record's byte of the hash index table at Register Pair HL into Register A. 4F18H comes here first.
4F4F
OR A B7
Set the flags from Register A (OR A): the Z FLAG for 00H, a free record.
4F50
If the Z FLAG is set (a free record), LOOP BACK to 4F30H for the next one.
4F52
LD B,L 45
Copy Register L (the record's number) into Register B, for DIRRD.
4F53
PUSH BC C5
Save Register Pair BC (Register B = the record, Register C = the drive) to the stack: 4F2CH takes it back.
4F54
LD A,L 7D
Copy Register L (the record's number) into Register A.
4F55
AND E0H E6 E0
Keep bits 7-5 of Register A (AND E0H): the record's offset in its directory sector.
4F57
LD L,A 6F
Copy Register A into Register L: the record's offset in the sector at 4300H.
4F58
XOR B A8
Exclusive OR Register B (the record's number) into Register A: Register A = bits 4-0 alone, the record's directory sector less 2.
4F59
CP FFH FE FF
Compare Register A against the sector already in 4300H, the operand at 4F5AH (FFH in the file) stored by 4F5DH.
4F5B
If the Z FLAG is set (the sector is already in 4300H), JUMP to 4F65H.
4F5D
LD (4F5AH),A 32 5A 4F
Store Register A (the sector) at 4F5AH, the operand of the CP at 4F59H.
4F60
GOSUB to 4B10H (DIRRD) to read the directory sector of record Register B of drive Register C into 4300H: the NZ FLAG with Register A = 11H on failure.
4F63
If the NZ FLAG is set (the sector could not be read), JUMP to 4FC2H, which drops the record from the stack and returns the error.
4F65
LD H,43H 26 43
Load Register H with 43H: Register Pair HL = the record in the sector at 4300H.
4F67
LD BC,4F2CH 01 2C 4F
Load Register Pair BC with 4F2CH, the address of the walk.
4F6A
PUSH BC C5
Save Register Pair BC (4F2CH) to the stack: the RET of the handler, or of a file passed over, goes there.
4F6B
LD A,00H 3E 00
Load Register A with the function, the operand at 4F6CH (00H in the file) stored by 4EF4H.
4F6D
PUSH HL E5
Save Register Pair HL (the record) to the stack.
4F6E
PUSH DE D5
Save Register Pair DE (the place in the buffer) to the stack.
4F6F
GOSUB to 4FC4H to test the record's extension against the mask when bit 1 of the function asks: the Z FLAG when the file is to be listed.
4F72
POP DE D1
Restore Register Pair DE (the place in the buffer) from the stack.
4F73
POP HL E1
Restore Register Pair HL (the record) from the stack. POP does not change the flags.
4F74
RET NZ C0
RETurn to 4F2CH if the NZ FLAG is set: the extension does not match.
4F75
LD A,(4F6CH) 3A 6C 4F
Fetch the function from 4F6CH into Register A.
4F78
RRCA 0F
Rotate Register A right (RRCA): bit 0, the buffer list, goes into the CARRY FLAG.
4F79
LD A,(HL) 7E
Fetch the record's attributes at Register Pair HL into Register A. LD does not change the flags.
4F7A
If the NO CARRY FLAG is set (the screen list), JUMP to 4F8FH.
4F7C
AND 90H E6 90
Keep bits 7 and 4 of Register A (AND 90H): extension record and in use.
4F7E
XOR 10H EE 10
Exclusive OR 10H into Register A: 00H with the Z FLAG for a primary record in use, system and invisible files too.
4F80
RET NZ C0
RETurn to 4F2CH if the NZ FLAG is set: an extension record is not listed.
4F81
LD BC,0010H 01 10 00
Load Register Pair BC with 0010H, the 16 bytes +00H-+0FH of the record.
4F84
LDIR ED B0
Copy (DE) = (HL) for Register Pair BC bytes, HL and DE counting up (LDIR): the attributes, the date, the end of file byte, the record length, the name and the extension go to the buffer.
4F86
INC HL 23
INCrement Register Pair HL past +10H, the first byte of the passwords.
4F87
INC HL 23
INCrement Register Pair HL past +11H, the second byte of the first password hash.
4F88
INC HL 23
INCrement Register Pair HL past +12H, the first byte of the second password hash.
4F89
INC HL 23
INCrement Register Pair HL past +13H, to +14H: the two password hashes are not copied.
4F8A
LD C,02H 0E 02
Load Register C with 02H, the two bytes of the ending record number (Register B is 00H after the LDIR).
4F8C
LDIR ED B0
Copy (DE) = (HL) for Register Pair BC bytes, HL and DE counting up (LDIR): +14H-+15H, the ending record number: 18 bytes in all.
4F8E
RET C9
RETurn to 4F2CH with Register Pair DE at the place for the next file.
4F8F
AND D8H E6 D8
Keep bits 7, 6, 4 and 3 of Register A (AND D8H): extension record, system file, in use, invisible.
4F91
XOR 10H EE 10
Exclusive OR 10H into Register A: 00H with the Z FLAG only for a primary record in use that is neither a system file nor invisible.
4F93
RET NZ C0
RETurn to 4F2CH if the NZ FLAG is set: the file is not shown.
4F94
LD DE,4488H 11 88 44
Point Register Pair DE to 4488H, CFCB$ +03H, after the mask: the place to build the name.
4F97
PUSH DE D5
Save Register Pair DE (4488H, the start of the name) to the stack.
4F98
GOSUB to 4FE2H to write the file's name as NAME/EXT:d and spaces, 15 bytes and 03H, at 4488H.
4F9B
POP HL E1
Restore Register Pair HL from the stack: 4488H, the name built.
4F9C
GOSUB to 4467H (@DSPLY) to display the text at Register Pair HL, ended by 03H.
4F9F
LD A,00H 3E 00
Load Register A with the files still to show on this line, the operand at 4FA0H (00H in the file) stored by 4F0BH, 4FA2H and 4FA8H.
4FA1
DEC A 3D
DECrement Register A (the files left on the line): one file shown.
4FA2
LD (4FA0H),A 32 A0 4F
Store Register A (the files left on the line) at 4FA0H.
4FA5
RET NZ C0
RETurn to 4F2CH if the NZ FLAG is set: the line has room.
4FA6
LD A,04H 3E 04
Load Register A with 04H: the next line has room for four files.
4FA8
LD (4FA0H),A 32 A0 4F
Store Register A (04H) at 4FA0H, the files left on the new line.
4FAB
LD A,0DH 3E 0D
Load Register A with 0DH, to end the line of four files.
4FAD
GOSUB to the ROM at 0033H to display Register A (0DH).
4FB0
LD A,00H 3E 00
Load Register A with the lines still to show on this screen, the operand at 4FB1H (00H in the file) stored by 4F10H, 4FB3H and 4FB9H.
4FB2
DEC A 3D
DECrement Register A (the lines left): one line shown.
4FB3
LD (4FB1H),A 32 B1 4F
Store Register A (the lines left on the screen) at 4FB1H.
4FB6
RET NZ C0
RETurn to 4F2CH if the NZ FLAG is set: the screen has room.
4FB7
LD A,0FH 3E 0F
Load Register A with 0FH: the next screen has room for fifteen lines.
4FB9
LD (4FB1H),A 32 B1 4F
Store Register A (0FH) at 4FB1H, the lines left on the new screen.
4FBC
GOSUB to the ROM at 0049H to wait for a key before the screen is cleared.
4FBF
JUMP to the ROM at 01C9H to clear the screen; its RET goes to 4F2CH.
4FC2
POP BC C1
Remove the record pushed at 4EC3H or 4F53H from the stack into Register Pair BC, after a directory read error.
4FC3
RET C9
RETurn through 4A14H with the NZ FLAG and Register A = 11H.

4FC4H - Test the Extension against the Mask

Register A = the function of @DODIR, Register Pair HL = the directory record. When bit 1 is clear every file passes (the Z FLAG). Else the three characters of the extension (+0DH-+0FH) are compared with the mask at 4485H-4487H: a dollar sign in the mask matches any character, and a mask character of 41H or above is made a capital (bit 5 cleared) first. The NZ FLAG on the first difference.

4FC4
BIT 1,A CB 4F
Test bit 1 of Register A (the function): set when only the files of the mask are listed.
4FC6
RET Z C8
RETurn with the Z FLAG when bit 1 is clear: every file passes.
4FC7
LD BC,000DH 01 0D 00
Load Register Pair BC with 000DH, the offset of the extension in a directory record.
4FCA
ADD HL,BC 09
ADD Register Pair BC to Register Pair HL: Register Pair HL = the record +0DH, the extension.
4FCB
LD B,03H 06 03
Load Register B with 03H, the three characters of the extension.
4FCD
LD DE,4485H 11 85 44
Point Register Pair DE to 4485H, the mask kept by 4F06H.
4FD0
LD A,(DE) 1A
Fetch the mask's character at Register Pair DE into Register A. 4FDFH loops back here.
4FD1
CP 24H FE 24
Compare Register A against 24H, a dollar sign.
4FD3
If the Z FLAG is set (a dollar sign), JUMP to 4FDDH: any character matches.
4FD5
CP 41H FE 41
Compare Register A against 41H, a capital A: the CARRY FLAG below it.
4FD7
If the CARRY FLAG is set (a digit or a sign), JUMP to 4FDBH to compare it as it is.
4FD9
RES 5,A CB AF
Clear bit 5 of Register A: a small letter becomes a capital.
4FDB
CP (HL) BE
Compare Register A (the mask's character) against the extension's character at Register Pair HL.
4FDC
RET NZ C0
RETurn with the NZ FLAG if they differ: the file is passed over.
4FDD
INC HL 23
INCrement Register Pair HL to the extension's next character.
4FDE
INC DE 13
INCrement Register Pair DE to the mask's next character.
4FDF
DECrement Register B and LOOP BACK to 4FD0H for the next character.
4FE1
RET C9
RETurn with the Z FLAG: the extension matches the mask.

4FE2H - Write NAME/EXT:d

Register Pair HL = a directory record (+00H), Register Pair DE = the destination. The name (+05H-+0CH) is copied up to its first space, then a slash and the extension (+0DH-+0FH) up to its first space when it has one, then a colon and the drive's digit (the operand at 5017H, stored by 50B1H), then spaces up to 15 bytes in all, and 03H. Register C counts the 13 places for the name, the slash and the extension. Register Pair DE ends at the 03H.

4FE2
LD A,L 7D
Copy Register L (the low byte of the record's address) into Register A.
4FE3
ADD A,05H C6 05
ADD 05H to Register A: the low byte of the record's +05H, the name.
4FE5
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the name (the record does not cross a page).
4FE6
LD C,0DH 0E 0D
Load Register C with 0DH, the 13 places for the name, the slash and the extension.
4FE8
LD B,08H 06 08
Load Register B with 08H, the eight characters of the name.
4FEA
LD A,(HL) 7E
Fetch the name's next character at Register Pair HL into Register A. 4FF3H loops back here.
4FEB
INC HL 23
INCrement Register Pair HL to the next character.
4FEC
CP 20H FE 20
Compare Register A against 20H, a space, which ends a name shorter than eight characters.
4FEE
If the Z FLAG is set (a space), JUMP to 4FF7H.
4FF0
LD (DE),A 12
Store Register A (the character) at Register Pair DE.
4FF1
INC DE 13
INCrement Register Pair DE to the next place.
4FF2
DEC C 0D
DECrement Register C, the places left for the name, slash and extension.
4FF3
DECrement Register B and LOOP BACK to 4FEAH for the next character of the name.
4FF5
JUMP to 4FFBH: Register Pair HL is at +0DH, the extension.
4FF7
LD A,L 7D
Copy Register L (the place after the space) into Register A.
4FF8
ADD A,B 80
ADD Register B (the characters of the name left, the space among them) to Register A.
4FF9
DEC A 3D
DECrement Register A: the low byte of the record's +0DH.
4FFA
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the extension.
4FFB
LD A,(HL) 7E
Fetch the extension's first character at Register Pair HL into Register A.
4FFC
CP 20H FE 20
Compare Register A against 20H, a space: a file with no extension.
4FFE
If the Z FLAG is set (no extension), JUMP to 5012H for the drive.
5000
LD A,2FH 3E 2F
Load Register A with 2FH, a slash, before the extension.
5002
LD (DE),A 12
Store Register A (the slash) at Register Pair DE.
5003
INC DE 13
INCrement Register Pair DE to the next place.
5004
DEC C 0D
DECrement Register C, the places left, for the slash.
5005
LD B,03H 06 03
Load Register B with 03H, the three characters of the extension.
5007
LD A,(HL) 7E
Fetch the extension's next character at Register Pair HL into Register A. 5010H loops back here.
5008
INC HL 23
INCrement Register Pair HL to the next character.
5009
CP 20H FE 20
Compare Register A against 20H, a space, which ends a shorter extension.
500B
If the Z FLAG is set (a space), JUMP to 5012H.
500D
LD (DE),A 12
Store Register A (the character) at Register Pair DE.
500E
INC DE 13
INCrement Register Pair DE to the next place.
500F
DEC C 0D
DECrement Register C, the places left, for the character.
5010
DECrement Register B and LOOP BACK to 5007H for the next character of the extension.
5012
LD A,3AH 3E 3A
Load Register A with 3AH, a colon, before the drive's digit.
5014
LD (DE),A 12
Store Register A (the colon) at Register Pair DE.
5015
INC DE 13
INCrement Register Pair DE to the next place.
5016
LD A,00H 3E 00
Load Register A with the drive's digit, the operand at 5017H (00H in the file) stored by 50B1H.
5018
LD (DE),A 12
Store Register A (the drive's digit) at Register Pair DE.
5019
INC DE 13
INCrement Register Pair DE to the next place.
501A
LD A,20H 3E 20
Load Register A with 20H, a space. 501FH loops back here.
501C
LD (DE),A 12
Store Register A (the space) at Register Pair DE.
501D
INC DE 13
INCrement Register Pair DE to the next place.
501E
DEC C 0D
DECrement Register C, the places left (at least one is left).
501F
If the NZ FLAG is set (places left), LOOP BACK to 501AH.
5021
LD A,03H 3E 03
Load Register A with 03H, the end of the text.
5023
LD (DE),A 12
Store Register A (03H) at Register Pair DE, after the 15 bytes.
5024
RET C9
RETurn with Register Pair DE at the 03H, after 15 bytes.

5025H - The Capacity and the Free Space in K

Register Pair HL = the place for four bytes, Register C = the drive. The granule allocation table is read into 5100H (508BH). The capacity in sectors is the cylinders (byte +06H of the drive code table plus one) times the sectors of a cylinder (the sectors of a granule, bits 4-0 of +08H plus one, times the granules of a track, bits 7-5 plus one, doubled when bit 5 of +04H says the drive has two sides). The free space in sectors is the free granules times the sectors of a granule. The free granules are counted in the table from 5100H for as many cylinders as the byte at 51CCH (allocation table +CCH) plus 23H (35): each byte is shifted right with a 1 coming in at the top until it is FFH, and every 0 bit shifted out is a free granule. 5075H turns a count of sectors into K, (sectors + 2) / 4 from the low 16 bits of the @MULT product, and stores it: the capacity at +00H-+01H, the free space at +02H-+03H. Register Pair HL ends at +03H and Register Pair DE holds the free space in K.

5025
GOSUB to 508BH to read the granule allocation table of drive Register C into 5100H-51FFH: the Z FLAG on success, else Register A = 14H.
5028
RET NZ C0
RETurn if the NZ FLAG is set (the table could not be read) with Register A = 14H, error 20: GAT read error.
5029
PUSH IY FD E5
Save Index Register IY to the stack: GETDCT changes it.
502B
GOSUB to 478FH (GETDCT) to point Index Register IY at the drive code table of drive Register C.
502E
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the place for the four bytes.
502F
LD H,00H 26 00
Load Register H with 00H, the high byte of the count of cylinders.
5031
LD L,(IY+06H) FD 6E 06
Fetch byte +06H of the drive code table at Index Register IY into Register L: the highest cylinder.
5034
INC HL 23
INCrement Register Pair HL: the number of cylinders.
5035
LD A,(IY+08H) FD 7E 08
Fetch byte +08H of the drive code table at Index Register IY into Register A: bits 7-5 the granules on a track less one, bits 4-0 the sectors of a granule less one.
5038
AND 1FH E6 1F
Keep bits 4-0 of Register A (AND 1FH): the sectors of a granule less one.
503A
INC A 3C
INCrement Register A: the sectors of a granule.
503B
PUSH AF F5
Save Register Pair AF (Register A = the sectors of a granule) to the stack, for the free space.
503C
PUSH DE D5
Save Register Pair DE (the place for the four bytes) to the stack.
503D
LD E,A 5F
Copy Register A (the sectors of a granule) into Register E.
503E
LD A,(IY+08H) FD 7E 08
Fetch byte +08H of the drive code table at Index Register IY into Register A again.
5041
AND E0H E6 E0
Keep bits 7-5 of Register A (AND E0H): the granules on a track less one.
5043
RLCA 07
Rotate Register A left one bit (RLCA): first of three rotations that bring bits 7-5 to bits 2-0.
5044
RLCA 07
Rotate Register A left one bit (RLCA): second of the three rotations.
5045
RLCA 07
Rotate Register A left one bit (RLCA): bits 2-0 now hold the granules on a track less one.
5046
INC A 3C
INCrement Register A: the granules on a track.
5047
GOSUB to 4B6BH (MULTEA) to multiply Register A (the granules on a track) by Register E (the sectors of a granule): Register A = the sectors on a track.
504A
BIT 5,(IY+04H) FD CB 04 6E
Test bit 5 of byte +04H of the drive code table at Index Register IY, set when the drive has two sides.
504E
If the Z FLAG is set (one side), JUMP to 5051H.
5050
ADD A,A 87
ADD Register A to itself: the sectors of a cylinder on both sides.
5051
POP BC C1
Restore Register Pair BC from the stack: the place for the four bytes.
5052
GOSUB to 5075H to multiply Register Pair HL (the cylinders) by Register A (the sectors of a cylinder) and store the capacity in K at Register Pair BC (+00H-+01H): Register Pair HL ends at +01H.
5055
INC HL 23
INCrement Register Pair HL to +02H, the place for the free space.
5056
PUSH HL E5
Save Register Pair HL (the place for the free space, +02H) to the stack.
5057
LD HL,5100H 21 00 51
Point Register Pair HL to 5100H, the allocation table byte of cylinder 0.
505A
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, the count of free granules.
505D
LD A,(51CCH) 3A CC 51
Fetch the byte at 51CCH, the allocation table +CCH, into Register A.
5060
ADD A,23H C6 23
ADD 23H (35) to Register A: the number of cylinders to count.
5062
LD B,A 47
Copy Register A (the cylinders to count) into Register B.
5063
LD A,(HL) 7E
Fetch the cylinder's allocation table byte at Register Pair HL into Register A. 506EH loops back here.
5064
SCF 37
Set the CARRY FLAG, the bit to shift in at the top. 506BH loops back here.
5065
RRA 1F
Rotate Register A right through the CARRY FLAG (RRA): a 1 comes in at bit 7, bit 0 (a granule's bit) goes into the CARRY FLAG.
5066
If the CARRY FLAG is set (the granule is in use, or not on the cylinder), JUMP to 5069H.
5068
INC DE 13
INCrement Register Pair DE: one more free granule.
5069
CP FFH FE FF
Compare Register A against FFH: all 1 when every granule bit of the byte is shifted out.
506B
If the NZ FLAG is set (a 0 is left), LOOP BACK to 5064H.
506D
INC L 2C
INCrement Register L to the next cylinder's byte.
506E
DECrement Register B and LOOP BACK to 5063H for the next cylinder.
5070
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the free granules.
5071
POP BC C1
Restore Register Pair BC from the stack: the place for the free space (+02H).
5072
POP AF F1
Restore Register Pair AF from the stack: Register A = the sectors of a granule.
5073
POP IY FD E1
Restore Index Register IY from the stack. Then on into 5075H for the free space.
5075
GOSUB to 444EH (@MULT) to multiply Register Pair HL by Register A: the product's high 16 bits in Register Pair HL, its low 8 bits in Register A.
5078
LD H,B 60
Copy Register B (the high byte of the place) into Register H.
5079
LD D,L 55
Copy Register L (bits 15-8 of the product) into Register D.
507A
LD L,C 69
Copy Register C (the low byte of the place) into Register L: Register Pair HL = the place for the result.
507B
LD E,A 5F
Copy Register A (bits 7-0 of the product) into Register E: Register Pair DE = the sectors (the low 16 bits).
507C
INC DE 13
INCrement Register Pair DE (the sectors) by one.
507D
INC DE 13
INCrement Register Pair DE again: the sectors + 2, to round to the nearest K.
507E
SRL D CB 3A
Shift Register D right (SRL): the first of two halvings of Register Pair DE.
5080
RR E CB 1B
Rotate Register E right through the CARRY FLAG (RR), taking the bit from Register D.
5082
SRL D CB 3A
Shift Register D right (SRL): the second halving.
5084
RR E CB 1B
Rotate Register E right through the CARRY FLAG (RR): Register Pair DE = the sectors / 4, the K.
5086
LD (HL),E 73
Store Register E (the low byte of the K) at Register Pair HL.
5087
INC HL 23
INCrement Register Pair HL to the high byte's place.
5088
LD (HL),D 72
Store Register D (the high byte of the K) at Register Pair HL.
5089
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG.
508A
RET C9
RETurn: to 5055H after the capacity, to the caller of 5025H after the free space.

508BH - Read the Allocation Table or the Hash Index Table

508BH reads sector 0 of the directory cylinder, the granule allocation table, into 5100H (error 14H); 509CH reads sector 1, the hash index table, into 5100H (error 16H) and returns Register Pair HL = 5100H. Both use RDSSEC, which accepts only a sector with the deleted data mark, and return the Z FLAG on success.

508B
PUSH DE D5
Save Register Pair DE to the stack while Register Pair DE names the sector.
508C
PUSH HL E5
Save Register Pair HL to the stack while Register Pair HL points at the buffer.
508D
GOSUB to 4B64H (DIRCYL) to load Register D with the directory cylinder of drive Register C.
5090
LD HL,5100H 21 00 51
Load Register Pair HL with 5100H, the buffer after the code of SYS12/SYS.
5093
LD E,L 5D
Copy Register L (00H) into Register E: sector 0, the granule allocation table.
5094
GOSUB to 4B45H (RDSSEC) to read cylinder Register D, sector Register E of drive Register C into 5100H: the Z FLAG when it was read and carries the deleted data mark.
5097
LD A,14H 3E 14
Load Register A with 14H, error 20: GAT read error. LD does not change the flags.
5099
POP HL E1
Restore the caller's Register Pair HL from the stack.
509A
POP DE D1
Restore Register Pair DE from the stack. POP does not change the flags.
509B
RET C9
RETurn with the Z FLAG on success, or the NZ FLAG and Register A = 14H.
509C
LD HL,5100H 21 00 51
Load Register Pair HL with 5100H, the buffer for the hash index table.
509F
PUSH BC C5
Save Register Pair BC (Register C = the drive) to the stack.
50A0
PUSH DE D5
Save Register Pair DE to the stack while Register Pair DE names the sector.
50A1
GOSUB to 4B64H (DIRCYL) to load Register D with the directory cylinder of drive Register C.
50A4
LD E,01H 1E 01
Load Register E with 01H, sector 1 of the directory cylinder: the hash index table.
50A6
GOSUB to 4B45H (RDSSEC) to read cylinder Register D, sector Register E of drive Register C into 5100H: the Z FLAG when it was read and carries the deleted data mark.
50A9
POP DE D1
Restore the caller's Register Pair DE from the stack.
50AA
POP BC C1
Restore Register Pair BC from the stack. POP does not change the flags.
50AB
LD A,16H 3E 16
Load Register A with 16H, error 22: HIT read error. LD does not change the flags.
50AD
RET C9
RETurn with the Z FLAG on success, or the NZ FLAG and Register A = 16H; Register Pair HL = 5100H.

50AEH - Is the Drive There and Turning?

Register C = the drive. Its digit (30H + the drive) is stored at 5017H for the name builder. The drive's code table must start with C3H (a JP to its driver); otherwise the NZ FLAG is returned with Register A = that byte less C3H (06H for the C9H of a drive that is not present). The drive is selected by a seek to the cylinder the head is on and the controller waited for. Unless bit 4 of byte +04H of the code table is set, the index pulse (bit 1 of the controller's status) must then go off, on and off before TIMER$ (4288H, 30 ticks a second on the Model III) has counted 15 more ticks: else error 08H, device not available, with the NZ FLAG. 50ECH reads the status once and gives up at the deadline by dropping its caller's return address and going to 50E8H. Index Register IY and Register Pair HL are kept.

50AE
LD A,C 79
Copy Register C (the drive, 0-7) into Register A.
50AF
OR 30H F6 30
OR 30H into Register A: the drive's digit, 0 to 7.
50B1
LD (5017H),A 32 17 50
Store Register A (the digit) at 5017H, the operand of the LD A at 5016H in the name builder.
50B4
PUSH IY FD E5
Save Index Register IY to the stack: GETDCT changes it.
50B6
GOSUB to 478FH (GETDCT) to point Index Register IY at the drive code table of drive Register C.
50B9
LD A,(IY+00H) FD 7E 00
Fetch byte +00H of the drive code table at Index Register IY into Register A: C3H (a JP) for a drive that is there, C9H (a RET) for one that is not.
50BC
SUB C3H D6 C3
SUBtract C3H from Register A: 00H with the Z FLAG for C3H.
50BE
If the NZ FLAG is set (no driver), JUMP to 50E9H to return Register A = the byte less C3H with the NZ FLAG.
50C0
PUSH HL E5
Save the caller's Register Pair HL to the stack.
50C1
LD D,(IY+05H) FD 56 05
Fetch byte +05H of the drive code table at Index Register IY into Register D: the cylinder the head is on.
50C4
LD E,A 5F
Copy Register A (00H) into Register E: sector 0.
50C5
GOSUB to 475EH (SEEK) to seek cylinder Register D of drive Register C: the drive is selected and its motor started, the head stays where it is.
50C8
EI FB
Enable interrupts, so that TIMER$ counts during the wait.
50C9
GOSUB to 4759H (TSTBSY) to wait until the controller of drive Register C is not busy.
50CC
BIT 4,(IY+04H) FD CB 04 66
Test bit 4 of byte +04H of the drive code table at Index Register IY: when set, the index pulse is not tested.
50D0
If the NZ FLAG is set (no index test), JUMP to 50E7H: the drive is taken as ready.
50D2
LD A,(4288H) 3A 88 42
Fetch TIMER$, the tick count, from 4288H into Register A.
50D5
ADD A,0FH C6 0F
ADD 0FH (15 ticks of the 30 a second, half a second) to Register A: the deadline.
50D7
LD D,A 57
Copy Register A (the deadline) into Register D.
50D8
GOSUB to 50ECH to read the controller's status: the NZ FLAG while the index pulse is on. 50DBH loops back here.
50DB
If the NZ FLAG is set (the index pulse is on), LOOP BACK to 50D8H until it goes off.
50DD
GOSUB to 50ECH to read the controller's status: the Z FLAG while the index pulse is off. 50E0H loops back here.
50E0
If the Z FLAG is set (the index pulse is off), LOOP BACK to 50DDH until it comes on.
50E2
GOSUB to 50ECH to read the controller's status: the NZ FLAG while the index pulse is on. 50E5H loops back here.
50E5
If the NZ FLAG is set (the index pulse is on), LOOP BACK to 50E2H until it goes off: the diskette turns.
50E7
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG: the drive is ready.
50E8
POP HL E1
Restore Register Pair HL from the stack. 50FCH comes here with error 08H.
50E9
POP IY FD E1
Restore the caller's Index Register IY from the stack.
50EB
RET C9
RETurn with the Z FLAG for a ready drive, else the NZ FLAG and the error in Register A.
50EC
LD A,(4288H) 3A 88 42
Fetch TIMER$, the tick count, from 4288H into Register A.
50EF
CP D BA
Compare Register A against Register D (the deadline): the Z FLAG when it is reached.
50F0
If the Z FLAG is set (the deadline), JUMP to 50F8H to give up.
50F2
GOSUB to 4759H (TSTBSY) for the controller's status in Register A.
50F5
BIT 1,A CB 4F
Test bit 1 of Register A (the status): the index pulse, the NZ FLAG while it is on.
50F7
RET C9
RETurn with the NZ FLAG while the index pulse is on.
50F8
POP AF F1
Remove the return address into 50D8H, 50DDH or 50E2H from the stack into Register Pair AF: the wait is given up.
50F9
LD A,08H 3E 08
Load Register A with 08H, error 8: device not available.
50FB
OR A B7
Set the flags from Register A (OR A): the NZ FLAG.
50FC
JUMP to 50E8H to put Register Pair HL and Index Register IY back and return the error.