TRS-80 DOS - NEWDOS/80 v2.0 for the Model III - BOOT/SYS Disassembled

Introduction/Summary:

Complete analysis summary of the NEWDOS/80 Model III bootstrap loader.

BOOT PROCESS OVERVIEW

  1. ROM Boot (Model III ROM at 34E0H):
    • ROM loads Track 0, Sector 1 to address 4300H
    • ROM jumps to 4300H to begin bootstrap execution
  2. Bootstrap Initialization (4300H-430BH):
    • Disables interrupts
    • Sets up stack at 41E0H
    • Initializes buffer pointer to 51FFH
    • Preserves state in shadow registers (EXX)
  3. Relocatable Block Loader (430EH-433EH):
    • Reads control byte from buffer
    • If control byte < 20H: Process load block
    • If control byte >= 20H: End of blocks, verify signature
    • For each block: reads count, destination address, and data
    • Copies data bytes to destination address
    • Repeats until end marker found
    • Verifies signature A5H
    • Jumps to DOS entry point
  4. Buffer Management (4345H-43A0H):
    • Reads bytes from buffer at 5200H-52FFH
    • When buffer exhausted, loads next sector
    • Uses shadow registers (EXX) to maintain disk position
    • Implements sector read with FDC commands:
    • • SEEK (1BH)
    • position head on track
    • • READ SECTOR (88H)
    • read 256 bytes
    • Transfers data using INI instruction
    • Installs NMI handler for interrupt-driven completion
  5. NMI Handler and Error Recovery (43A2H-43D5H):
    • Triggered when sector read completes
    • Checks FDC status for errors
    • Success: advances to next sector, handles track boundaries
    • Error: issues RESTORE (0BH) command, retries up to 10 times
    • Fatal error: displays error message and halts
  6. Support Routines:
    • 43D7H: Wait for FDC not busy
    • 43E2H: Short timing delay
    • 43E8H: Error messages ("ERROR", "NO SYS")
  7. Load Block Data (4407H-45FFH):
    • Relocatable block format with headers and data
    • Contains bootstrap code and DOS modules
    • Specifies destination addresses for each block
    • Simple copy operation, NO compression
  8. Sector Padding (4600H-47FFH):
    • Filled with 5BH bytes (LD E,E)
    • Ensures 512-byte sector size

KEY FEATURES

  • Relocatable block loader (NOT compression)
  • Self-modifying (overwrites bootstrap with loaded code)
  • Port-based FDC I/O (F0H-F4H)
  • WD1793 controller support
  • NMI interrupt handling
  • 10-retry error recovery
  • Track boundary management (18 sectors/track)

Variables:

AddressBytesPurpose
F0HPortWD1793 FDC Command/Status Register (I/O Port). Used for reading FDC status and sending commands like SEEK (1BH), READ SECTOR (88H), RESTORE (0BH), or FORCE INTERRUPT (D0H). The Model III uses port-based I/O instead of the Model I's memory-mapped controller.
F1HPortWD1793 FDC Track Register (I/O Port). Used to specify the target track number for FDC seek operations. Not directly used in this bootstrap code.
F2HPortWD1793 FDC Sector Register (I/O Port). Used to specify the target sector number (0-17) for read/write operations. Written at 4361H with the sector to read.
F3HPortWD1793 FDC Data Register (I/O Port). Used to read data bytes from the FDC during sector reads. The INI instruction at 4397H and 439CH reads from this port. Also used at 435EH to send track number for SEEK command.
F4HPortDrive Select Register (I/O Port). Controls which drive is active and sets density mode. Written with 81H (Drive 0, single density) or 91H (Drive 0, double density or alternate configuration). Bit 6 can be set (OR 40H at 436BH) to enable write precompensation.
E4HPortNMI Mask Register (I/O Port). Controls Non-Maskable Interrupt enable/disable. Written with C0H at 4383H to enable NMI during disk reads, and with 00H at 43A3H to disable NMI after read completion. Model III specific - Model I doesn't have this.
4049H3NMI Vector. Modified at runtime to create "JP 43A2H" instruction. Byte at 4049H is set to C3H (JP opcode) at 437EH, and address 43A2H is stored at 404AH-404BH at 4379H. This redirects NMI interrupts to the local handler.
41E0HN/AStack Pointer Location. Set at 4308H with "LD SP,41E0H". The stack grows downward from this address during bootstrap execution.
43A6H2Saved Stack Pointer. The SP is saved here at 4372H before the NMI handler is called, allowing recovery if an interrupt occurs during the disk read loop.
43E8H8"ERROR" Message Data. Contains: 1CH 1FH "ERROR" 03H. Referenced at 43C9H for fatal error display.
43F0H8"NO SYS" Message Data. Contains: 1CH 1FH "NO SYS" 03H. Referenced at 433FH when boot disk validation fails.
5100H-51FFH256FDC Sector Data Buffer. Temporary buffer where sector data is loaded from disk via INI instructions at 4397H and 439CH. Each disk sector (256 bytes) is read into this buffer before being processed by the relocatable block loader.
5200H-52FFH256Load Block Processing Buffer. The bootstrap code reads relocatable load blocks from this buffer. HL is initialized to 51FFH at 430BH, and the first INC L at 4345H makes it 5200H. The loader fetches control bytes, addresses, and data from this buffer to copy DOS modules to their final RAM locations.

Register Usage Notes:

  • Primary Register Set: Used for buffer management. HL points to the load block buffer (5200H-52FFH), DE points to destination addresses for loaded code, BC is used for I/O port addresses and byte counts.
  • Shadow Register Set (via EXX): Used for disk I/O management. DE' holds track/sector position, HL' points to FDC registers or disk state, BC' holds retry counters and other disk-related state.
  • EXX Instruction: Called at 4307H (initialization), 4348H (page boundary/disk load), 43BDH (return to buffer processing) to switch between the two register sets. This elegant design allows the same code to manage both buffer processing and disk I/O without excessive stack usage.

Key Differences from Model I:

  • Model III uses port-based I/O (F0H-F4H) instead of memory-mapped FDC access (37ECH-37EFH)
  • Model III has NMI interrupt support (E4H port) for disk operations, Model I does not
  • Model III uses WD1793 controller (double-density capable) instead of WD1771 (single-density only)
  • Model III bootstrap loads to 4300H instead of 4200H (Model I)
  • Model III has 30ms stepping rate (SEEK command 1BH bits 0-1 = 11) vs 15ms on Model I

Disasembly:

4300H - BOOT/SYS Entry Point and Decompression Loader

This is the initial entry point for the NEWDOS/80 Model III bootstrap loader. The ROM loads this code from Track 0, Sector 1 of the boot disk into memory at address 4300H and transfers control here.

4300
NOP 00
No operation. This serves as a safety byte and may also be used as a signature or padding at the start of the boot sector.
4301
CP 11H FE 11
Compare Register A against 11H. This is not executed during normal boot - it appears to be part of a version check or signature that would be performed by calling code. If Register A equals 11H, the Z FLAG is set; otherwise the NZ FLAG is set.
4303
DI F3
[BOOT ENTRY POINT] Disable interrupts. The bootstrap loader runs with interrupts disabled to prevent any ROM interrupt handlers from interfering with the boot process.
4304
LD DE,0005H 11 05 00
Load Register Pair DE with 0005H. This will be preserved in the shadow register set and may be used as the initial track/sector position or a state variable for the disk loading routine.
4307
EXX D9
Exchange Register Pairs (BC, DE, HL) with their shadow counterparts (BC', DE', HL'). This preserves the value 0005H in DE' while using the primary register set for the buffer processing operations. The shadow registers will manage disk I/O state.
4308
LD SP,41E0H 31 E0 41
Load Stack Pointer with 41E0H. This establishes a temporary stack in low RAM for the bootstrap loader to use during load block processing and disk I/O operations. The stack grows downward from 41E0H.
430B
LD HL,51FFH 21 FF 51
Point Register Pair HL to 51FFH. This initializes the buffer pointer. The fetch routine at 4345H will increment L first, making it 5200H, so the actual buffer used is 5200H-52FFH (256 bytes) where load block data is staged.

[LOAD BLOCK PROCESSING LOOP START] The following code processes relocatable load blocks from the buffer. Each block specifies where to copy data in memory. The format is: control byte, byte count, destination address (2 bytes), then data bytes.

430E
GOSUB to 4345H to fetch the next byte from the load block buffer. This routine reads a byte from (HL) and increments HL, advancing through the buffer sequentially.
4311
CP 20H FE 20
Compare Register A against 20H (32 decimal). This determines if we have a valid load block. If Register A < 20H, the CARRY FLAG is set indicating a valid block follows. If Register A >= 20H, the NO CARRY FLAG is set indicating end of load blocks.
4313
LD B,A 47
Load Register B with the control byte value from Register A. Register B will be used to control the block processing logic.
4314
If the NO CARRY FLAG has been set (meaning the control byte was >= 20H), JUMP to 433FH to handle the end of load blocks sequence. This indicates all blocks have been processed.

[LOAD BLOCK HEADER PROCESSING] When the control byte is less than 20H, it indicates a valid load block follows. The loader reads the block descriptor: byte count and destination address.

4316
LD D,A 57
Load Register D with the control byte value. This is saved for potential use in block processing logic.
4317
GOSUB to 4345H to fetch the next byte, which is the byte count - how many bytes to copy in this block.
431A
LD C,A 4F
Load Register C with the byte count. This tells us how many data bytes follow in this block.
431B
GOSUB to 4345H to fetch the low byte of the destination address.
431E
LD E,A 5F
Load Register E with the low byte of the destination address.
431F
DECrement Register B (the control byte) and JUMP to 4333H if not zero. This handles special block processing modes based on the control byte value. If B becomes zero, execution continues with the data copy operation below.

[DATA COPY OPERATION] This section copies data bytes from the buffer to the destination address specified in the block header. The number of bytes to copy is in Register C.

4321
GOSUB to 4345H to fetch a data byte from the buffer.
4324
LD D,A 57
Load Register D with the fetched byte. This forms the high byte of the destination address (DE now points to the destination).
4325
DEC C 0D
DECrement Register C by 1. This adjusts the byte count.
4326
DEC C 0D
DECrement Register C by 1 again. Register C now contains the number of data bytes to copy (original count minus 2 for the address bytes).
4327
INC L 2C
[COPY LOOP START] INCrement Register L by 1. This advances the buffer pointer to the next data byte.
4328
If the Z FLAG is set (meaning Register L rolled over from FFH to 00H), GOSUB to 4348H to handle the page boundary crossing by loading more data from disk into the buffer.
432B
LD A,(HL) 7E
Load Register A with the byte at address HL. This fetches one data byte from the buffer.
432C
LD (DE),A 12
Store the byte from Register A to the memory location pointed to by Register Pair DE. This writes one byte to the destination address.
432D
INC DE 13
INCrement Register Pair DE by 1. This advances the destination pointer to the next byte location.
432E
DEC C 0D
DECrement Register C by 1. This reduces the remaining byte count.
432F
If the NZ FLAG is set (meaning Register C has not reached zero), LOOP BACK to 4327H to continue copying bytes. [COPY LOOP END] When C reaches zero, all data bytes for this block have been copied.
4331
JUMP back to 430EH to fetch the next control byte and process the next load block.

[ALTERNATE BLOCK PROCESSING PATH] This section handles special block processing when the control byte indicates a different mode (when DJNZ at 431FH branches here).

4333
DECrement Register B and JUMP to 432EH if not zero. This handles iteration for blocks that require special processing.
4335
GOSUB to 4345H to fetch another byte from the buffer.
4338
LD D,A 57
Load Register D with the fetched byte, which forms the high byte of an address.
4339
LD A,(DE) 1A
Load Register A with the byte at the address pointed to by Register Pair DE. This fetches a verification byte to confirm successful loading.
433A
CP A5H FE A5
Compare Register A against A5H (the signature byte). This verifies that the DOS system was loaded successfully. If Register A equals A5H, the Z FLAG is set indicating success; otherwise the NZ FLAG is set indicating an error.
433C
INC DE 13
INCrement Register Pair DE by 1. This advances past the signature byte to point to the entry point address.
433D
PUSH DE D5
Save Register Pair DE onto the stack. This preserves the entry point address (the address immediately following the A5H signature byte) for the loaded DOS code.
433E
RET Z C8
If the Z FLAG is set (meaning the signature byte matched A5H), RETURN to the address now on top of the stack. This transfers control to the loaded NEWDOS/80 operating system code, completing the bootstrap process successfully.

[ERROR PATH - SIGNATURE VERIFICATION FAILED] If the signature verification fails, execution continues here to display an error message and halt the system.

433F
LD HL,43F0H 21 F0 43
Point Register Pair HL to 43F0H. This is the address of the "NO SYS" error message string located later in this boot sector code.
4342
JUMP to 43CCH, which is an error display routine that will show the error message pointed to by HL and halt the system.

4345H - Buffer Fetch Routine

This routine fetches a single byte from the load block buffer, automatically handling page boundaries and loading additional sectors from disk when the buffer is exhausted. It reads sequentially through memory starting at 5200H. When the buffer pointer wraps around (L goes from FFH to 00H), it switches to the shadow register set and loads more data from the next sector on disk.

4345
INC L 2C
INCrement Register L by 1. This advances the buffer pointer to the next byte. On the very first call (HL=51FFH), this makes L=00H, so HL becomes 5200H, pointing to the start of the buffer.
4346
LD A,(HL) 7E
Load Register A with the byte at address HL. This fetches the next byte from the load block buffer.
4347
RET NZ C0
If the NZ FLAG is set (meaning the previous INC L at 4345H did not result in zero), RETURN with the fetched byte in Register A. This is the normal fast-path exit when no page boundary was crossed.
4348
EXX D9
[PAGE BOUNDARY HANDLER / DISK LOAD TRIGGER] Exchange Register Pairs with shadow counterparts. This switches to the shadow register set which contains the disk track/sector position (DE') and other state needed to load the next sector of load block data.
4349
LD B,0AH 06 0A
Load Register B with 0AH (10 decimal). This is a retry counter for disk read operations. The bootstrap will attempt up to 10 times to read each sector before giving up.
434B
LD L,81H 2E 81
[RETRY ENTRY POINT] Load Register L with 81H. This value will be used for the drive select register, selecting Drive 0 with SIDE 0 set.
Port F4H Value: 81H (10000001)Drive Select/Control Register
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Function
10000001Drive 0 Selected
Drive 1: 0=Not Selected
Drive 2: 0=Not Selected
Drive 3: 0=Not Selected
Side Select: 0=Side 0 (or N/A on single-sided)
Write Precomp: 0=Disabled
Wait States: 0=Disabled
Density: 1=Double/MFM
434D
PUSH DE D5
Save Register Pair DE onto the stack. This preserves the current track and sector position during the disk read operation.
434E
PUSH BC C5
Save Register Pair BC onto the stack. This preserves the retry counter (Register B) and other state information.
434F
LD A,E 7B
Load Register A with the value from Register E (the current sector number being read from disk).
4350
SUB 12H D6 12
SUBtract 12H (18 decimal) from Register A. Model III disks have 18 sectors per track (sectors 0-17). This checks if we've reached or passed sector 18. If the result is negative (A < 12H), the CARRY FLAG is set.
4352
If the CARRY FLAG has been set (meaning the sector number is less than 18), JUMP to 4357H to continue with the current track and sector.
4354
LD E,A 5F
Load Register E with the adjusted sector number (original sector - 18). This wraps the sector number back to 0 for the next track.
4355
LD L,91H 2E 91
Load Register L with 91H. This changes the drive select byte to 91H. Comparing to 81H: bit 4 is now set (91H = 10010001 vs 81H = 10000001), which indicates SIDE 1.
Port F4H Value: 91H (10010001)Drive Select/Control Register
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Function
10010001Drive 0 Selected
Drive 1: 0=Not Selected
Drive 2: 0=Not Selected
Drive 3: 0=Not Selected
Side Select: 1=Side 1 (or unknown function on single-sided drives)
Write Precomp: 0=Disabled
Wait States: 0=Disabled
Density: 1=Double/MFM

[FDC PREPARATION AND SEEK OPERATION] The following code prepares the WD1793 Floppy Disk Controller for a sector read operation. The Model III uses port-based I/O to communicate with the FDC chip.

4357
GOSUB to 43D7H to perform timing delays required by the FDC before issuing commands. The WD1793 requires specific settle times between operations to ensure reliable disk access.
435A
LD A,L 7D
Load Register A with the value from Register L (either 81H or 91H, set at 434BH or 4355H). This contains the drive select bits and density control flags.
435B
OUT (F4H),A D3 F4
Send the byte in Register A to I/O port F4H (Drive Select Register). This selects Drive 0 and sets the density mode (single or double density) for the upcoming FDC operation.
435D
LD A,D 7A
Load Register A with the value from Register D (the track number to seek to).
435E
OUT (F3H),A D3 F3
Send the track number to I/O port F3H (FDC Data Register). This loads the desired track number into the FDC's internal Data Register as preparation for the SEEK command.
4360
LD A,E 7B
Load Register A with the value from Register E (the sector number to read from the track).
4361
OUT (F2H),A D3 F2
Send the sector number to I/O port F2H (FDC Sector Register). This tells the FDC which sector to read once the head reaches the correct track.
4363
LD A,1BH 3E 1B
Load Register A with 1BH. This is the WD1793 SEEK command with specific control flags.
1793 FDC Command: 1BH (00011011)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
00011011Command=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 11=30ms, 10=20ms, 01=12ms, 00=6ms
4365
OUT (F0H),A D3 F0
Send the SEEK command to I/O port F0H (FDC Command/Status Register). This initiates a seek operation to move the disk head to the track specified in the Data Register (port F3H), with head load enabled, track ID verification enabled, and 30ms stepping rate.
4367
GOSUB to 43D7H to wait for the seek operation to complete. This routine polls the FDC status register until the BUSY bit clears, indicating the head has reached the destination track.
436A
LD A,L 7D
Load Register A with the value from Register L (the drive select byte, either 81H or 91H).
436B
OR 40H F6 40
Perform bitwise OR of Register A with 40H. This sets bit 6, which enables write precompensation on the WD1793 FDC. Precompensation adjusts write timing for inner tracks to compensate for bit shift.
436D
LD E,A 5F
Load Register E with the modified drive select byte (now with precompensation bit set). This will be used in the read loop to maintain proper drive selection.
436E
LD D,02H 16 02
Load Register D with 02H. This is a bit mask for testing bit 1 (DRQ - Data Request) in the FDC status register during the sector read operation.
4370
IN A,(F0H) DB F0
Read the FDC Status Register from I/O port F0H into Register A. This retrieves the final status of the SEEK command to check for errors.

[NMI INTERRUPT HANDLER INSTALLATION] Before starting the critical sector read operation, the code installs a custom NMI (Non-Maskable Interrupt) handler. This ensures that if an interrupt occurs during the tight timing loop of the disk read, execution can be recovered properly.

4372
LD (43A6H),SP ED 73 A6 43
Store the current Stack Pointer value to memory location 43A6H. This saves the stack pointer so it can be restored if an NMI interrupt occurs during the critical disk read timing loop.
4376
LD HL,43A2H 21 A2 43
Point Register Pair HL to 43A2H. This is the address of the NMI interrupt handler routine within this boot sector code.
4379
LD (404AH),HL 22 4A 40
Store the interrupt handler address (43A2H) to memory location 404AH. Location 4049H will contain a JP instruction (C3H) and 404AH-404BH will contain this address, forming "JP 43A2H".
437C
LD A,C3H 3E C3
Load Register A with C3H, which is the Z80 machine code opcode for JP (Jump) instruction.
437E
LD (4049H),A 32 49 40
[SELF-MODIFYING CODE] Store the JP opcode (C3H) to memory location 4049H. Combined with the address at 404AH-404BH, this creates a complete JP 43A2H instruction at the NMI vector location (4049H), redirecting NMI interrupts to the local handler.
4381
LD A,C0H 3E C0
Load Register A with C0H (11000000 binary). This is the NMI mask control value for the Model III.
4383
OUT (E4H),A D3 E4
Send C0H to I/O port E4H (NMI Mask Register). This enables NMI interrupts during the disk read operation on the Model III, allowing the FDC to signal data ready conditions.

[SECTOR READ COMMAND AND DATA TRANSFER] The following code issues a READ SECTOR command to the FDC and transfers the 256-byte sector data into memory at 5100H using a tight timing loop. The DRQ (Data Request) bit must be serviced quickly to avoid Lost Data errors.

4385
LD A,88H 3E 88
Load Register A with 88H, which is the WD1793 READ SECTOR command with Multiple Records flag set.
1793 FDC Command: 88H (10001000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
10010000Command=Read Sector
Bit 7-5: Read Command (100)
m: 1=Multiple Records, 0=Single Record
S: Side Compare Flag (MFM only) 0=Disabled
E: 1=15ms Settle Delay, 0=No Delay
C: Side Compare (MFM only) 0=Disabled
Bit 0: 0
4387
OUT (F0H),A D3 F0
Send the READ SECTOR command to I/O port F0H (FDC Command/Status Register). This initiates the read operation for the sector specified in the Sector Register (port F2H). The Multiple Records flag allows reading consecutive sectors automatically.
4389
LD HL,5100H 21 00 51
Point Register Pair HL to 5100H. This is the destination buffer where the 256-byte sector data will be loaded. The bootstrap loader uses this buffer (5100H-51FFH) for staging data read from disk.
438C
LD BC,00F3H 01 F3 00
Load Register Pair BC with 00F3H. Register C contains F3H, which is the I/O port address for the FDC Data Register (used by INI instruction). Register B contains 00H (which represents 256 in this context), serving as the byte counter for the sector read.
438F
GOSUB to 43E2H to perform a timing delay. This ensures the FDC has time to locate the sector address mark and begin reading data before we start polling for DRQ. This delay prevents premature polling.

[DRQ POLLING LOOP] This tight loop waits for the FDC to set the DRQ (Data Request) bit, indicating a byte is ready in the Data Register. The timing is critical - if the CPU doesn't read the byte within about 24 microseconds, a "Lost Data" error will occur and the sector read will fail.

4392
IN A,(F0H) DB F0
Read the FDC Status Register from I/O port F0H into Register A. This checks the current status including the DRQ bit.
4394
AND D A2
Perform bitwise AND of Register A with Register D (02H). This masks out all bits except bit 1 (DRQ). If DRQ is set, the result will be non-zero and the NZ FLAG will be set.
4395
If the Z FLAG is set (meaning DRQ bit is not set yet), LOOP BACK to 4392H to continue polling. This waits in a tight loop until the FDC has a data byte ready to transfer.

[DATA TRANSFER LOOP] Once DRQ is set, this loop reads 256 bytes from the FDC Data Register into memory at 5100H. The INI instruction provides efficient I/O-to-memory transfers. The loop alternates between reading data and refreshing the drive select to maintain reliable operation.

4397
INI ED A2
Input from port (C) to memory at (HL), then increment HL and decrement B. This reads one byte from the FDC Data Register (port F3H) into the buffer at 5100H+offset, automatically advancing the buffer pointer and decrementing the 256-byte counter. This is an optimized Z80 block I/O instruction.
4399
LD A,E 7B
Load Register A with the value from Register E (the drive select byte with precompensation enabled, set at 436DH).
439A
OUT (F4H),A D3 F4
Send the drive select byte to I/O port F4H (Drive Select Register). This maintains the drive selection and ensures the drive motor stays enabled during the data transfer. This refresh is needed because the INI instruction may affect timing.
439C
INI ED A2
Input from port (C) to memory at (HL), then increment HL and decrement B. This reads another byte from the FDC Data Register, continuing the sector data transfer.
439E
If the NZ FLAG is set (meaning Register B has not reached zero yet), JUMP to 439AH to continue reading bytes. This loop continues until all 256 bytes of the sector have been transferred from the FDC to memory. [DATA TRANSFER LOOP END]
43A0
[INFINITE LOOP / SAFETY HALT] JUMP to itself (43A0H), creating an infinite loop. This should never be reached during normal operation - it serves as a safety halt point if the code flow somehow bypasses the NMI handler. Execution should continue via the NMI interrupt to 43A2H.

43A2H - NMI Handler and Read Completion

This routine is the NMI (Non-Maskable Interrupt) handler that gets control after a sector read completes. The FDC generates an interrupt when the read finishes, and the ROM's NMI vector at 4049H jumps here. This code handles cleanup after disk operations, checks for read errors, manages multi-sector read sequences, handles track boundary crossings, and implements retry logic for failed reads.

43A2
XOR A AF
[NMI ENTRY POINT] Set Register A to ZERO and clear all flags. This prepares for disabling interrupts.
43A3
OUT (E4H),A D3 E4
Send zero to I/O port E4H (NMI Mask Register). This disables NMI interrupts now that the critical disk read operation has completed successfully.
43A5
LD SP,0000H 31 00 00
Load Stack Pointer with 0000H. This temporarily sets an invalid stack pointer. The actual correct stack pointer will be restored from the saved value at 43A6H if needed, or reset properly before returning to the decompressor.
43A8
IN A,(F0H) DB F0
Read the FDC Status Register from I/O port F0H into Register A. This retrieves the final status of the completed READ SECTOR operation to check for any errors.
Status Register Value: Read from F0H (Type II)Type II Status
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Function
NWRFCLDBType II Command Status Register
N: 1=Not Ready, 0=Ready
W: 1=Write Protected, 0=Not Protected
R: 1=Record Type/Deleted Data, 0=Normal Data
F: 1=Record Not Found, 0=Record Found
C: 1=CRC Error, 0=No Error
L: 1=Lost Data, 0=No Data Lost
D: 1=Data Request (DRQ), 0=No Request
B: 1=Busy, 0=Not Busy
43AA
AND FCH E6 FC
Perform bitwise AND of Register A with FCH (11111100 binary). This masks out bits 0 and 1 (Busy and DRQ), leaving only the error status bits in positions 2-7 (Not Ready, Write Protect, Record Type, Record Not Found, CRC Error, Lost Data). If any error bits are set, the result will be non-zero and the NZ FLAG will be set.
43AC
LD A,D0H 3E D0
Load Register A with D0H, which is the FORCE INTERRUPT command for the WD1793 FDC.
1793 FDC Command: D0H (11010000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
11010000Command=Force Interrupt
Bit 7-4: Command Code (1101)
I3: 0=No Immediate Interrupt, 1=Interrupt Immediately
I2: 0=No Index Pulse Interrupt, 1=Interrupt on Index Pulse
I1: 0=No Ready Change Interrupt, 1=Interrupt on Ready→Not Ready
I0: 0=No Ready Change Interrupt, 1=Interrupt on Not Ready→Ready
All interrupt bits 0: Terminate command without interrupt
43AE
OUT (F0H),A D3 F0
Send the FORCE INTERRUPT command to I/O port F0H (FDC Command/Status Register). This terminates any ongoing FDC operation, clears the BUSY flag, and resets the controller to idle state, ensuring it's ready for the next command.
43B0
POP BC C1
Restore Register Pair BC from the stack. This recovers the retry counter in Register B (set to 0AH at 4349H) and the port address in Register C (F3H from 438CH), which were saved at 434EH.
43B1
POP DE D1
Restore Register Pair DE from the stack. This recovers the track number (Register D) and sector number (Register E) for the current disk position, which were saved at 434DH.
43B2
If the NZ FLAG is set (meaning error bits were detected in the FDC status at 43AAH), JUMP to 43C0H to handle the read error with retry logic. [ERROR PATH]

[SUCCESS PATH - ADVANCE TO NEXT SECTOR] If the read completed successfully (no error bits set in the FDC status), advance to the next sector on the disk. This handles sequential reading of the boot loader data across multiple consecutive sectors.

43B4
INC E 1C
INCrement Register E by 1. This advances to the next sector number on the current track.
43B5
LD A,E 7B
Load Register A with the new sector number from Register E.
43B6
SUB 12H D6 12
SUBtract 12H (18 decimal) from Register A. Model III disks have 18 sectors per track (numbered 0-17 or 1-18), so this checks if we've moved past the last sector of the track. If the result equals 12H exactly, we've reached sector 18 and need to advance to the next track.
43B8
If the NZ FLAG is set (meaning the sector number after subtraction is not equal to zero, so we haven't wrapped to the next track yet), JUMP to 43BDH to continue with the current track.

[TRACK BOUNDARY CROSSING] When we've read all 18 sectors on the current track (sector numbers 0-17 or 1-18), we need to advance to track number and reset the sector number back to 0 or 1 for the beginning of the next track.

43BA
INC D 14
INCrement Register D by 1. This advances to the next track number.
43BB
LD E,00H 1E 00
Load Register E with 00H. This resets the sector number to 0 (or will be treated as sector 1 depending on the DOS sector numbering scheme) for the first sector on the new track.
43BD
EXX D9
Exchange Register Pairs with shadow counterparts. This switches back to the primary register set that contains the decompression state, specifically HL which points to the current position in the compressed data source buffer.
43BE
LD A,(HL) 7E
Load Register A with the byte at address HL. This fetches the next byte from the decompressed or source buffer, ready for the decompression routine to process.
43BF
RET C9
RETURN to the caller (the compressed data fetch routine at 4346H-4347H). Control continues with the bootstrap decompression process using the newly loaded sector data.

[ERROR RETRY LOGIC] This section handles read errors by performing a RESTORE operation (returning the head to track 0) and then retrying the read from the beginning. The Model III bootstrap allows up to 10 retry attempts (counter set at 4349H) before giving up.

43C0
GOSUB to 43E2H to perform a timing delay. This gives the disk drive mechanism and FDC time to settle after the error before attempting the RESTORE operation.
43C3
LD A,0BH 3E 0B
Load Register A with 0BH, which is a WD1793 RESTORE command with specific control flags.
1793 FDC Command: 0BH (00001011)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
00001011Command=Restore
Bit 7-4: Command Code (0000)
h: 1=Enable Head Load/Settle, 0=No delay
V: 1=Verify Track 0 ID, 0=No verification
r1, r0: Stepping Motor Rate 11=30ms, 10=20ms, 01=12ms, 00=6ms
43C5
OUT (F0H),A D3 F0
Send the RESTORE command to I/O port F0H (FDC Command/Status Register). This moves the disk head back to track 0 with head load enabled and 30ms stepping rate. Track verification is disabled to speed up the retry process. This essentially resets the drive to a known state before retrying.
43C7
DECrement Register B (the retry counter, originally set to 0AH at 4349H) and JUMP to 434BH if not zero. This loops back to retry the entire disk read operation from the track/sector position saved in DE. If Register B reaches zero (all 10 retry attempts exhausted), execution falls through to the fatal error handling code below.

[FATAL ERROR - ALL RETRIES EXHAUSTED] If we reach this point, all 10 retry attempts have failed to successfully read the sector. The code prepares to display an error message and enter an infinite loop, effectively halting the system. The user must power cycle or reset the computer.

43C9
LD HL,43E8H 21 E8 43
Point Register Pair HL to 43E8H. This is the address of error message data or an error status control byte in this boot sector.
43CC
LD A,(HL) 7E
[ERROR DISPLAY LOOP ENTRY / ERROR WAIT LOOP] Load Register A with the byte at address HL (43E8H). This reads an error status or control byte that may change during error processing.
43CD
CP 03H FE 03
Compare Register A against 03H. This checks for a specific error condition code or ready state marker. If Register A equals 03H, the Z FLAG is set; otherwise the NZ FLAG is set.
43CF
If the Z FLAG is set (meaning the byte equals 03H, indicating a wait state), LOOP BACK to 43CCH to continue waiting. This creates a spin loop waiting for the status to change, though in practice this may loop forever on a fatal boot error.
43D1
INC HL 23
INCrement Register Pair HL by 1. This advances to the next byte in the error message or control structure at 43E9H.
43D2
GOSUB to 0033H in the Model III ROM which display the message pointed to by HL on the screen.
43D5
JUMP back to 43CCH. This creates an infinite loop in the error handler, effectively halting the system after a fatal boot error. The system is stuck and requires a hardware reset or power cycle.

43D7H - FDC Wait for Not Busy

This routine polls the WD1793 FDC Status Register in a tight loop, waiting for the BUSY bit (bit 0) to clear. This is used after issuing FDC commands that require time to complete, such as SEEK, RESTORE, or READ SECTOR operations. The routine includes a timing delay at the start to prevent excessive polling immediately after the command is issued, then polls continuously until the FDC completes its operation.

43D7
GOSUB to 43E2H to perform a brief timing delay. This prevents the CPU from polling the FDC status too rapidly immediately after issuing a command, which could interfere with the FDC's internal state machine and command processing.
43DA
IN A,(F0H) DB F0
[BUSY POLL LOOP START] Read the FDC Status Register from I/O port F0H into Register A. This retrieves the current status including the BUSY bit.
43DC
RRCA 0F
Rotate Register A Right through Carry. This moves bit 0 (the BUSY bit) into the CARRY FLAG. If the FDC is still busy executing the command, bit 0 will be 1 and the CARRY FLAG will be set. If the command is complete, bit 0 will be 0 and the CARRY FLAG will be clear.
43DD
If the CARRY FLAG is set (meaning the BUSY bit was 1 and the FDC is still executing), LOOP BACK to 43DAH to continue polling. This tight loop waits until the FDC command completes. [BUSY POLL LOOP END]
43DF
IN A,(F0H) DB F0
Read the FDC Status Register from I/O port F0H into Register A one final time. This retrieves the complete final status of the finished command (including any error bits) for the caller to examine and process.
43E1
RET C9
RETURN to the caller with the final FDC status in Register A. The caller can check error bits to determine if the operation succeeded.

43E2H - Short Timing Delay

This is a simple software timing delay routine that creates a short pause of approximately 8 loop iterations. It's used throughout the bootstrap code to ensure proper timing for FDC operations, giving the disk controller hardware time to respond to commands, settle between operations, or complete internal state changes. The delay prevents race conditions and timing-related errors.

43E2
LD A,08H 3E 08
Load Register A with 08H (8 decimal). This is the delay loop counter value.
43E4
DEC A 3D
[DELAY LOOP START] DECrement Register A by 1. Each iteration of this loop takes a fixed number of clock cycles (the DEC A instruction plus the conditional jump), creating a calibrated timing delay.
43E5
If the NZ FLAG is set (meaning Register A has not reached zero), LOOP BACK to 43E4H. This continues the delay until all 8 iterations complete. [DELAY LOOP END]
43E7
RET C9
RETURN to the caller after the timing delay has completed.

43E8H - Error Messages and Boot Data

This section contains embedded error messages, control bytes, and data strings that are displayed when the bootstrap process fails. The messages are stored in a format with length bytes, control codes, and ASCII text. These strings are shown on the screen when fatal errors occur during the boot process, such as disk read failures or missing system files. The format includes display control codes for positioning and formatting the error messages on the TRS-80 Model III screen.

43E8
DEFB 1CH + 1FH1C 1F
Data byte 1CH and 1FH combine to CLEAR THE SCREEN.
43EA
DEFM "ERROR" 45 52 52 4F 52
ASCII text string "ERROR" (5 bytes). This is the first part of the boot error message that will be displayed on screen when the bootstrap fails.
43EF
DEFB 03H 03
Data byte 03H. This is a terminator the end of the "ERROR" message string.
43F0
DEFB 1CH + 1FH1C 1F
Data byte 1CH and 1FH combine to CLEAR THE SCREEN.
43F2
DEFM "NO SYS" 4E 4F 20 53 59 53
ASCII text string "NO SYS" (6 bytes). This error message indicates that the system files could not be found or loaded from the boot disk, meaning BOOT/SYS is present but the main DOS system files are missing or unreadable.
43F8
DEFB 03H 03
Data byte 03H. Message terminator for the "NO SYS" string.
43F9
NOP 00
No operation (padding or filler byte).
43FA
NOP 00
No operation (padding or filler byte).
43FB
NOP 00
No operation (padding or filler byte).
43FC
NOP 00
No operation (padding or filler byte to align data).
43FD
DEFB FEH,07H FE 07
No idea :(
43FF
DEFB 01H,00H,FEH 01 00 FE
No idea :(
4402
DEFB 11H,F3H,11H 11 F3 11
No idea :(
4405
DEFB 05H 05
No idea :(
4406
NOP 00
No operation (end of data section padding). This marks the end of the error message and control data area.

4407H - Relocatable Load Block Data

This section contains relocatable load blocks in a simple format that specifies where to load code and data in memory. The format is NOT compression - it's a straightforward block loader that reads: control byte, byte count, destination address, and then copies the specified bytes to the destination. This allows NEWDOS/80 to load different modules to different memory locations from a single sequential data stream stored on disk.

[RELOCATABLE BLOCK LOADER FORMAT]

The loader at 430EH-433EH processes blocks in this format:

Block Structure:
1. Control Byte: If < 20H, it's a valid block; if ≥ 20H, end of blocks
2. Byte Count: Number of bytes in this block
3. Destination Low: Low byte of destination address
4. Destination High: High byte of destination address
5. Data Bytes: The actual code/data to copy to destination

The loader reads blocks sequentially from the 5100H-51FFH buffer,
copying each block's data to its specified destination address in RAM.
This continues until a control byte >= 20H is encountered, then the
loader verifies the signature byte A5H and transfers control to the
DOS entry point.

[DATA AT 4407H - ACTUAL BOOTSTRAP CODE]

Looking at the bytes at 4407H, we see they match the bootstrap code at 4300H+:

4407H: D9H = EXX instruction (same as 4307H)
4408H: 31H E0H 41H = LD SP,41E0H (same as 4308H-430AH)
440BH: 21H FFH 51H = LD HL,51FFH (same as 430BH-430DH)

This is the bootstrap loader code itself being stored in relocatable block format!
The data represents load blocks that will write the bootstrap code (and additional
DOS modules) to their proper locations in memory.

[HOW THE BLOCKS ARE PROCESSED]

STEP
Example of processing a typical block:
1
430E: CALL 4345H
Fetch control byte (e.g., 07H = 7 bytes follow)
2
4311: CP 20H
Check if 07H < 20H (yes, so continue)
3
4313: LD B,A
B = 07H (control byte saved)
4
4314: JR NC,433FH
07H < 20H, so CARRY is set, do NOT jump
5
4316-431E
Read byte count into C, destination address into DE
6
4321-4331
Copy C bytes from buffer at HL to destination at DE
7
4331: JR 430EH
Loop back to process next block

[BUFFER MANAGEMENT]

The buffer at 5100H-51FFH is filled by reading sectors from disk.

At 4218H: LD HL,51FFH - Initialize buffer pointer
At 4345H: INC L - First increment makes L=00H, so HL=5200H
Wait, that would make it 5200H not 5100H...

Actually looking more carefully at the Model III code vs Model I:
- Model I: LD HL,51FFH makes buffer 5200H-52FFH (after first INC L)
- Model III: LD HL,51FFH at 430BH, same logic applies

So the Model III buffer is also 5200H-52FFH (256 bytes).

When the buffer is exhausted (L wraps to 00H), the page boundary
handler at 4348H loads another sector from disk into the buffer.

[SELF-LOADING BOOTSTRAP]

The bootstrap process:

1. ROM loads Track 0, Sector 1 (512 bytes) to 4300H
2. ROM jumps to 4303H (DI instruction)
3. Bootstrap initializes, sets up buffer pointer to 51FFH
4. Bootstrap begins processing load blocks from buffer
5. When buffer empties, page handler loads next sector from disk
6. Load blocks specify where to write code in memory
7. This includes rewriting the bootstrap area itself (4300H+)
8. Additional blocks load SYS0/SYS and other DOS modules
9. When control byte >= 20H found, verify signature A5H
10. Jump to DOS entry point

The bootstrap OVERWRITES ITSELF as it loads the DOS system!

4407
DEFB D9H D9
[Start of load block data] This byte D9H is part of the relocatable load block data. It represents the EXX instruction that will be copied to a destination address specified earlier in the block header.
4408
DEFB 31H,E0H,41H 31 E0 41
These three bytes form the LD SP,41E0H instruction that will be copied to memory as part of a load block.
440B
DEFB 21H,FFH,51H 21 FF 51
The LD HL,51FFH instruction stored as data in a load block.

The data continues with more bootstrap code bytes that will be copied to their destinations. The actual block headers (control byte, count, address) are interspersed with this data in the proper format.

440E
[Load block data continues... approximately 504 bytes containing bootstrap code and additional DOS modules in relocatable block format]

4600H - Boot Sector Padding

This section contains padding bytes (5BH) that fill the remainder of the boot sector from the end of the load block data to the end of the 512-byte sector. The padding ensures the boot sector is exactly 512 bytes (200H) as required by the Model III disk format. The value 5BH (01011011 binary) disassembles as "LD E,E" which is a harmless instruction. This padding starts at approximately 4600H and continues to 47FFH.

4600
DEFB 5BH 5B
[PADDING START] Padding byte 5BH (LD E,E instruction opcode). This fills unused space in the boot sector.
4601
[Padding continues with 5BH bytes through to 47FFH - total of 512 padding bytes]

The boot sector padding continues with 5BH bytes through to the end of the 512-byte sector at 47FFH. These padding bytes ensure the sector is the correct size. The 5BH byte (LD E,E) was chosen as it's a harmless instruction that loads E into itself - if execution accidentally reaches this area, the instructions do nothing harmful.

47FF
DEFB 5BH 5B
[END OF BOOT SECTOR] Final padding byte at the end of the 512-byte boot sector.