Model 4 ROM Explained – XDROM – Diagnostic Part

3800H – This would be the “D” in XDROM – The diagnostic mode.

The way this is set up, it disassembled to 3800H-, but when involked, it relocates all of this code to 3000H-. With this, the below will be use the 3000 numbering system, as the JUMPs will make no sense otherwise.


3000-3014 – Copyright Text

3000
MESSAGE
“(c) 1989 M.A.D.” + 00h.
3010
NOP
NO OPERATION
3011
NOP
NO OPERATION
3012
NOP
NO OPERATION
3013
NOP
NO OPERATION
3014
NOP
NO OPERATION

3015-3037 – Initialization of the routine. Set the STACK, reset the Model 4 controls and set to 4Mhz, and program the CRTC

3015
LD SP,4100H
Let the STACK POINTER = 4100H.
3018
LD A,50H
LET A = 0101 0000.
301A
OUT (ECH),A
OUTput the value held in Register A to Port ECH to effectuate: Cassette: OFF, Character Width: NORMAL, Character Set: KANA, I/O Bus: Enabled, Video Waits: Disabled, CPU clock speed: 4Mhz.

NOTE: Port ECH is the Miscellaneous Controls port:
  • Bit 0: Supposedly not used, but is on the Model III to test to see if the clock is on [0=off, 1=on].
  • Bit 1: Cass motor [0 = on, 1 = off]
  • Bit 2: Double width [0 = normal, 1 = double]
  • Bit 3: Alt. char. [0 = Kana, 1 = Misc]
  • Bit 4: I/O bus [0 = disable, 1 = enable]
  • Bit 5: Video waits [0 = disable, 1 = enable]
  • Bit 6: CPU clock speed [0 = 2 mhz, 1 = 4 mhz] – Model 4 ONLY
  • Bit 7: not used
301C
IM 1
Set the INTERRUPT MODE to 1.
301E
XOR A
Zero register A and clear the flags.
301F
OUT (E4H),A
OUTput the value held in Register A to Port E4H.

NOTE: Port E4H is the non-maskable interrupt latch. When outputting 0000 0000 (i.e., Bit 6 disabled / Bit 7 enabled) to Port E4H, INTRQ Interrupt is ENABLED and DRQ Interrupt is DISABLED.
3021
LD (42FCH),A
Put a 0 into 42FCH.

These next instructions are to program the CRTC (15 bytes) and are setting up the variables for an OUTD command.

3024
LD A,0FH
Let A = 0F (Decimal: 15).
3026
LD HL,3407H
Let HL = 3407H.
3029
LD C,89H
Let C = 89H, which will be the port which will feed (HL) in an OUTD statement.
302B
OUT (88H),A
Send the value held in Register A (0FH) to Port 88H.
NOTE: Port 88H is the CRT Controller Control Register.
302D
OUTD
This command takes the byte held in the memory location pointed to by HL and writes it to Port C. HL and B are then both decremented.
302F
DEC A
Decrement the value held in Register A by 1.
3030
If the PARITY BIT is set, LOOP BACK to 302BH to continue OUTing. The Parity Bit won’t be set until A has been decremented to FFH.
3033
LD HL,3045H
If the PARITY BIT is not set, then point HL to 3045H in preparation for the next instruction (a JUMP).
3036
JUMP to 3082H to clear the screen.
3038
NOP
NO OPERATION.

3039 – This is jumped to only via interrupt.

3039
JUMP to 3629H which is an alternate entry to the routine at 362DH. Entering here will put the value at the top of the STACK into Register Pair BC, clear Register A and all flags, disable the DRQ and INTRQ interrupts and then continue to 362DH.

383CH – Display a “STATIC RAM BAD” message and then HALT the system.

303C
LD HL,3092H
In preparation for a LDIR block memory move, set HL (the source) to 3092H.
303F
LD BC,000EH
and set the BC (the number of bytes) to 0EH (14 bytes).
3042
LDIR
Moves the byte from (HL) to (DE), then HL and DE are incremented, and BC is decremented until BC is zero. Interrupts CAN fire during this command.
3044
HALT
Freeze the System.

3045 – This instruction is jumped to by 3091H. At this point, screen memory should be all 20H’s, so this routine is testing screen memory and then displays the on-screen menu.

3045
LD DE,3400H
Set DE to the first location on the screen.
3048
LD A,(DE)
Fetch the character at the memory location pointed to by (DE) into Register A. These should all be spaces if the screen clear routine in 3882H did its job.
3049
CP 20H
Compare Register A with a SPACE. If A is a SPACE, then the Z FLAG is set, otherwise the NZ FLAG is set.
304B
If the character is not a SPACE, then we have a problem! JUMP to 303CH to display the message “STATIC RAM BAD”.
304D
LD HL,30A0H
Prepare for a LDIR by loading HL (the source) to 30A0H (the copyright message and on-screen menu).
3050
LD BC,00E1H
and set BC to E1H to move 237 bytes from HL to DE. DE is pointing at the screen.
3053
LDIR
Moves the byte from (HL) to (DE), then HL and DE are incremented, and BC is decremented until BC is zero. Interrupts CAN fire during this command.
3055
LD D,B
LET DE=BC by first putting Register B into Register D. Note: DE was 3CE1H and BC is 0000H.
3056
LD E,C
… and then putting Register C into Register E.

3857H – MAIN MENU – This routine tests the keyboard for D, B, V, P, S and M keys, and jumps accordingly. Falls through to 3882H if those keys were not pressed.

3057
LD A,(3801H)
Fetch KEYBOARD ROW 0 (@ABCDEFG) from the keyboard and put the value of the keypress into Register A.
305A
BIT 4,A
Check to see if Bit 4 of Keyboard Row 0 is high. This would be the D key.
305C
LD HL,3322H
Put 3322H into Register Pair HL.
305F
If the key was a D (meaning Bit 4 was 1, which is NZ) jump to 3082H to clear the screen and then 3322H since that’s in HL.
3061
BIT 2,A
Check to see if Bit 4 of Keyboard Row 0 is high. This would be the B key.
3063
If the B key was pressed (meaning Bit 2 was 1, which is NZ) , JUMP to 3426H.
3066
LD A,(3804H)
Fetch KEYBOARD ROW 2 (PQRSTUVW) from the keyboard and put the value of the keypress into Register A.
3069
BIT 6,A
Check to see if Bit 6 of Keyboard Row 2 is high. This would be the V key.
306B
If the V key was pressed (meaning Bit 6 was 1, which is NZ) , JUMP to 3181H.
306E
BIT 0,A
Check to see if Bit 0 of Keyboard Row 2 is high. This would be the P key.
3070
If the P key was pressed (meaning Bit 0 was 1, which is NZ) , JUMP to 3408H.
3073
BIT 3,A
Check to see if Bit 3 of Keyboard Row 2 is high. This would be the S key.
3075
If the S key was pressed (meaning Bit 0 was 1, which is NZ) , JUMP to 365CH.
3078
LD A,(3802H)
Fetch KEYBOARD ROW 1 (HIJKLMNO) from the keyboard and put the value of the keypress into Register A.
307B
AND 20H
Mask Register A against 20H (Binary: 0010 0000) to keep only Bit 5. This would be the M key.
307D
JUMP to 3057H if the M key was pressed.
307F
LD HL,31C8H
Let HL = 31C8H, which will be the jump for M.

3082H – This routine fills the screen with spaces, backwards.

3082
EXX
Swap out BC (F089H), DE (0000H), and HL (3045H) with BC’, DE’, and HL’.
3083
LD HL,37FFH
Load HL with the memory address of the last screen position.
3086
LD BC,03FFH
Load BC with 3FFH (or the amount of bytes on the screen).
3089
LD D,H
Copy HL into DE in 2 steps – Copy H into D.
308A
LD E,L
Copy HL into DE in 2 steps – Copy L into E.
308B
LD (HL),20H
Put a space on the screen position pointed to by (HL).
308D
DEC DE
DE = DE – 1 (so DE is now 3FFEH).
308E
LDDR
Fill the screen with spaces via LDDR. LDDR transfers a byte of data from the memory location pointed to by HL to the memory location pointed to by DE. Then HL, DE, and BC are decremented. If BC is not zero, this operation is repeated. Interrupts can trigger while this instruction is processing.
3090
EXX
Swap out BC (0000H), DE (3BFFH), and HL (3C00H) with BC’ (F089H), DE’ (0000H), and HL’ (3045H).
3091
JP (HL)
Jump to the address in HL. HL is 31C8H if it falls through to this routine because no valid key was pressed. HL is 3322H (the middle of the cassette routine) if called from 385FH and is 3045H (the middle of Frank’s copyright text) if called from 3836H.

3092H – Message Storage Area for initial messages.

3092
MESSAGE
Static RAM Bad
30A0
MESSAGE
XDROM Diagnostics (c) 1989 M.A.D. software. All rights reserved.
30E0
MESSAGE
Version is
30EC
MESSAGE
1(1)
30F2
MESSAGE
1-Apr-89
30FC
MESSAGE
Select:(M|D|V|B|P|S) RESET to exit.M – Memory D – Disk V – Video B – Boot from Floppy Disk P – Printer S – Boot from RS232

3181H – MEMORY – Loop to keep testing memory location 31C1H until it is NOT a zero

3181
LD HL,31C1H
Let HL = 31C1H.

3184H – Loop to keep testing the memory location pointed to by Register Pair HL until it is NOT a zero

3184
LD A,(HL)
Load the value stored in the memory location pointed to by Register Pair HL into Register A.
3185
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
3186
If the value stored at (HL) is ZERO, LOOP back to 3181H.

If we are here, then the byte stored in the memory location pointed to by Register Pair HL was NOT zero.

3188
INC HL
Let HL = HL + 1.
3189
EXX
Swap out BC, DE, and HL with BC’, DE’, and HL’.
318A
GOSUB to 33D0H to fill Video Page 1 with the character held in Register A, swap to Video Page 0, and fills that with the character held in Register A, return.
318D
GOSUB to the next routine at 31A3H to rotate the bit stored at 3840H until a 0 is found, delay 255 x 10, rotate again until a 1 is found, and return here.
3190
LD A,04H
Let Register A = 0000 0011.
3192
OUT (84H),A
OUTput Register A to Port 84H, which will have the effect of saying: Model III ROMs Disabled, Video/Keyboard = Model 4, Reverse Video DISABLED, Lower 32K Ram in Bank 0, Upper 32K RAM in Bank 1, and Video Page Select 0.
NOTE: Port 84H handles miscellaneous memory items. Output:
  • Bits 0-1: Identify the Model of the Computer
  • Bit 2: Video Display Mode [0=64×16, 1=80×24]
  • Bit 3: Reverse Video
  • Bit 4-6: RAM Bank Select
  • Bit 7: Video Page Select [0=Page 0, 1=Page 1]
3194
LD HL,31C0H
Let HL = 31C0H.
3197
GOSUB to 33ECH with HL being 31C0H to program the CRT Controller Registers.
319A
GOSUB to 31A3H (which calls a RST 38H to Enable Interrupts).
319D
GOSUB to 33E9H to set HL to 3401H to program the CRT Controller Registers.
31A0
EXX
Swap out BC, DE, and HL with BC’, DE’, and HL’.
31A1
LOOP BACK to 3184H.

31A3H – MEMORY – This routine is called twice in the prior routine, once at 318D and once at 319A

31A3
LD HL,3840H
Let HL = 3840H.

The next sets of instructions pick up the byte at 3840H, rotate it until a bit 0 is found, and then delay 256 x 10 cycles.

31A6
LD BC,000AH
Let B = 00H and Let C = 0AH This sets up an internal loop of 256 via Register B and a larger loop of 10 via Register C.
31A9
LD A,(HL)
Fetch the value stored in the memory location pointed to by Register Pair HL into Register A.
31AA
RLCA
Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0).
31AB
If the current Bit 7 of Register A was a 1 (so now the CARRY FLAG is set), JUMP to 31A6H to rotate Register A’s bits and try again until a ZERO is found.
31AD
DJNZ 31ADH
Do a delay by LOOPing back to this instruction until Register B = 0.
31AF
DEC C
Decrement the value held in Register C by 1.
31B0
If Register C has not yet hit ZERO, LOOP back to 31ADH (to run a delay loop of 256, 10 times).

Once the delay loop ends, we pick up here to keep torturing that poor location.

31B2
LD A,(HL)
Fetch the value stored in the memory location pointed to by Register Pair HL into Register A.
31B3
RLCA
Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0).
31B4
RET C
If the current Bit 7 of Register A was a 1 (so now the CARRY FLAG is set), RETurn.
31B5
If the current Bit 7 of Register A was a 0, JUMP to 31B2H to keep rotating.

31B7H – This table is moved to the CRT Controller Control Registers in the 33E9H if called from the DISKETTE TEST routine.

31B7
63H
31B8
50H
31B9
55H
31BA
0AH
31BB
19H
31BC
04H
31BD
18H
31BE
18H
31BF
00H
31C0
09H
31C1
45H
31C2
5FH
31C3
7CH
31C4
2BH
31C5
BAH
31C6
BFH
31C7
00H

31C8H – MEMORY – This is the MEMORY TEST routine start.

31C8
XOR A
Zero register A and clear the flags.
31C9
LD (3C2FH),A
I guess Frank is using 3C2FH for something!.
31CC
LD SP,3FFEH
Set the STACK POINTER to 3FFEH.
31CF
LD HL,3261H
Let HL = 3261H to point to the “64K Memory Test Pass=” + 00h message.
31D2
GOSUB to 345EH display the messages pointed to by HL at the screen location of DE.

31D5H – MEMORY – Start of the memory test.

31D5
LD HL,3C16H
Let HL = 3C16H (an address left of middle on the first line of the screen).
31D8
LD BC,3C2FH
Let BC = 3C2FH (an address about 3/4 of the way to the right on the first line of the screen).
31DB
LD A,(BC)
Fetch the character from the screen pointed to by Register Pair BC and put it into Register A.
31DC
INC A
LET A = A + 1 to increment the ASCII value of that character by 1.
31DD
LD (BC),A
Replace the character on screen with the character value + 1.
31DE
GOSUB to 330CH, which does some SERIOUS byte manipulation on the character held in Register A to turn it into an ASCII HEX Digit and display it at (HL), and then BUMP HL.
31E1
LD IX,3C30H
LET IX = 3C30H (a screen location on the top line of the screen).
31E5
LD HL,8000H
LET HL = 8000H.
31E8
GOSUB to 32B7H to write memory locations 8000H-FFFFH out and then read them back.
31EB
LD A,(IX+00H)
Fetch the value stored at IX+00H and put it into Register A.
31EE
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
31EF
If the value held at 3C30H is NOT 00H then JUMP to 3215H.

If we are here, then we are going to move the RAM test to 8100H and JUMP to it there.

31F1
LD IX,FC30H
Let IX = FC30H.
31F5
LD DE,8100H
Let DE = 8100H.
31F8
LD BC,0059H
Let BC = 59H (or 89 bytes).
31FB
LD HL,32B3H
Let HL = 32B3H.
31FE
LDIR
Move the byte from (HL) to (DE), then HL and DE are incremented, and BC is decremented until BC is zero. Interrupts CAN fire during this command.
3200
LD H,B
LET Register H = Register B (which is 0).
3201
LD L,C
LET Register L = Register C (which is 0).
3202
LD SP,FFFEH
Set the STACK POINTER to top of memory.
3205
GOSUB to 8100H (the relocated 32B3H routine). Note: Entering the memory test at 32B3 sets the RAM Bank Select at: Lower 32K Ram in Bank 0 / Upper 32K RAM in Bank 2.
3208
LD SP,37FEH
Set the STACK POINTER to 37FEH.
320B
LD IX,3C30H
Let IX = 3C30H.
320F
LD A,(IX+00H)
Fetch the contents of the memory location pointed to by IX+00H into Register A.
3212
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
3213
If the memory location pointed to by IX+00H into Register A was ZERO, JUMP to 31D5H to do another pass.

If the memory location pointed to by IX+00H into Register A was NOT zero then we have a problem, so we need to display the memory test failure messages and addresses.

3215
LD DE,3C80H
Let DE = 3C80H, the first character on the 3rd line of the video screen.
3218
LD HL,3278H
Let HL = 3278H to point to a message.
321B
GOSUB to 345EH display the messages pointed to by HL at the screen location of DE, in this case “Intr Errors= Hard Errors= Last Error @” + 00h.
321E
LD DE,3D00H
Set DE to be the 1st character on the 5th line of the screen.
3221
INC HL
Let HL = HL + 1 (to point to the next error message).
3222
GOSUB to 345EH display the messages pointed to by HL at the screen location of DE, in this case “Failing Rows:”,00h.
3225
PUSH IX
Save register pair IX to the top of the STACK.
3227
POP BC
Move the value at the top of the STACK (which is the value stored in Register IX, i.e., 3C30H) to Register Pair BC.
3228
LD HL,3C8CH
Set HL to be the 13th character on the 3rd line of the screen. This would the screen location to put the first byte of the 2 byte number of “Intr Errors”.
322B
LD A,(BC)
Load the value stored in the memory location pointed to by Register Pair BC (the first byte of the 2 byte number of “Intr Errors” to report) into Register A.
322C
INC BC
Bump BC to point to the second byte of the 2 byte number of “Intr Errors”.
322D
GOSUB to 330CH, which does some SERIOUS byte manipulation on the character held in Register A to turn it into an ASCII HEX Digit and display it at (HL), and then BUMP HL.
3230
LD HL,3C9CH
Set HL to be the 29th character on the 3rd line of the screen. This would the screen location to put the first byte of the 2 byte number of “Hard Errors”.
3233
LD A,(BC)
Load the value stored in the memory location pointed to by Register Pair BC (the first byte of the 2 byte number of “Hard Errors” to report) into Register A.
3234
INC BC
Bump BC to point to the screen location to put the second byte of the 2 byte number of “Hard Errors”.
3235
GOSUB to 330CH, which does some SERIOUS byte manipulation on the character held in Register A to turn it into an ASCII HEX Digit and display it at (HL), and then BUMP HL.
3238
LD HL,3CADH
Set HL to be the 46th character on the 3rd line of the screen. This would the screen location to put the first byte of the 4 byte number of “Last Error @”.
323B
LD A,(BC)
Load the value stored in the memory location pointed to by Register Pair BC into Register A.
323C
INC BC
Bump BC to the screen location to put the second byte of the 4 byte number of “Last Error @”.
323D
GOSUB to 330CH, which does some SERIOUS byte manipulation on the character held in Register A to turn it into an ASCII HEX Digit and display it at (HL), and then BUMP HL.
3240
LD A,(BC)
Load the value stored in the memory location pointed to by Register Pair BC into Register A.
3241
INC BC
Bump BC to the screen location to put the third byte of the 4 byte number of “Last Error @”.
3242
GOSUB to 330CH, which does some SERIOUS byte manipulation on the character held in Register A to turn it into an ASCII HEX Digit and display it at (HL), and then BUMP HL.
3245
LD HL,32A5H
Let HL = 32A5H to point to the message “Failing Rows:”,00h.
3248
LD DE,3D00H
Set DE to be the 1st character on the 5th line of the screen.
324B
GOSUB to 345EH display the messages pointed to by HL at the screen location of DE, in this case “Failing Rows:”,00h.

Now we will point to the 15th character on Row 5, right after the “Failing Rows:” message, to populate 8 single number rows (7 to 0). The byte at Register Pair BC will be the binary representation of the rows which failed.

324E
LD A,(BC)
Load the value stored in the memory location pointed to by Register Pair BC into Register A.
324F
LD B,08H
Let B = 08.

Start of a loop of 08 cycles.

3251
RLCA
Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0).
3252
If Bit 7 was a 0, meaning that that bit does not represent a FAILED ROW, move to the next cycle of the loop as we are not going to display anything (since it didn’t fail).
3254
EX AF,AF’
Preserve the rotated Register A by EXchanging the values of AF and AF’.
3255
LD A,B
Let Register A = Register B (i.e., where we are in the loop of 8).
3256
ADD 2FH
ADD 2FH to Register A. 2F is one character below a 0 so this would turn a number into the ASCII equivalent of that number.
3258
LD (DE),A
Put the ASCII value of B onto the screen in the FAILING ROWS list.
3259
EX AF,AF’
Restore Register A by EXchanging (back) the values of AF and AF’.
325A
INC DE
Bump DE to the next space on the video display (so it doesn’t overwrite the just displayed number).
325B
INC DE
Bump DE to the next space on the video display (so as to put a space between the displayed numbers).
325C
Loop back to 3251H and keep looping until Register B = 0 (i.e., all 8 bits of the byte holding the failing rows have been displayed or skipped).
325E
JUMP to 344FH to disable the interrupts and HALT.

3261H – MEMORY – Message Storage Area.

3261
MESSAGE
64K Memory Test Pass=+00h
3278
MESSAGE
Intr Errors= Hard Errors= Last Error @ + 00h
32A5
MESSAGE
Failing Rows: + 00h

32B3H – MEMORY – An alternate way of entry to the memory test but with setting Lower 32K Ram in Bank 0 / Upper 32K RAM in Bank 2.

32B3
LD A,20H
Let A = Binary 0010 0000.
32B5
OUT (84H),A
OUTput Register A to Port 84H, which will have the effect of saying: Model III ROMs Enabled, Video/Keyboard are Model III, Video Mode 64×16, Lower 32K Ram in Bank 0 / Upper 32K RAM in Bank 2, and Video Page Select 0.
NOTE: Port 84H handles miscellaneous memory items. Output:
  • Bits 0-1: Identify the Model of the Computer
  • Bit 2: Video Display Mode [0=64×16, 1=80×24]
  • Bit 3: Reverse Video
  • Bit 4-6: RAM Bank Select
  • Bit 7: Video Page Select [0=Page 0, 1=Page 1]

32B7H – MEMORY – What the memory test does, at least on the initial passes is to load up memory with 00H-FF, test it, decrement each memory location 1, test it, etc. So, for example, a row of RAM might have 43H-52H, and on the next pass, that row will have 42H-51H, and then 41H-50H.

32B7
PUSH IX
Save register pair IX to the STACK. On FIRST call, this will put 3C30H at the top of the STACK.
32B9
POP BC
Move the value at the top of the STACK (which was IX) to Register Pair BC. On FIRST call, this will put 3C30H into BC.
32BA
LD D,05H
Let D = 05H.
32BC
XOR A
Zero register A and clear the flags.
32BD
LD (BC),A
(Beginning of a Loop). Put the contents of Register A into the memory location pointed to by Register Pair BC. On first call, this will put a 00H into 3C30H.
32BE
INC BC
Bump BC to point to the next location on the screen.
32BF
DEC D
Decrement the value held in Register D by 1 (for a count of 6).
32C0
LOOP back to 32BDH to keep loading 6 characters, starting at 3C30H with 00H.

If we are here, the screen locations 3C30H-3C35H have had 00H put into them.

32C2
LD B,FFH
LET Register B = FFH.

32C4H – Write from 8000H and then read it back.

32C4
PUSH BC
(This is the start of a loop, with loopback at 32E3H) Save the contents of Register Pair BC to the top of the STACK.
32C5
PUSH HL
Save the contents of Register Pair HL to the top of the STACK.
32C6
LD D,B
Save Register B into Register D.
32C7
LD BC,8000H
Let Register BC = 8000H.
32CA
PUSH BC
Save the contents of Register Pair BC to the top of the STACK.
32CB
LD (HL),D
(Start of a loop until BC = 0). Put the contents of Register D into the memory buffer set up at 8000H.
32CC
INC D
Let D = D + 1 (on first run, 00H).
32CD
INC HL
Let HL = HL + 1 (on first run, 8001H).
32CE
DEC BC
Decrement the value held in Register Pair BC by 1 (on first run, 7FFFH).
32CF
LD A,B
OR C
Since the Z-80 cannot test BC against Zero, it is a common trick to LD the MSB into A and then OR the LSB. Only if MSB and LSB are both 0 will Register A be 0.
32D1
If BC is still not ZERO, JUMP BACK to 32CBH.

If we are here, 8000H and higher (HL) have been filled with numbers and BC (8000H and lower) hit zero.

32D3
POP BC
Move the value at the top of the STACK (which is 8000H) to Register Pair BC.
32D4
POP HL
Move the value at the top of the STACK (which is 8000H) to Register Pair HL.
32D5
PUSH HL
Save the contents of Register Pair HL (which is 8000H) to the STACK.
32D6
LD A,(HL)
(Start of a loop). Load the value stored in the memory location pointed to by Register Pair HL into Register A.
32D7
CP D
Compare Register A with what’s in Register D. If Register A equals Register D, the Z FLAG is set; otherwise, the NZ flag will be set.
32D8
If there is no match, JUMP to 32EDH.
32DA
DEC BC
Decrement the value held in Register Pair BC by 1 (on first run, 7FFFH).
32DB
INC HL
Let HL = HL + 1 (on first run, 8001H).
32DC
INC D
Let D = D + 1 (on first run, 00H).
32DD
LD A,B
OR C
Since the Z-80 cannot test BC against Zero, it is a common trick to LD the MSB into A and then OR the LSB. Only if MSB and LSB are both 0 will Register A be 0.
32DF
If BC is still not ZERO, JUMP BACK to 32D6H.

If we are here, then BC hit 00H, as did HL.

32E1
POP HL
Move the value at the top of the STACK (i.e. 8000H) to Register Pair HL.
32E2
POP BC
Move the value at the top of the STACK to Register Pair BC.
32E3
Loop back to 3AC4H and keep looping until Register B = 0.
32E5
Skip the next 2 instructions and JUMP to 32E9H.

32E7H – MEMORY – Restore HL and BC from the STACK

32E7
POP HL
Move the value at the top of the STACK to Register Pair HL.
32E8
POP BC
Move the value at the top of the STACK to Register Pair BC.

32E9H – Clear A, reset the TRS-80, RETurn (to 31EBH)

32E9
XOR A
Zero register A and clear the flags.
32EA
OUT (84H),A
OUTput Register A to Port 84H, which will have the effect of saying: Model III ROMs Enabled, Video/Keyboard are Model III, Video Mode 64×16, Lower 32K RAM in Bank 0 and Upper 32K RAM in Bank 1, and Video Page Select 0.
NOTE: Port 84H handles miscellaneous memory items. Output:
  • Bits 0-1: Identify the Model of the Computer
  • Bit 2: Video Display Mode [0=64×16, 1=80×24]
  • Bit 3: Reverse Video
  • Bit 4-6: RAM Bank Select
  • Bit 7: Video Page Select [0=Page 0, 1=Page 1]
32EC
RET
Return(to 31EBH).

32EDH – MEMORY – This routine is JUMPed to from 32D8H if the 8000H readback failed. It put gibberish onto the screen based on the value of the failed read.

32ED
XOR D
Bitwise XOR on A with D. .
32EE
OR (IX+04H)
IX should be 3C30H, so IX+04H = 3C34H or the 54th character on line 1 of the video screen. This instruction takes the byte that is already there and, OR’s it with the XOR’d A.
32F1
LD (IX+04H),A
Put that manipulated byte back on the screen.
32F4
LD A,(IX+00H)
Put the character appearing on the screen at IX+00H into Register A.
32F7
INC A
LET A = A + 1.
32F8
If A hits FFH, JUMP to 32E7H to restore all Register Pairs from the STACK, reset the Model III, and RETURN.
32FA
LD (IX+00H),A
Put the value held in Register A onto the screen at screen location IX+00H.
32FD
LD A,(HL)
Fetch the Load the value stored in the memory location pointed to by Register Pair HL into Register A.
32FE
CP D
Compare Register A (the value from the memory location pointed to by HL) with the value held in Register D. If Register A equals the value held in Register D, the Z FLAG is set.
32FF
If the value in Register A equals the value held in Register D, skip the next instruction (i.e., JUMP to 3304H).
3301
INC (IX+01H)
If the value in Register A does NOT equal the value held in Register D, BUMP the value stored on the screen at memory location IX+01H.
3304
LD (IX+00H),L
Put the value held in Register L onto the screen at screen location IX+00H.
3306
INC BC
LET BC = BC + 1.
3307
LD (IX+00H),H
Put the value held in Register H onto the screen at screen location IX+00H.
3309
LD (BC),A
Put the value stored in Register A into the memory location pointed to by Register Pair BC.
330A
JUMP BACK to 32DAH.

330CH – SHARED ROUTINE – This routine does 4 RRA’s to the character held in Register A, and then JUMPs to 3317H to do some SERIOUS byte manipulation on the character held in Register A, puts the manipulated character into (HL), Bumps HL, and JUMPs to 3317H to do that again before it RETurns.

330C
LD D,A
Save the character held in Register A into Register D.
330D
GOSUB to 3313H to do that 4 RRA’s, some byte manipulation, and put the manipulated character into (HL), Bump HL, and come back.
3310
LD A,D
Restore the character from Register D back into Register A.
3311
JUMP to 3317H to do that to the same character but in the next (HL) and then RETurn.

3313H – SHARED ROUTINE – Do 4 RRA commands to move the bits around

3313
RRA
Rotate A right one bit, putting the old BIT 0 into the CARRY FLAG, and putting the old CARRY FLAG into BIT 7, which is the SIGN bit. So if A = 01 before the instruction, then A will be 00 and the C Flag set once run.
3314
RRA
Rotate A right one bit, putting the old BIT 0 into the CARRY FLAG, and putting the old CARRY FLAG into BIT 7, which is the SIGN bit. So If A = 00 before the instruction, then A will be 80 and the C Flag NOT set once run.
3315
RRA
Rotate A right one bit, putting the old BIT 0 into the CARRY FLAG, and putting the old CARRY FLAG into BIT 7, which is the SIGN bit. So If A = 80 before the instruction, then A will be 40 and the C Flag NOT set once run.
3316
RRA
Rotate A right one bit, putting the old BIT 0 into the CARRY FLAG, and putting the old CARRY FLAG into BIT 7, which is the SIGN bit. So If A = 40 before the instruction, then A will be 20 and the C Flag NOT set once run.

3317H – SHARED ROUTINE – Take Register A, do some manipulation to turn a hex value into an ASCII character representing that hex value, and put the manipulated character into (HL), Bump HL, and RETURN

3317
AND 0FH
Mask Register A against 0FH (Binary: 0000 1111) to turn off Bits 4-7. So if A was 20H, then A becomes 00H.
3319
ADD 90H
ADD 90H (Binary: 1001 0000)to Register A. If A was 00H, then A becomes 90H.
331B
DAA
Adjust A for BCD addition and subtraction operations.
331C
ADC 40H
ADD with CARRY 40H. So if A was 90H, A becomes D0H.
331E
DAA
Adjust A for BCD addition and subtraction operations. if A was D0H, it is now 30H, which is a 0.
331F
LD (HL),A
Put the adjusted character into the memory location pointed to by (HL).
3320
INC HL
Bump HL by one.
3321
RET
RETurn to caller.

3322H – DISKETTE – This is the DISK TEST routine entry point.

3322
LD HL,352DH
Set HL to point to 352DH.
3325
GOSUB to 345EH display the messages pointed to by HL at the screen location of DE, in this case “Cylinder Sector Drive:” + 00H.
3328
LD A,(3810H)
Put the character which has been pressed on Keyboard Row 4 (“01234567”) into Register A.
332B
AND 0FH
Mask Register A against 0FH (Decimal: 0000 1111) to keep only bits 0-3 (meaning “0123”).
332D
If the Z flag is set, then a key other than 0-3 was pressed, so LOOP back to 3228H to scan the keyboard again.
332F
LD B,A
Store the “0-3” character which was pressed into Register B.
3330
XOR A
Zero register A and clear the flags.
3331
RR B
The RR B command rotates the contents of Register B rotated right one bit position, with the contents of bit 0 are copied to the carry flag and the previous contents of the carry flag are copied to bit 7. So …
  • 0 sets masked B as “0000 0001”. After the RR B, B is “0000 0000” and CARRY is SET
  • 1 sets masked B as “0000 0010”. After the RR B, B is “0000 0001” and CARRY is 0
  • 2 sets masked B as “0000 0011”. After the RR B, B is “0000 0010” and CARRY is 0
  • 3 sets masked B as “0000 0100”. After the RR B, B is “0000 0100” and CARRY is 0
3333
If the CARRY flag is set (meaning a 0 was pressed), skip the next couple of instructions (which keeps bumping Register A until it matches the drive number) and JUMP to 3338H since Register A is already 0.
3335
INC A
LET A = A + 1.
3336
LOOP back to JUMP to 3331H so that Register A will ultimately match the drive number.

3338H – DISKETTE – Continue with the DISKETTE Test. On Entry Register A is holding a number 0-3 matching the disk drive number selected for testing.

3338
LD (42FCH),A
42FCH appears to be a storage area for the disk drive selected. Put the drive number selected (held in Register A) into the memory location 42FCH.
333B
ADD 30H
ADDing 30H to a number turns it into its ASCII equivalent, turning, lets say, the INTEGER 0 into the STRING 0.
333D
LD (DE),A
Put the ASCII equivalent of the drive number selected onto the screen immediately following the “Drive:” message.
333E
GOSUB to 3453H to reset the FDC, set track number and density, get the FDC status, and error out if it is CRC ERROR, SEEK ERROR, or NOT READY. Otherwise, clear all flags and Register A, and RETURN.
3341
If NZ is set, meaning there is a CRC ERROR, SEEK ERROR, or NOT READY, then JUMP to 3439H to display the error message on screen and then HALT.
3344
LD IY,3440H
Let Register IY = 3440H.
3348
LD C,00H
Let Register C = 00H.
334A
LD B,00H
Let Register B = 00H.
334C
GOSUB to 3371H to update the screen with the Cylinder and Sector being tested. B points to the cylinder and C points to one HIGHER than the Sector.
334F
INC B
Let B = B + 1 to advance the track number.
3350
LD A,B
Let A = Register B to point to the track number.
3351
CP 28H
Compare Register A with 28H (Decimal: 40 = 40 Tracks). If Register A equals track 40, the Z FLAG is set; otherwise NZ is set.
3353
If we are not at track 40, JUMP to 334CH to test the next track.

If we are here, we have hit track 40.

3355
DEC B
Decrement the value held in Register B (the track number being tested) by 1 (to track 39).

3356H – This routine will do the track test in reverse.

3356
GOSUB to 3371H to update the screen with the Cylinder and Sector being tested. B points to the cylinder and C points to one HIGHER than the Sector.
3359
DEC B
Decrement the value held in Register B (the track number being tested) by 1.
335A
Loop back 2 instructions if we have not hit track 0 yet.

If we are here, we have hit track 0.

335C
LD HL,0027H
Let HL = 0027H. L is then Decimal 39 (for track 39).

Start of a loop between here and 336DH to do even more testing, this time with wild swings … 00 then 39 then 01 then 38 …

335F
LD B,H
Let B = H. On the first loop B will = 00H.
3360
PUSH HL
Save the contents of Register Pair HL to the STACK.
3361
GOSUB to 3371H to update the screen with the Cylinder and Sector being tested. B points to the cylinder and C points to one HIGHER than the Sector.
3364
POP HL
Restore the HL back from top of the STACK to Register Pair HL.
3365
LD B,L
Let B = L. On the first loop B will = 27H (Track 39).
3366
PUSH HL
Save the contents of Register Pair HL to the STACK.
3367
GOSUB to 3371H to update the screen with the Cylinder and Sector being tested. B points to the cylinder and C points to one HIGHER than the Sector.
336A
POP HL
Move the value at the top of the STACK to Register Pair HL.
336B
INC H
Now that 2 tests have been run on the value of HL, one to track H and one to track L, we now increase track H (the low end).
336C
DEC L
and reduce track L (the high end).
336D
Loop back to test track H and track L until Track L is zero.
336F
We’re done with this phase so 334AH for more.

3371H – DISKETTE – Display the 2 bytes comprising BC onto the screen at location IY. In specific, it loads the screen responses for CYLINDER and SECTOR, overwriting them as they update.

3371
PUSH IY
Store the contents of Register IY to the top of the STACK. IY should be holding a screen position value.
3373
POP HL
Restore the contents at the top of the STACK (here, the value of Register IY) into Register Pair HL because the routine at 330CH requires that HL be a video location.
3374
LD A,B
Let Register A = Register B (for the first of the 2 bytes held in Register Pair BC) so that Register B can be converted into an ASCII character for display.
3375
GOSUB to 330CH, which does some SERIOUS byte manipulation on the character held in Register A to turn it into an ASCII HEX Digit and display it at (HL), and then BUMP HL.
3378
LD A,C
Let Register A = Register C (for the second of the 2 bytes held in Register Pair BC) so that Register B can be converted into an ASCII character for display.
3379
SET 3,L
Turn Bit 3 of Register L on, which has the same effect as adding 8 to Register L, pushing the video location 8 bytes.
337B
GOSUB to 330CH, which does some SERIOUS byte manipulation on the character held in Register A to turn it into an ASCII HEX Digit and display it at (HL), and then BUMP HL.
337E
PUSH BC
Save the contents of Register Pair BC to the top of the STACK.
337F
LD E,B
Put the contents of Register B into Register E.
3380
GOSUB to 3765H to set Register A=1, HL=4300, B=0CH, and JUMP to 3546 to decrement A, reset the floppy controller and wait, set C to 01-08H based on the drive number selected before exiting to 355DH.

3383H is one of many interrupt jump points in this routine and is a VERY common one at that.

3383
JUMP to 33C6H if the Z (ZERO) Flag is set. 33C6H POPs the STACK to BC, Bump C, Set A as C – 12H and, if A = 0, set C = 0. Return out of this routine.

If we are here, there was an error, so we need to set up to display the error message on screen.

3385
LD HL,346AH
Let HL = 346AH. This is a base address to point to the error messages lookup table.
3388
ADD A,A
Let Register A = entry Register A * 2. Example: For an ID NOT FOUND, the error code is 0BH, so after this instruction, A = 16H.
3389
LD D,00H
Let Register D = 0 because the math requires use of the Register Pair DE and not just Register E.
338B
LD E,A
Let Register E = Register A. At this point DE has been set to 00(entry A*2)H. In our example, Register E = is now 16H and DE = 0016H.
338C
ADD HL,DE
LET HL = HL + DE to advance HL by 2 * entry A bytes. In our example, HL is now 3480H. HL is in the middle of a lookup table and is set at E0H (next instruction 34H). .
338D
LD A,(HL)
Load the value stored in the memory location pointed to by Register Pair HL into Register A. In our example, HL is now 3480H so Register A will contain the memory contents of 3480, which is E0H.
338E
INC HL
Let HL = HL + 1. In our example, HL is now 3481H.
338F
LD H,(HL)
Load the value stored in the memory location pointed to by Register Pair HL into Register H. In our example, H is now 34H.
3390
LD L,A
Let Register L = Register A. Now HL has been re-set to be what was the memory contents of HL. In our example, L is now E0H and HL is 34E0H, which points to the error message ID NOT FOUND.
3391
EX DE,HL
Save HL by EXchanging the value held in Register Pair DE and the value held in Register Pair HL. In our example, DE is 34E0H (pointing to the error message) and HL is 0016H.
3392
LD HL,0010H
Let HL = 10H.
3395
PUSH IY
Store the screen position value of Register IY to the stop of the STACK.
3397
POP BC
Move the value at the top of the STACK (which is the screen position value of Register IY) to Register Pair BC.
3398
ADD HL,BC
LET HL = HL + BC. This is the same adding HL and IY, so it is pushing the screen location held in IY out by 10H characters (to 3C50H).
3399
EX DE,HL
Exchange the value held in Register Pair DE (the pointer to the error message) and the value held in Register Pair HL (the screen location).
339A
XOR A
Zero register A and clear the flags.
339B
GOSUB to 345EH display the messages pointed to by HL at the screen location of DE.
339E
GOSUB to 3453H to reset the FDC, set track number and density, get the FDC status, and error out if it is CRC ERROR, SEEK ERROR, or NOT READY. Otherwise, clear all flags and Register A, and RETURN.
33A1
XOR A
Zero register A and clear the flags.
33A2
LD HL,3F80H
Let HL = 3F80H.
33A5
PUSH IY
Store the screen location held in Register IY to the top of the STACK.
33A7
POP DE
Restore the screen position held in Register IY into Register Pair DE.
33A8
SBC HL,DE
Subtract with CARRY DE from HL. On the first run, HL = 3F80H, DE = 3C40H so the SBC leaves HL = 0340H.
33AA
If the SBC results in 0 (meaning HL and DE were the same), JUMP to the next routine at 33B3H to scroll the display.
33AC
LD DE,0040H
If HL and DE were not the same, Let DE = 0040H.
33AF
ADD IY,DE
Push out the screen position held in Register IY by 40H bytes (= 1 video line).
33B1
JUMP to 3BC6H to restore the STACK to BC, Bump C, Set A as C – 12H and, if A = 0, set C = 0. Return.

33B3H – DISKETTE – This routine will scroll the display by moving everything on the 3rd video line and later to the 2nd video line and later.

33B3
LD HL,3C80H
Let HL = 3C80H which is the first character on the 3rdline of the video display.
33B6
LD DE,3C40H
Let DE = 3C30H which is the first character on the 2nd line of the video display.
33B9
LD BC,0341H
Let BC = 341H (or 833 bytes).
33BC
LDIR
Moves the byte from (HL) to (DE), then HL and DE are incremented, and BC is decremented until BC is zero. Interrupts CAN fire during this command.
33BE
LD A,20H
Put a SPACE into Register A.
33C0
LD C,37H
Let Register C = 37H.
33C2
EX DE,HL
Exchange the value held in Register Pair DE (i.e., 3F81H) and the value held in Register Pair HL (i.e., 3FC1H).
33C3
GOSUB to 33E2H to do a little more screen cleanup and then fall through.

33C6H – DISKETTE – This is the exit routine for a 3371H call. STACK to BC, Bump C, Set A as C – 12H and, if A = 0, set C = 0. Return

33C6
POP BC
Move the value at the top of the STACK to Register Pair BC.
33C7
INC C
Let Register C = Register C + 1.
33C8
LD A,C
Let Register A = Register C.
33C9
SUB 12H
Subtract 12H from Register A.
33CB
RET NZ
If the NZ flag is set because Register A was not 12H, RETURN.
33CC
LD C,A
IF A is zero, then Let Register C = 0.
33CD
RET
RETurn to caller.

33CEH – BOOT – This routine is CALLed from 3426H as an alternate entry to 33D0H but with the character in Register A being a SPACE.

33CE
LD A,20H
Put a SPACE into Register A and then pass through.

33D0H – MULTIPLE USE ROUTINE – This routine fills Video Page 1 with the character held in Register A, swaps to Video Page 0, and fills that with the character held in Register A.

33D0
EX AF,AF’
EXchange the values of AF and AF’ (since EXX does not include Register Pair AF). Since we flip around, let me point out this PRESERVES Register A.
33D1
LD A,80H
Let A = 1000 0000 Binary. This value. when sent to port 84H, will change the VIDEO PAGE SELECT to PAGE 1.
33D3
GOSUB to 33D9H to set the VIDEO PAGE SELECT 1 to PAGE 1, fill that video page with the character held in Register A, and return here.
33D6
EX AF,AF’
EXchange the values of AF and AF’.
33D7
LD A,00H
Let A = 0000 0000 Binary. This value. when sent to port 84H, will, among other things, change the VIDEO PAGE SELECT to PAGE 0.

This code sets the Model III MISC controls (including the VIDEO PAGE SELECT), and fills the screen with the character held in Register A.

33D9
OUT (84H),A
OUTput Register A to Port 84H.
NOTE: Port 84H handles miscellaneous memory items. Output:
  • Bits 0-1: Identify the Model of the Computer
  • Bit 2: Video Display Mode [0=64×16, 1=80×24]
  • Bit 3: Reverse Video
  • Bit 4-6: RAM Bank Select
  • Bit 7: Video Page Select [0=Page 0, 1=Page 1]
33DB
EX AF,AF’
EXchange the values of AF and AF’. This RESTORES Register A.
33DC
LD HL,3C00H
Let HL = 3C00H, which is the first character location on the video display.
33DF
LD BC,03FFH
Let BC = 3FFH, which is the number of bytes on the video display.

33E2H – This routine is a pass through as well as being called. It is called as a subroutine from the DISK scan after scrolling the screen and DE will be 3FC1 and HL will be 3F81.

33E2
LD (HL),A
Fetch the contents of Register A on the screen location pointed to by Register Pair HL.
33E3
LD D,H
Copy HL to DE, which needs to be in 2 steps. First Let D = H.
33E4
LD E,L
and then Let E = L.
33E5
INC DE
Bump DE by 1 (which is then HL + 1).
33E6
LDIR
Copy each character on the screen to the next location on the screen. This is accomplished by LDIR which moves the byte from (HL) to (DE) (which in this case is simply HL + 1), then HL and DE are incremented, and BC is decremented until BC is zero. Interrupts CAN fire during this command.
33E8
RET
RETurn to caller.

33E9H – MEMORY – This routine is CALLed from 319DH to program the CRT Controller Registers, but to set HL at 3401H.

33E9
LD HL,3401H
LET HL = 3401H.

33ECH – This routine is CALLed from 3197H to program the CRT Controller Registers.

33EC
LD A,09H
Let Register A = 09H.
33EE
LD C,89H
Let Register C = 89H, which is the CRT CONTROLLER DATA REGISTER.

Top of a loop

33F0
OUT (88H),A
Send the value held in Register A to Port 88H (the CRT Controller Control Register).
33F2
OUTD
This command takes the byte held in the memory location pointed to by HL and writes it to Port C (the the CRT CONTROLLER DATA REGISTER). HL and B are then both decremented. When called from 3197H, HL starts at 31C0H.
33F4
DEC A
Decrement the value held in Register A by 1.
33F5
RET M
If the SIGN FLAG is set, RETURN.
33F6
If the SIGN FLAG is NOT set, LOOP BACK to 3BF0H.

33F8H – MEMORY – This table is moved to the CRT Controller Control Registers in the 33E9H if called from the MEMORY TEST page.

33F8
4F
33F9
40
33FA
44
33FB
08
33FC
15
33FD
00
33FE
10
33FF
12
3400
00
3401
0B
3402
00
3403
00
3404
00
3405
00
3406
00
3407
00

3408H – PRINTER – Main Menu Jump Point

3408
LD A,20H
Put a SPACE into Register A.

Start of a loop.

340A
GOSUB to 3419H to keep polling the Printer Port (0F8H) until No Printer Fault and Device Select are returned and then send the character held in Register A out to the printer. Register A is preserved.
340D
INC A
Increment the character held in Register A by 1 (these characters get printed).
340E
CP 80H
Compare Register A with Decimal 128, which is one higher than the highest character to print. Results:
  • If A < that value, it is safe to print another character and the CARRY FLAG will be set
  • if A >= 128, it is NOT safe to print that character, and the NO CARRY FLAG will be set.
3410
If the CARRY flag is set (i.e., the character is less than 128 and therefore can be printed) LOOP back to 340AH to keep testing and printing.

If we are here, then Register A is now set to a character too high to print.

3412
LD A,0DH
Let Register A = a CARRIAGE RETURN.
3414
GOSUB to 3419H to keep polling the Printer Port (0F8H) until No Printer Fault and Device Select are returned and then send the character held in Register A out to the printer. Register A is preserved.
3417
LOOP back to to 3408H and keep printing until the end of time.

3419H – PRINTER – Keep polling the Printer Port (0F8H) until No Printer Fault and Device Select are returned. Register A is preserved.

3419
PUSH AF
Save the contents of Register Pair AF to the STACK so we can use Register A in the loop that follows.

Start of a loop.

341A
IN A,(F8H)
Fetch the Printer Status Byte into Register A.
NOTE: F8H is the printer port. If Bit 7 is set, the printer is not busy. If Bit 6 is set the printer is not out of paper. If bit 5 is set, the device is selected. If Bit 4 is set, no printer fault.
341C
AND F0H
AND A against F0H (Binary: 1111 0000) to strip off BITS 3-0, leaving BITS 7-4 intact.
341E
CP 30H
Compare Register A with 30H (Decimal: 0011 0000). If Register A equals that 0011 0000, then the printer port is responding No Printer Fault and Device Select, meaning all good! If so, Z is set; otherwise NZ is set.
3420
If NZ is set, meaning there is something wrong, LOOP back to 341AH to keep polling and testing.
3422
POP AF
If we are here, then we got the OK, so restore Register Pair AF from the top of the STACK.
3423
OUT (FAH),A
Sent the contents of Register A to the PRINTER OUTPUT port of 0FAH.
3425
RET
RETurn.

3426H – BOOT – Main Menu Jump Point

3426
GOSUB to 33CEH to clear the screen and reset the Model 4.
3429
GOSUB to 3453H to reset the FDC, set track number and density, get the FDC status, and error out if it is CRC ERROR, SEEK ERROR, or NOT READY. Otherwise, clear all flags and Register A, and RETURN.
342C
If there was an error, JUMP to 3439H to display the error message on screen and then HALT.
342E
LD C,01H
If we are here, then there was no error from the FDC. Let Register C = 01H.
3430
LD DE,0000H
Let Register Pair DE = 0000H.
3433
GOSUB to 3767H to set HL=4300, B=0CH, and JUMP to 3546H to decrement A, reset the floppy controller and waits, sets C to 01-08H based on the drive number selected before exiting to 355DH.
3436
If there were no errors, JUMP to 4300H to run DOS.

3439H – SHARED ROUTINE (BOOT and DISKETTE). This is the jump point if there is a CRC ERROR, SEEK ERROR, or NOT READY from the FDC.

3439
PUSH AF
Save the contents of Register Pair AF to the STACK. This will preserve the FDC error code (held in Register A).
343A
GOSUB to 33CEH to clear the screen and reset the Model 4.
343D
POP AF
Restore the contents of Register Pair AF from the STACK.
343E
ADD A,A
Let A = 2 x the FDC ERROR CODE as a basis for offset to the proper message.
343F
LD HL,346AH
Let HL = 346AH. This is a base address to point to the error messages lookup table.
3442
LD D,00H
Let Register D = 00H since any operation will require use of the Register Pair DE and not just Register E.
3444
LD E,A
Put the ERROR CODE * 2 into Register E.
3445
ADD HL,DE
Advance Register Pair HL by DE bytes.
3446
LD A,(HL)
Load the value stored in the memory location pointed to by Register Pair HL (which is the LSB of the message) into Register A.
3447
INC HL
Bump Register Pair HL by 1 to point to the next memory location
3448
LD H,(HL)
Load the value stored in the memory location pointed to by Register Pair HL (which is the MSB of the message) into Register H.
3449
LD L,A
LET Register L (which is the LSB of the message) = Register A. Now HL points to the error message.
344A
XOR A
Zero register A and clear the flags.
344B
LD E,A
Let E = 00H.
344C
GOSUB to 345EH display the messages pointed to by HL at the screen location of DE, in this case, an error message, and then fall through to the below HALT routine.

344FH – MEMORY – This routine is the jump point when a final, hard error, has been found, but with DISABLING the interrupts.

344F
DI
Disable Interrupts so they don’t interrupt this routine. NOTE: This turns off the clock.
3450
HALT
Freeze the system.
3451
JR 3450H
JUMP to the HALT.

3453H – DISKETTE – This is the entry point for a routine to ultimately reset the FDC, set track number and density, get the FDC status, and error out if it is CRC ERROR, SEEK ERROR, or NOT READY. Otherwise, clear all flags and Register A, and RETURN.

3453
LD A,01H
Let Register A = 01H.
3455
LD B,04H
Let Register B = 04H.
3457
GOSUB to 3546H to decrement A, reset the floppy controller and waits, sets C to 01-08H based on the drive number selected before exiting to 355DH.
345A
LD (42FFH),A
Put the value held in Register A into the memory location at 42FFH
345D
RET
RETurn to caller.

345EH – SHARED ROUTINE – This routine is called to display one of the messages. When called from the DISKETTE test routine, for example, it displays “Cylinder Sector Drive:” + 00H. On entry from the DISKETTE test routine, BC and DE are 00, HL is 352DH, and A is 10H.

345E
LD A,D
LET Register A = Register D.
345F
OR 3CH
OR Register A against 3CH (the MSB of a screen location).
3461
LD D,A
Put the masked Register A back into Register D.
3462
LD A,(HL)
Load the value stored in the memory location pointed to by Register Pair HL into Register A. On the first run from the DISKETTE TEST routine, HL is 352DH which is the message pointer to “Cylinder Sector Drive:” + 00H.
3463
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A. On the first run from the DISKETTE TEST routine, A is 43H (which is the “C” in Cylinder).
3464
RET Z
If the Z flag is set because we hit the end of that message, RETURN.
3465
LD (DE),A
Put the character in Register A into the memory location pointed to by Register Pair DE. On the first run from the DISKETTE TEST routine, DE is 3C00H which is the first spot on the screen.
3466
INC HL
BUMP Register Pair HL to point to the next character in the message message.
3467
INC DE
BUMP Register Pair DE to point to the next space on the screen.
3468
Loop back to 3462H to keep displaying characters until this routine is exited in 3464H by hitting the 00H delimeter.

346AH – DISKETTE – This is a lookup table for Disk errors in 3482H

346A
A0H
346B
34H
346C
A0H
346D
34H
346E
A0H
346F
34H
3470
A0H
3471
34H
3472
A0H
3473
34H
3474
A0H
3475
34H
3476
F3H
3477
34H
3478
B0H
3479
34H
347A
BFH
347B
34H
347C
A0H
347D
34H
347E
D0H
347F
34H
3480
E0H
3481
34H

3482H – DISKETTE – Error Message Storage Area

3482
MESSAGE
Host Not Ready+00H
3491
MESSAGE
Host Is Ready+00H
34A0
MESSAGE
Drive Not Ready+00H
34B0
MESSAGE
Data CRC Error+00H
34BF
MESSAGE
Track Seek Error+00H
34D0
MESSAGE
Lost Data Error+00H
34E0
MESSAGE
ID Not Found Error+00H
34F3
MESSAGE
Drive Door Open+00H
3503
MESSAGE
Drive Not Available+00H
3517
MESSAGE
Loading…+00H
3522
MESSAGE
Load Error+00H
352D
MESSAGE
Cylinder Sector Drive:+00H

3546H – DISKETTE – This routine decrements A, resets the floppy controller and waits, sets C to 01-08H based on the drive number selected before exiting to 355DH.

3546
DEC A
LET A = A – 1.
3547
JUMP to the next instruction (354AH) if the Z (ZERO) Flag is set.
354A
GOSUB to 363AH.

Note: 363AH resets the Floppy Drive Controller, Push BC, Set Register B as 00H, and JUMP to 3E46H to LOOP for FF, Restores BC From the STACK (so B is unchanged from entry value), and RETURNs.
354D
LD A,C
Let Register A = Register C.
354E
OUT (F2H),A
OUTput the value held in Register A to Port F2H.
NOTE: Port F2H is the Floppy Disk Controller Track Register.
3550
LD A,(42FCH)
Fetch the disk drive from 42FCH and put it into Register A.
3553
LD C,01H
Let Register C = 01H.
3555
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.

The next loop sets C=01 when the Disk Drive Selected (held in A) is 0, C=02 when the drive 1, C=04 when the drive is drive 2, and C=08 when the drive is drive 3 and then exits to 355DH.

3556
If the Drive Drive was 0, then exit the loop by JUMPing to the next routine (3D5DH).
3558
RL C
The RL C command rotates the contents of C rotated left one bit position with the contents of bit 7 are copied to the carry flag and bit 0.
355A
DEC A
LET A = A – 1.
355B
LOOP back to 3556H to keep Decrementing A and Rotating C.

355DH – DISKETTE – We’re here when Z is set from the loop in the above routine and C is now 01, 02, 04, or 08 based on the disk drive selected.

355D
LD A,C
Let Register A = Register C. A will hold 1 for Drive 0, 2 for Drive 1, 4 for Drive 2, and 8 for Drive 3.
355E
OR 80H
OR Register A against 80H (Decimal: 1000 0000) to turn Bit 7 on (which will force a DOUBLE DENSITY selection onto byte holding the drive letter).
3560
LD (42FDH),A
Put the masked Register A into the memory location 42FDH.
3563
OUT (F4H),A
OUTput the value held in Register A to Port F4H.
NOTE: Port F4H is the Disk Drive, Side, and Disk Density Select. Bits 0-3 are the drive select of 0-3, Bit 4 is the Side Select, Bit 5 is Writer Precom (on/off), Bit 6 is Wait State Generation (on/off) and Bit 7 is 0 for single density and 1 for double density.
3565
LD A,B
Put the contents of Register B into Register A.
3566
CP 05H
Compare Register A (which was Register B) with 05H. Results:
  • If Register A equals 05H, the Z FLAG is set.
  • If A < 05H, the CARRY FLAG will be set
  • if A >= 05H, the NO CARRY FLAG will be set.
3568
If A >= 05H (so NC is set), JUMP to 3588H.
356A
LD A,0CH
Let Register A = 0CH (Decimal: 0000 1100). This command will be sent to the FDC in the next instruction, so let me explain here that “0000 wxyz” is the FDC instruction for RESTORE. w=1 says to load head at beginning, x=1 says verify, y=z=0 says 6ms stepping).
356C
GOSUB to 364AH to send the command held in Register A to the FDC (Port F0H) and LOOP until anything other than “NOT READY” is returned by the FDC. Register A holds the response from the FDC on exit.
356F
LD B,A
Let Register B = Register A (the result from the FDC).
3570
AND DFH
Mask Register A against 1101 1111 to turn off bit 5 and keep the rest.
3572
CP 81H
Compare Register A with 81H (Decimal: 1000 0001). If Register A equals 81H (meaning the FDC responded only BUSY and NOT READY), the Z FLAG is set; otherwise the NZ flag is set.
3574
NZ will be set if the floppy drive responded anything other than BUSY and NOT READY, and in that case, JUMP to 35C7H to test for SEEK ERROR, CRC ERROR, and NOT READY and jump out if they are found; return with A=0 and clear flags if OK.
3576
LD A,C
Restore the old contents of Register A (1 for Drive 0, 2 for Drive 1, 4 for Drive 2, and 8 for Drive 3) from Register C.
3577
AND 9EH
Mask Register A by ANDing it against 1001 1110 to turn off bits 0, 5, and 6.
3579
LD D,05H
Let Register D = 05H.
357B
If the Z FLAG is set (based on the AND 9EH, above), then JUMP to 3585H to put the contents of Register D into Register A (in this case an 05H), set the flags, and RETURN.
357D
LD D,03H
Let Register D = 03H
357F
CP 06H
Compare Register A with 06H. If Register A equals 06H, the Z FLAG is set; otherwise the NZ FLAG is set.
3581
If the Z FLAG is set (based on the CP 06H, above), then JUMP to 3585H to put the contents of Register D into Register A (in this case an 03H), set the flags, and RETURN.
3583
LD D,06H
Let Register D = 06H.

3585H – DISKETTE – This routine is called when we need to fail out of the DISKETTE test routine. A specified value is put into Register A, the flags are set, and then we RETurn out of the call.

3585
LD A,D
Copy the contents of Register D (which seems to have been an specifically set value before calling this routine) into Register A.
3586
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
3587
RET
RETurn to caller.

3588H – DISKETTE – Routine is jumped to from 3568H if Register A (which was Register B) is >= 05H. I have no idea when this occurs; I couldn’t trigger it. FWIW, bot Dh and E point to the track number to test.

3588
CP 32H
Compare Register A with 32H. If Register A equals 32H, the Z FLAG is set; otherwise the NZ Flag is set.
358A
If Register A is not 32H then JUMP to 3DAFH, with B continuing to be 0CH.

If we are here, then Register A was 32H.

358C
LD B,04H
Let Register B = 04H.
358E
LD D,01H
Let Register D = 01H.

This is the start of a DJNZ loop for a count of 4.

3590
GOSUB to 363AH.

Note: 363AH resets the Floppy Drive Controller, Push BC, Set Register B as 00H, and JUMP to 3E46H to LOOP for FF, Restores BC From the STACK (so B is unchanged from entry value), and RETURNs.
3593
LD A,D
Let Register A = Register D. I do not see that the prior call changed Register D at all.
3594
OUT (F4H),A
OUTput the value held in Register A to Port F4H, and if that is 0000 0001, then the FDC is being instructed to select Drive 0..
NOTE: Port F4H is the Disk Drive, Side, and Disk Density Select. Bits 0-3 are the drive select of 0-3, Bit 4 is the Side Select, Bit 5 is Writer Precom (on/off), Bit 6 is Wait State Generation (on/off) and Bit 7 is 0 for single density and 1 for double density.
3596
RLCA
Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0). By doing this, A is multiplied by 2.
3597
LD D,A
Preserve the rotated (doubled) Register A into Register D.
3598
IN A,(F0H)
INput from Port F0H and put the result into Register A.
NOTE: Port F0H is the FDC Status port on input. Input Results:
  • Bit 0: Busy
  • Bit 1: Index/DRQ
  • Bit 2: Track 0/Data Lost
  • Bit 3: CRC error
  • Bit 4: Seek error/Record not found
  • Bit 5: If Reading, Record Type, if Writing, Write Fault/Head loaded
  • Bit 6: Write Protect
  • Bit 7: Not ready
359A
AND 20H
Mask Register A by ANDing 0010 0000, to remove all bits except for Bit 5.
359C
If Bit 5 was LOW (no error), JUMP to 3DACH.
359E
XOR A
Zero register A and clear the flags.
359F
OUT (F0H),A
OUTput the value held in Register A (which is a ZERO) to Port F0H.
NOTE: Port F0H is the FDC Status Register. Outputting 0000 0000 is issuing the command RESTORE (000), SINGLE RECORD (0), SIDE 0 (0), NO 15MS DELAY (0), DISABLE SIDE COMPARE (0).
35A1
Delay for a loop of 13H cycles to allow the FDC to process the command via a GOSUB to 3643H.

Begin a loop.

35A4
IN A,(F0H)
INput from Port F0H.
NOTE: Port F0H is the FDC Status port on input. Input Results:
  • Bit 0: Busy
  • Bit 1: Index/DRQ
  • Bit 2: Track 0/Data Lost
  • Bit 3: CRC error
  • Bit 4: Seek error/Record not found
  • Bit 5: If Reading, Record Type, if Writing, Write Fault/Head loaded
  • Bit 6: Write Protect
  • Bit 7: Not ready
35A6
XOR 01H
XOR 01, meaning If Bit 0 of Register A is a 1 (meaning the FDC returned BUSY) then make it a 0 and vice versa. Leave all the other bits alone.
35A8
AND 05H
MASK that result by ANDing it against 0000 0101 to keep only bits 0 and 2. If Bit 2 is low then we are not yet at “TRACK 0” from the FDC, and If Bit 0 is low then we received a “BUSY” from the FDC.
35AA
If the result of the Masked/XOR’d A was Bit 0 and 2 low, LOOP back to 35A4H and keep polling the FDC until we get TRACK 0 and NOT BUSY.
35AC
Loop back to 3590H and keep looping until Register B = 0.
35AE
RET
RETurn to caller.

35AFH – DISKETTE – This routine is JUMPED to by 358AH if Register A did not equal 32H. On Entry E holds the track number to test.

35AF
IN A,(F1H)
Fetch the value currently held by the FDC Track Register (Port F1H) into Register A.
35B1
CP E
Check to see if we are actually at the track number we want to test by comparing Register A (which is the track that the FDC reports as current) with the contents of Register E (the track where we want to be). If Register A equals the contents of Register E, the Z FLAG is set; otherwise the NZ FLAG is set
35B2
If we are at the track we want to test then Register A will equal Register E, so JUMP down to 35C1H.
35B4
LD A,E
Since Register A did not equal Register E, we are NOT at the track we need, so load up Register A with the track we need to test (held in Register E).
35B5
OUT (F3H),A
OUTput the byte stored in Register A to the FDC Data Register (Port F3H).
35B7
LD A,1CH
Let A = 0001 1 1 00. When OUTput to the FDC (Port F0H) this sends the command: Seek (0001), Load head at beginning (1), Verify (1) 6ms stepping rate (00).
35B9
GOSUB to 364AH to send the command held in Register A to the FDC (Port F0H) and LOOP until anything other than “NOT READY” is returned by the FDC. Register A holds the response from the FDC on exit.
35BC
LD D,A
Preserve the contents of Register A (the FDC response code) into Register D.
35BD
AND 98H
MASK Register A against 98H (Binary: 1001 1000) to keep only Bits 3, 4, and 7 (which are CRC ERROR, SEEK ERROR, and NOT READY).
35BF
If any of those error bits are high JUMP to 35C6H to Set B = D (the FDC response value).

If we are here, then EITHER the FDC track register equalled E (and 35B2 jumped here) OR we told the FDC to go to track E and didn’t get any errors, so we are where we need to be on the diskette.

35C1
LD A,B
Let Register A = Register B
35C2
CP 0CH
Compare Register A with 0CH. If Register A equals 0CH, the Z FLAG is set; otherwise the NZ FLAG is set.
35C4
If Register A is still holding onto the 0CH value, JUMP to 35E5H.

35C6H – DISKETTE – Alternate entry point to the disk test continuation routine EXCEPT that the contents of Register D (the response from the FDC that spat out an error) are preserved into Register B.

35C6
LD B,D
Preserve Register D (the FDC response code) in Register B and THEN continue the diskette test with the next instruction.

35C7H – DISKETTE – If the selected floppy drive is not returning BUSY and NOT READY, we wind up here to continue the test. Set D to 08H and then JUMP to 35D3H to test for SEEK ERROR, CRC ERROR, and NOT READY and jump out if they are found; return with A=0 and clear flags if OK.

35C7
LD D,08H
Let D = 08H.
35C9
JUMP to 35D3H to test for SEEK ERROR, CRC ERROR, and NOT READY and jump out if they are found; return with A=0 and clear flags if OK.

35CBH – DISKETTE – This routine is jumped to from 3636H. It tests Bit 2 of the FDC Status and proceeds with D=0AH if that was high (and JUMP to 3585H) or 0BH if it was low (and PASS THROUGH to 35D3H).

35CB
LD D,0AH
Let Register D = 0000 1010
35CD
BIT 2,B
Test Bit 2 of Register B. Register B is very likely to contain the FDC Status from Port F0H. If so, Bit 2 indicates either TRACK 0 (if this was from a RESTORE, SEEK, or STEP command) or LOST DATA (if this was from a SECTOR READ or SECTOR WRITE command).
35CF
If Bit 2 of Register B is 1, JUMP to 3585H to put the contents of Register D into Register A (in this case an 0AH), set the flags, and RETURN.
35D1
LD D,0BH
If Bit 2 of Register B is 0, then Let D = 0BH and continue

35D3H – DISKETTE – This routine tests for SEEK ERROR, CRC ERROR, and NOT READY. If any are found, the error routine at 3585 is jumped to. Otherwise, Register A and the flags are reset, and we RETURN. On Entry Register B contains the response from the FDC and D has an error code.

35D3
BIT 4,B
Test Bit 4 of Register B and set the flags accordingly. Bit 4 from the FDC (Port F0H) is “SEEK ERROR”.
35D5
If the Z FLAG is set (based on the BIT 4, B, above), then JUMP to 3585H to put the contents of Register D into Register A, set the flags, and RETURN.
35D7
LD D,07H
Let Register D = 07H.
35D9
BIT 3,B
Test Bit 3 of Register B and set the flags accordingly. Bit 3 from the FDC (Port F0H) is “CRC ERROR”.
35DB
If the NZ flag is set (based on the BIT 3, B, above), then JUMP to 3585H to put the contents of Register D (in this case an 07H) into Register A, set the flags, and RETURN.
35DD
LD D,03H
Let Register D = 03H.
35DF
BIT 7,B
Test Bit 7 of Register B and set the flags accordingly. Bit 7 from the FDC (Port F0H) is “NOT READY”.
35E1
If the Z FLAG is set (based on the BIT 7, B, above), then JUMP to 3585H to put the contents of Register D (in this case, an 03H) into Register A, set the flags, and RETURN.
35E3
XOR A
Zero register A and clear the flags.
35E4
RET
RETurn to caller.

35E5H – DISKETTE – This routine is jumped to from 35C54 if we are on the track that we want to test AND Register A is still holding 0CH.

35E5
XOR A
Zero register A and clear the flags.
35E6
LD B,A
Let B = 0
35E7
LD A,(42FDH)
Fetch the contents of memory location 42FDH into Register A. 42FDH stores a byte representing the Drive Number and Double Density, and was put there at 3560H
35EA
LD D,A
Let Register D = Register A.
35EB
LD C,82H
Let Register C = 1000 0010. Register C will ultimately be sent to the FDC Command/Status port (Port F0H), although it may change from the current value. At the current value, this sends the command: READ SECTOR (100), SINGLE RECORD (0), SIDE 0 (0), No 15ms Delay (0), Side Compare Flag Enabled (10).
35ED
IN A,(F2H)
Fetch the current sector from the FDC Sector Register (Port F2H) and put it into Register A.
35EF
CP 12H
Compare Register A with 12H (Decimal 18). Results:
  • If Register A equals sector 18, the Z FLAG is set.
  • If A < sector 18, the CARRY FLAG will be set
  • if A >= sector 18, the NO CARRY FLAG will be set.
35F1
If we have not yet arrived at sector 18 on the track, JUMP to 35F9H to keep reading the track.

If we are here, then we have reached Sector 18, so we need to check SIDE 1 of the disk …

35F3
SUB 12H
Reduce Register A (the sector number) by 18.

I’m not convinced this is best practices. This assumes nothing went wrong and the FDC never reported above 12H. Perhaps a LD A,00H would have been safer.
35F5
SET 4,D
Turn on Bit 4 of Register D. Bit 4 of the FDC Drive Select/Options value (Port F4) toggles the SIDE SELECT, so this command selects SIDE 1. Register D is holding the Drive Number in Bits 0-2, a flag for Side 0 (bit 4) and a flag for Double Density (bit 7).
35F7
SET 3,C
Turn on Bit 3 of Register C. Bit 3 of the FDC Command/Status Port (F0H), when applied to a READ SECTOR command, is the SIDE COMPARE FLAG, so this will flip that bit to COMPARE FOR SIDE 1.

35F9H – DISKETTE – This routine is JUMPed to from 35F1H if we have not yet hit Sector 18 on the track we are testing.

35F9
ADD A,B
Add the value in register B to the value in register A.
35FA
OUT (F2H),A
OUTput the value held in Register A to Port F2H.
NOTE: Port F2H is the Floppy Disk Controller SECTOR Register.
35FC
LD A,D
Let Register A = Register D (the FDC Drive Select/Options [Port F4H] command).
35FD
OUT (F4H),A
OUTput the value held in Register A to Port F4H.
NOTE: Port F4H is the Disk Drive, Side, and Disk Density Select. Bits 0-3 are the drive select of 0-3, Bit 4 is the Side Select, Bit 5 is Writer Precom (on/off), Bit 6 is Wait State Generation (on/off) and Bit 7 is 0 for single density and 1 for double density.

The variable shuffling that is about to happen is so that the INI command in 361EH is properly set. INI requires that the port be in Register C, the destination memory location in Register Pair HL, and the bytes to read in Register B.

35FF
LD B,00H
Let Register B = 00H.
3601
LD E,H
Let Register E = Register H. Register Pair HL should be 4300H.
3602
LD A,C
Let Register A = Register C. Register C is holding the FDC Command/Status Port (F0H) command.
3603
LD C,F3H
Let Register C = F3H. Port F3H is the FDC Data Register (the data byte to be READ or WRITTEN to disk).

The only register being modified from this point to the end of the routine is Register A. Everything else is now set up for the INI command in 361EH.

3605
OUT (F0H),A
OUTput the value held in Register A (formerly held in Register C) to Port F0H.
NOTE: Port F0H is the FDC Command Register.
3607
LD A,80H
Let Register A = 1000 0000
3609
OUT (E4H),A
OUTput the value held in Register A to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. Sending an 80H to Port E4 sets DISABLE DRQ and ENABLE INTRQ. This is done because the routine at 361EH has no exit, and will exit only on INTRQ
360B
Delay for a loop of 13H cycles to allow the FDC to process the command via a GOSUB to 3643H.
360E
LD A,D
Let Register A = Register D (the FDC Drive Select/Options [Port F4H] command).
360F
OUT (F4H),A
OUTput the value held in Register A to Port F4H.
NOTE: Port F4H is the Disk Drive, Side, and Disk Density Select. Bits 0-3 are the drive select of 0-3, Bit 4 is the Side Select, Bit 5 is Writer Precom (on/off), Bit 6 is Wait State Generation (on/off) and Bit 7 is 0 for single density and 1 for double density.
3611
IN A,(F0H)
Fetch the FDC Status from Port F0H and put it into Register A.
NOTE: Port F0H is the FDC Status port on input. Input Results:
  • Bit 0: Busy
  • Bit 1: Index/DRQ
  • Bit 2: Track 0/Data Lost
  • Bit 3: CRC error
  • Bit 4: Seek error/Record not found
  • Bit 5: If Reading, Record Type, if Writing, Write Fault/Head loaded
  • Bit 6: Write Protect
  • Bit 7: Not ready
3613
BIT 1,A
Test Bit 1 of the FDC Status. Bit 1 is the Index/DRQ flag.
3615
If the Index/DRQ flag is set to 1, JUMP to 361EH to continue the test, as we NOW have the right track, sector, side and INDEX mark.
3618
RLCA
Test to see if the FDC reports BUSY (Bit 7) by rotating the bits in A left (with Bit 7 going into both the CARRY and Bit 0).
3619
If NC is set, then the drive did NOT report BUSY, so LOOP back to 3611H until we hit the index.
361C
If we are here then we have found the INDEX and the FDC isn’t BUSY, so JUMP to 3E2DH.

361EH – DISKETTE – This routine is JUMPed to from 3615H if we have found the INDEX mark after moving to the right track, sector, and side to test. It loads the buffer at 4300H with the sector values but is exited only by an interrupt.

361E
INI
Read a byte from Port C (set at 3603H to be Port F3H) into the memory location pointed to by HL (on entry 4300H), then DECrement HL and INCrement B. Z is set if B = 0; otherwise it is reset.
3620
LD A,D
Let Register A = Register D (the FDC Drive Select/Options [Port F4H] command).
3621
OR 40H
Mask Register A via OR against 0100 0000 to turn on Bit 6 of Register A. Bit 6 of a coammnd going to the FDC Drive Select port (F4H) turns on WAIT STATE GENERATION.

Start of a loop of reading bytes into memory location HL, exited only via an interrupt.

3623
OUT (F4H),A
OUTput the value held in Register A to Port F4H.
NOTE: Port F4H is the Disk Drive, Side, and Disk Density Select. Bits 0-3 are the drive select of 0-3, Bit 4 is the Side Select, Bit 5 is Writer Precom (on/off), Bit 6 is Wait State Generation (on/off) and Bit 7 is 0 for single density and 1 for double density.
3625
INI
Read a byte from Port C into the memory location pointed to by HL, then DECrement HL and INCrement B. Z is set if B = 0; otherwise it is reset.
3627
LOOP back to 3623H.

3629H – DISKETTE – Put the value at the top of the STACK into Register Pair BC, Clear Register A and all flags, Disable the DRQ and INTRQ interrupts and then continue to 362DH

3629
POP BC
Move the value at the top of the STACK to Register Pair BC.
362A
XOR A
Zero register A and clear the flags.
362B
OUT (E4H),A
OUTput the value held in Register A (a 0) to Port E4H to deal with interrupts and then fall through to the common jump point of 362DH.
NOTE: Port E4H is the non-maskable interrupt latch. Sending a 0 disable the DRQ and INTRQ interrupts.

362DH – DISKETTE – If we are here, we are on the right drive, side, track, sector, and the INDEX just triggered!

362D
IN A,(F0H)
Fetch the FDC Status from Port F0H and put it into Register A.
NOTE: Port F0H is the FDC Status port on input. Input Results:
  • Bit 0: Busy
  • Bit 1: Index/DRQ
  • Bit 2: Track 0/Data Lost
  • Bit 3: CRC error
  • Bit 4: Seek error/Record not found
  • Bit 5: If Reading, Record Type, if Writing, Write Fault/Head loaded
  • Bit 6: Write Protect
  • Bit 7: Not ready
362F
LD B,A
Preserve the FDC Status into Register B
3630
GOSUB to 3638H to POP the STACK into the Program Counter.

3633H is one of many interrupt jump points in this routine.

3633
LD A,H
Let Register A = Register H. Register H seems to be either 43H or 44H as a pointed to a memory buffer holding sector contents.
3634
SUB E
Register A = Register A – Register E. From what I can see, Register E tends to be 43H
3635
LD E,A
Put the difference back into Register E.
3636
Continue on by JUMPing to 3DCBH.

3638H – DISKETTE – Execute a RETN

3638
RETN
RETN is used at the end of a non-maskable interrupt service routine to POP the top of the STACK entry into PC. The value of IFF2 is copied to IFF1 so that maskable interrupts are allowed to continue as before. NMIs are not enabled on the TI.

363AH – Reset the Floppy Drive Controller, Push BC, Set Register B as 00H, and JUMP to 3646H. B is unchanged from entry value.

363A
LD A,D0H
Let A = D0H (Decimal: 1101 0000).
363C
OUT (F0H),A
OUTput the value held in Register A to Port F0H. When D0H is sent, it resets the FDC and puts FDC in mode 1 (INTRQ; �000� terminate command without interrupt)
NOTE: Port F0H is the FDC Status Register. Outputting 1000 0000 is issuing the command READ SECTOR (100), SINGLE RECORD (0), SIDE 0 (0), NO 15MS DELAY (0), DISABLE SIDE COMPARE (0).
363E
PUSH BC
Save the contents of Register Pair BC to the top of the STACK.
363F
LD B,00H
Let Register B = 00H in preparation for the next instruction.
3641
JUMP to 3646H to Delay FFH loops, Restore BC from the top of the STACK, and RETurn.

3643H – DISKETTE – Delay (13H loops) to let the FDC process a command and RETurn

3643
PUSH BC
Save the contents of Register Pair BC to the STACK.
3644
LD B,13H
Let B = 13H. This will be a delay loop long enough to allow a FDC command to process.

3646H – Delay REGISTER B loops, Restore BC from the top of the STACK, and RETurn

3646
Loop back to 3646H and keep looping until Register B = 0.
3648
POP BC
Restore Register Pair BC from the top of the STACK.
3649
RET
RETurn to caller.

364AH – DISKETTE – Send the command held in Register A to the FDC and LOOP until anything other than “NOT READY” is returned by the FDC. Register A holds the response from the FDC on exit.

364A
OUT (F0H),A
OUTput the value held in Register A to Port F0H.
NOTE: Port F0H is the FDC Status Register. Outputting 1000 0000 is issuing the command READ SECTOR (100), SINGLE RECORD (0), SIDE 0 (0), NO 15MS DELAY (0), DISABLE SIDE COMPARE (0).
364C
Delay for a loop of 13H cycles to allow the FDC to process the command via a GOSUB to 3643H.
364F
LD C,00H
Let Register C = 00H.
3651
IN A,(F0H)
INput from Port F0H.
NOTE: Port F0H is the FDC Status port on input. Input Results:
  • Bit 0: Busy
  • Bit 1: Index/DRQ
  • Bit 2: Track 0/Data Lost
  • Bit 3: CRC error
  • Bit 4: Seek error/Record not found
  • Bit 5: If Reading, Record Type, if Writing, Write Fault/Head loaded
  • Bit 6: Write Protect
  • Bit 7: Not ready
3653
BIT 0,A
Check BIT 0 (the bit for “BUSY” status on the FDC) of Register A.
3655
RET Z
If the FDC replied “BUSY”, RETURN.
3656
BIT 7,A
Check BIT 0 (the bit for “NOT READY” status on the FDC) of Register A.
3658
RET NZ
If the FDC replied anything other than “NOT READY”, RETURN.
3659
LD C,A
Let C = Register A.
365A
LOOP back to 3651H.

365CH – BOOT FROM RS-232 – Routine Entry Point.

365C
LD A,04H
Let Register A = 04H. I’m not at ALL clear why this is here, as 33CEH resets Register A.
365E
GOSUB to 33CEH to clear the screen and reset the Model 4.
3661
LD A,64H
>Let Register A = 0110 0100. When applied to port EAH, this is DTR On, RTS On, Send Break Signal, Parity Enabled, 0 Stop Bits, Word Length 8, Parity Odd.
3663
OUT (EAH),A
OUTput the value held in Register A to Port EAH.
NOTE: Port EAH is the RS-232 UART Control Register/Status Register. For output:
  • Bit 0: Data Terminal Ready
  • Bit 1: Request to End
  • Bit 2: Break
  • Bit 3: Parity Enable
  • Bit 4: Stop Bits
  • Bit 5: Select
  • Bit 6: Word Length
  • Bit 7: Parity (0=Odd, 1=Even)
3665
LD HL,3482H
Let Register Pair HL = 3482H to point to the message “Host Not Ready”
3668
LD DE,0000H
Let Register Pair DE = 0000H so that, in part, D=00H so that the message at 3482H will be placed on the screen at 3C00H + 00H
366B
GOSUB to 345EH display the messages pointed to by HL at the screen location of DE, in this case “Host Not Ready”.

Do a loop polling Port E8H, Masking against 00100000 to leave only Bit 5 on, and LOOPing back until Bit 5 is NOT ZERO.

366E
IN A,(E8H)
Fetch the RS-232 Status Register (Port E8H) and put the result into A.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Input:
  • Bit 4: Ring Indicator
  • Bit 5: Carrier Detect
  • Bit 6: Data Set Ready
  • Bit 7: Clear to Send
3670
AND 20H
AND Register A against 0010 0000 to preserve only Bit 5 (= Carrier Detect).
3672
If there is no carrier detect, LOOP back to 3E6EH.

At this point, we have a carrier detect.

3674
LD DE,0000H
Let Register Pair DE = 0000H so that, in part, D=00H so that the message at 3491H will be placed on the screen at 3C00H + 00H
3677
LD HL,3491H
Let Register Pair HL = 3491H to point to the message “Host Is Ready”
367A
GOSUB to 345EH display the messages pointed to by HL at the screen location of DE, in this case “Host Is Ready”.
367D
LD HL,42FEH
Let HL = 42FEH as an initial value for the loop at 3680H.

3680H – This location is LOOPED back to from either 3699H, 36A0H or 36A4H.

3680
LD A,FFH
Let Register A = FFH as an initial value for the loop at 3682H. It will be reduced by 11H each large loop pass, but then ultimately jumped back here on a failure to start the large loop again.

3682H – This location is LOOPED back to from 3697H

3682
LD C,A
Preserve Register A in Register C
3683
OUT (E9H),A
OUTput the value held in Register A to Port E9H.
NOTE: Port E9H is the RS-232 Baud Rate Selects and Sense Switches. Outputting to Port E9H will set the Baud Rate as follows:
  • Bits 0-3 – Select the Receive Rate
  • Bits 4-7 – Select the Transmit Rate
3685
OUT (E8H),A
OUTput the value held in Register A to Port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
3687
LD B,0AH
Let Register B = 0AH. This will form a counter for the DJNZ statement in 3692H
3689
GOSUB to 36F1H to wait for DATA READY from the RS-232 and return either NZ if the RS-232 threw an error (with Register A holding the 3 RS-232 error bits), or Z with A holding a byte from the RS-232. Only Register A is affected.
368C
if 36F1H returns with NZ set, then the RS-232 threw an error, so JUMP forward to 3692H.

If we are here, then we fetched a byte from the RS-232 into Register A.

368E
CP 55H
Compare Register A with 55H. If Register A equals 55H, the Z FLAG is set; otherwise the NZ FLAG is set.
NOTE:A 55H is a standard header byte of an executable file
3690
If the byte read from the RS-232 is a 55H then JUMP to 369BH to continue, otherwise pass through to retry the RS-232. This is the loop exit.

3692H – This routine is passed through if the byte received from the RS-232 was not 55H -or- was jumped to from 368CH if the RS-232 threw an error.

3692
Loop back to 3689H and keep looping until Register B = 0.

If we are here, then the RS-232 spat out errors 10H times.

3694
LD A,C
Restore Register A from Register C (preserved at 3682H).
3695
SUB 11H
Let A = A – 11H.
NOTE: If a borrow results from the subtraction, the C FLAG will be set. Since A was set to FFH at the beginning of this routine (and preserved in Register C), this is actually a counter of 16. FF to EE to DD etc.
3697
If, as a result of the subtraction, there was no borrow (i.e., a loop of 16), JUMP back to 3682H to keep polling.

If we are here, then the RS-232 spat out errors 10H x 16 times.

3699
JUMP to 3680H to restart the process, with A as FFH.

369BH – BOOT FROM RS-232 – If we are here, then we received a 55H byte from the RS-232 at 3690H.

369B
LD B,0AH
Let Register B = 0AH for a DJNZ loop of 10.

Start of a loop which exits if we get 55H from the RS-232 10 times.

369D
GOSUB to 36F1H to wait for DATA READY from the RS-232 and return either NZ if the RS-232 threw an error (with Register A holding the 3 RS-232 error bits), or Z with A holding a byte from the RS-232. Only Register A is affected.
36A0
If the RS-232 threw an error, JUMP to 3680H to start the RS-232 polling/reading process all over again.
36A2
CP 55H
Compare Register A with 55H. If Register A equals 55H, the Z FLAG is set; otherwise the NZ FLAG will be set.
36A4
If the RS-232 did not provide a 55H, JUMP to 3680H to start the RS-232 polling/reading process all over again.

If we are here then the RS-232 gave us a 55H with no errors.

36A6
Loop back to 369DH and keep looping until Register B = 0.

If we are here then the RS-232 gave us TEN 55H’s with no errors, so proceed …

36A8
LD DE,0184H
Let Register Pair DE = 0184H to point to a location on the screen..
36AB
LD HL,3710H
Let Register Pair HL = 3710H to point to the Found Baud Rate + 00H message
36AE
GOSUB to 36FDH to display a message to both the screen AND the RS-232, and RETurn once sent. Only Registers A and HL are affected. In this case, the message is Found Baud Rate + 00H.

From what I can tell, if we are here, Register C is still holding FF as it was set in 3682H and DE is pointing the to screen location after the message. I can’t test because I cannot figure out how to get TRS80GP to fake RS-232 responses.

36B1
LD A,C
Let Register A = Register C.
36B2
AND 0FH
Mask Register A by ANDing it againt 00001111 to keep only bits 0-3
36B4
ADD 41H
Let A = A + 41H
36B6
LD (DE), A
Put the contents of Register A into the memory location pointed to by Register Pair DE.
36B7
IN A,(EBH)
Fetch a byte from the RS-232 (Port EBH) and put it into Register A.
NOTE: Port EBH is the RS-232C Data Register. It contains the data received from the RS-232C.

Start of a loop to keep getting bytes from the RS-232 until a FFH is received.

36B9
GOSUB to 36F1H to wait for DATA READY from the RS-232 and return either NZ if the RS-232 threw an error (with Register A holding the 3 RS-232 error bits), or Z with A holding a byte from the RS-232. Only Register A is affected.
36BC
If the RS-232 threw an error, JUMP DOWN to 36D1H to show a LOAD ERROR message and restart the BOOT FROM RS-232 routine.
36BE
CP FFH
Compare the byte received from the RS-232 (held in Register A) with FFH. If Register A equals FFH, the Z FLAG is set; otherwise the NZ FLAG is set.
36C0
If the byte received from the RS-232 (held in Register A) is not FFH, LOOP back to 36B9H<.

If we are here, we received a FFH from the RS-232, so display LOADING and continue on.

36C2
LD DE,0244H
Let Register Pair DE = 0244H to point to a screen location.
36C5
LD HL,3517H
Let Register Pair HL = 3517H to point to the message Loading… + 00H.
36C8
GOSUB to 36FDH to display a message to both the screen AND the RS-232, and RETurn once sent. Only Registers A and HL are affected. In this case, the message is Loading… + 00H.
36CB
GOSUB to 3721H to load the boot routine from the RS-232 and set HL with the transfer address. If there is any error, NZ is set.
36CE
If there was an error loading the boot routine via 3721H, the NZ FLAG will be set. If so, JUMP to the next routine at 36D1H to display an error and restart the BOOT FROM RS-232 routine.

If we are here, then the load from RS-232 completed without error and HL contains the transfer address.

36D0
JP (HL)
Jump to the address in HL to run the code transferred from the RS-232.

36D1H – BOOT FROM RS-232 – This routine puts a LOAD ERROR message along with a modified version of the error code received on the screen, and then restarts the BOOT FROM RS-232 routine again. This routine is JUMPed to from either 36BCH (if got the 55H’s ok, but were looking for FFH’s and got an error instead) or 36CEH (if we were already loading from the RS-232 but then got an error). When 36F1H exits with an error, A holds only the error bits returned by the RS-232 Controller (bits 3, 4, and 5).

36D1
RRCA
Rotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. RS-232 error bits 5, 4, and 3 are moved to Bits 4, 3, and 2.
36D2
RRCA
Rotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. RS-232 error bits 5, 4, and 3 are moved to Bits 3, 2, and 1.
36D3
RRCA
Rotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. RS-232 error bits 5, 4, and 3 are moved to Bits 2, 1, and 0.
36D4
PUSH AF
Save the rotated RS-232 error bits held in Register A to the STACK.
36D5
GOSUB to 33CEH to clear the screen and reset the Model 4.
36D8
LD DE,03C4H
Let Register Pair DE = 03C4H to point to a location on the screen..
36DB
LD HL,3522H
Let Register Pair HL = 3522H to point to the Load Error + 00H message
36DE
GOSUB to 36FDH to display a message to both the screen AND the RS-232, and RETurn once sent. Only Registers A and HL are affected. In this case, the message is Load Error + 00H.
36E1
POP AF
Restore Register A (i.e., the rotated RS-232 error bits) from the STACK.
36E2
ADD 41H
Let A = A + 41H so that A holds 41 + an offset of the value of the error bits.
36E4
INC DE
Bump Register Pair DE by 1 to point to the screen location after the displayed Load Error message.
36E5
LD (DE), A
Put the contents of Register A (an offsetted value of the error bits) into the memory location pointed to by Register Pair DE (a screen address).
36E6
LD BC,0000H
Let Register Pair BC = 0000H to set up a delay loop of FFFFH.
36E9
DJNZ 36E9H
Loop back to THIS INSTRUCTION and keep looping until Register B = 0.
36EB
DEC C
Decrement the value held in Register C by 1.
36EC
Loop back to 36E9H until C has hit 0.
36EE
JUMP to 3661H to put start the whole process of trying to boot from the RS-232 again.

36F1H – BOOT FROM RS-232 – This SUBROUTINE waits for DATA READY, and then either returns NZ with an error or fetches a byte from the RS-232 into Register A with the Z Flag set.

36F1
IN A,(EAH)
Fetch the status of the RS-232 Controller (Port EAH) into Register A.
NOTE: Port EAH is the RS-232 UART Control Register/Status Register. Input:
  • Bit 3: Parity error (1=True)
  • Bit 4: Framing Error (1=True)
  • Bit 5: Overrun (1=True)
  • Bit 6: Data Sent (1=True)
  • Bit 7: Data Ready (1=True)
36F3
BIT 7,A
Test Bit 7 of A, which is the bit for a “Data Ready”.
36F5
If the DATA READY is not indicated (because Bit 7 is 0), LOOP back to the top of this routine (36F1H) and poll again.

If we are here, then DATA READY was set and Register A holds the RS-232 Controller Status byte.

36F7
AND 38H
Mask Register A by ANDing it against 0011 1000 to leave only the error bits (bits 3, 4, and 5) live.
36F9
RET NZ
If NZ is set, there there was some error from the RS-232 Controller Status (we don’t care which it was), so RETURN.

If we are here, then DATA READY was set and we have no errors from the RS-232 Controller.

36FA
IN A,(EBH)
Fetch a byte from the RS-232 (Port EBH) and put it into Register A.
NOTE: Port EBH is the RS-232C Data Register. It contains the data received from the RS-232C.
36FC
RET
RETurn to caller.

36FDH – BOOT FROM RS-232 – This SUBROUTINE is called from a number of locations to display a message to both the screen AND the RS-232, and RETurn once sent. Only Registers A and HL are affected. Before entry HL and DE are specifically set for a message display:

  • If called from 36AE, the message will be Found Baud Rate + 00H
  • If called from 36C8, the message will be Loading… + 00H
  • If called from 36DE, the message will be Load Error + 00H
36FD
PUSH HL
Save the contents of Register Pair HL to the top of the STACK.
36FE
GOSUB to 345EH display the messages pointed to by HL at the screen location of DE.
3701
POP HL
Restore the value at the top of the STACK to Register Pair HL.

This is the beginning of a loop. On Entry HL points to the first letter of the message which was displayed.

3702
IN A,(EAH)
Fetch the status of the RS-232 Controller (Port EAH) into Register A.
NOTE: Port EAH is the RS-232 UART Control Register/Status Register. Input:
  • Bit 3: Parity error (1=True)
  • Bit 4: Framing Error (1=True)
  • Bit 5: Overrun (1=True)
  • Bit 6: Data Sent (1=True)
  • Bit 7: Data Ready (1=True)
3704
AND 40H
MASK Register A against 0100 0000 to leave only Bit 6 live. Bit 6 would be the “Data Sent” bit and will be NZ if TRUE and Z if FALSE.
3706
If the RS-232 reports DATA NOT SENT (Because Bit 6 is 0), JUMP back 2 instructions (to 3702H) to read the status again.

If we are here, then the RS-232 reported DATA SENT. For some reason, the next instructions take the message which was displayed at the beginning of the routine, and push them out the RS-232 port, RETurning once it is completely sent.

3708
LD A,(HL)
Fetch the value stored in the memory location pointed to by Register Pair HL into Register A.
3709
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
370A
RET Z
If the Z flag is set because the memory contents pointed to by Register HL were zero (meaning, the end of the message delimiter was found), then RETURN.
370B
OUT (EBH),A
OUTput the value held in Register A (which was the memory contents pointed to by Register HL) to Port EBH.
NOTE: Port EBH is the RS-232C Data Register. It contains the data to be sent to the RS-232C.
370D
Bump Register Pair HL by 1.
370E
LOOP back to 3702H until the entire message has been pushed out the RS-232 port.

3710H – BOOT FROM RS-232 – Message Storage Area for RS-232 Test.

3710
MESSAGE
“Found Baud Rate “,00h

3721H – BOOT FROM RS-232 – This SUBROUTINE loads the boot routine from the RS-232 and sets HL with the transfer address. It is called from 36CBH. If an error occurs, NZ is set and Register A will hold the 3 error bits)

3721
LD E,FFH
Let Register E = FFH. This value is set for the main loop at 3723H starts.

Start of a loop which encompasses this whole subroutine.

3723
GOSUB to 36F1H to wait for DATA READY from the RS-232 and return either NZ if the RS-232 threw an error (with Register A holding the 3 RS-232 error bits), or Z with A holding a byte from the RS-232. Only Register A is affected.
3726
RET NZ
If the RS-232 threw an error, RETURN with NZ set and with Register A holding the 3 RS-232 error bits.
3727
DEC A
If we are here then we got a byte from the RS-232 and it is held in Register A. Decrement that value by 1 to make testing for ZERO easier.
3728
If the byte we received was an 01H, JUMP to 373BH.
NOTE: An 01H, when applied to an executable program, indicates a LOAD BLOCK, so we need to process those bytes.
372A
DEC A
Decrement the value held in Register A by 1 again.
372B
If the byte we received was something other than a 02H, JUMP to 3750H.

If we are here, the RS-232 gave us 02H. An 02H in an executable program signals that the TRANSFER ADDRESS is coming. It then has 3 bytes afterwards — the length of the block, then the LSB of the transfer address, and then the MSB of the transfer address.

372D
GOSUB to 36F1H to wait for DATA READY from the RS-232 and return either NZ if the RS-232 threw an error (with Register A holding the 3 RS-232 error bits), or Z with A holding a byte from the RS-232. Only Register A is affected.
3730
RET NZ
If the RS-232 threw an error, RETURN with NZ set and with Register A holding the 3 RS-232 error bits.

3731 – This is both a pass through (if an 02H was received) and a a JUMP point from 3742H (if an 01H was received). In either case, the RS-232 is about to provide a load address in LSB, MSB order, so we need to capture that into Register HL.

3731
GOSUB to 36F1H to wait for DATA READY from the RS-232 and return either NZ if the RS-232 threw an error (with Register A holding the 3 RS-232 error bits), or Z with A holding a byte from the RS-232. Only Register A is affected.
3734
RET NZ
If the RS-232 threw an error, RETURN with NZ set and with Register A holding the 3 RS-232 error bits.

If we are here, the RS-232 gave us 2 non-errors after giving us a 02H, and Register A holds the 2nd byte the RS-232 sent after the 02H or an 01H (if called from there), which is the LSB of the transfer address.

3735
LD L,A
LET Register L = Register A (the 2nd byte the RS-232 sent after the 02H or 01H).
3736
GOSUB to 36F1H to wait for DATA READY from the RS-232 and return either NZ if the RS-232 threw an error (with Register A holding the 3 RS-232 error bits), or Z with A holding a byte from the RS-232. Only Register A is affected.
3739
LD H,A
LET Register H = Register A (the 3rd byte the RS-232 sent after the 02H or 01H), which is the MSB of the transfer address.
373A
RET
RETurn to caller.

373BH – BOOT FROM RS-232 – Process the LOAD BLOCK. This routine is jumped to if we received a 01H byte from the RS-232. An 01H, when applied to a executable program, indicates a LOAD BLOCK.

373B
GOSUB to 36F1H to wait for DATA READY from the RS-232 and return either NZ if the RS-232 threw an error (with Register A holding the 3 RS-232 error bits), or Z with A holding a byte from the RS-232. Only Register A is affected.
373E
RET NZ
If the RS-232 threw an error, RETURN with NZ set and with Register A holding the 3 RS-232 error bits.
373F
DEC A
The first byte after an 01H indicates how many bytes the block is. It is either 00, 01, or 02 to indicate whether the block is 254, 255, or 256 bytes. Whatever that byte is, DECremenet it by 1.
3740
DEC A
and Decrement it again.
3741
LD B,A
Put the block code – 2 into Register B. This sets up a loop to read the appropriate number of bytes.
3742
GOSUB to 3731H to get the next 2 bytes from the RS-232 and put them into HL.
3745
RET NZ
The address to get the load address from the RS-232 provided an error, RETURN.

If we’re here we have no error from the RS-232 and HL has the LOAD address. This is the start of a loop of B passes.

3746
GOSUB to 36F1H to wait for DATA READY from the RS-232 and return either NZ if the RS-232 threw an error (with Register A holding the 3 RS-232 error bits), or Z with A holding a byte from the RS-232. Only Register A is affected.
3749
RET NZ
If the RS-232 threw an error, RETURN with NZ set and with Register A holding the 3 RS-232 error bits.
374A
LD (HL),A
Put the bytes received from the RS-232 into the memory location pointed to by HL.
374B
Bump Register Pair HL by 1.
374C
LOOP back to 3746H and keep looping until Register B = 0 (meaning the number of bytes indicated by the first byte after the 01H has been read).
374E
LOOP back to 3723H for the next block.

3750H – This routine is jumped to if we received anything other than a 01H or 02H from the RS-232

3750
GOSUB to 36F1H to wait for DATA READY from the RS-232 and return either NZ if the RS-232 threw an error (with Register A holding the 3 RS-232 error bits), or Z with A holding a byte from the RS-232. Only Register A is affected.
3753
LD B,A
Put Register A into Register B to set up a DJNZ loop for that many iterations.

Start of a DJNZ loop of that number of bytes.

3754
GOSUB to 36F1H to wait for DATA READY from the RS-232 and return either NZ if the RS-232 threw an error (with Register A holding the 3 RS-232 error bits), or Z with A holding a byte from the RS-232. Only Register A is affected.
3757
RET NZ
If the RS-232 threw an error, RETURN with NZ set and with Register A holding the 3 RS-232 error bits.
3758
Loop back to 3754H and keep looping until Register B = 0.
375A
JUMP back to the top of the routine at 3723H.

375C – I don’t see what jumps to this.

375C
OR A
Set the flags based on Register A.
375D
RET Z
If the Z flag is set, RETURN.
375E
LD B,A
Let Register B = Register A to set up a DJNZ loop for that number of iterations.
375F
XOR A
Zero register A and clear the flags.

Start of a Loop

3760
ADD A,C
Let A = A + C
3761
Loop back to 3760H and keep looping until Register B = 0.
3763
RET
RETurn to caller.
3764
LD C,A
Let Register C = Register A.

3765H – DISKETTE – This SUBROUTINE was called from 3380H to set Register A=1 and pass through

3765
LD A,01H
Let A = 01H

3767H – DISKETTE and BOOT – Set HL=4300, B=0CH, and JUMP to 3546 to decrement A, reset the floppy controller and waits, sets C to 01-08H based on the drive number selected before exiting to 355DH.

3767
LD HL,4300H
Let HL = 4300H as a memory storage area.
376A
LD B,0CH
Let Register B = 0CH.
376C
JUMP to 3546H.

376FH and on … Just FF’s.

What went into this:

In order to do this page I needed to use a few tools:

  • TRDL, to convert a ROM/BIN file to a CMD file, as diassemblers won’t disassemble a ROM/BIN file. The command line was trld file.rom file.cmd. Lawrence Kesteloot’s TRS80-TOOL can do this as well, effective as of v2.4.1, with trs80-tool convert –start 0x1000 in.rom out.cmd
  • David Goben’s Model I/III/4 Disassembler v1.4. It is the only disassembler which seemed to give me everything needed, including NO labels, no errors in disassembling (XOR A,A), the hex and ASCII, uniform columns so I could manipulate them easily, etc.
  • TRS80GP because David Goben’s disassembler produced unusable output when I tried it with saving source to disk, but produced wonderful output when sent to the printer; and TRS80GP allows you to save the printer output!.
  • Microsoft Visual Basic 6. Had to write a program to parse the columns and to add HTML cross reference jumps for jumps. I also used my prior disassemblies to populate the comment field. A waste in terms of things like PUSH HL, but a godsend for jumps to code in ROM A and B, or explaining how the C and NC jumps work.
  • TRS80GP’s amazing Z-80 Debugger.

and the following pages to help with the commenting:

10 CLS
20 INPUT Y
30 CLS
40 FORX=1 TO 7
50 PRINT "        10        20         30       40        50"
60 PRINT "12345689012345678901234567890123456789012345678901234567"
70 NEXT X
80 PRINT@0,Y;
90 POKE Y,88
100 IF INKEY$="" THEN 100