1. Program Overview
SYS6 is the TRSDOS 1.3 DOS Command Handler overlay for the TRS-80 Model III. It is a demand-loaded overlay residing in the SYSHI (high-system) PSECT, mapped to addresses 5200H–623DH. SYS6 is not resident in memory at all times; it is loaded by SYS0 whenever the user invokes any of the nineteen DOS commands it services.
On entry, Register C holds the order number of the requested command (matched against the TABCOM jump table), and Register Pair HL points to the remainder of the command line supplied by the user. SYS6 dispatches to the appropriate command handler via GETTAB/TABCOM, then returns control to the caller or to the DOS error handler as appropriate.
The nineteen commands handled by SYS6 are: APPEND, ATTRIB, AUTO, BUILD, CLEAR, CLOCK, CREATE, DATE, DIR, DO, DUMP, ERROR, FREE, LIB, LOAD, PAUSE, PROT, RENAME, and TIME. CLOCK, CLOCKN, and CLOCKF are forwarded to ROM routines; LIB lists available SYS1-resident library commands from the TABCOM table. LOAD uses the SYS0 program loader (LDPGM). The DO command installs a replacement keyboard DCB driver in high RAM to replay a .BLD script file as simulated keyboard input.
2. Memory Map
| Address Range | Label | Description |
|---|---|---|
| 5200H–623DH | SYS6 / SYSHI | Entire SYS6 overlay (assembled at ORG 5200H, END 5200H) |
| 5200H | BEGIN | Entry point — command dispatcher |
| 529BH | GETTAB | Table-search routine |
| 52A8H | TABCOM | Command jump table (19 DCF entries + terminator) |
| 52F6H (approx.) | AUTO | AUTO command handler |
| S6BUF3 | S6BUF3 | 64-byte (40H) scratch buffer (DEFS 40H) |
| S6DCB1 | S6DCB1 | Primary file DCB (DEFS DCBSIZ) |
| S6DCB2 | S6DCB2 | Secondary file DCB (immediately follows S6DCB1) |
| S6BUF1 | S6BUF1 | 256-byte sector buffer (ORG aligned to 256-byte boundary) |
| S6BUF2 | S6BUF2 | Second 256-byte sector buffer (S6BUF1 + 256) |
3. Self-Modifying Code Variables and Workspace Locations
SYS6 makes heavy use of self-modifying code — immediate operand bytes within instructions serve as in-place storage for runtime values. These locations are internal to the overlay and are NOT recorded in the global RAM JSON. The table below documents all such locations identified from the source.
| Label | Source Pattern | Description |
|---|---|---|
| AUTDRV | EQU $+1 / LD C,0 | Drive number for AUTO command (self-modifying byte in LD C,n) |
| LPFLAG | EQU $+1 / LD BC,0 | Line-printer flag for DIR and FREE output routing |
| S6DLFN | EQU $+1 / LD B,0 | DIR command: current directory LFN counter |
| S6DDRV | EQU $+1 / LD C,0 | DIR command: current drive number |
| PNTBUF | EQU $+1 / LD HL,0 | DIR command: rolling pointer into hash-code buffer |
| SYS | EQU $+1 / LD DE,0 | DIR command: user's "show SYS files" switch |
| S6INV | EQU $+1 / LD DE,0 | DIR command: user's "show invisible files" switch |
| SAVMTH | EQU $+1 / LD A,0 | DIR: creation month saved for display |
| SAVYR | EQU $+1 / LD A,0 | DIR: creation year saved for display |
| LRL2 | EQU $+1 / LD A,0 | DIR: logical record length saved for record-count calculation |
| S6EOF | EQU $+1 / LD L,0 | DIR: EOF byte saved for record-count calculation |
| DRV3 | EQU $+1 / LD C,0 | PROT command: drive number |
| INPPAS | EQU $+1 / LD DE,0 | PROT command: pointer to entered old password hash |
| PW | EQU $+1 / LD DE,0 | PROT command: new password hash (from SWITCH parse) |
| LOCK | EQU $+1 / LD DE,0 | PROT command: lock password hash |
| PASSW | EQU $+1 / LD DE,0 | PROT command: user-supplied hash to write to directory |
| BUFSAV | EQU $+1 / LD HL,0 | PROT command: rolling pointer through directory buffer |
| STAT | EQU $+1 / LD D,0 | ATTRIB command: attribute OR-mask to apply |
| STAT1 | EQU $+1 / OR 0 | ATTRIB command: protection-level value to OR in |
| ISTAT | EQU $+1 / OR 0 | ATTRIB command: invisible-bit OR-mask |
| ISTAT1 | EQU $+1 / AND 0FFH | ATTRIB command: invisible-bit AND-mask (for clearing) |
| RELO1 / START | EQU $+1 / LD HL,0 | DUMP command: modified start address (self-modifying LD HL,nn) |
| RELO | EQU $+2 / LD IY,0 | DUMP command: relocation base address (self-modifying LD IY,nn) |
| END | EQU $+1 / LD HL,0 | DUMP command: ending address |
| TRA | EQU $+1 / LD HL,0 | DUMP command: transfer address written to CMD file |
| TMPDCB | EQU $+1 / LD DE,0 | DO command: temporary DCB pointer during setup |
| GODCB | EQU $+1 / LD DE,0 | DO command: DCB pointer stored in the GOGO1 keyboard stub |
| LOWRAM | EQU $+1 / LD DE,0 | CLEAR command: lower address of range to clear |
| HIGRAM | EQU $+1 / LD HL,0 | CLEAR command: upper address of range to clear |
| NEWMEM | EQU $+1 / LD HL,0 | CLEAR command: new MEMEND value if MEM option given |
| S6LRL | EQU $+1 / LD BC,0 | CREATE command: parsed LRL value |
| NRECS | EQU $+1 / LD BC,0 | CREATE command: parsed record count |
| HEXADD | (label on DEFM '0000') | CLEAR/DUMP: 4-byte ASCII hex address in "MEMORY FAULT AT X" message |
| DCOUNT | DEFB 0 | DUMP command: residual byte count for partial sector |
| NAMFIL | DEFW 0 × 4 / DEFB 0 | DUMP command: 9-byte filename scratch area |
| BIN | DEFB 0 / DEFW 0 | DATE/TIME conversion: 3-byte binary result buffer |
| NAME1 | DEFS 10H | RENAME command: 16-byte buffer for old filename |
| NAME2 | DEFS 10H | RENAME command: 16-byte buffer for new filename |
| BUFPNT | DEFW 0 | Miscellaneous buffer pointer (reserved word) |
| COUNT | DEFB 0 | FREE command: current track counter |
| CDIRET | DEFB 0 | FREE command: directory track number (from TRKGET) |
| DPRT1/DPRT2/DPRT3 | DEFM '00'/'00'/'00~' | DATE/TIME display: three two-digit ASCII fields |
| DELIM1/DELIM2 | DEFM ':' | DATE/TIME display: delimiter characters (patched to '/' for date) |
| GOSAVE | (referenced in DO) | DO command: saved original keyboard DCB driver address |
| DOFLAG | (referenced in DO/CLEAR/PAUSE) | DO active flag (FFH = active, 0 = inactive) — in external RAM |
4. Major Routine List
| Label | Description | Entry / Exit Conditions |
|---|---|---|
| BEGIN | Main entry point — command dispatcher. Saves command line pointer, clears FLAG1/SCAFLG/BASICG, calls GETTAB to look up command in TABCOM, then returns into the command via EX (SP),HL / RET. | Entry: C = command order number, HL → command line. Exit: falls through to command, or jumps to error handler. |
| BEGIN1 | Continuation for "command not found" path — pops HL and falls into ERR2. | Entry: called from BEGIN on C flag set by GETTAB. |
| ERR1 | FILE ALREADY EXISTS error (28H / EFAT). | Loads A = 28H, jumps to ERRX → ERR (SYS0). |
| ERR2 | ILLEGAL PARAMETER error (0BH / ERICP). | Loads A = 0BH, jumps to ERRX. |
| ERR3 | MISSING PARAMETER error (0AH / ERCPM). | Loads A = 0AH, jumps to ERRX. |
| ERR4 | ACCESS DENIED error (19H / EFAD). | Loads A = 19H, jumps to ERRX. |
| ERR5 | Displays "End less than Start" message. | HL → MES5, jumps to ERRY → PRINT. |
| ERR6 | Displays "Start less than 6000~" message. | HL → MES6, jumps to ERRY. |
| ERR7 | Displays "Memory fault at X nnnn" message. | HL → BADRAM, jumps to ERRY. Address filled in by HEXS into HEXADD. |
| ERR9 | DISK DRIVE NOT IN SYSTEM error (02H / EDNS). | Loads A = 02H, jumps to ERRX. |
| ERR13 | Displays "LRL of Files Do NOT Match~" message. | HL → BADLRL, jumps to ERRY. |
| ERRX | Common error jump to SYS0 ERR routine. | Entry: A = error code. JP ERR (SYS0 at 6132H). |
| ERRY | Common message print and return. | Entry: HL → message. JP PRINT (SYS0). |
| GETTAB | Searches TABCOM for a matching command order number. Each entry is a 3-byte DCF record: [order-byte][addr-low][addr-high]. Scans sequentially until match or 0 terminator. | Entry: C = order number, HL → TABCOM. Exit: NZ+NC with HL = command address on match; SCF (carry set) if not found. |
| TABCOM | Jump table — 19 DCF macro entries, each 3 bytes: command order code byte + 16-bit handler address. Terminated by DEFB 0. | Read-only data; searched by GETTAB. |
| AUTO | AUTO command — reads or writes the auto-start command in the disk GAT sector. Optionally accepts a drive specifier and a command name. Pads command to 32 bytes and writes via WRGAT. | Entry: HL → command line. Uses RDGAT/WRGAT (SYS0). Displays "Auto = <command>" or "Auto = ~" if cleared. |
| S6DATE | DATE command — displays current date or updates it. Parses MM/DD/YY via CONV1/DECBIN. Validates ranges. Stores into YEAR/DTSAVE (SYS0 RAM). | Entry: HL → command line, C = '/'. Exit: RET or JP ERR2. |
| S6TIME | TIME command — displays current time or updates it. Parses HH:MM:SS via CONV1/DECBIN. Validates ranges. Stores into SECOND (SYS0 RAM). | Entry: HL → command line, C = ':'. Exit: RET or JP ERR2. |
| CLOCK | CLOCK command — calls SYS1 CHKFLG to determine ON/OFF, then dispatches to ROM CLOCKF or CLOCKN. | Entry: HL → command line. Forwards to ROM clock routines. |
| CONV1 | Decimal-to-binary conversion for 3-field values (date and time). Calls DECBIN twice per field, stores result into BIN+2 downward. | Entry: HL → ASCII string, C = delimiter, DE → BIN+2. Exit: Z on success, NZ on syntax error. |
| DECBIN | Converts two ASCII decimal digits at (HL) to a binary value. | Entry: HL → digit string. Exit: A = value (0–99), Z on success, NZ on error. |
| DECCHK | Reads one character from (HL), subtracts 30H. Returns NC if not a decimal digit. | Entry: HL → character. Exit: A = digit value; carry clear if not numeric. |
| LIB | LIB command — iterates through TABCOM and prints the first 6 characters of each command name, 8 per row. | Entry: none. Calls DSP (SYS0) for each character. |
| S6PROT | PROT command — changes the master disk password. Reads old password via MASINP/RDGAT, verifies it matches, prompts for new password, writes via WRGAT. Optionally locks all unprotected files. | Entry: HL → command line. Uses RDGAT, WRGAT, SREAD, XWRITE (SYS0). |
| MASINP | Reads up to 8 characters of keyboard input into BUFF1, then calls TRAP to compute the trap-door hash. | Exit: HL = computed hash code. |
| TRAP / SETTDC | Computes a two-byte trap-door hash code from an 8-character password string. Used for master password and file access/update passwords. | Entry: DE → password string, B = length. Exit: HL = hash. Returns NZ if hash would be zero (increments L). |
| RENAME | RENAME command — checks protection level (must be < write = level 3), moves names to DCBs, verifies old file exists and new name does not, updates directory record and hash table. | Entry: HL → command line. Calls GETSYN, OPEN, RDDIR, WRDIR, RDHIT, WRHIT, HASH, DSPNAM. |
| MOVNAM | Copies a filename from a DCB (DE) into a buffer (HL) until a control character is encountered, then appends a 03H terminator. | Entry: HL → dest, DE → DCB name field. |
| DSPNAM | Displays "oldname ---> newname CR" using PRINT (SYS0). | Uses NAME1, NAMARO, NAME2 data strings. |
| APPEND | APPEND command — opens two files, verifies their LRLs match, positions to EOF of the destination, copies all bytes from source to destination. | Entry: HL → command line. Calls GETSYN, SYNTAX, OPEN, OPENIT, POSEOF, GET, PUT, CLOSE. |
| DUMP | DUMP command — creates a CMD-format load file containing a memory range. Accepts START, END, TRA, RELO parameters. Writes load-format records (type 01) followed by an end record (type 02). Includes a remark record (type 05) with filename and date. | Entry: HL → command line. Uses SWITCH, GETSYN, INIT, CLOSE, PUTIT, PUT (SYS0). |
| PUTIT | Increments the DCOUNT residual count and writes one byte to disk via PUT (SYS0). | Entry: A = byte to write. |
| CREATE | CREATE command — initialises a new file with optional LRL and REC count parameters. Optionally pre-extends the file to the specified number of records. | Entry: HL → command line. Calls GETSYN, SWITCH, CONVHX, INIT, POSN, WRITE, CLOSE. |
| CONVHX | Converts a BCD value (from SWITCH parsing) to a 16-bit binary integer using a POWR10 lookup table. | Entry: HL = BCD value. Exit: HL = binary value. |
| CLEAR | CLEAR command — with no parameters, tests and clears RAM from S6DCB1 to MEMEND; with START/END parameters, clears a specified range; with MEM parameter, resets MEMEND. Detects and reports bad RAM via ERR7. | Entry: HL → command line. Calls SWITCH (SYS0), CLS (SYS0). |
| BUILD | BUILD command — creates a .BLD script file by accepting keyboard input line by line (up to 63 chars per line) until BREAK is pressed, then writes a terminating FFH byte and closes the file. | Entry: HL → command line. Uses GETSYN, PUTEXT, INITIT, KEYN (SYS0), PUT, CLOSE. |
| DO | DO command — sets up a .BLD file replay. Allocates high-RAM space, copies the GOGO1 keyboard stub there, redirects the keyboard DCB to it. Each subsequent keyboard read pulls one character from the DO file. Restores the original keyboard DCB on EOF. | Entry: HL → command line. Modifies KDCB+1 (SYS0 keyboard DCB), MEMEND, MEM1. |
| GOGO1–GOGO4 | The DO keyboard stub — a small self-contained routine copied to high RAM. On each keyboard call it reads one byte from the DO file; on EOF it restores the original keyboard driver and clears DOFLAG. | Relocated to high RAM by DO. Calls GET (SYS0) via the relocated DCB pointer (GODCB). |
| SPAUSE | PAUSE command — prints "Press <ENTER> to continue~" then waits for ENTER. If the DO file is active it uses GETCR (keyboard matrix scan); otherwise it uses KEYN (SYS0). | Entry: HL → command line. |
| GETCR | Waits for the ENTER key by scanning memory location 3840H (keyboard matrix row). | Exit: Z set when ENTER detected. |
| EZZOR | ERROR command — converts a 1- or 2-digit decimal error number from the command line, ORs in 0C0H (detail bits), then falls into the ERR handler to display the corresponding error message. | Entry: HL → command line after 'ERROR '. Uses DECCHK. |
| PDATA | Outputs one character to the display via DSP (SYS0) or to the line printer via PRT (SYS0), depending on the LPFLAG self-modifying field. | Entry: A = character to output. |
| PRT2 | Outputs a full message string via LPRINT (if LPFLAG set) and PRINT (SYS0). | Entry: HL → message string. |
| LPRINT | Prints a 03H-terminated string to the line printer via PRT (SYS0). | Entry: HL → string. Stops on 03H or after 0DH. |
| INKEY | Checks for BREAK (via BRKCHK / SYS0) then calls GETKBD. Supports hold-mode via HLDPAT. | Exit: A = character (NZ), or A=0 (Z) if no key pressed. |
| GETKBD | Keyboard scan — if DO is not active uses KBD (SYS0); if DO is active calls the keyboard DCB (KEYBDZ) via IX=KDCB. | Exit: A = character or 0. |
| HEXS | Converts one byte to two hex ASCII digits and stores them at (HL), advancing HL by 2. Used to fill HEXADD in the "MEMORY FAULT" message. | Entry: A = byte. Entry at HEXS1 for low nibble only. |
| HEXAAS | Converts a binary value to two decimal ASCII digits and stores them at (HL), advancing HL by 2. Used in date/time display. | Entry: A = value. |
| BINDEC | Outputs a 16-bit value as a right-justified decimal number preceded by two spaces. Calls ROM routines at 0A9AH / 1034H / 0FD9H and then prints digits from 4131H buffer. | Entry: HL = value. Calls PDATA for each character. |
| MSGPRT / MSGPR0 | Formats date or time for display. Loads the delimiter into DELIM1/DELIM2 and fills DPRT1/DPRT2/DPRT3 with two-digit hex pairs from the date or time RAM variables, then calls PRINT. | Entry: C = delimiter ('/' for date, ':' for time). IX → HOUR or MONTH (SYS0 RAM). |
| REDIT / OPENIT / INITIT / GETSYN / GETDCB / GETDC1 | Convenience wrappers that set up S6DCB1/S6BUF1 and call the SYS0 READ, OPEN, INIT, SYNTAX routines. | Standardised DCB/buffer setup for all file operations in this overlay. |
| DIR | DIR command — reads the GAT, HIT (hash index table), and directory records for a selected drive; displays filename, attributes, LRL, record count, granule count, extent count, EOF byte, and creation date for each visible file; then displays free-granule count. | Entry: HL → command line. Supports :n (drive), SYS, INV, PRT options via SWITCH. Calls RDGAT, RDHIT, RDDIR, BRKCHK, INKEY, BINDEC, PDATA, PRT2, CLS (all SYS0). |
| S6FREE | FREE command — reads the GAT for a selected drive and displays a graphical free-space map showing granule allocation status per track. Supports :n (drive) and PRT option. | Entry: HL → command line. Calls RDGAT, TRKGET, HEXCOV, PDATA, PRT2, CLS (SYS0). |
| HEXCOV | Converts a binary value to two decimal ASCII digits (BCD-style using repeated subtraction) and stores at (HL). Used to fill track numbers in the FREE map display. | Entry: A = value, HL → destination. Advances HL by 2. |
| SETTDC (alias TRAP) | Trap-door hash computation routine. Referenced in ATTRIB as SETTDC for file access/update password hashing. | Same routine as TRAP above. |
| LOAD | LOAD command — calls GETSYN to parse the filename, sets IY to SYS1IN (return address), pushes IY, and jumps to LDPGM (SYS0 program loader). | Entry: HL → command line. Returns to SYS1IN after program loads and runs. |
| ATTRIB | ATTRIB command — modifies protection attributes, visibility (I/N), access password, and update password of a file. Reads current directory record, patches attribute byte and password hash fields, writes directory back. | Entry: HL → command line. Calls GETSYN, OPEN, RDDIR, WRDIR. Uses SETTDC for password hashing. |
| LEGAL / LEAL | Character-class tests. LEGAL: accepts 0–9 and A–F (hex digits). LEAL: accepts 0–9 and A–Z (alphanumeric). Called by DUMP's filename-copy loop. | Entry: A = character. Exit: carry clear if character is in the accepted set. |
5. Jump Table Mapping (TABCOM)
TABCOM is the command dispatch table searched by GETTAB. Each entry is produced by the DCF macro and consists of a 1-byte command order code followed by a 2-byte little-endian handler address (3 bytes total per entry). The table is terminated by a DEFB 0.
| Order Code | Label | Command | Handler Label |
|---|---|---|---|
| 01H (APP) | APP | APPEND | APPEND |
| 02H (ATT) | ATT | ATTRIB | ATTRIB |
| 03H (AUT) | AUT | AUTO | AUTO |
| 05H (BULD) | BULD | BUILD | BUILD |
| 06H (CLER) | CLER | CLEAR | CLEAR |
| 07H (CLCK) | CLCK | CLOCK | CLOCK |
| 0AH (CRET) | CRET | CREATE | CREATE |
| 0BH (DTE) | DTE | DATE | S6DATE |
| 0DH (DIRE) | DIRE | DIR | DIR |
| 0EH (DODO) | DODO | DO | DO |
| 10H (DMP) | DMP | DUMP | DUMP |
| 11H (RERR) | RERR | ERROR | EZZOR |
| 14H (FREEE) | FREEE | FREE | S6FREE |
| 17H (LIBB) | LIBB | LIB | LIB |
| 19H (LOADD) | LOADD | LOAD | LOAD |
| 1CH (PAU) | PAU | PAUSE | SPAUSE |
| 1DH (PROTT) | PROTT | PROT | S6PROT |
| 20H (RENME) | RENME | RENAME | RENAME |
| 24H (TTIME) | TTIME | TIME | S6TIME |
| 00H | — | (end of table) | DEFB 0 terminator |
6. Cross-References and Analysis Notes
ROM dependency: BINDEC calls three Model III ROM routines directly by address (0A9AH, 1034H, 0FD9H) and reads an ASCII result string from ROM address 4131H. These addresses are ROM-version specific and are not resolved through SYS0 vectors.
Keyboard matrix scan: GETCR reads address 3840H directly for the ENTER key row. This is a hardware-level dependency on the Model III keyboard matrix layout.
DO command high-RAM placement: The GOGO1–GOGO4 stub is copied to an address derived from MEMEND at runtime. The exact destination address is not fixed at assembly time; GODCB and GOSAVE within the stub are patched in place after the copy. The stub length is computed as GOGO4−GOGO1 in the source.
Self-modifying code note: Several locations (RELO1/START, END, TRA, RELO) use the EQU $+n pattern targeting the operand of multi-byte LD instructions. The disassembler mis-decodes some of these — most notably at the RELO/START/END cluster — rendering .BYTE 32H in place of LD (nnnn),A. The source is authoritative for byte counts and instruction boundaries in these regions.
LEAL vs. LEGAL: SYS6 defines two character-class predicates: LEGAL (hex digits 0–9, A–F) and LEAL (alphanumeric 0–9, A–Z). These are used in DUMP's filename copying loop. The near-identical names appear to be a deliberate abbreviation convention in the original source.
LIBCOM dependency: The LIB command iterates TABCOM treating each entry as a 3-byte record where the first 6 characters are the printable command name. However, the source label is LIBCOM, not TABCOM. Examination shows LIB starts at LIBCOM which in context resolves to the same table — the two labels may be aliases or LIBCOM may refer to a separate string-only table not present in the visible source. This warrants further investigation against the binary.