Model I TRSDOS v2.3 SYS3/SYS Explained

This is a disassembly of TRSDOS v2.3’s SYS3/SYS file. SYS3/SYS handles the CLOSE and KILL file management.


4E00H-4E6CH – Process the call to see if CLOSE or KILL was selected.

4E00
PUSH IX
Save the DCB address to the Stack
4E02
PUSH HL
Save the buffer address to the Stack
4E03
PUSH AF
Save the incoming request code to the Stack
4E04
LD A,D
Set A to the MSB of the DCB address
4E05
CP 40H
Compare the MSB of the DCB address to 40H to see if it is in the system area
4E07
Since CP returns C set if A is less than the test value (40H), C being set means that we ARE in the system area, and so we have an error. JUMP to 4E65H
4E09
LD HL,404AH
If we are here, then we are not in the system area, so let us continue. Load HL with 404AH, the MSB of the MEMORY SIZE
4E0C
CP (HL)
Compare the MSB of the DCB address to the end of memory
4E0D
If the MSB of the DCB address is at the end of memory, then we have an error, so skip the next instruction
4E0F
Since CP returns NC set if A is equal to or greater than the test value, if the address is HIGHEr than the end of memory, we also have an error, so JUMP to 4E65H
4E11
LD A,(DE)
Load the first byte of the DCB (stored in DE) into Register A
4E12
BIT 7,A
Check Bit 7 of that first byte. Bit 7 is the FILE OPEN bit
4E14
If the FILE OPEN bit isn’t set, then we have an error, so JUMP to 4E65H
4E16
PUSH DE
Save the current DCB Buffer Address to the stack
4E17
POP IX
Restore the current DCB Buffer Address to the stack into the IX register so we can do math on it
4E19
LD A,(IX+04H)
IX+04H = the MSB of the DCB Buffer Address. Put the contents into Register A
4E1C
CP 40H
Compare the MSB of the DCB address to 40H to see if it is in the system area
4E1E
Since CP returns C set if A is less than the test value (40H), C being set means that we ARE in the system area, and so we have an error. JUMP to 4E65H
4E20
CP (HL)
If its not an error, then check to see if the buffer starts at the end of memory
4E21
If the buffer is at the end of memory, then we have an error, so skip the next instruction and JUMP to 4E25H
4E23
Since CP returns NC set if A is equal to or greater than the test value, this means the buffer start address is BEYOND the end of memory, so we have an error. JUMP to 4E65H
4E25
LD A,(IX+06H)
IX+06H = the drive number. Load the drive number into Register A
4E28
CP 00H
Compare the drive number to 0
4E2A
Since CP returns C set if A is less than the test value (00H), this means we have an invalid drive number, so JUMP to 4E65H
4E2C
CP 04H
Compare the drive number to the maximum number of drive (4)
4E2E
Since CP returns NC set if A is equal to or greater than the test value, if the test is above 4, this means we have an invalid drive number, so JUMP to 4E65H
4E30
LD A,(IX+07H)
IX+07H = the HIT index. Load the HIT index into Register A
4E33
BIT 4,A
Check Bit 4 of the HIT index. Bit 4 is an unused bit and should be 0
4E35
If the bit is not the expected 0 then we have an error, so JUMP to 4E65H
4E37
BIT 3,A
Check Bit 3 of the HIT index. Bit 3 is an unused bit and should be 0
4E39
If the bit is not the expected 0 then we have an error, so JUMP to 4E65H
4E3B
LD A,(IX+0EH)
IX+0EH = Track number of the 1st GAP. Load the track number of the 1st GAP into Register A
4E3E
CP 00H
Compare the track number of the 1st GAP against 00 to see if any tracks were assigned
4E40
If we have NZ then tracks were assigned, so JUMP to 4E4CH
4E42
LD A,(IX+0FH)
IX+0FH = Granule count. Load the granule count into Register A
4E45
CP 20H
Compare the granule count against 20H
4E47
Since CP returns C set if A is less than the test value (20H), C being set means if the first granule is track 0, sector 0, which is reserved for system use. If so, JUMP to 4E65H
4E49
LD A,(IX+0EH)
IX+0EH = The track number of the lst GAP. Load the track number of the 1st GAP into Register A
4E4C
CP 0FFH
Compare the track number of the 1st GAP against FFH
4E4E
If the track number of the 1st GAP is FFH, then there were no tracks assigned. If so, JUMP to 4E54H
4E50
CP 23H
Compare the track number of the 1st GAP against 23H, which is the maximum number of tracks
4E42
Since CP returns NC set if A is equal to or greater than the test value of 23H, the track is greater than 34 tracks so we have an error. If so, JUMP to 4E65H

4E54H-4E64H If we are here then the parameter validation is complete. Next we need to process what we were called to do … KILL or CLOSE

4E54
POP AF
Restore the SYS3 request code from the stack into Register Pair AF
4E55
POP HL
Restore buffer address from the stack into Register Pair HL
4E56
POP IX
Restore DCB address from the stack into Register Pair IX
4E58
AND 70H
Isolate the option by ANDing A against 01110000
4E5A
CP 10H
Test for CLOSE request
4E5C
JP Z,4E6DH
Jump to 4E6DH if the SYS3 request code was CLOSE
4ESF
CP 20H
Test for KILL request
4E61
JP Z,4FD0H
Jump to 4FD0H if the SYS3 request code was KILL
4E64
RET
If we are here then neither CLOSE nor KILL was requested, so EXIT out

4E65H-4E6CH This is the routine which processes all the errors which arose in the initial check

4E65
POP AF
Restore the SYS3 request code from the stack into Register Pair AF
4E66
POP HL
Restore buffer address from the stack into Register Pair HL
4E67
POP IX
Restore DCB address from the stack into Register Pair IX
4E69
LD A,26H
Load Register A with the error code for “FILE NOT OPENED”
4E6B
OR A
Set the registers
4E6C
RET
RETURN

4E6D – Process the CLOSE Routine

4E6D
GOSUB to 4892H to save the Registers
4E70
RET NZ
If the file is not currently open then NZ will be set, so return with an error
4E71
GOSUB to 4878H to flush the sector buffer, if needed
4E74
RET NZ
If there was an error in flushing the buffer then NZ will be set, so return with an error
4E75
LD B,(IX+07H)
IX+07 holds the directory sector number with the HIT Index, put that into Register B
4E78
LD C,(IX+06H)
IX+06 holds the unit, put that into Register C
4E7B
GOSUB to 4AC1H to read the directory entry pointed to by Register Pair BC into a buffer starting at 4D00H. Note that 4AC1H exits with Register Pair HL holding the address of the entry
4E7E
RET NZ
If there was an error reading that directory entry then NZ will be set, so return with an error
4E7F
4E81
4E82
LD A,03H
ADD A,L
LD L,A
We need to move HL to the EOF byte 3 bytes later, so this set of instructions accomplishes that
4E83
PUSH HL
Save the updated pointer to the EOF byte
4E84
LD A,(IX+08H)
IX+08 holds the EOF offset held in the DCB. Put that into Register A for testing
4E87
CP (HL)
Check the EOF offset byte held in the DCB (held in A) against the byte pointed to by the EOF byte read from the directory
4E88
If do not match, then JUMP down to 4E9EH to update the directory entry EOF
4E8A
4E8C
4E8D
LD A,11H
ADD A,L
LD L,A
We need to move to the EOF sector number in the actual directory, which is another 11H bytes down, so this is L = L + 11H
4E8E
LD A,(IX+0CH)
IX+0CH holds the LSB of the EOF sector held in the DCB. Put that into Register A for testing
4E91
CP (HL)
Check the LSB of the EOF sector held in the DCB against the LSB of the EOF sector from the actual directory
4E92
If they don’t match, then JUMP to 4E9EH to update the directory’s EOF sector
4E94
INC L
Bump Register L to point to the MSB of the EOF sector held in the DCB
4E95
LD A,(IX+0DH)
IX+0DH holds the MSB of the EOF sector held in the DCB. Put that into Register A for testing
4E98
CP (HL)
Check the MSB of the EOF sector held in the DCB against the MSB of the EOF sector from the actual directory
4E99
If they don’t match, then JUMP to 4E9EH to update the directory’s EOF sector
4E9B
POP AF
Clear the stack
4E9C
JUMP to 4EB6H to calculate the number of grans

4E9EH-4ECAH – Routine inside the CLOSE Routine. This routine updates the directory entry EOF

4E9E
POP HL
Restore the EOF (that is, the base address of the directory entry + 03H bytes)
4E9F
LD A,(IX+08H)
IX+08 holds the EOF offset held in the DCB. Put that into Register A for moving to the buffer of the actual directory
4EA2
LD (HL),A
Copy that byte into the buffer read from the diskette
4EA3
4EA5
4EA6
LD A,11H
ADD A,L
LD L,A
We need to move to the EOF sector number in the actual directory, which is another 11H bytes down, so this is L = L + 11H
4EA7
LD A,(IX+0CH)
IX+0CH holds the LSB of the EOF sector held in the DCB. Put that into Register A for moving to the buffer of the actual directory
4EAA
LD (HL),A
Copy that byte into the buffer read from the diskette
4EAB
INC L
Bump Register L to point to the MSB of the EOF sector held in the DCB
4EAC
LD A,(IX+0DH)
IX+0DH holds the MSB of the EOF sector held in the DCB. Put that into Register A for moving to the buffer of the actual directory
4EAF
LD (HL),A
Copy that byte into the buffer read from the diskette
4EB0
PUSH HL
Store the current directory address pointer to the stack
4EB1
GOSUB to 4AD6H to write the diskette buffer held at 4D00H to the diskette
4EB4
POP HL
Restore the current directory address pointer from the stack
4EB5
RET NZ
If there was an error writing the diskette buffer to the diskette, RETURN out
4EB6
INC L
If we are here, then the diskette buffer has been successfully written to the disk. Bump the L pointer by 1 to now point to the track number for the 1st GAP
4EB7
LD DE,0000H
We are going to use Register Pair DE to hold the number of total granules, so zero it out first
4EBA
LD A,(HL)
Put the starting track number for the 1st GAP into Register A
4EBB
INC L
Bump the pointer by 1
4EBC
CP 0FEH
Test to see if that bump caused an overflow or the end of GAPS
4EBE
Since CP returns NC set if A is equal to or greater than the test value, JUMP to 4ECCH if there was an overflow or we reached the end of the GAPs
4EC0
LD A,(HL)
If we are here, then the bump didn’t trigger any errors, so put the number of granules (pointed to by the bumped HL) into Register A
4EC1
INC L
Bump Register L again to now point to the 1st byte of the NEXT GAP
4EC2
AND 1FH
AND Register A against 00011111 to keep only bits 0-4, isolating the count of granules
4EC4
INC A
Bump A to now have the true granule count
4EC5
ADD A,E
Add A to the DE granule counter
4EC6
LD E,A
Put that granule count as the LSB of the granule counter
4EC7
So long as the LSB is not over 255, LOOP back to 4EBAH without increasing the MSB
4EC9
INC D
Increase the MSB
4ECA
LOOP back to 4EBAH

4ECCH-4ED7H – Routine inside the CLOSE Routine. This routine is jumped to by 4EBEH when there is an overflow or we reach the end of the GAPs when we weren’t ready for that

4ECC
When we get to this routine, we have either an overflow or an end of GAPs. NZ would be if we have an end of GAPs. So if NZ is set, JUMP to 4ED9H
4ECE
LD B,(HL)
If we are here, then we have an overflow instead. Put the HIT index for the overflow entry from the directory (pointed to by HL) into Register B
4ECF
GOSUB to 4AC1H to read the overflow entry from the directory
4ED2
RET NZ
If NZ is set, there was an error, so RETURN
4ED3
4ED4
4ED6
LD A,L
ADD A,16H
LD L,A
If we are here, then the Overflow Entry was read from the directory, so now we need to push HL out 16H bytes to go to the next GAP. So … REGISTER L = REGISTER L + 16H
4ED7
JUMP BACK 4EBAH to continue scanning the GAPs (since we didn’t error out; we need to keep going)

4ED9H-4F6DE – Routine inside the CLOSE Routine. If we are here, then we reached the end of the GAPs. At this point, then, we need to check for a change in the file size. This is accomplished by (granule total from directory GAP’s MINUS granule total from ending sector in DCB) divided by 5. The result is negative if the file size increased, positive if the file size decreased, and zero if there was no change.

4ED9
PUSH HL
If we are here, then HL is pointing to the end of the GAPs in the directory. Save it to the stack
4EDA
LD L,(IX+0CH)
IX+0CH holds the LSB of the EOF sector held in the DCB. Put that into Register L
4EDD
LD H,(IX+0DH)
IX+0DH holds the MSB of the EOF sector held in the DCB. Put that into Register H
4EE0
LD A,05H
We are going to have to do some division, so set up Register A to hold 05H, the divisor
4EE2
GOSUB to 4B84H to divide HL by A and put the result into HL
4EE5
INC HL
Bump HL by 1 to get the true number of grans
4EE6
XOR A
Clear the CARRY flag
4EE7
EX DE,HL
Swap DE (# of granules in directory) and HL (true file size)
4EE8
SBC HL,DE
Do a SUBTRACT WITH CARRY to compare true file size (by record count in DCB) to directory file size (by GAP’s)
4EEA
EX DE,HL
Swap them back so the difference is held in DE
4EEB
POP HL
Restore the address of the end of the GAPs in the directory
4EEC
If the gran count from the directory is equal to the ending sector in DCB, JUMP to 4F7BH to skip over the directory update
4EEF
If the gran count from the directory is greater than the ending sector in DCB, JUMP to 4F7BH to skip over the directory update
4EF2
DEC L
If we are here, then the gran count from the directory is LESS THAN the ending sector in DCB, so we need to update the directory. First drop the granule count by 1 ..
4EF3
DEC L
… and then by 1 again, to backspace HL to to the granule count for last good GAP in the directory
4EF4
PUSH HL
Save the address of the last good GAP in the directory to the stack
4EF6
PUSH DE
Save the granule difference to the stack
4EF6
GOSUB to 4AF0H to read GAT sector into the buffer at 4D00H
4EF9
If NZ is returned then there was an error reading the GAT sector, so RETURN
4EFB
If we are here, the GAT sector has been read into 4D00H so we GOSUB to 5068H to read the HIT sector into the buffer at 5100H
4EFE
POP DE
Restore the granule difference to DE
4EFF
POP HL
Restore the address of the last good GAP in the directory to HL
4F00
RET NZ
If NZ is returned then there was an error reading the HIT sector, so RETURN
4F01
LD A,E
We need to do some checking on the granule difference, so load the LSB (Register E of the pair DE) into Register A
4F02
CP 00H
Compare the LSB of the granule difference against zero
4F04
If it is zero, then we are done so JUMP to 4F6FH to write the updated directory and reconstruct the DCB
4F06
PUSH DE
If we are here, then there is still more work to do, so once again save the granule difference to the stack
4F07
LD A,(HL)
HL points to the last GAP in the directory, so read its value (which is the granule count) into Register A
4F08
AND 0E0H
Since the starting granule number is held in bit 5 of the last GAP in the directory, mask the granule count against 00001110
4F0A
4F0B
4F0C
RLCA
RLCA
RLCA
Three RLCA’s will shift into low order of A register. These shifts will result in 0 if the file starts on sector 0 of first track, 1 if file begins on sector 5 of first track
4F0D
LD E,A
Put the shifted result into Register E
4F0E
LD A,(HL)
HL points to the last GAP in the directory, so read its value (which is the granule count) into Register A
4F0F
AND 1FH
We want the number of assigned grans, and those are held in bits 0-5. So we AND against 00011111 to keep only bits 0-5 and zero out the rest
4F11
ADD A,E
Add the modified number of assigned grans (held in A) to the previous determination of 0 vs 5 for the location of the file (so it adds 0 if the starting sector is 0, and 1 if the starting sector is 5)
4F12
LD E,A
Put that result into Register E as well
4F13
AND 0FEH
We are about to divide, and don’t want to trigger a carry, so AND Register A against 11111110 to strip off Bit 0
4F15
RRCA
ROTATE WITH CARRY Register A, which is the same as dividing it by 2. So now the granule count divided by 2, otherwise known as the number of tracks, is in Register A
4F16
LD (4F22H),A
Save the number of tracks into memory location 4F22H
4F19
LD A,E
Restore the original granule count and offset from Register E into Register A
4F1A
AND 01H
Isolate bit 0 by ANDing Register A against 00000001
4F1C
INC A
A=A+1
4F1D
CPL
Complement A (meaning, invert every bit). This will generate a mask for clearing the last assigned granule in the GAT
4F1E
PUSH AF
Save the mask held in AF to the stack
4F1F
DEC HL
Reduce HL (which is the GAP pointer to the track number) by 1
4F20
LD A,(HL)
Place the starting track number for the GAP into Register A
4F21
ADD A,00H
Add no. of consecutively assigned tracks. Gives ending track #
4F23
EX DE,HL
Swap DE and HL so that DE will hold the directory GAP address
4F24
4F26
LD H,4DH
LD L,A
Form the address of the GAT entry for the ending track held in HL
4F27
POP AF
Restore the mask used to clear the last granule assigned into AF
4F28
AND (HL)
Apply the mask. This will clear the bit for the last granule assigned
4F29
LD (HL),A
Now that the mask has been applied, put the masked byte back into the GAT buffer pointed to by HL

Note: We now need to look for GAPS which have no granules assigned. When one is found, we must set both bytes of the GAP entry to FFH. If all GAP’s in an entry are released (set to FF’ s), then we need to release the directory entry and the HIT index in the HIT sector for the directory entry..

4F2A
EX DE,HL
Swap DE and HL so that HL will hold the directory GAP address
4F2B
INC HL
HL = HL + 1 to skip forward to number of granules assigned this GAP
4F2C
DEC (HL)
Prepare to test for the number of granules assigned by reducing the number of granules assigned, so now it is granules assigned – 2
4F2D
LD A,(HL)
Put granules assigned – 2 into Register A
4F2E
INC A
A=A+1. If this results in a ZERO then none were assigned
4F2F
AND 1FH
AND Register A against 00011111 to keep only bits 0-4 (and ensure the value is less than 30) so as to isolate the true granule count for the GAP
4F31
POP DE
Restore the difference in granule count between DCB and the directory to Register Pair DE
4F32
If the AND was not zero, then we had at least one gran assigned, so JUMP to 4F6CH
4F34
LD (HL),0FFH
If we are here, then there were no grans assigned, so initialize the GAP to FFH
4F36
DEC HL
HL = HL – 1 to now point to the track number
4F37
LD (HL),0FFH
Initialize the track number to FFH
4F39
DEC HL
HL = HL – 1 to now point to the granule count for last GAP
4F3A
LD A,L
Put the LSB of HL into Register A, so that Register A holds the granule count for last GAP
4F3B
AND 1FH
AND Register A against 00011111 to keep only bits 0-4 (and ensure the value is less than 30) so as to isolate the gap index
4F3D
CP 15H
Test the GAP index against 15H to see if it is the start of the GAP entries in the directory
4F3F
If this was NOT the start of the GAP entries in the directory, JUMP to 4F6CH
4F41
XOR L
Bitwise XOR on A with L to form the LSB of the directory address in Register A
4F42
LD L,A
Load Register L with the XOR’d value to reform the start of the directory entry pointed to by HL
4F43
LD A,(HL)
Get the address and directory flags from HL
4F44
LD (HL),00H
Set the directory entry to AVAILABLE
4F46
INC HL
HL = HL + 1 to now point to the OVERFLOW byte of the directory entry
4F47
LD D,(HL)
Put the value of the OVERFLOW byte of the directory entry into Register D
4F48
LD H,51H
Put the MSB of the HIT buffer (5100H) into Register H
4F4A
LD L,B
Set the offset of that table (the LSB) from Register B (which is the HIT index for the file)
4F4B
LD (HL),00H
Now that HL points to the applicable directory entry in the HIT, clear the HIT entry
4F4D
BIT 7,A
Test the type of directory entry being cleared
4F4F
If that returns a ZERO, then this is a PRIMARY directory entry, so JUMP to 4F6FH to write the updated directory and reconstruct the DCB
4F51
PUSH DE
Save the remainder of granule count difference into Register Pair DE
4F52
GOSUB to 4AD6 to write the released directory entry to disk
4F55
POP DE
Restore the granule count difference from the stack
4F56
RET NZ
If there was an error writing the released directory entry to disk, RETURN
4F57
LD A,B
If we are here, then the released directory entry was successfully written to disk, so put the HIT index from Register B into Register A
4F58
XOR D
Bitwise XOR Register A with Register D and then ..
4F59
AND 07H
… AND Register A against 00000111 to keep only bits 0-2 (and ensure the value is less than 8). This leaves us with the overflow pointer
4F5B
LD B,D
Put the sector number for the next overflow pointer into Register B
4F5C
GOSUB to 4AC1H to read the overflow directory entry (if there is one)
4F5F
RET NZ
If there was an error reading the overflow directory entry, then RETURN
4F60
LD A,B
If we are here, we have successfully read the overflow directory entry, so put the entry addr/sector number – 2 into Register A
4F61
AND 0E0H
AND Register A against 11100000 to keep only bits 5-7 = the directory entry addres
4F63
ADD A,1FH
A = A + 1F to skip to the end of GAP’s in the overflow directory entry
4F65
LD L,A
Start to form the ending directory address in HL
4F66
LD (HL),0FFH
Signal end of GAP’s by putting a FFH
4F68
DEC HL
HL = HL – 1 to now point to the starting track number
4F69
LD (HL),0FFH
Initialize the starting track number with FFH
4F6B
DEC HL
HL = HL – 1 to now point to the granule count for next GAP
4F6C
DEC E
Reduce the difference in granule count
4F6D
JP to 4F01H to test to see if we are done

4F6FH – Routine inside the CLOSE Routine. Write updated directory entry and reconstruct the DCB.

4F6F
GOSUB to 4AD6H to write the updated directory entry to diskette
4F72
RET NZ
If there was an error in writing the directory entry then NZ is set, so RETURN
4F73
If we are here, then the updated directory entry was written correctly so we now GOSUB to 5077H to write the updated HIT sector to diskette
4F76
RET NZ
If there was an error in writing the HIT sector then NZ is set, so RETURN
4F77
GOSUB to 4B03H to write the GAT sector to diskette
4F7A
RET NZ
If there was an error in writing the GAT sector then NZ is set, so RETURN
4F7B
LD A,(IX+07H)
If we are here, then the updated directory, HIT, and GAT were all successfully written to disk. IX+07 holds the directory sector number with the HIT Index, put that into Register A
4F7E
XOR B
XOR Register A against Register B (which holds the last HIT index)
4F7F
AND 1FH
MASK that XOR with 00011111 to see if there was any difference
4F81
If they were different then NZ is set so we need to GOSUB to 4AC1H to read the directory entry pointed to by Register Pair BC into a buffer starting at 4D00H. Note that 4AC1H exits with Register Pair HL holding the address of the entry
4F84
RET NZ
If there was an error reading the directory entry from diskette, RETURN
4F85
LD A,(IX+06H)
IX+06H = the drive number. Load the drive number into Register A
4F88
AND 03H
AND the drive number held in Register A against 03H so that it does not exceed 4 drives
4F8A
OR 30H
OR against 30H to turn those bits into an ASCII value
4F8C
LD (4FC8H),A
Put the ASCII value of the drive number into 4FC8H
4F8F
LD H,42H
Start to form the address of the file name in the directory by pointing to the beginning of the buffer at 4200H
4F91
LD A,(IX+07H)
IX+07 holds the directory sector number with the HIT Index, put that into Register A
4F94
AND 0E0H
AND that value against 11100000 to isolate the offset to the file entry
4F96
OR 05H
ADD 05H to it ..
4F98
LD L,A
and put it into Register L, so that HL points to the filename in the buffer in memory
4F99
PUSH HL
Save the address of filename in the directory to the stack
4F9A
PUSH IX
We are now going to get the DCB address into DE, so first push IX (DCB address) to the stack
4F9C
POP DE
Restore the DCB address to DE
4F9D
LD B,08H
Prepare for a DJNZ loop by setting Register B to the maximum number of characters in a filename
4F9F
LD A,(HL)
Get the currently pointed to filename character and put it into Register A
4FA0
CP ‘ ‘
Check to see if it is blank
4FA2
If it is blank, then we have read the full filename, so JUMP out of the loop to 4FA9H
4FA4
LD (DE),A
If it isn’t blank, then we have a character and are not at the end. Put that character (from the directory) into the DCB (pointed to by DE)
4FA5
INC HL
HL = HL + 1 to point to the next character in the filename on the diskette
4FA6
INC DE
DE = DE + 1 to to do the same for the DCB
4FA7
DJNZ 4F9FH
LOOP back to 4F9FH until 8 characters have been read
4FA9
POP HL
If we are here, the full filename has been read and put into the DCB pointed to by DE. Restore the address of filename in the directory to HL
4FAA
LD A,L
We need to do some math, so put the LSB of the address of the filename in the directory into Register A
4FAB
ADD A,08H
A = A + 8 to now point to the character after the filename
4FAD
LD L,A
Put L+8 into L, so now HL points to the extension
4FAE
LD A,(HL)
Get the character pointed to by (HL) into Register A
4FAF
CP ‘ ‘
Check to see if it is a blank
4FB1
If it is a blank then we have no extension, so JUMP to 4FC3H
4FB3
LD A,’/’
The only valid character, other than a blank, for this memory location would be a “/”, so put that character into Register A
4FB5
LD (DE),A
Put a “/” into the memory location pointed to by DE regardless of what may ahve been read from (HL)
4FB6
INC DE
DE = DE + 1 since we just put a “/” in there
4FB7
LD B,03H
Prepare for a DJNZ loop by setting the loop count to 3
4FB9
LD A,(HL)
Start of a loop to read the extension – Get the character pointed to by (HL) into Register A
4FBA
CP ‘ ‘
Check to see if it is a blank
4FBC
If it is a blank then we have read all we will be reading for the extension, so JUMP to 4FC3H
4FBE
LD (DE),A
Put the character read from the disk into (DE)
4FBF
INC HL
HL = HL + 1 to point to the next character in the extension on the diskette
4FC0
INC DE
DE = DE + 1 to to do the same for the DCB
4FC1
DJNZ 4FB9H
LOOP back to 4FB9H until 3 characters have been read from diskette
4FC3
4FC5
LD A,’:’
LD (DE),A
The next character MUST be a “:” so just put it into the DCB
4FC6
INC DE
Since we just filled DE with a colon, DE = DE + 1 to point to the next character
4FC7
LD A,00H
Set the drive number to 0
4FC9
LD (DE),A
Put the drive number into DE
4FCA
INC DE
DE = DE + 1
4FCB
LD A,03H
Put the 03H terminator into Register A
4FCD
LD (DE),A
Put the terminator into DE
4FCE
XOR A
Clear all flags to signal a good status
4FCF
RET
RETURN

4FD0 – The KILL Routine

4FD0
Save all the callers registers and open the file
4FD3
RET NZ
If NZ is set, then the file is not open, so RETURN
4FD4
LD A,(IX+01H)
IX+01 holds the permission flags for the file, so load those into Register A
4FD7
AND 07H
AND the permission flag byte against 00000111 to keep only bits 0-2
4FD9
CP 02H
Compare the masked permission flag byte against 02H (0010)
4FDB
Since CP returns C set if A is less than the test value (02H), C being set means that we have either 01H or 00H
4FDD
LD A,25H
If we are here, then we have an error. Load up the “ILLEGAL FILE ACCESS” error code into Register A
4FDF
OR A
Set the flags for error
4FE0
RET
RETURN
4FE1
LD C,(IX+06H)
IX+06H = the drive number. Load the drive number into Register C
4FE4
LD B,(IX+07H)
IX+07H = the HIT index. Load the HIT index into Register B
4FE7
GOSUB to 5068H to read the HIT sector into a memory buffer starting at 5100H
4FEA
RET NZ
If NZ is set, we have an error, so RETURN
4FEB
If we are here, the HIT sector read correctly, so GOSUB to 4AF0H to read the GAT into a memory buffer starting at 4D00H
4FEE
RET NZ
If NZ is set, we have an error, so RETURN
4FEF
If we are here, the GAT sector read correctly, so GOSUB to 4AC1H to read the directory entry pointed to by Register Pair BC into a buffer starting at 4D00H. Note that 4AC1H exits with Register Pair HL holding the address of the entry
4FF2
RET NZ
If NZ is set, we have an error, so RETURN
4FF3
4FF5
4FF6
LD A,16H
ADD A,L
LD L,A
Register L = Register L + 16H so that L points to the start of the GAPs
4FF7
LD E,(HL)
Start of a loop. Put the track address from the GAP from diskette into Register E
4FF8
INC L
Bump the HL pointer to now point to the number of assigned granules for that file
4FF9
LD D,(HL)
Put the number of assigned granules for that file into Register D
4FFA
LD A,E
We need to test for the end of the GAP, but since there is no CP for Register E, we need to move Register E into Register A for testing
4FFB
CP 0FEH
Test Register A against 0FE to see if either we are at the end of the GAP’s or we have an overflow pointer
4FFD
Since CP returns NC set if A is equal to or greater than the test value of 0FEH, JUMP to 5005H if we are at the end of the GAPs or have found an overflow pointer
4FFF
INC L
If we are here, then there was no overflow flag and we are not at the end of the GAP’s, so we need to go to the next GAP by bumping Register L
5000
GOSUB to 5039H to deallocate the GAP
5003
LOOP back to 4FF7H until we are at the end of the GAP’s or we have an overflow pointer
5005
LD A,L
Put the LSB of directory entry address into the sector buffer
5006
AND 0E0H
AND Register A against 11100000 to keep only bits 5-7, which would be the starting address of the directory entry
5008
LD L,A
Now that A is masked, put it back into Register L to form the beginning address which is held in HL
5009
LD (HL),00H
Clear access flags, flag entry as available
500B
PUSH BC
Save the HIT index/unit number to the stack
500C
PUSH DE
Save the GAP flags (FF or FE) to the stack
500D
PUSH HL
Save the start of the directory entry address to the stack so that we can put it into DE
500E
POP DE
Restore the start of the directory entry address from the stack into DE
500F
INC DE
Bump DE so that it now points to the 2nd byte of directory entry address
5010
LD BC,001FH
Put the number of bytes to move into BC in preparation for a LDIR
5013
LDIR
Move the bytes
5015
POP DE
Restore the GAP flags (FF or FE) to from stack
5016
POP BC
Restore the HIT index/unit number from the stack
5017
GOSUB to 4AD6H to write the diskette buffer held at 4D00H to the diskette
501A
RET NZ
If there was an error, then NZ will be set, so RETURN
501B
LD H,51H
The HIT Buffer Address is 5100H, so load Register H with the MSB of the HIT Buffer
501D
LD L,B
Load Register L (the LSB of the HIT Buffer), with Register B’s contents which are the HIT index, so that HL points to the HIT Buffer of the byte containing the matching hash code for the filename
501E
LD (HL),00H
Zero out the primary HIT entry for the hash code matching the file
5020
LD B,D
Put Register D, which is holding the sector number of the possible overflow entry into Register B
5021
LD A,E
Put Register E, which is holding the GAP terminating flag, into Register A
5022
CP 0FEH
Test the GAP terminating flag against 0FEH to test to see if there is an overflow entry
5024
If that matches 0FEH then there is an overflow entry, so then JUMP to 4FEFH to read the overflow entry and process it
5026
If we are here, then there is no overflow entry for the deleted HIT, so GOSUB to 4B03H to write the updated GAT sector from the GAT sector buffer of 4D00H
5029
RET NZ
If that routine returns a NZ, there was an error; so RETURN
502A
If we are here, then the GAT was sucessfully written from the GAT sector buffer, so now GOSUB to 5077H to write the udpated HIT sector from the HIT sector buffer of 5200H
502D
RET NZ
If that routine returns a NZ, there was an error; so RETURN
502E
PUSH IX
If we are here, then the HIT was successfully written from the HIT sector buffer, so now we need to zero out the associated DCB. First PUSH IX onto the stack ..
5030
POP HL
… to transfer it to Register Pair HL
5031
LD B,20H
Set up for a DJNZ loop of 20H Entries (as that is the number of bytes of the DCB to clear)
5033
XOR A
Zero out Register A
5034
LD (HL),A
Start of a loop. Fill the memory contents of (HL) with 00H
5035
INC HL
Bump HL to the next value
5036
DJNZ 5034H
LOOP back two instructions to 5034H for 20H iterations
5038
RET
Return to the KILL caller

5039H – Routine inside the KILL Routine. Deallocate the GAP.

5039
PUSH HL
Save the current directory address for GAP deallocation to the stack
503A
PUSH BC
Save the HIT index/unit number to the stack
503B
LD L,E
Put E (Track Number) into L. This is will become the index into GAT
503C
LD H,4DH
Put the MDB of 4DH into H so that HL will point to the buffer in memory for the starting track number
503E
LD A,D
Put the number of consecutively assigned granules held in Register D into Register A for manipulation
503F
AND 1FH
AND the number of consecutively assigned granules by 00011111 to keep only bits 0-4 to isolate the granule count – 1
5041
LD C,A
Put that into C
5042
INC C
Bump C so that it now set to the granule count
5043
XOR D
XOR A against D to mask the starting granule number flag (which is either 0 for sector 0, or 1 for sector 5)
5044
5045
5046
RLCA
RLCA
RLCA
Move it to the lower bits of Register A
5047
PUSH AF
Save the result as the initial bit counter for each GAT entry
5048
LD B,(HL)
Put the current GAT entry for the current track number (pointed to by HL) into Register B
5049
GOSUB to 505BH to release a granule (make it available) for current GAT entry
504C
LD (HL),B
Put the updated GAT entry back into (HL)
504D
POP AF
Restore the result as the initial bit counter for each GAT entry
504E
INC A
Bump A by 1
504F
CP 02H
Test A+1 against 02H to see if the maximum number of granules were released
5051
If not, then JUMP to 5055H as more granules need to be released for this GAT entry
5053
XOR A
If we are here, then all granules were released, so reset the bit counter to 0
5054
INC L
Bump Register L by 1 so that Register L points to the GAT entry for next track
5055
DEC C
DECrement C by 1 since we released 1 granule
5056
If C is not zero, then we have more granules to release, so JUMP back to 5047H
5058
POP BC
Restore the HIT index/unit number from the stack
5059
POP HL
Restore the current directory address for GAP deallocation from the stack
505A
RET
RETURN

505BH – Release a granule (make it available) for current GAT entry.

505B
AND 07H
AND Register A against 0000 0111 to isolate the bit number to be reset
505D
505E
505F
RLCA
RLCA
RLCA
Move the bit into position for a RES OP CODE
5060
OR 80H
Combine the RES OP CODE (which starts at 80H) with the bit to be cleared
5062
LD (5066H),A
Take the constructed RES OP CODE and put it into 5066H
5065
RES 0,B
Execute the RES OP CODE to clear the bit associated with the granule
5067
RET
RETURN

5068H – Read the HIT Sector into the Buffer.

5068
GOSUB to 4B55H to get the directory track number into D
506B
LD E,01H
Put a 01 into E to form Register Pair DE to specify the HIT sector
506D
LD HL,5100H
Set Register Pair HL to point to 5100H which will be the HIT sector buffer in RAM
5070
GOSUB to 4B35H to read the HIT sector into RAM
5073
RET Z
If Z is set then there was no error, so RETURN
5074
LD A,16H
If we are here, there was an error, so load Register A with the error code for “HIT READ ERROR”
5076
RET
Return

5077H – Write the HIT Sector from the Buffer.

5077
GOSUB to 4B55H to get the directory track number into D
507A
LD E,01H
Put a 01 into E to form Register Pair DE to specify the HIT sector
507C
LD HL,5100H
Set Register Pair HL to point to 5100H which will be the HIT sector buffer in RAM
507F
GOSUB to 46EFH to write the HIT sector into RAM
5082
RET Z
If Z is set then there was no error, so RETURN
5083
LD A,17H
If we are here, there was an error, so load Register A with the error code for “HIT WRITE ERROR”
5085
RET
Return