TRS-80 DOS - NEWDOS/80 v2.0 for the Model III - BOOT/SYS Disassembled
Page Index
Introduction/Summary:
Complete analysis summary of the NEWDOS/80 Model III bootstrap loader.
BOOT PROCESS OVERVIEW
- ROM Boot (Model III ROM at 34E0H):
- ROM loads Track 0, Sector 1 to address 4300H
- ROM jumps to 4300H to begin bootstrap execution
- Bootstrap Initialization (4300H-430BH):
- Disables interrupts
- Sets up stack at 41E0H
- Initializes buffer pointer to 51FFH
- Preserves state in shadow registers (EXX)
- 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
- 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
- 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
- Support Routines:
- 43D7H: Wait for FDC not busy
- 43E2H: Short timing delay
- 43E8H: Error messages ("ERROR", "NO SYS")
- 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
- 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:
| Address | Bytes | Purpose |
|---|---|---|
| F0H | Port | WD1793 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. |
| F1H | Port | WD1793 FDC Track Register (I/O Port). Used to specify the target track number for FDC seek operations. Not directly used in this bootstrap code. |
| F2H | Port | WD1793 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. |
| F3H | Port | WD1793 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. |
| F4H | Port | Drive 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. |
| E4H | Port | NMI 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. |
| 4049H | 3 | NMI 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. |
| 41E0H | N/A | Stack Pointer Location. Set at 4308H with "LD SP,41E0H". The stack grows downward from this address during bootstrap execution. |
| 43A6H | 2 | Saved 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. |
| 43E8H | 8 | "ERROR" Message Data. Contains: 1CH 1FH "ERROR" 03H. Referenced at 43C9H for fatal error display. |
| 43F0H | 8 | "NO SYS" Message Data. Contains: 1CH 1FH "NO SYS" 03H. Referenced at 433FH when boot disk validation fails. |
| 5100H-51FFH | 256 | FDC 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-52FFH | 256 | Load 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.
[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.
[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.
[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.
[ALTERNATE BLOCK PROCESSING PATH] This section handles special block processing when the control byte indicates a different mode (when DJNZ at 431FH branches here).
[ERROR PATH - SIGNATURE VERIFICATION FAILED] If the signature verification fails, execution continues here to display an error message 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.
| Port F4H Value: 81H (10000001) | Drive Select/Control Register | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Function | |
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | Drive 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 | |
| Port F4H Value: 91H (10010001) | Drive Select/Control Register | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Function | |
| 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | Drive 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.
| 1793 FDC Command: 1BH (00011011) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 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 11=30ms, 10=20ms, 01=12ms, 00=6ms | |
[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.
[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.
| 1793 FDC Command: 88H (10001000) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | Command=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 | |
[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.
[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.
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.
| Status Register Value: Read from F0H (Type II) | Type II Status | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Function | |
| N | W | R | F | C | L | D | B | Type 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 | |
| 1793 FDC Command: D0H (11010000) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | Command=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 | |
[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.
[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.
[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.
| 1793 FDC Command: 0BH (00001011) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | Command=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 | |
[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.
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.
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.
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.
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]
[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!
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.
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.
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.