Model I TRSDOS v2.3 SYS2/SYS Explained

This is a disassembly of TRSDOS v2.3’s SYS2/SYS file. SYS2/SYS handles the OPEN and INITIALIZE (i.e., NEW FILE) file management.


4E00H-4E11H – Test to see if options 10, 20, or 30 were passed, and if so, handle. If not, RETURN.

4E00
AND 70H
AND Register A against 01110000 so that only Bits 5, 6, and 7 are possible
4E02
CP 10H
Compare Register A against 10H, which is only Bit 5 high
4E04
If its a match, JUMP to the OPEN routine at 4E12H
4E07
CP 20H
Compare Register A against 20H, which is only Bit 6 high
4E09
If its a match, JUMP to the INIT routine at 4ED8H
4E0C
CP 30H
Compare Register A against 30H, which is only Bit 7 high
4E0E
If its a match, JUMP to the CREATE OVERFLOW routine at 4ED8H
4E11
RET
If it wasn’t 10H, 20H, or 30H then return to the caller

4E12H-4ED7H – The OPEN Routine. This routine saves the registers, copies the filename passed to this routine into a buffer, computes a hash code for that filename, computes the password value (if any), searches the HIT table for a matching hash code, and reads the associated directory entry and sets up the DCB if found.

4E12
GOSUB to 4896H to save the Registers and set up the return sequence to 48B3H to restore context and RETURN
4E15
LD A,B
Register B currently holds the record length, transfer that to Register A since you can’t load a memory location with the contents of Register B
4E16
LD (4FAFH),A
Save the record length in memory location 4FAFH
4E19
LD (4FC0H),HL
Save the callers sector buffer location address in memory location 4FC0H
4E1C
PUSH IX
Register Pair IX holds the DCB address. The only way to put the contents of Register Pair IX into Register Pair HL is to use the stack, so PUSH Register Pair IX to the Stack
4E1E
POP HL
… and POP the value into HL
4E1F
GOSUB to 5027H to move the filename to the local DCB
4E22
RET NZ
If that routine returns NZ, then there are illegal characters in the filename, so RETURN out of the routine
4E23
LD HL,515DH
If we are here, there were no illegal characters in the filename, so put the start of the local DCB buffer which starts at 515DH, into Register Pair HL
4E26
GOSUB to 509BH to compute the hash code for the filename and put it in Register A
4E29
LD (4E4EH),A
Put the hash code of the filename (held in Register A) into memory location 4E4EH
4E2C
LD DE,5155H
Put the address of the password buffer (memory location 5155H), into Register Pair DE. The pasword is a 16 bit value
4E2F
GOSUB to 50D1H to encode the password and store the result in Register Pair HL
4E32
LD (4168H),HL
Save that encode into the local DCB at both 4168H
4E35
LD (416AH),HL
… and 416AH
4E38
LD A,(5154H)
Fetch the drive specification flag from memory location 5154H and store it in Register A
4E3B
LD C,A
Copy Register A into Register C
4E3C
INC A
Bump Register A as a way to test to see if none was given. If that’s the case, it was 0FFH which is now 00H, triggering a Z or NZ flag
4E3D
If there was a drive specified, then the flag is NZ so JUMP down to 4040H
4E3F
LD C,A
If we are here, no drive was specified, and A was bumped from 0FFH to 00H. So now Register C has a 0 as the starting drive spec
4E40
GOSUB to 50FDH to see if the drive spec held in Register C exists
4E43
If that routine returns a NZ, the drive is NOT present, so JUMP to 4E54H to increase to the next drive, if any
4E45
If we are here, the drive WAS present, so GOSUB to 5121EH to get the table and read it into 4D00H
4E48
RET NZ
If that routine returns a NZ, there was an error. So RETURN to the caller due to error
4E49
LD A,(HL)
If we are here then the table was read without an error, and (HL) is pointing to the hash code for the HIT entry. Put the hash code for the HIT entry into Register A
4E4A
OR A
Set the status flags for the hash code for the HIT
4E4B
If the Z flag is set, the slot is empty, so JUMP to 4E51H
4E4D
CP 00H
Compare the READ hash code from the HIT to the computed hash code for the HIT
4E4F
If the Z flag is set, the hash codes match, so JUMP to 4E6AH (the routine to read the directory sector which contains the file entry matching the found hash code)
4E51
INC L
Bump Register L to point to the next location in the hash table
4E52
If that bump returns a NZ flag, then there are still more entries in the hash table to read, so JUMP BACK to 4E49H to read another one
4E54
LD A,(5154H)
If we are here, then we ran out of entries and had no match, so there was no file to be found on that drive spec. With this, we need to move to the next drive, so fetch the drive specification from memory location 5154H into Register A
4E57
INC A
… and increase Register A by 1 to point to the next drive spec
4E58
If that increase is NZ, then we had been given a drive spec, in which case the file is just not found, and JUMP forward to 4E60H to set the FILE NOT FOUND error and return
4E5A
INC C
If we’re here then the file wasn’t found AND we weren’t given a drive spec, so we need to check the next drive. BUMP Register C to point to the next drive
4E5B
LD A,C
Put Register C into Register A since you cannot test Register C in the Z-80
4E5C
CP 04H
Check that new drive spec against 04H which is 1 more than the highest drive spec possible (out of :0, :1, :2, and :3)
4E5E
Since CP returns C set if A is less than the test value (04H), there are more drives to search so JUMP to 4E40H
4E60
LD A,18H
If we are here, then we have exhausted the number of drives to search, so put an 18H into Register A. 18H is the error code for FILE NOT FOUND
4E62
OR A
Set the status flags
4E63
RET
RETURN

4E64H-4E69H – A subroutine in the main OPEN Routine which clears the stack and prepares to search the HIT table.

4E64
POP BC
Empty an unnecessary entry in the stack
4E65
POP HL
Empty another unnecessary entry in the stack
4E66
POP BC
Restore the drive number to the C register
4E67
POP HL
Restore the current hash sector index to Register Pair HL
4E68
JUMP to 4E51H to test the next hash value

4E6AH-4E74H – A subroutine in the main OPEN Routine which reads the directory sector which contains the file entry that matches the found HIT hash code.

4E6A
PUSH HL
Save the hash code address into the Stack
4E6B
PUSH BC
Save Register B and the drive number (held in Register C) to the Stack
4E6C
LD B,L
HL holds the has code address, with L being the address of the entry in the directory, so put that into Register B
4E6D
GOSUB to 4AC1H to read that director sector into the buffer at 4200H
4E70
If that routine returns a Z, then there was no error. If there was no error, then JUMP forward to 4E75H
4E72
POP BC
If we are here, there was an error. So we need to clear the stack
4E73
POP HL
Clear the stack
4E74
RET
RETURN with an error status

4E75H-4E8BH – A subroutine in the main OPEN Routine which reads locates the file entry in the directory sector.

4E75
PUSH HL
HL currently holds the address of the file entry in the directory sector. Save it to the stack
4E76
PUSH BC
BC currently holds the directory index (in B) and drive number (in C). Save those to the stack
4E77
BIT 07H,(HL)
Check bit 7 of Register Pair HL which identifies the primary directory entry
4E79
If that test produced a NZ, then it is NOT the primary entry, so JUMP back to 4E64H to go to the next directory entry
4E7B
LD A,05H
If we are here, then we have found the primary entry. First offset 5 bytes so as to reach the filename in the directory entry
4E7D
ADD A,L
Add that 5 to Register L which held the address of the entry
4E7E
LD L,A
Put that increased value back into Register L so now Register Pair HL holds the address of the filename in the directory
4E7F
LD DE,515DH
Put the address of the local DCB (which contains the name), of 515DH, into Register Pair De
4E82
LD B,0BH
There are a maximum of 11 characters in a filename, so load up Register B with 11
4E84
LD A,(DE)
Fetchthe current character to test (pointed to in the local DCB) into Register A
4E85
CP (HL)
Compare the character from the local DCB against the character in the directory sector
4E86
If there is NO MATCH, then JUMP back to 4E64H to go to the next directory entry
4E88
INC HL
BUMP HL by 1 to point to the next character of the filename in the directory sector
4E89
INC DE
BUMP DE by 1 to point to the next character of the filename held in the DCB
4E8A
Perform a LOOP to check all 11 characters

4E8BH – A subroutine in the main OPEN Routine which gets the access permissions and password encodes once the filename has been located in the directory sector

4E8C
POP BC
Restore the drive number from the stack (which will be stored in Register C)
4E8D
LD A,C
Put the drive number into Register A
4E8E
LD (5154H),A
Put the drive number into memory location 5154H
4E9l
POP HL
Restore the stack into Register Pair HL. This would be the directory entry sector address which contains the filename
4E92
POP AF
Clear the stack
4E93
POP AF
Clear the stack
4E94
PUSH BC
Save the drive number to the stack
4E95
PUSH HL
Save the directory entry sector address which contains the filename to the stack
4E96
LD A,(HL)
Fetch the first byte of the directory entry which contains the filename to the stack
4E97
AND 07H
AND it against 0000 0111 to isolate the first 3 bits, which hold the permissions
4E99
LD C,A
Store that modified permission byte into Register C
4E9A
LD B,00H
Put a 00H (no restriction) into Register B
4E9C
LD A,10H
Since the password is 16 bytes away, put a 16 into Register A
4E9E
ADD A,L
Combine the first byte of the directory entry which contains the filename with the offset to get to the UPDATE PASSWORD
4E9F
LD L,A
Put that value into Register L so that Register Pair HL will now point to the UPDATE PASSWORD in the directory entry
4EA0
LD DE,(516AH)
Get the encoded password from memory location 516AH and put it into Register Pair DE
4EA4
PUSH HL
Save the location of the UPDATE PASSWORD in the directory entry to the stack
4EA5
LD HL,61A2H
Put 61A2H into Register Pair HL
4EA8
XOR A
Clear everything
4EA9
SBC HL,DE
Subtract with Carry 61A2H from the encoded password (held in Register Pair DE)
4EAB
POP HL
Restore the address of the password into Register Pair HL
4EAC
JUMP to 4ECFH to fill in the DCB

4EAEH-4ECEH – A subroutine in the main OPEN Routine which does not seem to ever get called. I don’t know what its doing without SOME context

4EAE
LD A,C
Put the contents of Register C into Register A so it can be tested
4EAF
CP 07H
Compare against 07
4EB1
JR Z,4EC9H
If A is 07, JUMP to the bottom of this routine which clears the stack, sets ERROR CODE 19H (“FILE ACCESS DENIED”), and RETURNS
4EB3
LD A,(HL)
Fetch the character held in (HL) into Register A
4EB4
INC HL
Bump Register Pair HL
4EB5
PUSH HL
Save Register Pair HL to the stack
4EB6
LD H,(HL)
Whatever the incremented (HL) is pointing to, put it in Register H
4EB7
LD L,A
Put Register A (which was the memory contents of the prior memory location pointed to by Register Pair HL) into Register L
48B8
XOR A
Clear the flags
4EB9
SBC HL,DE
Subtract with Carry HL from DE
4EBB
POP HL
Restore HL from the Stack
4EBC
JR Z,4ECFH
If the Z flag is set, to 4ECFH to fill in the DCB
4EBE
INC HL
Bump Register Pair HL
4EBF
LD B,C
Copy Register C into Register B
4EC0
LD A,(HL)
Fetch the character pointed to by HL into Register A
4EC1
INC HL
Bump Register Pair HL
4EC2
LD H,(HL)
Whatever the incremented (HL) is pointing to, put it in Register H
4EC3
LD L,A
Put Register A (which was the memory contents of the prior memory location pointed to by Register Pair HL) into Register L
4EC4
XOR A
Clear the flags
4EC5
SBC HL,DE
Subtract with Carry HL from DE
4EC7
JR Z,4ECFH
If the Z flag is set, to 4ECFH to fill in the DCB
4EC9
POP HL
Restore HL from the stack
4ECA
POP BC
Restore BC from the stack
4ECB
LD A,19H
If we are here, then we have some form of error. Load error code 19H (“FILE ACCESS DENIED”) into A
4ECD
OR A
Set the flags so Z is set, which I am assuming means an error
4ECE
RET
RETURN out

4ECFH – This continues the prior prior routine (i.e., picks up where the last routine jumped out; skipping the prior routine)

4ECF
POP HL
Restore the directory entry sector address which contains the filename to Register Pair HL
4ED0
LD A,B
Put the contents of Register B (which holds the access permission) into Register A
4ED1
LD (4FB2H),A
Save the access permission to the DCB
4ED4
POP BC
Restore the drive number from the stack (into Register C)
4ED5
JUMP to 4FA7H to complete the DCB now that all the variables are set

4ED8H-4F4FH – Main Routine # 2 – The INIT Routine.

4ED8
GOSUB to 4896H to save the Registers and set up the return sequence
4EDB
GOSUB to the OPEN routine
4EDE
RET Z
If the OPEN routine returns with Z set, then a file was found, so RETURN
4EDF
CP 18H
If we are here, then OPEN did not find a file, so test Register A for 18H (a FILE NOT FOUND error code)
4EEl
RET NZ
If there was no file found AND a FILE NOT FOUND was not set, then we have some other error, so RETURN
4EE2
LD A,10H
Put a 10H (entry assigned / unrestricted access) into Register A. This will be byte 0 of the directory entry to be assigned to the new file
4EE4
LD (4F1DH),A
Store this in memory location 4F1DH to signify that the entry is assigned for unrestricted access
4EE7
LD A,(5154H)
Fetch the drive spec from 5154H and put it into Register A
4EEA
LD C,A
Place the drive spec into Register C
4EEB
INC A
Bump the drive spec held in Register A
4EEC
If a drive number was specified (meaning A didn’t used to be FF) theN JUMP down to 4EEFH
4EEE
LD C,A
If we are here, then a drive number was specified (held in Register A), so put that drive number into Register C
4EEF
GOSUB down to 50FDH to re-test the drive held in Register A/C
4EF2
If the drive is NOT present, JUMP DOWN to 4EFFH
4EF4
If the drive is NOT ready, also JUMP DOWN to 4EFFH
4EF6
If we are here, the drive is present and ready so GOSUB to 512EH to read the hash table
4EF9
RET NZ
If the hash table read routine of 512EH returns NZ then there was an error during the HIT read, so RETURN
4EFA
If we are here, then there was a successful HIT read, so GOSUB to 50ABH to find the first available slot in the HIT table
4EFD
If that routine returned a Z, then a slot was found, so JUMP DOWN to 4F0EH
4EFF
LD A,(5154H)
If we are here, then a slot was not found. Fetch the drive spec (stored at 5154H) and put it into Register A
4F02
INC A
Bump Register A to see if a drive spec was specified
4F03
If NZ was returned, then a drive spec WAS specified, meaning that the directory is full and there is nowhere else to go, so JUMP DOWN to 4F0BH
4F05
INC C
If we are here then no drive spec was specified so we need to try to see if another drive is available. Bump Register C to point to the next drive
4F06
LD A,C
Since we cannot test Register C, we must load Register C (contaning the next drive number to check) into Register A
4F07
CP 04H
Compare the next drive number to check (held in Register A) against 04H
4F09
If the Carry Flag is set, then the next drive number is less than 04H, meaning its ok to check, so JUMP BACK to 4EEFH
4F0B
LD A,1AH
If we are here, then we ran out of drives to check, so load Register A with 1AH (“SPACE FULL error”)
4F0D
RET
… and exit

4F0EH – A continuation of the INIT Routine to process when an available HIT entry was found.

4F0E
LD B,L
Put the directory sector number into Register B
4F0F
LD A,(4E4EH)
Fetch the hash code from memory location 4E4EH and put it into Register A
4Fl2
LD (HL),A
Put the hash code into the HIT (pointed at by HL)
4Fl3
CALL 5141H
GOSUB to 5141H to write the updated HIT
4Fl6
RET NZ
If that routine returns with NZ flag set, then there was an error writing the HIT, so RETURN
4Fl7
If we are here then the HIT wrote correctly, so GOSUB to read the director sector that will contain the file entry
4FlA
RET NZ
If that routine returns with the NZ flag set, then there was an error reading that directory sector, so RETURN
4FlB
PUSH HL
If we are here then the directoy sector read fine, so Save the starting address of that directory entry to the stack
4FlC
LD (HL),00H
Put a 00H into that starting address to clear the ACCESS CONTROL byte of the directory entry
4FlE
INC HL
Bump HL to the OVERFLOW POINTER byte
4FlF
LD (HL),00H
Put a 00H into the next byte to clear the OVERFLOW POINTER byte of the directory entry
4F2l
INC HL
Bump HL to the RESERVED BYTE
4F22
LD (HL),00H
Put a 00H into the next byte to clear the RESERVED byte of the directory entry
4F24
INC HL
Bump HL to the EOF OFFSET BYTE
4F25
LD (HL),00H
Put a 00H into the next byte to clear the EOF OFFSET byte of the directory entry
4F27
INC HL
Bump HL one more time (to the 5th byte of the directory entry)
4F28
LD A,(4FAFH)
Fetch the record length (held at 4FAFH) into Register A
4F2B
LD (HL),A
Put the record length into the 5th byte of the directory entry
4F2C
INC HL
Bump HL one more time (to the 6th byte of the directory entry)
4F2D
PUSH BC
Save Register Pair BC to the Stack so we can use BC for a LDIR
4F2E
LD BC,000FH
Put a 15 into Register Pair BC
4F31
EX DE,HL
Swap Register Pair DE with Register Pair HL, so that DC is pointing to the 6th byte of the directory entry (which is the filename)
4F32
LD HL,515DH
Store 515DH (the DCB address) into Register Pair HL
4F35
LDIR
LOOP to copy the filename into the directory entry
4F37
EX DE,HL
Swap Register Pair DE with Register Pair HL, so that HL is now pointing to the 15th byte of the directory entry which is the EOF Sector Number (LSB)
4F38
LD (HL),00H
Set the EOF Sector Number (LSB) to zero
4F3A
INC HL
Bump HL one more time (to the 16th byte of the directory entry) which is the EOF Sector Number (MSB)
4F3B
LD (HL),00H
Set the EOF Sector Number (MSB) to zero
4F3D
INC HL
Setting up for a DJZN loop so first bump HL one more time (to the 17th byte of the directory entry)
4F3E
LD B,0A0H
Load Register B with a 10 (which is the number of bytes to initialize in the DJNZ routine)
4F40
LD (HL),0FFH
Put a FF into the memory location pointed to by HL
4F42
INC HL
Bump HL to point to the next memory location / byte of the directory entry
4F43
JUMP back to 4F40H until all 10 bytes have been written to the directory entry
4F45
POP BC
Restore the stack to Register Pair BC so that BC now holds the HIT slot index used to derive the director sector number being worked on
4F46
GOSUB to 4AD6H to write out the updated directory sector
4F49
POP HL
Restore the stack to Register Pair HL so that HL holds the directory entry address
4F4A
RET NZ
If that GOSUB to write out the sector returned an error, RETURN
4F4B
If there was no error, then GOSUB to 4FA7H to initialize the DCB in the user’s area
4F4E
SCF
Set the carry flag to signal that the file was not previously opened
4F4F
RET
RETURN

4F50H – Main Routine # 3 – The OVERLOW DIRECTORY ENTRY CREATION Routine.

This routine will look for an available slot in the HIT sector. If one is found, it will put the hash code into that slot, write the updated HIT sector, read the directory sector for that HIT slot, and initialize the directory sector as an OVERFLOW entry. It will then read the directory sector for the PRIMARY entry, update it to trigger the overflow flag and set the pointer, and write the updated primary directory entry back.

4F50
LD C,(IX+06H)
Put the DRIVE NUMBER of the drive where the chosen file exists into into Register C
4F53
GOSUB to 512EH to read the HIT
4F56
RET NZ
If the GOSUB returns with an error, RETURN
4F57
LD C,(IX+07H)
Put the DIRECTORY SECTOR OFFSET for the chosen file into Register A
4F5A
GOSUB to 50B6H to find an available slot in the HIT table
4F5D
LD A,1EH
Prepare for an “DIRECTORY FULL” error by loading the error code 1E into A
4F5F
RET NZ
If the hash table was full, RETURN
4F60
LD B,L
If we are here, the hash table was not full and a slot was found in the HIT table for the overflow entry. Put that available slot into Register B
4F61
LD A,L
Also put that available slot into Register A
4F62
LD (4FA2H),A
Put that available slot for the overflow entry into memory location 4FA2H
4F65
LD D,H
At this point HL is holding the HIT buffer. Put the MSB of the HIT buffer memory location into Register D
4F66
LD E,(IX+07H)
Put the index for this file into Register E, to form a Register Pair DE which has the MSB and LSB of the HIT buffer
4F69
LD A,(DE)
Fetch the hash code for that HIT buffer entry and put it into Register A
4F6A
LD (HL),A
Put that has code into the memory location pointed to by HL, which is the overflow entry
4F6B
GOSUB to 5141H to write the updated HIT entry into the table
4F6E
RET NZ
If there was an error reported, RETURN out of this routine
4F6F
If there was no error reported, and the updated HIT entry was written, GOSUB to 4AC1H to read the directory sector of that HIT slot
4F72
RET NZ
If there was an error reported, RETURN out of this routine
4F73
LD (HL),90H
If we are here, then we successfully read the directory sector of that HIT slot. Flag the directory entry with a 90H to indicate that it is assigned as an overflow
4F75
INC L
Bump L to point to the next byte of the directory entry pointing to the overflow entry
4F76
PUSH BC
Save BC to the stack, so that the drive number in Register C is preserved
4F77
LD A,(4ABEH)
Since the directroy sector index for the chosen file is held at 4ABEH, store that into Register A
4F7A
LD (HL),A
And then store that into the 2nd byte of the overflow entry
4F7B
INC L
Bump L to point to the next byte of the directory entry
4F7C
LD B,14H
In preparation for a DJZN loop, load Register B with the number of bytes to zero in the directory entry, which would otherwise be the filename and password
4F7E
LD (HL),00H
Zero a byte in the directory entry
4F80
INC L
Bump L to point to the next byte in the directory entry to zero
4F81
LOOP back to 4F7EH until all 20 bytes have been zero’ed
4F83
PUSH HL
Now that 22 bytes of the overflow directory entry have been set, store the pointer to the next byte (which will be the first GAP) in the directory entry to the stack
4F84
LD B,0AH
In preparation for a DJZN loop, load Register B with the number of bytes to set to FFH in the directory entry, which would otherwise be the GAP area
4F86
LD (HL),0FFH
Put a FFH into that directory entry
4F88
INC L
Bump L to point to the next byte in the directory entry to set to FFH to fill the Gap area with FFH
4F89
LOOP back to 4F86H until all 10 bytes of the first GAP have set to FFH
4F8B
POP DE
Restore the address of the first GAP entry from the stack
4F8C
INC DE
Bump to the 17th byte (which is the number of grans)
4F8D
POP BC
Restore the drive number contaning the file to Register C
4F8E
GOSUB to 4AD6H to write the updated (i.e., now containing the overflow entry) directory sector
4F91
RET NZ
If there was an error writing directory sector, RETURN
4F92
LD A,(4ABEH)
If there was no error writing the updated directory sector, Fetch put the sector number for the primary entry into Register A
4F95
LD B,A
and into Register B
4F96
GOSUB to 4AC1H to read the primary entry sector
4F99
RET NZ
If there was an error reading the primary sector, RETURN
4F9A
LD A,L
If we are here, then there was no error reading the primary entry in the directory sector so put the LSB of the primary entry into Register A
4F9B
ADD A,1EH
Bump that location by 30 to skip to first byte of the FIFTH Gap Entry
4F9D
LD L,A
Put that offset into Register L so that Register Pair HL now points to the directory entry
4F9E
LD (HL),0FEH
Put a FEH into that byte to signal that there is an overflow entry which also applies to that directory entry
4FA0
INC L
Bump Register L to point to the second sector of the 5th Gap entry
4FAl
LD (HL),00H
Put a 00H into the second byte
4FA3
GOSUB to 4AD6H to write the updated primary directory entry
4FA6
RET
RETURN out of the main OVERFLOW routine

4FA7H – A subroutine in the main OVERFLOW ENTRY CREATION routine which fills out the DCB by copying portions of the primary entry to the DCB. Register Pair DE needs to point to the address of the directory entry and HL needs to point to the addess of the DCB.

4FA7
EX DE,HL
Swap Register Pairs DE and HL, so now HL points to address of the directory entry and DE points to the address of the DCB
4FA8
PUSH IX
Save Register IX (which is the address of the callers DCB) to the stack so it can go into HL
4FAA
POP HL
Restore the stack to HL
4FAB
LD (HL),80H
Put an 80H into the first byte of the DCB to signal that the file is open
4FAD
INC HL
BUMP HL by 1 to move to the second byte of the DCB
4FAE
LD A,00H
Put a 00H into the Register A to signal a zero record length
4FB0
OR A
Set the status flags
4FB1
LD A,00H
Put a 00H into the Register A to signal a zero record length
4FB3
If the Z Flag was set (meaning that the record length is 0), skip the next instruction by JUMPing to 4FB7H
4FB5
OR 80H
If we are here, then the record length was not zero, so we need to set flags to signal logical I/O
4FB7
OR 20H
… and to force read on first access
4FB9
LD (HL),A
Put that adjusted status into the second byte of the DCB
4FBA
INC HL
BUMP HL by 1 to move to the third byte of the DCB (an unused byte)
4FBB
LD (HL),00H
Put a 00H into the third byte of the DCB, as that byte is not used
4FBD
INC HL
BUMP HL by 1 to move to the 4th byte of the DCB (the LSB of the sector buffer address)
4FBE
PUSH DE
Save Register Pair DE (the original DCB address) to the stack
4FBF
LD DE,0000H
Initialize Register Pair DE with 00’s as the sector buffer address
4FC2
LD (HL),E
Get the LSB of the sector buffer address and put it into Register E
4FC3
INC HL
BUMP HL by 1 to move to the 5th byte of the DCB (the MSB of the sector buffer address)
4FC4
LD (HL),D
Get the MSB of the sector buffer address and put it into Register E
4FC5
INC HL
BUMP HL by 1 to move to the 6th byte of the DCB (the end of the current record)
4FC6
POP DE
Restore Register Pair DE from the stack so that DE holds the directory entry address
4FC7
LD (HL),00H
Set the end of the current record to 00H
4FC9
INC HL
BUMP HL by 1 to move to the 7th byte of the DCB
4FCA
LD (HL),C
Save the drive number (held in Register C) to the 7th byte of the DCB
4FCB
INC HL
BUMP HL by 1 to move to the 8th byte of the DCB (the overflow pointer)
4FCC
LD (HL),B
Save the overflow pointer held in Register B to the 8th byte of the DCH
4FCD
INC HL
BUMP HL by 1 to move to the 9th byte of the DCB
4FCE
LD A,03H
Put an 03 into Register A to signify the index to the EOF byte in the directory
4FD0
ADD A,E
Add 03 to the LSB of the address of the base of the directory sector
4FDl
LD E,A
Transfer that revised number back to Register E so Register Pair DE points to the EOF byte offset from the directory entry
4FD2
LD A,(DE)
Load the EOF byte offset from the directory entry into Register A
4FD3
LD (HL),A
Save EOF byte offset from the directory entry into the 9th byte of the DCB
4FD4
INC HL
BUMP HL by 1 to move to the 10th byte of the DCB (record length)
4FD5
INC DE
BUMP DE by 1 to point to the next byte of the directory
4FD6
LD A,(4FAFH)
Load the record length from memory location 4FAFH into Register A
4FD9
LD (HL),A
Put the record length into the 10th byte of the DCB
4FDA
INC HL
BUMP HL by 1 to move to the 11th byte of the DCB (LSB of the next record number)
4FDB
LD (HL),00H
Clear the LSB of the next record number
4FDD
INC HL
BUMP HL by 1 to move to the 12th byte of the DCB (MSB of the next record number)
4FDE
LD (HL),00H
Clear the MSB of the next record number
4FE0
INC HL
BUMP HL by 1 to move to the 13th byte of the DCB
4FE1
LD A,10H
Load Register A with a 10H to signify an EOF sector number
4FE3
ADD A,E
Add to the base of the directory sector address
4FE4
LD E,A
Take the adjusted address and load it into Register E so that Register Pair DE now holds the EOF sector
4FE5
LD A,(DE)
Get the LSB of the EOF sector and put it into Register A
4FE6
LD (HL),A
Put the LSB of the EOF sector into 13th byte of the DCB
4FE7
INC HL
BUMP HL by 1 to move to the 14th byte of the DCB
4FE8
GOSUB to 5180H to copy the MSB of number of sectors
4FEB
INC HL
BUMP HL by 1 to move to the 15th byte of the DCB (first track address)
4FEC
INC DE
BUMP DE by 1 to the first track address in the directory
4FED
LD A,(DE)
Get the starting track number from the directory (held in DE) and put it into Register A
4FEE
LD (HL),A
Put that starting track number in the 15th byte of the DCB
4FEF
INC HL
BUMP HL by 1 to move to the 16th byte of the DCB (granule count for the first GAP)
4FF0
INC DE
BUMP DE by 1 to now point to the directory sector and LSB of the accumulated granule
4FF1
LD A,(DE)
Get the number of granules from the directory (held in DE) and put it into Register A
4FF2
LD (HL),A
Put the number of granules into the 16th byte of the DCB
4FF3
INC HL
BUMP HL by 1 to move to the 17th byte of the DCB (track number for the second GAP)
4FF4
AND 1FH
Register A currently holds the number of granules. This instruction ANDs A against 00011111 to isolate bits 0-5 to get the number of consecutive granules -1 for the first GAP
4FF6
INC A
Bump A by 1 so that A now holds the number of consecutive granules of the first GAP
4FF7
LD C,A
Copy Register A to Register C so that
4FF8
LD B,00H
… Register Pair BC will now hold the total number of granules
4FFA
LD A,04H
Put a 4 into Register A to hold the nuymber of extant pairs remaining
4FFC
PUSH AF
Save that counto the stack
4FFD
INC DE
BUMP DE by 1 to the point to the next GAP
4FFE
LD A,(DE)
Fetch the next GAP from DE
4FFF
CP 0FEH
Compare it to FEH which would be the end of the GAP or the overflow pointer
5001
JR NC,501CH
Since CP returns C reset if A is greater than or equal to 0FEH, JUMP to 501CH if we are at the end of the GAP or the overflow pointer is found
5003
LD (HL),C
If we are here, then we are not at the end or in overflow, so save the LSB of the total granules into the 17th byte of the DCB
5004
INC HL
BUMP HL by 1 to move to the 18th byte of the DCB
5005
LD (HL),B
Save the MSB of the total granules into the 18th byte of the DCB
5006
INC HL
BUMP HL by 1 to move to the 19th byte of the DCB (first byte of the next GAP)
5007
LD A,(DE)
Put the track for the next GAP into Register A
5008
LD (HL),A
Store the track for the next GAP into the 19th byte of the DCB
5009
INC HL
BUMP HL by 1 to move to the 20th byte of the DCB (second byte of the next GAP)
509A
INC DE
BUMP DE by 1 to now point to the count of assigned granules in the directory sector
5008
LD A,(DE)
Store the count of assigned granules into Register A
500C
LD (HL),A
Store the count of assigned granules into the 20th byte of the DCB
500D
INC HL
BUMP HL by 1 to move to the 21st byte of the DCB (the LSB of the total granules)
590E
AND 1FH
AND A against 00011111 to isolate bits 0-5 to get the count from the starting sector number
5010
INC A
Bump A by 1 so that it will contain the accurate granule count
5011
ADD A,C
Add Register C to Register A to ghet the new total granule count
5012
LD C,A
Put the new total granule count into Register C
5013
LD A,B
Put the number of GAPS so far (from Register B) into Register A
5014
ADC A,00H
ADD WITH CARRY 00 to A to set the carry flags to indicate if an overflow has occcurred
5016
LD B,A
Put Register A into Register B, so that Register Pair BC now hold the total number of granules accessible
5017
POP AF
Restore the number of GAPs to copy
5018
DEC A
DECrement A by 1 to see if we have hit 0 and run out of GAPs to copy
5019
If we have not hit zero and run out of GAPs to copy, LOOP back up to 4FFCH to move to the next GAP
501B
RET
If we are here then we have run out of GAPs to copy, so exit out of the subroutine

501CH-5026H – A subroutine in the main OVERFLOW ENTRY CREATION which is called from 5001H since we are either at the end of the GAP or an overflow pointer was found. If that happens we need to fill the remainder of the DCB GAP areas with FF’s.

501C
POP AF
Restore the number of unfilled entries from the stack
501D
RLCA
RLCA
Multiply it by 4 to get the byte count
501F
LD B,A
Copy that byte count into Register B for purposes of a DJNZ loop
5020
LD (HL),0FFH
Put a 0FFH into the DCB
5022
INC HL
Bump HL by 1. HL is still pointing to the DCB
5023
LOOP until all those DCB bytes have been filled with a FFH
5025
XOR A
Clear all flags to signal a GOOD status
5026
RET
RETURN

5027H – A subroutine in the main OVERFLOW ENTRY CREATION which is called from 4ElF to move the filename to the local/internal DCB. The filename is held at 515DH for 8 bytes, the password is held at 5155H for 8 bytes, the extension is held at 5165H for 3 bytes, and the drive spec is stored at 5154H.

5027
LD B,08H
Set up a LOOP with the number of characters to copy (i.e., 11) for the filename
5029
LD DE,515DH
Put the start of the DCB buffer (which starts at 515DH) into Register Pair DE
502C
LD A,20H
Load Register A with an ASCII space
502E
LD (DE),A
Put an ASCII space into the current byte of the local DCB buffer (which is filling the filename)
502F
INC DE
BUMP DE to the next byte of the local DCB buffer
5030
LOOP back 2 instructions to fill 11 bytes with spaces
5032
LD B,08H
Set up a LOOP with the number of characters to copy (i.e., 11)
5034
LD DE,5155H
Put the start of the password in the local DCB buffer (which starts at 5155H into Register Pair DE
5037
LD (DE),A
Put an ASCII space into the current byte of the local DCB buffer
5038
INC DE
BUMP DE to the next byte of the local DCB buffer
5039
LOOP back 2 instructions to fill 11 bytes with spaces
503B
LD A,0FFH
Set up to signal that no drive spec was provided
503D
LD (5154H),A
Put the drive spec into 5154H
5040
LD DE,515DH
Put the start of the local DCB buffer (which starts at 515DH) into Register Pair DE
5043
LD B,08H
Prepare for the next GOSUB by setting Register B to 8
5045
GOSUB to 5081H to move the number of characters held in B into a buffer at 515DH. In this case, it’s the filename
5048
LD C,A
Register A is currently still pointing to the last character of the filename, so put that into Register C
5049
LD A,B
Load Register A with 8 minus the number of characters copied
504A
CP 08H
Check Register A against 8
504C
If the number of characters copied is not 8 (meaning, the number of characters was legitimate), JUMP to 5052H
504E
LD A,13H
If we are here, the filename was not legitimate, so set Register A with the error code 13H (“ILLEGAL CHARACTER”)
5050
OR A
Set the status flags
5051
RET
RETURN

5052H – JUMPed to from 504C if the number of characters copied was legitimate

5052
LD A,C
Put the last character of the filename into Register A
5053
CP 2F
Compare that character against a “/”
5055
If it is not a “/” then JUMP to 505FH to skip the extension reading routine
5057
LD DE,5165H
If we are here then there is an extension and we need to read it
505A
LD B,03
Put the number of characters of an extension into Register B
505C
GOSUB to 5081H to move the number of characters held in B into a buffer at 515DH. In this case, it’s the extension
505F
CP 2EH
Compare the final character character against a “.”
5961
If it is not a “.” then there is no password, so JUMP down to 506BH to skip the password reading routine
5063
LD DE,5155H
Put the address of the password buffer (memory location 5155H), into Register Pair DE. The pasword is a 16 bit value
5066
LD B,08H
Put the number of characters of a password into Register B
5068
GOSUB to 5081H to move the number of characters held in B into a buffer at 515DH. In this case, it’s the password
506B
CP 3AH
Compare the final character character against a “:”
506D
If it is not a “:” then JUMP down to 507FH to skip reading the drive numer
506F
LD A,(HL)
Get the drive number from the current pointer held by HL
5070
SUB 30H
Subtract 30H from that drive number to convert it from ASCII to decimal
5072
If the Carry Flag is set then the character was not a digit, so JUMP to 5078H to set the error and exit
5074
CP 04H
Compare the drive number against 4
5076
If the drive number is less than 4, JUMP down to 507CH to continue
5078
LD A,20H
If we are here the drive number was not less than 4, so set an error code 20H
507A
OR A
Set the flags
5078
RET
RETURN
507C
LD (5154H),A
If we are here, the drive number is less than 4, so store it into 5154H
507F
XOR A
Set the flags for a GOOD status
5080
RET
RETURN

5081H – This routine copies the number of characters held in B from the location pointed to by HL into a buffer at held in DE and validates them to ensure they are either a letter or a number. At the end of the routine Register A has the next character and HL points to the character after that.

5081
LD A,(HL)
Put the current character pointed to by HL into Register A for testing
5082
INC HL
BUMP HL by one to point to the next character
5083
JUMP down to skip the numerical test
5085
LD A,(HL)
Put the current character pointed to by HL into Register A for testing
5086
INC HL
BUMP HL by one to point to the next character
5087
CP 30H
Compare the character to 30H to see if it is at least numeric
5089
RET C
Since CP returns C set if A is less than the test value (30H), we have character which is less than a number (=invalid), so lets RETURN
508A
CP 3AH
Compare the character to 3AH to see if it is between 0 and 9
508C
If we are here, then the character is at least a “0”. Since CP returns C set if A is less than the test value (39H), we have character which between 0 and 9 so JUMP to 5094H
508E
CP 41H
If we are here, then the character is at least a “9”. Compare the character to 41H to see if it is at least an “A”
5090
RET C
Since CP returns C set if A is less than the test value (41H), we have character which is more than a number but less than “A” (=invalid), so lets RETURN
5091
CP 5BH
If we are here, then the character is at least a “A”. Compare the character to 5BH to see if it is at least an “Z”
5093
RET NC
Since CP returns NC set if A is equal to or greater than the test value (“Z”), we have character which is above “Z” (=invalid), so lets RETURN
5094
LD (DE),A
If we are here, then we have a valid character! Put that character into the memory address pointed to by DE
5095
INC DE
BUMP DE to point to the next empty space in the buffer
5096
LOOP back to 5085H (to get the next character and test it), B times
509B
LD A,(HL)
If we are here, then we have finished the loops, so we just need to get the next character from the input string so its teed up
5099
INC HL
BUMP HL to point to the next character after that so thats teed up too
509A
RET
RETURN

509BH-50AAH – This computes filename hash codes. It does so by running a XOR on each character of the filename pointed to by (HL) with the following character. It then multiplies that by 2.

5098
LD B,0BH
Since there are going to be 11 characters (filename or password), set Register B to 11
509D
LD C,00H
Initialize the hash to 0
509F
LD A,(HL)
Start of a loop – put the current character pointed to by (HL) into Register A
50A0
INC HL
BUMP HL to point to the next character
50A1
XOR C
XOR A on C (meaning that only bits which are either ON in A or on in C, but not both, survive)
50A2
RLCA
Multiply by 2
50A3
LD C,A
Put that XOR’d result into C so C is set for the next character
50A4
LOOP back 5 instructions to process the next character
50A6
LD A,C
If we are here, we have processed all characters, so put the XOR’d result from C into A
50A7
OR A
Set the flags
50A8
RET NZ
If A is not zero, RETURN
50A9
INC A
If A was zero, then force A to be 1
50AA
RET
RETURN

50ABH – This routine searches for an available slot in the HIT sector. This routine was called from 4EFAH due to a successful HIT sector read.

50AB
LD A,(4040H)
Put the 25 ms heartbeat counter (held at 4040H) into Register A, as a pseudo-random number
50AE
AND 0E7H
Since the HIT entries are 20H bytes apart, mask it against 11100111 so that it can only be 00-07H, 20-27H, 40-47H, 60-67H, 80-87H, A0-A7H, C0-C7H, or E0-E7H
50B0
CP 40H
Test it aghainst 40H
50B2
Since NZ is set if the comparison is equal to or greater than, SKIP the next instruction if it is not below 40H
50B4
OR 80H
OR it against 80H to force it to 80-87H, A0-A7H, C0-C7H, or E0-E7H
50B6
LD L,A
Register Pair HL will ultimately have the search address, so start setting that up by zero’ing out Register L
50B7
GOSUB to 50BDH below to search the HIT table for an available slot
50BA
RET Z
If a slot was found (i.e., the Z flag was set), RETURN
50BB
LD L,40H
If we are here, a slot was not found, so let’s start again, but this time from the start of the table at 40H
50BD
LD A,(HL)
This is the start of a loop. Put the current character from the HIT entry into Register A
50BE
OR A
Set the status flags
50BF
RET Z
If it is a ZERO, then we have found a slot, so EXIT
50C0
LD A,L
If we are here, then the current byte is not a Zero and we have not found a slot, so prepare to go to the first byte of the next series by first loading A with L
50Cl
LD A,20H
Add 20H to L to get to the first byte of the next series
50C3
LD L,A
Register Pair HL will ultimately have the search address, so set that up by replacing the L with the A (which was the prior L + 20H)
50C4
JR NC,50BDH
If NC is set, then the end of the table was NOT reached, so loop back to 50BDH to test the current HL byte
50C6
ADD A,41H
If we are here, then we hit end of table without finding anything, prepare to redo that loop for 4x, 6x, etc
50CB
LD L,A
Register Pair HL will ultimately have the search address, so set that up by replacing the L with the A (which is now the next set)
50C9
AND 0E7H
Isolate to subcounts 1-7 (i.e., 41-47H, 61-67H, etc)
50C8
CP L
Compare L to the result, testing to see if L is E7
50CC
If L is E7H, then the end of the table has been found
50CE
OR 01H
Set a flag to signal that there are no available entries in the HIT table
50D0
RET
RETURN

50DlH-50FCH – This routine computes the encode of the password held in the 8 byte buffer starting at 5155H. On entry DE must point to the address of the password buffer.

50Dl
LD HL,0FFFFH
Prepare the encoding mask by setting HL to FFFFH
50D4
LD B,08H
Set the maximum number of characters to encode (i.e., 8) and put them in Register B because we will be doing the loop as a DJNZ
50D6
LD A,E
Since DE has the password buffer address, this puts the LSB of the password buffer address into Register A
50D7
ADD A,07H
Add 7 to the LSB of the password buffer address in Register A
50D9
LD E,A
Put the LSB+7 back into Register E so DE points to the end of the password buffer
50DA
BUT by doing this we may have pushed things too far and need to increase the MSB, so test for a CARRY flag. If the CARRY flag is not set, then skip the next instruction
50DC
INC D
If we are here, the carry flag was set, to BUMP the MSB of the password buffer as a result
50DD
LD A,(DE)
This is the beginning of a loop. Fetch the character pointed to by (DE) into Register A
50DE
PUSH DE
Save the character buffer pointer to the Stack
50DF
LD D,A
Put the character found, which is the character to be encoded, into Register D
50E0
LD E,H
Put a value into Register E. We will call it Value 1
50El
LD A,L
Put a value into Register A. We will call it Value 2
50E2
AND 07H
XOR Value 2 by 07H (00000111) to keep only Bits 0-2
50E4
RRCA
RRCA
RRCA
Move Bits 0-2 to Bits 5-7
50E7
XOR L
XOR the lower and upper 3 bits
50E8
LD L,A
Save that rotated and XOR’d value as the new Value 2
50E9
LD H,00H
Clear H so that we can multiply Value 2 by 16
50EB
ADD HL,HL
ADD HL,HL
ADD HL,HL
ADD HL,HL
Multiply HL by 16
50EF
XOR H
XOR D
Do some XOR’s to combine the upper 8 bits of Value 2 * 16 and partially reformed Value 2 and the current character
50Fl
LD D,A
Save the upper 8 bits of the encode to Register D
50F2
LD A,L
Load Register A with the lower 8 bits of Value 2* 16
58F3
ADD HL,HL
Multiply Value 2 * 16 by 2
50F4
XOR H
XOR E
Combine the upper 8 bits of Value 2 * 32 with the lower 8 bits of Value 2 * 16 and Value 1
50F6
LD E,A
Put that value into Register E
50F7
EX DE,HL
EXchange DE and HL, so that HL now has the current encode
50F8
POP DE
Restore the password buffer address from the stack
50F9
DEC DE
Move to the prior character
50FA
LOOP back to 50DDH until the entire password has been encoded in this fashion
50FC
RET
RETURN

50FDH-5l1DH – This routine determines drive availabilty by selecting a drive and waiting for an index. If there is no index in 1 second, it flags either no drive or drive not ready.

50FD
LD A,0D0H
Put the Floppy Disk Controller code for “FORCE INTERRUPT” (1101 xxxx) into Register A. This will clear the controller so the next read is reliable
50FF
LD (37ECH),A
Send the code to the Floppy Disk Controller at 37ECH
5102
GOSUB to 4600 to select the drive held in Register C. This spins up the drive
5105
PUSH BC
Save the routine callers Register Pair BC to the stack
5106
LD BC,3155H
Put the delay of approximately 8 seconds into Register Pair BC
5109
This is the first of 3 reads of the index. It GOSUBs to 511EH to read the index bit from the floppy disk controller. That routine decrements BC before reading the bit. If the counter hits zero before the index bit is high, NZ is set
510C
LOOP back to the prior instruction if the index is TRUE until it goes FALSE. This is unlikely and would only occur if the index hole was directly under the sector at the time the drive was told to spin up
510E
This is the second of 3 reads of the index. It calls 511EH to read the index bit
5111
LOOP back to the prior instruction until the index becomes TRUE, meaning a drive is present and a disk was mounted
5113
This is the third of 3 reads of the index. It calls 511EH to read the index bit
5116
LOOP back to the prior instruction until the index read is false. This means that it got a read, then a no read, so it the true wasn’t a false read
5118
POP BC
Restore the callers BC
5119
RLCA
AND 80H
ADD A,A
It is not clear why this code is here, it will always return a ZERO in Register A
5l1D
RET
RETURN

511EH – This routine looks for the index hole. It decrements BC, checks for an index, and keeps going until an index is found or BC hits zero. If BC hits zero before an index is found, it returns NZ.

511E
DEC BC
Decrement BC
511F
5120
LD A,B
OR C
Since you cannot test BC against 0, these 2 instructions effectively do that
5121
If BC was zero, then JUMP to 5129H to clear the stack, set an error of NZ, and RETURN
5123
LD A,(37ECH)
If BC is not zero, then we should check the for the index bit again, so get the Floppy Disk Controller status and put it into Register A
5126
BIT 01H,A
Check Bit 1 of the Floppy Disk Controller status
5128
RET
RETURN
5129
POP BC
Clear the stack
512A
POP BC
Clear the stack
512B
OR 01H
Set a flag to error
512D
RET
RETURN with error

512EH-5140H – This routine reads the HIT sector from the disk drive specified in Register C

512E
PUSH BC
Save the caller’s BC
512F
PUSH DE
Save the caller’s DE
5130
GOSUB to 4B55H to get the directory from the drive held in C into Register D
5133
LD E,01H
Directory sector 1 is the HIT table, so load a 1 into Register E
5135
LD HL,4D00H
Load HL with 4D00H which is the buffer to be used to load the HIT table into
5138
GOSUB to 4B35H to read the sector held in Register E of the track held in D on the drive held in C into the buffer pointed to by HL
513C
POP DE
Restore the caller’s DE
513C
POP BC
Restore the caller’s BC
513D
RET Z
If the Z flag is set, RETURN
513E
LD A,16H
Load A with error code 16H for “HIT READ ERROR”
5140
RET
RETURN

5141H-5153H – This routine reads the HIT sector from the disk drive specified in Register C

5141
PUSH BC
Save the CALLers BC
5142
PUSH DE
Save the CALLers DE
5143
GOSUB to 4B55H to get the directory from the drive held in C into Register D
5146
LD E,01H
Directory Section 1 is the HIT table, so load a 1 into Register E
514B
LD HL,4D00H
Load HL with 4D00H which is the buffer to be used to load the HIT table into
5148
GOSUB to 46EFH to write the HIT sector to the directory track
514E
POP DE
Restore DE
514F
POP BC
Restore BC
5150
RET Z
If the Z flag is set, RETURN
5151
LD A,17H
Load A with error code 17H for “HIT WRITE ERROR”
5153
RET
RETURN

5154 – Reserved Memory Space (Drive Spec)

5154
DEFB 00H
This is the memory location for the drive spec. It is set to 0 initially

5180 – Subroutine called by 4FE8H to copy the MSB of number of sectors

5180
INC DE
BUMP DE by 1 to get to the MSB of the End of File
518l
LD A,(DE)
Get the MSB of the End of File and put it in Register A
5182
LD (HL),A
Put the MSB of the End of File into the DCB (pointed to by HL)
5183
LD A,(IX+08H)
Get the EOF Address from the DBC and put it in Register A
5186
OR A
Set the flags
5187
RET Z
If INIT or OPEN the EXIT out the routine
Sl8B
LD A,(IX+0CH)
Put the LSB of EOF sector from DCB in Register A
5188
DEC (IX+0CH)
Decrement the LSB of EOF sector from DCB by 1
518E
OR A
Set the flags
5l8F
RET NZ
If not INIT or OPEN exit out of the routine
5l90
DEC (IX+0DH)
Decrement the MSB of EOF sector from DCB by 1
5l93
RET
RETURN