TRS-80 DOS - TRSDOS v2.3 for the Model I - SYS3/SYS Disassembled

Page Customization

Introduction:

TRSDOS 2.3 SYS3/SYS Disassembly - Close File and Kill File (Model I)

SYS3 is a disk overlay for TRSDOS 2.3 on the TRS-80 Model I that handles two file management operations: Close File (request code 10H) and Kill File (request code 20H). It loads into the overlay area at 4E00H-50FFH when invoked through the RST 28H supervisor call mechanism with SVC code 95H (Close) or A5H (Kill).

The Close File handler (4E6DH) writes the current EOF offset and extent chain data from the FCB back to the directory entry, then deallocates any unused granules beyond the file's actual size. This ensures the on-disk directory accurately reflects the file's current state when the file is closed.

The Kill File handler (4FD0H) verifies the file is open for write access, then traverses the entire extent chain in the directory, deallocating every granule from the GAT (Granule Allocation Table) sector buffer at 5100H. After all granules are freed, it clears the directory entry and zeroes the 32-byte FCB.

SYS3 relies heavily on SYS0 resident routines for disk I/O: 4892H (verify file open), 4878H (set up overlay context), 4AC1H (read directory sector), 4AD6H (write directory and GAT sectors), 4AF0H (read directory sector into 4D00H buffer), 4B03H (write sector from 4D00H buffer), 4B55H (track cache lookup), 4B84H (16-bit divide), and 46EFH/4B35H (sector read with retry). The GAT sector buffer at 5100H-51FFH holds the GAT data for the current drive during granule deallocation.

Variable and Memory Reference Table

Address RangePurpose
404AH-404AH
(1 byte)
High memory page boundary (top of usable RAM / 256)
4200H-42FFH
(256 bytes)
Directory sector buffer (used by SYS0 routines 4AC1H/4AD6H)
4D00H-4DFFH
(256 bytes)
Overlay/sector buffer (used for secondary directory sector reads)
4F22H-4F22H
(1 byte)
Self-modifying operand: track number for GAT deallocation (ADD A,00H at 4F21H)
4FC8H-4FC8H
(1 byte)
Self-modifying operand: drive number with command bits for filespec construction (LD A,00H at 4FC7H)
5066H-5066H
(1 byte)
Self-modifying operand: RES/SET bit opcode for GAT bit manipulation (RES 0,B at 5065H)
5100H-51FFH
(256 bytes)
GAT sector buffer (holds Granule Allocation Table for current drive)

FCB Fields Referenced by SYS3

OffsetFieldUsed By
IX+00HStatus flags (bit 7 = file open)Validation at 4E12H
IX+01HAccess mode (bits 0-2)Kill handler write-access check at 4FD7H
IX+04HSector buffer high byte (must be in valid RAM range)Validation at 4E19H
IX+06HDrive number (0-3)Drive validation at 4E25H, directory reads, filespec construction
IX+07HDirectory entry info byte (bits 7-5 = directory sector offset, bits 4-0 = entry position)Directory sector reads, entry location
IX+08HEOF byte offset within last sectorClose handler directory update at 4E84H
IX+0CHExtent chain position low byteClose handler directory update at 4E8EH
IX+0DHExtent chain position high byteClose handler directory update at 4E95H
IX+0EHExtended status / overflow sector count high byteValidation at 4E3BH, 4E49H
IX+0FHExtended status / overflow sector count low byteMinimum size validation at 4E42H

Major Routine List

AddressPurpose
4E00HRequest Code Dispatcher and FCB Validator
Validates FCB integrity, dispatches to Close (10H) or Kill (20H)
4E65HValidation Failure Exit
Returns error 26H (file not open / invalid FCB)
4E6DHClose File Handler
Writes EOF/extent data to directory, deallocates unused granules
4EB6HExtent Chain Sector Counter
Walks extent chain entries, accumulates total sector count
4ED9HCompute Sectors to Deallocate
Divides total sectors by granule size, determines excess granules
4EF2HGranule Deallocation Loop
Walks extent chain backward, frees granules from GAT
4F06HDecode and Free Single Granule
Extracts track/sector from extent byte pair, clears GAT bit
4F34HExtent Entry Exhausted - Clear and Chain
Marks extent entry as unused (FFFFH), follows chain pointers
4F6FHWrite Directory and GAT After Deallocation
Writes updated directory and GAT sectors to disk
4F7BHBuild Filespec Into FCB
Constructs NAME/EXT:D filespec string from directory entry into FCB
4FD0HKill File Handler
Verifies write access, deallocates all granules, clears directory entry and FCB
5039HFree Granules In Extent Entry
Processes one extent entry pair, frees allocated granules from GAT
505BHBuild GAT Bit Mask
Converts granule position to RES opcode for self-modifying GAT bit clear
5068HRead GAT Sector
Reads GAT sector (track 0, sector 1) into 5100H buffer
5077HWrite GAT Sector
Writes GAT sector from 5100H buffer back to disk
5086HUnused Padding
FFH fill bytes to end of overlay (5086H-50FFH)

Cross-Reference Notes

SYS3 is invoked by the SYS0 jump table entries at 4428H (Close File, SVC code 95H) and 442CH (Kill File, SVC code A5H). Both entry points load the SVC code into Register A and execute RST 28H, which triggers the SYS0 overlay loader at 4BA2H to load SYS3 into the 4E00H overlay area from disk.

SYS3 calls the following SYS0 resident routines: 4878H (set up overlay call context), 4892H (verify file open and set up overlay context), 4AC1H (read directory sector into 4200H buffer), 4AD6H (write directory and GAT sectors), 4AF0H (read directory sector into 4D00H buffer), 4B03H (write sector from 4D00H buffer), 4B35H (read sector with retry), 4B55H (track cache lookup), 4B84H (16-bit divide HL / A), and 46EFH (disk read routine).

SYS3 is called by any program that opens files through TRSDOS, including BASIC programs using OPEN/CLOSE statements, and the DOS command processor when executing file-related commands. SYS2 (Open File) is the complementary overlay that populates the FCB fields that SYS3 later reads back during close or kill operations.

Dissassembly

4E00H - Request Code Dispatcher and FCB Validator

Entry point for SYS3. Validates the FCB pointed to by Register DE, checking that the buffer address, drive number, directory info byte, and sector count fields are all within legal ranges. If validation passes, extracts the request code from bits 4-6 of Register A (preserved on the stack from the original SVC call) and dispatches to the Close File handler (10H) at 4E6DH or the Kill File handler (20H) at 4FD0H. Any other request code returns silently. If validation fails, returns error code 26H (file not open).

4E00
PUSH IX DD E5
Save Register IX onto the stack. IX holds the caller's index register which must be preserved across the overlay call.
4E02
PUSH HL E5
Save Register HL onto the stack. HL holds the caller's general-purpose pointer which must be preserved.
4E03
PUSH AF F5
Save Register AF onto the stack. Register A contains the original SVC code whose bits 4-6 encode the request type (10H = Close, 20H = Kill). This value will be recovered after validation to dispatch to the correct handler.
4E04
LD A,D 7A
Load Register A with the high byte of the FCB address from Register D. Register DE was set by the caller to point to the FCB (File Control Block) that is being closed or killed. The high byte indicates which 256-byte page the FCB resides in.
4E05
CP A,40H FE 40
Compare Register A (FCB high address byte) against 40H. If the FCB address is below 4000H, it would be in ROM space, which is invalid for an FCB. If Register A is less than 40H, the CARRY FLAG is set.
4E07
If the CARRY FLAG is set (FCB address high byte is below 40H, meaning the FCB is in ROM space below 4000H), JUMP to 4E65H to return error 26H. An FCB cannot reside in ROM.
4E09
LD HL,404AH 21 4A 40
Point Register HL to 404AH, which contains the high memory page boundary. This byte holds the top-of-usable-RAM value divided by 256 (the high byte of the highest valid RAM address). It is used to verify the FCB does not reside above available memory.
4E0C
CP A,(HL) BE
Compare Register A (FCB high address byte) against the value at 404AH (high memory boundary). If the FCB address exactly equals the boundary, it is still valid (the last usable page).
4E0D
If the Z FLAG is set (FCB high address byte equals the high memory boundary exactly), JUMP to 4E11H to continue validation. The FCB is at the highest valid page, which is acceptable.
4E0F
If the NO CARRY FLAG is set (FCB high address byte is above the high memory boundary), JUMP to 4E65H to return error 26H. The FCB resides above the top of usable RAM.
4E11
LD A,(DE) 1A
Load Register A with the first byte of the FCB (at the address in Register DE). This is the FCB status byte at offset IX+00H. Bit 7 of this byte indicates whether the file is currently open (bit 7 set = file open).
4E12
BIT 7,A CB 7F
Test bit 7 of Register A (FCB status byte). If bit 7 is set, the file is open and the Z FLAG is cleared (NZ). If bit 7 is clear, the file is not open and the Z FLAG is set.
4E14
If the Z FLAG is set (bit 7 of FCB status byte is clear, meaning the file is not open), JUMP to 4E65H to return error 26H. Cannot close or kill a file that is not open.
4E16
PUSH DE D5
Save Register DE (FCB base address) onto the stack. This will be transferred to Register IX for indexed access to FCB fields.
4E17
POP IX DD E1
Restore the value just pushed (FCB base address) into Register IX. This is a compact way to copy DE to IX. From this point on, IX points to the FCB and all field accesses use IX+offset notation.
4E19
LD A,(IX+04H) DD 7E 04
Load Register A with the byte at FCB offset 04H. This is the high byte of the sector buffer base address (IX+03H/04H stores the 16-bit buffer pointer, with 04H being the high byte). The sector buffer must reside in valid RAM.
4E1C
CP A,40H FE 40
Compare Register A (sector buffer high byte) against 40H. If the buffer address is below 4000H, it would be in ROM, which is invalid.
4E1E
If the CARRY FLAG is set (buffer high byte below 40H), JUMP to 4E65H. The sector buffer cannot be in ROM space.
4E20
CP A,(HL) BE
Compare Register A (buffer high byte) against the value at 404AH (high memory boundary, still pointed to by HL from 4E09H).
4E21
If the Z FLAG is set (buffer high byte equals the memory boundary exactly), JUMP to 4E25H to continue. The buffer is at the highest valid page.
4E23
If the NO CARRY FLAG is set (buffer high byte exceeds the memory boundary), JUMP to 4E65H. The sector buffer is above usable RAM.
4E25
LD A,(IX+06H) DD 7E 06
Load Register A with the byte at FCB offset 06H, which is the drive number (0-3). TRSDOS 2.3 supports up to 4 disk drives numbered 0 through 3.
4E28
CP A,00H FE 00
Compare Register A (drive number) against 00H. This sets the flags for the following carry check. Since drive 0 is valid, this comparison ensures the value is not negative (which cannot happen for an unsigned byte, but the pattern is consistent with the range-check style used throughout).
4E2A
If the CARRY FLAG is set (drive number is somehow below 0, which is impossible for an unsigned byte), JUMP to 4E65H. This is a defensive check that can never actually trigger but is part of the standard validation template.
4E2C
CP A,04H FE 04
Compare Register A (drive number) against 04H. Valid drives are 0-3, so a value of 4 or higher is invalid. If Register A >= 4, the NO CARRY FLAG is set.
4E2E
If the NO CARRY FLAG is set (drive number is 4 or higher), JUMP to 4E65H to return error 26H. The drive number exceeds the maximum of 3.
4E30
LD A,(IX+07H) DD 7E 07
Load Register A with the byte at FCB offset 07H, the directory entry info byte. Bits 7-5 encode the directory sector offset (which sector within the directory contains this file's entry). Bits 4-0 encode the position within that sector. Bits 4 and 3 have special meanings as flags.
4E33
BIT 4,A CB 67
Test bit 4 of Register A (directory entry info byte). In the TRSDOS 2.3 directory structure, bit 4 of the info byte is reserved and must be 0 for a valid file entry. If bit 4 is set, the Z FLAG is cleared (NZ).
4E35
If the NZ FLAG is set (bit 4 of the directory info byte is set), JUMP to 4E65H to return error 26H. This bit must be clear for a valid directory entry.
4E37
BIT 3,A CB 5F
Test bit 3 of Register A (directory entry info byte). Bit 3 is also reserved and must be 0 for a valid file entry.
4E39
If the NZ FLAG is set (bit 3 of the directory info byte is set), JUMP to 4E65H to return error 26H. Another reserved bit that must be clear.
4E3B
LD A,(IX+0EH) DD 7E 0E
Load Register A with the byte at FCB offset 0EH. This field holds the high byte of the total sector count (when combined with IX+0FH, it forms the 16-bit total sector count for the file).
4E3E
CP A,00H FE 00
Compare Register A (sector count high byte) against 00H. If the high byte is zero, the total sector count is 0-255 and further checking of the low byte is needed.
4E40
If the NZ FLAG is set (sector count high byte is non-zero, meaning the file has 256 or more sectors), JUMP to 4E4CH to skip the low-byte minimum check. Files with 256+ sectors are inherently large enough.
4E42
LD A,(IX+0FH) DD 7E 0F
Load Register A with the byte at FCB offset 0FH, the low byte of the total sector count. Since the high byte was zero, Register A now holds the full sector count (0-255).
4E45
CP A,20H FE 20
Compare Register A (total sector count) against 20H (32 decimal). A minimum of 32 sectors is required for a valid file in TRSDOS 2.3. Files with fewer than 32 sectors have an invalid sector count in the FCB. If Register A is less than 20H, the CARRY FLAG is set.
4E47
If the CARRY FLAG is set (total sector count is less than 32), JUMP to 4E65H to return error 26H. The FCB contains an invalid sector count that is too small.
4E49
LD A,(IX+0EH) DD 7E 0E
Load Register A again with the byte at FCB offset 0EH (sector count high byte). This re-read after the low-byte check is needed because the 4E40H branch for non-zero high bytes skipped the low-byte check and falls through here.
4E4C
CP A,0FFH FE FF
Compare Register A (sector count high byte) against FFH. A high byte of FFH indicates a special file type (system file or extended file) rather than an error, so it is accepted.
4E4E
If the Z FLAG is set (sector count high byte is FFH, indicating a special/system file), JUMP to 4E54H to accept the FCB and proceed with the request dispatch.
4E50
CP A,23H FE 23
Compare Register A (sector count high byte) against 23H (35 decimal). The maximum sector count high byte is 22H (total sectors = 22FFH = 8959, which is the maximum for a TRSDOS 2.3 single-density disk). A value of 23H or above (except FFH) is invalid.
4E52
If the NO CARRY FLAG is set (sector count high byte is 23H or above), JUMP to 4E65H to return error 26H. The sector count exceeds the maximum possible for a TRSDOS 2.3 disk.
4E54
POP AF F1
Restore Register AF from the stack. Register A now contains the original SVC code that was saved at 4E03H. Bits 4-6 of this value encode the request type: 10H = Close File, 20H = Kill File.
4E55
POP HL E1
Restore Register HL from the stack (the caller's HL saved at 4E02H).
4E56
POP IX DD E1
Restore Register IX from the stack (the caller's IX saved at 4E00H). Note: the FCB pointer was loaded into IX at 4E17H via PUSH DE / POP IX, but the caller's original IX was saved first and is now restored. The FCB pointer in DE is still available for the handlers to reconstruct.
4E58
AND A,70H E6 70
Mask Register A (original SVC code) with 70H (binary 01110000) to isolate bits 4-6, which encode the request type. This extracts the function number: 10H = Close File, 20H = Kill File.
4E5A
CP A,10H FE 10
Compare the masked request code against 10H (Close File). If the request code is 10H, the Z FLAG is set.
4E5C
If the Z FLAG is set (request code is 10H), JUMP to 4E6DH to execute the Close File handler.
4E5F
CP A,20H FE 20
Compare the masked request code against 20H (Kill File). If the request code is 20H, the Z FLAG is set.
4E61
If the Z FLAG is set (request code is 20H), JUMP to 4FD0H to execute the Kill File handler.
4E64
RET C9
Return to the caller. If the request code was neither 10H nor 20H, return with no action. This should not occur under normal operation since SYS3 is only loaded for Close and Kill requests.

4E65H - Validation Failure Exit

Reached when any of the FCB validation checks fail. Restores the original registers from the stack and returns error code 26H (file not open / invalid FCB) with the NZ flag set to indicate an error to the caller.

4E65
POP AF F1
Restore Register AF from the stack (discard the saved SVC code from 4E03H).
4E66
POP HL E1
Restore Register HL from the stack (the caller's HL saved at 4E02H).
4E67
POP IX DD E1
Restore Register IX from the stack (the caller's IX saved at 4E00H).
4E69
LD A,26H 3E 26
Load Register A with error code 26H. In TRSDOS 2.3, error 26H means "file not open" or more broadly indicates an invalid FCB was passed to a file operation.
4E6B
OR A,A B7
OR Register A with itself. This does not change the value but sets the NZ FLAG (since A = 26H, which is non-zero). The NZ flag signals to the caller that an error occurred. Register A contains the error code.
4E6C
RET C9
Return to the caller with error code 26H in Register A and the NZ FLAG set.

4E6DH - Close File Handler

Handles request code 10H (Close File). First calls SYS0 routines to verify the file is open and set up the overlay context. Then reads the directory sector containing this file's entry using the drive number (IX+06H) and directory entry info byte (IX+07H). Compares the FCB's current EOF offset (IX+08H) and extent chain position (IX+0CH/0DH) against the on-disk directory entry. If they differ, updates the directory entry with the FCB's values and writes the sector back to disk. Then walks the extent chain to count total allocated sectors and deallocates any granules beyond the file's actual usage.

4E6D
GOSUB to SYS0 routine at 4892H to verify the file is open and set up the overlay call context. This routine checks the FCB status byte and prepares Register IX to point to the FCB. Returns with Z FLAG set on success, NZ FLAG set on error (error code in A).
4E70
RET NZ C0
If the NZ FLAG is set (error from 4892H, file not properly open), return immediately with the error code in Register A.
4E71
GOSUB to SYS0 routine at 4878H to set up the overlay call context. This saves the caller's return address and FCB pointer into the overlay context area at 430AH-430BH, ensuring the overlay can properly return to the caller when done.
4E74
RET NZ C0
If the NZ FLAG is set (error from 4878H), return immediately with the error code.
4E75
LD B,(IX+07H) DD 46 07
Load Register B with the directory entry info byte from FCB offset 07H. Bits 7-5 encode the directory sector offset (which of the directory's sectors contains this file's entry). Bits 4-0 encode the entry's byte position within that sector. Register B will be used by the directory read routine.
4E78
LD C,(IX+06H) DD 4E 06
Load Register C with the drive number from FCB offset 06H. The directory read routine at 4AC1H needs the drive number in Register C to select the correct disk drive.
4E7B
GOSUB to SYS0 routine at 4AC1H to read the directory sector for this file into the 4200H buffer. Register B contains the directory entry info byte, Register C contains the drive number. On return, HL points to the base of this file's directory entry within the 4200H buffer. Returns Z FLAG on success.
4E7E
RET NZ C0
If the NZ FLAG is set (directory read error), return with the error code.

At this point, HL points to the base of the file's 32-byte directory entry in the 4200H buffer. The entry layout places the EOF byte offset at relative position +03H and the extent chain data starting at +14H (offset 20 decimal). The code now computes the address of the EOF offset field.

4E7F
LD A,03H 3E 03
Load Register A with 03H. This is the offset within the directory entry to the EOF byte offset field (the byte offset within the last sector where the file ends).
4E81
ADD A,L 85
ADD 03H to Register L (low byte of the directory entry base address in the 4200H buffer). This computes the address of the EOF byte offset field within the directory entry. The ADD to L alone works because directory entries are aligned within the 256-byte sector buffer.
4E82
LD L,A 6F
Store the computed address back into Register L. Register HL now points to the EOF byte offset field within the directory entry in the 4200H buffer.
4E83
PUSH HL E5
Save Register HL (pointer to EOF byte offset in directory entry) onto the stack. This pointer will be needed later after the comparison to write updated values if they differ.
4E84
LD A,(IX+08H) DD 7E 08
Load Register A with the byte at FCB offset 08H, the EOF byte offset. This is the FCB's current record of where the file ends within its last sector (00H means the file ends exactly on a sector boundary).
4E87
CP A,(HL) BE
Compare Register A (FCB's EOF byte offset) against the value at (HL) (the directory entry's EOF byte offset at entry+03H). If they match, the directory already has the correct EOF offset.
4E88
If the NZ FLAG is set (FCB EOF offset differs from directory EOF offset), JUMP to 4E9EH to update the directory entry with the FCB's values.

The EOF byte offset matches. Now check the extent chain position fields at entry+14H and entry+15H against FCB fields IX+0CH and IX+0DH.

4E8A
LD A,11H 3E 11
Load Register A with 11H (17 decimal). Adding this to the current L value (which points to entry+03H) produces entry+14H, the location of the extent chain position low byte in the directory entry.
4E8C
ADD A,L 85
ADD 11H to Register L to advance from the EOF byte offset field (entry+03H) to the extent chain position field (entry+14H).
4E8D
LD L,A 6F
Store the result in Register L. HL now points to directory entry offset +14H (extent chain position low byte).
4E8E
LD A,(IX+0CH) DD 7E 0C
Load Register A with the byte at FCB offset 0CH, the extent chain position low byte from the FCB. This tracks the current position within the file's extent chain.
4E91
CP A,(HL) BE
Compare Register A (FCB extent chain position low byte) against the directory entry's value at entry+14H.
4E92
If the NZ FLAG is set (extent chain low bytes differ), JUMP to 4E9EH to update the directory entry.
4E94
INC L 2C
INCrement Register L by 1. HL now points to directory entry offset +15H (extent chain position high byte).
4E95
LD A,(IX+0DH) DD 7E 0D
Load Register A with the byte at FCB offset 0DH, the extent chain position high byte from the FCB.
4E98
CP A,(HL) BE
Compare Register A (FCB extent chain position high byte) against the directory entry's value at entry+15H.
4E99
If the NZ FLAG is set (extent chain high bytes differ), JUMP to 4E9EH to update the directory entry.

All three fields (EOF offset, extent chain low, extent chain high) match between the FCB and the directory entry. No directory write is needed. Pop the saved directory pointer and skip directly to the extent chain walk at 4EB6H.

4E9B
POP AF F1
Discard the saved HL value (directory entry pointer from 4E83H) by popping it into AF. It is no longer needed since no directory update is required.
4E9C
JUMP unconditionally to 4EB6H to proceed with the extent chain walk and sector counting, skipping the directory write since all fields already match.

4E9EH - Update Directory Entry From FCB

Reached when at least one of the three compared fields (EOF offset, extent chain low, extent chain high) differs between the FCB and the on-disk directory entry. Writes the FCB's current values into the directory entry in the 4200H buffer, then calls 4AD6H to write the updated directory and GAT sectors to disk.

4E9E
POP HL E1
Restore Register HL from the stack (the pointer to directory entry+03H, the EOF byte offset field, saved at 4E83H).
4E9F
LD A,(IX+08H) DD 7E 08
Load Register A with the FCB's EOF byte offset from offset 08H.
4EA2
LD (HL),A 77
Store Register A (FCB EOF byte offset) into the directory entry at offset +03H in the 4200H buffer. This updates the directory's record of where the file ends within its last sector.
4EA3
LD A,11H 3E 11
Load Register A with 11H (17 decimal) to compute the offset from entry+03H to entry+14H (extent chain position low byte).
4EA5
ADD A,L 85
ADD 11H to Register L to advance HL from entry+03H to entry+14H.
4EA6
LD L,A 6F
Store the result in Register L. HL now points to directory entry offset +14H (extent chain position low byte).
4EA7
LD A,(IX+0CH) DD 7E 0C
Load Register A with the FCB's extent chain position low byte from offset 0CH.
4EAA
LD (HL),A 77
Store Register A (FCB extent chain position low byte) into the directory entry at offset +14H.
4EAB
INC L 2C
INCrement Register L by 1 to advance to directory entry offset +15H (extent chain position high byte).
4EAC
LD A,(IX+0DH) DD 7E 0D
Load Register A with the FCB's extent chain position high byte from offset 0DH.
4EAF
LD (HL),A 77
Store Register A (FCB extent chain position high byte) into the directory entry at offset +15H.
4EB0
PUSH HL E5
Save Register HL (pointer into directory entry at offset +15H) onto the stack. It will be needed after the directory write to continue with the extent chain walk.
4EB1
GOSUB to SYS0 routine at 4AD6H to write the updated directory and GAT sectors from the 4200H buffer to disk. This commits the EOF offset and extent chain position changes to the physical directory.
4EB4
POP HL E1
Restore Register HL from the stack (pointer to directory entry offset +15H).
4EB5
RET NZ C0
If the NZ FLAG is set (directory write error from 4AD6H), return immediately with the error code in Register A.

4EB6H - Extent Chain Sector Counter

Walks the file's extent chain in the directory entry, accumulating a total sector count in Register DE. Each extent entry consists of a pair of bytes: the first byte encodes the starting granule (track and position), and bits 4-0 of the second byte encode the granule count minus 1 for that extent. The loop reads pairs of bytes starting at directory entry offset +16H, advancing through the extent chain until it encounters either FEH (end of extent chain with continuation) or FFH (end of extent chain, no continuation). The accumulated sector count will be compared against the FCB's record to determine if granules need to be deallocated.

4EB6
INC L 2C
INCrement Register L by 1. After the directory write path, HL was at entry+15H; this advances to entry+16H. After the no-update path (from 4E9CH), HL was at entry+15H from the last comparison; this also advances to entry+16H. Entry+16H is the start of the extent chain data.
4EB7
LD DE,0000H 11 00 00
Load Register DE with 0000H. Register DE serves as the running sector count accumulator. It will be incremented as each extent entry's granule count is added.
4EBA
LD A,(HL) 7E
Loop Start
Load Register A with the first byte of the current extent entry (the starting granule descriptor). This byte encodes the track and granule position. Special values FEH and FFH mark the end of the extent chain.
4EBB
INC L 2C
INCrement Register L by 1 to advance to the second byte of the extent entry pair (the granule count byte).
4EBC
CP A,0FEH FE FE
Compare Register A (first extent byte) against FEH. If A >= FEH (which includes both FEH and FFH), the NO CARRY FLAG is set. FEH means the extent chain continues in another directory sector. FFH means the extent chain is complete.
4EBE
If the NO CARRY FLAG is set (first extent byte is FEH or FFH, indicating end of the extent chain in this sector), JUMP to 4ECCH to handle the chain terminator.
4EC0
LD A,(HL) 7E
Load Register A with the second byte of the extent entry (granule count byte). Bits 4-0 encode the number of granules in this extent minus 1 (so 00H = 1 granule, 1FH = 32 granules).
4EC1
INC L 2C
INCrement Register L by 1 to advance past this extent entry pair, positioning HL at the start of the next extent entry.
4EC2
AND A,1FH E6 1F
Mask Register A with 1FH (binary 00011111) to isolate bits 4-0, the granule count minus 1. Bits 7-5 of this byte contain other extent information and are discarded.
4EC4
INC A 3C
INCrement Register A by 1 to convert from "count minus 1" to actual granule count. For example, if the stored value was 04H (5 granules minus 1), A now holds 05H (5 granules).
4EC5
ADD A,E 83
ADD Register A (granule count for this extent) to Register E (running sector count low byte). Each granule corresponds to one allocation unit. The sum accumulates in DE.
4EC6
LD E,A 5F
Store the sum back into Register E.
4EC7
If the NO CARRY FLAG is set (no overflow from the ADD), LOOP BACK to 4EBAH to process the next extent entry.
4EC9
INC D 14
INCrement Register D by 1 to handle the carry from the low byte addition. This propagates the overflow from E into the high byte D of the 16-bit sector count.
4ECA
LOOP BACK unconditionally to 4EBAH to process the next extent entry. Loop End

4ECCH - Extent Chain Terminator Handler

Reached when the extent chain walker encounters a byte >= FEH. If the byte is FEH, the extent chain continues in a different directory sector (the next byte contains the continuation sector info) and the code follows the chain. If the byte is FFH, the extent chain is complete and the code proceeds to compare the total accumulated sector count against the FCB's extent data to determine whether granule deallocation is needed.

4ECC
If the NZ FLAG is set (the first extent byte was FFH, not FEH, because the CP FEH at 4EBCH would set Z for FEH and NZ for FFH since FFH > FEH), JUMP to 4ED9H to handle the "chain complete" case. Register A was the first extent byte, and CP FEH set Z only for FEH (exact match).

The first extent byte is FEH, meaning the extent chain continues in a different directory sector. The second byte of this pair (at the current HL position) contains the directory entry info byte for the continuation sector. The code reads the continuation entry sector, then resumes the extent chain walk.

4ECE
LD B,(HL) 46
Load Register B with the continuation directory entry info byte (the second byte of the FEH pair). This byte encodes the sector and position of the next directory entry in the chain, using the same format as IX+07H.
4ECF
GOSUB to SYS0 routine at 4AC1H to read the continuation directory sector into the 4200H buffer. Register B has the directory entry info byte, and Register C still holds the drive number from the initial setup. On return, HL points to the base of the continuation entry.
4ED2
RET NZ C0
If the NZ FLAG is set (directory read error), return with the error code.
4ED3
LD A,L 7D
Load Register A with the low byte of HL (the byte position of the continuation entry within the sector buffer). The extent data starts at offset +16H within each directory entry.
4ED4
ADD A,16H C6 16
ADD 16H (22 decimal) to Register A to advance from the entry base to the extent chain data area at entry+16H.
4ED6
LD L,A 6F
Store the result in Register L. HL now points to the extent chain data area of the continuation directory entry.
4ED7
LOOP BACK to 4EBAH to resume the extent chain walk from the continuation entry, continuing to accumulate the sector count in Register DE.

4ED9H - Compute Sectors to Deallocate

Reached when the extent chain is fully walked and FFH is encountered. Register DE holds the total number of allocated sectors (accumulated granule count from all extents). This routine compares the total allocated sectors against the FCB's extent chain position (IX+0CH/0DH), which represents the file's actual sector usage. If the file uses fewer sectors than are allocated, the excess granules must be freed from the GAT. The computation uses the SYS0 16-bit divide routine at 4B84H to convert sector counts to granule counts.

4ED9
PUSH HL E5
Save Register HL (current position in the extent chain data area) onto the stack. This pointer will be needed later when deallocating granules from the chain.
4EDA
LD L,(IX+0CH) DD 6E 0C
Load Register L with the FCB's extent chain position low byte from offset 0CH. This is the number of sectors the file actually uses (low byte).
4EDD
LD H,(IX+0DH) DD 66 0D
Load Register H with the FCB's extent chain position high byte from offset 0DH. Register HL now holds the file's actual sector usage count.
4EE0
LD A,05H 3E 05
Load Register A with 05H (5 decimal). This is the number of sectors per granule on a TRSDOS 2.3 single-density disk. Each granule is 5 sectors.
4EE2
GOSUB to SYS0 routine at 4B84H to perform 16-bit divide: HL = HL / A. Divides the FCB's actual sector count (in HL) by 5 (sectors per granule). On return, HL holds the quotient (number of complete granules the file needs), and Register A holds the remainder (extra sectors beyond complete granules).
4EE5
INC HL 23
INCrement Register HL by 1. This rounds up the granule count: if the file uses any sectors in a partial granule, that granule must be kept allocated. After this, HL holds the minimum number of granules that must remain allocated.
4EE6
XOR A,A AF
Set Register A to 00H by XORing A with itself. This clears the carry flag and zeros A in preparation for the subtraction.
4EE7
EX DE,HL EB
Exchange DE and HL. Now DE = minimum granules needed (from the divide), HL = total allocated sectors (from the extent chain walk). This swaps them so HL holds the total and DE holds the minimum.
4EE8
SBC HL,DE ED 52
SUBtract DE (minimum granules needed) from HL (total allocated granules) with borrow. The result in HL is the number of excess granules that should be deallocated. If HL is zero or negative, no deallocation is needed.
4EEA
EX DE,HL EB
Exchange DE and HL again. Now DE = excess granule count (the number to deallocate), HL = minimum granules needed.
4EEB
POP HL E1
Restore Register HL from the stack (the pointer into the extent chain data area, saved at 4ED9H).
4EEC
If the Z FLAG is set (excess granule count is zero, meaning the file uses all allocated granules), JUMP to 4F7BH to skip deallocation and proceed with building the filespec into the FCB.
4EEF
If the CARRY FLAG is set (the subtraction produced a negative result, meaning more granules are needed than allocated, which indicates the file has grown since the extent data was recorded), JUMP to 4F7BH. No deallocation is needed.

4EF2H - Granule Deallocation Loop Setup

Prepares for the granule deallocation loop. Register DE holds the number of excess granules to deallocate. HL points into the extent chain data area. The loop walks backward through the extent chain, freeing granules from the GAT for each excess. This requires reading the GAT sector into the 5100H buffer, then processing each extent entry from the end of the chain backward.

4EF2
DEC L 2D
DECrement Register L by 1. HL was pointing past the last extent entry (at the FFH terminator). Backing up two bytes positions HL at the second byte of the last valid extent entry (the granule count byte).
4EF3
DEC L 2D
DECrement Register L by 1 again. HL now points to the first byte of the last valid extent entry (the starting granule descriptor byte).
4EF4
PUSH HL E5
Save Register HL (pointer to the last extent entry) onto the stack for later restoration.
4EF5
PUSH DE D5
Save Register DE (excess granule count to deallocate) onto the stack.
4EF6
GOSUB to SYS0 routine at 4AF0H to read the directory sector into the 4D00H secondary buffer. This loads a fresh copy of the directory sector for the deallocation process, separate from the 4200H primary buffer.
4EF9
If the NZ FLAG is set (directory read error), JUMP to 4EFEH to skip the GAT read and fall through to the error return path.
4EFB
GOSUB to the Read GAT Sector routine at 5068H within this overlay. This reads the GAT (Granule Allocation Table) from track 0, sector 1 of the current drive into the 5100H buffer. The GAT contains a bitmap where each bit represents one granule on the disk; a set bit means the granule is allocated.
4EFE
POP DE D1
Restore Register DE (excess granule count) from the stack.
4EFF
POP HL E1
Restore Register HL (pointer to the last extent entry) from the stack.
4F00
RET NZ C0
If the NZ FLAG is set (error from either the directory read or GAT read), return with the error code in Register A.
4F01
LD A,E 7B
Loop Start
Load Register A with the low byte of the excess granule count (Register E). This checks whether there are still granules to deallocate.
4F02
CP A,00H FE 00
Compare Register A against 00H. If the remaining excess count (low byte) is zero, all granules have been deallocated.
4F04
If the Z FLAG is set (remaining excess count low byte is zero), JUMP to 4F6FH to write the updated directory and GAT to disk and finish the close operation. Note: this only checks the low byte, so it works correctly as long as the excess count does not exceed 255. For a TRSDOS 2.3 single-density disk, the maximum granule count is about 68 (340 sectors / 5), so this single-byte check is sufficient.

4F06H - Decode and Free Single Granule

Processes one granule from the current extent entry. Extracts the track number and granule position from the extent entry byte pair, computes the GAT byte and bit position, and clears the corresponding bit in the GAT sector buffer at 5100H to mark the granule as free. Then decrements the granule count in the extent entry and loops back for the next granule.

4F06
PUSH DE D5
Save Register DE (remaining excess granule count) onto the stack.
4F07
LD A,(HL) 7E
Load Register A with the first byte of the current extent entry (at HL). This byte encodes the starting granule: bits 7-5 contain the relative granule position within the track, and bits 4-0 contain additional extent information.
4F08
AND A,0E0H E6 E0
Mask Register A with E0H (binary 11100000) to isolate bits 7-5, which encode the granule position within the track. There are up to 8 granules per track (positions 0-7), stored in the top 3 bits.
4F0A
RLCA 07
Rotate Register A left through carry. This shifts the 3-bit granule position left, moving bit 7 into bit 0.
4F0B
RLCA 07
Rotate Register A left again.
4F0C
RLCA 07
Rotate Register A left a third time. The three rotates move the top 3 bits (positions 7-5) into positions 2-0, converting the granule position to a 0-7 value.
4F0D
LD E,A 5F
Store the granule position (0-7) in Register E for later use in the bit mask calculation.
4F0E
LD A,(HL) 7E
Reload Register A with the first extent byte. This time the lower bits will be extracted.
4F0F
AND A,1FH E6 1F
Mask Register A with 1FH (binary 00011111) to isolate bits 4-0, which encode the granule count minus 1 for this extent (the number of consecutive granules starting from the position).
4F11
ADD A,E 83
ADD Register E (granule position, 0-7) to Register A (granule count minus 1). This computes the position of the last granule in this extent: starting position + count - 1 = last granule index.
4F12
LD E,A 5F
Store the last granule index in Register E.
4F13
AND A,0FEH E6 FE
Mask Register A with FEH (binary 11111110) to clear bit 0. This rounds the granule index down to the nearest even number. Each GAT byte covers 2 granules (one in each nibble), so this selects the GAT byte offset.
4F15
RRCA 0F
Rotate Register A right through carry. This divides by 2, converting the granule pair index into the GAT byte offset. Each byte in the GAT covers 2 granules.
4F16
LD (4F22H),A 32 22 4F
Self-Modifying Code
Store Register A (GAT byte offset / track-related offset) into the operand of the ADD A,00H instruction at 4F21H. At runtime, the instruction at 4F21H becomes ADD A,[track offset], which adds the track's starting position in the GAT to compute the correct GAT buffer address.
4F19
LD A,E 7B
Load Register A with the last granule index (from Register E).
4F1A
AND A,01H E6 01
Mask Register A with 01H to isolate bit 0. This determines which of the two granules in the GAT byte to clear: 0 = lower granule (bits 3-0), 1 = upper granule (bits 7-4).
4F1C
INC A 3C
INCrement Register A by 1. Now A = 1 for the lower granule or 2 for the upper granule.
4F1D
CPL 2F
Complement Register A (invert all bits). A = FEH (11111110) for lower granule or FDH (11111101) for upper granule. This will be used as a mask to clear the appropriate bit.
4F1E
PUSH AF F5
Save Register AF (the bit mask) onto the stack. The mask will be applied to the GAT byte after computing the GAT buffer address.
4F1F
DEC HL 2B
DECrement Register HL by 1. This backs up from the current extent entry's first byte to the preceding byte, which is the second byte of the previous extent entry. This byte's value encodes the track number for the granule.
4F20
LD A,(HL) 7E
Load Register A with the track number byte. This byte (from the previous extent entry's second byte or the extent chain's track data) identifies which track on the disk contains the granule being deallocated.
4F21
ADD A,00H C6 00
Self-Modifying Code
ADD the operand (originally 00H, but modified at runtime by the LD at 4F16H to contain the GAT byte offset) to Register A (track number). The result is the offset into the GAT buffer at 5100H for the byte that controls this granule's allocation bit.
4F23
EX DE,HL EB
Exchange DE and HL. Register DE now holds the extent chain pointer, and HL is free for constructing the GAT buffer address.
4F24
LD H,4DH 26 4D
Load Register H with 4DH. Combined with the offset in L (computed next), HL will point into the 4D00H directory sector buffer where the GAT-related data was loaded.
4F26
LD L,A 6F
Load Register L with the computed GAT byte offset (from the ADD at 4F21H). HL now points to the specific byte in the 4D00H buffer area that contains the allocation bit for the granule being freed.
4F27
POP AF F1
Restore Register AF from the stack. Register A now contains the bit mask (FEH or FDH) computed at 4F1DH.
4F28
AND A,(HL) A6
AND Register A (bit mask) with the byte at (HL) (the GAT byte in the 4D00H buffer). This clears the specific bit corresponding to the granule being deallocated while preserving all other bits.
4F29
LD (HL),A 77
Store the modified GAT byte back to the buffer. The granule's allocation bit is now cleared, marking it as free in the GAT.
4F2A
EX DE,HL EB
Exchange DE and HL. HL now points back to the extent chain data area, DE is free.
4F2B
INC HL 23
INCrement Register HL by 1 to reposition at the first byte of the current extent entry (advancing from the track byte we backed up to at 4F1FH).
4F2C
DEC (HL) 35
DECrement the byte at (HL) by 1. This decrements the granule count byte in the second byte of the current extent entry, reflecting that one granule has been freed.
4F2D
LD A,(HL) 7E
Load Register A with the updated granule count byte.
4F2E
INC A 3C
INCrement Register A by 1. The extent entry stores the count as "count minus 1", so if the stored value went to FFH (underflow from 00H), adding 1 produces 00H. This means the extent had only 1 granule and it has now been fully consumed.
4F2F
AND A,1FH E6 1F
Mask with 1FH to isolate the 5-bit granule count field (bits 4-0). If this is zero, the extent entry has been fully consumed and needs to be cleared.
4F31
POP DE D1
Restore Register DE (remaining excess granule count) from the stack (saved at 4F06H).
4F32
If the NZ FLAG is set (granule count for this extent entry is still non-zero, meaning it has more granules to process), JUMP to 4F6CH to decrement the excess count and loop back for the next granule.

4F34H - Extent Entry Exhausted - Clear and Follow Chain

Reached when the current extent entry's granule count reaches zero after deallocation. The entry must be marked as unused (set both bytes to FFH) and the code must check whether this was the last entry in the current directory sector's extent area. If so, follow the chain pointer to the previous directory entry (going backward through the chain). If not, move to the previous extent entry and continue deallocating.

4F34
LD (HL),0FFH 36 FF
Store FFH into the current position (HL), which is the second byte of the exhausted extent entry. FFH marks this byte as unused.
4F36
DEC HL 2B
DECrement Register HL by 1 to point to the first byte of the exhausted extent entry.
4F37
LD (HL),0FFH 36 FF
Store FFH into the first byte. Both bytes of this extent entry are now FFH, marking it as completely unused.
4F39
DEC HL 2B
DECrement Register HL by 1 to point to the second byte of the previous extent entry (or the chain pointer area if at the start of the extent data).
4F3A
LD A,L 7D
Load Register A with the low byte of HL. The extent data starts at entry+16H within the 32-byte directory entry. Since directory entries are aligned within the 256-byte sector buffer, the low byte of HL encodes the position within the entry.
4F3B
AND A,1FH E6 1F
Mask Register A with 1FH (binary 00011111) to isolate the position within the 32-byte directory entry (entries are 32 bytes, so bits 4-0 give the offset within the entry).
4F3D
CP A,15H FE 15
Compare the entry offset against 15H (21 decimal). The extent chain data area runs from entry+16H to entry+1FH. If the current position is at entry+15H, we have backed past the start of the extent data, meaning all extent entries in this directory sector have been exhausted.
4F3F
If the NZ FLAG is set (we have not backed past the start of the extent data), JUMP to 4F6CH to continue deallocating from the previous extent entry in the same directory sector.

All extent entries in the current directory sector have been exhausted. The code must follow the extent chain continuation pointer to find the previous directory entry in the chain. The chain pointer is located at entry+14H/+15H.

4F41
XOR A,L AD
XOR Register A (which contains the offset 15H from the AND 1FH) with Register L. Since L has the same low 5 bits as 15H (confirmed by the CP), XORing with 15H clears bits 4-0, leaving only the entry base address (the top 3 bits of L that identify which 32-byte slot this entry occupies). This computes the base address of the directory entry.
4F42
LD L,A 6F
Store the entry base address into Register L. HL now points to the beginning of the directory entry (byte 0, which is the directory flags byte).
4F43
LD A,(HL) 7E
Load Register A with the directory entry flags byte (byte 0 of the entry). This byte contains status information including a chain continuation flag in bit 7.
4F44
LD (HL),00H 36 00
Store 00H into the directory entry flags byte, clearing it. This effectively marks the directory entry as deleted/empty since a zero flags byte indicates no active file.
4F46
INC HL 23
INCrement Register HL by 1 to point to byte 1 of the directory entry.
4F47
LD D,(HL) 56
Load Register D with byte 1 of the directory entry. This byte contains the chain pointer data (the directory entry info byte for the previous entry in the extent chain).
4F48
LD H,51H 26 51
Load Register H with 51H. The GAT status tracking area uses 5100H-51FFH. Each byte at address 5100H + (directory info byte high bits) tracks whether that directory sector's GAT data has been modified.
4F4A
LD L,B 68
Load Register L with Register B (the current directory entry info byte from the initial LD B,(IX+07H) at 4E75H). HL now points to 5100H + B, the tracking byte for the current directory sector.
4F4B
LD (HL),00H 36 00
Store 00H at the tracking byte, marking that this directory sector's entry has been cleared.
4F4D
BIT 7,A CB 7F
Test bit 7 of Register A (the original directory entry flags byte loaded at 4F43H). Bit 7 indicates whether this entry has a chain continuation to a previous entry in the extent chain.
4F4F
If the Z FLAG is set (bit 7 is clear, meaning there is no chain continuation), JUMP to 4F6FH to write the final directory and GAT sectors and finish. There is no previous entry to follow in the chain.

Bit 7 of the flags byte is set, meaning this directory entry chains to a previous entry. The chain pointer in Register D (loaded at 4F47H from byte 1 of the entry) identifies the previous directory entry. The code must write the current sector's changes, then read the previous sector and continue deallocation.

4F51
PUSH DE D5
Save Register DE (D = chain pointer info byte, E = remaining excess granule count) onto the stack.
4F52
GOSUB to SYS0 routine at 4AD6H to write the updated directory and GAT sectors from the 4200H buffer to disk. This commits the changes made to the current directory sector (the cleared entry).
4F55
POP DE D1
Restore Register DE from the stack.
4F56
RET NZ C0
If the NZ FLAG is set (write error), return with the error code.
4F57
LD A,B 78
Load Register A with Register B (current directory entry info byte).
4F58
XOR A,D AA
XOR Register A (current info byte) with Register D (chain pointer info byte). The result has bits set wherever the two info bytes differ. Bits 2-0 encode the sector number within the directory, so checking bits 2-0 of the XOR result reveals whether the chain pointer refers to a different directory sector.
4F59
AND A,07H E6 07
Mask with 07H (binary 00000111) to isolate the sector number bits. If the result is non-zero, the chain pointer refers to a different directory sector that must be read from disk.
4F5B
LD B,D 42
Copy the chain pointer info byte (Register D) into Register B. Register B now holds the directory entry info byte for the previous entry in the chain, which will be used for the next directory read.
4F5C
If the NZ FLAG is set (the chain pointer refers to a different directory sector), GOSUB to SYS0 routine at 4AC1H to read that directory sector into the 4200H buffer. If the sectors are the same (Z FLAG), the read is skipped because the data is already in the buffer.
4F5F
RET NZ C0
If the NZ FLAG is set (directory read error), return with the error code.
4F60
LD A,B 78
Load Register A with Register B (the chain pointer info byte for the previous directory entry).
4F61
AND A,0E0H E6 E0
Mask with E0H (binary 11100000) to isolate bits 7-5, which encode the directory entry's position within the sector (which 32-byte slot it occupies). Each sector holds up to 8 directory entries.
4F63
ADD A,1FH C6 1F
ADD 1FH (31 decimal) to the entry base offset. This computes the address of the last byte of the directory entry (byte +1FH = offset 31). For the extent chain data, the last two bytes of the entry (at +1EH and +1FH) are the last extent entry pair. Starting from the end is correct for backward deallocation.
4F65
LD L,A 6F
Store the computed address into Register L. HL now points to byte +1FH (the second byte of the last extent entry pair) in the previous directory entry.
4F66
LD (HL),0FFH 36 FF
Store FFH into the last extent entry pair's second byte, marking it as unused.
4F68
DEC HL 2B
DECrement HL to point to byte +1EH (the first byte of the last extent entry pair).
4F69
LD (HL),0FFH 36 FF
Store FFH into the first byte, fully clearing this extent entry pair.
4F6B
DEC HL 2B
DECrement HL to position at the end of the previous extent entry pair, ready for continued backward processing.
4F6C
DEC E 1D
DECrement Register E (remaining excess granule count) by 1. One granule has been deallocated.
4F6D
LOOP BACK to 4F01H to check if more granules need to be deallocated. Loop End

4F6FH - Write Directory and GAT After Deallocation

Final write sequence after all excess granules have been deallocated (or after an extent chain with no continuation pointer is fully processed). Writes the updated directory sector, writes the updated GAT sector, and writes the secondary buffer, completing the close operation's disk updates.

4F6F
GOSUB to SYS0 routine at 4AD6H to write the updated directory and GAT sectors from the 4200H buffer to disk.
4F72
RET NZ C0
If the NZ FLAG is set (write error), return with the error code.
4F73
GOSUB to the Write GAT Sector routine at 5077H within this overlay. This writes the updated GAT sector from the 5100H buffer back to disk (track 0, sector 1), committing the freed granule bits.
4F76
RET NZ C0
If the NZ FLAG is set (GAT write error), return with the error code.
4F77
GOSUB to SYS0 routine at 4B03H to write the sector from the 4D00H secondary buffer to disk. This writes back the directory sector that was loaded into the secondary buffer at 4EF6H.
4F7A
RET NZ C0
If the NZ FLAG is set (write error), return with the error code. If successful, fall through to 4F7BH.

4F7BH - Build Filespec Into FCB

Constructs a filespec string in "NAME/EXT:D" format from the directory entry and stores it into the FCB area pointed to by IX. This is the final phase of the Close File operation. The routine reads the directory sector for the file (if needed), then copies the 8-character filename, appends a slash separator and the 3-character extension, appends a colon and drive number, and terminates with 03H (end-of-string marker). The filespec is written starting at the FCB base address (IX+00H), overwriting the old FCB status fields since the file is being closed.

4F7B
LD A,(IX+07H) DD 7E 07
Load Register A with the directory entry info byte from FCB offset 07H. This identifies which directory sector and entry position contains the filename for this file.
4F7E
XOR A,B A8
XOR Register A (file's directory info byte) with Register B (the currently loaded directory sector's info byte). The result has bits set wherever the two differ.
4F7F
AND A,1FH E6 1F
Mask with 1FH to check the sector number bits (bits 4-0). If the result is non-zero, the file's directory entry is in a different sector than what is currently loaded in the buffer.
4F81
If the NZ FLAG is set (different directory sector needed), GOSUB to SYS0 routine at 4AC1H to read the correct directory sector into the 4200H buffer. Register B is updated from IX+07H at 4F7BH implicitly through the dispatching logic.
4F84
RET NZ C0
If the NZ FLAG is set (directory read error), return with the error code.
4F85
LD A,(IX+06H) DD 7E 06
Load Register A with the drive number from FCB offset 06H (0-3).
4F88
AND A,03H E6 03
Mask with 03H to ensure only the 2-bit drive number is kept (drives 0-3).
4F8A
OR A,30H F6 30
OR with 30H to convert the drive number to its ASCII digit: 0 becomes 30H (ASCII '0'), 1 becomes 31H (ASCII '1'), 2 becomes 32H (ASCII '2'), 3 becomes 33H (ASCII '3').
4F8C
LD (4FC8H),A 32 C8 4F
Self-Modifying Code
Store the ASCII drive number into the operand of the LD A,00H instruction at 4FC7H. At runtime, the instruction at 4FC7H becomes LD A,[drive ASCII], which loads the drive letter for insertion into the filespec string.
4F8F
LD H,42H 26 42
Load Register H with 42H. The directory sector is in the 4200H buffer, so H = 42H forms the high byte of the buffer address. The low byte will be computed from the directory entry info byte.
4F91
LD A,(IX+07H) DD 7E 07
Load Register A with the directory entry info byte from FCB offset 07H. Bits 7-5 encode the entry position within the sector (0-7, each 32 bytes).
4F94
AND A,0E0H E6 E0
Mask with E0H (binary 11100000) to isolate bits 7-5 (the entry position). This gives the byte offset of the entry base within the 256-byte sector buffer (00H, 20H, 40H, 60H, 80H, A0H, C0H, or E0H).
4F96
OR A,05H F6 05
OR with 05H to add offset 5 to the entry base. In a TRSDOS 2.3 directory entry, bytes 5-12 contain the 8-character filename, so entry+05H is the start of the filename field.
4F98
LD L,A 6F
Store the computed address into Register L. HL now points to the filename field (8 bytes) within the directory entry in the 4200H buffer.
4F99
PUSH HL E5
Save Register HL (pointer to filename in directory entry) onto the stack. It will be needed after the filename copy to locate the extension field.
4F9A
PUSH IX DD E5
Save Register IX (FCB base address) onto the stack.
4F9C
POP DE D1
Restore into Register DE the value just pushed from IX. This is a compact DE = IX copy. Register DE now points to the FCB base address where the filespec string will be stored.
4F9D
LD B,08H 06 08
Load Register B with 08H (8 decimal). This is the loop counter for copying the 8-character filename.
4F9F
LD A,(HL) 7E
Loop Start
Load Register A with the next character of the filename from the directory entry.
4FA0
CP A,20H FE 20
Compare Register A against 20H (ASCII space). Filenames shorter than 8 characters are padded with spaces in the directory. If a space is found, the filename portion is complete.
4FA2
If the Z FLAG is set (character is a space), JUMP to 4FA9H to stop copying the filename. Trailing spaces are not included in the filespec string.
4FA4
LD (DE),A 12
Store the filename character (Register A) into the FCB area at the address in DE.
4FA5
INC HL 23
INCrement HL to advance to the next character in the directory entry filename.
4FA6
INC DE 13
INCrement DE to advance to the next position in the FCB filespec string.
4FA7
DECrement B and LOOP BACK to 4F9FH if B is not zero. Continue copying filename characters until all 8 are processed or a space is found. Loop End
4FA9
POP HL E1
Restore Register HL from the stack (pointer to filename start in directory entry, saved at 4F99H).
4FAA
LD A,L 7D
Load Register A with the low byte of HL (address of the filename start within the directory entry).
4FAB
ADD A,08H C6 08
ADD 08H to Register A to advance from the filename field (entry+05H) to the extension field (entry+0DH). The 8-byte filename is followed by the 3-byte extension.
4FAD
LD L,A 6F
Store the result in Register L. HL now points to the 3-character extension field in the directory entry.
4FAE
LD A,(HL) 7E
Load Register A with the first character of the extension.
4FAF
CP A,20H FE 20
Compare Register A against 20H (ASCII space). If the extension is entirely blank (padded with spaces), there is no extension to include in the filespec.
4FB1
If the Z FLAG is set (first extension character is a space), JUMP to 4FC3H to skip the extension and proceed with the drive specifier. No extension is appended to the filespec.
4FB3
LD A,2FH 3E 2F
Load Register A with 2FH (ASCII '/'). In TRSDOS 2.3 filespec format, the slash character separates the filename from the extension (e.g., "FILENAME/EXT").
4FB5
LD (DE),A 12
Store the slash separator into the FCB filespec string at the current DE position.
4FB6
INC DE 13
INCrement DE to advance past the separator.
4FB7
LD B,03H 06 03
Load Register B with 03H (3 decimal). This is the loop counter for copying the 3-character extension.
4FB9
LD A,(HL) 7E
Loop Start
Load Register A with the next character of the extension from the directory entry.
4FBA
CP A,20H FE 20
Compare Register A against 20H (ASCII space). If a space is found, the remaining extension characters are padding.
4FBC
If the Z FLAG is set (character is a space), JUMP to 4FC3H to stop copying. Trailing spaces are not included.
4FBE
LD (DE),A 12
Store the extension character into the FCB filespec string.
4FBF
INC HL 23
INCrement HL to advance to the next extension character in the directory entry.
4FC0
INC DE 13
INCrement DE to advance to the next position in the FCB filespec string.
4FC1
DECrement B and LOOP BACK to 4FB9H if B is not zero. Continue copying extension characters until all 3 are processed or a space is found. Loop End
4FC3
LD A,3AH 3E 3A
Load Register A with 3AH (ASCII ':'). The colon separates the file name/extension from the drive specifier in the filespec format (e.g., "FILENAME/EXT:0").
4FC5
LD (DE),A 12
Store the colon separator into the FCB filespec string.
4FC6
INC DE 13
INCrement DE to advance past the colon.
4FC7
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the drive number ASCII digit. The operand 00H was modified at runtime by the LD at 4F8CH to contain the ASCII character for the drive number (30H-33H for drives 0-3).
4FC9
LD (DE),A 12
Store the drive number character into the FCB filespec string.
4FCA
INC DE 13
INCrement DE to advance past the drive number.
4FCB
LD A,03H 3E 03
Load Register A with 03H. This is the end-of-string terminator used by TRSDOS display routines (the ROM routine at 021BH prints strings until encountering a 03H byte).
4FCD
LD (DE),A 12
Store the 03H terminator into the FCB filespec string, marking the end of the "NAME/EXT:D" string.
4FCE
XOR A,A AF
Set Register A to 00H and set the Z FLAG. This clears the error code and sets the success flag for the return to the caller.
4FCF
RET C9
Return to the caller with A = 00H (no error) and Z FLAG set (success). The Close File operation is complete. The filespec string has been stored in the FCB area, the directory entry has been updated with the current EOF and extent data, and any excess granules have been freed from the GAT.

4FD0H - Kill File Handler

Handles request code 20H (Kill File). Verifies the file is open and was opened with write access (access mode bits 0-2 must be less than 2, meaning read or write mode, not random access or higher modes). Then reads the GAT sector, the file's directory sector, and walks the entire extent chain, deallocating every granule by calling the Free Granules routine at 5039H. After all granules are freed, clears the directory entry, writes the updates to disk, and zeroes the entire 32-byte FCB.

4FD0
GOSUB to SYS0 routine at 4892H to verify the file is open and set up the overlay call context. Returns Z FLAG on success with IX pointing to the FCB.
4FD3
RET NZ C0
If the NZ FLAG is set (file not properly open), return with the error code.
4FD4
LD A,(IX+01H) DD 7E 01
Load Register A with the access mode byte from FCB offset 01H. Bits 0-2 encode the access mode: 0 = read, 1 = write, 2 = random, etc.
4FD7
AND A,07H E6 07
Mask with 07H (binary 00000111) to isolate the 3-bit access mode field.
4FD9
CP A,02H FE 02
Compare the access mode against 02H. To kill a file, the access mode must be less than 2 (read mode = 0 or write mode = 1). Random access files (mode 2+) cannot be killed through this path because they may have pending buffered data.
4FDB
If the CARRY FLAG is set (access mode is less than 2, meaning read or write mode), JUMP to 4FE1H to proceed with the kill operation.
4FDD
LD A,25H 3E 25
Load Register A with error code 25H (access denied). The file was opened in a mode that does not permit deletion.
4FDF
OR A,A B7
OR Register A with itself to set the NZ FLAG (since A = 25H is non-zero), signaling an error to the caller.
4FE0
RET C9
Return to the caller with error code 25H (access denied) and NZ FLAG set.
4FE1
LD C,(IX+06H) DD 4E 06
Load Register C with the drive number from FCB offset 06H. This is needed by the disk I/O routines for drive selection.
4FE4
LD B,(IX+07H) DD 46 07
Load Register B with the directory entry info byte from FCB offset 07H. This identifies which directory sector and entry position to read.
4FE7
GOSUB to the Read GAT Sector routine at 5068H to load the GAT (track 0, sector 1) into the 5100H buffer. The GAT will be modified as granules are freed.
4FEA
RET NZ C0
If the NZ FLAG is set (GAT read error), return with the error code.
4FEB
GOSUB to SYS0 routine at 4AF0H to read the directory sector into the 4D00H secondary buffer.
4FEE
RET NZ C0
If the NZ FLAG is set (directory read error), return with the error code.
4FEF
GOSUB to SYS0 routine at 4AC1H to read the directory sector into the primary 4200H buffer. Register B has the directory entry info byte, Register C has the drive number. On return, HL points to the base of the file's directory entry.
4FF2
RET NZ C0
If the NZ FLAG is set (directory read error), return with the error code.
4FF3
LD A,16H 3E 16
Load Register A with 16H (22 decimal). This is the offset to the extent chain data area within the 32-byte directory entry.
4FF5
ADD A,L 85
ADD 16H to Register L (low byte of directory entry base address) to compute the address of the extent chain data start.
4FF6
LD L,A 6F
Store the result in Register L. HL now points to the first extent entry pair in the file's directory entry.
4FF7
LD E,(HL) 5E
Loop Start
Load Register E with the first byte of the current extent entry (the starting granule descriptor).
4FF8
INC L 2C
INCrement Register L to advance to the second byte of the extent entry (the granule count byte).
4FF9
LD D,(HL) 56
Load Register D with the second byte of the extent entry.
4FFA
LD A,E 7B
Load Register A with Register E (first extent byte).
4FFB
CP A,0FEH FE FE
Compare Register A against FEH. If A >= FEH (FEH = chain continuation, FFH = chain end), this is a terminator and not a normal extent entry.
4FFD
If the NO CARRY FLAG is set (first byte is FEH or FFH), JUMP to 5005H to handle the extent chain terminator.
4FFF
INC L 2C
INCrement Register L to advance past this extent entry pair, positioning at the start of the next entry.
5000
GOSUB to the Free Granules In Extent Entry routine at 5039H. Register DE holds the extent entry pair. This routine processes the extent, freeing each granule by clearing its bit in the GAT buffer at 5100H. Register B is updated with the track number.
5003
LOOP BACK to 4FF7H to process the next extent entry pair.

5005H - Kill File - Extent Chain Terminator

Reached when the Kill handler encounters an extent chain terminator (FEH or FFH). If FEH (chain continues), the second byte points to the next directory entry in the chain. The code clears the current directory entry, writes it to disk, then follows the chain. If FFH (chain complete), clears the entry, writes all changes, and proceeds to zero the FCB.

5005
LD A,L 7D
Load Register A with Register L (current position in the directory entry). The low 5 bits indicate the offset within the 32-byte entry.
5006
AND A,0E0H E6 E0
Mask with E0H to isolate bits 7-5, which gives the base address of the current directory entry within the sector buffer (the 32-byte entry slot number x 32).
5008
LD L,A 6F
Store the entry base address into Register L. HL now points to byte 0 of the current directory entry.
5009
LD (HL),00H 36 00
Store 00H into byte 0 of the directory entry. This clears the entry's flags byte, effectively marking the entry as deleted/empty.
500B
PUSH BC C5
Save Register BC onto the stack. B contains the current directory entry info byte, C contains the drive number.
500C
PUSH DE D5
Save Register DE onto the stack. E contains the first byte of the terminator pair (FEH or FFH), D contains the second byte (chain pointer if FEH).
500D
PUSH HL E5
Save Register HL (pointer to the cleared directory entry base) onto the stack.
500E
POP DE D1
Restore into DE the value just pushed from HL. DE now points to byte 0 of the directory entry. This is a compact DE = HL copy.
500F
INC DE 13
INCrement DE to point to byte 1 of the directory entry.
5010
LD BC,001FH 01 1F 00
Load Register BC with 001FH (31 decimal). This is the byte count for the LDIR block move: 31 bytes from byte 0 to bytes 1-31, filling the entire 32-byte entry with 00H.
5013
LDIR ED B0
Execute block move: copy BC (31) bytes from (HL) to (DE), incrementing both pointers after each byte. Since byte 0 was set to 00H and HL starts at byte 0 while DE starts at byte 1, this propagates 00H across all 32 bytes of the directory entry, completely zeroing it. This is the standard "fill with zeros" pattern using LDIR.
5015
POP DE D1
Restore Register DE from the stack (the original DE that held the terminator pair values from 500CH).
5016
POP BC C1
Restore Register BC from the stack (the original BC with B = directory info byte, C = drive number, from 500BH).
5017
GOSUB to SYS0 routine at 4AD6H to write the updated directory sector (with the zeroed entry) and GAT from the 4200H buffer to disk.
501A
RET NZ C0
If the NZ FLAG is set (write error), return with the error code.
501B
LD H,51H 26 51
Load Register H with 51H. The GAT/directory tracking area at 5100H-51FFH is used to record which entries have been processed.
501D
LD L,B 68
Load Register L with Register B (the current directory entry info byte). HL now points to 5100H + B, the tracking byte for this directory entry.
501E
LD (HL),00H 36 00
Store 00H at the tracking byte, marking this directory entry as processed.
5020
LD B,D 42
Copy Register D (the second byte of the terminator pair, which is the chain pointer info byte if the terminator was FEH) into Register B. This sets up B for the next directory sector read if the chain continues.
5021
LD A,E 7B
Load Register A with Register E (the first byte of the terminator pair: FEH = chain continues, FFH = chain ends).
5022
CP A,0FEH FE FE
Compare Register A against FEH. If A equals FEH, the chain continues to another directory entry.
5024
If the Z FLAG is set (terminator is FEH, chain continues), LOOP BACK to 4FEFH to read the next directory sector (using the chain pointer now in Register B) and continue freeing granules. Loop End

The terminator is FFH, meaning the entire extent chain has been processed. All granules have been freed from the GAT, and all directory entries in the chain have been zeroed. Now write the GAT back to disk and zero the FCB.

5026
GOSUB to SYS0 routine at 4B03H to write the sector from the 4D00H secondary buffer to disk. This writes back the directory sector that was in the secondary buffer.
5029
RET NZ C0
If the NZ FLAG is set (write error), return with the error code.
502A
GOSUB to the Write GAT Sector routine at 5077H to write the updated GAT sector from the 5100H buffer back to disk, committing the freed granule bits.
502D
RET NZ C0
If the NZ FLAG is set (GAT write error), return with the error code.

502EH - Zero the FCB

Final step of the Kill File operation. Zeroes all 32 bytes of the FCB, clearing the file-open status and all associated data. Returns with A = 00H and Z FLAG set to indicate success.

502E
PUSH IX DD E5
Save Register IX (FCB base address) onto the stack.
5030
POP HL E1
Restore into HL the value just pushed from IX. This is a compact HL = IX copy. HL now points to the FCB base address.
5031
LD B,20H 06 20
Load Register B with 20H (32 decimal). This is the loop counter for zeroing all 32 bytes of the FCB.
5033
XOR A,A AF
Set Register A to 00H by XORing A with itself. This value will be stored into each byte of the FCB, and the Z FLAG is set.
5034
LD (HL),A 77
Loop Start
Store 00H (Register A) into the current FCB byte at (HL).
5035
INC HL 23
INCrement HL to advance to the next byte of the FCB.
5036
DECrement B and LOOP BACK to 5034H if B is not zero. Continue zeroing FCB bytes until all 32 are cleared. Loop End
5038
RET C9
Return to the caller with A = 00H (no error) and Z FLAG set (success). The Kill File operation is complete. All of the file's granules have been freed in the GAT, all directory entries in the extent chain have been zeroed, and the 32-byte FCB has been cleared.

5039H - Free Granules In Extent Entry

Processes one extent entry pair (in DE) and frees all granules described by that entry from the GAT buffer at 5100H. Register E contains the first byte of the pair (starting granule descriptor), and Register D contains the second byte (granule count and track information). The routine uses the Build GAT Bit Mask subroutine at 505BH to compute the correct RES opcode for each granule's bit in the GAT, then applies it via self-modifying code. Called by the Kill handler for each extent entry in the chain.

5039
PUSH HL E5
Save Register HL (current position in the extent chain) onto the stack.
503A
PUSH BC C5
Save Register BC (B = directory info byte, C = drive number) onto the stack.
503B
LD L,E 6B
Load Register L with Register E (the first byte of the extent entry: bits 7-5 = granule position, bits 4-0 = additional extent info). This byte is also the GAT byte address offset within the track's GAT data.
503C
LD H,4DH 26 4D
Load Register H with 4DH. HL now points to 4D00H + E, addressing the GAT data for this granule in the 4D00H secondary buffer.
503E
LD A,D 7A
Load Register A with Register D (the second byte of the extent entry).
503F
AND A,1FH E6 1F
Mask with 1FH to isolate bits 4-0, the granule count minus 1 for this extent entry.
5041
LD C,A 4F
Store the granule count minus 1 in Register C. This will be used as the loop counter.
5042
INC C 0C
INCrement Register C by 1 to convert from "count minus 1" to actual granule count.
5043
XOR A,D AA
XOR Register A with Register D. Since A held (D AND 1FH), XORing with D yields the top 3 bits of D (bits 7-5), which encode the starting granule position within the track. Bits 4-0 cancel out, leaving only bits 7-5.
5044
RLCA 07
Rotate Register A left. This begins shifting the top 3 bits into the low-order position.
5045
RLCA 07
Rotate Register A left again.
5046
RLCA 07
Rotate Register A left a third time. The three rotates move bits 7-5 into positions 2-0, giving the starting granule position (0-7) for this extent.
5047
PUSH AF F5
Loop Start
Save Register A (current granule position, 0-7) onto the stack.
5048
LD B,(HL) 46
Load Register B with the current GAT byte from the 4D00H buffer (at address 4D00H + track offset). This byte contains allocation bits for the granules at the current position.
5049
GOSUB to the Build GAT Bit Mask routine at 505BH. This routine takes the granule position in Register A and constructs a self-modifying RES instruction at 5065H that will clear the appropriate bit in Register B (the GAT byte). On return, the RES instruction at 5065H has been modified to clear the correct bit.
504C
LD (HL),B 70
Store the modified GAT byte (Register B, with the granule's bit cleared by the RES instruction in 505BH) back into the 4D00H buffer. The granule is now marked as free in the GAT.
504D
POP AF F1
Restore Register A (current granule position) from the stack.
504E
INC A 3C
INCrement Register A by 1 to advance to the next granule position.
504F
CP A,02H FE 02
Compare Register A against 02H. Each GAT byte covers 2 granules (positions 0 and 1 within the byte). When the position reaches 2, the code must advance to the next GAT byte.
5051
If the NZ FLAG is set (granule position has not reached 2), JUMP to 5055H to continue with the current GAT byte.
5053
XOR A,A AF
Set Register A to 00H, resetting the granule position counter back to 0 for the next GAT byte.
5054
INC L 2C
INCrement Register L by 1 to advance HL to the next GAT byte in the 4D00H buffer.
5055
DEC C 0D
DECrement Register C (remaining granule count) by 1.
5056
If the NZ FLAG is set (more granules to free), LOOP BACK to 5047H to process the next granule. Loop End
5058
POP BC C1
Restore Register BC from the stack (B = directory info byte, C = drive number).
5059
POP HL E1
Restore Register HL from the stack (position in the extent chain).
505A
RET C9
Return to the caller. All granules in this extent entry have been freed from the GAT buffer.

505BH - Build GAT Bit Mask

Converts a granule position (0 or 1) in Register A into a self-modifying RES instruction that clears the corresponding bit in a GAT byte. The GAT encodes each granule as a single bit; this routine determines which bit to clear and patches the RES instruction at 5065H to target that bit. The RES instruction at 5065H then executes, clearing the bit in Register B (the GAT byte).

505B
AND A,07H E6 07
Mask Register A with 07H to ensure the granule position is in the range 0-7. Only the low 3 bits are meaningful for bit selection.
505D
RLCA 07
Rotate Register A left. The Z80 CB-prefix RES instruction encodes the bit number in bits 5-3 of the second opcode byte. These three rotates shift the bit number into position 5-3.
505E
RLCA 07
Rotate Register A left again.
505F
RLCA 07
Rotate Register A left a third time. The 3-bit granule position is now in bits 5-3 of Register A, which is the encoding position for the bit number in a CB-prefix instruction.
5060
OR A,80H F6 80
OR Register A with 80H. The opcode for RES n,B is CB (80H + n x 8) where n is the bit number. The base opcode for RES 0,B is CB 80H. ORing 80H sets bit 7, which combined with the bit number in bits 5-3 forms the complete second byte of the RES n,B instruction.
5062
LD (5066H),A 32 66 50
Self-Modifying Code
Store the computed opcode into address 5066H. This is the second byte of the RES instruction at 5065H (the CB prefix is at 5065H, and the second byte at 5066H selects which bit of which register to reset). The instruction at 5065H-5066H is now "RES n,B" where n is the granule's bit number.
5065
RES 0,B CB 80
Self-Modifying Code
Execute the RES instruction. The second byte (80H, shown as RES 0,B in the initial binary) was modified at runtime by the LD at 5062H to target the correct bit. This clears the granule's allocation bit in Register B (the GAT byte), marking the granule as free.
5067
RET C9
Return to the caller. Register B now has the specified bit cleared.

5068H - Read GAT Sector

Reads the GAT (Granule Allocation Table) sector from disk into the 5100H buffer. The GAT is always located at track 0, sector 1 on every TRSDOS 2.3 disk. Uses the SYS0 track cache lookup at 4B55H and the sector read with retry routine at 4B35H. Returns with Z FLAG on success, or NZ FLAG with error code 16H (directory read error) on failure.

5068
GOSUB to SYS0 routine at 4B55H (track cache lookup). This selects the drive specified in Register C and seeks to the track where the GAT resides (track 0). The track cache avoids unnecessary seek operations if the head is already positioned.
506B
LD E,01H 1E 01
Load Register E with 01H. The GAT is always at sector 1 on a TRSDOS 2.3 disk. Register E specifies the sector number for the read routine.
506D
LD HL,5100H 21 00 51
Point Register HL to 5100H. This is the destination buffer where the GAT sector data will be loaded. The 256-byte buffer at 5100H-51FFH is dedicated to GAT operations within this overlay.
5070
GOSUB to SYS0 routine at 4B35H (read sector with retry). Reads the sector specified by Register E (sector 1) from the current track (track 0) into the buffer at HL (5100H). Includes automatic recalibration on soft errors. Returns Z FLAG on success.
5073
RET Z C8
If the Z FLAG is set (read successful), return immediately with the success status.
5074
LD A,16H 3E 16
Load Register A with error code 16H. In TRSDOS 2.3, error 16H indicates a directory read error (the GAT is considered part of the directory structure).
5076
RET C9
Return with error code 16H in Register A and NZ FLAG set (since 16H is non-zero).

5077H - Write GAT Sector

Writes the GAT sector from the 5100H buffer back to disk at track 0, sector 1. Uses the SYS0 track cache lookup at 4B55H and the disk write routine at 46EFH. Returns with Z FLAG on success, or NZ FLAG with error code 17H (directory write error) on failure.

5077
GOSUB to SYS0 routine at 4B55H (track cache lookup). Selects the drive and seeks to track 0.
507A
LD E,01H 1E 01
Load Register E with 01H (sector 1, the GAT sector location).
507C
LD HL,5100H 21 00 51
Point Register HL to 5100H, the GAT buffer containing the updated allocation data.
507F
GOSUB to SYS0 routine at 46EFH (disk write). Writes the 256-byte buffer at HL (5100H) to the sector specified by Register E (sector 1) on the current track (track 0).
5082
RET Z C8
If the Z FLAG is set (write successful), return immediately with the success status.
5083
LD A,17H 3E 17
Load Register A with error code 17H. In TRSDOS 2.3, error 17H indicates a directory write error.
5085
RET C9
Return with error code 17H in Register A and NZ FLAG set.

5086H - Unused Padding

FFH fill bytes from 5086H to 50FFH (122 bytes). This is unused space between the end of the SYS3 code and the end of the overlay area. The FFH pattern is the default value for erased EPROM or uninitialized disk sectors. The 256-byte buffer at 5100H-51FFH is used at runtime for the GAT sector data but is not part of the overlay's stored code.

5086-50FF
RST 38H x 122 FF x 122
Unused padding. 122 bytes of FFH fill the remainder of the overlay area. Although the disassembler interprets each FFH byte as an RST 38H instruction, this area is never executed. It simply fills the space between the last code byte (5085H) and the end of the overlay sector (50FFH).