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 llH
Directory track number (11H is 17 Decimal). Note, this puts 11H into 4202H.
4203
DI
Disable interrupts so disk I/O is not interrupted
4204
LD SP,41FCH
Initialize stack pointer address (41FCH is 16892 Decimal).
4207
LD HL,42E2H
Address of “CLS” equivalent in character codes at the bottom of this routine to display.
420A
Call 429AH to issue a display-on-video command. HL must point to the message being displayed.
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 should be 11H, into Register A.
4215
LD D,A
Put it in Register D
4216
LD E,04H
Put SYS0/SYS directory entry number in 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)
421E
Jump to 4290H to display the “DISK ERROR” message if there was an error during READ
4220
LD A,(4D00H)
Get lst byte of SYS0/SYS directory entry
4223
AND 10H
Isolate entry occupied flag from Bit 4 (10H is 00010000 in Binary)
4225
LD HL,42E5H
Put the address of “NO SYSTEM” message into Register Pair HL
4226
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.

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 1st GAP for SYS0/SYS (i.e., 17H bytes into the sector storage buffer).
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 the resulting rotated bit against 00000111. It isolated Bit 5 and Bits 6 and 7 are now).
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 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.
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
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.
4241
DEC A
Reduce Register A by 1 to make it easy to check for a Zero.
4242
JR NZ,425BH
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
Since we just read a byte, we have to decrement the number of bytes to read counter by 1.
4252
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.
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
JR 4251H
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
JR Z,4269H
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
GOSUB to 4275H to read another byte into Register A.
4265
DJNZ 4262H
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. 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.

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
JR NZ,428DH
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
CALL 42AAH
GOSUB to 42AAH to read one sector from the disk. This routine requires the track and sector number in Register Pair DE.
4262
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.
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
JR NZ,428DH
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.

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

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

429A
PUSH HL
HL contains the pointer to the message to be displayed, so save that address to the stack.
429B
LD A,(HL)
Put the current character from the message into Register A.
.
429C
CP 03H
Compare the current character against the delimeter character of 03H.
429E
JR Z,42A6H
If the character was the delimeter character of 03H then JUMP to 42A6H.
42A0
CALL 0033H
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, JUMP 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 – 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.

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.
42B0
LD B,H
If there IS a disk error, load BC with HL to retry the operation.
42Bl
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.
42B6
LD HL,37ECH
Load Register Pair HL with the Floppy Disk Controller Address status.
42B9
LD (HL),lBH
Put a 1BH into the Floppy Disk Controller Status memory location. 1BH is the SEEK command.
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)
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.
42Cl
If the controller status is busy, then JUMP back 2 instructions to 42BFH to test it again.
42C3
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.
42C5
PUSH DE
Put Register Pair DE into the stack to save the current track/sector number.
42C6
LD DE,37EFH
Load Register Pair with 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
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)
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
LD A,(HL)
Get the disk controller status and put it into Register A.
42DB
AND 5CH
AND the controller status against 5CH (Binary: 01011100) to isolate the error flags.
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 into the disk controller status to stop the controller with a force interrupt.
42El
RET
RETURN.

42E2H – Message Storage Location

42E2
DEFB 01CH
Clear Screen character code.
42E3
DEFB 01FH
Home Cursor character code.
42E4
DEFB 003H
Message delimeter/terminator.
42E5
DEFB 017H
Select 32 character screen control code.
42E6
DEFB 0E8H
Compression code for 40 blanks.
42E7
DEFM ‘NO SYSTEM’
42F0
DEFB 00DH
Message delimeter/terminator.
42Fl
DEFB 017H
42F2
DEFB 0E8H
Compression code for 40 blanks.
42F3
DEFM ‘DISK ERROR’
42FD
DEFB 00DH
Message delimeter/terminator.
42FE
DEFB 0EBH
Not Used.
42FF
DEFB 05FH
Not Used.