Model I TRSDOS BOOT/SYS Explained

This is a disassembly of TRSDOS v2.3’s BOOT/SYS file. The sole purpose of this 256 byte “program” is to check for SYS1/SYS and if its directory entry is found on Drive :0, Track 11, Directory Entry 4, it is found , load it into 4D00H and then jump to it. If SYS1/SYS is not present, it displays a NO SYSTEM message and halts. If at any point there is a disk read error, it displays a DISK ERROR message and halts.


4200H-4226H – Clear the screen and look to see if directory entry 4 on track 17 on drive 0 (which would be SYS1/SYS) has the “occupied” flag set. If no, jump to “NO SYSTEM” routine.

4200H
NOP
BOOT/SYS flag. Used by BACKUP
4201H
CP llH
Directory track number (11H is 17 Decimal). Note, this puts 11H into 4202H.
4203H
DI
Disable interrupts so disk I/O is not interrupted
4204H
LD SP,41FCH
Initialize stack pointer address (41FCH is 16892 Decimal).
4207H
LD HL,42E2H
Address of “CLS” equivalent in character codes at the bottom of this routine to display.
420AH
Call 429AH to issue a display-on-video command. HL must point to the message being displayed.
4200H
LD A,01H
Put 01H into Register A in anticipation of the next command.
420FH
LD (37E1H),A
Select drive 0. 37E1H is the Disk Drive Select Latch Address, and 01 is the value for Drive :0
4212H
LD A,(4202H)
Put the directory track number, which should be 11H, into Register A.
4215H
LD D,A
Put it in Register D
4216H
LD E,04H
Put SYS0/SYS directory entry number in Register E
4218H
LD BC,4D00H
Set up 4D00H as a local buffer for the read by loading it into Register Pair BC.
421BH
GOSUB to 42AAH to get SYS0/SYS dir entry (trk 11/sec 4)
421EH
Jump to 4290H to display the “DISK ERROR” message if there was an error during READ
4220H
LD A,(4D00H)
Get lst byte of SYS0/SYS directory entry
4223H
AND 10H
Isolate entry occupied flag from Bit 4 (10H is 00010000 in Binary)
4225H
LD HL,42E5H
Put the address of “NO SYSTEM” message into Register Pair HL
4226H
JR Z,4293H
If the Z flag is set (i.e., no entry occupied flag was present) then Jump to the “NO SYSTEM” message display routine.

422AH – Get the next byte from the sector buffer (at 4275H). Register pair DE holds the track and sector number for the next sector to be read. BC contains the buffer address, and is set to 4DFFH to signal that the sector buffer is empty.

422AH
EXX
Swap to use the alternate register set since we have so many registers set up already for the read.
422BH
LD HL,(4D16H)
Fetch 1st GAP for SYS0/SYS (i.e., 17H bytes into the sector storage buffer).
422EH
LD D,L
D = track number for SYS0/SYS.
422FH
LD A,H
A = byte count, sector offset.
4230H
RLCA
The next 3 commands push bit 5 (sector offset) into bit 0 for testing. The RLCA command moves every bit 1 potition to the left (so the contents of Bit 0 move to Bit 1, and the contents of Bit 7 move to the Carry Flag AND to Bit 0).
4231H
RLCA
4232H
RLCA
4233H
AND 07H
AND the resulting rotated bit against 00000111. It isolated Bit 5 and Bits 6 and 7 are now).
4235H
LD H,A
Put that rotated byte (sector offset) …
4236H
RLCA
… multiply it by 2.
4237H
RLCA
… multiply it by 2 again (so now its x 4)
4236H
ADD A,H
… add it one more time (so now its x 5) to give the first sector number (0/5).
4239H
LD E,A
Put that starting sector number into Register E.
423AH
LD BC,4DFFH
Put 4DFF into Register Pair BC (which would be the very very top of the sector buffer set up and pointed to by Register Pair BC) so set up to signal an empty buffer, which will force a read on next buffer access.
423DH
EXX
Switch back to main register set.

423EH – This is the main loop. It reads bytes from the disk to find the load address and transfer address for SYS1/SYS, and then JUMPs to that code.

423EH
CALL 4275H
GOSUB to 4275H to get next byte from sector buffer and put it into Register A. At this point, it should be a loader code.
4241H
DEC A
Reduce Register A by 1 to make it easy to check for a Zero.
4242H
JR NZ,425BH
If A was a “1”, then we have load data, so jump to 425BH.
4244H
If it wasn’t a “1”, then GOSUB to 4275H to read another byte into Register A. In this case, it is the number of bytes to load.
4247H
LD B,A
We are going to need a DJZN loop, so load the B Register with the number of bytes to load from Register A.
4248H
GOSUB to 4275H to read another byte into Register A. In this case, it is the LSB of load address of SYS1/SYS and put it into Register A.
424BH
LD L,A
Start populating Register Pair HL by putting the LSB from Register A into Register L.
424CH
DEC B
Since we just read a byte, we have to decrement the number of bytes to read counter by 1.
424DH
GOSUB to 4275H to read another byte into Register A. In this case, it is the MSB of load address of SYS1/SYS and put it into Register A.
4250H
LD H,A
Finish populating Register Pair HL by putting the MSB from Register A into Register H.
4251H
DEC B
Since we just read a byte, we have to decrement the number of bytes to read counter by 1.
4252H
JR Z,423EH
Make sure that there are still bytes to read by checking to see if that decrement turned the byte counter to 0. If yes, nothing left to read, and JUMP to 423EH to get the next control code.
4254H
If we are here, there are still more bytes to read so GOSUB to 4275H to read another byte.
4257H
LD (HL),A
Since Register Pair HL is the pointer to the code to run (and starts off with the load address), put that byte into the memory slot pointed to by Register Pair HL.
4258H
INC HL
Increase Register Pair HL by 1 so it points to the next empty byte.
4259H
JR 4251H
LOOP back to 4251H to keep reading in bytes and putting them into memory pointed to by Register Pair HL.
425BH
DEC A
Decrement Register A by 1. This is to help test for code 2 (a transfer address flag).
425CH
JR Z,4269H
If that was a code 2, JUMP to 4269H.
425EH
If we are here then that was NOT a code 2 (or code 1), don’t bother with it and GOSUB to 4275H to read another byte into Register A.
4261H
LD B,A
Register A should now hold the byte count to discard. Move it to Register B, because Register A is about to get used.
4262H
GOSUB to 4275H to read another byte into Register A.
4265H
DJNZ 4262H
LOOP back to 4262H until all bytes are discarded.
4267H
JUMP back to 423EH to get the next control code.
4269H
GOSUB to 4275H to read another byte into Register A. We are going to skip this byte.
426CH
GOSUB to 4275H to read another byte into Register A. In this case it is the LSB of the transfer address of SYS1/SYS.
426FH
LD L,A
Start populating Register Pair HL by putting the LSB from Register A into Register L.
4270H
GOSUB to 4275H to read another byte into Register A. In this case it is the MSB of the transfer address of SYS1/SYS.
4273H
LD H,A
Finish populating Register Pair HL by putting the MSB from Register A into Register H.
4274H
JP (HL)
JUMP to the transfer address of SYS1/SYS.

4275H – This is the routine to read a byte from the diskette and put it into Register A.

4275H
EXX
Swap to use the alternate register set since we have so many registers set up already for the read.
4276H
INC C
Bump buffer address to next byte.
4277H
JR NZ,428DH
So long as Register C isn’t zero, then the sector buffer still has data. JUMP to 428DH in that case.
4279H
PUSH BC
Save the sector buffer address to the stack.
427AH
LD A,01H
Put an 01H into A. 01H is the unit select mask for drive :0.
427CH
LD (37E1H),A
Put the unit select mask for :0 into the memory location 37E1H which is the Disk Drive Select Latch Address.
427FH
CALL 42AAH
GOSUB to 42AAH to read one sector from the disk. This routine requires the track and sector number in Register Pair DE.
4262H
JR NZ,4290H
If the routine at 42AAH routine returns NZ then there was a disk error, so JUMP to 4290H to display the error and halt the system.
4284H
POP BC
If we are here, then there was no error from the read. Restore Register Pair BC (containing the sector buffer address) from the stack.
4265H
INC E
Bump the sector number by 1.
4266H
LD A,E
Store the sector number into Register A.
4287H
SUB 0AH
Subtract 0AH (Decimal: 10, which is the number of sectors per track on a TRSDOS v2.3 disk) from Register A to test to see if the last sector of the current track has been read.
4289H
JR NZ,428DH
If this was NOT the last sector on the track, JUMP down to 428DH.
428BH
LD E,A
If we are here, then it WAS the last sector on the track, so we need to reset the current sector number pointer to 0 …
428CH
INC D
… and then increase the track pointer by 1.
428DH
LD A,(BC)
Put the next byte from buffer the buffer into Register A in preparation to exit the routine. Last byte if 428DH not jumped to by 4277H.
428EH
EXX
Switch back to main register set.
428FH
RET
RETURN from the routine.

4290H – Display the “DISK ERROR” message.

4290H
HL,42F1H
Point HL to the address of the “DISK ERROR” error message at 42F1H.
4293H
Call 429AH to issue a display-on-video command. HL must point to the message being displayed.
4296H
CALL 0040H
Call the ROM routine to wait for keyboard input.
4299H
HALT
Stop the computer dead.

429AH – Display a message. On entry HL points to the message to be displayed.

429AH
PUSH HL
HL contains the pointer to the message to be displayed, so save that address to the stack.
429BH
LD A,(HL)
Put the current character from the message into Register A.
.
429CH
CP 03H
Compare the current character against the delimeter character of 03H.
429EH
JR Z,42A6H
If the character was the delimeter character of 03H then JUMP to 42A6H.
42A0H
CALL 0033H
If the character wasn’t the delimeter character of 03H then GOSUB to the display character routine in ROM at 0033H.
42A3H
INC HL
Bump HL to point to the next character in the message to be displayed.
42A4H
CP 0DH
Compare the current character against the delimeter character of 0DH.
42A6H
If the current character is NOT the delimeter character of 0DH, JUMP back to 429BH.
42A8H
POP HL
If we are here, then we hit an end of message delimeter, so restore HL to be the beginning of the message.
42A9H
RET
RETURN from the GOSUB.

42AAH – Get the SYS0/SYS dir entry. On entry, D has the directory track (11H), E has the sector (04H), and BC points to the memory buffer to hold data.

42AAH
PUSH BC
Save Register Pair BC (the sector buffer address) to the stack.
42ABH
GOSUB to 42B2H to read the sector pointed to in Register Pair DE.
42AEH
POP HL
Put the sector buffer address into Register Pair HL.
42AFH
RET Z
If there is no disk error, RETURN.
42B0H
LD B,H
If there IS a disk error, load BC with HL to retry the operation.
42BlH
LD C,L
42B2H
LD (37EEH),DE
Load Register Pair DE (which is holding the track/sector) into the DISK SECTOR SELECT memory location at 37EEH.
42B6H
LD HL,37ECH
Load Register Pair HL with the Floppy Disk Controller Address status.
42B9H
LD (HL),lBH
Put a 1BH into the Floppy Disk Controller Status memory location. 1BH is the SEEK command.
42BBH
PUSH AF
The next 4 instructions are a delay to give the controller time to respond to the command before we address it again.
42BCH
POP AF
42BDH
PUSH AF
42BEH
POP AF
42BFH
LD A,(HL)
Put the Floppy Disk Controller Address Status location contents into Register A.
42C0H
RRCA
Rotate the results of the controller status right one bit so that the controller busy flag winds up in the Carry flag location for easy testing.
42ClH
If the controller status is busy, then JUMP back 2 instructions to 42BFH to test it again.
42C3H
LD (HL),88H
If we are here, then the controller is no longer busy, so put a 88H into the Floppy Disk Controller Status memory location. 88H is the READ SECTOR command.
42C5H
PUSH DE
Put Register Pair DE into the stack to save the current track/sector number.
42C6H
LD DE,37EFH
Load Register Pair with 37EFH which is the memory address of the floppy disk controller’s data register.
42C9H
PUSH BC
The next 2 commands are a delay to give the controller time to respond to the command before we address it again.
42CAH
POP BC
42CBH
JUMP forward to 42D6H to simulate a call to 4200H to get data byte (or OPERATION DONE status bit).
42CDH
RRCA
Rotate the results of the controller status right one bit so that the controller busy flag winds up in the Carry flag location for easy testing.
42CEH
If the controller status is NOT busy, then JUMP forward to 42DAH because the operation is done.
42D0H
LD A,(HL)
Get the disk controller status and put it into Register A.
42D1H
BIT 01H,A
Test Register A bit 1 (the DATA READY status bit).
42D3H
If data is not available, then JUMP back to 42CDH.
42D5H
LD A,(DE)
If we are here then data is available, so read a data byte from controller.
42D6H
LD (BC),A
Store that data byte into the sector buffer pointed to by Register Pair BC.
42D7H
INC BC
Bump Register Pair BC to the next available memory buffer location.
42D6H
LOOP back to 42D0H until the controller is not busy.
42DAH
LD A,(HL)
Get the disk controller status and put it into Register A.
42DBH
AND 5CH
AND the controller status against 5CH (Binary: 01011100) to isolate the error flags.
4200H
POP DE
Restore the track/sector address from the stack into Register Pair DE.
42DEH
RET Z
If there was no error, then RETURN.
42DFH
LD (HL),0D0H
If there WAS an error, then put a 0D0H into the disk controller status to stop the controller with a force interrupt.
42ElH
RET
RETURN.

42E2H – Message Storage Location

42E2H
DEFB 01CH
Clear Screen character code.
42E3H
DEFB 01FH
Home Cursor character code.
42E4H
DEFB 003H
Message delimeter/terminator.
42E5H
DEFB 017H
Select 32 character screen control code.
42E6H
DEFB 0E8H
Compression code for 40 blanks.
42E7H
DEFM ‘NO SYSTEM’
42F0H
DEFB 00DH
Message delimeter/terminator.
42FlH
DEFB 017H
42F2H
DEFB 0E8H
Compression code for 40 blanks.
42F3H
DEFM ‘DISK ERROR’
42FDH
DEFB 00DH
Message delimeter/terminator.
42FEH
DEFB 0EBH
Not Used.
42FFH
DEFB 05FH
Not Used.