TRS-80 DOS - VTOS v4.0.2 for the Model I - SYS6/SYS Disassembled
Page Customization
Page Index
SYS6/SYS - Library Command Overlay
Disassembly
Other Navigation
Introduction:
VTOS 4.0 SYS6/SYS Disassembly - Library Command Overlay (Model I)
SYS6/SYS is the largest single overlay in VTOS 4.0, occupying addresses 5200H-5B19H (approximately 2330 bytes) when loaded into the overlay area. It is invoked by the resident command interpreter (SYS1) whenever the user types a library command such as COPY, DIR, ATTRIB, DUMP, FREE, PURGE, RENAME, DEVICE, LIB, CLOCK, BUILD, AUTO, BACKUP, VERIFY, TAPE, DEBUG, DO, FILTER, FORMS, KILL, KSM, LINK, LIST, LSCROLL, MEMORY, PATCH, or SYSTEM. The dispatch mechanism uses RST 28H with SVC code 88H, which causes the SYS0 overlay loader to read SYS6/SYS from disk into the overlay area at 5200H-5BFFH.
Unlike a conventional load module, SYS6/SYS is a Partitioned Data Set (PDS) - a file format unique to LDOS-family operating systems (including its ancestor VTOS 4.0). A PDS consists of a directory of named members, each member being an independent load module with its own code and transfer address. The directory is indexed by ISAM (Indexed Sequential Access Method) entry numbers - the ISAM values in the table below. When SYS0 dispatches to SYS6, it passes the ISAM entry number corresponding to the requested command, the loader then searches the PDS ISAM directory for a matching entry, loads that member's code blocks into memory, and transfers control to that member's entry address.
This means that each command in SYS6 is effectively a separate program sharing the same overlay file. Several commands share physical PDS members (their code is identical but the entry address within the member differs). The ISAM directory records both the entry point and a pointer (NRN/RBO - Next Record Number / Relative Byte Offset) to the first byte of the member's data on disk.
SYS6 contains 37 ISAM directory entries mapping to 30 distinct physical PDS members. The table below documents every ISAM entry. The disassembly that follows is organized by ISAM entry number (the ISAM column), so that each entry can be found directly via the page index.
PDS/ISAM Architecture
Because SYS6 is a PDS, the VTOS system loader does not simply load the entire file at once. When a library command is requested, SYS1 determines the ISAM entry number and passes it to SYS0. SYS0 reads only the PDS member associated with that ISAM entry. This means that at any given moment only one command's code is actually resident in the 5200H-5BFFH overlay area. The NRN (Next Record Number) and RBO (Relative Byte Offset) in the ISAM directory entry are a triad pointer locating the first byte of that member's load blocks on disk.
Members that share the same NRN/RBO are physically the same member on disk but have different transfer addresses (entry points). When such a member is loaded, the ISAM directory entry provides the correct transfer address to jump to, so the same binary code serves multiple commands by entering at different offsets.
Multiple ISAM numbers frequently share the same PDS member when commands are closely related or provide alternate syntax or entry points (for example, DIR/ATTRIB, DUMP/FREE, LIST Model I/III, SYSTEM, AUTO, BACKUP, and TAPE all use aliasing).
How the SYS6 File Is Processed into ISAM Entries and PDS Members
SYS6/SYS is a Partitioned Data Set (PDS). The raw sys6.hex is not a flat binary — it is a structured container that begins with identification records, followed by a directory of commands (ISAM entries), and then the actual code for each command (the PDS members).
Here are the very first bytes from sys6.hex (shown exactly as they appear, grouped for readability):
05 06 53 59 53 36 20 20
1F 91 0D 2A 20 2A 20 2A 20 4E 20 4F 20 54 20 49 20 43 20 45 20 2A 20 2A 20 2A 0D ...
08 06 11 00 52 01 00 C9
08 06 15 00 52 02 00 1D
08 06 16 2D 52 02 00 1D
08 06 13 00 52 02 00 B2 ...
Step-by-Step Breakdown of the First Records
- Type 05H – Filename Header
05 06 53 59 53 36 20 20
- 05 = header record type
- 06 = length of data
- 53 59 53 36 20 20 = ASCII "SYS6 "
- Type 1FH – Copyright Block
1F 91 0D 2A 20 2A ...
This is a long informational block containing the copyright notice. The loader ignores this for normal execution.* * * N O T I C E * * *
* PROPRIETARY PROGRAM *
* COPYRIGHT (C) 1980 *
* BY RANDOLPH COOK *
* ALL RIGHTS RESERVED *
* * * N O T I C E * * * -
ISAM Directory Begins (Type 08H records)
After the copyright and some padding, the real directory starts. Here is a consecutive snippet containing the first few ISAM entries:08 06 11 00 52 01 00 C9 ← ISAM 11H (AUTO)
08 06 15 00 52 02 00 1D ← ISAM 15H (DATE)
08 06 16 2D 52 02 00 1D ← ISAM 16H (TIME)
08 06 13 00 52 02 00 B2 ← ISAM 13H (ALLOC)Take this real record from later in the directory:
08 06 31 00 52 1D 00 FD- 08 = ISAM directory entry type
- 06 = 6 data bytes follow
- 31 = ISAM number = 31H (APPEND main)
- 00 52 = Transfer / entry address = 5200H
-
1D 00 FD = Triad pointer = NRN 1DH 00H / RBO FDH — this tells the loader exactly where on disk (or in the hex file) the code for this member begins.
Since the TRS-80 uses 256 byte sectors, this math is easily shortcut to just the first byte and the last ... so this would be byte 1DFDH
Specifically, the formula is File Offset = (NRN × 256) + RBO where NRN (Next Record Number) is the 2 byte logical record (sector group) in the file where the PDS member starts in lower-endian format (for a TRS-80 generally, this will always be nnH) and a 1 byte RBO (Relative Byte Offset) which is the byte offset within that record where the first byte of the member's code is located.
The loader seeks to that byte offset in the file, then begins reading the load records (type 01H) that contain the actual Z80 code. It loads those bytes into memory starting at the address specified inside the load blocks (usually 5200H), and finally jumps to the transfer address from the ISAM record.
This structure allows VTOS to load only the needed command into the overlay area at 5200H+, keeping memory usage efficient, and By including a transfer address in this code, you can use the identical routine at the Triad Pointer for different purposes, because it is loaded elsewhere.
Examples:- AUTO triad
01 00 C9→ NRN=01 00 = 0001H (= go to record 1 of the SYS6 file), RBO=C9H (= 201 bytes to skip) → offset = (1 × 256) + 201 = 457 decimal (01C9H) - DATE/TIME triad
02 00 1D→ NRN=02 00 = 0002H (= go to record 2 of the SYS6 file), RBO=1DH (= 29 bytes to skip) → offset = (2 × 256) + 29 = 541 decimal (021DH) as the exact byte to start
- AUTO triad
Utility
Tim Mann has developed, as part of his XTRS emulator, some routines which can assist in processing ISAM and PDS files:
- LOADCMD - C routines that help in parsing TRS-80 cmd files including ISAM and PDS file. They can be found at https://github.com/TimothyPMann/xtrs/blob/master/load_cmd.c and https://github.com/TimothyPMann/xtrs/blob/master/load_cmd.h.
- CMDDUMP - C routine that displays information about TRS-80 DOS CMD (binary executable) files. It can be found at https://github.com/TimothyPMann/xtrs/blob/master/cmddump.c and https://github.com/TimothyPMann/xtrs/blob/master/cmddump.man.
Full ISAM/PDS Breakdown
| Offset Range | Library Command | Hex Bytes | Explanation |
|---|---|---|---|
| 009B-00A0 | AUTO | 08 06 11 00 52 01 00 C9 |
|
| 00A1-00A6 | DATE | 08 06 15 00 52 02 00 1D |
|
| 00A7-00AC | TIME | 08 06 16 2D 52 02 00 1D |
|
| 00AD-00B2 | ALLOC | 08 06 13 00 52 02 00 B2 |
|
| 00B3-00B8 | KILL | 08 06 18 00 52 03 00 71 |
|
| 00B9-00BE | LIB | 08 06 19 00 52 03 00 B1 |
|
| 00BF-00C4 | DEBUG | 08 06 14 00 52 03 00 E8 |
|
| 00C5-00CA | CLOCK | 08 06 17 41 52 03 00 E8 |
|
| 00CB-00D0 | TRACE | 08 06 1A 57 52 03 00 E8 |
|
| 00D1-00D6 | VERIFY | 08 06 1B 67 52 |
|
| 00D7-00DC | O! (Copyright Notice) | 08 06 1D 00 52 04 00 E2 |
|
| 00DD-00E2 | MEMORY | 08 06 1E 00 52 05 00 35 |
|
| 00E3-00E8 | SYSTEM's SYSGEN Command | 08 06 1C 00 52 05 00 C3 |
|
| 00E9-00EE | DIR | 08 06 21 00 52 08 00 27 |
|
| 00EF-00F4 | FREE | 08 06 22 00 52 0D 00 43 |
|
| 00F5-00FA | CHAIN | 08 06 91 00 52 0F 00 20 |
|
| 00FB-0100 | SYSTEM | 08 06 A1 00 52 14 00 B9 |
|
| 0101-0106 | SPOOL | 08 06 A2 08 52 19 00 D7 |
|
| 0107-010C | APPEND | 08 06 31 00 52 1D 00 FD |
|
| 010D-0112 | COPY | 08 06 32 70 52 1D 00 FD |
|
| 0113-0118 | BUILD | 08 06 33 00 52 20 00 A9 |
|
| 0119-011E | LIST | 08 06 41 00 52 21 00 7C |
|
| 011F-0124 | 08 06 42 06 52 21 00 7C |
| |
| 0125-012A | ATTRIB | 08 06 51 00 52 24 00 A7 |
|
| 012B-0130 | PROT | 08 06 52 00 52 26 00 8D |
|
| 0131-0136 | RENAME | 08 06 53 00 52 29 00 BB |
|
| 0137-013C | DEVICE | 08 06 61 00 52 2B 00 60 |
|
| 013D-0142 | LINK | 08 06 62 00 52 2D 00 C7 |
|
| 0143-0148 | RESET | 08 06 63 00 52 2E 00 D6 |
|
| 0149-014E | ROUTE | 08 06 64 00 52 30 00 CA |
|
| 014F-0154 | SET | 08 06 65 01 5A 32 00 6C |
|
| 0155-015A | FILTER | 08 06 66 00 5A 32 00 6C |
|
| 015B-0160 | DUMP | 08 06 71 00 52 33 00 91 |
|
| 0161-0166 | PURGE | 08 06 72 00 52 35 00 26 |
|
| 0167-016C | XFER | 08 06 73 00 52 37 00 81 |
|
| 016D-0172 | LOAD | 08 06 81 00 52 3A 00 5E |
|
| 0173-0178 | RUN | 08 06 82 0D 52 3A 00 5E |
|
| Offset Range | Hex Bytes | Explanation |
|---|---|---|
| 01C3-01C5 | 01 00 04 |
|
| 01C6-01C7 | 01 00 |
|
| 01C8-01C9 | 04 01 |
|
| 01CA | 00 |
|
ISAM 11H - AUTO
The AUTO command will cause the specified command line to automatically be executed every time the system is loaded due to a power-on or RESET unless the ENTER key is held down during the load. The presence of an asterisk will cause VTOS 4.0 to disable the BREAK key.
ISAM A2H transfers here at 5208H, bypassing the source-disk setup at 5200H-5205H. Load Register D with 42H, forming the high byte of the sector buffer base address 4200H. Together with Register E loaded at 520AH, DE will point to the sector buffer at 4200H where the directory sector has been (or will be) read.
Source: DE = 42E0H (sector buffer, first directory entry area, within the 4200H buffer)
Destination: HL = 4318H (DOS command input buffer, 63 bytes total at 4318H-4357H)
Count: BC = 0020H (32 bytes)
After the LDIR, HL = 4338H and DE = 4300H. The 32 bytes have been written to the command buffer. The routine now proceeds to verify the write by reading the sector back and comparing it against what was just copied.
Error Path
Execution reaches 521CH only if either the directory I/O setup at 5202H failed (JP NZ at 5205H) or the write-verify at 5234H failed (JP NZ at 5216H). The error code is already in Register A at this point.
Subroutine: Read Sector into Buffer (5221H)
This local subroutine is called from 5202H to read the directory sector for the source disk into the 256-byte buffer at 4200H. It sets up directory I/O via 4B65H, then calls 4B45H to perform the actual sector read. Returns Z on success, NZ with error code in A on failure.
Subroutine: Write and Verify Sector (5234H)
This local subroutine is called from 5213H to write the sector buffer at 4200H back to the disk and optionally read it back to verify correctness. It mirrors the structure of the read subroutine at 5221H but uses 4768H (write sector) and 4772H (write-verify) instead of 4B45H. Returns Z on success, NZ with error code in A on failure.
If A = 06H: Z flag set, CP result = equal, indicating a verify fault has been detected.
If A != 06H: NZ flag set from the compare, but the subroutine continues to the shared exit below.
Shared Exit for Write-Verify Subroutine (5248H)
Both the write-failed path (JR NZ from 5241H) and the normal exit flow reach 5248H. On the error path, Register A already holds the error code from 4768H or 4772H. On the success path, A holds the FDC status and CP at 5246H has set the flags. The subroutine restores HL and DE and returns to the caller.
ISAM 15H (DATE) and 16H (TIME) - Offset 021D
5200H - DATE Command (ISAM 15H)
Main DATE command entry point. Parses a user-supplied date string in the format mm/dd/yy using / (2FH) as the field separator. Stores the raw parsed values to the system date variables at 4044H-4046H, then encodes a packed date representation into the directory date attribute bytes at 4306H-4307H for use by SYS3 directory date-stamping. Valid year range is 1980-2011; years 1980-1999 are entered as 80-99, years 2000-2011 as 00-11. Exits via JP 402DH (DOS READY) on success, or displays "BAD FORMAT" and exits via JP 4030H on parse failure.
Copy 3 bytes from the parser work buffer (HL=524CH) to the system date variables (DE=4044H), incrementing both pointers after each byte. This transfers: 524CH to 4044H, 524DH to 4045H, 524EH to 4046H. After the copy, HL=524FH and DE=4047H.
Date Encoding
The following code reads the three date components back from 4044H-4046H and packs them into two attribute bytes at 4306H and 4307H. These packed bytes are used by SYS3's directory date-stamping logic at 4E6BH-4E82H. The first byte (from 4044H) is adjusted by subtracting 50H, the second byte (from 4045H) is rotated left 3 times (multiplied by 8) and ORed with the adjusted first byte, and the third byte (from 4046H) is ORed with 50H.
522DH - TIME Command (ISAM 16H)
Alternate entry point for the TIME command. Parses a user-supplied time string in the format hh:mm:ss using : (3AH) as the field separator. Stores the parsed values directly to the system clock variables at 4041H-4043H (seconds, minutes, hours). The manual notes that "ss" defaults to 00 if omitted; however, this entry point parses all three fields (the default handling is performed by the caller before dispatching here). The CLOCK=ON|OFF functionality is handled separately by the CLOCK command (ISAM 22H). Exits via JP 402DH on success or displays "BAD FORMAT" on failure.
Copy 3 bytes from the parser work buffer (HL=524CH) to the system clock variables (DE=4041H), incrementing both pointers. This transfers: 524CH (seconds) to 4041H, 524DH (minutes) to 4042H, 524EH (hours) to 4043H. The system clock ISR in SYS0 will use these new values immediately.
5243H - "BAD FORMAT" Error Handler
Shared error handler for both DATE and TIME commands. Displays the "BAD FORMAT" error message and exits to the error-already-displayed handler at 4030H.
524CH - Parser Work BufferString
Three-byte work buffer used by the decimal parser at 525AH to store parsed numeric values in reverse order
3-byte buffer for storing parsed decimal values. The parser at 525AH stores values in reverse order starting from 524EH and decrementing: 524EH receives the first parsed field, 524DH the second, 524CH the third. Both DATE and TIME use LDIR from 524CH to copy these values to their respective system variable areas (4044H for date, 4041H for time). Initial contents are overwritten at runtime.
524CH - Error Message String
"BAD FORMAT" error message string at 524FH.
ASCII string "BAD FORMAT" followed by a carriage return (0DH). Displayed by the error handler at 5243H via CALL 447BH when the user enters an invalid date or time string.
525AH - Three-Field Decimal Parser
Shared subroutine that parses up to three decimal numbers separated by a configurable separator character. Used by both the DATE command (separator /) and the TIME command (separator :). Each field is parsed as a 1-2 digit decimal number by the subroutine at 526FH. Values are stored in reverse order into the 3-byte work buffer at 524CH-524EH.
Loop Start
Main parse loop. For each iteration: save DE, parse one decimal number via CALL 526FH, restore DE, store the result, decrement DE, decrement the field counter, and check for the separator character before looping.
Loop End
526FH - Two-Digit Decimal Number Parser
Parses a 1-2 digit decimal number from the input string pointed to by HL. Uses the multiply-by-10 technique (RLCA/RLCA/ADD/RLCA = x2, x4, x5, x10) for the tens digit, then adds the units digit. Returns the binary value in Register A with Z FLAG set on success. This is the same algorithm documented in Section 7O of the continuation file.
Tens Digit Multiply-by-10
The first digit becomes the tens value. The following sequence multiplies it by 10 using shifts and addition: RLCA (x2), RLCA (x4), ADD original (x5), RLCA (x10). This avoids a costly multiply instruction.
5286H - Single ASCII Digit Validator
Reads one character from the input string pointed to by HL, advances HL, converts from ASCII to binary by subtracting 30H, and tests whether the result is a valid decimal digit (0-9). Returns with CARRY FLAG set if valid, NO CARRY FLAG if not a digit.
ISAM 13H - ALLOC - Offset 02B2
Allocates disk space based on the optional SIZE parameter. The routine parses the filename, allocates the disk space, creates a directory entry, but does not actually use the disk space. To do this, it sets up the file but marks it as killed setting bit 7 of the directory flags byte to mark the file. Requires a filespec on the command line; displays "FILE SPEC REQUIRED" if none is provided, or "PARAMETER ERROR" if the file cannot be opened. Uses two FCB areas: the primary FCB at 52A3H receives the parsed filespec, and the secondary FCB/template at 52C3H is passed to the open file utility.
5200H - Entry for ALLOC
5200LD DE,52A3H 11 A3 52Point Register Pair DE to 52A3H, the primary FCB (File Control Block) work area. This 32-byte area (52A3H-52C2H) will receive the parsed filespec from the command line.
5203GOSUB to the SYS0 routine at 441CH to extract the filespec from the command line into the FCB at DE (52A3H). On return: Z FLAG set = filespec parsed successfully; NZ FLAG set = no filespec found on the command line.
5206If the NZ FLAG (Not Zero) has been set because no filespec was provided on the command line, JUMP to 526EH to display the "FILE SPEC REQUIRED" error message and exit.
5208LD DE,52C3H 11 C3 52Point Register Pair DE to 52C3H, the secondary FCB/filespec template area. This is passed to the open file utility to locate and open the file specified by the user.
520BGOSUB to the SYS0 open file utility at 4476H. Opens the file using the parsed filespec. On return: Z FLAG set = file opened successfully, FCB at 52A3H populated with file information; NZ FLAG set = open failed (file not found, access denied, etc.).
520EIf the NZ FLAG (Not Zero) has been set because the file could not be opened, JUMP to 528AH to display the "PARAMETER ERROR" error message and exit.
The file has been successfully opened. The FCB at 52A3H is now populated with the file's directory information, including the sector count at 52AFH (FCB offset +0CH) and the directory info bytes at 52A9H (FCB offset +06H-+07H). The next step reads a record from the file to establish the I/O state.
5210LD DE,52A3H 11 A3 52Point Register Pair DE to 52A3H, the primary FCB, for the record read operation.
5213LD HL,52D4H 21 D4 52Point Register Pair HL to 52D4H, the 256-byte sector buffer used for file I/O. Data read from the file will be placed here.
5216LD B,00H 06 00Load Register B with 00H (read mode byte). This specifies a standard sequential read operation.
5218GOSUB to the SYS0 routine at 4420H to read a record from the file. B=00H (mode), DE=52A3H (FCB), HL=52D4H (buffer). This establishes the file I/O state and reads the first sector of data.
521BIf the NZ FLAG (Not Zero) has been set because the record read failed, JUMP to the error exit at 5269H.
521DLD BC,0000H 01 00 00Self-Modifying Code
Load Register Pair BC with the sector overwrite count. The 0000H operand at 521EH-521FH is the initial value; at runtime, this location is patched by the file open or record read routines with the actual number of sectors to process. If BC=0 (no sectors to overwrite), execution skips past the overwrite loop to 5253H.
5220LD A,B 78Load Register A with the high byte of the sector count (Register B).
5221OR A,C B1OR Register A with Register C (low byte of sector count). If BC=0000H, the result is zero and the Z FLAG is set.
5222If the Z FLAG (Zero) has been set because BC=0 (no sectors to overwrite), JUMP to 5253H to skip the overwrite loop and proceed directly to marking the directory entry as deleted.
Sector Overwrite Loop
The following code overwrites the file's data sectors with E5H bytes (the standard deleted-entry marker). It saves the current sector count from the FCB, positions to the calculated record, fills the buffer with E5H, writes the filled buffer back, then restores the sector count and updates the directory entry display.
5224LD HL,(52AFH) 2A AF 52Fetch the total sector count from the FCB at 52AFH (FCB offset +0CH-+0DH, which holds IX+0CH-0DH: total sector count from the directory entry) into Register Pair HL. This value is saved on the stack to be restored after the overwrite.
5227PUSH HL E5Save the original total sector count (HL) onto the stack for later restoration.
5228SLA C CB 21Shift Register C Left Arithmetic (multiply low byte by 2). First step of multiplying BC by 4 to convert the sector count to a record position index.
522ARL B CB 10Rotate Register B Left through Carry (propagate the carry from SLA C into the high byte). BC is now multiplied by 2.
522CSLA C CB 21Shift Register C Left Arithmetic again (second multiply by 2). BC is now multiplied by 4 from its original value.
522ERL B CB 10Rotate Register B Left through Carry. BC now holds the original value multiplied by 4.
5230DEC BC 0BDECrement Register Pair BC by 1 to convert from a count to a zero-based position index. The result is (original_count x 4) - 1, which is the last record position to write.
5231GOSUB to the SYS0 routine at 4442H to position the file to the record specified by BC. This seeks to the target sector for the E5H overwrite.
5234LD HL,52D4H 21 D4 52Point Register Pair HL to 52D4H, the 256-byte sector buffer.
5237LD DE,52D5H 11 D5 52Point Register Pair DE to 52D5H (one byte past the start of the buffer). This sets up the LDIR to propagate the E5H fill byte through the entire buffer.
523ALD BC,00FFH 01 FF 00Load Register Pair BC with 00FFH (255 bytes). Combined with the single byte written at (HL), this fills all 256 bytes of the buffer.
523DLD (HL),0E5H 36 E5Store E5H at the first byte of the sector buffer (52D4H). E5H is the standard TRS-80 deleted-entry marker byte used to indicate erased data.
523FLDIR ED B0Block Copy
Copy 255 bytes from (HL=52D4H) to (DE=52D5H), propagating the E5H fill byte through the entire 256-byte sector buffer. After this, every byte from 52D4H to 53D3H contains E5H.
5241LD DE,52A3H 11 A3 52Point Register Pair DE to 52A3H, the primary FCB, for the write record operation.
5244GOSUB to the SYS0 routine at 4439H to write the E5H-filled sector buffer back to the file. This overwrites the file's data sector with deletion markers.
5247POP HL E1Restore the original total sector count from the stack into Register Pair HL.
5248LD (52AFH),HL 22 AF 52Store the original total sector count back to the FCB field at 52AFH (FCB offset +0CH-+0DH), restoring the sector count that was potentially modified by the position/write operations.
524BPUSH AF F5Save Register AF (including the status flags from the write operation) onto the stack.
524CGOSUB to the SYS0 routine at 443FH to flush the file (close-and-reopen). This ensures the E5H-overwritten sector is committed to disk.
524FGOSUB to the SYS0 directory entry display helper at 444EH. This displays the file's directory entry information to the screen, confirming to the user which file was allocated.
5252POP AF F1Restore Register AF (write status flags) from the stack.
Directory Entry Update
The following code marks the file's directory entry as deleted by setting bit 7 of the flags/attribute byte (offset +01H). This prevents the file from appearing in directory listings and marks its space as reclaimable.
5253PUSH AF F5Save Register AF (current status flags) onto the stack. The flags will be tested after the directory update to determine the final exit path.
5254LD BC,(52A9H) ED 4B A9 52Fetch the directory info bytes from the FCB at 52A9H (FCB offset +06H-+07H) into Register Pair BC. These bytes identify the directory sector and entry position for the file being allocated.
5258GOSUB to the SYS0 directory entry validation routine at 4B10H. On entry, BC holds the directory info bytes. On return: Z FLAG set = valid directory entry found, HL points to the entry's status byte in the directory sector buffer; NZ FLAG set = validation failed.
525BIf the NZ FLAG (Not Zero) has been set because directory entry validation failed, JUMP to the error exit at 5269H.
525DINC HL 23INCrement Register Pair HL to advance from the directory entry status byte (offset +00H) to the flags/attribute byte (offset +01H).
525ESET 7,(HL) CB FESET bit 7 of the flags/attribute byte at (HL). This marks the directory entry as killed/deleted. Bit 7 of the attribute byte is the kill flag in the VTOS directory entry format. This is because the disk space is allocated to the file, and the file is created in the directory, but it does not actually occupy the disk space.
5260GOSUB to the SYS0 directory entry write/update routine at 4B1FH. This writes the modified directory sector (with the kill flag set) back to disk.
5263If the NZ FLAG (Not Zero) has been set because the directory write failed, JUMP to the error exit at 5269H.
5265POP AF F1Restore Register AF from the stack. The Z FLAG reflects the overall success/failure status of the kill operation.
5266If the Z FLAG (Zero) has been set indicating the entire kill operation completed successfully, JUMP to 402DH in SYS0, the DOS READY / No-Error Exit.
5269OR A,40H F6 40OR Register A with 40H to set bit 6 of the error code. Bit 6 suppresses extended error context display (matching 430FH bit 6 behavior documented in SYS4).
526BJUMP to 4409H in SYS0, the DOS Error Exit. Register A contains the error code with bit 6 set to suppress extended context.
526EH - "FILE SPEC REQUIRED" Error Handler
The file has been successfully opened. The FCB at 52A3H is now populated with the file's directory information, including the sector count at 52AFH (FCB offset +0CH) and the directory info bytes at 52A9H (FCB offset +06H-+07H). The next step reads a record from the file to establish the I/O state.
Load Register Pair BC with the sector overwrite count. The 0000H operand at 521EH-521FH is the initial value; at runtime, this location is patched by the file open or record read routines with the actual number of sectors to process. If BC=0 (no sectors to overwrite), execution skips past the overwrite loop to 5253H.
Sector Overwrite Loop
The following code overwrites the file's data sectors with E5H bytes (the standard deleted-entry marker). It saves the current sector count from the FCB, positions to the calculated record, fills the buffer with E5H, writes the filled buffer back, then restores the sector count and updates the directory entry display.
Copy 255 bytes from (HL=52D4H) to (DE=52D5H), propagating the E5H fill byte through the entire 256-byte sector buffer. After this, every byte from 52D4H to 53D3H contains E5H.
Directory Entry Update
The following code marks the file's directory entry as deleted by setting bit 7 of the flags/attribute byte (offset +01H). This prevents the file from appearing in directory listings and marks its space as reclaimable.
Error handler invoked when the KILL command is entered without a filespec on the command line. Displays the "FILE SPEC REQUIRED" message and exits via the error-already-displayed handler.
5277H - "FILE SPEC REQUIRED" Message String
ASCII error message displayed when no filespec is provided on the KILL command line.
ASCII string "FILE SPEC REQUIRED" followed by carriage return (0DH). 19 bytes total.
528AH - "PARAMETER ERROR" Error Handler
Error handler invoked when the specified file cannot be opened (file not found, access denied, or other open failure). Displays the "PARAMETER ERROR" message and exits.
5293H - "PARAMETER ERROR" Message String
ASCII error message displayed when the file open operation fails.
ASCII string "PARAMETER ERROR" followed by carriage return (0DH). 16 bytes total.
52A3H - FCB and Buffer Data Areas
File Control Block work areas and sector buffer used by the KILL command. The primary FCB at 52A3H receives the parsed filespec from 441CH and is populated by the open file utility at 4476H. Key FCB fields referenced by the code: 52A9H (FCB+06H/+07H = directory info bytes) and 52AFH (FCB+0CH/+0DH = total sector count). The secondary filespec template at 52C3H is passed to the open utility. The 256-byte sector buffer at 52D4H is used for record read/write operations and the E5H fill.
32-byte File Control Block. Key offsets: 52A9H (+06H/+07H) = directory info bytes, read by LD BC,(52A9H) at 5254H for directory entry lookup. 52AFH (+0CH/+0DH) = total sector count, read at 5224H and restored at 5248H.
17-byte filespec/FCB template area passed to CALL 4476H (open file utility) at 520BH. Contains pre-initialized filename and extension fields with internal control bytes (1EH field separators).
256-byte buffer for file I/O. Used as the destination for CALL 4420H (read record) at 5218H. Filled with E5H (deleted-entry marker) at 523DH-523FH and written back via CALL 4439H (write record) at 5244H to overwrite the file's data sectors during the kill process.
ISAM 18H - KILL Command - Offset 0371
The KILL command in VTOS follows a procedure where it first extracts the filespec from the command line, performs a basic file open/close, followed by a GAT update. On success, exits via the DOS READY path. Displays "FILE SPEC REQUIRED" if no filespec was provided; exits with an error code if the open or GAT-close fails.
5200H - File Open/Close Routine
Error Path
Reached when the file open (4424H) or the GAT read/close (442CH) returns NZ. Sets the error code high bit and jumps to the DOS error exit.
No Filespec Error Path
Reached when 441CH returns NZ indicating no filespec was found on the command line. Displays the inline "FILE SPEC REQUIRED" message and exits via the error-already-displayed path.
Error Message
The bytes from 5226H to 5238H are a text string "FILE SPEC REQUIRED" terminated by 0DH (carriage return).
ISAM 19H - LIB Command: Display Command Library - Offset 03B1
Implements the LIB command, which displays the complete VTOS 4.0 primary command library in a four-column tabular format. Each command name is stored as a 6-byte space-padded entry in the data table at 4EADH. The routine reads four names per screen row, prefixing each with the C2H indent character and suffixing it with the C8H column-spacing character to produce the aligned four-column layout seen on screen. A termination flag byte of 00H in the table signals the end of the list.
5200H - LIB Command: Display Command Library
Inner Loop Start
Displays the 6-character space-padded command name from the current table entry. Register B counts down from 6 to 0, advancing HL through the name bytes.
Inner Loop End
All six characters of the current command name have been displayed. Output the C8H column-spacing character, skip the two non-display bytes in the table entry, then check the termination flag.
End Of Row
All four command names for the current screen row have been displayed. Issue a carriage return to move to the next line, then loop back to begin the next row of four names.
Clean Exit Path
Reached when the termination flag byte at (HL) is 00H, confirming all 36 command names have been displayed. Issues a final carriage return and returns control to the DOS prompt.
DEBUG (14H) / CLOCK (17H) / TRACE (1AH) / VERIFY (1BH) Offset 03E8
Introduction
SYS6/SYS is a Partitioned Data Set (PDS) file containing all of VTOS 4.0's library commands - the commands that are too large to fit in the resident DOS but are invoked by name from the command line. Member 03 is a single 256-byte overlay block (load address 5200H-52F1H) that implements four distinct library commands, each with its own ISAM entry point:
- DEBUG (IDAM 14H) - Enables or disables the interactive Z80 debugger monitor.
- CLOCK (IDAM 17H) - Turns the real-time clock display on or off.
- TRACE (IDAM 1AH) - Enables or disables the real-time program counter display.
- VERIFY (IDAM 1BH) - Enables or disables read-after-write verification on all disk writes.
All four commands accept an optional flag argument (ON/YES or OFF/NO) parsed from the command line. If no flag is provided, the command toggles its current state. The four commands share a common parameter-parsing subroutine at 527BH (for CLOCK, TRACE, and VERIFY) and a dedicated entry at 5276H for DEBUG. Each command performs its action by reading or writing specific bits or memory locations in the VTOS resident DOS area at 4000H-4DFFH.
This member resides at disk seek pointer offset 0003E8H within the SYS6 PDS file and is shared by all four ISAM entries (14H, 17H, 1AH, 1BH), which enter the member code at different addresses: 5200H (DEBUG), 5241H (CLOCK), 5257H (TRACE), and 5267H (VERIFY).
Memory Locations and Variables
| Address Range Size | Purpose |
|---|---|
| 430FH 1 byte | System State Flags register in the VTOS resident DOS. Bit 7 (80H) is the "Debug Active / Return to DEBUG on error" flag. DEBUG (IDAM 14H) sets bit 7 to enable the debugger and clears it to disable. Also controls overlay dispatch behavior in SYS0. |
| 4315H 1 byte | Patchable opcode byte in the VTOS resident DOS. Normally C9H (RET). DEBUG writes C3H (JP opcode) here to arm the BREAK-key re-entry path to the DEBUG monitor, or C9H to disarm it. |
| 4316H-4317H 2 bytes | BREAK handler jump target in the VTOS resident DOS. DEBUG writes 0FH, 40H here (the address 400FH, the DEBUG entry point) to route BREAK key interrupts to the debugger. |
| 400FH 3 bytes | RST 30H hook in the VTOS resident DOS. This is the DEBUG monitor entry point. When 4315H=C3H and 4316H-4317H=0F40H, the BREAK key causes a JP 400FH, entering the DEBUG monitor. |
| 400FH (LD HL reference) - | Also used as source address: LD HL,400FH at 521BH loads the address of the RST 30H vector itself to set bit 7 of the byte at that address, arming the DEBUG intercept within SYS0's hook table. |
| 4DABh FCB area | Drive parameter FCB area referenced by the CLOCK command (IDAM 17H) path at 5244H. Loaded into DE as the file/device control block pointer passed to the SYS0 routine at 4410H. |
| 4DDBh FCB area | Drive parameter FCB area referenced by the TRACE command (IDAM 1AH) path at 525AH. Loaded into DE as the device control block pointer passed to the SYS0 routine at 4410H. |
| 48C0H - | VERIFY command (IDAM 1BH) target address when the flag argument is non-zero (enabling verification). Stored into the word at 443AH. |
| 48DBH - | VERIFY command (IDAM 1BH) target address when the flag argument is zero (disabling verification). Stored into the word at 443AH. |
| 443AH-443BH 2 bytes | VTOS resident DOS: dispatch vector or function pointer patched by the VERIFY command to redirect all disk write operations through or around read-after-write verification logic. |
| 52A2H-52F1H 80 bytes | Data area: contains ASCII parameter option strings (EXT, ON, OFF, YES, NO, RY, RN) used by the common parameter parser at 527EH, plus two NOP padding bytes at 5297H-5298H. |
| 5290H 2 bytes (self-modifying) | Runtime variable. The common parameter parser (5276H/527BH/527EH) stores the parsed flag result (0000H = flag absent, non-zero = flag present with sense) into 5290H via LD (5290H),BC at 5281H. DEBUG reads this back via LD BC,(5290H) at 528FH. |
| 523CH-523DH 2 bytes (self-modifying) | Runtime variable. The DEBUG entry initializes this to 0000H (LD (523CH),BC at 5203H), and the parameter parser also clears it (LD (523CH),BC at 5285H). It is then set by SET 7,(HL) at 5219H. The parser at 5276H/527EH also clears it (5285H). Read at 5210H to determine the current debug enable state. |
Major Routines
| Address | Name and Purpose |
|---|---|
| 5200H | DEBUG Command Entry Point (IDAM 14H) Implements the VTOS DEBUG command. Parses the optional ON/OFF/EXT flag argument, then toggles or sets the debug-active state. Enabling DEBUG patches 430FH bit 7, 4315H (JP opcode), and 4316H-4317H (400FH target) to route BREAK key presses and error conditions into the SYS5 DEBUG monitor. Disabling reverses these patches. If the EXT flag is set, issues RST 28H SVC code 8BH to load the extended debugger. Exits via JP 402DH (DOS READY). |
| 5241H | CLOCK Command Entry Point (IDAM 17H) Implements the VTOS CLOCK command. Calls the common parameter parser at 527BH to determine the ON/OFF flag state, loads DE with the CLOCK device control block address (4DABH), and loads A with 06H. Branches to either 4413H (enable, Z set) or 4410H (disable, NZ) in SYS0 to turn the real-time clock display on or off. Exits via JP 402DH. |
| 5257H | TRACE Command Entry Point (IDAM 1AH) Implements the VTOS TRACE command. Identical structure to CLOCK. Calls the common parser at 527BH, loads DE with the TRACE device control block address (4DDBH), loads A with 0BH. Branches to 4413H (enable) or 4410H (disable). Exits via JP 402DH. |
| 5267H | VERIFY Command Entry Point (IDAM 1BH) Implements the VTOS VERIFY command. Calls the common parser at 527BH, then conditionally loads HL with either 48C0H (enable) or 48DBH (disable). Stores the chosen address into the two-byte dispatch vector at 443AH. Returns via RET (which returns to the SYS0 overlay dispatch framework). |
| 5276H | DEBUG Parameter Parser Entry Entry point for the DEBUG command's parameter parsing. Loads DE with 52B2H (the "EXT" option string base) and falls through to the common parser body at 527EH. This allows DEBUG to also recognize the EXT flag in addition to ON/OFF/YES/NO. |
| 527BH | Common Parameter Parser Entry (CLOCK/TRACE/VERIFY) Entry point for CLOCK, TRACE, and VERIFY parameter parsing. Loads DE with 52C2H (the "ON/OFF" option string base) and falls through to the common parser body. |
| 527EH | Common Parameter Parser Body Shared by all four commands. Clears BC and stores 0000H into both 5290H and 523CH. Calls the SYS0 open-file/parse routine at 4476H to extract the command line argument. If no argument is present (Z flag set), returns with the XOR result (0FFH in A, effectively indicating toggle). If an argument is present, calls 447BH to scan for a matching keyword in the option string table at DE. Returns with Z set if a positive (ON/YES) keyword matched, NZ if a negative (OFF/NO) keyword matched. |
| 5299H | Parameter Error Display and Exit Loads HL with 52A2H (the "PARAMETER ERROR" string), calls 447BH to display it, then exits via JP 4030H (error-already-displayed exit). |
5200H - DEBUG Command Entry Point (IDAM 14H)
DEBUG command (IDAM 14H). Entry point for all four commands at 5200H. Initializes registers, tests whether DEBUG is currently active via the flag byte at 523CH, and toggles or sets the debug-enable state based on the optional command-line argument.
Store 0000H (Register pair BC) into the two-byte flag variable at 523CH-523DH. This clears the debug-enable flag, initializing the state before the command-line argument is parsed. The runtime value at 523CH will be set to a non-zero value (via SET 7,(HL) at 5219H) if DEBUG is being enabled.
Enable Path: The following instructions arm the DEBUG monitor. They patch three locations in the resident DOS so that the BREAK key and error conditions route to the DEBUG entry point at 400FH.
Set bit 7 of the byte at the memory address pointed to by HL (which is 430FH). This writes a 1 into bit 7 of the System State Flags register, enabling the "Return to DEBUG on error / Debug Active" state. With this bit set, the SYS0 overlay dispatcher at 4CDEH will route error conditions to the DEBUG monitor at 400FH rather than to the command loop.
Store Register pair HL (containing 400FH, the DEBUG entry point address) into the two bytes at 4316H-4317H in the resident DOS. Together with the C3H written to 4315H below, this creates a complete JP 400FH instruction at 4315H-4317H. When the BREAK key is pressed and the ISR fires, it will read this sequence and jump into the DEBUG monitor.
Store Register A (containing C3H, the JP opcode) into 4315H in the resident DOS. This is the final step of arming the BREAK-key intercept. With C3H at 4315H and 400FH at 4316H-4317H, the BREAK key path is now live. The ISR at 4561H-4577H reads 4315H, detects C3H, and branches into the DEBUG monitor entry sequence.
Disable Path: The following instructions disarm the DEBUG monitor, reversing the patches made by the enable path.
Clear bit 7 of the byte at the memory address pointed to by HL (which is 430FH, the System State Flags register). This writes a 0 into bit 7, disabling the "Debug Active" state. The SYS0 overlay dispatcher and error handler will no longer route to the DEBUG monitor.
Store Register A (containing C9H, the RET opcode) into 4315H in the resident DOS. This disarms the BREAK-key intercept. The BREAK key will no longer trigger entry to the DEBUG monitor; instead, the ISR's BREAK path will return normally.
Two NOP bytes (00H 00H) at 523CH-523DH. These bytes serve as a 2-byte runtime variable initialized to 0000H by LD (523CH),BC at 5203H. The value at 523CH is read back at 5210H to determine whether the debug-enable state flag has been set. The second byte at 523DH participates in the 16-bit zero-store operation.
523EH - Extended DEBUG Loader (SVC 8BH)
Reached by CALL NZ,523EH at 5215H when the EXT flag was detected. Issues RST 28H SVC code 8BH to invoke SYS7's debug-enable path, which loads the extended DEBUG facility into high memory (above X'5200'). Returns to the caller at 5218H.
5241H - CLOCK Command Entry Point (IDAM 17H)
CLOCK command (IDAM 17H). Parses the optional ON/OFF flag argument, then calls the SYS0 routine at 4413H (enable) or 4410H (disable) to turn the real-time clock display on or off. The internal clock continues to run regardless.
5257H - TRACE Command Entry Point (IDAM 1AH)
TRACE command (IDAM 1AH). Parses the optional ON/OFF flag argument, then enables or disables the real-time program counter (PC) display. When enabled, the current execution address of a running assembly language program is continuously shown on the top line of the display.
5267H - VERIFY Command Entry Point (IDAM 1BH)
VERIFY command (IDAM 1BH). Parses the optional ON/OFF flag argument, then patches the disk write dispatch vector at 443AH-443BH in the resident DOS to route all disk writes either through (enable) or around (disable) read-after-write verification logic.
Store Register pair HL (containing either 48C0H for enable or 48DBH for disable) into the two bytes at 443AH-443BH in the VTOS resident DOS. This patches the disk write dispatch vector, redirecting all disk write operations either through the verification routine (48C0H) or directly to the unverified write routine (48DBH). The VERIFY command's effect is system-wide: all subsequent disk writes - regardless of which program or SVC issued them - will use the newly installed vector.
5276H - DEBUG Parameter Parser Entry
Entry point for the DEBUG command's argument parsing (called from 5207H). Loads DE with the address of the "EXT" option string at 52B2H, then falls through to the common parser body at 527EH. This allows DEBUG to recognize the EXT (extended debugger) keyword in addition to ON/OFF/YES/NO.
527BH - Common Parameter Parser Entry (CLOCK/TRACE/VERIFY)
Entry point for parameter parsing used by CLOCK (5241H), TRACE (5257H), and VERIFY (5267H). Loads DE with 52C2H (the ON/OFF/YES/NO option string base) and falls through to the common parser body at 527EH.
527EH - Common Parameter Parser Body
Shared by all four commands. Clears the runtime flag variables, calls the SYS0 argument extraction routine at 4476H to read the command line parameter, and returns with the Z flag indicating ON (Z) or OFF (NZ), or performs keyword matching against the option string table at DE.
Store 0000H (Register pair BC) into the two-byte variable at 5290H-5291H. This clears the parsed-flag result area before the argument is read. The DEBUG command will read this value back after the CALL to determine the flag state.
Store 0000H (Register pair BC) into 523CH-523DH, clearing the debug-enable flag variable. This ensures a clean starting state regardless of which command is being processed.
The bytes at 528FH-5291H are decoded as LD BC,0000H by the disassembler, but the operand at 5290H-5291H is the self-modifying runtime variable initialized by LD (5290H),BC at 5281H. At this point, BC is loaded with whatever value was stored into 5290H-5291H during or after the CALL 4476H. The loaded value in BC reflects the parsed flag result (0000H = no argument or absent, non-zero = argument present and matched).
Two NOP bytes at 5297H-5298H serve as padding between the parser return and the parameter error handler below.
5299H - Parameter Error Display and Exit
Reached by JP NZ,5299H at 528CH when the command-line argument was invalid or unrecognized. Displays the "PARAMETER ERROR" message string and exits via the error-already-displayed path at 4030H.
52A2H - Data Area: "PARAMETER ERROR" String
ASCII string "PARAMETER ERROR" followed by a carriage return (0DH) terminator. Displayed by the parameter error handler at 5299H. The disassembler incorrectly interprets these bytes as Z80 instructions.
52B2H - Data Area: "EXT" and Related Option Strings (DEBUG Parser)
Option keyword strings for the DEBUG command's parameter parser. The DEBUG parser entry at 5276H points DE to 52B2H. The string table encodes the "EXT", "ON"/"YES", and "OFF"/"NO" keywords used by the SYS0 keyword-matching routine at 447BH. The disassembler incorrectly interprets these bytes as Z80 instructions.
ISAM 1DH - O! - Offset 04E2
5200H - Displays the encoded copyright notice
Displays the encoded copyright notice "COPYRIGHT (C) 1980 BY RANDOLPH V. COOK, ALL RIGHTS RESERVED" by subtracting the key byte 90H from each stored byte to recover the original ASCII characters, then outputting each via ROM 0033H. The string is stored with each character value increased by 90H, preventing the text from appearing in plain ASCII in a binary dump.
Fetch the next encoded character byte from the string into Register A.
520EH - Encoded Copyright String Data
Obfuscated copyright string. Each character is stored as (ASCII value + 90H), preventing the text from appearing in plain ASCII in a binary dump. The first byte is the decode key (90H). The string is terminated by a 00H byte. Decoded content: "COPYRIGHT (C) 1980 BY RANDOLPH V. COOK, ALL RIGHTS RESERVED" + CR.
Correction
9DH - 90H = 0DH = Carriage Return (CR). This is the line terminator at the end of the copyright string, causing the cursor to move to the start of the next line after display.
ISAM 1EH - MEMORY Command - Offset 0535
5200H - MEMORY Command
Implements the MEMORY command. With no parameters, displays the current HIGH$ value as "HIGH = X'XXXX'". With a HIGH=addr parameter, validates the new address and updates the system HIGH$ pointer at 4049H. Produces PARAMETER ERROR if the keyword is unrecognised, or RANGE ERROR if the supplied address is above 5200H or above the current HIGH$ value.
This instruction's operand field (0000H) is overwritten at runtime by 4476H with the parsed HIGH= address value. If no HIGH= parameter was given, BC remains 0000H. If HIGH=addr was supplied, BC holds the new requested HIGH$ address.
A HIGH=addr parameter was supplied. BC holds the requested new HIGH$ address. Two range checks follow before the value is accepted.
No HIGH= parameter was given. Fetch the current HIGH$ address from system variable 4049H into Register Pair DE. 4DE7H expects the address to display in DE.
An unrecognised parameter was found on the command line (e.g., "MEMORY (200)" as shown in the screenshot). Point HL to the "PARAMETER ERROR" message at 5242H.
5242H - "PARAMETER ERROR" Message String
Error message displayed when the MEMORY command receives an unrecognised parameter (e.g., a bare number instead of HIGH=addr). Terminated by CR (0DH).
5252H - Range Check Failure / "RANGE ERROR" Message
Reached when the supplied HIGH= address fails either range check: address at or above 5200H (would overwrite this overlay), or address above the current HIGH$ value (cannot raise HIGH$). Displays "RANGE ERROR" and exits.
Point HL to the "RANGE ERROR" message string at 525BH. Reached from either the overlay collision check at 5215H or the above-current-HIGH$ check at 521DH.
525BH - "RANGE ERROR" Message String
Error message displayed when the supplied HIGH= address is outside the permitted range. Terminated by CR (0DH).
5267H - HIGH Keyword Parameter Table
Parameter descriptor table passed to 4476H at DE=5267H. Defines the "HIGH" keyword and its single-letter abbreviation "H" for command line parsing. Uses the standard 8-byte entry format: 6-byte space-padded keyword, 1-byte result, 1-byte destination high address. Terminated by 00H.
5278H - "HIGH = X'XXXX'" Display String
Display string used when MEMORY is invoked with no parameters. The four 'X' placeholder bytes at 5281H are overwritten in place by 4DE7H with the ASCII hex digits of the current HIGH$ address before 447BH prints the string. Terminated by CR (0DH).
Four ASCII 'X' placeholder bytes (58H). Overwritten at runtime by the CALL 4DE7H at 522DH, which replaces these four bytes with the ASCII hex digits of the current HIGH$ address from DE. For example, if HIGH$ is FDE8H, these bytes become 46H 44H 45H 38H = "FDE8".
ISAM 1CH - SYSTEM/SYSGEN Command Entry Point - Offset 05C3
5200H - SYSTEM's SYSGEN Command Entry Point
Entry point for the SYSTEM command's SYSGEN parameter. Saves or restores the complete VTOS 4.0 system configuration to/from the file CONFIG/SYS.CC on drive 0. If called with HL non-zero (SYSGEN=ON path), packages key system memory regions into sector records and writes them to disk, displaying "USER CONFIGURATION BUILT" on success. If called with HL=0000H (display/read path), attempts to open the configuration file and restore the saved configuration, displaying "USER CONFIGURATION DELETED" or "NO USER CONFIGURATION FOUND" as appropriate. Cannot be invoked while program chaining is active.
Write path: HL is non-zero (SYSGEN=ON). Open CONFIG/SYS.CC for writing and package all system configuration regions into sector records.
File opened successfully. Write two single-byte marker records to the file, then write the "*** CONFIGURING ***" banner to video RAM, then write all configuration memory regions.
Fetch the next byte from the data block at 53BDH into Register A.
DECrement B and LOOP BACK to 522AH if not zero. Repeats for all 6 bytes of the header data block.
Fetch the low byte of the byte count for this configuration region entry into Register C.
All named configuration regions have been saved. Now detect the actual top of installed RAM by probing from FFFFH downward. Load HL with FFFFH as the starting probe address.
Read the current byte at the probe address into Register A and save it in B.
Real RAM found at HL. Fetch the current HIGH$ limit from system variable 4049H into DE. The difference between detected RAM top (HL) and HIGH$ (DE) represents the usable memory range to record.
File written and closed successfully. Now write a sector to drive 0 track 0 sector 2 to mark the disk as containing a valid system configuration.
52CEH - Configuration Region Write Subroutine
Packages a region of system memory into one or more sector records and writes each byte to the configuration file via 53B2H. On entry: DE = source address in memory (the destination address when restoring), BC = byte count. Handles regions larger than 254 bytes by splitting into multiple records. Each record begins with a type byte (01H), a length+2 byte, then the 16-bit destination address, followed by the data bytes.
Fetch the next data byte from the source memory region pointed to by HL.
DECrement B and LOOP BACK to 52FAH if not zero. Repeats until all data bytes for this record have been written.
5307H - Block Copy and File Write Subroutine
Copies B bytes from source HL to destination DE while simultaneously writing each byte to the configuration file via 53B2H. Used to update video RAM with status banners and write the RST hook bytes while recording them to the CONFIG/SYS.CC file in a single pass. The record written has type 01H, length B+2, followed by the destination address (little-endian), followed by the B data bytes.
Fetch the next byte from the source buffer at HL into Register A.
DECrement B and LOOP BACK to 5321H if not zero. Repeats until all B data bytes have been written to the file.
5329H - Configuration Read / Delete Path
Entered when SYSGEN is invoked with no address parameter (HL=0000H). Attempts to open the CONFIG/SYS.CC file. If the file does not exist, displays "NO USER CONFIGURATION FOUND". If found, reads the configuration marker sector from drive 0 track 0 sector 2. If the marker byte at 4201H is already C9H (no config present), displays "NO USER CONFIGURATION FOUND". Otherwise clears the marker byte (00H), writes the sector back, and displays "USER CONFIGURATION DELETED".
File opened. Now read the configuration marker sector from drive 0 track 0 sector 2 to check whether a valid user configuration is present on this disk.
Point HL to the "SYSGEN INVALID DURING PROGRAM CHAINING" error message at 5366H.
5366H - "SYSGEN INVALID DURING PROGRAM CHAINING" Message
Error message displayed when a SYSGEN operation is attempted while program chaining (430FH bit 5) is active. Terminated by CR (0DH).
538DH - Disk Error Exit
Common error exit for all disk I/O failures within the SYSGEN command. ORs 40H into Register A (sets bit 6 to suppress extended error context) then jumps to the SYS0 error handler at 4409H.
5392H - CONFIG/SYS.CC Filespec
Null-terminated filespec string for the VTOS system configuration file. Passed to 4420H, 4424H as the FCB filename. The format "CONFIG/SYS.CC:0" specifies filename CONFIG/SYS, extension CC, drive 0.
53B2H - File Output Byte Subroutine
Writes the byte in Register A to the configuration file via the SYS0 I/O device interface at ROM 001BH. On entry: A = byte to write, DE = DCB address (5392H). Returns with Z FLAG set if the write succeeded. If the write fails (NZ), jumps to the disk error handler at 538DH rather than returning to the caller.
53B8H - "USER CONFIGURATION BUILT" Message
Success message displayed after the SYSGEN write path completes successfully. Terminated by CR (0DH).
53D1H - "USER CONFIGURATION DELETED" Message
Message displayed after the SYSGEN read/delete path successfully clears the configuration marker from the disk boot sector. Terminated by CR (0DH).
53ECH - "NO USER CONFIGURATION FOUND" Message
Message displayed when the CONFIG/SYS.CC file does not exist on drive 0, or when the file open at 532CH fails. Terminated by CR (0DH).
5408H - Single Byte Data: C9H (RET Opcode)
A single byte containing C9H (the Z80 RET opcode). Used at 5241H as the source for a 1-byte block write to 4012H via 5307H: the RST 38H hook at 4012H is set to RET during configuration saving, and this value is simultaneously recorded in the configuration file.
5409H - "*** CONFIGURING ***" Screen Banner Data
19-byte data block written to video RAM at 3F93H during SYSGEN to display a status banner while the configuration is being saved to disk. Also written to the configuration file as a memory region record by the call to 5307H at 5239H, with destination address 3F93H.
541CH - Screen Banner Clear Data
19-byte block of ASCII space characters (20H) written to video RAM at 3F93H by the second call to 5307H at 5292H. Clears the "*** CONFIGURING ***" banner from the screen. Also written to the configuration file as a second memory region record with destination 3F93H, so that the screen area is also cleared when the configuration is restored on next boot.
542FH - Configuration Region Table
Table of system memory regions to be saved into the CONFIG/SYS.CC configuration file. Each entry is 4 bytes: count-low, count-high, destination-low, destination-high. The table is processed by the loop at 524EH-525FH; each entry causes 52CEH to read that many bytes from the specified destination address and write them as a type-01H sector record to the configuration file. When the configuration is restored on boot, the destination address in each record tells the loader where to copy the data back. The table is terminated by a 2-byte 0000H count sentinel, which the loop detects at 5252H-5254H before reading the destination bytes.
ISAM 21H - DIR - Offset 0827
5200H - DIR Command Entry Point and Filespec Parser
Entry point for the DIR command (ISAM 21H). Parses the optional filespec from the command line, extracting up to 8 filename characters and 3 extension characters into the filename buffer at 56F6H and extension buffer at 56FEH. If a colon and drive number are present, stores the drive number. Then opens the directory via SYS0 utilities and iterates through directory sectors displaying matching file entries with attributes, protection level, EOF count, file size, and optional date.
5203H - Compute DE from Directory Entry Offset
Helper subroutine that reads a 16-bit value from the directory entry pointed to by HL, using A as the low-byte index. H is pre-set to 42H by the caller, so HL points into the sector buffer at 4200H. Also checks the EOF flag at 5211H and decrements DE by 1 if the EOF offset is non-zero (meaning the last sector is only partially filled).
520FH - DIR Working Variables
Work area used as runtime storage by the DIR command. These locations are patched by self-modifying code during execution.
EOF byte offset variable. Written by the file size routine at 5435H with the EOF byte from directory entry offset +03H. Read by the helper subroutine at 520AH to determine whether to adjust the sector count. 00H means the file ends exactly on a sector boundary.
5213H - Command Line Filename Parser
Parses the filespec from the command line buffer. Skips leading spaces, then extracts up to 8 filename characters (alphanumeric only) into the filename buffer at 56F6H. If a slash separator is found, extracts up to 3 extension characters into 56FEH. If a colon and drive number are present, stores the drive number for single-drive directory listing. Non-alphanumeric characters terminate each field.
Fetch the next character from the command line buffer pointed to by HL into Register A. On entry from 5200H, HL points to the command line text after the DIR command keyword.
Loop End
The following loop extracts up to 8 alphanumeric characters from the command line into the filename buffer. Characters below ASCII 0 (30H) or between 9 (39H) and A (41H) are treated as delimiters and terminate the filename.
Compare Register A against 41H (ASCII A). If Register A >= 41H, the NO CARRY FLAG is set (character is a letter or above).
Loop End
The following loop extracts up to 3 extension characters, using the same alphanumeric validation logic as the filename parser above.
Fetch the next character from the command line buffer into Register A.
Loop End
Store the drive number to 52E7H. This patches the operand byte of the LD A,0FFH instruction at 52E6H so that subsequent reads via LD A,(52E7H) at 52F7H return the specified drive number instead of the initial FFH. A value of 00H means drive 0 was specified.
5268H - Directory Scan Initialization and Header Display
Initializes the directory scan for a given drive. Sets the lines-per-page counter, inserts the ASCII drive digit into the header template, calls SYS0 to validate the drive, displays the "FILE DIRECTORY --- DRIVE X" banner and column headers, reads the first directory sector (the HIT at sector 0), retrieves the sectors-per-granule value, then reads the first file-entry directory sector (sector 1) to begin scanning entries.
Store the value 03H to 53F5H. This patches the operand of the LD A,03H instruction at 53F4H, initializing the lines-per-page countdown counter. Each time a file entry line is displayed, this counter is decremented; when it reaches zero, a keyboard pause check is performed and the counter is reset.
Store the ASCII drive digit into the directory header template at 568AH. This is the position of the X placeholder in the FILE DIRECTORY --- DRIVE X banner string at 5670H, replacing it with the actual drive number.
The GAT sector is now in the buffer at 4200H. The code copies 16 bytes from offset D0H (42D0H) into the filename/date display template at 5691H, then displays the column header line. These 16 bytes contain the disk name (8 bytes at D0H) and date (8 bytes at D8H).
Store the sectors-per-granule value to 5466H. This patches the operand of the LD A,00H instruction at 5465H, used later by the file size calculation routine at 5467H to compute file sizes in kilobytes.
52D2H - Advance to Next Directory Entry
Called after processing a directory entry (either displayed or skipped). Advances HL to the next 32-byte entry within the current directory sector buffer at 5800H. When the buffer is exhausted, checks the page counter and either wraps to the next drive or fetches the next sector. Also handles the multi-drive scan when no specific drive was requested.
All directory sectors on this drive have been scanned. Output a carriage return, then check if more drives need to be scanned.
Load Register A with the value at 52E7H. The initial value is FFH, but this operand is patched by 5259H with the user-specified drive number (0-7). If a specific drive was requested, A loads that drive number; if no drive was specified (default), A loads FFH.
52F6H - Drive Validation Error Handler
Handles the case where the SYS0 drive validation call at 5274H failed. If a specific drive was requested, this reports an error. If scanning all drives, it silently skips the unavailable drive and advances to the next one.
If we reach here, no specific drive was requested (all-drive scan mode). The current drive is simply not available, so skip it and try the next drive.
5308H - Directory Entry Scan Loop
Main loop that iterates through 32-byte directory entries in the sector buffer at 5800H. For each entry, checks if it is active (non-zero status byte), validates the entry via the HIT, tests file attribute bits (visible, system, password-protected), and compares the filename/extension against the search pattern at 56F6H. Matching entries proceed to the display routine.
Fetch the status byte of the current directory entry from (HL) into Register A. HL points to the start of a 32-byte entry in the sector buffer at 5800H. A value of 00H means this entry slot is empty (no file).
Compare Register A against the value at 5314H. The operand byte at 5314H is patched by 5317H below with the current directory sector index. If A matches (same sector as last validated), the entry is in the same sector already loaded in the primary buffer at 4200H, so no re-read is needed.
Store the new directory sector index to 5314H. This patches the operand of the CP A,0FFH instruction at 5313H so that subsequent entries in the same sector will match and skip the re-read.
Load Register Pair DE with the value at 5332H-5333H. The initial value is 0000H. This location may be patched at runtime to hold a password hash or comparison value for access-level filtering. If the operand is still 0000H, no password filter is active.
Load Register Pair DE with the value at 5340H-5341H. Like 5331H, this may be patched with a password hash for access-level filtering. If 0000H, no filter is active.
The following loop compares the directory entry's filename and extension against the search pattern. Space characters (20H) in the pattern act as wildcards matching any character in that position.
Fetch the next character from the search pattern buffer at (DE) into Register A.
Loop End
535DH - Display Matching Directory Entry
The filename/extension matched the search pattern. Display the file entry: output the filename (up to 8 characters, suppressing trailing spaces), the extension (up to 3 characters with slash separator), and padding spaces. Then display the attribute flags (S=System, I=Invisible), password-protection flag (P), and the optional update indicator (+).
The following loop outputs the filename characters, stopping early if a trailing space is encountered.
Fetch the next filename character from the directory entry at (HL) into Register A.
Loop End
Fetch the next extension character from the directory entry at (HL) into Register A.
Loop End
Load Register A with 20H (ASCII space) to output padding spaces after the filename/extension to align the attribute columns.
5398H - Attribute Flags and Protection Display
Displays the attribute flag characters for the current directory entry. Tests attribute bits in the entry's status byte to output flag characters: S for system file (bit 6), I for invisible (bit 3), and P for password-protected (determined by comparing extent pointers against the disk capacity at 4296H). Also displays the + update indicator if the entry's update flag (bit 6 of offset +01H) is set. Outputs trailing spaces to fill the column, then calls the file size calculation and date display routine.
Now check whether the file is password-protected by examining the extent pointers. The code compares the extent end pointer (at entry offset +10H/+11H) against the disk capacity value stored at 4296H. If they do not match, the file is marked as password-protected with P.
Protection level is zero but extent did not match disk capacity. Check the secondary extent pointer at offset +12H/+13H as a second test for password protection.
Store the protection flag character (either 50H = P or 20H = space) to 5416H. This patches the operand of the LD A,00H instruction at 5415H, which is tested later by the file size display routine at 5414H to determine which protection level name to display from the table at 5650H.
Output trailing spaces to fill the remaining column width, aligning the protection/EOF/size information that follows.
Load Register A with 20H (ASCII space) for padding.
Loop End
Column padding complete. Now call the file size calculation and display routine if the entry has valid extent data.
Load Register Pair DE with the value at 53EDH-53EEH. The initial value is 0000H. This may be patched at runtime with a filter value. If DE is 0000H, no filter is active.
Load Register A with the lines-per-page counter from 53F5H. The initial value is 03H, but this operand is decremented each time a file entry is displayed. When it reaches zero, a pause check is performed.
Store the decremented counter back to 53F5H, patching the operand of the LD A,03H instruction at 53F4H for the next iteration.
The lines-per-page counter has reached zero. Reset the counter and check for a keypress to pause or abort the listing.
Store the reset value 03H to 53F5H, re-initializing the page counter for the next group of file entries.
5410H - Output Character with Column Decrement
Short helper subroutine that decrements the column position counter in C, then outputs the character in A via the character output routine at 5577H. Used by the attribute flag display code to output flag characters (S, I, P, +) while tracking column position.
5414H - File Size Calculation and Display
Calculates and displays the file's protection level name, EOF sector count, size in kilobytes, and creation/modification date. Reads the protection access level from the status byte, looks up the 4-byte protection name from the table at 5650H, reads the EOF byte and sectors-per-granule from the entry, computes total sectors via the extent chain walker at 550AH, converts to kilobytes, and formats the date from the entry's date bytes using the month name table at 56D2H.
Load Register A with the value at 5416H. The initial value is 00H, but this operand is patched by 53D2H with either 50H (ASCII P, password-protected) or 20H (space, not protected). This determines which branch to take below.
Store the EOF byte offset to the working variable at 5211H. This is read by the helper subroutine at 520AH to adjust sector counts during size calculation.
Store the sectors-per-granule value to 5454H. This patches the operand of the LD C,00H instruction at 5453H, used by the extent walker's sector calculation below.
Load Register C with the sectors-per-granule value from 5454H. The operand at 5454H was patched by 543AH with the file's sectors-per-granule value. C is used as the divisor for the 24-bit division at 55DDH.
Load Register A with the sectors-per-granule value from 5466H. The operand at 5466H was patched by 52B8H with the disk's sectors-per-granule count (typically 5).
5492H - Date Formatting and Entry Line Output
Formats the file's creation/modification date from the directory entry's packed date bytes into the display template at 56C8H using the month name table at 56D2H. Initializes the date template, decodes the year, month, and day from the packed format, fills in the DD-MMM-YY fields, and determines the sign character for the date display (+/-). Then outputs the complete attribute/size line via the display string routine.
A date is present. Extract the day from bits 4-0 of the packed date byte. The day is stored as a value which must be divided by 10 to get the two decimal digits.
Convert the binary day value to two ASCII decimal digits via repeated subtraction of 10.
INCrement the tens-digit counter in Register B.
Loop End
Now extract the month from the packed date byte. The month is encoded in bits 7-5 of the flags byte at offset +01H (the byte before the date byte), combined with other data. The code reads the flags byte, isolates the month, and looks up the 3-character month name from the table at 56D2H.
Store 01H to the lines-per-page counter at 53F5H. After displaying the attribute line, the counter is set to 1 so that on the next file entry, the page counter will count down from 1 rather than 3.
550AH - Sector Count Calculator (Extent Chain Walker)
Walks the file's extent chain in the directory entry to count the total number of sectors allocated to the file. Each extent record is a 2-byte pair where one byte encodes the granule count (sectors in this extent) and the other encodes the continuation information. A sentinel value of FEH or above marks the last extent. Returns the total sector count in DE and extent count in BC. For multi-sector files that span beyond the current directory sector, reads additional sectors via SYS0.
Fetch the first byte of the current extent record from (HL) into Register A.
The sentinel was FEH, which means the extent chain continues in another directory sector. The second byte of the FEH record contains the sector number and entry index for the continuation. The code reads that sector and continues walking the chain.
Compare Register A against the value at 553CH. The operand at 553CH is patched by 5541H with the continuation sector index when reading an extended sector. If A matches this previously-read extended sector, the data is already in the 5900H buffer.
Store the new continuation sector index to 553CH. This patches the operand of the CP A,00H at 553BH so that future continuation checks against this sector will match without re-reading.
556CH - Display String with Printer Support
Displays a string via SYS0 routine 4467H, then checks the printer-active flag at 557DH. If the flag is non-zero, also outputs the string via SYS0 routine 446AH for printer output. This provides dual output (screen + printer) for the directory listing.
5577H - Character Output with Printer Counter
Outputs a single character via the ROM display routine at 0033H. Maintains a character counter in DE (initialized to 0000H at 557CH). Each call increments E; when E overflows from FFH to 00H (256 characters output), jumps to the ROM printer output routine at 003BH. This provides automatic line-end handling for printer output.
Load Register Pair DE with the character counter value at 557DH-557EH. The initial value is 0000H. The low byte at 557DH also serves as the printer-active flag read by 556FH. When a caller patches 557DH to non-zero, printer output is activated.
5584H - Parameter Error Display
Displays the "PARAMETER ERROR" message and exits to the DOS error handler. Called when the initial filespec open at 4476H fails.
558DH - "PARAMETER ERROR" String
ASCII string: PARAMETER ERROR followed by a carriage return (0DH).
559DH - DOS Error Exit
Error exit point used throughout the DIR command. Sets bit 6 of the error code (to suppress extended context display) and jumps to the SYS0 error handler at 4409H.
55A2H - 16-Bit to Decimal ASCII Conversion
Converts the 16-bit unsigned value in HL to a right-justified decimal ASCII string written to the buffer at (DE). Uses repeated subtraction by powers of 10 (10000, 1000, 100, 10, 1) via the helper at 55C2H. Leading zeros are suppressed (replaced with spaces) until the first significant digit is found.
55C2H - Decimal Digit Extraction Helper
Extracts one decimal digit by repeated subtraction of BC from HL. On entry, E (saved in D) is the leading-zero suppression character (space or digit). If the digit is zero and we are still in leading zeros (suppression char is space), outputs a space instead of 0. Once a non-zero digit is found, switches to outputting 0 for subsequent zero digits.
INCrement the digit counter in Register D.
Loop End
55DDH - 24-Bit Division Subroutine
Divides a 24-bit unsigned value stored at (HL) through (HL+2) by the 8-bit divisor in C. Returns the 16-bit quotient in DE. If C is zero, simply reads a 16-bit value from (HL) directly. Uses a shift-and-subtract long division algorithm: shifts the dividend left one bit at a time, testing and subtracting the divisor from the running accumulator.
Save the outer loop counter onto the stack.
Exchange DE and HL. HL now holds the quotient accumulator, DE holds H=dividend-byte, E was the low quotient byte.
Inner Loop End
Outer Loop End
5603H - Keyboard Pause Check
Checks for user input to pause or abort the directory listing. First tests the system state flags at 430FH bit 5 (re-entry mode), then polls the keyboard rows at 3840H, 3801H, and 3880H for BREAK key or other keypresses. If a key is detected, waits for the key to be released before returning. Returns with Z flag set if no abort requested, NZ if the user pressed a key to abort.
A key was detected that triggers a pause. The following loop waits for the user to press and release a key before continuing. This implements the "press any key to continue" pause behavior. The listing will resume after the keypress.
Fetch keyboard row 6 status from 3840H to check for BREAK key during the pause wait.
Loop End
562FH - Directory Column Header Template
32-byte display template used as the column header for the directory listing. Contains abbreviated column labels separated by TRS-80 semigraphics characters. Also serves as the file open work area passed to SYS0 at 4476H. The bytes include ASCII text and semigraphics delimiter characters (EDH, 40H, 7DH, 32H) that create visual column separators on the TRS-80 display.
5650H - Protection Level Name Table
Table of 8 protection level names, 4 bytes each (32 bytes total). Indexed by the 3-bit access level field from the directory entry status byte (bits 2-0). The access level multiplied by 4 gives the byte offset into this table. Each name is displayed in the directory listing to indicate the file's protection level.
5670H - Directory Header Banner String
Display string for the directory listing header. Contains a linefeed, the text "FILE DIRECTORY --- DRIVE X", trailing spaces, and an ETX (03H) terminator. The drive digit at offset 568AH is patched at runtime by 5271H.
5691H - Disk Name and Date Display Template
Display template for the disk name and date line. The first 8 bytes (5691H-5698H) are overwritten with the disk name from the GAT sector, followed by a separator, then 8 bytes (569DH-56A4H) are overwritten with the disk date. Terminated by 0DH (carriage return).
56A6H - Attribute and Size Display Template
Display template for the file attribute line. Contains the protection level name (4 bytes at 56A6H, filled from the table at 5650H), the text " / EOF=", the EOF count (5 digits at 56B1H, filled by decimal conversion), " / SIZ", separator character at 56BCH (= or :), the kilobyte count (5 digits at 56BDH), fractional digit at 56C3H, and " K " suffix. Followed by the date template at 56C8H.
56D2H - Month Name Table
Table of 12 three-character month abbreviations (36 bytes total). Indexed by month number (1-12, converted to 0-based). Used by the date formatting routine at 54D8H to look up the month name for directory entry dates.
56F6H - Filename and Extension Search Buffers
Work buffers used to hold the parsed filename (8 bytes) and extension (3 bytes) search pattern from the command line. Pre-initialized to spaces (20H), which act as wildcards during the filename comparison at 5350H. When the user specifies a filespec, the parser at 5213H writes characters into these buffers; any positions left as spaces will match any character in the directory entry.
ISAM 22H - FREE- Offset 0D43
5200H - FREE Command Main Entry
FREE command: Displays unallocated disk space and empty directory entries for all mounted drives. Loops through drives 0-7, printing a summary line for each drive present.
Store BC (0000H) into address 5312H. This initialises the two-byte field at 5312H-5313H which is used as the LD DE,0000H operand at 5311H. At runtime this field accumulates the free-file count returned by the drive-info call.
5212H - Per-Drive Loop Top
Top of the drive loop (drives 0-7). C holds the current drive number. Checks whether the drive-info word is non-zero and prints a leading space/CR if so.
5222H - Check Drive Present
Saves the drive counter, then calls SYS0 at 44B8H to test whether the current drive (C) is mounted. If the drive is not present, advances to the next drive.
5229H - Format Drive Number into Display Buffer
Converts the drive number in C to ASCII and stores it at 5398H, which is the "X" placeholder inside the "DRIVE X - " string at 5392H.
Store the ASCII drive digit into 5398H, which is the X placeholder byte within the "DRIVE X - " display string at 5392H. At runtime this byte holds the current drive number character.
522FH - Read Track 0 / Sector 0 (GAT)
Calls the local sector-read helper at 5376H to load track 0, sector 0 (the GAT sector) of the current drive into the buffer at 4200H. On failure, jumps to the error exit.
5235H - Compute Total Disk Space
Selects the drive parameter block via IY (CALL 478FH), then computes the total disk capacity in sectors from the drive parameter fields. The result is converted to kilobytes and stored in the display template.
5259H - Convert Total Sectors to Kilobytes
Converts the total sector count in A (and HL) to kilobytes, storing the result into the display template at 52E6H via the number-formatting routine at 5337H.
526DH - Store Total KB and Format
Stores the computed total-kilobytes value into the display template self-modify field at 52E6H, then calls the number formatter at 5337H to write the decimal digits into the "SPACE=" portion of the output string.
Store HL (total kilobytes) into 52E6H-52E7H. This is the operand of the LD DE,nnnn instruction at 52E5H which is used later to print the total-space field.
5278H - Compute Free Disk Space
Calls 4B8FH to multiply the sector count by sectors-per-granule, then derives the free kilobytes from the GAT bitmap. The result is formatted into the display template.
528DH - Copy Drive Name and Date from GAT Buffer
Copies the 8-byte drive name from the GAT buffer at 42D0H into the display template at 539CH, then copies the 8-byte date string into 53A5H.
52A0H - Count Used Directory Entries
Scans the GAT sector buffer from 4200H through the directory area, counting directory entries that are in use (high bit of entry byte clear and value not FFH). The count is accumulated in DE.
52ACH - Directory Entry Scan Loop
Scans each byte in the directory area of the GAT buffer. For each entry, checks bit 7 (in-use flag via RRA with carry) and whether the value is FFH (free slot). Counts used entries in DE.
Fetch the current directory entry byte from (HL) into A.
52B2H - Directory Entry Scan Continue
Checks whether the current entry byte is FFH (end-of-directory sentinel). If not FFH, advances HL and loops back. If FFH, exits the scan loop.
When B reaches zero, all directory entries have been scanned.
52B9H - Format Used Files Count
Exchanges DE and HL so HL holds the used-entry count, then pops the saved AF (sectors-per-track) and calls 4B8FH again for the free-granule count. Formats the used-files count into the display template.
52D0H - Read Directory Sector and Count Free Entries
Restores the drive counter from the stack, then reads the directory sector to count free (empty) directory slots. The free count is formatted into the display template and the completed line is printed.
52E2H - Count Free Directory Entries
Scans the directory sector buffer at 4200H counting entries whose first byte is 00H (free slot). The scan advances HL through the buffer checking each 32-byte entry header byte.
Load DE with 0000H. The operand at 52E6H-52E7H was written by LD (52E6H),HL at 526DH with the total-kilobytes value. At runtime DE is initialised to the total disk space in kilobytes for use in the display.
DECrement DE by 1. DE counts free directory entries, starting at -1 so the first INC DE produces 0.
52F3H - Advance Directory Scan Pointer
Advances HL to the next byte in the directory sector buffer. When L wraps through zero (full 256-byte sector scanned), exits the loop.
When L wraps to 00H the full 256-byte directory sector has been scanned.
52F6H - Format Free Files Count and Print Line
Exchanges DE and HL to put the free-entry count in HL, formats it into the display template at 53B5H, then calls the display-message routine to print the completed drive summary line.
5303H - Drive Loop Advance
Restores the drive counter from the stack, increments it, and loops back to check the next drive (0-7). When all 8 drives have been checked, exits via the DOS READY routine.
530EH - Display Message and Update File Count
Helper routine: calls 4467H to display the message at HL, then uses the 5311H/5312H self-modifying field to increment the printed-line count. If the count wraps to zero, jumps to 446AH to issue a pause.
Load DE with 0000H. The operand at 5312H-5313H is patched at 5203H with 0000H and incremented at 5314H. At runtime DE holds the count of drive summary lines printed so far.
5319H - Parameter Error Handler
Displays the "PARAMETER ERROR" message and exits to the error-already-displayed handler at 4030H.
5322H - "PARAMETER ERROR" Message String
ASCII message displayed when the FREE command is invoked with an invalid parameter. Terminated by 0DH (carriage return).
5332H - Error Exit with OR 40H
ORs the error code in A with 40H (sets bit 6 to suppress extended error context) and jumps to the DOS error exit at 4409H.
5337H - Decimal Formatter (Space Prefix Variant)
Formats the 16-bit value in HL as a decimal number into the buffer pointed to by DE. This variant prefixes the output with a single space character before calling the main formatter at 5349H.
533BH - Decimal Formatter (Full Width Variant)
Formats the 16-bit value in HL as a right-justified decimal number into the buffer at DE, with leading spaces for unused digit positions. Handles values up to 99,999 (5 digits). Entry at 533BH outputs all 5 digit positions; entry at 5337H prefixes one space then outputs 4 digit positions.
5349H - Decimal Formatter Hundreds Onward
Continuation entry point for the decimal formatter, handling the hundreds, tens, and units digits. Also the target of the JR from 5337H (space-prefix variant).
535BH - Single Digit Extractor
Extracts one decimal digit from HL by repeated subtraction of BC. Stores the digit (or a leading space if zero and no prior non-zero digit seen) into the buffer at DE. Updates DE and the leading-zero suppression character in A.
5360H - Digit Count Loop
Repeatedly subtracts BC from HL until the result goes negative (carry set). The number of successful subtractions is the digit value.
INCrement D. On first entry D becomes 00H; each subsequent iteration increments the digit count.
When carry is set, HL has gone negative; D holds the digit value.
5374H - Digit Extractor Exit
Advances the buffer pointer DE and returns. A holds either the ASCII digit just written (for non-zero) or the fill character (for suppressed zero), ready for the next digit extraction call.
5376H - GAT Sector Read Helper
Saves DE and HL, calls 4B65H to set up the track/sector for the GAT (track 0, sector 0), then reads it into the buffer at 4200H via 4B45H. Restores HL and DE on exit. Returns Z on success, NZ with A=14H on read error.
5389H - Drive Filespec Template
Null-terminated filespec string passed to the drive-open routine at 4476H. The byte at 5390H (12H) is the drive-specification character; 5391H (00H) is the null terminator.
5392H - Drive Summary Line Template
The display template for one drive summary line. Fields marked with placeholder bytes are filled in at runtime by the FREE command before calling 530EH to display the line. The complete formatted output is: "DRIVE X - NNNNNNNN MM/DD/YY FILES= NNN/ NNN, SPACE= NNN/ NNN K".
ISAM 91H - CHAIN - Offset 0F20
VTOS 4.0 SYS6/SYS CHAIN Command Disassembly - JCL Processor (Model I)
The CHAIN command is one of the most sophisticated components of VTOS 4.0. It implements a complete Job Control Language (JCL) interpreter, allowing the user to execute a sequence of DOS commands stored in a file with the extension /JCL on the system disk. CHAIN is the direct ancestor of the LDOS DO command and the foundation of VTOS batch processing.
When invoked, CHAIN reads a SYSTEM/JCL file from the system drive. The file contains a sequence of command lines interspersed with JCL directives beginning with //. CHAIN executes each command line by submitting it to the DOS command interpreter via SVC 99H (RST 28H with A=99H). JCL directives are processed internally by this overlay without returning to the DOS command level.
A 32-entry symbol table is maintained in RAM at 5C00H. Each symbol occupies 33 bytes: 8 bytes for the name (space-padded, uppercase) and 1 byte for the value. Symbols are referenced in command lines using the #name# notation. The //SET directive assigns a value (0 or non-zero) to a symbol; //RESET inverts the value; //ASSIGN copies a value. The //IF and //ELSE///END directives provide conditional execution using a push-down stack at 58F6H-5918H.
Three entry points are defined for this overlay: ISAM 91H at 5200H (full CHAIN with JCL compile and execute), ISAM A1H (not in this chunk - alternate entry), and the = prefix entry at 52BFH which skips the compile phase and re-executes the previously compiled JCL buffer. The * prefix entry at 52D7H re-runs the last CHAIN command by restoring the saved filespec.
Variable and Buffer Reference
| Address Bytes | Purpose |
|---|---|
| 56D2H 32 | Filespec Work Buffer Holds the current JCL filespec being constructed or parsed. Initialized from the command line. Saved/restored when processing nested INCLUDE files. |
| 56F2H 8 | Symbol Name Buffer Fixed-width 8-byte field, space-padded, uppercase. Filled by the token scanner at 55C0H. Used as search key for the symbol table lookup at 5628H and keyword table lookup at 563AH/564DH. |
| 56FAH 1 | Symbol Name Length Set by 5582H to the number of significant characters in the name (0-32). Also serves as the first byte of the combined symbol entry when writing to the table. |
| 56FBH 32 | Symbol Value Buffer Holds the current symbol value string (up to 32 characters). Filled by the value scanner at 5582H. Written to the symbol table by 561DH. |
| 571BH 8 | JCL Filename String Contains "SYSTEM/JCL" + 03H. Used as the filename when opening the JCL file via CALL 4476H. The 03H byte terminates the string for the SYS0 filespec parser. |
| 5722H 4 | JCL Extension String Contains "JCL" + 03H. Passed to CALL 4473H (insert default extension) when building the filespec. |
| 57B2H 2 | JCL Output Buffer Pointer 16-bit pointer into the compiled JCL output area at 5959H-59FFH. Advanced as command lines are written during the compile phase. Reset to 5959H at start of execute phase. |
| 57B4H 2 | JCL Buffer End Sentinel Marks the end of valid compiled data in the JCL output buffer. Used at 527EH to detect buffer exhaustion. |
| 58F4H 2 | Conditional Stack Pointer 16-bit pointer into the IF/ELSE/END condition stack at 58F6H. Initialized to 58F6H (empty stack). Each //IF pushes one byte; //END pops one. The byte value encodes: 00H = condition true (execute), FFH = condition false (skip). |
| 58F6H 34 | Conditional Execution Stack Push-down stack for nested //IF conditions. Each byte is 00H (true/executing) or FFH (false/skipping). The stack grows upward from 58F6H; 58F4H tracks the current top. Maximum nesting depth is approximately 34 levels. |
| 5916H 3 | Error/EOF Marker String Contains "--" + 00H. Displayed when a JCL error occurs (via CALL 447BH at 53ACH). The 00H terminator distinguishes it from a live command line. |
| 5919H 64 | JCL Line Input Buffer Receives raw lines read from the JCL file by the line reader at 53E8H. Also used as source for the compile-phase line assembler at 531BH-533AH. DE tracks the write pointer into 5959H during line assembly. |
| 5959H variable | Compiled JCL Output Buffer Holds the assembled output from the compile phase. Each entry is a complete command line terminated by 0DH, with //directives expanded in-place. The execute phase at 5312H reads from this buffer sequentially via 57B2H. |
| 5C00H 33x32=1056 | Symbol Table 32 symbol entries, each 33 bytes. Bytes 0-7: symbol name (space-padded uppercase). Byte 8: value byte (00H = false/unset, non-zero = true/set, FFH = multiply-defined or inverted). Searched by 5628H using 8-byte name comparison with the content of 56F2H. |
Major Routine Reference
| Address | Name and Purpose |
|---|---|
| 5200H | CHAIN Entry Point - Full Execute Main entry. Dispatches on first command character: '*' goes to 52D7H (rerun last), '=' goes to 52BFH (skip compile), '$' strips prefix. Otherwise falls through to filespec extraction, JCL file open, compile phase, and execute phase. |
| 52BFH | CHAIN Entry Point - Skip Compile '=' prefix entry. Skips the JCL file read and compile phase. Opens the JCL file directly and begins the execute phase at 52AEH, re-using the previously compiled buffer at 5959H. |
| 52D7H | CHAIN Entry Point - Rerun Last '*' prefix entry. Restores the saved filespec from 571BH into the work buffer at 56D2H, then falls through to the normal open/execute path at 52AEH. |
| 52E1H | Parameter List Parser Handles the '(' parameter list syntax "CHAIN file (sym=val, sym=val)". Loops reading symbol name / '=' / value triplets separated by commas. Calls 5577H/5628H/560BH/5582H/561DH to process each assignment. Returns when ')' or CR is seen. |
| 530FH | Compile Phase Entry Reads the JCL file line by line via 53E8H. Passes each line through the line assembler at 531BH which copies characters to the output buffer at 5959H, performing #symbol# substitution inline. Continues until the file returns EOF (1CH flag). |
| 5312H | Execute Phase Main Loop Reads the next compiled command line from the output buffer via 57B2H. Checks the conditional stack via 58F4H. If condition is true, submits the line to the DOS via RST 28H (SVC 99H). Handles //directive lines by calling the keyword dispatcher at 563AH/564DH. |
| 5340H | PAUSE Keyword Handler Detected during the compile phase when a line begins with 'P'. Verifies the full word "PAUSE" by calling 5367H for each letter. If confirmed, shifts the compiled buffer content and inserts "//" prefix so the execute phase treats it as a directive. |
| 5367H | Character Match with Backtrack Compares A against the character at (HL). If match, advances HL and returns. If no match, pops two levels of return address (HL and caller's HL), loads 'P' into A, and returns - effectively signalling a failed keyword match to the caller. |
| 536FH | Symbol Substitution Handler Called when '#' is seen in a line. Looks up the symbol name in the table via 5628H. If found and non-zero, copies the symbol's value string into the output buffer at DE. If not found or zero, writes the literal "#...#" text unchanged. |
| 53A9H | JCL Format Error Handler Displays the "--" error prefix from 5916H via CALL 447BH, then displays the "INVALID JCL FORMAT" message from 578AH via CALL 447BH, then exits to 4030H (error-already-displayed exit). |
| 53B8H | Double-Slash Line Dispatcher Called when '//' is seen at the start of a line during the execute phase. Reads the keyword via 5577H and searches both the compile-phase keyword table (563AH) and the execute-phase keyword table (564DH). Dispatches to the appropriate handler or falls through to error. |
| 53E8H | JCL File Line Reader Reads characters from the JCL file (FCB at 4358H, file at 5B00H) one at a time via ROM CALL 0013H. Stores into 5919H until CR or EOF (1CH). Returns with Z set if successful, NZ if EOF or read error. Sets A=1CH on EOF. |
| 5406H | Compiled Line Output Sender Reads the current compiled line from the output buffer (57B2H pointer). Sends each character to the device via ROM CALL 001BH. Handles '//' detection: if the line begins with '//', returns without sending (execute phase handles it as a directive). Returns after CR. |
| 5481H | Execute-Phase Keyword Dispatcher Main dispatch for //IF, //ELSE, //END, //SET, //TRESET, //UASSIGN, //INCLUDE, //QUIT keywords during the execute phase. Reads keyword, calls 54D0H to get condition value, then routes to the appropriate handler. |
| 5491H | //IF Handler Evaluates the condition expression (calls 54D0H). Pushes 00H (true) or FFH (false) onto the conditional stack at 58F6H via 58F4H pointer. Advances 58F4H. Returns to execute phase main loop at 5312H. |
| 54A2H | //ELSE Handler Checks that the stack is not empty (58F4H != 58F6H). Inverts the top-of-stack byte using CPL and OR with previous level. This implements the standard IF/ELSE toggle so that the ELSE block executes when the IF block did not. |
| 54AEH | //END Handler Pops the conditional stack by decrementing 58F4H. Verifies stack is not underflowing below 58F6H. Returns to execute phase main loop. |
| 54BAH | //END Handler (alternate) Second END handler variant. Decrements 58F4H and returns to main loop at 5312H without the underflow check. Used when a //END is encountered during the skip (false) phase. |
| 54D0H | Condition Expression Evaluator Reads a symbol name via 5577H and looks it up in the symbol table via 5628H. Returns Z set if the symbol value is zero (false condition), NZ if non-zero (true condition). Handles the '-' NOT prefix by inverting the result. Skips delimiters (commas, spaces). |
| 54DAH | NOT Operator Handler Detects '-' prefix on a symbol name. If present, evaluates the symbol (calls 54E9H) and if result is Z (false), sets bit 0 to return true; if NZ, returns false. Implements logical NOT for //IF conditions. |
| 54E9H | Symbol Evaluator Calls 5577H to read the symbol name, then 5628H to look it up. Returns with the Z flag from the table lookup: Z = symbol not found or value zero, NZ = symbol found with non-zero value. |
| 54F6H | //SET Inline Handler Reads symbol name, looks up or creates table entry, writes non-zero value (sets symbol true). Called during both compile and execute phases. |
| 5505H | //RESET Inline Handler Reads symbol name, looks up table entry. If symbol exists, writes zero value (sets symbol false). If not found, no action. |
| 551AH | //ASSIGN Handler Reads a symbol name and value pair separated by '='. Looks up or creates the symbol. Copies the value string into the table entry via 561DH. |
| 5539H | //INCLUDE Handler Saves the current output buffer pointer (57B2H) to a temporary. Opens a new JCL file specified on the directive line (filespec via 441CH, extension via 4473H). Reads and compiles the included file into the output buffer. Restores context on return. |
| 5568H | //QUIT / Exit Handler Displays the current JCL line from 5919H via CALL 447BH. Closes the JCL file (FCB at 4358H via CALL 4428H). Exits to 402DH (DOS READY - no error). |
| 5577H | Symbol Name Token Scanner Reads up to 8 characters from (HL) into the symbol name buffer at 56F2H. Calls 55C0H with B=8, DE=56F2H. Space-pads the remainder of the 8-byte field. Advances HL past the token. Returns with A = first delimiter character. |
| 5582H | Symbol Value Token Scanner Reads up to 32 characters from (HL) into the value buffer at 56FBH. Calls 55A1H with B=32, DE=56FBH. Stores the actual length (0-32) at 56FAH. Returns NZ if value exceeded 32 characters (error). |
| 55A1H | Delimiter-Aware Character Scanner Advances HL collecting non-delimiter characters into DE buffer. Stops and returns when it encounters space, comma, CR, '=', '(', ')' or '#'. The '#' case calls 536FH to perform symbol substitution before continuing. |
| 55C0H | Fixed-Width Token Collector Reads up to B characters from (HL) into the buffer at DE. Converts lowercase a-z to uppercase. Stops at control characters (03H, 0DH), '(', or characters below '0'. Space-pads the remainder of the B-character field. Returns A = terminating character, C = last significant character. |
| 560BH | Clear Symbol Name Entry Copies 8 spaces from 56F2H into the symbol table entry at DE. Writes 00H at DE+8 (value byte) and at DE+33 (next entry's first byte). Effectively initializes a new empty symbol slot. |
| 561DH | Write Symbol Value Entry Copies 33 bytes (1 length byte + 32 value bytes) from 56FAH into the symbol table entry at DE. LDIR with BC=0021H. Used by //SET, //ASSIGN, and the parameter list parser to record a symbol value. |
| 5628H | Symbol Table Search Searches the symbol table at 5C00H for the 8-byte name currently in 56F2H. Uses the generic table search engine at 5660H with B=8 (entry count), C=29H (entry stride = 41 bytes). Returns DE pointing to the found entry (or first empty slot), Z set if found. |
| 563AH | JCL Compile-Phase Keyword Lookup Searches the keyword table at 5435H for the name in 56F2H. Uses 5660H with B=6 (entries), C=08H (stride). Returns DE = address of the 2-byte handler address following the matched keyword. Used during the compile phase to recognize //IF, //ELSE, //END, //SET, //RESET, //ASSIGN. |
| 564DH | JCL Execute-Phase Keyword Lookup Searches the keyword table at 544EH for the name in 56F2H. Uses 5660H with B=8 (entries), C=0AH (stride). Returns DE = address of the 2-byte handler address. Used during the execute phase to recognize all keywords including //INCLUDE and //QUIT. |
| 5660H | Generic Table Search Engine Searches a table at HL for an 8-byte key at DE. B = number of entries, C = entry stride in bytes. On match, advances HL past the key and returns Z set with HL pointing to the data following the key. On no match, returns NZ with A = 01H. |
| 56A9H | JCL Error Exit ORs A with 40H (sets error flag bits), then jumps to SYS0 error handler at 4409H. Used by all parameter error paths throughout the overlay. |
| 56AEH | Error: File Spec Required Displays "FILE SPEC REQUIRED" message from 5726H via CALL 447BH, then exits to 4030H. |
| 56B7H | Error: Cannot Create JCL File Displays "CANNOT CREATE SYSTEM" + continuation message from 573AH via CALL 447BH, then exits to 4030H. |
| 56C0H | Error: Parameter Error Displays "PARAMETER ERROR" from 575EH via CALL 447BH, then exits to 4030H. |
| 56C9H | Error: Multiply Defined Parameter Displays "MULTIPLY DEFINED PARAMETER" from 576EH via CALL 447BH, then exits to 4030H. |
5200H - CHAIN Entry Point - Prefix Dispatcher
Main entry point for the CHAIN command. Examines the first character of the command tail to select operating mode: * reruns the last CHAIN filespec, = re-executes without re-reading the file, $ marks a batch-mode invocation. All paths converge on filespec extraction at 5219H.
Load Register A with the first character of the command tail. HL points to the DOS command line buffer on entry from the overlay dispatcher.
Store Register A (24H = $) into address 52A9H. Address 52A9H is the immediate operand of the LD A,00H instruction at 52A8H. At runtime this patches that instruction to become LD A,24H, recording that a batch-mode $ prefix was used. The patched value is tested later at 52AAH to suppress the "command not found" error exit.
5219H - Filespec Extraction and JCL File Open
Extracts the JCL filespec from the command line, inserts the default "JCL" extension, opens a work FCB at 5A00H, copies the "SYSTEM/JCL" filename into the FCB at 4358H, and reads the first record of the JCL file into the buffer at 5B00H.
5251H - Command Tail Parameter Scanner
Scans the remainder of the command tail after the filespec. Skips spaces and commas. A ( character branches to the parameter list parser at 52E1H. A ; character triggers an interactive prompt. Any other non-CR character is a parameter error. On CR the scan is complete and execution falls through to the command submission loop at 5312H.
Load Register A with the current character from the command tail.
Load Register A with 3FH (ASCII: ?). The ; character on the command tail requests an interactive parameter prompt.
527EH - Buffer End Check and Command Submission Loop
Checks whether the command output buffer pointer (57B2H) has reached the end sentinel (57B4H). If so, closes the file and exits. Otherwise, uses LDDR to extract the next command line from the buffer, submits it to DOS via RST 28H (SVC 99H), then loops.
Load Register Pair HL from (57B2H). Fetch the current command buffer read pointer.
52A2H - Buffer Exhausted - Close FCB and Exit
All compiled command lines have been processed. Closes the system FCB at 4358H and exits to DOS READY.
Load Register A with 00H. This instruction's operand byte at 52A9H was patched to 24H by the code at 520FH if a $ prefix was used. If patched, A = 24H (batch mode); otherwise A = 00H (normal mode).
52AEH - Command Submission via RST 28H
Opens the command buffer FCB at 4358H with the data buffer at 4200H, then issues RST 28H with A=99H to submit the current command line to the DOS command interpreter for execution.
52BFH - CHAIN '=' Entry - Re-Execute Without File Read
Entry point when the command is prefixed with '='. Skips the JCL file compile phase. Extracts a new filespec from the command tail, inserts the default extension, and jumps to 52AEH to open and submit directly.
52D7H - CHAIN '*' Entry - Rerun Last Filespec
Entry point when the command is prefixed with '*'. Restores the saved "SYSTEM/JCL" filename from 571BH into the work area via LDIR, then jumps to 52AEH to open and submit directly using the last-used filespec.
52E1H - Parameter List Parser
Processes an inline parameter list of the form (sym=val, sym=val). Reads symbol name tokens via 5577H, looks them up via 5628H, and assigns values via 5582H/561DH. Loops on spaces and commas. A closing ) returns to the main command tail scanner at 5251H.
GOSUB to 5577H to read the next token from the command tail into HL. Returns A = delimiter character that terminated the token, Z FLAG set if token was empty.
5305H - Symbol Assignment Handler
Reads the value token following a '=' delimiter via 5582H, stores it via 561DH, then loops back to the delimiter scanner at 52F5H.
530FH - Output Buffer Check and JCL Record Read Entry
Two consecutive entry points. 530FH first flushes the output buffer via 5406H. 5312H reads the next JCL record from the open file via 53E8H. If the read fails (end of file or error), jumps to the buffer management loop at 527EH.
5318H - JCL Line Processing Loop
Scans the JCL record just read into the line buffer at 5919H, copying processed characters to the output buffer at 5959H via DE. Handles the 'P' prefix (inserts "AUSE" to form "PAUSE"), '//' end-of-job marker, and '#' symbol substitution. On carriage return, loops back to read the next record.
Load Register A with the current character from the JCL line buffer.
5340H - PAUSE Keyword Completion Subroutine
Called when a P is found in the JCL stream. Attempts to match "AUSE" in the remaining input to confirm the word is "PAUSE". Uses 5367H to match each character in sequence. If the match fails, 5367H unwinds the call stack and returns 'P' to the main loop. On success, inserts the "AUSE" substring into the output and writes a '//' end-of-job marker.
5367H - Character Match and Advance Subroutine
Compares Register A against the character at (HL). If they match, advances HL and returns normally (Z FLAG set). If they do not match, pops two stack frames (unwinding the CALL 5340H and its caller) and returns 50H ('P') to the JCL line processing loop, effectively abandoning the PAUSE keyword match.
Pop Register Pair HL from the stack. Discards the return address of this subroutine call (one of the four CALL 5367H sites in 5340H).
536FH - Symbol Substitution for '#' Token
Called when a '#' character is found in the JCL stream. Attempts to find and expand a symbol reference. If the next character matches a '#' in the input, looks up the symbol via 5628H and copies its value into the output buffer via LDIR. If no match or symbol not found, copies the '#' and following characters literally until the next '#' or CR.
Pop Register Pair DE from the stack. Recovers the output buffer pointer for literal '#' copy below.
Store Register A into the output buffer at (DE). Copies the current character literally.
53A9H - JCL Syntax Error Exit
Displays two error messages - a JCL-specific message from 5916H and a general error message from 578AH - then exits via SYS0 error-already-displayed exit at 4030H.
53B8H - End-of-Job Handler
Processes the '//' end-of-job marker found in the JCL stream. Reads the next token via 5577H, then checks the buffer state via 563AH and 564DH. If a valid entry is found it is pushed onto the stack; otherwise the line buffer is refilled from the saved source via LDIR.
Point Register Pair DE to 5919H - the start of the JCL line input buffer. This is the destination for the LDIR restore below.
53E8H - Read Next JCL Record into Line Buffer
Reads characters one at a time from the device at 56D2H via ROM 0013H into the line buffer at 5919H. Reads until carriage return (0DH) or end-of-file (1CH). Returns Z FLAG set if a line was read successfully, NZ (with A=1CH) if end-of-file was reached.
GOSUB to ROM routine at 0013H to get one byte from the input device at DE (56D2H). Returns A = byte received, Z FLAG set if device ready.
5406H - Output Buffer Check and Flush
Checks whether the output command buffer pointed to by (58F4H) is empty. If empty, copies the current output line from 5959H into the FCB at 4358H and transmits it character by character via ROM 001BH. Handles the special case where the line begins with '//' by calling 4467H to display it rather than transmitting.
Load Register A with the current character from the output line buffer.
5435H - JCL Keyword Dispatch Table
Data table of JCL keyword strings and their handler addresses, used by the command dispatcher. Each entry is 8 bytes: a 6-character keyword (space-padded) followed by a 2-byte little-endian handler address. The table is terminated by a 00H entry. The disassembler has misinterpreted these bytes as Z80 instructions - they are pure data.
5481H - JCL Keyword Command Dispatcher
Entry point for processing recognised JCL keywords (IF, ELSE, TEND, SET, RESET, ASSIGN, INCLUDE, QUIT). Reads an optional condition token via 54D0H, then dispatches to the appropriate handler based on the keyword type byte. Updates the condition flag byte at (58F4H).
54A2H - RESET Command Handler
Implements the JCL RESET keyword. Validates that the output buffer pointer has not reached its limit at 58F6H. Complements the current flag byte at (58F4H) by ORing its complement with the previous byte, then continues JCL processing.
54BAH - QUIT / INCLUDE Command Handler
Implements the JCL QUIT and INCLUDE keywords. Validates the buffer pointer against the limit at 58F6H. Decrements the buffer write pointer at (58F4H) and continues JCL processing.
54D0H - Condition Token Reader
Reads an optional condition token from the JCL line. Calls 54DAH to get a token character. If the character is a dot separator, loops to skip it and read again. Returns Z FLAG set if the condition evaluates true, NZ if false.
GOSUB to 54DAH to read the next token character from the JCL line. Returns A = character, Z FLAG set if a valid token was found.
54DAH - Token Character Reader with Negation Flag
Reads the next character from the JCL token stream. If a '-' prefix is found, calls 54E9H to get the actual token and sets bit 0 of the result to flag negation. Returns A = token character with optional negation flag, Z FLAG set if a valid token was read.
54E9H - Token Character Fetch via 5577H
Reads the next token character from the JCL line via 5577H. If the read is successful, saves AF and HL on the stack, looks up the token via 5628H, then restores BC (from saved AF) as the result. Returns A = original token character, Z FLAG set if token was valid.
54F6H - SET Command Handler
Implements the JCL SET keyword. Reads a token via 5577H, looks it up in the symbol table via 5628H, and if not found calls 560BH to create a new entry. Then continues JCL processing.
5505H - RESET Command Handler
Implements the JCL RESET keyword. Reads a token via 5577H, looks it up in the symbol table via 5628H. If the symbol is found, clears it by storing a space (20H) into the symbol value field. If not found, skips silently and continues JCL processing.
551AH - ASSIGN Command Handler
Implements the JCL ASSIGN keyword. Reads a variable name token via 5577H, optionally creates a symbol table entry via 560BH, then validates that an equals sign follows. Reads the value token via 5582H and stores it into the symbol table entry via 561DH.
5539H - INCLUDE Command Handler
Implements the JCL INCLUDE keyword. Saves HL on the stack, copies 32 bytes from the current overlay parameter block at (57B2H) to the template buffer at 56D2H, then extracts a filespec from the JCL line via 441CH. Inserts a default extension via 4473H, opens the file via 4424H, and continues JCL processing. On any error, jumps to 53A9H.
5568H - QUIT Command Handler
Implements the JCL QUIT keyword. Displays the message at 5919H via 447BH, closes the device FCB at 4358H via 4428H, then exits cleanly to the DOS READY prompt via 402DH.
5577H - Token Reader (8-char limit, delimiter set A)
Reads the next token from the JCL line into the token buffer at 56F2H. Calls 55C0H with B=08H (8-character limit) and DE pointing to 56F2H. Preserves and restores DE around the call. Returns A = delimiter character that terminated the token, NZ if no characters were read.
5582H - Value Token Reader (32-char limit, delimiter set B)
Reads a value token from the JCL line into the value buffer at 56FBH. Calls 55A1H with B=20H (32-character limit) and DE pointing to 56FBH. After the read, computes the length of the stored value and validates it does not exceed 20H (32) characters. Stores the length at 56FAH. Preserves and restores DE around the call.
Store Register A (value length) to (56FAH). Saves the computed length byte immediately before the value buffer at 56FBH. This length byte is used by 561DH when copying the value into the symbol table.
55A1H - Character Reader with Delimiter Detection
Reads characters from the JCL line (via HL) into the buffer at DE, stopping when a recognised delimiter is found. Delimiters are: space (20H), comma (2CH), carriage return (0DH), equals (3DH), open paren (28H), close paren (29H), hash (23H). If a hash comment marker is encountered, calls 536FH to skip to end of line. Converts lowercase to uppercase (SUB 20H). Stores up to B characters.
GOSUB to 55C0H to read the next character from the JCL line and store it into the buffer at DE (if valid). Returns A = delimiter or character; Z FLAG set if character was stored.
55C0H - Single Character Fetch and Store
Reads one character from the JCL line at (HL), converts lowercase to uppercase, and stores it into the output buffer at (DE) if it is a valid alphanumeric character. Pads the remainder of the output buffer with spaces if the character count limit B is reached. Returns A = the character (or delimiter), with the overflow counter stored at 5607H. The NZ FLAG is set if the character was not stored (delimiter or buffer full).
Store Register A (current remaining count) to (5607H). This location is used at 5607H as the operand for an OR instruction that reflects whether any characters remain in the buffer limit.
Load Register A with (HL). Fetches the next character from the JCL line scan pointer.
Store 00H to (5607H). Clears the overflow flag now that a character has been successfully stored.
Load Register A with 20H (space). Prepares a space character for padding the remainder of the output buffer.
Decrement Register B and JUMP to 55FFH if B is not zero. Continues padding with spaces until the full buffer width is filled.
OR Register A with itself. The byte at 5607H was written by the LD (5607H),A instructions at 55C1H and 55F2H. If 5607H is non-zero (overflow - buffer was full before delimiter), this OR sets the NZ FLAG; if 5607H is zero (normal), Z FLAG remains set.
560BH - Symbol Table Entry Initialiser
Creates a new symbol table entry at DE. Copies the 8-byte token name from the token buffer at 56F2H into the entry at DE via LDIR, stores a 00H terminator at DE+8, then stores a 00H at DE+0x21 (DE+33) to mark the end of the value field. Preserves and restores HL.
561DH - Symbol Value Copy into Table Entry
Copies 33 bytes (21H) from the value buffer at 56FAH into the symbol table entry pointed to by DE. The first byte at 56FAH is the length byte stored by 5582H; the following 32 bytes are the value string. Preserves and restores HL.
5628H - Symbol Table Lookup (User Variables)
Searches the user variable symbol table at 5C00H for a match against the 8-character token in the buffer at 56F2H. Calls the generic table search routine at 5660H with B=08H (8-char key), C=29H (41-byte entry stride). On return, HL points to the matched entry in the table. Saves the result into DE before restoring HL and returning.
563AH - Keyword Table Lookup (JCL Commands)
Searches the JCL keyword dispatch table at 5435H for a match against the 6-character token in the buffer at 56F2H. Calls the generic table search routine at 5660H with B=06H (6-char key), C=08H (8-byte entry stride). On match, loads DE with the 2-byte handler address from the matched entry. Preserves and restores HL.
564DH - Operator Table Lookup
Searches a second keyword table (at 544EH) for a match against the token in the buffer at 56F2H. Called when comparing JCL conditional operators. Uses B=08H (8-char key) and C=0AH (10-byte entry stride). On match, loads DE with the 2-byte handler address from the matched entry. Preserves and restores HL.
5660H - Generic Table Search Routine
Searches a fixed-stride table at HL for an entry whose first byte matches (DE). Entry stride is C bytes, key length is B characters. On a first-byte match, compares all B characters of the key at DE against the entry at HL. Handles wildcard matching: if a table entry character is a space (20H) or carriage return (0DH), it matches any alphanumeric in the key. On full match, returns HL pointing past the matched name, Z FLAG set. On no match advances HL by C bytes and repeats. Returns NZ if the table is exhausted (first byte of entry is 00H).
Load Register A with (HL). Fetches the first byte of the current table entry.
Save Register Pair HL onto the stack. Preserves the table entry pointer for potential restoration if the full key comparison fails.
Increment Register Pair DE. Advances the search key pointer to the next character.
Decrement Register B and JUMP to 5674H if B is not zero. Continues comparing the remaining characters of the key.
56A9H - JCL Error Exit (Value Too Long)
Called when a value token exceeds the maximum length. ORs 40H into A to set error flags, then calls the DOS error handler at 4409H.
56AEH - Error Message Display Stubs
Four short routines that each load a pointer to a specific error message string, call 447BH to display it, then jump to 4030H (error-already-displayed exit). These handle JCL-specific error conditions.
571BH - String Data Area
Pure ASCII string data used as messages and default extension strings. The disassembler has misinterpreted these bytes as Z80 instructions - they are DEFM data. Strings are terminated by 0DH (carriage return) or 03H (end-of-text). Each string is referenced by the error display stubs at 56AEH-56CFH and by the INCLUDE handler at 5554H.
58F4H - Data Bytes
Two isolated data bytes at 58F4H. Their purpose within the overlay is unclear without further context of what references this address.
16-bit pointer into the IF/ELSE/END condition stack at 58F6H. Initialized to 58F6H (empty stack). Each //IF pushes one byte; //END pops one. The byte value encodes: 00H = condition true (execute), FFH = condition false (skip).
Push-down stack for nested //IF conditions. Each byte is 00H (true/executing) or FFH (false/skipping). The stack grows upward from 58F6H; 58F4H tracks the current top. Maximum nesting depth is approximately 34 levels.
5916H - QUIT Message String
Short data area containing the QUIT command message. The bytes at 5916H-5917H are ASCII dashes forming a separator, followed by a 0DH terminator. Referenced by the QUIT handler at 5568H via LD HL,5919H - note the QUIT handler points two bytes into this area, skipping the leading dashes.
5C00H - User Variable Symbol Table
Start of the user variable symbol table, referenced by the symbol table lookup routine at 5628H via LD HL,5C00H. Each entry is 41 bytes: an 8-character space-padded symbol name, a 00H name terminator, a 32-byte value field, and a 00H value terminator. The table is searched sequentially; a 00H in the first byte of an entry signals the end of the table. At load time this area is uninitialised - the single 00H byte shown by the disassembler represents the empty table with no symbols defined.
ISAM A1H - SYSTEM Command - Offset 14B9
VTOS 4.0 SYS6/SYS - SYSTEM Command Disassembly (IDAM A1H, Member 14H) - Model I
The SYSTEM command is VTOS 4.0's master configuration utility, allowing the user to change the operating configuration of the system in memory. It is loaded as IDAM A1H from the SYS6/SYS Partitioned Data Set overlay library, occupying Member 14H at file offset 14H/00B9H, and loads into memory at 5200H.
SYSTEM accepts a wide range of keyword parameters that control processor speed (FAST/SLOW), ROM BASIC entry, BREAK key detection, cursor blinking (BLINK/LARGE/SMALL), type-ahead keyboard buffering, the JKL screen-print feature, graphic printer mode, drive configuration (DRIVE/ENABLE/DISABLE/STEP/DELAY), and SYSGEN (saving the configuration to disk). The command parses its parameter list via a variable pointer table at 5679H, which maps each keyword to its handler routine address.
The architecture uses SYS0 routine 4476H for parameter table lookup and dispatching. Each keyword handler receives control with Register Pair BC loaded with the parsed parameter value (0000H for absent/OFF, 0001H for present/ON, or a numeric value). Many handlers test BC to determine ON/OFF/value state and then modify the system state flags at 430FH, install or remove interrupt-driven callback tasks via SYS0 routines 4410H/4413H, or directly manipulate hardware ports.
Notable features include the BASIC parameter which copies a 54-byte ROM re-entry stub from 06D2H to the video RAM base at 4000H before jumping to the warm-start entry at 0075H; the TYPE parameter which installs a 214-byte type-ahead keyboard driver as a scheduled task; the JKL parameter which installs a 78-byte screen-print driver; and the SYSGEN parameter which saves the current system configuration to the system disk in drive 0.
The SYSTEM command also includes a "CAN'T" error check at 5627H which verifies that the command was issued from the VTOS command level (402DH must contain C3H/JP) and rejects the command with "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" if called from within a running program.
Program Flow Summary
| Address Range | Purpose |
|---|---|
| 5200H | Entry point Load parameter table pointer (5679H), CALL 4476H to parse and dispatch. |
| 5209H-522EH | FAST/SLOW handlers Toggle 430FH bit 3 and write to port 0FEH for processor speed. |
| 522FH-524BH | BASIC handler Copy ROM re-entry stub to 4000H, JP 0075H for BASIC warm start. |
| 524BH-526EH | BREAK handler Enable/disable BREAK key detection via 430FH bit 4 and 4C2DH. |
| 526FH-5305H | BLINK handler Set cursor character, install/remove blink task. |
| 5305H-547FH | TYPE handler Install type-ahead keyboard driver as scheduled task. |
| 547FH-54DDH | SLOWER/JKL-related handler Install screen-print driver task. |
| 54DDH-5572H | JKL handler Install screen-print interrupt handler. |
| 5572H-55B4H | GRAPHIC/ALIVE handler Toggle alive/graphic status. |
| 55B4H-5619H | DRIVE/ENABLE/DISABLE/STEP/DELAY handlers Drive configuration. |
| 5619H-5626H | SYSGEN handler Save configuration to system disk. |
| 5627H-565FH | Utility routines and error messages. |
| 5679H-5709H | Parameter keyword table (FAST, SLOW, BASIC, BREAK, BLINK, LARGE, SMALL, TYPE, SLOWER, JKL, GRAPHIC, ALIVE, DRIVE, ENABLE, DISABLE, STEP, DELAY, SYSGEN) |
Key Memory Locations Referenced
| Address Range | Purpose |
|---|---|
| 430FH (1 byte) | System state flags - bit 3: FAST mode, bit 4: BREAK enabled, bit 5: type-ahead re-entry |
| 4049H (2 bytes) | High memory pointer - decremented when installing task code blocks |
| 4015H (6 bytes) | IX base for task installation (timer callback slots) |
| 401DH (6 bytes) | IX base for SLOWER/JKL task installation |
| 4C2DH (1 byte) | BREAK key handler byte (00H = disabled, C8H = enabled) |
| 43BEH (2 bytes) | Saved keyboard vector for type-ahead (when 430FH bit 5 set) |
| 439CH (1 byte) | Type-ahead flag byte (patched to 21H during TYPE install) |
| 4BC1H (2 bytes) | Type-ahead buffer overflow pointer |
| 4536H (1 byte) | JKL handler activation opcode (C4H = active, CCH = reset) |
| 4537H (2 bytes) | JKL handler target address (0000H = idle) |
| 3C3FH (1 byte) | Video RAM end-of-row marker byte (toggled between 86H and 89H for ALIVE) |
| 5679H-5709H (137 bytes) | Parameter keyword table - 18 entries mapping keyword strings to handler addresses |
Major Routines
| Address | Name and Purpose |
|---|---|
| 5200H | SYSTEM Entry Point Load DE with parameter table at 5679H, CALL 4476H to parse command line keywords and dispatch to handlers. On no match, JP 5660H for "PARAMETER ERROR". |
| 5209H | FAST/SLOW Handlers Toggle 430FH bit 3 and output to port 0FEH to switch processor clock speed. |
| 522FH | BASIC Handler Copy ROM re-entry stub from 06D2H to 4000H (54 bytes), zero 39 bytes after, JP 0075H for BASIC warm start. |
| 524BH | BREAK Handler Enable or disable BREAK key detection by writing C8H or 00H to 4C2DH and toggling 430FH bit 4. |
| 526FH | BLINK Handler Set cursor character via self-modifying code at 52E6H/52F3H/52FCH, allocate 62-byte task block, install blink task via CALL 4410H. |
| 52C5H | LARGE Cursor Handler Set cursor character to 8FH (large block), falls into BLINK installation. |
| 528BH | SMALL Cursor Handler Set cursor character to 88H (small block), falls into BLINK installation. |
| 5306H | TYPE Handler Install 214-byte type-ahead keyboard driver as scheduled task via CALL 4410H. |
| 5480H | SLOWER Handler Install 30-byte task block for SLOWER function via CALL 4410H. |
| 54DEH | JKL Handler Install 78-byte JKL screen-print handler as scheduled task via CALL 4410H. |
| 54E9H | GRAPHIC Handler Toggle graphic printer capability flag. |
| 5573H | ALIVE Handler Toggle video RAM end-of-row marker between 86H and 89H at 3C3FH. |
| 55B5H | DRIVE Handler Select target drive by number, set IY to drive parameter block via CALL 478FH. |
| 55C5H | DISABLE Handler Write C9H (RET) to IY+00H to disable drive I/O handler vector. |
| 55D3H | ENABLE Handler Write C3H (JP) to IY+00H to re-enable drive I/O handler vector. |
| 55DFH | STEP Handler Set stepping rate bits (1:0) in IY+03H drive flags byte. |
| 55FBH | DELAY Handler Set or clear motor-on delay bit (bit 2) in IY+03H drive flags byte. |
| 561AH | SYSGEN Handler Save current system configuration to disk via JP 402DH or issue error 1CH/88H. |
| 5627H | Command Level Check Verify 402DH contains C3H (JP opcode), display "CAN'T" error if not at VTOS command level. |
| 5660H | Parameter Error Handler Display "PARAMETER ERROR" message via 447BH, exit via JP 4030H. |
5200H - SYSTEM Command Entry Point
Entry point for IDAM A1H. Loads the parameter keyword table pointer and calls the SYS0 parameter table search/dispatch routine at 4476H. If no keyword match is found, jumps to the parameter error handler at 5660H.
After 4476H returns with Z set, all specified parameters have been processed. The code falls through to the FAST handler at 5209H, which doubles as the first post-dispatch check. The LD BC,0000H at 5209H is actually a self-modifying code target - 4476H writes the parsed FAST parameter value into the operand bytes at 520AH-520BH before dispatching here.
5209H - FAST/SLOW Parameter Handlers
Handles the FAST and SLOW parameters. FAST switches the processor to the high-speed clock; SLOW switches to the standard speed clock. Both modify 430FH bit 3 and output to port 0FEH. The parameter table maps FAST to 5211H and SLOW to 520AH, but the code at 5209H-522EH is a continuous block with self-modifying operands.
Load Register Pair BC with the SLOW parameter value. The operand bytes at 520AH-520BH are overwritten at runtime by 4476H with the parsed value for the SLOW keyword: 0000H = SLOW not specified, 0001H = SLOW specified.
SLOW was not specified. Now check the FAST parameter.
Load Register Pair BC with the FAST parameter value. The operand bytes at 5211H-5212H are overwritten at runtime by 4476H with the parsed value for the FAST keyword: 0000H = FAST not specified, 0001H = FAST specified. The parameter table entry for FAST points to 5211H as its handler address, meaning 4476H patches this operand directly.
FAST was specified. Enable the high-speed clock.
Slow Handler
Reached when SLOW was specified (BC was non-zero at 520EH). Clears 430FH bit 3 and outputs 00H to port 0FEH to switch to the standard-speed clock.
522EH - BASIC Parameter Handler
Handles the BASIC parameter. When specified, copies a 54-byte ROM re-entry stub from address 06D2H (in Level II BASIC ROM) to 4000H (start of RAM), then zeroes 39 additional bytes and jumps to the BASIC warm-start entry point at 0075H. This effectively exits VTOS and returns to ROM BASIC.
Load Register Pair BC with the BASIC parameter value. The operand bytes at 522FH-5230H are overwritten at runtime by 4476H with the parsed value for the BASIC keyword. The parameter table maps BASIC to address 522FH. 0000H = BASIC not specified, 0001H = BASIC specified.
BASIC was specified. Copy the ROM re-entry stub and exit to BASIC.
Loop Start
Zero 39 bytes starting at (DE) = 4036H.
Loop End
524BH - BREAK Parameter Handler
Handles the BREAK parameter which enables or disables BREAK key detection. When enabled (BREAK=ON or BREAK with no value), writes C8H to the BREAK handler flag at 4C2DH and sets 430FH bit 4. When disabled (BREAK=OFF), writes 00H to 4C2DH and clears 430FH bit 4.
Load Register Pair BC with the BREAK parameter value. The operand bytes at 524CH-524DH are overwritten at runtime by 4476H. The initial value 0001H means BREAK defaults to ON. 0000H = BREAK=OFF, 0001H = BREAK=ON (or BREAK with no flag).
BC is non-zero. If BC = 0001H, this is BREAK=ON (disable detection). If BC = FFFFH (flag present with no value), also handle as ON. Otherwise, BC holds a numeric parameter - check for explicit OFF.
BC was FFFFH (bare BREAK flag with no value). Disable BREAK detection by writing 00H to 4C2DH.
Break Enable Path
Reached when BC = 0000H (BREAK=OFF was explicitly specified, which counterintuitively means enable detection since OFF = "turn off the break-disable").
526FH - BLINK Parameter Handler
Handles the BLINK parameter which controls the blinking cursor. When BLINK=ON or BLINK=nnn (a specific ASCII cursor character), installs a 62-byte cursor blink task via SYS0 CALL 4410H. When BLINK=OFF, removes the blink task via CALL 4413H. The LARGE and SMALL sub-handlers at 52C5H and 528BH pre-set the cursor character before falling into this handler. The cursor character value is stored via self-modifying code at three locations within the installed task (52E6H, 52F3H, 52FCH).
Load Register Pair BC with the BLINK parameter value. The operand bytes at 5270H-5271H are overwritten at runtime by 4476H with the parsed value for the BLINK keyword. 0000H = BLINK=OFF, 0001H = BLINK=ON (default cursor), FFFFH = bare BLINK flag, or a numeric nnn = specific ASCII character code for the cursor.
BC = 0000H - BLINK=OFF was specified. Remove the blink task.
Reached when BC is non-zero and not 0001H. This means either a bare BLINK flag (FFFFH) or BLINK=nnn (specific cursor character). Check for FFFFH vs numeric value.
Bare BLINK flag (FFFFH). Use the default cursor character based on the LARGE/SMALL sub-handler setting.
Load Register Pair BC with the SMALL parameter value. The operand bytes at 528BH-528CH are overwritten at runtime by 4476H when the SMALL keyword is specified. The parameter table maps SMALL to 528BH. 0000H = SMALL not specified.
Cursor Character Store
Register A now holds the cursor character: 8FH (LARGE), 88H (SMALL), or a user-specified ASCII value. Store it into three self-modifying locations within the installed blink task code.
Store the cursor character (Register A) into the operand byte at 52E6H within the blink task code template. This is the first of three locations where the cursor character appears in the blink driver.
Store the cursor character into the second location at 52F3H within the blink task code template.
Store the cursor character into the third location at 52FCH within the blink task code template.
Now allocate a 62-byte block in high memory for the installed blink task, copy the task template into it, patch the self-reference pointer, and register it with SYS0.
Wait - BC was used in the SBC HL,BC, so it should be 0000H now. Actually, LDIR uses BC as the byte count and decrements it. Let me re-examine: BC was loaded with 003EH at 529EH, SBC HL,BC does not modify BC, so BC is still 003EH when LDIR executes. The LDIR copies 62 bytes. After LDIR, BC = 0000H.
52CCH - Installed Cursor Blink Task Code (Template at 52C7H-5304H)
This is the cursor blink task code template. The 62 bytes starting at 52C7H are copied into high memory and registered as task ID 05H. The task is called periodically by the interrupt-driven timer. It reads the cursor position from the video display, toggles the cursor character on and off at that position, and handles cursor position tracking. The .ORG 52CCH directive in the source indicates the disassembler shows this code at its template position, but at runtime it executes from the installed high-memory location. Note: the first 5 bytes (52C7H-52CBH) are task header/linkage data used by SYS0's task scheduler and are not shown as executable code here.
Load Register A with the cursor character. The operand byte at 52E6H is patched at 5295H with the selected cursor character (8FH for LARGE, 88H for SMALL, or a user-specified value). The initial template value is 8FH (large block cursor).
Compare Register A against the cursor character. The operand byte at 52F3H is patched at 5298H with the same cursor character value. If A equals the cursor character, the Z FLAG is set, meaning the cursor is still displayed and should be toggled off.
The character at the cursor position is not the cursor character. Check if the cursor has moved by comparing the current position against the saved position minus 40H (one row offset in video RAM).
Compare Register A against the cursor character. The operand byte at 52FCH is patched at 529BH. If the cursor character is found one row above, the screen has scrolled and the cursor position needs updating.
Cursor Restore Path
The cursor character was found at the saved position (or one row above after scroll). Restore the original character.
5305H - TYPE Parameter Handler (Type-Ahead Keyboard Driver)
Handles the TYPE parameter which enables the VTOS type-ahead feature. When TYPE=ON, allocates a 214-byte block in high memory for a keyboard type-ahead driver, copies the driver template from 53A9H, patches self-reference pointers, and registers it as task ID 0AH via CALL 4410H. When TYPE=OFF, removes the task via CALL 4413H. The installed driver intercepts keyboard input, buffering keystrokes ahead of program demand.
Load Register Pair BC with the TYPE parameter value. The operand bytes at 5306H-5307H are overwritten at runtime by 4476H with the parsed value for the TYPE keyword. The parameter table maps TYPE to address 5306H. 0000H = TYPE=OFF, 0001H = TYPE=ON.
BC = 0000H - TYPE=OFF was specified. Remove the type-ahead task.
Type-Ahead Driver Installation
BC is non-zero and not 0001H (bare flag or numeric). Verify command level, then install the 214-byte type-ahead driver.
Store HL (buffer management start address) into the task template at 53ADH. This patches a LD HL,nnnn instruction within the template so the installed copy will reference the correct buffer location.
Store HL into the second buffer reference at 53DAH within the template. A second LD HL,nnnn instruction in the driver is also patched.
Store the keyboard chain vector (HL) into the task template at 53E6H. This patches a CALL nnnn instruction in the driver so it will chain to the original keyboard handler after processing.
Store the keyboard chain vector into a second reference at 53D1H in the template.
First-time installation. Save the task entry address in IX+01H/+02H and record the keyboard vector.
53A9H - Installed Type-Ahead Driver Template (53A9H-547EH)
This is the 214-byte type-ahead keyboard driver template. It is copied into high memory and registered as task ID 0AH. The driver intercepts keyboard input, buffers keystrokes ahead of program demand, and chains to the original keyboard handler. The installed copy has self-reference pointers patched by the installation code at 5305H-53A6H. The first few bytes (53A9H-53A8H area) are the task header, and executable code begins at 53A9H. Note: .ORG directives indicate the disassembler's view of template offsets, but runtime addresses differ.
Point Register Pair HL to the type-ahead buffer management area. The operand bytes at 53ADH-53AEH are patched at 5334H with the actual buffer address in high memory. At runtime, HL points to the 3-byte buffer header (count byte + read/write pointers).
Buffer Not Empty
There are buffered keystrokes. Read the next one and return it.
Buffer Empty Path
The buffer read and write indices were equal at 53B5H, meaning no keystrokes are buffered. Call the original keyboard handler to check for new input and buffer any keystroke found.
GOSUB to the original keyboard handler. The operand bytes at 53D1H-53D2H are patched at 534AH with the original keyboard vector saved from 4016H (or 43BEH if re-installing). This chains to the real keyboard scan routine, which returns A = key character (or 00H if no key pressed).
53D9H - Type-Ahead Keystroke Store Routine (Part of Installed Driver)
This portion of the type-ahead driver is called when a keystroke is detected during the interrupt-driven keyboard scan. It checks whether the buffer is currently being read (flag byte non-zero), and if not, stores the keystroke into the circular buffer. It also handles buffer overflow detection and the special CLEAR+SHIFT+@ clear-buffer sequence.
Point Register Pair HL to the buffer management area. The operand bytes at 53DAH-53DBH are patched at 5337H with the actual buffer address in high memory (same address as the 53ACH reference).
GOSUB to the original keyboard handler. The operand bytes at 53E6H-53E7H are patched at 5347H with the keyboard chain vector. This scans the keyboard and returns A = key character (or 00H).
A keystroke was detected. Check for special keys and buffer overflow before storing.
Normal keystroke. Check if the buffer has room.
Buffer has existing data. Check for overflow condition.
Keystroke Store
Buffer has room. Store the keystroke (BC) at the current write position.
Clear Type-Ahead Buffer
Reached when CLEAR+SHIFT+@ (C0H) was detected at 53EEH. Reset the buffer indices and pointer to empty state.
5426H - Key Decode Subroutine (Part of Installed Type-Ahead Driver)
This subroutine translates the raw keyboard matrix data into ASCII character codes. It uses the keyboard row address and bit pattern to compute a character code, handling SHIFT key state, graphic characters, and special key combinations. Called from within the installed type-ahead driver during keyboard scan processing. The routine reads from keyboard memory-mapped I/O at 3880H (SHIFT row) and 3840H (special keys row), and references the key decode table in ROM at 0050H.
Loop Start
Find which bit is set in the key pattern (Register E) to determine the column within the row.
Loop End
Found the pressed key. D holds the character code offset (row x 8 + column). Now apply SHIFT and special key processing.
Character is in the 40H-5FH range (@ through _). Check SHIFT for lowercase conversion.
SHIFT is pressed. Convert to shifted character. For alphabetic keys (40H-5FH), SHIFT produces graphic characters (add 20H to get the C0H-DFH range).
Special Keys Path
Character code >= 60H. Handle numeric keys (rows 4-5) and function keys (row 6-7).
Character was in the 60H-6FH range (row 6: ENTER, CLEAR, BREAK, arrows, SPACE). Adjust back to the correct code.
Numeric/Punctuation Keys
A >= 70H: rows 4-5 containing digits and punctuation marks.
Final Character Validation
Register A holds the decoded character. Check for the special BREAK key code (01H) before returning.
547FH - SLOWER Parameter Handler
Handles the SLOWER parameter. When specified, allocates a 30-byte task block in high memory, copies the task template from 54BFH, and registers it as task ID 07H via CALL 4410H. When SLOWER=OFF, removes the task. The SLOWER feature adjusts system timing for compatibility with slower peripherals or operations.
Load Register Pair BC with the SLOWER parameter value. The operand bytes at 5480H-5481H are overwritten at runtime by 4476H with the parsed value for the SLOWER keyword. The parameter table maps SLOWER to address 5480H. 0000H = SLOWER not specified.
54BFH - Installed SLOWER Task Code Template (54BFH-54DCH)
The 30-byte SLOWER task code template. When installed in high memory and invoked by the timer callback, this code provides timing adjustment for slower peripherals. It reads cursor/display state and adjusts character output timing.
54DDH - JKL Parameter Handler (Screen Print Feature)
Handles the JKL parameter which enables the screen-print feature. When enabled, pressing the J, K, and L keys simultaneously causes VTOS to print the contents of the display on the *PR device. This handler allocates a 78-byte task block for the JKL screen-print driver and registers it as task ID 04H. When JKL=OFF, skips to the next handler.
Load Register Pair BC with the JKL parameter value. The operand bytes at 54DEH-54DFH are overwritten at runtime by 4476H with the parsed value for the JKL keyword. The parameter table maps JKL to address 54DEH. 0000H = JKL not specified.
Load Register Pair BC with the GRAPHIC parameter value. The operand bytes at 54E9H-54EAH are overwritten at runtime by 4476H with the parsed value for the GRAPHIC keyword. The parameter table maps GRAPHIC to address 54E9H. This check occurs here because the JKL handler needs to know if GRAPHIC mode is also specified to configure the print driver accordingly.
GRAPHIC was also specified. Patch the JKL driver template to handle graphic characters during screen print.
Load Register Pair HL with a template patch address. The operand bytes at 54F0H-54F1H are overwritten at runtime. This points to the location within the JKL template where the graphic handling code address is stored.
Store the graphic patch address into the JKL template at 5561H. This patches the installed JKL driver to substitute '.' (period) for non-printable characters when GRAPHIC is not enabled, or to pass graphic characters through when GRAPHIC is enabled.
Now allocate the 78-byte JKL screen-print driver block in high memory.
Store HL into the JKL template at 5524H. This patches a self-reference address within the template before copying.
Store HL into the JKL template at 5533H. This patches the second self-reference before the template is copied.
5526H - Installed JKL Screen-Print Driver Template (5524H-5571H)
The JKL screen-print driver template (78 bytes starting at 5524H, with executable code at 5526H). When installed, this driver is called periodically by the timer callback. It monitors for the simultaneous J+K+L key combination (keyboard row 1 at 3802H = 1CH, which is bits 2+3+4 set). When detected, it prints the entire video RAM (3C00H-3FFFH) to the printer, substituting '.' for non-printable graphic characters unless GRAPHIC mode is enabled.
J+K+L are all pressed simultaneously. Check if a screen print is already in progress.
No print in progress. Initialize the screen print by setting the start address and activation byte.
Load Register Pair HL with the screen print handler entry address. The operand bytes at 5533H-5534H are patched at 550EH with the correct address within the installed driver copy.
JKL Print Loop Body
This code is called by the timer interrupt when 4536H = CCH and 4537H points here. It prints one character per invocation from video RAM to the printer, advancing through the screen.
Loop Start
Print characters from video RAM, one per timer tick. Handle end-of-line, non-printable characters, and end-of-screen detection.
Load Register A with 2EH (ASCII '.', period). The operand byte at 5562H may be patched at 54F2H when GRAPHIC mode is enabled, replacing '.' with a different character or a pass-through that allows graphic characters to be printed. Without GRAPHIC, all characters >= 80H are replaced with '.' on the printout.
Screen Print Complete / Aborted
Reached when either BREAK was pressed (5548H) or the entire screen has been printed (5559H). Clean up and deactivate the JKL print handler.
5572H - ALIVE Parameter Handler
Handles the ALIVE parameter which toggles the video RAM end-of-row marker byte at 3C3FH between 86H and 89H. This provides a visual "alive" indicator on the screen, showing that the system is active and responsive by alternating a visible marker character.
Load Register Pair BC with the ALIVE parameter value. The operand bytes at 5573H-5574H are overwritten at runtime by 4476H. The parameter table maps ALIVE to address 5573H. 0000H = ALIVE not specified.
Store HL into the ALIVE template at 55A3H. This patches a self-reference address within the template.
55A3H - Installed ALIVE Task Code Template (55A3H-55B3H)
The 17-byte ALIVE task template. When installed and invoked by the timer callback, this code toggles the byte at the end of the first screen row (3C3FH) between two graphic character values, creating a blinking "alive" indicator in the top-right corner of the display.
55B4H - DRIVE Parameter Handler and Drive Configuration Dispatcher
Handles the DRIVE parameter which selects a target drive number for subsequent ENABLE, DISABLE, STEP, and DELAY parameters. The drive number is validated (must be 0-7), and the IY register is loaded with the drive parameter block address via CALL 478FH. After drive selection, the ENABLE/DISABLE, STEP, and DELAY parameters are processed. Finally, the SYSGEN parameter is checked.
Load Register Pair BC with the DRIVE parameter value. The operand bytes at 55B5H-55B6H are overwritten at runtime by 4476H with the parsed value for the DRIVE keyword. The parameter table maps DRIVE to address 55B5H. FFFFH = DRIVE not specified (initial/default). A numeric value indicates the drive number.
IY now points to the drive parameter block. Process the ENABLE/DISABLE parameter.
Load Register Pair BC with the DISABLE parameter value. The operand bytes at 55C5H-55C6H are overwritten at runtime by 4476H. The parameter table maps DISABLE to address 55C5H. 0000H = DISABLE not specified.
DISABLE was specified. Write C9H (RET opcode) to IY+00H to disable the drive's I/O handler.
DISABLE was not specified. Check the ENABLE parameter.
Load Register Pair BC with the ENABLE parameter value. The operand bytes at 55D3H-55D4H are overwritten at runtime by 4476H. The parameter table maps ENABLE to address 55D3H. 0000H = ENABLE not specified.
ENABLE was specified. Write C3H (JP opcode) to IY+00H to re-enable the drive's I/O handler.
Now process the STEP parameter (stepping rate for the drive).
Load Register Pair BC with the STEP parameter value. The operand bytes at 55DFH-55E0H are overwritten at runtime by 4476H. The parameter table maps STEP to address 55DFH. FFFFH = STEP not specified.
Now process the DELAY parameter (motor-on delay for mini-floppy drives).
Load Register Pair BC with the DELAY parameter value. The operand bytes at 55FBH-55FCH are overwritten at runtime by 4476H. The parameter table maps DELAY to address 55FBH. Default 0001H = DELAY=ON (extended motor-on delay).
5619H - SYSGEN Parameter Handler
Handles the SYSGEN parameter which saves the current system configuration to the system disk in drive 0. When SYSGEN=ON, jumps to 402DH (DOS READY) to trigger the configuration save. When SYSGEN=OFF, removes the saved configuration from disk. When not specified, returns to the parameter dispatch.
Load Register Pair HL with the SYSGEN parameter value. The operand bytes at 561AH-561BH are overwritten at runtime by 4476H. The parameter table maps SYSGEN to address 561AH. 0001H = SYSGEN=ON (default).
SYSGEN was either OFF or not specified. Issue an error for the unsupported SYSGEN=OFF case.
5627H - Command Level Check Subroutine
Utility subroutine called by TYPE, SLOWER, JKL, and ALIVE handlers to verify that the SYSTEM command was issued from the VTOS command level (not from within a running program). Checks that the byte at 402DH contains C3H (JP opcode), which is present only when the DOS READY prompt is active. If the check fails, displays "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" and exits to DOS via 4030H.
5636H - Error Message: "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL"
ASCII string displayed by the command level check at 5627H when the SYSTEM command is issued from within a running program instead of from the VTOS command prompt.
5660H - Parameter Error Handler
Displays "PARAMETER ERROR" and exits via 4030H. Reached when an invalid parameter keyword is specified, an out-of-range drive number is given, or an invalid stepping rate is specified.
5669H - Error Message: "PARAMETER ERROR"
ASCII string displayed by the parameter error handler at 5660H.
5679H - Parameter Keyword Table (18 Entries)
This is the parameter keyword lookup table used by CALL 4476H at 5200H. Each entry consists of a keyword string (padded to a fixed width with spaces) followed by a 2-byte handler address (little-endian). The 4476H routine compares command-line tokens against these keywords and dispatches to the corresponding handler address. The table is terminated by a 00H byte at 5709H. The handler addresses point to the self-modifying LD BC,nnnn operand bytes within each handler, which 4476H patches with the parsed parameter value before dispatching.
Keyword "FAST" (padded to 6 characters with spaces), handler address 5211H. Points to the FAST self-modifying operand at 5211H within the FAST/SLOW handler. Switches processor to high-speed clock.
Keyword "SLOW" (padded), handler address 520AH. Points to the SLOW self-modifying operand at 520AH. Switches processor to standard-speed clock.
Keyword "BASIC" (padded to 6 chars), handler address 522FH. Points to the BASIC self-modifying operand at 522FH. Exits to ROM Level II BASIC.
Keyword "BREAK" (padded), handler address 524CH. Points to the BREAK self-modifying operand at 524CH. Enables or disables BREAK key detection.
Keyword "BLINK" (padded), handler address 5270H. Points to the BLINK self-modifying operand at 5270H. Enables or disables the blinking cursor with optional character selection.
Keyword "LARGE" (padded), handler address 52C5H. Points to the LARGE cursor sub-handler which sets the cursor character to 8FH (full-block graphic), then falls into the BLINK installation code.
Keyword "SMALL" (padded), handler address 528BH. Points to the SMALL cursor sub-handler which sets the cursor character to 88H (left-half-block graphic), then falls into the BLINK installation code.
Keyword "TYPE" (padded), handler address 5306H. Points to the TYPE self-modifying operand at 5306H. Installs or removes the type-ahead keyboard driver.
Keyword "SLOWER" (6 characters, no padding needed), handler address 5480H. Points to the SLOWER self-modifying operand at 5480H. Installs timing adjustment task for slower peripherals.
Keyword "JKL" (padded to 6 chars), handler address 54DEH. Points to the JKL self-modifying operand at 54DEH. Installs the JKL screen-print driver.
Keyword "GRAPHI" (truncated to 6 chars, matching first 6 of "GRAPHIC"), handler address 54E9H. Points to the GRAPHIC self-modifying operand at 54E9H. Configures the JKL screen-print to handle graphic characters.
Keyword "ALIVE" (padded), handler address 5573H. Points to the ALIVE self-modifying operand at 5573H. Installs the blinking alive indicator at screen position 3C3FH.
Keyword "DRIVE" (padded), handler address 55B5H. Points to the DRIVE self-modifying operand at 55B5H. Selects the target drive number for subsequent drive-related parameters.
Keyword "ENABLE" (6 chars), handler address 55D3H. Points to the ENABLE self-modifying operand at 55D3H. Re-enables I/O to the selected drive by writing C3H (JP) to the drive parameter block.
Keyword "DISABL" (truncated to 6 chars), handler address 55C5H. Points to the DISABLE self-modifying operand at 55C5H. Disables I/O to the selected drive by writing C9H (RET) to the drive parameter block.
Keyword "STEP" (padded), handler address 55DFH. Points to the STEP self-modifying operand at 55DFH. Sets the stepping rate (0-3) for the selected floppy drive.
Keyword "DELAY" (padded), handler address 55FBH. Points to the DELAY self-modifying operand at 55FBH. Sets or clears the extended motor-on delay for the selected drive.
Keyword "SYSGEN" (6 chars), handler address 561AH. Points to the SYSGEN self-modifying operand at 561AH. Saves the current system configuration to the system disk in drive 0.
End-of-table sentinel byte (00H). The 4476H routine stops searching when it encounters this byte, indicating no more keyword entries.
ISAM A2H - SPOOL - Offset 19D7
VTOS 4.0 SYS6 - SPOOL Command Disassembly
The SPOOL command installs a symbient (VTOS's term for a background spooler process) that intercepts output directed to a specified device and redirects it into a queue of buffers. These buffers may reside in memory, on disk, or both, and the symbient process dynamically manages their contents to provide the most efficient operating environment given total system load.
The command syntax is SPOOL <devspec> [prep] <filespec> ([MEM=m][DISK=d]). The device specification must begin with an asterisk (e.g., *PR). The optional filespec names the disk buffer file; if omitted, a file named dd/SPLx is created automatically, where x is the device name character and dd is the drive number. The MEM and DISK parameters specify the number of 1K blocks to allocate for memory and disk buffering, respectively, defaulting to 1K memory and approximately 5K disk.
The SPOOL overlay performs two distinct phases. The setup phase (5208H-53F3H) validates arguments, opens or creates the spool file, looks up the target device in the drive configuration tables, calculates buffer addresses, builds ring-buffer descriptor blocks in high memory, copies the symbient code template to its runtime location, patches all self-referential addresses within the copied code, and installs a JP instruction at 4300H pointing to the resident symbient entry point. The symbient code (54D3H-5628H) is the template that gets relocated to high memory; it contains the interrupt-driven character queuing logic, disk flush and refill routines, and the sector-based buffer management engine.
Variable and Data Area Reference
| Address Range | Purpose |
|---|---|
| 5200-5207 (8 bytes) | NOP padding block - filler before real entry point at 5208H |
| 5295-5296 (2 bytes) | Self-Modifying - MEM parameter value; written by argument parser; loaded at 527EH and 52DBH to compute memory ring-buffer size |
| 52C9 (1 byte) | Self-Modifying - DISK parameter value (number of 1K disk blocks); loaded at 52EBH (via LD A,(52C9H)) for disk buffer calculation |
| 536F-5370 (2 bytes) | Self-Modifying - pointer to device parameter block entry in drive config table; stored at 527BH after chain walk, read at 53C1H during symbient code patching |
| 542A-543E (21 bytes) | Error string: "DEVICE SPEC REQUIRED" + 0DH; displayed when no device spec is provided on command line |
| 5448-5471 (42 bytes) | Error string: "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" + 0DH; displayed when SPOOL is invoked from within a program rather than the DOS command prompt |
| 547B-548A (16 bytes) | Error string: "PARAMETER ERROR" + 0DH; displayed for invalid MEM or DISK parameter values |
| 548B-54A9 (31 bytes) | Device filespec work area; 441CH writes the device name here; 548CH holds the pointer to the actual device name characters within this area |
| 54AB-54B1 (7 bytes) | Default spool filename template: "XX/SPL" + 03H terminator; the two "X" bytes at 54ABH-54ACH are overwritten with the actual drive number digits from the device spec |
| 54AE-54B1 (4 bytes) | Default extension template "SPL" + 03H; used by 4473H (insert default extension) |
| 54B2-54D2 (33 bytes) | FCB work area for the spool disk file; passed as DE to CALL 4476H (open file utility) at 5260H |
| 54D3-54DB (9 bytes) | Symbient helper: conditional-call stub - calls a patched address only if A is non-zero; used as a building block within the relocated symbient code |
| 54DC-553C (97 bytes) | Symbient output-character routine template; stores received character into the current memory buffer slot; if the slot is full, flushes to disk and advances the ring; self-modifying stubs (CALL 0000H, LD IX,0000H) are patched to runtime addresses during setup |
| 553D-5576 (58 bytes) | Symbient disk-write routine template; triggered when a memory buffer is full; positions the spool file to the correct sector, writes the buffer, advances ring pointers; self-modifying stubs patched during setup |
| 5577-55B5 (63 bytes) | Symbient disk-read / buffer-refill routine template; reads the next queued sector from the spool file back into a memory buffer so it can be sent to the printer; self-modifying stubs patched during setup |
| 55BC-5628 (109 bytes) | Symbient ISR-level buffer-dequeue routine template; called from the interrupt service routine to transfer a character from the memory ring buffer to the output device; handles wrap-around and triggers disk refill when a buffer is exhausted; self-modifying stubs patched during setup |
| 5629-5653 (43 bytes) | Symbient FCB/parameter data template block; copied to high memory as part of the symbient installation; contains the device name pointer and initial ring-buffer state values |
| 5654-5655 (2 bytes) | NOP padding - end of overlay |
Major Routine Reference
| Address | Name and Description |
|---|---|
| 5208H | SPOOL Main Entry Point Validates command-level context, parses device spec and optional filespec, opens the spool file, looks up the device in drive config tables, and orchestrates the full symbient installation sequence |
| 52C8H | Memory Buffer Calculation Computes the number of 1K memory ring-buffer blocks (default 1, or the MEM= value), validates the count is in range 1-32, and falls into the ring-buffer builder |
| 52DCH | Ring Buffer Builder Allocates ring-buffer descriptor blocks in high memory; each block is 4 bytes describing one 256-byte memory buffer; builds as many blocks as requested by MEM= parameter |
| 5309H | Disk Buffer Descriptor Builder If DISK= parameter was given, appends disk-extent descriptor entries to the ring; each entry is 4 bytes with bit 7 of byte 3 set to flag it as a disk (not memory) buffer sector |
| 5334H | Ring Terminator and Pointer Initialization Writes the end-of-ring sentinel (FFH FFH), saves the ring base address, and initializes the read, write, and ISR pointers that the symbient uses to walk the ring |
| 534FH | Symbient Code Relocator and Patcher Copies the symbient template block from the overlay's load address to its runtime location in high memory, then patches all self-modifying stub addresses (CALL 0000H, LD IX,0000H, LD A,(0000H)) to their actual runtime values |
| 53BFH | Symbient Hook Installer Lowers the high-memory limit at 4049H to protect the symbient workspace, installs the symbient entry point as a JP instruction at 4300H-4302H (the patchable drive-0 handler slot in the resident DOS), and returns to DOS READY via JP 402DH |
| 53F6H | Device Config Table Search Searches the two drive configuration tables (4015H and 43C0H) for an entry whose +06H/+07H bytes match the device name in DE; returns Z set and HL pointing to the matching entry, or NZ with A=08H if not found |
| 54D3H | Symbient: Conditional Output Stub Template routine; calls the patched output-character address only when A is non-zero; used within the symbient output-character handler as a conditional branch building block |
| 54DCH | Symbient: Queue Character Template routine; stores the incoming character (in C) into the current memory buffer at the position indicated by IX+20H; if the buffer is full (position wraps to 00H), triggers the disk-write routine; enables interrupts between buffer operations |
| 5577H | Symbient: Disk Read Stub Template routine; calls the disk-refill routine to load the next spool sector from disk into a memory buffer; switches IX context to the system FCB at 4025H during the read |
| 55BCH | Symbient: ISR Dequeue Character Template routine; called from the interrupt handler to deliver the next queued character to the output device; reads from the memory ring, handles sector boundaries by triggering disk refills, and advances all ring pointers |
5200H - NOP Padding Block
Eight NOP bytes precede the real entry point. This padding is common in VTOS SYS6 PDS members and aligns the true entry point that follows.
5208H - SPOOL Command Entry Point
The SPOOL command begins here. The first task is to confirm that it was invoked from the VTOS command prompt and not from within a running program - SPOOL can only be installed at command level. It then extracts and validates the required device specification argument.
Note On Logic
This check is inverted from what one might expect: finding C3H at 402DH actually means something has already been installed there - or, more likely, the test is detecting that the command-level JP is still intact and SPOOL has not yet been set up. The jump target at 543FH shows the error message for an invalid context, confirming that Z=set (402DH = C3H as the unmodified DOS READY jump) means SPOOL is already active or context is wrong.
The SPOOL command reaches its main argument-parsing logic only when 402DH does not contain C3H - meaning the resident DOS READY vector is in its original unpatched state and SPOOL has not yet been installed for this session.
The device name has been validated as starting with *. DE now points to the asterisk character at the start of the device name within the 548BH buffer. The code now builds the default spool filename by embedding the device name characters into the template at 54ABH.
Store Register Pair DE (the device name pointer) into the two-byte field at 54ABH-54ACH within the default spool filename template. This overwrites the placeholder "XX" bytes in the template "XX/SPL" with the address of the actual device name characters, so the default filename can later be built from the device name.
5227H - Parse Optional Filespec and Build Default Filename
After capturing the device name, the code attempts to extract an optional filespec from the command line. If only a drive number was given (indicated by the colon separator character), the code constructs the default spool filename by appending the device name characters followed by the "SPL" extension. If a full filespec was provided, it is used as-is.
Drive-Only Filespec Path
A colon separator means the user typed something like SPOOL *PR :1, giving only a drive number with no filename. The code must now construct the default filename by scanning past the device name and appending a colon plus the "SPL" default name.
Fetch the byte at the current position in DE (scanning through the device name characters that were stored in the 562AH buffer area). This loop is searching for the end of the device name - the first 0DH (carriage return) or 03H (end-of-string terminator).
Loop End
DE now points to the terminator byte at the end of the device name. The code overwrites this terminator with a colon (3AH) and then appends the "SPL" default filename from the template at 54AEH.
Fetch the next character from the default extension template at HL (starting at 54AEH, which contains "SPL" + 0DH). This loop copies the default name characters one at a time into the filespec buffer.
Note
This jump target at 5472H (PARAMETER ERROR) appearing here likely means the drive-only filespec path falls into an error exit if the file construction loop reaches the end-of-template marker without having produced a valid filespec - i.e., the drive spec alone without the device name was insufficient to construct a usable filename.
525DH - Open Spool File and Look Up Device
With the spool filename now fully constructed (either from the command line or built as a default), the code opens or creates the spool disk file and then searches the drive configuration tables to find the parameter block for the target output device.
The device was found in the drive config tables. HL now points to the matching entry. The device config table uses a linked-list structure where each entry may point to the next; the code now walks this chain to find the terminal (non-link) entry, which holds the actual device parameter block.
Test bit 4 of the byte at the memory location pointed to by HL (the current drive config table entry's type byte). Bit 4 set (10H) indicates this is a forward-link entry rather than a terminal entry - execution must follow the chain pointer to the next entry.
Loop End
HL points to the terminal device parameter block entry for the target output device. This block contains the device's I/O handler vector and configuration data that the symbient will use at runtime.
Store Register Pair HL (the address of the device parameter block terminal entry) into the two-byte self-modifying field at 536FH-5370H. This pointer will be read later during the symbient code patching phase (at 53C1H) to embed the device parameter block address into the relocated symbient code.
527EH - Read Spool File Header and Validate MEM Parameter
If the spool disk file already exists (from a previous SPOOL command), its header record is read and validated. The MEM parameter value is then checked: if zero (not specified), a default of 1K is used; otherwise the value is validated and used to compute the memory ring-buffer layout.
A non-zero MEM value was given. The spool file may already exist from a previous session; the code reads its first record to load the existing configuration, which includes the disk sector layout that must be preserved.
52C8H - Compute Memory Buffer Count and Build Ring Descriptor Table
This section computes how many 256-byte memory buffer blocks to allocate in the ring, validates the count is within allowed bounds, then constructs the ring-buffer descriptor table in high memory. Each descriptor is a 4-byte record that the symbient uses to manage one 256-byte memory buffer slot in the circular queue.
Register A now holds the 0-based loop count for the ring descriptor builder. The code now computes the base address of the memory ring buffer area in high memory, working downward from the detected RAM top at 4049H.
Store Register Pair HL (the ring buffer base address) into 564AH-564BH within the overlay's data template area. This address will be used during symbient code patching to embed the ring base into the relocated symbient routines.
Store Register Pair HL again into 564CH-564DH, a second patching target in the data template. Two separate self-modifying fields in the symbient code reference the ring base address.
Memory Ring Descriptor Builder Loop
Each descriptor entry is 4 bytes: byte 0 = buffer position (00H = empty), byte 1 = 00H, byte 2 = page number (high byte of buffer address), byte 3 = 00H. The page number in byte 2 increments by 1 for each successive 256-byte memory buffer slot. Register B holds the loop count (number of MEM descriptors to build); Register C holds the current page number.
Store 00H to the memory location at HL (descriptor byte 0: current buffer fill position, initially empty).
Loop End
530FH - Build Disk Buffer Descriptors
If a DISK= parameter was given, disk buffer descriptors are appended to the ring immediately after the memory descriptors. Each disk descriptor represents one 256-byte sector slot in the spool file on disk. Disk descriptors are distinguished from memory descriptors by bit 7 of descriptor byte 3 being set.
Disk Descriptor Builder
Register Pair DE holds the DISK block count. The code converts this to a sector count (x4) and then builds that many 4-byte disk descriptors. Each disk descriptor records a sequential sector number in the spool file.
Store 00H to the memory location at HL (disk descriptor byte 0: initial fill position, same as memory descriptors).
Loop End
5334H - Write Ring Terminator and Initialize Pointer Fields
After all memory and disk descriptors have been built, a sentinel value (FFH FFH) is written to mark the end of the ring. The code then saves the ring base address and writes it into several self-modifying pointer fields within the symbient code template - the read pointer, write pointer, and ISR pointer that the active symbient will use to walk the ring.
Store Register Pair HL (ring base address) into 564EH-564FH in the data template. This patches the symbient's "write pointer" initial value - the address in the ring where the next character will be enqueued.
Store Register Pair HL (ring base address) into 5650H-5651H in the data template. This patches the symbient's "read pointer" initial value - where the ISR will dequeue characters from for output to the device.
Store Register Pair HL (ring base address) into 5652H-5653H in the data template. This patches a third pointer field, likely the "ISR current position" pointer used during interrupt-driven output.
534FH - Copy Symbient Code to High Memory and Patch Addresses
This is the heart of the SPOOL installation: the symbient code template (the interrupt-driven spooler process) is copied from its load-time location in the overlay to its runtime home in high memory. After copying, numerous self-modifying stubs within the copied code are patched with their actual runtime addresses - the CALL 0000H stubs become CALL to real routines, and LD IX,0000H stubs become loads of actual FCB addresses.
The FCB block has been copied. Now the setup code patches numerous self-modifying address fields within the copy. Each field was set to 0000H as a placeholder in the template; the code computes the actual runtime offset from the copy base and stores the real address.
Store Register Pair HL (the runtime base of the copied symbient FCB block) into 54DEH-54DFH. This patches the LD IX,0000H stub in the symbient output-character routine (at 54DCH) with the actual FCB base address.
Store Register Pair HL into 558AH-558BH. This patches a second LD IX,0000H stub in the symbient disk-read routine with the FCB base address.
Store Register Pair HL into 55CAH-55CBH. This patches a third LD IX,0000H stub in the symbient ISR dequeue routine.
Store Register Pair HL (FCB base + 23H) into 55BEH-55BFH. This patches the LD A,(0000H) stub in the symbient ISR dequeue routine with the actual address of the sector-count field.
Store Register Pair BC into 5583H-5584H. This patches a CALL 0000H stub in the symbient output-character routine with the actual address of the disk write entry point, so the character queuer can flush to disk when a buffer fills.
Store Register Pair BC into 55A9H-55AAH. This patches a second CALL 0000H stub (in the symbient disk-read stub) with the disk write address.
Store Register A (the opcode byte currently at 4300H) into 55B9H. This embeds the previously-installed opcode into the symbient's own hook chain, so if multiple spoolers or hooks are chained together, the existing hook is preserved and called in sequence.
Store Register Pair HL (the current 4301H-4302H jump target) into 55BAH-55BBH. This preserves the existing hook chain target within the symbient code, completing the chain-forwarding setup.
Store Register Pair HL into 54D8H-54D9H. This patches the CALL 0000H stub address field in the symbient's conditional output helper (at 54D7H) with the actual runtime address of the disk-write entry point.
Store Register Pair HL into 5514H-5515H. This patches the CALL 0000H stub in the symbient disk-read refill path.
Store Register Pair HL into 55B7H-55B8H. This patches another CALL 0000H stub at a second location referencing the same disk-read entry point.
Store Register Pair HL into 54EEH-54EFH. This patches a CALL 0000H stub in the symbient buffer-full flush path.
Store Register Pair HL into 5510H-5511H. This patches a second reference to the same flush entry point at another location in the symbient code.
Store Register Pair HL into 5578H-5579H. This patches a third reference to this flush/write entry within the symbient's disk-read stub.
53BFH - Install Symbient Hook and Return to DOS READY
With the symbient code fully relocated and patched, the final step installs the hook that routes device output through the spooler. The symbient entry point is stored as a JP instruction at 4300H-4302H (the patchable drive-0 handler slot), so that every call through that vector will transfer control to the running symbient. The high-memory limit is already lowered to protect the workspace.
Store Register Pair HL (the symbient dispatch entry point address) into 4301H-4302H (the operand field of the JP instruction at 4300H in the resident DOS). This sets the jump target for the hook.
Store Register A (C3H = JP opcode) to address 4300H. This is the final act of installation: 4300H now holds a complete JP nnnnH instruction (C3H + 16-bit target in 4301H-4302H) pointing to the symbient dispatch entry in high memory. From this moment, any call through the drive-0 handler slot will transfer control to the SPOOL symbient.
53F6H - Device Config Table Search Subroutine
This subroutine searches the two VTOS drive configuration tables (first at 4015H, then at 43C0H) for an entry whose +06H/+07H bytes match the two device name characters passed in DE. It is called at 526AH to locate the parameter block for the device being spooled.
Save Register Pair HL (the current table entry pointer) onto the stack. The stack-save/restore pattern allows the code to compute offsets from HL while still being able to recover the entry base address.
Match Found
Both device name characters match. Pop the entry base address from the stack into HL and return Z set.
No Match At This Entry
The current entry's device name did not match. The code advances to the next 8-byte entry and checks whether the table is exhausted or switches to the second table.
Loop End
541CH - DOS Error Exit
A shared error exit used by several JP NZ paths throughout the main setup routine. The error code already in Register A is ORed with 40H (the extended-context suppression flag) before being passed to the DOS error handler.
5421H - "DEVICE SPEC REQUIRED" Error
Displayed when the SPOOL command is given without a valid device specification (no argument, or an argument that does not begin with an asterisk).
Data: "DEVICE SPEC REQUIRED" + 0DH
The 21 bytes at 542AH-543EH are ASCII text, not Z80 instructions. The disassembler decodes them as mnemonics (LD B,H / LD B,L / etc.) because it has no way to know this region is data.
543FH - "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" Error
Displayed when the SPOOL command detects it is being run from within a user program rather than from the VTOS command prompt. SPOOL can only be installed at command level.
Data: "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" + 0DH
The 42 bytes at 5448H-5471H are ASCII text data. The disassembler decodes them as Z80 mnemonics.
5472H - "PARAMETER ERROR" Error
Displayed when the MEM= or DISK= parameter value is invalid (out of range, non-numeric, or otherwise malformed), or when the spool file cannot be opened with the given filespec.
Data: "PARAMETER ERROR" + 0DH
The 16 bytes at 547BH-548AH are ASCII text data decoded by the disassembler as Z80 mnemonics.
548BH - Device Filespec Work Area and Default Filename Template
This data region serves as both the destination buffer for the device name extracted by 441CH and the template for the default spool filename. The first 32 bytes are the device filespec work area; starting at 54ABH is the default filename "XX/SPL\x03" where the XX bytes are overwritten with the actual device name pointer.
54D3H - Symbient Template: Conditional Call Helper
This 9-byte routine is the first section of the symbient code template. It is a conditional-call building block: if the character in Register A is non-zero, it calls a patched routine address; if zero, it returns immediately. After relocation to high memory, the CALL 0000H stub is replaced with the actual address of the disk-write routine.
GOSUB to the address stored at 54D8H-54D9H (initially 0000H; patched at 539BH with the runtime address of the disk-write entry point + 9 bytes into the symbient code). After patching, this call transfers control to the output-character queue routine within the relocated symbient.
54DCH - Symbient Template: Queue Output Character
This is the primary symbient output-character routine. When VTOS routes device output through the 4300H hook, this routine stores the character into the current memory ring buffer. If the buffer is full (the 256-byte page boundary is crossed), it triggers a disk flush and advances to the next ring slot. Interrupts are carefully managed around the critical buffer-pointer update.
Load Index Register IX with 0000H. After patching at 535EH, IX will hold the base address of the symbient FCB (File Control Block) in high memory. IX provides indexed access to all symbient runtime state variables throughout this routine.
GOSUB to the address stored at 54EEH-54EFH (patched at 53B0H with the runtime address of the buffer-availability check routine). This call performs a background check to see if any previously-queued disk buffers need servicing (i.e., if the printer has consumed a buffer and we can advance the read pointer).
Buffer Full Path
The 256-byte memory buffer page is full. The code must advance the ring to the next descriptor entry. If the next descriptor represents a disk buffer, the full page must be flushed to the spool file. The ring uses a linked-list structure where each descriptor's byte 2 and 3 identify either a memory page (bit 7 of byte 3 = 0) or a disk sector (bit 7 of byte 3 = 1).
Fetch the first byte of the current ring descriptor entry (byte 0 = buffer fill level / busy flag). This byte is tested to determine whether the next ring slot is available.
Loop End
An available ring slot was found. HL now points to descriptor byte 1. The code backs HL up to byte 0 (DEC HL is at 5503H) and then reads bytes 2 and 3 to determine whether this is a memory or disk buffer slot.
The ring sentinel was encountered, meaning the scan reached the end of the ring. The code wraps around to the start of the ring (55EFH onwards) before seeking an actual free slot on retry.
GOSUB to the address stored at 5510H-5511H (patched at 53B3H with the runtime address of the disk-read refill routine). This call attempts to advance the ring read pointer and free up a slot, before the scan retries to find a free slot after wrap-around.
GOSUB to the address stored at 5514H-5515H (patched at 53A4H with the runtime address of a second helper routine). This second call performs additional ring management - possibly the buffer-availability check - before the scan resumes.
Real Descriptor Found - Determine Memory vs Disk
A non-sentinel descriptor was found. Bit 7 of descriptor byte 3 (in Register A) distinguishes the type: 0 = memory buffer (use the page number in Register B as the high byte of the buffer address), 1 = disk buffer (flush the old memory buffer to this disk sector via the spool file).
Memory Buffer Path
The next ring slot is a memory buffer. Simply update IX+20H and IX+21H to point to the new page, store the new descriptor pointer in IX+24H-25H, and update the descriptor's address field with the ring base.
553FH - Symbient Template: Disk Buffer Flush (Write Memory Buffer to Spool File)
When the ring scan reaches a disk buffer descriptor (bit 7 of byte 3 set), the current 256-byte memory buffer must be flushed to the spool file sector identified by that descriptor before the ring pointer can advance. This routine positions the spool file to the target sector and writes the buffer.
5577H - Symbient Template: Disk Read Stub
A short stub that switches IX context to the system FCB at 4025H, calls the disk-refill routine (patched address), restores IX, and returns. This is used when the ISR dequeue routine needs to read a sector from the spool file back into a memory buffer for delivery to the output device.
GOSUB to the address stored at 5578H-5579H (patched at 53B6H with the runtime address of the buffer-availability check routine). This pre-call ensures any pending buffer state is resolved before the disk read begins.
GOSUB to the address stored at 5583H-5584H (patched at 5375H with the runtime address of the disk-write entry point in the symbient). This call reads the next queued sector from the spool file back into the memory ring buffer so it can be output to the device.
Load Index Register IX with 0000H. After patching at 5361H, IX will be reloaded with the symbient FCB base address. This restores the symbient context after the disk read stub has completed its system-FCB operation.
GOSUB to the address stored at 55A9H-55AAH (patched at 5379H with the runtime address of the disk write entry). This call delivers the character in Register C to the output device through the device driver.
Unreferenced stub (not reached by the current control flow structure). Patched address stored at 55B7H-55B8H (written at 53A7H). This may be a dead-code remnant or a future hook point in the symbient code template.
55BCH - Symbient Template: ISR Dequeue Character
This routine is called from the interrupt service routine to deliver queued spool output to the device. It checks whether data is available in the ring, reads the next character from the appropriate buffer (memory or disk-backed), and advances the ring read pointer. It also handles the transition from disk-backed sectors back to memory buffers when a disk sector is exhausted.
Fetch the byte stored at address 0000H. After patching at 536BH, this becomes LD A,(nnnnH) where nnnnH is the FCB+23H field (the sector-count / data-available flag). If the byte is 00H, there is no data in the ring to dequeue; if non-zero, data is waiting.
Data is available. The ISR dequeue logic saves all registers, loads the symbient FCB pointer, and begins reading from the ring.
Load Index Register IX with 0000H. After patching at 5364H, IX will be loaded with the symbient FCB base address, giving the dequeue routine access to all symbient state variables via IX-indexed addressing.
Memory Buffer Path
The current read descriptor points to a memory buffer. Update IX+23H with the buffer's sector count, clear the descriptor bytes 0-1, advance the ring read pointer to the next descriptor (from BC+28H/29H), and return.
Disk Buffer Path
The current read descriptor points to a disk sector. The sector must be loaded from the spool file into a memory buffer before characters can be dequeued from it. The code positions the spool file and reads the sector.
5654H - End Padding
Two NOP bytes at the end of the overlay binary. These pad the overlay to its full allocated size.
APPEND (31H) / COPY (32H) - Offset 1DFD
Introduction/Summary:
APPEND and COPY share identical functionality except that one creates a new file before copying the data from the source file and the other goes to the end of an existing file before copying that data. With this, it makes sense to share the code
These commands transfer a file from one diskette to another without requiring a VTOS system present on either of the two data disks. The user is prompted to insert the "Source", "Destination", and "System" disks as needed during the transfer process.
Two filespecs are required: a source file and a destination file. The command rejects wildcards ('*') in either filespec. It opens the source file for reading (via CALL 4424H) and the destination file for writing (via CALL 4420H), using high memory as a multi-page transfer buffer. Data is read from the source disk into the buffer pages (5500H-5FFFH), then written from the buffer to the destination disk, with the user prompted to swap disks between read and write phases.
The overlay is organized into two parallel code paths at 5200H and 5270H. The first path, APPEND at 5200H, handles the initial file open sequence - opening both source and destination files, checking if the destination already exists, and if so comparing source and destination sizes to verify the transfer has room. The second path, COPY at 5270H, handles the continuation/re-open sequence for multi-pass transfers when the file is larger than the buffer.
The disk swap prompting is handled by the subroutine at 5428H, which checks 430FH bit 5 and polls the keyboard for user confirmation before proceeding with disk I/O operations. The subroutine at 5349H computes the high-memory buffer page limit and patches three self-modifying locations (52F8H, 530DH, 5331H) with the upper page boundary.
Key Memory Locations Referenced
| Address Range | Purpose |
|---|---|
| 549DH-54BCH (32 bytes) | Source filespec/FCB buffer |
| 54BDH-54DCH (32 bytes) | Destination filespec/FCB buffer |
| 5500H-5FFFH (variable) | Multi-page data transfer buffer (256 bytes per page, upper limit computed from 4049H) |
| 54A0H-54A1H (2 bytes) | Current buffer page pointer (self-modifying) |
| 54A3H-54A4H (2 bytes) | Source file size / position tracking |
| 54A5H-54A6H (2 bytes) | Transfer state variable |
| 54A9H-54AAH (2 bytes) | Record position offset |
| 54C0H-54C1H (2 bytes) | Write buffer pointer (patched during write loops) |
| 54C3H-54C4H (2 bytes) | Destination file size comparison value |
| 54C5H-54C6H (2 bytes) | Transfer continuation flag |
| 54C9H-54CAH (2 bytes) | Record count tracking for multi-pass transfers |
| 430FH (1 byte) | System state flags - bit 5 tested by disk swap routine at 5428H |
| 4049H (2 bytes) | High memory pointer - used to compute buffer page limit |
Major Routines
| Address | Name and Purpose |
|---|---|
| 5200H | APPEND Entry Point (Initial Open Path) Compute buffer limit, extract source filespec, extract destination filespec, copy default extension, open source for read, open destination for write, begin transfer. |
| 5270H | COPY Entry Point (Re-Open) Re-open source and destination files for multi-pass transfers when file exceeds buffer size. |
| 52DCH | Read Loop Read records from source file into buffer pages (5500H-nnFFH), advancing page by page until buffer full or EOF. |
| 52FEH | Write Loop Write records from buffer pages to destination file, page by page. |
| 5312H | EOF / Transfer Complete Handler Flush remaining buffer data, close destination file, exit to DOS READY. |
| 532CH | Partial Page Flush Write remaining pages from buffer when page count is less than full. |
| 5349H | Buffer Limit Computer Read 4049H, compute upper page boundary, patch self-modifying CP instructions at 52F8H, 530DH, 5331H. |
| 535BH | Initialize Destination Sector Position to record, fill 256-byte buffer with E5H (empty marker), write sector to destination file. |
| 5391H | Device I/O Transfer Loop Read from source device, write to destination device via ROM 0013H/001BH with disk swap handling. |
| 5428H | Disk Swap Wait Check 430FH bit 5, poll keyboard for BREAK release and key confirmation, wait for user to swap disks. |
| 5454H | Error Exit OR A,40H / JP 4409H error handler. |
| 5459H | "FILE SPEC REQUIRED" Error Display message at 5462H, exit via JP 4030H. |
| 5475H | "DESTINATION FILE SPEC REQUIRED" Error Display message at 547EH, exit via JP 4030H. |
5200H - APPEND Entry Point (Initial Open Path)
Entry point for IDAM 73H. Computes the high-memory buffer page limit via CALL 5349H, extracts the source filespec into 549DH, extracts the destination filespec into 54BDH, copies the default extension from source to destination if not specified, checks for wildcard '*' in the source filespec, opens the source file for reading, opens the destination file for writing, then checks if the destination file already exists and handles the size comparison and initial transfer setup.
No destination filespec was provided. Copy the source filespec to the destination buffer as a default (same filename, different disk).
Check for wildcard '*' in the source filespec. APPEND does not support wildcards.
Open the source file for reading.
Source file is open. Now open the destination file for writing.
Both files are now open. Check if the destination file already has content (54C5H holds a continuation flag from the file open).
Destination file already exists. Perform directory info lookup and set up the write buffer for appending/overwriting.
Fresh Transfer Setup
Destination is a new file. Compute the initial record position and initialize the destination sector.
5270H - COPY ENTRY POINT (Re-Open for Multi-Pass)
This path serves two purposes:
- Entry point for the copy command
- Multi-pass transfers when the APPEND file is larger than the available buffer.
Re-computes the buffer limit, re-opens both source and destination files, and positions to the correct record for continued reading/writing. Also handles the case where both source and destination filespecs contain wildcards.
Check both source and destination for wildcard '*'.
Both files re-opened. Compare source and destination file sizes to determine transfer remaining.
52DCH - Main Read Loop
Reads records from the source file into the multi-page buffer, one page (256 bytes) at a time. The buffer starts at 5500H and extends upward to the computed high-memory limit. When the buffer is full or EOF is reached, control transfers to the write loop to output the buffered data to the destination file.
Read returned non-zero. Check if this is EOF (error codes 1CH or 1DH) or a real error.
Advance Buffer Page
Read was successful. Move to the next 256-byte page in the buffer.
Compare Register A against the buffer upper page limit. The operand byte at 52F8H is patched by the subroutine at 5349H with the computed page boundary (derived from 4049H). The initial template value 60H allows pages 55H-5FH (11 pages, 2816 bytes).
Buffer Full
The buffer is full. Write all buffered pages to the destination file.
Write Loop
Write buffered pages to the destination file, one page at a time.
Compare against the buffer upper page limit. The operand byte at 530DH is patched by 5349H (same boundary as 52F8H).
5312H - EOF / Transfer Complete Handler
Reached when EOF (error 1CH or 1DH) is detected during reading. Flushes any remaining buffered pages to the destination file, closes the destination file, and exits to DOS READY.
Close and Exit
532CH - Partial Page Flush Subroutine
Writes remaining buffered pages from 5500H up to the current read position to the destination file. Called when EOF is reached mid-buffer. Checks if the current page (H register) is at 55H (no data to write) or at the computed limit (already flushed), and writes page by page if data remains.
Compare against the buffer upper page limit. The operand at 5331H is patched by 5349H. If H equals the limit, the buffer was already fully written in the write loop.
Loop Start
Write pages from 5500H up to (but not including) the page in B.
5349H - Buffer Limit Computation Subroutine
Reads the high memory pointer from 4049H, computes the upper page boundary for the transfer buffer, and patches three self-modifying CP operands (at 52F8H, 530DH, and 5331H) with the page limit value. This ensures the buffer does not extend into memory used by the system or other tasks.
Store the page limit to the CP operand at 52F8H in the read loop. The read loop will stop filling the buffer when H reaches this value.
Store the page limit to the CP operand at 530DH in the write loop.
Store the page limit to the CP operand at 5331H in the partial flush routine.
535BH - Initialize Destination Sector Subroutine
Positions to the specified record in the destination file, fills the 256-byte buffer at 5500H with E5H (empty directory entry marker, used as fill pattern), and writes the initialized sector to the destination file. Called during transfer setup to prepare destination sectors.
5375H - Wildcard/Device Transfer Path
Handles the case where either source or destination filespec begins with '*'. Opens both files with mode 00H and enters the device-level I/O transfer loop at 5391H. This path uses ROM 0013H/001BH for byte-level device I/O rather than the block-level 4436H/4439H record routines.
5391H - Device I/O Transfer Loop
Byte-level transfer loop using ROM 0013H (get byte from device) and ROM 001BH (output byte to device). Reads bytes from the source device/file and writes them to the destination, handling disk swap prompts via CALL 5428H between read and write phases. Continues until EOF (error 1CH) is detected or an error occurs.
Source is a device. Read bytes from device with disk swap check between reads.
Disk Swap Check Before Read
Source is a disk file. Check if disk swap is needed via 5428H before reading.
Write Byte to Destination
A byte has been read. Write it to the destination file/device.
Write returned NZ. Check if the destination is a device with bit 7 set.
Disk Swap Check After Write
Destination write succeeded or is a disk file needing retry.
53CDH - Filespec Comparison and Extension Copy Subroutine
Compares source (HL) and destination (DE) filespecs, validates drive letters, and copies the source file extension to the destination if the destination has no extension. Scans for '/' (extension separator) and '.' (drive separator) delimiters.
Delimiter Scan Subroutine
Scans the filespec at (DE) for the delimiter character in Register B. Advances DE past alphanumeric characters until the delimiter or a terminator (03H, 0DH) is found.
Extension Copy
Delimiter not found in the destination. Copy the extension from the source (HL) to the destination (DE) by inserting the delimiter and extension characters.
5408H - Drive Letter Validation and Character Insertion Subroutines
Two utility subroutines. 5408H validates and advances past a drive letter prefix in the filespec. 5414H inserts characters from the source filespec into the destination by shifting existing characters right.
Character Insertion Subroutine
Inserts characters from the source position into the destination filespec by shifting existing characters right. Used to copy extension data.
Loop Start
Shift characters in the destination right to make room for the inserted extension.
5428H - Disk Swap Wait Subroutine
Checks whether a disk swap is required (430FH bit 5) and waits for user confirmation. Polls keyboard rows 3840H (BREAK/special keys), 3801H (@ key), and 3880H (SHIFT key) to detect key release/press events, then waits for a keypress via ROM 002BH. Returns Z if the user confirmed the swap, NZ if cancelled (backtick 60H pressed).
Bit 5 of 430FH is clear. Wait for BREAK release, then poll for keys to confirm disk swap.
Both '@' and SHIFT are pressed. Wait for BREAK release, then wait for a confirming keypress.
5454H - Error Exit
Common error exit. Sets bit 6 in the error code and jumps to the DOS error handler.
5459H - "FILE SPEC REQUIRED" Error
Displays the source file error message and exits.
5462H - Data: "FILE SPEC REQUIRED"
ASCII error message string for missing source filespec.
5475H - "DESTINATION FILE SPEC REQUIRED" Error
Displays the destination file error message and exits. Reached when the source and destination file sizes match (indicating same disk or already-transferred file).
547EH - Data: "DESTINATION FILE SPEC REQUIRED"
ASCII error message string for missing/invalid destination filespec.
ISAM 33H - BUILD - Offset 20A9
VTOS 4.0 SYS6/SYS - BUILD Command Disassembly (IDAM 33H, Member 20H) - Model I
The BUILD command is a simple ASCII file builder that accepts keyboard input on a line-by-line basis and writes the text to disk. It is loaded as IDAM 33H from the SYS6/SYS Partitioned Data Set overlay library, occupying Member 20H at file offset 20H/00A9H, and loads into memory at 5200H.
BUILD opens a new file (via CALL 4420H) and then enters a line input loop using ROM routine 0040H. Each line of text is written to the output file byte-by-byte via ROM routine 001BH (output byte to device). The file is closed and saved when the user presses BREAK as the first keystroke of a new input line (detected via the CARRY flag from ROM 0040H with B=0). A 03H (ETX) end-of-text marker is written as the final byte before closing.
BUILD includes special support for KSM (Keystroke Macro) files. When the filespec contains the "/KSM" extension, the builder enters KSM mode. In KSM mode, each input line is preceded by a prompt showing the key assignment: "A=> ", "B=> ", through to "ESC" (1BH). The key counter at 5243H is self-modifying, incrementing from 01H through 1AH (26 entries, A-Z), with entry 1BH triggering the ESC handler. KSM files allow up to 256 characters per line (B=FFH at 5260H) versus the standard mode's limit.
The overlay also checks for an existing file with the same name and rejects the operation with "FILE ALREADY EXISTS" if the file is found (detected via the NC flag from CALL 4420H).
Key Memory Locations Referenced
| Address Range | Purpose |
|---|---|
| 52CCH-52FFH (52 bytes) | Filespec buffer - receives parsed filespec from CALL 441CH |
| 523EH (1 byte) | KSM mode flag - self-modifying operand: 00H = normal mode, FFH = KSM mode |
| 5243H (1 byte) | KSM key counter - self-modifying operand: increments from 00H to 1BH (ESC) |
| 5300H-53FFH (256 bytes) | File I/O record buffer (used by CALL 4420H open) |
| 5400H+ (variable) | Line input buffer (keyboard input via ROM 0040H) |
Major Routines
| Address | Name and Purpose |
|---|---|
| 5200H | BUILD Entry Point Extract filespec via CALL 441CH, scan for /KSM extension, open new file, enter input loop. |
| 523AH | Input Loop KSM mode: display key assignment prompt (A=> , B=> , etc.). Normal mode: accept line input via ROM 0040H. |
| 5272H | Write and Close Store 03H terminator, write buffer to file via ROM 001BH, close file via CALL 4428H. |
| 528EH | Error Exit OR A,40H / JP 4409H - set error bit and exit via DOS error handler. |
| 5293H | "FILE SPEC REQUIRED" Error Display error message and exit via JP 4030H. |
| 52AFH | "FILE ALREADY EXISTS" Error Display error message and exit via JP 4030H. |
5200H - BUILD Entry Point and KSM Extension Detection
Entry point for IDAM 33H. Extracts the filespec from the command line, scans it for the "/KSM" extension to determine whether to enter KSM keystroke macro mode, then opens the file for writing. If the file already exists, displays an error. If no filespec is given, displays "FILE SPEC REQUIRED".
A filespec was found. Now scan it for the "/KSM" extension to determine if this is a KSM keystroke macro file.
Found '/' separator. Check if the extension is "KSM".
Extension is "/KSM". Enable KSM keystroke macro mode by setting the flag byte.
Store FFH to the KSM mode flag at 523EH. This patches the operand of a LD A,00H instruction at 523DH, changing it to LD A,FFH. When this byte is non-zero, the input loop at 523AH enters KSM mode with key assignment prompts.
522CH - Open File and Enter Input Loop
Opens a new file for writing using the parsed filespec. If the file already exists (NC return from 4420H), displays an error. Otherwise enters the main line input loop. In KSM mode, displays sequential key assignment prompts before each input line.
Main Input Loop
The file is now open for writing. Accept line-by-line keyboard input and write each line to the file. In KSM mode, display the key assignment prompt before each line.
Load Register A with the KSM mode flag. The operand byte at 523EH is patched to FFH at 5229H when the filespec has a "/KSM" extension. 00H = normal mode, FFH = KSM mode.
KSM Mode Prompt
Display the key assignment prompt: increment the key counter, display the character code + "=> " prompt.
Load Register A with the current KSM key counter. The operand byte at 5243H is patched by the LD (5243H),A instruction at 5245H. Starts at 00H and increments through each key assignment (01H='A', 02H='B', ... 1AH='Z', 1BH=ESC).
Store the updated key counter back to 5243H. This patches the operand of the LD A,00H instruction at 5242H so the next iteration loads the incremented value.
Line Input
Accept a line of keyboard input. KSM mode uses B=FFH (up to 255 characters); normal mode also uses B=FFH here (though the manual states 64 char limit for non-KSM, the code does not enforce a different limit).
Break Key Handler
BREAK was pressed. Check if any characters were typed on this line (B=0 means BREAK was the first keystroke).
5272H - Write Buffer to File and Close
BREAK was pressed as the first keystroke on an empty line (or all 26 KSM keys were assigned). Terminate the buffer with 03H (ETX), write all buffered data to the output file byte-by-byte via ROM 001BH, then close the file and exit to DOS READY.
Loop Start
Write the buffer to the output file one byte at a time.
Loop End
All data has been written. Close the file.
528EH - Error Exit
Common error exit path. Sets bit 6 in the error code (via OR 40H) and jumps to the SYS0 DOS error handler at 4409H.
5293H - "FILE SPEC REQUIRED" Error
Displays the "FILE SPEC REQUIRED" error message and exits via 4030H. Reached when BUILD is invoked without a filespec on the command line.
529CH - Data: "FILE SPEC REQUIRED"
ASCII error message string.
52AFH - "FILE ALREADY EXISTS" Error
Displays the "FILE ALREADY EXISTS" error message and exits. Reached when BUILD attempts to create a file that already exists on disk (4420H returns NC).
52B8H - Data: "FILE ALREADY EXISTS"
ASCII error message string.
ISAM 41H - LIST and PRINT - Offset 217C
VTOS 4.0 SYS6/SYS PRINT Command Disassembly (Model I)
The PRINT command prints a listing of a disk file on the *PR device (the system printer). The command accepts a filespec or device specification, and supports several optional parameters controlling the output format. Two default file extensions are recognized: TXT is tried first; if not found, an all-blanks extension is tried.
The command syntax is:
PRINT <filespec> ([NUM][LINE=n][HEX][REC=r][LRL=n])
The options are:
- NUM - Enable line numbering. Each output line is prefixed with a line number.
- LINE=n - Begin output at line number n of an ASCII file.
- HEX - Produce a hexadecimal listing instead of ASCII text. Each output line shows 16 bytes as two-digit hex values followed by their printable ASCII equivalents.
- REC=r - Begin output at record number r of a file (for hexadecimal mode).
- LRL=n - Specify the logical record length n for hexadecimal listings.
This member is IDAM 42H in the SYS6 partitioned data set, sharing member 21H with the LIST command (IDAM 41H). The PRINT entry point is at 5206H; LIST enters at 5200H. Both converge at 520BH for shared initialization and processing logic.
The overlay loads at 5200H and occupies approximately 824 bytes through 5517H, followed by data buffers extending to 5600H.
Runtime Variables and Work Areas
| Address | Bytes | Purpose |
|---|---|---|
| 5282H | 2 | Record counter (initialized to 0001H; counts records read and displayed) |
| 527AH | 2 | Line number counter (initialized to 0000H; incremented after each output line in ASCII mode) |
| 52ADH | 2 | Auxiliary counter (initialized to 0000H; role determined by option flags) |
| 5329H | 2 | File open mode byte (initialized to 0000H; set to 0001H when a second extension try is needed) |
| 5380H | 1 | Self-Modifying Code Command entry flag: 00H = LIST entry (5200H), FFH = PRINT entry (5206H). Used to select which drive configuration table entry pointer to hold on the stack. |
| 52C2H | 1 | Self-Modifying Code Current input byte captured from the input device. Written by LD (52C2H),A at 52A9H; read back as the operand of LD A,00H at 52C1H during output processing. |
| 530FH | 2 | Self-Modifying Code Operand of LD BC,0000H at 530EH. Written by CALL 4442H (Position to Record) at 5314H; holds the target record number for positioning before HEX mode output begins. |
| 532CH | 1 | Self-Modifying Code Operand of LD A,00H at 532BH. Initialized to 00H at 5325H; incremented by 10H after each 16-byte row of HEX output. Tracks the byte offset within the current record for the address column of HEX listings. |
| 54D8H-54DBH | 4 | Decimal record/line number display field (4 space characters, filled in by the number formatter at 542BH) |
| 54DCH | 1 | Separator byte between record number and line offset fields (2EH = '.') |
| 54DDH | 1 | Separator byte (03H = end-of-string marker for the record number sub-field) |
| 54DEH-54E1H | 4 | Record line offset display field (4 space characters, filled in by 542BH for HEX row sub-addressing) |
| 54E2H-54E4H | 3 | Separator and end marker (20H 20H 20H = three spaces before the hex address label) |
| 54E5H-54EBH | 7 | Hex address label template: 3AH=':' 58H='X' 58H='X' 20H=' ' 3DH='=' 20H=' ' 03H=end. The 'XX' placeholders are overwritten at runtime by the hex address formatter at 4DE7H. |
| 54ECH-54EEH | 3 | Default file extension string "TXT" (passed to 4473H to insert as default extension) |
| 54EFH-550EH | 32 | Input file FCB (File Control Block, 32 bytes). Passed to 4476H as DE to open the source file for reading. |
| 5518H-5527H | 16 | HEX mode byte accumulation buffer. As each 16-byte row is assembled, raw byte values are stored here for printable-character display after the hex columns. |
| 5528H-5547H | 32 | Command line / filespec work buffer. Filespec extracted here by 441CH; then used as the FCB passed to 4424H for file open; also used as the DCB for device I/O via 0013H/001BH. |
| 5548H-5567H | 32 | Saved copy of the original filespec (copied here by LDIR at 5246H before the default extension is inserted, for the second-try open with blank extension) |
| 5600H | varies | Output FCB / *PR device control block. Opened by 4424H (Basic file open) to direct output to the printer device. |
Major Routines
| Address | Name and Purpose |
|---|---|
| 5200H | LIST Entry Point Entry for the LIST command (IDAM 41H). Sets A=00H and DE=401DH (drive 1 config entry pointer), then falls through to shared initialization at 520BH. |
| 5206H | PRINT Entry Point Entry for the PRINT command (IDAM 42H). Sets A=FFH and DE=4025H (drive 2 config entry pointer), then falls through to shared initialization at 520BH. |
| 520BH | Shared Initialization Stores the entry flag to 5380H, saves DE on the stack, zeroes all counters, sets the record counter to 1, extracts the filespec, and opens the source file with TXT then blank-extension fallback. |
| 5279H | File Open Result Dispatcher Tests the open result; if BC=0000H (file not found with TXT extension), tries again with blank extension. If BC=0001H (already tried blank), positions to the requested starting record and begins output. |
| 5298H | ASCII Print Main Loop Formats and outputs the record/line number prefix, then reads and prints characters from the source file one at a time, handling carriage return as end-of-line. Loops per line until file end or BREAK. |
| 530EH | HEX Print Setup and Main Loop Positions to the requested starting record, then enters the HEX dump loop that reads 16 bytes per row, prints them as two-digit hex values with printable-character sidebar, and outputs the row address label. |
| 5402H | Printable Character Filter Given a byte in A, outputs it as its ASCII character if it is printable (20H-5AH or 61H-7AH), or outputs a period (2EH) if it is a control character or non-printable byte. Used for the ASCII sidebar in HEX mode. |
| 5417H | Hex Byte Output Outputs the byte in A as two ASCII hexadecimal digits (high nibble first, then low nibble) by rotating and calling the nibble-to-hex routine at 5420H. |
| 5420H | Hex Nibble Output Converts the low 4 bits of A to an ASCII hexadecimal digit ('0'-'9' or 'A'-'F') using the DAA trick, then outputs it to the *PR device via ROM 001BH. |
| 542BH | Decimal Number Formatter Formats the 16-bit value in HL as a decimal number (up to 5 digits: 10000s, 1000s, 100s, 10s, 1s) into a pre-formatted buffer pointed to by BC, with leading-zero suppression based on the buffer's existing content. |
| 5461H | Numeric Buffer Incrementer Increments a decimal digit field within the format buffer at 54D8H by walking 4 bytes in from HL, performing a carry-propagating increment on the ASCII digit at that position. |
| 5472H | Printer Ready and BREAK Check Tests bit 5 of system flags at 430FH, checks printer port status at 3840H (bit 2), verifies RS-232 status ports 3801H and 3880H, and polls for a keypress via ROM 002BH. Returns Z if output can proceed, NZ if BREAK or printer not ready. |
5200H - LIST / PRINT Entry Points
Two entry points share a common initialization path. LIST (IDAM 41H) enters at 5200H; PRINT (IDAM 42H) enters at 5206H. The sole difference is which drive configuration table entry pointer is preserved on the stack and what value is stored to the command mode flag at 5380H.
520BH - Shared Initialization
Both LIST and PRINT converge here. The command mode flag is saved, counters are zeroed, the filespec is extracted from the command line, and the source file is opened using TXT as the default extension.
Store Register A (00H for LIST, FFH for PRINT) to the command-mode flag byte at 5380H. This byte is tested later to distinguish the two commands when selecting output options.
5279H - File Open Result Dispatcher
After a successful file open, BC holds the result code from 4424H. If BC=0000H the file was opened for the first time with the TXT extension and should now be retried with a blank extension first (BC=0000H means "not yet tried without extension"); if BC is non-zero the file is open and ready. This section also handles the case where a starting record number has been specified.
Begin the command-line scan loop. Transfer Register B (high byte of the countdown) into Register A.
DECrement the countdown counter BC after finding a carriage return. Each carriage return found decrements the counter, advancing through the file one line at a time when seeking a starting line number.
5298H - ASCII Print Main Loop
With the file positioned at the correct starting line, this loop reads and prints the file one line at a time in ASCII mode. Each line is prefixed with the record/line number formatted into the display buffer at 54D8H by the decimal formatter at 542BH. Characters are read from the source file and written to the *PR output device via ROM 001BH. The loop terminates when an error, end-of-file, or BREAK condition is detected.
Fetch the current record counter from 5282H (2 bytes) into Register Pair HL. This 16-bit value (initialized to 0001H) holds the number of the record currently being printed. It will be passed to 542BH (decimal number formatter) to generate the line-number prefix.
GOSUB to ROM 0013H to read one byte from the input file at DE (5528H) into Register A. Returns Z set and the byte in A if a character was read successfully; NZ if an error or end-of-file occurred.
Store the character just read from the input file (in Register A) to 52C2H. This address is the operand of the LD A,00H instruction at 52C1H. Modifying 52C2H means the next execution of that instruction loads the actual character rather than a constant. This is the mechanism by which the character is buffered and re-read for output.
Load Register A with the byte at 52C2H (the operand of this LD A,nn instruction). The operand 00H is overwritten at runtime by LD (52C2H),A at 52A9H, which stores each incoming character there before this instruction re-loads it. This is the mechanism that delivers the character from the input read to the output path.
If the Z FLAG is set (no BREAK, printer is ready), LOOP BACK to 52A1H to format the next line number, read the next character, and continue printing. This is the main outer loop iteration that advances to the next line.
POP the output DCB pointer from the stack into Register Pair DE. BREAK was pressed; a final carriage return is output before returning to DOS READY.
POP the output DCB pointer from the stack into Register Pair DE. An end-of-file or read error occurred; save the error code in AF before outputting the final carriage return.
530EH - HEX Mode Setup and Main Loop
When the HEX option is specified, execution enters here. The command line is scanned for a starting record number (REC=r parameter), the file is positioned accordingly, and then the hexadecimal dump loop begins. Each iteration prints 16 bytes formatted as two-digit hex values in columns, followed by a printable-character sidebar, with a record/offset address label on the left.
Load Register Pair BC with 0000H. The operand of this instruction (at 530FH, 2 bytes) is overwritten at runtime by CALL 4442H at 5314H. After 4442H executes, 530FH holds the starting record number specified by the REC=r parameter. The NOP at 531AH and the LD HL,(530FH) at 531BH then read this value back out as the record number for HEX output.
Store 00H to 532CH, the operand of the LD A,00H instruction at 532BH. Initializes the byte-offset counter to zero at the start of each new record in HEX mode. This counter is incremented by 10H (16 decimal) after each row of 16 bytes, producing the row address suffix shown after the record number.
Load Register A with the byte at 532CH (the operand of this LD A,nn instruction). This operand is 00H initially (set by 5325H) and is incremented by 10H after each row by the instruction at 5340H. The loaded value is the current row's byte offset within the record (0, 10H, 20H, etc.), used to construct the HEX display address label.
Store the updated byte-offset counter (A) back to 532CH, the operand of the LD A,00H instruction at 532BH. On the next row, 532BH will load this new value as the row offset for the address label.
Point Register Pair DE to the source file FCB at 5528H. This is the DCB address for ROM 0013H to read the next byte from the input file.
If the NZ FLAG is set (B is non-zero, more bytes remain in this 16-byte row), LOOP BACK to 5348H to read the next byte from the input file and output its hex representation. This inner loop iterates 16 times per HEX dump row.
Load Register A with 20H (ASCII space) for the sidebar alignment spacing loop.
DECrement Register B and if Not Zero, LOOP BACK to 538CH to output another space. Outputs 12 spaces total, aligning the print position for the sidebar.
Fetch one byte from the HEX row buffer at (HL) (5518H+n) into Register A. This is the raw byte value that will be passed through the printable-character filter at 5402H and output as either its ASCII character or a period.
DECrement Register B and if Not Zero, LOOP BACK to 539BH to process and output the next byte from the HEX row buffer. When B reaches zero, all bytes for this row's sidebar have been output.
53B5H - Partial Row Sidebar Padding
When the end-of-file is reached mid-row (fewer than 16 bytes were read), the hex columns printed so far must be padded with spaces to align the sidebar under the full-row output. This section computes the padding width from the remaining column count in B and outputs it before the sidebar.
Load Register A with 20H (ASCII space) for the padding output loop.
DECrement Register B and if Not Zero, LOOP BACK to 53C1H to output another space. After this loop, enough spaces have been output to align the partial row's sidebar with full-row sidebars.
Fetch one byte from the HEX row buffer at (HL) into Register A for printable-character output.
DECrement Register B and if Not Zero, LOOP BACK to 53D0H to output the next byte's printable character. When B reaches zero, the partial sidebar is complete.
JUMP to 5324H to initialize the row byte-offset counter to 00H and begin output of the next record's HEX rows. This is the outer loop back-edge for the HEX dump, iterating once per record in the file.
5402H - Printable Character Filter
This subroutine is called from the HEX dump sidebar output loops. Given a byte in Register A, it outputs either the corresponding printable ASCII character or a period (2EH) if the character is a control code or lies in the extended character range. The ranges accepted as printable are 20H-5AH (space through 'Z') and 61H-7AH ('a' through 'z').
5417H - Hex Byte Output
Outputs the byte in Register A as two ASCII hexadecimal digits to the printer device at DE. The high nibble is output first by rotating right 4 times and calling the nibble-to-hex routine at 5420H, then the original low nibble is output by falling through into 5420H.
5420H - Hex Nibble Output
Converts the low 4 bits of Register A to an ASCII hexadecimal digit ('0'-'9' or 'A'-'F') and outputs it to the printer device at DE. Uses the DAA (Decimal Adjust Accumulator) trick: adding 90H and then using DAA and ADC to produce the correct ASCII code for hex digits 0-F.
542BH - Decimal Number Formatter
Formats the 16-bit value in Register Pair HL as a decimal number of up to 5 digits (0-65535) by successive subtraction. Each digit from the 10000s place down to the 1s place is extracted by counting subtractions and stored as an ASCII digit into the buffer pointed to by BC. Leading zeros are suppressed by checking whether the buffer position already holds a space character.
5446H - Decimal Digit Extraction
Called by 542BH for each decimal place. Counts how many times DE can be subtracted from HL (without going negative), producing the digit count for that place value. Converts the count to ASCII, then applies leading-zero suppression: if the digit is '0' and the buffer position already holds a space, the '0' is not written (suppressed). The digit (or suppressed zero) is written to (BC) and BC is advanced.
SUBtract Register Pair DE (the current place value, e.g. 1000) from Register Pair HL (the remaining value), with borrow. If HL >= DE, the subtraction succeeds (CARRY clear) and the digit count increases. If HL < DE, the subtraction underflows (CARRY set) and the loop exits.
JUMP back to 5447H to attempt another subtraction. This loop runs until HL goes negative, giving the digit count in A.
5461H - Numeric Buffer Incrementer
Increments a decimal digit field within a format buffer. Called with HL pointing to the start of a buffer region; the routine advances 4 bytes in (to the last digit of the number field), increments the ASCII digit there with carry propagation, and wraps digits that exceed '9' back to '0' while carrying into the next more-significant digit position to the left.
Fetch the ASCII digit at (HL) (initially the ones digit, then higher digits on carry) into Register A.
LOOP BACK to 5465H to increment the next more-significant digit. This loop propagates a carry leftward through the digit field until it finds a digit that does not overflow (or until it exits the buffer boundary).
5472H - Printer Ready and BREAK Check
Called after each output line to verify the printer is ready and to detect a BREAK keypress. First checks whether the command interpreter is active (bit 5 of 430FH) and exits early if not in interactive mode. Then checks the disk status port at 3840H for the printer-busy flag (bit 2) and verifies RS-232 status via ports 3801H and 3880H. Finally polls for a keypress via ROM 002BH, returning NZ (with the key in A) if BREAK or another key was detected, Z if the printer is ready and no key was pressed.
Fetch the keyboard status byte from 3840H again. This time the check is for a user keypress (BREAK or any other key) to abort the listing. The printer-busy bit (bit 2) is also re-checked on each poll iteration.
549EH - DOS Error Exit
Common error exit. ORs bit 6 into the error code in A to mark it as a command-level error, then jumps to the SYS0 DOS error handler at 4409H.
54A3H - "FILE SPEC REQUIRED" Error Handler
Displayed when no filespec is present on the command line, or when the filespec begins with '*' (a device specification, which PRINT does not accept as input).
54ACH - Data: "FILE SPEC REQUIRED" String
ASCII error message string. The disassembler incorrectly decodes these bytes as Z80 instructions; they are text data.
54BFH - "PARAMETER ERROR" Error Handler
Displayed when the file open utility at 4476H returns an error, indicating an invalid or unrecognized parameter in the command line.
54C8H - Data: "PARAMETER ERROR" String
ASCII error message string. The disassembler incorrectly decodes these bytes as Z80 instructions; they are text data.
54D8H - Data: HEX/ASCII Print Format Buffer
A composite 20-byte format buffer used to construct the line-prefix and row-address output for both ASCII and HEX mode listings. The disassembler misinterprets these bytes as instructions; they are data. Sections of this buffer are filled in at runtime by the decimal formatter (542BH) and the hex address display routine (4DECH).
54EFH - Data: Input File FCB
32-byte File Control Block for the input file. Passed to 4476H (Open file utility) with DE=54EFH at 5234H. The FCB fields (status, drive config pointer, sector buffer, etc.) are populated by the file open routine and used for all subsequent file reads via ROM 0013H.
550FH - Data: Additional FCB/Option Data
Continuation of the FCB area and option parameter data. The disassembler decodes bytes 550FH-5517H as instructions; they are data associated with the option parameter table or FCB overflow fields.
5517H - Data: Table Terminator
ISAM 51H - ATTRIB - Offset 24A7
VTOS 4.0 ATTRIB and PROT Commands Disassembly (Model I)
The ATTRIB command (IDAM 52H, SYS6 member 26) modifies the protection attributes of an individual file. It accepts an optional file specification and a parenthesized parameter list containing any combination of: ACC= (access password), UPD= (update password), PROT= (protection level), INV (make invisible), and VIS (make visible). The protection level may be one of EXEC, READ, WRIT, RENA, NAME, KILL, or FULL/ALL. The command parses the parameter list character by character, validates each keyword against an 8-entry table, encodes the protection settings into a numeric value, and updates the directory entry via SYS0 routines.
Data Areas
| Address Range | Purpose |
|---|---|
| 51E0H-51E7H (8 bytes) | Password work buffer - 8-byte space-padded field used as both input staging area and comparison buffer for ACC=/UPD= password parsing |
| 51F3H-51F4H (2 bytes) | Access password hash storage - holds the 2-byte encoded access password (ACC=) after parsing and normalization |
| 51F5H-51F6H (2 bytes) | Update password hash storage - holds the 2-byte encoded update password (UPD=) after parsing and normalization |
| 5337H (1 byte) | Protection level byte - self-modifying operand; receives the encoded PROT= numeric value (0=EXEC through 7=FULL/ALL) computed by the keyword table scanner |
| 533FH (1 byte) | Attribute flags accumulator - self-modifying operand; bit 3 (08H) = INV flag set, bit 3 cleared (AND F7H) = VIS flag set; OR'd into the directory entry attribute byte |
| 532FH (1 byte) | Parameter presence flags - self-modifying operand; bit 0 set = PROT= was supplied, bit 1 set = UPD= was supplied (B=01H and B=02H OR'd in respectively) |
| 53CBH-53DAH (16 bytes) | PROT= keyword comparison table - 8 entries of 2 ASCII bytes each: AL, EX, RE, WR, RN/RE, NA, KI, FU (the disassembler decodes these as instructions; they are data) |
| 53DBH (1 byte) | Filespec template pointer - points to a null or space that serves as the template for CALL 441CH (Extract Filespec); contains the filespec default-extension stub |
| 53DCH (1 byte) | Drive number byte embedded in the filespec template area - ANDed with 07H and tested to determine if a wildcard drive (*) was specified |
Major Routines
| Address | Name and Purpose |
|---|---|
| 5200H | ATTRIB Main Entry Entry: HL = command line pointer. Calls 441CH to extract the filespec, validates that a wildcard drive was not specified, calls 4424H to open the file, then parses the parameter list. |
| 5237H | Parameter List Dispatcher Entry: HL = pointer to current position in the '(' parameter block. Reads one character and dispatches to the handler for I (INV), V (VIS), A (ACC=), U (UPD=), or P (PROT=). Unrecognized characters jump to the PARAMETER ERROR exit. |
| 5250H | PROT= Handler Entry: HL = pointer past 'P'. Calls 52F7H to advance past '=' and locate the value. Then scans the 8-entry keyword table at 53CBH, comparing the first two characters against each entry, computing the encoded protection level (0-7 or 2/5 for special cases), and storing it to 5337H. |
| 52CAH | ACC= Handler Entry: HL = pointer past 'A'. Calls 52F7H to advance to the value, then calls 52F4H (RST 28H SVC 0E4H) to hash/encode the password. Stores the 2-byte result at 51F3H. |
| 52DFH | UPD= Handler Entry: HL = pointer past 'U'. Identical flow to ACC= handler but stores result at 51F5H and sets B=04H as the presence flag. |
| 52F4H | Password Encoder SVC Stub Issues SVC 0E4H (RST 28H) to invoke the VTOS password hashing routine. DE = pointer to 8-byte space-padded password buffer at 51E0H. Returns HL = 2-byte encoded password. |
| 52F7H | Advance-to-Value Scanner Entry: HL = pointer somewhere before '='. Advances HL past any characters until '=' is found, then one more position to point at the value. Returns Z flag set if the next character is a delimiter (0DH, ')', or ','), indicating an empty or absent value. |
| 530CH | Password String Copier Entry: HL = pointer to quoted or unquoted password value in the command line. Pre-fills the 8-byte buffer at 51E0H with spaces, then calls 50B1H to copy up to 8 characters. Returns OR A,01H to signal that a password was provided. |
| 5323H | Directory Update and Exit Entry: HL = pointer to closing ')'. Loads the BCB (directory entry descriptor) via 4B10H, applies the PROT level to the entry byte (masking bits 7-4 or bit 3 as appropriate), applies the INV/VIS flag to the entry byte, writes the ACC= and UPD= password bytes if their presence flags are set, then calls 4B1FH to write the updated entry back to disk and exits via 402DH. |
| 536BH | Error Exit with Bit 6 Set OR A,40H / JP 4409H - sets the extended-context suppression flag (bit 6) in the error code and jumps to the DOS error handler. |
| 5370H | FILE SPEC REQUIRED Error Displays the message at 5379H ("FILE SPEC REQUIRED" + 0DH) via 447BH and exits via 4030H. |
| 538CH | -NOTHING DONE- Error Displays the message at 5395H ("-NOTHING DONE-" + 0DH) via 447BH and exits via 4030H. Reached when the character after '(' is not a recognized keyword initial letter. |
| 53A4H | PARAMETER ERROR Exit Displays "PARAMETER ERROR" + 0DH via 447BH and exits via 4030H. Reached when a keyword is unrecognized or a value is missing/invalid. |
| 53BDH | Uppercase Converter / Advance Scanner Entry: HL = pointer to first character of a token. Advances HL past leading spaces (CP 20H / RET C), converts each character in-place to uppercase (RES 5,(HL) if >= 'a'), and continues until a non-alpha character is reached. Returns HL pointing at the terminator. |
5200H - ATTRIB Entry: Extract Filespec and Open File
The ATTRIB command begins by parsing the filespec from the command line, confirming it is not a wildcard-drive reference, and opening the named file to obtain its directory entry for modification.
The file is now open and the FCB at 4200H contains the directory entry. The self-modifying storage variables at 532FH, 5337H, and 533FH retain whatever values they held from a prior command invocation; they will be overwritten as parameters are parsed below. Register HL holds the command line pointer.
5225H - Skip Spaces and Find Opening Parenthesis
After the file is opened, the command line pointer in HL is advanced past any spaces to locate the opening '(' of the parameter list. If no '(' is found, an appropriate error is displayed.
Space Skip Loop
The following loop advances HL past any spaces until the '(' is found, or branches to an error if an unexpected character is encountered.
5237H - Parameter List Dispatcher
With HL pointing one character past the opening '(', this dispatcher reads the next keyword initial letter and branches to the appropriate parameter handler: I for INV, V for VIS, A for ACC=, U for UPD=, or P for PROT=. Unrecognized characters fall through to the PARAMETER ERROR exit.
5250H - PROT= Handler: Protection Level Keyword Scanner
This handler processes the PROT= parameter. It advances past the '=' sign to find the protection keyword value, then scans an 8-entry two-character comparison table to match the keyword and compute its numeric protection level encoding (0-7, with special cases for READ+WRIT and NAME).
Loop Start
B = 8 (entries remaining). HL = pointer into the keyword table. DE = the first two characters of the user-typed protection level keyword (from the command line buffer via 51E0H).
Loop End
526CH - PROT= Second-Character Match and Level Encoder
Called conditionally when the first character of a keyword table entry matches. Checks the second character, then computes the numeric protection level (0-7, adjusted for READ/WRIT and NAME special cases) and stores it via self-modifying code.
Match Found
Both characters matched. Now compute the protection level. The loop counter B holds the number of remaining entries including the current one (8 for the first entry AL, 7 for EX, down to 1 for FU). The formula maps B to the correct VTOS protection level code.
The following code handles a special case for the NAME protection level (encoded as 05H initially but requiring adjustment based on the extension character at 51E2H in the command buffer).
Store Register A (the computed protection level: 00H=FULL, 01H=KILL, 02H=NAME/RENA, 03H=WRIT, 04H=READ, 05H=EXEC, or other values per the table) into memory location 5337H. Address 5337H is the operand byte of an OR A,00H instruction at 5336H in the directory-update section. At runtime, this instruction will OR the directory entry's attribute byte with the protection level just stored here.
5289H - Parameter Presence Flag Update and Next Parameter Loop
After any parameter handler has completed its work, this section updates the parameter presence flag byte at 532FH, then reads the next character to determine whether to continue (comma separator), close (')'), or error (anything else).
Store the updated presence flags byte back to 532FH. This running OR accumulates the flags for all parameters seen so far. The directory update section at 5323H will read this byte to determine which directory entry fields to modify.
52A6H - INV Parameter Handler
Sets the invisible flag (bit 3) in the attribute accumulator at 533FH. The INV option causes the file to become invisible in directory listings.
Store the updated attribute byte (with bit 3 set = invisible) back to 533FH. This value will be ORed into the directory entry's attribute byte during the directory update at 5323H.
52B8H - VIS Parameter Handler
Clears the invisible flag (bit 3) in the attribute accumulator at 533FH. The VIS option makes the file visible in directory listings by clearing the invisible bit.
Store the updated attribute byte (bit 3 cleared = visible) back to 533FH.
52CAH - ACC= Handler: Access Password Entry
Processes the ACC= (access password) parameter. Advances to the value, calls the password string copier to collect the typed password into the work buffer, hashes it via SVC 0E4H, and stores the 2-byte result at 51F3H.
52DFH - UPD= Handler: Update Password Entry
Processes the UPD= (update password) parameter. Identical flow to ACC= but stores the hashed result at 51F3H and uses presence flag bit 04H.
52F4H - Password Encoder SVC Stub
A two-instruction stub that loads the SVC code 0E4H and issues RST 28H to invoke the VTOS password hashing function. The hash function takes the 8-byte space-padded password at DE and returns the 2-byte encoded hash in HL.
52F7H - Advance-to-Value Scanner
Scans forward from the current HL position to find the '=' sign and position HL at the first character of the value. Returns Z if the value is empty or absent (next character is a delimiter), NZ if a value is present. Also serves as the keyword-termination checker for INV and VIS (which have no '=' or value).
530DH - Password String Copier
Pre-fills the 8-byte password work buffer at 51E0H with spaces, then calls the subroutine at 50B1H to copy the typed password characters from the command line into the buffer. Returns OR A,01H to signal that a non-empty password was collected.
5323H - Directory Entry Update and Successful Exit
Called when the closing ')' is found and all parameters have been accumulated. Reads the directory entry descriptor via 4B10H, applies the PROT level, INV/VIS bit, and password bytes according to the presence flags, writes the updated entry via 4B1FH, and exits via 402DH (DOS READY).
The following sequence reads the self-modifying presence flags accumulator at 532FH and tests individual bits to build the modification flag set in D. Each bit corresponds to one of the parameter types that was parsed.
OR Register A with the byte at address 5337H. The operand byte at 5337H was written by LD (5337H),A at 5286H and holds the encoded PROT= level value (00H-07H). At runtime, this instruction ORs the cleared attribute byte with the new protection level, producing the updated attribute byte with the new PROT setting in bits 2-0.
OR Register A with the byte at address 533FH. The operand byte at 533FH was written by LD (533FH),A in the INV handler (52B1H, sets bit 3 to 08H) or the VIS handler (52C3H, clears bit 3 to 00H). At runtime, this ORs the current attribute byte with either 08H (invisible) or 00H (visible), applying the INV/VIS change.
536BH - Error Exit with Extended-Context Suppression
Sets bit 6 in the error code (extended-context suppression) and jumps to the DOS error handler. Used for I/O errors and directory access failures.
5370H - FILE SPEC REQUIRED Error Display
Displays the "FILE SPEC REQUIRED" message and exits via 4030H. Reached when no filespec was provided or a wildcard drive was specified.
5379H - "FILE SPEC REQUIRED" String Data
ASCII string data. The disassembler decodes these bytes as Z80 instructions; they are in fact the "FILE SPEC REQUIRED" error message terminated by 0DH (carriage return).
538CH - "-NOTHING DONE-" Error Display
Displays the "-NOTHING DONE-" message and exits via 4030H. Reached when the character after '(' is not a recognized keyword initial letter, meaning no valid parameter was entered.
5395H - "-NOTHING DONE-" String Data
ASCII string data terminated by 0DH.
53A4H - PARAMETER ERROR Display and Exit
Displays the "PARAMETER ERROR" message and exits via 4030H. Reached when a keyword is unrecognized, a required value is absent, or the parameter list is malformed.
53ADH - "PARAMETER ERROR" String Data
ASCII string data terminated by 0DH.
53BDH - Uppercase Converter and Advance Scanner
Advances HL past leading spaces and converts alphabetic characters in the command buffer to uppercase in place. Stops when a character below 20H (space) is encountered, returning with HL pointing at the non-space, non-alpha delimiter.
53CAH - PROT= Keyword Comparison Table Data
An 8-entry table of 2-byte ASCII keyword stubs used by the PROT= scanner at 525DH-5267H. Each entry holds the first two uppercase characters of a protection level keyword. The disassembler decodes these bytes as instructions; they are data.
ISAM 52H - PROT - Offset 268D
PROT Command Disassembly (Model I)
The PROT command (IDAM 53H, SYS6 member 29) operates at the disk level rather than the file level. It reads and optionally rewrites the master password and disk name stored in the GAT sector. It also performs two maintenance services on the directory that are required for compatibility with non-VTOS-generated disks. These services normalize directory entries that may have been created by other operating systems. The command requires the user to supply the current master password before any changes are made, either via the MPW= parameter or through an interactive prompt.
PROT Command - Variable and Data Areas
| Address Range | Purpose |
|---|---|
| 5370H (1 byte) | Drive number - self-modifying operand; receives the drive digit from the ':d' operand of the command line (ASCII '0' minus 30H = binary 0-7); used as C register for drive-selection calls |
| 5394H-5395H (2 bytes) | Self-modifying storage for BC (LD (5394H),BC) - used to pass the two-word DE pair representing the new disk name or master password pointer between the main entry and subroutines |
| 5339H-533AH (2 bytes) | HL pointer save - stores the HL value (directory entry pointer) received from the directory scan loop at 530DH, restored at 534DH for the directory update write sequence |
| 531EH (1 byte) | Self-modifying directory entry address byte - the low byte of the current directory entry's HL pointer, used at 534DH to verify that the entry being written is the one that was read |
| 57CCH (1 byte) | Password comparison result byte - receives the computed password character (2AH='*', 23H='#', 05H, or 00H) determined by examining the GAT sector fields at 57A6H, 5788H, and 5783H; 00H indicates an exact master-password match |
| 57CBH (1 byte) | GAT sector flag byte - read at 57CBH within the GAT buffer (5800H base); tested CP 30H to determine if the disk uses a format with a stored master password |
| 5600H-5607H (8 bytes) | Password input work buffer - 8-byte field used by the password prompt/entry subroutines at 539CH and 53BBH for collecting user-typed master password characters |
| 5700H-57FFH (256 bytes) | GAT sector read buffer (track 0, sector 0) - the complete GAT sector is read here by 4B45H; key fields: 57CBH (format flag), 57CCH (password result), 57CEH (2-byte disk name pointer or name length), 57D0H-57D7H (master password, 8 bytes), 5783H, 5788H, 57A6H (examined for password equivalence) |
| 5800H-58FFH (256 bytes) | Directory sector buffer - used by 4B45H (read) and 4768H (write) for the disk directory; the first directory sector is read here at 527EH |
| 53D8H-53F7H (32 bytes) | String data area - contains the ASCII prompt strings: "MASTER PASSWORD " at 53D8H (used as the password prompt label) and "INVALID MASTER PASSWORD" + 0DH at 53F0H |
| 53F8H-5407H (16 bytes) | Error strings: "INVALID COMMAND DURING PROGRAM CHAINING" referenced from 545BH-5482H; "PARAMETER ERROR" + 0DH at 5442H-5451H |
PROT Command - Major Routines
| Address | Name and Purpose |
|---|---|
| 5200H | PROT Main Entry Entry: HL = command line pointer. Tests 430FH bit 5 for chaining mode (error if set). Parses the optional ':d' drive argument, saves BC to 5394H. Calls 4476H to open the disk's GAT/directory information file (FCB at 54F4H). Then examines the three option flags (from 5239H, 52EDH, 52F7H, 524FH) to determine if any work was requested. |
| 520DH | Exit Path for Empty/Null Work Loads DE=0 (no new disk name), then merges into the main flow. Tests DE for zero to decide whether to skip the disk-name update path. |
| 5239H | Option Flag Check and Master Password Validation ORs together the three option presence bytes (at 5239H, 52EDH, 52F7H, 524FH). If all are zero (no options requested), jumps to 5483H (display "-NOTHING DONE-" and exit). Otherwise calls 5364H to validate the current master password. |
| 524BH | New Disk Name Handler If a new disk name was provided (DE non-zero after 5364H return), calls 539CH to prompt for and collect the new disk name string, stores the HL pointer in 57CEH, and copies the 8-byte name to 57D0H via LDIR. |
| 5266H | New Master Password Handler If a new master password pointer is non-zero (DE from 52F7H option), calls 5396H to prompt for and collect the new master password into 5600H, and stores HL (pointer past the collected string) at 57CEH. |
| 5276H | Directory Maintenance Loop Calls 4B65H to set up track/sector for directory I/O. Reads directory sectors via 4B45H. For each sector, walks every 32-byte directory entry. Clears obsolete non-VTOS password fields (blanks out the ACC/UPD pairs if the entry is a non-system, non-invisible active file), then calls 4B1FH to write the updated entry back. Loops through all directory sectors. |
| 5364H | Master Password Validator Entry: HL = command line pointer, DE = pointer saved in 5394H. Calls 539CH to collect the master password from the command line or prompt. Computes an equivalence check against the GAT master password: first XOR-checks against 0C294H (the standard no-password sentinel), then XOR-checks against the saved disk name pointer in 57CEH. Returns Z if the password matches a valid credential, NZ if invalid. On NZ, displays "INVALID MASTER PASSWORD" and exits via 4030H. |
| 5396H | Password Collect with SVC Wrapper Entry: DE = pointer to password value in command line. Calls 539CH, then issues RST 28H / SVC 0E4H to hash the collected string. Returns DE = pointer past the collected field. |
| 539CH | Password String Collector Entry: DE = pointer to password value in command line or prompt. If DE is zero (no command-line value), calls 4467H to display the "MASTER PASSWORD " prompt then calls 0040H (ROM line input) to collect the password into 5600H. If DE is non-zero, copies characters from DE into 5600H, stopping at 0DH, ',' or '"', padding the remainder to 8 spaces with DJNZ. Returns DE = pointer past collected string, HL = pointer to 5600H end. |
| 5408H | GAT Sector Read Subroutine Entry: C = drive number. Calls 4B65H to set up track/sector, then 4B45H to read sector 0 (track 0) of the specified drive into 5700H. Returns Z on success, NZ with A=14H (Read Error) on failure. |
| 541BH | GAT Sector Write Subroutine Entry: C = drive number. Calls 4B65H, then 4768H to write, then 4772H to verify (read-after-write). If the verify comparison byte (A) equals 06H, returns A=15H (Write Error). Otherwise returns A=15H unconditionally on NZ from 4768H. Returns A=15H to signal write failure. |
| 5434H | I/O Error Exit OR A,40H / JP 4409H - sets the extended-context suppression bit in the error code and jumps to the DOS error handler. |
| 5439H | PARAMETER ERROR Exit Loads HL = 5442H ("PARAMETER ERROR" + 0DH), calls 447BH, then jumps to 4030H. |
| 5452H | INVALID COMMAND DURING PROGRAM CHAINING Exit Loads HL = 545BH, calls 447BH, then exits via 4030H. Reached when bit 5 of 430FH is set (chaining mode active). |
| 5483H | -NOTHING DONE- Exit Loads HL = 548CH ("-NOTHING DONE-" + 0DH), calls 447BH, then exits via 4030H. |
5200H - PROT Entry: Chaining Check, Drive Parse, and File Open
The PROT command begins by verifying it is not being called from a program chain (bit 5 of 430FH), then parses the optional ':d' drive argument from the command line, initializes BC to zero (no new disk name or master password yet), and calls 4476H to open the disk's directory control file.
The following instructions at 520DH-521AH form an alternate entry path reached when DE and A hold a computed result from a subroutine. Under normal entry at 5200H, this path is skipped by the JR above.
Store Register Pair BC (currently 0000H from the initialization at 5208H) to 5394H-5395H. These two bytes form the self-modifying operand for LD DE,(5394H) instructions at 5364H and 52ECH. Storing 0000H here initializes the "new disk name pointer" to null.
Store Register A (the drive number) to 5370H. Address 5370H is the operand byte of a LD C,nn instruction that appears later in the code (e.g., at 52A7H-52AAH). At runtime, that instruction will load C with this drive number to pass to the disk I/O routines.
5239H - Option Flag Check and Master Password Validation
Reads three self-modifying flag bytes to determine whether any options were specified (LOCK/UNLOCK/PW=/NAME=/MPW=). If none were requested, displays "-NOTHING DONE-" and exits. Otherwise, validates the master password before proceeding.
524EH - New Disk Name Handler
Tests whether a new disk name was provided. If so, calls the password collector to gather the new name string and copies it into the GAT buffer's master password area.
5266H - New Master Password Handler
Tests whether a new master password was provided. If so, calls the password collector to gather the new password and stores the end-of-string pointer in 57CEH for later use by the password validator.
5276H - GAT Sector Read and Password Format Check
Reads sector 0 (the GAT sector) from track 0 of the target drive into the buffer at 5800H (first read) and 5700H (second read, via 541BH). Examines the format flag byte at 57CBH and compares several fields to determine the stored password representation and whether a clean password match is achievable.
The disk format does not support a stored master password. The following code computes a substitute password representation based on other GAT fields, to determine what the "equivalent" password character should be for this disk format.
52A7H - Read GAT Sector and Validate Password
Reads the GAT sector into the primary buffer at 5700H (via the sector-read subroutine at 541BH), then calls 541BH to validate the drive's master password against the stored value.
52B1H - Directory Sector Format Verification
Checks whether the first directory sector begins with a valid, current master password format marker. If the first byte of sector 1 is a comma (2CH), rewrites it as 0C4H to update the sector marker to the VTOS format before continuing.
52E9H - Directory Entry Maintenance Loop: Load Directory Pointer
Loads the current disk name pointer from 57CEH, checks whether it is null, and sets up for the directory sector walk that normalizes file protection attributes across all directory entries.
5300H - Directory Maintenance Loop Setup and Entry Scan
Initializes the directory walk by saving the HL pointer and setting up the sector loop. Reads each directory sector and iterates through every 32-byte entry, advancing by 20H bytes per entry. Calls 4B1FH to write updated entries and loops through the full directory structure.
Store Register Pair HL (the directory entry pointer) to 5339H-533AH. This saves the HL pointer for recovery during the directory entry iteration. The value will be reloaded at 534DH.
Loop Start
The following loop iterates through all directory entries in the current sector. HL = pointer to current directory entry position in the sector buffer. The loop advances by 20H bytes per entry (32 bytes) and processes the full sector before moving to the next.
Store Register A (the within-entry offset of the current directory pointer) to 531EH. This address is the operand byte of an XOR A,L instruction at 5350H. At runtime, 5350H will XOR the current L with this stored value to verify that the entry being written matches the one that was read.
Forward-Link Entry
The entry at the current HL position is a forward-link (type 10H). VTOS uses these to chain directory sectors together. The forward-link entry's next-pointer field (at HL+01H and HL+02H) is cleared to 0000H to normalize it, then the walk continues.
Entry Advance and Loop Continue
HL now points to the current directory entry. The following advances HL by 20H bytes to the next entry.
Loop End
The sector has been fully scanned. The following checks whether the current entry matches the saved pointer and calls 4B1FH to write if so, then continues to the next sector or exits.
Loop End
5364H - Master Password Validator
Collects the master password from the command line (MPW= value) or prompts the user interactively, then validates it against the disk's stored master password. Compares the supplied password against both the standard no-password sentinel (0C294H) and the stored disk name pointer in 57CEH. Returns Z on success; on failure, displays "INVALID MASTER PASSWORD" and exits via 4030H.
538FH - I/O Error Exit with Context Suppression
Sets the extended-context suppression bit in the error code and exits via the DOS error handler. Used for read/write hardware failures.
5396H - Password Collect with SVC Wrapper
A two-step wrapper: calls the string collector at 539CH to gather the password characters, then issues RST 28H SVC 0E4H to hash/encode the collected string. Used for both master password validation and new password collection.
539CH - Password String Collector
Collects a password or name string either from the command line (if DE is non-zero, pointing at the value) or interactively via a prompted ROM line-input call (if DE is zero). Copies up to 8 characters into the buffer at 5600H, padding the remainder with spaces. Used by both the master password validator and the new-name/new-password handlers.
Command-Line Value Path
DE is non-zero, pointing at the password/name value in the command line. The following loop copies up to 8 characters, stopping at delimiter characters (0DH, ',', '"').
Interactive Prompt Path
DE was zero (no command-line value). Display the prompt string and collect the password via ROM line input.
53D8H - String Data: Prompt and Error Messages
ASCII string data used as prompts and error messages. The disassembler decodes these bytes as Z80 instructions; they are in fact text data terminated by 0DH or 03H.
5408H - GAT Sector Read Subroutine
Reads the GAT sector (track 0, sector 0) from the target drive into the buffer at 5700H. Called by the master password validator at 5371H. Returns Z on success, NZ with A=14H on read error.
541BH - GAT Sector Write Subroutine
Writes the GAT sector buffer at 5700H back to track 0, sector 0 of the target drive, then performs a read-after-write verify via 4772H. Returns Z on success, NZ with A=15H (Write Error) on failure.
5434H - I/O Error Exit
Sets the extended-context suppression bit and exits via the DOS error handler. Used for all hardware read/write failures within PROT.
5439H - PARAMETER ERROR Display and Exit
Displays "PARAMETER ERROR" and exits via 4030H. Reached when the file open (CALL 4476H) fails, indicating a bad drive number or other parameter problem.
5442H - "PARAMETER ERROR" String Data
ASCII string data terminated by 0DH.
5452H - INVALID COMMAND DURING PROGRAM CHAINING Exit
Displays the chaining-context error message and exits via 4030H. Reached when bit 5 of 430FH is set at entry (PROT is being invoked from within a program chain, which is not permitted).
545BH - "INVALID COMMAND DURING PROGRAM CHAINING" String Data
ASCII string data terminated by 0DH. The disassembler decodes these bytes as Z80 instructions; they are text data.
5483H - "-NOTHING DONE-" Display and Exit
Displays "-NOTHING DONE-" and exits via 4030H. Reached when no options were specified in the PROT command (all option flags were zero).
548CH - Remaining String Data and FCB/Work Areas
ASCII string data for remaining messages, followed by FCB and work buffer areas. The disassembler decodes these bytes as instructions; they are data.
ISAM 53H - RENAME - Offset 29BB
VTOS 4.0 RENAME Command Disassembly
VTOS 4.0 SYS6 library overlay member for the RENAME command (IDAM 51H, SYS6 member 24). The overlay loads at 5200H in the standard overlay slot and is dispatched through the RST 28H SVC 88H mechanism from SYS1.
The RENAME command accepts two filespecs separated by a preposition (TO, AS, or similar noise word). The first filespec identifies the existing file; the second specifies the new name. VTOS uses dynamic defaults for the second filespec: any field (name, extension, drive) that is omitted from the new filespec is taken from the corresponding field of the first filespec. This means "RENAME FOO/BAS TO /CMD" renames only the extension, keeping the name FOO and the original drive. The command opens the source file's directory entry, applies the new name fields, then calls the directory-update routines to write the change back to disk. It also handles the case where the new filespec specifies a different drive by inserting the correct ':d' prefix into the new filespec string before opening.
Two subroutines at 52C0H and 52D4H perform the core filespec-merging and character-scanning work: 52C0H normalizes the new filespec (applying dynamic defaults from the first filespec), and 52D4H scans a filespec field advancing a source pointer while optionally copying characters to a destination buffer under control of a character-class test at 52FBH. A password hash SVC stub at 52BDH encodes the password for the renamed directory entry. The command exits via 402DH on success or the error path at 531BH (OR A,40H / JP 4409H) on failure.
Variable and Data Areas
| Address Range | Purpose |
|---|---|
| 539AH-539FH (6 bytes) | First filespec template/buffer - receives the parsed first filespec (source file) from CALL 441CH. The drive byte is at 53A0H (one byte past the base), used for drive comparison. Byte at 539BH holds the drive field of the first filespec. |
| 53BAH-53D9H (32 bytes) | Second filespec template/buffer - receives the parsed second filespec (new name) from the second CALL 441CH at 5212H. After merging, this buffer contains the fully resolved new filespec with dynamic defaults applied. Also used as source for the directory entry name/extension write at 5294H-529AH. |
| 51DEH-51DFH (2 bytes) | FCB pointer save area - stores the original FCB pointer (HL) before the second CALL 4424H at 5275H, restored immediately after. This preserves the FCB address across the second open call so that the renamed directory entry can be located. |
| 51E8H-51F2H (11 bytes) | Filename/extension work buffer (11 bytes = 8-char name + 3-char extension, from SYS2 runtime variable area) - source for the LDIR at 529AH that copies the new name/extension into the directory entry at +05H. |
| 53F6H-53FFH (data) | String data area at the end of the overlay - contains error messages and string constants used by the display routines. Decoded as instructions by the disassembler; they are ASCII data. |
Major Routines
| Address | Name and Purpose |
|---|---|
| 5200H | RENAME Main Entry Entry: HL = command line pointer. Extracts the first filespec via 441CH, validates it is not a wildcard drive, then extracts the second filespec (new name) via 441CH. If the second filespec was parsed with Z set (exact match, no defaults needed), skips the LDIR merge. Otherwise copies 32 bytes from the first filespec buffer into the second, then calls the merge subroutine at 52C0H. Opens the source file via 4424H, validates the drive number, locates the drive separator in the new filespec, and builds the final directory-update call sequence. |
| 52BDH | Password Hash SVC Stub Loads A=0D4H and issues RST 28H to invoke the VTOS password encoding service for the renamed entry. |
| 52C0H | New Filespec Normalizer Entry: HL = first filespec buffer (539AH), DE = second filespec buffer (53BAH). Applies dynamic defaults: if the second filespec lacks a drive field (first character not uppercase alphabetic), calls 52FBH to check and possibly insert the drive from the first filespec. Then scans past the '/' separator via 52D4H (B=2FH) and past the '.' extension separator via 52D4H (B=2EH). Returns with the merged filespec in the destination buffer. |
| 52D4H | Field Scanner / Default Copier Entry: DE = source pointer (new filespec), B = delimiter character ('/' or '.'). Advances DE through characters, skipping alphabetic/numeric chars (those >= 'A' are passed without copying, those that are digits 0-9 or ':' are advanced). When a non-field character is encountered, calls 52E8H to copy the corresponding field from the first filespec buffer (HL) if the new filespec had no value for that field. |
| 52E8H | Field Copy from First Filespec Entry: HL = pointer into first filespec buffer, B = delimiter character. Copies characters from (HL) into (DE) until the delimiter B or an end-of-string marker (03H or 0DH) is found in the source. After copying, calls 5307H to write the delimiter into both buffers and advance DE past it. |
| 52FBH | Drive Character Validator Entry: HL = pointer into first filespec buffer, A = first character of second filespec. Tests whether A is a digit (30H-39H), alphabetic after '9' but below 'A', or already uppercase. Returns without modifying if A is already a valid drive designator; calls INC HL / continues scanning otherwise. |
| 5307H | Delimiter Inserter Copies current HL char into (DE), advances DE, then shuffles the remainder of the second filespec destination buffer one byte forward to make room for the inserted delimiter. Loops until 03H or 0DH (end-of-string) is found in the source buffer. |
| 531BH | Error Exit with Bit 6 Set OR A,40H / JP 4409H. Sets extended-context suppression bit and exits via the DOS error handler. |
| 5320H | FILE SPEC REQUIRED Error Displays the message at 5329H ("FILE SPEC REQUIRED" + 0DH) via 447BH and exits via 4030H. |
| 533CH | RENAME IT TO WHAT? Error Displays the message at 5345H ("RENAME IT TO WHAT?" + 0DH) via 447BH and exits via 4030H. Reached when the new filespec drive byte is less than 0EH (no valid new name). |
| 5358H | ILLEGAL DRIVE SPECIFICATION Error Displays the message at 5361H ("ILLEGAL DRIVE SPECIFICATION" + 0DH) via 447BH and exits via 4030H. Reached when the source and destination drive numbers differ and the difference is non-zero after the AND/XOR test. |
| 537DH | Successful Rename Exit via 402DH Jumps to 402DH (DOS READY) after all directory updates complete successfully. |
5200H - RENAME Entry: First Filespec Extract and Validation
The RENAME command opens by extracting the source filespec from the command line, rejecting wildcard drives, then extracting the second (new-name) filespec. If the second filespec was fully specified, it is used directly; otherwise the first filespec's fields are merged in as dynamic defaults.
Dynamic Defaults Merge
The second filespec was not fully specified. Copy the entire first filespec buffer (32 bytes) into the second filespec buffer, then call the normalizer at 52C0H to overwrite only those fields that were explicitly provided in the new filespec, leaving the rest as copies from the first filespec.
521DH - Drive Validation and Filespec Normalization
Checks whether the second filespec's drive byte is valid (>= 0EH), then calls the normalizer to merge any unspecified fields from the first filespec into the second. Finally, opens the source file to obtain its directory entry.
5243H - Locate Drive Separator in New Filespec
Scans the second (new-name) filespec buffer to find the ':' drive separator. If found, compares the drive digit that follows it against the source file's drive number. If they differ, the rename would move the file across drives (not supported), and an "ILLEGAL DRIVE SPECIFICATION" error is displayed. If they match (or if no ':' is present), the rename proceeds.
The ':' character was found at the current HL position. HL now points one past the ':', so (HL) holds the drive digit that follows the colon.
525FH - Insert ':d' Drive Prefix into New Filespec
The new filespec had no ':' drive separator. This section inserts ':' followed by the source file's drive digit (as an ASCII character) and a new 0DH terminator into the new filespec buffer, ensuring the directory open call will use the correct drive.
526EH - Open New Filespec and Perform Directory Rename
Opens the new filespec (to validate it and create/locate the target directory slot), saves and restores the FCB pointer, then calls the directory entry update routines to write the new name into the source file's directory entry.
The new filespec returned error code 18H, which indicates the target name is available (not yet in the directory). The rename proceeds by finding the source file's directory entry via 4B10H and copying the new name/extension fields into it.
5288H - Validate Directory Entry and Write New Name
Calls 4B10H to validate and locate the source file's directory entry, then copies the new name/extension from the second filespec buffer into the entry at offset +05H, and calls 4B1FH to write the updated entry back to disk.
52BDH - Password Hash SVC Stub
A two-instruction stub that loads the SVC code 0D4H and issues RST 28H to invoke the VTOS password encoding service for the renamed directory entry.
52C0H - New Filespec Normalizer
Applies dynamic defaults from the first filespec to the second filespec. Checks whether the second filespec already starts with a valid drive designator (uppercase letter); if not, calls the drive validator/copier at 52FBH. Then advances through the '/' name separator and '.' extension separator via the field scanner at 52D4H, copying defaults for any absent fields.
52D3H-52D4H - Field Scanner / Default Copier
Advances DE through a filespec field (name or extension), distinguishing between field characters (uppercase letters 'A'+, digits '0'-'9') and delimiter/other characters. When a non-field character is encountered before the delimiter B is found, calls 52E8H to copy the corresponding field from the first filespec into the second. When the delimiter B itself is found, advances past it and returns.
52E8H - Field Copy from First Filespec (Default Apply)
Called when the new filespec lacks a value for a particular field. Copies characters from the corresponding field in the first filespec (HL) into the second filespec destination (DE), until the delimiter B or an end-of-string is found in the source. Then calls 5307H to write the delimiter into the destination and advance DE past it.
52FBH - Drive Character Validator
Called when the first character of the new filespec is less than 'A'. Tests whether it is a valid drive character (digit '0'-'9') or falls outside valid ranges. If the character is already a valid digit, returns immediately. Otherwise INC HL to skip to the next character in the first filespec buffer and continues. Used to find the drive field of the first filespec for copying as a default.
5307H - Delimiter Inserter / Buffer Shifter
Inserts the current first-filespec character into the second filespec destination buffer at (DE), shifting the remainder of the destination buffer forward by one byte to make room, until an end-of-string marker (03H or 0DH) is reached. Then advances DE past the inserted character and loops back to 52FBH to continue copying.
531BH - Error Exit with Extended-Context Suppression
Sets bit 6 in the error code (extended-context suppression) and jumps to the DOS error handler at 4409H.
5320H - FILE SPEC REQUIRED Error Display
Displays "FILE SPEC REQUIRED" + 0DH and exits via 4030H. Reached when no filespec was found in the command line or when a wildcard drive was specified.
5329H - "FILE SPEC REQUIRED" String Data
ASCII string data terminated by 0DH. The disassembler decodes these bytes as Z80 instructions; they are data.
533CH - RENAME IT TO WHAT? Error Display
Displays "RENAME IT TO WHAT?" + 0DH and exits. Reached when the second filespec's drive byte is less than 0EH, indicating no valid new name was provided.
5345H - "RENAME IT TO WHAT?" String Data
ASCII string data terminated by 0DH.
5358H - ILLEGAL DRIVE SPECIFICATION Error Display
Displays "ILLEGAL DRIVE SPECIFICATION" + 0DH and exits. Reached when the new filespec specifies a drive number different from the source file's drive.
5361H - "ILLEGAL DRIVE SPECIFICATION" String Data
ASCII string data terminated by 0DH.
537DH - Successful Exit
The rename was completed successfully. Jump directly to the DOS READY exit.
5386H - "DUPLICATE FILE NAME" String Data
ASCII string data terminated by 0DH. Displayed when the rename target name already exists in the directory.
ISAM 61H - DEVICE - Offset 2B60
VTOS 4.0 DEVICE Command Disassembly
This page documents the VTOS 4.0 SYS6 library overlay member for the DEVICE command (IDAM 61H, SYS6 member 27). The overlay loads at 5200H in the standard overlay slot and is dispatched through the RST 28H SVC 88H mechanism from SYS1.
The DEVICE command displays a status line for each of the eight logical device slots (drives 0-7) and then walks the device chain starting at 4015H to display information for any additional connected devices. For each active device (one whose drive parameter block at 4700H has bit 7 of IY+03H set, i.e., a JP instruction at IY+00H = 0C3H), DEVICE prints: the device number (0-7 in the drive scan), a sector size indicator (5 for 256-byte, 8 for 512-byte), a density indicator (based on bit 5 of IY+03H), an I/O direction display (using arrow symbols), the driver address in hex, and timing parameters for step, sides, and delay. For chained DCB devices starting at 4015H, DEVICE also displays routing information, driver address, and I/O direction arrows using the separate display subroutine at 53A0H.
The display output for each drive device line uses a combination of direct ROM character output (CALL 0033H), string display (CALL 4467H), and the in-place decimal-with-leading-zero subroutine pair at 53BFH/53D3H. The drive parameter block (IY-indexed, 10 bytes each at 4700H, IY = 4700H + drive * 10) is the primary data source for drive devices. The device chain walk uses the linked-list structure at 4015H (and 43C0H as a second table), following +01H/+02H next-pointers in type-10H forward-link entries.
The data area from 541EH to 547FH contains ASCII strings used as labels and format strings for the display. These bytes are decoded as Z80 instructions by the disassembler but are data. Key strings include the sector-size/density/option labels at 541EH, the arrow display strings, and the step/sides/delay labels at 5444H-547FH.
Drive Parameter Block Reference (IY-indexed, 10 bytes, base 4700H)
IY is set to 4700H + (drive number x 0AH) by CALL 478FH. The key fields used by DEVICE are listed below.
| IY Offset | Field Used by DEVICE |
|---|---|
| IY+00H | JP opcode byte - if 0C3H, this drive slot is active (has a configured driver). DEVICE checks this to skip unconfigured drives. |
| IY+03H bit 7 | Not used directly for the active test (IY+00H = C3H is the test). Bit 5 = double-density (1) vs. single-density (0) for the sector-size indicator. Bit 3 = double-sided. Bit 2 = input capable. Bit 1 = output capable. Bit 0 = combined input/output. Bit 6 = ??? |
| IY+04H bits 3-0 | Encoded sector-size / density nibble - ANDed with 0FH, then processed through two DAA steps (ADD A,90H / DAA / ADC A,40H / DAA) to produce an ASCII hex digit for display. |
| IY+06H | Number of cylinders minus 1 (used to compute the displayed step count by loading into L, clearing H, INCrementing HL, and doubling if double-sided). |
Variable and Data Areas
| Address Range | Purpose |
|---|---|
| 53F6H-53FFH (10 bytes) | Output line assembly buffer - used by the device chain display at 5303H-5365H. Starts with '*' (2AH) and the two driver address bytes, then the I/O arrow string and 0DH terminator. Built by the store-and-INC-DE sequence and displayed by CALL 4467H at 5362H. |
| 53EEH-53F0H (3 bytes) | 3-byte string "NIL" - copied by LDIR at 539CH when a device has no routing (bit 3 of IY+03H clear). Decoded as instructions by the disassembler; this is data: 4EH 49H 4CH = "NIL". |
| 53F1H-53F5H (5 bytes) | 5-byte string " <=>" - copied by LDIR at 537FH when a routed device has an active input/output link (bit 7 of the driver DCB at +00H is clear). Decoded as instructions; data: 20H 3CH 3DH 3EH 20H = " <=> ". |
| 541EH-542DH (data) | Sector size / density / option label strings - the bytes from 541EH are ASCII data used as format strings for the drive device display. Contains strings like " 122304 0 36101 5" (the sector size/density option table) decoded incorrectly as instructions by the disassembler. |
| 542EH (1 byte) | Single character 22H (ASCII ") - the "FLOPPY #" line separator character used when bit 3 of IY+03H is clear (single-sided / non-double-density). Pointed to by LD HL,542EH at 5234H. |
| 5439H-5443H (data) | String " RIGID #" + 03H - displayed instead of " FLOPPY #" when bit 3 of IY+03H is set (double-sided = hard disk / rigid). Pointed to by LD HL,5439H at 5239H. |
| 5444H-544AH (data) | String ", CYL=" + 03H - displayed after the drive number by CALL 4467H at 5263H. |
| 544BH-5450H (data) | Label string used as the base for the 53BFH decimal-with-leading-zero display of the cylinder count. Contains " " + number format characters. |
| 5451H-545AH (data) | String "REMOVABLE" + 03H - displayed when bit 2 of IY+03H is set. Pointed to by LD HL,5451H at 5270H. |
| 545BH-5460H (data) | String "FIXED" + 03H - displayed when bit 2 of IY+03H is clear. Pointed to by LD HL,545BH at 5275H. |
| 5461H-546CH (data) | String "DEN, SIDES=" + 03H (preceded by 'S' or 'D' for Single/Double density prepended at 5287H). |
| 546DH-5474H (data) | String ", STEP=" + 03H - displayed after the sides count. Pointed to by LD HL,546DH at 529DH. |
| 5475H-5478H (data) | String " MS" + 03H - the milliseconds suffix displayed after the step count. Pointed to by LD HL,5475H at 52BEH. |
| 5479H-547FH (data) | String ", DLY=" + 03H - displayed for delay value. Pointed to by LD HL,5479H at 52CAH. |
Major Routines
| Address | Name and Purpose |
|---|---|
| 5200H | DEVICE Main Entry - Drive Scan Loop Entry: no parameters. C = drive counter (0-7). For each drive, calls 478FH to load IY from the drive parameter block table. Tests IY+00H against 0C3H to determine if the drive is active. If active, displays ":n " (colon + drive digit + two spaces), then the sector size indicator (5 or 8 based on bit 5 of IY+03H), then the density/option string via a table lookup using IY+04H low nibble encoded as an ASCII hex digit. Loops through all 8 drive slots. |
| 52F4H | Drive Scan Loop Continue Restores BC, increments C (drive counter), and loops back to 5202H if C < 08H. When all 8 drives have been processed, falls through to the device chain walk starting at 52FCH. |
| 52FCH | Device Chain Walk Entry Loads the first device chain entry from 4015H. Tests for zero (empty chain). For non-zero chain entries, builds the output line in the buffer at 53F6H: '*' + driver address bytes + I/O arrow sequence + 0DH, then displays via CALL 4467H. |
| 5318H | Device Chain Entry Processor Checks bit 4 of the current chain entry byte. If set, the device has a next-link (type 10H entry): follows the next-pointer at +01H/+02H to the driver DCB, tests bit 7 of the DCB's first byte for active status, and displays the driver address and routing. If bit 4 clear, calls 53A0H to build the arrow display for this entry then advances the chain. |
| 535CH | Display Output Buffer and Chain Advance Writes 0DH terminator to the output buffer at 53F6H, calls CALL 4467H to display the assembled line, pops HL, advances by 08H bytes, checks for chain wrap (carry = past end of 4015H table), and either exits via 402DH or continues to the 43C0H table. |
| 5378H | Active Routed Device Display Copies " <=>" (5 bytes) from 53F1H into the output buffer via LDIR, then reads the driver address from the chain entry (+06H/+07H), calls CALL 44BBH to display it, and scans the output buffer for the 03H terminator to position DE past the displayed driver string. |
| 5396H | NIL Routing Display Copies "NIL" (3 bytes) from 53EEH into the output buffer via LDIR, then jumps to 535CH to close the line and advance. |
| 53A0H | I/O Arrow Builder Entry: HL = chain entry pointer, DE = output buffer pointer. Writes ' ' (space) to DE. Tests bit 0 of (HL) (input capable): if set, writes '<' (3CH); otherwise writes ' '. Writes '=' (3DH). Tests bit 1 of (HL) (output capable): if set, writes '>' (3EH); otherwise writes ' '. Writes ' '. Returns DE advanced by 5. |
| 53BFH | Decimal Display with Leading Zero Suppression Entry: HL = 16-bit value to display, DE = output buffer. Calls 53D3H twice - first with BC=0064H (100) then with BC=000AH (10) - to extract and display the hundreds and tens digits. Then displays the units digit directly via ADD A,30H. Uses a modified division-by-subtraction algorithm. |
| 53D3H | Division-by-Subtraction Digit Extractor Entry: HL = value, BC = divisor, DE = output buffer, A = leading character (space 20H). Subtracts BC from HL repeatedly, counting in D (from FFH upward). If quotient is zero and the leading character is space (leading zero), stores space; otherwise converts D to ASCII digit and stores. Returns HL = remainder, DE advanced. |
5200H - DEVICE Main Entry: Initialize Drive Counter
The DEVICE command initializes the drive counter C to zero and enters the main drive display loop. For each of the eight drive parameter blocks (drives 0-7), it calls 478FH to load IY, tests whether the drive is active, and if so displays a formatted status line.
This drive is active. Display the status line beginning with ': n ' where n is the drive digit.
5223H - Display Sector Size Indicator
Tests bit 5 of the drive flags byte at IY+03H to determine whether the drive uses 512-byte sectors (double density) or 256-byte sectors (single density). Displays '8' for 512-byte sectors or '5' for 256-byte sectors.
5230H - Display FLOPPY/RIGID Label and Cylinder Count
Tests bit 3 of IY+03H to select the "FLOPPY #" or "RIGID #" label string, then calls 4467H to display it. Next, reads IY+04H low nibble through a BCD encoding to produce an ASCII hex digit representing the drive's density/option code, and displays it. Then computes and displays the cylinder count.
5266H - Display REMOVABLE/FIXED and Density/Sides Labels
Tests bit 3 of IY+03H again to branch between the double-sided (rigid/hard disk) display path and the single-sided (floppy) path. For double-sided drives, tests bit 2 to select "REMOVABLE" or "FIXED", then displays 'S'/'D' + "DEN, SIDES=" and the sides count. For single-sided (non-rigid) drives, tests bit 6 and displays 'S'/'D' directly.
52A3H - Compute and Display Step Rate
Extracts the step-rate encoding from IY+03H, applies a bit-rotation to reconstruct the WD1771 step rate index, converts it to an index into the step-rate label table, and displays the result.
52C4H - Test Double-Density and Display Delay Parameter
Tests bit 5 of IY+03H (double density) to determine whether a delay parameter applies. If single density (bit 5 clear), displays the delay value as either "1" (space + '1') for non-double-density or "." + '5' for double-density, followed by the ", DLY=" label and the delay value string.
52F4H - Drive Loop Advance and Chain Entry
Restores BC from the stack, increments the drive counter C, and loops back to 5202H for the next drive if C < 8. After all 8 drives are processed, falls through to the device chain display starting at 52FCH.
All 8 drive slots have been displayed. Now walk the device chain starting at 4015H to display any additional connected DCB devices.
52FCH - Device Chain Walk: First Table Entry
Loads the first entry from the device chain at 4015H. If the entry is zero (no DCB devices configured), skips to the exit. For non-zero entries, builds the output line in the buffer at 53F6H using the driver address bytes, then displays it.
Type 10H Forward-Link Entry
Bit 4 of (HL) is set: this entry is a forward-link. The next-pointer at +01H/+02H leads to the actual DCB. Follow the link and check the DCB's bit 7.
The forward-link DCB exists but bit 7 is clear (device is not currently routed/active). Build the I/O arrow sequence from this entry's own flag byte and store the driver address in the output buffer.
533FH - Type 08H (Terminal) Entry: Build Arrow and Check Routing
For a terminal-type (08H) chain entry: calls 53A0H to build the I/O arrows, then tests bit 3 to determine if routing is active. If not routed (bit 3 clear), copies "NIL" to the output buffer. If routed, calls 44BBH to display the routing target address.
5366H - Chain Advance and Table Switch
Advances HL by 8 bytes to the next chain entry. If carry occurs (HL advanced past 402DH, meaning the walk has passed the end of the first table), exits via 402DH. If HL reaches 2DH (the end sentinel of the 4015H table), exits. Otherwise, tests whether HL has moved to 2DH to switch to the second table at 43C0H, and loops back to 52FFH.
5378H - Active Routed Device: Copy " <=>" and Display Driver
Called when the forward-link DCB has bit 7 set (active/routed device). Copies the " <=>" routing indicator string from 53F1H into the output buffer via LDIR, reads the driver address from chain entry +06H/+07H, calls 44BBH to display it, then scans for the 03H terminator to position DE past the displayed string.
Scan DE forward past the displayed content to find the 03H terminator, then position DE at the correct end of the buffer for the 0DH appended at 535CH.
5396H - NIL Routing Display
Copies "NIL" (3 bytes) from 53EEH into the output buffer via LDIR, then jumps to 535CH to close the line and display it. Used when a device has no routing target.
53A0H - I/O Arrow Builder
Builds the 5-character I/O direction display into the output buffer at DE. Tests bits 0 and 1 of the chain entry byte at (HL) to determine input and output capability. Writes: space, '<' or space (input bit), '=', '>' or space (output bit), space.
53BFH - Decimal Display with Leading Zero Suppression
Converts a 16-bit value in HL to a decimal ASCII string in the output buffer at DE. Calls the division-by-subtraction digit extractor at 53D3H twice (for hundreds and tens digits), then outputs the units digit directly. Suppresses leading zeros using a space character.
53D3H - Division-by-Subtraction Digit Extractor
Extracts one decimal digit from HL by repeatedly subtracting BC (the divisor) and counting subtractions in D. Applies leading-zero suppression using A as the pending leading character. Writes either a space (suppressed leading zero) or the ASCII digit to (DE).
53EEH - "NIL" String Data
3-byte ASCII string data. The disassembler decodes these as Z80 instructions; they are data: 4EH 49H 4CH = "NIL". Used by the LDIR at 539CH.
53F1H - " <=> " String Data
5-byte ASCII string data: space + '<' + '=' + '>' + space (20H 3CH 3DH 3EH 20H). The disassembler decodes the first two bytes as JR NZ,542FH and the remaining as instructions; they are data. Copied by LDIR at 537FH for active routed devices.
541EH - Step Rate / Option String Data Table
Binary data area decoded as instructions by the disassembler. Contains the sector size / density option label table and step-rate digit pairs used by the drive display loop. The 8 two-byte entries at 541EH-542DH provide the step-rate digits for the 8 possible encoded combinations; each pair is two ASCII characters.
542EH - FLOPPY/RIGID Label String Data Area
ASCII string data for the drive type labels and format strings displayed by the drive loop. The disassembler decodes these bytes as instructions; they are data.
ISAM 62H - LINK Command - Offset 2DC7
VTOS 4.0 SYS6 LINK Command Disassembly (Model I)
LINK is an I/O redirection command that establishes a one-way connection between two logical I/O devices, causing all output sent to the first device to also be routed to the second device, and allowing input requested from the first device to be satisfied from either device.
The command syntax is LINK <devspec> [prep] <devspec>, where both device specifications must begin with '*' (the VTOS device prefix). Two device specification arguments are parsed from the command line using the SYS0 filespec extractor at 441CH. Each is validated by the device chain scanner at 5288H, which walks the drive configuration tables at 4015H-402CH and 43C0H-43D7H to locate the matching Driver Control Block entry.
Once both DCBs are located, LINK reads the 2-byte "next device" pointer from the first DCB entry (stored at offset +01H/+02H in the entry), saves it as the new link pointer for the first device, and installs the address of the second device's DCB as the forward-routing destination. The actual linkage is performed by patching two addresses embedded in a relay code stub (at 526AH-527CH) that is copied into the High Memory area (just below the current High Memory top at 4049H). The resident relay stub intercepts I/O for the first device and duplicates it to the second.
If either device specification is invalid or not found in the configuration tables, appropriate error messages are displayed: "DEVICE SPEC REQUIRED" if an argument does not begin with '*', or "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" if the command is invoked while a foreground task is active. The command validates that the first argument begins with '*' before attempting a table lookup.
Memory Locations Referenced
| Address | Purpose |
|---|---|
| 402DH | DOS READY SVC stub. Byte at this address is tested (C3H = JP = unmodified) at 5208H to confirm that no foreground task has patched the vector. Also the No-Error Exit target jumped to at end of successful LINK. |
| 4015H-402CH | Drive configuration table #1 (3 entries x 8 bytes). Scanned by the device chain scanner at 5288H for both source and destination device specifications. |
| 4049H | High Memory pointer (2 bytes). Read at 524EH to determine where to install the relay code stub. The stub is placed immediately below the current High Memory top; 4049H is then decremented by 1EH (30 bytes, the stub size) to reserve that space. |
| 43C0H-43D7H | Drive configuration table #2 (3 entries x 8 bytes). Scanned as the overflow table when table #1 does not contain a matching entry. |
| 526AH-527CH | Relay code stub template (19 bytes). This is the actual Z80 relay code that is copied into high memory to perform the device linkage. It contains two self-modifying address operands (at 526FH and 527EH for the second device, and at 5275H and 5283H for the first device's "next pointer") that are patched before copying. |
| 5288H | Device chain scanner subroutine. Scans the drive configuration tables for a DCB entry whose device-type field (+06H/+07H) matches DE. Returns Z with HL pointing to the matching entry on success; NZ on failure. |
| 5304H | Filespec work area template #1 (for the first device argument). Used as the buffer argument to 441CH at 520BH. |
| 5324H | Filespec work area template #2 (for the second device argument). Used as the buffer argument to 441CH at 521AH. |
Key Subroutines
| Address | Purpose |
|---|---|
| 441CH | Extract filespec / device spec token from the command line. Returns Z with DE pointing to the parsed token on success. |
| 4467H | Display message string pointed to by HL on the screen. |
| 447BH | Display string with CONFIG/SYS processing. Used for error messages. |
| 001BH | ROM: Output a byte to a device. Called by the relay stub at 5278H with A = output byte and DE = DCB address of the destination device. |
| 0013H | ROM: Get a byte from an input device. Called by the relay stub at 527DH with DE = DCB address of the source device when an input request falls through. |
| 402DH | DOS READY / No-Error Exit. Jumped to at end of successful command. |
| 4030H | Error-already-displayed exit. Jumped to after displaying an error message. |
| 4409H | DOS Error Exit. Jumped to for fatal errors via OR A,40H / JP 4409H. |
5200H - LINK Entry Point: Validate Command Context
LINK command entry point. The first action is to verify that the command is being executed at the VTOS command level (not from within a running program). The byte at 402DH is tested: if it equals C3H (JP, the unmodified DOS-ready vector), execution proceeds normally. If 402DH has been patched by a foreground program (indicating the system is inside a running application), the "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" error is displayed.
Note on logic: The comparison CP C3H sets Z when A=C3H (system at command level). The JP Z then jumps away to the error. This is intentional: LINK requires the system to be running a program (a foreground context) in order to install a meaningful device link. At bare command level with no program loaded, LINK cannot operate usefully. When a program IS active (402DH != C3H, NZ), execution falls through to parse arguments.
5208H - Parse First Device Specification
Parses the first device specification argument from the command line. The filespec extractor at 441CH is called with the first work area template at 5304H. If no argument is present, the error path is taken. If the argument's first character is not '*', the error message "DEVICE SPEC REQUIRED" is displayed.
5217H - Parse Second Device Specification
Parses the second device specification from the command line. A second call to 441CH is made using a different work area template (5324H). A noise word (e.g., "TO") between the two device specs is automatically skipped by 441CH. The second argument is also validated for the '*' prefix.
5226H - Locate First Device in Configuration Tables
Both device specifications have been parsed and validated. Now the device chain scanner at 5288H is called to locate the first device's DCB entry. The 2-byte device-type identifier (the two ASCII characters after '*') is loaded from offset +01H/+02H of the first work area (5305H). On success, HL points to the matching DCB entry and the address is saved for later use.
5230H - Save First Device Address and Locate Second Device
The first device has been found (HL = its DCB entry address). HL is saved onto the stack while the second device is located. Then the second device's address is installed into the relay stub's output operand, and the first device's address is installed into the relay stub's input fallback operand.
Store Register Pair HL (the DCB address of the second device) into address 5276H. Address 5276H is the operand field of the LD DE,xxxxH instruction at 5275H within the relay stub template. At runtime, when the relay stub executes, this instruction loads DE with the second device's DCB address before calling ROM routine 001BH to send output to that device.
Store Register Pair HL (the DCB address of the second device) into address 5283H. Address 5283H is the operand field of the LD DE,xxxxH instruction at 5282H within the relay stub template. When the relay stub executes its input fallback path (at 527DH-5285H), this instruction loads DE with the second device's DCB address before calling ROM routine 0013H to request input from that device.
Store Register Pair HL (the existing forward-link address from the first device's DCB entry) into address 526FH. Address 526FH is the operand field of the CALL xxxxH instruction at 526EH within the relay stub. This operand is the address of the original device handler that was previously linked; by installing it here, the relay stub will call the original handler after duplicating I/O to the linked device, preserving the existing I/O chain.
Store Register Pair HL (the same existing forward-link address) into address 527EH. Address 527EH is the operand field of the JP xxxxH instruction at 527DH within the relay stub. On the input path of the stub (when the relay stub is invoked for input rather than output), if the primary device call fails or returns no data, this jump will redirect to the original handler, ensuring that input requests also propagate correctly through the existing chain.
524EH - Install Relay Stub in High Memory
With all four operands patched into the relay stub template, the 30-byte stub is copied into the memory area immediately below the current High Memory top (4049H). The High Memory pointer is decremented by 1EH (30 bytes) to reserve this space, and then the stub is copied there from the template at 526AH.
526AH - Data/Code: Relay Stub Template (30 Bytes)
This 30-byte block is both the relay stub template (copied into high memory during LINK execution) and valid Z80 code (the actual relay logic that will execute in high RAM after being copied). Four operand fields within this template are patched by the code at 523BH-524BH before the LDIR copy at 5260H installs the stub in high RAM. The stub handles two cases: output (when called for device output, it duplicates the byte to the linked device and then calls the original handler) and input (when called for device input, it routes to the original handler or the linked device as appropriate).
GOSUB to address 0000H (initial placeholder; at runtime this operand is patched by 5248H with the existing forward-link address from the first device's DCB entry). This call invokes the original device handler that was previously installed for the first device - the handler that was in place before LINK was executed. All output will be processed by this original handler first, then duplicated to the second device.
Load Register Pair DE with 0000H (placeholder; at runtime this operand at 5276H is patched by 523BH with the DCB address of the second (linked) device). DE now holds the DCB address of the linked device, which will be passed to ROM 001BH at 5278H to duplicate the output byte.
The following code (527DH-5285H) is the input path of the relay stub. It is entered when the CARRY FLAG is clear on entry to the stub (the JR C at 526AH did not branch), indicating that the caller is requesting input from the device rather than sending output.
GOSUB to address 0000H (placeholder; at runtime this operand at 527EH is patched by 523EH with the DCB address of the second linked device). This calls the second device's input handler to request input from the linked device. The return value in A is the byte received (if any).
Load Register Pair DE with 0000H (placeholder; at runtime this operand at 5283H is patched by 523EH with the DCB address of the second linked device). This reloads DE with the second device's DCB address for the fallback JP at 5285H.
5288H - Subroutine: Device Configuration Chain Scanner
Scans drive configuration tables #1 (4015H-402CH) and #2 (43C0H-43D7H), looking for an entry whose device-type field (2-byte ASCII identifier at offset +06H/+07H in each entry) matches the identifier in Register Pair DE. Returns Z with HL pointing to the matching entry base address on success. Returns NZ (A=08H) on failure after exhausting both tables.
52AEH - Fatal Error Exit
Reached when a device was not found in the configuration tables. Sets the fatal error flag (bit 6 of A) and exits through the DOS Error Exit at 4409H.
52B3H - Error: Device Spec Required
Reached when either device specification argument is missing or does not begin with '*'. Displays the error message "DEVICE SPEC REQUIRED" and returns to the error-already-displayed exit at 4030H.
52BCH - Data: Error Strings
Error message strings displayed by the LINK command. The first string "DEVICE SPEC REQUIRED" is displayed when a device argument is missing or invalid. The second string "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" is displayed when LINK is invoked from a context where a foreground program has not modified 402DH (i.e., the system is at command level, and LINK cannot operate).
5304H - Data: Filespec Work Area Templates
Two separate filespec work area templates, one for each device specification argument. Template #1 at 5304H receives the first parsed device token from 441CH at 520BH; template #2 at 5324H receives the second token from 441CH at 521AH. After parsing, the 2-byte device-type identifiers are located at 5305H and 5325H respectively, and are loaded into DE at 5226H and 5231H for the device chain scanner calls.
ISAM 63H - RESET Command - Offset 2ED6
VTOS 4.0 SYS6 RESET Command Disassembly (Model I)
The RESET command is member 2EH of the SYS6 partitioned data set, dispatched via IDAM code 63H. It loads at address 5200H in the VTOS overlay slot (4E00H-51FFH range, but this member's code starts at 5200H as presented by the disassembler). RESET is a device management command that restores logical I/O devices to their default system state.
When invoked with a device specification argument (e.g., RESET *DO), RESET locates the named device's Driver Control Block (DCB) in the drive configuration tables, resets it to its default driver, and stores 08H in its type byte to mark it as a standard system device. When invoked without arguments, RESET performs a global reset: it scans all drive configuration entries, restores the default DCB table from a static template embedded in the overlay, zeroes the extended drive state area, and redisplays the VTOS READY prompt.
The "RESET ALL" path also probes physical RAM to determine the actual top of memory, updating the High Memory pointer at 4049H accordingly - unless a foreground task is resident in high memory (detected by checking whether the JP instruction at 402DH has been patched), in which case the High Memory pointer is left unchanged.
The command validates the given device specification using the same drive-chain scanner (at 535CH) used by other SYS6 members. If an invalid device is given, the error message "DEVICE SPEC REQUIRED" is displayed. If the word "ALL" (or no argument at all) is given, the global reset path at 5273H executes.
Memory Locations Referenced
| Address | Purpose |
|---|---|
| 402DH | DOS READY SVC stub / No-Error Exit. Also tested (byte at 402DH compared to C3H) to detect whether a user program has patched the DOS-ready vector, indicating an active foreground task. |
| 4015H-402CH | Drive configuration table #1 (3 entries x 8 bytes). The RESET ALL path copies the default DCB template from 532CH over this table. |
| 4020H | Cursor position variable. Saved before overwriting the config table, restored afterward so the screen state is preserved. |
| 4049H | High Memory pointer (2 bytes). Updated by the RESET ALL path to reflect the detected top of physical RAM. |
| 43B8H-43BCH | Three 2-byte self-modifying target addresses (43B8H, 43BAH, 43BCH). At entry, these are copied into three LDIR-target operands embedded within the overlay code at 532DH, 5335H, and 533DH, patching those addresses in before use. |
| 43BEH | Saved interrupt vector / chaining context. Cleared to 0000H during RESET ALL to discard any saved overlay context. |
| 43C0H-43D7H | Drive configuration table #2 (3 entries x 8 bytes). The RESET ALL path copies the default DCB template from 5344H over this table. |
| 43D8H-43FFH | Extended drive state area (40 bytes). Zeroed during RESET ALL by a fill loop at 52DCH. |
| 4500H | Start of drive parameter block area. Scanned during RESET ALL to verify that no active task occupies high memory before updating 4049H. |
| 5309H | A 3-byte inline data block used as the message argument to CALL 4467H. Contains a control byte (1CH = set cursor position) followed by two position bytes. Written at 52AAH to override the message byte to 03H (string terminator) when the "CAN'T RESET MEMORY" error is displayed, suppressing the normal READY prompt redisplay. |
| 532CH-5343H | Default DCB template for drive configuration table #1 (24 bytes). Copied to 4015H during RESET ALL. |
| 5344H-535BH | Default DCB template for drive configuration table #2 (24 bytes). Copied to 43C0H during RESET ALL. |
| 535CH | Device chain scanner subroutine. Scans the drive configuration tables for a DCB whose device-type field (+06H/+07H) matches the DE register pair. Returns Z with HL pointing to the DCB entry on success, NZ on failure. |
| 53D8H | Error message string "CAN'T RESET MEMORY" + 0DH. Displayed when the High Memory update cannot be performed because a foreground task is active. |
| 5390H | Error message string "DEVICE SPEC REQUIRED" + 0DH. Displayed when an invalid or unrecognized device specification is given. |
| 53AEH | Error message string "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" + 0DH. Displayed when a command is invoked from a context where it is not permitted (shared with the LINK error path). |
Key Subroutines
| Address | Purpose |
|---|---|
| 441CH | Extract filespec / device spec from the command line. Returns Z and DE pointing to the parsed token on success. |
| 4467H | Display message string pointed to by HL on the screen. |
| 447BH | Display string with CONFIG/SYS processing. Used for error messages. |
| 402DH | DOS READY / No-Error Exit. Jumped to at end of successful command. |
| 4030H | Error-already-displayed exit. Jumped to after displaying an error message. |
| 4409H | DOS Error Exit. Jumped to when a non-displayed error code needs to be reported (OR A,40H first to set the fatal flag). |
5200H - RESET Entry Point: Patch Self-Modifying Addresses
RESET command entry point. Before any argument parsing, three self-modifying target addresses are read from the live drive configuration area and written into LDIR-destination operands embedded in the overlay code. This patches the overlay at runtime with the actual addresses of the drive configuration tables, which are stored at fixed known locations in SYS0.
Store Register Pair HL (loaded from 43B8H) into the 2-byte operand field at 532DH. Address 532DH is the destination operand of the LDIR instruction at 52BBH (which copies the default DCB template into the live drive configuration table #1). At runtime, HL now contains the actual target address for that copy.
Store Register Pair HL (loaded from 43BAH) into the 2-byte operand field at 5335H. Address 5335H is the destination operand of the second copy LDIR in the RESET ALL path (at 52C1H, copying the template for drive configuration table #2). At runtime this operand holds the actual destination address.
Store Register Pair HL (loaded from 43BCH) into the 2-byte operand field at 533DH. Address 533DH is the destination operand of the third copy LDIR in the RESET ALL path. With all three destination addresses now patched in, the RESET ALL LDIR sequence is ready to execute.
5214H - Parse Device Specification Argument
Calls the SYS0 filespec extractor to parse the first argument on the command line. If no argument is present (Z flag clear on return from 441CH), control transfers to the RESET ALL path. If an argument is found but its first character is not '*' (ASCII 2AH, the VTOS device specification prefix), the error path is taken.
522DH - Reset Single Device DCB
A valid matching DCB has been found (HL points to the entry, Z flag set). This section resets that single device by calling the subroutine at 52EAH, which walks the DCB's forward-link chain to locate the real (terminal) DCB entry and resets it. Then the VTOS READY prompt is redisplayed - unless the device is in the high-memory area (page 40H), in which case a further check is made before returning.
The following code (5239H-5259H) executes only when the device being reset is located in drive configuration table #1 (4015H-402CH). In this case, the single-device reset has modified a live entry in the resident DOS area. The code that follows performs an additional action: it copies the updated entry back into the correct slot within the table at 4015H by computing the slot offset from the entry address.
525CH - RESET ALL: Scan for Active Foreground Task
The RESET ALL path begins here. With no device argument given, RESET performs a global system reset. This section searches the drive parameter block area at 4500H for any active foreground task that would prevent the High Memory pointer from being updated. It does this by reading pointer pairs from the parameter block table and comparing them against the current value at 4049H (High Memory).
5273H - RESET ALL: Detect Top of Physical RAM
All drive configuration table entries have been reset. This section probes physical RAM to find the actual top of addressable memory. It walks the drive parameter block area at 4500H, reading each 2-byte pointer pair, and compares each against the current High Memory value at 4049H to find the highest address not yet claimed by any active task. The innermost loop reads successive 3-byte blocks from 4500H upward.
Loop Start
The following loop reads 3-byte blocks from the drive parameter block area (4500H+), extracting the 2-byte address pointer from each block and comparing it against the current High Memory value at 4049H. If any pointer is greater than or equal to High Memory, that block describes an active foreground task at high address, and the loop sets the carry flag to signal that High Memory cannot be updated.
Loop End
All 12 parameter block entries have been scanned without finding an active high-memory task. Execution continues to probe actual physical RAM.
5288H - RESET ALL: Probe Physical RAM Top
No active foreground task was found. This section tests whether the DOS-ready vector at 402DH has been patched (indicating a program is using it as a hook), and if not, probes physical memory from 0FFFFH downward in 4KB steps to find the actual top of RAM. The result is stored at 4049H.
Note: The logic here is inverted from what the preceding comparison implies: Z set means the byte IS C3H (unmodified), yet the jump is taken to the error path. This is the intended behavior: if 402DH = C3H, the DOS is at VTOS READY level and the full RESET ALL can proceed - but on further examination the JP Z at 528DH jumps to the error display only if C3H is NOT present. Re-reading: CP C3H sets Z if A=C3H; JP Z,53A5H jumps if Z is set, i.e., when 402DH = C3H. The message at 53A5H is "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL", but the RAM probe at 5290H is reached only when 402DH != C3H (a foreground program is present). This means the RAM physical probe only runs when a program has modified 402DH. This is the actual design: the physical RAM probe is a fallback when the system state is non-standard.
Loop Start
The following loop probes physical memory by reading, complementing, writing, reading back, and restoring each test address. If the read-back matches the written value, writable RAM is present at that address.
Loop End
The loop exits either by jumping to 52B1H (RAM found) or continuing downward until a valid RAM page is located.
52A2H - RESET ALL: High Memory Cannot Be Updated
Reached when an active foreground task is resident in high memory (carry set by the comparison at 5281H). The "CAN'T RESET MEMORY" error string is displayed, a marker byte is written to suppress the READY prompt, interrupts are disabled, and execution falls through to the common RESET ALL completion path at 52AEH.
Store Register A (03H, the string terminator) into address 5309H, which is the first byte of the READY prompt message control block used at 52E1H-52E4H. Writing 03H here causes the CALL 4467H at 52E4H to display an empty message (immediate terminator), suppressing the cursor repositioning that normally follows a successful RESET ALL.
52AEH - RESET ALL: Store New High Memory and Restore Tables
Common completion path for RESET ALL (entered from both the "no task found" path at 52B1H and the "task found" path from 52ADH). The detected RAM top is stored at 4049H, the default drive configuration tables are copied from the embedded templates, the extended drive state area is zeroed, and the VTOS READY prompt is redisplayed.
Loop Start
The following loop zeroes the 40-byte extended drive state area at 43D8H-43FFH by storing 00H into each byte, advancing L through the range.
Loop End
The extended drive state area at 43D8H-43FFH has been completely zeroed.
52EAH - Subroutine: Reset Single DCB Entry
This subroutine is called with HL pointing to a drive configuration table entry. If the entry has bit 4 of its type byte set (a forwarding/non-terminal entry), the routine follows the forward-link chain to locate the actual (terminal) driver block. Once the terminal block is found, it copies 32 bytes of default driver data from 530CH into it, effectively resetting the device to its system default driver. Returns Z on success.
The following three instructions execute only when bit 4 of the type byte is set, indicating a forwarding entry. The forward-link pointer (a 2-byte address stored at offset +01H/+02H within the entry, i.e., the bytes at HL+1 and HL+2) is loaded into HL to follow the chain to the terminal entry.
5309H - Data: READY Prompt Message Control Block
A 3-byte inline data record used as the message argument to CALL 4467H at 52E4H and 5256H. Contains a display control sequence to reposition the screen cursor. The first byte (1CH) is the cursor-position control byte; the following two bytes give the cursor row and column. This block is overwritten at 52AAH on the "CAN'T RESET MEMORY" error path, replacing the first byte with 03H (string terminator) to suppress the display.
530CH - Data: Default Driver Data Block (32 Bytes)
A 32-byte block of factory-default driver data, used as the LDIR source by the single-device reset subroutine at 52F8H-52FFH. When a device DCB is reset, these 32 bytes are copied into the terminal DCB entry, restoring it to the system-default driver state.
532CH - Data: Default DCB Template for Table #1 (24 Bytes)
A 24-byte default template for drive configuration table #1 (4015H-402CH). Copied to the live table during RESET ALL at 52C1H. Contains the factory-default 8-byte entries for the three DCB slots in table #1.
Entry 1 (532CH-5333H): Type=01H, link=0000H, reserved x3, ID bytes 4BH 49H ("KI").
Entry 2 (5334H-533BH): Type=07H, link=0000H, reserved x3, ID bytes 53H 49H ("SI").
Entry 3 (533CH-5343H): Type=10H (forward-link), link target=401DH, reserved x3, ID bytes 4CH 4FH ("LO").
At runtime, the self-modifying operands at 532DH, 5335H, and 533DH are patched with the actual destination addresses from 43B8H, 43BAH, and 43BCH before this template is used.
5344H - Data: Default DCB Template for Table #2 (24 Bytes)
A 24-byte default template for drive configuration table #2 (43C0H-43D7H). Copied to the live table during RESET ALL at 52D6H. Contains the factory-default 8-byte entries for the three DCB slots in table #2.
Entry 1 (5344H-534BH): Type=08H (terminal), link=0000H, reserved x3, ID bytes 4AH 4CH ("JL") - Drive 0 terminal entry.
Entry 2 (534CH-5353H): Type=10H (forward-link), link=4015H (table #1 entry 1), reserved x3, ID bytes 53H 49H ("SI") - Drive 1 forward link.
Entry 3 (5354H-535BH): Type=10H (forward-link), link=401DH (table #1 entry 2), reserved x3, ID bytes 4CH 4FH ("LO") - Drive 2 forward link.
535CH - Subroutine: Drive Configuration Chain Scanner
Scans the drive configuration tables at 4015H-402CH and 43C0H-43D7H, searching for a DCB entry whose device-type field (the 2-byte ASCII identifier at offset +06H/+07H within each entry) matches the value in Register Pair DE. Returns Z with HL pointing to the matching entry on success. Returns NZ (A=08H) on failure after scanning all entries.
5382H - Fatal Error Exit
Reached when a non-displayed fatal error has occurred (e.g., Close File failure, device not found in tables during a critical operation). Sets the fatal error flag (bit 6 of A) and exits through the DOS Error Exit at 4409H.
5387H - Error: Device Spec Required
Reached when the argument given to RESET does not begin with '*' (the device specification prefix). Displays the error message "DEVICE SPEC REQUIRED" and jumps to the error-already-displayed exit at 4030H.
5390H - Data: Error Strings
Error message strings displayed by the RESET command. The first string "DEVICE SPEC REQUIRED" is displayed when the argument does not begin with '*'. The second string "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" is displayed when RESET is invoked from a non-command-level context (402DH modified by a foreground program).
53ECH - Data: Filespec Work Area Template
The filespec work area template and related data used by 441CH at 5214H. The template at 5405H provides the buffer into which the parsed device specification is written. The data begins at 53ECH and extends to the end of the member.
ISAM 64H - ROUTE - Offset 30CA
VTOS 4.0 SYS6 ROUTE Command Disassembly (IDAM 64H, Model I)
The ROUTE command creates and reroutes I/O for a logical device. Its syntax is:
ROUTE <devspec> [(NIL)] [prep] <filespec/devspec>
When called, ROUTE can redirect the I/O stream of one logical device to one of three destinations: a bit-bucket (NIL, which discards all output), a disk file (which causes a Device Control Block and blocking buffer to be dynamically allocated in high memory), or a different logical device (which causes the DCB chain to be patched so that the source device forwards to the target).
This overlay is SYS6 IDAM 64H and loads at 5200H in the overlay slot. It is invoked from the SYS1 command dispatcher via the standard RST 28H SVC mechanism. On completion it jumps to 402DH (DOS READY / No-Error Exit) or 4030H (Error-already-displayed exit), or uses 4409H for error-with-code exits.
The overlay uses the two VTOS device configuration tables: the primary table at 4015H-402CH (three 8-byte entries) and the secondary table at 43C0H-43D7H (three 8-byte entries). Each entry's 2-byte device identifier lives at offset +06H/+07H within the entry. The device match routine at 53C9H scans both tables sequentially to locate the target device by its identifier, returning Z on a match.
DCB chain walking is performed by the subroutine at 530EH. It follows bit 4 of the control byte at each DCB to traverse forward links, then at the chain tail tests bit 7 (redirect flag). If a redirect exists and a new route block must be allocated, 32 bytes are carved from the top of the TPA by decrementing the pointer at 4049H.
All DCB patching is performed under DI/EI to prevent the ISR from observing a partially modified DCB.
Memory Locations Referenced
| Address Range | Purpose |
|---|---|
| 402DH (3 bytes) | DOS READY / No-Error Exit - jumped to on successful completion |
| 4030H (3 bytes) | Error-already-displayed exit - jumped to after printing an error message string |
| 4049H (2 bytes) | Detected RAM top address - decremented by 32 (0020H) when a new route block is allocated from high memory |
| 4015H-402CH (24 bytes) | Drive/device configuration table #1 - three 8-byte entries; each entry's 2-byte device identifier is at offset +06H/+07H |
| 43C0H-43D7H (24 bytes) | Drive/device configuration table #2 - three 8-byte entries in same format as table #1; scanned when table #1 produces no match |
| 5220H (2 bytes) | Self-modifying operand - holds the parsed second-argument device/file address (set at 5212H, read back at 5235H and 52D6H) |
| 5389H (varies) | First device specifier work area - populated by the filespec extractor at 441CH |
| 53A9H (varies) | Second device specifier work area - populated by the second call to 441CH; also used as LDIR source when copying a 32-byte route entry |
| 5305H (varies) | Parameter string "NIL " - compared against the first parsed argument to detect NIL routing mode |
Major Routines
| Address | Name and Description |
|---|---|
| 5200H | ROUTE Entry Point Parses the first device specifier from the command line, validates the wildcard, then parses the second argument and dispatches to one of three routing modes: device-to-device, device-to-NIL, or device-to-file. |
| 530EH | DCB Chain Walker Follows the forward-link chain of Device Control Blocks (bit 4 of control byte = chain active). At the chain tail checks bit 7 (redirect flag); if set, allocates a new 32-byte block from high memory, copies the current DCB entry into it via LDIR, and registers it via 4428H. |
| 53C9H | Device Table Scanner Scans the device configuration tables at 4015H and 43C0H looking for a 2-byte device identifier that matches DE. Returns Z on match with HL pointing to the matched entry, or NZ with A=08H when no match is found. |
5200H - ROUTE Entry Point: Parse First Device Specifier
Execution begins here when the SYS1 dispatcher loads SYS6 IDAM 64H. The command line cursor is positioned past the "ROUTE" keyword. This block calls the SYS0 filespec extractor to pull the first device specifier from the command line, then validates that it is a wildcard (*).
Parse Second Argument and Open File
Having validated the first specifier, the code now initialises the self-modifying storage at 5220H to zero, then calls the SYS0 file-open utility at 4476H to parse and open the second argument (the routing destination). 4476H returns BC as a 16-bit value representing the opened device or file handle.
Store Register Pair DE (currently 0000H) to the two bytes at 5220H-5221H. These bytes are the operand of the LD BC,nnnn instruction at 521FH; by writing to them here the code initialises the slot to zero before the open call overwrites it with the real address.
Load Register Pair BC with the value currently stored in the two bytes at 5220H-5221H. At first entry those bytes were initialised to 0000H at 5212H, but 4476H may have overwritten 5220H-5221H with the opened device address. This instruction therefore reads back whatever 4476H stored, giving BC the second-argument result.
Dispatch: NIL vs Device-to-Device vs New Route
BC now holds the second-argument result from 4476H. If BC is zero the destination is absent or NIL; if BC is non-zero it is a device address. The code tests BC and dispatches accordingly.
BC is Zero - Second Argument is Wildcard or NIL
BC was zero, meaning 4476H did not return a device address. The code now checks whether the second argument begins with * (another wildcard), which means "route to another device class", versus being literally "NIL".
Second Argument is Wildcard - Device-to-Device Patch
Both arguments are wildcards. The code will look up the source device in the device tables, look up the destination device, and patch the source DCB to forward to the destination.
Source Device Not Found - Allocate New Route Block
The source device did not exist in either table. A new 32-byte route block must be carved from the top of the TPA.
Patch the DCB - Source Device Found or Slot Allocated
HL points to the DCB entry for the source device (or the newly allocated slot). The stack holds the destination entry pointer from the PUSH at 523FH. The code now walks the source DCB chain and patches it to forward to the destination.
Second Argument Not a Wildcard - Check for Existing Route Table
The second argument did not begin with *. The code now checks whether a routing table already exists (by testing whether address 402DH contains a JP opcode, C3H), then either appends to the existing table or carves space for a new one.
No Existing Route Table - Allocate New 32-Byte Block
No route table exists yet. The code reads the current TPA top pointer, carves 32 bytes from it, copies the second-argument specifier data into the new block, then patches the DCB.
Store Register Pair HL (the new TPA top, which is one byte below the start of the new route block) to 4049H. This permanently reduces available TPA by 32 bytes to reserve the route block.
Route Table Already Exists - Display Error
The byte at 402DH was C3H (JP), indicating a route table is already installed. ROUTE cannot be re-applied once routing is active at this level. The code displays the "CAN'T" message and returns to the command level.
52ACH - Embedded Data: "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" Message
This block is not executable code. It is an ASCII message string stored inline in the overlay. The bytes decode to the error text displayed when ROUTE is attempted after a route table has already been installed. The disassembler has decoded these bytes as Z80 instructions in error.
52D6H - Device-to-Existing-Device Routing (BC Non-Zero)
Reached from 5224H when the second argument returned a non-zero device address in BC. BC contains the target device's DCB address. This block looks up the source device in the configuration tables, walks its DCB chain, and patches the chain tail to forward to the destination DCB at BC.
Source Found - Walk Chain and Set NIL/Forward
The source device was found at HL. Walk the DCB chain to its tail, then patch the tail to type 08H (NIL/bit-bucket) and return.
Source Not Found - Allocate New NIL Route Entry
The source device had no existing entry. A new entry must be created with type 08H (NIL) and the source device identifier, then the destination address from BC is written into the new entry.
5305H - Embedded Data: "NIL" Parameter String
This block is not executable code. It is the ASCII string "NIL " (followed by spaces and a NUL terminator) stored inline. This area is pointed to by DE at 5216H when the second argument is being opened; 4476H uses this as the parameter block for the NIL routing mode. The disassembler has decoded these bytes as Z80 instructions in error.
530EH - DCB Chain Walker Subroutine
This subroutine walks the Device Control Block forward-link chain. It is called with HL pointing to a DCB entry. Bit 4 of the control byte at (HL) is the chain-active flag. If set, HL is advanced to the next entry in the chain and the test repeats. At the chain tail (bit 4 clear), bit 7 (redirect flag) is tested. If bit 7 is set, a new 32-byte block is allocated from high memory and the current DCB entry is copied into it via LDIR; the new block is then registered with SYS0 via 4428H.
Test bit 4 of the byte at (HL), which is the control byte at offset +00H of the current DCB entry. Bit 4 set means this entry is a forward-link (chain continues); bit 4 clear means this is the tail of the chain.
LOOP BACK to 530EH to test bit 4 of the next DCB entry's control byte. This continues walking the forward-link chain until the tail is found.
Tail Entry Reached - Test Redirect Flag
Bit 4 was clear, so HL points to the tail DCB entry. Now test bit 7 (redirect flag). If set, the tail entry itself must be copied into a new block before the route patch can be applied, to avoid destroying the original terminal entry.
Redirect Flag Set - Allocate New Block and Copy
Bit 7 was set. A new 32-byte block must be allocated from high memory to hold a copy of this tail entry, so the original table entry is preserved.
532DH - Embedded Data: 32-Byte DCB Work Buffer
This 32-byte area at 532DH-534CH is used by the chain walker at 530EH as the destination for the LDIR copy of a tail DCB entry when the redirect flag (bit 7) is set. It is not executable code; the disassembler has decoded these bytes as Z80 instructions in error.
534DH - Error Exit with Flag
Common error exit reached from multiple failure paths throughout the overlay. The error code (if any) is already in Register A. This block ORs bit 6 into A to set the VTOS error-display suppression flag, then exits via 4409H (DOS Error Exit).
5352H - Error: "DEVICE SPEC REQUIRED"
Reached when the first or second device specifier is missing from the command line, or does not begin with the required * wildcard. The error message string is embedded inline at 535BH.
535BH - Embedded Data: "DEVICE SPEC REQUIRED" Message
ASCII error string printed by 5355H. The disassembler has decoded these bytes as Z80 instructions in error.
5370H - Error: "PARAMETER ERROR"
Reached when the second argument fails to open correctly (NZ return from 4476H at 521CH). The "PARAMETER ERROR" message string is embedded inline at 5379H.
5379H - Embedded Data: "PARAMETER ERROR" Message
ASCII error string printed by 5373H. The disassembler has decoded these bytes as Z80 instructions in error.
5389H - Embedded Data: First Device Specifier Work Area
This area is the destination buffer for the first device specifier token extracted by 441CH at 5203H. The byte at 5389H is the wildcard *; the 2-byte device type identifier follows at 538AH. The area is large enough for a full device specifier token.
53A9H - Embedded Data: Second Device Specifier Work Area
This area is the destination buffer for the second device specifier token extracted by 441CH at 522AH. The byte at 53A9H is the wildcard *; the 2-byte device type identifier follows at 53AAH. This area is also used as the LDIR source at 529CH when copying a 32-byte route entry into a newly allocated high-memory block.
53C9H - Device Table Scanner Subroutine
This subroutine searches the VTOS device configuration tables for an entry whose 2-byte identifier at offset +06H/+07H matches the value in Register Pair DE. The primary table is at 4015H-402CH (three 8-byte entries). If no match is found there, scanning continues in the secondary table at 43C0H-43D7H. Returns Z flag set with HL pointing to the matched entry on success, or NZ with A=08H on failure.
Save Register Pair HL (the current table entry base address) onto the stack. HL will be advanced to the identifier field for comparison; the stack preserves the entry base for the next-entry calculation.
Match Found
Both identifier bytes matched. Discard the saved entry base from the stack and return with Z set and HL pointing to the matched entry.
No Match - Advance to Next Entry
The identifier did not match. Pop the saved entry base, add 8 to advance to the next 8-byte entry, and loop. When L reaches 2DH, the primary table is exhausted and scanning switches to the secondary table at 43C0H.
Primary Table Exhausted - Switch to Secondary Table
L reached 2DH, marking the end of the primary table at 4015H. Restart the scan from the secondary table at 43C0H.
ISAM 65H/66H - SET / FILTER - Offset 326C
5A00H - SET / FILTER - Dual-Entry Preamble and Command-Level Validation
Shared overlay implementing both the SET command (IDAM 65H, entry at 5A01H) and the FILTER command (IDAM 66H, entry at 5A00H). Both are PDS Member 32, loaded at 5A00H. SET establishes a new logical I/O device driver (default extension DVR). FILTER establishes a data filter in the I/O path (default extension FLT). The dual-entry mechanism uses overlapping opcodes: the two bytes F6 AFH at 5A00H decode as OR A,0AFH when entered at 5A00H (FILTER), but when entered one byte later at 5A01H, the CPU sees only AFH which decodes as XOR A,A (SET). This sets a command discrimination flag at 5A64H to non-zero (FILTER) or zero (SET), controlling all subsequent behavioral differences: default file extension selection, device table search behavior, and whether the driver address is patched into the device configuration entry.
Dual-Entry Point and Command Discrimination
FILTER (IDAM 66H) enters at 5A00H, executing OR A,0AFH which forces A to non-zero (bits 0, 1, 2, 3, 5, and 7 are always set). SET (IDAM 65H) enters at 5A01H, where the CPU sees only AFH = XOR A,A which clears A to zero. The result is stored as the command discrimination flag via self-modifying code.
OR Register A with the immediate value 0AFH (10101111 binary). Since 0AFH has bits 0, 1, 2, 3, 5, and 7 set, the result in Register A is guaranteed non-zero regardless of A's prior value. This identifies the command as FILTER.
SET Entry Point (IDAM 65H, at 5A01H)
When execution begins at 5A01H, the CPU sees only the second byte AFH, which decodes as XOR A,A. This clears Register A to zero and clears all flags. This identifies the command as SET.
Store Register A (non-zero for FILTER, zero for SET) into the operand byte of the LD A,00H instruction at 5A63H. Address 5A64H is the immediate-data byte of that LD A,nn instruction. After this write, 5A63H becomes LD A,00H (SET path, flag zero) or LD A,<non-zero> (FILTER path, flag non-zero). This flag is re-tested at 5A2CH and at 5A65H to control extension selection and driver-address patching.
5A0DH - Device and File Specification Parsing
Parses the command line to extract the device specification (e.g., *CL) and the file specification (e.g., RS232/DVR). Uses the SYS0 filespec extraction routine at 441CH for both. The device spec must begin with '*' to identify it as a device. The file spec must NOT begin with '*' (it must be a filename, not another device reference).
First Filespec Extraction: Device Specification
Extracts the device specification (e.g., *CL, *PR) from the command line into the buffer at 5B1AH. After extraction, verifies that the parsed spec begins with '*' (2AH), confirming it is a device reference rather than a filename.
Second Filespec Extraction: Driver/Filter Filename
Extracts the driver or filter file specification (e.g., RS232, PRINTER) from the command line into the buffer at 5B3AH. The file spec must be a filename, not a device reference, so '*' is rejected.
5A2BH - Extension Selection, Device Table Search, and Driver/Filter Installation
Selects the default file extension (DVR for SET, FLT for FILTER), searches the device configuration table for the specified device, loads the driver/filter program file, and (for SET only) patches the device address into the device table entry.
Default Extension Selection
Loads the command discrimination flag to select "DVR" (SET) or "FLT" (FILTER) as the default file extension, then calls 4473H to insert it into the filespec if no explicit extension was provided.
Device Table Search
Loads the 2-byte device address from the parsed device specification buffer and searches the drive configuration tables (4015H and 43C0H) for a matching entry. For SET, if no match is found, a second search looks for an unassigned (0000H) slot.
Device Not Found Handling
If the device was not found in the table, the behavior depends on the command. FILTER requires the device to already exist and reports an error. SET attempts a second search for an empty (unassigned) device slot to install a new device.
SET: Search for Empty Device Slot
SET allows installing a new device. If the specified device was not already in the table, search again for an unassigned slot (device address 0000H) where the new device can be placed.
Driver/Filter File Load and Address Installation
Loads the driver or filter program file into memory via the SYS0 program loader at 4430H. For SET, the device address from the devspec buffer is then patched into the device configuration table entry at offset +06H/+07H under interrupt protection. For FILTER, the address patching is skipped (the filter inserts itself into the I/O chain through its own initialization code).
Self-Modifying Code: Command Flag Re-Test
The LD A,00H instruction below has been modified by the self-modifying code at 5A02H. For SET, the operand byte at 5A64H remains 00H. For FILTER, it was overwritten with a non-zero value. This re-tests the command identity to decide whether to patch the device table entry.
Load Register A with the immediate byte at 5A64H. The initial (on-disk) value is 00H, but at runtime this operand was overwritten by the LD (5A64H),A at 5A02H. For SET: A = 00H (original value preserved). For FILTER: A = non-zero value (written during entry preamble).
SET: Device Address Installation (Critical Section)
For the SET command, the 2-byte device address from the devspec buffer (5B1BH-5B1CH) is written into the device configuration table entry at offset +06H/+07H. Interrupts are disabled during this write to prevent the ISR from seeing an inconsistent (half-written) address.
Common Exit: Cleanup and Return
Both SET and FILTER converge here to clean up the stack and return to the caller (the VTOS command dispatcher).
5A7CH - Default Extension Data Strings
Three-byte ASCII extension strings used as default file extensions when the user does not specify one explicitly. SET uses "DVR" (device driver), FILTER uses "FLT" (filter routine).
ASCII string "DVR" (44H='D', 56H='V', 52H='R'). Used as the default file extension for the SET command. Referenced by LD HL,5A7CH at 5A30H. If the user issues SET *CL TO RS232 without specifying an extension, the system will search for RS232/DVR.
ASCII string "FLT" (46H='F', 4CH='L', 54H='T'). Used as the default file extension for the FILTER command. Referenced by LD HL,5A7FH at 5A35H. If the user issues FILTER *PR USING MYFILTER without specifying an extension, the system will search for MYFILTER/FLT.
5A82H - Device Configuration Table Search Subroutine
Searches both VTOS drive configuration tables (table #1 at 4015H-402CH and table #2 at 43C0H-43D7H) for an entry whose 2-byte device address at offset +06H/+07H matches the value in Register Pair DE. Returns Z FLAG set and HL pointing to the matching entry if found, or NZ FLAG set with A = 08H if no match exists. Used to find the device entry for an existing device (matching the DCB address) or to find an empty slot (matching 0000H).
Loop Start
Scan begins at configuration table #1 (4015H). Each iteration checks one 8-byte entry by comparing DE against the 2-byte field at entry+06H/+07H. If no match is found in table #1 (entries at 4015H, 401DH, 4025H), the scan continues in table #2 at 43C0H.
Match Found
Both bytes of the device address match. Return with Z FLAG set and HL pointing to the matching entry's base address (recovered from the stack).
No Match: Advance to Next Entry
The current entry did not match. Discard the saved entry base, advance L to the next 8-byte entry boundary, and check whether we have reached the end of the current table.
Table #1 Exhausted: Switch to Table #2
All three entries in configuration table #1 (4015H-402CH) have been checked without a match. Switch to configuration table #2 at 43C0H-43D7H and continue the search.
Loop End
5AA8H - Error Exit with Context Suppression
Common error exit path for SET/FILTER. Sets bit 6 of the error code to suppress extended error context display, then jumps to the DOS Error Exit at 4409H.
5AADH - Error Message Handlers
Three error exit handlers, each loading a pointer to a specific error message string, displaying it via the SYS0 display routine at 447BH, and then jumping to the error-already-displayed exit at 4030H.
"FILE SPEC REQUIRED" Error Handler
Reached when the FILTER or SET command is missing the driver/filter filename argument, or when the second argument is a device spec ('*') instead of a filename.
5AB6H - Error Message Data: "FILE SPEC REQUIRED"
ASCII error message string terminated by 0DH (carriage return). Displayed when the driver/filter filename is missing from the command line.
5AC9H - "DEVICE SPEC REQUIRED" Error Handler
Reached when the SET or FILTER command is missing the device specification argument (e.g., *CL, *PR), or when the first argument is not prefixed with '*'.
5AD2H - Error Message Data: "DEVICE SPEC REQUIRED"
ASCII error message string terminated by 0DH. Displayed when the device specification is missing from the command line.
5AE7H - "CAN'T -- VALID ONLY AT VTOS COMMAND LEVEL" Error Handler
Reached when SET or FILTER is invoked from within a running program (402DH contains C3H, indicating a program return vector is installed) rather than from the interactive VTOS command prompt.
5AF0H - Error Message Data: "CAN'T -- VALID ONLY AT VTOS COMMAND LEVEL"
ASCII error message string terminated by 0DH. Displayed when SET or FILTER is invoked from within a program rather than from the interactive command prompt.
ISAM 71H - DUMP - Offset 3391
VTOS 4.0 SYS6 DUMP Command Disassembly (IDAM 71H, Model I)
The DUMP command saves a range of memory to a disk file in VTOS load-module (CIM, core image) format. Its syntax is:
DUMP <filespec> (START=s, END=e, TRA=t)
The parameters START (or S), END (or E), and TRA (or T) specify the starting address, ending address, and transfer (entry) address respectively. Addresses may be expressed in decimal or in hexadecimal preceded by X'. The memory area to be dumped must begin at or above address 6000H. The default file extension is CIM (Core Image). If the TRA parameter is omitted, the transfer address defaults to the system warm-start address at 402DH.
This overlay is SYS6 IDAM 71H and loads at 5200H in the overlay slot. The overlay begins with a short dispatch stub at 5200H (a JP to 5212H) followed by the character-output subroutine at 5203H (which calls ROM 001BH to write a byte to the open output file). The main logic starts at 5212H.
The output file is written as a sequence of standard VTOS load-module records. Each record begins with a type byte (01H = object/load block), followed by a length byte, a 2-byte load address, and then the data bytes. The data is output in pages: the loop at 52A2H-52C9H writes up to 254 data bytes per record (one full page, type 01H with length byte FEH which encodes as 254 payload bytes). After all data pages are written, a transfer record is emitted (type 02H, length 02H, 2-byte transfer address) to mark the end of the file.
Address validation rejects START values below 6000H. The END address is checked against both START (must be >= START) and 5FFFH (must be above the protected low-memory boundary). The overlap computation at 527DH-5291H uses SBC HL,BC to detect these conditions and selects the appropriate error message pointer.
Memory Locations Referenced
| Address Range | Purpose |
|---|---|
| 402DH (3 bytes) | DOS READY / No-Error Exit - jumped to on successful completion; also used as the default transfer address when TRA is not specified by the user |
| 4030H (3 bytes) | Error-already-displayed exit - jumped to after printing an error message string |
| 4409H (3 bytes) | DOS Error Exit (error code in A) - reached via the OR A,40H / JP 4409H stub at 52EAH |
| 5294H-5295H (2 bytes) | Self-modifying storage for the parsed START address - written by the hex digit parser loop at 5232H-5248H and read back at 527DH |
| 529AH-529BH (2 bytes) | Self-modifying storage for the parsed END address - read at 527AH to compute the byte range size |
| 538AH (6 bytes) | Hex address parse buffer - the six bytes at 538AH-538FH receive the hexadecimal digit characters (or spaces for unused digits) parsed from the START/END/TRA parameter values |
| 5390H (varies) | Filespec work area - populated by the filespec extractor at 441CH; also serves as the source pointer when scanning parsed hex digits into 538AH |
| 5400H (varies) | FCB (File Control Block) for the output CIM file - used by the file-open and write operations |
| 5356H (varies) | Default extension "CIM" - pointed to by HL at 5253H and passed to 4473H to install "CIM" as the default file extension when none was provided by the user |
Major Routines
| Address | Name and Description |
|---|---|
| 5200H | DUMP Entry Stub / Dispatch A JP instruction redirecting execution to the real entry point at 5212H. The three bytes between 5203H and 5211H are the character output subroutine. |
| 5203H | Character Output Subroutine Called with A = byte to write. Checks whether a file is ready (ROM 001BH), RST 30H on error, masks the high bit of A (RES 7,A), and exits via 4409H on device error. This is the routine that writes every byte of the output CIM file. |
| 5212H | DUMP Main Entry Point Extracts the filespec, parses the START/END/TRA hex parameters into the parse buffer at 538AH, opens the output file at 5400H with default extension CIM, then dispatches to the output loop. |
| 526EH | Output Loop: Write CIM Records to File Outputs the 6-byte filename header record (type 05H/06H), then the address bytes from 538AH, validates START vs END vs 6000H, and enters the page-output loop that writes 254-byte load blocks (type 01H) until all memory is written, then appends the transfer record (type 02H). |
| 5301H | Error: Display HL String and Exit Prints the string at HL via 447BH and jumps to 4030H. Used for the range-error paths. |
5200H - DUMP Entry Stub
The overlay begins at 5200H with a three-byte JP that transfers immediately to the real entry point at 5212H. The three bytes between 5203H and 5211H are not skipped code - they are the character output subroutine used throughout the overlay to write bytes to the output CIM file.
5203H - Character Output Subroutine
This subroutine writes a single byte (in Register A) to the currently open output file. It is called throughout the output loop via CALL 5203H. It uses ROM routine 001BH to perform device output. On device-not-ready it triggers RST 30H (the DEBUG/error hook). After masking bit 7 of A, it checks the return status and exits via 4409H if the device reports an error.
5212H - DUMP Main Entry Point: Parse Filespec and Parameters
Execution reaches here from the JP at 5200H. The command line cursor is positioned past the "DUMP" keyword. This block extracts the output filespec, parses the START/END/TRA hex parameter values from the command line into a 6-byte buffer at 538AH, then opens the output file and validates address ranges before entering the output loop.
Parse Hex Address Parameters (START/END/TRA)
The filespec is valid. Now parse the hex parameter string. Each parameter value is up to 6 hex digits (for a 16-bit address in 6-character hex notation). The parser reads characters from the filespec work area at 5390H, classifying each character as a hex digit (0-9, A-F) or not, and stores valid hex digit characters into the 6-byte buffer at 538AH. Non-hex characters are stored as space (20H) to mark unused digit positions.
Hex Digit Classifier Loop
Each iteration reads one character from (HL), tests whether it falls in the ranges 30H-39H (ASCII digits 0-9), 41H-5AH (uppercase hex A-F), or neither. Valid hex digits are stored to (DE). Non-hex characters are replaced with 20H (space). B counts down from 6; when zero the parse buffer is complete.
Fetch the next character from the parameter string at (HL) into Register A. This character will be tested to determine whether it is a valid hexadecimal digit.
DECrement B and loop back to 5232H if B is not zero. After storing a space placeholder, the loop continues from the same source character position (HL unchanged) for the next parse buffer slot.
Open Output File and Insert Default Extension
The parse buffer at 538AH is complete. Now open the output file using the filespec at 5390H, inserting "CIM" as the default extension if the user did not supply one.
Write CIM Header Records
The file is open. Now write the load-module header records. Record type 05H is the load module header; type 06H is the PDS (partitioned data set) header. Both are written with the 6-byte filename from the parse buffer at 538AH.
Write 6-Byte Filename from Parse Buffer
Now write the 6 bytes from the hex address parse buffer at 538AH. These bytes are the address digits (or spaces) that serve as the module-name field in the header records.
Fetch the next byte from the parse buffer at (HL) into Register A. This byte is either a hex digit character or a space (20H).
DECrement B and loop back to 5273H if B is not zero. This writes all 6 bytes of the filename/address field to the output CIM file header.
Validate Address Range
The headers have been written. Now load the START and END addresses from their self-modifying storage locations and validate that START >= 6000H and END > START. If the range is invalid, select the appropriate error message and exit.
Address Range Valid - Begin Data Output Loop
START >= 6000H and END >= START. The memory range is valid. Now write the data to the file as a sequence of type 01H load records. Each record holds up to 254 bytes (FEH length byte encodes 254 data bytes in VTOS CIM format). The start address (BC = START) is used as the first load address.
Page Output: Write Type 01H Record Header
The byte count for this page is in HL. Write the record type (01H), the length byte (FEH encodes 254+2=256 bytes), and the 2-byte load address, then write the data bytes.
Data Byte Output Inner Loop
Write the actual memory bytes from (HL) to the file. B contains the count of data bytes to write for this record. Each byte is fetched from (HL), HL is incremented, and the byte is written via the character output subroutine. When B reaches zero this record is complete.
Fetch the next memory byte from the address pointed to by HL (the current position in the memory range being dumped) into Register A.
DECrement B and loop back to 52C2H if B is not zero. This writes all B data bytes of the current record to the output file. When B reaches zero the current type 01H record is complete.
All Data Written - Write Transfer Address Record
The data loop has written all pages. Now write the type 02H transfer record (2-byte entry point) and close the output file.
52EAH - Error Exit with Flag
Common error exit reached when the file open or file close operation fails. ORs bit 6 into A (the error-context suppression flag) and exits via 4409H (DOS Error Exit).
52EFH - Error: "FILE SPEC REQUIRED"
Reached when no valid output filename was found on the command line, or when the first argument begins with * (a wildcard). The error message string is embedded at 5333H.
52F8H - Error: "PARAMETER ERROR"
Reached when the second argument to DUMP fails validation. The error message string is embedded at 5346H.
5301H - Common Error Display and Exit
Common error display exit used by both address-range validation error paths. On entry HL points to the error message string to display. Prints the message via 447BH and exits via 4030H.
5307H - Embedded Data: "END LESS THAN START" Message
ASCII error string displayed when the END address is less than the START address. The disassembler has decoded these bytes as Z80 instructions in error.
531BH - Embedded Data: "START LESS THAN X'6000'" Message
ASCII error string displayed when the START address is below 6000H (the protected low-memory boundary). The disassembler has decoded these bytes as Z80 instructions in error.
5333H - Embedded Data: "FILE SPEC REQUIRED" Message
ASCII error string printed when no valid output filename was provided. The disassembler has decoded these bytes as Z80 instructions in error.
5346H - Embedded Data: "PARAMETER ERROR" Message
ASCII error string printed when a parameter validation fails. The disassembler has decoded these bytes as Z80 instructions in error.
5356H - Embedded Data: Default Extension "CIM" and Parse Buffer Area
This area begins with the 3-byte default extension string "CIM" pointed to by HL at 5253H and passed to 4473H. Immediately following is the 6-byte hex address parse buffer at 538AH, and the filespec work area at 5390H. These areas are filled at runtime and are not executable code; the disassembler has decoded them as Z80 instructions in error.
NOT DONE YET
ISAM 72H - PURGE - Offset 3526
VTOS 4.0 PURGE Command Disassembly (SYS6 Library Member, Model I)
The PURGE command provides a quick method to eliminate unwanted clutter that accumulates on a disk. All files (except BOOT and DIR) are listed one at a time; the user responds with Y to purge (kill) the file, or any other key (or ENTER) to leave the file intact. Due to the severity of this operation, the user must first supply the disk's master password - unless it is passed on the command line via the MPW= parameter.
PURGE is a SYS6 library member that loads at 5200H in the overlay slot. It is dispatched by SYS1's command table via the standard RST 28H SVC mechanism. The member spans 5200H-5700H (the upper boundary being a 256-byte GAT/directory sector buffer used for password verification and directory sector reads).
The command operates in three phases: (1) parse the optional MPW= drive and password parameter from the command line, (2) verify the master password against the disk's directory sector, and (3) traverse the directory, displaying each eligible filename and prompting the user for a kill decision. Files protected by attribute bit 4 (system/locked) or bit 7 (invisible) are silently skipped.
Variable and Buffer Reference
| Address Range Bytes | Purpose |
|---|---|
| 5277H 1 byte | Directory entry scan index / self-modifying: holds the info byte used to address directory sectors. Initialized to FFH (scan all drives) and updated as each entry is processed. Also used as a single-byte self-modifying operand for the CALL 4B10H path to pass the sector info byte. |
| 5320H 1 byte | Drive number parsed from the MPW=d: prefix on the command line (0-based). Stored here after subtracting ASCII '0' (30H). Used when setting up track/sector for directory I/O and when building the FCB for the kill operation. |
| 5344H-5345H 2 bytes | Pointer to the password string within the command line buffer. Set by LD BC,0000H / LD (5344H),BC during initialization (null = no password supplied), then populated by the password-parser subroutine at 5314H which returns DE pointing into the command line at the password text. |
| 5388H-53B7H 48 bytes | Data Region - not executable code. Two DEFM strings used by the password verification routine. 5388H-5397H: "MASTER PASSWORD ? " (18 bytes, the prompt displayed when no password is supplied on the command line). 53A0H-53B7H: "INVALID MASTER PASSWORD" + 0DH (error message displayed when the supplied or entered password does not match the directory's master password hash). |
| 541EH-5429H 12 bytes | Data Region. DEFM string "PURGE FILE: " + 03H (display terminator). Displayed before each eligible filename when prompting the user. |
| 542BH-542DH 3 bytes | Data Region. DEFM string " ? " + 03H. The question-mark prompt displayed after the filename, inviting the user to press Y or another key. |
| 541AH-541DH 4 bytes | Line input buffer for the user's single-character Y/N response. 541AH receives the character typed at the "PURGE FILE: name ?" prompt. The input is collected via ROM routine 0040H (line input, B=03H max chars, HL=541AH). |
| 5430H-543DH 14 bytes | Data Region. DEFM control sequence + "*** PURGED ***" + 0DH. The confirmation message displayed after a file is successfully killed. Begins with 1BH/E3H control bytes for terminal positioning. |
| 5441H-544DH 13 bytes | Work buffer into which the filename (8 chars) + "/" + extension (3 chars) are assembled from the raw directory entry fields (offsets +05H and +0DH). Terminated with 03H. Passed to 4467H for display. |
| 544EH-5456H 9 bytes | Data Region. DEFM string "MPW DS" + 00H. This is the FCB-style filespec used when opening the directory file to read the master password record. The open call at 4476H uses DE=544EH as the filespec pointer. |
| 5457H-545EH 8 bytes | FCB (File Control Block) used for the kill (delete) operation. 5457H holds the FCB status byte (initialized to 80H = open-for-output). 5458H holds the record-length byte (01H). 545DH receives the drive number (from 5320H). 545EH receives the directory entry info byte (from the B register at time of kill decision). |
| 5500H-5507H 8 bytes | Password input buffer. When the user is prompted for the master password (no MPW= on command line), up to 8 characters are collected here via ROM routine 0040H. Also used as the comparison target when the password is read from the command line. |
| 5600H-56FFH 256 bytes | Sector buffer for reading the directory's password record (sector 0 of the directory track). Read via CALL 4B45H with E=00H (sector 0). The master password field lives within this sector and is compared against the user-supplied password. |
| 5700H-57FFH 256 bytes | Directory sector buffer for the main traversal loop. Each directory sector is read here via CALL 4B45H with E=01H. The loop walks entries at 5700H, 5720H, 5740H, ... (8 entries x 32 bytes = 256 bytes per sector). |
Major Routine Reference
| Address | Name and Description |
|---|---|
| 5200H | PURGE Entry Point Main entry from SYS1 dispatcher. Checks 430FH bit 5 (re-entry guard), parses optional MPW=d:password from command line (HL), stores drive number to 5320H, opens the directory file via 4476H, calls password verifier at 5314H, then falls into the directory traversal loop at 524FH via 5266H. |
| 524FH | Directory Traversal Loop - Advance Entry Pops the directory entry address from the stack, computes the address of the next 32-byte entry within the 5700H sector buffer. When the pointer overflows past the end of the sector (L wraps past 1FH past last entry), issues a carriage return and returns to DOS READY (402DH). Otherwise falls through to 5266H. |
| 5266H | Directory Traversal Loop - Check Entry Validity Tests the low byte of the entry pointer: skips entries whose L is even-zero (alignment guard) or whose status byte (at HL) is 00H (empty/deleted). For valid entries, saves the current sector info byte and calls 4B10H to validate the directory entry. On success, checks attribute bits 4 and 7 to skip system/invisible files. Eligible files reach the display/prompt section at 5290H. |
| 5290H | Filename Display and Kill Prompt Displays "PURGE FILE: name/ext ?" by assembling the filename from directory entry offsets +05H (8-char name) and +0DH (3-char extension) into the work buffer at 5441H, then calling 4467H twice (once for filename, once for the " ? " suffix). Collects a single character via ROM 0040H (B=03H, HL=541AH). If the user presses Y (59H), branches to 52E7H to perform the kill. |
| 52E7H | File Kill Execution Builds the FCB at 5457H: sets status to 80H (open-output), record length to 01H, stores drive number from 5320H to 545DH, stores directory entry info byte (saved in B) to 545EH. Calls SYS0 routine 442CH (close-with-GAT-read) passing DE=5457H. On success, displays the "*** PURGED ***" confirmation at 5430H via 4467H, resets 5277H to FFH, and loops back to 524FH to advance to the next entry. |
| 5314H | Password Verifier Subroutine Entry: DE = pointer to password string (from command line or 0000H if none). Displays prompt at 5388H if DE=0, collects up to 8 chars into 5500H via ROM 0040H. Calls 5346H to read the directory password sector into 5600H. Calls 53B8H to compare the supplied password against the directory's stored master password. Returns Z on match, NZ on mismatch. On mismatch, ORs the error code with 40H and jumps to 4409H (DOS error exit). |
| 5346H | Password Read Helper Entry: DE = password string pointer, HL = comparison target address (5388H on first call). Calls 534CH to read sector 0 of the directory track into 5600H. If DE is non-null, copies up to 8 chars from (DE) into 5500H stopping at 0DH, comma, or quote delimiters. If DE is null, calls 4467H to display the "MASTER PASSWORD ?" prompt, then collects input via ROM 0040H. Pads remaining bytes of 5500H to 20H (space). Returns DE=5500H pointing to the normalized password buffer. |
| 534CH | Directory Sector 0 Reader Sets up track/sector for directory sector 0 on drive stored at 5320H via CALL 4B65H (E=00H = sector 0), then reads the sector into 5600H via CALL 4B45H. Returns Z on success, NZ on error with A=14H (read error code). |
| 53B8H | Password Comparison Subroutine Entry: DE = normalized password buffer (5500H), HL = directory sector buffer base (5388H on entry - but the actual comparison target within 5600H is derived via the 4B65H/4B45H read). Calls 4B65H and 4B45H to position and read the directory sector. Returns Z if the password in 5500H matches the stored master password in the directory sector, NZ otherwise. |
| 53CBH | Error Exit with Bit 6 Set ORs the current A register with 40H (sets bit 6, which in VTOS signals an error with extended context suppressed) then jumps to 4409H (DOS error exit). Called by the traversal loop on directory read errors and attribute check failures. |
| 53D0H | Parameter Error Display Loads HL=53E2H (the "PARAMETER ERROR" string), calls 447BH to display it with CONFIG/SYS paging, then jumps to 4030H (error-already-displayed exit). Reached when the 4476H open call fails (invalid filespec or drive). |
| 53D9H | Invalid Command During Program Chaining Error Loads HL=53F2H (the "INVALID COMMAND DURING PROGRAM CHAINING" string), calls 447BH, then jumps to 4030H. Reached when 430FH bit 5 is set on entry, indicating PURGE was invoked during program chaining, which is not permitted. |
5200H - PURGE Entry Point: Re-entry Guard and Command Line Parse
The PURGE command entry point. The first instruction checks whether PURGE was invoked during program chaining (bit 5 of the system state flags register 430FH). If so, execution is immediately redirected to an error message. Otherwise, the code parses the optional MPW=d:password parameter from the command line buffer pointed to by HL on entry from the SYS1 dispatcher.
If bit 5 was clear (chaining not active), execution continues here. Register HL on entry from the SYS1 dispatcher points into the command line buffer (4318H area) immediately after the command name "PURGE". The code now scans for an optional drive specifier in the form :d followed by a password string.
A colon was found. The next character is the drive digit (0, 1, 2, or 3). Read and convert it.
Store the binary drive number (0-3) from Register A into the drive number variable at 5320H. This single-byte location is read by multiple routines throughout PURGE: the password verifier at 534CH uses it to select which drive's directory to read, and the kill FCB builder at 52EDH uses it to fill 545DH (FCB drive field).
Drive validated successfully. Now initialize the password pointer (null by default, meaning no password was supplied on the command line) and open the directory file.
Directory file opened successfully. Now call the password verification subroutine to read the disk's master password and compare it against what the user supplied (or will supply interactively).
Password verified. Now set up the directory traversal. Load the drive number and prepare to read the first directory sector into the 5700H buffer.
524FH - Directory Traversal Loop: Advance to Next Entry
On each iteration (except the very first), execution returns here from the prompt/kill section to advance to the next 32-byte directory entry within the 5700H sector buffer. When the entry pointer overflows past the end of the 256-byte sector, a carriage return is printed and control returns to the DOS READY prompt at 402DH.
Loop Start
The previous iteration pushed BC (holding the current entry's low byte in B and directory info byte in C) before reaching the prompt. Here we recover B to compute the next entry address.
The pointer has gone past the end of the 256-byte sector (CARRY was set). The current sector's entries have all been examined. Output a carriage return and return to DOS READY.
All entries in this directory sector have been examined. Print a carriage return and return to the DOS command level.
5266H - Directory Traversal Loop: Entry Validity Check
Tests each directory entry to determine whether it is eligible for the PURGE prompt. Entries that are deleted (status byte = 00H), system-protected (attribute bit 4 set), or invisible (attribute bit 7 set) are silently skipped. Valid, purgeable entries proceed to the filename display and Y/N prompt section at 5290H.
The entry is active (non-zero status byte). Save the entry's low-byte address and directory info byte for later use, then call 4B10H to validate the directory entry fully.
Store the computed entry info byte from Register A into the scan index variable at 5277H. This single-byte location is read by 4B10H to identify which directory entry and sector to validate. It is overwritten on every non-first iteration of the traversal loop, directing 4B10H to the correct entry.
The entry has passed all checks: it is active, non-system, and non-invisible. This file is eligible to be purged. Proceed to display its filename and prompt the user.
5290H - Filename Display and Kill Prompt
Assembles the filename and extension from the directory entry into the display buffer at 5441H, displays "PURGE FILE: name/ext ?", collects a single character from the user, and branches to the kill execution at 52E7H if the user pressed Y.
Loop Start
Copy the 8-character filename from the directory entry (at 42xxH+05H) into the display buffer (5441H), stopping early if a space (20H) padding character is encountered.
Loop End
A space was encountered in the filename before 8 characters were copied. Rewind HL to compensate for the INC HL that advanced past the space character (so that HL correctly addresses the extension field).
The file has an extension. Write the "/" separator character into the display buffer, then copy up to 3 extension characters (stopping on spaces).
Loop Start
Copy up to 3 extension characters from the directory entry into the display buffer, stopping on a space padding character.
Loop End
Filename and extension characters have been copied into the display buffer. Write the 03H terminator and display the assembled string.
Full prompt displayed. Now collect the user's single-character response via the ROM line-input routine.
The user pressed 'Y'. Proceed to kill the file.
52E7H - File Kill Execution
Builds the FCB at 5457H and calls the SYS0 close-with-GAT-read routine at 442CH to kill (delete) the confirmed file. On success, displays "*** PURGED ***" and loops back to the traversal.
Store the directory entry offset byte from Register A into 545EH, which is the directory entry info byte field of the FCB at 5457H (at FCB offset +07H relative to the FCB base 5457H). The close/kill routine at 442CH uses this byte to locate the specific directory entry to deallocate and mark as deleted.
Store the drive number from Register A into 545DH, which is the drive number field of the FCB at 5457H (at FCB offset +06H relative to 5457H). The close/kill routine at 442CH reads the drive number from this FCB field to select the correct drive during deallocation.
Store 01H (record length) from Register A into 5458H, the record-length field of the FCB at 5457H (FCB offset +01H). This completes the record-length initialization of the FCB alongside the status and info bytes set above.
Store 80H (kill-mode FCB status) from Register A into 5457H, the status byte at the base of the FCB (FCB offset +00H). The FCB at 5457H is now fully initialized: status=80H (kill), record-length=01H, drive=5320H value, info=entry offset byte.
File successfully killed. Display the "*** PURGED ***" confirmation message, then reset the scan variable and loop to the next entry.
Store 0FFH (reset sentinel) from Register A into the scan index variable at 5277H (the same location written at 527AH). Resetting this to 0FFH ensures the next directory validation call via 4B10H does a full re-scan rather than using a stale sector-position value from the deleted file.
5314H - Password Verification Subroutine
Reads the two-byte password pointer from 5344H-5345H. If null (0000H = no password on command line), initiates a prompt-and-read sequence. Reads directory sector 0 into 5600H and compares the supplied password against the disk's stored master password record. Returns Z on success; on mismatch exits via DOS error 4409H.
Password matched. Now check two special-case passwords that bypass the normal verification (representing the built-in "super" passwords recognized by VTOS for the PURGE command).
Neither bypass sentinel matched. The password comparison at 53B8H already returned Z (normal match), so control falls through here on a successful normal match. Display the error message and abort.
5346H - Password Read Helper
Calls the directory-sector-0 reader (534CH), then either copies the password from the command line buffer (if DE is non-null) or prompts the user interactively and reads up to 8 characters. Pads the result to exactly 8 characters with spaces and returns DE=5500H.
DE is non-null: a password was supplied on the command line. Copy up to 8 characters from (DE) into the 5500H password buffer, stopping at carriage return (0DH), comma (2CH), or quote (22H) delimiters.
Loop Start
Copy password characters from the command line buffer at (DE) into 5500H, one character at a time, stopping at a delimiter or 8 characters.
Loop Start
Pad the remainder of the 5500H password buffer with space (20H) characters to normalize the password to exactly 8 bytes.
Loop End
Password buffer at 5500H is now exactly 8 bytes long. Set DE=5500H and return to the caller.
5388H - Data: "MASTER PASSWORD ? " Prompt String
18-byte DEFM string used as the interactive master password prompt. Displayed by 4467H (via 536BH) when the user has not supplied the password on the command line.
18-byte ASCII text string: "MASTER PASSWORD ? ". Displayed by the password prompt path at 536BH when the user did not supply the master password via the MPW= parameter. The string is followed immediately by the "INVALID MASTER PASSWORD" error string at 53A0H.
24-byte ASCII error string: "INVALID MASTER PASSWORD" terminated by 0DH (carriage return). Displayed by 447BH at 5339H when the user supplies a password that does not match the disk's stored master password hash. The 0DH terminator causes the display routine to emit a carriage return after the message.
53B8H - Password Comparison Subroutine
Sets up track/sector for directory sector 0 on the drive at 5320H (via 4B65H), reads the sector into 5600H (via 4B45H), then returns Z on success (the calling routine in 5314H will perform the comparison) or NZ on disk read error.
53CBH - Error Exit: OR with 40H then Jump to 4409H
Sets bit 6 of the error code (suppresses extended context display) and jumps to the DOS error exit at 4409H. Used by multiple error paths throughout PURGE.
53D0H - Parameter Error Display and Exit
Displays the "PARAMETER ERROR" string at 53E2H via 447BH and exits via 4030H (error-already-displayed). Reached when the directory file open call at 4476H fails.
53D9H - "Invalid Command During Program Chaining" Error
Displays the "INVALID COMMAND DURING PROGRAM CHAINING" string at 53F2H and exits via 4030H. Reached when PURGE is invoked while bit 5 of 430FH (chaining active) is set.
53E2H - Data: Error and Message Strings
Two DEFM error strings used by the error exit routines at 53D0H and 53D9H.
16-byte ASCII error string: "PARAMETER ERROR" terminated by 0DH (carriage return). Displayed by the parameter-error exit at 53D0H when the directory file cannot be opened (invalid drive specifier or missing directory).
40-byte ASCII error string: "INVALID COMMAND DURING PROGRAM CHAINING" terminated by 0DH (carriage return). Displayed by the chaining-guard exit at 53D9H when PURGE is invoked while program chaining is active (430FH bit 5 = 1). PURGE requires interactive keyboard input and cannot run unattended in a chained sequence.
541EH - Data: Display Strings and Input Buffers
The prompt label, user response buffer, confirmation message, filename work buffer, and filespec string used throughout the PURGE display and kill sequence.
13-byte display string: "PURGE FILE: " terminated by 03H (4467H display terminator). Displayed before each eligible filename in the PURGE loop at 5294H. The cursor remains immediately after the colon+space when 4467H returns, ready for the filename to be appended.
4-byte line input buffer at 541AH. Filled at runtime by ROM routine 0040H (B=03H, HL=541AH) at 52DBH with the user's Y/N response character. The first byte (541AH) is tested at 52DEH against 59H ('Y') to determine whether to kill the file.
4-byte display string: " ? " terminated by 03H. Displayed after the filename at 52D3H as the second half of the full prompt "PURGE FILE: name/ext ? ".
17-byte confirmation display sequence. The 1BH (ESC) and E3H control bytes position the cursor appropriately on the terminal before displaying "*** PURGED ***" followed by 0DH (carriage return). This confirmation appears immediately after a file is successfully killed at 5309H.
13-byte work buffer filled at runtime by the filename/extension assembly code at 5290H-52C8H. Receives up to 8 filename characters, optionally a '/' separator, up to 3 extension characters, and a 03H terminator. Passed to 4467H at 52CCH for display as part of the "PURGE FILE: name/ext ? " prompt.
8-byte filespec string: "MPW DS" + 00H (null terminator). This is the VTOS filespec passed to 4476H at 522DH to open the directory file for reading. "MPW" is the filename placeholder and "DS" is the extension identifying the directory sector file type on VTOS disks.
5457H - Data: FCB for File Kill Operation
8-byte FCB (File Control Block) used by the kill execution path at 52E7H. Fields are filled at runtime by self-modifying code before calling SYS0 routine 442CH.
8-byte FCB structure filled at runtime by the kill execution path at 52F8H-52FDH:
5457H (+00H): Status byte - set to 80H (kill-mode FCB) at 52FA.
5458H (+01H): Record length - set to 01H at 52F5H.
545DH (+06H): Drive number - set from 5320H at 52F0H.
545EH (+07H): Directory entry info byte - set from the entry offset in Register B at 52EAH.
The FCB is passed as DE=5457H to SYS0 routine 442CH (close-with-GAT-read) at 5300H which deactivates the file's extents and marks the directory entry deleted.
ISAM 73H - XFER - Offset 3781
XFER Command - Single-Disk File Transfer
The XFER command is VTOS 4.0's solution for transferring a file between two diskettes on a single-drive system. Unlike COPY, which requires a VTOS system disk to be present on both the source and destination, XFER manages all disk swaps itself: it prompts the user to insert the Source disk, the Destination disk, and the System disk (containing VTOS) as needed. The user may optionally specify a non-zero drive number in the filespec.
XFER loads as SYS6 member 37 (IDAM code 73H) at overlay address 5200H. The member is self-contained: it includes its own disk I/O helper stubs, its own disk-swap prompt loop, and its own source/destination verification routines. It calls into the VTOS SYS0 resident layer for low-level sector I/O and file management operations.
The transfer algorithm reads records from the source file one sector at a time into an internal buffer at 5600H, writing each sector to the destination FCB. Because both source and destination are on different physical diskettes, the overlay must repeatedly swap disks and re-read the source when it cannot keep both open simultaneously. The disk-swap verification logic compares an 18-byte signature from the GAT sector (at offset CEH) to a saved snapshot, looping with a prompt until the correct disk is reinserted.
XFER occupies address space 5200H-54CDH (source code and executable logic) plus a data area at 5600H (FCB and sector buffer). The total working code span is approximately 716 bytes. Data strings and message text are embedded within the executable range at 5407H-54CDH.
Variable and Data Area Table
| Address Range Bytes | Purpose |
|---|---|
| 5358H 1 byte | Drive number extracted from filespec (self-modifying operand target). Stored at 5233H; read back at 524AH, 5273H, 526BH, 538BH. |
| 54CEH-54CDH+20H 32 bytes | Source FCB (File Control Block). Initialized by filespec extraction (441CH) and file open (4424H). Also used as LDIR source for destination FCB copy at 523FH. |
| 54EEH-54EEH+1FH 32 bytes | Destination FCB. Populated by LDIR copy of source FCB at 523FH, then used for all write-record calls (4439H) and close (4428H). |
| 54D1H-54D2H 2 bytes | Self-modifying: current sector buffer pointer for read loop (LD (54D1H),HL at 52BBH). Used as FCB record pointer during the read/copy loop. |
| 54F1H-54F2H 2 bytes | Self-modifying: current sector buffer pointer for write loop (LD (54F1H),HL at 52DDH and 5314H). |
| 54D6H-54D7H 2 bytes | Saved HL pointer from read loop at end-of-extent path (LD HL,(54D6H) at 5311H). Preserves read position across disk swap. |
| 54F6H-54F7H 2 bytes | Saved write pointer, populated at 5314H from 54D6H. Used by write-continuation path after disk swap. |
| 550EH-551FH 18 bytes | Source disk GAT signature buffer (18 bytes at GAT offset CEH). Saved at 5257H-525DH via LDIR. Used by 5364H to verify source disk reinserted correctly. |
| 5520H-5531H 18 bytes | Destination disk GAT signature buffer. Saved at 527DH-5286H via LDIR. Used by 5381H to verify destination disk reinserted correctly. |
| 5600H onwards variable | Sector I/O buffer (HL base address). Used as the LD HL,5600H base for both FCB open (4424H) and read-record (4420H) calls. Grows upward as records accumulate. |
| 5407H-5419H 19 bytes | String: "FILE SPEC REQUIRED" + 0DH. Displayed when no filespec is provided on the command line. |
| 541AH-5441H 40 bytes | String: "INVALID COMMAND DURING PROGRAM CHAINING" + 0DH. Displayed when XFER is invoked while a program chain is active (430FH bit 5 set). |
| 5442H-546BH 42 bytes | String: "SOURCE AND DESTINATION DISKS ARE THE SAME" + 0DH. Displayed when source and destination GAT signatures match. |
| 546CH-548AH 31 bytes | Disk-swap prompt: 1DH + 1EH + "INSERT SYSTEM DISK (ENTER)" + 1DH + 03H. Displayed by the system-disk swap routine at 5357H. |
| 548BH-54A9H 31 bytes | Disk-swap prompt: 1DH + 1EH + "INSERT SOURCE DISK (ENTER)" + 1DH + 03H. Displayed by the source-disk verify loop at 5364H. |
| 54AAH-54CDH 36 bytes | Disk-swap prompt: 1DH + 1EH + "INSERT DESTINATION DISK (ENTER)" + 1DH + 03H. Displayed by the destination-disk verify loop at 5381H. |
Major Routine Table
| Address | Name and Purpose |
|---|---|
| 5200H | XFER Entry Point Entry: A = SVC dispatch byte from SYS1. HL = command-line buffer pointer. Checks 430FH bit 5 (re-entry/chaining mode), extracts filespec, reads high-memory limit, determines drive number. Falls through to main transfer logic. |
| 5241H | Source Disk Open and Signature Capture Entry: C = drive number (from 5358H). Calls 53D4H (SVC 84H) to mount source drive, then 53DDH to read its GAT sector. Saves 18-byte signature at 550EH. Opens the source file via 4424H. |
| 526DH | Destination Disk Open and Signature Capture Entry: C = drive number. Calls 53DAH (SVC 8AH), reads destination GAT, saves 18-byte signature at 5520H. Compares source/destination signatures; if equal, displays "SOURCE AND DESTINATION DISKS ARE THE SAME" and exits. |
| 52A1H | Main Read/Write Transfer Loop Entry: Source FCB at 54EEH, destination FCB at 54EEH, buffer base HL=5600H. Reads one record via 4420H, writes via 4439H. On EOF (1CH/1DH) branches to end-of-file handler. Advances buffer pointer in H (page-granular). |
| 52F0H | Transfer Completion and Close Entry: fall-through from write loop on EOF (1CH). Calls 5333H to conditionally swap to destination disk, closes destination FCB via 4428H, swaps back to system disk via 5357H, exits via 402DH. |
| 530EH | End-of-Extent Handler Entry: A = 1CH or 1DH (extent boundary error from read or write). Saves read pointer, swaps to destination disk (5333H/53DAH), writes remaining sector via 4439H, closes destination FCB, swaps to system disk, exits via 402DH. |
| 5333H | Conditional Destination Disk Swap Entry: H = current page. Checks if H = 56H (page of buffer base). If already on destination disk, returns immediately. Otherwise calls 5381H (destination disk insert loop). Reloads buffer base HL=5600H and loops writing any remaining sectors from the buffer. |
| 534FH | Error Exit Path Entry: A = error code. Saves A, calls 5357H (swap to system disk), restores A, jumps to 53F0H (OR A,40H / JP 4409H = DOS error exit). |
| 5357H | System Disk Swap Prompt Entry: A = 00H (tested via OR A / RET NZ to skip swap if already on system disk). Displays the "INSERT SYSTEM DISK" prompt at 546CH via 53A7H. Waits for ENTER, then returns. |
| 5364H | Source Disk Verify Loop Entry: C = drive number. Displays "INSERT SOURCE DISK" prompt at 548BH, reads GAT via 53DDH, compares 18-byte signature at 42CEH against saved snapshot at 550EH via 539EH. Loops until match. |
| 5381H | Destination Disk Verify Loop Entry: C = drive number. Displays "INSERT DESTINATION DISK" prompt at 54AAH, reads GAT via 53DDH, compares 18-byte signature at 42CEH against saved snapshot at 5520H. Loops until match. |
| 539EH | 18-Byte Memory Compare Entry: HL = expected signature, DE = actual signature, B = 18H (byte count). Compares byte-by-byte via DJNZ loop. Returns Z if all bytes match, NZ on first mismatch. |
| 53A7H | Display Prompt and Wait for ENTER Entry: HL = pointer to display string. Calls 4467H to display string. Polls 3840H bit 0 (BREAK/ENTER key) until released, then polls with 4000H timeout. On timeout: displays 1EH (clear screen), delays via 0060H, loops to re-display prompt. On keypress: calls 0049H, clears screen (1EH), returns. |
| 53D4H | SVC Stub: Mount Source Drive (SVC 84H) Loads A=84H and issues RST 28H to invoke overlay SVC 84H. No entry conditions beyond a valid drive context. |
| 53D7H | SVC Stub: SVC 8AH Loads A=8AH and issues RST 28H. |
| 53DAH | SVC Stub: Mount Destination Drive (SVC 85H) Loads A=85H and issues RST 28H. |
| 53DDH | Read GAT Sector into 4200H Entry: C = drive number. Calls 4B65H to set up track/sector for directory I/O, sets E=00H (GAT sector), reads into 4200H via 4B45H. Returns Z on success, A=14H on read error. |
| 53F0H | Error Code Augmenter and DOS Error Exit Entry: A = raw error code. ORs A with 40H (sets bit 6, suppresses extended error context), then jumps to 4409H (DOS Error Exit). |
5200H - XFER Entry Point: Chaining Check and Filespec Extraction
The overlay begins by checking whether VTOS is currently executing a program chain (which would make XFER illegal), then extracts the source filespec from the command-line buffer and determines the drive number to use.
Having confirmed XFER is allowed to run, the code now extracts the filespec from the command-line buffer. The user typed something like "XFER MYFILE/TXT:1" and SYS1 has already tokenized the command name, leaving the remainder (the filespec) accessible via the standard extract routine.
The filespec was successfully parsed into the FCB at 54CEH. Now XFER must determine the high-memory page boundary so it knows how large an in-memory sector buffer it can use. The buffer runs from 5600H upward, one page (256 bytes) per sector read. The code reads the RAM top address to establish where the buffer must stop.
Store Register A (the maximum buffer page) into address 52D4H. Address 52D4H is the operand byte of a CP A,nn instruction inside the read loop at 52D3H. At runtime, the comparison CP A,60H is patched to CP A,[max_page], so the read loop knows when the in-memory buffer is full and it must switch to writing. The disassembler shows the static value 60H at 52D3H-52D4H, but the actual runtime value is determined here from the detected RAM top.
Store Register A (the same maximum buffer page) into address 52EBH. Address 52EBH is the operand byte of a parallel CP A,nn instruction inside the write loop at 52EAH. This patches the write loop's page-boundary check with the same runtime limit, ensuring the write phase uses the identical stopping criterion as the read phase.
521DH - Drive Number Extraction from Filespec
The FCB at 54CEH has been populated with the parsed filespec. XFER now walks the filespec string to locate the colon-delimited drive number (e.g., ":1") and extracts it as a binary drive index. If no explicit drive is given, drive 0 is used.
5222H - Filespec Scan Loop
Fetch the current byte from the filespec buffer at (HL) into Register A. This is the character being examined during the scan for the colon drive-number separator.
5232H - Drive Number Save and FCB Copy
Store Register A (the drive number) into address 5358H. Address 5358H is the operand byte of a LD A,nn instruction, used throughout the overlay to reload the drive number into Register A before passing it to C for I/O calls. Every disk access in XFER - reading the GAT, verifying signatures, reading/writing records - uses this stored drive number.
5241H - Source Disk Mount and GAT Signature Capture
With both FCBs prepared, XFER now mounts the source drive and reads its GAT sector to capture the 18-byte disk identity signature. This signature is saved so the verify loops can later confirm the correct disk is reinserted after swapping.
525FH - Open Source File
The source disk is verified in the drive. XFER now opens the source file using the FCB at 54CEH.
526DH - Destination Disk Mount and GAT Signature Capture
The source file is now open. XFER prompts for the destination diskette, reads its GAT, saves its 18-byte signature, and verifies that the source and destination are not the same disk.
With both disk signatures saved, XFER now compares them. If both signatures are identical, the user has inserted the same disk for both source and destination - which would silently overwrite the source file with a copy of itself. XFER detects and rejects this scenario.
If we reach here, the source and destination signatures matched - the same disk was inserted for both. XFER displays an error and aborts.
52A1H - Main Read/Write Transfer Loop
Source and destination are confirmed to be different diskettes. XFER now enters its main transfer loop: it reads records from the source file into an in-memory buffer (one 256-byte page per record), then writes them out to the destination file. Because only one disk can be in the single drive at a time, XFER first reads as many sectors as will fit in memory, then prompts for a disk swap and writes them all out.
Write Loop Start
Having read records into the buffer, XFER now writes them to the destination. The write loop mirrors the structure of the read loop: it processes one page (256-byte sector) at a time, advancing the destination buffer pointer by incrementing H, and looping until the same page limit is reached.
Store Register Pair HL (5600H) into address 54D1H. Address 54D1H is the operand of a LD HL,nn instruction used by the read loop to track its current buffer page. By writing 5600H here, XFER resets the read pointer back to the start of the buffer for the next read phase.
52C1H - Read Record Into Buffer (Inner Read Loop)
GOSUB to SYS0 routine at 4436H to read the next record from the source file (DE = Source FCB at 54CEH). Each call transfers one 256-byte sector from the source disk into the current buffer page (pointed to by the FCB's internal buffer pointer). Returns Z on success, NZ with error code in A on end-of-file or I/O error.
52D1H - Buffer Page Advance and Overflow Check (Read Loop)
The in-memory buffer is now full. XFER must flush the buffered records to the destination disk before reading more. It begins the flush sequence by verifying the destination disk is in the drive.
Store Register Pair HL (5600H) into address 54F1H. Address 54F1H is the operand of a LD HL,nn instruction in the write loop, tracking the current write buffer pointer. Resetting to 5600H starts the write phase from the beginning of the buffered data.
52E3H - Write Record from Buffer (Inner Write Loop)
GOSUB to SYS0 routine at 4439H to write one record to the destination file (DE = Destination FCB at 54EEH). Transfers one 256-byte page from the current buffer position to the destination diskette. Returns Z on success, NZ with error code in A on failure.
Write Loop End
All buffered records have been written to the destination disk. XFER must now swap back to the source disk to read the next batch. It jumps back to the source-disk verify phase at 52B5H to re-enter the read/write cycle.
52F0H - Transfer Completion and Destination File Close
End-of-file (code 1CH) has been returned from the read loop. The final batch of records is still in the in-memory buffer. XFER now writes these remaining records to the destination disk and closes the destination file.
530EH - End-of-Extent Handler
Error codes 1CH (end of file) or 1DH (end of extent, more data follows) were returned from the read loop. This path saves the current read position, swaps to the destination disk, writes any remaining buffered data, and either closes the file (1CH) or continues the transfer (1DH).
Store Register Pair HL (the restored read position) into address 54F6H. Address 54F6H is the operand of a LD HL,nn instruction used by the write-continuation path to know where the next write should begin in the buffer.
5333H - Conditional Destination Disk Swap and Buffer Flush
This subroutine checks whether the destination disk is already active. If the buffer's current page pointer (H) indicates we are still in the first page range (i.e., H = 56H, the base page), no flush is needed. Otherwise, the destination disk verify loop is invoked and all buffered records are written out via a dedicated write loop.
533EH - Buffer Flush Write Loop
Loop Start
Store Register Pair HL (current write buffer pointer) into address 54F1H (the operand of the write-loop pointer instruction). This tracks the current write position in the buffer across iterations.
All buffered records have been written to the destination disk. Return to caller (either 52F0H or 530EH) with the flush complete.
534FH - Error Exit Path
Any I/O error in the transfer logic jumps here. The error code in A is preserved while the system disk is swapped back in, then the DOS error exit is invoked.
5357H - System Disk Swap Prompt
This subroutine prompts the user to insert the system disk and waits for ENTER. If A is non-zero on entry (meaning the caller has not explicitly zeroed it to request a swap), the routine returns immediately without prompting - this acts as a conditional skip for paths that know the system disk is already present.
5364H - Source Disk Verify Loop
This subroutine loops, displaying the "INSERT SOURCE DISK" prompt and verifying the disk's GAT signature, until the correct source diskette is confirmed in the drive. It is called whenever the source disk must be present for reading.
Save Register Pair HL onto the stack. HL holds the current context (buffer pointer or other caller state) and must be preserved through the disk-swap prompt and GAT verification sequence.
The source disk signature matches. The correct source diskette is confirmed in the drive. Return to caller with Z set.
5381H - Destination Disk Verify Loop
Mirrors 5364H but verifies the destination disk by comparing against the signature saved at 5520H. Loops until the correct destination diskette is confirmed.
Save Register Pair HL onto the stack.
The correct destination diskette is confirmed. Return to caller with Z set.
539EH - 18-Byte Memory Compare Subroutine
A general-purpose byte-comparison loop. Called with HL = expected buffer, DE = actual buffer, B = byte count. Returns Z if all bytes match, NZ on first mismatch.
Fetch the current byte from the actual buffer at (DE) into Register A. DE points to one of the two 18-byte signature buffers being compared (either 550EH or 5520H).
Return to caller with Z set (all 18 bytes matched - disks have the same identity signature).
53A7H - Display Prompt and Wait for ENTER (with Timeout and Retry)
The central disk-swap wait routine. Displays a prompt string, waits for the user to release any currently-pressed key, then polls for a new keypress with a countdown timeout. On timeout, it clears the screen, delays briefly, and re-displays the prompt (creating a blinking/pulsing effect). On keypress, it clears the screen and returns.
The routine first waits for any currently-pressed key to be released. This prevents an accidental key from immediately satisfying the ENTER check. The keyboard is read at 3840H; bit 0 going low indicates the BREAK/ENTER key is released.
53AAH - Wait for Key Release Loop
Fetch the keyboard status byte from memory-mapped keyboard row at 3840H into Register A. This row contains ENTER, CLEAR, BREAK, and cursor keys. The bit pattern reflects which keys are currently pressed (active LOW on Model I keyboard).
The key has been released. Now the routine polls for a new keypress with a countdown timeout. BC is loaded with 4000H (16384 decimal) as the timeout count. Each poll iteration decrements BC; if BC reaches zero before a key is pressed, the timeout branch fires and re-displays the prompt.
53B3H - Keypress Poll Loop with Timeout
Fetch the keyboard status byte from 3840H again. Polling for a newly pressed key.
Timeout expired - no key was pressed during the countdown. The routine now clears the screen and re-displays the prompt after a brief delay, creating a visual refresh or "blinking" effect to draw the user's attention.
53CBH - Keypress Detected: Clear Screen and Return
53D4H - SVC Dispatch Stubs
Three back-to-back single-function stubs that issue RST 28H overlay SVC calls for drive mounting. Each stub loads a specific SVC code into A and issues RST 28H.
53DDH - Read GAT Sector into 4200H
Sets up track/sector addressing for the GAT (track 0, sector 0) and reads it into the standard sector buffer at 4200H. Called by all three disk-swap verify routines to obtain the current disk's signature.
53F0H - Error Code Augmenter and DOS Error Exit
Any error in XFER that requires a VTOS error message to be displayed routes through here. Bit 6 is set in the error code to suppress the extended error context (filename/address details), then control is passed to the VTOS DOS Error Exit.
53F5H - "FILE SPEC REQUIRED" Error Display
Jumped to when no filespec was found on the command line (from 520EH). Displays the error message and exits via the error-already-displayed path.
53FEH - "INVALID COMMAND DURING PROGRAM CHAINING" Error Display
Jumped to when 430FH bit 5 is set (chaining active) at entry (from 5205H). Displays the chaining-restriction error and exits.
5407H - Data: Error and Prompt Strings
The remainder of the XFER overlay is data - ASCII message strings and disk-swap prompt strings. The disassembler has decoded these bytes as Z80 instructions, but they are text. All strings are display output only; none is executed as code.
LOAD (ISAM 81H) / RUN (ISAM 82H) - Offset 3A5E
VTOS 4.0 SYS6 LOAD and RUN Commands - Member 3AH (Model I)
This routine implements two closely related VTOS library commands: LOAD and RUN. Both commands load a program file from disk into memory. They differ in exactly one respect: LOAD leaves the program in memory without executing it, while RUN loads and then transfers control to it. Both entry points share a common body that handles filespec parsing, the optional (X) disk-swap prompt, file opening, and loading via SYS0's program loader at 4430H/4433H.
The two ISAM entries for this member are:
| IDAM | Command | Entry Point | Member |
|---|---|---|---|
| 81H | LOAD | 5200H | 3AH |
| 82H | RUN | 520DH | 3AH |
The VTOS documentation describes the commands as follows:
LOAD <filespec> [(X)] - Loads a CMD or CIM file into memory without executing it. Useful for loading high-memory drivers and BASIC functions that reside above address X'6000'. The (X) option prompts for disk insertion (useful on single-drive systems when the program does not reside on the VTOS system disk). Default extension is CMD.
RUN <filespec> [(X)] - Like LOAD but also executes the loaded program. Useful for running programs that do not reside on the VTOS system disk.
Key Routines and Data Areas
| Address | Purpose |
|---|---|
| 5200H | LOAD entry point (IDAM 81H) |
| 520DH | RUN entry point (IDAM 82H) |
| 5216H | Common setup - extract filespec, check for wildcard, set default extension |
| 5245H | Core load loop - poll for disk ready, call loader, retry on timeout |
| 526FH | Issue SVC 84H to load overlay (open file / prepare load) |
| 5272H | Error exit - "FILE SPEC REQUIRED" (missing or wildcard filespec) |
| 527BH | Error exit - "PARAMETER ERROR" (bad option / open failure) |
| 52F2H | LOAD completion - call 4430H (load only), check (X) flag, exit |
| 5300H | RUN completion - call 4430H, push HL, fall through to check (X) flag and execute |
Memory Locations Referenced
| Address | Purpose |
|---|---|
| 3840H | Disk status port (read). Bit 1 = disk motor/drive-ready flag (RRCA shifts bit 1 into carry). |
| 402DH | DOS READY / No-Error Exit stub in resident SYS0. |
| 4030H | Error-already-displayed exit in resident SYS0. |
| 4430H | SYS0 Load Program File routine (load only, no execute). |
| 441CH | SYS0 Extract Filespec from command line into DCB/work area. |
| 4467H | SYS0 Display message to screen (HL = message address). |
| 4473H | SYS0 Insert default extension into filespec work area. |
| 4476H | SYS0 Open file utility (DE = filespec template, returns Z on success). |
| 447BH | SYS0 Display string at (HL) with CONFIG/SYS processing. |
| 5239H | Runtime flag byte: non-zero = (X) option was specified by user. |
| 52C6H-52CEH | Filespec work area template ("X 9R"+00H, 9 bytes). |
| 52CFH-52D1H | Default extension string: "CMD" (3 bytes, no terminator - used by 4473H). |
| 52D2H-52F1H | FCB data area (32 bytes) used by the file open and load operations. |
| 5284H-5296H | Error message: "FILE SPEC REQUIRED" + 0DH. |
| 5297H-52A6H | Error message: "PARAMETER ERROR" + 0DH. |
| 52A7H-52C5H | Disk swap prompt: 1DH+1EH+"INSERT SOURCE DISK (ENTER)"+1DH+03H. |
| 5306H-5324H | Disk swap prompt: 1DH+1EH+"INSERT SYSTEM DISK (ENTER)"+1DH+03H. |
LOAD Entry Point (IDAM 81H) - 5200H
Execution arrives here when the user types a LOAD command at the VTOS prompt. The SYS1 command dispatcher has already matched the command name, issued the RST 28H SVC call with IDAM 81H, and the overlay loader has loaded this member into the overlay slot at 5200H. At this point DE holds 52D2H (the FCB work area) passed in by the caller, and HL holds the address of the command line buffer containing the filespec and any options the user typed.
RUN Entry Point (IDAM 82H) - 520DH
Execution arrives here when the user types a RUN command. The setup is identical to LOAD, and the two entry points share almost all their code. The only difference is that RUN jumps to the completion path at 5300H, which calls 4430H (load the program) and then falls through to execute it, while LOAD jumps to 52F2H which only loads.
Common Setup Routine - 5216H
This shared subroutine is called by both the LOAD entry at 5200H and the RUN entry at 520DH. On entry, HL points to the command line buffer. On return: Z set = file opened successfully; NZ = an error was displayed and the caller should exit.
Disk-Wait and Load Core Routine - 5245H
This routine handles the LOAD/RUN disk-wait and program-load operation. It is called with HL pointing to a disk-swap prompt message. It polls the disk hardware, displays retry/wait messages on timeout, and performs the actual load via SYS0.
Disk Ready - Clean Exit - 5269H
Reached when the disk status poll at 5255H detected the disk becoming ready. The routine issues a screen clear and returns.
SVC 84H Helper - 526FH
This two-byte stub issues an RST 28H SVC call with function code 84H. SVC 84H is the VTOS overlay function dispatcher used to invoke additional setup or file preparation services.
Error Exit: "FILE SPEC REQUIRED" - 5272H
Reached when no filespec was found on the command line or a wildcard was used. Displays error and exits.
Error Exit: "PARAMETER ERROR" - 527BH
Reached when the file open at 4476H returned NZ. Displays "PARAMETER ERROR" and exits.
Data: "FILE SPEC REQUIRED" Message - 5284H
Data: "PARAMETER ERROR" Message - 5297H
Data: "INSERT SOURCE DISK (ENTER)" Prompt - 52A7H
Data: Filespec Work Area Template - 52C6H
Data: Default Extension "CMD" - 52CFH
Data: FCB Work Area - 52D2H
LOAD Completion Path - 52F2H
Reached from the LOAD entry after common setup. This path loads the program without executing it, checks the (X) flag, and exits.
RUN Completion Path - 5300H
Reached via JP 5300H from the RUN entry point at 5213H. This path loads the program and then passes control to the program's transfer address to execute it.
Data: "INSERT SYSTEM DISK (ENTER)" Prompt - 5306H
Disk-swap prompt used to restore the system environment after a LOAD or RUN operation involving the (X) option.