4D00H - SYS2 Main Function Dispatcher
SYS2 is the NEWDOS/80 overlay that handles $OPEN (open existing file) and $INIT (initialize/create new file) operations. When the DOS invokes SYS2 via the RST 28H supervisor call mechanism, entry is at 4D00H with the function code in Register A. The low 5 bits of the function code encode this as SYS2 (directory sector 6, function 0 = binary 00100). The high 3 bits (bits 5-7) encode which specific function within SYS2 is requested. Valid function codes for SYS2 are: 24H, 44H, 64H, 84H, A4H, C4H, and E4H. Register Pair IX points to the File Control Block (FCB) for the operation. Register Pair DE points to the user-supplied parameter block. Register Pair IY points to the DOS system area base (4380H on Model III).
4D00
CP 24H FE24
Compare the function code in Register A against 24H. Function code 24H is the $OPEN command (open an existing file for access). If Register A equals 24H, the Z FLAG is set; otherwise the NZ FLAG is set.
4D02
If the Z FLAG is set (function code = 24H), JUMP to 4E2EH to handle the $OPEN file operation.
4D05
CP 44H FE44
Compare the function code in Register A against 44H. Function code 44H is the $INIT command (initialize/create a new file). If Register A equals 44H, the Z FLAG is set.
4D07
If the Z FLAG is set (function code = 44H), JUMP to 4DBDH to handle the $INIT (create new file) operation.
4D0A
CP 64H FE64
Compare the function code in Register A against 64H. Function code 64H is the $POSN (position/reposition) file operation. If Register A equals 64H, the Z FLAG is set.
4D0C
If the Z FLAG is set (function code = 64H), JUMP to 4F82H to handle the $POSN file position operation.
4D0F
CP 84H FE84
Compare the function code in Register A against 84H. Function code 84H is the hash code computation function. If Register A equals 84H, the Z FLAG is set.
4D11
If the Z FLAG is set (function code = 84H), JUMP to 5155H to compute the hash code for a filename.
4D14
CP A4H FEA4
Compare the function code in Register A against A4H. Function code A4H is the $FSPEC (file specification parse and search) function. If Register A equals A4H, the Z FLAG is set.
4D16
If the Z FLAG is set (function code = A4H), JUMP to 4D92H to handle the $FSPEC file specification parsing and directory search.
4D18
CP C4H FEC4
Compare the function code in Register A against C4H. Function code C4H is the $FEXT (file extent/locate) function. If Register A equals C4H, the Z FLAG is set.
4D1A
If the Z FLAG is set (function code = C4H), JUMP to 4D80H to handle the $FEXT file extent operation.
4D1C
CP E4H FEE4
Compare the function code in Register A against E4H. Function code E4H has multiple sub-functions selected by the value in Register C. If Register A does NOT equal E4H, the NZ FLAG is set.
4D1E
If the NZ FLAG is set (function code does not match any valid SYS2 code), JUMP to 4D2EH to return error code 2AH (general error).
4D20
DEC C 0D
DECrement Register C (the sub-function selector) by 1. If C was 1, it becomes 0 and the Z FLAG is set. (Note: C's value originates from user param at DE, passed via RST 28H.)
4D21
If the Z FLAG is set (sub-function C=1), JUMP to 4D32H to handle E4H sub-function 1: rename a file or modify file attributes.
4D23
DEC C 0D
DECrement Register C by 1. If C was 2 (now 0), the Z FLAG is set.
4D24
If the Z FLAG is set (sub-function C=2), JUMP to 50CAH to handle E4H sub-function 2: read a directory sector.
4D27
DEC C 0D
DECrement Register C by 1. If C was 3 (now 0), the Z FLAG is set.
4D28
If the Z FLAG is set (sub-function C=3), JUMP to 4D7BH to handle E4H sub-function 3: chain/link operation.
4D2A
DEC C 0D
DECrement Register C by 1. If C was 4 (now 0), the Z FLAG is set.
4D2B
If the Z FLAG is set (sub-function C=4), JUMP to 4D72H to handle E4H sub-function 4: $INIT with additional validation.
4D2EH - General Error Return (Error 2AH)
This routine returns to the caller with error code 2AH (general error) and the NZ FLAG set to indicate failure.
4D2E
LD A,2AH 3E2A
Load Register A with 2AH (decimal 42), which is the NEWDOS/80 error code for general error.
4D30
OR A B7
OR Register A with itself. Since A contains 2AH (non-zero), this sets the NZ FLAG to indicate an error condition to the caller. The Z FLAG is cleared.
4D31
RET C9
RETURN to caller. Register A = 2AH (error code), NZ FLAG set (indicating error).
4D32H - E4H Sub-Function 1: Rename/Modify File Attributes
This routine handles function code E4H with sub-function C=1. It renames a file or modifies its attributes. On entry, Register Pair DE points to the parameter block (first byte is the FCB pointer, second byte onward contains the new filename/attributes). Register Pair IX is set from DE to preserve the parameter block address.
4D32
PUSH DE D5
Save Register Pair DE (pointer to user parameter block) onto the stack.
4D33
POP IX DD E1
Restore the stack value into Register Pair IX. This copies DE into IX, so IX now points to the user parameter block.
4D35
INC DE 13
INCrement Register Pair DE by 1, advancing DE to point to the second byte of the parameter block.
4D36
LD A,(DE) 1A
Load Register A with the byte at address pointed to by DE. This fetches the access level / protection flags from the parameter block.
4D37
AND 07H E6 07
Mask Register A with 07H (binary 00000111), isolating the low 3 bits which represent the protection level (0-7).
4D39
CP 03H FE 03
Compare Register A (protection level, 0-7) against 03H. If the protection level is less than 3, the CARRY FLAG is set (valid). If 3 or greater, the NO CARRY FLAG is set (invalid for this operation).
4D3B
LD A,25H 3E 25
Load Register A with 25H, which is the NEWDOS/80 error code for access denied. This is pre-loaded in case the protection level check fails.
4D3D
If the NO CARRY FLAG is set (protection level >= 3, meaning the access level is too restrictive), JUMP to 4D30H to return with error 25H (access denied).
The protection level is valid (0, 1, or 2). Now set up to perform the rename operation. The code modifies a byte at 4E75H which is a [SELF-MODIFYING CODE] location that controls the behavior of the file search routine at 4E2CH.
4D3F
LD HL,4E75H 21 75 4E
Point Register Pair HL to address 4E75H, which is a [SELF-MODIFYING CODE] location inside the file search routine. The byte at this location is an instruction opcode that will be patched to alter the search behavior.
4D42
LD (HL),3EH 36 3E
Store 3EH at address 4E75H. The opcode 3EH is LD A,nn (load A with the next byte). [SELF-MODIFYING CODE] This patches the instruction at 4E75H to become LD A,08H (3E 08), which causes the search routine to return the drive number instead of skipping to the next phase.
4D44
LD DE,51E0H 11 E0 51
Point Register Pair DE to 51E0H, which is a buffer area in the overlay workspace used to store directory information during the rename operation.
4D47
GOSUB to 4E2CH to perform the file specification parse and directory search. This searches the directory for the file to be renamed. On return: Z FLAG set = file found, NZ FLAG = error, Register A = error code or status.
4D4A
LD (HL),18H 36 18
Store 18H at the address still in HL (4E75H), restoring the original instruction. The opcode 18H is JR nn (relative jump). [SELF-MODIFYING CODE] This undoes the patch made at 4D42H, restoring normal search behavior.
4D4C
If the Z FLAG is set (file search returned with no match found but no error - actually this means a specific condition where the file was not found), JUMP to 4D77H to return error 35H.
4D4E
CP 18H FE 18
Compare Register A against 18H. The value 18H is a special return code from the search routine indicating the file was found and the directory entry is loaded.
4D50
If the Z FLAG is set (file was found, A=18H), GOSUB to 48F0H in SYS0 to perform access validation on the file (check passwords and protection levels). On return: Z FLAG set = access granted, NZ FLAG = access denied.
4D53
RET NZ C0
If the NZ FLAG is set (either the file was not found with a non-18H error, or access was denied by the validation routine at 48F0H), RETURN to caller with the error code in Register A.
The file has been found and access is granted. Now copy the new filename/attributes from the user's buffer at 51CDH into the directory entry. Register Pair HL points to the directory sector buffer entry. Register A contains L (low byte of the directory entry pointer).
4D54
LD A,L 7D
Load Register A with the low byte of HL (the current pointer into the directory sector buffer).
4D55
ADD 05H C6 05
ADD 05H to Register A, advancing the pointer 5 bytes into the directory entry. This skips past the file status byte, the hash code, and other header fields to reach the filename field starting at offset 05H of the directory entry.
4D57
LD L,A 6F
Store the adjusted offset back into Register L, so HL now points to the filename field within the directory entry.
4D58
EX DE,HL EB
Exchange DE and HL. Now DE points to the filename field in the directory buffer, and HL is free to be set to the source buffer.
4D59
LD HL,51CDH 21 CD 51
Point Register Pair HL to 51CDH, which is the parsed filename buffer containing the new filename that was parsed from the user's input by the search routine.
4D5C
LD BC,000BH 01 0B 00
Load Register Pair BC with 000BH (decimal 11). This is the length of the filename field: 8 characters for the filename + 3 characters for the extension = 11 bytes.
4D5F
LDIR ED B0
Block copy 11 bytes from HL (51CDH, parsed filename buffer) to DE (directory entry filename field). This overwrites the old filename in the directory entry with the new filename. After execution, DE points past the filename field in the directory entry.
4D61
GOSUB to 48C4H in SYS0 to write the modified directory sector back to disk. On return: Z FLAG set = write succeeded, NZ FLAG = write error with code in A.
4D64
LD A,01H 3E 01
Load Register A with 01H. This value will be passed to the next routine as a parameter requesting that the HIT (Hash Index Table) sector be read.
4D66
If the Z FLAG is set (directory sector write succeeded), GOSUB to 48AFH in SYS0 to read/access the HIT sector. Register A=01H selects the HIT sector. On return: Z FLAG set = success, HL points to the HIT buffer.
4D69
RET NZ C0
If the NZ FLAG is set (either the directory write or the HIT read failed), RETURN to caller with the error code in Register A.
The directory sector was written and the HIT sector is now in the buffer. HL points to the HIT entry for this file. The hash code stored in the HIT needs to be updated to match the new filename. IX+07H contains the offset into the HIT buffer for this file's entry.
4D6A
LD L,(IX+07H) DD 6E 07
Load Register L with the byte at IX+07H. The parameter block at IX contains at offset 07H the HIT buffer offset for this file's hash entry. This sets L to point within the HIT sector buffer to the hash code byte that needs updating.
4D6D
LD (HL),00H 36 00
Store 00H at the HIT entry pointed to by HL. This clears the old hash code from the HIT table. The new hash code will be written by the subsequent call to 48C4H which writes the modified HIT sector back to disk.
4D6F
JUMP to 48C4H in SYS0 to write the modified HIT sector back to disk, then return to the original caller. The hash code for the old filename has been cleared; the new hash code will be set when the file is next accessed.
4D72H - E4H Sub-Function 4: $INIT with Validation
This routine handles function code E4H with sub-function C=4. It calls the $INIT handler at 4DBDH (same as function code 44H) and returns based on the result.
4D72
GOSUB to 4DBDH to execute the $INIT (create new file) operation. On return: Z FLAG set = success, NZ FLAG = error, CARRY FLAG indicates additional status.
4D75
RET NZ C0
If the NZ FLAG is set (error occurred during $INIT), RETURN to caller with error code in Register A.
4D76
RET C D8
If the CARRY FLAG is set (the $INIT operation succeeded and the CARRY flag was set as a secondary success indicator), RETURN to caller with success.
4D77H - File Not Found Error Return (Error 35H)
This routine returns to the caller with error code 35H and the NZ FLAG set, indicating the specified file was not found in the directory.
4D77
LD A,35H 3E 35
Load Register A with 35H (decimal 53), which is the NEWDOS/80 error code for a file-related error (file specification error or file not found during rename).
4D79
OR A B7
OR Register A with itself. Since A contains 35H (non-zero), this sets the NZ FLAG to indicate an error condition.
4D7A
RET C9
RETURN to caller. Register A = 35H (error code), NZ FLAG set (indicating error).
4D7BH - E4H Sub-Function 3: Chain/Link Operation
This routine handles function code E4H with sub-function C=3. It calls a support routine at 4C7AH in SYS0/SYS1, and based on the result, either returns with error or falls through to the $FEXT handler at 4D80H.
4D7B
GOSUB to 4C7AH in SYS0/SYS1 to perform a preliminary chain/link operation. On return: CARRY FLAG set = operation completed (return immediately), CARRY FLAG clear = continue to the $FEXT handler.
4D7E
RET C D8
If the CARRY FLAG is set (chain operation completed), RETURN to caller.
4D7F
POP AF F1
Restore Register Pair AF from the stack. This discards the return address from the CALL at 4D7BH, so the next RET will return to the original caller of the E4H sub-function dispatcher rather than returning to the middle of the dispatcher. The fall-through to 4D80H is intentional.
4D80H - Function C4H: $FEXT (File Extent/Locate) Handler
This routine handles function code C4H ($FEXT). It calls the $FSPEC routine to parse and search for a file, and based on the result, dispatches to either the error handler at 4409H, the DEBUG entry at 440DH, or returns normally via 4BC5H. The POP AF at 4D7F means that when entered from the E4H/C=3 path, the stack has been adjusted.
4D80
EX (SP),HL E3
Exchange the value at the top of the stack with Register Pair HL. This saves the current HL value on the stack and loads HL with the return address that was on the stack. This preserves HL while replacing the return address.
4D81
GOSUB to 4D92H to perform the $FSPEC file specification parse and directory search.
4D84
If the NZ FLAG is set (file search returned an error), JUMP to 4409H in SYS0 to invoke the error handler. Register A contains the error code.
4D87
EX (SP),HL E3
Exchange the value at the top of the stack with HL again, restoring the original HL and putting the original value back on the stack.
4D88
LD A,(4289H) 3A 89 42
Load Register A with the byte at 4289H. This address contains a system status flag that indicates whether the DOS is in MINI-DOS mode or normal mode. Bit 7 is the relevant flag.
4D8B
RLCA 07
Rotate Register A Left through Carry. This shifts bit 7 (the MINI-DOS flag) into the CARRY FLAG for testing.
4D8C
If the CARRY FLAG is set (bit 7 of 4289H was set, meaning the system is in MINI-DOS mode), JUMP to 440DH to enter the DEBUG facility.
4D8F
JUMP to 4BC5H in SYS0 to perform the normal return processing for the $FEXT operation.
4D92H - $FSPEC: File Specification Parse and Directory Search
This routine (function code A4H) parses a filespec string pointed to by Register Pair DE and searches the directory for a matching file. It first calls the full $OPEN search routine at 4E2CH, then examines the result. If the file is found, it sets up the FCB and returns the file's directory information.
4D92
LD HL,4300H 21 00 43
Point Register Pair HL to 4300H, which is the system sector buffer on Model III (equivalent to 4200H on Model I). This buffer is used to hold directory and HIT sectors during file operations.
4D95
GOSUB to 4E2CH to perform the file specification parse and directory search. On entry: DE points to the filespec string, HL points to the sector buffer. On return: Z FLAG set = file found with no errors, NZ FLAG = error or not found, A = status/error code.
4D98
If the Z FLAG is set (file was found), JUMP to 4DA0H to process the found directory entry.
4D9A
CP 18H FE 18
Compare Register A against 18H. The value 18H is a special status code meaning "file found and directory entry loaded in buffer."
4D9C
RET NZ C0
If the NZ FLAG is set (A is not 18H, meaning a real error occurred during the search), RETURN to caller with the error code in Register A.
4D9D
ADD 07H C6 07
ADD 07H to Register A (which contains 18H). This produces 1FH in Register A. The value 1FH serves as a modified status code indicating the file was found via the 18H path. The NZ FLAG is set as a side effect since the result is non-zero.
4D9F
RET C9
RETURN to caller. Register A = 1FH (file found via secondary path), NZ FLAG set.
4DA0H - Process Found Directory Entry
When the file search at 4E2CH returned with Z FLAG set, the file was found in the directory. Register Pair DE points to the buffer area where the directory data has been stored. This routine extracts the file's attributes, validates access permissions, stores the directory location, and returns the information to the caller.
4DA0
EX DE,HL EB
Exchange DE and HL. Now HL points to the search result buffer, and DE is free.
4DA1
INC HL 23
INCrement HL by 1, advancing to offset +1 in the search result. This points to the file attributes/flags byte of the directory entry.
4DA2
LD A,(HL) 7E
Load Register A with the file attributes byte at the current HL address. This contains the file's protection level and status flags.
4DA3
PUSH AF F5
Save Register Pair AF (containing the file attributes in A) onto the stack for later use.
4DA4
PUSH HL E5
Save Register Pair HL (pointer to the attributes byte in the directory entry) onto the stack for later restoration.
4DA5
AND 07H E6 07
Mask Register A with 07H (binary 00000111), isolating the low 3 bits which represent the file's protection level (0=no protection, up to 7=maximum protection).
4DA7
LD B,A 47
Copy the protection level from Register A into Register B for comparison.
4DA8
LD A,06H 3E 06
Load Register A with 06H, which is the maximum allowed protection level for this operation.
4DAA
CP B B8
Compare Register A (06H) against Register B (file's protection level). If the file's protection level is less than or equal to 6, the NO CARRY FLAG is set (valid). If greater than 6, the CARRY FLAG is set (too restrictive).
4DAB
LD A,25H 3E 25
Load Register A with 25H, the NEWDOS/80 error code for access denied. Pre-loaded in case the protection check fails.
4DAD
LD (HL),2DH 36 2D
Store 2DH at the address pointed to by HL. HL still points to the attributes byte in the directory entry. The value 2DH is a marker byte stored temporarily to flag that this entry has been processed.
4DAF
DEC HL 2B
DECrement HL by 1, moving back to offset +0 of the directory entry (the first byte/status byte).
4DB0
If the NO CARRY FLAG is set (protection level is acceptable, <= 6), GOSUB to 4BCDH in SYS0 to perform further validation or setup. This call is skipped if the file is over-protected.
4DB3
LD (4403H),HL 22 03 44
Store Register Pair HL at address 4403H. The address 4403H is a DOS system variable that holds the pointer to the current directory entry being processed.
4DB6
EX DE,HL EB
Exchange DE and HL. Now DE holds the directory entry pointer, and HL is available.
4DB7
POP HL E1
Restore Register Pair HL from the stack (the pointer to the attributes byte that was saved at 4DA4H).
4DB8
POP BC C1
Restore Register Pair BC from the stack. The AF pair was pushed at 4DA3H, so B now contains the original attributes byte that was in A.
4DB9
LD (HL),B 70
Store Register B (the original file attributes byte) back to the address pointed to by HL. This restores the attributes byte that was temporarily overwritten with 2DH at 4DADH.
4DBA
DEC HL 2B
DECrement HL by 1, moving back to the start of the directory entry.
4DBB
EX DE,HL EB
Exchange DE and HL. This swaps the pointers back to their expected positions for the return to the caller.
4DBC
RET C9
RETURN to caller. The file has been found, attributes validated, and the directory entry pointer stored at 4403H. The flags reflect the result of the validation at 4BCDH.
4DBDH - Function 44H: $INIT (Initialize/Create New File) Handler
This routine handles function code 44H ($INIT), which creates a new file on disk. It first calls the $OPEN search routine at 4E2EH to check if a file with the specified name already exists. If the file already exists (Z FLAG set), it returns immediately (error - duplicate filename). If the file does not exist but a special condition is indicated (A=18H), it proceeds to allocate a new directory entry and set up the file.
4DBD
GOSUB to 4E2EH to perform the file specification parse and directory search. This is the $OPEN entry with B=00H preset. On return: Z FLAG set = file already exists, NZ FLAG = file not found or error, A = status code.
4DC0
RET Z C8
If the Z FLAG is set (file already exists in the directory), RETURN to caller. For $INIT, finding an existing file with the same name means the operation cannot proceed (duplicate filename). The Z FLAG indicates the duplicate condition to the caller.
4DC1
CP 18H FE 18
Compare Register A against 18H. The value 18H is a special status code meaning "file not found but directory is accessible and ready for a new entry."
4DC3
RET NZ C0
If the NZ FLAG is set (A is not 18H, meaning a real error occurred such as disk error or directory full), RETURN to caller with the error code in Register A.
At this point, the file does not exist (good for $INIT) and A=18H (directory is ready). The code now proceeds to find a free directory slot and create the new file entry. First, it calls 492BH to set up the file context, then determines the drive number.
4DC4
GOSUB to 492BH in SYS0 to set up the file context. This routine prepares the FCB and internal data structures for the new file operation.
4DC7
LD BC,(4E0AH) ED 4B 0A 4E
Load Register Pair BC with the 16-bit value at address 4E0AH. This address contains the drive number pair: B = the drive number from the filespec, C = the default drive number. These were stored during the file specification parse at 4E2CH.
4DCB
LD A,B 78
Load Register A with Register B (the drive number from the filespec, or 0 if none was specified).
4DCC
CP C B9
Compare Register A (filespec drive number) against Register C (default drive number). If they are equal, it means the filespec either specified the default drive or no drive was specified. If they are equal, the Z FLAG is set.
4DCD
If the Z FLAG is set (drive numbers match or no drive specified), JUMP to 4DD2H to skip the drive override and use the value already in A.
4DCF
LD A,(42C1H) 3A C1 42
Load Register A with the byte at 42C1H. This address contains the system default drive number (the drive from which the DOS was booted or the current default). This overrides the filespec drive when the filespec drive differs from the default.
4DD2
LD (4E0EH),A 32 0E 4E
Store Register A (the resolved drive number) at address 4E0EH. This [SELF-MODIFYING CODE] location is used by the directory search routine at 4791H as the target drive number for subsequent disk operations.
4DD5
GOSUB to 4791H in SYS0 to read the directory from the target drive. This loads the GAT (Granule Allocation Table) and HIT (Hash Index Table) sectors into the system sector buffer. On return: Z FLAG set = success, NZ FLAG = error.
4DD8
If the NZ FLAG is set (error reading the directory), JUMP to 4DE3H to handle the error by trying to increment the error count and retry.
The directory was read successfully. Now check if the hash code (stored at 4D6EH by the earlier parse routine) can find a free slot in the HIT sector. The byte at 4D6EH is a [SELF-MODIFYING CODE] location that stores the computed hash code for the new filename.
4DDA
LD HL,4D6EH 21 6E 4D
Point Register Pair HL to 4D6EH. This address contains the computed hash code for the new filename. It was stored there as [SELF-MODIFYING CODE] by the parse routine.
4DDD
LD B,(HL) 46
Load Register B with the byte at 4D6EH, which is the hash code for the new filename.
4DDE
GOSUB to 50CFH to search the HIT for a free slot matching the hash code in Register B. On return: Z FLAG set = free slot found, A = slot index, NZ FLAG = no free slot (directory full).
4DE1
If the Z FLAG is set (a free HIT slot was found), JUMP to 4DEAH to proceed with writing the new file entry.
4DE3
LD E,1AH 1E 1A
Load Register E with 1AH (decimal 26). This is an error count or retry parameter passed to the error/retry handler at 4E09H.
4DE5
GOSUB to 4E09H to handle the error condition. This routine manages retry logic: it checks if the error has been retried enough times and either returns to try again or returns an error code. On return: Z/NZ indicates whether to retry or abort.
4DE8
JUMP back to 4DD2H to retry the directory read and HIT search with the (possibly updated) drive number. This forms a retry loop: read directory → search HIT → if error, call error handler → retry.
A free HIT slot has been found. Register A contains the slot index (the position within the HIT sector where the new hash code should be written). The code now writes the hash code, reads the corresponding directory sector, and sets up the new file's directory entry.
4DEA
LD (4F56H),A 32 56 4F
Store Register A (the HIT slot index for the new file) at address 4F56H. This [SELF-MODIFYING CODE] location preserves the slot index for later use when building the directory entry.
4DED
LD (HL),10H 36 10
Store 10H at the address pointed to by HL. HL still points to the HIT entry for the new file. The value 10H is a marker that indicates a directory entry is being initialized at this HIT slot. This is a preliminary write; the actual hash code will be written later.
4DEF
GOSUB to 4E1FH to extract directory parameters from the current buffer position. This reads the directory track and sector information needed to access the actual directory sector for this HIT slot.
4DF2
LD A,(4F5EH) 3A 5E 4F
Load Register A with the byte at 4F5EH. This address contains the drive number that was stored during the file parse operation. The drive number identifies which disk drive contains the target directory.
4DF5
LD (HL),A 77
Store the drive number (Register A) at the current HL address. This writes the drive number into the directory parameter area for the upcoming disk write operation.
4DF6
INC HL 23
INCrement HL by 1, advancing to the next byte in the parameter area.
4DF7
EX DE,HL EB
Exchange DE and HL. Now DE points to the next position in the parameter area, and HL is free for the source address.
4DF8
LD HL,51CDH 21 CD 51
Point Register Pair HL to 51CDH, the parsed filename buffer containing the new filename (8+3 format) and its attributes.
4DFB
LD BC,000FH 01 0F 00
Load Register Pair BC with 000FH (decimal 15). This is the number of bytes to copy: 11 bytes filename + 4 bytes attributes/metadata = 15 bytes from the parsed filename buffer into the directory entry template.
4DFE
LDIR ED B0
Block copy 15 bytes from HL (51CDH, parsed filename buffer) to DE (directory entry template). This populates the new directory entry with the filename, extension, and initial attribute bytes.
4E00
GOSUB to 48C4H in SYS0 to write the modified HIT sector back to disk. The HIT now contains the hash code marker (10H) at the slot allocated for the new file. On return: Z FLAG set = write succeeded.
4E03
RET NZ C0
If the NZ FLAG is set (HIT sector write failed), RETURN to caller with the error code in Register A.
4E04
GOSUB to 4F2EH to initialize the FCB (File Control Block) for the new file. This sets up the in-memory file tracking structure with default values, including the file position, buffer pointers, and status flags.
4E07
SCF 37
Set the CARRY FLAG. The CARRY FLAG is used as a success indicator for the $INIT operation, signaling to the caller that a new file was created (as opposed to opening an existing one).
4E08
RET C9
RETURN to caller. CARRY FLAG set = new file created, Z FLAG state from FCB init at 4F2EH.
4E09H - Error/Retry Handler
This routine handles error conditions during directory operations. Register E contains the error type/count parameter (1AH for directory-full type errors). Register D is preserved from the caller. The routine manages a retry counter and determines whether to retry the operation or return a fatal error. The address 4E0DH is a [SELF-MODIFYING CODE] location that stores the retry counter.
4E09
LD HL,0000H 21 00 00
Point Register Pair HL to 0000H. This loads a zero value into HL for use in the comparison below. L=00H will be compared against the retry counter.
4E0C
LD D,A 57
Copy Register A (the current error/status code) into Register D for preservation.
4E0D
LD A,00H 3E 00
Load Register A with 00H. This is a [SELF-MODIFYING CODE] location: the operand byte at address 4E0EH is overwritten by the code at 4DD2H and 4E9FH with the current drive number. So this instruction actually loads A with the drive number that was last stored there.
4E0F
INC A 3C
INCrement Register A by 1. This advances to the next drive number for retry purposes (trying the next drive in sequence).
4E10
CP L BD
Compare Register A (incremented drive number) against Register L (00H from the LD HL,0000H instruction). If A=00H (wrapped around past FFH), the CARRY FLAG would not be set. This checks if the drive number has wrapped around.
4E11
RET C D8
If the CARRY FLAG is set (the drive number comparison indicates the search should stop), RETURN to caller with the CARRY FLAG set as a signal to abort.
4E12
POP HL E1
Restore Register Pair HL from the stack. This pops the return address of the caller (the CALL 4E09H instruction), effectively discarding it. The routine will now return to the caller's caller.
4E13
If the Z FLAG is set (from the CP L at 4E10H, meaning the new drive number equals zero), JUMP to 4E18H to return with error code E.
4E15
LD A,D 7A
Load Register A with Register D (the preserved error/status code from the original error).
4E16
OR A B7
OR Register A with itself. This sets the NZ FLAG if D was non-zero (indicating an actual error to propagate).
4E17
RET NZ C0
If the NZ FLAG is set (there is a non-zero error code to return), RETURN to the caller's caller with the error code in Register A.
4E18
LD A,E 7B
Load Register A with Register E (the error type parameter that was passed to this routine, e.g., 1AH).
4E19
OR A B7
OR Register A with itself. This sets the NZ FLAG (since E is non-zero), indicating an error condition.
4E1A
RET C9
RETURN to the caller's caller. Register A = error code (from E), NZ FLAG set (error condition).
4E1BH - Invalid Drive Number Error Return (Error 20H)
This short routine returns error code 20H (decimal 32) with the NZ FLAG set, indicating an invalid drive specification in the filespec.
4E1B
LD A,20H 3E 20
Load Register A with 20H (decimal 32), which is the NEWDOS/80 error code for invalid drive specification.
4E1D
OR A B7
OR Register A with itself. Since A contains 20H (non-zero), this sets the NZ FLAG to indicate an error condition.
4E1E
RET C9
RETURN to caller. Register A = 20H (error code), NZ FLAG set.
4E1FH - Extract Directory Parameters from Buffer
This routine reads 3 bytes from the buffer pointed to by HL, extracting directory track, sector, and drive information used for disk I/O operations. The values are stored in system variables at 4F46H and 4F58H for use by the disk read/write routines.
4E1F
INC HL 23
INCrement HL by 1, advancing to the next byte in the buffer.
4E20
LD A,(HL) 7E
Load Register A with the byte at the current HL address. This reads the directory track number from the buffer.
4E21
LD (4F46H),A 32 46 4F
Store the directory track number at address 4F46H. This [SELF-MODIFYING CODE] location is used by disk I/O routines as the target track number for the next disk operation.
4E24
INC HL 23
INCrement HL by 1.
4E25
INC HL 23
INCrement HL by 1, advancing past a reserved byte to the next data byte.
4E26
LD A,(HL) 7E
Load Register A with the byte at the current HL address. This reads the directory sector number from the buffer.
4E27
LD (4F58H),A 32 58 4F
Store the directory sector number at address 4F58H. This [SELF-MODIFYING CODE] location is used by disk I/O routines as the target sector number for the next disk operation.
4E2A
INC HL 23
INCrement HL by 1, advancing past the sector number to position for the next read.
4E2B
RET C9
RETURN to caller. The directory track number is stored at 4F46H and the sector number at 4F58H. HL points to the byte after the extracted parameters.
4E2CH - Pre-Entry: Set B=00H Before $OPEN
This two-byte stub sets Register B to 00H before falling through to the main $OPEN entry at 4E2EH. When entered directly via function code 24H ($OPEN), B is set here. When entered from other paths (like $INIT at 4DBDH), B may already contain a specific value and the call targets 4E2EH directly.
4E2C
LD B,00H 06 00
Load Register B with 00H. Register B serves as a mode flag for the $OPEN routine: 00H = normal open (search and return directory info).
4E2EH - Function 24H: $OPEN (Open Existing File) Main Entry
This is the main entry point for the $OPEN operation. It calls the SYS0 context setup routine, then parses the user-supplied filespec string, computes the hash code, and searches the directory for a matching file. On entry: Register Pair DE points to the user parameter block containing the filespec string. Register B contains a mode flag (00H for normal open). Register Pair IY points to the DOS system area base at 4380H.
4E2E
GOSUB to 492BH in SYS0 to save registers and set up the file context. This routine pushes critical registers, establishes the FCB pointer, and prepares the return sequence to 48B3H for register restoration on exit.
4E31
LD (4F48H),HL 22 48 4F
Store Register Pair HL at address 4F48H. HL contains the sector buffer pointer (either 4300H passed by the caller or the system default). This is saved for later use by disk I/O routines.
4E34
LD A,B 78
Load Register A with Register B (the mode flag: 00H for normal open).
4E35
LD (4F5EH),A 32 5E 4F
Store the mode flag at address 4F5EH. This [SELF-MODIFYING CODE] location preserves the mode flag for use during the file search and FCB initialization phases.
Now parse the filespec string pointed to by DE. The filespec format is: FILENAME/EXT:D where FILENAME is up to 8 characters, /EXT is up to 3 characters, and :D is the optional drive number. The parsed components are stored in the filename buffer at 51CDH.
4E38
LD HL,51CDH 21 CD 51
Point Register Pair HL to 51CDH, which is the parsed filename buffer (11 bytes for 8+3 filename format). The parsed filename components will be stored here.
4E3B
DEC DE 1B
DECrement Register Pair DE by 1. This backs up the filespec pointer by one byte so that the first call to the character fetch routine (which pre-increments DE) will read the first character of the filespec.
4E3C
XOR A AF
Set Register A to ZERO (XOR A with itself). This clears A and all flags, establishing the initial state for the filename parse. A=0 is passed to the character extraction routine as a mode parameter.
4E3D
GOSUB to 5121H to extract the filename portion (up to 8 characters) from the filespec string at DE. This routine reads characters, converts to uppercase, pads with spaces, and stores them in the buffer at HL. On return: A = the delimiter character that terminated the filename, HL advanced past the stored bytes.
4E40
CP 2FH FE 2F
Compare Register A against 2FH (ASCII: /). The slash character is the extension separator in NEWDOS/80 filespecs (FILENAME/EXT format). If A equals 2FH, the Z FLAG is set.
4E42
LD B,03H 06 03
Load Register B with 03H (decimal 3). This sets the maximum extension length to 3 characters for the next character extraction call.
4E44
GOSUB to 5123H to extract the extension portion (up to 3 characters) from the filespec string. Entry at 5123H (instead of 5121H) skips the initial setup and uses the B=3 character count already set. On return: A = the next delimiter character, HL advanced past the extension bytes in the buffer.
4E47
CP 2EH FE 2E
Compare Register A against 2EH (ASCII: .). The period is the password separator in NEWDOS/80 filespecs (FILENAME/EXT.PASSWORD format). If A equals 2EH, the Z FLAG is set.
4E49
GOSUB to 5121H to extract the password portion (up to 8 characters) from the filespec. Even though the password is extracted, it is stored but may not be used unless the file is password-protected. On return: A = the next delimiter character.
4E4C
LD B,00H 06 00
Load Register B with 00H. This clears Register B to prepare for the drive number, indicating "no explicit drive specified" as the default.
4E4E
LD C,(IY+3FH) FD 4E 3F
Load Register C with the byte at IY+3FH (address 4380H+3FH = 43BFH). This is the default drive number stored in the DOS system area. Register C will hold the default drive if no drive is specified in the filespec.
4E51
CP 3AH FE 3A
Compare Register A (the delimiter character after the password/extension) against 3AH (ASCII: :). The colon is the drive separator in filespecs (FILENAME/EXT:D). If A equals 3AH, the Z FLAG is set.
4E53
If the NZ FLAG is set (no colon found, meaning no drive number was specified), JUMP to 4E75H to skip the drive parsing and use the default drive in Register C.
A colon was found after the filename, indicating an explicit drive number follows. The drive number is a 1 or 2 digit decimal number (0-9 or 00-99). The code parses this number and validates it against the maximum drive count.
4E55
INC DE 13
INCrement Register Pair DE by 1, advancing to the character after the colon (the first digit of the drive number).
4E56
LD A,(DE) 1A
Load Register A with the byte at address pointed to by DE. This fetches the first digit of the drive number from the filespec string.
4E57
SUB 30H D6 30
SUBtract 30H (ASCII 0) from Register A, converting the ASCII digit character to its binary value (0-9). If the character was not a digit, the result will be >= 10.
4E59
CP 0AH FE 0A
Compare Register A against 0AH (decimal 10). If A < 10, the CARRY FLAG is set (valid digit). If A >= 10, the NO CARRY FLAG is set (not a digit).
4E5B
If the NO CARRY FLAG is set (the character after the colon is not a valid digit), JUMP to 4E1BH to return error 20H (invalid drive specification).
4E5D
LD C,A 4F
Copy the first drive digit value (0-9) from Register A into Register C.
4E5E
INC DE 13
INCrement DE by 1, advancing to the next character (possible second digit of drive number).
4E5F
LD A,(DE) 1A
Load Register A with the next character from the filespec string.
4E60
SUB 30H D6 30
SUBtract 30H from Register A, converting from ASCII to binary value.
4E62
CP 0AH FE 0A
Compare Register A against 0AH. If A < 10, the CARRY FLAG is set (valid second digit). If A >= 10, the NO CARRY FLAG is set (not a digit, meaning the drive number was a single digit).
4E64
If the NO CARRY FLAG is set (no second digit), JUMP to 4E74H. The single-digit drive number is already in C.
A second digit was found. The code now computes the two-digit drive number: first_digit × 10 + second_digit. This is done by adding C to itself 9 times (C × 10) then adding the second digit.
4E66
LD L,A 6F
Copy the second digit value from Register A into Register L for temporary storage.
4E67
LD A,C 79
Load Register A with Register C (the first digit value).
4E68
LD B,09H 06 09
Load Register B with 09H (decimal 9). This is the loop counter for the multiply-by-10 operation: add C to A nine times, which when combined with the original value in A makes A = C × 10.
4E6A
ADD A,C 81
[LOOP START] ADD Register C (first digit value) to Register A. This accumulates C × 10 by repeated addition.
4E6B
If the CARRY FLAG is set (the multiplication overflowed past 255), JUMP to 4E1BH to return error 20H (invalid drive number - too large).
4E6D
DECrement B and loop back to 4E6AH if not zero. [LOOP END] After 9 iterations, A contains C × 10.
4E6F
ADD A,L 85
ADD Register L (the second digit value) to Register A (C × 10). Register A now contains the complete two-digit drive number: first_digit × 10 + second_digit.
4E70
If the NO CARRY FLAG is set (no overflow, the computed drive number fits in 8 bits), JUMP back to 4E5DH. NOTE: This creates a loop that could parse additional digits, but in practice NEWDOS/80 drive numbers are limited and the validation at 4E62H will catch the terminator.
4E72
If the addition overflowed (CARRY set), JUMP to 4E1BH to return error 20H (drive number too large).
4E74
LD B,C 41
Copy Register C (the parsed drive number) into Register B. Now B = explicit drive from filespec, C = same value (will be compared with the default drive).
4E75
JR 4E7FH 18 08
JUMP to 4E7FH to store the drive numbers and continue with the hash computation. NOTE: This is the [SELF-MODIFYING CODE] target at 4E75H. The byte at 4E75H (normally 18H = JR opcode) is changed to 3EH (LD A,nn) by the rename routine at 4D42H to alter behavior.
4E77H - Drive Number Validation
This code validates the parsed drive number against the system maximum and retrieves the actual drive number from the DOS system area if no explicit drive was specified.
4E77
LD A,C 79
Load Register A with Register C (the default drive number from IY+3FH).
4E78
CP B B8
Compare Register A (default drive) against Register B (explicit drive from filespec). If they are equal, the Z FLAG is set.
4E79
If the Z FLAG is set (the default drive equals the explicit drive), JUMP to 4E1BH to return error 20H. This indicates a conflict or invalid condition where the filespec drive matches the default in a context where it should not.
4E7B
LD B,(IY+FEH) FD 46 FE
Load Register B with the byte at IY+FEH (address 4380H+FEH = 447EH). This is the maximum drive number configured in the system. Register B now contains the drive count limit.
4E7E
LD C,B 48
Copy Register B (maximum drive number) into Register C. Both B and C now hold the max drive value.
4E7F
LD (4E0AH),BC ED 43 0A 4E
Store Register Pair BC at address 4E0AH. B = the explicit drive number (or max drive as default), C = the default/max drive number. These values are saved for later use by the $INIT and directory search routines.
4E83H - Hash Code Computation and Directory Search
After parsing the filespec and storing the drive numbers, this section computes the hash code for the parsed filename, stores it for later matching, and begins the directory search loop. The hash code is a single byte derived from the 11-byte filename+extension that provides a quick lookup index into the HIT (Hash Index Table) sector.
4E83
PUSH BC C5
Save Register Pair BC (drive numbers: B=explicit, C=default) onto the stack for later restoration.
4E84
GOSUB to 5152H to compute the hash code for the parsed filename at 51CDH. This routine XORs and rotates the 11 filename bytes through a polynomial hash algorithm to produce a single-byte hash code. On return: HL = the computed 16-bit hash result, with the hash code in the low byte.
4E87
LD (51D8H),HL 22 D8 51
Store Register Pair HL (the hash computation result) at address 51D8H. This saves the primary hash value used for filename matching during directory searches.
4E8A
LD (51DAH),HL 22 DA 51
Store Register Pair HL at address 51DAH as well. This creates a backup copy of the hash value that can be used for comparison if the primary value is modified during the search.
4E8D
LD HL,51CDH 21 CD 51
Point Register Pair HL to 51CDH, the parsed filename buffer (11 bytes: 8 filename + 3 extension).
4E90
LD B,0BH 06 0B
Load Register B with 0BH (decimal 11), the number of bytes in the filename+extension.
4E92
XOR A AF
Set Register A to ZERO. This initializes the accumulator for computing a simple checksum of the filename bytes.
[LOOP START] This loop XORs all 11 bytes of the filename together with rotate operations to compute a secondary hash/checksum value. This checksum is stored at 4D6EH and used to verify filename matches during directory searches.
4E93
XOR (HL) AE
XOR Register A with the byte at the address pointed to by HL (current filename character). This folds each filename byte into the running checksum.
4E94
INC HL 23
INCrement HL by 1, advancing to the next filename byte.
4E95
RLCA 07
Rotate Register A Left through Carry. This spreads the bit influence of each filename character across different bit positions, improving the hash distribution.
4E96
DECrement B and loop back to 4E93H if not zero. [LOOP END] After 11 iterations, A contains the checksum of the entire filename.
4E98
If the NZ FLAG is set (checksum is non-zero), JUMP to 4E9BH to store the checksum as-is.
4E9A
INC A 3C
INCrement Register A by 1. If the checksum happened to be zero, change it to 1. A zero checksum would be ambiguous because zero in the HIT table means "empty slot." This ensures the checksum is always non-zero.
4E9B
LD (4D6EH),A 32 6E 4D
Store the filename checksum at address 4D6EH. This is a [SELF-MODIFYING CODE] location: the byte at 4D6EH is the operand of a LD (HL),00H instruction at 4D6DH. By writing the checksum here, it changes the instruction to LD (HL),checksum. This value is used later to verify filename matches in the HIT search.
4E9E
POP AF F1
Restore Register Pair AF from the stack. The BC pair was pushed at 4E83H, so A now contains the value that was in B (the explicit drive number from the filespec) and F contains the value that was in C.
4E9F
LD (4E0EH),A 32 0E 4E
Store Register A (the drive number) at address 4E0EH. This is the [SELF-MODIFYING CODE] location at the operand of the LD A,00H instruction at 4E0DH, setting the target drive number for the directory read.
4EA2
GOSUB to 4791H in SYS0 to read the GAT and HIT sectors from the target drive into the system sector buffer. On return: Z FLAG set = success, NZ FLAG = error.
4EA5
If the Z FLAG is set (directory read succeeded), JUMP to 4EAEH to begin searching the HIT for the filename hash code.
4EA7
LD E,18H 1E 18
Load Register E with 18H. This is the error parameter for the error/retry handler, indicating a directory read error.
4EA9
GOSUB to 4E09H to handle the directory read error. The retry handler may increment the drive number and signal to retry.
4EAC
JUMP back to 4E9FH to retry the directory read with the (possibly updated) drive number. This forms a retry loop: store drive → read directory → if error, call handler → retry.
4EAEH - HIT Search Loop: Find Matching Hash Code in Directory
The GAT and HIT sectors have been successfully read from disk. Register Pair DE is set to 51ADH (the start of the working buffer for directory data). Register Pair BC is loaded with the size of the search range. The code now scans through the HIT (Hash Index Table) looking for entries that match the computed hash code of the target filename. When a match is found, the corresponding directory sector is read and the full 11-byte filename is compared.
4EAE
LD DE,51ADH 11 AD 51
Point Register Pair DE to 51ADH, which is the directory working buffer used to store HIT slot indices of candidate hash matches found during the search.
4EB1
LD BC,001FH 01 1F 00
Load Register Pair BC with 001FH. B=00H is the starting HIT offset (beginning of the HIT table), and C=1FH (decimal 31) is the ending HIT position limit. The HIT contains up to 32 entries (offsets 0-31), each representing a directory slot.
4EB4
LD A,B 78
[LOOP START - HIT Scan Outer Loop] Load Register A with Register B (the current HIT offset/position being examined).
4EB5
SUB C 91
SUBtract Register C (the ending HIT position limit, 1FH) from Register A (current offset). If B equals C, the result is zero (all entries scanned).
4EB6
If the Z FLAG is set (B equals C, meaning all HIT entries have been scanned without finding a match), JUMP to 4EA7H to handle the "file not found" condition by calling the error/retry handler with E=18H.
4EB8
LD A,01H 3E 01
Load Register A with 01H. This parameter tells the SYS0 routine at 48AFH to read the HIT sector (sector type 1).
4EBA
GOSUB to 48AFH in SYS0 to read the HIT sector into the system buffer. On return: Z FLAG set = success, HL points to the start of the HIT data in the buffer.
4EBD
RET NZ C0
If the NZ FLAG is set (HIT read failed), RETURN to caller with the error code in Register A.
4EBE
LD A,B 78
Load Register A with Register B (the current HIT offset being examined).
4EBF
LD B,A 47
[INNER LOOP START] Copy Register A into Register B (updating the current HIT offset).
4EC0
LD (DE),A 12
Store Register A (current HIT offset) at the address pointed to by DE (the directory working buffer). This records which HIT slot is being examined as a candidate.
4EC1
LD L,A 6F
Copy the HIT offset into Register L. Since H already contains the high byte of the HIT buffer address, HL now points to the specific HIT entry at this offset.
4EC2
LD A,E 7B
Load Register A with Register E (the low byte of the DE pointer into the candidate buffer).
4EC3
CP CCH FE CC
Compare Register A against CCH. This checks if DE has advanced to 51CCH, which is the end of the directory working buffer (buffer holds up to 31 candidate entries from 51ADH to 51CBH).
4EC5
If the Z FLAG is set (candidate buffer is full), JUMP to 4EDDH to process the accumulated candidate entries.
4EC7
LD A,(4D6EH) 3A 6E 4D
Load Register A with the byte at 4D6EH. This is the filename checksum/hash code computed from the target filename and stored as [SELF-MODIFYING CODE] at 4E9BH.
4ECA
CP (HL) BE
Compare Register A (target hash code) against the byte at HL (the current HIT entry). If they match, the Z FLAG is set (potential filename match).
4ECB
If the NZ FLAG is set (hash codes do not match), JUMP to 4ECEH to skip this entry and advance to the next HIT slot.
4ECD
INC DE 13
INCrement DE by 1. This advances the candidate buffer pointer, recording this HIT slot as a candidate match needing full filename verification.
4ECE
LD A,B 78
Load Register A with Register B (current HIT offset).
4ECF
ADD 20H C6 20
ADD 20H (decimal 32) to Register A, advancing to the next HIT entry. The HIT entries are spaced 32 bytes apart in the sector buffer.
4ED1
If the NO CARRY FLAG is set (no overflow past 255, more entries in this column), JUMP back to 4EBFH to check the next HIT entry.
4ED3
INC A 3C
INCrement Register A by 1. After the ADD 20H caused a carry (overflow past 255), this wraps to the next HIT column starting offset.
4ED4
CP C B9
Compare Register A against Register C (the ending HIT position limit, 1FH). Checks if we have scanned past the last valid HIT column.
4ED5
LD B,A 47
Copy the new HIT offset from Register A into Register B for the next iteration.
4ED6
If the CARRY FLAG is set (A < C, still valid HIT entries to check), JUMP back to 4EBFH to continue scanning. [INNER LOOP END]
4ED8
LD A,E 7B
Load Register A with Register E (low byte of DE, indicating how far the candidate buffer has been filled).
4ED9
CP ADH FE AD
Compare Register A against ADH (the starting low byte of the candidate buffer at 51ADH). If E still equals ADH, no candidate matches were found in this pass.
4EDB
If the Z FLAG is set (no candidates found), JUMP back to 4EB4H to check if more HIT entries remain. [OUTER LOOP continues]
One or more HIT entries had matching hash codes. The candidate HIT slot indices are stored in the buffer at 51ADH. Now read each candidate's directory sector and compare the full 11-byte filename to confirm a true match (hash collisions are possible with single-byte hash codes).
4EDD
DEC DE 1B
DECrement DE by 1. Back up to point to the last valid candidate entry in the buffer.
4EDE
LD A,(DE) 1A
Load Register A with the byte at DE. This reads the HIT slot index of the current candidate to verify.
4EDF
LD (4F56H),A 32 56 4F
Store the HIT slot index at address 4F56H. This [SELF-MODIFYING CODE] location preserves the slot index for use if this is the matching file.
4EE2
GOSUB to 48D4H in SYS0 to read the directory sector for this HIT slot. The routine converts the slot index to a track/sector address, reads the sector, and positions HL at the directory entry. On return: Z FLAG set = success, HL points to directory entry.
4EE5
RET NZ C0
If the NZ FLAG is set (directory sector read failed), RETURN to caller with the error code in Register A.
4EE6
PUSH DE D5
Save Register Pair DE (pointer into the candidate list) onto the stack.
4EE7
PUSH BC C5
Save Register Pair BC (HIT scan state) onto the stack.
4EE8
LD A,(HL) 7E
Load Register A with the byte at HL, the first byte of the directory entry (file status/flags byte).
4EE9
LD (4F24H),A 32 24 4F
Store the file status byte at address 4F24H. This [SELF-MODIFYING CODE] location overwrites the operand of the AND instruction at 4F23H, so a later AND 00H becomes AND (status_byte).
4EEC
AND 90H E6 90
Mask Register A with 90H (binary 10010000), isolating bits 7 and 4. Bit 7 = entry in use/deleted flag, Bit 4 = primary/overflow entry type.
4EEE
CP 10H FE 10
Compare Register A against 10H. A value of 10H (only bit 4 set) means a valid primary directory entry. Any other value (empty, deleted, or overflow) is not a primary match.
4EF0
If the NZ FLAG is set (not a valid primary entry), JUMP to 4F00H to skip this candidate and try the next one.
The directory entry is a valid primary file entry (status = 1xH with bit 4 set). Now compare the 11-byte filename in the directory entry against the parsed filename buffer at 51CDH.
4EF2
GOSUB to 4E1FH to extract directory parameters (track/sector numbers) from the directory entry and store them at 4F46H and 4F58H for potential disk I/O.
4EF5
LD DE,51CDH 11 CD 51
Point Register Pair DE to 51CDH, the parsed filename buffer containing the target filename.
4EF8
LD B,0BH 06 0B
Load Register B with 0BH (decimal 11), the number of bytes to compare (8 filename + 3 extension).
[LOOP START - Filename Comparison] Compare each byte of the directory entry filename against the parsed filename byte by byte.
4EFA
INC HL 23
INCrement HL by 1, advancing to the next byte in the directory entry filename.
4EFB
LD A,(DE) 1A
Load Register A with the next character from the parsed filename buffer.
4EFC
CP (HL) BE
Compare Register A (parsed filename character) against (HL) (directory entry character). If they match, the Z FLAG is set.
4EFD
INC DE 13
INCrement DE by 1, advancing the parsed filename pointer.
4EFE
If the Z FLAG is set (characters match), JUMP to 4F04H to continue the comparison loop.
4F00
POP BC C1
Restore Register Pair BC (HIT scan state) from the stack.
4F01
POP DE D1
Restore Register Pair DE (candidate list pointer) from the stack.
4F02
JUMP back to 4ED8H to try the next candidate. This character mismatch means this candidate was a hash collision, not a true filename match.
4F04
DECrement B and loop back to 4EFAH if not zero. [LOOP END - Filename Comparison] When B reaches zero, all 11 bytes matched.
[SUCCESS PATH] All 11 bytes of the filename match. The file has been found. Now extract the file's extent information from the directory entry and set up the FCB.
4F06
POP BC C1
Restore Register Pair BC from the stack (clean up saved HIT scan state).
4F07
POP DE D1
Restore Register Pair DE from the stack (clean up saved candidate list pointer).
4F08
INC HL 23
INCrement HL by 1, advancing past the last filename byte to the first extent data byte.
4F09
LD E,(HL) 5E
Load Register E with the first extent descriptor byte (extent start track or low byte of extent address).
4F0A
INC HL 23
INCrement HL by 1.
4F0B
LD D,(HL) 56
Load Register D with the second extent descriptor byte. DE now contains the first extent descriptor word.
4F0C
INC HL 23
INCrement HL by 1.
4F0D
LD C,(HL) 4E
Load Register C with the third extent field byte.
4F0E
INC HL 23
INCrement HL by 1.
4F0F
LD B,(HL) 46
Load Register B with the fourth extent field byte. BC contains the second extent descriptor word.
4F10
INC HL 23
INCrement HL by 1.
4F11
PUSH HL E5
Save Register Pair HL (pointer to the remaining directory entry data, including EOF fields) onto the stack for later use by the FCB init routine.
4F12
LD HL,(51D8H) 2A D8 51
Load Register Pair HL with the 16-bit hash value stored at 51D8H during the filename parse phase.
4F15
BIT 7,(IY+0CH) FD CB 0C 7E
Test bit 7 of the byte at IY+0CH (address 438CH). This system flag controls whether overflow directory chain validation is required (bit 7 set = validate overflow).
4F19
If the Z FLAG is set (bit 7 clear, no overflow validation needed), JUMP to 4F2FH to proceed directly to FCB initialization.
4F1B
OR A B7
OR Register A with itself (clear the CARRY FLAG) before the SBC instruction.
4F1C
SBC HL,DE ED 52
SUBtract DE (first extent descriptor) from HL (hash value) with borrow. Compares the hash against the extent to verify chain integrity.
4F1E
If the Z FLAG is set (hash equals extent start, primary entry confirmed), JUMP to 4F2FH for FCB initialization.
4F20
ADD HL,DE 19
ADD DE back to HL, restoring the original hash value (undoing the SBC).
4F21
LD A,07H 3E 07
Load Register A with 07H.
4F23
AND 00H E6 00
AND Register A with 00H. This is [SELF-MODIFYING CODE]: the operand at 4F24H was overwritten at 4EE9H with the file status byte. This actually ANDs 07H with the status byte, isolating the low 3 bits.
4F25
SBC HL,BC ED 42
SUBtract BC (second extent descriptor) from HL with borrow. Checks if the overflow chain descriptor matches.
4F27
If the Z FLAG is set (overflow chain matches), JUMP to 4F30H to proceed with FCB initialization via the overflow path.
4F29
POP HL E1
Restore Register Pair HL from the stack (directory entry pointer saved at 4F11H).
4F2A
LD A,19H 3E 19
Load Register A with 19H, an error code indicating overflow chain mismatch.
4F2C
OR A B7
OR Register A with itself, setting the NZ FLAG to indicate error.
4F2D
RET C9
RETURN to caller. Register A = 19H (overflow error), NZ FLAG set.
4F2EH - FCB Initialization Entry (from $INIT path)
This entry point is used when initializing a new file ($INIT at 4E04H). It pushes DE and clears A before falling through to the FCB initialization at 4F30H.
4F2E
PUSH DE D5
Save Register Pair DE onto the stack. For $INIT, DE contains extent allocation data for the new file.
4F2F
XOR A AF
Set Register A to ZERO and the Z FLAG. A=0 indicates a new file with no existing extent data to preserve.
4F30H - FCB Initialization: Set Up File Control Block
This routine initializes the FCB (File Control Block) for a newly opened or created file. IX points to the FCB base address. The routine clears the FCB, then fills in the status flags, drive info, extent descriptors, and EOF position.
4F30
PUSH IX DD E5
Save Register Pair IX (the FCB base address) onto the stack.
4F32
POP HL E1
Restore into HL. This copies IX into HL, so HL now points to the FCB base.
4F33
GOSUB to 5110H to clear the FCB buffer area. The routine zeroes out FCB fields and writes FFH sentinels at extent block boundaries. On return, HL points to the FCB start.
4F36
LD (HL),80H 36 80
Store 80H at FCB offset +0 (the FCB status byte). Bit 7 = 1 means the file is open/active.
4F38
INC HL 23
INCrement HL to FCB offset +1 (file mode/flags byte).
4F39
OR 28H F6 28
OR Register A with 28H (binary 00101000). This sets bits 5 and 3 in the mode byte, enabling read mode and buffer flag.
4F3B
LD (HL),A 77
Store the mode byte at FCB offset +1.
4F3C
LD A,(4F5EH) 3A 5E 4F
Load Register A with the mode flag stored at 4F5EH (set during $OPEN setup).
4F3F
OR A B7
OR A with itself, testing if the mode flag is zero.
4F40
If Z FLAG set (mode = 0, normal open), JUMP to 4F44H to skip the special mode bit.
4F42
SET 7,(HL) CB FE
Set bit 7 of FCB offset +1, enabling exclusive/locked access mode.
4F44
INC HL 23
INCrement HL to FCB offset +2.
4F45
LD (HL),00H 36 00
Store 00H at FCB offset +2 (additional flags, cleared).
4F47
LD DE,0000H 11 00 00
Load DE with 0000H for initializing position fields to zero.
4F4A
INC HL 23
Advance to FCB offset +3.
4F4B
LD (HL),E 73
Store 00H at FCB offset +3 (file position low byte).
4F4C
INC HL 23
Advance to FCB offset +4.
4F4D
LD (HL),D 72
Store 00H at FCB offset +4. Offsets +3/+4 = file position = 0000H (start of file).
4F4E
INC HL 23
Advance to FCB offset +5.
4F4F
INC HL 23
Advance to FCB offset +6.
4F50
LD A,(IY+FEH) FD 7E FE
Load Register A with the byte at 447EH (IY+FEH), the maximum drive number.
4F53
LD (HL),A 77
Store the drive number at FCB offset +6.
4F54
INC HL 23
Advance to FCB offset +7.
4F55
LD (HL),00H 36 00
Store 00H at FCB offset +7.
4F57
LD A,00H 3E 00
Load A with 00H. [SELF-MODIFYING CODE]: The operand at 4F58H is overwritten by 4E27H with the directory sector number.
4F59
INC HL 23
Advance to FCB offset +8.
4F5A
OR A B7
Test if A (sector number) is zero.
4F5B
LD (HL),A 77
Store sector number at FCB offset +8.
4F5C
INC HL 23
Advance to FCB offset +9.
4F5D
LD (HL),00H 36 00
Store 00H at FCB offset +9.
4F5F
INC HL 23
Advance to FCB offset +10.
4F60
INC HL 23
Advance to FCB offset +11.
4F61
INC HL 23
Advance to FCB offset +12.
4F62
POP DE D1
Restore DE from the stack. This retrieves the pointer to the EOF data from the directory entry.
4F63
LD A,(DE) 1A
Load A with the EOF sector count low byte from the directory entry.
4F64
INC DE 13
Advance DE to the next byte.
4F65
If Z FLAG set (EOF low = 0), JUMP to 4F69H to skip the decrement.
4F67
SUB 01H D6 01
SUBtract 1 from A, converting the 1-based EOF count to 0-based.
4F69
LD (HL),A 77
Store adjusted EOF low byte at FCB offset +12.
4F6A
INC HL 23
Advance to FCB offset +13.
4F6B
LD A,(DE) 1A
Load A with the EOF sector count high byte.
4F6C
SBC 00H DE 00
SUBtract 0 with borrow, propagating the borrow from the low byte SUB to complete the 16-bit decrement.
4F6E
LD (HL),A 77
Store the high byte of adjusted EOF count at FCB offset +13.
4F6F
INC DE 13
Advance DE to the next directory entry field.
4F70
INC HL 23
Advance to FCB offset +14.
4F71
LD A,2CH 3E 2C
Load A with 2CH, error code for directory full. Pre-loaded for the error check below.
4F73
RET C D8
If CARRY set (SBC at 4F6CH underflowed, indicating corrupted EOF data), RETURN with error 2CH.
4F74
GOSUB to 4F79H to copy the remaining 8 bytes of extent data from the directory entry into the FCB.
4F77
XOR A AF
Set A to ZERO and Z FLAG. Indicates success (no error).
4F78
RET C9
RETURN to caller. A = 00H (success), Z FLAG set.
4F79H - Copy 8-Byte Extent Data Block
Utility routine that copies 8 bytes from the source at DE to the destination at HL. Used to transfer extent descriptor data between directory entries and FCBs.
4F79
EX DE,HL EB
Exchange DE and HL. HL becomes the source, DE becomes the destination.
4F7A
LD A,08H 3E 08
Load A with 08H (8 bytes to copy).
4F7C
LD C,A 4F
Copy byte count into C.
4F7D
LD B,00H 06 00
Set B to 00H. BC = 0008H.
4F7F
LDIR ED B0
Block copy 8 bytes from HL to DE. After execution, both pointers advance past the copied data.
4F81
RET C9
RETURN to caller.
4F82H - Function 64H: $POSN (File Position) Handler
This routine handles function code 64H ($POSN), which repositions a file's read/write pointer to a specified location within the file. It verifies the file is open with the correct access mode, then walks the file's extent chain to locate the disk sector corresponding to the requested position. On entry: IX points to the FCB, DE points to the user parameter block containing the new position.
4F82
LD A,3DH 3E 3D
Load Register A with 3DH. This is the NEWDOS/80 error code for file not open. Pre-loaded in case the access check fails.
4F84
BIT 7,(IX+02H) DD CB 02 7E
Test bit 7 of the byte at IX+02H (FCB offset +2). Bit 7 of this field indicates whether the file is open and active. If bit 7 is clear, the file is not open.
4F88
If the Z FLAG is set (bit 7 is clear, file is not open), GOSUB to 471BH in SYS0. This routine attempts to validate or reopen the file. On return: Z FLAG set = file now accessible, NZ FLAG = cannot access.
4F8B
If the NZ FLAG is set (file is not accessible), JUMP to 4FFBH to return the error code in Register A to the caller via the error exit path.
4F8D
LD A,(480FH) 3A 0F 48
Load Register A with the byte at 480FH. This address contains the current active drive number for the file being positioned.
4F90
LD (505EH),A 32 5E 50
Store the drive number at address 505EH. This [SELF-MODIFYING CODE] location preserves the drive number for later use by the extent allocation/deallocation routines.
4F93
PUSH AF F5
Save Register Pair AF (drive number in A) onto the stack.
4F94
GOSUB to 48DBH in SYS0 to set up the directory context for this file. This reads the directory sector for the file and positions HL at the directory entry.
4F97
GOSUB to 5036H to perform initial validation. This checks the directory entry status and ensures the file has not been corrupted or deleted since it was opened.
4F9A
INC DE 13
INCrement DE by 1, advancing past the first byte of the user parameter block to point to the position data.
4F9B
PUSH DE D5
Save Register Pair DE (pointer to the position data) onto the stack.
4F9C
GOSUB to 50B4H to load the extent table for this file into the working buffer. The extent table describes which disk tracks and granules contain the file's data.
The extent table is now loaded. The code will walk through the extents to find which granule contains the target position. Register B is loaded with the sectors-per-granule count, C starts at 01H as a bitmask for tracking granule boundaries, and E holds the first extent byte.
4F9F
LD B,(IY+05H) FD 46 05
Load Register B with the byte at IY+05H (address 4385H). This is the sectors per granule value for the current drive, read from the PDRIVE table.
4FA2
LD C,01H 0E 01
Load Register C with 01H. This initializes the granule bitmask used to track which granule within a track is being processed.
4FA4
LD E,(HL) 5E
Load Register E with the byte at HL. This is the first byte of the current extent entry (the track number or extent terminator).
4FA5
INC E 1C
INCrement Register E by 1. If E was FFH (extent terminator), it wraps to 00H and the Z FLAG is set.
4FA6
If the Z FLAG is set (extent entry was FFH = end of extent list), JUMP to 4FC5H. The requested position is beyond the end of the file's extent chain.
4FA8
DEC E 1D
DECrement Register E by 1, restoring the original track number.
4FA9
DEC E 1D
DECrement Register E by 1 again. If E was FEH (end-of-file marker), it becomes FCH. If E was a track number, it is now track-2. This second DEC tests for the FEH terminator.
4FAA
INC HL 23
INCrement HL by 1, advancing to the second byte of the extent entry.
4FAB
LD A,(HL) 7E
Load Register A with the second byte of the extent entry. This byte contains the granule count (bits 0-4) and the starting granule offset within the track (bits 5-7).
4FAC
AND 1FH E6 1F
Mask Register A with 1FH (binary 00011111), isolating bits 0-4: the number of contiguous granules in this extent.
4FAE
LD D,A 57
Copy the granule count into Register D.
4FAF
INC D 14
INCrement Register D by 1. The stored count is 0-based, so add 1 to get the actual number of granules.
4FB0
LD A,(HL) 7E
Reload Register A with the same second extent byte.
4FB1
AND E0H E6 E0
Mask Register A with E0H (binary 11100000), isolating bits 5-7: the starting granule position within the track (encoded in the high 3 bits).
4FB3
DEC HL 2B
DECrement HL by 1, moving back to the first byte of the extent entry (track number).
4FB4
RLCA 07
Rotate Register A Left. First of 3 rotations to shift the starting granule from bits 5-7 to bits 0-2.
4FB5
RLCA 07
Rotate Register A Left. Second rotation.
4FB6
RLCA 07
Rotate Register A Left. Third rotation. A now contains the starting granule offset (0-7) within the track.
4FB7
ADD A,D 82
ADD the granule count (D) to the starting position (A). This computes the total number of sectors covered by this extent (starting_gran + count = ending position).
4FB8
INC E 1C
[LOOP START - Sector Counting] INCrement Register E by 1. E is being used as a counter.
4FB9
SUB B 90
SUBtract Register B (sectors per granule) from Register A. This consumes one granule's worth of sectors from the remaining count.
4FBA
If the NO CARRY FLAG is set (there are still sectors remaining in this extent), JUMP back to 4FB8H to count the next granule.
4FBC
ADD A,B 80
ADD Register B back to A, restoring the remainder (number of sectors within the last partial granule). [LOOP END]
4FBD
If the Z FLAG is set (A=0, meaning the extent is exactly consumed with no remainder), JUMP to 4FC5H to move to the next extent.
4FBF
RLC C CB 01
Rotate Register C Left through Carry. This shifts the granule bitmask left by 1 bit, moving to the next granule position within the current track.
4FC1
DEC B 05
DECrement Register B (sectors per granule counter) by 1.
4FC2
DEC A 3D
DECrement Register A (remaining sector count) by 1.
4FC3
If the NZ FLAG is set (more sectors remain), JUMP back to 4FBFH to continue shifting the granule bitmask.
4FC5H - Position Traversal: Walk Extent Chain
This section manages the traversal of the file's extent chain during positioning. It reads sectors from the file, tracks the current position through the extent table, and handles transitions between extents and tracks. The code also manages granule allocation when the position extends beyond the current end of file (for write operations).
4FC5
PUSH HL E5
Save Register Pair HL (pointer to the current extent entry) onto the stack.
4FC6
XOR A AF
Set Register A to ZERO. This parameter tells the 48AFH routine to read the GAT sector (sector type 0).
4FC7
GOSUB to 48AFH in SYS0 to read the GAT (Granule Allocation Table) sector into the buffer. On return: Z FLAG set = success, HL points to the GAT data.
4FCA
If the NZ FLAG is set (GAT read failed), JUMP to 4FFBH to handle the error.
4FCC
LD L,E 6B
Copy Register E (the track number from the extent entry) into Register L. Since H holds the high byte of the GAT buffer, HL now addresses the GAT entry for this track.
4FCD
POP DE D1
Restore Register Pair DE from the stack (the extent entry pointer that was saved at 4FC5H).
4FCE
LD A,01H 3E 01
Load Register A with 01H. This initializes a counter or flag for the position tracking loop.
4FD0
EX AF,AF' 08
Exchange Register A with the alternate A register (AF'). This saves the counter in the shadow register for later retrieval.
4FD1
JUMP to 4FE5H to begin the position scanning loop.
4FD3
LD A,(HL) 7E
[LOOP ENTRY - Sector Scan] Load Register A with the byte at HL (the GAT entry for the current track). The GAT byte contains a bitmask showing which granules on this track are allocated.
4FD4
AND C A1
AND Register A with Register C (the granule bitmask). This tests if the specific granule at position C is allocated on this track.
4FD5
LD A,(DE) 1A
Load Register A with the byte at DE (a value from the extent data or position parameter).
4FD6
If the Z FLAG is set (the granule is NOT allocated in the GAT, meaning a free granule), JUMP to 4FFDH to handle the unallocated sector condition.
4FD8
INC A 3C
INCrement Register A by 1. If A was FFH (extent terminator), it wraps to 00H and Z FLAG is set.
4FD9
If the NZ FLAG is set (not at end of extent list), JUMP to 504CH to continue processing the next extent entry.
4FDB
RLC C CB 01
Rotate Register C Left through Carry. Shift the granule bitmask to the next position.
4FDD
DECrement B and loop back to 4FD3H if not zero. B counts the sectors per granule being traversed.
4FDF
INC L 2C
INCrement Register L by 1, advancing to the next track's GAT entry.
4FE0
LD B,(IY+05H) FD 46 05
Reload Register B with the sectors per granule from IY+05H (address 4385H).
4FE3
LD C,01H 0E 01
Reset Register C to 01H, restarting the granule bitmask at the first granule of the new track.
4FE5
LD A,L 7D
Load Register A with Register L (the current track number in the GAT).
4FE6
CP (IY+01H) FD BE 01
Compare Register A against the byte at IY+01H (address 4381H), which is the maximum track number for this drive.
4FE9
If the CARRY FLAG is set (current track < maximum track), JUMP back to 4FD3H to continue scanning the GAT.
4FEB
EX AF,AF' 08
Exchange A with the alternate A register, retrieving the saved counter.
4FEC
DEC A 3D
DECrement Register A by 1. If this is the second pass (A was 1, now 0), the Z FLAG is set.
4FED
LD L,A 6F
Copy Register A into Register L.
4FEE
If the Z FLAG is set (second pass starting), JUMP back to 4FD0H to restart the scan from track 0. This implements a wrap-around search of the entire disk.
4FF0
BIT 0,(IY+0BH) FD CB 0B 46
Test bit 0 of the byte at IY+0BH (address 438BH). This flag indicates whether the extent chain needs to be finalized.
4FF4
If bit 0 is set (finalization needed), JUMP to 502AH to finalize the extent chain and return.
4FF6
GOSUB to 508CH to save the current extent data back to the directory entry.
4FF9
LD A,1BH 3E 1B
Load Register A with 1BH, the error code for disk full (no free granules available).
4FFB
JUMP to 503DH to invoke the error exit routine with the error code in Register A.
4FFDH - Handle Unallocated Granule During Position
When the GAT scan finds an unallocated granule during file positioning, this code determines whether to allocate a new granule (for write/extend operations) or to continue scanning. The code manages extent chain building and sector allocation.
4FFD
INC A 3C
INCrement Register A by 1. A was loaded from (DE), the extent data byte. If it was FFH (terminator), it wraps to 00H and Z FLAG is set.
4FFE
If the NZ FLAG is set (not an extent terminator), JUMP to 5043H to handle the mid-extent case.
5000
LD A,L 7D
Load Register A with Register L (the current track number from the GAT scan).
5001
LD (DE),A 12
Store the track number at the address pointed to by DE. This writes the track number into the extent entry, beginning a new extent for the allocated granule.
5002
INC DE 13
INCrement DE by 1, advancing to the second byte of the extent entry.
5003
LD A,(IY+05H) FD 7E 05
Load Register A with the byte at IY+05H (address 4385H), the sectors per granule value.
5006
SUB B 90
SUBtract Register B (remaining sectors in current granule) from Register A. This computes the granule offset (how many sectors into the granule we are).
5007
RRCA 0F
Rotate Register A Right. First of 3 rotations to shift the granule offset from bits 0-2 into bits 5-7 of the extent byte.
5008
RRCA 0F
Rotate Right. Second rotation.
5009
RRCA 0F
Rotate Right. Third rotation. A now has the starting granule position in bits 5-7.
500A
DEC A 3D
DECrement Register A by 1. This adjusts the granule count to be 0-based (the extent stores count-1).
500B
INC A 3C
INCrement Register A by 1. Combined with the DEC, this effectively tests for zero without changing the value for the following store. If the count was 0, it becomes 1 (minimum extent size).
500C
LD (DE),A 12
Store the packed granule info byte (starting position in bits 5-7, count in bits 0-4) at the second byte of the extent entry.
500D
DEC DE 1B
DECrement DE by 1, moving back to the first byte of the extent entry.
500E
LD A,(HL) 7E
Load Register A with the byte at HL (the GAT entry for this track).
500F
OR C B1
OR Register A with Register C (the granule bitmask). This marks the granule as allocated in the GAT by setting the corresponding bit.
5010
LD (HL),A 77
Store the updated GAT byte back to the buffer. The granule is now marked as allocated.
5011
EX (SP),HL E3
Exchange HL with the top of the stack. This retrieves the position counter from the stack while saving the GAT pointer.
5012
DEC HL 2B
DECrement HL by 1. This decrements the remaining sectors to position counter.
5013
LD A,H 7C
Load Register A with Register H (high byte of remaining sector count).
5014
OR L B5
OR Register A with Register L (low byte). Tests if HL (remaining count) has reached zero.
5015
EX (SP),HL E3
Exchange HL with the top of stack again, restoring the GAT pointer and saving the updated counter.
5016
If the NZ FLAG is set (more sectors to position through), JUMP back to 4FDBH to continue the extent traversal loop.
The positioning is complete (remaining sector count reached zero). Now check whether the extent chain needs finalization.
5018
LD A,(IY+0BH) FD 7E 0B
Load Register A with the byte at IY+0BH (address 438BH), a system status flag.
501B
AND 03H E6 03
Mask with 03H, isolating bits 0 and 1. These bits indicate the extent chain state.
501D
If NZ (either bit is set), JUMP to 502AH to finalize the extent chain.
501F
EX (SP),HL E3
Exchange HL with top of stack.
5020
INC HL 23
INCrement HL by 1.
5021
INC HL 23
INCrement HL by 1.
5022
INC HL 23
INCrement HL by 1. Advance past 3 bytes in the extent/position data.
5023
EX (SP),HL E3
Exchange HL with top of stack again.
5024
SET 0,(IY+0BH) FD CB 0B C6
Set bit 0 of the byte at IY+0BH (438BH). This marks the extent chain as modified, requiring it to be written back to disk.
5028
JUMP back to 4FDBH to continue the extent traversal.
502AH - Finalize Extent Chain and Return
This routine finalizes the extent chain after positioning is complete or when the extent has been modified. It writes the updated extent data and GAT sector back to disk, then performs cleanup and returns to the caller.
502A
RES 0,(IY+0BH) FD CB 0B 86
Reset (clear) bit 0 of the byte at IY+0BH (438BH). This clears the extent chain modified flag now that the chain is being written out.
502E
GOSUB to 508CH to save the current extent data from the working buffer back to the directory sector.
5031
POP AF F1
Restore Register Pair AF from the stack (cleaning up the position counter).
5032
POP AF F1
Restore another AF from the stack (cleaning up the drive number saved at 4F93H).
5033
GOSUB to 48D4H in SYS0 to write the updated directory sector back to disk.
5036
If the NZ FLAG is set (directory write failed), JUMP to 503DH to handle the error.
5038
BIT 4,(HL) CB 66
Test bit 4 of the byte at HL (the directory entry status byte). Bit 4 indicates a valid primary directory entry.
503A
RET NZ C0
If the NZ FLAG is set (bit 4 is set, valid entry confirmed), RETURN to caller with success.
503B
LD A,2CH 3E 2C
Load Register A with 2CH, error code for directory full / corrupt entry.
503D
GOSUB to 4BC5H in SYS0 to process the error. This routine sets up the error return context.
5040
JUMP to 4972H in SYS0 to complete the error exit and return to the original caller with the error code.
5043H - Mid-Extent Position Continuation
When the position scan encounters a non-terminator extent byte during granule allocation, this code continues processing the extent chain, handling transitions between extent entries and tracks.
5043
INC DE 13
INCrement DE by 1, advancing to the next byte in the extent data.
5044
LD A,(DE) 1A
Load Register A with the next extent byte. This is the second byte of the extent pair (packed granule info).
5045
INC A 3C
INCrement A by 1. Tests for FFH terminator.
5046
AND 1FH E6 1F
Mask with 1FH, isolating the granule count (bits 0-4) after the increment.
5048
LD A,(DE) 1A
Reload the original extent byte (without the increment).
5049
If NZ (granule count is not at maximum), JUMP to 500BH to increment the count in the current extent entry.
504B
DEC DE 1B
DECrement DE, moving back to the first extent byte.
504C
INC DE 13
INCrement DE. These two instructions (DEC then INC) are a no-op on DE but clear certain flags. Actually advances DE past the pair.
504D
INC DE 13
INCrement DE again, advancing to the next extent pair.
504E
LD A,(DE) 1A
Load the first byte of the next extent entry.
504F
INC A 3C
INCrement A. Test for FFH terminator (becomes 00H).
5050
If Z (next extent is FFH terminator = end of chain), JUMP to 4FD3H to continue scanning the GAT.
5052
BIT 0,(IY+0BH) FD CB 0B 46
Test bit 0 of IY+0BH (438BH), the extent modified flag.
5056
If set (extent modified), JUMP to 502AH to finalize.
5058
PUSH HL E5
Save HL onto the stack.
5059
PUSH BC C5
Save BC onto the stack.
505A
GOSUB to 508CH to save current extent data.
505D
LD B,00H 06 00
Load B with 00H.
505F
LD L,B 68
Copy B (00H) into L. L=0 as starting offset for search.
5061
GOSUB to 50CFH to search for a free directory slot.
5064
If NZ (no free slot), JUMP to 503DH to return error.
5066
LD C,A 4F
Copy the slot index into C.
5067
LD (HL),90H 36 90
Store 90H at the HIT slot. This marks the entry as an overflow directory entry (bits 7 and 4 set).
506A
POP DE D1
Restore DE from stack.
506B
LD (HL),D 72
Store D at the overflow entry.
506C
GOSUB to 50AEH to write the directory sector and handle errors.
506F
LD A,D 7A
Load A with D.
5070
GOSUB to 5033H to write the directory sector.
5073
ADD 1FH C6 1F
ADD 1FH to A, computing an offset into the directory buffer.
5075
LD L,A 6F
Set L to the computed offset.
5076
LD (HL),C 71
Store C (the overflow slot index) at this position.
5077
DEC HL 2B
DECrement HL.
5078
LD (HL),FEH 36 FE
Store FEH. The value FEH marks the end of the primary extent list, indicating the file continues in an overflow entry.
507A
GOSUB to 50AEH to write the updated directory sector.
507D
LD A,C 79
Load A with C (overflow slot index).
507E
LD (505EH),A 32 5E 50
Store at 505EH. [SELF-MODIFYING CODE] updates the current directory slot reference.
5081
GOSUB to 5033H to validate/write the directory sector.
5084
GOSUB to 50B4H to reload the extent table for the overflow entry.
5088
POP DE D1
Restore DE (was HL before, now swapped).
5089
JUMP back to 4FC5H to continue the position traversal with the new overflow extent chain.
508CH - Save Current Extent Data to Directory
Utility routine that copies extent data from the working buffer (51ADH) back to the directory sector buffer, then writes the sector to disk.
508C
GOSUB to 50AEH to write the current directory sector to disk.
508F
LD A,(505EH) 3A 5E 50
Load A with the byte at 505EH, the [SELF-MODIFYING CODE] location containing the current directory slot number.
5092
GOSUB to 5033H to validate and write the directory sector.
5095
ADD 16H C6 16
ADD 16H (decimal 22) to A, computing the offset to the extent data area within the directory entry (extent data starts at offset +22).
5097
BIT 7,(HL) CB 7E
Test bit 7 of the byte at HL. This checks the directory entry status.
5099
LD L,A 6F
Set L to the computed extent offset. HL now points to the extent data area within the directory buffer.
509A
PUSH HL E5
Save HL (extent data destination).
509B
PUSH IX DD E5
Save IX (FCB base).
509D
POP HL E1
Copy IX into HL. HL now points to the FCB.
509E
LD BC,000EH 01 0E 00
Load BC with 000EH (decimal 14). This is the offset to the extent data within the FCB structure.
50A1
ADD HL,BC 09
ADD 14 to HL, advancing to FCB+14 where the extent data begins.
50A2
LD DE,51ADH 11 AD 51
Point DE to 51ADH, the extent working buffer.
50A6
If Z FLAG set, GOSUB to 4F79H to copy 8 bytes from the FCB extent area to the working buffer.
50A9
POP HL E1
Restore HL (51ADH, extent working buffer).
50AA
POP DE D1
Restore DE (extent data destination in directory buffer).
50AB
GOSUB to 4F7AH to copy 8 bytes from the working buffer to the directory sector buffer.
50AEH - Write Directory Sector with Error Check
Writes the current directory sector to disk and checks for errors. On error, jumps to the error exit at 503DH.
50AE
GOSUB to 48C4H in SYS0 to write the directory sector to disk.
50B1
RET Z C8
If Z FLAG set (write succeeded), RETURN to caller.
50B2
If NZ (write failed), JUMP to 503DH to handle the error.
50B4H - Load Extent Table from Directory Entry
Reads the extent table from the current directory entry into the working buffer at 51ADH. Also scans for empty extent entries to determine the end of the extent chain.
50B4
ADD 16H C6 16
ADD 16H (22) to A, computing the offset to the extent data within the directory entry.
50B6
LD L,A 6F
Set L to the offset. HL points to the extent data in the directory buffer.
50B7
LD DE,51ADH 11 AD 51
Point DE to the extent working buffer at 51ADH.
50BA
GOSUB to 4F7AH to copy 8 bytes of extent data from the directory buffer to the working buffer.
50BD
EX DE,HL EB
Exchange DE and HL. HL now points to the working buffer after the copy.
50BE
LD (HL),FEH 36 FE
Store FEH as a sentinel/terminator after the extent data in the working buffer.
50C0
RRCA 0F
Rotate A Right.
50C1
LD B,A 47
Copy A into B as a loop counter.
50C2
DEC HL 2B
[LOOP START] DECrement HL, moving backwards through the extent data.
50C3
DEC HL 2B
DECrement HL again (each extent entry is 2 bytes).
50C4
LD A,(HL) 7E
Load A with the first byte of the current extent entry.
50C5
INC A 3C
INCrement A. Tests for FFH terminator.
50C6
RET NZ C0
If NZ (not an FFH terminator, so a valid extent entry), RETURN. HL points to the last valid extent entry.
50C7
DECrement B and loop back if not zero. [LOOP END]
50C9
RET C9
RETURN. All extent entries are FFH (empty file, no extents).
50CAH - E4H Sub-Function 2: Read Directory Sector
Handles function code E4H with sub-function C=2. Reads a specific directory sector. Register D contains the directory-relative sector number to read.
50CA
LD A,D 7A
Load Register A with Register D, the directory-relative sector number to read.
50CB
GOSUB to 4723H in SYS0 to read the specified directory sector into the system buffer.
50CE
RET NZ C0
If NZ (read failed), RETURN with error.
50CFH - Search HIT for Free Slot
Searches the HIT sector for a free (empty) directory slot. Reads the HIT, then scans through the 256-byte sector buffer looking for zero entries (indicating unoccupied directory slots).
50CF
EX DE,HL EB
Exchange DE and HL.
50D0
LD A,01H 3E 01
Load A with 01H to request a HIT sector read.
50D2
GOSUB to 48AFH in SYS0 to read the HIT sector. On return: HL points to HIT buffer.
50D5
RET NZ C0
If NZ (read failed), RETURN with error.
50D6
LD A,(431FH) 3A 1F 43
Load A with the byte at 431FH. This contains a directory configuration value that determines the starting offset for the HIT scan.
50D9
ADD 08H C6 08
ADD 08H to A, computing the upper limit of the HIT scan range.
50DB
LD C,A 4F
Copy the limit into C.
50DC
LD A,B 78
Load A with B (the starting offset for the scan).
50DD
AND 1FH E6 1F
Mask with 1FH, isolating the low 5 bits of the starting offset.
50DF
SUB C 91
SUBtract C (upper limit) from A. If A < C, the CARRY FLAG is set.
50E0
If NO CARRY (A >= C), loop back to 50DFH. This is a modular reduction loop that brings A into the valid range below C.
50E2
ADD A,C 81
ADD C back to A, restoring the reduced offset.
50E3
LD B,A 47
Copy the starting offset into B.
50E4
LD L,A 6F
Set L to the offset. HL points to the HIT entry at this position.
50E5
JUMP to 50F2H to start the scan.
50E7
LD A,(HL) 7E
[LOOP START - HIT Free Slot Search] Load A with the HIT entry at HL.
50E8
OR A B7
Test if A is zero. A zero HIT entry means the directory slot is free.
50E9
If Z (entry is zero = free slot found), JUMP to 50FEH to process the free slot.
50EB
LD A,L 7D
Load A with L (current HIT offset).
50EC
ADD 20H C6 20
ADD 20H (32) to advance to the next row in the HIT table.
50EE
LD L,A 6F
Update L with the new offset.
50EF
If NO CARRY (no overflow), JUMP back to 50E7H to check the next entry in this column.
50F1
INC L 2C
INCrement L, wrapping to the next column in the HIT table.
50F2
LD A,L 7D
Load A with the current offset.
50F3
CP C B9
Compare A against C (upper limit).
50F4
If CARRY (A < C, still in range), JUMP to 50E7H to continue scanning.
50F6
XOR A AF
Set A to zero. Reset the offset to start of HIT.
50F7
INC B 04
INCrement B (pass counter).
50F8
DEC B 05
DECrement B. Tests if B was zero (meaning we have made a complete pass through the HIT without finding a free slot). If B=0, Z FLAG set.
50F9
If NZ (more passes to make), JUMP to 50E3H to restart with a new offset. [LOOP END]
50FB
OR 1AH F6 1A
OR A with 1AH (decimal 26). This sets A to 1AH (error code for directory full) and sets the NZ FLAG.
50FD
RET C9
RETURN. A = 1AH (directory full), NZ FLAG set (error).
[SUCCESS PATH] A free HIT slot was found at the current HL position.
50FE
LD A,(DE) 1A
Load A with the byte at DE (the hash code to store in the free slot).
50FF
LD (HL),A 77
Store the hash code at the free HIT slot, claiming this directory entry.
5100
LD C,L 4D
Copy L (the HIT offset of the claimed slot) into C for the caller.
5101
GOSUB to 48C4H in SYS0 to write the modified HIT sector back to disk.
5104
RET NZ C0
If NZ (write failed), RETURN with error.
5105
LD A,C 79
Load A with C (the claimed HIT offset).
5106
GOSUB to 48DBH in SYS0 to read the directory sector for the claimed slot.
5109
RET NZ C0
If NZ (read failed), RETURN with error.
510A
BIT 4,(HL) CB 66
Test bit 4 of the directory entry status byte. If bit 4 is set, this slot is already occupied by a file entry (should not happen if HIT said it was free).
510C
LD A,2CH 3E 2C
Load A with 2CH (directory full/corrupt error code).
510E
RET NZ C0
If NZ (bit 4 set, slot occupied), RETURN with error 2CH.
510F
LD A,C 79
Load A with C (the HIT offset). Falls through to 5110H to initialize the directory entry.
5110H - Clear/Initialize Directory Entry or FCB Buffer
This utility routine clears a block of memory used for directory entries or FCB buffers. It writes 00H bytes for the main data area (22 bytes per extent block) and FFH sentinel bytes at the boundaries. This creates the empty extent structure needed for a new file.
5110
LD BC,0A16H 01 16 0A
Load Register Pair BC with 0A16H. B=0AH (decimal 10) is the number of extent blocks to initialize, and C=16H (decimal 22) is the number of bytes per block to zero.
5113
PUSH HL E5
Save HL (start of area to clear) onto the stack for later restoration.
5114
LD (HL),00H 36 00
[INNER LOOP START] Store 00H at the current position, clearing this byte.
5116
INC HL 23
INCrement HL to the next byte.
5117
DEC C 0D
DECrement C (bytes remaining in this block).
5118
If NZ (more bytes to clear), loop back. [INNER LOOP END]
511A
LD (HL),FFH 36 FF
[OUTER LOOP] Store FFH as a sentinel/terminator at the end of this extent block. FFH marks the boundary between extent entries.
511C
INC HL 23
INCrement HL past the sentinel.
511D
DECrement B and loop back if not zero. [OUTER LOOP END] After 10 blocks, the entire area is initialized.
511F
POP HL E1
Restore HL (start of the cleared area) from the stack.
5120
RET C9
RETURN. The memory block has been cleared with 00H data and FFH sentinels.
5121H - Extract Filename Characters from Filespec String
This routine extracts up to 8 characters from the filespec string pointed to by DE, converting them to uppercase and storing them in the buffer pointed to by HL. If fewer than 8 characters are provided (terminated by a delimiter), the remaining positions are padded with spaces. On return, A contains the delimiter character that terminated the extraction.
5121
LD B,08H 06 08
Load Register B with 08H (decimal 8), the maximum number of characters to extract for a filename (or 3 if called from 5123H for extension).
5123
If the NZ FLAG is set (the delimiter from the previous call was not a matching separator), JUMP to 5140H to pad the remaining positions with spaces. This handles the case where the filespec had no extension or no password.
5125
GOSUB to 5146H to fetch and validate the next character from the filespec string. On return: A = the character (uppercase), CARRY set if not alphabetic.
5128
If CARRY set (first character is not alphabetic, meaning the filespec starts with a non-letter), JUMP to 513BH to handle the error/empty filename case.
512A
LD (HL),A 77
[LOOP START - Character Store] Store the validated character at the current buffer position.
512B
INC HL 23
INCrement HL to the next buffer position.
512C
GOSUB to 5146H to fetch the next character.
512F
If NO CARRY (character is alphabetic), JUMP to 5139H to check if it is also alphanumeric and continue the loop.
5131
CP 30H FE 30
Compare A against 30H (ASCII 0). Tests if the character is a digit.
5133
If CARRY (A < 30H, not a digit or letter), JUMP to 5143H. The character is a delimiter.
5135
CP 3AH FE 3A
Compare A against 3AH (ASCII :, one past 9). If A < 3AH, it is a digit (30H-39H).
5137
If NO CARRY (A >= 3AH, not a digit), JUMP to 5143H. The character is a delimiter (colon or other).
5139
DECrement B and loop back to 512AH if not zero. Continue storing characters until the maximum count is reached. [LOOP END]
513B
POP AF F1
Restore AF from the stack (discard a saved return address or state).
513C
LD A,30H 3E 30
Load A with 30H. This is a status/error return value indicating the filename extraction was incomplete or failed.
513E
OR A B7
OR A with itself, setting NZ FLAG (A=30H is non-zero).
513F
RET C9
RETURN. A=30H, NZ FLAG set.
5140
LD (HL),20H 36 20
[PADDING LOOP] Store 20H (ASCII space) at the current buffer position. This pads the remaining filename/extension positions with spaces.
5142
INC HL 23
INCrement HL to the next buffer position.
5143
DECrement B and loop back to 5140H if not zero. [PADDING LOOP END]
5145
RET C9
RETURN. The buffer contains the extracted characters padded with spaces. A contains the delimiter character.
5146H - Fetch and Validate Next Character from Filespec
Fetches the next character from the filespec string at DE, converts it to uppercase via the ROM routine at 4548H, and tests whether it is an alphabetic letter (A-Z). Returns with CARRY set if not alphabetic.
5146
INC DE 13
INCrement DE by 1, advancing to the next character in the filespec string.
5147
LD A,(DE) 1A
Load Register A with the character at the current DE position.
5148
GOSUB to 4548H in SYS0 to convert the character to uppercase. If A contains a lowercase letter (61H-7AH), it is converted to the corresponding uppercase letter (41H-5AH).
514B
CP 41H FE 41
Compare A against 41H (ASCII A). If A < 41H, the CARRY FLAG is set (not an uppercase letter).
514D
RET C D8
If CARRY set (character < 'A', not alphabetic), RETURN with CARRY set.
514E
CP 5BH FE 5B
Compare A against 5BH (one past ASCII Z). If A < 5BH, the CARRY FLAG is set (character is A-Z).
5150
CCF 3F
Complement the Carry Flag. If the character was A-Z (CARRY was set), now CARRY is clear (NO CARRY = valid letter). If the character was > Z (CARRY was clear), now CARRY is set (not a letter).
5151
RET C9
RETURN. A = uppercase character. CARRY clear = alphabetic letter (A-Z). CARRY set = not alphabetic.
5152H - Compute Filename Hash Code (Entry with HL=51DFH)
Entry point that sets HL to 51DFH before falling through to the main hash computation at 5155H. Used when the hash workspace needs to be at the default location.
5152
LD HL,51DFH 21 DF 51
Point HL to 51DFH, the hash computation workspace.
5155H - Function 84H: Compute Filename Hash Code
This routine computes a 16-bit hash code from the 8-byte workspace buffer. The algorithm uses a series of XOR, rotate, and shift operations to produce a well-distributed hash value. The hash is used for quick directory lookups via the HIT (Hash Index Table). On entry: HL points to the end of the 8-byte workspace buffer. On return: HL contains the computed hash value.
5155
PUSH DE D5
Save Register Pair DE onto the stack.
5156
PUSH BC C5
Save Register Pair BC onto the stack.
5157
LD DE,FFFFH 11 FF FF
Load DE with FFFFH. This initializes the hash accumulator to all 1s (the starting seed for the hash computation).
515A
LD B,08H 06 08
Load B with 08H (decimal 8), the number of bytes to process in the hash computation.
[LOOP START - Hash Computation] For each of the 8 workspace bytes, the algorithm performs a series of XOR, rotate, and masking operations to fold the byte into the 16-bit hash accumulator in DE. The algorithm is a polynomial hash with good distribution properties.
515C
PUSH BC C5
Save the loop counter.
515D
LD A,E 7B
Load A with E (low byte of hash accumulator).
515E
AND 07H E6 07
Mask with 07H, isolating the low 3 bits of E.
5160
LD C,A 4F
Save the low 3 bits in C.
5161
LD A,E 7B
Reload A with E.
5162
RLCA 07
Rotate A Left. First rotation.
5163
RLCA 07
Rotate Left. Second.
5164
RLCA 07
Rotate Left. Third. A now has bits shifted left by 3 positions.
5165
XOR C A9
XOR A with C (the saved low 3 bits). This folds the low bits back into the shifted value.
5166
RLCA 07
Rotate Left again. Fourth rotation.
5167
LD C,A 4F
Save the intermediate result in C.
5168
AND F0H E6 F0
Mask with F0H, keeping only the high 4 bits.
516A
LD B,A 47
Save the high nibble in B.
516B
LD A,C 79
Reload the full intermediate value from C.
516C
RLCA 07
Rotate Left. Fifth rotation.
516D
AND 1FH E6 1F
Mask with 1FH, keeping the low 5 bits.
516F
XOR B A8
XOR with B (the saved high nibble). Combines high and low portions.
5170
XOR D AA
XOR with D (high byte of hash accumulator). Folds the computed value into the hash.
5171
LD E,A 5F
Store the result as the new low byte of the hash accumulator (E).
5172
LD A,C 79
Reload the intermediate value from C.
5173
AND 0FH E6 0F
Mask with 0FH, keeping the low nibble.
5175
LD B,A 47
Save the low nibble in B.
5176
LD A,C 79
Reload the intermediate value again.
5177
RLCA 07
Rotate Left. First of 4 rotations.
5178
RLCA 07
Second rotation.
5179
RLCA 07
Third rotation.
517A
RLCA 07
Fourth rotation. A has been rotated left by 4 total positions.
517B
XOR B A8
XOR with B (the low nibble). Combines the rotated value with the saved nibble.
517C
POP BC C1
Restore the loop counter from the stack.
517D
XOR (HL) AE
XOR A with the byte at HL (the current workspace byte being hashed). This folds the data byte into the hash computation.
517E
LD D,A 57
Store the result as the new high byte of the hash accumulator (D).
517F
LD (HL),20H 36 20
Store 20H (space) at the workspace byte. This clears the byte after it has been processed, preventing it from affecting future hash computations.
5181
DEC HL 2B
DECrement HL, moving to the previous workspace byte (the hash processes bytes in reverse order).
5182
DECrement B and loop back to 515CH if not zero. [LOOP END - Hash Computation]
5184
EX DE,HL EB
Exchange DE and HL. HL now contains the 16-bit hash value (the final result), and DE is discarded.
5185
POP BC C1
Restore Register Pair BC from the stack.
5186
POP DE D1
Restore Register Pair DE from the stack.
5187
RET C9
RETURN. HL contains the computed 16-bit hash code for the filename.
5188H - NOP Padding (Unused Space)
Addresses 5188H through 51ACH contain NOP (00H) instructions. This is unused padding space at the end of the SYS2 overlay module. The overlay occupies addresses 4D00H through 51FFH (5 sectors × 256 bytes = 1280 bytes), and the active code ends at 5187H. The remaining 37 bytes are filled with zeros.
5188-51AC
NOP 00 (×37)
37 bytes of NOP padding. Unused space at the end of the SYS2 overlay.