4BC9H - Resident Low-RAM Load Block
A two-byte block the overlay loader deposits into SYS0's 4BBC-4BCA unused padding, immediately ahead of the resident exec-drive-op at 4BCBH. It is the same low-RAM slot each SYS overlay writes a small block into; it is not executed on the SYS2 code path.
4BC9
NOP 00
No operation. First byte of the two-byte block written into the SYS0 padding at 4BBC-4BCA (just below the resident exec-drive-op at 4BCBH).
4BCA
INC B 04
Second byte of the resident load block. Because control never enters at 4BC9H on this overlay's path, the pair acts only as the loaded contents of the padding bytes ahead of the SYS0 exec-drive-op.
4E00H - Overlay Entry and Sub-Function Dispatch
The overlay is loaded at 4E00H and entered here by the SYS0 RST 28H dispatcher with Register A holding the request code. Bits 6-4 of the request code (isolated with 70H) select the sub-function; the low nibble carries per-call flags used further on.
4E00
AND 70H E6 70
Mask Register A (the RST 28H request code passed in by the SYS0 dispatcher) down to bits 6-4, the sub-function selector. The remaining bits of the request code carry per-call flags examined later.
4E02
CP 10H FE 10
Compare the selector against 10H. If Register A equals 10H the Z FLAG is set; otherwise the NZ FLAG is set.
4E04
If the Z FLAG is set (selector 10H), JUMP to the Open File routine at 4F2BH.
4E07
CP 20H FE 20
Compare the selector against 20H. If Register A equals 20H the Z FLAG is set; otherwise the NZ FLAG is set.
4E09
If the Z FLAG is set (selector 20H), JUMP to the Initialize (Create) File routine at 5016H.
4E0C
CP 40H FE 40
Compare the selector against 40H. If Register A equals 40H the Z FLAG is set; otherwise the NZ FLAG is set.
4E0E
If the Z FLAG is set (selector 40H), JUMP to the Check Drive / Read GAT routine at 4E52H (the drive-check that services @CKDRV).
4E10
CP 60H FE 60
Compare the selector against 60H. If Register A equals 60H the Z FLAG is set; otherwise the NZ FLAG is set.
4E12
If the Z FLAG is set (selector 60H), JUMP to the 16-Bit Password Hash routine at 4E27H.
4E14
CP 50H FE 50
Compare the selector against 50H. If Register A equals 50H the Z FLAG is set; otherwise the NZ FLAG is set.
4E16
RET NZ C0
If the NZ FLAG is set the selector was none of the recognized codes, so return to the SYS0 dispatcher. Selector 50H falls through into the Filename HIT Hash routine below.
4E17H - Filename HIT Hash
Computes the LDOS directory Hash Index Table code for a filename: an XOR-and-rotate hash over the eleven-byte name-plus-extension field pointed to by HL, reduced to a single byte and stored at 51E1H. A hash of zero is reserved (it marks an empty HIT slot) and is forced to one. Reached by sub-function 50H and called from the open path at 4F51H.
4E17
LD B,0BH 06 0B
Load Register B with 11, the number of bytes in the filename field (an eight-character name plus a three-character extension, both space padded).
4E19
XOR A AF
Set Register A to ZERO, the initial value of the running hash accumulator.
4E1A
XOR (HL) AE
Loop Start
Exclusive-OR the accumulator (Register A) with the next filename byte at the address in HL.
4E1B
INC HL 23
Advance HL to the next byte of the filename field.
4E1C
RLCA 07
Rotate the accumulator left one bit; bit 7 wraps into bit 0. This spreads each byte's contribution across the hash.
4E1D
DECrement Register B and LOOP BACK to 4E1AH until all eleven filename bytes have been folded into the hash.
Loop End
4E1F
OR A B7
Test the finished hash in Register A. If it is zero the Z FLAG is set; otherwise the NZ FLAG is set.
4E20
If the NZ FLAG is set the hash is already non-zero, so JUMP to 4E23H to store it.
4E22
INC A 3C
The hash was zero, which is the reserved code for an empty HIT slot; INCrement it to 1 so a real filename never hashes to the empty marker.
4E23
LD (51E1H),A 32 E1 51
Store the finished one-byte filename hash into the HIT hash-code variable at 51E1H, where the directory search compares it against each HIT entry.
4E26
RET C9
Return to the caller.
4E27H - 16-Bit Password Hash
Computes a 16-bit hash over the eight-byte password field whose base is passed in DE, returning the hash in HL with the Z FLAG set. The result is stored at 51F5H and compared against the owner and update password hashes held in each directory record. Reached by sub-function 60H and called from the open path at 4F57H.
4E27
LD HL,0007H 21 07 00
Load HL with 7, the offset of the last byte of the eight-byte password field.
4E2A
ADD HL,DE 19
Add the field base (DE) to form HL = base+7, a pointer to the final password byte. The hash is computed from the last byte down to the first.
4E2B
EX DE,HL EB
Move that end pointer into DE (the data pointer used by the loop), freeing HL to hold the running 16-bit hash.
4E2C
LD HL,0FFFFH 21 FF FF
Seed the 16-bit hash in HL with FFFFH.
4E2F
LD B,08H 06 08
Load Register B with 8, the number of password bytes to fold into the hash.
4E31
LD A,(DE) 1A
Loop Start
Fetch the current password byte from the address in DE into Register A.
4E32
PUSH DE D5
Save the data pointer; the loop reuses DE as scratch for the hash mixing below.
4E33
LD D,A 57
Hold the password byte in Register D for mixing.
4E34
LD E,H 5C
Copy the high byte of the current hash (H) into E so it can be recombined after mixing.
4E35
LD A,L 7D
Load Register A with the low byte of the current hash.
4E36
AND 07H E6 07
Keep only the low three bits of the hash low byte.
4E38
RRCA 0F
Rotate Register A right one bit.
4E39
RRCA 0F
Rotate Register A right one bit.
4E3A
RRCA 0F
Rotate Register A right a third time, moving those three bits to the top of the byte.
4E3B
XOR L AD
Exclusive-OR with the hash low byte (L) to fold the rotated bits back in.
4E3C
LD L,A 6F
Store the intermediate value in L; Register A retains the same value for the shifts that follow.
4E3D
LD H,00H 26 00
Clear H so HL holds just the intermediate value for the shift-by-16 below.
4E3F
ADD HL,HL 29
Shift HL left one bit (multiply by 2).
4E40
ADD HL,HL 29
Shift HL left again (multiply by 4).
4E41
ADD HL,HL 29
Shift HL left again (multiply by 8).
4E42
ADD HL,HL 29
Shift HL left a fourth time, so the intermediate value is multiplied by 16 (its low nibble now sits in H).
4E43
XOR H AC
Exclusive-OR Register A (still the intermediate value) with the high byte of the shifted result.
4E44
XOR D AA
Exclusive-OR with the password byte held in D, mixing the data into the low half of the new hash.
4E45
LD D,A 57
Save this new low half of the hash in Register D.
4E46
LD A,L 7D
Reload Register A with L (the low byte of the times-16 shift result) to build the high half.
4E47
ADD HL,HL 29
Shift HL left once more (times 32 overall), producing another mixing term in H.
4E48
XOR H AC
Exclusive-OR Register A with the new high byte.
4E49
XOR E AB
Exclusive-OR with E, the saved high byte of the previous hash, completing the new high half.
4E4A
LD E,A 5F
Store the new high half in Register E, so DE now holds the fully updated 16-bit hash (D = low, E = high).
4E4B
EX DE,HL EB
Move the updated hash from DE back into HL for the next iteration.
4E4C
POP DE D1
Restore the password data pointer saved at 4E32H.
4E4D
DEC DE 1B
Step the data pointer back one byte (the hash walks the field from its last byte toward its first).
4E4E
DECrement Register B and LOOP BACK to 4E31H until all eight password bytes have been hashed.
Loop End
4E50
CP A BF
Compare Register A with itself to force the Z FLAG set, signaling the caller that the hash completed normally.
4E51
RET C9
Return to the caller with the 16-bit password hash in HL.
4E52H - Check Drive / Read GAT
Verifies that the current drive is configured, waits for it to become ready, seeks it, and reads the Granule Allocation Table (GAT) sector to obtain the disk geometry and density flags, which it stores into the drive's Drive Code Table (DCT). This is the drive-check logic that services @CKDRV; it is reached by sub-function 40H and called from the open and create search loops. The DCT is the ten-byte-per-drive table based at 4700H (IY): offset 00-02 is the resident JP 45FBH vector, offset 03 the option flags, offset 04 the select-mask/flag byte, offset 05 the current track, offsets 06-09 the geometry.
4E52
PUSH IY FD E5
Save Register IY (the caller's index base) so this routine can repoint it at the DCT.
4E54
GOSUB to the resident SYS0 GETDCT routine, which sets IY = 4700H + drive number times 10, pointing at this drive's Drive Code Table.
4E57
LD A,(IY+00H) FD 7E 00
Fetch the first byte of the DCT (offset 00), which is the opcode of the resident JP 45FBH vector - C3H when the drive is configured.
4E5A
SUB 0C3H D6 C3
Subtract C3H. If the DCT held the expected JP opcode, Register A becomes zero (drive present); any other value leaves A non-zero (drive not configured).
4E5C
If the NZ FLAG is set the drive is not configured, so JUMP to the exit at 4ED4H returning a non-zero (error) code.
4E5F
PUSH HL E5
Save the caller's HL (it carries the 16-bit hash context through this routine).
4E60
PUSH DE D5
Save Register DE for the same reason.
4E61
LD D,(IY+05H) FD 56 05
Load Register D with the drive's current track (DCT offset 05), the seek destination.
4E64
LD E,A 5F
Load Register E with 0 (Register A is still zero from the successful drive-present test), selecting sector 0.
4E65
GOSUB to the resident SYS0 SEEK primitive (disk op 06) to position the head over the track in Register D.
4E68
EI FB
Enable interrupts so the clock task keeps incrementing the interrupt tick counter used by the ready timing below.
4E69
GOSUB to the resident SYS0 RESELECT primitive (disk op 07) to reselect the drive and read its Type I status into Register A.
4E6C
BIT 4,(IY+04H) FD CB 04 66
Test bit 4 of the DCT flag byte (offset 04). If it is set the drive was already confirmed ready, so the spin-up timing can be skipped.
4E70
If the NZ FLAG is set (bit 4 set), JUMP past the ready wait to 4E87H.
4E72
Load Register A with the SYS0 interrupt tick counter at 4040H, incremented every interrupt by the clock task.
4E75
ADD A,14H C6 14
Add 20 ticks to form a timeout deadline.
4E77
LD D,A 57
Hold the deadline in Register D for the ready-poll subroutine.
4E78
Loop Start
GOSUB to the ready-poll subroutine at 4ED7H, which returns Z reflecting the drive index-pulse status and the deadline.
4E7B
LOOP BACK to 4E78H while the index bit is set, waiting for the current index pulse to end.
4E7D
Poll again, now waiting for the gap between index pulses.
4E80
LOOP BACK to 4E7DH while the index bit is clear.
4E82
Poll a third time to observe the next index pulse, confirming the disk is turning.
4E85
LOOP BACK to 4E82H until the index pulse is seen, completing the drive-ready confirmation.
Loop End
4E87
PUSH AF F5
Save the drive status; the timeout path pops it if the wait expired.
4E88
GOSUB to the resident SYS0 get-directory-track routine, which computes the directory cylinder for this disk (Register C returns directory context used below).
4E8B
LD HL,4200H 21 00 42
Point HL at the shared 4200H sector buffer, the destination for the GAT sector.
4E8E
LD E,L 5D
Load Register E with 0 (low byte of 4200H), selecting sector 0 of the directory track, which is the GAT.
4E8F
GOSUB to the resident SYS0 RDSSEC routine (read a sector into the directory buffer) to read the GAT sector into the 4200H buffer.
4E92
If the NZ FLAG is set the GAT read failed, so JUMP to the error path at 4EE3H.
4E94
LD DE,(42CCH) ED 5B CC 42
Load DE with the disk configuration word from offset 0CCH of the GAT sector now in the 4200H buffer. Its bits encode the drive's density and geometry options examined below.
4E98
LD A,C 79
Load Register A with the directory context returned in Register C from 4B65H.
4E99
RLCA 07
Rotate Register A left one bit.
4E9A
RLCA 07
Rotate left again.
4E9B
RLCA 07
Rotate left a third time, shifting the directory context into the field used to build the bit-instruction opcode.
4E9C
OR 0C6H F6 C6
OR in C6H, whose low three bits (110) form the "operate on (HL)" register field of a CB-prefixed bit instruction.
4E9E
BIT 3,D CB 5A
Test bit 3 of the configuration word's high byte in Register D.
4EA0
If the NZ FLAG is set (bit 3 set), JUMP to 4EA4H leaving the opcode as built.
4EA2
XOR 40H EE 40
Otherwise toggle bit 6 of the opcode, switching the bit-instruction between a RES/SET and a BIT form for the alternate density case.
4EA4
LD (4EABH),A 32 AB 4E
Self-Modifying Code
Store the constructed opcode into address 4EABH, the operand byte of the CB-prefixed instruction at 4EAAH. The disassembly shows the initial byte (RLC B); at run time it becomes a BIT, RES or SET of a configuration-selected bit on the byte at (HL).
4EA7
Point HL at the SYS0 spare byte at 475DH, which SYS2 repurposes as a disk density/geometry status byte and which the self-modified bit instruction that follows sets or clears.
4EAA
RLC B CB 00
Self-Modifying Code
The operand byte at 4EABH was patched at 4EA4H, so this executes at run time as a BIT/RES/SET of the configuration-selected bit on the SYS0 status byte at 475DH. The listed RLC B is only the unpatched image.
4EAC
LD A,22H 3E 22
Load Register A with 22H, the base number of directory records per sector used to derive the drive geometry.
4EAE
ADD A,E 83
Add the low byte of the configuration word (Register E), adjusting the geometry value for this disk format.
4EAF
LD (IY+06H),A FD 77 06
Store the derived value into DCT offset 06, part of the drive geometry the directory routines consult.
4EB2
RES 7,(IY+04H) FD CB 04 BE
Clear bit 7 of the DCT flag byte (offset 04) before conditionally setting it below.
4EB6
BIT 4,D CB 62
Test bit 4 of the configuration high byte (Register D).
4EB8
If the Z FLAG is set (bit 4 clear), JUMP to 4EBEH leaving DCT bit 7 clear.
4EBA
SET 7,(IY+04H) FD CB 04 FE
Otherwise set bit 7 of the DCT flag byte to record this geometry option for the drive.
4EBE
RES 5,(IY+04H) FD CB 04 AE
Clear bit 5 of the DCT flag byte before conditionally setting it below.
4EC2
BIT 5,D CB 6A
Test bit 5 of the configuration high byte (Register D).
4EC4
If the Z FLAG is set (bit 5 clear), JUMP to 4ECAH leaving DCT bit 5 clear.
4EC6
SET 5,(IY+04H) FD CB 04 EE
Otherwise set bit 5 of the DCT flag byte to record the second geometry option.
4ECA
POP AF F1
Restore the drive status saved at 4E87H.
4ECB
RLCA 07
Rotate the status left so its former bit 7 falls into position for the mask below.
4ECC
OR (IY+03H) FD B6 03
OR in the DCT option flags (offset 03) to merge the drive's configured options with the live status.
4ECF
AND 80H E6 80
Keep only bit 7, isolating the single result flag returned to the caller.
4ED1
ADD A,A 87
Add Register A to itself, moving bit 7 into the CARRY flag and leaving A zero, so the caller sees success (A=0) with the option reflected in carry.
4ED2
POP DE D1
Restore the caller's DE saved at 4E60H (the shared exit for both the success and error paths).
4ED3
POP HL E1
Restore the caller's HL saved at 4E5FH.
4ED4
POP IY FD E1
Restore the caller's IY saved at 4E52H (the not-configured path enters here directly).
4ED6
RET C9
Return to the caller.
4ED7H - Drive Ready Poll
A helper the ready wait calls repeatedly. It checks whether the frame-counter deadline in Register D has been reached and reads the drive's index-pulse status bit, returning the Z FLAG for the caller's timing loops. Reaching the deadline diverts to the timeout error path.
4ED7
Load Register A with the current SYS0 interrupt tick counter at 4040H.
4EDA
CP D BA
Compare it against the deadline in Register D. If they are equal the Z FLAG is set, meaning the drive did not become ready in time.
4EDB
If the Z FLAG is set the deadline was reached, so JUMP to the error path at 4EE3H.
4EDD
GOSUB to the resident SYS0 RESELECT primitive to reread the drive Type I status into Register A.
4EE0
BIT 1,A CB 4F
Test status bit 1, the WD1771 Type I index-mark bit (1 = index hole detected). The Z FLAG returned reflects the current index state.
4EE2
RET C9
Return to the timing loop with the Z FLAG set from the index-bit test.
4EE3H - Drive Error Exit
The shared error tail for the GAT-read failure and the drive-ready timeout. It discards the saved status, forces an error indication, and rejoins the main exit sequence.
4EE3
POP AF F1
Discard the drive status saved at 4E87H, since the operation has failed.
4EE4
OR 01H F6 01
Set bit 0 of Register A and clear the Z FLAG, so the caller sees a non-zero error result.
4EE6
JUMP to the common exit at 4ED2H to restore the saved registers and return.
4EE8H - Open Device
Handles a device open, reached from the open path at 4F3EH when the first filespec character is an asterisk. It searches the resident Device Control Block chain (the KI/DO/PR blocks starting at 4015H and the JL/SI/SO blocks at 43C0H) for the caller's two-character device name, then links the caller's FCB to the matching DCB and tags it as a device.
4EE8
LD E,(IX+01H) DD 5E 01
Load Register E with the first device-name character from FCB offset 01 (the caller's requested device name).
4EEB
LD D,(IX+02H) DD 56 02
Load Register D with the second device-name character from FCB offset 02. DE now holds the two-character name being sought.
4EEE
Point HL at 4015H, the base of the resident KI/DO/PR Device Control Blocks in SYS0.
4EF1
PUSH HL E5
Loop Start
Save the current DCB base so it can be recovered on a match or advanced on a miss.
4EF2
LD A,L 7D
Copy the low byte of the DCB base into Register A to compute the name-field address.
4EF3
ADD A,06H C6 06
Add 6, the offset of the two-character name within each DCB.
4EF5
LD L,A 6F
Store the result back into L so HL now points at the DCB's name field.
4EF6
LD A,(HL) 7E
Fetch the first name character of this DCB.
4EF7
INC L 2C
Advance HL to the second name character.
4EF8
CP E BB
Compare the DCB's first character against the requested first character (Register E).
4EF9
If they differ, JUMP to 4F02H to try the next DCB.
4EFB
LD A,(HL) 7E
Fetch the DCB's second name character.
4EFC
CP D BA
Compare it against the requested second character (Register D).
4EFD
If they differ, JUMP to 4F02H to try the next DCB.
4EFF
POP HL E1
Both characters matched; recover the DCB base into HL.
4F00
JUMP to 4F14H to link the FCB to this DCB.
4F02
POP AF F1
Discard the saved DCB base (recovered into AF) so the stack is balanced for the next attempt.
4F03
INC L 2C
Advance L past this DCB toward the next one.
4F04
If L did not wrap to zero, JUMP to 4F0AH to check the table boundary.
4F06
LD A,08H 3E 08
The DCB pointer wrapped past the table; load error code 08H (device not available).
4F08
OR A B7
Clear the Z FLAG so the caller sees the non-zero error.
4F09
RET C9
Return with the device-not-available error.
4F0A
LD A,L 7D
Load Register A with the current DCB pointer low byte to test the end of the first table.
4F0B
CP 2DH FE 2D
Compare against 2DH, the address just past the KI/DO/PR block table.
4F0D
If the end of the first table has not been reached, LOOP BACK to 4EF1H to test the next DCB.
4F0F
Point HL at 43C0H, the base of the resident JL/SI/SO Device Control Blocks in SYS0, to continue the search there.
4F12
JUMP back to 4EF1H to scan the second DCB table.
4F14
LD B,H 44
Copy the matched DCB address high byte into Register B.
4F15
LD C,L 4D
Copy the low byte into Register C, so BC now holds the DCB address to store into the FCB.
4F16
PUSH IX DD E5
Push the caller's FCB pointer (IX) to copy it into HL.
4F18
POP HL E1
HL now points at the caller's FCB for the field stores below.
4F19
LD (HL),10H 36 10
Store 10H into FCB offset 00, the type byte that marks this FCB as an open device rather than a disk file.
4F1B
INC HL 23
Advance to FCB offset 01.
4F1C
LD (HL),C 71
Store the DCB address low byte into FCB offset 01.
4F1D
INC HL 23
Advance to FCB offset 02.
4F1E
LD (HL),B 70
Store the DCB address high byte into FCB offset 02, so the FCB now points at the device's control block.
4F1F
INC HL 23
Advance to FCB offset 03.
4F20
XOR A AF
Set Register A to ZERO to clear the following FCB bytes.
4F21
LD (HL),A 77
Clear FCB offset 03.
4F22
INC HL 23
Advance to FCB offset 04.
4F23
LD (HL),A 77
Clear FCB offset 04.
4F24
INC HL 23
Advance to FCB offset 05.
4F25
LD (HL),A 77
Clear FCB offset 05.
4F26
INC HL 23
Advance to FCB offset 06.
4F27
LD (HL),E 73
Store the first device-name character (Register E) into FCB offset 06.
4F28
INC HL 23
Advance to FCB offset 07.
4F29
LD (HL),D 72
Store the second device-name character (Register D) into FCB offset 07, completing the device FCB.
4F2A
RET C9
Return to the caller with the FCB linked to the device.
4F2BH - Open File
The sub-function 10H entry. It validates the FCB, parses the filespec, hashes the filename and password, then searches each candidate drive's directory for a matching record. A leading asterisk diverts to the device open. The 4F2EH label is a second entry the create path calls to attempt an open without re-validating the FCB.
4F2B
GOSUB to the resident SYS0 FCB prologue, which validates and prepares the caller's File Control Block (pointed to by IX) for the open.
4F2E
Load Register A with the SYS0 system configuration flags at 430FH. The create path re-enters the open here at 4F2EH.
4F31
LD (4FFDH),A 32 FD 4F
Save the configuration flags at 4FFDH so they can be restored after the open modifies them.
4F34
AND 0F9H E6 F9
Clear flag bits 1 and 2 in the working copy, resetting the per-open state those bits carry.
4F36
Write the cleared flags back to the SYS0 configuration byte at 430FH.
4F39
LD A,(IX+00H) DD 7E 00
Load Register A with the first character of the caller's filespec from FCB offset 00.
4F3C
CP 2AH FE 2A
Compare it against * (2AH), the device-open prefix.
4F3E
If the Z FLAG is set the filespec names a device, so JUMP to the Open Device routine at 4EE8H.
4F40
LD A,B 78
Load Register A with the caller's access/update byte passed in Register B.
4F41
LD (51FFH),A 32 FF 51
Save that access byte at 51FFH for the FCB finalizer to fold into the open FCB.
4F44
LD (50A0H),HL 22 A0 50
Self-Modifying Code
Store the caller's HL into address 50A0H, the pointer operand of an instruction inside the FCB finalizer at 5084H, so the finalizer can reach the caller's buffer context.
4F47
PUSH IX DD E5
Push the FCB pointer to copy it into HL for the parser.
4F49
POP HL E1
HL now points at the caller's filespec in the FCB.
4F4A
GOSUB to the filespec parser at 50F4H, which splits the filespec into the name, extension, password and drive fields.
4F4D
RET NZ C0
If the NZ FLAG is set the filespec was malformed, so return the parser's error code to the caller.
4F4E
LD HL,51EAH 21 EA 51
Point HL at the parsed eleven-byte name/extension field at 51EAH.
4F51
GOSUB to the Filename HIT Hash routine, which stores the one-byte filename hash at 51E1H.
4F54
LD DE,51E2H 11 E2 51
Point DE at the parsed eight-byte password field at 51E2H.
4F57
GOSUB to the 16-Bit Password Hash routine, returning the password hash in HL.
4F5A
LD (51F5H),HL 22 F5 51
Store the 16-bit password hash at 51F5H for the record verifier to compare against the directory record.
4F5D
LD A,00H 3E 00
Load Register A with 0, the first drive number to try.
4F5F
LD C,A 4F
Set the drive counter (Register C) to 0.
4F60
INC A 3C
Increment Register A to 1, testing whether it stays non-zero (it does).
4F61
Since A is non-zero, JUMP to 4F64H, skipping the re-zero of the drive counter.
4F63
LD C,A 4F
Reset the drive counter (unreached on this fall-through; present for the shared code path).
4F64
Loop Start
GOSUB to the Check Drive / Read GAT routine for the current drive (Register C), confirming it is ready and loading its geometry.
4F67
If the NZ FLAG is set the drive is not usable, so JUMP to 4F7CH to advance to the next drive.
4F69
GOSUB to the Read HIT Sector routine, loading this drive's Hash Index Table into the 4200H buffer with HL pointing at it.
4F6C
RET NZ C0
If the NZ FLAG is set the HIT read failed, so return the error to the caller.
4F6D
LD A,(HL) 7E
Loop Start
Fetch the next HIT entry byte at the address in HL.
4F6E
OR A B7
Test the HIT byte. If it is zero the slot is empty; the Z FLAG is set.
4F6F
If the slot is empty, JUMP to 4F79H to step to the next HIT entry.
4F71
PUSH HL E5
Save the HIT scan pointer before comparing against the target hash.
4F72
LD HL,51E1H 21 E1 51
Point HL at the target filename hash at 51E1H.
4F75
CP (HL) BE
Compare the HIT entry byte against the target filename hash. If they match the Z FLAG is set.
4F76
POP HL E1
Restore the HIT scan pointer.
4F77
If the hash matched, JUMP to the Verify Directory Record routine at 4F9AH to confirm the full name.
4F79
INC L 2C
Advance the HIT scan pointer to the next entry within the sector.
4F7A
LOOP BACK to 4F6DH until the HIT scan pointer wraps past the end of the sector.
Loop End
4F7C
GOSUB to the Advance To Next Drive routine, which steps the drive counter when no specific drive was requested.
4F7F
If the CARRY FLAG is set there are more drives to search, so LOOP BACK to 4F64H.
4F81
LD A,18H 3E 18
All drives exhausted with no match; load error code 18H (file not found).
4F83
OR A B7
Clear the Z FLAG so the caller sees the non-zero not-found error.
4F84
RET C9
Return with the file-not-found error.
4F85H - Advance To Next Drive
Steps the drive counter during a multi-drive search. If a specific drive was requested (4F5EH is not FFH) there is nowhere else to look, so it returns NZ. Otherwise it advances the counter and returns carry while it is still below drive 8.
4F85
LD A,(4F5EH) 3A 5E 4F
Load Register A with the requested drive number at 4F5EH (FFH means no specific drive).
4F88
INC A 3C
Increment it; only the FFH "search all" case becomes zero.
4F89
OR A B7
Test the result. If it is non-zero a specific drive was named.
4F8A
RET NZ C0
If the NZ FLAG is set a specific drive was requested, so return NZ - there are no other drives to try.
4F8B
INC C 0C
Advance the drive counter (Register C) to the next drive.
4F8C
LD A,C 79
Copy the new drive number into Register A.
4F8D
CP 08H FE 08
Compare against 8, the number of possible drives. If Register C is below 8 the CARRY FLAG is set, signaling more drives remain.
4F8F
RET C9
Return with carry set while more drives remain to search, clear when all eight have been tried.
4F90H - Resume Directory Scan
The continue point used when a HIT hash matched but the directory record failed verification. It unwinds the verifier's saved registers, rereads the HIT sector, and resumes the scan at the next entry.
4F90
POP BC C1
Discard a saved register pair from the verifier's stack frame.
4F91
POP HL E1
Recover the HIT scan pointer saved by the verifier.
4F92
POP BC C1
Discard the second saved register pair to balance the stack.
4F93
GOSUB to Read HIT Sector to reload the HIT into the 4200H buffer (the directory read overwrote it).
4F96
POP HL E1
Restore the HIT scan pointer to resume the scan.
4F97
RET NZ C0
If the HIT reread failed, return the error to the caller.
4F98
JUMP to 4F79H to advance to the next HIT entry and continue scanning.
4F9AH - Verify Directory Record
Entered when a HIT byte matched the filename hash. It reads the corresponding directory record, checks the record is an active primary entry, confirms the full eleven-character name and extension, then compares the caller's password hash against the record's password hash and enforces the access level. On success it records the matched drive and jumps to the FCB finalizer. The LDOS directory record layout used here is: offset 00 attribute byte, offset 05 the eleven-byte name/extension, and a password-hash word further into the record.
4F9A
PUSH HL E5
Save the HIT scan pointer.
4F9B
PUSH BC C5
Save the drive counter and access byte.
4F9C
LD B,L 45
Copy the HIT entry index (low byte of the scan pointer) into Register B; it is the directory record number.
4F9D
GOSUB to the resident SYS0 DIRRD routine to read the directory sector holding record B into the 4200H buffer, with HL pointing at the record.
4FA0
If the directory read succeeded (Z set), JUMP to 4FA5H to examine the record.
4FA2
POP BC C1
Restore the drive counter and access byte after a read failure.
4FA3
POP HL E1
Restore the HIT scan pointer.
4FA4
RET C9
Return the directory-read error to the caller.
4FA5
PUSH HL E5
Save the directory record pointer.
4FA6
PUSH BC C5
Save the drive counter and access byte again for the compare loop.
4FA7
BIT 7,(HL) CB 7E
Test bit 7 of the record's attribute byte (offset 00). When set, the entry is not a usable primary directory record.
4FA9
If bit 7 is set, this is a false hash hit, so JUMP to 4F90H to resume the scan.
4FAB
BIT 4,(HL) CB 66
Test bit 4 of the attribute byte, which marks the record as active/in use.
4FAD
If bit 4 is clear the record is not active, so JUMP to 4F90H to resume the scan.
4FAF
LD A,05H 3E 05
Load Register A with 5, the offset of the name/extension field within the directory record.
4FB1
ADD A,L 85
Add the record pointer low byte.
4FB2
LD L,A 6F
Store the sum back into L so HL points at the record's name/extension field.
4FB3
LD DE,51EAH 11 EA 51
Point DE at the parsed eleven-byte name/extension we are searching for.
4FB6
LD B,0BH 06 0B
Load Register B with 11, the number of name/extension bytes to compare.
4FB8
LD A,(DE) 1A
Loop Start
Fetch the next byte of the sought name from the address in DE.
4FB9
CP (HL) BE
Compare it against the corresponding byte of the record's name. If they differ the NZ FLAG is set.
4FBA
On any mismatch this is a different file with the same hash, so JUMP to 4F90H to resume the scan.
4FBC
INC HL 23
Advance the record name pointer.
4FBD
INC DE 13
Advance the sought name pointer.
4FBE
DECrement Register B and LOOP BACK to 4FB8H until all eleven name bytes have matched.
Loop End
4FC0
POP BC C1
Full name matched; restore the drive counter and access byte.
4FC1
LD A,C 79
Copy the matched drive number from Register C.
4FC2
LD (4F5EH),A 32 5E 4F
Record the matched drive number at 4F5EH so the FCB reflects the drive the file was found on.
4FC5
POP HL E1
Recover the directory record pointer.
4FC6
POP AF F1
Discard a saved frame word from the outer search loop.
4FC7
POP AF F1
Discard the second saved frame word, unwinding the search-loop return so control will pass to the finalizer directly.
4FC8
PUSH BC C5
Re-save the drive counter for the finalizer.
4FC9
PUSH HL E5
Re-save the directory record pointer.
4FCA
LD A,(HL) 7E
Load Register A with the record's attribute byte (offset 00).
4FCB
LD (51FBH),A 32 FB 51
Save the attribute byte at 51FBH for the FCB finalizer.
4FCE
AND 07H E6 07
Keep the low three bits, the file's access-level (protection) value.
4FD0
LD C,A 4F
Hold the access level in Register C for the protection check.
4FD1
LD A,10H 3E 10
Load Register A with 10H, the offset from the record base to the stored password-hash word.
4FD3
ADD A,L 85
Add the record pointer low byte.
4FD4
LD L,A 6F
Store the sum back into L so HL points at the record's password-hash word.
4FD5
LD DE,(51F5H) ED 5B F5 51
Load DE with the caller's computed password hash from 51F5H.
4FD9
PUSH HL E5
Save the record password pointer.
4FDA
LD HL,113DH 21 3D 11
Load HL with 113DH, the standard LDOS hash of a blank password (no password supplied).
4FDD
XOR A AF
Clear Register A and the CARRY flag ahead of the 16-bit subtract.
4FDE
SBC HL,DE ED 52
Subtract the caller's password hash from 113DH. If the caller supplied no password the result is zero and the Z FLAG is set.
4FE0
POP HL E1
Restore the record password pointer.
4FE1
If the caller gave no password, accept the file and JUMP to 500EH to finish the open.
4FE3
LD A,(HL) 7E
Otherwise fetch the low byte of the record's stored password hash.
4FE4
INC HL 23
Advance to the high byte.
4FE5
LD H,(HL) 66
Load the high byte of the record's password hash.
4FE6
LD L,A 6F
Assemble the record's stored password hash into HL.
4FE7
XOR A AF
Clear Register A and the CARRY flag ahead of the compare.
4FE8
SBC HL,DE ED 52
Subtract the caller's hash from the record's stored hash. If they match the result is zero and the Z FLAG is set.
4FEA
If the password hash matched, accept the file and JUMP to 500EH to finish the open.
4FEC
LD A,C 79
The password did not match; load the file's access level from Register C.
4FED
CP 07H FE 07
Compare it against 7, the fully unprotected access level.
4FEF
If the access level is not 7 (some protection applies), JUMP to 4FF7H for the restricted-access handling.
4FF1
POP HL E1
Restore the record pointer.
4FF2
POP BC C1
Restore the drive counter and access byte.
4FF3
LD A,19H 3E 19
Load error code 19H (password protection violation).
4FF5
OR A B7
Clear the Z FLAG so the caller sees the non-zero protection error.
4FF6
RET C9
Return the protection error to the caller.
4FF7
LD A,C 79
Reload the file's access level.
4FF8
CP 06H FE 06
Compare it against access level 6.
4FFA
If the access level is not 6, accept with the limited access and JUMP to 500EH.
4FFC
LD B,00H 06 00
Load Register B with 0 for the following bit test.
4FFE
BIT 2,B CB 50
Test bit 2 of Register B (always zero here), so the Z FLAG is set.
5000
The test is always zero, so JUMP to 5009H to deposit the dispatcher patch.
5002
Point HL at the SYS0 configuration flags at 430FH.
5005
SET 1,(HL) CB CE
Set configuration bit 1 to flag the limited-access condition (unreached on this path).
5007
LD A,05H 3E 05
Load the reduced access value 05H.
5009
Point HL at 4BE9H, a self-modified byte inside the SYS0 RST 28H dispatcher.
500C
LD (HL),0C9H 36 C9
Self-Modifying Code
Deposit C9H (RET) into the SYS0 dispatcher at 4BE9H so its self-modified CALL returns immediately on this open completion path.
500E
LD (5090H),A 32 90 50
Self-Modifying Code
Store the access/attribute value in Register A into address 5090H, the operand of an instruction in the FCB finalizer at 5084H that writes the FCB access byte.
5011
POP HL E1
Recover the directory record pointer for the finalizer.
5012
POP BC C1
Recover the drive counter for the finalizer.
5013
JUMP to the Build FCB From Directory Record finalizer at 5084H.
5016H - Initialize (Create) File
The sub-function 20H entry. It first attempts an ordinary open; if the file already exists it returns that. If the open reports file-not-found it allocates a free HIT slot and directory record on a candidate drive, writes the new directory entry (attributes, name, password hashes, cleared extents), then builds the FCB and returns with carry set to signal a newly created file.
5016
GOSUB to the resident SYS0 FCB prologue to validate and prepare the caller's File Control Block.
5019
LD (5090H),A 32 90 50
Self-Modifying Code
Seed the FCB finalizer's access operand at 5090H with the value returned by the prologue.
501C
LD A,10H 3E 10
Load Register A with 10H, the default directory access mode for the create.
501E
LD (5030H),A 32 30 50
Self-Modifying Code
Store 10H into address 5030H, the immediate operand of the LD A,10H at 502FH, presetting the directory access mode (the extension handler at 5141H may later change it to 17H).
5021
PUSH HL E5
Save HL across the flag update.
5022
Point HL at the SYS0 configuration flags at 430FH.
5025
RES 2,(HL) CB 96
Clear configuration bit 2, resetting the create-in-progress state before the open attempt.
5028
GOSUB to the open routine's second entry at 4F2EH to look for an existing file of this name.
502B
RET Z C8
If the Z FLAG is set the file already exists and is now open, so return it to the caller.
502C
CP 18H FE 18
Compare the open's error code against 18H (file not found).
502E
RET NZ C0
If the error was anything other than not-found, return it to the caller.
502F
LD A,10H 3E 10
Self-Modifying Code
Load Register A with the directory access mode. The immediate at 5030H was patched at 501EH (10H) or by the extension handler at 5141H (17H).
5031
LD (51FBH),A 32 FB 51
Store the attribute byte into the directory-record template at 51FBH for the new entry.
5034
LD A,(4F5EH) 3A 5E 4F
Load Register A with the requested drive number at 4F5EH.
5037
LD C,A 4F
Copy it into the drive counter (Register C).
5038
INC A 3C
Increment it; the FFH "no drive specified" case becomes zero.
5039
If a specific drive was named, JUMP to 503CH keeping that drive number.
503B
LD C,A 4F
Otherwise set the drive counter to 0 (start creating on drive 0).
503C
Loop Start
GOSUB to Check Drive / Read GAT for the current drive, confirming it is ready and loading its geometry.
503F
If the drive is not usable, JUMP to 504CH to try the next drive.
5041
If the GAT returned the carry flag (a geometry option this create rejects), JUMP to 504CH to try the next drive.
5043
GOSUB to Read HIT Sector to load this drive's Hash Index Table.
5046
RET NZ C0
If the HIT read failed, return the error.
5047
GOSUB to Allocate Free Directory Slot, which scans the HIT for a free entry within the directory size.
504A
If a free slot was found (Z set), JUMP to 5055H to write the new record.
504C
GOSUB to Advance To Next Drive to try creating on another drive if none was specified.
504F
If more drives remain, LOOP BACK to 503CH.
Loop End
5051
LD A,1AH 3E 1A
No drive had a free directory slot; load error code 1AH (directory full).
5053
OR A B7
Clear the Z FLAG so the caller sees the non-zero directory-full error.
5054
RET C9
Return the directory-full error.
5055
LD B,L 45
Copy the free HIT slot index (low byte of HL) into Register B; it becomes the new directory record number.
5056
LD A,(51E1H) 3A E1 51
Load Register A with the filename hash at 51E1H.
5059
LD (HL),A 77
Store the filename hash into the free HIT slot, claiming it for this file.
505A
GOSUB to the write-HIT entry at 51BEH to write the updated HIT sector back to disk.
505D
If the HIT write succeeded, GOSUB to the resident SYS0 DIRRD to read the directory sector holding record B, with HL pointing at the record.
5060
RET NZ C0
If either the write or the read failed, return the error.
5061
PUSH HL E5
Save the directory record pointer.
5062
PUSH BC C5
Save the record number and drive counter.
5063
EX DE,HL EB
Move the record pointer into DE, the destination for the block copies below.
5064
LD BC,0005H 01 05 00
Load a count of 5 bytes.
5067
LD HL,51FBH 21 FB 51
Point HL at the directory-record template at 51FBH (attribute and leading fields).
506A
LDIR ED B0
Block-move 5 bytes from the template (HL) into the directory record (DE), copying the attribute and leading fields.
506C
LD C,11H 0E 11
Load a count of 17 bytes (BC = 0011H, since B is already 0 after the prior LDIR).
506E
LD HL,51EAH 21 EA 51
Point HL at the parsed name/extension field at 51EAH.
5071
LDIR ED B0
Block-move 17 bytes into the record: the eleven-byte name/extension followed by the password-hash and related fields.
5073
EX DE,HL EB
Move the advancing record pointer back into HL to clear the extent area.
5074
LD B,0AH 06 0A
Load Register B with 10, the number of extent bytes to clear.
5076
GOSUB to Fill FFH Block to write FFH into the 10 extent bytes, marking them unused.
5079
POP BC C1
Restore the record number and drive counter.
507A
GOSUB to the resident SYS0 DIRWR to write the completed directory sector back to disk.
507D
POP HL E1
Restore the directory record pointer.
507E
RET NZ C0
If the directory write failed, return the error.
507F
GOSUB to Build FCB From Directory Record to populate the caller's FCB from the new record.
5082
SCF 37
Set the CARRY flag to signal the caller that the file was newly created rather than found.
5083
RET C9
Return to the caller with the new file open.
5084H - Build FCB From Directory Record
Populates the caller's File Control Block (IX) from the located or newly created directory record pointed to by HL. It writes the FCB type and access bytes, initializes the buffer and position fields from the finalizer operands patched during the open, copies the directory-locator bytes, and walks the record's extents to compute the file's end-of-file position.
5084
EX DE,HL EB
Move the directory record pointer into DE; HL is repointed at the FCB next.
5085
PUSH IX DD E5
Push the caller's FCB pointer to copy it into HL.
5087
POP HL E1
HL now points at the caller's FCB.
5088
LD (HL),80H 36 80
Store 80H into FCB offset 00, the open-file flag bit that marks the FCB as an active disk file.
508A
INC HL 23
Advance to FCB offset 01 (the access/flags byte).
508B
LD A,(51FFH) 3A FF 51
Load Register A with the caller's requested access byte saved at 51FFH.
508E
OR A B7
Test whether any access bits were requested.
508F
LD A,00H 3E 00
Load Register A with 0 to start building the FCB flag byte.
5091
If no access byte was requested (Z set), JUMP to 5095H leaving the write-access bit clear.
5093
OR 80H F6 80
Otherwise set bit 7 of the FCB flag byte to grant the requested access.
5095
OR 20H F6 20
Set bit 5 of the FCB flag byte (the buffer-holds-record indicator initialized here).
5097
OR 00H F6 00
Self-Modifying Code
OR in the flag bits at address 5098H, patched by the parser at 513BH to 08H when the filespec had a trailing terminator or to 00H otherwise.
5099
LD (HL),A 77
Store the assembled flag byte into FCB offset 01.
509A
INC HL 23
Advance to FCB offset 02.
509B
XOR A AF
Set Register A to ZERO to clear the next FCB byte.
509C
LD (HL),A 77
Clear FCB offset 02.
509D
INC HL 23
Advance to FCB offset 03 (the buffer address low byte).
509E
PUSH DE D5
Save the directory record pointer during the buffer-address store.
509F
LD DE,0000H 11 00 00
Self-Modifying Code
Load DE with the caller's buffer pointer. The operand at 50A0H-50A1H was patched at 4F44H with the caller's HL (the record buffer context).
50A2
LD (HL),E 73
Store the buffer address low byte into FCB offset 03.
50A3
INC HL 23
Advance to FCB offset 04.
50A4
LD (HL),D 72
Store the buffer address high byte into FCB offset 04.
50A5
INC HL 23
Advance to FCB offset 05 (the byte-within-sector offset).
50A6
POP DE D1
Restore the directory record pointer.
50A7
LD (HL),A 77
Clear FCB offset 05 (Register A is still zero), starting the buffer offset at 0.
50A8
INC HL 23
Advance to FCB offset 06 (the directory locator).
50A9
LD (HL),C 71
Store the drive number (Register C) into FCB offset 06, recording which drive the file lives on.
50AA
INC HL 23
Advance to FCB offset 07.
50AB
LD (HL),B 70
Store the directory record number (Register B) into FCB offset 07, completing the directory locator.
50AC
INC HL 23
Advance to FCB offset 08 (the EOF byte offset).
50AD
INC DE 13
Advance the directory record pointer toward the fields copied next.
50AE
INC DE 13
Advance the record pointer again.
50AF
INC DE 13
Advance the record pointer a third time to the record's EOF-offset byte.
50B0
LD A,(DE) 1A
Fetch the record's end-of-file byte offset.
50B1
LD (HL),A 77
Store it into FCB offset 08.
50B2
INC HL 23
Advance to FCB offset 09 (the logical record length).
50B3
INC DE 13
Advance the record pointer to the logical record length field.
50B4
LD A,(51FFH) 3A FF 51
Load Register A with the caller's access byte again (the low byte doubles as the logical record length request).
50B7
LD (HL),A 77
Store the logical record length into FCB offset 09.
50B8
INC HL 23
Advance to FCB offset 0A (the next record number low byte).
50B9
XOR A AF
Set Register A to ZERO to clear the record-number fields.
50BA
LD (HL),A 77
Clear FCB offset 0A.
50BB
INC HL 23
Advance to FCB offset 0B.
50BC
LD (HL),A 77
Clear FCB offset 0B, zeroing the next-record-number field.
50BD
INC HL 23
Advance to FCB offset 0C (the ending record number).
50BE
SET 4,E CB E3
Set bit 4 of Register E, repointing the record pointer at the record's extent map (offset within the directory record).
50C0
LD BC,0002H 01 02 00
Load a count of 2 bytes.
50C3
EX DE,HL EB
Swap so HL is the record extent source and DE is the FCB destination.
50C4
LDIR ED B0
Block-move the 2-byte ending record number from the record into the FCB.
50C6
EX DE,HL EB
Swap back so HL again references the record extent map.
50C7
LD A,05H 3E 05
Load Register A with 5, the number of extent slots to walk.
50C9
PUSH AF F5
Save the extent counter across the loop body.
50CA
LD A,(DE) 1A
Loop Start
Fetch the first byte of the next extent (the starting granule) from the record.
50CB
LD (HL),A 77
Store it into the FCB extent area.
50CC
INC HL 23
Advance the FCB extent pointer.
50CD
INC DE 13
Advance the record extent pointer to the granule count byte.
50CE
LD A,(DE) 1A
Fetch the extent's granule count byte.
50CF
LD (HL),A 77
Store it into the FCB extent area.
50D0
INC HL 23
Advance the FCB extent pointer.
50D1
AND 1FH E6 1F
Keep the low five bits, the number of granules in this extent.
50D3
INC A 3C
Add one, converting the count field to the actual granule total.
50D4
ADD A,C 81
Add the running granule total held in Register C.
50D5
LD C,A 4F
Store the updated running total back into Register C.
50D6
If the running total did not overflow a byte, JUMP to 50D9H.
50D8
INC B 04
Otherwise carry into Register B, extending the granule total to 16 bits.
50D9
POP AF F1
Restore the extent counter.
50DA
DEC A 3D
Decrement the extent counter.
50DB
RET Z C8
If all five extents have been processed, return with the FCB fully built.
50DC
PUSH AF F5
Save the extent counter for the next iteration.
50DD
INC DE 13
Advance the record extent pointer to the next extent's first byte.
50DE
LD A,(DE) 1A
Fetch that extent's starting-granule byte.
50DF
CP 0FEH FE FE
Compare it against FEH/FFH, the markers that terminate the extent chain.
50E1
If the byte is FEH or above the extent chain has ended, so JUMP to 50E9H to finalize.
50E3
LD (HL),C 71
Store the running granule total low byte into the FCB.
50E4
INC HL 23
Advance the FCB pointer.
50E5
LD (HL),B 70
Store the running granule total high byte into the FCB.
50E6
INC HL 23
Advance the FCB pointer.
50E7
LOOP BACK to 50CAH to process the next extent.
Loop End
50E9
POP AF F1
Discard the saved extent counter; the chain is complete.
50EA
RLCA 07
Rotate the running granule total left one bit.
50EB
RLCA 07
Rotate left again, multiplying the granule total by four to derive a sector count for the tail-clear below.
50EC
LD B,A 47
Move the derived count into Register B, then fall into the FFH-fill helper to clear the remaining FCB extent slots.
50EDH - Fill FFH Block
Writes FFH into B consecutive bytes starting at the address in HL, then returns with Register A cleared. It clears directory extent bytes for a new record and the trailing FCB extent slots after the extent walk.
50ED
LD (HL),0FFH 36 FF
Loop Start
Store FFH into the byte at the address in HL, marking it unused.
50EF
INC HL 23
Advance to the next byte.
50F0
DECrement Register B and LOOP BACK to 50EDH until B bytes have been filled.
Loop End
50F2
XOR A AF
Set Register A to ZERO and set the Z FLAG to signal success.
50F3
RET C9
Return to the caller.
50F4H - Parse Filespec
Splits the caller's filespec (pointed to by HL) into its component fields in the 51E2H buffer. It first blanks all nineteen field bytes, defaults the drive to "search all", scans the eight-character name, then honors an optional / extension, . password and : drive number. Throughout, Register D stays 51H so the LD E,xx instructions simply select the destination sub-field within the 51E2H-51F4H buffer.
50F4
LD B,13H 06 13
Load Register B with 19, the size of the field buffer to blank.
50F6
LD DE,51E2H 11 E2 51
Point DE at the start of the field buffer at 51E2H (password, name, extension).
50F9
LD A,20H 3E 20
Load Register A with the ASCII space used to pad every field.
50FB
LD (DE),A 12
Loop Start
Store a space into the current buffer byte.
50FC
INC DE 13
Advance to the next buffer byte.
50FD
DECrement Register B and LOOP BACK to 50FBH until all nineteen buffer bytes are blanked.
Loop End
50FF
LD A,0FFH 3E FF
Load Register A with FFH, the "no specific drive" sentinel.
5101
LD (4F5EH),A 32 5E 4F
Store FFH at 4F5EH so the search defaults to trying every drive unless a drive is named.
5104
LD E,0EAH 1E EA
Set the DE low byte to EAH so DE points at the name sub-field at 51EAH (Register D is still 51H).
5106
GOSUB to the Scan Name Field routine to copy up to eight name characters into the name sub-field; Register B returns the count left unfilled and Register A the terminating character.
5109
LD C,A 4F
Hold the terminating character in Register C.
510A
LD A,B 78
Load Register A with the remaining count from the scan.
510B
SUB 08H D6 08
Subtract 8. If the count is unchanged (no name characters were consumed) the result is non-zero.
510D
If at least one name character was scanned, JUMP to 5112H to process the remaining fields.
510F
OR 13H F6 13
The name was empty; load error code 13H (illegal filespec) and clear the Z FLAG.
5111
RET C9
Return the illegal-filespec error.
5112
LD A,C 79
Reload the terminating character from Register C.
5113
CP 2FH FE 2F
Compare it against / (2FH), the extension separator.
5115
LD E,0F2H 1E F2
Point DE at the extension sub-field at 51F2H in case an extension follows.
5117
LD B,03H 06 03
Load Register B with 3, the extension length limit.
5119
If the terminator was /, GOSUB to the scanner at 5146H to copy up to three extension characters.
511C
CP 2EH FE 2E
Compare the current terminator against . (2EH), the password separator.
511E
LD E,0E2H 1E E2
Point DE at the password sub-field at 51E2H in case a password follows.
5120
If the terminator was ., GOSUB to 513FH which sets the password directory mode and scans the eight-character password.
5123
CP 3AH FE 3A
Compare the current terminator against : (3AH), the drive separator.
5125
If no drive was given, JUMP to 5134H to finish parsing.
5127
LD A,(HL) 7E
Fetch the drive digit following the colon.
5128
SUB 30H D6 30
Convert the ASCII digit to a binary drive number.
512A
LD (4F5EH),A 32 5E 4F
Store the requested drive number at 4F5EH, overriding the "search all" default.
512D
AND 0F8H E6 F8
Test whether the drive number is 8 or greater (any high bit set).
512F
LD A,20H 3E 20
Load error code 20H (illegal drive number) in case the test fails.
5131
RET NZ C0
If the drive number was out of range, return the illegal-drive error.
5132
INC HL 23
Advance past the drive digit.
5133
LD A,(HL) 7E
Fetch the character following the drive number to check for a clean terminator.
5134
SUB 21H D6 21
Subtract 21H; the result reflects whether the trailing character is a space or control code (a valid end of filespec).
5136
LD A,08H 3E 08
Load 08H, the flag value written when the filespec ended cleanly.
5138
If the filespec ended cleanly (Z set), JUMP to 513BH keeping the 08H flag.
513A
XOR A AF
Otherwise set Register A to ZERO, clearing the terminator flag.
513B
LD (5098H),A 32 98 50
Self-Modifying Code
Store the terminator flag into address 5098H, the operand of the OR at 5097H in the FCB finalizer, which folds it into the FCB flag byte. The Z FLAG set here also reports parse success to the open path.
513E
RET C9
Return to the open path with the filespec parsed.
513FH - Set Password Field Mode
Called when the filespec contains a password. It patches the create path's directory access mode to the password value, then falls straight into the name-field scanner to copy the eight-character password.
513F
LD A,17H 3E 17
Load Register A with 17H, the directory access mode used when a password is present.
5141
LD (5030H),A 32 30 50
Self-Modifying Code
Store 17H into address 5030H, the immediate operand of the LD A,10H at 502FH, so the create path adopts the password access mode. Execution falls into the scanner below with Register B still holding the field width.
5144H - Scan Name Field
Copies up to B alphanumeric characters from the filespec at HL into the field buffer at DE, stopping at the first non-alphanumeric character, which it returns in Register A. The 5144H entry presets a width of 8 (the name); callers enter at 5146H with their own width for the extension and password. The first character is accepted through the letter test only, and subsequent characters are validated as digits or letters.
5144
LD B,08H 06 08
Load Register B with 8, the default field width for the eight-character name.
5146
LD A,(HL) 7E
Fetch the first filespec character at the address in HL.
5147
INC HL 23
Advance the filespec pointer.
5148
JUMP to 5153H so the first character is checked against the letter range only, bypassing the digit test.
514A
LD A,(HL) 7E
Loop Start
Fetch the next filespec character.
514B
INC HL 23
Advance the filespec pointer.
514C
CP 30H FE 30
Compare against 0 (30H). If the character is below 0 the CARRY FLAG is set.
514E
RET C D8
If the character is below 0 it is a field terminator, so return it in Register A.
514F
CP 3AH FE 3A
Compare against : (3AH), one past 9. If the character is a digit the CARRY FLAG is set.
5151
If the character is a digit (0-9), JUMP to 5159H to store it.
5153
CP 41H FE 41
Compare against A (41H). If below A the CARRY FLAG is set.
5155
RET C D8
If the character is below A (a separator like /, . or :), return it as the field terminator.
5156
CP 5BH FE 5B
Compare against [ (5BH), one past Z. If the character is a letter the CARRY FLAG is set.
5158
RET NC D0
If the character is above Z it is not a valid name character, so return it as the terminator.
5159
LD (DE),A 12
Store the accepted alphanumeric character into the field buffer at DE.
515A
INC DE 13
Advance the field buffer pointer.
515B
DECrement Register B and LOOP BACK to 514AH until the field is full or a terminator is reached.
Loop End
515D
LD A,(HL) 7E
The field filled completely; fetch the next character as the terminator to return.
515E
INC HL 23
Advance the filespec pointer past it.
515F
RET C9
Return with the terminating character in Register A.
5160H - Allocate Free Directory Slot
Computes the number of directory records this disk holds from the drive geometry, then scans the Hash Index Table already in the 4200H buffer for a free (zero) entry within that range. It seeds the scan position from the interrupt tick counter, skips the low HIT entries reserved by the system, and returns the Z FLAG with HL pointing at a free slot, or NZ when the directory is full. The scan limit is written into the self-modified compare at 5197H.
5160
LD A,07H 3E 07
Load Register A with 7, the DCT geometry index for the sectors-per-cylinder value.
5162
GOSUB to the resident SYS0 DCTBYT routine to read geometry byte 7 of the current drive's Drive Code Table into Register A.
5165
PUSH DE D5
Save DE across the geometry calculation.
5166
LD D,A 57
Keep the geometry byte in Register D.
5167
AND 1FH E6 1F
Keep the low five bits, the sectors-per-track portion of the geometry.
5169
LD E,A 5F
Hold that value in Register E.
516A
INC E 1C
Add one to convert the encoded count to the actual number of sectors.
516B
XOR D AA
Exclusive-OR with the full geometry byte to recover the upper bits that encode the directory sizing.
516C
RLCA 07
Rotate Register A left one bit.
516D
RLCA 07
Rotate left again.
516E
RLCA 07
Rotate left a third time, scaling the value to a multiplier.
516F
INC A 3C
Add one to form the multiplicand for the record-count multiply.
5170
GOSUB to the resident SYS0 MULTEA multiply helper to combine the sector count (Register E) and the multiplier (Register A) into the directory record count.
5173
LD E,A 5F
Hold the record-count result in Register E.
5174
LD A,04H 3E 04
Load Register A with 4, the DCT geometry index for the density/sides flags.
5176
GOSUB to SYS0 DCTBYT to read geometry byte 4 into Register A.
5179
BIT 5,A CB 6F
Test geometry bit 5, which selects whether the directory occupies a double-size layout.
517B
LD A,E 7B
Reload the record count into Register A.
517C
If bit 5 was clear, JUMP to 517FH keeping the single record count.
517E
ADD A,A 87
Otherwise double the record count for the larger directory layout.
517F
POP DE D1
Restore the saved DE.
5180
SUB 02H D6 02
Subtract 2 to exclude the GAT and HIT sectors, leaving the highest usable directory record index.
5182
LD (5198H),A 32 98 51
Self-Modifying Code
Store the directory record limit into address 5198H, the immediate operand of the CP at 5197H that bounds the free-slot scan.
5185
Load Register A with the SYS0 interrupt tick counter at 4040H, used to seed the scan start so new files spread across the directory.
5188
LD L,A 6F
Set the low byte of the HIT pointer to that seed (Register H remains 42H, the HIT buffer page).
5189
GOSUB to the slot test at 5194H to check whether the seeded HIT entry is free.
518C
RET Z C8
If the seeded slot was free, return with HL pointing at it.
518D
LD L,07H 2E 07
Otherwise reset the scan pointer to entry 7, past the low HIT entries the system reserves.
518F
INC L 2C
Loop Start
Advance the HIT scan pointer to the next entry.
5190
While the pointer has not wrapped past the end of the buffer, JUMP to 5194H to test the entry.
5192
OR H B4
The whole buffer was scanned; OR in Register H (42H) to force a non-zero result, signaling the directory is full.
5193
RET C9
Return NZ - no free directory slot on this drive.
5194
LD A,L 7D
Load Register A with the current HIT entry index (pointer low byte).
5195
AND 1FH E6 1F
Keep the low five bits, the entry position within its HIT block.
5197
CP 00H FE 00
Self-Modifying Code
Compare the entry position against the directory record limit; the immediate at 5198H was patched at 5182H. If the position is within the limit the CARRY FLAG is set.
5199
LD A,L 7D
Reload the full entry index without disturbing the compare flags.
519A
If the entry is within the usable directory range, JUMP to 51A1H to test whether it is free.
519C
OR 1FH F6 1F
Otherwise set the low five index bits so the next increment skips to the following HIT block boundary.
519E
LD L,A 6F
Store the advanced index back into the pointer.
519F
LOOP BACK to 518FH to continue scanning at the next block.
51A1
LD A,(HL) 7E
Fetch the HIT entry byte at the scan pointer.
51A2
OR A B7
Test it. A zero byte marks a free directory slot and sets the Z FLAG.
51A3
RET Z C8
If the slot is free, return with HL pointing at it.
51A4
Otherwise LOOP BACK to 518FH to try the next entry.
Loop End
51A6H - Reserved Workspace
Addresses 51A6H through 51BCH (23 bytes) are not present in the file image; the overlay's ORG jumps from the end of the free-slot allocator directly to 51BDH. This region is reserved, uninitialized overlay RAM lying between the allocator and the HIT read/write routine. No SYS2 code reads or writes it.
51BDH - Read / Write HIT Sector
Reads or writes this drive's Hash Index Table sector (sector 1 of the directory track) through the 4200H buffer. It is a dual-entry routine built from overlapping bytes: entering at 51BDH performs a read, while entering one byte later at 51BEH performs a write. Both entries share the sector setup and converge at 51BFH; the read/write choice is carried in the Z FLAG derived from the differing entry byte.
51BD
OR 0AFH F6 AF
Read entry. OR Register A with AFH so the result is non-zero (NZ), selecting the read path at 51CBH. The Read HIT Sector callers enter here.
51BE
XOR A AF
Overlapping Code
Write entry. This byte is the second byte (AFH) of the OR 0AFH instruction above, executed on its own as XOR A when the create path calls 51BEH. It clears Register A and sets the Z FLAG, selecting the write path at 51D4H. Both entries then fall through to 51BFH.
51BF
PUSH BC C5
Save Register BC across the directory access.
51C0
PUSH DE D5
Save Register DE.
51C1
PUSH AF F5
Save the read/write selector (the Z FLAG state) for the branch after the sector setup.
51C2
GOSUB to the resident SYS0 get-directory-track routine to position on this disk's directory cylinder.
51C5
LD E,01H 1E 01
Select sector 1 of the directory track, which holds the Hash Index Table.
51C7
LD HL,4200H 21 00 42
Point HL at the shared 4200H buffer, the source or destination for the HIT sector.
51CA
POP AF F1
Restore the read/write selector.
51CB
If the write entry was used (Z set), JUMP to 51D4H to write the sector; otherwise fall through to the read.
51CD
GOSUB to the resident SYS0 RDSSEC routine (read a sector into the directory buffer) to read the HIT sector into the 4200H buffer.
51D0
LD A,16H 3E 16
Load error code 16H (HIT read error) for the caller; the read routine's flags are preserved through this load.
51D2
JUMP to the common exit at 51DEH.
51D4
GOSUB to the resident SYS0 WRPROT primitive (disk operation 0EH), which writes the HIT sector held in the 4200H buffer back to the directory track with a deleted-data address mark, the standard LDOS way of writing directory sectors.
51D7
If the write succeeded, GOSUB to the resident SYS0 VERSEC primitive to verify the written sector.
51DA
CP 06H FE 06
Compare the primitive's status against 6; the resulting Z FLAG becomes the routine's success indication.
51DC
LD A,17H 3E 17
Load error code 17H (HIT write error) for the caller, without disturbing the compare flags.
51DE
POP DE D1
Restore Register DE.
51DF
POP BC C1
Restore Register BC.
51E0
RET C9
Return to the caller with the Z FLAG reflecting success and the appropriate error code in Register A on failure.
51E1H - Overlay Variable Block
Addresses 51E1H through 51F8H (24 bytes) are not present in the file image; the ORG jumps from 51E0H to 51F9H. This region is the overlay's run-time variable block, initialized by the code above rather than loaded from disk. Its fields are the one-byte filename hash (51E1H), the parsed password/name/extension buffer (51E2H-51F4H) and the 16-bit password hash (51F5H-51F6H).
51F9H - Tail Padding and Directory Template
The final bytes of the loaded image are zero padding. At run time the overlaid region from 51FBH holds the directory-record template staged by the create path (attribute byte at 51FBH) and the caller access flag at 51FFH.
51F9-51FE
NOP x 6 00 x 6
Six bytes of zero padding at the tail of the loaded image. The bytes from 51FBH onward double at run time as the directory-record template (51FBH attribute byte) written by the create path; the access flag variable at 51FFH sits just past the loaded image. The overlay ends here (END 4E00H).