TRS-80 DOS - LDOS 5.3.1 for the Model I - SYS3/SYS Disassembled
Page Customization
Page Index
SYS3/SYS
Other Navigation
Summary:
LDOS 5.3.1 SYS3/SYS Disassembly - File/Device Close and @FNAME (Model I)
SYS3/SYS is the LDOS 5.3.1 system overlay that closes files and logical devices and that services the @FNAME system vector. It is loaded on demand into the transient overlay region at 4E00H by the RST 28H supervisor dispatcher in the resident core (SYS0/SYS) and returns to that dispatcher when finished. This overlay must not be eliminated from a working system disk, and together with SYS2/SYS it must remain on any booting disk from which a SYSGEN configuration file is to be loaded.
When an open disk file is closed, SYS3/SYS finalizes that file's directory record: it flushes any buffered record still waiting to be written, copies the file's end-of-file byte and ending-record-number from the File Control Block (FCB) into the directory entry, and - on LDOS 5.3 disks whose dating is enabled - stamps the entry with the current modification date and time. It then reconciles the granules the file actually needs against the granules that were pre-allocated to it, releasing any excess granules back to the Granule Allocation Table (GAT) and, when a whole secondary directory record (an extended directory entry, or FXDE) becomes empty, unlinking that FXDE and clearing its Hash Index Table (HIT) slot. Finally it rebuilds the caller's FCB into a printable file specification of the form NAME/EXT:d. Closing a logical device instead rebuilds the FCB into the two-character device specification *XX.
The @FNAME service (RST 28H request code A5H) reads a named directory record and constructs the same NAME/EXT:d file-specification string into a caller-supplied buffer, sharing the file-specification builder used by the close path.
On the Model I the physical disk I/O is performed by the resident WD1771 engine in SYS0/SYS through the memory-mapped floppy-disk controller (command/status at 37ECH); SYS3/SYS itself performs no direct controller access, reaching the media only through the SYS0 directory-read, directory-write, sector-read, and sector-write primitives.
Variable and Buffer List
The following table lists the self-modifying operand cells and work buffers that SYS3/SYS uses. Addresses in the 40xxH-4Dxx resident range belong to SYS0/SYS and are cross-referenced there.
| Address Range | Purpose |
|---|---|
| 4BC9H-4BCAH 2 bytes | Resident load block (A6H 02H) deposited by the overlay loader into the SYS0 padding at 4BBC-4BCA, ahead of the execute-drive-operation routine at 4BCBH. It is data only and is not executed on the SYS3 path. |
| 4E9DH 1 byte | Self-modifying operand for the BIT b,A instruction at 4E9CH. It is patched at 4E96H so the test selects the current drive's bit within the 475DH extended-dating flag byte. |
| 4F9DH 1 byte | Self-modifying operand for the LD B,nn instruction at 4F9CH. It is patched at 4F8EH with the parent directory record number so the FXDE unlink logic can re-read that parent record. |
| 504DH 1 byte | Self-modifying operand for the RES b,B instruction at 504CH. It is patched at 5049H so the correct granule bit is cleared in the GAT byte. |
| 5014H 1 byte | Self-modifying operand for the LD A,nn instruction at 5013H. It is patched at 4FDDH with the ASCII drive digit that terminates the file specification (the ":d" suffix). |
| 4200H-42FFH 256 bytes | The shared SYS0 directory/sector buffer. SYS3/SYS reads and writes the HIT sector here and reads directory-record sectors here through the SYS0 primitives. |
| 5100H-51FFH 256 bytes | The GAT sector work buffer. The Granule Allocation Table sector is read here (504FH), modified in place as granules are freed, and written back (5062H). |
Major Routine List
| Address | Name and Purpose |
|---|---|
| 4E00H | Overlay Entry and Request Dispatch Masks the RST 28H request code in Register A with 70H and routes 10H (@CLOSE) to 4E0CH and 20H (@FNAME) to 501CH, returning for any other value. |
| 4E0CH | Close an Open Disk File Routes device FCBs to 5029H, then for a disk file reads the directory record, flushes the buffer, and copies the EOF byte and ending-record-number from the FCB into the directory entry. |
| 4E69H | Directory Modification Date and Time Stamp Packs the system date into the directory entry's date bytes and, when the drive's dating flag is set, packs the modification time and extended year into the reused access-password field. |
| 4ECFH | Rewrite Directory Record and Measure Granule Usage Writes the finalized directory record, walks the extent and FXDE chain to total the allocated granules, computes how many the file actually needs, and reads the GAT when excess granules must be released. |
| 4F2EH | Free One Extent's Granules from the GAT Computes each granule's absolute position from the extent's cylinder and granule fields, clears its GAT bit, shrinks the extent, and unlinks and de-hashes an FXDE record that becomes empty. |
| 4FB2H | Finish Granule Release and Commit Loops until all excess granules are freed, rewrites the directory record and the GAT sector, re-reads the primary directory record, and points DE at the FCB for the file-specification builder. |
| 4FD8H | Build the File Specification String Constructs the printable specification NAME/EXT:d, terminated by 03H, from a directory record into the buffer addressed by DE. Shared by the close path and by @FNAME. |
| 501CH | @FNAME Service Entry Reads the requested directory record and calls the file-specification builder to return the file specification to the caller's buffer. |
| 5029H | Close a Logical Device Rebuilds the FCB into the two-character device specification *XX terminated by 03H. |
| 5042H | GAT Bit-Clear Helper Self-modifies a RES instruction so the granule bit selected by Register A is cleared in Register B. |
| 504FH | Read the Granule Allocation Table Sector Reads sector 0 of the directory track (the GAT) into 5100H, returning error 14H on failure. |
| 5062H | Write the Granule Allocation Table Sector Writes the GAT buffer at 5100H back to sector 0 with a deleted-data address mark and verifies it, returning error 15H on failure. |
| 507BH | Read the Hash Index Table Sector Reads sector 1 of the directory track (the HIT) into 4200H, returning error 16H on failure. |
| 508DH | Write the Hash Index Table Sector Writes the HIT buffer at 4200H back to sector 1 with a deleted-data address mark and verifies it, returning error 17H on failure. |
Cross-Reference Notes
SYS3/SYS is invoked by the resident RST 28H dispatcher in SYS0/SYS for request code 95H (@CLOSE) and request code A5H (@FNAME); both codes are masked with 70H at the overlay entry to select the close (10H) or file-name (20H) sub-function. The overlay calls the following resident SYS0/SYS routines: the file-operation prologue at 49F1H and its secondary entry 49F3H, the buffer-flush routine at 4993H, the directory-record read 4B10H (DIRRD) and write 4B1FH (DIRWR), the directory-sector reader 4B45H (RDSSEC), the directory-track locator 4B65H, the deleted-data-mark sector writer 4768H (WRPROT) and sector verifier 4772H (VERSEC), the DCT geometry-byte reader 479CH (DCTBYT), the divide helper at 44C4H, and the multiply/divide primitive at 4B7BH. It reads the SYS0 clock workspace bytes at 4042H-4046H (minutes, hours, year, day, month) and the SYS2-maintained extended-dating flag byte at 475DH.
Disassembly:
4BC9H - Resident Load Block
A two-byte block the overlay loader deposits into the SYS0 padding area at 4BBC-4BCA, immediately ahead of the resident execute-drive-operation routine at 4BCBH. On the SYS3 path these two bytes are never executed; they are shown as data. Each LDOS overlay writes a different pair here (SYS1 wrote F9H 03H, SYS2 wrote 00H 04H, SYS3 writes A6H 02H).
4E00H - Overlay Entry and Request Dispatch
Execution enters here from the SYS0 RST 28H dispatcher with the service request code still in Register A. The code is masked and compared to route the request to the close routine or the @FNAME routine.
4E0CH - Close an Open Disk File
Register DE points to the caller's File Control Block (FCB). This routine separates logical devices from disk files, reads the file's directory record, flushes any pending buffered record, and copies the end-of-file byte and ending-record-number from the FCB into the directory entry.
4E69H - Directory Modification Date and Time Stamp
Register HL points within the directory record. This routine sets the modification flag in the entry, packs the current system date (month, day, and low year bits) into the LDOS date bytes, and, when the drive's extended-dating bit is set, packs the modification time and the extended year into the field that LDOS 5.3 reclaimed from the old access-password hash. The system clock is kept in the SYS0 workspace bytes at 4042H (minutes), 4043H (hours), 4044H (year), 4045H (day), and 4046H (month).
Add 8 to Register A on each pass so that after the loop Register A holds 3FH + 8 times (drive + 1), which equals 47H + 8 times the drive number - the opcode byte for BIT (drive),A.
DECrement Register B and LOOP BACK to 4E92H until Register B reaches zero.
Store the value in Register A into the operand byte at 4E9DH, which is the second byte of the BIT instruction at 4E9CH. This rewrites that instruction from BIT 0,A into BIT (drive),A, so the following test selects the current drive's bit.
As patched at 4E96H this is BIT (drive),A. Test the current drive's extended-dating bit within the 475DH flag byte held in Register A. If the bit is set the NZ FLAG is set.
Shift Register D (the minutes) right one bit, moving bit 0 into the CARRY FLAG.
DECrement Register B and LOOP BACK to 4EB2H until the minutes have been shifted right 3 bits (into Register D) with their low 3 bits gathered at the top of Register E.
4ECFH - Rewrite Directory Record and Measure Granule Usage
The finalized directory record is written back to disk. The routine then walks the file's extent fields, following the FXDE chain into any secondary directory records, to total the granules currently allocated to the file, compares that total against the granules the file actually needs, and reads the GAT when excess granules must be released.
Register HL points at an extent's first byte. Load Register A with that byte, the starting cylinder of this extent (or a terminator value at the end of the extent list).
4F2EH - Free One Extent's Granules from the GAT
This is the granule-release worker. For the granule at the top of the current extent it computes the absolute cylinder and the granule position within that cylinder, clears that granule's bit in the GAT buffer, and shrinks the extent by one granule. When an extent empties its fields are cleared to FFFFH, and when an emptied extent belongs to a secondary (FXDE) directory record that record is marked free, de-hashed from the HIT, and unlinked from its parent. The loop is first entered at 4F6FH from 4F2CH.
Register DE holds the remaining count of excess granules to free. Save it while one granule is released.
Store the parent record number into the operand byte at 4F9DH, which is the immediate value of the LD B instruction at 4F9CH, so the parent record can be re-read after the HIT is updated.
As patched at 4F8EH, this loads Register B with the parent record number so the parent directory record can be re-read.
4FB2H - Finish Granule Release and Commit
The release loop converges here. While excess granules remain it re-enters the worker; once all are freed it writes the shrunken directory record and the updated GAT sector back to disk, re-reads the file's primary directory record, and points Register DE at the FCB so the file specification can be rebuilt into it.
4FD8H - Build the File Specification String
Register HL addresses the directory record for the file and Register DE addresses the destination buffer. This routine copies the name and extension from the directory record and appends the drive suffix, producing the printable specification NAME/EXT:d terminated by an 03H byte. It is shared by the close path (which builds the specification back into the FCB) and by the @FNAME service.
Store the ASCII drive digit into the operand byte at 5014H, which is the immediate value of the LD A instruction at 5013H, so the drive suffix written at the end of the string carries the correct digit.
Load Register A with the next file-name character from the directory record.
DECrement Register B and LOOP BACK to 4FEBH until all 8 name characters have been examined.
Load Register A with the next extension character from the directory record.
DECrement Register B and LOOP BACK to 5005H until all 3 extension characters have been examined.
As patched at 4FDDH, this loads Register A with the ASCII drive digit that completes the ":d" suffix.
501CH - @FNAME Service Entry
This is the entry reached for the @FNAME request (RST 28H code A5H). Register DE addresses the caller's buffer and Registers B and C select the directory record number and drive. The routine reads that directory record and calls the file-specification builder to return the file name to the caller.
5029H - Close a Logical Device
Reached when the FCB being closed is a logical device rather than an open disk file. The routine rebuilds the FCB into the two-character device specification of the form *XX, terminated by 03H, restoring it to a form that can be re-opened.
5042H - GAT Bit-Clear Helper
A small self-modifying helper that clears one granule bit in a GAT byte. Register A selects the bit and Register B holds the GAT byte; the routine rewrites its own RES instruction so the selected bit is the one cleared.
Store the computed opcode from Register A into the operand byte at 504DH, which is the second byte of the CB-prefixed instruction at 504CH, converting RES 0,B into RES (bit),B.
As patched at 5049H, this clears the selected granule bit in Register B, freeing that granule within the cylinder's GAT byte.
504FH - Read the Granule Allocation Table Sector
Reads sector 0 of the directory track - the Granule Allocation Table - into the 5100H work buffer so its granule bits can be examined and cleared. Returns error 14H on a read failure.
5062H - Write the Granule Allocation Table Sector
Writes the GAT work buffer at 5100H back to sector 0 of the directory track with a deleted-data address mark, then verifies it. Directory-track sectors carry a deleted-data address mark, which is how LDOS distinguishes them. Returns error 15H on failure.
507BH - Read the Hash Index Table Sector
Reads sector 1 of the directory track - the Hash Index Table - into the 4200H directory buffer so a freed record's hash entry can be cleared. Returns error 16H on a read failure.
508DH - Write the Hash Index Table Sector
Writes the HIT buffer at 4200H back to sector 1 of the directory track with a deleted-data address mark, then verifies it. Returns error 17H on failure.