TRS-80 DOS - VTOS 4.0.2 for the Model I - SYS4/SYS Disassembled
Page Customization
Page Index
SYS4/SYS
Other Navigation
Introduction/Summary:
VTOS 4.0 SYS4/SYS Disassembly - Error Display Handler (Model I)
SYS4/SYS is the error display overlay for VTOS 4.0 (Virtual Technology Operating System v4.0.2 by Randolph Cook). It is loaded into the overlay area at 4E00H-51DDH when an error condition triggers SVC code 86H via RST 28H. SYS4 is a pure display overlay: it contains no disk I/O logic except a single CALL to 4797H to read a directory sector for filename extraction. All error handling decisions are made by the calling overlay before invoking SYS4.
SYS4 uses a dictionary-based message system to convert numeric error codes into human-readable text. The system decomposes 42 error messages (codes 00H-29H) into 55 reusable words stored in a contiguous string pool, referenced through a 3-level lookup: an offset table maps each error code to a descriptor chain, each descriptor byte encodes a word index (bits 0-5) plus continuation/context flags (bits 6-7), and a DEFW address table provides the start address and implied length of each word in the pool. This overlapping-entry scheme reads 4 consecutive bytes per word (current start, next start) and computes word length by subtraction, eliminating the need for stored lengths or delimiters.
The total vocabulary of 55 words occupies 299 bytes in the string pool, plus 112 bytes for the DEFW table, 42 bytes for the offset table, and 160 bytes for the descriptor chains, totaling approximately 613 bytes for the complete error message system. This is roughly 20-25% smaller than storing the 42 messages as individual full-text strings.
SYS4's error display includes three diagnostic components beyond the message text: an error code prefix ("*** ERRCOD=XX, "), a device/file context section showing the drive and filename associated with the error, and a return address display ("REFERENCED AT X'nnnn'") identifying the instruction that triggered the error. The filename can be extracted from either the FCB filespec string (if the FCB contains status 2AH indicating an initialized reference) or from the directory entry (requiring a disk read via 4797H).
The architecture is closely related to TRSDOS 2.3 SYS4, reflecting VTOS's lineage as the direct ancestor of LDOS. VTOS extends the error code range from TRSDOS 2.3's 40 codes (00H-27H) to 42 codes (00H-29H), adding messages for "DEVICE IN USE" (27H) and "PROTECTED SYSTEM DEVICE" (28H). Several messages are also reworded, and the word dictionary is expanded from 53 to 55 entries.
The entry mechanism uses the standard VTOS inline parameter block technique: the CALL to SYS4 is followed by an error code byte in Register A, and the overlay extracts context from the stack (saved return address) and from the caller's FCB pointer. The exit path distinguishes fatal errors (bit 7 of the error code set, causing JP M,0000H cold restart) from recoverable errors (bit 7 clear, falling through to JP 4030H for error-already-displayed exit). Bit 6 controls whether extended context (device, filename, return address) is displayed.
Error Code Bit Flags
Bit 6 of the error code controls whether extended context is displayed: when clear, the full diagnostic output includes the "*** ERRCOD=XX, " prefix, the error message words, and the extended context (device, file, return address). When bit 6 is set, only the error message words are displayed with no prefix or context. Bit 7 is the fatal error flag: when clear, the exit goes to DOS READY via JP 4030H; when set, a cold restart occurs via JP M,0000H.
Descriptor Byte Format
Each byte in a descriptor chain encodes: bits 0-5 = word index (1-55) into the DEFW table at 4FB1H; bit 6 = context flag (on the last byte only: 0 = filename extracted from directory entry, 1 = filename taken from FCB filespec string); bit 7 = last-word flag (0 = more words follow, 1 = this is the final word).
Dictionary System Architecture
SYS4 uses a 3-level lookup to convert error codes to messages: (1) Offset Table at 4F89H (42 bytes): maps error_code to a page offset into 51xxH where the descriptor chain begins; (2) Descriptor Chain at 513EH-51DDH: sequence of bytes, each encoding a word index with continuation and context flags; (3) Word Address Table at 4FB1H (112 bytes): word_index x 2 gives a DEFW pointing to the start of the word in the string pool, with word length computed as next_DEFW minus current_DEFW; (4) String Pool at 5023H-513DH (283 bytes): 55 contiguous ASCII words with no separators, boundaries defined by the DEFW table.
Key Variables
| Address | Bytes | Purpose |
|---|---|---|
| 4E97H | 2 | Self-modified JP M operand: overlay return address stored here during entry (written by 4E01H). For fatal errors (bit 7 set), JP M,0000H performs cold restart; for normal errors, execution falls through to JP 4030H. |
| 430AH | 2 | Saved overlay return address (SYS0 context). Read by extended context routine at 4E9EH to load IX with FCB pointer. |
| 430CH | 2 | Saved overlay return address (alternate). Read at 4F2EH; subtracted by 3 to compute the caller's SVC stub address for "REFERENCED AT X'nnnn'" display. |
| 430FH | 1 | System mode flags. Bit 6 tested at 4E0DH: when set, XOR with error code bit 6 suppresses certain context display modes. |
| 4200H | 256 | Message assembly buffer. Error message text is built here character by character before display via CALL 447BH. |
| 4DE7H | - | SYS0 routine: Hex display for return address (called at 4F38H). Displays HL as 4-digit hexadecimal. |
| 447BH | - | SYS0 routine: Display message string on screen (called at 4E86H, 4F2BH, 4F3EH). Prints from HL until 03H or 0DH terminator. |
| 4797H | - | SYS0 routine: Read directory sector by info byte. Used at 4EEAH to load directory entry for filename extraction. |
| 4B10H | - | SYS0 routine: Directory entry validation. Used at 4EEAH to read and validate directory data. |
Self-Modifying Code Locations
| Writer | Target | Purpose |
|---|---|---|
| 4E01H | 4E97H | Overlay return address stored as JP M operand. For fatal errors, this becomes the cold restart vector (0000H); for normal errors, execution falls through past it. |
| 4E1FH-4E35H | 4F42H-4F43H | Decimal error code digits written into the "*** ERRCOD=XX, " template string at 4F41H. The tens digit at 4F42H and units digit at 4F43H. |
| 4EB5H | 4F57H-4F58H | FCB drive number and directory info byte stored into the "DEVICE=*XX" template string at 4F4DH. |
Major Routines
| Address | Name | Purpose |
|---|---|---|
| 4E00H | Error Display Entry Point | Context save, error code extraction, bit 6/7 flag processing, decimal conversion, error code prefix display, message lookup and word printing loop. |
| 4E71H | Error Display Completion | Appends "*** " closing marker, displays assembled message, conditionally calls extended context routine, restores registers, and exits. |
| 4E9CH | Extended Error Context Display | Loads FCB pointer from 430AH, determines filename source (FCB chain vs directory entry), extracts and formats device/file context, displays return address. |
| 4EBEH | FCB Direct Info Extraction | For FCB with status not equal to 2AH: copies up to 24 bytes from the FCB to the filename buffer for display. |
| 4EE4H | Directory Entry Filename Extraction | Reads directory sector via 4B10H, extracts 8-byte filename and 3-byte extension in NAME/EXT:D format. |
| 4F06H | Display Return Address | Shows "REFERENCED AT X'nnnn'" using the overlay return address at 430CH minus 3, pointing back to the caller's SVC stub. |
Cross-Reference Notes
SYS4 Calls Into SYS0
447BH (display message string on screen), 4797H (read directory sector by info byte), 4B10H (directory entry validation), 4030H (error-already-displayed DOS exit), 4DE7H (hex display routine).
SYS4 Calls Into ROM
0000H (cold restart, via self-modified JP M target for fatal errors).
SYS4 Called By
Any overlay that triggers SVC code 86H (error display) via RST 28H. Error codes are passed in Register A. The calling overlay's return address and FCB pointer provide context for the diagnostic display.
VTOS 4.0 Error Codes Handled by SYS4
| 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 SYSTEM DATA RECORD |
| 07H | ATTEMPTED TO READ LOCKED/DELETED 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 DISK |
| 10H | ILLEGAL LOGICAL FILE NUMBER |
| 11H | DIRECTORY READ ERROR |
| 12H | DIRECTORY WRITE ERROR |
| 13H | ILLEGAL ACCESS ATTEMPTED TO PROTECTED FILE |
| 14H | PROTECTED FILE |
| 15H | READ LOCKED/DELETED DATA RECORD |
| 16H | RECORD |
| 17H | LOCKED/DELETED DATA RECORD |
| 18H | DEVICE NOT IN DIRECTORY |
| 19H | FILE ACCESS DENIED |
| 1AH | FULL OR WRITE PROTECTED DISK |
| 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 | DEVICE IN USE |
| 28H | PROTECTED SYSTEM DEVICE |
| 29H | UNKNOWN ERROR CODE |
Disassembly:
4E00H - Error Display Entry Point
Entry point for the error display overlay. Saves context, extracts and processes the error code from Register A, converts it to decimal for display, builds the error message prefix string, then enters the word lookup and display loop.
The overlay is entered via RST 28H with SVC code 86H. At entry, the stack contains the caller's return address (pointing past the RST 28H instruction) and Register A holds the error code. The EX (SP),HL trick extracts the return address while simultaneously saving HL.
Store the overlay return address (in Register Pair HL) into the operand field of the JP M instruction at 4E96H. This address becomes the cold restart target if the error is fatal (bit 7 set). The JP M instruction at 4E96H tests the sign flag: if the error code has bit 7 set, the jump is taken to 0000H (cold restart); otherwise execution falls through to JP 4030H.
The next four instructions implement a subtle bit manipulation that merges the system flags byte at 430FH with the error code's bit 6. The effect is: if 430FH bit 6 is set AND the error code's bit 6 is clear, the result forces bit 6 on, suppressing the extended context display. If 430FH bit 6 is clear, the error code's own bit 6 is preserved unchanged.
The following block (4E1FH-4E3DH) assembles the "*** ERRCOD=XX, " prefix into the buffer at 4200H. It copies the template string from 4F41H, then overwrites the "XX" placeholder with the actual error code converted to decimal ASCII digits using repeated subtraction.
The following loop at 4E2BH-4E33H converts the binary error code in Register C to two decimal ASCII digits using repeated subtraction of 10. The tens digit is built at (HL) starting from ASCII '0' (2FH + INC = 30H), and the units digit is recovered by adding back 3AH to the negative remainder.
INCrement the tens digit at (HL) by 1. On first entry, this changes 2FH to 30H (ASCII '0'). Each subsequent iteration increments the digit by one, counting how many times 10 can be subtracted from the error code.
Loop End
4E3EH - Error Message Lookup and Word Display Loop
Clamps the error code to the maximum supported value (29H), looks up the descriptor chain via the offset table, then enters a loop that reads each descriptor byte, extracts the word index, copies the word text from the string pool into the message buffer, and continues until the last-word flag (bit 7) is set.
At this point, Register C holds the raw error code number (00H-29H), Register B = 00H (so BC is a clean 16-bit index), and Register Pair DE points to the current position in the message assembly buffer at 4200H (either after the "*** ERRCOD=XX, " prefix, or at the very start if bit 6 suppressed the prefix).
The offset table lookup begins. The table at 4F89H contains one byte per error code (42 entries), where each byte is an offset into page 51xxH where the descriptor chain for that error begins. The code uses BC (with B=00H, C=error code) as a 16-bit index to fetch the correct offset byte.
The main word display loop begins at 4E4BH. For each iteration: (1) read a descriptor byte from the chain at (HL), (2) extract the word index from bits 0-5, (3) look up the word's start address and length from the DEFW table at 4FB1H, (4) copy the word text into the message buffer, (5) append a space separator, (6) check bit 7 of the descriptor to decide whether to continue or exit.
Fetch the current descriptor byte from the chain at (HL) into Register A. This byte encodes: bits 0-5 = word index into the DEFW table, bit 6 = context flag (meaningful only on the last byte), bit 7 = last-word flag (1 = this is the final word in the message).
The overlapping-entry scheme reads 4 consecutive bytes: the first 2 bytes (at HL) are the current word's start address, and the next 2 bytes (at HL+2) are the next word's start address. The word length is computed as (next_start - current_start) via SBC HL,BC.
Loop End - Word Display Loop
4E71H - Error Display Completion and Exit Dispatch
After the word display loop has assembled the complete error message text into the buffer at 4200H, this section appends the closing marker, displays the assembled message on screen, conditionally invokes the extended error context display, restores the caller's registers, and dispatches to either a cold restart (fatal) or the DOS error-already-displayed exit (normal).
At this point the word display loop at 4E4BH-4E6FH has exited with the carry flag set (bit 7 of the last descriptor was set). Register Pair DE points to the current write position in the message assembly buffer at 4200H, past all the copied word text. The processed error code (with merged bit 6) was saved on the stack at 4E12H.
If the SIGN FLAG is set (bit 7 of the error code is set, indicating a fatal error), JUMP to the address stored in the operand field. The operand was written at 4E01H with the overlay return address from the stack. For fatal errors, this effectively performs a warm restart by jumping to 0000H (the cold restart vector). The default operand 0000H shown here is the initial value; at runtime, the address stored by 4E01H may differ, but the JP M only executes when bit 7 is set, meaning the system is performing a fatal error restart regardless. If bit 7 is clear (normal error), the sign flag is clear and execution falls through to the next instruction.
4E9CH - Extended Error Context Display
Displays extended diagnostic information after the error message: the device (drive) number, the filename associated with the error, and the return address of the instruction that triggered the error. The filename source depends on the FCB status: if the FCB contains status 2AH (initialized reference, not formally opened), the device info is taken from IX+06H/IX+07H; otherwise, the FCB's own data is used to extract the filename from either the FCB buffer or a directory entry read.
This routine is called conditionally at 4E8EH only when the error code's bit 6 is clear (extended context enabled). Register Pair HL points past the last descriptor byte in the chain (from the word display loop exit). The last descriptor's bit 6 carries the filename context flag: 0 = filename from directory entry (file is open, use drive/info from IX+06H/+07H), 1 = filename from FCB filespec string.
The directory-entry filename path begins here. The file is identified by drive number at IX+06H and directory info byte at IX+07H. These values are saved into the device template string, then the code checks the FCB status byte to decide between the 2AH (initialized reference) shortcut or the full directory-entry read path.
The 2AH shortcut path: the FCB is in an initialized reference state (not formally opened). The drive/info values are stored into the device display template, then execution jumps to the common display path at 4F2BH.
Store Register Pair BC (C = drive number, B = directory info byte) into the device display template string at 4F57H. These two bytes overwrite the "*XX" placeholder in the "DEVICE=*XX" template at 4F4DH, so the actual drive and directory info values are displayed.
4EBEH - FCB Direct Info Extraction Path
When the last descriptor's bit 6 is set, the filename is taken directly from the FCB buffer. This path first extracts the drive/info bytes from IX+01H/IX+02H (the FCB's config table entry pointer), checks for the special 2AH status (FCB chain indicator), then copies up to 24 bytes from the FCB into the filename display buffer at 4F62H.
The FCB has a status other than 2AH and bit 6 of the descriptor indicated the filename should come from the FCB buffer. The next block copies up to 24 bytes (18H) from the FCB starting at IX+00H into the filename display buffer at 4F62H. The copy terminates early if a 03H (ETX) or 0DH (CR) byte is encountered, as these mark the end of the filespec string.
The copy loop reads each byte from the FCB, checks for terminators, and copies it to the display buffer. The loop runs up to 24 iterations (the length of a VTOS filespec string: "FILENAME/EXT.PASSWORD:D" plus terminator).
Fetch the next byte from the FCB (pointed to by HL) into Register A.
Loop End - FCB Copy Loop
4EE4H - Directory Entry Filename Extraction
When the file is formally open (IX+00H bit 7 set) and the descriptor's bit 6 is clear, the filename is extracted from the directory entry on disk. This routine reads the directory sector using the drive number (C) and info byte (B) from the FCB, then formats the filename as "NAME/EXT:D" in the display buffer at 4F62H.
Entry: Register C = drive number from IX+06H, Register B = directory info byte from IX+07H. These were loaded at 4EA7H-4EAAH before the BIT 7 test dispatched here.
The filename copy loop reads up to 8 characters from the directory entry, skipping any space (20H) characters. Spaces in TRS-80 filenames indicate padding; only the significant characters are copied to produce a compact display.
Fetch the next character from the directory entry filename field at (HL) into Register A.
Loop End - Filename Copy
After the filename, a '/' separator is appended, then the 3-byte extension field is copied (also skipping spaces). Finally, ':D' (colon + drive digit) is appended.
Fetch the next character from the directory entry extension field at (HL) into Register A.
Loop End - Extension Copy
4F1FH - Append Terminator and Display Context Strings
Common exit point for both filename extraction paths (FCB copy and directory entry). Appends the ">" closing bracket and carriage return to the filename buffer, restores IX, then displays the device template, filename, and return address strings in sequence.
Both the FCB copy path (4ED4H-4EE2H) and the directory entry path (4EE4H-4F1EH) converge here. Register Pair DE points to the current position in the filename display buffer at 4F62H, where the formatted filename string needs the ">" and CR terminator appended.
4F41H - Data: Error Code Display Template String
Template string used to build the "*** ERRCOD=XX, " error code prefix. Copied into the message assembly buffer at 4200H by LDIR at 4E26H (12 bytes). The "XX" digits at offsets 4F42H-4F43H are overwritten by the decimal conversion loop at 4E2BH-4E35H.
4F4DH - Data: Device Display Template String
Template string displayed by the extended context routine to show the drive and directory info associated with the error. The two bytes at 4F57H-4F58H are overwritten at runtime by the LD (4F57H),BC instruction at 4EB5H with the actual drive number and directory info byte from the FCB.
3 bytes: asterisk placeholder "*" followed by two "X" placeholder characters at 4F57H-4F58H. At runtime, the LD (4F57H),BC instruction at 4EB5H overwrites the two X bytes with the actual drive number (C) and directory info byte (B) from the FCB. The asterisk at 4F56H remains as a visual separator.
4F5BH - Data: File Display Prefix String
Prefix string displayed before the filename in the extended error context. Displayed by CALL 447BH at 4F2BH. The display routine prints up to the terminator, then continues into the filename buffer at 4F62H which contains the assembled filename string.
4F62H - Data: Filename Display Buffer
16-byte work area filled at runtime with the formatted filename string by either the FCB copy loop (4ED4H) or the directory entry extraction routine (4EE4H). The initial content shown here is the default template that is overwritten during error processing.
4F72H - Data: Return Address Display Prefix String
Prefix string for the return address diagnostic line. Displayed by JP 447BH at 4F3EH as a tail call. The string includes the "REFERENCED AT X'" label followed by the hex address buffer at 4F83H.
4F83H - Data: Return Address Hex Buffer and Suffix
Work area for the hex return address digits plus the closing suffix. The "NNNN" placeholder bytes are overwritten by CALL 4DE7H at 4F38H with the 4-digit hexadecimal representation of the adjusted return address. The suffix "'" + 0DH completes the display line.
4F89H - Data: Error Code Offset Table
Lookup table mapping error codes (00H-29H) to descriptor chain addresses. Each byte is the low byte of an address in page 51xxH where the descriptor chain for that error code begins. The table contains 42 entries (one per supported error code). The word display loop at 4E44H-4E48H uses this table to find the starting descriptor for any given error code.
Each entry is a single byte representing the low byte of an address in the range 513EH-51DDH. For example, entry 00H contains 3EH, meaning the descriptor chain for error code 00H ("NO ERROR") starts at 513EH. Entry 29H contains DBH, meaning error code 29H ("UNKNOWN ERROR CODE") starts at 51DBH.
4FB1H - Data: Word Address DEFW Table
Table of 57 two-byte entries (114 bytes) providing the start address of each word in the string pool. The overlapping-entry lookup scheme at 4E57H-4E60H reads 4 consecutive bytes (the current word's DEFW and the next word's DEFW) and computes the word length by subtraction. Entry 0 (DBD8H) is a sentinel/unused value; entries 1-55 point to actual words; entry 56 (513EH) is the end sentinel that provides the length of word 55.
Each pair of bytes is a little-endian 16-bit address pointing into the word string pool at 5023H-513DH. The word index used in descriptor bytes (bits 0-5) is multiplied by 2 to index into this table. Words 3/4 and 20/21 and 36/37 share the same address, meaning they are aliases for the same word. This occurs because the DEFW table structure requires unique entries for length calculation, but multiple word indices can map to the same string when adjacent entries happen to share a boundary.
5023H - Data: Word String Pool
Contiguous block of 55 ASCII words totaling 283 bytes (5023H-513DH). Words are stored consecutively with no separators or terminators; the DEFW table at 4FB1H defines each word's start address and the word length is computed by subtracting adjacent DEFW entries. Words range from 2 bytes ("NO", "ON", "OF", "IN", "OR") to 14 bytes ("LOCKED/DELETED" and "- CAN'T EXTEND"), with multi-word phrases stored as single dictionary entries where the embedded spaces are part of the word data.
513EH - Data: Error Message Descriptor Chains
Variable-length descriptor chains for all 42 error codes (00H-29H), totaling 160 bytes (513EH-51DDH). Each chain is a sequence of bytes where bits 0-5 encode the word index (1-55), bit 7 is the last-word flag (set on the final byte), and bit 6 on the last byte is the filename context flag (0 = filename from directory entry, 1 = filename from FCB). The chains are packed sequentially with no separators; the offset table at 4F89H identifies the start of each chain.
In the decoded chains below, each byte is shown with its raw hex value, and the resulting word is identified. The last byte in each chain has bit 7 set (values 80H-FFH). Bit 6 on the last byte indicates the context flag: when set (C0H-FFH range after masking), filename comes from the FCB; when clear (80H-BFH range), filename comes from the directory entry.