TRS-80 DOS - TRSDOS v2.3 for the Model I - BOOT/SYS Disassembled

Page Customization

Introduction:

TRSDOS 2.3 BOOT/SYS Disassembly - Bootstrap Loader (Model I)

BOOT/SYS is the 256-byte boot sector of TRSDOS v2.3 for the TRS-80 Model I. It resides on Track 0, Sector 0 of the system disk and is loaded into memory at 4200H by the Level II ROM bootstrap routine (located at 06B6H-06CBH in the ROM). The ROM reads this single sector using the FDC at 37ECH, then jumps to 4203H (the entry point embedded in the CP instruction at 4201H) to transfer control to the boot code.

The sole purpose of BOOT/SYS is to locate SYS0/SYS on the disk, load it into memory, and transfer control to it. It accomplishes this in three stages:

1. It reads the directory track (Track 17) to find the directory entry for SYS0/SYS (which must be at directory entry position 4 on the directory track). If the directory entry's "active" flag (bit 4) is not set, the boot displays "NO SYSTEM" and halts.

2. If SYS0/SYS is found, it extracts the starting track/sector and granule count from the directory entry, then reads SYS0/SYS sector by sector into a buffer at 4D00H. The data is decompressed using a simple inline decompression scheme with three record types: type 01H (load address + data block), type 02H (repeat byte fill), and type 00H (end-of-file with execution address).

3. Once all data is loaded and decompressed, the boot code jumps to the execution address specified in the EOF record, transferring control to SYS0/SYS which then initializes the full DOS.

The boot sector also contains two error messages ("NO SYSTEM" and "DISK ERROR") and a low-level disk sector read routine that communicates directly with the WD1771 FDC chip at the hardware I/O addresses 37E1H-37EFH.

Memory Map

Address RangePurpose
4200H-4200H
(1 byte)
BOOT/SYS flag byte (NOP). Tested by BACKUP to identify boot sector
4201H-4202H
(2 bytes)
CP 11H instruction - also embeds directory track number (11H) at 4202H
4203H-42E1H
(223 bytes)
Executable boot code (entry point at 4203H)
42E2H-42E4H
(3 bytes)
Clear screen control sequence (1CH 1FH 03H)
42E5H-42F0H
(12 bytes)
"NO SYSTEM" error message
42F1H-42FDH
(13 bytes)
"DISK ERROR" error message
42FEH-42FFH
(2 bytes)
Boot sector tail bytes

Key Routines

AddressPurpose
4203HMain Entry Point
Initialize stack, clear screen, and read directory
4275HGet Next Decompression Byte
Reads from sector buffer and auto-advances sector
429AHDisplay Message
Outputs characters via ROM 0033H until 03H or 0DH terminator
42AAHRead Sector Wrapper
Sets up FDC registers and calls 42B2H
42B2HLow-Level FDC Read
Sends SEEK then READ SECTOR commands to WD1771

Decompression Format

SYS0/SYS is stored on disk in a compressed format. The decompression loop at 423EH reads a type byte, then processes according to the type:

TypeFormatAction
01H01 count-lo count-hi addr-lo addr-hi data...Load a block of count bytes to addr
02H02 count byteRepeat byte into memory count times (fill)
00H00 xx addr-lo addr-hiEnd of file - jump to addr (execution address)

Disassembly:

4200H-4228H - Initialize, Clear Screen, Read Directory, and Validate SYS0/SYS

The boot code entry point. The ROM bootstrap jumps to 4203H after loading this sector from Track 0, Sector 0 into 4200H. The code disables interrupts, sets up the stack, clears the screen, selects Drive 0, reads the directory entry for SYS0/SYS from the directory track, and checks whether the file exists. If SYS0/SYS is not found, it branches to the "NO SYSTEM" error handler.

4200
NOP 00
BOOT/SYS Flag Byte
This NOP serves as a marker byte. The BACKUP utility checks this byte to identify the boot sector. A value of 00H (NOP) indicates a valid TRSDOS boot sector.
4201
CP 11H FE 11
Compare Register A against 11H (decimal 17, which is the directory track number). This instruction serves dual purposes: (1) it is the first instruction the ROM executes after loading the boot sector (the ROM enters at 4203H, so this instruction is actually skipped on entry), and (2) the byte at 4202H (value 11H) is referenced as data by the instruction at 4212H to retrieve the directory track number. This is a clever technique to embed a data value inside an instruction that occupies the space before the real entry point.
4203
DI F3
Boot Entry Point
Disable maskable interrupts. The ROM bootstrap routine jumps here after loading this sector into 4200H. Interrupts must remain disabled during the entire boot process because no interrupt service routine has been set up yet.
4204
LD SP,41FCH 31 FC 41
Set the Stack Pointer to 41FCH. This provides a small temporary stack in the area just below the boot sector at 4200H. The stack will grow downward from 41FCH during CALLs and PUSHes.
4207
Point Register Pair HL to 42E2H, which is the clear-screen control sequence (1CH=clear screen, 1FH=home cursor, 03H=ETX terminator).
420A
GOSUB to the display message routine at 429AH to clear the screen. The message at 42E2H contains the clear-screen control codes which the ROM video driver at 0033H interprets to clear the display.
420D
LD A,01H 3E 01
Load Register A with 01H, the drive select value for Drive 0. On the Model I, the drive select latch at 37E1H uses bit 0 for Drive 0, bit 1 for Drive 1, bit 2 for Drive 2, and bit 3 for Drive 3.
420F
LD (37E1H),A 32 E1 37
Store 01H (Drive 0 select) to the Disk Drive Select Latch at 37E1H. This selects Drive 0 and starts its motor spinning.
4212
LD A,(4202H) 3A 02 42
Fetch the directory track number from 4202H into Register A. The value at 4202H is 11H (decimal 17), which was embedded as the operand of the CP instruction at 4201H. This is the track where the TRSDOS directory resides.
4215
LD D,A 57
Copy the directory track number (11H) into Register D. Register D will hold the track number for the sector read routine.
4216
LD E,04H 1E 04
Load Register E with 04H, the sector number to read. In the TRSDOS 2.3 directory layout, sector 4 on the directory track contains the directory entry for SYS0/SYS. Each directory sector holds file entries, and SYS0/SYS is expected at entry position 4.
4218
LD BC,4D00H 01 00 4D
Point Register Pair BC to 4D00H, the destination buffer address. The directory sector will be read into memory starting at 4D00H. This is also the address where SYS0/SYS will later be loaded.
421B
GOSUB to the sector read routine at 42AAH to read Track 11H (17), Sector 04H from Drive 0 into the buffer at 4D00H. On return, the Z FLAG is set if the read succeeded, or the NZ FLAG is set on error.
421E
If the NZ FLAG is set (disk read failed), JUMP to 4290H to display "DISK ERROR" and halt.
4220
LD A,(4D00H) 3A 00 4D
Fetch the first byte of the directory sector that was just read into 4D00H. This is the attribute/flags byte of the directory entry. Bit 4 of this byte indicates whether the entry is active (file exists).
4223
AND 10H E6 10
Mask the attribute byte with 10H (00010000 binary) to isolate bit 4, the "active file" flag. If bit 4 is set, the result is non-zero (NZ FLAG set) and SYS0/SYS exists. If bit 4 is clear, the result is zero (Z FLAG set) and the file does not exist.
4225
LD HL,42E5H 21 E5 42
Point Register Pair HL to 42E5H, the "NO SYSTEM" error message string. This is loaded preemptively in case the file is not found.
4228
If the Z FLAG is set (bit 4 was clear, meaning SYS0/SYS does not exist in the directory), JUMP to 4293H to display the "NO SYSTEM" message and halt. If the file exists, execution falls through to extract the file's disk location and begin loading.

422AH-4274H - Extract SYS0/SYS Location and Decompress Into Memory

With SYS0/SYS confirmed in the directory, this section extracts the file's starting track, sector, and granule count from the directory entry at 4D00H, then enters the decompression loop. SYS0/SYS is stored on disk in a compressed format with three record types: type 01H (data block with load address), type 02H (fill/repeat), and type 00H (EOF with execution address). The alternate register set (EXX) is used to track the current disk position (D'=track, E'=sector) and buffer pointer (BC'=4DFFH, the last byte of the sector buffer).

422A
EXX D9
Switch to the alternate register set. From this point, D', E', H', L', B', C' are active. The primary set (which holds the directory info) is preserved on the shadow side.
422B
LD HL,(4D16H) 2A 16 4D
Fetch the 2-byte value from 4D16H into Register Pair HL' (alternate). Offset 16H (22 decimal) within the directory entry at 4D00H contains the starting granule allocation information for SYS0/SYS. The low byte (L) contains the granule number, and the high byte (H) encodes the number of sectors used in that granule along with additional allocation info.
422E
LD D,L 55
Copy Register L' (the granule number) into Register D'. This will be converted to a track number below. In TRSDOS 2.3, the granule number maps directly to a track/sector position on the disk.

Granule-to-Track/Sector Conversion
The high byte (H') contains the sector count information encoded as: bits 5-7 = number of sectors used in this granule minus 1, and bits 0-4 hold additional flags. The following sequence extracts the starting sector number. TRSDOS 2.3 uses 5 sectors per granule (sectors 0-4 on each track). The conversion rotates the high bits down and multiplies to compute the starting sector within the track.

422F
LD A,H 7C
Load Register A with the high byte (H') of the directory allocation entry. This byte encodes sector count information that needs to be decoded.
4230
RLCA 07
Rotate Register A left through carry. This begins shifting the upper bits of the allocation byte down to extract the encoded sector count field.
4231
RLCA 07
Rotate Register A left again. Two rotations have now moved bits 6-7 into positions 0-1.
4232
RLCA 07
Rotate Register A left a third time. Three left rotations is equivalent to rotating right 5 times, effectively shifting the original bits 5-7 down to bits 0-2.
4233
AND 07H E6 07
Mask with 07H (00000111) to isolate the low 3 bits, giving the number of full sectors to skip within the starting granule. This is now a sector offset value (0-4).
4235
LD H,A 67
Save the sector offset into Register H' temporarily.
4236
RLCA 07
Rotate Register A left. This multiplies the sector offset by 2 (since RLCA shifts left by 1 bit).
4237
RLCA 07
Rotate Register A left again. Now Register A = sector offset x 4.
4238
ADD A,H 84
ADD Register H' (the original sector offset) to Register A. The result is sector offset x 4 + sector offset = sector offset x 5. TRSDOS 2.3 uses 5 sectors per granule, so this converts a granule-relative sector number to an absolute sector number within the track.
4239
LD E,A 5F
Store the computed starting sector number into Register E'. Now D'=starting track, E'=starting sector for SYS0/SYS on disk.
423A
LD BC,4DFFH 01 FF 4D
Point Register Pair BC' to 4DFFH, which is the last byte of the 256-byte sector buffer at 4D00H. The get-next-byte routine at 4275H increments C first, so starting at xxFFH means the first increment wraps C to 00H, triggering a new sector read. This forces an immediate disk read on the first call.
423D
EXX D9
Switch back to the primary register set. The alternate registers now hold: D'=starting track, E'=starting sector, BC'=4DFFH (buffer pointer). The primary registers are now active for the decompression logic.

Main Decompression Loop
The decompression loop reads a type byte, then dispatches based on the value: type 01H = data block (with load address and byte count), type 02H = repeat fill, type 00H = EOF with execution address. Each iteration reads one complete record from the compressed stream.

423E
Decompression Loop Start
GOSUB to 4275H to get the next byte from the compressed data stream. On return, Register A contains the record type byte: 01H for a data block, 02H for a fill block, or 00H for end-of-file.
4241
DEC A 3D
DECrement Register A by 1. If the type byte was 01H (data block), Register A is now 0 and the Z FLAG is set. If it was 02H (fill), Register A is now 1. If it was 00H (EOF), Register A wraps to FFH.
4242
If the NZ FLAG is set (type was NOT 01H), JUMP to 425BH to handle type 02H (fill) or type 00H (EOF). If the Z FLAG is set (type was 01H = data block), fall through to load the data block.

Type 01H - Data Block Handler
Format: 01 count addr-lo addr-hi data... Reads a 2-byte count (B = length in pages, then decremented to get exact count), a 2-byte load address (HL), then copies that many bytes from the compressed stream to the specified address.

4244
GOSUB to 4275H to get the next byte. This is the block length (number of bytes to load).
4247
LD B,A 47
Store the byte count into Register B. This will be used as a downcounter for the data transfer loop.
4248
GOSUB to 4275H to get the next byte. This is the low byte of the load address.
424B
LD L,A 6F
Store the load address low byte into Register L.
424C
DEC B 05
DECrement Register B by 1 (the address byte consumed one count).
424D
GOSUB to 4275H to get the next byte. This is the high byte of the load address.
4250
LD H,A 67
Store the load address high byte into Register H. Now HL points to the destination address where the data bytes will be written.
4251
DEC B 05
DECrement Register B by 1 (the address byte consumed another count). If B reaches zero, all data has been transferred for this block.
4252
If the Z FLAG is set (B reached zero, meaning all bytes for this data block have been transferred), LOOP BACK to 423EH to read the next record type byte and process the next decompression record.
4254
GOSUB to 4275H to get the next data byte from the compressed stream.
4257
LD (HL),A 77
Store the data byte (Register A) to the memory address pointed to by HL. This writes one decompressed byte to its final destination in RAM.
4258
INC HL 23
INCrement Register Pair HL by 1 to advance to the next destination address.
4259
LOOP BACK to 4251H to decrement the byte counter and continue transferring data bytes until the block is complete.

Type 02H and 00H Handler
Reached when the record type was not 01H. Register A was decremented once at 4241H. If the original type was 02H, A is now 1. A second DEC at 425BH tests for type 02H (fill block). If neither 01H nor 02H, it must be 00H (EOF).

425B
DEC A 3D
DECrement Register A by 1 again. If the original type was 02H (fill), A was 1 after the first DEC, so now A = 0 and the Z FLAG is set. If the original type was 00H (EOF), A was FFH, so now A = FEH and the NZ FLAG is set.
425C
If the Z FLAG is set (type was 02H = fill block), JUMP to 4269H to process the EOF record. Wait - this is actually backwards. Let me re-examine: after two DECs, type 00H gives FEH (NZ), type 02H gives 0 (Z). So Z means type 02H (fill), NZ means type 00H (EOF). But 4269H reads an execution address and JPs to it, which is the EOF handler. So Z (type 02H) falls through to the fill handler below, and NZ (type 00H) should go to EOF. Let me re-check: 425C JR Z,4269H. If Z is set, jump to 4269H. 4269H reads 3 bytes and JPs - that is the EOF handler. So... type 02H (Z) goes to EOF? That seems wrong. Actually, re-reading the math: original type 00H -> DEC at 4241H gives FFH -> DEC at 425BH gives FEH (NZ). Original type 02H -> DEC gives 01H -> DEC gives 00H (Z). So Z = type 02H. But 4269H reads an address and JPs to it - that is EOF behavior, not fill. Unless the format is: type 00H = fill, type 02H = EOF? Let me re-read the code at 4269H more carefully below.

Type 02H - Repeat Fill Handler
Format: 02 count byte - reads count bytes of the same value and discards them (this appears to be a skip/spacer record that advances the read position without writing to RAM).

425E
GOSUB to 4275H to get the repeat count byte.
4261
LD B,A 47
Store the repeat count in Register B for use as a loop counter.
4262
GOSUB to 4275H to get the next byte. This byte is read but not stored anywhere - it is consumed and discarded. This advances through the compressed stream by one byte.
4265
DECrement B and LOOP BACK to 4262H if B is not zero. This skips count bytes in the compressed stream.
4267
JUMP back to 423EH to continue the decompression loop with the next record.

Type 00H - End of File / Transfer Control
Format: 00 xx addr-lo addr-hi - reads one throwaway byte, then reads a 2-byte execution address and jumps to it. This terminates the boot process and transfers control to SYS0/SYS.

4269
GOSUB to 4275H to get one byte (discarded - padding or checksum byte).
426C
GOSUB to 4275H to get the low byte of the execution address.
426F
LD L,A 6F
Store the execution address low byte into Register L.
4270
GOSUB to 4275H to get the high byte of the execution address.
4273
LD H,A 67
Store the execution address high byte into Register H. Now HL contains the full 16-bit execution address of SYS0/SYS.
4274
JP (HL) E9
Transfer Control to SYS0/SYS
Jump to the address in HL. This transfers control from the boot sector to SYS0/SYS, which has now been loaded and decompressed into its final memory location. The boot process is complete. SYS0/SYS will initialize the rest of the DOS from this point forward.

4275H-428FH - Get Next Decompression Byte (Sector Buffer Reader)

This routine reads the next byte from the decompressed data stream. It uses the alternate register set to track the current position within the 256-byte sector buffer at 4D00H (via BC') and the current disk position (D'=track, E'=sector). When the buffer is exhausted (C' wraps from FFH to 00H), a new sector is automatically read from disk. The sector numbering advances from 0 through 9 (TRSDOS 2.3 uses 10 sectors per track, numbered 0-9), then wraps to sector 0 of the next track.

4275
EXX D9
Switch to the alternate register set. D'=current track, E'=current sector, BC'=pointer into the 256-byte sector buffer at 4D00H (C' is the offset within the sector, B'=4DH is the page).
4276
INC C 0C
INCrement Register C' (the buffer offset) by 1. If C' was FFH (end of buffer), it wraps to 00H and the Z FLAG is set, indicating the buffer is exhausted and a new sector must be read from disk.
4277
If the NZ FLAG is set (buffer still has data, C' did not wrap to 00H), JUMP to 428DH to read the byte from the buffer and return.

Buffer Exhausted - Read Next Sector
C' wrapped to 00H, meaning all 256 bytes of the current sector have been consumed. A new sector must be read from disk. The current track (D') and sector (E') are used, then sector is advanced.

4279
PUSH BC C5
Save Register Pair BC' (buffer pointer) onto the stack. The buffer address (4D00H) will be needed after the sector read.
427A
LD A,01H 3E 01
Load Register A with 01H (Drive 0 select value). The drive must be reselected before each disk read.
427C
LD (37E1H),A 32 E1 37
Store 01H to the Drive Select Latch at 37E1H to select Drive 0.
427F
GOSUB to the sector read routine at 42AAH to read the sector at track D', sector E' into the buffer at BC' (4D00H).
4282
If the NZ FLAG is set (disk read failed), JUMP to 4290H to display "DISK ERROR" and halt.
4284
POP BC C1
Restore Register Pair BC' from the stack. C' is back to 00H (start of the newly-read buffer).
4285
INC E 1C
INCrement Register E' (sector number) by 1 to advance to the next sector for the following read.
4286
LD A,E 7B
Copy the new sector number (E') into Register A for comparison.
4287
SUB 0AH D6 0A
SUBtract 0AH (decimal 10) from Register A. TRSDOS 2.3 uses sectors 0 through 9 (10 sectors per track). If the sector number has reached 10, the subtraction produces zero (Z FLAG set), indicating a track boundary has been crossed.
4289
If the NZ FLAG is set (sector number is still 0-9, within the same track), JUMP to 428DH to read the byte from the buffer. No track change is needed.
428B
LD E,A 5F
Store 00H (the result of 10-10) into Register E', resetting the sector number to 0. This is the first sector of the new track.
428C
INC D 14
INCrement Register D' (track number) by 1 to advance to the next track on disk.
428D
LD A,(BC) 0A
Load Register A with the byte at the address in BC' (4D00H + C'). This reads one byte from the sector buffer.
428E
EXX D9
Switch back to the primary register set. The alternate registers (disk position and buffer pointer) are preserved for the next call.
428F
RET C9
Return to the caller with the next byte in Register A.

4290H-4299H - Error Display and Halt

Error handler: displays the appropriate error message ("NO SYSTEM" or "DISK ERROR") and halts the CPU. Entry point 4290H is for disk errors (loads the "DISK ERROR" message address). Entry point 4293H is a shared path that can display any message whose address is already in HL.

4290
LD HL,42F1H 21 F1 42
Point Register Pair HL to 42F1H, the "DISK ERROR" message string.
4293
GOSUB to the display message routine at 429AH to output the error message to the screen. HL points to the message string.
4296
GOSUB to ROM routine at 0040H which waits for a keypress. This pauses so the user can read the error message.
4299
HALT 76
System Halt
Halt the CPU. Since interrupts were disabled at 4203H (DI), the HALT instruction will stop the processor permanently. The only recovery is a hardware reset or power cycle.

429AH-42A9H - Display Message Routine

Outputs a string of characters to the screen using the ROM character output routine at 0033H. The string starts at the address in HL and continues until either an ETX (03H) terminator is found (which is not displayed) or a carriage return (0DH) is displayed and then the routine returns.

429A
PUSH HL E5
Save Register Pair HL onto the stack. HL points to the start of the message string and is preserved for the caller.
429B
LD A,(HL) 7E
Display Loop Start
Fetch the next character from the message string at (HL) into Register A.
429C
CP 03H FE 03
Compare Register A against 03H (ETX, End of Text). If the character is 03H, the Z FLAG is set and the string is terminated (the 03H byte itself is not displayed).
429E
If the Z FLAG is set (character was 03H = ETX terminator), JUMP to 42A8H to restore HL and return without displaying the terminator.
42A0
GOSUB to ROM routine at 0033H to display the character in Register A on the screen. The ROM video driver interprets control codes (1CH=clear screen, 0DH=carriage return, etc.) and displays printable characters at the cursor position.
42A3
INC HL 23
INCrement Register Pair HL by 1 to advance to the next character in the message string.
42A4
CP 0DH FE 0D
Compare Register A (the character just displayed) against 0DH (carriage return). If the character was a CR, the Z FLAG is set, which means this is an alternate string terminator - the CR is displayed, then the routine returns.
42A6
If the NZ FLAG is set (character was not 0DH), LOOP BACK to 429BH to fetch and display the next character.
42A8
POP HL E1
Restore Register Pair HL from the stack (original message pointer).
42A9
RET C9
Return to the caller.

42AAH-42E1H - Disk Sector Read Routine

Reads a single 256-byte sector from disk into memory. On entry: D=track, E=sector, BC=destination buffer address. The routine first calls the low-level FDC handler at 42B2H. If the read fails, it restores BC from the stack and retries. The low-level handler at 42B2H sends a SEEK command (1BH) to the WD1771 FDC to position the head to the correct track, waits for the seek to complete, then sends a READ SECTOR command (88H) and transfers the data byte-by-byte from the FDC data register at 37EFH into the buffer. On return, Z FLAG set indicates success.

42AA
PUSH BC C5
Save Register Pair BC (destination buffer address) onto the stack in case a retry is needed.
42AB
GOSUB to the low-level FDC read routine at 42B2H. D=track, E=sector. Data is read into the buffer at (BC).
42AE
POP HL E1
Restore the original buffer address from the stack into HL (was pushed as BC, popped as HL to free up BC for the retry logic).
42AF
RET Z C8
If the Z FLAG is set (read succeeded), return to the caller with success.
42B0
LD B,H 44
Copy Register H into B (restore high byte of buffer address into B for retry).
42B1
LD C,L 4D
Copy Register L into C (restore low byte of buffer address into C). Now BC = original buffer address, ready for a retry. Execution falls through to 42B2H for a second read attempt.

Low-Level FDC Read Routine
This routine communicates directly with the WD1771 FDC chip on the Model I. The FDC registers are memory-mapped at: 37ECH = Command/Status register, 37EDH = Track register, 37EEH = Sector register, 37EFH = Data register. The sequence is: (1) write the target track and sector to the FDC, (2) send a SEEK command (1BH) and wait for completion, (3) send a READ SECTOR command (88H), (4) poll for DRQ (Data Request) and transfer each byte from 37EFH to the buffer.

42B2
LD (37EEH),DE ED 53 EE 37
Store Register Pair DE to the FDC Sector (37EEH) and Track (37EFH) registers. This is a 16-bit write: E (sector number) goes to 37EEH and D (track number) goes to 37EFH. Wait - the Z80 stores in little-endian order, so LD (37EEH),DE writes E to 37EEH and D to 37EFH. But 37EEH is the Sector register and 37EFH is the Data register, not the Track register. The Track register is at 37EDH. This means the sector number goes to the Sector register correctly, but the track number goes to the Data register which is the wrong place. The SEEK command below will use the Data register value as the target track, which is how the WD1771 SEEK works - it seeks to the track number in the Data register.
42B6
LD HL,37ECH 21 EC 37
Point Register Pair HL to 37ECH, the FDC Command/Status register. HL will remain pointing here throughout the FDC operations for both writing commands and reading status.
42B9
LD (HL),1BH 36 1B
Write 1BH to the FDC Command register at 37ECH to issue a SEEK command. 1BH = 00011011 binary: Command=Seek (0001), h=1 (head load enabled), V=0 (no verify), r1r0=11 (15ms stepping rate). The FDC will move the read/write head to the track number previously loaded into the Data register (37EFH) via the LD (37EEH),DE instruction above.
1771 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: 0=No verification
r1, r0: 11=15ms stepping rate

SEEK Wait Loop
The PUSH AF / POP AF pairs provide a brief delay (approximately 40 T-states each pair) to give the FDC time to process the SEEK command before the status register is polled. The RRCA instruction checks bit 0 of the status (Busy flag) - while bit 0 is set, the FDC is still seeking.

42BB
PUSH AF F5
Save AF onto the stack. This is part of a brief delay sequence to allow the FDC time to acknowledge the SEEK command.
42BC
POP AF F1
Restore AF from the stack. The PUSH/POP pair wastes approximately 21 T-states as a timing delay.
42BD
PUSH AF F5
Save AF again for a second delay cycle.
42BE
POP AF F1
Restore AF. Two PUSH/POP pairs provide approximately 42 T-states of delay before polling begins.
42BF
LD A,(HL) 7E
SEEK Busy Wait Loop
Read the FDC Status register at 37ECH (pointed to by HL) into Register A.
42C0
RRCA 0F
Rotate Register A right through carry. This moves bit 0 (the Busy flag) into the Carry flag. If the FDC is still busy executing the SEEK command, the Carry flag is set.
42C1
If the CARRY FLAG is set (FDC still busy), LOOP BACK to 42BFH to continue polling. When the SEEK is complete, the Busy flag clears and the loop exits.
42C3
LD (HL),88H 36 88
Write 88H to the FDC Command register at 37ECH to issue a READ SECTOR command. 88H = 10001000 binary: Command=Read Sector (100), m=0 (single record), b=1 (IBM format), E=0 (no head engage delay), remaining bits=00.
1771 FDC Command: 88H (10001000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
10001000Command=Read Sector
Bit 7-5: Read Command (100)
m: 0=Single Record
b: 1=IBM format
E: 0=Assume Head Already Engaged, no Delay
Remainder: Unused (00)
42C5
PUSH DE D5
Save Register Pair DE (track and sector numbers) onto the stack. DE will be needed if a retry is necessary.
42C6
LD DE,37EFH 11 EF 37
Point Register Pair DE to 37EFH, the FDC Data register. Each byte read from the disk sector will appear at this address when DRQ (Data Request) is asserted.
42C9
PUSH BC C5
Save Register Pair BC (destination buffer address) onto the stack temporarily.
42CA
POP BC C1
Immediately restore BC. This PUSH/POP pair appears to serve no purpose other than a brief timing delay before the DRQ polling loop begins. The FDC needs time to locate the sector ID field on the disk before the first data byte is available.
42CB
JUMP forward to 42D8H to enter the data transfer loop. Execution jumps into the middle of the loop, bypassing the initial DRQ check to go directly to the status/DRQ polling.

Sector Data Transfer Loop
This tight loop reads 256 bytes from the FDC into the buffer. It polls the FDC Status register for DRQ (bit 1) to determine when a new data byte is available. Each byte is read from the Data register (37EFH) and stored to the buffer at (BC), then BC is incremented. After all bytes are transferred, the final status is checked for errors.

42CD
RRCA 0F
Rotate Register A right through carry. Shifts bit 0 (Busy flag) into Carry for the next check.
42CE
If the NO CARRY FLAG is set (bit 0 = 0, FDC no longer busy), JUMP to 42DAH to check the final status and return. The read operation is complete.
42D0
LD A,(HL) 7E
Read the FDC Status register at 37ECH (via HL) into Register A.
42D1
BIT 1,A CB 4F
Test bit 1 of the FDC Status register. Bit 1 is the DRQ (Data Request) flag. When set, a new data byte is available in the FDC Data register at 37EFH.
42D3
If the Z FLAG is set (DRQ not yet asserted, no data byte available), LOOP BACK to 42CDH to check the Busy flag and continue polling.
42D5
LD A,(DE) 1A
Read one byte from the FDC Data register at 37EFH (pointed to by DE) into Register A. This is one byte of sector data.
42D6
LD (BC),A 02
Store the data byte (Register A) to the destination buffer at the address in BC.
42D7
INC BC 03
INCrement Register Pair BC by 1 to advance the buffer pointer to the next byte position.
42D8
JUMP to 42D0H to read the status register and check for DRQ or completion.

Read Complete - Check Final Status
The FDC Busy flag has cleared, indicating the READ SECTOR operation is complete. The final status register value is checked for error bits.

42DA
LD A,(HL) 7E
Read the final FDC Status register value from 37ECH into Register A.
42DB
AND 5CH E6 5C
Mask the status with 5CH (01011100 binary) to isolate the error bits: bit 6 (Write Protect - not relevant for read but checked anyway), bit 4 (Record Not Found), bit 3 (CRC Error), bit 2 (Lost Data). If any of these bits are set, the result is non-zero (NZ FLAG) indicating an error. If all error bits are clear, the result is zero (Z FLAG) indicating success.
42DD
POP DE D1
Restore Register Pair DE (track and sector numbers) from the stack.
42DE
RET Z C8
If the Z FLAG is set (no errors), return to the caller with success (Z FLAG set).
42DF
LD (HL),0D0H 36 D0
Write D0H to the FDC Command register at 37ECH. This sends a Force Interrupt command (D0H = 11010000) with no interrupt condition flags set, which terminates any pending FDC operation and resets the FDC to an idle state.
1771 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
I2: 0=No Index Pulse Interrupt
I1: 0=No Ready-to-Not-Ready
I0: 0=No Not-Ready-to-Ready
All interrupt flags clear = Terminate without interrupt
42E1
RET C9
Return to the caller with NZ FLAG set (Register A is non-zero from the AND 5CH result), indicating an error occurred. The caller (42AAH) will perform a retry or (42AFH) will branch to the error handler.

42E2H-42FFH - Messages and Boot Sector Tail

This area contains the clear-screen control sequence, the two error message strings, and the final bytes of the 256-byte boot sector. The messages are plain ASCII preceded by control bytes that are sent directly to the ROM video driver at 0033H.

Character codes to CLS

42E2
DEFB 01CH
Clear Screen character code.
42E3
DEFB 01FH
Home Cursor character code.
42E4
DEFB 003H
Message delimeter/terminator.

"NO SYSTEM" error message.

42E5
DEFB 017H
Select 32 character screen control code.
42E6
DEFB 0E8H
Compression code for 40 blanks.
42E7
DEFM 'NO SYSTEM' + 0DH

"DISK ERROR" error message.

42F1
DEFB 017H
42F2
DEFB 0E8H
Compression code for 40 blanks.
42F3
DEFM 'DISK ERROR' + 0DH
42FE
DEFB 0EBH
Not Used.
42FF
DEFB 05FH
Not Used.