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
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 mixed data area containing a source-listing-style symbol table for the SYM command (dispatched via 4E55H), disk configuration parameters, and an auto-load command string.
Each symbol entry follows the general format: NAME + TAB + EQU + TAB + ADDRESS + TABs + ;COMMENT + record separator.
CRLNUM Entry (Current Line Number)
4459-445F
DEFM "CRLNUM",09H 43 52 4C 4E 55 4D 09
Symbol name + TAB separator.
4460-4463
DEFM "EQU",09H 45 51 55 09
"EQU" + TAB.
4464-4468
DEFM "40ECH" 34 30 45 43 48
Address as ASCII hex string.
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 + comment.
447F-4480
DEFB 0CH,17H 0C 17
Entry/record separator.
PRGEND Entry + Disk/FCB Data
4481-4487
DEFM "&PRGEND" 26 50 52 47 45 4E 44
Symbol name (note the & prefix, common for variables).
4488-448B
DEFB 01H,00H,01H,0FFH 01 00 01 FF
Disk FCB-style header (interrupts pure symbol flow).
448C
DEFB 00H 00
Disk parameters (drive, track, sector, etc.).
448D-448F
DEFB 44H,28H,0AH
Disk parameters (drive, track, sector, etc.).
4490-4494
DEFB 03H,00H,00H,00H,0FFH 03 00 00 00 FF
Disk parameters (drive, track, sector, etc.).
4495-44A4
DEFB 0FFH,0FFH,00H,05H,01H,06H,02H,07H,03H,08H,04H,09H FF FF 00 05 01 06 02 07 03 08 04 09
PerCom format track parameter table / step rates.
Auto-Load Command String
44A5
DEFB 0DH 0D
Carriage Return (clear input buffer).
44A6-44AC
DEFM "LOAD 30,R" 4C 4F 41 44 20 33 30 2C 52
Auto-boot command.
44AD
DEFB 0DH 0D
Trailing CR to execute the LOAD.
PRGEND Symbol Continuation
44AE-44B0
DEFB 00H,09H 00 09
Null + TAB (cleanup from previous section).
44B1-44B4
DEFM "EQU",09H 45 51 55 09
"EQU" + TAB.
44B5-44B9
DEFM "40FBH" 34 30 46 42 48
Address for end of simple variables.
44BA-44D5
DEFM 09H,09H,";END OF SIMPLE VARIABLES" 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
Comment.
44D6
DEFB 70H 70
Record separator.
44D7
DEFB 17H 17
Record separator.
ARREND Entry
44D7-44DE
DEFM " ARREND" 20 41 52 52 45 4E 44
Leading space + symbol name.
44DF-44E2
DEFM 09H,"EQU" 09 45 51 55
TAB + "EQU".
44E3-44E8
DEFM 09H,"40FDH" 09 34 30 46 44 48
TAB + address.
44E9-44F7
DEFM 09H,09H,";END OF ARRAYS" 09 09 3B 45 4E 44 20 4F 46 20 41 52 52 41 59 53
Comment.
44F8
DEFB 0A2H,17H A2 17
Record separator.
ACCUM Entry
44F9-4500
DEFM "$ACCUM" 24 41 43 43 55 4D
Symbol name (note the $ prefix).
4501-4521
DEFM 09H,"EQU",09H,"4121H",09H,09H,";NUMBER ACCUMULATOR" 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
Full EQU line + comment.
4522
DEFB 0D4H,17H D4 17
Record separator.
Next Entry Start
4523-4525
DEFB 1EH,"INB" 1E 49 4E 42
Next record type (1EH) + start of "INBU" (likely INBUF or similar).
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
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
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
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
DECrement Register B and LOOP BACK to 45B1H if not zero, continuing the 8-byte signature compare.
45BA
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
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
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
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
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
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 |
| 1 | 0 | 0 | m | b | E | 0 | 0 | | Command=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
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
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
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
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
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 |
| 1 | 1 | 0 | 1 | I3 | I2 | I1 | I0 | | Command=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
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
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
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 |
| 0 | 0 | 0 | 1 | h | V | r1 | r0 | | Command=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
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
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
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
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
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
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
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
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
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
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
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
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
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
Token entry 0: JP to MicroDOS error code 4E25H.
4697
Token entry 1: JP to MicroDOS error code 4E43H.
469A
Token entry 2: JP to MicroDOS error code 4E28H.
469D
Token entry 3: JP to MicroDOS error code 4E3DH.
46A0
Token entry 4: JP to MicroDOS error code 4E2BH.
46A3
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
Token entry 6: JP to MicroDOS error code 4E4FH.
46A9
Token entry 7: JP to MicroDOS error code 4E4CH.
46AC
Token entry 8: JP to MicroDOS error code 4E1CH.
46AF
Token entry 9: JP to MicroDOS error code 4E1FH.
46B2
Token entry 10: JP to MicroDOS error code 4E22H.
46B5
Token entry 11: JP to BASTOK at 47C9H (CMD/CALL token dispatcher with single-letter argument).
46B8
Token entry 12: JP to ROM 012DH (NAVERR: loads error code 2CH and jumps to the BASIC error handler at 19A2H).
46BB
Token entry 13: JP to ROM 012DH (NAVERR: loads error code 2CH and jumps to the BASIC error handler at 19A2H).
46BE
Token entry 14: JP to MicroDOS error code 4E19H.
46C1
Token entry 15: JP to OPENRD (read sector handler) at 4AAEH.
46C4
Token entry 16: JP to write-sector handler at 4AE4H.
46C7
Token entry 17: JP to ROM 012DH (NAVERR: loads error code 2CH and jumps to the BASIC error handler at 19A2H).
46CA
Token entry 18: JP to OPEN-existing-file handler at 49AEH.
46CD
Token entry 19: JP to CREATE-file handler at 49F6H.
46D0
Token entry 20: JP to ROM 012DH (NAVERR: loads error code 2CH and jumps to the BASIC error handler at 19A2H).
46D3
Token entry 21: JP to ROM 012DH (NAVERR: loads error code 2CH and jumps to the BASIC error handler at 19A2H).
46D6
Token entry 22: JP to MicroDOS error code 4E3AH.
46D9
Token entry 23: JP to MicroDOS error code 4E2EH.
46DC
Token entry 24: JP to MicroDOS error code 4E31H.
46DF
Token entry 25: JP to MicroDOS error code 4E58H.
46E2
Token entry 26: JP to OPEN-file handler entry at 4941H.
46E5
Token entry 27: JP to MicroDOS error code 4E37H.
46E8
Token entry 28: JP to MicroDOS error code 4E34H.
46EB
Token entry 29: JP to MicroDOS error code 4E40H.
46EE
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
Token entry 31: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
46F4
Token entry 32: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
46F7
Token entry 33: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
46FA
Token entry 34: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
46FD
Token entry 35: JP to MicroDOS error code 4E49H.
4700
Token entry 36: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4703
Token entry 37: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4706
Token entry 38: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4709
Token entry 39: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
470C
Token entry 40: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
470F
Token entry 41: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4712
Token entry 42: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4715
Token entry 43: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4718
Token entry 44: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
471B
Token entry 45: JP to MicroDOS error code 4E55H.
471E
Token entry 46: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4721
Token entry 47: JP to ROM 0043H (VDPRT video output trampoline at 0043H, used as a placeholder for unimplemented BASIC tokens).
4724
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
If the Z FLAG has been set (character is 'M'), jump forward to 4841H (the MERGE handler).
47EF
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
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
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
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
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
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
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
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
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
If NZ, Syntax Error at 1997H.
4822
PUSH HL E5
Save Register Pair HL onto the stack.
4823
GOSUB to FILEIO at 4870H to load the file.
4826
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
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.
4835
PUSH HL E5
Save Register Pair HL onto the stack.
4836
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
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
GOSUB to 4B3FH (FILESPEC parser).
4846
DEC HL 2B
DECrement Register Pair HL by 1.
4847
RST 10H D7
RST 10H, advancing past whitespace.
484B
PUSH HL E5
Save Register Pair HL onto the stack.
484C
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
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
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
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
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
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
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
Verify Loop Start
GOSUB to the verify-track routine at 48BCH, which reads back the track and counts errors.
488B
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
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
GOSUB to 4DBCH again to issue a step-back.
4897
GOSUB to FORMAT at 4C91H to re-format the failed track.
489A
GOSUB to verify the track again at 48BCH.
489D
If the Z FLAG has been set (re-format-and-verify succeeded), jump forward to 48B5H.
489F
GOSUB to FDC Restore command routine at 4DACH (return head to track 0).
48A2
GOSUB to Restore-then-Seek at 4D89H (re-seek to the current target track).
48A5
GOSUB to FORMAT at 4C91H to re-format after the calibration.
48A8
GOSUB to verify the track at 48BCH.
48AB
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
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
GOSUB to FDCSEEK at 4D44H to position the FDC head at the current track/sector.
48C9
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
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
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
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).
48E2
LD E,34H 1E 34
Set Register E to 34H, the error code for "Parse Error".
48E6
LD E,32H 1E 32
Set Register E to 32H, an error code.
48E8
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.
48EE
LD E,42H 1E 42
Set Register E to 42H, an error code (66 decimal).
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
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
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
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
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
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
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
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
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
GOSUB to ROM 28A7H to display the "LAST SECTOR USED = " string.
NOTE: 28A7H is the Level II BASIC ROM OUTSTR routine.
495D
GOSUB to 4E52H, an internal routine that fetches the last-used sector value into the floating-point accumulator.
4960
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
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
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
GOSUB to 497DH, the chain-marker-write helper.
4972
GOSUB to 498FH, the sector-advance helper.
4975
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
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
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
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
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
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
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
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
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
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
GOSUB to 558CH, the BASIC program load helper at the resident's tail. Loads sector data from the chain into memory at HL.
49D0
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
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
GOSUB to the sector-advance helper at 498FH to advance the SECTOR variable to the next track/sector.
49DD
LOOP BACK to 49CCH to continue loading the next sector of the chain.
49DF
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
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
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
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
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
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
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
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
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
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
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
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
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
GOSUB to ROM 1B5DH (BASIC RUN initialization).
4A45
LOOP BACK to 4A08H to read the next directory entry.
4A47
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
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
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
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
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
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
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
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
If the NZ FLAG has been set (previous chain ended), jump forward to 4AA6H to set up an end-of-chain state.
4A8B
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
If the NZ FLAG has been set (chain marker is non-FFH = end-of-chain), skip the sector-advance.
4A9C
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
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
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
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
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
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
If the C FLAG has been set (A < C), jump forward to 4ADFH to skip the byte-overflow handling.
4ACA
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
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
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
GOSUB to FILESPEC parser at 4B59H.
4AE7
RST 08H => 2CH CF 2C
Syntax check requiring a comma (2CH).
4AE9
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
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
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
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
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
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
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
Loop End
LOOP BACK to 4B14H to subtract again.
4B1CH - Drive-Number Parser Continuation / Record Validation
Reached when the drive-digit subtraction loop at 4B14H detects HL has gone negative. Restores HL by adding back the over-subtracted DE (=2710H), validates the drive number via 4B49H, then converts the residual record number into a track/sector pair. The records-per-track value is at 4492H and the bytes-per-record value is at 4493H. Computes track = record / records-per-track and sector = record MOD records-per-track. On exit, 448DH = track and 448EH = sector.
4B1C
ADD HL,DE 19
ADD Register Pair DE (=2710H) to HL. Restores HL to the value before the last over-subtraction in the 4B14H loop. HL now holds the residual after extracting the drive digit (= record number, range 0-9999).
4B1D
GOSUB to 4B49H, the drive-number validator (validates A < 4 and builds the one-hot drive-select mask at 448CH).
4B20
LD A,(4492H) 3A 92 44
Load Register A with the byte at memory address 4492H (the records-per-track count).
4B23
LD B,A 47
Copy Register A (records-per-track) into Register B for use as the DJNZ counter.
4B24
LD A,(4493H) 3A 93 44
Load Register A with the byte at memory address 4493H (the bytes-per-record count, used here as the divisor).
4B27
LD E,A 5F
Copy Register A (bytes-per-record) into Register E.
4B28
LD D,00H 16 00
Set Register D to 00H. DE now holds the bytes-per-record value as a 16-bit number.
4B2A
XOR A,A AF
Set Register A to 00H and clear all flags. A is the track-counter accumulator.
4B2B
SBC HL,DE ED 52
Loop Start
SUBtract Register Pair DE (bytes-per-record) from HL with carry borrow. Each iteration subtracts one record's worth.
4B2D
If the M FLAG has been set (HL went negative, the requested record exceeds this track), JUMP to 4B36H to finalize track and sector.
4B30
INC A 3C
INCrement Register A by 1, counting another sector subtracted.
4B31
DECrement Register B (records-per-track countdown) and LOOP BACK to 4B2BH if not zero.
4B33
JUMP to 48DEH (the "Invalid Sector Number" error setup) - the requested record exceeded the maximum, so issue an error.
4B36
ADD HL,DE 19
ADD Register Pair DE back to HL, restoring HL to the residual sector offset within the track (positive value).
4B37
LD (448DH),A 32 8D 44
Store Register A (the track number) to memory address 448DH (the track variable of SECTOR).
4B3A
LD A,L 7D
Copy the low byte of HL (the sector number, range 0-9) into Register A.
4B3B
LD (448EH),A 32 8E 44
Store Register A (the sector number) to memory address 448EH (the sector variable of SECTOR).
4B3E
RET C9
RETurn to the caller with 448DH = track, 448EH = sector, and the drive-select mask at 448CH already set.
4B3FH - Single-Drive-Argument Parser
Used by FETCH (47F4H), HEX (481AH), KILL (4843H) and similar handlers that take only a drive number (no record number). Calls the BASIC ROM general-parameter parser, then validates the value is an integer-type 8-bit, then falls through to 4B49H to validate the drive number.
4B3F
GOSUB to ROM 2337H to fetch a general parameter into the BASIC accumulator.
NOTE: 2337H is the Level II BASIC ROM general-parameter parser. Returns with type flag at 40AFH set.
4B42
GOSUB to ROM 2B05H.
NOTE: 2B05H is the Level II BASIC ROM type-flag tester. Returns Z if the type flag at 40AFH indicates an integer; NZ otherwise.
4B45
If the NZ FLAG has been set (the parameter was not an integer), JUMP to 48DAH (the "Invalid Drive Number" error setup).
4B48
LD A,E 7B
Copy Register E (the low byte of the integer parameter from BASIC) into Register A. This is the drive number, expected to be 0-3.
4B49H - Drive-Number Validator and Drive-Select Mask Builder
Validates that Register A holds a drive number 0-3. If valid, converts the drive number into a one-hot drive-select mask (drive 0 = 01H, drive 1 = 02H, drive 2 = 04H, drive 3 = 08H) and stores at 448CH. If invalid, jumps to the "Invalid Drive Number" error path at 48DAH.
4B49
CP A,04H FE 04
Compare Register A against 04H. If A < 4, C FLAG is set; if A >= 4, NC.
4B4B
If the NC FLAG has been set (drive number 4 or higher), JUMP to 48DAH (the "Invalid Drive Number" error setup).
4B4E
INC A 3C
INCrement Register A by 1. A is now drive_number+1, the iteration count for the rotation loop.
4B4F
LD B,A 47
Copy Register A (rotation iteration count) into Register B for use as the DJNZ counter.
4B50
LD A,80H 3E 80
Set Register A to 80H, the seed value for the RLCA rotation. After the loop with B iterations, A will hold the one-hot mask: B=1 -> 01H, B=2 -> 02H, B=3 -> 04H, B=4 -> 08H.
4B52
RLCA 07
Loop Start
Rotate Register A Left Circular through itself. Each iteration shifts the set bit one position to the left (with wraparound from bit 7 to bit 0).
4B53
DECrement Register B and LOOP BACK to 4B52H if not zero. After exit, A holds the one-hot drive-select mask.
4B55
LD (448CH),A 32 8C 44
Store Register A (the drive-select mask) to memory address 448CH (the drive-select mask variable).
4B58
RET C9
RETurn to the caller with 448CH containing the one-hot drive mask.
4B59H - FILESPEC Parser (String or Number)
Used by handlers that accept either a string filespec or a numeric record specifier. If the next character is "#" (23H, indicating a previously-OPENed file handle), calls 4E46H to look up the file-handle table at 5600H. Otherwise calls Level II BASIC ROM 260DH (VARPTR) and ROM 0AF4H (MOVEA) to fetch a string variable's address and length, then sets the buffer pointer at 4490H. Returns with C = string length (or FFH for #handle), buffer pointer prepared.
4B59
CP A,23H FE 23
Compare Register A against 23H ("#" character). Sets Z FLAG if the upcoming character is the file-handle prefix.
4B5B
If the NZ FLAG has been set (not a "#" prefix), jump forward to 4B69H to handle a string-variable filespec.
4B5D
GOSUB to 4E46H, the file-handle resolver. Returns DE pointing to the entry in the handle table at 5600H for the requested handle.
4B60
INC DE 13
INCrement Register Pair DE by 1, advancing past the first byte of the handle entry.
4B61
LD (4490H),DE ED 53 90 44
Store Register Pair DE to memory address 4490H, setting the buffer pointer to the handle's data.
4B65
DEC DE 1B
DECrement Register Pair DE by 1, restoring DE to the handle entry's first byte.
4B66
LD C,0FFH 0E FF
Set Register C to 0FFH, indicating "file-handle" mode (length is unknown / handle-managed).
4B68
RET C9
RETurn to the caller with C=0FFH and 4490H pointing to the handle's data area.
4B69
GOSUB to ROM 260DH.
NOTE: 260DH is the Level II BASIC ROM VARPTR routine. Gets the address of a variable into HL.
4B6C
GOSUB to ROM 0AF4H.
NOTE: 0AF4H is the Level II BASIC ROM MOVEA routine. Moves the variable descriptor into the work area.
4B6F
PUSH DE D5
Save Register Pair DE onto the stack.
4B70
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now points at the string descriptor.
4B71
LD C,(HL) 4E
Load Register C with the byte at HL (the string length, first byte of the descriptor).
4B72
INC HL 23
INCrement HL by 1, advancing to the second descriptor byte.
4B73
LD A,(HL) 7E
Load Register A with the byte at HL (the low byte of the string-data pointer).
4B74
LD (4490H),A 32 90 44
Store Register A (string pointer low byte) to memory address 4490H.
4B77
INC HL 23
INCrement HL by 1, advancing to the third descriptor byte.
4B78
LD A,(HL) 7E
Load Register A with the byte at HL (the high byte of the string-data pointer).
4B79
LD (4491H),A 32 91 44
Store Register A (string pointer high byte) to memory address 4491H. 4490H-4491H now holds the full pointer to the string data.
4B7C
EX DE,HL EB
Exchange Register Pairs DE and HL again, restoring orientation.
4B7D
POP DE D1
Restore Register Pair DE from the stack.
4B7E
RET C9
RETurn to the caller with C = string length and 4490H-4491H pointing at the string data.
4B7FH - Sector-Read-and-Buffer-Copy Wrapper
Saves the working registers, calls the disk-read routine at 4B8CH, then calls the buffer-to-RAM copy routine at 4BB4H, then restores the registers. Used as a single-shot read-and-copy primitive by handlers that need to fetch a sector and immediately move its contents to a target buffer.
4B7F
PUSH HL E5
Save Register Pair HL onto the stack.
4B80
PUSH DE D5
Save Register Pair DE onto the stack.
4B81
PUSH BC C5
Save Register Pair BC onto the stack.
4B82
GOSUB to 4B8CH, the FDC sector-read routine that loads the requested sector into the buffer at 4300H.
4B85
GOSUB to 4BB4H, the buffer-copy helper that moves the chain-marker count of bytes from 4300H to the destination buffer.
4B88
POP BC C1
Restore Register Pair BC from the stack.
4B89
POP DE D1
Restore Register Pair DE from the stack.
4B8A
POP HL E1
Restore Register Pair HL from the stack.
4B8B
RET C9
RETurn to the caller.
4B8CH - Sector-Read Routine (FDC Read with Retry)
Performs a single sector read from the disk into the buffer at 4300H. Calls 4D44H to seek the requested track and select the drive, then calls 4BC4H to issue the read command (with up to 3 retries). On error, attempts a recalibration sequence (CALL 4DACH at 4BA3H, CALL 4D89H, retry 4BC4H). If the third retry also fails, jumps to the "Disk Read/Write Error" path at 48E6H. After successful read, captures the chain-marker byte from 4300H (offset 0 of the sector) into 448FH.
4B8C
GOSUB to 4D44H, the FDC seek-and-select routine that positions the head over the track stored at 448DH on the drive selected by 448CH.
4B8F
GOSUB to 4BC4H, the FDC sector-read with up-to-3-retries helper. Returns with C FLAG clear if successful, set if all retries failed.
4B92
If the NC FLAG has been set (read succeeded), jump forward to 4BAFH to capture the chain-marker.
4B94
LD A,43H 3E 43
Set Register A to 43H, an FDC RESTORE command (Type I, with verify and 6ms step rate).
4B96
GOSUB to 4DBCH, the FDC command issuer with status-poll.
4B99
LD A,63H 3E 63
Set Register A to 63H, an FDC SEEK command variant (Type I, no verify, with head-load-V flag set).
4B9B
GOSUB to 4DBCH again to issue the seek command.
4B9E
GOSUB to 4BC4H to retry the read after recalibration.
4BA1
If the NC FLAG has been set (read succeeded), jump forward to 4BAFH.
4BA3
GOSUB to 4DACH, an FDC head-load helper that reads the FDC status and tests for ready.
4BA6
GOSUB to 4D89H, the seek-and-verify retry helper.
4BA9
GOSUB to 4BC4H once more to retry the read after the secondary recovery.
4BAC
If the C FLAG has been set (all retries failed), JUMP to 48E6H (the "Disk Read/Write Error" error setup).
4BAF
LD A,(HL) 7E
Load Register A with the byte at HL (=4300H, the start of the sector buffer, holding the chain-marker).
4BB0
LD (448FH),A 32 8F 44
Store Register A (the chain-marker) to memory address 448FH (the chain-marker / bytes-remaining variable).
4BB3
RET C9
RETurn to the caller with the sector loaded at 4300H and 448FH = chain-marker.
4BB4H - Buffer-To-RAM Copy Helper
Copies the sector buffer at 4300H+1 to the destination at 4490H. Reads the chain-marker (bytes-remaining count) from 4300H into Register C, sets B=00H, then performs an LDIR (block copy of BC bytes). Returns immediately if the count is zero.
4BB4
LD HL,4300H 21 00 43
Set Register Pair HL to 4300H, the start of the sector buffer.
4BB7
LD C,(HL) 4E
Load Register C with the byte at HL (=4300H, the chain-marker which doubles as the byte count for this sector's data).
4BB8
INC HL 23
INCrement HL by 1, advancing past the chain-marker to the first data byte at 4301H.
4BB9
XOR A,A AF
Set Register A to 00H and clear all flags.
4BBA
LD B,A 47
Copy Register A (=00H) into Register B. BC now holds the byte count (low byte = chain-marker, high byte = 0).
4BBB
OR A,C B1
Logical OR Register A with Register C, setting Z FLAG if C=00H (no bytes to copy).
4BBC
RET Z C8
If the Z FLAG has been set (zero bytes), RETurn immediately.
4BBD
LD DE,(4490H) ED 5B 90 44
Load Register Pair DE with the 16-bit value at memory address 4490H (the destination buffer pointer).
4BC1
LDIR ED B0
Block-move BC bytes from (HL) to (DE). Copies the sector data from 4301H to the destination.
4BC3
RET C9
RETurn to the caller.
4BC4H - Sector-Read with 3 Retries
Issues the FDC read at 4BCDH; if it returns NC (success), returns immediately. Otherwise retries up to 3 times via DJNZ. Returns with C set if all retries failed.
4BC4
LD B,03H 06 03
Set Register B to 03H, the maximum number of read retries.
4BC6
Loop Start
GOSUB to 4BCDH, the actual FDC sector-read primitive. Returns NC on success, C on failure.
4BC9
RET NC D0
If the NC FLAG has been set (read succeeded), RETurn to the caller immediately.
4BCA
Loop End
DECrement Register B and LOOP BACK to 4BC6H if not zero. After all 3 retries fail, falls through.
4BCC
RET C9
RETurn to the caller with C FLAG set (read failed after retries).
4BCDH - FDC Sector Read Primitive (with DRQ Polling via EXX)
Issues an FDC READ SECTOR command (88H) via 4DE3H, then polls the FDC status register at (DE) for the DRQ bit (bit 1). When DRQ is set, reads a data byte from (HL) and stores it at (BC=4300H), incrementing BC each iteration. Uses the alternate register set (EXX) to keep BC, DE, HL available for the polling loop while preserving the caller's main registers. On status bits 9CH (record-not-found, CRC error, lost-data, write-protect), saves status at 4495H and writes 0D0H (FORCE INTERRUPT) to terminate. Returns with C FLAG set on error.
4BCD
LD A,88H 3E 88
Set Register A to 88H, an FDC READ SECTOR command (Type II, no multiple-records, head-load delay enabled, sector-length 256).
4BCF
GOSUB to 4DE3H, the FDC command issuer (writes A to FDC command register at 37ECH and sets up DE/HL/BC for the data-transfer loop).
4BD2
EXX D9
EXchange the main register set with the alternate set, putting the data-transfer working registers (BC=4300H, DE=37ECH FDC status, HL=37EFH FDC data) into the active set.
4BD3
JUMP to 4BDBH to enter the polling loop.
4BD5
AND A,81H E6 81
Logical AND Register A with 81H (mask = bit 7 NOT_READY + bit 0 BUSY).
4BD7
XOR A,01H EE 01
Logical XOR Register A with 01H, flipping the BUSY bit. Result is zero only if BUSY was set and NOT_READY was clear (FDC actively transferring).
4BD9
If the NZ FLAG has been set (FDC stopped or NOT_READY), jump forward to 4BE5H to handle completion or error.
4BDB
LD A,(DE) 1A
Polling Loop Start
Load Register A with the byte at (DE=37ECH), the FDC status register.
4BDC
BIT 1,A CB 4F
Test bit 1 (DRQ - Data Request) of Register A. Sets Z FLAG if DRQ is clear, NZ if data is ready.
4BDE
If the Z FLAG has been set (DRQ not yet asserted), LOOP BACK to 4BD5H to recheck the BUSY/READY status.
4BE0
LD A,(HL) 7E
Load Register A with the byte at (HL=37EFH), the FDC data register.
4BE1
LD (BC),A 02
Store Register A (the just-read data byte) to (BC), the current sector-buffer write pointer.
4BE2
INC BC 03
INCrement Register Pair BC by 1, advancing the buffer write pointer.
4BE3
Polling Loop End
LOOP BACK to 4BDBH to wait for the next DRQ.
4BE5
LD A,(DE) 1A
Load Register A with the byte at (DE=37ECH), the FDC status register, captured one final time.
4BE6
AND A,9CH E6 9C
Logical AND Register A with 9CH (mask = bit 7 NOT_READY + bit 4 RECORD_NOT_FOUND + bit 3 CRC_ERROR + bit 2 LOST_DATA).
4BE8
LD BC,4300H 01 00 43
Set Register Pair BC to 4300H. HL will be loaded as 4300H below.
4BEB
If the Z FLAG has been set (no error bits), jump forward to 4BF4H to return success.
4BED
LD A,(DE) 1A
Load Register A with the byte at (DE=37ECH), the FDC status, retrieved again to capture the error code.
4BEE
LD (4495H),A 32 95 44
Store Register A (the FDC error status) to memory address 4495H (the saved-FDC-status variable).
4BF1
LD (HL),0D0H 36 D0
Store 0D0H to (HL=37ECH), the FDC FORCE INTERRUPT command. Terminates any pending operation cleanly.
4BF3
SCF 37
Set the C FLAG, signaling failure to the caller.
4BF4
EXX D9
EXchange register sets back to the main register set.
4BF5
RET C9
RETurn to the caller with C clear (success) or C set (error).
Track 0 / Sector 9 (4BF8H - 4CF6H) - boundary falls within following PUT-byte chain-write routine at 4BF8H
4BF6H - PUT-Byte Chain-Write Routine
Writes the byte at 448FH (the chain-marker variable, repurposed as the byte to PUT) to the current sector position on disk. Saves working registers, calls 4D44H to seek and select the drive, tests the FDC step-rate flags at 37ECH for write-protect (bit 6) and aborts to 48E2H if write-protected, copies the buffer count from 448FH into BC, exchanges DE<->HL, writes the byte, optionally LDIRs additional bytes, then calls the actual sector-write at 4C36H, with retry-and-recalibrate logic identical to the read path.
4BF6
PUSH HL E5
Save Register Pair HL onto the stack.
4BF7
PUSH DE D5
Save Register Pair DE onto the stack.
4BF8
PUSH BC C5
Save Register Pair BC onto the stack.
4BF9
GOSUB to 4D44H, the FDC seek-and-select routine.
4BFC
LD A,(37ECH) 3A EC 37
Load Register A with the byte at memory address 37ECH (the FDC status register).
4BFF
BIT 6,A CB 77
Test bit 6 of Register A (the WRITE PROTECT bit of FDC status during a Type II/III command).
4C01
If the NZ FLAG has been set (write-protect detected), JUMP to 48E2H (the "Disk Write Protected" error setup).
4C04
LD A,(448FH) 3A 8F 44
Load Register A with the byte at memory address 448FH (the byte to PUT, set by the PUT handler at 4AF3H).
4C07
LD C,A 4F
Copy Register A (the data byte) into Register C. C also serves as the LDIR low-byte count below.
4C08
LD B,00H 06 00
Set Register B to 00H. BC = the data byte / count value padded to 16 bits.
4C0A
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds what DE had (the destination address); DE holds the source.
4C0B
LD (DE),A 12
Store Register A (the data byte) to (DE), placing it at the start of the source buffer.
4C0C
INC DE 13
INCrement Register Pair DE by 1, advancing the source pointer past the just-written byte.
4C0D
OR A,A B7
Logical OR Register A with itself, setting Z FLAG if A=00H.
4C0E
If the Z FLAG has been set (count is zero), jump forward to 4C12H to skip the LDIR.
4C10
LDIR ED B0
Block-move BC bytes from (HL) to (DE). Used here to fill subsequent bytes; counter is the just-loaded data byte.
4C12
GOSUB to 4C36H, the FDC sector-write with up-to-3-retries helper.
4C15
If the NC FLAG has been set (write succeeded), jump forward to 4C32H to clean up.
4C17
LD A,43H 3E 43
Set Register A to 43H, an FDC RESTORE command.
4C19
GOSUB to 4DBCH to issue the RESTORE.
4C1C
LD A,63H 3E 63
Set Register A to 63H, an FDC SEEK command variant.
4C1E
GOSUB to 4DBCH to issue the SEEK.
4C21
GOSUB to 4C36H to retry the write after recalibration.
4C24
If the NC FLAG has been set (write succeeded), jump forward to 4C32H.
4C26
GOSUB to 4DACH, an FDC head-load helper.
4C29
GOSUB to 4D89H, the seek-and-verify retry helper.
4C2C
GOSUB to 4C36H one more time.
4C2F
If the C FLAG has been set (still failed), JUMP to 48E6H (the "Disk Read/Write Error" error setup).
4C32
POP BC C1
Restore Register Pair BC from the stack.
4C33
POP DE D1
Restore Register Pair DE from the stack.
4C34
POP HL E1
Restore Register Pair HL from the stack.
4C35
RET C9
RETurn to the caller.
4C36H - Sector-Write with 3 Retries (and verify-via-read)
Issues the FDC write at 4C44H, then verifies via 4C6DH (a re-read). If both succeed, returns NC. Otherwise retries up to 3 times via DJNZ.
4C36
LD B,03H 06 03
Set Register B to 03H, the maximum number of write retries.
4C38
Loop Start
GOSUB to 4C44H, the actual FDC sector-write primitive.
4C3B
If the C FLAG has been set (write failed), jump forward to 4C41H to retry via DJNZ.
4C3D
GOSUB to 4C6DH, the verify-by-read primitive (issues another READ to confirm the data was written correctly).
4C40
RET NC D0
If the NC FLAG has been set (verify succeeded), RETurn to the caller.
4C41
Loop End
DECrement Register B and LOOP BACK to 4C38H if not zero. After 3 retries fail, falls through.
4C43
RET C9
RETurn to the caller with C FLAG set if all retries failed.
4C44H - FDC Sector Write Primitive
Mirror image of the read primitive at 4BCDH but for write. Issues an FDC WRITE SECTOR command (0A8H) via 4DE3H, then polls the FDC status register at (DE) for the DRQ bit and shovels bytes from (BC) to (HL) until the FDC reports busy-cleared. On error bits 0E4H (NOT_READY, WRITE_PROTECT, WRITE_FAULT, LOST_DATA), saves status at 4495H and writes 0D0H FORCE INTERRUPT.
4C44
LD A,0A8H 3E A8
Set Register A to 0A8H, an FDC WRITE SECTOR command (Type II, no multiple-records, head-load delay enabled).
4C46
GOSUB to 4DE3H to issue the command.
4C49
EXX D9
EXchange register sets, putting BC=4300H source pointer / DE=37ECH FDC status / HL=37EFH FDC data into active.
4C4A
JUMP to 4C52H to enter the polling loop.
4C4C
AND A,81H E6 81
Logical AND Register A with 81H (mask = bit 7 NOT_READY + bit 0 BUSY).
4C4E
XOR A,01H EE 01
Logical XOR Register A with 01H, flipping the BUSY bit. Result is zero only if BUSY was set and NOT_READY was clear.
4C50
If the NZ FLAG has been set (FDC stopped or error), jump forward to 4C5CH to handle completion or error.
4C52
LD A,(DE) 1A
Polling Loop Start
Load Register A with the byte at (DE=37ECH), the FDC status register.
4C53
BIT 1,A CB 4F
Test bit 1 (DRQ) of Register A.
4C55
If the Z FLAG has been set (DRQ not yet asserted), LOOP BACK to 4C4CH to recheck status.
4C57
LD A,(BC) 0A
Load Register A with the byte at (BC), the source buffer position to write to disk.
4C58
LD (HL),A 77
Store Register A (the data byte) to (HL=37EFH), the FDC data register.
4C59
INC BC 03
INCrement Register Pair BC by 1, advancing the source pointer.
4C5A
Polling Loop End
LOOP BACK to 4C52H to wait for the next DRQ.
4C5C
LD A,(DE) 1A
Load Register A with the byte at (DE=37ECH), the FDC status, captured for error analysis.
4C5D
AND A,0E4H E6 E4
Logical AND Register A with 0E4H (mask = bit 7 NOT_READY + bit 6 WRITE_PROTECT + bit 5 WRITE_FAULT + bit 2 LOST_DATA).
4C5F
LD BC,4300H 01 00 43
Set Register Pair BC to 4300H, restoring the buffer pointer base.
4C62
If the Z FLAG has been set (no error bits), jump forward to 4C6BH to return success.
4C64
LD A,(DE) 1A
Load Register A with the byte at (DE=37ECH), the FDC status, retrieved again.
4C65
LD (4495H),A 32 95 44
Store Register A (the FDC error status) to memory address 4495H.
4C68
LD (HL),0D0H 36 D0
Store 0D0H to (HL=37ECH), the FDC FORCE INTERRUPT command.
4C6A
SCF 37
Set the C FLAG, signaling failure.
4C6B
EXX D9
EXchange register sets back.
4C6C
RET C9
RETurn to the caller.
4C6DH - Verify-By-Read Primitive
Issues an FDC READ SECTOR (88H) and reads the data, but discards it (does not store). Used after a write to verify that the sector can be read back without error. If the read succeeds with no status error bits, returns NC. On error bits 9CH (NOT_READY, RECORD_NOT_FOUND, CRC_ERROR, LOST_DATA), saves status at 4495H, writes FORCE INTERRUPT, sets C.
4C6D
LD A,88H 3E 88
Set Register A to 88H, an FDC READ SECTOR command (same as 4BCDH).
4C6F
GOSUB to 4DE3H to issue the command.
4C72
EXX D9
EXchange register sets.
4C73
JUMP to 4C7BH to enter the polling loop.
4C75
AND A,81H E6 81
Logical AND Register A with 81H (NOT_READY + BUSY).
4C77
XOR A,01H EE 01
Logical XOR Register A with 01H, flipping BUSY.
4C79
If the NZ FLAG has been set (FDC stopped or error), jump forward to 4C83H.
4C7B
LD A,(DE) 1A
Polling Loop Start
Load Register A with the byte at (DE=37ECH), the FDC status.
4C7C
BIT 1,A CB 4F
Test bit 1 (DRQ) of Register A.
4C7E
If the Z FLAG has been set (DRQ clear), LOOP BACK to 4C75H.
4C80
LD A,(HL) 7E
Load Register A with the byte at (HL=37EFH), the FDC data register. This consumes the byte but does not store it (verify-only).
4C81
Polling Loop End
LOOP BACK to 4C7BH to consume the next byte.
4C83
LD A,(DE) 1A
Load Register A with the byte at (DE=37ECH), the final FDC status.
4C84
AND A,9CH E6 9C
Logical AND Register A with 9CH (NOT_READY + RECORD_NOT_FOUND + CRC_ERROR + LOST_DATA).
4C86
If the Z FLAG has been set (no error), jump forward to 4C8FH to return success.
4C88
LD A,(DE) 1A
Load Register A with the byte at (DE=37ECH).
4C89
LD (4495H),A 32 95 44
Store Register A (FDC error status) to memory address 4495H.
4C8C
LD (HL),0D0H 36 D0
Store 0D0H to (HL=37ECH), the FDC FORCE INTERRUPT command.
4C8E
SCF 37
Set the C FLAG.
4C8F
EXX D9
EXchange register sets back.
4C90
RET C9
RETurn to the caller.
4C91H - FORMAT TRACK Routine
Formats the current track of the disk. Saves working registers, calls 4D44H to seek and select drive, tests for write-protect bit 6 of FDC status (jumps to 48E2H "Disk Write Protected" on error), then issues an FDC WRITE TRACK command (0F4H) and feeds the gap-byte sequence required by the IBM 3740 SD format used by the Model I. Each byte stream is fed via the helper at 4D2BH, which polls DRQ and writes Register C to the FDC data register. The track structure consists of: 0EH bytes of FFH (post-index gap), then 06H bytes of 00H (sync), then F4H/F5H index AM, then 06H bytes of 00H, then F7H/F8H, then ten sector regions each consisting of: 0EH bytes 00H, F5H, F8H, F9H, sector header (FE+T+S+L+CRC1+CRC2), gap, F5H, FB, 256 data bytes, F7, gap. Final 0FFH fills track tail until INDEX detected.
4C91
PUSH HL E5
Save Register Pair HL onto the stack.
4C92
PUSH DE D5
Save Register Pair DE onto the stack.
4C93
PUSH BC C5
Save Register Pair BC onto the stack.
4C94
GOSUB to 4D44H, the FDC seek-and-select routine.
4C97
LD A,(37ECH) 3A EC 37
Load Register A with the byte at memory address 37ECH (the FDC status register).
4C9A
BIT 6,A CB 77
Test bit 6 of Register A (the WRITE_PROTECT bit).
4C9C
If the NZ FLAG has been set (write-protect detected), JUMP to 48E2H (the "Disk Write Protected" error setup).
4C9F
LD C,03H 0E 03
Set Register C to 03H, the maximum number of format retries.
4CA1
LD A,(4493H) 3A 93 44
Format-Retry Loop Start
Load Register A with the byte at memory address 4493H (the records-per-track count).
4CA4
LD B,A 47
Copy Register A (records-per-track) into Register B for use as the sector-count loop variable.
4CA5
LD HL,449BH 21 9B 44
Set Register Pair HL to 449BH (the format-track parameter table at 449BH-44A4H).
4CA8
LD A,0F4H 3E F4
Set Register A to 0F4H, an FDC WRITE TRACK command (Type III, with head-load delay enabled).
4CAA
GOSUB to 4DE3H to issue the WRITE TRACK command.
4CAD
EXX D9
EXchange register sets, putting BC=4300H / DE=37ECH FDC status / HL=37EFH FDC data into active.
4CAE
LD C,0FFH 0E FF
Set Register C (alternate set) to 0FFH (gap byte for FFH fill region).
4CB0
LD B,0EH 06 0E
Set Register B (alternate set) to 0EH (=14 decimal), the byte count for the post-index gap (14 FFH bytes).
4CB2
GOSUB to 4D2BH, the byte-stream helper that writes B copies of C to the FDC data register.
4CB5
LD C,00H 0E 00
Per-Sector Format Loop Start
Set Register C (alternate set) to 00H (sync byte).
4CB7
LD B,06H 06 06
Set Register B to 06H, the byte count for the pre-index-AM sync (6 zero bytes).
4CB9
GOSUB to 4D2BH to write 6 zero bytes.
4CBC
LD C,0FEH 0E FE
Set Register C to 0FEH, the IBM 3740 ID-Address-Mark byte (sector header start).
4CBE
INC B 04
INCrement Register B by 1. B was 00H from the DJNZ at 4D31H; now B=01H to write a single byte.
4CBF
GOSUB to 4D2BH to write the 0FEH ID Address Mark.
4CC2
LD A,(448DH) 3A 8D 44
Load Register A with the byte at memory address 448DH (the track number).
4CC5
LD C,A 4F
Copy Register A (track number) into Register C.
4CC6
INC B 04
INCrement Register B by 1. B is now 01H.
4CC7
GOSUB to 4D2BH to write the track-number byte to the sector header.
4CCA
LD C,B 48
Copy Register B (=00H, the side number for single-sided disk) into Register C.
4CCB
INC B 04
INCrement Register B by 1.
4CCC
GOSUB to 4D2BH to write the side-number byte (=00H).
4CCF
EXX D9
EXchange register sets back to main.
4CD0
LD A,(HL) 7E
Load Register A with the byte at HL (the next sector number from the format-table at 449BH).
4CD1
INC HL 23
INCrement HL by 1, advancing the format-table pointer.
4CD2
EXX D9
EXchange register sets back to alternate.
4CD3
LD C,A 4F
Copy Register A (the sector number from the format-table) into Register C.
4CD4
INC B 04
INCrement Register B by 1.
4CD5
GOSUB to 4D2BH to write the sector number to the header.
4CD8
INC B 04
INCrement Register B by 1.
4CD9
LD C,B 48
Copy Register B (now 01H, the sector-length code 01H = 256 bytes for IBM 3740) into Register C.
4CDA
GOSUB to 4D2BH to write the sector-length code byte.
4CDD
LD C,0F7H 0E F7
Set Register C to 0F7H, the FDC's "write CRC" sentinel byte (causes WD1771 to compute and write the 2-byte CRC).
4CDF
INC B 04
INCrement Register B by 1.
4CE0
GOSUB to 4D2BH to write the F7H CRC sentinel.
4CE3
LD C,0FFH 0E FF
Set Register C to 0FFH (gap byte).
4CE5
LD B,0CH 06 0C
Set Register B to 0CH (=12 decimal), the byte count for the inter-AM gap.
4CE7
GOSUB to 4D2BH to write 12 FFH gap bytes.
4CEA
LD C,B 48
Copy Register B (now 00H) into Register C (sync byte).
4CEB
LD B,06H 06 06
Set Register B to 06H, the count for the pre-DAM sync.
4CED
GOSUB to 4D2BH to write 6 zero bytes.
4CF0
LD C,0FBH 0E FB
Set Register C to 0FBH, the IBM 3740 Data Address Mark.
4CF2
INC B 04
INCrement Register B by 1.
4CF3
GOSUB to 4D2BH to write the 0FBH DAM byte.
4CF6
LD C,B 48
Copy Register B (=00H) into Register C (data fill byte).
Track 1 / Sector 0 (4CF7H - 4DF5H) - boundary falls within preceding FORMAT loop at 4CF7H
4CF7
INC B 04
INCrement Register B by 1.
4CF8
GOSUB to 4D2BH to write a single 00H byte.
4CFB
LD C,0E5H 0E E5
Set Register C to 0E5H, the standard IBM 3740 sector-data fill byte (so freshly formatted sectors read as repeated E5H pattern).
4CFD
DEC B 05
DECrement Register B by 1. B becomes the count to write 256 fill bytes via the "expanded" code path.
4CFE
GOSUB to 4D2BH to write 0FFH copies (B wrapped to 0FFH after DEC) of E5H, then 1 more for 256 total fill bytes.
4D01
LD C,0F7H 0E F7
Set Register C to 0F7H, the CRC sentinel.
4D03
INC B 04
INCrement Register B by 1. B=01H.
4D04
GOSUB to 4D2BH to write the CRC sentinel.
4D07
LD C,0FFH 0E FF
Set Register C to 0FFH (gap byte).
4D09
LD B,0CH 06 0C
Set Register B to 0CH, the byte count for the inter-sector gap.
4D0B
GOSUB to 4D2BH to write 12 FFH gap bytes.
4D0E
EXX D9
EXchange register sets to main set.
4D0F
DEC B 05
DECrement Register B by 1 (the main-set sector-count loop variable, set at 4CA4H from records-per-track).
4D10
EXX D9
EXchange register sets back to alternate.
4D11
Per-Sector Format Loop End
If the NZ FLAG has been set (more sectors to format), LOOP BACK to 4CB5H to write the next sector.
4D13
LD C,0FFH 0E FF
Set Register C to 0FFH (track-tail gap byte).
4D15
GOSUB to 4D2BH; with B=0 from the loop this is a single-byte fill that continues until the FDC indicates BUSY=0.
4D18
OR A,A B7
Logical OR Register A with itself, setting Z flag if A=0 (no error).
4D19
If the Z FLAG has been set (no error), jump forward to 4D21H to clean up.
4D1B
DEC C 0D
DECrement Register C by 1 (the format-retry counter).
4D1C
If the Z FLAG has been set (all retries exhausted), JUMP to 48F2H (the "Disk Format" error setup).
4D1F
Format-Retry Loop End
LOOP BACK to 4CA1H to retry the format.
4D21
POP BC C1
Restore Register Pair BC from the stack.
4D22
POP DE D1
Restore Register Pair DE from the stack.
4D23
POP HL E1
Restore Register Pair HL from the stack.
4D24
RET C9
RETurn to the caller.
4D25H - Byte-Stream Helper for Format (Status-Polling Sub-Loop)
Used inside the FORMAT TRACK byte-feed loop at 4D2BH. Tests bits 7+5+2+0 (NOT_READY, RECORD_TYPE, LOST_DATA, BUSY) and bit 7+0 only after XOR 01. If FDC has gone NOT_BUSY without error, jumps to 4D34H to compute the post-mortem status. Otherwise falls through to 4D2BH to keep feeding bytes.
4D25
AND A,0E5H E6 E5
Logical AND Register A with 0E5H (mask = bit 7 NOT_READY + bit 6 WRITE_PROTECT + bit 5 WRITE_FAULT + bit 2 LOST_DATA + bit 0 BUSY).
4D27
XOR A,01H EE 01
Logical XOR Register A with 01H, flipping the BUSY bit. Result is non-zero if any error bit was set OR if BUSY went clear.
4D29
If the NZ FLAG has been set (FDC stopped or error during format), jump forward to 4D34H to capture status.
4D2B
LD A,(DE) 1A
Byte-Feed Loop Start
Load Register A with the byte at (DE=37ECH), the FDC status.
4D2C
BIT 1,A CB 4F
Test bit 1 (DRQ) of Register A.
4D2E
If the Z FLAG has been set (DRQ clear), LOOP BACK to 4D25H to recheck status.
4D30
LD (HL),C 71
Store Register C (the format byte) to (HL=37EFH), the FDC data register.
4D31
Byte-Feed Loop End
DECrement Register B and LOOP BACK to 4D2BH if not zero.
4D33
RET C9
RETurn to the caller.
4D34
EX DE,HL EB
Exchange Register Pairs DE and HL.
4D35
LD A,(HL) 7E
Load Register A with the byte at HL (the FDC status now in HL because of the swap).
4D36
EX DE,HL EB
Exchange Register Pairs DE and HL again.
4D37
EXX D9
EXchange register sets to main.
4D38
POP DE D1
Restore Register Pair DE from the stack (the caller's return address that was pushed at 4D2BH entry).
4D39
LD HL,4D18H 21 18 4D
Set Register Pair HL to 4D18H, the address to compare DE against (the format-loop status-check entry).
4D3C
OR A,A B7
Logical OR Register A with itself, clearing the C flag for the SBC HL,DE below.
4D3D
SBC HL,DE ED 52
SUBtract Register Pair DE from HL with carry borrow. Tests if the return address equals 4D18H.
4D3F
If the Z FLAG has been set (return address was 4D18H, called from format), JUMP to 4D18H with A=status.
4D41
CPL 2F
Complement Register A (one's-complement, inverts all bits). Coerces a non-format-loop error into the bit-pattern needed.
4D42
JUMP to 4D18H with the inverted status.
4D44H - FDC Seek-and-Select Routine
Selects the drive specified by the one-hot mask at 448CH and seeks the head to the track at 448DH. Uses the per-drive step-rate selector at 4497H-449AH to choose the appropriate FDC step rate (FFH = needs recalibration via 4DACH). Sets up DE=37ECH (FDC status), HL=37EFH (FDC data), then calls 4DF6H for the actual seek. Reads the current track from 37EDH and compares against the desired track; calls 4D89H if seek-and-verify is needed. After successful seek, sets up HL=4300H, DE=(4490H) for any subsequent buffer operations and clears 4495H (saved FDC status).
4D44
LD HL,4497H 21 97 44
Set Register Pair HL to 4497H, the start of the per-drive step-rate selector array (4 bytes for drives 0-3).
4D47
LD A,(448CH) 3A 8C 44
Load Register A with the byte at memory address 448CH (the one-hot drive-select mask).
4D4A
LD B,04H 06 04
Set Register B to 04H, the maximum number of drives to scan.
4D4C
RRA 1F
Drive-Slot Search Loop Start
Rotate Register A right through the C FLAG. Shifts the one-hot bit toward bit 0; when it reaches bit 0, C FLAG is set (drive found).
4D4D
If the C FLAG has been set (the drive bit found in this position), jump forward to 4D55H with HL pointing at the matching step-rate selector.
4D4F
INC HL 23
INCrement HL by 1, advancing to the next drive's step-rate selector.
4D50
Drive-Slot Search Loop End
DECrement Register B and LOOP BACK to 4D4CH if not zero.
4D52
JUMP to 48DAH (the "Invalid Drive Number" error setup) - the drive-select mask was zero or invalid.
4D55
PUSH HL E5
Save Register Pair HL onto the stack (the pointer to this drive's step-rate selector).
4D56
LD BC,4300H 01 00 43
Set Register Pair BC to 4300H, the sector buffer address (used by the EXX'd transfer loops).
4D59
LD DE,37ECH 11 EC 37
Set Register Pair DE to 37ECH, the FDC status/command register address.
4D5C
LD HL,37EFH 21 EF 37
Set Register Pair HL to 37EFH, the FDC data register address.
4D5F
EXX D9
EXchange the main register set with the alternate set, parking BC/DE/HL into the alternate set for the data-transfer loops.
4D60
GOSUB to 4DF6H, the drive-select-and-step-rate-test routine. Updates 37E1H (drive-select latch) and tests if the drive needs recalibration.
4D63
POP HL E1
Restore Register Pair HL from the stack (the per-drive step-rate selector pointer).
4D64
LD A,(HL) 7E
Load Register A with the byte at HL (the per-drive step-rate selector value, FFH = needs recalibration).
4D65
LD (37EDH),A 32 ED 37
Store Register A to memory address 37EDH (the FDC track register), preparing the seek operation.
4D68
INC A 3C
INCrement Register A by 1. If selector was FFH (needs recalibration), A becomes 00H setting Z FLAG.
4D69
If the Z FLAG has been set (needs recalibration), conditionally GOSUB to 4DACH to perform an FDC RESTORE.
4D6C
LD A,(448DH) 3A 8D 44
Load Register A with the byte at memory address 448DH (the desired track number).
4D6F
LD (HL),A 77
Store Register A (desired track) to (HL), updating the per-drive step-rate selector to remember the current track.
4D70
LD A,(37EDH) 3A ED 37
Load Register A with the byte at memory address 37EDH (the FDC's current track register).
4D73
CP A,(HL) BE
Compare Register A against (HL) (the desired track now stored). Sets Z if already on the right track, NZ otherwise.
4D74
If the NZ FLAG has been set (track mismatch), conditionally GOSUB to 4D89H to issue an FDC SEEK and verify.
4D77
LD A,(448EH) 3A 8E 44
Load Register A with the byte at memory address 448EH (the desired sector number).
4D7A
LD (37EEH),A 32 EE 37
Store Register A (sector number) to memory address 37EEH (the FDC sector register).
4D7D
LD HL,4300H 21 00 43
Set Register Pair HL to 4300H, the sector-buffer base address.
4D80
LD DE,(4490H) ED 5B 90 44
Load Register Pair DE with the 16-bit value at memory address 4490H (the destination buffer pointer).
4D84
XOR A,A AF
Set Register A to 00H and clear all flags.
4D85
LD (4495H),A 32 95 44
Store Register A (=00H) to memory address 4495H, clearing the saved-FDC-status variable.
4D88
RET C9
RETurn to the caller with the FDC positioned, the buffer pointer in DE, and HL=4300H.
4D89H - Seek-and-Verify with Recalibrate Retry
Issues an FDC SEEK to the track stored in 448DH and verifies via 4D97H. If the verify fails (NC return from 4D97H), recalibrates via 4DACH and tries again. If the second verify also fails, jumps to "Disk Seek Error" at 48EEH.
4D89
GOSUB to 4D97H, which issues an FDC SEEK to the desired track and tests the verify result.
4D8C
RET NC D0
If the NC FLAG has been set (seek-verify succeeded), RETurn to the caller.
4D8D
GOSUB to 4DACH to issue an FDC RESTORE (recalibrate).
4D90
GOSUB to 4D97H to retry the seek-and-verify after recalibration.
4D93
If the C FLAG has been set (still failed), JUMP to 48EEH (the "Disk Seek Error" error setup).
4D96
RET C9
RETurn to the caller.
4D97H - Single Seek-and-Verify Pass
Stores the desired track from 448DH at the FDC data register 37EFH, builds an FDC SEEK command from the step-rate flags at 4494H low nibble OR'd with 10H (verify-flag for Type I), and issues it via 4DBCH. Tests the resulting status against bits 98H (NOT_READY + RECORD_NOT_FOUND + CRC_ERROR) and returns NC if no error, C set otherwise.
4D97
LD A,(448DH) 3A 8D 44
Load Register A with the byte at memory address 448DH (the desired track number).
4D9A
LD (37EFH),A 32 EF 37
Store Register A (desired track) to memory address 37EFH (the FDC data register, used for SEEK target).
4D9D
LD A,(4494H) 3A 94 44
Load Register A with the byte at memory address 4494H (the FDC step-rate flags).
4DA0
AND A,03H E6 03
Logical AND Register A with 03H, isolating the step-rate selector bits (00=3ms, 01=6ms, 02=10ms, 03=15ms for WD1771).
4DA2
OR A,10H F6 10
Logical OR Register A with 10H. 10H is the FDC SEEK opcode with V (verify) flag set; OR'ing with the step-rate produces the complete command.
4DA4
GOSUB to 4DBCH, the FDC command issuer with status-poll. Returns with FDC status in Register A.
4DA7
AND A,98H E6 98
Logical AND Register A with 98H (mask = bit 7 NOT_READY + bit 4 RECORD_NOT_FOUND + bit 3 CRC_ERROR).
4DA9
RET Z C8
If the Z FLAG has been set (no error bits), RETurn with NC (success).
4DAA
SCF 37
Set the C FLAG, signaling failure.
4DAB
RET C9
RETurn to the caller with C set.
4DACH - FDC RESTORE (Recalibrate to Track 0)
Builds an FDC RESTORE command (Type I) from the step-rate flags at 4494H low nibble (no extra flags - bits 4-7 are zero in the opcode). Issues the command via 4DBCH, tests the result against bits 9CH (NOT_READY + RECORD_NOT_FOUND + CRC_ERROR + LOST_DATA), XOR'd with 04H (LOST_DATA expected from RESTORE). If the result is non-zero (real error), jumps to "Disk Seek Error" at 48EEH.
4DAC
LD A,(4494H) 3A 94 44
Load Register A with the byte at memory address 4494H (the FDC step-rate flags).
4DAF
AND A,03H E6 03
Logical AND Register A with 03H, isolating the step-rate bits.
4DB1
GOSUB to 4DBCH. With A=00H-03H, this is an FDC RESTORE command (opcode bits 0-3 are step rate, bits 4-7 are zero = RESTORE).
4DB4
AND A,9CH E6 9C
Logical AND Register A with 9CH (NOT_READY + RECORD_NOT_FOUND + CRC_ERROR + LOST_DATA).
4DB6
XOR A,04H EE 04
Logical XOR Register A with 04H, flipping the LOST_DATA bit. RESTORE on Track 0 produces a benign LOST_DATA flag; this clears it from the test.
4DB8
RET Z C8
If the Z FLAG has been set (no real error), RETurn to the caller.
4DB9
JUMP to 48EEH (the "Disk Seek Error" error setup).
4DBCH - FDC Command Issuer (Type I, with status return)
Issues a Type I (RESTORE/SEEK/STEP) command to the FDC. Calls 4DE3H to write the command and start polling, then 4DBFH to wait for completion. On exit, Register A contains the final FDC status (with NOT_BUSY).
4DBC
GOSUB to 4DE3H, the FDC command-write-and-poll-start routine.
4DBF
EXX D9
EXchange the main register set with the alternate set, parking the working registers (BC/DE/HL) into the alternate set.
4DC0
PUSH BC C5
Save Register Pair BC onto the stack.
4DC1
LD BC,0000H 01 00 00
Set Register Pair BC to 0000H, the timeout counter (will count down through 65535 iterations).
4DC4
LD A,(448CH) 3A 8C 44
Status-Poll Loop Start
Load Register A with the byte at memory address 448CH (the drive-select mask, used here to keep the latch refreshed).
4DC7
LD (37E1H),A 32 E1 37
Store Register A to memory address 37E1H (the drive-select latch). The latch is one-shot, so each poll iteration must re-write it.
4DCA
DEC BC 0B
DECrement Register Pair BC by 1 (the timeout counter).
4DCB
LD A,B 78
Copy Register B (high byte of timeout) into Register A.
4DCC
OR A,C B1
Logical OR Register A with Register C, setting Z FLAG if BC=0000H (timeout exhausted).
4DCD
If the Z FLAG has been set (timeout), jump forward to 4DDAH to handle the timeout.
4DCF
LD A,(DE) 1A
Load Register A with the byte at (DE=37ECH), the FDC status register.
4DD0
LD (4495H),A 32 95 44
Store Register A (current FDC status) to memory address 4495H.
4DD3
BIT 0,A CB 47
Test bit 0 (BUSY) of Register A.
4DD5
If the NZ FLAG has been set (still BUSY), LOOP BACK to 4DC4H to keep polling.
4DD7
POP BC C1
Restore Register Pair BC from the stack.
4DD8
EXX D9
EXchange register sets back to main.
4DD9
RET C9
RETurn to the caller with Register A holding the final FDC status.
4DDA
LD A,0D0H 3E D0
Set Register A to 0D0H, the FDC FORCE INTERRUPT command (terminates any pending operation).
4DDC
LD (37ECH),A 32 EC 37
Store Register A to memory address 37ECH (the FDC command register), forcing a clean shutdown.
4DDF
EXX D9
EXchange register sets back to main.
4DE0
JUMP to 48EAH (the "Disk Missing or Door Open" error setup) - the FDC never came back ready.
4DE3H - FDC Command Write Helper
Saves Register A on the stack, calls 4DF6H to refresh the drive-select latch and check the step-rate selector, calls 4DBFH to start the busy-poll, then writes the saved Register A to the FDC command register at 37ECH. After writing, polls bit 0 (BUSY) and waits until BUSY is set, indicating the FDC has accepted the command.
4DE3
PUSH AF F5
Save Register AF onto the stack (preserves the FDC command byte in A).
4DE4
GOSUB to 4DF6H, the drive-select-and-step-rate-test helper.
4DE7
GOSUB to 4DBFH (the EXX-and-status-poll setup, partial entry into 4DBCH at the EXX instruction).
4DEA
POP AF F1
Restore Register AF from the stack (the FDC command byte).
4DEB
EXX D9
EXchange register sets to alternate, getting DE=37ECH FDC status / HL=37EFH FDC data.
4DEC
LD (DE),A 12
Store Register A (the FDC command byte) to (DE=37ECH), the FDC command register. This issues the command.
4DED
EXX D9
EXchange register sets back to main.
4DEE
LD A,(37ECH) 3A EC 37
Busy-Set Wait Loop Start
Load Register A with the byte at memory address 37ECH (the FDC status register).
4DF1
BIT 0,A CB 47
Test bit 0 (BUSY) of Register A.
4DF3
If the Z FLAG has been set (BUSY not yet asserted), LOOP BACK to 4DEEH to recheck.
4DF5
RET C9
RETurn to the caller with the FDC actively executing the command.
Track 1 / Sector 1 (4DF6H - 4EF4H) - boundary falls at start of following Drive-Select Routine at 4DF6H
4DF6H - Drive-Select-and-Step-Rate-Test Helper
Refreshes the drive-select latch at 37E1H from the mask at 448CH. Tests bit 7 (sign) of the byte at the per-drive step-rate selector pointer in HL: if set, the drive is "fresh" and a debounce delay is applied (decrementing pair from 0xFFFF + 256 DJNZ inner loop totaling about 16ms) before re-asserting drive-select. If bit 7 clear, returns immediately. Validates that the lower nibble is non-zero or jumps to "Invalid Drive Number" 48DAH.
4DF6
EXX D9
EXchange register sets to main, parking the FDC pointers in alternate.
4DF7
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the per-drive step-rate selector pointer (was in DE before).
4DF8
BIT 7,(HL) CB 7E
Test bit 7 (sign) of the byte at HL (the per-drive step-rate selector). Bit 7 set indicates "drive freshly selected, debounce needed".
4DFA
LD A,(448CH) 3A 8C 44
Load Register A with the byte at memory address 448CH (the drive-select mask).
4DFD
LD (37E1H),A 32 E1 37
Store Register A to memory address 37E1H, asserting the one-hot drive-select latch.
4E00
EX DE,HL EB
Exchange Register Pairs DE and HL again, restoring orientation.
4E01
If the Z FLAG has been set (bit 7 was clear, no debounce needed), jump forward to 4E17H to skip the delay.
4E03
AND A,0FH E6 0F
Logical AND Register A with 0FH, masking off the upper nibble of the drive-select mask. Checks that at least one drive bit is set.
4E05
If the Z FLAG has been set (no drive bits in lower nibble), JUMP to 48DAH (the "Invalid Drive Number" error).
4E08
PUSH BC C5
Save Register Pair BC onto the stack.
4E09
XOR A,A AF
Set Register A to 00H and clear all flags.
4E0A
LD B,A 47
Copy Register A (=00H, which means 256 iterations for DJNZ) into Register B.
4E0B
DEC A 3D
Inner Delay Loop Start
DECrement Register A by 1. Each pass through this loop and the JR NZ at 4E0C makes A wrap around 0xFF-0x01 several times.
4E0C
If the NZ FLAG has been set, LOOP BACK to 4E0BH. This inner loop runs 256 iterations (until A wraps to 0).
4E0E
Inner Delay Loop End
DECrement Register B and LOOP BACK to 4E0BH. Outer loop runs 256 times, totaling 256x256 = 65536 inner cycles for an approximate 16ms drive-spin-up debounce.
4E10
LD A,(448CH) 3A 8C 44
Load Register A with the byte at memory address 448CH (the drive-select mask).
4E13
LD (37E1H),A 32 E1 37
Store Register A to memory address 37E1H, re-asserting the drive-select latch (which has gone stale during the delay).
4E16
POP BC C1
Restore Register Pair BC from the stack.
4E17
EXX D9
EXchange register sets back to alternate (where the FDC pointers BC/DE/HL live).
4E18
RET C9
RETurn to the caller.
4E19H - ERREXIT Jump Table (BASIC Token Dispatch)
A 17-entry jump table that the JTABLE entries (relocated to 4152H-41E4H) point to. Each entry is a 3-byte JP instruction that vectors to the actual handler for a particular BASIC keyword or token. This indirection lets MicroDOS replace the original BASIC handler addresses with patched ones, while still going through the same 4152H+ dispatch.
4E19
JUMP to 4EC0H, the OPEN-keyword secondary handler (handles "OPEN A" / "OPEN R" syntax).
4E1C
JUMP to 4F12H, the OPEN-mode-2 handler.
4E1F
JUMP to 4F16H, the OPEN-mode-4 handler.
4E22
JUMP to 4F1AH, the OPEN-mode-8 handler.
4E25
JUMP to 4F2DH, the CLOSE-mode-1 handler.
4E28
JUMP to 4F31H, the CLOSE-mode-3 handler.
4E2B
JUMP to 4F35H, the CLOSE-mode-7 handler.
4E2E
JUMP to 4F4CH, the LOAD-without-RUN handler entry.
4E31
JUMP to 4F4FH, the LOAD-with-RUN handler entry.
4E34
JUMP to 52DEH, the BASIC error message dispatch routine (uses Reg E as error code).
4E37
JUMP to 501DH, the AS keyword (string formatter helper).
4E3A
JUMP to 503EH, the &H/&O hexadecimal/octal-literal parser.
4E3D
JUMP to 507AH, the LSET / RSET / FIELD assignment helper.
4E40
JUMP to 519EH, the field-buffer-by-handle resolver.
4E43
JUMP to 5092H, the FIELD keyword dispatcher.
4E46
JUMP to 4E66H, the file-handle resolver (called from FILESPEC parser at 4B5DH).
4E49
JUMP to 4E5BH, the file-handle table initializer.
4E4C
JUMP to 5010H, the EOF function handler.
4E4F
JUMP to 4FB4H, the LOC/LOF/POS/CSRLIN function handler.
4E52
JUMP to 4FC7H, the LOC function handler (calculates current sector position).
4E55
JUMP to 51DBH, the GET keyword handler (random-access file read).
4E58
JUMP to 5257H, the MID$/LEFT$/RIGHT$/INSTR statement handler.
4E5BH - File-Handle Table Initializer
Zeros the 4-byte file-handle directory at 5600H, 5700H, 5800H, 5900H (one byte per handle: handle 0 reserved, handles 1-3 user-allocatable). Used by the FILES / OPEN preamble.
4E5B
LD B,04H 06 04
Set Register B to 04H, the count of handle pages to zero.
4E5D
LD HL,5600H 21 00 56
Set Register Pair HL to 5600H, the start of the file-handle table.
4E60
XOR A,A AF
Set Register A to 00H and clear all flags.
4E61
LD (HL),A 77
Loop Start
Store Register A (=00H) to (HL), zeroing this handle entry's first byte.
4E62
INC H 24
INCrement Register H by 1, advancing HL by 256 bytes (next handle page: 5700H, 5800H, 5900H).
4E63
Loop End
DECrement Register B and LOOP BACK to 4E61H if not zero. Zeros first byte of all 4 handle pages.
4E65
RET C9
RETurn to the caller.
4E66H - File-Handle Resolver
Parses a file-handle reference of the form #N or N (where N is 1-3) and returns DE pointing to the handle's table entry at 5600H+N. If the next character is "#" (23H), advances past it via RST 10H. Calls Level II BASIC ROM 2B1CH to fetch the integer handle number, validates it is 1-3, and computes 5600H + N. On invalid handle, jumps to ERROR 3EH ("Invalid File Buffer Number") via 19A2H.
4E66
CP A,23H FE 23
Compare Register A against 23H ("#" character). Sets Z FLAG if the next char is the handle prefix.
4E68
If the NZ FLAG has been set (no "#" prefix), jump forward to 4E6BH.
4E6A
RST 10H D7
RST 10H, advancing the BASIC parser pointer past the "#" character.
4E6B
GOSUB to ROM 2B1CH.
NOTE: 2B1CH is the Level II BASIC ROM integer-parser routine. Returns Register A holding the parsed integer 0-255.
4E6E
DEC A 3D
DECrement Register A by 1. After this, valid handles 1-3 become 0-2, and 0 becomes 0FFH (will fail the CP test).
4E6F
CP A,04H FE 04
Compare Register A against 04H. C FLAG set if A < 4 (valid 0-3 after decrement).
4E71
If the C FLAG has been set (handle valid), jump forward to 4E78H to compute the address.
4E73
LD E,3EH 1E 3E
Set Register E to 3EH, the BASIC error code for "Invalid File Buffer Number".
4E75
JUMP to ROM 19A2H.
NOTE: 19A2H is the Level II BASIC ROM ERROR routine. Displays the message indexed by Register E and returns to READY.
4E78
LD DE,5600H 11 00 56
Set Register Pair DE to 5600H, the base of the file-handle table.
4E7B
ADD A,D 82
ADD Register D (=56H) to Register A, computing the high byte of the handle's table entry (56H + handle_num = 56H, 57H, 58H, or 59H).
4E7C
LD D,A 57
Copy Register A back into Register D. DE now points at the handle's page (5600H, 5700H, 5800H, or 5900H).
4E7D
RET C9
RETurn to the caller with DE = handle table entry pointer.
4E7EH - FIELD Keyword Body
Implements the FIELD keyword which assigns string variables to fixed-byte regions of a random-access file's record buffer. Calls 4E66H to resolve the file handle, then iterates: each iteration parses a comma, an integer (field width), the AS keyword (token A0H = AS), and a string variable name; stores the field width and updates the running offset. Loop continues until end-of-statement.
4E7E
RST 10H D7
RST 10H, advancing the BASIC parser past whitespace.
4E7F
GOSUB to 4E66H to resolve the file handle (#N). Returns DE pointing to the handle's table entry.
4E82
PUSH DE D5
Save Register Pair DE (handle entry pointer) onto the stack.
4E83
INC DE 13
INCrement Register Pair DE by 1, advancing past the handle's status byte to the field-data area.
4E84
LD BC,0000H 01 00 00
Set Register Pair BC to 0000H, the running offset accumulator.
4E87
XOR A,A AF
Set Register A to 00H and clear all flags.
4E88
EX DE,HL EB
Field-Loop Start
Exchange Register Pairs DE and HL. HL now holds the field pointer.
4E89
ADD HL,BC 09
ADD Register Pair BC (the running offset) to HL.
4E8A
LD B,A 47
Copy Register A (=00H) into Register B. Resets B for next field.
4E8B
EX DE,HL EB
Exchange Register Pairs DE and HL again.
4E8C
LD A,(HL) 7E
Load Register A with the byte at HL (the next BASIC source character).
4E8D
CP A,2CH FE 2C
Compare Register A against 2CH (","). Z FLAG set if it's a comma (more fields), NZ if end-of-statement.
4E8F
If the NZ FLAG has been set (no more fields), jump forward to 4EB9H to finalize.
4E91
PUSH DE D5
Save Register Pair DE onto the stack.
4E92
PUSH BC C5
Save Register Pair BC (running offset) onto the stack.
4E93
GOSUB to ROM 2B1BH (a wrapper that increments HL past the comma then calls 2B1CH).
NOTE: 2B1BH is the Level II BASIC ROM RST 10H + integer-parse combo. Returns A = parsed integer.
4E96
PUSH AF F5
Save Register AF (the field width) onto the stack.
4E97
RST 08H CF
RST 08H, the BASIC syntax-check vector that requires the next byte to match the inline operand.
4E98
DB 41H 41
RST 08H operand: 41H = ASCII 'A'. This is the literal character 'A', not a tokenized keyword (BASIC tokens are 80H and above). Together with the next pair this requires the user typed "AS" un-tokenized after the field width.
4E99
RST 08H CF
RST 08H, second syntax check.
4E9A
DB 53H 53
RST 08H operand: 53H, literal "S". Together with 41H this requires the user typed "AS".
4E9B
GOSUB to ROM 260DH.
NOTE: 260DH is the Level II BASIC ROM VARPTR routine. Returns DE pointing at the variable's descriptor.
4E9E
GOSUB to ROM 0AF4H.
NOTE: 0AF4H is the Level II BASIC ROM MOVEA routine. Copies the descriptor into the work area.
4EA1
POP AF F1
Restore Register AF from the stack (the field width).
4EA2
POP BC C1
Restore Register Pair BC from the stack (the running offset).
4EA3
EX (SP),HL E3
Exchange the top of the stack with HL. Stack-top now holds the parser pointer; HL holds the saved field-data pointer.
4EA4
LD C,A 4F
Copy Register A (field width) into Register C.
4EA5
LD A,B 78
Copy Register B (high byte of running offset, normally 00H) into Register A.
4EA6
ADD A,C 81
ADD Register C (the field width) to Register A.
4EA7
If the NC FLAG has been set (no overflow past 256), jump forward to 4EAEH to record the field.
4EA9
LD E,3AH 1E 3A
Set Register E to 3AH, the BASIC error code for "Field Overflow".
4EAB
JUMP to ROM 19A2H, the error display routine.
4EAE
EX DE,HL EB
Exchange Register Pairs DE and HL.
4EAF
LD (HL),C 71
Store Register C (field width) to (HL), recording it in the field table.
4EB0
INC HL 23
INCrement HL by 1.
4EB1
LD (HL),E 73
Store Register E (low byte of variable address) to (HL).
4EB2
INC HL 23
INCrement HL by 1.
4EB3
LD (HL),D 72
Store Register D (high byte of variable address) to (HL).
4EB4
LD B,00H 06 00
Set Register B to 00H. Resets B for the next field iteration.
4EB6
POP HL E1
Restore Register Pair HL from the stack (the BASIC parser pointer).
4EB7
Field-Loop End
LOOP BACK to 4E88H to parse the next field.
4EB9
POP DE D1
Restore Register Pair DE from the stack (the saved handle entry pointer).
4EBA
LD A,(DE) 1A
Load Register A with the byte at (DE), the current field count in the handle.
4EBB
CP A,B B8
Compare Register A (current count) against Register B (newly-counted field count).
4EBC
RET NC D0
If the NC FLAG has been set (existing count >= new count), RETurn (no shrink needed).
4EBD
LD A,B 78
Copy Register B (new field count) into Register A.
4EBE
LD (DE),A 12
Store Register A (new field count) to (DE), updating the handle's count.
4EBF
RET C9
RETurn to the caller.
4EC0H - LOAD / SAVE Keyword Handler with #N AS filename$
Implements the MicroDOS extension syntax "LOAD #N AS filename$" and "SAVE #N AS filename$", reached via the ERREXIT table at 4E19H = JP 4EC0H. On entry Register A holds the BASIC keyword token that was peeked by the caller: token 0A7H is the Disk BASIC LOAD keyword, token 0ADH is the SAVE keyword. Anything else falls through to ROM 1997H (Syntax Error). After validating the token, the routine advances the parser, calls 4E66H to resolve a "#N" file-handle reference, requires the next parser byte to be token 0BFH (AS), then takes one of two paths. The LOAD path (token 0A7H) calls GETPAR (2337H) to evaluate the filename string expression, walks its 3-byte descriptor (length + data pointer), and copies the filename into the handle entry. The SAVE path (token 0ADH) calls VARPTR (260DH) for the destination string variable's address and MOVEA (0AF4H) to copy its descriptor to the work area, then optionally calls STRINI / STRSPA to allocate fresh string space if the existing buffer is too small. Both paths converge at the common LDIR-copy-to-handle epilogue at 4F08H.
NOTE: Token assignments confirmed against the canonical Level II / Disk BASIC token table: 0A7H = LOAD, 0ADH = SAVE, 0BFH = AS.
4EC0
PUSH AF F5
Save Register AF onto the stack. Register A holds the BASIC keyword token (0A7H = LOAD or 0ADH = SAVE) that was peeked by the caller.
4EC1
CP A,0A7H FE A7
Compare Register A against 0A7H, the Level II BASIC token for LOAD. Z FLAG set if it matches.
4EC3
If the Z FLAG has been set (token is LOAD), jump forward to 4ECAH to begin parsing.
4EC5
CP A,0ADH FE AD
Compare Register A against 0ADH, the Level II BASIC token for SAVE. Z FLAG set if it matches.
4EC7
If the NZ FLAG has been set (token is neither LOAD nor SAVE), JUMP to ROM 1997H to raise a Syntax Error.
NOTE: 1997H is the Level II BASIC ROM SNERR (Syntax Error) entry point.
4ECA
RST 10H D7
RST 10H, advancing the BASIC parser pointer past the keyword token and any whitespace.
4ECB
GOSUB to 4E66H, the file-handle resolver. Parses the "#N" reference and returns DE pointing at the handle's table entry (one of 5600H, 5700H, 5800H, 5900H).
4ECE
RST 08H CF
RST 08H, the BASIC syntax-check vector that requires the next parser byte to match the inline operand at 4ECFH.
NOTE: RST 08H is the Level II BASIC ROM SYNTAX check.
4ECF
DB 0BFH BF
RST 08H operand: 0BFH, the Disk BASIC token for the AS keyword. Requires the user typed "AS" after the file handle (e.g. "LOAD #1 AS filename$" or "SAVE #1 AS filename$").
4ED0
POP AF F1
Restore Register AF from the stack (the saved keyword token, 0A7H = LOAD or 0ADH = SAVE).
4ED1
CP A,0A7H FE A7
Compare Register A against 0A7H (LOAD). Z FLAG set if LOAD was the keyword token.
4ED3
If the NZ FLAG has been set (keyword was SAVE, not LOAD), jump forward to 4EE9H to take the SAVE path.
4ED5
PUSH DE D5
LOAD Path
Save Register Pair DE (the handle table entry pointer) onto the stack.
4ED6
GOSUB to ROM 2337H, the BASIC GETPAR routine. Parses a general parameter expression and sets the type flag at 40AFH; for APPEND this is the string filename expression.
NOTE: 2337H is the Level II BASIC ROM GETPAR (Get General Parameter) routine.
4ED9
EX (SP),HL E3
Exchange the top of the stack with HL. Stack-top now holds the parser pointer; HL holds the saved handle entry pointer (DE was pushed at 4ED5H).
4EDA
PUSH HL E5
Save Register Pair HL (handle entry pointer) back onto the stack. This sequence has effectively saved both the parser pointer and the handle pointer with the parser pointer on top.
4EDB
GOSUB to ROM 29D7H, the BASIC string-pointer-to-data resolver. Returns HL pointing at the 3-byte string descriptor (length, low-addr, high-addr).
NOTE: 29D7H is the Level II BASIC ROM string descriptor accessor.
4EDE
LD A,(HL) 7E
Load Register A with the byte at HL, the string length (count of characters in the filename).
4EDF
INC HL 23
INCrement HL by 1, advancing to the low byte of the string-data pointer.
4EE0
LD E,(HL) 5E
Load Register E with the byte at HL (low byte of the string-data address).
4EE1
INC HL 23
INCrement HL by 1, advancing to the high byte of the string-data pointer.
4EE2
LD D,(HL) 56
Load Register D with the byte at HL (high byte of the string-data address). DE now holds the address of the actual filename characters in string space.
4EE3
POP HL E1
Restore Register Pair HL from the stack (the handle entry pointer, saved at 4EDAH).
4EE4
LD (HL),A 77
Store Register A (string length) to (HL), writing the filename length into the first byte of the handle entry.
4EE5
INC HL 23
INCrement HL by 1. HL now points at the destination for the filename data within the handle entry.
4EE6
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the source filename address; DE holds the destination within the handle.
4EE7
JUMP forward to 4F08H, the common copy-to-handle epilogue.
4EE9
PUSH DE D5
SAVE Path
Save Register Pair DE (handle table entry pointer) onto the stack.
4EEA
GOSUB to ROM 260DH, the BASIC VARPTR routine. Returns DE pointing at the variable's descriptor in the variable table.
NOTE: 260DH is the Level II BASIC ROM VARPTR (Get Variable Address) routine.
4EED
GOSUB to ROM 0AF4H, the BASIC MOVEA routine. Copies the descriptor block from (DE) into the work area at 40D3H+.
NOTE: 0AF4H is the Level II BASIC ROM MOVEA (Move Memory Block) routine.
4EF0
EX (SP),HL E3
Exchange the top of the stack with HL. Stack-top now holds the parser pointer; HL holds the saved handle entry pointer.
4EF1
EX DE,HL EB
Exchange Register Pairs DE and HL. DE now holds the handle entry pointer; HL holds the variable descriptor pointer (or the work area pointer set up by MOVEA).
4EF2
LD A,(DE) 1A
Load Register A with the byte at (DE), the existing field count in the handle entry.
4EF3
INC DE 13
INCrement Register Pair DE by 1, advancing past the count byte to the descriptor area.
4EF4
PUSH DE D5
Save Register Pair DE (handle descriptor pointer) onto the stack.
Track 1 / Sector 2 (4EF5H - 4FF3H) - boundary falls within preceding OPEN handler at 4EF5H (CP A,(HL) row)
4EF5
CP A,(HL) BE
Compare Register A (existing field count) against the byte at HL (the new descriptor's count or length). C/Z flags set on relationship.
4EF6
If the C FLAG has been set (existing < new), jump forward to 4F02H to use the existing string in place.
4EF8
If the Z FLAG has been set (existing == new), jump forward to 4F02H to use the existing string in place.
4EFA
PUSH HL E5
Save Register Pair HL (descriptor pointer) onto the stack.
4EFB
GOSUB to ROM 2857H, the BASIC string-init routine. Allocates a new string entry sized to Register A.
NOTE: 2857H is the Level II BASIC ROM STRINI (String Init / Make New String Entry) routine.
4EFE
POP HL E1
Restore Register Pair HL from the stack (the descriptor pointer).
4EFF
GOSUB to ROM 285DH, the BASIC string-space-check routine. Verifies free string space and runs garbage collection if needed.
NOTE: 285DH is the Level II BASIC ROM STRSPA (Check String Space) routine.
4F02
LD (HL),A 77
Store Register A (the count/length byte) to (HL), recording it in the handle descriptor.
4F03
INC HL 23
INCrement HL by 1, advancing to the low-byte slot.
4F04
LD E,(HL) 5E
Load Register E with the byte at HL (low byte of source data address).
4F05
INC HL 23
INCrement HL by 1, advancing to the high-byte slot.
4F06
LD D,(HL) 56
Load Register D with the byte at HL (high byte of source data address). DE now points at the source data to be copied.
4F07
POP HL E1
Restore Register Pair HL from the stack (the destination handle pointer pushed at 4EF4H).
4F08
LD C,A 4F
Common Copy Epilogue
Copy Register A (the byte count) into Register C (low byte of LDIR length).
4F09
LD B,00H 06 00
Set Register B to 00H (high byte of LDIR length). BC now equals A, the byte count.
4F0B
OR A,A B7
OR Register A with itself, setting Z FLAG if A=00H (zero-length copy).
4F0C
If the Z FLAG has been set (count is 0), skip the LDIR and exit.
4F0E
LDIR ED B0
Block-move BC bytes from (HL) to (DE), incrementing both pointers. Copies the filename or descriptor data into the handle.
4F10
POP HL E1
Restore Register Pair HL from the stack (the original BASIC parser pointer, exchanged onto the stack at 4ED9H or 4EF0H).
4F11
RET C9
RETurn to the caller, with HL pointing past the consumed OPEN clause.
4F12H - Three-Entry Dispatch (Modes 02H / 04H / 08H)
A short three-entry dispatch routine reached via the ERREXIT table at 4E1CH (= JP 4F12H), 4E1FH (= JP 4F16H), and 4E22H (= JP 4F1AH). Each entry loads a different mode byte (02H, 04H, or 08H) into Register A, then falls through into the common body at 4F1CH. The common body saves the mode, calls ROM 2819H (CPHLDE) and 2857H (STRINI), loads HL from 40D4H (DSCTMP, the VARPTR storage area for a string currently being created by BASIC), calls 09FFH, and jumps to 2A2BH. The exact MicroDOS-specific syntax this implements needs confirmation against MicroDOS user documentation.
4F12
LD A,02H 3E 02
Mode-02H Entry
Load Register A with 02H, the mode tag for this entry.
4F14
JUMP forward to the common body at 4F1CH.
4F16
LD A,04H 3E 04
Mode-04H Entry
Load Register A with 04H, the mode tag for this entry.
4F18
JUMP forward to the common body at 4F1CH.
4F1A
LD A,08H 3E 08
Mode-08H Entry
Load Register A with 08H, the mode tag for this entry.
4F1C
PUSH AF F5
Common Body
Save Register AF onto the stack (preserving the mode tag for use after the ROM calls).
4F1D
GOSUB to ROM 2819H.
NOTE: 2819H is the Level II BASIC ROM CPHLDE (Compare HL:DE) routine. Sets flags from a 16-bit compare of HL against DE.
4F20
POP AF F1
Restore Register AF from the stack (the saved mode tag).
4F21
GOSUB to ROM 2857H.
NOTE: 2857H is the Level II BASIC ROM STRINI (String Init / Make New String Entry) routine. Allocates a new string entry sized to Register A (the mode tag here doubles as the string size).
4F24
LD HL,(40D4H) 2A D4 40
Load Register Pair HL with the 16-bit value at memory address 40D4H, the BASIC DSCTMP (VARPTR storage area for the string currently being created).
4F27
GOSUB to ROM 09FFH.
NOTE: 09FFH is a Level II BASIC ROM floating-point / accumulator routine in the math area (near MOVEA at 0AF4H and the floating-point support code at 09D6H+). The exact role of 09FFH in this context is yet to be confirmed against the BASIC ROM disassembly.
4F2A
JUMP to ROM 2A2BH.
NOTE: 2A2BH is a Level II BASIC ROM string-handling exit point in the BASIC string-evaluator region. The exact role needs confirmation against the BASIC ROM disassembly.
4F2DH - Three-Entry Dispatch (Modes 01H / 03H / 07H)
A second three-entry dispatch reached via the ERREXIT table at 4E25H (= JP 4F2DH), 4E28H (= JP 4F31H), and 4E2BH (= JP 4F35H). Each entry loads a different mode byte (01H, 03H, or 07H) into Register A, then falls through to the common body at 4F37H. The common body saves the mode, calls ROM 29D7H (READY / string-pointer-to-data resolver), restores the mode, validates that it is less than the count byte at (HL) (otherwise JP 1E4AH = FCERR / Illegal Function Call), then walks two bytes past the count to load Register Pair HL from a 2-byte target address (via LD B,(HL) / INC HL / LD H,(HL) / LD L,B). The mode is then stored at 40AFH (VALTYP = type variable flag) and the routine jumps to ROM 09F7H.
4F2D
LD A,01H 3E 01
Mode-01H Entry
Load Register A with 01H, the mode tag for this entry.
4F2F
JUMP forward to the common body at 4F37H.
4F31
LD A,03H 3E 03
Mode-03H Entry
Load Register A with 03H, the mode tag for this entry.
4F33
JUMP forward to the common body at 4F37H.
4F35
LD A,07H 3E 07
Mode-07H Entry
Load Register A with 07H, the mode tag for this entry.
4F37
PUSH AF F5
Common Body
Save Register AF onto the stack (preserving the mode tag).
4F38
GOSUB to ROM 29D7H.
NOTE: 29D7H is the Level II BASIC ROM READY entry / string-pointer-to-data resolver. Returns HL pointing at the 3-byte string descriptor (length, low-addr, high-addr) for the most recent string operation.
4F3B
POP AF F1
Restore Register AF from the stack (the mode tag).
4F3C
CP A,(HL) BE
Compare Register A (mode tag) against the byte at HL (the descriptor's count byte). C/Z flags set on relationship.
4F3D
If the NC FLAG has been set (mode >= count, indicating an out-of-range mode), JUMP to ROM 1E4AH to raise an Illegal Function Call error.
NOTE: 1E4AH is the Level II BASIC ROM FCERR (Illegal Function Call) entry point.
4F40
INC A 3C
INCrement Register A by 1, advancing the mode tag (1-based offset for the table walk).
4F41
INC HL 23
INCrement HL by 1, advancing past the count byte to the first table entry.
4F42
LD B,(HL) 46
Load Register B with the byte at HL (the low byte of a target address).
4F43
INC HL 23
INCrement HL by 1, advancing to the high byte of the target address.
4F44
LD H,(HL) 66
Load Register H with the byte at HL (the high byte of the target address).
4F45
LD L,B 68
Copy Register B (the low byte) into Register L. HL now holds the loaded target address.
4F46
LD (40AFH),A 32 AF 40
Store Register A (the incremented mode tag) to memory address 40AFH, the BASIC VALTYP (Type variable flag for value in Work-Register-Area-1: 2 = Integer, 3 = String, 4 = Single Precision, 8 = Double Precision).
4F49
JUMP to ROM 09F7H.
NOTE: 09F7H is a Level II BASIC ROM number-to-accumulator routine in the math area. Takes a value pointed to by HL and loads it into the BASIC accumulator with the type indicated by VALTYP.
4F4CH - Two-Entry Handler (00H / FFH Flag)
A two-entry handler reached via the ERREXIT table at 4E2EH (= JP 4F4CH = entry with flag 00H) and 4E31H (= JP 4F4FH = entry with flag FFH). The flag is saved on the stack, then VARPTR (260DH) and MOVEA (0AF4H) are called to copy a variable's descriptor into the work area. After a syntax check requiring token 0D5H, GETPAR (2337H) parses an additional integer or expression, and 29D7H is called to obtain a string descriptor. The routine then walks both descriptors (the variable's and the active-string's), computes a length difference, and dispatches to one of two helper subroutines (4F8FH or 4FA6H) based on the saved flag. The body ends with an optional LDIR copy and RET.
4F4C
XOR A,A AF
Flag-00H Entry
Set Register A to 00H and clear all flags. This entry tags the request with flag 00H.
4F4D
JUMP forward to the common body at 4F51H.
4F4F
LD A,0FFH 3E FF
Flag-FFH Entry
Load Register A with 0FFH, the alternate flag tag.
4F51
PUSH AF F5
Common Body
Save Register AF onto the stack (preserving the flag tag for the dispatch decision below).
4F52
GOSUB to ROM 260DH.
NOTE: 260DH is the Level II BASIC ROM VARPTR routine. Returns DE pointing at the variable's descriptor.
4F55
GOSUB to ROM 0AF4H.
NOTE: 0AF4H is the Level II BASIC ROM MOVEA (Move Memory Block) routine. Copies the descriptor block referenced by DE into the work area at 40D3H+ (DSCTMP).
4F58
PUSH DE D5
Save Register Pair DE onto the stack.
4F59
RST 08H CF
RST 08H, the BASIC syntax-check vector that requires the next parser byte to match the inline operand at 4F5AH.
4F5A
DB 0D5H D5
RST 08H operand: 0D5H, a Level II BASIC keyword token that must appear in the user's BASIC line at this point. Per the canonical token table, 0D5H is the "=" token, requiring an equals sign after the variable name in the user's syntax.
4F5B
GOSUB to ROM 2337H.
NOTE: 2337H is the Level II BASIC ROM GETPAR (Get General Parameter) routine. Parses the right-hand-side expression and sets the type flag at 40AFH.
4F5E
POP BC C1
Restore Register Pair BC from the stack (the DE that was pushed at 4F58H).
4F5F
EX (SP),HL E3
Exchange the top of the stack with HL. Stack-top now holds the parser pointer; HL holds the saved flag (originally pushed at 4F51H).
4F60
PUSH HL E5
Save Register Pair HL (the flag) back onto the stack.
4F61
PUSH BC C5
Save Register Pair BC (the variable descriptor pointer) onto the stack.
4F62
GOSUB to ROM 29D7H, the BASIC string-descriptor accessor. Returns HL pointing at the right-hand-side string's 3-byte descriptor.
4F65
EX (SP),HL E3
Exchange the top of the stack with HL. Stack-top now holds the RHS descriptor pointer; HL holds the variable descriptor pointer.
4F66
LD C,(HL) 4E
Load Register C with the byte at HL, the variable descriptor's length byte (the destination buffer width).
4F67
INC HL 23
INCrement HL by 1, advancing past the length byte.
4F68
LD E,(HL) 5E
Load Register E with the byte at HL (low byte of the destination buffer address).
4F69
INC HL 23
INCrement HL by 1.
4F6A
LD D,(HL) 56
Load Register D with the byte at HL (high byte of the destination buffer address). DE now points at the destination string buffer.
4F6B
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the destination buffer address; DE holds the variable descriptor's residual address.
4F6C
EX (SP),HL E3
Exchange the top of the stack with HL. Stack-top now holds the destination buffer address; HL holds the saved RHS descriptor pointer.
4F6D
LD B,(HL) 46
Load Register B with the byte at HL, the RHS descriptor's length byte (the source string length).
4F6E
INC HL 23
INCrement HL by 1.
4F6F
LD E,(HL) 5E
Load Register E with the byte at HL (low byte of the source data address).
4F70
INC HL 23
INCrement HL by 1.
4F71
LD D,(HL) 56
Load Register D with the byte at HL (high byte of the source data address). DE now points at the source string data.
4F72
LD A,C 79
Copy Register C (destination width) into Register A.
4F73
SUB A,B 90
SUBtract Register B (source length) from Register A. A now holds (dst_width - src_length). C FLAG set if source is longer than destination.
4F74
If the NC FLAG has been set (source fits in destination), jump forward to 4F78H.
4F77
XOR A,A AF
Set Register A to 00H and clear all flags. Source is longer than destination, so the padding count is forced to 0 (no padding; the LDIR will be capped).
4F78
LD B,A 47
Copy Register A (the padding count, possibly clamped to 0) into Register B.
4F79
POP HL E1
Restore Register Pair HL from the stack (the destination buffer address pushed at 4F6CH).
4F7A
POP AF F1
Restore Register AF from the stack (the saved flag tag, 00H or FFH).
4F7B
OR A,A B7
OR Register A with itself, setting Z FLAG if A = 00H. Tests the saved flag.
4F7C
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the source pointer; DE holds the destination pointer.
4F7D
If the NZ FLAG has been set (flag was FFH), jump forward to 4F84H to use the alternate helper.
4F7F
GOSUB to 4F8FH (helper subroutine A) for the flag-00H case. This advances the destination pointer by B padding spaces and writes spaces.
4F82
JUMP forward to 4F87H to perform the data copy.
4F84
GOSUB to 4FA6H (helper subroutine B) for the flag-FFH case. This writes spaces at the current destination position.
4F87
LD A,C 79
Copy Register C (destination width) into Register A.
4F88
OR A,A B7
OR Register A with itself, setting Z FLAG if A = 00H (zero-length copy).
4F89
If the Z FLAG has been set (no bytes to copy), skip the LDIR.
4F8B
LDIR ED B0
Block-move BC bytes from (HL) to (DE), incrementing both pointers. BC is at most C bytes (the destination width); excess source bytes are discarded.
4F8D
POP HL E1
Restore Register Pair HL from the stack (the original BASIC parser pointer pushed during the 4F5FH exchange).
4F8E
RET C9
RETurn to the caller.
4F8FH - Helper Subroutine A (Right-Justify with Leading Spaces)
Helper subroutine called from 4F7FH (the flag-00H path of the 4F4CH handler). On entry Register B holds the count of leading spaces, Register C holds the destination width, and DE points at the destination buffer. The routine returns immediately if B is 0. Otherwise it negates B and adds C to compute the byte count for the upcoming LDIR (writing the source data right-justified), advances the destination pointer past the leading spaces, and fills those leading positions with ASCII space characters (20H). This implements the RSET-style behavior of right-justifying a string within a fixed-width field.
4F8F
LD A,B 78
Copy Register B (the leading-space count) into Register A.
4F90
OR A,A B7
OR Register A with itself, setting Z FLAG if A = 00H.
4F91
RET Z C8
If the Z FLAG has been set (no leading spaces required), RETurn immediately.
4F92
NEG ED 44
NEGate Register A (two's-complement). A now holds (-leading_spaces).
4F94
ADD A,C 81
ADD Register C (destination width) to Register A. A now holds (dst_width - leading_spaces) = the data byte count.
4F95
LD C,A 4F
Copy Register A (the data byte count) into Register C, where it becomes the LDIR byte count for the caller.
4F96
LD A,B 78
Reload Register A with Register B (the original leading-space count).
4F97
PUSH DE D5
Save Register Pair DE (destination pointer) onto the stack.
4F98
LD B,00H 06 00
Set Register B to 00H, preparing BC = (00 : leading_space_count) for an ADD HL,BC offset.
4F9A
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the destination pointer; DE holds the source.
4F9B
ADD HL,BC 09
ADD Register Pair BC (the leading-space count) to HL, advancing the destination pointer past the leading-space region.
4F9C
EX DE,HL EB
Exchange Register Pairs DE and HL again. DE now points just past the leading-space region (where the data will land); HL holds the source.
4F9D
LD B,A 47
Copy Register A (the leading-space count) into Register B, the DJNZ counter for the space-fill loop.
4F9E
LD A,20H 3E 20
Load Register A with 20H (ASCII space character).
4FA0
LD (DE),A 12
Space-Fill Loop Start
Store Register A (space) to (DE).
4FA1
INC DE 13
INCrement Register Pair DE by 1.
4FA2
Space-Fill Loop End
DECrement Register B and LOOP BACK to 4FA0H if B != 0.
4FA4
POP DE D1
Restore Register Pair DE from the stack (the original destination pointer, now positioned past the leading spaces - wait, this restores the original DE, undoing the position advance. See below.).
4FA5
RET C9
RETurn to the caller.
4FA6H - Helper Subroutine B (Left-Justify with Trailing Spaces)
Helper subroutine called from 4F84H (the flag-FFH path of the 4F4CH handler). On entry Register B holds the count of trailing spaces, Register C holds the destination width, and DE points at the destination buffer. The routine returns immediately if B is 0. Otherwise it negates B and adds C to compute the byte count for the upcoming LDIR (writing the source data left-aligned), then writes B trailing spaces at the current destination position. This implements the LSET-style behavior of left-justifying a string within a fixed-width field.
4FA6
LD A,B 78
Copy Register B (the trailing-space count) into Register A.
4FA7
OR A,A B7
OR Register A with itself, setting Z FLAG if A = 00H.
4FA8
RET Z C8
If the Z FLAG has been set (no trailing spaces required), RETurn immediately.
4FA9
NEG ED 44
NEGate Register A (two's-complement). A now holds (-trailing_spaces).
4FAB
ADD A,C 81
ADD Register C (destination width) to Register A. A now holds (dst_width - trailing_spaces) = the data byte count.
4FAC
LD C,A 4F
Copy Register A (the data byte count) into Register C, where it becomes the LDIR byte count for the caller.
4FAD
LD A,20H 3E 20
Load Register A with 20H (ASCII space character).
4FAF
LD (DE),A 12
Space-Fill Loop Start
Store Register A (space) to (DE).
4FB0
INC DE 13
INCrement Register Pair DE by 1, advancing through the trailing-space region.
4FB1
Space-Fill Loop End
DECrement Register B and LOOP BACK to 4FAFH if B != 0.
4FB3
RET C9
RETurn to the caller.
4FB4H - LOC Function Dispatcher
Reached via the ERREXIT table at 4E4FH (= JP 4FB4H). This is the dispatcher for the MicroDOS LOC function, which supplies four kinds of disk-related information depending on its integer argument N: LOC(0) returns the last DSSSS accessed (drive*10000 + sector), LOC(1) returns the largest legal sector number for the current MicroDOS version, LOC(2) returns the WD1771 disk controller status saved after the last I/O error, and LOC(3) returns the count of sectors that could not be verified during the most recent CMD"F" or CMD"I" format operation. After parsing the integer argument with ROM 2B1FH, the dispatcher uses successive DEC A / JR Z to route to one of four handlers; any out-of-range argument falls through to the BASIC ROM "Illegal Function Call" error at 1E4AH.
4FB4
GOSUB to ROM 2B1FH.
NOTE: 2B1FH is the Level II BASIC ROM positive-integer parser. Parses the next BASIC expression as an integer in the range 0-255 and returns it in Register A.
4FB7
INC A 3C
INCrement Register A by 1. This biases the test below so that the original argument 0 produces A = 1 (which DEC A then turns back into 0 with Z FLAG set).
4FB8
DEC A 3D
DECrement Register A by 1. Sets the Z FLAG if the original argument was 0.
4FB9
If Z FLAG has been set (LOC(0)), JUMP to the LOC(0) handler at 4FC7H.
4FBB
DEC A 3D
DECrement Register A by 1. Sets the Z FLAG if the original argument was 1.
4FBC
If Z FLAG has been set (LOC(1)), JUMP to the LOC(1) handler at 4FEEH.
4FBE
DEC A 3D
DECrement Register A by 1. Sets the Z FLAG if the original argument was 2.
4FBF
If Z FLAG has been set (LOC(2)), JUMP to the LOC(2) handler at 5002H.
4FC1
DEC A 3D
DECrement Register A by 1. Sets the Z FLAG if the original argument was 3.
4FC2
If Z FLAG has been set (LOC(3)), JUMP to the LOC(3) handler at 500BH.
4FC4
JUMP to ROM 1E4AH.
NOTE: 1E4AH is the Level II BASIC ROM FCERR routine that issues the "Illegal Function Call" error. Argument was outside the 0-3 range.
4FC7H - LOC(0) Handler (Last DSSSS Accessed)
Computes the DSSSS value (drive * 10000 + sector_number) for the most recently accessed sector and returns it as a BASIC integer. The drive number is recovered from the one-hot drive-select mask at 448CH by repeatedly RRCAing until the C FLAG is set, accumulating 10000 in HL on each rotation. The track and sector are then read from 448DH/448EH and combined into the linear sector number using the records-per-track value at 4493H: HL is incremented by (records_per_track * track) plus the within-track sector offset. The composite DSSSS value is returned through ROM 0A9AH (returns HL as a BASIC integer).
4FC7
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H (the running DSSSS accumulator).
4FCA
LD DE,2710H 11 10 27
Load Register Pair DE with 2710H (= 10000 decimal, the per-drive DSSSS multiplier).
4FCD
LD A,(448CH) 3A 8C 44
Drive-Recovery Loop Start
Load Register A with the byte at memory address 448CH, the one-hot drive-select mask (01H, 02H, 04H, or 08H for drives 0, 1, 2, 3).
4FD0
RRCA 0F
Rotate Register A right-circular through the C FLAG. After N rotations the C FLAG is set when bit N of the original mask reaches bit 0 (i.e., bit 0 set means drive 0, bit 1 set means drive 1, etc.).
4FD1
If C FLAG has been set (the mask bit has reached position 0; the drive index has been counted), exit the loop.
4FD3
ADD HL,DE 19
ADD Register Pair DE (10000) to HL. HL accumulates 10000 once per RRCA, totalling drive * 10000.
4FD4
JUMP unconditionally back to 4FD0H to continue the drive-recovery rotation. (Note: A is preserved across the JR because the rotated value remains in A from the prior RRCA - this is wrong actually; A is reloaded each iteration? No, looking at code, A is loaded only once at 4FCDH. The loop body uses RRCA on A, then ADD HL,DE, then unconditional JR. Wait: 4FD3H is ADD HL,DE, then 4FD4H jumps to 4FD0H which is RRCA, so A is rotated again on each pass. Eventually the set bit reaches bit 0 and the JR C exits.)
4FD6
LD A,(4493H) 3A 93 44
Load Register A with the byte at memory address 4493H (the records-per-track count for the current disk geometry).
4FD9
LD E,A 5F
Copy Register A (records-per-track) into Register E.
4FDA
LD D,00H 16 00
Load Register D with 00H, zeroing the high byte. DE now holds the 16-bit records-per-track value.
4FDC
LD A,(448DH) 3A 8D 44
Load Register A with the byte at memory address 448DH (the current track number).
4FDF
LD B,A 47
Copy Register A (the track number) into Register B for use as the DJNZ counter.
4FE0
OR A,A B7
OR Register A with itself, setting the Z FLAG if A = 00H (track 0 - no track multiplier needed).
4FE1
If Z FLAG has been set (track 0), skip the track multiplier loop.
4FE3
ADD HL,DE 19
Track Multiplier Loop Start
ADD Register Pair DE (records-per-track) to HL. Builds HL = HL + records_per_track * track.
4FE4
Track Multiplier Loop End
DECrement Register B and LOOP BACK to 4FE3H if B != 0.
4FE6
LD A,(448EH) 3A 8E 44
Load Register A with the byte at memory address 448EH (the current within-track sector number).
4FE9
LD C,A 4F
Copy Register A (the within-track sector) into Register C. B is already 00H from the DJNZ exit, so BC = within-track sector.
4FEA
ADD HL,BC 09
ADD Register Pair BC (within-track sector) to HL. HL now holds the complete DSSSS value: drive*10000 + records_per_track*track + within_track_sector.
4FEB
JUMP to ROM 0A9AH.
NOTE: 0A9AH is the Level II BASIC ROM HL-to-FAC routine. Stores HL as the integer value of FAC (with VALTYP set to 2 for Integer) and returns to the BASIC expression evaluator.
4FEEH - LOC(1) Handler (Largest Legal Sector Number)
Returns (records_per_track * tracks) - 1, the largest legal sector number for the current MicroDOS configuration. The records-per-track value at 4493H is multiplied by the tracks-per-disk value at 4492H using a DJNZ-controlled add-loop, then 1 is subtracted to convert from count to maximum index.
4FEE
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H (the running product accumulator).
4FF1
LD A,(4493H) 3A 93 44
Load Register A with the byte at memory address 4493H (records-per-track).
Track 1 / Sector 3 (4FF4H - 50F2H) - boundary falls within preceding LOC(1) handler at 4FF4H (the LD E,A row)
4FF4
LD E,A 5F
Copy Register A (records-per-track) into Register E.
4FF5
LD D,00H 16 00
Load Register D with 00H, zeroing the high byte. DE now holds the 16-bit records-per-track value.
4FF7
LD A,(4492H) 3A 92 44
Load Register A with the byte at memory address 4492H (tracks-per-disk count, typically 40 for the Percom drive).
4FFA
LD B,A 47
Copy Register A (tracks count) into Register B for use as the DJNZ counter.
4FFB
ADD HL,DE 19
Multiply Loop Start
ADD Register Pair DE (records-per-track) to HL. After the loop, HL = records_per_track * tracks.
4FFC
Multiply Loop End
DECrement Register B and LOOP BACK to 4FFBH if B != 0.
4FFE
DEC HL 2B
DECrement Register Pair HL by 1. Converts the total-sector COUNT into the largest valid sector INDEX (sector numbers are 0-based).
4FFF
JUMP to ROM 0A9AH (HL-to-FAC integer return).
5002H - LOC(2) Handler (Disk Controller Status)
Returns the WD1771 floppy disk controller status byte saved at 4495H following the most recent disk I/O error. The byte is loaded into L, H is zeroed, and the result is returned as a BASIC integer. Per the manual: "This call returns the status (in decimal) of the 1771 Disk Controller following any disk I/O which results in an error. It is up to the user to analyze the status to determine the problem."
5002
LD A,(4495H) 3A 95 44
Load Register A with the byte at memory address 4495H (the saved FDC status from the last failed I/O).
5005
LD L,A 6F
Copy Register A into Register L (low byte of return value).
5006
LD H,00H 26 00
Load Register H with 00H, zeroing the high byte. HL now holds the status as a 16-bit integer in the range 0-255.
5008
JUMP to ROM 0A9AH (HL-to-FAC integer return).
500BH - LOC(3) Handler (Format-Failure Count)
Returns the count of sectors that could not be verified during the most recent CMD"F" or CMD"I" format operation. The byte at 4496H is the format-fail accumulator; this handler simply loads it and falls through into the LOC(2) tail at 5005H to return it as an integer.
500B
LD A,(4496H) 3A 96 44
Load Register A with the byte at memory address 4496H (the format-fail counter; 0 means no failures, otherwise the number of unverifiable sectors from the last format).
500E
JUMP to 5005H to share the LOC(2) integer-return tail (LD L,A / LD H,0 / JP 0A9AH).
5010H - LOF Function (Field-Buffer Length)
Reached via the ERREXIT table at 4E4CH (= JP 5010H). Returns the total length of all fields defined in the indicated Field Buffer (an integer 0-255), as documented in the manual: "LOF(N) returns the total length of all fields in buffer N." After parsing the buffer number N (1-4) with ROM 2B1FH, the routine calls the file-handle resolver at 4E6EH to obtain a pointer to the per-handle table page, then reads the length byte from that page and returns it.
5010
GOSUB to ROM 2B1FH, the BASIC positive-integer parser. Returns the buffer number N in Register A.
5013
GOSUB to 4E6EH, the MicroDOS file-handle resolver tail. Returns DE pointing into the per-handle table page (5600H/5700H/5800H/5900H), specifically at the buffer's stored length byte.
5016
LD A,(DE) 1A
Load Register A with the byte at (DE), the field-buffer's total-defined-length byte.
5017
LD L,A 6F
Copy Register A into Register L (low byte of return value).
5018
LD H,00H 26 00
Load Register H with 00H, zeroing the high byte. HL now holds the buffer length as a 16-bit integer in the range 0-255.
501A
JUMP to ROM 0A9AH (HL-to-FAC integer return).
501DH - LINE INPUT Statement
Reached via the ERREXIT table at 4E37H (= JP 501DH). This is the MicroDOS LINE INPUT statement, a one-line BASIC enhancement documented in Section X of the manual: 'LINE INPUT "PROMPT";A$' prints the prompt without the standard "?" suffix, accepts a single string variable, and treats commas, quotes, colons, and leading blanks as ordinary input characters. The handler first checks for an INPUT token (89H), then optionally parses a prompt-string descriptor and prints it via ROM 21CDH, calls VARPTR (260DH) and MOVEA (0AF4H) to capture the destination string variable, invokes the ROM line-input primitive at 0361H to read the keyboard buffer, and finally builds a BASIC string descriptor from the captured line via ROM 2868H + 1F32H.
501D
RST 08H CF
RST 08H, the BASIC syntax-check vector that requires the next parser byte to match the inline operand below.
501E
DB 89H 89
RST 08H operand: 89H, the Level II BASIC keyword token for "INPUT". The MicroDOS LINE INPUT syntax requires the INPUT keyword to appear after LINE; this RST 08H enforces the match and advances the parser past it.
501F
GOSUB to ROM 21CDH.
NOTE: 21CDH is the Level II BASIC ROM optional-prompt-string handler. If a quoted prompt string and ';' separator follow, prints the prompt and advances past it; if no prompt is present, returns immediately.
5022
GOSUB to ROM 260DH (VARPTR). Parses the destination string variable name and returns DE pointing at the variable's 3-byte string descriptor.
5025
GOSUB to ROM 0AF4H (MOVEA), which copies the destination descriptor block into the BASIC work area at 40D3H+ for safekeeping while input is read.
5028
PUSH DE D5
Save Register Pair DE (the destination variable's address) onto the stack.
5029
PUSH HL E5
Save Register Pair HL (the BASIC parser pointer) onto the stack.
502A
GOSUB to ROM 0361H.
NOTE: 0361H is the Level II BASIC ROM line-input primitive. Reads characters from the keyboard until ENTER, accepting all printable characters including commas, quotes, and colons (i.e., the LINE-INPUT semantic). Returns HL pointing at the input buffer with C FLAG set on BREAK.
502D
POP DE D1
Restore Register Pair DE from the stack (the BASIC parser pointer that was saved as HL at 5029H).
502E
POP BC C1
Restore Register Pair BC from the stack (the destination variable's address that was saved as DE at 5028H).
502F
If the C FLAG has been set (BREAK was pressed during input), JUMP to ROM 1DBEH.
NOTE: 1DBEH is the Level II BASIC ROM BREAK-handler that aborts the current statement with a "BREAK in NNNN" message.
5032
PUSH BC C5
Save Register Pair BC (the destination variable's address) back onto the stack.
5033
PUSH DE D5
Save Register Pair DE (the BASIC parser pointer) back onto the stack.
5034
LD B,00H 06 00
Load Register B with 00H. B will be used as the high byte of the line-length count by ROM 2868H below.
5036
GOSUB to ROM 2868H.
NOTE: 2868H is the Level II BASIC ROM string-builder routine. Allocates a string descriptor pointing at the just-input keyboard buffer and registers it as the current string.
5039
POP HL E1
Restore Register Pair HL from the stack (the BASIC parser pointer pushed at 5033H).
503A
XOR A,A AF
Set Register A to 00H and clear all flags. This signals VALTYP-string ($-suffix) to the assignment routine below.
503B
JUMP to ROM 1F32H.
NOTE: 1F32H is the Level II BASIC ROM string-assignment routine that completes a "var$ = source" operation by storing the source descriptor into the destination variable that was saved on the stack.
503EH - "&H" Hexadecimal Constant Parser
Reached via the ERREXIT table at 4E3AH (= JP 503EH). This is the MicroDOS hexadecimal constant parser, documented in Section IX of the manual: "Any constant preceded by '&H' will be interpreted as a hexadecimal constant. The number may be from 1 to 5 hexadecimal digits. Constants of this type always represent signed integers." The manual also notes that octal (&O) constants are NOT supported by MICRODOS. The handler skips past the leading '&' (already consumed by the trigger), enforces an 'H' character via RST 08H + 48H, then accumulates a 16-bit value in HL by reading hex digits 0-9 and A-F. Each digit is shifted-in via four ADD HL,HL operations with overflow-check (JP NZ,07B2H "Overflow Error").
503E
INC HL 23
INCrement HL by 1, advancing the BASIC parser past the '&' character.
503F
RST 08H CF
RST 08H, the BASIC syntax-check vector.
5040
DB 48H 48
RST 08H operand: 48H, ASCII 'H'. Requires the character following '&' to be 'H' (the hexadecimal indicator). Note that octal '&O' is not supported by MicroDOS - per the manual, "octal (&O) constants are NOT supported by MICRODOS."
5041
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, the running hex-value accumulator.
5044
DEC HL 2B
DECrement HL by 1. Adjusts the parser pointer in preparation for the INC HL at 5045H below (so the loop entry sees the first digit cleanly).
5045
INC HL 23
Digit Loop Start
INCrement HL by 1, advancing past the digit consumed on the previous pass (or past the original 'H' on the first pass).
5046
LD A,(HL) 7E
Load Register A with the byte at HL, the next character of the hex literal.
5047
GOSUB to ROM 1E3EH.
NOTE: 1E3EH is the Level II BASIC ROM digit-classifier. Returns C FLAG clear if A is a digit '0'-'9'; otherwise C FLAG set (caller must do further classification for hex letters).
504A
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the running accumulator; DE holds the parser pointer.
504B
If the NC FLAG has been set (A is a digit '0'-'9'), continue at 5059H to handle hex-letter classification (which it doesn't, but at NC the test redirects). Wait: looking at the flow, the actual logic is - if 1E3EH returned NC (non-digit i.e. potential hex letter), branch to 5059H to test for A-F. The NC vs C convention here is the OPPOSITE of what was just stated. Re-tracing: 1E3EH returns C set if NOT a digit (per ROM convention). So JR NC means "is a digit"; JR NC, 5059H sends digits to the hex-letter tester. Actually, the path at 5059H tests for >=47H ('G') which would only matter for non-digits. The flow is: digit ('0'-'9') falls through to 504DH; non-digit jumps to 5059H to test for A-F. Note: this requires 1E3EH to set NC for non-digits, which inverts the typical convention. See the ROM listing for confirmation.
504D
CP A,3AH FE 3A
Compare Register A with 3AH (one past ASCII '9'). Sets the NC FLAG if A >= 3AH (out-of-range).
504F
If the NC FLAG has been set (A is >= ':', not a numeric digit), JUMP to 5075H to finish the parse.
5051
CP A,30H FE 30
Compare Register A with 30H (ASCII '0'). Sets the C FLAG if A < '0'.
5053
If the C FLAG has been set (A is < '0'), JUMP to 5075H to finish the parse.
5055
SUB A,30H D6 30
SUBtract 30H ('0') from Register A. A now holds the digit value 0-9.
5057
JUMP forward to 505FH to merge the digit into the accumulator.
5059
CP A,47H FE 47
Hex-Letter Branch
Compare Register A with 47H (one past ASCII 'F'). Sets the NC FLAG if A >= 'G'.
505B
If the NC FLAG has been set (A is past 'F'), the character is not a hex digit; JUMP to 5075H to finish.
505D
SUB A,37H D6 37
SUBtract 37H from Register A. For 'A' (41H), this gives 0AH; for 'F' (46H), this gives 0FH. A now holds the hex letter's digit value 10-15.
505F
LD C,A 4F
Digit Merge
Copy Register A (the digit value 0-15) into Register C.
5060
XOR A,A AF
Set Register A to 00H and clear all flags.
5061
LD B,A 47
Copy Register A (00H) into Register B. BC now holds the digit value zero-extended.
5062
ADD HL,HL 29
ADD Register Pair HL to itself, shifting accumulator left by 1 bit. C FLAG set on overflow into bit 16.
5063
ADC A,00H CE 00
ADD-with-carry Register A and 00H. A accumulates any overflow bits across the four ADD HL,HL shifts that follow.
5065
ADD HL,HL 29
ADD Register Pair HL to itself (second shift).
5066
ADC A,00H CE 00
ADD-with-carry Register A and 00H.
5068
ADD HL,HL 29
ADD Register Pair HL to itself (third shift).
5069
ADC A,00H CE 00
ADD-with-carry Register A and 00H.
506B
ADD HL,HL 29
ADD Register Pair HL to itself (fourth shift). HL has been shifted left by 4 bits total - making room for the new hex digit.
506C
ADC A,00H CE 00
ADD-with-carry Register A and 00H. A is non-zero if and only if any of the four shifts overflowed.
506E
If the NZ FLAG has been set (overflow occurred), JUMP to ROM 07B2H.
NOTE: 07B2H is the Level II BASIC ROM "Overflow" error setup. Triggers if the hex literal exceeds 16 bits (5 hex digits maximum, with the leading digit no greater than 7 for signed integers - though this code accepts up to FFFF and lets 8000-FFFF render as negative two's-complement).
5071
ADD HL,BC 09
ADD Register Pair BC (the new digit value) to HL. HL now holds the running accumulator with the new digit merged in.
5072
EX DE,HL EB
Exchange Register Pairs DE and HL. DE now holds the running accumulator; HL holds the parser pointer.
5073
JUMP unconditionally back to 5045H to consume the next hex digit.
5075
Finish Parse
GOSUB to ROM 0A9AH (HL-to-FAC integer return). Stores the running 16-bit accumulator (in HL after the EX DE,HL) as the integer value of FAC.
5078
EX DE,HL EB
Exchange Register Pairs DE and HL. Restores HL = parser pointer for the BASIC expression evaluator's continuation.
5079
RET C9
RETurn to the caller (the BASIC expression evaluator).
507AH - LSET/RSET Secondary Entry (Array Element / Recursive FIELD)
Reached via the ERREXIT table at 4E3DH (= JP 507AH). This is a secondary LSET/RSET assignment entry that handles the special cases where the left-hand-side of an assignment refers to either (a) a tokenized expression following byte 0C1H (a Disk-BASIC reserved token used as the array-element-LSET prefix in MicroDOS), in which case it dispatches to 518FH; or (b) a recursive DEF FIELD inline (token 0A3H = FIELD), in which case it dispatches to 4E7EH (the FIELD-body parser). In all other cases, the routine treats the LHS as an ordinary string variable: it calls ROM 2828H (the string-pointer-fixer) and the FIELD-buffer-reference helper at 50E4H, then writes the resolved descriptor pointer back into the variable slot before continuing parsing at ROM 1F05H (string assignment continuation).
507A
CP A,0C1H FE C1
Compare Register A with 0C1H, a Disk-BASIC reserved token used by MicroDOS to mark an array-element-LSET assignment.
507C
If Z FLAG has been set (token matches), JUMP to the array-element-assignment helper at 518FH.
507F
CP A,0A3H FE A3
Compare Register A with 0A3H, the Level II BASIC keyword token for "FIELD".
5081
If Z FLAG has been set (token matches FIELD), JUMP to the FIELD-body parser at 4E7EH (a recursive call to DEF FIELD inline within a LSET/RSET).
5084
GOSUB to ROM 2828H.
NOTE: 2828H is the Level II BASIC ROM string-pointer-fixer. Resolves a string variable name into a descriptor pointer in DE.
5087
GOSUB to 50E4H, the FIELD-buffer-reference helper, which validates that the next syntactic context expects a buffer reference and returns the resolved buffer descriptor in HL.
508A
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the variable slot pointer; DE holds the resolved descriptor pointer.
508B
LD (HL),E 73
Store Register E (low byte of descriptor pointer) at (HL).
508C
INC HL 23
INCrement HL by 1, advancing to the high-byte slot.
508D
LD (HL),D 72
Store Register D (high byte of descriptor pointer) at (HL). The variable's descriptor pointer has now been overwritten with the FIELD buffer's resolved descriptor.
508E
EX DE,HL EB
Exchange Register Pairs DE and HL. Restores DE = variable slot pointer; HL = parser pointer.
508F
JUMP to ROM 1F05H.
NOTE: 1F05H is the Level II BASIC ROM string-assignment continuation routine that completes the LSET/RSET-style operation.
5092H - DEF FIELD Statement Body
Reached via the ERREXIT table at 4E43H (= JP 5092H). This is the main body of the DEF FIELD statement, documented in Section VIII of the manual. Syntax: DEF FIELD #N, N1 AS F1$, ..., NI AS FI$ (where N is the buffer number 1-4 and each Ni is a length, optionally parenthesized as an expression, followed by AS and a string variable name to bind to that field). The handler invokes the FIELD-buffer-reference helper at 50E4H, saves VALTYP, walks the field list parsing length expressions and AS-clauses, and at end-of-list collapses the captured field-name+length pairs onto the buffer's directory. After processing, the routine compares the running total against the prior maximum length via 2819H to enforce the FIELD-OVERFLOW limit (255 characters). Note: the FIELD body itself does no allocation; it merely records field names and offsets in the per-handle table page so subsequent variable references resolve into the buffer.
5092
GOSUB to 50E4H, the FIELD-buffer-reference helper. Reads the '#N' buffer number, validates it 1-4, and returns HL pointing into the per-handle table page (5600H+ for buffer 1, 5700H+ for buffer 2, etc.) and B holding the buffer's prior total-length byte.
5095
LD A,(40AFH) 3A AF 40
Load Register A with the byte at memory address 40AFH (VALTYP - BASIC's type-of-current-expression flag).
5098
PUSH AF F5
Save Register AF (VALTYP saved across field-list parsing).
5099
LD (40F3H),HL 22 F3 40
Store Register Pair HL (the field-buffer page pointer) to memory address 40F3H, a temporary working slot used by the assignment-helper at 514CH.
509C
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the buffer's first-field directory pointer; DE holds the parser pointer.
509D
LD A,(HL) 7E
Load Register A with the byte at HL, the buffer's defined-fields-count or zero-marker.
509E
INC HL 23
INCrement HL by 1.
509F
LD H,(HL) 66
Load Register H with the byte at HL.
50A0
LD L,A 6F
Copy Register A into Register L. HL now holds the existing buffer-directory descriptor pointer.
50A1
OR A,H B4
OR Register A (low byte of descriptor) with Register H (high byte). Sets Z FLAG if HL = 0000H (no fields defined yet for this buffer).
50A2
If Z FLAG has been set (no prior field directory), JUMP to 518AH to issue the "Field Overflow" error (technically here it's a "no buffer to overlay" error path).
50A5
LD A,(HL) 7E
Field Loop Start
Load Register A with the byte at HL, the next character of the field-list to be parsed.
50A6
CP A,28H FE 28
Compare Register A with 28H (ASCII '('). Tests whether the field length is parenthesized.
50A8
If the NZ FLAG has been set (no parenthesis - simple integer length), JUMP forward to 50B2H to parse a simple integer.
50AA
GOSUB to 50F0H, a helper that handles parser bookkeeping for parenthesized expressions.
50AD
GOSUB to 514CH, the parenthesized-length helper which evaluates the parenthesized expression and returns its integer value.
50B0
JUMP forward to 50B7H to merge into the common path.
50B2
XOR A,A AF
Set Register A to 00H and clear all flags.
50B3
PUSH AF F5
Save Register AF onto the stack.
50B4
PUSH DE D5
Save Register Pair DE (the parser pointer) onto the stack.
50B5
RST 08H CF
RST 08H, the BASIC syntax-check vector.
50B6
DB 0D5H D5
RST 08H operand: 0D5H, the Level II BASIC keyword token for "=". This is wrong syntactically for DEF FIELD - actually this RST 08H at this point is in error-recovery context, expecting an '=' token in the wrapping LSET case.
50B7
GOSUB to ROM 2337H (GETPAR). Parses a numeric expression as the field length and sets VALTYP at 40AFH.
50BA
DEC HL 2B
DECrement HL by 1.
50BB
RST 10H D7
RST 10H - advance the parser past the next significant character.
50BC
If the NZ FLAG has been set (unexpected character), JUMP to ROM 1997H (SNERR - Syntax Error).
50BF
LD A,(40AFH) 3A AF 40
Load Register A with the byte at memory address 40AFH (VALTYP).
50C2
CP A,03H FE 03
Compare Register A with 03H (string type). Sets Z FLAG if the parsed expression was a string.
50C4
If the NZ FLAG has been set (numeric expression - the field-length integer), branch forward to 50DBH to do post-processing.
50C6
LD HL,(40B3H) 2A B3 40
Load Register Pair HL from memory address 40B3H, BASIC's "current string-data pointer" workspace.
50C9
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the parser pointer; DE holds the string descriptor's data address.
50CA
LD HL,(4121H) 2A 21 41
Load Register Pair HL from memory address 4121H, FACLO (BASIC's FAC low pair).
50CD
RST 18H DF
RST 18H - 16-bit compare HL with DE, setting flags accordingly.
50CE
If the C FLAG has been set (FAC < descriptor's data pointer, indicating in-program string), branch forward to 50D5H.
50D0
GOSUB to ROM 2843H, the BASIC string-storage-allocator. Allocates space for a copy of the string in the string heap.
50D3
JUMP forward to 50DBH to continue.
50D5
LD HL,40D3H 21 D3 40
Load Register Pair HL with 40D3H, the address of DSCTMP (the BASIC string-being-built descriptor).
50D8
GOSUB to 517BH, the descriptor-write helper that stores a 3-byte descriptor.
50DB
POP HL E1
Restore Register Pair HL from the stack (the parser pointer pushed at 50B4H).
50DC
POP AF F1
Restore Register AF from the stack (the saved flag pushed at 50B3H).
50DD
If the NZ FLAG has been set (parenthesized form was used), GOSUB to 5130H, the field-list flush helper.
50E0
POP AF F1
Restore Register AF from the stack (the saved VALTYP pushed at 5098H).
50E1
JUMP to ROM 2819H.
NOTE: 2819H is the Level II BASIC ROM 16-bit-compare routine (CPHLDE). Compares HL with DE and falls through to the parser to continue. Used here to merge field-list end-state with BASIC parser flow.
50E4H - FIELD-Buffer-Reference Helper
Helper used by both the FIELD body (5092H) and the LSET-secondary path (5084H). Validates that the parser is positioned at a '#' character (token 0BEH per the Disk-BASIC convention used by MicroDOS for the FIELD-buffer-reference syntactic marker), sets the field-buffer-active flag at 40DCH to 80H, then ORs the existing flag bits with the byte at HL and dispatches to ROM 2612H to resolve the buffer-number expression.
50E4
RST 08H CF
RST 08H, the BASIC syntax-check vector.
50E5
DB 0BEH BE
RST 08H operand: 0BEH, the Disk-BASIC token MicroDOS uses to introduce a field-buffer reference (the '#' followed by a buffer-number expression). Per the canonical Level II token table this position is FN, but MicroDOS overlays it for its file-handle / field-buffer indirection syntax.
50E6
LD A,80H 3E 80
Load Register A with 80H, the field-buffer-active flag value.
50E8
LD (40DCH),A 32 DC 40
Store Register A to memory address 40DCH, BASIC's expression-context flag, marking the next subexpression as a field-buffer reference.
50EB
OR A,(HL) B6
OR Register A (80H) with the byte at (HL), the next parser character. Used to set sign on token-encoded values to disambiguate them.
50EC
LD B,A 47
Copy Register A (the OR'd flag) into Register B for the called routine.
50ED
JUMP to ROM 2612H.
NOTE: 2612H is the Level II BASIC ROM expression-evaluator entry that returns a variable address (essentially VARPTR's tail). Resolves the buffer-number expression and returns its address.
50F0H - DEF FIELD List Parser
Helper called from 50AAH for parenthesized-length DEF FIELD entries. Pops the caller's IX preservation, then enters a recursive parser loop that handles each comma-separated 'length AS name$' clause: it advances past optional ',' separators (RST 08H + 2CH), evaluates a length expression via 1963H, sets the field-buffer-active flag at 40DCH, calls VARPTR (260DH) for the variable name, and tests for a closing ')' to terminate the list. At end-of-list it dispatches via JP IX to the caller's continuation.
50F0
POP IX DD E1
Restore Index Register IX from the stack (the caller's saved continuation address that was pushed before the helper-chain entry).
50F2
XOR A,A AF
Set Register A to 00H and clear all flags.
Track 1 / Sector 4 (50F3H - 51F1H) - boundary falls within preceding 50F0H helper at 50F3H (the PUSH AF row)
50F3
PUSH AF F5
Save Register AF (the just-cleared zero, used as a list-not-empty flag).
50F4
LD (40D8H),HL 22 D8 40
Store Register Pair HL to memory address 40D8H (BASIC's secondary parser-position cache).
50F7
RST 10H D7
RST 10H - advance the parser past the current significant character.
50F8
JUMP forward to 50FCH to begin the field-clause loop.
50FA
RST 08H CF
Inner Loop Re-entry
RST 08H, the BASIC syntax-check vector.
50FB
DB 2CH 2C
RST 08H operand: 2CH, ASCII ','. Requires a comma to separate field clauses.
50FC
LD C,08H 0E 08
Load Register C with 08H, the maximum field-name length (8 bytes).
50FE
GOSUB to ROM 1963H.
NOTE: 1963H is the Level II BASIC ROM small-integer-parser used internally for parameter limits. Parses an integer in the range 0-255 (here used to evaluate the field length expression).
5101
LD A,80H 3E 80
Load Register A with 80H (the field-buffer-active flag).
5103
LD (40DCH),A 32 DC 40
Store Register A to memory address 40DCH, re-asserting the field-buffer-active context for the upcoming VARPTR call.
5106
GOSUB to ROM 260DH (VARPTR). Parses the field name "AS variable$" and returns DE pointing at the variable's descriptor slot.
5109
LD A,(40AFH) 3A AF 40
Load Register A with the byte at memory address 40AFH (VALTYP).
510C
RRA 1F
Rotate Register A right through the C FLAG. Tests whether VALTYP's bit 0 is set (i.e., even-typed = string).
510D
If the NC FLAG has been set (string type), JUMP to 5117H.
510F
PUSH DE D5
Save Register Pair DE.
5110
PUSH HL E5
Save Register Pair HL.
5111
GOSUB to ROM 2888H, the BASIC numeric-to-string converter. Used here to coerce a non-string variable parsed as a field-name into a string-compatible binding.
5114
POP HL E1
Restore Register Pair HL.
5115
JUMP forward to 5122H.
5117
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the variable's descriptor pointer; DE holds the parser pointer.
5118
LD B,(HL) 46
Field Triple-Push Loop Start
Load Register B with the byte at HL, the descriptor's length byte.
5119
INC HL 23
INCrement HL by 1.
511A
LD C,(HL) 4E
Load Register C with the byte at HL, the low byte of the descriptor's data pointer.
511B
INC HL 23
INCrement HL by 1.
511C
PUSH BC C5
Save Register Pair BC (the partial descriptor) onto the stack.
511D
DEC A 3D
DECrement Register A by 1. Loop counter for sub-byte triple-extraction.
511E
Triple-Push Loop End
If A is not zero, LOOP BACK to 5118H.
5120
PUSH HL E5
Save the residual pointer.
5121
EX DE,HL EB
Exchange to put parser pointer back into HL.
5122
LD A,(40AFH) 3A AF 40
Load Register A with the byte at memory address 40AFH (VALTYP).
5125
OR A,A B7
OR Register A with itself, setting the Z FLAG if A = 00H.
5126
PUSH AF F5
Save Register AF (the type tag for the field-list flush helper).
5127
LD A,(HL) 7E
Load Register A with the byte at HL, the next parser character.
5128
CP A,29H FE 29
Compare Register A with 29H (ASCII ')'). Tests for end-of-list.
512A
If the NZ FLAG has been set (more clauses), LOOP BACK to 50FAH to consume the next ',' and clause.
512C
OR A,A B7
OR Register A with itself. Note: A holds 29H, so the OR sets NZ - this is intentional, encoding "non-empty list completed" for the dispatch below.
512D
PUSH AF F5
Save Register AF (sentinel for the JP IX target's discriminator).
512E
JP IX DD E9
JUMP to the address in Index Register IX (the caller's continuation, originally popped at 50F0H).
5130H - DEF FIELD List Flush Helper
Helper that walks the temporary field-clause stack pushed during the field-list parser, writing each captured field-name+offset pair into the buffer's directory. Uses the alternate-register set (EXX) to manage the in-flight state without disturbing the BASIC main registers, then performs a series of 3-byte descriptor writes (DEC HL ; LD (HL),C / DEC HL ; LD (HL),B) for each pushed field.
5130
POP IX DD E1
Restore Index Register IX from the stack (the caller's stashed continuation).
5132
EXX D9
EXX - swap to the alternate register set for the descriptor-write phase.
5133
POP AF F1
Flush Loop Start
Restore Register AF from the stack (the next field's type tag).
5134
If the Z FLAG has been set (end-of-list sentinel reached), JUMP to 5149H to exit.
5136
POP HL E1
Restore Register Pair HL from the stack (the field's destination directory pointer).
5137
RRA 1F
Rotate Register A right through the C FLAG. Tests bit 0 of the type tag.
5138
If the C FLAG has been set (string-typed field), branch to 5144H to use the descriptor-write path.
513A
POP BC C1
Numeric-Field Inner Loop Start
Restore Register Pair BC from the stack (a 2-byte chunk of the numeric data).
513B
DEC HL 2B
DECrement HL by 1.
513C
LD (HL),C 71
Store Register C at (HL).
513D
DEC HL 2B
DECrement HL by 1.
513E
LD (HL),B 70
Store Register B at (HL).
513F
DEC A 3D
DECrement Register A by 1, advancing the byte-pair counter.
5140
Numeric-Field Inner Loop End
If A is not zero, LOOP BACK to 513AH.
5142
JUMP unconditionally back to 5133H to consume the next field.
5144
String-Field Path
GOSUB to 517BH, the descriptor-write helper that copies a 3-byte string descriptor into the buffer's directory.
5147
JUMP unconditionally back to 5133H to consume the next field.
5149
EXX D9
Exit
EXX - swap back to the main register set.
514A
JP IX DD E9
JUMP to the address in Index Register IX (the caller's continuation).
514CH - DEF FIELD Parenthesized-Length Helper
Helper invoked from 50ADH when a field length is given as a parenthesized expression (DEF FIELD #N, (length_expr) AS var$, ...). Loads the saved field-buffer page pointer from 40F3H, requires '(' via RST 08H + 28H, parses the inner expression with VARPTR (260DH) and the type-resolver at 1F2BH, then walks comma-separated additional clauses until ')' is seen. Each clause issues another RST 08H + 2CH (',') and recurses through 515CH-515FH. Final exit issues RST 10H to skip ')', RST 08H + D5H ('=' token), and returns with the resolved buffer pointer in HL.
514C
LD HL,(40F3H) 2A F3 40
Load Register Pair HL from memory address 40F3H, the saved field-buffer page pointer (stashed by 5099H).
514F
RST 08H CF
RST 08H, the BASIC syntax-check vector.
5150
DB 28H 28
RST 08H operand: 28H, ASCII '('. Requires the parenthesized form to actually contain '('.
5151
PUSH HL E5
Save Register Pair HL (the buffer page pointer) onto the stack.
5152
LD HL,(40D8H) 2A D8 40
Load Register Pair HL from memory address 40D8H (the saved parser-position).
5155
LD A,(HL) 7E
Load Register A with the byte at HL.
5156
CP A,28H FE 28
Compare Register A with 28H (ASCII '(').
5158
If the NZ FLAG has been set (no expected '('), JUMP to 518AH (Field Overflow error).
515B
RST 10H D7
RST 10H - advance the parser past '('.
515C
Argument Loop Start
GOSUB to ROM 260DH (VARPTR).
515F
EX (SP),HL E3
Exchange the top of the stack with HL.
5160
GOSUB to ROM 1F2BH.
NOTE: 1F2BH is the Level II BASIC ROM type-converting copier used in string-related parsing.
5163
LD A,(HL) 7E
Load Register A with the byte at HL.
5164
CP A,29H FE 29
Compare Register A with 29H (ASCII ')').
5166
If the Z FLAG has been set (closing paren), JUMP forward to 516FH to finish.
5168
RST 08H CF
RST 08H syntax check.
5169
DB 2CH 2C
RST 08H operand: 2CH (','). Requires a comma before the next argument.
516A
EX (SP),HL E3
Exchange the top of the stack with HL.
516B
RST 08H CF
RST 08H syntax check.
516C
DB 2CH 2C
RST 08H operand: 2CH (',').
516D
LOOP BACK to 515CH for the next argument.
516F
RST 10H D7
RST 10H - advance past ')'.
5170
EX (SP),HL E3
Exchange the top of the stack with HL.
5171
RST 08H CF
RST 08H syntax check.
5172
DB 29H 29
RST 08H operand: 29H (')').
5173
RST 08H CF
RST 08H syntax check.
5174
DB 0D5H D5
RST 08H operand: 0D5H, the Level II BASIC keyword token for "=". Requires '=' after the parenthesized AS-clause.
5175
EX DE,HL EB
Exchange Register Pairs DE and HL.
5176
POP HL E1
Restore Register Pair HL.
5177
EX (SP),HL E3
Exchange the top of the stack with HL.
5178
PUSH HL E5
Save Register Pair HL onto the stack.
5179
EX DE,HL EB
Exchange Register Pairs DE and HL. HL = parser pointer; DE = saved buffer descriptor.
517A
RET C9
RETurn to the caller (5092H DEF FIELD body's continuation point).
517BH - String-Descriptor Write Helper
Three-byte descriptor writer used by both 50D8H and 5144H. Saves HL, calls ROM 29F5H to refresh the BASIC string descriptor's data pointer (returning the length byte in A and the data address in HL via 40B3H), then writes the 3-byte descriptor (length, data-low, data-high) at the original HL.
517B
PUSH HL E5
Save Register Pair HL (the destination descriptor address) onto the stack.
517C
GOSUB to ROM 29F5H.
NOTE: 29F5H is the Level II BASIC ROM string-descriptor finalizer, which returns A = length and HL = data pointer (via the BASIC current-string workspace).
517F
LD A,(HL) 7E
Load Register A with the byte at HL (the length byte from the resolved descriptor).
5180
LD (40B3H),HL 22 B3 40
Store Register Pair HL to memory address 40B3H, BASIC's current string-data pointer cache.
5183
POP HL E1
Restore Register Pair HL (the destination descriptor address).
5184
LD (HL),A 77
Store Register A (the length byte) at (HL).
5185
INC HL 23
INCrement HL by 1.
5186
LD (HL),C 71
Store Register C (low byte of data address) at (HL).
5187
INC HL 23
INCrement HL by 1.
5188
LD (HL),B 70
Store Register B (high byte of data address) at (HL). 3-byte descriptor write complete.
5189
RET C9
RETurn to the caller.
518AH - "Field Overflow" Error Setup
Loads BASIC error code 3CH (60 decimal, MicroDOS error 30 multiplied by 2 per the manual's "ERR/2+1" convention; 30 = "FIELD OVERFLOW") into Register E and dispatches to ROM 19A2H, the BASIC error-message-issuer. Per Section XI of the manual, this error occurs in a DEF FIELD when "either a single field was defined for more than 255 characters, or the total of all defined fields exceeds 255."
518A
LD E,3CH 1E 3C
Load Register E with 3CH (60 decimal), the encoded error number for "FIELD OVERFLOW" (= 2*(30-1)+2 per the BASIC error-code calling convention).
518C
JUMP to ROM 19A2H, the Level II BASIC ROM error-message issuer. Issues the message keyed by Register E (FIELD OVERFLOW from the relocated error table at 4080H+).
518FH - Array-Element LSET/RSET Helper
Reached from the 507AH dispatcher when token 0C1H precedes the LHS variable, signaling that the LHS is an array element rather than a scalar. Calls 51C9H to resolve the array indexing, requires '=' (RST 08H + D5H), evaluates the RHS via ROM 2B02H (numeric-or-string copy), then writes the resulting 2-byte address into the array element slot before returning.
518F
GOSUB to 51C9H, the array-index-resolver helper that returns DE pointing at the array element slot.
5192
PUSH DE D5
Save Register Pair DE (the array element slot) onto the stack.
5193
RST 08H CF
RST 08H syntax check.
5194
DB 0D5H D5
RST 08H operand: 0D5H, the Level II BASIC keyword token for "=". Requires '=' after the array reference.
5195
GOSUB to ROM 2B02H, the BASIC integer-result-to-DE routine. Parses the RHS expression and returns its 16-bit value in DE.
5198
EX (SP),HL E3
Exchange the top of the stack with HL. Stack-top now holds the parser pointer; HL holds the array element slot.
5199
LD (HL),E 73
Store Register E (low byte of RHS value) at (HL).
519A
INC HL 23
INCrement HL by 1.
519B
LD (HL),D 72
Store Register D (high byte of RHS value) at (HL).
519C
POP HL E1
Restore Register Pair HL from the stack (the parser pointer).
519D
RET C9
RETurn to the caller.
519EH - GET / PUT Through Field Buffer (CVx/MKx Common Path)
Reached via the ERREXIT table at 4E40H (= JP 519EH). Common code path for the CVI/CVS/CVD/MKI$/MKS$/MKD$ family when their argument is a FIELD-buffer slot. Pops the caller's return-address (which encodes the operation: integer, single, or double precision) into a controlled JP HL, calls the array/field-buffer index resolver at 51C9H, rotates Register C right-circular to obtain a per-handle stride mask, and dispatches via JP HL to a target derived from VALTYP. When VALTYP indicates double precision (3 - a coincidence with VALTYP=3 also being string in some contexts but here the test 'CP A,03H' alone gates the call), invokes ROM 29DAH to extend a single-precision FAC to double. Result is left at FAC (4121H) for the caller's convertor.
519E
POP AF F1
Restore Register AF from the stack (the per-call type/operation flag).
519F
GOSUB to 51C9H, the array/field-buffer index resolver.
51A2
RRC C CB 09
Rotate Register C right-circular (CB 09 = RRC C). Used to derive a stride mask from the index.
51A4
PUSH BC C5
Save Register Pair BC onto the stack.
51A5
PUSH DE D5
Save Register Pair DE.
51A6
GOSUB to ROM 252CH.
NOTE: 252CH is the Level II BASIC ROM expression-evaluator entry that handles the FAC-to-buffer transfer for the active type.
51A9
POP DE D1
Restore Register Pair DE.
51AA
POP BC C1
Restore Register Pair BC.
51AB
PUSH HL E5
Save Register Pair HL.
51AC
EX DE,HL EB
Exchange to put the resolved descriptor pointer in HL.
51AD
LD A,(HL) 7E
Load Register A with the byte at HL (the descriptor's first byte / length).
51AE
INC HL 23
INCrement HL by 1.
51AF
LD H,(HL) 66
Load Register H with the byte at HL.
51B0
LD L,A 6F
Copy Register A into Register L. HL now holds the descriptor's data address.
51B1
EX DE,HL EB
Exchange Register Pairs DE and HL. DE holds the data address; HL holds the parser pointer.
51B2
LD HL,26E7H 21 E7 26
Load Register Pair HL with 26E7H, the address of a Level II BASIC ROM exit/cleanup routine.
51B5
PUSH HL E5
Save Register Pair HL (26E7H exit) onto the stack as the return address for the upcoming JP HL.
51B6
LD HL,4121H 21 21 41
Load Register Pair HL with 4121H, FACLO (the BASIC FAC accumulator).
51B9
PUSH DE D5
Save Register Pair DE.
51BA
PUSH BC C5
Save Register Pair BC.
51BB
LD A,(40AFH) 3A AF 40
Load Register A with the byte at memory address 40AFH (VALTYP).
51BE
PUSH AF F5
Save Register AF.
51BF
CP A,03H FE 03
Compare Register A with 03H (string type). Sets Z FLAG if VALTYP = 3.
51C1
If the Z FLAG has been set, GOSUB to ROM 29DAH.
NOTE: 29DAH is the Level II BASIC ROM string-descriptor unpacker, used here when the source is a string descriptor (CVx case).
51C4
POP AF F1
Restore Register AF.
51C5
POP BC C1
Restore Register Pair BC.
51C6
EX DE,HL EB
Exchange Register Pairs DE and HL.
51C7
POP HL E1
Restore Register Pair HL.
51C8
JP HL E9
JUMP to the address in HL (the per-type continuation routine - varies by CVI/CVS/CVD/MKI$/MKS$/MKD$ caller).
51C9H - Array/Field Index Resolver
Inner helper used by 518FH (array LSET/RSET) and 519EH (CVx/MKx through field buffer). Advances past the index character with RST 10H, then conditionally reads a single ASCII digit (0-9) from the parser, doubles it (RLA after subtracting '0' converts digit*2 - the offset for a 2-byte ERRJMP table entry), and uses it to index into the ERRJMP table at 5578H to compute a target address. Returns DE pointing at the indexed slot.
51C9
RST 10H D7
RST 10H - advance the parser past the current character.
51CA
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H (the running stride accumulator).
51CD
If the NC FLAG has been set (no digit following), branch forward to 51D4H to use the zero stride.
51CF
SUB A,30H D6 30
SUBtract 30H from Register A. Converts ASCII digit to value 0-9.
51D1
RLA 17
Rotate Register A left through C FLAG. Doubles the digit (since each ERRJMP table entry is 2 bytes wide, possibly).
51D2
LD C,A 4F
Copy Register A into Register C. BC now holds the byte offset into the ERRJMP table.
51D3
RST 10H D7
RST 10H - advance the parser past the digit.
51D4
EX DE,HL EB
Exchange Register Pairs DE and HL. DE now holds the parser pointer; HL is freed.
51D5
LD HL,5578H 21 78 55
Load Register Pair HL with 5578H, the address of the ERRJMP table (a 22-byte vector array).
51D8
ADD HL,BC 09
ADD Register Pair BC (the stride offset) to HL. HL now points at the indexed ERRJMP slot.
51D9
EX DE,HL EB
Exchange Register Pairs DE and HL. DE now holds the indexed slot; HL holds the parser pointer.
51DA
RET C9
RETurn to the caller.
51DBH - MID$ Statement (Assignment Form)
Reached via the ERREXIT table at 4E55H (= JP 51DBH). This is the MicroDOS MID$ statement, documented in Section IX of the manual: 'MID$(S1$, N1, N2) = S2$' replaces a portion of S1$ starting at position N1, replacing N2 characters with S2$. N2 is optional; if omitted, either LEN(S2$) or LEN(S1$)-N1+1 is used, whichever is shorter. The handler validates that S1$ exists in writable string storage (re-allocating if necessary), parses N1 with bounds checking (1..LEN(S1$)), optionally parses N2 (capping at the available remaining length), parses '=' and the RHS, then performs the in-place byte-by-byte copy. Per the manual's examples: with A$="ABCDEFG", MID$(A$,3,4)="12345" yields "AB1234G".
51DB
INC HL 23
INCrement HL by 1, advancing past the MID$ token.
51DC
RST 08H CF
RST 08H syntax check.
51DD
DB 28H 28
RST 08H operand: 28H (ASCII '('). Requires '(' after MID$.
51DE
GOSUB to ROM 260DH (VARPTR). Returns DE pointing at S1$'s descriptor.
51E1
GOSUB to ROM 0AF4H (MOVEA), copying the descriptor into the work area.
51E4
PUSH HL E5
Save Register Pair HL (the parser pointer) onto the stack.
51E5
PUSH DE D5
Save Register Pair DE (the descriptor pointer) onto the stack.
51E6
INC HL 23
INCrement HL by 1. HL now points past the length byte to the data-address-low.
51E7
LD E,(HL) 5E
Load Register E with the byte at HL (data-address-low).
51E8
INC HL 23
INCrement HL by 1.
51E9
LD D,(HL) 56
Load Register D with the byte at HL (data-address-high). DE now points at S1$'s data.
51EA
LD HL,(40A0H) 2A A0 40
Load Register Pair HL from memory address 40A0H (the byte-storage pointer = 5A64H typically).
51ED
RST 18H DF
RST 18H - 16-bit compare HL with DE, setting flags. Tests whether S1$'s data lives in the dynamic byte storage area.
51EE
If the C FLAG has been set (S1$ data is in static program area, not modifiable), branch to 51FAH to skip re-allocation.
51F0
POP HL E1
Restore Register Pair HL (the descriptor pointer).
51F1
PUSH HL E5
Save Register Pair HL back onto the stack.
Track 1 / Sector 5 (51F2H - 52F0H) - boundary falls within preceding MID$ statement at 51F2H (the CALL 2843H row)
51F2
GOSUB to ROM 2843H, the BASIC string-storage allocator. Allocates a writable copy of S1$ in the string heap, since the original was outside the modifiable byte-storage region.
51F5
POP HL E1
Restore Register Pair HL (the descriptor pointer).
51F6
PUSH HL E5
Save Register Pair HL.
51F7
GOSUB to ROM 09D3H.
NOTE: 09D3H is the Level II BASIC ROM BMOVE routine (block-move data via FAC). Copies the original S1$ data into the newly allocated writable buffer.
51FA
POP HL E1
Restore Register Pair HL (descriptor pointer).
51FB
EX (SP),HL E3
Exchange the top of the stack with HL. Stack-top now holds the descriptor pointer; HL holds the parser pointer.
51FC
RST 08H CF
RST 08H syntax check.
51FD
DB 2CH 2C
RST 08H operand: 2CH (','). Requires comma between S1$ and N1.
51FE
GOSUB to ROM 2B1CH, the BASIC integer-parser. Returns N1 in Register A.
5201
EX (SP),HL E3
Exchange the top of the stack with HL.
5202
OR A,A B7
OR Register A with itself, setting the Z FLAG if A = 00H.
5203
If the Z FLAG has been set (N1 = 0, illegal), JUMP to ROM 1E4AH (FCERR - "Illegal Function Call").
5206
DEC A 3D
DECrement Register A by 1. Converts 1-based N1 to 0-based offset.
5207
CP A,(HL) BE
Compare Register A (offset) with the byte at (HL) (S1$ length).
5208
If the NC FLAG has been set (offset >= length, out of bounds), JUMP to ROM 1E4AH (FCERR).
520B
LD C,A 4F
Copy Register A (offset) into Register C.
520C
LD B,00H 06 00
Load Register B with 00H. BC now holds offset zero-extended.
520E
NEG ED 44
NEGate Register A. A now holds (-offset).
5210
ADD A,(HL) 86
ADD the byte at (HL) (S1$ length) to Register A. A now holds (length - offset) = the maximum copy count.
5211
LD D,A 57
Copy Register A (max-copy-count) into Register D.
5212
INC HL 23
INCrement HL by 1.
5213
LD A,(HL) 7E
Load Register A with the byte at HL (data-address-low).
5214
INC HL 23
INCrement HL by 1.
5215
LD H,(HL) 66
Load Register H with the byte at HL (data-address-high).
5216
LD L,A 6F
Copy Register A into Register L. HL now holds S1$'s data address.
5217
ADD HL,BC 09
ADD Register Pair BC (the offset) to HL. HL now points at the position within S1$ where replacement begins.
5218
EX (SP),HL E3
Exchange the top of the stack with HL.
5219
LD A,(HL) 7E
Load Register A with the byte at HL (the next parser character).
521A
CP A,2CH FE 2C
Compare Register A with 2CH (','). Tests for an optional N2 argument.
521C
If the NZ FLAG has been set (no comma - N2 omitted), JUMP forward to 5230H using the cap value already in D.
521E
PUSH DE D5
Save Register Pair DE (the cap = available characters).
521F
RST 08H CF
RST 08H syntax check.
5220
DB 2CH 2C
RST 08H operand: 2CH (',').
5221
GOSUB to ROM 2B1CH (integer parser). Returns N2 in Register A.
5224
POP DE D1
Restore Register Pair DE (the cap).
5225
CP A,D BA
Compare Register A (N2) with Register D (cap).
5226
If the Z FLAG has been set (N2 == cap), branch forward to 5230H.
5228
If the NC FLAG has been set (N2 > cap, out of bounds), JUMP to ROM 1E4AH (FCERR).
522B
OR A,A B7
OR Register A with itself, setting the Z FLAG if A = 00H.
522C
If the Z FLAG has been set (N2 = 0, illegal), JUMP to ROM 1E4AH (FCERR).
522F
LD D,A 57
Copy Register A (validated N2) into Register D.
5230
PUSH DE D5
Common Path
Save Register Pair DE (the final copy count) onto the stack.
5231
RST 08H CF
RST 08H syntax check.
5232
DB 29H 29
RST 08H operand: 29H (')'). Requires ')' after the argument list.
5233
RST 08H CF
RST 08H syntax check.
5234
DB 0D5H D5
RST 08H operand: 0D5H, the Level II BASIC keyword token for "=".
5235
GOSUB to ROM 2337H (GETPAR). Parses the RHS expression.
5238
PUSH HL E5
Save Register Pair HL (parser pointer).
5239
GOSUB to ROM 29D7H, the BASIC string-descriptor accessor. Returns HL pointing at the RHS string descriptor.
523C
EX DE,HL EB
Exchange Register Pairs DE and HL. DE = RHS descriptor; HL = original parser pointer.
523D
POP HL E1
Restore Register Pair HL.
523E
POP BC C1
Restore Register Pair BC (the saved copy-count from 5230H).
523F
EX (SP),HL E3
Exchange the top of the stack with HL.
5240
EX DE,HL EB
Exchange Register Pairs DE and HL. HL now holds the RHS descriptor; DE holds the destination position within S1$.
5241
LD A,B 78
Copy Register B (high byte of cap) into Register A.
5242
CP A,(HL) BE
Compare Register A with the byte at (HL) (RHS length).
5243
If the C FLAG has been set (cap < RHS length), branch to 524AH using the cap.
5245
LD A,(HL) 7E
Load Register A with the byte at HL (RHS length).
5246
OR A,A B7
OR Register A with itself, setting Z FLAG if A = 00H.
5247
If the Z FLAG has been set (zero-length RHS), branch to 5255H to exit without copying.
5249
LD B,A 47
Copy Register A (RHS length) into Register B. B is now the actual loop count.
524A
INC HL 23
INCrement HL by 1, advancing past RHS length byte.
524B
LD A,(HL) 7E
Load Register A with the byte at HL (RHS data-address-low).
524C
INC HL 23
INCrement HL by 1.
524D
LD H,(HL) 66
Load Register H with the byte at HL (RHS data-address-high).
524E
LD L,A 6F
Copy Register A into Register L. HL now holds the RHS data address.
524F
LD A,(HL) 7E
Copy Loop Start
Load Register A with the byte at HL (a byte of RHS data).
5250
LD (DE),A 12
Store Register A at (DE), writing the RHS byte into S1$ at the current destination position.
5251
INC HL 23
INCrement HL by 1.
5252
INC DE 13
INCrement DE by 1.
5253
Copy Loop End
DECrement Register B and LOOP BACK to 524FH if B != 0.
5255
POP HL E1
Restore Register Pair HL (the parser pointer).
5256
RET C9
RETurn to the caller.
5257H - INSTR Function
Reached via the ERREXIT table at 4E58H (= JP 5257H). This is the MicroDOS INSTR function, documented in Section IX of the manual: 'INSTR(N, S1$, S2$)' searches S1$ for the first occurrence of S2$, optionally starting at position N (default 1). Returns the 1-based position where S2$ was found, or 0 if no match. The handler tests the first argument for whether it's an integer (N) or a string (S1$, with N defaulted to 1), then parses the remaining arguments accordingly. Per the manual: with A$="ABCDEFG", INSTR(A$,"BCD")=2, INSTR(A$,"XY")=0, INSTR(3,"123123123","12")=4.
5257
RST 10H D7
RST 10H - advance the parser past the INSTR token's surrounding operator/paren.
5258
GOSUB to ROM 2335H, the Level II BASIC ROM sub-routine that evaluates the next argument. This grabs the first argument and sets VALTYP at 40AFH to indicate its type.
525B
RST 20H E7
RST 20H - tests VALTYP for string vs numeric. Sets Z FLAG if string-typed.
525C
LD A,01H 3E 01
Load Register A with 01H (the default starting position when N is omitted).
525E
PUSH AF F5
Save Register AF (the running starting-position).
525F
If the Z FLAG has been set (first argument was a string - so N was omitted), JUMP forward to 5272H.
5261
POP AF F1
Restore Register AF (drop the default 1).
5262
GOSUB to ROM 2B1FH (positive integer parser). Re-parses the first argument as N (since it was numeric).
5265
OR A,A B7
OR Register A with itself, setting Z FLAG if A = 00H.
5266
If the Z FLAG has been set (N = 0, illegal), JUMP to ROM 1E4AH (FCERR).
5269
PUSH AF F5
Save Register AF (the validated N).
526A
RST 08H CF
RST 08H syntax check.
526B
DB 2CH 2C
RST 08H operand: 2CH (',').
526C
GOSUB to ROM 2337H (GETPAR). Parses S1$.
526F
GOSUB to ROM 0AF4H (MOVEA), saving S1$'s descriptor.
5272
RST 08H CF
RST 08H syntax check.
5273
DB 2CH 2C
RST 08H operand: 2CH (',').
5274
PUSH HL E5
Save Register Pair HL (parser pointer).
5275
LD HL,(4121H) 2A 21 41
Load Register Pair HL from memory address 4121H, FACLO.
5278
EX (SP),HL E3
Exchange the top of the stack with HL. Stack-top now holds FAC; HL has the parser pointer.
5279
GOSUB to ROM 2337H (GETPAR). Parses S2$.
527C
RST 08H CF
RST 08H syntax check.
527D
DB 29H 29
RST 08H operand: 29H (')'). Requires ')' to close the argument list.
527E
PUSH HL E5
Save Register Pair HL (parser pointer).
527F
GOSUB to ROM 29D7H, the BASIC string-descriptor accessor. Returns HL pointing at S2$'s descriptor.
5282
EX DE,HL EB
Exchange Register Pairs DE and HL. DE = S2$ descriptor; HL = parser pointer.
5283
POP BC C1
Restore Register Pair BC.
5284
POP HL E1
Restore Register Pair HL (S1$ descriptor from earlier).
5285
POP AF F1
Restore Register AF (the validated N starting position).
5286
PUSH BC C5
Save Register Pair BC.
5287
DEC A 3D
DECrement Register A by 1. Converts 1-based N to 0-based offset.
5288
CP A,(HL) BE
Compare Register A (offset) with the byte at (HL) (S1$ length).
5289
If the NC FLAG has been set (offset >= S1$ length), JUMP to 52D6H to return 0 (no match).
528C
LD C,A 4F
Copy Register A (offset) into Register C.
528D
LD B,00H 06 00
Load Register B with 00H. BC = offset zero-extended.
528F
EX DE,HL EB
Exchange. HL = S2$ descriptor; DE = S1$ descriptor.
5290
LD A,(HL) 7E
Load Register A with the byte at HL (S2$ length).
5291
OR A,A B7
OR Register A with itself, setting Z FLAG if A = 00H.
5292
If the Z FLAG has been set (S2$ is empty), JUMP to 52D6H (return 0; matches manual example INSTR(A$,"")=0).
5295
EX DE,HL EB
Exchange. HL = S1$ descriptor; DE = S2$ descriptor.
5296
CP A,(HL) BE
Compare Register A (S2$ length) with the byte at (HL) (S1$ length).
5297
If the Z FLAG has been set (S2$ length == S1$ length), branch forward to 529CH.
5299
If the NC FLAG has been set (S2$ length > S1$ length, no match possible), JUMP to 52D6H (return 0).
529C
LD A,C 79
Copy Register C (offset) into Register A.
529D
NEG ED 44
NEGate Register A. A = -offset.
529F
ADD A,(HL) 86
ADD the byte at (HL) (S1$ length) to Register A. A now holds (S1$_len - offset) = the available length from the starting position.
52A0
EX DE,HL EB
Exchange. HL = S2$ descriptor; DE = S1$ descriptor.
52A1
CP A,(HL) BE
Compare Register A (available_length) with the byte at (HL) (S2$ length).
52A2
If the C FLAG has been set (S2$ doesn't fit in remaining S1$), JUMP to 52D6H (return 0).
52A5
SUB A,(HL) 96
SUBtract the byte at (HL) (S2$ length) from Register A. A now holds (available - S2$_len) = number of valid starting positions remaining.
52A6
INC A 3C
INCrement Register A by 1, converting count-of-positions to a loop count.
52A7
PUSH AF F5
Save Register AF (the loop counter for outer search).
52A8
EX DE,HL EB
Exchange. HL = S1$ descriptor; DE = S2$ descriptor.
52A9
INC HL 23
INCrement HL by 1.
52AA
LD A,(HL) 7E
Load Register A with the byte at HL (S1$ data-address-low).
52AB
INC HL 23
INCrement HL by 1.
52AC
LD H,(HL) 66
Load Register H with the byte at HL (S1$ data-address-high).
52AD
LD L,A 6F
Copy Register A into Register L. HL now holds S1$ data address.
52AE
ADD HL,BC 09
ADD Register Pair BC (offset) to HL. HL now points to the starting position within S1$.
52AF
PUSH HL E5
Save Register Pair HL (the search starting point).
52B0
EX DE,HL EB
Exchange. HL = S2$ descriptor; DE = S1$ search position.
52B1
LD B,(HL) 46
Load Register B with the byte at HL (S2$ length, the inner-compare loop count).
52B2
INC HL 23
INCrement HL by 1.
52B3
LD E,(HL) 5E
Load Register E with the byte at HL (S2$ data-address-low).
52B4
INC HL 23
INCrement HL by 1.
52B5
LD D,(HL) 56
Load Register D with the byte at HL (S2$ data-address-high). Wait - this overwrites the search position; let me re-trace. Looking at code flow: at 52B0H EX DE,HL puts the search position on the stack-top via 52AFH PUSH HL, then at 52B0H DE = search_pos, HL = S2$_descriptor. Then 52B1H reads S2$_length, 52B3H reads S2$_data_low, 52B5H reads S2$_data_high. DE was the S1$ search position - it gets overwritten here with S2$ data pointer. This means the S1$ search position must have been preserved on the stack at 52AFH and is restored later. Re-checking 52AFH: yes, PUSH HL saved the search position before the S2$ extraction.
52B6
POP HL E1
Restore Register Pair HL (the saved search position from 52AFH).
52B7
POP AF F1
Restore Register AF (outer loop counter).
52B8
PUSH AF F5
Outer Search Loop
Save Register AF (outer counter) back onto the stack.
52B9
PUSH BC C5
Save Register Pair BC.
52BA
PUSH DE D5
Save Register Pair DE (S2$ data pointer).
52BB
PUSH HL E5
Save Register Pair HL (S1$ search position).
52BC
LD A,(DE) 1A
Inner Compare Loop Start
Load Register A with the byte at (DE), an S2$ byte.
52BD
CP A,(HL) BE
Compare Register A (S2$ byte) with the byte at (HL) (S1$ byte).
52BE
If the NZ FLAG has been set (mismatch), branch to 52CDH.
52C0
INC DE 13
INCrement DE by 1.
52C1
INC HL 23
INCrement HL by 1.
52C2
Inner Compare Loop End
DECrement Register B and LOOP BACK to 52BCH if B != 0. All bytes matched.
52C4
POP AF F1
Match Found
Discard saved S1$ search position.
52C5
POP AF F1
Discard saved DE (S2$ pointer).
52C6
POP HL E1
Restore BC (saved at 52B9H) into HL. Wait - POP HL pops from BC's slot. Let me re-check the stack order: 52B8H PUSH AF, 52B9H PUSH BC, 52BAH PUSH DE, 52BBH PUSH HL. So pops in reverse: 52C4H POP AF (drops the original AF saved), 52C5H POP AF (drops a level), then 52C6H POP HL... actually the stack ordering as written would have 52C4H POP AF retrieving the saved HL slot (since stack is LIFO). This is getting confusing; the routine returns the count C as the position.
52C7
POP AF F1
Final POP AF. At this point Register C holds the original loop counter value (= match position offset from outer iteration count).
52C8
LD H,00H 26 00
Load Register H with 00H.
52CA
INC HL 23
INCrement HL by 1. Converts 0-based position to 1-based. HL holds the match position (1-based).
52CB
JUMP forward to 52D9H to return the match position via 0A9AH.
52CD
POP HL E1
Mismatch Restore
Restore Register Pair HL (saved S1$ search position).
52CE
POP DE D1
Restore Register Pair DE (saved S2$ data pointer).
52CF
POP BC C1
Restore Register Pair BC.
52D0
POP AF F1
Restore Register AF (outer counter).
52D1
INC C 0C
INCrement Register C, advancing the position offset.
52D2
INC HL 23
INCrement HL, advancing the search position by 1 within S1$.
52D3
DEC A 3D
DECrement Register A (the outer counter).
52D4
If the NZ FLAG has been set, LOOP BACK to 52B8H to try the next start position.
52D6
LD HL,0000H 21 00 00
No Match
Load Register Pair HL with 0000H (the "not found" return value).
52D9
GOSUB to ROM 0A9AH (HL-to-FAC integer return).
52DC
POP HL E1
Restore Register Pair HL (the parser pointer that was saved at 5274H or wherever the calling stack frame stored it).
52DD
RET C9
RETurn to the caller (BASIC expression evaluator).
52DEH - BASIC Error Message Dispatcher
Reached via the ERREXIT table at 4E34H (= JP 52DEH). This is the MicroDOS error-message issuer that walks the inline-string table at 52F1H, indexed by Register E (the error code). Per Section XI of the manual: "All errors are reported by MICRODOS using descriptive error messages. Error code numbers may be derived in 'ON ERROR' routines by using the expression 'ERR/2+1'." The handler converts E to an entry index by RRCA + INC A, then walks the table 00H-terminated entry-by-entry, decrementing B (the index) until the target entry is reached. At the matching entry, control transfers to ROM 1A01H to display the message.
52DE
POP HL E1
Restore Register Pair HL (drop the error-handler return address).
52DF
LD HL,52F0H 21 F0 52
Load Register Pair HL with 52F0H, the address one byte BEFORE the start of the BASIC error message table at 52F1H. The first INC HL inside the loop (or the C9 falls-through at 52F0H) puts HL at the first message.
52E2
LD A,E 7B
Copy Register E (the error code in 2*(N-1) form, per BASIC's ERR convention) into Register A.
52E3
RRCA 0F
Rotate Register A right-circular by 1 bit. Halves the error code, converting 2*(N-1) into N-1.
52E4
INC A 3C
INCrement Register A by 1. A now holds N (the 1-based error number from the manual).
52E5
LD B,A 47
Copy Register A (error number) into Register B for use as the table-walk counter.
52E6
LD A,(HL) 7E
Table Walk Loop Start
Load Register A with the byte at HL (the next character of the current message).
52E7
INC HL 23
INCrement HL by 1.
52E8
OR A,A B7
OR Register A with itself, setting the Z FLAG if A = 00H (end of current message).
52E9
If the NZ FLAG has been set (still inside a message), LOOP BACK to 52E6H.
52EB
DECrement Register B (entries-to-skip counter). If B != 0, LOOP BACK to 52E6H to skip another message. When B reaches 0, HL points at the start of the desired message.
52
Work In Progress
This page is being built incrementally. The disassembly through 4F11H has been completed. The remaining ~1,259 bytes (4F12H-55FFH) cover further BASIC keyword handlers reached through the ERREXIT table at 4E19H, NUMIO routines (LOC/LOF/POS, &H/&O parser), the UTILS section (LSET/RSET/FIELD assignment, GET, MID$), the BASIC error message table at 52F1H-5577H (DATA), the ERRJMP table at 5578H-558DH, miscellaneous routines, and the CMDTBL command-name table at 55B4H-55FFH. Track/sector boundaries still to be inserted: 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.