4E00H - Request Code Dispatcher
Entry point for SYS2. Register A contains the request code with the function number encoded in bits 4-6. This dispatcher masks those bits and routes to the appropriate handler: 10H = Open Existing File, 20H = Kill File, 30H = Open New or Existing File. Any other value causes a silent return.
4E00
AND A,70H E6 70
Mask Register A to isolate bits 4-6 (the request code). This strips all other bits, leaving only the function selector: 10H, 20H, 30H, or an unsupported value.
4E02
CP A,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 4E12H to handle the Open Existing File request.
4E07
CP A,20H FE 20
Compare the masked request code against 20H (Kill 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 4ED8H to handle the Kill File request.
4E0C
CP A,30H FE 30
Compare the masked request code against 30H (Open New or Existing File). If Register A equals 30H, the Z FLAG is set.
4E0E
If the Z FLAG (Zero) has been set because the request code is 30H, JUMP to 4F50H to handle the Open New or Existing File request.
4E11
RET C9
Return to caller. If none of the three recognized request codes matched, execution falls through to this RET, silently ignoring the unsupported request.
4E12H - Open Existing File Handler (Request Code 10H)
Opens an existing file by searching the directory for a filename matching the filespec provided by the caller. On entry, IX points to the FCB to be populated, HL points to the filespec string, and B contains the granule count parameter. This routine parses the filespec into internal template buffers, computes a filename hash, then iterates through all directory sectors on up to 4 drives searching for a match.
4E12
GOSUB to SYS0 routine at 4896H to verify the overlay call context and set up registers for the file operation. This routine validates that the call came through the proper RST 28H dispatch mechanism and prepares IX, HL, and B with the caller's parameters.
4E15
LD A,B 78
Load Register A with the granule count parameter from Register B. This value was passed by the caller and represents the number of granules to pre-allocate for the file.
4E16
LD (4FAFH),A 32 AF 4F
Store the granule count parameter (Register A) to the local variable at 4FAFH for later use during FCB population.
4E19
LD (4FC0H),HL 22 C0 4F
Store the HL register pair (pointer to caller's context or filespec) to the local variable at 4FC0H, preserving it for later use during FCB population.
4E1C
PUSH IX DD E5
Save the FCB pointer (IX) onto the stack. The next two instructions transfer IX to HL for use with the filespec parser.
4E1E
POP HL E1
Restore the FCB pointer into HL. This PUSH IX / POP HL sequence is a standard Z80 idiom to copy IX into HL, since there is no direct LD HL,IX instruction.
4E1F
GOSUB to 5027H to parse the filespec string pointed to by HL into the internal template buffers: the 11-byte filename/extension template at 515DH and the 8-byte password buffer at 5155H. Also extracts the drive number into 5154H. Returns Z FLAG set on success, NZ on parse error.
4E22
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the filespec parse failed, return to caller with the error code still in Register A.
4E23
LD HL,515DH 21 5D 51
Point Register Pair HL to 515DH, the 11-byte filename/extension template buffer that was just populated by the filespec parser.
4E26
GOSUB to 509BH to compute an 8-bit CRC hash of the 11-byte filename/extension template at 515DH. The hash is returned in Register A and is used for quick comparison against directory entry hash values.
4E29
LD (4E4EH),A 32 4E 4E
Store the computed filename hash (Register A) to the local variable at 4E4EH. This hash will be compared against each directory entry's hash byte for fast filename matching.
4E2C
LD DE,5155H 11 55 51
Point Register Pair DE to 5155H, the 8-byte password buffer that was populated by the filespec parser.
4E2F
GOSUB to 50D1H to compute a 16-bit CRC-style hash of the 8-byte password buffer at 5155H. The hash is returned in HL.
4E32
LD (5168H),HL 22 68 51
Store the 16-bit password hash (HL) to the variable at 5168H for later comparison during directory entry matching.
4E35
LD (516AH),HL 22 6A 51
Store a second copy of the 16-bit password hash (HL) to 516AH. This duplicate is used as a reference copy during the directory scan.
Directory Scan Setup
The filespec has been parsed, the filename hash computed, and the password hash stored. Now the code prepares to iterate through directory sectors on each drive, searching for a matching filename entry.
4E38
LD A,(5154H) 3A 54 51
Fetch the drive number from 5154H (set by the filespec parser at 5027H). A value of FFH means no drive was specified, so all drives should be searched.
4E3B
LD C,A 4F
Copy the drive number (Register A) into Register C. Register C will serve as the current drive counter during the directory scan loop.
4E3C
INC A 3C
INCrement Register A by 1. If the drive number was FFH (no drive specified), this wraps to 00H and sets the Z FLAG, indicating all drives should be searched.
4E3D
JR NZ,4E40H 20 01
If the NZ FLAG (Not Zero) has been set because a specific drive was specified (not FFH), JUMP to 4E40H to skip the next instruction.
4E3F
LD C,A 4F
Load Register C with 00H (Register A after INC of FFH). When no drive was specified, start the search from drive 0.
Drive Scan Loop Start
Register C now contains the starting drive number (0-3 for a specific drive, or 0 when searching all drives). The following loop selects each drive, reads its directory sector, and scans entries for a filename match.
4E40
GOSUB to 50FDH to select drive C and detect whether the disk is write-protected. This routine issues a Force Interrupt to the FDC, calls the SYS0 drive select routine at 4600H, then polls the FDC status register for the index pulse to determine disk presence. Returns Z FLAG set if disk is present and ready, NZ if not ready or write-protected.
4E43
JR NZ,4E54H 20 0F
If the NZ FLAG (Not Zero) has been set because the drive is not ready or an error occurred, JUMP to 4E54H to try the next drive or return an error.
4E45
GOSUB to 512EH to read the directory sector for drive C into the 4D00H buffer. This reads sector 1 (the directory sector) from the disk using SYS0 routines.
4E48
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the directory sector read failed, return to caller with the disk error code in Register A.
Directory Entry Scan Loop
The directory sector is now in the 4D00H buffer. Each directory entry is 32 bytes (20H). The code scans through entries in this sector, checking each one for a filename match against the template at 515DH.
4E49
LD A,(HL) 7E
Fetch the first byte of the current directory entry pointed to by HL. This byte is the directory entry's attribute/status byte. A value of 00H means the entry is unused (free slot).
4E4A
OR A,A B7
Test Register A against itself to set flags. If the byte is 00H, the Z FLAG is set, meaning this entry is unused.
4E4B
If the Z FLAG (Zero) has been set because the directory entry is unused (00H), JUMP to 4E51H to skip this entry and advance to the next one.
4E4D
CP A,00H FE 00
Compare Register A (the directory entry attribute byte) against the filename hash value stored at 4E4EH. Self-Modifying Code
The immediate operand 00H at address 4E4EH was overwritten at 4E29H with the computed filename hash. At runtime, this instruction compares the directory entry's attribute byte against the hash for a quick match test. If they match, the Z FLAG is set.
4E4F
If the Z FLAG (Zero) has been set because the directory entry's attribute byte matches the filename hash, JUMP to 4E6AH to perform a detailed 11-byte filename comparison to confirm the match.
4E51
INC L 2C
INCrement the low byte of HL by 1. Since directory entries in the 4D00H buffer are accessed by incrementing L through the 256-byte page, this advances to the next byte. When L wraps from FFH to 00H (crossing the page boundary), the Z FLAG is set, indicating all entries in this sector have been scanned.
4E52
If the NZ FLAG (Not Zero) has been set because L has not wrapped to 00H, LOOP BACK to 4E49H to check the next directory entry byte.
4E54H - Next Drive or File Not Found
Reached when no matching filename was found in the current drive's directory sector. If a specific drive was requested, returns error 18H (file not found). If searching all drives, advances to the next drive number and loops.
4E54
LD A,(5154H) 3A 54 51
Fetch the drive number from 5154H. If this is FFH, no specific drive was requested, meaning we should try additional drives.
4E57
INC A 3C
INCrement Register A by 1. If the drive number was FFH (search all drives), this wraps to 00H and sets the Z FLAG.
4E58
If the NZ FLAG (Not Zero) has been set because a specific drive was specified (not FFH), JUMP to 4E60H to return error 18H (file not found on the specified drive).
4E5A
INC C 0C
INCrement Register C (current drive counter) by 1 to advance to the next drive.
4E5B
LD A,C 79
Load Register A with the new drive number from Register C.
4E5C
CP A,04H FE 04
Compare Register A against 04H (maximum drive number + 1). TRSDOS 2.3 supports drives 0-3. If Register A is less than 04H, the CARRY FLAG is set, meaning more drives remain to be searched.
4E5E
If the CARRY FLAG has been set because the drive number is less than 4, LOOP BACK to 4E40H to select the next drive and search its directory.
4E60
LD A,18H 3E 18
Load Register A with error code 18H. In TRSDOS 2.3, this is the "File Not Found" error code.
4E62
OR A,A B7
Set the NZ FLAG by ORing A with itself. Since A contains 18H (non-zero), the NZ FLAG is set, indicating an error condition to the caller.
4E63
RET C9
Return to caller with error code 18H (File Not Found) in Register A and the NZ FLAG set.
4E64H - Directory Match Cleanup on Mismatch
Called when a detailed filename comparison at 4E84H fails after the hash matched. Pops the saved registers from the stack (two pairs pushed at 4E6AH-4E6BH and 4E75H-4E76H) and continues scanning at 4E51H.
4E64
POP BC C1
Restore Register Pair BC from the stack (the inner push at 4E76H).
4E65
POP HL E1
Restore Register Pair HL from the stack (the inner push at 4E75H). HL now points back to the directory entry in the 4D00H buffer.
4E66
POP BC C1
Restore Register Pair BC from the stack (the outer push at 4E6BH).
4E67
POP HL E1
Restore Register Pair HL from the stack (the outer push at 4E6AH). HL now points to the directory entry where the hash matched.
4E68
JUMP to 4E51H to advance to the next directory entry and continue scanning.
4E6AH - Hash Match - Begin Detailed Filename Comparison
Reached when a directory entry's attribute byte matches the computed filename hash. This section reads the full directory entry from disk and performs a detailed 11-byte comparison of the filename/extension against the template at 515DH.
4E6A
PUSH HL E5
Save Register Pair HL onto the stack. HL points to the matching directory entry's first byte in the scan buffer. This is saved so we can return to this position if the detailed comparison fails.
4E6B
PUSH BC C5
Save Register Pair BC onto the stack. Register C contains the current drive number.
4E6C
LD B,L 45
Load Register B with the low byte of HL (the directory entry offset within the 4D00H page). Register B is used as the directory entry number parameter for the Read Directory Sector routine.
4E6D
GOSUB to SYS0 routine at 4AC1H to read the directory sector containing the entry identified by Register B into the 4D00H buffer. Returns with HL pointing to the start of the directory entry within the buffer. Returns Z FLAG set on success.
4E70
If the Z FLAG (Zero) has been set because the directory sector was read successfully, JUMP to 4E75H to proceed with the detailed filename comparison.
4E72
POP BC C1
Restore Register Pair BC from the stack (reversing the push at 4E6BH) because the directory read failed.
4E73
POP HL E1
Restore Register Pair HL from the stack (reversing the push at 4E6AH).
4E74
RET C9
Return to caller with the disk error code from the failed directory read still in Register A and the NZ FLAG set.
4E75
PUSH HL E5
Save Register Pair HL onto the stack. HL now points to the directory entry within the 4D00H buffer after the successful read.
4E76
PUSH BC C5
Save Register Pair BC onto the stack. These two pushes create a second pair on the stack for the cleanup at 4E64H if the comparison fails.
4E77
BIT 7,(HL) CB 7E
Test bit 7 of the first byte of the directory entry pointed to by HL. In TRSDOS 2.3, bit 7 set in the directory entry's first byte indicates the entry has been deleted (marked as killed). Active entries have bit 7 clear.
4E79
If the NZ FLAG (Not Zero) has been set because bit 7 is set (entry is deleted), JUMP to 4E64H to clean up the stack and continue scanning for the next entry.
Filename Comparison
The directory entry is active (not deleted). Now compare the 11-byte filename/extension field starting at offset +05H within the directory entry against the template at 515DH.
4E7B
LD A,05H 3E 05
Load Register A with 05H, the offset within the directory entry where the filename field begins.
4E7D
ADD A,L 85
ADD the filename offset (05H) to Register L (the low byte of the directory entry pointer). This advances HL to point to the filename field within the entry.
4E7E
LD L,A 6F
Store the computed offset back into Register L, so HL now points to the filename field of the directory entry (at entry_base + 05H).
4E7F
LD DE,515DH 11 5D 51
Point Register Pair DE to 515DH, the 11-byte filename/extension template buffer to compare against.
4E82
LD B,0BH 06 0B
Load Register B with 0BH (11 decimal), the number of bytes to compare (8-byte filename + 3-byte extension).
Comparison Loop Start
The following loop compares the 11-byte filename/extension field of the directory entry against the template buffer, one byte at a time.
4E84
LD A,(DE) 1A
Fetch the current byte from the filename template at (DE).
4E85
CP A,(HL) BE
Compare the template byte (Register A) against the corresponding byte in the directory entry's filename field at (HL). If they match, the Z FLAG is set.
4E86
If the NZ FLAG (Not Zero) has been set because the bytes do not match, JUMP to 4E64H to clean up the stack and continue scanning for another entry.
4E88
INC HL 23
INCrement HL to point to the next byte of the directory entry's filename field.
4E89
INC DE 13
INCrement DE to point to the next byte of the filename template buffer.
4E8A
DECrement Register B (byte counter) and LOOP BACK to 4E84H if B is not zero. When B reaches zero, all 11 bytes have matched successfully.
Comparison Loop End
4E8CH - Filename Match Found - Extract File Metadata
Reached when all 11 bytes of the filename/extension match. This section extracts the file's metadata from the directory entry (extent chain, sector count, hash) and stores the drive number, then falls through to populate the FCB.
4E8C
POP BC C1
Restore Register Pair BC from the stack (the inner push at 4E76H). Register C contains the current drive number.
4E8D
LD A,C 79
Load Register A with the drive number from Register C.
4E8E
LD (5154H),A 32 54 51
Store the drive number (Register A) to 5154H. This records which drive the file was found on for use during FCB population.
4E91
POP HL E1
Restore Register Pair HL from the stack (the inner push at 4E75H). HL points to the start of the matched directory entry in the 4D00H buffer.
4E92
POP AF F1
Restore Register Pair AF from the stack (the outer push at 4E6BH, where BC was pushed). This discards the saved BC value.
4E93
POP AF F1
Restore Register Pair AF from the stack (the outer push at 4E6AH, where HL was pushed). This discards the saved HL value. The four POP instructions at 4E8CH-4E93H clean up the four values pushed at 4E6AH/4E6BH and 4E75H/4E76H.
4E94
PUSH BC C5
Save Register Pair BC onto the stack. Register C still holds the drive number.
4E95
PUSH HL E5
Save Register Pair HL onto the stack. HL points to the matched directory entry.
Extent Chain Extraction
Now extract the file's extent data from the directory entry. The extent chain starts at offset +10H within the entry. Each extent consists of a starting granule number and a sector count within that granule.
4E96
LD A,(HL) 7E
Fetch the first byte of the directory entry (the attribute/hash byte).
4E97
AND A,07H E6 07
Mask bits 0-2 to extract the extent count field from the attribute byte. This gives the number of extent entries (0-7) in this directory entry.
4E99
LD C,A 4F
Copy the extent count (Register A) into Register C for later use.
4E9A
LD B,00H 06 00
Load Register B with 00H, clearing the high byte of BC. BC now holds the extent count as a 16-bit value.
4E9C
LD A,10H 3E 10
Load Register A with 10H, the offset within the directory entry where the extent chain data begins.
4E9E
ADD A,L 85
ADD the extent offset (10H) to Register L to compute the address of the first extent entry within the directory entry.
4E9F
LD L,A 6F
Store the computed address back into Register L, so HL now points to the extent chain area of the matched directory entry.
4EA0
LD DE,(516AH) ED 5B 6A 51
Load Register Pair DE with the 16-bit password hash stored at 516AH (copied from 5168H earlier at 4E35H). This is used to verify the password field of the directory entry against the hash computed from the caller's password.
Password Verification
The code now compares the password hash from the directory entry's extent chain against the hash computed from the caller-supplied password. If no password was specified by the caller, the hash at 516AH will be the default (all-spaces hash = 61A2H). The code checks for both the "no password" case and a matching password.
4EA4
PUSH HL E5
Save Register Pair HL onto the stack. HL points to the extent chain area.
4EA5
LD HL,61A2H 21 A2 61
Load Register Pair HL with 61A2H, the known hash value for an all-spaces (blank) password. This is the default hash when no password is specified.
4EA8
XOR A,A AF
Set Register A to ZERO and clear the Carry flag, preparing for the 16-bit subtraction.
4EA9
SBC HL,DE ED 52
SUBtract the caller's password hash (DE) from the blank password hash (HL = 61A2H). If the caller did not specify a password, DE will also be 61A2H, and the result will be zero (Z FLAG set).
4EAB
POP HL E1
Restore Register Pair HL from the stack. HL points back to the extent chain area of the directory entry.
4EAC
If the Z FLAG (Zero) has been set because the caller's password hash matches the blank password hash (no password specified), JUMP to 4ECFH to skip password verification and proceed with FCB population. When no password was supplied, any file matches regardless of its actual password.
Password Field Comparison
The caller specified a password. Now compare the password hash against the file's password hash stored in the directory entry. The password hash is stored in the extent chain entries at specific offsets depending on the extent count.
4EAE
LD A,C 79
Load Register A with the extent count from Register C.
4EAF
CP A,07H FE 07
Compare the extent count against 07H. If the extent count is exactly 7 (maximum), the Z FLAG is set, meaning there is no room for a password hash in the extent chain and the comparison should be skipped.
4EB1
If the Z FLAG (Zero) has been set because the extent count is 7 (no password hash present in the entry), JUMP to 4EC9H to return error 19H (access denied).
4EB3
LD A,(HL) 7E
Fetch the low byte of the first extent entry's starting granule address. This is the first byte of the extent data at the current HL position.
4EB4
INC HL 23
INCrement HL to point to the next byte of the extent entry.
4EB5
PUSH HL E5
Save Register Pair HL onto the stack (pointing to the high byte of the first extent entry).
4EB6
LD H,(HL) 66
Load Register H with the high byte of the extent entry (the password hash high byte in the first extent slot after the file's actual extents).
4EB7
LD L,A 6F
Load Register L with the low byte fetched at 4EB3H. HL now contains the 16-bit password hash from the first potential password location in the extent chain.
4EB8
XOR A,A AF
Set Register A to ZERO and clear the Carry flag for the subtraction.
4EB9
SBC HL,DE ED 52
SUBtract the caller's password hash (DE) from the directory entry's password hash (HL). If they match, the Z FLAG is set.
4EBB
POP HL E1
Restore Register Pair HL from the stack (pointing to the high byte of the first extent entry).
4EBC
If the Z FLAG (Zero) has been set because the password hashes match, JUMP to 4ECFH to proceed with FCB population. The file's password matches the caller's password.
4EBE
INC HL 23
INCrement HL to skip past the first password hash location and advance to the second possible password hash position in the extent chain.
4EBF
LD B,C 41
Load Register B with the extent count from Register C. This is used below but is also effectively saving C into B temporarily.
4EC0
LD A,(HL) 7E
Fetch the low byte of the second potential password hash location in the extent chain.
4EC1
INC HL 23
INCrement HL to point to the high byte of the second password hash location.
4EC2
LD H,(HL) 66
Load Register H with the high byte of the second password hash.
4EC3
LD L,A 6F
Load Register L with the low byte fetched at 4EC0H. HL now contains the 16-bit password hash from the second location.
4EC4
XOR A,A AF
Set Register A to ZERO and clear the Carry flag.
4EC5
SBC HL,DE ED 52
SUBtract the caller's password hash (DE) from the second password hash (HL). If they match, the Z FLAG is set.
4EC7
If the Z FLAG (Zero) has been set because the second password hash matches, JUMP to 4ECFH to proceed with FCB population.
4EC9
POP HL E1
Restore Register Pair HL from the stack (the push at 4E95H). Discards the saved directory entry pointer.
4ECA
POP BC C1
Restore Register Pair BC from the stack (the push at 4E94H). Discards the saved BC value.
4ECB
LD A,19H 3E 19
Load Register A with error code 19H (Access Denied / Password Mismatch). The file exists but the password does not match.
4ECD
OR A,A B7
Set the NZ FLAG by ORing A with itself (19H is non-zero), indicating an error condition.
4ECE
RET C9
Return to caller with error code 19H (Access Denied) in Register A and the NZ FLAG set.
4ECFH - Password Verified - Prepare for FCB Population
Reached when the file has been found and the password (if any) has been verified. Saves the extent count and jumps to the FCB population routine at 4FA7H.
4ECF
POP HL E1
Restore Register Pair HL from the stack (the push at 4E95H). HL points to the matched directory entry.
4ED0
LD A,B 78
Load Register A with Register B, which contains the extent count (loaded at 4EBFH from C, or still the original loop counter if the blank-password path was taken).
4ED1
LD (4FB2H),A 32 B2 4F
Store the extent count (Register A) to the local variable at 4FB2H for use during FCB population.
4ED4
POP BC C1
Restore Register Pair BC from the stack (the push at 4E94H). Register C contains the drive number.
4ED5
JUMP to 4FA7H to populate the FCB at IX with the file's metadata extracted from the matched directory entry.
4ED8H - Kill File Handler (Request Code 20H)
Handles the Kill File request (request code 20H). First calls the Open Existing File routine (4E15H) to locate the file in the directory. If the file is found, deallocates its granules from the GAT and clears the directory entry. If the file is not found (error 18H), changes the error to 10H and retries with write-protect detection to allocate a new directory entry instead.
4ED8
GOSUB to SYS0 routine at 4896H to verify the overlay call context and set up registers (IX = FCB pointer, HL = filespec pointer, B = parameter) for the file operation.
4EDB
GOSUB to 4E15H, the Open Existing File handler core (bypassing the dispatcher). This searches the directory for the specified file and, if found, populates the FCB with the file's metadata. Returns Z FLAG set on success, NZ with error code in A on failure.
4EDE
RET Z C8
If the Z FLAG (Zero) has been set because the file was found and opened successfully, return to caller. The FCB has been populated and the file is ready for use. For a Kill operation, the file has been opened (located) but not yet deleted - the caller (in SYS0 or another overlay) will handle the actual deletion using the FCB data.
4EDF
CP A,18H FE 18
Compare the error code in Register A against 18H (File Not Found). If the error is specifically "file not found," the Z FLAG is set.
4EE1
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the error is not "file not found" (it is a disk error or other problem), return to caller with that error code.
File Not Found on Kill - Retry With Write Protect Check
The file was not found. For a Kill operation, this means the code now enters a mode where it searches for a free directory entry while also checking for write-protect status. The purpose is to determine whether the disk is write-protected before attempting any directory modifications. The value 10H stored at 4F1DH controls the GAT search behavior.
4EE2
LD A,10H 3E 10
Load Register A with 10H. This value will be stored as a control byte that modifies the directory entry processing behavior.
4EE4
LD (4F1DH),A 32 1D 4F
Store 10H to 4F1DH. Self-Modifying Code
This modifies the immediate operand at 4F1DH, which is part of the LD (HL),00H instruction at 4F1CH within the Open New handler. By writing 10H here, the code changes the behavior of that instruction when the Open New handler is called below.
4EE7
LD A,(5154H) 3A 54 51
Fetch the drive number from 5154H. This was set during the initial filespec parse.
4EEA
LD C,A 4F
Copy the drive number (Register A) into Register C for the drive scan loop.
4EEB
INC A 3C
INCrement Register A by 1. If the drive number was FFH (search all drives), this wraps to 00H and sets the Z FLAG.
4EEC
JR NZ,4EEFH 20 01
If the NZ FLAG (Not Zero) has been set because a specific drive was specified, JUMP to 4EEFH to skip the reset.
4EEE
LD C,A 4F
Load Register C with 00H (starting drive) when searching all drives.
Kill Retry Drive Scan Loop Start
This drive scan loop is similar to the one in the Open Existing handler but adds write-protect detection and free slot scanning.
4EEF
GOSUB to 50FDH to select drive C and detect write-protect status by polling the FDC. Returns Z FLAG set if drive is ready (no carry = not write-protected, carry = write-protected).
4EF2
If the NZ FLAG (Not Zero) has been set because the drive is not ready, JUMP to 4EFFH to try the next drive.
4EF4
If the CARRY FLAG has been set because the disk is write-protected, JUMP to 4EFFH to try the next drive. A Kill operation requires write access.
4EF6
GOSUB to 512EH to read the directory sector for drive C into the 4D00H buffer.
4EF9
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the directory read failed, return to caller with the disk error code.
4EFA
GOSUB to 50ABH to scan the directory sector at 4D00H for a free (unused) directory entry slot. Returns Z FLAG set if a free slot was found (HL points to it), NZ if no free slot.
4EFD
If the Z FLAG (Zero) has been set because a free directory entry slot was found, JUMP to 4F0EH to process it (write the file's directory entry into this slot for the Kill operation's allocation phase).
4EFF
LD A,(5154H) 3A 54 51
Fetch the drive number from 5154H to check whether to try additional drives.
4F02
INC A 3C
INCrement Register A by 1. If drive was FFH (all drives), wraps to 00H and sets Z FLAG.
4F03
If the NZ FLAG (Not Zero) has been set because a specific drive was specified, JUMP to 4F0BH to return error 1AH (directory full on the specified drive).
4F05
INC C 0C
INCrement Register C (current drive counter) to advance to the next drive.
4F06
LD A,C 79
Load Register A with the new drive number from Register C.
4F07
CP A,04H FE 04
Compare Register A against 04H (maximum drive + 1). If less than 4, the CARRY FLAG is set.
4F09
If the CARRY FLAG has been set because more drives remain to search, LOOP BACK to 4EEFH to try the next drive.
4F0B
LD A,1AH 3E 1A
Load Register A with error code 1AH. In TRSDOS 2.3, this is the "Directory Full" error code - no free directory entries were found on any available drive.
4F0D
RET C9
Return to caller with error code 1AH (Directory Full) in Register A. The NZ FLAG is inherently set since 1AH is non-zero (from the INC A / CP comparison path that fell through).
4F0EH - Free Slot Found - Write Directory Entry for Kill/Open New
Reached when a free directory entry slot has been found. Writes the filename hash and file metadata into the slot, initializes the extent chain and GAT allocation, then writes the updated directory and GAT sectors to disk.
4F0E
LD B,L 45
Load Register B with the low byte of HL (the offset of the free directory entry within the 4D00H page). This value identifies the directory entry number for the sector write routine.
4F0F
LD A,(4E4EH) 3A 4E 4E
Fetch the filename hash byte from 4E4EH. This is the self-modifying location that was written at 4E29H with the computed hash of the filename.
4F12
LD (HL),A 77
Store the filename hash byte (Register A) into the first byte of the free directory entry. This marks the entry as active and stores the hash for fast filename lookup.
4F13
GOSUB to 5141H to write the directory sector (with the newly marked entry) from the 4D00H buffer back to disk.
4F16
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the directory write failed, return to caller with the disk error code.
4F17
GOSUB to SYS0 routine at 4AC1H to read the full directory sector for the entry identified by Register B into the 4D00H buffer. Returns HL pointing to the start of the directory entry within the buffer.
4F1A
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the directory read failed, return with the error code.
Initialize New Directory Entry
The directory entry has been located. Now clear the first 4 bytes of the entry (status/flags area) and populate it with the file's metadata: granule count, filename, extension, password hash, and initial extent chain.
4F1B
PUSH HL E5
Save Register Pair HL onto the stack. HL points to the start of the directory entry in the 4D00H buffer.
4F1C
LD (HL),00H 36 00
Store 00H to the first byte of the directory entry (the attribute/status byte). This clears the entry. Self-Modifying Code
The immediate operand at address 4F1DH was modified at 4EE4H to 10H when called from the Kill handler. When entered from the Kill path, this instruction stores 10H instead of 00H, setting bit 4 of the attribute byte to mark the entry differently.
4F1E
INC HL 23
INCrement HL to point to byte +01H of the directory entry.
4F1F
LD (HL),00H 36 00
Store 00H to byte +01H of the directory entry, clearing the second status byte.
4F21
INC HL 23
INCrement HL to point to byte +02H of the directory entry.
4F22
LD (HL),00H 36 00
Store 00H to byte +02H of the directory entry, clearing the third byte.
4F24
INC HL 23
INCrement HL to point to byte +03H of the directory entry.
4F25
LD (HL),00H 36 00
Store 00H to byte +03H of the directory entry, clearing the fourth byte.
4F27
INC HL 23
INCrement HL to point to byte +04H of the directory entry (the granule count field).
4F28
LD A,(4FAFH) 3A AF 4F
Fetch the granule count parameter from the local variable at 4FAFH (stored at 4E16H from Register B on entry).
4F2B
LD (HL),A 77
Store the granule count (Register A) to byte +04H of the directory entry.
4F2C
INC HL 23
INCrement HL to point to byte +05H of the directory entry (the start of the 11-byte filename/extension field).
Copy Filename Into Directory Entry
Copy the 11-byte filename/extension and 4-byte password (15 bytes total) from the template buffers at 515DH into the directory entry starting at offset +05H.
4F2D
PUSH BC C5
Save Register Pair BC onto the stack (preserving the drive number in C).
4F2E
LD BC,000FH 01 0F 00
Load Register Pair BC with 000FH (15 decimal), the number of bytes to copy (11 bytes filename/extension + 4 bytes additional data from the template area).
4F31
EX DE,HL EB
Exchange DE and HL. DE now points to the directory entry destination (offset +05H), and HL will be set to the source.
4F32
LD HL,515DH 21 5D 51
Point Register Pair HL to 515DH, the 11-byte filename/extension template buffer (source for the copy).
4F35
LDIR ED B0
Block copy 15 bytes from the template buffer at 515DH (HL, source) to the directory entry at offset +05H (DE, destination). HL and DE are incremented, BC is decremented to 0. After this, DE points to byte +14H of the directory entry.
4F37
EX DE,HL EB
Exchange DE and HL. HL now points to byte +14H of the directory entry (after the copied filename data).
Clear Remaining Entry Fields
Zero out 2 bytes after the filename, then fill 10 bytes of the extent chain area with FFH (marking all extent slots as unused).
4F38
LD (HL),00H 36 00
Store 00H to byte +14H of the directory entry (clearing a reserved field).
4F3A
INC HL 23
INCrement HL to byte +15H.
4F3B
LD (HL),00H 36 00
Store 00H to byte +15H of the directory entry.
4F3D
INC HL 23
INCrement HL to byte +16H (start of the extent chain area).
4F3E
LD B,0AH 06 0A
Load Register B with 0AH (10 decimal), the number of extent chain bytes to fill with FFH.
Fill Extent Chain Loop Start
4F40
LD (HL),0FFH 36 FF
Store FFH to the current extent chain byte. FFH marks the extent slot as unused/unallocated.
4F42
INC HL 23
INCrement HL to the next extent chain byte.
4F43
DECrement Register B and LOOP BACK to 4F40H if not zero. Fills all 10 extent chain bytes with FFH.
Fill Extent Chain Loop End
4F45
POP BC C1
Restore Register Pair BC from the stack (the push at 4F2DH). Register C contains the drive number.
4F46
GOSUB to SYS0 routine at 4AD6H to write both the directory sector and the GAT sector to disk. This commits the new directory entry and any granule allocation changes to the disk.
4F49
POP HL E1
Restore Register Pair HL from the stack (the push at 4F1BH). HL points to the start of the directory entry.
4F4A
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the directory/GAT write failed, return to caller with the disk error code.
4F4B
GOSUB to 4FA7H to populate the FCB at IX with the file's metadata from the newly created directory entry.
4F4E
SCF 37
Set the Carry flag. This signals to the caller that a new file was created (as opposed to an existing file being opened). The Z FLAG from the CALL at 4F4BH indicates success.
4F4F
RET C9
Return to caller. Z FLAG indicates success/failure from the FCB population, and CARRY FLAG is set to indicate a new file was created.
4F50H - Open New or Existing File Handler (Request Code 30H)
Handles the Open New or Existing File request (request code 30H). This routine is called from the SYS0 entry point at 4420H (SVC A4H). It reads the directory sector for the file's drive, locates the file's directory entry using the directory entry info byte from the FCB, and updates the entry with new metadata including a system attribute byte (90H) and extent chain data.
4F50
LD C,(IX+06H) DD 4E 06
Load Register C with the drive number from the FCB at IX+06H. The FCB was already populated by a prior Open call, so the drive number is valid.
4F53
GOSUB to 512EH to read the directory sector for drive C into the 4D00H buffer.
4F56
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the directory read failed, return to caller with the error code.
4F57
LD A,(IX+07H) DD 7E 07
Load Register A with the directory entry info byte from the FCB at IX+07H. This byte encodes which directory entry on the disk corresponds to this file.
4F5A
GOSUB to 50B6H to look up the directory entry using the info byte. This routine navigates the directory structure to find the entry and returns HL pointing to it. Returns Z FLAG set on success (entry found), NZ on failure.
4F5D
LD A,1EH 3E 1E
Load Register A with error code 1EH. This is a preloaded error code in case the directory entry lookup fails.
4F5F
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the directory entry lookup failed, return to caller with error code 1EH (invalid directory entry).
Directory Entry Located
HL points to the directory entry in the 4D00H buffer. Now update the entry with the file's new metadata.
4F60
LD B,L 45
Load Register B with the low byte of HL (the directory entry offset within the page), saving it for the directory write routine.
4F61
LD A,L 7D
Load Register A with the low byte of HL (same directory entry offset).
4F62
LD (4FA2H),A 32 A2 4F
Store the directory entry offset (Register A) to the local variable at 4FA2H. Self-Modifying Code
This stores the low byte of the directory entry pointer into the operand at 4FA2H, which is part of the LD (HL),00H instruction at 4FA1H in the FCB population routine. This allows the routine to reference the correct directory entry later.
4F65
LD D,H 54
Copy Register H (high byte of directory entry pointer, which is 4DH for the 4D00H buffer page) into Register D.
4F66
LD E,(IX+07H) DD 5E 07
Load Register E with the directory entry info byte from the FCB at IX+07H. DE now points to a location in the 4D00H page indexed by the directory entry info byte.
4F69
LD A,(DE) 1A
Fetch the byte at (DE) - the directory entry's attribute byte as indexed by the info byte.
4F6A
LD (HL),A 77
Store the attribute byte (Register A) into the current directory entry position at (HL). This copies the attribute from one position to another within the directory buffer.
4F6B
GOSUB to 5141H to write the updated directory sector from the 4D00H buffer to disk.
4F6E
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the directory write failed, return with the error code.
4F6F
GOSUB to SYS0 routine at 4AC1H to read the directory sector again for the entry identified by Register B. Returns HL pointing to the entry. This re-read ensures we have the latest data after the write.
4F72
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the read failed, return with the error code.
Initialize New File Entry Fields
Set the system attribute byte to 90H (marking the file as a system file with specific flags), copy the directory entry info byte from SYS0, clear 20 bytes of the entry, and fill the extent chain with FFH.
4F73
LD (HL),90H 36 90
Store 90H to the first byte of the directory entry. 90H sets bit 7 (system file flag) and bit 4, establishing the file's initial attribute state.
4F75
INC L 2C
INCrement Register L to advance to byte +01H of the directory entry.
4F76
PUSH BC C5
Save Register Pair BC onto the stack (preserving the entry number in B and drive number in C).
4F77
LD A,(4ABEH) 3A BE 4A
Fetch the directory entry info byte from the SYS0 variable at 4ABEH. This byte was set by the SYS0 Decode Directory Entry routine at 4B1EH and contains encoded information about the entry's position in the directory.
4F7A
LD (HL),A 77
Store the directory entry info byte (Register A) to byte +01H of the directory entry.
4F7B
INC L 2C
INCrement Register L to advance to byte +02H.
4F7C
LD B,14H 06 14
Load Register B with 14H (20 decimal), the number of bytes to clear (bytes +02H through +15H of the directory entry).
Clear Entry Fields Loop Start
4F7E
LD (HL),00H 36 00
Store 00H to the current byte of the directory entry, clearing it.
4F80
INC L 2C
INCrement Register L to the next byte.
4F81
DECrement Register B and LOOP BACK to 4F7EH if not zero. Clears 20 bytes of the directory entry.
Clear Entry Fields Loop End
4F83
PUSH HL E5
Save Register Pair HL onto the stack. HL now points to byte +16H of the directory entry (the start of the extent chain area).
4F84
LD B,0AH 06 0A
Load Register B with 0AH (10 decimal), the number of extent chain bytes to fill.
Fill Extent Chain Loop Start
4F86
LD (HL),0FFH 36 FF
Store FFH to the current extent chain byte, marking it as unused/unallocated.
4F88
INC L 2C
INCrement Register L to the next extent chain byte.
4F89
DECrement Register B and LOOP BACK to 4F86H if not zero. Fills all 10 extent chain bytes with FFH.
Fill Extent Chain Loop End
4F8B
POP DE D1
Restore the saved HL value into DE (the push at 4F83H). DE now points to byte +16H of the directory entry. This is an intentional POP DE instead of POP HL to use DE as a secondary pointer.
4F8C
INC DE 13
INCrement DE to point to byte +17H of the directory entry.
4F8D
POP BC C1
Restore Register Pair BC from the stack (the push at 4F76H). Register B has the entry number, Register C has the drive number.
4F8E
GOSUB to SYS0 routine at 4AD6H to write both the directory sector and the GAT sector to disk, committing the initialized directory entry.
4F91
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the write failed, return with the error code.
Update GAT With Directory Entry Info
Read the directory sector again for the same entry, then update the GAT (Granule Allocation Table) to record the directory entry's location. The GAT sector is at offset +1EH within the entry.
4F92
LD A,(4ABEH) 3A BE 4A
Fetch the directory entry info byte from SYS0 variable 4ABEH again.
4F95
LD B,A 47
Copy the directory entry info byte into Register B for the read routine.
4F96
GOSUB to SYS0 routine at 4AC1H to read the directory sector for entry B into the 4D00H buffer. Returns HL pointing to the entry.
4F99
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the read failed, return with the error code.
4F9A
LD A,L 7D
Load Register A with the low byte of HL (the directory entry offset within the 4D00H page).
4F9B
ADD A,1EH C6 1E
ADD 1EH (30 decimal) to Register A, computing the offset to the GAT linkage field at byte +1EH of the directory entry.
4F9D
LD L,A 6F
Store the computed offset back into Register L. HL now points to byte +1EH of the directory entry.
4F9E
LD (HL),0FEH 36 FE
Store FEH to byte +1EH of the directory entry. FEH marks this as the end-of-chain marker in the extent chain, indicating this is the last (and only) extent for a newly created file.
4FA0
INC L 2C
INCrement Register L to point to byte +1FH of the directory entry.
4FA1
LD (HL),00H 36 00
Store 00H to byte +1FH of the directory entry (the extent chain continuation byte). 00H indicates no continuation. Self-Modifying Code
The operand at 4FA2H was written at 4F62H with the directory entry's low byte offset, but this instruction uses it as a literal 00H store. The self-modification at 4F62H updates 4FA2H for a different purpose (storing the entry offset for later reference), not to change this instruction's behavior in this path.
4FA3
GOSUB to SYS0 routine at 4AD6H to write the updated directory and GAT sectors to disk.
4FA6
RET C9
Return to caller. The Z FLAG from the CALL at 4FA3H indicates success or failure.
4FA7H - Populate FCB From Directory Entry
Populates the FCB pointed to by IX with file metadata extracted from the matched directory entry. Sets the FCB status byte, access mode flags, drive number, directory entry info byte, EOF marker, sector count, and extent chain data. On entry, DE points to the directory entry in the 4D00H buffer, and BC contains the drive number in C.
4FA7
EX DE,HL EB
Exchange DE and HL. HL now points to the directory entry in the 4D00H buffer (the parameter passed to this routine), and DE is available for use.
4FA8
PUSH IX DD E5
Save the FCB pointer (IX) onto the stack.
4FAA
POP HL E1
Restore the FCB pointer into HL. This PUSH IX / POP HL sequence copies IX into HL.
4FAB
LD (HL),80H 36 80
Store 80H to byte IX+00H of the FCB (the status/error flags byte). Bit 7 set (80H) marks the file as open.
4FAD
INC HL 23
INCrement HL to point to byte IX+01H (the access mode and state flags byte).
4FAE
LD A,00H 3E 00
Load Register A with 00H. Self-Modifying Code
The operand at 4FAFH was written at 4E16H with the granule count parameter from the caller's Register B. At runtime, this loads the actual granule count value, not 00H.
4FB0
OR A,A B7
Test the granule count (Register A) against itself to set flags. If the count is zero, the Z FLAG is set.
4FB1
LD A,00H 3E 00
Load Register A with 00H. Self-Modifying Code
The operand at 4FB2H was written at 4ED1H with the extent count extracted from the matched directory entry. At runtime, this loads the actual extent count value.
4FB3
If the Z FLAG (Zero) has been set because the granule count is zero (from the OR A,A at 4FB0H), JUMP to 4FB7H to skip setting bit 7 in the access flags.
4FB5
OR A,80H F6 80
Set bit 7 of Register A. This sets the "fixed record length mode" flag in the FCB access mode byte, indicating the file uses fixed-length records.
4FB7
OR A,20H F6 20
Set bit 5 of Register A. This sets the "buffer needs refresh" flag in the FCB access mode byte, indicating the sector buffer needs to be loaded from disk before use.
4FB9
LD (HL),A 77
Store the access mode flags (Register A) to byte IX+01H of the FCB.
4FBA
INC HL 23
INCrement HL to byte IX+02H.
4FBB
LD (HL),00H 36 00
Store 00H to byte IX+02H (clearing a reserved or unused FCB field).
4FBD
INC HL 23
INCrement HL to byte IX+03H (the sector buffer base address low byte).
4FBE
PUSH DE D5
Save Register Pair DE onto the stack (DE is available for temporary use).
4FBF
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H. Self-Modifying Code
The operand at 4FC0H-4FC1H was written at 4E19H with the saved HL register from the caller. At runtime, DE contains the caller's original HL value (the filespec pointer or caller context address).
4FC2
LD (HL),E 73
Store Register E (low byte of the caller's saved HL) to byte IX+03H of the FCB (sector buffer base address low byte).
4FC3
INC HL 23
INCrement HL to byte IX+04H (sector buffer base address high byte).
4FC4
LD (HL),D 72
Store Register D (high byte of the caller's saved HL) to byte IX+04H of the FCB.
4FC5
INC HL 23
INCrement HL to byte IX+05H (byte offset within current sector).
4FC6
POP DE D1
Restore Register Pair DE from the stack (the push at 4FBEH). DE is available again.
4FC7
LD (HL),00H 36 00
Store 00H to byte IX+05H (byte offset = 0, positioned at start of sector).
4FC9
INC HL 23
INCrement HL to byte IX+06H (drive number).
4FCA
LD (HL),C 71
Store Register C (the drive number) to byte IX+06H of the FCB.
4FCB
INC HL 23
INCrement HL to byte IX+07H (directory entry info byte).
4FCC
LD (HL),B 70
Store Register B (the directory entry number/offset) to byte IX+07H of the FCB.
4FCD
INC HL 23
INCrement HL to byte IX+08H (EOF byte offset).
Copy Extent Chain Into FCB
Now copy the extent chain data from the directory entry into the FCB. The extent chain starts at offset +03H within the directory entry (pointed to by DE). The data includes the EOF offset and 4 extent entries (starting granule + sector count pairs), plus the total sector count.
4FCE
LD A,03H 3E 03
Load Register A with 03H, the offset within the directory entry where the extent-related data begins.
4FD0
ADD A,E 83
ADD the offset (03H) to Register E (the low byte of the directory entry pointer). This computes the address of the extent data within the entry.
4FD1
LD E,A 5F
Store the computed address back into Register E. DE now points to the extent data area of the directory entry.
4FD2
LD A,(DE) 1A
Fetch the EOF byte offset from the directory entry's extent data at (DE).
4FD3
LD (HL),A 77
Store the EOF byte offset (Register A) to byte IX+08H of the FCB.
4FD4
INC HL 23
INCrement HL to byte IX+09H (record length).
4FD5
INC DE 13
INCrement DE to the next byte of the directory entry's extent data.
4FD6
LD A,(4FAFH) 3A AF 4F
Fetch the granule count parameter from 4FAFH. This is the self-modifying variable that holds the caller's original B register value.
4FD9
LD (HL),A 77
Store the granule count (Register A) to byte IX+09H of the FCB (record length field).
4FDA
INC HL 23
INCrement HL to byte IX+0AH (current sector position low byte).
4FDB
LD (HL),00H 36 00
Store 00H to byte IX+0AH (current sector position low byte = 0, positioned at the beginning of the file).
4FDD
INC HL 23
INCrement HL to byte IX+0BH (current sector position high byte).
4FDE
LD (HL),00H 36 00
Store 00H to byte IX+0BH (current sector position high byte = 0).
4FE0
INC HL 23
INCrement HL to byte IX+0CH (total sector count low byte).
Copy Extent Entries From Directory
Now copy the individual extent entries from the directory. Each extent has a 2-byte starting granule address and a 2-byte sector count. The code reads up to 4 extent entries from the directory entry starting at offset +10H.
4FE1
LD A,10H 3E 10
Load Register A with 10H, the offset within the directory entry where the extent chain begins.
4FE3
ADD A,E 83
ADD the extent chain offset (10H) to Register E, computing the address of the first extent entry.
4FE4
LD E,A 5F
Store the computed address into Register E. DE now points to the first extent entry in the directory entry.
4FE5
LD A,(DE) 1A
Fetch the first byte of the first extent entry (low byte of starting granule address).
4FE6
LD (HL),A 77
Store the low byte of the first extent's starting granule to the FCB at the current position (IX+0CH, total sector count low).
4FE7
INC HL 23
INCrement HL to the next FCB byte.
4FE8
INC DE 13
INCrement DE to the next byte of the extent entry.
4FE9
LD A,(DE) 1A
Fetch the second byte of the first extent entry (high byte of starting granule address).
4FEA
LD (HL),A 77
Store the high byte to the FCB.
4FEB
INC HL 23
INCrement HL to the next FCB byte.
4FEC
INC DE 13
INCrement DE to the next extent data byte.
4FED
LD A,(DE) 1A
Fetch the third byte of the first extent entry.
4FEE
LD (HL),A 77
Store the third byte to the FCB.
4FEF
INC HL 23
INCrement HL to the next FCB byte.
4FF0
INC DE 13
INCrement DE to the next extent data byte.
4FF1
LD A,(DE) 1A
Fetch the fourth byte of the first extent entry (the sector count within this extent, with the sector count encoded in bits 0-4).
4FF2
LD (HL),A 77
Store the fourth byte to the FCB.
4FF3
INC HL 23
INCrement HL to the next FCB byte.
4FF4
AND A,1FH E6 1F
Mask bits 0-4 of the last extent byte to extract the sector count within this extent (0-31 sectors per granule).
4FF6
INC A 3C
INCrement the sector count by 1. The stored value is 0-based (0 = 1 sector), so adding 1 gives the actual count.
4FF7
LD C,A 4F
Copy the actual sector count into Register C. This will be accumulated as the total sector count across all extents.
4FF8
LD B,00H 06 00
Load Register B with 00H, clearing the high byte of the 16-bit total sector count in BC.
Extent Chain Copy Loop
Process up to 4 additional extent entries from the directory, copying each into the FCB and accumulating the total sector count in BC.
4FFA
LD A,04H 3E 04
Load Register A with 04H, the maximum number of additional extent entries to process (loop counter).
4FFC
PUSH AF F5
Save Register A (loop counter) onto the stack.
4FFD
INC DE 13
INCrement DE to point to the next extent entry in the directory.
4FFE
LD A,(DE) 1A
Fetch the first byte of the next extent entry.
4FFF
CP A,0FEH FE FE
Compare Register A against FEH. Values of FEH or FFH indicate the end of the extent chain (FEH = end-of-chain marker, FFH = unused slot). If A >= FEH, the NO CARRY FLAG is set.
5001
If the NO CARRY FLAG has been set because the extent byte is FEH or FFH (end of chain), JUMP to 501CH to pad the remaining FCB extent slots with FFH and return.
5003
LD (HL),C 71
Store Register C (accumulated sector count low byte) to the current FCB position.
5004
INC HL 23
INCrement HL to the next FCB byte.
5005
LD (HL),B 70
Store Register B (accumulated sector count high byte) to the FCB.
5006
INC HL 23
INCrement HL to the next FCB byte.
5007
LD A,(DE) 1A
Fetch the first byte of the current extent entry again (the starting granule low byte).
5008
LD (HL),A 77
Store the extent starting granule low byte to the FCB.
5009
INC HL 23
INCrement HL.
500A
INC DE 13
INCrement DE to the next byte of the directory extent entry.
500B
LD A,(DE) 1A
Fetch the second byte of the extent entry (high byte with sector count in bits 0-4).
500C
LD (HL),A 77
Store the second extent byte to the FCB.
500D
INC HL 23
INCrement HL.
500E
AND A,1FH E6 1F
Mask bits 0-4 to extract the sector count within this extent.
5010
INC A 3C
INCrement the sector count by 1 (converting from 0-based to actual count).
5011
ADD A,C 81
ADD this extent's sector count to the accumulated total in Register C.
5012
LD C,A 4F
Store the updated total sector count low byte back into Register C.
5013
LD A,B 78
Load Register A with the high byte of the total sector count (Register B).
5014
ADC A,00H CE 00
ADD any carry from the low byte addition to the high byte, propagating the carry through the 16-bit accumulator.
5016
LD B,A 47
Store the updated high byte back into Register B. BC now contains the running total of sectors across all extents processed so far.
5017
POP AF F1
Restore Register A (loop counter) from the stack.
5018
DEC A 3D
DECrement the loop counter. If it reaches zero, all 4 additional extent slots have been processed.
5019
If the NZ FLAG (Not Zero) has been set because more extents remain to process, LOOP BACK to 4FFCH.
501B
RET C9
Return to caller. All extent entries have been copied to the FCB.
501CH - Pad Remaining FCB Extent Slots With FFH
Called when the end of the extent chain is reached (FEH or FFH marker found). Pads the remaining unused extent slots in the FCB with FFH bytes so the FCB has a consistent size regardless of the actual extent count.
501C
POP AF F1
Restore Register A (remaining loop counter) from the stack. This was the number of extent slots left to process when the chain ended.
501D
RLCA 07
Rotate Register A left by 1 bit (multiply by 2). Each extent slot occupies 2 bytes in the FCB, and each also has 2 bytes of accumulated count, so 4 bytes per slot.
501E
RLCA 07
Rotate Register A left by 1 bit again (multiply by 4 total). A now contains the number of FFH bytes to write to pad the remaining extent slots.
501F
LD B,A 47
Copy the byte count into Register B for the fill loop.
5020
LD (HL),0FFH 36 FF
Store FFH to the current FCB byte, marking it as unused.
5022
INC HL 23
INCrement HL to the next FCB byte.
5023
DECrement Register B and LOOP BACK to 5020H if not zero. Fills all remaining extent slots with FFH.
5025
XOR A,A AF
Set Register A to ZERO and clear all flags. The Z FLAG is set, indicating success.
5026
RET C9
Return to caller with the Z FLAG set (success). The FCB has been fully populated with file metadata and the extent chain.
5027H - Parse Filespec Into Template Buffers
Parses a filespec string pointed to by HL into the internal template buffers. The filespec format is NAME/EXT.PASSWORD:DRIVE. The 8-byte filename is stored at 515DH, the 3-byte extension at 5165H, the 8-byte password at 5155H, and the drive number at 5154H. Unspecified fields are filled with spaces (20H). Returns Z FLAG set on success, NZ on parse error.
5027
LD B,0BH 06 0B
Load Register B with 0BH (11 decimal), the number of bytes to fill in the filename/extension template buffer at 515DH.
5029
LD DE,515DH 11 5D 51
Point Register Pair DE to 515DH, the 11-byte filename/extension template buffer (destination for the filename and extension).
502C
LD A,20H 3E 20
Load Register A with 20H (ASCII space), the fill character for unused positions in the template.
502E
LD (DE),A 12
Store a space (20H) to the current template buffer position at (DE).
502F
INC DE 13
INCrement DE to the next template buffer position.
5030
DECrement Register B and LOOP BACK to 502EH if not zero. Fills all 11 bytes of the filename/extension template with spaces.
5032
LD B,08H 06 08
Load Register B with 08H (8 decimal), the number of bytes in the password buffer at 5155H.
5034
LD DE,5155H 11 55 51
Point Register Pair DE to 5155H, the 8-byte password buffer (destination for the password).
5037
LD (DE),A 12
Store a space (20H) to the current password buffer position at (DE). Register A still contains 20H from 502CH.
5038
INC DE 13
INCrement DE to the next password buffer position.
5039
DECrement Register B and LOOP BACK to 5037H if not zero. Fills all 8 bytes of the password buffer with spaces.
503B
LD A,0FFH 3E FF
Load Register A with FFH, the "no drive specified" sentinel value.
503D
LD (5154H),A 32 54 51
Store FFH to 5154H, initializing the drive number to "unspecified" (search all drives).
Parse Filename (Up to 8 Characters)
Extract up to 8 alphanumeric characters from the filespec into the filename portion of the template buffer at 515DH.
5040
LD DE,515DH 11 5D 51
Point Register Pair DE to 515DH, the start of the filename portion of the template buffer.
5043
LD B,08H 06 08
Load Register B with 08H (8 decimal), the maximum number of filename characters to extract.
5045
GOSUB to 5081H to extract alphanumeric characters from the filespec at (HL) into the buffer at (DE). Reads up to B characters, stopping on a non-alphanumeric delimiter. Returns the delimiter character in A and the remaining count in B.
5048
LD C,A 4F
Save the delimiter character (Register A) into Register C for later analysis.
5049
LD A,B 78
Load Register A with Register B (the remaining character count from the extraction).
504A
CP A,08H FE 08
Compare Register A against 08H (the initial count). If A equals 08H, no characters were extracted (the filespec started with a non-alphanumeric character).
504C
If the NZ FLAG (Not Zero) has been set because at least one character was extracted, JUMP to 5052H to continue parsing.
504E
LD A,13H 3E 13
Load Register A with error code 13H (invalid filespec - no filename characters found).
5050
OR A,A B7
Set the NZ FLAG by ORing A with itself (13H is non-zero).
5051
RET C9
Return to caller with error code 13H (invalid filespec) and the NZ FLAG set.
Check For Extension Separator
The filename has been extracted. Now check if the delimiter was a '/' (2FH) indicating an extension follows.
5052
LD A,C 79
Load Register A with the delimiter character saved in Register C.
5053
CP A,2FH FE 2F
Compare Register A against 2FH (ASCII '/'). If the delimiter is '/', the Z FLAG is set, indicating an extension follows.
5055
If the NZ FLAG (Not Zero) has been set because the delimiter is not '/', JUMP to 505FH to check for a password separator instead.
5057
LD DE,5165H 11 65 51
Point Register Pair DE to 5165H, the 3-byte extension portion of the template buffer (at 515DH + 8).
505A
LD B,03H 06 03
Load Register B with 03H (3 decimal), the maximum number of extension characters.
505C
GOSUB to 5081H to extract up to 3 alphanumeric characters from the filespec into the extension buffer at 5165H.
Check For Password Separator
Check if the current delimiter is a '.' (2EH) indicating a password follows.
505F
CP A,2EH FE 2E
Compare Register A against 2EH (ASCII '.'). If the delimiter is '.', the Z FLAG is set, indicating a password follows.
5061
If the NZ FLAG (Not Zero) has been set because the delimiter is not '.', JUMP to 506BH to check for a drive separator instead.
5063
LD DE,5155H 11 55 51
Point Register Pair DE to 5155H, the 8-byte password buffer.
5066
LD B,08H 06 08
Load Register B with 08H (8 decimal), the maximum number of password characters.
5068
GOSUB to 5081H to extract up to 8 alphanumeric characters from the filespec into the password buffer at 5155H.
Check For Drive Separator
Check if the current delimiter is a ':' (3AH) indicating a drive specifier follows.
506B
CP A,3AH FE 3A
Compare Register A against 3AH (ASCII ':'). If the delimiter is ':', the Z FLAG is set, indicating a drive number follows.
506D
If the NZ FLAG (Not Zero) has been set because the delimiter is not ':', JUMP to 507FH to return success (no drive specifier, use default).
506F
LD A,(HL) 7E
Fetch the next character from the filespec at (HL). This should be the ASCII drive number digit ('0'-'3').
5070
SUB A,30H D6 30
SUBtract 30H (ASCII '0') from Register A, converting the ASCII digit to binary (0-9). If the character is below '0', the CARRY FLAG is set.
5072
If the CARRY FLAG has been set because the character is below ASCII '0' (not a valid digit), JUMP to 5078H to return an error.
5074
CP A,04H FE 04
Compare the binary drive number against 04H. TRSDOS supports drives 0-3. If A < 4, the CARRY FLAG is set (valid drive).
5076
If the CARRY FLAG has been set because the drive number is valid (0-3), JUMP to 507CH to store it.
5078
LD A,20H 3E 20
Load Register A with error code 20H (invalid drive specifier).
507A
OR A,A B7
Set the NZ FLAG by ORing A with itself (20H is non-zero).
507B
RET C9
Return to caller with error code 20H (invalid drive) and the NZ FLAG set.
507C
LD (5154H),A 32 54 51
Store the validated drive number (Register A, 0-3) to the drive variable at 5154H.
507F
XOR A,A AF
Set Register A to ZERO and clear all flags. The Z FLAG is set, indicating successful filespec parsing.
5080
RET C9
Return to caller with the Z FLAG set (success). The filespec has been parsed into 515DH (filename/extension), 5155H (password), and 5154H (drive number).
5081H - Character Extraction Subroutine
Extracts alphanumeric characters (A-Z, 0-9) from the string pointed to by HL into the buffer pointed to by DE. Reads up to B characters, stopping when a non-alphanumeric character is encountered. Returns the delimiter character in Register A and the remaining count in Register B.
5081
LD A,(HL) 7E
Fetch the first character from the filespec string at (HL).
5082
INC HL 23
INCrement HL to point to the next character in the filespec string.
5083
JUMP to 508EH to validate the first character (skipping the digit check on the first iteration, which allows the first character to be a letter without checking for digits first).
Character Extraction Loop Start
After the first character, subsequent characters are checked against both digit range (30H-39H) and letter range (41H-5AH).
5085
LD A,(HL) 7E
Fetch the next character from the filespec string at (HL).
5086
INC HL 23
INCrement HL to point to the next character.
5087
CP A,30H FE 30
Compare Register A against 30H (ASCII '0'). If A < 30H, the CARRY FLAG is set, meaning the character is below the digit range and is a delimiter.
5089
RET C D8
If the CARRY FLAG has been set because the character is below '0', return to caller. Register A contains the delimiter character, and B contains the remaining character count.
508A
CP A,3AH FE 3A
Compare Register A against 3AH (one past ASCII '9'). If A < 3AH, the CARRY FLAG is set, meaning the character is in the digit range ('0'-'9').
508C
If the CARRY FLAG has been set because the character is a digit ('0'-'9'), JUMP to 5094H to store it in the buffer.
508E
CP A,41H FE 41
Compare Register A against 41H (ASCII 'A'). If A < 41H, the CARRY FLAG is set, meaning the character is between ':' and 'A' (a non-alphanumeric delimiter like '/' or '.').
5090
RET C D8
If the CARRY FLAG has been set because the character is below 'A', return to caller with the delimiter character in A.
5091
CP A,5BH FE 5B
Compare Register A against 5BH (one past ASCII 'Z'). If A < 5BH, the CARRY FLAG is set, meaning the character is an uppercase letter ('A'-'Z').
5093
RET NC D0
If the NO CARRY FLAG has been set because the character is above 'Z', return to caller with the non-letter character in A as a delimiter.
5094
LD (DE),A 12
Store the valid alphanumeric character (Register A) to the current position in the output buffer at (DE).
5095
INC DE 13
INCrement DE to the next position in the output buffer.
5096
DECrement Register B (remaining character count) and LOOP BACK to 5085H if not zero. Continues extracting characters until the buffer is full or a delimiter is found.
Character Extraction Loop End
5098
LD A,(HL) 7E
Fetch the next character from the filespec (the character that would follow the full buffer). This becomes the delimiter for the caller.
5099
INC HL 23
INCrement HL past the delimiter character.
509A
RET C9
Return to caller. Register A contains the delimiter (or next character after the buffer was filled), B is zero (buffer full).
509BH - Compute 8-Bit Filename CRC Hash
Computes an 8-bit CRC-style hash of the 11-byte filename/extension template at (HL). The hash is computed by XORing each byte with the running accumulator and rotating left. The result is returned in Register A. If the hash is zero, it is changed to 01H to avoid confusion with an empty directory entry.
509B
LD B,0BH 06 0B
Load Register B with 0BH (11 decimal), the number of bytes to hash (8-byte filename + 3-byte extension).
509D
LD C,00H 0E 00
Load Register C with 00H, initializing the CRC accumulator to zero.
Hash Loop Start
509F
LD A,(HL) 7E
Fetch the current byte from the filename/extension template at (HL).
50A0
INC HL 23
INCrement HL to point to the next template byte.
50A1
XOR A,C A9
XOR Register A with the CRC accumulator in Register C. This mixes the current byte into the running hash.
50A2
RLCA 07
Rotate Register A left by 1 bit. This shifts the hash value left, wrapping bit 7 into bit 0, spreading the influence of each byte across all bit positions.
50A3
LD C,A 4F
Store the updated hash value back into the CRC accumulator (Register C).
50A4
DECrement Register B and LOOP BACK to 509FH if not zero. Processes all 11 bytes of the filename/extension template.
Hash Loop End
50A6
LD A,C 79
Load Register A with the final hash value from the CRC accumulator (Register C).
50A7
OR A,A B7
Test the hash value against itself. If the hash is zero, the Z FLAG is set.
50A8
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the hash is non-zero, return with the hash in Register A. A non-zero hash is valid.
50A9
INC A 3C
INCrement Register A from 00H to 01H. A zero hash would be indistinguishable from an empty directory entry (which has 00H in its first byte), so it is changed to 01H.
50AA
RET C9
Return to caller with the hash value 01H in Register A.
50ABH - Scan Directory Sector for Free Entry Slot
Scans the directory sector currently in the 4D00H buffer for an unused (free) directory entry slot. Uses the system tick counter at 4040H to compute a starting offset within the directory page, then searches entries in a specific pattern. Returns Z FLAG set if a free slot is found (HL points to it), NZ if no free slot exists.
50AB
LD A,(4040H) 3A 40 40
Fetch the master tick counter from 4040H (SYS0 system variable, incremented by the timer interrupt service routine). This provides a pseudo-random starting point for the directory scan to distribute entries across the sector.
50AE
AND A,0E7H E6 E7
Mask the tick counter with E7H (11100111 binary). This clears bits 3 and 4, which are used as the directory entry offset within a 32-byte entry (bits 3-4 select 0, 8, 16, or 24 within the 32-byte boundary). The result aligns the starting offset to a directory entry boundary.
50B0
CP A,40H FE 40
Compare the masked tick value against 40H. If A < 40H, the value is in the lower half of the page. The CARRY FLAG is set if A < 40H.
50B2
If the NO CARRY FLAG has been set because A >= 40H, JUMP to 50B6H to use the value as-is (it is in the upper half of the page, corresponding to the 4D40H-4DFFH range of the directory sector).
50B4
OR A,80H F6 80
Set bit 7 of Register A. For values less than 40H, this shifts them into the 80H-E7H range (the 4D80H-4DE7H portion of the page), ensuring the scan starts in the upper portion of the directory sector.
50B6
LD L,A 6F
Load Register L with the computed starting offset. HL now points to a directory entry in the 4D00H page (with H = 4DH from the caller's context).
50B7
GOSUB to 50BDH to check if the directory entry at (HL) is free. Returns Z FLAG set if the entry is free (first byte is 00H).
50BA
RET Z C8
If the Z FLAG (Zero) has been set because a free entry was found, return to caller. HL points to the free directory entry slot.
50BB
LD L,40H 2E 40
Load Register L with 40H, resetting the scan to the start of the second quarter of the directory page (entry at 4D40H). This serves as a fallback starting point if the tick-based entry was not free.
Linear Scan Loop
Now scan through the directory page starting from offset 40H, checking each entry by advancing in 20H (32-byte) steps through the page. The loop wraps around the 256-byte page boundary.
50BD
LD A,(HL) 7E
Fetch the first byte of the directory entry at (HL).
50BE
OR A,A B7
Test the byte against itself. If it is 00H, the Z FLAG is set, indicating the entry is free.
50BF
RET Z C8
If the Z FLAG (Zero) has been set because the entry is free, return to caller. HL points to the free slot.
50C0
LD A,L 7D
Load Register A with the current entry offset (Register L).
50C1
ADD A,20H C6 20
ADD 20H (32 decimal) to the offset, advancing to the next directory entry (each entry is 32 bytes).
50C3
LD L,A 6F
Store the new offset back into Register L.
50C4
If the NO CARRY FLAG has been set because the addition did not wrap past FFH, LOOP BACK to 50BDH to check the next entry. The loop continues through entries in the upper half of the page.
50C6
ADD A,41H C6 41
ADD 41H to Register A. After the carry from the previous ADD wrapped A around past 00H, this shifts the offset into the 01H-3FH range, scanning the lower portion of the directory page. The +1 ensures we do not re-scan offset 00H immediately.
50C8
LD L,A 6F
Store the adjusted offset into Register L.
50C9
AND A,0E7H E6 E7
Mask with E7H to align to a directory entry boundary (clearing bits 3 and 4).
50CB
CP A,L BD
Compare the masked value against the original L. If they are equal, L was already aligned. If not equal, L has non-entry-boundary bits set, meaning we have scanned all entries.
50CC
If the Z FLAG (Zero) has been set because L is aligned to an entry boundary, LOOP BACK to 50BDH to check this entry.
50CE
OR A,01H F6 01
Set bit 0 of Register A, ensuring A is non-zero. This sets the NZ FLAG to indicate no free slot was found.
50D0
RET C9
Return to caller with the NZ FLAG set (no free directory entry found).
50D1H - Compute 16-Bit Password Hash (CRC)
Computes a 16-bit CRC-style hash of the 8-byte password buffer pointed to by DE. Uses a polynomial feedback algorithm with XOR, shift, and rotate operations to produce a 16-bit hash in HL. This hash is used for fast password comparison during directory entry matching.
50D1
LD HL,0FFFFH 21 FF FF
Load Register Pair HL with FFFFH, initializing the 16-bit CRC accumulator to all ones.
50D4
LD B,08H 06 08
Load Register B with 08H (8 decimal), the number of bytes to process (8-byte password).
50D6
LD A,E 7B
Load Register A with Register E (the low byte of the source pointer).
50D7
ADD A,07H C6 07
ADD 07H to Register A, computing the address of the last byte of the 8-byte password buffer (starting from the end and processing backwards).
50D9
LD E,A 5F
Store the adjusted address back into Register E. DE now points to the last byte of the password buffer.
50DA
If the NO CARRY FLAG has been set because the addition did not overflow, JUMP to 50DDH to begin processing.
50DC
INC D 14
INCrement Register D to handle carry from the ADD at 50D7H (in case E + 07H overflowed past FFH).
CRC Computation Loop Start
Process each password byte from last to first, feeding it through a polynomial CRC algorithm. The algorithm uses XOR, shift (ADD HL,HL), and rotate operations to mix each byte into the 16-bit accumulator.
50DD
LD A,(DE) 1A
Fetch the current password byte from (DE) (processing from last byte to first).
50DE
PUSH DE D5
Save Register Pair DE (password buffer pointer) onto the stack.
50DF
LD D,A 57
Load Register D with the current password byte. D is used as a temporary for CRC computation.
50E0
LD E,H 5C
Load Register E with the high byte of the CRC accumulator (H). E saves the old high byte for feedback.
50E1
LD A,L 7D
Load Register A with the low byte of the CRC accumulator (L).
50E2
AND A,07H E6 07
Mask bits 0-2 of the CRC low byte, isolating a 3-bit feedback value.
50E4
RRCA 0F
Rotate Register A right by 1 bit.
50E5
RRCA 0F
Rotate Register A right by 1 bit again.
50E6
RRCA 0F
Rotate Register A right by 1 bit a third time. Three rotations right move the 3-bit feedback value from bits 0-2 to bits 5-7.
50E7
XOR A,L AD
XOR Register A with the CRC low byte (L), mixing the rotated feedback back into the accumulator.
50E8
LD L,A 6F
Store the mixed value back into Register L.
50E9
LD H,00H 26 00
Clear Register H. The 16-bit shift operations below will rebuild H from the shifted L value.
50EB
ADD HL,HL 29
Shift HL left by 1 bit (multiply by 2).
50EC
ADD HL,HL 29
Shift HL left by 1 bit (multiply by 4 total).
50ED
ADD HL,HL 29
Shift HL left by 1 bit (multiply by 8 total).
50EE
ADD HL,HL 29
Shift HL left by 1 bit (multiply by 16 total). After four shifts, the original 8-bit value has been spread across both H and L.
50EF
XOR A,H AC
XOR Register A with the shifted high byte (H), mixing more feedback into the accumulator.
50F0
XOR A,D AA
XOR Register A with the current password byte (saved in D at 50DFH), incorporating the input data into the CRC.
50F1
LD D,A 57
Store the intermediate CRC result into Register D (will become the new high byte).
50F2
LD A,L 7D
Load Register A with the current CRC low byte (L) after the shifts.
50F3
ADD HL,HL 29
Shift HL left by 1 more bit (multiply by 32 total from the original value).
50F4
XOR A,H AC
XOR Register A with the new shifted high byte, adding another layer of feedback.
50F5
XOR A,E AB
XOR Register A with the saved old CRC high byte (E, saved at 50E0H), completing the polynomial feedback.
50F6
LD E,A 5F
Store the computed CRC low byte into Register E.
50F7
EX DE,HL EB
Exchange DE and HL. HL now contains the new 16-bit CRC value (D=high, E=low from the computation).
50F8
POP DE D1
Restore Register Pair DE (password buffer pointer) from the stack.
50F9
DEC DE 1B
DECrement DE to point to the previous password byte (processing from last to first).
50FA
DECrement Register B and LOOP BACK to 50DDH if not zero. Processes all 8 password bytes.
CRC Computation Loop End
50FC
RET C9
Return to caller with the 16-bit password hash in HL.
50FDH - Select Drive and Detect Write Protect
Selects drive C by first issuing a Force Interrupt command to the FDC, then calling the SYS0 drive select routine. After selection, polls the FDC status register for the index pulse to detect whether the disk is present and write-protected. Returns Z FLAG set if the drive is ready (CARRY FLAG indicates write-protect status), NZ if the drive is not ready or timed out.
50FD
LD A,0D0H 3E D0
Load Register A with D0H, the Force Interrupt command for the WD1771 FDC. This terminates any in-progress FDC operation without generating an interrupt.
| 1771 FDC Command: D0H (11010000) | | Function Description |
| 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | | Command=Force Interrupt Bit 7-4: Command Code (1101) I3: 0=No Immediate Interrupt I2: 0=No Index Pulse Interrupt I1: 0=No Ready-to-Not-Ready Interrupt I0: 0=No Not-Ready-to-Ready Interrupt |
50FF
LD (37ECH),A 32 EC 37
Store the Force Interrupt command (D0H) to the FDC Command/Status register at 37ECH (Model I memory-mapped FDC). This aborts any pending FDC operation.
5102
GOSUB to SYS0 routine at 4600H to select the drive identified by Register C. This routine saves the current drive's track position, loads the new drive's cached track position, and sends the drive select command to the drive select latch at 37E0H.
5105
PUSH BC C5
Save Register Pair BC onto the stack (preserving the drive number in C).
5106
LD BC,3155H 01 55 31
Load Register Pair BC with 3155H (12629 decimal), a timeout counter for FDC polling. This provides approximately 200ms of waiting time at the Model I's clock speed.
Wait for Index Pulse - Phase 1
Poll the FDC status register waiting for the index pulse (bit 1) to go high. The first phase waits for the index hole to appear, the second phase waits for it to disappear, and the third phase waits for it to reappear. This confirms the disk is spinning and allows the code to read the write-protect status from the FDC.
5109
GOSUB to 511EH to poll the FDC status register bit 1 with timeout. Returns Z FLAG set if bit 1 is set (index pulse detected), NZ if bit 1 is clear.
510C
If the NZ FLAG (Not Zero) has been set because bit 1 is clear (no index pulse yet), LOOP BACK to 5109H to keep polling. Wait for the index pulse to appear.
Wait for Index Pulse - Phase 2
Index pulse detected. Now wait for it to disappear (bit 1 goes low).
510E
GOSUB to 511EH to poll the FDC status register bit 1. Returns Z FLAG set if bit 1 is set.
5111
If the Z FLAG (Zero) has been set because bit 1 is still set (index pulse still present), LOOP BACK to 510EH. Wait for the index pulse to end.
Wait for Index Pulse - Phase 3
Index pulse has ended. Now wait for it to reappear (confirming a full rotation).
5113
GOSUB to 511EH to poll the FDC status register bit 1.
5116
If the NZ FLAG (Not Zero) has been set because bit 1 is clear (waiting for next index pulse), LOOP BACK to 5113H. Wait for the index pulse to reappear.
5118
POP BC C1
Restore Register Pair BC from the stack (the push at 5105H). Register C contains the drive number.
Extract Write Protect Status
The FDC status register was read during the last successful poll at 511EH. Register A contains the FDC status byte. Bit 6 indicates write-protect status. Extract this into the CARRY FLAG.
5119
RLCA 07
Rotate Register A (FDC status) left by 1 bit. This shifts bit 6 (write-protect) into bit 7.
511A
AND A,80H E6 80
Mask Register A with 80H, isolating bit 7 (which was originally bit 6, the write-protect status). If the disk is write-protected, A = 80H; otherwise A = 00H.
511C
ADD A,A 87
ADD Register A to itself (double it). If A is 80H (write-protected), 80H + 80H = 00H with CARRY set. If A is 00H (not protected), 00H + 00H = 00H with no CARRY. This converts the write-protect status into the CARRY FLAG: CARRY set = write-protected, CARRY clear = not protected. Either way, A becomes 00H, setting the Z FLAG to indicate the drive is ready.
511D
RET C9
Return to caller. Z FLAG is set (drive is ready). CARRY FLAG indicates write-protect status: set = write-protected, clear = not protected.
511EH - Poll FDC Status Register Bit 1 With Timeout
Polls the FDC status register at 37ECH and tests bit 1 (Index pulse in Type I status). Decrements the timeout counter in BC on each call. If BC reaches zero, aborts with an error by popping two return addresses and returning NZ. Returns with Z FLAG reflecting the state of bit 1: Z set if bit 1 is set, NZ if bit 1 is clear.
511E
DEC BC 0B
DECrement Register Pair BC (timeout counter) by 1.
511F
LD A,B 78
Load Register A with Register B (high byte of timeout counter).
5120
OR A,C B1
OR Register A with Register C. If BC is zero, the Z FLAG is set, indicating timeout.
5121
If the Z FLAG (Zero) has been set because the timeout counter BC has reached zero, JUMP to 5129H to handle the timeout error.
5123
LD A,(37ECH) 3A EC 37
Fetch the FDC Status Register from 37ECH (Model I memory-mapped FDC). This reads the current status of the WD1771 FDC.
5126
BIT 1,A CB 4F
Test bit 1 of the FDC status byte. In Type I status (after Restore/Seek/Step), bit 1 is the Index pulse indicator: 1 = index hole detected, 0 = no index hole. The Z FLAG is set if bit 1 is clear.
5128
RET C9
Return to caller. The Z FLAG reflects the state of FDC status bit 1: Z set if bit 1 is set (index detected), NZ if bit 1 is clear (no index).
5129
POP BC C1
Pop the return address of the immediate caller (the polling loop in 50FDH) from the stack and discard it.
512A
POP BC C1
Pop the saved BC value from the stack (pushed at 5105H in the drive select routine), restoring the drive number.
512B
OR A,01H F6 01
Set bit 0 of Register A, ensuring A is non-zero. This sets the NZ FLAG to indicate an error (FDC timeout - disk not spinning or no disk in drive).
512D
RET C9
Return directly to the caller of 50FDH (bypassing the polling loop) with the NZ FLAG set, indicating the drive is not ready.
512EH - Read Directory Sector for Drive C
Reads the directory sector for drive C into the 4D00H buffer. Uses SYS0 routines to look up the track cache and perform the sector read with retry on soft errors. Returns Z FLAG set on success, NZ with error code in A on failure.
512E
PUSH BC C5
Save Register Pair BC onto the stack (preserving the drive number in C).
512F
PUSH DE D5
Save Register Pair DE onto the stack.
5130
GOSUB to SYS0 routine at 4B55H (Track Cache Lookup). This looks up the current track position for drive C in the track cache table at 4304H+ and prepares the FDC for the sector read.
5133
LD E,01H 1E 01
Load Register E with 01H, the sector number to read. Sector 1 is the directory sector in TRSDOS 2.3.
5135
LD HL,4D00H 21 00 4D
Point Register Pair HL to 4D00H, the destination buffer for the directory sector data.
5138
GOSUB to SYS0 routine at 4B35H (Read Sector with Retry). Reads sector E from the current track into the buffer at HL. Performs recalibration and retry on soft errors. Returns Z FLAG set on success.
513B
POP DE D1
Restore Register Pair DE from the stack.
513C
POP BC C1
Restore Register Pair BC from the stack.
513D
RET Z C8
If the Z FLAG (Zero) has been set because the sector read succeeded, return to caller with success.
513E
LD A,16H 3E 16
Load Register A with error code 16H. This is the directory read error code for TRSDOS 2.3.
5140
RET C9
Return to caller with error code 16H (directory read error) in Register A. The NZ FLAG is inherently set since 16H is non-zero.
5141H - Write Directory Sector to Disk
Writes the directory sector from the 4D00H buffer to disk for the current drive. Uses SYS0 routines for track cache lookup and sector write. Returns Z FLAG set on success, NZ with error code in A on failure.
5141
PUSH BC C5
Save Register Pair BC onto the stack.
5142
PUSH DE D5
Save Register Pair DE onto the stack.
5143
GOSUB to SYS0 routine at 4B55H (Track Cache Lookup) to prepare the FDC for the write operation.
5146
LD E,01H 1E 01
Load Register E with 01H (directory sector number).
5148
LD HL,4D00H 21 00 4D
Point Register Pair HL to 4D00H, the source buffer containing the directory sector data to write.
514B
GOSUB to SYS0 routine at 46EFH (Write Sector). Writes the sector data from the buffer at HL to sector E on the current track. Returns Z FLAG set on success.
514E
POP DE D1
Restore Register Pair DE from the stack.
514F
POP BC C1
Restore Register Pair BC from the stack.
5150
RET Z C8
If the Z FLAG (Zero) has been set because the sector write succeeded, return to caller with success.
5151
LD A,17H 3E 17
Load Register A with error code 17H. This is the directory write error code for TRSDOS 2.3.
5153
RET C9
Return to caller with error code 17H (directory write error) in Register A.
5154H - Data Area
Single-byte data variable used as the drive number for the current file operation. Initialized to FFH (no drive specified) during filespec parsing and set to the actual drive number (0-3) when a drive specifier is found or when a file match is located.
5154
DEFB 00H 00
Drive Number Variable
Runtime storage for the current drive number. Initial value in the binary is 00H, but this is overwritten during execution: FFH means "search all drives," 00H-03H means a specific drive. The 8-byte password buffer (5155H-515CH) and 11-byte filename/extension template (515DH-5167H) follow this byte but are beyond the SYS2 code boundary and are populated at runtime by the filespec parser at 5027H.