TRS-80 DOS - TRSDOS v2.3 for the Model I - SYS4/SYS Disassembled
Page Customization
Page Index
SYS4/SYS
Other Navigation
Introduction/Summary:
Program Overview
SYS4/SYS is the TRSDOS 2.3 error display overlay for the TRS-80 Model I, loaded into memory at 4E00H when an error condition is reported via the SVC code 86H (LD A,86H / RST 28H) dispatcher at SYS0. SYS4 converts a numeric error code into a human-readable multi-word error message and displays it on screen.
The overlay uses a compact dictionary-based message system rather than storing complete error message strings. A vocabulary of 53 common words (such as "READ", "WRITE", "ERROR", "DISK", "FILE", etc.) is stored in a string pool at 5030H-515AH. Each error code maps through a two-level lookup: first, an offset table at 4F84H translates the error code to a pointer into a descriptor chain at 515BH-51F2H. Each descriptor byte encodes a word index (bits 0-5) and a continuation flag (bit 7). The display loop walks the chain, printing each word separated by spaces, until it reaches a descriptor with bit 7 set (marking the last word).
Word addresses are stored in a DEFW address table at 4FC2H using a clever overlapping-entry scheme: each word's start address is a 2-byte entry, and the word's length is computed by subtracting the current entry from the next entry (SBC HL,DE). This eliminates the need to store separate length fields.
Beyond the core error message, SYS4 optionally displays contextual information. When bit 6 of the original error code is clear, the overlay first prints "*** ERRCODE=XX, " with the error number converted to a two-digit decimal value. After the error message words, if bit 6 is clear, it also displays extended context: the device name and drive number (from the FCB at 430AH), the associated filename (extracted from the FCB or directory entry), and the return address in hexadecimal format. The return address display uses the saved overlay return address at 430CH, adjusted by subtracting 3 to point back to the LD A,xxH instruction that triggered the error.
SYS4 terminates by testing bit 7 of the original error code. If bit 7 is set, it performs a cold restart via JP 0000H. If bit 7 is clear, it jumps to the error-already-displayed exit at 4030H to return to the DOS READY prompt.
Variable and Data Area List
| Address Range | Purpose |
|---|---|
| 430AH-430BH (2 bytes) | Saved FCB pointer (set by SYS0 overlay call context setup) |
| 430CH-430DH (2 bytes) | Saved overlay return address (points past the RST 28H that invoked SYS4) |
| 4F36H-4F46H (17 bytes) | Error code display template: LF + "*** ERRCODE=XX, " + ETX (XX at 4F42H-4F43H are self-modified) |
| 4F42H-4F43H (2 bytes) | Self-modified error code digits (tens and units, ASCII) within the 4F36H template |
| 4F47H-4F4AH (4 bytes) | Closing error marker string: "***" + CR |
| 4F4BH-4F58H (14 bytes) | Device display template (screen code + "<DEVICE=*XX>" + CR; bytes at 4F55H-4F56H are self-modified with drive/info) |
| 4F55H-4F56H (2 bytes) | Self-modified device context bytes within the 4F4BH template (drive number and info byte from FCB) |
| 4F59H-4F5FH (7 bytes) | File display prefix (screen code + "<FILE=") |
| 4F60H-4F6FH (16 bytes) | Filename buffer (filled at runtime with NAME/EXT:D> string + CR terminator) |
| 4F70H-4F81H (18 bytes) | Return address display prefix (screen code + "REFERENCED AT X'" + ETX) |
| 4F82H-4F83H (2 bytes) | Return address display suffix ("'" + CR) |
| 4F84H-4FC1H (62 bytes) | Error code offset table (one byte per error code 00H-3DH, indexing into 51xxH descriptor area; codes 27H-3DH all map to 51F0H default) |
| 4FC2H-4F2DH (108 bytes) | Word address DEFW table (54 entries, each a 2-byte little-endian pointer to the start of a word in the string pool; word length derived from next entry) |
| 5030H-515AH (299 bytes) | Error message word string pool (53 words: "NO", "ERROR", "FORMAT", "PARITY", "DURING", "HEADER", "DATA", "SEEK", "READ", "WRITE", "LOST", "NOT", "ATTEMPTED TO", "LOCKED/DELETED", "SYSTEM", "DIRECTORY", "MEMORY", "ON", "DISK", "DISKETTE", "FAULT", "PROTECTED", "ILLEGAL", "LOGICAL", "NUMBER", "FILE", "RECORD", "END", "OF", "OUT", "RANGE", "ENCOUNTERED", "CODE", "GAT", "HIT", "OVERLAY", "UNKNOWN", "LOAD", "SPACE", "ONLY", "NAME", "DEVICE", "FORMAT", "FOUND", "IN", "ACCESS", "FULL", "DRIVE", "DENIED", "PROGRAM", "AVAILABLE", "- CAN'T EXTEND", "OPEN") |
| 515BH-51F2H (152 bytes) | Error message descriptor chains (variable-length byte sequences; bits 0-5 = word index, bit 6 = context flag, bit 7 = last-word-in-chain flag) |
Self-Modifying Code Locations
| Writer | Target | Purpose |
|---|---|---|
| 4E20H | 4F42H-4F43H | Error code decimal digits (tens digit at 4F42H, units digit at 4F43H) written into the "*** ERRCODE=XX, " template string |
| 4E94H | 4F55H-4F56H | Drive number (C) and directory info byte (B) from the FCB, stored into the "<DEVICE=*XX>" template string |
| 4E6EH | 4E6EH | Overlay return address stored by EX (SP),HL at entry (4E00H-4E01H); the LD (4E6EH),HL at 4E01H saves the return address for later JP M,0000H target |
Major Routine List
| Address | Purpose |
|---|---|
| 4E00H | Error Display Entry Point Main entry; saves context, converts error code to decimal, displays "*** ERRCODE=XX, " prefix (if bit 6 clear), then falls through to message lookup |
| 4E2AH | Error Message Lookup and Display Uses error code as index into offset table (4F84H), reads descriptor chain from 51xxH, looks up each word from DEFW table (4FC2H), and prints words separated by spaces |
| 4E5CH | Error Display Completion Optionally displays extended context (device, file, return address) via 4E73H, outputs CR, restores registers, and exits via JP M,0000H or JP 4030H |
| 4E73H | Extended Error Context Display Displays "***" closing, then device info, filename (from FCB or directory), and return address in hex |
| 4EC4H | Filename From Directory Entry Reads directory sector via 4AC1H, extracts 8-char filename and 3-char extension into 4F60H buffer as NAME/EXT:D format |
| 4EF9H | Append Drive and Terminator Appends ":D>" + CR to filename buffer, then displays file and return address context strings |
| 4F06H | Display Return Address Displays "REFERENCED AT X'nnnn'" using the saved return address at 430CH minus 3 |
| 4F1BH | Display HL as Hexadecimal Outputs H then L as 2-digit hex values via the nibble converter at 4F20H |
| 4F20H | Hex Byte Display Converts a byte in A to two hex ASCII characters and outputs them via ROM 0033H |
Cross-Reference Notes
SYS4 is invoked by SYS0 via SVC code 86H (LD A,86H / RST 28H). The RST 28H overlay loader at 4BA2H loads SYS4 from disk into 4E00H and transfers control. SYS4 calls back into SYS0 for three services: 4467H (display message string on screen), 4AC1H (read directory sector into 4200H buffer), and 4030H (error-already-displayed exit). SYS4 also calls ROM routine 0033H directly for individual character output during the error message word display loop and the hex display routine.
Error Code Table
SYS4 handles error codes 00H through 26H with unique messages. Codes 27H through 3DH all display the default message "UNKNOWN ERROR". The complete error message for each code is assembled from dictionary words:
| Code | Message |
|---|---|
| 00H | NO ERROR |
| 01H | PARITY ERROR DURING HEADER READ |
| 02H | SEEK ERROR DURING READ |
| 03H | LOST DATA DURING READ |
| 04H | PARITY ERROR DURING READ |
| 05H | DATA RECORD NOT FOUND DURING READ |
| 06H | ATTEMPTED TO READ LOCKED/DELETED DATA RECORD |
| 07H | ATTEMPTED TO READ SYSTEM DATA RECORD |
| 08H | DEVICE NOT AVAILABLE |
| 09H | PARITY ERROR DURING HEADER WRITE |
| 0AH | SEEK ERROR DURING WRITE |
| 0BH | LOST DATA DURING WRITE |
| 0CH | PARITY ERROR DURING WRITE |
| 0DH | DATA RECORD NOT FOUND DURING WRITE |
| 0EH | WRITE FAULT ON DISK DRIVE |
| 0FH | WRITE PROTECTED DISKETTE |
| 10H | ILLEGAL LOGICAL FILE NUMBER |
| 11H | DIRECTORY READ ERROR |
| 12H | DIRECTORY WRITE ERROR |
| 13H | ILLEGAL FILE NAME |
| 14H | GAT READ ERROR |
| 15H | GAT WRITE ERROR |
| 16H | HIT READ ERROR |
| 17H | HIT WRITE ERROR |
| 18H | FILE NOT IN DIRECTORY |
| 19H | FILE ACCESS DENIED |
| 1AH | DIRECTORY SPACE FULL |
| 1BH | DISK SPACE FULL |
| 1CH | END OF FILE ENCOUNTERED |
| 1DH | RECORD NUMBER OUT OF RANGE |
| 1EH | DIRECTORY FULL - CAN'T EXTEND FILE |
| 1FH | PROGRAM NOT FOUND |
| 20H | ILLEGAL DRIVE NUMBER |
| 21H | NO DEVICE SPACE AVAILABLE |
| 22H | LOAD FILE FORMAT ERROR |
| 23H | MEMORY FAULT |
| 24H | ATTEMPTED TO LOAD READ ONLY MEMORY |
| 25H | ILLEGAL ACCESS ATTEMPTED TO PROTECTED FILE |
| 26H | FILE NOT OPEN |
| 27H+ | UNKNOWN ERROR (default for all undefined codes) |
Disassembly:
4E00H - Error Display Entry Point
Main entry point for the error display overlay. Called by the RST 28H overlay loader when SVC code 86H is invoked. On entry, the stack contains the saved return address, the original Register A (error code), and the caller's saved registers. This routine saves context, converts the error code to a two-digit decimal number, and optionally displays the "*** ERRCODE=XX, " prefix before falling through to the message lookup.
Store the overlay return address (now in HL) into the operand of the JP M instruction at 4E6DH. The two bytes at 4E6EH-4E6FH form the target address of that JP M instruction. At assembly time the target is 0000H, but at runtime this stores the actual return address so that if the error code has bit 7 set, the JP M will jump to the overlay loader's return point (which in practice is 0000H for a cold restart, since SYS0 sets bit 7 for fatal errors requiring reboot).
Error Code to Decimal Conversion
The following loop converts the binary error code in Register A (range 00H-3FH, maximum 63 decimal) into a two-digit decimal number. Register E accumulates the tens digit as an ASCII character (starting at 30H = '0'), and the units digit remains in Register A after the loop.
SUBtract 10 (0AH) from Register A. If the result is negative (A was less than 10), the CARRY FLAG is set, meaning all tens have been extracted and the remainder is the units digit.
Store the two ASCII decimal digits (DE) into the error code template string at 4F42H-4F43H. The template at 4F36H reads: LF + "*** ERRCODE=XX, " + ETX. The "XX" placeholder bytes at 4F42H-4F43H are now overwritten with the actual tens digit (E at 4F42H) and units digit (D at 4F43H) of the error code. Note: the Z80 stores DE as E first (low byte at 4F42H) then D (high byte at 4F43H), which correctly places tens-then-units since E holds the tens digit and D holds the units digit.
4E2AH - Error Message Lookup and Display Loop
Looks up the error code in the offset table to find the descriptor chain, then walks the chain displaying each word from the dictionary. Each descriptor byte encodes a word index (bits 0-5) and a continuation flag (bit 7). Words are separated by spaces. The loop continues until a descriptor with bit 7 set is encountered.
Descriptor Chain Display Loop
The following loop reads descriptor bytes from the chain at (HL). Each descriptor byte contains a word index in bits 0-5 (0-63), which is doubled and used as an offset into the DEFW address table at 4FC2H. From that table, two consecutive DEFWs are read: the first gives the word's start address, the second gives the address past the end of the word. The word length is computed by subtraction, and the word characters are output one at a time via ROM 0033H. After each word, a space is printed. Then the descriptor byte's bit 7 is checked: if clear, the next descriptor byte is read and the loop continues. If set, this was the last word in the message.
Word Character Print Loop
Print B characters starting at (HL), outputting each via the ROM character display routine at 0033H.
Fetch the next character of the word from the string pool at (HL) into Register A.
DECrement Register B (character count) and loop back to 4E4AH if not zero. When B reaches zero, all characters of the current word have been printed.
4E5CH - Error Display Completion
After all error message words have been displayed, this section optionally calls the extended context display (device, file, return address), outputs a carriage return, restores all saved registers, and exits. The exit path depends on bit 7 of the original error code: bit 7 set causes a cold restart via JP M,0000H; bit 7 clear exits to the error-already-displayed handler at 4030H.
If the SIGN FLAG is set (bit 7 of the original error code is 1, indicating a fatal error requiring reboot), JUMP to the address stored at 4E6EH-4E6FH. The target address was written by the LD (4E6EH),HL instruction at 4E01H during entry setup. For fatal errors, this typically jumps to 0000H (cold restart). If the SIGN FLAG is clear, execution falls through to 4E70H.
4E73H - Extended Error Context Display
Called when bit 6 of the error code is clear. Displays the "***" closing marker, then the device/drive information from the FCB, the associated filename (either copied directly from the FCB filespec or read from the directory entry), and the return address where the error was triggered. This routine provides the user with full diagnostic context about which file and operation caused the error.
Directory-Based Filename Path
When bit 6 of the last descriptor byte is clear, the file is still open and has a valid FCB with drive and directory info. The filename is extracted by reading the directory sector and copying the name/extension fields. First, the drive number and directory info byte are saved into the device template string.
File Not Open Path
If the file is not open (bit 7 of IX+00H is clear), the directory cannot be read to get the filename. Instead, display only the device information with the drive/info bytes, and show the generic "DEVICE=*XX" message.
Store BC (C=drive number, B=directory info byte) into the device template string at 4F55H-4F56H. The template at 4F4BH contains a screen code followed by "<DEVICE=*XX>" where XX at 4F55H-4F56H are the placeholders. After this store, the two bytes become the actual drive number and directory info byte values (displayed as their raw character representations).
4EA0H - FCB Filespec Extraction Path
When the last descriptor byte has bit 6 set, the FCB contains a filespec string (NAME/EXT:D format terminated by 03H) starting at IX+00H. This path copies that string directly into the filename display buffer at 4F60H. The FCB status byte is first checked for the special value 2AH (which indicates the FCB pointer is actually a chained pointer to another FCB).
FCB Chain Follow
The FCB status byte is 2AH, meaning IX+01H/IX+02H contain a pointer to the real FCB. Load BC with this pointer and jump back to 4E92H to store the device info and display from the redirected FCB's context.
Direct Filespec Copy From FCB
The FCB at IX contains a filespec string (such as "MYFILE/BAS:0>" terminated by 03H). Copy up to 24 bytes from IX into the filename buffer at 4F60H, stopping early if a 03H terminator is encountered.
Filespec Copy Loop
Copy characters from (HL) to (DE), stopping when a 03H (ETX) terminator is found. The loop counter B starts at 00H (from the high byte of 0018H), so DJNZ would wrap to FFH - the 03H check is the real loop terminator.
Fetch the next character from the FCB filespec string at (HL) into Register A.
DECrement Register B and loop back to 4EB8H if not zero. Since B started at 00H, the first DJNZ decrements to FFH and continues. The loop is effectively terminated by the 03H check at 4EB9H, not by B reaching zero.
4EC4H - Filename From Directory Entry
When the file is open and the descriptor byte's bit 6 is clear, the filename is read from the actual directory sector on disk. The directory sector is loaded via the SYS0 routine at 4AC1H, and the 8-character filename and 3-character extension are extracted into the display buffer at 4F60H in NAME/EXT:D format.
Filename Copy Loop
Copy up to 8 characters of the filename from the directory entry to the display buffer, stopping early if a space (20H) is encountered. Spaces in the filename indicate the end of the actual name (the rest is padding).
Fetch the next filename character from the directory entry at (HL) into Register A.
DECrement Register B and loop back to 4ED0H if not zero. After all 8 filename characters have been processed (or a space was found), fall through.
Filename/Extension Separator and Extension Copy
After the filename, append a '/' separator and then copy the 3-character extension, again stopping on spaces.
Fetch the next extension character from the directory entry at (HL) into Register A.
DECrement Register B and loop back to 4EE4H if not zero. After all 3 extension characters have been processed, fall through to append the drive specifier.
Append Drive Specifier and Terminator
After the filename and extension, append ":D>" (colon, drive number as ASCII digit, closing angle bracket) followed by a carriage return to complete the display string. Then display the file information and return address strings.
4F06H - Display Return Address
Displays the return address where the error was triggered, formatted as "REFERENCED AT X'nnnn'" where nnnn is the 4-digit hexadecimal address. The return address is obtained from the saved overlay return address at 430CH, minus 3 bytes to point back to the LD A,xxH instruction that originally invoked the error handler.
4F1BH - Display HL as 4-Digit Hexadecimal
Converts the 16-bit value in Register Pair HL to four hexadecimal ASCII characters and outputs them on screen. Displays H first (high byte), then L (low byte), each as two hex digits.
4F20H - Hex Byte Display
Converts a single byte in Register A to two hexadecimal ASCII characters and outputs them via ROM 0033H. The high nibble is output first, then the low nibble. Each nibble is converted using the standard hex-to-ASCII algorithm: digits 0-9 map to '0'-'9' (30H-39H), and digits A-F map to 'A'-'F' (41H-46H).
4F36H - Error Code Display Template String
Template string displayed when bit 6 of the error code is clear. Contains LF + "*** ERRCODE=XX, " + ETX, where the "XX" bytes at 4F42H-4F43H are overwritten at runtime with the decimal error code digits.
Placeholder for the two-digit decimal error code. At assembly time these bytes are "XX" (58H 58H). At runtime, the LD (4F42H),DE instruction at 4E20H overwrites them with the actual tens digit (at 4F42H) and units digit (at 4F43H) as ASCII characters.
4F47H - Closing Error Marker String
Displayed by the extended context routine at 4E74H to close the error message line before showing device and file details.
4F4BH - Device Display Template String
Template for displaying device (drive/directory info) context. Shown when the file is not open and only device identification is available.
Placeholder for the drive number (low byte at 4F55H) and directory info byte (high byte at 4F56H). The LD (4F55H),BC instruction at 4E94H overwrites these with the actual FCB values at runtime.
4F59H - File Display Prefix String
Prefix for the filename display line. This string is immediately followed in memory by the filename buffer at 4F60H, so the 4467H display routine reads through both contiguously.
4F60H - Filename Display Buffer
16-byte work area where the filename is assembled at runtime in NAME/EXT:D> format followed by a 0DH terminator. Filled by either the FCB filespec copy loop (4EB8H) or the directory entry extraction routine (4EC4H).
4F70H - Return Address Display Prefix String
Prefix for the "REFERENCED AT X'nnnn'" line that shows where the error was triggered.
4F82H - Return Address Display Suffix String
Closing suffix for the return address display: a single quote and carriage return.
4F84H - Error Code Offset Table
Lookup table with one byte per error code (00H-3DH, 62 entries). Each byte is a page offset into the 51xxH descriptor area. The code at 4E2AH uses the masked error code as an index into this table, loads the byte, and combines it with H=51H to form the descriptor chain address. Error codes 27H through 3DH all map to offset F0H (address 51F0H), which is the "UNKNOWN ERROR" default descriptor.
4FC2H - Word Address DEFW Table
Table of 54 two-byte (DEFW) little-endian addresses, each pointing to the start of a word in the string pool at 5030H-515AH. The word's length is determined by subtracting its start address from the next entry's address (the overlapping-entry technique used by the SBC HL,DE at 4E46H). Entry 0 (F0F0H) is padding and unused; entries 1-53 correspond to the 53 vocabulary words.
5030H - Error Message Word String Pool
Contiguous block of 53 error message vocabulary words stored as plain ASCII text without any separators or terminators between them. Word boundaries are defined entirely by the DEFW address table at 4FC2H. The words are arranged sequentially from "NO" at 5030H through "OPEN" at 5157H.
515BH - Error Message Descriptor Chains
Variable-length byte sequences that define the word composition of each error message. Each byte encodes: bits 0-5 = word index (1-53) into the DEFW table at 4FC2H, bit 6 = context flag (when set on the last byte, indicates the FCB contains a filespec string for filename display), bit 7 = last-word flag (when set, this is the final word in the message). Chains for different error codes are stored contiguously; the offset table at 4F84H provides the start address of each chain. The descriptor chain area spans from 515BH to 51F2H (152 bytes).