TRS-80 DOS - VTOS 4.0.2 for the Model I - SYS3/SYS Disassembled
Page Customization
Page Index
SYS3/SYS
Other Navigation
Introduction/Summary:
VTOS 4.0.2 SYS3/SYS Disassembly - File Close, Extent Chain, and GAT Deallocation Overlay (Model I)
SYS3/SYS is the VTOS 4.0 overlay responsible for file close operations, extent chain management, and Granule Allocation Table (GAT) deallocation. It occupies the overlay address space from 4E00H to 5061H and is loaded on demand by the resident DOS when an SVC request code targeting SYS3 is issued via RST 28H.
The overlay handles three SVC request codes: 10H (file close), 30H (directory-to-filespec conversion), and 00H (FCB initialization for unopened files). The file close operation is the most complex, encompassing directory entry updates with EOF offset and extent chain pointers, date and time stamping using the system clock at 4044H-4046H, extent chain traversal to count allocated sectors, comparison of allocated versus used sectors, and sector-by-sector deallocation of excess extents with GAT bitmap updates.
A filespec builder routine at 4F87H constructs human-readable filespec strings in the format FILENAME/EXT:D from directory entries. The GAT bit manipulation routine at 4FF6H uses self-modifying code to dynamically construct a Z80 CB-prefix RES instruction, allowing any bit position (0-7) to be cleared in a single byte. Four directory/GAT I/O helper routines handle sector reads and writes with error codes and write-verification.
SYS3 uses two separate buffers: 5100H for directory sector I/O (distinct from the SYS0 directory buffer at 4200H) and 4200H for GAT sector I/O (shared with SYS2). This dual-buffer approach allows simultaneous access to both directory and GAT data during the deallocation loop.
Variable and Memory Locations
| Address | Bytes | Purpose |
|---|---|---|
| 4044H-4046H | 3 | System date: day (4044H), month (4045H), year (4046H). Read by close routine for directory date stamping. Day uses internal base 50H format. |
| 4200H | 256 | GAT sector buffer. Shared with SYS2. Read/written by 502FH and 5041H. |
| 430FH | 1 | System mode flags. Bit 2 (kill-file active) tested at 4E55H and 4E5DH to control date stamping and kill processing. |
| 4F4BH | 1 | Self-modifying: LD B,nn operand. Stores HIT entry count during continuation entry cleanup (written by 4F3CH). |
| 4FC3H | 1 | Self-modifying: LD A,nn operand. Stores ASCII drive digit for filespec builder (written by 4F8CH). |
| 5001H | 1 | Self-modifying: second byte of CB-prefix RES instruction. Stores computed RES n,B opcode (written by 4FFDH). |
| 5100H | 256 | Directory sector buffer. Used by SYS3 read/write directory helpers (5003H, 5016H). Separate from the SYS0 directory buffer at 4200H. |
Major Routines
| Address | Name / Purpose |
|---|---|
| 4E00H | SVC Dispatcher Routes request codes: 10H to close file, 30H to filespec builder, 00H to FCB initializer. |
| 4E0DH | Close File Validates FCB, reads directory entry, updates EOF offset and extent pointers, performs date stamping, writes directory back to disk. |
| 4E63H | Date Encoder Reads system date from 4044H-4046H, encodes month/day/year into directory entry flags and date bytes. |
| 4E91H | Extent Chain Walker Traverses extent chain counting total allocated sectors. Follows FEH continuation pointers across directory entries. |
| 4EDEH | Sector Release Loop Deallocates excess sectors by clearing GAT bits and freeing continuation directory entries. Uses self-modifying RES instruction for bit manipulation. |
| 4F74H | Close/Kill Finalization Re-reads original directory entry and transfers IX (FCB address) to DE for return to caller. |
| 4F87H | Filespec Builder Constructs FILENAME/EXT:D string from directory entry. Uses self-modifying code for drive digit. |
| 4FCBH | Request 30H Handler Validates directory entry and calls filespec builder for directory-to-filespec conversion. |
| 4FD8H | FCB Initializer Sets FCB status to 2AH (reference state), stores drive config pointers and default buffer address. |
| 4FF6H | GAT Bit Clear Clears a single bit in a GAT allocation byte using dynamically constructed RES n,B instruction. |
| 5003H | Read Directory Sector Reads one directory sector into buffer at 5100H. Returns error code 14H on failure. |
| 5016H | Write Directory Sector Writes directory sector from 5100H buffer with write-verify. Returns error code 15H on failure. |
| 502FH | Read GAT Sector Reads GAT sector (track 0, sector 1) into buffer at 4200H. Returns error code 16H on failure. |
| 5041H | Write GAT Sector Writes GAT sector from 4200H buffer with write-verify. Returns error code 17H on failure. |
| 505AH | HL Offset Add Adds Register A to Register Pair HL with carry propagation. Preserves A. |
Cross-References
SYS3 calls these SYS0 resident routines: 4768H (write sector), 4772H (write-verify), 4797H (read directory sector by info byte), 4988H (FCB validation and directory sector setup), 49E9H/49EBH (overlay call context setup), 4B10H (directory entry validation), 4B1FH (directory entry write/update), 4B45H (read sector from disk), 4B65H (set up track/sector for directory I/O), 4B7BH (multiply A by sectors-per-granule), and 4BA9H (16-bit subtract/compare). SYS3 makes no direct ROM calls; all disk I/O is through SYS0 wrappers.
Error Codes
| Code | Source | Meaning |
|---|---|---|
| 0FH | 4F6BH | Non-fatal directory write error (tolerated during close) |
| 14H | 5013H | Directory read error |
| 15H | 502AH | Directory write error |
| 16H | 503EH | GAT read error |
| 17H | 5055H | GAT write error |
Self-Modifying Code Locations
| Writer | Target | Purpose |
|---|---|---|
| 4F3CH | 4F4BH | Stores HIT entry count as LD B,nn operand for continuation entry processing loop. |
| 4F8CH | 4FC3H | Stores ASCII drive digit ('0'-'7') as LD A,nn operand for filespec string construction. |
| 4FFDH | 5001H | Stores computed RES opcode byte (CB-prefix second byte) to dynamically construct RES n,B for GAT bit clearing. |
Disassembly:
4E00H - SVC Request Dispatcher
Entry point for SYS3 overlay. Register A contains the full SVC request code from the RST 28H dispatcher. The routine masks out the overlay number (lower nibble) to isolate the function code in bits 4-6, then routes to the appropriate handler: 10H dispatches to the file close routine at 4E0DH, 30H dispatches to the directory-to-filespec handler at 4FCBH, and 00H falls through to a RET which returns to the SYS0 overlay context setup that redirects to the FCB initializer at 4FD8H.
4E0DH - Close File - Validate FCB and Update Directory Entry
Main file close routine. Validates the FCB, reads the directory entry, compares the current EOF offset and extent pointers against the directory copy, and updates the directory if they differ. If the kill-file flag (bit 2 of IX+00H) is set, the routine branches to date stamping and then to the extent chain walker for deallocation. Register DE points to the FCB on entry.
Directory Entry Comparison
HL now points to the start of the directory entry in the 4200H buffer. The following code compares the FCB's current EOF offset and extent chain pointers against the directory's stored values to detect changes.
No Changes Detected
All three comparisons matched (EOF offset, extent pointer low, extent pointer high). The directory entry is already current, so skip the update and proceed to check the kill-file flag.
Directory Update Required
At least one of the three values (EOF offset, extent pointer low, extent pointer high) has changed. Write all three current FCB values into the directory entry buffer.
Kill-File Flag Check (No Update Path)
This entry point is reached when the directory entry did not need updating (all three comparisons matched at 4E2CH/4E36H/4E3DH). The kill-file flag is still checked to determine whether extent chain analysis is needed.
4E63H - Date Encoder - Stamp Directory Entry with Current Date
Reads the system date from 4044H-4046H and encodes it into the directory entry's flags and date bytes. Sets bit 6 (modified flag) and bit 4 (date present flag) in the directory entry's attribute byte at offset +01H. The day value uses an internal format with base 50H (SUB 50H converts to a 0-based value). The month is rotated left three times into bits 5-7, and the year is stored in the lower 5 bits of the attribute byte.
Write Updated Directory Entry
The directory entry has been updated with the current EOF offset, extent pointers, and date. Now write it back to disk.
4E91H - Extent Chain Walker - Count Total Allocated Sectors
Traverses the file's extent chain in the directory entry, counting the total number of allocated sectors. Each extent is a 2-byte pair where the second byte's lower 5 bits encode the sector count minus 1. Special values FEH and FFH serve as continuation pointer and end-of-chain markers respectively. The total allocated sector count in DE is compared against the FCB's used sector count to determine if excess sectors need deallocation. HL points to the extent chain area of the directory entry on entry.
Extent Chain Traversal Loop
Walk through the extent chain, reading 2-byte pairs. For each extent, extract the sector count from the second byte's lower 5 bits (plus 1) and add to the running total in DE. FEH marks a continuation pointer, FFH marks end-of-chain.
Normal Extent Entry
The first byte was a regular extent entry (value below FEH). Read the second byte, extract the sector count from its lower 5 bits, increment by 1 (since the stored value is count minus 1), and add to the running total in DE.
Special Extent Markers
The first byte was FEH (continuation pointer) or FFH (end-of-chain). Determine which one and take the appropriate action.
Continuation Pointer (FEH)
The current extent entry is a continuation pointer. The second byte contains the directory entry index of the next continuation entry. Read that entry and continue the chain from its extent data at offset +16H.
End of Extent Chain (FFH)
The extent chain has been fully traversed. DE contains the total number of allocated sectors. Compare this against the FCB's used sector count to determine if deallocation is needed.
4EE1H - Sector Release Loop - Deallocate Excess Sectors via GAT
Deallocates excess allocated sectors one at a time by clearing the corresponding bits in the GAT. For each sector, the routine extracts the granule position and sector offset from the last extent entry, reads the directory sector into the SYS3 buffer at 5100H, loads the GAT sector into 4200H, clears the appropriate allocation bit using the self-modifying RES instruction at 4FF6H, and updates the extent chain. When an extent entry is fully released, continuation directory entries are freed by zeroing their first byte, clearing the HIT entry, and writing back the GAT. DE contains the number of sectors to deallocate on entry, and HL points to the end of the extent chain.
Sector Deallocation Loop Start
Process one sector at a time. Extract the granule number and sector position from the current extent entry, then use those to locate and clear the corresponding GAT bit.
Extent Fully Released
The current extent entry has been completely deallocated. Mark it with FFH end-of-chain markers and check whether this position is at the start of a continuation directory entry (offset +15H), in which case the continuation entry itself needs to be freed.
Continuation Entry Cleanup
The extent chain in this continuation directory entry has been completely freed. Delete the continuation entry by zeroing its first byte, update the HIT (Hash Index Table) entry, and write back the GAT.
Update HIT Entry
Clear the HIT (Hash Index Table) entry corresponding to the deleted continuation directory entry. The HIT byte position is computed from the directory entry encoding in Register B.
Store the HIT byte value into the immediate operand of the LD B,nn instruction at 4F4BH. This saves the original HIT entry count for use after the GAT read/write cycle.
Load Register B with 00H. The immediate operand at 4F4BH was patched by the LD (4F4BH),A at 4F3CH with the saved HIT entry count. At runtime, B receives the original HIT value.
Deallocation Counter Update
One sector has been deallocated. Decrement the remaining count and loop back if more sectors need freeing.
Deallocation Complete
All excess sectors have been freed. Write the final directory entry update and the directory sector from the 5100H buffer.
4F74H - Close/Kill Finalization - Read Original Directory and Prepare FCB Return
Final phase of the close/kill operation. This routine reads the directory sector for the file's original directory entry (using the FCB's stored drive and directory info), then copies the IX (FCB) pointer to DE for return to the caller. This ensures the caller receives the FCB address in DE for any post-close operations.
4F87H - Build Filespec String from Directory Entry
Constructs a human-readable filespec string from a directory entry. The output format is FILENAME/EXT:D followed by a 03H load-type marker byte. Register B contains the directory entry encoding, Register C contains the drive number, and Register DE points to the output buffer where the filespec string will be written. HL is used internally to read from the directory entry in the sector buffer at 4200H.
Store the ASCII drive digit into the immediate operand of the LD A,nn instruction at 4FC3H. When that instruction executes later, it will load the correct drive character for insertion into the filespec string.
Loop Start
Copy the filename from the directory entry to the output buffer, skipping trailing spaces.
Loop Start
Copy the file extension characters to the output buffer, stopping at spaces.
Load Register A with 00H. The immediate operand at 4FC3H was patched by the LD (4FC3H),A at 4F8CH with the ASCII drive digit character. At runtime, A receives the correct drive character (e.g., '0', '1').
4FCBH - Request 30H Handler - Directory Lookup and Filespec Build
Handles SVC request 30H (directory-to-filespec conversion). Validates the directory entry by reading it from disk, then calls the filespec builder to construct the filespec string. Register DE points to the output buffer, and the directory entry encoding is already set up in BC from the caller.
4FD8H - FCB Initializer for Unopened Files
Reached when the close operation detects that the FCB's status byte has bits 6-7 both clear (file is not formally open). Instead of performing a full close, this routine initializes the FCB with a basic configuration: status byte 2AH, the drive config table entry pointer, and a default buffer address. This allows the FCB to be used for file lookups and other operations that do not require a full open.
4FF6H - GAT Bit Clear - Free a Granule in the Allocation Bitmap
Clears a single bit in a GAT allocation byte to mark a granule as free. Register A contains the bit position (0-7), and Register B contains the GAT byte to modify. The routine builds a Z80 CB-prefix RES n,B instruction using self-modifying code, where n is derived from the bit position in A. The RES instruction encoding places the bit number in bits 3-5 of the second opcode byte, with the register field in bits 0-2 (B=000) and the operation type in bits 6-7 (10=RES). The modified byte is returned in B.
Store the computed RES opcode byte into address 5001H, which is the second byte of the CB-prefix instruction at 5000H. This dynamically creates the instruction RES n,B where n is the bit position to clear.
Execute the dynamically constructed RES instruction. The initial value shown (RES 0,B = CB 80) is the default; at runtime, the second byte (80H at 5001H) has been overwritten with the correct opcode for the target bit. This clears the specified bit in Register B, marking the corresponding granule as free in the GAT allocation byte.
5003H - Read Directory Sector into Buffer at 5100H
Reads one directory sector from disk into the SYS3 directory buffer at 5100H. Uses the SYS0 directory I/O setup routine to configure the FDC track and sector registers, then calls the sector read routine. This buffer is separate from the SYS0 directory buffer at 4200H, allowing SYS3 to maintain both directory and GAT data simultaneously. Returns Z on success, NZ with error code 14H on failure.
5016H - Write Directory Sector from Buffer at 5100H
Writes the SYS3 directory sector buffer at 5100H back to disk with write-verify. Uses the same directory I/O setup as the read routine, followed by a write and read-after-write verification. The verify check compares the FDC status against 06H to detect write/CRC errors. Returns Z on success, NZ with error code 15H on failure.
502FH - Read GAT Sector into Buffer at 4200H
Reads the GAT (Granule Allocation Table) sector from disk into the buffer at 4200H. The GAT is always at sector 1 on the system track (E=01H). Returns Z on success, NZ with error code 16H on failure.
5041H - Write GAT Sector from Buffer at 4200H
Writes the GAT sector buffer at 4200H back to disk with write-verify. The GAT is always at sector 1 on the system track. Returns Z on success, NZ with error code 17H on failure.
505AH - HL Offset Add Utility
A small utility routine that adds the value in Register A to Register Pair HL, with proper carry propagation from L to H. Used by the extent chain walker to advance HL by a computed offset. Register A is preserved across this call (saved and restored via PUSH AF / POP AF).