4E00H - Request Code Dispatcher
Entry point for the SYS2 overlay. Register A contains the full SVC code from RST 28H. The dispatcher masks bits 4-6 to extract the request code and routes to the appropriate handler. Six request codes are supported (10H through 60H); any other value falls through to RET.
4E00
AND 70H E6 70
Mask Register A to isolate bits 4-6, extracting the request code (10H, 20H, 30H, 40H, 50H, or 60H). All other bits (the overlay file number in bits 0-3 and bit 7) are cleared.
4E02
CP 10H FE 10
Compare the masked request code against 10H (Open Existing File). If Register A equals 10H, the Z FLAG is set.
4E04
If the Z FLAG (Zero) has been set because the request code is 10H, JUMP to 4E69H to handle the Open Existing File request. This searches the directory for a matching filename and populates the FCB.
4E07
CP 20H FE 20
Compare the request code against 20H (Open New or Existing File). If Register A equals 20H, the Z FLAG is set.
4E09
If the Z FLAG (Zero) has been set because the request code is 20H, JUMP to 4E61H to handle the Open New or Existing File request. This first attempts to find the file; if not found, it allocates a new directory slot.
4E0C
CP 40H FE 40
Compare the request code against 40H (Drive Select and Write-Protect Detection). If Register A equals 40H, the Z FLAG is set.
4E0E
If the Z FLAG (Zero) has been set because the request code is 40H, JUMP to 5147H to handle the Drive Select request. This selects the drive via the SYS0 routine at 478FH and detects write-protect status using FDC index pulse polling.
4E11
CP 50H FE 50
Compare the request code against 50H (Filename Hash Computation). If Register A equals 50H, the Z FLAG is set.
4E13
If the Z FLAG (Zero) has been set because the request code is 50H, JUMP to 50CBH to compute the 8-bit filename CRC hash. This XOR/rotate hash over 11 bytes is used for fast directory entry matching.
4E16
CP 60H FE 60
Compare the request code against 60H (Password Hash Computation). If Register A equals 60H, the Z FLAG is set.
4E18
If the Z FLAG (Zero) has been set because the request code is 60H, JUMP to 511EH to compute the 16-bit password hash. This polynomial CRC over 8 bytes produces a hash used for password verification during file open.
4E1B
RET C9
Return to caller. If none of the six request codes matched, execution falls through here. Register A still contains the masked value; the NZ FLAG is set from the last failed CP instruction.
4E1CH - INIT FCB Handler (Direct Sector I/O Setup)
This routine is reached from the Open Existing File handler (4E7CH) when the FCB status byte at IX+00H contains 2AH ('*'), which is the INIT marker. Instead of performing a directory search, INIT populates the FCB directly from a drive configuration table entry, enabling raw sector-level I/O without a file association. The routine scans two configuration table ranges (4015H-402CH and 43C0H-43D7H) looking for an entry whose sector buffer address matches the value in IX+01H/IX+02H.
4E1C
LD E,(IX+01H) DD 5E 01
Load Register E with the low byte of the sector buffer address from FCB offset +01H. This is the buffer pointer that the caller specified in the FCB before invoking INIT.
4E1F
LD D,(IX+02H) DD 56 02
Load Register D with the high byte of the sector buffer address from FCB offset +02H. Register Pair DE now holds the full 16-bit sector buffer address that will be matched against configuration table entries.
4E22
LD HL,4015H 21 15 40
Point Register Pair HL to 4015H, the start of the first drive configuration table range. This table contains entries for configured drives, with each 8-byte entry having a 2-byte sector buffer address at offset +06H/+07H.
Loop Start
The following loop scans 8-byte configuration table entries, comparing the sector buffer address at offset +06H/+07H against the target address in DE. It first scans entries from 4015H through 402CH (3 entries), then if no match is found, continues with 43C0H through 43D7H (3 more entries).
4E25
PUSH HL E5
Save the current table entry base address onto the stack. This will be restored later if the comparison fails, or popped cleanly if a match is found.
4E26
LD A,L 7D
Load Register A with the low byte of the current table base address. This will be used to compute the offset to the sector buffer address field within the entry.
4E27
ADD A,06H C6 06
ADD 06H to Register A to advance the pointer to offset +06H within the current table entry, where the sector buffer address low byte is stored.
4E29
LD L,A 6F
Store the computed offset back into Register L. Register Pair HL now points to the sector buffer address field (offset +06H) of the current configuration table entry.
4E2A
LD A,(HL) 7E
Fetch the low byte of the sector buffer address from the configuration table entry at offset +06H into Register A.
4E2B
INC L 2C
INCrement Register L by 1 to advance to offset +07H, the high byte of the sector buffer address. Using INC L instead of INC HL is safe because the table entries do not cross page boundaries.
4E2C
CP E BB
Compare Register A (table entry buffer address low byte) against Register E (target buffer address low byte from the FCB). If they match, the Z FLAG is set.
4E2D
If the NZ FLAG (Not Zero) has been set because the low bytes do not match, JUMP to 4E36H to advance to the next configuration table entry.
4E2F
LD A,(HL) 7E
Fetch the high byte of the sector buffer address from the configuration table entry at offset +07H into Register A.
4E30
CP D BA
Compare Register A (table entry buffer address high byte) against Register D (target buffer address high byte from the FCB). If both bytes match, the sector buffer addresses are identical.
4E31
If the NZ FLAG (Not Zero) has been set because the high bytes do not match, JUMP to 4E36H to advance to the next configuration table entry.
Match Found
Both bytes of the sector buffer address match. The configuration table entry base address is still on the stack from the PUSH at 4E25H.
4E33
POP HL E1
Restore the matching configuration table entry base address from the stack into Register Pair HL. This points to the start of the 8-byte entry that will be used to populate the FCB.
4E34
JUMP to 4E48H to populate the FCB from the matched configuration table entry. HL points to the table entry base and DE still holds the sector buffer address.
No Match at This Entry
The sector buffer address did not match. Advance to the next 8-byte configuration table entry.
4E36
POP AF F1
Discard the saved table entry base address from the stack (popped into AF since the value is no longer needed). This cleans up the PUSH from 4E25H.
4E37
INC L 2C
INCrement Register L by 1 to advance past the current entry's last byte (offset +07H). After the ADD A,06H at 4E27H and the INC L at 4E2BH, L is now at offset +08H, which is the start of the next entry.
4E38
If the NZ FLAG (Not Zero) has been set because Register L did not wrap around to 00H, JUMP to 4E3EH to check if we have reached the end of the current table range. The Z FLAG from INC L is set only when L wraps from FFH to 00H, which would indicate a page boundary crossing.
Table Range Overflow
Register L wrapped around to 00H, meaning the scan has gone past the end of a 256-byte page. This is an error condition since the table should not extend that far.
4E3A
LD A,08H 3E 08
Load Register A with error code 08H (drive not found / invalid configuration). No matching table entry was found within either range.
4E3C
OR A B7
OR Register A with itself to set the NZ FLAG (since A = 08H, not zero). This signals an error condition to the caller.
4E3D
RET C9
Return to caller with NZ FLAG set and Register A = 08H (error: drive not found in configuration tables).
4E3E
LD A,L 7D
Load Register A with the current value of Register L (pointing to the start of the next table entry).
4E3F
CP 2DH FE 2D
Compare Register A against 2DH. This checks whether we have reached the boundary between the first table range (4015H-402CH) and the gap before the second range. The value 2DH is past the last valid entry in the 4015H range (3 entries x 8 bytes = 18H bytes, so last byte is at 402CH, next entry would start at 402DH).
4E41
If the NZ FLAG (Not Zero) has been set because we have NOT reached the boundary at 2DH, LOOP BACK to 4E25H to check the next configuration table entry in the current range.
Switch to Second Table Range
The first configuration table (4015H-402CH) has been exhausted without finding a match. Switch to the second range at 43C0H-43D7H.
4E43
LD HL,43C0H 21 C0 43
Point Register Pair HL to 43C0H, the start of the second drive configuration table range. This table extends from 43C0H to 43D7H and contains up to 3 additional 8-byte drive configuration entries.
4E46
JUMP back to 4E25H to begin scanning the second table range. The loop will continue checking entries until a match is found, the L register reaches 2DH again (which it cannot since 43C0H+18H = 43D8H and L = D8H != 2DH, so the loop would continue until L wraps or another boundary is hit), or the page boundary is crossed.
Match Found - Populate FCB
A matching configuration table entry has been found. HL points to the entry base address, DE holds the sector buffer address. The FCB at IX is populated with the drive configuration data for direct sector I/O.
4E48
LD B,H 44
Load Register B with the high byte of the matched configuration table entry address. Register Pair BC will hold the table entry pointer.
4E49
LD C,L 4D
Load Register C with the low byte of the matched configuration table entry address. Register Pair BC now holds the full table entry base address.
4E4A
PUSH IX DD E5
Save the FCB base address (IX) onto the stack.
4E4C
POP HL E1
Restore the FCB base address into Register Pair HL. This PUSH IX / POP HL sequence transfers the IX value into HL for byte-by-byte FCB population.
4E4D
PUSH HL E5
Save the FCB base address onto the stack. It will be popped into DE at 4E5FH for the return value.
4E4E
LD (HL),10H 36 10
Store 10H at FCB offset +00H (status byte). The value 10H indicates the FCB is initialized for direct sector I/O (INIT mode).
4E50
INC HL 23
INCrement HL to point to FCB offset +01H.
4E51
LD (HL),C 71
Store the low byte of the configuration table entry address (Register C) at FCB offset +01H.
4E52
INC HL 23
INCrement HL to point to FCB offset +02H.
4E53
LD (HL),B 70
Store the high byte of the configuration table entry address (Register B) at FCB offset +02H. FCB bytes +01H/+02H now contain a pointer back to the matching configuration table entry.
4E54
INC HL 23
INCrement HL to point to FCB offset +03H.
4E55
XOR A AF
Set Register A to zero and clear all flags. This zero value will be stored in the next three FCB bytes to initialize the sector position fields.
4E56
LD (HL),A 77
Store 00H at FCB offset +03H (byte offset within sector, initialized to start of sector).
4E57
INC HL 23
INCrement HL to point to FCB offset +04H.
4E58
LD (HL),A 77
Store 00H at FCB offset +04H (sector buffer base low byte, cleared for INIT mode).
4E59
INC HL 23
INCrement HL to point to FCB offset +05H.
4E5A
LD (HL),A 77
Store 00H at FCB offset +05H (sector buffer base high byte, cleared for INIT mode).
4E5B
INC HL 23
INCrement HL to point to FCB offset +06H.
4E5C
LD (HL),E 73
Store the low byte of the sector buffer address (Register E, originally from IX+01H) at FCB offset +06H.
4E5D
INC HL 23
INCrement HL to point to FCB offset +07H.
4E5E
LD (HL),D 72
Store the high byte of the sector buffer address (Register D, originally from IX+02H) at FCB offset +07H. The sector buffer address is now stored in FCB bytes +06H/+07H.
4E5F
POP DE D1
Restore the FCB base address (saved at 4E4DH) into Register Pair DE. This provides the return value to the caller.
4E60
RET C9
Return to caller with Register Pair DE pointing to the initialized FCB. The Z FLAG is set (from XOR A at 4E55H), indicating success.
4E61H - Open New or Existing File Handler (Request 20H)
Entry point for request code 20H. This handler sets up the overlay context via SYS0 at 49E9H, then falls through to the open-new-file logic at 4F5EH with Register A = 00H (indicating no access restrictions). The actual directory search for an existing file is performed within the 4F5EH routine.
4E61
GOSUB to the SYS0 overlay context save routine at 49E9H. This saves the return address and FCB pointer (IX) into the overlay parameter block at 430AH-430BH so that the overlay can be re-entered if needed.
4E64
XOR A AF
Set Register A to zero and clear all flags. The value 00H will be passed to 4F5EH as the access mode, indicating no special access restrictions for the open-new path.
4E65
JUMP to 4F5EH to enter the open-new-or-existing file handler with Register A = 00H. This routine will search for an existing file first; if not found (error 18H), it allocates a new directory slot.
4E68
NOP 00
Unused padding byte between the request 20H stub and the request 10H handler.
4E69H - Open Existing File Handler (Request 10H)
Entry point for request code 10H. This is the primary file open routine that searches the directory for a matching filename. It saves and modifies the system flags at 430FH, checks for the INIT marker ('*' = 2AH), parses the filespec, computes the filename hash, and begins the directory search loop.
4E69
GOSUB to the SYS0 overlay context save routine at 49E9H. This saves the return address and FCB pointer (IX) into the overlay parameter block so the overlay can be properly unloaded after completion.
4E6C
LD A,(430FH) 3A 0F 43
Fetch the system mode flags byte from 430FH into Register A. This byte contains various state bits: bit 0 = DEBUG mode, bit 1 = file-open-with-write flag, bit 2 = open-new active flag, bit 5 = re-entry mode.
4E6F
LD (4F45H),A 32 45 4F
Self-Modifying Code
Store the current 430FH value as the operand of the AND instruction at 4F44H. This saves the original flags so they can be restored later when the open operation completes. The runtime instruction at 4F44H becomes AND xxH where xxH is the saved flags value.
4E72
AND 0F9H E6 F9
AND Register A with F9H (binary 11111001) to clear bits 1 and 2 of the system flags. Bit 1 is the file-open-with-write flag and bit 2 is the open-new active flag. These are cleared at the start of a new open operation.
4E74
LD (430FH),A 32 0F 43
Store the modified flags (with bits 1 and 2 cleared) back to the system flags byte at 430FH.
4E77
LD A,(IX+00H) DD 7E 00
Fetch the FCB status byte from offset +00H into Register A. This byte indicates the current state of the FCB. The special value 2AH ('*') triggers the INIT handler instead of a normal file open.
4E7A
CP 2AH FE 2A
Compare the FCB status byte against 2AH (ASCII '*'). This is the INIT marker that indicates the caller wants direct sector I/O setup rather than a file-based open.
4E7C
If the Z FLAG (Zero) has been set because the FCB status byte is 2AH ('*'), JUMP to 4E1CH to execute the INIT FCB handler which populates the FCB from the drive configuration table for direct sector I/O.
4E7E
LD A,B 78
Load Register A with the access mode from Register B. The access mode was passed by the caller in Register B and specifies read/write/extend permissions for the file.
4E7F
LD (51FDH),A 32 FD 51
Self-Modifying Code
Store the access mode into the data area at 51FDH. This value is referenced later during FCB population at 5000H and 4FD5H to set the access mode field in the FCB.
4E82
LD (4FEAH),HL 22 EA 4F
Self-Modifying Code
Store the filespec pointer (Register Pair HL, pointing to the command line text) into the operand of the LD DE instruction at 4FE9H. This saves the filespec position so it can be retrieved later during FCB population.
4E85
PUSH IX DD E5
Save the FCB base address (IX) onto the stack.
4E87
POP HL E1
Restore the FCB base address into Register Pair HL. This PUSH IX / POP HL sequence transfers IX to HL so the filespec parser can use HL as the filename buffer pointer.
4E88
GOSUB to the filespec parser at 5050H. This parses the command line text pointed to by HL into the filename buffer at 51E8H (8+3 bytes), password buffer at 51E0H (8 bytes), and drive specifier at 4E9FH. On return, Register A = 00H and Z FLAG set if successful; NZ FLAG if parse error.
4E8B
RET NZ C0
Return to caller if the NZ FLAG (Not Zero) has been set because the filespec parser encountered an error (e.g., no valid filename characters found, error 13H). The error code is already in Register A.
4E8CH - Directory Search Loop
With the filespec successfully parsed, this routine computes the filename and password hashes, then searches the directory across multiple drives and sectors. For each directory entry, the 8-bit filename hash is compared first for fast rejection; on a hash match, the full 11-byte filename comparison is performed. Register C tracks the drive number (0-7), and the self-modifying operand at 4E9FH tracks the specific drive identifier (FFH = no specific drive requested).
4E8C
LD HL,51E8H 21 E8 51
Point Register Pair HL to the filename buffer at 51E8H (11 bytes: 8 name + 3 extension, space-padded). This buffer was populated by the filespec parser at 5050H.
4E8F
GOSUB to the filename hash routine at 50CBH. This computes an 8-bit CRC hash by XOR/rotate over the 11 filename bytes. On return, the hash is stored at 51DEH (both bytes set to the same value).
4E92
LD DE,51E0H 11 E0 51
Point Register Pair DE to the password buffer at 51E0H (8 bytes, space-padded). This buffer was populated by the filespec parser if a password was specified after the '.' separator.
4E95
GOSUB to the password hash routine at 511EH. This computes a 16-bit polynomial CRC hash over the 8 password bytes. On return, Register Pair HL contains the 16-bit hash value.
4E98
LD (51F3H),HL 22 F3 51
Store the 16-bit password hash into the first password hash slot at 51F3H. This value will be compared against the file's stored password hash during the match verification.
4E9B
LD (51F5H),HL 22 F5 51
Store the same 16-bit password hash into the second slot at 51F5H. Both slots are initialized to the same value; during extent chain processing, the second slot may be updated from the directory entry if the file has a secondary password.
Self-Modifying Code
The LD A instruction at 4E9EH has its operand modified at runtime by the filespec parser (50A1H writes the drive number) and by the drive iteration logic (4EFBH writes the matched drive). The initial value 00H in the binary is overwritten before reaching here.
4E9E
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the drive number. The operand at 4E9FH was set by the filespec parser at 50A1H. A value of FFH means no specific drive was requested (search all drives); any other value (00H-07H) specifies the target drive.
4EA0
LD C,A 4F
Load Register C with the drive number from Register A. Register C will track the current drive being searched throughout the directory scan loop.
4EA1
INC A 3C
INCrement Register A by 1. If A was FFH (no specific drive), it wraps to 00H and the Z FLAG is set. If A was a valid drive number (00H-07H), the result is non-zero.
4EA2
If the NZ FLAG (Not Zero) has been set because a specific drive was requested (A was not FFH), JUMP to 4EA5H to begin the search on that specific drive, skipping the drive 0 override.
4EA4
LD C,A 4F
Set Register C to 00H (Register A is 00H after INC from FFH). When no specific drive was requested, the search starts on drive 0.
Drive Search Loop Start
Register C holds the current drive number. The loop selects the drive, reads the directory sector, then scans each 32-byte entry in the sector for a matching filename hash.
4EA5
GOSUB to the drive select routine at 5147H. This selects drive C via the SYS0 routine at 478FH and performs write-protect detection using FDC index pulse polling. On return, Z FLAG set = success, NZ = drive error, CARRY = write-protected.
4EA8
If the NZ FLAG (Not Zero) has been set because the drive select failed (drive not ready or error), JUMP to 4EBDH to try the next drive or return a file-not-found error.
4EAA
GOSUB to the directory sector read routine at 51B5H. This reads directory sector 1 into the 4200H buffer. On return, HL points into the directory buffer, Z FLAG set = success, A = error code if NZ.
4EAD
RET NZ C0
Return to caller if the NZ FLAG (Not Zero) has been set because the directory read failed. The error code (16H = directory read error) is in Register A.
Directory Entry Scan Loop
HL points to the current directory entry within the 4200H buffer. Each entry's first byte is a flags/hash byte: 00H indicates a free slot, any other value is the filename hash. The scan compares each non-zero hash against the target hash at 51DEH.
4EAE
LD A,(HL) 7E
Fetch the first byte of the current directory entry. This byte is the filename CRC hash for active entries, or 00H for empty/deleted entries.
4EAF
OR A B7
OR Register A with itself to test if the directory entry hash byte is zero. If zero, the entry is free (deleted or never used).
4EB0
If the Z FLAG (Zero) has been set because the hash byte is 00H (free entry), JUMP to 4EBAH to skip this entry and advance to the next one. Free entries cannot contain a matching file.
4EB2
PUSH HL E5
Save the current directory entry pointer onto the stack. It will be needed if the hash matches and we need to continue with the full filename comparison.
4EB3
LD HL,51DEH 21 DE 51
Point Register Pair HL to the stored filename hash at 51DEH. This hash was computed by the 50CBH routine from the parsed filename.
4EB6
CP (HL) BE
Compare Register A (the directory entry's hash byte) against the target filename hash at (51DEH). If they match, this entry is a potential filename match that needs full verification.
4EB7
POP HL E1
Restore the directory entry pointer from the stack into Register Pair HL. This is done regardless of whether the hash matched, to maintain the scan position.
4EB8
If the Z FLAG (Zero) has been set because the directory entry hash matches the target hash, JUMP to 4ED7H to perform the full 11-byte filename comparison and read the directory entry for detailed verification.
Advance to Next Directory Entry
The hash did not match (or the entry was free). Advance to the next 32-byte entry within the directory sector buffer.
4EBA
INC L 2C
INCrement Register L by 1 to advance past the current byte. Since directory entries are processed one hash byte at a time and the loop re-enters at 4EAEH to read the next hash, INC L effectively skips to the next entry only if the hash byte spacing is 1 byte apart in this scan mode. However, looking at the broader context, HL is being used to scan consecutive hash bytes in the directory, not full 32-byte entries - the hash bytes are positioned at the start of each 32-byte entry, meaning this INC L advances within the buffer and the scan relies on the 256-byte page structure where each entry's hash falls on a predictable offset.
4EBB
If the NZ FLAG (Not Zero) has been set because Register L did not wrap to 00H (still within the directory buffer page), LOOP BACK to 4EAEH to check the next entry. When L wraps to 00H, all entries in this sector have been checked.
Directory Sector Exhausted - Try Next Drive
All entries in the current directory sector have been scanned without finding a match. Check whether we should try another drive or return file-not-found.
4EBD
LD A,(4E9FH) 3A 9F 4E
Fetch the requested drive number from the self-modifying operand at 4E9FH. A value of FFH means search all drives; any other value means a specific drive was requested.
4EC0
INC A 3C
INCrement Register A by 1. If the drive was FFH (all drives), A wraps to 00H and the Z FLAG is set. If a specific drive was requested, A becomes non-zero.
4EC1
If the NZ FLAG (Not Zero) has been set because a specific drive was requested (the value was not FFH), JUMP to 4EC9H to return error 18H (file not found). When a specific drive is requested and the file is not on that drive, there are no other drives to search.
4EC3
INC C 0C
INCrement Register C (current drive number) by 1 to try the next drive. The drive numbers range from 0 through 7.
4EC4
LD A,C 79
Load Register A with the current drive number from Register C.
4EC5
CP 08H FE 08
Compare Register A against 08H (one past the last valid drive number 07H). If Register A < 08H, the CARRY FLAG is set and there are still drives to check.
4EC7
If the CARRY FLAG has been set because the drive number is still less than 8, LOOP BACK to 4EA5H to select the next drive and search its directory.
File Not Found
All drives have been searched (or the specific requested drive was exhausted). Return error 18H.
4EC9
LD A,18H 3E 18
Load Register A with error code 18H (file not found). The directory search has completed without finding a matching filename.
4ECB
OR A B7
OR Register A with itself to set the NZ FLAG (since A = 18H, not zero). This signals an error condition to the caller.
4ECC
RET C9
Return to caller with NZ FLAG set and Register A = 18H (file not found).
4ECDH - Hash Match Cleanup and Re-Read Directory
This routine is the error/mismatch exit from the full filename comparison at 4EE2H. When the detailed comparison fails (after a hash match), the saved stack state from the comparison must be cleaned up, the directory sector re-read (since it may have been overwritten), and the scan resumed at the next entry. This is also reached when the directory entry's bit 7 flag indicates a chain continuation entry (not a primary filename entry).
4ECD
POP BC C1
Restore Register Pair BC from the stack. This pops the BC value saved by the PUSH BC at 4EE3H (within the filename comparison routine), restoring the drive counter.
4ECE
POP HL E1
Restore Register Pair HL from the stack. This pops the HL value saved by the PUSH HL at 4EE2H (within the filename comparison routine), restoring the directory entry pointer.
4ECF
POP BC C1
Restore Register Pair BC from the stack. This pops the BC value saved by the PUSH BC at 4ED8H (in the hash match handler at 4ED7H), restoring the outer loop's drive counter.
4ED0
GOSUB to the directory sector read routine at 51B5H. The directory sector must be re-read because the buffer at 4200H may have been overwritten by the 4B10H call during the detailed comparison. HL is set to point into the buffer on return.
4ED3
POP HL E1
Restore Register Pair HL from the stack. This pops the HL value saved by the PUSH HL at 4ED7H (the hash match entry), which was the directory entry pointer at the time of the hash match. This restores the scan position.
4ED4
RET NZ C0
Return to caller if the NZ FLAG (Not Zero) has been set because the directory re-read at 51B5H failed. The error code (16H) is in Register A.
4ED5
JUMP to 4EBAH to advance to the next directory entry and continue the scan. The INC L at 4EBAH moves past the current hash byte that produced the false match.
4ED7H - Hash Match - Read Directory and Compare Filename
When the 8-bit hash at a directory entry matches the target filename hash, this routine performs the full verification. It reads the complete directory entry from disk using the SYS0 routine at 4B10H, checks whether the entry is a chain continuation (bit 7 of first byte), and if not, compares all 11 bytes of the filename (8 name + 3 extension) against the parsed filename buffer at 51E8H.
4ED7
PUSH HL E5
Save the directory entry pointer onto the stack. This preserves the scan position in case the full comparison fails and scanning must resume.
4ED8
PUSH BC C5
Save Register Pair BC (drive counter in C) onto the stack. This preserves the drive iteration state.
4ED9
LD B,L 45
Load Register B with the low byte of the directory entry pointer (Register L). This is the directory entry's offset within the buffer page, which serves as the directory info byte / entry identifier for the SYS0 directory read routine.
4EDA
GOSUB to the SYS0 directory write/read routine at 4B10H. With Register B containing the directory entry offset, this reads the complete directory entry. On return, HL points to the entry data and Z FLAG indicates success.
4EDD
If the Z FLAG (Zero) has been set because the directory read succeeded, JUMP to 4EE2H to perform the full filename comparison.
4EDF
POP BC C1
Restore Register Pair BC from the stack (cleaning up the PUSH at 4ED8H). The directory read failed.
4EE0
POP HL E1
Restore Register Pair HL from the stack (cleaning up the PUSH at 4ED7H). The directory read failed.
4EE1
RET C9
Return to caller with the error condition from the failed directory read. The NZ FLAG is set and Register A contains the error code.
4EE2H - Full Filename Comparison (11-Byte Match)
After the directory entry has been read successfully, this routine checks whether the entry is a chain continuation (bit 7 of byte 0) and, if not, performs a byte-by-byte comparison of the 11-byte filename (8 name + 3 extension) at directory entry offset +05H against the parsed filename buffer at 51E8H. A mismatch jumps to the cleanup routine at 4ECDH.
4EE2
PUSH HL E5
Save the directory entry pointer onto the stack. This is needed for cleanup if the comparison fails.
4EE3
PUSH BC C5
Save Register Pair BC onto the stack. This preserves the drive counter and entry offset.
4EE4
BIT 7,(HL) CB 7E
Test bit 7 of the first byte of the directory entry. If bit 7 is set, this entry is a chain continuation record (part of a multi-extent file's chain), not a primary filename entry.
4EE6
If the NZ FLAG (Not Zero) has been set because bit 7 is set (chain continuation entry), JUMP to 4ECDH to clean up the stack and continue scanning. Chain continuation entries do not contain filenames and should be skipped.
4EE8
LD A,05H 3E 05
Load Register A with 05H, the offset within the directory entry where the 8-byte filename begins.
4EEA
ADD A,L 85
ADD the directory entry base offset (Register L) to 05H to compute the absolute position of the filename within the buffer. Since HL points to the start of the entry, L + 05H points to the filename field.
4EEB
LD L,A 6F
Store the computed filename offset into Register L. HL now points to the first byte of the filename within the directory entry.
4EEC
LD DE,51E8H 11 E8 51
Point Register Pair DE to the parsed filename buffer at 51E8H (11 bytes: 8 name + 3 extension). This is the target filename from the user's filespec.
4EEF
LD B,0BH 06 0B
Load Register B with 0BH (11 decimal), the total length of the filename field (8 name + 3 extension bytes). This is the loop counter for the byte-by-byte comparison.
Comparison Loop
Compare each of the 11 filename bytes between the directory entry and the parsed filename buffer.
4EF1
LD A,(DE) 1A
Fetch the current byte from the parsed filename buffer (at DE) into Register A.
4EF2
CP (HL) BE
Compare Register A (parsed filename byte) against the corresponding byte in the directory entry (at HL). If they match, the Z FLAG is set.
4EF3
If the NZ FLAG (Not Zero) has been set because the filename bytes do not match, JUMP to 4ECDH to clean up the stack and continue scanning the directory. This is a false hash match (hash collision).
4EF5
INC HL 23
INCrement HL to point to the next byte in the directory entry filename.
4EF6
INC DE 13
INCrement DE to point to the next byte in the parsed filename buffer.
4EF7
DECrement B and loop back to 4EF1H if not zero. This continues the comparison until all 11 bytes have been checked or a mismatch is found.
Full Filename Match Confirmed
All 11 bytes match. The directory entry at the current position is the target file. Clean up the comparison stack state and proceed to extract file metadata.
4EF9
POP BC C1
Restore Register Pair BC from the stack (from the PUSH at 4EE3H). Register C contains the drive number where the file was found.
4EFA
LD A,C 79
Load Register A with the drive number from Register C.
4EFB
LD (4E9FH),A 32 9F 4E
Self-Modifying Code
Store the matched drive number into the operand of the LD A instruction at 4E9EH. This records which drive the file was found on, so subsequent operations reference the correct drive.
4EFE
POP HL E1
Restore Register Pair HL from the stack (from the PUSH at 4EE2H). HL points to the start of the matching directory entry.
4EFFH - Match Found - Extract File Metadata and Verify Password
After the full 11-byte filename comparison succeeds, this routine extracts the directory entry metadata and verifies the password. It reads the directory entry flags byte, extracts the extent count from bits 0-2, and checks up to two password hash locations in the extent chain area. The blank-password hash value CAD2H is special: if the caller's password hash matches CAD2H, password verification is bypassed entirely, allowing access to any file without a password.
4EFF
POP AF F1
Discard a saved value from the stack. This cleans up one of the entries pushed during the directory search loop (the saved HL from 4ED7H that was already restored at 4EFEH).
4F00
POP AF F1
Discard another saved value from the stack. This cleans up the second entry from the outer search loop, bringing the stack back to the state expected by the caller.
4F01
PUSH BC C5
Save Register Pair BC onto the stack. Register C contains the drive number where the matching file was found.
4F02
PUSH HL E5
Save Register Pair HL onto the stack. HL points to the start of the matching directory entry.
4F03
LD A,(HL) 7E
Fetch the first byte of the directory entry (flags/info byte) into Register A. This byte contains the extent count in bits 0-2 and various flags in the upper bits.
4F04
LD (51F9H),A 32 F9 51
Self-Modifying Code
Store the directory entry flags byte into the data area at 51F9H. This saves the full flags value for use during FCB population.
4F07
AND 07H E6 07
AND Register A with 07H to isolate bits 0-2, extracting the extent count. This count indicates how many extent entries exist for this file in the directory.
4F09
LD C,A 4F
Load Register C with the extent count (0-7). This will be used to track position within the extent chain during password verification.
4F0A
LD B,00H 06 00
Load Register B with 00H to clear the high byte. Register Pair BC now holds the extent count as a 16-bit value (B=00H, C=count).
4F0C
LD A,10H 3E 10
Load Register A with 10H (16 decimal), the offset within the directory entry where the extent chain data begins.
4F0E
ADD A,L 85
ADD the directory entry base offset (Register L) to 10H to compute the absolute position of the extent chain area.
4F0F
LD L,A 6F
Store the computed extent chain offset into Register L. HL now points to the first extent entry within the directory entry.
4F10
LD DE,(51F5H) ED 5B F5 51
Load Register Pair DE with the caller's password hash from 51F5H. This 16-bit value was computed by the password hash routine at 511EH and stored at 4E9BH.
Blank Password Bypass Check
If the caller provided no password (all spaces), the computed hash will be CAD2H. This special value causes the password check to be skipped entirely, granting access to the file regardless of its actual password.
4F14
PUSH HL E5
Save the extent chain pointer onto the stack. It will be restored after the password comparison.
4F15
LD HL,0CAD2H 21 D2 CA
Load Register Pair HL with the blank-password hash value CAD2H. This is the hash produced when all 8 password bytes are spaces (20H).
4F18
XOR A AF
Set Register A to zero and clear the CARRY FLAG. The cleared carry is required for the SBC instruction that follows.
4F19
SBC HL,DE ED 52
SUBtract Register Pair DE (caller's password hash) from HL (blank-password hash CAD2H) with borrow. If the values are equal, HL becomes 0000H and the Z FLAG is set.
4F1B
POP HL E1
Restore the extent chain pointer from the stack into Register Pair HL.
4F1C
If the Z FLAG (Zero) has been set because the caller's password hash equals CAD2H (blank password), JUMP to 4F56H to bypass password verification entirely and proceed to FCB population. When no password is provided, the file opens without restriction.
First Password Location Check
The caller provided a non-blank password. Check if it matches the first password hash stored in the extent chain. The password hash is stored as a 2-byte value at the current extent position.
4F1E
LD A,(HL) 7E
Fetch the low byte of the first password hash from the directory entry's extent chain area into Register A.
4F1F
INC HL 23
INCrement HL to point to the high byte of the password hash.
4F20
PUSH HL E5
Save the extent chain pointer (now at the high byte) onto the stack.
4F21
LD H,(HL) 66
Load Register H with the high byte of the stored password hash from the directory entry.
4F22
LD L,A 6F
Load Register L with the low byte of the stored password hash (previously fetched into A at 4F1EH). Register Pair HL now contains the stored password hash from the directory.
4F23
XOR A AF
Set Register A to zero and clear the CARRY FLAG for the SBC comparison.
4F24
SBC HL,DE ED 52
SUBtract Register Pair DE (caller's password hash) from HL (stored password hash). If they match, HL = 0000H and the Z FLAG is set.
4F26
POP HL E1
Restore the extent chain pointer from the stack.
4F27
If the Z FLAG (Zero) has been set because the first password hash matches, JUMP to 4F56H to proceed to FCB population. The password has been verified.
Second Password Location Check
The first password hash did not match. If the file has a second password hash location (extent count allows it), check that as well.
4F29
LD A,C 79
Load Register A with the extent count from Register C (bits 0-2 of the directory flags byte, range 0-7).
4F2A
CP 07H FE 07
Compare the extent count against 07H. If the count is 7, all extent slots are used and there is no room for a second password hash.
4F2C
If the Z FLAG (Zero) has been set because the extent count is exactly 7 (all slots full), JUMP to 4F39H to return error 19H (access denied). There is no second password location to check.
4F2E
INC HL 23
INCrement HL to advance past the high byte of the first password hash, positioning to the next extent entry.
4F2F
LD B,C 41
Load Register B with the extent count from Register C. This saves the count for later use.
4F30
LD A,(HL) 7E
Fetch the low byte of the second password hash from the next extent chain position into Register A.
4F31
INC HL 23
INCrement HL to point to the high byte of the second password hash.
4F32
LD H,(HL) 66
Load Register H with the high byte of the second password hash from the directory entry.
4F33
LD L,A 6F
Load Register L with the low byte (from 4F30H). Register Pair HL now contains the second stored password hash.
4F34
XOR A AF
Set Register A to zero and clear the CARRY FLAG for the SBC comparison.
4F35
SBC HL,DE ED 52
SUBtract Register Pair DE (caller's password hash) from HL (second stored password hash). If they match, the Z FLAG is set.
4F37
If the Z FLAG (Zero) has been set because the second password hash matches, JUMP to 4F3FH to handle the secondary-password-matched case (which may set additional access flags).
Password Mismatch - Access Denied
Neither password hash matched the caller's password. Return error 19H.
4F39
POP HL E1
Restore Register Pair HL from the stack (cleaning up the PUSH at 4F02H).
4F3A
POP BC C1
Restore Register Pair BC from the stack (cleaning up the PUSH at 4F01H).
4F3B
LD A,19H 3E 19
Load Register A with error code 19H (access denied / password mismatch). Neither the first nor the second password hash in the directory entry matched the caller's password.
4F3D
OR A B7
OR Register A with itself to set the NZ FLAG (since A = 19H). This signals an error condition.
4F3E
RET C9
Return to caller with NZ FLAG set and Register A = 19H (access denied).
Secondary Password Matched
The second password hash matched. Check whether the access mode requires setting the file-open-with-write flag (bit 1 of 430FH). Also patch the overlay return instruction at 4C4DH.
4F3F
LD A,C 79
Load Register A with the extent count from Register C.
4F40
CP 06H FE 06
Compare the extent count against 06H. The value 06H may indicate a specific access control level that triggers additional flag settings.
4F42
If the NZ FLAG (Not Zero) has been set because the extent count is not 06H, JUMP to 4F56H to proceed directly to FCB population without setting additional flags.
Self-Modifying Code
The LD B instruction at 4F44H has its operand modified by the code at 4E6FH, which saves the original 430FH system flags value. At runtime, B receives the saved flags value rather than the initial 00H.
4F44
LD B,00H 06 00
Self-Modifying Code
Load Register B with the saved system flags value. The operand at 4F45H was written by the code at 4E6FH with the original 430FH value captured at the start of the open operation.
4F46
BIT 2,B CB 50
Test bit 2 of Register B (the saved system flags). Bit 2 is the open-new active flag. If it was set before SYS2 cleared it at 4E72H, additional processing is needed.
4F48
If the Z FLAG (Zero) has been set because bit 2 was not set in the saved flags, JUMP to 4F4FH to skip the flag-setting step. The open-new flag was not active.
4F4A
LD HL,430FH 21 0F 43
Point Register Pair HL to the system flags byte at 430FH.
4F4D
SET 1,(HL) CB CE
SET bit 1 of the system flags byte at 430FH. Bit 1 is the file-open-with-write flag, indicating that the file was opened using the secondary password (which may grant write access).
4F4F
LD HL,4C4DH 21 4D 4C
Point Register Pair HL to the SYS0 overlay return instruction at 4C4DH. This address contains an instruction that is patched to control overlay return behavior.
4F52
LD (HL),0C9H 36 C9
Store C9H (the RET opcode) at 4C4DH. This patches the overlay return instruction to a simple RET, ensuring the overlay exits cleanly after the file open completes.
4F54
LD A,05H 3E 05
Load Register A with 05H. This access mode value will be stored as the OR operand for FCB population, indicating the secondary-password access level.
Proceed to FCB Population
Store the access mode value and fall through to the FCB builder.
4F56
LD (4FDAH),A 32 DA 4F
Self-Modifying Code
Store the access mode value (Register A) into the operand of the OR instruction at 4FD9H. This value will be OR'd into the FCB access flags during FCB population at 4FCEH. The value varies: 00H for blank-password access, 05H for secondary-password access.
4F59
POP HL E1
Restore Register Pair HL from the stack (from the PUSH at 4F02H). HL points to the start of the matching directory entry.
4F5A
POP BC C1
Restore Register Pair BC from the stack (from the PUSH at 4F01H). Register C contains the drive number.
4F5B
JUMP to 4FCEH to populate the FCB from the matched directory entry. HL points to the entry, C contains the drive number, and the access mode is stored at 4FDAH.
4F5EH - Open New or Existing File Entry
Entry point for the open-new-or-existing file path. Reached from request 20H (via 4E61H with A=00H) or internally when the open-existing path needs to fall through to creating a new file. This routine clears bit 2 of the system flags at 430FH, then attempts to open an existing file. If the file is not found (error 18H), it falls through to the new-file creation logic.
4F5E
LD (4FDAH),A 32 DA 4F
Self-Modifying Code
Store the access mode value (Register A, which is 00H from the request 20H entry at 4E64H) into the OR operand at 4FDAH for FCB population.
4F61
PUSH HL E5
Save Register Pair HL (filespec pointer) onto the stack.
4F62
LD HL,430FH 21 0F 43
Point Register Pair HL to the system flags byte at 430FH.
4F65
RES 2,(HL) CB 96
RESET bit 2 of the system flags at 430FH. Bit 2 is the open-new active flag. Clearing it indicates that we are not currently in the open-new path (even though we may fall into it later if the file is not found).
4F67
POP HL E1
Restore Register Pair HL (filespec pointer) from the stack.
4F68
GOSUB to 4E6CH, which is the body of the Open Existing File handler starting just after the 49E9H context save call. This saves the 430FH flags, clears bits 1-2, checks for INIT marker, parses filespec, and searches the directory. If the file is found, the FCB is populated and the routine returns with Z FLAG set.
4F6B
RET Z C8
Return to caller if the Z FLAG (Zero) has been set because the file was found and opened successfully. The FCB has been populated.
4F6C
CP 18H FE 18
Compare Register A (the error code from the failed open) against 18H (file not found). If the file was not found, we can proceed to create a new file. Any other error is a real failure.
4F6E
RET NZ C0
Return to caller if the NZ FLAG (Not Zero) has been set because the error was something other than file-not-found (e.g., directory read error 16H, access denied 19H). The error code is in Register A.
File Not Found - Create New Entry
The file does not exist. Prepare to allocate a new directory entry. The drive number and filespec are set up for the free-slot scanner.
4F6F
LD A,10H 3E 10
Load Register A with 10H, the initial directory entry flags value for a new file. The value 10H in bit 4 indicates this is a newly created entry.
4F71
LD (51F9H),A 32 F9 51
Store the initial flags value 10H into the directory entry flags area at 51F9H. This will be used when the new directory entry is written.
4F74
LD A,(4E9FH) 3A 9F 4E
Fetch the drive number from the self-modifying operand at 4E9FH. If a specific drive was requested in the filespec, this contains that drive number. If no drive was specified, it contains FFH.
4F77
LD C,A 4F
Load Register C with the drive number from Register A. Register C will track the current drive during the free-slot search.
4F78
INC A 3C
INCrement Register A. If A was FFH (no specific drive), it wraps to 00H and the Z FLAG is set.
4F79
If the NZ FLAG (Not Zero) has been set because a specific drive was requested (A was not FFH), JUMP to 4F7CH to search only that drive for a free slot.
4F7B
LD C,A 4F
Set Register C to 00H (A = 00H after INC from FFH). When no drive is specified, the free-slot search starts on drive 0.
Drive Loop for Free Slot Search
Register C = current drive number. For each drive, select it, read the directory, and scan for an empty entry using the tick-based random-start scanner.
4F7C
GOSUB to the drive select routine at 5147H. This selects drive C and detects write-protect status.
4F7F
If the NZ FLAG (Not Zero) has been set because the drive select failed, JUMP to 4F8CH to try the next drive or return directory-full error.
4F81
If the CARRY FLAG has been set because the drive is write-protected, JUMP to 4F8CH to skip this drive. A new file cannot be created on a write-protected disk.
4F83
GOSUB to the directory sector read routine at 51B5H. This reads directory sector 1 into the 4200H buffer.
4F86
RET NZ C0
Return to caller if the directory read failed. Error code 16H is in Register A.
4F87
GOSUB to the free directory slot scanner at 50DDH. This uses the system tick counter as a pseudo-random starting offset to distribute new entries across the directory. On return, Z FLAG set = free slot found at (HL), NZ = no free slot on this drive.
4F8A
If the Z FLAG (Zero) has been set because a free directory slot was found, JUMP to 4F9CH to write the new directory entry into that slot.
No Free Slot on This Drive
Try the next drive or return directory-full error.
4F8C
LD A,(4E9FH) 3A 9F 4E
Fetch the requested drive number from the self-modifying operand at 4E9FH. FFH means search all drives.
4F8F
INC A 3C
INCrement Register A. If FFH, wraps to 00H (Z FLAG set = search all drives).
4F90
If the NZ FLAG (Not Zero) has been set because a specific drive was requested, JUMP to 4F98H to return error 1AH (directory full on the specified drive).
4F92
INC C 0C
INCrement Register C (current drive number) to try the next drive.
4F93
LD A,C 79
Load Register A with the updated drive number.
4F94
CP 08H FE 08
Compare against 08H (one past last valid drive). If A < 08H, CARRY is set.
4F96
If the CARRY FLAG has been set because there are still more drives to check, LOOP BACK to 4F7CH to try the next drive.
Directory Full
All drives have been checked without finding a free directory slot.
4F98
LD A,1AH 3E 1A
Load Register A with error code 1AH (directory full). No free directory entries are available on any accessible drive.
4F9A
OR A B7
OR Register A with itself to set the NZ FLAG.
4F9B
RET C9
Return to caller with NZ FLAG set and Register A = 1AH (directory full).
4F9CH - Write New Directory Entry
A free directory slot has been found by the scanner at 50DDH. This routine writes the filename hash as the entry's first byte, writes the directory sector to disk, then reads the full entry back and populates it with the filename, extension, flags, and padding. Finally it writes the entry to disk and calls the FCB population routine.
4F9C
LD B,L 45
Load Register B with the low byte of the free directory slot pointer (Register L). This is the directory entry offset within the 4200H buffer, used as the directory info byte for the SYS0 write/read routines.
4F9D
LD A,(51DEH) 3A DE 51
Fetch the computed 8-bit filename hash from 51DEH into Register A. This hash was computed by the routine at 50CBH from the parsed filename.
4FA0
LD (HL),A 77
Store the filename hash as the first byte of the directory entry at the free slot. This marks the slot as active and provides the fast-match hash for future directory searches.
4FA1
GOSUB to the directory sector write routine at 51C7H. This writes the modified directory sector (with the new hash byte) from the 4200H buffer to disk.
4FA4
If the directory write succeeded (Z FLAG set), GOSUB to the SYS0 directory read routine at 4B10H. This reads back the complete 32-byte directory entry using the info byte in Register B. On return, HL points to the entry data.
4FA7
RET NZ C0
Return to caller if either the directory write or the read-back failed. The error code (17H or 16H) is in Register A.
Populate Directory Entry
The directory entry has been read back into the buffer. HL points to the entry. Now copy the flags, filename, and extension data into the entry, and fill the extent chain area with FFH padding.
4FA8
PUSH HL E5
Save the directory entry pointer onto the stack. This will be restored after the LDIR operations for the FCB population call.
4FA9
PUSH BC C5
Save Register Pair BC (B = entry offset, C = drive number) onto the stack.
4FAA
EX DE,HL EB
Exchange DE and HL. DE now points to the directory entry (destination for the LDIR copy), and HL is free to be set to the source.
4FAB
LD BC,0005H 01 05 00
Load Register Pair BC with 0005H (5 bytes). This is the number of bytes to copy from the flags area at 51F9H to the directory entry starting at offset +00H.
4FAE
LD HL,51F9H 21 F9 51
Point Register Pair HL to the directory entry flags data at 51F9H. This contains the flags byte (10H for a new file) followed by 4 bytes of initialization data.
4FB1
LDIR ED B0
Block Move
Copy 5 bytes from 51F9H (source) to the directory entry (destination). Source: HL = 51F9H (flags and init data). Destination: DE = directory entry start. Count: BC = 5 bytes. Direction: incrementing. This fills the first 5 bytes of the new directory entry with the flags and initialization data.
4FB3
LD BC,0011H 01 11 00
Load Register Pair BC with 0011H (17 decimal). This is the number of bytes to copy for the filename (8 bytes), extension (3 bytes), and 6 additional bytes of metadata.
4FB6
LD HL,51E8H 21 E8 51
Point Register Pair HL to the filename buffer at 51E8H (11 bytes: 8 name + 3 extension, space-padded by the filespec parser).
4FB9
LDIR ED B0
Block Move
Copy 17 bytes from the filename buffer at 51E8H to the directory entry at offset +05H (after the 5-byte flags block). Source: HL = 51E8H. Destination: DE = entry + 05H. Count: BC = 17 bytes. This writes the filename, extension, and additional metadata fields.
4FBB
EX DE,HL EB
Exchange DE and HL. HL now points to the position after the last LDIR byte (entry + 16H), which is the start of the extent chain area.
Fill Extent Chain with FFH
The remaining 10 bytes of the directory entry (the extent chain area) are filled with FFH to indicate no extents are allocated yet.
4FBC
LD B,0AH 06 0A
Load Register B with 0AH (10 decimal), the number of extent chain bytes to fill with FFH padding.
4FBE
LD (HL),0FFH 36 FF
Store FFH at the current extent chain position. The value FFH indicates an unused/empty extent slot.
4FC0
INC HL 23
INCrement HL to the next extent chain byte.
4FC1
DECrement B and loop back to 4FBEH if not zero. This fills all 10 extent chain bytes with FFH.
4FC3
POP BC C1
Restore Register Pair BC from the stack. B = directory entry offset, C = drive number.
4FC4
GOSUB to the SYS0 directory write routine at 4B1FH. This writes the fully populated directory entry back to disk.
4FC7
POP HL E1
Restore Register Pair HL (directory entry pointer, saved at 4FA8H) from the stack.
4FC8
RET NZ C0
Return to caller if the directory write failed. Error code 17H is in Register A.
4FC9
GOSUB to the FCB population routine at 4FCEH. This fills the FCB at IX with the file's metadata from the directory entry. HL points to the entry, C = drive number.
4FCC
SCF 37
Set the CARRY FLAG. This indicates to the caller that a new file was created (as opposed to opening an existing file). The carry flag distinguishes new-file-created from existing-file-opened.
4FCD
RET C9
Return to caller with CARRY FLAG set (new file created) and Z FLAG set (success, since 4FCEH returns with Z on success).
4FCEH - Populate FCB From Directory Entry
This routine fills the FCB at IX with the file's metadata extracted from the directory entry. It sets the status byte to 80H (file open), constructs the access mode flags from the saved parameters, copies the sector buffer address, drive number, directory info byte, EOF byte offset, access mode, sector count, and extent chain data from the directory entry into the FCB fields.
4FCE
EX DE,HL EB
Exchange DE and HL. Register Pair DE now points to the directory entry, and HL is free for FCB addressing.
4FCF
PUSH IX DD E5
Save the FCB base address (IX) onto the stack.
4FD1
POP HL E1
Restore the FCB base address into Register Pair HL. This PUSH IX / POP HL transfers IX to HL for sequential byte-by-byte writing.
4FD2
LD (HL),80H 36 80
Store 80H at FCB offset +00H (status byte). Bit 7 set indicates the file is open.
4FD4
INC HL 23
INCrement HL to FCB offset +01H (access mode and state flags).
Build Access Mode Flags
The access mode byte is constructed by testing the saved access mode parameter and OR'ing together several flag bits from self-modifying locations.
4FD5
LD A,(51FDH) 3A FD 51
Fetch the access mode parameter from the data area at 51FDH. This value was saved from Register B at 4E7FH and represents the caller's requested access mode (read, write, extend, etc.).
4FD8
OR A B7
OR Register A with itself to test if the access mode is zero. The Z FLAG is set if the access mode is 00H (no access restrictions).
4FD9
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the password-level access value. The operand at 4FDAH was written at 4F56H or 4F5EH: 00H for blank-password access, 05H for secondary-password access. This value contributes to the FCB access flags.
4FDB
If the Z FLAG (Zero) has been set because the caller's access mode at 51FDH was 00H, JUMP to 4FDFH to skip setting bit 7. A zero access mode means no special mode is requested.
4FDD
OR 80H F6 80
OR Register A with 80H to set bit 7 of the access flags. Bit 7 indicates that the caller specified a non-zero access mode (fixed record length mode or similar).
4FDF
OR 20H F6 20
OR Register A with 20H to set bit 5 of the access flags. Bit 5 indicates the file is open for extend (can allocate new sectors when writing past EOF).
4FE1
OR 00H F6 00
Self-Modifying Code
OR Register A with the '!' flag value. The operand at 4FE2H was written at 50ADH: 08H if the filespec was followed by '!' (exclamation mark), or 00H otherwise. When 08H, bit 3 is set in the access flags, indicating special handling.
4FE3
LD (HL),A 77
Store the constructed access mode flags at FCB offset +01H. This byte now contains the combined access permissions and mode flags.
4FE4
INC HL 23
INCrement HL to FCB offset +02H.
4FE5
LD (HL),00H 36 00
Store 00H at FCB offset +02H (additional state flags, initialized to zero).
4FE7
INC HL 23
INCrement HL to FCB offset +03H.
Self-Modifying Code
The LD DE instruction at 4FE9H has its operand modified by the code at 4E82H, which saved the original filespec pointer (HL). At runtime, DE receives the saved filespec address.
4FE8
PUSH DE D5
Save Register Pair DE (directory entry pointer) onto the stack. DE is needed after the self-modifying LD DE instruction.
4FE9
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the saved filespec pointer. The operand at 4FEAH was written at 4E82H with the original HL value (the command line filespec position).
4FEC
LD (HL),E 73
Store the low byte of the filespec pointer at FCB offset +03H.
4FED
INC HL 23
INCrement HL to FCB offset +04H.
4FEE
LD (HL),D 72
Store the high byte of the filespec pointer at FCB offset +04H. FCB bytes +03H/+04H now contain the sector buffer address (actually the filespec pointer in VTOS, which differs from TRSDOS 2.3's usage).
4FEF
INC HL 23
INCrement HL to FCB offset +05H.
4FF0
POP DE D1
Restore Register Pair DE (directory entry pointer) from the stack.
4FF1
LD (HL),00H 36 00
Store 00H at FCB offset +05H (byte offset within current sector, initialized to start of sector).
4FF3
INC HL 23
INCrement HL to FCB offset +06H.
4FF4
LD (HL),C 71
Store the drive number (Register C) at FCB offset +06H.
4FF5
INC HL 23
INCrement HL to FCB offset +07H.
4FF6
LD (HL),B 70
Store the directory entry info byte (Register B, the entry offset within the directory sector) at FCB offset +07H. This identifies the directory entry for future Close/Kill operations.
4FF7
INC HL 23
INCrement HL to FCB offset +08H.
Extract EOF and Extent Data From Directory Entry
The following code reads fields from the directory entry (pointed to by DE) and copies them into the FCB. It accesses directory entry offsets by adding constants to E (the low byte of DE).
4FF8
LD A,03H 3E 03
Load Register A with 03H, the offset within the directory entry where the EOF byte offset field is stored.
4FFA
ADD A,E 83
ADD the directory entry base (Register E) to 03H to compute the absolute address of the EOF byte offset field.
4FFB
LD E,A 5F
Store the computed address into Register E. DE now points to directory entry offset +03H (EOF byte offset).
4FFC
LD A,(DE) 1A
Fetch the EOF byte offset from the directory entry at offset +03H into Register A. This indicates where the file ends within the last sector (00H = ends on a sector boundary).
4FFD
LD (HL),A 77
Store the EOF byte offset at FCB offset +08H.
4FFE
INC HL 23
INCrement HL to FCB offset +09H.
4FFF
INC DE 13
INCrement DE to directory entry offset +04H.
5000
LD A,(51FDH) 3A FD 51
Fetch the access mode parameter from the data area at 51FDH. This is the original access mode value passed by the caller in Register B.
5003
LD (HL),A 77
Store the access mode parameter at FCB offset +09H (record length / access mode field).
5004
INC HL 23
INCrement HL to FCB offset +0AH.
5005
LD (HL),00H 36 00
Store 00H at FCB offset +0AH (current sector position low byte, initialized to sector 0).
5007
INC HL 23
INCrement HL to FCB offset +0BH.
5008
LD (HL),00H 36 00
Store 00H at FCB offset +0BH (current sector position high byte, initialized to sector 0).
500A
INC HL 23
INCrement HL to FCB offset +0CH.
Copy Sector Count From Directory Entry
The sector count field in the directory entry is at offset +14H from the start. Since DE currently points to entry +04H, we need to add 10H to reach offset +14H.
500B
LD A,10H 3E 10
Load Register A with 10H, the distance from directory entry offset +04H to offset +14H (where the sector count data begins).
500D
ADD A,E 83
ADD 10H to Register E to advance the directory entry pointer to offset +14H.
500E
LD E,A 5F
Store the updated pointer into Register E. DE now points to directory entry offset +14H (extent chain/sector count area).
500F
LD A,(DE) 1A
Fetch the first extent data byte from directory entry offset +14H.
5010
LD (HL),A 77
Store it at FCB offset +0CH (total sector count low byte or first extent byte).
5011
INC HL 23
INCrement HL to FCB offset +0DH.
5012
INC DE 13
INCrement DE to directory entry offset +15H.
5013
LD A,(DE) 1A
Fetch the second extent data byte from directory entry offset +15H.
5014
LD (HL),A 77
Store it at FCB offset +0DH.
5015
INC HL 23
INCrement HL to FCB offset +0EH.
5016
INC DE 13
INCrement DE to directory entry offset +16H.
5017
LD A,(DE) 1A
Fetch the third extent data byte from directory entry offset +16H.
5018
LD (HL),A 77
Store it at FCB offset +0EH.
5019
INC HL 23
INCrement HL to FCB offset +0FH.
501A
INC DE 13
INCrement DE to directory entry offset +17H.
501B
LD A,(DE) 1A
Fetch the fourth extent data byte from directory entry offset +17H into Register A. This is the last byte of the first 4-byte extent group.
501C
LD (HL),A 77
Store it at FCB offset +0FH.
501D
INC HL 23
INCrement HL to FCB offset +10H.
Compute Sector Count From First Extent
The last extent data byte (bits 0-4) contains the granule count minus 1 for the first extent. Extract this value, increment by 1, and store as the initial sector count accumulator in BC.
501E
AND 1FH E6 1F
AND Register A with 1FH to isolate bits 0-4, extracting the granule count minus 1 from the last extent data byte. Bits 5-7 contain track-related data.
5020
INC A 3C
INCrement Register A by 1 to convert from granule-count-minus-1 to actual granule count. For example, if the field was 04H, the actual count is 5 granules.
5021
LD C,A 4F
Load Register C with the granule count for the first extent.
5022
LD B,00H 06 00
Load Register B with 00H. Register Pair BC now contains the running granule total (initially just the first extent's count).
5024H - Copy Remaining Extent Chain Into FCB
This loop copies up to 4 additional extent entries from the directory entry into the FCB, accumulating the total granule count in BC. Each extent entry is a 2-byte pair where the second byte's bits 0-4 contain the granule count minus 1. The loop terminates when a terminator byte (FEH or FFH) is encountered or all 4 entries have been processed. Any remaining FCB extent slots are filled with FFH padding.
5024
LD A,04H 3E 04
Load Register A with 04H, the maximum number of additional extent entries to process (up to 4 more after the first). This serves as the loop counter.
Extent Copy Loop
For each iteration, check the next byte in the directory entry. If it is FEH or FFH, the extent chain has ended. Otherwise, copy the 2-byte extent entry to the FCB and accumulate the granule count.
5026
PUSH AF F5
Save the loop counter (Register A) onto the stack.
5027
INC DE 13
INCrement DE to advance to the next byte in the directory entry's extent chain area.
5028
LD A,(DE) 1A
Fetch the next byte from the directory entry's extent chain into Register A.
5029
CP 0FEH FE FE
Compare Register A against FEH. Both FEH (chain continuation) and FFH (end of chain) are terminators. If A >= FEH, the CARRY FLAG is clear (NC).
502B
If the NO CARRY FLAG has been set because the byte is FEH or FFH (a terminator), JUMP to 5045H to pad the remaining FCB extent slots with FFH.
Copy Extent Entry
The byte is not a terminator, so it is the first byte of a valid extent entry. Store the running granule count, then copy the 2-byte extent entry and update the accumulator.
502D
LD (HL),C 71
Store the low byte of the running granule count (Register C) at the current FCB extent position. This records the cumulative sector count up to this point in the chain.
502E
INC HL 23
INCrement HL to the next FCB byte.
502F
LD (HL),B 70
Store the high byte of the running granule count (Register B).
5030
INC HL 23
INCrement HL to the next FCB byte.
5031
LD A,(DE) 1A
Fetch the first byte of the extent entry from the directory (this is the same byte loaded at 5028H, re-read from DE).
5032
LD (HL),A 77
Store the first extent byte in the FCB.
5033
INC HL 23
INCrement HL.
5034
INC DE 13
INCrement DE to the second byte of the extent entry.
5035
LD A,(DE) 1A
Fetch the second byte of the extent entry from the directory. Bits 0-4 contain the granule count minus 1 for this extent.
5036
LD (HL),A 77
Store the second extent byte in the FCB.
5037
INC HL 23
INCrement HL.
Accumulate Granule Count
Extract the granule count from this extent (bits 0-4 of the second byte + 1) and add it to the running total in BC.
5038
AND 1FH E6 1F
AND Register A with 1FH to isolate bits 0-4, extracting the granule count minus 1.
503A
INC A 3C
INCrement Register A by 1 to get the actual granule count for this extent.
503B
ADD A,C 81
ADD the new extent's granule count to the low byte of the running total (Register C). The result is in A.
503C
LD C,A 4F
Store the updated low byte back into Register C.
503D
ADC A,B 88
ADD the carry (from the previous ADD) to Register B (high byte of running total). This handles 8-bit overflow: A = C + B + carry.
503E
SUB C 91
SUBtract Register C from Register A to isolate just the high byte: A = (C + B + carry) - C = B + carry. This is the new high byte of the running total.
503F
LD B,A 47
Store the updated high byte into Register B. Register Pair BC now contains the updated running granule total.
5040
POP AF F1
Restore the loop counter from the stack.
5041
DEC A 3D
DECrement the loop counter by 1.
5042
If the NZ FLAG (Not Zero) has been set because more extent entries remain to process, LOOP BACK to 5026H.
5044
RET C9
Return to caller. All 4 additional extent entries have been processed and the FCB is fully populated. The Z FLAG is set (from DEC A reaching zero).
5045H - Pad Remaining FCB Extent Slots With FFH
When the extent chain terminates early (FEH or FFH encountered before all 4 entries are processed), this routine fills the remaining FCB extent slots with FFH to indicate unused entries. The number of remaining slots is calculated from the loop counter.
5045
POP AF F1
Restore the loop counter from the stack (saved by PUSH AF at 5026H). Register A contains the number of remaining extent entries that were not processed (1-4).
5046
RLCA 07
Rotate Register A left by 1 bit, multiplying by 2. Each unprocessed extent entry requires 4 bytes of FFH padding in the FCB (2-byte count + 2-byte extent data).
5047
RLCA 07
Rotate Register A left by 1 bit again, multiplying by 4 total. Register A now contains the number of FFH bytes needed (4 per unprocessed entry).
5048
LD B,A 47
Load Register B with the byte count for the FFH padding loop.
FFH Padding Loop
5049
LD (HL),0FFH 36 FF
Store FFH at the current FCB position. FFH marks the extent slot as unused/empty.
504B
INC HL 23
INCrement HL to the next FCB byte.
504C
DECrement B and loop back to 5049H if not zero. This fills all remaining extent slots with FFH.
504E
XOR A AF
Set Register A to zero and set the Z FLAG. This indicates successful completion (no error).
504F
RET C9
Return to caller with Z FLAG set (success) and Register A = 00H.
5050H - Filespec Parser (NAME/EXT.PASSWORD:DRIVE)
Parses a filespec string from the command line (pointed to by HL) into separate buffers for filename (51E8H, 8 bytes), password (51E0H, 8 bytes), extension (51F0H, 3 bytes within the filename area), and drive number (4E9FH). All buffers are first filled with spaces (20H). The parser handles the format NAME/EXT.PASSWORD:DRIVE, where / separates name from extension, . separates extension from password, and : precedes the drive number. The '!' suffix is detected and flagged.
Initialize Buffers
Fill the filename buffer (11 bytes) and password buffer (8 bytes) with spaces before parsing.
5050
LD B,0BH 06 0B
Load Register B with 0BH (11 decimal), the size of the filename buffer (8 name + 3 extension bytes).
5052
LD DE,51E8H 11 E8 51
Point Register Pair DE to the filename buffer at 51E8H.
5055
LD A,20H 3E 20
Load Register A with 20H (ASCII space). All buffer positions will be initialized to spaces.
5057
LD (DE),A 12
Store a space character at the current buffer position.
5058
INC DE 13
INCrement DE to the next buffer position.
5059
DECrement B and loop back to 5057H if not zero. This fills all 11 bytes of the filename buffer with spaces.
505B
LD B,08H 06 08
Load Register B with 08H (8 decimal), the size of the password buffer.
505D
LD DE,51E0H 11 E0 51
Point Register Pair DE to the password buffer at 51E0H.
5060
LD (DE),A 12
Store a space character at the current password buffer position (A still = 20H from 5055H).
5061
INC DE 13
INCrement DE to the next password buffer position.
5062
DECrement B and loop back to 5060H if not zero. This fills all 8 bytes of the password buffer with spaces.
Initialize Drive to "All Drives"
Set the drive specifier to FFH (search all drives) as the default.
5064
LD A,0FFH 3E FF
Load Register A with FFH, the "all drives" marker. This is the default when no :D drive specifier is present in the filespec.
5066
LD (4E9FH),A 32 9F 4E
Self-Modifying Code
Store FFH into the drive number operand at 4E9FH. If the filespec contains a :D drive specifier, this will be overwritten later at 50A1H with the actual drive number.
Parse Filename (8 Characters Maximum)
Call the character extraction subroutine to copy up to 8 alphanumeric characters from the command line into the filename buffer.
5069
LD DE,51E8H 11 E8 51
Point Register Pair DE to the filename buffer at 51E8H (8-byte name portion).
506C
LD B,08H 06 08
Load Register B with 08H (8 decimal), the maximum number of filename characters to extract.
506E
GOSUB to the character extraction subroutine at 50B1H. This reads alphanumeric characters from (HL), stores them at (DE), and returns with A containing the delimiter character that stopped extraction. Register B reflects how many characters were NOT stored (8 - count).
5071
LD C,A 4F
Save the delimiter character into Register C. This is the character that terminated the filename extraction (e.g., '/' for extension, '.' for password, ':' for drive, or a non-alphanumeric terminator).
5072
LD A,B 78
Load Register A with Register B (the remaining character count after extraction).
5073
CP 08H FE 08
Compare Register A against 08H. If B is still 08H, no characters were extracted (the filename is empty).
5075
If the NZ FLAG (Not Zero) has been set because B is not 08H (at least one character was extracted), JUMP to 507BH to continue parsing the remaining filespec components.
Empty Filename Error
No alphanumeric characters were found before a delimiter. Return error 13H.
5077
LD A,13H 3E 13
Load Register A with error code 13H (invalid filespec - no filename characters).
5079
OR A B7
OR Register A with itself to set the NZ FLAG.
507A
RET C9
Return to caller with NZ FLAG set and Register A = 13H (invalid filespec).
Parse Extension, Password, and Drive
Check the delimiter character to determine which component to parse next. '/' = extension follows, '.' = password follows, ':' = drive follows. Any other character means the filespec is complete.
507B
LD A,C 79
Restore the delimiter character from Register C into Register A.
507C
CP 2FH FE 2F
Compare Register A against 2FH (ASCII '/'). If the delimiter is '/', an extension follows.
507E
LD DE,51F0H 11 F0 51
Point Register Pair DE to the extension buffer at 51F0H (3 bytes within the filename area, at positions 51F0H-51F2H = bytes 8-10 of the 11-byte filename).
5081
LD B,03H 06 03
Load Register B with 03H (3 decimal), the maximum number of extension characters.
5083
If the Z FLAG was set because the delimiter was '/' (extension separator), GOSUB to the character extractor at 50B1H to parse up to 3 extension characters. If the delimiter was not '/', this call is skipped and A still holds the delimiter.
5086
CP 2EH FE 2E
Compare Register A against 2EH (ASCII '.'). If the delimiter is '.', a password follows.
5088
LD DE,51E0H 11 E0 51
Point Register Pair DE to the password buffer at 51E0H (8 bytes).
508B
LD B,08H 06 08
Load Register B with 08H (8 decimal), the maximum number of password characters.
508D
If the Z FLAG was set because the delimiter was '.' (password separator), GOSUB to the character extractor at 50B1H to parse up to 8 password characters. If the delimiter was not '.', this call is skipped.
5090
CP 3AH FE 3A
Compare Register A against 3AH (ASCII ':'). If the delimiter is ':', a drive number follows.
5092
If the NZ FLAG (Not Zero) has been set because the delimiter is not ':', JUMP to 50A6H to skip drive parsing and check for the '!' suffix.
Parse Drive Number
The ':' was found. The next character should be a digit 0-7 representing the drive number.
5094
LD A,(HL) 7E
Fetch the next character from the command line (the drive digit) into Register A.
5095
SUB 30H D6 30
SUBtract 30H (ASCII '0') from Register A to convert from ASCII digit to binary value. If the character was '0'-'7', the result is 00H-07H. If less than '0', the CARRY FLAG is set.
5097
If the CARRY FLAG has been set because the character was less than '0' (not a valid digit), JUMP to 509DH to return error 20H (invalid drive specifier).
5099
CP 08H FE 08
Compare the binary drive number against 08H. Valid drives are 0-7, so if A >= 08H, it is out of range.
509B
If the CARRY FLAG has been set because A < 08H (valid drive number), JUMP to 50A1H to store the drive number.
Invalid Drive Number
509D
LD A,20H 3E 20
Load Register A with error code 20H (invalid drive specifier).
509F
OR A B7
OR Register A with itself to set the NZ FLAG.
50A0
RET C9
Return to caller with NZ FLAG set and Register A = 20H (invalid drive specifier).
Store Valid Drive Number
50A1
LD (4E9FH),A 32 9F 4E
Self-Modifying Code
Store the valid drive number (00H-07H) into the operand of the LD A instruction at 4E9EH, overwriting the default FFH (all drives). Subsequent directory searches will target only this specific drive.
50A4
INC HL 23
INCrement HL to advance past the drive digit character in the command line.
50A5
LD A,(HL) 7E
Fetch the next character from the command line into Register A. This is the character after the drive digit, which will be checked for the '!' suffix.
Check for '!' Suffix
The '!' character after the filespec triggers special handling (sets bit 3 in the FCB access mode flags).
50A6
SUB 21H D6 21
SUBtract 21H (ASCII '!') from Register A. If the character is '!', the result is 00H and the Z FLAG is set.
50A8
LD A,08H 3E 08
Load Register A with 08H. This value will be stored as the '!' flag if the character was '!' (Z FLAG set).
50AA
If the Z FLAG (Zero) has been set because the character was '!', JUMP to 50ADH to store 08H as the flag value.
50AC
XOR A AF
Set Register A to zero. The character was not '!', so the flag value is 00H (no special handling).
50AD
LD (4FE2H),A 32 E2 4F
Self-Modifying Code
Store the '!' flag (08H or 00H) into the operand of the OR instruction at 4FE1H. During FCB population, this value will be OR'd into the access mode flags. The value 08H sets bit 3, enabling special file handling.
50B0
RET C9
Return to caller with Z FLAG set (from XOR A at 50ACH or JR Z at 50AAH). Register A = 00H or 08H. The filespec has been successfully parsed.
50B1H - Character Extraction Subroutine
Extracts alphanumeric characters from the command line (at HL) and stores them into a buffer (at DE), up to a maximum of B characters. The first character is accepted if it is a letter (A-Z); subsequent characters are accepted if they are digits (0-9) or letters (A-Z). Extraction stops when a non-alphanumeric character is encountered or the buffer is full. On return, Register A contains the delimiter character that stopped extraction.
50B1
LD A,(HL) 7E
Fetch the first character from the command line at (HL) into Register A. The first character must be a letter to be accepted (digits are not valid as the first character).
50B2
INC HL 23
INCrement HL to advance to the next command line character.
50B3
JUMP to 50BEH to perform the letter-only check on the first character. This skips the digit check that is applied to subsequent characters.
Subsequent Character Loop
After the first character, both digits (0-9) and letters (A-Z) are accepted.
50B5
LD A,(HL) 7E
Fetch the next character from the command line into Register A.
50B6
INC HL 23
INCrement HL to advance to the next character.
50B7
CP 30H FE 30
Compare Register A against 30H (ASCII '0'). If A < 30H, it is not a digit or letter.
50B9
RET C D8
Return if the CARRY FLAG is set (character < '0'). Register A contains the non-alphanumeric delimiter character.
50BA
CP 3AH FE 3A
Compare Register A against 3AH (one past ASCII '9'). If A < 3AH, the character is a digit (0-9).
50BC
If the CARRY FLAG is set because A < 3AH (a valid digit 0-9), JUMP to 50C4H to store the character in the buffer.
Letter Check
The character is not a digit. Check if it is an uppercase letter (A-Z).
50BE
CP 41H FE 41
Compare Register A against 41H (ASCII 'A'). If A < 41H, it is not a letter.
50C0
RET C D8
Return if the CARRY FLAG is set (character < 'A'). Register A contains the non-alphabetic delimiter.
50C1
CP 5BH FE 5B
Compare Register A against 5BH (one past ASCII 'Z'). If A < 5BH, the character is an uppercase letter.
50C3
RET NC D0
Return if the NO CARRY FLAG is set (character >= 5BH, not a letter). Register A contains the non-alphabetic delimiter.
Store Valid Character
The character is alphanumeric. Store it in the output buffer.
50C4
LD (DE),A 12
Store the valid alphanumeric character at the current buffer position (DE).
50C5
INC DE 13
INCrement DE to the next buffer position.
50C6
DECrement B (remaining character count) and loop back to 50B5H if not zero. When B reaches zero, the buffer is full.
Buffer Full
The maximum number of characters has been stored. Read the next character (the delimiter) and return it.
50C8
LD A,(HL) 7E
Fetch the next character from the command line (the first character after the full buffer). This is the delimiter character.
50C9
INC HL 23
INCrement HL to advance past the delimiter character.
50CA
RET C9
Return to caller with Register A containing the delimiter character. Register B = 00H (buffer is full).
50CBH - Compute 8-Bit Filename CRC Hash (Request 50H)
Computes an 8-bit CRC hash over 11 bytes of filename data (8 name + 3 extension) pointed to by HL. The algorithm XORs each byte into an accumulator and rotates left after each byte. The result is stored at 51DEH (both bytes set to the same hash value). A special case ensures the hash is never zero (since 00H is used for empty directory entries): if the computed hash is zero, it is changed to 01H. This routine is also the handler for request code 50H.
50CB
LD B,0BH 06 0B
Load Register B with 0BH (11 decimal), the number of filename bytes to hash.
50CD
XOR A AF
Set Register A to zero. This initializes the hash accumulator.
Hash Loop
For each of the 11 filename bytes: XOR it into the accumulator, then rotate left.
50CE
XOR (HL) AE
XOR Register A with the current filename byte at (HL). This folds the byte value into the running hash.
50CF
INC HL 23
INCrement HL to the next filename byte.
50D0
RLCA 07
Rotate Register A left by 1 bit (circular, bit 7 wraps to bit 0). This distributes the hash bits to produce better distribution across all 8 bit positions.
50D1
DECrement B and loop back to 50CEH if not zero. After all 11 bytes, Register A contains the final 8-bit hash.
Zero Hash Fixup
If the hash is 00H, change it to 01H to avoid collision with the empty-entry marker.
50D3
OR A B7
OR Register A with itself to test if the hash is zero.
50D4
If the NZ FLAG (Not Zero) has been set because the hash is non-zero, JUMP to 50D7H to store the hash. No fixup is needed.
50D6
INC A 3C
INCrement Register A from 00H to 01H. This prevents a zero hash, which would be indistinguishable from an empty directory entry.
50D7
LD L,A 6F
Load Register L with the hash value. Both L and H will be set to the same value.
50D8
LD H,A 67
Load Register H with the same hash value. Register Pair HL now contains the hash in both bytes.
50D9
LD (51DEH),HL 22 DE 51
Store the hash value (both bytes identical) at 51DEH. The low byte at 51DEH is used for directory entry comparisons; the high byte at 51DFH is also set for consistency.
50DC
RET C9
Return to caller. The filename hash is stored at 51DEH and also in Register Pair HL.
50DDH - Scan Directory for Free Entry Slot
Searches the directory for an unused entry slot to hold a new file. Uses the system tick counter at 4040H as a pseudo-random starting offset to distribute new files across the directory, reducing clustering. The routine scans through directory sectors, advancing by 32-byte entry boundaries, and returns Z flag set with HL pointing to the free slot when found.
50DD
LD A,(4040H) 3A 40 40
Fetch the system tick counter (heartbeat timer incremented by the interrupt service routine at 4040H) to use as a pseudo-random seed for the directory scan starting position. This distributes new file entries across the directory to reduce clustering.
50E0
PUSH AF F5
Save the tick counter value onto the stack for later use as the scan starting offset at 50FBH.
50E1
LD A,07H 3E 07
Load Register A with 07H, the directory entry info byte for the last standard directory entry on the disk. This will be passed to 4797H to read that directory sector.
50E3
GOSUB to the SYS0 routine at 4797H to read the directory sector identified by the info byte in Register A. This reads the last directory sector to determine the total directory capacity. Returns with Register A containing the directory entry's data byte.
50E6
PUSH DE D5
Save Register Pair DE onto the stack (preserving the caller's context).
50E7
LD D,A 57
Copy the directory data byte from Register A into Register D for field extraction. This byte encodes both the number of entries per sector and the track/sector position.
50E8
AND A,1FH E6 1F
Mask bits 0-4 to extract the entry count field (lower 5 bits). This gives the number of directory entries in the last sector minus one.
50EA
LD E,A 5F
Store the masked entry count into Register E.
50EB
INC E 1C
INCrement Register E by 1 to convert from zero-based count to actual count of entries in the last directory sector.
50EC
XOR A,D AA
XOR Register A (which still holds the masked lower 5 bits) with Register D (the full data byte) to isolate the upper 3 bits (bits 5-7). These upper bits encode the track-related portion of the directory layout.
50ED
RLCA 07
Rotate Register A Left through Carry. First of three rotates to shift the upper 3 bits down into the lower bit positions.
50EE
RLCA 07
Rotate Register A Left through Carry (second rotate).
50EF
RLCA 07
Rotate Register A Left through Carry (third rotate). The three RLCA instructions rotate the upper 3 bits into bits 0-2, giving the number of full directory sectors.
50F0
INC A 3C
INCrement Register A by 1 to convert from zero-based sector count to actual sector count. Register A now holds the total number of directory sectors.
50F1
GOSUB to the SYS0 24-bit multiply routine at 4B6CH. This computes the total number of directory entries across all sectors: full_sectors x entries_per_sector (Register A x Register E). Returns the product in HL:A.
50F4
POP DE D1
Restore the caller's Register Pair DE from the stack.
50F5
SUB A,02H D6 02
SUBtract 2 from the total entry count in Register A. This excludes the first two directory entries (entry 0 is the GAT and entry 1 is the HIT), which are reserved system entries and cannot hold file data.
50F7
LD (510CH),A 32 0C 51
Self-Modifying Code
Store the adjusted total entry count as the operand of the CP instruction at 510BH. This sets the upper boundary for the directory scan loop so it wraps around correctly when scanning through all available slots.
50FA
POP AF F1
Restore the tick counter value (saved at 50E0H) from the stack into Register A. This pseudo-random starting offset ensures new files are distributed across the directory.
50FB
LD L,A 6F
Store the tick counter value into Register L to use as the starting directory entry index for the scan.
50FC
GOSUB to 5103H to check whether the directory entry at offset L is a free slot. This begins the scan from the pseudo-random starting position.
50FF
RET Z C8
If the Z FLAG (Zero) has been set, a free directory slot was found. Return to the caller with HL pointing to the free entry.
Wraparound Scan
If the initial scan from the tick-based starting point reached the end of the directory without finding a free slot, the code wraps around to scan from the beginning. Register L is set to 3FH, then incremented to 40H (the first valid entry after the reserved system entries at positions 00H-1FH), ensuring all slots are checked before declaring directory full.
5100
LD L,3FH 2E 3F
Load Register L with 3FH. This will be incremented to 40H at 5102H to start scanning from the first valid file entry position (entry index 2, offset 40H = 2 x 32 bytes).
5102
INC L 2C
INCrement Register L by 1. On first entry from 5100H, this sets L to 40H (first file entry). On subsequent iterations from 511AH, this advances to the next entry position within the current sector.
5103
LD A,L 7D
Copy the current scan position from Register L into Register A for boundary testing.
5104
AND A,0D8H E6 D8
Mask with 0D8H (11011000 binary). This tests whether the entry index is within the valid range for the current sector. If the result is zero, the entry position is within the first 32 bytes of a sector (a valid entry boundary at offset 00H-1FH within the 256-byte sector page).
5106
If the Z FLAG (Zero) has been set (entry index is within valid range), JUMP to 5112H to advance to the next 32-byte entry boundary within the sector.
5108
LD A,L 7D
Reload the current entry index from Register L into Register A.
5109
AND A,1FH E6 1F
Mask bits 0-4 to extract the entry offset within the current directory sector (0-31). Each directory entry occupies 32 bytes, so this gives the entry number within the sector.
510B
CP A,00H FE 00
Self-Modifying Code
Compare Register A against the total available entry count. The operand 00H is overwritten at 50F7H with the actual directory entry limit (total entries minus 2 for reserved system entries). If Register A >= the limit, the NO CARRY FLAG is set indicating the scan has wrapped past the end.
510D
If the NO CARRY FLAG has been set (entry index >= total entries), JUMP to 511CH to return NZ indicating directory full (all entries have been scanned without finding a free slot).
510F
LD A,(HL) 7E
Fetch the first byte of the directory entry at the current position pointed to by Register Pair HL. A value of 00H indicates an unused/free directory entry slot.
5110
OR A,A B7
Test Register A against zero. If the first byte of the directory entry is 00H, the Z FLAG is set indicating a free slot has been found.
5111
RET Z C8
If the Z FLAG (Zero) has been set (entry byte is 00H), this is a free directory slot. Return to the caller with HL pointing to the free entry and Z flag set to indicate success.
5112
LD A,L 7D
Copy the current position from Register L into Register A to advance to the next directory entry boundary.
5113
ADD A,20H C6 20
ADD 20H (32 decimal) to Register A to advance to the next 32-byte directory entry. Each directory entry occupies exactly 32 bytes within the 256-byte sector buffer.
5115
LD L,A 6F
Store the updated position back into Register L.
5116
If the NO CARRY FLAG has been set (no overflow past 256-byte sector boundary), LOOP BACK to 5103H to test this entry position. The ADD at 5113H sets Carry when L wraps past FFH, indicating the end of the current sector page.
Sector Boundary Crossed
When the ADD at 5113H carries (L wrapped past FFH), the scan has reached the end of the current 256-byte directory sector buffer. The code checks whether the position has wrapped back to 1FH (the last byte of the reserved area), which indicates all sectors have been scanned. Otherwise, it continues scanning from the next valid position.
5118
CP A,1FH FE 1F
Compare Register A (the wrapped position) against 1FH. A value of 1FH after carry means the scan has wrapped completely around the directory back to the reserved system entry area.
511A
If the NZ FLAG (Not Zero) has been set (position is not at the wraparound point), LOOP BACK to 5102H to increment L and continue scanning the next entry.
511C
OR A,A B7
Set flags based on Register A. Since Register A is non-zero at this point (either from the sector count limit check or the wraparound detection), the NZ FLAG is set to indicate failure (no free directory slot found).
511D
RET C9
Return to the caller with NZ flag set indicating the directory is full and no free entry slot was found.
511EH - Compute 16-Bit Password Hash
Computes a 16-bit polynomial CRC hash over an 8-byte password string. The hash is used for password verification during file open operations. The algorithm processes each byte of the password through a series of bit manipulations, shifts, and XOR operations that produce a well-distributed 16-bit hash value. Entry: DE points to the password buffer (8 bytes, processed in reverse order from offset +07H). Exit: HL contains the 16-bit password hash.
511E
LD HL,0FFFFH 21 FF FF
Initialize Register Pair HL to FFFFH as the starting hash accumulator value. Using all-ones as the seed ensures that an all-zeros input does not produce a zero hash.
5121
LD B,08H 06 08
Load Register B with 08H (8 decimal) as the byte counter. The password is exactly 8 bytes long, so this loop will process each byte once.
5123
LD A,E 7B
Copy the low byte of the password buffer pointer from Register E into Register A to calculate the starting offset.
5124
ADD A,07H C6 07
ADD 07H to Register A to point to the last byte of the 8-byte password string (offset +07H from the buffer start). The password is processed in reverse order, from the last byte to the first.
5126
LD E,A 5F
Store the adjusted pointer back into Register E. DE now points to the last byte of the password.
5127
LD A,(DE) 1A
Loop Start
Fetch the current password byte from the address pointed to by Register Pair DE into Register A. On the first iteration, this reads the last byte of the password.
5128
PUSH DE D5
Save the password pointer onto the stack. DE will be repurposed as a working register pair during the hash computation.
5129
LD D,A 57
Copy the current password byte into Register D for use in the XOR mixing operations below.
512A
LD E,H 5C
Copy the high byte of the current hash accumulator from Register H into Register E. This saves the previous high byte for the polynomial feedback computation.
512B
LD A,L 7D
Copy the low byte of the hash accumulator from Register L into Register A for bit manipulation.
512C
AND A,07H E6 07
Mask bits 0-2 of the hash low byte, extracting the lowest 3 bits for use in the polynomial feedback calculation.
512E
RRCA 0F
Rotate Register A Right through Carry (first of three). This shifts the 3-bit value right, moving bit 0 into bit 7.
512F
RRCA 0F
Rotate Register A Right through Carry (second rotate). Continues shifting the extracted bits into the upper positions.
5130
RRCA 0F
Rotate Register A Right through Carry (third rotate). The three RRCA instructions effectively rotate the lower 3 bits into bits 5-7, creating a feedback value for the polynomial CRC.
5131
XOR A,L AD
XOR Register A with the hash low byte in Register L. This mixes the rotated feedback bits back into the hash accumulator.
5132
LD L,A 6F
Store the mixed result back into Register L as the new hash low byte.
5133
LD H,00H 26 00
Clear Register H to zero. Register Pair HL now holds only the mixed low byte in L, preparing for the shift-left operation.
5135
ADD HL,HL 29
Shift Register Pair HL left by 1 bit (multiply by 2). First of four left shifts.
5136
ADD HL,HL 29
Shift Register Pair HL left by 1 bit (second shift, total multiply by 4).
5137
ADD HL,HL 29
Shift Register Pair HL left by 1 bit (third shift, total multiply by 8).
5138
ADD HL,HL 29
Shift Register Pair HL left by 1 bit (fourth shift, total multiply by 16). The four ADD HL,HL instructions shift the mixed value left by 4 bits (one nibble), spreading the polynomial feedback across both bytes of HL.
5139
XOR A,H AC
XOR Register A (which still holds the pre-shift mixed value from 5131H) with the shifted high byte in Register H. This combines the original and shifted feedback values.
513A
XOR A,D AA
XOR Register A with the current password character byte in Register D. This mixes the input data into the high byte of the hash.
513B
LD D,A 57
Store the new high hash byte into Register D.
513C
LD A,L 7D
Copy the shifted low byte of HL into Register A for the low byte mixing step.
513D
ADD HL,HL 29
Shift Register Pair HL left by 1 more bit (fifth total shift, multiply by 32). This creates additional bit dispersion for the polynomial.
513E
XOR A,H AC
XOR Register A (pre-fifth-shift low byte) with the post-fifth-shift high byte. This creates cross-byte mixing between the two halves of the hash.
513F
XOR A,E AB
XOR Register A with Register E (the saved previous high hash byte from 512AH). This feeds the previous iteration's high byte into the new low byte, creating inter-iteration dependency.
5140
LD E,A 5F
Store the computed new low hash byte into Register E.
5141
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the new 16-bit hash value (D=high, E=low moved into H and L), and DE is freed for restoring the password pointer.
5142
POP DE D1
Restore the password buffer pointer from the stack.
5143
DEC DE 1B
DECrement Register Pair DE by 1 to move to the previous password byte (the password is processed in reverse order, from byte 7 down to byte 0).
5144
DECrement Register B and LOOP BACK to 5127H if not zero. This repeats the hash computation for all 8 password bytes.
Loop End
5146
RET C9
Return to the caller with Register Pair HL containing the computed 16-bit password hash.
5147H - Select Drive and Detect Write Protect Status
Selects the specified drive, reads the disk's GAT sector, and detects the write-protect status using a 3-phase FDC index pulse polling technique. The routine checks whether the drive has a valid configuration (IY+00H contains a JP instruction C3H) and whether the drive supports write operations (IY+03H bit 3 clear). It reads the GAT sector to extract format parameters and then polls the FDC status register for the index pulse to determine write-protect state. Entry: Register C contains the drive number. Exit: Z flag set if drive valid and accessible; Carry flag indicates write-protect status (set = write-protected).
5147
PUSH IY FD E5
Save Register Pair IY onto the stack. IY will be used to point to the drive parameter block during this routine.
5149
GOSUB to the SYS0 routine at 478FH to select the drive specified in Register C. This routine sets up IY to point to the drive parameter block and activates the drive's motor and select latch.
514C
LD A,(IY+00H) FD 7E 00
Fetch the first byte of the drive parameter block pointed to by IY. A valid, configured drive has a JP instruction (C3H) at this position as part of its dispatch vector.
514F
CP A,0C3H FE C3
Compare Register A against C3H (the opcode for JP). If the drive parameter block does not begin with a JP instruction, the drive is not configured.
5151
If the NZ FLAG (Not Zero) has been set (first byte is not C3H), this drive is not configured. JUMP to 51A1H to restore IY and return with NZ flag indicating an invalid drive.
5153
LD A,(IY+03H) FD 7E 03
Fetch byte at offset +03H of the drive parameter block. This byte contains drive capability flags, where bit 3 indicates whether the drive supports write operations.
5156
CPL 2F
Complement Register A (flip all bits). This inverts the flag byte so that the subsequent AND test checks for the bit being clear in the original.
5157
AND A,08H E6 08
Mask bit 3. After the CPL, this tests whether bit 3 was clear in the original (meaning the drive is a writable drive). If the original bit 3 was set (read-only), the CPL clears it and the AND result is zero.
5159
If the Z FLAG (Zero) has been set (original bit 3 was set, meaning the drive is read-only or not writable), JUMP to 51A1H to restore IY and return with NZ indicating the drive cannot be written to.
515B
PUSH DE D5
Save Register Pair DE onto the stack before using DE as working registers for the disk I/O operations.
515C
LD D,(IY+05H) FD 56 05
Fetch the number of tracks on this drive from offset +05H of the drive parameter block into Register D. This is needed for the GAT sector read operation.
515F
LD E,00H 1E 00
Load Register E with 00H. Register Pair DE now holds (track_count, 0), specifying track 0 for the GAT sector read.
5161
GOSUB to the SYS0 routine at 475EH to seek to track 0 on the selected drive, where the GAT sector resides.
5164
GOSUB to the SYS0 routine at 4759H to read the FDC status register into Register A. This initial read clears any pending status and prepares for the index pulse detection sequence.
3-Phase Index Pulse Detection
The write-protect detection requires waiting for a complete disk rotation to read the write-protect tab sensor. This is accomplished by polling the FDC status register for three index pulse transitions: (1) wait for the index pulse to match a timeout-derived target (synchronization), (2) wait for the pulse state to change (confirming the disk is spinning), (3) read the final status to extract the write-protect bit. The system tick counter at 4040H provides the timeout reference.
5167
LD A,(4040H) 3A 40 40
Fetch the current system tick counter from 4040H into Register A. This provides the timeout base for the index pulse polling loops.
516A
ADD A,0BH C6 0B
ADD 0BH (11 decimal) to the tick counter to create a timeout target. At approximately 25 ticks per second (40ms per tick), 11 ticks equals about 440ms, which is more than enough time for one complete disk rotation (approximately 200ms at 300 RPM).
516C
LD D,A 57
Store the timeout target into Register D for comparison in the polling loops at 51A4H.
516D
Phase 1
GOSUB to 51A4H to poll the FDC status register. This routine reads the status register and tests bit 1 (Index Pulse). It returns with Z set if timeout occurred, or NZ with the bit 1 state.
5170
If the NZ FLAG (Not Zero) has been set (index pulse bit is active, no timeout yet), LOOP BACK to 516DH to keep waiting. Phase 1 waits for the index pulse to go inactive (bit 1 = 0, Z flag set by BIT test).
5172
Phase 2
GOSUB to 51A4H to poll the FDC status register again. This phase waits for the index pulse to become active (bit 1 = 1), confirming the disk has completed a partial rotation.
5175
If the Z FLAG (Zero) has been set (index pulse bit is still inactive), LOOP BACK to 5172H to keep waiting. Phase 2 waits for the index pulse to become active (bit 1 = 1, NZ flag set by BIT test).
Write-Protect Status Extraction
After the index pulse sequence confirms the disk is spinning, the code reads the GAT sector from track 0 to validate the disk format, then extracts the write-protect status from the FDC status register. The write-protect tab is a hardware sensor read through bit 6 of the Type I status register.
5177
PUSH AF F5
Save the FDC status register value (from the last 51A4H call) onto the stack. This contains the write-protect bit that will be extracted after the GAT read.
5178
GOSUB to the SYS0 routine at 4B65H to set up the track/sector parameters for the directory read. This routine uses the drive parameter block to compute the physical disk address for the GAT sector.
517B
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H, the directory sector buffer where the GAT sector data will be loaded.
517E
LD E,00H 1E 00
Load Register E with 00H to specify sector 0 (the GAT sector on track 0).
5180
GOSUB to the SYS0 sector read routine at 4B45H to read the GAT sector from disk into the buffer at 4200H. Returns Z flag set on success, NZ on error.
5183
If the NZ FLAG (Not Zero) has been set (disk read error), JUMP to 51B0H to clean up the stack and return with an error condition.
5185
LD DE,(42CCH) ED 5B CC 42
Load Register Pair DE with the 16-bit value at 42CCH in the GAT sector buffer. Offset CCH within the 256-byte GAT sector contains the disk format parameters: E holds the low byte (disk type/configuration) and D holds the high byte (additional format flags).
5189
LD A,22H 3E 22
Load Register A with 22H (34 decimal), the base value for constructing the drive configuration entry. This base value will be combined with the disk format parameters.
518B
ADD A,E 83
ADD the disk type byte from Register E to the base value 22H. This creates the drive's configuration index value that encodes the disk format.
518C
LD (IY+06H),A FD 77 06
Store the computed configuration index into offset +06H of the drive parameter block. This records the disk format detected during the drive select operation.
518F
RES 5,(IY+04H) FD CB 04 AE
Clear bit 5 of the drive parameter block at offset +04H. This resets the double-density flag, assuming single-density until proven otherwise.
5193
BIT 5,D CB 6A
Test bit 5 of Register D (the high byte of the GAT format parameters from 42CDH). Bit 5 indicates whether the disk uses double-density recording.
5195
If the Z FLAG (Zero) has been set (bit 5 is clear, indicating single-density), JUMP to 519BH to skip setting the double-density flag.
5197
SET 5,(IY+04H) FD CB 04 EE
Set bit 5 of the drive parameter block at offset +04H. This marks the drive as using double-density recording, matching the disk format detected in the GAT sector.
519B
POP AF F1
Restore the FDC status register value (saved at 5177H) from the stack. This contains the write-protect status captured during the index pulse detection.
519C
RLCA 07
Rotate Register A Left through Carry. This shifts bit 6 (Write Protect) into bit 7.
519D
AND A,80H E6 80
Mask bit 7 to isolate the write-protect status (which was originally bit 6 of the FDC status register, now shifted to bit 7 by the RLCA).
519F
ADD A,A 87
ADD Register A to itself (double it). This shifts bit 7 into the Carry flag. If the disk is write-protected (bit 6 was set), the Carry flag is now set. The result in A is 00H, which sets the Z flag indicating a valid drive.
51A0
POP DE D1
Restore Register Pair DE from the stack (saved at 515BH).
51A1
POP IY FD E1
Restore Register Pair IY from the stack (saved at 5147H).
51A3
RET C9
Return to the caller. Z flag set = drive valid and accessible (Carry flag indicates write-protect: set = write-protected, clear = writable). NZ flag set = drive not configured or not writable.
51A4H - Poll FDC Status Register With Timeout
Polls the FDC status register for index pulse transitions (bit 1) with a timeout based on the system tick counter. Returns the current index pulse state in the Z flag, or forces a timeout exit if the tick counter reaches the target value in Register D. Used by the 3-phase index pulse detection at 516DH-5175H.
51A4
LD A,(4040H) 3A 40 40
Fetch the current system tick counter from 4040H into Register A. This is compared against the timeout target in Register D to detect a timeout condition.
51A7
CP A,D BA
Compare Register A (current tick count) against Register D (timeout target, set at 516CH). If they are equal, the timeout has expired.
51A8
If the Z FLAG (Zero) has been set (tick counter matches timeout target), JUMP to 51B0H to abort the polling and return with an error condition. The timeout prevents the system from hanging if the drive is not spinning or not present.
51AA
GOSUB to the SYS0 routine at 4759H to read the FDC status register into Register A. This reads the current hardware state of the floppy disk controller.
51AD
BIT 1,A CB 4F
Test bit 1 of the FDC status register. Bit 1 is the Index Pulse flag: 1 = index hole detected (pulse active), 0 = no index hole (pulse inactive). The result sets the Z flag accordingly: Z = bit clear (no pulse), NZ = bit set (pulse active).
51AF
RET C9
Return to the caller with the Z flag reflecting the index pulse state. The calling code at 516DH-5175H uses this to detect pulse transitions.
51B0H - Timeout or Error Exit for Drive Operations
Error exit point for drive select timeout or disk read failure. Cleans up the stack by popping the saved AF value, forces the carry flag set (OR 01H makes A non-zero, then the JR falls through to POP DE and POP IY), and returns with NZ flag indicating an error.
51B0
POP AF F1
Pop and discard the top stack entry. This removes either the saved FDC status (from 5177H during write-protect detection) or the return address of the calling poll loop, cleaning up the stack before the error return.
51B1
OR A,01H F6 01
OR Register A with 01H to force a non-zero result. This ensures the NZ flag is set on return to indicate an error condition to the caller.
51B3
JUMP to 51A0H to restore DE and IY from the stack and return. The NZ flag (from the OR 01H) indicates that the drive operation failed due to timeout or read error.
51B5H - Read Directory Sector
Reads directory sector 1 from the current drive into the buffer at 4200H. Preserves BC and DE. Returns with Z flag set on success, NZ on error (with error code 16H preloaded in Register A). This is the standard directory sector read routine used throughout SYS2.
51B5
PUSH BC C5
Save Register Pair BC onto the stack to preserve the caller's context.
51B6
PUSH DE D5
Save Register Pair DE onto the stack to preserve the caller's context.
51B7
GOSUB to the SYS0 routine at 4B65H to set up the track/sector parameters for the directory read. This routine configures the FDC for the correct track and sector based on the current drive's parameter block.
51BA
LD E,01H 1E 01
Load Register E with 01H to specify sector 1, the first directory data sector (sector 0 is the GAT).
51BC
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H, the directory sector buffer where the data will be loaded.
51BF
GOSUB to the SYS0 sector read routine at 4B45H to read the directory sector from disk into the buffer at 4200H. Returns Z flag set on success, NZ on disk error.
51C2
POP DE D1
Restore Register Pair DE from the stack.
51C3
POP BC C1
Restore Register Pair BC from the stack.
51C4
LD A,16H 3E 16
Load Register A with error code 16H (directory read error). This is preloaded so that if the caller tests NZ (from the CALL at 51BFH failing), the error code is already in Register A for error reporting.
51C6
RET C9
Return to the caller. Z flag from the 4B45H call indicates success or failure; error code 16H is in Register A for the failure case.
51C7H - Write Directory Sector
Writes the directory sector buffer at 4200H back to directory sector 1 on the current drive, with optional write-verify. Preserves BC and DE. Returns with the result of the write operation in Register A and flags. Uses the SYS0 write routine at 4768H followed by a conditional verify read at 4772H.
51C7
PUSH BC C5
Save Register Pair BC onto the stack to preserve the caller's context.
51C8
PUSH DE D5
Save Register Pair DE onto the stack to preserve the caller's context.
51C9
GOSUB to the SYS0 routine at 4B65H to set up the track/sector parameters for the directory write. This configures the FDC for the correct physical disk address.
51CC
LD E,01H 1E 01
Load Register E with 01H to specify sector 1, the directory data sector.
51CE
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H, the directory sector buffer containing the data to be written.
51D1
GOSUB to the SYS0 sector write routine at 4768H to write the directory sector buffer to disk. Returns Z flag set on success.
51D4
If the Z FLAG (Zero) has been set (write succeeded), GOSUB to the SYS0 write-verify routine at 4772H to read the sector back and verify the data was written correctly. This conditional call skips verification if the initial write already failed.
51D7
CP A,06H FE 06
Compare the verify result in Register A against error code 06H. This checks for a specific recoverable error condition from the write-verify operation. If the result equals 06H, the Z flag is set.
51D9
LD A,17H 3E 17
Load Register A with error code 17H (directory write error). This preloads the error code for the failure case. Note that this LD instruction does not affect the flags set by the CP at 51D7H.
51DB
POP DE D1
Restore Register Pair DE from the stack.
51DC
POP BC C1
Restore Register Pair BC from the stack.
51DD
RET C9
Return to the caller with the write result in flags and error code 17H preloaded in Register A for the failure case.
51DEH - Data Area (Runtime Variables and Buffers)
Runtime variable storage and working buffers used by SYS2. These locations are modified during execution by the various file management routines. All values shown are initial/default values from the overlay image on disk; runtime values are set by the routines that use them.
51DE-51DF
DEFW 0000H 00 00
Filename Hash Storage
Two-byte area used to store the computed filename CRC hash (from 50CBH). The hash at 51DEH is compared against directory entries during the file search loop at 4EB3H-4EB8H. Written by 50D9H (LD (51DEH),HL).
51E0-51E7
DEFM " " 00 00 00 00 00 00 00 00
Password Buffer
8-byte buffer used to store the parsed password string from the filespec. Filled by the filespec parser at 5088H-508DH when a period (.) separator is encountered in the filespec. Initialized to spaces (20H) by 505DH-5064H. Used as input to the password hash routine at 511EH.
51E8-51F2
DEFM " " 00 00 00 00 00 00 00 00 00 00 00
Filename/Extension Buffer
11-byte buffer storing the parsed filename (8 bytes) and extension (3 bytes) from the filespec. Filled by the filespec parser at 506BH-5083H. Used for directory entry comparison at 4EECH-4EF7H and for writing new directory entries at 4FB6H-4FB9H. Initialized to spaces (20H) by 5052H-505BH.
51F3-51F4
DEFW 0000H 00 00
Password Hash (Primary)
Stores the computed 16-bit password hash for the filespec being processed. Written at 4E98H after calling the password hash routine. Used during password verification in the file match handler.
51F5-51F6
DEFW 0000H 00 00
Password Hash (Secondary)
Second copy of the password hash, written at 4E9BH simultaneously with the primary copy. Used as a comparison value during extent chain password verification at 4F14H-4F1BH and 4F23H-4F28H.
51F7-51F8
NOP x 2 00 00
Unused Padding
Two bytes of zero padding between the password hash storage and the directory entry flags area.
51F9
DEFB 00H 00
Directory Entry Flags
Stores the directory entry's flags byte during file creation and directory writes. Written at 4F04H and 4F71H with values that encode the file type and access attributes for the new directory entry.
51FA-51FC
NOP x 3 00 00 00
Directory Entry Data
Three bytes of working storage used during directory entry construction. Part of the 5-byte block copied via LDIR at 4FB1H (from 51F9H, BC=0005H) to initialize the first 5 bytes of a new directory entry.
51FD
DEFB 00H 00
Drive Number Variable
Stores the drive number (0-7) for the current file operation. Written at 4E7FH by the Open Existing File handler. Read at 4FD5H and 5000H during FCB population to record which drive the file resides on.
Gap: 51DEH-51FFH
The area from 51DEH to 51FFH (34 bytes) serves as runtime variable storage for SYS2. Although initialized to zeros in the overlay image on disk, these locations are written by the file management routines during execution. The overlay occupies the full 1024-byte slot from 4E00H to 51FFH.