TRS-80 DOS - TRSDOS v2.3 for the Model I - SYS2/SYS Disassembled

Page Customization

Introduction/Summary

TRSDOS v2.3 SYS2/SYS Disassembly - Opening Files (Model I)

SYS2/SYS is the TRSDOS 2.3 system overlay that opens files. SYS0/SYS loads it into the overlay area at 4E00H whenever an RST 28H code with 4 in bits 0-3 is executed, and enters it at 4E00H with that code in Register A. It does three jobs: open an existing file (SYS0/SYS's 4424H), open a file or create it when it is not found (4420H), and make a continuation directory entry when a file needs a new extent and only its entry's last extent field is left, which SYS0/SYS's granule allocation asks for while writing.

The file holds 853 bytes in four load records covering 4E00H-5154H, after a name record and a 169-byte copyright comment record; its transfer address is 4E00H. The work area 5155H-516BH after the last byte is not in the file. Every byte is shown once on this page, in address order, checked against S:\1\SYS2.SYS and its listing.

Memory Map

Address RangeUse
4E00H-4E11H
18 bytes
Function dispatcher on bits 4-6 of the RST 28H code.
4E12H-4ED7H
198 bytes
Open an existing file: parse, hash, search drives, check the password, fill the FCB.
4ED8H-4F4FH
120 bytes
Open or create: the open code, then create a new file.
4F50H-4FA6H
87 bytes
Make a continuation directory entry.
4FA7H-5026H
128 bytes
Build the FCB from a directory entry.
5027H-5080H
90 bytes
Parse a filespec into the name, password and drive work area.
5081H-509AH
26 bytes
Copy one field of a filespec.
509BH-50AAH
16 bytes
Hash a file name to one byte.
50ABH-50D0H
38 bytes
Find a free entry in the hash index.
50D1H-50FCH
44 bytes
Hash an eight-byte password to a word.
50FDH-512DH
49 bytes
Test whether a drive is ready.
512EH-5153H
38 bytes
Read and write the hash index table.
5154H
1 byte
The drive of the filespec (last byte of the file).
5155H-516BH
23 bytes
Password, name template and password hashes, not in the file.

Functions

Bits 0-3 of every code below are 4 (SYS2/SYS); bits 4-6 are the function that 4E00H tests. SYS0/SYS's fixed entry points supply the codes.

CodeEntered FromFunction
94H4424HOpen the existing file named in the FCB at Register Pair DE (4E12H).
A4H4420HOpen the file, or create it when it is not in the directory (4ED8H).
B4H4A5CHMake a continuation directory entry for the file in Index Register IX (4F50H), for SYS0/SYS's granule allocation.

The Directory and the Hash Index

A file is found through the hash index table in directory sector 1, read into 4D00H-4DFFH. The table has one byte for each directory entry, at the offset of the entry's locator (bits 5-7 the entry in its sector, bits 0-4 the sector minus 2): 00H for a free entry, otherwise the one-byte hash of the file name. To open a file, SYS2/SYS hashes the typed name and walks the table; each byte that matches the hash is a candidate, and its directory entry is read and its eleven name bytes compared to be sure. To create one, the table is searched for a 00H byte, starting at a point taken from the clock so new files spread through the directory.

A directory entry is 32 bytes: byte 0 attributes (bits 0-2 the access level, bit 7 set on a continuation entry), byte 1 the primary entry's locator on a continuation entry, byte 3 the end-of-file byte, byte 4 the logical record length, bytes 5-15 the eleven-byte name and extension, bytes 16-19 the owner and user password hashes, bytes 20-21 the ending record number, and bytes 22-31 five two-byte extents (track, then a byte whose bits 5-7 are the first granule and bits 0-4 one less than the granule count). A track byte of FEH means the file is continued in the entry whose locator follows; FFH ends the extents.

Passwords

Each file carries two sixteen-bit password hashes in its directory entry, the owner password and the user password, computed by the hash at 50D1H. When a file is opened the typed password is hashed and compared with both; the owner password opens the file at access level 0, the user password at the level in the entry. A typed password whose hash is 61A2H opens any file whatever its own password, and a file at access level 7 cannot be opened without it.

Notes from the Code

  • The scan at 4E49H reads the hash index table at 4D00H, not the directory entries; only a hash that matches leads to a directory entry being read and its name compared.
  • The name hash at 509BH is one byte and the password hash at 50D1H is two, so different names or passwords can share a hash; the name is always confirmed byte by byte, but a password is accepted on its hash alone.
  • The hash 61A2H typed as a password opens any file (4EA5H). An all-spaces password hashes to 4296H, so this is a separate master value, not the hash of a blank password.
  • The create path at 4EF4H skips write-protected diskettes; the open path does not test write protect.
  • A new file's directory entry is given attribute byte 10H and its owner and user password words both hold the hash of the password typed when it was created (4F35H).

Variables

Work Area and Self-Modified Operands

Address RangePurpose
4E4EH
1 byte
Operand of CP nn at 4E4DH: the name hash the index scan looks for (set at 4E29H).
4F1DH
1 byte
Operand of LD (HL),nn at 4F1CH: a new entry's attribute byte, 10H (set at 4EE4H).
4FA2H
1 byte
Operand of LD (HL),nn at 4FA1H: the continuation locator stored in the primary entry (set at 4F62H).
4FAFH
1 byte
Operand of LD A,nn at 4FAEH: the logical record length (set at 4E16H).
4FB2H
1 byte
Operand of LD A,nn at 4FB1H: the access level the password granted (set at 4ED0H).
4FC0H-4FC1H
2 bytes
Operand of LD DE,nn at 4FBFH: the caller's buffer address for the FCB (set at 4E19H).
5154H
1 byte
The drive of the filespec, FFH for none (5027H).
5155H-515CH
8 bytes
Password buffer, not in the file.
515DH-5167H
11 bytes
Name and extension template, not in the file.
5168H-5169H
2 bytes
Password hash, not in the file.
516AH-516BH
2 bytes
Password hash copy, not in the file.

Addresses Outside SYS2/SYS

Address RangeUse
4040H
1 byte
Clock tick counter, read at 50ABH to vary where a new entry is placed.
4200H-42FFH
256 bytes
System buffer where SYS0/SYS's 4AC1H and 4AD6H read and write a directory sector.
4ABEH
1 byte
The primary entry's locator, left by SYS0/SYS at 49BDH and read at 4F77H and 4F92H.
4D00H-4DFFH
256 bytes
The hash index table (directory sector 1), read and written by 512EH and 5141H.
37ECH
1 byte
Floppy controller command and status register, used by the drive-ready test at 50FDH.
4896H, 4AC1H, 4AD6H, 4B35H, 4B55H, 4600H, 46EFH
SYS0/SYS
File call set-up, read and write a directory sector, read and write a system sector, select a drive, write with the FAH mark.

Disassembly

4E00H - Function Dispatcher

SYS0/SYS's overlay call (4BFBH) enters SYS2/SYS here with Register A = the RST 28H code whose bits 0-3 are 4. Bits 4-6 select the function: 10H (code 94H, 4424H) opens an existing file, 20H (code A4H, 4420H) opens a file or creates it when it is not found, and 30H (code B4H) makes a continuation directory entry for SYS0/SYS's granule allocation (4A5CH). Register Pair DE = the FCB; for 30H Index Register IX = the FCB whose extents overflowed.

4E00
AND 70H E6 70
AND Register A (the RST 28H code) with 70H, keeping bits 4-6, the function number.
4E02
CP 10H FE 10
Compare Register A (the function) against 10H, open an existing file (code 94H).
4E04
If the Z FLAG is set (10H), JUMP to 4E12H to open an existing file.
4E07
CP 20H FE 20
Compare Register A (the function) against 20H, open or create a file (code A4H).
4E09
If the Z FLAG is set (20H), JUMP to 4ED8H to open or create a file.
4E0C
CP 30H FE 30
Compare Register A (the function) against 30H, make a continuation directory entry (code B4H).
4E0E
If the Z FLAG is set (30H), JUMP to 4F50H to make a continuation entry.
4E11
RET C9
RETURN to SYS0/SYS (4BFEH) for any other function number, doing nothing.

4E12H - Open an Existing File (4424H)

Register Pair DE = the FCB holding the filespec as text. Parses the name, computes its hash and the password hash, then searches drives for a directory entry whose hash, name and password match and fills the FCB from it. Returns Register A = 00H and the Z FLAG with the file open, or an error number and the NZ FLAG: 13H (ILLEGAL FILE NAME), 16H (HIT READ ERROR), 11H (DIRECTORY READ ERROR), 18H (FILE NOT IN DIRECTORY) or 19H (FILE ACCESS DENIED).

4E12
GOSUB to SYS0/SYS's 4896H, the file call set-up without the open check: Index Register IX = the FCB at Register Pair DE, the caller's registers are saved and 48B3H is set as the return.
4E15
LD A,B 78
Copy Register B into Register A: the logical record length the caller asked for (00H means whole 256-byte sectors). 4EDBH calls here from the create path.
4E16
LD (4FAFH),A 32 AF 4F
Store Register A (the logical record length) into 4FAFH, the operand of LD A,nn at 4FAEH, read by 4FA7H when it builds the FCB and written into a new entry at 4F28H. Self-Modifying Code
4E19
LD (4FC0H),HL 22 C0 4F
Store Register Pair HL (SYS0/SYS's return) into 4FC0H, the operand of LD DE,nn at 4FBFH. Self-Modifying Code
4E1C
PUSH IX DD E5
PUSH Index Register IX (the FCB) onto the stack.
4E1E
POP HL E1
POP Register Pair HL from the stack: Register Pair HL = the FCB, the text to parse.
4E1F
GOSUB to 5027H to parse the filespec at Register Pair HL into the name template at 515DH, the password at 5155H and the drive at 5154H.
4E22
RET NZ C0
If the NZ FLAG is set (the name is not valid), RETURN with Register A = 13H.
4E23
LD HL,515DH 21 5D 51
Point Register Pair HL to 515DH, the eleven-byte name and extension template.
4E26
GOSUB to 509BH to compute the directory hash of the name into Register A (never 00H).
4E29
LD (4E4EH),A 32 4E 4E
Store Register A (the name hash) into 4E4EH, the operand of CP nn at 4E4DH that scans the hash index. Self-Modifying Code
4E2C
LD DE,5155H 11 55 51
Point Register Pair DE to 5155H, the eight-byte password.
4E2F
GOSUB to 50D1H to compute the password hash into Register Pair HL.
4E32
LD (5168H),HL 22 68 51
Store Register Pair HL (the password hash) into 5168H, kept for the create path.
4E35
LD (516AH),HL 22 6A 51
Store Register Pair HL (the password hash) into 516AH, read by the password check at 4EA0H.
4E38
LD A,(5154H) 3A 54 51
Fetch the drive from 5154H into Register A: the drive in the filespec, or FFH when none was given.
4E3B
LD C,A 4F
Copy the drive from Register A into Register C.
4E3C
INC A 3C
INCrement Register A by 1: FFH (no drive) becomes 00H and sets the Z FLAG.
4E3D
If the NZ FLAG is set (a drive was given), JUMP to 4E40H with Register C = that drive.
4E3F
LD C,A 4F
Copy Register A (00H) into Register C: start the search at drive 0.
4E40
Search a Drive
GOSUB to 50FDH to test whether drive Register C is ready. 4E5EH loops back here for the next drive.
4E43
If the NZ FLAG is set (the drive is not ready), JUMP to 4E54H to try the next drive.
4E45
GOSUB to 512EH to read the hash index table (directory sector 1) of drive Register C into 4D00H.
4E48
RET NZ C0
If the NZ FLAG is set (16H, HIT READ ERROR), RETURN with that error.
4E49
LD A,(HL) 7E
Scan the Hash Index
Fetch a hash byte from the table at (HL) (Register Pair HL = 4D00H plus the entry locator) into Register A. 4E52H loops back here.
4E4A
OR A B7
OR Register A with itself: the Z FLAG is set for 00H, an entry that is not in use.
4E4B
If the Z FLAG is set (an empty slot), JUMP to 4E51H to step to the next entry.
4E4D
CP 00H FE 00
Compare Register A (the entry's hash) against the name hash stored at 4E4EH. Self-Modifying Code
00H in the file.
4E4F
If the Z FLAG is set (the hashes match), JUMP to 4E6AH to look at the entry.
4E51
INC L 2C
INCrement Register L (the entry locator) by 1. 4E4BH and 4E68H come here.
4E52
Scan End
If the NZ FLAG is set (Register L has not wrapped past 255), LOOP BACK to 4E49H.
4E54
LD A,(5154H) 3A 54 51
Next Drive
Fetch the drive from 5154H into Register A again. 4E43H comes here.
4E57
INC A 3C
INCrement Register A by 1: FFH (no drive) becomes 00H and sets the Z FLAG.
4E58
If the NZ FLAG is set (a single drive was given), JUMP to 4E60H to report the file is not found.
4E5A
INC C 0C
INCrement Register C (the drive number) by 1, to the next drive.
4E5B
LD A,C 79
Copy the drive from Register C into Register A.
4E5C
CP 04H FE 04
Compare Register A (the drive) against 04H, one past the last drive.
4E5E
If the CARRY FLAG is set (drive 0-3), LOOP BACK to 4E40H to search it.
4E60
LD A,18H 3E 18
Load Register A with 18H, error FILE NOT IN DIRECTORY. 4E58H comes here.
4E62
OR A B7
OR Register A (18H) with itself, setting the NZ FLAG.
4E63
RET C9
RETURN with Register A = 18H and the NZ FLAG.
4E64
POP BC C1
Skip This Entry
Restore Register Pair BC from the stack. 4E79H and 4E86H jump here.
4E65
POP HL E1
Restore Register Pair HL from the stack, dropping the entry saved at 4E6AH.
4E66
POP BC C1
Restore Register Pair BC from the stack, the drive saved at 4E6BH.
4E67
POP HL E1
Restore Register Pair HL (the position in the hash index) from the stack.
4E68
JUMP to 4E51H to go on scanning the hash index.
4E6A
PUSH HL E5
Hash Matches: Read the Entry
Save Register Pair HL (the position in the hash index) onto the stack. 4E4FH comes here.
4E6B
PUSH BC C5
Save Register Pair BC (Register C = drive) onto the stack.
4E6C
LD B,L 45
Copy the entry locator from Register L into Register B.
4E6D
GOSUB to SYS0/SYS's 4AC1H to read the directory entry with locator Register B of drive Register C into 4200H; Register Pair HL = the entry.
4E70
If the Z FLAG is set (the entry was read), JUMP to 4E75H to check it.
4E72
POP BC C1
Restore Register Pair BC from the stack, the drive saved at 4E6BH.
4E73
POP HL E1
Restore Register Pair HL from the stack, the hash position saved at 4E6AH.
4E74
RET C9
RETURN with Register A = 11H (DIRECTORY READ ERROR) and the NZ FLAG.
4E75
PUSH HL E5
Check the Name
Save Register Pair HL (the entry) onto the stack. 4E70H comes here.
4E76
PUSH BC C5
Save Register Pair BC (Register C = the drive) onto the stack.
4E77
BIT 7,(HL) CB 7E
Test bit 7 of (HL) (Register Pair HL = the entry's attribute byte): set for a continuation entry (90H), which is not a file name.
4E79
If the NZ FLAG is set (a continuation entry), JUMP to 4E64H to go on scanning.
4E7B
LD A,05H 3E 05
Load Register A with 05H, the offset of the name in the entry.
4E7D
ADD A,L 85
ADD Register L (the entry's low byte) to Register A.
4E7E
LD L,A 6F
Copy the sum from Register A into Register L (Register H is unchanged): Register Pair HL = the entry's name field.
4E7F
LD DE,515DH 11 5D 51
Point Register Pair DE to 515DH, the name template.
4E82
LD B,0BH 06 0B
Load Register B with 0BH, the eleven bytes of the name and extension.
4E84
LD A,(DE) 1A
Compare Loop
Fetch a template byte from (DE) (Register Pair DE = the template) into Register A. 4E8AH loops back here.
4E85
CP (HL) BE
Compare Register A (the template byte) against the entry's name byte at (HL) (Register Pair HL = the entry's name field).
4E86
If the NZ FLAG is set (the names differ), JUMP to 4E64H to go on scanning.
4E88
INC HL 23
INCrement Register Pair HL (the entry pointer) by 1.
4E89
INC DE 13
INCrement Register Pair DE (the template pointer) by 1.
4E8A
Compare End
DECrement Register B and LOOP BACK to 4E84H until all eleven bytes match.
4E8C
POP BC C1
Restore Register Pair BC (Register C = the drive the file is on) from the stack.
4E8D
LD A,C 79
Copy the drive from Register C into Register A.
4E8E
LD (5154H),A 32 54 51
Store Register A (the drive) into 5154H, the drive the FCB will record.
4E91
POP HL E1
Restore Register Pair HL (the entry) from the stack.
4E92
POP AF F1
Restore Register Pair AF from the stack, dropping the locator saved at 4E6AH.
4E93
POP AF F1
Restore Register Pair AF from the stack, dropping the drive saved at 4E6BH.
4E94
PUSH BC C5
Check the Password
Save Register Pair BC onto the stack.
4E95
PUSH HL E5
Save Register Pair HL (the entry) onto the stack.
4E96
LD A,(HL) 7E
Fetch the entry's attribute byte from (HL) (Register Pair HL = the entry) into Register A.
4E97
AND 07H E6 07
AND Register A with 07H, keeping the file's access level (bits 0-2).
4E99
LD C,A 4F
Copy the access level from Register A into Register C.
4E9A
LD B,00H 06 00
Load Register B with 00H, the access level that an open password grants by default.
4E9C
LD A,10H 3E 10
Load Register A with 10H, the offset of the password words in the entry.
4E9E
ADD A,L 85
ADD Register L (the entry's low byte) to Register A.
4E9F
LD L,A 6F
Copy the sum from Register A into Register L (Register H is unchanged): Register Pair HL = the entry's owner password word.
4EA0
LD DE,(516AH) ED 5B 6A 51
Fetch the typed password hash from 516AH into Register Pair DE.
4EA4
PUSH HL E5
Save Register Pair HL (the password word pointer) onto the stack.
4EA5
LD HL,61A2H 21 A2 61
Load Register Pair HL with 61A2H, the hash that opens any file whatever its password.
4EA8
XOR A AF
Set Register A to ZERO and clear the CARRY FLAG.
4EA9
SBC HL,DE ED 52
SUBtract Register Pair DE (the typed hash) and the CARRY FLAG from Register Pair HL (61A2H): zero when the typed password hashes to 61A2H.
4EAB
POP HL E1
Restore Register Pair HL (the password word pointer) from the stack.
4EAC
If the Z FLAG is set (the master hash was typed), JUMP to 4ECFH to open the file.
4EAE
LD A,C 79
Copy the access level from Register C into Register A.
4EAF
CP 07H FE 07
Compare Register A (the access level) against 07H, the level that denies all access.
4EB1
If the Z FLAG is set (level 7), JUMP to 4EC9H to deny access.
4EB3
LD A,(HL) 7E
Fetch the low byte of the owner password word from (HL) (Register Pair HL = the entry's owner password word) into Register A.
4EB4
INC HL 23
INCrement Register Pair HL by 1, to the high byte.
4EB5
PUSH HL E5
Save Register Pair HL (the owner word pointer) onto the stack.
4EB6
LD H,(HL) 66
Fetch the high byte of the owner password word from (HL) (Register Pair HL = the owner word plus 1) into Register H.
4EB7
LD L,A 6F
Copy the low byte from Register A into Register L: Register Pair HL = the owner password hash.
4EB8
XOR A AF
Set Register A to ZERO and clear the CARRY FLAG.
4EB9
SBC HL,DE ED 52
SUBtract Register Pair DE (the typed hash) and the CARRY FLAG from Register Pair HL (the owner hash): zero when they match.
4EBB
POP HL E1
Restore Register Pair HL (the owner word pointer) from the stack.
4EBC
If the Z FLAG is set (the owner password matches), JUMP to 4ECFH to open the file.
4EBE
INC HL 23
INCrement Register Pair HL by 1, to the user password word.
4EBF
LD B,C 41
Copy the access level from Register C into Register B: the level the FCB records when the user password is used.
4EC0
LD A,(HL) 7E
Fetch the low byte of the user password word from (HL) (Register Pair HL = the entry's user password word) into Register A.
4EC1
INC HL 23
INCrement Register Pair HL by 1, to the high byte.
4EC2
LD H,(HL) 66
Fetch the high byte of the user password word from (HL) (Register Pair HL = the user word plus 1) into Register H.
4EC3
LD L,A 6F
Copy the low byte from Register A into Register L: Register Pair HL = the user password hash.
4EC4
XOR A AF
Set Register A to ZERO and clear the CARRY FLAG.
4EC5
SBC HL,DE ED 52
SUBtract Register Pair DE (the typed hash) and the CARRY FLAG from Register Pair HL (the user hash): zero when they match.
4EC7
If the Z FLAG is set (the user password matches), JUMP to 4ECFH to open the file.
4EC9
POP HL E1
Access Denied
Restore Register Pair HL from the stack. 4EB1H comes here.
4ECA
POP BC C1
Restore Register Pair BC from the stack, saved at 4E94H.
4ECB
LD A,19H 3E 19
Load Register A with 19H, error FILE ACCESS DENIED.
4ECD
OR A B7
OR Register A (19H) with itself, setting the NZ FLAG.
4ECE
RET C9
RETURN with Register A = 19H and the NZ FLAG.
4ECF
POP HL E1
Fill the FCB
Restore Register Pair HL (the entry) from the stack. 4EACH, 4EBCH and 4EC7H come here.
4ED0
LD A,B 78
Copy Register B (the access level the password granted) into Register A.
4ED1
LD (4FB2H),A 32 B2 4F
Store Register A (the access level) into 4FB2H, the operand of LD A,nn at 4FB1H, read by 4FA7H. Self-Modifying Code
4ED4
POP BC C1
Restore Register Pair BC from the stack, saved at 4E94H.
4ED5
JUMP to 4FA7H to build the FCB from the entry at Register Pair HL and return.

4ED8H - Open or Create a File (4420H)

Register Pair DE = the FCB holding the filespec, Register B = the logical record length. Opens the file through the same code as 4E12H; when it is not in the directory a new file is created. Returns Register A = 00H and the Z FLAG with the file open, the Z FLAG and the CARRY FLAG with a new file created, or an error number and the NZ FLAG: the open errors, 1AH (DIRECTORY SPACE FULL) or 12H (DIRECTORY WRITE ERROR).

4ED8
GOSUB to SYS0/SYS's 4896H, the file call set-up: Index Register IX = the FCB at Register Pair DE.
4EDB
GOSUB to 4E15H to run the open from its second instruction: parse the name, search the drives and fill the FCB if the file is found.
4EDE
RET Z C8
If the Z FLAG is set (the file was opened), RETURN with Register A = 00H.
4EDF
CP 18H FE 18
Compare Register A (the error from the open) against 18H, FILE NOT IN DIRECTORY.
4EE1
RET NZ C0
If the NZ FLAG is set (any other error), RETURN with that error.
4EE2
LD A,10H 3E 10
Load Register A with 10H, the attribute byte of a new file.
4EE4
LD (4F1DH),A 32 1D 4F
Store Register A (10H) into 4F1DH, the operand of LD (HL),nn at 4F1CH that writes the new entry's attribute byte. Self-Modifying Code
4EE7
LD A,(5154H) 3A 54 51
Fetch the drive from 5154H into Register A: the drive in the filespec, or FFH when none was given.
4EEA
LD C,A 4F
Copy the drive from Register A into Register C.
4EEB
INC A 3C
INCrement Register A by 1: FFH (no drive) becomes 00H and sets the Z FLAG.
4EEC
If the NZ FLAG is set (a drive was given), JUMP to 4EEFH with Register C = that drive.
4EEE
LD C,A 4F
Copy Register A (00H) into Register C: create on drive 0.
4EEF
Try a Drive
GOSUB to 50FDH to test whether drive Register C is ready. 4F09H loops back here.
4EF2
If the NZ FLAG is set (the drive is not ready), JUMP to 4EFFH to try the next drive.
4EF4
If the CARRY FLAG is set (the diskette is write protected), JUMP to 4EFFH: a file cannot be created on it.
4EF6
GOSUB to 512EH to read the hash index table of drive Register C into 4D00H.
4EF9
RET NZ C0
If the NZ FLAG is set (16H, HIT READ ERROR), RETURN with that error.
4EFA
GOSUB to 50ABH to find a free entry in the hash index: Register Pair HL = 4D00H plus the free locator and the Z FLAG, or the NZ FLAG when the table is full.
4EFD
If the Z FLAG is set (a free entry was found), JUMP to 4F0EH to create the file.
4EFF
LD A,(5154H) 3A 54 51
Next Drive
Fetch the drive from 5154H into Register A. 4EF2H and 4EF4H come here.
4F02
INC A 3C
INCrement Register A by 1: FFH (no drive) becomes 00H and sets the Z FLAG.
4F03
If the NZ FLAG is set (a single drive was given), JUMP to 4F0BH to report the directory is full.
4F05
INC C 0C
INCrement Register C (the drive number) by 1, to the next drive.
4F06
LD A,C 79
Copy the drive from Register C into Register A.
4F07
CP 04H FE 04
Compare Register A (the drive) against 04H, one past the last drive.
4F09
If the CARRY FLAG is set (drive 0-3), LOOP BACK to 4EEFH to try it.
4F0B
LD A,1AH 3E 1A
Load Register A with 1AH, error DIRECTORY SPACE FULL. 4F03H comes here.
4F0D
RET C9
RETURN with Register A = 1AH and the NZ FLAG.
4F0E
LD B,L 45
Create the File
Copy the free locator from Register L into Register B. 4EFDH comes here.
4F0F
LD A,(4E4EH) 3A 4E 4E
Fetch the name hash from 4E4EH into Register A.
4F12
LD (HL),A 77
Store Register A (the name hash) into the hash index at (HL) (Register Pair HL = 4D00H plus the free locator): the entry is now in use.
4F13
GOSUB to 5141H to write the hash index table (directory sector 1) back to the diskette.
4F16
RET NZ C0
If the NZ FLAG is set (17H, HIT WRITE ERROR), RETURN with that error.
4F17
GOSUB to SYS0/SYS's 4AC1H to read the directory sector holding entry Register B into 4200H; Register Pair HL = the entry.
4F1A
RET NZ C0
If the NZ FLAG is set (11H, DIRECTORY READ ERROR), RETURN with that error.
4F1B
PUSH HL E5
Save Register Pair HL (the entry) onto the stack.
4F1C
LD (HL),00H 36 00
Store 10H (the operand set at 4EE4H) into the entry's attribute byte at (HL) (Register Pair HL = the entry). Self-Modifying Code
00H in the file.
4F1E
INC HL 23
INCrement Register Pair HL (the entry pointer) by 1, to byte 1.
4F1F
LD (HL),00H 36 00
Store 00H into the entry at (HL) (Register Pair HL = entry+01H).
4F21
INC HL 23
INCrement Register Pair HL (the entry pointer) by 1, to byte 2 of the entry.
4F22
LD (HL),00H 36 00
Store 00H into the entry at (HL) (Register Pair HL = entry+02H).
4F24
INC HL 23
INCrement Register Pair HL by 1, to byte 3, the end-of-file byte.
4F25
LD (HL),00H 36 00
Store 00H into the entry at (HL) (Register Pair HL = entry+03H): the file is empty.
4F27
INC HL 23
INCrement Register Pair HL by 1, to byte 4, the logical record length.
4F28
LD A,(4FAFH) 3A AF 4F
Fetch the logical record length from 4FAFH into Register A (the caller's Register B, saved at 4E16H).
4F2B
LD (HL),A 77
Store Register A (the logical record length) into the entry at (HL) (Register Pair HL = entry+04H).
4F2C
INC HL 23
INCrement Register Pair HL by 1, to byte 5, the name field.
4F2D
PUSH BC C5
Save Register Pair BC (Register B = the free locator) onto the stack.
4F2E
LD BC,000FH 01 0F 00
Load Register Pair BC with 000FH, the fifteen bytes of the name and the two password words.
4F31
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the entry's name field.
4F32
LD HL,515DH 21 5D 51
Point Register Pair HL to 515DH, the name template followed by the two copies of the password hash.
4F35
LDIR ED B0
Copy Register Pair BC (fifteen) bytes from Register Pair HL to Register Pair DE (LDIR): the eleven name bytes into entry+05H-0FH and the password hash into the owner and user words entry+10H-13H.
4F37
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = entry+14H, the ending record number.
4F38
LD (HL),00H 36 00
Store 00H into the entry at (HL) (Register Pair HL = entry+14H).
4F3A
INC HL 23
INCrement Register Pair HL by 1, to entry+15H.
4F3B
LD (HL),00H 36 00
Store 00H into the entry at (HL) (Register Pair HL = entry+15H): the ending record number is 0000H.
4F3D
INC HL 23
INCrement Register Pair HL by 1, to entry+16H, the first extent.
4F3E
LD B,0AH 06 0A
Load Register B with 0AH, the ten bytes of the five extents.
4F40
LD (HL),FFH 36 FF
Extent Loop
Store FFH into the entry at (HL) (Register Pair HL = entry+16H to +1FH). 4F43H loops back here.
4F42
INC HL 23
INCrement Register Pair HL (the entry pointer) by 1.
4F43
Extent Loop End
DECrement Register B and LOOP BACK to 4F40H: the five extents are all FFH, no granules.
4F45
POP BC C1
Restore Register Pair BC (Register B = the free locator) from the stack.
4F46
GOSUB to SYS0/SYS's 4AD6H to write the directory sector at 4200H back to the diskette.
4F49
POP HL E1
Restore Register Pair HL (the entry) from the stack.
4F4A
RET NZ C0
If the NZ FLAG is set (12H, DIRECTORY WRITE ERROR), RETURN with that error.
4F4B
GOSUB to 4FA7H to build the FCB from the new entry at Register Pair HL.
4F4E
SCF 37
Set the CARRY FLAG: the file was created, not opened.
4F4F
RET C9
RETURN with Register A = 00H, the Z FLAG and the CARRY FLAG.

4F50H - Make a Continuation Directory Entry (code B4H)

SYS0/SYS's granule allocation (4A5CH) calls this through RST 28H when a file needs a new extent and only the entry's last extent field (bytes 1EH-1FH) is left. Index Register IX = the FCB; its byte 06H is the drive and byte 07H the locator of the primary entry, whose locator SYS0/SYS also left in 4ABEH. A free directory entry is claimed, marked as a continuation (90H) that points back to the primary at byte 01H, and the primary's last extent is set to FEH and the new locator so the file's extents carry on in it. Returns the Z FLAG with the new locator in Register B, or 1EH (DIRECTORY FULL) and the NZ FLAG, or a directory read or write error.

4F50
LD C,(IX+06H) DD 4E 06
Fetch the drive from byte 06H of the FCB at (IX+06H) (Index Register IX = the FCB) into Register C.
4F53
GOSUB to 512EH to read the hash index table of drive Register C into 4D00H.
4F56
RET NZ C0
If the NZ FLAG is set (16H, HIT READ ERROR), RETURN with that error.
4F57
LD A,(IX+07H) DD 7E 07
Fetch the primary entry's locator from byte 07H of the FCB at (IX+07H) (Index Register IX = the FCB) into Register A.
4F5A
GOSUB to 50B6H to find a free entry in the hash index near Register A: Register Pair HL = 4D00H plus the free locator and the Z FLAG, or the NZ FLAG when the table is full.
4F5D
LD A,1EH 3E 1E
Load Register A with 1EH, error DIRECTORY FULL - CAN'T EXTEND FILE.
4F5F
RET NZ C0
If the NZ FLAG is set (no free entry), RETURN with Register A = 1EH.
4F60
LD B,L 45
Copy the free locator from Register L into Register B: the continuation entry's locator.
4F61
LD A,L 7D
Copy the free locator from Register L into Register A.
4F62
LD (4FA2H),A 32 A2 4F
Store Register A (the continuation locator) into 4FA2H, the operand of LD (HL),nn at 4FA1H that links the primary entry to it. Self-Modifying Code
4F65
LD D,H 54
Copy Register H (4DH, the high byte of the hash index) into Register D.
4F66
LD E,(IX+07H) DD 5E 07
Fetch the primary entry's locator from byte 07H of the FCB at (IX+07H) (Index Register IX = the FCB) into Register E: Register Pair DE = the primary's hash byte in the table.
4F69
LD A,(DE) 1A
Fetch the primary entry's hash byte from (DE) (Register Pair DE = 4D00H plus the primary locator) into Register A.
4F6A
LD (HL),A 77
Store Register A (the hash) into the hash index at (HL) (Register Pair HL = the continuation entry's byte): it carries the same hash as the primary.
4F6B
GOSUB to 5141H to write the hash index table back to the diskette.
4F6E
RET NZ C0
If the NZ FLAG is set (17H, HIT WRITE ERROR), RETURN with that error.
4F6F
GOSUB to SYS0/SYS's 4AC1H to read the directory sector holding entry Register B into 4200H; Register Pair HL = the continuation entry.
4F72
RET NZ C0
If the NZ FLAG is set (11H, DIRECTORY READ ERROR), RETURN with that error.
4F73
LD (HL),90H 36 90
Store 90H into the entry's attribute byte at (HL) (Register Pair HL = the entry): a continuation entry.
4F75
INC L 2C
INCrement Register L (the entry pointer) by 1, to byte 1.
4F76
PUSH BC C5
Save Register Pair BC (Register B = the continuation locator) onto the stack.
4F77
LD A,(4ABEH) 3A BE 4A
Fetch the primary entry's locator from 4ABEH into Register A (SYS0/SYS stored it there at 49BDH).
4F7A
LD (HL),A 77
Store Register A (the primary locator) into the entry at (HL) (Register Pair HL = entry+01H): the back link to the primary.
4F7B
INC L 2C
INCrement Register L (the entry pointer) by 1, to byte 2 of the entry.
4F7C
LD B,14H 06 14
Load Register B with 14H, the twenty bytes of entry+02H to +15H.
4F7E
LD (HL),00H 36 00
Clear Loop
Store 00H into the entry at (HL) (Register Pair HL = entry+02H to +15H). 4F81H loops back here.
4F80
INC L 2C
INCrement Register L (the entry pointer) by 1.
4F81
Clear Loop End
DECrement Register B and LOOP BACK to 4F7EH until twenty bytes are zero.
4F83
PUSH HL E5
Save Register Pair HL (entry+16H) onto the stack.
4F84
LD B,0AH 06 0A
Load Register B with 0AH, the ten bytes of the five extents.
4F86
LD (HL),FFH 36 FF
Extent Loop
Store FFH into the entry at (HL) (Register Pair HL = entry+16H to +1FH). 4F89H loops back here.
4F88
INC L 2C
INCrement Register L (the entry pointer) by 1.
4F89
Extent Loop End
DECrement Register B and LOOP BACK to 4F86H: the continuation entry's extents are all FFH.
4F8B
POP DE D1
Restore Register Pair DE (entry+16H) from the stack.
4F8C
INC DE 13
INCrement Register Pair DE by 1, to entry+17H.
4F8D
POP BC C1
Restore Register Pair BC (Register B = the continuation locator) from the stack.
4F8E
GOSUB to SYS0/SYS's 4AD6H to write the continuation entry's directory sector back to the diskette.
4F91
RET NZ C0
If the NZ FLAG is set (12H, DIRECTORY WRITE ERROR), RETURN with that error.
4F92
LD A,(4ABEH) 3A BE 4A
Fetch the primary entry's locator from 4ABEH into Register A.
4F95
LD B,A 47
Copy the primary locator from Register A into Register B.
4F96
GOSUB to SYS0/SYS's 4AC1H to read the directory sector holding the primary entry into 4200H; Register Pair HL = the primary entry.
4F99
RET NZ C0
If the NZ FLAG is set (11H, DIRECTORY READ ERROR), RETURN with that error.
4F9A
LD A,L 7D
Copy the primary entry's low byte from Register L into Register A.
4F9B
ADD A,1EH C6 1E
ADD 1EH to Register A: the offset of the entry's last extent.
4F9D
LD L,A 6F
Copy the sum from Register A into Register L: Register Pair HL = the primary entry's last extent.
4F9E
LD (HL),FEH 36 FE
Store FEH into the extent's track byte at (HL) (Register Pair HL = entry+1EH): the file is continued in another entry.
4FA0
INC L 2C
INCrement Register L (the entry pointer) by 1, to entry+1FH, the extent's second byte.
4FA1
LD (HL),00H 36 00
Store the continuation locator (the operand set at 4F62H) into the entry at (HL) (Register Pair HL = entry+1FH): where the file carries on. Self-Modifying Code
00H in the file.
4FA3
GOSUB to SYS0/SYS's 4AD6H to write the primary entry's directory sector back to the diskette.
4FA6
RET C9
RETURN with the result of the write in Register A.

4FA7H - Build the FCB from a Directory Entry

Register Pair HL = the directory entry in 4200H, Register C = drive, Register B = the entry locator; 4FAFH holds the logical record length, 4FB2H the access level the password granted and 4FC0H the buffer address the caller gave. Fills the 32-byte FCB at Index Register IX from the entry and returns Register A = 00H. The five four-byte extents are copied with a running count of the granules before each; a track byte of FEH (continued) or FFH (end) stops the copy and the rest of the extent area is set to FFH.

4FA7
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the directory entry. 4ED5H and 4F4BH come here.
4FA8
PUSH IX DD E5
PUSH Index Register IX (the FCB) onto the stack.
4FAA
POP HL E1
POP Register Pair HL from the stack: Register Pair HL = the FCB.
4FAB
LD (HL),80H 36 80
Store 80H into byte 0 of the FCB at (HL) (Register Pair HL = the FCB): bit 7 marks the file open.
4FAD
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 1.
4FAE
LD A,00H 3E 00
Load Register A with the logical record length from the operand at 4FAFH (the caller's Register B). Self-Modifying Code
00H in the file.
4FB0
OR A B7
OR Register A (the logical record length) with itself: the Z FLAG is set when it is 00H, whole sectors.
4FB1
LD A,00H 3E 00
Load Register A with the access level from the operand at 4FB2H (set at 4ED0H). Self-Modifying Code
00H in the file.
4FB3
If the Z FLAG is set (whole sectors), JUMP to 4FB7H.
4FB5
OR 80H F6 80
OR Register A (the access level) with 80H: bit 7 marks fixed-length logical records.
4FB7
OR 20H F6 20
OR Register A with 20H: bit 5 makes the first read reload the buffer. 4FB3H comes here.
4FB9
LD (HL),A 77
Store Register A (the flags) into byte 1 of the FCB at (HL) (Register Pair HL = FCB+01H).
4FBA
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 2.
4FBB
LD (HL),00H 36 00
Store 00H into byte 2 of the FCB at (HL) (Register Pair HL = FCB+02H).
4FBD
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 3.
4FBE
PUSH DE D5
Save Register Pair DE (the directory entry) onto the stack.
4FBF
LD DE,0000H 11 00 00
Load Register Pair DE with the buffer address from the operand at 4FC0H (saved at 4E19H). Self-Modifying Code
0000H in the file.
4FC2
LD (HL),E 73
Store Register E (the low byte of the buffer address) into byte 3 of the FCB at (HL) (Register Pair HL = FCB+03H).
4FC3
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 4.
4FC4
LD (HL),D 72
Store Register D (the high byte of the buffer address) into byte 4 of the FCB at (HL) (Register Pair HL = FCB+04H).
4FC5
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 5.
4FC6
POP DE D1
Restore Register Pair DE (the directory entry) from the stack.
4FC7
LD (HL),00H 36 00
Store 00H into byte 5 of the FCB at (HL) (Register Pair HL = FCB+05H): the byte offset is zero.
4FC9
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 6.
4FCA
LD (HL),C 71
Store Register C (the drive) into byte 6 of the FCB at (HL) (Register Pair HL = FCB+06H).
4FCB
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 7.
4FCC
LD (HL),B 70
Store Register B (the entry locator) into byte 7 of the FCB at (HL) (Register Pair HL = FCB+07H).
4FCD
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 8.
4FCE
LD A,03H 3E 03
Load Register A with 03H, the offset of the end-of-file byte in the entry.
4FD0
ADD A,E 83
ADD Register E (the entry's low byte) to Register A.
4FD1
LD E,A 5F
Copy the sum from Register A into Register E: Register Pair DE = entry+03H.
4FD2
LD A,(DE) 1A
Fetch the end-of-file byte from (DE) (Register Pair DE = entry+03H) into Register A.
4FD3
LD (HL),A 77
Store Register A (the end-of-file byte) into byte 8 of the FCB at (HL) (Register Pair HL = FCB+08H).
4FD4
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 9.
4FD5
INC DE 13
INCrement Register Pair DE by 1, to entry+04H.
4FD6
LD A,(4FAFH) 3A AF 4F
Load Register A with the logical record length from 4FAFH (the operand saved at 4E16H).
4FD9
LD (HL),A 77
Store Register A (the logical record length) into byte 9 of the FCB at (HL) (Register Pair HL = FCB+09H).
4FDA
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 0AH.
4FDB
LD (HL),00H 36 00
Store 00H into byte 0AH of the FCB at (HL) (Register Pair HL = FCB+0AH).
4FDD
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 0BH.
4FDE
LD (HL),00H 36 00
Store 00H into byte 0BH of the FCB at (HL) (Register Pair HL = FCB+0BH): the next record number is 0000H.
4FE0
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 0CH.
4FE1
LD A,10H 3E 10
Load Register A with 10H, the offset of the ending record number in the entry.
4FE3
ADD A,E 83
ADD Register E (the entry pointer's low byte) to Register A.
4FE4
LD E,A 5F
Copy the sum from Register A into Register E: Register Pair DE = entry+14H.
4FE5
LD A,(DE) 1A
Fetch the low byte of the ending record number from (DE) (Register Pair DE = entry+14H) into Register A.
4FE6
LD (HL),A 77
Store Register A into byte 0CH of the FCB at (HL) (Register Pair HL = FCB+0CH).
4FE7
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 0DH.
4FE8
INC DE 13
INCrement Register Pair DE by 1, to entry+15H.
4FE9
LD A,(DE) 1A
Fetch the high byte of the ending record number from (DE) (Register Pair DE = entry+15H) into Register A.
4FEA
LD (HL),A 77
Store Register A into byte 0DH of the FCB at (HL) (Register Pair HL = FCB+0DH).
4FEB
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 0EH.
4FEC
INC DE 13
INCrement Register Pair DE by 1, to entry+16H, the first extent.
4FED
LD A,(DE) 1A
Fetch the first extent's track from (DE) (Register Pair DE = entry+16H) into Register A.
4FEE
LD (HL),A 77
Store Register A (the track) into byte 0EH of the FCB at (HL) (Register Pair HL = FCB+0EH).
4FEF
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 0FH.
4FF0
INC DE 13
INCrement Register Pair DE by 1, to entry+17H.
4FF1
LD A,(DE) 1A
Fetch the first extent's granule byte from (DE) (Register Pair DE = entry+17H) into Register A.
4FF2
LD (HL),A 77
Store Register A (the granule byte) into byte 0FH of the FCB at (HL) (Register Pair HL = FCB+0FH).
4FF3
INC HL 23
INCrement Register Pair HL by 1, to FCB byte 10H.
4FF4
AND 1FH E6 1F
AND Register A (the granule byte) with 1FH, keeping bits 0-4, one less than the number of granules.
4FF6
INC A 3C
INCrement Register A by 1: the number of granules in the first extent.
4FF7
LD C,A 4F
Copy the granule count from Register A into Register C.
4FF8
LD B,00H 06 00
Load Register B with 00H: Register Pair BC = the granules before the next extent.
4FFA
LD A,04H 3E 04
Load Register A with 04H, the four extents still to copy.
4FFC
PUSH AF F5
Extent Loop
Save Register Pair AF (Register A = the count of extents left) onto the stack. 5019H loops back here.
4FFD
INC DE 13
INCrement Register Pair DE by 1, to the next extent's track.
4FFE
LD A,(DE) 1A
Fetch the extent's track from (DE) (Register Pair DE = the entry's extent) into Register A.
4FFF
CP FEH FE FE
Compare Register A (the track) against FEH, the mark of a continued file.
5001
If the NO CARRY FLAG is set (FEH continued or FFH end), JUMP to 501CH to fill the rest with FFH.
5003
LD (HL),C 71
Store Register C (the low byte of the running granule count) into the FCB at (HL) (Register Pair HL = the FCB extent).
5004
INC HL 23
INCrement Register Pair HL (the FCB pointer) by 1, to the extent's track byte.
5005
LD (HL),B 70
Store Register B (the high byte of the running granule count) into the FCB at (HL) (Register Pair HL = the FCB extent plus 1).
5006
INC HL 23
INCrement Register Pair HL (the FCB pointer) by 1, to the extent's granule byte position.
5007
LD A,(DE) 1A
Fetch the extent's track from (DE) (Register Pair DE = the entry's extent) into Register A.
5008
LD (HL),A 77
Store Register A (the track) into the FCB at (HL) (Register Pair HL = the FCB extent plus 2).
5009
INC HL 23
INCrement Register Pair HL (the FCB pointer) by 1, past the stored track byte.
500A
INC DE 13
INCrement Register Pair DE by 1, to the extent's granule byte.
500B
LD A,(DE) 1A
Fetch the extent's granule byte from (DE) (Register Pair DE = the entry's extent plus 1) into Register A.
500C
LD (HL),A 77
Store Register A (the granule byte) into the FCB at (HL) (Register Pair HL = the FCB extent plus 3).
500D
INC HL 23
INCrement Register Pair HL by 1, to the next FCB extent.
500E
AND 1FH E6 1F
AND Register A (the granule byte) with 1FH, keeping bits 0-4.
5010
INC A 3C
INCrement Register A by 1: the number of granules in this extent.
5011
ADD A,C 81
ADD Register C (the running count's low byte) to Register A.
5012
LD C,A 4F
Copy the new low byte from Register A into Register C.
5013
LD A,B 78
Copy the running count's high byte from Register B into Register A.
5014
ADC A,00H CE 00
ADD with carry 00H to Register A, carrying into the high byte.
5016
LD B,A 47
Copy the new high byte from Register A into Register B: Register Pair BC = the granules through this extent.
5017
POP AF F1
Restore Register Pair AF (Register A = the count of extents left) from the stack.
5018
DEC A 3D
DECrement Register A (the count of extents left) by 1.
5019
Extent Loop End
If the NZ FLAG is set (extents are left), LOOP BACK to 4FFCH.
501B
RET C9
RETURN with Register A = 00H: all five extents are in the FCB.
501C
POP AF F1
Fill Unused Extents
Restore Register Pair AF (Register A = the count of extents left) from the stack. 5001H comes here.
501D
RLCA 07
Rotate Register A left: the count of extents left times two.
501E
RLCA 07
Rotate Register A left again: the count times 4, the bytes left in the FCB extent area.
501F
LD B,A 47
Copy the byte count from Register A into Register B.
5020
LD (HL),FFH 36 FF
Fill Loop
Store FFH into the FCB at (HL) (Register Pair HL = the FCB extent area). 5023H loops back here.
5022
INC HL 23
INCrement Register Pair HL (the FCB pointer) by 1, to the next byte to fill.
5023
Fill Loop End
DECrement Register B and LOOP BACK to 5020H until the extent area is full of FFH.
5025
XOR A AF
Set Register A to ZERO and clear all flags: the FCB extent area is complete.
5026
RET C9
RETURN to the caller with Register A = 00H and the FCB filled.

5027H - Parse a Filespec into the Work Area

Register Pair HL = the filespec as text ending in 03H. Fills the name template at 515DH with eight characters of name and three of extension (padded with spaces), the password at 5155H with eight characters (padded with spaces) and the drive at 5154H. The name must start with a letter; later characters may be letters or digits. Returns Register A = 00H and the Z FLAG, or 13H (ILLEGAL FILE NAME) or 20H (ILLEGAL DRIVE NUMBER) and the NZ FLAG.

5027
LD B,0BH 06 0B
Load Register B with 0BH, the eleven bytes of the name template. 4E1FH calls here.
5029
LD DE,515DH 11 5D 51
Point Register Pair DE to 515DH, the name template.
502C
LD A,20H 3E 20
Load Register A with 20H, a space, to fill the template with.
502E
LD (DE),A 12
Fill Name Loop
Store the space (Register A) into the template at (DE) (Register Pair DE = the template). 502FH loops here.
502F
INC DE 13
INCrement Register Pair DE (the template pointer) by 1.
5030
DECrement Register B and LOOP BACK to 502EH until the eleven bytes are spaces.
5032
LD B,08H 06 08
Load Register B with 08H, the eight bytes of the password.
5034
LD DE,5155H 11 55 51
Point Register Pair DE to 5155H, the password buffer.
5037
LD (DE),A 12
Fill Password Loop
Store the space (Register A) into the password at (DE) (Register Pair DE = the password buffer). 5038H loops here.
5038
INC DE 13
INCrement Register Pair DE (the password pointer) by 1.
5039
DECrement Register B and LOOP BACK to 5037H until the eight bytes are spaces.
503B
LD A,FFH 3E FF
Load Register A with FFH, the mark of no drive.
503D
LD (5154H),A 32 54 51
Store Register A (FFH) into 5154H, the drive.
5040
LD DE,515DH 11 5D 51
Point Register Pair DE to 515DH, the name field of the template.
5043
LD B,08H 06 08
Load Register B with 08H, the most name characters.
5045
GOSUB to 5081H to copy the name from Register Pair HL to Register Pair DE; Register A returns the character that ended it and Register B the room left.
5048
LD C,A 4F
Copy the ending character from Register A into Register C.
5049
LD A,B 78
Copy the room left from Register B into Register A.
504A
CP 08H FE 08
Compare Register A (the room left) against 08H: equal when no name character was copied.
504C
If the NZ FLAG is set (a name was copied), JUMP to 5052H.
504E
LD A,13H 3E 13
Load Register A with 13H, error ILLEGAL FILE NAME.
5050
OR A B7
OR Register A (13H) with itself, setting the NZ FLAG.
5051
RET C9
RETURN with Register A = 13H and the NZ FLAG.
5052
LD A,C 79
Copy the ending character from Register C back into Register A. 504CH comes here.
5053
CP 2FH FE 2F
Compare Register A (the character that ended the name) against 2FH, the slash before an extension.
5055
If the NZ FLAG is set (no slash), JUMP to 505FH.
5057
LD DE,5165H 11 65 51
Point Register Pair DE to 5165H, the extension field of the template.
505A
LD B,03H 06 03
Load Register B with 03H, the most extension characters.
505C
GOSUB to 5081H to copy the extension from Register Pair HL to Register Pair DE; Register A returns the character that ended it.
505F
CP 2EH FE 2E
Compare Register A (the character that ended the last field) against 2EH, the period before a password. 5055H comes here.
5061
If the NZ FLAG is set (no password), JUMP to 506BH.
5063
LD DE,5155H 11 55 51
Point Register Pair DE to 5155H, the password buffer.
5066
LD B,08H 06 08
Load Register B with 08H, the most password characters.
5068
GOSUB to 5081H to copy the password from Register Pair HL to Register Pair DE.
506B
CP 3AH FE 3A
Compare Register A (the character that ended the last field) against 3AH, the colon before a drive number. 5061H comes here.
506D
If the NZ FLAG is set (no drive), JUMP to 507FH to finish.
506F
LD A,(HL) 7E
Fetch the drive digit from (HL) (Register Pair HL = the text after the colon) into Register A.
5070
SUB 30H D6 30
SUBtract 30H from Register A, turning the digits 0-9 into 00H-09H.
5072
If the CARRY FLAG is set (below the digit 0), JUMP to 5078H to report a bad drive.
5074
CP 04H FE 04
Compare Register A (the drive number) against 04H, one past the last drive.
5076
If the CARRY FLAG is set (drive 0-3), JUMP to 507CH to store it.
5078
LD A,20H 3E 20
Load Register A with 20H, error ILLEGAL DRIVE NUMBER. 5072H comes here.
507A
OR A B7
OR Register A (20H) with itself, setting the NZ FLAG.
507B
RET C9
RETURN with Register A = 20H and the NZ FLAG.
507C
LD (5154H),A 32 54 51
Store Register A (the drive number) into 5154H, the drive. 5076H comes here.
507F
XOR A AF
Set Register A to ZERO and set the Z FLAG: the filespec was parsed. 506DH comes here.
5080
RET C9
RETURN to the caller with Register A = 00H and the Z FLAG: a valid filespec.

5081H - Copy One Field of a Filespec

Register Pair HL = the text, Register Pair DE = where the field goes, Register B = the most characters to keep. The first character is copied only when it is a letter; later characters may be letters (41H-5AH) or digits (30H-39H). Any other character ends the field and is returned in Register A with Register Pair HL past it. A character beyond the limit is still passed over. Returns Register A = the ending character and Register B = the characters not used.

5081
LD A,(HL) 7E
Fetch the first character from (HL) (Register Pair HL = the text) into Register A. 5045H, 505CH and 5068H call here.
5082
INC HL 23
INCrement Register Pair HL (the text pointer) by 1.
5083
JUMP to 508EH to test the first character as a letter.
5085
LD A,(HL) 7E
Loop Start
Fetch the next character from (HL) (Register Pair HL = the text pointer) into Register A. 5096H loops back here.
5086
INC HL 23
INCrement Register Pair HL (the text pointer) by 1.
5087
CP 30H FE 30
Compare Register A (the field character) against 30H, the digit 0.
5089
RET C D8
If the CARRY FLAG is set (below the digit 0), RETURN with that character in Register A.
508A
CP 3AH FE 3A
Compare Register A against 3AH, one past the digit 9.
508C
If the CARRY FLAG is set (a digit), JUMP to 5094H to store it.
508E
CP 41H FE 41
Compare Register A against 41H, the letter A. 5083H comes here for the first character.
5090
RET C D8
If the CARRY FLAG is set (below the letter A), RETURN with that character in Register A.
5091
CP 5BH FE 5B
Compare Register A against 5BH, one past the letter Z.
5093
RET NC D0
If the NO CARRY FLAG is set (above the letter Z), RETURN with that character in Register A.
5094
LD (DE),A 12
Store the character (Register A) into the field at (DE) (Register Pair DE = the field). 508CH comes here.
5095
INC DE 13
INCrement Register Pair DE (the field pointer) by 1.
5096
Loop End
DECrement Register B and LOOP BACK to 5085H until the field is full.
5098
LD A,(HL) 7E
Fetch the character after the full field from (HL) (Register Pair HL = the text pointer) into Register A.
5099
INC HL 23
INCrement Register Pair HL (the text pointer) by 1, past it.
509A
RET C9
RETURN to the caller with the ending character in Register A and Register Pair HL past it.

509BH - Hash a File Name

Register Pair HL = the eleven-byte name and extension. Combines the bytes into a one-byte value for the hash index. Returns Register A = the hash, never 00H (00H marks an unused slot). Register Pair HL ends past the name.

509B
LD B,0BH 06 0B
Load Register B with 0BH, the eleven bytes of the name. 4E26H calls here.
509D
LD C,00H 0E 00
Load Register C with 00H, the running hash value so far.
509F
LD A,(HL) 7E
Loop Start
Fetch the next name byte from (HL) (Register Pair HL = the name) into Register A. 50A4H loops back here.
50A0
INC HL 23
INCrement Register Pair HL (the name pointer) by 1.
50A1
XOR C A9
XOR Register A (the name byte) with Register C (the running hash).
50A2
RLCA 07
Rotate Register A left, circularly, spreading the bits of the hash.
50A3
LD C,A 4F
Copy Register A (the mixed value) into Register C, the running hash.
50A4
Loop End
DECrement Register B and LOOP BACK to 509FH until all eleven bytes are mixed in.
50A6
LD A,C 79
Copy the hash from Register C into Register A.
50A7
OR A B7
OR Register A (the hash) with itself: the Z FLAG is set when it is 00H.
50A8
RET NZ C0
If the NZ FLAG is set (not 00H), RETURN with the hash in Register A.
50A9
INC A 3C
INCrement Register A by 1: a hash of 00H becomes 01H.
50AA
RET C9
RETURN to the caller with the hash (01H or more) in Register A.

50ABH - Find a Free Entry in the Hash Index

Looks for a byte of 00H in the table at 4D00H, so a new file gets a directory entry. The search starts at a locator taken from the clock at 4040H so new files spread through the directory, and falls back to locator 40H. Returns Register Pair HL = 4D00H plus the free locator and the Z FLAG, or the NZ FLAG when the table is full.

50AB
LD A,(4040H) 3A 40 40
Fetch the clock tick counter from 4040H into Register A. 4EFAH calls here.
50AE
AND E7H E6 E7
AND Register A with E7H, clearing bits 3 and 4.
50B0
CP 40H FE 40
Compare Register A (the clock-based locator) against 40H, the start of the table's second half.
50B2
If the NO CARRY FLAG is set (40H or more), JUMP to 50B6H.
50B4
OR 80H F6 80
OR Register A with 80H: move a low value into the second half of the table.
50B6
LD L,A 6F
Copy the starting locator from Register A into Register L. 4F5AH calls here with Register A = a locator; 50B2H comes here.
50B7
GOSUB to 50BDH to search for a free entry from Register L.
50BA
RET Z C8
If the Z FLAG is set (a free entry was found), RETURN with Register Pair HL = the entry.
50BB
LD L,40H 2E 40
Load Register L with 40H, a second starting locator.
50BD
LD A,(HL) 7E
Search Loop
Fetch the hash byte from the table at (HL) (Register Pair HL = 4D00H plus the locator) into Register A. 50B7H calls here; 50C4H and 50CCH loop back here.
50BE
OR A B7
OR Register A with itself: the Z FLAG is set for 00H, a free entry.
50BF
RET Z C8
If the Z FLAG is set (free), RETURN with Register Pair HL = the entry.
50C0
LD A,L 7D
Copy the locator from Register L into Register A.
50C1
ADD A,20H C6 20
ADD 20H to Register A: the next entry in the same column (bits 5-7).
50C3
LD L,A 6F
Copy Register A (the stepped locator) into Register L.
50C4
If the NO CARRY FLAG is set (still in the table), LOOP BACK to 50BDH.
50C6
ADD A,41H C6 41
ADD 41H to Register A: step to the next column (the sector part, bits 0-4).
50C8
LD L,A 6F
Copy Register A (the next column's locator) into Register L.
50C9
AND E7H E6 E7
AND Register A with E7H, clearing bits 3 and 4, which are not part of a locator.
50CB
CP L BD
Compare Register A against Register L: equal only when bits 3 and 4 were already clear.
50CC
If the Z FLAG is set (a valid locator), LOOP BACK to 50BDH.
50CE
OR 01H F6 01
OR Register A with 01H, setting the NZ FLAG: the table is full.
50D0
RET C9
RETURN to the caller with the NZ FLAG: no free directory entry.

50D1H - Hash a Password

Register Pair DE = the eight-byte password. Combines the bytes into a sixteen-bit value stored in the directory entry and compared when a file is opened. Reads the eight bytes from the last to the first. Returns Register Pair HL = the hash.

50D1
LD HL,FFFFH 21 FF FF
Load Register Pair HL with FFFFH, the starting hash. 4E2FH calls here.
50D4
LD B,08H 06 08
Load Register B with 08H, the eight password bytes.
50D6
LD A,E 7B
Copy Register E (the password pointer's low byte) into Register A.
50D7
ADD A,07H C6 07
ADD 07H to Register A: the last of the eight bytes.
50D9
LD E,A 5F
Copy Register A (the address of the last byte) into Register E.
50DA
If the NO CARRY FLAG is set (no overflow), JUMP to 50DDH without changing Register D.
50DC
INC D 14
INCrement Register D (the password pointer's high byte) by 1.
50DD
LD A,(DE) 1A
Loop Start
Fetch the next password byte from (DE) (Register Pair DE = the password pointer) into Register A. 50FAH loops back here.
50DE
PUSH DE D5
Save Register Pair DE (the password pointer) onto the stack.
50DF
LD D,A 57
Copy the password byte from Register A into Register D.
50E0
LD E,H 5C
Copy Register H (the hash's high byte) into Register E.
50E1
LD A,L 7D
Copy Register L (the hash's low byte) into Register A.
50E2
AND 07H E6 07
AND Register A with 07H, keeping the low three bits of the hash.
50E4
RRCA 0F
Rotate Register A right, circularly, moving the low three bits toward the top.
50E5
RRCA 0F
Rotate Register A right, circularly, moving the bits further up.
50E6
RRCA 0F
Rotate Register A right, circularly: the three bits move to the top.
50E7
XOR L AD
XOR Register A with Register L (the hash's low byte).
50E8
LD L,A 6F
Copy Register A (the mixed value) into Register L.
50E9
LD H,00H 26 00
Load Register H with 00H, the high byte before the shifts.
50EB
ADD HL,HL 29
ADD Register Pair HL to itself: the value times two.
50EC
ADD HL,HL 29
ADD Register Pair HL to itself: the value times four.
50ED
ADD HL,HL 29
ADD Register Pair HL to itself: the value times eight.
50EE
ADD HL,HL 29
ADD Register Pair HL to itself: the value times sixteen.
50EF
XOR H AC
XOR Register A (the low byte before the shifts) with Register H.
50F0
XOR D AA
XOR Register A with Register D (the password byte).
50F1
LD D,A 57
Copy Register A (the mixed value) into Register D, the new high byte.
50F2
LD A,L 7D
Copy Register L (the shifted low byte) into Register A.
50F3
ADD HL,HL 29
ADD Register Pair HL to itself once more: the value times thirty-two.
50F4
XOR H AC
XOR Register A with Register H (the high byte of the shifted value).
50F5
XOR E AB
XOR Register A with Register E (the saved high byte).
50F6
LD E,A 5F
Copy Register A (the mixed value) into Register E, the new low byte.
50F7
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the new hash.
50F8
POP DE D1
Restore Register Pair DE (the password pointer) from the stack.
50F9
DEC DE 1B
DECrement Register Pair DE by 1, to the previous byte.
50FA
Loop End
DECrement Register B and LOOP BACK to 50DDH until all eight bytes are mixed in.
50FC
RET C9
RETURN with Register Pair HL = the password hash.

50FDH - Test Whether a Drive Is Ready

Register C = drive. Selects the drive and watches the index pulse in the controller's status for about one revolution to tell whether a diskette is turning. Returns the NZ FLAG when no index pulse arrives in time (the drive is empty or not turning). When one does, returns Register A = 00H, the Z FLAG and the CARRY FLAG set when the controller reports the diskette is write protected.

50FD
LD A,D0H 3E D0
Load Register A with D0H, FORCE INTERRUPT, which stops any command the controller is running. 4E40H and 4EEFH call here.
50FF
LD (37ECH),A 32 EC 37
Store Register A (D0H) into the controller's command register at 37ECH.
1771 FDC Command: D0H (11010000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
11010000Command=Force Interrupt (bits 7-4 = 1101)
I3-I0 = 0000: end the command, no interrupt
5102
GOSUB to SYS0/SYS's 4600H to select drive Register C and start its motor.
5105
PUSH BC C5
Save Register Pair BC (Register C = drive) onto the stack.
5106
LD BC,3155H 01 55 31
Load Register Pair BC with 3155H, the time to wait for the index pulse.
5109
Wait for the Pulse to Clear
GOSUB to 511EH to read the index bit of the status, counting down Register Pair BC. 510CH loops back here.
510C
If the NZ FLAG is set (the index pulse is present), LOOP BACK to 5109H until it clears or the time runs out.
510E
Wait for the Pulse
GOSUB to 511EH to read the index bit again. 5111H loops back here.
5111
If the Z FLAG is set (no index pulse yet), LOOP BACK to 510EH until one arrives or the time runs out.
5113
Wait for the Pulse to Clear Again
GOSUB to 511EH to read the index bit again. 5116H loops back here.
5116
If the NZ FLAG is set (the index pulse is still present), LOOP BACK to 5113H.
5118
POP BC C1
Restore Register Pair BC (Register C = drive) from the stack.
5119
RLCA 07
Rotate Register A (the last status byte) left, circularly.
511A
AND 80H E6 80
AND Register A with 80H, keeping the write-protect bit in bit 7.
511C
ADD A,A 87
ADD Register A to itself: the write-protect bit moves into the CARRY FLAG and Register A becomes 00H and sets the Z FLAG.
511D
RET C9
RETURN with the Z FLAG and the CARRY FLAG set when the diskette is write protected.
511E
DEC BC 0B
Read the Index Bit
DECrement Register Pair BC (the time left) by 1. 5109H, 510EH and 5113H call here.
511F
LD A,B 78
Copy Register B (the high byte of the time) into Register A.
5120
OR C B1
OR Register A with Register C (the low byte): the Z FLAG is set when the time has run out.
5121
If the Z FLAG is set (out of time), JUMP to 5129H.
5123
LD A,(37ECH) 3A EC 37
Fetch the controller's status from 37ECH into Register A.
5126
BIT 1,A CB 4F
Test bit 1 of Register A (the index pulse in a Type I status).
5128
RET C9
RETURN to 50FDH with the Z FLAG or NZ FLAG of the index bit.
5129
POP BC C1
Out of Time
Restore Register Pair BC from the stack, dropping this routine's return. 5121H comes here.
512A
POP BC C1
Restore Register Pair BC from the stack, dropping the caller's return as well.
512B
OR 01H F6 01
OR Register A with 01H, setting the NZ FLAG: no index pulse arrived.
512D
RET C9
RETURN to the code that called 50FDH with the NZ FLAG: the drive is not ready.

512EH - Read the Hash Index Table

Register C = drive. Reads directory sector 1, the hash index table, into 4D00H-4DFFH. Returns the Z FLAG, or Register A = 16H (HIT READ ERROR) and the NZ FLAG.

512E
PUSH BC C5
Save Register Pair BC (Register C = drive) onto the stack. 4E45H, 4EF6H and 4F53H call here.
512F
PUSH DE D5
Save Register Pair DE (the caller's) onto the stack.
5130
GOSUB to SYS0/SYS's 4B55H: Register D = the directory track of drive Register C.
5133
LD E,01H 1E 01
Load Register E with 01H, directory sector 1, the hash index.
5135
LD HL,4D00H 21 00 4D
Point Register Pair HL to 4D00H, where the table is read.
5138
GOSUB to SYS0/SYS's 4B35H to read drive Register C, track Register D, sector Register E into 4D00H (a system sector with the FAH data mark).
513B
POP DE D1
Restore Register Pair DE (the caller's) from the stack.
513C
POP BC C1
Restore Register Pair BC (Register C = drive) from the stack.
513D
RET Z C8
If the Z FLAG is set (read), RETURN with Register A = 00H.
513E
LD A,16H 3E 16
Load Register A with 16H, error HIT READ ERROR.
5140
RET C9
RETURN with Register A = 16H and the NZ FLAG.

5141H - Write the Hash Index Table

Register C = drive. Writes 4D00H-4DFFH back to directory sector 1 with the FAH data mark. Returns the Z FLAG, or Register A = 17H (HIT WRITE ERROR) and the NZ FLAG.

5141
PUSH BC C5
Save Register Pair BC (Register C = drive) onto the stack. 4F13H and 4F6BH call here.
5142
PUSH DE D5
Save Register Pair DE (the caller's) onto the stack.
5143
GOSUB to SYS0/SYS's 4B55H: Register D = the directory track of drive Register C.
5146
LD E,01H 1E 01
Load Register E with 01H, directory sector 1, the hash index.
5148
LD HL,4D00H 21 00 4D
Point Register Pair HL to 4D00H, the table to write.
514B
GOSUB to SYS0/SYS's 46EFH to write 4D00H to drive Register C, track Register D, sector Register E with the FAH data mark.
514E
POP DE D1
Restore Register Pair DE (the caller's) from the stack.
514F
POP BC C1
Restore Register Pair BC (Register C = drive) from the stack.
5150
RET Z C8
If the Z FLAG is set (written), RETURN with Register A = 00H.
5151
LD A,17H 3E 17
Load Register A with 17H, error HIT WRITE ERROR.
5153
RET C9
RETURN with Register A = 17H and the NZ FLAG.

5154H - Data and Work Area

The drive variable is the last byte of the file; the buffers that follow it are not loaded.

5154
DEFB 00H 00
Drive of the Filespec
FFH while no drive is given, 0-3 once one is parsed (5027H, 507CH). 4E38H, 4E54H, 4E8EH, 4EE7H and 4EFFH read it. 00H in the file.
5155-515C
DEFS 8
Password Buffer
Not in the file. 5027H fills it with the typed password and 50D1H hashes it (4E2CH).
515D-5164
DEFS 8
Name Template
Not in the file. 5027H fills it with eight characters of name; 509BH hashes it and 4E7FH compares it with a directory entry.
5165-5167
DEFS 3
Extension Template
Not in the file. 5027H fills it with three characters of extension, following the name at 515DH.
5168-5169
DEFS 2
Password Hash
Not in the file. 4E32H stores the hash computed by 50D1H; 4F35H copies it with the copy at 516AH into a new entry's owner and user password words.
516A-516B
DEFS 2
Password Hash Copy
Not in the file. 4E35H stores a second copy; 4EA0H reads it to check a file's password.