TRS-80 DOS - LDOS 5.3.1 for the Model I - SYS10/SYS Disassembled
Page Customization
Page Index
SYS10/SYS
Other Navigation
Summary:
LDOS 5.3.1 SYS10/SYS Disassembly - File Deletion and Module Lookup (Model I)
SYS10/SYS is the LDOS 5.3.1 overlay that deletes files. It services the @KILL vector at 442CH, which issues RST 28H with request code 9CH, and it is the exact counterpart of SYS8/SYS: where SYS8/SYS claims granules and builds extent entries as a file grows, SYS10/SYS gives every one of them back.
The overlay also carries a second, unrelated service. Request code 0BCH masks to sub-function 30H and selects the resident module and driver control-block lookup at 4F28H, which walks the chain of modules installed in high memory looking for one whose name matches the string the caller points at. That is the service SYS6/SYS LINK and ROUTE and SYS7/SYS SYSTEM, FORMS and SETCOM all use to locate the control block they are about to edit, and it is the source of the "not resident" and "not installed" messages those commands produce.
The file is small: 371 bytes spanning 4E00H through 4F72H, plus a 2-byte load block deposited at 4BC9H in the resident padding area, with a transfer address of 4E00H. It should remain on working SYSTEM diskettes, since without it no file can be removed.
Deletion begins with a permission check. LDOS numbers access levels from 0 for full access upwards, and only levels 0 and 1, full access and remove access, permit a file to be deleted; anything higher is refused with error 37. The overlay then reads the drive's Granule Allocation Table into its buffer at 5000H and walks the file's directory record. Each of the four 2-byte extent entries at DIR+16H through DIR+1DH names a cylinder and, in a packed byte, a starting granule and a granule count; for every one of them the routine at 4E8BH clears the corresponding allocation bits, rolling forward onto the next cylinder whenever a granule number reaches the number of granules a cylinder holds.
With the extents released, bit 4 of the record's attribute byte is cleared to mark the directory slot free, the record is rewritten, and the file's byte in the Hash Index Table is zeroed so the name no longer hashes to anything. If the record ended with the FEH marker at DIR+1EH the file continues in a secondary extended directory record, and the whole sequence repeats for it. Only when the chain is exhausted is the Granule Allocation Table written back, at which point the freed space genuinely becomes available. Finally the caller's 32-byte File Control Block is zeroed so it cannot go on referring to a file that no longer exists.
Deleting a logical device takes none of this. A device owns no granules and no directory record, so its control block is simply rebuilt as the four-byte specification *XX followed by an 03H terminator, in the same shape SYS3/SYS produces when it closes a device.
Two pieces of the overlay are worth noting for their construction. The Granule Allocation Table handler at 4ED4H has two entry points one byte apart which select reading or writing through a single shared body: 4ED4H is OR 0AFH, and entering at 4ED5H executes that instruction's second byte alone as XOR A, setting the Z FLAG that the test at 4EE2H reads. And the helper at 4EC7H builds a RES n,B instruction for a granule number and plants it before executing it, mirroring exactly the SET n,A builder SYS8/SYS uses when it allocates.
Memory Map
| Address Range | Purpose |
|---|---|
| 4BC9H-4BCAH 2 bytes | Load block deposited into the resident padding area below the RST 28H dispatcher. SYS10/SYS writes the bytes 73H and 01H there. |
| 4E00H-4E09H 10 bytes | Overlay entry and request dispatch across the two services, sub-function 10H for @KILL and 30H for the module lookup. |
| 4E0AH-4E71H 104 bytes | Disk file deletion: access check, extent release, directory and Hash Index Table maintenance, FXDE chain following, File Control Block clear. |
| 4E72H-4E8AH 25 bytes | Logical device close-out, rebuilding the control block as a re-openable device specification. |
| 4E8BH-4EC6H 60 bytes | Release of one extent's granules into the Granule Allocation Table image, with cylinder roll-over. |
| 4EC7H-4ED3H 13 bytes | Self-modifying builder for the granule allocation bit reset. |
| 4ED4H-4EF7H 36 bytes | Granule Allocation Table read and write, with two entry points sharing one body. |
| 4EF8H-4F0EH 23 bytes | Hash Index Table read, with two entry points choosing the destination buffer. |
| 4F0FH-4F27H 25 bytes | Hash Index Table write and verify. |
| 4F28H-4F72H 75 bytes | Resident module and driver control-block lookup, the service behind request code 0BCH. |
| 5000H-50FFH 256 bytes | Granule Allocation Table buffer. Scratch space above the overlay image, not part of the loaded file. |
Variables and Self-Modified Operands
| Address Range | Purpose |
|---|---|
| 4E5BH-4E5CH 2 bytes | Operand of the LD DE,nnnn at 4E5AH. Written at 4E34H with each extent entry as it is read, so that after the record has been processed the terminator can be re-examined to decide whether an FXDE link must be followed. |
| 4EBCH 1 byte | Operand of the CP nn at 4EBBH. Written at 4EA4H with the number of granules on one cylinder, which tells the release loop when to roll over to the next cylinder. |
| 4ED2H 1 byte | Second opcode byte of the RES 0,B at 4ED1H. Written at 4ECEH so the instruction clears the wanted granule's bit in a cylinder allocation byte. |
| 5000H-50FFH 256 bytes | Granule Allocation Table sector image. One byte per cylinder, each set bit marking an allocated granule. SYS8/SYS uses 5100H for the same purpose because its code reaches further up. |
| 4200H-42FFH 256 bytes | Resident shared sector buffer SBUFF$, used here to hold the Hash Index Table image while the file's hash byte is zeroed. |
Major Routines
| Address | Entry | Exit | Name and Purpose |
|---|---|---|---|
| 4E00H | Register A = request code, Register Pair DE = File Control Block or name | Returns to the RST 28H dispatcher | Overlay Entry Masks the request code with 70H and dispatches sub-function 10H to @KILL and 30H to the module lookup. |
| 4E0AH | Register Pair DE = File Control Block of an open file or device | Z set on success, or NZ with an error code in Register A | @KILL Deletes a disk file completely, or closes out a logical device. |
| 4E72H | Register Pair DE = File Control Block of an open device | Z set, block rebuilt as *XX | Device Close-Out Rebuilds the control block as a re-openable device specification. |
| 4E8BH | Register Pair DE = extent word, Register C = drive number, Granule Allocation Table image at 5000H | Registers preserved, allocation bits cleared | Release One Extent Clears the allocation bit of every granule the extent describes, rolling onto the next cylinder as needed. |
| 4EC7H | Register A = granule number 0-7, Register B = cylinder allocation byte | Register B with that granule's bit cleared | Granule Bit Reset Builder Constructs and executes a RES n,B instruction for the given granule number. |
| 4ED4H | Register C = drive number | Z set on success, or NZ with error 20 | Read Granule Allocation Table Reads sector 0 of the directory cylinder into 5000H. |
| 4ED5H | Register C = drive number | Z set on success, or NZ with error 21 | Write Granule Allocation Table Alternate entry one byte into the instruction at 4ED4H; writes 5000H back to sector 0 and verifies it. |
| 4EFDH | Register C = drive number | Z set on success, or NZ with error 22 | Read Hash Index Table Reads sector 1 of the directory cylinder into SBUFF$ at 4200H. The entry at 4EF8H reads it into 5000H instead. |
| 4F0FH | Register C = drive number | Z set on success, or NZ with error 23 | Write Hash Index Table Writes 4200H back to sector 1 with a deleted-data address mark and verifies it. |
| 4F28H | Register Pair DE = module or device name | Z set with Register Pair DE = control block address, or NZ with error 8 | Module Lookup Walks the chain of resident modules above HIGH$ and returns the matching module's control block. |
Error Codes Returned
| Code | Raised At | Meaning |
|---|---|---|
| 08H (8) | 4F6FH | Device not available. No resident module of the requested name is installed in the high-memory chain. |
| 14H (20) | 4EE7H | GAT read error. Sector 0 of the directory cylinder could not be read. |
| 15H (21) | 4EF3H | GAT write error. The amended Granule Allocation Table could not be written or failed to verify. |
| 16H (22) | 4F0CH | HIT read error. Sector 1 of the directory cylinder could not be read. |
| 17H (23) | 4F23H | HIT write error. The amended Hash Index Table could not be written or failed to verify. |
| 25H (37) | 4E1BH | Illegal access attempted to a protected file. The file was opened at an access level above 1, which does not permit removal. |
Cross-References
SYS10/SYS is reached through the resident supervisor. The @KILL vector at 442CH in SYS0/SYS loads request code 9CH and issues RST 28H, and the dispatcher at 4BCDH brings this overlay in at 4E00H. The overlay calls back into the resident core for all disk work: the File Control Block prologue at 49F1H and its second entry at 49F3H, DIRRD at 4B10H and DIRWR at 4B1FH for directory records, DIRCYL at 4B65H and RDSSEC at 4B45H to reach the directory cylinder, WRPROT at 4768H and VERSEC at 4772H to write and verify the two table sectors with a deleted-data address mark, and DCTBYT at 479CH for drive geometry. It uses the resident shared sector buffer SBUFF$ at 4200H, and reads HIGH$ at 4049H and the top-of-memory word at 4403H when searching the module chain.
It is the mirror image of SYS8/SYS, which allocates the granules this overlay releases and builds the extent entries it walks; the two share the directory extent layout, the FXDE link convention and the Granule Allocation Table and Hash Index Table sector handling, and their bit helpers are opposites. SYS3/SYS performs the same kind of partial release when it trims unused granules on close, and produces the same *XX device specification at 5029H. The error codes returned here are turned into readable text by SYS4/SYS. The module lookup at 4F28H is called by SYS6/SYS LINK and ROUTE and by SYS7/SYS SYSTEM, FORMS and SETCOM.
Disassembly:
4BC9H - Overlay Load Block
The two bytes every SYS overlay deposits into the padding area of the resident core immediately below the RST 28H dispatcher. They are data, not instructions, and are never executed. SYS10/SYS writes 73H and 01H here, in the same way SYS8/SYS writes 96H and 02H and SYS4/SYS writes AFH and 03H.
4E00H - Overlay Entry and Request Dispatch
SYS10/SYS services two supervisor requests. Request code 9CH, which masks to sub-function 10H, is @KILL, the file and device deletion service. Request code 0BCH, which masks to 30H, is the resident module lookup. Any other value returns without action.
4E0AH - Delete a Disk File
The deletion path checks that the file was opened with an access level that permits removal, then walks the directory extent chain releasing every granule the file owns back into the Granule Allocation Table, marks each directory record free and de-hashes it in the Hash Index Table, follows any FXDE link to a secondary extended record, writes both tables back, and finally clears the caller's File Control Block.
Fetch the cylinder byte of the current extent entry, addressed by Register Pair HL, into Register E. A value below FEH is a real cylinder number, FEH marks a link to a secondary extended directory record, and FFH ends the chain.
Store the extent word now in Register Pair DE into 4E5BH, the operand of the LD DE,nnnn instruction at 4E5AH. Once the whole record has been dealt with, that instruction reloads this word so the code at 4E5DH can tell whether the record ended with an FXDE link and, if it did, which record it points at.
Loop End
The 0000H operand at 4E5BH was overwritten at 4E34H with the entry that ended this directory record. Register Pair DE therefore comes back holding that terminator: Register E is FEH for a link or FFH for the end of the chain, and Register D is the linked directory record number when Register E is FEH.
Store zero into the byte of the File Control Block addressed by Register Pair HL.
Loop End
4E72H - Close Out a Logical Device
A logical device owns no granules and no directory record, so there is nothing to release. The control block is simply rebuilt as the four-byte device specification *XX followed by the 03H terminator, exactly as SYS3/SYS does at 5029H when it closes a device, leaving a block that can be handed straight back to an open request.
4E8BH - Release One Extent Into the Granule Allocation Table
Given an extent word in Register Pair DE, this routine clears the allocation bit of every granule the extent describes. It reads the drive geometry to find how many granules a cylinder holds, doubling it for a double-sided drive, then walks the extent granule by granule, rolling over to the next cylinder's allocation byte whenever the granule number reaches the per-cylinder figure.
Store the granules-per-cylinder figure held in Register A into 4EBCH, the immediate operand of the CP nn instruction at 4EBBH, so the loop below knows when a granule number has run off the end of a cylinder and must roll over to the next one.
Save the current granule number held in Register A, since the helper called below uses Register A to build its instruction.
Compare the granule number in Register A against the operand at 4EBCH, which 4EA4H set to the number of granules on one cylinder. If they are equal the extent has run off the end of this cylinder.
Loop End
4EC7H - Granule Allocation Bit Reset Builder
A granule number from 0 to 7 cannot be used directly by a Z80 bit instruction, so this helper constructs the second opcode byte of a RES n,B instruction for that granule and plants it in the instruction at 4ED1H before executing it against a cylinder's allocation byte. It is the exact counterpart of the bit-setting helper SYS8/SYS uses when it allocates a granule.
Store the constructed opcode byte held in Register A into 4ED2H, which is the second byte of the RES 0,B instruction at 4ED1H, turning it into a reset of the wanted granule's bit.
Clear the bit of Register B selected by the operand planted at 4ED2H. Register B holds a cylinder's Granule Allocation Table byte, so this marks that granule as free. This is the exact counterpart of the bit-setting helper SYS8/SYS uses at 5027H when it allocates a granule.
4ED4H - Read or Write the Granule Allocation Table
One body serves both directions, selected by which of two entry addresses the caller uses. Sector 0 of the directory cylinder holds the Granule Allocation Table, one byte per cylinder with a set bit for every granule in use, and it is held in this overlay's buffer at 5000H. Reads return error 20 on failure; writes go out with a deleted-data address mark, are verified, and return error 21.
Two Entry Points In One Instruction
This routine is entered at 4ED4H to read the Granule Allocation Table and at 4ED5H to write it. 4ED4H is the two-byte instruction OR 0AFH, whose second byte is 0AFH. Entering at 4ED5H therefore executes that byte on its own as XOR A, which sets the Z FLAG, while entering at 4ED4H performs the OR and leaves the NZ FLAG set. The test at 4EE2H reads that flag to choose between reading and writing, so the two entry addresses select the two directions through one shared body. The file-deletion path calls 4ED4H at 4E25H to read the table and 4ED5H at 4E63H to write it back.
4EF8H - Read the Hash Index Table
Sector 1 of the directory cylinder holds the Hash Index Table, one hash byte per directory record with zero marking a free record. Two entry points choose the destination buffer: 4EF8H reads it into the overlay buffer at 5000H and 4EFDH into the resident shared buffer SBUFF$ at 4200H, which is the one the deletion path uses. A failure returns error 22.
4F0FH - Write the Hash Index Table
The amended Hash Index Table image at 4200H is written back to sector 1 of the directory cylinder with a deleted-data address mark and then verified, committing the release of the file's directory records. A failure returns error 23.
4F28H - Resident Module and Driver Control Block Lookup
This is the service behind request code 0BCH. Register Pair DE addresses a module or device name; the routine measures it, then walks the chain of resident modules installed above HIGH$ comparing each header's name against it. A match returns the address of that module's control block in Register Pair DE with the Z FLAG set, which is what SYS6/SYS LINK and ROUTE and SYS7/SYS SYSTEM, FORMS and SETCOM use to find the block they are about to edit. No match returns error 8.
Resident Module Chain
Each resident driver, filter or module installed above HIGH$ begins with a header whose first byte is 18H, a relative jump over the header itself. Header offset 2 and 3 hold a link word, offset 4 holds the length of the module name in its low nibble, offset 5 begins the name, and the bytes after the name are the module control block that this service returns. The link word is stored one below the header it points at, so incrementing it yields the next header and the FFFFH end marker becomes zero.
Increment Register C, the running count of characters in the name supplied by the caller.
Loop End
Fetch the first byte of the module header addressed by Register Pair HL into Register A. Every resident LDOS module begins with a jump over its own header.
Fetch the next character of the caller's name, addressed by Register Pair DE, into Register A.
Loop End
Loop End