TRS-80 DOS - VTOS 4.0.2 - SYS8/SYS Disassembled
Page Customization
Page Index
SYS8/SYS
Other Navigation
Introduction:
VTOS 4.0 SYS8/SYS Disassembly - Granule Allocation and Directory Update Overlay (Model I)
SYS8/SYS is the VTOS 4.0 overlay responsible for granule allocation, directory entry construction, and the low-level sector read/write stubs that the DOS kernel calls when extending or writing files. It loads at 4E00H and occupies approximately 663 bytes through 5096H.
The overlay is structured in two major sections. The upper section (4E00H-4FEFH) implements the granule-allocation engine: it walks the drive's Granule Allocation Table (GAT), locates free granules, constructs directory extent records, and writes updated directory sectors back to disk. The lower section (5000H-5096H) provides a set of compact sector I/O stubs - read-sector, write-sector, and read/write-directory-sector wrappers - that the kernel calls through a fixed jump table.
Key operations performed by this overlay include:
- Scanning the GAT sector (loaded at 5100H) for free granules, subject to a per-drive granule-count limit.
- Constructing extent records in directory entries: encoding the starting granule, sector count, and granule count into the two-byte extent format used by VTOS.
- Writing new extent data into an existing directory entry and updating the directory sector on disk.
- Providing read-sector and write-sector stubs (with optional read-after-write verify) for both the GAT/directory area and the main data area.
This overlay interacts heavily with SYS0 routines at 4797H (read directory sector), 4B10H (directory entry validation), 4B1FH (directory entry write), 4B45H (read sector), 4B65H (directory I/O setup), 4B6CH (24-bit multiply), 4B7BH (multiply by sectors-per-granule), 4768H (write sector), and 4772H (write-verify).
Variable / Work Area Reference
| Address Range Bytes | Purpose |
|---|---|
| 4E4AH 1 byte | Self-Modifying Operand - Remaining Count Written at 4E12H with the computed number of free granules remaining in the current extent. Read back as the operand of a CP instruction at 4F3BH-4F3CH to limit granule scanning. |
| 4F88H 1 byte | Current Directory Track/Sector Info Byte Saved at 4E54H from (IX+07H) (the directory info byte for the active drive). Read back at 4FB5H and 4FD3H to re-read the correct directory sector when writing updated entries. |
| 4FE0H 1 byte | Self-Modifying Operand - Starting Granule Written at 4FA0H with the starting granule number (L at that point). Read back as the operand of a LD A,nn instruction at 4FDF-4FE0H when constructing the final extent byte. |
| 5014H 1 byte | Self-Modifying Operand - Total Sector Count Written at 4FFFH with the computed total sector count for the extent (result of the 24-bit multiply via 4B6CH). Read back as the operand of a CP or LD instruction at 5013H-5014H during sector-count validation in the GAT scan loop. |
| 5032H 1 byte | Self-Modifying Operand - Encoded Granule Byte (SET form) Written at 502EH with the granule index shifted and OR'd with 40H. Used as the operand to SET a bit in the GAT bitmap byte at 5031H-5032H. |
| 503EH 1 byte | Self-Modifying Operand - Encoded Granule Byte (RES form) Written at 5039H with the granule index shifted and OR'd with C7H. Used as the operand to RES a bit in the GAT bitmap byte at 503DH-503EH. |
| 5100H-51FFH 256 bytes | GAT Sector Buffer One full sector (256 bytes) used as the working buffer when the overlay reads the GAT sector via 4B45H. The granule bitmap occupies the first N bytes of this buffer, where N is the number of granules on the drive divided by 8. |
Major Routine Reference
| Address | Name and Purpose |
|---|---|
| 4E00H | Granule Availability Check Entry: A = command/status byte, B = drive info, C = drive number, DE = requested granule count, HL = ?. Checks if the drive has enough free granules; returns Z set if the request can be satisfied, Carry set with count in A on partial availability. |
| 4E4CH | Directory Entry Scan and Extent Allocator Entry: IX points to drive parameter block, BC = requested record count, DE = target address. Scans the directory for a matching entry, calls 4B10H for validation, computes available extent space, and either records a partial fill or loops for more extents. |
| 4E97H | Directory Sector Read Helper Entry: IX points to drive parameter block, BC preserved. Reads the directory sector for the active drive into the working area and validates the entry. Returns NZ on error. |
| 4F80H | Directory Sector Write - Data Area Writes the updated sector (containing new extent data) back to disk using the write stub at 5053H. On success, sets B=00H and calls 4B1FH to update the directory entry. |
| 4F8CH | Directory Sector Write - Directory Area Reads the directory sector (via 506CH), extracts the drive's max-granule count from (IX+07H), calls 4FE8H to compute the sector count, constructs the two-byte extent encoding, and writes the directory sector. |
| 4FE8H | Sector Count Calculator Entry: A = granule count for the extent. Calls 4797H to read the GAT sector info, then uses 4B6CH (24-bit multiply) to convert granule count to total sector count. Returns result in HL:A (24-bit). |
| 5000H | GAT Bit Set / Clear Utility Restores AF from stack, places byte value in L, then falls into the GAT scan entry at 500BH. Marks a granule as used or free in the GAT bitmap buffer at 5100H. |
| 500BH | GAT Bitmap Scanner Scans the GAT buffer at 5100H for a free granule, applying the self-modifying limit at 5014H. Returns Z set if a suitable free granule is found. |
| 5027H | GAT Bit Set Encoder Entry: A = granule index (bits 2-0 = bit position, bits 7-3 = byte offset). Encodes the SET-bit instruction operand into 5032H and checks the current bit state via BIT 0,B. |
| 5034H | GAT Bit Clear Encoder Entry: A = granule index. Encodes the RES-bit instruction operand into 503EH. Returns with the encoded byte in A, CF clear. |
| 5040H | Read Sector Stub (Data Area) Entry: (IX+06H) = drive/sector info. Calls 4B65H to set up the track/sector, then calls 4B45H to read one sector into the buffer at 5100H. Returns Z on success, A=14H on error. |
| 5053H | Write Sector Stub (Data Area) Calls 4B65H, then 4768H to write the sector. Optionally calls 4772H for read-after-write verify if the first write succeeds. Returns A=15H. |
| 506CH | Read Directory Sector Stub Entry: (IX+06H) = drive info. Calls 4B65H, then reads the directory sector at E=01H into the buffer at 4200H via 4B45H. Returns Z on success, A=16H on error. |
| 507EH | Write Directory Sector Stub Calls 4B65H, then 4768H to write the directory sector from 4200H. Optionally verifies. Returns A=17H. |
Cross-Reference Notes
Called by: SYS0 kernel dispatches to this overlay via the RST 28H overlay mechanism whenever a file-write or file-extend operation requires granule allocation or directory update. SYS2 also references the sector I/O stubs.
Calls into SYS0: 4797H (read directory sector by info byte), 4B10H (directory entry validation), 4B1FH (directory entry write/update), 4B45H (read sector), 4B65H (directory I/O setup), 4B6CH (24-bit multiply), 4B7BH (multiply by sectors-per-granule), 4768H (write sector), 4772H (write-verify).
Disassembly:
4E00H - Granule Availability / Drive State Check
Entry point for the granule allocation overlay. Checks the command/status byte and dispatches to the main allocation engine if the drive is in the correct state.
Store the remaining record count (Register A) into address 4E4AH. This address is the operand byte of a CP instruction at 4E49H-4E4AH inside the allocator loop. At runtime, this CP will compare the loop's progress against the dynamically computed remaining demand.
Loop Start
The following loop (4E21H-4E32H) walks up to 5 extent slots in the directory entry, comparing the extent's drive/track byte against DE to find the correct entry, and checking granule availability.
No-Match Path
All 5 extent slots were examined without finding a matching entry. The following code at 4E34H shifts the extent block toward the start of the structure using LDDR, then returns with Carry set and Z clear to signal "partial or no allocation".
Match Found Path
A matching extent slot was found at 4E2AH. The following code at 4E45H writes D into (HL) to update the granule-count byte of the matched extent slot, then recovers the stack and returns with Z set and Carry clear.
Load Register A with the byte at the operand position (4E4AH). The operand byte 00H is the cold-start default; at runtime it is overwritten by the store at 4E12H with the computed remaining-count value. This CP effectively tests whether the remaining demand has been satisfied.
4E4CH - Directory Entry Scan and Extent Allocator
Main allocation engine. Called with IX = drive parameter block pointer, BC = requested record count, DE = target address. Reads the directory, validates the entry via 4B10H, computes available granules, and fills or partially fills the extent.
Store Register A (the directory info byte) into address 4F88H. This address is the operand byte of LD A,(4F88H) instructions at 4FB5H and 4FD3H. At runtime those instructions will reload the same directory info byte to re-read the correct directory sector during the write-back phase.
Extent Chain Walk Loop
The following code (4E64H-4E87H) walks the extent chain in the directory entry. Each extent is 2 bytes. The loop reads each 2-byte extent pair, decodes the granule start and count, accumulates the total sector count, and either finds the current end-of-file extent or steps to the next one.
Unconditionally loop back to 4E64H to process the next extent record in the chain.
Extent Found - Extract Address
The accumulated extent count has met or exceeded the requested count. The following code at 4E80H recovers the pointer to the matching extent's two-byte record from the saved HL, and returns the extent's granule-block pointer.
End-of-Chain Handler
A sentinel byte (0FEH or 0FFH) was found at 4E67H, indicating the end of the existing extent chain. The following code at 4E88H handles both the case of reaching a terminal entry (0FEH: the last written extent) and the case of finding an empty slot (0FFH: no further extents exist), dispatching to either a new-extent allocation path or the main loop continuation.
4E97H - Directory Sector Read Helper
Reads the directory sector for the current drive via 5040H and validates the directory entry via 4B10H. Returns NZ on error.
When the offset equals 1EH, all extent pairs in this directory sector are exhausted. The following code calls 4F80H to write the current sector back to disk, then either returns an error or reads the next directory sector and loops.
Store Register A (the clamped upper-bound value) into address 4F3CH. This address is the operand byte of the CP A,00H instruction at 4F3BH-4F3CH. At runtime that CP will compare the loop's granule scan index against this dynamically set upper limit, terminating the scan when the index reaches the bound.
GAT Bitmap Scan Inner Loop
The loop at 4F3BH-4F48H scans the GAT buffer at 5100H for a free granule. Each iteration checks whether the current granule byte (HL) contains an available slot by comparing L against the self-modified upper bound. If L is within range and the entry at (HL) is 0FFH (used), the loop advances to the next GAT byte.
Compare Register A (the current scan offset in L) against the operand at 4F3CH. The operand byte (initially 00H, written at runtime by the store at 4F35H) is the upper limit for the GAT scan. If A equals this limit, the Z flag is set and the scan is complete.
Jump back to 4F3AH to reload A from L and re-test the upper bound. This continues the scan through the GAT buffer until a free granule is found or the limit is reached.
Free Granule Found - Allocate
A GAT bitmap byte with at least one free granule was found at 4F41H. The following code at 4F53H walks the 8 bits of that byte to find the specific free bit, marks it allocated, and updates the directory entry extent chain.
Bit Scan Loop
The following loop (4F5AH-4F64H) scans bits 0-7 of the GAT byte in B to find the first zero bit (free granule). C counts the bit position; 5027H encodes the SET instruction for that position and tests the bit in B.
Jump back to 4F59H to load A from C and test the next bit. The loop continues until a free bit (zero bit) is found in the GAT bitmap byte.
Granule Allocation - Write GAT and Directory
A free granule (bit position in C, byte at HL) has been identified. The following code at 4F66H marks the granule as allocated in the GAT bitmap, updates the directory extent chain, then writes both the GAT sector and the directory sector back to disk.
4F80H - Directory Sector Write (Data Area)
Writes the updated directory data sector back to disk. Sets B=00H, then calls 4B1FH to update the directory entry. Called when an extent has been fully allocated and the directory must be committed.
4F8CH - Directory Sector Write (Directory Area)
Reads the directory sector via 506CH, computes the sector count for the current extent via 4FE8H, constructs the two-byte extent encoding, and writes the directory sector back via 507EH.
Store Register A (the starting granule index / sector count from L) into address 4FE0H. This address is the operand byte of a LD A,nn instruction at 4FDFH-4FE0H later in this routine. At runtime that LD A will reload this value to construct the final extent first byte.
Load Register A with the byte at address 4F88H. This address holds the directory info byte saved at 4E54H by the store LD (4F88H),A. At runtime A receives the directory track/sector info byte for the current drive, which is written into the directory entry at offset +01H.
Directory Entry Clear Loop
The following loop (4FBCH-4FBFH) fills 20 bytes of the directory entry with 00H, clearing fields +02H through +15H (date, reserved, filename, extension, and the start of the extent area).
Extent Chain Sentinel Fill Loop
The following loop (4FC4H-4FC7H) fills 10 bytes of the extent chain area with 0FFH, pre-marking all 5 extent slots as empty (end-of-chain).
Load Register A with the byte at address 4F88H (the directory info byte stored at 4E54H). This re-reads the saved directory track/sector info byte for use in the second directory sector write that follows.
Load Register A with the byte at the operand position 4FE0H. The operand byte (initially 00H, written at runtime by the store at 4FA0H) holds the starting granule index computed earlier. A now contains the starting granule for this extent's second byte encoding.
4FE8H - Sector Count Calculator
Converts a granule count (in A) into a total sector count using the drive's GAT data and the 24-bit multiply routine at 4B6CH. Called by 4F8CH to determine how many sectors an extent of A granules occupies.
Store Register A (the adjusted sector count) into address 5014H. This address is the operand byte of a CP A,00H instruction at 5013H-5014H inside the GAT bitmap scanner at 500BH. At runtime that CP compares the scanner's progress index against this dynamically computed sector count limit.
500BH - GAT Bitmap Scanner
Scans the GAT buffer at 5100H for a free granule, starting at offset L and wrapping around. The self-modifying byte at 5014H provides the upper limit. Returns Z set with L = offset of the free granule if found, NZ if no free granule exists within the limit.
Compare Register A (the 5-bit byte index) against the operand at 5014H. The operand byte (initially 00H, written at runtime by the store at 4FFFH with the adjusted sector count) is the upper limit for the valid GAT range. If A < this limit (Carry set), the byte is within range; if A >= the limit (No Carry), the byte is beyond the valid GAT data.
5027H - GAT Bit Set Encoder
Encodes the SET-bit instruction operand for a given granule index into the self-modifying byte at 5032H, then tests whether that bit is currently set in Register B (the GAT bitmap byte). Returns Z if the bit is clear (granule free), NZ if set (granule in use).
Store Register A (the encoded BIT n,B opcode byte) into address 5032H. This overwrites the second byte of the CB-prefix instruction at 5031H-5032H, dynamically changing which bit of Register B will be tested at runtime.
Test bit 0 of Register B (the GAT bitmap byte). The second opcode byte at 5032H is overwritten at runtime by the store at 502EH to test the actual bit position (0-7) corresponding to the granule being checked. The cold-start value CB 40H tests bit 0; at runtime CB xxH tests bit n where xx was written by 502EH. Returns Z if the tested bit is clear (granule free), NZ if set (granule in use).
5034H - GAT Bit Clear Encoder
Encodes the RES-bit instruction operand for a given granule index into the self-modifying byte at 503EH, then executes the RES to clear that bit in Register A (marking the granule as allocated in the GAT bitmap). Returns with the modified byte in A.
Store Register A (the encoded RES n,A opcode byte) into address 503EH. This overwrites the second byte of the CB-prefix instruction at 503DH-503EH, dynamically changing which bit of Register A will be cleared at runtime.
Clear bit 0 of Register A. The second opcode byte at 503EH is overwritten at runtime by the store at 5039H to clear the actual bit position (0-7) corresponding to the granule being allocated. The cold-start value CB C7H clears bit 0 of A; at runtime CB xxH clears bit n of A where xx was written by 5039H. Since A was zeroed at 503CH and a RES on zero still produces zero, this effectively generates a single-bit mask with only the target bit clear and all others set (via OR with the bitmap byte in the caller at 4F6AH).
5040H - Read Sector Stub (Data Area)
Sets up the track and sector via 4B65H, then reads one sector into the buffer at 5100H via 4B45H. Returns Z on success, A=14H (Read Error) on failure.
5053H - Write Sector Stub (Data Area)
Sets up the track and sector via 4B65H, writes one sector via 4768H, and optionally performs a read-after-write verify via 4772H. Returns A=15H (Write Error) on failure, Z on success.
506CH - Read Directory Sector Stub
Sets up the track and sector via 4B65H, then reads the directory sector (E=01H) into the buffer at 4200H via 4B45H. Returns Z on success, A=16H (Directory Read Error) on failure.
507EH - Write Directory Sector Stub
Sets up the track and sector via 4B65H, writes the directory sector from the buffer at 4200H via 4768H, and optionally verifies via 4772H. Returns A=17H (Directory Write Error) on failure, Z on success.