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

Page Customization

Introduction/Summary

NEWDOS/80 v2.0 SYS2/SYS Disassembly - Open, Load, Allocate and RENAME (Model III)

SYS2/SYS is the overlay that opens files. It loads into the overlay area at 4D00H-51ACH (1,197 bytes, transfer address 4D00H) and is called with LD A,code / RST 28H; the SYS0/SYS dispatcher at 4B67H loads it when 4288H does not already hold its directory slot (04H). Besides the two opens (4424H and 4420H) it loads and runs programs (4430H and 4433H), allocates granules when a file grows (called by SYS0/SYS from inside a read or write), encodes passwords, and does the directory work of RENAME and of running a program named on the command line for SYS1/SYS.

The page shows every byte of the file once, in address order. 51ADH-51FFH, after the end of the file, is the work space of SYS2/SYS (see Variables).

RST 28H Codes Handled by SYS2/SYS

CodeCalled fromFunction
24H4424HOpen an existing file (4E2EH).
44H4420HOpen a file, creating it if it does not exist (4DBDH).
64HSYS0/SYS 4862H, SYS6/SYS 5C53HAllocate granules to a file whose read or write has gone past its last granule (4F82H).
84Hnone foundEncode an 8-character password into its 16-bit code (5155H). SYS2/SYS uses the routine itself at 4E84H.
A4H4430HLoad a program file (4D92H).
C4H4433HLoad a program file and run it, or enter DEBUG first if the DEBUG command was given (4D80H).
E4H, C = 1SYS1/SYS (RENAME)Rename the open file at DE to the filespec at 51E0H (4D32H).
E4H, C = 2none foundFind a free directory entry on drive D and give it a HIT code (50CAH). SYS2/SYS and SYS6/SYS call 50CFH directly.
E4H, C = 3SYS1/SYS 4EADHRun the program named by the command word (4D7BH).
E4H, C = 4none foundCreate a new file; error 35H if it exists (4D72H).
anything elseSYS6/SYS 568DH (C = FFH)Error 2AH (4D2EH). SYS6/SYS uses this to load SYS2/SYS without running anything and then calls 4E21H, 4F2EH and 50CFH itself.

None found: no other file of this DOS was found to load Register A with that code.

How a File Is Opened

  1. Filespec. NAME/EXT.PASSWORD:d is copied to 51CDH-51DFH (5121H): each field starts with a letter and holds letters and digits; lower case is changed to upper case. The drive number may have more than one digit.
  2. Codes. The password is replaced by its 16-bit code (5152H; blank = 4296H). The name and extension give a one-byte hash code (4E8DH): the 11 characters XORed with a left rotate after each; 00H becomes 01H.
  3. Drives. With a drive number only that drive is searched; otherwise drives 0 up to option AL (4E9FH). A drive without a diskette is skipped.
  4. Search. The HIT (directory sector 1) is scanned for bytes equal to the hash code (4EAEH); each such entry is read and must be an FPDE in use with the same name and extension (4EDDH).
  5. Password. With option AA on, the code must match the update password (access FULL) or the access password (the file's access level); otherwise error 19H (4F06H).
  6. FCB. The FCB is rebuilt from the FPDE (4F2EH): open, access level, record mode, FPDE byte 1, buffer, drive, DEC, EOF, record length and the first four extent elements. The EOF middle and high bytes are one lower than in the entry when the low byte is not 0.

The 4420H open does the same and, when the file is not found, takes the first free entry (50CFH) on the drive given, or on the first drive from option AO up that has room, and writes a new FPDE (4DEAH).

How Granules Are Allocated

The sector mapping of SYS0/SYS (47AEH) calls function 64H (at 4862H) when the sector wanted is past the file's last granule, with the DEC of the file's last directory entry at 480FH. SYS2/SYS reads the GAT and looks for free granules from the one after the file's last granule, to the end of the diskette and then from lump 0 (4FC5H). A free granule that follows the last extent element is added to it (up to 32 granules per element); otherwise it starts a new element. When the entry's four elements are used, a new FXDE is created and linked (5058H). After the granules asked for, SYS2/SYS takes up to three more (5018H); they are found the same way as the others and may start a new extent element, and running out of room for them is not an error; CLOSE (SYS3/SYS) releases the granules the file does not use. A file with the ASE=N attribute (bit 7 of FCB+2) gives error 3DH.

SYS6/SYS Uses the Code of SYS2/SYS

SYS6/SYS loads SYS2/SYS through 568DH-5698H (code E4H with C = FFH) and then, for COPY, stores the DEC at 4F56H and the record length at 4F5EH, calls 4E21H and 4F2EH to open a file whose entry it has found itself, and calls 50CFH to take destination directory entries.

Variables

Memory Used by SYS2/SYS

Address
Size
Contents
4D6EH
1 byte
Hash Code (Self-Modifying Code)
Operand of the LD (HL),nn at 4D6DH: the hash code of the name being opened, stored at 4E9BH. 4DDAH points Register Pair HL at it for the HIT byte of a new file.
4E0AH-4E0BH
2 bytes
Drive Range (Self-Modifying Code)
Operand of the LD HL,nnnn at 4E09H, stored at 4E7FH: the drive limit (4E0AH) and the first drive (4E0BH). Read again by the create at 4DC7H.
4E0EH
1 byte
Drive Being Tried (Self-Modifying Code)
Operand of the LD A,nn at 4E0DH, stored at 4DD2H and 4E9FH.
4E75H
1 byte
RENAME Switch (Self-Modifying Code)
Opcode of the JR 4E7FH at 4E75H (18H in the file). RENAME stores 3EH at 4D42H, which turns it into LD A,08H so that 4E77H runs, and 18H back at 4D4AH.
4F24H
1 byte
FPDE Byte 0 (Self-Modifying Code)
Operand of the AND nn at 4F23H, stored at 4EE9H: byte 0 of the FPDE found, whose bits 0-2 are the access level.
4F46H
1 byte
FPDE Byte 1 (Self-Modifying Code)
Operand of the LD (HL),nn at 4F45H, stored at 4E21H: becomes FCB+2.
4F48H-4F49H
2 bytes
Buffer Address (Self-Modifying Code)
Operand of the LD DE,nnnn at 4F47H, stored at 4E31H: becomes FCB+3 and FCB+4.
4F56H
1 byte
DEC (Self-Modifying Code)
Operand of the LD (HL),nn at 4F55H, stored at 4DEAH and 4EDFH, and by SYS6/SYS (56B9H): becomes FCB+7.
4F58H
1 byte
EOF Low Byte (Self-Modifying Code)
Operand of the LD A,nn at 4F57H, stored at 4E27H: becomes FCB+8.
4F5EH
1 byte
Record Length (Self-Modifying Code)
Operand of the LD (HL),nn at 4F5DH, stored at 4E35H, and by SYS6/SYS (56C0H): becomes FCB+9. Read at 4DF2H for byte 4 of a new FPDE and at 4F3CH for the record mode bit.
505EH
1 byte
Entry Being Extended (Self-Modifying Code)
Operand of the LD B,nn at 505DH, stored at 4F90H and 507EH: the DEC of the entry whose extent elements are being filled. Read at 508FH.
51ADH-51CCH
32 bytes
DEC List or Extent Elements
Not in the file. The open (4EAEH) keeps the list of DECs whose HIT byte matches at 51ADH-51CBH, with 51CCH as the place that shows the list is full. The allocation keeps the four extent elements of the entry being extended at 51ADH-51B4H (50B4H) and FEH at 51B5H.
51CDH-51D7H
11 bytes
Name and Extension
Not in the file. The name (8) and extension (3) of the parsed filespec, padded with spaces (4E3DH, 4E44H). Hashed at 4E8DH, compared at 4EF5H, copied into a new FPDE at 4DF8H and into the renamed FPDE at 4D59H.
51D8H-51DFH
8 bytes
Password
Not in the file. The password of the parsed filespec (4E49H). After 5152H encodes it, 51D8H-51D9H and 51DAH-51DBH both hold its 16-bit code (4E87H, 4E8AH), the update and access password codes of a new file.
51E0H-51FFH
32 bytes
Second Filespec
Not in the file. The new filespec of RENAME, put there by SYS1/SYS and parsed at 4D44H.
4300H-43FFH
256 bytes
Directory Sector Buffer
The SYS0/SYS directory routines read the GAT, the HIT and FDE sectors here; also the buffer of the program load (4D92H).
427EH
1 byte
Current Drive
IY-02H. Becomes FCB+6 (4F50H); RENAME searches this drive (4E7BH).
4281H
1 byte
Lumps on the Diskette
IY+01H, byte 1 of the PDRIVE entry of the selected drive. The end of the GAT scan (4FE6H).
4285H
1 byte
Granules per Lump
IY+05H, byte 5 of the PDRIVE entry of the selected drive. Read at 4F9FH, 4FE0H and 5003H.
4289H
1 byte
System Flags
Bit 7 (DEBUG enabled) is tested at 4D88H; bit 6 is cleared by SYS0/SYS 4BC5H at the exits (4D8FH, 503DH).
428BH
1 byte
Allocation Flags
IY+0BH. Bit 0: the extra granules are being taken (5024H, 502AH). Bit 1: no extra granules, set by SYS6/SYS (538EH) and SYS14/SYS (5020H) while they allocate (tested 5018H).
428CH
1 byte
Options
IY+0CH. Bit 7 is option AA, passwords are checked (4F15H).
42BFH
1 byte
Option AL
IY+3FH. The number of drives, the limit of the drive search (4E4EH).
42C1H
1 byte
Option AO
The first drive tried for a new file (4DCFH).
4403H-4404H
2 bytes
Transfer Address
The transfer address of the last program loaded (4DB3H).
480FH
1 byte
Last Entry of the File
The operand of the LD A,nn at SYS0/SYS 480EH, stored by 4845H: the DEC of the last directory entry of the file being extended (4F8DH).

Outside Routines Called

AddressContents
4409HSYS0/SYS: the error exit that displays the error and returns to DOS READY (4D84H).
440DHSYS0/SYS: DEBUG, entered with the transfer address on the stack (4D8CH).
4548HSYS0/SYS: change Register A to upper case (5148H).
471BH, 4723HSYS0/SYS: select the drive of the FCB (4F88H) or the drive in Register A (50CBH).
4791HSYS0/SYS: select the drive in Register A and check for a turning diskette (4DD5H, 4EA2H).
48AFHSYS0/SYS: read directory sector Register A into 4300H (4D66H, 4EBAH, 4FC7H, 50D2H).
48C4HSYS0/SYS: write the directory sector in 4300H (4D61H, 4D6FH, 4E00H, 50AEH, 5101H).
48D4H, 48DBHSYS0/SYS: read the FDE sector of the DEC in Register A, if needed (4EE2H, 5033H) or always (4F94H, 5106H).
48F0HSYS0/SYS: read the FPDE of the open FCB at Index Register IX (4D50H).
492BHSYS0/SYS: run the rest of the routine as a DOS function with Index Register IX = the FCB and Index Register IY = 4280H, entered after the open check that 4925H makes (4DC4H, 4E2EH).
4972HSYS0/SYS: the error exit of a DOS function (5040H).
4BC5HSYS0/SYS: clear bit 6 of 4289H (4D8FH, 503DH).
4BCDHSYS0/SYS: load a program file through the FCB at Register Pair HL (4DB0H).
4C7AHSYS0/SYS: check what follows a filespec on the command line (4D7BH).

Disassembly

4D00H - Function Dispatcher

SYS2/SYS is entered here by the SYS0/SYS RST 28H dispatcher (4B67H) with Register A = the RST 28H code and the caller's Register Pairs BC, DE and HL unchanged. Bits 5-7 of the code are the function number (bits 0-4 = 04H, SYS2/SYS's directory slot): 24H open an existing file (4424H), 44H open a new or existing file (4420H), 64H allocate granules (SYS0/SYS 4862H, inside the allocate routine 47AEH), 84H encode a password, A4H load a program (4430H), C4H load and run a program (4433H). Function 7 (E4H) takes a sub-function in Register C: 1 RENAME (SYS1/SYS), 2 find a free directory entry on drive D, 3 run the program named on the command line (SYS1/SYS 4EADH), 4 create a new file. Any other code or sub-function returns error 2AH; SYS6/SYS (568DH) relies on this to load SYS2/SYS with Register C = FFH and then call into it.

4D00
CP 24H FE 24
Compare Register A (the RST 28H code) against 24H, function 1: open an existing file (the 4424H vector).
4D02
If the Z FLAG is set (Register A = 24H), JUMP to 4E2EH to open the existing file named in the FCB at Register Pair DE.
4D05
CP 44H FE 44
Compare Register A (the RST 28H code) against 44H, function 2: open a new or existing file (the 4420H vector).
4D07
If the Z FLAG is set (Register A = 44H), JUMP to 4DBDH.
4D0A
CP 64H FE 64
Compare Register A (the RST 28H code) against 64H, function 3: allocate granules (SYS0/SYS calls it at 4862H through 4982H when a write goes past the last allocated granule).
4D0C
If the Z FLAG is set (Register A = 64H), JUMP to 4F82H.
4D0F
CP 84H FE 84
Compare Register A (the RST 28H code) against 84H, function 4: encode a password.
4D11
If the Z FLAG is set (Register A = 84H), JUMP to 5155H with Register Pair HL pointing to the last of the 8 password characters.
4D14
CP A4H FE A4
Compare Register A (the RST 28H code) against A4H, function 5: load a program (the 4430H vector).
4D16
If the Z FLAG is set (Register A = A4H), JUMP to 4D92H.
4D18
CP C4H FE C4
Compare Register A (the RST 28H code) against C4H, function 6: load and run a program (the 4433H vector).
4D1A
If the Z FLAG is set (Register A = C4H), JUMP to 4D80H.
4D1C
CP E4H FE E4
Compare Register A (the RST 28H code) against E4H, function 7, whose sub-function is in Register C.
4D1E
If the NZ FLAG is set (any other code), JUMP to 4D2EH to return error 2AH.
4D20
DEC C 0D
Function 7: DECrement Register C (the sub-function). The Z FLAG is set for sub-function 1.
4D21
If the Z FLAG is set (Register C was 1, RENAME from SYS1/SYS), JUMP to 4D32H.
4D23
DEC C 0D
DECrement Register C (the sub-function) again. The Z FLAG is set for sub-function 2.
4D24
If the Z FLAG is set (Register C was 2, find a free directory entry on drive D), JUMP to 50CAH.
4D27
DEC C 0D
DECrement Register C (the sub-function) again. The Z FLAG is set for sub-function 3.
4D28
If the Z FLAG is set (Register C was 3, run the program named on the command line, from SYS1/SYS 4EADH), JUMP to 4D7BH.
4D2A
DEC C 0D
DECrement Register C (the sub-function) again. The Z FLAG is set for sub-function 4.
4D2B
If the Z FLAG is set (Register C was 4, create a new file), JUMP to 4D72H. Any other sub-function (0, 5 and up, and the FFH that SYS6/SYS uses to load SYS2/SYS) falls through to 4D2EH.

4D2EH - Return Error 2AH

Reached from the dispatcher for an unknown function code or function 7 sub-function. RENAME also uses 4D30H to return error 25H.

4D2E
LD A,2AH 3E 2A
Load Register A with error code 2AH (ILLEGAL DOS FUNCTION).
4D30
OR A B7
OR Register A (the error code, 2AH or 25H from 4D3DH) with itself to set the NZ FLAG, since Register A is not zero.
4D31
RET C9
RETurn to the caller of the RST 28H with the error code in Register A and the NZ FLAG set.

4D32H - Function E4H, C = 1: RENAME

Called by SYS1/SYS for the RENAME command (manual section 2.43) after it has opened the old file. Register Pair DE points to the open FCB of the old file; SYS1/SYS has put the new filespec at 51E0H. The new name is looked for on the drive of the old file only (4E75H is patched so that 4E77H-4E7EH run); if it exists the result is error 35H. Otherwise the new name and extension are written into the old file's FPDE and the new name's hash code into its HIT byte. The password and everything else in the entry are kept.

4D32
PUSH DE D5
Save Register Pair DE (the FCB address) onto the stack.
4D33
POP IX DD E1
Restore it into Index Register IX: Index Register IX = the FCB of the old file, for SYS0/SYS 48F0H below.
4D35
INC DE 13
INCrement Register Pair DE to FCB+1, the access level byte.
4D36
LD A,(DE) 1A
Fetch FCB+1 at Register Pair DE into Register A.
4D37
AND 07H E6 07
AND Register A (FCB+1) with 07H, keeping bits 0-2: the access level the file was opened with.
4D39
CP 03H FE 03
Compare Register A (the access level) against 03H. RENAME needs level 0 (FULL), 1 (KILL) or 2 (RENAME): the CARRY FLAG is set for those.
4D3B
LD A,25H 3E 25
Load Register A with error code 25H (ILLEGAL ACCESS TRIED TO PROTECTED FILE). LD does not change the flags.
4D3D
If the NO CARRY FLAG is set (level 3 or more), JUMP to 4D30H to return error 25H in Register A.
4D3F
LD HL,4E75H 21 75 4E
Point Register Pair HL to 4E75H, the JR 4E7FH in the open routine.
4D42
LD (HL),3EH 36 3E
Store 3EH at Register Pair HL (4E75H): the JR becomes LD A,08H and the open falls into the RENAME code at 4E77H (the new name must not have a drive number and is looked for on the current drive only).
4D44
LD DE,51E0H 11 E0 51
Point Register Pair DE to 51E0H, the new filespec put there by SYS1/SYS.
4D47
GOSUB to 4E2CH to parse the new filespec at Register Pair DE and look for it (open existing with Register B = 0). This fills in 51CDH-51D7H (name and extension) and 4D6EH (the name's hash code). Register Pair HL (4E75H) is kept.
4D4A
LD (HL),18H 36 18
Store 18H (the JR opcode) back at Register Pair HL (4E75H).
4D4C
If the Z FLAG is set (the new name exists), JUMP to 4D77H to return error 35H (FILE ALREADY EXISTS).
4D4E
CP 18H FE 18
Compare Register A (the open's error) against 18H (FILE NOT IN DIRECTORY), the result wanted.
4D50
If the Z FLAG is set (not found), GOSUB to 48F0H (SYS0/SYS) to read the old file's FPDE through the FCB at Index Register IX: Register Pair HL = the entry.
4D53
RET NZ C0
If the NZ FLAG is set (any other error, Register A = its code), RETurn with it.
4D54
LD A,L 7D
Load Register A with Register L, the low byte of the FPDE address in 4300H.
4D55
ADD A,05H C6 05
ADD 05H to Register A: the offset of the name in the entry.
4D57
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte 5 of the FPDE, the name.
4D58
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the name in the FPDE.
4D59
LD HL,51CDH 21 CD 51
Point Register Pair HL to 51CDH, the new name and extension (8 + 3 characters, padded with spaces).
4D5C
LD BC,000BH 01 0B 00
Load Register Pair BC with 000BH, the 11 bytes to copy.
4D5F
LDIR ED B0
Copy Register Pair BC (11) bytes from Register Pair HL (51CDH) to Register Pair DE: the new name and extension go into bytes 5-15 of the FPDE.
4D61
GOSUB to 48C4H (SYS0/SYS) to write the directory sector in 4300H back with the directory mark.
4D64
LD A,01H 3E 01
Load Register A with 01H, directory sector 1, the HIT. LD does not change the flags.
4D66
If the Z FLAG is set (the write was good), GOSUB to 48AFH (SYS0/SYS) to read the HIT (Register A = 01H) into 4300H.
4D69
RET NZ C0
If the NZ FLAG is set (Register A = the write or read error), RETurn with it.
4D6A
LD L,(IX+07H) DD 6E 07
Load Register L with the file's DEC from (IX+07H) (Index Register IX = the FCB), FCB+7: Register Pair HL = 4300H + DEC, the file's HIT byte.
4D6D
LD (HL),00H 36 00
Store the new name's hash code at Register Pair HL, the file's HIT byte. Self-Modifying Code
The operand at 4D6EH is the hash code, stored by 4E9BH.
4D6F
JUMP to 48C4H (SYS0/SYS) to write the HIT back; it RETurns to the caller with its result.

4D72H - Function E4H, C = 4: Create a New File

Opens a file as the 4420H function does, but returns error 35H if the file already exists. Register Pair DE points to the FCB holding the filespec, Register Pair HL to the buffer, Register B = the record length.

4D72
GOSUB to 4DBDH to open the file named in the FCB at Register Pair DE, creating it if it does not exist. The CARRY FLAG returns set if it was created.
4D75
RET NZ C0
If the NZ FLAG is set (Register A = an error), RETurn with it.
4D76
RET C D8
If the CARRY FLAG is set (the file was created), RETurn with Register A = 00H and the Z FLAG.
4D77
LD A,35H 3E 35
The file already existed: load Register A with error code 35H (FILE ALREADY EXISTS). Also reached from RENAME (4D4CH).
4D79
OR A B7
OR Register A (35H) with itself to set the NZ FLAG.
4D7A
RET C9
RETurn to the caller of the RST 28H with error 35H (FILE ALREADY EXISTS) in Register A and the NZ FLAG set.

4D7BH - Function E4H, C = 3: Run the Program Named on the Command Line

SYS1/SYS calls this (its 4EADH, code E443H) for a command word that is not a library command: the word has been put into the FCB at Register Pair DE as a filespec with the default extension /CMD, and Register Pair HL points to the command line after it. The program is loaded and run with Register Pair HL pointing to the rest of the command line.

4D7B
GOSUB to 4C7AH (SYS0/SYS) to check what follows the filespec at Register Pair HL: the Z FLAG at the end of the line (0DH), NO CARRY after a comma or spaces (Register Pair HL moved past them), the CARRY FLAG with Register A = 34H for anything else.
4D7E
RET C D8
If the CARRY FLAG is set (something else follows the name), RETurn with error 34H (ILLEGAL KEYWORD OR SEPARATOR OR TERMINATOR) in Register A.
4D7F
POP AF F1
Restore Register Pair AF from the stack to drop the return address into the SYS0/SYS dispatcher (4BC5H), so that 4D80H puts the command line pointer in place of SYS1/SYS's return address. Fall into 4D80H.

4D80H - Function C4H: Load and Run a Program (4433H)

Register Pair DE points to the FCB holding the program's filespec. The program is loaded (4D92H) and entered at its transfer address with Register Pair HL = the caller's HL (from 4D7BH, the command line pointer). If 4289H bit 7 is set (the DEBUG command was given), DEBUG is entered instead, with the transfer address on the stack as the program counter.

4D80
EX (SP),HL E3
Exchange Register Pair HL (the caller's HL) with the word on top of the stack (the Stack Pointer's target): it takes the place of the dispatcher's return address (4BC5H), or, from 4D7BH, of SYS1/SYS's return address.
4D81
GOSUB to 4D92H to load the program named in the FCB at Register Pair DE: Register Pair HL = its transfer address.
4D84
If the NZ FLAG is set (Register A = the load error), JUMP to 4409H, the SYS0/SYS error exit, which displays the error and returns to DOS READY.
4D87
EX (SP),HL E3
Exchange Register Pair HL (the transfer address) with the word on top of the stack (the Stack Pointer's target): the transfer address goes on the stack and Register Pair HL gets the caller's HL (the command line pointer) back.
4D88
LD A,(4289H) 3A 89 42
Fetch the DOS flags at 4289H into Register A (bit 7 = DEBUG enabled by the DEBUG command).
4D8B
RLCA 07
Rotate Register A (4289H) left: bit 7 goes into the CARRY FLAG.
4D8C
If the CARRY FLAG is set (DEBUG enabled), JUMP to 440DH (DEBUG) with the transfer address on the stack as the program counter.
4D8F
JUMP to 4BC5H (SYS0/SYS), which clears bit 6 of 4289H (overlay function running) and RETurns: the RET goes to the transfer address on the stack, with Register Pair HL = the command line pointer.

4D92H - Function A4H: Load a Program (4430H)

Register Pair DE points to the FCB holding the program's filespec. The file is opened with 4300H as the buffer and loaded by the SYS0/SYS loader (4BCDH). The access level must allow EXEC (level 6 or below); for the load it is set to READ and put back afterwards. Returns Register Pair HL = the transfer address (also stored at 4403H-4404H) and the Z FLAG, or an error.

4D92
LD HL,4300H 21 00 43
Point Register Pair HL to 4300H, the buffer for the open (the SYS0/SYS loader reads through 4300H).
4D95
GOSUB to 4E2CH to open the file named in the FCB at Register Pair DE with Register B = 0 (256-byte records).
4D98
If the Z FLAG is set (the file is open), JUMP to 4DA0H.
4D9A
CP 18H FE 18
Compare Register A (the open's error) against 18H (FILE NOT IN DIRECTORY).
4D9C
RET NZ C0
If the NZ FLAG is set (any other error), RETurn with it in Register A.
4D9D
ADD A,07H C6 07
ADD 07H to Register A: error 18H becomes 1FH (PROGRAM NOT FOUND).
4D9F
RET C9
RETurn with error 1FH in Register A and the NZ FLAG.
4DA0
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the FCB.
4DA1
INC HL 23
INCrement Register Pair HL to FCB+1, the access level byte.
4DA2
LD A,(HL) 7E
Fetch FCB+1 at Register Pair HL into Register A (access level in bits 0-2).
4DA3
PUSH AF F5
Save Register Pair AF (Register A = the original FCB+1) onto the stack.
4DA4
PUSH HL E5
Save Register Pair HL (the pointer to FCB+1) onto the stack.
4DA5
AND 07H E6 07
AND Register A (FCB+1) with 07H, keeping the access level.
4DA7
LD B,A 47
Copy Register A (the access level) into Register B.
4DA8
LD A,06H 3E 06
Load Register A with 06H, level EXEC, the highest level that may be loaded.
4DAA
CP B B8
Compare Register A (6) against Register B (the file's level). The CARRY FLAG is set for level 7 (LOCK), which may not be loaded.
4DAB
LD A,25H 3E 25
Load Register A with error code 25H (ILLEGAL ACCESS TRIED TO PROTECTED FILE), kept if the load is skipped. LD does not change the flags.
4DAD
LD (HL),2DH 36 2D
Store 2DH as FCB+1 at Register Pair HL: buffer empty (bit 5), v2 format (bit 3), access level 5 (READ), so that the loader may read the file.
4DAF
DEC HL 2B
DECrement Register Pair HL back to FCB+0, so that Register Pair HL again points to the start of the FCB.
4DB0
If the NO CARRY FLAG is set (level 0-6), GOSUB to 4BCDH (SYS0/SYS) to load the file through the FCB at Register Pair HL. It returns Register Pair HL = the transfer address and the Z FLAG, or an error with the NZ FLAG.
4DB3
LD (4403H),HL 22 03 44
Store Register Pair HL (the transfer address) at 4403H-4404H, the DOS-CALL parameter word.
4DB6
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the transfer address.
4DB7
POP HL E1
Restore Register Pair HL from the stack: the pointer to FCB+1.
4DB8
POP BC C1
Restore Register Pair BC from the stack: Register B = the original FCB+1 (pushed as Register A at 4DA3H).
4DB9
LD (HL),B 70
Store Register B (the original FCB+1) back at Register Pair HL.
4DBA
DEC HL 2B
DECrement Register Pair HL back to FCB+0, so that Register Pair HL again points to the start of the FCB.
4DBB
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the transfer address, Register Pair DE = the FCB.
4DBC
RET C9
RETurn with the loader's result (Register A and the flags).

4DBDH - Function 44H: Open a New or Existing File (4420H)

Manual section 3.4. Register Pair DE points to the FCB holding the filespec, Register Pair HL to the buffer, Register B = the record length (0 = 256). The file is looked for first (4E2EH). If it does not exist, a free directory entry is taken on the drive given in the filespec, or, without a drive number, on the first drive from option AO (42C1H) up that has room, and a new FPDE is written: in use, access FULL, EOF 0, no granules, the password's code as both passwords. Returns the Z FLAG with the CARRY FLAG set if the file was created, NO CARRY if it existed.

4DBD
GOSUB to 4E2EH to open the file named in the FCB at Register Pair DE if it exists.
4DC0
RET Z C8
If the Z FLAG is set (opened), RETurn with the Z FLAG and NO CARRY: the file existed.
4DC1
CP 18H FE 18
Compare Register A (the open's error) against 18H (FILE NOT IN DIRECTORY).
4DC3
RET NZ C0
If the NZ FLAG is set (any other error), RETurn with it in Register A.
4DC4
GOSUB to 492BH (SYS0/SYS): the rest of this routine runs as a DOS function with Index Register IX = the FCB and Index Register IY = 4280H; on return all registers except AF are restored, and an error exit (4972H) returns from here to the caller.
4DC7
LD BC,(4E0AH) ED 4B 0A 4E
Load Register Pair BC from 4E0AH-4E0BH, stored by the open: Register C = the drive limit, Register B = the first drive. They are equal when the filespec had a drive number.
4DCB
LD A,B 78
Load Register A with Register B, the first drive.
4DCC
CP C B9
Compare Register A (the first drive) against Register C (the limit). The Z FLAG is set when a drive number was given.
4DCD
If the Z FLAG is set (a drive number was given), JUMP to 4DD2H to use that drive.
4DCF
LD A,(42C1H) 3A C1 42
No drive number: fetch option AO from 42C1H into Register A, the first drive tried for a new file.

Drive Loop
Register A = the drive to try.

4DD2
LD (4E0EH),A 32 0E 4E
Store Register A (the drive) at 4E0EH, the operand of the LD A,nn at 4E0DH (the drive being tried).
4DD5
GOSUB to 4791H (SYS0/SYS) to select the drive in Register A and check that it holds a turning diskette.
4DD8
If the NZ FLAG is set (not ready), JUMP to 4DE3H to try the next drive.
4DDA
LD HL,4D6EH 21 6E 4D
Point Register Pair HL to 4D6EH, the name's hash code.
4DDD
LD B,(HL) 46
Load Register B with the hash code at Register Pair HL: the free entry search starts in the FDE sector it selects.
4DDE
GOSUB to 50CFH to find a free directory entry, put the hash code at Register Pair HL (4D6EH) into its HIT byte and clear the entry. Register Pair HL returns the entry, Register A its DEC.
4DE1
If the Z FLAG is set (an entry was found), JUMP to 4DEAH.
4DE3
LD E,1AH 1E 1A
Load Register E with error code 1AH (DIRECTORY SPACE FULL), returned if no drive has room.
4DE5
GOSUB to 4E09H: it returns Register A = the next drive, or returns the error to our caller.
4DE8
LOOP BACK to 4DD2H to try the next drive in Register A.

4DEAH - Write the New FPDE

Register Pair HL points to the cleared directory entry (bytes 0-21 are 00H, 22-31 FFH), Register A = its DEC.

4DEA
LD (4F56H),A 32 56 4F
Store Register A (the new entry's DEC) at 4F56H, from where it goes to FCB+7.
4DED
LD (HL),10H 36 10
Store 10H as byte 0 of the entry at Register Pair HL: an FPDE in use, access level 0 (FULL).
4DEF
GOSUB to 4E1FH to take byte 1 (00H) and the EOF low byte (00H) into the FCB building code. Register Pair HL returns pointing to byte 4.
4DF2
LD A,(4F5EH) 3A 5E 4F
Fetch the record length from 4F5EH (stored by the open) into Register A.
4DF5
LD (HL),A 77
Store Register A (the record length) as byte 4 of the entry at Register Pair HL (LRL).
4DF6
INC HL 23
INCrement Register Pair HL to byte 5 of the entry, the name.
4DF7
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = byte 5 of the entry.
4DF8
LD HL,51CDH 21 CD 51
Point Register Pair HL to 51CDH: name, extension, and the password code twice (51D8H-51DBH).
4DFB
LD BC,000FH 01 0F 00
Load Register Pair BC with 000FH, the 15 bytes to copy.
4DFE
LDIR ED B0
Copy Register Pair BC (15) bytes from Register Pair HL (51CDH) to Register Pair DE: bytes 5-19 = name, extension, update password code, access password code. Register Pair DE ends at byte 20.
4E00
GOSUB to 48C4H (SYS0/SYS) to write the directory sector in 4300H.
4E03
RET NZ C0
If the NZ FLAG is set (Register A = the write error), RETurn with it.
4E04
GOSUB to 4F2EH to build the open FCB with access level 0 (FULL); Register Pair DE points to the EOF middle and high bytes of the entry.
4E07
SCF 37
Set the CARRY FLAG with SCF: it tells the caller of function 44H that the file was created, not found.
4E08
RET C9
RETurn with Register A = 00H, the Z FLAG and the CARRY FLAG.

4E09H - Try the Next Drive or Return the Error

Called by the drive loops of the open (4EA9H) and of the create (4DE5H) with Register E = the error to give if no drive is left (18H or 1AH) and Register A = the error of this drive (or 0). Returns Register A = the next drive if there is one. Otherwise it discards its return address and RETurns from the routine that called it with an error: when all drives have been tried, the one in Register E; when a drive number was given, the drive's own error if there was one, else the one in Register E.

4E09
LD HL,0000H 21 00 00
Load Register Pair HL with the drive range (0000H in the file). Self-Modifying Code
The operand at 4E0AH-4E0BH is stored at 4E7FH: Register L = the limit (the number of drives, option AL, or the drive number given), Register H = the first drive.
4E0C
LD D,A 57
Copy Register A (this drive's error, or 0) into Register D.
4E0D
LD A,00H 3E 00
Load Register A with the drive just tried. Self-Modifying Code
The operand at 4E0EH is stored at 4DD2H and 4E9FH.
4E0F
INC A 3C
INCrement Register A (the drive just tried) to the number of the next drive.
4E10
CP L BD
Compare Register A (the next drive) against Register L (the limit). The CARRY FLAG is set while drives are left.
4E11
RET C D8
If the CARRY FLAG is set (below the limit), RETurn with the next drive in Register A.
4E12
POP HL E1
Restore Register Pair HL from the stack to drop the return address: the RET below returns from the caller.
4E13
If the Z FLAG is set (the next drive is the limit: every drive was tried), JUMP to 4E18H to return the error in Register E.
4E15
LD A,D 7A
A drive number was given (the next drive is past it): load Register A with Register D, that drive's error.
4E16
OR A B7
OR Register A (the drive's error) with itself to set the flags.
4E17
RET NZ C0
If the NZ FLAG is set (the drive had an error), RETurn with it in Register A.
4E18
LD A,E 7B
Load Register A with Register E, the error to give (18H or 1AH).
4E19
OR A B7
OR Register A (the error) with itself to set the NZ FLAG.
4E1A
RET C9
RETurn from the caller with the error in Register A.

4E1BH - Return Error 20H

Reached for a bad drive number in the filespec: not a digit, over 255, or (for RENAME) given at all.

4E1B
LD A,20H 3E 20
Load Register A with error code 20H (ILLEGAL OR MISSING DRIVE #).
4E1D
OR A B7
OR Register A (20H) with itself to set the NZ FLAG.
4E1E
RET C9
RETurn from the open with error 20H in Register A.

4E1FH - Take Byte 1 and the EOF Low Byte of an Entry

Register Pair HL points to byte 0 of a directory entry. Byte 1 is stored at 4F46H (it becomes FCB+2) and byte 3 (the EOF low byte) at 4F58H (it becomes FCB+8). Returns Register Pair HL pointing to byte 4. SYS6/SYS calls 4E21H (56C4H) with Register Pair HL = byte 1 and Register A = 00H, which it stores instead of byte 1.

4E1F
INC HL 23
INCrement Register Pair HL to byte 1 of the entry.
4E20
LD A,(HL) 7E
Fetch byte 1 at Register Pair HL into Register A (bit 7 no extend, bit 6 no deallocate, bit 5 updated).
4E21
LD (4F46H),A 32 46 4F
Store Register A (byte 1, or SYS6/SYS's 00H) at 4F46H, the operand of the LD (HL),nn that sets FCB+2.
4E24
INC HL 23
INCrement Register Pair HL to byte 2 of the entry.
4E25
INC HL 23
INCrement Register Pair HL to byte 3, the EOF low byte.
4E26
LD A,(HL) 7E
Fetch the EOF low byte at Register Pair HL into Register A.
4E27
LD (4F58H),A 32 58 4F
Store Register A (the EOF low byte) at 4F58H, the operand of the LD A,nn that sets FCB+8.
4E2A
INC HL 23
INCrement Register Pair HL to byte 4 of the entry, the LRL.
4E2B
RET C9
RETurn with Register Pair HL pointing to byte 4.

4E2CH - Function 24H: Open an Existing File (4424H)

Manual section 3.4. Register Pair DE points to the FCB holding the filespec NAME/EXT.PASSWORD:d, Register Pair HL to the 256-byte buffer, Register B = the record length (0 = 256; the entry at 4E2CH sets 0). The filespec is parsed into 51CDH-51DFH, the password encoded, the hash code of the name worked out, and the directory of each drive searched (4EAEH) until the file is found. The FCB is then built from the FPDE (4F2EH). Errors: 30H bad filespec, 20H bad drive number, 18H not found, 19H wrong password, or the disk error of a drive that was named in the filespec.

4E2C
LD B,00H 06 00
Load Register B with 00H: 256-byte records. RENAME (4D47H) and the program load (4D95H) enter here; the open function itself (4D02H) and 4DBDH enter at 4E2EH with the caller's record length in Register B.
4E2E
GOSUB to 492BH (SYS0/SYS): the rest of the open runs as a DOS function with Index Register IX = the FCB (the caller's Register Pair DE) and Index Register IY = 4280H, whether the FCB is open or not.
4E31
LD (4F48H),HL 22 48 4F
Store Register Pair HL (the buffer address) at 4F48H, the operand of the LD DE,nnnn at 4F47H that sets FCB+3 and FCB+4.
4E34
LD A,B 78
Copy Register B (the record length, 0 = 256) into Register A.
4E35
LD (4F5EH),A 32 5E 4F
Store Register A (the record length) at 4F5EH, the operand of the LD (HL),nn at 4F5DH that sets FCB+9.
4E38
LD HL,51CDH 21 CD 51
Point Register Pair HL to 51CDH, where the 8-character name of the parsed filespec goes.
4E3B
DEC DE 1B
DECrement Register Pair DE (the FCB holding the filespec) to one byte before the filespec, because 5146H moves Register Pair DE on before each fetch.
4E3C
XOR A AF
Set Register A to 0 with XOR, which also sets the Z FLAG: for 5121H the Z FLAG means that the field must be present.
4E3D
GOSUB to 5121H to copy the name (1 to 8 characters) from Register Pair DE to 51CDH-51D4H at Register Pair HL, padded with spaces. Register A returns the character after the name; Register Pair HL returns 51D5H.
4E40
CP 2FH FE 2F
Compare Register A (the character after the name) against 2FH (/). The Z FLAG is set when an extension follows.
4E42
LD B,03H 06 03
Load Register B with 03H, the length of the extension field. LD does not change the flags.
4E44
GOSUB to 5123H with Register B = 3: with the Z FLAG (a / was found) the extension is copied to 51D5H-51D7H, otherwise 51D5H-51D7H are filled with spaces. Register A returns the next character, Register Pair HL returns 51D8H.
4E47
CP 2EH FE 2E
Compare Register A (the next character) against 2EH (.). The Z FLAG is set when a password follows.
4E49
GOSUB to 5121H (Register B = 8): with the Z FLAG the password is copied to 51D8H-51DFH, otherwise 51D8H-51DFH are filled with spaces. Register A returns the next character.
4E4C
LD B,00H 06 00
Load Register B with 00H, the first drive to search when no drive number is given.
4E4E
LD C,(IY+3FH) FD 4E 3F
Load Register C with (IY+3FH): Index Register IY = 4280H, so this is 42BFH, option AL, the number of drives. Register C is the limit of the drive search.
4E51
CP 3AH FE 3A
Compare Register A (the character after the filespec) against 3AH (:). The Z FLAG is set when a drive number follows.
4E53
If the NZ FLAG is set (no drive number), JUMP to 4E75H with Register B = 0 and Register C = the number of drives.
4E55
INC DE 13
INCrement Register Pair DE to the character after the colon.
4E56
LD A,(DE) 1A
Fetch the character at Register Pair DE into Register A, the first digit of the drive number.
4E57
SUB 30H D6 30
SUBtract 30H from Register A to turn the ASCII digit into its value.
4E59
CP 0AH FE 0A
Compare Register A against 0AH (10). The NO CARRY FLAG is set when the character was not a digit 0-9.
4E5B
If the NO CARRY FLAG is set (no digit after the colon), JUMP to 4E1BH to return error 20H (ILLEGAL OR MISSING DRIVE #).

Drive Number Loop
Register A = the value of the drive number so far, Register Pair DE = its last digit.

4E5D
LD C,A 4F
Copy Register A (the drive number so far) into Register C.
4E5E
INC DE 13
INCrement Register Pair DE to the next character of the drive number.
4E5F
LD A,(DE) 1A
Fetch the character at Register Pair DE into Register A.
4E60
SUB 30H D6 30
SUBtract 30H from Register A to turn the ASCII digit into its value.
4E62
CP 0AH FE 0A
Compare Register A against 0AH (10). The NO CARRY FLAG is set when the character is not a digit 0-9.
4E64
If the NO CARRY FLAG is set (the drive number has ended), JUMP to 4E74H with the drive number in Register C.
4E66
LD L,A 6F
Copy Register A (the new digit) into Register L to keep it during the multiply.
4E67
LD A,C 79
Copy Register C (the drive number so far) into Register A.
4E68
LD B,09H 06 09
Load Register B with 09H: Register C is added to Register A nine more times, giving the number times 10.
4E6A
ADD A,C 81
ADD Register C (the drive number so far) to Register A.
4E6B
If the CARRY FLAG is set (the product is over 255), JUMP to 4E1BH to return error 20H.
4E6D
DECrement Register B (the additions left) and LOOP BACK to 4E6AH until it is 0. Register A = the drive number so far times 10.
4E6F
ADD A,L 85
ADD Register L (the new digit) to Register A.
4E70
If the NO CARRY FLAG is set (the result is 255 or less), LOOP BACK to 4E5DH to keep it in Register C and fetch the next character.
4E72
The drive number is over 255: JUMP to 4E1BH to return error 20H.
4E74
LD B,C 41
A drive number was given: copy Register C (the drive number) into Register B, so that the first drive and the limit are the same and only that drive is searched. Whether the drive exists is checked by the drive select at 4791H (SYS0/SYS).
4E75
JUMP to 4E7FH with Register B = the first drive and Register C = the limit. Self-Modifying Code
RENAME stores 3EH here (4D42H) and 18H back (4D4AH): while it is 3EH this is LD A,08H and the open goes on into 4E77H.

4E77H - RENAME Only: Use the Old File's Drive

Reached only while 4E75H holds LD A,08H, during RENAME. The new name may not have a drive number, and it is looked for on the drive the old file is on.

4E77
LD A,C 79
Copy Register C (the drive limit) into Register A.
4E78
CP B B8
Compare Register A (the drive limit) against Register B (the first drive). They are the same only when the new filespec has a drive number (4E74H).
4E79
If the Z FLAG is set (the new name has a drive number), JUMP to 4E1BH to return error 20H.
4E7B
LD B,(IY-02H) FD 46 FE
Load Register B with (IY-02H): Index Register IY = 4280H, so this is 427EH, the current drive, which still holds the drive of the old file selected when SYS1/SYS opened it.
4E7E
LD C,B 48
Copy Register B (that drive) into Register C, so that the first drive and the limit are the same and only that drive is searched.

4E7FH - Encode the Password and Hash the Name

Stores the drive range for 4E09H, replaces the password with its 16-bit code (51D8H-51DBH) and works out the hash code of the 11 name and extension characters, which is kept at 4D6EH.

4E7F
LD (4E0AH),BC ED 43 0A 4E
Store Register Pair BC at 4E0AH-4E0BH, the operand of the LD HL,nnnn at 4E09H: 4E0AH = Register C (the drive limit), 4E0BH = Register B (the first drive).
4E83
PUSH BC C5
Save Register Pair BC onto the stack: Register B (the first drive) comes back as Register A at 4E9EH.
4E84
GOSUB to 5152H to encode the 8 password characters at 51D8H-51DFH. Register Pair HL returns the 16-bit code; the 8 characters are set to spaces.
4E87
LD (51D8H),HL 22 D8 51
Store Register Pair HL (the password code) at 51D8H-51D9H. For a new file (4DF8H) this becomes the update password code, bytes 16-17 of the FPDE.
4E8A
LD (51DAH),HL 22 DA 51
Store Register Pair HL (the password code) at 51DAH-51DBH as well. For a new file this becomes the access password code, bytes 18-19 of the FPDE.
4E8D
LD HL,51CDH 21 CD 51
Point Register Pair HL to 51CDH, the 8-character name followed by the 3-character extension.
4E90
LD B,0BH 06 0B
Load Register B with 0BH (11), the number of characters in the name and extension.
4E92
XOR A AF
Set Register A to 0 with XOR: the hash code starts at 0.

Hash Loop
Register A = the hash code so far, Register Pair HL = the next character, Register B = the characters left.

4E93
XOR (HL) AE
XOR the character at Register Pair HL into Register A (the hash code so far).
4E94
INC HL 23
INCrement Register Pair HL to the next character of the name or extension.
4E95
RLCA 07
Rotate Register A (the hash code) left one bit, bit 7 going into bit 0.
4E96
DECrement Register B (the characters left) and LOOP BACK to 4E93H until all 11 characters are in. DJNZ does not change the flags, so the Z FLAG still comes from the XOR at 4E93H.
4E98
If the NZ FLAG is set (the hash code is not 0), JUMP to 4E9BH. RLCA does not change the Z FLAG, and a value that is 0 stays 0 after the rotate, so the flag from 4E93H matches Register A.
4E9A
INC A 3C
The hash code is 0: INCrement Register A to 01H, because 00H in the HIT marks a free entry.
4E9B
LD (4D6EH),A 32 6E 4D
Store Register A (the hash code) at 4D6EH, the operand of the LD (HL),nn at 4D6DH (RENAME) and the value 4DDAH puts into the HIT of a new file.
4E9E
POP AF F1
Restore Register Pair AF from the Register Pair BC saved at 4E83H: Register A = the first drive (the old Register B).

4E9FH - Search the Drives

Tries each drive from the first to the limit. 4E09H gives the next drive, or leaves the open with the error in Register E (18H) or the error of a drive named in the filespec.

Drive Loop
Register A = the drive to try.

4E9F
LD (4E0EH),A 32 0E 4E
Store Register A (the drive to try) at 4E0EH, the operand of the LD A,nn at 4E0DH that 4E09H uses to find the next drive.
4EA2
GOSUB to 4791H (SYS0/SYS) to select the drive in Register A and check that it holds a turning diskette. Register A returns 0 with the Z FLAG when it is ready, otherwise the error code.
4EA5
If the Z FLAG is set (the drive is ready), JUMP to 4EAEH to search its directory.
4EA7
LD E,18H 1E 18
Load Register E with error code 18H (FILE NOT IN DIRECTORY), the error 4E09H returns when no drive is left. 4EB6H also comes here when a directory has been searched without finding the file, with Register A = 0.
4EA9
GOSUB to 4E09H with Register A = this drive's error (or 0): Register A returns the next drive, or 4E09H returns the error from the open itself.
4EAC
LOOP BACK to 4E9FH to try the drive in Register A.

4EAEH - Search One Directory

Directory sector 1, the HIT, holds one byte per DEC: 00H for a free entry, otherwise the hash code of that entry's name. A DEC is the HIT offset: bits 0-4 = the FDE sector minus 2, bits 5-7 = the entry (0-7) in that sector. The DECs whose HIT byte equals the hash code of the name are collected at 51ADH-51CBH (up to 31 at a time), then each one is checked (4EDDH). Register B = the next DEC to look at, Register C = 1FH (HIT offsets with bits 0-4 = 1FH are not DECs).

4EAE
LD DE,51ADH 11 AD 51
Point Register Pair DE to 51ADH, the start of the list of DECs whose HIT byte matches. Register E is the list position: ADH when it is empty, CCH when it is full.
4EB1
LD BC,001FH 01 1F 00
Load Register Pair BC with 001FH: Register B = 00H, the first DEC to look at; Register C = 1FH, the limit of bits 0-4.

HIT Scan Loop
Register B = the next DEC to look at, Register Pair DE = the next free place in the list.

4EB4
LD A,B 78
Copy Register B (the next DEC) into Register A.
4EB5
SUB C 91
SUBtract Register C (1FH) from Register A. The result is 0 only when 4ED3H has stepped Register B to 1FH: every DEC has been looked at.
4EB6
If the Z FLAG is set (the whole HIT has been looked at and the file was not found), JUMP to 4EA7H with Register A = 0 to try the next drive.
4EB8
LD A,01H 3E 01
Load Register A with 01H, directory sector 1, the HIT.
4EBA
GOSUB to 48AFH (SYS0/SYS) to read directory sector 1 into the buffer at 4300H. It is read again each time, because 4EE2H may have read an FDE sector into 4300H since.
4EBD
RET NZ C0
If the NZ FLAG is set (a read error), RETurn with the error code in Register A.
4EBE
LD A,B 78
Copy Register B (the next DEC) into Register A.

HIT Byte Loop
Register A = the DEC to look at.

4EBF
LD B,A 47
Copy Register A (the DEC to look at) into Register B.
4EC0
LD (DE),A 12
Store Register A (the DEC) at Register Pair DE, the next place in the list. It stays in the list only if Register Pair DE is moved on at 4ECDH.
4EC1
LD L,A 6F
Copy Register A (the DEC) into Register L: Register Pair HL = 4300H + DEC, its HIT byte (Register H is still 43H from the read at 48AFH).
4EC2
LD A,E 7B
Copy Register E (the low byte of the list position) into Register A.
4EC3
CP CCH FE CC
Compare Register A (the list position) against CCH: the list at 51ADH-51CBH holds 31 DECs and is full at 51CCH.
4EC5
If the Z FLAG is set (the list is full), JUMP to 4EDDH to check the DECs in it. The DEC in Register B (stored at 51CCH) has not been looked at yet; the scan goes on from it afterwards.
4EC7
LD A,(4D6EH) 3A 6E 4D
Fetch the hash code of the name wanted from 4D6EH into Register A.
4ECA
CP (HL) BE
Compare Register A (the hash code wanted) against the HIT byte at Register Pair HL.
4ECB
If the NZ FLAG is set (a different hash code, or a free entry), JUMP to 4ECEH without keeping the DEC.
4ECD
INC DE 13
The hash codes match: INCrement Register Pair DE to keep the DEC in the list.
4ECE
LD A,B 78
Copy Register B (the DEC just looked at) into Register A.
4ECF
ADD A,20H C6 20
ADD 20H to Register A: the next entry (bits 5-7) in the same FDE sector.
4ED1
If the NO CARRY FLAG is set (still entry 0-7), LOOP BACK to 4EBFH to look at that DEC.
4ED3
INC A 3C
Past entry 7, bits 5-7 are back to 0: INCrement Register A to the next FDE sector (bits 0-4), entry 0.
4ED4
CP C B9
Compare Register A (the next DEC) against Register C (1FH). The CARRY FLAG is set while bits 0-4 are below 1FH.
4ED5
LD B,A 47
Copy Register A (the next DEC) into Register B. LD does not change the flags.
4ED6
If the CARRY FLAG is set (more FDE sectors), LOOP BACK to 4EBFH to look at the DEC. Otherwise Register B = 1FH and the scan ends at 4EB5H once the list is empty.
4ED8
LD A,E 7B
Copy Register E (the low byte of the list position) into Register A. 4F02H comes here after an entry that does not match.
4ED9
CP ADH FE AD
Compare Register A (the list position) against ADH, the start of the list at 51ADH.
4EDB
If the Z FLAG is set (the list is empty), LOOP BACK to 4EB4H to go on with the HIT scan from the DEC in Register B, or to end it.

4EDDH - Check the Matching Entries

Takes the DECs from the list, last first. An entry must be an FPDE in use (byte 0 bit 7 clear, bit 4 set) with the same name and extension (bytes 5-15). When one matches, the password is checked (4F06H).

4EDD
DEC DE 1B
DECrement Register Pair DE (the list position) to the last DEC in the list, taking it out of the list.
4EDE
LD A,(DE) 1A
Fetch the DEC at Register Pair DE into Register A.
4EDF
LD (4F56H),A 32 56 4F
Store Register A (the DEC) at 4F56H, the operand of the LD (HL),nn at 4F55H that sets FCB+7 if this entry is the file.
4EE2
GOSUB to 48D4H (SYS0/SYS) with Register A = the DEC to read its FDE sector into 4300H if it is not already there. Register Pair HL returns the address of the entry in 4300H.
4EE5
RET NZ C0
If the NZ FLAG is set (a read error), RETurn with the error code in Register A.
4EE6
PUSH DE D5
Save Register Pair DE (the list position) onto the stack.
4EE7
PUSH BC C5
Save Register Pair BC onto the stack: Register B = where the HIT scan goes on, Register C = 1FH.
4EE8
LD A,(HL) 7E
Fetch byte 0 of the entry at Register Pair HL into Register A: the entry type bits and the access level.
4EE9
LD (4F24H),A 32 24 4F
Store Register A (byte 0) at 4F24H, the operand of the AND nn at 4F23H, which takes the access level (bits 0-2) from it.
4EEC
AND 90H E6 90
AND Register A (byte 0) with 90H, keeping bit 7 (the entry is an FXDE) and bit 4 (the entry is in use).
4EEE
CP 10H FE 10
Compare Register A against 10H: an entry in use that is an FPDE.
4EF0
If the NZ FLAG is set (a free entry or an FXDE), JUMP to 4F00H to try the next DEC in the list.
4EF2
GOSUB to 4E1FH to store byte 1 at 4F46H and the EOF low byte (byte 3) at 4F58H for the FCB. Register Pair HL returns byte 4 of the entry.
4EF5
LD DE,51CDH 11 CD 51
Point Register Pair DE to 51CDH, the name and extension wanted.
4EF8
LD B,0BH 06 0B
Load Register B with 0BH (11), the number of characters to compare.

Name Compare Loop
Register Pair HL = the entry, Register Pair DE = the name wanted, Register B = the characters left.

4EFA
INC HL 23
INCrement Register Pair HL to the next byte of the entry (bytes 5-15, the name and extension).
4EFB
LD A,(DE) 1A
Fetch the next character wanted at Register Pair DE into Register A.
4EFC
CP (HL) BE
Compare Register A (the character wanted) against the byte of the entry at Register Pair HL.
4EFD
INC DE 13
INCrement Register Pair DE to the next character wanted. INC of a register pair does not change the flags.
4EFE
If the Z FLAG is set (the characters are the same), JUMP to 4F04H to go on with the next one.
4F00
POP BC C1
Not this entry. Restore Register Pair BC (Register B = where the HIT scan goes on, Register C = 1FH) from the stack.
4F01
POP DE D1
Restore Register Pair DE (the list position) from the stack.
4F02
JUMP to 4ED8H to take the next DEC from the list.
4F04
DECrement Register B (the characters left) and LOOP BACK to 4EFAH until all 11 characters match.

4F06H - The File Is Found: Check the Password

Register Pair HL points to byte 15 of the FPDE. If option AA (428CH bit 7, passwords are checked) is off, or the password given has the code of the update password, the file is opened with access level 0 (FULL). If it has the code of the access password, the access level in bits 0-2 of byte 0 is used. Otherwise the result is error 19H.

4F06
POP BC C1
Discard the saved Register Pair BC (the HIT scan position) from the stack: the search is over.
4F07
POP DE D1
Discard the saved Register Pair DE (the list position) from the stack.
4F08
INC HL 23
INCrement Register Pair HL to byte 16 of the FPDE.
4F09
LD E,(HL) 5E
Load Register E with byte 16 at Register Pair HL, the low byte of the update password code.
4F0A
INC HL 23
INCrement Register Pair HL to byte 17 of the FPDE.
4F0B
LD D,(HL) 56
Load Register D with byte 17 at Register Pair HL: Register Pair DE = the update password code.
4F0C
INC HL 23
INCrement Register Pair HL to byte 18 of the FPDE.
4F0D
LD C,(HL) 4E
Load Register C with byte 18 at Register Pair HL, the low byte of the access password code.
4F0E
INC HL 23
INCrement Register Pair HL to byte 19 of the FPDE.
4F0F
LD B,(HL) 46
Load Register B with byte 19 at Register Pair HL: Register Pair BC = the access password code.
4F10
INC HL 23
INCrement Register Pair HL to byte 20 of the FPDE, the EOF middle byte.
4F11
PUSH HL E5
Save Register Pair HL (byte 20 of the FPDE) onto the stack; 4F62H restores it into Register Pair DE.
4F12
LD HL,(51D8H) 2A D8 51
Fetch the code of the password given from 51D8H-51D9H (stored at 4E87H) into Register Pair HL.
4F15
BIT 7,(IY+0CH) FD CB 0C 7E
Test bit 7 of (IY+0CH): Index Register IY = 4280H, so this is 428CH, whose bit 7 is option AA (passwords are checked).
4F19
If the Z FLAG is set (option AA off), JUMP to 4F2FH to open the file with access level 0.
4F1B
OR A B7
Clear the CARRY FLAG with OR Register A, ready for the SBC.
4F1C
SBC HL,DE ED 52
SUBtract Register Pair DE (the update password code) from Register Pair HL (the code given).
4F1E
If the Z FLAG is set (the update password was given), JUMP to 4F2FH to open the file with access level 0.
4F20
ADD HL,DE 19
ADD Register Pair DE back to Register Pair HL: Register Pair HL = the code given again.
4F21
LD A,07H 3E 07
Load Register A with 07H, the mask of the access level bits.
4F23
AND 00H E6 00
AND Register A (07H) with byte 0 of the FPDE: Register A = the access level (bits 0-2). AND also clears the CARRY FLAG for the SBC. Self-Modifying Code
The operand at 4F24H is stored at 4EE9H.
4F25
SBC HL,BC ED 42
SUBtract Register Pair BC (the access password code) from Register Pair HL (the code given).
4F27
If the Z FLAG is set (the access password was given), JUMP to 4F30H to open the file with the access level in Register A.
4F29
POP HL E1
Wrong password. Discard the saved Register Pair HL (byte 20 of the FPDE) from the stack.
4F2A
LD A,19H 3E 19
Load Register A with error code 19H (FILE ACCESS DENIED).
4F2C
OR A B7
OR Register A (19H) with itself to set the NZ FLAG.
4F2D
RET C9
RETurn from the open with error 19H in Register A.

4F2EH - Build the Open FCB

Index Register IX points to the FCB. The word on the stack points to byte 20 of the FPDE (pushed at 4F2EH from Register Pair DE, or at 4F11H), Register A (at 4F30H) = the access level. The other values come from operands stored earlier: byte 1 of the entry (4F46H), the buffer (4F48H), the DEC (4F56H), the EOF low byte (4F58H) and the record length (4F5EH). The new file code (4E04H) and SYS6/SYS (56CCH) call 4F2EH to open a file whose entry they have. Returns Register A = 00H and the Z FLAG, or error 2CH if the EOF in the entry is bad.

4F2E
PUSH DE D5
Save Register Pair DE (byte 20 of the FPDE) onto the stack; 4F62H restores it.
4F2F
XOR A AF
Set Register A to 0 with XOR: access level 0 (FULL). 4F19H and 4F1EH come here.
4F30
PUSH IX DD E5
Save Index Register IX (the FCB address) onto the stack. 4F27H comes here with Register A = the access level.
4F32
POP HL E1
Restore it into Register Pair HL: Register Pair HL = the FCB address.
4F33
GOSUB to 5110H to set FCB+0 to FCB+21 to 00H and FCB+22 to FCB+31 to FFH. Register A (the access level) and Register Pair HL (the FCB) are kept.
4F36
LD (HL),80H 36 80
Store 80H at Register Pair HL as FCB+0: bit 7 set, the file is open.
4F38
INC HL 23
INCrement Register Pair HL (the FCB pointer) to FCB+1, the access level and mode byte.
4F39
OR 28H F6 28
OR Register A (the access level) with 28H: bit 5 (the buffer does not hold the sector at NEXT) and bit 3 (the FCB is in the version 2 format).
4F3B
LD (HL),A 77
Store Register A at Register Pair HL as FCB+1.
4F3C
LD A,(4F5EH) 3A 5E 4F
Fetch the record length from 4F5EH into Register A.
4F3F
OR A B7
OR Register A (the record length) with itself to set the flags: the Z FLAG for 0 (256-byte records).
4F40
If the Z FLAG is set (256-byte records), JUMP to 4F44H.
4F42
SET 7,(HL) CB FE
Any other record length: set bit 7 of FCB+1 at Register Pair HL, the file is read and written in records.
4F44
INC HL 23
INCrement Register Pair HL (the FCB pointer) to FCB+2, which takes byte 1 of the FPDE.
4F45
LD (HL),00H 36 00
Store byte 1 of the FPDE at Register Pair HL as FCB+2. Self-Modifying Code
The operand at 4F46H is stored at 4E21H; a new file and SYS6/SYS store 00H.
4F47
LD DE,0000H 11 00 00
Load Register Pair DE with the buffer address (0000H in the file). Self-Modifying Code
The operand at 4F48H-4F49H is stored at 4E31H from the caller's Register Pair HL.
4F4A
INC HL 23
INCrement Register Pair HL (the FCB pointer) to FCB+3, the low byte of the buffer address.
4F4B
LD (HL),E 73
Store Register E (the low byte of the buffer address) at Register Pair HL as FCB+3.
4F4C
INC HL 23
INCrement Register Pair HL (the FCB pointer) to FCB+4, the high byte of the buffer address.
4F4D
LD (HL),D 72
Store Register D (the high byte of the buffer address) at Register Pair HL as FCB+4.
4F4E
INC HL 23
INCrement Register Pair HL to FCB+5, the low byte of NEXT, which stays 00H.
4F4F
INC HL 23
INCrement Register Pair HL (the FCB pointer) to FCB+6, the drive number of the file.
4F50
LD A,(IY-02H) FD 7E FE
Fetch (IY-02H) into Register A: Index Register IY = 4280H, so this is 427EH, the current drive, the drive the entry was read from.
4F53
LD (HL),A 77
Store Register A (the drive) at Register Pair HL as FCB+6.
4F54
INC HL 23
INCrement Register Pair HL (the FCB pointer) to FCB+7, the DEC of the FPDE.
4F55
LD (HL),00H 36 00
Store the DEC of the FPDE at Register Pair HL as FCB+7. Self-Modifying Code
The operand at 4F56H is stored at 4DEAH and 4EDFH, and by SYS6/SYS at 56B9H.
4F57
LD A,00H 3E 00
Load Register A with the EOF low byte of the entry. Self-Modifying Code
The operand at 4F58H is stored at 4E27H.
4F59
INC HL 23
INCrement Register Pair HL (the FCB pointer) to FCB+8, the EOF low byte.
4F5A
OR A B7
OR Register A (the EOF low byte) with itself to set the flags: the Z FLAG when it is 0. The flag is used at 4F65H.
4F5B
LD (HL),A 77
Store Register A (the EOF low byte) at Register Pair HL as FCB+8.
4F5C
INC HL 23
INCrement Register Pair HL (the FCB pointer) to FCB+9, the record length.
4F5D
LD (HL),00H 36 00
Store the record length at Register Pair HL as FCB+9. Self-Modifying Code
The operand at 4F5EH is stored at 4E35H, and by SYS6/SYS at 56C0H.
4F5F
INC HL 23
INCrement Register Pair HL to FCB+10, the middle byte of NEXT, which stays 00H.
4F60
INC HL 23
INCrement Register Pair HL to FCB+11, the high byte of NEXT, which stays 00H.
4F61
INC HL 23
INCrement Register Pair HL to FCB+12, the EOF middle byte.
4F62
POP DE D1
Restore Register Pair DE from the stack: byte 20 of the FPDE.
4F63
LD A,(DE) 1A
Fetch byte 20 at Register Pair DE (the EOF middle byte of the entry) into Register A.
4F64
INC DE 13
INCrement Register Pair DE to byte 21 of the FPDE, the EOF high byte. INC of a register pair does not change the flags.
4F65
If the Z FLAG (from 4F5AH) is set (the EOF low byte is 0), JUMP to 4F69H with the CARRY FLAG clear by that OR.
4F67
SUB 01H D6 01
The EOF low byte is not 0: SUBtract 01H from Register A (the EOF middle byte). The entry holds the middle and high bytes one higher than the FCB in this case; the CARRY FLAG is a borrow when the middle byte was 0.
4F69
LD (HL),A 77
Store Register A (the EOF middle byte) at Register Pair HL as FCB+12.
4F6A
INC HL 23
INCrement Register Pair HL (the FCB pointer) to FCB+13, the EOF high byte.
4F6B
LD A,(DE) 1A
Fetch byte 21 at Register Pair DE (the EOF high byte of the entry) into Register A.
4F6C
SBC A,00H DE 00
SUBtract the borrow (CARRY FLAG) from Register A (the EOF high byte).
4F6E
LD (HL),A 77
Store Register A (the EOF high byte) at Register Pair HL as FCB+13.
4F6F
INC DE 13
INCrement Register Pair DE to byte 22 of the FPDE, the first extent element.
4F70
INC HL 23
INCrement Register Pair HL to FCB+14, where the extent elements go.
4F71
LD A,2CH 3E 2C
Load Register A with error code 2CH (BAD DIRECTORY DATA). LD does not change the flags.
4F73
RET C D8
If the CARRY FLAG is set (a borrow out of the high byte: the middle and high bytes were 0 with a low byte that is not 0), RETurn with error 2CH in Register A and the NZ FLAG from the SBC.
4F74
GOSUB to 4F79H to copy the four extent elements (bytes 22-29 at Register Pair DE) to FCB+14 to FCB+21 at Register Pair HL.
4F77
XOR A AF
Set Register A to 0 with XOR, which also sets the Z FLAG: the file is open.
4F78
RET C9
RETurn to the caller with Register A = 00H and the Z FLAG: the FCB is open and ready.

4F79H - Copy 8 Bytes

4F79H copies 8 bytes from Register Pair DE to Register Pair HL; 4F7AH copies 8 bytes from Register Pair HL to Register Pair DE. 8 bytes are four 2-byte extent elements.

4F79
EX DE,HL EB
Exchange Register Pairs DE and HL, so that the LDIR copies from the old Register Pair DE to the old Register Pair HL. 4F74H and 50A6H call here.
4F7A
LD A,08H 3E 08
Load Register A with 08H, the number of bytes to copy. 50ABH and 50BAH call here.
4F7C
LD C,A 4F
Copy Register A (8, the byte count) into Register C, the low byte of the LDIR count.
4F7D
LD B,00H 06 00
Load Register B with 00H: Register Pair BC = 0008H.
4F7F
LDIR ED B0
Copy Register Pair BC (8) bytes from Register Pair HL to Register Pair DE. Register Pairs HL and DE return 8 bytes further on, Register Pair BC = 0000H.
4F81
RET C9
RETurn with Register A = 08H, which 50C0H turns into the element count 4.

4F82H - Function 64H: Allocate Granules

Called by SYS0/SYS at 4862H (CALL 4982H) from the sector mapping routine (47AEH) when the sector wanted lies past the last granule the file has. Index Register IX = the FCB, Index Register IY = 4280H, Register Pair DE = the number of granules missing minus 1, and 480FH (the operand of the LD A,nn at SYS0/SYS 480EH) holds the DEC of the file's last directory entry (the FPDE, or its last FXDE). SYS2/SYS allocates DE + 1 granules, extending the last extent element while the next granule is free, else starting a new element; when the four elements of the entry are used up it creates an FXDE (5058H). After the granules asked for it takes up to 3 more (5018H); CLOSE (SYS3/SYS) gives back what the file does not use. The GAT and the entries are written, the elements of the FPDE copied into FCB+14 to FCB+21, and SYS2/SYS returns with Register Pair HL pointing to the last entry, from which SYS0/SYS (4867H) maps the sector again. Errors (3DH no extend, 1BH diskette full, 2CH, disk errors) leave through the SYS0/SYS DOS function error exit (4972H). SYS6/SYS calls this function the same way at 5C53H.

4F82
LD A,3DH 3E 3D
Load Register A with error code 3DH (ASE=N ATTRIBUTE. CAN'T EXTEND FILE), the error if the file may not be extended.
4F84
BIT 7,(IX+02H) DD CB 02 7E
Test bit 7 of (IX+02H) (Index Register IX = the FCB), FCB+2: the no extend bit taken from byte 1 of the FPDE when the file was opened.
4F88
If the Z FLAG is set (the file may be extended), GOSUB to 471BH (SYS0/SYS) to select the drive of the FCB (FCB+6). Register A returns 0 with the Z FLAG, or the error code with the NZ FLAG.
4F8B
If the NZ FLAG is set (Register A = 3DH for a no extend file, or the drive error), JUMP to 4FFBH to leave with the error.
4F8D
LD A,(480FH) 3A 0F 48
Fetch the DEC of the last directory entry of the file from 480FH (stored by SYS0/SYS at 4845H) into Register A.
4F90
LD (505EH),A 32 5E 50
Store Register A (the DEC) at 505EH, the operand of the LD B,nn at 505DH: the entry whose extent elements are being filled.
4F93
PUSH AF F5
Save Register Pair AF (Register A = the DEC) onto the stack; 5032H restores it to point to this entry at the end.
4F94
GOSUB to 48DBH (SYS0/SYS) with Register A = the DEC to read its FDE sector into 4300H. Register Pair HL returns the entry, Register A its offset in the sector, the Z FLAG for success.
4F97
GOSUB to 5036H: it leaves with the error if the read failed, or with error 2CH if the entry at Register Pair HL is not in use. It RETurns with Register A = the offset kept.
4F9A
INC DE 13
INCrement Register Pair DE (the granules missing minus 1): Register Pair DE = the number of granules to allocate.
4F9B
PUSH DE D5
Save Register Pair DE onto the stack as the count of granules still wanted; 5011H-5015H and 501FH-5023H work on it there.
4F9C
GOSUB to 50B4H with Register A = the offset to copy the four extent elements of the entry to 51ADH-51B4H. Register Pair HL returns the last element in use, or 51ADH when all four are empty.
4F9F
LD B,(IY+05H) FD 46 05
Load Register B with (IY+05H): Index Register IY = 4280H, so this is 4285H, the number of granules per lump of the selected drive.
4FA2
LD C,01H 0E 01
Load Register C with 01H, the GAT bit of granule 0 of a lump.
4FA4
LD E,(HL) 5E
Fetch the lump byte of the element at Register Pair HL into Register E.
4FA5
INC E 1C
INCrement Register E (the lump byte): it becomes 0 if the element is empty (FFH).
4FA6
If the Z FLAG is set (no element is in use), JUMP to 4FC5H to search from granule 0 (Register C = 01H) of lump 0 (Register E = 0), with Register B = the granules per lump.
4FA8
DEC E 1D
DECrement Register E back to the lump number.
4FA9
DEC E 1D
DECrement Register E again: Register E = the lump number minus 1, ready for the INC at 4FB8H.
4FAA
INC HL 23
INCrement Register Pair HL to the second byte of the element.
4FAB
LD A,(HL) 7E
Fetch the second byte at Register Pair HL into Register A.
4FAC
AND 1FH E6 1F
AND Register A with 1FH, keeping bits 0-4: the number of granules in the element minus 1.
4FAE
LD D,A 57
Copy Register A (the granules minus 1) into Register D.
4FAF
INC D 14
INCrement Register D: Register D = the number of granules in the element.
4FB0
LD A,(HL) 7E
Fetch the second byte of the element at Register Pair HL into Register A again.
4FB1
AND E0H E6 E0
AND Register A with E0H, keeping bits 5-7: the first granule of the element within its lump.
4FB3
DEC HL 2B
DECrement Register Pair HL back to the lump byte of the element.
4FB4
RLCA 07
Rotate Register A left (the first of three RLCAs).
4FB5
RLCA 07
Rotate Register A left (the second of three RLCAs).
4FB6
RLCA 07
Rotate Register A left (the third): bits 5-7 are now in bits 0-2, Register A = the first granule of the element.
4FB7
ADD A,D 82
ADD Register D (the granules in the element) to Register A: the granule after the last one of the element, counted from the start of its lump.

Lump Loop
Register A = the granule counted from lump Register E + 1.

4FB8
INC E 1C
INCrement Register E (the lump number) to the next lump on the diskette.
4FB9
SUB B 90
SUBtract Register B (the granules per lump) from Register A.
4FBA
If the NO CARRY FLAG is set (the granule is past lump Register E), LOOP BACK to 4FB8H. On the borrow Register E = the lump that holds the next granule.
4FBC
ADD A,B 80
ADD Register B (the granules per lump) back to Register A: Register A = the next granule within lump Register E.
4FBD
If the Z FLAG is set (granule 0 of the lump), JUMP to 4FC5H with Register C = 01H and Register B = the granules per lump.

Granule Bit Loop
Register A = the granules to step over.

4FBF
RLC C CB 01
Rotate Register C (the GAT bit mask) left to the next granule.
4FC1
DEC B 05
DECrement Register B: one granule fewer from here to the end of the lump.
4FC2
DEC A 3D
DECrement Register A (the granules to step over).
4FC3
If the NZ FLAG is set, LOOP BACK to 4FBFH. Register C = the GAT bit of the next granule, Register B = the granules from it to the end of the lump.

4FC5H - Scan the GAT

The GAT (directory sector 0) has one byte per lump and one bit per granule (1 = in use). On entry Register Pair HL = the extent element being filled (in 51ADH-51B4H), Register E = the lump to start in, Register C = the GAT bit of the granule and Register B = the granules left in the lump. In the scan Register Pair HL = 4300H + lump, its GAT byte, and Register Pair DE = the element. The scan runs to the last lump (4281H), then once more from lump 0.

4FC5
PUSH HL E5
Save Register Pair HL (the extent element) onto the stack. 4FA6H, 4FBDH and 5089H come here.
4FC6
XOR A AF
Set Register A to 0 with XOR: directory sector 0, the GAT.
4FC7
GOSUB to 48AFH (SYS0/SYS) to read directory sector 0 (the GAT) into 4300H. Register Pair HL returns 4300H; Register A returns the error with the NZ FLAG.
4FCA
If the NZ FLAG is set (a read error), JUMP to 4FFBH to leave with it.
4FCC
LD L,E 6B
Copy Register E (the lump) into Register L: Register Pair HL = 4300H + lump, its GAT byte.
4FCD
POP DE D1
Restore Register Pair DE from the stack: the extent element.
4FCE
LD A,01H 3E 01
Load Register A with 01H: the number of times the scan may go back to lump 0.
4FD0
EX AF,AF' 08
Exchange AF with the alternate AF', keeping the count in the alternate Register A. 4FEEH comes here.
4FD1
JUMP to 4FE5H to check that the lump in Register L is on the diskette.

Granule Scan Loop
Register Pair HL = the GAT byte, Register C = the granule's bit, Register B = the granules left in the lump, Register Pair DE = the element.

4FD3
LD A,(HL) 7E
Fetch the GAT byte of the lump at Register Pair HL into Register A.
4FD4
AND C A1
AND Register A (the GAT byte) with Register C (the granule's bit): the Z FLAG is set when the granule is free.
4FD5
LD A,(DE) 1A
Fetch the lump byte of the element at Register Pair DE into Register A. LD does not change the flags.
4FD6
If the Z FLAG is set (the granule is free), JUMP to 4FFDH to take it, with Register A = the lump byte of the element.
4FD8
INC A 3C
The granule is in use. INCrement Register A (the lump byte of the element): 0 if the element is empty (FFH).
4FD9
If the NZ FLAG is set (the element is in use, so the next granule after it is taken by another file), JUMP to 504CH to go on with the next element.
4FDB
RLC C CB 01
Rotate Register C (the GAT bit mask) left to the next granule. 5016H and 5028H come here to look for the next granule.
4FDD
DECrement Register B (the granules left in the lump) and LOOP BACK to 4FD3H while it is not 0.
4FDF
INC L 2C
INCrement Register L to the GAT byte of the next lump.
4FE0
LD B,(IY+05H) FD 46 05
Load Register B with (IY+05H): Index Register IY = 4280H, so this is 4285H, the granules per lump: all of them are left in the new lump.
4FE3
LD C,01H 0E 01
Load Register C with 01H, the GAT bit of granule 0.
4FE5
LD A,L 7D
Copy Register L (the lump) into Register A. 4FD1H comes here.
4FE6
CP (IY+01H) FD BE 01
Compare Register A (the lump) against (IY+01H): Index Register IY = 4280H, so this is 4281H, the number of lumps on the diskette (byte 1 of the drive's PDRIVE entry).
4FE9
If the CARRY FLAG is set (the lump is on the diskette), LOOP BACK to 4FD3H.
4FEB
EX AF,AF' 08
Past the last lump. Exchange Register Pair AF with the alternate AF' to fetch the count of returns to lump 0 into Register A.
4FEC
DEC A 3D
DECrement Register A (the count): the Z FLAG is set the first time.
4FED
LD L,A 6F
Copy Register A into Register L: the first time Register A is 0, so Register Pair HL = 4300H, the GAT byte of lump 0. Register Pair DE (the element) is not changed.
4FEE
If the Z FLAG is set (the first time), JUMP to 4FD0H to keep the count in the alternate AF' and scan again from lump 0 with Register B and Register C already set for granule 0.
4FF0
BIT 0,(IY+0BH) FD CB 0B 46
The second time: no granule is free. Test bit 0 of (IY+0BH): Index Register IY = 4280H, so this is 428BH, whose bit 0 is set while the extra granules (5018H) are being taken.
4FF4
If the NZ FLAG is set (only the extra granules were missing), JUMP to 502AH to finish without an error.
4FF6
GOSUB to 508CH to write the GAT and the extent elements taken so far.
4FF9
LD A,1BH 3E 1B
Load Register A with error code 1BH (DISKETTE SPACE FULL).
4FFB
JUMP to 503DH to leave with the error in Register A. 4F8BH and 4FCAH also come here.

4FFDH - Take a Free Granule

Register A = the lump byte of the element (FFH if it is empty), Register Pair HL = the GAT byte, Register C = the granule's bit, Register B = the granules left in the lump, Register Pair DE = the element.

4FFD
INC A 3C
INCrement Register A (the lump byte of the element): 0 if the element is empty (FFH).
4FFE
If the NZ FLAG is set (the element is in use), JUMP to 5043H to add the granule to it.
5000
LD A,L 7D
The element is empty: copy Register L (the lump of the granule) into Register A.
5001
LD (DE),A 12
Store Register A (the lump) at Register Pair DE as the lump byte of the new element.
5002
INC DE 13
INCrement Register Pair DE to the second byte of the element.
5003
LD A,(IY+05H) FD 7E 05
Fetch (IY+05H) into Register A: Index Register IY = 4280H, so this is 4285H, the granules per lump.
5006
SUB B 90
SUBtract Register B (the granules left in the lump) from Register A: Register A = the granule within the lump (0-7).
5007
RRCA 0F
Rotate Register A right (the first of three RRCAs).
5008
RRCA 0F
Rotate Register A right (the second of three RRCAs).
5009
RRCA 0F
Rotate Register A right (the third): bits 0-2 are now bits 5-7, the first granule of the element, and bits 0-4 are 0.
500A
DEC A 3D
DECrement Register A, so that the INC at 500BH gives a granule count of 0 in bits 0-4: one granule.
500B
INC A 3C
INCrement Register A, the second byte of the element: bits 0-4 (the granules minus 1) go up by one. 5049H comes here with the second byte of an element in use.
500C
LD (DE),A 12
Store Register A at Register Pair DE as the second byte of the element.
500D
DEC DE 1B
DECrement Register Pair DE back to the lump byte of the element.
500E
LD A,(HL) 7E
Fetch the GAT byte at Register Pair HL into Register A.
500F
OR C B1
OR Register A (the GAT byte) with Register C (the granule's bit) to set the bit.
5010
LD (HL),A 77
Store Register A at Register Pair HL: the granule is now in use.
5011
EX (SP),HL E3
Exchange Register Pair HL (the GAT byte) with the word at the top of the stack (Stack Pointer), the count of granules still wanted.
5012
DEC HL 2B
DECrement Register Pair HL (the count): one granule fewer is wanted.
5013
LD A,H 7C
Copy Register H (the high byte of the count) into Register A.
5014
OR L B5
OR Register L (the low byte of the count) into Register A: the Z FLAG is set when no more granules are wanted.
5015
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (Stack Pointer) again: the count goes back onto the stack and Register Pair HL = the GAT byte. EX (SP),HL does not change the flags.
5016
If the NZ FLAG is set (more granules are wanted), JUMP to 4FDBH to look at the next granule.
5018
LD A,(IY+0BH) FD 7E 0B
All the granules asked for are taken. Fetch (IY+0BH) into Register A: Index Register IY = 4280H, so this is 428BH, the flags of the allocation.
501B
AND 03H E6 03
AND Register A with 03H, keeping bit 0 (the extra granules are being taken) and bit 1 (no extra granules: SYS6/SYS at 538EH and SYS14/SYS at 5020H set it while they allocate).
501D
If the NZ FLAG is set (either bit is set), JUMP to 502AH to finish.
501F
EX (SP),HL E3
Exchange Register Pair HL (the GAT byte) with the word at the top of the stack (Stack Pointer): Register Pair HL = the count of granules still wanted (0).
5020
INC HL 23
INCrement Register Pair HL (the count of granules still wanted) from 0 to 1.
5021
INC HL 23
INCrement Register Pair HL (the count of granules still wanted) from 1 to 2.
5022
INC HL 23
INCrement Register Pair HL (the count) to 3: three more granules are wanted.
5023
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (Stack Pointer) again: the count goes back onto the stack and Register Pair HL = the GAT byte.
5024
SET 0,(IY+0BH) FD CB 0B C6
Set bit 0 of (IY+0BH): Index Register IY = 4280H, so this is 428BH; bit 0 shows that the extra granules are being taken.
5028
JUMP to 4FDBH to step to the granule after the last one taken and look at it.
502A
RES 0,(IY+0BH) FD CB 0B 86
Clear bit 0 of (IY+0BH): Index Register IY = 4280H, so this is 428BH; the extra granules are no longer being taken. 4FF4H, 501DH and 5056H come here.
502E
GOSUB to 508CH to write the GAT and the extent elements of the entry being extended.
5031
POP AF F1
Discard the word at the top of the stack (the count of granules still wanted) into Register Pair AF.
5032
POP AF F1
Restore Register Pair AF from the stack: Register A = the DEC saved at 4F93H, the last entry of the file at the start.
5033
GOSUB to 48D4H (SYS0/SYS) with Register A = the DEC to read its FDE sector into 4300H if it is not already there. Register Pair HL returns the entry, Register A its offset in the sector. 5070H, 5081H and 5092H call here.
5036
If the NZ FLAG is set (a read error, Register A = its code), JUMP to 503DH to leave with it. 4F97H calls here.
5038
BIT 4,(HL) CB 66
Test bit 4 of byte 0 of the entry at Register Pair HL: the entry is in use.
503A
RET NZ C0
If the NZ FLAG is set (the entry is in use), RETurn with Register Pair HL = the entry and Register A = its offset. From 5032H this RETurn goes back through the SYS0/SYS dispatcher to SYS0/SYS 4867H.
503B
LD A,2CH 3E 2C
The entry is not in use: load Register A with error code 2CH (BAD DIRECTORY DATA).
503D
GOSUB to 4BC5H (SYS0/SYS) to clear bit 6 of 4289H, which the dispatcher would have done on its return. Register A (the error code) is kept. 4FFBH, 5036H, 5064H and 50B2H come here.
5040
JUMP to 4972H, the SYS0/SYS error exit of a DOS function: Register A (the error) goes back to the caller of the read or write that needed the granules.

5043H - Add the Granule to the Element

Register Pair DE points to the lump byte of an element in use, and the granule is the one after its last. The granule is added if the element has fewer than 32 granules.

5043
INC DE 13
INCrement Register Pair DE to the second byte of the element.
5044
LD A,(DE) 1A
Fetch the second byte at Register Pair DE into Register A.
5045
INC A 3C
INCrement Register A: bits 0-4 (the granules minus 1) go up by one.
5046
AND 1FH E6 1F
AND Register A with 1FH, keeping bits 0-4: the Z FLAG is set if the element already has 32 granules.
5048
LD A,(DE) 1A
Fetch the second byte at Register Pair DE into Register A again. LD does not change the flags.
5049
If the NZ FLAG is set (the element has room), JUMP to 500BH to add the granule.
504B
DEC DE 1B
The element is full: DECrement Register Pair DE back to its lump byte and go on into 504CH.

504CH - Next Element

The granule does not follow the element, or the element is full: go on with the next element of the entry. After the fourth element comes the FEH that 50BEH stores at 51B5H.

504C
INC DE 13
INCrement Register Pair DE to the second byte of the element.
504D
INC DE 13
INCrement Register Pair DE to the lump byte of the next element.
504E
LD A,(DE) 1A
Fetch the lump byte of the next element at Register Pair DE into Register A.
504F
INC A 3C
INCrement Register A: 0 if the element is empty (FFH). For the FEH at 51B5H it is FFH.
5050
If the Z FLAG is set (an empty element), LOOP BACK to 4FD3H to look at the same granule for this element.
5052
BIT 0,(IY+0BH) FD CB 0B 46
All four elements of the entry are used. Test bit 0 of (IY+0BH): Index Register IY = 4280H, so this is 428BH; bit 0 is set while the extra granules are being taken.
5056
If the NZ FLAG is set (only the extra granules are missing), JUMP to 502AH to finish without an FXDE.

5058H - Create an FXDE

All four elements of the entry are used. A free directory entry is found (50CFH), starting in the FDE sector of the old entry and with the HIT byte of the old entry as its hash code. It becomes an FXDE: byte 0 = 90H, byte 1 = the DEC of the old entry. The old entry gets FEH and the new DEC in bytes 30-31. The allocation then goes on with the empty elements of the new entry.

5058
PUSH HL E5
Save Register Pair HL (the GAT byte of the lump being scanned) onto the stack.
5059
PUSH BC C5
Save Register Pair BC onto the stack: Register C = the granule's bit, Register B = the granules left in the lump.
505A
GOSUB to 508CH to write the GAT and the elements of the old entry. Register Pair HL returns 4300H.
505D
LD B,00H 06 00
Load Register B with the DEC of the entry being extended. Self-Modifying Code
The operand at 505EH is stored at 4F90H and 507EH.
505F
LD L,B 68
Copy Register B (the old DEC) into Register L: Register Pair HL = 4300H + DEC, which is the HIT byte of the old entry once 50D2H has read the HIT.
5060
PUSH BC C5
Save Register Pair BC onto the stack: Register B = the old DEC.
5061
GOSUB to 50CFH with Register B = the old DEC and Register Pair HL = its HIT byte: find a free entry, starting in the FDE sector of the old entry, and give it the HIT byte of the old entry. Register Pair HL returns the new entry (cleared), Register A its DEC.
5064
If the NZ FLAG is set (Register A = 1AH, no free entry, or a disk error), JUMP to 503DH to leave with it.
5066
LD C,A 4F
Copy Register A (the new DEC) into Register C.
5067
LD (HL),90H 36 90
Store 90H at Register Pair HL as byte 0 of the new entry: bit 7 (an FXDE) and bit 4 (in use).
5069
INC HL 23
INCrement Register Pair HL to byte 1 of the new entry.
506A
POP DE D1
Restore Register Pair DE from the stack (the BC saved at 5060H): Register D = the old DEC.
506B
LD (HL),D 72
Store Register D (the old DEC) at Register Pair HL as byte 1 of the new entry, the link back to the entry before it.
506C
GOSUB to 50AEH to write the FDE sector of the new entry.
506F
LD A,D 7A
Copy Register D (the old DEC) into Register A.
5070
GOSUB to 5033H with Register A = the old DEC to read its FDE sector if needed. Register Pair HL returns the old entry, Register A its offset in the sector.
5073
ADD A,1FH C6 1F
ADD 1FH to Register A (the offset of the old entry): the offset of its byte 31.
5075
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte 31 of the old entry.
5076
LD (HL),C 71
Store Register C (the new DEC) at Register Pair HL as byte 31 of the old entry.
5077
DEC HL 2B
DECrement Register Pair HL to byte 30 of the old entry.
5078
LD (HL),FEH 36 FE
Store FEH at Register Pair HL as byte 30: the fifth element of the old entry is a link, whose second byte is the DEC of the FXDE.
507A
GOSUB to 50AEH to write the FDE sector of the old entry.
507D
LD A,C 79
Copy Register C (the new DEC) into Register A.
507E
LD (505EH),A 32 5E 50
Store Register A (the new DEC) at 505EH: the new entry is now the one being extended.
5081
GOSUB to 5033H with Register A = the new DEC. Register Pair HL returns the new entry, Register A its offset in the sector.
5084
GOSUB to 50B4H to copy the four extent elements of the new entry (all empty) to 51ADH-51B4H. Register Pair HL returns 51ADH.
5087
POP BC C1
Restore Register Pair BC from the stack: Register C = the granule's bit, Register B = the granules left in the lump.
5088
POP DE D1
Restore Register Pair DE from the stack (the HL saved at 5058H): Register E = the lump being scanned.
5089
JUMP to 4FC5H with Register Pair HL = 51ADH (the first element) and Register E = the lump, to read the GAT again and go on.

508CH - Write the GAT and the Extent Elements

Writes the directory sector in 4300H (the GAT), then copies 51ADH-51B4H into bytes 22-29 of the entry being extended (505EH) and writes its sector. If that entry is the FPDE, the elements are also copied to FCB+14 to FCB+21. Returns Register Pair HL = 4300H.

508C
GOSUB to 50AEH to write the GAT in 4300H. 4FF6H, 502EH and 505AH call here.
508F
LD A,(505EH) 3A 5E 50
Fetch the DEC of the entry being extended from 505EH into Register A.
5092
GOSUB to 5033H with Register A = the DEC to read its FDE sector if needed. Register Pair HL returns the entry, Register A its offset in the sector.
5095
ADD A,16H C6 16
ADD 16H to Register A (the offset of the entry): the offset of byte 22, the first extent element.
5097
BIT 7,(HL) CB 7E
Test bit 7 of byte 0 of the entry at Register Pair HL: the Z FLAG is set for an FPDE, the NZ FLAG for an FXDE.
5099
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte 22 of the entry.
509A
PUSH HL E5
Save Register Pair HL (byte 22 of the entry) onto the stack.
509B
PUSH IX DD E5
Save Index Register IX (the FCB address) onto the stack.
509D
POP HL E1
Restore it into Register Pair HL: Register Pair HL = the FCB address.
509E
LD BC,000EH 01 0E 00
Load Register Pair BC with 000EH (14), the offset of the extent elements in the FCB.
50A1
ADD HL,BC 09
ADD Register Pair BC (14) to Register Pair HL: Register Pair HL = FCB+14, the copy of the FPDE elements in the FCB. ADD HL,BC does not change the Z FLAG.
50A2
LD DE,51ADH 11 AD 51
Point Register Pair DE to 51ADH, the extent elements.
50A5
PUSH DE D5
Save Register Pair DE (51ADH) onto the stack.
50A6
If the Z FLAG is set (an FPDE), GOSUB to 4F79H to copy the 8 bytes from Register Pair DE (51ADH) to Register Pair HL (FCB+14).
50A9
POP HL E1
Restore Register Pair HL from the stack: 51ADH.
50AA
POP DE D1
Restore Register Pair DE from the stack: byte 22 of the entry.
50AB
GOSUB to 4F7AH to copy the 8 bytes from Register Pair HL (51ADH) to Register Pair DE (bytes 22-29 of the entry), then go on into 50AEH to write the sector.

50AEH - Write the Directory Sector

Writes the directory sector in 4300H; an error leaves the allocation through 503DH.

50AE
GOSUB to 48C4H (SYS0/SYS) to write the directory sector in 4300H with the directory mark. Register Pair HL returns 4300H; Register A returns the error with the NZ FLAG. 506CH, 507AH and 508CH call here.
50B1
RET Z C8
If the Z FLAG is set (the sector was written), RETurn to the caller with Register A = 00H.
50B2
JUMP to 503DH to leave with the error in Register A (12H, DIRECTORY WRITE ERROR).

50B4H - Copy the Extent Elements of an Entry

Register A = the offset of the entry in 4300H. Copies bytes 22-29 to 51ADH-51B4H and puts FEH at 51B5H. Returns Register Pair HL pointing to the last element in use with the NZ FLAG, or to 51ADH with the Z FLAG if all four are empty.

50B4
ADD A,16H C6 16
ADD 16H to Register A (the offset of the entry): the offset of byte 22. 4F9CH and 5084H call here.
50B6
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte 22 of the entry in 4300H.
50B7
LD DE,51ADH 11 AD 51
Point Register Pair DE to 51ADH, where the elements go.
50BA
GOSUB to 4F7AH to copy the 8 bytes from Register Pair HL to Register Pair DE. Register A returns 08H, Register Pair DE returns 51B5H.
50BD
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = 51B5H.
50BE
LD (HL),FEH 36 FE
Store FEH at Register Pair HL (51B5H), after the fourth element, so that 504CH sees the entry is full.
50C0
RRCA 0F
Rotate Register A (08H) right: Register A = 04H.
50C1
LD B,A 47
Copy Register A (4) into Register B, the number of elements to look at.

Last Element Loop
Register Pair HL = the element after the one to look at.

50C2
DEC HL 2B
DECrement Register Pair HL (the first of two DECs).
50C3
DEC HL 2B
DECrement Register Pair HL (the second): Register Pair HL = the lump byte of the element before.
50C4
LD A,(HL) 7E
Fetch the lump byte at Register Pair HL into Register A.
50C5
INC A 3C
INCrement Register A (the lump byte): 0 if the element is empty (FFH).
50C6
RET NZ C0
If the NZ FLAG is set (the element is in use), RETurn with Register Pair HL = that element.
50C7
DECrement Register B (the elements left) and LOOP BACK to 50C2H while it is not 0.
50C9
RET C9
All four are empty: RETurn with Register Pair HL = 51ADH and the Z FLAG from 50C5H.

50CAH - Function E4H, C = 2: Find a Free Directory Entry

Register D = the drive, Register Pair HL = the address of the hash code to put in the HIT, Register B = the DEC or hash code whose FDE sector the search starts in. 50CFH is called directly by the create (4DDEH), by 5061H and by SYS6/SYS (610EH) with the drive already selected. The search starts in FDE sector (B AND 1FH) modulo the number of FDE sectors (8 + HIT byte 1FH), takes the first entry whose HIT byte is 00H, stores the hash code there and writes the HIT, then clears the entry (5110H). Returns Register Pair HL = the entry, Register A = its DEC and the Z FLAG, or 1AH (DIRECTORY SPACE FULL), 2CH, or a disk error.

50CA
LD A,D 7A
Copy Register D (the drive number) into Register A.
50CB
GOSUB to 4723H (SYS0/SYS) to select the drive in Register A. Register A returns 0 with the Z FLAG, or the error code.
50CE
RET NZ C0
If the NZ FLAG is set (a bad drive), RETurn with the error in Register A.
50CF
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the address of the hash code. 4DDEH and 5061H call here.
50D0
LD A,01H 3E 01
Load Register A with 01H, directory sector 1, the HIT.
50D2
GOSUB to 48AFH (SYS0/SYS) to read the HIT into 4300H. Register Pair HL returns 4300H; Register A returns the error with the NZ FLAG.
50D5
RET NZ C0
If the NZ FLAG is set (a read error), RETurn with it in Register A.
50D6
LD A,(431FH) 3A 1F 43
Fetch HIT byte 1FH from 431FH into Register A: the number of FDE sectors over 8.
50D9
ADD A,08H C6 08
ADD 08H to Register A: every directory has at least 8 FDE sectors.
50DB
LD C,A 4F
Copy Register A into Register C: Register C = the number of FDE sectors.
50DC
LD A,B 78
Copy Register B (the start value) into Register A.
50DD
AND 1FH E6 1F
AND Register A (the start value) with 1FH, keeping bits 0-4, the FDE sector part of a DEC.

Modulo Loop

50DF
SUB C 91
SUBtract Register C (the number of FDE sectors) from Register A.
50E0
If the NO CARRY FLAG is set, LOOP BACK to 50DFH until Register A goes below 0.
50E2
ADD A,C 81
ADD Register C back to Register A: Register A = the FDE sector to start in, below the number of FDE sectors.
50E3
LD B,A 47
Copy Register A (the FDE sector to start in) into Register B. 50F9H comes here with Register A = 0.
50E4
LD L,A 6F
Copy Register A into Register L: Register Pair HL = 4300H + DEC, the HIT byte of entry 0 in that FDE sector.
50E5
JUMP to 50F2H to check the DEC against the number of FDE sectors.

Free Entry Loop
Register Pair HL = the HIT byte of the DEC being looked at.

50E7
LD A,(HL) 7E
Fetch the HIT byte at Register Pair HL into Register A.
50E8
OR A B7
OR Register A (the HIT byte) with itself to set the flags.
50E9
If the Z FLAG is set (00H, a free entry), JUMP to 50FEH to take it.
50EB
LD A,L 7D
Copy Register L (the DEC being looked at) into Register A to step to the next one.
50EC
ADD A,20H C6 20
ADD 20H to Register A: the next entry (bits 5-7) in the same FDE sector.
50EE
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the HIT byte of the next DEC.
50EF
If the NO CARRY FLAG is set (still entry 0-7), LOOP BACK to 50E7H.
50F1
INC L 2C
Past entry 7: INCrement Register L to the next FDE sector, entry 0.
50F2
LD A,L 7D
Copy Register L (the DEC) into Register A. 50E5H comes here.
50F3
CP C B9
Compare Register A (the DEC) against Register C (the number of FDE sectors). The CARRY FLAG is set while bits 0-4 are below it.
50F4
If the CARRY FLAG is set, LOOP BACK to 50E7H.
50F6
XOR A AF
Set Register A to 0 with XOR: the FDE sector to start the second pass in.
50F7
INC B 04
INCrement Register B (the FDE sector the search started in).
50F8
DEC B 05
DECrement Register B back: the Z FLAG is set if the search started in FDE sector 0.
50F9
If the NZ FLAG is set (the search did not start at sector 0), JUMP to 50E3H with Register A = 0 to search again from FDE sector 0.
50FB
OR 1AH F6 1A
OR Register A (0) with 1AH: Register A = error code 1AH (DIRECTORY SPACE FULL) with the NZ FLAG.
50FD
RET C9
RETurn to the caller with error 1AH in Register A and the NZ FLAG.
50FE
LD A,(DE) 1A
Fetch the hash code at Register Pair DE into Register A.
50FF
LD (HL),A 77
Store Register A (the hash code) at Register Pair HL, the HIT byte of the free entry: the entry is taken.
5100
LD C,L 4D
Copy Register L (the DEC of the entry) into Register C.
5101
GOSUB to 48C4H (SYS0/SYS) to write the HIT in 4300H back to the diskette. Register A returns the error with the NZ FLAG.
5104
RET NZ C0
If the NZ FLAG is set (a write error), RETurn with it in Register A.
5105
LD A,C 79
Copy Register C (the DEC of the new entry) into Register A for the sector read.
5106
GOSUB to 48DBH (SYS0/SYS) with Register A = the DEC to read its FDE sector into 4300H. Register Pair HL returns the entry.
5109
RET NZ C0
If the NZ FLAG is set (a read error), RETurn with it in Register A.
510A
BIT 4,(HL) CB 66
Test bit 4 of byte 0 of the entry at Register Pair HL: the entry is in use.
510C
LD A,2CH 3E 2C
Load Register A with error code 2CH (BAD DIRECTORY DATA). LD does not change the flags.
510E
RET NZ C0
If the NZ FLAG is set (the entry is in use although its HIT byte was 00H), RETurn with error 2CH in Register A.
510F
LD A,C 79
Copy Register C (the DEC) into Register A and go on into 5110H to clear the entry at Register Pair HL.

5110H - Clear an Entry or an FCB

Register Pair HL points to 32 bytes: bytes 0-21 are set to 00H and bytes 22-31 to FFH (empty extent elements). Used for a new directory entry (510FH) and for the FCB (4F33H). Register A and Register Pair HL are kept, Register Pair BC returns 0000H, and the Z FLAG is set.

5110
LD BC,A16H 01 16 0A
Load Register Pair BC with 0A16H: Register B = 0AH (10 bytes of FFH), Register C = 16H (22 bytes of 00H). 4F33H calls here; 510FH goes on into it.
5113
PUSH HL E5
Save Register Pair HL (the start of the 32 bytes) onto the stack.

Zero Loop

5114
LD (HL),00H 36 00
Store 00H at Register Pair HL, the next of the 22 bytes to clear.
5116
INC HL 23
INCrement Register Pair HL to the next of the 32 bytes being set.
5117
DEC C 0D
DECrement Register C (the bytes of 00H left). The Z FLAG is set when it reaches 0.
5118
If the NZ FLAG is set, LOOP BACK to 5114H until 22 bytes are 00H.

FFH Loop

511A
LD (HL),FFH 36 FF
Store FFH at Register Pair HL, part of an empty extent element.
511C
INC HL 23
INCrement Register Pair HL to the next of the 32 bytes being set.
511D
DECrement Register B (the bytes of FFH left) and LOOP BACK to 511AH until 10 bytes are FFH. DJNZ does not change the flags, so the Z FLAG from 5117H stays set.
511F
POP HL E1
Restore Register Pair HL from the stack: the start of the 32 bytes.
5120
RET C9
RETurn with Register Pair HL = the start, Register A unchanged and the Z FLAG.

5121H - Copy a Filespec Field

Register Pair DE points to the character before the field, Register Pair HL to where it goes, Register B = the field length (5121H sets 8). With the NZ FLAG on entry the field is not there and is filled with spaces. Otherwise its first character must be a letter, the others letters or digits; it ends at the first other character, which is returned in Register A with Register Pair DE pointing to it, and the rest of the field is filled with spaces. A bad first character or a field that is too long returns error 30H (BAD FILESPEC) from the open.

5121
LD B,08H 06 08
Load Register B with 08H, the length of a name or a password field. LD does not change the flags. 4E3DH and 4E49H call here.
5123
If the NZ FLAG is set (the field is not there), JUMP to 5140H to fill all Register B places with spaces. 4E44H calls here with Register B = 3.
5125
GOSUB to 5146H to fetch the first character of the field into Register A (in upper case). The CARRY FLAG is set if it is not a letter.
5128
If the CARRY FLAG is set (the first character is not a letter), JUMP to 513BH to return error 30H.

Copy Loop
Register A = a character of the field, Register B = the places left (counting this one).

512A
LD (HL),A 77
Store Register A (the character) at Register Pair HL in the field.
512B
INC HL 23
INCrement Register Pair HL to the next place in the field.
512C
GOSUB to 5146H to fetch the next character into Register A. The NO CARRY FLAG is set for a letter.
512F
If the NO CARRY FLAG is set (a letter), JUMP to 5139H to store it.
5131
CP 30H FE 30
Compare Register A (the character) against 30H (0).
5133
If the CARRY FLAG is set (below 0), the field has ended: JUMP to 5143H to pad it.
5135
CP 3AH FE 3A
Compare Register A (the character) against 3AH, one past 9.
5137
If the NO CARRY FLAG is set (not a digit), the field has ended: JUMP to 5143H to pad it.
5139
DECrement Register B (the places left) and, while it is not 0, LOOP BACK to 512AH to store the letter or digit. When it reaches 0 the field is too long and the code goes on into 513BH.
513B
POP AF F1
Error 30H. Discard the return address of the call to 5121H or 5123H from the stack into Register Pair AF, so that the RET below returns from the open itself.
513C
LD A,30H 3E 30
Load Register A with error code 30H (BAD FILESPEC).
513E
OR A B7
OR Register A (30H) with itself to set the NZ FLAG.
513F
RET C9
RETurn from the open with error 30H in Register A.

Pad Loop
Register B = the places left plus 1.

5140
LD (HL),20H 36 20
Store 20H (a space) at Register Pair HL in the field. 5123H comes here.
5142
INC HL 23
INCrement Register Pair HL to the next place in the field.
5143
DECrement Register B (the places left) and LOOP BACK to 5140H while it is not 0. 5133H and 5137H come here.
5145
RET C9
RETurn with Register A = the character after the field and Register Pair HL = the place after the field.

5146H - Fetch the Next Filespec Character

Moves Register Pair DE on to the next character and returns it, in upper case, in Register A, with the NO CARRY FLAG if it is a letter A-Z and the CARRY FLAG otherwise.

5146
INC DE 13
INCrement Register Pair DE to the next character of the filespec. 5125H and 512CH call here.
5147
LD A,(DE) 1A
Fetch the character at Register Pair DE into Register A.
5148
GOSUB to 4548H (SYS0/SYS) to change Register A to upper case if it is a lower case letter.
514B
CP 41H FE 41
Compare Register A (the character) against 41H (A). The CARRY FLAG is set below A.
514D
RET C D8
If the CARRY FLAG is set (below A, not a letter), RETurn with the CARRY FLAG.
514E
CP 5BH FE 5B
Compare Register A (the character) against 5BH, one past Z. The CARRY FLAG is set for A-Z.
5150
CCF 3F
Complement the CARRY FLAG: the NO CARRY FLAG for A-Z, the CARRY FLAG above Z.
5151
RET C9
RETurn with the character in Register A and the CARRY FLAG showing whether it is a letter.

5152H - Function 84H: Encode a Password

Works out the 16-bit code of the 8 characters ending at Register Pair HL (5152H: the password at 51D8H-51DFH), going from the last character to the first, and sets the characters to spaces. Returns Register Pair HL = the code; Register Pairs BC and DE are kept. A blank password gives 4296H, PASSWORD gives 42E0H. Through RST 28H (code 84H) the caller's Register Pair HL points to the last character and the code is returned in Register Pair HL.

5152
LD HL,51DFH 21 DF 51
Point Register Pair HL to 51DFH, the last character of the parsed password. 4E84H calls here.
5155
PUSH DE D5
Save Register Pair DE onto the stack. Function 84H (4D11H) enters here with Register Pair HL pointing to the last of the 8 characters.
5156
PUSH BC C5
Save Register Pair BC (the caller's) onto the stack; 5185H restores it.
5157
LD DE,FFFFH 11 FF FF
Load Register Pair DE with FFFFH, the starting code: Register D = the high byte, Register E = the low byte.
515A
LD B,08H 06 08
Load Register B with 08H, the number of characters.

Encode Loop
Register Pair DE = the code so far, Register Pair HL = the character, Register B = the characters left.

515C
PUSH BC C5
Save Register Pair BC (Register B = the characters left) onto the stack, freeing Register B and Register C for the work.
515D
LD A,E 7B
Copy Register E (the low byte of the code) into Register A.
515E
AND 07H E6 07
AND Register A (the low byte of the code) with 07H, keeping bits 0-2.
5160
LD C,A 4F
Copy Register A (bits 0-2 of the low byte) into Register C.
5161
LD A,E 7B
Copy Register E (the low byte of the code) into Register A again.
5162
RLCA 07
Rotate Register A left (the first of three RLCAs).
5163
RLCA 07
Rotate Register A left (the second of three RLCAs).
5164
RLCA 07
Rotate Register A left (the third): Register A = the low byte rotated left 3 bits.
5165
XOR C A9
XOR Register C (bits 0-2 of the low byte) into Register A.
5166
RLCA 07
Rotate Register A left once more: the mixed value used for both new bytes.
5167
LD C,A 4F
Copy Register A (the mixed value) into Register C.
5168
AND F0H E6 F0
AND Register A with F0H, keeping bits 4-7 of the mixed value.
516A
LD B,A 47
Copy Register A (bits 4-7 of the mixed value) into Register B.
516B
LD A,C 79
Copy Register C (the mixed value) into Register A.
516C
RLCA 07
Rotate Register A (the mixed value) left one bit, bit 7 going into bit 0.
516D
AND 1FH E6 1F
AND Register A with 1FH, keeping bits 0-4 of the rotated mixed value.
516F
XOR B A8
XOR Register B (bits 4-7 of the mixed value) into Register A.
5170
XOR D AA
XOR Register D (the high byte of the code) into Register A.
5171
LD E,A 5F
Copy Register A into Register E: the new low byte of the code.
5172
LD A,C 79
Copy Register C (the mixed value) into Register A.
5173
AND 0FH E6 0F
AND Register A (the mixed value) with 0FH, keeping bits 0-3.
5175
LD B,A 47
Copy Register A (bits 0-3 of the mixed value) into Register B.
5176
LD A,C 79
Copy Register C (the mixed value) into Register A.
5177
RLCA 07
Rotate Register A left (the first of four RLCAs).
5178
RLCA 07
Rotate Register A left (the second of four RLCAs).
5179
RLCA 07
Rotate Register A left (the third of four RLCAs).
517A
RLCA 07
Rotate Register A left (the fourth): the two halves of the mixed value are swapped.
517B
XOR B A8
XOR Register B (bits 0-3 of the mixed value) into Register A.
517C
POP BC C1
Restore Register Pair BC from the stack: Register B = the characters left.
517D
XOR (HL) AE
XOR the character at Register Pair HL into Register A.
517E
LD D,A 57
Copy Register A into Register D: the new high byte of the code.
517F
LD (HL),20H 36 20
Store 20H (a space) at Register Pair HL in place of the character, so that the password text is not left in memory.
5181
DEC HL 2B
DECrement Register Pair HL to the character before.
5182
DECrement Register B (the characters left) and LOOP BACK to 515CH until all 8 are done.
5184
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the code.
5185
POP BC C1
Restore Register Pair BC from the stack (the caller's, saved at 5156H).
5186
POP DE D1
Restore Register Pair DE from the stack (the caller's, saved at 5155H).
5187
RET C9
RETurn to the caller with the 16-bit password code in Register Pair HL.

5188H - Unused Bytes

The end of the file. Nothing in SYS2/SYS reaches these bytes.

5188-51AC
NOP x 37 00 x 37
37 bytes of 00H to the end of the file (51ACH). 51ADH-51FFH is not in the file; SYS2/SYS uses it as work space (see Variables).