LDOS 5.3.1 for the Model III - SYS2/SYS Disassembled

Page Customization

Introduction/Summary

LDOS 5.3.1 SYS2/SYS Disassembly - Open, Create and Check a Drive (Model III)

SYS2/SYS opens files and devices for LDOS. It is a system file, loaded into the overlay area at 4E00H by the supervisor dispatcher of SYS0/SYS (4BCCH) whenever a request code names it: bits 3-0 of the code are 4. It serves @OPEN (open an existing file or a device), @INIT (open a file, creating it when it is on no diskette) and @CKDRV (check that a drive is ready), and it holds the two hash routines of the directory: the one byte hash code of a file name, kept in the Hash Index Table (HIT, sector 1 of the directory cylinder), and the 16 bit hash code of a password, kept in the directory record.

The file is a load module of 1079 bytes: a record of type 1FH with the text Copyright 1991 MISOSYS, Inc., All rights reserved, six load blocks for 4E00H-51FEH (the bytes 51B8H-51BCH and 51E1H-51F8H are not loaded), a load block for the two bytes 4BC8H-4BC9H (0400H, the 1024 bytes 4E00H-51FFH that SYS2/SYS takes up, its work cell 51FFH included) and the transfer address record for 4E00H. A library command of SYS6/SYS or SYS7/SYS runs at 5200H, above SYS2/SYS, so after it has opened a file it can use the work area at 51E1H-51FFH and call the HIT routine at 51BDH (RENAME, ATTRIB).

Functions

4E00H masks the request code in Register A with 70H and branches on bits 6-4.

RequestFunction
84H00H: returns at once (Register A = 00H, the NZ FLAG). SYS6/SYS, BACKUP/CMD and FORMAT/CMD issue it so that SYS2/SYS is in memory before they need it.
94H10H: @OPEN (4424H), 4F3DH: open the file or device whose specification is in the control block at Register Pair DE; Register Pair HL = the 256 byte buffer, Register B = the record length (00H for 256).
A4H20H: @INIT (4420H), 5028H: as @OPEN, but a file that is on no diskette is created; the CARRY FLAG comes back set for a new file.
C4H40H: @CKDRV (4209H), 4E52H: check drive Register C; the Z FLAG when it is ready, then the CARRY FLAG when it is write protected.
D4H50H: 4E17H: the hash code of the 11 byte name and extension at Register Pair HL, returned in Register A and stored at 51E1H (SYS6/SYS's RENAME).
E4H60H: 4E27H: the hash code of the 8 byte password at Register Pair DE, returned in Register Pair HL (SYS7/SYS, BACKUP/CMD, FORMAT/CMD).

Functions 30H and 70H (requests B4H and F4H) return at once like 00H; no file on the diskette issues them.

How a File Is Opened

@OPEN and @INIT start with the file prologue of SYS0/SYS (49F1H), so their last RET restores the caller's Register Pairs BC, DE and HL and Index Register IX. A control block that starts with 2AH (a device specification: an asterisk and the two letters of the device name) is opened as a device (4EDCH): its name is looked up in the table of device names of SYS0/SYS (42ECH-42FFH), which gives the device control block at 4015H-402CH or 42C2H-42EBH, and the control block is given a copy of the six bytes at 4F37H as its driver. For a file:

StepWhat SYS2/SYS does
1Bits 1 and 2 of SFLAG$ (442BH) are cleared, the old value kept at 500FH (4F40H).
2The specification is taken apart (5106H): name, extension and password into the work area, the drive into 4F70H, and a closing ! noted for bit 3 of the control block's +01H.
3The name and the extension are hashed to one byte (51E1H), the password to a word (51F5H).
4For the drive named, or for drives 0-7 in turn: @CKDRV, then the HIT is read and every byte equal to the hash code is followed to its directory record, which must have bit 7 of its attribute clear, bit 4 set and the same 11 characters of name and extension (4FACH). No match on any drive: error 18H.
5The access is decided (4FD2H) and the control block filled (5096H).

@INIT runs the same open; on error 18H it takes the drive named, or the first ready drive of 0-7 that is not write protected, finds a free byte in its HIT (5172H), and makes the directory record (5067H).

Passwords and Access

The access the open gives goes into bits 2-0 of byte +01H of the control block; the file routines of SYS0/SYS refuse a write with access 5 or more.

Password givenAccess
RS0LT0FF (hash code 113DH)00H, every access, whatever the file's password.
Hash code equal to the record's +10H-+11H00H. A file made without a password holds 4296H there, the hash code of eight spaces, so it opens with every access when no password is given.
Any otherThe protection level of the attribute (bits 2-0): level 7 is refused with error 19H (file access denied); level 6 (execute only) gives 06H, or 05H (read) with bit 1 of SFLAG$ set (run without DEBUG) when the open is the program load of @RUN; for level 6 the BREAK trap is off when the request returns (C9H at 4BE8H); levels 0-5 give the level.

A file created with a password (a . in the specification) gets the attribute 17H, protection level 7, so it opens only with its password or the master password; without one it gets 10H, level 0.

The Control Block of an Open File

ByteContents (5096H)
+00H80H, an open file (10H for an open device: the address of its own byte +08H at +01H-+02H, the name at +06H-+07H, and at +08H-+0DH LD HL with the address of the device control block and JP 4D62H, 4EDCH).
+01HBit 7 byte mode (record length below 256), bit 5 the buffer must be read first, bit 3 the directory is brought up to date after every write that moves the end of the file (the specification ended with !), bits 2-0 the access.
+02H00H.
+03H-+04HThe buffer address (the caller's Register Pair HL).
+05H00H, the position of the next byte in the buffer.
+06H, +07HThe drive and the directory record number.
+08HThe end of file byte (record +03H).
+09HThe record length the caller asked for (Register B).
+0AH-+0BH0000H, the next record number.
+0CH-+0DHThe ending record number (record +14H-+15H).
+0EH-+1FHThe extents of the directory record (cylinder, granule byte), from the second on each preceded by the number of granules in the extents before it; FFH after the last.

@CKDRV and the Drive Code Table

@CKDRV (4E52H) needs the drive's table entry to start with C3H, then seeks the drive to the cylinder its head is on and, unless bit 4 of +04H is set, waits for the index hole to pass (the whole test within 15 ticks of TIMER$, half a second at the 30 ticks a second of the Model III). It reads the allocation table and sets from its bytes CCH (cylinders less 35) and CDH (configuration): +06H, the highest cylinder; bit 5 of +04H for two sides (bit 5 of CDH); and bit n of 475DH for drive n when bit 3 of CDH says the diskette carries the directory date format of this release. The write protect sensor (bit 6 of the controller's status) and bit 7 of +03H give the CARRY FLAG. The Model I SYS2/SYS also sets bit 7 of +04H when bit 4 of CDH says cylinder 0 is single density while the others are double; this file has no such test.

About This Page

Every byte of the file is shown, in address order: the load block at 4BC8H first, then 4E00H-51FEH, with DEFS rows for the cells the file does not load (51B8H-51BCH, 51E1H-51F8H and 51FFH). The two bytes of OR AFH at 51BDH are also the write entry XOR A at 51BEH. The bit instruction at 4EAAH is RLC B in the file; it is always rewritten as SET or RES before it runs.

Memory Map

Load Blocks in File Order

The seven 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-50FFH256 bytes
5100H-51B7H184 bytes
51BDH-51E0H36 bytes
51F9H-51FEH6 bytes
4BC8H-4BC9H2 bytes

Sections of the Disassembly

AddressContents
4BC8HThe Overlay Length (Data)
4E00HEntry: Pick the Function of the Request
4E17HFunction 50H (Request D4H): The File Name Hash
4E27HFunction 60H (Request E4H): The Password Hash
4E52HFunction 40H (Request C4H, @CKDRV): Check a Drive
4ECBHRead the Index Hole, or Give Up at the Time Limit
4ED7HThe Drive Is Not Ready
4EDCHOpen a Device
4F37HThe Driver Copied into the Control Block of a Device
4F3DHFunction 10H (Request 94H, @OPEN): Open a File
4F97HGo On to the Next Drive
4FA2HNot This Record: Read the HIT Again
4FACHA Hash Match: Check the Directory Record
4FD2HThe File Is Found: Decide the Access
5028HFunction 20H (Request A4H, @INIT): Open or Create a File
5067HCreate the Directory Record
5096HFill the File Control Block
50FFHFill with FFH
5106HTake the File Specification Apart
5151HThe Password Field
5156HCopy a Field
5172HFind a Free Directory Record
51B8HNot in the File
51BDHRead or Write the Hash Index Table
51E1HThe Work Area

Memory SYS2/SYS Uses

AddressContents
4015H-402CHKIDCB$, DODCB$ and PRDCB$, the device control blocks of slots 0-2 of the table of names (4F0BH).
42C2H-42EBHJLDCB$, SIDCB$, SODCB$ and the four spare device control blocks of SYS0/SYS, slots 3-9 of the table of names (42B0H + 6 x n, 4EFBH).
42ECH-42FFHThe table of device names of SYS0/SYS, KIDOPRJLSISO and four empty slots, searched for a device open (4EE2H).
4288HTIMER$, the tick count: the time limit of the index hole test (4E72H, 4ECBH) and the start of the free record search (5197H).
4300H-43FFHThe system buffer: the allocation table (43CCH-43CDH read at 4E94H), the HIT and the directory sectors are read into it.
442BHSFLAG$: bits 1 and 2 cleared by @OPEN (4F46H), bit 2 cleared by @INIT (5037H), bit 1 set for an execute only program (5017H).
4700H-474FHDCT$, the drive code tables: +00H, +03H, +04H, +05H, +07H and +09H read, +04H and +06H written by @CKDRV.
475DHThe date stamping flags, one bit for each drive, set or cleared by @CKDRV (4EAAH).
4759H, 475EH, 4768H, 4772HRSELCT, SEEK, WRPROT and VERSEC, the disk primitives of SYS0/SYS.
478FH, 479CHGETDCT and DCTBYT: a drive code table and one of its bytes.
4D62HThe entry before @DVRHK in SYS0/SYS: Index Register IX = Register Pair HL, then the driver of that device control block (4F3AH, run from the control block of an open device).
49F1HThe file prologue of SYS0/SYS (4F3DH, 5028H).
4B10H, 4B1FH, 4B45H, 4B64H, 4B6BHDIRRD, DIRWR, RDSSEC, DIRCYL and MULTEA of SYS0/SYS.
4BC8H-4BC9HThe length of the system file in the overlay area: 0400H for SYS2/SYS.
4BE8HThe BREAK trap switch the supervisor dispatcher keeps during a request: C9H stored for an execute only file (501EH).
4E00H-51FFHSYS2/SYS in the overlay area, its work cells included.

Variables

The entry points of SYS2/SYS, then every data cell of the file and the cells it keeps outside the file, the operands it changes, and the cells of SYS0/SYS it uses, by address.

Entry Points

AddressNameContents
4E00HThe entry: 4E00H picks the function from bits 6-4 of the request code in Register A.
4E17HFunction 50H (request D4H): the hash code of a file name and extension.
4E27HFunction 60H (request E4H): the hash code of a password.
4E52H@CKDRVFunction 40H (request C4H, 4209H): check a drive.
4EDCHOpen a device.
4F37HThe driver copied into the control block of an open device: LD HL with the device control block, JP 4D62H.
4F3DH@OPENFunction 10H (request 94H, 4424H): open a file or a device.
4F40H@OPEN after its prologue, called by @INIT.
5028H@INITFunction 20H (request A4H, 4420H): open or create a file.
5096HFill the control block from a directory record.
5106HTake a file specification apart.
5172HFind a free directory record in the HIT.
51BDHRead the HIT of drive Register C into 4300H.
51BEHWrite the HIT at 4300H to drive Register C.

Data in the File and the Work Area

Address
Size
Contents
4BC8H-4BC9H
2 bytes
DEFW 0400H
4BC8H-4BC9H: 0400H (1024), the number of bytes from 4E00H that SYS2/SYS takes up (4E00H-51FFH), its work cell 51FFH included. Loading SYS2/SYS puts it over the length the system file loaded before it left here.
51B8H-51BCH
5 bytes
DEFS 5
51B8H-51BCH: Not in the file: 5 bytes between the free record search and the HIT routine. No code of SYS2/SYS reads or writes them.
51E1H
1 byte
DEFS 1
51E1H: Not in the file: the hash code of the file name, 01H-FFH, stored by 4E23H, compared with the HIT by 4F84H and put into the HIT for a new file by 5068H. SYS6/SYS reads and writes it (its 5272H, 5285H).
51E2H-51E9H
8 bytes
DEFS 8
51E2H-51E9H: Not in the file: the password of the specification, filled with spaces by 5106H and copied by 5132H; 4F66H hashes it. SYS7/SYS reads it (its 5284H, 52A6H, 5306H, 531BH, 535FH).
51EAH-51F1H
8 bytes
DEFS 8
51EAH-51F1H: Not in the file: the file name of the specification, spaces after its last character (5106H, 5118H); compared with a directory record's +05H-+0CH at 4FC5H and copied into a new record at 5080H. SYS6/SYS uses it (its 5298H, 52B0H).
51F2H-51F4H
3 bytes
DEFS 3
51F2H-51F4H: Not in the file: the extension of the specification, spaces when none (512BH); it follows the name, so 51EAH-51F4H are the 11 bytes a directory record holds at +05H-+0FH.
51F5H-51F6H
2 bytes
DEFS 2
51F5H-51F6H: Not in the file: the hash code of the password at 51E2H (4F6CH), compared with 113DH and a record's +10H-+11H (4FE7H) and copied to +10H-+11H of a new record. SYS7/SYS stores and reads it (its 5321H, 539BH, 539FH).
51F7H-51F8H
2 bytes
DEFS 2
51F7H-51F8H: Not in the file: SYS2/SYS does not set these two bytes; @INIT copies them to +12H-+13H of a new directory record (5083H). SYS7/SYS stores a hash code here (its 530CH) and reads it (its 53ABH, 53AFH).
51F9H-51FAH
2 bytes
DEFB 00H x 2
51F9H-51FAH: 0000H, copied by @INIT to +14H-+15H of a new directory record: the ending record number of an empty file.
51FBH
1 byte
DEFB 00H
51FBH: the attribute: of the file found (4FDDH), or of a new file, 10H or 17H (5043H); @INIT copies it to +00H of a new directory record. 00H in the file.
51FCH
1 byte
DEFB 00H
51FCH: 00H, copied to +01H of a new directory record: no flags and no month of a date yet.
51FDH
1 byte
DEFB 00H
51FDH: 00H, copied to +02H of a new directory record: no day and year yet.
51FEH
1 byte
DEFB 00H
51FEH: 00H, copied to +03H of a new directory record: the end of file byte of an empty file. The last byte the file loads.
51FFH
1 byte
DEFS 1
51FFH: Not in the file: the record length the caller asked for in Register B (4F53H), 00H for 256; it goes to +09H of the control block (50C6H), decides byte mode (509DH) and is copied to +04H of a new directory record.

Self-Modified Operands

Address
Size
Contents
4EABH
1 byte
The second byte of the bit instruction at 4EAAH (00H, RLC B, in the file): 4EA4H stores C6H + the drive x 8 (SET) or 86H + the drive x 8 (RES), so bit n of 475DH follows bit 3 of the diskette's configuration byte.
4F38H
2 bytes
The operand of LD HL,0000H at 4F37H: the device control block of the name found (4F0FH), copied with the six bytes 4F37H-4F3CH into the control block of an open device (4F2EH).
4F70H
1 byte
The operand of LD A,00H at 4F6FH: the drive of the specification, FFH when none was named (5113H, 513CH), then the drive where the file was found (4FD4H). 4F97H and 5046H read it.
500FH
1 byte
The operand of LD B,00H at 500EH: SFLAG$ as it was before the open cleared bits 1 and 2 (4F43H); bit 2 is tested at 5010H.
5042H
1 byte
The operand of LD A,10H at 5041H: the attribute of a new file, 10H (5030H) or 17H when the specification holds a password (5153H).
50A2H
1 byte
The operand of LD A,00H at 50A1H: the access the open gives, 00H-06H (5020H, 502BH), for bits 2-0 of +01H of the control block.
50AAH
1 byte
The operand of OR 00H at 50A9H: 08H when the specification ended with ! (514DH), else 00H, for bit 3 of +01H of the control block.
50B2H
2 bytes
The operand of LD DE,0000H at 50B1H: the caller's buffer address (4F56H), for +03H-+04H of the control block.
51AAH
1 byte
The operand of CP 00H at 51A9H: the number of directory record sectors of the drive (5194H).

Cells of SYS0/SYS That SYS2/SYS Uses

Address
Size
Contents
4288H
1 byte
TIMER$, the tick count of SYS0/SYS, 30 a second: read for the time limit of the index hole test (4E72H, 4ECBH) and for the HIT byte where the free record search starts (5197H).
43CCH-43CDH
2 bytes
Bytes CCH and CDH of the allocation table read into the system buffer: the cylinders less 35 and the configuration byte (4E94H).
442BH
1 byte
SFLAG$, the system flags of SYS0/SYS: @OPEN keeps the old value at 500FH and clears bits 1 (run without DEBUG) and 2 (a program load by @RUN is in progress); @INIT clears bit 2 first (5037H); 5017H sets bit 1 for an execute only program loaded by @RUN.
475DH
1 byte
The date stamping flags, the fifth byte of RSELCT among the disk primitives of SYS0/SYS: bit n for drive n, set by @CKDRV when the diskette carries the directory date format of this release (4EAAH). SYS3/SYS reads it.
4BE8H
1 byte
The operand of LD A at 4BE7H in the supervisor dispatcher of SYS0/SYS, the BREAK trap switch saved at the start of the request: 501EH stores C9H, so 4405H gets C9H (off) when the open of an execute only file returns.

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 SYS2/SYS 0400H, the 1024 bytes 4E00H-51FFH, one more than the last loaded byte (51FEH), because 51FFH is a cell of SYS2/SYS that the file does not load. In the file this block is the last load block, after the six blocks of 4E00H-51FEH and before the transfer address record (4E00H). SYS7/SYS reads and writes the word (its 55E4H, 5692H). The two bytes are data, not instructions.

4BC8-4BC9
DEFW 0400H 00 04
4BC8H-4BC9H: 0400H (1024), the number of bytes from 4E00H that SYS2/SYS takes up (4E00H-51FFH), its work cell 51FFH included. Loading SYS2/SYS puts it over the length the system file loaded before it left here.

4E00H - Entry: Pick the Function of the Request

The supervisor dispatcher (4BCCH) loads SYS2/SYS into 4E00H for a request code whose bits 3-0 are 4 and calls 4E00H with Register A = the request code (from OVRLY$, 4414H, which always gets the code of the latest request) and the caller's Register Pairs BC, DE and HL. Bits 6-4 of the code pick the function: 10H (94H, @OPEN 4424H) open an existing file or a device; 20H (A4H, @INIT 4420H) open a file or create it; 40H (C4H, @CKDRV 4209H) check a drive; 50H (D4H) the file name hash; 60H (E4H) the password hash. Any other function (00H for request 84H, which SYS6/SYS, BACKUP/CMD and FORMAT/CMD issue to have SYS2/SYS in memory before they need it) returns at once.

4E00
AND 70H E6 70
Mask Register A (the request code, 84H to E4H, whose low four bits 4 name SYS2/SYS) with 70H, keeping bits 6-4, the function.
4E02
CP 10H FE 10
Compare Register A (the function) against 10H, @OPEN (request 94H).
4E04
If the Z FLAG is set (function 10H), JUMP to 4F3DH to open the existing file or the device whose control block is at Register Pair DE.
4E07
CP 20H FE 20
Compare Register A (the function) against 20H, @INIT (request A4H).
4E09
If the Z FLAG is set (function 20H), JUMP to 5028H to open the file whose control block is at Register Pair DE, creating it when it is not on any diskette.
4E0C
CP 40H FE 40
Compare Register A (the function) against 40H, @CKDRV (request C4H).
4E0E
If the Z FLAG is set (function 40H), JUMP to 4E52H to check that drive Register C is present and has a diskette that turns.
4E10
CP 60H FE 60
Compare Register A (the function) against 60H, the password hash (request E4H).
4E12
If the Z FLAG is set (function 60H), JUMP to 4E27H to work out the hash code of the eight byte password at Register Pair DE.
4E14
CP 50H FE 50
Compare Register A (the function) against 50H, the file name hash (request D4H).
4E16
RET NZ C0
If the NZ FLAG is set (function 00H, 30H or 70H: no service), RETurn to the dispatcher with Register A = the function (00H for request 84H). Function 50H goes on into 4E17H.

4E17H - Function 50H (Request D4H): The File Name Hash

Register Pair HL = 11 bytes, the file name (8 bytes) and the extension (3 bytes), each padded with spaces, as a directory record holds them at +05H-+0FH. The hash code is the Exclusive OR of the 11 bytes, rotated left one bit after each byte; 00H is turned into 01H, because 00H in the Hash Index Table (the HIT, sector 1 of the directory cylinder) marks a free directory record. The code is stored at 51E1H and returned in Register A with the NZ FLAG. @OPEN and @INIT call it at 4F63H; SYS6/SYS's RENAME issues request D4H (its 52D0H).

4E17
LD B,0BH 06 0B
Load Register B with 0BH (11), the number of bytes of a file name and its extension.
4E19
XOR A AF
Set Register A to 00H (XOR A): the hash code starts at 00H.
4E1A
XOR (HL) AE
Exclusive OR the byte at Register Pair HL (the next character of the name or the extension) into Register A, the hash code so far.
4E1B
INC HL 23
INCrement Register Pair HL to the next character.
4E1C
RLCA 07
Rotate Register A (the hash code) left one bit, bit 7 going into bit 0, so that the place of each character in the name counts.
4E1D
DECrement Register B (the characters left) and LOOP BACK to 4E1AH until all 11 bytes are in the hash code.
4E1F
OR A B7
Set the flags from Register A (OR A), the hash code: the Z FLAG is set when it came out 00H.
4E20
If the NZ FLAG is set (the code is not 00H), JUMP to 4E23H to keep it.
4E22
INC A 3C
INCrement Register A from 00H to 01H: a hash code of 00H would mark a free record in the HIT, so 01H is used instead. The INC leaves the NZ FLAG.
4E23
LD (51E1H),A 32 E1 51
Store Register A (the hash code, 01H-FFH) at 51E1H, where @OPEN and @INIT compare it with the HIT and @INIT puts it into the HIT for a new file.
4E26
RET C9
RETurn with the hash code in Register A, the NZ FLAG, Register Pair HL past the 11 bytes and Register B = 00H.

4E27H - Function 60H (Request E4H): The Password Hash

Register Pair DE = an 8 byte password field, padded with spaces. The bytes are taken from the last to the first and mixed into a 16 bit value that starts at FFFFH; the result is returned in Register Pair HL with the Z FLAG. Eight spaces (no password) give 4296H, the password PASSWORD gives 42E0H, and the master password RS0LT0FF (the manual: it allows access to any file) gives 113DH, the value @OPEN compares with at 4FECH. @OPEN and @INIT call it at 4F69H; SYS7/SYS (its 532CH and 54E3H), BACKUP/CMD (its 6C6BH) and FORMAT/CMD (its 7178H) issue request E4H.

4E27
LD HL,0007H 21 07 00
Load Register Pair HL with 0007H, the offset of the last of the 8 bytes of the password field.
4E2A
ADD HL,DE 19
ADD Register Pair DE (the start of the password field) to Register Pair HL: Register Pair HL = the address of the field's last byte.
4E2B
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the address of the last byte, where the hash starts; Register Pair HL = the start of the field, no longer needed.
4E2C
LD HL,FFFFH 21 FF FF
Load Register Pair HL with FFFFH, the starting value of the hash.
4E2F
LD B,08H 06 08
Load Register B with 08H, the number of password bytes to mix in.
4E31
LD A,(DE) 1A
Fetch the password byte at Register Pair DE into Register A.
4E32
PUSH DE D5
Save Register Pair DE (the address of the byte) to the stack: Register Pair DE is used for the arithmetic below.
4E33
LD D,A 57
Copy Register A (the password byte) into Register D.
4E34
LD E,H 5C
Copy Register H (the high byte of the hash so far) into Register E.
4E35
LD A,L 7D
Copy Register L (the low byte of the hash so far) into Register A.
4E36
AND 07H E6 07
Mask Register A with 07H to bits 2-0, the three low bits of the low byte.
4E38
RRCA 0F
Rotate Register A right one bit (bit 0 into bit 7): the first of three rotations.
4E39
RRCA 0F
Rotate Register A right one bit again: the second of the three rotations.
4E3A
RRCA 0F
Rotate Register A right one bit a third time: the three low bits of Register L are now bits 7-5 of Register A, the other bits 0.
4E3B
XOR L AD
Exclusive OR Register L (the low byte of the hash so far) into Register A: Register A = the low byte with bits 2-0 copied onto bits 7-5.
4E3C
LD L,A 6F
Copy Register A (the mixed low byte) into Register L.
4E3D
LD H,00H 26 00
Load Register H with 00H: Register Pair HL = the mixed low byte alone.
4E3F
ADD HL,HL 29
ADD Register Pair HL to itself: Register Pair HL = the mixed byte x 2.
4E40
ADD HL,HL 29
ADD Register Pair HL to itself: Register Pair HL = the mixed byte x 4.
4E41
ADD HL,HL 29
ADD Register Pair HL to itself: Register Pair HL = the mixed byte x 8.
4E42
ADD HL,HL 29
ADD Register Pair HL to itself: Register Pair HL = the mixed byte x 16; Register H = its four high bits, Register L = its four low bits moved up.
4E43
XOR H AC
Exclusive OR Register H (the mixed byte shifted right four bits) into Register A (the mixed byte).
4E44
XOR D AA
Exclusive OR Register D (the password byte) into Register A: Register A = the new high byte of the hash.
4E45
LD D,A 57
Copy Register A (the new high byte) into Register D.
4E46
LD A,L 7D
Copy Register L (the mixed byte x 16, low eight bits) into Register A.
4E47
ADD HL,HL 29
ADD Register Pair HL to itself: Register Pair HL = the mixed byte x 32; Register H = the mixed byte shifted right three bits.
4E48
XOR H AC
Exclusive OR Register H (the mixed byte shifted right three bits) into Register A.
4E49
XOR E AB
Exclusive OR Register E (the old high byte of the hash) into Register A: Register A = the new low byte of the hash.
4E4A
LD E,A 5F
Copy Register A (the new low byte) into Register E: Register Pair DE = the new hash value.
4E4B
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the new hash value.
4E4C
POP DE D1
Restore Register Pair DE from the stack: the address of the password byte just mixed in.
4E4D
DEC DE 1B
DECrement Register Pair DE to the byte before it in the field.
4E4E
DECrement Register B (the bytes left) and LOOP BACK to 4E31H until all 8 bytes are mixed in.
4E50
CP A BF
Compare Register A against itself (CP A): the Z FLAG is set and the CARRY FLAG cleared; Register A keeps the low byte of the hash.
4E51
RET C9
RETurn with the hash code in Register Pair HL, the Z FLAG, Register Pair DE one byte before the field and Register B = 00H.

4E52H - Function 40H (Request C4H, @CKDRV): Check a Drive

Register C = the drive (0-7). The drive must be present in its drive code table, its diskette must turn (the index hole must pass the sensor within half a second, 15 ticks of the 30 a second of the Model III, unless bit 4 of byte +04H of the table is set) and the allocation table (sector 0 of the directory cylinder) must read. Then the drive code table is brought into line with the diskette from the allocation table's bytes CCH and CDH: the highest cylinder (+06H), and bit 5 of +04H (two sides; unlike the Model I SYS2/SYS it does not set bit 7 of +04H for a diskette whose cylinder 0 is single density); and bit Register C of 475DH is set when the diskette carries the directory date format of this release (bit 3 of byte CDH), cleared when not. Returns the Z FLAG and Register A = 00H when the drive is ready, the CARRY FLAG then set when the diskette is write protected (the controller's status bit 6 or bit 7 of +03H); otherwise the NZ FLAG. Register Pairs BC, DE and HL and Index Register IY are kept. @OPEN and @INIT call it at 4F76H and 504EH; BACKUP/CMD, CMDFILE/CMD, DATECONV/CMD, HELP/CMD, LOG/CMD, SYS6/SYS and SYS7/SYS call @CKDRV (4209H).

4E52
PUSH IY FD E5
Save Index Register IY (the caller's) to the stack; it is restored at 4EC8H.
4E54
GOSUB to 478FH (GETDCT) to point Index Register IY at the drive code table of drive Register C (4700H + the drive x 10).
4E57
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 present, C9H (a RET) for one that is not.
4E5A
SUB C3H D6 C3
SUBtract C3H from Register A: Register A = 00H with the Z FLAG set for a drive that is present, 06H with the NZ FLAG for C9H.
4E5C
If the NZ FLAG is set (the drive is not present), JUMP to 4EC8H to restore Index Register IY and return with the NZ FLAG.
4E5F
PUSH HL E5
Save Register Pair HL (the caller's) to the stack.
4E60
PUSH DE D5
Save Register Pair DE (the caller's) to the stack.
4E61
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 drive's head is on, so the Seek below does not move it.
4E64
LD E,A 5F
Copy Register A (00H) into Register E: sector 0, on side 0.
4E65
GOSUB to 475EH (SEEK) to seek drive Register C to cylinder Register D for sector Register E: the drive is selected, its motor started, and a Seek to the cylinder the head is on is given to the controller, so the controller reports the drive's state.
4E68
EI FB
Enable the interrupts, so that the clock counts TIMER$ (4288H) while the index hole is watched.
4E69
GOSUB to 4759H (RSELCT) to wait until the controller is not busy: Register A comes back holding the controller's status after the Seek (bit 1 the index hole, bit 6 write protected, bit 7 not ready).
4E6C
BIT 4,(IY+04H) FD CB 04 66
Test bit 4 of byte +04H of the drive code table at Index Register IY. When it is set the index hole is not watched.
4E70
If the NZ FLAG is set (bit 4 is set), JUMP to 4E87H with the status in Register A, without waiting for the index hole.
4E72
LD A,(4288H) 3A 88 42
Fetch TIMER$ from 4288H into Register A: the tick count, 30 ticks a second (the clock interrupt of the Model III).
4E75
ADD A,0FH C6 0F
ADD 0FH (15) to Register A: the tick count half a second from now at 30 ticks a second, the time limit of the whole test (the Model I SYS2/SYS adds 14H, 20 of its 40 ticks).
4E77
LD D,A 57
Copy Register A (the time limit) into Register D, where 4ECBH compares it with TIMER$.
4E78
GOSUB to 4ECBH to read the controller's status: the NZ FLAG is set while the index hole is at the sensor. At the time limit 4ECBH does not return here (4ED7H).
4E7B
If the NZ FLAG is set (the index hole is at the sensor), LOOP BACK to 4E78H to wait until it has passed.
4E7D
GOSUB to 4ECBH to read the controller's status again.
4E80
If the Z FLAG is set (no index hole at the sensor), LOOP BACK to 4E7DH to wait for the next index hole.
4E82
GOSUB to 4ECBH to read the controller's status again.
4E85
If the NZ FLAG is set (the index hole is at the sensor), LOOP BACK to 4E82H until it has passed: the diskette has turned past its index hole, so a diskette is in the drive and turning.
4E87
PUSH AF F5
Save Register Pair AF (Register A = the controller's status, whose bit 6 is the write protect sensor) to the stack; 4EBEH takes it back.
4E88
GOSUB to 4B64H (DIRCYL): Register D = byte +09H of drive Register C's table, the directory cylinder.
4E8B
LD HL,4300H 21 00 43
Point Register Pair HL to 4300H, the system buffer, where the sector is read.
4E8E
LD E,L 5D
Copy Register L (00H) into Register E: sector 0 of the directory cylinder, the allocation table.
4E8F
GOSUB to 4B45H (RDSSEC) to read cylinder Register D, sector Register E of drive Register C into 4300H. It accepts only a sector with the deleted data mark of a directory sector, and when the directory is not on cylinder Register D it takes the directory cylinder from the diskette's boot sector into +09H and reads it from there. The Z FLAG is set on success.
4E92
If the NZ FLAG is set (the allocation table could not be read), JUMP to 4ED7H to return with the NZ FLAG.
4E94
LD DE,(43CCH) ED 5B CC 43
Fetch the allocation table's bytes CCH-CDH from 43CCH-43CDH into Register Pair DE: Register E = the number of cylinders less 35 (byte CCH), Register D = the configuration byte (byte CDH).
4E98
LD A,C 79
Copy Register C (the drive, 0-7) into Register A.
4E99
RLCA 07
Rotate Register A left one bit: the drive x 2.
4E9A
RLCA 07
Rotate Register A left one bit: the drive x 4.
4E9B
RLCA 07
Rotate Register A left one bit: the drive x 8, the bit number in bits 5-3 of a bit instruction.
4E9C
OR C6H F6 C6
OR C6H into Register A: C6H + the drive x 8 is the second byte of SET n,(HL), with n = the drive.
4E9E
BIT 3,D CB 5A
Test bit 3 of Register D (the configuration byte): set when the diskette carries the directory date format of this release.
4EA0
If the NZ FLAG is set (bit 3 is set), JUMP to 4EA4H to keep SET.
4EA2
XOR 40H EE 40
Exclusive OR 40H into Register A: 86H + the drive x 8, the second byte of RES n,(HL).
4EA4
LD (4EABH),A 32 AB 4E
Store Register A (the SET or RES byte) at 4EABH, the second byte of the bit instruction at 4EAAH.
4EA7
LD HL,475DH 21 5D 47
Point Register Pair HL to 475DH, the date stamping flags: one bit for each drive (the fifth byte of RSELCT among the disk primitives of SYS0/SYS). SYS3/SYS reads them (its 4E99H) when it closes a file.
4EAA
RLC B CB 00
Bit instruction CB xx: the file holds CB 00H, RLC B, but 4EA4H has always stored the second byte before this runs, so it is SET or RES of bit Register C (the drive) of the byte at Register Pair HL (475DH): set when the diskette has the date format of this release, cleared when not. Register B is not changed.
4EAC
LD A,22H 3E 22
Load Register A with 22H (34), the highest cylinder of a diskette of 35 cylinders.
4EAE
ADD A,E 83
ADD Register E (the number of cylinders less 35, byte CCH of the allocation table) to Register A: Register A = the diskette's highest cylinder.
4EAF
LD (IY+06H),A FD 77 06
Store Register A (the highest cylinder) into byte +06H of the drive code table at Index Register IY.
4EB2
RES 5,(IY+04H) FD CB 04 AE
Clear bit 5 of byte +04H of the drive code table at Index Register IY: one side, until 4EBAH finds the diskette written on two sides.
4EB6
BIT 5,D CB 6A
Test bit 5 of Register D (the configuration byte): set for a diskette written on two sides.
4EB8
If the Z FLAG is set (one side), JUMP to 4EBEH.
4EBA
SET 5,(IY+04H) FD CB 04 EE
Set bit 5 of byte +04H of the drive code table at Index Register IY: the drive uses both sides of this diskette.
4EBE
POP AF F1
Restore Register Pair AF from the stack: Register A = the controller's status saved at 4E87H.
4EBF
RLCA 07
Rotate Register A (the status) left one bit: bit 6, the write protect sensor, moves to bit 7.
4EC0
OR (IY+03H) FD B6 03
OR byte +03H of the drive code table at Index Register IY into Register A: its bit 7 is the drive's software write protect.
4EC3
AND 80H E6 80
Mask Register A to bit 7: 80H when the diskette or the drive is write protected, 00H when not.
4EC5
ADD A,A 87
Double Register A (ADD A,A): Register A = 00H with the Z FLAG set, and bit 7 goes into the CARRY FLAG: the CARRY FLAG is set for a write protected diskette.
4EC6
POP DE D1
Restore the caller's Register Pair DE from the stack.
4EC7
POP HL E1
Restore the caller's Register Pair HL from the stack.
4EC8
POP IY FD E1
Restore the caller's Index Register IY from the stack.
4ECA
RET C9
RETurn: the Z FLAG and Register A = 00H (the CARRY FLAG set when write protected) when the drive is ready, the NZ FLAG when it is not.

4ECBH - Read the Index Hole, or Give Up at the Time Limit

Called from the three loops at 4E78H-4E85H with Register D = the time limit. When TIMER$ (4288H) has reached the limit it does not return to its caller but jumps to 4ED7H; otherwise it returns the NZ FLAG when the controller's status shows the index hole at the sensor (bit 1), the Z FLAG when not.

4ECB
LD A,(4288H) 3A 88 42
Fetch TIMER$ from 4288H into Register A: the tick count now.
4ECE
CP D BA
Compare Register A (the tick count) against Register D (the time limit set at 4E77H).
4ECF
If the Z FLAG is set (half a second has gone by), JUMP to 4ED7H to give up: no diskette turns in the drive.
4ED1
GOSUB to 4759H (RSELCT) to wait until the controller is not busy: Register A = its status.
4ED4
BIT 1,A CB 4F
Test bit 1 of Register A (the status): the index hole, set while the hole is at the sensor.
4ED6
RET C9
RETurn with the NZ FLAG when the index hole is at the sensor, the Z FLAG when not.

4ED7H - The Drive Is Not Ready

Reached from 4ECFH (the time limit, with the return address of the CALL 4ECBH on the stack) and from 4E92H (the allocation table could not be read, with the status saved at 4E87H on the stack). Either word is taken off, Register A gets bit 0 set and the routine returns with the NZ FLAG.

4ED7
POP AF F1
Restore Register Pair AF from the stack: from 4ECFH the return address of the CALL 4ECBH (Register A = its high byte, 4EH), from 4E92H the status saved at 4E87H. The stack is now as 4EC6H needs it.
4ED8
OR 01H F6 01
OR 01H into Register A: Register A is not 00H (4FH from 4ECFH, the status with bit 0 set from 4E92H), so the NZ FLAG is set.
4EDA
JUMP to 4EC6H to restore Register Pairs DE and HL and Index Register IY and return with the NZ FLAG.

4EDCH - Open a Device

@OPEN and @INIT come here when the control block at Index Register IX starts with 2AH, a device specification that @FSPEC (SYS1/SYS) copied there: 2AH (an asterisk) and the two letters of the device name in +01H-+02H. The name is looked up in the table of device names of SYS0/SYS at 42ECH-42FFH: ten slots of two letters, KI, DO, PR, JL, SI, SO and four slots of 00H 00H for the devices that SET and ROUTE make. Slot n gives the device control block: slots 0-2 the blocks of 8 bytes at 4015H + 8 x n (KIDCB$ 4015H, DODCB$ 401DH, PRDCB$ 4025H), slots 3-9 the blocks of 6 bytes of SYS0/SYS at 42B0H + 6 x n (JLDCB$ 42C2H, SIDCB$ 42C8H, SODCB$ 42CEH and the spare blocks 42D4H, 42DAH, 42E0H, 42E6H). The control block is then made a device control block of its own: +00H 10H, +01H-+02H the address of its own byte +08H, +03H-+05H 00H, +06H-+07H the name, and +08H-+0DH a copy of the six bytes at 4F37H, LD HL with the address of the device's block and JP 4D62H. A request on the open device goes through @DVRHK (4D65H) to that copy, which enters the driver of the device's block. The Model I SYS2/SYS instead searches the names at +06H-+07H of the blocks themselves and stores the block's address at +01H-+02H. A name that is not found returns error 08H (device not available).

4EDC
LD E,(IX+01H) DD 5E 01
Fetch byte +01H of the control block at Index Register IX into Register E: the first letter of the device name.
4EDF
LD D,(IX+02H) DD 56 02
Fetch byte +02H of the control block at Index Register IX into Register D: the second letter of the device name.
4EE2
LD HL,42ECH 21 EC 42
Point Register Pair HL to 42ECH, the table of device names of SYS0/SYS (42ECH-42FFH): two letters a slot, KI DO PR JL SI SO, then four slots of 00H 00H.
4EE5
LD A,(HL) 7E
Fetch the first letter of the slot at Register Pair HL into Register A.
4EE6
INC L 2C
INCrement Register L to the second letter of the slot. The table lies in one 256 byte page (42ECH-42FFH), so Register H stays 42H.
4EE7
CP E BB
Compare Register A (the block's first letter) against Register E (the first letter wanted).
4EE8
If the NZ FLAG is set (the first letters differ), JUMP to 4F00H to go on to the next slot.
4EEA
LD A,(HL) 7E
Fetch the second letter of the slot from Register Pair HL into Register A.
4EEB
CP D BA
Compare Register A (the block's second letter) against Register D (the second letter wanted).
4EEC
If the NZ FLAG is set (the second letters differ), JUMP to 4F00H to go on to the next slot.
4EEE
DEC L 2D
DECrement Register L: Register Pair HL = the first letter of the slot whose name matches, 42ECH + 2 x n for slot n.
4EEF
LD BC,BD14H 01 14 BD
Load Register Pair BC with BD14H, which is 10000H less 42ECH, the start of the table of names.
4EF2
ADD HL,BC 09
ADD Register Pair BC (BD14H) to Register Pair HL: Register Pair HL = 2 x n, twice the slot number (0000H-0012H). The CARRY FLAG this sets is not used.
4EF3
LD A,L 7D
Copy Register L (2 x n, twice the slot number) into Register A for the test.
4EF4
CP 06H FE 06
Compare Register A (2 x n) against 06H: the CARRY FLAG is set for slots 0-2 (KI, DO, PR), whose control blocks are at 4015H-402CH.
4EF6
If the CARRY FLAG is set (slot 0, 1 or 2), JUMP to 4F07H to work out the address of a block of 8 bytes at 4015H.
4EF8
ADD A,A 87
ADD Register A to itself: Register A = 4 x n.
4EF9
ADD A,L 85
ADD Register L (2 x n) to Register A: Register A = 6 x n, the offset of a block of 6 bytes.
4EFA
LD L,A 6F
Copy Register A (6 x n) into Register L: Register Pair HL = 6 x n, since Register H is 00H.
4EFB
LD BC,42B0H 01 B0 42
Load Register Pair BC with 42B0H: 42B0H + 6 x n gives the blocks of SYS0/SYS, 42C2H (JLDCB$) for slot 3 up to 42E6H for slot 9.
4EFE
JUMP to 4F0EH to add Register Pair BC (42B0H) to Register Pair HL (6 x n).
4F00
INC L 2C
INCrement Register L to the first letter of the next slot. Register L comes to 00H after the last slot (42FEH-42FFH).
4F01
If the NZ FLAG is set (Register L did not come to 00H), LOOP BACK to 4EE5H to test the next slot.
4F03
LD A,08H 3E 08
Load Register A with 08H, error 8: device not available.
4F05
OR A B7
Set the flags from Register A (OR A): the NZ FLAG, the CARRY FLAG cleared.
4F06
RET C9
RETurn with the NZ FLAG and Register A = 08H. The RET goes to 4A14H, which restores the caller's registers (the prologue at 4F3DH).
4F07
SLA L CB 25
Shift Register L left one bit (SLA L): Register L = 4 x n.
4F09
SLA L CB 25
Shift Register L left one bit (SLA L): Register Pair HL = 8 x n, the offset of a block of 8 bytes, since Register H is 00H.
4F0B
LD BC,4015H 01 15 40
Load Register Pair BC with 4015H, KIDCB$: 4015H + 8 x n gives KIDCB$ (4015H), DODCB$ (401DH) or PRDCB$ (4025H).
4F0E
ADD HL,BC 09
ADD Register Pair BC (4015H or 42B0H) to Register Pair HL: Register Pair HL = the device control block of the name.
4F0F
LD (4F38H),HL 22 38 4F
Store Register Pair HL (the device control block) at 4F38H-4F39H, the operand of the LD HL at 4F37H, which 4F27H-4F2EH copy into the control block being opened.
4F12
PUSH IX DD E5
Save Index Register IX (the control block being opened) to the stack.
4F14
POP HL E1
Restore Register Pair HL from the stack: Register Pair HL = the control block (the value of Index Register IX).
4F15
LD (HL),10H 36 10
Store 10H at Register Pair HL, byte +00H of the control block: bit 4 set, an open device.
4F17
INC HL 23
INCrement Register Pair HL to byte +01H of the control block being opened.
4F18
PUSH HL E5
Save Register Pair HL (the address of byte +01H of the control block) to the stack; 4F31H takes it back to store the driver address there.
4F19
INC HL 23
INCrement Register Pair HL to byte +02H of the control block being opened.
4F1A
INC HL 23
INCrement Register Pair HL to byte +03H of the control block being opened.
4F1B
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG set: no error.
4F1C
LD (HL),A 77
Store Register A (00H) at Register Pair HL, byte +03H of the control block.
4F1D
INC HL 23
INCrement Register Pair HL to byte +04H of the control block being opened.
4F1E
LD (HL),A 77
Store Register A (00H) at Register Pair HL, byte +04H.
4F1F
INC HL 23
INCrement Register Pair HL to byte +05H of the control block being opened.
4F20
LD (HL),A 77
Store Register A (00H) at Register Pair HL, byte +05H.
4F21
INC HL 23
INCrement Register Pair HL to byte +06H of the control block being opened.
4F22
LD (HL),E 73
Store Register E (the first letter of the device name) at Register Pair HL, byte +06H.
4F23
INC HL 23
INCrement Register Pair HL to byte +07H of the control block being opened.
4F24
LD (HL),D 72
Store Register D (the second letter of the device name) at Register Pair HL, byte +07H.
4F25
INC HL 23
INCrement Register Pair HL to byte +08H of the control block being opened, where the six bytes at 4F37H go.
4F26
PUSH HL E5
Save Register Pair HL (the address of byte +08H) to the stack; 4F30H takes it back.
4F27
LD DE,4F37H 11 37 4F
Load Register Pair DE with 4F37H, the six bytes LD HL,nnnnH and JP 4D62H.
4F2A
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = 4F37H, the source, and Register Pair DE = byte +08H of the control block, the destination.
4F2B
LD BC,0006H 01 06 00
Load Register Pair BC with 0006H, the count of the six bytes 4F37H-4F3CH.
4F2E
LDIR ED B0
Copy 6 bytes from Register Pair HL (4F37H-4F3CH) to Register Pair DE (+08H-+0DH of the control block): LD HL with the address of the device control block, and JP 4D62H. Register Pair BC ends at 0000H.
4F30
POP DE D1
Restore Register Pair DE from the stack: the address of byte +08H of the control block, pushed at 4F26H.
4F31
POP HL E1
Restore Register Pair HL from the stack: the address of byte +01H of the control block, pushed at 4F18H.
4F32
LD (HL),E 73
Store Register E (the low byte of the address of byte +08H) at Register Pair HL, byte +01H of the control block.
4F33
INC HL 23
INCrement Register Pair HL to byte +02H of the control block.
4F34
LD (HL),D 72
Store Register D (the high byte of the address of byte +08H) at Register Pair HL, byte +02H: bytes +01H-+02H, the driver address that @DVRHK (4D65H) takes, now point at the copy at +08H.
4F35
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG set: no error.
4F36
RET C9
RETurn with the Z FLAG and Register A = 00H: the device is open. The RET goes to 4A14H, which restores the caller's registers.

4F37H - The Driver Copied into the Control Block of a Device

The six bytes 4F37H-4F3CH are not run here: 4F2EH copies them into bytes +08H-+0DH of the control block of an opened device, after 4F0FH has stored the address of the device control block at 4F38H. A request on the open device goes through @DVRHK (4D65H) to the address at +01H-+02H of the control block, which is this copy: Register Pair HL is loaded with the device control block, and 4D62H in SYS0/SYS makes it Index Register IX and enters the driver of that block.

4F37
LD HL,0000H 21 00 00
Load Register Pair HL with the device control block: the operand 4F38H-4F39H is 0000H in the file and gets the address of the block from 4F0FH. Run only as the copy at +08H of the control block of an open device.
4F3A
JUMP to 4D62H in SYS0/SYS, which pushes Register Pair HL, pops it into Index Register IX and runs on into @DVRHK (4D65H): the driver of the device control block is entered with the registers and flags of the request.

4F3DH - Function 10H (Request 94H, @OPEN): Open a File

Register Pair DE = a control block holding a file specification NAME/EXT.PASSWORD:D (as @FSPEC leaves it, ended by 03H or 0DH) or a device specification (2AH and the two letters of a device name), Register Pair HL = the address of the file's 256 byte buffer, Register B = the record length (00H for 256). The prologue 49F1H points Index Register IX at the block and makes the final RET restore the caller's registers. The specification is taken apart (5106H) and hashed; then the Hash Index Table of the drive named, or of drives 0-7 in turn when no drive is named, is searched for the name's hash code, each match checked against its directory record, and when the file is found its access is decided from the password and the control block is filled (5096H). Returns the Z FLAG and Register A = 00H, or the NZ FLAG and Register A = the error number: 13H illegal file name, 16H HIT read error, 11H directory read error, 18H file not in directory, 19H file access denied, 20H illegal drive number, 08H device not available. @INIT calls 4F40H.

4F3D
GOSUB to 49F1H, the file prologue without the open test: it points Index Register IX at the control block at Register Pair DE, notes the caller in JRET$ (4BCAH) and JDCB$ (4220H), saves Register Pairs BC, DE and HL and Index Register IX so that the final RET (to 4A14H) restores them, and returns with Register A = 00H.
4F40
LD A,(442BH) 3A 2B 44
Fetch SFLAG$, the system flags, from 442BH into Register A. @INIT enters here (503AH) with bit 2 already cleared.
4F43
LD (500FH),A 32 0F 50
Store Register A (SFLAG$) at 500FH, the operand of the LD B at 500EH, so that bit 2 (a program load by @RUN is in progress) can be tested once the file is found.
4F46
AND F9H E6 F9
Mask Register A with F9H: bit 2 (a program load by @RUN is in progress) and bit 1 (run the program without DEBUG) are cleared.
4F48
LD (442BH),A 32 2B 44
Store Register A at 442BH, SFLAG$: both flags are off for every later open; 5017H sets bit 1 again for a program that may only be executed.
4F4B
LD A,(IX+00H) DD 7E 00
Fetch byte +00H of the control block at Index Register IX into Register A: the first character of the specification.
4F4E
CP 2AH FE 2A
Compare Register A against 2AH, the asterisk that starts a device specification.
4F50
If the Z FLAG is set (a device specification: 2AH and the two letters of a device name), JUMP to 4EDCH to open the device.
4F52
LD A,B 78
Copy Register B (the record length the caller asked for, 00H for 256) into Register A.
4F53
LD (51FFH),A 32 FF 51
Store Register A (the record length) at 51FFH, for the control block (+09H, 50C6H) and, for a new file, for its directory record (+04H, 5079H).
4F56
LD (50B2H),HL 22 B2 50
Store Register Pair HL (the caller's buffer address) at 50B2H-50B3H, the operand of the LD DE at 50B1H, which puts it into the control block (+03H-+04H).
4F59
PUSH IX DD E5
Save Index Register IX (the control block) to the stack.
4F5B
POP HL E1
Restore Register Pair HL from the stack: Register Pair HL = the control block, where the specification text is.
4F5C
GOSUB to 5106H to take the specification at Register Pair HL apart: the name and the extension into 51EAH-51F4H, the password into 51E2H-51E9H, the drive into 4F70H (FFH when none), the ! flag into 50AAH. Returns the Z FLAG, or the NZ FLAG with Register A = 13H or 20H.
4F5F
RET NZ C0
If the NZ FLAG is set (the specification is not valid), RETurn with the error number in Register A.
4F60
LD HL,51EAH 21 EA 51
Point Register Pair HL to 51EAH, the name and the extension of the specification (11 bytes).
4F63
GOSUB to 4E17H to work out the hash code of the name and the extension: it is stored at 51E1H.
4F66
LD DE,51E2H 11 E2 51
Point Register Pair DE to 51E2H, the password field of the specification (8 bytes, spaces when none was given).
4F69
GOSUB to 4E27H to work out the hash code of the password: Register Pair HL = the hash code.
4F6C
LD (51F5H),HL 22 F5 51
Store Register Pair HL (the password's hash code) at 51F5H-51F6H, where 4FE7H compares it and @INIT puts it into a new directory record (+10H-+11H).
4F6F
LD A,00H 3E 00
Load Register A with the drive of the specification: the operand at 4F70H, 00H in the file, is the drive 0-7 that 513CH stored, or FFH (5111H) when no drive was named; 4FD4H changes it to the drive where the file is found.
4F71
LD C,A 4F
Copy Register A (the drive, or FFH) into Register C.
4F72
INC A 3C
INCrement Register A: the Z FLAG is set when it was FFH (no drive named).
4F73
If the NZ FLAG is set (a drive was named), JUMP to 4F76H to search only drive Register C.
4F75
LD C,A 4F
Copy Register A (00H) into Register C: no drive named, so the search starts with drive 0.
4F76
GOSUB to 4E52H (@CKDRV) to check drive Register C: present, a diskette turning, its allocation table read. The Z FLAG is set when the drive is ready.
4F79
If the NZ FLAG is set (the drive is not ready), JUMP to 4F8EH to go on to the next drive.
4F7B
GOSUB to 51BDH to read the Hash Index Table (sector 1 of the directory cylinder) of drive Register C into 4300H; Register Pair HL comes back as 4300H.
4F7E
RET NZ C0
If the NZ FLAG is set (the HIT could not be read), RETurn with error 16H in Register A.
4F7F
LD A,(HL) 7E
Fetch the HIT byte at Register Pair HL into Register A: the hash code of directory record Register L, 00H when the record is free.
4F80
OR A B7
Set the flags from Register A (OR A): the Z FLAG is set for a free record.
4F81
If the Z FLAG is set (a free record), JUMP to 4F8BH to go on to the next byte of the HIT.
4F83
PUSH HL E5
Save Register Pair HL (the position in the HIT) to the stack.
4F84
LD HL,51E1H 21 E1 51
Point Register Pair HL to 51E1H, the hash code of the name being searched for.
4F87
CP (HL) BE
Compare Register A (the HIT byte) against the hash code at Register Pair HL (51E1H).
4F88
POP HL E1
Restore Register Pair HL from the stack: the position in the HIT. POP does not change the flags.
4F89
If the Z FLAG is set (the hash codes match), JUMP to 4FACH to read directory record Register L and compare its name.
4F8B
INC L 2C
INCrement Register L: the next byte of the HIT, the next directory record.
4F8C
If the NZ FLAG is set (Register L did not come back to 00H), LOOP BACK to 4F7FH: all 256 bytes of the HIT are tested.
4F8E
GOSUB to 4F97H to choose the next drive: the CARRY FLAG is set when there is one to search.
4F91
If the CARRY FLAG is set (Register C = the next drive), LOOP BACK to 4F76H to search it.
4F93
LD A,18H 3E 18
Load Register A with 18H, error 24: file not in directory.
4F95
OR A B7
Set the flags from Register A (OR A): the NZ FLAG, the CARRY FLAG cleared.
4F96
RET C9
RETurn with the NZ FLAG and Register A = 18H. @INIT (503EH) takes this error as the sign to create the file.

4F97H - Go On to the Next Drive

Register C = the drive just searched. When the specification named a drive (4F70H not FFH) there is no other drive to search: the NZ FLAG is returned with the CARRY FLAG clear. Otherwise Register C is advanced and the CARRY FLAG is set while it is below 8.

4F97
LD A,(4F70H) 3A 70 4F
Fetch the drive of the specification from 4F70H, the operand of the LD A at 4F6FH, into Register A: FFH when no drive was named.
4F9A
INC A 3C
INCrement Register A (the drive of the specification): 00H when it was FFH, no drive named.
4F9B
OR A B7
Set the flags from Register A (OR A): the Z FLAG for no drive named, and the CARRY FLAG cleared.
4F9C
RET NZ C0
If the NZ FLAG is set (a drive was named), RETurn with the CARRY FLAG clear: that drive was the only one.
4F9D
INC C 0C
INCrement Register C, the drive: the next drive.
4F9E
LD A,C 79
Copy Register C (the next drive) into Register A.
4F9F
CP 08H FE 08
Compare Register A (the next drive) against 08H: the CARRY FLAG is set for drives 0-7.
4FA1
RET C9
RETurn with the CARRY FLAG set when Register C is a drive to search, clear after drive 7.

4FA2H - Not This Record: Read the HIT Again

The directory record whose hash code matched is not the file (an extension record, not in use, or another name). Its sector has replaced the HIT in 4300H, so the HIT is read again and the search goes on with the next byte.

4FA2
POP BC C1
Restore Register Pair BC from the stack (pushed at 4FB8H): discarded, the same values come back below.
4FA3
POP HL E1
Restore Register Pair HL from the stack (pushed at 4FB7H): the directory record's address, no longer needed.
4FA4
POP BC C1
Restore Register Pair BC from the stack (pushed at 4FADH): Register C = the drive being searched.
4FA5
GOSUB to 51BDH to read the Hash Index Table of drive Register C into 4300H again. Register Pair BC is kept.
4FA8
POP HL E1
Restore Register Pair HL from the stack (pushed at 4FACH): the position in the HIT of the record just tested.
4FA9
RET NZ C0
If the NZ FLAG is set (the HIT could not be read), RETurn with error 16H in Register A.
4FAA
JUMP to 4F8BH to go on with the next byte of the HIT.

4FACH - A Hash Match: Check the Directory Record

Register L = the position in the HIT, which is the directory record number, Register C = the drive. The record is read (DIRRD reads the directory sector that holds it into 4300H) and must be the first record of a file (bit 7 of its attribute clear: SYS8/SYS gives the extension records it makes attribute 90H), in use (bit 4) and have the same name and extension (+05H-+0FH) as 51EAH-51F4H.

4FAC
PUSH HL E5
Save Register Pair HL (the position in the HIT) to the stack.
4FAD
PUSH BC C5
Save Register Pair BC (Register C = the drive) to the stack.
4FAE
LD B,L 45
Copy Register L (the position in the HIT) into Register B: the directory record number.
4FAF
GOSUB to 4B10H (DIRRD) to read the directory sector that holds record Register B of drive Register C into 4300H; Register Pair HL comes back pointing at the record. The Z FLAG is set on success.
4FB2
If the Z FLAG is set (the sector was read), JUMP to 4FB7H to look at the record.
4FB4
POP BC C1
Restore Register Pair BC from the stack: the values pushed at 4FADH, taken off.
4FB5
POP HL E1
Restore Register Pair HL from the stack: the value pushed at 4FACH, taken off.
4FB6
RET C9
RETurn with the NZ FLAG and Register A = 11H, the directory read error from DIRRD.
4FB7
PUSH HL E5
Save Register Pair HL (the address of the directory record in 4300H) to the stack.
4FB8
PUSH BC C5
Save Register Pair BC (Register B = the record number, Register C = the drive) to the stack.
4FB9
BIT 7,(HL) CB 7E
Test bit 7 of the byte at Register Pair HL, the record's attribute: set for an extension record, which holds more extents of a file whose first record is elsewhere.
4FBB
If the NZ FLAG is set (an extension record), JUMP to 4FA2H to read the HIT again and go on.
4FBD
BIT 4,(HL) CB 66
Test bit 4 of the byte at Register Pair HL (the attribute): set when the record is in use.
4FBF
If the Z FLAG is set (the record is not in use), JUMP to 4FA2H to read the HIT again and go on.
4FC1
LD A,05H 3E 05
Load Register A with 05H, the offset of the file name in a directory record.
4FC3
ADD A,L 85
ADD Register L (the low byte of the record's address) to Register A.
4FC4
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the record's +05H, the file name.
4FC5
LD DE,51EAH 11 EA 51
Point Register Pair DE to 51EAH, the name and the extension of the specification.
4FC8
LD B,0BH 06 0B
Load Register B with 0BH (11), the bytes of the name and the extension.
4FCA
LD A,(DE) 1A
Fetch the next character of the specification from Register Pair DE into Register A.
4FCB
CP (HL) BE
Compare Register A against the record's character at Register Pair HL.
4FCC
If the NZ FLAG is set (the names differ), JUMP to 4FA2H to read the HIT again and go on.
4FCE
INC HL 23
INCrement Register Pair HL to the record's next character.
4FCF
INC DE 13
INCrement Register Pair DE to the specification's next character.
4FD0
DECrement Register B (the characters left) and LOOP BACK to 4FCAH until all 11 are compared.

4FD2H - The File Is Found: Decide the Access

The name matches. The drive where the file is becomes the drive of the specification (4F70H), the attribute goes to 51FBH, and the access the open gives (bits 2-0 of +01H of the control block, 0 the most access) is decided: 00H when the password's hash code is 113DH (the master password RS0LT0FF) or equals the record's +10H-+11H; otherwise the protection level in bits 2-0 of the attribute, except that level 7 is refused (error 19H, file access denied) and level 6 (execute only) gives 06H, or 05H (read) with bit 1 of SFLAG$ set (run the program without DEBUG) when the open is the program load of @RUN; for level 6 the BREAK trap is also left off when the request returns.

4FD2
POP BC C1
Restore Register Pair BC from the stack (pushed at 4FB8H): Register B = the directory record number, Register C = the drive.
4FD3
LD A,C 79
Copy Register C (the drive where the file was found) into Register A.
4FD4
LD (4F70H),A 32 70 4F
Store Register A (the drive) at 4F70H, the operand of the LD A at 4F6FH: @INIT (5046H) reads it after an open.
4FD7
POP HL E1
Restore Register Pair HL from the stack (pushed at 4FB7H): the address of the directory record in 4300H.
4FD8
POP AF F1
Restore Register Pair AF from the stack: this takes off Register Pair BC pushed at 4FADH, no longer needed.
4FD9
POP AF F1
Restore Register Pair AF from the stack: this takes off the position in the HIT pushed at 4FACH, no longer needed.
4FDA
PUSH BC C5
Save Register Pair BC (Register B = the record number, Register C = the drive) to the stack for 5024H.
4FDB
PUSH HL E5
Save Register Pair HL (the address of the directory record) to the stack for 5023H.
4FDC
LD A,(HL) 7E
Fetch the byte at Register Pair HL, the record's attribute, into Register A.
4FDD
LD (51FBH),A 32 FB 51
Store Register A (the attribute of the directory record) at 51FBH, the work cell of the attribute.
4FE0
AND 07H E6 07
Mask Register A to bits 2-0: the file's protection level, 0-7.
4FE2
LD C,A 4F
Copy Register A (the protection level) into Register C.
4FE3
LD A,10H 3E 10
Load Register A with 10H, the offset of the password hash code in a directory record.
4FE5
ADD A,L 85
ADD Register L (the low byte of the record's address) to Register A.
4FE6
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the record's +10H, the password hash code of the file.
4FE7
LD DE,(51F5H) ED 5B F5 51
Fetch the hash code of the specification's password from 51F5H-51F6H into Register Pair DE.
4FEB
PUSH HL E5
Save Register Pair HL (the address of the record's +10H) to the stack.
4FEC
LD HL,113DH 21 3D 11
Load Register Pair HL with 113DH, the hash code of the master password RS0LT0FF.
4FEF
XOR A AF
Set Register A to 00H (XOR A), which also clears the CARRY FLAG for the SBC; 00H is the access granted when a password matches.
4FF0
SBC HL,DE ED 52
SUBtract Register Pair DE (the specification's hash code) from Register Pair HL (113DH): the Z FLAG is set when they are equal.
4FF2
POP HL E1
Restore Register Pair HL from the stack: the address of the record's +10H. POP does not change the flags.
4FF3
If the Z FLAG is set (the master password was given), JUMP to 5020H with Register A = 00H: every access.
4FF5
LD A,(HL) 7E
Fetch the low byte of the record's password hash code from Register Pair HL (+10H) into Register A.
4FF6
INC HL 23
INCrement Register Pair HL to the record's +11H.
4FF7
LD H,(HL) 66
Fetch the high byte of the record's password hash code from Register Pair HL (+11H) into Register H.
4FF8
LD L,A 6F
Copy Register A (the low byte) into Register L: Register Pair HL = the record's password hash code.
4FF9
XOR A AF
Set Register A to 00H (XOR A), the access for a matching password; the CARRY FLAG is cleared.
4FFA
SBC HL,DE ED 52
SUBtract Register Pair DE (the specification's hash code) from Register Pair HL (the record's): the Z FLAG is set when they are equal.
4FFC
If the Z FLAG is set (the file's password was given, or the file has none and none was given: both hash to 4296H), JUMP to 5020H with Register A = 00H: every access.
4FFE
LD A,C 79
Copy Register C (the file's protection level) into Register A.
4FFF
CP 07H FE 07
Compare Register A (the protection level) against 07H, no access without the password.
5001
If the NZ FLAG is set (level 0-6), JUMP to 5009H.
5003
POP HL E1
Restore Register Pair HL from the stack (pushed at 4FDBH): taken off.
5004
POP BC C1
Restore Register Pair BC from the stack (pushed at 4FDAH): taken off.
5005
LD A,19H 3E 19
Load Register A with 19H, error 25: file access denied.
5007
OR A B7
Set the flags from Register A (OR A): the NZ FLAG.
5008
RET C9
RETurn with the NZ FLAG and Register A = 19H. The RET goes to 4A14H, which restores the caller's registers.
5009
LD A,C 79
Copy Register C (the file's protection level) into Register A.
500A
CP 06H FE 06
Compare Register A (the protection level) against 06H, execute only.
500C
If the NZ FLAG is set (level 0-5), JUMP to 5020H with Register A = the level: the open gives the access of the file's protection level.
500E
LD B,00H 06 00
Load Register B with the old SFLAG$: the operand at 500FH, 00H in the file, is the value 4F43H stored before bits 1 and 2 were cleared.
5010
BIT 2,B CB 50
Test bit 2 of Register B (the old SFLAG$): set when this open is the program load of @RUN (4C61H).
5012
If the Z FLAG is set (an ordinary open), JUMP to 501BH with Register A = 06H: the file may only be executed.
5014
LD HL,442BH 21 2B 44
Point Register Pair HL to 442BH, SFLAG$, the system flags of SYS0/SYS.
5017
SET 1,(HL) CB CE
Set bit 1 of the byte at Register Pair HL (442BH): @RUN runs the program without DEBUG (4C6DH), so an execute only program cannot be looked at.
5019
LD A,05H 3E 05
Load Register A with 05H, read access, which the loader needs to read the program.
501B
LD HL,4BE8H 21 E8 4B
Point Register Pair HL to 4BE8H, the operand of the LD A at 4BE7H in the supervisor dispatcher of SYS0/SYS, which holds the BREAK trap switch saved at the start of the request.
501E
LD (HL),C9H 36 C9
Store C9H at Register Pair HL (4BE8H): when the request returns, the dispatcher stores C9H at 4405H, so the BREAK trap is off.
5020
LD (50A2H),A 32 A2 50
Store Register A (the access granted, 00H-06H) at 50A2H, the operand of the LD A at 50A1H that puts it into bits 2-0 of +01H of the control block.
5023
POP HL E1
Restore Register Pair HL from the stack: the address of the directory record (pushed at 4FDBH).
5024
POP BC C1
Restore Register Pair BC from the stack: Register B = the record number, Register C = the drive (pushed at 4FDAH).
5025
JUMP to 5096H to fill the control block from the directory record; its RET goes to 4A14H.

5028H - Function 20H (Request A4H, @INIT): Open or Create a File

The same registers as @OPEN. The file is opened when it is on a diskette (the Z FLAG, the CARRY FLAG clear). When @OPEN returns error 18H (file not in directory), the file is created: on the drive named, or on the first of drives 0-7 that is ready, not write protected and has a free directory record. A new file gets the access 00H, the attribute 10H (in use, protection level 0), or 17H (protection level 7) when the specification holds a password, its record length, its name and extension and the password's hash code; it has no extents. Returns the Z FLAG and the CARRY FLAG for a new file; the NZ FLAG with Register A = 1AH (full or write protected disk) when no drive could take it, or with the error of the open or of a disk access.

5028
GOSUB to 49F1H, the file prologue without the open test: Index Register IX = the control block at Register Pair DE, the caller's registers saved for the final RET (to 4A14H), Register A = 00H.
502B
LD (50A2H),A 32 A2 50
Store Register A (00H) at 50A2H, the operand of the LD A at 50A1H: a file created here gets every access. An open of an existing file stores its own value (5020H).
502E
LD A,10H 3E 10
Load Register A with 10H, the attribute of a new file: bit 4 in use, protection level 0.
5030
LD (5042H),A 32 42 50
Store Register A (10H) at 5042H, the operand of the LD A at 5041H; 5151H stores 17H there when the specification holds a password.
5033
PUSH HL E5
Save Register Pair HL (the caller's buffer address) to the stack.
5034
LD HL,442BH 21 2B 44
Point Register Pair HL to 442BH, SFLAG$, the system flags of SYS0/SYS.
5037
RES 2,(HL) CB 96
Clear bit 2 of the byte at Register Pair HL (442BH): this open is not the program load of @RUN.
5039
POP HL E1
Restore Register Pair HL from the stack: the caller's buffer address.
503A
GOSUB to 4F40H, @OPEN after its prologue, to open the file: the Z FLAG when it is found, else the NZ FLAG with the error number in Register A.
503D
RET Z C8
If the Z FLAG is set (the file exists and is open), RETurn with the CARRY FLAG clear.
503E
CP 18H FE 18
Compare Register A (the error number) against 18H, file not in directory.
5040
RET NZ C0
If the NZ FLAG is set (another error), RETurn with the error number in Register A.
5041
LD A,10H 3E 10
Load Register A with the attribute of the new file: the operand at 5042H, 10H in the file, 10H from 5030H or 17H from 5151H.
5043
LD (51FBH),A 32 FB 51
Store Register A (the attribute) at 51FBH, the first of the five bytes 51FBH-51FFH that become +00H-+04H of the new directory record.
5046
LD A,(4F70H) 3A 70 4F
Fetch the drive of the specification from 4F70H into Register A: FFH when no drive was named.
5049
LD C,A 4F
Copy Register A (the drive, or FFH) into Register C.
504A
INC A 3C
INCrement Register A: the Z FLAG is set when it was FFH (no drive named).
504B
If the NZ FLAG is set (a drive was named), JUMP to 504EH to try only drive Register C.
504D
LD C,A 4F
Copy Register A (00H) into Register C: no drive named, so drive 0 is tried first.
504E
GOSUB to 4E52H (@CKDRV) to check drive Register C: the Z FLAG when it is ready, the CARRY FLAG then set when it is write protected.
5051
If the NZ FLAG is set (the drive is not ready), JUMP to 505EH to try the next drive.
5053
If the CARRY FLAG is set (the diskette is write protected), JUMP to 505EH to try the next drive.
5055
GOSUB to 51BDH to read the Hash Index Table of drive Register C into 4300H; Register Pair HL = 4300H.
5058
RET NZ C0
If the NZ FLAG is set (the HIT could not be read), RETurn with error 16H in Register A.
5059
GOSUB to 5172H to find a free directory record: the Z FLAG with Register Pair HL at its byte in the HIT.
505C
If the Z FLAG is set (a free record was found), JUMP to 5067H to make the directory record.
505E
GOSUB to 4F97H to choose the next drive: the CARRY FLAG is set when there is one to try.
5061
If the CARRY FLAG is set (Register C = the next drive), LOOP BACK to 504EH to try it.
5063
LD A,1AH 3E 1A
Load Register A with 1AH, error 26: full or write protected disk.
5065
OR A B7
Set the flags from Register A (OR A): the NZ FLAG.
5066
RET C9
RETurn with the NZ FLAG and Register A = 1AH.

5067H - Create the Directory Record

Register Pair HL = the byte of the free record in the HIT at 4300H, so Register L = the record number; Register C = the drive. The name's hash code goes into the HIT and the HIT is written back; then the record's sector is read and the record is made: +00H-+04H from 51FBH-51FFH (the attribute, 00H, 00H, the end of file byte 00H, the record length), +05H-+15H from 51EAH-51FAH (the name, the extension, the password hash code at +10H-+11H, the two bytes 51F7H-51F8H at +12H-+13H, and the ending record number 0000H), +16H-+1FH FFH (no extents). The sector is written back and the control block filled.

5067
LD B,L 45
Copy Register L (the free record's place in the HIT) into Register B: the directory record number.
5068
LD A,(51E1H) 3A E1 51
Fetch the name's hash code from 51E1H into Register A.
506B
LD (HL),A 77
Store Register A (the hash code) at Register Pair HL, the record's byte in the HIT: the record is taken.
506C
GOSUB to 51BEH to write the HIT at 4300H back to sector 1 of the directory cylinder of drive Register C, with the deleted data mark, and verify it. The Z FLAG is set on success, else Register A = 17H.
506F
If the Z FLAG is set (the HIT was written), GOSUB to 4B10H (DIRRD) to read the directory sector that holds record Register B into 4300H; Register Pair HL comes back pointing at the record.
5072
RET NZ C0
If the NZ FLAG is set (the HIT write or the directory read failed), RETurn with the error number in Register A (17H or 11H).
5073
PUSH HL E5
Save Register Pair HL (the address of the new record in 4300H) to the stack.
5074
PUSH BC C5
Save Register Pair BC (Register B = the record number, Register C = the drive) to the stack.
5075
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the address of the new record.
5076
LD BC,0005H 01 05 00
Load Register Pair BC with 0005H, the bytes +00H-+04H of the record.
5079
LD HL,51FBH 21 FB 51
Point Register Pair HL to 51FBH: the attribute (51FBH), 00H (51FCH), 00H (51FDH), the end of file byte 00H (51FEH) and the record length (51FFH).
507C
LDIR ED B0
Copy 5 bytes from Register Pair HL (51FBH-51FFH) to Register Pair DE (the record's +00H-+04H). Register Pair HL ends at 5200H, Register Pair DE at the record's +05H, Register Pair BC at 0000H.
507E
LD C,11H 0E 11
Load Register C with 11H (17): Register Pair BC = 0011H, the bytes +05H-+15H of the record.
5080
LD HL,51EAH 21 EA 51
Point Register Pair HL to 51EAH: the name (51EAH-51F1H), the extension (51F2H-51F4H), the password hash code (51F5H-51F6H), 51F7H-51F8H, which SYS2/SYS does not set (SYS7/SYS stores a hash code there, its 530CH), and 0000H (51F9H-51FAH).
5083
LDIR ED B0
Copy 17 bytes from Register Pair HL (51EAH-51FAH) to Register Pair DE (the record's +05H-+15H): the ending record number at +14H-+15H is 0000H. Register Pair DE ends at the record's +16H, Register Pair BC at 0000H.
5085
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the record's +16H, the first extent.
5086
LD B,0AH 06 0A
Load Register B with 0AH (10), the bytes +16H-+1FH: the four extents and the two link bytes.
5088
GOSUB to 50FFH to store FFH in the 10 bytes at Register Pair HL: no extents and no link (FFH at +1EH). It returns Register A = 00H.
508B
POP BC C1
Restore Register Pair BC from the stack: Register B = the record number, Register C = the drive.
508C
GOSUB to 4B1FH (DIRWR) to write the directory sector at 4300H back for record Register B of drive Register C, with the deleted data mark, and verify it. The Z FLAG is set on success.
508F
POP HL E1
Restore Register Pair HL from the stack: the address of the new record in 4300H.
5090
RET NZ C0
If the NZ FLAG is set (the sector could not be written), RETurn with the error number in Register A.
5091
GOSUB to 5096H to fill the control block from the new record at Register Pair HL.
5094
SCF 37
Set the CARRY FLAG: the file was created, it was not on the diskette before. SCF does not change the Z FLAG.
5095
RET C9
RETurn with the Z FLAG and Register A = 00H from 5096H and the CARRY FLAG set. The RET goes to 4A14H, which restores the caller's registers.

5096H - Fill the File Control Block

Register Pair HL = the file's directory record in 4300H, Register B = its record number, Register C = the drive, Index Register IX = the control block. The block gets: +00H 80H (an open file); +01H the access (bits 2-0, from 50A2H), bit 5 (the buffer must be read first), bit 3 when the specification ended with ! (50AAH) and bit 7 when the record length is not 256 (byte mode); +02H 00H; +03H-+04H the buffer (50B2H); +05H 00H, the position in the buffer; +06H the drive; +07H the record number; +08H the end of file byte (record +03H); +09H the record length the caller asked for (51FFH); +0AH-+0BH 0000H, the next record; +0CH-+0DH the ending record number (record +14H-+15H); from +0EH the record's extents (cylinder, granule byte), each one after the first preceded by the number of granules in the extents before it, as far as the record has them; the rest of the 32 bytes FFH. Returns the Z FLAG and Register A = 00H.

5096
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the address of the directory record.
5097
PUSH IX DD E5
Save Index Register IX (the control block) to the stack.
5099
POP HL E1
Restore Register Pair HL from the stack: Register Pair HL = the control block (the value of Index Register IX).
509A
LD (HL),80H 36 80
Store 80H at Register Pair HL, byte +00H of the control block: bit 7, the block describes an open file.
509C
INC HL 23
INCrement Register Pair HL to byte +01H of the control block.
509D
LD A,(51FFH) 3A FF 51
Fetch the record length the caller asked for from 51FFH into Register A (00H for 256).
50A0
OR A B7
Set the flags from Register A (OR A): the Z FLAG is set for 256 byte records.
50A1
LD A,00H 3E 00
Load Register A with the access granted: the operand at 50A2H, 00H in the file, is stored by 5020H (an existing file) or 502BH (a new file, 00H). LD does not change the flags.
50A3
If the Z FLAG is set (256 byte records), JUMP to 50A7H.
50A5
OR 80H F6 80
OR 80H into Register A: bit 7, byte mode, for a record length below 256.
50A7
OR 20H F6 20
OR 20H into Register A: bit 5, the buffer must be read before its first byte is used.
50A9
OR 00H F6 00
OR the ! flag into Register A: the operand at 50AAH, 00H in the file, is 08H (bit 3) from 514DH when the specification ended with !, which makes the file routines bring the directory up to date after every write that moves the end of the file (49E9H).
50AB
LD (HL),A 77
Store Register A (the flags and the access) at Register Pair HL, byte +01H of the control block.
50AC
INC HL 23
INCrement Register Pair HL to byte +02H of the control block.
50AD
XOR A AF
Set Register A to 00H (XOR A), the value of byte +02H and +05H of the control block.
50AE
LD (HL),A 77
Store Register A (00H) at Register Pair HL, byte +02H.
50AF
INC HL 23
INCrement Register Pair HL to byte +03H of the control block.
50B0
PUSH DE D5
Save Register Pair DE (the address of the directory record) to the stack.
50B1
LD DE,0000H 11 00 00
Load Register Pair DE with the caller's buffer address: the operand at 50B2H-50B3H, 0000H in the file, is stored by 4F56H.
50B4
LD (HL),E 73
Store Register E (the low byte of the buffer address) at Register Pair HL, byte +03H.
50B5
INC HL 23
INCrement Register Pair HL to byte +04H of the control block.
50B6
LD (HL),D 72
Store Register D (the high byte of the buffer address) at Register Pair HL, byte +04H.
50B7
INC HL 23
INCrement Register Pair HL to byte +05H of the control block.
50B8
POP DE D1
Restore Register Pair DE from the stack: the address of the directory record.
50B9
LD (HL),A 77
Store Register A (00H) at Register Pair HL, byte +05H: the position of the next byte in the buffer.
50BA
INC HL 23
INCrement Register Pair HL to byte +06H of the control block.
50BB
LD (HL),C 71
Store Register C (the drive) at Register Pair HL, byte +06H.
50BC
INC HL 23
INCrement Register Pair HL to byte +07H of the control block.
50BD
LD (HL),B 70
Store Register B (the directory record number) at Register Pair HL, byte +07H.
50BE
INC HL 23
INCrement Register Pair HL to byte +08H of the control block.
50BF
INC DE 13
INCrement Register Pair DE to the record's +01H.
50C0
INC DE 13
INCrement Register Pair DE to the record's +02H.
50C1
INC DE 13
INCrement Register Pair DE to the record's +03H, the end of file byte.
50C2
LD A,(DE) 1A
Fetch the record's end of file byte from Register Pair DE (+03H) into Register A: the position of the end of the file in its last sector.
50C3
LD (HL),A 77
Store Register A (the end of file byte) at Register Pair HL, byte +08H of the control block.
50C4
INC HL 23
INCrement Register Pair HL to byte +09H of the control block.
50C5
INC DE 13
INCrement Register Pair DE to the record's +04H.
50C6
LD A,(51FFH) 3A FF 51
Fetch the record length the caller asked for from 51FFH into Register A.
50C9
LD (HL),A 77
Store Register A (the record length) at Register Pair HL, byte +09H.
50CA
INC HL 23
INCrement Register Pair HL to byte +0AH of the control block.
50CB
XOR A AF
Set Register A to 00H (XOR A), the value of bytes +0AH and +0BH of the control block.
50CC
LD (HL),A 77
Store Register A (00H) at Register Pair HL, byte +0AH: the next record number starts at 0000H.
50CD
INC HL 23
INCrement Register Pair HL to byte +0BH of the control block.
50CE
LD (HL),A 77
Store Register A (00H) at Register Pair HL, byte +0BH.
50CF
INC HL 23
INCrement Register Pair HL to byte +0CH of the control block.
50D0
SET 4,E CB E3
Set bit 4 of Register E: Register Pair DE = the record's +14H, the ending record number (a record starts at a multiple of 32, so +04H becomes +14H).
50D2
LD BC,0002H 01 02 00
Load Register Pair BC with 0002H, the two bytes of the ending record number. Register B and Register C are no longer needed: Register Pair BC then counts the granules (0000H after the LDIR).
50D5
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the record's +14H, Register Pair DE = the control block's +0CH.
50D6
LDIR ED B0
Copy 2 bytes from Register Pair HL (the record's +14H-+15H) to Register Pair DE (+0CH-+0DH of the control block). Register Pair HL ends at the record's +16H, Register Pair DE at +0EH, Register Pair BC at 0000H.
50D8
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the record's +16H, the first extent; Register Pair HL = the control block's +0EH.
50D9
LD A,05H 3E 05
Load Register A with 05H, the count of the extent loop.
50DB
PUSH AF F5
Save Register Pair AF (Register A = the count) to the stack.
50DC
LD A,(DE) 1A
Fetch the extent's first byte from Register Pair DE into Register A: the cylinder where the extent starts.
50DD
LD (HL),A 77
Store Register A (the cylinder) at Register Pair HL in the control block.
50DE
INC HL 23
INCrement Register Pair HL to the next byte of the control block.
50DF
INC DE 13
INCrement Register Pair DE to the extent's second byte.
50E0
LD A,(DE) 1A
Fetch the extent's second byte from Register Pair DE into Register A: bits 7-5 the first granule on the cylinder, bits 4-0 the granules in the extent less one.
50E1
LD (HL),A 77
Store Register A (the granule byte) at Register Pair HL in the control block.
50E2
INC HL 23
INCrement Register Pair HL to the next byte of the control block.
50E3
AND 1FH E6 1F
Mask Register A with 1FH to bits 4-0: the granules in the extent less one.
50E5
INC A 3C
INCrement Register A: the granules in the extent.
50E6
ADD A,C 81
ADD Register C (the low byte of the granules counted so far) to Register A.
50E7
LD C,A 4F
Copy Register A (the new low byte of the granule count) into Register C.
50E8
If the NO CARRY FLAG is set (no carry out of Register C), JUMP to 50EBH.
50EA
INC B 04
INCrement Register B: Register Pair BC = the granules in the extents so far.
50EB
POP AF F1
Restore Register Pair AF from the stack: Register A = the count.
50EC
DEC A 3D
DECrement Register A (the count of the extent loop): the Z FLAG is set when it reaches 0.
50ED
RET Z C8
If the Z FLAG is set (the count is used up), RETurn. This is reached only when a fifth pair was copied from the record's +1EH-+1FH, which needs a byte below FEH at +1EH; SYS2/SYS writes FFH there (5088H) and SYS8/SYS FEH (its 4FD5H), so the loop ends at 50F3H first.
50EE
PUSH AF F5
Save Register Pair AF (Register A = the count) to the stack.
50EF
INC DE 13
INCrement Register Pair DE to the record's next extent.
50F0
LD A,(DE) 1A
Fetch the next extent's cylinder byte from Register Pair DE into Register A.
50F1
CP FEH FE FE
Compare Register A against FEH: FEH (a link to an extension record) or FFH (no more extents) leave the CARRY FLAG clear.
50F3
If the NO CARRY FLAG is set (no more extents in this record), JUMP to 50FBH to fill the rest of the block with FFH.
50F5
LD (HL),C 71
Store Register C (the low byte of the granules so far) at Register Pair HL in the control block.
50F6
INC HL 23
INCrement Register Pair HL to the next byte of the control block.
50F7
LD (HL),B 70
Store Register B (the high byte of the granules so far) at Register Pair HL in the control block.
50F8
INC HL 23
INCrement Register Pair HL to the next byte of the control block, the place of the next extent.
50F9
LOOP BACK to 50DCH to copy the next extent of the directory record into the control block.
50FB
POP AF F1
Restore Register Pair AF from the stack: Register A = the count left, 1-4.
50FC
RLCA 07
Rotate Register A left one bit: the count x 2.
50FD
RLCA 07
Rotate Register A left one bit: the count x 4, the bytes from Register Pair HL to the end of the 32 byte control block.
50FE
LD B,A 47
Copy Register A (the bytes to fill) into Register B.

50FFH - Fill with FFH

Stores FFH in Register B bytes from Register Pair HL on, then returns Register A = 00H with the Z FLAG. 50FBH ends the control block with it, 5088H the extents of a new directory record.

50FF
LD (HL),FFH 36 FF
Store FFH at Register Pair HL: an unused extent byte (or the end of the control block).
5101
INC HL 23
INCrement Register Pair HL to the next byte to fill with FFH.
5102
DECrement Register B (the bytes left) and LOOP BACK to 50FFH until it reaches 0.
5104
XOR A AF
Set Register A to 00H (XOR A): the Z FLAG, the CARRY FLAG cleared.
5105
RET C9
RETurn with the Z FLAG and Register A = 00H; Register Pair HL is past the bytes filled and Register B is 00H.

5106H - Take the File Specification Apart

Register Pair HL = the specification NAME/EXT.PASSWORD:D (the name a letter and up to 7 letters or digits, the extension a letter and up to 2 more, the password the same as the name; capitals only, which @FSPEC makes of small letters). 51E2H-51F4H are filled with spaces, then the name goes to 51EAH-51F1H, the extension to 51F2H-51F4H, the password to 51E2H-51E9H, the drive number to 4F70H (FFH when none) and the ! flag to 50AAH. Returns the Z FLAG, or the NZ FLAG with Register A = 13H (illegal file name: the name does not start with a letter) or 20H (illegal drive number: not 0-7).

5106
LD B,13H 06 13
Load Register B with 13H (19), the bytes 51E2H-51F4H: the password (8), the name (8) and the extension (3).
5108
LD DE,51E2H 11 E2 51
Point Register Pair DE to 51E2H, the password field.
510B
LD A,20H 3E 20
Load Register A with 20H, a space, the filler of the password, name and extension fields.
510D
LD (DE),A 12
Store Register A (a space) at Register Pair DE.
510E
INC DE 13
INCrement Register Pair DE to the next byte of the fields 51E2H-51F4H.
510F
DECrement Register B and LOOP BACK to 510DH until the 19 bytes 51E2H-51F4H are spaces. A specification without a password keeps eight spaces, whose hash code is 4296H.
5111
LD A,FFH 3E FF
Load Register A with FFH, the value that stands for no drive named.
5113
LD (4F70H),A 32 70 4F
Store Register A (FFH) at 4F70H, the operand of the LD A at 4F6FH: no drive named so far.
5116
LD E,EAH 1E EA
Load Register E with EAH: Register Pair DE = 51EAH, the name field (Register D is 51H from 5108H).
5118
GOSUB to 5156H to copy the name from Register Pair HL to Register Pair DE (51EAH), up to 8 characters: Register A comes back as the character after the name, Register B as 8 less the characters copied.
511B
LD C,A 4F
Copy Register A (the character after the name) into Register C.
511C
LD A,B 78
Copy Register B (8 less the characters copied) into Register A.
511D
SUB 08H D6 08
SUBtract 08H from Register A: the Z FLAG is set when no character of the name was copied.
511F
If the NZ FLAG is set (the name has at least one character), JUMP to 5124H.
5121
OR 13H F6 13
OR 13H into Register A (00H): Register A = 13H, error 19: illegal file name, with the NZ FLAG.
5123
RET C9
RETurn with the NZ FLAG and Register A = 13H.
5124
LD A,C 79
Copy Register C (the character after the name) into Register A.
5125
CP 2FH FE 2F
Compare Register A against 2FH, the / before an extension.
5127
LD E,F2H 1E F2
Load Register E with F2H: Register Pair DE = 51F2H, the extension field. LD does not change the flags.
5129
LD B,03H 06 03
Load Register B with 03H, the most characters of an extension. LD does not change the flags.
512B
If the Z FLAG is set (a /), GOSUB to 5158H to copy the extension from Register Pair HL to Register Pair DE (51F2H), up to 3 characters: Register A comes back as the character after it.
512E
CP 2EH FE 2E
Compare Register A (the character after the name or the extension) against 2EH, the . before a password.
5130
LD E,E2H 1E E2
Load Register E with E2H: Register Pair DE = 51E2H, the password field. LD does not change the flags.
5132
If the Z FLAG is set (a .), GOSUB to 5151H to note the password and copy it from Register Pair HL to Register Pair DE (51E2H), up to 8 characters: Register A comes back as the character after it.
5135
CP 3AH FE 3A
Compare Register A (the character after the fields so far) against 3AH, the : before a drive number.
5137
If the NZ FLAG is set (no drive number), JUMP to 5146H to test the character for !.
5139
LD A,(HL) 7E
Fetch the character after the : from Register Pair HL into Register A: the drive number.
513A
SUB 30H D6 30
SUBtract 30H from Register A: the digit 0-7 becomes the drive 0-7.
513C
LD (4F70H),A 32 70 4F
Store Register A (the drive) at 4F70H, the operand of the LD A at 4F6FH.
513F
AND F8H E6 F8
Mask Register A with F8H: the Z FLAG is set only for the drives 0-7.
5141
LD A,20H 3E 20
Load Register A with 20H, error 32: illegal drive number. LD does not change the flags.
5143
RET NZ C0
If the NZ FLAG is set (not a drive 0-7), RETurn with the NZ FLAG and Register A = 20H.
5144
INC HL 23
INCrement Register Pair HL past the drive number.
5145
LD A,(HL) 7E
Fetch the character after the drive number from Register Pair HL into Register A.
5146
SUB 21H D6 21
SUBtract 21H from Register A (the character that ends the specification): the Z FLAG is set for !.
5148
LD A,08H 3E 08
Load Register A with 08H, bit 3 of byte +01H of the control block. LD does not change the flags.
514A
If the Z FLAG is set (the specification ends with !), JUMP to 514DH to store 08H.
514C
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG set: no ! flag.
514D
LD (50AAH),A 32 AA 50
Store Register A (08H or 00H) at 50AAH, the operand of the OR at 50A9H.
5150
RET C9
RETurn with the Z FLAG: the specification is valid.

5151H - The Password Field

Called when the specification holds a . and so a password: a file created with a password gets the attribute 17H (in use, protection level 7, no access without the password) instead of 10H. Then the password is copied like the name, up to 8 characters.

5151
LD A,17H 3E 17
Load Register A with 17H, the attribute of a new file with a password: in use (bit 4) and protection level 7.
5153
LD (5042H),A 32 42 50
Store Register A (17H) at 5042H, the operand of the LD A at 5041H that @INIT uses for a new file.

5156H - Copy a Field

5156H copies a field of up to 8 characters (the name or the password), 5158H a field of up to Register B characters (the extension), from the text at Register Pair HL to Register Pair DE. The first character must be a capital letter A-Z, the others capital letters or digits; any other character ends the field. Returns Register A = the character after the field and Register Pair HL past it, and Register B = the characters not used.

5156
LD B,08H 06 08
Load Register B with 08H, the most characters of a name or a password.
5158
LD A,(HL) 7E
Fetch the first character of the field from Register Pair HL into Register A.
5159
INC HL 23
INCrement Register Pair HL past the first character of the field.
515A
JUMP to 5165H: the first character must be a letter.
515C
LD A,(HL) 7E
Fetch the next character of the field from Register Pair HL into Register A.
515D
INC HL 23
INCrement Register Pair HL past the character just fetched.
515E
CP 30H FE 30
Compare Register A (the character) against 30H, the digit 0.
5160
RET C D8
If the CARRY FLAG is set (below 0), RETurn: the field ends with Register A = the character.
5161
CP 3AH FE 3A
Compare Register A (the character) against 3AH, the character after the digit 9.
5163
If the CARRY FLAG is set (a digit 0-9), JUMP to 516BH to store it.
5165
CP 41H FE 41
Compare Register A (the character) against 41H, the letter A.
5167
RET C D8
If the CARRY FLAG is set (below A), RETurn: the field ends with Register A = the character.
5168
CP 5BH FE 5B
Compare Register A (the character) against 5BH, the character after the letter Z.
516A
RET NC D0
If the NO CARRY FLAG is set (above Z, a small letter included), RETurn: the field ends with Register A = the character.
516B
LD (DE),A 12
Store Register A (the letter or digit) at Register Pair DE in the field.
516C
INC DE 13
INCrement Register Pair DE to the field's next byte.
516D
DECrement Register B (the characters left) and LOOP BACK to 515CH while it is not 0.
516F
LD A,(HL) 7E
The field is full: fetch the character after it from Register Pair HL into Register A.
5170
INC HL 23
INCrement Register Pair HL past the character after the full field.
5171
RET C9
RETurn with Register A = the character after the field and Register B = 00H.

5172H - Find a Free Directory Record

The Hash Index Table of drive Register C is at 4300H (Register H = 43H). Byte n of the HIT stands for directory record n, which is in sector (n AND 1FH) + 2 of the directory cylinder, so only the bytes whose bits 4-0 are below the number of directory record sectors (the sectors of the cylinder less the allocation table and the HIT) stand for records on the diskette. The search starts at the byte that TIMER$ (4288H) gives and goes up to FFH; when it finds no free byte it searches again from 08H. Returns the Z FLAG with Register Pair HL at a free byte (00H), or the NZ FLAG when every record is taken.

5172
LD A,07H 3E 07
Load Register A with 07H, the offset of byte +07H in a drive code table.
5174
GOSUB to 479CH (DCTBYT): Register A = byte +07H of drive Register C's table: bits 4-0 the highest sector on a track, bits 7-5 the heads less one.
5177
PUSH DE D5
Save Register Pair DE (the caller's) to the stack; Register Pair DE is used for the arithmetic below.
5178
LD D,A 57
Copy Register A (byte +07H of the drive code table) into Register D.
5179
AND 1FH E6 1F
Mask Register A with 1FH to bits 4-0: the highest sector on a track.
517B
LD E,A 5F
Copy Register A (the highest sector) into Register E.
517C
INC E 1C
INCrement Register E: Register E = the sectors on a track.
517D
XOR D AA
Exclusive OR Register D (byte +07H) into Register A: the low bits cancel, leaving bits 7-5, the heads less one.
517E
RLCA 07
Rotate Register A left one bit: the first of three rotations.
517F
RLCA 07
Rotate Register A left one bit again: the second of the three rotations.
5180
RLCA 07
Rotate Register A left one bit a third time: bits 7-5 are now bits 2-0, the heads less one.
5181
INC A 3C
INCrement Register A: Register A = the heads.
5182
GOSUB to 4B6BH (MULTEA) to multiply Register A (the heads) by Register E (the sectors on a track): Register A = the sectors on a cylinder.
5185
LD E,A 5F
Copy Register A (the sectors on a cylinder) into Register E.
5186
LD A,04H 3E 04
Load Register A with 04H, the offset of byte +04H in a drive code table.
5188
GOSUB to 479CH (DCTBYT): Register A = byte +04H of drive Register C's table.
518B
BIT 5,A CB 6F
Test bit 5 of Register A (byte +04H): set when the drive uses both sides of the diskette (4EBAH sets it from the allocation table).
518D
LD A,E 7B
Copy Register E (the sectors on a cylinder) into Register A. LD does not change the flags.
518E
If the Z FLAG is set (one side), JUMP to 5191H.
5190
ADD A,A 87
Double Register A (ADD A,A): both sides, so twice the sectors on the cylinder.
5191
POP DE D1
Restore Register Pair DE (the caller's) from the stack. POP does not change Register A.
5192
SUB 02H D6 02
SUBtract 02H from Register A: less the allocation table (sector 0) and the HIT (sector 1), the number of directory record sectors.
5194
LD (51AAH),A 32 AA 51
Store Register A (the number of directory record sectors) at 51AAH, the operand of the CP at 51A9H.
5197
LD A,(4288H) 3A 88 42
Fetch TIMER$ from 4288H into Register A: the tick count, which picks the byte of the HIT where the search starts.
519A
LD L,A 6F
Copy Register A (the tick count) into Register L: Register Pair HL = the HIT byte to start at (Register H = 43H).
519B
GOSUB to 51A6H to search from Register L to the end of the HIT.
519E
RET Z C8
If the Z FLAG is set (a free record was found), RETurn with Register Pair HL at its HIT byte.
519F
LD L,07H 2E 07
Load Register L with 07H; the INC L below makes it 08H, so the second search starts at byte 08H.
51A1
INC L 2C
INCrement Register L: the next byte of the HIT.
51A2
If the NZ FLAG is set (Register L did not come back to 00H), JUMP to 51A6H to test it.
51A4
OR H B4
OR Register H (43H) into Register A: the NZ FLAG, no free record up to the end of the HIT.
51A5
RET C9
RETurn with the NZ FLAG: from the first search to 519EH, from the second to the caller.
51A6
LD A,L 7D
Copy Register L (the HIT byte) into Register A.
51A7
AND 1FH E6 1F
Mask Register A with 1FH to bits 4-0: the record's sector less 2.
51A9
CP 00H FE 00
Compare Register A against the number of directory record sectors: the operand at 51AAH, 00H in the file, is stored by 5194H. The CARRY FLAG is set when the record's sector is on the cylinder.
51AB
LD A,L 7D
Copy Register L (the HIT byte) into Register A. LD does not change the flags.
51AC
If the CARRY FLAG is set (the record exists on the diskette), JUMP to 51B3H to see if it is free.
51AE
OR 1FH F6 1F
OR 1FH into Register A: the last byte of this group of 32.
51B0
LD L,A 6F
Copy Register A into Register L: the INC L at 51A1H goes on to the first byte of the next group of 32, the next record of sector 2.
51B1
JUMP to 51A1H to step on to the first byte of the next group of 32.
51B3
LD A,(HL) 7E
Fetch the HIT byte at Register Pair HL into Register A: 00H for a free record.
51B4
OR A B7
Set the flags from Register A (OR A): the Z FLAG for a free record.
51B5
RET Z C8
If the Z FLAG is set (a free record), RETurn with Register Pair HL at its HIT byte.
51B6
JUMP to 51A1H to test the next byte of the HIT: this record is taken.

51B8H - Not in the File

The file loads nothing at 51B8H-51BCH; no code of SYS2/SYS addresses these bytes, so they keep whatever was there before SYS2/SYS was loaded.

51B8-51BC
DEFS 5
51B8H-51BCH: Not in the file: 5 bytes between the free record search and the HIT routine. No code of SYS2/SYS reads or writes them.

51BDH - Read or Write the Hash Index Table

51BDH reads, 51BEH writes, sector 1 of the directory cylinder (the HIT) of drive Register C, with the buffer 4300H. The bytes F6H AFH at 51BDH are OR AFH, which leaves the NZ FLAG (read); entered at 51BEH, the AFH alone is XOR A, which leaves the Z FLAG (write). A read accepts only a sector with the deleted data mark of a directory sector (error 16H, HIT read error, when it fails); a write puts the deleted data mark on it and verifies it (error 17H, HIT write error). Returns the Z FLAG on success, else the NZ FLAG with Register A = the error; Register Pair HL = 4300H, Register Pairs BC and DE kept. SYS6/SYS's RENAME calls both entries (its 52A7H, 52B8H).

51BD
OR AFH F6 AF
Read entry: OR AFH into Register A, which leaves the NZ FLAG (AFH is not 00H): read the HIT. 51BEH, the second byte of this instruction, is the write entry: AFH is XOR A, which sets Register A to 00H with the Z FLAG: write the HIT. 4F7BH, 4FA5H and 5055H call 51BDH; 506CH calls 51BEH.
51BF
PUSH BC C5
Save Register Pair BC (Register C = the drive) to the stack.
51C0
PUSH DE D5
Save Register Pair DE (the caller's) to the stack.
51C1
PUSH AF F5
Save Register Pair AF (the Z FLAG for a write, the NZ FLAG for a read) to the stack.
51C2
GOSUB to 4B64H (DIRCYL): Register D = byte +09H of drive Register C's table, the directory cylinder.
51C5
LD E,01H 1E 01
Load Register E with 01H: sector 1 of the directory cylinder, the HIT.
51C7
LD HL,4300H 21 00 43
Point Register Pair HL to 4300H, the system buffer.
51CA
POP AF F1
Restore Register Pair AF from the stack: the Z FLAG for a write, the NZ FLAG for a read.
51CB
If the Z FLAG is set (a write), JUMP to 51D4H.
51CD
GOSUB to 4B45H (RDSSEC) to read cylinder Register D, sector Register E of drive Register C into 4300H, accepted only with the deleted data mark (and found on the directory cylinder the boot sector gives when it is not on Register D). The Z FLAG is set on success.
51D0
LD A,16H 3E 16
Load Register A with 16H, error 22: HIT read error. LD does not change the flags.
51D2
JUMP to 51DEH to restore Register Pairs DE and BC and return.
51D4
GOSUB to 4768H (WRPROT) to write the HIT at 4300H to cylinder Register D, sector Register E of drive Register C with the deleted data mark. The Z FLAG is set on success.
51D7
If the Z FLAG is set (written), GOSUB to 4772H (VERSEC) to read the sector back without storing it: a directory sector gives error 06H (the deleted data mark), the good result.
51DA
CP 06H FE 06
Compare Register A (the error number) against 06H: the Z FLAG is set when the sector was written and read back with its deleted data mark.
51DC
LD A,17H 3E 17
Load Register A with 17H, error 23: HIT write error. LD does not change the flags.
51DE
POP DE D1
Restore the caller's Register Pair DE from the stack.
51DF
POP BC C1
Restore Register Pair BC (Register C = the drive) from the stack.
51E0
RET C9
RETurn: the Z FLAG on success, else the NZ FLAG with Register A = 16H (read) or 17H (write); Register Pair HL = 4300H.

51E1H - The Work Area

The cells where @OPEN and @INIT keep the parts of the specification and the five bytes that start a new directory record. 51E1H-51F8H are not in the file; 51F9H-51FEH are loaded with 00H; 51FFH is the last byte of the 0400H that SYS2/SYS takes up (4BC8H) but is not in the file. SYS6/SYS (RENAME, its 5272H-52B8H) and SYS7/SYS (its 5284H-53AFH) use these cells after an @OPEN.

51E1
DEFS 1
51E1H: Not in the file: the hash code of the file name, 01H-FFH, stored by 4E23H, compared with the HIT by 4F84H and put into the HIT for a new file by 5068H. SYS6/SYS reads and writes it (its 5272H, 5285H).
51E2-51E9
DEFS 8
51E2H-51E9H: Not in the file: the password of the specification, filled with spaces by 5106H and copied by 5132H; 4F66H hashes it. SYS7/SYS reads it (its 5284H, 52A6H, 5306H, 531BH, 535FH).
51EA-51F1
DEFS 8
51EAH-51F1H: Not in the file: the file name of the specification, spaces after its last character (5106H, 5118H); compared with a directory record's +05H-+0CH at 4FC5H and copied into a new record at 5080H. SYS6/SYS uses it (its 5298H, 52B0H).
51F2-51F4
DEFS 3
51F2H-51F4H: Not in the file: the extension of the specification, spaces when none (512BH); it follows the name, so 51EAH-51F4H are the 11 bytes a directory record holds at +05H-+0FH.
51F5-51F6
DEFS 2
51F5H-51F6H: Not in the file: the hash code of the password at 51E2H (4F6CH), compared with 113DH and a record's +10H-+11H (4FE7H) and copied to +10H-+11H of a new record. SYS7/SYS stores and reads it (its 5321H, 539BH, 539FH).
51F7-51F8
DEFS 2
51F7H-51F8H: Not in the file: SYS2/SYS does not set these two bytes; @INIT copies them to +12H-+13H of a new directory record (5083H). SYS7/SYS stores a hash code here (its 530CH) and reads it (its 53ABH, 53AFH).
51F9-51FA
DEFB 00H x 2 00 x 2
51F9H-51FAH: 0000H, copied by @INIT to +14H-+15H of a new directory record: the ending record number of an empty file.
51FB
DEFB 00H 00
51FBH: the attribute: of the file found (4FDDH), or of a new file, 10H or 17H (5043H); @INIT copies it to +00H of a new directory record. 00H in the file.
51FC
DEFB 00H 00
51FCH: 00H, copied to +01H of a new directory record: no flags and no month of a date yet.
51FD
DEFB 00H 00
51FDH: 00H, copied to +02H of a new directory record: no day and year yet.
51FE
DEFB 00H 00
51FEH: 00H, copied to +03H of a new directory record: the end of file byte of an empty file. The last byte the file loads.
51FF
DEFS 1
51FFH: Not in the file: the record length the caller asked for in Register B (4F53H), 00H for 256; it goes to +09H of the control block (50C6H), decides byte mode (509DH) and is copied to +04H of a new directory record.