TRS-80 DOS - PerCom MicroDOS Version 2.20 for the Model I - Resident OS Disassembled

Page Customization

Introduction/Summary:

PerCom MicroDOS 2.20 Resident OS Disassembly (Model I)

The PerCom MicroDOS resident operating system is the 4,608-byte image that the boot sector loads from the chain T0/S1 -> T0/S9 -> T1/S0 -> T1/S9 into RAM starting at 4400H. After the final sector is read, the boot sector at 42BAH executes a JP NZ,4400H, and the three bytes at 4400H (C3 27 47 = JP 4727H) hand control to the resident's initialization routine.

MicroDOS sits between Level II BASIC and the user. It does not replace BASIC; it intercepts BASIC. The initialization routine at 4727H performs five jobs in sequence: (1) it copies 39 bytes of the BASIC ROM error-message table from 18F7H to 4080H (creating an editable copy that MicroDOS can extend with its own error messages); (2) it sets up a comma-separator parsing template at 41E5H; (3) it copies the 147-byte JP-table at 4694H down to 4152H, where the empty entries land on top of the Level II BASIC RST 28H hook addresses (400CH, 4015H) and convert them into MicroDOS DOS-call entries; (4) it initializes the string-storage and byte-storage pointers at 5A00H and 5A64H respectively; and (5) it relocates the stack to 41F8H, prints the PerCom MicroDOS banner via the Level II BASIC ROM 28A7H string-output routine, hooks the keyboard input vector at 4016H to 478FH, and finally jumps to 00B5H to enter Level II BASIC.

From that point on, every keypress comes through 4016H to MicroDOS' keyboard-driver hook at 478FH, which feeds either a stored AUTO-LOAD command sequence (initially "LOAD 30,R" from 44A5H) or a normal keyboard scan into BASIC. Every disk-related BASIC keyword (CMD, CALL, GET, PUT, GETVAL, AND, etc.) is dispatched through the relocated jump table at 4152H, which arranges the entries so that BASIC's reserved-word table indices land on either MicroDOS's own handlers (CMD, F, I, H, K, M, etc.) or on the original Level II BASIC handlers via JP 0043H (a stub that returns "FC Error" for unsupported keywords).

The disk I/O subsystem at 4BCDH (read sector) and 4C44H (verify sector) drives the WD1771 FDC at 37ECH-37EFH using the same alternate-register trick as the boot sector: BC' holds 4300H (the sector buffer), DE' holds 37ECH (FDC command/status), and HL' holds 37EFH (FDC data). The FDC routines re-execute EXX whenever they need to access the FDC, and again to switch back. The sector address is held in 448DH-448EH (a 16-bit logical sector number), and the drive is selected from a one-hot mask at 448CH (01H, 02H, 04H, or 08H).

The custom error message table starting at 52F1H extends the Level II BASIC error list with twelve MicroDOS-specific errors: BAD FILE DATA, FEATURE NOT IMPLEMENTED, INVALID DRIVE NUMBER, INVALID SECTOR NUMBER, DISK READ/WRITE ERROR, DISK WRITE PROTECTED, DISK OVERFLOW, DISK MISSING OR DOOR OPEN, FIELD OVERFLOW, FUNCTION DEFINITION ERROR, INVALID FIELD BUFFER NUMBER, DISK DRIVE NOT AVAILABLE, DISK SEEK ERROR, and CAN'T FORMAT DISK. These messages are reached via JP 19A2H with the error code in Register E.

The 4,608-byte image is laid out as four functional regions. The first region (4400H-4593H) is mostly data: the JP entry at 4400H, the MICRODOS startup banner at 4401H, an internal symbol table at 4459H listing five Level II BASIC variable addresses (CRLNUM, PRGEND, ARREND, ACCUM, and a fifth at 4506H), the AUTO-LOAD command string at 44A5H, three small data tables at 4490H-44A4H used by the FCB system, and the PerCom copyright banner at 4527H. The second region (4594H-4670H) is an embedded copy of the boot sector code, present so that a SYSTEM call or external reload can invoke the loader without re-reading T0/S0. The third region (4671H-46FFH) contains the boot-sector-style data block (TRACK, SECTOR, DISK ERROR, NO MICRODOS, $STEPL) followed by the 147-byte dispatch table that gets relocated to 4152H. The fourth region (4727H-55FFH) is the actual MicroDOS code: the entry routine, the keyboard hook, the BASIC token interceptor, the file directory walker, the disk I/O subsystem, the FCB management routines, and the BASIC error message table extension.

From the user's perspective, MicroDOS appears as an extended BASIC. After the banner prints, the system displays the standard "MEMORY SIZE?" prompt (because 00B5H is the Level II BASIC cold-start entry that ran after MEMORY SIZE prompts), then the BASIC READY prompt. Typing a BASIC command works as it did under cassette Level II BASIC. Typing CMD"D" or CALL DIR (or whichever keyword Stutsman implemented for directory listing) routes through the dispatch table at 4152H and produces a MicroDOS-specific behavior. Disk file operations use MicroDOS's own FCB at 448CH-449FH and its own sector-chain file format (the same chained-sector mechanism used by the boot sector to load the OS itself).

Memory Map

The resident occupies the entire RAM region 4400H-55FFH. After initialization, MicroDOS additionally uses 4080H-40A6H (relocated BASIC error table), 4152H-41E4H (relocated DOS supervisor / dispatch jump table), 41E5H-41E7H (comma-separator template), 41F8H (stack), 5A00H+ (string storage), and 5A64H+ (byte storage).

Address RangeLabelContents
4400H-4402H(3 bytes)ENTRYJP 4727H. The first three bytes of the resident image, executed by the boot sector's final JP NZ,4400H. Hands control to the initialization routine.
4403H-4458H(86 bytes)BANNER1The startup banner string, decoded as bytes: "MICRODOSA SIMPLE, POWERFUL DOS\rWRITTEN BY JAMES W. STUTSMAN\rFOR PERCOM DATA COMPANY\r\r\0". The leading byte at 4403H is M (4DH), used by the FCB system as a default signature byte. The string is never displayed; it is a build-time signature embedded by Stutsman to identify the OS image.
4459H-4505H(173 bytes)SYMTABBuilt-in symbol table holding five label/address pairs in source-listing format: "CRLNUM EQU 40ECH ;CURRENT LINE NUMBER", "&PRGEND EQU 40FBH ;END OF SIMPLE VARIABLES", "ARREND EQU 40FDH ;END OF ARRAYS", "ACCUM EQU 4121H ;NUMBER ACCUMULATOR", and a fifth entry "NUMBER ACCUMULATOR" at 4506H. Used by the SYM/CALL command (4E55H dispatch entry) to convert symbolic names to addresses.
4488H-448BH(4 bytes)FCBHDRFCB header bytes 01 00 01 FF. Marks an unallocated FCB slot.
448CH(1 byte)DRVSELDrive select mask (one-hot). 01H=Drive 0, 02H=Drive 1, 04H=Drive 2, 08H=Drive 3. Written to 37E1H (drive select latch) by the FDC routines. Initialized to 01H (Drive 0) by 4780H.
448DH-448EH(2 bytes)SECTORCurrent logical sector number (16-bit). Hi byte at 448DH = track number, lo byte at 448EH = sector number within track. Used by the FDC routines at 4D44H to seek the head and select the sector.
448FH(1 byte)BUFOFFBuffer offset / chain-marker storage. Holds the chain-marker byte read from the first byte of each sector during a sector-chain walk. FFH means continuation, any other value N means end-of-chain with N data bytes.
4490H-4491H(2 bytes)BUFPTRPointer into the 256-byte sector buffer at 4300H. Points to the next byte to read from the chain. Initialized to 4301H (skipping the chain-marker byte at offset 0) by 4929H.
4492H-4493H(2 bytes)RECNORecord number / record byte count. Used by the file allocation and disk-space-checking routines at 4B20H and 4CA1H.
4494H(1 byte)FDCFLGFDC flags / step-rate. Bits 0-1 hold the WD1771 stepping rate (00=3ms, 01=6ms, 10=10ms, 11=15ms). Bit 7 may be a write-protect flag.
4495H(1 byte)FDCSTATSaved FDC status from the last command. Written by 48F7H (Type I error save) and 4BEEH (Type II error save). Read by 490FH to test for write-protect on disk command failure.
4496H(1 byte)FDCCKSUMRunning checksum / accumulator used by FORMAT operations to verify a track's data after writing.
4497H-449AH(4 bytes)STEPRATEStep-rate selection table. Four bytes selecting the FDC step-rate command for each of the four drives.
449BH-44A4H(10 bytes)FORMTBLFormat-track parameter table. 10 bytes used by the FORMAT routine at 4CA1H to lay down sector ID fields.
44A5H-44ADH(9 bytes)AUTOCMDThe default AUTO-LOAD command string "LOAD 30,R" terminated by a 0DH carriage return. The keyboard input hook at 478FH feeds this string to BASIC one byte at a time on first boot, causing MicroDOS to automatically execute LOAD 30,R after the BASIC READY prompt appears. Once exhausted, the hook switches to live keyboard input.
44AEH-4525H(120 bytes)SYMTAB2More entries in the built-in symbol table: "EQU 40FBH ;END OF SIMPLE VARIABLES", "ARREND EQU 40FDH ;END OF ARRAYS", "ACCUM EQU 4121H ;NUMBER ACCUMULATOR", with NEG-encoded source-listing characters interspersed.
4527H-457FH(89 bytes)BANNER2The PerCom MicroDOS user banner: "[D]PERCOM MICRODOS VERSION 2.20\rCOPYRIGHT (C) 1979 PERCOM DATA COMPANY\rALL RIGHTS RESERVED\r\0". The leading 'D' at 4527H is a stray byte used by the symbol-table tail; the actual displayed string starts at 4528H. Output by 4761H-4766H via Level II BASIC ROM 28A7H.
4581H-4593H(19 bytes)LSUMSGThe string "LAST SECTOR USED =" terminated by 00H. Used by the format/disk-space routines to display the last allocated sector number.
4594H-4670H(221 bytes)BOOTCOPYAn exact copy of the boot sector code from 4200H-42DCH, embedded in the resident image. Provides a re-entry point for SYSTEM calls or external relocators to re-load the OS without reading T0/S0. The code is identical to the boot sector but executes from 4594H instead of 4200H, so the internal references (e.g., LD HL,42F1H at 45A9H) still point at the original boot sector data area at 42xxH; this means BOOTCOPY only works correctly if the original boot sector is still resident at 4200H, which is the normal case.
4671H(1 byte)PADBYTEOne pad byte (00H) separating the boot copy from the data block.
4672H-4674H(3 bytes)DEFM_HEADThe bytes 01 17 E8, encoding LD BC,0E817H but used as the lead-in for the DISK ERROR string (17H E8H = HOME and a graphics marker). Same as the boot sector's 42DDH-42DFH region.
4675H-467FH(11 bytes)DISKERR2The string "DISK ERROR" terminated by 00H. A second copy of the boot sector's DISK ERROR string. Used by the embedded boot copy at 4636H (which jumps to 4671H, not 42DEH).
4680H-468DH(14 bytes)NOMDOS2The string "NO MICRODOS" preceded by 17H E8H and terminated by 00H. Same content as the boot sector's NO MICRODOS message.
468EH-4693H(6 bytes)STEPLThe six bytes "$STEPL" - a residual symbol-table label from the source listing, never read at runtime. Companion to the boot sector's "$STEPH".
4694H-4726H(147 bytes)JTABLEThe 49-entry JP-table relocated to 4152H by the init routine at 4741H-474BH. Each entry is 3 bytes (C3 lo hi). After relocation, the table at 4152H provides the BASIC token dispatch for MicroDOS keywords. Many entries are JP 0043H (the Level II BASIC ROM VDPRT video output trampoline at 0043H, used as a filler for unsupported keywords) and JP 012DH (the Level II BASIC ROM NAVERR routine that displays L3 error 2CH).
4727H-478CH(102 bytes)INITThe MicroDOS initialization routine. Sets up RAM, hooks the keyboard, prints the banner, and JPs to BASIC.
478FH-47A2H(20 bytes)KBDHOOKKeyboard input hook. While the auto-command string at 44A5H still has bytes left, returns one byte at a time from the AUTOCMD pointer. When exhausted, re-points the keyboard hook to 47A3H (live keyboard scanner) and falls through.
47A3H-47C8H(38 bytes)KBDSCANLive keyboard scanner. Walks the keyboard memory at 3801H, 3802H, 3804H, 3808H, 3810H, 3820H, 3840H, 3880H, computes the row offset, and falls through to the Level II BASIC ROM key-decode routine at 03FBH.
47C9H-485FH(151 bytes)BASTOKBASIC token dispatcher. Interprets the byte after a CMD/CALL token as a single-letter command (F, I, H, K, M, etc.) and dispatches to the appropriate MicroDOS routine. Implements F (FETCH), I (INPUT), H (HEX), K (KILL), and M (MERGE) - the AUTO-LOAD-related operations.
4870H-4905H(150 bytes)FILEIOSector-chain file load routine. Walks the chained-sector format used by MicroDOS files: each sector starts with a length-or-continuation byte (FFH = continue, else N = final with N data bytes), and the file ends when a non-FFH marker is found.
491BH-499DH(131 bytes)RAMOPSRAM management: copy regions, allocate file space, walk through memory looking for free zones.
49AEH-4A47H(154 bytes)FCBOPSFCB allocation, file directory walk, and CMD-line parsing for OPEN/CREATE/READ.
4A4AH-4ABFH(118 bytes)DIRWALKFile directory walker. Reads the chained directory record format and decodes filenames.
4AAEH-4AFAH(77 bytes)OPENRDOpen file for read - locates the file in the directory and prepares the FCB.
4AFBH-4B58H(94 bytes)NUMPARSENumeric argument parser. Handles drive-number and record-number arguments to disk commands.
4B59H-4BCCH(116 bytes)FILESPECFilespec parser. Accepts either a quoted string ("FILENAME") or a BASIC string variable, and resolves it to a buffer pointer.
4BCDH-4C90H(196 bytes)SECREADFDC sector read routine. Issues WD1771 Read Sector (88H), polls for DRQ, and transfers bytes into the buffer.
4C91H-4D43H(179 bytes)FORMATDisk format routine. Lays down sector IDs and writes data fields for an entire track.
4D44H-4DE2H(159 bytes)FDCSEEKFDC seek and drive-select routine. Selects the drive, sets the step-rate, seeks the head to the target track, sets the sector register, and prepares for a read or write command.
4DE3H-4E18H(54 bytes)FDCCMDFDC command issue routine. Same general structure as the boot sector's 42B9H, but with hooks for write commands and error reporting.
4E19H-4E5DH(69 bytes)ERREXITError exit jump table. 16 single-byte error codes loaded into Register E and JPed to 19A2H (the Level II BASIC ROM error handler) to display the corresponding MicroDOS error message from the table at 52F1H.
4E66H-4F11H(172 bytes)STRMOVString move and assignment. Used by LET-style statements involving disk strings.
4F12H-4FB6H(165 bytes)BASICBASIC keyword handlers: WRITE, READ, FIELD, CLOSE - the disk-file-related BASIC statements.
4FB7H-505FH(169 bytes)NUMIONumeric I/O: convert numbers to/from disk files, handle field formats.
5060H-52F0H(657 bytes)UTILSUtility routines: string compare, copy, length, hex digit decode, and miscellaneous BASIC support.
52F1H-5577H(647 bytes)ERRMSGBASIC error message table. Re-implements the Level II BASIC ROM messages at 18C9H and adds 13 MicroDOS-specific messages. Each message is a NUL-terminated ASCII string, located by index from Register E via JP 19A2H.
5578H-558DH(22 bytes)ERRJMPError code jump indirection table. 11 entries of LD E,4AH style instructions, each providing a unique error code via Register E.
558EH-55B3H(38 bytes)MISCRTMiscellaneous tail routines: string termination, string-to-string move, and an alternative entry to 4977H.
55B4H-55FFH(76 bytes)CMDTBLThe CMD/CALL token name table. NUL-terminated ASCII command names ("AND PROCESSING", "GETVAL", "GETARGUME...") used by the symbol-table parser. The final 0DH-and-NUL-terminated string at 55ECH is "GET ARGUME" (truncated by the end of the image at 55FFH).

Self-Modifying Workspace Variables

MicroDOS uses both internal storage within its image (in the FCB area at 448CH-449FH) and external low-RAM workspace (4080H-40A7H, populated by the init routine). The list below is keyed by address. All entries marked Self-Modifying Code show their initial values from the source disassembly and the runtime values they take after initialization.

AddressBytesDescription
4080H-40A6H39Relocated BASIC Error Table
A copy of the Level II BASIC ROM error message table at 18F7H, written here at 472DH-4731H. MicroDOS extends this in-RAM copy with disk-specific error messages, which is why the error handler at 19A2H sees both ROM and MicroDOS messages.
40A0H2Byte Storage Pointer
Set to 5A64H by the init routine at 474CH-474EH. Marks the start of MicroDOS's byte-storage area for compiled BASIC variables.
40A4H2String Storage Pointer
Set to 5A01H by the init routine at 4752H-4759H (loads HL=5A00H, writes 00H, increments HL, stores HL). Marks the start of the string storage area.
40A7H2Comma Separator Template Pointer
Set to 41E8H by the init routine at 473EH. Points just past the three-byte separator template stored at 41E5H-41E7H, which holds 3AH ":" 00H NUL 2CH ",".
4016H2Self-Modifying Code
The Level II BASIC ROM keyboard input vector. Init routine at 4767H-476CH writes 478FH here, hooking MicroDOS into BASIC's keyboard input. Once the AUTO-LOAD string is exhausted, 47A0H rewrites this to 47A3H (live keyboard scan).
4152H-41E4H147Relocated Dispatch Table
A copy of the JP-table at 4694H, written here at 4747H-474BH. After relocation, this table provides the RST 28H-style DOS-call dispatch for every BASIC keyword that MicroDOS overrides.
41E5H-41E7H3Comma Separator Template
Holds the bytes 3A 00 2C (":" NUL ","), written by the init routine at 4732H-473DH. Used by the argument parser at 4B59H to recognize statement separators in BASIC commands.
41F8H(stack)Stack Top
Set by the init routine at 475BH (LD SP,41F8H). Stack grows downward into the unused RAM at 41F7H and below. Note this is 4 bytes lower than the boot sector's 41FCH, freeing 4 bytes for an additional level of nested CALL.
4495H1Self-Modifying Code
FDC saved status. Written by 48F7H whenever a Type-I FDC operation completes (saves the status register before issuing FORCE INTERRUPT D0H), and by 4BEEH whenever a Type-II operation completes. Read by 490FH to test bit 7 (write protect / not ready) when reporting an error.
4496H1Self-Modifying Code
FORMAT routine running checksum. Written by 487CH (cleared at start of FORMAT), updated by 489CH-48B2H during the format-and-verify pass.
4497H-449AH4Self-Modifying Code
FORMAT/SEEK step-rate selector. Written by 4775H-4778H during init (LD HL,FFFFH stored to both 4497H and 4499H sets bytes 4497H-449AH all to FFH). Used as a per-drive step-rate selector at 4D44H-4D54H (right-rotated and tested for carry to find the matching drive bit).
448CH1Self-Modifying Code
Drive-select mask. Init routine at 4780H-4783H writes 01H (drive 0). Modified by drive-selection commands and by the FCB allocation routines.
448DH1Self-Modifying Code
Track number for next FDC operation. Cleared to 00H by 4926H. Written to 37EFH (FDC data register) by FDCSEEK at 4D6EH-4D6FH.
448EH1Self-Modifying Code
Sector number for next FDC operation. Initialized to 01H by 4933H (the file-load context init). Written to 37EEH (FDC sector register) by FDCSEEK at 4D77H-4D7AH.
448FH1Self-Modifying Code
Chain marker / buffer offset. Written by the file load routine at 491DH-4923H whenever a new sector is read (captures the marker byte). Read by 4A6CH-4A70H to drive the chain-walker decisions.
4490H-4491H2Self-Modifying Code
Buffer pointer (16-bit). Written to (4490H) by 4923H to hold the address of the next byte to read from the buffer. Used in EX DE,HL operations at 4490H-4491H to advance through the buffer.
4492H-4493H2Self-Modifying Code
Record number / record byte count. Written by 4B20H during file allocation; read by 4CA1H during FORMAT to determine the number of bytes per sector. Used as a 16-bit value at NUMPARSE.
4494H1Self-Modifying Code
FDC flags. Bit 0 set when a file is being written; bit 6 (tested at 4BFFH and 4C9AH) indicates write-protect on the current drive.
5A00H1String storage initial sentinel. Set to 00H by 4755H. Marks the start of the heap that MicroDOS uses for BASIC string variables.
5A01H+variableActive string storage. Each entry is a length-prefixed string. The pointer at 40A4H tracks the next free byte.
5A64H+variableByte storage / variable area. Each entry is a 4-byte BASIC variable record (header byte + 3 bytes of value). The pointer at 40A0H tracks the next free record.

Major Routines

AddressFunction / Entry-Exit
4727HInitialization Entry Point (INIT)
The first executable code in the resident image. Copies the BASIC error table to 4080H, writes the comma-separator template at 41E5H, copies the dispatch table to 4152H, sets up the string and byte storage pointers at 5A00H/5A64H, relocates the stack to 41F8H, displays the banner via 28A7H, hooks the keyboard at 4016H, and JPs to 00B5H to enter Level II BASIC.
478FHKeyboard Input Hook (KBDHOOK)
Replacement for the BASIC ROM keyboard read at 0049H. Reads from the AUTO-LOAD command string at 44A5H one byte at a time. Returns the byte in Register A. When the string is exhausted (NUL byte read), rewrites the keyboard hook at 4016H to point at 47A3H (the live keyboard scanner) and falls through.
47A3HLive Keyboard Scanner (KBDSCAN)
Scans the keyboard memory rows at 3801H, 3802H, 3804H, 3808H, 3810H, 3820H, 3840H, 3880H. For each row, computes the bit-mask of pressed keys and transitions to the Level II BASIC ROM key-decode routine at 03FBH. Returns Z if no keys are pressed.
47C9HBASIC Token Dispatcher (BASTOK)
Called via the relocated dispatch table for CMD/CALL with a string argument. Reads the first character of the argument, compares against F (4FETCH), I (4INPUT), H (4HEX), K (4KILL), and M (4MERGE), and JPs to the matching handler. Default exit is JP 1E4AH (Illegal Function Call).
4870HSector Chain Loader (FILEIO)
Reads a sector-chain file. Initializes the FCB header at 4488H-448FH, calls FDCSEEK at 4D44H to position the head, reads the first sector via SECREAD at 4BCDH, and walks the chain marker by marker. Each sector is read into the buffer at 4300H, the marker byte is captured at 448FH, and the data is appended to the destination buffer.
491BHSYSTEM Re-Init Helper (RAMOPS)
Reads a single byte from 4594H (the embedded boot copy entry point), saves it to 448FH, sets up the buffer pointer to 4595H, and calls 4BF6H to write the byte. Used by the SYSTEM-call re-initialization path.
4941HOPEN File Handler (FCBOPS entry)
Routes from the dispatch table at 4152H entry for the OPEN keyword. Calls 4AFBH to extract the filespec (a quoted string or string variable), then performs directory lookup and FCB initialization.
49AEHOPEN-Existing-File Handler
Specifically for the variant that opens an existing file (versus CREATE-new). Reads the FCB number from the BASIC argument list, looks up the file, and prepares the FCB for sequential read.
49F6HCREATE File Handler
Creates a new file in the directory. Sets up an empty FCB, allocates a starting sector via 4977H, writes the directory entry, and prepares the FCB for sequential write.
4AAEHREAD Sector Handler
Routes from the GET keyword. Reads a numeric record number, calls FDCSEEK at 4D44H, calls SECREAD at 4BCDH. On success, copies the sector buffer to the BASIC string variable.
4AE4HWRITE Sector Handler
Routes from the PUT keyword. Reads a numeric record number, copies the BASIC string buffer into the 256-byte sector buffer, calls FDCSEEK and the FDC Write command.
4AFBHFilespec Parser (FILESPEC entry)
Calls Level II BASIC ROM 2337H to fetch a parameter, then calls 2819H (typeflag check) and FILESPEC at 4B10H. Returns with HL pointing at the resolved filename buffer.
4B10HDrive-Number Argument Parser
Subtracts 2710H (10000 decimal) from HL repeatedly, counting iterations to extract the drive digit. Result in Register A. Falls through to record-number parser at 4B49H.
4B49HRecord Number Validator
Validates that Register E is less than 04H (max drive number 3). If valid, computes the drive-select mask via the (1 << drive) pattern (set A=80H, RLCA repeated drive+1 times) and stores it at 448CH.
4B59HFilespec Resolver
Accepts a quoted string ("#" prefix means a file-buffer reference) or a BASIC string variable. Calls 260DH (VARPTR) and 0AF4H (which at this offset is part of MOVEA / MOVEB) to resolve the variable.
4BCDHRead Sector via FDC (SECREAD)
Issues WD1771 Read Sector command (88H), polls for DRQ, transfers 256 bytes from 37EFH (FDC data register) into the buffer at BC' (4300H). On success, sets BC=4300H and returns; on error (status & 9CH), saves status at 4495H and returns with FDC at FORCE INTERRUPT.
4C44HVerify Sector (Compare-after-Write)
Reads sector and compares against buffer. Returns NC if all bytes match (verify success), C if mismatch.
4C6DHRead Sector for Compare
Issues Read Sector but discards the data, only checking for clean read status. Used by Verify to ensure the sector exists before doing a compare.
4C91HFORMAT Track (FORMAT)
Writes a complete track including sector ID fields and data fields. Used by the FORMAT command. Writes 1771 Format Track command (F4H), then streams the track image with all sector IDs.
4D44HFDC Seek and Drive-Select (FDCSEEK)
Reads the drive-select mask from 448CH, finds which drive is selected (RRA-and-test-carry pattern), retrieves the per-drive step-rate, sets the FDC track and sector registers, and issues the WD1771 Seek command. Equivalent in function to the boot sector's 4275H, but supports four drives and per-drive step rates.
4D89HRestore-then-Seek (Track 0 Calibration)
If a Seek fails, calls Restore (00H) to return the head to Track 0, then re-issues the Seek. Used to recover from misaligned heads.
4DACHFDC Restore Command
Issues WD1771 Restore (00H or 03H based on step-rate). Returns Z if Track 0 reached, NZ if seek error.
4DE3HSend FDC Command (FDCCMD)
Equivalent to the boot sector's 42B9H. Saves the command on the stack, waits for the FDC not-busy, restores the command, writes it via DE' (37ECH), and polls for completion. Re-asserts drive select at 37E1H during the polling loop.
4E19H-4E5DHError Code Jump Table (ERREXIT)
16 LD E,nn / JP 19A2H pairs, each providing a unique error code (4AH=BAD FILE DATA, 4EH=FEATURE NOT IMPLEMENTED, etc.). Called from the FDC error paths and the BASIC keyword handlers when an unsupported operation is requested.
4E66HString Move and Pad (STRMOV)
Copies a length-prefixed string from a BASIC string variable into a destination buffer. Pads with spaces if the destination is longer than the source.
4F12H, 4F16H, 4F1AHWRITE Statement Handlers
Three entry points for the BASIC WRITE statement variants: PRINT#, PRINT#USING, PUT. Each loads a different command-code-byte into Register A and then jumps to 4F1CH (the common write dispatcher).
4F2DH, 4F31H, 4F35HREAD Statement Handlers
Three entry points for the BASIC READ statement variants: GET, INPUT#, FIELD. Same pattern as WRITE: each loads a command-code byte and jumps to 4F37H.
4F4CH, 4F4FHOPEN Mode Handlers
Two entries differing in whether the file is opened in append mode (FFH) or sequential read/write mode (00H).

Cross-References and Analysis Notes

The resident OS depends on the following Level II BASIC ROM routines: 0033H (display char), 0040H (keyboard line input - via the relocated table), 0043H (VDPRT - Video Display Output trampoline; jumps to 0434H to print character in Register C), 0049H (keyboard scan - replaced by the hook at 4016H), 0060H (timing delay), 00B5H (BASIC entry after MEMORY SIZE prompt), 012DH (NAVERR - loads error code 2CH and jumps to error handler at 19A2H), 01C9H (CLS), 03FBH (key-decode dispatch), 0AF4H (MOVEA: move memory block, related to 09D6H), 18F7H (just past the end of the BASIC error message table at 18F6H - source for the relocated copy), 1955H (BASIC RUN initialization), 1997H (Syntax Error), 19A2H (Error Handler with code in Register E), 1A19H (BASIC READY entry), 1AF8H (write line pointers), 1B2CH (find line by number), 1B4DH (reset BASIC pointers), 1B5DH (BASIC initialization), 1B8FH (STKINI: initialize BASIC pointers, stack, and variables), 1C3DH (?), 1D1EH (run BASIC statement), 1E17H (?), 1E4AH (Illegal Function Call), 197AH (?), 23xxH range (parameter parsing), 28A7H (output 0/22H-terminated string), 29D7H (READY: BASIC READY prompt and command loop entry), 2819H (CPHLDE: 16-bit compare HL against DE), 2857H (STRINI: initialize/create new string entry), 285DH (STRSPA: check string space, garbage collect if needed), 2BE4H (FNDVAR: scan variable table for variable address), 2A2BH (?), 260DH (VARPTR), 09FFH (?), 09F7H (move number to SA), and 558CH (an internal MicroDOS routine called from outside the resident code, suggesting cross-disassembly references).

The resident does not depend on the boot sector being resident at 4200H during normal operation; the embedded boot copy at 4594H-4670H is purely a re-init path that the user must explicitly invoke (via SYSTEM/D or similar) to re-load the OS. During steady-state BASIC operation, only 4400H-4593H (data) and 4727H-55FFH (code/data) are reached.

The "WRITE# is the wrong name; use PUT" convention does not apply here because MicroDOS uses GET/PUT terminology only for random-access binary files (matching NEWDOS/80 and disk extensions of Microsoft BASIC), not as a wholesale replacement. The resident does not implement the WRITE# / READ# keywords; the dispatch table at 4152H simply doesn't route those tokens to MicroDOS code.

The custom error message table at 52F1H-5577H differs from the Level II BASIC ROM table at 18C9H-18F6H in three ways: (1) the strings are full ASCII rather than the ROM's two-letter abbreviations (e.g., "OUT OF MEMORY" instead of "OM"); (2) MicroDOS adds 13 disk-specific messages that the ROM lacks; and (3) the messages are accessed via the relocated copy at 4080H, not directly from 52F1H, so the table at 52F1H is the source data, while the actual lookup table that 19A2H walks is at 4080H.

Disassembly:

Track 0 / Sector 1 (4400H - 44FEH)

4400H - ENTRY - JP to Initialization Routine

When the boot sector at 42BAH executes JP NZ,4400H (the resident is in place because the chain marker on the final sector was non-FFH), the very first three bytes of the resident image take control. This is a forward jump to the initialization routine at 4727H. The 86 bytes immediately following the JP are not executable code; they are the build-time signature banner. The byte at 4403H (4DH = 'M') is also referenced by the FCB system as a default file-record signature, which is why Stutsman placed the M character at this specific offset.

4400
JP 4727H C3 27 47
JUMP to 4727H to begin MicroDOS initialization. Control reaches this instruction either from the boot sector's JP NZ,4400H at 42BAH, or from a SYSTEM-call re-entry that explicitly transfers to 4400H.

4403H - BANNER1 - Build Signature Banner (DATA)

86-byte build-time signature string. Never displayed at runtime. The string reads "MICRODOS / A SIMPLE, POWERFUL DOS / WRITTEN BY JAMES W. STUTSMAN / FOR PERCOM DATA COMPANY", with carriage returns (0DH) between the lines and a NUL terminator at 4458H. The byte at 4403H (4DH = 'M') doubles as the FCB default-record signature byte, which is why this specific 'M' must remain at this specific address even though the banner is never printed.

4403-4421
DEFM "MICRODOSA SIMPLE, POWERFUL DOS",0DH 4D 49 43 52 4F 44 4F 53 41 20 53 49 4D 50 4C 45 2C 20 50 4F 57 45 52 46 55 4C 20 44 4F 53 0D
The first line of the build signature: 31 bytes spelling "MICRODOSA SIMPLE, POWERFUL DOS" followed by 0DH (carriage return). The lack of a space between MICRODOS and A is intentional - the M of MICRODOS is the FCB signature byte and the rest of the string is just descriptive padding. Read by the FCB system at 4870H-4877H during file-create operations: the byte at 4403H is copied into the new FCB header as a default value.
4422-443E
DEFM "WRITTEN BY JAMES W. STUTSMAN",0DH 57 52 49 54 54 45 4E 20 42 59 20 4A 41 4D 45 53 20 57 2E 20 53 54 55 54 53 4D 41 4E 0D
The second line of the build signature: 28 bytes spelling "WRITTEN BY JAMES W. STUTSMAN" followed by 0DH. James W. Stutsman wrote MicroDOS for PerCom Data Company in 1979.
443F-4456
DEFM "FOR PERCOM DATA COMPANY",0DH,0DH 46 4F 52 20 50 45 52 43 4F 4D 20 44 41 54 41 20 43 4F 4D 50 41 4E 59 0D 0D
The third line of the build signature: 23 bytes spelling "FOR PERCOM DATA COMPANY" followed by two 0DH bytes (a blank line at the end of the signature).
4458
DB 00H 00
NUL terminator for the build signature string. Nothing reads this byte at runtime.

4459H - SYMTAB - Built-in Symbol Table (DATA)

A 109-byte source-listing-format symbol table. Each entry is a label name followed by EQU and a hex address, then a semicolon-prefixed comment. The five entries provided are: CRLNUM (40ECH = current line number), PRGEND (40FBH = end of simple variables), ARREND (40FDH = end of arrays), and ACCUM (4121H = number accumulator). The table is data, not code, and would be referenced by a SYM/CALL command (dispatch entry 4E55H) for symbolic-name-to-address conversion.

Current Line Number Section

4459-445F
DEFM "CRLNUM",09H 43 52 4C 4E 55 4D 09
Symbol table entry label CRLNUM (6 bytes) followed by a tab.
4460-4463
DEFM "EQU",09H 45 51 55 09
Instruction EQU followed by a tab.
4464-4468
DEFM "40ECH" 34 30 45 43 48
The hex address string for the Level II BASIC ROM workspace.
4469-447E
DEFM 09H,09H,";CURRENT LINE NUMBER" 09 09 3B 43 55 52 52 45 4E 54 20 4C 49 4E 45 20 4E 55 4D 42 45 52
Two tabs and the descriptive comment.
447F-4480
DEFM 0CH,17H 0C 17
Trailing separator codes.

Program End and FCB Section

4481-4487
DEFM "&PRGEND" 26 50 52 47 45 4E 44
Symbol table label for &PRGEND.
4488-448B
DEFB 01H,00H,01H,FFH 01 00 01 FF
FCB initial header (record-type 01, ID 00 01 FF) interrupting the symbol entry.
448C-4494
DEFB 00H,44H,28H,0AH,03H,00H,00H,00H,FFH 00 44 28 0A 03 00 00 00 FF
FCB disk parameters: drive mask, track, sector, and step rates.
4495-44A4
DEFB FFH,FFH,00H,05H,01H,06H,02H,07H,03H,08H,04H,09H FF FF FF 00 05 01 06 02 07 03 08 04 09
Remaining step rates and the PerCom format-track parameter table.

Auto-Load String Section

44A5-44A5
DEFM 0DH 0D
Preamble byte (Enter) to clear the BASIC input buffer.
44A6-44AC
DEFM "LOAD 30,R" 4C 4F 41 44 20 33 30 2C 52
The auto-load command string.
44AD-44AD
DEFM 0DH 0D
Trailing Enter to execute the LOAD command.

Continuation

44AE-44B0
DEFM 00H,09H 00 09
Leading null and tab.
44B1-44B4
DEFM "EQU",09H 45 51 55 09
EQU instruction and tab.
44B5-44B9
DEFM "40FBH" 34 30 46 42 48
Address string for PRGEND (End of simple variables).
44BA-44D6
DEFM 09H,09H,";END OF SIMPLE VARIABLES",70H,17H 09 09 3B 45 4E 44 20 4F 46 20 53 49 4D 50 4C 45 20 56 41 52 49 41 42 4C 45 53 70 17
Comments and record separators for the PRGEND entry.
44D7-44DE
DEFM " ARREND" 20 41 52 52 45 4E 44
Label for ARREND (End of arrays).
44DF-44E2
DEFM 09H,"EQU" 09 45 51 55
Tab and EQU instruction.
44E3-44E8
DEFM 09H,"40FDH" 09 34 30 46 44 48
Tab and address string.
44E9-44F8
DEFM 09H,09H,";END OF ARRAYS",A2H,17H 09 09 3B 45 4E 44 20 4F 46 20 41 52 52 41 59 53 A2 17
Comments and separators for ARREND.
44F9-4500
DEFM "$ACCUM" 24 41 43 43 55 4D
Label for ACCUM (Number accumulator).
4501-4522
DEFM 09H,"EQU",09H,"4121H",09H,09H,";NUMBER ACCUMULATOR",D4H,17H 09 45 51 55 09 34 31 32 31 48 09 09 3B 4E 55 4D 42 45 52 20 41 43 43 55 4D 55 4C 41 54 4F 52 D4 17
Address, comment, and separators for ACCUM.
4523-4525
DEFM 1EH,"INB" 1E 49 4E 42
The record type (1EH) and the start of the next label "INBU".

Track 0 / Sector 2 (44FFH - 45FDH) - boundary falls within preceding SYMTAB2 row at 44AE-4525

4526H - AUTOPTR - Auto-Load Pointer (DATA)

Two bytes of pointer storage written by the init routine at 476DH-4772H to hold 44A5H (the start of the auto-load command string). The keyboard input hook at 478FH reads from this pointer and increments it.

4526-4527
DW 44A5H A5 44
Self-Modifying Code
Pointer to the next byte of the auto-load command string. Initialized to 44A5H by 476DH-4772H of the init routine. Read and incremented by the keyboard input hook at 478FH-4794H. When the byte read is NUL, the hook re-points the keyboard vector at 4016H and stops feeding auto-load characters.

4527H - BANNER2 - PerCom MicroDOS User Banner (DATA)

The user-visible banner printed by 4761H-4766H during initialization. The leading byte at 4527H is the high byte of the AUTOPTR (44H), but it is also the first byte of a string when read by 28A7H starting at 4527H instead of 4528H. The init routine specifically loads HL=4528H to skip the byte at 4527H, so the actual displayed banner is "PERCOM MICRODOS VERSION 2.20\rCOPYRIGHT (C) 1979 PERCOM DATA COMPANY\rALL RIGHTS RESERVED\r".

4528-4543
DEFM "PERCOM MICRODOS VERSION 2.20",0DH 50 45 52 43 4F 4D 20 4D 49 43 52 4F 44 4F 53 20 56 45 52 53 49 4F 4E 20 32 2E 32 30 0D
The first line of the user banner: "PERCOM MICRODOS VERSION 2.20" followed by 0DH (carriage return). 29 bytes total. Output by the Level II BASIC ROM 28A7H string-output routine.
4544-456B
DEFM "COPYRIGHT (C) 1979 PERCOM DATA COMPANY",0DH 43 4F 50 59 52 49 47 48 54 20 28 43 29 20 31 39 37 39 20 50 45 52 43 4F 4D 20 44 41 54 41 20 43 4F 4D 50 41 4E 59 0D
The copyright line of the user banner: "COPYRIGHT (C) 1979 PERCOM DATA COMPANY" followed by 0DH. 39 bytes total.
456C-457F
DEFM "ALL RIGHTS RESERVED",0DH,00H 41 4C 4C 20 52 49 47 48 54 53 20 52 45 53 45 52 56 45 44 0D 00
The closing line of the user banner: "ALL RIGHTS RESERVED" followed by 0DH and the 00H NUL terminator that 28A7H needs to know where to stop.

4581H - LSUMSG - Last Sector Used Message (DATA)

A 19-byte NUL-terminated string used by the format/disk-space utility commands. Output via 28A7H when the user runs a sector-allocation status command.

4581-4593
DEFM "LAST SECTOR USED = ",00H 4C 41 53 54 20 53 45 43 54 4F 52 20 55 53 45 44 20 3D 20 00
The "LAST SECTOR USED = " string. The trailing space (20H) is intentional - the calling code prints the sector number immediately after the string, so the space is part of the format. The 00H NUL terminator at 4593H ends the string.

4594H - BOOTCOPY - Embedded Copy of Boot Sector Code

An exact 221-byte copy of the boot sector code from 4200H-42DCH. Provided so that a SYSTEM-call re-entry can restart the OS load sequence without reading T0/S0 from disk. The code is byte-for-byte identical to the boot sector, including the FE 00 placeholder at the entry point. Note: the absolute references inside this copy (e.g., LD HL,42F1H at 45A9H, LD HL,42DEH at 45CEH, CALL 424CH at 45A6H) all still point at the original boot sector data area at 42xxH, which means BOOTCOPY only works correctly if the original boot sector is still resident at 4200H. This is the normal case during steady-state operation; the boot sector RAM at 4200H-42FFH is never overwritten by BASIC unless the user explicitly POKEs there.

4594
CP 00H FE 00
Compare Register A against 00H. The result is never read; the two bytes are a placeholder so that future versions could test A on entry. Same as the boot sector's 4200H.
4596
LD SP,41FCH 31 FC 41
Set Register Pair SP to 41FCH (the boot-sector stack location). This destroys the MicroDOS stack at 41F8H if BOOTCOPY is invoked, which is acceptable because BOOTCOPY does a full re-init.
4599
CALL 01C9H CD C9 01
GOSUB to the Level II BASIC ROM CLS routine.
NOTE: 01C9H is the Level II BASIC ROM CLS routine. It clears the screen and homes the cursor.
459C
LD BC,4300H 01 00 43
Set Register Pair BC to 4300H, the address of the 256-byte sector buffer. Same setup as the boot sector at 4208H.
459F
LD DE,37ECH 11 EC 37
Set Register Pair DE to 37ECH, the WD1771 FDC Command/Status register. Same as boot sector 420BH.
45A2
LD HL,37EFH 21 EF 37
Set Register Pair HL to 37EFH, the WD1771 FDC Data register. Same as boot sector 420EH.
45A5
EXX D9
Exchange the BC, DE, HL primary registers with their alternate counterparts BC', DE', HL'. Now BC'=4300H, DE'=37ECH, HL'=37EFH; the primary registers are free for use by string compares.
45A6
CALL 424CH CD 4C 42
GOSUB to the boot sector's Read Sector routine at 424CH.
NOTE: 424CH is the BOOT sector Read Sector routine. It reads Track 0 / Sector 1 (the first OS sector) into the buffer at 4300H. This call only works if the boot sector is still resident at 4200H-42FFH; if the boot RAM has been overwritten, BOOTCOPY fails.
45A9
LD DE,42F1H 11 F1 42
Set Register Pair DE to 42F1H, the address of the 8-byte MICRODOS signature in the original boot sector. Used as the source pointer for the signature compare loop.
45AC
LD HL,4304H 21 04 43
Set Register Pair HL to 4304H, the address in the buffer where the MICRODOS signature should appear (offset 4 of the buffer).
45AF
LD B,08H 06 08
Set Register B to 8, the loop counter for the 8-byte signature compare.
45B1
LD A,(DE) 1A
Load Register A with the byte at the address held in Register Pair DE (the next byte of the signature template at 42F1H+).
45B2
CP A,(HL) BE
Compare Register A against the byte at the address held in Register Pair HL (the next byte of the buffer at 4304H+). If they match, the Z FLAG is set; otherwise the NZ FLAG is set.
45B3
JP NZ,42A7H C2 A7 42
If the NZ FLAG has been set (signature mismatch), JUMP to 42A7H (the boot sector's NO MICRODOS error handler).
NOTE: 42A7H is the BOOT sector NO MICRODOS error handler at the original location. Triggers the "NO MICRODOS" message and reboot path. This jump only works if the original boot sector is still resident.
45B6
INC DE 13
INCrement Register Pair DE by 1, advancing the signature template pointer.
45B7
INC HL 23
INCrement Register Pair HL by 1, advancing the buffer pointer.
45B8
DJNZ 45B1H 10 F7
DECrement Register B and LOOP BACK to 45B1H if not zero, continuing the 8-byte signature compare.
45BA
CALL 42D4H CD D4 42
GOSUB to 42D4H to print the signature/banner.
NOTE: 42D4H is the BOOT sector NUL-terminated string print routine. This is a print of the buffer contents (the loaded sector, which begins with "MICRODOS"-prefixed text after the chain-marker).
45BD
LD DE,4400H 11 00 44
Set Register Pair DE to 4400H, the destination address for the OS load. This will be the running pointer in the LDIR copy loop.
45C0
LD B,00H 06 00
Set Register B to 00H. Used as the high byte of BC in the upcoming LDIR; the low byte (C) gets loaded with the chain marker so BC = 00MM where MM is the marker. Note: MicroDOS uses LD B,00H here even though the boot sector at 4220H uses LD B,00H too - the routines are identical because BOOTCOPY is a byte-for-byte copy.
45C2
LD HL,4300H 21 00 43
Set Register Pair HL to 4300H, the start of the sector buffer (which now holds the just-loaded sector).
45C5
LD C,(HL) 4E
Load Register C with the byte at the address held in Register Pair HL (the chain marker byte at 4300H, which is FFH for "continue" or N for "final with N data bytes"). BC now equals 00MM where MM is the marker.
45C6
LD A,(HL) 7E
Load Register A with the same byte (the chain marker), preserved for the INC A test below.
45C7
INC HL 23
INCrement Register Pair HL by 1, so HL now points at 4301H (the first data byte of the buffer, just past the chain marker).
45C8
LDIR ED B0
Block-copy BC bytes from (HL) to (DE), incrementing HL and DE on each byte. Copies the data portion of the sector (BC bytes, where BC=00MM) from the buffer to the OS load area. After the copy, HL points at the next byte past the buffer (4301H + MM), DE points at the next byte past the OS load (4400H + MM), and BC = 0.
45CA
INC A 3C
INCrement Register A by 1. This was the chain marker. If marker = FFH, INC A makes A = 00H and the Z FLAG is set; otherwise NZ.
45CB
JP NZ,4400H C2 00 44
If the NZ FLAG has been set (marker was not FFH, so this was the final sector), JUMP to 4400H. This is the same JP that the boot sector uses; it transfers control to the JP 4727H at the start of the resident image (recursing into the init routine again).
45CE
LD HL,42DEH 21 DE 42
Set Register Pair HL to 42DEH, the address of the SECTOR variable in the original boot sector data block. This is the byte that holds the next sector to read (initialized to 01H by the source).
45D1
LD A,09H 3E 09
Set Register A to 9, the maximum sector number (sectors 0-9 = 10 sectors per track).
45D3
INC (HL) 34
INCrement the byte at the address held in Register Pair HL by 1, advancing the SECTOR counter at 42DEH.
45D4
CP A,(HL) BE
Compare Register A (=09H) against the byte at the address held in Register Pair HL (the just-incremented sector number). If sector > 9, the C FLAG is set; if sector <= 9, the NC FLAG is set.
45D5
JR NC,45DBH 30 04
If the NC FLAG has been set (sector number is still 0-9), jump forward to 45DBH to issue the next sector read.
45D7
LD (HL),00H 36 00
Otherwise (sector wrapped to 10), set the byte at the address held in Register Pair HL to 0, resetting SECTOR back to 0.
45D9
DEC HL 2B
DECrement Register Pair HL by 1, so HL now points at 42DDH (the TRACK variable).
45DA
INC (HL) 34
INCrement the byte at the address held in Register Pair HL by 1, advancing the TRACK counter at 42DDH.
45DB
CALL 424CH CD 4C 42
GOSUB to the boot sector's Read Sector routine at 424CH.
NOTE: 424CH reads (TRACK,SECTOR) from 42DDH/42DEH and fills the buffer at 4300H.
45DE
JR 45C0H 18 E0
LOOP BACK to 45C0H to copy this newly-loaded sector and continue the chain.

Track 0 / Sector 3 (45FEH - 46FCH) - boundary falls within preceding BOOTCOPY main loop at 45CEH-45DEH

45E0H - BOOTCOPY Read Sector Subroutine

Internal subroutine of BOOTCOPY equivalent to the boot sector's 424CH-4274H routine. Issues an FDC Read Sector command and transfers data into the buffer. Note: this routine is independent of the boot sector's 424CH; it is invoked only from within BOOTCOPY (via 45A6H) when BOOTCOPY runs.

45E0
CALL 4275H CD 75 42
GOSUB to the boot sector's Seek-and-Drive-Select routine at 4275H to position the FDC head.
NOTE: 4275H is the BOOT sector Seek/Drive-Select routine. Reads track and sector from 42DDH/42DEH, selects drive 0, and issues a Seek command. Returns Z if the seek succeeded.
45E3
LD A,88H 3E 88
Set Register A to 88H, the WD1771 Read Sector command (Type II, single record, IBM format, no head-load delay).
1771 FDC Command: 88H (10001000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
100mbE00Command=Read Sector
Bit 7-5: Read Command (100)
m: 0=Single Record, 1=Multiple Records
b: 1=IBM format, 0=Non-IBM Format
E: 0=Assume Head Already Engaged, no Delay, 1=Enable HLD, HLT, and 10ms Delay
Remainder: Unused (00)
45E5
CALL 42B9H CD B9 42
GOSUB to the boot sector's Send-FDC-Command routine at 42B9H.
NOTE: 42B9H writes the command in Register A to the FDC Command register at 37ECH and waits for the busy flag to clear.
45E8
EXX D9
Exchange BC,DE,HL with BC',DE',HL'. Brings BC=4300H (sector buffer), DE=37ECH (FDC status), HL=37EFH (FDC data) into the primary registers for the data transfer.
45E9
JR 45F1H 18 06
Jump forward to 45F1H, skipping the error-check fall-through code. This is the entry point for the FDC polling loop on the first byte (no error has been detected yet).
45EB
AND A,81H E6 81
Logical AND Register A with 81H (10000001 binary), masking out all bits except the not-ready bit (bit 7) and the busy bit (bit 0). After the AND, Register A holds the masked status byte.
45ED
XOR A,01H EE 01
Logical XOR Register A with 01H, flipping the busy bit so that "busy and ready" becomes 00H (transfer in progress with valid data) and any other state is non-zero.
45EF
JR NZ,45FBH 20 0A
If the NZ FLAG has been set (FDC is in an error or completion state), jump forward to 45FBH to read the final status and exit the read loop.
45F1
LD A,(DE) 1A
Load Register A with the byte at the address held in Register Pair DE (the FDC status register at 37ECH).
45F2
BIT 1,A CB 4F
Test bit 1 of Register A (the Data Request bit of the FDC status register). If DRQ is set, the Z FLAG is cleared (data ready to read); otherwise Z is set.
45F4
JR Z,45EBH 28 F5
If the Z FLAG has been set (no DRQ yet), LOOP BACK to 45EBH to re-check status. This is the polling spin loop.
45F6
LD A,(HL) 7E
Load Register A with the byte at the address held in Register Pair HL (the FDC Data register at 37EFH), capturing the data byte just transferred by the FDC.
45F7
LD (BC),A 02
Store Register A to the address held in Register Pair BC (the buffer at 4300H+offset), writing the data byte into the buffer.
45F8
INC BC 03
INCrement Register Pair BC by 1, advancing the buffer pointer to the next byte.
45F9
JR 45F1H 18 F6
LOOP BACK to 45F1H to read the next byte from the FDC.
45FB
LD A,(DE) 1A
Load Register A with the byte at the address held in Register Pair DE (the FDC status register), capturing the final status of the read operation.
45FC
EX DE,HL EB
Exchange Register Pairs DE and HL. After the exchange, DE = 37EFH (data register) and HL = 37ECH (status register), repurposing HL to write the FDC FORCE INTERRUPT command in the next instruction.
45FD
LD (HL),0D0H 36 D0
Set the byte at the address held in Register Pair HL (the FDC command register at 37ECH) to D0H, the WD1771 FORCE INTERRUPT command (terminates any in-progress operation cleanly).
1771 FDC Command: D0H (11010000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
1101I3I2I1I0Command=Force Interrupt
Bit 7-4: Command Code (1101)
I3: 1=Interrupt Immediately
I2: 1=Interrupt on the next Index Pulse
I1: 1=Interrupt the next time Ready goes to Not Ready
I0: 1=Interrupt the next time Not Ready goes to Ready
All 0: Terminate without interrupt
45FF
EX DE,HL EB
Exchange Register Pairs DE and HL again, restoring DE=37ECH (status) and HL=37EFH (data) for the standard layout.
4600
AND A,9CH E6 9C
Logical AND Register A with 9CH (10011100 binary), keeping bits 7 (Not Ready), 4 (Record Not Found), 3 (CRC Error), and 2 (Lost Data). If any of these are set, the read failed.
4602
JR NZ,4636H 20 32
If the NZ FLAG has been set (any error bit was set), jump forward to 4636H to enter the disk error handler.
4604
LD BC,4300H 01 00 43
Restore Register Pair BC to 4300H (the buffer start) for the caller's convenience, in case BOOTCOPY's loop needs the buffer pointer reset.
4607
EXX D9
Exchange BC,DE,HL with BC',DE',HL', putting the alternate set back where it belongs (BC'=4300H, DE'=37ECH, HL'=37EFH) and freeing the primary registers.
4608
RET C9
RETurn to the caller (45A6H or 45DBH or wherever BOOTCOPY's read was invoked from).

4609H - BOOTCOPY Seek and Drive-Select Subroutine

Internal subroutine of BOOTCOPY equivalent to the boot sector's 4275H-42A1H. Selects drive 0, sets the FDC track and sector registers, and issues a Seek command. Note this is independent of the boot sector's actual 4275H; it serves the BOOTCOPY data flow only.

4609
LD A,(37ECH) 3A EC 37
Load Register A with the byte at memory address 37ECH (the WD1771 FDC Status register). Reads the current FDC status to check ready/error state.
460C
RLCA 07
Rotate Register A left circularly. Bit 7 (Not Ready) becomes bit 0 and the C FLAG. After this, the C FLAG holds the Not Ready bit.
460D
LD A,01H 3E 01
Set Register A to 01H, the drive-0 select mask.
460F
LD (37E1H),A 32 E1 37
Store Register A (=01H) to memory address 37E1H, the Model I drive select latch. Selects drive 0 (the bit pattern 00000001 turns on the drive 0 select line).
4612
JR NC,461FH 30 0B
If the NC FLAG has been set (FDC is ready), jump forward to 461FH to skip the delay loop.
4614
LD BC,0000H 01 00 00
Set Register Pair BC to 0000H, the maximum delay count for the ROM 0060H delay routine (which counts down from BC).
4617
CALL 0060H CD 60 00
GOSUB to 0060H, the Level II BASIC ROM delay routine.
NOTE: 0060H is the Level II BASIC ROM DELAY routine. It loops BC times, providing a calibrated delay. With BC=0000H, this is approximately 65,536 iterations - a long enough wait for an unselected drive to spin up.
461A
LD A,01H 3E 01
Set Register A to 01H again (drive-0 select mask).
461C
LD (37E1H),A 32 E1 37
Store Register A (=01H) to memory address 37E1H, re-asserting drive 0 select. This is necessary because the Model I drive-select latch is a one-shot - it deselects automatically if not refreshed periodically.
461F
LD A,(42DDH) 3A DD 42
Load Register A with the byte at memory address 42DDH (the boot sector's TRACK variable). This still references the original boot sector at 4200H, not BOOTCOPY's own data.
4622
LD (37EFH),A 32 EF 37
Store Register A (the track number) to memory address 37EFH, the FDC Data register. The FDC's Seek command reads the target track from this register.
4625
LD A,(42DEH) 3A DE 42
Load Register A with the byte at memory address 42DEH (the boot sector's SECTOR variable).
4628
LD (37EEH),A 32 EE 37
Store Register A (the sector number) to memory address 37EEH, the FDC Sector register.
462B
LD A,17H 3E 17
Set Register A to 17H, the WD1771 Seek command with stepping rate 11 (15ms/step) and head-load enabled.
1771 FDC Command: 17H (00010111)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
0001hVr1r0Command=Seek
Bit 7-4: Command Code (0001)
h: 1=Enable Head Load/Settle, 0=No delay
V: 1=Verify Destination Track ID, 0=No verification
r1, r0: Stepping Motor Rate (00=3ms, 01=6ms, 10=10ms, 11=15ms)
462D
CALL 42B9H CD B9 42
GOSUB to the boot sector's Send-FDC-Command routine at 42B9H to issue the Seek.
NOTE: 42B9H writes the command in Register A to 37ECH and waits for the FDC busy flag to clear.
4630
CALL 42C7H CD C7 42
GOSUB to the boot sector's Wait-for-FDC-Not-Busy routine at 42C7H, ensuring the Seek has completed before reading status.
NOTE: 42C7H polls the FDC status register at 37ECH until bit 0 (busy) clears.
4633
AND A,98H E6 98
Logical AND Register A (the just-read FDC status) with 98H (10011000 binary), keeping bits 7 (Not Ready), 4 (Seek Error), and 3 (CRC Error). If any are set, the seek failed.
4635
RET Z C8
If the Z FLAG has been set (none of the error bits were set, seek succeeded), RETurn to the caller. Otherwise fall through to the disk-error handler at 4636H.

4636H - BOOTCOPY Disk Error Handler

Internal disk-error handler for BOOTCOPY. Equivalent to the boot sector's 42A2H, but uses BOOTCOPY's own copies of the DISK ERROR and NO MICRODOS messages at 4675H and 4682H. Displays the message, waits for any keypress, and reboots.

4636
LD HL,42DFH 21 DF 42
Set Register Pair HL to 42DFH, the address of the boot sector's DISK ERROR message. Note: this still references the original boot sector at 4200H. HL is the source pointer for the message-print routine.
4639
JR 463EH 18 03
Jump forward to 463EH, skipping the alternate-message-pointer setup. Both DISK ERROR and NO MICRODOS paths converge at 463EH.
463B
LD HL,42ECH 21 EC 42
Alternate entry: set Register Pair HL to 42ECH, the address of the boot sector's NO MICRODOS message. This entry is never reached from BOOTCOPY's own code (no JP/CALL targets 463BH within BOOTCOPY).
463E
CALL 01C9H CD C9 01
GOSUB to the Level II BASIC ROM CLS routine.
NOTE: 01C9H is the Level II BASIC ROM CLS routine. Clears the screen and homes the cursor.
4641
CALL 42D4H CD D4 42
GOSUB to the boot sector's NUL-terminated string print routine at 42D4H to display the message in HL.
NOTE: 42D4H walks the byte stream at HL, calling 0033H for each non-zero byte and returning when a zero byte is reached.
4644
CALL 002BH CD 2B 00
GOSUB to the Level II BASIC ROM keyboard scan routine at 002BH.
NOTE: 002BH is the Level II BASIC ROM KBDSCN routine. Returns Register A with the ASCII code of the pressed key (0 if no key).
4647
OR A,A B7
Logical OR Register A with itself. Side effect: sets Z FLAG if A=0 (no key pressed), NZ otherwise. Used to check whether the user pressed a key.
4648
JR Z,4644H 28 FA
If the Z FLAG has been set (no key pressed), LOOP BACK to 4644H to scan again. This spins until the user presses any key.
464A
JP 0000H C3 00 00
JUMP to 0000H, the Level II BASIC ROM cold-start vector. This reboots the system, including re-reading the boot sector and re-loading the OS.

464DH - BOOTCOPY Send FDC Command Subroutine

Internal Send-FDC-Command subroutine of BOOTCOPY, equivalent to the boot sector's 42B9H. Saves the command, waits for FDC not-busy, restores the command, writes it through the alternate DE' (37ECH), and polls bit 0 (busy) until cleared.

464D
PUSH AF F5
Save Register Pair AF (which holds the command byte in A and the current flags in F) onto the stack. This preserves the command across the wait-for-not-busy call.
464E
CALL 42C7H CD C7 42
GOSUB to the boot sector's Wait-for-FDC-Not-Busy routine at 42C7H.
NOTE: 42C7H polls 37ECH until bit 0 (busy) clears, re-asserting drive select at 37E1H during each poll.
4651
POP AF F1
Restore Register Pair AF from the stack. Register A again holds the command byte.
4652
EXX D9
Exchange BC,DE,HL with BC',DE',HL'. Brings DE' (=37ECH) into the primary DE.
4653
LD (DE),A 12
Store Register A (the command byte) to the address held in Register Pair DE (=37ECH, FDC Command register). This issues the FDC command.
4654
LD A,(DE) 1A
Load Register A with the byte at the address held in Register Pair DE (the FDC status register at 37ECH), reading back the FDC's response.
4655
BIT 0,A CB 47
Test bit 0 of Register A (the busy bit of the FDC status register). If busy, Z FLAG is cleared; if not busy, Z is set.
4657
JR Z,4654H 28 FB
If the Z FLAG has been set (FDC is busy), LOOP BACK to 4654H to re-read status. This is the polling loop until the command has been accepted by the FDC.
4659
EXX D9
Exchange BC,DE,HL with BC',DE',HL' again, restoring the alternate set and freeing the primary registers.
465A
RET C9
RETurn to the caller (which is 45E5H or 462DH within BOOTCOPY).

465BH - BOOTCOPY Wait for FDC Not Busy Subroutine

Internal wait-for-FDC-not-busy loop, equivalent to the boot sector's 42C7H. Re-asserts drive 0 select on each iteration to defeat the Model I one-shot drive-select latch.

465B
EXX D9
Exchange BC,DE,HL with BC',DE',HL'. Brings DE' (=37ECH) into primary DE for the FDC status read.
465C
LD A,01H 3E 01
Set Register A to 01H, the drive-0 select mask.
465E
LD (37E1H),A 32 E1 37
Store Register A (=01H) to memory address 37E1H, re-asserting drive 0 select. Necessary because the latch is a one-shot.
4661
LD A,(DE) 1A
Load Register A with the byte at the address held in Register Pair DE (the FDC status register at 37ECH).
4662
BIT 0,A CB 47
Test bit 0 of Register A (the busy bit). If busy, Z FLAG is cleared (NZ); if idle, Z is set.
4664
JR NZ,465CH 20 F6
If the NZ FLAG has been set (FDC is still busy), LOOP BACK to 465CH to re-assert drive select and re-read status.
4666
EXX D9
Exchange BC,DE,HL with BC',DE',HL', restoring the alternate set.
4667
RET C9
RETurn to the caller.

4668H - BOOTCOPY Print NUL-Terminated String Subroutine

Internal NUL-terminated string print routine, equivalent to the boot sector's 42D4H. Walks bytes pointed at by HL, displays each via 0033H, and returns on a zero byte.

4668
LD A,(HL) 7E
Load Register A with the byte at the address held in Register Pair HL (the next character of the string).
4669
OR A,A B7
Logical OR Register A with itself, setting Z FLAG if A=0 (NUL terminator) and NZ otherwise.
466A
RET Z C8
If the Z FLAG has been set (NUL byte found), RETurn to the caller. The string has been fully displayed.
466B
CALL 0033H CD 33 00
GOSUB to the Level II BASIC ROM character output routine.
NOTE: 0033H is the Level II BASIC ROM VDCHAR routine. Displays the character in Register A at the cursor position and advances the cursor.
466E
INC HL 23
INCrement Register Pair HL by 1, advancing the string pointer.
466F
JR 4668H 18 F7
LOOP BACK to 4668H to read and display the next character.

4671H - BOOTCOPY Data Block (DATA)

29 bytes of data containing the BOOTCOPY's own copies of the DISK ERROR and NO MICRODOS messages, plus the $STEPL residual symbol. These bytes are reachable by the BOOTCOPY error path (which actually targets 42xxH addresses, so these copies are never read at runtime; they exist as a build-time consequence of copying the entire boot sector).

4671
DB 00H 00
One pad byte (00H), separating the BOOTCOPY code from the data block.
4672-4674
DB 01H,17H,0E8H 01 17 E8
Three bytes 01 17 E8. The 17H E8H sequence is the screen-control prefix used by the original boot sector's DISK ERROR message (HOME cursor + cursor-position byte 0E8H). The leading 01H is unused at runtime.
4675-467F
DEFM "DISK ERROR",00H 44 49 53 4B 20 45 52 52 4F 52 00
The string "DISK ERROR" (10 bytes) followed by the NUL terminator at 467FH. This is a verbatim copy of the boot sector's 42E1H-42EBH message, but BOOTCOPY's error handler at 4636H references 42DFH (the boot sector copy) instead of this one, so this copy is never displayed.
4680-468DH
DEFM 17H,0E8H,"NO MICRODOS",00H 17 E8 4E 4F 20 4D 49 43 52 4F 44 4F 53 00
The "NO MICRODOS" message (11 bytes) preceded by the 17H E8H screen-control prefix and followed by the NUL terminator at 468DH. Unused at runtime for the same reason as DISK ERROR.
468E-4693
DEFM "$STEPL" 24 53 54 45 50 4C
Six bytes spelling "$STEPL", a residual source-listing label that ran past the end of the boot sector's last DEFM directive. Companion to the boot sector's "$STEPH". Never read at runtime.

4694H - JTABLE - BASIC Token Dispatch Table (DATA)

A 147-byte table of 49 JP instructions (3 bytes each = C3 lo hi). The init routine at 4741H-474BH copies this entire table to RAM at 4152H, where it overlaps the BASIC RST 28H hook addresses (400CH, 4015H) and provides MicroDOS keyword dispatch. Each entry corresponds to one BASIC reserved-word index. Entries that point to JP 0043H or JP 012DH are no-op slots reserved for keywords that MicroDOS does not implement (the original Level II BASIC handler handles them). Entries pointing into the 4940H-4ECBH range are MicroDOS handlers.

4694
JP 4E25H C3 25 4E
Token entry 0: JP to MicroDOS error code 4E25H.
4697
JP 4E43H C3 43 4E
Token entry 1: JP to MicroDOS error code 4E43H.
469A
JP 4E28H C3 28 4E
Token entry 2: JP to MicroDOS error code 4E28H.
469D
JP 4E3DH C3 3D 4E
Token entry 3: JP to MicroDOS error code 4E3DH.
46A0
JP 4E2BH C3 2B 4E
Token entry 4: JP to MicroDOS error code 4E2BH.
46A3
JP 012DH C3 2D 01
Token entry 5: JP to ROM 012DH (BASIC passthrough).
NOTE: 012DH is in the Level II BASIC ROM area; on entry, this skips to the original BASIC handler for the corresponding keyword.
46A6
JP 4E4FH C3 4F 4E
Token entry 6: JP to MicroDOS error code 4E4FH.
46A9
JP 4E4CH C3 4C 4E
Token entry 7: JP to MicroDOS error code 4E4CH.
46AC
JP 4E1CH C3 1C 4E
Token entry 8: JP to MicroDOS error code 4E1CH.
46AF
JP 4E1FH C3 1F 4E
Token entry 9: JP to MicroDOS error code 4E1FH.
46B2
JP 4E22H C3 22 4E
Token entry 10: JP to MicroDOS error code 4E22H.
46B5
JP 47C9H C3 C9 47
Token entry 11: JP to BASTOK at 47C9H (CMD/CALL token dispatcher with single-letter argument).
46B8
JP 012DH C3 2D 01
Token entry 12: JP to ROM 012DH (NAVERR: loads error code 2CH and jumps to the BASIC error handler at 19A2H).
46BB
JP 012DH C3 2D 01
Token entry 13: JP to ROM 012DH (NAVERR: loads error code 2CH and jumps to the BASIC error handler at 19A2H).
46BE
JP 4E19H C3 19 4E
Token entry 14: JP to MicroDOS error code 4E19H.
46C1
JP 4AAEH C3 AE 4A
Token entry 15: JP to OPENRD (read sector handler) at 4AAEH.
46C4
JP 4AE4H C3 E4 4A
Token entry 16: JP to write-sector handler at 4AE4H.
46C7
JP 012DH C3 2D 01
Token entry 17: JP to ROM 012DH (NAVERR: loads error code 2CH and jumps to the BASIC error handler at 19A2H).
46CA
JP 49AEH C3 AE 49
Token entry 18: JP to OPEN-existing-file handler at 49AEH.
46CD
JP 49F6H C3 F6 49
Token entry 19: JP to CREATE-file handler at 49F6H.
46D0
JP 012DH C3 2D 01
Token entry 20: JP to ROM 012DH (NAVERR: loads error code 2CH and jumps to the BASIC error handler at 19A2H).
46D3
JP 012DH C3 2D 01
Token entry 21: JP to ROM 012DH (NAVERR: loads error code 2CH and jumps to the BASIC error handler at 19A2H).
46D6
JP 4E3AH C3 3A 4E
Token entry 22: JP to MicroDOS error code 4E3AH.
46D9
JP 4E2EH C3 2E 4E
Token entry 23: JP to MicroDOS error code 4E2EH.
46DC
JP 4E31H C3 31 4E
Token entry 24: JP to MicroDOS error code 4E31H.
46DF
JP 4E58H C3 58 4E
Token entry 25: JP to MicroDOS error code 4E58H.
46E2
JP 4941H C3 41 49
Token entry 26: JP to OPEN-file handler entry at 4941H.
46E5
JP 4E37H C3 37 4E
Token entry 27: JP to MicroDOS error code 4E37H.
46E8
JP 4E34H C3 34 4E
Token entry 28: JP to MicroDOS error code 4E34H.
46EB
JP 4E40H C3 40 4E
Token entry 29: JP to MicroDOS error code 4E40H.
46EE
JP 0043H C3 43 00
Token entry 30: JP to ROM 0043H (VDPRT video output trampoline).
NOTE: 0043H is the Level II BASIC ROM VDPRT entry. It jumps to 0434H to print the character held in Register C. Used here as a default placeholder slot for BASIC reserved-word indices that MicroDOS does not override; if the BASIC dispatcher falls through to one of these entries, the resulting behavior depends on the C register at the time.
46F1
JP 0043H C3 43 00
Token entry 31: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
46F4
JP 0043H C3 43 00
Token entry 32: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
46F7
JP 0043H C3 43 00
Token entry 33: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
46FA
JP 0043H C3 43 00
Token entry 34: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
46FD
JP 4E49H C3 49 4E
Token entry 35: JP to MicroDOS error code 4E49H.
4700
JP 0043H C3 43 00
Token entry 36: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4703
JP 0043H C3 43 00
Token entry 37: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4706
JP 0043H C3 43 00
Token entry 38: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4709
JP 0043H C3 43 00
Token entry 39: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
470C
JP 0043H C3 43 00
Token entry 40: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
470F
JP 0043H C3 43 00
Token entry 41: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4712
JP 0043H C3 43 00
Token entry 42: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4715
JP 0043H C3 43 00
Token entry 43: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4718
JP 0043H C3 43 00
Token entry 44: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
471B
JP 4E55H C3 55 4E
Token entry 45: JP to MicroDOS error code 4E55H.
471E
JP 0043H C3 43 00
Token entry 46: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4721
JP 0043H C3 43 00
Token entry 47: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4724
JP 0043H C3 43 00
Token entry 48: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).

Track 0 / Sector 4 (46FDH - 47FBH) - boundary falls within preceding JTABLE rows at 46EE-4724

4727H - INIT - MicroDOS Initialization

The first executable code of the resident OS, reached via JP 4727H from the entry trampoline at 4400H. Performs five jobs in sequence: (1) copies 39 bytes of post-error-table data from the Level II BASIC ROM at 18F7H to RAM at 4080H; (2) sets up a 3-byte separator template at 41E5H and points 40A7H at it; (3) copies the 147-byte JTABLE from 4694H down to RAM at 4152H; (4) initializes the byte-storage and string-storage pointers at 5A64H and 5A00H; (5) relocates the stack to 41F8H, prints the user banner via 28A7H, hooks the keyboard input vector at 4016H to KBDHOOK at 478FH, sets the AUTOPTR at 4526H to the auto-load string at 44A5H, initializes FCB defaults, and JPs to BASIC's 00B5H.

4727
LD DE,4080H 11 80 40
Set Register Pair DE to 4080H, the destination address for the relocated post-error-table region. This is just below the Level II BASIC RAM workspace. DE will be the destination of the upcoming LDIR.
472A
LD HL,18F7H 21 F7 18
Set Register Pair HL to 18F7H, the source address (one past the end of the Level II BASIC ROM error message table at 18C9H-18F6H). HL is the source for the LDIR.
472D
LD BC,0027H 01 27 00
Set Register Pair BC to 0027H (=39 decimal), the byte count for the upcoming LDIR.
4730
LDIR ED B0
Block-move BC bytes from (HL) to (DE), incrementing HL and DE on each byte. Copies 39 bytes from 18F7H to 4080H. After the copy, HL=191EH, DE=40A7H, BC=0.
4732
LD HL,41E5H 21 E5 41
Set Register Pair HL to 41E5H, the address of the comma-separator template buffer. HL is the destination for the next 3 byte-stores.
4735
LD (HL),3AH 36 3A
Set the byte at the address held in Register Pair HL (=41E5H) to 3AH (the ASCII colon ':'). This is the BASIC statement separator.
4737
INC HL 23
INCrement Register Pair HL by 1, advancing to 41E6H.
4738
LD (HL),00H 36 00
Set the byte at the address held in Register Pair HL (=41E6H) to 00H (NUL terminator for the colon-only single-character "string").
473A
INC HL 23
INCrement Register Pair HL by 1, advancing to 41E7H.
473B
LD (HL),2CH 36 2C
Set the byte at the address held in Register Pair HL (=41E7H) to 2CH (the ASCII comma ','). Used as a different separator marker for argument-list parsing.
473D
INC HL 23
INCrement Register Pair HL by 1, advancing to 41E8H (one past the end of the 3-byte template).
473E
LD (40A7H),HL 22 A7 40
Store Register Pair HL (=41E8H) to memory address 40A7H, the comma-separator template pointer used by the argument parser at 4B59H to recognize statement separators.
4741
LD DE,4152H 11 52 41
Set Register Pair DE to 4152H, the destination address for the relocated dispatch table. This address is chosen so that the table entries land on top of the BASIC RST 28H hooks at 400CH-4015H, converting them into MicroDOS DOS-call entries.
4744
LD HL,4694H 21 94 46
Set Register Pair HL to 4694H, the source address of the JTABLE (the 147-byte dispatch table inside the resident image).
4747
LD BC,0093H 01 93 00
Set Register Pair BC to 0093H (=147 decimal), the byte count for the upcoming LDIR. 147 = 49 entries x 3 bytes = the entire JTABLE.
474A
LDIR ED B0
Block-move BC bytes from (HL) to (DE). Copies the 147-byte JTABLE from 4694H to 4152H. After the copy, the dispatch table is in RAM and can be patched if needed.
474C
LD HL,5A64H 21 64 5A
Set Register Pair HL to 5A64H, the start address of the byte-storage area for compiled BASIC variables.
474F
LD (40A0H),HL 22 A0 40
Store Register Pair HL (=5A64H) to memory address 40A0H, the byte-storage pointer.
4752
LD HL,5A00H 21 00 5A
Set Register Pair HL to 5A00H, the start address of the string-storage area.
4755
LD (HL),00H 36 00
Set the byte at the address held in Register Pair HL (=5A00H) to 00H, the end-of-string sentinel marking an empty string-storage area.
4757
INC HL 23
INCrement Register Pair HL by 1, advancing to 5A01H (the first byte after the sentinel).
4758
LD (40A4H),HL 22 A4 40
Store Register Pair HL (=5A01H) to memory address 40A4H, the string-storage pointer.
475B
LD SP,41F8H 31 F8 41
Set Register Pair SP to 41F8H, relocating the stack from the boot sector's 41FCH down by 4 bytes.
475E
CALL 1B8FH CD 8F 1B
GOSUB to ROM 1B8FH.
NOTE: 1B8FH is the Level II BASIC ROM STKINI routine. Initializes BASIC pointers, stack, and variables.
4761
LD HL,4528H 21 28 45
Set Register Pair HL to 4528H, the address of the user banner string "PERCOM MICRODOS VERSION 2.20\rCOPYRIGHT (C) 1979 PERCOM DATA COMPANY\rALL RIGHTS RESERVED\r". Note: HL = 4528H, not 4527H, so the leading byte at 4527H (which is the high byte of the AUTOPTR pointer) is skipped.
4764
CALL 28A7H CD A7 28
GOSUB to the Level II BASIC ROM string-output routine.
NOTE: 28A7H is the Level II BASIC ROM OUTSTR routine. Outputs a string starting at HL, terminated by either a 00H NUL or a 22H quote. Uses the device selected by 409CH (defaults to video).
4767
LD HL,478FH 21 8F 47
Set Register Pair HL to 478FH, the entry point of the KBDHOOK keyboard input handler. HL is the value to be installed in the keyboard input vector.
476A
LD (4016H),HL 22 16 40
Store Register Pair HL (=478FH) to memory address 4016H, the BASIC keyboard input vector. After this store, every CALL to (4016H) from BASIC's keyboard scanning code will land at 478FH (KBDHOOK).
476D
LD HL,44A5H 21 A5 44
Set Register Pair HL to 44A5H, the start of the auto-load command string "LOAD 30,R\r" (with leading 0DH).
4770
LD (4526H),HL 22 26 45
Store Register Pair HL (=44A5H) to memory address 4526H, the AUTOPTR. Initialized to point at the first byte of the auto-load string.
4773
LD HL,0FFFFH 21 FF FF
Set Register Pair HL to FFFFH (the all-bits-set value). Used to initialize FCB self-modifying-code locations to "no FCB allocated" markers.
4776
LD (4497H),HL 22 97 44
Store Register Pair HL (=FFFFH) to memory address 4497H, setting the step-rate selector entries 4497H-4498H to FFFFH.
4779
LD (4499H),HL 22 99 44
Store Register Pair HL (=FFFFH) to memory address 4499H, setting the step-rate selector entries 4499H-449AH to FFFFH. After both stores, all four entries 4497H-449AH hold FFH.
477C
INC HL 23
INCrement Register Pair HL by 1. Since HL was FFFFH, the increment wraps to 0000H. HL=0000H is needed for the next store.
477D
LD (448DH),HL 22 8D 44
Store Register Pair HL (=0000H) to memory address 448DH, clearing the SECTOR variable (track byte at 448DH and sector byte at 448EH) to 0000H.
4780
LD A,01H 3E 01
Set Register A to 01H, the default drive-select mask (drive 0).
4782
LD (448CH),A 32 8C 44
Store Register A (=01H) to memory address 448CH, the drive-select mask. Selects drive 0 by default.
4785
XOR A,A AF
XOR Register A with itself, setting Register A to 00H and clearing all flags.
4786
LD (4495H),A 32 95 44
Store Register A (=00H) to memory address 4495H, clearing the FDC saved status.
4789
LD (4496H),A 32 96 44
Store Register A (=00H) to memory address 4496H, clearing the FORMAT checksum accumulator.
478C
JP 00B5H C3 B5 00
JUMP to ROM 00B5H, the Level II BASIC cold-start entry point.
NOTE: 00B5H is the Level II BASIC cold-start entry that goes directly to the BASIC READY prompt. At this point, the screen shows the MicroDOS banner followed by READY, and the keyboard hook at 4016H is feeding the auto-load string "LOAD 30,R" to BASIC.

478FH - KBDHOOK - Keyboard Input Hook

Replacement for the BASIC keyboard input call at (4016H), installed by the init routine at 4767H-476CH. Reads one byte from the AUTOPTR location at 4526H, increments AUTOPTR, and returns the byte to BASIC as if the user had typed it. When the byte read is NUL, the hook re-points 4016H to point at KBDSCAN at 47A3H.

478F
LD DE,(4526H) ED 5B 26 45
Load Register Pair DE with the 16-bit value at memory address 4526H (the AUTOPTR, currently pointing at the next byte of the auto-load string).
4793
LD A,(DE) 1A
Load Register A with the byte at the address held in Register Pair DE (the next character of the auto-load string).
4794
INC DE 13
INCrement Register Pair DE by 1, advancing the AUTOPTR to the next character.
4795
LD (4526H),DE ED 53 26 45
Store Register Pair DE (the new AUTOPTR value) to memory address 4526H, updating AUTOPTR for the next call.
4799
OR A,A B7
Logical OR Register A with itself, setting Z FLAG if A=0 (NUL terminator) or NZ otherwise.
479A
RET NZ C0
If the NZ FLAG has been set (a non-NUL character was read), RETurn to the caller, with Register A holding the character.
479B
LD DE,47A3H 11 A3 47
Set Register Pair DE to 47A3H, the entry point of KBDSCAN.
479E
LD (4016H),DE ED 53 16 40
Store Register Pair DE (=47A3H) to memory address 4016H, re-pointing the keyboard input vector from KBDHOOK to KBDSCAN.
47A2
RET C9
RETurn to the caller (BASIC). Register A holds 00H (NUL).

47A3H - KBDSCAN - Live Keyboard Scanner

The live keyboard scanner, installed at 4016H once the auto-load string has been exhausted. Walks through the 8 keyboard memory rows by left-rotating Register C from 01H through 80H: 3801H, 3802H, 3804H, 3808H, 3810H, 3820H, 3840H, 3880H. For each row, reads the keyboard memory, computes the bit-mask of newly-pressed keys (XORing with the previous-frame state), and either continues scanning (if no keys are newly pressed) or transfers control to the Level II BASIC ROM key-decode routine at 03FBH with the row number in Register D and the keypress mask in Register A.

47A3
LD HL,4036H 21 36 40
Set Register Pair HL to 4036H, the start of the BASIC keyboard-state array (8 bytes, one per keyboard row). Each byte holds the previous-frame state of keys in that row, used to detect key transitions.
47A6
LD BC,3801H 01 01 38
Set Register Pair BC to 3801H, where B=38H (high byte of keyboard row addresses) and C=01H (the bit mask for row 0). BC is used directly as the keyboard memory address.
47A9
LD D,00H 16 00
Set Register D to 00H, the row counter.
47AB
LD A,(BC) 0A
Load Register A with the byte at the address held in Register Pair BC (the keyboard memory at the current row).
47AC
LD E,A 5F
Copy Register A (the just-read keyboard state) to Register E, preserving it for the AND below.
47AD
XOR A,(HL) AE
XOR Register A with the byte at the address held in Register Pair HL (the previous-frame state for this row). After the XOR, A holds the bit-mask of keys that have changed state.
47AE
LD (HL),E 73
Store Register E (the just-read state) to the address held in Register Pair HL, updating the previous-frame state for this row.
47AF
AND A,E A3
Logical AND Register A (changed bits) with Register E (current state), giving the bit-mask of keys that just transitioned from up to down (newly pressed).
47B0
JR NZ,47BAH 20 08
If the NZ FLAG has been set (any key was newly pressed in this row), jump forward to 47BAH to debounce and dispatch.
47B2
INC D 14
INCrement Register D by 1, advancing the row counter.
47B3
INC L 2C
INCrement Register L by 1. HL was 4036H+row, now becomes 4037H+row (next byte of the previous-state array).
47B4
RLC C CB 01
Rotate Register C left circularly. Each iteration doubles the value of C, walking through the keyboard row addresses 3801H, 3802H, 3804H, 3808H, 3810H, 3820H, 3840H, 3880H.
47B6
JP P,47ABH F2 AB 47
If the P FLAG has been set (Register C does not have bit 7 set), LOOP BACK to 47ABH to scan the next row. Continues until C=80H (after the RLC, the result has bit 7 still 0 because it was 1 in the previous iteration; the sign flag becomes set when the next RLC pushes the 1 out into the carry).
47B9
RET C9
RETurn to the caller (BASIC) with Register A=0 indicating no key was pressed.
47BA
LD E,A 5F
Copy Register A (the newly-pressed bit-mask) to Register E, preserving it across the upcoming debounce delay.
47BB
PUSH BC C5
Save Register Pair BC (the keyboard row address) onto the stack, preserving it across the delay.
47BC
LD BC,04E2H 01 E2 04
Set Register Pair BC to 04E2H (=1250 decimal), a debounce delay count for the ROM 0060H delay routine.
47BF
CALL 0060H CD 60 00
GOSUB to the Level II BASIC ROM DELAY routine.
NOTE: 0060H is the Level II BASIC ROM DELAY routine. Loops BC times, providing a calibrated delay (debounce here).
47C2
POP BC C1
Restore Register Pair BC (the keyboard row address) from the stack.
47C3
LD A,(BC) 0A
Load Register A with the byte at the address held in Register Pair BC (re-read the keyboard row after the debounce delay).
47C4
AND A,E A3
Logical AND Register A (current state after debounce) with Register E (the previously detected newly-pressed mask). If the key is still pressed, the result is non-zero.
47C5
RET Z C8
If the Z FLAG has been set (the key was released during debounce, i.e. it was a glitch), RETurn to the caller with A=0.
47C6
JP 03FBH C3 FB 03
JUMP to ROM 03FBH, the Level II BASIC ROM key-decode dispatch.
NOTE: 03FBH is the Level II BASIC ROM keyboard key-decode and dispatch routine. Entry: A = keypress bitmask, D = row number. Returns the ASCII code of the pressed key in Register A.

47C9H - BASTOK - BASIC Token Dispatcher

Reached via JTABLE entry 11 (4152H+33 = 4175H, which is C3 C9 47 = JP 47C9H) when BASIC encounters the CMD/CALL token followed by a string argument. Reads the first character of the string argument, compares it against F (FETCH), I (INPUT), H (HEX), K (KILL), and M (MERGE), and JPs to the matching handler at 47F2H, 4818H, 4802H, 482BH, or 4841H respectively. Default exit is JP 1E4AH (Illegal Function Call).

47C9
CALL 2337H CD 37 23
GOSUB to the Level II BASIC ROM general parameter parser.
NOTE: 2337H gets a general (string, integer, single, or double precision) parameter in the accumulator and sets the type flag (40AFH) accordingly.
47CC
PUSH HL E5
Save Register Pair HL onto the stack (the BASIC parser's pointer just past the parameter, needed for return).
47CD
CALL 29D7H CD D7 29
GOSUB to ROM 29D7H.
NOTE: 29D7H is the Level II BASIC ROM READY-related routine. After the parameter parser, this prepares the string argument for processing.
47D0
LD A,(HL) 7E
Load Register A with the byte at the address held in Register Pair HL (the length byte of the BASIC string descriptor).
47D1
DEC A 3D
DECrement Register A by 1. If the string was exactly 1 character long, A is now 0; if longer, A is non-zero.
47D2
JP NZ,1997H C2 97 19
If the NZ FLAG has been set (string is not exactly 1 character), JUMP to ROM 1997H to display Syntax Error.
NOTE: 1997H is the Level II BASIC ROM SN ERROR routine.
47D5
INC HL 23
INCrement Register Pair HL by 1, advancing past the length byte to the string-pointer-low byte.
47D6
LD E,(HL) 5E
Load Register E with the byte at the address held in Register Pair HL (the low byte of the string data pointer).
47D7
INC HL 23
INCrement Register Pair HL by 1, advancing to the string-pointer-high byte.
47D8
LD D,(HL) 56
Load Register D with the byte at the address held in Register Pair HL (the high byte of the string data pointer). After this, DE points at the actual string character data.
47D9
LD A,(DE) 1A
Load Register A with the byte at the address held in Register Pair DE (the first - and only - character of the BASIC string argument).
47DA
POP HL E1
Restore Register Pair HL from the stack (the BASIC parser pointer for resume).
47DB
CP A,46H FE 46
Compare Register A against 46H (ASCII 'F'). If A='F', Z FLAG is set.
47DD
JR Z,47F2H 28 13
If the Z FLAG has been set (character is 'F'), jump forward to 47F2H (the FETCH handler).
47DF
CP A,49H FE 49
Compare Register A against 49H (ASCII 'I'). If A='I', Z FLAG is set.
47E1
JR Z,4818H 28 35
If the Z FLAG has been set (character is 'I'), jump forward to 4818H (the INPUT handler).
47E3
CP A,48H FE 48
Compare Register A against 48H (ASCII 'H'). If A='H', Z FLAG is set.
47E5
JR Z,4802H 28 1B
If the Z FLAG has been set (character is 'H'), jump forward to 4802H (the HEX handler).
47E7
CP A,4BH FE 4B
Compare Register A against 4BH (ASCII 'K'). If A='K', Z FLAG is set.
47E9
JR Z,482BH 28 40
If the Z FLAG has been set (character is 'K'), jump forward to 482BH (the KILL handler).
47EB
CP A,4DH FE 4D
Compare Register A against 4DH (ASCII 'M'). If A='M', Z FLAG is set.
47ED
JR Z,4841H 28 52
If the Z FLAG has been set (character is 'M'), jump forward to 4841H (the MERGE handler).
47EF
JP 1E4AH C3 4A 1E
JUMP to ROM 1E4AH (Illegal Function Call) for any unrecognized command character.
NOTE: 1E4AH is the Level II BASIC ROM FCERR routine, which prints "FC Error".

47F2H - FETCH Command (CMD"F")

Handles CMD"F" (FETCH). Skips a comma, parses an address argument via 4B3FH, validates it via RST 10H, then calls 4870H (the file-load routine) to fetch a file at the parsed address.

47F2
RST 08H => 2CH CF 2C
Syntax check via RST 08H, requiring a comma (2CH) at the BASIC parser pointer.
NOTE: RST 08H is the Level II BASIC ROM SYNTAX check. HL points to byte to be checked; the proper byte (2CH = ',') follows the RST 08H instruction.
47F4
CALL 4B3FH CD 3F 4B
GOSUB to 4B3FH (the FILESPEC parser at the # branch entry, equivalent to the address-parse path).
47F7
DEC HL 2B
DECrement Register Pair HL by 1, backing up the BASIC parser pointer by one byte.
47F8
RST 10H D7
RST 10H, advance the BASIC text pointer past whitespace, returning the next non-blank character in Register A.
NOTE: RST 10H advances HL past spaces and tabs, returning the next significant byte.
47F9
JP NZ,1997H C2 97 19
If the NZ FLAG has been set (more characters found, but the FETCH command takes only the address argument), JUMP to ROM 1997H to display Syntax Error.
47FC
PUSH HL E5
Save Register Pair HL onto the stack (BASIC parser pointer).
47FD
CALL 4870H CD 70 48
GOSUB to FILEIO at 4870H, the file-load routine.
4800
POP HL E1
Restore Register Pair HL from the stack.
4801
RET C9
RETurn to the BASIC dispatcher.

Track 0 / Sector 5 (47FCH - 48FAH) - boundary falls within preceding FETCH block at 47FCH (PUSH HL row)

4802H - HEX Command (CMD"H")

Handles CMD"H" (HEX dump or HEX-related operation). Calls 2337H to fetch a parameter, validates with RST 10H, parses additional context via 4851H.

4802
RST 08H => 2CH CF 2C
Syntax check requiring a comma (2CH) following the H command character.
4804
CALL 2337H CD 37 23
GOSUB to the Level II BASIC ROM general parameter parser to fetch the next argument.
4807
DEC HL 2B
DECrement Register Pair HL by 1, backing the BASIC parser pointer.
4808
RST 10H D7
RST 10H, advancing past whitespace.
4809
JP NZ,1997H C2 97 19
If the NZ FLAG has been set (extra characters), JUMP to 1997H (Syntax Error).
480C
PUSH HL E5
Save Register Pair HL onto the stack.
480D
CALL 29D7H CD D7 29
GOSUB to ROM 29D7H to prepare the string argument.
4810
LD DE,440BH 11 0B 44
Set Register Pair DE to 440BH (a position within the symbol table region used as a default destination).
4813
CALL 4851H CD 51 48
GOSUB to 4851H, the string-copy-to-destination routine.
4816
POP HL E1
Restore Register Pair HL from the stack.
4817
RET C9
RETurn to the BASIC dispatcher.

4818H - INPUT Command (CMD"I")

Handles CMD"I" (INPUT-related, file-input). Same general pattern as FETCH but additionally calls 491BH after the file load to perform the BASIC line-pointer relink.

4818
RST 08H => 2CH CF 2C
Syntax check requiring a comma (2CH).
481A
CALL 4B3FH CD 3F 4B
GOSUB to 4B3FH (FILESPEC parser at # branch).
481D
DEC HL 2B
DECrement Register Pair HL by 1.
481E
RST 10H D7
RST 10H, advancing past whitespace.
481F
JP NZ,1997H C2 97 19
If NZ, Syntax Error at 1997H.
4822
PUSH HL E5
Save Register Pair HL onto the stack.
4823
CALL 4870H CD 70 48
GOSUB to FILEIO at 4870H to load the file.
4826
CALL 491BH CD 1B 49
GOSUB to 491BH, which performs additional initialization (including line-pointer relinking via 4BF6H).
4829
POP HL E1
Restore Register Pair HL from the stack.
482A
RET C9
RETurn to the BASIC dispatcher.

482BH - KILL Command (CMD"K")

Handles CMD"K" (KILL/delete file). Uses the symbol table at 44A5H as a destination buffer (the auto-load string area is reused after init since the auto-load completes before the user issues commands).

482B
RST 08H => 2CH CF 2C
Syntax check requiring a comma (2CH).
482D
CALL 2337H CD 37 23
GOSUB to the parameter parser at 2337H.
4830
DEC HL 2B
DECrement Register Pair HL by 1.
4831
RST 10H D7
RST 10H, advancing past whitespace.
4832
JP NZ,1997H C2 97 19
If NZ, Syntax Error.
4835
PUSH HL E5
Save Register Pair HL onto the stack.
4836
CALL 29D7H CD D7 29
GOSUB to ROM 29D7H to prepare the string argument.
4839
LD DE,44A5H 11 A5 44
Set Register Pair DE to 44A5H, the start of the auto-load string area, reused as a temporary buffer.
483C
CALL 4851H CD 51 48
GOSUB to 4851H, the string-copy-to-destination routine.
483F
POP HL E1
Restore Register Pair HL from the stack.
4840
RET C9
RETurn to the BASIC dispatcher.

4841H - MERGE Command (CMD"M")

Handles CMD"M" (MERGE). Loads a file using FILEIO at 4870H, then performs MERGE-style line-pointer integration via 491BH.

4841
RST 08H => 2CH CF 2C
Syntax check requiring a comma (2CH).
4843
CALL 4B3FH CD 3F 4B
GOSUB to 4B3FH (FILESPEC parser).
4846
DEC HL 2B
DECrement Register Pair HL by 1.
4847
RST 10H D7
RST 10H, advancing past whitespace.
4848
JP NZ,1997H C2 97 19
If NZ, Syntax Error.
484B
PUSH HL E5
Save Register Pair HL onto the stack.
484C
CALL 491BH CD 1B 49
GOSUB to 491BH (file-load and line-pointer relink combined).
484F
POP HL E1
Restore Register Pair HL from the stack.
4850
RET C9
RETurn to the BASIC dispatcher.

4851H - String-Copy-To-Destination Helper

Copies a BASIC string descriptor's data into a destination buffer at DE. HL points at the BASIC string descriptor (length byte + 2-byte data pointer). Caps the length at 80H (128 bytes). Translates 0AH (line feed) to 0DH (carriage return) during the copy. NUL-terminates the destination.

4851
LD A,(HL) 7E
Load Register A with the byte at the address held in Register Pair HL (the length byte of the BASIC string descriptor).
4852
OR A,A B7
Logical OR Register A with itself, setting Z FLAG if length=0, NZ otherwise.
4853
JR Z,486DH 28 18
If the Z FLAG has been set (string is empty), jump forward to 486DH to write the NUL terminator and exit.
4855
CP A,80H FE 80
Compare Register A against 80H (=128). If length >= 128, C FLAG is cleared.
4857
JR C,485BH 38 02
If the C FLAG has been set (length < 128), jump forward to 485BH to use the length as-is.
4859
LD A,80H 3E 80
Set Register A to 80H, capping the length at 128 bytes.
485B
LD B,A 47
Copy Register A (the capped length) into Register B, the loop counter for DJNZ.
485C
INC HL 23
INCrement Register Pair HL by 1, advancing past the length byte to the string-pointer-low.
485D
LD A,(HL) 7E
Load Register A with the byte at HL (the low byte of the string data pointer).
485E
INC HL 23
INCrement HL by 1, advancing to the string-pointer-high byte.
485F
LD H,(HL) 66
Load Register H with the byte at HL (the high byte of the string data pointer).
4860
LD L,A 6F
Copy Register A (the saved low byte) into Register L. HL now holds the address of the string data.
4861
LD A,(HL) 7E
Load Register A with the byte at HL (the next character of the string data).
4862
CP A,0AH FE 0A
Compare Register A against 0AH (line feed).
4864
JR NZ,4868H 20 02
If the NZ FLAG has been set (not a line feed), jump forward to 4868H to store the byte as-is.
4866
LD A,0DH 3E 0D
Set Register A to 0DH (carriage return), translating LF to CR.
4868
LD (DE),A 12
Store Register A to the address held in DE (the destination buffer).
4869
INC DE 13
INCrement DE by 1, advancing the destination pointer.
486A
INC HL 23
INCrement HL by 1, advancing the source pointer.
486B
DJNZ 4861H 10 F4
DECrement B and LOOP BACK to 4861H if not zero, continuing the copy.
486D
XOR A,A AF
Set Register A to 00H and clear all flags.
486E
LD (DE),A 12
Store Register A (=00H) to (DE), writing the NUL terminator at the end of the destination buffer.
486F
RET C9
RETurn to the caller (the FETCH/HEX/INPUT/KILL/MERGE handler).

4870H - FILEIO - File Format/Initialize Routine

Reached from the FETCH and INPUT command handlers at 47FDH and 4823H. The name FILEIO is a misnomer in the original continuation file - this is actually the FORMAT-and-write routine, used to lay down a chain of sectors. Clears the SECTOR variable at 448DH-448EH, fetches a record byte count from RECNO at 4492H, calls FORMAT at 4C91H repeatedly, and uses the verify routine at 48BCH to confirm written data.

4870
LD HL,448DH 21 8D 44
Set Register Pair HL to 448DH, the address of the SECTOR variable (track byte at 448DH, sector byte at 448EH).
4873
XOR A,A AF
Set Register A to 00H and clear all flags.
4874
LD (HL),A 77
Store Register A (=00H) to the address held in Register Pair HL (=448DH), clearing the track byte.
4875
INC HL 23
INCrement Register Pair HL by 1, advancing to 448EH (sector byte).
4876
LD (HL),A 77
Store Register A (=00H) to the address held in Register Pair HL (=448EH), clearing the sector byte.
4877
DEC HL 2B
DECrement Register Pair HL by 1, restoring HL to 448DH.
4878
LD DE,(4492H) ED 5B 92 44
Load Register Pair DE with the 16-bit value at memory address 4492H (RECNO, the record byte count / records-per-track).
487C
LD (4496H),A 32 96 44
Store Register A (still =00H) to memory address 4496H, clearing the FORMAT checksum accumulator.
487F
LD B,E 43
Copy Register E (low byte of RECNO) into Register B, the FORMAT outer-loop counter.
4880
CALL 4C91H CD 91 4C
Loop Start
GOSUB to FORMAT at 4C91H, which lays down sector ID fields and data fields for one track.
4883
INC (HL) 34
INCrement the byte at the address held in Register Pair HL (=448DH, track byte) by 1, advancing the track number for the next iteration.
4884
DJNZ 4880H 10 FA
DECrement Register B and LOOP BACK to 4880H if not zero. Formats RECNO low-byte tracks total.
4886
LD (HL),B 70
Loop End
Store Register B (=00H after DJNZ exit) to the address held in Register Pair HL (=448DH), resetting the track byte to 00H for the verify pass.
4887
LD B,E 43
Copy Register E (low byte of RECNO) back into Register B, restarting the loop counter for the verify pass.
4888
CALL 48BCH CD BC 48
Verify Loop Start
GOSUB to the verify-track routine at 48BCH, which reads back the track and counts errors.
488B
JR Z,48B5H 28 28
If the Z FLAG has been set (verify succeeded with no errors), jump forward to 48B5H to advance the track counter.
488D
LD A,43H 3E 43
Set Register A to 43H (ASCII 'C'). Used as a parameter to 4DBCH (the FDC stepping routine that takes a step direction code in Register A).
488F
CALL 4DBCH CD BC 4D
GOSUB to FDCSEEK helper at 4DBCH to issue an FDC command with code 'C' (step calibration).
4892
LD A,63H 3E 63
Set Register A to 63H (ASCII 'c'). Step-back command code.
4894
CALL 4DBCH CD BC 4D
GOSUB to 4DBCH again to issue a step-back.
4897
CALL 4C91H CD 91 4C
GOSUB to FORMAT at 4C91H to re-format the failed track.
489A
CALL 48BCH CD BC 48
GOSUB to verify the track again at 48BCH.
489D
JR Z,48B5H 28 16
If the Z FLAG has been set (re-format-and-verify succeeded), jump forward to 48B5H.
489F
CALL 4DACH CD AC 4D
GOSUB to FDC Restore command routine at 4DACH (return head to track 0).
48A2
CALL 4D89H CD 89 4D
GOSUB to Restore-then-Seek at 4D89H (re-seek to the current target track).
48A5
CALL 4C91H CD 91 4C
GOSUB to FORMAT at 4C91H to re-format after the calibration.
48A8
CALL 48BCH CD BC 48
GOSUB to verify the track at 48BCH.
48AB
JR Z,48B5H 28 08
If the Z FLAG has been set (verify succeeded), jump forward to 48B5H.
48AD
LD C,A 4F
Copy Register A (the verify error count) into Register C, preserving for the bad-sector accumulator.
48AE
LD A,(4496H) 3A 96 44
Load Register A with the byte at memory address 4496H (the FORMAT checksum/bad-track accumulator).
48B1
ADD A,C 81
ADD Register C (the bad-sector count) to Register A. After the ADD, A holds the running total of bad sectors across all tracks.
48B2
LD (4496H),A 32 96 44
Store Register A (the updated total) to memory address 4496H.
48B5
INC (HL) 34
INCrement the byte at the address held in Register Pair HL (=448DH, track byte) by 1.
48B6
DJNZ 4888H 10 D0
DECrement Register B and LOOP BACK to 4888H if not zero, continuing the verify pass for all tracks.
48B8
DEC (HL) 35
Verify Loop End
DECrement the byte at the address held in Register Pair HL (=448DH) by 1, since the loop overshot by one (DJNZ exits with one extra increment).
48B9
INC HL 23
INCrement Register Pair HL by 1, advancing to 448EH (sector byte).
48BA
DEC (HL) 35
DECrement the byte at the address held in Register Pair HL (=448EH) by 1, similarly correcting the sector byte.
48BB
RET C9
RETurn to the caller (the FETCH or INPUT handler).

48BCH - Verify Track Routine

Reads back all sectors of a track and counts errors. Called from the FORMAT loop after each track is written. Returns Z if all sectors verify clean, NZ with error count in Register A if any failed.

48BC
INC HL 23
INCrement Register Pair HL by 1, advancing the pointer (originally 448DH = track byte) to 448EH (sector byte).
48BD
LD (HL),00H 36 00
Set the byte at the address held in Register Pair HL (=448EH) to 00H, resetting the sector byte to 0 for the verify scan.
48BF
PUSH BC C5
Save Register Pair BC (the outer-loop counter) onto the stack.
48C0
LD B,D 42
Copy Register D (high byte of RECNO from earlier load at 4878H) into Register B, the inner-loop counter (sectors per track).
48C1
LD C,00H 0E 00
Set Register C to 00H, the bad-sector counter for this track.
48C3
PUSH BC C5
Loop Start
Save Register Pair BC (loop counter and bad-sector count) onto the stack.
48C4
PUSH DE D5
Save Register Pair DE (RECNO) onto the stack.
48C5
PUSH HL E5
Save Register Pair HL (sector byte address at 448EH) onto the stack.
48C6
CALL 4D44H CD 44 4D
GOSUB to FDCSEEK at 4D44H to position the FDC head at the current track/sector.
48C9
CALL 4C6DH CD 6D 4C
GOSUB to the read-sector-for-compare routine at 4C6DH.
48CC
POP HL E1
Restore Register Pair HL from the stack.
48CD
POP DE D1
Restore Register Pair DE from the stack.
48CE
POP BC C1
Restore Register Pair BC from the stack.
48CF
JR NC,48D2H 30 01
If the NC FLAG has been set (read succeeded), skip the bad-sector increment.
48D1
INC C 0C
INCrement Register C by 1, counting this as a bad sector.
48D2
INC (HL) 34
INCrement the byte at the address held in Register Pair HL (=448EH, sector byte) by 1, advancing to the next sector.
48D3
DJNZ 48C3H 10 EE
DECrement Register B and LOOP BACK to 48C3H if not zero, scanning all sectors of the track.
48D5
LD A,C 79
Loop End
Copy Register C (the bad-sector count for this track) into Register A.
48D6
POP BC C1
Restore the original outer-loop BC from the stack.
48D7
DEC HL 2B
DECrement Register Pair HL by 1, restoring HL to 448DH (track byte).
48D8
OR A,A B7
Logical OR Register A with itself, setting Z FLAG if A=0 (no bad sectors) or NZ otherwise.
48D9
RET C9
RETurn with Register A holding the bad-sector count and Z/NZ flag set accordingly.

48DAH - ERREXIT2 - Error Code Setup Table (Variant 2)

Eight 4-byte entries. Each entry loads Register E with a unique error code (2EH, 30H, 34H, 32H, 38H, 42H, 44H) and JRs to either 4918H (final-error JP 19A2H) or 48F4H (the FDC FORCE INTERRUPT path that saves status before erroring). These entry points are reached by JP from various FDC error paths in the disk I/O routines.

48DA
LD E,2EH 1E 2E
Set Register E to 2EH, the error code for "Device Not Ready".
48DC
JR 4918H 18 3A
JUMP to 4918H to dispatch through the final error JP 19A2H path.
48DE
LD E,30H 1E 30
Set Register E to 30H, an error code (between 2EH and 34H, used internally).
48E0
JR 4918H 18 36
JUMP to 4918H.
48E2
LD E,34H 1E 34
Set Register E to 34H, the error code for "Parse Error".
48E4
JR 4918H 18 32
JUMP to 4918H.
48E6
LD E,32H 1E 32
Set Register E to 32H, an error code.
48E8
JR 48F4H 18 0A
JUMP to 48F4H, the FORCE INTERRUPT pre-error path that saves the FDC status before raising the error.
48EA
LD E,38H 1E 38
Set Register E to 38H, an error code.
48EC
JR 48F4H 18 06
JUMP to 48F4H.
48EE
LD E,42H 1E 42
Set Register E to 42H, an error code (66 decimal).
48F0
JR 48F4H 18 02
JUMP to 48F4H.
48F2
LD E,44H 1E 44
Set Register E to 44H, an error code (68 decimal).

Track 0 / Sector 6 (48FBH - 49F9H) - boundary falls within preceding ERREXIT2 table at 48F4H

48F4H - FDC FORCE INTERRUPT Pre-Error Path

Common entry from the 48E6H, 48EAH, 48EEH, 48F2H error setups. Saves the current FDC status to 4495H, issues a FORCE INTERRUPT (D0H) to terminate any in-progress operation, marks the offending drive's step-rate entry as FFH, then either tests for write-protect or falls through to the error JP at 4918H.

48F4
LD A,(37ECH) 3A EC 37
Load Register A with the byte at memory address 37ECH (the WD1771 FDC Status register), capturing the current FDC status.
48F7
LD (4495H),A 32 95 44
Store Register A (FDC status) to memory address 4495H, saving it for later inspection by 490FH.
48FA
LD A,0D0H 3E D0
Set Register A to D0H, the WD1771 FORCE INTERRUPT command (terminate operation cleanly).
48FC
LD (37ECH),A 32 EC 37
Store Register A (=D0H) to memory address 37ECH, the FDC Command register. This issues the FORCE INTERRUPT.
48FF
LD HL,4496H 21 96 44
Set Register Pair HL to 4496H (initial position; will be incremented in the loop below).
4902
LD A,(448CH) 3A 8C 44
Load Register A with the byte at memory address 448CH (the drive-select mask, which is one-hot for drives 0-3).
4905
AND A,0FH E6 0F
Logical AND Register A with 0FH, masking off the high nibble (which is unused on Model I).
4907
OR A,08H F6 08
Logical OR Register A with 08H, ensuring bit 3 is set. This guarantees that the right-rotation loop below will terminate even if the drive-select mask was 00H.
4909
RRCA 0F
Loop Start
Rotate Register A right circularly. Bit 0 becomes bit 7 and the C FLAG.
490A
INC HL 23
INCrement Register Pair HL by 1, advancing to the next step-rate entry (4497H, 4498H, 4499H, 449AH).
490B
JR NC,4909H 30 FC
If the NC FLAG has been set (the rotated bit was 0, meaning this drive is not the selected one), LOOP BACK to 4909H to test the next bit.
490D
LD (HL),0FFH 36 FF
Loop End
Set the byte at the address held in Register Pair HL (the matching step-rate entry for the selected drive) to FFH, marking that drive's step-rate as "needs recalibration".
490F
LD A,(4495H) 3A 95 44
Load Register A with the byte at memory address 4495H (the saved FDC status).
4912
OR A,A B7
Logical OR Register A with itself. Side effect: sets the Sign flag based on bit 7 (Not Ready) of the saved FDC status.
4913
JP P,19A2H F2 A2 19
If the P FLAG has been set (Sign positive, bit 7 of saved status is 0, drive IS ready - so the error must be a different cause), JUMP to ROM 19A2H with whatever error code is in Register E to display the error.
NOTE: 19A2H is the Level II BASIC ROM ERROR routine. Displays the error message indexed by Register E.
4916
LD E,40H 1E 40
Set Register E to 40H (the error code for "Disk Write Protected" - the actual cause if bit 7 of the FDC status was set).
4918
JP 19A2H C3 A2 19
JUMP to ROM 19A2H to display the error.
NOTE: 19A2H is the Level II BASIC ROM ERROR handler.

491BH - SYSTEM Re-Init / Line-Pointer Relink

Reached from the INPUT handler at 4826H, the MERGE handler at 484CH, and from the file-load post-processing path. Loads the byte at 4594H (the embedded boot copy entry, used as a 1-byte signature), saves it to 448FH (the chain marker variable), and sets up the buffer pointer at 4490H to 4595H. Then clears the SECTOR variable, calls 4BF6H (the chain-walker write-back), advances 448EH to 01H (sector 1), and computes the loaded-program length (5600H - 4400H = 1200H) for the BASIC line-pointer relink call at 4968H.

491B
LD HL,4594H 21 94 45
Set Register Pair HL to 4594H, the start of the embedded BOOTCOPY code. The first byte (FEH = CP A,nn opcode) is reused as a magic signature.
491E
LD A,(HL) 7E
Load Register A with the byte at the address held in Register Pair HL (=4594H), reading the FEH signature byte.
491F
INC HL 23
INCrement Register Pair HL by 1, advancing to 4595H (the second byte of BOOTCOPY's CP 00H instruction).
4920
LD (448FH),A 32 8F 44
Store Register A (=FEH) to memory address 448FH, the chain-marker variable. FEH means "245 data bytes left in the buffer" (a non-FFH end-of-chain marker).
4923
LD (4490H),HL 22 90 44
Store Register Pair HL (=4595H) to memory address 4490H, the buffer pointer. Subsequent reads from the buffer will start at 4595H.
4926
LD HL,0000H 21 00 00
Set Register Pair HL to 0000H.
4929
LD (448DH),HL 22 8D 44
Store Register Pair HL (=0000H) to memory address 448DH, clearing the SECTOR variable (track byte 448DH and sector byte 448EH).
492C
CALL 4BF6H CD F6 4B
GOSUB to 4BF6H, the chain-walker write-back routine.
492F
LD A,01H 3E 01
Set Register A to 01H.
4931
LD (448EH),A 32 8E 44
Store Register A (=01H) to memory address 448EH (sector byte), pointing at sector 1 (the first OS chain sector).
4934
LD DE,4400H 11 00 44
Set Register Pair DE to 4400H, the start of the resident OS image / BASIC program area.
4937
LD HL,5600H 21 00 56
Set Register Pair HL to 5600H, just past the end of the resident OS image.
493A
OR A,A B7
Logical OR Register A with itself, clearing the C FLAG (needed for the SBC below).
493B
SBC HL,DE ED 52
SUBtract Register Pair DE from HL with carry borrow (carry was just cleared, so this is HL - DE). After the subtract, HL holds 5600H - 4400H = 1200H (the length of the loaded image).
493D
CALL 4968H CD 68 49
GOSUB to 4968H, the BASIC program checksum / line-pointer relink routine.
4940
RET C9
RETurn to the caller.

4941H - OPEN/CALL Argument Handler

JTABLE entry 26 routes here for the BASIC OPEN keyword. Calls FILESPEC parser at 4AFBH to fetch a string argument, validates the line ending via RST 10H, then computes the gap between the current string-storage pointer at 40A4H and the string-area top at 40F9H. Calls 4968H to display the gap as a numeric value (probably "FREE BYTES = NN").

4941
CALL 4AFBH CD FB 4A
GOSUB to FILESPEC parser at 4AFBH to fetch a string argument.
4944
DEC HL 2B
DECrement Register Pair HL by 1, backing the BASIC parser pointer.
4945
RST 10H D7
RST 10H to advance past whitespace and check end-of-statement.
4946
JP NZ,1997H C2 97 19
If the NZ FLAG has been set (extra characters), JUMP to 1997H (Syntax Error).
4949
PUSH HL E5
Save Register Pair HL onto the stack.
494A
LD DE,(40A4H) ED 5B A4 40
Load Register Pair DE with the 16-bit value at memory address 40A4H (the string-storage start pointer = 5A01H initially).
494E
LD HL,(40F9H) 2A F9 40
Load Register Pair HL with the 16-bit value at memory address 40F9H (the BASIC string-area end pointer).
4951
OR A,A B7
Logical OR Register A with itself, clearing the C FLAG.
4952
SBC HL,DE ED 52
SUBtract DE from HL, giving the gap between string-storage pointer and string-area end (= number of free bytes).
4954
CALL 4968H CD 68 49
GOSUB to 4968H, the display-gap-as-number routine.
4957
LD HL,4581H 21 81 45
Set Register Pair HL to 4581H, the address of the "LAST SECTOR USED = " message string.
495A
CALL 28A7H CD A7 28
GOSUB to ROM 28A7H to display the "LAST SECTOR USED = " string.
NOTE: 28A7H is the Level II BASIC ROM OUTSTR routine.
495D
CALL 4E52H CD 52 4E
GOSUB to 4E52H, an internal routine that fetches the last-used sector value into the floating-point accumulator.
4960
CALL 0FBDH CD BD 0F
GOSUB to ROM 0FBDH (CSAASC).
NOTE: 0FBDH is the Level II BASIC ROM CSAASC routine. Converts the value in the software accumulator to ASCII and places it in the buffer at 4130H, with HL=4130H on return.
4963
CALL 28A7H CD A7 28
GOSUB to ROM 28A7H again to display the ASCII number at HL=4130H.
4966
POP HL E1
Restore Register Pair HL from the stack.
4967
RET C9
RETurn to the BASIC dispatcher.

4968H - Display Gap as Decimal Number / Allocate Loop

A multipurpose routine reached from 491BH (with HL = image length) and 4954H (with HL = free-byte count). Calls 559EH to display the high byte of HL, then loops calling 497DH and 498FH to walk the chain marker chain.

4968
LD A,H 7C
Copy Register H (high byte of HL) into Register A.
4969
CALL 559EH CD 9E 55
GOSUB to 559EH, the byte-display helper at the resident's tail.
496C
LD BC,00FFH 01 FF 00
Set Register Pair BC to 00FFH (255 decimal), the standard chain-segment data-byte count.
496F
CALL 497DH CD 7D 49
GOSUB to 497DH, the chain-marker-write helper.
4972
CALL 498FH CD 8F 49
GOSUB to 498FH, the sector-advance helper.
4975
JR 4968H 18 F1
LOOP BACK to 4968H to continue chain. This is an open-ended loop that exits when 498FH or 497DH triggers an error path.

4977H - Chain Tail Allocation

Final step of a chain allocation: stores BC into HL via the chain-marker-write helper. Reached as a branch target from 55A4H (an alternate entry from the misc-routines tail block).

4977
LD B,H 44
Copy Register H (high byte of HL) into Register B.
4978
LD C,L 4D
Copy Register L (low byte of HL) into Register C. After both copies, BC = HL (the byte count to allocate).
4979
CALL 497DH CD 7D 49
GOSUB to 497DH to write the chain marker.
497C
RET C9
RETurn to the caller.

497DH - Chain Marker Write Helper

Writes Register C (the chain marker byte) to 448FH and Register Pair DE (the buffer pointer) to 4490H, then calls 4BF6H (the actual disk write). After the write, recomputes HL = HL - BC for the caller's gap-tracking.

497D
LD A,C 79
Copy Register C (the chain marker byte) into Register A.
497E
LD (448FH),A 32 8F 44
Store Register A (the chain marker) to memory address 448FH.
4981
LD (4490H),DE ED 53 90 44
Store Register Pair DE (the buffer pointer) to memory address 4490H.
4985
CALL 4BF6H CD F6 4B
GOSUB to 4BF6H, the chain-walker write-back routine.
4988
OR A,A B7
Logical OR Register A with itself, clearing the C FLAG for the upcoming SBC.
4989
SBC HL,BC ED 42
SUBtract Register Pair BC from HL with carry borrow. HL now holds the remaining gap after this segment was written.
498B
EX DE,HL EB
Exchange Register Pairs DE and HL. Now DE = remaining gap, HL = original buffer pointer.
498C
ADD HL,BC 09
ADD Register Pair BC to HL, advancing the buffer pointer past the just-written segment.
498D
EX DE,HL EB
Exchange DE and HL again. Now HL = remaining gap, DE = advanced buffer pointer.
498E
RET C9
RETurn to the caller.

498FH - Sector Advance Helper

Advances the SECTOR variable at 448DH-448EH to the next track/sector. INC H (track byte), check against record limit at 4492H; if past limit, INC L (the sector wraps), check against limit at 4493H; if both exceeded, raise error 36H ("Disk Overflow").

498F
PUSH HL E5
Save Register Pair HL onto the stack (the caller's gap-remaining value).
4990
PUSH DE D5
Save Register Pair DE onto the stack (the caller's buffer pointer).
4991
LD HL,(4492H) 2A 92 44
Load Register Pair HL with the 16-bit value at memory address 4492H (RECNO, the records-per-track / records-per-sector limit).
4994
EX DE,HL EB
Exchange Register Pairs DE and HL. Now DE = RECNO limit, HL is free.
4995
LD HL,(448DH) 2A 8D 44
Load Register Pair HL with the 16-bit value at memory address 448DH (the SECTOR variable: H=track, L=sector).
4998
INC H 24
INCrement Register H by 1, advancing the track byte.
4999
LD A,H 7C
Copy Register H (the just-incremented track) into Register A.
499A
CP A,D BA
Compare Register A (track) against Register D (high byte of RECNO limit).
499B
JR C,49A8H 38 0B
If the C FLAG has been set (track < high-limit), jump forward to 49A8H to commit the new SECTOR value.
499D
LD H,00H 26 00
Set Register H to 00H (track wraps back to 0).
499F
INC L 2C
INCrement Register L by 1 (the sector wraps to next track).
49A0
CP A,E BB
Compare Register A (track) against Register E (low byte of RECNO limit).
49A1
JR C,49A8H 38 05
If the C FLAG has been set (sector < low-limit), jump forward to 49A8H to commit.
49A3
LD E,36H 1E 36
Set Register E to 36H, the error code for "Disk Overflow".
49A5
JP 19A2H C3 A2 19
JUMP to ROM 19A2H to display Disk Overflow error.
NOTE: 19A2H is the Level II BASIC ROM ERROR routine.
49A8
LD (448DH),HL 22 8D 44
Store Register Pair HL (the new SECTOR: H=new track, L=new sector) to memory address 448DH.
49AB
POP DE D1
Restore Register Pair DE from the stack (the buffer pointer).
49AC
POP HL E1
Restore Register Pair HL from the stack (the gap-remaining value).
49AD
RET C9
RETurn to the caller.

49AEH - LOAD/RUN Handler (JTABLE entry 18)

Reached via JTABLE entry 18 (4152H+54 = 4188H = JP 49AEH) for the LOAD-and-optionally-RUN keyword. Calls the FILESPEC parser at 4AFBH to fetch the filename argument. Then optionally reads a R suffix (which turns the LOAD into a LOAD-and-RUN), reinitializes the BASIC pointers via 1B4DH, walks through the loaded program updating BASIC line pointers, and finally either returns to the BASIC READY prompt at 1A19H or executes the program via JP 1D1EH.

49AE
CALL 4AFBH CD FB 4A
GOSUB to FILESPEC parser at 4AFBH to fetch and resolve the filename argument from the BASIC text stream. On return, HL points just past the parameter and DE points at the resolved filename buffer.
49B1
DEC HL 2B
DECrement Register Pair HL by 1, backing the BASIC parser pointer to re-read the byte after the filename.
49B2
RST 10H D7
RST 10H, advance the BASIC text pointer past whitespace and return the next non-blank character in Register A.
NOTE: RST 10H advances HL past spaces and tabs, returning the next significant byte.
49B3
LD A,00H 3E 00
Set Register A to 00H, the default "no R suffix" flag. This will be overwritten if the actual R suffix is present.
49B5
JR Z,49C0H 28 09
If the Z FLAG has been set (end of statement reached - no R suffix), jump forward to 49C0H to skip the R-suffix parsing.
49B7
RST 08H => 2CH CF 2C
Syntax check via RST 08H, requiring a comma (2CH) at the BASIC parser pointer.
NOTE: RST 08H is the Level II BASIC ROM SYNTAX check. HL points to the byte being checked; the proper byte (2CH = ',') follows the RST 08H instruction.
49B9
RST 08H => 52H CF 52
Syntax check via RST 08H, requiring an R (52H = ASCII 'R') at the BASIC parser pointer. This confirms the R suffix on a LOAD-and-RUN command.
49BB
JP NZ,1997H C2 97 19
If the NZ FLAG has been set (the syntax check failed because something other than 'R' was present), JUMP to ROM 1997H to display Syntax Error.
NOTE: 1997H is the Level II BASIC ROM SN ERROR routine.
49BE
LD A,0FFH 3E FF
Set Register A to FFH, the "R suffix present" flag. Will be saved at 4121H to indicate that the program should auto-run after loading.
49C0
LD (4121H),A 32 21 41
Store Register A (00H or FFH) to memory address 4121H, the BASIC ACCUM (number accumulator). Used here as the LOAD-and-RUN flag: 00H = LOAD only, FFH = LOAD and RUN.
49C3
CALL 1B4DH CD 4D 1B
GOSUB to ROM 1B4DH.
NOTE: 1B4DH is the Level II BASIC ROM NEW routine entry, which resets the BASIC pointers (clears variables, resets line pointers). Required before loading a new program.
49C6
LD HL,(40A4H) 2A A4 40
Load Register Pair HL with the 16-bit value at memory address 40A4H (the string-storage start pointer = 5A01H initially). This is the destination address for the loaded BASIC program.
49C9
LD DE,0000H 11 00 00
Set Register Pair DE to 0000H, used as the offset accumulator for the upcoming ADD HL,DE (initially zero, accumulates as the program loads).
49CC
ADD HL,DE 19
Loop Start
ADD Register Pair DE to HL. After the ADD, HL is the current write pointer into the BASIC program area.
49CD
CALL 558CH CD 8C 55
GOSUB to 558CH, the BASIC program load helper at the resident's tail. Loads sector data from the chain into memory at HL.
49D0
CALL 4B7FH CD 7F 4B
GOSUB to 4B7FH, a chain-walk continuation helper that advances the buffer pointer.
49D3
LD A,(448FH) 3A 8F 44
Load Register A with the byte at memory address 448FH (the chain marker variable, holding the marker byte from the last sector read).
49D6
LD E,A 5F
Copy Register A (chain marker) into Register E, preserving for the loop-counter use below.
49D7
INC A 3C
INCrement Register A by 1. If marker = FFH (continuation), A becomes 00H and Z FLAG is set; if marker = anything else (end-of-chain), Z FLAG is cleared.
49D8
JR NZ,49DFH 20 05
If the NZ FLAG has been set (chain marker was not FFH, end-of-chain reached), jump forward to 49DFH to finalize the load.
49DA
CALL 498FH CD 8F 49
GOSUB to the sector-advance helper at 498FH to advance the SECTOR variable to the next track/sector.
49DD
JR 49CCH 18 ED
LOOP BACK to 49CCH to continue loading the next sector of the chain.
49DF
CALL 1AF8H CD F8 1A
Loop End
GOSUB to ROM 1AF8H.
NOTE: 1AF8H is the Level II BASIC ROM routine that writes line pointers from the start of the BASIC program, linking the loaded BASIC text into a runnable program.
49E2
INC HL 23
INCrement Register Pair HL by 1, advancing past the program-end terminator.
49E3
LD (40F9H),HL 22 F9 40
Store Register Pair HL to memory address 40F9H, the BASIC string-area end pointer. Marks the new boundary between BASIC program and string area.
49E6
CALL 1B5DH CD 5D 1B
GOSUB to ROM 1B5DH.
NOTE: 1B5DH is the Level II BASIC ROM RUN initialization. Sets up the run-time environment so the loaded program can execute.
49E9
LD HL,(40DFH) 2A DF 40
Load Register Pair HL with the 16-bit value at memory address 40DFH (the BASIC text-pointer for the next statement to execute). Set up by 1B5DH.
49EC
LD A,(4121H) 3A 21 41
Load Register A with the byte at memory address 4121H (the LOAD-and-RUN flag, 00H or FFH).
49EF
OR A,A B7
Logical OR Register A with itself, setting Z FLAG if A=00H (LOAD only) or NZ if A=FFH (LOAD and RUN).
49F0
JP Z,1A19H CA 19 1A
If the Z FLAG has been set (LOAD only, no R suffix), JUMP to ROM 1A19H to display the BASIC READY prompt.
NOTE: 1A19H is the Level II BASIC ROM BASIC entry point that displays READY.
49F3
JP 1D1EH C3 1E 1D
JUMP to ROM 1D1EH to begin executing the loaded program.
NOTE: 1D1EH is the Level II BASIC ROM RUNSTM routine. HL points at the next statement to execute; control transfers into the BASIC interpreter loop.

Track 0 / Sector 7 (49FAH - 4AF8H) - boundary falls within preceding LOAD/RUN handler at 49FAH (DEC HL row)

49F6H - SAVE Handler (JTABLE entry 19)

Reached via JTABLE entry 19 (4152H+57 = 418BH = JP 49F6H) for the SAVE keyword. POPs the stale return address that the BASIC dispatcher pushed (because SAVE handles its own argument processing differently from LOAD). Calls FILESPEC parser at 4AFBH, validates end-of-statement, initializes the buffer with FFH chain markers, and enters a directory-scan loop calling DIRWALK at 4A4AH to find the file's directory entry. When found, allocates new disk space, writes the BASIC program contents using the chained-sector format, and finally re-runs the file allocation routines via 4A45H.

49F6
POP BC C1
POP the stale 16-bit return address from the stack into Register Pair BC, discarding it. This is needed because the BASIC dispatcher pushes a return-to-statement-loop address that SAVE does not honor (SAVE has its own exit path).
49F7
CALL 4AFBH CD FB 4A
GOSUB to FILESPEC parser at 4AFBH to fetch the filename argument.
49FA
DEC HL 2B
DECrement Register Pair HL by 1, backing the BASIC parser pointer.
49FB
RST 10H D7
RST 10H, advancing past whitespace.
49FC
JP NZ,1997H C2 97 19
If the NZ FLAG has been set (extra characters after filename), JUMP to ROM 1997H (Syntax Error).
49FF
LD A,0FFH 3E FF
Set Register A to FFH, the chain-continuation marker.
4A01
LD (4300H),A 32 00 43
Store Register A (=FFH) to memory address 4300H, the first byte of the sector buffer. Initializes the buffer with a continuation marker for the upcoming write.
4A04
INC A 3C
INCrement Register A by 1. Since A was FFH, the increment wraps to 00H.
4A05
LD (448FH),A 32 8F 44
Store Register A (=00H) to memory address 448FH, the chain-marker variable. 00H means "no chain marker yet seen" - signals that the next read will populate it.
4A08
CALL 4A4AH CD 4A 4A
Loop Start
GOSUB to DIRWALK at 4A4AH to read the next directory entry. Returns Z if no more entries (end of directory), NZ with DE pointing at the entry data otherwise.
4A0B
JR Z,4A47H 28 3A
If the Z FLAG has been set (end of directory reached - file not found), jump forward to 4A47H to display READY.
4A0D
LD (40ECH),DE ED 53 EC 40
Store Register Pair DE (the directory entry data pointer) to memory address 40ECH (the BASIC current-line-number variable, repurposed as a temporary).
4A11
PUSH DE D5
Save Register Pair DE onto the stack.
4A12
PUSH BC C5
Save Register Pair BC onto the stack.
4A13
CALL 1B2CH CD 2C 1B
GOSUB to ROM 1B2CH (FNDLIN).
NOTE: 1B2CH is the Level II BASIC ROM FNDLIN routine. Searches for the BASIC line number held in DE; on exit, BC contains the address of that line in the program.
4A16
PUSH BC C5
Save Register Pair BC (the found line address) onto the stack.
4A17
CALL C,2BE4H DC E4 2B
If the C FLAG has been set, GOSUB to ROM 2BE4H.
NOTE: 2BE4H is the Level II BASIC ROM FNDVAR routine. Scans the variable table for the address of a specific variable. Called conditionally to handle variable-name lookup when the line was found.
4A1A
POP DE D1
Restore Register Pair DE from the stack (originally was the saved BC, now repurposed).
4A1B
LD HL,(40F9H) 2A F9 40
Load Register Pair HL with the 16-bit value at memory address 40F9H (the BASIC string-area end pointer).
4A1E
EX (SP),HL E3
Exchange the 16-bit value at the top of the stack with Register Pair HL. This swaps the saved BC (line address) with the current 40F9H value.
4A1F
POP BC C1
POP the swapped value from the stack into Register Pair BC. BC now holds the original 40F9H value.
4A20
ADD HL,BC 09
ADD Register Pair BC to HL. After the ADD, HL holds the new end pointer accounting for the appended SAVE data.
4A21
PUSH HL E5
Save Register Pair HL onto the stack.
4A22
CALL 1955H CD 55 19
GOSUB to ROM 1955H.
NOTE: 1955H is the Level II BASIC ROM OMERR (Out of Memory) error check routine. Verifies enough RAM is available for the proposed allocation; raises "OM Error" if not.
4A25
POP HL E1
Restore Register Pair HL from the stack (the new end pointer).
4A26
LD (40F9H),HL 22 F9 40
Store Register Pair HL to memory address 40F9H, updating the BASIC string-area end pointer.
4A29
EX DE,HL EB
Exchange Register Pairs DE and HL. After the swap, DE = new end pointer, HL = original DE (saved-line address from earlier).
4A2A
LD (HL),H 74
Store Register H (high byte of new end pointer, was original DE) to the address held in Register Pair HL. This writes the line-link byte (high byte) to the directory entry.
4A2B
POP DE D1
Restore Register Pair DE from the stack (originally pushed at 4A11H).
4A2C
PUSH HL E5
Save Register Pair HL onto the stack.
4A2D
INC HL 23
INCrement Register Pair HL by 1, advancing past the line-link byte.
4A2E
INC HL 23
INCrement Register Pair HL by 1 again, advancing past the second link byte (forming a 2-byte gap).
4A2F
LD (HL),E 73
Store Register E (low byte of DE) to the address held in Register Pair HL. Writes the low byte of the line number into the directory entry.
4A30
INC HL 23
INCrement HL by 1.
4A31
LD (HL),D 72
Store Register D (high byte of DE) to the address held in Register Pair HL. Writes the high byte of the line number.
4A32
INC HL 23
INCrement HL by 1, advancing past the line-number bytes.
4A33
EX DE,HL EB
Exchange DE and HL again. Now DE points just past the line-number bytes (write target), HL holds the new end pointer.
4A34
LD HL,(40A7H) 2A A7 40
Load Register Pair HL with the 16-bit value at memory address 40A7H (the comma-separator template pointer = 41E8H). HL is the source for the upcoming byte-copy loop.
4A37
LD A,(HL) 7E
Inner Loop Start
Load Register A with the byte at HL (the next byte of the source).
4A38
LD (DE),A 12
Store Register A to (DE), copying the byte to the destination.
4A39
INC DE 13
INCrement DE by 1, advancing the destination.
4A3A
INC HL 23
INCrement HL by 1, advancing the source.
4A3B
OR A,A B7
Logical OR Register A with itself, setting Z FLAG if A=00H or NZ otherwise.
4A3C
JR NZ,4A37H 20 F9
If the NZ FLAG has been set (not yet at NUL terminator), LOOP BACK to 4A37H to copy the next byte.
4A3E
POP DE D1
Inner Loop End
Restore Register Pair DE from the stack.
4A3F
CALL 1AF8H CD F8 1A
GOSUB to ROM 1AF8H to write line pointers from the start of the BASIC program.
NOTE: 1AF8H is the Level II BASIC ROM line-pointer-link routine.
4A42
CALL 1B5DH CD 5D 1B
GOSUB to ROM 1B5DH (BASIC RUN initialization).
4A45
JR 4A08H 18 C1
LOOP BACK to 4A08H to read the next directory entry.
4A47
JP 1A19H C3 19 1A
JUMP to ROM 1A19H, the Level II BASIC ROM BASIC entry point that displays READY.
NOTE: 1A19H is the Level II BASIC ROM READY entry. The SAVE has completed; control returns to the user prompt.

4A4AH - DIRWALK - Directory Walker

Reads the next directory record from the file directory. Each directory record begins with a 4-byte header (loaded into BC=0004H, then read via 4A6CH), followed by a variable-length name field. Returns Z if end-of-directory reached, NZ with DE=line number, A=count if a record was successfully read.

4A4A
LD BC,0004H 01 04 00
Set Register Pair BC to 0004H, the size of a directory record header (4 bytes).
4A4D
CALL 4A6CH CD 6C 4A
GOSUB to 4A6CH, the chain-byte-fetch helper. Returns the next byte of the chain in Register A.
4A50
LD D,A 57
Copy Register A (the first header byte) into Register D.
4A51
CALL 4A6CH CD 6C 4A
GOSUB to 4A6CH again to fetch the second header byte into Register A.
4A54
OR A,D B2
Logical OR Register A with Register D. If both bytes are 00H (the directory end-marker), the Z FLAG is set.
4A55
RET Z C8
If the Z FLAG has been set (end-of-directory marker found), RETurn to the caller with Z indicating end-of-directory.
4A56
CALL 4A6CH CD 6C 4A
GOSUB to 4A6CH to fetch the third byte (low byte of line number).
4A59
LD E,A 5F
Copy Register A (line number low byte) into Register E.
4A5A
CALL 4A6CH CD 6C 4A
GOSUB to 4A6CH to fetch the fourth byte (high byte of line number).
4A5D
LD D,A 57
Copy Register A (line number high byte) into Register D. Now DE = the BASIC line number for this directory entry.
4A5E
LD HL,(40A7H) 2A A7 40
Load Register Pair HL with the 16-bit value at memory address 40A7H (the comma-separator template pointer = 41E8H). HL will be the destination for the upcoming filename copy.
4A61
CALL 4A6CH CD 6C 4A
Filename Copy Loop Start
GOSUB to 4A6CH to fetch the next filename byte into Register A.
4A64
LD (HL),A 77
Store Register A (the filename byte) to (HL).
4A65
INC HL 23
INCrement HL by 1, advancing the destination.
4A66
INC BC 03
INCrement Register Pair BC by 1, counting the bytes copied.
4A67
OR A,A B7
Logical OR Register A with itself, setting Z FLAG if A=00H (NUL terminator).
4A68
JR NZ,4A61H 20 F7
If the NZ FLAG has been set (not yet at NUL), LOOP BACK to 4A61H to copy the next byte.
4A6A
INC A 3C
Filename Copy Loop End
INCrement Register A by 1. Since A was 0 (NUL), A is now 01H, ensuring the NZ flag is set on return.
4A6B
RET C9
RETurn to the caller with NZ flag set, DE = line number, BC = total record byte count.

4A6CH - Chain-Byte-Fetch Helper

Fetches one byte from the chained sector buffer. Decrements the byte counter at 448FH; if it reaches zero, calls 4A82H to read the next sector before fetching. Returns the byte in Register A and updates the buffer pointer at 4490H.

4A6C
LD A,(448FH) 3A 8F 44
Load Register A with the byte at memory address 448FH (the chain-marker variable, used here as the bytes-remaining-in-buffer counter).
4A6F
OR A,A B7
Logical OR Register A with itself, setting Z FLAG if A=00H (no more bytes in current buffer) or NZ otherwise.
4A70
CALL Z,4A82H CC 82 4A
If the Z FLAG has been set (buffer exhausted), GOSUB to 4A82H to read the next sector and refill 448FH and 4490H.
4A73
DEC A 3D
DECrement Register A by 1. This is the bytes-remaining counter, post-decrement after the fetch.
4A74
LD (448FH),A 32 8F 44
Store the decremented Register A to memory address 448FH.
4A77
PUSH HL E5
Save Register Pair HL onto the stack.
4A78
LD HL,(4490H) 2A 90 44
Load Register Pair HL with the 16-bit value at memory address 4490H (the buffer pointer).
4A7B
LD A,(HL) 7E
Load Register A with the byte at the address held in HL (the next buffer byte).
4A7C
INC HL 23
INCrement HL by 1, advancing the buffer pointer.
4A7D
LD (4490H),HL 22 90 44
Store the advanced HL to memory address 4490H, updating the buffer pointer.
4A80
POP HL E1
Restore Register Pair HL from the stack.
4A81
RET C9
RETurn to the caller with Register A holding the fetched byte.

4A82H - Read Next Sector and Test Marker

Reads the next sector into the buffer at 4300H, captures the chain-marker byte, and updates the bytes-remaining counter at 448FH and the buffer pointer at 4490H. If the chain marker is FFH (continuation), returns with A holding the byte count. If non-FFH (end-of-chain), takes the special-case path through 4AA6H.

4A82
PUSH HL E5
Save Register Pair HL onto the stack.
4A83
PUSH DE D5
Save Register Pair DE onto the stack.
4A84
PUSH BC C5
Save Register Pair BC onto the stack.
4A85
LD A,(4300H) 3A 00 43
Load Register A with the byte at memory address 4300H (the first byte of the sector buffer, which holds the previous sector's chain-marker).
4A88
INC A 3C
INCrement Register A by 1. If marker = FFH, A becomes 00H (Z set); else NZ.
4A89
JR NZ,4AA6H 20 1B
If the NZ FLAG has been set (previous chain ended), jump forward to 4AA6H to set up an end-of-chain state.
4A8B
CALL 4B8CH CD 8C 4B
GOSUB to 4B8CH, the actual disk-read routine that loads the next sector into the buffer at 4300H.
4A8E
LD HL,4300H 21 00 43
Set Register Pair HL to 4300H, the start of the sector buffer.
4A91
LD A,(HL) 7E
Load Register A with the byte at HL (=4300H), the new sector's chain-marker.
4A92
LD (448FH),A 32 8F 44
Store Register A (the new chain-marker) to memory address 448FH.
4A95
INC HL 23
INCrement HL by 1, advancing past the chain-marker to the first data byte at 4301H.
4A96
LD (4490H),HL 22 90 44
Store the advanced HL (=4301H) to memory address 4490H, the buffer pointer.
4A99
INC A 3C
INCrement Register A by 1. If the just-stored marker = FFH, A becomes 00H (Z set); else NZ.
4A9A
JR NZ,4A9FH 20 03
If the NZ FLAG has been set (chain marker is non-FFH = end-of-chain), skip the sector-advance.
4A9C
CALL 498FH CD 8F 49
GOSUB to the sector-advance helper at 498FH to advance the SECTOR variable to the next track/sector for the subsequent read.
4A9F
LD A,(448FH) 3A 8F 44
Load Register A with the byte at memory address 448FH (the chain-marker). This is the value the caller expects in A on return.
4AA2
POP BC C1
Restore Register Pair BC from the stack.
4AA3
POP DE D1
Restore Register Pair DE from the stack.
4AA4
POP HL E1
Restore Register Pair HL from the stack.
4AA5
RET C9
RETurn to the caller (4A6CH or 4A70H) with the chain-marker in Register A.
4AA6
LD HL,4301H 21 01 43
Set Register Pair HL to 4301H, the first data byte of the buffer (skipping the chain-marker at 4300H).
4AA9
XOR A,A AF
Set Register A to 00H and clear all flags.
4AAA
LD (HL),A 77
Store Register A (=00H) to (HL=4301H), zeroing the first data byte. This creates a clean end-of-chain marker.
4AAB
INC A 3C
INCrement Register A by 1. A is now 01H.
4AAC
JR 4AA2H 18 F4
JUMP to 4AA2H to restore registers and return.

4AAEH - APPEND/Field Handler (JTABLE entry 15)

Reached via JTABLE entry 15 (4152H+45 = 417FH = JP 4AAEH) for the APPEND or FIELD-style keyword. Calls FILESPEC parser at 4B59H, validates a comma, fetches another argument via 4AFBH, validates end-of-statement, then reads a byte from the chain and stores it. If the byte exceeds Register C (the limit), enters the multi-step processing path that calls 55A7H, copies 3 bytes from 40D3H, and updates 4490H from 40D4H.

4AAE
CALL 4B59H CD 59 4B
GOSUB to FILESPEC parser at 4B59H to fetch a string variable or quoted-string filespec.
4AB1
RST 08H => 2CH CF 2C
Syntax check requiring a comma (2CH).
4AB3
CALL 4AFBH CD FB 4A
GOSUB to FILESPEC parser at 4AFBH to fetch a second argument (a numeric value).
4AB6
DEC HL 2B
DECrement Register Pair HL by 1, backing the BASIC parser pointer.
4AB7
RST 10H D7
RST 10H, advancing past whitespace.
4AB8
JP NZ,1997H C2 97 19
If the NZ FLAG has been set (extra characters), JUMP to 1997H (Syntax Error).
4ABB
PUSH HL E5
Save Register Pair HL onto the stack.
4ABC
PUSH DE D5
Save Register Pair DE onto the stack.
4ABD
PUSH BC C5
Save Register Pair BC onto the stack.
4ABE
CALL 4B8CH CD 8C 4B
GOSUB to 4B8CH, the disk-read routine that loads a sector into the buffer at 4300H.
4AC1
LD A,(448FH) 3A 8F 44
Load Register A with the byte at memory address 448FH (the chain-marker, freshly captured by 4B8CH).
4AC4
POP BC C1
Restore Register Pair BC from the stack.
4AC5
POP DE D1
Restore Register Pair DE from the stack.
4AC6
LD (DE),A 12
Store Register A (the chain-marker) to (DE), writing it into the destination.
4AC7
CP A,C B9
Compare Register A against Register C (the limit byte). If A < C, C FLAG is set; if A == C, Z FLAG is set; otherwise neither.
4AC8
JR C,4ADFH 38 15
If the C FLAG has been set (A < C), jump forward to 4ADFH to skip the byte-overflow handling.
4ACA
JR Z,4ADFH 28 13
If the Z FLAG has been set (A == C), jump forward to 4ADFH (also skip overflow handling).
4ACC
PUSH DE D5
Save Register Pair DE onto the stack.
4ACD
CALL 55A7H CD A7 55
GOSUB to 55A7H, a tail-routine helper.
4AD0
POP DE D1
Restore Register Pair DE from the stack.
4AD1
LD HL,40D3H 21 D3 40
Set Register Pair HL to 40D3H, the source for a 3-byte LDIR copy. This BASIC ROM RAM area holds string-related work data.
4AD4
LD BC,0003H 01 03 00
Set Register Pair BC to 0003H, the byte count for the LDIR.
4AD7
LDIR ED B0
Block-move BC bytes from (HL) to (DE). Copies 3 bytes from 40D3H to the destination at DE.
4AD9
LD HL,(40D4H) 2A D4 40
Load Register Pair HL with the 16-bit value at memory address 40D4H (the second and third bytes of the just-copied data, repurposed as a buffer pointer).
4ADC
LD (4490H),HL 22 90 44
Store Register Pair HL to memory address 4490H, the buffer pointer.
4ADF
CALL 4BB4H CD B4 4B
GOSUB to 4BB4H, a chain-advance helper.
4AE2
POP HL E1
Restore Register Pair HL from the stack.
4AE3
RET C9
RETurn to the BASIC dispatcher.

4AE4H - PUT Handler (JTABLE entry 16)

Reached via JTABLE entry 16 (4152H+48 = 4182H = JP 4AE4H) for the PUT keyword. Calls FILESPEC parser at 4B59H, validates a comma, fetches a numeric argument via 4AFBH, validates end-of-statement, swaps DE and HL, reads a byte from (HL), stores at 448FH, calls the chain-write at 4BF6H.

4AE4
CALL 4B59H CD 59 4B
GOSUB to FILESPEC parser at 4B59H.
4AE7
RST 08H => 2CH CF 2C
Syntax check requiring a comma (2CH).
4AE9
CALL 4AFBH CD FB 4A
GOSUB to FILESPEC parser at 4AFBH for the numeric argument.
4AEC
DEC HL 2B
DECrement Register Pair HL by 1.
4AED
RST 10H D7
RST 10H, advancing past whitespace.
4AEE
JP NZ,1997H C2 97 19
If NZ, Syntax Error at 1997H.
4AF1
EX DE,HL EB
Exchange Register Pairs DE and HL.
4AF2
LD A,(HL) 7E
Load Register A with the byte at HL (the data byte to PUT).
4AF3
LD (448FH),A 32 8F 44
Store Register A to memory address 448FH (the chain-marker variable, here used as the byte to write).
4AF6
CALL 4BF6H CD F6 4B
GOSUB to 4BF6H, the chain-write routine that writes the byte to disk.
4AF9
EX DE,HL EB
Exchange Register Pairs DE and HL again, restoring the original orientation.
4AFA
RET C9
RETurn to the BASIC dispatcher.

Track 0 / Sector 8 (4AF9H - 4BF7H) - boundary falls at end of preceding PUT handler row at 4AF9H

4AFBH - FILESPEC Parser (Quoted String or Numeric)

Reached from many keyword handlers including FETCH, INPUT, MERGE, KILL, LOAD/RUN, SAVE, APPEND, PUT. Calls Level II BASIC ROM 2337H to fetch a parameter into the accumulator, sets the type-flag at 40AFH to 02H (single-precision), invokes Level II BASIC ROM 2819H (16-bit compare HL:DE), then calls 4B10H (drive-number argument parser). On exit, HL points just past the parameter and DE points at the resolved buffer.

4AFB
PUSH DE D5
Save Register Pair DE onto the stack.
4AFC
PUSH BC C5
Save Register Pair BC onto the stack.
4AFD
CALL 2337H CD 37 23
GOSUB to ROM 2337H to fetch a general parameter.
NOTE: 2337H is the Level II BASIC ROM general-parameter parser. Gets a string, integer, single, or double precision parameter and sets the type flag at 40AFH.
4B00
PUSH HL E5
Save Register Pair HL (BASIC parser pointer) onto the stack.
4B01
LD A,02H 3E 02
Set Register A to 02H, the single-precision type flag value.
4B03
CALL 2819H CD 19 28
GOSUB to ROM 2819H.
NOTE: 2819H is the Level II BASIC ROM CPHLDE routine. Performs a 16-bit comparison of HL against DE. Used here to ensure the parsed value is in the valid range.
4B06
LD HL,(4121H) 2A 21 41
Load Register Pair HL with the 16-bit value at memory address 4121H (the BASIC ACCUM, holding the just-parsed numeric result as a 16-bit integer).
4B09
CALL 4B10H CD 10 4B
GOSUB to 4B10H, the drive-number-and-record-number argument parser.
4B0C
POP HL E1
Restore Register Pair HL from the stack.
4B0D
POP BC C1
Restore Register Pair BC from the stack.
4B0E
POP DE D1
Restore Register Pair DE from the stack.
4B0F
RET C9
RETurn to the caller.

4B10H - Drive-Number/Record-Number Parser

Splits a 16-bit numeric argument into a drive number (high digit) and record number (low digits). Subtracts 2710H (10000 decimal) from HL repeatedly, counting iterations. When the subtraction goes negative, the iteration count is the drive number and the residue is the record number. Falls through to 4B49H to validate the drive number.

4B10
LD DE,2710H 11 10 27
Set Register Pair DE to 2710H (=10000 decimal), the divisor for extracting the drive digit.
4B13
XOR A,A AF
Set Register A to 00H and clear all flags. A is the iteration counter (drive number).
4B14
SBC HL,DE ED 52
Loop Start
SUBtract Register Pair DE from HL with carry borrow. Successive subtractions count down by 10000.
4B16
JP M,4B1CH FA 1C 4B
If the M FLAG (sign minus) has been set (HL went negative, drive count exhausted), JUMP to 4B1CH to restore HL and finalize.
4B19
INC A 3C
INCrement Register A by 1, counting another drive-digit subtraction.
4B1A
JR 4B14H 18 F8
Loop End
LOOP BACK to 4B14H to subtract again.

Work In Progress

This page is being built incrementally. The disassembly through 49ADH has been completed. The remaining 1,747 bytes (49AEH-55FFH) cover the OPEN-existing-file handler, CREATE-file handler, the directory walker (4A4AH), the OPENRD/WRITE handlers, the FILESPEC parser, the SECREAD/FORMAT/FDCSEEK/FDCCMD routines, the ERREXIT jump tables, the BASIC keyword handlers, the NUMIO routines, the utility functions, and the BASIC error message table at 52F1H-5577H. Track/sector boundaries to be inserted: T0/S7 at 49FAH, T0/S8 at 4AF9H, T0/S9 at 4BF8H, T1/S0 at 4CF7H, T1/S1 at 4DF6H, T1/S2 at 4EF5H, T1/S3 at 4FF4H, T1/S4 at 50F3H, T1/S5 at 51F2H, T1/S6 at 52F1H, T1/S7 at 53F0H, T1/S8 at 54EFH, T1/S9 at 55EEH.