Model I TRSDOS v2.3 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.

4200
NOP
BOOT/SYS flag. Used by BACKUP
4201
CP 11H
Compare Register A againg 11H (which is 17 Decimal, or the Directory track number). This is a sneaky way to put 11H into (4202H).
4203
DI
Disable interrupts so disk I/O is not interrupted
4204
LD SP,41FCH
Initialize stack pointer address to 41FCH.
4207
LD HL,42E2H
Point HL to the “CLS” equivalent in character codes at the bottom of this routine to display.
420A
Call 429AH to send the message pointed to by Register Pair HL to the video display.
Check to see if SYS0/SYS is on the diskette.
4200
LD A,01H
Put 01H into Register A in anticipation of the next command.
420F
LD (37E1H),A
Select drive 0. 37E1H is the Disk Drive Select Latch Address, and 01 is the value for Drive :0
4212
LD A,(4202H)
Put the directory track number (which is 11H) into Register A.
4215
LD D,A
Copy the directory track number into Register D
4216
LD E,04H
Put the SYS0/SYS directory entry number of 04H into Register E
4218
LD BC,4D00H
Set up 4D00H as a local buffer for the read by loading it into Register Pair BC.
421B
GOSUB to 42AAH to get SYS0/SYS dir entry (trk 11/sec 4) into the RAM Buffer from 4D00H-4DFFH
421E
If the NZ flag is set, then the CALL returned an error, so JUMP to 4290H to display the “DISK ERROR” message
To test to see if SYS0/SYS is there, we check the first byte of the SYS0/SYS directory entry for the “entry occupied” flag.
4220
LD A,(4D00H)
Get 1st byte of SYS0/SYS directory entry which is now held in RAM at 4D00H-
4223
AND 10H
AND 00010000
Isolate the “entry occupied” flag from Bit 4 (10H is 00010000 in Binary)
4225
LD HL,42E5H
Point HL to the “NO SYSTEM” message
4226
If the Z flag is set (i.e., no entry occupied flag was present) then JUMP to 42E5H to display 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.

422A
EXX
Swap to use the alternate register set since we have so many registers set up already for the read.
422B
LD HL,(4D16H)
Fetch the 17th byte from the buffer at 4D00H into Register Pair HL. That byte is the 1st GAP for SYS0/SYS and contains the track number for SYS0/SYS and the sector offset in bytes.
422E
LD D,L
D = track number for SYS0/SYS.
422F
LD A,H
A = byte count, sector offset.
4230
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).
4231
RLCA
4232
RLCA
4233
AND 07H
AND 00000111
AND the resulting rotated bit against 00000111 to keep only what used to be Bits 5, 6, and 7.
4235
LD H,A
Put that rotated byte (sector offset) …
4236
RLCA
… multiply it by 2.
4237
RLCA
… multiply it by 2 again (so now its x 4)
4236
ADD A,H
… add it one more time (so now its x 5) to give the first sector number (0/5).
4239
LD E,A
Put that starting sector number into Register E.
423A
LD BC,4DFFH
Put 4DFFH 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.
423D
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.

423E
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.
4241
DEC A
Reduce Register A by 1 to make it easy to check for a Zero.
4242
If A was a “1”, then we have load data, so jump to 425BH.
4244
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.
4247
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.
4248
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.
424B
LD L,A
Start populating Register Pair HL by putting the LSB from Register A into Register L.
424C
DEC B
Since we just read a byte, we have to decrement the number of bytes to read counter by 1.
424D
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.
4250
LD H,A
Finish populating Register Pair HL by putting the MSB from Register A into Register H.
4251
DEC B
Top of a loop. Since we just read a byte, we have to decrement the number of bytes to read counter by 1.
4252
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.
4254
If we are here, there are still more bytes to read so GOSUB to 4275H to read another byte.
4257
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.
4258
INC HL
Increase Register Pair HL by 1 so it points to the next empty byte.
4259
LOOP back to 4251H to keep reading in bytes and putting them into memory pointed to by Register Pair HL.
425B
DEC A
Decrement Register A by 1. This is to help test for code 2 (a transfer address flag).
425C
If that was a code 2, JUMP to 4269H.
425E
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.
4261
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.
4262
Top of a loop. GOSUB to 4275H to read another byte into Register A.
4265
LOOP back to 4262H until all bytes are discarded.
4267
JUMP back to 423EH to get the next control code.
4269
GOSUB to 4275H to read another byte into Register A. This first byte would traditionally be the “length” byte. We are going to skip this byte.
426C
GOSUB to 4275H to read another byte into Register A. In this case it is the LSB of the transfer address of SYS1/SYS.
426F
LD L,A
Start populating Register Pair HL by putting the LSB from Register A into Register L.
4270
GOSUB to 4275H to read another byte into Register A. In this case it is the MSB of the transfer address of SYS1/SYS.
4273
LD H,A
Finish populating Register Pair HL by putting the MSB from Register A into Register H.
4274
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. BC must point to the buffer holding the disk sector.

4275
EXX
Swap to use the alternate register set since we have so many registers set up already for the read.
4276
INC C
Bump buffer address to next byte.
4277
So long as Register C isn’t zero, then the sector buffer still has data. JUMP to 428DH in that case.
4279
PUSH BC
Save the sector buffer address to the stack.
427A
LD A,01H
Put an 01H into A. 01H is the unit select mask for drive :0.
427C
LD (37E1H),A
Put the unit select mask for :0 into the memory location 37E1H which is the Disk Drive Select Latch Address.
427F
Read one sector from the disk by CALLing 42AAH. This routine requires the track and sector number in Register Pair DE.
4262
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.
4284
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.
4265
INC E
Bump the sector number by 1.
4266
LD A,E
Store the sector number into Register A.
4287
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.
4289
If this was NOT the last sector on the track, JUMP down to 428DH.
428B
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 …
428C
INC D
… and then increase the track pointer by 1.
428D
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.
428E
EXX
Switch back to main register set.
428F
RET
RETURN from the routine.

4290H – Display the “DISK ERROR” message and Lock the System.

4290
LD HL,42F1H
Point HL to the address of the “DISK ERROR” error message at 42F1H.
4293
Call 429AH to display the message pointed to by Register Pair HL onto the video display.
4296
Call the ROM routine to wait for keyboard input.
4299
HALT
Stop the computer dead.

429AH – Display the message pointed to by Register Pair HL. Message must end with an 0DH or 03H delimeter.

429A
PUSH HL
HL contains the pointer to the message to be displayed, so save that address to the stack.
429B
LD A,(HL)
Top of a loop. Put the current character from the message into Register A.
429C
CP 03H
Compare the current character against the delimeter character of 03H.
429E
If the character was the delimeter character of 03H then JUMP to 42A6H.
42A0
If the character wasn’t the delimeter character of 03H then GOSUB to the display character routine in ROM at 0033H.
42A3
INC HL
Bump HL to point to the next character in the message to be displayed.
42A4
CP 0DH
Compare the current character against the delimeter character of 0DH.
42A6
If the current character is NOT the delimeter character of 0DH, LOOP back to 429BH.
42A8
POP HL
If we are here, then we hit an end of message delimeter, so restore HL to be the beginning of the message.
42A9
RET
RETURN from the GOSUB.

42AAH – Read a sector from the disk. D must hold the track, E must hold the sector, and BC must point to the start of the memory buffer to hold the sector data.

In this BOOT routine, this gets the SYS0/SYS dir entry. On entry, D has the directory track (11H), E has the sector (04H).
42AA
PUSH BC
Save Register Pair BC (the sector buffer address) to the stack.
42AB
GOSUB to 42B2H to read the sector pointed to in Register Pair DE.
42AE
POP HL
Put the sector buffer address into Register Pair HL.
42AF
RET Z
If there is no disk error, RETURN.
There is a disk error.
42B0
42B1
LD B,H
LD C,L
If there IS a disk error, load BC with HL to retry the operation.
42B2H
LD (37EEH),DE
Load Register Pair DE (which is holding the track/sector) into the DISK SECTOR SELECT memory location at 37EEH.
42B6
LD HL,37ECH
Load Register Pair HL with the Floppy Disk Controller Address status.
42B9
LD (HL),1BH
LD (HL),00011011
Put a 1BH (Decimal: 0001 1011) into the Floppy Disk Controller Status memory location. 1BH is the command for: SEEK (0001), load head at beginning (1), no verify (0), 20ms stepping rate (11).
42BB
PUSH AF
The next 4 instructions are a delay to give the controller time to respond to the command before we address it again.
42BC
POP AF
42BD
PUSH AF
42BE
POP AF
42BFH
LD A,(HL)
Top of a loop. Put the Floppy Disk Controller Address Status location contents into Register A.
42C0
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.
42C1
If the controller status is busy, then JUMP back 2 instructions to 42BFH to test it again.
42C3
LD (HL),88H
LD (HL),10001000
If we are here, then the controller is no longer busy, so put a 88H (Binary: 1000 1000) into the Floppy Disk Controller Status memory location. 88H is the READ SECTOR command as follows: Read Command (100), Single record (0), 1=IBM format (1), Assume head is engaged (0), Unused (00).
42C5
PUSH DE
Put Register Pair DE into the stack to save the current track/sector number.
42C6
LD DE,37EFH
Point Register Pair DE to 37EFH which is the memory address of the floppy disk controller’s data register.
42C9
PUSH BC
The next 2 commands are a delay to give the controller time to respond to the command before we address it again.
42CA
POP BC
42CB
JUMP forward to 42D6H to simulate a call to 4200H to get data byte (or OPERATION DONE status bit).

42CDH – This routine does not appear to be called in BOOT/SYS.

42CD
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.
42CE
If the controller status is NOT busy, then JUMP forward to 42DAH because the operation is done.
42D0H
LD A,(HL)
Top of a loop. Get the disk controller status and put it into Register A.
42D1
BIT 01H,A
Test Register A bit 1 (the DATA READY status bit).
42D3
If data is not available, then JUMP back to 42CDH.
42D5
LD A,(DE)
If we are here then data is available, so read a data byte from controller.
42D6
LD (BC),A
Store that data byte into the sector buffer pointed to by Register Pair BC.
42D7
INC BC
Bump Register Pair BC to the next available memory buffer location.
42D6
LOOP back to 42D0H until the controller is not busy.

42DAH – Jumped here if the operation is done and the controller status is NOT busy.

42DAH
LD A,(HL)
Get the disk controller status and put it into Register A.
42DB
AND 5CH
AND 01011100
AND the controller status against 5CH (Binary: 01011100) to isolate the error flags. This represents Unused (Bit 6), Record Not Found (Bit 4), CRC Error (Bit 3), and Lost Data (Bit 2)
4200
POP DE
Restore the track/sector address from the stack into Register Pair DE.
42DE
RET Z
If there was no error, then RETURN.
42DF
LD (HL),0D0H
If there WAS an error, then put a 0D0H (Decimal: 1101 0000) into the disk controller status to stop the controller with a force interrupt.
42El
RET
RETURN.

42E2H – Message Storage Location

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.