TRS-80 DOS - TRSDOS v2.3 for the Model I - SYS2/SYS Disassembled
Page Customization
Page Index
SYS2/SYS
Other Navigation
Introduction/Summary
TRSDOS v2.3 SYS2/SYS Disassembly - Opening Files (Model I)
SYS2/SYS is the TRSDOS 2.3 system overlay that opens files. SYS0/SYS loads it into the overlay area at 4E00H whenever an RST 28H code with 4 in bits 0-3 is executed, and enters it at 4E00H with that code in Register A. It does three jobs: open an existing file (SYS0/SYS's 4424H), open a file or create it when it is not found (4420H), and make a continuation directory entry when a file needs a new extent and only its entry's last extent field is left, which SYS0/SYS's granule allocation asks for while writing.
The file holds 853 bytes in four load records covering 4E00H-5154H, after a name record and a 169-byte copyright comment record; its transfer address is 4E00H. The work area 5155H-516BH after the last byte is not in the file. Every byte is shown once on this page, in address order, checked against S:\1\SYS2.SYS and its listing.
Memory Map
| Address Range | Use |
|---|---|
| 4E00H-4E11H 18 bytes | Function dispatcher on bits 4-6 of the RST 28H code. |
| 4E12H-4ED7H 198 bytes | Open an existing file: parse, hash, search drives, check the password, fill the FCB. |
| 4ED8H-4F4FH 120 bytes | Open or create: the open code, then create a new file. |
| 4F50H-4FA6H 87 bytes | Make a continuation directory entry. |
| 4FA7H-5026H 128 bytes | Build the FCB from a directory entry. |
| 5027H-5080H 90 bytes | Parse a filespec into the name, password and drive work area. |
| 5081H-509AH 26 bytes | Copy one field of a filespec. |
| 509BH-50AAH 16 bytes | Hash a file name to one byte. |
| 50ABH-50D0H 38 bytes | Find a free entry in the hash index. |
| 50D1H-50FCH 44 bytes | Hash an eight-byte password to a word. |
| 50FDH-512DH 49 bytes | Test whether a drive is ready. |
| 512EH-5153H 38 bytes | Read and write the hash index table. |
| 5154H 1 byte | The drive of the filespec (last byte of the file). |
| 5155H-516BH 23 bytes | Password, name template and password hashes, not in the file. |
Functions
Bits 0-3 of every code below are 4 (SYS2/SYS); bits 4-6 are the function that 4E00H tests. SYS0/SYS's fixed entry points supply the codes.
| Code | Entered From | Function |
|---|---|---|
| 94H | 4424H | Open the existing file named in the FCB at Register Pair DE (4E12H). |
| A4H | 4420H | Open the file, or create it when it is not in the directory (4ED8H). |
| B4H | 4A5CH | Make a continuation directory entry for the file in Index Register IX (4F50H), for SYS0/SYS's granule allocation. |
The Directory and the Hash Index
A file is found through the hash index table in directory sector 1, read into 4D00H-4DFFH. The table has one byte for each directory entry, at the offset of the entry's locator (bits 5-7 the entry in its sector, bits 0-4 the sector minus 2): 00H for a free entry, otherwise the one-byte hash of the file name. To open a file, SYS2/SYS hashes the typed name and walks the table; each byte that matches the hash is a candidate, and its directory entry is read and its eleven name bytes compared to be sure. To create one, the table is searched for a 00H byte, starting at a point taken from the clock so new files spread through the directory.
A directory entry is 32 bytes: byte 0 attributes (bits 0-2 the access level, bit 7 set on a continuation entry), byte 1 the primary entry's locator on a continuation entry, byte 3 the end-of-file byte, byte 4 the logical record length, bytes 5-15 the eleven-byte name and extension, bytes 16-19 the owner and user password hashes, bytes 20-21 the ending record number, and bytes 22-31 five two-byte extents (track, then a byte whose bits 5-7 are the first granule and bits 0-4 one less than the granule count). A track byte of FEH means the file is continued in the entry whose locator follows; FFH ends the extents.
Passwords
Each file carries two sixteen-bit password hashes in its directory entry, the owner password and the user password, computed by the hash at 50D1H. When a file is opened the typed password is hashed and compared with both; the owner password opens the file at access level 0, the user password at the level in the entry. A typed password whose hash is 61A2H opens any file whatever its own password, and a file at access level 7 cannot be opened without it.
Notes from the Code
- The scan at 4E49H reads the hash index table at 4D00H, not the directory entries; only a hash that matches leads to a directory entry being read and its name compared.
- The name hash at 509BH is one byte and the password hash at 50D1H is two, so different names or passwords can share a hash; the name is always confirmed byte by byte, but a password is accepted on its hash alone.
- The hash 61A2H typed as a password opens any file (4EA5H). An all-spaces password hashes to 4296H, so this is a separate master value, not the hash of a blank password.
- The create path at 4EF4H skips write-protected diskettes; the open path does not test write protect.
- A new file's directory entry is given attribute byte 10H and its owner and user password words both hold the hash of the password typed when it was created (4F35H).
Variables
Work Area and Self-Modified Operands
| Address Range | Purpose |
|---|---|
| 4E4EH 1 byte | Operand of CP nn at 4E4DH: the name hash the index scan looks for (set at 4E29H). |
| 4F1DH 1 byte | Operand of LD (HL),nn at 4F1CH: a new entry's attribute byte, 10H (set at 4EE4H). |
| 4FA2H 1 byte | Operand of LD (HL),nn at 4FA1H: the continuation locator stored in the primary entry (set at 4F62H). |
| 4FAFH 1 byte | Operand of LD A,nn at 4FAEH: the logical record length (set at 4E16H). |
| 4FB2H 1 byte | Operand of LD A,nn at 4FB1H: the access level the password granted (set at 4ED0H). |
| 4FC0H-4FC1H 2 bytes | Operand of LD DE,nn at 4FBFH: the caller's buffer address for the FCB (set at 4E19H). |
| 5154H 1 byte | The drive of the filespec, FFH for none (5027H). |
| 5155H-515CH 8 bytes | Password buffer, not in the file. |
| 515DH-5167H 11 bytes | Name and extension template, not in the file. |
| 5168H-5169H 2 bytes | Password hash, not in the file. |
| 516AH-516BH 2 bytes | Password hash copy, not in the file. |
Addresses Outside SYS2/SYS
| Address Range | Use |
|---|---|
| 4040H 1 byte | Clock tick counter, read at 50ABH to vary where a new entry is placed. |
| 4200H-42FFH 256 bytes | System buffer where SYS0/SYS's 4AC1H and 4AD6H read and write a directory sector. |
| 4ABEH 1 byte | The primary entry's locator, left by SYS0/SYS at 49BDH and read at 4F77H and 4F92H. |
| 4D00H-4DFFH 256 bytes | The hash index table (directory sector 1), read and written by 512EH and 5141H. |
| 37ECH 1 byte | Floppy controller command and status register, used by the drive-ready test at 50FDH. |
| 4896H, 4AC1H, 4AD6H, 4B35H, 4B55H, 4600H, 46EFH SYS0/SYS | File call set-up, read and write a directory sector, read and write a system sector, select a drive, write with the FAH mark. |
Disassembly
4E00H - Function Dispatcher
SYS0/SYS's overlay call (4BFBH) enters SYS2/SYS here with Register A = the RST 28H code whose bits 0-3 are 4. Bits 4-6 select the function: 10H (code 94H, 4424H) opens an existing file, 20H (code A4H, 4420H) opens a file or creates it when it is not found, and 30H (code B4H) makes a continuation directory entry for SYS0/SYS's granule allocation (4A5CH). Register Pair DE = the FCB; for 30H Index Register IX = the FCB whose extents overflowed.
4E12H - Open an Existing File (4424H)
Register Pair DE = the FCB holding the filespec as text. Parses the name, computes its hash and the password hash, then searches drives for a directory entry whose hash, name and password match and fills the FCB from it. Returns Register A = 00H and the Z FLAG with the file open, or an error number and the NZ FLAG: 13H (ILLEGAL FILE NAME), 16H (HIT READ ERROR), 11H (DIRECTORY READ ERROR), 18H (FILE NOT IN DIRECTORY) or 19H (FILE ACCESS DENIED).
GOSUB to 50FDH to test whether drive Register C is ready. 4E5EH loops back here for the next drive.
Fetch a hash byte from the table at (HL) (Register Pair HL = 4D00H plus the entry locator) into Register A. 4E52H loops back here.
00H in the file.
If the NZ FLAG is set (Register L has not wrapped past 255), LOOP BACK to 4E49H.
Fetch the drive from 5154H into Register A again. 4E43H comes here.
Restore Register Pair BC from the stack. 4E79H and 4E86H jump here.
Save Register Pair HL (the position in the hash index) onto the stack. 4E4FH comes here.
Save Register Pair HL (the entry) onto the stack. 4E70H comes here.
Fetch a template byte from (DE) (Register Pair DE = the template) into Register A. 4E8AH loops back here.
DECrement Register B and LOOP BACK to 4E84H until all eleven bytes match.
Save Register Pair BC onto the stack.
Restore Register Pair HL from the stack. 4EB1H comes here.
Restore Register Pair HL (the entry) from the stack. 4EACH, 4EBCH and 4EC7H come here.
4ED8H - Open or Create a File (4420H)
Register Pair DE = the FCB holding the filespec, Register B = the logical record length. Opens the file through the same code as 4E12H; when it is not in the directory a new file is created. Returns Register A = 00H and the Z FLAG with the file open, the Z FLAG and the CARRY FLAG with a new file created, or an error number and the NZ FLAG: the open errors, 1AH (DIRECTORY SPACE FULL) or 12H (DIRECTORY WRITE ERROR).
GOSUB to 50FDH to test whether drive Register C is ready. 4F09H loops back here.
Fetch the drive from 5154H into Register A. 4EF2H and 4EF4H come here.
Copy the free locator from Register L into Register B. 4EFDH comes here.
00H in the file.
Store FFH into the entry at (HL) (Register Pair HL = entry+16H to +1FH). 4F43H loops back here.
DECrement Register B and LOOP BACK to 4F40H: the five extents are all FFH, no granules.
4F50H - Make a Continuation Directory Entry (code B4H)
SYS0/SYS's granule allocation (4A5CH) calls this through RST 28H when a file needs a new extent and only the entry's last extent field (bytes 1EH-1FH) is left. Index Register IX = the FCB; its byte 06H is the drive and byte 07H the locator of the primary entry, whose locator SYS0/SYS also left in 4ABEH. A free directory entry is claimed, marked as a continuation (90H) that points back to the primary at byte 01H, and the primary's last extent is set to FEH and the new locator so the file's extents carry on in it. Returns the Z FLAG with the new locator in Register B, or 1EH (DIRECTORY FULL) and the NZ FLAG, or a directory read or write error.
Store 00H into the entry at (HL) (Register Pair HL = entry+02H to +15H). 4F81H loops back here.
DECrement Register B and LOOP BACK to 4F7EH until twenty bytes are zero.
Store FFH into the entry at (HL) (Register Pair HL = entry+16H to +1FH). 4F89H loops back here.
DECrement Register B and LOOP BACK to 4F86H: the continuation entry's extents are all FFH.
00H in the file.
4FA7H - Build the FCB from a Directory Entry
Register Pair HL = the directory entry in 4200H, Register C = drive, Register B = the entry locator; 4FAFH holds the logical record length, 4FB2H the access level the password granted and 4FC0H the buffer address the caller gave. Fills the 32-byte FCB at Index Register IX from the entry and returns Register A = 00H. The five four-byte extents are copied with a running count of the granules before each; a track byte of FEH (continued) or FFH (end) stops the copy and the rest of the extent area is set to FFH.
00H in the file.
00H in the file.
0000H in the file.
Save Register Pair AF (Register A = the count of extents left) onto the stack. 5019H loops back here.
Restore Register Pair AF (Register A = the count of extents left) from the stack. 5001H comes here.
Store FFH into the FCB at (HL) (Register Pair HL = the FCB extent area). 5023H loops back here.
DECrement Register B and LOOP BACK to 5020H until the extent area is full of FFH.
5027H - Parse a Filespec into the Work Area
Register Pair HL = the filespec as text ending in 03H. Fills the name template at 515DH with eight characters of name and three of extension (padded with spaces), the password at 5155H with eight characters (padded with spaces) and the drive at 5154H. The name must start with a letter; later characters may be letters or digits. Returns Register A = 00H and the Z FLAG, or 13H (ILLEGAL FILE NAME) or 20H (ILLEGAL DRIVE NUMBER) and the NZ FLAG.
Store the space (Register A) into the template at (DE) (Register Pair DE = the template). 502FH loops here.
Store the space (Register A) into the password at (DE) (Register Pair DE = the password buffer). 5038H loops here.
5081H - Copy One Field of a Filespec
Register Pair HL = the text, Register Pair DE = where the field goes, Register B = the most characters to keep. The first character is copied only when it is a letter; later characters may be letters (41H-5AH) or digits (30H-39H). Any other character ends the field and is returned in Register A with Register Pair HL past it. A character beyond the limit is still passed over. Returns Register A = the ending character and Register B = the characters not used.
Fetch the next character from (HL) (Register Pair HL = the text pointer) into Register A. 5096H loops back here.
509BH - Hash a File Name
Register Pair HL = the eleven-byte name and extension. Combines the bytes into a one-byte value for the hash index. Returns Register A = the hash, never 00H (00H marks an unused slot). Register Pair HL ends past the name.
Fetch the next name byte from (HL) (Register Pair HL = the name) into Register A. 50A4H loops back here.
DECrement Register B and LOOP BACK to 509FH until all eleven bytes are mixed in.
50ABH - Find a Free Entry in the Hash Index
Looks for a byte of 00H in the table at 4D00H, so a new file gets a directory entry. The search starts at a locator taken from the clock at 4040H so new files spread through the directory, and falls back to locator 40H. Returns Register Pair HL = 4D00H plus the free locator and the Z FLAG, or the NZ FLAG when the table is full.
Fetch the hash byte from the table at (HL) (Register Pair HL = 4D00H plus the locator) into Register A. 50B7H calls here; 50C4H and 50CCH loop back here.
50D1H - Hash a Password
Register Pair DE = the eight-byte password. Combines the bytes into a sixteen-bit value stored in the directory entry and compared when a file is opened. Reads the eight bytes from the last to the first. Returns Register Pair HL = the hash.
Fetch the next password byte from (DE) (Register Pair DE = the password pointer) into Register A. 50FAH loops back here.
DECrement Register B and LOOP BACK to 50DDH until all eight bytes are mixed in.
50FDH - Test Whether a Drive Is Ready
Register C = drive. Selects the drive and watches the index pulse in the controller's status for about one revolution to tell whether a diskette is turning. Returns the NZ FLAG when no index pulse arrives in time (the drive is empty or not turning). When one does, returns Register A = 00H, the Z FLAG and the CARRY FLAG set when the controller reports the diskette is write protected.
| 1771 FDC Command: D0H (11010000) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | Command=Force Interrupt (bits 7-4 = 1101) I3-I0 = 0000: end the command, no interrupt | |
GOSUB to 511EH to read the index bit of the status, counting down Register Pair BC. 510CH loops back here.
GOSUB to 511EH to read the index bit again. 5111H loops back here.
GOSUB to 511EH to read the index bit again. 5116H loops back here.
DECrement Register Pair BC (the time left) by 1. 5109H, 510EH and 5113H call here.
Restore Register Pair BC from the stack, dropping this routine's return. 5121H comes here.
512EH - Read the Hash Index Table
Register C = drive. Reads directory sector 1, the hash index table, into 4D00H-4DFFH. Returns the Z FLAG, or Register A = 16H (HIT READ ERROR) and the NZ FLAG.
5141H - Write the Hash Index Table
Register C = drive. Writes 4D00H-4DFFH back to directory sector 1 with the FAH data mark. Returns the Z FLAG, or Register A = 17H (HIT WRITE ERROR) and the NZ FLAG.
5154H - Data and Work Area
The drive variable is the last byte of the file; the buffers that follow it are not loaded.
FFH while no drive is given, 0-3 once one is parsed (5027H, 507CH). 4E38H, 4E54H, 4E8EH, 4EE7H and 4EFFH read it. 00H in the file.
Not in the file. 5027H fills it with the typed password and 50D1H hashes it (4E2CH).
Not in the file. 5027H fills it with eight characters of name; 509BH hashes it and 4E7FH compares it with a directory entry.
Not in the file. 5027H fills it with three characters of extension, following the name at 515DH.
Not in the file. 4E32H stores the hash computed by 50D1H; 4F35H copies it with the copy at 516AH into a new entry's owner and user password words.
Not in the file. 4E35H stores a second copy; 4EA0H reads it to check a file's password.