TRS-80 DOS - PerCom MicroDOS Version 2.20 for the Model I - Boot Sector Disassembled
Page Customization
Page Index
Boot Sector (Track 0, Sector 0)
Other Navigation
Introduction/Summary:
PerCom MicroDOS 2.20 Boot Sector Disassembly (Model I)
The PerCom MicroDOS boot sector is the first 256 bytes of any MicroDOS-formatted floppy. It lives at Track 0, Sector 0, and is loaded into RAM at 4200H by the Model I floppy boot ROM (the "boot strap" code). After loading the sector, the boot ROM transfers control to 4200H, and the code documented on this page takes over.
MicroDOS was written by James W. Stutsman and sold by PerCom Data Company in 1979 as a smaller, faster alternative to TRSDOS for users who wanted a Disk BASIC environment without the overhead of TRSDOS' supervisor calls and overlays. The entire resident operating system is just 4,608 bytes long; the boot sector that loads it is correspondingly compact, fitting the disk-detection signature, the MICRODOS / DISK ERROR / NO MICRODOS messages, the chained-sector reader, and the entire WD1771 FDC driver into 220 bytes of code plus 36 bytes of data, all within a single 256-byte sector.
The boot sector performs four jobs in sequence: (1) it sets up a stack at 41FCH and clears the screen via the Level II BASIC ROM CLS routine at 01C9H; (2) it reads Track 0 / Sector 1 into a buffer at 4300H and verifies that the eight-byte signature MICRODOS appears at offset 4 of the buffer (i.e., at 4304H); (3) on a successful match, it walks a chain of sectors starting at T0/S1, copying each one to the OS load area at 4400H, with each sector's first byte serving as a length-and-continuation marker; and (4) on the final sector, it transfers control to 4400H, which holds a JP 4727H to the MicroDOS resident entry point.
The chained-sector mechanism is the key trick. Each sector that participates in the OS load begins with a single byte: FFH means "I am a continuation; copy my remaining 255 bytes and read the next sector," and any other value N means "I am the final sector; copy my next N bytes, then jump to 4400H to start the OS." On this disk, the chain runs T0/S1 through T0/S9, then T1/S0 through T1/S8 (all with FFH markers), and ends at T1/S9 (with marker 12H = 18 bytes), giving a total OS image of 18 sectors x 255 bytes + 18 bytes = 4,608 bytes loaded contiguously at 4400H through 55FFH. What this means is that a straight review of the hex of the DMK is not relevant. The hex needs to be interleaved in sector order to present a disassemblable program.
If the MICRODOS signature is missing at 4304H, control falls through to the NO MICRODOS error path at 42A7H, which clears the screen, prints the message, and waits for any key before rebooting via JP 0000H. If any FDC operation returns an error status (bits 9CH set after a Read Sector, or bits 98H set after a Seek), control falls through to the DISK ERROR path at 42A2H, which displays the message and reboots in the same way.
The Boot Sector
The first bytes are a sector header table. 0000H-04B0H contains 2,901 entries of 3 bytes each. Each entry has the form TT SS FF where TT is the track number, SS is the sector number, and FF is a flags byte. Unused entries are filled with FFH FFH FFH. Order in the table is the on-disk order, which on a Model I single-density disk is the 5:1 interleave (S0, S5, S1, S6, S2, S7, S3, S8, S4, S9 within each track).
This MicroDOS disk uses 400 of the 2,901 available header entries (40 tracks x 10 sectors = 400 sectors), so file offsets 0000H-04AFH contain the active table and offsets 04B0H-21FEH are filled with FFH (free entries). The first ten entries describe the ten sectors of Track 0 in physical (interleaved) order:
| File Offset | Bytes | Meaning |
|---|---|---|
| 0000H-0002H | 00 00 00 | Track 0, Sector 0 (the boot sector), 256 bytes, single density, side 0. Sector data lives at file offset 2200H. |
| 0003H-0005H | 00 05 00 | Track 0, Sector 5. Sector data lives at file offset 2300H. |
| 0006H-0008H | 00 01 00 | Track 0, Sector 1 (first OS sector). Sector data lives at file offset 2400H. |
| 0009H-000BH | 00 06 00 | Track 0, Sector 6. Sector data lives at file offset 2500H. |
| 000CH-000EH | 00 02 00 | Track 0, Sector 2. |
| 000FH-0011H | 00 07 00 | Track 0, Sector 7. |
| 0012H-0014H | 00 03 00 | Track 0, Sector 3. |
| 0015H-0017H | 00 08 00 | Track 0, Sector 8. |
| 0018H-001AH | 00 04 00 | Track 0, Sector 4. |
| 001BH-001DH | 00 09 00 | Track 0, Sector 9. |
The 5:1 interleave (S0, S5, S1, S6, S2, S7, S3, S8, S4, S9) is the standard Model I single-density layout: by the time the WD1771 finishes reading sector S0 and the CPU finishes processing it, the disk has rotated to where sector S1 is just arriving at the head, so the system reads sectors in numerical order without ever waiting a full rotation. The same pattern repeats for tracks 1 through 39, occupying file offsets 001EH-04AFH (390 more entries x 3 bytes = 1,170 bytes).
Memory Map
The boot sector occupies the entirety of memory page 42H. The buffer at 4300H and the OS load area starting at 4400H are also reserved by the boot sector's design.
| Address Range | Label | Contents |
|---|---|---|
| 4200H-42DCH | BOOT | Boot sector executable code (221 bytes). Loaded by the Model I boot ROM from Track 0 / Sector 0. |
| 42DDH | TRACK | One-byte storage holding the track number to be loaded next. Initialized to 00H so the first FDC seek targets Track 0; updated by the load loop at 4246H as the chain advances. |
| 42DEH | SECTOR | One-byte storage holding the sector number to be loaded next. Initialized to 01H so the first read fetches Track 0 / Sector 1; updated by the load loop at 423FH as the chain advances and rolled back to 00H by 4243H when it passes 9. |
| 42DFH-42EBH | DISKERR | The DISK ERROR message, prefixed by 17H E8H (a HOME / cursor-position byte sequence) and terminated by 00H. Displayed when an FDC operation returns an error. |
| 42ECH-42F0H | NOMDOS | The lead-in of the NO MICRODOS message, prefixed by 17H E8H (HOME and a graphics marker) and continuing with the three characters NO followed by a space. Continues into the SIG label below, which holds the shared MICRODOS tail. |
| 42F1H-42F8H | SIG | The eight-byte ASCII string MICRODOS, used by the loader at 421DH to verify that the disk holds a real PerCom MicroDOS image, and simultaneously serving as the tail of the NO MICRODOS error message at 42ECH. The boot sector deliberately overlaps the signature and the error string to save eight bytes. |
| 42F9H | - | One pad byte (00H), the NUL terminator for the NO MICRODOS error message. |
| 42FAH-42FFH | - | The six-byte string $STEPH. This is the tail of an internal label name that ran past the end of the source's last DEFM directive when MicroDOS was assembled, and is meaningless at runtime; it is never read by any code. |
| 4300H-43FFH | BUFFER | 256-byte sector read buffer. Each FDC Read Sector call fills this buffer; the loader then either copies its contents (minus the first byte) into the OS load area or compares its contents against the MICRODOS signature. |
| 4400H-55FFH | MICRODOS | The resident MicroDOS operating system, 4,608 bytes total, loaded by the boot sector from the chain T0/S1 -> T0/S9 -> T1/S0 -> T1/S9. The first three bytes at 4400H are JP 4727H, the entry point that the boot sector's final JP NZ,4400H transfers to. |
| 41FCH-41FFH | STACK | Initial stack location set by 4202H. The boot sector's CALLs and PUSHes use this region; once MicroDOS takes over at 4727H, it relocates the stack to 41F8H. |
Self-Modifying Workspace Variables
The boot sector keeps two single-byte variables in the data region following its code. Both are written by the load-loop advance code at 423FH-4246H and read by the FDC seek setup at 428BH-4294H.
| Address | Label | Size | Description |
|---|---|---|---|
| 42DDH | TRACK | 1 | Holds the track number passed to the WD1771 Seek command. Initialized in the source to 00H. Incremented by the INC (HL) at 4246H whenever the sector counter wraps from 9 to 0. After the load completes, it holds the track number of the final sector read (01H on a stock disk). |
| 42DEH | SECTOR | 1 | Holds the sector number written to the WD1771 Sector Register at 4294H. Initialized in the source to 01H. Incremented by the INC (HL) at 423FH on each loop iteration; reset to 00H by the LD (HL),0 at 4243H when it would otherwise reach 0AH. After the load completes, it holds the sector number of the final sector read (09H on a stock disk). |
Major Routines
| Address | Label | Function / Entry-Exit |
|---|---|---|
| 4200H | BOOT Entry Point Receives control from the Model I boot ROM. Sets up the stack, clears the screen, initializes the FDC pointer registers in the alternate set, reads Track 0 / Sector 1, verifies the MICRODOS signature, and falls through to the load loop. | |
| 422CH | Sector Load Loop Top of the chained-sector copy loop. Examines the first byte of the buffer at 4300H to decide whether to copy 255 bytes and continue (FFH marker) or copy N bytes and jump to 4400H (any other marker). Loops via 424AH back to 422CH. | |
| 424CH | Read Sector Routine Calls 4275H to seek the head and select the drive, issues the WD1771 Read Sector command (88H), polls the FDC status register for Data Request flags, and transfers each data byte from 37EFH into the sector buffer. Returns with BC restored to 4300H. On error (FDC status bits 9CH set), jumps to 42A2H. | |
| 4275H | Seek and Drive-Select Routine Reads the FDC status, ensures drive 0 is selected at 37E1H, optionally calls the Level II BASIC ROM delay routine at 0060H if the drive is not yet ready, writes the track number from 42DDH into the FDC data register at 37EFH, writes the sector number from 42DEH into the sector register at 37EEH, and issues the WD1771 Seek command (17H). Returns with the Z flag set if the seek completed without error; otherwise the caller transfers control to the DISK ERROR path. | |
| 42A2H | Disk Error Handler Loads HL with a pointer to the DISK ERROR message at 42DFH and falls through to the message-display path at 42AAH. | |
| 42A7H | No-MicroDOS Handler Loads HL with a pointer to the NO MICRODOS message at 42ECH and falls through to the message-display path at 42AAH. | |
| 42AAH | Message Display and Reboot Calls the Level II BASIC ROM CLS routine at 01C9H, calls the print-string routine at 42D4H, calls the Level II BASIC ROM keyboard scan at 002BH in a loop until any key is pressed, and then jumps to 0000H to reboot the system. | |
| 42B9H | Send FDC Command Routine Saves the command byte (in Register A) on the stack, calls 42C7H to wait for the FDC to be ready, restores the command byte, and writes it through the alternate-register pointer in Register Pair DE' (37ECH) to the FDC command register. Polls bit 0 of the status register until the busy flag clears. | |
| 42C7H | Wait for FDC Not Busy Loops, repeatedly writing 01H to the drive-select latch at 37E1H and reading the FDC status register, until bit 0 (busy) goes low. The repeated drive-select writes are necessary because the Model I drive-select latch is not a true latch: it is a one-shot driven by the system clock, and if the routine takes too long, the drive deselects itself. | |
| 42D4H | Print NUL-Terminated String Walks the byte stream pointed at by Register Pair HL, calling the Level II BASIC ROM character-output routine at 0033H for each non-zero byte and returning when a zero byte is reached. |
Cross-References and Analysis Notes
The boot sector's only external dependencies are four routines in the Level II BASIC ROM: 0033H (display character), 002BH (keyboard scan), 0060H (timing delay), and 01C9H (clear screen). Every other call and jump is internal to the boot sector itself.
The boot sector also depends on the Model I boot ROM's pre-conditions: when 4200H receives control, the boot ROM has just read Track 0 / Sector 0 from drive 0 using a Restore-then-Read-Sector sequence, so the WD1771 head is already on Track 0, drive 0 is already selected, and the FDC's busy flag is clear. The very first instruction (CP 0 at 4200H) does not assume any particular value in Register A; it is a one-byte placeholder so that the boot ROM's entry point lands cleanly on a Z80 instruction boundary, while preserving the option of testing A on entry should a future revision want to.
The sector load loop at 422CH-424AH is unusually compact for a Model I bootstrap. Compare TRSDOS 1.3, TRSDOS 2.3, NEWDOS/80, and LDOS, all of which reserve at least 512 bytes (two sectors) for their bootstrap because they need to fit a directory-aware loader; MicroDOS sidesteps the entire directory mechanism by chaining sectors physically across track boundaries with a one-byte length prefix in each sector. This restricts the resident OS image to a contiguous run starting at T0/S1, but it also means the bootstrap is correspondingly smaller.
Disassembly:
4200H - BOOT - Boot Entry Point and Stack Setup
The Model I floppy boot ROM has just loaded Track 0 / Sector 0 (the boot sector) into RAM at 4200H-42FFH and jumped here. On entry, drive 0 is selected, the WD1771 head is at Track 0, and the FDC busy flag is clear. The first job is to set up a stack, clear the screen, and stash the FDC register addresses in the alternate register set so they survive the upcoming string-comparison loop.
NOTE: 01C9H is the Level II BASIC ROM CLS (Clear Screen) routine. It fills video RAM at 3C00H-3FFFH with 1024 spaces (20H) and homes the cursor to 3C00H. Uses Registers A, B, and HL.
4215H - SIGCHK - MICRODOS Signature Verification
The buffer at 4300H now holds the first sector of what should be the MicroDOS resident image. To guard against trying to boot a non-MicroDOS disk, the loader compares the eight-byte ASCII string MICRODOS held in the boot sector at 42F1H-42F8H against the eight bytes at 4304H-430BH (the buffer offset 4 through 11). On a match, control falls through to the banner-print and load-loop code; on a mismatch, control jumps to the NO MICRODOS error handler at 42A7H.
Loop Start
Compare each byte of the MICRODOS signature in the boot sector against the corresponding byte at offset 4 of the sector buffer.
Loop End
Signature verified: this disk is a valid MicroDOS image. Continue with banner display and OS load.
4226H - LOADLP - OS Load Loop
With the signature verified, the boot sector now copies the MicroDOS resident image into memory at 4400H. The first sector (T0/S1) has already been read into the buffer at 4300H by the call at 4212H. The loop walks each sector's first byte: FFH means "this is a continuation sector, copy bytes 1-255 to the OS load area and read the next sector," and any other value N means "this is the final sector, copy bytes 1-N and jump to 4400H to start the OS." The same one-byte marker is used as both the continuation flag and the byte count via the trick that LDIR with B=0 and C=FFH copies 255 bytes (00FFH = 255), and INC A on FFH rolls the byte over to 00H, setting the Z flag.
Loop Start
Top of the load loop. Each iteration copies one sector's data to the OS load area. The loop terminates when a non-FFH first byte indicates the final sector.
Source = HL = 4301H (first payload byte of the buffer); Destination = DE = current OS load offset (initially 4400H, advances with each sector); Byte count = BC = 00FFH on continuation sectors (255 bytes) or 00xxH on the final sector (xx = the marker value, in this case 12H = 18 bytes).
NOTE: 4400H is the start of the MicroDOS resident image just loaded by this loop. The byte at 4400H is C3 (JP), at 4401H is 27H, and at 4402H is 47H, decoding as JP 4727H.
Loop End
Control leaves this loop only via the JP NZ,4400H at 4237H, which is taken when the final sector's chain marker is reached.
424CH - RDSEC - Read One Sector via the WD1771 FDC
Issues a WD1771 Read Sector command for the track and sector held at 42DDH and 42DEH, transferring 256 bytes from the disk into the sector buffer pointed at by BC' (4300H, set up at 4208H and saved into the alternate register set by the EXX at 4211H). The routine works in the alternate register set so that DE' = 37ECH (FDC command/status) and HL' = 37EFH (FDC data) need not be reloaded on every call. On a successful read, returns with BC restored to 4300H ready for the next iteration of the load loop. On error, jumps to 42A2H to display DISK ERROR and reboot.
| 1771 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 | 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) | |
Status Test
Reached at the end of each byte-fetch iteration. If both busy and DRQ are clear in the masked status, the sector has finished transferring and control falls through to 4267H. Otherwise control loops back to the data-byte fetch.
Data Request Wait
The FDC is still busy and the drive is still ready. Spin until the next data byte arrives.
End of Sector
The FDC has cleared its busy flag, indicating the Read Sector command has completed (successfully or with an error).
| 1771 FDC Status Register (after Read Sector) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| 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 | |
| 1771 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 | 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 condition bits 0: Terminate command and clear busy | |
4275H - SETSEC - Drive Select, Seek, and Sector Setup
Called by the Read Sector routine at 424CH before each Read Sector command, this routine ensures the FDC is positioned correctly for the read. It selects drive 0 by writing 01H to 37E1H, optionally calls the ROM delay at 0060H if the drive was not ready (giving the spindle time to come up to speed), writes the desired track number from 42DDH into the FDC data register, writes the desired sector number from 42DEH into the FDC sector register, and issues a WD1771 Seek command (17H) to position the head over the requested track. On a successful seek, returns with the Z flag set; on a seek error, jumps to 42A2H.
Drive Spin-Up Delay
The drive was not yet ready. Use the ROM keypress-loop routine at 0060H to wait for the spindle to come up to speed, then re-select the drive.
NOTE: 0060H is the Level II BASIC ROM keyboard-poll-with-delay routine. It loops up to BC times, reading the keyboard on each pass and returning early if any key is pressed (with the keycode in Register A). When called with BC = 0000H from a boot context where no user is expected to be pressing keys, it serves as a calibrated 0.5-second delay.
| 1771 FDC Command: 17H (00010111) | 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 | h | V | r1 | r0 | Command=Seek Bit 7-4: Command Code (0001) h: 0=No delay, 1=Enable Head Load/Settle V: 1=Verify Destination Track ID, 0=No verification r1, r0: Stepping Motor Rate (00=3ms, 01=6ms, 10=10ms, 11=15ms) | |
42A2H - DSKERR - Disk Error and No-MicroDOS Error Display
Reached either by falling through from the Seek error test at 42A1H, by the JR NZ,42A2H from the Read Sector error test at 426EH, or by the JP NZ,42A7H from the MICRODOS signature compare at 421FH. Loads HL with a pointer to the appropriate error message, displays it on a cleared screen, waits for any key, and reboots the system. The two entry points 42A2H (DISK ERROR) and 42A7H (NO MICRODOS) differ only in which message they display.
NOTE: 01C9H is the Level II BASIC ROM CLS routine. It clears video RAM at 3C00H-3FFFH and homes the cursor.
Wait for Keypress
Spin until the user presses any key, then reboot.
NOTE: 002BH is the Level II BASIC ROM keyboard-scan entry point. It returns with the ASCII code of the pressed key in Register A, or 00H if no key is currently pressed.
42B9H - SNDCMD - Send Command to FDC
Writes the byte held in Register A to the WD1771 command register at 37ECH after waiting for the FDC to be not busy. Used by both the Read Sector routine at 4251H (which passes 88H = Read Sector) and the Seek and Drive Select routine at 4299H (which passes 17H = Seek). On entry, the FDC may be busy with a previous command; on exit, the new command has been issued and the routine has waited for the resulting busy state to begin.
Busy Wait
Spin reading the status register until the busy flag goes high. The FDC takes a small number of microseconds to actually start executing after the command write; this loop ensures the caller does not see the old (clear) busy flag and assume the command is already done.
42C7H - WAITNB - Wait for FDC Not Busy
Loops reading the FDC status register, re-asserting drive 0's select line on each iteration, until the busy flag (bit 0) goes low. Used by 42BAH before sending a new command (to ensure the previous command has finished) and by 429CH after issuing a Seek (to wait for the seek to complete). On exit, Register A holds the most recent status byte read.
Loop Start
Each iteration re-selects drive 0 (because the latch is a one-shot) and re-reads the status. The loop terminates when the busy bit goes low.
Loop End
The FDC has cleared its busy flag. Register A holds the final status byte for inspection by the caller.
42D4H - PRINTS - Print NUL-Terminated String
Walks the byte stream pointed at by Register Pair HL, calling the Level II BASIC ROM character output routine at 0033H for each non-zero byte and returning when a zero byte is reached. Used three times in the boot sector: at 4226H to print the MicroDOS banner from the just-read first OS sector, at 42ADH to print the DISK ERROR message, and at 42ADH again to print the NO MICRODOS message. The first byte of every message is 17H (the Model I VDU control code for HOME), and the second is E8H (the high bit of which switches the next character into reverse video on Model I systems with the optional video-modification kit; on stock systems it is treated as a graphics character and is harmless).
NOTE: 0033H is the Level II BASIC ROM character-output routine. It takes the character in Register A and writes it to the current video cursor position, advancing the cursor and handling control codes (CR, LF, HOME, etc.) as appropriate.
42DDH - DATA - Workspace Variables and Message Strings
The remainder of the boot sector holds the two workspace variables (the track and sector numbers used by the FDC seek routine), the two error message strings, and the eight-byte MICRODOS signature used for disk identification. None of this data is executable; the disassembler's interpretation of these bytes as Z80 instructions is meaningless and is replaced here with DB / DEFM directives showing the actual contents.