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

Introduction/Summary:

General

The BOOT/SYS module is the initial loader for the NEWDOS/80 v2.0 Disk Operating System on the TRS-80 Model I. Its primary purpose is to load and execute the next stage of the DOS, SYS0/SYS, from the floppy disk into RAM.

The module, which starts execution at memory address 4200H, operates by efficiently using the Z80's alternate register set (BC', DE', HL') to manage two distinct sets of pointers: one for the Floppy Disk Controller (FDC) operations and one for the RAM buffer/target load location.

This boot module is basically two main routine, and Cliff Ide treated them as such by use of the EXX register swap. One routine uses HL to point to the Floppy Disk Controller memory locations and DE to point to the track and sector being worked on. The other routine uses HL to point to a RAM buffer which contains the sector data. In fact, Cliff uses the PUSH command only 3 times in this entire boot routine!

Another thing done here, more out of necessity than anything else, is to use the Model I RAM location for the Floppy Disk Controller access. The Model I didn't have ports so it couldn't be any other way (thanks to Eric Dittman for pointing this out), but it remains that by doing it this way only 1 opcode is needed to test and manipulate the controller, instead of 2.

These two things are a HUGE time saver.

Core Process:

Initialization (4200H - 4214H):

  • Disables interrupts (DI).
  • Initializes the Floppy Disk Controller (FDC) by setting the FDC Status/Command Register (37ECH) to use the 1771 single-density chip (for Percom Doublers) and then resets the FDC (0D0H).
  • Sets the FDC Track Register (37EDH) to Track 0 and the Sector Register (37EEH) to Sector 0.
  • Initializes the disk/sector pointer DE to 0005H, pointing to Track 0, Sector 5, the expected location of SYS0/SYS.
  • Swaps register sets (EXX) to prepare the main set (HL, DE, BC) for RAM buffer management.

RAM Buffer Setup (4215H - 4218H):

  • Sets the Stack Pointer (SP) to 41E0H.
  • Sets the RAM Buffer pointer HL to 51FFH, which prepares a 256-byte RAM buffer (5200H-52FFH) to hold sector data read from the disk.

Loading SYS0/SYS (421BH - 423EH):

  • The code proceeds in segments, reading an initial load byte, followed by the length and the starting address for the code segment.
  • It uses a loop to CALL 4252H (get next byte from RAM buffer) and then store the byte from the buffer (pointed to by HL) into the target RAM location (pointed to by DE).
  • If the RAM buffer pointer (L-register) rolls over (hits 00H), the code reads the next sector from the disk into the RAM buffer via a call to 4255H.
  • The loop continues until all segments of SYS0/SYS are loaded into memory.

Disk I/O / Sector Read (4255H - 42B8H):

  • The 4255H subroutine performs a full sector read.
  • It swaps registers back (EXX) to use the disk-related pointers.
  • It performs a disk drive select (425BH) and sets up a retry count of 10 (0AH).
  • It sends the FDC a SEEK command (1BH) to move to the track/sector specified in DE.
  • It sends the READ Sector command (88H) and then enters a long loop to poll the FDC status and transfer 256 bytes of data from the FDC Data Register (37EFH) into a separate sector data buffer starting at 5100H.
  • It increments the sector/track pointer (DE) for the next sector load and swaps registers back (EXX) before returning.

Execution or Error (4240H - 424BH, 424CH - 42C2H):

Once all sectors are loaded, the code checks the entry point address and verifies a signature byte (A5H) to confirm that SYS0/SYS was successfully loaded.

If successful (Z FLAG set), it RETurns (RET Z), which is the final step, transferring control to the loaded SYS0/SYS entry point (via the address saved on the stack).

If the disk is not a DOS disk ("NO SYS") or a disk error occurs, the code jumps to 42C3H to display the respective error message ("NO SYS" or "DISK ERROR") and enters an endless loop to freeze the system.

Variables:

AddressBytesPurpose
37E1H1DISK DRIVE SELECT LATCH ADDRESS. Used to select the drive (e.g., 01H for Drive :0) and control the drive lights (e.g., 09H for all lights on).
37ECH1Floppy Disk Controller Status/Command Register. Used for FDC status checks and sending commands like SEEK, READ, or RESET. Mirrors Port F0H.
37EDH1Floppy Disk Controller TRACK REGISTER. Used to specify the target track for FDC commands. Mirrors Port F1H.
37EEH1Floppy Disk Controller SECTOR REGISTER. Used to specify the target sector for FDC commands. Mirrors Port F2H.
37EFH1Floppy Disk Controller DATA Register. Used to read (input) or write (output) the actual sector data to/from the FDC. Mirrors Port F3H.
41E0HN/AInitial Stack Pointer (SP) location. The stack grows downward from this address.
5100H-51FFH256FDC Sector Data Buffer. A temporary buffer used by the 4255H routine to hold one entire sector (256 bytes) read directly from the floppy disk controller.
5200H-52FFH256RAM Buffer for Code Segments. This buffer is used to stage code segments loaded from the disk before they are moved to their final execution addresses.
42E5H7"NO SYS" Error Message data storage.
42DDH10"DISK ERROR" Message data storage.

Disassembly:

 
ORG 4200H
4200H
NOP
No Operation (Do Nothing).
4201H
FEH + 11H
DOS slang for: [FE] - Recognize this as a DOS disk and [11H] Directory track is on track 11H (Decimal: 17).
4203H
DI
Disable Interrupts.
4204H
LD HL,37ECH
Let Register Pair HL equal 37ECH, which is the RAM location for the Floppy Disk Controller Status/Command Register and mirrors Port F0H.
4207H
LD (HL),0FEH
Store the value 0FEH (Binary: 1111 1110) into the FDC. On its own, this would be an invalid command to send to the stock FDC. HOWEVER, as Blair Robbins has pointed out, this is a perfectly valid command to a Percom Doubler, and it sets the doubler to use the 1771 single density chip instead of the 1791 double density chip.
4209H
LD (HL),0D0H
Store the value 0D0H (Binary: 1101 0000) into the memory location pointed to by Register Pair HL, which resets the Floppy Disk Controller and puts it into INTRQ mode for NOT READY to READY transition.
420BH
INC HL
INCrement the value stored in Register Pair HL by 1 to now point to 37EDH, which is the memory location of the Floppy Disk Controller TRACK REGISTER (which would be Port F1H if they were using ports).
420CH
LD (HL),00H
Store the value 00H into memory location 37EDH (aka Port F1H) to set to TRACK 0.
420EH
INC HL
INCrement the value stored in Register Pair HL by 1 to now point to 37EEH, which is the memory location of the Floppy Disk Controller SECTOR REGISTER (which would be Port F2H if they were using ports).
420FH
LD (HL),00H
Store the value 00H into memory location 37EEH (aka Port F2H) to set to SECTOR 0.
4211H
LD DE,0005H
Let Register Pair DE equal 0005H in preparation of pointing to Track 0, Sector 5, which is the location of SYS0/SYS.
4214H
EXX
EXchanges the 16-bit contents of BC, DE, and HL with BC', DE', and HL'. Note: AF is not handled via EXX.
4215H
LD SP,41E0H
Move the Stack Pointer to 41E0H.
4218H
LD HL,51FFH
Let Register Pair HL equal 51FFH as a sector buffer in RAM from 5200H-52FFH. It had to be set 1 lower because the common 4252H fetch routine increments Register L as its first instruction.
421BH
GOSUB to 4252H to bump to the next byte of the RAM BUFFER and then fetch the byte into Register A.
421EH
CP 20H
Compare the byte read from disk (held in Register A) against 20H. If the byte was GREATER THAN 1FH the NC Flag would be set to signify an error.
4220H
LD B,A
Save the byte (held in Register A) in Register B which will act as a countdown for the DJNZ loop at 422CH.
4221H
If the byte was GREATER THAN 1FH then we know already that there is no DOS present, so JUMP to 424CH to display the "NO SYS" error message and freeze the system.
4223H
LD D,A
If we didn't jump away then we might still have a DOS! First, save the byte read into Register D.
4224H
GOSUB to 4252H to bump to the next byte of the RAM BUFFER and then fetch the byte into Register A.
4227H
LD C,A
Save the byte (held in Register A), which should be the number of bytes, in Register C.
4228H
GOSUB to 4252H to bump to the next byte of the RAM BUFFER and then fetch the byte into Register A.
422BH
LD E,A
Save the byte (held in Register A), which should be the MSB of the starting RAM location to load the code from DISK, in Register E.
422CH
JUMP to 4240H, reducing Register B each time, and continue to LOOP until Register B has been reduced to ZERO, in which case, continue with the next instruction.
422EH
GOSUB to 4252H to bump to the next byte of the RAM BUFFER and then fetch the byte into Register A.
4231H
LD D,A
Save the byte (held in Register A), which should be the MSB of the starting RAM location to load the code from DISK, in Register D.
4233H
DEC C
DECrement the value stored in Register C by 2 since the address was 2 bytes.
4234H
INC L
INCrement the BUFFER POINTER (held in HL) by 1.
4235H
If the BUFFER POINTER has just rolled over, then GOSUB to 4255H to read another sector into the RAM BUFFER.
4238H
LD A,(HL)
Fetch the value held in the BUFFER (pointed to by Register Pair HL) and store it into Register A.
4239H
LD (DE),A
Store that value into the memory location pointed to by Register Pair DE (which is the location in RAM for that code to be placed as set in DE from disk).
423AH
INC DE
INCrement the memory location pointed to by Register Pair DE (i.e., the memory location to put the bytes from diskette) by 1.
423BH
DEC C
DECrement the remaining number of bytes to read counter (stored in Register C) by 1.
423CH
If there are still bytes to read (meaning Register C did not yet hit 0), JUMP to 4234H.
423EH
If we read all the bytes we needed to read (and C is now 0), JUMP to 421BH.

4240H - Jump Point if the Byte was a 20H.

4240H
LOOP back to 423BH, reducing Register B each time, and continue to LOOP until Register B has been reduced to ZERO, in which case, continue with the next instruction.
4242H
GOSUB to 4252H to bump to the next byte of the RAM BUFFER and then fetch the byte into Register A.
4245H
LD D,A
Copy the byte just read (held in Register A), which should be the MSB of the entry point, into Register D so that DE now contains the entry point.
4246H
LD A,(DE)
Fetch the value held in the memory location pointed to by Register Pair DE and store it into Register A.
4247H
CP 0A5H
Compare the value held in Register A against A5H to see if SYS0/SYS is being read. If the byte matches and we are actually at SYS0/SYS, then the Z FLAG will be set.
4249H
INC DE
INCrement the value stored in Register Pair DE by 1 to skip past the A5H.
424AH
PUSH DE
Save the contents of Register Pair DE to the top of the stack.
424BH
RET Z
If the Z FLAG (Zero) has been set, then we are at SYS0/SYS, so RETurn. If we did not find SYS0/SYS then ...

424CH - Display the "NO SYS" Error Message.

424CH
LD HL,42E5H
Let Register Pair HL equal 42E5H to as to point to the "NO SYS" message.
424FH
JUMP to 42C3H to display the "NO SYS" message and then hang the system.

4252H - Fetch a byte from the RAM BUFFER.

4252H
INC L
INCrement the BUFFER POINTER (held in HL) by 1. On the very first run, this turns L to 00H which will permit a pass through at 4254H to load the first sector (and not RETurn).
4253H
LD A,(HL)
Fetch the value held in the RAM Buffer (pointed to by Register Pair HL) and store it into Register A.
4254H
RET NZ
If the NZ FLAG (Not Zero) has been set, RETurn to the caller.

4255H - Routine to read a whole sector into the RAM Buffer

4255H
EXX
EXchanges the 16-bit contents of BC, DE, and HL with BC', DE', and HL'. Among other things, this puts DE back to point to a TRACK and SECTOR, and HL back to pointing to the Floppy Disk Controller DATA BYTE.Note: AF is not handled via EXX.
4256H
LD B,0AH
Let Register B equal 0AH (Decimal: 10) for a retry count of 10 times.
4258H
LD HL,37E1H
Let Register Pair HL equal 37E1H, which is the DISK DRIVE SELECT LATCH ADDRESS.
425BH
LD (HL),01H
Store the value 01H into the drive select latch, which sets to DRIVE :0.
425DH
PUSH DE
Save the contents of Register Pair DE (i.e., the track and sector) to the top of the stack.
425EH
PUSH BC
Save the contents of Register Pair BC (i.e., the retry count) to the top of the stack.
425FH
LD A,E
Copy the contents of Register E (i.e., the sector) into Register A.
4260H
SUB 0AH
SUBtract the value 0AH (Decimal: 10) from Register A. If Register A was greater than 10, the CARRY FLAG will trip.
4262H
If the Carry Flag is set, then the sector number is too high! JUMP to 4267H.
4264H
LD E,A
Copy the SUBTRACTED FROM 10 contents of Register A into Register E.
4265H
LD (HL),09H
Store the value 09H (Binary:0000 1001) into the drive select latch, which turns on all drive lights.
4267H
LD HL,37ECH
Let Register Pair HL equal 37ECH, which is the RAM location for the Floppy Disk Controller.
426AH
GOSUB to 42CEH to wait until the Floppy Disk Controller signals READY.
426DH
LD (37EEH),DE
Store the value held in Register Pair DE into the FLOPPY DRIVE CONTROLLER SECTOR SELECT Register (i.e., 37EEH).
4271H
LD (HL),1BH
Store the value 1BH (Binary:0001 1011) into the Floppy Drive Controller. This is the command to SEEK (0001), Load Head at Beginning (1), No Verify (0), 30ms Stepping Rate (11)
1771 FDC Command: 1BH (0001 1011)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
0001hVr1r0Command=Seek
Bit 7-4: Command Code (0001)
h: 1=Enable Head Load/Settle, 0=No delay
V: 1=Verify Destination Track ID, 0=No verification
r1, r0: Stepping Motor Rate (00=3ms, 01=6ms, 10=10ms, 11=15ms)
4273H
GOSUB to 42CEH to wait until the Floppy Disk Controller signals READY.
4276H
LD (HL),88H
Store the value 88H (Binary: 1000 1000) into the Floppy Drive Controller. This is the command to READ (100), Single Record (0), Compare for Side 1 (1), No 15ms Delay (0), Disable Side Compare (00)
1771 FDC Command: 88H (1000 1000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
100mbE00Command=Read Sector
Bit 7-5: Read Command (100)
m: 1=Multiple Records, 0=Single Record
b: 1=IBM format, 0=Non-IBM Format
E: 1=Enable HLD, HLT, and 10ms Delay, 0=Assume Head Already Engaged, no Delay
Remainder: Unused (00)
4278H
LD DE,37EFH
Let Register Pair DE equal 37EFH (aka Port F3H) to point to the Floppy Disk Controller DATA Register.
427BH
LD BC,5100H
Let Register Pair BC equal 5100H to be a RAM BUFFER to hold the sector data.
427EH
GOSUB to 42D7H for a very short delay.
4281H
LD A,(HL)
Fetch the value from the Floppy Disk Controller Status Register and put the result into Register A.
4282H
AND 83H
MASK the value of Register A against 83H (1000 0011). This has the effect of leaving only bits 7 (1=NOT READY), 1 (1=Index Found), and 0 (1=Busy/Command in Progress) active.
4284H
If the PARITY/OVERFLOW FLAG has been RESET, LOOP back to 4281H and keep polling.
4287H
LD A,(DE)
Fetch a byte from the Floppy Disk Controller Data Register and store it into Register A.
4288H
LD (BC),A
Store the byte (now held in Register A) into the RAM BUFFER (pointed to by Register Pair BC).
4289H
INC BC
INCrement the RAM BUFFER pointer (stored in Register Pair BC) by 1.
428AH
BIT 1,(HL)
Test Bit Number 1 of the Floppy Disk Controller Status Register. Z FLAG will be set if that bit is 0 (meaning NO index mark), and NZ FLAG will be set if that bit is 1 (meaning, index mark found).
428CH
If the index mark was found, JUMP to 4287H.
428FH
BIT 1,(HL)
Check for the index mark a second time ... Z=Not Found, NZ=Found.
4291H
If the index mark was found, JUMP to 4287H.
4294H
BIT 1,(HL)
Check for the index mark a THIRD time ... Z=Not Found, NZ=Found.
4296H
If the index mark was found, JUMP to 4287H.
4298H
BIT 0,(HL)
So far, we have failed to find an index mark 3 times. Next, test Bit 0 of the Floppy Disk Controller Status Register. Z FLAG will be set if that bit is 0 (meaning NOT busy), and NZ FLAG will be set if that bit is 1 (meaning, BUSY).
429AH
If the drive is BUSY, JUMP to 42A4H.
429CH
BIT 1,(HL)
Check for the index mark again ... Z=Not Found, NZ=Found.
429EH
If the index mark was found, JUMP to 4287H.
42A0H
BIT 7,(HL)
Test Bit Number 7 of the Floppy Disk Controller Status Register. Z FLAG will be set if that bit is 0 (READY), and NZ FLAG will be set if that bit is 1 (NOT READY).
42A2H
If drive is READY, JUMP to 428AH.
42A4H
LD A,(HL)
Fetch the status from the Floppy Drive Controller Status Register and store it into Register A.
42A5H
LD (HL),0D0H
Store the value 0D0H (Binary: 1101 0000) into the Floppy Drive Controller Command Register, which resets the Floppy Disk Controller and puts it into INTRQ mode for NOT READY to READY transition
1771 FDC Command: D0H (1101 0000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
1101I3I2I1I0Command=Force Interrupt
Bit 7-4: Command Code (1101)
I3: 1=Interrupt Immediately, 0=No Interrupt Immediately
I2: 1=Interrupt on the next Index Pulse, 0=No Interrupt on the next Index Pulse
I1: 1=Interrupt the next time Ready goes to Not Ready, 0=No Interrupt the next time Ready goes to Not Ready
I0: 1=Interrupt the next time Not Ready goes to Ready, 0=No Interrupt the next time Not Ready goes to Ready
42A7H
POP BC
Put the value held at the top of the STACK (i.e., the retry count) into Register Pair BC, and then remove the entry from the stack.
42A8H
POP DE
Put the value held at the top of the STACK (i.e., the track and sector) into Register Pair DE, and then remove the entry from the stack.
42A9H
AND 0FCH
MASK the value of Register A (i.e., the status from the Floppy Disk Controller; read at 42A4H) against 0FCH (1111 1100). This has the effect of turning off bits 1 and 0. With that NZ will only be set if the following is true: The head is positioned to Track 0, there is a CRC error, there is a Seek error, the head is loaded, write protect is actived, or the drive is not ready.
42ABH
If any of that stuff is true then that's a bad thing ... JUMP to 42B9H to wait for the Floppy Drive Controller to be ready, send a RESET to the FDC, and continue a countdown of retries; failing to DISK ERROR if the retry count expires.
42ADH
INC E
INCrement the sector number (stored in Register E) by 1.
42AEH
LD A,E
Copy the new sector number (held in Register E) into Register A.
42AFH
SUB 0AH
SUBtract the value 0AH (Decimal: 10) from Register A. If Register A was less than 10, the NZ FLAG will be set.
42B1H
If the NZ FLAG (Not Zero) has been set, JUMP to 42B6H to skip over incrementing the track.
42B3H
INC D
INCrement the track (stored in Register D) by 1.
42B4H
LD E,00H
Let Register E equal 00H to reset to Sector 0 on that new track.
42B6H
EXX
EXchanges the 16-bit contents of BC, DE, and HL with BC', DE', and HL'. Note: AF is not handled via EXX.
42B7H
LD A,(HL)
Fetch the value held in the RAM BUFFER pointed to by Register Pair HL and store it into Register A.
42B8H
RET
RETurn to the caller.

42B9H - Wait for the Floppy Drive Controller to be ready, Send a RESET to the FDC, and continue a countdown of retries; failing to DISK ERROR if the retry count expires.

42B9H
GOSUB to 42D7H for a VERY short delay to wait for the Floppy Disk Controller to be ready.
42BCH
LD (HL),0BH
Store the value 0BH (Binary: 0000 1011) into the Floppy Disk Controller Command Register, which sends the command:
1771 FDC Command: 0BH (0000 1011)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
0000hVr1r0Command=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 (00=3ms, 01=6ms, 10=10ms, 11=15ms)
42BEH
LOOP back to 4258H, reducing Register B each time, and continue to LOOP until Register B has been reduced to ZERO, in which case, continue with the next instruction.
42C0H
LD HL,42DDH
If we fall through then we have an error, so let Register Pair HL equal 42DDH to point to the message "DISK ERROR".

42C3H - Display a Message (Pointed to by HL) and hang the system.

42C3H
LD A,(HL)
Fetch the value held in the memory location pointed to by Register Pair HL (which should be pointing to a message) and store it into Register A.
42C4H
CP 03H
Compare the value held in Register A against 03H, which is a standard End-of-Message delimeter. If Register A equals 03H/3, the Z FLAG is set; otherwise the NZ FLAG is set.
42C6H
If we are at the End-of-Message, LOOP back to 42C3H to freeze the system.
42C8H
INC HL
If we are NOT at the End-of-Message, INCrement the value stored in Register Pair HL by 1 to point to the next character in the message to display.
42C9H
GOSUB to 0033H.
NOTE: 0033H is the Model III ROM character print routine; displays the character held in Register A at the current cursor position.
42CCH
LOOP back to the top of this routine (i.e., 42C3H) to either keep displaying message letters or be in an endless loop.

42CEH - Wait until the Floppy Disk Controller signals READY.

42CEH
GOSUB to 42D7H for a VERY short delay to wait for the Floppy Disk Controller to be ready.
42D1H
BIT 0,(HL)
Test Bit Number 0 of the Floppy Disk Controller Status. Bit 0 is the BUSY bit. Z FLAG will be set if that bit is 0, and NZ FLAG will be set if the Floppy Disk Controller reported that a command is in progress (=1).
42D3H
If a command is in progress (and the NZ FLAG has been set as a result), JUMP BACK one instruction to 42D1H and test again.
42D5H
LD A,(HL)
Fetch the value held in the memory location pointed to by Register Pair HL and store it into Register A.
42D6H
RET
RETurn to the caller.

42D7H - Subroutine with a VERY short delay.

42D7H
LD A,06H
Let Register A equal 06H for a very short delay.
42D9H
DEC A
DECrement the value stored in Register A by 1.
42DAH
If the NZ FLAG (Not Zero) has been set, LOOP BACK to the prior instruction.
42DCH
RET
RETurn to the caller.

42DDH - MESSAGE STORAGE TABLE.

42DDH
DEFM ...
1C + 1F + "ERROR" + 03H
42E5H
DEFM ...
1C + 1F + "NO SYS" + 03H
 
END 4200H
That's it!