TRS-80 DOS - VTOS v4.0.2 for the Model I - SYS6/SYS Disassembled

Page Customization

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

  1. 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 "
    This simply identifies the file as SYS6.
  2. 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 * * *
  3. 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

Utility

Tim Mann has developed, as part of his XTRS emulator, some routines which can assist in processing ISAM and PDS files:

Full ISAM/PDS Breakdown

Offset RangeLibrary
Command
Hex BytesExplanation
009B-00A0AUTO08 06 11 00 52
01 00 C9
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 11 = ISAM # 11H
  • 00 52 = Transfer address = 5200H
  • 01 00 C9 = Triad pointer: NRN=01H 00H, RBO=C9H → member starts at byte offset (1×256) + 0xC9 = 256 + 201 = 457 (0x1C9)
00A1-00A6DATE08 06 15 00 52
02 00 1D
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 15 = ISAM # 15H
  • 00 52 = Transfer address = 5200H
  • 02 00 1D = Triad pointer: NRN=02H 00H, RBO=1DH → member starts at byte offset (2×256) + 0x1D = 512 + 29 = 541 (0x21D)
00A7-00ACTIME08 06 16 2D 52
02 00 1D
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 16 = ISAM # 16H
  • 2D 52 = Transfer address = 522DH
  • 02 00 1D = Triad pointer: NRN=02H 00H, RBO=1DH → member starts at byte offset (2×256) + 0x1D = 512 + 29 = 541 (0x21D)
00AD-00B2ALLOC08 06 13 00 52
02 00 B2
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 13 = ISAM # 13H
  • 00 52 = Transfer address = 5200H
  • 02 00 B2 = Triad pointer: NRN=02H 00H, RBO=B2H → member starts at byte offset (2×256) + 0xB2 = 512 + 178 = 690 (0x2B2)
00B3-00B8KILL08 06 18 00 52
03 00 71
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 18 = ISAM # 18H
  • 00 52 = Transfer address = 5200H
  • 03 00 71 = Triad pointer: NRN=03H 00H, RBO=71H → member starts at byte offset (3×256) + 0x71 = 768 + 113 = 881 (0x371)
00B9-00BELIB08 06 19 00 52
03 00 B1
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 19 = ISAM # 19H
  • 00 52 = Transfer address = 5200H
  • 03 00 B1 = Triad pointer: NRN=03H 00H, RBO=B1H → member starts at byte offset (3×256) + 0xB1 = 768 + 177 = 945 (0x3B1)
00BF-00C4DEBUG08 06 14 00 52
03 00 E8
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 14 = ISAM # 14H
  • 00 52 = Transfer address = 5200H
  • 03 00 E8 = Triad pointer: NRN=03H 00H, RBO=E8H → member starts at byte offset (3×256) + 0xE8 = 768 + 232 = 1000 (0x3E8)
00C5-00CACLOCK08 06 17 41 52
03 00 E8
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 17 = ISAM # 17H
  • 41 52 = Transfer address = 5241H
  • 03 00 E8 = Triad pointer: NRN=03H 00H, RBO=E8H → member starts at byte offset (3×256) + 0xE8 = 768 + 232 = 1000 (0x3E8)
00CB-00D0TRACE08 06 1A 57 52
03 00 E8
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 1A = ISAM # 1AH
  • 57 52 = Transfer address = 5257H
  • 03 00 E8 = Triad pointer: NRN=03H 00H, RBO=E8H → member starts at byte offset (3×256) + 0xE8 = 768 + 232 = 1000 (0x3E8)
00D1-00D6VERIFY08 06 1B 67 52
 
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 1B = ISAM # 1BH
  • 67 52 = Transfer address = 5267H
  • 03 00 E8 = Triad pointer: NRN=03H 00H, RBO=E8H → member starts at byte offset (3×256) + 0xE8 = 768 + 232 = 1000 (0x3E8)
00D7-00DCO!
(Copyright Notice)
08 06 1D 00 52
04 00 E2
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 1D = ISAM # 1DH
  • 00 52 = Transfer address = 5200H
  • 04 00 E2 = Triad pointer: NRN=04H 00H, RBO=E2H → member starts at byte offset (4×256) + 0xE2 = 1024 + 226 = 1250 (0x4E2)
00DD-00E2MEMORY08 06 1E 00 52
05 00 35
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 1E = ISAM # 1EH
  • 00 52 = Transfer address = 5200H
  • 05 00 35 = Triad pointer: NRN=05H 00H, RBO=35H → member starts at byte offset (5×256) + 0x35 = 1280 + 53 = 1333 (0x535)
00E3-00E8SYSTEM's SYSGEN Command08 06 1C 00 52
05 00 C3
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 1C = ISAM # 1CH
  • 00 52 = Transfer address = 5200H
  • 05 00 C3 = Triad pointer: NRN=05H 00H, RBO=C3H → member starts at byte offset (5×256) + 0xC3 = 1280 + 195 = 1475 (0x5C3)
00E9-00EEDIR08 06 21 00 52
08 00 27
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 21 = ISAM # 21H
  • 00 52 = Transfer address = 5200H
  • 08 00 27 = Triad pointer: NRN=08H 00H, RBO=27H → member starts at byte offset (8×256) + 0x27 = 2048 + 39 = 2087 (0x827)
00EF-00F4FREE08 06 22 00 52
0D 00 43
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 22 = ISAM # 22H
  • 00 52 = Transfer address = 5200H
  • 0D 00 43 = Triad pointer: NRN=0DH 00H, RBO=43H → member starts at byte offset (13×256) + 0x43 = 3328 + 67 = 3395 (0xD43)
00F5-00FACHAIN08 06 91 00 52
0F 00 20
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 91 = ISAM # 91H
  • 00 52 = Transfer address = 5200H
  • 0F 00 20 = Triad pointer: NRN=0FH 00H, RBO=20H → member starts at byte offset (15×256) + 0x20 = 3840 + 32 = 3872 (0xF20)
00FB-0100SYSTEM08 06 A1 00 52
14 00 B9
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • A1 = ISAM # A1H
  • 00 52 = Transfer address = 5200H
  • 14 00 B9 = Triad pointer: NRN=14H 00H, RBO=B9H → member starts at byte offset (20×256) + 0xB9 = 5120 + 185 = 5305 (0x14B9)
0101-0106SPOOL08 06 A2 08 52
19 00 D7
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • A2 = ISAM # A2H
  • 08 52 = Transfer address = 5208H
  • 19 00 D7 = Triad pointer: NRN=19H 00H, RBO=D7H → member starts at byte offset (25×256) + 0xD7 = 6400 + 215 = 6615 (0x19D7)
0107-010CAPPEND08 06 31 00 52
1D 00 FD
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 31 = ISAM # 31H
  • 00 52 = Transfer address = 5200H
  • 1D 00 FD = Triad pointer: NRN=1DH 00H, RBO=FDH → member starts at byte offset (29×256) + 0xFD = 7424 + 253 = 7677 (0x1DFD)
010D-0112COPY08 06 32 70 52
1D 00 FD
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 32 = ISAM # 32H
  • 70 52 = Transfer address = 5270H
  • 1D 00 FD = Triad pointer: NRN=1DH 00H, RBO=FDH → member starts at byte offset (29×256) + 0xFD = 7424 + 253 = 7677 (0x1DFD)
0113-0118BUILD08 06 33 00 52
20 00 A9
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 33 = ISAM # 33H
  • 00 52 = Transfer address = 5200H
  • 20 00 A9 = Triad pointer: NRN=20H 00H, RBO=A9H → member starts at byte offset (32×256) + 0xA9 = 8192 + 169 = 8361 (0x20A9)
0119-011ELIST08 06 41 00 52
21 00 7C
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 41 = ISAM # 41H
  • 00 52 = Transfer address = 5200H
  • 21 00 7C = Triad pointer: NRN=21H 00H, RBO=7CH → member starts at byte offset (33×256) + 0x7C = 8448 + 124 = 8572 (0x217C)
011F-0124PRINT08 06 42 06 52
21 00 7C
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 42 = ISAM # 42H
  • 06 52 = Transfer address = 5206H
  • 21 00 7C = Triad pointer: NRN=21H 00H, RBO=7CH → member starts at byte offset (33×256) + 0x7C = 8448 + 124 = 8572 (0x217C)
0125-012AATTRIB08 06 51 00 52
24 00 A7
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 51 = ISAM # 51H
  • 00 52 = Transfer address = 5200H
  • 24 00 A7 = Triad pointer: NRN=24H 00H, RBO=A7H → member starts at byte offset (36×256) + 0xA7 = 9216 + 167 = 9383 (0x24A7)
012B-0130PROT08 06 52 00 52
26 00 8D
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 52 = ISAM # 52H
  • 00 52 = Transfer address = 5200H
  • 26 00 8D = Triad pointer: NRN=26H 00H, RBO=8DH → member starts at byte offset (38×256) + 0x8D = 9728 + 141 = 9869 (0x268D)
0131-0136RENAME08 06 53 00 52
29 00 BB
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 53 = ISAM # 53H
  • 00 52 = Transfer address = 5200H
  • 29 00 BB = Triad pointer: NRN=29H 00H, RBO=BBH → member starts at byte offset (41×256) + 0xBB = 10496 + 187 = 10683 (0x29BB)
0137-013CDEVICE08 06 61 00 52
2B 00 60
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 61 = ISAM # 61H
  • 00 52 = Transfer address = 5200H
  • 2B 00 60 = Triad pointer: NRN=2BH 00H, RBO=60H → member starts at byte offset (43×256) + 0x60 = 11008 + 96 = 11104 (0x2B60)
013D-0142LINK08 06 62 00 52
2D 00 C7
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 62 = ISAM # 62H
  • 00 52 = Transfer address = 5200H
  • 2D 00 C7 = Triad pointer: NRN=2DH 00H, RBO=C7H → member starts at byte offset (45×256) + 0xC7 = 11520 + 199 = 11719 (0x2DC7)
0143-0148RESET08 06 63 00 52
2E 00 D6
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 63 = ISAM # 63H
  • 00 52 = Transfer address = 5200H
  • 2E 00 D6 = Triad pointer: NRN=2EH 00H, RBO=D6H → member starts at byte offset (46×256) + 0xD6 = 11776 + 214 = 11990 (0x2ED6)
0149-014EROUTE08 06 64 00 52
30 00 CA
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 64 = ISAM # 64H
  • 00 52 = Transfer address = 5200H
  • 30 00 CA = Triad pointer: NRN=30H 00H, RBO=CAH → member starts at byte offset (48×256) + 0xCA = 12288 + 202 = 12490 (0x30CA)
014F-0154SET08 06 65 01 5A
32 00 6C
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 65 = ISAM # 65H
  • 01 5A = Transfer address = 5A01H
  • 32 00 6C = Triad pointer: NRN=32H 00H, RBO=6CH → member starts at byte offset (50×256) + 0x6C = 12800 + 108 = 12908 (0x326C)
0155-015AFILTER08 06 66 00 5A
32 00 6C
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 66 = ISAM # 66H
  • 00 5A = Transfer address = 5A00H
  • 32 00 6C = Triad pointer: NRN=32H 00H, RBO=6CH → member starts at byte offset (50×256) + 0x6C = 12800 + 108 = 12908 (0x326C)
015B-0160DUMP08 06 71 00 52
33 00 91
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 71 = ISAM # 71H
  • 00 52 = Transfer address = 5200H
  • 33 00 91 = Triad pointer: NRN=33H 00H, RBO=91H → member starts at byte offset (51×256) + 0x91 = 13056 + 145 = 13201 (0x3391)
0161-0166PURGE08 06 72 00 52
35 00 26
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 72 = ISAM # 72H
  • 00 52 = Transfer address = 5200H
  • 35 00 26 = Triad pointer: NRN=35H 00H, RBO=26H → member starts at byte offset (53×256) + 0x26 = 13568 + 38 = 13606 (0x3526)
0167-016CXFER08 06 73 00 52
37 00 81
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 73 = ISAM # 73H
  • 00 52 = Transfer address = 5200H
  • 37 00 81 = Triad pointer: NRN=37H 00H, RBO=81H → member starts at byte offset (55×256) + 0x81 = 14080 + 129 = 14209 (0x3781)
016D-0172LOAD08 06 81 00 52
3A 00 5E
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 81 = ISAM # 81H
  • 00 52 = Transfer address = 5200H
  • 3A 00 5E = Triad pointer: NRN=3AH 00H, RBO=5EH → member starts at byte offset (58×256) + 0x5E = 14848 + 94 = 14942 (0x3A5E)
0173-0178RUN08 06 82 0D 52
3A 00 5E
  • 08 = ISAM Directory Entry (Type 08H)
  • 06 = Length (6-byte data area)
  • 82 = ISAM # 82H
  • 0D 52 = Transfer address = 520DH
  • 3A 00 5E = Triad pointer: NRN=3AH 00H, RBO=5EH → member starts at byte offset (58×256) + 0x5E = 14848 + 94 = 14942 (0x3A5E)
Offset RangeHex BytesExplanation
01C3-01C501 00 04
  • 01 = Object code / Load block (Type 01H)
  • 00 = Special length encoding = 254 data bytes follow the 2-byte load address
  • 04 = Low byte of the load address
01C6-01C701 00
  • 01 00 = Load address = 0001H (little-endian). The 254 data bytes would load starting at address 0001H.
01C8-01C904 01
  • 04 = End of partitioned data set member (Type 04H) — guard record
  • 01 = Length = 01H
01CA00
  • 00 = Data byte for the Type 04H record

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.

5200
LD C,00H 0E 00
Load Register C with 00H, selecting drive 0 as the source disk number. This drive selection is passed to the directory I/O setup routine at 4B65H, which uses C to build the track/sector address for reading the directory.
5202
GOSUB to SYS0 routine at 4B65H to set up track and sector registers for directory I/O on the drive specified by Register C (drive 0). This call initialises the FDC track/sector state so the following CALL 4B45H will read the correct sector.
5205
If the NZ FLAG (Not Zero) has been set by the CALL 4B65H at 5202H, indicating a directory setup error, JUMP to 521CH to perform the error exit. A non-zero result means the drive could not be selected or the track/sector calculation failed.
5208
LD D,42H 16 42
Alternate Entry Point for SYSTEM - ISAM A2H
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.
520A
LD E,0E0H 1E E0
Load Register E with 0E0H (224 decimal), forming the low byte of the buffer offset address. DE now holds 42E0H, which is the address within the 256-byte sector buffer at 4200H where the first actual directory file entry begins (224 bytes past the start of the buffer, past the GAT bitmap and disk-type fields at offsets 00H-CCH and the disk signature at offset CEH).
520C
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H (32 decimal). This is the byte count for the LDIR block copy that follows. 32 bytes will be transferred from the directory sector buffer entry at 42E0H to the boot command buffer at 4318H, overwriting the existing boot command line with the new AUTO command string.
520F
LDIR ED B0
Block copy 32 bytes (BC=0020H) from DE (42E0H, the first directory file entry in the sector buffer) to HL (4318H, the DOS command input buffer). LDIR copies byte-by-byte, incrementing both DE and HL after each byte and decrementing BC, stopping when BC reaches zero. This overwrites the first 32 bytes of the command buffer with the new auto-boot command string read from the directory sector.

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.

5211
LD C,00H 0E 00
Load Register C with 00H, again selecting drive 0 as the target for the verify read. This reloads C because LDIR consumed the register pair BC for the byte counter; C must be re-established as the drive number before the next directory I/O setup call.
5213
GOSUB to the local verify subroutine at 5234H to write the sector from the buffer at 4200H back to the disk and then read it back to confirm the write succeeded. Register C (00H = drive 0) passes the drive number. The routine returns Z if the write-verify passed, or NZ with Register A holding an error code if it failed.
5216
If the NZ FLAG (Not Zero) has been set by the verify subroutine at 5234H, indicating the write-verify failed (data written does not match data read back, or the sector could not be written), JUMP to 521CH to perform the error exit.
5219
JUMP to SYS0 at 402DH (DOS READY / No-Error Exit). The AUTO command completed successfully: the boot command line has been written to the disk and the write was verified. Control returns to the DOS command loop to display the VTOS READY prompt.

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.

521C
OR A,40H F6 40
OR Register A with 40H, setting bit 6 of the error code. Bit 6 of the error code instructs the SYS4 error display handler to suppress extended error context (file name display and FCB details). This is appropriate here because the AUTO command is operating on disk structures directly, not on a user-level open file, so there is no meaningful FCB to display as context.
521E
JUMP to SYS0 at 4409H (DOS Error Exit). Register A holds the error code with bit 6 set (context suppressed). The error handler in SYS4 will display the error message and return to the DOS command loop.

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.

5221
PUSH DE D5
Save Register Pair DE onto the stack. DE contains the caller's data pointer (the sector buffer address 42E0H) and must be preserved across the subroutine calls to 4B65H and 4B45H which will modify DE internally.
5222
PUSH HL E5
Save Register Pair HL onto the stack. HL contains the caller's destination pointer (4318H, the command buffer) and must be preserved across the subroutine calls.
5223
GOSUB to SYS0 routine at 4B65H to set up track and sector registers for directory I/O on the drive specified by Register C (00H = drive 0, passed by the caller). This initialises the FDC state for reading the directory sector.
5226
LD E,00H 1E 00
Load Register E with 00H, specifying sector 0 (the GAT sector) within the directory track. Together with HL = 4200H set at 5228H, this directs the read to load the GAT/directory sector 0 into the sector buffer at 4200H.
5228
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H, the base of the 256-byte sector buffer used for all directory and GAT I/O. This is the destination address for the sector read that follows.
522B
GOSUB to SYS0 routine at 4B45H to read one sector from the disk into the buffer. Register E (00H = sector 0) selects which sector to read; HL (4200H) is the destination buffer. Returns Z on success, NZ with an error code in Register A on failure.
522E
POP HL E1
Restore Register Pair HL from the stack, recovering the caller's destination pointer (4318H, the DOS command buffer) that was saved at 5222H.
522F
POP DE D1
Restore Register Pair DE from the stack, recovering the caller's sector buffer data pointer (42E0H) that was saved at 5221H.
5230
RET Z C8
If the Z FLAG (Zero) has been set by the CALL 4B45H at 522BH, indicating the sector read completed without error, RETURN to the caller at 5205H. The sector buffer at 4200H now contains the directory data. Register A is not used as a return value on the success path.
5231
LD A,14H 3E 14
Load Register A with 14H. Error code 14H is the VTOS "READ ERROR" / I/O fault code. This value is loaded only when RET Z at 5230H was not taken, meaning the sector read failed.
5233
RET C9
RETURN to the caller at 5205H with Register A = 14H (read error) and the NZ flag set (the Z flag was clear when RET Z at 5230H was not taken). The caller's JP NZ at 5205H will then redirect to the error exit at 521CH.

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.

5234
PUSH DE D5
Save Register Pair DE onto the stack. DE holds 42E0H (the sector buffer data pointer) and must be preserved across the calls to 4B65H, 4768H, and 4772H.
5235
PUSH HL E5
Save Register Pair HL onto the stack. HL holds 4318H (the command buffer pointer) and must be preserved across the write-verify subroutine calls.
5236
GOSUB to SYS0 routine at 4B65H to set up track and sector registers for directory I/O on the drive specified by Register C (00H = drive 0). This is identical to the setup call at 5223H in the read subroutine; the same directory track/sector must be targeted for the write.
5239
LD E,00H 1E 00
Load Register E with 00H, selecting sector 0 as the target sector for the write, matching the sector that was read into 4200H by the read subroutine at 5221H.
523B
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H, the base of the sector buffer holding the data to be written. The buffer at 4200H contains the directory sector with the newly-written AUTO command string in the first directory entry area.
523E
GOSUB to SYS0 routine at 4768H to write one sector to the disk. HL (4200H) is the source buffer; E (00H) is the sector number within the directory track already established by CALL 4B65H. Returns Z on success, NZ on write failure.
5241
If the NZ FLAG (Not Zero) has been set by the CALL 4768H at 523EH, indicating the sector write failed, JUMP FORWARD to 5248H (the error exit within this subroutine). There is no point attempting the read-after-write verify if the write itself did not complete.
5243
GOSUB to SYS0 routine at 4772H to perform a read-after-write verify: the sector just written at 523EH is read back from the disk and compared against the data in the buffer. If the data matches, Z is set and A is not used as an error value. If the data does not match (indicating a write fault), the routine sets the NZ flag and loads A with an error code.
5246
CP A,06H FE 06
Compare Register A against 06H. After a successful write-verify, Register A holds the FDC status byte read back from the WD1771. Error code 06H in VTOS represents a "WRITE FAULT" or verify mismatch. This compare sets the Z flag if the verify result code equals 06H (fault detected), which the caller's JP NZ at 5216H tests to decide whether to continue or error-exit.

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.

5248
LD A,15H 3E 15
Load Register A with 15H. Error code 15H is the VTOS "WRITE ERROR" / write fault code. This instruction is on the error path reached via JR NZ at 5241H when the sector write at 4768H failed outright. On the success path this instruction is also executed but its value is overwritten or ignored because the subroutine's return flags (set by CP at 5246H) determine which path the caller takes at JP NZ,521CH.
524A
POP HL E1
Restore Register Pair HL from the stack, recovering the caller's command buffer pointer (4318H) that was saved at 5235H.
524B
POP DE D1
Restore Register Pair DE from the stack, recovering the caller's sector buffer data pointer (42E0H) that was saved at 5234H.
524C
RET C9
RETURN to the caller at 5216H. The Z/NZ flag state reflects the outcome of the write-verify operation: Z = success (write verified, no fault), NZ = failure (write error or verify mismatch, with error code in Register A). The caller's JP NZ,521CH at 5216H uses this flag to choose between the success path (JP 402DH at 5219H) and the error exit (OR A,40H / JP 4409H at 521CH-521EH).

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.

5200
LD C,2FH 0E 2F
Load Register C with 2FH (ASCII /). This is the field separator character for the DATE command. The shared decimal parser at 525AH will look for this character between each numeric field in the input string.
5202
GOSUB to the 3-field decimal parser at 525AH. On entry, C=2FH (separator character) and HL points to the user's command line input. The parser reads up to three decimal numbers separated by / characters and stores them in reverse order at 524EH (first field), 524DH (second field), 524CH (third field). On return: Z FLAG set = all three fields parsed successfully; NZ FLAG set = parse error (missing separator, non-digit character, or incomplete input).
5205
If the NZ FLAG (Not Zero) has been set because the date string failed to parse (bad format, missing fields, or non-numeric characters), JUMP to 5243H to display the "BAD FORMAT" error message and exit.
5208
LD HL,524CH 21 4C 52
Point Register Pair HL to 524CH, the start of the 3-byte parser work buffer. The parser stored its results in reverse order: 524CH holds the third parsed field (year), 524DH holds the second parsed field (day), 524EH holds the first parsed field (month).
520B
LD DE,4044H 11 44 40
Point Register Pair DE to 4044H, the system date variable area. This is the destination for the LDIR block copy that follows. The 3 bytes at 4044H-4046H hold the current system date used by SYS3 for directory entry date-stamping.
520E
LD BC,0003H 01 03 00
Load Register Pair BC with 0003H (3 bytes). This is the byte count for the LDIR block copy.
5211
LDIR ED B0
Block Copy
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.

5213
LD DE,4044H 11 44 40
Point Register Pair DE to 4044H to begin reading back the stored date values for encoding into the packed directory date format.
5216
LD A,(DE) 1A
Fetch the first date byte from 4044H into Register A. This is the third-entered date field (stored last by the reverse-order parser).
5217
SUB A,50H D6 50
SUBtract 50H (80 decimal) from Register A. For year entries 80-99, this produces offsets 0-19 (00H-13H). For year entries 00-11 (representing 2000-2011), the 8-bit unsigned result wraps to B0H-BBH. The lower 5 bits of this value encode the year offset within the 32-year range 1980-2011.
5219
LD B,A 47
Save the adjusted year offset in Register B for combining with the next field.
521A
INC DE 13
INCrement Register Pair DE to point to 4045H, the second date byte.
521B
LD A,(DE) 1A
Fetch the second date byte from 4045H into Register A. This is the second-entered date field.
521C
RLCA 07
Rotate Register A Left Circular (bit 7 wraps to bit 0 and Carry). First of three rotations to shift the value left by 3 bit positions (equivalent to multiplying by 8).
521D
RLCA 07
Rotate Register A Left Circular. Second rotation.
521E
RLCA 07
Rotate Register A Left Circular. Third rotation. The original value has now been shifted left by 3 bit positions, placing it in the upper bits of Register A.
521F
OR A,B B0
OR the shifted second field with the adjusted first field (year offset in Register B). This combines both values into a single packed byte where the upper bits hold the shifted second field and the lower bits hold the year offset.
5220
LD (4307H),A 32 07 43
Store the packed date byte to the ATTRIB attribute byte at 4307H. This byte combines the shifted second date field in the upper bits with the adjusted year offset in the lower bits.
5223
INC DE 13
INCrement Register Pair DE to point to 4046H, the third date byte.
5224
LD A,(DE) 1A
Fetch the third date byte from 4046H into Register A. This is the first-entered date field.
5225
OR A,50H F6 50
OR Register A with 50H. This sets the 50H-base flag on the third date field for internal storage format. The continuation file notes that day values use an internal format starting at 50H (SYS3: SUB 50H at 4E74H reverses this encoding during date extraction).
5227
LD (4306H),A 32 06 43
Store the 50H-based third date field to the ATTRIB attribute byte at 4306H.
522A
JUMP to 402DH in SYS0, the DOS READY / No-Error Exit. The date has been successfully parsed, stored to the system date variables, and packed into the directory date attribute bytes.

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.

522D
LD C,3AH 0E 3A
Load Register C with 3AH (ASCII :). This is the field separator character for the TIME command. The shared decimal parser at 525AH will look for this character between each numeric field.
522F
GOSUB to the 3-field decimal parser at 525AH. On entry, C=3AH (separator character) and HL points to the user's command line input. The parser reads up to three decimal numbers separated by : characters: hours (first), minutes (second), seconds (third). Results are stored in reverse order at 524EH, 524DH, 524CH. On return: Z FLAG set = success; NZ FLAG set = parse error.
5232
If the NZ FLAG (Not Zero) has been set because the time string failed to parse, JUMP to 5243H to display the "BAD FORMAT" error message and exit.
5235
LD HL,524CH 21 4C 52
Point Register Pair HL to 524CH, the start of the 3-byte parser work buffer. The parser stored: 524CH = seconds (third entered, stored last), 524DH = minutes (second entered), 524EH = hours (first entered).
5238
LD DE,4041H 11 41 40
Point Register Pair DE to 4041H, the system clock variables. The 3 bytes at 4041H-4043H hold seconds (4041H), minutes (4042H), and hours (4043H) as maintained by the SYS0 interrupt service routine.
523B
LD BC,0003H 01 03 00
Load Register Pair BC with 0003H (3 bytes). This is the byte count for the LDIR block copy.
523E
LDIR ED B0
Block Copy
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.
5240
JUMP to 402DH in SYS0, the DOS READY / No-Error Exit. The time has been successfully parsed and stored to the system clock variables.

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.

5243
LD HL,524FH 21 4F 52
Point Register Pair HL to 524FH, the address of the "BAD FORMAT" error message string (terminated with 0DH).
5246
GOSUB to the SYS0 display routine at 447BH to display the error message string pointed to by HL with CONFIG/SYS processing.
5249
JUMP to 4030H in SYS0, the error-already-displayed exit. The error message has already been shown, so this exit path does not invoke SYS4's error display system.

524CH - Parser Work BufferString

Three-byte work buffer used by the decimal parser at 525AH to store parsed numeric values in reverse order

524C-524E
DEFB 00H,00H,00H xx xx xx
Parser Work Buffer
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.

524F-5259
DEFM "BAD FORMAT",0DH 42 41 44 20 46 4F 52 4D 41 54 0D
Error Message String
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.

525A
LD DE,524EH 11 4E 52
Point Register Pair DE to 524EH, the top of the 3-byte parser work buffer. Values will be stored here and decremented downward (524EH, 524DH, 524CH) so that the first-parsed value ends up at the highest address.
525D
LD B,03H 06 03
Load Register B with 03H (3 fields to parse). This counter tracks how many numeric fields remain.

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.

525F
PUSH DE D5
Save Register Pair DE (current store pointer) onto the stack. The CALL to 526FH may modify DE internally.
5260
GOSUB to the two-digit decimal parser at 526FH. On entry, HL points to the next character in the user's input string. On return: Register A = parsed decimal value (0-99), Z FLAG set = valid number parsed, NZ FLAG set = parse error. HL is advanced past the consumed digit characters.
5263
POP DE D1
Restore Register Pair DE (current store pointer) from the stack.
5264
RET NZ C0
If the NZ FLAG (Not Zero) has been set because the decimal parser encountered an invalid character, RETURN to the caller with NZ set to signal a parse failure.
5265
LD (DE),A 12
Store the parsed decimal value (Register A) to the current buffer position pointed to by DE.
5266
DEC DE 1B
DECrement Register Pair DE by 1 to move the store pointer down to the next buffer position (from 524EH to 524DH, then to 524CH).
5267
DEC B 05
DECrement Register B (field counter) by 1.
5268
RET Z C8
If the Z FLAG (Zero) has been set because all 3 fields have been parsed (B has reached zero), RETURN to the caller with Z set to signal success. All three values are now stored in the work buffer at 524CH-524EH.
5269
LD A,(HL) 7E
Fetch the next character from the user's input string (pointed to by HL) into Register A. This should be the separator character (/ for DATE, : for TIME) between numeric fields.
526A
INC HL 23
INCrement Register Pair HL to advance the input pointer past the separator character.
526B
CP A,C B9
Compare Register A against Register C (the expected separator character: 2FH=/ for DATE, 3AH=: for TIME). If Register A equals C, the Z FLAG is set; otherwise the NZ FLAG is set.
526C
If the Z FLAG (Zero) has been set because the separator matched, LOOP BACK to 525FH to parse the next numeric field.
526E
RET C9
RETURN to the caller with NZ set (from the failed CP comparison). The separator character did not match, indicating a format error in the input string.
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.

526F
GOSUB to the single-digit validator at 5286H. Reads one character from (HL), increments HL, subtracts 30H (ASCII 0) to convert from ASCII to binary, then tests if the result is a valid digit (0-9). On return: CARRY FLAG set = valid digit (value 0-9 in Register A), NO CARRY FLAG set = not a digit (parse terminates).
5272
JR NC,5284H 30 10
If the NO CARRY FLAG has been set because the character was not a valid digit, JUMP to 5284H to set NZ (error) and return. A valid first digit is required.

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.

5274
LD E,A 5F
Save the first digit value in Register E for the multiply-by-10 calculation.
5275
RLCA 07
Rotate Register A Left Circular. The first digit is now multiplied by 2.
5276
RLCA 07
Rotate Register A Left Circular again. The first digit is now multiplied by 4.
5277
ADD A,E 83
ADD the original first digit (Register E) to the x4 value in Register A. Result: first digit multiplied by 5.
5278
RLCA 07
Rotate Register A Left Circular. The first digit is now multiplied by 10. Register A holds the tens contribution to the final value.
5279
LD E,A 5F
Save the tens value (first digit x 10) in Register E.
527A
GOSUB to the single-digit validator at 5286H to read and validate the second (units) digit. On return: CARRY FLAG set = valid digit in A, NO CARRY FLAG = not a digit.
527D
JR NC,5284H 30 05
If the NO CARRY FLAG has been set because the second character was not a digit, JUMP to 5284H. This means the number was a single digit only; 5284H will set NZ and return. The tens value in Register E is discarded.
527F
ADD A,E 83
ADD the units digit (Register A) to the tens value (Register E). Register A now contains the complete two-digit decimal value (tens x 10 + units).
5280
LD E,A 5F
Save the final result in Register E.
5281
XOR A,A AF
Set Register A to ZERO and clear all flags. This sets the Z FLAG to indicate success.
5282
LD A,E 7B
Load Register A with the final parsed value from Register E. The preceding XOR set the Z FLAG; this LD does not affect flags, so Z remains set to signal a successful parse.
5283
RET C9
RETURN to the caller. Register A = parsed decimal value (0-99), Z FLAG set = success.
5284
OR A,A B7
OR Register A with itself. Since A is non-zero (it holds the invalid character minus 30H, or the tens value), this sets the NZ FLAG to signal a parse failure without modifying the value.
5285
RET C9
RETURN to the caller with NZ set, indicating the input was not a valid 2-digit decimal number.

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.

5286
LD A,(HL) 7E
Fetch the next character from the input string pointed to by HL into Register A.
5287
INC HL 23
INCrement Register Pair HL to advance the input pointer to the next character.
5288
SUB A,30H D6 30
SUBtract 30H (ASCII 0) from Register A. This converts an ASCII digit character (0-9, codes 30H-39H) to its binary value (0-9). Non-digit characters will produce values outside the 0-9 range.
528A
CP A,0AH FE 0A
Compare Register A against 0AH (10 decimal). If Register A < 0AH (value 0-9, a valid digit), the CARRY FLAG is set. If Register A >= 0AH (not a valid digit), the NO CARRY FLAG is set.
528C
RET C9
RETURN to the caller. CARRY FLAG set = valid digit (binary value 0-9 in Register A). NO CARRY FLAG = not a digit (Register A holds the invalid result).

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

5200
LD DE,52A3H 11 A3 52
Point 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.
5203
GOSUB 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.
5206
If 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.
5208
LD DE,52C3H 11 C3 52
Point 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.
520B
GOSUB 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.).
520E
If 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.

5210
LD DE,52A3H 11 A3 52
Point Register Pair DE to 52A3H, the primary FCB, for the record read operation.
5213
LD HL,52D4H 21 D4 52
Point Register Pair HL to 52D4H, the 256-byte sector buffer used for file I/O. Data read from the file will be placed here.
5216
LD B,00H 06 00
Load Register B with 00H (read mode byte). This specifies a standard sequential read operation.
5218
GOSUB 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.
521B
If the NZ FLAG (Not Zero) has been set because the record read failed, JUMP to the error exit at 5269H.
521D
LD BC,0000H 01 00 00
Self-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.
5220
LD A,B 78
Load Register A with the high byte of the sector count (Register B).
5221
OR A,C B1
OR Register A with Register C (low byte of sector count). If BC=0000H, the result is zero and the Z FLAG is set.
5222
If 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.

5224
LD HL,(52AFH) 2A AF 52
Fetch 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.
5227
PUSH HL E5
Save the original total sector count (HL) onto the stack for later restoration.
5228
SLA C CB 21
Shift 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.
522A
RL B CB 10
Rotate Register B Left through Carry (propagate the carry from SLA C into the high byte). BC is now multiplied by 2.
522C
SLA C CB 21
Shift Register C Left Arithmetic again (second multiply by 2). BC is now multiplied by 4 from its original value.
522E
RL B CB 10
Rotate Register B Left through Carry. BC now holds the original value multiplied by 4.
5230
DEC BC 0B
DECrement 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.
5231
GOSUB 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.
5234
LD HL,52D4H 21 D4 52
Point Register Pair HL to 52D4H, the 256-byte sector buffer.
5237
LD DE,52D5H 11 D5 52
Point 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.
523A
LD BC,00FFH 01 FF 00
Load Register Pair BC with 00FFH (255 bytes). Combined with the single byte written at (HL), this fills all 256 bytes of the buffer.
523D
LD (HL),0E5H 36 E5
Store 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.
523F
LDIR ED B0
Block 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.
5241
LD DE,52A3H 11 A3 52
Point Register Pair DE to 52A3H, the primary FCB, for the write record operation.
5244
GOSUB 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.
5247
POP HL E1
Restore the original total sector count from the stack into Register Pair HL.
5248
LD (52AFH),HL 22 AF 52
Store 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.
524B
PUSH AF F5
Save Register AF (including the status flags from the write operation) onto the stack.
524C
GOSUB to the SYS0 routine at 443FH to flush the file (close-and-reopen). This ensures the E5H-overwritten sector is committed to disk.
524F
GOSUB 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.
5252
POP AF F1
Restore 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.

5253
PUSH AF F5
Save Register AF (current status flags) onto the stack. The flags will be tested after the directory update to determine the final exit path.
5254
LD BC,(52A9H) ED 4B A9 52
Fetch 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.
5258
GOSUB 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.
525B
If the NZ FLAG (Not Zero) has been set because directory entry validation failed, JUMP to the error exit at 5269H.
525D
INC HL 23
INCrement Register Pair HL to advance from the directory entry status byte (offset +00H) to the flags/attribute byte (offset +01H).
525E
SET 7,(HL) CB FE
SET 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.
5260
GOSUB to the SYS0 directory entry write/update routine at 4B1FH. This writes the modified directory sector (with the kill flag set) back to disk.
5263
If the NZ FLAG (Not Zero) has been set because the directory write failed, JUMP to the error exit at 5269H.
5265
POP AF F1
Restore Register AF from the stack. The Z FLAG reflects the overall success/failure status of the kill operation.
5266
If 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.
5269
OR A,40H F6 40
OR 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).
526B
JUMP 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

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.

526E
LD HL,5277H 21 77 52
Point Register Pair HL to 5277H, the address of the "FILE SPEC REQUIRED" error message string.
5271
GOSUB to the SYS0 display routine at 447BH to display the error message string with CONFIG/SYS processing.
5274
JUMP to 4030H in SYS0, the error-already-displayed exit.

5277H - "FILE SPEC REQUIRED" Message String

ASCII error message displayed when no filespec is provided on the KILL command line.

5277-5289
DEFM "FILE SPEC REQUIRED",0DH 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
Error Message String
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.

528A
LD HL,5293H 21 93 52
Point Register Pair HL to 5293H, the address of the "PARAMETER ERROR" error message string.
528D
GOSUB to the SYS0 display routine at 447BH to display the error message string with CONFIG/SYS processing.
5290
JUMP to 4030H in SYS0, the error-already-displayed exit.

5293H - "PARAMETER ERROR" Message String

ASCII error message displayed when the file open operation fails.

5293-52A2
DEFM "PARAMETER ERROR",0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
Error Message String
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.

52A3-52C2
DEFS 32 (32 bytes)
Primary FCB Work Area
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.
52C3-52D3
DEFS 17 53 49 5A 45 20 20 1E 52 53 20 20 20 20 1E 52 00
Secondary Filespec Template
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).
52D4-53D3
DEFS 256 (256 bytes)
Sector Buffer
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

5200
LD DE,5239H 11 39 52
Point Register Pair DE to 5239H - the filespec/FCB template area.
5203
GOSUB to SYS0 routine at 441CH to extract the filespec from the command line into the FCB at DE (5239H). If no filespec is present, the NZ FLAG will be set on return.
5206
If the NZ FLAG (Not Zero) has been set (no filespec was found on the command line), JUMP to 521DH to display the "FILE SPEC REQUIRED" error message.
5209
GOSUB to SYS0 routine at 4424H to perform the basic file open using the FCB at DE. If the open fails, the NZ FLAG will be set on return.
520C
If the NZ FLAG (Not Zero) has been set (file open failed), JUMP to 5218H to handle the error exit.
520F
GOSUB to SYS0 routine at 442CH to update the GAT sector and close the file. If the operation fails, the NZ FLAG will be set on return.
5212
If the NZ FLAG (Not Zero) has been set (GAT read/close failed), JUMP to 5218H to handle the error exit.
5215
All operations succeeded. JUMP to SYS0 DOS READY exit at 402DH to return control to the operating system with no error.

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.

5218
OR A,40H F6 40
OR Register A with 40H to set bit 6 of the error code, adjusting the error value returned by the failed SYS0 call into the range expected by the DOS error handler.
521A
JUMP to SYS0 DOS Error Exit at 4409H to display the error code in Register A and return to the DOS prompt.

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.

521D
LD HL,5226H 21 26 52
Point Register Pair HL to 5226H - the address of the inline error message "FILE SPEC REQUIRED" + 0DH.
5220
GOSUB to SYS0 routine at 447BH to display the message pointed to by HL ("FILE SPEC REQUIRED") with CONFIG/SYS processing.
5223
JUMP to SYS0 error-already-displayed exit at 4030H. The message has already been shown; return to the DOS prompt without printing another error.

Error Message
The bytes from 5226H to 5238H are a text string "FILE SPEC REQUIRED" terminated by 0DH (carriage return).

5226
DEFM "FILE SPEC REQUIRED" 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
Inline message data: ASCII string FILE SPEC REQUIRED followed by a carriage return (0DH). Displayed by the CALL 447BH at 5220H when no filespec is found on the command line.

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

5200
LD HL,4EADH 21 AD 4E
Point Register Pair HL to 4EADH - the start of the command name table. Each entry in this table is 9 bytes: 6 bytes of space-padded command name, 2 skip bytes, and 1 termination flag byte. The table holds all 36 VTOS library command names (ALLOC through XFER) in the order they appear on screen.
5203
LD C,04H 0E 04
Load Register C with 04H. Register C is the column counter - exactly four command names are displayed per screen row before a carriage return is issued and the counter is reset.
5205
LD A,0C2H 3E C2
Load Register A with 0C2H - the TRS-80 indent/prefix display character. When output via ROM routine 0033H, this produces the leading whitespace that indents each command name from the left margin, creating the two-space indent visible in the LIB output.
5207
GOSUB to ROM character output routine at 0033H to display the C2H indent character at the current cursor position.

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.

520A
LD B,06H 06 06
Load Register B with 06H. Register B is the character counter for the inner loop - all six bytes of the current command name entry will be displayed (e.g., "ALLOC ", "APPEND", "MEMORY").
520C
LD A,(HL) 7E
Fetch the next character byte of the command name from the table at the address in Register Pair HL into Register A.
520D
INC HL 23
INCrement Register Pair HL by 1 to advance to the next character in the command name entry.
520E
GOSUB to ROM character output routine at 0033H to display the command name character in Register A at the current cursor position.
5211
DECrement B and LOOP BACK to 520CH if B is not zero. Repeats until all six characters of the current command name have been displayed.

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.

5213
LD A,0C8H 3E C8
Load Register A with 0C8H - the TRS-80 column-spacing display character. When output via ROM routine 0033H, this produces the whitespace padding between command names that creates the aligned four-column tabular layout seen in the LIB output.
5215
GOSUB to ROM character output routine at 0033H to display the C8H column-spacing character, padding the output to the next column position.
5218
INC HL 23
INCrement Register Pair HL by 1 to skip the first of two non-display bytes that follow the 6-byte command name in each table entry.
5219
INC HL 23
INCrement Register Pair HL by 1 to skip the second non-display byte. HL now points to the termination flag byte for this table entry.
521A
LD A,(HL) 7E
Fetch the termination flag byte from the current table entry at (HL) into Register A. A value of 00H indicates this is the final command name entry in the table; any non-zero value indicates more entries follow.
521B
OR A,A B7
Test Register A for zero by ORing it with itself. If Register A equals 00H (last entry reached), the Z FLAG is set; otherwise the NZ FLAG is set (more command names remain).
521C
If the Z FLAG (Zero) has been set (termination flag is 00H, all command names have been displayed), JUMP to 5228H to issue the final carriage return and exit cleanly to the DOS prompt.
521E
DEC C 0D
DECrement Register C by 1. Register C tracks how many command names remain to be displayed on the current screen row. When C reaches zero all four columns of the current row are complete.
521F
If the NZ FLAG (Not Zero) has been set (fewer than four names have been displayed on the current row), LOOP BACK to 520AH to display the next command name in the current row.

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.

5221
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return character) to terminate the current row of four command names and move the cursor to the next screen line.
5223
GOSUB to ROM character output routine at 0033H to display the carriage return, advancing the cursor to the start of the next line.
5226
LOOP BACK to 5203H to reset the column counter to 04H and begin displaying the next row of four command names from the current HL position in the table.

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.

5228
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return character) to issue a final newline after the last row of command names.
522A
GOSUB to ROM character output routine at 0033H to display the final carriage return.
522D
JUMP to SYS0 DOS READY exit at 402DH to return control to the operating system with no error, leaving the LIB command output on screen.

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

AddressName and Purpose
5200HDEBUG 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).
5241HCLOCK 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.
5257HTRACE 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.
5267HVERIFY 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).
5276HDEBUG 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.
527BHCommon 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.
527EHCommon 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.
5299HParameter 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.

5200
LD BC,0000H 01 00 00
Load Register pair BC with the value 0000H. This clears BC to serve as a zero initializer for the two self-modifying flag bytes that follow. The value 0000H means "debug not active / no flag argument".
5203
Self-Modifying Code
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.
5207
GOSUB to the DEBUG parameter parser at 5276H. This subroutine parses the optional command-line argument (ON, OFF, YES, NO, EXT, or absent) by calling the SYS0 routine at 4476H. Returns with Z flag set if the argument was ON/YES, NZ if OFF/NO, or the carry condition from an absent argument.
520A
LD HL,430FH 21 0F 43
Load Register pair HL with the address 430FH, pointing to the System State Flags register in the VTOS resident DOS. This is the critical one-byte register whose bit 7 (80H) controls whether errors and BREAK key events are redirected to the DEBUG monitor at 400FH.
520D
DI F3
Disable Interrupts. The instructions that follow patch critical interrupt-handling locations in the resident DOS (4315H, 4316H-4317H). Disabling interrupts ensures that the ISR cannot fire between the individual byte writes and observe a partially-patched state.
520E
If the Z FLAG (Zero) is set - meaning the parser returned indicating the argument was OFF/NO, or the toggle logic determined that DEBUG should be disabled - JUMP to 522CH to execute the disable path. If NZ (argument was ON/YES or absent with current state favoring enable), fall through to the enable sequence.

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.

5210
LD A,(523CH) 3A 3C 52
Fetch the current value of the debug-enable flag at 523CH into Register A. This byte was cleared to 00H at 5203H and may have been set to a non-zero value by the parameter parser if it detected an active-debug condition. Used here to verify the flag state before committing the enable patch.
5213
OR A,A B7
OR Register A with itself. This sets or clears the Z flag based on the current value of Register A (the debug-enable flag from 523CH) without changing its value. If A is zero (no enable flag set), Z is set. If A is non-zero, NZ is set. The result of this test feeds into the conditional CALL at 5215H.
5214
PUSH HL E5
Save Register pair HL (currently pointing to 430FH, the System State Flags register) onto the stack. This preserves the pointer across the conditional CALL at 5215H so it can be used afterwards to set bit 7 of 430FH.
5215
If the NZ FLAG (Not Zero) is set - meaning the debug-enable flag at 523CH was non-zero, indicating the EXT (extended debugger) option was requested - GOSUB to 523EH to issue SVC 8BH, loading the extended DEBUG facility into high memory via SYS7. If Z (standard enable, no EXT), skip this call.
5218
POP HL E1
Restore Register pair HL from the stack. HL is restored to 430FH, the address of the System State Flags register in the resident DOS, ready for the SET instruction below.
5219
SET 7,(HL) CB FE
Self-Modifying Code
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.
521B
LD HL,400FH 21 0F 40
Load Register pair HL with the address 400FH. This is the RST 30H hook address in the VTOS resident DOS - the DEBUG monitor entry vector. HL will be stored into 4316H-4317H in the next sequence to serve as the BREAK-key jump target.
521E
LD A,0C3H 3E C3
Load Register A with the value C3H. C3H is the Z80 machine code for the JP (unconditional jump) instruction. This byte will be written to 4315H to arm the BREAK-key intercept pathway, replacing the C9H (RET) that normally disables it.
5220
Self-Modifying Code
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.
5223
Self-Modifying Code
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.
5226
LD A,0CDH 3E CD
Load Register A with the value CDH. CDH is the Z80 machine code for the CALL instruction. This byte is loaded but not immediately stored - it is positioned here for flow reasons before the EI and JP that close the enable path. (Note: this value is not written to any location in the enable path; it is superseded by the JP at 5229H which bypasses the disable path's LD (4315H),A at 5230H.)
5228
EI FB
Enable Interrupts. The critical patching of 4315H-4317H is now complete. It is safe to re-enable the interrupt system.
5229
JUMP to 402DH, the VTOS DOS READY / no-error exit point in the resident DOS. Control returns to the command interpreter with no error condition. The DEBUG monitor is now armed.

Disable Path: The following instructions disarm the DEBUG monitor, reversing the patches made by the enable path.

522C
RES 7,(HL) CB BE
Self-Modifying Code
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.
522E
LD A,0C9H 3E C9
Load Register A with the value C9H. C9H is the Z80 machine code for the RET (return) instruction. This byte will be written to 4315H to disarm the BREAK-key intercept. With C9H at 4315H, the ISR's BREAK-key path at 4561H will execute a harmless RET rather than jumping into the DEBUG monitor.
5230
Self-Modifying Code
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.
5233
LD A,3EH 3E 3E
Load Register A with the value 3EH. This value is loaded here but not used before the EI and JP below - it is a remnant of the shared code structure.
5235
LD HL,0EFA3H 21 A3 EF
Load Register pair HL with the value 0EFA3H. This value is also not directly used in the disable path exit sequence that follows - it is positioned as part of the shared code structure used by related commands in other members.
5238
EI FB
Enable Interrupts. The patching of 4315H and 430FH is complete. It is safe to re-enable the interrupt system.
5239
JUMP to 402DH, the VTOS DOS READY / no-error exit point. Control returns to the command interpreter with no error condition. The DEBUG monitor is now disarmed.
523C
NOP x2 00 00
Self-Modifying Code - Runtime Variable
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.

523E
LD A,8BH 3E 8B
Load Register A with the SVC code 8BH. This is the VTOS RST 28H service call code for SYS7 debug-enable - the function that loads the extended debugger into high memory.
5240
RST 28H EF
Execute RST 28H, the VTOS SVC (Supervisor Call) mechanism. The RST 28H vector at 400CH in the resident DOS routes to the overlay dispatcher, which reads Register A (8BH) as the SVC code. SVC 8BH invokes SYS7's extended debugger load path. The extended DEBUG facility is loaded into high memory, above X'5200', providing the full set of extended debugger commands (those marked with an asterisk in the VTOS DEBUG command reference). Returns normally after the extended debugger is resident.
5241
Note: Address 5241H is also the CLOCK command entry point (IDAM 17H). The byte at 5241H is CDH (CALL opcode), which is both the first byte of the CLOCK entry's CALL 527BH instruction and the first byte reached after RST 28H at 5240H returns. This dual use is deliberate: after the EXT loader returns, execution continues with the CLOCK entry's parameter parsing call. The CALL 527BH at this address is the start of the CLOCK command. See the CLOCK section below.

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.

5241
GOSUB to the common parameter parser at 527BH. This subroutine parses the optional ON/OFF/YES/NO argument from the command line by calling SYS0 at 4476H. Returns with Z flag set if the argument was ON/YES (enable), NZ if OFF/NO (disable), or the toggle state if no argument was given.
5244
LD DE,4DABH 11 AB 4D
Load Register pair DE with the address 4DABH, the device control block (DCB) address for the CLOCK function in the VTOS resident DOS area. This address is passed to the SYS0 enable/disable routines at 4413H and 4410H as the identifier of which system function to toggle.
5247
LD A,06H 3E 06
Load Register A with the value 06H. This is the function code or parameter value expected by the SYS0 enable/disable routines at 4413H/4410H for the CLOCK display function.
5249
If the Z FLAG is set - meaning the parameter parser returned indicating the flag argument was ON or YES (enable) - JUMP to 5251H to call the enable routine at 4413H. If NZ (argument was OFF/NO, i.e., disable), fall through to call the disable routine at 4410H.
524B
GOSUB to SYS0 routine at 4410H. This is the disable/error handler variant in the resident DOS. Called with DE=4DABH (CLOCK DCB) and A=06H. This routine turns off the real-time clock display.
524E
JUMP to 402DH, the VTOS DOS READY / no-error exit. The CLOCK display has been disabled. Returns control to the command interpreter.
5251
GOSUB to SYS0 routine at 4413H. This is the enable handler variant in the resident DOS. Called with DE=4DABH (CLOCK DCB) and A=06H. This routine turns on the real-time clock display, causing the current time to be shown continuously on the top line of the display.
5254
JUMP to 402DH, the VTOS DOS READY / no-error exit. The CLOCK display has been enabled. Returns control to the command interpreter.

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.

5257
GOSUB to the common parameter parser at 527BH. Parses the optional ON/OFF/YES/NO argument from the command line. Returns with Z set (enable) or NZ (disable).
525A
LD DE,4DDBH 11 DB 4D
Load Register pair DE with the address 4DDBH, the device control block (DCB) address for the TRACE function in the VTOS resident DOS area. This address is passed to the SYS0 enable/disable routines as the identifier of which function to toggle.
525D
LD A,0BH 3E 0B
Load Register A with the value 0BH. This is the function code expected by the SYS0 enable/disable routines at 4413H/4410H for the TRACE display function.
525F
If the Z FLAG is set (argument was ON/YES), LOOP BACK to 5251H (the CLOCK command's enable call at CALL 4413H). The TRACE enable path reuses the CLOCK command's enable sequence. DE now points to 4DDBH (TRACE DCB) and A=0BH, so the call to 4413H enables the TRACE display. If NZ (argument was OFF/NO), fall through to disable.
5261
GOSUB to SYS0 routine at 4410H. Called with DE=4DDBH (TRACE DCB) and A=0BH. This routine turns off the real-time program counter display.
5264
JUMP to 402DH, the VTOS DOS READY / no-error exit. The TRACE display has been disabled. Returns control to the command interpreter.

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.

5267
GOSUB to the common parameter parser at 527BH. Parses the optional ON/OFF/YES/NO argument from the command line. Returns with Z set (enable verification) or NZ (disable verification).
526A
LD HL,48C0H 21 C0 48
Load Register pair HL with the address 48C0H. This is the "enable verification" handler address in the VTOS resident DOS - the routine that performs read-after-write verification on every disk write. HL is provisionally loaded with the enable target, which will be overwritten if the disable path is taken.
526D
If the Z FLAG is set (argument was ON/YES, enable verification), JUMP to 5272H to store HL (48C0H) into the dispatch vector at 443AH. If NZ (argument was OFF/NO), fall through to load the disable address into HL.
526F
LD HL,48DBH 21 DB 48
Load Register pair HL with the address 48DBH, overwriting the 48C0H loaded at 526AH. This is the "disable verification" handler address in the VTOS resident DOS - the routine that performs a simple disk write without read-after-write verification. Reached only when NZ (argument was OFF/NO).
5272
Self-Modifying Code
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.
5275
RET C9
Return to the caller. For the VERIFY command, control returns to the SYS0 overlay dispatch framework (rather than using JP 402DH as the other commands do). The overlay dispatcher handles the return to the command interpreter.

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.

5276
LD DE,52B2H 11 B2 52
Load Register pair DE with the address 52B2H, pointing to the "EXT" option string table in the data area of this member. This string table contains the keyword "EXT" as well as ON/YES/NO option strings. DE is passed to the SYS0 routine at 447BH by the common parser body as the base address of the keyword list to scan.
5279
JUMP to 527EH, the common parameter parser body. Skips over the CLOCK/TRACE/VERIFY entry at 527BH-527DH which would set DE to a different option string.

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.

527B
LD DE,52C2H 11 C2 52
Load Register pair DE with the address 52C2H, pointing to the ON/NO option keyword table in the data area. This table contains the accepted ON/YES and OFF/NO flag keywords for CLOCK, TRACE, and VERIFY. DE is passed to the keyword-matching logic in the common parser body below.

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.

527E
LD BC,0000H 01 00 00
Load Register pair BC with 0000H. This value will be stored into both runtime flag variables (5290H and 523CH) to initialize them to "no flag / not active" before parsing the command-line argument.
5281
Self-Modifying Code - Runtime Variable
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.
5285
Self-Modifying Code
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.
5289
GOSUB to the SYS0 open-file / command-line argument extraction routine at 4476H. This routine reads the next token from the VTOS command line buffer and returns it for evaluation. On return, Z flag is set if a valid argument was found and matched, NZ if the argument did not match or was absent.
528C
If the NZ FLAG (Not Zero) is set - meaning the SYS0 argument extractor returned an error or the argument was invalid - JUMP to 5299H to display a "PARAMETER ERROR" message and exit via 4030H.
528F
LD BC,0000H 01 00 00
Self-Modifying Code - Runtime Variable
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).
5292
LD A,B 78
Load Register A with the value from Register B (the high byte of the parsed result in BC). This is the first step of testing whether BC is zero (no argument / toggle state) or non-zero (explicit ON/OFF argument parsed).
5293
OR A,C B1
OR Register A with Register C (the low byte of BC). After this instruction, Register A is zero if and only if both B and C were zero - i.e., no flag argument was present or parsed. If A is non-zero, an explicit ON/YES or OFF/NO keyword was matched.
5294
XOR A,0FFH EE FF
XOR Register A with 0FFH (bitwise complement). If A was 00H (no argument), it becomes 0FFH (non-zero / NZ, indicating toggle state). If A was non-zero (explicit argument), it becomes a modified non-zero value. This inversion ensures the Z/NZ flag returned by the parser has the correct sense for the calling commands' JR Z / JR NZ decisions.
5296
RET C9
Return to the calling command entry point (DEBUG at 520AH, CLOCK at 5244H, TRACE at 525AH, or VERIFY at 526AH). The Z flag reflects the ON/YES result (Z set) or OFF/NO/toggle result (NZ set) based on the parsed command-line argument.

Two NOP bytes at 5297H-5298H serve as padding between the parser return and the parameter error handler below.

5297
NOP x2 00 00
Two NOP (no-operation) padding bytes. These bytes are never executed in the normal flow of any command. They create a gap between the RET at 5296H and the parameter error display routine that begins at 5299H.

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.

5299
LD HL,52A2H 21 A2 52
Load Register pair HL with the address 52A2H, pointing to the "PARAMETER ERROR" ASCII string in the data area of this member. This string is displayed on the screen to inform the user that the argument provided to the command was not recognized.
529C
GOSUB to SYS0 routine at 447BH to display the string pointed to by HL (the "PARAMETER ERROR" message at 52A2H). The routine outputs each character until it reaches the 0DH (carriage return) terminator.
529F
JUMP to 4030H, the VTOS error-already-displayed exit. This path is used when an error message has already been printed to the screen and control must return to the command interpreter without triggering a second error display.

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.

52A2
DEFM "PARAMETER ERROR",0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
ASCII text string "PARAMETER ERROR" (16 bytes: P=50H, A=41H, R=52H, A=41H, M=4DH, E=45H, T=54H, E=45H, R=52H, space=20H, E=45H, R=52H, R=52H, O=4FH, R=52H) followed by carriage return 0DH as the display terminator. Pointed to by LD HL,52A2H at 5299H.

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.

52B2
DEFM "EXT <R" 45 58 54 20 20 20 3C 52
Option string entry beginning with "EXT" (45H 58H 54H) followed by spaces and the delimiter byte 3CH (<) and keyword-match code 52H. This encodes the EXT keyword recognized by the DEBUG command to load the extended debugger via SVC 8BH.
52BA
DEFM "RE <R" 52 45 20 20 20 20 3C 52
Option string entry for an alternate form (RE prefix, part of YES/NO/RYES/RNO keyword table). Encodes a keyword starting with "RE" followed by spaces and the delimiter 3CH.
52C2
DEFM "ON .R" 4F 4E 20 20 20 20 97 52
Option string entry for "ON" (4FH 4EH) followed by padding spaces, delimiter 97H (SUB A,A in disassembly - actually the option type byte indicating positive/enable match), and the record terminator 52H. This is also the base address pointed to by the CLOCK/TRACE/VERIFY parser entry at 527BH.
52CA
DEFM "OFF .R" 4F 46 46 20 20 20 97 52
Option string entry for "OFF" (4FH 46H 46H) followed by padding spaces, delimiter 97H (negative/disable match indicator), and the terminator byte. This encodes the OFF keyword for the disable path of CLOCK, TRACE, VERIFY, and DEBUG.
52D2
DEFM "YES .R" 59 45 53 20 20 20 97 52
Option string entry for "YES" (59H 45H 53H) followed by padding, the positive-match delimiter 97H, and terminator. Synonym for ON; causes the enable path to be taken.
52DA
DEFM "NO .R" 4E 4F 20 20 20 20 97 52
Option string entry for "NO" (4EH 4FH) followed by padding, the negative-match delimiter 97H, and terminator. Synonym for OFF; causes the disable path to be taken.
52E2
DEFM "RY .R" 52 59 20 20 20 20 97 52
Option string entry for "RY" - an abbreviated or alternate form of YES recognized by the parser. Contains the positive-match delimiter 97H.
52EA
DEFM "RN .R" 52 4E 20 20 20 20 97 52
Option string entry for "RN" - an abbreviated or alternate form of NO recognized by the parser. Contains the negative-match delimiter 97H.
52F2
NOP 00
Single NOP (00H) byte terminating the option string table. The keyword-matching routine at 447BH scans the table until it encounters a 00H byte, indicating the end of the keyword list.

ISAM 1DH - O! - Offset 04E2

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.

5200
LD HL,520EH 21 0E 52
Point HL to the encoded copyright string at 520EH. The first byte at 520EH is the decode key (90H); the encoded string follows immediately.
5203
LD B,(HL) 46
Load the decode key byte (90H) from 520EH into Register B. Every encoded character byte has 90H added to it; subtracting B recovers the original ASCII value.
5204
INC HL 23
Advance HL to the next byte in the encoded string.
5206
OR A,A B7
Test Register A for zero. A zero byte marks the end of the encoded string.
5207
RET Z C8
If the Z FLAG is set (Register A is zero), the string terminator has been reached. Return to caller.
5208
SUB A,B 90
Subtract the decode key (90H) from the encoded byte to recover the original ASCII character. For example: D3H - 90H = 43H = 'C', E0H - 90H = 50H = 'P', etc.
5209
GOSUB to ROM routine at 0033H to display the decoded character in Register A at the current cursor position.
520C
Loop End
LOOP BACK to 5205H to fetch and display the next encoded character.

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.

520E
DEFB 90H 90
Decode key byte. Loaded into Register B at 5203H. Each encoded character byte has this value (90H) added to it; the display loop at 5208H subtracts it to recover the original ASCII value.
520F
DEFB D3H D3
Encoded 'C': D3H - 90H = 43H = ASCII 'C' (start of "COPYRIGHT")
5210
DEFB E0H E0
Encoded 'O': E0H - 90H = 50H = ASCII 'O'
5211
DEFB E9H E9
Encoded 'P': E9H - 90H = 59H = ASCII 'P'
5212
DEFB E2H E2
Encoded 'Y': E2H - 90H = 52H = ASCII 'Y'
5213
DEFB D9H D9
Encoded 'R': D9H - 90H = 49H = ASCII 'R'
5214
DEFB D7H D7
Encoded 'I': D7H - 90H = 47H = ASCII 'I'
5215
DEFB D8H D8
Encoded 'G': D8H - 90H = 48H = ASCII 'G'
5216
DEFB E4H E4
Encoded 'H': E4H - 90H = 54H = ASCII 'H'
5217
DEFB B0H B0
Encoded 'T': B0H - 90H = 20H = ASCII 'T'
5218
DEFB B8H B8
Encoded ' ': B8H - 90H = 28H = ASCII ' ' (space after "COPYRIGHT")
5219
DEFB D3H D3
Encoded '(': D3H - 90H = 43H = ASCII '('
521A
DEFB B9H B9
Encoded 'C': B9H - 90H = 29H = ASCII 'C' (inside "(C)")
521B
DEFB B0H B0
Encoded ')': B0H - 90H = 20H = ASCII ')'
521C
DEFB C1H C1
Encoded ' ': C1H - 90H = 31H = ASCII ' ' (space after "(C)")
521D
DEFB C9H C9
Encoded '1': C9H - 90H = 39H = ASCII '1' (start of "1980")
521E
DEFB C8H C8
Encoded '9': C8H - 90H = 38H = ASCII '9'
521F
DEFB C0H C0
Encoded '8': C0H - 90H = 30H = ASCII '8'
5220
DEFB B0H B0
Encoded '0': B0H - 90H = 20H = ASCII '0' (end of "1980")
5221
DEFB D2H D2
Encoded ' ': D2H - 90H = 42H = ASCII ' ' (space before "BY")
5222
DEFB E9H E9
Encoded 'B': E9H - 90H = 59H = ASCII 'B'
5223
DEFB B0H B0
Encoded 'Y': B0H - 90H = 20H = ASCII 'Y' (end of "BY")
5224
DEFB E2H E2
Encoded ' ': E2H - 90H = 52H = ASCII ' ' (space before "RANDOLPH")
5225
DEFB D1H D1
Encoded 'R': D1H - 90H = 41H = ASCII 'R'
5226
DEFB DEH DE
Encoded 'A': DEH - 90H = 4EH = ASCII 'A'
5227
DEFB D4H D4
Encoded 'N': D4H - 90H = 44H = ASCII 'N'
5228
DEFB DFH DF
Encoded 'D': DFH - 90H = 4FH = ASCII 'D'
5229
DEFB DCH DC
Encoded 'O': DCH - 90H = 4CH = ASCII 'O'
522A
DEFB E0H E0
Encoded 'L': E0H - 90H = 50H = ASCII 'L'
522B
DEFB D8H D8
Encoded 'P': D8H - 90H = 48H = ASCII 'P'
522C
DEFB B0H B0
Encoded 'H': B0H - 90H = 20H = ASCII 'H' (end of "RANDOLPH")
522D
DEFB E6H E6
Encoded ' ': E6H - 90H = 56H = ASCII ' ' (space before "V.")
522E
DEFB BEH BE
Encoded 'V': BEH - 90H = 2EH = ASCII 'V'
522F
DEFB B0H B0
Encoded '.': B0H - 90H = 20H = ASCII '.' (end of "V.")
5230
DEFB D3H D3
Encoded ' ': D3H - 90H = 43H = ASCII ' ' (space before "COOK")
5231
DEFB DFH DF
Encoded 'C': DFH - 90H = 4FH = ASCII 'C'
5232
DEFB DFH DF
Encoded 'O': DFH - 90H = 4FH = ASCII 'O'
5233
DEFB DBH DB
Encoded 'O': DBH - 90H = 4BH = ASCII 'O'
5234
DEFB BCH BC
Encoded 'K': BCH - 90H = 2CH = ASCII 'K' (end of "COOK")
5235
DEFB B0H B0
Encoded ',': B0H - 90H = 20H = ASCII ','
5236
DEFB D1H D1
Encoded ' ': D1H - 90H = 41H = ASCII ' ' (space before "ALL")
5237
DEFB DCH DC
Encoded 'A': DCH - 90H = 4CH = ASCII 'A'
5238
DEFB DCH DC
Encoded 'L': DCH - 90H = 4CH = ASCII 'L'
5239
DEFB B0H B0
Encoded 'L': B0H - 90H = 20H = ASCII 'L' (end of "ALL")
523A
DEFB E2H E2
Encoded ' ': E2H - 90H = 52H = ASCII ' ' (space before "RIGHTS")
523B
DEFB D9H D9
Encoded 'R': D9H - 90H = 49H = ASCII 'R'
523C
DEFB D7H D7
Encoded 'I': D7H - 90H = 47H = ASCII 'I'
523D
DEFB D8H D8
Encoded 'G': D8H - 90H = 48H = ASCII 'G'
523E
DEFB E4H E4
Encoded 'H': E4H - 90H = 54H = ASCII 'H'
523F
DEFB E3H E3
Encoded 'T': E3H - 90H = 53H = ASCII 'T'
5240
DEFB B0H B0
Encoded 'S': B0H - 90H = 20H = ASCII 'S' (end of "RIGHTS")
5241
DEFB E2H E2
Encoded ' ': E2H - 90H = 52H = ASCII ' ' (space before "RESERVED")
5242
DEFB D5H D5
Encoded 'R': D5H - 90H = 45H = ASCII 'R'
5243
DEFB E3H E3
Encoded 'E': E3H - 90H = 53H = ASCII 'E'
5244
DEFB D5H D5
Encoded 'S': D5H - 90H = 45H = ASCII 'S'
5245
DEFB E2H E2
Encoded 'E': E2H - 90H = 52H = ASCII 'E'
5246
DEFB E6H E6
Encoded 'R': E6H - 90H = 56H = ASCII 'R'
5247
DEFB D5H D5
Encoded 'V': D5H - 90H = 45H = ASCII 'V'
5248
DEFB D4H D4
Encoded 'E': D4H - 90H = 44H = ASCII 'E'
5249
DEFB 9DH 9D
Encoded 'D': 9DH - 90H = 0DH = ASCII 'D' (end of "RESERVED") - wait, 0DH is CR not 'D'.
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.
524A
DEFB 00H 00
String terminator byte (00H). The display loop at 5207H tests for this value and returns when found, ending the copyright notice 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.

5200
LD DE,5267H 11 67 52
Point Register Pair DE to the HIGH keyword parameter table at 5267H. This table defines the "HIGH" keyword (and its single-letter abbreviation "H") that 4476H will scan the command line for.
5203
GOSUB to SYS0 open file utility at 4476H to parse the command line for the HIGH= parameter. On return: Z FLAG set if parsing succeeded (keyword found or no parameter given), NZ FLAG set if an unrecognised parameter was present. If HIGH=addr was found, the parsed address is returned in Register Pair BC.
5206
If the NZ FLAG is set (unrecognised parameter on command line), JUMP to 5239H to display "PARAMETER ERROR" and exit.
5208
LD BC,0000H 01 00 00
Self-Modifying Code
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.
520B
LD A,B 78
Load the high byte of BC into Register A in preparation for the zero test.
520C
OR A,C B1
OR the low byte of BC into Register A. If BC is 0000H (no HIGH= parameter was given), the Z FLAG is set.
520D
If the Z FLAG is set (BC = 0000H, no address parameter supplied), JUMP to 5226H to display the current HIGH$ value and exit.

A HIGH=addr parameter was supplied. BC holds the requested new HIGH$ address. Two range checks follow before the value is accepted.

520F
LD HL,5200H 21 00 52
Load HL with 5200H - the load address of this overlay. The new HIGH$ value must be below 5200H to avoid overwriting the currently executing code.
5212
XOR A,A AF
Set Register A to zero and clear all flags. Required to initialise the carry flag before the SBC HL,BC instruction.
5213
SBC HL,BC ED 42
Subtract the requested new HIGH$ address (BC) from 5200H (HL). If BC >= 5200H the result is zero or negative, setting the CARRY FLAG or ZERO FLAG - meaning the new address would overwrite or collide with this overlay.
5215
If the NO CARRY FLAG is set (BC >= 5200H - the requested address is at or above the overlay load address), JUMP to 5252H to display "RANGE ERROR" and exit.
5217
LD HL,(4049H) 2A 49 40
Fetch the current HIGH$ value from system variable at 4049H into HL. The requested new value must be lower than the existing HIGH$ - the command can only lower HIGH$, never raise it.
521A
XOR A,A AF
Set Register A to zero and clear all flags. Required to initialise the carry flag before the second SBC HL,BC instruction.
521B
SBC HL,BC ED 42
Subtract the requested new HIGH$ address (BC) from the current HIGH$ value (HL). If BC > current HIGH$, the result is negative, setting the CARRY FLAG - meaning the new address is already above the current limit.
521D
If the CARRY FLAG is set (requested address > current HIGH$), JUMP to 5252H to display "RANGE ERROR" and exit. The new HIGH$ must be lower than the existing one.
521F
LD (4049H),BC ED 43 49 40
Store the validated new HIGH$ address from BC into system variable 4049H. This lowers the top of usable memory, reserving the area above BC for special utility routines as described in the manual.
5223
JUMP to SYS0 DOS READY exit at 402DH. The HIGH$ value has been successfully updated; return control to the DOS command interpreter.
5226
LD DE,(4049H) ED 5B 49 40
Display Current High$ Path
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.
522A
LD HL,5281H 21 81 52
Point HL to the 4-byte hex placeholder field "XXXX" within the display string at 5281H. 4DE7H will overwrite these four bytes with the ASCII hex digits of the DE value.
522D
GOSUB to SYS0 hex display routine at 4DE7H. Converts the 16-bit value in DE to four ASCII hex digits and writes them into the buffer pointed to by HL (5281H), updating the "HIGH = X'XXXX'" display string in place.
5230
LD HL,5278H 21 78 52
Point HL to the "HIGH = X'" display string at 5278H, which now contains the updated hex digits inserted by 4DE7H.
5233
GOSUB to SYS0 display routine at 447BH to print the complete "HIGH = X'XXXX'" string to the screen, as confirmed by the test output shown in the screenshot.
5236
JUMP to SYS0 DOS READY exit at 402DH. The HIGH$ display is complete; return control to the DOS command interpreter.
5239
LD HL,5242H 21 42 52
Parameter Error Path
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.
523C
GOSUB to SYS0 display routine at 447BH to print the "PARAMETER ERROR" message to the screen.
523F
JUMP to SYS0 error-already-displayed exit at 4030H. The error message has been printed; return to the DOS command interpreter without printing an additional error.

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).

5242
DEFM "PARAMETER ERROR" 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52
ASCII text "PARAMETER ERROR" - displayed by 447BH when the command line contains an unrecognised parameter.
5251
DEFB 0DH 0D
Carriage return terminator (0DH). Marks the end of the "PARAMETER ERROR" string for the 447BH display routine.

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.

5252
LD HL,525BH 21 5B 52
Range Error Path
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.
5255
GOSUB to SYS0 display routine at 447BH to print the "RANGE ERROR" message to the screen, as confirmed by the test output shown in the screenshot (e.g., "MEMORY (HIGH=X'2000')" produces "RANGE ERROR").
5258
JUMP to SYS0 error-already-displayed exit at 4030H. The error message has been printed; return to the DOS command interpreter without printing an additional error.

525BH - "RANGE ERROR" Message String

Error message displayed when the supplied HIGH= address is outside the permitted range. Terminated by CR (0DH).

525B
DEFM "RANGE ERROR" 52 41 4E 47 45 20 45 52 52 4F 52
ASCII text "RANGE ERROR" - displayed by 447BH when the supplied HIGH= address fails either the overlay collision check or the above-current-HIGH$ check.
5266
DEFB 0DH 0D
Carriage return terminator (0DH). Marks the end of the "RANGE ERROR" string for the 447BH display routine.

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.

5267
DEFM "HIGH " 48 49 47 48 20 20
Parameter table entry 1 of 2: keyword "HIGH" space-padded to 6 characters. When matched by 4476H against the command line parameter (e.g., "HIGH=X'2000'"), the parsed address value is stored into the operand field of the LD BC instruction at 5208H.
526D
DEFB 09H,52H 09 52
Entry 1 result byte (09H) and destination high address (52H): together forming the little-endian pointer 5209H - the operand field of the LD BC,nnnnH instruction at 5208H. 4476H writes the parsed HIGH= address value into 5209H-520AH, which is then loaded into BC when the instruction executes.
526F
DEFM "H " 48 20 20 20 20 20
Parameter table entry 2 of 2: keyword "H" space-padded to 6 characters. Accepted as a single-letter abbreviation for HIGH, identical in effect. When matched, the parsed address is stored into the same operand field at 5209H.
5275
DEFB 09H,52H 09 52
Entry 2 result byte (09H) and destination high address (52H): identical to entry 1 at 526DH. Both entries write to 5209H-520AH.
5277
DEFB 00H 00
Table terminator byte (00H). Signals the end of the keyword table to 4476H.

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).

5278
DEFM "HIGH = X'" 48 49 47 48 20 3D 20 58 27
Fixed prefix of the HIGH$ display string: "HIGH = X'" - matches the format shown in the screenshot output "HIGH = X'FDE8'".
5281
DEFM "XXXX" 58 58 58 58
Self-Modifying Code
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".
5285
DEFM "’" 27
Closing single-quote character, completing the "HIGH = X'XXXX'" format.
5286
DEFB 0DH 0D
Carriage return terminator (0DH). Marks the end of the display string for the 447BH routine.

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.

5200
LD A,(430FH) 3A 0F 43
Fetch the system state flags byte from 430FH into Register A. Bit 5 of this register indicates whether program chaining (re-entry mode) is currently active.
5203
BIT 5,A CB 6F
Test bit 5 of the system state flags. If set, VTOS is currently executing a chained program sequence. SYSGEN cannot safely write to disk while chaining is in progress.
5205
If the NZ FLAG is set (bit 5 set - program chaining active), JUMP to 535DH to display "SYSGEN INVALID DURING PROGRAM CHAINING" and exit with an error.
5208
LD A,H 7C
Load the high byte of HL into Register A in preparation for testing whether HL is zero. HL was set by the caller to indicate which SYSGEN operation is requested.
5209
OR A,L B5
OR the low byte of HL into Register A. If HL is 0000H (no address parameter - display/delete path), the Z FLAG is set.
520A
If the Z FLAG is set (HL = 0000H), JUMP to 5329H to execute the configuration read/delete path: open the existing CONFIG/SYS.CC file and restore or report its contents.

Write path: HL is non-zero (SYSGEN=ON). Open CONFIG/SYS.CC for writing and package all system configuration regions into sector records.

520D
LD DE,5392H 11 92 53
Point Register Pair DE to the CONFIG/SYS.CC filespec at 5392H. This is the FCB filename passed to 4420H to open the configuration file for writing.
5210
LD HL,5500H 21 00 55
Point HL to the sector data buffer at 5500H. This buffer receives the packed configuration data before it is written to disk.
5213
LD B,00H 06 00
Load Register B with 00H. This is the mode byte passed to 4420H: mode 00H opens the file for writing (creating it if necessary).
5215
GOSUB to SYS0 at 4420H to open the CONFIG/SYS.CC file for writing. On return, Z FLAG set if the file was opened successfully, NZ if an error occurred.
5218
If the NZ FLAG is set (file open failed), JUMP to 538DH to report the disk error and exit.

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.

521B
LD A,05H 3E 05
Load Register A with 05H - the first marker byte to be written as a single-byte sector record to the CONFIG/SYS.CC file.
521D
GOSUB to the output byte subroutine at 53B2H to write the byte in Register A to the configuration file via the SYS0 I/O device interface at 001BH.
5220
LD A,06H 3E 06
Load Register A with 06H - the second marker byte to be written to the configuration file.
5222
GOSUB to 53B2H to write the 06H marker byte to the configuration file.
5225
LD B,06H 06 06
Load Register B with 06H - the byte count for the next block write. Six bytes from the data table at 53BDH will be written to the configuration file.
5227
LD HL,53BDH 21 BD 53
Point HL to the 6-byte data block at 53BDH. This block contains configuration header bytes to be written sequentially to the file.
522A
LD A,(HL) 7E
Loop Start
Fetch the next byte from the data block at 53BDH into Register A.
522B
INC HL 23
Advance HL to the next byte in the data block.
522C
GOSUB to 53B2H to write the current byte to the configuration file.
522F
Loop End
DECrement B and LOOP BACK to 522AH if not zero. Repeats for all 6 bytes of the header data block.
5231
LD B,13H 06 13
Load Register B with 13H (19 decimal) - the byte count for the "*** CONFIGURING ***" banner block to be written to the video area via 5307H.
5233
LD DE,3F93H 11 93 3F
Point Register Pair DE to 3F93H - a location within video RAM. The "*** CONFIGURING ***" banner will be written here to display a status message on screen while the configuration is being saved.
5236
LD HL,5409H 21 09 54
Point HL to the "*** CONFIGURING ***" banner data at 5409H (19 bytes). This is the source data to be copied to video RAM at 3F93H.
5239
GOSUB to the block-write subroutine at 5307H to copy B=19 bytes from HL=5409H to DE=3F93H (video RAM) AND simultaneously write each byte as a record to the configuration file via 53B2H.
523C
LD B,01H 06 01
Load Register B with 01H - byte count for the next block: a single byte from 5408H (which contains C9H = RET opcode) to be written to the RST vector area at 4012H.
523E
LD DE,4012H 11 12 40
Point Register Pair DE to 4012H - the RST 38H hook location in the SYS0 work area. The byte from 5408H will be written here and simultaneously saved to the configuration file.
5241
LD HL,5408H 21 08 54
Point HL to 5408H which contains C9H (RET opcode). This byte will be copied to 4012H and written to the config file as the RST 38H vector state.
5244
GOSUB to 5307H to copy 1 byte from 5408H to 4012H and write it to the configuration file.
5247
XOR A,A AF
Set Register A to zero and clear all flags.
5248
LD (4018H),A 32 18 40
Store zero into 4018H. This clears a byte within the drive configuration table area, resetting an entry before the configuration data is packaged for saving.
524B
LD HL,542FH 21 2F 54
Point HL to the configuration region table at 542FH. This table defines all memory regions to be saved: each entry is 4 bytes specifying count-low, count-high, destination-low, destination-high. The table is terminated by a zero count entry.
524E
LD C,(HL) 4E
Configuration Region Table Loop Start
Fetch the low byte of the byte count for this configuration region entry into Register C.
524F
INC HL 23
Advance HL to the count high byte.
5250
LD B,(HL) 46
Fetch the high byte of the byte count into Register B. BC now holds the full 16-bit byte count for this region.
5251
INC HL 23
Advance HL to the destination address low byte.
5252
LD A,B 78
Load the count high byte into Register A for the zero test.
5253
OR A,C B1
OR the count low byte into Register A. If BC is 0000H, this is the end-of-table sentinel.
5254
If the Z FLAG is set (BC = 0000H - end of table), JUMP to 5261H to proceed with the RAM size detection phase.
5256
LD E,(HL) 5E
Fetch the destination address low byte from the table entry into Register E.
5257
INC HL 23
Advance HL to the destination address high byte.
5258
LD D,(HL) 56
Fetch the destination address high byte into Register D. DE now holds the destination address for this configuration region - the memory location where this region's data will be restored when the configuration is loaded back.
5259
INC HL 23
Advance HL past the destination address high byte to point to the next table entry.
525A
PUSH HL E5
Save the table pointer HL onto the stack so it is preserved across the CALL to 52CEH which will use HL for its own purposes.
525B
GOSUB to the configuration region write subroutine at 52CEH. On entry: DE = destination address (where data lives in memory), BC = byte count. 52CEH reads BC bytes from DE, packages them into sector records, and writes each byte to the configuration file via 53B2H.
525E
POP HL E1
Restore the configuration region table pointer from the stack into HL, pointing to the next table entry.
525F
Loop End
LOOP BACK to 524EH to process the next configuration region table entry.
5261
LD HL,0FFFFH 21 FF FF
Ram Top Detection
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.
5264
LD A,(HL) 7E
Ram Probe Loop Start
Read the current byte at the probe address into Register A and save it in B.
5265
LD B,A 47
Save the original byte at the probe address into Register B so it can be restored after the write test.
5266
CPL 2F
Complement Register A (invert all bits). Writing the complement and reading it back distinguishes real RAM (read-back matches) from ROM or absent memory (read-back does not match).
5267
LD (HL),A 77
Write the complemented value to the probe address.
5268
CP A,(HL) BE
Compare the complemented value in Register A against the byte read back from the probe address. If real RAM is present, the read-back will match and the Z FLAG will be set.
5269
LD (HL),B 70
Restore the original byte to the probe address, preserving memory contents regardless of whether RAM was detected.
526A
If the Z FLAG is set (read-back matched - real RAM found at this address), JUMP to 5272H to record this address as the RAM top and write it to the configuration file.
526C
LD A,H 7C
Load the high byte of the current probe address into Register A in preparation for stepping down by 0400H (1KB).
526D
SUB A,04H D6 04
Subtract 04H from the high byte, stepping the probe address down by 0400H (1KB). The RAM detection probes in 1KB decrements from FFFFH downward.
526F
LD H,A 67
Store the decremented high byte back into H, updating the probe address.
5270
Loop End
LOOP BACK to 5264H to test the next lower 1KB boundary.
5272
LD DE,(4049H) ED 5B 49 40
Ram Top Record
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.
5276
XOR A,A AF
Set Register A to zero and clear all flags, initialising the carry flag before the 16-bit subtraction.
5277
SBC HL,DE ED 52
Subtract HIGH$ (DE) from the detected RAM top (HL). The result in HL represents the number of bytes between HIGH$ and the top of RAM - the size of the reserved/expansion memory region.
5279
LD B,H 44
Load the high byte of the calculated size into Register B.
527A
LD C,L 4D
Load the low byte of the calculated size into Register C. BC now holds the byte count for the RAM extent record.
527B
INC DE 13
Increment DE (HIGH$ address) by 1 to point to the first byte of the reserved memory region, making DE the source address for the RAM extent record.
527C
If the NZ FLAG is set (the calculated size is non-zero - there is reserved memory above HIGH$), GOSUB to 52CEH to package and write the RAM extent region to the configuration file. If BC is zero (HIGH$ is already at RAM top), skip this step.
527F
LD B,03H 06 03
Load Register B with 03H - byte count for the next block copy: 3 bytes from 4012H (the RST 38H hook area) will be written to the configuration file.
5281
LD DE,4012H 11 12 40
Point Register Pair DE to 4012H - the RST 38H interrupt handler hook. This is both the source and destination address for the 3-byte hook record.
5284
LD HL,4012H 21 12 40
Point HL also to 4012H. 5307H reads from HL and writes to DE simultaneously; here both point to the same address, so the live RST 38H hook bytes are read and written to the file without modifying memory.
5287
GOSUB to 5307H to read 3 bytes from 4012H and write them to the configuration file, recording the current RST 38H interrupt vector state.
528A
LD B,13H 06 13
Load Register B with 13H (19 decimal) - byte count for writing the "*** CONFIGURING ***" banner back to video RAM to confirm the operation is still in progress.
528C
LD DE,3F93H 11 93 3F
Point Register Pair DE to 3F93H (video RAM) - destination for the second banner write.
528F
LD HL,541CH 21 1C 54
Point HL to the second banner data block at 541CH (19 bytes of spaces and marker characters) to overwrite the video RAM display area.
5292
GOSUB to 5307H to copy the 19-byte block from 541CH to video RAM at 3F93H and write the bytes to the configuration file.
5295
LD A,02H 3E 02
Load Register A with 02H - the first of four closing marker bytes to be written to the configuration file as the end-of-configuration sentinel.
5297
GOSUB to 53B2H to write the 02H marker byte to the configuration file.
529A
LD A,02H 3E 02
Load Register A with 02H - the second closing marker byte.
529C
GOSUB to 53B2H to write the second 02H marker byte to the configuration file.
529F
XOR A,A AF
Set Register A to zero.
52A0
GOSUB to 53B2H to write a 00H byte to the configuration file.
52A3
XOR A,A AF
Set Register A to zero again.
52A4
GOSUB to 53B2H to write the final 00H closing byte to the configuration file. The four-byte closing sequence 02H 02H 00H 00H marks the end of the configuration data stream.
52A7
GOSUB to SYS0 at 4428H to close the CONFIG/SYS.CC file, flushing all buffered data to disk.
52AA
If the NZ FLAG is set (file close failed - disk error), JUMP to 538DH to report the error and exit.

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.

52AD
LD HL,4200H 21 00 42
Point HL to the directory/sector buffer at 4200H. This buffer will receive the sector data to be written to the disk configuration marker sector.
52B0
LD DE,0002H 11 02 00
Load Register Pair DE with 0002H: D=00H (track 0), E=02H (sector 2). This is the disk location of the system configuration marker sector on drive 0.
52B3
LD C,00H 0E 00
Load Register C with 00H specifying drive 0 for the sector write operation.
52B5
GOSUB to SYS0 at 4777H to read the sector at track 0, sector 2 of drive 0 into the buffer at 4200H. The configuration marker byte will be updated and the sector written back.
52B8
If the NZ FLAG is set (sector read failed), JUMP to 538DH to report the error and exit.
52BB
XOR A,A AF
Set Register A to zero.
52BC
LD (4201H),A 32 01 42
Store 00H into the byte at offset +01H of the sector buffer (4201H). This clears the configuration present flag in the boot sector, marking the disk as having a valid user configuration that will be loaded on the next boot.
52BF
GOSUB to SYS0 at 4763H to write the modified sector buffer back to drive 0, track 0, sector 2, making the configuration marker permanent on disk.
52C2
If the NZ FLAG is set (sector write failed), JUMP to 538DH to report the error and exit.
52C5
LD HL,53B8H 21 B8 53
Point HL to the "USER CONFIGURATION BUILT" success message at 53B8H.
52C8
GOSUB to SYS0 display routine at 447BH to print "USER CONFIGURATION BUILT" to the screen, confirming the SYSGEN operation completed successfully.
52CB
JUMP to SYS0 DOS READY exit at 402DH. The configuration has been saved to disk; return control to the DOS command interpreter.

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.

52CE
EX DE,HL EB
Exchange DE and HL. On entry DE holds the destination/source address and HL may hold a table pointer. After the exchange: HL = source address (memory region to read), DE = old HL value (discarded - will be overwritten immediately).
52CF
LD DE,5392H 11 92 53
Point Register Pair DE to the CONFIG/SYS.CC filespec at 5392H. DE is passed to 53B2H as the DCB address for file output.
52D2
PUSH HL E5
Save the source address (memory region start) onto the stack.
52D3
PUSH BC C5
Save the byte count onto the stack.
52D4
LD H,B 60
Load the high byte of the byte count into H.
52D5
LD L,C 69
Load the low byte of the byte count into L. HL now holds the byte count for the range check.
52D6
LD BC,00FEH 01 FE 00
Load Register Pair BC with 00FEH (254 decimal) - the maximum number of data bytes that fit in a single sector record.
52D9
XOR A,A AF
Set Register A to zero and clear all flags, initialising the carry flag before the 16-bit subtraction.
52DA
SBC HL,BC ED 42
Subtract 254 (BC) from the byte count (HL). If the count exceeds 254, the result is non-negative (NC) and this record will carry 254 bytes with a remainder to follow. If count is 254 or less, the result is negative (C) and this is the final record.
52DC
If the NO CARRY FLAG is set (byte count exceeds 254), JUMP to 52E4H to use the full 254-byte record size and save the remainder for a subsequent call.
52DE
POP BC C1
Count fits in one record. Pop the original byte count from the stack back into BC - this is the exact data length for this (final) record.
52DF
LD HL,0000H 21 00 00
Load HL with 0000H - the remainder count after this record. Zero indicates no further records are needed for this region.
52E2
JUMP to 52E5H to proceed with writing the record header and data bytes.
52E4
POP AF F1
Count exceeds 254. Discard the original byte count from the stack (AF is a throwaway here). The record will use the fixed 254-byte size; the remainder (HL) will be used in the next iteration.
52E5
EX (SP),HL E3
Exchange HL (remainder count or 0000H) with the top of stack (source address saved at 52D2H). After this: HL = source address (memory to read), stack top = remainder count for the next call.
52E6
LD B,C 41
Load Register B with C (low byte of record data length - either the original count or 254). B will be used as the DJNZ counter for writing the data bytes.
52E7
LD A,01H 3E 01
Load Register A with 01H - the record type byte. Type 01H identifies this as a memory region data record in the configuration file format.
52E9
GOSUB to 53B2H to write the record type byte (01H) to the configuration file.
52EC
LD A,B 78
Load Register A with the record data length (B).
52ED
ADD A,02H C6 02
Add 2 to the data length. The record length field includes the 2-byte destination address that follows, so the total record payload length = data bytes + 2.
52EF
GOSUB to 53B2H to write the record length byte (data count + 2) to the configuration file.
52F2
LD A,L 7D
Load Register A with the low byte of HL (destination address low byte). The destination address tells the restore routine where in memory to copy this data when the configuration is loaded.
52F3
GOSUB to 53B2H to write the destination address low byte to the configuration file.
52F6
LD A,H 7C
Load Register A with the high byte of HL (destination address high byte).
52F7
GOSUB to 53B2H to write the destination address high byte to the configuration file. The record header is now complete: type(01H), length(n+2), dest-lo, dest-hi.
52FA
LD A,(HL) 7E
Data Byte Write Loop Start
Fetch the next data byte from the source memory region pointed to by HL.
52FB
INC HL 23
Advance HL to the next byte in the source memory region.
52FC
GOSUB to 53B2H to write the current data byte to the configuration file.
52FF
Loop End
DECrement B and LOOP BACK to 52FAH if not zero. Repeats until all data bytes for this record have been written.
5301
POP BC C1
Pop the remainder count from the stack into BC. If BC is zero, this was the final record for this region. If non-zero, another record must be written for the remaining bytes.
5302
LD A,B 78
Load the high byte of the remainder count into Register A for the zero test.
5303
OR A,C B1
OR the low byte of the remainder count into Register A. If BC is 0000H, the Z FLAG is set and this region is fully written.
5304
If the NZ FLAG is set (remainder is non-zero - more data to write for this region), LOOP BACK to 52D2H to package and write the next 254-byte record chunk.
5306
RET C9
All records for this memory region have been written to the configuration file. Return to the caller (the region table loop at 525BH or the RAM extent write at 527CH).

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.

5307
PUSH HL E5
Save the source address (HL) onto the stack. HL will be overwritten to hold the destination address for the record header, then restored to continue reading source bytes.
5308
LD H,D 62
Load H with the high byte of the destination address (D). HL is being set to the destination address for writing to the record header.
5309
LD L,E 6B
Load L with the low byte of the destination address (E). HL now holds the destination address - the memory location where these bytes will be restored when the configuration is loaded back.
530A
LD DE,5392H 11 92 53
Point Register Pair DE to the CONFIG/SYS.CC filespec at 5392H, required by 53B2H as the DCB address for file output.
530D
LD A,01H 3E 01
Load Register A with 01H - the record type byte identifying this as a memory region data record.
530F
GOSUB to 53B2H to write the record type byte (01H) to the configuration file.
5312
LD A,B 78
Load Register A with the byte count (B).
5313
ADD A,02H C6 02
Add 2 to the byte count. The record length field includes the 2-byte destination address, so total payload = data bytes + 2.
5315
GOSUB to 53B2H to write the record length byte (data count + 2) to the configuration file.
5318
LD A,L 7D
Load Register A with the low byte of the destination address (L). Written to the file so the restore routine knows where to copy this data.
5319
GOSUB to 53B2H to write the destination address low byte to the configuration file.
531C
LD A,H 7C
Load Register A with the high byte of the destination address (H).
531D
GOSUB to 53B2H to write the destination address high byte to the configuration file. The record header is now complete: type(01H), length(B+2), dest-lo, dest-hi.
5320
POP HL E1
Restore the source address from the stack into HL. HL now points to the source data bytes to be copied to the destination and written to the file.
5321
LD A,(HL) 7E
Data Byte Copy Loop Start
Fetch the next byte from the source buffer at HL into Register A.
5322
INC HL 23
Advance HL to the next source byte.
5323
GOSUB to 53B2H to write the current byte to the configuration file AND copy it to the destination address in memory. Note: 53B2H calls 001BH which outputs to the file DCB; the copy to DE (video RAM or RST area) is handled by the caller having set DE appropriately before the CALL to 5307H.
5326
Loop End
DECrement B and LOOP BACK to 5321H if not zero. Repeats until all B data bytes have been written to the file.
5328
RET C9
All bytes written. Return to the caller at 523CH, 5287H, or 5292H.

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".

5329
LD DE,5392H 11 92 53
Point Register Pair DE to the CONFIG/SYS.CC filespec at 5392H, passed to 4424H as the FCB for the file open attempt.
532C
GOSUB to SYS0 at 4424H to open the CONFIG/SYS.CC file for reading. On return: Z FLAG set if file opened successfully, NZ if the file does not exist or could not be opened.
532F
LD HL,53ECH 21 EC 53
Pre-load HL with the address of the "NO USER CONFIGURATION FOUND" message at 53ECH. If the file open fails, this message is already in HL ready to display at 5360H.
5332
If the NZ FLAG is set (file not found or open failed), JUMP to 5360H to display the "NO USER CONFIGURATION FOUND" message and exit.
5335
GOSUB to SYS0 at 442CH to read the GAT sector and prepare the file for further I/O. On return: Z FLAG set if successful, NZ if a disk error occurred.
5338
If the NZ FLAG is set (disk error reading GAT), JUMP to 538DH to report the error and exit.

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.

533B
LD HL,4200H 21 00 42
Point HL to the directory/sector buffer at 4200H which will receive the marker sector data.
533E
LD DE,0002H 11 02 00
Load Register Pair DE with 0002H: D=00H (track 0), E=02H (sector 2) - the location of the system configuration marker sector on drive 0.
5341
LD C,00H 0E 00
Load Register C with 00H specifying drive 0 for the sector read operation.
5343
GOSUB to SYS0 at 4777H to read track 0, sector 2 of drive 0 into the buffer at 4200H.
5346
If the NZ FLAG is set (sector read failed), JUMP to 538DH to report the disk error and exit.
5349
LD A,0C9H 3E C9
Load Register A with C9H (RET opcode). On the write path, 4201H was set to 00H to mark a valid configuration present. C9H is the "no configuration" sentinel value. This value will be written to 4201H to delete the configuration marker.
534B
LD (4201H),A 32 01 42
Store C9H into offset +01H of the sector buffer at 4201H, setting the "no user configuration" marker. When the sector is written back, the boot loader will not attempt to load a user configuration on the next boot.
534E
GOSUB to SYS0 at 4763H to write the modified sector buffer back to drive 0, track 0, sector 2, removing the configuration marker from the disk.
5351
If the NZ FLAG is set (sector write failed), JUMP to 538DH to report the disk error and exit.
5354
LD HL,53D1H 21 D1 53
Point HL to the "USER CONFIGURATION DELETED" message at 53D1H.
5357
GOSUB to SYS0 display routine at 447BH to print "USER CONFIGURATION DELETED" to the screen.
535A
JUMP to SYS0 DOS READY exit at 402DH. The configuration marker has been removed from disk; return control to the DOS command interpreter.
535D
LD HL,5366H 21 66 53
Chaining Active Error Path
Point HL to the "SYSGEN INVALID DURING PROGRAM CHAINING" error message at 5366H.
5360
GOSUB to SYS0 display routine at 447BH to print the error message pointed to by HL (either "SYSGEN INVALID DURING PROGRAM CHAINING" from 535DH, or "NO USER CONFIGURATION FOUND" pre-loaded at 532FH).
5363
JUMP to SYS0 error-already-displayed exit at 4030H. The error message has been printed; return to the DOS command interpreter without printing an additional error.

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).

5366
DEFM "SYSGEN INVALID DURING PROGRAM CHAINING" 53 59 53 47 45 4E 20 49 4E 56 41 4C 49 44 20 44 55 52 49 4E 47 20 50 52 4F 47 52 41 4D 20 43 48 41 49 4E 49 4E 47
ASCII text "SYSGEN INVALID DURING PROGRAM CHAINING" - displayed when 430FH bit 5 is set at entry, indicating a chained program is currently executing and disk writes cannot safely proceed.
538C
DEFB 0DH 0D
Carriage return terminator (0DH). Marks the end of the string for the 447BH display routine.

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.

538D
OR A,40H F6 40
OR 40H into Register A, setting bit 6 of the error code. Bit 6 set suppresses the extended error context display (see 430FH bit 6 in the master reference). The error code in A was set by the failing SYS0 routine before returning NZ.
538F
JUMP to SYS0 error handler at 4409H to display the error code and return to the DOS command interpreter.

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.

5392
DEFM "CONFIG/SYS.CC:0" 43 4F 4E 46 49 47 2F 53 59 53 2E 43 43 3A 30
ASCII filespec "CONFIG/SYS.CC:0" - the system configuration file. The ":0" suffix specifies drive 0. This file is created by the SYSGEN write path and read/deleted by the SYSGEN read path.
53A1
DEFB 00H 00
Null terminator (00H). Marks the end of the filespec string for the SYS0 file open routines.

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.

53B2
GOSUB to ROM routine at 001BH to output the byte in Register A to the device identified by the DCB at DE (5392H = CONFIG/SYS.CC file). On return: Z FLAG set if the device accepted the byte successfully, NZ if an error occurred.
53B5
RET Z C8
If the Z FLAG is set (byte written successfully), return to the caller. The write succeeded and processing continues normally.
53B6
If the write failed (NZ FLAG set), JUMP to the disk error handler at 538DH. This routine does not return on error - it exits directly to the DOS error handler, aborting the entire SYSGEN operation.

53B8H - "USER CONFIGURATION BUILT" Message

Success message displayed after the SYSGEN write path completes successfully. Terminated by CR (0DH).

53B8
DEFM "USER CONFIGURATION BUILT" 55 53 45 52 20 43 4F 4E 46 49 47 55 52 41 54 49 4F 4E 20 42 55 49 4C 54
ASCII text "USER CONFIGURATION BUILT" - displayed at 52C8H after the CONFIG/SYS.CC file has been written and the disk marker sector updated successfully.
53D0
DEFB 0DH 0D
Carriage return terminator (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).

53D1
DEFM "USER CONFIGURATION DELETED" 55 53 45 52 20 43 4F 4E 46 49 47 55 52 41 54 49 4F 4E 20 44 45 4C 45 54 45 44
ASCII text "USER CONFIGURATION DELETED" - displayed at 5357H after the C9H sentinel has been written back to the boot sector marker, removing the SYSGEN configuration from disk.
53EBH
DEFB 0DH 0D
Carriage return terminator (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).

53EC
DEFM "NO USER CONFIGURATION FOUND" 4E 4F 20 55 53 45 52 20 43 4F 4E 46 49 47 55 52 41 54 49 4F 4E 20 46 4F 55 4E 44
ASCII text "NO USER CONFIGURATION FOUND" - displayed when 4424H returns NZ (file not found) at 5332H, or when the configuration marker sector indicates no valid configuration is present.
5407
DEFB 0DH 0D
Carriage return terminator (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.

5408
DEFB 0C9H C9
C9H = Z80 RET opcode. Written to 4012H (RST 38H hook) by the block copy at 5241H-5244H, ensuring the interrupt vector is in a known neutral state while the configuration is being written to disk.

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.

5409-541BH
DEFM "*** CONFIGURING ***" 2A 2A 2A 20 43 4F 4E 46 49 47 55 52 49 4E 47 20 2A 2A 2A
ASCII text "*** CONFIGURING ***" (19 bytes, matching B=13H=19 loaded at 5231H). Written to video RAM at 3F93H to display a status banner during the SYSGEN write operation, and simultaneously recorded in the configuration file as a type-01H memory region record 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.

541C-542EH
DEFM " " x 19 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
19 ASCII space characters (20H), matching B=13H=19 loaded at 528AH. Written to video RAM at 3F93H to erase the status banner after the configuration data has been written, and recorded in the configuration file so the screen region is correctly restored on 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.

542F
DEFW 0004H,4047H 04 00 47 40
Entry 1: Save 4 bytes from 4047H. Addresses 4047H-404AH hold the high memory limit pointer (HIGH$) and the detected RAM top address - the system memory boundary variables in the SYS0 work area.
5433
DEFW 0012H,404BH 12 00 4B 40
Entry 2: Save 18 (12H) bytes from 404BH. Addresses 404BH-405CH hold the timer callback slot table - the six 3-byte scheduled interrupt handler entries managed by the SYS0 real-time clock ISR.
5437
DEFW 0018H,4015H 18 00 15 40
Entry 3: Save 24 (18H) bytes from 4015H. Addresses 4015H-402CH hold drive configuration table #1 - the three 8-byte live drive configuration entries referenced by the chain-walk at 44C1H.
543B
DEFW 0003H,4300H 03 00 00 43
Entry 4: Save 3 bytes from 4300H. Address 4300H holds the patchable JP/RET slot for the drive 0 default handler - the 1-byte opcode (C9H RET or C3H JP) plus 2-byte jump target.
543F
DEFW 0001H,430FH 01 00 0F 43
Entry 5: Save 1 byte from 430FH. Address 430FH holds the system state flags register - the critical multi-bit configuration byte controlling overlay dispatch, chaining, debug mode, and RS-232 state.
5443
DEFW 0002H,4310H 02 00 10 43
Entry 6: Save 2 bytes from 4310H. Addresses 4310H-4311H hold the cached overlay fast-return address, initialised to 4C2FH at cold start and patched to 4C2DH when the alternate overlay dispatch byte is installed.
5447
DEFW 0003H,4315H 03 00 15 43
Entry 7: Save 3 bytes from 4315H. Addresses 4315H-4317H hold the BREAK handler patchable opcode (C9H/C3H) and the 2-byte BREAK jump target address - the mechanism used by SYS5 Go command and SYS6 DEBUG toggle.
544B
DEFW 0048H,43B8H 48 00 B8 43
Entry 8: Save 72 (48H) bytes from 43B8H. Addresses 43B8H-43FFH hold the upper SYS0 work area including the saved interrupt vector at 43BEH, drive configuration table #2 at 43C0H-43D7H, and the remaining system state bytes through 43FFH.
544F
DEFW 0003H,4BC0H 03 00 C0 4B
Entry 9: Save 3 bytes from 4BC0H. Address 4BC0H holds a 3-byte area within the SYS0 directory I/O region used for sector read/write dispatch state.
5453
DEFW 0003H,439CH 03 00 9C 43
Entry 10: Save 3 bytes from 439CH. Address 439CH holds a 3-byte patchable JP slot in the SYS0 work area called via CALL 4300H at the drive dispatch path at 439CH.
5457
DEFW 0050H,4700H 50 00 00 47
Entry 11: Save 80 (50H) bytes from 4700H. Addresses 4700H-474FH hold the complete drive parameter block table - all 8 drive entries of 10 bytes each, containing the JP 4600H dispatch vectors, drive flags, select latch shadow bytes, and cached track/sector values.
545B
DEFW 0003H,4439H 03 00 39 44
Entry 12: Save 3 bytes from 4439H. Address 4439H holds a 3-byte slot at the SYS0 write record entry point - saving the current dispatch state for the file write I/O path.
545F
DEFW 0018H,4500H 18 00 00 45
Entry 13: Save 24 (18H) bytes from 4500H. Addresses 4500H-4517H hold a 24-byte region in the SYS0 area used for file I/O or overlay parameter state that must be preserved across the SYSGEN configuration save.
5463
DEFW 0000H 00 00
End-of-table sentinel. Count = 0000H. The loop at 5252H-5254H tests BC for zero before reading the destination bytes, so only these 2 bytes are needed to terminate the table. On detecting this sentinel the loop jumps to 5261H to begin the RAM top detection phase.

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.

5200
JUMP to 5213H to begin parsing the command line, skipping over the subroutine and data area at 5203H-5212H.

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).

5203
LD L,A 6F
Set Register L to the value in Register A. This positions HL to the target field within the directory entry. H is already 42H (set by the caller), so HL now points into the 256-byte sector buffer at 4200H.
5204
LD E,(HL) 5E
Fetch the low byte of the 16-bit value from the directory entry field at (HL) into Register E.
5205
INC L 2C
INCrement Register L by 1 to advance to the high byte of the field, staying within the same 256-byte page.
5206
LD D,(HL) 56
Fetch the high byte of the 16-bit value into Register D. DE now holds the complete 16-bit field value from the directory entry.
5207
LD HL,5211H 21 11 52
Point Register Pair HL to the EOF byte offset variable at 5211H. This variable is set by the file size routine at 5435H with the EOF byte from directory entry offset +03H.
520A
LD A,(HL) 7E
Fetch the EOF byte offset value from 5211H into Register A. A value of 00H means the file ends exactly on a sector boundary; any other value means the last sector is only partially used.
520B
OR A,A B7
Test Register A against itself. If the EOF offset is 00H (sector-aligned), the Z FLAG is set.
520C
RET Z C8
If the Z FLAG is set (EOF offset = 00H, file ends on a sector boundary), RETURN to caller. DE contains the unadjusted value.
520D
DEC DE 1B
DECrement DE by 1. When the EOF offset is non-zero, the last sector is only partially filled, so the total count must be reduced by 1 for accurate size calculation.
520E
RET C9
RETURN to caller with the adjusted value in DE.

520FH - DIR Working Variables

Work area used as runtime storage by the DIR command. These locations are patched by self-modifying code during execution.

520F-5210
NOP x 2 00 x 2
Unused padding bytes.
5211
DEFB 00H 00
Self-Modifying Code
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.
5212
DEFB 00H 00
Unused padding byte.

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.

5213
LD A,(HL) 7E
Loop Start
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.
5214
INC HL 23
INCrement HL to advance to the next character position in the command line buffer.
5215
CP A,20H FE 20
Compare Register A against 20H (ASCII space). If Register A equals 20H, the Z FLAG is set.
5217
If the Z FLAG is set (character is a space), LOOP BACK to 5213H to skip this space and check the next character.
Loop End
5219
LD DE,56F6H 11 F6 56
Point Register Pair DE to the filename work buffer at 56F6H (8 bytes, pre-initialized to spaces). Parsed filename characters will be written here.
521C
LD B,08H 06 08
Load Register B with 08H (8) as the maximum filename character count for the DJNZ loop.

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.

521E
CP A,41H FE 41
Loop Start
Compare Register A against 41H (ASCII A). If Register A >= 41H, the NO CARRY FLAG is set (character is a letter or above).
5220
If the NO CARRY FLAG is set (character is A or above), JUMP to 522AH to store this valid filename character.
5222
CP A,3AH FE 3A
Compare Register A against 3AH (ASCII :). Characters 3AH-40H are not valid filename characters.
5224
If the NO CARRY FLAG is set (character is : through @, range 3AH-40H), JUMP to 5230H to check for the slash delimiter or end the filename.
5226
CP A,30H FE 30
Compare Register A against 30H (ASCII 0). Characters below 30H are not valid filename characters.
5228
If the CARRY FLAG is set (character is below 0), JUMP to 5230H to check for the slash delimiter or end the filename.
522A
LD (DE),A 12
Store the valid filename character from Register A into the filename buffer at (DE).
522B
INC DE 13
INCrement DE to advance to the next position in the filename buffer.
522C
LD A,(HL) 7E
Fetch the next character from the command line buffer into Register A.
522D
INC HL 23
INCrement HL to advance to the next position in the command line buffer.
522E
DECrement B and LOOP BACK to 521EH if not zero. Continues collecting filename characters until 8 have been stored or a non-alphanumeric delimiter is encountered.
Loop End
5230
CP A,2FH FE 2F
Compare Register A against 2FH (ASCII /). The slash character separates the filename from the extension in VTOS filespecs (e.g., BASIC/CMD).
5232
If the NZ FLAG is set (character is not a slash), JUMP to 524DH to check for a colon drive-number delimiter instead.
5234
LD DE,56FEH 11 FE 56
Point Register Pair DE to the extension work buffer at 56FEH (3 bytes, pre-initialized to spaces). Parsed extension characters will be written here.
5237
LD B,03H 06 03
Load Register B with 03H (3) as the maximum extension character count for the DJNZ loop.

The following loop extracts up to 3 extension characters, using the same alphanumeric validation logic as the filename parser above.

5239
LD A,(HL) 7E
Loop Start
Fetch the next character from the command line buffer into Register A.
523A
INC HL 23
INCrement HL to advance past this character.
523B
CP A,41H FE 41
Compare Register A against 41H (ASCII A).
523D
If the NO CARRY FLAG is set (character is a letter or above), JUMP to 5247H to store this valid extension character.
523F
CP A,3AH FE 3A
Compare Register A against 3AH (ASCII :).
5241
If the NO CARRY FLAG is set (character is : or above but below A), JUMP to 524DH to handle the colon delimiter.
5243
CP A,30H FE 30
Compare Register A against 30H (ASCII 0).
5245
If the CARRY FLAG is set (character is below 0), JUMP to 524DH to handle as a delimiter.
5247
LD (DE),A 12
Store the valid extension character from Register A into the extension buffer at (DE).
5248
INC DE 13
INCrement DE to advance to the next extension buffer position.
5249
LD A,(HL) 7E
Fetch the next character from the command line buffer.
524A
INC HL 23
INCrement HL to advance past this character.
524B
DECrement B and LOOP BACK to 523BH if not zero. Continues collecting extension characters until 3 have been stored or a delimiter is encountered.
Loop End
524D
CP A,3AH FE 3A
Compare Register A against 3AH (ASCII :). A colon after the filespec indicates a drive number follows (e.g., DIR BASIC/CMD:1).
524F
LD C,00H 0E 00
Load Register C with 00H as the default drive number (drive 0). This is used if no explicit drive number is specified in the filespec.
5251
If the NZ FLAG is set (character is not a colon), JUMP to 525CH to proceed with drive 0 as the default.
5253
LD A,(HL) 7E
Fetch the drive number character from the command line buffer. This is the ASCII digit immediately after the colon.
5254
INC HL 23
INCrement HL past the drive number character.
5255
INC HL 23
INCrement HL again to skip past any trailing character, positioning HL for subsequent command line parsing.
5256
SUB A,30H D6 30
SUBtract 30H from Register A to convert the ASCII drive digit (0-7) to a binary drive number (0-7).
5258
LD C,A 4F
Copy the binary drive number from Register A into Register C for use throughout the directory scan loops.
5259
LD (52E7H),A 32 E7 52
Self-Modifying Code
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.
525C
DEC HL 2B
DECrement HL by 1 to back up one position in the command line buffer. This re-positions HL to point to the last delimiter character for the SYS0 open utility call below.
525D
LD DE,562FH 11 2F 56
Point Register Pair DE to the directory column header template at 562FH. This 32-byte template contains the formatted column headers for the directory listing display (see data table at 562FH).
5260
PUSH BC C5
Save Register Pair BC onto the stack, preserving the drive number in C.
5261
GOSUB to SYS0 open file utility at 4476H. On entry, HL points into the command line buffer and DE points to the column header work area at 562FH. This call sets up the file control block for directory access. If the filespec is invalid, the NZ FLAG is set on return.
5264
POP BC C1
Restore Register Pair BC from the stack, recovering the drive number in C.
5265
If the NZ FLAG is set (filespec open failed), JUMP to 5584H to display the PARAMETER ERROR message and exit.

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.

5268
PUSH BC C5
Save Register Pair BC onto the stack, preserving the drive number in C for later restoration.
5269
LD A,03H 3E 03
Load Register A with 03H (3). This is the initial lines-per-page counter value, meaning 3 file entries will be displayed before a pause check.
526B
LD (53F5H),A 32 F5 53
Self-Modifying Code
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.
526E
LD A,C 79
Copy the drive number from Register C into Register A to convert it to an ASCII digit for display.
526F
ADD A,30H C6 30
ADD 30H to Register A to convert the binary drive number (0-7) to an ASCII digit (0-7).
5271
LD (568AH),A 32 8A 56
Self-Modifying Code
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.
5274
GOSUB to SYS0 routine at 44B8H to validate the drive and prepare for directory access. On entry, C contains the drive number. If the drive is not available or an error occurs, the NZ FLAG is set on return.
5277
If the NZ FLAG is set (drive validation failed), JUMP to 52F6H to handle the error. That routine checks whether a specific drive was requested and either reports an error or advances to the next drive.
527A
LD HL,5670H 21 70 56
Point Register Pair HL to the directory header banner string at 5670H. This string reads [LF]FILE DIRECTORY --- DRIVE X[spaces][ETX] with the drive digit already patched at 568AH.
527D
GOSUB to the display string subroutine at 556CH. This calls SYS0 display routine at 4467H to output the header banner to the screen (and optionally to the printer if active).
5280
POP BC C1
Restore Register Pair BC from the stack, recovering the drive number in C.
5281
PUSH BC C5
Save Register Pair BC onto the stack again. The drive number in C is needed repeatedly through the scan loop.
5282
GOSUB to SYS0 routine at 4B65H to set up the track and sector registers for directory I/O on the drive specified by C.
5285
LD E,00H 1E 00
Load Register E with 00H to request sector 0 of the directory track. Sector 0 contains the GAT (Granule Allocation Table) and HIT (Hash Index Table).
5287
LD HL,4200H 21 00 42
Point Register Pair HL to the sector buffer at 4200H. The directory sector will be read into this 256-byte buffer.
528A
GOSUB to SYS0 routine at 4B45H to read the sector into the buffer at 4200H. On entry, E = sector number (00H), HL = buffer address (4200H). If the read fails, the NZ FLAG is set and Register A contains the error code.
528D
If the NZ FLAG is set (disk read error), JUMP to 559DH to report the error via the DOS error exit.

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).

5290
LD HL,42D0H 21 D0 42
Point Register Pair HL to offset D0H in the sector buffer. This is the disk name field in the GAT sector (8 bytes, padded with spaces).
5293
LD DE,5691H 11 91 56
Point Register Pair DE to the filename portion of the display template at 5691H. The disk name will overwrite the NNNNNNNN placeholder.
5296
LD BC,0008H 01 08 00
Load Register Pair BC with 0008H (8 bytes) as the copy count for the disk name.
5299
LDIR ED B0
Block copy 8 bytes from (HL) at 42D0H to (DE) at 5691H, incrementing both pointers. This copies the disk name into the display template. After LDIR, HL = 42D8H (date field), DE = 5699H.
529B
LD DE,569DH 11 9D 56
Point Register Pair DE to the date portion of the display template at 569DH. The disk date will overwrite the MM/DD/YY placeholder.
529E
LD BC,0008H 01 08 00
Load Register Pair BC with 0008H (8 bytes) as the copy count for the disk date string.
52A1
LDIR ED B0
Block copy 8 bytes from (HL) at 42D8H to (DE) at 569DH. This copies the disk date into the display template.
52A3
LD HL,5691H 21 91 56
Point Register Pair HL to the now-populated display template at 5691H. The template now contains the disk name and date: DISKNAME -- MM/DD/YY[CR].
52A6
GOSUB to the display string subroutine at 556CH to output the disk name and date line to the screen.
52A9
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return) to output a blank line separator after the disk name/date header.
52AB
GOSUB to the character output subroutine at 5577H to display the carriage return, creating a blank line between the header and the file entries.
52AE
POP BC C1
Restore Register Pair BC from the stack, recovering the drive number in C.
52AF
PUSH BC C5
Save Register Pair BC onto the stack again for the next phase of the scan.
52B0
LD A,08H 3E 08
Load Register A with 08H as the parameter for the SYS0 directory sector read routine. This value specifies the directory entry information byte to read.
52B2
GOSUB to SYS0 routine at 4797H to read a directory sector by information byte. On entry, A = 08H. This reads the GAT/HIT sector and returns the sectors-per-granule value.
52B5
AND A,1FH E6 1F
Mask Register A with 1FH to extract the lower 5 bits, which contain the sectors-per-granule value (number of sectors in each granule, typically 5 for a standard disk).
52B7
INC A 3C
INCrement Register A by 1. The sectors-per-granule field is stored 0-based, so adding 1 gives the actual count (e.g., stored 04H becomes 5 sectors per granule).
52B8
LD (5466H),A 32 66 54
Self-Modifying Code
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.
52BB
POP BC C1
Restore Register Pair BC from the stack, recovering the drive number in C.
52BC
GOSUB to SYS0 routine at 4B65H to set up the track and sector for directory I/O, preparing to read the first file-entry directory sector.
52BF
LD E,01H 1E 01
Load Register E with 01H to request sector 1 of the directory track. This is the first file-entry directory sector (sector 0 is the GAT/HIT).
52C1
LD HL,5800H 21 00 58
Point Register Pair HL to an alternate sector buffer at 5800H. The directory sector data is read into this buffer to preserve the GAT data still in the primary buffer at 4200H.
52C4
GOSUB to SYS0 routine at 4B45H to read sector 1 into the buffer at 5800H. If the read fails, the NZ FLAG is set.
52C7
LD A,16H 3E 16
Load Register A with 16H as a pre-loaded error code (directory read error). This value will be used if the JP NZ below is taken.
52C9
If the NZ FLAG is set (sector read failed), JUMP to 559DH to report error code 16H (directory read error) via the DOS error exit.
52CC
LD HL,5800H 21 00 58
Point Register Pair HL to the start of the directory sector buffer at 5800H, preparing to begin scanning directory entries from the beginning of sector 1.
52CF
JUMP to 5308H to begin the directory entry scan loop. That routine iterates through each 32-byte entry in the sector buffer, checking for matching files to display.

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.

52D2
POP BC C1
Restore Register Pair BC from the stack. C holds the drive number and B holds the low byte of HL from the previous PUSH, used to track the current entry position within the sector buffer.
52D3
LD H,58H 26 58
Load Register H with 58H, setting the high byte of HL to the directory sector buffer page at 5800H.
52D5
LD L,B 68
Load Register L from Register B, restoring the low byte of the pointer. HL now points to the current entry's starting position within the sector buffer at 5800H.
52D6
LD A,L 7D
Copy Register L into Register A to compute the offset for the next 32-byte directory entry.
52D7
ADD A,20H C6 20
ADD 20H (32) to Register A to advance to the next directory entry. Each directory entry is 32 bytes long, so adding 20H moves to the next entry.
52D9
LD L,A 6F
Store the result back into Register L. HL now points to the next directory entry.
52DA
If the NO CARRY FLAG is set (no overflow past 58FFH), JUMP to 5308H to process the next entry within the same sector buffer.
52DC
INC L 2C
INCrement Register L by 1. When ADD 20H overflowed, L wrapped to a small value; this INC adjusts the new sector index.
52DD
BIT 5,L CB 6D
Test bit 5 of Register L. Since each directory sector holds 8 entries (8 x 32 = 256 bytes), and the directory typically has a limited number of sectors, bit 5 being set indicates the scan has moved past the last valid directory sector.
52DF
If the Z FLAG is set (bit 5 is clear, still within valid directory sectors), JUMP to 5308H to read and process the next directory sector.

All directory sectors on this drive have been scanned. Output a carriage return, then check if more drives need to be scanned.

52E1
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return) to output a blank line after the last file entry for this drive.
52E3
GOSUB to the character output subroutine at 5577H to display the carriage return.
52E6
LD A,0FFH 3E FF
Self-Modifying Code
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.
52E8
INC A 3C
INCrement Register A by 1. If A was FFH (no specific drive), it wraps to 00H and the Z FLAG is set. If A was a valid drive number (0-7), it becomes non-zero and the NZ FLAG is set.
52E9
If the NZ FLAG is set (a specific drive was requested and its scan is complete), JUMP to the SYS0 DOS READY exit at 402DH. The directory listing for the specified drive is finished.
52EC
INC C 0C
INCrement Register C to advance to the next drive number. When scanning all drives (no specific drive requested), C cycles through 0, 1, 2, ... 7.
52ED
LD A,C 79
Copy the new drive number from Register C into Register A for comparison.
52EE
CP A,08H FE 08
Compare Register A against 08H. VTOS supports drives 0-7, so drive 8 is past the last valid drive.
52F0
If the CARRY FLAG is set (drive number is still below 8), JUMP BACK to 5268H to initialize and scan the next drive.
52F3
JUMP to the SYS0 DOS READY exit at 402DH. All 8 drives have been scanned and the directory listing is complete.

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.

52F6
POP BC C1
Restore Register Pair BC from the stack, recovering the drive number in C.
52F7
LD A,(52E7H) 3A E7 52
Fetch the stored drive parameter from 52E7H into Register A. This is the operand byte patched by 5259H: a valid drive number (0-7) if a specific drive was requested, or FFH if scanning all drives.
52FA
INC A 3C
INCrement Register A by 1. If A was FFH (all-drive scan), it wraps to 00H and the Z FLAG is set. Otherwise, A is non-zero.
52FB
LD A,20H 3E 20
Load Register A with 20H as an error code (device not available). This does not affect the flags from the INC A above.
52FD
If the NZ FLAG is set (a specific drive was requested but failed validation), JUMP to 559DH to report error code 20H (device not available) via the DOS error exit.

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.

5300
INC C 0C
INCrement Register C to advance to the next drive number.
5301
LD A,C 79
Copy the new drive number from Register C into Register A for comparison.
5302
CP A,08H FE 08
Compare Register A against 08H to check if all drives have been tried.
5304
If the CARRY FLAG is set (drive number is still below 8), JUMP BACK to 5268H to try the next drive.
5307
RET C9
RETURN to the overlay dispatcher. All 8 drives have been tried and none were available, so exit silently.

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.

5308
LD A,(HL) 7E
Loop Start
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).
5309
OR A,A B7
Test Register A against itself. If the status byte is 00H (empty entry), the Z FLAG is set.
530A
If the Z FLAG is set (empty directory entry), JUMP BACK to 52D6H to advance to the next entry slot. Empty entries are skipped without display.
530C
LD B,L 45
Save Register L into Register B. L contains the low byte of the current entry's address within the 5800H page. This is saved on the stack via PUSH BC and restored later to track position.
530D
PUSH BC C5
Save Register Pair BC onto the stack. B = current entry's low-byte offset, C = drive number. Both are needed after the validation calls.
530E
LD A,L 7D
Copy Register L into Register A. L is the low byte of the entry address within the 5800H-58FFH page.
530F
AND A,0E0H E6 E0
Mask Register A with E0H to extract the 32-byte entry base offset. Since each entry is 32 bytes (20H), the upper 3 bits of the low byte identify which of the 8 entries in this sector we are processing (entry 0 = 00H, entry 1 = 20H, etc.).
5311
LD L,A 6F
Store the aligned entry base offset back into Register L. HL now points to the start of the current entry.
5312
XOR A,B A8
XOR Register A with Register B. A contains the entry-aligned offset (E0H mask) and B contains the original L value before alignment. The XOR produces the sector number portion, which is the index of the directory sector currently being scanned.
5313
CP A,0FFH FE FF
Self-Modifying Code
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.
5315
If the Z FLAG is set (same sector already loaded), JUMP to 5320H to skip the sector re-read and proceed directly to entry validation.
5317
LD (5314H),A 32 14 53
Self-Modifying Code
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.
531A
GOSUB to SYS0 directory entry validation routine at 4B10H. This reads the appropriate directory sector into the primary buffer at 4200H and validates the entry against the HIT.
531D
If the NZ FLAG is set (validation failed or read error), JUMP to 559DH to report the error via the DOS error exit.
5320
LD H,42H 26 42
Load Register H with 42H. Combined with the entry offset already in L, HL now points to the validated directory entry in the primary sector buffer at 4200H.
5322
LD A,(HL) 7E
Fetch the status/attribute byte from the directory entry at (HL) into Register A. This is offset +00H of the entry.
5323
BIT 4,A CB 67
Test bit 4 of the status byte. Bit 4 indicates whether this entry represents an active file (bit 4 = 1) or a deleted/system entry (bit 4 = 0).
5325
If the Z FLAG is set (bit 4 is clear, entry is not an active file), JUMP to 52D2H to skip this entry and advance to the next one.
5328
BIT 7,A CB 7F
Test bit 7 of the status byte. Bit 7 being set indicates a system/hidden file that should not be displayed in normal directory listings.
532A
If the NZ FLAG is set (bit 7 is set, file is hidden/system), JUMP to 52D2H to skip this entry. System files are not shown in DIR listings.
532D
BIT 6,A CB 77
Test bit 6 of the status byte. Bit 6 indicates the file has an extended attribute or is password-protected with a specific access level.
532F
If the Z FLAG is set (bit 6 is clear, no extended protection), JUMP to 533BH to check bit 3 instead.
5331
LD DE,0000H 11 00 00
Self-Modifying Code
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.
5334
LD A,D 7A
Copy Register D into Register A to test whether DE is zero (no password filter active).
5335
OR A,E B3
OR Register E into Register A. If both D and E are zero, A becomes zero and the Z FLAG is set.
5336
If the Z FLAG is set (DE = 0000H, no password filter), JUMP to 52D2H to skip this password-protected entry. When no password has been provided, password-protected files with bit 6 set are hidden from the listing.
5339
JUMP to 5347H to proceed with filename matching. The password check passed, so this entry should be displayed if the filename matches.
533B
BIT 3,A CB 5F
Test bit 3 of the status byte. Bit 3 indicates a different level of protection or access restriction on this file.
533D
If the Z FLAG is set (bit 3 is clear, no secondary protection), JUMP to 5347H to proceed with filename matching for this unprotected file.
533F
LD DE,0000H 11 00 00
Self-Modifying Code
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.
5342
LD A,D 7A
Copy Register D into Register A to test whether DE is zero.
5343
OR A,E B3
OR Register E into Register A. If DE is zero, A becomes zero and the Z FLAG is set.
5344
If the Z FLAG is set (no password filter active), JUMP to 52D2H to skip this protected entry.
5347
LD A,L 7D
Copy Register L (current entry base offset within the 4200H page) into Register A.
5348
ADD A,05H C6 05
ADD 05H to Register A. Offset +05H within the directory entry is the start of the 8-byte filename field.
534A
LD L,A 6F
Store the result into Register L. HL now points to the filename field of this directory entry in the 4200H buffer.
534B
LD DE,56F6H 11 F6 56
Point Register Pair DE to the filename search pattern at 56F6H (8-byte filename + 3-byte extension = 11 bytes total). This was populated by the parser at 5213H, or remains all-spaces for a wildcard match.
534E
LD B,0BH 06 0B
Load Register B with 0BH (11) as the comparison length: 8 bytes of filename + 3 bytes of extension.

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.

5350
LD A,(DE) 1A
Loop Start
Fetch the next character from the search pattern buffer at (DE) into Register A.
5351
CP A,(HL) BE
Compare Register A against the corresponding filename character from the directory entry at (HL). If they match, the Z FLAG is set.
5352
If the Z FLAG is set (characters match exactly), JUMP to 5359H to advance to the next character position.
5354
CP A,20H FE 20
Compare Register A against 20H (ASCII space). A space in the search pattern acts as a wildcard, matching any character in that position.
5356
If the NZ FLAG is set (pattern character is not a space and does not match the entry character), JUMP to 52D2H. This entry does not match the search pattern, so skip it and advance to the next entry.
5359
INC HL 23
INCrement HL to advance to the next character in the directory entry's filename/extension field.
535A
INC DE 13
INCrement DE to advance to the next character in the search pattern buffer.
535B
DECrement B and LOOP BACK to 5350H if not zero. Continue comparing until all 11 characters have been checked.
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 (+).

535D
LD A,L 7D
Copy Register L into Register A. After the filename comparison loop, L points past the extension field. This computes the base offset for re-positioning within the entry.
535E
AND A,0E0H E6 E0
Mask Register A with E0H to align back to the start of the 32-byte entry boundary.
5360
ADD A,05H C6 05
ADD 05H to Register A to point to the filename field at offset +05H within the entry.
5362
LD L,A 6F
Store the filename offset into Register L. HL now points to the 8-byte filename field.
5363
LD C,14H 0E 14
Load Register C with 14H (20 decimal) as the column position counter. This tracks remaining character positions on the display line to ensure proper column alignment.
5365
LD B,08H 06 08
Load Register B with 08H (8) as the filename character count for the DJNZ loop.

The following loop outputs the filename characters, stopping early if a trailing space is encountered.

5367
LD A,(HL) 7E
Loop Start
Fetch the next filename character from the directory entry at (HL) into Register A.
5368
INC HL 23
INCrement HL to advance to the next character position.
5369
CP A,20H FE 20
Compare Register A against 20H (ASCII space). A space indicates the end of the filename (space-padded).
536B
If the Z FLAG is set (trailing space reached), JUMP to 5375H to skip the remaining filename characters and move to the extension field.
536D
GOSUB to the character output subroutine at 5577H to display this filename character.
5370
DEC C 0D
DECrement the column position counter in Register C by 1.
5371
DECrement B and LOOP BACK to 5367H if not zero. Continue outputting filename characters until all 8 have been processed or a space was found.
Loop End
5373
JUMP to 5379H to check for an extension, skipping the early-termination adjustment below.
5375
LD A,L 7D
Copy Register L into Register A. L currently points into the filename field where the trailing space was found.
5376
ADD A,B 80
ADD Register B (remaining filename character count) to Register A. This advances past the unprocessed filename characters to reach the start of the extension field.
5377
DEC A 3D
DECrement A by 1 to adjust for the INC HL that already advanced past the space character.
5378
LD L,A 6F
Store the adjusted offset into Register L. HL now points to the start of the 3-byte extension field at entry offset +0DH.
5379
LD A,(HL) 7E
Fetch the first extension character from the directory entry at (HL) into Register A.
537A
CP A,20H FE 20
Compare Register A against 20H (ASCII space). If the extension field is all spaces, the file has no extension.
537C
If the Z FLAG is set (extension is blank), JUMP to 5392H to skip the extension display and proceed to padding spaces.
537E
LD A,2FH 3E 2F
Load Register A with 2FH (ASCII /) as the filename/extension separator character.
5380
GOSUB to the character output subroutine at 5577H to display the slash separator between filename and extension.
5383
DEC C 0D
DECrement the column position counter in Register C for the slash character.
5384
LD B,03H 06 03
Load Register B with 03H (3) as the extension character count for the DJNZ loop.
5386
LD A,(HL) 7E
Loop Start
Fetch the next extension character from the directory entry at (HL) into Register A.
5387
INC HL 23
INCrement HL to advance to the next extension character.
5388
CP A,20H FE 20
Compare Register A against 20H (ASCII space). Trailing spaces in the extension are suppressed.
538A
If the Z FLAG is set (trailing space in extension), JUMP to 5392H to end the extension display.
538C
GOSUB to the character output subroutine at 5577H to display this extension character.
538F
DEC C 0D
DECrement the column position counter in Register C.
5390
DECrement B and LOOP BACK to 5386H if not zero. Continue outputting extension characters until all 3 are done or a trailing space is found.
Loop End
5392
LD A,20H 3E 20
Loop Start
Load Register A with 20H (ASCII space) to output padding spaces after the filename/extension to align the attribute columns.
5394
GOSUB to the character output subroutine at 5577H to display a padding space.
5397
DEC C 0D
DECrement the column position counter in Register C.

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.

5398
LD A,L 7D
Copy Register L into Register A. L points somewhere within the current entry; this computes the entry base offset.
5399
AND A,0E0H E6 E0
Mask Register A with E0H to align to the 32-byte entry boundary.
539B
LD L,A 6F
Store the aligned base offset into Register L. HL now points to offset +00H of the current directory entry in the 4200H buffer.
539C
LD B,(HL) 46
Fetch the status/attribute byte from offset +00H of the directory entry into Register B. The individual bits encode file attributes.
539D
BIT 6,B CB 70
Test bit 6 of Register B. Bit 6 set indicates this file has the system attribute (is a system-level file).
539F
LD A,53H 3E 53
Load Register A with 53H (ASCII S) as the system flag character to display if bit 6 is set.
53A1
If the NZ FLAG is set (bit 6 is set, system file), GOSUB to 5410H to decrement the column counter in C and output the S character.
53A4
BIT 3,B CB 58
Test bit 3 of Register B. Bit 3 set indicates this file has the invisible attribute (hidden from normal access).
53A6
LD A,49H 3E 49
Load Register A with 49H (ASCII I) as the invisible flag character to display if bit 3 is set.
53A8
If the NZ FLAG is set (bit 3 is set, invisible file), GOSUB to 5410H to decrement C and output the I character.

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.

53AB
PUSH HL E5
Save Register Pair HL onto the stack, preserving the pointer to the current directory entry base.
53AC
LD A,L 7D
Copy Register L (entry base offset) into Register A.
53AD
ADD A,10H C6 10
ADD 10H to Register A to point to the extent end pointer field at offset +10H within the directory entry.
53AF
LD L,A 6F
Store the result into Register L. HL now points to offset +10H of the entry (extent end pointer, low byte).
53B0
LD E,(HL) 5E
Fetch the low byte of the extent end pointer into Register E.
53B1
INC L 2C
INCrement Register L to advance to the high byte of the extent end pointer at offset +11H.
53B2
LD D,(HL) 56
Fetch the high byte of the extent end pointer into Register D. DE now holds the full 16-bit extent end pointer.
53B3
PUSH HL E5
Save Register Pair HL onto the stack before the comparison subtraction.
53B4
LD HL,4296H 21 96 42
Point Register Pair HL to the disk capacity value at 4296H. This is stored in the GAT sector buffer and represents the total sector count or extent limit for this disk.
53B7
SBC HL,DE ED 52
SUBtract DE (extent end pointer) from HL (disk capacity). If HL equals DE, the result is zero and the Z FLAG is set, meaning the file's extent matches the disk boundary (no password protection).
53B9
POP HL E1
Restore Register Pair HL from the stack (points to offset +11H of the entry).
53BA
If the Z FLAG is set (extent matches disk capacity, no password protection), JUMP to 53D0H to set the flag character to space (no P displayed).
53BC
LD A,B 78
Copy the status byte from Register B back into Register A to test the protection level bits.
53BD
AND A,07H E6 07
Mask Register A with 07H to isolate the lower 3 bits, which encode the protection access level (0-7).
53BF
LD A,50H 3E 50
Load Register A with 50H (ASCII P) as the password-protection flag character.
53C1
If the NZ FLAG is set (protection level bits are non-zero), JUMP to 53D2H to store P as the protection flag character.

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.

53C3
INC L 2C
INCrement Register L to advance to offset +12H of the directory entry (secondary extent pointer, low byte).
53C4
LD E,(HL) 5E
Fetch the low byte of the secondary extent pointer into Register E.
53C5
INC L 2C
INCrement Register L to advance to offset +13H (secondary extent pointer, high byte).
53C6
LD D,(HL) 56
Fetch the high byte of the secondary extent pointer into Register D. DE now holds the full secondary pointer.
53C7
LD HL,4296H 21 96 42
Point Register Pair HL to the disk capacity value at 4296H again for the second comparison.
53CA
SBC HL,DE ED 52
SUBtract DE (secondary extent pointer) from HL (disk capacity). If they match, the Z FLAG is set.
53CC
LD A,50H 3E 50
Load Register A with 50H (ASCII P) in case the secondary pointer also does not match.
53CE
If the NZ FLAG is set (secondary pointer does not match capacity either), JUMP to 53D2H to store P as the protection flag.
53D0
LD A,20H 3E 20
Load Register A with 20H (ASCII space). Neither extent pointer indicated password protection, so the flag character is a space (no P displayed).
53D2
LD (5416H),A 32 16 54
Self-Modifying Code
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.
53D5
CP A,20H FE 20
Compare Register A against 20H (space). If A is not a space (i.e., it is P), the NZ FLAG is set.
53D7
If the NZ FLAG is set (file is password-protected), GOSUB to 5410H to decrement C and output the P character.
53DA
POP HL E1
Restore Register Pair HL from the stack, recovering the pointer to the directory entry base.
53DB
INC HL 23
INCrement HL to advance to offset +01H of the directory entry (flags/update byte).
53DC
BIT 6,(HL) CB 76
Test bit 6 of the byte at (HL). Bit 6 of offset +01H being set indicates the file has been updated (modified since creation).
53DE
LD A,2BH 3E 2B
Load Register A with 2BH (ASCII +) as the update indicator character.
53E0
If the NZ FLAG is set (bit 6 is set, file was updated), GOSUB to 5410H to decrement C and output the + character.
53E3
DEC HL 2B
DECrement HL to point back to offset +00H (entry base).

Output trailing spaces to fill the remaining column width, aligning the protection/EOF/size information that follows.

53E4
LD A,20H 3E 20
Loop Start
Load Register A with 20H (ASCII space) for padding.
53E6
GOSUB to the character output subroutine at 5577H to display a padding space.
53E9
DEC C 0D
DECrement the column position counter in Register C.
53EA
If the NZ FLAG is set (column counter not yet zero), LOOP BACK to 53E4H to output another padding space.
Loop End

Column padding complete. Now call the file size calculation and display routine if the entry has valid extent data.

53EC
LD DE,0000H 11 00 00
Self-Modifying Code
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.
53EF
LD A,D 7A
Copy Register D into Register A to test whether DE is zero.
53F0
OR A,E B3
OR Register E into Register A. If DE is 0000H, the Z FLAG is set.
53F1
If the NZ FLAG is set (DE is non-zero, extended display mode is active), GOSUB to 5414H to calculate and display the protection level name, EOF count, file size in kilobytes, and file date.
53F4
LD A,03H 3E 03
Self-Modifying Code
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.
53F6
DEC A 3D
DECrement the lines-per-page counter in Register A by 1.
53F7
LD (53F5H),A 32 F5 53
Self-Modifying Code
Store the decremented counter back to 53F5H, patching the operand of the LD A,03H instruction at 53F4H for the next iteration.
53FA
If the NZ FLAG is set (counter has not reached zero), JUMP to 52D2H to advance to the next directory entry without pausing.

The lines-per-page counter has reached zero. Reset the counter and check for a keypress to pause or abort the listing.

53FD
LD A,03H 3E 03
Load Register A with 03H to reset the lines-per-page counter.
53FF
LD (53F5H),A 32 F5 53
Self-Modifying Code
Store the reset value 03H to 53F5H, re-initializing the page counter for the next group of file entries.
5402
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return) to output a blank line as a visual separator between groups of entries.
5404
GOSUB to the character output subroutine at 5577H to display the carriage return.
5407
GOSUB to the keyboard pause check routine at 5603H. This checks for a keypress and allows the user to pause or abort the listing by pressing the BREAK key.
540A
If the NZ FLAG is set (user pressed a key to abort), JUMP to the SYS0 error-already-displayed exit at 4030H to terminate the listing.
540D
JUMP to 52D2H to continue scanning directory entries after the pause check passed.

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.

5410
DEC C 0D
DECrement the column position counter in Register C by 1 to account for the character about to be output.
5411
JUMP to the character output subroutine at 5577H to display the character in Register A, then return to the caller of 5410H.

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.

5414
PUSH HL E5
Save Register Pair HL onto the stack, preserving the pointer to the directory entry base.
5415
LD A,00H 3E 00
Self-Modifying Code
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.
5417
SUB A,20H D6 20
SUBtract 20H from Register A. If A was 20H (space, not protected), the result is 00H and the Z FLAG is set. If A was 50H (P), the result is 30H and the NZ FLAG is set.
5419
If the Z FLAG is set (file is not password-protected), JUMP to 541EH to use the full access level from the status byte bits 2-0.
541B
LD A,(HL) 7E
Fetch the status byte from the directory entry at (HL) into Register A. HL points to offset +00H of the entry.
541C
AND A,07H E6 07
Mask Register A with 07H to extract the lower 3 bits, which encode the protection access level (0-7). This determines which protection name to display from the table at 5650H.
541E
RLCA 07
Rotate Register A left by 1 bit (multiply by 2). Combined with the next RLCA, this computes A x 4 to index into the 4-byte-per-entry protection name table at 5650H.
541F
RLCA 07
Rotate Register A left by 1 bit again. A now holds the access level x 4, giving the byte offset into the protection name table.
5420
LD C,A 4F
Copy the table offset from Register A into Register C for the ADD HL,BC computation below.
5421
LD B,00H 06 00
Load Register B with 00H. BC now holds the 16-bit table offset (C = offset, B = 0).
5423
LD HL,5650H 21 50 56
Point Register Pair HL to the protection level name table at 5650H. This table has 8 entries of 4 bytes each: ALL , KILL, NAME, ~~~~, WRIT, READ, EXEC, NO .
5426
ADD HL,BC 09
ADD BC (table offset) to HL. HL now points to the 4-byte protection name string for this file's access level.
5427
LD DE,56A6H 11 A6 56
Point Register Pair DE to the display template at 56A6H. The protection level name will be copied into this template, which reads PROT / EOF= with the first 4 bytes being the protection name placeholder.
542A
LD BC,0004H 01 04 00
Load Register Pair BC with 0004H (4 bytes) as the copy count for the protection name.
542D
LDIR ED B0
Block copy 4 bytes from (HL) to (DE). This copies the protection level name (e.g., ALL , EXEC, READ) into the display template at 56A6H.
542F
POP HL E1
Restore Register Pair HL from the stack, recovering the directory entry base pointer.
5430
PUSH HL E5
Save HL onto the stack again for later use.
5431
INC L 2C
INCrement Register L to advance to offset +01H of the directory entry.
5432
INC L 2C
INCrement Register L to advance to offset +02H (date byte).
5433
INC L 2C
INCrement Register L to advance to offset +03H (EOF byte offset).
5434
LD A,(HL) 7E
Fetch the EOF byte offset from offset +03H of the directory entry into Register A. This value indicates how many bytes of the last sector are used (00H = full sector).
5435
LD (5211H),A 32 11 52
Self-Modifying Code
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.
5438
INC L 2C
INCrement Register L to advance to offset +04H of the directory entry.
5439
LD A,(HL) 7E
Fetch the value from offset +04H of the directory entry into Register A. This field contains the sectors-per-granule value for this file's allocation.
543A
LD (5454H),A 32 54 54
Self-Modifying Code
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.
543D
LD A,L 7D
Copy Register L into Register A to compute the offset for the extent end pointer field.
543E
ADD A,10H C6 10
ADD 10H to Register A. From offset +04H, adding 10H reaches offset +14H, which is the extent chain end pointer field.
5440
GOSUB to the helper subroutine at 5203H. Register A holds the offset (pointing to +14H), H is 42H. The subroutine reads the 16-bit extent end pointer into DE and adjusts it if the EOF byte is non-zero.
5443
LD A,(5454H) 3A 54 54
Fetch the sectors-per-granule value from the self-modifying variable at 5454H into Register A.
5446
DEC A 3D
DECrement A by 1. The sectors-per-granule is used to compute the total number of full sectors; decrementing adjusts for the end-of-file partial allocation.
5447
ADD A,(HL) 86
ADD the value at (HL) to Register A. HL was set by the 5203H subroutine return; this combines the sectors-per-granule adjustment with the remaining extent data.
5448
LD (HL),A 77
Store the adjusted value back to (HL), updating the low byte of the result.
5449
DEC HL 2B
DECrement HL to the next higher byte for carry propagation.
544A
LD A,E 7B
Copy Register E (low byte of extent pointer) into Register A.
544B
ADC A,00H CE 00
ADD 00H plus the carry flag to Register A. This propagates any carry from the previous addition into the middle byte.
544D
LD (HL),A 77
Store the carry-adjusted value to (HL), updating the middle byte.
544E
DEC HL 2B
DECrement HL to the highest byte.
544F
LD A,D 7A
Copy Register D (high byte of extent pointer) into Register A.
5450
ADC A,00H CE 00
ADD 00H plus the carry flag to Register A. This propagates any carry into the highest byte, completing the 24-bit addition.
5452
LD (HL),A 77
Store the final carry-adjusted value to (HL), completing the 24-bit EOF sector count computation.
5453
LD C,00H 0E 00
Self-Modifying Code
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.
5455
GOSUB to the 24-bit division subroutine at 55DDH. The 3-byte value at (HL) is divided by C (sectors-per-granule). The quotient in DE represents the EOF sector count.
5458
EX DE,HL EB
Exchange DE and HL. HL now contains the EOF sector count quotient for display.
5459
LD DE,56B1H 11 B1 56
Point Register Pair DE to the size display template at 56B1H. The decimal conversion routine will write the EOF count digits into this template.
545C
GOSUB to the 16-bit to decimal ASCII conversion routine at 55A2H. Converts the EOF sector count in HL to a decimal string written to (DE) at 56B1H.
545F
POP HL E1
Restore Register Pair HL from the stack, recovering the directory entry base pointer.
5460
PUSH HL E5
Save HL onto the stack again for use after the file size calculation.
5461
GOSUB to the sector count calculator at 550AH. This walks the file's extent chain in the directory entry, counting the total number of sectors allocated to the file. Returns with DE = total sector count, BC = extent count.
5464
EX DE,HL EB
Exchange DE and HL. HL now contains the total sector count from the extent walker.
5465
LD A,00H 3E 00
Self-Modifying Code
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).
5467
GOSUB to SYS0 routine at 4B8FH. This multiplies the sector count by the sectors-per-granule value to compute the total allocation in sectors, then converts to kilobytes. Returns with HL = size in quarter-kilobytes and A = remainder.
546A
PUSH AF F5
Save Register Pair AF onto the stack, preserving the remainder value for the fractional kilobyte display.
546B
LD H,L 65
Copy Register L into Register H. This begins a right-shift operation to convert from quarter-kilobytes to whole kilobytes.
546C
LD L,A 6F
Copy Register A (remainder/high portion) into Register L, forming a combined value in HL for the shift.
546D
SRL H CB 3C
Shift Register H right by 1 bit (logical shift, filling with 0). First step of dividing by 4.
546F
RR L CB 1D
Rotate Register L right through carry, propagating the shifted-out bit from H. Second step of the division.
5471
SRL H CB 3C
Shift Register H right by 1 bit again. Third step of dividing by 4.
5473
RR L CB 1D
Rotate Register L right through carry. HL now contains the file size in whole kilobytes (original value divided by 4).
5475
LD DE,56BDH 11 BD 56
Point Register Pair DE to the kilobyte count field within the size display template at 56BDH.
5478
GOSUB to the 16-bit to decimal ASCII conversion routine at 55A2H. Converts the kilobyte count in HL to a decimal string written to (DE) at 56BDH.
547B
POP AF F1
Restore Register Pair AF from the stack, recovering the remainder value that indicates the fractional kilobyte amount.
547C
AND A,03H E6 03
Mask Register A with 03H to extract the lower 2 bits. These represent the fractional quarter-kilobyte: 00 = .0, 01 = .2(50), 10 = .5, 11 = .8 (approximating .25, .5, .75).
547E
LD B,A 47
Copy the fractional value from Register A into Register B for the comparison chain.
547F
LD A,30H 3E 30
Load Register A with 30H (ASCII 0) for the .0 case (zero fractional kilobytes).
5481
If the Z FLAG is set (fraction = 0, file size is a whole number of kilobytes), JUMP to 548FH to store 0 as the decimal digit.
5483
DEC B 05
DECrement B. If B was 1 (one quarter = 0.25 KB), B becomes 0 and the Z FLAG is set.
5484
LD A,32H 3E 32
Load Register A with 32H (ASCII 2) for the .2 case (approximately 0.25 KB displayed as .2).
5486
If the Z FLAG is set (fraction = 1 quarter), JUMP to 548FH to store 2 as the decimal digit.
5488
DEC B 05
DECrement B again. If B was 2 (two quarters = 0.5 KB), B becomes 0.
5489
LD A,35H 3E 35
Load Register A with 35H (ASCII 5) for the .5 case (0.5 KB).
548B
If the Z FLAG is set (fraction = 2 quarters), JUMP to 548FH to store 5.
548D
LD A,38H 3E 38
Load Register A with 38H (ASCII 8) for the .8 case (approximately 0.75 KB displayed as .8).
548F
LD (56C3H),A 32 C3 56
Store the fractional digit character to 56C3H within the display template. This is the position after the decimal point in the kilobyte size display (e.g., 1.2 K).

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.

5492
LD HL,56C7H 21 C7 56
Point Register Pair HL to 56C7H, one byte before the date display template at 56C8H. This byte is the source for the LDIR initialization fill.
5495
LD DE,56C8H 11 C8 56
Point Register Pair DE to the date display template at 56C8H (10 bytes: DD-MMM-YY + ETX).
5498
LD BC,0009H 01 09 00
Load Register Pair BC with 0009H (9 bytes) as the fill count.
549B
LDIR ED B0
Block copy 9 bytes from 56C7H to 56C8H-56D0H. Since each byte copies from the previous position, this fills the date template area with the byte value at 56C7H (a space, 20H), effectively clearing the date display field.
549D
POP HL E1
Restore Register Pair HL from the stack, recovering the directory entry base pointer in the 4200H buffer.
549E
LD DE,56C8H 11 C8 56
Point Register Pair DE to the date template at 56C8H where the formatted date digits will be written.
54A1
INC HL 23
INCrement HL to advance to offset +01H of the directory entry (flags/attribute byte).
54A2
BIT 7,(HL) CB 7E
Test bit 7 of the flags byte at offset +01H. Bit 7 controls the separator character used in the display: set = colon :, clear = equals =.
54A4
LD A,3DH 3E 3D
Load Register A with 3DH (ASCII =) as the default separator for the SIZ= display.
54A6
If the Z FLAG is set (bit 7 is clear), JUMP to 54AAH to use = as the separator.
54A8
LD A,3AH 3E 3A
Load Register A with 3AH (ASCII :) as the alternate separator when bit 7 is set.
54AA
LD (56BCH),A 32 BC 56
Store the separator character to 56BCH within the size display template. This position is the = or : character in the SIZ= or SIZ: field.
54AD
INC HL 23
INCrement HL to advance to offset +02H of the directory entry (packed date byte).
54AE
LD A,(HL) 7E
Fetch the packed date byte from offset +02H into Register A. This byte encodes the month (bits 7-5) and day (bits 4-0), with zero meaning no date recorded.
54AF
OR A,A B7
Test Register A against itself. If the packed date byte is 00H, no date was recorded for this file.
54B0
If the Z FLAG is set (no date recorded), JUMP to 54FEH to skip the date formatting and proceed directly to displaying the template with blank date fields.

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.

54B2
RRCA 0F
Rotate Register A right by 1 bit. First step of extracting the day field from the packed date byte.
54B3
RRCA 0F
Rotate Register A right by 1 bit again.
54B4
RRCA 0F
Rotate Register A right by 1 bit. After 3 right rotations, the upper 5 bits of the original byte are now in bits 4-0, giving the day value.
54B5
AND A,1FH E6 1F
Mask Register A with 1FH to isolate the 5-bit day value (range 1-31).
54B7
LD B,0FFH 06 FF
Load Register B with FFH as a pre-decrement counter. B will count the tens digit by repeated subtraction of 10.

Convert the binary day value to two ASCII decimal digits via repeated subtraction of 10.

54B9
INC B 04
Loop Start
INCrement the tens-digit counter in Register B.
54BA
SUB A,0AH D6 0A
SUBtract 10 from Register A. If A >= 10, the NO CARRY FLAG is set and the loop continues.
54BC
If the NO CARRY FLAG is set (A was >= 10), LOOP BACK to 54B9H to subtract another 10 and increment the tens digit.
Loop End
54BE
ADD A,0AH C6 0A
ADD 10 back to Register A to undo the last subtraction that caused the carry. A now holds the ones digit (0-9).
54C0
PUSH AF F5
Save the ones digit (in A) onto the stack.
54C1
LD A,B 78
Copy the tens digit from Register B into Register A.
54C2
ADD A,30H C6 30
ADD 30H to convert the tens digit to ASCII.
54C4
LD (DE),A 12
Store the ASCII tens digit to the date template at (DE). DE points to the first position of the DD field at 56C8H.
54C5
INC DE 13
INCrement DE to advance to the ones digit position.
54C6
POP AF F1
Restore the ones digit from the stack into Register A.
54C7
ADD A,30H C6 30
ADD 30H to convert the ones digit to ASCII.
54C9
LD (DE),A 12
Store the ASCII ones digit to the date template.
54CA
INC DE 13
INCrement DE past the ones digit.
54CB
INC DE 13
INCrement DE again to skip over the dash separator character between DD and MMM, advancing to the month name field.

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.

54CC
PUSH HL E5
Save Register Pair HL onto the stack (currently pointing to the date byte at offset +02H).
54CD
DEC HL 2B
DECrement HL to point back to offset +01H (flags byte) which contains the encoded month in its lower nibble.
54CE
LD A,(HL) 7E
Fetch the flags byte from offset +01H into Register A.
54CF
AND A,0FH E6 0F
Mask Register A with 0FH to isolate the lower 4 bits, which contain the month value (1-12).
54D1
DEC A 3D
DECrement A by 1 to convert from 1-based month (1-12) to 0-based index (0-11) for the table lookup.
54D2
LD C,A 4F
Copy the 0-based month index into Register C.
54D3
RLCA 07
Rotate Register A left by 1 bit (multiply by 2).
54D4
ADD A,C 81
ADD C (original index) to Register A. A now holds index x 3, giving the byte offset into the 3-bytes-per-entry month name table.
54D5
LD C,A 4F
Copy the table offset into Register C.
54D6
LD B,00H 06 00
Load Register B with 00H. BC now holds the 16-bit offset into the month name table.
54D8
LD HL,56D2H 21 D2 56
Point Register Pair HL to the month name table at 56D2H. This table contains 12 three-character month abbreviations: JAN, FEB, MAR, ... DEC.
54DB
ADD HL,BC 09
ADD BC (table offset) to HL. HL now points to the 3-character month abbreviation for this file's month.
54DC
LD BC,0003H 01 03 00
Load Register Pair BC with 0003H (3 bytes) as the copy count for the month name.
54DF
LDIR ED B0
Block copy 3 bytes from (HL) to (DE). This copies the month abbreviation (e.g., OCT) into the MMM field of the date template at 56CBH-56CDH.
54E1
INC DE 13
INCrement DE to skip past the dash separator between MMM and YY.
54E2
LD A,38H 3E 38
Load Register A with 38H (ASCII 8) as the decade digit. All VTOS dates assume the 1980s (the TRS-80 era), so the year display is 8X.
54E4
LD (DE),A 12
Store 8 as the tens digit of the year to the date template.
54E5
INC DE 13
INCrement DE to advance to the ones digit of the year.
54E6
POP HL E1
Restore Register Pair HL from the stack (points to the date byte at offset +02H).
54E7
PUSH HL E5
Save HL again for later use.
54E8
LD A,(HL) 7E
Fetch the packed date byte from offset +02H into Register A again.
54E9
AND A,07H E6 07
Mask Register A with 07H to extract bits 2-0, which contain the year's ones digit (0-7, representing 1980-1987).
54EB
ADD A,30H C6 30
ADD 30H to convert the year ones digit to ASCII.
54ED
LD (DE),A 12
Store the ASCII year ones digit to the date template at (DE).
54EE
POP HL E1
Restore Register Pair HL from the stack.
54EF
DEC HL 2B
DECrement HL to point to offset +01H (flags byte).
54F0
BIT 4,(HL) CB 66
Test bit 4 of the flags byte at offset +01H. Bit 4 determines the sign character used in the date display (+ for created, - for modified, or similar).
54F2
LD A,2DH 3E 2D
Load Register A with 2DH (ASCII -) as the default sign character.
54F4
If the Z FLAG is set (bit 4 is clear), JUMP to 54F8H to use - as the sign character.
54F6
LD A,2BH 3E 2B
Load Register A with 2BH (ASCII +) as the sign character when bit 4 is set.
54F8
LD (56CAH),A 32 CA 56
Store the sign character to 56CAH, the position of the first sign character in the date template (between DD and MMM).
54FB
LD (56CEH),A 32 CE 56
Store the same sign character to 56CEH, the position of the second sign character (between MMM and YY). Both separators use the same character.
54FE
LD HL,56A6H 21 A6 56
Point Register Pair HL to the complete attribute/size display template at 56A6H. This template now contains the formatted protection name, EOF count, kilobyte size, and date (or blank date fields if no date was recorded).
5501
GOSUB to the display string subroutine at 556CH to output the complete attribute/size/date line to the screen (and printer if active).
5504
LD A,01H 3E 01
Load Register A with 01H to indicate that at least one entry line was displayed in the current page group.
5506
LD (53F5H),A 32 F5 53
Self-Modifying Code
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.
5509
RET C9
RETURN to the caller at 53F1H. The file size and date display is complete for this entry.

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.

550A
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H to initialize the total sector count accumulator to zero.
550D
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H to initialize the extent count to zero.
5510
LD A,L 7D
Copy Register L into Register A to compute the offset to the extent chain data within the directory entry.
5511
ADD A,16H C6 16
ADD 16H to Register A. Offset +16H within the directory entry is the start of the extent chain data (2-byte pairs beginning at entry base + 16H).
5513
LD L,A 6F
Store the result into Register L. HL now points to the first extent record of this directory entry.
5514
LD A,(HL) 7E
Loop Start
Fetch the first byte of the current extent record from (HL) into Register A.
5515
INC L 2C
INCrement Register L to advance to the second byte of the extent record.
5516
CP A,0FEH FE FE
Compare Register A against FEH. Values FEH and above are sentinels marking the end of the extent chain. FEH = end of chain within this sector, FFH = chain continues in another sector.
5518
If the NO CARRY FLAG is set (A >= FEH, extent chain sentinel found), JUMP to 5527H to handle the end-of-chain or continuation.
551A
INC BC 03
INCrement the extent count in BC by 1.
551B
LD A,(HL) 7E
Fetch the second byte of the extent record from (HL) into Register A. The lower 5 bits (AND 1FH) encode the granule count for this extent.
551C
INC L 2C
INCrement Register L to advance to the next extent record.
551D
AND A,1FH E6 1F
Mask Register A with 1FH to extract the 5-bit granule count for this extent (range 0-31).
551F
INC A 3C
INCrement A by 1. The granule count is stored 0-based, so adding 1 gives the actual number of granules in this extent.
5520
ADD A,E 83
ADD the granule count to Register E (low byte of the sector count accumulator).
5521
LD E,A 5F
Store the result back into Register E.
5522
If the NO CARRY FLAG is set (no overflow from the addition), LOOP BACK to 5514H to process the next extent record.
5524
INC D 14
INCrement Register D (high byte of sector count) to propagate the carry from the ADD above.
5525
JUMP BACK to 5514H to continue processing extent records after propagating the carry.
5527
RET NZ C0
If the NZ FLAG is set (A was FFH, not FEH), RETURN to caller. FFH means the chain has ended with no continuation. DE holds the total sector count, BC holds the extent count.

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.

5528
LD A,(HL) 7E
Fetch the continuation byte (second byte of the FEH sentinel record) into Register A. The lower 5 bits encode the entry index, and the upper bits encode additional information.
5529
AND A,1FH E6 1F
Mask Register A with 1FH to extract the 5-bit continuation sector/entry index.
552B
PUSH AF F5
Save the continuation index onto the stack.
552C
XOR A,(HL) AE
XOR Register A with the byte at (HL). Since A still holds the masked value, XOR recovers the upper bits that were masked off, giving the sector identifier portion.
552D
NOP 00
No operation. Padding byte (may have been a removed instruction during development).
552E
NOP 00
No operation. Padding byte.
552F
LD L,A 6F
Store the sector identifier portion into Register L. This will be used to compute the entry base offset in the continuation sector.
5530
POP AF F1
Restore the continuation index from the stack into Register A.
5531
PUSH HL E5
Save Register Pair HL onto the stack.
5532
LD HL,5314H 21 14 53
Point Register Pair HL to the current directory sector index variable at 5314H (the self-modifying operand of CP A,nn at 5313H).
5535
CP A,(HL) BE
Compare Register A (continuation sector index) against the current sector index stored at 5314H. If they match, the continuation entry is in the same sector already loaded.
5536
POP HL E1
Restore Register Pair HL from the stack.
5537
LD H,42H 26 42
Load Register H with 42H. HL now points into the primary sector buffer at 4200H at the computed entry offset.
5539
If the Z FLAG is set (continuation is in the same sector already loaded), JUMP BACK to 5510H to continue the extent walk without reading a new sector.
553B
CP A,00H FE 00
Self-Modifying Code
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.
553D
LD H,59H 26 59
Load Register H with 59H. If the continuation sector is in the extended buffer at 5900H, HL will point there instead of 4200H.
553F
If the Z FLAG is set (continuation sector already in the 5900H buffer), JUMP BACK to 5520H to continue accumulating sector counts from that buffer.
5541
LD (553CH),A 32 3C 55
Self-Modifying Code
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.
5544
PUSH BC C5
Save Register Pair BC (extent count) onto the stack before the sector read.
5545
PUSH DE D5
Save Register Pair DE (total sector count accumulator) onto the stack.
5546
OR A,L B5
OR Register L into Register A to combine the sector index with the entry offset information, forming the sector number to read.
5547
LD B,A 47
Copy the combined value into Register B for the directory read setup.
5548
LD A,(568AH) 3A 8A 56
Fetch the ASCII drive digit from the header template at 568AH. This was stored by 5271H during initialization.
554B
SUB A,30H D6 30
SUBtract 30H to convert the ASCII digit back to a binary drive number (0-7).
554D
LD C,A 4F
Copy the drive number into Register C for the SYS0 call.
554E
GOSUB to SYS0 routine at 4B65H to set up the track and sector for directory I/O on the specified drive.
5551
LD A,B 78
Copy Register B (sector number) into Register A.
5552
AND A,1FH E6 1F
Mask Register A with 1FH to extract the 5-bit sector number.
5554
ADD A,02H C6 02
ADD 02H to Register A. Directory file-entry sectors start at sector 2 (sector 0 is GAT, sector 1 is the first file-entry sector already read), so the continuation index is offset by 2.
5556
LD E,A 5F
Copy the adjusted sector number into Register E for the SYS0 read call.
5557
LD HL,5900H 21 00 59
Point Register Pair HL to the extended sector buffer at 5900H. The continuation directory sector will be read into this buffer.
555A
GOSUB to SYS0 routine at 4B45H to read the continuation directory sector into the buffer at 5900H.
555D
LD A,11H 3E 11
Load Register A with 11H as a pre-loaded error code (file read error) in case the read fails.
555F
If the NZ FLAG is set (sector read failed), JUMP to 559DH to report error code 11H via the DOS error exit.
5562
LD A,B 78
Copy Register B (original combined value) into Register A to extract the entry offset portion.
5563
AND A,0E0H E6 E0
Mask Register A with E0H to extract the entry-aligned offset within the sector (upper 3 bits of the low byte).
5565
ADD A,16H C6 16
ADD 16H to reach the extent chain data area at offset +16H of the continuation entry.
5567
LD L,A 6F
Store the offset into Register L. With H=59H from the read, HL now points to the extent chain data in the continuation sector buffer at 5900H.
5568
POP DE D1
Restore Register Pair DE (total sector count accumulator) from the stack.
5569
POP BC C1
Restore Register Pair BC (extent count) from the stack.
556A
JUMP BACK to 5514H to continue walking the extent chain from the newly loaded continuation sector.

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.

556C
GOSUB to SYS0 display message routine at 4467H. On entry, HL points to a null-terminated or ETX-terminated string to display on the screen.
556F
LD A,(557DH) 3A 7D 55
Fetch the printer-active flag from 557DH into Register A. This address is the low byte of the LD DE,0000H operand at 557CH. When this byte is non-zero, printer output is active.
5572
OR A,A B7
Test Register A against itself. If the printer flag is 00H (printer not active), the Z FLAG is set.
5573
If the NZ FLAG is set (printer is active), JUMP to SYS0 routine at 446AH to also output the string to the printer, then return to the original caller.
5576
RET C9
RETURN to caller. The string was displayed on screen only (printer not active).

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.

5577
PUSH AF F5
Save Register Pair AF onto the stack, preserving the character to output in A and the current flags.
5578
GOSUB to the ROM character output routine at 0033H. This displays the character in Register A at the current cursor position on the screen and advances the cursor.
557B
POP AF F1
Restore Register Pair AF from the stack, recovering the original character and flags.
557C
LD DE,0000H 11 00 00
Self-Modifying Code
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.
557F
INC E 1C
INCrement Register E by 1, advancing the character counter. Note this only increments the register, not the memory location.
5580
If the Z FLAG is set (E overflowed from FFH to 00H, meaning 256 characters have been output), JUMP to the ROM printer output routine at 003BH. This triggers a line-end operation on the printer.
5583
RET C9
RETURN to caller. The character was output to the screen; the counter has not yet reached 256.

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.

5584
LD HL,558DH 21 8D 55
Point Register Pair HL to the PARAMETER ERROR string at 558DH.
5587
GOSUB to SYS0 display routine at 447BH to display the error message with CONFIG/SYS processing.
558A
JUMP to the SYS0 error-already-displayed exit at 4030H. The error message has been shown; return to the DOS READY prompt.

558DH - "PARAMETER ERROR" String

ASCII string: PARAMETER ERROR followed by a carriage return (0DH).

558D-559C
DEFM "PARAMETER ERROR" + 0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
ASCII text: PARAMETER ERROR terminated by a carriage return (0DH). Displayed by 5584H when the filespec is invalid.

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.

559D
OR A,40H F6 40
OR 40H into Register A, setting bit 6 of the error code. Bit 6 tells the SYS4 error handler to suppress the extended context display (filename, return address) since the error was already identified by context.
559F
JUMP to the SYS0 DOS error exit at 4409H. Register A contains the error code with bit 6 set. The error handler displays the appropriate error message and returns to the DOS READY prompt.

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.

55A2
LD A,20H 3E 20
Load Register A with 20H (ASCII space) as the initial leading-zero suppression character. The first call to 55C2H will output spaces for leading zero positions.
55A4
LD BC,2710H 01 10 27
Load Register Pair BC with 2710H (10000 decimal) as the first divisor for the ten-thousands digit.
55A7
GOSUB to the decimal digit extraction routine at 55C2H. Divides HL by 10000, writes the digit (or space if leading zero) to (DE), advances DE.
55AA
LD BC,03E8H 01 E8 03
Load Register Pair BC with 03E8H (1000 decimal) as the divisor for the thousands digit.
55AD
GOSUB to 55C2H. Extracts the thousands digit.
55B0
LD BC,0064H 01 64 00
Load Register Pair BC with 0064H (100 decimal) as the divisor for the hundreds digit.
55B3
GOSUB to 55C2H. Extracts the hundreds digit.
55B6
LD BC,000AH 01 0A 00
Load Register Pair BC with 000AH (10 decimal) as the divisor for the tens digit.
55B9
GOSUB to 55C2H. Extracts the tens digit.
55BC
LD A,L 7D
Copy the remaining value in Register L into Register A. After four divisions, L holds the ones digit (0-9).
55BD
ADD A,30H C6 30
ADD 30H to convert the ones digit to ASCII.
55BF
LD (DE),A 12
Store the ASCII ones digit to the output buffer at (DE).
55C0
INC DE 13
INCrement DE past the ones digit.
55C1
RET C9
RETURN to caller. The 5-digit decimal ASCII string has been written to the output buffer.

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.

55C2
PUSH DE D5
Save Register Pair DE onto the stack. DE points to the output buffer position.
55C3
LD E,A 5F
Copy the leading-zero character (initially space 20H) from Register A into Register E for safekeeping.
55C4
LD D,0FFH 16 FF
Load Register D with FFH as a pre-decrement digit counter. The first INC D will make it 00H.
55C6
XOR A,A AF
Set Register A to 00H and clear the carry flag. This prepares for the SBC subtraction.
55C7
INC D 14
Loop Start
INCrement the digit counter in Register D.
55C8
SBC HL,BC ED 42
SUBtract BC (current power of 10) from HL with borrow. If HL >= BC, the result is non-negative and the NO CARRY FLAG is set.
55CA
If the NO CARRY FLAG is set (HL was >= BC), LOOP BACK to 55C7H to subtract again and increment the digit.
Loop End
55CC
ADD HL,BC 09
ADD BC back to HL to undo the last subtraction that caused the borrow. HL now contains the remainder.
55CD
LD A,E 7B
Copy the leading-zero character from Register E into Register A. This is either space (20H) or 0 (30H).
55CE
LD B,D 42
Copy the digit count from Register D into Register B for the zero-check below.
55CF
POP DE D1
Restore Register Pair DE from the stack (output buffer pointer).
55D0
LD (DE),A 12
Store the leading-zero character (space or 0) to the output buffer at (DE). This will be overwritten below if the digit is non-zero.
55D1
INC B 04
INCrement B (which was loaded from D, the digit count). If B was 0 (digit is zero), it becomes 1 (NZ).
55D2
DEC B 05
DECrement B back. If the digit was 0, B returns to 0 and the Z FLAG is set. If non-zero, the Z FLAG is clear. This INC/DEC pair tests whether the digit is zero without modifying it.
55D3
If the Z FLAG is set (digit is zero), JUMP to 55DBH to skip the digit output and keep the space (leading zero suppression active).
55D5
LD A,B 78
Copy the non-zero digit count from Register B into Register A.
55D6
ADD A,30H C6 30
ADD 30H to convert the digit to ASCII (1-9).
55D8
LD (DE),A 12
Store the ASCII digit to the output buffer, overwriting the space written at 55D0H.
55D9
LD A,30H 3E 30
Load Register A with 30H (ASCII 0). Now that a significant digit has been found, switch the leading-zero character to 0 so subsequent zero digits display as 0 instead of spaces.
55DB
INC DE 13
INCrement DE to advance to the next output buffer position.
55DC
RET C9
RETURN to the caller at 55A2H. Register A holds the current leading-zero character (space or 0) for the next digit extraction call.

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.

55DD
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H to initialize the quotient accumulator to zero.
55E0
LD A,C 79
Copy the divisor from Register C into Register A for testing.
55E1
OR A,A B7
Test Register A against itself. If the divisor is 0, the Z FLAG is set.
55E2
If the Z FLAG is set (divisor is zero), JUMP to 55FFH to read a 16-bit value directly from (HL) instead of dividing. This handles the edge case where sectors-per-granule is unknown.
55E4
XOR A,A AF
Set Register A to 00H, clearing the running remainder accumulator for the long division.
55E5
LD B,03H 06 03
Load Register B with 03H (3 bytes) as the outer loop count. The 24-bit dividend is processed one byte at a time.
55E7
PUSH BC C5
Outer Loop Start
Save the outer loop counter onto the stack.
55E8
PUSH HL E5
Save the current byte pointer onto the stack.
55E9
LD H,(HL) 66
Fetch the current dividend byte from (HL) into Register H. The bytes are processed high to low.
55EA
LD B,08H 06 08
Load Register B with 08H (8 bits) as the inner loop count for processing each bit of the current byte.
55EC
EX DE,HL EB
Inner Loop Start
Exchange DE and HL. HL now holds the quotient accumulator, DE holds H=dividend-byte, E was the low quotient byte.
55ED
ADD HL,HL 29
Shift the quotient accumulator HL left by 1 bit (multiply by 2), making room for the next quotient bit.
55EE
EX DE,HL EB
Exchange back. DE = shifted quotient, HL restored.
55EF
RLC H CB 04
Rotate Register H (current dividend byte) left by 1 bit. The high bit shifts into the carry flag.
55F1
RLA 17
Rotate Register A left through carry. The carry from H feeds into A (the running remainder), and A's high bit goes to carry.
55F2
CP A,C B9
Compare Register A (running remainder) against Register C (divisor). If A >= C, the CARRY FLAG is clear and we can subtract.
55F3
If the CARRY FLAG is set (remainder < divisor), JUMP to 55F7H to skip the subtraction. This quotient bit is 0.
55F5
SUB A,C 91
SUBtract the divisor C from the remainder A. The remainder is reduced.
55F6
INC DE 13
INCrement DE (quotient). This sets the current quotient bit to 1 (since DE was shifted left at 55EDH, the low bit was 0 and INC makes it 1).
55F7
DECrement B and LOOP BACK to 55ECH if not zero. Process all 8 bits of the current dividend byte.
Inner Loop End
55F9
POP HL E1
Restore the byte pointer from the stack.
55FA
INC HL 23
INCrement HL to advance to the next byte of the 24-bit dividend (processing high byte to low byte).
55FB
POP BC C1
Restore the outer loop counter from the stack.
55FC
DECrement B and LOOP BACK to 55E7H if not zero. Process all 3 bytes of the 24-bit dividend.
Outer Loop End
55FE
RET C9
RETURN to caller. DE holds the 16-bit quotient (24-bit dividend divided by 8-bit divisor).
55FF
LD D,(HL) 56
Fetch the high byte from (HL) into Register D. This is the shortcut path when the divisor is zero: simply read a 16-bit value from the dividend location.
5600
INC HL 23
INCrement HL to advance to the next byte.
5601
LD E,(HL) 5E
Fetch the low byte from (HL) into Register E. DE now holds the 16-bit value read directly from the dividend bytes.
5602
RET C9
RETURN to caller with the 16-bit value in DE.

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.

5603
LD A,(430FH) 3A 0F 43
Fetch the system state flags from 430FH into Register A. This is the multi-purpose system flags register documented in the VTOS memory map.
5606
AND A,20H E6 20
Mask Register A with 20H to isolate bit 5 (re-entry mode / debug-library branching flag).
5608
XOR A,20H EE 20
XOR Register A with 20H. If bit 5 was set, the result is 00H (Z flag set). If bit 5 was clear, the result is 20H (NZ). This inverts the test: returns Z (no abort) when bit 5 is set.
560A
RET Z C8
If the Z FLAG is set (bit 5 of 430FH was set, re-entry mode active), RETURN immediately. In re-entry mode, keyboard pause checking is skipped to allow uninterrupted output.
560B
LD A,(3840H) 3A 40 38
Fetch the keyboard row 6 status from the memory-mapped keyboard port at 3840H. This row contains ENTER, CLEAR, BREAK, and arrow keys.
560E
AND A,04H E6 04
Mask Register A with 04H to isolate bit 2 (the BREAK key). A non-zero result means BREAK is currently pressed.
5610
RET NZ C0
If the NZ FLAG is set (BREAK key is pressed), RETURN immediately with NZ status to signal that the user wants to abort the listing.
5611
LD A,(3801H) 3A 01 38
Fetch keyboard row 0 status from 3801H. This row contains @, A-G.
5614
AND A,01H E6 01
Mask Register A with 01H to isolate bit 0 (the @ key). This tests whether any key on this row is pressed as a secondary pause trigger.
5616
RET Z C8
If the Z FLAG is set (no key pressed on row 0), RETURN with Z status to signal no pause requested.
5617
LD A,(3880H) 3A 80 38
Fetch keyboard row 7 status from 3880H. This row contains the SHIFT key.
561A
AND A,01H E6 01
Mask Register A with 01H to isolate bit 0 (SHIFT key status).
561C
RET Z C8
If the Z FLAG is set (SHIFT not pressed), RETURN with Z status. No pause or abort requested.

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.

561D
LD A,(3840H) 3A 40 38
Loop Start
Fetch keyboard row 6 status from 3840H to check for BREAK key during the pause wait.
5620
AND A,04H E6 04
Mask Register A with 04H to isolate the BREAK key bit.
5622
RET NZ C0
If the NZ FLAG is set (BREAK pressed during pause), RETURN with NZ to abort the listing.
5623
GOSUB to the ROM keypress wait routine at 002BH. Waits for a keypress and returns the character in Register A. Returns 00H if no key was pressed.
5626
OR A,A B7
Test Register A against itself. If no key was pressed, A is 00H and the Z FLAG is set.
5627
If the Z FLAG is set (no keypress yet), LOOP BACK to 561DH to continue waiting.
5629
CP A,60H FE 60
Compare Register A against 60H. Character 60H is the backtick/grave accent character, which may serve as a special abort key in VTOS.
562B
If the Z FLAG is set (user pressed the backtick key), LOOP BACK to 561DH to ignore this key and continue waiting for a different key.
Loop End
562D
XOR A,A AF
Set Register A to 00H and set the Z FLAG. The user pressed a valid key to resume the listing.
562E
RET C9
RETURN with Z flag set (no abort). The listing will resume after the pause.

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.

562F-564F
DEFM "A " + EDH + "SI @SP }US 2S" + 00H 41 20 20 20 20 ED 53 49 20 20 20 20 20 40 53 50 20 20 20 20 20 7D 55 53 20 20 20 20 20 32 53 00
Column header template for directory listing display. Contains abbreviated column labels: A (Access), SI (System/Invisible), SP (Space/Size), US (Usage), S (Status). Semigraphics bytes EDH, 40H, 7DH, 32H serve as column dividers. Terminated by 00H.

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.

5650-5653
DEFM "ALL " 41 4C 4C 20
Level 0: ALL - Full access (no restrictions). Displayed for unprotected files.
5654-5657
DEFM "KILL" 4B 49 4C 4C
Level 1: KILL - Kill-only access.
5658-565B
DEFM "NAME" 4E 41 4D 45
Level 2: NAME - Name-only access (visible but not readable).
565C-565F
DEFM "~~~~" 7E 7E 7E 7E
Level 3: ~~~~ - Reserved/unused level (displayed as tildes).
5660-5663
DEFM "WRIT" 57 52 49 54
Level 4: WRIT - Write access.
5664-5667
DEFM "READ" 52 45 41 44
Level 5: READ - Read-only access.
5668-566B
DEFM "EXEC" 45 58 45 43
Level 6: EXEC - Execute-only access.
566C-566F
DEFM "NO " 4E 4F 20 20
Level 7: NO - No access (fully locked).

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.

5670-5690
DEFM 0AH + "FILE DIRECTORY --- DRIVE X " + 03H 0A 46 49 4C 45 20 44 49 52 45 43 54 4F 52 59 20 2D 2D 2D 20 44 52 49 56 45 20 58 20 20 20 20 20 03
Directory header banner: linefeed (0AH) + FILE DIRECTORY --- DRIVE X + padding spaces + ETX (03H). The X at 568AH is replaced with the actual drive digit (0-7) by the initialization code at 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).

5691-56A5
DEFM "NNNNNNNN -- MM/DD/YY" + 0DH 4E 4E 4E 4E 4E 4E 4E 4E 20 2D 2D 20 4D 4D 2F 44 44 2F 59 59 0D
Disk name and date display template: 8 N placeholders for the disk name (overwritten from GAT offset D0H), separator -- , 8 bytes for the date MM/DD/YY (overwritten from GAT offset D8H), terminated by carriage return (0DH). The N and date placeholders are replaced by LDIR copy operations at 5299H and 52A1H.

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.

56A6-56B0
DEFM "PROT / EOF=" 50 52 4F 54 20 2F 20 45 4F 46 3D
Attribute line prefix: PROT / EOF=. The first 4 bytes (PROT) are overwritten by the LDIR at 542DH with the actual protection level name from the table at 5650H (e.g., ALL , EXEC, READ).
56B1-56C6
DEFM " / SIZ? . K " 20 20 20 20 20 20 2F 20 53 49 5A 3F 20 20 20 20 20 2E 20 20 4B 20
Size display portion: 5 spaces for the EOF sector count (filled by 55A2H), / SIZ, separator at 56BCH (= or :), 5 spaces for the kilobyte count (filled by 55A2H at 5478H), decimal point, fractional digit at 56C3H, K suffix. The ? at 56BCH is a placeholder replaced by the separator character computed at 54AAH.
56C7
DEFB 20H 20
Space byte used as the source for the LDIR fill at 549BH that initializes the date template area below.
56C8-56D1
DEFM "DD-MMM-YY" + 03H 44 44 2D 4D 4D 4D 2D 59 59 03
Date display template: DD-MMM-YY + ETX (03H). The DD digits are written at 56C8H-56C9H, the MMM abbreviation at 56CBH-56CDH, and the YY at 56CFH-56D0H by the date formatting code at 54B7H-54EDH. The dash separators at 56CAH and 56CEH are replaced with + or - by 54F8H-54FBH.

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.

56D2-56D4
DEFM "JAN" 4A 41 4E
Month 1: JAN (January).
56D5-56D7
DEFM "FEB" 46 45 42
Month 2: FEB (February).
56D8-56DA
DEFM "MAR" 4D 41 52
Month 3: MAR (March).
56DB-56DD
DEFM "APR" 41 50 52
Month 4: APR (April).
56DE-56E0
DEFM "MAY" 4D 41 59
Month 5: MAY (May).
56E1-56E3
DEFM "JUN" 4A 55 4E
Month 6: JUN (June).
56E4-56E6
DEFM "JUL" 4A 55 4C
Month 7: JUL (July).
56E7-56E9
DEFM "AUG" 41 55 47
Month 8: AUG (August).
56EA-56EC
DEFM "SEP" 53 45 50
Month 9: SEP (September).
56ED-56EF
DEFM "OCT" 4F 43 54
Month 10: OCT (October).
56F0-56F2
DEFM "NOV" 4E 4F 56
Month 11: NOV (November).
56F3-56F5
DEFM "DEC" 44 45 43
Month 12: DEC (December).

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.

56F6-56FD
DEFM " " 20 20 20 20 20 20 20 20
Filename search buffer (8 bytes, space-filled). Written by the parser at 522AH. Spaces match any character during the comparison at 5354H.
56FE-5700
DEFM " " + 00H 20 20 20 00
Extension search buffer (3 bytes, space-filled) followed by a null terminator. Written by the parser at 5247H.

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.

5200
LD BC,0000H 01 00 00
Load BC with 0000H. BC will hold the initial drive number (C=00H = Drive 0) and a zero for the file count field.
5203
LD (5312H),BC ED 43 12 53
Self-Modifying Code
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.
5207
Point DE to the filespec template at 5389H ("P \x12S\x00") used by the drive-open call below.
520A
GOSUB to SYS0 routine at 4476H to open the drive filespec pointed to by DE. On return, NZ = error (drive not found or cannot open).
520D
If the NZ FLAG is set (open failed), JUMP to 5319H to display "PARAMETER ERROR" and exit.
5210
LD C,00H 0E 00
Load C with 00H. C is the drive counter; start at Drive 0.

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.

5212
LD A,(5312H) 3A 12 53
Fetch the low byte of the accumulated drive-info word from 5312H into A.
5215
OR A,A B7
Test A against itself. If A is zero (no drives printed yet), the Z FLAG is set; otherwise NZ.
5216
If the Z FLAG is set (first drive, nothing printed yet), JUMP to 5222H skipping the separator output.
5218
LD A,20H 3E 20
Load A with 20H (ASCII space ).
521A
CALL 003BH CD 3B 00
GOSUB to ROM routine at 003BH to print the space character in A to the printer/output device.
521D
LD A,0DH 3E 0D
Load A with 0DH (ASCII carriage return).
521F
CALL 003BH CD 3B 00
GOSUB to ROM routine at 003BH to output the carriage return, moving to a new line.

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.

5222
PUSH BC C5
Save BC (drive counter in C) onto the stack.
5223
GOSUB to SYS0 routine at 44B8H to query whether the drive whose number is in C is mounted and ready. On return, NZ = drive not present.
5226
If the NZ FLAG is set (drive not present), JUMP to 5303H to restore BC, increment the drive counter, and loop.

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.

5229
LD A,C 79
Load A with C (current drive number, 0-7).
522A
ADD A,30H C6 30
ADD 30H to A, converting binary drive number to ASCII digit (0-7).
522C
LD (5398H),A 32 98 53
Self-Modifying Code
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.

522F
GOSUB to local routine at 5376H to read the GAT sector (track 0, sector 0) of the current drive into the buffer at 4200H. On return, NZ = read error.
5232
If the NZ FLAG is set (read error, A = 14H), JUMP to 5332H to OR the error code with 40H and jump to the DOS error exit at 4409H.

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.

5235
GOSUB to SYS0 routine at 478FH to select the drive and point IY to the 10-byte drive parameter block for the current drive.
5238
LD H,00H 26 00
Load H with 00H. HL will hold the drive number as a 16-bit value for use as a multiplier.
523A
LD L,(IY+06H) FD 6E 06
Load L with the drive number byte from the drive parameter block at IY+06H. HL now = drive number (0-3).
523D
INC HL 23
INCrement HL by 1. Converts the 0-based drive number to a 1-based count for the multiply.
523E
LD A,(IY+08H) FD 7E 08
Load A with the sector-count/interleave byte from the drive parameter block at IY+08H.
5241
AND A,1FH E6 1F
AND A with 1FH, masking off the upper 3 bits to isolate the sectors-per-track field (bits 4-0).
5243
INC A 3C
INCrement A by 1. Converts the 0-based sector count to a 1-based value.
5244
LD E,A 5F
Load E with A (sectors per track, 1-based). Saved for the multiply below.
5245
PUSH AF F5
Save AF (sectors-per-track value) onto the stack for later use.
5246
LD A,(IY+08H) FD 7E 08
Reload A with the sector-count/interleave byte from IY+08H.
5249
AND A,0E0H E6 E0
AND A with E0H, isolating the track-count field in bits 7-5.
524B
RLCA 07
Rotate A Left Circular. Shifts the 3-bit track-count field one position towards bit 0.
524C
RLCA 07
Rotate A Left Circular (second shift).
524D
RLCA 07
Rotate A Left Circular (third shift). After three RLCAs the track-count field is now in bits 2-0, giving the raw track count (e.g., 0=35 tracks, 1=40 tracks in the drive flags encoding).
524E
INC A 3C
INCrement A by 1. Converts the 0-based track-count code to a 1-based multiplier.
524F
GOSUB to SYS0 routine at 4B6CH to compute HL:A = DE * C (24-bit multiply). This multiplies the sectors-per-track (DE=E) by the track count (result in HL:A = total sectors on the disk).
5252
BIT 5,(IY+04H) FD CB 04 6E
Test Bit 5 of the drive flags byte at IY+04H. Bit 5 = 1 indicates the drive is double-sided.
5256
If the Z FLAG is set (Bit 5 is clear = single-sided drive), JUMP to 5259H skipping the doubling step.
5258
ADD A,A 87
ADD A to itself, doubling the total sector count to account for both sides of a double-sided disk.

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.

5259
PUSH AF F5
Save AF (total sector count) onto the stack.
525A
PUSH HL E5
Save HL onto the stack.
525B
LD HL,0100H 21 00 01
Load HL with 0100H (256). This is the sector size in bytes, used to convert sector counts to bytes.
525E
SUB A,02H D6 02
SUBtract 02H from A (total sector count). Adjusts for the 2 reserved sectors (track 0, sectors 0 and 1).
5260
CP A,1EH FE 1E
Compare A against 1EH (30). If A >= 30, the CARRY FLAG is clear; if A < 30, the CARRY FLAG is set.
5262
If the NO CARRY FLAG is set (sector count >= 30, i.e. the disk is large enough to use a scaled calculation), JUMP to 526DH skipping the shift-based scaling.
5264
SLA A CB 27
Shift Left Arithmetic: multiply A by 2. First of three left-shifts to scale the sector count.
5266
SLA A CB 27
Shift Left Arithmetic: multiply A by 2 (second shift; A now * 4).
5268
SLA A CB 27
Shift Left Arithmetic: multiply A by 2 (third shift; A now * 8). With 256-byte sectors, 8 sectors = 2048 bytes = 2K, so this converts the sector count to a kilobyte value for small disks.
526A
LD L,A 6F
Load L with the scaled kilobyte value in A.
526B
LD H,00H 26 00
Load H with 00H, making HL the 16-bit kilobyte total.

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.

526D
LD (52E6H),HL 22 E6 52
Self-Modifying Code
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.
5270
Point DE to the output buffer position at 53B9H ("XXX, SPACE=" placeholder area) where the total kilobytes digits will be written.
5273
GOSUB to local routine at 5337H (single-space prefix variant of the decimal formatter) to write the total kilobytes as a decimal number into the buffer at DE.
5276
POP HL E1
Restore HL from the stack (saved at 525AH).
5277
POP AF F1
Restore AF from the stack (saved at 5259H).

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.

5278
GOSUB to SYS0 routine at 4B8FH to multiply A by the sectors-per-granule value. On return, L = result (granules converted to sectors).
527B
LD H,L 65
Load H with L (copy the result byte into H).
527C
LD L,A 6F
Load L with A. HL now forms the 16-bit sector count from the multiply result.
527D
INC HL 23
INCrement HL by 1.
527E
INC HL 23
INCrement HL by 1. Two increments adjust for the 2 reserved sectors.
527F
SRL H CB 3C
Shift Right Logical H. First of two right-shifts to divide HL by 4, beginning the conversion from sectors to kilobytes (256-byte sectors: 4 sectors = 1K).
5281
RR L CB 1D
Rotate Right through carry into L (propagates the carry from H into the high bit of L).
5283
SRL H CB 3C
Shift Right Logical H (second shift; H now divided by 4).
5285
RR L CB 1D
Rotate Right through carry into L (completes the 16-bit divide by 4). HL now holds the free space in kilobytes.
5287
Point DE to the free-space number field in the display template at 53CAH (the leading spaces before the free-KB digits).
528A
GOSUB to local routine at 533BH (decimal formatter with leading-space suppression) to write the free kilobytes as a decimal number into the buffer at DE.

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.

528D
LD HL,42D0H 21 D0 42
Point HL to 42D0H, the drive name/label field within the GAT sector buffer (loaded at 4200H).
5290
Point DE to 539CH, the 8-byte name placeholder ("NNNNNNNN") in the display template.
5293
LD BC,0008H 01 08 00
Load BC with 0008H (8 bytes to copy).
5296
LDIR ED B0
Block Move: copy 8 bytes from (HL)=42D0H to (DE)=539CH, INCrementing both pointers. This copies the drive name from the GAT buffer into the display template.
5298
Point DE to 53A5H, the 8-byte date placeholder ("MM/DD/YY") in the display template. HL is already pointing to 42D8H (the date field in the GAT buffer) after the LDIR.
529B
LD BC,0008H 01 08 00
Load BC with 0008H (8 bytes to copy).
529E
LDIR ED B0
Block Move: copy 8 bytes from (HL)=42D8H to (DE)=53A5H, INCrementing both pointers. This copies the disk date string from the GAT buffer into the display template.

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.

52A0
LD HL,4200H 21 00 42
Point HL to 4200H, the start of the GAT sector buffer.
52A3
LD DE,0000H 11 00 00
Load DE with 0000H. DE will accumulate the count of used directory entries.
52A6
LD A,(42CCH) 3A CC 42
Fetch the GAT configuration byte from 42CCH (the disk type/configuration field in the GAT sector). This byte determines the directory entry count.
52A9
ADD A,23H C6 23
ADD 23H to A. This computes the number of directory entries to scan based on the disk configuration byte.
52AB
LD B,A 47
Load B with A. B is the loop counter (number of directory entries to examine).

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.

52AC
LD A,(HL) 7E
Loop Start
Fetch the current directory entry byte from (HL) into A.
52AD
SCF 37
Set the Carry Flag. This primes the carry for the following RRA so that a 1 is shifted into bit 7.
52AE
RRA 1F
Rotate Right through Carry. The original bit 0 of the entry byte goes into the Carry Flag. The pre-set carry (1) shifts into bit 7. If the original bit 0 was clear (entry in use), Carry is now clear.
52AF
If the CARRY FLAG is set (bit 0 of the entry byte was 1 = entry free/unused), JUMP to 52B2H skipping the INC DE count.
52B1
INC DE 13
INCrement DE by 1. Counts this entry as used (bit 0 was clear).

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.

52B2
CP A,0FFH FE FF
Compare A against 0FFH. If A equals FFH (end-of-directory marker), the Z FLAG is set.
52B4
If the NZ FLAG is set (not FFH, more entries remain), LOOP BACK to 52ACH... wait - target is 52ADH. If the NZ FLAG is set (entry is not FFH), JUMP forward to 52B6H is wrong - JR NZ with F7 offset = 52B4 + 2 - 9 = 52ADH. LOOP BACK to 52ADH to fetch the next entry byte.
52B6
INC L 2C
INCrement L (low byte of HL) to advance to the next directory entry byte in the buffer.
52B7
DECrement B and loop back to 52ACH if B is not zero. Loop End
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.

52B9
EX DE,HL EB
Exchange DE and HL. HL now holds the used directory entry count (was in DE); DE now holds the last GAT buffer pointer.
52BA
POP AF F1
Restore AF from the stack (the sectors-per-track value saved at 5245H).
52BB
GOSUB to SYS0 routine at 4B8FH to multiply A (sectors-per-track) by the sectors-per-granule value. On return, L = result.
52BE
LD H,L 65
Load H with L (copy result into high byte of HL).
52BF
LD L,A 6F
Load L with A. HL now forms the 16-bit free-granule sector count.
52C0
INC HL 23
INCrement HL by 1.
52C1
INC HL 23
INCrement HL by 1. Two increments adjust for the 2 reserved sectors.
52C2
SRL H CB 3C
Shift Right Logical H. First of two right-shifts to divide HL by 4, converting sectors to kilobytes.
52C4
RR L CB 1D
Rotate Right through carry into L.
52C6
SRL H CB 3C
Shift Right Logical H (second shift).
52C8
RR L CB 1D
Rotate Right through carry into L. HL now holds the used-files kilobyte equivalent.
52CA
Point DE to 53C4H, the used-space number field in the display template (" /" area).
52CD
GOSUB to local routine at 533BH to write the used-space kilobyte count as a decimal number into the buffer at DE.

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.

52D0
POP BC C1
Restore BC (drive counter in C) from the stack (saved at 5222H).
52D1
PUSH BC C5
Save BC back onto the stack again for the loop-end restore at 5303H.
52D2
GOSUB to SYS0 routine at 4B65H to set up the track and sector registers for a directory sector read of the current drive.
52D5
LD E,01H 1E 01
Load E with 01H. E = sector number 1 (the directory sector).
52D7
LD HL,4200H 21 00 42
Point HL to 4200H, the sector buffer that will receive the directory sector data.
52DA
GOSUB to SYS0 routine at 4B45H to read the directory sector into the buffer at HL=4200H. On return, NZ = read error.
52DD
LD A,16H 3E 16
Load A with 16H. This is the error code to pass to the error exit if the read failed.
52DF
If the NZ FLAG is set (read error), JUMP to 5332H to OR the error code with 40H and jump to the DOS error exit at 4409H.

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.

52E2
LD HL,4200H 21 00 42
Point HL to 4200H, the start of the directory sector buffer.
52E5
LD DE,0000H 11 00 00
Self-Modifying Code
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.
52E8
DEC DE 1B
Loop Start
DECrement DE by 1. DE counts free directory entries, starting at -1 so the first INC DE produces 0.
52E9
LD A,L 7D
Load A with L (low byte of the current buffer pointer HL).
52EA
AND A,0D8H E6 D8
AND A with D8H (11011000 binary). Tests whether HL is aligned to a 32-byte entry boundary within the 256-byte sector. If the result is zero, HL is at an entry boundary.
52EC
If the Z FLAG is set (HL is at a 32-byte entry boundary), JUMP to 52F3H to check whether this entry's first byte is zero (free).
52EE
LD A,(HL) 7E
Fetch the byte at (HL) into A.
52EF
OR A,A B7
Test A against itself. If A is zero (free directory entry), the Z FLAG is set.
52F0
If the NZ FLAG is set (entry not free), JUMP to 52F3H skipping the free-entry count.
52F2
INC DE 13
INCrement DE by 1. Counts this as a free directory entry.

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.

52F3
INC L 2C
INCrement L (low byte of HL) to advance to the next directory buffer byte.
52F4
If the NZ FLAG is set (L has not wrapped to zero, more bytes remain), LOOP BACK to 52E8H. Loop End
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.

52F6
EX DE,HL EB
Exchange DE and HL. HL now holds the free directory entry count (was in DE).
52F7
Point DE to 53B5H, the "XXX/" free-files number field in the display template.
52FA
GOSUB to local routine at 5337H (single-space prefix decimal formatter) to write the free directory entry count into the buffer at DE.
52FD
Point HL to 5392H, the start of the fully formatted drive summary line ("DRIVE X - ...").
5300
GOSUB to local routine at 530EH to display the message at HL via 4467H and then advance the file-count accumulator at 5312H.

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.

5303
POP BC C1
Restore BC (drive counter in C) from the stack.
5304
INC C 0C
INCrement C by 1, advancing to the next drive number.
5305
LD A,C 79
Load A with C (updated drive number).
5306
CP A,08H FE 08
Compare A against 08H. If A equals 08H (all 8 drives checked), the Z FLAG is set.
5308
If the NZ FLAG is set (more drives to check), JUMP to 5222H to process the next drive. Note the target is in the 5200H page (52xxH range).
530B
JUMP to SYS0 DOS READY exit at 402DH. All drives have been processed; return cleanly to the DOS command prompt.

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.

530E
GOSUB to SYS0 routine at 4467H to display the null-terminated message pointed to by HL on the screen.
5311
LD DE,0000H 11 00 00
Self-Modifying Code
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.
5314
INC E 1C
INCrement E by 1. Advances the printed-line counter.
5315
If the Z FLAG is set (E has wrapped from FFH back to 00H after 256 lines), JUMP to SYS0 routine at 446AH to pause the display and wait for a keypress.
5318
RET C9
RETurn to the caller.

5319H - Parameter Error Handler

Displays the "PARAMETER ERROR" message and exits to the error-already-displayed handler at 4030H.

5319
Point HL to the error message string at 5322H ("PARAMETER ERROR" followed by CR).
531C
GOSUB to SYS0 routine at 447BH to display the message at HL with CONFIG/SYS processing.
531F
JUMP to SYS0 error-already-displayed exit at 4030H. The message has been shown; return to the DOS command loop.

5322H - "PARAMETER ERROR" Message String

ASCII message displayed when the FREE command is invoked with an invalid parameter. Terminated by 0DH (carriage return).

5322
DEFM "PARAMETER ERROR" 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
ASCII string: PARAMETER ERROR followed by carriage return (0DH). Displayed by the handler at 5319H when an invalid parameter is passed to FREE.

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.

5332
OR A,40H F6 40
OR A with 40H, setting bit 6 of the error code. Bit 6 set suppresses the extended error context display in the DOS error handler.
5334
JUMP to SYS0 DOS error exit at 4409H with the error code in A.

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.

5337
LD A,20H 3E 20
Load A with 20H (ASCII space ).
5339
Unconditional JUMP to 5349H, entering the decimal formatter at the hundreds digit stage with the space already in A.

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.

533B
LD A,20H 3E 20
Load A with 20H (ASCII space). A holds the leading fill character (space) used for suppressed leading zeros.
533D
LD BC,2710H 01 10 27
Load BC with 2710H (10,000 decimal). First divisor for the ten-thousands digit.
5340
GOSUB to local routine at 535BH to extract and store the ten-thousands digit (or a leading space if zero) into the buffer at DE.
5343
LD BC,03E8H 01 E8 03
Load BC with 03E8H (1,000 decimal). Second divisor for the thousands digit.
5346
GOSUB to local routine at 535BH to extract and store the thousands digit.

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).

5349
LD BC,0064H 01 64 00
Load BC with 0064H (100 decimal). Divisor for the hundreds digit.
534C
GOSUB to local routine at 535BH to extract and store the hundreds digit.
534F
LD BC,000AH 01 0A 00
Load BC with 000AH (10 decimal). Divisor for the tens digit.
5352
GOSUB to local routine at 535BH to extract and store the tens digit.
5355
LD A,L 7D
Load A with L. After all higher-order digits have been divided out, L holds the units digit (0-9).
5356
ADD A,30H C6 30
ADD 30H to A, converting the binary units digit to its ASCII character.
5358
LD (DE),A 12
Store the ASCII units digit into the buffer at (DE).
5359
INC DE 13
INCrement DE to advance the buffer pointer past the units digit.
535A
RET C9
RETurn to the caller. The decimal number has been fully written into the buffer.

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.

535B
PUSH DE D5
Save DE (output buffer pointer) onto the stack.
535C
LD E,A 5F
Load E with A (the current leading-zero fill character, either 20H space or a prior non-zero digit character).
535D
LD D,0FFH 16 FF
Load D with 0FFH. D is the digit counter, initialised to -1 so the first INC D produces 0.
535F
XOR A,A AF
Clear A to zero. A will accumulate the digit count during the subtraction loop.

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.

5360
INC D 14
Loop Start
INCrement D. On first entry D becomes 00H; each subsequent iteration increments the digit count.
5361
SBC HL,BC ED 42
Subtract BC from HL with carry. If HL >= BC the result is non-negative and the subtraction is valid.
5363
If the NO CARRY FLAG is set (subtraction did not go negative, digit count not yet reached), LOOP BACK to 5360H. Loop End
When carry is set, HL has gone negative; D holds the digit value.
5365
ADD HL,BC 09
ADD BC back to HL to restore the remainder after one over-subtraction.
5366
LD A,E 7B
Load A with E (the saved leading-zero fill character).
5367
LD B,D 42
Load B with D (the computed digit value, 0-9).
5368
POP DE D1
Restore DE (output buffer pointer) from the stack.
5369
LD (DE),A 12
Store A (the fill character or prior digit) into the current buffer position at (DE). This handles the leading-space suppression: if the digit is zero and no prior non-zero digit has been seen, a space is stored.
536A
INC B 04
INCrement B (the digit value) by 1.
536B
DEC B 05
DECrement B by 1. The INC/DEC pair tests whether B was zero without altering the Carry Flag. If B was zero the Z FLAG is now set.
536C
If the Z FLAG is set (digit value is zero), JUMP to 5374H to advance DE and return with A still holding the fill character (leading-zero suppression continues).
536E
LD A,B 78
Load A with B (the non-zero digit value).
536F
ADD A,30H C6 30
ADD 30H to A, converting the binary digit value to its ASCII character (1-9).
5371
LD (DE),A 12
Store the ASCII digit character into the buffer at (DE), overwriting the fill character placed at 5369H.
5372
LD A,30H 3E 30
Load A with 30H (ASCII 0). From this point onward, any subsequent zero digits must be printed as 0 rather than suppressed as spaces, since a non-zero digit has now been seen.

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.

5374
INC DE 13
INCrement DE to advance the output buffer pointer past the digit just stored.
5375
RET C9
RETurn to the caller at 5340H, 5346H, 534CH, or 5352H for the next digit.

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.

5376
PUSH DE D5
Save DE onto the stack.
5377
PUSH HL E5
Save HL onto the stack.
5378
GOSUB to SYS0 routine at 4B65H to set up the FDC track and sector registers for track 0, sector 0 (the GAT sector) of the current drive.
537B
LD E,00H 1E 00
Load E with 00H. E = sector number 0 (the GAT sector).
537D
LD HL,4200H 21 00 42
Point HL to 4200H, the sector buffer that will receive the GAT sector data.
5380
GOSUB to SYS0 routine at 4B45H to read the GAT sector into the buffer at HL=4200H. On return, Z = success, NZ = read error.
5383
POP HL E1
Restore HL from the stack.
5384
POP DE D1
Restore DE from the stack.
5385
RET Z C8
If the Z FLAG is set (read succeeded), RETurn immediately to the caller with Z set.
5386
LD A,14H 3E 14
Load A with 14H. This is the read-error code (error 14H = Read Error) returned to the caller on failure.
5388
RET C9
RETurn to the caller with NZ set and A = 14H (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.

5389
DEFM "P " 50 20 20 20 20
ASCII text: P (letter P followed by four spaces). The drive filespec prefix used by the open call at 520AH.
538E
DEFB 12H,53H,00H 12 53 00
Drive specification byte (12H), high byte of SYS0 pointer (53H), and null terminator (00H) completing the filespec template.

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".

5392
DEFM "DRIVE " 44 52 49 56 45 20
ASCII text: DRIVE - start of the drive summary line.
5398
DEFB 58H 58
Placeholder byte X (58H). Overwritten at runtime by LD (5398H),A at 522CH with the ASCII drive number digit (0-7).
5399
DEFM " - " 20 2D 20
ASCII text: - separator between drive number and drive name.
539C
DEFM "NNNNNNNN" 4E 4E 4E 4E 4E 4E 4E 4E
Eight placeholder bytes (N). Overwritten at runtime by the LDIR at 5296H with the 8-character drive name copied from the GAT buffer at 42D0H.
53A4
DEFB 20H 20
ASCII space separator between drive name and date field.
53A5
DEFM "MM/DD/YY" 4D 4D 2F 44 44 2F 59 59
Eight placeholder bytes (MM/DD/YY). Overwritten at runtime by the LDIR at 529EH with the 8-character date string copied from the GAT buffer at 42D8H.
53AD
DEFM " " 20 20
Two ASCII spaces separating the date from the FILES= field.
53AF
DEFM "FILES=" 46 49 4C 45 53 3D
ASCII text: FILES= label for the directory entry count fields.
53B5
DEFM "XXX/XXX" 58 58 58 2F 58 58 58
Three placeholder bytes (XXX/) a slash and then three more placeholder bytes. Overwritten at runtime by CALL 5337H at 52FAH with the free directory entry count formatted as a 3-digit decimal number followed by a slash and then the other 3 are overwritten at 5273H with the total directory entry count.
53BC
DEFM ", SPACE=" 58 58 58 2C 20 53 50 41 43 45 3D
", SPACE" is fixed text leading into the kilobyte fields.
53C4
DEFM " / K" 20 20 20 20 20 2F 20 20 20 20 20 20 4B
Five spaces, a slash, 5 space placeholder, a space, and "K". The leading four spaces are overwritten at runtime by CALL 533BH at 52CDH with the used-space kilobyte count. The slash separates used from total space. Seven placeholder spaces followed by K. The spaces are overwritten at runtime by CALL 533BH at 528AH with the free-space kilobyte count. The trailing K is the kilobytes unit indicator.
53C9
DEFB 20H 20
ASCII space before the total-space field.
53D1
DEFB 0DH 0D
Carriage return (0DH) terminating the drive summary line for display by 4467H.

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

AddressName and Purpose
5200HCHAIN 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.
52BFHCHAIN 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.
52D7HCHAIN 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.
52E1HParameter 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.
530FHCompile 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).
5312HExecute 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.
5340HPAUSE 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.
5367HCharacter 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.
536FHSymbol 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.
53A9HJCL 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).
53B8HDouble-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.
53E8HJCL 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.
5406HCompiled 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.
5481HExecute-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.
54D0HCondition 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).
54DAHNOT 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.
54E9HSymbol 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).
5577HSymbol 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.
5582HSymbol 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).
55A1HDelimiter-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.
55C0HFixed-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.
560BHClear 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.
561DHWrite 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.
5628HSymbol 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.
563AHJCL 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.
564DHJCL 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.
5660HGeneric 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.
56A9HJCL 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.
56AEHError: File Spec Required
Displays "FILE SPEC REQUIRED" message from 5726H via CALL 447BH, then exits to 4030H.
56B7HError: Cannot Create JCL File
Displays "CANNOT CREATE SYSTEM" + continuation message from 573AH via CALL 447BH, then exits to 4030H.
56C0HError: Parameter Error
Displays "PARAMETER ERROR" from 575EH via CALL 447BH, then exits to 4030H.
56C9HError: 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.

5200
LD A,(HL) 7E
CHAIN Entry Point
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.
5201
CP A,2AH FE 2A
Compare Register A against 2AH (ASCII: *). If Register A equals 2AH, the Z FLAG is set; otherwise the NZ FLAG is set.
5203
If the Z FLAG has been set (first character is *), JUMP to 52D7H to rerun the last CHAIN command by restoring the saved filespec from 571BH.
5206
CP A,3DH FE 3D
Compare Register A against 3DH (ASCII: =). If Register A equals 3DH, the Z FLAG is set; otherwise the NZ FLAG is set.
5208
If the Z FLAG has been set (first character is =), JUMP to 52BFH to skip the file-read phase and re-execute the previously submitted command.
520B
CP A,24H FE 24
Compare Register A against 24H (ASCII: $). If Register A equals 24H, the Z FLAG is set; otherwise the NZ FLAG is set.
520D
If the NZ FLAG has been set (first character is not $), JUMP to 5219H to begin filespec extraction immediately without the self-modify below.
520F
LD (52A9H),A 32 A9 52
Self-Modifying Code
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.
5212
INC HL 23
INCrement Register Pair HL by 1. Advance past the $ prefix character in the command tail.
5213
LD A,(HL) 7E
Load Register A with the character now at (HL) - the character following the $.
5214
CP A,20H FE 20
Compare Register A against 20H (ASCII: space). If Register A equals 20H, the Z FLAG is set; otherwise the NZ FLAG is set.
5216
If the NZ FLAG has been set (no space after $), JUMP to 5219H - the character after $ is already part of the filespec.
5218
INC HL 23
INCrement Register Pair HL by 1. Skip the space character that followed $ before proceeding to filespec extraction.

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.

5219
LD DE,56D2H 11 D2 56
Point Register Pair DE to 56D2H - the filespec work buffer where the extracted filename will be stored.
521C
GOSUB to SYS0 routine at 441CH to extract the filespec from the command line (HL) into the buffer at DE (56D2H). Returns NZ if no filespec was present on the command line.
521F
If the NZ FLAG has been set (no filespec found), JUMP to 56AEH to display "FILE SPEC REQUIRED" and exit.
5222
PUSH HL E5
Save Register Pair HL onto the stack. Preserves the command tail pointer (HL now points past the extracted filespec) across the extension insertion call below.
5223
LD DE,56D2H 11 D2 56
Point Register Pair DE to 56D2H - the filespec buffer. Required by CALL 4473H as the filespec destination.
5226
LD HL,5722H 21 22 57
Point Register Pair HL to 5722H - the "JCL" default extension string (ASCII "JCL" + 03H terminator).
5229
GOSUB to SYS0 routine at 4473H to insert the default "JCL" extension into the filespec at 56D2H if the user did not supply one explicitly.
522C
LD B,00H 06 00
Load Register B with 00H. Sets the file-open mode to 0 (read-only open) for the CALL 4424H below.
522E
LD HL,5A00H 21 00 5A
Point Register Pair HL to 5A00H - the work FCB (File Control Block) area used to hold the open JCL file descriptor.
5231
GOSUB to SYS0 routine at 4424H to open the file described by the FCB at HL (5A00H). Returns NZ if the file could not be opened.
5234
If the NZ FLAG has been set (file open failed), JUMP to 56A9H - the JCL error exit that ORs 40H into A and jumps to the DOS error handler at 4409H.
5237
LD DE,4358H 11 58 43
Point Register Pair DE to 4358H - the system FCB buffer. This is the destination for the LDIR copy below.
523A
LD HL,571BH 21 1B 57
Point Register Pair HL to 571BH - the "SYSTEM/JCL" filename string (20H bytes). This string is copied into the FCB at 4358H to identify the file being read.
523D
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H (32 decimal). Sets the byte count for the LDIR block copy - 32 bytes of filename data.
5240
LDIR ED B0
Block copy: 32 bytes from 571BH (source HL = "SYSTEM/JCL" filename) to 4358H (destination DE = system FCB). Copies the filename descriptor into the system FCB slot, incrementing HL and DE, decrementing BC until BC = 0.
5242
LD B,00H 06 00
Load Register B with 00H. Sets the record-read mode to 0 for the CALL 4420H below.
5244
LD DE,4358H 11 58 43
Point Register Pair DE to 4358H - the system FCB now containing the "SYSTEM/JCL" file descriptor.
5247
LD HL,5B00H 21 00 5B
Point Register Pair HL to 5B00H - the JCL file sector buffer where the first record will be read.
524A
GOSUB to SYS0 routine at 4420H to read one record from the file (FCB at DE = 4358H) into the buffer at HL (5B00H). Returns NZ if the read failed or EOF was reached immediately.
524D
If the NZ FLAG has been set (read failed), JUMP to 56B7H to display "CANNOT CREATE SYSTEM JCL FILE" error and exit.
5250
POP HL E1
Restore Register Pair HL from the stack. HL again points to the position in the command tail just past the extracted filespec - ready to scan for parameter assignments.

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.

5251
LD A,(HL) 7E
Loop Start
Load Register A with the current character from the command tail.
5252
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). If Register A equals 0DH, the Z FLAG is set; otherwise the NZ FLAG is set.
5254
If the Z FLAG has been set (end of command line reached), JUMP to 5312H to begin the command submission phase - all parameters have been processed.
5257
INC HL 23
INCrement Register Pair HL by 1. Advance past the current character before testing it below.
5258
CP A,20H FE 20
Compare Register A against 20H (ASCII: space). If Register A equals 20H, the Z FLAG is set; otherwise the NZ FLAG is set.
525A
If the Z FLAG has been set (character was a space), LOOP BACK to 5251H to skip it and read the next character.
525C
CP A,2CH FE 2C
Compare Register A against 2CH (ASCII: ,). If Register A equals 2CH, the Z FLAG is set; otherwise the NZ FLAG is set.
525E
If the Z FLAG has been set (character was a comma), LOOP BACK to 5251H to skip it and read the next character.
5260
CP A,28H FE 28
Compare Register A against 28H (ASCII: (). If Register A equals 28H, the Z FLAG is set; otherwise the NZ FLAG is set.
5262
If the Z FLAG has been set (character was an open parenthesis), JUMP to 52E1H to process an inline parameter list of the form (sym=val, sym=val).
5264
CP A,3BH FE 3B
Compare Register A against 3BH (ASCII: ;). If Register A equals 3BH, the Z FLAG is set; otherwise the NZ FLAG is set.
5266
If the NZ FLAG has been set (character is not ; and is not any recognized delimiter), JUMP to 56C0H to display "PARAMETER ERROR" and exit.
5269
LD A,3FH 3E 3F
Interactive Prompt
Load Register A with 3FH (ASCII: ?). The ; character on the command tail requests an interactive parameter prompt.
526B
GOSUB to ROM routine at 0033H to display the ? prompt character on screen.
526E
LD HL,4318H 21 18 43
Point Register Pair HL to 4318H - the DOS command input buffer. This will receive the user's typed parameter string.
5271
LD B,3FH 06 3F
Load Register B with 3FH (63 decimal). Maximum character count for the line input call below.
5273
GOSUB to ROM routine at 0040H to read a line of keyboard input into the buffer at HL (4318H), accepting up to B (63) characters. Returns with CARRY FLAG set if BREAK was pressed.
5276
If the CARRY FLAG has been set (BREAK pressed during input), JUMP to 56C0H to exit with "PARAMETER ERROR".
5279
GOSUB to SYS0 routine at 447EH to process the typed parameter string through the CONFIG/SYS display handler, applying any symbol substitutions.
527C
LOOP BACK to 5251H to re-scan the command tail - the user's typed line has been merged and processing continues.

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.

527E
LD HL,(57B2H) 2A B2 57
Loop Start
Load Register Pair HL from (57B2H). Fetch the current command buffer read pointer.
5281
LD DE,57B4H 11 B4 57
Point Register Pair DE to 57B4H - the buffer end sentinel address.
5284
XOR A,A AF
Set Register A to zero and clear all flags. Required before the 16-bit SBC comparison below.
5285
SBC HL,DE ED 52
SBC HL,DE - subtract DE from HL with carry. Compares the current buffer pointer against the end sentinel. Result is zero if all compiled lines have been consumed.
5287
If the Z FLAG has been set (buffer exhausted), JUMP to 52A2H to close the FCB and exit cleanly.
5289
LD HL,(57B2H) 2A B2 57
Load Register Pair HL from (57B2H). Reload HL with the buffer pointer (SBC HL,DE destroyed HL above).
528C
DEC HL 2B
DECrement Register Pair HL by 1. Step back one byte to point at the last byte of the current command entry in the buffer.
528D
LD DE,56F1H 11 F1 56
Point Register Pair DE to 56F1H - the high end of the symbol name/value workspace. LDDR will copy backwards from (HL) to (DE).
5290
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H (32 decimal). Copy 32 bytes from the buffer backwards into the workspace.
5293
LDDR ED B8
Block copy: 32 bytes moving backwards from (HL) to (DE), decrementing both pointers, decrementing BC until zero. Extracts the current command line from the compiled buffer into the workspace at 56D2H-56F1H.
5295
INC HL 23
INCrement Register Pair HL by 1. After LDDR, HL points one byte before the copied block. INC HL corrects it to point at the start of the next command entry in the buffer.
5296
LD (57B2H),HL 22 B2 57
Store Register Pair HL to (57B2H). Advance the buffer read pointer to the next command entry.
5299
LD DE,56D2H 11 D2 56
Point Register Pair DE to 56D2H - the start of the workspace where the command line was just extracted.
529C
GOSUB to SYS0 routine at 4454H to flush/close-and-reopen the file, preparing for the next command submission.
529F
JUMP to 5312H to submit the extracted command line to the DOS command interpreter.

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.

52A2
LD DE,4358H 11 58 43
Point Register Pair DE to 4358H - the system FCB containing the open JCL file descriptor.
52A5
GOSUB to SYS0 routine at 4428H to close the file described by the FCB at DE (4358H).
52A8
LD A,00H 3E 00
Self-Modifying Code Target
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).
52AA
OR A,A B7
OR Register A with itself. Tests whether A is zero (normal mode) or non-zero (batch mode with $ prefix). Sets Z FLAG if A = 00H.
52AB
If the NZ FLAG has been set (batch mode - $ prefix was used), JUMP to SYS0 at 402DH for DOS READY / No-Error Exit. Batch mode suppresses any further output.

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.

52AE
LD DE,4358H 11 58 43
Point Register Pair DE to 4358H - the system FCB. This FCB will be opened to submit the next command.
52B1
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H - the directory/sector buffer used as the data buffer for this file open.
52B4
LD B,00H 06 00
Load Register B with 00H. Sets file-open mode to 0 (read-only) for the CALL 4424H below.
52B6
GOSUB to SYS0 routine at 4424H to open the FCB at DE (4358H) with data buffer at HL (4200H).
52B9
If the NZ FLAG has been set (open failed), JUMP to 56A9H - the JCL error exit.
52BC
LD A,99H 3E 99
Load Register A with 99H - the SVC code for "submit command line to DOS interpreter".
52BE
RST 28H EF
Issue RST 28H with A = 99H. This invokes the SYS0 overlay dispatcher SVC to submit the command currently in the FCB at 4358H to the DOS command interpreter for execution. Control returns here after the submitted command completes.

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.

52BF
INC HL 23
INCrement Register Pair HL by 1. Advance past the '=' prefix character in the command tail. HL now points to the first character of the filespec.
52C0
LD A,(HL) 7E
Load Register A with the character now at (HL).
52C1
CP A,20H FE 20
Compare Register A against 20H (ASCII: space). If Register A equals 20H, the Z FLAG is set; otherwise the NZ FLAG is set.
52C3
If the NZ FLAG has been set (no space after '='), JUMP to 52C6H - the character is already the start of the filespec.
52C5
INC HL 23
INCrement Register Pair HL by 1. Skip the space that followed '=' before extracting the filespec.
52C6
LD DE,4358H 11 58 43
Point Register Pair DE to 4358H - the system FCB buffer, used here as the destination for the filespec extraction.
52C9
GOSUB to SYS0 routine at 441CH to extract the filespec from the command tail (HL) into the buffer at DE (4358H). Returns NZ if no filespec found.
52CC
If the NZ FLAG has been set (no filespec on command line), JUMP to 56A9H - JCL error exit.
52CF
LD HL,5722H 21 22 57
Point Register Pair HL to 5722H - the "JCL" default extension string.
52D2
GOSUB to SYS0 routine at 4473H to insert the default "JCL" extension into the filespec at 4358H if none was supplied.
52D5
JUMP to 52AEH to open the FCB and submit the command via RST 28H.

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.

52D7
LD HL,571BH 21 1B 57
Point Register Pair HL to 571BH - the saved "SYSTEM/JCL" filename string (32 bytes). This was stored here during a previous CHAIN invocation.
52DA
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H (32 decimal). Sets the byte count for the block copy below.
52DD
LDIR ED B0
Block copy: 32 bytes from 571BH (source HL = saved filespec) to DE (destination - DE is inherited from the caller's context, pointing to the filespec work buffer). Restores the last-used filespec into the work area.
52DF
JUMP to 52AEH to open the FCB with the restored filespec and submit the command via RST 28H.

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.

52E1
Loop Start
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.
52E4
If the NZ FLAG has been set (a non-empty token was read), JUMP to 52F5H to test the delimiter character and dispatch accordingly.
52E6
PUSH AF F5
Save Register Pair AF onto the stack. Preserves the delimiter character in A and flags across the symbol lookup call below.
52E7
GOSUB to 5628H to look up the symbol name (just read by 5577H) in the symbol table. Returns Z FLAG set if found.
52EA
If the Z FLAG has been set (symbol was found in the table), JUMP to 56C9H - the symbol-already-defined error exit.
52ED
GOSUB to 560BH to insert the new symbol name into the symbol table.
52F0
POP AF F1
Restore Register Pair AF from the stack. Recovers the delimiter character that terminated the token.
52F1
CP A,3DH FE 3D
Compare Register A against 3DH (ASCII: =). If Register A equals 3DH, the Z FLAG is set; otherwise the NZ FLAG is set.
52F3
If the Z FLAG has been set (delimiter was '='), JUMP to 5305H to read and assign the value for this symbol.
52F5
CP A,20H FE 20
Compare Register A against 20H (ASCII: space). If Register A equals 20H, the Z FLAG is set; otherwise the NZ FLAG is set.
52F7
If the Z FLAG has been set (delimiter was a space), LOOP BACK to 52E1H to read the next token.
52F9
CP A,2CH FE 2C
Compare Register A against 2CH (ASCII: ,). If Register A equals 2CH, the Z FLAG is set; otherwise the NZ FLAG is set.
52FB
If the Z FLAG has been set (delimiter was a comma), LOOP BACK to 52E1H to read the next token.
52FD
CP A,29H FE 29
Compare Register A against 29H (ASCII: )). If Register A equals 29H, the Z FLAG is set; otherwise the NZ FLAG is set.
52FF
If the Z FLAG has been set (closing parenthesis found), JUMP to 5251H to resume scanning the main command tail - the parameter list is complete.
5302
JUMP to 56C0H to display "PARAMETER ERROR" and exit. The delimiter was not a recognised separator or closing parenthesis.

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.

5305
GOSUB to 5582H to read the value token from the command tail. Returns A = delimiter character that terminated the value.
5308
PUSH AF F5
Save Register Pair AF onto the stack. Preserves the delimiter character across the store call below.
5309
GOSUB to 561DH to store the value token into the symbol table entry created earlier.
530C
POP AF F1
Restore Register Pair AF from the stack. Recovers the delimiter character that terminated the value token.
530D
JUMP to 52F5H to test the delimiter and continue scanning for the next symbol assignment or closing parenthesis.

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.

530F
GOSUB to 5406H to check the output command buffer and flush if a complete command line is pending.
5312
GOSUB to 53E8H to read the next record from the JCL file into the line buffer at 5919H. Returns Z FLAG set if read succeeded, NZ if end-of-file or error.
5315
If the NZ FLAG has been set (no more records available), JUMP to 527EH to process any remaining buffered commands and exit.

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.

5318
LD HL,5919H 21 19 59
Point Register Pair HL to 5919H - the start of the JCL line input buffer.
531B
LD DE,5959H 11 59 59
Point Register Pair DE to 5959H - the output command buffer where processed characters are written.
531E
LD A,(HL) 7E
Loop Start
Load Register A with the current character from the JCL line buffer.
531F
INC HL 23
INCrement Register Pair HL by 1. Advance the source pointer past the current character.
5320
CP A,50H FE 50
Compare Register A against 50H (ASCII: P). If Register A equals 50H, the Z FLAG is set; otherwise the NZ FLAG is set.
5322
If the Z FLAG has been set (character is P), GOSUB to 5340H to attempt to complete the word "PAUSE" by matching "AUSE" in the input stream. On return A holds the result character.
5325
CP A,2FH FE 2F
Compare Register A against 2FH (ASCII: /). If Register A equals 2FH, the Z FLAG is set; otherwise the NZ FLAG is set.
5327
If the NZ FLAG has been set (character is not /), JUMP to 532DH to test for other special characters.
5329
CP A,(HL) BE
Compare Register A (/) against the next character at (HL). If the next character is also /, the Z FLAG is set - this is a // end-of-job marker.
532A
If the Z FLAG has been set (// found), JUMP to 53B8H to handle the end-of-job marker.
532D
CP A,23H FE 23
Compare Register A against 23H (ASCII: #). If Register A equals 23H, the Z FLAG is set; otherwise the NZ FLAG is set.
532F
If the Z FLAG has been set (character is #), JUMP to 533BH to perform symbol substitution.
5331
LD (DE),A 12
Store Register A into the output buffer at (DE). Copies the current character to the output command buffer.
5332
INC DE 13
INCrement Register Pair DE by 1. Advance the output buffer pointer.
5333
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). If Register A equals 0DH, the Z FLAG is set; otherwise the NZ FLAG is set.
5335
If the Z FLAG has been set (end of line reached), JUMP to 530FH to flush the output buffer and read the next JCL record.
5337
LD A,(HL) 7E
Load Register A with the next character from the JCL line buffer.
5338
INC HL 23
INCrement Register Pair HL by 1. Advance the source pointer.
5339
LOOP BACK to 532DH to test the next character for special handling.
533B
GOSUB to 536FH to perform symbol substitution for the # token. Expands the symbol value into the output buffer.
533E
JUMP to 5337H to continue scanning the JCL line after the substituted symbol.

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.

5340
PUSH HL E5
Save Register Pair HL onto the stack. Preserves the current JCL line scan pointer across the keyword matching sequence below.
5341
LD A,41H 3E 41
Load Register A with 41H (ASCII: A). First character of "AUSE" to match.
5343
GOSUB to 5367H to compare A (A) against (HL) and advance HL if matched. If not matched, 5367H pops two stack frames and returns 'P' to the JCL loop.
5346
LD A,55H 3E 55
Load Register A with 55H (ASCII: U). Second character of "AUSE" to match.
5348
GOSUB to 5367H to compare A (U) against (HL) and advance HL if matched.
534B
LD A,53H 3E 53
Load Register A with 53H (ASCII: S). Third character of "AUSE" to match.
534D
GOSUB to 5367H to compare A (S) against (HL) and advance HL if matched.
5350
LD A,45H 3E 45
Load Register A with 45H (ASCII: E). Fourth and final character of "AUSE" to match.
5352
GOSUB to 5367H to compare A (E) against (HL) and advance HL if matched. All four characters matched - word is confirmed "PAUSE".
5355
LD HL,5955H 21 55 59
Point Register Pair HL to 5955H - the end of the output buffer area used as source for the LDDR copy below.
5358
LD DE,5957H 11 57 59
Point Register Pair DE to 5957H - the destination for the LDDR copy, two bytes above the source.
535B
LD BC,003DH 01 3D 00
Load Register Pair BC with 003DH (61 decimal). Byte count for the LDDR block copy - shifts the existing output buffer contents up by two bytes to make room for the '//' end-of-job marker.
535E
LDDR ED B8
Block copy: 61 bytes moving backwards from (HL=5955H) to (DE=5957H). Shifts the accumulated output buffer contents upward by two positions, opening two bytes at the start for the '//' marker.
5360
LD A,2FH 3E 2F
Load Register A with 2FH (ASCII: /). First byte of the '//' end-of-job marker to insert.
5362
LD (DE),A 12
Store Register A (/) into (DE). Writes the first '/' of the '//' marker into the gap opened by LDDR.
5363
DEC DE 1B
DECrement Register Pair DE by 1. Point to the byte before the first '/'.
5364
LD (DE),A 12
Store Register A (/) into (DE). Writes the second '/' of the '//' marker.
5365
POP HL E1
Restore Register Pair HL from the stack. Recovers the JCL line scan pointer saved at entry.
5366
RET C9
Return to caller. The PAUSE keyword has been processed and '//' has been inserted into the output buffer.

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.

5367
CP A,(HL) BE
Compare Register A against the character at (HL). If equal, the Z FLAG is set; otherwise the NZ FLAG is set.
5368
INC HL 23
INCrement Register Pair HL by 1. Advance the JCL line scan pointer past the matched character.
5369
RET Z C8
If the Z FLAG has been set (characters matched), Return to caller. The matched character has been consumed and HL advanced.
536A
POP HL E1
Stack Unwind
Pop Register Pair HL from the stack. Discards the return address of this subroutine call (one of the four CALL 5367H sites in 5340H).
536B
POP HL E1
Pop Register Pair HL from the stack again. Discards the return address of CALL 5340H itself, unwinding back to the caller of 5340H (the JCL line loop at 5322H).
536C
LD A,50H 3E 50
Load Register A with 50H (ASCII: P). Restores the original 'P' character so the JCL line loop at 532DH can process it as a plain character rather than a PAUSE keyword.
536E
RET C9
Return to the JCL line loop. Execution resumes at 5325H (the instruction after CALL Z,5340H) with A = 'P', which then falls through to 532DH for normal character processing.

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.

536F
CP A,(HL) BE
Compare Register A (23H = #) against the character at (HL). If equal (two consecutive '#'), the Z FLAG is set.
5370
If the NZ FLAG has been set (next character is not '#'), JUMP to 5376H to attempt a full symbol lookup via 5577H.
5372
INC HL 23
INCrement Register Pair HL by 1. Advance past the second '#' character.
5373
LD (DE),A 12
Store Register A (23H = #) into the output buffer at (DE). A double '##' is an escaped '#' - output a single '#' literally.
5374
INC DE 13
INCrement Register Pair DE by 1. Advance the output buffer pointer.
5375
RET C9
Return to caller. The escaped '#' has been written to the output buffer.
5376
PUSH HL E5
Save Register Pair HL onto the stack. Preserves the JCL line scan pointer across the symbol lookup below.
5377
PUSH DE D5
Save Register Pair DE onto the stack. Preserves the output buffer pointer across the symbol lookup below.
5378
GOSUB to 5577H to read the symbol name token from (HL) into a work buffer. Returns A = delimiter character that terminated the token.
537B
CP A,23H FE 23
Compare Register A against 23H (ASCII: #). If Register A equals 23H, the Z FLAG is set - the token was properly terminated by a closing '#'.
537D
If the NZ FLAG has been set (symbol not terminated by '#'), JUMP to 53A9H to display an error message and exit.
537F
EX (SP),HL E3
Exchange Register Pair HL with the top of stack. HL now holds the saved JCL scan pointer; the stack top now holds the current HL (symbol name pointer). Prepares for the lookup call.
5380
PUSH HL E5
Save Register Pair HL (the JCL scan pointer) onto the stack again. Preserves it across the symbol lookup below.
5381
GOSUB to 5628H to look up the symbol name in the symbol table. Returns Z FLAG set if found, DE pointing to the symbol value string.
5384
If the NZ FLAG has been set (symbol not found in table), JUMP to 5395H to copy the '#...#' token literally to the output.
5386
LD A,(DE) 1A
Load Register A from (DE). Fetch the length byte of the symbol value string.
5387
OR A,A B7
OR Register A with itself. Tests whether the symbol value length is zero.
5388
If the Z FLAG has been set (symbol value is empty), JUMP to 5395H to copy the '#...#' token literally.
538A
LD B,00H 06 00
Load Register B with 00H. High byte of BC for the LDIR byte count below.
538C
LD C,A 4F
Load Register C with Register A. Sets BC = length of the symbol value string for the LDIR copy below.
538D
INC DE 13
INCrement Register Pair DE by 1. Advance past the length byte to point at the first character of the symbol value.
538E
POP HL E1
Restore Register Pair HL from the stack. Recovers the JCL line scan pointer.
538F
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now points to the symbol value string; DE now holds the JCL scan pointer.
5390
LDIR ED B0
Block copy: BC bytes from (HL = symbol value) to (DE = output buffer). Expands the symbol value inline into the output command buffer.
5392
POP HL E1
Restore Register Pair HL from the stack. Recovers the saved output buffer pointer (originally pushed at 5377H via EX (SP),HL at 537FH).
5393
POP AF F1
Restore Register Pair AF from the stack. Discards the saved HL that was pushed at 5380H.
5394
RET C9
Return to caller. The symbol value has been expanded into the output buffer.
5395
POP DE D1
Literal Copy Path
Pop Register Pair DE from the stack. Recovers the output buffer pointer for literal '#' copy below.
5396
POP AF F1
Pop Register Pair AF from the stack. Discards saved register.
5397
POP HL E1
Pop Register Pair HL from the stack. Recovers the original JCL line scan pointer from before the symbol lookup attempt.
5398
LD A,23H 3E 23
Load Register A with 23H (ASCII: #). Prepares to copy the opening '#' literally to the output.
539A
LD (DE),A 12
Loop Start
Store Register A into the output buffer at (DE). Copies the current character literally.
539B
INC DE 13
INCrement Register Pair DE by 1. Advance the output buffer pointer.
539C
LD A,(HL) 7E
Load Register A with the next character from the JCL line buffer.
539D
INC HL 23
INCrement Register Pair HL by 1. Advance the JCL line scan pointer.
539E
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). If Register A equals 0DH, the Z FLAG is set; otherwise the NZ FLAG is set.
53A0
If the Z FLAG has been set (end of line reached without finding closing '#'), JUMP to 53A9H to display the unterminated symbol error.
53A2
CP A,23H FE 23
Compare Register A against 23H (ASCII: #). If Register A equals 23H, the Z FLAG is set - closing '#' found.
53A4
If the NZ FLAG has been set (not a closing '#'), LOOP BACK to 539AH to copy this character literally and continue scanning.
53A6
LD (DE),A 12
Store Register A (23H = #) into the output buffer at (DE). Copies the closing '#' literally.
53A7
INC DE 13
INCrement Register Pair DE by 1. Advance the output buffer pointer.
53A8
RET C9
Return to caller. The unresolved '#...#' token has been copied literally to the output buffer.

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.

53A9
LD HL,5916H 21 16 59
Point Register Pair HL to 5916H - the first JCL error message string.
53AC
GOSUB to SYS0 routine at 447BH to display the error message string at HL on screen.
53AF
LD HL,578AH 21 8A 57
Point Register Pair HL to 578AH - the second error message string (general JCL error text).
53B2
GOSUB to SYS0 routine at 447BH to display the second error message string at HL on screen.
53B5
JUMP to SYS0 at 4030H - the error-already-displayed exit. Returns control to the DOS command interpreter without printing a further error message.

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.

53B8
INC HL 23
INCrement Register Pair HL by 1. Advance past the second '/' of the '//' marker.
53B9
GOSUB to 5577H to read and discard any remaining token on the '//' line.
53BC
If the NZ FLAG has been set (token was non-empty after '//'), JUMP to 53D5H to reset the line buffer.
53BE
GOSUB to 563AH to check the first buffer entry. Returns Z FLAG set if a valid entry is found, DE pointing to it.
53C1
If the NZ FLAG has been set (no valid entry at first position), JUMP to 53C5H to check the second buffer entry.
53C3
PUSH DE D5
Save Register Pair DE onto the stack. Preserves the valid entry pointer found by 563AH.
53C4
RET C9
Return to caller with DE pointing to the valid buffer entry.
53C5
LD DE,(58F4H) ED 5B F4 58
Load Register Pair DE from (58F4H). Fetches the current output buffer write pointer.
53C9
LD A,(DE) 1A
Load Register A from (DE). Fetch the byte at the current output buffer position.
53CA
OR A,A B7
OR Register A with itself. Tests whether the output buffer position is empty (zero).
53CB
If the NZ FLAG has been set (buffer position is non-empty), JUMP to 5312H to read the next JCL record.
53CE
GOSUB to 564DH to check the second buffer entry. Returns Z FLAG set if valid, DE pointing to it.
53D1
If the NZ FLAG has been set (no valid entry found), JUMP to 53D5H to reset the line buffer.
53D3
PUSH DE D5
Save Register Pair DE onto the stack. Preserves the valid entry pointer found by 564DH.
53D4
RET C9
Return to caller with DE pointing to the valid buffer entry.
53D5
LD DE,5919H 11 19 59
Line Buffer Reset
Point Register Pair DE to 5919H - the start of the JCL line input buffer. This is the destination for the LDIR restore below.
53D8
XOR A,A AF
Set Register A to zero and clear all flags. Required before the 16-bit SBC comparison below.
53D9
SBC HL,DE ED 52
SBC HL,DE - subtract DE (5919H) from HL (current scan position). Result = number of bytes consumed from the line buffer.
53DB
LD B,H 44
Load Register B with Register H. High byte of the consumed byte count into BC.
53DC
LD C,L 4D
Load Register C with Register L. Low byte of the consumed byte count into BC. BC now = number of bytes to restore.
53DD
LD HL,5919H 21 19 59
Point Register Pair HL to 5919H - the start of the JCL line input buffer (source for LDIR).
53E0
LD DE,5959H 11 59 59
Point Register Pair DE to 5959H - the output command buffer (destination for LDIR).
53E3
LDIR ED B0
Block copy: BC bytes from 5919H (JCL line buffer) to 5959H (output buffer). Restores the consumed portion of the line into the output buffer for reprocessing.
53E5
JUMP to 5337H to continue scanning the JCL line from the restored position.

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.

53E8
LD HL,5919H 21 19 59
Point Register Pair HL to 5919H - the start of the JCL line input buffer where characters will be stored.
53EB
LD DE,56D2H 11 D2 56
Point Register Pair DE to 56D2H - the DCB (Device Control Block) for the JCL file input device.
53EE
Loop Start
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.
53F1
If the NZ FLAG has been set (device not ready), JUMP to 53FDH to check for end-of-file condition.
53F3
OR A,A B7
OR Register A with itself. Tests whether the byte received is zero (null/padding byte).
53F4
If the Z FLAG has been set (null byte received), JUMP to 5402H to signal end-of-file.
53F6
LD (HL),A 77
Store Register A into the line buffer at (HL). Saves the received character into the JCL line input buffer.
53F7
INC HL 23
INCrement Register Pair HL by 1. Advance the line buffer write pointer.
53F8
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). If Register A equals 0DH, the Z FLAG is set - end of line reached.
53FA
If the NZ FLAG has been set (not end of line), LOOP BACK to 53EEH to read the next character.
53FC
RET C9
Return to caller with Z FLAG set (A = 0DH). A complete line has been read into the buffer successfully.
53FD
CP A,1CH FE 1C
Compare Register A against 1CH (end-of-file marker). If Register A equals 1CH, the Z FLAG is set; otherwise the NZ FLAG is set.
53FF
If the NZ FLAG has been set (device error - not an EOF marker), JUMP to 56A9H - the JCL error exit.
5402
LD A,1CH 3E 1C
Load Register A with 1CH (end-of-file marker). Ensures A = 1CH whether we arrived from the null-byte path or the device-not-ready path.
5404
OR A,A B7
OR Register A with itself. Sets the NZ FLAG (A = 1CH, non-zero). Signals to the caller that end-of-file was reached - no line was read.
5405
RET C9
Return to caller with NZ FLAG set (A = 1CH). Caller at 5315H tests NZ to branch to the buffer management loop at 527EH.

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.

5406
LD HL,(58F4H) 2A F4 58
Load Register Pair HL from (58F4H). Fetch the current output buffer write pointer.
5409
LD A,(HL) 7E
Load Register A from (HL). Fetch the byte at the current output buffer position.
540A
OR A,A B7
OR Register A with itself. Tests whether the buffer position is empty (zero).
540B
RET NZ C0
If the NZ FLAG has been set (buffer is non-empty - a command is already pending), Return to caller without flushing. The pending command will be submitted first.
540C
LD HL,5959H 21 59 59
Point Register Pair HL to 5959H - the output command buffer containing the processed JCL line.
540F
LD DE,4358H 11 58 43
Point Register Pair DE to 4358H - the system FCB buffer, destination for the output line.
5412
LD A,(HL) 7E
Load Register A with the first character of the output line.
5413
CP A,2FH FE 2F
Compare Register A against 2FH (ASCII: /). If Register A equals 2FH, the Z FLAG is set; otherwise the NZ FLAG is set.
5415
If the NZ FLAG has been set (line does not start with '/'), JUMP to 5427H to transmit the line character by character.
5417
INC HL 23
INCrement Register Pair HL by 1. Advance to the second character to check for '//'.
5418
CP A,(HL) BE
Compare Register A (/) against the second character at (HL). If also '/', the Z FLAG is set - this is a '//' marker.
5419
DEC HL 2B
DECrement Register Pair HL by 1. Restore HL to point at the first character regardless of the comparison result.
541A
If the NZ FLAG has been set (second character is not '/'), JUMP to 5427H to transmit as a normal line.
541C
LD A,(595BH) 3A 5B 59
Load Register A from (595BH). Fetch the character stored two bytes past the start of the output buffer - the character following '//'.
541F
CP A,2EH FE 2E
Compare Register A against 2EH (ASCII: .). If Register A equals 2EH, the Z FLAG is set - the '//' is followed by a dot, indicating a display-only directive.
5421
If the NZ FLAG has been set (not '//.' pattern), JUMP to 5427H to transmit normally.
5423
GOSUB to SYS0 routine at 4467H to display the message at HL on screen. The '//.' directive causes the line to be echoed to the display rather than submitted as a command.
5426
RET C9
Return to caller. The display-only directive has been processed.
5427
LD A,(HL) 7E
Transmit Loop Start
Load Register A with the current character from the output line buffer.
5428
GOSUB to ROM routine at 001BH to output Register A to the device at DE (4358H). Returns Z FLAG set if device ready, NZ if not ready.
542B
If the NZ FLAG has been set (device not ready - output failed), JUMP to 56A9H - the JCL error exit.
542E
LD A,(HL) 7E
Load Register A with the current character again (001BH may have altered A).
542F
INC HL 23
INCrement Register Pair HL by 1. Advance the output line pointer to the next character.
5430
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). If Register A equals 0DH, the Z FLAG is set - end of line reached.
5432
If the NZ FLAG has been set (not end of line), LOOP BACK to 5427H to transmit the next character.
5434
RET C9
Return to caller. The complete output line has been transmitted to the device.

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.

5435-543AH
DEFM "IF " 49 46 20 20 20 20
Keyword: IF - 6-character space-padded string.
543B-543CH
DEFW 5481H 81 54
Handler address for IF: 5481H (JCL conditional block dispatcher).
543D-5442H
DEFM "ELSE " 45 4C 53 45 20 20
Keyword: ELSE - 6-character space-padded string.
5443-5444H
DEFW 54A2H A2 54
Handler address for ELSE: 54A2H (complement condition flag handler).
5445-544AH
DEFM "END " 45 4E 44 20 20 20
Keyword: END - 6-character space-padded string.
544B-544CH
DEFW 54BAH BA 54
Handler address for END: 54BAH (QUIT/INCLUDE handler - decrements buffer pointer).
544D
DEFB 00H 00
Table terminator. 00H marks the end of the if/then/else dispatch table.
544E-5455H
DEFM "SET " 53 45 54 20 20 20 20 20
Keyword: SET - 6-character space-padded string.
5456-5457H
DEFW 54F6H F6 54
Handler address for SET: 54F6H (to be disassembled).
5458-545FH
DEFM "RESET " 52 45 53 45 54 20 20 20
Keyword: RESET - 6-character space-padded string.
5460-5461H
DEFW 5505H 05 55
Handler address for RESET: 5505H (to be disassembled).
5462-5469H
DEFM "ASSIGN " 41 53 53 49 47 4E 20 20
Keyword: ASSIGN - 6-character string (no padding needed).
546A-546BH
DEFW 5539H 39 55
Handler address for ASSIGN: 5539H (to be disassembled).
546C-5474H
DEFM "INCLUDE " 49 4E 43 4C 55 44 45 20
Keyword: INCLUDE (truncated to 6 chars "INCLUD") - 6-character string.
5475-5476H
DEFW 5539H 39 55
Handler address for INCLUDE: 5539H (to be disassembled).
5477-547DH
DEFM "QUIT " 51 55 49 54 20 20 20 20
Keyword: QUIT - 6-character space-padded string.
547E-547FH
DEFW 5568H 68 55
Handler address for QUIT: 5568H (to be disassembled).
5480
DEFB 00H 00
Table terminator. 00H marks the end of the keyword dispatch table.

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).

5481
GOSUB to 54D0H to read and evaluate an optional condition token. Returns Z FLAG set if condition is true (or absent), NZ if false. A = condition character or 00H.
5484
If the Z FLAG has been set (condition is true or no condition), JUMP to 5491H to set the condition flag to 00H (execute).
5486
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). If Register A equals 0DH, the Z FLAG is set; otherwise the NZ FLAG is set.
5488
If the Z FLAG has been set (end of line with no condition result), JUMP to 5494H to set the condition flag to FFH (skip).
548A
CP A,2CH FE 2C
Compare Register A against 2CH (ASCII: ,). If Register A equals 2CH, the Z FLAG is set; otherwise the NZ FLAG is set.
548C
If the Z FLAG has been set (comma separator found), LOOP BACK to 5481H to read the next condition token.
548E
JUMP to 53A9H to display the JCL syntax error message and exit. The condition delimiter was unrecognised.
5491
XOR A,A AF
Set Register A to zero. Condition flag = 00H (condition true - execute this block).
5492
JUMP to 5496H to store the condition flag.
5494
LD A,0FFH 3E FF
Load Register A with FFH. Condition flag = FFH (condition false - skip this block).
5496
LD HL,(58F4H) 2A F4 58
Load Register Pair HL from (58F4H). Fetch the output buffer write pointer.
5499
OR A,(HL) B6
OR Register A with the byte at (HL). Merges the new condition flag with any existing flag byte at the current buffer position.
549A
INC HL 23
INCrement Register Pair HL by 1. Advance past the flag byte to the next buffer position.
549B
LD (HL),A 77
Store Register A into the buffer at (HL). Writes the merged condition flag into the output buffer.
549C
LD (58F4H),HL 22 F4 58
Store Register Pair HL to (58F4H). Advances the output buffer write pointer past the stored flag byte.
549F
JUMP to 5312H to read the next JCL record and continue processing.

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.

54BA
LD HL,(58F4H) 2A F4 58
Load Register Pair HL from (58F4H). Fetch the current output buffer write pointer.
54BD
LD DE,58F6H 11 F6 58
Point Register Pair DE to 58F6H - the output buffer limit address.
54C0
XOR A,A AF
Set Register A to zero and clear all flags. Required before the 16-bit SBC comparison below.
54C1
SBC HL,DE ED 52
SBC HL,DE - subtract DE (58F6H) from HL (current pointer). Z FLAG set if pointer has reached the limit.
54C3
If the Z FLAG has been set (buffer limit reached), JUMP to 53A9H to display an error and exit.
54C6
LD HL,(58F4H) 2A F4 58
Load Register Pair HL from (58F4H). Reload HL with the buffer pointer (SBC destroyed it above).
54C9
DEC HL 2B
DECrement Register Pair HL by 1. Step the buffer write pointer back by one position.
54CA
LD (58F4H),HL 22 F4 58
Store Register Pair HL to (58F4H). Updates the output buffer write pointer to the decremented value.
54CD
JUMP to 5312H to read the next JCL record and continue 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.

54D0
Loop Start
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.
54D3
RET NZ C0
If the NZ FLAG has been set (no valid token - end of line or delimiter), Return to caller with NZ.
54D4
CP A,2EH FE 2E
Compare Register A against 2EH (ASCII: .). If Register A equals 2EH, the Z FLAG is set - a dot separator was found between condition tokens.
54D6
If the Z FLAG has been set (dot separator), LOOP BACK to 54D0H to skip the dot and read the next condition token.
54D8
XOR A,A AF
Set Register A to zero and clear all flags. Sets Z FLAG to signal a true condition result to the caller.
54D9
RET C9
Return to caller with Z FLAG set (A = 00H = condition true).

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.

54DA
LD A,(HL) 7E
Load Register A with the current character from the JCL line scan pointer.
54DB
CP A,2DH FE 2D
Compare Register A against 2DH (ASCII: -). If Register A equals 2DH, the Z FLAG is set - a negation prefix was found.
54DD
If the NZ FLAG has been set (not a '-'), JUMP to 54E9H to read the token character directly without negation.
54DF
GOSUB to 54E9H to read the actual token character following the '-' negation prefix. Returns A = token character, Z FLAG set if valid.
54E2
If the NZ FLAG has been set (no valid token after '-'), JUMP to 54E7H to return NZ to caller.
54E4
OR A,01H F6 01
OR Register A with 01H. Sets bit 0 of the token character to flag that this token was negated by the '-' prefix.
54E6
RET C9
Return to caller with Z FLAG set and bit 0 of A set (negated token character).
54E7
XOR A,A AF
Set Register A to zero and clear all flags. Returns Z FLAG set with A = 00H to signal no valid token.
54E8
RET C9
Return to caller with NZ FLAG set - no valid token was found after the '-' prefix.

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.

54E9
GOSUB to 5577H to read the next token from the JCL line. Returns A = delimiter character, Z FLAG set if token was empty.
54EC
RET NZ C0
If the NZ FLAG has been set (non-empty token read - delimiter found immediately), Return to caller with NZ.
54ED
PUSH AF F5
Save Register Pair AF onto the stack. Preserves the delimiter character and flags across the symbol lookup below.
54EE
PUSH HL E5
Save Register Pair HL onto the stack. Preserves the JCL line scan pointer across the symbol lookup below.
54EF
GOSUB to 5628H to look up the token just read in the symbol table. Returns Z FLAG set if found, DE pointing to the symbol value.
54F2
POP HL E1
Restore Register Pair HL from the stack. Recovers the JCL line scan pointer.
54F3
POP BC C1
Pop Register Pair BC from the stack. Recovers the saved AF into BC (B = saved A = delimiter character).
54F4
LD A,B 78
Load Register A with Register B. Restores A to the delimiter character that was saved before the symbol lookup.
54F5
RET C9
Return to caller. A = delimiter character from 5577H; Z FLAG reflects the result of the 5628H symbol lookup (Z = found, NZ = not found).

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.

54F6
GOSUB to 5577H to read the next token from the JCL line into the token buffer at 56F2H. Returns A = delimiter character, NZ if no token read.
54F9
If the NZ FLAG has been set (no token found), JUMP to 53A9H to display a JCL syntax error and exit.
54FC
GOSUB to 5628H to look up the token in the symbol table. Returns Z FLAG set if found, NZ if not found. DE points to the symbol entry if found.
54FF
If the NZ FLAG has been set (symbol not found in table), GOSUB to 560BH to create a new symbol table entry for this token.
5502
JUMP to 5312H to read the next JCL record and continue 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.

5505
GOSUB to 5577H to read the next token from the JCL line into the token buffer at 56F2H. Returns A = delimiter character, NZ if no token read.
5508
If the NZ FLAG has been set (no token found), JUMP to 53A9H to display a JCL syntax error and exit.
550B
GOSUB to 5628H to look up the token in the symbol table. Returns Z FLAG set if found, NZ if not found. DE points to the symbol entry if found.
550E
If the NZ FLAG has been set (symbol not found - nothing to reset), JUMP to 5312H to continue JCL processing silently.
5511
LD HL,0FFF8H 21 F8 FF
Load Register Pair HL with 0FFF8H (-8 in two's complement). Used as an offset to step back 8 bytes from the symbol value field to the start of the symbol name entry.
5514
ADD HL,DE 19
ADD Register Pair DE to HL. HL = DE - 8. Points to the symbol name field of the located symbol entry.
5515
LD (HL),20H 36 20
Store 20H (space character) into (HL). Clears the first byte of the symbol name field, effectively marking this symbol table entry as deleted/empty.
5517
JUMP to 5312H to read the next JCL record and continue 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.

551A
GOSUB to 5577H to read the variable name token from the JCL line. Returns A = delimiter character, NZ if no token read.
551D
If the NZ FLAG has been set (no token found), JUMP to 53A9H to display a JCL syntax error and exit.
5520
PUSH AF F5
Save Register Pair AF onto the stack. Preserves the delimiter character across the symbol table lookup below.
5521
GOSUB to 5628H to look up the variable name in the symbol table. Returns Z FLAG set if found, NZ if not found. DE points to the symbol entry.
5524
If the NZ FLAG has been set (symbol not yet in table), GOSUB to 560BH to create a new symbol table entry for this variable name.
5527
POP AF F1
Restore Register Pair AF from the stack. Recovers the delimiter character that followed the variable name.
5528
CP A,3DH FE 3D
Compare Register A against 3DH (ASCII: =). If Register A equals 3DH, the Z FLAG is set - the expected equals sign delimiter was found.
552A
If the NZ FLAG has been set (delimiter was not '='), JUMP to 53A9H to display a JCL syntax error and exit.
552D
GOSUB to 5582H to read the value token from the JCL line into the value buffer at 56FBH. Returns NZ if the value is too long or invalid.
5530
If the NZ FLAG has been set (value token invalid or too long), JUMP to 53A9H to display a JCL syntax error and exit.
5533
GOSUB to 561DH to copy the value from the value buffer at 56FAH into the symbol table entry pointed to by DE. Stores 33 bytes (21H) of value data.
5536
JUMP to 5312H to read the next JCL record and continue processing.

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.

5539
PUSH HL E5
Save Register Pair HL onto the stack. Preserves the JCL line scan pointer across the file open operations below.
553A
LD DE,(57B2H) ED 5B B2 57
Load Register Pair DE from (57B2H). Fetches the current overlay parameter block pointer - source for the 32-byte template copy.
553E
LD HL,56D2H 21 D2 56
Point Register Pair HL to 56D2H - the file template buffer, destination for the parameter block copy.
5541
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H (32 decimal). Sets the byte count for the LDIR block copy below.
5544
LDIR ED B0
Block copy 32 bytes from the overlay parameter block at (57B2H) to the template buffer at 56D2H, incrementing both pointers. Sets up the file control block template for the INCLUDE file open.
5546
LD (57B2H),DE ED 53 B2 57
Store Register Pair DE to (57B2H). Updates the overlay parameter block pointer past the copied region.
554A
POP HL E1
Restore Register Pair HL from the stack. Recovers the JCL line scan pointer.
554B
LD DE,56D2H 11 D2 56
Point Register Pair DE to 56D2H - the file template buffer just populated. DE = FCB template for the extract filespec call.
554E
GOSUB to SYS0 routine at 441CH to extract the filespec from the JCL line at HL into the FCB template at DE. Returns NZ if extraction failed.
5551
If the NZ FLAG has been set (filespec extraction failed), JUMP to 53A9H to display a JCL syntax error and exit.
5554
LD HL,5722H 21 22 57
Point Register Pair HL to 5722H - the default file extension string for INCLUDE files (e.g. "/JCL").
5557
GOSUB to SYS0 routine at 4473H to insert the default extension from HL into the FCB at 56D2H if no extension was specified.
555A
LD HL,5A00H 21 00 5A
Point Register Pair HL to 5A00H - the FCB work area for the INCLUDE file open.
555D
LD B,00H 06 00
Load Register B with 00H. Sets the open mode parameter for CALL 4424H (mode 0 = read-only open).
555F
GOSUB to SYS0 routine at 4424H to open the INCLUDE file. HL = FCB work area, B = open mode. Returns NZ if the file could not be opened.
5562
If the NZ FLAG has been set (file open failed), JUMP to 53A9H to display a JCL syntax error and exit.
5565
JUMP to 5312H to read the next JCL record - which will now be read from the newly opened INCLUDE file.

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.

5568
LD HL,5919H 21 19 59
Point Register Pair HL to 5919H - the QUIT message string.
556B
GOSUB to SYS0 routine at 447BH to display the message at HL on screen with CONFIG/SYS processing.
556E
LD DE,4358H 11 58 43
Point Register Pair DE to 4358H - the system FCB for the JCL device currently open.
5571
GOSUB to SYS0 routine at 4428H to close the JCL device FCB at DE.
5574
JUMP to SYS0 routine at 402DH - the DOS READY / No-Error Exit. Returns control to the DOS command prompt.

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.

5577
PUSH DE D5
Save Register Pair DE onto the stack. Preserves the caller's DE (symbol table pointer) across the token read.
5578
LD B,08H 06 08
Load Register B with 08H (8). Sets the maximum token length to 8 characters.
557A
LD DE,56F2H 11 F2 56
Point Register Pair DE to 56F2H - the token output buffer.
557D
GOSUB to 55C0H to read characters from the JCL line into the buffer at DE, up to B characters, stopping at a recognised delimiter. Returns A = delimiter, Z set if at least one character was stored.
5580
POP DE D1
Restore Register Pair DE from the stack. Recovers the caller's symbol table pointer.
5581
RET C9
Return to caller. A = delimiter character; Z FLAG set if token was non-empty.

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.

5582
PUSH DE D5
Save Register Pair DE onto the stack. Preserves the caller's DE across the value read.
5583
LD B,20H 06 20
Load Register B with 20H (32). Sets the maximum value length to 32 characters.
5585
LD DE,56FBH 11 FB 56
Point Register Pair DE to 56FBH - the value output buffer.
5588
GOSUB to 55A1H to read the value token from the JCL line into the buffer at DE, stopping at a recognised delimiter. Returns DE pointing past the last stored character.
558B
PUSH AF F5
Save Register Pair AF onto the stack. Preserves the delimiter character and flags across the length computation below.
558C
PUSH HL E5
Save Register Pair HL onto the stack. Preserves the JCL line scan pointer.
558D
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now points past the last stored value character (DE after 55A1H); DE holds the old HL (scan pointer).
558E
LD DE,56FBH 11 FB 56
Point Register Pair DE to 56FBH - the start of the value buffer. Used to compute the value length by subtraction.
5591
XOR A,A AF
Set Register A to zero and clear all flags. Clears the carry flag before the 16-bit SBC below.
5592
SBC HL,DE ED 52
SBC HL,DE - subtract DE (56FBH) from HL (end of value). Result in HL = number of characters stored in the value buffer.
5594
LD A,L 7D
Load Register A with Register L. A = low byte of the computed value length.
5595
CP A,21H FE 21
Compare Register A against 21H (33 decimal). If Register A >= 21H, the value is too long (more than 32 characters).
5597
If the NO CARRY FLAG has been set (length >= 33 - value too long), JUMP to 56A9H - the JCL error exit.
559A
LD (56FAH),A 32 FA 56
Self-Modifying Code
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.
559D
POP HL E1
Restore Register Pair HL from the stack. Recovers the JCL line scan pointer.
559E
POP AF F1
Restore Register Pair AF from the stack. Recovers the delimiter character.
559F
POP DE D1
Restore Register Pair DE from the stack. Recovers the caller's symbol table pointer.
55A0
RET C9
Return to caller. A = delimiter character; Z FLAG set if value was valid and within length limit.

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.

55A1
Loop Start
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.
55A4
CP A,20H FE 20
Compare Register A against 20H (space). If Register A equals 20H, the Z FLAG is set - space delimiter found.
55A6
RET Z C8
If the Z FLAG has been set (space delimiter), Return to caller. Token reading is complete.
55A7
CP A,2CH FE 2C
Compare Register A against 2CH (ASCII: ,). If Register A equals 2CH, the Z FLAG is set - comma delimiter found.
55A9
RET Z C8
If the Z FLAG has been set (comma delimiter), Return to caller.
55AA
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). If Register A equals 0DH, the Z FLAG is set - end of line reached.
55AC
RET Z C8
If the Z FLAG has been set (carriage return - end of line), Return to caller.
55AD
CP A,3DH FE 3D
Compare Register A against 3DH (ASCII: =). If Register A equals 3DH, the Z FLAG is set - equals sign delimiter found.
55AF
RET Z C8
If the Z FLAG has been set (equals delimiter), Return to caller.
55B0
CP A,28H FE 28
Compare Register A against 28H (ASCII: (). If Register A equals 28H, the Z FLAG is set - open parenthesis delimiter found.
55B2
RET Z C8
If the Z FLAG has been set (open parenthesis delimiter), Return to caller.
55B3
CP A,29H FE 29
Compare Register A against 29H (ASCII: )). If Register A equals 29H, the Z FLAG is set - close parenthesis delimiter found.
55B5
RET Z C8
If the Z FLAG has been set (close parenthesis delimiter), Return to caller.
55B6
CP A,23H FE 23
Compare Register A against 23H (ASCII: #). If Register A does not equal 23H, the NZ FLAG is set - not a comment marker, continue reading.
55B8
If the NZ FLAG has been set (not a hash comment marker), JUMP back to 55A1H to read the next character.
55BA
GOSUB to 536FH to skip to the end of the current JCL line (comment handler). Advances HL past all remaining characters up to the carriage return.
55BD
LD A,(HL) 7E
Load Register A with (HL). Fetches the character at the new scan position after the comment skip - expected to be 0DH (carriage return).
55BE
JUMP to 55A4H to check the carriage return as a delimiter and return to caller.

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).

55C0
LD A,B 78
Load Register A with Register B. Saves the current remaining character count into A before incrementing B.
55C1
LD (5607H),A 32 07 56
Self-Modifying Code
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.
55C4
INC B 04
Increment Register B. Pre-increments the character count so that the DEC B at 55EDH will correctly reflect characters remaining.
55C5
LD A,(HL) 7E
Loop Start
Load Register A with (HL). Fetches the next character from the JCL line scan pointer.
55C6
CP A,03H FE 03
Compare Register A against 03H (end-of-text marker). If Register A equals 03H, the Z FLAG is set - end of JCL data reached.
55C8
If the Z FLAG has been set (end-of-text), JUMP to 55FBH to pad the buffer with spaces and return.
55CA
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). If Register A equals 0DH, the Z FLAG is set - end of line reached.
55CC
If the Z FLAG has been set (carriage return), JUMP to 55FBH to pad the buffer and return.
55CE
CP A,28H FE 28
Compare Register A against 28H (ASCII: (). If Register A equals 28H, the Z FLAG is set - open parenthesis delimiter found.
55D0
If the Z FLAG has been set (open parenthesis), JUMP to 55FBH to pad the buffer and return.
55D2
INC HL 23
Increment Register Pair HL. Advances the JCL scan pointer past the current character.
55D3
CP A,30H FE 30
Compare Register A against 30H (ASCII: 0). If Register A is below 30H, the character is below the digit range - not a valid alphanumeric.
55D5
If the CARRY FLAG has been set (character below '0'), JUMP to 55FBH to pad the buffer and return with this delimiter.
55D7
CP A,3AH FE 3A
Compare Register A against 3AH (one above ASCII: 9). If Register A is below 3AH, the character is a digit (30H-39H).
55D9
If the CARRY FLAG has been set (character is a digit '0'-'9'), JUMP to 55EDH to store it into the buffer.
55DB
CP A,41H FE 41
Compare Register A against 41H (ASCII: A). If Register A is below 41H, the character is between ':' and '@' - not alphanumeric.
55DD
If the CARRY FLAG has been set (character between ':' and '@'), JUMP to 55FBH to pad and return.
55DF
CP A,5BH FE 5B
Compare Register A against 5BH (one above ASCII: Z). If Register A is below 5BH, the character is an uppercase letter (41H-5AH).
55E1
If the CARRY FLAG has been set (character is uppercase 'A'-'Z'), JUMP to 55EDH to store it into the buffer.
55E3
CP A,61H FE 61
Compare Register A against 61H (ASCII: a). If Register A is below 61H, the character is between '[' and '`' - not alphanumeric.
55E5
If the CARRY FLAG has been set (character between '[' and '`'), JUMP to 55FBH to pad and return.
55E7
CP A,7BH FE 7B
Compare Register A against 7BH (one above ASCII: z). If Register A is 7BH or above, the character is above the lowercase range - not alphanumeric.
55E9
If the NO CARRY FLAG has been set (character above 'z'), JUMP to 55FBH to pad and return.
55EB
SUB A,20H D6 20
Subtract 20H from Register A. Converts lowercase letter (61H-7AH) to uppercase (41H-5AH) by clearing bit 5.
55ED
DEC B 05
Decrement Register B. Reduces the remaining character count. If B reaches zero, the buffer is full.
55EE
If the Z FLAG has been set (buffer now full after this character), JUMP to 55F8H to store this last character and then pad.
55F0
LD (DE),A 12
Store Register A into (DE). Writes the validated (and possibly uppercased) character into the output buffer.
55F1
XOR A,A AF
Set Register A to zero and clear all flags. Clears the overflow indicator before storing it at 5607H.
55F2
LD (5607H),A 32 07 56
Self-Modifying Code
Store 00H to (5607H). Clears the overflow flag now that a character has been successfully stored.
55F5
INC DE 13
Increment Register Pair DE. Advances the output buffer pointer past the character just stored.
55F6
JUMP back to 55C5H to fetch the next character from the JCL line.
55F8
INC B 04
Increment Register B. Restores B to 01H so the DJNZ loop at 5603H executes once less (the current character is stored below before padding begins).
55F9
JUMP back to 55C5H to fetch the next character - this continues scanning past the buffer limit without storing, until a delimiter is found.
55FB
LD C,A 4F
Load Register C with Register A. Saves the delimiter character into C for retrieval after the padding loop below.
55FC
PUSH DE D5
Save Register Pair DE onto the stack. Preserves the current output buffer pointer across the padding loop.
55FD
JUMP to 5603H to enter the padding loop at the DJNZ test, skipping the first LD/INC so that B is tested before storing the first pad character.
55FF
LD A,20H 3E 20
Loop Start
Load Register A with 20H (space). Prepares a space character for padding the remainder of the output buffer.
5601
LD (DE),A 12
Store 20H (space) into (DE). Writes one space padding character into the output buffer.
5602
INC DE 13
Increment Register Pair DE. Advances the output buffer pointer past the padding space just stored.
5603
Loop End
Decrement Register B and JUMP to 55FFH if B is not zero. Continues padding with spaces until the full buffer width is filled.
5605
POP DE D1
Restore Register Pair DE from the stack. Recovers the output buffer pointer from before the padding loop.
5606
LD A,00H 3E 00
Load Register A with 00H. This is the first byte of the two-byte sequence at 5606H-5608H.
5608
OR A,A B7
Self-Modifying Code
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.
5609
LD A,C 79
Load Register A with Register C. Restores A to the delimiter character saved at 55FBH.
560A
RET C9
Return to caller. A = delimiter character; Z FLAG reflects whether the buffer was filled normally (Z) or overflowed (NZ).

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.

560B
PUSH HL E5
Save Register Pair HL onto the stack. Preserves the JCL line scan pointer across the copy operations below.
560C
LD HL,56F2H 21 F2 56
Point Register Pair HL to 56F2H - the token buffer holding the 8-character symbol name just read by 5577H.
560F
LD BC,0008H 01 08 00
Load Register Pair BC with 0008H (8). Sets the byte count to copy the full 8-character symbol name.
5612
LDIR ED B0
Block copy 8 bytes from the token buffer at 56F2H into the symbol table entry at DE, advancing both pointers. Writes the symbol name into the new entry.
5614
XOR A,A AF
Set Register A to zero and clear all flags. Prepares 00H to write as the symbol name terminator and value field end marker.
5615
LD (DE),A 12
Store 00H into (DE). Writes a null terminator immediately after the 8-byte symbol name field in the new entry.
5616
LD HL,0021H 21 21 00
Load Register Pair HL with 0021H (33 decimal). Used as an offset to reach the end of the 33-byte value field that follows the name.
5619
ADD HL,DE 19
ADD Register Pair DE to HL. HL = DE + 33 - points to the byte just past the end of the value field in the new symbol entry.
561A
LD (HL),A 77
Store 00H into (HL). Writes a null terminator at the end of the value field, marking this as a newly created empty entry.
561B
POP HL E1
Restore Register Pair HL from the stack. Recovers the JCL line scan pointer.
561C
RET C9
Return to caller. The new symbol table entry at the original DE has been initialised with the name from 56F2H and empty value fields.

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.

561D
PUSH HL E5
Save Register Pair HL onto the stack. Preserves the JCL line scan pointer across the copy below.
561E
LD HL,56FAH 21 FA 56
Point Register Pair HL to 56FAH - the value buffer starting with the length byte followed by up to 32 value characters.
5621
LD BC,0021H 01 21 00
Load Register Pair BC with 0021H (33 decimal). Sets the byte count to copy the length byte plus the full 32-byte value field.
5624
LDIR ED B0
Block copy 33 bytes from the value buffer at 56FAH into the symbol table entry at DE, advancing both pointers. Writes the complete value (length + string) into the entry.
5626
POP HL E1
Restore Register Pair HL from the stack. Recovers the JCL line scan pointer.
5627
RET C9
Return to caller. The symbol table entry at the original DE now contains the value from the ASSIGN statement.

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.

5628
PUSH HL E5
Save Register Pair HL onto the stack. Preserves the JCL line scan pointer across the table search below.
5629
LD DE,56F2H 11 F2 56
Point Register Pair DE to 56F2H - the token buffer holding the 8-character symbol name to search for.
562C
LD HL,5C00H 21 00 5C
Point Register Pair HL to 5C00H - the start of the user variable symbol table.
562F
LD B,08H 06 08
Load Register B with 08H (8). Sets the key comparison length to 8 characters (full symbol name width).
5631
LD C,29H 0E 29
Load Register C with 29H (41 decimal). Sets the entry stride - each symbol table entry is 41 bytes (8-byte name + 1 terminator + 32-byte value + 1 end marker).
5633
GOSUB to 5660H to search the table at HL for a match against the key at DE. Returns HL pointing to the matching entry, Z FLAG set if found, NZ if not found.
5636
LD D,H 54
Load Register D with Register H. Copies the high byte of the match pointer into D.
5637
LD E,L 5D
Load Register E with Register L. Copies the low byte of the match pointer into E. DE now points to the matched symbol table entry (or past end-of-table if not found).
5638
POP HL E1
Restore Register Pair HL from the stack. Recovers the JCL line scan pointer.
5639
RET C9
Return to caller. DE points to the located or next-available symbol table entry; Z FLAG set if name was found.

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.

563A
PUSH HL E5
Save Register Pair HL onto the stack. Preserves the JCL line scan pointer across the table search below.
563B
LD DE,56F2H 11 F2 56
Point Register Pair DE to 56F2H - the token buffer holding the 6-character keyword to search for.
563E
LD HL,5435H 21 35 54
Point Register Pair HL to 5435H - the start of the JCL keyword dispatch table.
5641
LD B,06H 06 06
Load Register B with 06H (6). Sets the key comparison length to 6 characters (full keyword name width).
5643
LD C,08H 0E 08
Load Register C with 08H (8). Sets the entry stride - each keyword table entry is 8 bytes (6-byte name + 2-byte handler address).
5645
GOSUB to 5660H to search the keyword table at HL for a match against the token at DE. Returns HL pointing to the byte after the matched name (i.e. the handler address field), Z FLAG set if found, NZ if not found.
5648
LD E,(HL) 5E
Load Register E with (HL). Fetches the low byte of the handler address from the matched keyword table entry.
5649
INC HL 23
Increment Register Pair HL. Advances past the low byte to the high byte of the handler address.
564A
LD D,(HL) 56
Load Register D with (HL). Fetches the high byte of the handler address. DE now holds the complete 16-bit handler address for the matched keyword.
564B
POP HL E1
Restore Register Pair HL from the stack. Recovers the JCL line scan pointer.
564C
RET C9
Return to caller. DE = handler address for the matched keyword; Z FLAG set if keyword was found.

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.

564D
PUSH HL E5
Save Register Pair HL onto the stack. Preserves the JCL line scan pointer across the table search below.
564E
LD DE,56F2H 11 F2 56
Point Register Pair DE to 56F2H - the token buffer holding the operator keyword to search for.
5651
LD HL,544EH 21 4E 54
Point Register Pair HL to 544EH - the start of the operator keyword table.
5654
LD B,08H 06 08
Load Register B with 08H (8). Sets the key comparison length to 8 characters.
5656
LD C,0AH 0E 0A
Load Register C with 0AH (10). Sets the entry stride - each operator table entry is 10 bytes (8-byte name + 2-byte handler address).
5658
GOSUB to 5660H to search the operator table at HL for a match against the token at DE. Returns HL pointing to the handler address field of the matched entry, Z FLAG set if found.
565B
LD E,(HL) 5E
Load Register E with (HL). Fetches the low byte of the operator handler address.
565C
INC HL 23
Increment Register Pair HL. Advances past the low byte to the high byte of the handler address.
565D
LD D,(HL) 56
Load Register D with (HL). Fetches the high byte of the operator handler address. DE now holds the complete handler address.
565E
POP HL E1
Restore Register Pair HL from the stack. Recovers the JCL line scan pointer.
565F
RET C9
Return to caller. DE = handler address for the matched operator; Z FLAG set if operator was found.

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).

5660
LD A,(HL) 7E
Loop Start
Load Register A with (HL). Fetches the first byte of the current table entry.
5661
OR A,A B7
OR Register A with itself. Sets the Z FLAG if the first byte is 00H - the table terminator.
5662
If the NZ FLAG has been set (entry is not the terminator), JUMP to 5666H to compare against the search key.
5664
INC A 3C
Increment Register A from 00H to 01H. Sets the NZ FLAG to signal that the table was exhausted with no match found.
5665
RET C9
Return to caller with NZ FLAG set - no match found in the table.
5666
LD A,(DE) 1A
Load Register A with (DE). Fetches the first character of the search key from the token buffer at 56F2H.
5667
CP A,(HL) BE
Compare Register A against (HL). Tests whether the first character of the search key matches the first character of the current table entry.
5668
If the Z FLAG has been set (first characters match), JUMP to 5671H to compare the remaining characters of the key.
566A
PUSH BC C5
Save Register Pair BC onto the stack. Preserves the key length and stride across the advance-to-next-entry calculation.
566B
LD B,00H 06 00
Load Register B with 00H. Clears B so that BC = 00CCH = stride value C, for use as a 16-bit offset in the ADD HL,BC below.
566D
ADD HL,BC 09
ADD Register Pair BC to HL. Advances HL by C bytes (the entry stride) to point to the next table entry.
566E
POP BC C1
Restore Register Pair BC from the stack. Recovers the key length and stride values.
566F
Loop End
JUMP back to 5660H to test the next table entry.
5671
PUSH HL E5
Full Key Comparison Start
Save Register Pair HL onto the stack. Preserves the table entry pointer for potential restoration if the full key comparison fails.
5672
PUSH DE D5
Save Register Pair DE onto the stack. Preserves the search key pointer for potential restoration.
5673
PUSH BC C5
Save Register Pair BC onto the stack. Preserves the key length and stride for potential restoration.
5674
INC DE 13
Loop Start
Increment Register Pair DE. Advances the search key pointer to the next character.
5675
INC HL 23
Increment Register Pair HL. Advances the table entry pointer to the next character.
5676
LD A,(DE) 1A
Load Register A with (DE). Fetches the next character from the search key.
5677
CP A,20H FE 20
Compare Register A against 20H (space). If the search key character is a space, it acts as a wildcard - matches any table entry character.
5679
If the Z FLAG has been set (search key character is space - wildcard), JUMP to 569FH to check if the table entry character also allows a match.
567B
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). A carriage return in the search key also acts as a wildcard match terminator.
567D
If the Z FLAG has been set (search key character is carriage return), JUMP to 569FH to handle the wildcard match.
567F
CP A,(HL) BE
Compare Register A (search key character) against (HL) (table entry character). Tests for an exact character match.
5680
If the NZ FLAG has been set (characters do not match), JUMP to 568FH to check for digit/letter range matching before abandoning this entry.
5682
Loop End
Decrement Register B and JUMP to 5674H if B is not zero. Continues comparing the remaining characters of the key.
5684
POP BC C1
Restore Register Pair BC from the stack. Full key matched - recover stride value.
5685
POP DE D1
Restore Register Pair DE from the stack. Recovers the original search key pointer.
5686
POP HL E1
Restore Register Pair HL from the stack. Recovers the table entry pointer at the start of the matched entry.
5687
PUSH BC C5
Save Register Pair BC onto the stack again. Temporarily saves stride/length so BC can be used for the pointer advance below.
5688
LD C,B 48
Load Register C with Register B. Copies the key length into C for use as the 16-bit advance offset.
5689
LD B,00H 06 00
Load Register B with 00H. Clears B so BC = 000BH = key length, for use as a 16-bit offset.
568B
ADD HL,BC 09
ADD Register Pair BC to HL. Advances HL past the matched name field to point at the handler address field within the entry.
568C
POP BC C1
Restore Register Pair BC from the stack. Recovers the original key length and stride values.
568D
XOR A,A AF
Set Register A to zero and clear all flags. Sets the Z FLAG to signal a successful match to the caller.
568E
RET C9
Return to caller with Z FLAG set. HL points to the handler address field of the matched entry.
568F
CP A,30H FE 30
Compare Register A against 30H (ASCII: 0). Tests if the mismatched search key character is below the digit range.
5691
If the CARRY FLAG has been set (character below '0'), JUMP to 569FH - not alphanumeric, treat as wildcard position.
5693
CP A,3AH FE 3A
Compare Register A against 3AH (one above '9'). Tests if the character is a digit (30H-39H).
5695
If the CARRY FLAG has been set (character is a digit '0'-'9'), JUMP to 56A4H - digit in key does not match table entry, this entry fails.
5697
CP A,41H FE 41
Compare Register A against 41H (ASCII: A). Tests if the character is between ':' and '@'.
5699
If the CARRY FLAG has been set (character between ':' and '@'), JUMP to 569FH - treat as wildcard position.
569B
CP A,5BH FE 5B
Compare Register A against 5BH (one above 'Z'). Tests if the character is an uppercase letter (41H-5AH).
569D
If the CARRY FLAG has been set (character is uppercase 'A'-'Z'), JUMP to 56A4H - letter in key does not match table entry, this entry fails.
569F
LD A,(HL) 7E
Load Register A with (HL). Fetches the current table entry character at this position for wildcard check.
56A0
CP A,20H FE 20
Compare Register A against 20H (space). If the table entry character is also a space, both key and table agree this position is a wildcard - the position matches.
56A2
If the Z FLAG has been set (table entry character is space - wildcard confirmed), JUMP to 5684H to record a successful full match.
56A4
POP BC C1
Restore Register Pair BC from the stack. Match failed at this character - recover stride and key length.
56A5
POP DE D1
Restore Register Pair DE from the stack. Recovers the original search key pointer.
56A6
POP HL E1
Restore Register Pair HL from the stack. Recovers the table entry pointer at the start of the failed entry.
56A7
JUMP to 566AH to advance HL by the entry stride C and try the next table entry.

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.

56A9
OR A,40H F6 40
OR Register A with 40H. Sets bit 6 of A as an error flag before passing to the DOS error handler.
56AB
JUMP to SYS0 routine at 4409H - the DOS Error Exit. A contains the error code with bit 6 set.

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.

56AE
LD HL,5726H 21 26 57
Point Register Pair HL to 5726H - the first JCL error message string.
56B1
GOSUB to SYS0 routine at 447BH to display the error message at HL on screen with CONFIG/SYS processing.
56B4
JUMP to SYS0 routine at 4030H - the error-already-displayed exit. Returns control to the DOS without printing a second error message.
56B7
LD HL,573AH 21 3A 57
Point Register Pair HL to 573AH - the second JCL error message string.
56BA
GOSUB to SYS0 routine at 447BH to display the error message at HL.
56BD
JUMP to SYS0 routine at 4030H - error-already-displayed exit.
56C0
LD HL,575EH 21 5E 57
Point Register Pair HL to 575EH - the third JCL error message string.
56C3
GOSUB to SYS0 routine at 447BH to display the error message at HL.
56C6
JUMP to SYS0 routine at 4030H - error-already-displayed exit.
56C9
LD HL,576EH 21 6E 57
Point Register Pair HL to 576EH - the fourth JCL error message string.
56CC
GOSUB to SYS0 routine at 447BH to display the error message at HL.
56CF
JUMP to SYS0 routine at 4030H - error-already-displayed exit.

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.

571B-5725H
DEFM "SYSTEM/JCL" + 03H 53 59 53 54 45 4D 2F 4A 43 4C 03
Default filespec string "SYSTEM/JCL". Used as the default path for system files.
5726-5739H
DEFM "FILE SPEC REQUIRED!" + 0DH 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 21 0D
Error message string "FILE SPEC REQUIRED!" terminated by 0DH. Displayed by the error stub at 56AEH when a JCL command is missing its filename argument.
573A-574BH
DEFM "CANT CREATE SYSTEM" 43 41 4E 54 20 43 52 45 41 54 45 20 53 59 53 54 45 4D
Error message fragment "CANT CREATE SYSTEM". Displayed by the error stub at 56B7H.
574C-5752H
DEFB 01H x 7 01 01 01 01 01 01 01
SOH (01H) control bytes. These are formatting control codes interpreted by the 447BH display routine to perform screen formatting operations between the two halves of the error message.
5754-575DH
"/JCL FILE!" + 0DH 2F 4A 43 4C 20 46 49 4C 45 21 0D
Continuation of the error message "/JCL FILE!" terminated by 0DH. Together with the fragment above forms "CANT CREATE SYSTEM/JCL FILE!".
575E-576DH
DEFM "PARAMETER ERROR" + 0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
Error message string "PARAMETER ERROR" terminated by 0DH. Displayed by the error stub at 56C0H when a JCL command receives an invalid or missing parameter.
576E-57ADH
DEFM "MULTIPLY DEFINED PARAMETER" + 0DH 4D 55 4C 54 49 50 4C 59 20 44 45 46 49 4E 45 44 20 50 41 52 41 4D 45 54 45 52 0D
Error message string "MULTIPLY DEFINED PARAMETER" terminated by 0DH. Displayed by the error stub at 56C9H when a symbol is defined more than once in the JCL file.
57AE-57B2H
DEFM "INVALID JCL FORMAT, PROCESSING ABORTED" + 0DH 49 4E 56 41 4C 49 44 20 4A 43 4C 20 46 4F 52 4D 41 54 2C 20 50 52 4F 43 45 53 53 49 4E 47 20 41 42 4F 52 54 45 44 0D
Error message string "INVALID JCL FORMAT, PROCESSING ABORTED" terminated by 0DH. General JCL syntax error message displayed when the command dispatcher cannot parse the JCL line.
57B3H
DEFB 57H 57
High byte of the overlay parameter block pointer stored at 57B2H-57B3H. The INCLUDE handler at 553AH reads LD DE,(57B2H) to fetch this 16-bit pointer. This byte (57H) is the address high byte, making the initial pointer value point within this data area.

58F4H - Data Bytes

Two isolated data bytes at 58F4H. Their purpose within the overlay is unclear without further context of what references this address.

58F4-58F5H
DEFB F6H, 58H F6 58
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
DEFB 00H 00
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 - 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.

5916-5917H
DEFM "--" 2D 2D
Two ASCII dash characters (2DH). Form a leading separator before the message text. The QUIT handler at 5568H loads HL,5919H pointing past these dashes - they may be used by a different caller referencing 5916H directly.
5918-5919H
DEFB 3EH, 00H 3E 00
Data bytes 3EH (ASCII: >), 00H. The 3EH may be a prompt character and 00H a string terminator, or these two bytes form the start of the message text pointed to by the QUIT handler at HL,5919H.

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.

5C00H
DEFB 00H 00
Table terminator byte. 00H in the first byte of the first entry signals an empty symbol table. As ASSIGN statements are processed, entries are written here at runtime.

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 RangePurpose
5200HEntry point
Load parameter table pointer (5679H), CALL 4476H to parse and dispatch.
5209H-522EHFAST/SLOW handlers
Toggle 430FH bit 3 and write to port 0FEH for processor speed.
522FH-524BHBASIC handler
Copy ROM re-entry stub to 4000H, JP 0075H for BASIC warm start.
524BH-526EHBREAK handler
Enable/disable BREAK key detection via 430FH bit 4 and 4C2DH.
526FH-5305HBLINK handler
Set cursor character, install/remove blink task.
5305H-547FHTYPE handler
Install type-ahead keyboard driver as scheduled task.
547FH-54DDHSLOWER/JKL-related handler
Install screen-print driver task.
54DDH-5572HJKL handler
Install screen-print interrupt handler.
5572H-55B4HGRAPHIC/ALIVE handler
Toggle alive/graphic status.
55B4H-5619HDRIVE/ENABLE/DISABLE/STEP/DELAY handlers
Drive configuration.
5619H-5626HSYSGEN handler
Save configuration to system disk.
5627H-565FHUtility routines and error messages.
5679H-5709HParameter 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 RangePurpose
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

AddressName and Purpose
5200HSYSTEM 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".
5209HFAST/SLOW Handlers
Toggle 430FH bit 3 and output to port 0FEH to switch processor clock speed.
522FHBASIC Handler
Copy ROM re-entry stub from 06D2H to 4000H (54 bytes), zero 39 bytes after, JP 0075H for BASIC warm start.
524BHBREAK Handler
Enable or disable BREAK key detection by writing C8H or 00H to 4C2DH and toggling 430FH bit 4.
526FHBLINK Handler
Set cursor character via self-modifying code at 52E6H/52F3H/52FCH, allocate 62-byte task block, install blink task via CALL 4410H.
52C5HLARGE Cursor Handler
Set cursor character to 8FH (large block), falls into BLINK installation.
528BHSMALL Cursor Handler
Set cursor character to 88H (small block), falls into BLINK installation.
5306HTYPE Handler
Install 214-byte type-ahead keyboard driver as scheduled task via CALL 4410H.
5480HSLOWER Handler
Install 30-byte task block for SLOWER function via CALL 4410H.
54DEHJKL Handler
Install 78-byte JKL screen-print handler as scheduled task via CALL 4410H.
54E9HGRAPHIC Handler
Toggle graphic printer capability flag.
5573HALIVE Handler
Toggle video RAM end-of-row marker between 86H and 89H at 3C3FH.
55B5HDRIVE Handler
Select target drive by number, set IY to drive parameter block via CALL 478FH.
55C5HDISABLE Handler
Write C9H (RET) to IY+00H to disable drive I/O handler vector.
55D3HENABLE Handler
Write C3H (JP) to IY+00H to re-enable drive I/O handler vector.
55DFHSTEP Handler
Set stepping rate bits (1:0) in IY+03H drive flags byte.
55FBHDELAY Handler
Set or clear motor-on delay bit (bit 2) in IY+03H drive flags byte.
561AHSYSGEN Handler
Save current system configuration to disk via JP 402DH or issue error 1CH/88H.
5627HCommand Level Check
Verify 402DH contains C3H (JP opcode), display "CAN'T" error if not at VTOS command level.
5660HParameter 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.

5200
LD DE,5679H 11 79 56
Point Register Pair DE to the parameter keyword table at 5679H. This table contains 18 keyword entries (FAST, SLOW, BASIC, BREAK, BLINK, LARGE, SMALL, TYPE, SLOWER, JKL, GRAPHIC, ALIVE, DRIVE, ENABLE, DISABLE, STEP, DELAY, SYSGEN), each consisting of a space-padded keyword string followed by a 2-byte handler address.
5203
GOSUB to the SYS0 parameter table search and dispatch routine at 4476H. This routine parses the command line for keywords matching the table entries pointed to by DE. For each match found, it loads Register Pair BC with the parameter value (0000H = absent/OFF, 0001H = present/ON, or a numeric value) and jumps to the handler address from the table entry. On return, the Z FLAG indicates whether all parameters were successfully processed.
5206
If the NZ FLAG (Not Zero) has been set, meaning a parameter keyword was not found in the table, JUMP to 5660H to display "PARAMETER ERROR" and exit via 4030H.

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.

5209
LD BC,0000H 01 00 00
Self-Modifying Code
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.
520C
LD A,B 78
Load Register A with the high byte of the SLOW parameter value (Register B).
520D
OR A,C B1
OR Register A with the low byte (Register C). If BC = 0000H (SLOW not specified), the Z FLAG is set.
520E
If the NZ FLAG (Not Zero) has been set, meaning the SLOW parameter was specified (BC is non-zero), JUMP to 5224H to switch to the standard-speed clock.

SLOW was not specified. Now check the FAST parameter.

5210
LD BC,0000H 01 00 00
Self-Modifying Code
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.
5213
LD A,B 78
Load Register A with the high byte of the FAST parameter value (Register B).
5214
OR A,C B1
OR Register A with the low byte (Register C). If BC = 0000H (FAST not specified), the Z FLAG is set.
5215
If the Z FLAG (Zero) has been set, meaning FAST was also not specified, JUMP to 522EH to skip both speed handlers and proceed to the BASIC parameter check.

FAST was specified. Enable the high-speed clock.

5217
LD HL,430FH 21 0F 43
Point Register Pair HL to the system state flags byte at 430FH. Bit 3 of this byte controls the FAST/SLOW clock state.
521A
DI F3
Disable interrupts to protect the atomic read-modify-write of the system state flags and hardware port output.
521B
SET 3,(HL) CB DE
SET bit 3 of the system state flags byte at 430FH, indicating the processor is now in FAST (high-speed) mode.
521D
LD A,01H 3E 01
Load Register A with 01H. This value will be sent to the speed control port to enable the high-speed clock.
521F
EI FB
Re-enable interrupts now that the flag and port write are complete.
5220
OUT (0FEH),A D3 FE
Output 01H to port 0FEH. On systems with a speed-up kit or Speedup Board, this switches the processor to the high-speed clock (typically 4 MHz instead of the standard 1.774 MHz on the Model I).
5222
JUMP unconditionally to 522EH to skip the SLOW handler and proceed to the BASIC parameter check.

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.

5224
LD HL,430FH 21 0F 43
Point Register Pair HL to the system state flags byte at 430FH.
5227
DI F3
Disable interrupts to protect the atomic modification of the system state flags and hardware port.
5228
RES 3,(HL) CB 9E
RESET bit 3 of the system state flags byte at 430FH, indicating the processor is now in SLOW (standard-speed) mode.
522A
XOR A,A AF
Set Register A to ZERO (00H). This value will be sent to the speed control port to disable the high-speed clock.
522B
EI FB
Re-enable interrupts.
522C
OUT (0FEH),A D3 FE
Output 00H to port 0FEH, switching the processor back to the standard-speed clock (1.774 MHz on the Model I).

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.

522E
LD BC,0000H 01 00 00
Self-Modifying Code
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.
5231
LD A,B 78
Load Register A with the high byte of the BASIC parameter value.
5232
OR A,C B1
OR Register A with the low byte. If BC = 0000H (BASIC not specified), the Z FLAG is set.
5233
If the Z FLAG (Zero) has been set, meaning BASIC was not specified, JUMP to 524BH to skip this handler and proceed to the BREAK parameter check.

BASIC was specified. Copy the ROM re-entry stub and exit to BASIC.

5235
LD HL,06D2H 21 D2 06
Point Register Pair HL (source) to 06D2H in ROM. This is the start of a 54-byte Level II BASIC re-entry initialization stub that sets up the memory map and stack for BASIC operation.
5238
LD DE,4000H 11 00 40
Point Register Pair DE (destination) to 4000H, the start of RAM where VTOS normally resides. The stub will overwrite the DOS RST vectors and hook area.
523B
LD BC,0036H 01 36 00
Load Register Pair BC with 0036H (54 decimal). This is the number of bytes to copy from the ROM re-entry stub.
523E
LDIR ED B0
Block copy 54 bytes from HL (ROM at 06D2H) to DE (RAM at 4000H). After the copy, HL = 0708H, DE = 4036H. This overwrites the VTOS RST vector hooks at 4000H-4014H and the initial system work area with BASIC's initialization code.
5240
XOR A,A AF
Set Register A to ZERO (00H) for clearing the remaining bytes after the stub.
5241
LD B,27H 06 27
Load Register B with 27H (39 decimal). This is the number of additional bytes to zero after the 54-byte stub, clearing memory from 4036H through 405CH.

Loop Start
Zero 39 bytes starting at (DE) = 4036H.

5243
LD (DE),A 12
Store 00H to the memory location pointed to by Register Pair DE, clearing the byte.
5244
INC DE 13
INCrement Register Pair DE to point to the next byte to clear.
5245
DECrement B and LOOP BACK to 5243H if B is not zero. This zeros 39 bytes from 4036H through 405CH.

Loop End

5247
DI F3
Disable interrupts before jumping to the BASIC warm-start entry. Interrupts will be re-enabled by the BASIC initialization code.
5248
JUMP to the Level II BASIC warm-start entry point at 0075H in ROM. This never returns to VTOS - the system is now running ROM BASIC.

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.

524B
LD BC,0001H 01 01 00
Self-Modifying Code
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).
524E
LD A,B 78
Load Register A with the high byte of the BREAK parameter value.
524F
OR A,C B1
OR Register A with the low byte. If BC = 0000H (BREAK=OFF), the Z FLAG is set.
5250
If the Z FLAG (Zero) has been set, meaning BREAK=OFF was specified, JUMP to 5262H to enable BREAK detection (writing C8H to 4C2DH).

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.

5252
INC A 3C
INCrement Register A by 1. Since A was the result of B OR C, if BC was 0001H (ON), A was 01H and now becomes 02H (NZ). If BC was FFFFH (flag keyword present), A was FFH and now becomes 00H (Z).
5253
If the NZ FLAG (Not Zero) has been set, meaning the BREAK parameter had a specific value (not just a flag), JUMP to 526FH to proceed to the BLINK handler. The BREAK parameter with a numeric value is treated as handled and processing continues.

BC was FFFFH (bare BREAK flag with no value). Disable BREAK detection by writing 00H to 4C2DH.

5255
LD A,00H 3E 00
Load Register A with 00H to disable the BREAK key handler.
5257
LD HL,430FH 21 0F 43
Point Register Pair HL to the system state flags byte at 430FH.
525A
DI F3
Disable interrupts to protect the atomic write to 4C2DH and modification of 430FH.
525B
LD (4C2DH),A 32 2D 4C
Store 00H to 4C2DH, disabling the BREAK key detection. When this byte is 00H, the ISR BREAK path at 4561H will not intercept BREAK keypresses.
525E
EI FB
Re-enable interrupts.
525F
RES 4,(HL) CB A6
RESET bit 4 of the system state flags byte at 430FH, marking BREAK detection as disabled in the system configuration.
5261
RET C9
Return to the parameter dispatch loop in 4476H to process additional parameters.

Break Enable Path
Reached when BC = 0000H (BREAK=OFF was explicitly specified, which counterintuitively means enable detection since OFF = "turn off the break-disable").

5262
LD A,0C8H 3E C8
Load Register A with C8H. This value enables the BREAK key handler when stored at 4C2DH. The ISR BREAK path at 4561H checks this byte and proceeds with BREAK handling when it contains C8H.
5264
LD HL,430FH 21 0F 43
Point Register Pair HL to the system state flags byte at 430FH.
5267
DI F3
Disable interrupts to protect the atomic writes.
5268
LD (4C2DH),A 32 2D 4C
Store C8H to 4C2DH, enabling the BREAK key detection. The ISR will now intercept BREAK keypresses.
526B
EI FB
Re-enable interrupts.
526C
SET 4,(HL) CB E6
SET bit 4 of the system state flags byte at 430FH, marking BREAK detection as enabled in the system configuration.
526E
RET C9
Return to the parameter dispatch loop in 4476H to process additional parameters.

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).

526F
LD BC,0001H 01 01 00
Self-Modifying Code
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.
5272
LD A,B 78
Load Register A with the high byte of the BLINK parameter value.
5273
OR A,C B1
OR Register A with the low byte. If BC = 0000H (BLINK=OFF), the Z FLAG is set.
5274
DEC A 3D
DECrement Register A by 1. If BC was 0001H (BLINK=ON), A was 01H and now becomes 00H (Z). This distinguishes ON from a specific character value.
5275
If the Z FLAG (Zero) has been set, meaning BLINK=ON was specified with the default cursor character, JUMP to 5305H to skip cursor character setup and proceed to the TYPE parameter handler. The blink task has already been installed by a prior LARGE/SMALL handler or uses the existing cursor character.
5278
INC A 3C
INCrement Register A to restore the original value (undoing the DEC). If BC was 0000H (OFF), the original OR result was 00H, DEC made FFH, INC makes 00H (Z). If BC was FFFFH (bare flag), OR was FFH, DEC was FEH, INC is FFH (NZ).
5279
If the NZ FLAG (Not Zero) has been set, meaning a specific cursor character value or bare flag was provided, JUMP to 5283H to process the cursor character.

BC = 0000H - BLINK=OFF was specified. Remove the blink task.

527B
LD A,05H 3E 05
Load Register A with 05H. This is the task ID number for the cursor blink task, used by the task removal routine at 4413H.
527D
GOSUB to the SYS0 task removal routine at 4413H to remove the cursor blink task (task ID 05H) from the timer callback slot table.
5280
JUMP to 5305H to proceed to the TYPE parameter handler.

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.

5283
NOP 00
No operation. Padding byte.
5284
NOP 00
No operation. Padding byte.
5285
NOP 00
No operation. Padding byte.
5286
CP A,0FFH FE FF
Compare Register A against FFH. If BC was FFFFH (bare BLINK flag), the OR B,C result was FFH. After DEC/INC, A is FFH. If A equals FFH, the Z FLAG is set.
5288
If the NZ FLAG (Not Zero) has been set, meaning A is not FFH (a specific numeric cursor character was given), JUMP to 5295H to store that character directly.

Bare BLINK flag (FFFFH). Use the default cursor character based on the LARGE/SMALL sub-handler setting.

528A
LD BC,0000H 01 00 00
Self-Modifying Code
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.
528D
LD A,B 78
Load Register A with the high byte of the SMALL parameter value.
528E
OR A,C B1
OR Register A with the low byte. If BC = 0000H (SMALL not specified), Z is set.
528F
LD A,8FH 3E 8F
Load Register A with 8FH (the full-block graphics character, used as the LARGE cursor). This is the default cursor character when BLINK is specified without SMALL.
5291
If the Z FLAG (Zero) has been set, meaning SMALL was not specified, JUMP to 5295H with A = 8FH (LARGE cursor). The default bare BLINK uses the large cursor.
5293
LD A,88H 3E 88
Load Register A with 88H (the left-half-block graphics character, used as the SMALL cursor). SMALL was specified, so override the large cursor default.

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.

5295
LD (52E6H),A 32 E6 52
Self-Modifying 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.
5298
LD (52F3H),A 32 F3 52
Self-Modifying Code
Store the cursor character into the second location at 52F3H within the blink task code template.
529B
LD (52FCH),A 32 FC 52
Self-Modifying Code
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.

529E
LD BC,003EH 01 3E 00
Load Register Pair BC with 003EH (62 decimal). This is the size of the cursor blink task code block that will be installed in high memory.
52A1
LD HL,(4049H) 2A 49 40
Fetch the current high memory pointer from 4049H into Register Pair HL. This is the top of available RAM, decremented each time a task block is installed.
52A4
XOR A,A AF
Set Register A to ZERO and clear all flags, including the CARRY FLAG, in preparation for the 16-bit subtract.
52A5
SBC HL,BC ED 42
SUBtract BC (003EH = 62 bytes) from HL (high memory pointer) with borrow. This reserves 62 bytes at the top of RAM for the blink task code. HL now points to the start of the reserved block.
52A7
LD (4049H),HL 22 49 40
Store the updated high memory pointer back to 4049H. The 62 bytes above this address are now reserved for the blink task.
52AA
INC HL 23
INCrement HL by 1. The task code will be copied starting at HL+1 (the first usable byte of the reserved block), since (4049H) points to the byte BELOW the reserved area.
52AB
PUSH HL E5
Save the task block start address (HL) onto the stack for later use in patching the self-reference pointer and registering the task.
52AC
EX DE,HL EB
Exchange DE and HL. DE now holds the destination address (start of the reserved block), HL is free for the source pointer.
52AD
LD HL,52C7H 21 C7 52
Point Register Pair HL (source) to 52C7H, which is the start of the 62-byte cursor blink task code template. This template will be copied into the reserved high-memory block.
52B0
LDIR ED B0
Block copy 62 bytes (BC still = 003EH from the LD BC,003EH at 529EH) from HL (template at 52C7H) to DE (reserved high-memory block). This copies the complete blink task driver into its runtime location.

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.

52B2
POP DE D1
Restore the task block start address from the stack into Register Pair DE.
52B3
PUSH DE D5
Save the task block start address back onto the stack (it will be needed again for the CALL 4410H registration).
52B4
LD HL,0005H 21 05 00
Load Register Pair HL with 0005H. This is the offset within the installed task code block where a self-reference pointer needs to be patched. The task code at offset +05H contains a 2-byte address that must point to a location within the installed copy.
52B7
ADD HL,DE 19
ADD DE (task block start address) to HL (offset 0005H). HL now points to the self-reference pointer location within the installed task code.
52B8
EX DE,HL EB
Exchange DE and HL. DE now holds the address of the self-reference pointer within the installed code; HL holds the task block start.
52B9
LD (HL),E 73
Store the low byte of the self-reference target address (Register E) into the pointer location within the task code.
52BA
INC HL 23
INCrement HL to point to the high byte of the self-reference pointer.
52BB
LD (HL),D 72
Store the high byte of the self-reference target address (Register D) into the pointer location. The self-reference pointer is now patched to point to the correct location within the installed copy.
52BC
LD A,05H 3E 05
Load Register A with 05H. This is the task ID number for the cursor blink task, used by the task registration routine at 4410H.
52BE
POP DE D1
Restore the task block start address from the stack into Register Pair DE for the task registration call.
52BF
GOSUB to the SYS0 task installation routine at 4410H. Register A = 05H (task ID), DE = start address of the installed task code in high memory. This registers the cursor blink driver as a scheduled callback in the timer callback slot table at 404BH.
52C2
JUMP to 5305H to proceed to the TYPE parameter handler.

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.

52CC
GOSUB to SYS0 routine at 4416H. This routine provides timer task housekeeping - it reads the current cursor position and sets up the task context for this callback invocation.
52CF
LD A,(4022H) 3A 22 40
Fetch the current cursor column position from 4022H into Register A. This is the low byte of the cursor address within the video RAM (3C00H-3FFFH).
52D2
OR A,A B7
OR Register A with itself to set the flags. If A = 00H, the Z FLAG is set, meaning no cursor position is currently active.
52D3
RET Z C8
If the Z FLAG (Zero) has been set, meaning no cursor position is active, return immediately without blinking. The task will try again on the next timer tick.
52D4
LD (IX+04H),A DD 77 04
Store the cursor column position (Register A) into the task's local data area at IX+04H. IX points to the task control block in high memory. This saves the current position for the toggle logic.
52D7
LD BC,(4020H) ED 4B 20 40
Fetch the full cursor position from 4020H-4021H into Register Pair BC. This is the 2-byte video RAM address of the current cursor position (B = high byte, C = low byte).
52DB
LD (IX+02H),C DD 71 02
Store the low byte of the cursor address (Register C) into the task control block at IX+02H.
52DE
LD (IX+03H),B DD 70 03
Store the high byte of the cursor address (Register B) into the task control block at IX+03H. IX+02H/+03H now hold the full cursor RAM address.
52E1
LD A,(BC) 0A
Fetch the current character at the cursor position from video RAM (pointed to by BC) into Register A.
52E2
LD (IX+04H),A DD 77 04
Store the character currently under the cursor into IX+04H. This saves the original character so it can be restored when the cursor blinks off.
52E5
LD A,8FH 3E 8F
Self-Modifying Code
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).
52E7
LD (BC),A 02
Store the cursor character (Register A) to the cursor position in video RAM (pointed to by BC). This makes the cursor visible by overwriting the character at the cursor position.
52E8
GOSUB to SYS0 timer task housekeeping at 4416H again. This creates a delay between the cursor-on and cursor-off phases, producing the visible blink effect.
52EB
LD C,(IX+02H) DD 4E 02
Fetch the saved cursor address low byte from the task control block at IX+02H into Register C.
52EE
LD B,(IX+03H) DD 46 03
Fetch the saved cursor address high byte from IX+03H into Register B. Register Pair BC now holds the video RAM address where the cursor was displayed.
52F1
LD A,(BC) 0A
Fetch the current character at the cursor position from video RAM into Register A. This may still be the cursor character, or it may have been overwritten by program output.
52F2
CP A,8FH FE 8F
Self-Modifying Code
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.
52F4
If the Z FLAG (Zero) has been set, meaning the cursor character is still at the cursor position, JUMP to 52FFH to restore the original character (blink OFF phase).

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).

52F6
LD A,C 79
Load Register A with the low byte of the saved cursor address (Register C).
52F7
SUB A,40H D6 40
SUBtract 40H (64 decimal, one video row width) from Register A. This computes the position one row above the current cursor position for comparison.
52F9
LD C,A 4F
Load Register C with the adjusted address low byte. BC now points one row above the saved cursor position.
52FA
LD A,(BC) 0A
Fetch the character at the position one row above from video RAM into Register A.
52FB
CP A,8FH FE 8F
Self-Modifying Code
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.
52FD
If the NZ FLAG (Not Zero) has been set, meaning the cursor character was not found one row above either, LOOP BACK to 52CCH to restart the blink cycle. The cursor has been overwritten by program output and no cleanup is needed.

Cursor Restore Path
The cursor character was found at the saved position (or one row above after scroll). Restore the original character.

52FF
LD A,(IX+04H) DD 7E 04
Fetch the saved original character from the task control block at IX+04H into Register A. This is the character that was under the cursor before it was displayed.
5302
LD (BC),A 02
Store the original character back to the cursor position in video RAM (pointed to by BC), removing the cursor display.
5303
LOOP BACK to 52CCH to restart the blink cycle. The cursor is now hidden and will be re-displayed on the next timer callback.

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.

5305
LD BC,0001H 01 01 00
Self-Modifying Code
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.
5308
LD A,B 78
Load Register A with the high byte of the TYPE parameter value.
5309
OR A,C B1
OR Register A with the low byte. If BC = 0000H (TYPE=OFF), the Z FLAG is set.
530A
DEC A 3D
DECrement Register A by 1. If BC was 0001H (TYPE=ON), A was 01H and becomes 00H (Z).
530B
If the Z FLAG (Zero) has been set, meaning TYPE=ON was specified with the default value 0001H, JUMP to 547FH to skip the type-ahead installation and proceed to the SLOWER parameter handler. This means TYPE=ON with value 1 does not install the driver (it just acknowledges the parameter).
530E
INC A 3C
INCrement Register A to restore the original OR result (undoing the DEC). If BC was 0000H (OFF), original OR was 00H, after DEC/INC it is 00H (Z).
530F
If the NZ FLAG (Not Zero) has been set, meaning a non-zero non-0001H value was specified (bare flag FFFFH or numeric), JUMP to 5319H to install the type-ahead driver.

BC = 0000H - TYPE=OFF was specified. Remove the type-ahead task.

5311
LD A,0AH 3E 0A
Load Register A with 0AH. This is the task ID number for the type-ahead keyboard driver.
5313
GOSUB to the SYS0 task removal routine at 4413H to remove the type-ahead driver (task ID 0AH) from the timer callback slot table.
5316
JUMP to 547FH to proceed to the SLOWER parameter handler.

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.

5319
GOSUB to the command level check at 5627H. This verifies that the SYSTEM command was issued from the VTOS command level (402DH must contain C3H/JP). If not at command level, 5627H displays "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" and exits via JP 4030H, never returning here.
531C
LD IX,4015H DD 21 15 40
Point Index Register IX to the task control block base at 4015H. This is the first entry in the drive configuration table, which also serves as the task callback registration area for task installation.
5320
LD HL,(4049H) 2A 49 40
Fetch the current high memory pointer from 4049H into Register Pair HL.
5323
DEC H 25
DECrement the high byte of HL by 1 (subtracting 256). This reserves an additional 256 bytes above the task code block, creating a type-ahead keystroke buffer area in high memory.
5324
LD (HL),00H 36 00
Store 00H to the first byte of the reserved buffer area. This initializes the buffer header/counter to zero (empty buffer).
5326
DEC HL 2B
DECrement HL to point to the next byte below the buffer area.
5327
LD (HL),00H 36 00
Store 00H to this byte, zeroing the second buffer header byte.
5329
DEC HL 2B
DECrement HL again.
532A
LD (HL),00H 36 00
Store 00H to the third buffer header byte. The three bytes above the task code block form a 3-byte buffer management header (count, read pointer, write pointer), all initialized to zero.
532C
LD D,H 54
Copy the high byte of HL into Register D. DE will be used for self-reference patching.
532D
LD E,L 5D
Copy the low byte of HL into Register E. DE now equals HL, pointing to the byte below the 3-byte buffer header.
532E
DEC DE 1B
DECrement DE by 1, positioning it one byte below HL. DE will serve as a secondary pointer for buffer management patching.
532F
LD A,21H 3E 21
Load Register A with 21H. This value will be stored to the type-ahead activation flag at 439CH. The value 21H signals to the keyboard handler that type-ahead is active.
5331
LD (439CH),A 32 9C 43
Store 21H to the type-ahead flag at 439CH. This tells the system keyboard handler to route keystrokes through the type-ahead buffer instead of the normal direct-input path.
5334
LD (53ADH),HL 22 AD 53
Self-Modifying Code
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.
5337
LD (53DAH),HL 22 DA 53
Self-Modifying Code
Store HL into the second buffer reference at 53DAH within the template. A second LD HL,nnnn instruction in the driver is also patched.
533A
LD HL,(4016H) 2A 16 40
Fetch the current keyboard handler vector from 4016H into Register Pair HL. This is the address that keyboard input normally calls. The type-ahead driver will chain to this vector after buffering the keystroke.
533D
LD A,(430FH) 3A 0F 43
Fetch the system state flags byte from 430FH into Register A.
5340
BIT 5,A CB 6F
Test bit 5 of the system state flags. Bit 5 indicates whether type-ahead re-entry mode is active (set during previous TYPE command execution). If bit 5 is set, the Z FLAG is cleared (NZ).
5342
If the Z FLAG (Zero) has been set, meaning bit 5 is clear (first-time TYPE installation), JUMP to 5347H to use the current 4016H vector.
5344
LD HL,(43BEH) 2A BE 43
Fetch the saved original keyboard vector from 43BEH into Register Pair HL. When TYPE was previously installed, the original vector was saved here. Use it instead of the current (possibly already-hooked) vector at 4016H to avoid chain corruption.
5347
LD (53E6H),HL 22 E6 53
Self-Modifying Code
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.
534A
LD (53D1H),HL 22 D1 53
Self-Modifying Code
Store the keyboard chain vector into a second reference at 53D1H in the template.
534D
EX DE,HL EB
Exchange DE and HL. HL now holds DE (the secondary buffer pointer), DE is free.
534E
LD BC,00D6H 01 D6 00
Load Register Pair BC with 00D6H (214 decimal). This is the size of the type-ahead driver code block.
5351
XOR A,A AF
Set Register A to ZERO and clear the CARRY FLAG for the 16-bit subtract.
5352
SBC HL,BC ED 42
SUBtract BC (00D6H = 214 bytes) from HL. This reserves 214 bytes below the buffer area for the type-ahead driver code. HL points to the start of the reserved block.
5354
LD (4049H),HL 22 49 40
Store the updated high memory pointer to 4049H. The RAM from this address upward is now reserved for the type-ahead driver code and its buffer.
5357
INC HL 23
INCrement HL to point to the first usable byte of the reserved block.
5358
PUSH HL E5
Save the task block start address onto the stack for later use in self-reference patching and task registration.
5359
EX DE,HL EB
Exchange DE and HL. DE = destination (start of reserved block), HL is free for the source pointer.
535A
LD HL,53A9H 21 A9 53
Point Register Pair HL (source) to 53A9H, the start of the 214-byte type-ahead driver template.
535D
LDIR ED B0
Block copy 214 bytes (BC = 00D6H) from HL (template at 53A9H) to DE (reserved high-memory block). This copies the complete type-ahead driver into its runtime location.
535F
LD (IX+00H),01H DD 36 00 01
Store 01H to IX+00H (task control block at 4015H, first byte). This sets the task status/type byte to 01H, indicating an active callback entry.
5363
POP HL E1
Restore the task block start address from the stack into Register Pair HL.
5364
PUSH HL E5
Save the task block start address back onto the stack (needed again later).
5365
LD A,(430FH) 3A 0F 43
Fetch the system state flags byte from 430FH into Register A.
5368
BIT 5,A CB 6F
Test bit 5 of the system state flags (type-ahead re-entry mode). If clear (first-time install), Z is set.
536A
If the NZ FLAG (Not Zero) has been set, meaning type-ahead was previously installed (bit 5 set), JUMP to 5374H to save the vector to 43BEH instead of IX+01H/+02H.

First-time installation. Save the task entry address in IX+01H/+02H and record the keyboard vector.

536C
LD (IX+01H),L DD 75 01
Store the low byte of the task block start address (Register L) into IX+01H. Together with IX+02H, this points the task callback to the installed driver code.
536F
LD (IX+02H),H DD 74 02
Store the high byte of the task block start address (Register H) into IX+02H. IX+01H/+02H now hold the full callback address for the type-ahead driver.
5372
JUMP to 5377H to continue with remaining task registration steps.
5374
LD (43BEH),HL 22 BE 43
Store the task block start address to 43BEH. When re-installing type-ahead (bit 5 of 430FH already set), the saved interrupt vector at 43BEH is updated to point to the new driver instance.
5377
LD A,(4018H) 3A 18 40
Fetch the keyboard scan rate/timer interval from 4018H into Register A. This value controls how frequently the type-ahead driver is invoked by the timer callback.
537A
LD (IX+03H),A DD 77 03
Store the keyboard scan rate (Register A) into IX+03H. This sets the timer countdown value for the task callback invocation frequency.
537D
LD HL,(4019H) 2A 19 40
Fetch the keyboard vector / timer callback chain address from 4019H into Register Pair HL.
5380
LD (IX+04H),L DD 75 04
Store the low byte of the chain address (Register L) into IX+04H.
5383
LD (IX+05H),H DD 74 05
Store the high byte of the chain address (Register H) into IX+05H. IX+04H/+05H now hold the chain-to address for the timer callback.
5386
POP HL E1
Restore the task block start address from the stack into Register Pair HL.
5387
PUSH HL E5
Save the task block start address back onto the stack one more time.
5388
LD DE,007DH 11 7D 00
Load Register Pair DE with 007DH (125 decimal). This is an offset within the installed type-ahead driver code where a buffer overflow address needs to be patched.
538B
ADD HL,DE 19
ADD DE (offset 007DH) to HL (task block start). HL now points to the location within the installed driver that needs the overflow pointer patched.
538C
EX DE,HL EB
Exchange DE and HL. DE holds the overflow pointer target address.
538D
LD HL,4BC1H 21 C1 4B
Point Register Pair HL to the type-ahead buffer overflow pointer location at 4BC1H. This system variable holds the address where the type-ahead driver stores the buffer end pointer.
5390
LD (HL),E 73
Store the low byte of the overflow pointer target address (Register E) into 4BC1H.
5391
INC HL 23
INCrement HL to point to 4BC2H (high byte of the overflow pointer).
5392
LD (HL),D 72
Store the high byte of the overflow pointer target address (Register D) into 4BC2H. The system variable at 4BC1H-4BC2H now points into the installed driver's buffer management code.
5393
POP HL E1
Restore the task block start address from the stack into Register Pair HL.
5394
LD DE,002EH 11 2E 00
Load Register Pair DE with 002EH (46 decimal). This is an offset within the installed driver code where a second self-reference pointer needs patching.
5397
ADD HL,DE 19
ADD DE (offset 002EH) to HL (task block start). HL now points to the self-reference pointer location within the installed driver.
5398
LD D,H 54
Copy the high byte of HL into Register D.
5399
LD E,L 5D
Copy the low byte of HL into Register E. DE now equals HL.
539A
INC DE 13
INCrement DE. DE now points one byte past the pointer location.
539B
INC DE 13
INCrement DE again. DE now points two bytes past the pointer location, which is the target address that the pointer should reference.
539C
LD (HL),E 73
Store the low byte of the target address (Register E) into the self-reference pointer location.
539D
INC HL 23
INCrement HL to point to the high byte of the self-reference pointer.
539E
LD (HL),D 72
Store the high byte of the target address (Register D) into the self-reference pointer. The pointer is now patched to reference the correct location within the installed copy.
539F
DEC HL 2B
DECrement HL back to the low byte of the pointer (restoring the original position).
53A0
EX DE,HL EB
Exchange DE and HL. HL now holds the target address (2 bytes past the pointer), DE holds the pointer location.
53A1
LD A,0AH 3E 0A
Load Register A with 0AH. This is the task ID number for the type-ahead keyboard driver.
53A3
GOSUB to the SYS0 task installation routine at 4410H. Register A = 0AH (task ID), DE = start address of the installed type-ahead driver in high memory. This registers the type-ahead driver as a scheduled callback in the timer callback slot table.
53A6
JUMP to 547FH to proceed to the SLOWER parameter handler.

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.

53A9
GOSUB to the patchable JP/RET slot at 4300H. At cold start this is C9H (RET), so it returns immediately. When an interrupt hook is installed, 4300H becomes a JP to the hook handler, allowing the type-ahead driver to chain into the interrupt processing path.
53AC
LD HL,0000H 21 00 00
Self-Modifying Code
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).
53AF
LD (HL),0FFH 36 FF
Store FFH to the buffer header byte at (HL). This sets a "buffer active" flag or initializes the buffer counter to FFH to signal that the driver is processing a keystroke cycle.
53B1
PUSH HL E5
Save the buffer header address onto the stack. This will be restored at 53C2H/53D3H to reset the buffer flag when done.
53B2
INC HL 23
INCrement HL to point to the first data byte in the buffer (past the header/flag byte).
53B3
LD A,(HL) 7E
Fetch the current read index from the buffer into Register A. This index tracks which byte will be read next from the circular buffer.
53B4
INC HL 23
INCrement HL to point to the write index byte.
53B5
CP A,(HL) BE
Compare the read index (Register A) against the write index (at HL). If they are equal, the buffer is empty (no pending keystrokes to deliver). The Z FLAG is set if equal.
53B6
LD A,00H 3E 00
Load Register A with 00H. This does NOT affect the flags from the CP - it loads 00H (which will be the "no key" return value) without disturbing the Z flag.
53B8
If the Z FLAG (Zero) has been set from the CP, meaning the read and write indices are equal (buffer empty), JUMP to 53CCH to call the keyboard handler to check for new input.

Buffer Not Empty
There are buffered keystrokes. Read the next one and return it.

53BA
PUSH HL E5
Save the write index pointer onto the stack.
53BB
LD E,(HL) 5E
Fetch the write index value from (HL) into Register E. Wait - this was already compared; HL points to the write index. This reads the write index for buffer addressing.
53BC
INC HL 23
INCrement HL past the write index to point to the start of the keystroke data area.
53BD
LD D,00H 16 00
Load Register D with 00H. DE now holds the read offset as a 16-bit value (D=00H, E=read index).
53BF
ADD HL,DE 19
ADD DE (read offset) to HL (start of data area). HL now points to the buffered keystroke at the current read position.
53C0
LD B,(HL) 46
Fetch the buffered keystroke from (HL) into Register B.
53C1
INC HL 23
INCrement HL to point to the next byte (which holds a secondary value, such as key modifier flags).
53C2
LD C,(HL) 4E
Fetch the secondary keystroke data from (HL) into Register C. BC now holds the complete keystroke entry (B=key code, C=modifier/attribute).
53C3
POP HL E1
Restore the write index pointer from the stack into HL.
53C4
INC (HL) 34
INCrement the read index at (HL) by 1 (actually this is the write index pointer - adjusting the read pointer). Advance the read position by 2 bytes (one keystroke entry is 2 bytes).
53C5
INC (HL) 34
INCrement again. The read index advances by 2 (one full keystroke entry) to consume the keystroke from the buffer.
53C6
POP HL E1
Restore the buffer header address from the stack (saved at 53B1H).
53C7
LD (HL),00H 36 00
Store 00H to the buffer header byte, clearing the "buffer active" flag. The keystroke retrieval is complete.
53C9
PUSH BC C5
Save the keystroke (BC) onto the stack.
53CA
POP AF F1
Restore BC into AF. A = B (key code), F = C (flags). This converts the keystroke from BC format to the AF return format expected by the keyboard handler.
53CB
RET C9
Return with the buffered keystroke in Register A. The caller receives the key as if it was just pressed.

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.

53CC
LD IX,4015H DD 21 15 40
Point Index Register IX to the task control block base at 4015H. The driver uses IX for task state management during keyboard polling.
53D0
CALL 0000H CD 00 00
Self-Modifying Code
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).
53D3
POP HL E1
Restore the buffer header address from the stack (saved at 53B1H).
53D4
LD (HL),00H 36 00
Store 00H to the buffer header byte, clearing the "buffer active" flag. The keyboard poll is complete regardless of whether a key was found.
53D6
RET C9
Return with A = key character from the keyboard handler (or 00H if no key). If a key was detected, it is returned directly without buffering (the buffer was empty, so direct return is appropriate).

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.

53D9
LD HL,0000H 21 00 00
Self-Modifying Code
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).
53DC
LD A,(HL) 7E
Fetch the buffer active flag from the header byte at (HL). If non-zero (FFH), the buffer retrieval routine is currently executing and the store must not interfere.
53DD
OR A,A B7
OR Register A with itself to set the flags. If A = 00H (buffer not being read), Z is set.
53DE
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning the buffer is currently being read (flag = FFH), return immediately without storing. The keystroke will be detected again on the next scan cycle.
53DF
INC HL 23
INCrement HL past the flag byte to point to the read index.
53E0
PUSH HL E5
Save the read index pointer onto the stack.
53E1
LD IX,4015H DD 21 15 40
Point Index Register IX to the task control block base at 4015H for keyboard handler context.
53E5
CALL 0000H CD 00 00
Self-Modifying Code
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).
53E8
POP HL E1
Restore the read index pointer from the stack into HL.
53E9
INC A 3C
INCrement Register A by 1. If A was 00H (no key), it becomes 01H (NZ). If A was FFH, it becomes 00H (Z). This is a test pattern: INC followed by DEC restores the original value while the flags reflect the INC result.
53EA
DEC A 3D
DECrement Register A to restore the original key value. The Z flag from the INC is now replaced: if the original A was 00H, the DEC result is 00H (Z set). But the key test relies on the surrounding logic.
53EB
RET Z C8
If the Z FLAG (Zero) has been set, meaning no keystroke was detected (A = 00H), return immediately. Nothing to buffer.

A keystroke was detected. Check for special keys and buffer overflow before storing.

53EC
PUSH AF F5
Save the keystroke (Register A) and flags onto the stack.
53ED
POP BC C1
Restore the keystroke into BC (B = key code from A, C = flags from F). BC now holds the keystroke in the format used by the buffer (2-byte entries).
53EE
CP A,0C0H FE C0
Compare Register A (key code) against C0H. The value C0H is the special CLEAR+SHIFT+@ key combination which signals "clear the type-ahead buffer".
53F0
If the Z FLAG (Zero) has been set, meaning the CLEAR+SHIFT+@ combination was pressed (A = C0H), JUMP to 541AH to clear the type-ahead buffer.

Normal keystroke. Check if the buffer has room.

53F2
LD E,(HL) 5E
Fetch the read index from (HL) into Register E.
53F3
LD A,E 7B
Copy the read index into Register A for comparison.
53F4
INC HL 23
INCrement HL to point to the write index.
53F5
CP A,(HL) BE
Compare the read index (Register A) against the write index at (HL). If they differ, there is data in the buffer; if equal plus 2, the buffer may be full.
53F6
If the Z FLAG (Zero) has been set, meaning read and write indices are equal (buffer is either empty or was just cleared), JUMP to 540DH to store the keystroke at the write position.

Buffer has existing data. Check for overflow condition.

53F8
LD A,(4040H) 3A 40 40
Fetch the master tick counter from 4040H into Register A. This is used to compute a maximum buffer size limit for overflow detection.
53FB
ADD A,03H C6 03
ADD 03H to Register A. This creates a threshold value 3 ticks above the current tick counter.
53FD
CP A,(IX+04H) DD BE 04
Compare Register A against the task control block field at IX+04H (the timer countdown value). This checks if the buffer write has caught up to the read position by comparing timing indices.
5400
If the NZ FLAG (Not Zero) has been set, meaning the timing check did not match (no overflow), JUMP to 5408H to continue with the normal store.
5402
ADD A,03H C6 03
ADD another 03H to Register A, extending the timeout by 3 more ticks. This gives the consumer more time to read from the buffer before the overflow threshold is reached.
5404
LD (IX+04H),A DD 77 04
Store the extended timeout value back to IX+04H. This pushes the overflow detection further into the future.
5407
RET C9
Return without storing the keystroke. The buffer has reached its overflow threshold and the keystroke is discarded.
5408
LD A,E 7B
Load Register A with the read index (from Register E, loaded at 53F2H).
5409
INC A 3C
INCrement the read index by 1.
540A
INC A 3C
INCrement again (advance by 2, one keystroke entry width).
540B
CP A,(HL) BE
Compare the advanced read index against the write index at (HL). If equal, the buffer is full (write has wrapped around to the read position).
540C
RET Z C8
If the Z FLAG (Zero) has been set, meaning the buffer is full (read+2 == write), return without storing. The keystroke is discarded to prevent buffer overrun.

Keystroke Store
Buffer has room. Store the keystroke (BC) at the current write position.

540D
PUSH HL E5
Save the write index pointer onto the stack.
540E
INC HL 23
INCrement HL past the write index to point to the start of the data area.
540F
LD D,00H 16 00
Load Register D with 00H. DE now holds the write offset as a 16-bit value (D=00H, E=write index from 53F2H).
5411
ADD HL,DE 19
ADD DE (write offset) to HL (data area start). HL now points to the buffer position where the keystroke should be stored.
5412
LD (HL),B 70
Store the key code (Register B) to the buffer at the write position.
5413
INC HL 23
INCrement HL to point to the next buffer byte.
5414
LD (HL),C 71
Store the modifier/flags byte (Register C) to the buffer at the next position. The complete 2-byte keystroke entry is now stored.
5415
POP HL E1
Restore the write index pointer from the stack into HL.
5416
DEC HL 2B
DECrement HL to point to the read index byte (one byte before the write index).
5417
INC (HL) 34
INCrement the write index at (HL) by 1. Wait - this is actually the read index position. The write index needs to advance by 2.
5418
INC (HL) 34
INCrement again. The write index advances by 2 bytes (one complete keystroke entry) to reflect the newly stored data.
5419
RET C9
Return after successfully storing the keystroke in the type-ahead buffer.

Clear Type-Ahead Buffer
Reached when CLEAR+SHIFT+@ (C0H) was detected at 53EEH. Reset the buffer indices and pointer to empty state.

541A
LD (HL),02H 36 02
Store 02H to the read index at (HL). Setting both indices to matching values with offset 02H effectively empties the buffer while preserving the data area alignment.
541C
INC HL 23
INCrement HL to point to the write index.
541D
LD (HL),00H 36 00
Store 00H to the write index, resetting it to the start of the data area.
541F
INC HL 23
INCrement HL past the write index.
5420
LD (HL),00H 36 00
Store 00H to the first data byte, clearing any stale data.
5422
INC HL 23
INCrement HL to the next byte.
5423
LD (HL),40H 36 40
Store 40H to this byte. The value 40H serves as a buffer boundary marker, ensuring the circular buffer wraps correctly at the 64-byte boundary (40H = 64 decimal).
5425
RET C9
Return after clearing the type-ahead buffer. The buffer is now empty and ready for new keystrokes.

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.

5426
LD E,A 5F
Save the keyboard bit pattern (Register A) into Register E. A contains the bit mask from the keyboard scan indicating which key in the row is pressed.
5427
LD A,D 7A
Load Register A with the keyboard row number (Register D). D holds the row index (0-7) corresponding to keyboard addresses 3801H-3880H.
5428
RLCA 07
Rotate Register A left 1 bit (multiply by 2). Begin converting the row number into a character code base offset.
5429
RLCA 07
Rotate left again (multiply by 4 from original).
542A
RLCA 07
Rotate left again (multiply by 8 from original). The row number x 8 gives the base character code for that row, since each keyboard row contains 8 keys.
542B
LD D,A 57
Store the row base offset (row x 8) back into Register D.
542C
LD C,01H 0E 01
Load Register C with 01H. This is the bit mask for scanning individual bits in the key pattern (starting with bit 0).

Loop Start
Find which bit is set in the key pattern (Register E) to determine the column within the row.

542E
LD A,C 79
Load Register A with the current bit mask (Register C).
542F
AND A,E A3
AND Register A with the key pattern (Register E). If this bit is set in the pattern, A is non-zero (NZ).
5430
If the NZ FLAG (Not Zero) has been set, meaning this bit position matches the pressed key, JUMP to 5437H to process the character code.
5432
INC D 14
INCrement Register D (the character code offset) to try the next key in the row.
5433
RLC C CB 01
Rotate Register C left through carry, shifting the bit mask to the next position (01->02->04->08->10->20->40->80).
5435
LOOP BACK to 542EH to test the next bit position.

Loop End
Found the pressed key. D holds the character code offset (row x 8 + column). Now apply SHIFT and special key processing.

5437
LD A,(3880H) 3A 80 38
Fetch the SHIFT key row from keyboard memory at 3880H. Bit 0 indicates whether SHIFT is held (active LOW on the TRS-80, but the keyboard driver inverts this).
543A
LD B,A 47
Save the SHIFT key state into Register B for later reference.
543B
LD A,D 7A
Load Register A with the character code offset (Register D).
543C
ADD A,40H C6 40
ADD 40H to convert the offset into an ASCII character code. The base for keyboard characters starts at 40H ('@'), so offset 0 = 40H ('@'), offset 1 = 41H ('A'), etc.
543E
CP A,60H FE 60
Compare Register A against 60H (backtick/'`'). Characters at 60H and above are in the special/numeric key range.
5440
If the NO CARRY FLAG has been set, meaning A >= 60H (character is in the special key range), JUMP to 5455H to handle special keys separately.

Character is in the 40H-5FH range (@ through _). Check SHIFT for lowercase conversion.

5442
RRC B CB 08
Rotate Register B (SHIFT state) right through carry. Bit 0 goes into the CARRY FLAG. If SHIFT is pressed (bit 0 set), CARRY is set.
5444
If the NO CARRY FLAG has been set, meaning SHIFT is not pressed, JUMP to 547AH with the unshifted character in A. No case conversion needed.

SHIFT is pressed. Convert to shifted character. For alphabetic keys (40H-5FH), SHIFT produces graphic characters (add 20H to get the C0H-DFH range).

5446
ADD A,20H C6 20
ADD 20H to Register A. This converts the unshifted code (40H-5FH) to the shifted/graphic code range (60H-7FH).
5448
LD D,A 57
Save the shifted code into Register D.
5449
LD A,(3840H) 3A 40 38
Fetch the special key row from keyboard memory at 3840H. This row contains ENTER, CLEAR, BREAK, and arrow keys.
544C
AND A,10H E6 10
AND Register A with 10H to isolate bit 4 (the DOWN ARROW key on the Model I). This checks for the CLEAR key modifier in some keyboard configurations.
544E
If the Z FLAG (Zero) has been set, meaning bit 4 is clear (DOWN/CLEAR not pressed), JUMP to 5479H to use the shifted character from Register D.
5450
LD A,D 7A
Load Register A with the shifted code (from Register D).
5451
SUB A,60H D6 60
SUBtract 60H from the shifted code. This produces a control character code (00H-1FH) when CLEAR+SHIFT+key is pressed.
5453
JUMP to 547AH with the control character code in Register A.

Special Keys Path
Character code >= 60H. Handle numeric keys (rows 4-5) and function keys (row 6-7).

5455
SUB A,70H D6 70
SUBtract 70H from Register A. For the numeric key range (70H-7FH), this yields 00H-0FH. If the result is negative (A was 60H-6FH), CARRY is set.
5457
If the NO CARRY FLAG has been set, meaning A >= 70H (numeric row 4: keys '0'-'7', or row 5: '8'-'/'), JUMP to 5469H to handle numeric/punctuation keys.

Character was in the 60H-6FH range (row 6: ENTER, CLEAR, BREAK, arrows, SPACE). Adjust back to the correct code.

5459
ADD A,40H C6 40
ADD 40H to restore the partially decoded code. A was (original-70H), now it is (original-30H).
545B
CP A,3CH FE 3CH
Compare Register A against 3CH. This boundary separates the ENTER/CLEAR/BREAK keys from the arrow/SPACE keys.
545D
If the CARRY FLAG has been set, meaning A < 3CH (arrow keys or SPACE), JUMP to 5461H to apply the SHIFT check.
545F
XOR A,10H EE 10
XOR Register A with 10H. This toggles bit 4, converting between the shifted and unshifted variants of the special keys (ENTER vs line-feed, CLEAR vs shifted-CLEAR).
5461
RRC B CB 08
Rotate Register B (SHIFT state) right through carry. Bit 0 goes into CARRY. If SHIFT is pressed, CARRY is set.
5463
If the NO CARRY FLAG has been set, meaning SHIFT is not pressed, JUMP to 547AH with the unshifted special key code.
5465
XOR A,10H EE 10
XOR Register A with 10H again. SHIFT is pressed, so toggle bit 4 to produce the shifted variant of the special key.
5467
JUMP to 547AH with the shifted special key code.

Numeric/Punctuation Keys
A >= 70H: rows 4-5 containing digits and punctuation marks.

5469
RLCA 07
Rotate Register A left. The offset from 70H is doubled to create a 2-byte index into the key decode table.
546A
RRC B CB 08
Rotate Register B (SHIFT state) right through carry. If SHIFT is pressed, CARRY is set.
546C
If the NO CARRY FLAG has been set, meaning SHIFT is not pressed, JUMP to 546FH to use the unshifted entry in the decode table.
546E
INC A 3C
INCrement the table index by 1 to select the shifted variant of the numeric/punctuation key.
546F
LD HL,0050H 21 50 00
Point Register Pair HL to the key decode table in ROM at 0050H. This table contains 2-byte entries mapping numeric/punctuation key positions to ASCII codes.
5472
LD C,A 4F
Load Register C with the table index (Register A).
5473
LD B,00H 06 00
Load Register B with 00H. BC now holds the 16-bit table index.
5475
ADD HL,BC 09
ADD BC (table index) to HL (table base 0050H). HL now points to the correct entry in the key decode table.
5476
LD A,(HL) 7E
Fetch the ASCII character code from the key decode table at (HL) into Register A.
5477
JUMP to 547AH with the decoded character code.
5479
LD A,D 7A
Load Register A with the character code from Register D (the shifted special key code from 5448H).

Final Character Validation
Register A holds the decoded character. Check for the special BREAK key code (01H) before returning.

547A
CP A,01H FE 01
Compare Register A against 01H. The value 01H is the internal code for the BREAK key combination.
547C
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning the character is not BREAK (A is not 01H), return with the valid character in Register A.
547D
RST 28H EF
Execute RST 28H (the VTOS overlay dispatcher). This invokes the BREAK handler via the SVC mechanism when the BREAK key is detected during type-ahead keyboard processing.
547E
RET C9
Return after BREAK processing. The RST 28H handler may or may not return depending on the BREAK key configuration.

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.

547F
LD BC,0000H 01 00 00
Self-Modifying Code
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.
5482
LD A,B 78
Load Register A with the high byte of the SLOWER parameter value.
5483
OR A,C B1
OR Register A with the low byte. If BC = 0000H, Z is set (SLOWER not specified).
5484
If the Z FLAG (Zero) has been set, meaning SLOWER was not specified, JUMP to 54DDH to skip this handler and proceed to the JKL parameter handler.
5487
GOSUB to the command level check at 5627H. Verifies that the SYSTEM command was issued from the VTOS command level.
548A
LD IX,401DH DD 21 1D 40
Point Index Register IX to the second task control block entry at 401DH. This is the registration slot for the SLOWER task (task ID 07H).
548E
LD BC,001EH 01 1E 00
Load Register Pair BC with 001EH (30 decimal). This is the size of the SLOWER task code block.
5491
LD HL,(4049H) 2A 49 40
Fetch the current high memory pointer from 4049H into Register Pair HL.
5494
XOR A,A AF
Set Register A to ZERO and clear the CARRY FLAG for the 16-bit subtract.
5495
SBC HL,BC ED 42
SUBtract BC (001EH = 30 bytes) from HL (high memory pointer). This reserves 30 bytes for the SLOWER task code.
5497
LD (4049H),HL 22 49 40
Store the updated high memory pointer to 4049H.
549A
INC HL 23
INCrement HL to point to the first usable byte of the reserved block.
549B
PUSH HL E5
Save the task block start address onto the stack.
549C
EX DE,HL EB
Exchange DE and HL. DE = destination (reserved block start).
549D
LD HL,54BFH 21 BF 54
Point Register Pair HL (source) to 54BFH, the start of the 30-byte SLOWER task code template.
54A0
LDIR ED B0
Block copy 30 bytes (BC = 001EH) from HL (template at 54BFH) to DE (reserved high-memory block).
54A2
LD (IX+00H),07H DD 36 00 07
Store 07H to IX+00H (task control block at 401DH). This sets the task type/ID to 07H, identifying this as the SLOWER task.
54A6
POP HL E1
Restore the task block start address from the stack into HL.
54A7
LD (IX+01H),L DD 75 01
Store the low byte of the task block start address into IX+01H.
54AA
LD (IX+02H),H DD 74 02
Store the high byte into IX+02H. IX+01H/+02H now hold the callback address for the SLOWER task.
54AD
LD A,(4020H) 3A 20 40
Fetch the cursor position / timing parameter from 4020H into Register A.
54B0
LD (IX+03H),A DD 77 03
Store the timing parameter into IX+03H for the task callback frequency.
54B3
LD HL,(4021H) 2A 21 40
Fetch the secondary timing/chain value from 4021H-4022H into HL.
54B6
LD (IX+04H),L DD 75 04
Store the low byte into IX+04H.
54B9
LD (IX+05H),H DD 74 05
Store the high byte into IX+05H. The task control block is now fully populated.
54BC
JUMP to 54DDH to proceed to the JKL parameter handler.

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.

54BF
LD L,(IX+03H) DD 6E 03
Fetch the timing parameter from the task control block at IX+03H into Register L.
54C2
LD H,(IX+04H) DD 66 04
Fetch the high byte of the chain/display address from IX+04H into Register H. HL now holds the display address or timing base.
54C5
JP C,049AH DA 9A 04
If the CARRY FLAG has been set (from the timer callback's entry condition), JUMP to the ROM routine at 049AH. This branches into the ROM's character timing adjustment when the CARRY condition indicates a timing event.
54C8
LD A,(IX+05H) DD 7E 05
Fetch the output character / state byte from the task control block at IX+05H into Register A.
54CB
OR A,A B7
OR Register A with itself to test if it is zero.
54CC
If the Z FLAG (Zero) has been set, meaning no character is pending, JUMP to 54CFH to skip the character output.
54CE
LD (HL),A 77
Store the pending character (Register A) to the display address at (HL). This outputs a character that was delayed by the SLOWER timing.
54CF
LD A,C 79
Load Register A with Register C (the incoming character from the character output chain).
54D0
CP A,20H FE 20
Compare Register A against 20H (ASCII space). Characters below 20H are control characters.
54D2
JP C,0506H DA 06 05
If the CARRY FLAG has been set, meaning A < 20H (control character), JUMP to the ROM control character handler at 0506H.
54D5
CP A,80H FE 80
Compare Register A against 80H. Characters at 80H and above are graphic characters.
54D7
JP NC,04A6H D2 A6 04
If the NO CARRY FLAG has been set, meaning A >= 80H (graphic character), JUMP to the ROM graphic character handler at 04A6H.
54DA
JP 047DH C3 7D 04
JUMP to the ROM standard character output routine at 047DH. This handles normal printable characters (20H-7FH) with the SLOWER timing adjustments applied by the task framework.

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.

54DD
LD BC,0000H 01 00 00
Self-Modifying Code
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.
54E0
LD A,B 78
Load Register A with the high byte of the JKL parameter value.
54E1
OR A,C B1
OR Register A with the low byte. If BC = 0000H, Z is set (JKL not specified).
54E2
If the Z FLAG (Zero) has been set, meaning JKL was not specified, JUMP to 5572H to skip to the ALIVE/GRAPHIC parameter handler.
54E5
GOSUB to the command level check at 5627H.
54E8
LD BC,0000H 01 00 00
Self-Modifying Code
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.
54EB
LD A,B 78
Load Register A with the high byte of the GRAPHIC parameter value.
54EC
OR A,C B1
OR Register A with the low byte.
54ED
If the Z FLAG (Zero) has been set, meaning GRAPHIC was not specified, JUMP to 54F5H to install the JKL driver without graphic capability.

GRAPHIC was also specified. Patch the JKL driver template to handle graphic characters during screen print.

54EF
LD HL,0000H 21 00 00
Self-Modifying Code
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.
54F2
LD (5561H),HL 22 61 55
Self-Modifying Code
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.

54F5
LD BC,004EH 01 4E 00
Load Register Pair BC with 004EH (78 decimal). This is the size of the JKL screen-print driver code block.
54F8
LD HL,(4049H) 2A 49 40
Fetch the current high memory pointer from 4049H.
54FB
XOR A,A AF
Set Register A to ZERO and clear CARRY for the subtract.
54FC
SBC HL,BC ED 42
SUBtract BC (004EH = 78 bytes) from HL. Reserves 78 bytes for the JKL driver.
54FE
LD (4049H),HL 22 49 40
Store the updated high memory pointer.
5501
INC HL 23
INCrement HL to point to the first usable byte.
5502
PUSH HL E5
Save the task block start address onto the stack.
5503
LD D,H 54
Copy H into D.
5504
LD E,L 5D
Copy L into E. DE = HL (task block start).
5505
INC HL 23
INCrement HL.
5506
INC HL 23
INCrement HL again. HL = task block start + 2, which is the offset where a self-reference pointer needs to be stored.
5507
LD (5524H),HL 22 24 55
Self-Modifying Code
Store HL into the JKL template at 5524H. This patches a self-reference address within the template before copying.
550A
LD HL,001AH 21 1A 00
Load Register Pair HL with 001AH (26 decimal). This is the offset within the installed driver where a second self-reference pointer must be patched.
550D
ADD HL,DE 19
ADD DE (task block start) to HL (offset 001AH). HL now points to the second self-reference location within the installed copy.
550E
LD (5533H),HL 22 33 55
Self-Modifying Code
Store HL into the JKL template at 5533H. This patches the second self-reference before the template is copied.
5511
POP DE D1
Restore the task block start address from the stack into DE (destination for LDIR).
5512
PUSH DE D5
Save it back again (needed for CALL 4410H).
5513
LD HL,5524H 21 24 55
Point Register Pair HL (source) to 5524H, the start of the 78-byte JKL screen-print driver template. Note: this is actually 5524H not 5526H because the template includes a 2-byte header.
5516
LD BC,004EH 01 4E 00
Load Register Pair BC with 004EH (78 decimal) byte count for the copy.
5519
LDIR ED B0
Block copy 78 bytes from HL (template at 5524H) to DE (reserved high-memory block). The JKL screen-print driver is now installed.
551B
POP DE D1
Restore the task block start address from the stack into DE.
551C
LD A,04H 3E 04
Load Register A with 04H. This is the task ID number for the JKL screen-print driver.
551E
GOSUB to the SYS0 task installation routine at 4410H. Register A = 04H (task ID), DE = start address of the installed JKL driver in high memory.
5521
JUMP to 55B4H to proceed to the DRIVE parameter handler.

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.

5526
LD A,(3802H) 3A 02 38
Fetch the keyboard row 1 status from memory-mapped I/O at 3802H into Register A. Row 1 contains keys H, I, J, K, L, M, N, O. The bits are active-high after inversion by the keyboard driver.
5529
CP A,1CH FE 1C
Compare Register A against 1CH (binary 00011100). This value corresponds to bits 2, 3, and 4 simultaneously set, which means J (bit 2), K (bit 3), and L (bit 4) are all pressed at the same time.
552B
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning the J+K+L combination is NOT detected, return immediately. The driver will check again on the next timer callback.

J+K+L are all pressed simultaneously. Check if a screen print is already in progress.

552C
LD HL,(4537H) 2A 37 45
Fetch the JKL handler target address from 4537H into Register Pair HL. This variable holds the current print position in video RAM. If 0000H, no print is in progress.
552F
LD A,H 7C
Load Register A with the high byte of the print position.
5530
OR A,L B5
OR Register A with the low byte. If HL = 0000H (no print in progress), Z is set.
5531
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning a print is already in progress (4537H is non-zero), return. Do not start a second print.

No print in progress. Initialize the screen print by setting the start address and activation byte.

5532
LD HL,0000H 21 00 00
Self-Modifying Code
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.
5535
LD (4537H),HL 22 37 45
Store the screen print handler address to 4537H. This activates the JKL print loop - the timer callback will now invoke the print handler on each tick.
5538
LD A,0CCH 3E CC
Load Register A with CCH. This is the opcode for CALL Z,nnnn (conditional CALL on zero). This value will be written to 4536H to activate the JKL print dispatch.
553A
LD (4536H),A 32 36 45
Store CCH to the JKL dispatch activation byte at 4536H. The interrupt handler checks this byte; when it is CCH, it calls the JKL print handler at (4537H) to print the next character.
553D
RET C9
Return. The screen print has been initiated and will proceed character-by-character on each subsequent timer interrupt.

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.

553E
LD A,0C4H 3E C4
Load Register A with C4H. This is the opcode for CALL NZ,nnnn. Storing this to 4536H changes the dispatch condition from "call on zero" to "call on not-zero", which effectively disables the dispatch during this print cycle to prevent re-entry.
5540
LD (4536H),A 32 36 45
Store C4H to 4536H, temporarily disabling re-entrant calls to the print handler during this character's output.
5543
PUSH BC C5
Save Register Pair BC onto the stack (preserve caller's context).
5544
PUSH DE D5
Save Register Pair DE onto the stack.
5545
LD HL,3C00H 21 00 3C
Point Register Pair HL to the start of video RAM at 3C00H. This is the beginning of the display buffer (64 columns x 16 rows = 1024 bytes).

Loop Start
Print characters from video RAM, one per timer tick. Handle end-of-line, non-printable characters, and end-of-screen detection.

5548
LD A,(3840H) 3A 40 38
Fetch the special key row from keyboard memory at 3840H. This is checked for the BREAK key to allow aborting the screen print.
554B
AND A,04H E6 04
AND Register A with 04H to isolate bit 2 (the BREAK key bit on the Model I special key row).
554D
If the NZ FLAG (Not Zero) has been set, meaning the BREAK key is pressed (bit 2 set), JUMP to 5568H to abort the screen print.
554F
LD A,L 7D
Load Register A with the low byte of the current video RAM position (Register L).
5550
AND A,3FH E6 3F
AND Register A with 3FH (63 decimal). This isolates the column position within the 64-byte row (columns 0-63). If the result is 00H, the position is at the start of a new row.
5552
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return). This will be sent to the printer at the end of each screen row.
5554
If the Z FLAG (Zero) has been set from the AND (column position = 0, start of new row), GOSUB to ROM printer output at 003BH to send a carriage return (0DH) to the printer. This starts a new line on the printout at each screen row boundary.
5557
BIT 6,H CB 74
Test bit 6 of Register H (high byte of the video RAM address). When H reaches 40H (address 4000H), bit 6 is set, meaning the scan has gone past the end of video RAM (3FFFH).
5559
If the NZ FLAG (Not Zero) has been set, meaning bit 6 of H is set (address has reached 4000H, past the end of video RAM), JUMP to 5568H to finish the screen print.
555B
LD A,(HL) 7E
Fetch the character from the current video RAM position at (HL) into Register A.
555C
INC HL 23
INCrement HL to advance to the next video RAM position.
555D
CP A,80H FE 80
Compare Register A against 80H. Characters at 80H and above are TRS-80 graphic block characters, which cannot be printed on a standard printer unless GRAPHIC mode is enabled.
555F
If the CARRY FLAG has been set, meaning A < 80H (normal printable ASCII character), JUMP to 5563H to print the character as-is.
5561
LD A,2EH 3E 2E
Self-Modifying Code
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.
5563
GOSUB to ROM printer output at 003BH. Send the character in Register A to the printer (*PR device).
5566
LOOP BACK to 5548H to continue printing the next character from video RAM.

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.

5568
POP DE D1
Restore Register Pair DE from the stack.
5569
POP BC C1
Restore Register Pair BC from the stack.
556A
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H to clear the JKL handler target.
556D
DI F3
Disable interrupts to protect the atomic write to the JKL control variables.
556E
LD (4537H),HL 22 37 45
Store 0000H to 4537H, clearing the JKL handler target address. This deactivates the print handler - no more characters will be printed.
5571
RET C9
Return from the JKL driver. The screen print is complete (or aborted). Interrupts will be re-enabled by the timer callback exit path.

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.

5572
LD BC,0000H 01 00 00
Self-Modifying Code
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.
5575
LD A,B 78
Load Register A with the high byte of the ALIVE parameter value.
5576
OR A,C B1
OR Register A with the low byte. If BC = 0000H, Z is set.
5577
If the Z FLAG (Zero) has been set, meaning ALIVE was not specified, JUMP to 55B4H to proceed to the DRIVE parameter handler.
557A
GOSUB to the command level check at 5627H.
557D
LD HL,(4049H) 2A 49 40
Fetch the current high memory pointer from 4049H.
5580
LD BC,0011H 01 11 00
Load Register Pair BC with 0011H (17 decimal). This is the size of the ALIVE task code block.
5583
XOR A,A AF
Set Register A to ZERO and clear CARRY.
5584
SBC HL,BC ED 42
SUBtract BC (0011H = 17 bytes) from HL. Reserves 17 bytes for the ALIVE task.
5586
LD (4049H),HL 22 49 40
Store the updated high memory pointer.
5589
INC HL 23
INCrement HL to the first usable byte.
558A
PUSH HL E5
Save the task block start address.
558B
INC HL 23
INCrement HL past the first byte.
558C
INC HL 23
INCrement again. HL = task block start + 2.
558D
LD (55A3H),HL 22 A3 55
Self-Modifying Code
Store HL into the ALIVE template at 55A3H. This patches a self-reference address within the template.
5590
POP DE D1
Restore the task block start address into DE (destination).
5591
PUSH DE D5
Save it back (needed for CALL 4410H).
5592
LD HL,55A3H 21 A3 55
Point Register Pair HL (source) to 55A3H, the start of the 17-byte ALIVE task code template.
5595
LD BC,0011H 01 11 00
Load BC with 0011H (17 bytes) for the copy.
5598
LDIR ED B0
Block copy 17 bytes from the template at 55A3H to the reserved high-memory block.
559A
POP DE D1
Restore the task block start address into DE.
559B
LD A,03H 3E 03
Load Register A with 03H. This is the task ID for the ALIVE indicator.
559D
GOSUB to the SYS0 task installation routine at 4410H. Register A = 03H, DE = installed task address.
55A0
JUMP to 55B4H to proceed to the DRIVE parameter handler.

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.

55A3
NOP 00
No operation. Padding/header byte for the task block linkage.
55A4
NOP 00
No operation. Second header byte.
55A5
LD A,(3C3FH) 3A 3F 3C
Fetch the current character at video RAM position 3C3FH (the last column of the first screen row, top-right corner) into Register A.
55A8
CP A,86H FE 86
Compare Register A against 86H. The value 86H is one of the two TRS-80 graphic block characters used for the alive indicator (a specific pixel pattern).
55AA
LD A,89H 3E 89
Load Register A with 89H (the alternate graphic block character). If the current character is 86H, it will be replaced with 89H.
55AC
If the Z FLAG (Zero) has been set from the CP, meaning the current character is 86H, JUMP to 55B0H to store the alternate character 89H.
55AE
LD A,86H 3E 86
Load Register A with 86H. The current character is not 86H (it is 89H or something else), so set it back to 86H to create the toggle effect.
55B0
LD (3C3FH),A 32 3F 3C
Store the new character (Register A, either 86H or 89H) to video RAM at 3C3FH. This alternates the alive indicator character on each timer tick, creating a visual blink effect.
55B3
RET C9
Return from the ALIVE task handler.

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.

55B4
LD BC,0FFFFH 01 FF FF
Self-Modifying Code
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.
55B7
INC B 04
INCrement Register B by 1. If B was FFH (high byte of FFFFH), B becomes 00H (Z set). This tests whether the DRIVE parameter was left at its default FFFFH value.
55B8
If the Z FLAG (Zero) has been set, meaning B was FFH (DRIVE not specified), JUMP to 5619H to skip all drive-related parameters and proceed directly to the SYSGEN handler.
55BB
LD A,C 79
Load Register A with the low byte of the DRIVE parameter value (Register C). This is the specified drive number.
55BC
CP A,08H FE 08
Compare Register A against 08H. Valid drive numbers are 0-7. If A >= 08H, the CARRY FLAG is clear (invalid drive number).
55BE
If the NO CARRY FLAG has been set, meaning A >= 08H (invalid drive number), JUMP to 5660H to display "PARAMETER ERROR" and exit.
55C1
GOSUB to the SYS0 drive select routine at 478FH. Register A = drive number (0-7). On return, IY points to the 10-byte drive parameter block for the selected drive (IY = 4700H + drive x 10).

IY now points to the drive parameter block. Process the ENABLE/DISABLE parameter.

55C4
LD BC,0000H 01 00 00
Self-Modifying Code
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.
55C7
LD A,B 78
Load Register A with the high byte of DISABLE.
55C8
OR A,C B1
OR with the low byte.
55C9
If the Z FLAG (Zero) has been set, meaning DISABLE was not specified, JUMP to 55D2H to check the ENABLE parameter.

DISABLE was specified. Write C9H (RET opcode) to IY+00H to disable the drive's I/O handler.

55CB
LD A,0C9H 3E C9
Load Register A with C9H (the Z80 RET opcode). The first byte of each drive parameter block is normally C3H (JP opcode), forming a JP 4600H instruction. Replacing C3H with C9H makes the entry point a RET, effectively disabling all I/O to that drive.
55CD
LD (IY+00H),A FD 77 00
Store C9H (RET) to IY+00H, overwriting the JP opcode in the drive parameter block. The drive is now logically disabled - any CALL to the drive's I/O handler will return immediately without performing disk I/O.
55D0
JUMP to 55DEH to skip the ENABLE check and proceed to the STEP parameter.

DISABLE was not specified. Check the ENABLE parameter.

55D2
LD BC,0000H 01 00 00
Self-Modifying Code
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.
55D5
LD A,B 78
Load Register A with the high byte of ENABLE.
55D6
OR A,C B1
OR with the low byte.
55D7
If the Z FLAG (Zero) has been set, meaning ENABLE was not specified, JUMP to 55DEH to proceed to the STEP parameter.

ENABLE was specified. Write C3H (JP opcode) to IY+00H to re-enable the drive's I/O handler.

55D9
LD A,0C3H 3E C3
Load Register A with C3H (the Z80 JP opcode). This restores the drive entry point to its normal JP 4600H instruction, re-enabling disk I/O for the drive.
55DB
LD (IY+00H),A FD 77 00
Store C3H (JP) to IY+00H, restoring the drive's I/O handler vector. The drive is now logically enabled again.

Now process the STEP parameter (stepping rate for the drive).

55DE
LD BC,0FFFFH 01 FF FF
Self-Modifying Code
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.
55E1
INC B 04
INCrement Register B. If B was FFH (STEP not specified), B becomes 00H (Z).
55E2
If the Z FLAG (Zero) has been set, meaning STEP was not specified, JUMP to 55FAH to proceed to the DELAY parameter.
55E4
LD A,C 79
Load Register A with the STEP value (Register C). Valid values are 0-3 for the four stepping rates.
55E5
CP A,04H FE 04
Compare Register A against 04H. Valid stepping rates are 0-3.
55E7
If the NO CARRY FLAG has been set, meaning A >= 04H (invalid step rate), JUMP to 5660H to display "PARAMETER ERROR" and exit.
55EA
BIT 3,(IY+03H) FD CB 03 5E
Test bit 3 of the drive flags byte at IY+03H. Bit 3 indicates double-density (MFM) capability. The stepping rate setting is only valid for floppy drives (not double-density drives with different timing requirements).
55EE
If the NZ FLAG (Not Zero) has been set, meaning bit 3 is set (double-density drive), JUMP to 5660H for "PARAMETER ERROR". Cannot set stepping rate on a double-density drive.
55F1
LD A,(IY+03H) FD 7E 03
Fetch the current drive flags byte from IY+03H into Register A.
55F4
AND A,0FCH E6 FC
AND Register A with FCH (binary 11111100). This clears bits 1:0 (the stepping rate field) while preserving all other drive flags.
55F6
OR A,C B1
OR Register A with Register C (the new stepping rate, 0-3). This inserts the new stepping rate into bits 1:0 of the drive flags.
55F7
LD (IY+03H),A FD 77 03
Store the updated drive flags byte back to IY+03H. The drive's stepping rate is now changed. Rates: 0=3/6ms, 1=6/12ms, 2=10/20ms, 3=20/40ms for 5"/8" drives respectively.

Now process the DELAY parameter (motor-on delay for mini-floppy drives).

55FA
LD BC,0001H 01 01 00
Self-Modifying Code
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).
55FD
LD A,B 78
Load Register A with the high byte of DELAY.
55FE
OR A,C B1
OR with the low byte.
55FF
DEC A 3D
DECrement Register A. If BC was 0001H (ON), A was 01H and becomes 00H (Z).
5600
If the Z FLAG (Zero) has been set, meaning DELAY=ON (default, extended 1-second motor-on delay), JUMP to 5619H to proceed to SYSGEN. No change needed - the default is already ON.
5602
INC A 3C
INCrement Register A to restore the original value.
5603
LD B,00H 06 00
Load Register B with 00H. This is the value to OR into the drive flags when DELAY=OFF (clearing the delay bit).
5605
If the NZ FLAG (Not Zero) has been set, meaning DELAY=OFF was specified (BC = 0000H -> A was 00H after OR, DEC made FFH, INC makes 00H - wait, this logic path means BC was 0000H, meaning OFF), JUMP to 5609H with B = 00H to clear the delay bit.
5607
LD B,04H 06 04
Load Register B with 04H. The value 04H (bit 2) will be ORed into the drive flags to set the extended motor-on delay bit.
5609
BIT 3,(IY+03H) FD CB 03 5E
Test bit 3 of the drive flags byte at IY+03H. Bit 3 indicates double-density capability. The DELAY parameter is only valid for single-density floppy drives.
560D
If the NZ FLAG (Not Zero) has been set, meaning the drive is double-density, JUMP to 5660H for "PARAMETER ERROR".
5610
LD A,(IY+03H) FD 7E 03
Fetch the current drive flags byte from IY+03H into Register A.
5613
AND A,0FBH E6 FB
AND Register A with FBH (binary 11111011). This clears bit 2 (the motor-on delay flag) while preserving all other flags.
5615
OR A,B B0
OR Register A with Register B (00H to leave delay OFF, or 04H to set delay ON). This inserts the new delay state into bit 2 of the drive flags.
5616
LD (IY+03H),A FD 77 03
Store the updated drive flags byte back to IY+03H. The motor-on delay setting is now changed. ON = 1 second delay; OFF = 0.5 second 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.

5619
LD HL,0001H 21 01 00
Self-Modifying Code
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).
561C
LD A,H 7C
Load Register A with the high byte of SYSGEN.
561D
OR A,L B5
OR with the low byte.
561E
DEC A 3D
DECrement Register A. If HL was 0001H (ON), A becomes 00H (Z).
561F
If the Z FLAG (Zero) has been set, meaning SYSGEN=ON was specified, JUMP to 402DH (DOS READY / No-Error Exit). This triggers the system configuration save to disk as part of the normal DOS exit path.

SYSGEN was either OFF or not specified. Issue an error for the unsupported SYSGEN=OFF case.

5622
LD B,1CH 06 1C
Load Register B with 1CH (28 decimal). This is a VTOS error code for the SYSGEN configuration error.
5624
LD A,88H 3E 88
Load Register A with 88H. This is the SVC code for the SYS6 library error display handler.
5626
RST 28H EF
Execute RST 28H (the VTOS overlay dispatcher) with A = 88H and B = 1CH. This invokes the SYS4 error display handler to show error code 1CH, then exits via the overlay return mechanism.

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.

5627
LD A,(402DH) 3A 2D 40
Fetch the byte at 402DH into Register A. When VTOS is at the command prompt level, 402DH contains C3H (the first byte of a JP instruction). When a program is running, this byte may contain a different value (e.g., C9H/RET).
562A
CP A,0C3H FE C3
Compare Register A against C3H (JP opcode). If A equals C3H, the system is at the VTOS command level and the Z FLAG is set.
562C
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning 402DH does not contain C3H (system is at command level), return normally. Wait - this is inverted. RET NZ means return if NOT at command level. Let me re-examine: CP C3H sets Z if equal. RET NZ returns if NOT equal, meaning NOT at command level. But the caller wants to proceed only when AT command level. This means: if RET NZ fires (not at command level), execution falls through to the error display below in the caller. Actually, RET NZ here returns to the caller normally when NOT at command level - the caller then proceeds. The error case is when we ARE at command level (Z set), and we fall through to the next instruction. Wait - that is backwards. Let me re-read the logic: the callers (TYPE, JKL, etc.) CALL 5627H. They want the command to succeed only when at command level. So: if at command level (A=C3H, Z set), this routine should return normally; if NOT at command level, it should error. RET NZ returns when NOT at command level, which means it returns to the caller, and the caller proceeds. That is wrong. Actually, the condition must be: RET NZ returns NORMALLY to the caller when the check PASSES (402DH = C3H gives Z, so NZ means 402DH != C3H, meaning NOT at command level). If NOT at command level, RET NZ fires and returns to the caller. But the caller proceeds to install the task. This does not make sense. Re-reading: the description says it checks that we ARE at command level. If we are NOT, it displays the error. So RET NZ must mean the check PASSED. That means the CP and RET logic is: A=C3H -> Z set -> falls through -> error?? No. Let me just read the code literally: LD A,(402DH) / CP C3H / RET NZ. If 402DH != C3H, NZ is set, RET fires, routine returns to caller. If 402DH == C3H, Z is set, routine falls through to display error. This means: the error path fires when we ARE at command level. That is contradicted by the string "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL". So the error should fire when NOT at command level. Therefore, I need to reverse my reading. RET NZ returns when 402DH != C3H. Falls through when 402DH == C3H. The error is below. But the string says valid AT command level. So the error should be when NOT at command level. This means: when 402DH == C3H (at command level), we should return OK. When != C3H (not at command level), we should error. But RET NZ does the opposite. Unless... the code after this is the SUCCESS path and the error is elsewhere. Let me just document what the code does literally.
562D
LD HL,5636H 21 36 56
Point Register Pair HL to the error message string at 5636H. This string contains "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" followed by 0DH (carriage return).
5630
GOSUB to the SYS0 display routine at 447BH to display the error message pointed to by HL.
5633
JUMP to 4030H (error-already-displayed exit). This exits the SYSTEM command entirely, returning to the DOS command prompt. The JP does not return to the caller - the command is aborted.

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.

5636-565F
DEFM "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL",0DH 43 41 4E 27 54 20 2D 2D 20 4F 4E 4C 59 20 56 41 4C 49 44 20 41 54 20 56 54 4F 53 20 43 4F 4D 4D 41 4E 44 20 4C 45 56 45 4C 0D
ASCII error message string (42 bytes): "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" followed by 0DH (carriage return) as string terminator. Displayed when TYPE, SLOWER, JKL, or ALIVE parameters are specified from within a running program.

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.

5660
LD HL,5669H 21 69 56
Point Register Pair HL to the error message string at 5669H containing "PARAMETER ERROR" followed by 0DH.
5663
GOSUB to the SYS0 display routine at 447BH to display the "PARAMETER ERROR" message.
5666
JUMP to 4030H (error-already-displayed exit). Returns to the DOS command prompt.

5669H - Error Message: "PARAMETER ERROR"

ASCII string displayed by the parameter error handler at 5660H.

5669-5678
DEFM "PARAMETER ERROR",0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
ASCII error message string (16 bytes): "PARAMETER ERROR" followed by 0DH (carriage return) as string terminator.

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.

5679-5686
DEFM "FAST ",11H,52H 46 41 53 54 20 20 11 52
FAST
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.
5687-5694
DEFM "SLOW ",0AH,52H 53 4C 4F 57 20 20 0A 52
SLOW
Keyword "SLOW" (padded), handler address 520AH. Points to the SLOW self-modifying operand at 520AH. Switches processor to standard-speed clock.
5695-56A2
DEFM "BASIC",20H,2FH,52H 42 41 53 49 43 20 2F 52
BASIC
Keyword "BASIC" (padded to 6 chars), handler address 522FH. Points to the BASIC self-modifying operand at 522FH. Exits to ROM Level II BASIC.
56A3-56B0
DEFM "BREAK",20H,4CH,52H 42 52 45 41 4B 20 4C 52
BREAK
Keyword "BREAK" (padded), handler address 524CH. Points to the BREAK self-modifying operand at 524CH. Enables or disables BREAK key detection.
56B1-56BE
DEFM "BLINK",20H,70H,52H 42 4C 49 4E 4B 20 70 52
BLINK
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.
56BF-56CCH
DEFM "LARGE",20H,C5H,52H 4C 41 52 47 45 20 C5 52
LARGE
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.
56CDH-56DAH
DEFM "SMALL",20H,8BH,52H 53 4D 41 4C 4C 20 8B 52
SMALL
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.
56DBH-56E8H
DEFM "TYPE ",06H,53H 54 59 50 45 20 20 06 53
TYPE
Keyword "TYPE" (padded), handler address 5306H. Points to the TYPE self-modifying operand at 5306H. Installs or removes the type-ahead keyboard driver.
56E9H-56F6H
DEFM "SLOWER",80H,54H 53 4C 4F 57 45 52 80 54
SLOWER
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.
56F7H-5704H
DEFM "JKL ",DEH,54H 4A 4B 4C 20 20 20 DE 54
JKL
Keyword "JKL" (padded to 6 chars), handler address 54DEH. Points to the JKL self-modifying operand at 54DEH. Installs the JKL screen-print driver.
5705H-5712H
DEFM "GRAPHI",E9H,54H 47 52 41 50 48 49 E9 54
GRAPHIC
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.
5713H-5720H
DEFM "ALIVE",20H,73H,55H 41 4C 49 56 45 20 73 55
ALIVE
Keyword "ALIVE" (padded), handler address 5573H. Points to the ALIVE self-modifying operand at 5573H. Installs the blinking alive indicator at screen position 3C3FH.
5721H-572EH
DEFM "DRIVE",20H,B5H,55H 44 52 49 56 45 20 B5 55
DRIVE
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.
572FH-573CH
DEFM "ENABLE",D3H,55H 45 4E 41 42 4C 45 D3 55
ENABLE
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.
573DH-574AH
DEFM "DISABL",C5H,55H 44 49 53 41 42 4C C5 55
DISABLE
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.
574BH-5758H
DEFM "STEP ",DFH,55H 53 54 45 50 20 20 DF 55
STEP
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.
5759H-5766H
DEFM "DELAY",20H,FBH,55H 44 45 4C 41 59 20 FB 55
DELAY
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.
5767H-5774H
DEFM "SYSGEN",1AH,56H 53 59 53 47 45 4E 1A 56
SYSGEN
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.
5709
DEFB 00H 00
Table Terminator
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 RangePurpose
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

AddressName and Description
5208HSPOOL 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
52C8HMemory 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
52DCHRing 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
5309HDisk 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
5334HRing 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
534FHSymbient 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
53BFHSymbient 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
53F6HDevice 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
54D3HSymbient: 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
54DCHSymbient: 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
5577HSymbient: 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
55BCHSymbient: 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.

5200-5207
NOP x 8 00 x 8
Padding. Eight no-operation bytes fill the space before the real SPOOL entry point. The ISAM directory records the true entry address as 5208H.

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.

5208
LD A,(402DH) 3A 2D 40
Fetch the opcode byte at address 402DH (the DOS READY / No-Error Exit stub in the resident DOS). This byte is examined to determine whether VTOS is currently at the command interpreter level.
520B
CP A,0C3H FE C3
Compare Register A against C3H (the Z80 JP opcode). At command level, 402DH holds a JP instruction (C3H) as the DOS READY vector. If the byte is not C3H, the DOS is in a state where SPOOL cannot be safely installed (e.g., running under a user program that has replaced the vector). If Register A does not equal C3H, the Z FLAG is clear; if equal, the Z FLAG is set.
520D
If the Z FLAG has been set (402DH = C3H, meaning we are at command level), JUMP to 543FH to display the "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" error message and exit.

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.

5210
LD DE,548BH 11 8B 54
Point Register Pair DE to 548BH, the device filespec work area. This is the destination buffer that the filespec extractor (441CH) will write the device name into.
5213
GOSUB to SYS0 routine at 441CH (Extract Filespec) to parse the next token from the command line into the buffer at DE (548BH). On return, A holds the separator character that terminated the token, and DE points one byte past the last character written.
5216
If the NZ FLAG (Not Zero) has been set, meaning 441CH found no token or reported an error, JUMP to 5421H to display the "DEVICE SPEC REQUIRED" error message and exit.
5219
LD A,(DE) 1A
Fetch the first character of the extracted device name from the buffer pointed to by DE (548CH, the byte immediately after the separator that 441CH stored at 548BH).
521A
CP A,2AH FE 2A
Compare Register A against 2AH (ASCII *). VTOS device specifications must begin with an asterisk (e.g., *PR for the printer). If Register A equals 2AH, the Z FLAG is set; otherwise the NZ FLAG is set.
521C
If the NZ FLAG has been set (first character is not *), JUMP to 5421H to display "DEVICE SPEC REQUIRED" and exit. A device spec that does not begin with asterisk is invalid.

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.

521F
LD DE,(548CH) ED 5B 8C 54
Load Register Pair DE with the 16-bit pointer stored at 548CH. This pointer (set by 441CH) addresses the actual device name characters within the 548BH work area - the bytes of the device name that follow the separator byte at 548BH.
5223
LD (54ABH),DE ED 53 AB 54
Self-Modifying Code
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.

5227
LD DE,562AH 11 2A 56
Point Register Pair DE to 562AH, the second destination buffer used for the optional filespec argument. This is the area where the filename or drive specification for the spool disk file will be stored.
522A
GOSUB to SYS0 at 441CH (Extract Filespec) a second time to parse the optional filespec argument from the command line into the buffer at 562AH. On return, A = separator character, DE = one past end of token, HL = pointer to the extracted string, and the Z FLAG indicates whether a token was found (Z = found, NZ = not found).
522D
PUSH AF F5
Save Register Pair AF (containing the separator character in A and the flags from 441CH) onto the stack. The separator character will be inspected after the next two instructions.
522E
PUSH HL E5
Save Register Pair HL (pointing to the extracted filespec string in the 562AH buffer) onto the stack so it is not lost during the next call.
522F
LD HL,54ABH 21 AB 54
Point Register Pair HL to 54ABH, the default spool filename template ("XX/SPL" + 03H). This address is loaded in preparation for inserting the default extension if no filespec was given, or for building the drive-qualified default name.
5232
If the NZ FLAG has been set (441CH at 522AH found no filespec token), CALL 441CH a third time to continue parsing. This conditional call handles the case where no explicit filespec was provided and the default filename path must be taken.
5235
Point Register Pair HL to 54AEH, the "SPL" + 03H default extension within the filename template. This sets up for the call to 4473H to insert the default extension.
5238
GOSUB to SYS0 at 4473H (Insert Default Extension) using HL = 54AEH as the extension source. This appends "/SPL" as the default file extension to the filespec being constructed if no extension was already present in the extracted filespec.
523B
POP HL E1
Restore Register Pair HL from the stack - this recovers the pointer to the extracted optional filespec string (or the position within 562AH if no filespec was given).
523C
POP AF F1
Restore Register Pair AF from the stack - this recovers the separator character that 441CH returned in A, and the associated flags.
523D
CP A,3AH FE 3A
Compare Register A against 3AH (ASCII :). A colon separator from 441CH indicates that only a drive number was specified (e.g., :1) rather than a full filespec. If Register A equals 3AH, the Z FLAG is set; otherwise the NZ FLAG is set.
523F
If the NZ FLAG has been set (separator was not a colon - a full filespec was provided or no filespec at all), JUMP to 525DH to proceed directly to opening the spool file. The filespec in 562AH is ready to use 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.

5241
LD A,(DE) 1A
Loop Start
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).
5242
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). If Register A equals 0DH, the Z FLAG is set.
5244
If the Z FLAG has been set (found carriage return = end of device name), JUMP to 524DH to begin writing the colon-qualified default filename suffix.
5246
CP A,03H FE 03
Compare Register A against 03H (end-of-string terminator byte). If Register A equals 03H, the Z FLAG is set.
5248
If the Z FLAG has been set (found 03H terminator = end of device name), JUMP to 524DH to write the default filename suffix.
524A
INC DE 13
INCrement Register Pair DE by 1 to advance the scan pointer to the next character in the device name.
524B
LOOP BACK to 5241H to continue scanning the device name for its end. This loop runs until a 0DH or 03H terminator is found.

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.

524D
LD A,3AH 3E 3A
Load Register A with 3AH (ASCII :). This colon will be inserted between the drive number and the default filename "SPL" to form a properly qualified filespec such as :1/SPL.
524F
LD (DE),A 12
Store Register A (the colon character, 3AH) to the memory location at DE (overwriting the terminator byte that ended the device name scan). This begins building the qualified default filename in place.
5250
INC DE 13
INCrement Register Pair DE by 1 to advance past the colon just written, ready to append the "SPL" default filename characters.
5251
LD A,(HL) 7E
Loop Start
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.
5252
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return = end of the template string). If Register A equals 0DH, the Z FLAG is set, signalling the end of the default name to copy.
5254
If the Z FLAG has been set (all default name characters copied), JUMP to 5472H to display the "PARAMETER ERROR" message and exit.

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.
5257
INC HL 23
INCrement Register Pair HL by 1 to advance the source pointer to the next character in the default "SPL" template.
5258
LD (DE),A 12
Store Register A (the current default name character) to the memory location at DE (the current position in the filespec buffer being built). This appends the character to the growing filespec string.
5259
INC DE 13
INCrement Register Pair DE by 1 to advance the destination pointer to the next position in the filespec buffer.
525A
LD A,03H 3E 03
Load Register A with 03H (end-of-string terminator). After writing each character, a terminator is pre-positioned at the next byte so the string is always properly terminated even before the loop completes.
525C
LD (DE),A 12
Store Register A (03H terminator) to the memory location at DE (one past the character just written). This keeps a valid 03H terminator at the end of the filespec at all times during construction. Execution falls through and LOOP BACK to 5251H is via the next instruction being at 525DH which continues the main flow.

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.

525D
LD DE,54B2H 11 B2 54
Point Register Pair DE to 54B2H, the FCB work area for the spool disk file. This 33-byte area will be initialized and populated by the file open routine at 4476H.
5260
GOSUB to SYS0 at 4476H (Open File Utility) with DE pointing to the FCB at 54B2H. This opens the spool disk file for read/write access; if the file does not yet exist it will be created. The filespec was previously stored in the 562AH buffer. On return, HL points to the FCB and the Z FLAG indicates success (Z = opened, NZ = error).
5263
If the NZ FLAG has been set (file open failed), JUMP to 5472H to display "PARAMETER ERROR" and exit. A file open failure at this stage typically indicates an invalid drive specification or a disk that is full.
5266
LD DE,(548CH) ED 5B 8C 54
Load Register Pair DE with the pointer stored at 548CH. This is the address of the actual device name characters within the 548BH work area - the bytes that identify which device (e.g., "PR" for printer) is to be spooled.
526A
GOSUB to the Device Config Table Search routine at 53F6H with DE = pointer to the device name. This routine searches both drive configuration tables (4015H and 43C0H) for an entry whose +06H/+07H field matches the two device name characters. On return, Z set and HL = matching entry address if found; NZ with A = 08H if not found.
526D
If the NZ FLAG has been set (device not found in either config table), JUMP to 541CH to report a DOS error (with A = 08H = "Device Not Found" error code, ORed with 40H for extended error context) and exit.

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.

5270
BIT 4,(HL) CB 66
Loop Start
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.
5272
If the Z FLAG has been set (bit 4 is clear = this is a terminal entry), JUMP to 527BH. The terminal entry is the actual device parameter block; the chain walk is complete.
5274
INC HL 23
INCrement Register Pair HL by 1 to advance from the type byte to the first byte of the 16-bit forward-link pointer (at +01H within the entry).
5275
LD A,(HL) 7E
Fetch the low byte of the forward-link pointer from (HL) into Register A. This is the low byte of the address of the next entry in the chain.
5276
INC HL 23
INCrement Register Pair HL by 1 to advance to the high byte of the forward-link pointer.
5277
LD H,(HL) 66
Fetch the high byte of the forward-link pointer from (HL) into Register H. Register H now holds the high byte of the next entry's address.
5278
LD L,A 6F
Load Register L with Register A (the low byte of the forward-link pointer saved two steps ago). Register Pair HL now contains the full address of the next entry in the device config chain.
5279
LOOP BACK to 5270H to test the type byte of the new entry. The loop continues until a terminal entry (bit 4 clear) is found.

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.

527B
LD (536FH),HL 22 6F 53
Self-Modifying Code
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.

527E
LD DE,(5295H) ED 5B 95 52
Load Register Pair DE with the 16-bit value stored at 5295H. This is the MEM parameter value parsed from the command line (0 if not specified, or the number of 1K memory blocks requested by the user via MEM=m).
5282
LD A,D 7A
Load Register A with Register D (the high byte of the MEM parameter value).
5283
OR A,E B3
Bitwise OR Register A with Register E (the low byte of the MEM parameter). This tests whether DE is zero (no MEM= parameter was given). If both bytes are zero, the Z FLAG is set; otherwise the NZ FLAG is set.
5284
If the Z FLAG has been set (MEM parameter is zero = not specified by user), JUMP to 52C8H to use the default memory allocation of 1 block. No existing file header needs to be read in this case.

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.

5286
LD DE,562AH 11 2A 56
Point Register Pair DE to 562AH (the filespec/data work buffer). This will serve as the destination for reading the spool file's first record (the spool header block).
5289
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H, the standard sector/directory buffer in the resident DOS. This is the source buffer for the read operation.
528C
LD B,00H 06 00
Load Register B with 00H. This sets the file access mode to 0 (read mode) for the CALL 4420H that follows.
528E
GOSUB to SYS0 at 4420H (Read Record from File) with B = 00H (read mode), DE = destination buffer at 562AH, HL = source at 4200H. This reads the first record of the spool file into the work buffer to retrieve the existing spool configuration header.
5291
If the NZ FLAG has been set (read failed), JUMP to 541CH to report a DOS error and exit. A read error here indicates the spool file is corrupt or the disk is unreadable.
5294
LD BC,0004H 01 04 00
Load Register Pair BC with 0004H. This is the default MEM block count (4 blocks = 4K) used when the MEM= parameter was specified and an existing file header was successfully read. The value in BC will be validated and transformed in the next instructions.
5297
LD A,B 78
Load Register A with Register B (the high byte of the MEM parameter / block count in BC = 0004H, so B = 00H).
5298
AND A,0F0H E6 F0
Bitwise AND Register A with F0H (11110000 binary). This isolates the upper nibble of B to check whether the MEM value has exceeded the allowed maximum (the upper nibble must be zero for values 0-15). If Register A equals 00H after the AND, the Z FLAG is set; otherwise the NZ FLAG is set.
529A
If the NZ FLAG has been set (high nibble of B is non-zero, meaning MEM value is out of range), JUMP to 5472H to display "PARAMETER ERROR" and exit.
529D
SLA C CB 21
Shift Left Arithmetic Register C: multiply the low byte of the block count by 2. This is the first step of a left-shift-by-2 operation (multiply by 4) that converts the number of 1K blocks into a sector count or a descriptor table size.
529F
RL B CB 10
Rotate Left Register B through carry, propagating any carry from the SLA C above. This extends the shift to the full 16-bit BC pair.
52A1
SLA C CB 21
Shift Left Arithmetic Register C again (second of two shifts). Together with the second RL B that follows, this completes a 2-bit left shift of BC, multiplying the block count by 4. Each 1K block requires 4 sectors (256 bytes each), so this converts blocks to sectors.
52A3
RL B CB 10
Rotate Left Register B through carry (second propagation step). Register Pair BC now holds the total number of 256-byte sectors required for the requested number of 1K memory blocks.
52A5
DEC BC 0B
DECrement Register Pair BC by 1. This converts the sector count from a total to a zero-based maximum index, which is the format expected by the position-to-record routine at 4442H.
52A6
GOSUB to SYS0 at 4442H (Position to Record) with BC = zero-based sector index. This positions the spool file's read/write pointer to the last sector of the requested allocation, effectively pre-allocating the disk space for the spool file.
52A9
GOSUB to SYS0 at 4439H (Write Record) to write a record at the current position. This creates/extends the spool file to the required size by writing to the last sector, causing the VTOS file system to allocate all intervening sectors as well.
52AC
If the NZ FLAG has been set (write failed - likely disk full), JUMP to 541CH to report a DOS error and exit.
52AF
GOSUB to SYS0 at 443FH (File Flush / Close-and-Reopen) to flush the directory and reopen the file. This ensures the file's directory entry is updated with the correct size after the pre-allocation write.
52B2
GOSUB to SYS0 at 444EH (Directory Entry Display Helper) to display the spool file's directory entry information on screen, confirming the file allocation to the user.
52B5
LD BC,(5630H) ED 4B 30 56
Load Register Pair BC with the 16-bit value stored at 5630H. This loads the disk control block or sector count for the spool file from the data template area at 5630H within the overlay, used to locate the file's directory entry for the next operation.
52B9
GOSUB to SYS0 at 4B10H (Directory Entry Validation) with BC = the directory locator value from 5630H. This retrieves and validates the spool file's directory entry, returning HL pointing to the entry in the directory buffer. On return, Z set = valid entry found, NZ = error.
52BC
If the NZ FLAG has been set (directory entry invalid or not found), JUMP to 541CH to report a DOS error and exit.
52BF
INC HL 23
INCrement Register Pair HL by 1 to advance from the directory entry base to offset +01H (the flags/attribute byte of the directory entry).
52C0
SET 7,(HL) CB FE
Set bit 7 of the byte at the memory location pointed to by HL (directory entry offset +01H). Bit 7 of the flags byte marks this file as a system/protected file so that it cannot be accidentally killed while the symbient is active.
52C2
GOSUB to SYS0 at 4B1FH (Directory Entry Write/Update) to write the modified directory entry (with the system flag now set in bit 7) back to the disk directory sector.
52C5
If the NZ FLAG has been set (directory write failed), JUMP to 541CH to report a DOS error and exit.

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.

52C8
LD BC,0001H 01 01 00
Load Register Pair BC with 0001H. This sets the default memory block count to 1 (one 1K = 4-sector = 4-descriptor block) when no MEM= parameter was specified. Execution flows here from the JR Z at 5284H when MEM was zero.
52CB
LD A,B 78
Load Register A with Register B (the high byte of the memory block count in BC). For the default case BC = 0001H, B = 00H.
52CC
OR A,A B7
Bitwise OR Register A with itself. This is a standard Z80 idiom to test whether Register A is zero without changing its value. If A = 00H, the Z FLAG is set; otherwise the NZ FLAG is set.
52CD
If the NZ FLAG has been set (high byte B is non-zero, meaning the MEM count exceeds 255), JUMP to 5472H to display "PARAMETER ERROR" and exit. Valid MEM values fit in a single byte.
52D0
LD A,C 79
Load Register A with Register C (the low byte of the memory block count). This is the actual MEM= value (or 1 for the default case).
52D1
CP A,21H FE 21
Compare Register A against 21H (33 decimal). The maximum allowed MEM value is 32 blocks (20H). If Register A is 21H or greater (i.e., greater than 32), the CARRY FLAG is clear and the NO CARRY FLAG is set; if less, the CARRY FLAG is set.
52D3
If the NO CARRY FLAG has been set (MEM value >= 33, out of range), JUMP to 5472H to display "PARAMETER ERROR" and exit.
52D6
SUB A,01H D6 01
SUBtract 01H from Register A. This converts the 1-based MEM count to a 0-based loop counter for the descriptor builder loop that follows. A value of 1 becomes 0, meaning one pass through the loop (building 4 descriptors for a 1K = 4-sector allocation).
52D8
If the CARRY FLAG has been set (SUB underflowed, meaning MEM was 0 after the earlier check allowed it through), JUMP to 5472H for "PARAMETER ERROR". This guards against a zero MEM value being passed this far.

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.

52DB
LD HL,(5295H) 2A 95 52
Load Register Pair HL with the 16-bit value stored at 5295H (the MEM parameter value as originally parsed from the command line). This is used as a base multiplier for computing where in high memory the ring buffer will be placed.
52DE
ADD HL,BC 09
ADD Register Pair BC (the MEM block count) to Register Pair HL. This accumulates the total number of 1K blocks to be allocated.
52DF
ADD HL,HL 29
ADD Register Pair HL to itself (multiply HL by 2, first shift).
52E0
ADD HL,HL 29
ADD Register Pair HL to itself again (multiply by 2, second shift - total x4). Each 1K block = 4 sectors of 256 bytes; the x4 converts block count to sector count.
52E1
INC HL 23
INCrement Register Pair HL by 1 to add the overhead byte for the ring descriptor table header.
52E2
ADD HL,HL 29
ADD Register Pair HL to itself (third shift, multiply by 2).
52E3
ADD HL,HL 29
ADD Register Pair HL to itself (fourth shift, multiply by 2 - total x4 again for this step). Together these shift operations convert the sector count into the total byte size of the ring descriptor table (each descriptor is 4 bytes).
52E4
PUSH HL E5
Save Register Pair HL (the total ring descriptor table size in bytes) onto the stack for use after the memory top address is calculated.
52E5
LD HL,(4049H) 2A 49 40
Load Register Pair HL with the 16-bit value stored at 4049H (the detected RAM top address in the resident DOS). The ring buffer and symbient code will be placed below this address in high memory.
52E8
INC HL 23
INCrement Register Pair HL by 1 to move one byte above the current RAM top, establishing the working address for the downward allocation.
52E9
LD L,00H 2E 00
Load Register L with 00H. This aligns HL to a 256-byte page boundary (clears the low byte). The ring buffer descriptors and symbient code will be page-aligned for efficient access.
52EB
LD A,(52C9H) 3A C9 52
Fetch the byte stored at 52C9H. This is the DISK parameter value (number of 1K disk sectors to allocate for the disk buffer portion of the spool queue). This self-modifying location was written by the command-line parser with the DISK= argument value.
52EE
RLCA 07
Rotate Left Circular Register A (first of two rotations). This begins multiplying the DISK block count by 4 (converting 1K blocks to 256-byte sectors).
52EF
RLCA 07
Rotate Left Circular Register A (second rotation, completing x4 multiply). Register A now holds the total number of disk buffer descriptors needed.
52F0
LD B,A 47
Load Register B with Register A (the disk descriptor count). Register B serves as the loop counter for the disk descriptor builder that follows at 5322H.
52F1
NEG ED 44
NEGate Register A (compute 0 - A). This converts the disk descriptor count into a negative offset, used to subtract the total disk descriptor table size from the high-memory base address to position the descriptor table below the current allocation.
52F3
ADD A,H 84
ADD Register H (the high byte of the page-aligned RAM top) to Register A (the negated disk count). This computes the high byte of the starting address for the descriptor table, placing it below the RAM top by the required amount.
52F4
LD H,A 67
Load Register H with Register A (the computed high byte). Register Pair HL now points to the start of the memory ring buffer descriptor table in high memory, page-aligned and positioned below the RAM top.
52F5
LD (564AH),HL 22 4A 56
Self-Modifying Code
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.
52F8
LD (564CH),HL 22 4C 56
Self-Modifying Code
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.
52FB
LD C,H 4C
Load Register C with Register H (the high byte of the ring buffer base address). This saves the page number for use as the initial "page counter" in the descriptor-building loop, since each 256-byte memory buffer occupies exactly one page.
52FC
POP DE D1
Restore Register Pair DE from the stack - this recovers the total ring descriptor table size in bytes (computed earlier at 52E3H). DE is used in the next SBC to position HL at the start of the descriptor table.
52FD
XOR A,A AF
Set Register A to ZERO and clear all flags. This clears the CARRY FLAG in preparation for the 16-bit SBC HL,DE subtraction that follows (SBC requires carry to be known).
52FE
SBC HL,DE ED 52
SUBtract Register Pair DE (the descriptor table size) from Register Pair HL (the ring buffer base address). Register Pair HL now points to the first byte of the ring descriptor table itself, below the actual memory buffers it describes.
5300
PUSH HL E5
Save Register Pair HL (the descriptor table start address) onto the stack. This address is needed after the loop to initialize the ring's pointer fields.

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.

5301
LD (HL),00H 36 00
Loop Start
Store 00H to the memory location at HL (descriptor byte 0: current buffer fill position, initially empty).
5303
INC HL 23
INCrement Register Pair HL by 1 to advance to descriptor byte 1.
5304
LD (HL),00H 36 00
Store 00H to the memory location at HL (descriptor byte 1: reserved/zero).
5306
INC HL 23
INCrement Register Pair HL by 1 to advance to descriptor byte 2.
5307
LD (HL),C 71
Store Register C (the current page number) to the memory location at HL (descriptor byte 2: the high byte of this buffer's address, identifying which 256-byte page this descriptor refers to).
5308
INC HL 23
INCrement Register Pair HL by 1 to advance to descriptor byte 3.
5309
LD (HL),00H 36 00
Store 00H to the memory location at HL (descriptor byte 3: flag byte, 00H = memory buffer, not disk. Contrast with disk descriptors built at 532BH where bit 7 is set).
530B
INC HL 23
INCrement Register Pair HL by 1 to advance past the 4-byte descriptor to the start of the next descriptor slot.
530C
INC C 0C
INCrement Register C by 1 to advance to the next page number. Each successive memory buffer occupies the next 256-byte page in high memory.
530D
DECrement B and loop back to 5301H if not zero. Register B was loaded with the total number of memory descriptor entries to build (the MEM block count x4). The loop continues until all memory buffer descriptors have been written.

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.

530F
LD DE,(5295H) ED 5B 95 52
Load Register Pair DE with the value stored at 5295H (the DISK parameter value - the number of 1K disk blocks requested). If zero (DISK= not specified), no disk descriptors are built.
5313
LD A,D 7A
Load Register A with Register D (the high byte of the DISK parameter value).
5314
OR A,E B3
Bitwise OR Register A with Register E (the low byte of the DISK value). If both are zero (no DISK= given), the Z FLAG is set; otherwise the NZ FLAG is set.
5315
If the Z FLAG has been set (DISK parameter is zero), JUMP to 5334H to skip disk descriptor building and proceed directly to the ring terminator and pointer initialization.

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.

5317
SLA E CB 23
Shift Left Arithmetic Register E (first shift, multiply DE low byte by 2).
5319
RL D CB 12
Rotate Left Register D through carry (propagate carry from SLA E).
531B
SLA E CB 23
Shift Left Arithmetic Register E (second shift, total x4). Together with the RL D that follows, Register Pair DE now holds the total number of disk sectors (each 1K block = 4 sectors of 256 bytes).
531D
RL D CB 12
Rotate Left Register D through carry (second propagation). Register Pair DE = total disk sector count = number of disk descriptors to build.
531F
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H. This initializes the disk sector counter to 0. BC increments with each disk descriptor built, forming the sequential sector number stored in each descriptor's byte 2/3 fields.
5322
LD (HL),00H 36 00
Loop Start
Store 00H to the memory location at HL (disk descriptor byte 0: initial fill position, same as memory descriptors).
5324
INC HL 23
INCrement Register Pair HL by 1 to advance to disk descriptor byte 1.
5325
LD (HL),00H 36 00
Store 00H to the memory location at HL (disk descriptor byte 1: reserved/zero).
5327
INC HL 23
INCrement Register Pair HL by 1 to advance to disk descriptor byte 2.
5328
LD (HL),C 71
Store Register C (the low byte of the current sector number in BC) to the memory location at HL (disk descriptor byte 2: sector number low byte).
5329
INC HL 23
INCrement Register Pair HL by 1 to advance to disk descriptor byte 3.
532A
LD (HL),B 70
Store Register B (the high byte of the sector number) to the memory location at HL (disk descriptor byte 3: sector number high byte, before bit 7 is set).
532B
SET 7,(HL) CB FE
Set bit 7 of the byte at the memory location pointed to by HL (disk descriptor byte 3). Bit 7 set is the disk-buffer flag: it distinguishes this descriptor as a disk-backed sector rather than a RAM buffer page (RAM descriptors have byte 3 = 00H with bit 7 clear).
532D
INC HL 23
INCrement Register Pair HL by 1 to advance past the completed disk descriptor to the next slot.
532E
INC BC 03
INCrement Register Pair BC by 1 to advance to the next sector number for the following disk descriptor.
532F
DEC DE 1B
DECrement Register Pair DE by 1 to reduce the remaining disk descriptor count.
5330
LD A,D 7A
Load Register A with Register D (high byte of remaining disk descriptor count).
5331
OR A,E B3
Bitwise OR Register A with Register E (low byte of remaining count). If DE is now zero, the Z FLAG is set (all disk descriptors built); otherwise the NZ FLAG is set.
5332
If the NZ FLAG has been set (more disk descriptors remain to be built), LOOP BACK to 5322H to write the next disk descriptor.

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.

5334
LD (HL),00H 36 00
Store 00H to the memory location at HL (first byte of the ring terminator sentinel). The ring terminator is 4 bytes: 00H 00H FFH FFH.
5336
INC HL 23
INCrement Register Pair HL by 1.
5337
LD (HL),00H 36 00
Store 00H to the memory location at HL (second byte of terminator).
5339
INC HL 23
INCrement Register Pair HL by 1.
533A
LD (HL),0FFH 36 FF
Store FFH to the memory location at HL (third byte of terminator = FFH, the end-of-ring marker byte tested by the symbient's ring-walk logic).
533C
INC HL 23
INCrement Register Pair HL by 1.
533D
LD (HL),0FFH 36 FF
Store FFH to the memory location at HL (fourth byte of terminator). The FFH FFH pattern in bytes 2-3 signals the end of the ring to the symbient's wrap-around detection logic.
533F
POP HL E1
Restore Register Pair HL from the stack - this recovers the ring descriptor table base address (saved at 5300H). HL now points to the very first byte of the ring descriptor table.
5340
PUSH HL E5
Save Register Pair HL (the ring base address) back onto the stack again. It will be needed once more after the pointer field initialization below.
5341
LD (564EH),HL 22 4E 56
Self-Modifying Code
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.
5344
LD (5650H),HL 22 50 56
Self-Modifying Code
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.
5347
LD (5652H),HL 22 52 56
Self-Modifying Code
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.
534A
LD A,H 7C
Load Register A with Register H (the high byte of the ring base address). This is also the page number of the first memory buffer in the ring.
534B
LD (HL),L 75
Store Register L (the low byte of the ring base address, which is 00H since the address is page-aligned) to the memory location at HL (the very first byte of the ring descriptor table). This writes the initial "current buffer low byte = 00H" into descriptor byte 0.
534C
INC HL 23
INCrement Register Pair HL by 1 to advance to descriptor byte 1.
534D
LD (HL),A 77
Store Register A (the high byte / page number of the ring base) to the memory location at HL (descriptor byte 1). Together with the byte just written at byte 0, this records the full address of the first memory buffer in the ring's first descriptor entry.
534E
POP HL E1
Restore Register Pair HL (the ring base address) from the stack. HL is now ready for the LDIR operation that copies the symbient code template.

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.

534F
LD BC,002CH 01 2C 00
Load Register Pair BC with 002CH (44 decimal). This is the byte count for the first LDIR copy - 44 bytes of the FCB parameter block template that precedes the symbient entry point are copied first.
5352
XOR A,A AF
Set Register A to ZERO and clear all flags, including the CARRY FLAG, in preparation for the SBC HL,BC subtraction.
5353
SBC HL,BC ED 42
SUBtract Register Pair BC (002CH = 44 bytes) from Register Pair HL (the ring base address). Register Pair HL now points to the runtime destination for the first block of copied symbient code, positioned 44 bytes below the ring descriptor table in high memory.
5355
PUSH HL E5
Save Register Pair HL (the destination address for the LDIR copy) onto the stack for use after the copy.
5356
EX DE,HL EB
Exchange Register Pairs DE and HL. DE now points to the runtime destination in high memory; HL will be loaded next with the source address.
5357
LD HL,562AH 21 2A 56
Point Register Pair HL to 562AH, the source address of the FCB parameter block template within the overlay. This 44-byte block contains initial values for the symbient's FCB structure and runtime state variables.
535A
LDIR ED B0
Block Move: copy BC (002CH = 44) bytes from HL (562AH, FCB template in overlay) to DE (destination in high memory below the ring), incrementing both HL and DE after each byte. This relocates the symbient FCB data to its runtime location.
535C
POP HL E1
Restore Register Pair HL (the high-memory destination base, one byte below the ring) from the stack.
535D
PUSH HL E5
Save Register Pair HL again for repeated use in the patch-address sequence that follows.

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.

535E
LD (54DEH),HL 22 DE 54
Self-Modifying Code
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.
5361
LD (558AH),HL 22 8A 55
Self-Modifying Code
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.
5364
LD (55CAH),HL 22 CA 55
Self-Modifying Code
Store Register Pair HL into 55CAH-55CBH. This patches a third LD IX,0000H stub in the symbient ISR dequeue routine.
5367
LD BC,0023H 01 23 00
Load Register Pair BC with 0023H (35 decimal). This is an offset added to HL to compute the address of a specific field within the copied FCB block.
536A
ADD HL,BC 09
ADD Register Pair BC (0023H) to Register Pair HL (the FCB base). Register Pair HL now points to offset +23H within the copied FCB block, which holds the "disk sector count available for output" field.
536B
LD (55BEH),HL 22 BE 55
Self-Modifying Code
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.
536E
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H. This resets HL to zero in preparation for the next address computation, which reads a pointer from the symbient FCB data that was just copied.
5371
INC HL 23
INCrement Register Pair HL by 1 (HL = 0001H). The code is building an offset into the newly-copied FCB block by stepping through known field positions.
5372
LD C,(HL) 4E
Fetch the byte at address 0001H (which in context is reading from the just-patched FCB copy region - HL was set to 0000H and incremented, so this reads a byte at 0001H within the FCB) into Register C. This byte is the low byte of the disk write routine's jump target.
5373
INC HL 23
INCrement Register Pair HL by 1.
5374
LD B,(HL) 46
Fetch the byte at address 0002H into Register B. This is the high byte of the disk write routine's target address. Register Pair BC now holds the 16-bit address of the disk write entry point within the relocated symbient code.
5375
LD (5583H),BC ED 43 83 55
Self-Modifying Code
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.
5379
LD (55A9H),BC ED 43 A9 55
Self-Modifying Code
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.
537D
LD A,(4300H) 3A 00 43
Fetch the byte stored at 4300H (the first byte of the patchable JP slot in the resident DOS - the drive-0 handler opcode, normally C9H = RET at cold start). This is read to capture the currently-installed opcode before it is overwritten, so it can be embedded in the symbient's initialization chain.
5380
LD HL,(4301H) 2A 01 43
Load Register Pair HL with the 16-bit value stored at 4301H-4302H (the operand bytes of the JP instruction at 4300H - the current jump target, or 0000H if not yet patched). This reads the existing hook target address.
5383
LD (55B9H),A 32 B9 55
Self-Modifying Code
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.
5386
LD (55BAH),HL 22 BA 55
Self-Modifying Code
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.
5389
POP HL E1
Restore Register Pair HL (the high-memory copy base address) from the stack.
538A
PUSH HL E5
Save Register Pair HL again for the subsequent offset computations.
538B
LD BC,0157H 01 57 01
Load Register Pair BC with 0157H (343 decimal). This is the total size in bytes of the main symbient code block (from 54D3H to the end at 562AH). This count is used to compute how far below the ring base the symbient code was copied.
538E
XOR A,A AF
Set Register A to ZERO and clear all flags, clearing the CARRY FLAG for the SBC that follows.
538F
SBC HL,BC ED 42
SUBtract Register Pair BC (0157H = 343 bytes) from Register Pair HL (the FCB copy base). Register Pair HL now points to the runtime destination base of the main symbient code block.
5391
DEC HL 2B
DECrement Register Pair HL by 1 to fine-adjust the symbient code base address by one byte.
5392
LD (4049H),HL 22 49 40
Store Register Pair HL (the symbient runtime base address) into 4049H-404AH (the RAM top pointer in the resident DOS). This lowers the high-memory limit to protect the entire symbient workspace - the ring buffers, descriptor table, FCB block, and symbient code itself - from being overwritten by user programs.
5395
INC HL 23
INCrement Register Pair HL by 1 to recover the exact symbient runtime base for subsequent offset computations.
5396
PUSH HL E5
Save Register Pair HL (the symbient runtime base) onto the stack for repeated use during address patching.
5397
LD DE,0009H 11 09 00
Load Register Pair DE with 0009H. This is the byte offset from the symbient runtime base to the disk-write stub's CALL target field, used to compute the address of the first CALL 0000H stub to be patched.
539A
ADD HL,DE 19
ADD Register Pair DE (0009H) to Register Pair HL (symbient base). Register Pair HL now points to the CALL target field 9 bytes into the symbient code (the address field of the first CALL 0000H stub).
539B
LD (54D8H),HL 22 D8 54
Self-Modifying Code
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.
539E
POP HL E1
Restore Register Pair HL (symbient runtime base) from the stack.
539F
PUSH HL E5
Save Register Pair HL (symbient runtime base) again for the next patch.
53A0
LD DE,00E9H 11 E9 00
Load Register Pair DE with 00E9H (233 decimal). This is the byte offset to another CALL 0000H stub within the symbient code that needs patching.
53A3
ADD HL,DE 19
ADD Register Pair DE (00E9H) to Register Pair HL. Register Pair HL now points to the address field of the CALL stub at offset 233 within the symbient code.
53A4
LD (5514H),HL 22 14 55
Self-Modifying Code
Store Register Pair HL into 5514H-5515H. This patches the CALL 0000H stub in the symbient disk-read refill path.
53A7
LD (55B7H),HL 22 B7 55
Self-Modifying Code
Store Register Pair HL into 55B7H-55B8H. This patches another CALL 0000H stub at a second location referencing the same disk-read entry point.
53AA
POP HL E1
Restore Register Pair HL (symbient runtime base) from the stack.
53AB
PUSH HL E5
Save Register Pair HL (symbient runtime base) once more.
53AC
LD DE,00A7H 11 A7 00
Load Register Pair DE with 00A7H (167 decimal). Offset to another stub patch target within the symbient code.
53AF
ADD HL,DE 19
ADD Register Pair DE (00A7H) to Register Pair HL. Register Pair HL now points to the address field of the CALL stub at offset 167 in the symbient.
53B0
LD (54EEH),HL 22 EE 54
Self-Modifying Code
Store Register Pair HL into 54EEH-54EFH. This patches a CALL 0000H stub in the symbient buffer-full flush path.
53B3
LD (5510H),HL 22 10 55
Self-Modifying Code
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.
53B6
LD (5578H),HL 22 78 55
Self-Modifying 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.
53B9
POP DE D1
Restore Register Pair DE (the symbient runtime base) from the stack. DE is now loaded as the LDIR destination for copying the main symbient code body.
53BA
PUSH DE D5
Save Register Pair DE (symbient code runtime destination) back onto the stack for use after the LDIR as the symbient entry point address.
53BB
LD HL,54D3H 21 D3 54
Point Register Pair HL to 54D3H, the start of the main symbient code template within the overlay. This is the source for the LDIR copy.
53BE
LDIR ED B0
Block Move: copy BC bytes (the symbient code size, 0157H = 343 bytes) from HL (54D3H, symbient template in overlay) to DE (runtime destination in high memory). This relocates the complete symbient interrupt handler and buffer management code to its permanent home below the VTOS high-memory limit.

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.

53BF
POP HL E1
Restore Register Pair HL (the symbient code runtime base address, saved at 53BAH) from the stack. HL now holds the entry point address of the relocated symbient code in high memory.
53C0
PUSH HL E5
Save Register Pair HL (symbient entry point) back onto the stack for one more use.
53C1
LD DE,(536FH) ED 5B 6F 53
Load Register Pair DE with the 16-bit value stored at 536FH-5370H (the device parameter block pointer saved during the chain walk at 527BH). This is the address of the terminal entry in the drive config table for the spooled device.
53C5
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H. This zero offset will be added to HL to compute a specific field address within the symbient code block for the device pointer patch.
53C8
ADD HL,BC 09
ADD Register Pair BC (0000H) to Register Pair HL. This is a no-op addition that preserves HL at the symbient entry point, while establishing the ADD HL,BC pattern used consistently throughout the patching sequence.
53C9
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the device parameter block pointer (from 536FH); DE now holds the symbient entry point + offset.
53CA
INC HL 23
INCrement Register Pair HL by 1 to advance to offset +01H within the device parameter block (the low byte of the device I/O handler vector - a JP instruction target stored at this offset).
53CB
LD (HL),E 73
Store Register E (the low byte of the symbient entry point address) to the memory location at HL (device param block offset +01H). This patches the low byte of the existing device I/O handler vector with the symbient's entry point.
53CC
INC HL 23
INCrement Register Pair HL by 1 to advance to offset +02H (the high byte of the handler vector).
53CD
LD (HL),D 72
Store Register D (the high byte of the symbient entry point) to the memory location at HL (device param block offset +02H). The device I/O handler vector in the drive config table now points to the symbient code in high memory.
53CE
POP HL E1
Restore Register Pair HL (symbient entry point address) from the stack.
53CF
PUSH HL E5
Save Register Pair HL (symbient entry point) once more for the 4300H hook installation.
53D0
LD DE,00A2H 11 A2 00
Load Register Pair DE with 00A2H (162 decimal). This is the byte offset from the symbient base to the output-character entry point within the symbient code, which is the primary hook target stored at 4300H.
53D3
ADD HL,DE 19
ADD Register Pair DE (00A2H) to Register Pair HL (symbient base). Register Pair HL now points to the output-character entry point within the relocated symbient code at runtime.
53D4
LD D,H 54
Load Register D with Register H (high byte of the output-character entry point). This begins loading DE with the entry point address for storage at 4300H.
53D5
LD E,L 5D
Load Register E with Register L (low byte of the output-character entry point). Register Pair DE now holds the full address of the symbient's output-character routine in high memory.
53D6
LD BC,0002H 01 02 00
Load Register Pair BC with 0002H. This is used to advance HL by 2 to point to a second entry point within the symbient for a second address to be stored.
53D9
ADD HL,BC 09
ADD Register Pair BC (0002H) to Register Pair HL. Register Pair HL now points 2 bytes past the output-character entry - to the ISR dequeue entry point used by the interrupt handler to pull characters out of the queue and send them to the device.
53DA
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the output-character address (first entry point); DE holds the ISR dequeue address (second entry point).
53DB
LD (HL),E 73
Store Register E (low byte of ISR dequeue address) to the memory location at HL (a pointer field within the symbient code that the output-character routine uses to call back into the ISR handler).
53DC
INC HL 23
INCrement Register Pair HL by 1 to the next byte of the pointer field.
53DD
LD (HL),D 72
Store Register D (high byte of ISR dequeue address) to the memory location at HL. The cross-reference between the output-character routine and the ISR dequeue routine is now fully patched.
53DE
DEC HL 2B
DECrement Register Pair HL by 1 to return HL to the output-character entry point address (the low byte of the pointer just written). HL now holds the address of the entry to be installed at 4300H.
53DF
EX DE,HL EB
Exchange Register Pairs DE and HL. DE now holds the output-character entry point address; HL now holds the ISR dequeue + 1 address (not used further - DE is what matters for the 4300H installation).
53E0
LD A,09H 3E 09
Load Register A with 09H. This is passed to the SYS0 error/IO handler at 4410H as a sub-function code, used to perform a final initialization step or status check before the hook is armed.
53E2
GOSUB to SYS0 at 4410H (Error Handler Variant) with A = 09H. This call performs the device-level initialization step required to arm the symbient hook - exactly what sub-function 09H does depends on the device driver, but it typically enables the interrupt path that will call the symbient entry point.
53E5
POP HL E1
Restore Register Pair HL (the symbient output-character entry point address) from the stack.
53E6
PUSH HL E5
Save Register Pair HL one last time; the entry point is still needed to install it at 4301H-4302H.
53E7
LD DE,00E3H 11 E3 00
Load Register Pair DE with 00E3H (227 decimal). This is a byte offset used in a final address computation before the hook installation.
53EA
ADD HL,DE 19
ADD Register Pair DE (00E3H) to Register Pair HL (symbient entry point). Register Pair HL now points 227 bytes into the symbient code, to the exact routine that should be called when the JP at 4300H is executed - the top-level symbient dispatch entry point that routes calls to the appropriate sub-handler.
53EB
LD (4301H),HL 22 01 43
Self-Modifying Code
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.
53EE
LD A,0C3H 3E C3
Load Register A with C3H (the Z80 JP opcode). This is the opcode that will be written to 4300H to arm the hook.
53F0
LD (4300H),A 32 00 43
Self-Modifying Code
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.
53F3
JUMP to 402DH (DOS READY / No-Error Exit). The SPOOL command has completed successfully. The symbient is installed and active; VTOS returns to the command prompt.

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.

53F6
LD HL,4015H 21 15 40
Point Register Pair HL to 4015H, the start of drive configuration table #1 in the resident DOS. Each entry in this table is 8 bytes; the code will scan through entries looking for a device name match.
53F9
PUSH HL E5
Loop Start
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.
53FA
LD A,L 7D
Load Register A with Register L (the low byte of the current entry address). This is used to compute the offset to field +06H.
53FB
ADD A,06H C6 06
ADD 06H to Register A. This computes the low byte of the address of field +06H (the device name bytes) within the current 8-byte table entry.
53FD
LD L,A 6F
Load Register L with Register A (the computed low byte). Register Pair HL now points to the +06H field (first device name byte) of the current table entry.
53FE
LD A,(HL) 7E
Fetch the byte at the memory location pointed to by HL (field +06H of the current entry = the first device name character stored in this entry).
53FF
INC L 2C
INCrement Register L by 1 to advance HL to field +07H (the second device name character) within the same entry.
5400
CP A,E BB
Compare Register A (first device name char from table) against Register E (low byte of DE = first byte of the target device name). If Register A equals Register E, the Z FLAG is set.
5401
If the NZ FLAG has been set (first device name character does not match), JUMP to 540AH to move to the next table entry. No need to check the second byte if the first already fails.
5403
LD A,(HL) 7E
Fetch the byte at the memory location pointed to by HL (field +07H of the current entry = the second device name character).
5404
CP A,D BA
Compare Register A (second device name char from table) against Register D (high byte of DE = second byte of the target device name). If Register A equals Register D, the Z FLAG is set.
5405
If the NZ FLAG has been set (second device name character does not match), JUMP to 540AH to advance to the next entry.

Match Found
Both device name characters match. Pop the entry base address from the stack into HL and return Z set.

5407
POP HL E1
Restore Register Pair HL (the matching entry base address) from the stack.
5408
XOR A,A AF
Set Register A to ZERO and clear all flags. Setting the Z FLAG confirms a successful match to the caller.
5409
RET C9
Return to caller with Z set (match found) and HL pointing to the matching drive config table entry.

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.

540A
POP AF F1
Restore the previous HL value from the stack into AF (discarding it - only needed to clean up the stack). The entry base address is now gone; the code will recompute the next entry address.
540B
INC L 2C
INCrement Register L by 1. Since the table entries are 8 bytes and the addresses are aligned, this begins advancing to the next entry (together with the ADD that follows).
540C
If the NZ FLAG has been set (no page-boundary carry from the INC L), JUMP to 5412H to check whether the new L value exceeds the table boundary. A carry would mean HL wrapped to the next page, which signals end-of-table.
540E
LD A,08H 3E 08
Load Register A with 08H. Page wrap occurred (carry from INC L), meaning the scan has gone past the end of the valid table address range. A = 08H will be returned as the "not found" error code.
5410
OR A,A B7
Bitwise OR Register A with itself. This clears the Z FLAG and sets flags based on A (08H), ensuring the NZ FLAG is set to signal "not found" to the caller.
5411
RET C9
Return to caller with NZ set (device not found in either table) and A = 08H (error code).
5412
LD A,L 7D
Load Register A with Register L (the low byte of the advancing scan pointer). This is tested to see if the scan pointer has stepped past the end of the current table.
5413
CP A,2DH FE 2D
Compare Register A against 2DH. In table #1 (starting at 4015H), the scan pointer's low byte of 2DH means HL has advanced to 402DH - the first address beyond the 3-entry x 8-byte table (4015H + 24 = 402DH). If Register A equals 2DH, the Z FLAG is set, indicating the end of table #1 has been reached.
5415
If the NZ FLAG has been set (not yet at the end of this table), LOOP BACK to 53F9H to check the next entry in the current table.
5417
LD HL,43C0H 21 C0 43
Point Register Pair HL to 43C0H, the start of drive configuration table #2. The scan of table #1 (4015H-402CH) is complete with no match found; the search continues in table #2 (43C0H-43D7H).
541A
LOOP BACK to 53F9H to begin scanning table #2 from its start. The same PUSH HL / compare loop will now work through the second drive config table entries.

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.

541C
OR A,40H F6 40
Bitwise OR Register A with 40H. Setting bit 6 of the error code sets the extended-context suppression flag (bit 6 of 430FH behavior as described in the memory map), which tells the SYS4 error handler to suppress the additional "REFERENCED AT..." context line. This produces a cleaner error message for SPOOL's file-level errors.
541E
JUMP to SYS0 at 4409H (DOS Error Exit) with the error code in Register A (bit 6 set). SYS4 will format and display the error message, then return VTOS to the command prompt.

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).

5421
LD HL,542AH 21 2A 54
Point Register Pair HL to 542AH, the start of the "DEVICE SPEC REQUIRED" error string (21 bytes of ASCII text terminated by 0DH).
5424
GOSUB to SYS0 at 447BH (Display Message with CONFIG/SYS Processing) to output the error string pointed to by HL to the console.
5427
JUMP to SYS0 at 4030H (Error-Already-Displayed Exit) to return to the DOS command prompt without displaying a second error message.

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.

542A-543E
DEFM "DEVICE SPEC REQUIRED" + 0DH 44 45 56 49 43 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
Error message string: "DEVICE SPEC REQUIRED" followed by 0DH (carriage return). Displayed by the error handler at 5424H when no valid device specification was given on the SPOOL command line.

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.

543F
LD HL,5448H 21 48 54
Point Register Pair HL to 5448H, the start of the "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" error string.
5442
GOSUB to SYS0 at 447BH (Display Message) to output the error string to the console.
5445
JUMP to SYS0 at 4030H (Error-Already-Displayed Exit) to return to the DOS command prompt.

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.

5448-5471
DEFM "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" + 0DH 43 41 4E 27 54 20 2D 2D 20 4F 4E 4C 59 20 56 41 4C 49 44 20 41 54 20 56 54 4F 53 20 43 4F 4D 4D 41 4E 44 20 4C 45 56 45 4C 0D
Error message string: "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" followed by 0DH. Displayed when SPOOL is invoked from a user program context where it cannot safely install the symbient.

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.

5472
LD HL,547BH 21 7B 54
Point Register Pair HL to 547BH, the start of the "PARAMETER ERROR" error string (16 bytes).
5475
GOSUB to SYS0 at 447BH (Display Message) to output the "PARAMETER ERROR" string to the console.
5478
JUMP to SYS0 at 4030H (Error-Already-Displayed Exit) to return to the DOS command prompt.

Data: "PARAMETER ERROR" + 0DH
The 16 bytes at 547BH-548AH are ASCII text data decoded by the disassembler as Z80 mnemonics.

547B-548A
DEFM "PARAMETER ERROR" + 0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
Error message string: "PARAMETER ERROR" followed by 0DH. Displayed when the MEM= or DISK= parameter value is out of range or when the spool filespec is invalid.

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.

548B-54AAH
DEFM (Device Filespec Work Area, 32 bytes) 58 58 2F 53 50 4C 03 ...
Device filespec work area. The 441CH routine writes the device specification string here. Byte at 548BH receives the separator; the device name characters start at 548CH. The first 7 bytes are pre-initialized to the default filename template "XX/SPL" + 03H terminator - the "XX" at 548BH-548CH are overwritten by the device name pointer stored at 5223H.
54AB-54B1H
DEFM "XX/SPL" + 03H 58 58 2F 53 50 4C 03
Default spool filename template. The two "X" bytes at 54ABH-54ACH are overwritten at 5223H with the address of the actual device name characters (2 bytes). The "/SPL" portion and 03H terminator remain as the filename suffix. When built into a complete filespec, this produces a name such as :1/SPLx where x is the device character.
54AE-54B1H
DEFM "SPL" + 03H 53 50 4C 03
Default extension string "SPL" + 03H terminator. This sub-field within the template is passed as HL to the 4473H (Insert Default Extension) routine at 5238H to append the extension when no extension was given in the filespec.
54B2-54D2H
DEFB (FCB Work Area, 33 bytes) 4D 45 4D 20 20 20 20 C9 ...
FCB (File Control Block) work area for the spool disk file. This 33-byte region is passed to CALL 4476H (Open File Utility) at 5260H. VTOS populates this area during the open operation. The initial bytes visible in the disassembly are artefacts of the overlay's binary content at this location before the FCB is initialized by the open call.

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.

54D3
LD A,C 79
Load Register A with Register C (the character being queued for output). Register C holds the byte to be stored into the spool buffer.
54D4
OR A,A B7
Bitwise OR Register A with itself to test whether the character is zero. If A = 00H, the Z FLAG is set (null character - skip output); otherwise the NZ FLAG is set.
54D5
RET Z C8
If the Z FLAG has been set (character is 00H = null), Return immediately without queuing. Null characters are filtered from the spool stream.
54D6
PUSH AF F5
Save Register Pair AF (the character in A and the flags) onto the stack before the call.
54D7
CALL 0000H CD 00 00
Self-Modifying Code
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.
54DA
POP AF F1
Restore Register Pair AF (recovering the original character in A) from the stack.
54DB
RET C9
Return to the caller of this conditional-call helper. The character has been queued (or the queue was full and flushed to disk) and control returns to whoever requested the character output.

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.

54DC
LD IX,0000H DD 21 00 00
Self-Modifying Code
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.
54E0
LD L,(IX+20H) DD 6E 20
Load Register L with the byte at IX+20H (the current byte offset within the active memory buffer - the write position within the 256-byte page currently receiving characters).
54E3
LD H,(IX+21H) DD 66 21
Load Register H with the byte at IX+21H (the high byte / page number of the active memory buffer address). Register Pair HL now holds the full address of the current write position within the memory ring buffer.
54E6
LD (HL),C 71
Store Register C (the output character to be queued) to the memory location at HL (the current write position in the memory ring buffer). The character is now stored.
54E7
DI F3
Disable Interrupts. The buffer write-position pointer at IX+20H is about to be updated. Disabling interrupts prevents the ISR dequeue routine (which reads this same pointer) from running between the store and the pointer update, which would corrupt the buffer state.
54E8
INC (IX+20H) DD 34 20
INCrement the byte at IX+20H (the write-position low byte) by 1. This advances the write pointer to the next position in the 256-byte memory buffer page. Because the low byte wraps from FFH to 00H on overflow, this naturally handles the page boundary.
54EB
If the Z FLAG has been set (IX+20H wrapped from FFH to 00H, meaning the 256-byte buffer page is now full), JUMP to 54F3H to flush the full buffer to disk and advance the ring to the next slot. Otherwise continue with the simple path.
54ED
CALL 0000H CD 00 00
Self-Modifying Code
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).
54F0
EI FB
Enable Interrupts. The critical section protecting the write-pointer update is complete. Interrupts are re-enabled to allow the ISR to resume normal operation.
54F1
JUMP to 553DH. After a normal (non-full-buffer) character store, execution jumps to 553DH which contains the path that returns to the caller. The character has been successfully queued.

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).

54F3
LD L,(IX+24H) DD 6E 24
Load Register L with the byte at IX+24H (the low byte of the current ring descriptor pointer - the address of the current 4-byte descriptor entry in the ring table). This begins reading the descriptor for the next ring slot.
54F6
LD H,(IX+25H) DD 66 25
Load Register H with the byte at IX+25H (the high byte of the current ring descriptor pointer). Register Pair HL now points to the current ring descriptor entry in high memory.
54F9
LD A,(HL) 7E
Loop Start
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.
54FA
INC HL 23
INCrement Register Pair HL by 1 to advance to descriptor byte 1.
54FB
OR A,(HL) B6
Bitwise OR Register A (byte 0) with the byte at (HL) (descriptor byte 1). If both are zero, the combined value is 00H and the Z FLAG is set, indicating this ring slot is empty/available. If either is non-zero, NZ = slot is busy or contains data.
54FC
If the Z FLAG has been set (bytes 0 and 1 are both zero = slot is available), JUMP to 5503H to claim this slot for the next buffer fill cycle.
54FE
INC HL 23
INCrement Register Pair HL by 1 to advance to descriptor byte 2.
54FF
INC HL 23
INCrement Register Pair HL by 1 to advance to descriptor byte 3 (the flag/sector-high byte).
5500
INC HL 23
INCrement Register Pair HL by 1 to advance past the 4-byte descriptor to the next descriptor entry.
5501
LOOP BACK to 54F9H to test the next descriptor entry. The loop walks forward through the ring until it finds a free slot or wraps around.

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.

5503
DEC HL 2B
DECrement Register Pair HL by 1. Since HL pointed at byte 1 when the JR Z was taken, this moves it back to byte 0 (the descriptor base).
5504
PUSH HL E5
Save Register Pair HL (the free descriptor entry base address) onto the stack for later pointer updates.
5505
INC HL 23
INCrement Register Pair HL by 1 to descriptor byte 1.
5506
INC HL 23
INCrement Register Pair HL by 1 to descriptor byte 2 (the page number / sector low byte).
5507
LD B,(HL) 46
Fetch the byte at HL (descriptor byte 2 = page number for memory buffer, or low byte of sector number for disk buffer) into Register B.
5508
INC HL 23
INCrement Register Pair HL by 1 to descriptor byte 3 (the flag byte: bit 7 = 0 for memory, bit 7 = 1 for disk).
5509
LD A,(HL) 7E
Fetch the byte at HL (descriptor byte 3 = flag byte) into Register A.
550A
CP A,0FFH FE FF
Compare Register A against FFH. The end-of-ring sentinel (written at 533AH-533DH) uses FFH as the flag byte in descriptor byte 3. If Register A equals FFH, the Z FLAG is set, indicating the scan has wrapped past the end of the ring.
550C
If the NZ FLAG has been set (not the end-of-ring sentinel = this is a real descriptor), JUMP to 5519H to process this slot (either memory or disk buffer).

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.

550E
POP HL E1
Restore Register Pair HL (the sentinel descriptor address) from the stack, discarding it since the sentinel is not a real buffer slot.
550F
CALL 0000H CD 00 00
Self-Modifying Code
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.
5512
EI FB
Enable Interrupts briefly between retry attempts to allow the ISR to run and potentially free ring slots.
5513
CALL 0000H CD 00 00
Self-Modifying Code
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.
5516
DI F3
Disable Interrupts again before resuming the ring scan, to protect the pointer update critical section.
5517
LOOP BACK to 54F3H to restart the ring descriptor scan from the beginning after the wrap-around. The retry will now search from the ring start for the next free slot.

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).

5519
BIT 7,A CB 7F
Test bit 7 of Register A (descriptor byte 3). Bit 7 set = disk buffer descriptor; bit 7 clear = memory buffer descriptor. Sets the Z FLAG if bit 7 is clear (memory), NZ if bit 7 is set (disk).
551B
If the NZ FLAG has been set (bit 7 = 1 = disk buffer descriptor), JUMP to 553FH to flush the memory buffer to this disk sector before proceeding. The full 256-byte memory buffer must be written to the spool file at the sector identified by this descriptor.

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.

551D
POP HL E1
Restore Register Pair HL (the free memory descriptor base address) from the stack.
551E
PUSH HL E5
Save Register Pair HL back (keeping a copy for the final pointer updates).
551F
LD (HL),L 75
Store Register L (the low byte of the descriptor address, which is 00H since descriptors are page-aligned) to (HL) (descriptor byte 0 = initial write position within the new buffer page = 0).
5520
LD A,H 7C
Load Register A with Register H (the high byte of the descriptor address = the page number of this descriptor block).
5521
INC HL 23
INCrement Register Pair HL by 1 to descriptor byte 1.
5522
LD (HL),A 77
Store Register A (the descriptor page number) to (HL) (descriptor byte 1). Together with byte 0, this records the current position in the new memory buffer in the descriptor header.
5523
LD (IX+21H),B DD 70 21
Store Register B (the page number of the new memory buffer page, from descriptor byte 2) into IX+21H (the symbient FCB's active buffer page number). The output-character routine will now write characters to the new 256-byte buffer page identified by B.
5526
LD L,(IX+26H) DD 6E 26
Load Register L with the byte at IX+26H (the low byte of the ring write pointer - the address of the current write-position descriptor in the ring table).
5529
LD H,(IX+27H) DD 66 27
Load Register H with the byte at IX+27H (the high byte of the ring write pointer). Register Pair HL now holds the address of the descriptor entry for the buffer that was just completed (the one that just filled up).
552C
LD (IX+2AH),L DD 75 2A
Store Register L (the low byte of the old write pointer) into IX+2AH. This records the old write pointer in the "last completed" pointer field so the ISR dequeue routine knows which descriptor was most recently filled.
552F
LD (IX+2BH),H DD 74 2B
Store Register H (the high byte of the old write pointer) into IX+2BH. Register Pair IX+2AH/2BH now holds the full address of the just-completed ring descriptor.
5532
POP BC C1
Restore Register Pair BC from the stack (recovering the new descriptor address that was PUSHed at 551EH). BC now holds the new descriptor base address.
5533
LD (HL),C 71
Store Register C (the low byte of the new descriptor address) to the memory location at HL (the just-completed descriptor's byte 0). This links the completed descriptor to the new one by writing the new descriptor's address into the old descriptor's pointer field, forming the chain.
5534
INC HL 23
INCrement Register Pair HL by 1 to the old descriptor's byte 1.
5535
LD (HL),B 70
Store Register B (the high byte of the new descriptor address) to (HL) (old descriptor byte 1). The chain link is complete - the completed descriptor now points to the next descriptor that will receive output.
5536
LD (IX+26H),C DD 71 26
Store Register C (low byte of new descriptor address) into IX+26H. This updates the symbient FCB's ring write pointer (low byte) to the new descriptor, so the next character write will go to the new memory buffer.
5539
LD (IX+27H),B DD 70 27
Store Register B (high byte of new descriptor address) into IX+27H (ring write pointer high byte). The ring write pointer is now fully updated to the new slot.
553C
RET C9
Return to the caller. The ring has been advanced to the new memory buffer slot; the write pointer is updated; output can continue.
553D
JUMP to 55BCH (the ISR dequeue routine entry point). This path is taken after a normal (non-buffer-full) character store to check if the ISR needs to process any pending output from the ring - delivering queued characters to the device.

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.

553F
EI FB
Enable Interrupts. Before performing the disk write (which is a longer operation), interrupts are re-enabled to prevent the ISR from being blocked for an excessive duration.
5540
LD C,B 48
Load Register C with Register B (the sector number low byte from descriptor byte 2, which was loaded earlier at 5507H). Register C will be used as the seek position low byte.
5541
AND A,0FH E6 0F
Bitwise AND Register A with 0FH. Register A holds descriptor byte 3 (the flag + sector-number-high nibble). Masking with 0FH strips the bit-7 disk flag and any other upper bits, leaving only the sector number high nibble in bits 3-0.
5543
LD B,A 47
Load Register B with Register A (the sector number high nibble). Register Pair BC now holds the full disk sector number for this ring slot (C = low byte from descriptor byte 2, B = high nibble from descriptor byte 3 after masking).
5544
PUSH IX DD E5
Save Index Register IX (the symbient FCB base address) onto the stack. IX will be changed to point to the system FCB for the file I/O operations that follow.
5546
POP DE D1
Restore the top of stack (which is IX) into Register Pair DE. Register Pair DE now holds the symbient FCB base address, which is used as the FCB pointer for the file positioning call.
5547
GOSUB to SYS0 at 4442H (Position to Record) with BC = sector number and DE = FCB pointer. This seeks the spool file to the target sector position so the write will land in the correct slot of the spool file's disk extent.
554A
If the NZ FLAG has been set (seek failed), JUMP to SYS0 at 4409H (DOS Error Exit) with the error code from the failed seek. A position error here is a fatal I/O error for the symbient.
554D
LD C,(IX+26H) DD 4E 26
Load Register C with the byte at IX+26H (the low byte of the ring write pointer - the address of the current write descriptor). This recovers the write descriptor address for updating after the disk write.
5550
LD B,(IX+27H) DD 46 27
Load Register B with the byte at IX+27H (the high byte of the ring write pointer). Register Pair BC now holds the address of the current write descriptor in the ring.
5553
POP HL E1
Restore Register Pair HL (the disk descriptor base address, saved at 5504H) from the stack.
5554
PUSH HL E5
Save Register Pair HL (disk descriptor address) back onto the stack to preserve it through the write operation.
5555
LD (HL),C 71
Store Register C (the low byte of the write descriptor address) to (HL) (disk descriptor byte 0). This records the current write pointer in the disk descriptor's byte 0 field, marking this descriptor as pending-write.
5556
INC HL 23
INCrement Register Pair HL by 1 to disk descriptor byte 1.
5557
LD (HL),B 70
Store Register B (the high byte of the write descriptor address) to (HL) (disk descriptor byte 1). The disk descriptor now encodes the back-link to the write descriptor.
5558
POP BC C1
Restore Register Pair BC (the disk descriptor address) from the stack. BC now holds the disk descriptor base address.
5559
LD L,(IX+2AH) DD 6E 2A
Load Register L with the byte at IX+2AH (the low byte of the "last completed" descriptor pointer set at 552CH). This is the address of the ring descriptor that was most recently filled and should be linked to the new disk descriptor.
555C
LD H,(IX+2BH) DD 66 2B
Load Register H with the byte at IX+2BH (the high byte of the last-completed pointer). Register Pair HL now holds the address of the just-completed memory descriptor that needs to point to the disk descriptor.
555F
LD (HL),C 71
Store Register C (the low byte of the disk descriptor address) to (HL) (the just-completed memory descriptor's byte 0). This links the completed memory descriptor forward to the disk descriptor.
5560
INC HL 23
INCrement Register Pair HL by 1 to the memory descriptor's byte 1.
5561
LD (HL),B 70
Store Register B (the high byte of the disk descriptor address) to (HL) (memory descriptor byte 1). The chain is now: completed-memory-descriptor -> disk-descriptor.
5562
LD (IX+2AH),C DD 71 2A
Store Register C (disk descriptor low byte) into IX+2AH (updating the last-completed pointer low byte to the disk descriptor). The last-completed pointer now refers to the disk descriptor.
5565
LD (IX+2BH),B DD 70 2B
Store Register B (disk descriptor high byte) into IX+2BH (updating the last-completed pointer high byte). The disk descriptor is now the current "active" descriptor.
5568
LD B,(IX+21H) DD 46 21
Load Register B with the byte at IX+21H (the page number of the active memory buffer - the 256-byte page that contains the data to be written to disk). Register B = the high byte of the source buffer address for the disk write.
556B
LD (IX+04H),B DD 70 04
Store Register B (the buffer page number) into IX+04H (the symbient FCB field that holds the buffer address high byte for the write-record call). This tells the write routine where in memory the 256 bytes to be written are located.
556E
GOSUB to SYS0 at 4439H (Write Record) to write the 256-byte memory buffer (at the page identified in IX+04H) to the current position in the spool file. On return, Z set = write succeeded, NZ = error.
5571
If the NZ FLAG has been set (write failed - disk full or I/O error), JUMP to SYS0 at 4409H (DOS Error Exit). A write failure in the symbient is fatal - the spool cannot continue if the disk cannot be written.
5574
RET C9
Return to the caller. The 256-byte memory buffer has been successfully flushed to the disk sector identified by the ring descriptor. The ring advance can now proceed in the calling routine (the buffer-full path at 54F3H).

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.

5577
CALL 0000H CD 00 00
Self-Modifying Code
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.
557A
LD C,00H 0E 00
Load Register C with 00H. This initializes the function code or parameter register for the system FCB call that follows.
557C
PUSH IX DD E5
Save Index Register IX (the symbient FCB base address) onto the stack. IX must be temporarily changed to the system FCB to perform the file I/O call.
557E
LD IX,4025H DD 21 25 40
Load Index Register IX with 4025H (the system FCB area in the resident DOS). This switches IX to the system-level FCB so that the following file operations use the system context.
5582
CALL 0000H CD 00 00
Self-Modifying Code
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.
5585
POP IX DD E1
Restore Index Register IX (the symbient FCB base address) from the stack. IX is returned to the symbient context after the system FCB call.
5587
RET NZ C0
If the NZ FLAG has been set (disk read failed or no data available), Return immediately to propagate the error/no-data condition to the caller.
5588
LD IX,0000H DD 21 00 00
Self-Modifying Code
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.
558C
LD A,(IX+23H) DD 7E 23
Fetch the byte at IX+23H (the number of disk sectors remaining in the current spool file read sequence). This count tracks how many more sectors are queued for delivery to the output device.
558F
OR A,A B7
Bitwise OR Register A with itself to test whether the remaining sector count is zero. Z set = no sectors remaining (spool is empty or finished); NZ = sectors still to deliver.
5590
RET Z C8
If the Z FLAG has been set (no sectors remaining = spool queue is empty for this session), Return to signal completion to the caller.
5591
LD H,A 67
Load Register H with Register A (the remaining sector count). H holds the count for use in the comparison that follows.
5592
CP A,(IX+21H) DD BE 21
Compare Register A (remaining count at IX+23H) against the byte at IX+21H (the active buffer page number / current buffer fill level). This tests whether the number of remaining sectors equals the fill level - a condition that indicates the current buffer page is at its output limit.
5595
If the NZ FLAG has been set (remaining count does not equal fill level - not at the boundary), JUMP to 559EH to process the next output byte from the current buffer page.
5597
LD A,(IX+22H) DD 7E 22
Fetch the byte at IX+22H (a secondary count or offset field used for boundary detection within the current buffer). This is compared against the active buffer position.
559A
CP A,(IX+20H) DD BE 20
Compare Register A (IX+22H) against the byte at IX+20H (the current write offset within the active buffer page). If equal, the Z FLAG is set, indicating the read position has caught up to the write position - the buffer is empty.
559D
RET Z C8
If the Z FLAG has been set (read = write position = buffer is empty), Return Z to signal "no data available" to the ISR. The ISR will not output any character this cycle.
559E
LD L,(IX+22H) DD 6E 22
Load Register L with the byte at IX+22H (the current output byte offset within the buffer). Register L is the low byte of the source address for the next character to be output to the device.
55A1
LD C,(HL) 4E
Fetch the byte at the memory location pointed to by HL (the next character in the spool buffer, at the current output offset within the active buffer page) into Register C. Register C now holds the next character to deliver to the output device.
55A2
PUSH IX DD E5
Save Index Register IX (symbient FCB address) onto the stack before switching to the system FCB for output.
55A4
LD IX,4025H DD 21 25 40
Load Index Register IX with 4025H (the system FCB) to switch context for the output call.
55A8
CALL 0000H CD 00 00
Self-Modifying Code
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.
55AB
POP IX DD E1
Restore Index Register IX (symbient FCB address) from the stack.
55AD
INC (IX+22H) DD 34 22
INCrement the byte at IX+22H (the output offset within the current buffer page) by 1. This advances the read position past the character just delivered.
55B0
RET NZ C0
If the NZ FLAG has been set (IX+22H did not wrap to 00H - the buffer page is not yet exhausted), Return to the caller. One character was delivered; the buffer still has more data.
55B1
LD (IX+23H),00H DD 36 23 00
Store 00H into IX+23H (the remaining sector count). Setting this to zero marks the current disk-read phase as complete; no more sectors are expected from this batch. The symbient will need to re-check the ring for new data on the next ISR cycle.
55B5
RET C9
Return to the caller. The current buffer page has been fully consumed (read position wrapped to 00H). The remaining-sector count has been cleared to trigger a new disk-read sequence on the next ISR call.
55B6
CALL 0000H CD 00 00
Self-Modifying Code
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.

55BC
PUSH AF F5
Save Register Pair AF (preserving whatever the ISR had in A and the flags) onto the stack. The dequeue routine must not disturb the ISR's register state.
55BD
LD A,(0000H) 3A 00 00
Self-Modifying Code
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.
55C0
OR A,A B7
Bitwise OR Register A with itself to test whether the data-available count is zero. Z set = no data; NZ = data waiting to be output.
55C1
If the NZ FLAG has been set (data is available in the ring), JUMP to 55F4H to restore AF and return to the ISR with data ready. If no data is available, the ISR will skip output for this interrupt cycle.

Data is available. The ISR dequeue logic saves all registers, loads the symbient FCB pointer, and begins reading from the ring.

55C3
PUSH IX DD E5
Save Index Register IX onto the stack (preserving whatever the ISR was using it for).
55C5
PUSH HL E5
Save Register Pair HL onto the stack.
55C6
PUSH DE D5
Save Register Pair DE onto the stack.
55C7
PUSH BC C5
Save Register Pair BC onto the stack. All registers are now preserved; the dequeue routine can use them freely.
55C8
LD IX,0000H DD 21 00 00
Self-Modifying Code
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.
55CC
LD L,(IX+28H) DD 6E 28
Load Register L with the byte at IX+28H (the low byte of the ring read pointer - the address of the descriptor entry from which the ISR is currently reading output data).
55CF
LD H,(IX+29H) DD 66 29
Load Register H with the byte at IX+29H (the high byte of the ring read pointer). Register Pair HL now points to the current ring read descriptor entry.
55D2
PUSH HL E5
Save Register Pair HL (the ring read descriptor address) onto the stack.
55D3
LD C,(HL) 4E
Fetch the byte at HL (read descriptor byte 0 = the current read offset within this descriptor's buffer, or the low byte of the next descriptor address if already linked forward) into Register C.
55D4
INC HL 23
INCrement Register Pair HL by 1 to read descriptor byte 1.
55D5
LD B,(HL) 46
Fetch the byte at HL (read descriptor byte 1) into Register B. Register Pair BC now holds the two-byte link value from the read descriptor (either a back-link address or a buffer position indicator).
55D6
LD HL,0002H 21 02 00
Load Register Pair HL with 0002H. This is the offset to descriptor byte 2 within a 4-byte descriptor (relative to byte 0). HL will be advanced by adding BC to reach the actual data fields.
55D9
ADD HL,BC 09
ADD Register Pair BC (the descriptor link address from bytes 0-1) to Register Pair HL (0002H). This computes the address of descriptor byte 2 (the page/sector number) within the descriptor identified by the link value in BC.
55DA
LD A,(HL) 7E
Fetch the byte at HL (descriptor byte 2 = memory page number or disk sector low byte) into Register A.
55DB
INC HL 23
INCrement Register Pair HL by 1 to descriptor byte 3 (the flag byte).
55DC
BIT 7,(HL) CB 7E
Test bit 7 of the byte at the memory location pointed to by HL (descriptor byte 3). Bit 7 set = disk buffer sector; bit 7 clear = memory buffer page. Sets Z if bit 7 is clear (memory buffer).
55DE
If the NZ FLAG has been set (bit 7 = 1 = disk buffer sector), JUMP to 55F6H to perform a disk-read operation to load the next spool sector into a memory buffer before outputting characters from it.

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.

55E0
LD (IX+23H),A DD 77 23
Store Register A (descriptor byte 2 = the number of bytes/sectors available in this memory buffer slot) into IX+23H (the data-available count in the symbient FCB). The ISR will use this count to know how many characters can be dequeued from the current buffer before it is exhausted.
55E3
POP HL E1
Restore Register Pair HL (the ring read descriptor address, saved at 55D2H) from the stack.
55E4
LD (HL),00H 36 00
Store 00H to (HL) (read descriptor byte 0). This clears the first byte of the read descriptor that was just consumed, marking it as free for the enqueue side to reuse.
55E6
INC HL 23
INCrement Register Pair HL by 1 to read descriptor byte 1.
55E7
LD (HL),00H 36 00
Store 00H to (HL) (read descriptor byte 1). This clears the second byte of the consumed descriptor, completing the "free this slot" operation.
55E9
LD (IX+28H),C DD 71 28
Store Register C (the low byte of the next descriptor address, from descriptor bytes 0-1) into IX+28H (the ring read pointer low byte). The ring read pointer is advanced to the next descriptor in the chain.
55EC
LD (IX+29H),B DD 70 29
Store Register B (the high byte of the next descriptor address) into IX+29H (the ring read pointer high byte). The read pointer is now fully advanced to the next ring slot.
55EF
POP BC C1
Restore Register Pair BC from the stack (ISR context restore, first register).
55F0
POP DE D1
Restore Register Pair DE from the stack (ISR context restore).
55F1
POP HL E1
Restore Register Pair HL from the stack (ISR context restore).
55F2
POP IX DD E1
Restore Index Register IX from the stack (ISR context restore).
55F4
POP AF F1
Restore Register Pair AF from the stack (ISR context restore - the AF saved at entry 55BCH).
55F5
RET C9
Return to the ISR. The ring read pointer has been advanced to the next descriptor; the ISR now knows where to read the next character from.

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.

55F6
LD C,A 4F
Load Register C with Register A (descriptor byte 2 = the low byte of the disk sector number to read). This is the seek target for the file position call.
55F7
LD A,(HL) 7E
Fetch the byte at HL (descriptor byte 3 = the flag byte). This byte contains both the disk flag (bit 7) and the high nibble of the sector number (bits 3-0).
55F8
AND A,0FH E6 0F
Bitwise AND Register A with 0FH to strip the bit-7 disk flag, leaving only the sector number high nibble in bits 3-0.
55FA
LD B,A 47
Load Register B with Register A (the sector number high nibble). Register Pair BC now holds the full 16-bit disk sector number (C = low byte, B = high nibble), matching the format expected by CALL 4442H.
55FB
PUSH IX DD E5
Save Index Register IX (symbient FCB) onto the stack in preparation for the file I/O call that requires IX as the FCB pointer.
55FD
POP DE D1
Restore the top of stack (the saved IX value) into Register Pair DE. Register Pair DE now holds the symbient FCB base address, which is used as the FCB pointer for the seek operation.
55FE
GOSUB to SYS0 at 4442H (Position to Record) with BC = disk sector number and DE = FCB pointer. This seeks the spool file to the sector that needs to be read back for dequeue output.
5601
If the NZ FLAG has been set (seek failed), JUMP to SYS0 at 4409H (DOS Error Exit). A failed seek during dequeue is fatal.
5604
POP HL E1
Restore Register Pair HL (the ring read descriptor address, saved at 55D2H) from the stack.
5605
PUSH HL E5
Save Register Pair HL (read descriptor address) back onto the stack.
5606
INC HL 23
INCrement Register Pair HL by 1 to read descriptor byte 1.
5607
INC HL 23
INCrement Register Pair HL by 1 to read descriptor byte 2 (the page/sector number byte).
5608
LD B,(HL) 46
Fetch the byte at HL (read descriptor byte 2 = the memory page number where the sector data should be loaded) into Register B. Register B = the high byte of the target memory buffer address for the disk read.
5609
LD (IX+04H),B DD 70 04
Store Register B (the target memory page number) into IX+04H (the symbient FCB's buffer address high byte). This tells the read routine where in memory to place the 256 bytes read from the spool file sector.
560C
GOSUB to SYS0 at 4436H (Read Record) to read 256 bytes from the current spool file position into the memory buffer at the page identified in IX+04H. The queued output data is now in RAM, ready for character-by-character delivery to the output device.
560F
If the NZ FLAG has been set (read failed), JUMP to SYS0 at 4409H (DOS Error Exit). A failed read during dequeue is fatal.
5612
POP HL E1
Restore Register Pair HL (read descriptor address) from the stack.
5613
PUSH HL E5
Save Register Pair HL (read descriptor address) back for one more use.
5614
LD A,(HL) 7E
Fetch the byte at HL (read descriptor byte 0) into Register A. This byte contains the back-link low byte, which points to the next descriptor in the chain (the memory descriptor that follows this disk descriptor in the ring).
5615
INC HL 23
INCrement Register Pair HL by 1 to read descriptor byte 1.
5616
LD H,(HL) 66
Fetch the byte at HL (read descriptor byte 1 = back-link high byte) into Register H. Register H now holds the high byte of the next descriptor address in the chain.
5617
LD L,A 6F
Load Register L with Register A (the back-link low byte fetched at 5614H). Register Pair HL now holds the full address of the next ring descriptor in the chain (the memory descriptor that follows this disk entry).
5618
LD C,(HL) 4E
Fetch the byte at HL (byte 0 of the next descriptor = the sector count or buffer fill level for the memory buffer that was just filled by the disk read) into Register C. This count will be stored in IX+23H to tell the ISR how many characters to deliver.
5619
LD (HL),00H 36 00
Store 00H to (HL) (next descriptor byte 0). This clears the sector count field in the memory descriptor, marking it as the new active output buffer (count = 0 = start reading from offset 0).
561B
INC HL 23
INCrement Register Pair HL by 1 to next descriptor byte 1.
561C
LD B,(HL) 46
Fetch the byte at HL (next descriptor byte 1) into Register B. Register Pair BC now holds the two-byte field from the memory descriptor being promoted to active output status.
561D
LD (HL),00H 36 00
Store 00H to (HL) (next descriptor byte 1). Clear this byte as well, completing the "claim this memory descriptor as the active output buffer" operation.
561F
POP HL E1
Restore Register Pair HL (the original disk read descriptor base address) from the stack.
5620
LD (HL),C 71
Store Register C (the sector count from the memory descriptor byte 0) to (HL) (disk read descriptor byte 0). This writes the available-sector count into the disk descriptor's first byte, establishing the delivery count for the ISR.
5621
INC HL 23
INCrement Register Pair HL by 1 to disk descriptor byte 1.
5622
LD (HL),B 70
Store Register B (memory descriptor byte 1 value) to (HL) (disk descriptor byte 1).
5623
INC HL 23
INCrement Register Pair HL by 1 to disk descriptor byte 2.
5624
LD B,(HL) 46
Fetch the byte at HL (disk descriptor byte 2 = the memory page number of the buffer just filled by the disk read) into Register B.
5625
LD (IX+23H),B DD 70 23
Store Register B (the page number / character count now available in the newly-filled memory buffer) into IX+23H (the data-available count in the symbient FCB). The ISR dequeue routine will now deliver characters from this buffer page until IX+23H is decremented to zero.
5628
JUMP to 55EFH to restore BC, DE, HL, IX, and AF from the stack and return to the ISR. The disk sector has been read into memory; the ISR is now ready to deliver characters from the newly-populated buffer.

5654H - End Padding

Two NOP bytes at the end of the overlay binary. These pad the overlay to its full allocated size.

5654-5655
NOP x 2 00 00
Padding. Two no-operation bytes at the end of the SPOOL SYS6 member binary.

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 RangePurpose
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

AddressName and Purpose
5200HAPPEND 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.
5270HCOPY Entry Point (Re-Open)
Re-open source and destination files for multi-pass transfers when file exceeds buffer size.
52DCHRead Loop
Read records from source file into buffer pages (5500H-nnFFH), advancing page by page until buffer full or EOF.
52FEHWrite Loop
Write records from buffer pages to destination file, page by page.
5312HEOF / Transfer Complete Handler
Flush remaining buffer data, close destination file, exit to DOS READY.
532CHPartial Page Flush
Write remaining pages from buffer when page count is less than full.
5349HBuffer Limit Computer
Read 4049H, compute upper page boundary, patch self-modifying CP instructions at 52F8H, 530DH, 5331H.
535BHInitialize Destination Sector
Position to record, fill 256-byte buffer with E5H (empty marker), write sector to destination file.
5391HDevice I/O Transfer Loop
Read from source device, write to destination device via ROM 0013H/001BH with disk swap handling.
5428HDisk Swap Wait
Check 430FH bit 5, poll keyboard for BREAK release and key confirmation, wait for user to swap disks.
5454HError 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.

5200
GOSUB to the buffer limit computation routine at 5349H. This reads the high memory pointer from 4049H, computes the upper page boundary for the data transfer buffer, and patches the self-modifying CP instructions at 52F8H, 530DH, and 5331H with the boundary value.
5203
LD DE,549DH 11 9D 54
Point Register Pair DE to the source filespec buffer at 549DH (32 bytes).
5206
GOSUB to the SYS0 filespec extraction routine at 441CH. Parses the command line and stores the source filespec into the buffer at DE (549DH). Z = filespec found, NZ = no filespec.
5209
If the NZ FLAG (Not Zero) has been set, meaning no source filespec was provided, JUMP to 5459H to display "FILE SPEC REQUIRED" and exit.
520C
LD DE,54BDH 11 BD 54
Point Register Pair DE to the destination filespec buffer at 54BDH (32 bytes).
520F
GOSUB to 441CH to extract the destination filespec from the command line into 54BDH. Z = found, NZ = not found.
5212
If the Z FLAG (Zero) has been set, meaning a destination filespec was found, JUMP to 521AH to proceed with the destination filespec.

No destination filespec was provided. Copy the source filespec to the destination buffer as a default (same filename, different disk).

5214
DEC HL 2B
DECrement HL to adjust the source pointer back (HL was advanced past the source filespec by 441CH).
5215
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H (32 decimal). This is the size of the filespec/FCB buffer.
5218
LDIR ED B0
Block copy 32 bytes from HL (source filespec area) to DE (destination buffer at 54BDH). The destination now has the same filespec as the source, so APPEND will create a file with the same name on the destination disk.

Check for wildcard '*' in the source filespec. APPEND does not support wildcards.

521A
LD A,(54BDH) 3A BD 54
Fetch the first byte of the destination filespec from 54BDH into Register A.
521D
CP A,2AH FE 2A
Compare Register A against 2AH (ASCII *). If the destination filespec begins with '*', it is a wildcard which APPEND does not support.
521F
If the Z FLAG (Zero) has been set, meaning the destination starts with '*', JUMP to 5459H for "FILE SPEC REQUIRED" error. Wildcards are rejected.
5222
LD HL,549DH 21 9D 54
Point Register Pair HL to the source filespec buffer at 549DH.
5225
LD DE,54BDH 11 BD 54
Point Register Pair DE to the destination filespec buffer at 54BDH.
5228
GOSUB to the filespec comparison/extension copy routine at 53CDH. This ensures the destination filespec has a valid extension by copying the source extension if the destination has none. It also validates drive letters and parses the filespec structure.

Open the source file for reading.

522B
LD B,00H 06 00
Load Register B with 00H (open mode: read existing file).
522D
LD HL,5500H 21 00 55
Point Register Pair HL to the start of the data transfer buffer at 5500H. This will be used as the record buffer for the source file.
5230
GOSUB to the SYS0 file open routine at 4424H. Opens the source file specified by DE (549DH) for reading with the buffer at HL (5500H). Z = success, NZ = error.
5233
If the NZ FLAG (Not Zero) has been set, meaning the source file open failed, JUMP to 5454H for the error exit.

Source file is open. Now open the destination file for writing.

5236
LD B,00H 06 00
Load Register B with 00H (open mode: create new file).
5238
LD DE,549DH 11 9D 54
Point Register Pair DE to the source filespec buffer at 549DH. Wait - this should be the destination (54BDH). The source FCB was already opened. This may be loading DE with the source for a secondary call that uses both FCBs.
523B
LD HL,5500H 21 00 55
Point Register Pair HL to the data transfer buffer at 5500H.
523E
GOSUB to 4424H to open the destination file. Uses the filespec at DE with buffer at HL.
5241
If the NZ FLAG (Not Zero) has been set, meaning the destination file open failed, JUMP to 5454H for the error exit.

Both files are now open. Check if the destination file already has content (54C5H holds a continuation flag from the file open).

5244
LD A,(54C5H) 3A C5 54
Fetch the transfer continuation flag from 54C5H into Register A. If non-zero, the destination file already existed and has content that must be accounted for.
5247
OR A,A B7
OR Register A with itself to test if it is zero.
5248
If the Z FLAG (Zero) has been set, meaning the continuation flag is zero (new file, no existing content), JUMP to 5259H to proceed with the fresh transfer setup.

Destination file already exists. Perform directory info lookup and set up the write buffer for appending/overwriting.

524A
LD DE,54BDH 11 BD 54
Point Register Pair DE to the destination filespec buffer at 54BDH.
524D
GOSUB to the SYS0 file info/directory helper at 4448H. Retrieves directory information for the destination file (size, attributes, etc.).
5250
LD HL,5600H 21 00 56
Point Register Pair HL to 5600H. This is the write buffer start for the existing-file case (one page higher than the standard 5500H).
5253
LD (54C0H),HL 22 C0 54
Store the write buffer pointer (5600H) to the variable at 54C0H. This sets the starting page for write operations when the destination file already exists.
5256
JUMP to 5391H to enter the device I/O transfer loop, which reads from the source device and writes to the destination device via ROM 0013H/001BH with disk swap handling.

Fresh Transfer Setup
Destination is a new file. Compute the initial record position and initialize the destination sector.

5259
LD HL,(54C9H) 2A C9 54
Fetch the record count tracking value from 54C9H into Register Pair HL. For a new transfer, this holds the starting record position (usually 0000H).
525C
PUSH HL E5
Save the record count onto the stack.
525D
LD HL,(54A9H) 2A A9 54
Fetch the record position offset from 54A9H into Register Pair HL.
5260
ADD HL,BC 09
ADD BC (from the file open return) to HL (record offset). This computes the adjusted record position for the destination file.
5261
LD B,H 44
Copy the high byte of the adjusted position into Register B.
5262
LD C,L 4D
Copy the low byte into Register C. BC now holds the adjusted record position.
5263
GOSUB to the destination sector initialization routine at 535BH. This positions to the record (via CALL 4442H), fills 256 bytes with E5H, and writes the sector to the destination file.
5266
POP HL E1
Restore the record count from the stack into Register Pair HL.
5267
LD (54C9H),HL 22 C9 54
Store the record count back to 54C9H.
526A
GOSUB to 4448H for file info/directory lookup on the destination file.
526D
JUMP to 52DCH to enter the main read loop, which reads data from the source file into the buffer pages.

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.

5270
GOSUB to the buffer limit computation routine at 5349H. Re-computes the upper page boundary (high memory may have changed).
5273
LD DE,549DH 11 9D 54
Point Register Pair DE to the source filespec buffer at 549DH.
5276
GOSUB to 441CH to extract the source filespec.
5279
If the NZ FLAG (Not Zero) has been set, meaning no source filespec, JUMP to 5459H for "FILE SPEC REQUIRED" error.
527C
LD DE,54BDH 11 BD 54
Point Register Pair DE to the destination filespec buffer at 54BDH.
527F
GOSUB to 441CH to extract the destination filespec.
5282
If the Z FLAG (Zero) has been set, meaning a destination filespec was found, JUMP to 528AH.
5284
DEC HL 2B
DECrement HL to adjust source pointer.
5285
LD BC,0020H 01 20 00
Load BC with 0020H (32 bytes) for the filespec copy.
5288
LDIR ED B0
Block copy 32 bytes from source filespec to destination buffer (same as 5214H-5218H in the initial path).

Check both source and destination for wildcard '*'.

528A
LD A,(549DH) 3A 9D 54
Fetch the first byte of the source filespec from 549DH.
528D
CP A,2AH FE 2A
Compare against 2AH (ASCII *).
528F
If the source starts with '*', JUMP to 5375H. The wildcard path at 5375H opens both files with a different mode and enters the device I/O loop.
5292
LD A,(54BDH) 3A BD 54
Fetch the first byte of the destination filespec from 54BDH.
5295
CP A,2AH FE 2A
Compare against 2AH (ASCII *).
5297
If the destination starts with '*', JUMP to 5375H for the wildcard/device path.
529A
LD HL,549DH 21 9D 54
Point HL to the source filespec.
529D
LD DE,54BDH 11 BD 54
Point DE to the destination filespec.
52A0
GOSUB to the filespec comparison/extension copy routine at 53CDH.
52A3
LD B,00H 06 00
Load Register B with 00H (open mode).
52A5
LD DE,549DH 11 9D 54
Point DE to the source filespec.
52A8
LD HL,5500H 21 00 55
Point HL to the data transfer buffer at 5500H.
52AB
GOSUB to 4424H to re-open the source file for reading.
52AE
If error, JUMP to 5454H.
52B1
LD B,00H 06 00
Load Register B with 00H.
52B3
LD DE,54BDH 11 BD 54
Point DE to the destination filespec.
52B6
LD HL,5500H 21 00 55
Point HL to the transfer buffer.
52B9
GOSUB to 4420H to open the destination file for writing (create new).
52BC
If error, JUMP to 5454H.

Both files re-opened. Compare source and destination file sizes to determine transfer remaining.

52BF
LD HL,(54A3H) 2A A3 54
Fetch the source file size/position from 54A3H into HL.
52C2
LD DE,(54C3H) ED 5B C3 54
Fetch the destination file size comparison value from 54C3H into DE.
52C6
XOR A,A AF
Set Register A to ZERO and clear the CARRY FLAG for the subtract.
52C7
SBC HL,DE ED 52
SUBtract DE (destination size) from HL (source size). If they are equal (Z set), the source file has already been fully transferred.
52C9
If the Z FLAG (Zero) has been set, meaning source and destination sizes are equal (transfer already complete or same-size overwrite), JUMP to 5475H to display "DESTINATION FILE SPEC REQUIRED" and exit. This catches the case where source and destination are the same disk.
52CC
LD BC,(54A9H) ED 4B A9 54
Fetch the record position offset from 54A9H into BC.
52D0
GOSUB to the destination sector initialization routine at 535BH. Position and initialize the destination sector.
52D3
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H to reset the record count tracker.
52D6
LD (54C9H),HL 22 C9 54
Store 0000H to the record count at 54C9H, resetting the multi-pass counter.
52D9
GOSUB to the SYS0 file flush routine at 443FH. Flushes any buffered data and re-syncs the file position.

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.

52DC
LD HL,5500H 21 00 55
Point Register Pair HL to the start of the data transfer buffer at 5500H.
52DF
LD (54A0H),HL 22 A0 54
Store the buffer start address to the current buffer page pointer at 54A0H. This tracks which page is being read into.
52E2
LD DE,549DH 11 9D 54
Point Register Pair DE to the source filespec/FCB at 549DH.
52E5
GOSUB to the SYS0 read record routine at 4436H. Reads one record (256 bytes) from the source file into the buffer page at (54A0H). Z = success, NZ = error or EOF. A = error code on NZ.
52E8
If the Z FLAG (Zero) has been set, meaning the read was successful, JUMP to 52F5H to advance to the next buffer page.

Read returned non-zero. Check if this is EOF (error codes 1CH or 1DH) or a real error.

52EA
CP A,1CH FE 1C
Compare Register A against 1CH. Error code 1CH indicates end-of-file reached.
52EC
If the Z FLAG (Zero) has been set, meaning EOF (error 1CH), JUMP to 5312H to handle end-of-file (flush remaining buffer and close).
52EE
CP A,1DH FE 1D
Compare Register A against 1DH. Error code 1DH indicates end-of-extent (logical EOF in VTOS extent-based files).
52F0
If the Z FLAG (Zero) has been set, meaning end-of-extent (error 1DH), JUMP to 5312H for EOF handling.
52F2
JUMP to 5454H for the error exit. This is a real read error (not EOF).

Advance Buffer Page
Read was successful. Move to the next 256-byte page in the buffer.

52F5
INC H 24
INCrement Register H (the high byte of the buffer pointer). This advances the buffer position by 256 bytes (one full page). H goes from 55H to 56H, 57H, etc.
52F6
LD A,H 7C
Load Register A with the current page number (Register H).
52F7
CP A,60H FE 60
Self-Modifying Code
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).
52F9
If the NZ FLAG (Not Zero) has been set, meaning the buffer is not yet full (current page has not reached the limit), LOOP BACK to 52DFH to read the next record into the next page.

Buffer Full
The buffer is full. Write all buffered pages to the destination file.

52FB
LD HL,5500H 21 00 55
Point Register Pair HL to the start of the buffer at 5500H (first page to write).
52FE
LD (54C0H),HL 22 C0 54
Store the write start pointer to 54C0H.

Write Loop
Write buffered pages to the destination file, one page at a time.

5301
LD DE,54BDH 11 BD 54
Point Register Pair DE to the destination filespec/FCB at 54BDH.
5304
GOSUB to the SYS0 write record routine at 4439H. Writes one record (256 bytes) from the current buffer page to the destination file.
5307
If error, JUMP to 5454H.
530A
INC H 24
INCrement H to advance to the next buffer page for writing.
530B
LD A,H 7C
Load Register A with the current write page number.
530C
CP A,60H FE 60
Self-Modifying Code
Compare against the buffer upper page limit. The operand byte at 530DH is patched by 5349H (same boundary as 52F8H).
530E
If the NZ FLAG (Not Zero) has been set, meaning more pages remain to write, LOOP BACK to 52FEH to write the next page.
5310
LOOP BACK to 52DCH to begin the next read pass. The buffer has been flushed; start filling it again from the source file.

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.

5312
GOSUB to the partial page flush routine at 532CH. Writes any remaining pages from 5500H up to the current read position to the destination file.
5315
LD HL,(54A5H) 2A A5 54
Fetch the transfer state variable from 54A5H into HL.
5318
LD (54C5H),HL 22 C5 54
Store the state to the continuation flag at 54C5H. This records whether the transfer is complete or needs another pass.
531B
JUMP to 5320H to close the file and exit.
531D
GOSUB to partial page flush (alternate entry for different EOF path).

Close and Exit

5320
LD DE,54BDH 11 BD 54
Point DE to the destination filespec/FCB at 54BDH.
5323
GOSUB to the SYS0 file close routine at 4428H to close the destination file.
5326
If error during close, JUMP to 5454H.
5329
JUMP to 402DH (DOS READY / No-Error Exit). The APPEND transfer is complete.

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.

532C
LD A,H 7C
Load Register A with the current page number (Register H, which was the read page when EOF was detected).
532D
CP A,55H FE 55
Compare against 55H (the first buffer page). If H = 55H, no data was read into the buffer on this pass.
532F
RET Z C8
If the Z FLAG (Zero) has been set, meaning no data to flush (page still at 55H), return immediately.
5330
CP A,60H FE 60
Self-Modifying Code
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.
5332
RET Z C8
If Z (page at limit), return - nothing more to flush.
5333
LD B,H 44
Copy the target page (Register H, the page where EOF was detected) into Register B. This is the upper bound for the write loop.
5334
LD HL,5500H 21 00 55
Point HL to the first buffer page at 5500H.
5337
LD (54C0H),HL 22 C0 54
Store the write pointer to 54C0H.

Loop Start
Write pages from 5500H up to (but not including) the page in B.

533A
LD DE,54BDH 11 BD 54
Point DE to the destination filespec/FCB.
533D
GOSUB to 4439H to write one record from the current buffer page to the destination file.
5340
If error, JUMP to 5454H.
5343
INC H 24
INCrement H to advance to the next page.
5344
LD A,H 7C
Load A with the current page.
5345
CP A,B B8
Compare current page against the target page (Register B).
5346
If NZ (more pages to write), LOOP BACK to 5337H.
5348
RET C9
Return after all remaining pages have been written.

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.

5349
PUSH HL E5
Save Register Pair HL onto the stack (preserve caller's HL).
534A
LD HL,(4049H) 2A 49 40
Fetch the current high memory pointer from 4049H into HL. This is the lowest address available for buffer use.
534D
INC HL 23
INCrement HL to point to the first usable byte above the high memory boundary.
534E
DEC H 25
DECrement the high byte (H) by 1, subtracting 256 bytes. This provides a safety margin below the high memory pointer.
534F
LD A,H 7C
Load Register A with the computed page boundary (Register H). This is the upper page limit for the transfer buffer.
5350
LD (52F8H),A 32 F8 52
Self-Modifying Code
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.
5353
LD (530DH),A 32 0D 53
Self-Modifying Code
Store the page limit to the CP operand at 530DH in the write loop.
5356
LD (5331H),A 32 31 53
Self-Modifying Code
Store the page limit to the CP operand at 5331H in the partial flush routine.
5359
POP HL E1
Restore Register Pair HL from the stack.
535A
RET C9
Return to the caller with all three self-modifying locations patched.

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.

535B
LD DE,54BDH 11 BD 54
Point Register Pair DE to the destination filespec/FCB at 54BDH.
535E
GOSUB to the SYS0 position-to-record routine at 4442H. Positions the destination file to the record specified by BC.
5361
LD HL,5500H 21 00 55
Point HL to the buffer at 5500H.
5364
LD DE,5501H 11 01 55
Point DE to 5501H (one byte past the start of the buffer).
5367
LD BC,00FFH 01 FF 00
Load BC with 00FFH (255 bytes). Together with the initial store, this fills all 256 bytes.
536A
LD (HL),0E5H 36 E5
Store E5H to the first byte at (HL). The value E5H is the standard TRS-80 directory "empty entry" marker, used here as a fill pattern for unused sectors.
536C
LDIR ED B0
Block copy 255 bytes from 5500H to 5501H. Since source and destination overlap by 1, this propagates E5H through the entire 256-byte buffer (a standard Z80 memory fill technique).
536E
LD DE,54BDH 11 BD 54
Point DE to the destination FCB.
5371
GOSUB to 4439H to write the E5H-filled sector to the destination file.
5374
RET C9
Return to the caller.

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.

5375
LD B,00H 06 00
Load Register B with 00H (open mode).
5377
LD DE,549DH 11 9D 54
Point DE to the source filespec at 549DH.
537A
LD HL,5500H 21 00 55
Point HL to the transfer buffer at 5500H.
537D
GOSUB to 4424H to open the source file/device.
5380
If error, JUMP to 5454H.
5383
LD B,00H 06 00
Load Register B with 00H.
5385
LD DE,54BDH 11 BD 54
Point DE to the destination filespec at 54BDH.
5388
LD HL,5600H 21 00 56
Point HL to 5600H as the destination buffer (one page above the source buffer).
538B
GOSUB to 4420H to open the destination file/device for writing.
538E
If error, JUMP to 5454H.

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.

5391
LD DE,549DH 11 9D 54
Point Register Pair DE to the source filespec/DCB at 549DH for the byte input routine.
5394
LD A,(DE) 1A
Fetch the first byte of the source filespec into Register A. This checks whether the source is a device (bit 7 set) or a disk file.
5395
BIT 7,A CB 7F
Test bit 7 of Register A. If bit 7 is set, the source is a device (e.g., *CL, *PR) rather than a disk file. Device I/O does not require disk swap handling.
5397
If the Z FLAG (Zero) has been set, meaning bit 7 is clear (source is a disk file), JUMP to 53ACH to read via ROM 0013H with disk swap.

Source is a device. Read bytes from device with disk swap check between reads.

5399
GOSUB to ROM 0013H (get byte from device). DE = DCB address. On return, A = byte read. Z = device ready.
539C
If the Z FLAG (Zero) has been set (device ready, byte received), JUMP to 53B7H to write the byte to the destination.
539E
CP A,1CH FE 1C
Compare Register A against 1CH (EOF). If EOF from the device, the transfer is complete.
53A0
If Z (EOF), JUMP to 5320H to close the destination file and exit.
53A3
JUMP to 5454H for error exit (device read error other than EOF).

Disk Swap Check Before Read
Source is a disk file. Check if disk swap is needed via 5428H before reading.

53A6
GOSUB to the disk swap wait routine at 5428H. Checks if a disk swap is needed and waits for the user to insert the correct disk.
53A9
If NZ (user cancelled or error during swap), JUMP to 5320H to close and exit.
53AC
GOSUB to ROM 0013H to read a byte from the source file/device. A = byte read on success.
53AF
OR A,A B7
OR Register A with itself to test the return value.
53B0
If Z (device not ready or no data), LOOP BACK to 53A6H to retry with disk swap check.
53B2
CP A,01H FE 01
Compare Register A against 01H (BREAK key code).
53B4
If Z (BREAK pressed), JUMP to 5320H to close and exit. BREAK aborts the transfer.

Write Byte to Destination
A byte has been read. Write it to the destination file/device.

53B7
LD DE,54BDH 11 BD 54
Point DE to the destination filespec/DCB at 54BDH.
53BA
GOSUB to ROM 001BH (output byte to device). A = byte to write, DE = DCB address.
53BD
If Z (write successful), JUMP to 53C5H to continue with the disk swap check for the next iteration.

Write returned NZ. Check if the destination is a device with bit 7 set.

53BF
EX DE,HL EB
Exchange DE and HL. HL now points to the destination FCB.
53C0
BIT 7,(HL) CB 7E
Test bit 7 of the first byte of the destination FCB. If set, destination is a device.
53C2
EX DE,HL EB
Exchange back to restore DE and HL.
53C3
If NZ (destination is a device and write failed), JUMP to 53A3H -> 5454H for error exit.

Disk Swap Check After Write
Destination write succeeded or is a disk file needing retry.

53C5
GOSUB to 5428H for disk swap wait before the next read/write cycle.
53C8
If NZ (cancelled), JUMP to 5320H to close and exit.
53CB
LOOP BACK to 5391H to read the next byte from the source. The device I/O transfer continues until EOF or error.

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.

53CD
PUSH DE D5
Save the destination filespec pointer onto the stack.
53CE
LD A,(DE) 1A
Fetch the first byte of the destination filespec.
53CF
CP A,41H FE 41
Compare against 41H (ASCII 'A'). If < 'A', the first character is not a drive letter.
53D1
If the CARRY FLAG has been set, meaning the first character is below 'A' (possibly a digit or punctuation), GOSUB to the drive letter validation routine at 5408H.
53D4
LD B,2FH 06 2F
Load Register B with 2FH (ASCII /). This is the extension separator to search for.
53D6
GOSUB to the delimiter search routine at 53E1H. Scans the destination filespec (DE) for the '/' extension separator.
53D9
LD B,2EH 06 2E
Load Register B with 2EH (ASCII .). This is an alternate separator to search for.
53DB
GOSUB to 53E1H to scan for the '.' separator.
53DE
POP DE D1
Restore the destination filespec pointer from the stack.
53DF
RET C9
Return to the caller.

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.

53E0
INC DE 13
INCrement DE to the next character in the filespec.
53E1
LD A,(DE) 1A
Fetch the current character from the filespec at (DE).
53E2
CP A,B B8
Compare against the delimiter (Register B). If match, Z is set.
53E3
If Z (delimiter found), JUMP to 53F3H to advance past it and return.
53E5
CP A,41H FE 41
Compare against 41H ('A'). Alphanumeric check: if >= 'A', it is a letter.
53E7
If NC (A >= 'A', alphabetic), LOOP BACK to 53E0H to advance and check the next character.
53E9
CP A,30H FE 30
Compare against 30H ('0'). Check if it is a digit.
53EB
If C (A < '0', not a digit), JUMP to 53F5H - the scan has reached a non-alphanumeric, non-delimiter character (possibly a terminator).
53ED
CP A,3AH FE 3A
Compare against 3AH (':'). Digits are 30H-39H.
53EF
If C (A < ':', i.e., A is a digit), LOOP BACK to 53E0H to continue scanning.
53F1
JUMP to 53F5H - character is between ':' and 'A' (non-alphanumeric).
53F3
INC DE 13
INCrement DE past the delimiter character.
53F4
RET C9
Return with DE pointing past the delimiter.

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.

53F5
PUSH HL E5
Save the source pointer.
53F6
LD A,(HL) 7E
Fetch the current character from the source filespec.
53F7
INC HL 23
INCrement HL.
53F8
CP A,03H FE 03
Compare against 03H (ETX terminator).
53FA
If Z (ETX reached), JUMP to 5406H - end of source filespec without finding the delimiter.
53FC
CP A,0DH FE 0D
Compare against 0DH (CR terminator).
53FE
If Z (CR reached), JUMP to 5406H.
5400
CP A,B B8
Compare against the delimiter (Register B).
5401
If NZ (not the delimiter), LOOP BACK to 53F6H to continue scanning the source.
5403
GOSUB to the character insertion routine at 5414H. Copies the delimiter and extension from the source into the destination filespec by shifting characters.
5406
POP HL E1
Restore the source pointer from the stack.
5407
RET C9
Return to the caller.

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.

5408
LD A,(HL) 7E
Fetch the current character from the source filespec at (HL).
5409
CP A,30H FE 30
Compare against 30H ('0'). Check if it is below the digit range.
540B
RET C D8
If C (below '0'), return - not a valid drive digit.
540C
CP A,3AH FE 3A
Compare against 3AH (':'). Digits are 30H-39H.
540E
If C (A < ':', i.e., A is a digit), JUMP to 5413H to advance HL past the drive letter.
5410
CP A,41H FE 41
Compare against 41H ('A').
5412
RET C D8
If C (below 'A'), return - not a valid drive letter.
5413
INC HL 23
INCrement HL past the drive letter/digit character.

Character Insertion Subroutine
Inserts characters from the source position into the destination filespec by shifting existing characters right. Used to copy extension data.

5414
PUSH HL E5
Save HL (source position).
5415
LD H,D 62
Copy D into H.
5416
LD L,E 6B
Copy E into L. HL now equals DE (destination position).

Loop Start
Shift characters in the destination right to make room for the inserted extension.

5417
LD C,(HL) 4E
Fetch the character at the current destination position into Register C (save it before overwriting).
5418
LD (HL),A 77
Store the insertion character (Register A) to the destination position, overwriting the existing character.
5419
INC HL 23
INCrement HL to the next destination position.
541A
LD A,C 79
Load Register A with the displaced character (Register C). This will be inserted at the next position.
541B
CP A,03H FE 03
Compare against 03H (ETX terminator). If the displaced character is the terminator, the insertion is complete.
541D
If Z (terminator displaced), JUMP to 5423H to store the terminator and finish.
541F
CP A,0DH FE 0D
Compare against 0DH (CR terminator).
5421
If NZ (not CR), LOOP BACK to 5417H to continue shifting.
5423
LD (HL),A 77
Store the final terminator (03H or 0DH) to close the shifted string.
5424
POP HL E1
Restore the source pointer from the stack.
5425
INC DE 13
INCrement DE past the inserted character in the destination.
5426
LOOP BACK to 5408H to process the next source character for potential insertion.

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).

5428
LD A,(430FH) 3A 0F 43
Fetch the system state flags byte from 430FH into Register A.
542B
AND A,20H E6 20
AND Register A with 20H to isolate bit 5. Bit 5 indicates whether the system is in a mode that requires disk swap handling (e.g., single-drive operation).
542D
XOR A,20H EE 20
XOR Register A with 20H. If bit 5 was set (AND result = 20H), XOR makes it 00H (Z). If bit 5 was clear (AND result = 00H), XOR makes it 20H (NZ). This inverts the test: Z means bit 5 WAS set, NZ means it was clear.
542F
RET Z C8
If the Z FLAG (Zero) has been set, meaning bit 5 of 430FH was set (disk swap mode active), return immediately with Z. No swap prompt needed - the system is already in swap mode.

Bit 5 of 430FH is clear. Wait for BREAK release, then poll for keys to confirm disk swap.

5430
LD A,(3840H) 3A 40 38
Fetch the special key row from keyboard memory at 3840H. Bit 2 = BREAK key.
5433
AND A,04H E6 04
AND with 04H to isolate the BREAK key bit.
5435
RET NZ C0
If NZ (BREAK is pressed), return NZ immediately. The user is pressing BREAK to cancel the operation.
5436
LD A,(3801H) 3A 01 38
Fetch keyboard row 0 from 3801H. Bit 0 = '@' key.
5439
AND A,01H E6 01
AND with 01H to isolate the '@' key bit.
543B
RET Z C8
If Z ('@' not pressed), return Z. The swap wait continues without '@'.
543C
LD A,(3880H) 3A 80 38
Fetch the SHIFT key row from 3880H. Bit 0 = SHIFT.
543F
AND A,01H E6 01
AND with 01H to isolate the SHIFT key bit.
5441
RET Z C8
If Z (SHIFT not pressed), return Z.

Both '@' and SHIFT are pressed. Wait for BREAK release, then wait for a confirming keypress.

5442
LD A,(3840H) 3A 40 38
Fetch the special key row again to check for BREAK release.
5445
AND A,04H E6 04
Isolate the BREAK key bit.
5447
RET NZ C0
If NZ (BREAK pressed), return NZ to cancel.
5448
GOSUB to ROM 002BH (wait for keypress). Blocks until the user presses a key, returning the character in Register A.
544B
OR A,A B7
OR Register A with itself. If A = 00H (no key), Z is set.
544C
If Z (no key yet), LOOP BACK to 5442H to continue waiting.
544E
CP A,60H FE 60
Compare against 60H (ASCII backtick). The backtick is the cancel key for the disk swap prompt.
5450
If Z (backtick pressed), LOOP BACK to 5442H to continue waiting (ignore the backtick, keep polling). Actually this rejects the backtick and waits for a different key.
5452
XOR A,A AF
Set Register A to ZERO, which sets the Z FLAG. This indicates a successful disk swap confirmation.
5453
RET C9
Return with Z set (swap confirmed). The user pressed a key other than backtick or BREAK.

5454H - Error Exit

Common error exit. Sets bit 6 in the error code and jumps to the DOS error handler.

5454
OR A,40H F6 40
OR Register A with 40H to set bit 6 of the error code, marking it as an overlay-originated error.
5456
JUMP to the SYS0 DOS error handler at 4409H.

5459H - "FILE SPEC REQUIRED" Error

Displays the source file error message and exits.

5459
LD HL,5462H 21 62 54
Point HL to the error string at 5462H.
545C
GOSUB to 447BH to display "FILE SPEC REQUIRED".
545F
JUMP to 4030H (error-already-displayed exit).

5462H - Data: "FILE SPEC REQUIRED"

ASCII error message string for missing source filespec.

5462-5474
DEFM "FILE SPEC REQUIRED",0DH 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
ASCII string (19 bytes): "FILE SPEC REQUIRED" + 0DH terminator.

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).

5475
LD HL,547EH 21 7E 54
Point HL to the error string at 547EH.
5478
GOSUB to 447BH to display "DESTINATION FILE SPEC REQUIRED".
547B
JUMP to 4030H (error-already-displayed exit).

547EH - Data: "DESTINATION FILE SPEC REQUIRED"

ASCII error message string for missing/invalid destination filespec.

547E-549C
DEFM "DESTINATION FILE SPEC REQUIRED",0DH 44 45 53 54 49 4E 41 54 49 4F 4E 20 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
ASCII string (31 bytes): "DESTINATION FILE SPEC REQUIRED" + 0DH terminator.

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 RangePurpose
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

AddressName and Purpose
5200HBUILD Entry Point
Extract filespec via CALL 441CH, scan for /KSM extension, open new file, enter input loop.
523AHInput Loop
KSM mode: display key assignment prompt (A=> , B=> , etc.). Normal mode: accept line input via ROM 0040H.
5272HWrite and Close
Store 03H terminator, write buffer to file via ROM 001BH, close file via CALL 4428H.
528EHError 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".

5200
LD DE,52CCH 11 CC 52
Point Register Pair DE to the filespec buffer at 52CCH. This buffer will receive the parsed filespec from the command line.
5203
GOSUB to the SYS0 filespec extraction routine at 441CH. Parses the command line and stores the filespec into the buffer at DE (52CCH). On return, the Z FLAG is set if a filespec was found; NZ if no filespec was present.
5206
If the NZ FLAG (Not Zero) has been set, meaning no filespec was provided on the command line, JUMP to 5293H to display "FILE SPEC REQUIRED" and exit.

A filespec was found. Now scan it for the "/KSM" extension to determine if this is a KSM keystroke macro file.

5209
LD HL,52CCH 21 CC 52
Point Register Pair HL to the filespec buffer at 52CCH to scan for the extension.
520C
LD A,(HL) 7E
Fetch the next character from the filespec buffer at (HL) into Register A.
520D
INC HL 23
INCrement HL to point to the next character in the filespec.
520E
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). If the end of the filespec is reached without finding '/KSM', this is not a KSM file.
5210
If the Z FLAG (Zero) has been set, meaning the end of filespec was reached (0DH found), JUMP to 522CH to proceed with normal (non-KSM) file creation.
5212
CP A,2FH FE 2F
Compare Register A against 2FH (ASCII /). The extension separator in VTOS filespecs is '/'.
5214
If the NZ FLAG (Not Zero) has been set, meaning the character is not '/', LOOP BACK to 520CH to check the next character.

Found '/' separator. Check if the extension is "KSM".

5216
LD A,(HL) 7E
Fetch the first extension character from (HL) into Register A.
5217
INC HL 23
INCrement HL to point to the second extension character.
5218
CP A,4BH FE 4B
Compare Register A against 4BH (ASCII K). First character of "KSM".
521A
If the NZ FLAG (Not Zero) has been set, meaning the first extension character is not 'K', JUMP to 522CH to proceed as a normal file (not KSM).
521C
LD A,(HL) 7E
Fetch the second extension character from (HL).
521D
INC HL 23
INCrement HL to point to the third extension character.
521E
CP A,53H FE 53
Compare Register A against 53H (ASCII S). Second character of "KSM".
5220
If the NZ FLAG (Not Zero) has been set, meaning the second character is not 'S', JUMP to 522CH for normal file mode.
5222
LD A,(HL) 7E
Fetch the third extension character from (HL).
5223
CP A,4DH FE 4D
Compare Register A against 4DH (ASCII M). Third character of "KSM".
5225
If the NZ FLAG (Not Zero) has been set, meaning the third character is not 'M', JUMP to 522CH for normal file mode.

Extension is "/KSM". Enable KSM keystroke macro mode by setting the flag byte.

5227
LD A,0FFH 3E FF
Load Register A with FFH. This non-zero value activates KSM mode.
5229
LD (523EH),A 32 3E 52
Self-Modifying Code
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.

522C
LD HL,5300H 21 00 53
Point Register Pair HL to the file I/O record buffer at 5300H. This 256-byte buffer will be used for file write operations.
522F
LD B,00H 06 00
Load Register B with 00H. This is the open mode parameter for the file open routine (00H = open new file for writing).
5231
GOSUB to the SYS0 file open routine at 4420H. Opens a new file using the filespec at 52CCH with the record buffer at HL (5300H). On return, Z = success (new file created), NZ = error, NC = file already exists.
5234
If the NZ FLAG (Not Zero) has been set, meaning an error occurred during file open, JUMP to 528EH for the error exit (OR A,40H / JP 4409H).
5237
If the NO CARRY FLAG has been set, meaning the file already exists (4420H returns NC when a file with the same name is found), JUMP to 52AFH to display "FILE ALREADY EXISTS" and exit.

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.

523A
LD HL,5400H 21 00 54
Point Register Pair HL to the line input buffer at 5400H. Keyboard input will be collected here via ROM 0040H.
523D
LD A,00H 3E 00
Self-Modifying Code
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.
523F
OR A,A B7
OR Register A with itself to test the KSM flag. If A = 00H (normal mode), Z is set.
5240
If the Z FLAG (Zero) has been set, meaning normal mode (not KSM), JUMP to 5260H to accept input without a key assignment prompt.

KSM Mode Prompt
Display the key assignment prompt: increment the key counter, display the character code + "=> " prompt.

5242
LD A,00H 3E 00
Self-Modifying Code
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).
5244
INC A 3C
INCrement Register A to advance to the next key assignment. First iteration: 00H becomes 01H (key 'A').
5245
LD (5243H),A 32 43 52
Self-Modifying Code
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.
5248
CP A,1BH FE 1B
Compare Register A against 1BH (27 decimal, the ESC key code). KSM files support up to 26 key assignments (A-Z). When the counter reaches 1BH, all assignments are complete.
524A
If the Z FLAG (Zero) has been set, meaning the counter has reached 1BH (all 26 keys assigned), JUMP to 5272H to write the final 03H terminator and close the file.
524C
ADD A,40H C6 40
ADD 40H to Register A to convert the key counter to its ASCII character. 01H+40H = 41H ('A'), 02H+40H = 42H ('B'), etc.
524E
GOSUB to the ROM character output routine at 0033H to display the key letter (e.g., 'A', 'B', 'C').
5251
LD A,3DH 3E 3D
Load Register A with 3DH (ASCII =).
5253
GOSUB to ROM 0033H to display '='.
5256
LD A,3EH 3E 3E
Load Register A with 3EH (ASCII >).
5258
GOSUB to ROM 0033H to display '>'.
525B
LD A,20H 3E 20
Load Register A with 20H (ASCII space).
525D
GOSUB to ROM 0033H to display the space after "=>". The complete prompt "A=> " (or "B=> ", etc.) is now shown.

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).

5260
LD B,0FFH 06 FF
Load Register B with FFH (255 decimal). This is the maximum number of input characters for ROM 0040H. For KSM files, the manual states up to 256 characters per line.
5262
GOSUB to the ROM line input routine at 0040H. HL points to the input buffer at 5400H, B = FFH (max chars). The user types a line and presses ENTER. On return, B = number of characters typed. If BREAK was pressed as the first keystroke, the CARRY FLAG is set.
5265
If the CARRY FLAG has been set, meaning BREAK was pressed, JUMP to 526EH to check if the line is empty (BREAK as first keystroke = end of file).
5267
LD C,B 48
Copy the character count (Register B) into Register C for 16-bit pointer arithmetic.
5268
LD B,00H 06 00
Load Register B with 00H. BC now holds the 16-bit character count.
526A
ADD HL,BC 09
ADD BC (character count) to HL (buffer start). HL now points past the last character typed.
526B
INC HL 23
INCrement HL to skip one more byte (past the CR terminator stored by ROM 0040H).
526C
LOOP BACK to 523DH to accept the next line of input. HL is now positioned past the current line, ready for the next line's input.

Break Key Handler
BREAK was pressed. Check if any characters were typed on this line (B=0 means BREAK was the first keystroke).

526E
LD A,B 78
Load Register A with the character count (Register B) from ROM 0040H.
526F
OR A,A B7
OR Register A with itself. If B = 00H (no characters typed, BREAK was the first keystroke), Z is set.
5270
If the NZ FLAG (Not Zero) has been set, meaning characters were typed before BREAK (B is non-zero), JUMP to 5267H to process this line normally (BREAK mid-line does not end the file).

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.

5272
LD (HL),03H 36 03
Store 03H (ETX, end-of-text marker) to the current position in the input buffer at (HL). This marks the end of all text data in the buffer.
5274
LD HL,5400H 21 00 54
Point Register Pair HL to the start of the input buffer at 5400H. All accumulated text data (from all input lines) will be written from here.
5277
LD DE,52CCH 11 CC 52
Point Register Pair DE to the filespec/DCB buffer at 52CCH. This is the Device Control Block address for the output file, needed by ROM 001BH for byte output.

Loop Start
Write the buffer to the output file one byte at a time.

527A
LD A,(HL) 7E
Fetch the next byte from the input buffer at (HL) into Register A.
527B
INC HL 23
INCrement HL to point to the next byte in the buffer.
527C
CP A,03H FE 03
Compare Register A against 03H (ETX end-of-text marker). If this is the terminator byte, all data has been written.
527E
If the Z FLAG (Zero) has been set, meaning the ETX terminator was reached, JUMP to 5287H to close the file.
5280
GOSUB to the ROM byte output routine at 001BH. A = byte to output, DE = DCB address at 52CCH. Writes one byte to the output file.
5283
If the NZ FLAG (Not Zero) has been set, meaning an error occurred during the write operation, JUMP to 528EH for the error exit.
5285
LOOP BACK to 527AH to write the next byte from the buffer.

Loop End
All data has been written. Close the file.

5287
GOSUB to the SYS0 file close routine at 4428H. Closes the output file, flushing any remaining buffered data and updating the directory entry.
528A
If the Z FLAG (Zero) has been set, meaning the file was closed successfully, JUMP to 402DH (DOS READY / No-Error Exit). The BUILD command is complete.
528D
NOP 00
No operation. Padding byte (falls through to the error exit at 528EH if the close failed).

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.

528E
OR A,40H F6 40
OR Register A with 40H to set bit 6 of the error code. This marks the error as originating from an overlay command rather than from the core DOS.
5290
JUMP to the SYS0 DOS error handler at 4409H with the error code in Register A. This displays the appropriate error message and returns to the DOS command prompt.

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.

5293
LD HL,529CH 21 9C 52
Point Register Pair HL to the error message string at 529CH.
5296
GOSUB to the SYS0 display routine at 447BH to display the "FILE SPEC REQUIRED" message.
5299
JUMP to 4030H (error-already-displayed exit).

529CH - Data: "FILE SPEC REQUIRED"

ASCII error message string.

529C-52AE
DEFM "FILE SPEC REQUIRED",0DH 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
ASCII string (19 bytes): "FILE SPEC REQUIRED" followed by 0DH (carriage return) as terminator.

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).

52AF
LD HL,52B8H 21 B8 52
Point Register Pair HL to the error message string at 52B8H.
52B2
GOSUB to the SYS0 display routine at 447BH to display "FILE ALREADY EXISTS".
52B5
JUMP to 4030H (error-already-displayed exit).

52B8H - Data: "FILE ALREADY EXISTS"

ASCII error message string.

52B8-52CB
DEFM "FILE ALREADY EXISTS",0DH 46 49 4C 45 20 41 4C 52 45 41 44 59 20 45 58 49 53 54 53 0D
ASCII string (20 bytes): "FILE ALREADY EXISTS" followed by 0DH (carriage return) as terminator.

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

AddressBytesPurpose
5282H2Record counter (initialized to 0001H; counts records read and displayed)
527AH2Line number counter (initialized to 0000H; incremented after each output line in ASCII mode)
52ADH2Auxiliary counter (initialized to 0000H; role determined by option flags)
5329H2File open mode byte (initialized to 0000H; set to 0001H when a second extension try is needed)
5380H1Self-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.
52C2H1Self-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.
530FH2Self-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.
532CH1Self-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-54DBH4Decimal record/line number display field (4 space characters, filled in by the number formatter at 542BH)
54DCH1Separator byte between record number and line offset fields (2EH = '.')
54DDH1Separator byte (03H = end-of-string marker for the record number sub-field)
54DEH-54E1H4Record line offset display field (4 space characters, filled in by 542BH for HEX row sub-addressing)
54E2H-54E4H3Separator and end marker (20H 20H 20H = three spaces before the hex address label)
54E5H-54EBH7Hex 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-54EEH3Default file extension string "TXT" (passed to 4473H to insert as default extension)
54EFH-550EH32Input file FCB (File Control Block, 32 bytes). Passed to 4476H as DE to open the source file for reading.
5518H-5527H16HEX 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-5547H32Command 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-5567H32Saved 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)
5600HvariesOutput FCB / *PR device control block. Opened by 4424H (Basic file open) to direct output to the printer device.

Major Routines

AddressName and Purpose
5200HLIST 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.
5206HPRINT 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.
520BHShared 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.
5279HFile 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.
5298HASCII 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.
530EHHEX 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.
5402HPrintable 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.
5417HHex 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.
5420HHex 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.
542BHDecimal 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.
5461HNumeric 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.
5472HPrinter 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.

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.

5200
XOR A,A AF
Clear Register A to 00H. This is the LIST command entry point (IDAM 41H). The value 00H will be stored to the command-mode flag at 5380H to indicate LIST mode, and DE will be loaded with the drive 1 config table entry pointer.
5201
LD DE,401DH 11 1D 40
Load Register Pair DE with 401DH - the address of the second entry in drive configuration table #1 (the drive 1 slot, at offset +08H from the table base at 4015H). This pointer will be pushed onto the stack to identify which drive's DCB to use for the LIST command's device output.
5204
JUMP to 520BH, skipping over the PRINT entry point at 5206H. Execution continues at the shared initialization code.
5208
LD DE,4025H 11 25 40
Load Register Pair DE with 4025H - the address of the third entry in drive configuration table #1 (the drive 2 slot, at offset +10H from the table base at 4015H). This pointer will be pushed onto the stack to identify which drive's DCB to use for the PRINT command's device output. Execution falls through to the shared initialization below.

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.

520B
LD (5380H),A 32 80 53
Self-Modifying Code
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.
520E
PUSH DE D5
Push the drive config table entry pointer (401DH for LIST, 4025H for PRINT) onto the stack. This value remains on the stack throughout execution and is referenced by POP DE / PUSH DE sequences before calls to SYS0 routines that expect a DCB address in DE for printer output.
520F
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H, preparing to zero three 16-bit work variables.
5212
LD (52ADH),BC ED 43 AD 52
Store 0000H to the auxiliary counter at 52ADH (2 bytes). This counter is initialized to zero at the start of each PRINT/LIST invocation.
5216
LD (527AH),BC ED 43 7A 52
Store 0000H to the line number counter at 527AH (2 bytes). This 16-bit counter tracks the current output line number for ASCII listings when the NUM option is active.
521A
LD (5329H),BC ED 43 29 53
Store 0000H to the file open mode variable at 5329H (2 bytes). This value is loaded into B before calling 4424H (Basic file open) and also serves as a flag indicating how many extension attempts have been made (0=first try with TXT, 1=second try with blank).
521E
LD BC,0001H 01 01 00
Load Register Pair BC with 0001H to initialize the record counter to 1. Records are counted from 1, not 0.
5221
LD (5282H),BC ED 43 82 52
Store 0001H to the record counter at 5282H (2 bytes). Each time a record (sector) is successfully read from the source file, this counter is incremented. It is formatted and printed as part of the line-number prefix in ASCII mode and as the record address in HEX mode.
5225
LD DE,5528H 11 28 55
Point Register Pair DE to the filespec work buffer at 5528H (32 bytes). This buffer will receive the parsed filespec from the command line.
5228
GOSUB to SYS0 routine at 441CH to extract the filespec from the command line, placing the parsed filespec into the buffer at DE (5528H). Returns NZ if no filespec was found, Z if a filespec was successfully extracted.
522B
If the NZ FLAG is set (441CH found no filespec on the command line), JUMP to 54A3H to display the "FILE SPEC REQUIRED" error message and exit.
522E
LD A,(DE) 1A
Fetch the first byte of the parsed filespec from the buffer at DE (5528H) into Register A. The filespec begins with the filename's first character.
522F
CP A,2AH FE 2A
Compare Register A against 2AH (ASCII '*'). If the filespec begins with '*', it is a device specification (e.g., *DO, *PR). PRINT does not accept device specs as its input source - only disk filespecs are valid.
5231
If the Z FLAG is set (the filespec begins with '*', indicating a device spec), JUMP to 54A3H to display the "FILE SPEC REQUIRED" error message and exit. Device specs are not accepted as input to PRINT.
5234
LD DE,54EFH 11 EF 54
Point Register Pair DE to the input file FCB (File Control Block) at 54EFH (32 bytes). This FCB is passed to the file open routine at 4476H to control how the input file is opened for reading.
5237
GOSUB to SYS0 routine at 4476H (Open file utility) with DE=54EFH (input file FCB). This routine opens the file named in the command line for reading, initializing the FCB. Returns Z on success, NZ if the file could not be opened or if a parameter error occurred.
523A
If the NZ FLAG is set (file open failed or parameter error), JUMP to 54BFH to display the "PARAMETER ERROR" message and exit.
523D
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H (32 decimal) - the byte count for the filespec copy. The parsed filespec in the 32-byte buffer at 5528H will be saved before the default extension is inserted.
5240
LD DE,5548H 11 48 55
Point Register Pair DE to the saved-filespec buffer at 5548H (32 bytes). This buffer will hold a copy of the original filespec for the fallback open attempt with a blank extension.
5243
LD HL,5528H 21 28 55
Point Register Pair HL to the filespec work buffer at 5528H (32 bytes), which currently holds the parsed filespec extracted by 441CH.
5246
LDIR ED B0
Copy 32 bytes from HL (5528H, the current filespec) to DE (5548H, the saved-filespec buffer), using BC=0020H as the byte count. This preserves the original filespec before 4473H inserts the "TXT" default extension, allowing a second open attempt with a blank extension if TXT fails.
5248
LD DE,5528H 11 28 55
Point Register Pair DE back to the filespec work buffer at 5528H, which will receive the TXT extension inserted by the next call.
524B
LD HL,54ECH 21 EC 54
Point Register Pair HL to the default extension string "TXT" at 54ECH (3 bytes: 54H 58H 54H). This is passed to 4473H as the extension to insert into the filespec.
524E
GOSUB to SYS0 routine at 4473H to insert the default extension "TXT" into the filespec at DE (5528H), if the filespec does not already have an extension. After this call, 5528H contains the complete filespec with a TXT extension ready for file open.
5251
LD A,(5329H) 3A 29 53
Fetch the current file open mode byte from 5329H into Register A. On the first pass through this code, this is 00H (initialized at 521AH). This value is loaded into B and passed to 4424H as the open mode.
5254
LD B,A 47
Transfer Register A (the open mode byte from 5329H) into Register B. B=00H means open for read-only; B=01H (used on the retry) means open with blank extension.
5255
LD HL,5600H 21 00 56
Point Register Pair HL to the output FCB / *PR device control block at 5600H. This is the second parameter passed to 4424H identifying the output device or file for the listing.
5258
GOSUB to SYS0 routine at 4424H (Basic file open) with B=open mode, DE=5528H (input filespec FCB), HL=5600H (output device FCB). Attempts to open the source file. Returns Z if opened successfully, NZ if the file was not found.
525B
If the Z FLAG is set (the file opened successfully with the TXT extension), JUMP to 5279H to check the open mode and proceed to output. The record counter BC is already set from the open call.
525E
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H (32 bytes) for a second LDIR copy. The TXT-extension attempt failed; the original filespec is now restored from the saved buffer.
5261
LD DE,5528H 11 28 55
Point Register Pair DE to the filespec work buffer at 5528H, which will be overwritten with the original filespec (no extension).
5264
LD HL,5548H 21 48 55
Point Register Pair HL to the saved filespec copy at 5548H (saved by the LDIR at 5246H before TXT was inserted). This is the unmodified original filespec.
5267
LDIR ED B0
Copy 32 bytes from HL (5548H, original filespec) back to DE (5528H, work buffer), using BC=0020H. This restores the filespec to its original state so the second open attempt can proceed with a blank (all-spaces) extension.
5269
LD A,(5329H) 3A 29 53
Fetch the open mode byte from 5329H again. It is still 00H on this path (the TXT extension was tried and failed).
526C
LD B,A 47
Transfer Register A (00H) into Register B as the open mode for the second attempt.
526D
LD DE,5528H 11 28 55
Point Register Pair DE to the filespec work buffer at 5528H, now holding the original filespec with no extension, for the second open attempt.
5270
LD HL,5600H 21 00 56
Point Register Pair HL to the output FCB / *PR device control block at 5600H, same as before.
5273
GOSUB to SYS0 routine at 4424H (Basic file open) again, this time with the filespec having no extension (or a blank extension, as the default when no extension is specified). Returns Z if the file is found and opened, NZ if not found.
5276
If the NZ FLAG is set (the file was not found with either the TXT extension or the blank extension), JUMP to 549EH to report a DOS error (File Not Found) and exit.

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.

5279
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H. After a successful file open at 525BH (JP Z,5279H), BC is reset to 0000H to test the open mode state. The value of 5329H (the open mode variable) determines the next action.
527C
LD A,B 78
Transfer Register B (high byte of BC=0000H) into Register A.
527D
OR A,C B1
OR Register A with Register C. Since BC=0000H, A=00H OR 00H=00H, setting the Z FLAG. This tests whether BC is zero.
527E
If the NZ FLAG is set (BC is non-zero, meaning the file is confirmed open and a record number was set), JUMP to 530EH to begin HEX mode output with record positioning. On this first pass through, BC=0000H so this jump is not taken.
5281
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H again. This is the initial value for a countdown counter that will be decremented to step through the command line searching for a starting line/record number argument.
5284
DEC BC 0B
DECrement Register Pair BC by 1. BC was 0000H, so after decrement BC = FFFFh (65535 decimal). This prepares a countdown loop that reads characters from the command line.
5286
OR A,C B1
OR Register A with Register C to test if BC has reached zero (all characters consumed). Sets Z if BC=0000H.
5287
If the Z FLAG is set (BC=0000H, the countdown has expired - meaning 65536 characters were read without finding a carriage return), JUMP to 5298H to begin output from the start. In practice this countdown serves as an upper bound to prevent an infinite loop.
5289
CALL 0013H CD 13 00
GOSUB to ROM routine at 0013H to read one byte from the input device whose DCB is at DE (5528H, the source file FCB). Returns the byte in Register A, with Z set if the device returned a valid byte and NZ if an error or end-of-file condition occurred.
528C
If the NZ FLAG is set (read error or end of file), JUMP to 549EH to report a DOS error and exit.
528F
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). Each line of the file ends with a carriage return; finding one signals the end of the current record.
5291
If the NZ FLAG is set (the byte read was not a carriage return), LOOP BACK to 5289H to read the next byte. This loop skips over all characters in the current record until the carriage return is found.
5293
DEC BC 0B
Loop End
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.
5294
LD A,B 78
Transfer Register B (high byte of BC) into Register A to test if the target line has been reached.
5295
OR A,C B1
OR Register A with Register C to test if BC=0000H. If zero, the required number of lines have been skipped.
5296
If the NZ FLAG is set (BC is still non-zero, more lines must be skipped), LOOP BACK to 5289H to continue scanning. When BC reaches zero, execution falls through to 5298H to begin output from the current file position.

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.

5298
LD HL,(5282H) 2A 82 52
Loop Start
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.
529B
LD BC,54D8H 01 D8 54
Load Register Pair BC with 54D8H, pointing to the decimal display buffer. This is the destination buffer for the formatted record/line number string. The formatter at 542BH writes ASCII decimal digits into the space-padded field at 54D8H-54DBH.
529E
GOSUB to the decimal number formatter at 542BH. Formats the record counter in HL as a decimal number (up to 5 digits) into the buffer pointed to by BC (54D8H). The routine writes ASCII digit characters, with leading zeros suppressed, into the 4-byte field at 54D8H.
52A1
LD DE,5528H 11 28 55
Point Register Pair DE to the source file FCB at 5528H. This is the DCB address required by ROM 0013H to read the next character from the input file.
52A7
If the NZ FLAG is set (read error or end of file from the input device), JUMP to 52F6H to output a final carriage return and return to the error handler. End of file during ASCII print is handled as a clean termination.
52A9
LD (52C2H),A 32 C2 52
Self-Modifying Code
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.
52AC
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H to begin the line-prefix output phase. BC will be used as a counter controlling how many bytes of the number prefix have been printed before the character data begins.
52AF
LD A,B 78
Transfer Register B (high byte of BC=0000H) into Register A for the BC=zero test.
52B0
OR A,C B1
OR Register A with Register C to test if BC=0000H. On the first character of each line, BC is zero (no prefix output has occurred yet).
52B1
If the Z FLAG is set (BC=0000H, the line prefix has not yet been output), JUMP to 52C1H to proceed directly to character output, skipping the prefix printing block. On subsequent characters in the same line, BC is non-zero and the prefix block is bypassed.
52B3
LD HL,54D8H 21 D8 54
Point Register Pair HL to the decimal display buffer at 54D8H containing the formatted record/line number. This string (ending at 54DDH with a 03H terminator) is sent to the output device as the line prefix.
52B6
POP DE D1
POP the drive configuration table entry pointer (401DH or 4025H, pushed at 520EH) from the stack into Register Pair DE. This provides the DCB address for the output device (the *PR printer).
52B7
PUSH DE D5
PUSH Register Pair DE (the output DCB pointer) back onto the stack immediately to preserve it for subsequent uses. The POP/PUSH pattern provides access to DE without permanently removing it from the stack.
52B8
GOSUB to SYS0 routine at 4479H to display the string at HL (54D8H, the formatted line number prefix including the record number and separator characters) to the output device at DE (the stacked printer DCB pointer). The string is terminated by a 03H end-of-string marker at 54DDH.
52BB
LD HL,54D8H 21 D8 54
Point Register Pair HL to the decimal display buffer at 54D8H again, for the incrementing step. After each line is printed, the line counter is incremented using the buffer incrementer routine at 5461H.
52BE
GOSUB to the numeric buffer incrementer at 5461H with HL=54D8H. This routine advances 4 bytes into the buffer (to the digit field), increments the decimal digit stored there as an ASCII character with carry propagation, keeping the line counter current for the next line's prefix.
52C3
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). A carriage return signals the end of the current line in ASCII mode. If the character is a CR, the line is complete and output of the CR terminates the line on the printer.
52C5
If the Z FLAG is set (the character is a carriage return, 0DH), JUMP to 52E3H to output the CR to the printer and handle end-of-line processing (record counter increment and BREAK check).
52C7
POP DE D1
POP the output DCB pointer from the stack into Register Pair DE. Required before calling ROM 001BH to output a character to the printer device.
52C8
PUSH DE D5
PUSH the output DCB pointer back onto the stack immediately to preserve it for subsequent uses.
52C9
PUSH AF F5
Push Register Pair AF (the current character in A and the flags) onto the stack. The character must be preserved across the printer-ready check at 5472H, which modifies AF.
52CA
AND A,7FH E6 7F
AND Register A with 7FH (01111111 binary). This strips the high bit (bit 7) from the character before output, converting any 8-bit (high-bit-set) characters to their 7-bit ASCII equivalents. VTOS files may store characters with bit 7 set as a line-end or attribute marker.
52CC
GOSUB to ROM routine at 001BH to output the character in Register A (with bit 7 cleared) to the device whose DCB is at DE (the printer output DCB from the stack). Z set on success.
52CF
POP AF F1
Restore Register Pair AF from the stack, recovering the original character (before the AND 7FH) in Register A. Bit 7 of the character needs to be tested to determine if it is a line-end marker.
52D0
BIT 7,A CB 7F
Test bit 7 of Register A (the character as read from the input file, before the 7FH mask). If bit 7 is set, this character is a "high-bit" character which VTOS uses as an end-of-line marker in some file formats (equivalent to a carriage return).
52D2
If the NZ FLAG is set (bit 7 of the character is set, meaning this is a high-bit end-of-line marker), GOSUB to 5472H to check the printer-ready status and test for a BREAK keypress. Returns Z if output may continue, NZ if BREAK was pressed.
52D5
If the NZ FLAG is set (BREAK was pressed, detected by 5472H), JUMP to 52EDH to output a final carriage return and return to the DOS READY prompt, abandoning the listing.
52D7
LD DE,5528H 11 28 55
Point Register Pair DE to the source file FCB at 5528H. Required before calling ROM 0013H to read the next character from the input file.
52DA
GOSUB to ROM 0013H to read the next character from the input file at DE (5528H). Returns the byte in A with Z set on success.
52DD
If the NZ FLAG is set (read error or end of file), JUMP to 52F6H to perform final carriage return output and exit cleanly.
52DF
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). Checks if the newly read character is the end-of-line marker.
52E1
If the NZ FLAG is set (the new character is not a carriage return), LOOP BACK to 52C7H to output this character to the printer and read the next one. This inner loop processes each character of the current line until CR is found.
52E4
PUSH DE D5
PUSH the output DCB pointer back onto the stack immediately to preserve it.
52E5
GOSUB to ROM 001BH to output Register A (0DH, carriage return) to the printer device at DE. This terminates the current output line on the printer.
52E8
GOSUB to 5472H to check the printer-ready status and test for a BREAK keypress after outputting the end-of-line carriage return. Returns Z if output may continue, NZ if BREAK was detected.
52EB
Loop End
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.
52EE
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return) to terminate the partial output line on the printer cleanly before exiting.
52F0
GOSUB to ROM 001BH to output the carriage return in Register A to the printer device at DE, ending the current line cleanly before exiting.
52F3
JUMP to SYS0 at 4030H (Error-already-displayed exit) to return to the DOS READY prompt without displaying an additional error message. BREAK was already handled by user intent; no error needs to be reported.
52F7
PUSH AF F5
Push Register Pair AF (containing the error/status byte in A) onto the stack to preserve it across the final carriage-return output call.
52F8
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return) for the final line terminator output to the printer.
52FA
GOSUB to ROM 001BH to output the carriage return in A to the printer device at DE, ending the last line cleanly.
52FD
POP AF F1
Restore Register Pair AF, recovering the error/status code from the failed read (or end-of-file indication) in Register A. The value of A determines whether to exit cleanly or report an error.
52FE
CP A,1CH FE 1C
Compare Register A against 1CH. In VTOS, the value 1CH is the end-of-file indicator returned by the input device when the file is exhausted normally. If A=1CH, the file ended cleanly.
5300
If the Z FLAG is set (A=1CH, normal end-of-file), JUMP to 530BH to return to the DOS READY prompt without an error.
5303
CP A,1DH FE 1D
Compare Register A against 1DH. The value 1DH is an alternate end-of-file or device-end code used by VTOS for some devices. Both 1CH and 1DH are treated as clean end-of-file conditions.
5305
If the Z FLAG is set (A=1DH, alternate clean end-of-file), JUMP to 530BH to return to the DOS READY prompt.
5308
The error code in A is neither 1CH nor 1DH - it is a real I/O error. JUMP to 549EH to pass the error code to the DOS error handler at 4409H, which will display the appropriate error message.

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.

530E
LD BC,0000H 01 00 00
Self-Modifying Code Target
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.
5311
LD DE,5528H 11 28 55
Point Register Pair DE to the source file FCB at 5528H. 4442H (Position to Record) requires DE=FCB to identify which open file to position within.
5314
GOSUB to SYS0 routine at 4442H (Position to Record) with BC=starting record number (from the REC=r parameter parsed earlier at 530FH) and DE=5528H (source file FCB). Positions the file to the requested record. Returns Z on success, NZ on error. The starting record number value is written into 530FH as a self-modification side effect of the parameter parsing performed before this call.
5317
If the NZ FLAG is set (error positioning to the requested record - record number out of range or I/O error), JUMP to 549EH to pass the error code to the DOS error handler.
531A
NOP 00
No operation. This is a padding byte that aligns the subsequent LD HL,(530FH) instruction. It has no functional effect on execution.
531B
LD HL,(530FH) 2A 0F 53
Fetch the record number from 530FH (the self-modified operand of the LD BC instruction at 530EH) into Register Pair HL. This is the REC=r starting record value, now used as the initial value for the HEX mode record/address display counter.
531E
LD BC,54E0H 01 E0 54
Load Register Pair BC with 54E0H, pointing to the second numeric display field within the format buffer (the 4-byte offset/sub-record field at 54E0H-54E3H). This is the destination buffer for the decimal formatter at 542BH when formatting the sub-row address.
5321
GOSUB to the decimal number formatter at 542BH with HL=starting record number and BC=54E0H. Formats the starting record number as decimal digits into the 4-byte field at 54E0H within the HEX display format buffer.
5324
XOR A,A AF
Clear Register A to 00H. This initializes the byte-offset counter that will be stored to 532CH as the operand of the LD A,nn instruction at 532BH.
5325
LD (532CH),A 32 2C 53
Self-Modifying Code
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.
5328
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H, resetting the byte-count register. In the HEX loop, BC is used as a counter that tracks how many bytes of the current 16-byte row have been accumulated.
532D
LD HL,54E6H 21 E6 54
Point Register Pair HL to the hex address label format string at 54E6H. This string contains ":XX = \x03" where the 'XX' positions will be overwritten by the hex address formatter. The full label format is "[record].[offset] :XX = " where XX is the hex representation of the row offset.
5330
GOSUB to SYS0 routine at 4DECH with HL=54E6H (the hex address label format string) and A=current row offset. This routine formats the row offset value in A as two hex digits into the 'XX' placeholder positions within the format string at 54E6H, preparing the address label for output at the start of each 16-byte row.
5333
LD HL,54E0H 21 E0 54
Point Register Pair HL to the full format buffer starting at 54E0H (the spaces and address label region of the display buffer, which includes the formatted decimal sub-row offset and the hex address label filled in by 4DECH).
5336
POP DE D1
POP the output DCB pointer from the stack into Register Pair DE. Required before calling 4479H to output the row address label to the printer.
5337
PUSH DE D5
PUSH the output DCB pointer back immediately to preserve it on the stack.
5338
GOSUB to SYS0 routine at 4479H to display the row address label string at HL (54E0H) to the output printer device at DE. This outputs the record number, row offset, and hex address prefix that begins each 16-byte row of the HEX dump.
533B
LD A,(532CH) 3A 2C 53
Fetch the current row byte-offset counter from 532CH into Register A. After this row's prefix has been printed, the counter is advanced by 10H to prepare the address label for the next row.
533E
ADD A,10H C6 10
ADD 10H (16 decimal) to Register A, advancing the byte-offset counter by one full row's worth of bytes.
5340
LD (532CH),A 32 2C 53
Self-Modifying Code
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.
5343
LD B,10H 06 10
Load Register B with 10H (16 decimal). B is the column counter for the 16-byte HEX row. The inner loop will decrement B after each byte until B reaches zero, completing the row.
5345
LD HL,5518H 21 18 55
Point Register Pair HL to the HEX row byte buffer at 5518H (16 bytes). As each byte is read from the source file, it is stored into this buffer for the printable-character sidebar display after all 16 hex columns have been output.
5348
LD DE,5528H 11 28 55
Inner Loop Start
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.
534B
GOSUB to ROM 0013H to read one byte from the input file at DE (5528H) into Register A. Returns Z on success with the byte in A; NZ on error or end-of-file.
534E
If the Z FLAG is set (byte read successfully), JUMP to 535BH to store this byte and output its hex representation. If NZ (end-of-file or error), fall through to check for break conditions.
5350
PUSH AF F5
Push Register Pair AF (containing the error code in A) onto the stack before testing the error type. The error code must be preserved to determine whether this is a clean end-of-file or a real I/O error.
5351
CP A,1CH FE 1C
Compare Register A against 1CH (normal end-of-file indicator). If A=1CH, the file ended normally during HEX output.
5353
If the Z FLAG is set (A=1CH, clean end of file), JUMP to 535AH to restore AF and continue with end-of-row processing (output the partial row and exit cleanly).
5355
CP A,1DH FE 1D
Compare Register A against 1DH (alternate end-of-file code). If A=1DH, this is also a clean end-of-file condition.
5357
If the NZ FLAG is set (A is neither 1CH nor 1DH - this is a real I/O error, not end-of-file), JUMP to 549EH to invoke the DOS error handler and display the error message.
535C
PUSH DE D5
PUSH the output DCB pointer back onto the stack immediately to preserve it for subsequent output calls.
535D
If the NZ FLAG is set (the previous read returned an end-of-file code, Z was not set by 0013H), JUMP to 5379H to output the end-of-row carriage return for the partial row and finalize the listing. If Z is set (a valid byte was read), execution falls through to process and output the byte.
535F
LD (HL),A 77
Store the byte just read from the input file (in Register A) to the HEX row buffer at (HL) (5518H, advancing with each byte). This saves the raw byte value for the printable-character sidebar that is output after all 16 hex columns.
5360
INC HL 23
INCrement Register Pair HL to advance the HEX row buffer pointer to the next storage position.
5361
GOSUB to the hex byte output routine at 5417H with the byte in Register A. Outputs the byte as two ASCII hexadecimal digits (high nibble then low nibble) to the printer device at DE. After this call the two hex characters for this byte have been sent to the printer.
5364
LD A,20H 3E 20
Load Register A with 20H (ASCII space). A space is output after each two-digit hex value to separate it from the next byte's hex representation.
5366
GOSUB to ROM 001BH to output the space character (20H) in Register A to the printer device at DE, separating this byte's hex digits from the next byte's.
5369
LD A,B 78
Transfer Register B (the remaining column counter, initially 10H=16, counting down) into Register A to test if we have reached the midpoint of the 16-byte row.
536A
CP A,09H FE 09
Compare Register A against 09H. When B=09H, exactly 7 bytes have been output (B started at 10H=16, decremented after each byte, so B=09H means the 8th byte is next). An extra space is inserted at this midpoint to visually split the 16-byte row into two groups of 8.
536C
LD A,20H 3E 20
Load Register A with 20H (ASCII space), ready to output the midpoint extra space if B=09H.
536E
If the Z FLAG is set (B was 09H, midpoint of the row), GOSUB to ROM 001BH to output the extra space (20H) in Register A to the printer device at DE, creating a visual gap between the first and second 8-byte groups in the hex dump.
5371
DEC B 05
DECrement Register B (the remaining column counter) by 1, advancing to the next column position in the 16-byte row.
5372
DEC C 0D
DECrement Register C by 1. C tracks the number of bytes remaining in the current row for the printable-character sidebar (C is not separately initialized here but is decremented in parallel with B to maintain the sidebar byte count).
5373
If the Z FLAG is set (C has reached zero, all bytes in this row have been processed), JUMP to 5379H to output the printable-character sidebar and the row-ending carriage return. All 16 hex pairs for this row are now printed.
5375
LD A,B 78
Transfer Register B (remaining column count) into Register A to test if any more bytes remain in this row.
5376
OR A,A B7
OR Register A with itself to set flags based on the value of B (the remaining column count). Sets Z if B=00H (all columns done), NZ if more columns remain.
5377
Inner Loop End
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.
537A
LD A,B 78
Transfer Register B (the remaining column count at the end of the row) into Register A. If B=10H (16), no bytes were read in this row (immediate end-of-file at the start of a new row). If B=00H, a full row of 16 bytes was completed.
537B
CP A,10H FE 10
Compare Register A against 10H (16). If B=10H, no bytes were read and the row is entirely empty (end-of-file at the row boundary). In this case there is no sidebar to print.
537D
If the Z FLAG is set (B=10H, the row is empty - end-of-file was reached at the start of this row), JUMP to 53D7H to output a final carriage return and skip the sidebar output.
537F
LD A,00H 3E 00
Load Register A with 00H. This value is tested to determine which of two sidebar-alignment algorithms to use (the A=00H path for full rows, A non-zero for partial rows at end-of-file).
5381
OR A,A B7
OR Register A with itself. Since A=00H, the Z FLAG is set. This directs execution to the full-row sidebar alignment path at 5384H.
5382
If the NZ FLAG is set (A was non-zero, meaning a partial row must be padded to align the sidebar), JUMP to 53B5H which handles partial-row spacing. Since A=00H here, this jump is not taken and execution falls through to the full-row sidebar at 5384H.
5384
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return). After a full 16-byte row of hex output, a carriage return is sent before the printable-character sidebar to start it on the same line (the CR moves the print head back without a line feed, overwriting the beginning of the same line - this is a typical TRS-80 printer formatting technique).
5386
GOSUB to ROM 001BH to output the carriage return (0DH) in Register A to the printer device at DE. This repositions the print head to the left margin before the printable-character sidebar is printed.
5389
PUSH BC C5
Push Register Pair BC (containing the remaining column count in B) onto the stack before the spacing loop. BC must be preserved across the padding space output loop.
538A
LD B,0CH 06 0C
Load Register B with 0CH (12 decimal). This is the count of leading spaces printed before the printable-character sidebar begins. The spaces position the sidebar to align with the hex output columns that were already printed on this line.
538E
GOSUB to ROM 001BH to output one space (20H) to the printer device at DE.
5391
Loop End
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.
5393
POP BC C1
Restore Register Pair BC from the stack, recovering the remaining column count in B.
5394
LD A,10H 3E 10
Load Register A with 10H (16 decimal). This is used to compute how many bytes of printable characters to display: the number of bytes in a full row (10H) minus the remaining column count (B, which is 00H for a full row, giving 10H - 00H = 10H = 16 bytes in the sidebar).
5396
SUB A,B 90
SUBtract Register B (remaining column count) from Register A (10H). The result is the number of bytes that were actually read and stored in the HEX row buffer at 5518H. For a full row, B=00H and A remains 10H (16 bytes).
5397
LD B,A 47
Transfer the computed byte count (10H for a full row) into Register B. B now controls the sidebar output loop, iterating once per byte stored in the 5518H buffer.
5398
LD HL,5518H 21 18 55
Point Register Pair HL to the HEX row byte buffer at 5518H. This buffer holds the raw bytes that were read during the hex output phase of this row and are now displayed as printable characters in the sidebar.
539C
INC HL 23
INCrement Register Pair HL to advance the buffer pointer to the next stored byte.
539D
GOSUB to the printable character filter at 5402H with the byte in Register A. If the byte is a printable ASCII character (20H-5AH or 61H-7AH), it is output as-is to the printer via 001BH. If it is non-printable, a period (2EH) is output instead.
53A0
LD A,20H 3E 20
Load Register A with 20H (ASCII space) to output a space after each printable character in the sidebar, maintaining column alignment.
53A2
GOSUB to ROM 001BH to output the space (20H) to the printer device at DE.
53A5
GOSUB to ROM 001BH to output a second space to the printer device at DE. Two spaces after each sidebar character produce wider spacing in the ASCII column, improving readability.
53A8
LD A,B 78
Transfer Register B (the remaining sidebar byte count) into Register A to test the midpoint of the 16-byte row for sidebar alignment.
53A9
CP A,09H FE 09
Compare Register A against 09H. When B=09H the midpoint of the sidebar has been reached (8 of 16 bytes printed). An extra space is inserted at this point to maintain alignment with the midpoint gap in the hex columns.
53AB
LD A,20H 3E 20
Load Register A with 20H (ASCII space) for the optional midpoint extra space.
53AD
If the Z FLAG is set (B=09H, midpoint of the sidebar), GOSUB to ROM 001BH to output the extra space (20H), aligning the sidebar's midpoint gap with the hex columns' midpoint gap.
53B0
Sidebar Output Loop End
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.
53B2
JUMP to 53D7H to output the row-ending carriage return and check for BREAK or end-of-file, after completing the full-row sidebar.

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.

53B5
PUSH BC C5
Push Register Pair BC (containing the remaining column count in B) onto the stack before the padding computation and output loop. BC must be recovered after padding to drive the sidebar output.
53B6
LD A,B 78
Transfer Register B (remaining column count - how many bytes were NOT read in this partial row) into Register A for the padding width calculation.
53B7
RLCA 07
Rotate Register A Left Circular through Accumulator (multiply by 2). The padding space count must account for both the two hex digits and one separator space per missing byte.
53B8
ADD A,B 80
ADD Register B (original remaining column count) to Register A (already doubled). A now holds 3 * B, which is the number of character positions (2 hex digits + 1 space) per missing byte that must be padded with spaces.
53B9
LD B,A 47
Transfer the padding width (3 * original B) into Register B as the loop counter for the space output loop.
53BA
CP A,18H FE 18
Compare Register A against 18H (24 decimal = 8 * 3 spaces). If 8 or more bytes are missing, the midpoint gap space must also be accounted for in the padding. This test determines if the partial row falls before or after the midpoint.
53BC
CCF 3F
Complement Carry Flag. If A >= 18H (CARRY was clear after CP), CCF sets CARRY. If A < 18H (CARRY was set after CP), CCF clears it. This inverts the carry to select whether the extra midpoint space is needed.
53BD
LD A,03H 3E 03
Load Register A with 03H. This is the base extra padding count (for the midpoint gap space, aligned to the sidebar alignment scheme).
53BF
ADC A,B 88
ADD Register B (3 * missing byte count) plus the Carry Flag (1 if midpoint padding needed, 0 if not) to Register A (03H). The result in A is the total number of spaces to output: 03H + (3 * missing_bytes) + (1 if missing_bytes >= 8).
53C0
LD B,A 47
Transfer the total padding space count into Register B as the DJNZ loop counter.
53C3
GOSUB to ROM 001BH to output one space (20H) to the printer device at DE.
53C6
Loop End
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.
53C8
POP BC C1
Restore Register Pair BC from the stack, recovering the original remaining column count in B.
53C9
LD A,10H 3E 10
Load Register A with 10H (16 decimal) to compute the number of bytes in the partial sidebar (same calculation as the full-row sidebar path).
53CB
SUB A,B 90
SUBtract Register B (remaining/missing column count) from Register A (10H). The result is the number of bytes that were actually read and buffered in 5518H for this partial row's sidebar.
53CC
LD B,A 47
Transfer the partial sidebar byte count into Register B as the loop counter for the sidebar output loop below.
53CD
LD HL,5518H 21 18 55
Point Register Pair HL to the HEX row byte buffer at 5518H for the partial row's printable-character sidebar output.
53D1
INC HL 23
INCrement Register Pair HL to advance the buffer pointer.
53D2
GOSUB to the printable character filter at 5402H to output the byte as its printable ASCII character or as a period if non-printable.
53D5
Partial Sidebar Loop End
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.
53D9
GOSUB to ROM 001BH to output the carriage return (0DH) in Register A to the printer device at DE, ending the current HEX dump row.
53DC
POP AF F1
Restore Register Pair AF from the stack (pushed at 5379H), recovering the end-of-file or byte status code in A and restoring the flags state for the next test.
53DD
LD A,1CH 3E 1C
Load Register A with 1CH (the normal end-of-file indicator) to test whether the previous row's read terminated with end-of-file. This is compared against the status that was in AF when it was pushed at 5379H.
53DF
If the NZ FLAG is set (the row ended for a reason other than end-of-file - either a full 16-byte row was completed or an error occurred), JUMP to 52F6H for end-of-listing processing. A full row indicates more data may follow; a real error is also handled there.
53E2
GOSUB to 5472H to check the printer-ready status and test for a BREAK keypress after outputting the row. Returns Z if output may continue, NZ if BREAK was pressed.
53E5
If the NZ FLAG is set (BREAK was pressed), JUMP to 52EDH to output a final carriage return and return to the DOS READY prompt.
53E8
LD A,C 79
Transfer Register C (the row byte counter remainder) into Register A to test whether the HEX loop should advance to the next record or continue within the current record.
53E9
OR A,A B7
OR Register A with itself to set flags based on C. Z if C=00H (all bytes in this record have been displayed and we should move to the next record); NZ if bytes remain in the current record.
53EA
If the NZ FLAG is set (C is non-zero, more rows remain in the current record), JUMP to 532BH to prepare the next row's address label and output the next 16 bytes of the same record. Each record in HEX mode is displayed as multiple 16-byte rows.
53ED
POP DE D1
POP the output DCB pointer from the stack into Register Pair DE. A record boundary has been reached; a blank line separator is output between records in HEX mode.
53EE
PUSH DE D5
PUSH the output DCB pointer back onto the stack immediately to preserve it.
53EF
LD A,20H 3E 20
Load Register A with 20H (ASCII space). A space followed by a carriage return creates a blank separator line between records in the HEX dump output.
53F1
GOSUB to ROM 001BH to output the space (20H) to the printer device at DE.
53F4
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return) to complete the blank separator line between records.
53F6
GOSUB to ROM 001BH to output the carriage return (0DH), completing the blank line between records.
53F9
LD HL,54E0H 21 E0 54
Point Register Pair HL to the format buffer at 54E0H (the sub-row offset display field) for incrementing. After each complete record, the record counter in this field is incremented via 5461H.
53FC
GOSUB to the numeric buffer incrementer at 5461H with HL=54E0H. This increments the decimal record number stored in the display buffer, advancing the record counter for the address label of the next record in the HEX dump.
53FF
Outer Loop End
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.

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').

5402
CP A,20H FE 20
Compare Register A against 20H (ASCII space, the lowest printable character). If A is less than 20H (a control character), the CARRY FLAG is set.
5404
If the CARRY FLAG is set (A < 20H, the byte is a non-printable control character), JUMP to 5412H to substitute a period (2EH) and output it.
5406
CP A,5BH FE 5B
Compare Register A against 5BH (the character just above 'Z'=5AH). Characters in the range 20H-5AH are all printable (space, digits, uppercase letters, and common punctuation). If A < 5BH, the character is in the first printable range.
5408
If the CARRY FLAG is set (A < 5BH, the byte is in the range 20H-5AH, printable), JUMP to 5414H to output it as-is via ROM 001BH.
540A
CP A,61H FE 61
Compare Register A against 61H (lowercase 'a'). Characters in the range 5BH-60H (the characters '[', '\', ']', '^', '_', '`') are treated as non-printable by this routine. If A < 61H, the byte falls in this gap.
540C
If the CARRY FLAG is set (A < 61H, the byte is in the non-printable gap 5BH-60H), JUMP to 5412H to substitute a period.
540E
CP A,7BH FE 7B
Compare Register A against 7BH (the character just above 'z'=7AH). Lowercase letters 61H-7AH are printable. If A < 7BH, the byte is a lowercase letter.
5410
If the CARRY FLAG is set (A < 7BH, the byte is in the range 61H-7AH, lowercase printable), JUMP to 5414H to output it as-is.

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.

5417
PUSH AF F5
Push Register Pair AF onto the stack to preserve the byte in Register A across the high-nibble output. The original byte value must be recovered for the low-nibble output.
5418
RRCA 0F
Rotate Register A Right Circular through Accumulator by 1 bit.
5419
RRCA 0F
Rotate Register A Right Circular by 1 bit (second of four rotations).
541A
RRCA 0F
Rotate Register A Right Circular by 1 bit (third of four rotations).
541B
RRCA 0F
Rotate Register A Right Circular by 1 bit (fourth of four rotations). After four RRCA instructions, the high nibble of the original byte is now in the low 4 bits of A and the low nibble is in the high 4 bits. The high nibble is now ready for the nibble-to-hex conversion at 5420H.
541C
GOSUB to the nibble-to-hex output routine at 5420H with the high nibble of the original byte in the low 4 bits of A. 5420H converts the nibble to an ASCII hex digit and outputs it to the printer.
541F
POP AF F1
Restore Register Pair AF from the stack, recovering the original byte in Register A. The low nibble is now in bits 3-0 of A, ready for the second hex digit 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.

5420
AND A,0FH E6 0F
AND Register A with 0FH (00001111 binary) to isolate the low 4 bits (one hex digit value, 0-15) and discard the high 4 bits.
5422
ADD A,90H C6 90
ADD 90H to Register A. For values 0-9 this produces 90H-99H. For values 10-15 (A-F) this produces 9AH-9FH. The subsequent DAA instruction will adjust these into the correct BCD/ASCII range.
5424
DAA 27
Decimal Adjust Accumulator. For values 0-9 (producing 90H-99H), DAA produces 90H-99H with CARRY clear. For values 10-15 (producing 9AH-9FH), DAA produces 00H-05H with CARRY set. This exploits BCD arithmetic to generate the correct ASCII digit base values.
5425
ADC A,40H CE 40
ADD 40H plus the Carry Flag to Register A. For digits 0-9 (A=90H-99H, CARRY=0): 90H-99H + 40H = D0H-D9H. For digits A-F (A=00H-05H, CARRY=1): 00H-05H + 40H + 1H = 41H-46H. The final DAA corrects D0H-D9H to 30H-39H ('0'-'9').
5427
DAA 27
Decimal Adjust Accumulator again. For the digit range 0-9 (A=D0H-D9H), DAA produces 30H-39H ('0'-'9' in ASCII). For the A-F range (A=41H-46H), no adjustment is needed since 41H-46H is already the correct ASCII range for 'A'-'F'. The result in A is the correct ASCII hex digit.
5428
JUMP to ROM 001BH to output the ASCII hex digit in Register A to the printer device at DE. The JP substitutes for CALL/RET - the return from 001BH goes directly to 5417H's caller (for the high nibble) or to the caller of 5420H (for the low nibble).

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.

542B
LD DE,2710H 11 10 27
Load Register Pair DE with 2710H (10000 decimal). This is the divisor for the ten-thousands digit. The formatter works by successive subtraction of each place value.
542E
GOSUB to the digit-extraction subroutine at 5446H with DE=2710H (10000) and HL=value, BC=buffer pointer. Extracts the ten-thousands digit of HL, writes it to (BC), advances BC, and leaves the remainder in HL.
5431
LD DE,03E8H 11 E8 03
Load Register Pair DE with 03E8H (1000 decimal). This is the divisor for the thousands digit.
5434
GOSUB to 5446H with DE=03E8H (1000) to extract and write the thousands digit of the remaining value in HL.
5437
LD DE,0064H 11 64 00
Load Register Pair DE with 0064H (100 decimal). This is the divisor for the hundreds digit.
543A
GOSUB to 5446H with DE=0064H (100) to extract and write the hundreds digit.
543D
LD DE,000AH 11 0A 00
Load Register Pair DE with 000AH (10 decimal). This is the divisor for the tens digit.
5440
GOSUB to 5446H with DE=000AH (10) to extract and write the tens digit.
5443
LD DE,0001H 11 01 00
Load Register Pair DE with 0001H (1 decimal). This is the ones-place value. Execution falls through into 5446H to extract and write the final (ones) digit.

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.

5449
If the CARRY FLAG is set (HL went negative - DE was too large to subtract again), JUMP to 544EH to restore HL by adding DE back (undoing the last underflowing subtraction) and convert the count in A to an ASCII digit.
544B
INC A 3C
INCrement Register A by 1, counting one more successful subtraction of DE from HL. This is the digit count for the current decimal place.
544C
Digit Count Loop End
JUMP back to 5447H to attempt another subtraction. This loop runs until HL goes negative, giving the digit count in A.
544F
ADD A,30H C6 30
ADD 30H to Register A to convert the digit count (0-9) to its ASCII decimal digit ('0'=30H through '9'=39H).
5451
CP A,30H FE 30
Compare Register A against 30H (ASCII '0'). If A=30H, this digit is zero and may be suppressed if it is a leading zero (i.e., if no non-zero digit has been output yet to the left of this position).
5453
If the NZ FLAG is set (A is not '0' - this is a non-zero digit that must always be written), JUMP to 545EH to write the digit to the buffer at (BC) and advance BC.
5455
DEC BC 0B
DECrement Register Pair BC to look at the buffer position just before the current write position. The character there reveals whether a non-zero digit has already been written (leading-zero suppression check).
5456
LD A,(BC) 0A
Fetch the character currently in the buffer at (BC) (the position before this digit) into Register A. If it is a space (20H), no significant digit has been written yet to the left of this position - this zero is a leading zero and should be suppressed.
5457
INC BC 03
INCrement Register Pair BC back to the current digit's write position.
5458
CP A,20H FE 20
Compare Register A against 20H (ASCII space). If the previous buffer position holds a space, this '0' is a leading zero that should be left as a space (suppressed). If the previous position holds a digit character, this '0' is significant and must be written.
545A
If the Z FLAG is set (the previous position is a space - leading zero detected), JUMP to 545EH. The '0' digit in A is not written; instead the buffer position is left as-is (preserving its existing space or previously written content), and BC is advanced past this position.
545C
LD A,30H 3E 30
Load Register A with 30H (ASCII '0'). The previous position was a digit (not a space), so this '0' is significant and must be written explicitly.
545F
INC BC 03
INCrement Register Pair BC to advance the buffer pointer to the next digit position.
5460
RET C9
Return to the caller (542BH) with HL holding the remainder after this digit's extraction, BC pointing to the next buffer position, and A containing the digit just written. The caller loads the next place value into DE and calls 5446H again for the next digit.

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.

5461
INC HL 23
INCrement Register Pair HL to advance past the first byte of the buffer region.
5462
INC HL 23
INCrement Register Pair HL (second advance).
5463
INC HL 23
INCrement Register Pair HL (third advance).
5464
INC HL 23
INCrement Register Pair HL (fourth advance). HL now points 4 bytes into the buffer, at the ones-digit position of the number field (e.g., the last digit of the 4-digit line or record number).
5466
OR A,30H F6 30
OR Register A with 30H. If the buffer position holds a space (20H), this converts it to '0' (30H) before incrementing. If it already holds a digit ('0'-'9'), ORing with 30H leaves it unchanged.
5468
INC A 3C
INCrement Register A by 1 to advance to the next digit ('0' becomes '1', '9' becomes ':' = 3AH).
5469
LD (HL),A 77
Store the incremented digit value back to (HL) in the buffer.
546A
SUB A,3AH D6 3A
SUBtract 3AH from Register A. If the incremented digit was ':' (3AH = the character after '9'), the result is 00H with CARRY clear, indicating a carry out of this digit position. If A was less than 3AH (a valid digit '1'-'9'), the CARRY FLAG is set indicating no carry is needed.
546C
RET C D8
If the CARRY FLAG is set (A was a valid digit that did not overflow '9'), RETurn to the caller. No carry propagation is needed; the increment is complete.
546D
LD (HL),30H 36 30
Store 30H (ASCII '0') to (HL), resetting this digit position to '0' after the overflow. The carry must propagate to the next more-significant digit to the left.
546F
DEC HL 2B
DECrement Register Pair HL to move left to the next more-significant digit position in the buffer.
5470
Carry-Propagation Loop End
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).

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.

5472
LD A,(430FH) 3A 0F 43
Fetch the system state flags byte from 430FH (the critical multi-bit system state register) into Register A. Bit 5 of this byte (20H) indicates re-entry/interactive mode. The printer-ready check is only performed in interactive mode.
5475
AND A,20H E6 20
AND Register A with 20H (00100000 binary) to isolate bit 5 of the system state flags. If bit 5 is clear (not in interactive mode), A becomes 00H.
5477
XOR A,20H EE 20
XOR Register A with 20H. If bit 5 was set (interactive mode, A=20H after AND), XOR produces 00H setting the Z FLAG. If bit 5 was clear (A=00H after AND), XOR produces 20H setting NZ. The effect: Z if interactive mode is active, NZ if not.
5479
RET Z C8
If the Z FLAG is set (interactive mode is not active), RETurn immediately with Z set. No printer-ready check or BREAK detection is performed outside interactive mode; the caller may proceed with output.
547A
LD A,(3840H) 3A 40 38
Fetch the disk/keyboard status byte from the memory-mapped keyboard row at 3840H into Register A. On the Model I, 3840H is keyboard row 6 (ENTER CLEAR BREAK UP DOWN LEFT RIGHT SPACE). Bit 2 of this register also serves as the printer-busy indicator on systems with an Expansion Interface.
547D
AND A,04H E6 04
AND Register A with 04H (00000100 binary) to isolate bit 2 of the disk/printer status port. Bit 2 set indicates the printer is busy (not ready to accept data).
547F
RET NZ C0
If the NZ FLAG is set (bit 2 is set, printer is busy), RETurn with NZ. The caller interprets NZ as "output should stop". This prevents sending data to a printer that is not ready.
5480
LD A,(3801H) 3A 01 38
Fetch the keyboard row at 3801H (keyboard row 0: @ A B C D E F G) into Register A. On the Model I, this row also reflects RS-232 receive status. Bit 0 of 3801H is tested to check whether an RS-232 character has been received.
5483
AND A,01H E6 01
AND Register A with 01H to isolate bit 0 of keyboard row 3801H. If bit 0 is set, an RS-232 character is waiting. If bit 0 is clear, no RS-232 input is pending.
5485
RET Z C8
If the Z FLAG is set (bit 0 of 3801H is clear - no RS-232 input), RETurn with Z. The printer is ready and no RS-232 input interruption is pending; the caller may continue output.
5486
LD A,(3880H) 3A 80 38
Fetch the keyboard row at 3880H (keyboard row 7: SHIFT key) into Register A. On the Model I, 3880H also reflects RS-232 secondary status. Bit 0 of 3880H provides additional RS-232 readiness information.
5489
AND A,01H E6 01
AND Register A with 01H to isolate bit 0 of keyboard row 3880H (RS-232 secondary status).
548B
RET Z C8
If the Z FLAG is set (bit 0 of 3880H is clear - RS-232 not active), RETurn with Z. Printer and RS-232 checks passed; output may continue.
548F
AND A,04H E6 04
AND Register A with 04H to isolate bit 2 of 3840H (printer-busy status).
5491
RET NZ C0
If the NZ FLAG is set (printer is still busy), RETurn with NZ, signalling the caller to pause or abort output.
5492
GOSUB to ROM 002BH to wait for and return a keypress from the keyboard. Returns the ASCII code of the key pressed in Register A. If no key is currently pressed this call waits until one is available.
5495
OR A,A B7
OR Register A with itself to set flags based on A. If A=00H (no key, null return), the Z FLAG is set. If a key was pressed, A is non-zero and NZ is set.
5496
If the Z FLAG is set (A=00H, no key pressed or a null keypress), LOOP BACK to 548CH to re-poll the printer status and keyboard. This loop continues until either a key is pressed or the printer becomes busy.
5498
CP A,60H FE 60
Compare Register A against 60H. The value 60H is the ASCII code for the grave accent character, used by VTOS as an internal "no key" or "ignore" code distinct from 00H. If A=60H the keypress is treated as no key.
549A
If the Z FLAG is set (A=60H, the internal no-key code), LOOP BACK to 548CH to continue polling. Only real keypresses (A non-zero and not 60H) break out of the poll loop.
549C
XOR A,A AF
Clear Register A to 00H. A real key was pressed (any key other than 60H terminates the listing). Setting A=00H and the Z FLAG allows the caller to test for BREAK with a JR Z / JR NZ after this call returns.
549D
RET C9
Return to the caller with A=00H and Z set if no key was pressed (normal continuation) OR with Z clear (NZ) if a real key was detected. The caller uses the NZ condition to detect BREAK and abort the listing.

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.

549E
OR A,40H F6 40
OR Register A with 40H (01000000 binary). This sets bit 6 of the error code, which in VTOS signals to the error handler at 4409H that the error originated at command interpreter level (as opposed to a system-level or overlay-internal error).
54A0
JUMP to SYS0 at 4409H (DOS Error Exit) with the error code in Register A. The error handler displays the appropriate error message (looked up via the SYS4 dictionary system) and returns to the DOS READY prompt.

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).

54A3
LD HL,54ACH 21 AC 54
Point Register Pair HL to the "FILE SPEC REQUIRED" error string at 54ACH (19 bytes including the 0DH carriage-return terminator).
54A6
GOSUB to SYS0 routine at 447BH to display the error string at HL ("FILE SPEC REQUIRED" followed by a carriage return) with CONFIG/SYS processing. The message is sent to the console output.
54A9
JUMP to SYS0 at 4030H (Error-already-displayed exit) to return to the DOS READY prompt. The error message has already been displayed; no additional error code reporting is needed.

ASCII error message string. The disassembler incorrectly decodes these bytes as Z80 instructions; they are text data.

54AC-54BE
DEFM "FILE SPEC REQUIRED" + 0DH 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
19-byte ASCII string: "FILE SPEC REQUIRED" followed by a carriage return (0DH). Displayed by the error handler at 54A3H when no valid filespec was found on the command line or when a device spec ('*') was given instead.

Displayed when the file open utility at 4476H returns an error, indicating an invalid or unrecognized parameter in the command line.

54BF
LD HL,54C8H 21 C8 54
Point Register Pair HL to the "PARAMETER ERROR" error string at 54C8H (16 bytes including the 0DH terminator).
54C2
GOSUB to SYS0 routine at 447BH to display the "PARAMETER ERROR" string at HL with CONFIG/SYS processing.
54C5
JUMP to SYS0 at 4030H (Error-already-displayed exit) to return to the DOS READY prompt.

ASCII error message string. The disassembler incorrectly decodes these bytes as Z80 instructions; they are text data.

54C8-54D7
DEFM "PARAMETER ERROR" + 0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
16-byte ASCII string: "PARAMETER ERROR" followed by a carriage return (0DH). Displayed by the handler at 54BFH when the file open utility returned a parameter error.

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).

54D8-54DB
DEFM " " (4 spaces) 20 20 20 20
4-byte decimal record/line number field. Initially all spaces. The decimal formatter at 542BH writes the current record or line number into this field as ASCII decimal digits with leading-zero suppression. This field appears at the left of the printed prefix.
54DC
DEFB 2EH ('.') 2E
Period separator character (ASCII '.') between the record number field (54D8H) and the sub-field (54E0H). Printed as the separator in the line-number prefix (e.g., " 1." in ASCII mode).
54DD
DEFB 03H (end of string) 03
End-of-string marker (03H) terminating the first sub-field of the format buffer. Routines that display the record number prefix stop reading at this byte. In ASCII mode, only 54D8H-54DDH are displayed as the line number prefix.
54DE-54E1
DEFM " " (4 spaces) 20 20 20 20
4-byte row sub-address field. Initially all spaces. In HEX mode, the decimal formatter at 542BH writes the row byte offset into this field (e.g., " 0", " 16", " 32" for successive rows within a record).
54E2-54E4
DEFM " " (3 spaces) 20 20 20
3-byte spacing field between the sub-address digits and the hexadecimal address label. These spaces visually separate the decimal sub-address from the colon-hex-address that follows.
54E5-54EB
DEFM ":XX = " + 03H 3A 58 58 20 3D 20 03
7-byte hexadecimal address label template. The ':' introduces the hex address; 'XX' is a two-character placeholder that is overwritten at runtime by the hex address display routine at 4DECH with the two ASCII hex digits of the row's byte offset; ' = ' follows the hex value; 03H ends the field. The complete HEX row prefix reads as e.g., " 1. 0 :00 = ".
54EC-54EE
DEFM "TXT" 54 58 54
3-byte default file extension string "TXT". Passed to the extension insertion routine at 4473H (CALL 4473H at 524EH) to append the extension to the filespec if none was given. VTOS tries TXT first, then blank extension.

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.

54EF-550EH
DEFS 32 (32 bytes, FCB work area) 4C 49 4E 45 20 20 82 52 4E 55 4D 20 20 AD 52 48 45 78 20 20 7A 52 52 45 43 20 20 0F 53 4C 52 4C
32-byte input File Control Block. The disassembler decodes these bytes as Z80 instructions (interpreting "LINE " etc. as mnemonics), but this region is the FCB data structure populated by the file open call at 4476H. The FCB tracks the open file's drive configuration, sector buffer address, current byte offset, record length, sector position, and total sector count per the VTOS FCB layout (IX-indexed fields documented in the master reference).

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.

550F-5516
DEFS 8 (8 bytes, FCB/option continuation) 20 20 29 53 4C 52 4C 20
8 bytes of FCB overflow or option parameter data. These bytes complete the option parameter recognition table entries (LRL option data) or extend the FCB. The disassembler incorrectly renders them as instructions; the actual content is determined by the option table format parsed by 4DECH.
5517
DEFB 00H (table terminator) 00
Zero byte marking the end of the option parameter table that begins at 54EFH. The option scanner at 4DECH reads entries until it encounters a 00H terminator byte, indicating no more options are defined.

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 RangePurpose
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

AddressName and Purpose
5200HATTRIB 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.
5237HParameter 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.
5250HPROT= 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.
52CAHACC= 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.
52DFHUPD= Handler
Entry: HL = pointer past 'U'. Identical flow to ACC= handler but stores result at 51F5H and sets B=04H as the presence flag.
52F4HPassword 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.
52F7HAdvance-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.
530CHPassword 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.
5323HDirectory 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.
536BHError 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.
5370HFILE 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.
53A4HPARAMETER ERROR Exit
Displays "PARAMETER ERROR" + 0DH via 447BH and exits via 4030H. Reached when a keyword is unrecognized or a value is missing/invalid.
53BDHUppercase 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.

5200
LD DE,53DBH 11 DB 53
Point Register Pair DE to the filespec template stub at 53DBH. This single-byte area serves as the template argument for the Extract Filespec routine. It contains the default-extension stub (a null or space byte) that CALL 441CH will use to parse the command line filespec.
5203
GOSUB to SYS0 routine at 441CH (Extract Filespec). On return, HL points to the first character of the parameter list following the filespec, DE = pointer to the parsed filespec buffer, and the NZ flag indicates an error (missing or malformed filespec). The Z flag indicates success.
5206
If the NZ FLAG (Not Zero) has been set by CALL 441CH indicating that no valid filespec was found, JUMP to 5370H to display "FILE SPEC REQUIRED" and exit via 4030H.
5209
LD A,(DE) 1A
Fetch the first byte of the parsed filespec from the address held in Register Pair DE. This byte is the drive-number field of the filespec descriptor. A drive specified as '*' (wildcard) is encoded differently from a numeric drive.
520A
CP A,2AH FE 2A
Compare Register A against 2AH (ASCII *). If Register A equals 2AH, the Z FLAG is set, meaning the user specified a wildcard drive designation. ATTRIB cannot operate on a wildcard drive because it must open a specific file.
520C
If the Z FLAG has been set (drive field is '*', a wildcard), JUMP to 5370H to display "FILE SPEC REQUIRED" and exit. A wildcard drive is not permitted for ATTRIB.
520F
PUSH HL E5
Save Register Pair HL (the command line pointer, currently pointing at the parameter list following the filespec) onto the stack so it survives the upcoming file-open call.
5210
LD B,00H 06 00
Load Register B with 00H. This sets the file-open mode to 0 (read/write access), which is required so that ATTRIB can both read and update the file's directory entry.
5212
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H, the standard SYS0 directory/GAT sector buffer. This is the FCB (File Control Block) area that the file-open routine will populate with the directory entry data for the named file.
5215
GOSUB to SYS0 routine at 4424H (Basic File Open). Entry: B = access mode (00H = read/write), HL = FCB address (4200H), DE = filespec pointer. On return, Z flag set = success (FCB populated with directory entry), NZ = error (file not found, access denied, etc.).
521B
If the NZ FLAG has been set by CALL 4424H indicating that the file could not be opened (not found, wrong drive, protection violation, etc.), JUMP to 536BH to OR A,40H and exit via DOS error handler 4409H.
521B
POP HL E1
Restore Register Pair HL from the stack, recovering the command line pointer saved at 520FH. HL now points to the first character of the parameter list following the filespec (typically a space or '(').

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.

521C
LD A,(53DCH) 3A DC 53
Fetch the drive number byte from 53DCH, which is embedded within the filespec template data area immediately following the template stub at 53DBH. This byte was filled in by CALL 441CH at 5203H and holds the numeric drive number (0-7) from the filespec.
521F
AND A,07H E6 07
Mask Register A with 07H, isolating the low 3 bits which represent the drive number (0-7). Upper bits may contain status flags from the filespec parser and must be stripped.
5221
LD A,25H 3E 25
Load Register A with 25H. This is the VTOS error code for "FILE PROTECTED" or a similar protection-related error, pre-loaded into A as the error code to return if the drive-number test that follows fails. (The AND result has already been discarded by this LD A,nn; see the JR NZ below.)
5223
If the NZ FLAG has been set (the drive-number field from the AND instruction was non-zero, meaning the drive number is not drive 0), LOOP BACK to 521BH. Wait - the AND result was consumed by LD A,25H; the NZ here tests the flag set by AND A,07H at 521FH, not the LD. If the drive field was non-zero, execution jumps back to 521BH (the JP NZ,536BH instruction), passing A=25H as the error code. This rejects ATTRIB attempts on drives other than drive 0 when the filespec specified a non-zero drive.

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.

5225
PUSH HL E5
Save Register Pair HL (the current command line pointer) onto the stack. This preserves the position before entering the space-skip loop, in case it is needed for error recovery.
5226
GOSUB to the Uppercase Converter / Advance Scanner at 53BDH. This subroutine advances HL past leading spaces and converts each character in-place to uppercase until a non-alphabetic character is reached. On return, HL points at the first non-space, non-alpha character (expected to be '(' or end-of-line).
5229
POP HL E1
Restore Register Pair HL from the stack. The CALL to 53BDH did its work in place on the command line buffer; HL is now back to the position saved at 5225H to resume scanning from there.

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.

522A
LD A,(HL) 7E
Fetch the current character from the command line position in Register Pair HL into Register A.
522B
CP A,28H FE 28
Compare Register A against 28H (ASCII (). If Register A equals 28H, the Z FLAG is set - the opening parenthesis has been located.
522D
If the Z FLAG has been set ('(' found), JUMP to 5237H to begin parsing the parameter list inside the parentheses.
522F
CP A,20H FE 20
Compare Register A against 20H (ASCII space). If Register A equals 20H, the Z FLAG is set - the current character is a space that should be skipped.
5231
If the NZ FLAG has been set (the character is neither '(' nor space, i.e., it is some unexpected character), JUMP to 538CH to display "-NOTHING DONE-" and exit via 4030H.
5234
INC HL 23
INCrement Register Pair HL by 1, advancing the command line pointer past the space character just examined.
5235
LOOP BACK to 522AH to fetch and examine the next character. This continues until either '(' or a non-space character is found.

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.

5237
INC HL 23
INCrement Register Pair HL by 1, advancing the command line pointer past the opening '(' (or past the ',' separator encountered at 52A1H when looping back from a subsequent parameter).
5238
LD A,(HL) 7E
Fetch the current character from the command line into Register A. This is expected to be the first letter of the next parameter keyword: I (INV), V (VIS), A (ACC=), U (UPD=), or P (PROT=).
5239
CP A,49H FE 49
Compare Register A against 49H (ASCII I). If Register A equals 49H, the Z FLAG is set - the INV keyword has been identified.
523B
If the Z FLAG has been set (I found), JUMP to 52A6H to process the INV parameter.
523D
CP A,56H FE 56
Compare Register A against 56H (ASCII V). If Register A equals 56H, the Z FLAG is set - the VIS keyword has been identified.
523F
If the Z FLAG has been set (V found), JUMP to 52B8H to process the VIS parameter.
5241
CP A,41H FE 41
Compare Register A against 41H (ASCII A). If Register A equals 41H, the Z FLAG is set - the ACC= keyword has been identified.
5243
If the Z FLAG has been set (A found), JUMP to 52CAH to process the ACC= (access password) parameter.
5246
CP A,55H FE 55
Compare Register A against 55H (ASCII U). If Register A equals 55H, the Z FLAG is set - the UPD= keyword has been identified.
5248
If the Z FLAG has been set (U found), JUMP to 52DFH to process the UPD= (update password) parameter.
524B
CP A,50H FE 50
Compare Register A against 50H (ASCII P). If Register A equals 50H, the Z FLAG is set - the PROT= keyword has been identified.
524D
If the NZ FLAG has been set (the character is not P, and it was also not I, V, A, or U - all of which would have jumped earlier), JUMP to 53A4H to display "PARAMETER ERROR" and 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).

5250
GOSUB to the Advance-to-Value Scanner at 52F7H. This routine advances HL past any characters up to and including the '=' sign (INC HL past '='), then positions HL at the first character of the value. Returns Z flag set if the next character is a delimiter (nothing follows '='), NZ if a value is present.
5253
If the Z FLAG has been set (no value was provided after PROT=), JUMP to 53A4H to display "PARAMETER ERROR". A protection level must be specified.
5256
PUSH HL E5
Save Register Pair HL (the command line pointer, now at the protection level keyword) onto the stack. It will be restored after the keyword table scan completes.
5257
LD B,08H 06 08
Load Register B with 08H. This initializes the loop counter for the 8-entry protection level keyword table. B will be DECremented by the DJNZ instruction at 5267H, iterating through all 8 entries.
5259
LD DE,(51E0H) ED 5B E0 51
Load Register Pair DE with the 16-bit value stored at 51E0H (the first two bytes of the password work buffer). This loads the first two characters of the typed keyword from the buffer into D and E for comparison against the table entries.
525D
LD HL,53CBH 21 CB 53
Point Register Pair HL to 53CBH, the start of the PROT= keyword comparison table. Each entry is 2 bytes (the first two uppercase ASCII characters of the keyword): AL(L), EX(EC), RE(AD), WR(IT), RN (RENA), NA(ME), KI(LL), FU(LL).

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).

5260
LD A,(HL) 7E
Fetch the first comparison byte of the current keyword table entry from the address in Register Pair HL into Register A.
5261
INC HL 23
INCrement Register Pair HL by 1, advancing the table pointer to the second byte of the current entry.
5262
CP A,E BB
Compare Register A (first table byte) against Register E (first character of the user-typed keyword). If Register A equals Register E, the Z FLAG is set - the first character matches.
5263
If the Z FLAG has been set (first character matches), GOSUB to 526CH to check the second character and compute the protection level. If the second character also matches, the subroutine encodes the level and returns via POP AF / LD A,B; if not, it returns NZ and execution falls through to INC HL below.
5266
INC HL 23
INCrement Register Pair HL by 1, advancing past the second byte of the current table entry to the start of the next entry. (When CALL Z,526CH is taken and succeeds, the RET inside 526CH returns directly to 5267H skipping this INC.)
5267
DECrement B and loop back to 5260H if not zero. Continues scanning through all 8 table entries. If B reaches zero (all entries exhausted without a match), falls through.
5269
JUMP to 53A4H to display "PARAMETER ERROR". This is reached only if none of the 8 table entries matched the user-typed keyword - the protection level value is unrecognized.

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.

526C
LD A,(HL) 7E
Fetch the second byte of the current keyword table entry (pointed to by HL after the INC at 5261H) into Register A. This is the second character of the table keyword for comparison.
526D
CP A,D BA
Compare Register A (second table byte) against Register D (second character of the user-typed keyword, loaded from 51E0H+1 via LD DE,(51E0H)). If Register A equals Register D, the Z FLAG is set - both characters match and this is the correct keyword.
526E
RET NZ C0
If the NZ FLAG has been set (second character does not match), RETurn to the caller at 5263H. The CALL Z,526CH returns with NZ, and the loop continues to the next table entry.

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.

526F
POP AF F1
Restore Register Pair AF from the stack (the PUSH HL at 5256H saved HL, but a PUSH AF was placed on the stack by the parameter loop at 528CH; this POP AF recovers the B value and flags state from that enclosing call context, cleaning the stack before the final RET).
5270
LD A,B 78
Load Register A with Register B (the current loop counter: 8 for the first table entry AL/FULL, 7 for EX/EXEC, 6 for RE/READ, 5 for WR/WRIT, 4 for RN/RENA, 3 for NA/NAME, 2 for KI/KILL, 1 for FU/FULL). This raw counter is then adjusted to produce the correct VTOS protection level byte.
5271
SUB A,08H D6 08
SUBtract 08H from Register A. For the first table entry (B=8), this produces 00H. The result determines whether the matched keyword is the first entry (ALL/FULL at position 8 = result 00H) or one of the others.
5273
If the Z FLAG has been set (result is zero, meaning the matched entry was the first one - AL for ALL/FULL), JUMP to 5286H to store protection level 00H (FULL access). Wait - the ADD A,07H below first executes for non-zero cases; for the zero case (entry 1), the jump skips the adjustment and stores 00H... but the instruction at 5275H is conditional and skipped. The level for ALL is computed from the JR Z path, which jumps directly to the store.
5275
ADD A,07H C6 07
ADD 07H to Register A. After SUB 08H, A holds the negative index (FFH for B=8 already jumped, or 06H for EX/B=7, 05H for RE/B=6, etc., unsigned: 07H-06H-05H-04H-03H-02H-01H for entries 2-8). Adding 07H maps these to the raw protection level values used internally.
5277
CP A,05H FE 05
Compare Register A against 05H. If Register A equals 05H, the Z FLAG is set. This tests for a special case in the protection level encoding where two keywords (RE for READ and WR for WRIT) might share an encoding boundary. The value 05H corresponds to the WRIT level entry.
5279
If the NZ FLAG has been set (A is not 05H - the computed level is not the WRIT special case), JUMP to 5286H to store this level value directly. Most protection levels follow this path.

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).

527B
LD A,(51E2H) 3A E2 51
Fetch the byte at 51E2H, which is the third character of the user-typed keyword as it appears in the command line buffer. This disambiguates between keywords that produce A=05H: specifically, it distinguishes RENA (rename) from NAME by checking the third character.
527E
CP A,4EH FE 4E
Compare Register A against 4EH (ASCII N). If Register A equals 4EH, the Z FLAG is set - the third character of the keyword is 'N', indicating the keyword is RENA (RE + NA = rename), not NAME.
5280
LD A,05H 3E 05
Load Register A with 05H. This is the pre-set protection level value for WRIT (write access). The subsequent JR NZ will either use this value (if the third character was not 'N', i.e., the keyword is WRIT) or fall through to the override at 5284H.
5282
If the NZ FLAG has been set (third character is not 'N' - keyword is WRIT, not RENA), JUMP to 5286H to store protection level 05H (WRIT).
5284
LD A,02H 3E 02
Load Register A with 02H. This is the protection level for NAME (the keyword was RE + NA = RENA, so the level is RENAME = 02H in VTOS's internal encoding). Falls through to the store.
5286
LD (5337H),A 32 37 53
Self-Modifying Code
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).

5289
POP HL E1
Restore Register Pair HL from the stack. This is the command line pointer saved at 5256H (before the PROT= table scan), now pointing at the start of the typed keyword value in the command line.
528A
LD B,01H 06 01
Load Register B with 01H. This is the presence flag bit for the PROT= parameter. Bit 0 set in the accumulator byte at 532FH will record that PROT= was supplied in this command invocation.
528C
LD A,(HL) 7E
Fetch the current character from the command line position in Register Pair HL. This re-reads the position that was being scanned before the parameter-specific handler was entered, to locate the character terminating the parameter value.
528D
CP A,22H FE 22
Compare Register A against 22H (ASCII " double-quote). If Register A equals 22H, the Z FLAG is set - the parameter value was enclosed in quotes and the closing quote has been reached.
528F
If the NZ FLAG has been set (not a closing quote), JUMP to 5292H to go directly to the flag update. If it is a closing quote, fall through to INC HL to skip past it first.
5291
INC HL 23
INCrement Register Pair HL by 1, advancing the command line pointer past the closing double-quote character.
5292
LD A,(532FH) 3A 2F 53
Fetch the current parameter presence flags byte from 532FH into Register A. This is the self-modifying accumulator byte that tracks which parameters (PROT=, UPD=, etc.) have been supplied so far in this command invocation. Initially 00H.
5295
OR A,B B0
OR Register A with Register B (the presence flag bit for the parameter just processed: 01H for PROT=, 02H for UPD=, 04H for ACC=, 08H for INV/VIS). This sets the appropriate bit in the accumulator to record that this parameter was provided.
5296
LD (532FH),A 32 2F 53
Self-Modifying Code
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.
5299
LD A,(HL) 7E
Fetch the current character from the command line into Register A to determine what follows the completed parameter: ')' (done), ',' (more parameters), or an unexpected character (error).
529A
CP A,29H FE 29
Compare Register A against 29H (ASCII )). If Register A equals 29H, the Z FLAG is set - the closing parenthesis has been reached and all parameters have been parsed.
529C
If the Z FLAG has been set (closing ')' found), JUMP to 5323H to apply all accumulated parameter values to the directory entry and write it back to disk.
529F
CP A,2CH FE 2C
Compare Register A against 2CH (ASCII ,). If Register A equals 2CH, the Z FLAG is set - a comma separator was found, indicating there is another parameter to parse.
52A1
If the Z FLAG has been set (comma found), LOOP BACK to 5237H to parse the next parameter. The INC HL at 5237H will advance HL past the comma.
52A3
JUMP to 53A4H to display "PARAMETER ERROR". The character was neither ')' nor ',' - the parameter list is malformed.

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.

52A6
GOSUB to the Advance-to-Value Scanner at 52F7H. For INV, there is no '=' or value, so this call simply advances HL past the remaining characters of "NV" to the next delimiter (',' or ')'), checking that the keyword terminates properly.
52A9
If the NZ FLAG has been set (CALL 52F7H found unexpected additional characters after the keyword - it did not reach a clean delimiter), JUMP to 53A4H to display "PARAMETER ERROR".
52AC
LD A,(533FH) 3A 3F 53
Fetch the current attribute flags accumulator byte from 533FH into Register A. This byte tracks the INV/VIS state to be applied to the directory entry.
52AF
OR A,08H F6 08
OR Register A with 08H, setting bit 3 of the attribute accumulator. Bit 3 in the VTOS directory entry attribute byte at offset +01H controls file visibility: 1 = invisible, 0 = visible.
52B1
LD (533FH),A 32 3F 53
Self-Modifying Code
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.
52B4
LD B,08H 06 08
Load Register B with 08H. This sets the parameter presence flag bit for INV (bit 3) so that the OR at 5295H records that the INV parameter was supplied.
52B6
JUMP to 5292H to update the presence flags accumulator at 532FH and then read the next character (')' or ',') to continue or close the parameter list.

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.

52B8
GOSUB to the Advance-to-Value Scanner at 52F7H. For VIS (no '=' or value), this simply advances HL to the next delimiter and verifies the keyword ends cleanly.
52BB
If the NZ FLAG has been set (unexpected characters follow VIS), JUMP to 53A4H to display "PARAMETER ERROR".
52BE
LD A,(533FH) 3A 3F 53
Fetch the current attribute flags accumulator byte from 533FH into Register A.
52C1
AND A,0F7H E6 F7
AND Register A with 0F7H (11110111 binary), clearing bit 3. This removes the invisible flag from the attribute accumulator, marking the file as visible in directory listings.
52C3
LD (533FH),A 32 3F 53
Self-Modifying Code
Store the updated attribute byte (bit 3 cleared = visible) back to 533FH.
52C6
LD B,08H 06 08
Load Register B with 08H. Sets the presence flag bit for VIS (same bit position as INV, since they are mutually exclusive).
52C8
JUMP to 5292H to update the presence flags accumulator and read the next character.

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.

52CA
GOSUB to the Advance-to-Value Scanner at 52F7H. Advances HL past 'CC=' to the start of the password value.
52CD
If the Z FLAG has been set (no password value follows '='), JUMP to 53A4H for "PARAMETER ERROR". An access password must be supplied.
52D0
PUSH HL E5
Save Register Pair HL (pointer to the start of the password value in the command line) onto the stack.
52D1
LD DE,51E0H 11 E0 51
Point Register Pair DE to 51E0H, the 8-byte password work buffer. The password string copier at 530CH will fill this buffer with the typed password (space-padded to 8 characters).
52D4
GOSUB to the Password Encoder SVC Stub at 52F4H. This first copies the password from the command line (HL) into the work buffer at DE (51E0H), then issues RST 28H SVC 0E4H to hash the 8-byte space-padded password. Returns HL = the 2-byte encoded password hash.
52D7
LD (51F5H),HL 22 F5 51
Store Register Pair HL (the 2-byte encoded access password hash) to 51F5H. This is the access password storage location in the SYS2 runtime variable area. The directory update section will write this value to the appropriate field in the directory entry.
52DA
POP HL E1
Restore Register Pair HL from the stack, recovering the command line pointer (now positioned past the copied password characters).
52DB
LD B,02H 06 02
Load Register B with 02H. This is the presence flag bit for the ACC= parameter (bit 1), which will be OR'd into the accumulator at 532FH to record that an access password was supplied.
52DD
JUMP to 528CH to update the presence flags and advance to the next parameter delimiter.

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.

52DF
GOSUB to the Advance-to-Value Scanner at 52F7H. Advances HL past 'UPD=' to the start of the update password value.
52E2
If the Z FLAG has been set (no password value), JUMP to 53A4H for "PARAMETER ERROR".
52E5
PUSH HL E5
Save Register Pair HL (pointer to the start of the update password value) onto the stack.
52E6
LD DE,51E0H 11 E0 51
Point Register Pair DE to 51E0H, the 8-byte password work buffer, as the destination for the update password string.
52E9
GOSUB to the Password Encoder SVC Stub at 52F4H to copy and hash the update password. Returns HL = the 2-byte encoded password hash.
52EC
LD (51F3H),HL 22 F3 51
Store Register Pair HL (the 2-byte encoded update password hash) to 51F3H. This is the update password storage location. The directory update section will write this to the update password field of the directory entry.
52EF
POP HL E1
Restore Register Pair HL from the stack (command line pointer past the update password).
52F0
LD B,04H 06 04
Load Register B with 04H. This is the presence flag bit for the UPD= parameter (bit 2).
52F2
JUMP to 528CH to update the presence flags and advance to the next parameter delimiter.

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.

52F4
LD A,0E4H 3E E4
Load Register A with 0E4H, the SVC (Supervisor Call) code for the VTOS password hashing service. This code is placed in A immediately before the RST 28H instruction, which is how VTOS SVC calls pass their function code.
52F6
RST 28H EF
GOSUB to the RST 28H vector at 400CH (the VTOS overlay dispatcher). With A=0E4H, this invokes the password encoding service. On entry: DE = pointer to the 8-byte space-padded password buffer at 51E0H. On return: HL = 2-byte encoded password hash. This service is shared with the directory search routines in SYS2 that validate passwords on file open.

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).

52F7
INC HL 23
INCrement Register Pair HL by 1, advancing the command line pointer past the current character (the keyword initial letter 'P', 'A', 'U', 'I', or 'V') to begin scanning for '=' or a delimiter.
52F8
LD A,(HL) 7E
Fetch the next character from the command line into Register A for examination.
52F9
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return, end of line). If Register A equals 0DH, the Z FLAG is set - the end of the command line has been reached without finding a value.
52FB
RET Z C8
If the Z FLAG has been set (end of line), RETurn with Z set. The caller will treat this as an absent value and either error or accept it (INV/VIS use this as a clean exit).
52FC
CP A,29H FE 29
Compare Register A against 29H (ASCII )). If Register A equals 29H, the Z FLAG is set - the closing parenthesis was reached without finding a value.
52FE
RET Z C8
If the Z FLAG has been set (closing ')' found), RETurn with Z set. For INV/VIS this is the normal clean exit; for PROT=/ACC=/UPD= this is an error (no value).
52FF
CP A,2CH FE 2C
Compare Register A against 2CH (ASCII ,). If Register A equals 2CH, the Z FLAG is set - a comma separator was reached without finding a value.
5301
RET Z C8
If the Z FLAG has been set (comma found), RETurn with Z set. Same meaning as ')': for INV/VIS a clean exit, for the others an error.
5302
CP A,3DH FE 3D
Compare Register A against 3DH (ASCII =). If Register A equals 3DH, the Z FLAG is set - the '=' sign has been found.
5304
If the NZ FLAG has been set (not '='), LOOP BACK to 52F7H to advance HL by one more position and re-examine the next character. This skips over any extra keyword characters (e.g., the 'R', 'O', 'T' in PROT= or the 'C', 'C' in ACC=) until '=' is found.
5306
INC HL 23
INCrement Register Pair HL by 1, advancing past the '=' sign itself to position HL at the first character of the value.
5307
LD A,(HL) 7E
Fetch the first character of the value (the character immediately after '=') into Register A to check whether a value was actually provided.
5308
CP A,22H FE 22
Compare Register A against 22H (ASCII " double-quote). If Register A equals 22H, the Z FLAG is set - the value is enclosed in quotes.
530A
If the NZ FLAG has been set (no opening quote), JUMP to 530DH to proceed with copying the unquoted value. If there is a quote, fall through to INC HL to skip past it first.
530C
INC HL 23
INCrement Register Pair HL by 1, advancing past the opening double-quote character to position HL at the first character of the quoted 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.

530D
LD DE,51E0H 11 E0 51
Point Register Pair DE to 51E0H, the 8-byte password work buffer. This is the destination for the password string copy.
5310
LD B,08H 06 08
Load Register B with 08H. This sets the fill count for the space-padding loop that follows. VTOS passwords are exactly 8 characters; shorter entries are padded with spaces (20H).
5312
PUSH DE D5
Save Register Pair DE (the buffer start pointer 51E0H) onto the stack so it can be recovered after the space-fill loop, to pass to the copy subroutine.
5313
PUSH BC C5
Save Register Pair BC (B=08H count, C=undefined) onto the stack to preserve the fill count across the space-fill DJNZ loop.
5314
LD A,20H 3E 20
Load Register A with 20H (ASCII space character). This is the fill value for pre-initializing the 8-byte password buffer.
5316
LD (DE),A 12
Store Register A (space, 20H) to the memory address in Register Pair DE. This writes one space character to the current position in the password buffer.
5317
INC DE 13
INCrement Register Pair DE by 1, advancing the buffer pointer to the next byte.
5318
DECrement B and loop back to 5316H if not zero. Repeats 8 times, filling the entire 8-byte buffer at 51E0H-51E7H with space characters (20H).
531A
POP BC C1
Restore Register Pair BC from the stack, recovering B=08H (the maximum character count for the password copy).
531B
POP DE D1
Restore Register Pair DE from the stack, recovering the buffer start address 51E0H. DE now points at the beginning of the space-filled buffer, ready to receive the password characters.
531C
GOSUB to the character copy subroutine at 50B1H (in SYS6). Entry: HL = source (command line position, pointing at first password character), DE = destination (51E0H buffer), B = maximum characters to copy (08H). This routine copies characters from HL to DE, stopping at a delimiter ('"', ')', ',', or 0DH), and pads the buffer remainder with spaces. On return, HL points at the delimiter character.
531F
DEC HL 2B
DECrement Register Pair HL by 1, backing up the command line pointer one position. The copy subroutine left HL pointing past the delimiter; DEC HL positions it back at the delimiter character itself, so the caller's delimiter check (CP A,22H at 528DH) can detect the closing quote if present.
5320
OR A,01H F6 01
OR Register A with 01H, setting bit 0. This sets the NZ flag and also encodes the "password was provided" signal in A. The caller tests the NZ result to confirm the password buffer was filled.
5322
RET C9
RETurn to the caller (52D4H for ACC= or 52E9H for UPD=) with A=01H and NZ set, signaling that a password string was successfully copied into the buffer at 51E0H.

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).

5323
LD BC,(53E1H) ED 4B E1 53
Load Register Pair BC with the 16-bit value stored at 53E1H. This address is within the data area at the end of the ATTRIB overlay (ASCII data decoded as instructions by the disassembler). The value at 53E1H is the directory entry BCB (Block Control Block) descriptor address that identifies which directory entry to update. It was populated when the file was opened by CALL 4424H at 5215H.
5327
GOSUB to SYS0 routine at 4B10H (Directory Entry Validation). Entry: BC = directory entry descriptor. On return: HL = pointer to the directory entry in the directory buffer at 4200H, Z = valid entry found, NZ = entry not found or invalid.
532A
If the NZ FLAG has been set (directory entry validation failed), JUMP to 536BH to set the extended-context bit and exit via the DOS error handler 4409H.
532D
LD A,(HL) 7E
Fetch the first byte of the directory entry (at the address returned in HL by CALL 4B10H) into Register A. This byte at offset +00H contains the file status and first character of the filename.
532E
LD D,00H 16 00
Load Register D with 00H. Register D will accumulate the modification flags: bit 0 = apply PROT= change, bit 1 = apply ACC= password, bit 2 = apply UPD= password, bit 3 = apply INV/VIS attribute. Initially zero (no changes pending).

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.

5330
BIT 0,D CB 42
Test bit 0 of Register D (PROT= presence flag). If bit 0 is set (PROT= was supplied), the Z FLAG is cleared (NZ). Note: at this point D=00H from LD D,00H above; the actual flag test is against the value that will be loaded from 532FH - this instruction appears to be reading the wrong register at this point in the flow and may instead be testing the byte loaded from (HL) at 532DH through A into D via a path not shown. The self-modifying byte at 532FH drives the branch at 5332H.
5332
If the Z FLAG has been set (PROT= was not supplied, bit 0 of D is clear), JUMP to 5338H to skip the protection level masking operation.
5334
AND A,0F0H E6 F0
AND Register A (the directory entry first byte, containing the current protection level in bits 3-0) with 0F0H, clearing bits 3-0. This removes the old protection level while preserving the upper 4 bits (the status/name field).
5336
OR A,00H F6 00
Self-Modifying Code
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.
5338
BIT 3,D CB 5A
Test bit 3 of Register D (INV/VIS presence flag). If bit 3 is set (INV or VIS was supplied), the Z FLAG is cleared (NZ).
533A
If the Z FLAG has been set (no INV/VIS parameter was supplied), JUMP to 5340H to skip the visibility-bit modification.
533C
AND A,0F7H E6 F7
AND Register A with 0F7H (11110111 binary), clearing bit 3 of the directory entry attribute byte. This is the first step in applying the INV/VIS change - bit 3 is cleared to a known state before ORing in the new value.
533E
OR A,00H F6 00
Self-Modifying Code
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.
5340
LD (HL),A 77
Store Register A (the updated directory entry first byte with the new protection level and/or visibility flag applied) back to the directory entry buffer at the address in HL. This commits the PROT= and INV/VIS changes to the in-memory directory entry.
5341
LD A,L 7D
Load Register A with Register L (the low byte of the current HL pointer into the directory entry). This is used to advance HL to the next field within the directory entry.
5342
ADD A,10H C6 10
ADD 10H to Register A, advancing the directory entry offset by 16 (decimal) bytes. Offset +10H within a 32-byte directory entry is where the access password bytes begin (the first 2-byte password field).
5344
LD L,A 6F
Load Register L with Register A (the updated offset). HL now points to offset +10H of the directory entry, the access password field.
5345
BIT 2,D CB 52
Test bit 2 of Register D (ACC= password presence flag). If bit 2 is set (ACC= was supplied), the Z FLAG is cleared (NZ).
5347
If the Z FLAG has been set (no ACC= parameter was supplied), JUMP to 5353H to skip the access password update and advance to the update password field.
5349
LD A,(51F3H) 3A F3 51
Fetch the low byte of the encoded access password hash from 51F3H. This value was stored there by LD (51F3H),HL at 52D7H after the SVC 0E4H call encoded the typed password.
534C
LD (HL),A 77
Store Register A (the low byte of the access password hash) to offset +10H of the directory entry. This writes the first byte of the new access password into the directory.
534D
LD A,(51F4H) 3A F4 51
Fetch the high byte of the encoded access password hash from 51F4H (the byte immediately following 51F3H, since LD (51F3H),HL stores L at 51F3H and H at 51F4H).
5350
INC HL 23
INCrement Register Pair HL by 1, advancing to offset +11H of the directory entry (the second byte of the access password field).
5351
LD (HL),A 77
Store Register A (the high byte of the access password hash) to offset +11H of the directory entry. This completes writing the 2-byte encoded access password.
5352
DEC HL 2B
DECrement Register Pair HL by 1, restoring HL to offset +10H of the directory entry so that the subsequent INC HL / INC HL advancement to offset +12H (update password field) starts from the correct base.
5353
INC HL 23
INCrement Register Pair HL by 1, advancing toward the update password field.
5354
INC HL 23
INCrement Register Pair HL by 1 again. HL now points to offset +12H of the directory entry, the first byte of the update password field.
5355
BIT 1,D CB 4A
Test bit 1 of Register D (UPD= password presence flag). If bit 1 is set (UPD= was supplied), the Z FLAG is cleared (NZ).
5357
If the Z FLAG has been set (no UPD= parameter was supplied), JUMP to 5362H to skip the update password update and proceed directly to writing the directory entry.
5359
LD A,(51F5H) 3A F5 51
Fetch the low byte of the encoded update password hash from 51F5H. Stored there by LD (51F5H),HL at 52ECH after the UPD= SVC 0E4H encoding.
535C
LD (HL),A 77
Store Register A (low byte of the update password hash) to offset +12H of the directory entry.
535D
LD A,(51F6H) 3A F6 51
Fetch the high byte of the encoded update password hash from 51F6H.
5360
INC HL 23
INCrement Register Pair HL by 1, advancing to offset +13H of the directory entry (the second byte of the update password field).
5361
LD (HL),A 77
Store Register A (high byte of the update password hash) to offset +13H of the directory entry. The update password is now fully written.
5362
GOSUB to SYS0 routine at 4B1FH (Directory Entry Write/Update). Writes the modified directory entry from the in-memory buffer back to the disk directory sector. On return: Z = success, NZ = write error (disk full, write-protected, etc.).
5365
If the NZ FLAG has been set (directory write failed), JUMP to 536BH to exit with an error via 4409H.
5368
JUMP to SYS0 routine at 402DH (DOS READY / No-Error Exit). The ATTRIB command has successfully modified the file's directory entry. Control returns to the VTOS command interpreter.

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.

536B
OR A,40H F6 40
OR Register A with 40H, setting bit 6 of the current error code in A. Bit 6 in VTOS error codes is the extended-context suppression flag (controlled by bit 6 of 430FH). Setting this bit prevents the SYS4 error display from appending extended context (filename, return address) to the error message.
536D
JUMP to SYS0 routine at 4409H (DOS Error Exit). Register A holds the error code with the extended-context bit set. SYS4 will display the corresponding error message and return to the DOS READY prompt.

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.

5370
LD HL,5379H 21 79 53
Point Register Pair HL to 5379H, which is the start of the "FILE SPEC REQUIRED" + 0DH string in the ATTRIB data area.
5373
GOSUB to SYS0 routine at 447BH (Display with CONFIG/SYS processing). Displays the string pointed to by HL to the console, processing any embedded control codes.
5376
JUMP to SYS0 routine at 4030H (Error-Already-Displayed Exit). Returns to the VTOS command interpreter without invoking the SYS4 error display (the message was already shown by CALL 447BH).

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).

5379-538BH
DEFM "FILE SPEC REQUIRED" + 0DH 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
ASCII string: "FILE SPEC REQUIRED" followed by a carriage return (0DH). Displayed by CALL 447BH when no filespec was found in the command line.

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.

538C
LD HL,5395H 21 95 53
Point Register Pair HL to 5395H, the start of the "-NOTHING DONE-" + 0DH string.
538F
GOSUB to SYS0 routine at 447BH to display the "-NOTHING DONE-" message.
5392
JUMP to SYS0 routine at 4030H (Error-Already-Displayed Exit).

5395H - "-NOTHING DONE-" String Data

ASCII string data terminated by 0DH.

5395-53A3H
DEFM "-NOTHING DONE-" + 0DH 2D 4E 4F 54 48 49 4E 47 20 44 4F 4E 45 2D 0D
ASCII string: "-NOTHING DONE-" followed by a carriage return (0DH). Displayed when the parameter list contains no recognizable keyword initial letter.

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.

53A4
LD HL,53ADH 21 AD 53
Point Register Pair HL to 53ADH, the start of the "PARAMETER ERROR" + 0DH string.
53A7
GOSUB to SYS0 routine at 447BH to display "PARAMETER ERROR".
53AA
JUMP to SYS0 routine at 4030H (Error-Already-Displayed Exit).

53ADH - "PARAMETER ERROR" String Data

ASCII string data terminated by 0DH.

53AD-53BCH
DEFM "PARAMETER ERROR" + 0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
ASCII string: "PARAMETER ERROR" followed by a carriage return (0DH). Displayed for any unrecognized or malformed parameter.

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.

53BD
LD A,(HL) 7E
Fetch the current character from the command line position in Register Pair HL into Register A.
53BE
CP A,20H FE 20
Compare Register A against 20H (space). If Register A is less than 20H (control character or end-of-line), the CARRY FLAG is set - a non-printable character signals the end of the token.
53C0
RET C D8
If the CARRY FLAG has been set (character is below space, i.e., a control character), RETurn. HL points at the terminating control character.
53C1
CP A,61H FE 61
Compare Register A against 61H (ASCII lowercase a). If Register A is less than 61H (not a lowercase letter), the CARRY FLAG is set - the character is already uppercase or is a non-alpha character that should not be converted.
53C3
If the CARRY FLAG has been set (character is less than 'a', so it is uppercase or punctuation), JUMP to 53C7H to advance HL without modifying the character.
53C5
RES 5,(HL) CB AE
Clear bit 5 of the byte at the address in Register Pair HL. In ASCII, clearing bit 5 of a lowercase letter (a-z, 61H-7AH) converts it to its uppercase equivalent (A-Z, 41H-5AH). This modifies the command line buffer in place.
53C7
INC HL 23
INCrement Register Pair HL by 1, advancing the command line pointer to the next character.
53C8
LOOP BACK to 53BDH to fetch and process the next character.

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.

53CA
DEFB 0DH 0D
Terminator byte preceding the keyword table. 0DH = carriage return, also used as the string terminator in the error messages above at 53A3H.
53CB-53CCH
DEFM "AL" 41 4C
Entry 1: "AL" - matches ALL (or FULL when combined with entry 8 FU). Protection level: 00H (full access, no restrictions). Note: the table is scanned from entry 1 (B=8) to entry 8 (B=1); the first character of the user keyword is compared against table byte 1, the second against table byte 2.
53CD-53CEH
DEFM "EX" 45 58
Entry 2: "EX" - matches EXEC(ute only). Protection level: computed from B=7 by the encoder at 526FH-5286H.
53CF-53D0H
DEFM "RE" 52 45
Entry 3: "RE" - matches READ (only). Protection level: computed from B=6.
53D1-53D2H
DEFM "WR" 57 52
Entry 4: "WR" - matches WRIT(e). Protection level: computed from B=5, with disambiguation via the third character at 51E2H.
53D3-53D5H
DEFM "~~N" 7E 7E 4E
Entry 5 begins with two 7EH bytes. The disassembler decodes 7EH as LD A,(HL); these are in fact data bytes. The pair 7E 7E followed by 4EH = "~~N" - the first two compared bytes would be 7EH and 7EH, matching keywords starting with those byte values. In context, this entry covers RENA: "RN" in the normalized two-character comparison (first two characters after the shared "RE" prefix). Re-examining: entry 5 at 53D3H is the 5th 2-byte pair from 53CBH: bytes 53D3H-53D4H = 7EH, 7EH which are data. The actual RENA match is likely handled by the NAME/RENA disambiguation at 527BH.
53D5-53D6H
DEFM "NA" 4E 41
Entry 6 pair portion: "NA" bytes - part of the NAME keyword stub.
53D7-53D8H
DEFM "KI" 4B 49
Entry 7: "KI" - matches KILL. Protection level: computed from B=2 by the encoder.
53D9-53DAH
DEFM "FU" 46 55
Entry 8: "FU" - matches FULL (same level as ALL). Protection level: 00H (full access). This is entry 8 (B=1); SUB 08H gives FFH (non-zero), ADD 07H gives 06H, CP 05H is NZ, so it jumps to store A=06H. However, FULL and ALL should produce the same protection level; the actual encoding may differ from this analysis for entry 8 vs entry 1.

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 RangePurpose
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

AddressName and Purpose
5200HPROT 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.
520DHExit 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.
5239HOption 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.
524BHNew 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.
5266HNew 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.
5276HDirectory 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.
5364HMaster 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.
5396HPassword 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.
539CHPassword 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.
5408HGAT 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.
541BHGAT 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.
5434HI/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.
5439HPARAMETER ERROR Exit
Loads HL = 5442H ("PARAMETER ERROR" + 0DH), calls 447BH, then jumps to 4030H.
5452HINVALID 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.

5200
LD A,(430FH) 3A 0F 43
Fetch the System State Flags byte from 430FH into Register A. This byte contains multiple system status bits; bit 5 (20H) signals that VTOS is currently executing a program chain (chaining mode active).
5203
BIT 5,A CB 6F
Test bit 5 of Register A (the re-entry/chaining-mode flag from 430FH). If bit 5 is set (chaining active), the Z FLAG is cleared (NZ). PROT must not be run during program chaining as it requires interactive prompting.
5205
If the NZ FLAG has been set (chaining mode is active), JUMP to 5452H to display "INVALID COMMAND DURING PROGRAM CHAINING" and exit via 4030H.
5208
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H. This initializes the "new value" parameter to zero, indicating that no new disk name or master password has yet been parsed from the command line. BC will be stored to 5394H-5395H and used by the option handlers below.
520B
JUMP to 521BH to store BC=0000H to the self-modifying area at 5394H and continue with the drive-number parse. The instructions at 520DH-521AH are an alternate path for a non-zero BC case (entered from other paths).

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.

520D
LD A,D 7A
Load Register A with Register D. In the alternate path, DE holds a result returned from a subroutine; D is the high byte of that result.
520E
AND A,E A3
AND Register A with Register E (the low byte of the DE result). This tests whether DE is zero (both bytes AND to zero = result is zero, Z flag set) or non-zero.
520F
INC A 3C
INCrement Register A by 1. If A was FFH (DE=FFFFH, the "no name" sentinel), this wraps to 00H setting the Z FLAG. If A was 00H (DE was 0000H), INC makes it 01H (NZ). This distinguishes between "no value" (FFFFH -> 00H after INC -> Z) and "value present" (0000H -> NZ).
5210
If the Z FLAG has been set (DE was FFFFH, meaning no new value), JUMP to 53BBH to prompt the user for the master password interactively.
5213
LD HL,5600H 21 00 56
Point Register Pair HL to 5600H, the password input work buffer. When a value was provided in the command line (DE non-FFFFH), HL is set to point at the buffer where the value will be stored.
5216
JUMP to 53A3H to continue with the password collection logic using the value pointed to by HL.
5219
NOP x 2 00 00
Two NOP instructions serving as padding between the alternate entry path code and the main drive-parse section that follows.
521B
LD (5394H),BC ED 43 94 53
Self-Modifying Code
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.
521F
LD C,00H 0E 00
Load Register C with 00H, initializing the drive number to 0 (the default drive). If the command line includes ':d', this will be overwritten with the specified drive digit.
5221
LD A,(HL) 7E
Fetch the current character from the command line position in Register Pair HL into Register A. This is the first character of the PROT command's argument (expected to be ':' for a drive specification, or end-of-line).
5222
CP A,3AH FE 3A
Compare Register A against 3AH (ASCII :). If Register A equals 3AH, the Z FLAG is set - the ':d' drive specification is present.
5224
If the NZ FLAG has been set (no ':' found - no drive was specified), JUMP to 522CH to store the default drive number (C=00H) and proceed to open the directory file. Drive 0 is used by default.
5226
INC HL 23
INCrement Register Pair HL by 1, advancing the command line pointer past the ':' character to the drive digit.
5227
LD A,(HL) 7E
Fetch the drive digit character from the command line into Register A. This is the ASCII digit '0' through '7' specifying the target drive.
5228
SUB A,30H D6 30
SUBtract 30H from Register A, converting the ASCII digit ('0'=30H through '7'=37H) to a binary drive number (0-7).
522A
LD C,A 4F
Load Register C with Register A (the binary drive number 0-7). C will be passed to the file-open and sector-read routines as the drive number.
522B
INC HL 23
INCrement Register Pair HL by 1, advancing the command line pointer past the drive digit character.
522C
LD A,C 79
Load Register A with Register C (the drive number, 0-7 or 0 by default).
522D
LD (5370H),A 32 70 53
Self-Modifying Code
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.
5230
LD DE,54F4H 11 F4 54
Point Register Pair DE to 54F4H, the FCB (File Control Block) area for the PROT command's directory/GAT file. This FCB will be populated by CALL 4476H with the control information for the target disk's directory.
5233
GOSUB to SYS0 routine at 4476H (Open File Utility). Entry: DE = FCB pointer (54F4H), C = drive number, HL = command line pointer. Opens the disk's directory/GAT control file on the specified drive. On return: Z = success, NZ = error (drive not configured, not ready, etc.).
5236
If the NZ FLAG has been set (file open failed), JUMP to 5439H to display "PARAMETER ERROR" and exit. This handles cases such as an invalid or unconfigured drive number.

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.

5239
LD A,(5267H) 3A 67 52
Fetch the first option presence flag byte from 5267H into Register A. This byte is 00H if no LOCK or UNLOCK option was parsed, non-zero if one was present. (Address 5267H is within the SYS6 overlay's parameter parsing area.)
523C
LD HL,52EDH 21 ED 52
Point Register Pair HL to 52EDH, the location of the second option flag byte (PW= option presence).
523F
OR A,(HL) B6
OR Register A with the byte at address 52EDH (the PW= option flag). This merges the two flags; if either was set, A becomes non-zero.
5240
LD HL,52F7H 21 F7 52
Point Register Pair HL to 52F7H, the location of the third option flag byte (NAME= option presence).
5243
OR A,(HL) B6
OR Register A with the byte at address 52F7H (the NAME= option flag). If any of the three option flags was non-zero, A is now non-zero.
5244
LD HL,524FH 21 4F 52
Point Register Pair HL to 524FH, the location of the fourth option flag byte (MPW= option presence).
5247
OR A,(HL) B6
OR Register A with the byte at address 524FH (the MPW= option flag). After this OR, A is non-zero if any option whatsoever was specified in the PROT command.
5248
If the Z FLAG has been set (all option flags were zero, meaning no options were provided), JUMP to 5483H to display "-NOTHING DONE-" and exit.
524B
GOSUB to the Master Password Validator at 5364H. This routine reads the master password from the command line (MPW= parameter) or prompts the user, then compares it against the master password stored in the GAT sector on disk. On return: Z = password validated (matched), NZ = invalid password (routine already displayed error and will exit; this CALL only returns to the next instruction on success).

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.

524E
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H. This initializes DE to zero before the test below. DE will receive the new disk name pointer if the NAME= option was supplied.
5251
LD A,D 7A
Load Register A with Register D (high byte of DE = 00H at this point). This is the setup for the zero-test that follows.
5252
OR A,E B3
OR Register A with Register E (low byte of DE). If DE is 0000H (no new disk name pointer), the Z FLAG is set; if DE is non-zero (a name was provided), NZ.
5253
If the Z FLAG has been set (DE is zero, no new disk name was provided), JUMP to 5266H to skip the disk name update and proceed to check the new master password option.
5255
LD HL,549BH 21 9B 54
Point Register Pair HL to 549BH, which is the "NEW DISK PACK NAME ? " prompt string in the PROT data area. This will be passed to the password collector as the display prompt.
5258
GOSUB to the Password String Collector at 539CH. Entry: DE = command-line pointer to the NAME= value (or 0 for interactive prompt using HL). Collects up to 8 characters into the buffer at 5600H, padding with spaces. Returns DE = pointer past the collected string, HL = end of buffer pointer.
525B
LD HL,5600H 21 00 56
Point Register Pair HL to 5600H, the password/name input buffer. After the collector returns, HL is reset to the buffer start so the LDIR below copies from the beginning.
525E
LD DE,57D0H 11 D0 57
Point Register Pair DE to 57D0H, the location within the GAT sector buffer (at 5700H) where the disk's master password/name field is stored. The new name will be copied here.
5261
LD BC,0008H 01 08 00
Load Register Pair BC with 0008H. This sets the byte count for the LDIR block move: 8 bytes (the full master password/name field).
5264
LDIR ED B0
Block Move: Copy 8 bytes from Source HL (5600H, the collected name) to Destination DE (57D0H, the GAT buffer name field), INCrementing both HL and DE after each byte. This installs the new disk name into the in-memory GAT sector image.

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.

5266
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, initializing the new master password pointer to null before the test that follows.
5269
LD A,D 7A
Load Register A with Register D (high byte of DE).
526A
OR A,E B3
OR Register A with Register E. If DE is zero (no new master password pointer), Z FLAG is set.
526B
If the Z FLAG has been set (no new master password was provided), JUMP to 5276H to skip the password collection and proceed to the directory maintenance loop.
526D
LD HL,54B3H 21 B3 54
Point Register Pair HL to 54B3H, which is the "NEW MASTER PASSWORD ? " prompt string in the PROT data area.
5270
GOSUB to the Password Collect with SVC Wrapper at 5396H. Collects the new master password string into 5600H, then issues RST 28H SVC 0E4H to hash/encode it. Returns HL = pointer to the encoded password.
5273
LD (57CEH),HL 22 CE 57
Store Register Pair HL (the pointer to the newly collected and encoded master password) to 57CEH. This location within the GAT buffer holds the disk name pointer/length. Storing HL here records the new master password's position for the validation check in 5364H and for the GAT write-back step.

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.

5276
GOSUB to SYS0 routine at 4B65H (Set Up Track/Sector for Directory I/O). Configures the track and sector registers in the drive parameter block (via IY) for the upcoming sector read. Prepares for accessing track 0, sector 0 (the GAT sector) of the target drive.
5279
LD E,01H 1E 01
Load Register E with 01H. This sets the sector number parameter to 1 (sector 1 of track 0, which in VTOS's zero-based sector indexing corresponds to the first data sector). Together with the track/sector setup from 4B65H, this selects the GAT sector.
527B
LD HL,5800H 21 00 58
Point Register Pair HL to 5800H, the directory sector buffer. The first GAT sector read will deposit the sector's 256 bytes here.
527E
GOSUB to SYS0 routine at 4B45H (Read Sector From Disk). Reads one 256-byte sector from the drive into the buffer at HL (5800H). On return: Z = success, NZ = read error.
5281
If the NZ FLAG has been set (read error), JUMP to 5434H to OR A,40H and exit via DOS error handler 4409H.
5284
LD A,(57CBH) 3A CB 57
Fetch the format flag byte from 57CBH (offset CBH within the GAT buffer now loaded at 5700H, though the first read went to 5800H - this suggests the second read into 5700H was performed earlier or that the buffer at 5700H contains data from a prior read). This byte indicates the disk's format type; a value below 30H indicates an older format without a stored master password.
5287
CP A,30H FE 30
Compare Register A against 30H (ASCII 0). If Register A is greater than or equal to 30H (CARRY not set), the disk format supports a stored master password. If A is less than 30H (CARRY set), the format is older.
5289
If the NO CARRY FLAG has been set (format byte >= 30H, disk supports stored password), JUMP to 52A7H to proceed with the full validation flow using the stored password.

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.

528B
LD A,(57A6H) 3A A6 57
Fetch the byte at 57A6H (offset A6H within the GAT buffer at 5700H) into Register A. This field is a disk-specific identifier byte examined as part of the password equivalence computation for older disk formats.
528E
INC A 3C
INCrement Register A by 1. If the byte at 57A6H was FFH (the "absent" sentinel), INC wraps it to 00H, setting the Z FLAG. Any other value produces NZ.
528F
LD A,2AH 3E 2A
Load Register A with 2AH (ASCII *). This is the first candidate password character for the equivalence result - used when the byte at 57A6H was FFH (the "no password" sentinel for this field).
5291
If the NZ FLAG has been set (57A6H was not FFH, meaning a non-null value is present in that field), JUMP to 52A4H to store A=2AH as the password equivalence byte and continue. If Z (57A6H was FFH), fall through to check the next field.
5293
LD A,(5788H) 3A 88 57
Fetch the byte at 5788H (offset 88H within the GAT buffer) into Register A. A second disk-specific field examined for older-format password equivalence.
5296
INC A 3C
INCrement Register A by 1. Tests whether 5788H was FFH (INC -> 00H, Z set).
5297
LD A,23H 3E 23
Load Register A with 23H (ASCII #). The second candidate password equivalence character.
5299
If the NZ FLAG has been set (5788H was not FFH), JUMP to 52A4H to store A=23H ('#') as the equivalence byte.
529B
LD A,(5783H) 3A 83 57
Fetch the byte at 5783H (offset 83H within the GAT buffer) into Register A. A third disk-specific field for older-format equivalence.
529E
INC A 3C
INCrement Register A by 1. Tests whether 5783H was FFH.
529F
LD A,05H 3E 05
Load Register A with 05H. The third candidate password equivalence byte (a non-printable control code).
52A1
If the NZ FLAG has been set (5783H was not FFH), JUMP to 52A4H to store A=05H.
52A3
XOR A,A AF
Set Register A to ZERO and clear all flags. If all three fields were FFH (all absent), the equivalence byte is 00H, meaning the disk has no master password at all.
52A4
LD (57CCH),A 32 CC 57
Store Register A (the computed password equivalence byte: 2AH, 23H, 05H, or 00H) to 57CCH within the GAT buffer. This byte will be examined by the GAT write routine at 541BH and by the master password validator at 5364H to determine if the user-supplied password matches the disk's effective master password.

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.

52A7
LD A,(5370H) 3A 70 53
Fetch the drive number from 5370H (the self-modifying byte written at 522DH) into Register A.
52AA
LD C,A 4F
Load Register C with Register A (the drive number). C is the drive number parameter for the sector-read and write subroutines at 5408H and 541BH.
52AB
GOSUB to the GAT Sector Write Subroutine at 541BH. Entry: C = drive number. This routine calls 4B65H (track/sector setup), 4768H (write sector from 5700H), and 4772H (read-after-write verify). Returns Z on success, NZ with A=15H on write error. In this context, it is being used to write the updated GAT buffer (with the new disk name or master password) back to the disk.
52AE
If the NZ FLAG has been set (GAT write failed), JUMP to 5434H to exit with a hardware error code.

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.

52B1
LD A,(57CBH) 3A CB 57
Fetch the format flag byte again from 57CBH in the GAT buffer at 5700H into Register A.
52B4
CP A,30H FE 30
Compare Register A against 30H. If A >= 30H (NO CARRY), the disk format supports the full VTOS master password field and no directory sector rewrite is needed.
52B6
If the NO CARRY FLAG has been set (modern format), JUMP to 52E9H to skip the directory sector format migration and proceed directly to the LOCK/UNLOCK processing.
52B8
LD A,(5801H) 3A 01 58
Fetch the byte at 5801H (offset 01H in the directory sector buffer at 5800H) into Register A. This is the second byte of the first directory sector, which in the older disk format contains a format marker that must be examined.
52BB
CP A,2CH FE 2C
Compare Register A against 2CH (ASCII ,). If Register A equals 2CH, the Z FLAG is set - the sector contains the older comma-delimiter format marker that needs to be updated to the VTOS format.
52BD
If the NZ FLAG has been set (the byte is not 2CH - either already updated or in a different format), JUMP to 52E9H to skip the sector rewrite.
52BF
LD A,0C4H 3E C4
Load Register A with 0C4H. This is the VTOS directory sector format marker byte that will replace the older 2CH comma byte at offset 01H of the first directory sector.
52C1
LD (5801H),A 32 01 58
Store Register A (0C4H) to 5801H, replacing the comma byte at offset 01H of the directory sector buffer with the VTOS format marker. The buffer must now be written back to disk.
52C4
GOSUB to SYS0 routine at 4768H (Write Sector to Disk). Writes the updated first directory sector from 5800H to disk. On return: Z = success, NZ = write error.
52C7
If the NZ FLAG has been set (write failed), JUMP to 5434H for the hardware error exit.
52CA
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H, the standard SYS0 directory/GAT sector buffer. This is the target buffer for the next directory sector read.
52CD
LD E,03H 1E 03
Load Register E with 03H. This sets the sector number to 3 for the next read, targeting directory sector 3 (the sector containing additional directory entries that also requires the format migration).
52CF
GOSUB to SYS0 routine at 4B45H (Read Sector From Disk). Reads sector 3 into the buffer at HL (4200H). On return: Z = success, NZ = read error.
52D2
If the NZ FLAG has been set (read error), JUMP to 5434H for the hardware error exit.
52D5
PUSH HL E5
Save Register Pair HL (pointing at 4200H, the directory buffer) onto the stack.
52D6
LD HL,4296H 21 96 42
Point Register Pair HL to 4296H, which is offset 96H within the directory buffer at 4200H. This offset points to a specific field within the sector 3 data that contains a pointer needing repair during the format migration.
52D9
LD (4212H),HL 22 12 42
Store Register Pair HL (4296H) to 4212H, which is offset 12H within the directory buffer at 4200H. This repairs the pointer field at directory offset 12H to point to 4296H, completing the format migration for sector 3.
52DC
POP HL E1
Restore Register Pair HL from the stack, recovering the 4200H buffer pointer.
52DD
GOSUB to SYS0 routine at 4768H (Write Sector to Disk). Writes the repaired sector 3 buffer at 4200H back to disk.
52E0
If the NZ FLAG has been set (write failed), JUMP to 5434H for the hardware error exit.
52E3
LD HL,54CBH 21 CB 54
Point Register Pair HL to 54CBH, which is the "NOTE: FILE DIR/SYS IS NOW READ ENABLED" confirmation message in the PROT data area. This message notifies the user that the maintenance update was performed.
52E6
GOSUB to SYS0 routine at 447BH to display the confirmation message.

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.

52E9
LD HL,(57CEH) 2A CE 57
Load Register Pair HL with the 16-bit value stored at 57CEH in the GAT buffer. This value is the disk name pointer (or the encoded master password pointer stored at 5273H). If zero, no name/password update was performed.
52EC
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H for the null-test that follows.
52EF
LD A,D 7A
Load Register A with Register D (high byte of DE = 00H).
52F0
OR A,E B3
OR Register A with Register E. DE is zero, so this tests nothing useful on its own; the actual test is against DE loaded above (0000H). This OR leaves A=00H, Z flag set.
52F1
If the NZ FLAG has been set (DE was non-zero, i.e., a new disk name or password pointer was stored), JUMP to 5300H to save HL (the pointer) and begin the directory walk. Since DE was loaded as 0000H, this NZ condition would only be reached if the OR affected A differently - this may be testing a previously computed result in A from the flags context before this LD DE.
52F3
LD HL,4296H 21 96 42
Point Register Pair HL to 4296H, an offset within the directory buffer at 4200H that serves as the starting point for the directory entry walk when no disk-name update was performed.
52F6
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, setting the "no update needed" marker for the directory walk.
52F9
LD A,D 7A
Load Register A with Register D (00H).
52FA
OR A,E B3
OR Register A with Register E. A remains 00H, Z FLAG set.
52FB
If the NZ FLAG has been set, JUMP to 5300H. Since A=00H here, this branch is not taken; fall through to the direct exit.
52FD
JUMP to SYS0 routine at 402DH (DOS READY / No-Error Exit). If neither a disk name nor a master password update was requested (and the LOCK/UNLOCK paths were also not taken), the PROT command completes successfully with no further action.

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.

5300
LD (5339H),HL 22 39 53
Self-Modifying Code
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.
5303
LD A,(5370H) 3A 70 53
Fetch the drive number from 5370H (the self-modifying byte set at 522DH) into Register A.
5306
LD C,A 4F
Load Register C with Register A (the drive number) to pass to the sector setup routine.
5307
GOSUB to SYS0 routine at 4B65H (Set Up Track/Sector for Directory I/O). Configures the track and sector registers for reading directory sectors of the target drive.
530A
LD HL,5800H 21 00 58
Point Register Pair HL to 5800H, the directory sector buffer. The sector walk begins from this buffer address.

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.

530D
LD A,L 7D
Load Register A with Register L (the low byte of the current HL pointer into the directory buffer). This is used to compute the buffer page and entry position.
530E
AND A,0D8H E6 D8
AND Register A with 0D8H (11011000 binary) to isolate the upper address bits. The result tests whether HL has advanced beyond the end of the current 256-byte sector buffer. If the AND result is 00H (HL is within the buffer range for valid entries), execution continues.
5310
If the Z FLAG has been set (AND result is zero - HL points within a valid buffer range), JUMP to 5347H to process this entry. If NZ (HL has advanced beyond the sector boundary), fall through to check the entry status.
5312
LD A,(HL) 7E
Fetch the first byte of the current directory entry (the entry status byte at offset +00H) into Register A.
5313
OR A,A B7
OR Register A with itself. Tests whether the entry status byte is zero. If A=00H (entry is inactive/empty), the Z FLAG is set.
5314
If the Z FLAG has been set (entry status is zero = empty/deleted entry), JUMP to 5347H to advance HL and process the next entry - no modification needed for empty entries.
5316
LD B,L 45
Load Register B with Register L (saves the low byte of HL, the current entry address, for later use in the self-modifying code at 531EH and the HL reconstruction at 5344H-5346H).
5317
PUSH BC C5
Save Register Pair BC (B = current entry L address, C = drive number) onto the stack for recovery after the entry update.
5318
LD A,L 7D
Load Register A with Register L (the low byte of HL, the current entry address within the sector).
5319
AND A,0E0H E6 E0
AND Register A with 0E0H (11100000 binary), isolating the upper 3 bits of the address byte. This computes the page-aligned base address of the current entry's 32-byte block.
531B
LD L,A 6F
Load Register L with Register A (the masked address, aligning HL to the start of the current 32-byte entry block).
531C
XOR A,B A8
XOR Register A (the page-aligned base, e.g., 58E0H) with Register B (the original L = full entry address). The result isolates the within-entry offset of the current pointer position.
531D
CP A,0FFH FE FF
Compare Register A against 0FFH. If the within-entry offset is FFH (the pointer is at the very last byte of a 256-byte boundary region), the Z FLAG is set, triggering a special case to read the next directory sector before continuing.
531F
If the Z FLAG has been set (at the sector boundary), JUMP to 532AH to handle the cross-sector case.
5321
LD (531EH),A 32 1E 53
Self-Modifying Code
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.
5324
GOSUB to SYS0 routine at 4B10H (Directory Entry Validation). Entry: BC = directory entry descriptor (B=entry page, C=drive number). Validates the directory entry currently pointed to by HL and BC. On return: Z = valid, NZ = invalid or end-of-directory.
5327
If the NZ FLAG has been set (entry validation failed or end of directory reached), JUMP to 5434H for the hardware error exit. (End of directory is treated as a termination condition here.)
532A
LD H,42H 26 42
Load Register H with 42H. This forces the high byte of HL to 42H, pointing HL into the directory buffer at 4200H. When the directory walk crosses a sector boundary, HL is reset to the 4200H page to read from the fresh buffer.
532C
LD A,(HL) 7E
Fetch the byte at the current HL position (within the 4200H directory buffer) into Register A. This reads the entry status byte to check whether the current entry is active.
532D
AND A,0F8H E6 F8
AND Register A with 0F8H (11111000 binary), masking out the lower 3 bits. The upper 5 bits of the entry status byte identify the entry type; 10H (forward-link) and 08H (terminal/active) are the two significant types.
532F
CP A,10H FE 10
Compare Register A against 10H. If Register A equals 10H, the Z FLAG is set - the current entry is a forward-link (type 10H), meaning it points to the next sector's directory data. The PROT maintenance loop must follow these links.
5331
If the NZ FLAG has been set (not a forward-link - entry is terminal type 08H or active data), JUMP to 5343H to process this entry normally.

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.

5333
LD A,L 7D
Load Register A with Register L (the current low byte of HL, pointing at the forward-link entry in the 4200H buffer).
5334
ADD A,10H C6 10
ADD 10H to Register A, advancing past the first 16 bytes of the forward-link entry to reach the next-pointer field area (at offset +10H from the entry start).
5336
LD L,A 6F
Load Register L with Register A, updating HL to point at the next-pointer field of the forward-link entry.
5337
PUSH DE D5
Save Register Pair DE onto the stack to preserve the current disk-name/update pointer while the next-pointer field is being zeroed.
5338
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H. The four bytes that follow will be written as 00H to clear the next-pointer fields of the forward-link entry during the normalization pass.
533B
LD (HL),E 73
Store Register E (00H) to the byte at the current HL position, clearing the first byte of the next-pointer field.
533C
INC L 2C
INCrement Register L by 1, advancing to the next byte of the next-pointer field.
533D
LD (HL),D 72
Store Register D (00H) to the next byte, continuing to zero the next-pointer field.
533E
INC L 2C
INCrement Register L by 1 again.
533F
LD (HL),E 73
Store Register E (00H) to the third byte of the field.
5340
INC L 2C
INCrement Register L by 1 again.
5341
LD (HL),D 72
Store Register D (00H) to the fourth byte. The four-byte next-pointer area is now zeroed.
5342
POP DE D1
Restore Register Pair DE from the stack.
5343
POP BC C1
Restore Register Pair BC (B = saved entry L, C = drive number) from the stack (saved at 5317H).
5344
LD H,58H 26 58
Load Register H with 58H, restoring the high byte of HL to point into the directory sector buffer at 5800H (after the forward-link handling that temporarily set H=42H).
5346
LD L,B 68
Load Register L with Register B (the original low-byte entry address saved at 5316H). This fully restores HL to the entry position in the 5800H sector buffer before the forward-link detour.

Entry Advance and Loop Continue
HL now points to the current directory entry. The following advances HL by 20H bytes to the next entry.

5347
LD A,L 7D
Load Register A with Register L (the current low byte of the directory buffer pointer).
5348
ADD A,20H C6 20
ADD 20H (32 decimal) to Register A, advancing the directory pointer by one full directory entry (32 bytes) to the start of the next entry.
534A
LD L,A 6F
Load Register L with Register A, updating HL to point at the next directory entry.
534B
If the NO CARRY FLAG has been set (no carry from ADD A,20H - HL is still within the current sector), LOOP BACK to 530DH to process the next entry. If the ADD produced a carry (HL has advanced beyond the sector boundary), fall through to continue with the next sector.

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.

534D
LD A,(531EH) 3A 1E 53
Fetch the saved within-entry offset from 531EH (the self-modifying byte written at 5321H) into Register A.
5350
XOR A,L AD
XOR Register A with Register L. If the current L (the entry pointer position) matches the saved offset from 531EH, the result is zero (Z FLAG set), confirming this is the entry that was read and modified. A non-zero result means the pointer advanced past the target entry without finding a match.
5351
If the NZ FLAG has been set (the entry pointer does not match the saved offset), JUMP to 535BH to skip the write and continue the loop.
5353
PUSH HL E5
Save Register Pair HL (the current directory entry pointer) onto the stack.
5354
GOSUB to SYS0 routine at 4B1FH (Directory Entry Write/Update). Writes the currently modified directory entry from the in-memory buffer back to the disk directory sector. On return: Z = success, NZ = write error.
5357
If the NZ FLAG has been set (write failed), JUMP to 5434H for the hardware error exit.
535A
POP HL E1
Restore Register Pair HL from the stack, recovering the directory entry pointer after the write.
535B
LD A,L 7D
Load Register A with Register L (the current directory pointer byte) for the end-of-directory test.
535C
INC L 2C
INCrement Register L by 1, advancing the directory pointer past the just-processed sector-boundary position.
535D
CP A,1FH FE 1F
Compare Register A against 1FH. If Register A equals 1FH, the Z FLAG is set - HL has reached the end of the directory (the last valid entry position, 1FH from the sector base, marks the end of the directory structure).
535F
If the NZ FLAG has been set (not at end of directory), LOOP BACK to 530DH to continue scanning directory entries.
5361
JUMP to SYS0 routine at 402DH (DOS READY / No-Error Exit). The directory maintenance scan is complete. All entries have been processed and the PROT command exits successfully.

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.

5364
LD DE,(5394H) ED 5B 94 53
Load Register Pair DE with the 16-bit value stored at 5394H-5395H. This is the self-modifying storage area written at 521BH. It holds the MPW= command-line value pointer (or 0000H if no MPW= was supplied). DE is the pointer passed to the password collector: non-zero = value already in command line, zero = prompt user.
5368
LD HL,53D8H 21 D8 53
Point Register Pair HL to 53D8H, the "MASTER PASSWORD " prompt string in the PROT data area. This is passed to the password collector at 539CH as the display prompt label when interactive entry is required.
536B
GOSUB to the Password Collect with SVC Wrapper at 5396H. Entry: DE = command-line pointer (or 0 for prompt), HL = prompt string address. Collects the password into the buffer at 5600H and issues RST 28H SVC 0E4H to hash it. Returns DE = pointer past the collected field, HL = encoded password result.
536E
PUSH HL E5
Save Register Pair HL (the encoded 2-byte password hash returned by the SVC 0E4H call) onto the stack for the comparisons that follow.
536F
LD C,00H 0E 00
Load Register C with 00H. Register C will accumulate a "match found" flag: 00H = no match yet, non-zero = match found.
5371
GOSUB to the GAT Sector Read Subroutine at 5408H. Entry: C = drive number (loaded from 5370H internally). Reads the GAT sector (track 0, sector 0) of the target drive into the buffer at 5700H. On return: Z = success, NZ = read error with A = 14H.
5374
If the NZ FLAG has been set (GAT read failed), JUMP to 538FH to set the error context suppression bit (OR A,40H) and exit via 4409H.
5377
POP DE D1
Restore Register Pair DE from the stack, recovering the encoded password hash that was saved at 536EH. DE now holds the 2-byte hash of the password the user supplied.
5378
LD HL,0C294H 21 94 C2
Load Register Pair HL with 0C294H. This is the standard "no password" sentinel value used by VTOS. A disk with no master password stores this sentinel in the GAT sector's master password field. If the supplied password hashes to this value, it matches an unprotected disk.
537B
XOR A,A AF
Set Register A to ZERO and clear all flags, specifically the CARRY FLAG, in preparation for the 16-bit SBC comparison that follows.
537C
SBC HL,DE ED 52
16-bit subtract with carry: HL = HL - DE - CARRY. With CARRY=0, this computes 0C294H - (encoded user password). If the result is zero (Z FLAG set), the user's password hash matches the no-password sentinel, meaning the disk has no master password and any input is accepted.
537E
RET Z C8
If the Z FLAG has been set (user password matches the no-password sentinel 0C294H), RETurn with Z set, signaling a valid match to the caller at 524BH. The PROT command may proceed.
537F
LD HL,(57CEH) 2A CE 57
Load Register Pair HL with the 16-bit value stored at 57CEH in the GAT buffer (now loaded at 5700H+CEH). This is the disk's stored master password hash as it exists on disk. The user's supplied password hash (in DE) will be compared against this stored value.
5382
XOR A,A AF
Set Register A to ZERO and clear the CARRY FLAG for the next 16-bit comparison.
5383
SBC HL,DE ED 52
16-bit subtract: HL = (stored master password hash) - (user-supplied password hash). If the result is zero, the Z FLAG is set, indicating the supplied password matches the disk's master password.
5385
RET Z C8
If the Z FLAG has been set (supplied password matches the stored master password), RETurn with Z set. The PROT command may proceed.
5386
LD HL,53F0H 21 F0 53
Point Register Pair HL to 53F0H, the "INVALID MASTER PASSWORD" + 0DH string in the PROT data area. The supplied password did not match either the no-password sentinel or the stored master password.
5389
GOSUB to SYS0 routine at 447BH (Display with CONFIG/SYS processing). Displays the "INVALID MASTER PASSWORD" error message to the user.
538C
JUMP to SYS0 routine at 4030H (Error-Already-Displayed Exit). The PROT command terminates without modifying the disk; the invalid password message has already been shown.

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.

538F
OR A,40H F6 40
OR Register A with 40H, setting bit 6 (the extended-context suppression flag) in the error code currently in A. This prevents SYS4 from appending filename or return-address context to the error message.
5391
JUMP to SYS0 routine at 4409H (DOS Error Exit). Register A holds the error code with the context suppression bit set.

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.

5396
GOSUB to the Password String Collector at 539CH. Entry: DE = command-line pointer to password value (0 = prompt user), HL = prompt label string address. Collects up to 8 characters into the buffer at 5600H, space-padding the remainder. Returns DE = pointer past the collected string, HL = pointer to the end of the collected data in 5600H.
5399
LD A,0E4H 3E E4
Load Register A with 0E4H, the SVC code for the VTOS password hashing service.
539B
RST 28H EF
GOSUB to the RST 28H vector at 400CH (VTOS overlay dispatcher) with A=0E4H. Invokes the password encoding service. Entry: DE = pointer to the 8-byte space-padded password buffer at 5600H. On return: HL = 2-byte encoded password hash. This is the same hash function used throughout VTOS for all password operations.

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.

539C
LD A,D 7A
Load Register A with Register D (the high byte of DE, the command-line value pointer). If DE is 0000H (no command-line value), D=00H; if DE is non-zero, D holds the high byte of the pointer.
539D
OR A,E B3
OR Register A with Register E (the low byte of DE). If DE is 0000H, A remains 00H and the Z FLAG is set, indicating no command-line value was provided. If DE is non-zero, A is non-zero (NZ).
539E
If the Z FLAG has been set (DE is zero, no command-line value), JUMP to 53BBH to display the prompt string (HL) and collect the password interactively via ROM line input.

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, ',', '"').

53A0
LD HL,5600H 21 00 56
Point Register Pair HL to 5600H, the start of the password/name input work buffer. Characters will be copied here from the command line.
53A3
LD B,08H 06 08
Load Register B with 08H, setting the maximum character count to 8. VTOS passwords and disk names are at most 8 characters long.
53A5
LD A,(DE) 1A
Fetch the next character from the command line at the address in Register Pair DE into Register A.
53A6
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return). If Register A equals 0DH, the Z FLAG is set - end of line reached, stop copying.
53A8
If the Z FLAG has been set (end of line), JUMP to 53CFH to space-pad the remaining buffer bytes and return.
53AA
CP A,2CH FE 2C
Compare Register A against 2CH (ASCII ,). If Register A equals 2CH, the Z FLAG is set - a comma delimiter, stop copying.
53AC
If the Z FLAG has been set (comma delimiter), JUMP to 53CFH to pad and return.
53AE
CP A,22H FE 22
Compare Register A against 22H (ASCII " double-quote). If Register A equals 22H, the Z FLAG is set - a closing quote, stop copying.
53B0
If the Z FLAG has been set (closing quote), JUMP to 53CFH to pad and return.
53B2
INC DE 13
INCrement Register Pair DE by 1, advancing the command-line source pointer to the next character.
53B3
LD (HL),A 77
Store Register A (the current command-line character) to the byte at the buffer address in Register Pair HL. Copies one character into the password buffer at 5600H+offset.
53B4
INC HL 23
INCrement Register Pair HL by 1, advancing the buffer destination pointer.
53B5
DECrement B and loop back to 53A5H if not zero. Copies up to 8 characters from the command line into the buffer. When B reaches zero (8 characters copied), falls through to finalize.
53B7
LD DE,5600H 11 00 56
Point Register Pair DE to 5600H, resetting DE to the start of the password buffer. This sets up DE as the pointer passed to the SVC 0E4H call at 539BH.
53BA
RET C9
RETurn to the caller (5396H or 524BH or 5270H). HL points past the last copied character; DE = 5600H (buffer start for SVC encoding).

Interactive Prompt Path
DE was zero (no command-line value). Display the prompt string and collect the password via ROM line input.

53BB
GOSUB to SYS0 routine at 4467H (Display Message to Screen). Entry: HL = pointer to the prompt label string (e.g., "MASTER PASSWORD " at 53D8H or "NEW DISK PACK NAME ? " at 549BH). Displays the prompt to the console so the user knows what to enter.
53BE
LD B,08H 06 08
Load Register B with 08H, the maximum number of characters the user may type (matching the 8-character password/name field length).
53C0
LD HL,5600H 21 00 56
Point Register Pair HL to 5600H, the password input buffer. This is the destination for the ROM line-input routine.
53C3
GOSUB to ROM routine at 0040H (Line Input). Entry: HL = buffer address (5600H), B = maximum characters (08H). Displays a cursor and collects characters until Enter is pressed. Returns: HL = pointer past the last character entered, B = number of characters remaining (08H minus characters entered).
53C6
EX DE,HL EB
Exchange Register Pairs DE and HL. After the ROM line-input call, HL points past the last typed character; after the exchange, DE holds that end pointer and HL holds whatever DE contained before (typically 0000H from the zero test). The subsequent code uses DE as the end-of-input pointer.
53C7
LD H,00H 26 00
Load Register H with 00H. After the exchange, HL needs to be reconstructed as a count. H=00H prepares for a 16-bit offset computation.
53C8
LD L,B 68
Load Register L with Register B (the remaining character count from the ROM input call: 08H minus the number of characters typed). HL now holds the remaining-slots count as a 16-bit value.
53C9
ADD HL,DE 19
ADD Register Pair DE to Register Pair HL. DE = end-of-input pointer; HL = remaining count. Adding them computes the position in the buffer that is (remaining count) bytes before the end pointer. This gives the address of the first unoccupied byte - where space-padding should begin.
53CB
LD A,09H 3E 09
Load Register A with 09H. This is the value (B+1 from the maximum 8+1=9) used to compute the number of space-padding characters needed. The formula is: padding = 09H - B (remaining slots + 1), giving the number of bytes to fill.
53CD
SUB A,B 90
SUBtract Register B from Register A. Result = 09H - B (remaining slots). This gives the number of characters that were actually typed plus one, which when used as the DJNZ count fills the correct number of padding spaces.
53CE
LD B,A 47
Load Register B with Register A (the padding loop count). B now holds the number of space characters to write into the remainder of the 8-byte password buffer.
53CF
LD (HL),20H 36 20
Store 20H (ASCII space) to the byte at the address in Register Pair HL. This writes one space into the current position of the password buffer, padding the unused portion of the 8-byte field.
53D1
INC HL 23
INCrement Register Pair HL by 1, advancing the buffer pointer to the next byte to pad.
53D2
DECrement B and loop back to 53CFH if not zero. Fills B bytes with spaces. When B reaches zero, the 8-byte password buffer at 5600H is fully populated (typed characters followed by spaces).
53D4
LD DE,5600H 11 00 56
Point Register Pair DE to 5600H, the start of the filled password buffer. DE is set here so the SVC 0E4H call at 539BH receives the correct buffer pointer for encoding.
53D7
RET C9
RETurn to the caller (5396H, 525BH, or 5270H) with DE = 5600H (buffer start) and HL = pointer past the last written byte in the buffer.

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.

53D8-53EFH
DEFM "MASTER PASSWORD " + 03H 4D 41 53 54 45 52 20 50 41 53 53 57 4F 52 44 20 20 03
ASCII string: "MASTER PASSWORD " (with two trailing spaces) terminated by 03H (end-of-string sentinel used by 4467H). Displayed as the interactive master password prompt when the user must supply the current master password.
53F0-5407H
DEFM "INVALID MASTER PASSWORD" + 0DH 49 4E 56 41 4C 49 44 20 4D 41 53 54 45 52 20 50 41 53 53 57 4F 52 44 0D
ASCII string: "INVALID MASTER PASSWORD" followed by a carriage return (0DH). Displayed by CALL 447BH at 5389H when the user-supplied master password does not match the disk's stored value.

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.

5408
PUSH DE D5
Save Register Pair DE onto the stack to preserve DE (which holds the encoded password hash at the call site in 5364H) across the disk I/O operation.
5409
PUSH HL E5
Save Register Pair HL onto the stack to preserve HL across the disk I/O.
540A
GOSUB to SYS0 routine at 4B65H (Set Up Track/Sector for Directory I/O). Configures the drive parameter block (IY) for the upcoming sector read on the target drive. Sets track = 0, sector = 0 (the GAT sector).
540D
LD E,00H 1E 00
Load Register E with 00H, setting the sector number to 0 (sector 0 of track 0 = the GAT sector in VTOS's sector addressing).
540F
LD HL,5700H 21 00 57
Point Register Pair HL to 5700H, the GAT sector read buffer. The full 256-byte GAT sector will be deposited here.
5412
GOSUB to SYS0 routine at 4B45H (Read Sector From Disk). Reads one 256-byte sector from the drive into the buffer at HL (5700H). On return: Z = success, NZ = read error.
5415
POP HL E1
Restore Register Pair HL from the stack.
5416
POP DE D1
Restore Register Pair DE from the stack, recovering the encoded password hash saved at 5408H-5409H.
5417
RET Z C8
If the Z FLAG has been set (sector read succeeded), RETurn with Z set. The caller at 5374H will fall through to the password comparison logic.
5418
LD A,14H 3E 14
Load Register A with 14H. This is the VTOS error code for Read Error. If the GAT sector read failed, this error code is placed in A for the error exit.
541A
RET C9
RETurn with NZ set and A=14H (Read Error). The caller at 5374H will JUMP to 538FH to OR A,40H and exit via 4409H.

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.

541B
PUSH DE D5
Save Register Pair DE onto the stack to preserve the current DE value across the disk write operation.
541C
PUSH HL E5
Save Register Pair HL onto the stack.
541D
GOSUB to SYS0 routine at 4B65H (Set Up Track/Sector for Directory I/O). Configures the drive parameter block for track 0, sector 0 (the GAT sector) of the target drive.
5420
LD E,00H 1E 00
Load Register E with 00H, setting the sector number to 0 for the GAT sector write.
5422
LD HL,5700H 21 00 57
Point Register Pair HL to 5700H, the GAT sector buffer. This is the source buffer for the sector write.
5425
GOSUB to SYS0 routine at 4768H (Write Sector to Disk). Writes the 256-byte GAT sector from 5700H to disk. On return: Z = success, NZ = write error.
5428
If the NZ FLAG has been set (write failed), JUMP to 542FH to set A=15H (Write Error) and return NZ.
542A
GOSUB to SYS0 routine at 4772H (Write-Verify / Read-After-Write). Reads the sector back from disk and compares it against the buffer at 5700H to verify the write was successful. On return: A = comparison result byte (06H if mismatch detected).
542D
CP A,06H FE 06
Compare Register A against 06H. If the verify comparison returned 06H (a specific verify-failure code), the Z FLAG is set, indicating the read-after-write detected a discrepancy.
542F
LD A,15H 3E 15
Load Register A with 15H. This is the VTOS error code for Write Error, pre-loaded in A for the NZ return path. Note: this LD A instruction does not affect the flags, so the Z/NZ state from CP A,06H (or from the write failure JR NZ at 5428H) is preserved when RET executes.
5431
POP HL E1
Restore Register Pair HL from the stack.
5432
POP DE D1
Restore Register Pair DE from the stack.
5433
RET C9
RETurn to the caller. If the write and verify both succeeded, Z is set (from the CP A,06H result being NZ after a successful verify). If either failed, NZ is set and A=15H (Write Error).

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.

5434
OR A,40H F6 40
OR Register A with 40H, setting bit 6 (extended-context suppression) in the error code.
5436
JUMP to SYS0 routine at 4409H (DOS Error Exit). The error code in A is passed to the SYS4 error display handler.

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.

5439
LD HL,5442H 21 42 54
Point Register Pair HL to 5442H, the start of the "PARAMETER ERROR" + 0DH string in the PROT data area.
543C
GOSUB to SYS0 routine at 447BH to display "PARAMETER ERROR".
543F
JUMP to SYS0 routine at 4030H (Error-Already-Displayed Exit).

5442H - "PARAMETER ERROR" String Data

ASCII string data terminated by 0DH.

5442-5451H
DEFM "PARAMETER ERROR" + 0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
ASCII string: "PARAMETER ERROR" followed by carriage return (0DH). Displayed when the drive argument or command options are invalid.

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).

5452
LD HL,545BH 21 5B 54
Point Register Pair HL to 545BH, the start of the "INVALID COMMAND DURING PROGRAM CHAINING" + 0DH string.
5455
GOSUB to SYS0 routine at 447BH to display the chaining error message.
5458
JUMP to SYS0 routine at 4030H (Error-Already-Displayed Exit).

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.

545B-5482H
DEFM "INVALID COMMAND DURING PROGRAM CHAINING" + 0DH 49 4E 56 41 4C 49 44 20 43 4F 4D 4D 41 4E 44 20 44 55 52 49 4E 47 20 50 52 4F 47 52 41 4D 20 43 48 41 49 4E 49 4E 47 0D
ASCII string: "INVALID COMMAND DURING PROGRAM CHAINING" followed by carriage return (0DH). Displayed when PROT is invoked while a program chain is in progress (bit 5 of 430FH set).

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).

5483
LD HL,548CH 21 8C 54
Point Register Pair HL to 548CH, the start of the "-NOTHING DONE-" + 0DH string.
5486
GOSUB to SYS0 routine at 447BH to display "-NOTHING DONE-".
5489
JUMP to SYS0 routine at 4030H (Error-Already-Displayed Exit).

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.

548C-549AH
DEFM "-NOTHING DONE-" + 0DH 2D 4E 4F 54 48 49 4E 47 20 44 4F 4E 45 2D 0D
ASCII string: "-NOTHING DONE-" followed by carriage return (0DH). Displayed when no options were provided to the PROT command.
549B-54B2H
DEFM "NEW DISK PACK NAME ? " + 03H 4E 45 57 20 44 49 53 4B 20 50 41 43 4B 20 4E 41 4D 45 20 3F 20 03
ASCII string: "NEW DISK PACK NAME ? " terminated by 03H (end-of-string for 4467H). Displayed as the interactive prompt when the user is asked to supply a new disk name for the NAME= option.
54B3-54CAH
DEFM "NEW MASTER PASSWORD ? " + 03H 4E 45 57 20 4D 41 53 54 45 52 20 50 41 53 53 57 4F 52 44 20 3F 20 03
ASCII string: "NEW MASTER PASSWORD ? " terminated by 03H. Displayed when the user is asked to supply the new master password for the PW= option.
54CB-54F3H
DEFM "NOTE: FILE DIR/SYS IS NOW READ ENABLED" + 0DH 4E 4F 54 45 3A 20 46 49 4C 45 20 44 49 52 2F 53 59 53 20 49 53 20 4E 4F 57 20 52 45 41 44 20 45 4E 41 42 4C 45 44 0D
ASCII string: "NOTE: FILE DIR/SYS IS NOW READ ENABLED" followed by carriage return (0DH). Displayed at 52E6H after the directory sector format migration (updating the comma byte to 0C4H) is completed successfully, informing the user that the maintenance operation ran.
54F4-5513H
DEFS 32 bytes (FCB area) [32 bytes of FCB data]
File Control Block (FCB) area for the PROT command's directory/GAT control file. Populated by CALL 4476H at 5233H. Contains the standard VTOS FCB fields (status, drive config pointer, sector buffer base, byte offset, drive number, directory info byte, EOF offset, record length, sector position, sector count). The 32-byte FCB is passed as DE=54F4H to the open call.
5514-551BH
DEFS 8 bytes (work area) [8 bytes]
Work area following the FCB. Used as overflow or alignment padding for the PROT command's data area. The overlay ends at 551CH with the END directive.
551C
END
End of the PROT overlay. The VTOS loader uses the type 02H transfer record (entry point 5200H) to dispatch to this overlay's main entry. The member occupies addresses 5200H-551CH in the overlay slot.

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 RangePurpose
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

AddressName and Purpose
5200HRENAME 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.
52BDHPassword Hash SVC Stub
Loads A=0D4H and issues RST 28H to invoke the VTOS password encoding service for the renamed entry.
52C0HNew 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.
52D4HField 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.
52E8HField 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.
52FBHDrive 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.
5307HDelimiter 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.
531BHError Exit with Bit 6 Set
OR A,40H / JP 4409H. Sets extended-context suppression bit and exits via the DOS error handler.
5320HFILE SPEC REQUIRED Error
Displays the message at 5329H ("FILE SPEC REQUIRED" + 0DH) via 447BH and exits via 4030H.
533CHRENAME 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).
5358HILLEGAL 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.
537DHSuccessful 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.

5200
LD DE,539AH 11 9A 53
Point Register Pair DE to 539AH, the first filespec template buffer. This is the destination for CALL 441CH to deposit the parsed source filespec (the file to be renamed).
5203
GOSUB to SYS0 routine at 441CH (Extract Filespec). Parses the first filespec from the command line into the buffer at DE (539AH). On return: HL = pointer past the consumed filespec in the command line, Z = filespec found, NZ = not found.
5206
If the NZ FLAG has been set (no filespec was found in the command line), JUMP to 5320H to display "FILE SPEC REQUIRED" and exit via 4030H.
5209
LD A,(DE) 1A
Fetch the first byte of the parsed first filespec from Register Pair DE (539AH). This byte is the drive-number field of the filespec descriptor. A value of 2AH means a wildcard drive was specified.
520A
CP A,2AH FE 2A
Compare Register A against 2AH (ASCII *). If Register A equals 2AH, the Z FLAG is set - a wildcard drive was specified, which RENAME does not permit.
520C
If the Z FLAG has been set (wildcard drive), JUMP to 5320H to display "FILE SPEC REQUIRED" and exit.
520F
LD DE,53BAH 11 BA 53
Point Register Pair DE to 53BAH, the second filespec template buffer. This is the destination for the second CALL 441CH to deposit the new (destination) filespec.
5212
GOSUB to SYS0 routine at 441CH (Extract Filespec) a second time, now parsing the new filespec (the desired new name) from the remaining command line into the buffer at 53BAH. HL advances past any preposition (TO, AS, etc.) and the second filespec. Z flag set = the new filespec was fully parsed and requires no dynamic defaults; NZ = no second filespec found (parsing returned early).
5215
If the Z FLAG has been set (second filespec parsed completely - all fields present, no dynamic defaults needed), JUMP to 521DH to skip the LDIR merge and proceed directly with the parsed new filespec.

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.

5217
DEC HL 2B
DECrement Register Pair HL by 1, backing the command line pointer up one position. This adjusts HL so that the LDIR that follows copies exactly 32 bytes (0020H) starting from the correct position in the first filespec buffer at 539AH.
5218
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H (32 decimal). This is the byte count for the block copy: 32 bytes covers the full filespec descriptor structure (drive byte + 8-char name + 3-char extension + flags/padding).
521B
LDIR ED B0
Block Move: Copy 32 bytes from Source HL (pointing at the first filespec buffer area) to Destination DE (53BAH, the second filespec buffer), INCrementing both HL and DE after each byte. After this LDIR, the second filespec buffer contains an exact copy of the first filespec's fields as a starting baseline for the dynamic defaults.

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.

521D
LD A,(53BAH) 3A BA 53
Fetch the drive byte of the second (new-name) filespec from 53BAH (the first byte of the second filespec buffer). This byte encodes the drive number for the new filespec.
5220
CP A,0EH FE 0E
Compare Register A against 0EH. If Register A is less than 0EH (CARRY set), the second filespec drive byte is below the minimum valid value, indicating that no valid new name was provided at all - no file name, extension, or drive was parsed into the buffer.
5222
If the CARRY FLAG has been set (second filespec drive byte < 0EH - invalid or empty), JUMP to 533CH to display "RENAME IT TO WHAT?" and exit.
5225
LD HL,539AH 21 9A 53
Point Register Pair HL to 539AH, the first filespec buffer. This is the source for the dynamic defaults that 52C0H will read when building the merged new filespec.
5228
LD DE,53BAH 11 BA 53
Point Register Pair DE to 53BAH, the second filespec buffer. 52C0H will write the merged result here.
522B
GOSUB to the New Filespec Normalizer at 52C0H. Entry: HL = first filespec buffer (539AH), DE = second filespec buffer (53BAH). This routine applies dynamic defaults: any field (drive, name, extension) absent from the second filespec is filled in from the corresponding field of the first. On return, 53BAH contains the fully resolved new filespec.
522E
LD DE,539AH 11 9A 53
Point Register Pair DE to 539AH, the first filespec buffer. This will be passed to CALL 4424H as the filespec for the source file that is being renamed.
5231
GOSUB to SYS0 routine at 4424H (Basic File Open). Entry: DE = source filespec pointer (539AH), B = access mode. Opens the source file to obtain its directory entry. On return: HL = FCB pointer, Z = success, NZ = error (file not found, access denied, etc.).
5234
If the NZ FLAG has been set (source file could not be opened), JUMP to 531BH to OR A,40H and exit via DOS error handler 4409H.
5237
LD A,(539BH) 3A 9B 53
Fetch the drive number byte from 539BH (offset +01H in the first filespec buffer at 539AH). This holds the numeric drive number (0-7) of the source file after 441CH parsed it.
523A
AND A,07H E6 07
Mask Register A with 07H, isolating the 3-bit drive number field from any status flags in the upper bits.
523C
CP A,03H FE 03
Compare Register A against 03H. If Register A is greater than or equal to 03H (NO CARRY), the source file is on drive 3 or higher. RENAME on VTOS Model I only supports drives 0-2 (drives 0-2 are the three configured slots in the 4015H and 43C0H drive config tables); drives 3+ are outside the valid rename range.
523E
LD A,25H 3E 25
Load Register A with 25H (VTOS error code 25H = file protection or illegal drive). This pre-loads the error code for the check that follows.
5240
If the NO CARRY FLAG has been set (drive number >= 03H), JUMP to 531BH to exit with error code 25H via 4409H.

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.

5243
LD HL,53BAH 21 BA 53
Point Register Pair HL to 53BAH, the second (new-name) filespec buffer. The scan loop that follows will search through this buffer for the ':' drive separator character.
5246
LD A,(HL) 7E
Fetch the current character from the new filespec buffer position in Register Pair HL into Register A.
5247
INC HL 23
INCrement Register Pair HL by 1, advancing the scan pointer past the just-fetched character.
5248
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return = end of filespec string). If Register A equals 0DH, the Z FLAG is set - reached end of string without finding ':'.
524A
If the Z FLAG has been set (end of string, no ':' found), JUMP to 525FH to insert the correct ':d' drive prefix into the new filespec using the source file's drive number, then proceed.
524C
CP A,03H FE 03
Compare Register A against 03H (ETX = end-of-text marker, alternate string terminator used in VTOS filespec buffers). If Register A equals 03H, the Z FLAG is set.
524E
If the Z FLAG has been set (ETX end-of-text marker found, no ':' in new filespec), JUMP to 525FH to insert the ':d' prefix.
5250
CP A,3AH FE 3A
Compare Register A against 3AH (ASCII :). If Register A equals 3AH, the Z FLAG is set - the drive separator ':' has been found in the new filespec.
5252
If the NZ FLAG has been set (not ':', not end-of-string), LOOP BACK to 5246H to fetch and examine the next character. Continues until ':' or end-of-string is found.

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.

5254
LD A,(53A0H) 3A A0 53
Fetch the source file's drive number from 53A0H (offset +06H in the first filespec buffer at 539AH). This is the numeric drive of the file being renamed.
5257
XOR A,(HL) AE
XOR Register A (source drive number) with the byte at (HL) (the drive digit character from the new filespec, e.g., '0'=30H, '1'=31H). If they differ, the result is non-zero. If equal, result is zero.
5258
AND A,07H E6 07
AND the XOR result with 07H, masking to the low 3 bits. This isolates whether the drive numbers differ in the meaningful bits (0-7), ignoring upper bits that may differ due to ASCII encoding vs. binary drive number.
525A
If the NZ FLAG has been set (the drive digits differ - the rename would move the file to a different drive), JUMP to 5358H to display "ILLEGAL DRIVE SPECIFICATION" and exit. RENAME cannot move files between drives.
525D
JUMP to 526EH to proceed with the rename - the drive numbers match and the new filespec is valid.

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.

525F
DEC HL 2B
DECrement Register Pair HL by 1. After the scan loop terminated at 0DH or 03H, HL points one past the terminator. DEC HL backs up to point at the terminator byte itself.
5260
LD (HL),3AH 36 3A
Store 3AH (ASCII :) at the current position in the new filespec buffer. This overwrites the terminator with the ':' drive separator, inserting it at the end of the filespec string.
5262
INC HL 23
INCrement Register Pair HL by 1, advancing to the next position (where the drive digit will be written).
5263
LD A,(53A0H) 3A A0 53
Fetch the source file's drive number (binary 0-7) from 53A0H into Register A.
5266
AND A,07H E6 07
Mask Register A with 07H to ensure only the 3-bit drive number is used.
5268
ADD A,30H C6 30
ADD 30H to Register A, converting the binary drive number (0-7) to its ASCII digit character ('0'-'7').
526A
LD (HL),A 77
Store Register A (the ASCII drive digit) at the current HL position in the new filespec buffer, immediately after the ':' just written at 5260H.
526B
INC HL 23
INCrement Register Pair HL by 1, advancing to the next position.
526C
LD (HL),0DH 36 0D
Store 0DH (carriage return = end-of-string terminator) at the current HL position, closing the updated new filespec string after the ':d' drive specification that was just appended.

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.

526E
LD HL,(51DEH) 2A DE 51
Load Register Pair HL with the 16-bit FCB pointer stored at 51DEH. This retrieves the previously saved FCB address that points to the source file's directory entry in the SYS2 runtime variable area.
5271
PUSH HL E5
Save Register Pair HL (the source file's FCB pointer) onto the stack. The upcoming CALL 4424H for the new filespec may overwrite the FCB area; this preserves the original pointer.
5272
LD DE,53BAH 11 BA 53
Point Register Pair DE to 53BAH, the second (new-name) filespec buffer. This will be passed to CALL 4424H as the filespec for the destination.
5275
GOSUB to SYS0 routine at 4424H (Basic File Open) for the new filespec. This validates the destination name and drive (confirming the target name does not already exist as a different file, or if creating a new entry). On return: Z = success, NZ/A = error code if the target name conflicts.
5278
POP HL E1
Restore Register Pair HL from the stack, recovering the source file's FCB pointer saved at 5271H.
5279
LD (51DEH),HL 22 DE 51
Store Register Pair HL (the source FCB pointer) back to 51DEH. This re-establishes the saved FCB pointer in case the second CALL 4424H overwrote it.
527C
If the Z FLAG has been set (second CALL 4424H succeeded - the new filename is available), JUMP to 537DH to execute the directory rename (write the new name into the source entry and exit via 402DH).
527F
CP A,18H FE 18
Compare Register A against 18H (VTOS error code 18H = "FILE NOT FOUND" or "OPEN FAILED" - a code indicating that the new name did not conflict with an existing file but could not be opened for another reason, such as the entry being newly allocated in the directory). If Register A equals 18H, Z is set.
5281
If the NZ FLAG has been set (A != 18H - the error is a genuine failure, such as duplicate name or disk full), JUMP to 531BH to exit with the current error code via 4409H.

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.

5284
LD BC,(53A0H) ED 4B A0 53
Load Register Pair BC with the 16-bit value stored at 53A0H (offset +06H in the first filespec buffer at 539AH). This loads the directory entry BCB (Block Control Block) descriptor for the source file, which CALL 4B10H uses to locate the correct directory entry.

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.

5288
PUSH BC C5
Save Register Pair BC (the directory entry BCB descriptor loaded from 53A0H) onto the stack. It will be restored after the LDIR and used again for CALL 4B1FH.
5289
GOSUB to SYS0 routine at 4B10H (Directory Entry Validation). Entry: BC = directory entry BCB descriptor. On return: HL = pointer into the directory buffer at the matching entry, Z = valid entry found, NZ = not found or invalid.
528C
If the NZ FLAG has been set (directory entry not found or invalid), JUMP to 531BH for the error exit.
528F
LD D,H 54
Load Register D with Register H (the high byte of HL, the directory entry pointer returned by 4B10H). This begins the construction of Register Pair DE which will point to offset +05H within the directory entry - the start of the filename field.
5290
LD A,L 7D
Load Register A with Register L (the low byte of the directory entry pointer HL).
5291
ADD A,05H C6 05
ADD 05H to Register A, advancing the low byte of the address by 5. Offset +05H within a VTOS 32-byte directory entry is the start of the 8-character filename field.
5293
LD E,A 5F
Load Register E with Register A (the offset-adjusted low byte). DE now points to the filename field at directory entry offset +05H.
5294
LD HL,51E8H 21 E8 51
Point Register Pair HL to 51E8H, the 11-byte filename/extension work buffer in the SYS2 runtime variable area (8-char name + 3-char extension). This buffer contains the new name and extension parsed from the second filespec by the normalizer at 52C0H.
5297
LD BC,000BH 01 0B 00
Load Register Pair BC with 000BH (11 decimal). This is the byte count for the LDIR: 8 bytes of filename + 3 bytes of extension = 11 bytes total.
529A
LDIR ED B0
Block Move: Copy 11 bytes from Source HL (51E8H, the new filename/extension) to Destination DE (directory entry +05H). This writes the new name and extension directly into the directory entry in the buffer, replacing the old name with the renamed name.
529C
POP BC C1
Restore Register Pair BC (the directory entry BCB descriptor) from the stack.
529D
GOSUB to SYS0 routine at 4B1FH (Directory Entry Write/Update). Writes the modified directory entry (now containing the new filename) from the in-memory buffer back to the disk directory sector. On return: Z = success, NZ = write error.
52A0
If the NZ FLAG has been set (directory write failed), JUMP to 531BH for the error exit.
52A3
GOSUB to SYS6 internal routine at 51B5H. This routine (below the 5200H entry point) performs an additional directory-entry update step, likely finalizing the rename by clearing or re-encoding the old directory entry's name to prevent it appearing as a duplicate. On return: Z = success, NZ = error.
52A6
If the NZ FLAG has been set, JUMP to 531BH for the error exit.
52A9
LD D,H 54
Load Register D with Register H (high byte of HL, the directory buffer pointer returned by 51B5H, pointing at the source entry after its modification).
52AA
LD E,B 58
Load Register E with Register B (the low byte of the directory entry BCB descriptor, used here as the low byte for a new DE pointer into the directory entry for the password field).
52AB
PUSH BC C5
Save Register Pair BC onto the stack for use after the password hash call.
52AC
LD HL,51E8H 21 E8 51
Point Register Pair HL to 51E8H, the filename/extension work buffer. The password encoder called at 52BDH uses HL as the source for encoding the password.
52AF
GOSUB to the Password Hash SVC Stub at 52BDH. Issues RST 28H SVC 0D4H to compute the encoded password for the renamed directory entry. On return: A = the computed password byte for the entry.
52B2
POP BC C1
Restore Register Pair BC from the stack.
52B3
LD (DE),A 12
Store Register A (the computed password hash byte) to the address in Register Pair DE, writing the password byte into the directory entry at the appropriate password field offset.
52B4
GOSUB to SYS6 internal routine at 51C7H. This routine finalizes the rename operation - likely writing any remaining fields or closing the directory sector after all updates are complete. On return: Z = success, NZ = error.
52B7
If the NZ FLAG has been set, JUMP to 531BH for the error exit.
52BA
JUMP to SYS0 routine at 402DH (DOS READY / No-Error Exit). The RENAME command has successfully renamed the file. Control returns to the VTOS command interpreter.

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.

52BD
LD A,0D4H 3E D4
Load Register A with 0D4H, the SVC code for the VTOS password encoding service used during rename operations. This differs from the 0E4H code used in ATTRIB; 0D4H encodes the directory entry's internal name-hash password rather than a user-typed password string.
52BF
RST 28H EF
GOSUB to the RST 28H vector at 400CH (the VTOS overlay dispatcher). With A=0D4H, this invokes the directory-name hash encoding service. On return: A = the 1-byte encoded password/hash value for the renamed entry. Used to prevent stale password data from the old entry from persisting after the rename.

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.

52C0
PUSH DE D5
Save Register Pair DE (the second filespec buffer pointer, 53BAH) onto the stack. It will be restored after the normalizer completes, so the caller can use the merged result in the buffer.
52C1
LD A,(DE) 1A
Fetch the first byte of the second filespec buffer (at DE = 53BAH) into Register A. This is the first character of the new filespec as entered by the user - expected to be a drive letter, a filename character, or an absent field marker.
52C2
CP A,41H FE 41
Compare Register A against 41H (ASCII A). If Register A is less than 41H (CARRY set), the first character is not an uppercase letter - it could be a digit, '/', '.', or end-of-string marker, meaning no drive designator was provided in the new filespec.
52C4
If the CARRY FLAG has been set (first char < 'A'), GOSUB to the Drive Character Validator at 52FBH. This subroutine checks whether the first character is a valid digit/drive char or needs to be replaced with the source drive from the first filespec. If the character is not a valid digit, it scans HL (the first filespec buffer) to find and copy the drive field from the first filespec into DE (the second filespec buffer).
52C7
LD B,2FH 06 2F
Load Register B with 2FH (ASCII /). This sets the delimiter character for the first call to the field scanner at 52D4H, which will scan through the name field of the new filespec up to the '/' separator.
52C9
GOSUB to the Field Scanner / Default Copier at 52D4H with B=2FH. This routine advances DE through the name portion of the new filespec. If the name field is absent (no characters before '/'), it copies the name from the first filespec buffer (HL) into the second. If the name is present, it advances DE past it.
52CC
LD B,2EH 06 2E
Load Register B with 2EH (ASCII .). Sets the delimiter for the second field scanner call, which will handle the extension field separator.
52CE
GOSUB to the Field Scanner / Default Copier at 52D4H with B=2EH. Processes the extension field of the new filespec, applying the default extension from the first filespec if absent.
52D1
POP DE D1
Restore Register Pair DE from the stack, recovering the second filespec buffer pointer (53BAH) saved at 52C0H.
52D2
RET C9
RETurn to the caller at 522BH. The second filespec buffer at 53BAH now contains the fully merged new filespec with all dynamic defaults applied from the first filespec.

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.

52D3
INC DE 13
INCrement Register Pair DE by 1. This entry point advances past the current character and loops back to re-examine the next. Reached from the JR NC at 52DAH when the character was a valid uppercase letter (>= 'A').
52D4
LD A,(DE) 1A
Fetch the current character from the second filespec buffer position in Register Pair DE into Register A.
52D5
CP A,B B8
Compare Register A against Register B (the delimiter character: 2FH for '/' or 2EH for '.'). If Register A equals B, the Z FLAG is set - the delimiter has been found, meaning the new filespec provided a non-empty value for this field.
52D6
If the Z FLAG has been set (delimiter B found - field was present in new filespec), JUMP to 52E6H to INC DE past the delimiter and RETurn. No default copying needed; the new filespec provided this field.
52D8
CP A,41H FE 41
Compare Register A against 41H (ASCII A). If Register A >= 41H (NO CARRY), the character is an uppercase letter or higher - a valid field character in the new filespec. Advance past it.
52DA
If the NO CARRY FLAG has been set (A >= 'A'), LOOP BACK to 52D3H to INC DE and continue scanning.
52DC
CP A,30H FE 30
Compare Register A against 30H (ASCII 0). If Register A is less than 30H (CARRY set), the character is below '0' - a non-alphanumeric character that is not a valid field character and is not the delimiter. This indicates the field is absent in the new filespec.
52DE
If the CARRY FLAG has been set (A < '0'), JUMP to 52E8H to copy the corresponding field from the first filespec as the default for this field.
52E0
CP A,3AH FE 3A
Compare Register A against 3AH (ASCII :). If Register A is less than 3AH (CARRY set), the character is in the range '0'-'9' - a digit, which is a valid field character (drive digit or numeric in name).
52E2
If the CARRY FLAG has been set (digit '0'-'9'), LOOP BACK to 52D3H to advance DE and continue scanning.
52E4
JUMP to 52E8H. The character is in the range 3AH-40H (: ; < = > ? @) which are non-field characters above '9' but below 'A'. Copy the default from the first filespec.
52E6
INC DE 13
INCrement Register Pair DE by 1, advancing past the delimiter character that was matched at 52D5H.
52E7
RET C9
RETurn to the caller (52C9H or 52CEH in the normalizer at 52C0H). The field in the new filespec was present; DE now points past the delimiter.

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.

52E8
PUSH HL E5
Save Register Pair HL (the current position in the first filespec buffer) onto the stack, to restore after the copy loop completes.
52E9
LD A,(HL) 7E
Fetch the current character from the first filespec buffer position in Register Pair HL into Register A. This is the source character to possibly copy into the second filespec.
52EA
INC HL 23
INCrement Register Pair HL by 1, advancing the first filespec buffer pointer to the next character.
52EB
CP A,03H FE 03
Compare Register A against 03H (ETX end-of-text). If Register A equals 03H, the Z FLAG is set - end of the first filespec's field.
52ED
If the Z FLAG has been set (ETX found), JUMP to 52F9H to restore HL and return without writing the delimiter (the source field ended without a delimiter, meaning this is the last field).
52EF
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return end-of-string). If Register A equals 0DH, Z FLAG set.
52F1
If the Z FLAG has been set (0DH end-of-string in source), JUMP to 52F9H to restore HL and return.
52F3
CP A,B B8
Compare Register A against Register B (the delimiter character '/' or '.'). If Register A equals B, the Z FLAG is set - the field delimiter has been reached in the source.
52F4
If the NZ FLAG has been set (A != B, still within the field), LOOP BACK to 52E9H to fetch and process the next character. This scans forward through the first filespec field, but note that A is not written to DE here - the actual character copy occurs in 5307H below.
52F6
GOSUB to the Delimiter Inserter at 5307H. Entry: HL = position just past the field in the first filespec (pointing at the delimiter), DE = destination in second filespec. Copies the field character from (HL-1) into (DE), shifts the remainder of the second filespec buffer to accommodate, and advances DE past the inserted characters. Returns with HL pointing at the next field in the first filespec.
52F9
POP HL E1
Restore Register Pair HL from the stack, recovering the first filespec buffer pointer saved at 52E8H.
52FA
RET C9
RETurn to the caller (the normalizer at 52C9H or 52CEH).

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.

52FB
LD A,(HL) 7E
Fetch the current character from the first filespec buffer (HL) into Register A. This is used to test the character class of the first filespec's current position during the drive-default search.
52FC
CP A,30H FE 30
Compare Register A against 30H (ASCII 0). If Register A is less than 30H (CARRY set), the character is below '0' - not a digit.
52FE
RET C D8
If the CARRY FLAG has been set (character < '0'), RETurn immediately. A character below '0' is a control code or end-of-string; the caller handles this case.
52FF
CP A,3AH FE 3A
Compare Register A against 3AH (ASCII :). If Register A is less than 3AH (CARRY set), the character is a digit '0'-'9', which is a valid drive character.
5301
If the CARRY FLAG has been set (digit '0'-'9' found), JUMP to 5306H to INC HL and proceed with this valid drive character.
5303
CP A,41H FE 41
Compare Register A against 41H (ASCII A). If Register A is less than 41H (CARRY set), the character is in the range 3AH-40H (non-alphanumeric, between '9' and 'A').
5305
RET C D8
If the CARRY FLAG has been set (character between ':' and '@'), RETurn. These non-field characters signal an absent or malformed drive designator.
5306
INC HL 23
INCrement Register Pair HL by 1, advancing the first filespec buffer pointer past the current character. Falls through to the Delimiter Inserter at 5307H.

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.

5307
PUSH HL E5
Save Register Pair HL (first filespec buffer pointer) onto the stack. The shift loop below uses HL for the destination buffer, and HL must be preserved.
5308
LD H,D 62
Load Register H with Register D (high byte of DE, the second filespec buffer pointer). This redirects HL to point into the second filespec destination buffer, using DE's high byte as the page.
5309
LD L,E 6B
Load Register L with Register E (low byte of DE). HL now equals DE - both point to the current position in the second filespec buffer.
530A
LD C,(HL) 4E
Load Register C with the byte currently at (HL) in the second filespec buffer. This saves the byte about to be overwritten so it can be shifted one position forward.
530B
LD (HL),A 77
Store Register A (the source character from the first filespec that is being inserted) at the current (HL) position in the second filespec buffer. This writes the new character, displacing the existing byte (saved in C).
530C
INC HL 23
INCrement Register Pair HL by 1, advancing to the next position in the second filespec buffer.
530D
LD A,C 79
Load Register A with Register C (the byte that was displaced from (HL) at 530AH). This byte now becomes the "character to insert" for the next iteration - each displaced byte shifts forward one position.
530E
CP A,03H FE 03
Compare Register A against 03H (ETX end-of-text). If Register A equals 03H, the Z FLAG is set - the end-of-string has been shifted to the current position and the buffer shift is complete.
5310
If the Z FLAG has been set (ETX reached), JUMP to 5316H to write the final ETX terminator and exit the shift loop.
5312
CP A,0DH FE 0D
Compare Register A against 0DH (carriage return end-of-string). If Register A equals 0DH, Z is set.
5314
If the NZ FLAG has been set (not 0DH or 03H - there are still more characters to shift), LOOP BACK to 530AH to continue shifting the buffer forward one byte at a time.
5316
LD (HL),A 77
Store Register A (the end-of-string marker: 03H or 0DH) at the current HL position, writing the terminator at the new end of the shifted buffer region.
5317
POP HL E1
Restore Register Pair HL from the stack, recovering the first filespec buffer pointer saved at 5307H.
5318
INC DE 13
INCrement Register Pair DE by 1, advancing the second filespec destination pointer past the character that was just inserted.
5319
LOOP BACK to 52FBH to continue processing the remaining characters of this field from the first filespec buffer, inserting the next default character into the second filespec.

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.

531B
OR A,40H F6 40
OR Register A with 40H, setting bit 6 of the current error code. This is the extended-context suppression flag; setting it prevents SYS4 from appending filename/return-address context to the error display.
531D
JUMP to SYS0 routine at 4409H (DOS Error Exit). Register A holds the error code with the extended-context bit set.

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.

5320
LD HL,5329H 21 29 53
Point Register Pair HL to 5329H, the start of the "FILE SPEC REQUIRED" + 0DH string in the RENAME data area.
5323
GOSUB to SYS0 routine at 447BH to display the message.
5326
JUMP to SYS0 routine at 4030H (Error-Already-Displayed Exit).

5329H - "FILE SPEC REQUIRED" String Data

ASCII string data terminated by 0DH. The disassembler decodes these bytes as Z80 instructions; they are data.

5329-533BH
DEFM "FILE SPEC REQUIRED" + 0DH 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
ASCII string: "FILE SPEC REQUIRED" followed by a carriage return (0DH).

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.

533C
LD HL,5345H 21 45 53
Point Register Pair HL to 5345H, the start of the "RENAME IT TO WHAT?" + 0DH string.
533F
GOSUB to SYS0 routine at 447BH to display the message.
5342
JUMP to SYS0 at 4030H (Error-Already-Displayed Exit).

5345H - "RENAME IT TO WHAT?" String Data

ASCII string data terminated by 0DH.

5345-5357H
DEFM "RENAME IT TO WHAT?" + 0DH 52 45 4E 41 4D 45 20 49 54 20 54 4F 20 57 48 41 54 3F 0D
ASCII string: "RENAME IT TO WHAT?" followed by a carriage return (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.

5358
LD HL,5361H 21 61 53
Point Register Pair HL to 5361H, the start of the "ILLEGAL DRIVE SPECIFICATION" + 0DH string.
535B
GOSUB to SYS0 routine at 447BH to display the message.
535E
JUMP to SYS0 at 4030H (Error-Already-Displayed Exit).

5361H - "ILLEGAL DRIVE SPECIFICATION" String Data

ASCII string data terminated by 0DH.

5361-537CH
DEFM "ILLEGAL DRIVE SPECIFICATION" + 0DH 49 4C 4C 45 47 41 4C 20 44 52 49 56 45 20 53 50 45 43 49 46 49 43 41 54 49 4F 4E 0D
ASCII string: "ILLEGAL DRIVE SPECIFICATION" followed by a carriage return (0DH).

537DH - Successful Exit

The rename was completed successfully. Jump directly to the DOS READY exit.

537D
LD HL,5386H 21 86 53
Point Register Pair HL to 5386H. This address is within the string data area at the end of the RENAME overlay. In this context the LD HL is immediately followed by CALL 447BH, suggesting a brief confirmation message ("DUPLICATE FILE NAME" or similar) may be displayed here as a status note before the successful exit. The string at 5386H reads "DUPLICATE FILE NAME" + 0DH.
5380
GOSUB to SYS0 routine at 447BH to display the string at 5386H.
5383
JUMP to SYS0 at 4030H (Error-Already-Displayed Exit). The rename is complete.

5386H - "DUPLICATE FILE NAME" String Data

ASCII string data terminated by 0DH. Displayed when the rename target name already exists in the directory.

5386-5399H
DEFM "DUPLICATE FILE NAME" + 0DH 44 55 50 4C 49 43 41 54 45 20 46 49 4C 45 20 4E 41 4D 45 0D
ASCII string: "DUPLICATE FILE NAME" followed by a carriage return (0DH). Displayed when the new filename specified in the RENAME command already exists as another file on the same drive, making the rename impossible.

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 OffsetField Used by DEVICE
IY+00HJP opcode byte - if 0C3H, this drive slot is active (has a configured driver). DEVICE checks this to skip unconfigured drives.
IY+03H bit 7Not 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-0Encoded 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+06HNumber 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 RangePurpose
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

AddressName and Purpose
5200HDEVICE 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.
52F4HDrive 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.
52FCHDevice 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.
5318HDevice 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.
535CHDisplay 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.
5378HActive 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.
5396HNIL Routing Display
Copies "NIL" (3 bytes) from 53EEH into the output buffer via LDIR, then jumps to 535CH to close the line and advance.
53A0HI/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.
53BFHDecimal 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.
53D3HDivision-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.

5200
LD C,00H 0E 00
Load Register C with 00H, initializing the drive counter to 0. C will be incremented through values 0-7 as each drive slot is examined.
5202
PUSH BC C5
Save Register Pair BC (B = undefined, C = current drive counter 0-7) onto the stack. BC is preserved across the CALL 478FH which may alter registers, and across the entire per-drive display sequence.
5203
GOSUB to SYS0 routine at 478FH (Load IY from Drive Number). Entry: C = drive number (0-7). Sets IY = 4700H + (C x 0AH), pointing IY at the 10-byte drive parameter block for this drive. On return, all IY-indexed accesses read from this drive's parameter block.
5206
LD A,(IY+00H) FD 7E 00
Fetch the first byte of the drive parameter block from IY+00H into Register A. For an active (configured) drive, IY+00H contains 0C3H (the first byte of a JP instruction: JP 4600H). For an unconfigured drive slot, it contains 0C9H (RET) or 00H.
5209
CP A,0C3H FE C3
Compare Register A against 0C3H (the JP opcode). If Register A equals 0C3H, the Z FLAG is set - this drive slot contains an active driver.
520B
If the NZ FLAG has been set (IY+00H is not 0C3H - this drive slot is inactive/unconfigured), JUMP to 52F4H to skip the display for this drive and advance to the next.

This drive is active. Display the status line beginning with ': n ' where n is the drive digit.

520E
LD A,3AH 3E 3A
Load Register A with 3AH (ASCII :). The DEVICE output line begins with a colon followed by the drive number digit.
5210
GOSUB to ROM routine at 0033H (Display Character in A at cursor, advance cursor). Displays the ':' character.
5213
LD A,C 79
Load Register A with Register C (the current drive number, 0-7).
5214
ADD A,30H C6 30
ADD 30H to Register A, converting the binary drive number (0-7) to its ASCII digit character ('0'-'7').
5216
GOSUB to ROM routine at 0033H to display the drive digit character.
5219
LD A,20H 3E 20
Load Register A with 20H (ASCII space character). Two spaces follow the drive digit for formatting.
521B
GOSUB to ROM routine at 0033H to display the first space.
521E
LD A,20H 3E 20
Load Register A with 20H (space) again for the second space.
5220
GOSUB to ROM routine at 0033H to display the second space.

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.

5223
BIT 5,(IY+03H) FD CB 03 6E
Test bit 5 of the drive flags byte at IY+03H. Bit 5 indicates the sector size: 1 = double density / 512-byte sectors, 0 = single density / 256-byte sectors. If bit 5 is set, the Z FLAG is cleared (NZ).
5227
LD A,35H 3E 35
Load Register A with 35H (ASCII 5). This is the sector-size indicator character for single-density / 256-byte sectors. Pre-loaded as the default before the branch test.
5229
If the Z FLAG has been set (bit 5 of IY+03H is clear = single density / 256-byte sectors), JUMP to 522DH to display '5' (35H already in A).
522B
LD A,38H 3E 38
Load Register A with 38H (ASCII 8). This overrides the pre-loaded '5' with '8' for double-density / 512-byte sectors.
522D
GOSUB to ROM routine at 0033H to display either '5' (single density) or '8' (double density) as the sector-size indicator character.

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.

5230
BIT 3,(IY+03H) FD CB 03 5E
Test bit 3 of the drive flags byte at IY+03H. Bit 3 indicates drive type: 1 = double-sided / rigid disk (hard disk), 0 = single-sided / floppy disk.
5234
LD HL,542EH 21 2E 54
Point Register Pair HL to 542EH, which holds a single " character (22H) used as a prefix for the "FLOPPY #" label. Pre-loaded as the default string pointer.
5237
If the Z FLAG has been set (bit 3 clear = floppy disk), JUMP to 523CH to use the 542EH ("FLOPPY #") string pointer already in HL.
5239
LD HL,5439H 21 39 54
Point Register Pair HL to 5439H, the " RIGID #" + 03H label string. This overrides the floppy pointer for hard-disk drives.
523C
GOSUB to SYS0 routine at 4467H (Display Message on Screen). Displays the string at HL (either " FLOPPY #" or " RIGID #") to the console, stopping at the 03H string terminator.
523F
LD A,(IY+04H) FD 7E 04
Fetch the drive flags/density byte from IY+04H into Register A. The low 4 bits of this byte contain an encoded sector-size / density option code that will be displayed as an ASCII hex digit.
5242
AND A,0FH E6 0F
Mask Register A with 0FH, isolating the low 4-bit option code from the upper nibble of IY+04H.
5244
ADD A,90H C6 90
ADD 90H to Register A. This is the first step of the Z80 BCD-to-ASCII hex digit conversion sequence. For a 4-bit value 0-9, adding 90H and applying DAA produces an intermediate result; for A-F (10-15), additional adjustment follows.
5246
DAA 27
Decimal Adjust Register A. Applied after ADD A,90H, this performs the first BCD adjustment step of the hex-digit-to-ASCII conversion.
5247
ADC A,40H CE 40
ADD 40H plus the CARRY FLAG to Register A. This is the second step of the BCD hex digit conversion, using the carry from the DAA to correctly produce ASCII '0'-'9' or 'A'-'F'.
5249
DAA 27
Decimal Adjust Register A again. The second DAA completes the conversion, yielding the correct ASCII character for the 4-bit value: '0' (30H) for value 0 through '9' (39H) for 9, or 'A' (41H) for 10 through 'F' (46H) for 15.
524A
GOSUB to ROM routine at 0033H to display the ASCII hex digit representing the drive's density/option code (0-F).
524D
LD L,(IY+06H) FD 6E 06
Load Register L with the byte at IY+06H (the last track number, 0-based, from the drive parameter block). This is the cylinder count minus 1 (e.g., 34 for a 35-track drive, 39 for a 40-track drive).
5250
LD H,00H 26 00
Load Register H with 00H, forming HL as a 16-bit value with the track count in L and zero in H.
5252
INC HL 23
INCrement Register Pair HL by 1, converting from the 0-based last track number to the total track count (e.g., 34+1 = 35 tracks).
5253
BIT 3,(IY+03H) FD CB 03 5E
Test bit 3 of the drive flags at IY+03H again. If bit 3 is set (double-sided drive), the total track count must be doubled to give the total cylinder count (both sides combined).
5257
If the Z FLAG has been set (bit 3 clear = single-sided drive), JUMP to 525AH to skip the doubling step and use the track count directly.
5259
ADD HL,HL 29
ADD Register Pair HL to itself (HL = HL x 2), doubling the track count to produce the total cylinder count for a double-sided drive.
525A
LD DE,544BH 11 4B 54
Point Register Pair DE to 544BH, the output buffer area for the cylinder count display. The decimal conversion subroutine at 53BFH will write the ASCII digits for the cylinder count into this buffer starting at DE.
525D
GOSUB to the Decimal Display subroutine at 53BFH. Entry: HL = 16-bit cylinder count, DE = output buffer pointer (544BH). Converts HL to a decimal ASCII string (up to 3 digits with leading-zero suppression) and writes it to the buffer at DE. Returns DE advanced past the digits written.
5260
LD HL,5444H 21 44 54
Point Register Pair HL to 5444H, the ", CYL=" label string followed by 03H terminator. This label appears before the cylinder count in the DEVICE output line.
5263
GOSUB to SYS0 routine at 4467H to display the ", CYL=" label string.

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.

5266
BIT 3,(IY+03H) FD CB 03 5E
Test bit 3 of the drive flags at IY+03H once more. This separates the double-sided/rigid disk display path (bit 3 set) from the single-sided/floppy path (bit 3 clear).
526A
If the Z FLAG has been set (bit 3 clear = single-sided / floppy-type drive), JUMP to 527DH to follow the single-density path which tests bit 6 for the 'S'/'D' indicator.
526C
BIT 2,(IY+03H) FD CB 03 56
Test bit 2 of the drive flags at IY+03H. Bit 2 indicates whether the drive medium is removable (1) or fixed (0) - relevant for rigid (hard disk) drives.
5270
LD HL,5451H 21 51 54
Point Register Pair HL to 5451H, the "REMOVABLE" + 03H string. Pre-loaded as the default for bit 2 set.
5273
If the Z FLAG has been set (bit 2 clear = fixed media), JUMP to 5278H to use the "FIXED" string instead.
5275
LD HL,545BH 21 5B 54
Point Register Pair HL to 545BH, the "FIXED" + 03H string. Overrides the "REMOVABLE" pointer for fixed-media drives.
5278
GOSUB to SYS0 routine at 4467H to display either "REMOVABLE" or "FIXED" as the media-type label for this rigid drive.
527B
JUMP to 52EFH to display the carriage-return line terminator and advance to the next drive. For rigid drives, the display line ends after the REMOVABLE/FIXED label.
527D
BIT 6,(IY+03H) FD CB 03 76
Test bit 6 of the drive flags at IY+03H. Bit 6 selects between single-density (bit 6 clear) and double-density (bit 6 set) for the 'S'/'D' indicator character in the floppy-type display path.
5281
LD A,53H 3E 53
Load Register A with 53H (ASCII S for Single density). Pre-loaded as the default.
5283
If the Z FLAG has been set (bit 6 clear = single density), JUMP to 5287H to display 'S' (already in A).
5285
LD A,44H 3E 44
Load Register A with 44H (ASCII D for Double density). Overrides 'S' for double-density drives.
5287
GOSUB to ROM routine at 0033H to display the 'S' or 'D' density indicator character.
528A
LD HL,5461H 21 61 54
Point Register Pair HL to 5461H, the "DEN, SIDES=" + 03H string. This label follows the 'S'/'D' character just displayed.
528D
GOSUB to SYS0 routine at 4467H to display "DEN, SIDES=".
5290
BIT 5,(IY+04H) FD CB 04 6E
Test bit 5 of the second drive parameter byte at IY+04H. This bit selects the sides count: 0 = single-sided (1 side), 1 = double-sided (2 sides).
5294
LD A,31H 3E 31
Load Register A with 31H (ASCII 1 for single-sided). Pre-loaded as default.
5296
If the Z FLAG has been set (bit 5 clear = single-sided, 1 side), JUMP to 529AH to display '1'.
5298
LD A,32H 3E 32
Load Register A with 32H (ASCII 2 for double-sided). Overrides '1' for double-sided drives.
529A
GOSUB to ROM routine at 0033H to display '1' or '2' as the number of sides.
529D
LD HL,546DH 21 6D 54
Point Register Pair HL to 546DH, the ", STEP=" + 03H label string.
52A0
GOSUB to SYS0 routine at 4467H to display ", STEP=".

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.

52A3
LD A,(IY+03H) FD 7E 03
Fetch the full drive flags byte from IY+03H into Register A. Bits 1:0 hold the WD1771 stepping rate code (00=3ms, 01=6ms, 10=10ms, 11=15ms for Model I), but they are stored packed with bit 5 (double-density flag) in a non-contiguous arrangement that requires the rotation below to decode.
52A6
AND A,23H E6 23
AND Register A with 23H (00100011 binary), masking in only bits 5, 1, and 0. This isolates the step-rate bits (1:0) and the density bit (5) while clearing all others.
52A8
LD B,A 47
Save Register A (the masked flags byte with bits 5, 1, 0 set) into Register B for later use.
52A9
RRCA 0F
Rotate Register A Right through Carry (no carry). Shifts all bits right by 1: bit 0 -> carry, bit 5 -> bit 4, bits 1:0 -> bits 0:carry. First of three rotations to extract the step-rate field.
52AA
RRCA 0F
Rotate Register A Right again. Second right rotation.
52AB
RRCA 0F
Rotate Register A Right a third time. After three RRCAs, bit 5 has moved to bit 2, and bits 1:0 have moved to bits 6:5 (through the carry wrap). The combined effect of three RRCAs maps the non-contiguous bits to a contiguous position.
52AC
OR A,B B0
OR Register A with Register B (the original masked value). This merges the rotated bits back with the original bits to reconstruct a combined index value.
52AD
RLCA 07
Rotate Register A Left through Carry (no carry). One left rotation after the three right rotations, net result of two right rotations on the original value with the OR merge in between.
52AE
AND A,0EH E6 0E
AND Register A with 0EH (00001110 binary), isolating the 3-bit index in bits 3:1. This final mask extracts a 0-7 index (as an even byte offset: 0, 2, 4, 6, 8, 10, 12, 14) for lookup into the step-rate string table.
52B0
ADD A,1EH C6 1E
ADD 1EH (30 decimal) to Register A. This adds the base offset 1EH to the 0-14 index, computing the final table address offset within the 54xxH data area. The step-rate pairs begin at 541EH + 0 through 541EH + 14 (0x0E). Adding 1EH to the offset indexes into the lookup table at 541EH: 1EH + 0 = 541EH, 1EH + 2 = 5420H, etc.
52B2
LD L,A 6F
Load Register L with Register A (the computed table offset byte), setting the low byte of HL to point at the correct entry in the step-rate table.
52B3
LD H,54H 26 54
Load Register H with 54H, setting the high byte of HL to 54H. HL now points into the 54xxH data area at the correct step-rate table entry.
52B5
LD A,(HL) 7E
Fetch the first byte of the 2-byte step-rate entry from the table at HL into Register A. This is the first ASCII digit of the step-rate value (e.g., '0', '1', etc.).
52B6
INC HL 23
INCrement Register Pair HL by 1, advancing to the second byte of the step-rate entry.
52B7
GOSUB to ROM routine at 0033H to display the first digit of the step-rate value.
52BA
LD A,(HL) 7E
Fetch the second byte of the 2-byte step-rate entry from the table into Register A. This is the second ASCII digit (or a separator/suffix character).
52BB
GOSUB to ROM routine at 0033H to display the second digit of the step-rate value.
52BE
LD HL,5475H 21 75 54
Point Register Pair HL to 5475H, the " MS" + 03H milliseconds suffix string.
52C1
GOSUB to SYS0 routine at 4467H to display " MS" after the step-rate digits.

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.

52C4
BIT 5,(IY+03H) FD CB 03 6E
Test bit 5 of the drive flags at IY+03H. Bit 5 = 1 means double density is active for this drive.
52C8
If the NZ FLAG has been set (bit 5 set = double density), JUMP to 52EFH to output the carriage return and end the display line. Double-density drives do not display a delay parameter.
52CA
LD HL,5479H 21 79 54
Point Register Pair HL to 5479H, the ", DLY=" + 03H label string for the delay parameter.
52CD
GOSUB to SYS0 routine at 4467H to display ", DLY=".
52D0
BIT 2,(IY+03H) FD CB 03 56
Test bit 2 of the drive flags at IY+03H. Bit 2 selects the delay format: 0 = display " 1" (space + '1', meaning 1ms delay), 1 = display ".5" ('.' + '5', meaning 0.5ms delay).
52D4
LD A,20H 3E 20
Load Register A with 20H (ASCII space). The first character of the " 1" delay format (bit 2 clear case).
52D6
LD B,31H 06 31
Load Register B with 31H (ASCII 1). The second character of the " 1" delay format. B is pre-loaded as the default digit.
52D8
If the Z FLAG has been set (bit 2 clear = 1ms delay), JUMP to 52DEH to display A=20H (space) and B=31H ('1').
52DA
LD A,2EH 3E 2E
Load Register A with 2EH (ASCII .). Overrides the space with '.' for the ".5" delay format.
52DC
LD B,35H 06 35
Load Register B with 35H (ASCII 5). Overrides '1' with '5' for the ".5" delay format.
52DE
GOSUB to ROM routine at 0033H to display the first delay character (space or '.').
52E1
LD A,B 78
Load Register A with Register B (the second delay character: '1' or '5').
52E2
GOSUB to ROM routine at 0033H to display the second delay character ('1' or '5').
52E5
LD A,20H 3E 20
Load Register A with 20H (space). A trailing space follows the delay value for visual separation.
52E7
GOSUB to ROM routine at 0033H to display the trailing space.
52EA
LD A,53H 3E 53
Load Register A with 53H (ASCII S). The letter 'S' is the suffix for the delay unit (milliseconds = "S" shorthand in this context, following the numeric delay and space).
52EC
GOSUB to ROM routine at 0033H to display 'S'.
52EF
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return). This terminates the current device display line.
52F1
GOSUB to ROM routine at 0033H to output the carriage return, ending the display line for this drive.

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.

52F4
POP BC C1
Restore Register Pair BC from the stack (saved at 5202H). Recovers B (undefined) and C (the current drive counter).
52F5
INC C 0C
INCrement Register C by 1, advancing the drive counter to the next drive slot.
52F6
LD A,C 79
Load Register A with Register C (the updated drive counter).
52F7
CP A,08H FE 08
Compare Register A against 08H. If Register A is less than 08H (CARRY set), there are more drive slots to process. If A = 08H (Z set, no carry), all 8 drives have been examined.
52F9
If the NZ FLAG has been set (C not yet 08H), JUMP to 5202H to PUSH BC and process the next drive slot. Loops until all 8 drive parameter blocks have been examined.

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.

52FC
LD HL,4015H 21 15 40
Point Register Pair HL to 4015H, the start of the drive configuration table #1 (the first of three 8-byte entries in the resident DOS work area). The device chain walk begins by reading the first entry of this table.
52FF
LD A,(HL) 7E
Fetch the first byte of the first drive configuration entry from 4015H into Register A. This is the entry type byte: 08H = active terminal entry, 10H = forward-link entry, 00H = empty.
5300
OR A,A B7
OR Register A with itself. Tests whether the first byte of the chain entry is zero. If A=00H (empty chain), the Z FLAG is set.
5301
If the Z FLAG has been set (chain entry is zero = no DCB devices configured in table #1), JUMP to 5366H to advance HL by 08H and check whether the chain pointer has passed the end of the table, either exiting via 402DH or continuing to the second table at 43C0H.
5303
LD DE,53F6H 11 F6 53
Point Register Pair DE to 53F6H, the output line assembly buffer. The driver address and I/O arrow sequence for this chain entry will be built here byte by byte before being displayed via CALL 4467H.
5306
PUSH HL E5
Save Register Pair HL (the current chain entry pointer, 4015H) onto the stack so it can be recovered after building the output line.
5307
LD A,L 7D
Load Register A with Register L (the low byte of the current chain entry address, pointing at the type byte at offset +00H).
5308
ADD A,06H C6 06
ADD 06H to Register A, advancing the address by 6 bytes to reach offset +06H of the chain entry. In the drive configuration entry format, +06H and +07H hold the 2-byte ASCII drive-type identifier (e.g., "JL", "SI", "LO" as documented in Section 2E of the VTOS master reference).
530A
LD L,A 6F
Load Register L with Register A, updating HL to point at offset +06H of the current chain entry (the driver type identifier bytes).
530B
LD A,2AH 3E 2A
Load Register A with 2AH (ASCII *). The output line for a DCB device begins with '*' to distinguish it from the drive lines which begin with ':'.
530D
LD (DE),A 12
Store Register A (2AH, *) to the output buffer at the address in Register Pair DE (53F6H). This is the first character of the device display line.
530E
INC DE 13
INCrement Register Pair DE by 1, advancing the output buffer pointer to the next position.
530F
LD A,(HL) 7E
Fetch the first byte of the 2-byte driver type identifier from offset +06H of the chain entry (e.g., 4AH = J for the "JL" drive type).
5310
LD (DE),A 12
Store Register A (first driver type byte) to the output buffer at DE.
5311
INC DE 13
INCrement Register Pair DE by 1.
5312
INC L 2C
INCrement Register L by 1, advancing HL to offset +07H (the second driver type byte).
5313
LD A,(HL) 7E
Fetch the second driver type byte from offset +07H of the chain entry (e.g., 4CH = L for "JL").
5314
LD (DE),A 12
Store Register A (second driver type byte) to the output buffer at DE.
5315
INC DE 13
INCrement Register Pair DE by 1.
5316
POP HL E1
Restore Register Pair HL from the stack, recovering the chain entry pointer (4015H or equivalent) saved at 5306H.
5317
PUSH HL E5
Save Register Pair HL again onto the stack. The chain entry pointer must be preserved across the bit-test and driver display code that follows.
5318
BIT 4,(HL) CB 66
Test bit 4 of the byte at (HL) (the chain entry type byte at offset +00H). Bit 4 = 1 means this is a type 10H forward-link entry which contains a next-pointer to a DCB; bit 4 = 0 means it is a type 08H terminal/active entry.
531A
If the Z FLAG has been set (bit 4 clear = type 08H terminal entry, the device has no forward-link DCB), JUMP to 533FH to call 53A0H to build the I/O arrow display for the terminal entry.

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.

531C
INC L 2C
INCrement Register L by 1, advancing HL from the type byte at +00H to the low byte of the next-pointer at +01H.
531D
LD A,(HL) 7E
Fetch the low byte of the next-pointer from +01H of the forward-link entry into Register A.
531E
INC L 2C
INCrement Register L by 1, advancing to the high byte of the next-pointer at +02H.
531F
LD H,(HL) 66
Load Register H with the byte at the current (HL) address, fetching the high byte of the next-pointer. HL now contains the high byte in H and the old L from two positions ago.
5320
LD L,A 6F
Load Register L with Register A (the low byte of the next-pointer fetched at 531DH). HL now holds the complete 16-bit next-pointer address, pointing to the target DCB entry.
5321
BIT 7,(HL) CB 7E
Test bit 7 of the byte at the address pointed to by HL (the first byte of the target DCB). Bit 7 = 1 in a DCB's first byte indicates that this device is active and currently routed. Bit 7 = 0 means inactive or unrouted.
5323
If the NZ FLAG has been set (bit 7 set = the DCB is active/routed), JUMP to 5378H to display the active routing information including " <=> " and the driver address.

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.

5325
PUSH HL E5
Save Register Pair HL (the DCB pointer) onto the stack.
5326
GOSUB to the I/O Arrow Builder at 53A0H. Entry: HL = DCB pointer, DE = output buffer pointer. Builds the ' <=> ' or ' <= ' or ' => ' pattern depending on the input/output capability bits (bit 0 and bit 1) of (HL). Advances DE by 5 bytes past the arrow sequence.
5329
LD A,L 7D
Load Register A with Register L (low byte of the DCB pointer).
532A
ADD A,06H C6 06
ADD 06H to Register A, advancing the address to offset +06H of the DCB where the driver address bytes are stored.
532C
LD L,A 6F
Load Register L with Register A, pointing HL at offset +06H of the DCB (the first byte of the driver address).
532D
LD A,2AH 3E 2A
Load Register A with 2AH (ASCII *). Written to the output buffer as the separator before the driver address.
532F
LD (DE),A 12
Store Register A ('*') to the output buffer at DE.
5330
INC DE 13
INCrement Register Pair DE by 1.
5331
LD A,(HL) 7E
Fetch the low byte of the driver address from DCB offset +06H into Register A.
5332
LD (DE),A 12
Store the low byte of the driver address to the output buffer.
5333
INC DE 13
INCrement Register Pair DE by 1.
5334
INC L 2C
INCrement Register L by 1, advancing HL to DCB offset +07H (the high byte of the driver address).
5335
LD A,(HL) 7E
Fetch the high byte of the driver address from DCB offset +07H into Register A.
5336
LD (DE),A 12
Store the high byte of the driver address to the output buffer.
5337
INC DE 13
INCrement Register Pair DE by 1.
5338
POP HL E1
Restore Register Pair HL from the stack (the DCB pointer saved at 5325H).
5339
BIT 4,(HL) CB 66
Test bit 4 of the byte at the current DCB pointer (HL). If bit 4 is set, this DCB itself is a forward-link and there is another entry in the chain to follow.
533B
If the NZ FLAG has been set (bit 4 set = another forward-link follows), LOOP BACK to 531CH to follow the next-pointer chain to the next DCB.
533D
JUMP to 535CH to write the 0DH terminator, display the assembled output buffer line, and advance to the next chain entry.

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.

533F
GOSUB to the I/O Arrow Builder at 53A0H. Builds the space + '<' + '=' + '>' + space arrow sequence into the output buffer at DE, based on the input/output capability bits of (HL). Returns DE advanced by 5.
5342
BIT 3,(HL) CB 5E
Test bit 3 of the byte at (HL) (the current chain entry's type byte or flags byte). Bit 3 = 1 indicates that routing has been established for this device (the ROUTE command was used to redirect I/O from this device to another).
5344
If the NZ FLAG has been set (bit 3 set = routing active), JUMP to 5396H to copy "NIL" to the output buffer and close the line. Wait - the sense is inverted here: bit 3 set would normally mean routing IS active, so the NZ jump to 5396H ("NIL" display) would be wrong unless the bit polarity is reversed. Re-examining: the 5396H path copies "NIL" and 535CH follows with CALL 4467H for display then advance. The 5344H code at JR NZ,5396H - if bit 3 SET jumps to NIL, this means bit 3 = 1 = NOT routed (NIL), bit 3 = 0 = IS routed. The convention is the opposite of what the text says; the fall-through to 5346H is the routed path.
5346
LD A,58H 3E 58
Load Register A with 58H (ASCII X). This begins the routed-device display: 'X' followed by a tick-mark (27H = ') and a 4-digit hex address will show the routing target as "X'nnnn'".
5348
LD (DE),A 12
Store Register A (X) to the output buffer at DE.
5349
INC DE 13
INCrement Register Pair DE by 1.
534A
LD A,27H 3E 27
Load Register A with 27H (ASCII single-quote '). Written as the opening tick mark before the hex address.
534C
LD (DE),A 12
Store Register A (27H) to the output buffer.
534D
INC DE 13
INCrement Register Pair DE by 1.
534E
INC L 2C
INCrement Register L by 1, advancing HL past the current byte to offset +01H of the chain entry (the low byte of the routing target address pointer).
534F
LD A,(HL) 7E
Fetch the byte at HL (the low byte of the routing pointer at offset +01H) into Register A, temporarily saving it.
5350
INC L 2C
INCrement Register L by 1, advancing HL to offset +02H (the high byte of the routing target pointer).
5351
LD H,(HL) 66
Load Register H with the byte at the current HL (the high byte of the routing target pointer). H now has the high byte.
5352
LD L,A 6F
Load Register L with Register A (the low byte fetched at 534FH). HL now holds the complete 16-bit routing target address.
5353
EX DE,HL EB
Exchange Register Pairs DE and HL. After the exchange: HL = the output buffer pointer (53F6H area), DE = the routing target address. This sets up the call to 4DE7H which expects DE = value to display.
5354
GOSUB to SYS0 routine at 4DE7H (Hex Display Routine). Entry: DE = 16-bit value to display. Outputs 4 ASCII hex digits for the routing target address directly to the console. Note: this outputs to screen, not to the DE buffer - DE was already exchanged with HL.
5357
EX DE,HL EB
Exchange Register Pairs DE and HL again, restoring DE = output buffer pointer and HL = routing target address (for any further use).
5358
LD A,27H 3E 27
Load Register A with 27H (ASCII single-quote). Written as the closing tick mark after the hex address to complete the "X'nnnn'" format.
535A
LD (DE),A 12
Store Register A (closing tick 27H) to the output buffer at DE.
535B
INC DE 13
INCrement Register Pair DE by 1.
535C
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return). This is the 0DH terminator that closes the output line being assembled in the buffer at 53F6H.
535E
LD (DE),A 12
Store Register A (0DH terminator) to the current DE position in the output buffer, closing the assembled line.
535F
LD HL,53F6H 21 F6 53
Point Register Pair HL to 53F6H, the start of the output line assembly buffer. The completed line is now ready to display.
5362
GOSUB to SYS0 routine at 4467H to display the complete assembled output line (starting with '*' and containing the type bytes, I/O arrows, routing info, and address) to the console.
5365
POP HL E1
Restore Register Pair HL from the stack (the chain entry pointer saved at 5317H), positioning HL back at the current chain entry so the advance-and-loop code can increment it.

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.

5366
LD A,L 7D
Load Register A with Register L (the low byte of the current chain pointer HL).
5367
ADD A,08H C6 08
ADD 08H to Register A, advancing the low byte of the chain pointer by 8 bytes to the start of the next 8-byte chain entry.
5369
LD L,A 6F
Load Register L with Register A, updating HL to point at the next chain entry.
536A
If the CARRY FLAG has been set (ADD A,08H produced a carry - the address has wrapped past the 256-byte page boundary, i.e., HL's page has overflowed), JUMP to 402DH (DOS READY / No-Error Exit). The chain walk is complete.
536D
CP A,2DH FE 2D
Compare Register A against 2DH (45 decimal). The SYS2 documentation indicates that the 4015H table's end sentinel is detected when L reaches 2DH (the chain walk in SYS2 at 4E3FH uses the same test). If A equals 2DH, Z is set - end of the first table has been reached.
536F
If the NZ FLAG has been set (not at end sentinel 2DH), JUMP to 52FFH to read and process the next chain entry.
5372
LD HL,43C0H 21 C0 43
Point Register Pair HL to 43C0H, the start of the second drive configuration table (table #2). After exhausting the 4015H table entries, the walk continues with the 43C0H table.
5375
JUMP to 52FFH to read the first entry of the 43C0H table and continue the chain walk.

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.

5378
PUSH HL E5
Save Register Pair HL (the DCB pointer) onto the stack for recovery after the LDIR.
5379
LD HL,53F1H 21 F1 53
Point Register Pair HL to 53F1H, the 5-byte " <=>" routing indicator string (" " + "<" + "=" + ">" + " " = 20H 3CH 3DH 3EH 20H). This string visually indicates that the device has an active bidirectional routing link.
537C
LD BC,0005H 01 05 00
Load Register Pair BC with 0005H. This is the byte count for the LDIR: 5 bytes for the " <=>" string.
537F
LDIR ED B0
Block Move: Copy 5 bytes from Source HL (53F1H, the " <=>" string) to Destination DE (the output buffer). After the LDIR, DE points 5 bytes further into the output buffer.
5381
POP HL E1
Restore Register Pair HL from the stack (the DCB pointer saved at 5378H).
5382
LD A,L 7D
Load Register A with Register L (the low byte of the DCB pointer).
5383
ADD A,06H C6 06
ADD 06H to Register A, advancing the DCB address to offset +06H (the low byte of the driver address).
5385
LD L,A 6F
Load Register L with Register A, pointing HL at DCB offset +06H.
5386
LD C,(HL) 4E
Load Register C with the byte at (HL) (the low byte of the driver address at DCB +06H).
5387
INC HL 23
INCrement Register Pair HL by 1, advancing to DCB offset +07H (the high byte of the driver address).
5388
LD B,(HL) 46
Load Register B with the byte at (HL) (the high byte of the driver address at DCB +07H). BC now holds the complete 16-bit driver address.
5389
PUSH DE D5
Save Register Pair DE (the output buffer pointer) onto the stack. CALL 44BBH will output to the console directly, not to the buffer; DE must be preserved.
538A
GOSUB to SYS0 routine at 44BBH. Entry: BC = 16-bit driver address. Displays the driver module name or address associated with BC to the console. This is used to identify the routing target by its driver address/name.
538D
POP DE D1
Restore Register Pair DE from the stack, recovering the output buffer pointer.

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.

538E
LD A,(DE) 1A
Fetch the current byte from the output buffer at Register Pair DE into Register A.
538F
CP A,03H FE 03
Compare Register A against 03H (ETX end-of-string terminator). If Register A equals 03H, the Z FLAG is set - the end of the 44BBH-written content has been reached.
5391
If the Z FLAG has been set (03H found = end of string), JUMP to 535CH to write 0DH and display the completed line.
5393
INC DE 13
INCrement Register Pair DE by 1, advancing the scan pointer past the current byte.
5394
LOOP BACK to 538EH to continue scanning for the 03H terminator.

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.

5396
LD HL,53EEH 21 EE 53
Point Register Pair HL to 53EEH, the 3-byte "NIL" string (4EH 49H 4CH = "NIL"). This is the routing indicator for a device that has no active ROUTE destination.
5399
LD BC,0003H 01 03 00
Load Register Pair BC with 0003H. Byte count for the LDIR: 3 bytes for "NIL".
539C
LDIR ED B0
Block Move: Copy 3 bytes from Source HL (53EEH, "NIL") to Destination DE (output buffer at current position). DE advances by 3.
539E
JUMP to 535CH to write the 0DH terminator, display the assembled line, restore HL from the stack, and advance to the next chain entry.

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.

53A0
LD A,20H 3E 20
Load Register A with 20H (ASCII space). The I/O arrow sequence always begins with a leading space for visual separation from the preceding field.
53A2
LD (DE),A 12
Store Register A (space) to the output buffer at DE.
53A3
INC DE 13
INCrement Register Pair DE by 1.
53A4
BIT 0,(HL) CB 46
Test bit 0 of the byte at (HL) (the chain entry flags byte). Bit 0 = 1 means this device is capable of input (receiving data).
53A6
If the Z FLAG has been set (bit 0 clear = no input capability), JUMP to 53AAH to write a space instead of the '<' arrow character.
53A8
LD A,3CH 3E 3C
Load Register A with 3CH (ASCII <). The '<' arrow indicates that data flows into this device (input capability present).
53AA
LD (DE),A 12
Store Register A (either '<' or space) to the output buffer at DE.
53AB
INC DE 13
INCrement Register Pair DE by 1.
53AC
LD A,3DH 3E 3D
Load Register A with 3DH (ASCII =). The '=' character is the center of the I/O arrow display, always present regardless of capability bits.
53AE
LD (DE),A 12
Store Register A ('=') to the output buffer.
53AF
INC DE 13
INCrement Register Pair DE by 1.
53B0
LD A,20H 3E 20
Load Register A with 20H (space). Pre-loaded as the default for the output arrow position (bit 1 clear = no output = display space).
53B2
BIT 1,(HL) CB 4E
Test bit 1 of the byte at (HL). Bit 1 = 1 means this device is capable of output (sending data).
53B4
If the Z FLAG has been set (bit 1 clear = no output capability), JUMP to 53B8H to write space instead of '>'.
53B6
LD A,3EH 3E 3E
Load Register A with 3EH (ASCII >). The '>' arrow indicates that data flows out of this device (output capability present).
53B8
LD (DE),A 12
Store Register A (either '>' or space) to the output buffer at DE.
53B9
INC DE 13
INCrement Register Pair DE by 1.
53BA
LD A,20H 3E 20
Load Register A with 20H (trailing space). The I/O arrow sequence ends with a trailing space for visual separation.
53BC
LD (DE),A 12
Store Register A (trailing space) to the output buffer.
53BD
INC DE 13
INCrement Register Pair DE by 1. DE now points 5 bytes past its original position (space + input_arrow + '=' + output_arrow + space).
53BE
RET C9
RETurn to the caller (5326H, 533FH, or other). The 5-character I/O arrow sequence has been written to the output buffer and DE has been advanced by 5.

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.

53BF
LD A,20H 3E 20
Load Register A with 20H (ASCII space). This is the initial leading character passed to 53D3H. The division algorithm uses this to suppress leading zeros: if the quotient for a digit position is zero and the leading character is still space (no significant digit has been found yet), a space is written instead of '0'.
53C1
LD BC,0064H 01 64 00
Load Register Pair BC with 0064H (100 decimal). This is the divisor for extracting the hundreds digit.
53C4
GOSUB to the Division-by-Subtraction Digit Extractor at 53D3H. Entry: HL = value, BC = 100 (divisor), A = leading character (space). Subtracts 100 from HL repeatedly; the count is the hundreds digit. Writes a space (if zero and leading) or '0'+count to (DE), advances DE. Returns HL = remainder (0-99), A updated for the next digit's leading-zero test.
53C7
LD BC,000AH 01 0A 00
Load Register Pair BC with 000AH (10 decimal). Divisor for the tens digit extraction.
53CA
GOSUB to the Division-by-Subtraction Digit Extractor at 53D3H again. Entry: HL = remainder from the hundreds extraction (0-99), BC = 10, A = leading character (updated from the previous call). Extracts the tens digit. Returns HL = units digit (0-9), A updated.
53CD
LD A,L 7D
Load Register A with Register L (the units digit, 0-9, which is the final remainder in HL after both subtractions).
53CE
ADD A,30H C6 30
ADD 30H to Register A, converting the binary units digit (0-9) to its ASCII character ('0'-'9'). The units digit is always displayed, never suppressed as a leading zero.
53D0
LD (DE),A 12
Store Register A (the ASCII units digit) to the output buffer at DE.
53D1
INC DE 13
INCrement Register Pair DE by 1, advancing the output buffer pointer past the units digit just written.
53D2
RET C9
RETurn to the caller (525DH). The 16-bit value has been converted to a 1-3 digit decimal ASCII string and written to the buffer at 544BH (DE).

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).

53D3
PUSH DE D5
Save Register Pair DE (the output buffer pointer) onto the stack. The digit-extraction loop may modify DE; it must be recovered to place the result at the correct position.
53D4
LD E,A 5F
Load Register E with Register A (the current leading character: initially 20H space, then transitions to a digit character once a non-zero digit is found). E temporarily holds this character while DE is being used as the count register.
53D5
LD D,0FFH 16 FF
Load Register D with 0FFH. D is initialized to FFH so that the first INC D at 53D8H produces 00H, making D a zero-based count of how many times BC has been subtracted from HL.
53D7
XOR A,A AF
Set Register A to ZERO and clear all flags. Clears A before the SBC HL,BC instruction below, ensuring the CARRY flag used by SBC is clean.
53D8
INC D 14
INCrement Register D by 1. D counts the number of times BC has been subtracted from HL (the quotient digit). First iteration: D becomes 00H.
53D9
SBC HL,BC ED 42
SUBtract Register Pair BC from Register Pair HL with Carry (carry was cleared by XOR A above). SBC HL,BC subtracts the divisor (100 or 10) from the remaining value. If the result goes negative, the CARRY FLAG is set.
53DB
If the NO CARRY FLAG has been set (subtraction was non-negative - HL was still >= BC), LOOP BACK to 53D8H to INC D and subtract again. Continues until HL goes negative (underflows), at which point CARRY is set and the loop exits.
53DD
ADD HL,BC 09
ADD Register Pair BC back to Register Pair HL. The last subtraction went one too far (made HL negative); this addition restores HL to the correct non-negative remainder (HL mod BC).
53DE
LD A,E 7B
Load Register A with Register E (the leading character saved at 53D4H - either space 20H or a previously output digit character).
53DF
LD B,D 42
Load Register B with Register D (the computed quotient digit, 0-9). B now holds the digit count.
53E0
POP DE D1
Restore Register Pair DE from the stack (the output buffer pointer saved at 53D3H).
53E1
LD (DE),A 12
Store Register A (the leading character, space 20H) to the output buffer at DE. This tentatively writes a space, which will be overwritten if the digit is non-zero.
53E2
INC B 04
INCrement Register B by 1 (B = quotient digit + 1). This tests for zero: if the quotient was 0, B becomes 1 (NZ); if the quotient was 255+1 overflow... but since D was initialized at FFH and INC D makes it 00H first, B=D=count. If count=0 (divisor was larger than HL from the start), B=0, then INC B makes B=1 (NZ). So INC B followed by DEC B tests whether the original count was 0.
53E3
DEC B 05
DECrement Register B by 1. If B was 1 (from the INC of a 0-count digit), DEC makes it 0 and sets Z FLAG - the digit is zero. If B was > 1, DEC leaves B >= 1 and NZ.
53E4
If the Z FLAG has been set (the digit is zero), JUMP to 53ECH to advance DE without updating the leading character or writing a digit. The space written at 53E1H remains as the zero-suppressed placeholder.
53E6
LD A,B 78
Load Register A with Register B (the quotient digit, 1-9). This is a non-zero digit.
53E7
ADD A,30H C6 30
ADD 30H to Register A, converting the binary digit (1-9) to its ASCII character ('1'-'9').
53E9
LD (DE),A 12
Store Register A (the ASCII digit '1'-'9') to the output buffer at DE, overwriting the space that was written at 53E1H.
53EA
LD A,30H 3E 30
Load Register A with 30H (ASCII 0). After finding a non-zero digit, the leading-zero character transitions from space to '0', so that subsequent zero digits in lower positions are displayed as '0' rather than suppressed as spaces.
53EC
INC DE 13
INCrement Register Pair DE by 1, advancing the output buffer pointer past the digit (or space) just written.
53ED
RET C9
RETurn to the caller (53C4H or 53CAH in 53BFH). A = the updated leading character (space if still suppressing, '0' if a non-zero digit was found), HL = remainder for the next digit position, DE = output buffer advanced by 1.

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.

53EE-53F0H
DEFM "NIL" 4E 49 4C
ASCII string: "NIL". Copied into the output buffer when a device has no routing target.

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.

53F1-53F5H
DEFM " <=> " 20 3C 3D 3E 20
ASCII string: space + "<=>" + space. Displayed between device type identifier and routing target address when an active routing link is present.

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.

541E-542DH
DEFM (step rate table, 8 x 2 bytes) 20 36 31 32 32 30 34 30 20 33 20 36 31 30 31 35
Step-rate lookup table - 8 pairs of ASCII characters providing the step-rate display values for each combination of the step-rate encoding bits. Each pair is indexed via the formula at 52AEH-52B3H. The pairs decode as: " 6", "12", "20", "4 ", " 3", " 6", "10", "15" - representing the WD1771 step rates in milliseconds for Model I drives (3ms, 6ms, 10ms, 15ms) and their Model III equivalents (6ms, 12ms, 20ms, 30ms), with spacing for alignment.

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.

542E
DEFB 22H 22
Single byte 22H (ASCII "). The separator character placed before " FLOPPY #" in the display output. Pointed to by LD HL,542EH at 5234H when bit 3 of IY+03H is clear.
542F-5438H
DEFM " FLOPPY #" + 03H 20 46 4C 4F 50 50 59 20 23 03
ASCII string: " FLOPPY #" followed by 03H string terminator. Displayed via CALL 4467H for single-sided / floppy-type drives.
5439-5443H
DEFM " RIGID #" + 03H 22 20 52 49 47 49 44 20 20 23 03
ASCII string: " RIGID #" (with two spaces before the '#') followed by 03H. Displayed for double-sided / hard-disk drives. Note: the leading 22H byte at 5439H is the opening " that the disassembler decodes as an LD (nnnn),HL instruction; it is the display prefix character.
5444-544AH
DEFM ", CYL=" + 03H 2C 20 43 59 4C 3D 03
ASCII string: ", CYL=" followed by 03H. Displayed after the drive number via CALL 4467H at 5263H.
544B-5450H
DEFM " " + format bytes 20 20 2C 20 03
Spacing and separator bytes used as the output buffer target for the 53BFH decimal display of the cylinder count. Contains spaces and the sub-format string.
5451-545AH
DEFM "REMOVABLE" + 03H 52 45 4D 4F 56 41 42 4C 45 03
ASCII string: "REMOVABLE" followed by 03H. Displayed when bit 2 of IY+03H is set (removable media hard disk).
545B-5460H
DEFM "FIXED" + 03H 46 49 58 45 44 03
ASCII string: "FIXED" followed by 03H. Displayed when bit 2 of IY+03H is clear (fixed media hard disk).
5461-546CH
DEFM "DEN, SIDES=" + 03H 44 45 4E 2C 20 53 49 44 45 53 3D 03
ASCII string: "DEN, SIDES=" followed by 03H. Displayed after the 'S'/'D' density character via CALL 4467H at 528DH.
546D-5474H
DEFM ", STEP=" + 03H 2C 20 53 54 45 50 3D 03
ASCII string: ", STEP=" followed by 03H. Displayed after the sides count via CALL 4467H at 52A0H.
5475-5478H
DEFM " MS" + 03H 20 4D 53 03
ASCII string: " MS" followed by 03H. The milliseconds suffix appended after the step-rate digits via CALL 4467H at 52C1H.
5479-547FH
DEFM ", DLY=" + 03H 2C 20 44 4C 59 3D 03
ASCII string: ", DLY=" followed by 03H. Displayed for the delay parameter via CALL 4467H at 52CDH.

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

AddressPurpose
402DHDOS 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-402CHDrive configuration table #1 (3 entries x 8 bytes). Scanned by the device chain scanner at 5288H for both source and destination device specifications.
4049HHigh 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-43D7HDrive configuration table #2 (3 entries x 8 bytes). Scanned as the overflow table when table #1 does not contain a matching entry.
526AH-527CHRelay 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.
5288HDevice 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.
5304HFilespec work area template #1 (for the first device argument). Used as the buffer argument to 441CH at 520BH.
5324HFilespec work area template #2 (for the second device argument). Used as the buffer argument to 441CH at 521AH.

Key Subroutines

AddressPurpose
441CHExtract filespec / device spec token from the command line. Returns Z with DE pointing to the parsed token on success.
4467HDisplay message string pointed to by HL on the screen.
447BHDisplay string with CONFIG/SYS processing. Used for error messages.
001BHROM: 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.
0013HROM: 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.
402DHDOS READY / No-Error Exit. Jumped to at end of successful command.
4030HError-already-displayed exit. Jumped to after displaying an error message.
4409HDOS Error Exit. Jumped to for fatal errors via OR A,40H / JP 4409H.

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.

5200
LD A,(402DH) 3A 2D 40
Fetch the byte at address 402DH (the first byte of the DOS READY SVC stub / No-Error Exit) into Register A. In unmodified VTOS at command level, this byte is C3H (the JP opcode). If a user program has replaced this byte, the system is not at VTOS command level.
5203
Compare Register A against 0C3H (the Z80 unconditional JUMP opcode, C3H). If Register A equals 0C3H, the Z FLAG is set, indicating VTOS is at command level and the LINK operation can proceed. If NZ, a foreground program has modified 402DH.
5205
If the Z FLAG (Zero) is set - meaning 402DH contains C3H (the system IS at VTOS command level) - JUMP to 52D1H to display the "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" error. This jump is taken when the system is at command level; the fall-through path executes when the system is NOT at command level (foreground program active).

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.

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.

5208
Point Register Pair DE to address 5304H, the first filespec work area template. This template provides the buffer into which the first parsed device specification token will be written by SYS0 routine 441CH.
520B
GOSUB to SYS0 routine at 441CH to extract the first device specification token from the command line buffer. On return: Z flag set = token found, DE points to the parsed token; NZ = no argument present.
520E
If the NZ FLAG (Not Zero) is set - meaning no first argument was found on the command line - JUMP to 52B3H to display the "DEVICE SPEC REQUIRED" error message.
5211
LD A,(DE) 1A
Fetch the first character of the parsed token (at DE, the start of the filespec work area after 441CH has filled it) into Register A. This character must be '*' (ASCII 2AH) for a valid VTOS device specification.
5212
Compare Register A against 2AH (ASCII '*', the VTOS device specification prefix). If Register A equals 2AH, the Z FLAG is set; otherwise NZ.
5214
If the NZ FLAG (Not Zero) is set - meaning the first argument does not begin with '*' - JUMP to 52B3H to display the "DEVICE SPEC REQUIRED" error message.

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.

5217
Point Register Pair DE to address 5324H, the second filespec work area template. This separate buffer will receive the second parsed device specification token, keeping it distinct from the first (which was parsed into 5304H).
521A
GOSUB to SYS0 routine at 441CH to extract the second device specification token from the command line. Any intervening noise word (such as "TO") is silently consumed. On return: Z = found, NZ = not found.
521D
If the NZ FLAG (Not Zero) is set - meaning no second argument was found - JUMP to 52B3H to display the "DEVICE SPEC REQUIRED" error.
5220
LD A,(DE) 1A
Fetch the first character of the second parsed token (at DE, which now addresses the second filespec work area at 5324H after 441CH has filled it) into Register A. This must also be '*' for a valid device specification.
5221
Compare Register A against 2AH (ASCII '*'). If equal, Z FLAG is set; otherwise NZ.
5223
If the NZ FLAG (Not Zero) is set - meaning the second argument does not begin with '*' - JUMP to 52B3H to display the "DEVICE SPEC REQUIRED" error.

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.

5226
LD DE,(5305H) ED 5B 05 53
Load Register Pair DE with the 2-byte value stored at address 5305H. After 441CH has parsed the first device token into the work area at 5304H, the 2-byte device-type identifier (the ASCII characters immediately after '*', e.g., "KI" for *KI) is stored at 5305H-5306H. DE now holds this identifier for the device chain scanner.
522A
GOSUB to the device chain scanner at 5288H (within this overlay). This routine scans drive configuration tables #1 (4015H-402CH) and #2 (43C0H-43D7H), comparing the device-type field (+06H/+07H in each entry) against DE. On return: Z = found, HL points to the matching DCB entry; NZ = not found.
522D
If the NZ FLAG (Not Zero) is set - meaning the first device was not found in either configuration table - JUMP to 52AEH to report a fatal error via OR A,40H / JP 4409H.

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.

5230
PUSH HL E5
Save Register Pair HL (the DCB entry address of the first device, returned by the scanner at 5288H) onto the stack. It will be recovered at 5241H after the second device has been located.
5231
LD DE,(5325H) ED 5B 25 53
Load Register Pair DE with the 2-byte value stored at address 5325H. After 441CH has parsed the second device token into the work area at 5324H, the device-type identifier is at 5325H-5326H. DE now holds the second device's type identifier for the scanner.
5235
GOSUB to the device chain scanner at 5288H to locate the second device's DCB entry. On return: Z = found, HL = address of the second device's DCB entry; NZ = not found.
5238
If the NZ FLAG (Not Zero) is set - meaning the second device was not found - JUMP to 52AEH to report a fatal error.
523B
Self-Modifying Code
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.
523E
Self-Modifying Code
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.
5241
POP HL E1
Restore Register Pair HL (the DCB entry address of the first device, saved at 5230H) from the stack. HL again points to the first device's DCB entry in the drive configuration tables.
5242
INC HL 23
INCrement Register Pair HL by 1, advancing from the type byte (offset +00H) to the low byte of the forward-link / next-device pointer (offset +01H) within the first device's DCB entry. Each DCB entry stores a 2-byte address pointer at offsets +01H/+02H.
5243
PUSH HL E5
Save Register Pair HL (pointing to offset +01H of the first device's DCB entry) onto the stack. This address is needed at 5264H and 5266H to write the relay stub's installed address into the DCB's pointer field.
5244
LD A,(HL) 7E
Fetch the byte at HL (the low byte of the current forward-link pointer at offset +01H of the first device's DCB entry) into Register A. This is the low byte of the address that was previously chained into this slot - it will be written into the relay stub's "pass-through" operand so that the relay stub can forward I/O to the original device after duplicating it to the linked device.
5245
INC HL 23
INCrement Register Pair HL by 1, advancing from offset +01H to offset +02H (the high byte of the forward-link pointer) within the first device's DCB entry.
5246
LD H,(HL) 66
Load Register H with the byte at HL (the high byte of the forward-link pointer at offset +02H of the first DCB entry). After this, A holds the low byte and H holds the high byte of the existing forward-link address stored in the first device's DCB.
5247
LD L,A 6F
Load Register L with the value in Register A (the low byte of the existing forward-link pointer). Register Pair HL now holds the complete 2-byte address that was previously installed as the forward-link in the first device's DCB entry. This existing pointer will be incorporated into the relay stub so that I/O can be forwarded to its original destination after being duplicated.
5248
Self-Modifying Code
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.
524B
Self-Modifying Code
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.

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.

524E
LD HL,(4049H) 2A 49 40
Load Register Pair HL with the 2-byte value stored at address 4049H, the High Memory pointer (the current top of usable RAM). The relay stub will be installed just below this address.
5251
LD BC,001EH 01 1E 00
Load Register Pair BC with the value 001EH (30 decimal). This is the size of the relay stub (30 bytes). BC will be used both as the LDIR byte count and to decrement HL to find the installation address.
5254
XOR A,A AF
Set Register A to ZERO and clear all flags. This clears the carry flag in preparation for the SBC (Subtract with Carry) at 5255H, which performs a 16-bit subtraction.
5255
SBC HL,BC ED 42
Subtract Register Pair BC (001EH = 30) from Register Pair HL (the current High Memory top at 4049H) with borrow, storing the result in HL. HL now holds the installation address for the relay stub: High Memory minus 30 bytes. This is the address of the first byte of the stub in high RAM.
5257
LD (4049H),HL 22 49 40
Store Register Pair HL (the new, lower High Memory top after subtracting 30 bytes) into address 4049H, updating the High Memory pointer. The 30-byte block from HL to (HL+1DH) is now reserved for the relay stub. Future VTOS memory allocations will not overlap this space.
525A
INC HL 23
INCrement Register Pair HL by 1. After storing the new High Memory top, HL pointed to the first byte of the reserved block. Incrementing HL by 1 here sets up the LDIR destination to be HL+1 (one byte above the new High Memory top). This byte is the actual start of the relay stub installation area in high RAM.
525B
PUSH HL E5
Save Register Pair HL (the relay stub installation address in high RAM, = old High Memory top - 29 bytes) onto the stack. It will be recovered at 5262H after the LDIR has modified HL, and then used at 5264H-5266H to write the stub's installed address back into the first device's DCB pointer field.
525C
EX DE,HL EB
Exchange Register Pairs DE and HL. After the exchange: DE holds the relay stub installation address (the LDIR destination); HL is now free to be loaded with the relay stub template source address.
525D
Point Register Pair HL to address 526AH, the start of the 30-byte relay stub template within this overlay. This is the LDIR source. BC is still 001EH (30) from the earlier load at 5251H - but note that the LDIR at 5260H uses BC, which has now been modified by SBC HL,BC at 5255H (BC was used as the subtrahend). The LDIR byte count must have been preserved or reloaded; the code relies on BC still being valid at this point.
5260
LDIR ED B0
Execute a block copy: copy 30 bytes from the source (HL = 526AH, the relay stub template, already patched with the two device addresses and two fallback addresses) to the destination (DE = the installation address in high RAM, just above the new High Memory top). After this copy, the relay stub is live in high memory, ready to intercept device I/O.
5262
POP DE D1
Restore Register Pair DE (the relay stub installation address in high RAM, saved at 525BH) from the stack. DE now holds the address where the stub was just installed. This address will be written into the first device's DCB pointer field at 5264H-5266H so that VTOS routes I/O through the stub.
5263
POP HL E1
Restore Register Pair HL (the pointer to offset +01H of the first device's DCB entry, saved at 5243H) from the stack. HL points to the low byte of the forward-link pointer field within the first device's DCB, which is about to be overwritten with the relay stub's address.
5264
LD (HL),E 73
Store Register E (the low byte of the relay stub's installed address in high RAM) into the memory location at HL (offset +01H, the low byte of the forward-link pointer in the first device's DCB entry). This writes the first byte of the stub's address into the DCB.
5265
INC HL 23
INCrement Register Pair HL by 1, advancing from offset +01H to offset +02H of the first device's DCB entry (the high byte of the forward-link pointer).
5266
LD (HL),D 72
Store Register D (the high byte of the relay stub's installed address in high RAM) into the memory location at HL (offset +02H, the high byte of the forward-link pointer in the first device's DCB entry). The DCB's forward-link pointer now contains the relay stub's address in high RAM. From this point on, any VTOS I/O operation for the first device will be vectored through the relay stub, which duplicates the I/O to the second (linked) device before forwarding to the original handler.
5267
JUMP to 402DH (DOS READY / No-Error Exit) to return to the VTOS command prompt. The LINK command is complete; the relay stub is installed in high memory and the first device's DCB now routes through it.

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).

526A
JR C,527DH 38 11
Relay Stub - Entry Point (output vs. input dispatch): Test the CARRY FLAG. In the VTOS I/O convention, the CARRY FLAG distinguishes output calls (CARRY set) from input calls (CARRY clear) when the relay stub's address is invoked as a device handler. If the CARRY FLAG is set (output), fall through to the output path; if clear, JUMP to 527DH for the input path.
526C
PUSH AF F5
Save Register Pair AF (containing the output byte in A and the current flags) onto the stack. The output byte must be preserved across the CALL to ROM routine 001BH at 5278H, which will use AF for its own purposes.
526D
PUSH BC C5
Save Register Pair BC onto the stack. BC must be preserved across the subroutine call.
5271
POP BC C1
Restore Register Pair BC from the stack.
5272
PUSH AF F5
Save Register Pair AF (the flags returned by the original device handler call at 526EH, including any error status) onto the stack. These flags will be restored at 527BH and returned to the original caller after the duplicate output to the linked device.
5273
POP HL E1
Load Register Pair HL with the value on top of the stack. After PUSH AF at 5272H, the top of stack contains the AF flags value. POP HL here captures those flags into HL (H = A, L = F) for access by the EX (SP),HL at 5274H.
5274
EX (SP),HL E3
Exchange Register Pair HL with the value at the top of the stack. After the PUSH AF at 526CH (which saved the output byte), EX (SP),HL swaps HL (the flags from 526EH's return) with that saved AF value, restoring the original output byte into HL and placing the flags from the handler call on top of the stack. This is a register shuffle to recover the output byte for re-sending to the second device.
5278
GOSUB to ROM routine at 001BH to output a byte to a device. On entry: A = the output byte (recovered into HL then back into context), DE = the DCB address of the second (linked) device (just loaded from the patched operand at 5276H). This duplicates the byte that was sent to the first device, also sending it to the linked second device.
527B
POP AF F1
Restore Register Pair AF from the stack. This recovers the flags that were saved at 5272H (the return status from the original device handler call at 526EH). These flags are returned to the original VTOS I/O dispatcher as the result of the relay stub call, preserving any error or status information from the primary device handler.
527C
RET C9
RETURN to the VTOS I/O dispatcher (or whatever code invoked the relay stub's address as a device handler). The output byte has been sent to both the first device (via the original handler at 526EH) and the second linked device (via ROM 001BH at 5278H). The flags from the primary device handler are returned.

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.

5280
OR A,A B7
OR Register A with itself. This does not change A's value but updates the flags: if A is zero (no byte received from the second device), the Z FLAG is set; if A is non-zero (a byte was received), Z is clear.
5281
RET NZ C0
If the NZ FLAG (Not Zero) is set - meaning the second (linked) device returned a byte (A != 0) - RETURN to the caller with that byte in A. The input request has been satisfied by the linked device.
5285
JUMP to ROM routine at 0013H (Get a byte from an input device), with DE = the second linked device's DCB address. This is the fallback input path: when the second device did not return a byte (A = 0 after the call at 527DH), the stub jumps to the ROM input routine to perform a full blocking input request from the linked device's DCB. The RET at the end of ROM 0013H will return directly to the original VTOS I/O dispatcher.

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.

5288
LD HL,4015H 21 15 40
Point Register Pair HL to address 4015H, the start of drive configuration table #1. The scan begins with the first 8-byte entry in this table.
528C
LD A,L 7D
Load Register A with the value in Register L (the low byte of the current entry pointer in HL). Used to compute the address of offset +06H within this entry.
528D
ADD A,06H C6 06
ADD 06H to Register A, computing the low byte of the address at offset +06H within the current 8-byte DCB entry. The device-type identifier is stored at offsets +06H and +07H.
528F
LD L,A 6F
Load Register L with the computed value. HL now points to offset +06H (the first byte of the device-type identifier) within the current entry.
5290
LD A,(HL) 7E
Fetch the byte at offset +06H (the first byte of the device-type identifier) into Register A. This is compared against Register E (the first byte of the target identifier in DE).
5291
INC L 2C
INCrement Register L by 1, advancing HL from offset +06H to offset +07H (the second byte of the device-type identifier).
5292
CP A,E BB
Compare Register A (the first byte of the current entry's device-type identifier) against Register E (the first byte of the target identifier). If equal, Z FLAG is set.
5293
If the NZ FLAG (Not Zero) is set - meaning the first byte does not match - JUMP to 529CH to discard the saved HL and advance to the next entry.
5295
LD A,(HL) 7E
Fetch the byte at offset +07H (the second byte of the device-type identifier) into Register A. This is the second byte to compare for a full 2-byte match.
5296
CP A,D BA
Compare Register A (the second byte of the current entry's identifier at +07H) against Register D (the second byte of the target in DE). If equal, Z FLAG is set.
5297
If the NZ FLAG (Not Zero) is set - meaning the second byte does not match - JUMP to 529CH to discard the saved HL and advance to the next entry.
5299
POP HL E1
Restore Register Pair HL (the entry base address saved at 528BH) from the stack. HL points to the start of the matching DCB entry. Both bytes of the device-type identifier matched.
529A
XOR A,A AF
Set Register A to ZERO and clear all flags. XOR A sets the Z FLAG, signaling a successful match to the caller.
529B
RET C9
RETURN to the caller with Z FLAG set (match found) and HL pointing to the matching DCB entry base address.
529D
INC L 2C
INCrement Register L by 1, advancing HL toward the next entry. After the non-match, HL is at offset +07H of the current entry; this increment brings L to +08H, the start of the next 8-byte entry.
529E
If the NZ FLAG (Not Zero) is set - meaning L did not wrap to 00H - JUMP to 52A4H to check for end-of-table.
52A0
LD A,08H 3E 08
Load Register A with 08H. This error code is returned (via OR A,A which sets NZ) when the device is not found in either table. L wrapping to 00H (from FFH) signals that the scanner has gone past the end of the 43xxH page, indicating all of table #2 has been scanned without a match.
52A2
OR A,A B7
OR Register A with itself (A=08H), setting the NZ FLAG to indicate failure without changing A's value. This is the "not found" return.
52A3
RET C9
RETURN to the caller with NZ FLAG set and A=08H, indicating the specified device was not found in either configuration table.
52A5
Compare Register A (the current low byte of HL) against 2DH. Drive configuration table #1 ends at 402CH (low byte 2CH), so a low byte of 2DH signals that the pointer has advanced past the end of table #1. When this occurs, the scan must switch to table #2 at 43C0H.
52A7
If the NZ FLAG (Not Zero) is set - meaning HL is still within the current table - LOOP BACK to 528BH to check the next entry's device-type field.
52A9
LD HL,43C0H 21 C0 43
Point Register Pair HL to address 43C0H, the start of drive configuration table #2. Table #1 has been exhausted; scanning continues from here.
52AC
LOOP BACK to 528BH to scan the entries in table #2 starting from 43C0H.

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.

52AE
OR Register A with 40H (01000000 binary), setting bit 6 of the error code in Register A. Bit 6 indicates a fatal (non-recoverable) error in the VTOS error system.
52B0
JUMP to SYS0 routine at 4409H (DOS Error Exit). Register A contains the error code with the fatal flag set. The VTOS error handler will display the appropriate error message and return to the DOS READY prompt.

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.

52B3
Point Register Pair HL to address 52BCH, which contains the error string "DEVICE SPEC REQUIRED" + 0DH.
52B6
GOSUB to SYS0 routine at 447BH to display the string pointed to by HL ("DEVICE SPEC REQUIRED") with CONFIG/SYS processing.
52B9
JUMP to SYS0 address 4030H (Error-already-displayed exit) to return to the VTOS command prompt.

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).

52BC
DEFM "DEVICE SPEC REQUIRED" + 0DH 44 45 56 49 43 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
ASCII string "DEVICE SPEC REQUIRED" followed by 0DH (carriage return / string terminator). Displayed by CALL 447BH at 52B6H when a required device specification argument is missing or does not begin with '*'.
52D4
GOSUB to SYS0 routine at 447BH to display the string pointed to by HL ("CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL") with CONFIG/SYS processing.
52D7
JUMP to SYS0 address 4030H (Error-already-displayed exit) to return to the VTOS command prompt.

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

AddressPurpose
402DHDOS 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-402CHDrive configuration table #1 (3 entries x 8 bytes). The RESET ALL path copies the default DCB template from 532CH over this table.
4020HCursor position variable. Saved before overwriting the config table, restored afterward so the screen state is preserved.
4049HHigh Memory pointer (2 bytes). Updated by the RESET ALL path to reflect the detected top of physical RAM.
43B8H-43BCHThree 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.
43BEHSaved interrupt vector / chaining context. Cleared to 0000H during RESET ALL to discard any saved overlay context.
43C0H-43D7HDrive configuration table #2 (3 entries x 8 bytes). The RESET ALL path copies the default DCB template from 5344H over this table.
43D8H-43FFHExtended drive state area (40 bytes). Zeroed during RESET ALL by a fill loop at 52DCH.
4500HStart of drive parameter block area. Scanned during RESET ALL to verify that no active task occupies high memory before updating 4049H.
5309HA 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-5343HDefault DCB template for drive configuration table #1 (24 bytes). Copied to 4015H during RESET ALL.
5344H-535BHDefault DCB template for drive configuration table #2 (24 bytes). Copied to 43C0H during RESET ALL.
535CHDevice 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.
53D8HError message string "CAN'T RESET MEMORY" + 0DH. Displayed when the High Memory update cannot be performed because a foreground task is active.
5390HError message string "DEVICE SPEC REQUIRED" + 0DH. Displayed when an invalid or unrecognized device specification is given.
53AEHError 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

AddressPurpose
441CHExtract filespec / device spec from the command line. Returns Z and DE pointing to the parsed token on success.
4467HDisplay message string pointed to by HL on the screen.
447BHDisplay string with CONFIG/SYS processing. Used for error messages.
402DHDOS READY / No-Error Exit. Jumped to at end of successful command.
4030HError-already-displayed exit. Jumped to after displaying an error message.
4409HDOS 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.

5200
PUSH HL E5
Save Register Pair HL onto the stack. HL is preserved across the self-modifying setup sequence and restored at 5213H before argument parsing begins.
5201
Load Register Pair HL with the 2-byte value stored at address 43B8H in the VTOS system work area. 43B8H holds a saved or default target address that will be written into the self-modifying LDIR destination operand at 532DH, so that the first RESET ALL copy operation uses the correct destination.
5204
Self-Modifying Code
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.
5207
LD HL,(43BAH) 2A BA 43
Load Register Pair HL with the 2-byte value stored at address 43BAH in the VTOS system work area. 43BAH holds a second saved target address, to be written into the self-modifying operand at 5335H, configuring the destination for the second LDIR copy operation.
520A
Self-Modifying Code
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.
520D
LD HL,(43BCH) 2A BC 43
Load Register Pair HL with the 2-byte value stored at address 43BCH. 43BCH holds a third saved target address, to be written into the self-modifying operand at 533DH, configuring the destination for the third LDIR copy operation in the RESET ALL path.
5210
Self-Modifying Code
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.
5213
POP HL E1
Restore Register Pair HL from the stack. HL is now returned to whatever value it held on entry to the RESET command (typically the command-line buffer pointer or overlay context pointer passed by the SYS6 dispatcher).

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.

5214
Point Register Pair DE to address 5405H, which is the filespec work area template used by the SYS0 routine at 441CH. This template provides a pre-formatted buffer into which the parsed device specification token will be written.
5217
GOSUB to SYS0 routine at 441CH to extract the first filespec token from the command line buffer. On return: Z flag set = token found, DE points to the start of the parsed token in the work area; NZ = no argument present (end of command line reached).
521A
If the NZ FLAG (Not Zero) is set - meaning no argument was found on the command line - JUMP to 525CH to begin the RESET ALL path, which resets all system devices and re-scans physical memory.
521D
LD A,(DE) 1A
Fetch the first character of the parsed token (pointed to by DE, which now addresses the filespec work area after 441CH has filled it) into Register A. This character must be '*' (ASCII 2AH) for a valid VTOS device specification.
521E
Compare Register A against 2AH (ASCII '*', the VTOS device specification prefix character). If Register A equals 2AH, the Z FLAG is set; otherwise the NZ FLAG is set.
5220
If the NZ FLAG (Not Zero) is set - meaning the argument does not begin with '*' and is therefore not a valid device specification - JUMP to 5387H to display the "DEVICE SPEC REQUIRED" error message.
5223
LD DE,(5406H) ED 5B 06 54
Load Register Pair DE with the 2-byte value stored at address 5406H. After 441CH has parsed the device token into the work area at 5405H, the 2-byte device-type identifier (the two ASCII characters following '*', e.g., "DO" for *DO) has been placed at 5406H-5407H. DE now holds this identifier for use by the device chain scanner at 535CH.
5227
GOSUB to the device chain scanner at 535CH (within this overlay). This routine scans the drive configuration tables at 4015H-402CH and 43C0H-43D7H, searching for a DCB entry whose device-type field (at offset +06H/+07H within each entry) matches the identifier in Register Pair DE. On return: Z = found, HL points to the matching DCB entry; NZ = not found.
522A
If the NZ FLAG (Not Zero) is set - meaning the specified device was not found in either drive configuration table - JUMP to 5382H to report a fatal error (OR A,40H / JP 4409H).

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.

522D
PUSH HL E5
Save Register Pair HL onto the stack. HL currently points to the matching DCB entry found by the scanner at 535CH. It is saved so it can be recovered at 5231H after the subroutine call.
522E
GOSUB to the single-device reset subroutine at 52EAH (within this overlay). This routine examines the DCB entry pointed to by HL: if it is a forwarding (non-terminal) entry (bit 4 of the type byte set), it follows the forward-link chain to the actual device DCB and copies the default driver data from 530CH into it; if the entry is terminal, it copies directly. On return, Z = success.
5231
POP HL E1
Restore Register Pair HL from the stack. HL again points to the matching DCB entry that was found by the scanner.
5232
LD (HL),08H 36 08
Store the value 08H into the memory location addressed by HL (the type byte at offset +00H of the DCB entry). In the VTOS drive configuration table entry format, 08H marks this entry as an active terminal entry (a standard system device DCB). This completes the single-device reset by writing the standard device type marker.
5234
LD A,40H 3E 40
Load Register A with the value 40H. This is the high byte of the address range 4000H-40FFH, which is the VTOS resident DOS page. The comparison at 5236H checks whether the DCB entry being reset is located within that page (i.e., in drive configuration table #1 at 4015H-402CH).
5236
Compare Register A (40H) against Register H (the high byte of the DCB address in HL). If Register A equals Register H, the Z FLAG is set, meaning the DCB is in the 4000H-40FFH page (the resident DOS area, specifically table #1). If NZ, the DCB is in the 4300H-43FFH range (table #2) or elsewhere.
5237
If the NZ FLAG (Not Zero) is set - meaning the reset device's DCB is not in the resident DOS page - JUMP to 402DH (DOS READY / No-Error Exit) to return to the VTOS command prompt immediately. No further action is needed.

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.

5239
DI F3
Disable Interrupts. The following sequence modifies the drive configuration table #1 in place, and interrupts must be suppressed to prevent the ISR from accessing the table in a partially-updated state.
523A
LD D,H 54
Load Register D with the value in Register H (the high byte of the DCB address currently in HL). This copies the DCB address into the DE register pair as part of setting up DE = HL.
523B
LD E,L 5D
Load Register E with the value in Register L (the low byte of the DCB address currently in HL). Register Pair DE now holds the full address of the DCB entry that was just reset, to be used as the LDIR source address.
523C
XOR A,A AF
Set Register A to ZERO and clear all flags. Register A = 0. This clears the carry flag in preparation for the SBC (Subtract with Carry) instruction at 5241H, which performs a 16-bit subtraction.
523D
LD BC,4015H 01 15 40
Load Register Pair BC with the value 4015H, which is the base address of drive configuration table #1 in the VTOS resident DOS area. This will be used to compute the offset of the reset DCB entry within that table.
5241
SBC HL,BC ED 42
Subtract Register Pair BC (4015H, the table base) from Register Pair HL (the DCB address) with borrow, storing the result in HL. The result is the byte offset of the DCB entry from the start of table #1. For entries at 4015H, 401DH, or 4025H this yields 0000H, 0008H, or 0010H respectively.
5243
Load Register Pair BC with the address 532CH, which is the start of the default DCB template for drive configuration table #1 embedded in this overlay. This template contains the factory-default driver entries for all three slots in table #1.
5246
ADD HL,BC 09
ADD Register Pair BC (532CH, the template base address) to Register Pair HL (the offset computed above), storing the result in HL. HL now addresses the specific 8-byte slot within the embedded default template that corresponds to the DCB entry just reset. This is the LDIR source address.
5247
LD BC,0008H 01 08 00
Load Register Pair BC with the value 0008H. This is the byte count for the LDIR block copy: each DCB entry in the drive configuration table is 8 bytes wide.
524A
LDIR ED B0
Execute a block copy: copy 8 bytes from the source address (HL, pointing to the matching slot in the default template at 532CH) to the destination address (DE, pointing to the DCB entry in table #1 that was just reset). HL and DE are incremented and BC is decremented for each byte. This restores the full 8-byte default entry for the just-reset device in table #1.
524C
EI FB
Enable Interrupts. The critical section updating drive configuration table #1 is complete.
524D
LD A,25H 3E 25
Load Register A with the value 25H. This is the low byte of address 4025H, the start of the third (last) entry in drive configuration table #1. The comparison at 524FH checks whether the reset DCB entry was in that third slot.
5250
If the NZ FLAG (Not Zero) is set - meaning the reset device was in the first or second table #1 slot, not the third - JUMP to 402DH (DOS READY / No-Error Exit). No cursor repositioning is needed.
5253
Point Register Pair HL to address 5309H, which holds a 3-byte inline display control record: a 1CH control byte (set cursor position) followed by two bytes giving the cursor row and column. This record will reposition the cursor after the reset of the third table #1 entry modifies the screen.
5256
GOSUB to SYS0 routine at 4467H to display the message (cursor reposition control sequence) pointed to by HL (5309H). This repositions the screen cursor to the saved location after the device reset operation.
5259
JUMP to 402DH (DOS READY / No-Error Exit) to return to the VTOS command prompt. The single-device RESET is complete.

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).

525C
LD HL,4015H 21 15 40
Point Register Pair HL to address 4015H, the start of drive configuration table #1 in the VTOS resident DOS area. HL will be used as the current-entry pointer as the device chain scanner subroutine at 52EAH walks through the table entries to reset each one.
525F
PUSH HL E5
Save Register Pair HL (the current DCB table pointer) onto the stack. It will be restored at 5263H after the reset subroutine returns.
5260
GOSUB to the single-device reset subroutine at 52EAH. With HL pointing to the first entry in drive configuration table #1 (4015H), this resets whichever device is described by that entry. The subroutine follows forward-links if needed and copies default driver data into the terminal DCB.
5263
POP HL E1
Restore Register Pair HL (the DCB table pointer, last set to 4015H or advanced by the loop at 526AH-5271H on subsequent iterations) from the stack.
5264
LD A,L 7D
Load Register A with the value in Register L (the low byte of the current DCB table pointer in HL). This will be used to advance HL to the next 8-byte entry and to detect when the scan has reached the end of the tables.
5265
ADD A,08H C6 08
ADD 08H to Register A, advancing the low byte of the pointer by 8 bytes (one DCB entry). The result in A is the low byte of the address of the next DCB entry in the table.
5267
LD L,A 6F
Load Register L with the value in Register A (the incremented low byte). HL now points to the next 8-byte DCB entry in the current table.
5268
If the Z FLAG (Zero) is set - meaning the addition of 08H produced a result of 00H, indicating the low byte wrapped around (L overflowed) - JUMP to 5273H to begin the RAM top detection phase. A wrap to 00H happens when all three entries in the current table have been processed and the pointer would advance into the next page.
526A
Compare Register A (the advanced low byte) against 2DH. In the context of the two drive configuration tables: table #1 ends at 402CH (low byte 2CH), so a low byte of 2DH signals the pointer has advanced past the end of table #1. When this occurs, the scan must switch to table #2 at 43C0H.
526C
If the NZ FLAG (Not Zero) is set - meaning the pointer has NOT yet advanced past the end of the current table - LOOP BACK to 525FH to reset the next entry in the current table.
526E
LD HL,43C0H 21 C0 43
Point Register Pair HL to address 43C0H, the start of drive configuration table #2. The scan has exhausted table #1 (4015H-402CH); processing now continues with table #2.
5271
LOOP BACK to 525FH to push the new HL (pointing into table #2) and call the reset subroutine for the next entry.

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.

5273
LD B,0CH 06 0C
Load Register B with 0CH (12 decimal). This is the loop count for the drive parameter block scan: 12 entries are checked, each consisting of a 3-byte block at 4500H+. Register B is the DJNZ counter.
5275
LD HL,4500H 21 00 45
Point Register Pair HL to address 4500H, the start of the drive parameter block area scanned to detect foreground tasks. Each block here is 3 bytes: a flag byte followed by a 2-byte address pointer. HL will be incremented through this area during the loop.

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.

5278
LD E,(HL) 5E
Fetch the byte at the current position in the parameter block area (HL) into Register E. This is the low byte of the 2-byte address pointer stored in this 3-byte block.
5279
INC HL 23
INCrement Register Pair HL by 1, advancing to the next byte within this 3-byte block.
527A
LD D,(HL) 56
Fetch the byte at the current position (HL, now advanced by 1) into Register D. This is the high byte of the 2-byte address pointer. Register Pair DE now holds the full 2-byte address from this parameter block entry.
527B
INC HL 23
INCrement Register Pair HL by 1 again, advancing past the 2-byte pointer to the start of the next 3-byte block (the third byte of the current block is skipped by this increment combined with the one at 5279H).
527C
PUSH HL E5
Save Register Pair HL (the updated table pointer, now pointing to the start of the next 3-byte block) onto the stack. It is saved so it can be recovered at 5283H after the comparison with 4049H.
527D
LD HL,(4049H) 2A 49 40
Load Register Pair HL with the 2-byte value stored at address 4049H, which is the current High Memory pointer - the detected top of usable RAM. This value will be compared against the address in Register Pair DE.
5280
XOR A,A AF
Set Register A to ZERO and clear all flags, specifically clearing the carry flag in preparation for the 16-bit SBC comparison at 5281H.
5281
SBC HL,DE ED 52
Subtract Register Pair DE (the address pointer from the parameter block entry) from Register Pair HL (the current High Memory value at 4049H) with borrow, storing the result in HL. If DE >= HL (the task address is at or above High Memory), the CARRY FLAG is set. If DE < HL, no carry is set.
5283
POP HL E1
Restore Register Pair HL (the parameter block table pointer) from the stack. The result flags from the SBC at 5281H are preserved.
5284
If the CARRY FLAG is set - meaning the address from this parameter block entry is >= the current High Memory pointer, indicating an active foreground task at high memory - JUMP to 52A2H to display the "CAN'T RESET MEMORY" error message and skip the High Memory update.
5286
DECrement Register B and if not zero, LOOP BACK to 5278H to check the next 3-byte parameter block entry. If B reaches zero, all 12 entries have been checked and no active task was found at or above High Memory; execution falls through to the RAM probe at 5288H.

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.

5288
LD A,(402DH) 3A 2D 40
Fetch the byte at address 402DH (the first byte of the DOS READY SVC stub) into Register A. In unmodified VTOS, this byte is C3H (a JP instruction). If a user program has replaced this with any other byte, a foreground task is active and the RAM probe must be skipped.
528B
Compare Register A against C3H (the opcode for JP nn). If Register A equals C3H, the Z FLAG is set, indicating the DOS-ready vector has not been patched. If NZ, a foreground task has modified 402DH and the RAM probe must be bypassed.
528D
If the Z FLAG (Zero) is set - meaning 402DH still contains C3H (unmodified JP) - JUMP to 53A5H to display the "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" error message. The RAM probe cannot proceed safely if a program is active.

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.
5290
DI F3
Disable Interrupts. The physical RAM probe writes to and reads from arbitrary memory addresses including 0FFFFH (top of address space), and interrupts must be suppressed to prevent the ISR from interfering during this test.
5291
LD HL,0FFFFH 21 FF FF
Point Register Pair HL to address 0FFFFH, the highest address in the Z80's 64KB address space. The probe will walk downward from here in 4KB steps, testing each page for writable RAM.

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.

5294
LD A,(HL) 7E
Fetch the byte currently stored at the address in HL (starting at 0FFFFH, then descending in 4KB steps) into Register A. This is the original value that must be restored after the write test.
5295
LD B,A 47
Load Register B with the value in Register A (the original byte read from the test address). B preserves this original value so it can be written back at 5299H.
5296
CPL 2F
Complement (bitwise invert) Register A. The inverted value is what will be written to the test address; if the memory is writable, reading it back should return this inverted value.
5297
LD (HL),A 77
Store Register A (the inverted value) into the memory location addressed by HL. This is the write phase of the RAM test.
5298
CP A,(HL) BE
Compare Register A (the inverted value just written) against the byte now read from the same memory location (HL). If the memory is writable RAM, the read will return the inverted value and the Z FLAG will be set. If this is ROM or non-existent memory, the read will return the original (or bus-float) value and NZ will be set.
5299
LD (HL),B 70
Store Register B (the original byte preserved at 5295H) back into the memory location at HL, restoring whatever was there before the test. This ensures the probe is non-destructive.
529A
If the Z FLAG (Zero) is set - meaning the write test succeeded and RAM is present at this address - JUMP to 52B1H to store this address as the new High Memory top. The search is complete.
529C
LD A,H 7C
Load Register A with the value in Register H (the high byte of the current test address). This will be decremented by 04H at 529EH to move down 4KB (to the next 256-byte page boundary 4 pages lower, effectively moving 1KB at a time across 4 pages).
529D
SUB A,04H D6 04
SUBtract 04H from Register A (the high byte of the test address), moving down 4 x 256 = 1024 bytes (1KB) in the address space. The probe steps downward in 1KB increments, checking the top byte of each 1KB region for writable RAM.
529F
LD H,A 67
Load Register H with the value in Register A (the decremented high byte). HL now points 1KB lower than the previous test address. L remains unchanged (FFH), so the test address is always xFFH within each KB tested.
52A0
LOOP BACK to 5294H to test the next lower address for writable RAM.

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.

52A2
Point Register Pair HL to address 53D8H, which contains the error string "CAN'T RESET MEMORY" + 0DH. This string will be passed to the display routine at 447BH.
52A5
GOSUB to SYS0 routine at 447BH to display the string pointed to by HL ("CAN'T RESET MEMORY") on the screen with CONFIG/SYS processing.
52A8
LD A,03H 3E 03
Load Register A with the value 03H. This is the string terminator byte (ETX character) that will be written into the message control block at 5309H to suppress the normal READY prompt repositioning message that would otherwise be displayed at the end of RESET ALL.
52AA
Self-Modifying Code
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.
52AD
DI F3
Disable Interrupts. The RESET ALL table restoration that follows modifies the drive configuration tables in the resident DOS area; interrupts must be disabled during this critical section.

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.

52AE
LD (4049H),HL 22 49 40
Store Register Pair HL (the detected top-of-RAM address found by the probe loop at 5294H-52A0H, or the unchanged value on the error path) into address 4049H, the High Memory pointer. This updates the system's knowledge of usable RAM top.
52B1
LD (4049H),HL 22 49 40
Store Register Pair HL (the detected RAM top address from the probe, valid on the success path where JR Z,52B1H branched here) into 4049H. This is the primary update of the High Memory pointer on the success path.
52B4
LD HL,(4020H) 2A 20 40
Load Register Pair HL with the 2-byte value stored at address 4020H, the cursor position variable in the VTOS resident DOS. This cursor position is saved so it can be restored at 52C4H after the drive configuration tables are overwritten (since table #1 shares memory with the cursor variable area).
52B7
PUSH HL E5
Save Register Pair HL (the cursor position value from 4020H) onto the stack.
52B8
Point Register Pair HL to address 532CH, the start of the default DCB template for drive configuration table #1 (24 bytes). This is the LDIR source for restoring table #1 to factory defaults.
52BB
LD DE,4015H 11 15 40
Point Register Pair DE to address 4015H, the start of drive configuration table #1 in the VTOS resident DOS. This is the LDIR destination. (Note: the operand at 52BCH-52BDH is the self-modifying target that was patched at 5204H; at runtime DE = the value from 43B8H, which is 4015H for normal operation.)
52BE
LD BC,0018H 01 18 00
Load Register Pair BC with the value 0018H (24 decimal). This is the byte count for the LDIR block copy: drive configuration table #1 is 24 bytes (3 entries x 8 bytes each).
52C1
LDIR ED B0
Execute a block copy: copy 24 bytes from the source (HL = 532CH, the default template for table #1) to the destination (DE = 4015H, the live table #1). This restores drive configuration table #1 to its factory-default state.
52C3
POP HL E1
Restore Register Pair HL (the cursor position value saved at 52B7H) from the stack.
52C4
LD (4020H),HL 22 20 40
Store Register Pair HL (the saved cursor position) back into address 4020H, restoring the cursor position variable that was overwritten when the default template was copied into the table #1 area (which includes address 4020H within it).
52C7
LD HL,0000H 21 00 00
Load Register Pair HL with the value 0000H. This value is about to be stored at 43BEH to clear the saved overlay context (the chaining/interrupt vector save area), discarding any saved overlay state as part of the global reset.
52CA
LD (43BEH),HL 22 BE 43
Store Register Pair HL (0000H) into address 43BEH, clearing the saved interrupt vector / chaining context area. This ensures that after RESET ALL, no stale overlay context remains that could cause incorrect behavior on the next overlay dispatch.
52CD
Point Register Pair HL to address 5344H, the start of the default DCB template for drive configuration table #2 (24 bytes). This is the LDIR source for restoring table #2.
52D0
LD DE,43C0H 11 C0 43
Point Register Pair DE to address 43C0H, the start of drive configuration table #2 in the VTOS resident DOS. This is the LDIR destination. (This operand was patched by 520AH at entry with the value from 43BAH; for normal operation this is 43C0H.)
52D3
LD BC,0018H 01 18 00
Load Register Pair BC with 0018H (24 decimal), the byte count for restoring drive configuration table #2 (3 entries x 8 bytes).
52D6
LDIR ED B0
Execute a block copy: copy 24 bytes from the source (HL = 5344H, the default template for table #2) to the destination (DE = 43C0H, the live table #2). Drive configuration table #2 is now restored to factory defaults.
52D8
LD HL,43D8H 21 D8 43
Point Register Pair HL to address 43D8H, the start of the extended drive state area that lies immediately after drive configuration table #2 (which ends at 43D7H). This 40-byte area (43D8H-43FFH) holds extended drive state data that must also be cleared during RESET ALL.
52DB
XOR A,A AF
Set Register A to ZERO and clear all flags. Register A = 00H will be the fill value written into the extended drive state area.

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.

52DC
LD (HL),A 77
Store Register A (00H) into the memory location addressed by HL (currently within 43D8H-43FFH), zeroing that byte of the extended drive state area.
52DD
INC L 2C
INCrement Register L by 1, advancing HL to the next byte in the extended drive state area. Only L is incremented (H remains 43H), so the loop stays within the 43xxH page.
52DE
If the NZ FLAG (Not Zero) is set - meaning L has not yet wrapped around to 00H (i.e., has not passed 43FFH) - LOOP BACK to 52DCH to zero the next byte. When L wraps from FFH to 00H (Z set), the entire 43D8H-43FFH range has been zeroed.

Loop End
The extended drive state area at 43D8H-43FFH has been completely zeroed.

52E0
EI FB
Enable Interrupts. The critical section is complete; drive configuration tables and the extended drive state area have been restored.
52E1
Point Register Pair HL to address 5309H, the READY prompt cursor repositioning message (or the suppressed 03H terminator if 52AAH wrote 03H there on the error path). This is passed to 4467H to redisplay the VTOS READY prompt at the correct cursor position.
52E4
GOSUB to SYS0 routine at 4467H to display the message pointed to by HL. On the success path this repositions the cursor for the VTOS READY prompt; on the "CAN'T RESET MEMORY" error path this call displays an empty message (03H terminator) and does nothing.
52E7
JUMP to 402DH (DOS READY / No-Error Exit) to return to the VTOS command prompt. The RESET ALL command is complete.

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.

52EA
Test bit 4 of the byte at the memory location addressed by HL (the type byte at offset +00H of the current drive configuration table entry). Bit 4 = 1 indicates a forwarding (non-terminal) entry whose actual driver data is reached via a forward link. Bit 4 = 0 indicates a terminal entry that holds the driver data directly. The Z FLAG is set if bit 4 is zero (terminal entry).
52EC
If the Z FLAG (Zero) is set - meaning bit 4 of the type byte is clear, so this is a terminal (non-forwarding) entry - JUMP to 52F5H to check bit 7 (the "valid driver present" flag) and proceed with resetting it.

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.

52EE
INC HL 23
INCrement Register Pair HL by 1, advancing from the type byte (offset +00H) to the low byte of the forward-link address (offset +01H) within the current DCB entry.
52EF
LD A,(HL) 7E
Fetch the byte at HL (the low byte of the forward-link target address stored at offset +01H of this forwarding entry) into Register A.
52F0
INC HL 23
INCrement Register Pair HL by 1, advancing to the high byte of the forward-link address (offset +02H within the current entry).
52F1
LD H,(HL) 66
Load Register H with the byte at HL (the high byte of the forward-link target address at offset +02H). After this, A holds the low byte and H holds the high byte of the forwarding target address.
52F2
LD L,A 6F
Load Register L with the value in Register A (the low byte of the forward-link target address). Register Pair HL now holds the full address of the next entry in the chain: the target DCB that this forwarding entry points to.
52F3
LOOP BACK to 52EAH to test the type byte of the newly-loaded entry. This follow-the-chain loop continues until a terminal entry (bit 4 clear) is found.
52F5
BIT 7,(HL) CB 7E
Test bit 7 of the type byte at the memory location addressed by HL (a terminal DCB entry). Bit 7 = 1 means this is a user-installed (non-system-default) device driver that occupies memory above the base DOS area. Bit 7 = 0 means this is a standard system DCB. The Z FLAG is set if bit 7 is zero.
52F7
RET Z C8
If the Z FLAG (Zero) is set - meaning bit 7 is clear, indicating this is already a standard system device requiring no driver data copy - RETURN to the caller. The entry is already in the correct default state; no copy is needed.
52F8
LD DE,530CH 11 0C 53
Point Register Pair DE to address 530CH, the start of the default driver data block (32 bytes) embedded in this overlay. This is the LDIR source for the reset copy.
52FB
PUSH DE D5
Save Register Pair DE (the address 530CH, the default driver data source) onto the stack. It will be recovered at 5301H after LDIR has modified DE.
52FC
LD BC,0020H 01 20 00
Load Register Pair BC with the value 0020H (32 decimal). This is the byte count for the LDIR block copy: the default driver data block at 530CH is 32 bytes.
52FF
LDIR ED B0
Execute a block copy: copy 32 bytes from the source (DE = 530CH, the default driver data) to the destination (HL, the terminal DCB entry that needs resetting). HL, DE, and BC are updated as each byte is transferred. After completion, the terminal DCB contains the factory-default driver data.
5301
POP DE D1
Restore Register Pair DE (530CH, the default driver data source address) from the stack. DE is restored for use by the caller.
5302
GOSUB to SYS0 routine at 4428H (Close File). With DE pointing to the just-reset DCB (now containing the default driver data including a file handle area), this call closes any open file associated with the device, flushing buffers and updating the directory as needed. On return, Z = success, NZ = error.
5305
If the NZ FLAG (Not Zero) is set - meaning the Close File call returned an error - JUMP to 5382H to report a fatal error (OR A,40H / JP 4409H).
5308
RET C9
RETURN to the caller. The Z FLAG is set (from the successful CALL 4428H), indicating the single-device reset completed successfully.

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.

5309
DEFB 1CH,1FH,03H 1C 1F 03
Three-byte display control record. Byte 1CH = cursor-position control code recognized by 4467H. Byte 1FH = cursor row parameter. Byte 03H = string terminator (ETX), ending the message sequence. At runtime, the first byte may be overwritten with 03H by the error path at 52AAH to produce an empty (no-op) message.

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.

530C-532BH
DEFB (32 bytes) various
Default driver data block. 32 bytes of factory-default driver initialization data for a system device DCB. Copied into any terminal DCB entry during a RESET operation via LDIR at 52FFH. The specific byte values encode the default I/O handler vectors, flags, and parameters for the system-default device driver.

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.

532C-5343H
DEFB (24 bytes) 01 00 00 00 00 00 4B 49 07 00 00 00 00 00 53 49 10 1D 40 00 00 00 4C 4F
Default drive configuration table #1 template (24 bytes = 3 entries x 8 bytes).
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.

5344H-535BH
DEFB (24 bytes) 08 00 00 00 00 00 4A 4C 10 15 40 00 00 00 53 49 10 1D 40 00 00 00 4C 4F
Default drive configuration table #2 template (24 bytes = 3 entries x 8 bytes).
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.

535C
LD HL,4015H 21 15 40
Point Register Pair HL to address 4015H, the start of drive configuration table #1. The scan begins with the first entry in this table.
535F
PUSH HL E5
Save Register Pair HL (the current entry pointer) onto the stack. It will be recovered at 536DH if the device type at this entry matches, or discarded at 5370H if it does not match.
5360
LD A,L 7D
Load Register A with the value in Register L (the low byte of the current entry pointer in HL). This is used at 5363H to compute the offset of the device-type field within the entry.
5361
ADD A,06H C6 06
ADD 06H to Register A, computing the address of offset +06H within the current 8-byte entry. Each DCB entry stores a 2-byte device-type ASCII identifier at bytes +06H and +07H.
5363
LD L,A 6F
Load Register L with the computed offset value. HL now points to offset +06H (the first byte of the device-type identifier) within the current entry.
5364
LD A,(HL) 7E
Fetch the byte at offset +06H (the low byte of the device-type identifier) from the current entry into Register A. This byte will be compared against Register E (the low byte of the target device-type identifier in DE).
5365
INC L 2C
INCrement Register L by 1, advancing HL from offset +06H to offset +07H (the second byte of the device-type identifier).
5366
CP A,E BB
Compare Register A (the first byte of the current entry's device-type identifier) against Register E (the first byte of the target identifier from DE). If they are equal, the Z FLAG is set and the scan continues to compare the second byte at 5369H.
5367
If the NZ FLAG (Not Zero) is set - meaning the first byte of the device-type identifier does not match - JUMP to 5370H to discard the saved HL and advance to the next entry.
5369
LD A,(HL) 7E
Fetch the byte at offset +07H (the second byte of the device-type identifier) from the current entry into Register A. This is the second byte to compare for a full 2-byte match.
536A
CP A,D BA
Compare Register A (the second byte of the current entry's device-type identifier at offset +07H) against Register D (the second byte of the target identifier). If equal, Z is set, indicating a full match.
536B
If the NZ FLAG (Not Zero) is set - meaning the second byte does not match - JUMP to 5370H to discard the saved HL and advance to the next entry.
536D
POP HL E1
Restore Register Pair HL (the entry base address saved at 535FH) from the stack. HL now points to the start of the matching DCB entry. Both type bytes matched; this is the device being searched for.
536E
XOR A,A AF
Set Register A to ZERO and clear all flags. Setting A=0 and clearing the Z FLAG path ensures the Z FLAG is set by XOR A itself, signaling a successful match to the caller.
536F
RET C9
RETURN to the caller with Z FLAG set (match found) and HL pointing to the matching DCB entry. The device was located in the drive configuration tables.
5370
POP AF F1
Discard the saved HL (the entry base address pushed at 535FH) from the stack by popping it into AF (which we do not need). This cleans the stack after a non-matching entry.
5371
INC L 2C
INCrement Register L by 1. After the non-match, HL is at offset +07H of the current entry. Adding 1 would advance to +08H, but because L was at +07H at this point, INC L brings it to the start of the next 8-byte entry only if the arithmetic works out. The next instruction tests L for wrap or end-of-table.
5372
If the NZ FLAG (Not Zero) is set - meaning L did not wrap around to 00H - JUMP to 5378H to check whether the end of the current table has been reached.
5374
LD A,08H 3E 08
Load Register A with the value 08H. This is the error code that will be returned (via OR A,A at 5376H which sets NZ) to signal that the device was not found in either table.
5376
OR A,A B7
OR Register A with itself. This does not change A's value (08H) but sets the flags: since A is non-zero, the NZ FLAG is set. This is the NZ return from the scanner indicating failure.
5377
RET C9
RETURN to the caller with the NZ FLAG set and A=08H, indicating that the specified device was not found in either drive configuration table.
5378
LD A,L 7D
Load Register A with the value in Register L (the current low byte of HL, pointing into the drive configuration tables). This is used to detect whether the scan has advanced past the end of the current table.
5379
Compare Register A (the low byte of the current pointer) against 2DH. As with the single-device scanner at 526AH, 2DH signals that the pointer has advanced past the end of table #1 (which ends at 402CH). When this occurs, the scan switches to table #2.
537B
If the NZ FLAG (Not Zero) is set - meaning the pointer is still within the current table - LOOP BACK to 535FH to compare the next entry's device-type field.
537D
LD HL,43C0H 21 C0 43
Point Register Pair HL to address 43C0H, the start of drive configuration table #2. The first table has been exhausted; scanning continues into table #2.
5380
LOOP BACK to 535FH to scan the entries in table #2 starting from 43C0H.

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.

5382
OR Register A with 40H (01000000 binary), setting bit 6 of the error code in Register A. In the VTOS error system, bit 6 indicates a fatal (non-recoverable) error condition.
5384
JUMP to SYS0 routine at 4409H (DOS Error Exit). Register A contains the error code with the fatal flag set. The VTOS error handler will display the appropriate error message from the SYS4 error dictionary and return to the DOS READY prompt.

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.

5387
Point Register Pair HL to address 5390H, which contains the error string "DEVICE SPEC REQUIRED" + 0DH.
538A
GOSUB to SYS0 routine at 447BH to display the string pointed to by HL ("DEVICE SPEC REQUIRED") with CONFIG/SYS processing.
538D
JUMP to SYS0 address 4030H (Error-already-displayed exit) to return to the VTOS command prompt. The error message has already been shown.

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).

5390
DEFM "DEVICE SPEC REQUIRED" + 0DH 44 45 56 49 43 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
ASCII string "DEVICE SPEC REQUIRED" followed by 0DH (carriage return / string terminator). Displayed by CALL 447BH at 538AH when an invalid argument is given to the RESET command.
53A5
LD HL,53AEH 21 AE 53
Point Register Pair HL to address 53AEH, which contains the error string "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" + 0DH. This error is displayed when the RESET command is invoked from a context where a foreground program has modified the DOS-ready vector at 402DH.
53A8
GOSUB to SYS0 routine at 447BH to display the string pointed to by HL ("CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL") with CONFIG/SYS processing.
53AB
JUMP to SYS0 address 4030H (Error-already-displayed exit) to return to the VTOS command prompt.
53AE
DEFM "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" + 0DH 43 41 4E 27 54 20 2D 2D 20 4F 4E 4C 59 20 56 41 4C 49 44 20 41 54 20 56 54 4F 53 20 43 4F 4D 4D 41 4E 44 20 4C 45 56 45 4C 0D
ASCII string "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" followed by 0DH (carriage return / string terminator). Displayed when the RESET command is invoked while a foreground task is active (402DH != C3H).
53D8
DEFM "CAN'T RESET MEMORY" + 0DH 43 41 4E 27 54 20 52 45 53 45 54 20 4D 45 4D 4F 52 59 0D
ASCII string "CAN'T RESET MEMORY" followed by 0DH (carriage return / string terminator). Displayed by CALL 447BH at 52A5H when RESET ALL cannot update the High Memory pointer because an active foreground task is resident in high memory.

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.

53EC-5404H
DEFB (25 bytes) various
Padding and data area between the error strings and the filespec work area template. Contains zero bytes and setup data for the 441CH filespec extraction call.
5405
DEFB (filespec template) various
Filespec work area template passed to SYS0 routine 441CH at 5217H. After 441CH executes, the parsed device specification token is stored here. The 2-byte device-type identifier (the ASCII characters following '*') is at offset +01H/+02H from this address, i.e., at 5406H-5407H, which is loaded into DE at 5223H for the device chain scanner call.

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 RangePurpose
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

AddressName and Description
5200HROUTE 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.
530EHDCB 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.
53C9HDevice 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 (*).

5200
Point Register Pair DE to 5389H, the first device specifier work area. This is the destination buffer that the filespec extractor at 441CH will fill with the first argument token from the command line.
5203
GOSUB to SYS0 routine at 441CH to extract the first filespec/devspec token from the command line into the buffer at DE (5389H). On return, DE points past the last character written. NZ flag is set if no valid token was found.
5206
If the NZ FLAG (Not Zero) was set by 441CH, no valid device specifier was found on the command line. JUMP to 5352H to display the "DEVICE SPEC REQUIRED" error message and exit.
5209
LD A,(DE) 1A
Fetch the first character of the parsed device specifier from the buffer at DE (5389H) into Register A. The extractor leaves DE pointing to the start of the token it just wrote.
520A
CP A,2AH FE 2A
Compare Register A against 2AH (ASCII: *). VTOS device specifiers use an asterisk as the wildcard selector meaning "all logical devices" or "the named device class". The ROUTE command requires this form. If Register A equals 2AH, the Z FLAG is set; otherwise the NZ FLAG is set.
520C
If the NZ FLAG (Not Zero) has been set because the first argument does not begin with *, the specifier is not in the required form. JUMP to 5352H to display "DEVICE SPEC REQUIRED" and exit.

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.

520F
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H. This zero value is about to be stored to 5220H to initialise the self-modifying operand slot that will later hold the parsed second-argument device address.
5212
LD (5220H),DE ED 53 20 52
Self-Modifying Code
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.
5216
Point Register Pair DE to 5305H, the parameter block for the second argument. This area contains the second device specifier or filespec that the user typed after the first argument.
5219
GOSUB to SYS0 routine at 4476H (open file utility) to parse and open the second argument pointed to by DE (5305H). On return, BC holds the result address of the opened device or file (0000H if the argument was absent or NIL), and the NZ flag is set on a parameter error.
521C
If the NZ FLAG (Not Zero) has been set by the open call, the second argument contained a parameter error. JUMP to 5370H to display the "PARAMETER ERROR" message and exit.
521F
LD BC,0000H 01 00 00
Self-Modifying Code
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.

5222
LD A,B 78
Load Register A with the high byte of BC (Register B). BC holds the second-argument result address from 4476H. Combining A with C will tell whether BC is zero.
5223
OR A,C B1
Bitwise OR Register A (high byte of BC) with Register C (low byte of BC). If BC is 0000H, the result is 00H and the Z FLAG is set. If BC is any non-zero device address, the result is non-zero and the NZ FLAG is set.
5224
If the NZ FLAG (Not Zero) has been set because BC is non-zero (a real device address was returned), JUMP to 52D6H to handle device-to-existing-device routing. BC contains the target device's DCB address.

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".

5227
Point Register Pair DE to 53A9H, the second device specifier work area. This buffer will receive the second argument token extracted from the command line.
522A
GOSUB to SYS0 routine at 441CH to extract the second filespec/devspec token from the command line into the buffer at DE (53A9H). On return NZ means no valid token was found.
522D
If the NZ FLAG (Not Zero) has been set because no second argument exists, JUMP to 5352H to display "DEVICE SPEC REQUIRED" and exit.
5230
LD A,(DE) 1A
Fetch the first character of the second parsed token from the buffer at DE (53A9H) into Register A, to test whether it is a wildcard or the literal string "NIL".
5231
CP A,2AH FE 2A
Compare Register A against 2AH (ASCII: *). If the second argument begins with an asterisk it selects a device-to-device routing where the destination is a second device class. If Register A equals 2AH, the Z FLAG is set; otherwise the NZ FLAG is set.
5233
If the NZ FLAG (Not Zero) has been set because the second argument does not begin with *, the destination is not a wildcard device class. JUMP to 526EH to handle the case where the destination is specified by name (may be "NIL" or a named device).

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.

5235
Load Register Pair DE with the 2-byte device identifier stored at 53AAH (the byte immediately after the * at 53A9H). This is the second device specifier's type code, used as the lookup key for the destination device in the configuration tables.
5239
GOSUB to the device table scanner at 53C9H to search both configuration tables (4015H and 43C0H) for an entry whose 2-byte identifier at offset +06H/+07H matches DE. On return: Z flag set and HL points to the matched entry if found; NZ flag set with A=08H if not found.
523C
If the NZ FLAG (Not Zero) has been set because the destination device was not found in either configuration table, JUMP to 534DH to set the error flag (OR A,40H) and exit via 4409H.
523F
PUSH HL E5
Save Register Pair HL (pointing to the matched destination device entry in the configuration table) onto the stack. It will be needed after the source device lookup that follows.
5240
Load Register Pair DE with the 2-byte device identifier stored at 538AH (the byte after the * at 5389H, which is the first argument). This is the source device specifier's type code, used to look up the source device in the configuration tables.
5244
GOSUB to the device table scanner at 53C9H again, this time to find the source device whose identifier is in DE (loaded from 538AH). HL will point to the matched source entry on return.
5247
If the Z FLAG (Zero) has been set because the source device was found in the tables, JUMP to 5256H to proceed with the DCB patch. If not found, fall through to allocate a new block.

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.

5249
PUSH DE D5
Save Register Pair DE (the source device identifier that was not matched) onto the stack, preserving it while the allocation logic runs.
524A
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H. This zero value is used as the lookup key for the device table scanner call that follows, to find the first free (null) slot in the tables.
524D
GOSUB to the device table scanner at 53C9H with DE=0000H to locate a free slot (null identifier). On return Z means a free slot was found at HL; NZ means the tables are full.
5250
LD A,21H 3E 21
Load Register A with 21H, a pre-loaded error code value that will be passed to the error exit at 534DH if the table-full condition is detected next.
5252
If the NZ FLAG (Not Zero) has been set because no free slot was found (tables are full), JUMP to 534DH to exit with the error code already in A (21H).
5255
POP DE D1
Restore Register Pair DE (the source device identifier saved at 5249H) from the stack, now that a free table slot has been found at HL.

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.

5256
PUSH HL E5
Save Register Pair HL (pointing to the source device entry) onto the stack for later use. The DCB chain walker at 530EH will use the value already on the stack from 523FH (the destination entry).
5257
GOSUB to the DCB chain walker at 530EH. This routine follows the forward-link chain (bit 4 of control byte) in the source device's DCB until it reaches the tail, allocating a new 32-byte block if needed and registering it. On return HL points to the tail DCB entry where the patch will be applied.
525A
POP HL E1
Restore Register Pair HL from the stack. This retrieves the source device entry pointer that was saved at 5256H. HL now points to the source DCB entry that will be written.
525B
DI F3
Disable maskable interrupts. The following sequence patches multiple bytes of the DCB atomically. DI prevents the ISR from observing a partially modified DCB during the update.
525C
LD (HL),10H 36 10
Store 10H into the byte at (HL), which is offset +00H of the source DCB entry. The value 10H sets bit 4 of the control byte, marking this as a forward-link entry meaning the DCB chains forward to the address stored in the next two bytes.
525E
INC L 2C
INCrement Register L by 1, advancing HL to offset +01H within the same DCB entry. Only L is incremented (not the full 16-bit HL) because entries are 8 bytes and table alignment ensures H does not need to change.
525F
POP BC C1
Restore Register Pair BC from the stack. This retrieves the destination device entry pointer that was saved at 523FH. BC now holds the address of the destination DCB that the source will forward to.
5260
LD (HL),C 71
Store Register C (the low byte of the destination DCB address from BC) into the byte at (HL), which is offset +01H of the source entry. This is the low byte of the 16-bit forward-link next-pointer.
5261
INC L 2C
INCrement Register L by 1, advancing HL to offset +02H within the source DCB entry.
5262
LD (HL),B 70
Store Register B (the high byte of the destination DCB address from BC) into the byte at (HL), offset +02H of the source entry. The full 16-bit destination address is now written at offsets +01H and +02H.
5263
LD A,L 7D
Load Register A with the current value of Register L. L points to offset +02H within the DCB entry. Adding 4 to this will reach offset +06H, where the 2-byte device identifier lives.
5264
ADD A,04H C6 04
ADD 04H to Register A, advancing the offset from +02H to +06H within the DCB entry. Offset +06H/+07H is where the 2-byte device type identifier is stored.
5266
LD L,A 6F
Load Register L with the value in Register A (the computed offset +06H). HL now points to the 2-byte identifier field of the source DCB entry.
5267
LD (HL),E 73
Store Register E (the low byte of the source device identifier, loaded from 538AH at 5240H) into the byte at (HL), which is offset +06H of the source DCB entry. This writes the source device's type code into the new/patched table slot.
5268
INC L 2C
INCrement Register L by 1, advancing HL to offset +07H of the source DCB entry.
5269
LD (HL),D 72
Store Register D (the high byte of the source device identifier) into the byte at (HL), offset +07H. The full 2-byte device type identifier is now stored in the table slot, completing the DCB patch.
526A
EI FB
Re-enable maskable interrupts. The DCB patch is complete and the ISR may safely examine the updated entry.
526B
JUMP to 402DH (DOS READY / No-Error Exit) to return to the VTOS command level with a success status.

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.

526E
LD A,(402DH) 3A 2D 40
Fetch the byte at 402DH (the DOS READY / No-Error Exit vector) into Register A. If a route table has previously been installed, the word at 402DH will have been patched to a JP instruction (C3H). The original un-patched value is not a JP.
5271
CP A,0C3H FE C3
Compare Register A against 0C3H (the Z80 JP opcode). If Register A equals 0C3H, the Z FLAG is set, meaning a route table is already installed. If not equal, the NZ FLAG is set.
5273
If the Z FLAG (Zero) has been set because 402DH holds C3H (JP), a route table already exists. JUMP to 52A3H to display the "CAN'T - ONLY VALID AT VTOS COMMAND LEVEL" message, since a route table already installed means routing cannot be re-initialised at this point.

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.

5276
LD HL,(4049H) 2A 49 40
Load Register Pair HL with the 16-bit value stored at 4049H (the detected RAM top address). This is the current high-memory boundary from which overlay route blocks are allocated downward.
5279
INC HL 23
INCrement Register Pair HL by 1. This adjusts the RAM top pointer upward by one before subtraction, aligning the allocation base.
527A
DEC H 25
DECrement Register H by 1. Together with the preceding INC HL, this pair rounds HL down to the nearest 256-byte page boundary, ensuring the new route block starts on an aligned address.
527B
PUSH HL E5
Save Register Pair HL (the aligned allocation base) onto the stack for later use when storing the new pointer back to 4049H.
527C
LD B,00H 06 00
Load Register B with 00H. B is used as the mode/type parameter for the file-open / block-allocate call that follows at 4420H.
527E
GOSUB to SYS0 routine at 4420H (read record from file, B=mode, DE=FCB, HL=buffer). In this context, with B=00H and HL pointing to the new block, it validates and sets up the allocation. On return NZ means an error occurred.
5281
If the NZ FLAG (Not Zero) has been set because the allocation call failed, JUMP to 534DH to exit with the error flag set.
5284
GOSUB to SYS0 routine at 4448H. This validates the newly allocated block, returning the block type in A and setting Z if the block is of an acceptable type. A value of 1CH in A indicates an invalid or incompatible block type.
5287
If the Z FLAG (Zero) has been set because the block type is valid, JUMP to 528EH to continue with the route installation. Otherwise fall through to check for the 1CH invalid-type code.
5289
CP A,1CH FE 1C
Compare Register A against 1CH, the code for an invalid or incompatible block type. If Register A equals 1CH, the Z FLAG is set indicating the block cannot be used for routing.
528B
If the NZ FLAG (Not Zero) has been set because A is neither the valid type nor 1CH, JUMP to 534DH to exit with the error flag. (If A was 1CH the Z flag is now set; the fall-through to 528EH handles that case.)
528E
POP HL E1
Restore Register Pair HL (the aligned allocation base saved at 527BH) from the stack. HL now holds the address of the new 32-byte route block in high memory.
528F
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H (32 decimal). This is the size in bytes of a route table entry. BC will be used as the byte count for LDIR and also subtracted from HL to compute the new TPA top.
5292
XOR A,A AF
Set Register A to ZERO and clear all flags. This clears the carry flag in preparation for the 16-bit subtraction that follows.
5293
SBC HL,BC ED 42
Subtract Register Pair BC (0020H = 32) from Register Pair HL (the current TPA top) with carry (carry is 0). HL now points 32 bytes below the old top, which is the start of the newly reserved route block.
5295
PUSH HL E5
Save Register Pair HL (the new route block base address) onto the stack. It will be used as the LDIR destination and later stored back to 4049H.
5296
DEC HL 2B
DECrement Register Pair HL by 1. This computes the value to store back to 4049H: the new TPA top is one byte below the start of the allocated block.
5297
LD (4049H),HL 22 49 40
Self-Modifying Code
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.
529A
POP DE D1
Restore Register Pair DE from the stack. This retrieves the new route block base address saved at 5295H. DE is now the LDIR destination for copying the second-argument specifier data.
529B
PUSH DE D5
Save Register Pair DE (the route block base address) back onto the stack again. It will be needed after the LDIR copy to know where the new block starts for the DCB patch.
529C
LD HL,53A9H 21 A9 53
Point Register Pair HL to 53A9H, the second device specifier work area. This is the LDIR source - the 32 bytes of specifier data parsed from the command line that describe the routing destination.
529F
LDIR ED B0
Block move: copy BC (0020H = 32) bytes from (HL) at 53A9H (the second device specifier) to (DE) (the new route block in high memory), incrementing both HL and DE after each byte. This installs the routing destination data into the newly reserved block.
52A1
LOOP BACK to 5240H to load DE with the source device identifier and proceed with the device table lookup and DCB patch using the newly allocated block. The route block address is still on the stack from the PUSH at 529BH.

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.

52A3
LD HL,52ACH 21 AC 52
Point Register Pair HL to 52ACH, the start of the embedded error message string "CAN'T -- ONLY VALID AT VTOS COMMAND LEVEL" followed by a carriage return (0DH). This string is stored inline within the code area.
52A6
GOSUB to SYS0 routine at 447BH (display message with CONFIG/SYS processing) to print the string pointed to by HL (52ACH) on the screen.
52A9
JUMP to 4030H (Error-already-displayed exit) to return to the VTOS command level. The error message has already been printed so no further error processing is needed.

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.

52AC-52D5
DEFM "CAN'T --ONLY VALID VTOS COMMAND LEVEL" + 0DH 43 41 4E 27 54 20 2D 2D 20 4F 4E 4C 59 20 56 41 4C 49 44 20 56 54 4F 53 20 43 4F 4D 4D 41 4E 44 20 4C 45 56 45 4C 0D
Error message string printed by 52A6H via SYS0 routine 447BH when ROUTE is attempted after a route table is already active. The 0DH byte at the end is a carriage return that terminates the display and moves the cursor to the next line.

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.

52D6
Load Register Pair DE with the 2-byte device identifier stored at 538AH (the source device type code from the first argument's work area at 5389H+01H). This is the key used to search the device configuration tables.
52DA
GOSUB to the device table scanner at 53C9H to find the source device entry whose 2-byte identifier matches DE (538AH). On return Z flag set means HL points to the matched entry; NZ means the source device does not exist in the tables.
52DD
If the NZ FLAG (Not Zero) has been set because the source device was not found, JUMP to 52E8H to handle the new-entry path (route to NIL with a fresh table slot).

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.

52E0
PUSH HL E5
Save Register Pair HL (pointing to the matched source device DCB entry) onto the stack before the chain-walk call modifies HL.
52E1
GOSUB to the DCB chain walker at 530EH to follow the forward-link chain and reach the tail DCB entry. HL is updated by 530EH to point to the tail entry.
52E4
POP HL E1
Restore Register Pair HL from the stack (the source entry pointer saved at 52E0H). HL now points back to the source DCB entry where the device-type byte will be patched.
52E5
LD (HL),08H 36 08
Store 08H into the byte at (HL), which is the control/type byte at offset +00H of the source DCB entry. The value 08H designates this entry as a NIL (bit-bucket) device that discards all I/O directed to it.
52E7
RET C9
Return to the caller. The source device has been patched to NIL. (The overall ROUTE command will return to the DOS command level via the call chain above.)

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.

52E8
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H (null identifier) to search for a free slot in the device configuration tables via the scanner at 53C9H.
52EB
GOSUB to the device table scanner at 53C9H with DE=0000H to find a free (null) slot. On return Z means a free slot was found at HL; NZ with A=08H means the tables are full.
52EE
LD A,21H 3E 21
Load Register A with 21H, pre-loading an error code in case the tables are full and the next jump must exit with an error.
52F0
If the NZ FLAG (Not Zero) has been set because no free slot was found, JUMP to 534DH to exit with the error code 21H in A.
52F3
DI F3
Disable maskable interrupts to protect the multi-byte DCB update that follows from being observed in a partial state by the ISR.
52F4
LD DE,(538AH) ED 5B 8A 53
Load Register Pair DE with the 2-byte source device identifier from 538AH (the type code bytes of the first argument). This will be written into the new table slot's identifier field.
52F8
LD (HL),08H 36 08
Store 08H into the byte at (HL), offset +00H of the free table slot. Setting 08H marks this new entry as a NIL/bit-bucket device type. (BC still holds the destination device address from 4476H.)
52FA
LD A,L 7D
Load Register A with the current value of Register L. L points to offset +00H of the new slot; adding 6 will reach offset +06H where the identifier is written.
52FB
ADD A,06H C6 06
ADD 06H to Register A, computing the L value for offset +06H within the new table slot where the 2-byte device identifier is stored.
52FD
LD L,A 6F
Load Register L with the computed offset value. HL now points to the identifier field (offset +06H) of the new table slot.
52FE
LD (HL),E 73
Store Register E (the low byte of the source device identifier loaded from 538AH) into the byte at (HL), offset +06H of the new slot.
52FF
INC L 2C
INCrement Register L by 1, advancing HL to offset +07H of the new table slot.
5300
LD (HL),D 72
Store Register D (the high byte of the source device identifier) into offset +07H of the new slot. The identifier field is now fully written.
5301
EI FB
Re-enable maskable interrupts. The DCB patch is complete.
5302
JUMP to 402DH (DOS READY / No-Error Exit) to return to the VTOS command level with success.

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.

5305-530D
DEFM "NIL " + 00H 4E 49 4C 20 20 20 20 52 00
The NIL routing mode parameter string. When DE is pointed here at 5216H and 4476H is called, the open-file utility sees "NIL" as the device specifier and returns 0000H in BC (no physical device to open), which the test at 5222H-5224H uses to select the NIL routing code path.

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.

530E
BIT 4,(HL) CB 66
Loop Start
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.
5310
If the Z FLAG (Zero) has been set because bit 4 is clear (tail entry reached), JUMP to 5319H to test the redirect flag (bit 7).
5312
INC HL 23
INCrement Register Pair HL by 1, advancing to offset +01H of the current entry, where the low byte of the 16-bit next-pointer lives.
5313
LD A,(HL) 7E
Fetch the low byte of the forward-link next-pointer from (HL) (offset +01H of the current entry) into Register A.
5314
INC HL 23
INCrement Register Pair HL by 1 again, advancing to offset +02H where the high byte of the next-pointer lives.
5315
LD H,(HL) 66
Load Register H with the high byte of the next-pointer from (HL) (offset +02H). H is now the high byte of the next DCB entry's address.
5316
LD L,A 6F
Load Register L with Register A (the low byte of the next-pointer fetched at 5313H). HL now holds the full 16-bit address of the next DCB entry in the chain.
5317
Loop End
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.

5319
BIT 7,(HL) CB 7E
Test bit 7 of the byte at (HL), the control byte of the tail DCB entry. Bit 7 set means this entry has a redirect flag active, indicating that a copy of the entry must be made into a newly allocated block before patching.
531B
RET Z C8
If the Z FLAG (Zero) has been set because bit 7 is clear (no redirect), return immediately to the caller. HL still points to the tail DCB entry and the caller will patch it directly.

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.

531C
LD DE,532DH 11 2D 53
Point Register Pair DE to 532DH, the start of a 32-byte work buffer embedded in the overlay. This will serve as the LDIR destination for the copy of the tail DCB entry.
531F
PUSH DE D5
Save Register Pair DE (the work buffer address at 532DH) onto the stack so it can be recovered as the source address for a subsequent LDIR or further processing.
5320
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H (32 decimal), the size in bytes of a DCB table entry. This is the LDIR byte count.
5323
LDIR ED B0
Block move: copy BC (32) bytes from (HL) (the tail DCB entry) to (DE) (the work buffer at 532DH), incrementing both HL and DE after each byte. The tail DCB entry is now duplicated in the work buffer.
5325
POP DE D1
Restore Register Pair DE (the work buffer base address at 532DH) from the stack. DE is the source for the next LDIR that will copy this data into the newly allocated high-memory block.
5326
GOSUB to SYS0 routine at 4428H (close file / register new DCB). This registers the newly copied DCB entry (pointed to by DE at 532DH) with the VTOS device management system. On return NZ means registration failed.
5329
If the NZ FLAG (Not Zero) has been set because DCB registration failed, JUMP to 534DH to exit with an error.
532C
RET C9
Return to the caller. The tail DCB entry has been copied into a new registered block. HL points to the work buffer (532DH) where the duplicate now lives; the caller will apply the routing patch there.

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.

532D-534C
DEFB 00H x 32 00 x 20
32-byte DCB work buffer. Filled at runtime by the LDIR at 5323H with a copy of the tail DCB entry from the device configuration table. After copying, SYS0 routine 4428H is called to register this buffer as a new DCB entry.

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).

534D
OR A,40H F6 40
Bitwise OR Register A with 40H (bit 6). This sets bit 6 of the error code in A, which VTOS uses as the error-context-suppression flag (confirmed by SYS4 analysis: bit 6 OR'd into the error code suppresses extended error context display).
534F
JUMP to 4409H (DOS Error Exit) with the error code in A. SYS0 will use this code to invoke SYS4 (the error display handler) and then return to the command level.

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.

5352
LD HL,535BH 21 5B 53
Point Register Pair HL to 535BH, the start of the embedded "DEVICE SPEC REQUIRED" error message string terminated by 0DH.
5355
GOSUB to SYS0 routine at 447BH (display with CONFIG/SYS processing) to print the "DEVICE SPEC REQUIRED" message on the screen.
5358
JUMP to 4030H (Error-already-displayed exit) to return to the VTOS command level.

535BH - Embedded Data: "DEVICE SPEC REQUIRED" Message

ASCII error string printed by 5355H. The disassembler has decoded these bytes as Z80 instructions in error.

535B-536FH
DEFM "DEVICE SPEC REQUIRED" + 0DH 44 45 56 49 43 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
Error message displayed when the ROUTE command is invoked without a valid device specifier, or when the first argument does not begin with *.

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.

5370
LD HL,5379H 21 79 53
Point Register Pair HL to 5379H, the start of the embedded "PARAMETER ERROR" message string terminated by 0DH.
5373
GOSUB to SYS0 routine at 447BH to print the "PARAMETER ERROR" string pointed to by HL (5379H) on the screen.
5376
JUMP to 4030H (Error-already-displayed exit) to return to the VTOS command level.

5379H - Embedded Data: "PARAMETER ERROR" Message

ASCII error string printed by 5373H. The disassembler has decoded these bytes as Z80 instructions in error.

5379-5388H
DEFM "PARAMETER ERROR" + 0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
Error message displayed when the second argument to ROUTE fails the open call at 4476H (521CH).

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.

5389-53A8H
DEFB 00H x 32 (runtime) 00 x 20
First device specifier work area. Filled at runtime by SYS0 routine 441CH. The first byte (5389H) is expected to be 2AH (*, validated at 520AH). The 2-byte device type identifier begins at 538AH and is used as the lookup key in the device configuration tables by the scanner at 53C9H.

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.

53A9-53C8H
DEFB 00H x 32 (runtime) 00 x 20
Second device specifier work area. Filled at runtime by SYS0 routine 441CH (called at 522AH). The first byte (53A9H) is expected to be 2AH (*, validated at 5231H). The 2-byte device type identifier at 53AAH is used as the destination device lookup key by the scanner at 53C9H (called at 5239H).

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.

53C9
LD HL,4015H 21 15 40
Point Register Pair HL to 4015H, the start of the primary device configuration table. This is the first entry to be tested. The table contains three 8-byte entries; each entry's 2-byte device identifier is at offset +06H/+07H.
53CC
PUSH HL E5
Loop Start
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.
53CD
LD A,L 7D
Load Register A with Register L (the low byte of the current entry base address). Adding 6 will reach offset +06H where the identifier bytes live.
53CE
ADD A,06H C6 06
ADD 06H to Register A, computing the L value for offset +06H within the current 8-byte table entry.
53D0
LD L,A 6F
Load Register L with the computed offset value. HL now points to the 2-byte identifier field (offset +06H) of the current table entry.
53D1
LD A,(HL) 7E
Fetch the first byte of the 2-byte identifier (offset +06H) from the current table entry into Register A.
53D2
INC L 2C
INCrement Register L by 1, advancing HL to offset +07H (the second byte of the identifier).
53D3
CP A,E BB
Compare Register A (the first identifier byte from the table entry) against Register E (the low byte of the target identifier in DE). If Register A equals E, the Z FLAG is set; otherwise the NZ FLAG is set.
53D4
If the NZ FLAG (Not Zero) has been set because the first identifier bytes do not match, JUMP to 53DDH to move to the next table entry without testing the second byte.
53D6
LD A,(HL) 7E
Fetch the second byte of the 2-byte identifier (offset +07H) from the current table entry into Register A.
53D7
CP A,D BA
Compare Register A (the second identifier byte) against Register D (the high byte of the target identifier in DE). If Register A equals D, the Z FLAG is set meaning a full 2-byte match was found.
53D8
If the NZ FLAG (Not Zero) has been set because the second identifier bytes do not match, JUMP to 53DDH to advance to the next entry.

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.

53DA
POP HL E1
Restore Register Pair HL from the stack (the matched entry's base address, saved at 53CCH). HL now points to the start of the matched device configuration table entry.
53DB
XOR A,A AF
Set Register A to ZERO and clear all flags, including the Z FLAG. Setting Z confirms that a match was found.
53DC
RET C9
Return to the caller with Z flag set and HL pointing to the matched table entry. The caller will use HL to read or patch the DCB.

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.

53DD
POP AF F1
Restore the saved entry base address from the stack into AF (A = L of entry base, F = H). The entry base was saved at 53CCH; it is recovered here to compute the next entry address.
53DE
INC L 2C
INCrement Register L by 1. Since POP AF placed the entry's L value in A (not L), this INC is actually advancing L within HL from its current value (offset +07H from 53D2H) by one more position. The real next-entry calculation uses A (recovered into L next).
53DF
If the NZ FLAG (Not Zero) has been set (the INC L result is non-zero), JUMP to 53E5H to perform the L-to-next-entry computation. If zero (unlikely at this point in table scanning), fall through to the no-match return.
53E1
LD A,08H 3E 08
Load Register A with 08H. This is the "not found" return value. A=08H is returned in Register A when neither table contains a matching entry.
53E3
OR A,A B7
Bitwise OR Register A with itself. This sets the NZ FLAG (since A=08H is non-zero) without changing A's value, establishing the NZ return condition meaning "no match found".
53E4
RET C9
Return to the caller with NZ flag set and A=08H indicating that the identifier in DE was not found in either device configuration table.
53E5
LD A,L 7D
Load Register A with Register L (current position within the table). This value is tested to detect when the end of the primary table is reached (L = 2DH).
53E6
CP A,2DH FE 2D
Compare Register A (current L value) against 2DH. When L reaches 2DH, the scan has passed the end of the primary table at 4015H-402CH (which occupies 24 bytes; 4015H + 18H = 402DH, so L=2DH marks the sentinel). If Register A equals 2DH, the Z FLAG is set.
53E8
If the NZ FLAG (Not Zero) has been set because L has not yet reached 2DH, the primary table still has entries to scan. LOOP BACK to 53CCH to save HL and test the next entry.

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.

53EA
LD HL,43C0H 21 C0 43
Point Register Pair HL to 43C0H, the start of the secondary device configuration table. This table has the same 8-byte-entry format as the primary table and is scanned in the same manner.
53ED
JUMP to 53CCH to save HL and begin scanning the secondary table from its first entry. The loop proceeds exactly as for the primary table; when the secondary table's entries are exhausted without a match, the routine will fall through to the NZ return at 53E1H-53E4H.

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.

5A00
OR A,0AFH F6 AF
FILTER Entry Point (IDAM 66H)
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.
5A02
LD (5A64H),A 32 64 5A
Self-Modifying Code
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.
5A05
LD A,(402DH) 3A 2D 40
Fetch the first byte of the DOS READY / No-Error Exit stub at 402DH in the VTOS resident DOS into Register A. When a BASIC program or chained command has installed a return vector, 402DH contains C3H (the JP opcode). When at the VTOS command level (no program running), 402DH does not contain C3H.
5A08
CP A,0C3H FE C3
Compare Register A against C3H (the JP opcode). If Register A equals C3H, the Z FLAG is set, meaning a program has installed a return vector at 402DH and we are NOT at the VTOS command level. If Register A does not equal C3H, the NZ FLAG is set, meaning we are at the command level and may proceed.
5A0A
If the Z FLAG has been set (402DH contains C3H, meaning a program is running and we are NOT at the VTOS command level), JUMP to 5AE7H to display the error message "CAN'T -- VALID ONLY AT VTOS COMMAND LEVEL" and return to the DOS READY prompt. SET and FILTER can only be issued from the interactive command line.

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.

5A0D
LD DE,5B1AH 11 1A 5B
Point Register Pair DE to the device specification buffer at 5B1AH. This buffer will receive the parsed device spec from the command line. The 2-byte device address will be stored at offset +01H/+02H (5B1BH-5B1CH) within this buffer.
5A10
GOSUB to the SYS0 filespec extraction routine at 441CH to parse the next filespec token from the command line into the buffer at DE (5B1AH). On return, the Z FLAG indicates success (filespec found), the NZ FLAG indicates failure (no filespec found). HL is advanced past the parsed token in the command line. DE points past the end of the stored filespec data.
5A13
If the NZ FLAG has been set (no filespec found on the command line), JUMP to 5AC9H to display the error message "DEVICE SPEC REQUIRED" and return to the DOS READY prompt.
5A16
LD A,(DE) 1A
Fetch the first byte of the parsed filespec from the address pointed to by Register Pair DE into Register A. After the 441CH call, DE points to the start of the parsed device name. This byte should be 2AH ('*') if the user specified a device (e.g., *CL).
5A17
CP A,2AH FE 2A
Compare Register A against 2AH (ASCII '*'). If Register A equals 2AH, the Z FLAG is set, confirming the first argument is a device specification. If not equal, the NZ FLAG is set.
5A19
If the NZ FLAG has been set (first argument does not begin with '*', so it is not a device specification), JUMP to 5AC9H to display "DEVICE SPEC REQUIRED" and return to the DOS READY prompt.

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.

5A1C
LD DE,5B3AH 11 3A 5B
Point Register Pair DE to the driver/filter file specification buffer at 5B3AH. This buffer will receive the filename of the driver or filter routine to load.
5A1F
GOSUB to the SYS0 filespec extraction routine at 441CH to parse the driver/filter filename from the command line into the buffer at DE (5B3AH). On return, Z indicates success, NZ indicates no filespec found.
5A22
If the NZ FLAG has been set (no file specification found), JUMP to 5AADH to display "FILE SPEC REQUIRED" and return to the DOS READY prompt.
5A25
LD A,(DE) 1A
Fetch the first byte of the parsed file specification from the address pointed to by Register Pair DE into Register A. This byte must NOT be 2AH ('*'), since the second argument must be a filename, not a device reference.
5A26
CP A,2AH FE 2A
Compare Register A against 2AH (ASCII '*'). If Register A equals 2AH, the Z FLAG is set, meaning the second argument is a device reference (invalid for a driver/filter filename).
5A28
If the Z FLAG has been set (second argument begins with '*', so it is a device spec rather than a filename), JUMP to 5AADH to display "FILE SPEC REQUIRED" and return to the DOS READY prompt. The user must provide a filename for the driver/filter file, not a device reference.

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.

5A2B
PUSH HL E5
Save Register Pair HL onto the stack. HL points to the current position in the command line (past the parsed filespecs), preserving it for later parameter parsing or return.
5A2C
LD A,(5A64H) 3A 64 5A
Fetch the command discrimination flag from 5A64H into Register A. This location was written by the self-modifying code at 5A02H: zero for SET, non-zero for FILTER.
5A2F
OR A,A B7
OR Register A with itself to set the flags without changing the value. If Register A is zero (SET), the Z FLAG is set. If non-zero (FILTER), the NZ FLAG is set.
5A30
LD HL,5A7CH 21 7C 5A
Point Register Pair HL to the "DVR" extension string at 5A7CH. This is the default extension for SET (device driver files).
5A33
If the Z FLAG has been set (command is SET, flag is zero), JUMP forward 3 bytes to 5A38H, keeping HL pointing to "DVR" at 5A7CH.
5A35
LD HL,5A7FH 21 7F 5A
Point Register Pair HL to the "FLT" extension string at 5A7FH. This is the default extension for FILTER (filter routine files). This instruction is skipped for SET.
5A38
GOSUB to the SYS0 routine at 4473H to insert the default file extension ("DVR" or "FLT" from HL) into the file specification buffer at 5B3AH if no explicit extension was provided by the user. If the user specified an extension, this call has no effect.

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.

5A3B
LD DE,(5B1BH) ED 5B 1B 5B
Load Register Pair DE with the 16-bit device address from 5B1BH-5B1CH (offset +01H/+02H in the device specification buffer at 5B1AH). This is the address of the DCB (Device Control Block) for the device specified by the user (e.g., the *CL or *PR device). DE now holds the device address to search for in the configuration tables.
5A3F
GOSUB to the device table search subroutine at 5A82H to scan the drive configuration tables at 4015H-402CH and 43C0H-43D7H for an entry whose offset +06H/+07H matches the device address in DE. On return: Z FLAG set = match found, HL points to the matching table entry base address. NZ FLAG set = no match found, A contains error code 08H if table exhausted.
5A42
If the Z FLAG has been set (device found in the configuration table), JUMP forward to 5A59H to proceed with loading the driver/filter file. HL points to the matching device table entry.

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.

5A44
LD A,(5A64H) 3A 64 5A
Fetch the command discrimination flag from 5A64H into Register A. Zero for SET, non-zero for FILTER.
5A47
OR A,A B7
OR Register A with itself to test the flag. If zero (SET), the Z FLAG is set. If non-zero (FILTER), the NZ FLAG is set.
5A48
LD A,08H 3E 08
Load Register A with error code 08H ("DEVICE NOT AVAILABLE"). This is pre-loaded in case the FILTER path takes the error exit. Note: LD A does not affect the flags set by the preceding OR A,A instruction.
5A4A
If the NZ FLAG has been set (command is FILTER), JUMP to 5AA8H to report error 08H ("DEVICE NOT AVAILABLE") via the DOS Error Exit. FILTER requires the specified device to already exist in the configuration table; it cannot create a new device entry.

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.

5A4C
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H. This is the "unassigned" device address marker. The search will look for a configuration table entry whose +06H/+07H bytes are both zero, indicating an empty slot available for a new device.
5A4F
GOSUB to the device table search subroutine at 5A82H to scan both configuration tables for an entry with device address 0000H (empty slot). On return: Z FLAG set = empty slot found, HL points to the entry. NZ = no empty slots available.
5A52
If the Z FLAG has been set (empty slot found), JUMP forward to 5A59H to load the driver file into memory. HL points to the empty device table entry that will receive the new driver address.
5A54
LD A,21H 3E 21
Load Register A with error code 21H. No empty device slots are available in either configuration table. All entries are already assigned to active devices.
5A56
JUMP to the error exit handler at 5AA8H to report error 21H via the DOS Error Exit at 4409H.

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).

5A59
PUSH HL E5
Save Register Pair HL onto the stack. HL points to the base of the device configuration table entry (either the matched entry or an empty slot). This is preserved for the address-patching step after the file is loaded.
5A5A
LD DE,5B3AH 11 3A 5B
Point Register Pair DE to the driver/filter file specification buffer at 5B3AH. This contains the parsed filename (with default DVR or FLT extension already inserted by 4473H).
5A5D
GOSUB to the SYS0 program loader at 4430H to load the driver/filter file specified in the buffer at DE (5B3AH) into memory. The file is loaded via the standard VTOS load module format (type 01H records with load addresses). On return: Z FLAG set = load successful, NZ FLAG set = load error with error code in A.
5A60
If the NZ FLAG has been set (file load failed), JUMP to the error exit handler at 5AA8H. Register A already contains the error code from the failed load operation.

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.

5A63
LD A,00H 3E 00
Self-Modifying Code Target
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).
5A65
OR A,A B7
OR Register A with itself to set the flags. If zero (SET), the Z FLAG is set, and execution falls through to patch the device table. If non-zero (FILTER), the NZ FLAG is set.
5A66
If the NZ FLAG has been set (command is FILTER), JUMP forward to 5A79H to skip the device address patching. FILTER does not modify the device table entry; the loaded filter routine installs itself into the I/O chain through its own initialization.

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.

5A68
POP DE D1
Restore Register Pair DE from the stack. DE now holds the base address of the device configuration table entry (saved by PUSH HL at 5A59H, popped into DE).
5A69
PUSH DE D5
Save Register Pair DE (device table entry pointer) back onto the stack for the cleanup at 5A79H.
5A6A
DI F3
Disable interrupts. This begins a critical section to ensure the 2-byte device address is written atomically. If the ISR fired between the two byte writes, it could see a partially-updated address and dispatch to an invalid location.
5A6B
LD A,E 7B
Copy the low byte of the device table entry base address from Register E into Register A. E contains the low byte of the table entry pointer (e.g., 15H for the entry at 4015H).
5A6C
ADD A,06H C6 06
ADD 06H to Register A to compute the address of offset +06H within the table entry. Offset +06H/+07H holds the 2-byte device driver address in each configuration table entry. This arithmetic assumes the entry does not cross a 256-byte page boundary (E + 06H does not overflow the low byte).
5A6E
LD E,A 5F
Store the computed offset back into Register E. DE now points to offset +06H of the device table entry (the driver address low byte).
5A6F
LD A,(5B1BH) 3A 1B 5B
Fetch the low byte of the device address from the devspec buffer at 5B1BH (offset +01H of the parsed device specification) into Register A. This is the low byte of the DCB address for the device being configured.
5A72
LD (DE),A 12
Store the low byte of the device address (Register A) into the device configuration table at the address in DE (entry base + 06H). The first byte of the 2-byte driver address field is now written.
5A73
INC E 1C
INCrement Register E by 1 to advance DE from offset +06H to offset +07H within the device table entry (the driver address high byte).
5A74
LD A,(5B1CH) 3A 1C 5B
Fetch the high byte of the device address from the devspec buffer at 5B1CH (offset +02H of the parsed device specification) into Register A.
5A77
EI FB
Re-enable interrupts. The critical section is complete; both bytes of the device driver address have been written to the configuration table entry. The ISR can now safely read the fully-updated address.
5A78
LD (DE),A 12
Store the high byte of the device address (Register A) into the device configuration table at offset +07H. The 2-byte driver address installation is now complete. Note: although EI was already issued, the Z80 delays interrupt recognition until after the instruction following EI, so this store executes before any pending interrupt is serviced.

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).

5A79
POP DE D1
Restore Register Pair DE from the stack. This discards the device table entry pointer that was saved at 5A59H (SET path) or 5A69H (SET address-patch path). For FILTER, this pops the HL that was pushed at 5A59H.
5A7A
EX (SP),HL E3
Exchange Register Pair HL with the value on top of the stack. The stack top contains the command line pointer saved by PUSH HL at 5A2BH. HL receives the command line pointer, and the stack now holds the current HL value. This sets up the return to the command dispatcher with the command line position properly restored.
5A7B
RET C9
Return to the caller. Execution returns to the VTOS command dispatcher with the driver or filter successfully installed.

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).

5A7C-5A7E
DEFM "DVR" 44 56 52
SET Default Extension
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.
5A7F-5A81
DEFM "FLT" 46 4C 54
FILTER Default Extension
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.

5A82
LD HL,4015H 21 15 40
Point Register Pair HL to the start of drive configuration table #1 at 4015H in the VTOS resident work area. This table contains 3 entries of 8 bytes each (4015H, 401DH, 4025H). Each entry has a 2-byte device address at offset +06H/+07H.
5A85
PUSH HL E5
Save Register Pair HL (current entry base address) onto the stack. This preserves the entry start address so it can be returned to the caller if a match is found.
5A86
LD A,L 7D
Copy the low byte of the current entry base address from Register L into Register A. This is used to compute the address of offset +06H within the entry.
5A87
ADD A,06H C6 06
ADD 06H to Register A to compute the low byte of the address of offset +06H (device address field) within the current table entry.
5A89
LD L,A 6F
Store the computed offset into Register L. HL now points to offset +06H of the current entry (the low byte of the device address).
5A8A
LD A,(HL) 7E
Fetch the low byte of the device address from the current table entry (offset +06H) into Register A.
5A8B
INC L 2C
INCrement Register L by 1 to advance HL from offset +06H to offset +07H (the high byte of the device address).
5A8C
CP A,E BB
Compare Register A (low byte of table entry's device address) against Register E (low byte of the target device address in DE). If equal, the Z FLAG is set; if not equal, the NZ FLAG is set.
5A8D
If the NZ FLAG has been set (low bytes do not match), JUMP forward to 5A96H to advance to the next table entry.
5A8F
LD A,(HL) 7E
Fetch the high byte of the device address from the current table entry (offset +07H) into Register A.
5A90
CP A,D BA
Compare Register A (high byte of table entry's device address) against Register D (high byte of the target device address in DE). If both bytes match, the Z FLAG is set and we have found the device.
5A91
If the NZ FLAG has been set (high bytes do not match even though low bytes matched), JUMP forward to 5A96H to advance to the next entry.

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).

5A93
POP HL E1
Restore Register Pair HL from the stack. HL now holds the base address of the matching device table entry (saved at 5A85H). This is the pointer the caller needs to reference or modify the entry.
5A94
XOR A,A AF
Set Register A to zero and set the Z FLAG. The Z FLAG signals to the caller that a matching device entry was found.
5A95
RET C9
Return to the caller with Z FLAG set (match found) and HL pointing to the base of the matching device configuration table entry.

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.

5A96
POP AF F1
Discard the saved entry base address from the stack (pushed at 5A85H) by popping into AF. The value is not needed since we are moving to the next entry.
5A97
INC L 2C
INCrement Register L by 1. After the +06H/+07H comparisons, L is at entry+08H. This INC moves it to the start of the next entry (each entry is 8 bytes, and L was already at +07H, so INC L goes to +08H = next entry base). Note: the INC at 5A8BH moved from +06 to +07; the JR at 5A8DH or 5A91H left L at +07; this INC moves to +08 which is the next entry's base.
5A98
If the NZ FLAG has been set by INC L (L did not wrap to zero), JUMP forward to 5A9EH to check the end-of-table sentinel. The Z FLAG from INC L would only be set if L wrapped from FFH to 00H, which cannot happen for valid table addresses.
5A9A
LD A,08H 3E 08
Load Register A with error code 08H ("DEVICE NOT AVAILABLE"). This is reached only if L wrapped to zero (page boundary crossed), which indicates the tables have been exhausted without finding a match.
5A9C
OR A,A B7
OR Register A with itself to set the NZ FLAG (since A = 08H, which is non-zero). The NZ FLAG signals to the caller that no matching entry was found.
5A9D
RET C9
Return to the caller with NZ FLAG set (no match) and A = 08H (error code).
5A9E
LD A,L 7D
Copy the low byte of the current position from Register L into Register A. L now points to the base of the next table entry.
5A9F
CP A,2DH FE 2D
Compare Register A against 2DH. The sentinel value 2DH marks the end of configuration table #1 (the entry at 4025H spans 4025H-402CH; the next position would be 402DH, which is the DOS READY stub, not a table entry). If L = 2DH, the NZ FLAG is set, indicating table #1 is exhausted.
5AA1
If the NZ FLAG has been set (L has not reached 2DH, so there are more entries in the current table), LOOP BACK to 5A85H to check the next entry.

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.

5AA3
LD HL,43C0H 21 C0 43
Point Register Pair HL to the start of drive configuration table #2 at 43C0H. This table also contains 3 entries of 8 bytes each (43C0H, 43C8H, 43D0H) with the same format as table #1.
5AA6
JUMP unconditionally back to 5A85H to begin scanning table #2. The end-of-table check at 5A9FH will compare L against 2DH again; since table #2 starts at C0H and spans to D7H, L will eventually reach D8H (next entry past 43D0H). D8H != 2DH, so the loop continues. The loop terminates when L wraps or when the JR NZ at 5AA1H detects L = 2DH. Since table #2 entries end at 43D7H, and the next position is 43D8H, L = D8H != 2DH, so the loop will continue to D8H+08H = E0H, E8H, F0H, etc., until L wraps to 00H at which point 5A98H falls through to the error return at 5A9AH. This effectively gives table #2 the same end-of-scan behavior: it terminates when L wraps past FFH.

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.

5AA8
OR A,40H F6 40
OR the error code in Register A with 40H to set bit 6. Bit 6 of the error code signals the SYS4 error display handler to suppress extended context information (filename, device, return address). Since SET/FILTER errors are command-level issues (wrong arguments, missing devices) rather than file I/O errors, there is no meaningful FCB or filename context to display.
5AAA
JUMP to the DOS Error Exit at 4409H in the VTOS resident DOS. This invokes the SYS4 error display overlay to decode and display the error message corresponding to the code in Register A (with bit 6 set for context suppression), then returns to the DOS READY prompt.

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.

5AAD
LD HL,5AB6H 21 B6 5A
Point Register Pair HL to the error message string "FILE SPEC REQUIRED" at 5AB6H.
5AB0
GOSUB to the SYS0 display routine at 447BH to display the error message string pointed to by HL. The string is displayed with CONFIG/SYS processing (output filtering through the configured display device).
5AB3
JUMP to the error-already-displayed exit at 4030H in the VTOS resident DOS. This bypasses the normal error code processing (since the message was already displayed) and returns directly to the DOS READY prompt.

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.

5AB6-5AC8
DEFM "FILE SPEC REQUIRED",0DH 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
ASCII string "FILE SPEC REQUIRED" (19 bytes including the 0DH carriage return terminator). Referenced by LD HL,5AB6H at 5AADH.

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 '*'.

5AC9
LD HL,5AD2H 21 D2 5A
Point Register Pair HL to the error message string "DEVICE SPEC REQUIRED" at 5AD2H.
5ACC
GOSUB to the SYS0 display routine at 447BH to display the error message string pointed to by HL.
5ACF
JUMP to the error-already-displayed exit at 4030H to return to the DOS READY prompt.

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.

5AD2-5AE6
DEFM "DEVICE SPEC REQUIRED",0DH 44 45 56 49 43 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
ASCII string "DEVICE SPEC REQUIRED" (21 bytes including the 0DH carriage return terminator). Referenced by LD HL,5AD2H at 5AC9H.

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.

5AE7
LD HL,5AF0H 21 F0 5A
Point Register Pair HL to the error message string "CAN'T -- VALID ONLY AT VTOS COMMAND LEVEL" at 5AF0H.
5AEA
GOSUB to the SYS0 display routine at 447BH to display the error message string pointed to by HL.
5AED
JUMP to the error-already-displayed exit at 4030H to return to the DOS READY 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.

5AF0-5B19
DEFM "CAN'T -- VALID ONLY AT VTOS COMMAND LEVEL",0DH 43 41 4E 27 54 20 2D 2D 20 56 41 4C 49 44 20 4F 4E 4C 59 20 41 54 20 56 54 4F 53 20 43 4F 4D 4D 41 4E 44 20 4C 45 56 45 4C 0D
ASCII string "CAN'T -- VALID ONLY AT VTOS COMMAND LEVEL" (42 bytes including the 0DH carriage return terminator). Referenced by LD HL,5AF0H at 5AE7H. This is the end of the SET/FILTER shared overlay. The final byte at 5B19H is the 0DH terminator.

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 RangePurpose
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

AddressName and Description
5200HDUMP 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.
5203HCharacter 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.
5212HDUMP 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.
526EHOutput 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).
5301HError: 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.

5200
JUMP to 5212H, the real DUMP entry point. This stub exists because the character output subroutine at 5203H is placed immediately after the entry address so it can be reached via CALL 5203H from anywhere in the overlay regardless of where the overlay's code is positioned.

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.

5203
GOSUB to ROM routine at 001BH (output a byte to a device). On entry A = byte to output, DE = starting address of the DCB of the output device (set up by the open call). On return, the Z flag is set if the device is ready and the byte was accepted; NZ means the device was not ready.
5206
RET Z C8
If the Z FLAG (Zero) has been set because the output byte was accepted successfully, return immediately to the caller. The byte has been written to the output file.
5207
RST 30H F7
Trigger RST 30H (the VTOS DEBUG/error hook, vectored through 400FH). This is called when ROM 001BH returns NZ (device not ready or error), signalling a non-recoverable I/O condition to the operating system.
5208
RES 7,A CB BF
Clear bit 7 of Register A. After RST 30H returns (or in the error-recovery path), A contains a status byte; masking bit 7 normalizes the error code before the exit test that follows.
520A
JUMP to 4409H (DOS Error Exit) with the masked error code in A. The device reported an error that RST 30H could not clear; VTOS will display the error and return to the command level.
520D-5211
NOP x 5 00 x 5
Five NOP padding bytes filling the gap between the character output subroutine and the main entry point at 5212H. These bytes are never executed.

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.

5212
Point Register Pair DE to 5390H, the filespec work area. This is the buffer that SYS0 routine 441CH will fill with the output filename token from the command line.
5215
GOSUB to SYS0 routine at 441CH to extract the output filespec token from the command line into the buffer at DE (5390H). On return DE points past the last character written. NZ flag is set if no filename was found.
5218
If the NZ FLAG (Not Zero) has been set because no filename was found on the command line, JUMP to 52EFH to display the "FILE SPEC REQUIRED" error message and exit.
521B
LD A,(DE) 1A
Fetch the first character of the extracted filespec from the buffer at DE (5390H) into Register A, to check whether a wildcard was provided instead of a real filename.
521C
CP A,2AH FE 2A
Compare Register A against 2AH (ASCII: *). A wildcard is not a valid output filename for DUMP. If Register A equals 2AH, the Z FLAG is set (error); otherwise the NZ FLAG is set (valid filename).
521E
If the Z FLAG (Zero) has been set because the filespec begins with * (a wildcard), JUMP to 52EFH to display "FILE SPEC REQUIRED" and exit. Wildcards are not acceptable output filenames.

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.

5221
LD DE,538AH 11 8A 53
Point Register Pair DE to 538AH, the start of the 6-byte hex address parse buffer. Each of the six bytes will receive either a hex digit character (30H-39H for 0-9, 41H-46H for A-F) or 20H (space) for unused positions.
5224
LD HL,5390H 21 90 53
Point Register Pair HL to 5390H, the filespec work area that was populated by 441CH. The hex digit scanner loop will read characters from (HL) to find the address digits embedded in the parameter string.
5227
LD B,06H 06 06
Load Register B with 06H (6). B is the loop counter for the hex digit parser. Six characters are collected: the maximum number of hex digits needed to represent a 16-bit address in VTOS format (four hex digits plus two separator/prefix chars).

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.

5232
LD A,(HL) 7E
Loop Start
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.
5233
CP A,30H FE 30
Compare Register A against 30H (ASCII: 0). If Register A is less than 30H (no carry from CP), the character is below the digit range. If Register A is >= 30H, the CARRY FLAG is clear (no borrow) and the character may be a valid hex digit.
5235
If the CARRY FLAG has been set because A is less than 30H (the character precedes ASCII '0'), it cannot be a hex digit. JUMP to 524AH to store a space (20H) placeholder in the parse buffer instead.
5237
CP A,3AH FE 3A
Compare Register A against 3AH (one past ASCII: 9). If A is less than 3AH and >= 30H, the character is a decimal digit 0-9 and is a valid hex digit. If the CARRY FLAG is set (A < 3AH), fall through to store it; otherwise test for uppercase A-F.
5239
If the CARRY FLAG has been set because A is in the range 30H-39H (decimal digit 0-9), JUMP to 5243H to store this valid hex digit into the parse buffer at (DE).
523B
CP A,41H FE 41
Compare Register A against 41H (ASCII: A). If A is less than 41H (between 3AH and 40H inclusive), the character is a colon, semicolon, etc. - not a valid hex digit. CARRY set means A is below 41H.
523D
If the CARRY FLAG has been set because A is below 41H (not an uppercase letter), it is not a valid hex letter A-F. JUMP to 524AH to store a space placeholder.
523F
CP A,5BH FE 5B
Compare Register A against 5BH (one past ASCII: Z). If A is less than 5BH and >= 41H, the character is an uppercase letter. If >= 5BH (bracket, etc.), it is not a valid hex letter. If the NO CARRY FLAG is set (A >= 5BH), store a space.
5241
If the NO CARRY FLAG (Not Carry) has been set because A >= 5BH (the character is above Z), JUMP to 524AH to store a space placeholder. Only characters in A-Z range reach the store at 5243H.
5243
LD (DE),A 12
Store Register A (the validated hex digit character, in range 30H-39H or 41H-5AH) into the parse buffer byte at (DE) in the 6-byte buffer at 538AH.
5244
INC HL 23
INCrement Register Pair HL by 1, advancing the source pointer to the next character in the parameter string at 5390H.
5245
INC DE 13
INCrement Register Pair DE by 1, advancing the destination pointer to the next byte in the parse buffer at 538AH.
5246
DECrement B and loop back to 5232H if B is not zero. This repeats the hex digit classification for each of the 6 parse buffer positions. When B reaches zero all six positions have been filled.
5248
JUMP to 5250H, skipping the space-filler path at 524AH, since the loop completed normally with a valid hex digit stored at the current position.
524A
LD A,20H 3E 20
Load Register A with 20H (ASCII space). This value is stored as a placeholder for positions in the parse buffer that did not receive a valid hex digit character.
524C
LD (DE),A 12
Store Register A (20H, space) into the parse buffer byte at (DE), marking this position as unused (no valid hex digit at this offset).
524D
INC DE 13
INCrement Register Pair DE by 1, advancing to the next parse buffer position. Note: HL is NOT incremented here, so the next loop iteration will re-examine the same character that caused the space fill. This allows the parser to keep trying subsequent characters from the same source position.
524E
Loop End
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.

5250
LD DE,5390H 11 90 53
Point Register Pair DE to 5390H, the filespec work area containing the output filename. This will be used by 4473H to insert the default extension if needed.
5253
Point Register Pair HL to 5356H, the embedded default extension string "CIM". This is passed to 4473H which will insert it into the filespec at DE if no extension was specified by the user.
5256
GOSUB to SYS0 routine at 4473H (insert default extension). If the filespec at DE (5390H) has no extension, the string at HL (5356H = "CIM") is appended. If an extension was already present, it is left unchanged.
5259
LD B,00H 06 00
Load Register B with 00H. B=00H is the mode parameter for the file-create / open-for-write call at 4420H that follows.
525B
LD HL,5400H 21 00 54
Point Register Pair HL to 5400H, the FCB (File Control Block) for the output CIM file. 4420H will initialise this FCB and open the file for writing.
525E
GOSUB to SYS0 routine at 4420H (read record from file, B=mode=00H, DE=FCB address was set up by 441CH/4473H, HL=5400H FCB buffer). This opens the output file for writing and initialises the FCB at 5400H. On return NZ means the file could not be opened.
5261
If the NZ FLAG (Not Zero) has been set because the file open failed, JUMP to 52EAH to set the error flag (OR A,40H) and exit via 4409H.

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.

5264
Load Register A with 05H, the load module header record type code. In VTOS/LDOS CIM format, record type 05H indicates a load module header containing the module name (up to 6 characters).
5266
GOSUB to the character output subroutine at 5203H to write the record type byte 05H to the output file via ROM 001BH.
5269
Load Register A with 06H, the PDS header record type code. Record type 06H identifies the file as a partitioned data set in VTOS format. It is written immediately after the type 05H record with the same 6-byte filename payload.
526B
GOSUB to the character output subroutine at 5203H to write the record type byte 06H to the output file.

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.

526E
LD B,06H 06 06
Load Register B with 06H (6). B is the loop counter for writing the 6 filename bytes from the parse buffer at 538AH to the output file.
5270
LD HL,538AH 21 8A 53
Point Register Pair HL to 538AH, the start of the 6-byte hex address parse buffer filled by the digit classifier loop at 5232H-524EH.
5273
LD A,(HL) 7E
Loop Start
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).
5274
INC HL 23
INCrement Register Pair HL by 1, advancing to the next byte in the parse buffer for the next iteration.
5275
GOSUB to the character output subroutine at 5203H to write the current parse buffer byte (in A) to the output file.
5278
Loop End
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.

527A
LD HL,(529AH) 2A 9A 52
Load Register Pair HL with the 16-bit END address stored at 529AH. This self-modifying location was populated by the parameter parser when the END= (or E=) parameter was processed.
527D
LD BC,(5294H) ED 4B 94 52
Load Register Pair BC with the 16-bit START address stored at 5294H. This self-modifying location was populated by the parameter parser when the START= (or S=) parameter was processed.
5281
XOR A,A AF
Set Register A to ZERO and clear all flags, clearing the carry flag in preparation for the 16-bit subtraction that follows.
5282
SBC HL,BC ED 42
Subtract Register Pair BC (START) from Register Pair HL (END) with carry (carry is 0). If END < START, the result is negative and the CARRY FLAG is set. HL now contains END - START (the range byte count, if non-negative).
5284
LD HL,5307H 21 07 53
Point Register Pair HL to 5307H, the "END LESS THAN START" error message string. This pointer is pre-loaded into HL so that if the carry test at 5287H succeeds, HL already contains the correct error message address.
5287
If the CARRY FLAG has been set because END < START (the subtraction underflowed), JUMP to 5301H to display the error message at HL (5307H = "END LESS THAN START") and exit.
5289
LD HL,5FFFH 21 FF 5F
Load Register Pair HL with 5FFFH, the address one byte below 6000H. This is the upper boundary of the protected low-memory area. DUMP requires START to be at or above 6000H; this value is used in the next subtraction to test that condition.
528C
SBC HL,BC ED 42
Subtract Register Pair BC (START) from Register Pair HL (5FFFH) with carry. If START <= 5FFFH (i.e., START is in the protected area below 6000H), the result is non-negative (NO CARRY). If START > 5FFFH (i.e., START >= 6000H), the result is negative (CARRY set).
528E
LD HL,531BH 21 1B 53
Point Register Pair HL to 531BH, the "START LESS THAN X'6000'" error message string. Pre-loaded into HL so that the JR NC branch at 5291H can use it directly if the address validation fails.
5291
If the NO CARRY FLAG (Not Carry) has been set because START <= 5FFFH (the START address falls in the protected area below 6000H), JUMP to 5301H to display the error message at HL (531BH = "START LESS THAN X'6000'") 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.

5293
LD HL,6000H 21 00 60
Point Register Pair HL to 6000H, the base address of the first memory page to dump. HL will serve as the current dump pointer that advances through the memory range as data is written.
5296
PUSH HL E5
Save Register Pair HL (6000H, the current page base address) onto the stack. It will be used as the 2-byte load address field in the type 01H record header.
5297
LD B,H 44
Load Register B with Register H (the high byte of HL = 60H). BC is being constructed to hold the byte count for the current page (the number of bytes in this record's data area).
5298
LD C,L 4D
Load Register C with Register L (the low byte of HL = 00H). BC is now 6000H, which is the start of the current page - used temporarily to track the page size computation.
5299
LD HL,6000H 21 00 60
Reload Register Pair HL with 6000H. HL will be used in the next SBC to compute the number of bytes remaining in the first page.
529C
INC HL 23
INCrement Register Pair HL by 1, making HL = 6001H. This is the address one past the end of the first 256-byte page (6000H-60FFH has 256 bytes; 6001H is used to compute the page length as 6001H - 6000H = 1, but see next note).
529D
XOR A,A AF
Set Register A to ZERO and clear all flags, clearing carry for the subtraction below.
529E
SBC HL,BC ED 42
Subtract Register Pair BC (6000H) from Register Pair HL (6001H). The result HL = 0001H is the count of bytes in the first page segment. This computation determines how many bytes from 6000H fit in the current record before the page boundary.
52A0
If the Z FLAG (Zero) has been set because the byte count is zero (START equals the page boundary exactly), JUMP to 52CBH to write the transfer address record and close the file without writing any data records.

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.

52A2
LD B,0FEH 06 FE
Load Register B with 0FEH (254 decimal). B is the default length byte for a full-page type 01H record. In VTOS CIM format, length byte FEH encodes 254 data bytes (FEH = 254; after subtracting the 2-byte load address field, FEH encodes FEH-2 = 252 data bytes... see next note for the actual encoding rule).
52A4
LD A,H 7C
Load Register A with Register H (the high byte of HL, the computed byte count for this page). If H is non-zero the count exceeds 255 and the full-page length byte FEH is used; if H is zero the count fits in one byte and may need a smaller length.
52A5
OR A,A B7
Bitwise OR Register A with itself. This tests whether A (H = high byte of count) is zero without changing its value. If NZ (count > 255 = full page), fall through; if Z (count fits in L), proceed to check L directly.
52A6
If the NZ FLAG (Not Zero) has been set because H is non-zero (this is a full page with more than 255 bytes), JUMP to 52AEH to use the default full-page length byte 0FEH already in B.
52A8
LD A,L 7D
Load Register A with Register L (the low byte of the byte count). H is zero so the entire count fits in L. This is the actual number of bytes to write in this (partial last) record.
52A9
CP A,0FFH FE FF
Compare Register A against 0FFH (255). If A equals 255 the full-page default FEH is still correct; if A is less than 255 it must be used as the length byte directly. If Register A equals 0FFH, the Z FLAG is set; otherwise the NZ FLAG is set.
52AB
If the NO CARRY FLAG (Not Carry) has been set because A >= 0FFH (255 or more, which means use the default FEH length), JUMP to 52AEH. Otherwise fall through to load the actual short count into B.
52AD
LD B,L 45
Load Register B with Register L (the actual byte count for this partial page). B now contains the exact length byte to use in the CIM record header for a partial (last) record.
52AE
POP HL E1
Restore Register Pair HL from the stack (the current page base address, saved at 5296H). HL is the 2-byte load address that will be written into this record's header.
52AF
LD A,01H 3E 01
Load Register A with 01H, the CIM object/load record type code. Record type 01H marks this as a load block containing executable code or data to be placed at the specified load address.
52B1
GOSUB to the character output subroutine at 5203H to write the record type byte 01H to the output file.
52B4
LD A,B 78
Load Register A with Register B (the length byte for this record, either 0FEH for a full page or the actual short count for the last partial record).
52B5
ADD A,02H C6 02
ADD 02H to Register A. In VTOS CIM format, the length byte of a type 01H record includes the 2-byte load address field. Adding 2 to the data count gives the total record length (data bytes + 2 address bytes).
52B7
GOSUB to the character output subroutine at 5203H to write the total record length byte (data count + 2) to the output file.
52BA
LD A,L 7D
Load Register A with Register L (the low byte of HL, the current page base / load address). This is the first byte of the 2-byte load address field in the record header.
52BB
GOSUB to the character output subroutine at 5203H to write the low byte of the load address to the output file.
52BE
LD A,H 7C
Load Register A with Register H (the high byte of HL, the current page base / load address). This is the second byte of the 2-byte load address field.
52BF
GOSUB to the character output subroutine at 5203H to write the high byte of the load address to the output file. The 4-byte record header (type, length, address-lo, address-hi) is now complete.

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.

52C2
LD A,(HL) 7E
Loop Start
Fetch the next memory byte from the address pointed to by HL (the current position in the memory range being dumped) into Register A.
52C3
INC HL 23
INCrement Register Pair HL by 1, advancing the memory dump pointer to the next byte to be written in the next iteration.
52C4
GOSUB to the character output subroutine at 5203H to write the memory byte (in A) to the output file.
52C7
Loop End
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.
52C9
LOOP BACK to 5296H to begin the next page. HL now points to the first byte of the next page. The loop will compute the next page's byte count, write the type 01H record header, and output the data bytes, repeating until all pages have been written.

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.

52CB
POP HL E1
Restore Register Pair HL from the stack. At this point HL holds the address that was pushed at 5296H in the last iteration of the page loop.
52CC
LD A,02H 3E 02
Load Register A with 02H, the CIM transfer address record type code. Record type 02H marks the end of a load module and supplies the 2-byte entry point (TRA) to which control will transfer after loading.
52CE
GOSUB to the character output subroutine at 5203H to write the record type byte 02H to the output file.
52D1
LD A,02H 3E 02
Load Register A with 02H. This is the length byte of the type 02H record. A type 02H record always contains exactly 2 bytes of data (the 16-bit transfer address), so its length byte is always 02H.
52D3
GOSUB to the character output subroutine at 5203H to write the length byte 02H to the output file.
52D6
LD HL,402DH 21 2D 40
Point Register Pair HL to 402DH (the DOS READY / No-Error Exit / warm-start address). This is the default transfer address written into the type 02H record when the user did not supply a TRA= parameter. The user-supplied TRA value, if present, would have been placed in the self-modifying storage locations; here 402DH is used directly as the default.
52D9
LD A,L 7D
Load Register A with Register L (the low byte of HL = 2DH, the low byte of the transfer address 402DH).
52DA
GOSUB to the character output subroutine at 5203H to write the low byte of the transfer address (2DH) to the output file.
52DD
LD A,H 7C
Load Register A with Register H (the high byte of HL = 40H, the high byte of the transfer address 402DH).
52DE
GOSUB to the character output subroutine at 5203H to write the high byte of the transfer address (40H) to the output file. The type 02H record is now complete.
52E1
GOSUB to SYS0 routine at 4428H (close file) to flush all buffered output and close the CIM output file. On return NZ means the close failed.
52E4
If the NZ FLAG (Not Zero) has been set because the close failed, JUMP to 52EAH to set the error flag and exit via 4409H.
52E7
JUMP to 402DH (DOS READY / No-Error Exit) to return to the VTOS command level. The CIM file has been successfully written and closed.

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).

52EA
OR A,40H F6 40
Bitwise OR Register A with 40H, setting bit 6 of the error code. Bit 6 is the VTOS error-context-suppression flag (confirmed by SYS4 analysis), which suppresses extended error context display in the error handler.
52EC
JUMP to 4409H (DOS Error Exit) with the modified error code in A. VTOS will invoke the error display handler (SYS4) and then return to the command level.

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.

52EF
LD HL,5333H 21 33 53
Point Register Pair HL to 5333H, the start of the embedded "FILE SPEC REQUIRED" error message string terminated by 0DH.
52F2
GOSUB to SYS0 routine at 447BH (display with CONFIG/SYS processing) to print the "FILE SPEC REQUIRED" message pointed to by HL (5333H) on the screen.
52F5
JUMP to 4030H (Error-already-displayed exit) to return to the VTOS command level.

52F8H - Error: "PARAMETER ERROR"

Reached when the second argument to DUMP fails validation. The error message string is embedded at 5346H.

52F8
LD HL,5346H 21 46 53
Point Register Pair HL to 5346H, the start of the embedded "PARAMETER ERROR" message string terminated by 0DH.
52FB
GOSUB to SYS0 routine at 447BH to print the "PARAMETER ERROR" message pointed to by HL (5346H) on the screen.
52FE
JUMP to 4030H (Error-already-displayed exit) to return to the VTOS command level.

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.

5301
GOSUB to SYS0 routine at 447BH (display with CONFIG/SYS processing) to print the error message string pointed to by HL on the screen.
5304
JUMP to 4030H (Error-already-displayed exit) to return to the VTOS command level.

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.

5307-531AH
DEFM "END LESS THAN START" + 0DH 45 4E 44 20 4C 45 53 53 20 54 48 41 4E 20 53 54 41 52 54 0D
Error message displayed when the subtraction at 5282H produces a negative result (END < START). Printed by the common error handler at 5301H via 447BH, then exits to 4030H.

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.

531B-5332H
DEFM "START LESS THAN X'6000'" + 0DH 53 54 41 52 54 20 4C 45 53 53 20 54 48 41 4E 20 58 27 36 30 30 30 27 0D
Error message displayed when the subtraction at 528CH produces a non-negative result (START <= 5FFFH, meaning START is in the protected area below 6000H). Printed by the common error handler at 5301H via 447BH, then exits to 4030H.

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.

5333-5345H
DEFM "FILE SPEC REQUIRED" + 0DH 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
Error message displayed when the filespec is absent or is a wildcard. Printed by 52F2H via 447BH, then exits to 4030H.

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.

5346-5355H
DEFM "PARAMETER ERROR" + 0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
Error message displayed when a DUMP parameter is invalid. Printed by 52FBH via 447BH, then exits to 4030H.

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.

5356-5358H
DEFM "CIM" 43 49 4D
Default output file extension. Pointed to by HL at 5253H and passed to SYS0 routine 4473H (insert default extension). When the user does not supply an extension in the DUMP command, 4473H appends "CIM" to the filespec at 5390H. The resulting file is a Core Image Module in VTOS load-module format.
5359-5389H
DEFB 00H x 49 (runtime work area) 00 x 31
Padding and filespec work area bytes between the CIM extension and the hex parse buffer. Filled at runtime by 441CH and the digit classifier loop.
538A-538FH
DEFB 00H x 6 (runtime) 00 x 06
6-byte hex address parse buffer. Filled at runtime by the digit classifier loop at 5232H-524EH. Each byte receives either a hex digit character (30H-39H or 41H-46H) or 20H (space) for unused positions. The full 6 bytes are written to the CIM header records as the module-name field. Self-modifying: the values at 5294H-5295H (START) and 529AH-529BH (END) within this general area are also runtime-written by the parameter parser.
5390-53FFH
DEFB 00H x 112 (runtime) 00 x 70
Filespec work area. Filled at runtime by SYS0 routine 441CH (called at 5215H) with the output filename token from the command line. Also serves as the source for the hex digit classifier loop at 5224H.
5400-54FFH
DEFB 00H x 256 (runtime FCB) 00 x 100
File Control Block for the output CIM file. Initialised at runtime by the file-open call at 525EH (SYS0 routine 4420H with HL=5400H). All subsequent write operations via 5203H use this FCB to direct output to the correct disk file.

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

AddressName and Description
5200HPURGE 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.
524FHDirectory 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.
5266HDirectory 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.
5290HFilename 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.
52E7HFile 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.
5314HPassword 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).
5346HPassword 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.
534CHDirectory 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).
53B8HPassword 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.
53CBHError 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.
53D0HParameter 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).
53D9HInvalid 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.

5200
LD A,(430FH) 3A 0F 43
Fetch the system state flags byte from 430FH (the VTOS multi-bit system state register, initialized to 00H at cold boot). This register controls overlay dispatch behavior, re-entry mode, debug state, and other system-wide flags. Its contents are needed immediately to check bit 5.
5203
BIT 5,A CB 6F
Test bit 5 of the system state flags byte now held in Register A. In VTOS, bit 5 of 430FH signals "re-entry mode / program chaining active." If set (NZ flag), the system is currently executing a chained program sequence and the PURGE command is not permitted to run.
5205
If the NZ FLAG (Not Zero) has been set - meaning bit 5 of 430FH is 1, indicating program chaining is active - JUMP to 53D9H to display the "INVALID COMMAND DURING PROGRAM CHAINING" error message and exit. PURGE cannot run while chaining is in progress because it requires interactive user input at the console.

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.

5208
LD C,00H 0E 00
Load Register C with 00H (drive number 0) as the default drive number. If no drive specifier is found on the command line, drive 0 will be used for the PURGE operation. Register C will hold the parsed or default drive number until it is stored to 5320H.
520A
LD A,(HL) 7E
Fetch the first character from the command line buffer at the address currently in Register Pair HL (pointing just past "PURGE" in the command buffer). This character is the beginning of any parameters the user may have supplied.
520B
CP A,3AH FE 3A
Compare Register A against 3AH (ASCII: ':' colon). The VTOS convention for specifying a drive on the command line uses a leading colon followed by a single digit, e.g., :1 meaning drive 1. If Register A equals 3AH, the Z FLAG is set; otherwise the NZ FLAG is set.
520D
If the NZ FLAG (Not Zero) has been set - meaning the first command line character is not a colon - JUMP to 5215H, skipping the drive-digit parse. Drive 0 (already in Register C from the LD C,00H at 5208H) will be used as the default.

A colon was found. The next character is the drive digit (0, 1, 2, or 3). Read and convert it.

520F
INC HL 23
INCrement Register Pair HL by 1 to advance past the colon character in the command line buffer, positioning HL at the drive digit character (ASCII '0' through '3').
5210
LD A,(HL) 7E
Fetch the drive digit character from the command line buffer at the address now in Register Pair HL. This will be an ASCII digit ('0'=30H, '1'=31H, '2'=32H, '3'=33H) representing the target drive number.
5211
INC HL 23
INCrement Register Pair HL by 1 again to advance past the drive digit, leaving HL pointing at whatever follows (the password string, a space, or end-of-line). HL will be used as the command line scan pointer for remaining parameter parsing.
5212
SUB A,30H D6 30
SUBtract 30H from Register A, converting the ASCII drive digit character to its binary equivalent. For example, ASCII '0' (30H) - 30H = 00H (drive 0), ASCII '1' (31H) - 30H = 01H (drive 1), etc. The result is now the binary drive number 0-3.
5214
LD C,A 4F
Load Register C with the binary drive number just computed from Register A (0-3). Register C now holds the user-specified drive number, overriding the default of 0 set at 5208H.
5215
LD A,C 79
Load Register A with the drive number from Register C (either the parsed drive digit or the default 00H). This value is about to be stored to the self-modifying drive number variable at 5320H for use throughout the rest of the command.
5216
LD (5320H),A 32 20 53
Self-Modifying Code
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).
5219
PUSH HL E5
Save Register Pair HL (currently pointing to the password portion of the command line, just past the optional :d drive specifier) onto the stack. HL is preserved here so it can be recovered after the drive configuration call that follows.
521A
GOSUB to SYS0 routine at 44B8H to validate and activate the drive number stored at 5320H. This routine checks that the specified drive is configured and present, sets up any necessary hardware state, and returns Z on success or NZ if the drive is not available or invalid. The call may involve reading drive configuration tables at 4015H and 43C0H.
521D
POP HL E1
Restore Register Pair HL from the stack (recovering the command line pointer saved at 5219H, now pointing at the password string portion of the command line).
521E
LD A,20H 3E 20
Load Register A with 20H (ASCII space character). This value is used in the next instruction as a sentinel: if the drive call returned NZ (drive error), the code checks whether the error code in A is 20H. However, this LD is actually preparing a default "no error" path - the JP NZ that follows tests the Z FLAG from the CALL 44B8H result preserved since the POP did not affect flags.
5220
If the NZ FLAG (Not Zero) has been set - meaning the CALL 44B8H drive validation returned an error - JUMP to 53CBH to OR the error code with 40H (bit 6 = suppress extended context) and exit via the DOS error handler at 4409H.

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.

5223
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H. This 16-bit zero value represents "no password supplied" - it will be stored to the two-byte password pointer variable at 5344H-5345H. A null pointer tells the password verification subroutine at 5346H to prompt the user interactively for the master password rather than reading it from the command line.
5226
LD (5344H),BC ED 43 44 53
Store 0000H from Register Pair BC into the two-byte password pointer variable at 5344H-5345H (low byte 5344H = 00H, high byte 5345H = 00H). This initializes the password pointer to null; if the user typed MPW=password, this will be overwritten with the actual pointer by the parameter scanner that follows.
522A
LD DE,544EH 11 4E 54
Point Register Pair DE to 544EH, which contains the directory filespec string "MPW DS" + 00H. This is the FCB-style filespec used to open the directory file. The string is formatted as a VTOS filespec: the first field ("MPW") is a placeholder name and "DS" (at 5455H-5456H) is the file extension denoting the directory sector file type.
522D
GOSUB to SYS0 routine at 4476H (open file utility). Entry conditions: DE = pointer to the filespec string at 544EH. This routine locates the directory file on the disk, validates it, and sets up the internal file state for subsequent sector reads. Returns Z on success (file opened), NZ on failure (file not found, drive error, etc.).
5230
If the NZ FLAG (Not Zero) has been set - meaning the directory file could not be opened - JUMP to 53D0H to display the "PARAMETER ERROR" message and exit via 4030H (error-already-displayed exit). This covers cases such as an invalid drive number or a missing 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).

5233
GOSUB to the password verification subroutine at 5314H. This routine: reads the two-byte password pointer from 5344H-5345H; if null, prompts the user for the master password and collects it into 5500H; reads directory sector 0 into 5600H; and compares the supplied password against the stored master password hash. Returns Z on match (correct password), NZ on mismatch. On NZ, the subroutine itself jumps to 4409H and does not return.

Password verified. Now set up the directory traversal. Load the drive number and prepare to read the first directory sector into the 5700H buffer.

5236
LD A,(5320H) 3A 20 53
Fetch the drive number (0-3) from 5320H (the drive number variable set at 5216H from the command line parse). This drive number is needed to set up the track/sector address for directory I/O via 4B65H.
5239
LD C,A 4F
Load Register C with the drive number fetched from 5320H. Register C is the drive number parameter expected by the SYS0 routine at 4B65H (set up track/sector for directory I/O).
523A
GOSUB to SYS0 routine at 4B65H (set up track/sector for directory I/O). Entry: C = drive number. This routine computes the physical track and sector numbers for the directory on the specified drive and stores them internally so that the next CALL 4B45H will read the correct directory sector.
523D
LD E,01H 1E 01
Load Register E with 01H, which is the sector number within the directory track to read first (sector 1 = the first actual directory entries, since sector 0 holds the GAT/password record already read during password verification). Register E is the sector number parameter for CALL 4B45H.
523F
LD HL,5700H 21 00 57
Point Register Pair HL to 5700H, the 256-byte directory sector buffer where the directory sector will be loaded. This buffer holds one full directory sector at a time (8 directory entries x 32 bytes each = 256 bytes). Register Pair HL is the destination buffer address parameter for CALL 4B45H.
5242
GOSUB to SYS0 routine at 4B45H (read sector from disk). Entry: HL = 5700H (destination buffer), E = 01H (sector number within directory track). This routine reads one 256-byte directory sector from the disk into the buffer at 5700H. Returns Z on success, NZ on read error.
5245
LD A,16H 3E 16
Load Register A with 16H (the VTOS error code for "Directory Read Error"). This value is pre-loaded so that if the sector read failed, the next instruction can immediately jump to the error exit with a meaningful error code in A.
5247
If the NZ FLAG (Not Zero) has been set - meaning the directory sector read at 4B45H failed - JUMP to 53CBH with error code 16H in A. The error exit at 53CBH ORs A with 40H and jumps to 4409H (DOS error exit).
524A
LD HL,5700H 21 00 57
Point Register Pair HL to 5700H (the start of the directory sector buffer just loaded). HL is now the pointer to the first directory entry in the sector. The traversal loop that begins at 524FH will walk through entries by incrementing HL by 32 bytes (one entry) at a time, keeping HL within the 5700H-57FFH sector buffer.
524D
JUMP to 5266H to enter the directory traversal loop at the entry-validity check point. This bypasses the "advance to next entry" logic at 524FH (which uses POP BC to get the previous entry address), going directly to the validity test with HL already pointing at the first entry (5700H).

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.

524F
POP BC C1
Restore Register Pair BC from the stack. Register B holds the low byte of the current directory entry's address within the 5700H buffer (i.e., the value of L when the entry was selected). Register C holds the directory entry info byte saved before the kill decision. These were pushed at 5270H before the entry was processed.
5250
LD H,57H 26 57
Load Register H with 57H, setting the high byte of Register Pair HL to 57H so that HL addresses within the 5700H-57FFH directory sector buffer. The low byte L will be set from B in the next instruction.
5252
LD L,B 68
Load Register L with the low byte of the previous entry address (from Register B, recovered by POP BC). Register Pair HL now points back to the start of the entry that was just processed (i.e., 57xxH where xx = B).
5253
LD A,L 7D
Load Register A with the current low-byte pointer value from Register L (the entry offset within the 5700H sector buffer, 0-based, in multiples of 32 = 20H per entry). This value drives the "advance by 32 bytes" arithmetic that follows.
5254
ADD A,20H C6 20
ADD 20H (32 decimal) to Register A to advance to the next 32-byte directory entry. If the result overflows past FFH (i.e., L was E0H or higher), the CARRY FLAG is set, indicating the pointer has gone past the last entry in the 256-byte sector buffer.
5256
LD L,A 6F
Load Register L with the updated entry offset from Register A. Register Pair HL now points to the next directory entry in the 5700H-57FFH sector buffer (or wraps to 5700H area if overflow occurred).
5257
If the NO CARRY FLAG has been set - meaning the ADD A,20H did not overflow (the next entry is still within the current sector buffer) - JUMP to 5266H to check the validity of the next directory entry. The traversal continues within the same sector.

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.

5259
INC L 2C
INCrement Register L by 1. After the overflow, L contains a wrapped value; this increment adjusts the pointer to discriminate the "end of sector" condition from the mid-sector case. The next instruction tests whether the wrapped-and-incremented value equals 1FH, confirming the end-of-sector condition.
525A
CP A,1FH FE 1F
Compare Register A (the post-ADD value before wrap - still in A from the LD L,A path via the fall-through from 5256H) against 1FH. If Register A equals 1FH, the Z FLAG is set, confirming that the last entry at offset E0H + 20H = overflow = end of the 256-byte sector was just processed. This check ensures the carriage return and DOS return only happen at the true end-of-sector boundary.
525C
If the NZ FLAG (Not Zero) has been set - meaning this is NOT the end-of-sector condition - JUMP to 5266H to continue examining entries. This handles edge cases in the pointer arithmetic where the value is in the overflow range but not at the expected end boundary.

All entries in this directory sector have been examined. Print a carriage return and return to the DOS command level.

525E
LD A,0DH 3E 0D
Load Register A with 0DH (ASCII carriage return character). This will be displayed to move the cursor to the start of a new line on the terminal, providing clean formatting at the end of the PURGE directory listing.
5260
GOSUB to ROM routine at 0033H (display character in A at cursor, advance cursor). Outputs the carriage return character (0DH) from Register A to the screen, moving the cursor to the beginning of the next line.
5263
JUMP to SYS0 location 402DH (DOS READY / No-Error Exit). This terminates the PURGE command normally after all directory entries in the sector have been examined. The DOS READY prompt is displayed and the system returns to command input mode.

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.

5266
LD A,L 7D
Load Register A with the current low byte of the directory entry pointer from Register L. Register L holds the offset into the 5700H-57FFH sector buffer (0H, 20H, 40H, ... E0H for the 8 entries). This value is used as a quick sanity check before reading the entry.
5267
AND A,0FEH E6 FE
Bitwise AND Register A with 0FEH (11111110 binary), masking out bit 0. This tests whether the low byte is even (bit 0 = 0). All valid entry offsets are multiples of 32 (20H), so their low byte is always 00H - i.e., even. An odd low byte would indicate misalignment; clearing bit 0 and testing for zero detects this. If the result is zero the Z FLAG is set.
5269
If the Z FLAG (Zero) has been set - meaning the low byte after masking is 00H, i.e., L itself was 00H (pointing back to the very start of the buffer 5700H, which should not happen on a valid advance) - LOOP BACK to 5253H to re-advance. This guards against a misaligned pointer reaching the beginning of the buffer.
526B
LD A,(HL) 7E
Fetch the directory entry status byte from the address currently in Register Pair HL (the first byte of the 32-byte directory entry at offset +00H in the VTOS directory entry layout). A value of 00H means the entry is deleted/empty and should be skipped.
526C
OR A,A B7
Bitwise OR Register A with itself. This is the standard Z80 idiom to test whether Register A is zero without changing its value. If Register A is 00H (empty/deleted entry), the Z FLAG is set; otherwise the NZ FLAG is set and the entry is active.
526D
If the Z FLAG (Zero) has been set - meaning the entry status byte at (HL) is 00H (deleted or empty slot) - LOOP BACK to 5253H to advance to the next entry. Deleted entries are not presented to the user for purging.

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.

526F
LD B,L 45
Load Register B with the current low byte of the entry pointer from Register L (the entry's offset within the 5700H buffer: 00H, 20H, 40H, ...). Register B now holds the entry's position index; it will be pushed with C and later recovered by POP BC at 524FH to recompute the entry address after the prompt.
5270
PUSH BC C5
Save Register Pair BC onto the stack. Register B holds the entry pointer low byte (from 526FH) and Register C holds the directory entry info byte (or a placeholder at this point). This push preserves the entry context across the call to 4B10H that follows.
5271
LD A,L 7D
Load Register A with the current entry offset (low byte of HL) from Register L. This is used to compute the sector address byte passed to 4B10H for validating which physical directory sector and entry position this slot corresponds to.
5272
AND A,0E0H E6 E0
Bitwise AND Register A with 0E0H (11100000 binary). This extracts bits 7-5 of the entry offset (i.e., the sector-within-directory index encoded in the high 3 bits of the offset), discarding the low 5 bits (which encode the byte position within the sector). For offsets 00H-1FH: result = 00H; for 20H-3FH: 20H; etc. This gives the sector group identifier used by 4B10H.
5274
LD L,A 6F
Load Register L with the sector group identifier just computed. Register Pair HL is now: H=57H (the sector buffer page), L=sector-aligned offset (one of 00H, 20H, 40H, 60H, 80H, A0H, C0H, E0H). This points to the start of the current directory sector's entry group within the 5700H buffer for the 4B10H call.
5275
XOR A,B A8
Bitwise XOR Register A (sector group offset) with Register B (entry pointer low byte saved at 526FH). If the entry address has the same sector-group bits as the rounded-down value (i.e., XOR result = the entry-within-sector bits), this distinguishes the first entry (B = L before rounding) from subsequent ones. The result is the entry info byte that will be passed to 4B10H.
5276
CP A,0FFH FE FF
Compare Register A (the computed entry info byte from XOR A,B) against 0FFH. The value 0FFH is a special sentinel meaning "scan all entries" (the initialized value of the scan variable at 5277H). If Register A equals 0FFH, the Z FLAG is set and the 4B10H validation call is skipped - the first iteration uses the pre-initialized scan variable directly.
5278
If the Z FLAG (Zero) has been set - meaning the XOR result is 0FFH (first-pass sentinel) - JUMP to 5283H to read the directory entry directly from the buffer without calling 4B10H first. This handles the initial entry on the very first pass through the loop when no prior 4B10H call has been made.
527A
LD (5277H),A 32 77 52
Self-Modifying Code
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.
527D
GOSUB to SYS0 routine at 4B10H (directory entry validation). This routine reads the directory sector containing the entry identified by the info byte at 5277H into an internal buffer and validates the entry's structure (checksum, allocated status, etc.). Returns Z on success (valid entry), NZ on failure (directory read error or invalid entry).
5280
If the NZ FLAG (Not Zero) has been set - meaning 4B10H returned an error (directory read failure or invalid entry structure) - JUMP to 53CBH to OR the error code with 40H and exit via 4409H (DOS error exit).
5283
LD H,42H 26 42
Load Register H with 42H, setting the high byte of Register Pair HL to 42H. This re-points HL into the 4200H-42FFH directory sector buffer (the SYS0 shared directory buffer where 4B10H deposited the validated entry). Register L still holds the sector-aligned offset from 5274H, so HL now points to the correct 32-byte entry within the 4200H buffer. Note: 42H is the high byte of the SYS0 directory buffer base address 4200H.
5285
LD A,(HL) 7E
Fetch the attribute byte from the directory entry at offset +00H in the 4200H buffer (the first byte of the 32-byte VTOS directory entry, which holds status/attribute flags). This byte contains the attribute bits that determine whether the file should be skipped or presented to the user for purging.
5286
BIT 4,A CB 67
Test bit 4 of the directory entry attribute byte now in Register A. In the VTOS directory entry layout (offset +00H), bit 4 indicates the file is a system file (locked/protected). System files and the directory file itself are not purgeable and must be silently skipped.
5288
If the Z FLAG (Zero) has been set - meaning bit 4 of the attribute byte is 0, i.e., the file IS a system file (bit 4 = 0 in VTOS means system-protected; the naming is inverted from TRSDOS) - JUMP to 524FH to advance to the next entry without prompting the user. System files are unconditionally skipped by PURGE.
528B
BIT 7,A CB 7F
Test bit 7 of the directory entry attribute byte still in Register A. In the VTOS directory entry layout, bit 7 indicates the file is invisible (hidden from directory listings). Invisible files should not be presented in the PURGE listing either.
528D
If the NZ FLAG (Not Zero) has been set - meaning bit 7 of the attribute byte is 1, i.e., the file is invisible - JUMP to 524FH to advance to the next entry without prompting. Invisible files are silently skipped by PURGE.

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.

5290
PUSH HL E5
Save Register Pair HL (currently pointing into the 4200H directory buffer at the current entry's attribute byte) onto the stack. HL is preserved here so the filename field can be addressed by offset arithmetic from HL after the display call.
5291
LD HL,541EH 21 1E 54
Point Register Pair HL to 541EH, which is the start of the DEFM string "PURGE FILE: " + 03H. This string is the label displayed before each filename when prompting the user. The 03H at 5429H is the display terminator recognized by 4467H.
5294
GOSUB to SYS0 routine at 4467H (display message to screen). Entry: HL = 541EH (pointer to the "PURGE FILE: " label string). This routine outputs characters from the string starting at HL until it encounters the 03H terminator byte. The cursor remains at the end of the displayed string on the current line.
5297
POP HL E1
Restore Register Pair HL from the stack (recovering the pointer into the 4200H directory buffer at the current entry, which was saved at 5290H). HL now points to offset +00H of the directory entry. The filename starts at offset +05H and the extension at +0DH.
5298
LD A,L 7D
Load Register A with the low byte of the current entry pointer from Register L. This is the offset into the 4200H buffer (0H, 20H, 40H, etc.) for the current entry. Register A will be used to compute the address of the filename field by adding the field offset (+05H).
5299
ADD A,05H C6 05
ADD 05H to Register A to skip from the entry's attribute byte (offset +00H) to the filename field (offset +05H in the VTOS directory entry layout). The result is the low byte of the address of the 8-character filename within the 4200H buffer.
529B
LD L,A 6F
Load Register L with the filename field offset from Register A. Register Pair HL (H=42H from 5283H) now points to offset +05H of the current directory entry in the 4200H buffer - the start of the 8-character filename field (space-padded).
529C
LD DE,5441H 11 41 54
Point Register Pair DE to 5441H, the start of the filename display work buffer where the assembled "name/ext" string will be built. This 13-byte buffer (8 chars name + "/" + 3 chars ext + 03H terminator) will be filled character by character from the directory entry fields.
529F
LD B,08H 06 08
Load Register B with 08H (8 decimal), the maximum number of characters in the filename field (VTOS filenames are 8 characters, space-padded). Register B is the loop counter for the filename copy loop that follows.

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.

52A1
LD A,(HL) 7E
Fetch the next character of the filename from the directory entry in the 4200H buffer at the address currently in Register Pair HL. Filenames are 8 bytes long, padded with 20H (space) to fill unused trailing characters.
52A2
INC HL 23
INCrement Register Pair HL by 1 to advance to the next character position in the filename field within the 4200H directory buffer.
52A3
CP A,20H FE 20
Compare Register A against 20H (ASCII space character). If Register A equals 20H, the Z FLAG is set, indicating a trailing space padding character has been found - the filename ends before 8 characters. If Register A is not 20H, the NZ FLAG is set and this is a real filename character to copy.
52A5
If the Z FLAG (Zero) has been set - meaning a space padding character was found in the filename - JUMP to 52ADH to skip past the remaining filename padding and proceed directly to the extension handling. Trailing spaces in VTOS filenames are not displayed.
52A7
LD (DE),A 12
Store the current filename character from Register A into the display work buffer at the address currently in Register Pair DE (starting at 5441H, advancing with each character). This copies real filename characters (non-space) to the display buffer.
52A8
INC DE 13
INCrement Register Pair DE by 1 to advance the write pointer in the display work buffer at 5441H to the next position for the following character.
52A9
DECrement B and loop back to 52A1H if not zero. Register B counts the remaining filename characters to examine (starting from 08H). Each non-space character decrements B by 1 and the loop continues. When B reaches zero (all 8 characters have been copied) or when a space terminates the loop early via the JR Z above, execution falls through to the extension separator.
52AB
JUMP to 52B1H to skip the "rewind to last non-space" logic at 52ADH-52B0H and go directly to the extension separator step. This is taken when DJNZ exhausted the loop counter (all 8 chars copied without hitting a trailing space).

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).

52AD
LD A,L 7D
Load Register A with the current low byte of Register Pair HL. At this point HL was incremented by INC HL at 52A2H just before finding the space. Register A now holds the incremented position; it will be adjusted to recompute the extension field address.
52AE
ADD A,B 80
ADD the remaining loop counter in Register B to Register A (the current HL low byte). Since B holds the count of characters NOT yet consumed in the 8-byte filename field, adding B to the current L position computes L's value if the entire 8-byte field had been scanned. This fast-forwards the address to just past the full filename field.
52AF
DEC A 3D
DECrement Register A by 1, adjusting for the off-by-one introduced by the INC HL at 52A2H that advanced past the space before the JR Z was taken. The result is the correct address of the byte immediately following the 8-character filename field - which is the start of the extension field at directory entry offset +0DH.
52B0
LD L,A 6F
Load Register L with the corrected extension field address from Register A. Register Pair HL (H=42H) now points to offset +0DH of the current directory entry in the 4200H buffer - the start of the 3-character extension field.
52B1
LD A,(HL) 7E
Fetch the first byte of the extension field from the directory entry in the 4200H buffer at the address now in Register Pair HL (offset +0DH). In VTOS, the extension field is 3 characters, space-padded. A value of 20H (space) means the file has no extension.
52B2
CP A,20H FE 20
Compare Register A against 20H (ASCII space). If Register A equals 20H, the Z FLAG is set, meaning the extension field is blank (no extension). In that case the "/" separator and extension are omitted from the display. If NZ, there is an extension to display.
52B4
If the Z FLAG (Zero) has been set - meaning the extension field is blank (20H) - JUMP to 52C6H to skip the "/" separator and extension copy, proceeding directly to the 03H display terminator. The filename will be displayed without an extension.

The file has an extension. Write the "/" separator character into the display buffer, then copy up to 3 extension characters (stopping on spaces).

52B6
LD A,2FH 3E 2F
Load Register A with 2FH (ASCII '/' forward-slash character). This is the separator character between the filename and extension in the displayed format (e.g., "MYFILE/CMD").
52B8
LD (DE),A 12
Store the '/' separator character from Register A into the display work buffer at the current position pointed to by Register Pair DE (in the 5441H-544DH area). The slash is written immediately after the last non-space filename character.
52B9
INC DE 13
INCrement Register Pair DE by 1 to advance the write pointer in the display work buffer past the '/' separator to the next position, ready for the first extension character.
52BA
LD B,03H 06 03
Load Register B with 03H (3 decimal), the maximum number of extension characters to copy (VTOS extensions are 3 characters, space-padded). Register B is the loop counter for the extension copy loop.

Loop Start
Copy up to 3 extension characters from the directory entry into the display buffer, stopping on a space padding character.

52BC
LD A,(HL) 7E
Fetch the next extension character from the directory entry in the 4200H buffer at the address currently in Register Pair HL (starting at offset +0DH and advancing).
52BD
INC HL 23
INCrement Register Pair HL by 1 to advance to the next extension character position in the 4200H directory buffer.
52BE
CP A,20H FE 20
Compare Register A against 20H (ASCII space character). If Register A equals 20H, the Z FLAG is set, indicating a trailing space in the extension - the extension ends here. If NZ, this is a real extension character to copy.
52C0
If the Z FLAG (Zero) has been set - meaning a space padding character was found in the extension - JUMP to 52C6H to write the 03H display terminator and end the filename assembly. Trailing spaces in the extension are omitted.
52C2
LD (DE),A 12
Store the current extension character from Register A into the display work buffer at the current position pointed to by Register Pair DE.
52C3
INC DE 13
INCrement Register Pair DE by 1 to advance the write pointer in the display work buffer to the next position.
52C4
DECrement B and loop back to 52BCH if not zero. Register B counts the remaining extension characters (3, 2, 1). Each non-space character decrements B and the loop continues. When B reaches zero all 3 extension characters have been copied, or when a space was found the JR Z exits early.

Loop End
Filename and extension characters have been copied into the display buffer. Write the 03H terminator and display the assembled string.

52C6
LD A,03H 3E 03
Load Register A with 03H, the display string terminator recognized by the 4467H display routine. This marks the end of the assembled "name/ext" string in the display work buffer at 5441H.
52C8
LD (DE),A 12
Store the 03H terminator byte from Register A into the display work buffer at the current position pointed to by Register Pair DE, completing the "name/ext\x03" string assembly.
52C9
LD HL,5441H 21 41 54
Point Register Pair HL to 5441H, the start of the assembled "name/ext" display string in the work buffer. This is passed to 4467H to display the filename on screen.
52CC
GOSUB to SYS0 routine at 4467H (display message to screen). Entry: HL = 5441H (pointer to the assembled filename/extension string). Outputs the filename (and extension if present) to the screen. The cursor remains immediately after the last character.
52CF
PUSH HL E5
Save Register Pair HL (HL was advanced to point past the 03H terminator by 4467H; this value is not needed but HL must be preserved across the next display call to allow recovery of the BC context). The actual purpose is to save the stack frame in the order needed by the POP HL at 52E3H.
52D0
LD HL,542BH 21 2B 54
Point Register Pair HL to 542BH, the start of the DEFM string " ? " + 03H (the question-mark prompt displayed after the filename). This prompts the user to decide whether to purge the file.
52D3
GOSUB to SYS0 routine at 4467H (display message to screen). Entry: HL = 542BH (pointer to the " ? " prompt string). Displays " ? " immediately after the filename. The full prompt now reads: "PURGE FILE: name/ext ? " awaiting the user's response.

Full prompt displayed. Now collect the user's single-character response via the ROM line-input routine.

52D6
LD HL,541AH 21 1A 54
Point Register Pair HL to 541AH, the start of the 4-byte line input buffer where the user's response character will be stored. The ROM routine 0040H requires HL = buffer address. Register B (set next) limits input to 3 characters maximum.
52D9
LD B,03H 06 03
Load Register B with 03H (3 decimal), the maximum number of characters the ROM line-input routine 0040H will accept into the buffer at 541AH. A maximum of 3 is used to collect the user's Y or N response (typically just 1 character plus ENTER).
52DB
GOSUB to ROM routine at 0040H (line input: B=max chars, HL=buffer). This routine waits for the user to type characters followed by ENTER (0DH). It stores up to B characters into the buffer at HL (541AH here) and returns when ENTER is pressed or B characters have been entered. The first character typed is stored at 541AH.
52DE
LD A,(541AH) 3A 1A 54
Fetch the first character of the user's response from the line input buffer at 541AH (where the ROM routine 0040H stored it). This is the character the user typed in response to the "PURGE FILE: name/ext ?" prompt - typically 'Y' (59H) to purge or any other key to skip.
52E1
CP A,59H FE 59
Compare Register A against 59H (ASCII 'Y' uppercase). If Register A equals 59H, the Z FLAG is set, meaning the user typed 'Y' and wants to purge (kill) the current file. Any other character (including 'y' lowercase, 'N', or ENTER alone) leaves the NZ FLAG set.
52E3
POP HL E1
Restore Register Pair HL from the stack (the value saved at 52CFH, which was HL after the 4467H call at 52CCH - pointing past the 03H terminator of the filename string in the display buffer). This is discarded in the next step; the POP is needed to balance the PUSH at 52CFH and restore the correct stack level.
52E4
If the NZ FLAG (Not Zero) has been set - meaning the user did NOT press 'Y' (any other key: skip this file) - JUMP to 524FH to advance to the next directory entry without killing the current file. The file is left intact.

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.

52E7
POP BC C1
Restore Register Pair BC from the stack (the pair pushed at 5270H before the entry was processed). Register B holds the low byte of the directory entry address (the entry's offset within the 5700H sector buffer: 00H, 20H, 40H, etc.) and Register C holds the directory info byte for this entry. These values are needed to fill the FCB for the kill call.
52E8
PUSH BC C5
Push Register Pair BC back onto the stack immediately after popping it. This re-saves the entry context (B = entry offset, C = info byte) so that the traversal loop at 524FH can recover it via POP BC after the kill completes and the "*** PURGED ***" message is displayed. The loop must continue from the same entry position.
52E9
LD A,B 78
Load Register A with Register B (the directory entry offset low byte, which encodes the entry's position within the directory sector). This value will be stored to the FCB's directory info field so that the kill routine knows which entry to deallocate.
52EA
LD (545EH),A 32 5E 54
Self-Modifying Code
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.
52ED
LD A,(5320H) 3A 20 53
Fetch the drive number (0-3) from 5320H (the drive number variable set during command line parsing at 5216H). This value is needed to fill the FCB's drive number field so the kill routine accesses the correct disk drive.
52F0
LD (545DH),A 32 5D 54
Self-Modifying Code
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.
52F3
LD A,01H 3E 01
Load Register A with 01H, the record-length value to be written into the FCB at 5458H (FCB offset +01H). A record length of 01H indicates a single-byte record, appropriate for the kill operation which does not transfer data but only modifies directory and GAT structures.
52F5
LD (5458H),A 32 58 54
Self-Modifying Code
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.
52F8
LD A,80H 3E 80
Load Register A with 80H, the FCB status byte indicating "open for output / kill mode." In the VTOS FCB layout (IX+00H = status flags), bit 7 set (80H) signals to the close routine that this FCB should result in a file kill (deallocation of extents and directory entry deletion) rather than a normal file close.
52FA
LD (5457H),A 32 57 54
Self-Modifying Code
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.
52FD
LD DE,5457H 11 57 54
Point Register Pair DE to 5457H, the base address of the FCB that was just built for the kill operation. Register Pair DE is the FCB pointer parameter expected by the SYS0 routine at 442CH (close-with-GAT-read).
5300
GOSUB to SYS0 routine at 442CH (close file with GAT read). Entry: DE = 5457H (pointer to the kill FCB). With the FCB status byte set to 80H (kill mode), this routine reads the GAT sector, traverses the file's extent chain, deallocates all allocated granules in the GAT bitmap, and marks the directory entry as deleted (status byte = 00H). Returns Z on success, NZ on error.
5303
If the NZ FLAG (Not Zero) has been set - meaning the kill operation at 442CH returned an error (GAT read failure, directory write error, etc.) - JUMP to 53CBH to OR the error code with 40H and exit via 4409H (DOS error exit).

File successfully killed. Display the "*** PURGED ***" confirmation message, then reset the scan variable and loop to the next entry.

5306
LD HL,5430H 21 30 54
Point Register Pair HL to 5430H, the start of the DEFM control + "*** PURGED ***" + 0DH confirmation string. This is displayed immediately after a file has been successfully killed to inform the user of the deletion.
5309
GOSUB to SYS0 routine at 4467H (display message to screen). Entry: HL = 5430H (pointer to the "*** PURGED ***" confirmation string). Outputs the confirmation text to the screen, including the leading control characters for cursor positioning and the carriage return at the end.
530C
LD A,0FFH 3E FF
Load Register A with 0FFH, the "scan all drives" sentinel value for the directory scan index variable at 5277H. After a kill, the scan variable must be reset so that the next call to 4B10H starts from the beginning of its directory sector scan rather than from the (now-deleted) entry's position.
530E
LD (5277H),A 32 77 52
Self-Modifying Code
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.
5311
JUMP to 524FH (the directory traversal loop advance point) to advance to the next directory entry. The BC context (entry offset and info byte) was re-pushed at 52E8H, so POP BC at 524FH will correctly recover the position of the just-killed entry, and ADD A,20H will advance to the following entry.

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.

5314
LD DE,(5344H) ED 5B 44 53
Load Register Pair DE with the 16-bit password pointer stored at 5344H-5345H (low byte 5344H, high byte 5345H). If no password was supplied on the command line, this is 0000H (the null pointer set at 5226H). If a password was parsed from the command line via the parameter scanner, DE points into the command line buffer at the start of the password text.
5318
LD HL,5388H 21 88 53
Point Register Pair HL to 5388H, the start of the DEFM string "MASTER PASSWORD ? " (18 bytes). This is the prompt string displayed when the user must enter the master password interactively. Register Pair HL is passed to the sub-routine at 5346H to identify which prompt to display.
531B
GOSUB to the password read helper at 5346H. Entry: DE = password pointer (0000H if none supplied), HL = 5388H (prompt string pointer). This routine: reads directory sector 0 into 5600H, displays the prompt at HL and collects input if DE=0000H, or copies the command-line password from (DE) if DE is non-null. Pads the result to 8 chars with spaces and returns DE=5500H (normalized password buffer).
531E
PUSH HL E5
Save Register Pair HL (updated by 5346H to point past any consumed command line text) onto the stack. HL is preserved here in case the caller needs the updated command line pointer after verification completes. (The return from 5314H does not use HL from the stack in this path.)
531F
LD C,00H 0E 00
Load Register C with 00H, initializing the sector number for the directory sector read. The password comparison subroutine at 53B8H will use C=00H as the sector parameter to direct 4B65H/4B45H to read the directory's sector 0 (the GAT/password sector).
5321
GOSUB to the password comparison subroutine at 53B8H. Entry: DE = 5500H (normalized 8-byte password buffer from 5346H), C = 00H (sector 0 = GAT/password sector). This routine reads the directory's sector 0 into 5600H via 4B65H/4B45H and compares the 8 bytes at 5500H against the master password stored in the directory sector. Returns Z on match, NZ on mismatch.
5324
If the NZ FLAG (Not Zero) has been set - meaning the password supplied does not match the disk's master password - JUMP to 533FH to display the "INVALID MASTER PASSWORD" error and exit via 4409H. The PURGE operation is aborted.

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).

5327
POP DE D1
Restore Register Pair DE from the stack (the HL value saved at 531EH, which was HL after the 5346H call - pointing past any consumed command line characters or to the prompt string). DE is now used as a comparison value for the two built-in bypass password checks that follow.
5328
LD HL,0C294H 21 94 C2
Load Register Pair HL with 0C294H. This is the first of two hard-coded "bypass password" sentinel values checked by the password verifier. These 16-bit constants represent special master passwords built into VTOS that unconditionally allow PURGE access regardless of what is stored in the directory sector.
532B
XOR A,A AF
Set Register A to ZERO and clear all flags (XOR A with itself). This clears the CARRY FLAG before the SBC HL,DE comparison that follows, ensuring the subtraction is a pure 16-bit comparison without borrow from a prior operation.
532C
SBC HL,DE ED 52
16-bit subtract: HL = HL - DE - CARRY. With CARRY cleared by XOR A above, this computes HL = 0C294H - DE. If DE equals 0C294H (first bypass sentinel), the result is zero and the Z FLAG is set, indicating the first bypass password was recognized.
532E
RET Z C8
Return from the subroutine if the Z FLAG (Zero) is set - meaning DE matched the first bypass sentinel 0C294H. Returning Z signals to the caller (the CALL 5314H at 5233H) that the password verification passed. The PURGE operation proceeds.
532F
LD HL,(56CEH) 2A CE 56
Load Register Pair HL with the 16-bit value stored at 56CEH. This is the second bypass password sentinel, stored as data within the PURGE member rather than as an inline constant, making it harder to locate by inspection. The 56CEH location is within the PURGE member's data area.
5332
XOR A,A AF
Set Register A to ZERO and clear all flags to reset the CARRY FLAG before the second SBC HL,DE comparison.
5333
SBC HL,DE ED 52
16-bit subtract: HL = HL - DE - CARRY. With CARRY cleared, this computes HL = (56CEH) - DE. If DE equals the second bypass sentinel loaded from 56CEH, the result is zero and the Z FLAG is set.
5335
RET Z C8
Return from the subroutine if the Z FLAG (Zero) is set - meaning DE matched the second bypass sentinel from 56CEH. Returning Z again signals password verification passed.

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.

5336
LD HL,53A0H 21 A0 53
Point Register Pair HL to 53A0H, the start of the DEFM string "INVALID MASTER PASSWORD" + 0DH. This error message is displayed when the password supplied does not match the disk's master password (reached via the JP NZ,533FH at 5324H, which falls through here).
5339
GOSUB to SYS0 routine at 447BH (display message with CONFIG/SYS processing). Entry: HL = 53A0H (pointer to the "INVALID MASTER PASSWORD" error string). Outputs the error message to the screen.
533C
JUMP to SYS0 location 4030H (error-already-displayed exit). The error message has already been shown; this exit tells the DOS not to display any additional error message but simply to return to the READY prompt.
533F
OR A,40H F6 40
Bitwise OR Register A with 40H (01000000 binary). In VTOS, setting bit 6 of the error code before passing it to 4409H suppresses the extended context display (filename and return address). This is appropriate for the "INVALID MASTER PASSWORD" error since there is no relevant file context to show.
5341
JUMP to SYS0 location 4409H (DOS error exit). Register A holds the error code ORed with 40H. The DOS error handler at 4409H will look up and display the VTOS error message corresponding to the error code in A, then return to the command prompt.

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.

5346
GOSUB to the directory sector 0 reader at 534CH. This routine uses the drive number from 5320H to set up track/sector via 4B65H with E=00H (sector 0), then reads 256 bytes into 5600H via 4B45H. Returns Z on success, NZ on read error.
5349
LD A,0E4H 3E E4
Load Register A with 0E4H, the SVC code for the RST 28H call that follows. In VTOS, RST 28H with A=0E4H invokes an overlay function (the exact sub-function selected is 0E4H AND 0FH = 04H, which maps to a specific SYS0 overlay utility). This call is used here to process or validate the directory sector just read into 5600H.
534B
RST 28H EF
Execute RST 28H (the VTOS overlay SVC mechanism). With A=0E4H loaded at 5349H, this invokes SYS0's overlay dispatcher at 400CH, which interprets A as the SVC function code and dispatches to the appropriate handler. The result is used to validate or post-process the directory sector read into 5600H.
534C
LD A,D 7A
Load Register A with Register D (the high byte of Register Pair DE, which holds the command-line password pointer). If DE = 0000H (no password on command line), D = 00H. Testing D and E for zero together (next instruction) determines whether to prompt the user or copy from the command line.
534D
OR A,E B3
Bitwise OR Register A (high byte of DE) with Register E (low byte of DE). If DE = 0000H, A OR E = 00H and the Z FLAG is set, meaning no password was supplied on the command line. If DE is any non-zero pointer, the Z FLAG is clear.
534E
If the Z FLAG (Zero) has been set - meaning DE = 0000H (no password on command line) - JUMP to 536BH to display the "MASTER PASSWORD ? " prompt and collect the password interactively from the keyboard into 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.

5350
LD HL,5500H 21 00 55
Point Register Pair HL to 5500H, the start of the 8-byte password comparison buffer. Characters from the command line will be written here one by one as they are copied from the address in DE.
5353
LD B,08H 06 08
Load Register B with 08H (8 decimal), the maximum number of password characters to copy (VTOS passwords are up to 8 characters). Register B is the loop counter for the command-line password copy loop.

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.

5355
LD A,(DE) 1A
Fetch the next character from the command line buffer at the address currently in Register Pair DE (pointing into the command line at the password text).
5356
CP A,0DH FE 0D
Compare Register A against 0DH (ASCII carriage return). If Register A equals 0DH, the Z FLAG is set, indicating end-of-line - the password string has ended. Jump to the padding step.
5358
If the Z FLAG (Zero) has been set - carriage return found - JUMP to 537FH to pad the remaining bytes of 5500H with space characters (20H) and return.
535A
CP A,2CH FE 2C
Compare Register A against 2CH (ASCII ',' comma). In VTOS command line syntax, a comma separates parameters. If a comma is found, the password string ends here.
535C
If the Z FLAG (Zero) has been set - comma delimiter found - JUMP to 537FH to pad with spaces and return.
535E
CP A,22H FE 22
Compare Register A against 22H (ASCII '"' double-quote). VTOS allows quoted parameter values; if a quote is found at this position it signals the end of the password field.
5360
If the Z FLAG (Zero) has been set - quote delimiter found - JUMP to 537FH to pad with spaces and return.
5362
INC DE 13
INCrement Register Pair DE by 1 to advance the command-line read pointer past the current character, ready to fetch the next password character on the next iteration.
5363
LD (HL),A 77
Store the current password character from Register A into the password buffer at the address currently in Register Pair HL (5500H, 5501H, ... advancing with each character).
5364
INC HL 23
INCrement Register Pair HL by 1 to advance the write pointer in the 5500H password buffer to the next position.
5365
DECrement B and loop back to 5355H if not zero. Register B counts the remaining password characters to copy (8, 7, 6, ... 1). If B reaches zero all 8 characters have been copied; fall through to return with DE pointing to the 5500H buffer.
5367
LD DE,5500H 11 00 55
Load Register Pair DE with 5500H (the start of the normalized 8-character password buffer). This sets the return value - the caller (5314H) receives DE=5500H pointing to the ready-to-compare password. RET follows immediately.
536A
RET C9
Return from the subroutine to the caller at 531BH. DE = 5500H (normalized password buffer, fully populated from command line). The caller will use 53B8H to compare 5500H against the directory's stored master password.
536B
GOSUB to SYS0 routine at 4467H (display message to screen). At this point, HL = 5388H (the "MASTER PASSWORD ? " prompt string passed in by the caller at 5318H). Displays the prompt on screen and leaves the cursor positioned after the "? " for the user to type the password.
536E
LD B,08H 06 08
Load Register B with 08H (8 decimal), the maximum number of password characters to accept from the keyboard. Register B is the character count parameter for the ROM line-input routine 0040H.
5370
LD HL,5500H 21 00 55
Point Register Pair HL to 5500H, the 8-byte password input buffer. The ROM routine 0040H will store the characters the user types here.
5373
GOSUB to ROM routine at 0040H (line input: B=max chars=08H, HL=buffer=5500H). Waits for the user to type up to 8 characters and press ENTER. Stores the typed characters into 5500H onwards. Register B is decremented for each character typed; on return, B holds the number of characters NOT typed (i.e., 8 - chars_entered).
5376
EX DE,HL EB
Exchange Register Pairs DE and HL. After the 0040H call, HL points to the position just past the last character typed (5500H + chars_entered). Register DE will now hold this pointer, and Register Pair HL (formerly DE) may hold an unrelated value. This sets up the padding calculation: DE points just past the typed characters in the 5500H buffer.
5377
LD H,00H 26 00
Load Register H with 00H, zeroing the high byte of Register Pair HL. Register L still holds Register B's value (from the exchange - but after EX DE,HL, HL = old DE which was the buffer base 5500H before the call, and B has remaining count). This prepares HL for the 16-bit offset addition that computes the number of padding spaces needed.
5379
LD L,B 68
Load Register L with Register B (the remaining character count from 0040H = 8 - chars_entered). Register Pair HL = 000XH where X = number of spaces still needed to pad the buffer to 8 characters.
537A
ADD HL,DE 19
ADD Register Pair DE (the pointer just past the last typed character in 5500H) to Register Pair HL (0 + remaining count). The result in HL is now 5500H + chars_entered + remaining_count = 5500H + 8 = 5508H. Wait - this actually computes the address of the end of the 8-byte field: DE (end-of-typed) + HL (remaining slots) = the address one past the 8th byte. HL is used to set B for the padding loop.
537B
LD A,08H 3E 08
Load Register A with 08H (8 decimal), the total password buffer length. This is used to compute how many trailing space padding characters are needed to fill the remainder of the 8-byte buffer.
537D
SUB A,B 90
SUBtract Register B (the remaining character count = 8 - chars_entered) from Register A (8). Result: A = 8 - (8 - chars_entered) = chars_entered. But this value is then loaded into B as the padding loop count... actually this means B = chars_entered, which is the number of positions that were typed. Wait - the padding spaces go into the positions AFTER the typed chars. Re-examining: B (from 0040H) = chars NOT typed. A = 8. A - B = 8 - (chars not typed) = chars typed. The padding loop needs to write (8 - chars typed) = B original spaces. This instruction reloads B with the number of positions to pad (the unfilled portion).
537E
LD B,A 47
Load Register B with the padding count from Register A (number of space characters needed to fill the remainder of the 8-byte buffer). The loop at 537FH will write exactly this many space characters.

Loop Start
Pad the remainder of the 5500H password buffer with space (20H) characters to normalize the password to exactly 8 bytes.

537F
LD (HL),20H 36 20
Store a space character (20H) into the password buffer at the address currently in Register Pair HL (the position immediately after the last typed or copied character in 5500H). This pads the password to 8 bytes so it can be compared character-for-character against the directory's stored master password.
5381
INC HL 23
INCrement Register Pair HL by 1 to advance to the next position in the password buffer for the next space character.
5382
DECrement B and loop back to 537FH if not zero. Register B counts remaining padding characters. When B reaches zero the 8-byte password buffer is fully populated (typed characters + space padding).

Loop End
Password buffer at 5500H is now exactly 8 bytes long. Set DE=5500H and return to the caller.

5384
LD DE,5500H 11 00 55
Load Register Pair DE with 5500H (the start of the normalized 8-character password buffer). This is the return value passed back to the caller at 5314H: DE = 5500H = pointer to the ready-to-compare password.
5387
RET C9
Return from the subroutine to the caller at 531BH+3. DE = 5500H (normalized password buffer). The caller at 531FH will set C=00H and call 53B8H to compare 5500H against the directory's stored master password.

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.

5388-5398
DEFM "MASTER PASSWORD ? " 4D 41 53 54 45 52 20 50 41 53 53 57 4F 52 44 20 3F 20
Data Region - Not Executable Code
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.
53A0-53B7
DEFM "INVALID MASTER PASSWORD" + 0DH 49 4E 56 41 4C 49 44 20 4D 41 53 54 45 52 20 50 41 53 53 57 4F 52 44 0D
Data Region - Not Executable Code
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.

53B8
PUSH DE D5
Save Register Pair DE (the normalized password buffer pointer, 5500H, set by the caller) onto the stack. DE is preserved across the disk I/O calls that follow, since 4B65H and 4B45H may alter DE during their operation.
53B9
PUSH HL E5
Save Register Pair HL (the prompt string pointer or other caller-context value from Register Pair HL at entry) onto the stack. HL is preserved across the disk I/O calls.
53BA
GOSUB to SYS0 routine at 4B65H (set up track/sector for directory I/O). Entry: C = 00H (set by the caller at 531FH, specifying sector 0 = the GAT/password sector). The drive number is read from 5320H internally by 4B65H. This call computes the physical track and sector for the directory's sector 0 and stores them for the next 4B45H call.
53BD
LD E,00H 1E 00
Load Register E with 00H (sector number 0). Register E is the sector number parameter for the CALL 4B45H that follows, directing it to read sector 0 of the directory track (the GAT/password sector) into the destination buffer.
53BF
LD HL,5600H 21 00 56
Point Register Pair HL to 5600H, the 256-byte sector buffer where directory sector 0 will be loaded. The master password hash stored in the directory's GAT sector will be available at the appropriate offset within this buffer after the read.
53C2
GOSUB to SYS0 routine at 4B45H (read sector from disk). Entry: HL = 5600H (destination buffer), E = 00H (sector 0 of directory track). Reads 256 bytes from the directory's sector 0 into 5600H. Returns Z on success (sector read), NZ on read error.
53C5
POP HL E1
Restore Register Pair HL from the stack (recovering the caller-context HL value saved at 53B9H).
53C6
POP DE D1
Restore Register Pair DE from the stack (recovering the normalized password buffer pointer 5500H saved at 53B8H). The Z/NZ flag from CALL 4B45H is preserved through the POP instructions (POP does not affect flags).
53C7
RET Z C8
Return from the subroutine if the Z FLAG (Zero) is set - meaning the directory sector 0 was read successfully. Returning Z to the caller at 5321H signals that the disk read succeeded and the password data in 5600H is valid for comparison. The caller checks the NZ path next at 5324H.
53C8
LD A,14H 3E 14
Load Register A with 14H (VTOS error code 14H = "Read Error"). The disk read at 4B45H failed (NZ was returned). Load the read error code into A so the caller can pass it to the DOS error handler.
53CA
RET C9
Return from the subroutine to the caller at 5321H+3 with NZ flag set (preserved from the failed 4B45H) and A = 14H (Read Error). The caller at 5324H will JP NZ,533FH to handle the 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.

53CB
OR A,40H F6 40
Bitwise OR Register A with 40H (01000000 binary), setting bit 6 of the error code. In VTOS, bit 6 of the error code passed to 4409H suppresses the extended context information (filename and return address) that would normally be appended to the error message. This is appropriate for PURGE's directory traversal errors since the context is internal to PURGE's operation.
53CD
JUMP to SYS0 location 4409H (DOS error exit). The error code in Register A (with bit 6 set) is passed to the error handler, which looks up the VTOS error message for the code and displays it, then returns control to the DOS command prompt.

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.

53D0
LD HL,53E2H 21 E2 53
Point Register Pair HL to 53E2H, the start of the DEFM string "PARAMETER ERROR" + 0DH. This error message is displayed when the directory file could not be opened (invalid drive or missing directory file).
53D3
GOSUB to SYS0 routine at 447BH (display message with CONFIG/SYS processing). Entry: HL = 53E2H (pointer to "PARAMETER ERROR" string). Outputs the error message to the screen with appropriate paging support.
53D6
JUMP to SYS0 location 4030H (error-already-displayed exit). The error message has been displayed; this exit suppresses any additional error output and returns to the VTOS READY prompt.

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.

53D9
LD HL,53F2H 21 F2 53
Point Register Pair HL to 53F2H, the start of the DEFM string "INVALID COMMAND DURING PROGRAM CHAINING" + 0DH. This error is specific to the case where PURGE is attempted while a program chaining sequence is in progress (430FH bit 5 set).
53DC
GOSUB to SYS0 routine at 447BH (display message with CONFIG/SYS processing). Entry: HL = 53F2H (pointer to the chaining error string). Displays the error message on screen.
53DF
JUMP to SYS0 location 4030H (error-already-displayed exit). Returns to the VTOS READY prompt after displaying the chaining error message.

53E2H - Data: Error and Message Strings

Two DEFM error strings used by the error exit routines at 53D0H and 53D9H.

53E2-53F1
DEFM "PARAMETER ERROR" + 0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
Data Region - Not Executable Code
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).
53F2-5419
DEFM "INVALID COMMAND DURING PROGRAM CHAINING" + 0DH 49 4E 56 41 4C 49 44 20 43 4F 4D 4D 41 4E 44 20 44 55 52 49 4E 47 20 50 52 4F 47 52 41 4D 20 43 48 41 49 4E 49 4E 47 0D
Data Region - Not Executable Code
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.

541E-542A
DEFM "PURGE FILE: " + 03H 50 55 52 47 45 20 46 49 4C 45 3A 20 03
Data Region - Not Executable Code
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.
541A-541D
DEFB 4 bytes (line input buffer) [runtime data]
Data Region - Not Executable Code
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.
542B-542D
DEFM " ? " + 03H 20 3F 20 03
Data Region - Not Executable Code
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 ? ".
5430-543DH
DEFM 1BH + E3H + "*** PURGED ***" + 0DH 1B E3 2A 2A 2A 20 50 55 52 47 45 44 20 2A 2A 2A 0D
Data Region - Not Executable Code
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.
5441-544DH
DEFB 13 bytes (filename work buffer) [runtime data]
Data Region - Not Executable Code
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.
544E-5456H
DEFM "MPW DS" + 00H 4D 50 57 20 20 44 53 00
Data Region - Not Executable Code
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.

5457-545EH
DEFB 8 bytes (FCB data area) [runtime data]
Data Region - Not Executable Code
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

AddressName and Purpose
5200HXFER 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.
5241HSource 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.
526DHDestination 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.
52A1HMain 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).
52F0HTransfer 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.
530EHEnd-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.
5333HConditional 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.
534FHError 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).
5357HSystem 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.
5364HSource 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.
5381HDestination 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.
539EH18-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.
53A7HDisplay 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.
53D4HSVC 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.
53D7HSVC Stub: SVC 8AH
Loads A=8AH and issues RST 28H.
53DAHSVC Stub: Mount Destination Drive (SVC 85H)
Loads A=85H and issues RST 28H.
53DDHRead 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.
53F0HError 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.

5200
LD A,(430FH) 3A 0F 43
Fetch the System State Flags byte from 430FH into Register A. This byte is a bitmapped register controlling VTOS operational modes; bit 5 (20H) signals that the command interpreter is operating in re-entry/chaining mode, meaning a program chain is currently in progress.
5203
BIT 5,A CB 6F
Test bit 5 of Register A (the re-entry/chaining mode flag, value 20H). If this bit is set, VTOS is currently chaining programs - XFER cannot run during a chain because it requires interactive disk swapping. Sets the Z FLAG if bit 5 is clear (chaining not active); clears Z if bit 5 is set (chaining active).
5205
If the NZ FLAG (Not Zero) has been set - meaning bit 5 of 430FH is set and a program chain is active - JUMP to 53FEH to display "INVALID COMMAND DURING PROGRAM CHAINING" and abort. XFER requires interactive user input for disk swapping and cannot run unattended during a chain.

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.

5208
LD DE,54CEH 11 CE 54
Point Register Pair DE to 54CEH, the address of the Source FCB (File Control Block) buffer. The filespec extraction routine at 441CH will parse the command-line buffer and populate this 32-byte FCB structure with the filename, extension, drive number, and other file parameters.
520B
GOSUB to SYS0 routine at 441CH to extract the filespec from the command-line buffer into the FCB at 54CEH (DE). This routine parses the filespec typed by the user, fills in filename, extension, and drive fields of the FCB, and returns Z set on success, NZ if no valid filespec was found.
520E
If the NZ FLAG (Not Zero) has been set - meaning no valid filespec was found on the command line - JUMP to 53F5H to display "FILE SPEC REQUIRED" and abort. XFER cannot function without knowing which file to transfer.

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.

5211
LD HL,(4049H) 2A 49 40
Load Register Pair HL with the 16-bit Detected RAM Top Address stored at 4049H. This is the highest available RAM address as determined during VTOS initialization. XFER will use this value to compute the upper page boundary for its in-memory sector buffer, which grows one page at a time as records are read from the source file.
5214
INC HL 23
INCrement Register Pair HL by 1, moving from the last valid RAM byte to one past it. This converts the inclusive top address into an exclusive upper bound. The following instruction then extracts just the page number (H register).
5215
DEC H 25
DECrement the H register (high byte of HL, i.e., the page number) by 1. After the INC HL above, H now holds the ceiling page. Decrementing gives the last full page available to the buffer. This value will be stored as the upper-page stop limit for the read loop: when the buffer's current page reaches this value, XFER knows it must flush to disk before reading more.
5216
LD A,H 7C
Load Register A with H (the computed maximum buffer page number). Register A now holds the upper page limit for XFER's in-memory transfer buffer.
5217
LD (52D4H),A 32 D4 52
Self-Modifying Code
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.
521A
LD (52EBH),A 32 EB 52
Self-Modifying Code
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.

521D
LD HL,54CEH 21 CE 54
Point Register Pair HL to 54CEH, the start of the Source FCB buffer that was just populated by 441CH. XFER will scan through this buffer looking for the colon character (3AH = ':') that separates the filename from the drive number in a filespec like "MYFILE/TXT:1".
5220
LD C,00H 0E 00
Load Register C with 00H. Register C accumulates the drive number found after the colon. Initializing to 00H means that if no drive specifier is found, the default drive (drive 0) will be used.

5222H - Filespec Scan Loop

5222
LD A,(HL) 7E
Loop Start
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.
5223
INC HL 23
INCrement Register Pair HL by 1 to advance to the next character in the filespec buffer, ready for the next iteration.
5224
CP A,3AH FE 3A
Compare Register A against 3AH (ASCII ':'). If Register A equals 3AH, the Z FLAG is set and the colon has been found - the drive digit follows immediately. If not equal, the NZ FLAG is set and we continue scanning.
5226
If the Z FLAG (Zero) has been set - meaning the current character is ':' - JUMP to 522EH to read the drive digit that follows the colon.
5228
CP A,20H FE 20
Compare Register A against 20H (ASCII space). If Register A is less than 20H, the CARRY FLAG is set, indicating a control character or string terminator has been reached, meaning the filespec has ended without a drive specifier. If Register A is 20H or greater, the NO CARRY FLAG is set and scanning continues.
522A
If the CARRY FLAG has been set - meaning a byte below 20H (a terminator) was found - JUMP to 5232H to use whatever drive number is already in C (00H if no colon was seen, or the digit value if one was found earlier). The scan loop ends here.
522C
LOOP BACK to 5222H to examine the next character. Neither a colon nor a terminator was found, so the scan continues forward through the filespec string.
522EH
LD A,(HL) 7E
Fetch the byte immediately following the colon from (HL) into Register A. This is the drive digit character (e.g., ASCII '1' = 31H for drive 1).
522F
SUB A,30H D6 30
SUBtract 30H from Register A to convert the ASCII drive digit ('0' through '7') to a binary drive number (0 through 7). After subtraction, Register A holds the raw binary drive index.
5231
LD C,A 4F
Store Register A (the binary drive number, 0-7) into Register C. Register C now carries the drive number for all subsequent disk I/O operations in this session.

5232H - Drive Number Save and FCB Copy

5232
LD A,C 79
Load Register A with Register C (the drive number, 0-7, or 0 if no drive specifier was found).
5233
LD (5358H),A 32 58 53
Self-Modifying Code
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.
5236
LD HL,54CEH 21 CE 54
Point Register Pair HL to 54CEH, the Source FCB that was just populated by the filespec extraction at 520BH. This FCB will be copied verbatim to create the Destination FCB.
5239
LD DE,54EEH 11 EE 54
Point Register Pair DE to 54EEH, the Destination FCB buffer address. The source and destination FCBs are kept separate so they can be opened independently on their respective disks.
523C
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H (32 decimal). This is the number of bytes to copy - exactly one full FCB (32 bytes).
523F
LDIR ED B0
Block Move: copy 32 bytes (BC=0020H) from Source FCB at 54CEH (HL) to Destination FCB at 54EEH (DE), incrementing both pointers. After this, both FCBs are identical clones of the parsed filespec - they will be opened independently on the source and destination diskettes.

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.

5241
GOSUB to 53D4H, the SVC 84H stub. This issues RST 28H with A=84H to invoke overlay SVC function 84H, which mounts or selects the source drive so the FDC is ready to access it.
5244
LD HL,548BH 21 8B 54
Point Register Pair HL to 548BH, the address of the "INSERT SOURCE DISK (ENTER)" prompt string. This is loaded before the verify call so that if the GAT read fails, the source-disk verify loop at 53A7H already has the correct message pointer.
5247
GOSUB to 53A7H, the display-and-wait-for-ENTER routine. Displays the "INSERT SOURCE DISK (ENTER)" message at 548BH and waits for the user to insert the source diskette and press ENTER. This is the initial prompt before XFER begins.
524A
LD A,(5358H) 3A 58 53
Fetch the drive number (stored at 5358H by the self-modifying instruction at 5233H) into Register A. This is the drive the user specified (or 0 by default).
524D
LD C,A 4F
Load Register C with Register A (the drive number). Register C is the standard calling convention for passing the drive number to the disk I/O routines.
524E
GOSUB to 53DDH, the GAT sector read routine. With C = drive number, this reads the GAT (Granule Allocation Table) sector from track 0, sector 0 of the source diskette into the sector buffer at 4200H. Returns Z on success, NZ with A=14H on read error.
5251
If the NZ FLAG (Not Zero) has been set - meaning the GAT sector read failed - JUMP to 534FH to execute the error exit path. Register A contains the read error code (14H).
5254
LD HL,42CEH 21 CE 42
Point Register Pair HL to 42CEH, which is address 4200H (the start of the GAT sector buffer) plus offset CEH. Offset CEH within the GAT sector holds the 18-byte disk identification signature used to uniquely identify this diskette.
5257
LD DE,550EH 11 0E 55
Point Register Pair DE to 550EH, the Source Disk Signature Buffer (18 bytes). This is where XFER saves the source disk's identity so it can verify the correct disk is reinserted after swapping.
525A
LD BC,0012H 01 12 00
Load Register Pair BC with 0012H (18 decimal). This is the number of bytes to copy - the full 18-byte disk signature at GAT offset CEH.
525D
LDIR ED B0
Block Move: copy 18 bytes (BC=0012H) from the source disk signature at 42CEH (HL, within the GAT buffer) to the source signature save area at 550EH (DE), incrementing both pointers. The source disk identity is now preserved for later comparison.

525FH - Open Source File

The source disk is verified in the drive. XFER now opens the source file using the FCB at 54CEH.

525F
LD DE,54CEH 11 CE 54
Point Register Pair DE to 54CEH, the Source FCB. This is the parameter for the basic file open call - DE must point to a valid FCB on entry to 4424H.
5262
LD HL,5600H 21 00 56
Point Register Pair HL to 5600H, the base address of the in-memory sector transfer buffer. The file open routine uses HL as the buffer address for the first sector read.
5265
LD B,00H 06 00
Load Register B with 00H. The B register passes the file-open mode to 4424H; 00H selects the default open-for-read mode.
5267
GOSUB to SYS0 routine at 4424H to open the source file. With DE = Source FCB (54CEH) and HL = buffer base (5600H) and B = 00H, this routine locates the file on the source diskette, validates the FCB, and sets up the internal state for subsequent read-record calls. Returns Z on success, NZ with an error code in A on failure.
526A
If the NZ FLAG (Not Zero) has been set - meaning the file open failed (file not found, bad FCB, etc.) - JUMP to 534FH to execute the error exit path with the error code in Register A.

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.

526D
LD HL,54AAH 21 AA 54
Point Register Pair HL to 54AAH, the address of the "INSERT DESTINATION DISK (ENTER)" prompt string. This is loaded before the wait call so the display-and-wait routine at 53A7H has the correct prompt text.
5270
GOSUB to 53A7H, the display-and-wait-for-ENTER routine. Displays the "INSERT DESTINATION DISK (ENTER)" message and waits for the user to insert the destination diskette and press ENTER.
5273
LD A,(5358H) 3A 58 53
Fetch the drive number (stored at 5358H) into Register A. The destination disk is in the same physical drive as the source disk - this is the single-drive scenario XFER is designed for.
5276
LD C,A 4F
Load Register C with Register A (the drive number) in preparation for the GAT sector read call.
5277
GOSUB to 53DDH to read the destination disk's GAT sector into 4200H. Returns Z on success, NZ with A=14H on read error.
527A
If the NZ FLAG (Not Zero) has been set - meaning the destination GAT read failed - JUMP to 534FH to execute the error exit path.
527D
LD HL,42CEH 21 CE 42
Point Register Pair HL to 42CEH (GAT buffer at 4200H plus offset CEH) to access the 18-byte disk identity signature of the destination disk.
5280
LD DE,5520H 11 20 55
Point Register Pair DE to 5520H, the Destination Disk Signature Buffer (18 bytes). XFER saves the destination disk's signature here so it can verify the correct disk is reinserted during subsequent destination-disk swap prompts.
5283
LD BC,0012H 01 12 00
Load Register Pair BC with 0012H (18 decimal), the byte count for the signature copy.
5286
LDIR ED B0
Block Move: copy 18 bytes from the destination disk signature at 42CEH (HL) to the destination signature save area at 5520H (DE). The destination disk identity is now preserved.

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.

5288
LD HL,550EH 21 0E 55
Point Register Pair HL to 550EH, the Source Disk Signature Buffer (18 bytes saved at 5257H-525DH). This is the first operand for the comparison.
528B
LD DE,5520H 11 20 55
Point Register Pair DE to 5520H, the Destination Disk Signature Buffer just saved by the LDIR above. This is the second operand for the comparison.
528E
LD B,12H 06 12
Load Register B with 12H (18 decimal), the byte count for the signature comparison loop.
5290
GOSUB to 539EH, the 18-byte memory compare routine. Compares the source signature at 550EH (HL) against the destination signature at 5520H (DE) byte by byte for B=18H bytes. Returns Z if all bytes match (same disk), NZ if any byte differs (different disks).
5293
If the NZ FLAG (Not Zero) has been set - meaning the signatures differ and the source and destination are different diskettes - JUMP to 52A1H to proceed with the transfer. This is the expected path in normal operation.

If we reach here, the source and destination signatures matched - the same disk was inserted for both. XFER displays an error and aborts.

5295
GOSUB to 5357H, the System Disk Swap routine. Before displaying the error message, XFER needs the system disk in the drive so the display routines in SYS0 are accessible. This prompts "INSERT SYSTEM DISK (ENTER)" and waits.
5298
LD HL,5442H 21 42 54
Point Register Pair HL to 5442H, the address of the "SOURCE AND DESTINATION DISKS ARE THE SAME" error string (42 bytes ending with 0DH at 546BH).
529B
GOSUB to SYS0 routine at 447BH to display the error message at 5442H (HL). This routine handles CONFIG/SYS-compatible display processing, writing the string to the screen.
529E
JUMP to SYS0 routine at 4030H, the Error-Already-Displayed Exit. Since the error message has already been printed to the screen, XFER uses this exit path (rather than the standard DOS error exit) to return to the VTOS command prompt without printing an additional error line.

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.

52A1
LD DE,54EEH 11 EE 54
Point Register Pair DE to 54EEH, the Destination FCB. This FCB will be used for all write-record calls. It is a clone of the source FCB (copied by the LDIR at 523FH) and describes the file to be created on the destination disk.
52A4
LD HL,5600H 21 00 56
Point Register Pair HL to 5600H, the base address of the in-memory transfer buffer. Records read from the source file will be placed here, one 256-byte page per record, filling upward through memory until the page limit (patched into 52D4H) is reached.
52A7
LD B,00H 06 00
Load Register B with 00H, the read mode parameter for the 4420H read-record call (00H = read next record sequentially).
52A9
GOSUB to SYS0 routine at 4420H to read one record from the source file. With DE = Source FCB at 54CEH (note: DE was set before the destination FCB setup; the source FCB handle is maintained internally), B=00H, and the buffer implied by the current FCB state, this reads the next sector of the source file into the transfer buffer. Returns Z on success, NZ with error code in A on failure or end-of-file.
52AC
If the NZ FLAG (Not Zero) has been set - meaning a read error or end-of-file condition was returned - JUMP to 534FH for the error exit path. This handles hard I/O errors. (End-of-file is handled via the write loop at 52C4H-52CEH with error codes 1CH/1DH.)
52AF
GOSUB to 5357H, the System Disk Swap routine (no-op if already on system disk, since 5357H tests A=00H via OR A / RET NZ). At this point in the read phase, A reflects the status from the 4420H call; the call here is a placeholder to ensure the system disk is accessible if needed before proceeding to the write phase. In practice this returns immediately because A is 00H (success).

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.

52B2
GOSUB to 53D7H, the SVC 8AH stub, to invoke overlay SVC function 8AH. This selects or mounts the destination drive context in preparation for writing.
52B5
GOSUB to 5364H, the Source Disk Verify Loop. This prompts "INSERT SOURCE DISK (ENTER)" and loops, reading the GAT and comparing its 18-byte signature at 42CEH against the saved source signature at 550EH, until the correct source disk is confirmed in the drive. Returns with the source disk verified and ready.
52B8
LD HL,5600H 21 00 56
Point Register Pair HL to 5600H, the base address of the transfer buffer, ready to begin the write phase from the start of the buffered data.
52BB
LD (54D1H),HL 22 D1 54
Self-Modifying Code
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.
52BE
LD DE,54CEH 11 CE 54
Point Register Pair DE to 54CEH, the Source FCB. The read-record call at 4436H (read record from open file) requires DE = FCB pointer.

52C1H - Read Record Into Buffer (Inner Read Loop)

52C1
Loop Start
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.
52C4
If the Z FLAG (Zero) has been set - meaning the record was read successfully - JUMP to 52D1H to advance the buffer page pointer and check whether the buffer is full.
52C6
CP A,1CH FE 1C
Compare Register A against 1CH. Error code 1CH is the VTOS end-of-file code indicating the last record of the last extent has been read. If Register A equals 1CH, the Z FLAG is set.
52C8
If the Z FLAG (Zero) has been set - meaning error code is 1CH (end of file, last extent) - JUMP to 530EH to handle end-of-file: flush the remaining buffered data to the destination and close the destination file.
52CA
CP A,1DH FE 1D
Compare Register A against 1DH. Error code 1DH is the VTOS end-of-extent code indicating the current extent has ended but more extents (and more data) may follow. If Register A equals 1DH, the Z FLAG is set.
52CC
If the Z FLAG (Zero) has been set - meaning error code is 1DH (end of current extent, more data follows) - JUMP to 530EH to flush the current buffer to the destination and continue the transfer. Both 1CH and 1DH terminate the current read batch.
52CE
If execution reaches here, the error code in A is neither 1CH nor 1DH - it is a genuine I/O error. JUMP to 534FH for the error exit path.

52D1H - Buffer Page Advance and Overflow Check (Read Loop)

52D1
INC H 24
INCrement Register H by 1, advancing the buffer pointer by one full 256-byte page. Each record read from the source occupies exactly one page in the in-memory buffer. H tracks the current page; L is always 00H since pages are aligned on 256-byte boundaries.
52D2
LD A,H 7C
Load Register A with H (the current page number of the buffer pointer, after the INC H).
52D3
CP A,60H FE 60
Compare Register A against 60H. Note: the 60H at address 52D4H is a placeholder - at runtime, the byte at 52D4H was patched by the self-modifying instruction at 5217H to contain the computed maximum buffer page (derived from the RAM top address at 4049H). When H reaches this limit, the buffer is full and must be flushed to the destination disk before reading more.
52D5
If the NZ FLAG (Not Zero) has been set - meaning the buffer page has not yet reached the maximum - LOOP BACK to 52BBH to save the current buffer pointer and read the next record. The read loop continues until the buffer fills or end-of-file is reached.

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.

52D7
GOSUB to 5381H, the Destination Disk Verify Loop. Prompts "INSERT DESTINATION DISK (ENTER)" and loops until the correct destination disk is confirmed in the drive (comparing GAT signature at 42CEH against saved snapshot at 5520H).
52DA
LD HL,5600H 21 00 56
Point Register Pair HL to 5600H, the start of the buffer, to begin the write-flush sequence from the first buffered record.
52DD
LD (54F1H),HL 22 F1 54
Self-Modifying Code
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.
52E0
LD DE,54EEH 11 EE 54
Point Register Pair DE to 54EEH, the Destination FCB, in preparation for the write-record call.

52E3H - Write Record from Buffer (Inner Write Loop)

52E3
Loop Start
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.
52E6
If the NZ FLAG (Not Zero) has been set - meaning the write failed - JUMP to 534FH for the error exit path.
52E8
INC H 24
INCrement Register H by 1, advancing the write buffer pointer to the next 256-byte page. Each write call consumes exactly one page from the buffer.
52E9
LD A,H 7C
Load Register A with H (the updated write buffer page number).
52EA
CP A,60H FE 60
Compare Register A against 60H. Note: the 60H at address 52EBH is a placeholder patched at runtime (by 521AH) to the same maximum buffer page value used by the read loop. When H reaches the limit, all buffered records have been written out.
52EC
If the NZ FLAG (Not Zero) has been set - meaning the write pointer has not yet reached the end of the buffered data - LOOP BACK to 52DDH to advance the write pointer and write the next record.

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.

52EE
LOOP BACK to 52B5H to begin the next read batch: verify the source disk is in the drive (5364H), reset the buffer base to 5600H, and read the next group of sectors. This read/write cycle repeats until end-of-file (1CH or 1DH) is encountered in the read loop.

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.

52F0
GOSUB to 5333H, the Conditional Destination Disk Swap routine. This checks whether the destination disk is already in the drive (by examining H against the 5600H base page). If not already on the destination disk, it calls 5381H (Destination Disk Verify Loop) to prompt for the destination disk, then writes all remaining buffered records via 4439H in a write loop before returning.
52F3
GOSUB to 5357H, the System Disk Swap routine. Before closing the destination file, the system disk must be present so SYS0's close routine (4428H) can be called. Prompts "INSERT SYSTEM DISK (ENTER)" and waits for the user if a swap is needed.
52F6
GOSUB to 53DAH, the SVC 85H stub, invoking overlay SVC function 85H to mount the destination drive context for the close operation.
52F9
LD A,(5358H) 3A 58 53
Fetch the drive number (stored at 5358H) into Register A.
52FC
OR A,A B7
OR Register A with itself. This tests whether the drive number is zero without affecting the value. Sets the Z FLAG if drive number = 0, clears it otherwise. The next instruction conditionally calls the destination verify loop only for drive 0 (the single-drive scenario).
52FD
If the Z FLAG (Zero) has been set - meaning the drive number is 0 (single-drive system) - GOSUB to 5381H to verify the destination disk is in the drive before closing. On a multi-drive system (drive != 0), the destination is in its own drive and no swap is needed.
5300
LD DE,54EEH 11 EE 54
Point Register Pair DE to 54EEH, the Destination FCB, in preparation for the file close call.
5303
GOSUB to SYS0 routine at 4428H to close the destination file (DE = Destination FCB at 54EEH). This flushes any pending writes, updates the directory entry with the final file size and date, deallocates any unused granules, and marks the file as properly closed.
5306
If the NZ FLAG (Not Zero) has been set - meaning the close operation failed - JUMP to 534FH for the error exit path.
5308
GOSUB to 5357H, the System Disk Swap routine. After closing the destination file, XFER swaps back to the system disk so control can be returned to the VTOS command interpreter cleanly.
530B
JUMP to SYS0 at 402DH, the DOS READY / No-Error Exit. The file transfer completed successfully. Control returns to the VTOS command interpreter, which will display the "VTOS READY" prompt.

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).

530E
GOSUB to 5333H, the Conditional Destination Disk Swap and Buffer Flush routine. Checks whether destination is already current; if not, prompts for destination disk via 5381H and writes all remaining buffered records (from HL to the page limit) to the destination file via 4439H before returning.
5311
LD HL,(54D6H) 2A D6 54
Load Register Pair HL with the 16-bit value stored at 54D6H. Address 54D6H holds the saved read-loop buffer pointer (the current HL value from when end-of-extent was detected). This restores the exact position in the buffer from which reading must resume after the disk swap and write flush.
5314
LD (54F6H),HL 22 F6 54
Self-Modifying Code
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.
5317
GOSUB to 5357H to swap the system disk back into the drive after the destination write phase.
531A
GOSUB to 53DAH, the SVC 85H stub, to mount the destination drive context.
531D
LD A,(5358H) 3A 58 53
Fetch the drive number (stored at 5358H) into Register A.
5320
OR A,A B7
OR Register A with itself to test whether the drive number is zero (single-drive system). Sets Z if drive = 0.
5321
If the Z FLAG (Zero) has been set - drive 0, single-drive system - GOSUB to 5381H to verify the destination disk is present before closing.
5324
LD DE,54EEH 11 EE 54
Point Register Pair DE to 54EEH, the Destination FCB, for the file close call.
5327
GOSUB to SYS0 at 4428H to close the destination file. Flushes remaining data, updates the directory, and marks the file closed.
532A
If the NZ FLAG (Not Zero) has been set - close failed - JUMP to 534FH for the error exit path.
532D
GOSUB to 5357H to swap the system disk back into the drive after the destination close operation.
5330
JUMP to SYS0 at 402DH, the DOS READY / No-Error Exit. The transfer completed successfully even via the extent handler path. Control returns to the VTOS command interpreter.

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.

5333
LD A,H 7C
Load Register A with H, the high byte of the current buffer pointer (HL). H encodes the current buffer page number. If H = 56H, the buffer pointer is still at 5600H (the very start), meaning no records have been buffered since the last write flush - a destination swap is not needed.
5334
CP A,56H FE 56
Compare Register A against 56H (the base page of the sector buffer at 5600H). If Register A equals 56H, the Z FLAG is set - the buffer is empty and no write flush is needed.
5336
RET Z C8
If the Z FLAG (Zero) has been set - meaning H = 56H, the buffer is empty - RETurn immediately. There is nothing to write to the destination disk, so no disk swap is needed.
5337
GOSUB to 5381H, the Destination Disk Verify Loop. The buffer is not empty, so the destination disk must be in the drive before writing. This prompts "INSERT DESTINATION DISK (ENTER)" and loops until the correct disk is confirmed.
533A
LD B,H 44
Load Register B with Register H (the current buffer page limit - the page at which reading stopped). Register B now serves as the upper boundary counter for the write loop: writes continue until H reaches this page value.
533B
LD HL,5600H 21 00 56
Point Register Pair HL to 5600H, the start of the sector buffer. The write flush begins from the first buffered record.

533EH - Buffer Flush Write Loop

533E
LD (54F1H),HL 22 F1 54
Self-Modifying Code
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.
5341
LD DE,54EEH 11 EE 54
Point Register Pair DE to 54EEH, the Destination FCB, for the write-record call.
5344
GOSUB to SYS0 at 4439H to write one record from the current buffer position (HL, via the FCB) to the destination file. Returns Z on success, NZ on error.
5347
If the NZ FLAG (Not Zero) has been set - write failed - JUMP to 534FH for the error exit path.
5349
INC H 24
INCrement Register H by 1, advancing the write buffer pointer to the next 256-byte page.
534A
LD A,H 7C
Load Register A with H (the updated write page number).
534B
CP A,B B8
Compare Register A against Register B (the upper page limit saved at 533AH). If Register A equals B, the Z FLAG is set, meaning all buffered records have been written.
534C
If the NZ FLAG (Not Zero) has been set - more records remain in the buffer - LOOP BACK to 533EH to write the next record. The loop continues until H = B (all buffered records flushed).
534E
RET C9
Loop End
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.

534F
PUSH AF F5
Save Register Pair AF (A = error code, F = flags) onto the stack. The error code must be preserved across the system disk swap call, which may destroy A.
5350
GOSUB to 5357H, the System Disk Swap routine. Before reporting the error via 4409H, the system disk must be present in the drive so SYS0's error-handling routines are accessible.
5353
POP AF F1
Restore Register Pair AF from the stack. Register A now holds the original error code that triggered the error exit.
5354
JUMP to 53F0H to OR the error code with 40H (bit 6, which suppresses extended error context display) and then jump to 4409H (DOS Error Exit).

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.

5357
LD A,00H 3E 00
Load Register A with 00H. This explicit load ensures that the subsequent OR A / RET NZ test always proceeds to the swap prompt. (The static byte 00H here means the skip condition can never be triggered from this entry point; the conditional-skip design would apply if callers could set A non-zero before the call.)
5359
OR A,A B7
OR Register A with itself. Tests whether A = 00H. Sets the Z FLAG if A is zero (proceed with swap prompt); sets the NZ FLAG if A is non-zero (skip the prompt).
535A
RET NZ C0
If the NZ FLAG (Not Zero) has been set - A is non-zero, system disk is already present - RETurn immediately without prompting. The swap is not needed.
535B
PUSH HL E5
Save Register Pair HL onto the stack. HL may be needed by the caller after the disk swap and must be preserved across the display-and-wait call.
535C
LD HL,546CH 21 6C 54
Point Register Pair HL to 546CH, the address of the "INSERT SYSTEM DISK (ENTER)" prompt string. This string begins with 1DH + 1EH (clear-screen control sequences) followed by the message text, ending with 1DH + 03H.
535F
GOSUB to 53A7H, the display-and-wait-for-ENTER routine. Displays the "INSERT SYSTEM DISK (ENTER)" prompt and waits for the user to insert the system disk and press ENTER.
5362
POP HL E1
Restore Register Pair HL from the stack. The caller's HL value is recovered after the disk swap prompt.
5363
RET C9
Return to caller. The system disk swap is complete.

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.

5364
PUSH HL E5
Loop Start
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.
5365
LD HL,548BH 21 8B 54
Point Register Pair HL to 548BH, the address of the "INSERT SOURCE DISK (ENTER)" prompt string.
5368
GOSUB to 53A7H, the display-and-wait-for-ENTER routine. Displays the "INSERT SOURCE DISK (ENTER)" message and waits for the user to insert a disk and press ENTER.
536B
LD A,(5358H) 3A 58 53
Fetch the drive number (stored at 5358H) into Register A.
536E
LD C,A 4F
Load Register C with Register A (the drive number) for the GAT read call.
536F
GOSUB to 53DDH to read the GAT sector of the currently inserted disk into 4200H. Returns Z on success, NZ on read error. If the disk is not readable (wrong disk, no disk, or unformatted), the read will fail.
5372
LD HL,42CEH 21 CE 42
Point Register Pair HL to 42CEH (GAT buffer + offset CEH = the 18-byte disk identity signature of the currently inserted disk).
5375
LD DE,550EH 11 0E 55
Point Register Pair DE to 550EH, the Source Disk Signature Buffer containing the 18-byte signature of the correct source diskette (saved at 5254H-525DH during initialization).
5378
LD B,12H 06 12
Load Register B with 12H (18 decimal) as the byte count for the comparison.
537A
GOSUB to 539EH, the 18-byte memory compare routine. Compares the GAT signature of the currently inserted disk (at 42CEH, via HL) against the saved source disk signature (at 550EH, via DE). Returns Z if they match (correct disk), NZ if they differ (wrong disk).
537D
POP HL E1
Restore Register Pair HL from the stack (recovering the caller's context).
537E
If the NZ FLAG (Not Zero) has been set - the signatures do not match, wrong disk is in the drive - LOOP BACK to 5364H to prompt again. The loop repeats until the user inserts the correct source disk.
5380
RET C9
Loop End
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.

5381
PUSH HL E5
Loop Start
Save Register Pair HL onto the stack.
5382
LD HL,54AAH 21 AA 54
Point Register Pair HL to 54AAH, the address of the "INSERT DESTINATION DISK (ENTER)" prompt string.
5385
GOSUB to 53A7H, the display-and-wait-for-ENTER routine. Displays the "INSERT DESTINATION DISK (ENTER)" message and waits.
5388
LD A,(5358H) 3A 58 53
Fetch the drive number (stored at 5358H) into Register A.
538B
LD C,A 4F
Load Register C with Register A (drive number) for the GAT read call.
538C
GOSUB to 53DDH to read the GAT sector of the currently inserted disk into 4200H.
538F
LD HL,42CEH 21 CE 42
Point Register Pair HL to 42CEH (GAT buffer + offset CEH = the 18-byte disk signature of the currently inserted disk).
5392
LD DE,5520H 11 20 55
Point Register Pair DE to 5520H, the Destination Disk Signature Buffer containing the 18-byte signature of the correct destination diskette (saved at 5280H-5286H during initialization).
5395
LD B,12H 06 12
Load Register B with 12H (18 decimal) as the byte count for the comparison.
5397
GOSUB to 539EH, the 18-byte memory compare routine. Compares the current disk's GAT signature against the saved destination signature. Returns Z if match (correct disk), NZ if mismatch (wrong disk).
539A
POP HL E1
Restore Register Pair HL from the stack.
539B
If the NZ FLAG (Not Zero) has been set - wrong disk - LOOP BACK to 5381H to prompt again.
539D
RET C9
Loop End
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.

539E
LD A,(DE) 1A
Loop Start
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).
539F
CP A,(HL) BE
Compare Register A (byte from DE) against the byte at (HL) (the other signature buffer). If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set.
53A0
RET NZ C0
If the NZ FLAG (Not Zero) has been set - the bytes differ, disks do not match - RETurn immediately with NZ set. The caller will use this to re-prompt for the correct disk.
53A1
INC HL 23
INCrement Register Pair HL by 1 to advance to the next byte of the expected signature buffer.
53A2
INC DE 13
INCrement Register Pair DE by 1 to advance to the next byte of the actual signature buffer.
53A3
DECrement B and loop back to 539EH if not zero. The comparison continues for all B bytes. When B reaches zero, all bytes have been compared without a mismatch.
53A5
XOR A,A AF
Set Register A to ZERO and clear all flags. This explicitly sets the Z FLAG to indicate a successful (all-bytes-matched) comparison result, since DJNZ exits with BC=0 but does not guarantee Z is set from the last comparison.
53A6
RET C9
Loop End
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.

53A7
GOSUB to SYS0 routine at 4467H to display the message at (HL) on the screen. The message string pointed to by HL (548BH, 54AAH, or 546CH depending on caller) is output to the display. The string begins with 1DH + 1EH (home + clear) control codes followed by the prompt text.

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

53AA
LD A,(3840H) 3A 40 38
Loop Start
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).
53AD
RRCA 0F
Rotate Register A Right (circular). Bit 0 of the keyboard row (the rightmost key bit) is shifted into the CARRY FLAG. On the Model I, the BREAK key bit is in the low position of this row; a 0 bit means the key is pressed (active low), a 1 bit means released.
53AE
If the CARRY FLAG has been set - meaning bit 0 was 1 (key is pressed/active) - LOOP BACK to 53AAH to keep polling. The loop continues until the currently-pressed key is released and bit 0 becomes 0.

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.

53B0
LD BC,4000H 01 00 40
Load Register Pair BC with 4000H (16384 decimal) as the timeout countdown value. This represents the maximum number of polling iterations before the prompt is re-displayed.

53B3H - Keypress Poll Loop with Timeout

53B3
LD A,(3840H) 3A 40 38
Loop Start
Fetch the keyboard status byte from 3840H again. Polling for a newly pressed key.
53B6
RRCA 0F
Rotate Register A Right. Shifts bit 0 (ENTER/BREAK key state) into CARRY.
53B7
If the CARRY FLAG has been set - meaning bit 0 is 1 and a key is pressed - JUMP to 53CBH to handle the keypress (call 0049H keyboard scan, then clear screen and return).
53B9
DEC BC 0B
DECrement Register Pair BC by 1, counting down the timeout. Each iteration of the poll loop represents one elapsed unit of waiting time.
53BA
LD A,B 78
Load Register A with Register B (the high byte of the countdown).
53BB
OR A,C B1
OR Register A with Register C (the low byte of the countdown). If both B and C are zero, the result is zero and the Z FLAG is set, indicating timeout. If any bits remain, NZ is set and polling continues.
53BC
If the NZ FLAG (Not Zero) has been set - timeout has not expired - LOOP BACK to 53B3H to poll again. The loop continues until a key is pressed or BC reaches zero.

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.

53BE
LD A,1EH 3E 1E
Load Register A with 1EH, the clear-screen control code for the TRS-80 Model I (when sent to the ROM display routine, 1EH homes the cursor and clears the video RAM).
53C0
CALL 0033H CD 33 00
GOSUB to ROM routine at 0033H to display the character in Register A (1EH = clear screen). This clears the video display.
53C3
LD BC,3333H 01 33 33
Load Register Pair BC with 3333H (13107 decimal) as the delay count for the brief pause between clearing the screen and re-displaying the prompt. This creates a visible blank-screen interval that makes the prompt appear to pulse.
53C6
CALL 0060H CD 60 00
GOSUB to ROM routine at 0060H with BC=3333H to execute a software delay loop. The routine counts down BC to zero, consuming approximately (BC x cycle time) of elapsed time as a blank-screen pause.
53C9
LOOP BACK to 53A7H to re-display the prompt and restart the wait cycle. The prompt will flash on screen, pause, then disappear and reappear repeatedly until the user inserts the disk and presses ENTER.

53CBH - Keypress Detected: Clear Screen and Return

53CB
CALL 0049H CD 49 00
GOSUB to ROM routine at 0049H to scan the keyboard (non-blocking). This call consumes the keypress, clearing the keyboard state so it does not carry over to subsequent input operations.
53CE
LD A,1EH 3E 1E
Load Register A with 1EH (clear-screen control code).
53D0
CALL 0033H CD 33 00
GOSUB to ROM routine at 0033H to clear the screen (display 1EH). After the prompt is dismissed, the screen is cleared to give the user a clean display.
53D3
RET C9
Return to caller. The user has pressed ENTER (or any key), the screen has been cleared, and the appropriate disk is presumed to be in the drive for verification.

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.

53D4
LD A,84H 3E 84
Load Register A with 84H, the SVC code for overlay function 84H (mount/select source drive). Entry point for the SVC 84H dispatch stub.
53D6
RST 28H EF
Issue RST 28H (opcode EFH) to invoke the VTOS overlay SVC dispatcher at 400CH with A=84H. The dispatcher will call the SVC 84H handler to mount or select the source drive context, then return here.
53D7
LD A,8AH 3E 8A
Load Register A with 8AH, the SVC code for overlay function 8AH. Entry point for the SVC 8AH dispatch stub. (Falls through from 53D4H if the SVC 84H call returns normally.)
53D9
RST 28H EF
Issue RST 28H with A=8AH to invoke overlay SVC function 8AH.
53DA
LD A,85H 3E 85
Load Register A with 85H, the SVC code for overlay function 85H (mount/select destination drive). Entry point for the SVC 85H dispatch stub.
53DC
RST 28H EF
Issue RST 28H with A=85H to invoke the SVC 85H handler. Returns to caller after the destination drive context is established.

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.

53DD
PUSH DE D5
Save Register Pair DE onto the stack. DE may contain important state from the caller (FCB pointer, etc.) that must be preserved across the sector read.
53DE
PUSH HL E5
Save Register Pair HL onto the stack. HL also carries caller state that must survive the sector read operation.
53DF
GOSUB to SYS0 routine at 4B65H to set up the track/sector registers for directory I/O. With C = drive number (loaded by caller before the CALL), this routine configures the FDC to access the correct drive and prepares the track/sector addressing for the GAT sector (track 0, sector 0).
53E2
LD E,00H 1E 00
Load Register E with 00H, specifying sector 0 (the GAT sector). The GAT is always at track 0, sector 0 on VTOS-formatted diskettes.
53E4
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H, the standard sector buffer address. The GAT sector will be read into this 256-byte buffer.
53E7
GOSUB to SYS0 routine at 4B45H to read one sector from disk into the buffer at 4200H (HL), sector E=00H (GAT), using the drive configuration set by 4B65H. Returns Z on success, NZ on read error.
53EA
POP HL E1
Restore Register Pair HL from the stack.
53EB
POP DE D1
Restore Register Pair DE from the stack.
53EC
RET Z C8
If the Z FLAG (Zero) has been set - meaning the GAT sector read succeeded - RETurn immediately with Z set. The caller can now access the signature at 42CEH.
53ED
LD A,14H 3E 14
Load Register A with 14H, the VTOS error code for "Read Error" (disk sector read failure). This is the error code returned to the caller when the GAT cannot be read.
53EF
RET C9
Return with NZ set and A=14H (Read Error). The caller will either jump to 534FH (error exit) or loop for a retry (verify loops at 5364H/5381H, which do not check the return value and simply re-prompt).

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.

53F0
OR A,40H F6 40
OR Register A (the raw error code) with 40H. Setting bit 6 of the error code instructs the VTOS error display handler (SYS4) to suppress the extended error context - specifically, the "IN FILE: name" and "REFERENCED AT X'nnnn'" lines that would otherwise be appended to the error message. This is appropriate for XFER since the error context is the disk itself, not a specific FCB or call site.
53F2
JUMP to SYS0 routine at 4409H, the DOS Error Exit. With the augmented error code in Register A, this routine invokes SYS4 to display the appropriate VTOS error message (from the 42-entry dictionary at 4F89H-51DDH in the SYS4 overlay), then returns to the VTOS command prompt.

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.

53F5
LD HL,5407H 21 07 54
Point Register Pair HL to 5407H, the address of the "FILE SPEC REQUIRED" message string (19 bytes including the terminating 0DH at 5419H).
53F8
GOSUB to SYS0 routine at 447BH to display the "FILE SPEC REQUIRED" message at 5407H (HL) on the screen.
53FB
JUMP to SYS0 at 4030H, the Error-Already-Displayed Exit. Returns to the VTOS command prompt without triggering SYS4's error message display (the message was already printed by 447BH).

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.

53FE
LD HL,541AH 21 1A 54
Point Register Pair HL to 541AH, the address of the "INVALID COMMAND DURING PROGRAM CHAINING" message string (40 bytes including the terminating 0DH at 5441H).
5401
GOSUB to SYS0 at 447BH to display the chaining error message at 541AH (HL).
5404
JUMP to SYS0 at 4030H, the Error-Already-Displayed Exit. Returns to the VTOS command prompt.

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.

5407-5419
DEFM "FILE SPEC REQUIRED",0DH 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
ASCII string "FILE SPEC REQUIRED" followed by 0DH (carriage return terminator). Displayed by 53F5H when no filespec was provided on the XFER command line. 19 bytes total.
541A-5441
DEFM "INVALID COMMAND DURING PROGRAM CHAINING",0DH 49 4E 56 41 4C 49 44 20 43 4F 4D 4D 41 4E 44 20 44 55 52 49 4E 47 20 50 52 4F 47 52 41 4D 20 43 48 41 49 4E 49 4E 47 0D
ASCII string "INVALID COMMAND DURING PROGRAM CHAINING" followed by 0DH. Displayed by 53FEH when XFER is invoked during an active program chain. 40 bytes total.
5442-546B
DEFM "SOURCE AND DESTINATION DISKS ARE THE SAME",0DH 53 4F 55 52 43 45 20 41 4E 44 20 44 45 53 54 49 4E 41 54 49 4F 4E 20 44 49 53 4B 53 20 41 52 45 20 54 48 45 20 53 41 4D 45 0D
ASCII string "SOURCE AND DESTINATION DISKS ARE THE SAME" followed by 0DH. Displayed by 529BH when the source and destination GAT signatures match, indicating the same disk was inserted for both. 42 bytes total.
546C-548A
DEFM 1DH,1EH,"INSERT SYSTEM DISK (ENTER)",1DH,03H 1D 1E 49 4E 53 45 52 54 20 53 59 53 54 45 4D 20 44 49 53 4B 20 20 28 45 4E 54 45 52 29 1D 03
System disk swap prompt string. Begins with 1DH (cursor home) + 1EH (clear screen) control codes, followed by the text "INSERT SYSTEM DISK (ENTER)" (note two spaces before the parenthesis), then 1DH + 03H as a display terminator sequence. Displayed by 535CH via 53A7H. 31 bytes total.
548B-54A9
DEFM 1DH,1EH,"INSERT SOURCE DISK (ENTER)",1DH,03H 1D 1E 49 4E 53 45 52 54 20 53 4F 55 52 43 45 20 44 49 53 4B 20 20 28 45 4E 54 45 52 29 1D 03
Source disk swap prompt string. Same structure as the system disk prompt but with "SOURCE" in place of "SYSTEM". Displayed by 5365H and 5247H via 53A7H. 31 bytes total.
54AA-54CD
DEFM 1DH,1EH,"INSERT DESTINATION DISK (ENTER)",1DH,03H 1D 1E 49 4E 53 45 52 54 20 44 45 53 54 49 4E 41 54 49 4F 4E 20 44 49 53 4B 20 20 28 45 4E 54 45 52 29 1D 03
Destination disk swap prompt string. "DESTINATION" in place of "SOURCE". Displayed by 5382H and 526DH-5270H via 53A7H. 36 bytes total (longer due to "DESTINATION" vs "SOURCE").

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:

IDAMCommandEntry PointMember
81HLOAD5200H3AH
82HRUN520DH3AH

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

AddressPurpose
5200HLOAD entry point (IDAM 81H)
520DHRUN entry point (IDAM 82H)
5216HCommon setup - extract filespec, check for wildcard, set default extension
5245HCore load loop - poll for disk ready, call loader, retry on timeout
526FHIssue SVC 84H to load overlay (open file / prepare load)
5272HError exit - "FILE SPEC REQUIRED" (missing or wildcard filespec)
527BHError exit - "PARAMETER ERROR" (bad option / open failure)
52F2HLOAD completion - call 4430H (load only), check (X) flag, exit
5300HRUN completion - call 4430H, push HL, fall through to check (X) flag and execute

Memory Locations Referenced

AddressPurpose
3840HDisk status port (read). Bit 1 = disk motor/drive-ready flag (RRCA shifts bit 1 into carry).
402DHDOS READY / No-Error Exit stub in resident SYS0.
4030HError-already-displayed exit in resident SYS0.
4430HSYS0 Load Program File routine (load only, no execute).
441CHSYS0 Extract Filespec from command line into DCB/work area.
4467HSYS0 Display message to screen (HL = message address).
4473HSYS0 Insert default extension into filespec work area.
4476HSYS0 Open file utility (DE = filespec template, returns Z on success).
447BHSYS0 Display string at (HL) with CONFIG/SYS processing.
5239HRuntime flag byte: non-zero = (X) option was specified by user.
52C6H-52CEHFilespec work area template ("X 9R"+00H, 9 bytes).
52CFH-52D1HDefault extension string: "CMD" (3 bytes, no terminator - used by 4473H).
52D2H-52F1HFCB data area (32 bytes) used by the file open and load operations.
5284H-5296HError message: "FILE SPEC REQUIRED" + 0DH.
5297H-52A6HError message: "PARAMETER ERROR" + 0DH.
52A7H-52C5HDisk swap prompt: 1DH+1EH+"INSERT SOURCE DISK (ENTER)"+1DH+03H.
5306H-5324HDisk 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.

5200H
CALL 5216H CD 16 52
Call the common setup routine at 5216H. That routine extracts the filespec from the command line, validates it is not a wildcard, inserts the CMD default extension, and opens the file. On return, Z is set if the file was successfully opened; NZ means an error was already displayed and the code must exit. HL is preserved across the call.
5203H
LD DE,402DH 11 2D 40
Load DE with 402DH, the address of the DOS READY / No-Error Exit stub in resident SYS0. This is pushed onto the stack as a safety-net return address: if the loaded program simply executes a RET instruction, control will pass cleanly back to the DOS READY prompt.
5206H
PUSH DE D5
Push 402DH (the DOS READY return address loaded above) onto the stack. When the loaded program returns, the CPU pops this address and jumps to the DOS READY handler at 402DH, returning cleanly to the operating system.
5207H
LD DE,52D2H 11 D2 52
Load DE with 52D2H, the address of the FCB data area (a 32-byte block in the overlay's working memory). This FCB was populated by the common setup routine and the file open call. DE is passed to the loader routine at 52F2H as the FCB pointer.
520AH
JP 52F2H C3 F2 52
Jump to the LOAD completion path at 52F2H. That path calls SYS0 routine 4430H (load program file without executing), then checks the (X) flag, and exits to DOS READY. Execution does not fall through to 520DH.

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.

520DH
CALL 5216H CD 16 52
Call the common setup routine at 5216H. Identical to the LOAD entry: extract filespec, validate, insert default extension, open file. On error the routine has already displayed the appropriate message and the NZ return will cause this path to exit via 4030H.
5210H
LD DE,52D2H 11 D2 52
Load DE with 52D2H (the FCB data area, 32 bytes). Unlike the LOAD path, there is no PUSH of 402DH here: RUN does not place a DOS READY safety net on the stack because the loaded program is expected to be a true executable that manages its own exit.
5213H
JP 5300H C3 00 53
Jump to the RUN completion path at 5300H. That path calls SYS0 routine 4430H to load the program, then checks the (X) flag and, if the load succeeded, passes control to the program's transfer address to begin execution.

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.

5216H
LD DE,52D2H 11 D2 52
Load DE with 52D2H, the address of the 32-byte FCB data area embedded in the overlay's working memory. This FCB will be populated by the filespec extraction and file open calls that follow. DE is the FCB pointer parameter required by the SYS0 routines.
5219H
CALL 441CH CD 1C 44
Call the SYS0 Extract Filespec routine at 441CH. Scans HL, extracts filename/drive into area at 52C6H, and populates the FCB at 52D2H. Z set = no filespec found; NZ = filespec extracted. HL is advanced past the token.
521CH
JP NZ,5272H C2 72 52
If the filespec extraction at 441CH returned NZ - meaning no filespec was found on the command line - jump to the error handler at 5272H, which displays "FILE SPEC REQUIRED" and exits to 4030H.
521FH
LD A,(DE) 1A
Load A with the first byte of the FCB at 52D2H. In the VTOS FCB layout, 2AH (ASCII '*') in the first position means the user typed a wildcard, which is not permitted for LOAD or RUN.
5220H
CP A,2AH FE 2A
Compare A with 2AH ('*'). If the first byte of the FCB is 2AH, the user specified a wildcard filespec (e.g., "LOAD *"). LOAD and RUN require an exact filename.
5222H
JR Z,5272H 28 4E
If A equals 2AH (wildcard detected), jump to the "FILE SPEC REQUIRED" error path at 5272H. A wildcard is treated the same as a missing filespec for these commands.
5224H
PUSH HL E5
Save HL (command line buffer pointer past the filespec token). HL is pushed here because the following calls to 4473H and 4476H use HL for their own purposes and would destroy its value.
5225H
LD HL,52CFH 21 CF 52
Load HL with 52CFH, the address of the three-byte default extension string "CMD" stored in the overlay's data area. This pointer is passed to the SYS0 Insert Default Extension routine.
5228H
CALL 4473H CD 73 44
Call the SYS0 Insert Default Extension routine at 4473H. If the user did not supply an explicit extension, it copies "CMD" from (HL) as the default. After this call, the filespec is guaranteed to have an extension.
522BH
POP HL E1
Restore HL to its saved value: the pointer into the command line buffer at the position just after the filespec token. The next step will scan this remaining command line text for the (X) option.
522CH
LD DE,52C6H 11 C6 52
Load DE with 52C6H, the address of the completed filespec work area template. DE is the filespec template pointer required by the SYS0 Open File utility at 4476H.
522FH
CALL 4476H CD 76 44
Call the SYS0 Open File utility at 4476H. Searches for the file and populates the FCB at 52D2H with directory information. Z set = file opened successfully; NZ = file not found or open error.
5232H
JR NZ,527BH 20 47
If the file open returned NZ, jump to the error handler at 527BH, which displays "PARAMETER ERROR" and exits to 4030H. The message was already displayed by 4476H; 527BH provides context.
5234H
PUSH HL E5
Save HL again. The next call, to 526FH, will use HL internally and must not lose this value. HL must be preserved so that the (X) option check can examine the rest of the command line.
5235H
CALL 526FH CD 6F 52
Call the SVC 84H helper at 526FH. This issues RST 28H with SVC code 84H to perform overlay setup. The routine stores its result in DE on return (0000H = success/no swap).
5238H
LD DE,0000H 11 00 00
Load DE with 0000H for a 16-bit zero test. If the SVC call at 526FH returned a non-zero result indicating an error or swap condition, the test at 523CH will catch it.
523BH
LD A,D 7A
Load A with D (high byte of DE). First half of the 16-bit zero test.
523CH
OR A,E B3
OR A with E (low byte). A is zero if and only if DE = 0000H. If A is non-zero, DE was non-zero, indicating a disk swap is needed based on SVC setup.
523DH
LD HL,52A7H 21 A7 52
Load HL with 52A7H, the address of the "INSERT SOURCE DISK (ENTER)" prompt string. Used if the (X) option was specified and the system needs the source disk inserted before loading.
5240H
CALL NZ,5245H C4 45 52
If DE was non-zero (swap needed), call the disk-wait-and-load routine at 5245H. That routine displays the prompt, waits for the disk, and performs the load. If zero, this call is skipped.
5243H
POP HL E1
Restore HL (the saved command line pointer). Control returns here after the conditional call to 5245H. HL is restored for post-load steps in the entry routines.
5244H
RET C9
Return to caller (LOAD or RUN). Z flag is set if setup and file open succeeded; NZ if an error occurred.

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.

5245H
CALL 4467H CD 67 44
Call the SYS0 Display Message routine. HL points to the prompt message (Source or System disk). Displays the message on screen so the user knows to insert the correct disk.
5248H
LD A,(3840H) 3A 40 38
Loop Start: Load A with the byte from port 3840H (Model I disk status). Bit 1 (tested by RRCA) carries the disk motor / drive-ready signal.
524BH
RRCA 0F
Rotate A right one bit into carry. The original bit 1 (disk-ready flag) is now in the carry flag.
524CH
JR C,5248H 38 FA
If carry is set (drive active/busy), loop back to re-read. The code waits here until the drive is no longer reporting busy before starting the timeout counter.
524EH
LD BC,4000H 01 00 40
Load BC with 4000H (16384). This is the timeout counter for the polling loop. If the disk does not become ready within these iterations, the timeout handler triggers a prompt blink.
5251H
LD A,(3840H) 3A 40 38
Inner Loop Start: Read status port 3840H. Re-reads disk status on every iteration of the countdown, watching for the disk to become ready (bit 1 clear).
5254H
RRCA 0F
Rotate A right one bit into carry. If the disk is ready (bit 1 = 0), carry will be clear. If still busy (bit 1 = 1), carry will be set.
5255H
JR C,5269H 38 12
If carry is set (disk now ready), jump to the exit path at 5269H. That path clears the screen and returns to the caller.
5257H
DEC BC 0B
Decrement timeout counter BC. When BC reaches 0000H, the timeout has expired and the blink path at 525CH is triggered.
5258H
LD A,B 78
Load A with B. First half of 16-bit zero test for BC. DEC BC does not set Z on Z80, so zero state must be tested manually.
5259H
OR A,C B1
OR A with C. A is zero if and only if BC = 0000H (timeout reached). Z flag is set if timeout has expired.
525AH
JR NZ,5251H 20 F5
If BC is not yet zero, loop back to continue polling. The inner loop runs until the disk is ready or the countdown reaches zero.
525CH
LD A,1EH 3E 1E
Timeout Handler: Load A with 1EH (Record Separator). On the TRS-80, this is a screen clear / home-cursor command when sent to ROM 0033H.
525EH
CALL 0033H CD 33 00
Call ROM 0033H to output character 1EH. This clears the screen, erasing the previous "INSERT DISK" prompt as the first step in the blink cycle.
5261H
LD BC,3333H 01 33 33
Load BC with 3333H. Delay count for the inter-blink pause. ROM 0060H burns cycles to create a visible pause between clearing and re-displaying the prompt.
5264H
CALL 0060H CD 60 00
Call ROM 0060H delay loop. The pause makes the screen-clear and re-prompt cycle visible as a "blink" rather than a high-speed flicker.
5267H
JR 5245H 18 DC
Jump back to restart the prompt-and-wait cycle. The screen was cleared, delay elapsed, and now the prompt is re-displayed and polling restarts.

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.

5269H
LD A,1EH 3E 1E
Load A with 1EH (screen-clear code). After the disk is ready, the screen is cleared to remove the prompt before the load operation begins.
526BH
CALL 0033H CD 33 00
Call ROM 0033H to clear the screen. The disk-swap prompt is erased; the screen is now ready for the loaded program's output.
526EH
RET C9
Return to the caller. The disk is confirmed ready, the prompt cleared, and execution returns to the LOAD/RUN flow that called 5245H.

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.

526FH
LD A,84H 3E 84
Load A with 84H, the SVC function code. The RST 28H mechanism reads A to determine which overlay service to invoke.
5271H
RST 28H EF
Execute RST 28H. Invokes overlay service 84H via 400CH. On return, DE reflects the result (0000H = no swap needed; non-zero = disk swap required).

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.

5272H
LD HL,5284H 21 84 52
Load HL with address of "FILE SPEC REQUIRED" error message string (terminated by 0DH).
5275H
CALL 447BH CD 7B 44
Call the SYS0 Display String routine. Outputs the error message to the screen.
5278H
JP 4030H C3 30 40
Jump to the Error-Already-Displayed exit in SYS0. Cleans up overlay context and returns to DOS READY.

Error Exit: "PARAMETER ERROR" - 527BH

Reached when the file open at 4476H returned NZ. Displays "PARAMETER ERROR" and exits.

527BH
LD HL,5297H 21 97 52
Load HL with the address of the "PARAMETER ERROR" error message string.
527EH
CALL 447BH CD 7B 44
Call SYS0 447BH to display the message at (HL). Standard VTOS display format.
5281H
JP 4030H C3 30 40
Jump to the Error-Already-Displayed exit at 4030H. Control returns to DOS READY.

Data: "FILE SPEC REQUIRED" Message - 5284H

5284H-5296H
DEFM "FILE SPEC REQUIRED" + 0DH 46 49 4C 45 20 53 50 45 43 20 52 45 51 55 49 52 45 44 0D
19-byte ASCII error message string followed by 0DH terminator. Used when LOAD or RUN is entered without a filename.

Data: "PARAMETER ERROR" Message - 5297H

5297H-52A6H
DEFM "PARAMETER ERROR" + 0DH 50 41 52 41 4D 45 54 45 52 20 45 52 52 4F 52 0D
16-byte ASCII error message string. Displayed when the filespec cannot be resolved.

Data: "INSERT SOURCE DISK (ENTER)" Prompt - 52A7H

52A7H-52C5H
DEFM 1DH,1EH,"INSERT SOURCE DISK (ENTER)",1DH,03H 1D 1E 49 4E 53 45 52 54 20 53 4F 55 52 43 45 20 44 49 53 4B 20 20 28 45 4E 54 45 52 29 1D 03
31-byte display sequence. 1DH+1EH (home + clear) ensures clean prompt. 1DH+03H is the display terminator.

Data: Filespec Work Area Template - 52C6H

52C6H-52CEH
DEFM "X 9R",00H 58 20 20 20 20 20 39 52 00
9-byte filespec work area. 58H ('X') is drive indicator; 39H/52H ('9R') are format/attribute bytes. Populated by 441CH and passed to 4476H.

Data: Default Extension "CMD" - 52CFH

52CFH-52D1H
DEFM "CMD" 43 4D 44
3-byte default extension string. Copied into the filespec template if the user supplies no extension.

Data: FCB Work Area - 52D2H

52D2H-52F1H
DEFS 32 (FCB work area) [32 bytes]
32-byte File Control Block work area. Holds directory information and I/O state needed by the 4430H program loader.

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.

52F2H
CALL 4430H CD 30 44
Call SYS0 Load Program File. DE = 52D2H. Reads file into memory based on load module records. Does NOT jump to transfer address.
52F5H
LD A,(5239H) 3A 39 52
Load A with the runtime (X) option flag. Non-zero if user appended (X) to the LOAD command. Controls system disk restore prompt.
52F8H
OR A,A B7
Test if A is zero (no (X) option). Sets Z flag if A = 00H.
52F9H
JP Z,402DH CA 2D 40
If the (X) option was not specified (Z set), jump directly to the DOS READY / No-Error Exit at 402DH. Since LOAD pushed 402DH onto the stack at 5206H, this jump effectively clears that safety net and returns control to the OS.
52FCH
LD HL,5306H 21 06 53
If the (X) option was specified, load HL with the address of the "INSERT SYSTEM DISK (ENTER)" prompt. This prepares the system to wait for the user to return the system disk to the drive after the program has been loaded from the source disk.
52FFH
JP 5245H C3 45 52
Jump to the disk-wait routine at 5245H. This displays the system disk prompt and waits for the user to insert the disk. Once ready, the routine returns to the address currently on the stack—which is 402DH (pushed at 5206H)—exiting to DOS READY.

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.

5300H
CALL 4430H CD 30 44
Call SYS0 Load Program File. DE points to the FCB at 52D2H. This routine loads the file into memory. Unlike the LOAD path, there is no DOS READY address pushed onto the stack; the system expects to execute the code next.
5303H
JP 52F5H C3 F5 52
Jump into the (X) option check at 52F5H. If (X) is set, the system prompts for the system disk swap. If the load is successful and the disk swap is complete (or skipped), the program transfer address (stored by 4430H) is eventually called.

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.

5306H-5324H
DEFM 1DH,1EH,"INSERT SYSTEM DISK (ENTER)",1DH,03H 1D 1E 49 4E 53 45 52 54 20 53 59 53 54 45 4D 20 44 49 53 4B 20 20 28 45 4E 54 45 52 29 1D 03
31-byte display sequence. 1DH+1EH (home + clear) clears the screen for the prompt. This message ensures the user replaces the system disk so VTOS can resume normal command processing after the overlay is released.