TRS-80 DOS – TRSDOS v1.3 – BOOT/SYS Disassembled

4000
4000H is where Level II BASIC jumps when a RST 08H is called.
4003
4003H is where Level II BASIC jumps when a RST 10H is called.
4006
4006H is where Level II BASIC jumps when a RST 18H is called.
4009
4009H is where Level II BASIC jumps when a RST 20H is called.
400C
400CH is the RST 28H Vector (i.e. OVERLAY LOADER) … this is a JUMP to 4B82H.
400F
400FH is the RST 30H Vector (i.e., DEBUG) … this is a JUMP to 440DH.
4012
4012H is the RST 38H Vector (i.e., INTERRUPTS) … this is a JUMP to 3018H in the ROM.
402D
JUMP to 4296H.

NOTE: 4296H is the DOS READY jump routine.
4030
LD A,A1H
LET Register A = A1H (1010 0001).
In the context of setting up Register A for a RST 28H call, x010 is the routine number within the overlay to run (i.e, routine 2) and 0001 is the overlay number being called (i.e., overlay 1). This is the NON-DOS error
4032
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
4033
JUMP to 44B0H.

NOTE: 44B0H is the INPUT/OUTPUT DISPATCHER.
4036-
403C
Keyboard Bit Image
403D
JUMP to 35FAH.

NOTE: This is INTERRUPT VECTOR 3. It isn’t used.
4040
JUMP to 35FAH.

NOTE: This is INTERRUPT VECTOR 6. It isn’t used.
4043
JUMP to 35FAH.

NOTE: This is INTERRUPT VECTOR 7. It isn’t used.
4046
JUMP to 3529H.

NOTE: This is INTERRUPT VECTOR 2 and JUMPs to 3529H in the Model III ROM. It sets and flashes the cursor and updates the clock/calendar.
4049
JUMP to 42BDH.

NOTE: This is NON-MASKABLE INTERRUPT VECTOR.
4243
JUMP to 022EH.

NOTE: This is VECTOR for a BREAK when hit during RS-232 or CASSETTE I/O.
4246
JUMP to 35FAH.

NOTE: This is INTERRUPT VECTOR 4.
4249
JUMP to 35FAH.

NOTE: This is INTERRUPT VECTOR 5.

427BH – Start of the Overlay in Earnest.

427B
LD A,9AH
LET Register A = 9AH (1001 1010).
In the context of setting up Register A for a RST 28H call, x001 is the routine number within the overlay to run (i.e, routine 1) and 1010 is the overlay number being called (i.e., overlay 10). This would be the BASIC Error Routine
427D
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
427E
LD A,8CH
LET Register A = 8CH (1000 1100).
In the context of setting up Register A for a RST 28H call, x000 is the routine number within the overlay to run (i.e, routine 0) and 1100 is the overlay number being called (i.e., overlay 12). This would be the BASIC Renumber Routine
4280
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
4281
LD A,9CH
LET Register A = 9CH (1001 1100).
In the context of setting up Register A for a RST 28H call, x001 is the routine number within the overlay to run (i.e, routine 1) and 1100 is the overlay number being called (i.e., overlay 12).
4283
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
4284
LD A,8DH
LET Register A = 8DH (1000 1101).
In the context of setting up Register A for a RST 28H call, x000 is the routine number within the overlay to run (i.e, routine 0) and 1101 is the overlay number being called (i.e., overlay 13).
4286
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
4287
LD A,9DH
LET Register A = 9DH (1001 1101).
In the context of setting up Register A for a RST 28H call, x001 is the routine number within the overlay to run (i.e, routine 1) and 1101 is the overlay number being called (i.e., overlay 13).
4289
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
428A
LD A,ADH
LET Register A = ADH (1010 1101).
In the context of setting up Register A for a RST 28H call, x010 is the routine number within the overlay to run (i.e, routine 2) and 1101 is the overlay number being called (i.e., overlay 13).
428C
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
428D
LD A,8EH
LET Register A = 8EH (1000 1110).
In the context of setting up Register A for a RST 28H call, x000 is the routine number within the overlay to run (i.e, routine 0) and 1110 is the overlay number being called (i.e., overlay 14). This will return the file pointers to the user.
428F
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
4290
LD A,9EH
LET Register A = 9EH (1001 1110).
In the context of setting up Register A for a RST 28H call, x001 is the routine number within the overlay to run (i.e, routine 1) and 1110 is the overlay number being called (i.e., overlay 14). This will return the directory of files in memory.
4292
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
4293
LD A,8FH
LET Register A = 8FH (1000 1111).
In the context of setting up Register A for a RST 28H call, x000 is the routine number within the overlay to run (i.e, routine 0) and 1111 is the overlay number being called (i.e., overlay 15). There is no overlay 15.
4295
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
4296
LD A,91H
LET Register A = 91H (1001 0001).
In the context of setting up Register A for a RST 28H call, x001 is the routine number within the overlay to run (i.e, routine 1) and 0001 is the overlay number being called (i.e., overlay 1). This is the normal DOS command processor (DOS READY).
4298
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
4299
LD A,B1H
LET Register A = B1H (1011 0001).
In the context of setting up Register A for a RST 28H call, x011 is the routine number within the overlay to run (i.e, routine 3) and 0001 is the overlay number being called (i.e., overlay 1). This will execute a DOS COMMAND and RETURN to DOS READY.
429B
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
429C
LD A,F1H
LET Register A = F1H (1111 0001).
In the context of setting up Register A for a RST 28H call, x111 is the routine number within the overlay to run (i.e, routine 7) and 0001 is the overlay number being called (i.e., overlay 1). This will execute a DOS COMMAND and RETURN to the USER PROGRAM.
429E
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
429F
LD A,A7H
LET Register A = A7H (1010 0111).
In the context of setting up Register A for a RST 28H call, x010 is the routine number within the overlay to run (i.e, routine 2) and 0111 is the overlay number being called (i.e., overlay 7). This will execute a FORMAT COMMAND.
42A1
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
42A2
LD A,97H
LET Register A = 97H (1001 0111).
In the context of setting up Register A for a RST 28H call, x001 is the routine number within the overlay to run (i.e, routine 1) and 0111 is the overlay number being called (i.e., overlay 7). This will execute a BACKUP COMMAND.
42A4
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
42A5
LD A,98H
LET Register A = 98H (1001 1000).
In the context of setting up Register A for a RST 28H call, x001 is the routine number within the overlay to run (i.e, routine 1) and 1000 is the overlay number being called (i.e., overlay 8). This will execute a HELP COMMAND.
42A7
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
42A8
LD A,99H
LET Register A = 99H (1001 1001).
In the context of setting up Register A for a RST 28H call, x001 is the routine number within the overlay to run (i.e, routine 1) and 1001 is the overlay number being called (i.e., overlay 9). This will process LIBRARY commands.
42AA
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
42AB
LD A,9BH
LET Register A = 9BH (1001 1011).
In the context of setting up Register A for a RST 28H call, x001 is the routine number within the overlay to run (i.e, routine 1) and 1011 is the overlay number being called (i.e., overlay 11). This will process MORE LIBRARY commands.
42AD
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
42AE
If we are here then there was a bad overlay request, so JUMP to 4B00H. Note: This memory location may be changed to 4A85H (or other values from other overlays) based on whether OVERLAY 1 has been loaded.
42B1
DEFB …
Byte Storage.
42B2
DEFB …
Byte Storage.
42B3
DEFB …
Byte Storage.
42B4
DEFB …
Byte Storage.
42B5
DEFB …
Byte Storage.
42B6
DEFB …
Byte Storage.
42B7
DEFB …
Byte Storage.
42B8
DEFB …
Byte Storage.
42B9
DEFB …
Byte Storage.
42BA
LD A,86H
LET Register A = 86H (1000 0110).
In the context of setting up Register A for a RST 28H call, x000 is the routine number within the overlay to run (i.e, routine 0) and 0110 is the overlay number being called (i.e., overlay 6). This will process EVEN MORE LIBRARY commands.
42BC
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.
42BD
RETN
RETN is used at the end of a NMI service routine. It POPs the value at the top of STACK into PC and copies the value of IFF2 into IFF1 (so that maskable interrupts are allowed to continue).

42BFH – Set the clock heartbeat counter to 25 and JUMP to ROM.

42BF
LD A,(4216H)
Fetch the value stored at memory location 4216H and put it into Register A.

NOTE: 4216H is the storage location for the Clock Data: Heartbeat Counter.
42C2
CP 1EH
Compare the value held in Register A against 1EH. Results: If Register A equals 1EH, the Z FLAG is set; otherwise the NZ FLAG is set.
42C4
If the NZ FLAG (Not Zero) is set, JUMP to 42CBH.
42C6
LD A,19H
LET Register A = 19H (Decimal: 25).
42C8
LD (4216H),A
Store the value held in Register A into the memory location 4216H.

NOTE: 4216H is the storage location for the Clock Data: Heartbeat Counter.
42CB
JUMP to 3529H.

NOTE: 3529H is in the Model III ROM. It sets and flashes the cursor and updates the clock/calendar.

42CEH – Execute the KILL command. On entry, DE needs to point to the memory location which states whether a file is currently open or not

42CE
LD A,(DE)
Fetch the value stored at memory location pointed to by Register Pair DE and put it into Register A. This should indicate if the file is currently open or not.
42CF
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A.
42D0
If the SIGN FLAG is set then the file is currently open, JUMP away to 42D9H.
42D3
PUSH DE
Preseve Register DE to the top of the STACK.
42D4
GOSUB to 4424H to open the file.
42D7
POP DE
Restore Register Pair DE from the top of the STACK.
42D8
RET NZ
If the NZ FLAG (Not Zero) is set then we have an error, so RETurn to the caller.
42D9
LD A,A3H
If we are here then there was no error, so LET Register A = A3H (1010 0011).
In the context of setting up Register A for a RST 28H call, x010 is the routine number within the overlay to run (i.e, routine 2) and 0011 is the overlay number being called (i.e., overlay 3).
42DB
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.

42DCH – Reset the FDC (including restore the drive to track 0).

42DC
GOSUB to 4566H to reset the FDC.
42DF
AND 10H
MASK the value of Register A against 10H (0001 0000). This has the effect of turning off bits 7, 6, 5, 3, 2, 1, 0, leaving only bit 4 active. Bit 4 from Port F0H is the flag for RECORD NOT FOUND.
42E1
RET Z
If the Z FLAG (Zero) is set then we found the record and have no error so RETurn to the caller.
42E2
PUSH BC
PUSH Register BC to the top of the STACK.
42E3
PUSH DE
PUSH Register DE to the top of the STACK.
42E4
PUSH HL
PUSH Register HL to the top of the STACK.
42E5
LD B,00H
Register Pair BC needs to be set to be the offset in the track table, so start off by setting Register B = 00H.
42E7
LD HL,445BH
LET Register Pair HL = 445BH, which is the track table in RAM.
42EA
ADD HL,BC
ADD the value held in Register Pair BC to Register Pair HL so that HL now points to the track table as offset by the approriate offset in BC.
42EB
LD (HL),00H
Store 00H into the memory location pointed to by Register Pair HL, which marks that particular track as track 0.
42ED
LD A,0CH
LET Register A = 0CH (0000 1100).
When this bit pattern in sent to Port F0H (the Floppy Disk Control Port) and bits 7-4 are 0000, this is a RESTORE command and is a Type I command. Bits 0-1 are the stepping rate (00=6ms, 01=12ms, 10=20ms, 11=30ms), Bit 2 is the Track Number Verify Flag (0=No Verify), and Bit 3 is the Head Load Flag (1=Load Head).

When activated, the Track Register is checked, and if its 0 (meaning at Track 0) an interrupt is generated. If it is not 0, stepping pulses are issued until the TR00 goes to 0, and then an interrupt is generated.
42EF
OUTput the value held in Register A to port F0H.

NOTE: Port F0H is the Floppy Disk Command/Status Register. OUTputting to Port F0H cannot be simply summarized as there are more than 11 variables held in the bit pattern.
42F1
POP HL
Restore Register Pair HL from the top of the STACK
42F2
POP DE
Restore Register Pair DE from the top of the STACK
42F3
POP BC
Restore Register Pair BC from the top of the STACK
42F4
RET
RETurn to the caller.

42FFH-43FF – Data Storage Area.

42FF
00H
Storage Byte for whether a password check is skipped or undertaken in an OPEN command. If Bit 0 is set, the check is skipped
4300-
43FF
00H
256 Byte Storage Area for Disk I/O

4400H – Short Delay Routine to Accomodate the FDC.

4400
PUSH AF
PUSH Register AF to the top of the STACK.
4401
POP AF
MOVE the value held at the stop of the STACK into Register Pair AF.
4402
PUSH AF
PUSH Register AF to the top of the STACK.
4403
POP AF
MOVE the value held at the stop of the STACK into Register Pair AF.
4404
PUSH AF
PUSH Register AF to the top of the STACK.
4405
POP AF
MOVE the value held at the stop of the STACK into Register Pair AF.
4406
PUSH AF
PUSH Register AF to the top of the STACK.
4407
POP AF
MOVE the value held at the stop of the STACK into Register Pair AF.
4408
RET
RETurn to the caller.

4409H – Process DOS Errors.

4409
PUSH AF
Since an overlay call uses Register A, which is otherwise holding the DOS ERROR CODE, PUSH Register AF to the top of the STACK.
440A
LD A,84H
LET Register A = 84H (1000 0100).
In the context of setting up Register A for a RST 28H call, x000 is the routine number within the overlay to run (i.e, routine 0) and 0100 is the overlay number being called (i.e., overlay 4).
440C
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.

440DH – Process DEBUG.

440D
PUSH AF
Since an overlay call uses Register A, PUSH Register AF to the top of the STACK to save Register A.
440E
LD A,85H
LET Register A = 85H (1000 0101).
In the context of setting up Register A for a RST 28H call, x000 is the routine number within the overlay to run (i.e, routine 0) and 0101 is the overlay number being called (i.e., overlay 5).
4410
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.

4411H – Data Storage Area

4411
Storage for the MEMORY PROTECT address.
4413
Storage for the NUMBER OF DISK DRIVES in the system.
4414
Storage for the NUMBER OF THE OVERLAY CURRENTLY IN MEMORY.
4415
Storage for the ADDRESS OF THE END OF PHYSICAL MEMORY.

4419H – Routine to display a SHORT DIRECTORY to the screen.

4419
LD A,8AH
LET Register A = 8AH (1000 1010).
In the context of setting up Register A for a RST 28H call, x000 is the routine number within the overlay to run (i.e, routine 0) and 1010 is the overlay number being called (i.e., overlay 10).
441B
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.

441CH – Routine to check the syntax of a filespec before it is used.

441C
LD A,C1H
LET Register A = C1H (1100 0001).
In the context of setting up Register A for a RST 28H call, x100 is the routine number within the overlay to run (i.e, routine 4) and 0001 is the overlay number being called (i.e., overlay 1).
441E
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.

4420H – Routine to INIT a disk file (i.e., create or open).

4420
LD A,A2H
LET Register A = A2H (1010 0010).
In the context of setting up Register A for a RST 28H call, x010 is the routine number within the overlay to run (i.e, routine 2) and 0010 is the overlay number being called (i.e., overlay 2).
4422
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.

4423H – Data Storage Area

4423
00H
Storage for the SAVED DISK DRIVE SELECT CODE.

4424H – Routine to OPEN A DISK FILE for use

LD A,92H
LET Register A = 92H (1001 0010).
In the context of setting up Register A for a RST 28H call, x001 is the routine number within the overlay to run (i.e, routine 1) and 0010 is the overlay number being called (i.e., overlay 2).
4426
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.

4427H – Data Storage Area

4427
00H
Storage for the CURRENTLY SELECTED DISK DRIVE NUMBR.

4428H – Routine to CLOSE A DISK FILE

4428
LD A,93H
LET Register A = 93H (1001 0011).
In the context of setting up Register A for a RST 28H call, x001 is the routine number within the overlay to run (i.e, routine 1) and 0011 is the overlay number being called (i.e., overlay 3).
442A
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.

442BH – Data Storage Area

442B
00H
Storage for the LOAD/EXECUTE flag when the disk loader is called. Bit 7 – Z=EXECUTE, NZ=LOAD

442CH – Vector for the KILL command

442C
JUMP to 42CEH.

442FH – Data Storage Area

442F
00H
Storage for the ROUTE/DUAL flag. 80H=ROUTE, FFH=DUAL, 00H=Neither

4430H – Various Jump Vectors

4430
JUMP to 4BE6H to LOAD PROGRAM INTO RAM.
4433
JUMP to 4BD9H to LOAD AND EXECUTE PROGRAM.
4436
JUMP to 4737H which is the READ VECTOR.
4439
JUMP to 47B3H which is the WRITE VECTOR.
443C
JUMP to 47B3H which is the VERIFY VECTOR when VERIFY is OFF. When VERIFY is ON, this JUMP is changed in RAM to 47CCH.
443F
JUMP to 4720H which is the REWIND VECTOR.
4442
JUMP to 46CFH which is the POSN VECTOR.
4445
JUMP to 4701H which is the BACKSPACE VECTOR.
4448
JUMP to 4729H which is the POSEOF VECTOR.

444BH – This routine will add a default extension to a filespec

444B
LD A,D1H
LET Register A = D1H (1101 0001).
In the context of setting up Register A for a RST 28H call, x101 is the routine number within the overlay to run (i.e, routine 5) and 0001 is the overlay number being called (i.e., overlay 1).
444D
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.

444EH – Various Jump Vectors

444E
JUMP to 4B55H which is the DMULT VECTOR – Multiply Register Pair HL by Register A.
4451
JUMP to 4B6FH which is the DIVIDE VECTOR.

4454H – This routine will parse a parameter string.

4454
LD A,E1H
LET Register A = E1H (1110 0001).
In the context of setting up Register A for a RST 28H call, x110 is the routine number within the overlay to run (i.e, routine 6) and 0001 is the overlay number being called (i.e., overlay 1).
4456
Call RST 28H run a specific routine in a specific DOS OVERLAY, all as held in Register A.

4457H – Data Storage Area

4457
Table of Directory Tracks (4 Bytes)
445B
Table of Current Head Positions (4 Bytes)
4463
Storage for the old system disk side number
4465
Storage for FILENAME/EXT.PASSWORD:D

4497H – Data Storage Area for the System Overlay File Control Block

4497
FCB + 00 – This holds the FILE TYPE byte
4498
FCB + 01 – This holds the OPTIONS byte. When paired with IX+01H, this can be the LSB of the device driver OR the new DCB if not working off disk.
  • Bit 0, 1, 2 = Access Level Permitted, 0=Yes
  • Bit 4 = Unwritten data in the buffer, 1=yes
  • Bit 5 = Is sector loaded, 0=Yes
  • Bit 6 = RANDOM I/O
  • Bit 7 = BIT 7 = FILE TYPE. 1=BYTE LEVEL I/O, 0=SECTOR LEVEL I/O.
4499
FCB + 02 – This is currently unused. When paired with IX+01H, this can be the MSB of the device driver OR the new DCB if not working off disk.
449A
FCB + 03 – This holds the BUFFER ADDRESS (2 Bytes)
449C
FCB + 05 – This holds the BYTE POSITION FOR NEXT
449D
FCB + 06 – This holds the DRIVE NUMBER OF THE FILE
449E
FCB + 07 – This holds the LOGICAL FILE NUMBER IN THE DIRECTORY
449F
FCB + 08 – This holds the BYTE POSITION OF THE EOF
44A0
FCB + 09 – This holds the LOGICAL RECORD LENGTH
44A1
FCB + 10 – This holds the SECTOR NUMBER FOR NEXT FILE (2 Bytes)
44A3
FCB + 12 – This holds the SECTOR NUMBER FOR THE EOF (2 Bytes)
44A4
FCB + 13 – This holds the SIDE NUMBER byte
44A5
FCB + 14 – This is currently unused
44A6
FCB + 15 – is currently unused
44A7
FCB + 16 – This holds the FIRST EXTENT ELEMENT

44A9H – Data Storage Area

44A9
Storage for the Saved Caller Address for DOS functions (2 Bytes)
44AB
Storage for the Saved FFCB Address for DOS functions (2 Bytes)

44ADH – Set up Special Index Register Pair IX with the address of the DCB

44AD
PUSH HL
PUSH Register HL (which is holding the NEW DCB) to the top of the STACK.
44AE
POP IX
Restore the NEW DCB from the top of the STACK into Register Pair IX.


44B0H – Continue the I/O Dispatcher Routine which started in ROM

44B0
LD L,(IX+01H)
Fetch the value stored at memory location IX+01H and put it into Register L. Set HL to be the device driver OR the new DCB if not diskette.
44B3
LD H,(IX+02H)
Fetch the value stored at memory location IX+02H and put it into Register H.
44B6
LD A,(IX+00H)
Fetch the value stored at memory location IX+00H and put it into Register A. Fetch the FILE TYPE byte into Register A.
44B9
CP 10H
Compare the value held in Register A against 10H. Results: If Register A equals 10H, the Z FLAG is set; otherwise the NZ FLAG is set.
44BB
If the Z FLAG (Zero) is set then we are needing to ROUTE to a NEW DCB so JUMP BACK to 44ADH to restore HL into IX and start this routine again.
44BD
If the NC FLAG (No Carry) is set then we are needing to ROUTE to a disk file so JUMP to 44C3H.
44BF
LD A,B
LET Register A = Register B, which is holding the KIND of I/O needed.
44C0
CP 02H
Compare the value held in Register A against 02H. This is done with an eye to setting the CARRY FLAG if read. Results:
  • If Register A equals 02H, the Z FLAG is set.
  • If A < 02H, the CARRY FLAG will be set.
  • if A >= 02H, the NO CARRY FLAG will be set.
44C2
JP (HL)
JUMP to (HL).


44C3H – Routine to ROUTE to a Disk File. Register B holds the KIND of I/O needed and Register C holds the byte to be sent.

44C3
SET 7,(IX+01H)
SET (i.e., set as 1) BIT 7 of Register (IX+01H) which is the OPTIONS byte. This sets the FILE TYPE for BYTE LEVEL I/O.
44C7
GOSUB to 4881H to set up for DISK I/O. This routine requires that Register Pair DE be pointing to the location in the buffer.
44CA
LD A,B
LET Register A = Register B, which is holding the KIND of I/O needed.
44CB
CP 02H
Compare the value held in Register A against 02H. This is done with an eye to setting the CARRY FLAG if read. Results:
  • If Register A equals 02H, the Z FLAG is set.
  • If A < 02H, the CARRY FLAG will be set.
  • if A >= 02H, the NO CARRY FLAG will be set.
44CD
LD A,C
LET Register A = Register C so that the byte to send is put into Register A.
44CE
If the NC FLAG (No Carry) is set then we want to WRITE, so JUMP to 4837H.
44D1
Otherwise, JUMP to 4791H to do a READ.


44D4H – Handle DRIVE SELECT. Register C holds the drive number to select.

44D4
LD A,C
LET Register A = Register C (which is the drive number).
44D5
GOSUB to 45E9H to calculate the DRIVE SELECT CODE.
44D8
OR 80H
OR Register A against 80H (1000 0000). This has the effect of turning on bit 7, thus selecting DOUBLE DENSITY when sent to Port F4H.
44DA
LD (4423H),A
Store the value held in Register A into the memory location 4423H.
44DD
LD A,(4423H)
Fetch the value stored at memory location 4423H (which holds the Disk Drive Select Code) and put it into Register A.
44E0
OUTput the value held in Register A to port F4H.

NOTE: Port F4H is the Floppy drive select and options register. For OUTPUT:
  • Bit 0: Drive 0 Select
  • Bit 1: Drive 1 Select
  • Bit 2: Drive 2 Select
  • Bit 3: Drive 3 Select
  • Bit 4: Side Select (0 = Select Side 0, 1 = Select Side 1)
  • Bit 5: Write Precompensation (0 = Disable WP, 1 = Enable WP)
  • Bit 6: Wait State Generation (0 = Disable WSG, 1 = Enable WSG)
  • Bit 7: Density Select (0 = Single/FM, 1 = Double/MFM)
44E2
RET
RETurn to the caller.


44E3H – Position the Disk Read/Write Head to the Proper Track. On entry, Register C holds the drive number, and Register D holds the track number.

44E3
PUSH BC
PUSH Register BC to the top of the STACK.
44E4
PUSH DE
PUSH Register DE to the top of the STACK.
44E5
PUSH HL
PUSH Register HL to the top of the STACK.
44E6
LD A,C
LET Register A = Register C (which holds the drive number).
44E7
LD (4427H),A
Store the value held in Register A (the drive number) into the memory location 4427H (which stores the currently selected disk drive number).
44EA
PUSH BC
Sve Register BC to the top of the STACK.
44EB
GOSUB to 45E9H to calculate the DRIVE SELECT CODE.
44EE
OR 80H
OR Register A against 80H (1000 0000). This has the effect of turning on bit 7, thus selecting DOUBLE DENSITY when sent to Port F4H.
44F0
LD C,A
LET Register C = Register A (which is the drive select code, modified to double density).
44F1
LD A,D
LET Register A = Register D (which is the track number we want).
44F2
CP 16H
Compare the value held in Register A against 16H. Results:
  • If Register A equals 16H, the Z FLAG is set.
  • If A < 16H, the CARRY FLAG will be set.
  • if A >= 16H, the NO CARRY FLAG will be set.
44F4
If the C FLAG (No Carry) is set, then the track number want is less than 16H (which is track 25), skip the next couple of instructions and JUMP to 44F8H.
44F6
SET 5,C
SET (i.e., set as 1) BIT 5 of Register C to enable WRITE PRECOMPENSATION.
44F8
LD A,C
LET Register A = Register C (which is the DRIVE SELECT CODE).
44F9
LD (4423H),A
Store the value held in Register A into the memory location 4423H (which holds the Disk Drive Select Code).
44FC
POP BC
Restore Register Pair from the top of the STACK into Register Pair BC.
44FD
GOSUB to 4547H to wait for the controller.
4500
If the NZ FLAG (Not Zero) is set then that call returned an error, so JUMP to 4536H.
4502
PUSH BC
Save Register BC to the STACK.
4503
LD B,00H
LET Register B = 00H sso that BC will be the OFFSET on the TRACK TABLE.
4505
LD HL,445BH
LET Register Pair HL = 445BH (which is the TRACK TABLE).
4508
ADD HL,BC
ADD the value held in Register Pair BC to Register Pair HL. The results (he entry in the TRACK TABLE for the track we are on) are held in Register Pair HL.
4509
POP BC
Restore Register Pair BC from the STACK.
450A
LD A,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL (which is the entry in the TRACK TABLE for the track we are on) and put it into Register A.
450B
CP D
Compare the value held in Register A (the current track number) against the value held in Register D (the track number we want). Results:
  • If Register A equals D, the Z FLAG is set.
  • If A < D, the CARRY FLAG will be set.
  • if A >= D, the NO CARRY FLAG will be set.
450C
GOSUB to 453AH to seek to the new track.
450F
If the NZ FLAG (Not Zero) is set, then there was an errior, so JUMP to 4536H to handle the error.
4511
BIT 4,A
Test Bit 4 of Register A, which would be a SEEK ERROR.
4513
If the Z FLAG (Zero) is set, meaning that we did not get a SEEK ERROR, JUMP to 4527H.
4515
LD A,0CH
LET Register A = 0CH (0000 1100).
When this bit pattern in sent to Port F0H (the Floppy Disk Control Port) and bits 7-4 are 0000, this is a RESTORE command and is a Type I command. Bits 0-1 are the stepping rate (00=6ms, 01=12ms, 10=20ms, 11=30ms), Bit 2 is the Track Number Verify Flag (0=No Verify), and Bit 3 is the Head Load Flag (1=Load Head).

When activated, the Track Register is checked, and if its 0 (meaning at Track 0) an interrupt is generated. If it is not 0, stepping pulses are issued until the TR00 goes to 0, and then an interrupt is generated.
4517
OUTput the value held in Register A to port F0H.

NOTE: Port F0H is the Floppy Disk Command/Status Register. OUTputting to Port F0H cannot be simply summarized as there are more than 11 variables held in the bit pattern.
4519
GOSUB to 4400H to wait for the FDC to be ready.
451C
INput a byte 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
451E
BIT 2,A
Test Bit 2 of Register A to see if we are at TRACK 0 yet.
4520
If the Z FLAG (Zero) is set then we are not yet at TRACK 0, so LOOP BACK 2 instructions to 451CH.
4522
GOSUB to 453CH to seek the track.
4525
If the NZ FLAG (Not Zero) is set then we had an error in seeking so JUMP FORWARD to 4536H to deal with the error.
4527
PUSH AF
PUSH Register AF to the top of the STACK to save Register A and the FLAGs.
4528
LD A,(4427H)
Fetch the value stored at memory location 4427H (the currently selected disk drive number) and put it into Register A.
452B
LD C,A
LET Register C = Register A.
452C
LD B,00H
LET Register B = 00H. This leaves Register Pair BC holding the currently selected disk drive number.
452E
LD HL,445BH
LET Register Pair HL = 445BH, which is the track table in RAM.
4531
ADD HL,BC
ADD the value held in Register Pair BC to Register Pair HL. The results (the table entry for the desired track) are held in Register Pair HL.
4532
Read the actual diskette drive track number from the FDC by doing an INput from Port F1H.
4534
LD (HL),A
Store the value held in Register A into the memory location pointed to by Register Pair HL (which is the table entry for the desired track).
4535
POP AF
Restore Register A and the FLAGs from the STACK.
4536
POP HL
Restore Register Register Pair HL from the STACK.
4537
POP DE
Restore Register Register Pair DE from the STACK.
4538
POP BC
Restore Register Register Pair BC from the STACK.
4539
RET
RETurn to the caller.


453AH – Prepare to do a TRACK SEEK. On entry, Register A should hold the current track number and Register D should hold the desired track number.

453A
Put the track we want to find (held in Register A) into the TRACK SELECT port for the FDC (Port F1H).
453C
LD A,D
LET Register A = Register D (the desired track number).
453D
OUTput the value held in Register A to the FDC Data Register (Port F3H).
The reason this is going to the data register is that the FDC seek command requires that the Track Register contains the track number of the current position of the head and the Data Register contains the desired track number.
453F
If the NZ FLAG (Not Zero) is set, then the actual track number is not the same as the desired track number so JUMP to 4543H to seek the track.
4541
XOR A
Set Register A to ZERO and clear all Flags.
4542
RET
RETurn to the caller.


4543H – Routine to SEEK a TRACK on the diskette.

4543
LD A,1CH
LET Register A = 1CH (0001 1100).
When this bit pattern in sent to Port F0H (the Floppy Disk Control Port) and bits 7-4 are 0001, this is a SEEK command and is a Type I command. Bits 0-1 are the stepping rate, Bit 2 is the Track Number Verify Flag (0=No Verify), and Bit 3 is the Head Load Flag (1=Load Head).
So long as the Track Register holds the current position of the head and the Data Register contains the desired track number, the FDC will update the Track Register and issue stepping pulses in the appropriate direction until the Track Register and Data Register match. An interrupt is generated when done.
4545
OUTput the value held in Register A to port F0H.

NOTE: Port F0H is the Floppy Disk Command/Status Register. OUTputting to Port F0H cannot be simply summarized as there are more than 11 variables held in the bit pattern.

Now we need to wait for the disk drive to actually finish seeking to the track.

4547
GOSUB to 44DDH to re-select the drive.
454A
GOSUB to 4400H to delay a little bit.
454D
PUSH BC
PUSH Register BC to the top of the STACK.
454E
LD BC,0000H
LET Register Pair BC = 0000H to set up for a counter.
4551
INput a byte 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
4553
BIT 0,A
Test Bit 0 of Register A, which would be the FDC BUSY bit from Port F0H.
4555
If the Z FLAG (Zero) is set, meaning that the FDC is NOT busy, JUMP DOWN to 4564H to continue.
4557
DEC BC
If we’re here, we got FDC BUSY, so DECrement Register Pair BC by 1.
4558
LD A,B
We need to check the counter, but we can’t test Register B, so LET Register A = Register B.
4559
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A.
455A
If the Z FLAG (Zero) is set then we did this FFFFH times, so JUMP to 4561H to error out.
455C
Otherwise, GOSUB to 44DDH to re-select the drive.
455F
LOOP BACK to 4551H until the drive times out.


4561H – Routine to ERROR OUT.

4561
LD A,0CH
LET Register A = 0CH (0000 1100).
4563
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A.
4564
POP BC
MOVE the value held at the stop of the STACK into Register Pair BC.
4565
RET
RETurn to the caller.


4566H – Reset the FDC

4566
PUSH AF
SAVE Register AF to the STACK.
4567
LD A,D0H
LET Register A = D0H (1101 0000).
When this bit pattern in sent to Port F0H (the Floppy Disk Control Port) and bits 7-4 are 1101, this is a FORCE INTERRUPT command and is a Type IV command. Bit 3 designates IMMEDIATE INTERRUPT. Bit 2 designates INDEX PULSE. Bit 1 designates READY to NOT READY TRANSITION. Bit 0 designates NOT READY to READY TRANSITION.
4569
OUTput the value held in Register A to port F0H.

NOTE: Port F0H is the Floppy Disk Command/Status Register. OUTputting to Port F0H cannot be simply summarized as there are more than 11 variables held in the bit pattern.
456B
POP AF
Restore Register Pair AF from the STACK.
456C
RET
RETurn to the caller.


456DH – Routine to WRITE SECTOR TO DISK. Assumes the head is already properly positioned.

456D
LD BC,00F3H
LET Register B = 00H for a counter and LET Register C = the FDC data port F3H.
4570
GOSUB to 44DDH to select the drive.
4573
OR 40H
OR Register A against 40H (0100 0000). This has the effect of turning on bit 6 which enables the WAIT BIT.
4575
LD D,A
LET Register D = Register A (which is holding the modified select code).
4576
GOSUB to 459CH to set the appropriate NMI Vector and clear the FDC status.
4579
LD A,C0H
LET Register A = C0H (1100 0000). When sent to the NMI port of E4H this actives NMI for FDC time-out and Disk Drive time-out
457B
OUTput the value held in Register A to port F4H.

NOTE: Port E4H is the Non-Maskable Interrupt Latch.
457D
LD E,02H
LET Register E = 02H to set up a mask of 0000 0010, which, when applied, would test for CONTROLLER READY.
457F
LD HL,0000H
LET Register Pair HL = 0000H.
NOTE: This value is set elsewhere and is likely to be 4580H.
4582
LD A,A5H
LET Register A = A5H (1010 0101).
When this bit pattern in sent to Port F0H (the Floppy Disk Control Port) and bits 7-5 are 101, this is a WRITE SECTOR command and is a Type II command. Bit 4 is the Multiple Record Flag (0=Single Record), Bit 3 is the Side Compare Flag (0=Side 0), Bit 2 is a 15ms Delay Flag (0=Off), Bit 1 is a Side Compare Flag (0=Disable). Bit 0 sets the Data Address Mark (0=FBH, 1=F8H).
4584
OUTput the value held in Register A to port F0H.

NOTE: Port F0H is the Floppy Disk Command/Status Register. OUTputting to Port F0H cannot be simply summarized as there are more than 11 variables held in the bit pattern.
4586
GOSUB to 4404H for a slight delay.
4589
INput a byte 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
458B
AND E
MASK the value of Register A against the value held in Register E to check for CONTROLLER READY.
458C
If the Z FLAG (Zero) is set then the FDC is not ready so LOOP BACK 2 instructions to 4589H.
458E
OUTI
Write a byte from the memory location pointed to by HL to port C. Register B is DECremented and Register Pair HL is INCremented.
4590
LD B,64H
LET Register B = 64H (0110 0100).
4592
DJNZ 4592H
LOOP back to this very instruction 64H times for a short delay.
4594
LD A,D
LET Register A = Register D (which was holding the drive select code).
4595
OUTput the value held in Register A to port F4H.

NOTE: Port F4H is the Floppy drive select and options register. For OUTPUT:
  • Bit 0: Drive 0 Select
  • Bit 1: Drive 1 Select
  • Bit 2: Drive 2 Select
  • Bit 3: Drive 3 Select
  • Bit 4: Side Select (0 = Select Side 0, 1 = Select Side 1)
  • Bit 5: Write Precompensation (0 = Disable WP, 1 = Enable WP)
  • Bit 6: Wait State Generation (0 = Disable WSG, 1 = Enable WSG)
  • Bit 7: Density Select (0 = Single/FM, 1 = Double/MFM)
4597
OUTI
Write a byte from the memory location pointed to by HL to port C. Register B is DECremented and Register Pair HL is INCremented.
4599
LOOP back to 4595H to keep writing until a NMI exits the loop.


459CH – Routine to set the NMI vector to 45C1H and clear the FDC status.

459C
PUSH HL
SAVE Register HL to the STACK.
459D
LD HL,45ADH
Part 1 of 4 instructions to set the NMI vector to 45C1H.
45A0
LD (404AH),HL
Store the value held in Register HL into the memory location 404AH.

NOTE: 404AH is part of the NMI VECTOR. I do not make believe I understand how that vector is set or how it works.
45A3
LD A,C3H
LET Register A = C3H (1100 0011).
45A5
LD (4049H),A
Store the value held in Register A into the memory location 4049H.

NOTE: 4049H is part of the NMI VECTOR. I do not make believe I understand how that vector is set or how it works.
45A8
INput a byte 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
45AA
POP HL
Restore Register Pair BC from the STACK.
45AB
DI
Disable Maskable Interrupts.
45AC
RET
RETurn to the caller.


45ADH – Routine to clear the NMI vector and disable NMIs.

45AD
XOR A
Set Register A to ZERO and clear all Flags.
45AE
OUTput the value held in Register A to port F4H.

NOTE: Port E4H is the Non-Maskable Interrupt Latch. Sending a 0 to E4 will turn off all Non-Maskable Interrupts.
45B0
LD HL,42BDH
LET Register Pair HL = 42BDH.
45B3
LD (404AH),HL
Store the value held in Register HL (i.e., 42BDH) into the memory location 404AH.

NOTE: 404AH is part of the NMI VECTOR. I do not make believe I understand how that vector is set or how it works.
45B6
JUMP to 45A8H in the prior routine to clear the FDC status and disable Maskable Interrupts, and RETurn.


45B8H – Routine to READ SECTOR FROM DISK. Assumes the head is already properly positioned and that Register A holds the drive select code.

45B8
LD BC,00F3H
LET Register B = 00H for a counter and LET Register C = the FDC data port F3H.
45BB
GOSUB to 44DDH (a short routine to re-select a selected drive).
45BE
LD D,A
Preserve the drive select code (held in Register A) into Register D.
45BF
CALL 459CH
GOSUB to 459CH to set the appropriate NMI Vector and clear the FDC status.
45C2
LD A,C0H
LET Register A = C0H (1100 0000) so that the NMI is programmed for controller and drive time-out when sent to Port E4H.
45C4
OUTput the value held in Register A to port F4H.

NOTE: Port E4H is the Non-Maskable Interrupt Latch. Sending a 0 to E4 will turn off all Non-Maskable Interrupts.
45C6
LD E,02H
LET Register E = 02H (0000 0010) to mask for CONTROLLER READY.
45C8
LD HL,0000H
LET Register Pair HL = 0000H.
45CB
LD A,84H
LET Register A = 84H (1000 0100).
When this bit pattern in sent to Port F0H (the Floppy Disk Control Port) and bits 7-5 are 100, this is a READ SECTOR command and is a Type II command. Bit 4 is the Multiple Record Flag (0=Single Record), Bit 3 is the Side Compare Flag (0=Side 0), Bit 2 is a 15ms Delay Flag (0=Off), Bit 1 is a Side Compare Flag (0=Disable). Bit 0 is supposed to be 0.
45CD
OUTput the value held in Register A to port F0H.

NOTE: Port F0H is the Floppy Disk Command/Status Register.
45CF
GOSUB to 4404H.

NOTE: 4404H is the routine which has a slight delay.
45D2
INput a byte 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
45D4
AND E
MASK the value of Register A against the value held in Register E.
45D5
If the Z FLAG (Zero) is set, meaning CONTROLLER NOT READY, LOOP back 2 entries to poll the FDC again.
45D7
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.
45D9
LD A,D
Restore the drive select code (saved to Register D) back into Register A.
45DA
OR 40H
OR Register A against 40H (0100 0000). This has the effect of turning on bit 6 which is the WAIT STATE ON bit when sent to Port F4H.
45DC
OUTput the value held in Register A to port F4H.

NOTE: Port F4H is the Floppy drive select and options register. For OUTPUT:
  • Bit 0: Drive 0 Select
  • Bit 1: Drive 1 Select
  • Bit 2: Drive 2 Select
  • Bit 3: Drive 3 Select
  • Bit 4: Side Select (0 = Select Side 0, 1 = Select Side 1)
  • Bit 5: Write Precompensation (0 = Disable WP, 1 = Enable WP)
  • Bit 6: Wait State Generation (0 = Disable WSG, 1 = Enable WSG)
  • Bit 7: Density Select (0 = Single/FM, 1 = Double/MFM)
45DE
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.
45E0
If the NZ FLAG (Not Zero) is set then the buffer is not yet full so LOOP BACK to 45DCH to keep reading bytes.
45E3
LD HL,0000H
If we are here then buffer IS full, so toss out HL by setting Register Pair HL = 0000H.
45E6
LOOP Back 4 instructions (45DCH). This routine will only exit via an interrupt.


45E9H – Routine to make a disk drive select code.

45E9
AND 07H
MASK the value of Register A against 07H (0000 0111). This has the effect of turning off bits 7, 6, 5, 4, 3, leaving only bits 2, 1, 0 active.
45EB
45EC
45ED
RLCA
RLCA
RLCA
Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0) three times. This rorates the irrelevant bits and moves the bits we need to bits 3-5.
45EE
OR C7H
OR Register A against C7H to create an opcode using bits 3-5 from the mask.
45F0
LD (45F5H),A
Store the value held in Register A into the memory location 45F5H.
45F3
XOR A
Set Register A to ZERO and clear all Flags.
45F4
SET 0,A
SET (i.e., set as 1) BIT 0 of Register A.
45F6
RET
RETurn to the caller.

45F7H – WRITE SECTOR TO DISK. Positions the head first. Register C must be set to … dunno.

First we need to check for write protect.

45F7
LD A,FFH
LET Register A = FFH to check to see if the disk is write protected. Note that other routines will put a different value in here (i.e., 45F8H).
45F9
CP C
Compare the value held in Register A against the value held in Register C. Results: If Register A equals C, the Z FLAG is set; otherwise the NZ FLAG is set.
45FA
If the NZ FLAG (Not Zero) is set then the disks is NOT write protected, so continue by JUMPing to 4600H.
45FC
LD A,0FH
LET Register A = 0FH (0000 1111).
45FE
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A.
45FF
RET
RETurn to the caller.

4600H – WRITE SECTOR TO DISK. Positions the head first (continuation after verification that the disk isn’t write protected).

4600
LD (4580H),HL
Store the value held in Register HL (which is the buffer address) into the memory location 4580H.
4603
GOSUB to 4C6BH to start up and the check the disk drive.
4606
RET NZ
If the NZ FLAG (Not Zero) is set then there was an error from that routine so, RETurn to the caller.
4607
If the C FLAG (No Carry) is set then the disk is write protected so, JUMP back a few instructions to the WRITE PROTECT ERROR routine (45FCH).

Start of a BIG loop for 9 iterations. The bottom of the loop is at 464EH.

4609
LD B,09H
LET Register B = 09H (0000 1001).
460B
GOSUB to 44E3H.

NOTE: 44E3H is the routine which handles positions the READ/WRITE HEAD to the proper track (Register C holds the drive number, and Register D holds the track).
460E
CP 0CH
Compare the value held in Register A against 0CH to see if we got a TIME OUT. Results: If Register A equals 0CH, the Z FLAG is set; otherwise the NZ FLAG is set.
4610
If the NZ FLAG (Not Zero) is set then we are good, so JUMP to 4614H.
4612
OR A
Prepare to return with error by setting the FLAGS based on the contents of Register A.
4613
RET
RETurn to the caller.

4614H – WRITE SECTOR TO DISK. Positions the head first (continuation after positioning the head successfully.

4614
BIT 6,A
Test Bit 6 of Register A to see if the disk is write protected.
4616
If the NZ FLAG (Not Zero) is set, then it is, so JUMP back a few instructions to the WRITE PROTECT ERROR routine (45FCH).
4618
AND 19H
MASK the value of Register A against 19H (0001 1001). This has the effect of turning off bits 7, 6, 5, 2, 1, leaving only bits 4 (FDC Seek Error), 3 (FDC CRC Error), 0 (FDC Busy) active.
461A
If the NZ FLAG (Not Zero) is set then one of those things happened, so JUMP to 465CH to reset the FDC and try again.

At this point, we know the disk isnt write protected, that we are on the right track, and that the FDC wasn’t busy and didn’t have an error … so we are good to start writing.

461C
LD A,D
LET Register A = Register D (which is holding the track number).
461D
CP 11H
Compare the value held in Register A against 11H (which is the directory track). Results: If Register A equals 11H, the Z FLAG is set; otherwise the NZ FLAG is set.
461F
If the NZ FLAG (Not Zero) is set (meaning we are NOT on the directory track), skip the next instructions (which set the READ PROTECT flag) and JUMP to 4629H.
4621
LD A,(4583H)
Fetch the value stored at memory location 4583H (which would be the write command) and put it into Register A.
4624
AND FEH
MASK the value of Register A (which is the write command) against FEH (1111 1110). This has the effect of turning off bit 0, leaving only bits 7, 6, 5, 4, 3, 2, 1 active. This change to bit 0 sets to READ PROTECT.
4626
LD (4583H),A
Store masked/changed write commend (held in Register A) back into the memory location 4583H.
4629
LD A,E
LET Register A = Register E.
462A
OUTput the value held in Register A to Port F2H.

NOTE: Port F2H is the Floppy Disk Controller SECTOR Register.
462C
PUSH BC
PUSH Register BC to the top of the STACK.
462D
PUSH DE
PUSH Register DE to the top of the STACK.
462E
PUSH HL
PUSH Register HL to the top of the STACK.
462F
LD (47D8H),BC
Store the value held in Register BC (the FILE NUMBER and the DISK DRIVE) into the memory location 47D8H.
4633
LD (47DBH),DE
Store the value held in Register DE (the TRACK NUMBER and SECTOR NUMBER) into the memory location 47DBH.
4637
GOSUB to 456DH.

NOTE: 456DH is the routine which to write a sector to diskette after the head is already properly positioned.
463A
POP HL
MOVE the value held at the stop of the STACK into Register Pair HL.
463B
POP DE
MOVE the value held at the stop of the STACK into Register Pair DE.
463C
POP BC
MOVE the value held at the stop of the STACK into Register Pair BC.
463D
INput a byte 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
463F
BIT 7,A
Test Bit 7 of Register A (which is the NOT READY flag).
4641
If the NZ FLAG (Not Zero) is set then the DRIVE IS NOT READY, so JUMP to 464BH to reset the FDC and try again.
4643
BIT 6,A
If we’re here, then the drive is ready so test Bit 6 (WRITE PROTECT Flag) of Register A (the FDC status from 463DH).
4645
If the NZ FLAG (Not Zero) is set then the WRITE PROTECT FLAG is set, so we have an error and need to JUMP to the WRITE PROTECT ERROR routine (45FCH).
4647
AND 3DH
MASK the value of Register A against 3DH (0011 1101). This has the effect of turning off bits 7, 6, 1, leaving only bits 5 (WRITE FAULT), 4 (RECORD NOT FOUND), 3 (CRC ERROR), 2 (LOST DATA), 0 (BUSY) active.
4649
If the Z FLAG (Zero) is set then we are error free so, JUMP to 4667H to DISABLE the READ-PROTECT.
464B
GOSUB to 42DCH to reset the FDC.
464E
DJNZ 460BH
LOOP back to 460BH until Register B is ZERO.

End of loop of 9 tries.

4650
GOSUB to 4667H to DISABLE the READ-PROTECT.
4653
LD B,0EH
LET Register B = 0EH in case there is a WRITE FAULT.
4655
BIT 5,A
Test Bit 5 of Register A for a WRITE FAULT.
4657
If the NZ FLAG (Not Zero) is set, meaning there was a WRITE FAULT, JUMP to 46CCH.
465A
JUMP to 46A9H to process the error.

465CH – Reset the FDC, Try Again, Disable Read-Protect, and JUMP to the error routine

465C
GOSUB to 4566H.

NOTE: 4566H is the routine which resets the FDC.
465F
DJNZ 460BH
LOOP back to 460BH until Register B is ZERO.
4661
GOSUB to 4667H to DISABLE the READ-PROTECT.
4664
JUMP to 46C4H to process the error.

4667H – Disable the READ-PROTECT bit.

4667
PUSH AF
Save Register Pair AF to STACK.
4668
LD A,(4583H)
Fetch the value stored at memory location 4583H and put it into Register A.

NOTE: 4583H is the storage location for the WRITE COMMAND to be used.
466B
OR 01H
OR Register A against 01H (0000 0001). This has the effect of turning on bit 0 (to disable the READ-PROTECT bit).
466D
LD (4583H),A
Store the value held in Register A into the memory location 4583H.

NOTE: 4583H is the storage location for the WRITE COMMAND to be used.
4670
POP AF
Restore Register Pair AF from the top of the STACK.
4671
RET
RETurn to the caller.

4672H/4675H – Position the Head and Read Requested Sector. This has 2 entry points. The first empties HL (to toss away any data held there) one one leaves it alone.

4672
LD HL,0000H
LET Register Pair HL = 0000H. This discards whatever was in HL.
4675
LD (45C9H),HL
Store the value held in Register HL into the memory location 45C9H.
4678
GOSUB to 4C6BH to start up the disk drive.
467B
RET NZ
If the NZ FLAG (Not Zero) is set, meaning we received an error from that call, RETurn to the caller.

Start of a BIG loop for 9 iterations. The bottom of the loop is at 46A7H.

467C
LD B,09H
LET Register B = 09H (0000 1001) for a loop of 9 tries.
467E
GOSUB to 44E3H.

NOTE: 44E3H is the routine which handles positions the READ/WRITE HEAD to the proper track (Register C holds the drive number, and Register D holds the track).
4681
CP 0CH
Test for a time-out by comparing the value held in Register A against 0CH. Results: If Register A equals 0CH, the Z FLAG is set; otherwise the NZ FLAG is set.
4683
If the NZ FLAG (Not Zero) is set then we did not get a time-out so continue the routine by a JUMP to 4687H.
4685
OR A
Set FLAGS based on the contents of Register A in preparation to error out.
4686
RET
RETurn to the caller.
4687
AND 19H
MASK the value of Register A against 19H (0001 1001). This has the effect of turning off bits 7, 6, 5, 2, 1, leaving only bits 4 (SEEK ERROR), 3 (CRC ERROR), 0 (BUSY) active.
4689
If the NZ FLAG (Not Zero) is set then we have one of those so JUMP to 46BFH to deal with the error.
468B
LD A,E
LET Register A = Register E (which is holding the SECTOR TO READ).
468C
OUTput the value held in Register A to Port F2H.

NOTE: Port F2H is the Floppy Disk Controller SECTOR Register.
468E
468F
4690
PUSH BC
PUSH DE
PUSH HL
Preserve Register Pairs BC, DE, and HL to the STACK.
4691
GOSUB to 45B8H to READ SECTOR FROM DISK (routine assumes the head is already properly positioned and that Register A holds the drive select code).
4694
4695
4696
POP HL
POP DE
POP BC
Restore Register Pairs BC, DE, and HL from the STACK.
4697
INput a byte 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
4699
EI
Enable Interrupts.
469A
LD (4B12H),A
Store the byte read from the FDC status (held in Register A) into the memory location 4B12H – which is in the middle of a LD A,nnH instruction inside the READ DIRECTORY SECTOR routine at 4B0DH.
469D
BIT 7,A
Test Bit 7 (NOT READY) of the FDC status (held in Register A).
469F
If the NZ FLAG (Not Zero) is set, then the FDC returned NOT READY, so JUMP to 46A4H.
46A1
AND 1DH
MASK the value of Register A against 1DH (0001 1101). This has the effect of turning off bits 7, 6, 5, 1, leaving only bits 4 (RECORD NOT FOUND), 3 (CRC ERROR), 2 (LOST DATA), 0 (BUSY) active.
46A3
RET Z
If the Z FLAG (Zero) is set then we have no error so RETurn to the caller.

If we are here, then we need to deal with a NOT READY FDC error from 469FH.

46A4
GOSUB to 42DCH to reset the FDC (including restore the drive to track 0).
46A7
DJNZ 467EH
LOOP back to 467EH to try again until Register B is ZERO.

46A9H – Error Processing Routine.

46A9
LD B,06H
LET Register B = 06H to set up for a HARDWARE FAULT error
46AB
BIT 7,A
Test Bit 7 of Register A (NOT READY).
46AD
If the NZ FLAG (Not Zero) is set, JUMP to 46CCH to display the error held in Register B.
46AF
LD B,03H
LET Register B = 03H.
46B1
BIT 2,A
Test Bit 2 (LOST DATA) of Register A.
46B3
If the NZ FLAG (Not Zero) is set, JUMP to 46CCH to display the error held in Register B.
46B5
LD B,01H
LET Register B = 01H.
46B7
BIT 3,A
Test Bit 3 (CRC ERROR) of Register A.
46B9
If the NZ FLAG (Not Zero) is set, JUMP to 46CCH to display the error held in Register B.
46BB
LD B,05H
LET Register B = 05H (SECTOR NOT FOUND).
46BD
JUMP to 46CCH to display the error held in Register B.

46BFH – Jumped here from 4689H to deal with a SEEK ERROR, CRC ERROR or BUSY FDC code.

46BF
GOSUB to 4566H.

NOTE: 4566H is the routine which resets the FDC.
46C2
DJNZ 467EH
LOOP back to 467EH until Register B is ZERO.
46C4
LD B,04H
LET Register B = 04H (0000 0100).
46C6
BIT 4,A
Test Bit 4 (span class=”code”>SEEK ERROR) of Register A.
46C8
If the NZ FLAG (Not Zero) is set, JUMP to 46CCH to display the error held in Register B.
46CA
LD B,01H
LET Register B = 01H (0000 0001).

46CCH – Set up to return with an error code.

46CC
LD A,B
LET Register A = Register B (holding the error code).
46CD
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A.
46CE
RET
RETurn to the caller.

46CFH – This is the VECTOR call for POSN – Position a File for Access.

46CF
GOSUB to 4881H to set up for I/O. This routine requires that Register Pair DE be pointing to the location in the buffer.
46D2
BIT 7,(IX+01H)
Test Bit 7 of Register (IX+01H), which is the OPTIONS byte, to see if we were set up for SECTOR I/O.
46D6
If the Z FLAG (Zero) is set then we are looking for SECTOR I/O so JUMP to 46E2H.
46D8
46D9
LD H,B
LD L,C
LET Register Pair HL = Register BC (the record number).
46DA
LD A,(IX+09H)
Fetch the value stored at memory location IX+09H (Record Length) and put it into Register A.
46DD
GOSUB to 4B55H to calculate the NEXT record number, calculated as Record Number * Record Length. On exit, Register A = Overflow and Register Pair HL = the result of the multiplication.
46E0
46E0
LD B,H
LD C,L
LET Register Pair BC = Register Pair HL (the new record number).
46E2
LD (46F2H),A
Store the value held in Register A (the overflow, if any, from the multiplication) into the memory location 46F2H (the byte offset in the sector).
46E5
LD (46F7H),BC
Store the value held in Register BC (the new record number) into the memory location 46F7H (record number).
46E9
GOSUB to 4868H to write the buffer (if necessary).
46EC
RET NZ
If the NZ FLAG (Not Zero) is set then there was an error, so RETurn to the caller.
46ED
SET 5,(IX+01H)
SET (i.e., set as 1) BIT 5 of Register (IX+01H), which is the OPTIONS byte, to indicate that the sector is not loaded.
46F1
LD A,00H
LET Register A = 00H. I suspect this byte is modified by code.
46F3
LD (IX+05H),A
Store the value held in Register A into the memory location IX+05H (which is the BYTE POSITION FOR NEXT).
46F6
LD BC,0000H
LET Register Pair BC = 0000H. I suspect this byte is modified by code.
46F9
LD (IX+0AH),C
Store the value held in Register C (LSB of the new “NEXT” sector number) into the memory location IX+0AH (which is the LSB of the sector number of the next file).
46FC
LD (IX+0BH),B
Store the value held in Register B (MSB of the new “NEXT” sector number) into the memory location IX+0BH (which is the MSB of the sector number of the next file).
46FF
XOR A
Set Register A to ZERO and clear all Flags to indicate NO ERROR.
4700
RET
RETurn to the caller.

4701H – This is the VECTOR call for BACKSPACE – Back Up To the Previous Record in a File.

4701
GOSUB to 4881H to set up for I/O. This routine requires that Register Pair DE be pointing to the location in the buffer.
4704
LD C,(IX+0AH)
Fetch the value stored at memory location IX+0AH (which is the LSB of the sector number of the “next” file) and put it into Register C.
4707
LD B,(IX+0BH)
Fetch the value stored at memory location IX+0BH (which is the LSB of the sector number of the “next” file) and put it into Register B.
470A
BIT 7,(IX+01H)
Test Bit 7 of Register (IX+01H), which is the OPTIONS byte, to see if we were set up for SECTOR I/O.
470E
If the Z FLAG (Zero) is set then we were set up for SECTOR I/O so JUMP to 471DH.
4710
LD A,(IX+09H)
Fetch the value stored at memory location IX+09H (Logical Record Length) and put it into Register A.
4713
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A.
4714
If the Z FLAG (Zero) is set then we were set for FULL sector records so JUMP to 471DH.
4716
NEG
NEGate Register A so that it is -LRL
4718
ADD A,(IX+05H)
Apply the -LRL to (IX+05H) (which is the BYTE POSITION FOR NEXT) to adjust the “NEXT” value
471B
If the C FLAG (No Carry) is set then we are still within the same sector so JUMP to 46E2H to finish up and RETurn.
471D
DEC BC
DECrement Register Pair BC by 1 to go to the prior sector.
471E
JUMP to 46E2H to finish up and RETurn.

4720H – This is the VECTOR call for REWIND – Position to the Start of a File.

4720
GOSUB to 4881H to set up for I/O. This routine requires that Register Pair DE be pointing to the location in the buffer.
4723
LD BC,0000H
LET Register Pair BC = 0000H so that the NEXT sector will be 0.
4726
XOR A
Set Register A to ZERO and clear all Flags so that the NEXT byte will be 0.
4727
JUMP to 46E2H to finish up and RETurn.

4729H – This is the VECTOR call for POSEOF – Position to the End of a File.

4729
GOSUB to 4881H to set up for I/O. This routine requires that Register Pair DE be pointing to the location in the buffer.
472C
LD C,(IX+0CH)
Fetch the value stored at memory location IX+0CH (LSB of the EOF sector number) and put it into Register C.
472F
LD B,(IX+0DH)
Fetch the value stored at memory location IX+0DH (MSB of the EOF sector number) and put it into Register B.
4732
LD A,(IX+08H)
Fetch the value stored at memory location IX+08H (the Byte Position of the EOF) and put it into Register A.
4735
JUMP to 46E2H to finish up and RETurn.

4737H – This is the VECTOR call for READ – Read the Logical Record at the “NEXT” Position.

4737
GOSUB to 4881H to set up for I/O. This routine requires that Register Pair DE be pointing to the location in the buffer.
473A
GOSUB to 4868H to write the buffer (if necessary).
473D
RET NZ
If the NZ FLAG (Not Zero) is set then we have an error so RETurn to the caller.
473E
RES 6,(IX+01H)
RESet (i.e., set as 0) BIT 6 of Register (IX+01H), which is the OPTIONS byte, to designate that that we need to update the EOF after all writes.
4742
BIT 7,(IX+01H)
Test Bit 7 of Register (IX+01H), which is the OPTIONS byte, to see if we were set up for SECTOR I/O.
4746
If the Z FLAG (Zero) is set then we were set up for SECTOR I/O so JUMP to 4772H to check the file security settings.
4748
LD B,(IX+09H)
Fetch the value stored at memory location IX+09H (Logical Record Length) and put it into Register B. This will serve as a DJNZ loop to read that number of bytes.
474B
474C
PUSH HL
PUSH BC
Preserve Register Pairs HL and BC to the STACK because the next routine uses them both.
474D
GOSUB to 4791H to read a byte.
4750
4751
POP BC
POP BC
RESTORE Register Pairs BC and HL from the STACK.
4752
RET NZ
If the NZ FLAG (Not Zero) is set then we have an error so RETurn to the caller.
4753
LD (HL),A
Store the byte read from disk (held in Register A) into the memory buffer (pointed to by Register Pair HL).
4754
INC HL
Bump Register Pair HL by 1 to point to the next byte in the memory buffer.
4755
DJNZ 474BH
LOOP back to 474BH until Register B is ZERO.
4757
XOR A
Set Register A to ZERO and clear all Flags so as to indicate NO ERROR.
4758
RET
RETurn to the caller.

4759H – SUBROUTINE to first verify that READ ACCESS or higher is permitted, and then continue at 476CH. Called from 4772 and 4797.

4759
LD A,(IX+01H)
Fetch the value stored at memory location IX+01H, which is the OPTIONS byte, and put it into Register A.
475C
AND 07H
MASK the value of Register A against 07H (0000 0111). This has the effect of turning off bits 7, 6, 5, 4, 3, leaving only bits 2, 1, 0 (the ACCESS LEVEL) active.
475E
CP 06H
Compare the value held in Register A against 06H to check for READ LEVEL ACCESS or higher. Results:
  • If Register A equals 06H, the Z FLAG is set.
  • If A < 06H, the CARRY FLAG will be set.
  • if A >= 06H, the NO CARRY FLAG will be set.
4760
If the C FLAG (No Carry) is set then we have READ LEVEL ACCESS or higher so, JUMP to 476CH.
4762
LD A,19H
LET Register A = 19H to set up fror a FILE ACCESS DENIED error.
4764
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A.
4765
RET
RETurn to the caller.

4766H – Read the Sector / Return With Error or Continue. Called from 4C58.

4766
GOSUB to 476CH to read a sector from the file.
4769
RET NZ
If the NZ FLAG (Not Zero) is set then there is an error, so RETurn to the caller.
476A
JUMP to 4776H to update “NEXT” and RETURN.

476CH – Routine to check If Past EOF / Return With Error or Continue at 4780H.

476C
GOSUB to 4A2EH to Check to see if we are past the EOF.
476F
RET NZ
If the NZ FLAG (Not Zero) is set then there is an error, so RETurn to the caller.
4770
JUMP to 4780H.

4772H – Routine to check the file security settings and increase the IX next sector memory locations.

4772
GOSUB to 4759H to check the file security settings.
4775
RET NZ
If the NZ FLAG (Not Zero) is set then we have an error, so RETurn to the caller.
4776
INC (IX+0AH)
Bump the “NEXT” sector LSB. IX+0AH holds the LSB of the sector number of the “next” file.
4779
If the NZ FLAG (Not Zero) is set then don’t have an overflow (i.e., no need to update the MSB), so skip the next instruction.
477B
INC (IX+0BH)
Bump the “NEXT” sector MSB because the bump of the LSB cycled back to zero. IX+0BH holds the MSB of the sector number of the “next” file.
477E
XOR A
Set Register A to ZERO and clear all Flags.
477F
RET
RETurn to the caller.

4780H – Continues from routine at 476CH (which makes sure we are not past EOF). Sets up HL for re-entry into the position head and read sector routine.

4780
GOSUB to 49D4H to find the physical track and sector number of the “NEXT” sector.
4783
RET NZ
If the NZ FLAG (Not Zero) is set then we have an error, so RETurn to the caller.
4784
RES 5,(IX+01H)
RESet (i.e., set as 0) BIT 5 of Register (IX+01H), which is the OPTIONS byte, to indicate sector IS loaded.
4788
LD L,(IX+03H)
Fetch the value stored at memory location IX+03H (the LSB of the 2 byte BUFFER ADDRESS) and put it into Register L.
478B
LD H,(IX+04H)
Fetch the value stored at memory location IX+04H (the MSB of the 2 byte BUFFER ADDRESS) and put it into Register H.
478E
JUMP to 4675H (the Position the Head and Read Requested Sector routine; although THIS time witH HL set).

4791H – Continues the READ LOGICAL RECORD routine

4791
BIT 5,(IX+01H)
Test Bit 5 of Register (IX+01H), which is the OPTIONS byte, to see if the current sector is loaded.
4795
If the Z FLAG (Zero) is set then the current sector is already loaded so JUMP to 479BH.
4797
GOSUB to 4759H to check the file’s security.
479A
RET NZ
If the NZ FLAG (Not Zero) is set then we have an error, so RETurn to the caller.
479B
GOSUB to 4A2EH to check to see if we are past the EOF.
479E
RET NZ
If the NZ FLAG (Not Zero) is set then we have an error, so RETurn to the caller.
479F
GOSUB to 4872H to determine the applicable byte position in the buffer (by adding the offset to the base buffer address, and then loading Register Pair DE to point to the buffer address of that specific byte).
47A2
XOR A
Set Register A to ZERO and clear all Flags.
47A3
LD A,(DE)
Fetch the value stored at memory location pointed to by Register Pair DE (which is the byte from the buffer) and put it into Register A.
47A4
PUSH AF
Preserve Register AF (which includes the byte from the buffer) to the top of the STACK.
47A5
INC (IX+05H)
IX+05H is the BYTE POSITION FOR NEXT, so we need to bump that to the next byte
47A8
If the NZ FLAG (Not Zero) is set then we don’t have a new sector, so skip the next 2 bytes and JUMP to 47B1H.
47AA
SET 5,(IX+01H)
SET (i.e., set as 1) BIT 5 of Register (IX+01H), which is the OPTIONS byte, to signify that the current sector is NOT loaded.
47AE
GOSUB to 4776H to update the “NEXT” sector.
47B1
POP AF
Restore Register Pair AF (including the byte read, which is held in Register A) from the STACK.
47B2
RET
RETurn to the caller.

47B3H – This is the WRITE VECTOR. This routine writes the file sector WITHOUT verify.

47B3
GOSUB to 4881H to prepare for the write. This routine requires that Register Pair DE be pointing to the location in the buffer.
47B6
BIT 7,(IX+01H)
Test Bit 7 of Register (IX+01H), which is the OPTIONS byte, to see if we are looking for SECTOR I/O.
47BA
If the Z FLAG (Zero) is set then we are looking for SECTOR I/O so JUMP to 47E9H.
47BC
LD B,(IX+09H)
Fetch the value stored at memory location IX+09H, which is the LRL, and put it into Register B since Register B will be the counter for the DJNZ write loop.

Start of a loop for the entire LRL of the sector.

47BF
LD A,(HL)
Fetch the byte to write (which is the value stored at memory location pointed to by Register Pair HL) and put it into Register A.
47C0
INC HL
Bump the pointer (held in Register Pair HL) by 1.
47C1
PUSH HL
PUSH Register HL to the top of the STACK.
47C2
PUSH BC
PUSH Register BC to the top of the STACK.
47C3
GOSUB to 4837H to write the byte to diskette.
47C6
POP BC
MOVE the value held at the stop of the STACK into Register Pair BC.
47C7
POP HL
MOVE the value held at the stop of the STACK into Register Pair HL.
47C8
RET NZ
If the NZ FLAG (Not Zero) is set then we have an error, so RETurn to the caller.
47C9
DJNZ 47BFH
LOOP back to 47BFH until Register B (which was the LRL) is ZERO.

End of the loop.

47CB
RET
RETurn to the caller.

47CCH – This is the WRITE VECTOR. This routine writes the file sector WITH verify.

47CC
GOSUB to 4881H to prepare for the write. This routine requires that Register Pair DE be pointing to the location in the buffer.
47CF
LD B,03H
LET Register B = 03H for a write of up to three tries.
47D1
PUSH BC
PUSH Register BC (i.e., save the counter held in Register B) to the top of the STACK because the B is used for the LRL counter in 47B6H.
47D2
GOSUB to 47B6H in the prior routine to loop the write of the sector.
47D5
If the NZ FLAG (Not Zero) is set then we have an error, so JUMP to 47E7H (to restore BC from the STACK and RETurn).
47D7
LD BC,0000H
LET Register Pair BC = 0000H. Note: This value will be changed dynamically as part of DOS operations. B is the file number, C is the drive number.
47DA
LD DE,0000H
LET Register Pair DE = 0000H. Note: This value will be changed dynamically as part of DOS operations. D is the track number, E is the sector number.
47DD
GOSUB to 4672H to perform a dummy read.
47E0
If the Z FLAG (Zero) is set then we got a good read, so JUMP to 47E7H (to restore BC from the STACK and RETurn).
47E2
POP BC
Restore Register Pair BC from the top of the STACK to get the “3 try” counter back into Register B.
47E3
DJNZ 47D1H
LOOP back to 47D1H until Register B is ZERO (for a total of 3 tries).
47E5
OR A
Set FLAGS based on the contents of Register A.
47E6
RET
RETurn to the caller.

47E7H – This little routine is called both when there is an error, or when there isn’t, in the above routine.

47E7
POP BC
MOVE the value held at the stop of the STACK into Register Pair BC.
47E8
RET
RETurn to the caller.

47E9H – Called from 47B3H (the WRITE VECTOR) if it was determined that SECTOR I/O was required.

47E9
SET 6,(IX+01H)
SET (i.e., set as 1) BIT 6 of Register (IX+01H), which is the OPTIONS byte, to signify RANDOM I/O.
47ED
LD A,(IX+01H)
Fetch the value stored at memory location IX+01H (the OPTIONS byte) and put it into Register A.
47F0
AND 07H
MASK the value of Register A against 07H (0000 0111). This has the effect of turning off bits 7, 6, 5, 4, 3, leaving only bits 2, 1, 0 active. Those bits are the ACCESS CODE.
47F2
CP 05H
Compare the value held in Register A against 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.
47F4
If the NC FLAG (No Carry) is set then we do not have WRITE access so JUMP to 4762H.
47F7
GOSUB to 49D4H to find the physical location of the track and sector for the I/O.
47FA
If the Z FLAG (Zero) is set then we had no errors in that CALL so JUMP to 4803H.

If we are here then we have an error, so we need to figure out which error and waht to do about it.

47FC
CP 1DH
Compare the value held in Register A against 1DH to see if we are past the EOF. Results:
  • If Register A equals 1DH, the Z FLAG is set.
  • If A < 1DH, the CARRY FLAG will be set.
  • if A >= 1DH, the NO CARRY FLAG will be set.
47FE
RET NZ
If the NZ FLAG (Not Zero) is set, meaning we are NOT past the EOF, RETurn to the caller.
47FF
GOSUB to 48ACH to allocate filespace and expand the file.
4802
RET NZ
If the NZ FLAG (Not Zero) is set then we have an error from that CALL so RETurn to the caller.
4803
LD L,(IX+03H)
Fetch the value stored at memory location IX+03H (the LSB of the 2 byte BUFFER ADDRESS) and put it into Register L.
4806
LD H,(IX+04H)
Fetch the value stored at memory location IX+04H (the MSB of the 2 byte BUFFER ADDRESS) and put it into Register H.
4809
GOSUB to 45F7H to write the sectors.
480C
RET NZ
If the NZ FLAG (Not Zero) is set then we have an error from that CALL so RETurn to the caller.
480D
SET 3,(IX+01H)
SET (i.e., set as 1) BIT 3 of Register (IX+01H), which is the OPTIONS byte.
4811
RES 4,(IX+01H)
RESet (i.e., set as 0) BIT 4 of Register (IX+01H), which is the OPTIONS byte.
4815
GOSUB to 4A2EH to see if we have past the EOF.
4818
If the Z FLAG (Zero) is set then we have NOT gone past the EOF so JUMP to 482AH.
481A
BIT 6,(IX+01H)
Test Bit 6 of Register (IX+01H), which is the OPTIONS byte for EOF.
481E
If the Z FLAG (Zero) is set then we do not need to update the EOF so JUMP to 4821H.
4820
INC HL
Bump Register Pair HL (which holds the EOF) by 1.
4821
LD (IX+0CH),L
Store the value held in Register L (LSB of the EOF) into the memory location IX+0CH (LSB of the EOF sector number).
4824
LD (IX+0DH),H
Store the value held in Register H (MSB of the EOF) into the memory location IX+0DH (MSB of the EOF sector number).
4827
LD (IX+08H),C
Store the value held in Register C into the memory location IX+08H (the Byte Position of the EOF).
482A
BIT 6,(IX+01H)
Test Bit 6 of Register (IX+01H), which is the OPTIONS byte, to see if we need to update the EOF.
482E
If the NZ FLAG (Not Zero) is set, then we don’t have to update the EOF, but we need to update the “NEXT” so GOSUB to 4776H.
4831
RES 6,(IX+01H)
RESet (i.e., set as 0) BIT 6 of Register (IX+01H), which is the OPTIONS byte, to flag that we need to udpate the EOF.
4835
XOR A
Set Register A to ZERO and clear all Flags.
4836
RET
RETurn to the caller.

4837H – Subroutine to Write a Byte at the “Next” Position.

4837
PUSH AF
PUSH Register Pair AF (which contains the byte to write) to the top of the STACK.
4838
SET 3,(IX+01H)
SET (i.e., set as 1) BIT 3 of Register (IX+01H), which is the OPTIONS byte.
483C
BIT 5,(IX+01H)
Test Bit 5 of Register (IX+01H), which is the OPTIONS byte to test to see if the current sector is loaded.
4840
If the Z FLAG (Zero) is set then the current sector is loaded so JUMP to 4850H.
4842
GOSUB to 4780H to load the current sector.
4845
If the Z FLAG (Zero) is set then we had no errors so continue at 4850H.
4847
AND FEH
If we’re here then we had an error, so we need to find out which one. First, MASK the value of Register A against FEH (1111 1110). This has the effect of turning off bit 0 (the BUSY bit), leaving only bits 7, 6, 5, 4, 3, 2, 1 active.
4849
CP 1CH
Compare the value held in Register A against 1CH (END OF FILE error). Results: If Register A equals 1CH, the Z FLAG is set; otherwise the NZ FLAG is set.
484B
If the Z FLAG (Zero) is set then we hit the END OF FILE so JUMP to 4850H.
484D
EX (SP),HL
Clear the STACK.
484E
POP HL
Clear the STACK.
484F
RET
RETurn to the caller.

4850H – Called from the prior WRITE A BYTE AT NEXT POSITION routine if the current sector is already loaded.

4850
GOSUB to 4872H to compute the applicable position in the buffer (by adding the offset to the base buffer address, and then loading Register Pair DE to point to the buffer address of that specific byte).
4853
POP AF
Restore the byte to be written from the top into Register A.
4854
LD (DE),A
Store the byte to be written (held in Register A) into the buffer (the memory location pointed to by Register Pair DE).
4855
INC DE
Move to the next buffer location by bumping Register Pair DE (which is the pointer) by 1.
4856
SET 4,(IX+01H)
SET (i.e., set as 1) BIT 4 of Register (IX+01H), which is the OPTIONS byte, to indicate that there is unwritten data in the buffer.
485A
INC (IX+05H)
IX+05H is the BYTE POSITION FOR NEXT in the FCB, so bump that as well.
485D
If the Z FLAG (Zero) is set then we have pushed from 256 bytes to 0, so it is time to actually write the buffer. To do so JUMP to 4861H.
485F
XOR A
Set Register A to ZERO and clear all Flags to indicate no error.
4860
RET
RETurn to the caller.

4861H – Jumped to from the prior routine when it is time to write the buffer out to diskette because the buffer is full.

4861
SET 5,(IX+01H)
SET (i.e., set as 1) BIT 5 of Register (IX+01H), which is the OPTIONS byte, to indicate that the sector is NOT loaded.
4865
JUMP to 47E9H (the WRITE VECTOR) to write the sector.

4868H – Write the Buffer to Disk (if necessary).

4868
LD A,(IX+01H)
Fetch the value stored at memory location IX+01H (which is the OPTIONS byte) and put it into Register A.
486B
AND 50H
MASK the value of Register A against 50H (0101 0000). This has the effect of turning off bits 7, 5, 3, 2, 1, 0, leaving only bits 6 (random I/O selected, 1=random), 4 (Unwritten data in the buffer, 1=yes) active.
486D
If the NZ FLAG (Not Zero) is set then either RANDOM I/O was selected or there is unwritten data in the buffer. If either is the case JUMP to 47EDH to write the sector.
4870
XOR A
Set Register A to ZERO and clear all Flags to indicate no error.
4871
RET
RETurn to the caller.

4872H – Compute the Applicable Position in the Buffer.

4872
LD A,(IX+05H)
Fetch the value stored at memory location IX+05H (which is the BYTE POSITION FOR NEXT) and put it into Register A. This will act as an offset value.
4875
LD C,A
Save the offset value in Register C.
4876
ADD A,(IX+03H)
IX+03H holds the LSB of the 2 byte BUFFER ADDRESS, so this adds the OFFSET to the BASE buffer address
4879
LD E,A
Put the Offset + Base address into Register E (so that Register Pair DE will point to the actual offset byte in the buffer).
487A
LD A,(IX+04H)
Fetch the value stored at memory location IX+04H (MSB of the 2 byte BUFFER ADDRESS) and put it into Register A.
487D
ADC 00H
This is cute. If the offset is such that it would have caused an overflow (i.e., CARRY) when added to the buffer, the CARRY FLAG will be set to 1. By doing an ADD WITH CARRY for a value of 00H, this will increase the MSB by 1 if there was a carry, and increase the MSB by 0 if there wasn’t!
487F
LD D,A
To round out having the MSB and LSB of the BUFFER which points to the OFFSET byte held in Register Pair DE, let Register D = Register A.
4880
RET
RETurn to the caller.

4881H – This routine is called a LOT and prepares for an actual diskette write.

4881
LD A,(DE)
Fetch the value stored in the buffer (the memory location pointed to by Register Pair DE) and put it into Register A.
4882
RLCA
Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0). This will have the effect of checking to see if the file is open by putting that indicator bit into the CARRY FLAG.
4883
If the NC FLAG (No Carry) is set, then the file isn’t open, and we cannot do a write, so EXIT by JUMPing to 48A7H.
4885
POP AF
Get the return address by moving the value held at the stop of the STACK into Register Pair AF.
4886
EX (SP),HL
Save the calling address (which is at the top of the STACK) by putting it into Register Pair HL.
4887
LD (44A9H),HL
Store the value held in Register HL into the memory location 44A9H.

NOTE: 44A9H is the storage location for the Saved Caller Address for DOS functions (2 Bytes).
488A
LD (44ABH),DE
Store the value held in Register DE into the memory location 44ABH.

NOTE: 44ABH is the storage location for the Saved FCB Address for DOS functions (2 Bytes).
488E
EX (SP),HL
Take the value that was held in 44A9H and overwrite the value at the top of the STACK.
488F
PUSH DE
PUSH Register DE (the buffer memory location) to the top of the STACK.
4890
EX (SP),IX
Swap the value that was held in 44A9H into Register Pair IX, and put the base DCB value (which is held in IX) into the top of the STACK.
4892
PUSH HL
PUSH Register HL to the top of the STACK.
4893
PUSH DE
PUSH Register DE to the top of the STACK.
4894
PUSH BC
PUSH Register BC to the top of the STACK.
4895
PUSH IY
PUSH Register IY to the top of the STACK.
4897
PUSH HL
PUSH Register HL to the top of the STACK. Why push it again? This is to set up a for a routine return via 489FH.
4898
LD HL,489FH
LET Register Pair HL = 489FH.
489B
EX (SP),HL
Put 489FH at the top of the STACK
489C
PUSH AF
PUSH Register AF to the top of the STACK so as to save t he old return.
489D
XOR A
Set Register A to ZERO and clear all Flags to signify no error.
489E
RET
RETurn to the caller.

489FH – This routine will finish up the diskette write and RETURN. It was set in 489BH.

489F
POP IY
MOVE the value held at the stop of the STACK into Register Pair IY.
48A1
POP BC
MOVE the value held at the stop of the STACK into Register Pair BC.
48A2
POP DE
MOVE the value held at the stop of the STACK into Register Pair DE.
48A3
POP HL
MOVE the value held at the stop of the STACK into Register Pair HL.
48A4
POP IX
MOVE the value held at the stop of the STACK into Register Pair IX.
48A6
RET
RETurn to the caller.

48A7H – This routine is JUMPed to from 4883H if the file we wanted to do I/O upon was not open.

48A7
POP AF
Since we are aborting; clear the STACK.
48A8
LD A,26H
LET Register A = 26H, which is the FILE NOT OPEN error.
48AA
OR A
Since a LD command does not set any FLAGS, set FLAGS based on the contents of Register A, which will be NZ for sure to signify an error.
48AB
RET
RETurn to the caller.

48ACH – This subroutine is called from 47FF to allocate filespace (and/or expand if we hit the EOF).

48AC
LD A,(IX+07H)
Fetch the value stored at memory location IX+07H (which is the LOGICAL FILE NUMBER IN THE DIRECTORY) and put it into Register A.
48AF
LD B,A
Preserve that file number into Register B.
48B0
LD C,(IX+06H)
Fetch the value stored at memory location IX+06H (which is the DRIVE NUMBER OF THE FILE) and put it into Register C.
48B3
GOSUB to 4A93H to read the GAT into Buffer # 2.
48B6
RET NZ
If the NZ FLAG (Not Zero) is set, then we have an error, so RETurn to the caller.
48B7
GOSUB to 4A67H to read the directory entry directly to Buffer # 1.
48BA
RET NZ
If the NZ FLAG (Not Zero) is set, then we have an error, so RETurn to the caller.
48BB
LD (4955H),HL
Store the directory entry pointer (held in Register HL) into the memory location 4955H (which is actually the middle of an OPCODE for LD HL,xxxx).
48BE
LD L,(IX+0AH)
Fetch the value stored at memory location IX+0AH (which is the LSB of the sector number of the “next” file) and put it into Register L.
48C1
LD H,(IX+0BH)
Fetch the value stored at memory location IX+0BH (which is the MSB of the sector number of the “next” file) and put it into Register H.
48C4
LD E,(IX+0CH)
Fetch the value stored at memory location IX+0CH (which is the LSB of the EOF sector number) and put it into Register E.
48C7
LD D,(IX+0DH)
Fetch the value stored at memory location IX+0DH (which is the MSB of the EOF sector number) and put it into Register D.
48CA
OR A
Set FLAGS based on the contents of Register A which is really just trying to clear the CARRY FLAG as we are about to use it.
48CB
SBC HL,DE
HL = HL – DE (with Carry), so this way HL = the NEXT – the EOF.
48CD
If the NC FLAG (No Carry) is set, then NEXT=EOF so we can skip the next code and JUMP to 48D2H.
48CF
LD HL,0001H
LET Register Pair HL = 0001H to set up for an allocation of ONE sector.
48D2
LD A,03H
To prepare for a division routine, set Register A = 03H.
48D4
GOSUB to 4B6FH to divide HL by 3, so as to determine the number of grans.
48D7
INC HL
No matter what, HL needs to be higher than ZERO, so bump Register Pair HL by 1 so we have at least 1 gran. This leaves Register Pair HL holding the number of grans to allocate.
48D8
LD (4942H),HL
Store the value held in Register HL (the number of grans to allocate) into the memory location 4942H (which is actually the middle of an OPCODE for LD HL,xxxx).

The next 4 instructions are designed to set Register Pair HL to be the extent elements by setting HL as a base of 10, putting the DCB into Register Pair DE, and then adding DE to HL.

48DB
LD HL,0010H
LET Register Pair HL = 0010H as a base value
48DE
PUSH IX
PUSH Register IX to the top of the STACK.
48E0
POP DE
Restore Register IX from the top of the STACK into Register Pair DE.
48E1
ADD HL,DE
ADD the value held in Register Pair DE to Register Pair HL. The results are held in Register Pair HL.
48E2
LD A,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL (which is the first element) and put it into Register A.
48E3
CP FFH
Compare the value held in Register A against FFH to see if it is free. If Register A equals FFH, the Z FLAG is set; otherwise the NZ FLAG is set.
48E5
DEC HL
DECrement Register Pair HL by 1 to back one one byte because the JP in the next instruction starts with an INC HL.
48E6
If the Z FLAG (Zero) is set, meaning that the element is free, JUMP to 4985H (to search for a free gran and if none found, return DISK FULL).
48E9
LD B,1AH
LET Register B = 1AH as a counter to go through 26 bytes.
48EB
INC HL
Bump Register Pair HL by 1 to get to the next byte.
48EC
LD A,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL (which is the next element) and put it into Register A.
48ED
CP FFH
Compare the value held in Register A against FFH to see if it is free. If Register A equals FFH, the Z FLAG is set; otherwise the NZ FLAG is set.
48EF
If the Z FLAG (Zero) is set then the element is free so stop looking and JUMP to 48F7H.
48F1
DJNZ 48EBH
LOOP back to 48EBH until Register B is ZERO.
48F3
LD A,1EH
LET Register A = 1EH to signify NO MORE EXTENTS AVAILABLE
48F5
OR A
Since a LD command does not set any FLAGS, set FLAGS based on the contents of Register A, which will be NZ for sure to signify an error.
48F6
RET
RETurn to the caller.

48F7H – Check to see if there are free extents.

48F7
48F8
DEC HL
DEC HL
DECrement Register Pair HL by 2 so as to back up to the start of the last extent.
48F9
LD A,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL (which would be the track number) and put it into Register A.
48FA
LD DE,4D00H
LET Register Pair DE = 4D00H so as to point to the GAT table.
48FD
ADD A,E
ADD the value held in Register E to Register A (Results held in Register A) to point to the entry for the given track.
48FE
LD E,A
Preserve the entry for the given track into Register E.
48FF
INC HL
Bump Register Pair HL by 1 to point to the storage location for the number of grans in the selected extent.
4900
LD A,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL (i.e., the number of grants in the selected extent) and put it into Register A.
4901
AND 1FH
MASK the value of Register A against 1FH (0001 1111). This has the effect of turning off bits 7, 6, 5, leaving only bits 4, 3, 2, 1, 0 active.
4903
CP 1FH
Compare the value held in Register A against 1FH to see if the extent is full. Results: If Register A equals 1FH (= the extent is full), the Z FLAG is set; otherwise the NZ FLAG (= not full) is set.
4905
If the NZ FLAG (Not Zero) is set, then the extent is not full, so JUMP to 490AH.
4907
INC HL
If we are here, then Z was set and the extent is full, so we need to bump Register Pair HL by 1 to check for the next extent.
4908
LOOP BACK to 48E2H.

490AH – This routine is JUMPed to from 4905H if an open extent is found.

490A
LD A,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL (the extent data) and put it into Register A.
490B
PUSH AF
PUSH Register AF to the top of the STACK.

The next few instructions basically set Register C to be the number of grans offset from the start of the track.

490C
AND E0H
MASK the value of Register A against E0H (1110 0000). This has the effect of turning off bits 4, 3, 2, 1, 0, leaving only bits 7, 6, 5 active.
490E
RLCA
Move bits 7,6,5 to 0,7,6 by rotating the bits in A left (with Bit 7 going into both the CARRY and Bit 0).
490F
RLCA
Move bits 0,7,6 to 1,0,7 by rotating the bits in A left (with Bit 7 going into both the CARRY and Bit 0).
4910
RLCA
Move bits 1,0,7 to 2,1,0 by rotating the bits in A left (with Bit 7 going into both the CARRY and Bit 0).
4911
LD C,A
LET Register C = Register A.

The next few instructions basically set Register A to be the number of grans in the extent.

4912
POP AF
MOVE the value held at the stop of the STACK into Register Pair AF.
4913
AND 1FH
MASK the value of Register A against 1FH (0001 1111). This has the effect of turning off bits 7, 6, 5, leaving only bits 4, 3, 2, 1, 0 active.

So now C holds the number of grans offset from the start of the track, and A holds the grans in the extent.

4915
ADD A,C
ADD the value held in Register C to Register A (Results held in Register A) so that Register A now equals the number of grans in total from the start.
4916
PUSH HL
Preserve Register Pair HL by pushing it to the top of the STACK.
4917
4918
LD L,A
LD H,00H
Set Register Pair HL to be the number of grans total from the start of the track.
491A
LD A,06H
Since TRSDOS v1.3 has 6 grans per track, set up Register A with a 6.
491C
GOSUB to 4B6FH to compute the track offset.
491F
PUSH AF
PUSH Register AF (the number of grans to go) to the top of the STACK.

There aren’t a lot of variables in the Z-80 so the next 3 instructions simply set DE to point to the GAT Table, by setting A=L, A=A+E, E=A.

4920
LD A,L
LET Register A = Register L.
4921
ADD A,E
ADD the value held in Register E to Register A (Results held in Register A).
4922
LD E,A
LET Register E = Register A.
4923
POP BC
MOVE the value held at the stop of the STACK (the number of grans into the track) into Register Pair BC.
4924
POP HL
Restore Register Pair HL from the top of the STACK.
4925
CP 28H
Test Register A against 40 (which is the highest number of tracks). Results:
  • If Register A equals 28H, the Z FLAG is set.
  • If A < 28H, the CARRY FLAG will be set.
  • if A >= 28H, the NO CARRY FLAG will be set.
4927
If the NC FLAG (No Carry) is set then we have exceeded track 40, which is a problem, so JUMP to 4985H (to search for a free gran and if none found, return DISK FULL).
492A
LD A,(DE)
Fetch the value stored at memory location pointed to by Register Pair DE (the GAT entry) and put it into Register A.
492B
CP 3FH
Compare the value held in Register A against 3FH to see if the track is completely allocated or not. Results: If Register A equals 3FH then the track is completely allocated and the Z FLAG is set; otherwise the NZ FLAG is set.
492D
If the Z FLAG (Zero) is set then the track is completely allocated so JUMP to 4985H (to search for a free gran and if none found, return DISK FULL).
492F
LD C,B
If we are here, then the track has room so we continue by setting Register C = the gran offset (held in Register B).
4930
LD B,A
LET Register B = the GAT entry (held in Register A).
4931
LD A,C
LET Register A = Register C (the gran offset).
4932
GOSUB to 4B48H to test to see if the gran is allocated.
4935
If the NZ FLAG (Not Zero) is set then the gran IS allocated, so create a new extent by JUMPing to 4985H.
4938
LD A,C
If we are here, then the gran is not allocated. LET Register A = Register C (the gran offset).
4939
GOSUB to 45E9H (the routine to calculate the DRIVE SELECT CODE to be used). This will set the bit for the gran.
493C
EX DE,HL
Swap Register Pair DE (the GAT table) and Register Pair HL (the extent element).
493D
OR (HL)
OR the value stored at (HL) against Register A to allocate the gran. The results are stored in Register A.
493E
LD (HL),A
Store the value held in Register A into the GAT table (i.e., the memory location pointed to by Register Pair HL).
493F
EX DE,HL
Swap Register Pair De and Register Pair HL back. Now HL will contain the extent element.
4940
INC (HL)
Since HL points to the extent element, bump it by 1 to allocate another gran in the extent

The next bunch of instructions set that there is now one less gran on the disk to allocate.

4941
LD HL,0000H
LET Register Pair HL = 0000H. Note: This 0000H is replaced at 48D8H and at 4945H with other values, and is supposed to be the number of grans to allocate.
4944
DEC HL
Reduce the number of grans remaining available by (held in Register Pair HL) by 1.
4945
LD (4942H),HL
Store the value held in Register HL (the newly reduced number of available grans) into the memory location 4942H (which is actually the middle of an OPCODE for LD HL,xxxx).
4948
4949
LD A,H
OR L
Since the Z-80 cannot test Register Pair HL against zero, the common trick is to set Register A to one, and Or the other. Only if every single bit was off in both will the Z FLAG be set.
494A
If the NZ FLAG (Not Zero) is set, meaning that HL was not zero, LOOP BACK to 48DBH.
494D
GOSUB to 4954H.
4950
RET NZ
If the NZ FLAG (Not Zero) is set then there was an error from that routine, so RETurn to the caller.
4951
JUMP to 49D4H to continue the routine.

4954H – This subroutine is called from 494DH to continue with the file space allocation.

The first part of this routine figures out what the EOF data is in the buffer and then saves the new EOF in the directory.

4954
LD HL,0000H
LET Register Pair HL = 0000H. This value gets repopulated at 48BBH to be the applicable directory entry pointer.
4957
LD DE,0014H
LET Register Pair DE = 0014H, which is the EOF data in the ENTRY.
495A
ADD HL,DE
ADD the value held in Register Pair DE (the EOF data in the entry) to Register Pair HL (the directory entry). The results, which should be the EOF data in the buffer) are held in Register Pair HL.
495B
LD A,(IX+0AH)
Fetch the value stored at memory location IX+0AH (which is the LSB of the sector number of the next file) and put it into Register A.
495E
LD (HL),A
Store the value held in Register A into the memory location pointed to by Register Pair HL.
495F
INC HL
Bump Register Pair HL by 1 to point to the next location.
4960
LD A,(IX+0BH)
Fetch the value stored at memory location IX+0BH (which is the MSB of the sector number of the next file) and put it into Register A.
4963
LD (HL),A
Store the value held in Register A into the memory location pointed to by Register Pair HL.
4964
INC HL
Bump Register Pair HL by 1.

Now that the new EOF has ben saved, need to deal with the extents.

4965
PUSH IX
PUSH Register IX (which is the point to the DCB in RAM) to the top of the STACK.
4967
LD DE,0010H
LET Register Pair DE = 0010H, which is the offset for the extent information.
496A
ADD IX,DE
ADD the value held in Register Pair DE (the offset) to Register Pair IX (the DCB). The results are held in Register Pair IX.
496C
496E
PUSH IX
POP DE
Copy IX to DE via a PUSH and a POP.
496F
POP IX
Restore the DCB to Register Pair IX.
4971
EX DE,HL
Swap Register Pair DE and Register Pair HL (so now HL will hold the extent information)
4972
LD BC,001AH
LET Register Pair BC = 001AH to set up a LDIR to move 26 bytes from the DCB to the directory.
4975
LDIR
Do that loop
4977
LD C,(IX+06H)
Fetch the value stored at memory location IX+06H (which is the DRIVE NUMBER OF THE FILE) and put it into Register C.
497A
LD B,(IX+07H)
Fetch the value stored at memory location IX+07H (which is the LOGICAL FILE NUMBER IN THE DIRECTORY) and put it into Register B.
497D
GOSUB to 4A9BH to write the new GAT sector.
4980
RET NZ
If the NZ FLAG (Not Zero) is set then there was an error from that routine, so RETurn to the caller.
4981
GOSUB to 4A7BH to write the directory entry.
4984
RET
RETurn to the caller.

4985H – Routine to search for an available gran and if none, error out.

4985
INC HL
Bump Register Pair HL by 1 so that it points to the next extent element.
4986
PUSH HL
Preserve Register Pair HL by PUSHing Register HL to the top of the STACK.
4987
LD (49C9H),HL
Preserve Register Pair HL AGAIN by storing it into the memory location 49C9H (whcih is actuall the opcode LD DE,nnnn).
498A
Search for a free gran by GOSUBing to 4997H. That routine should return a Z FLAG if a free gran has been found.
498D
POP HL
Restore HL from the top of the STACK.
498E
If the Z FLAG (Zero) is set then a free gran was found so JUMP to 4941H.
4990
GOSUB to 4954H to preserve the new allocation information.
4993
LD A,1BH
LET Register A = 1BH, which is the error code for DISK FULL
4995
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A and, based on A, it will definitely be NZ FLAG.
4996
RET
RETurn to the caller.

4997H – SUBROUTINE called from 4997H to find a free gran on a diskette and either JUMP to 49A7H if there is one or error out if there isn’t.

4997
LD HL,4D00H
LET Register Pair HL = 4D00H which is the buffer address of the GAT.
499A
LD B,28H
LET Register B = 28H (Decimal: 40) to set a loop for 40 tracks to search.
499C
LD A,(HL)
Fetch the status of the current gran (which is the value stored at memory location pointed to by Register Pair HL) and put it into Register A.
499D
CP 3FH
Compare the value held in Register A against 3FH to test if that gran is full. Results: If Register A equals 3FH (the gran is full), the Z FLAG is set; otherwise the NZ FLAG is set.
499F
If the NZ FLAG (Not Zero) is set then the gran is NOT full, so JUMP to 49A7H.
49A1
INC L
Bump Register L by 1 so that HL will point to the next entry.
49A2
DJNZ 499CH
LOOP back to 499CH until Register B is ZERO (i.e., all 40 tracks have been checked).
49A4
OR FFH
OR Register A against FFH to set the flags and otherwise prepare for a GRAN NOT FOUND.
49A6
RET
RETurn to the caller.

49A7H – Search for a free gran.

49A7
PUSH HL
PUSH Register HL (the element pointer) to the top of the STACK.
49A8
LD DE,0060H
LET Register Pair DE = 0060H as a base address to the granule existance table.
49AB
ADD HL,DE
ADD the value held in Register Pair DE to Register Pair HL so that HL now points to the appropriate element in the table.
49AC
LD A,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL and put it into Register A.
49AD
POP HL
Restore the element pointer back into HL from the top of the STACK.
49AE
CP FFH
Compare the value held in Register A against FFH to see if the track is bad. Results: If Register A equals FFH then the track is bad and the Z FLAG is set; otherwise the NZ FLAG is set.
49B0
If the Z FLAG (Zero) is set then the track was marked bad so JUMP BACK to 49A1H to keep searching.
49B2
LD C,00H
If we are here, then the table isn’t bad, so to prepare, LET Register C = 00H as the first gran.
49B4
LD B,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL (the GAT entry)and put it into Register B.
49B5
LD A,C
LET Register A = Register C.
49B6
Test to see if the gran is allocated by GOSUBing to 4B48H.
49B9
If the Z FLAG (Zero) is set then the gran is NOT allocated so JUMP to 49BEH.
49BB
INC C
If we’re here then the gran was allocated so to move to the next one Bump Register C by 1.
49BC
LOOP BACK to 49B5H until a free gran is found.

49BEH – Routine to update the GAT entry and number of free grans.

49BE
LD A,C
LET Register A = Register C (the gran number in the track).
49BF
GOSUB to 45E9H.

NOTE: 45E9H is the routine to calculate the DRIVE SELECT CODE to be used.
49C2
OR (HL)
OR the value stored at (HL) against Register A to combine the gran number in track with the GAT entry. The results are stored in Register A.
49C3
LD (HL),A
Store the new GAT entry (held in Register A) into the memory location pointed to by Register Pair HL.
49C4
LD A,C
LET Register A = Register C (the gran number in the track).
49C5
49C6
49C7
RRCA
RRCA
RRCA
Rotate the gran offset into bits 5-7 by doing three RRCA’s (which rotates the contents of A right one bit position; so that, for example, the contents of bit 0 are copied bit 7).
49C8
LD DE,0000H
LET Register Pair DE = 0000H. This 0000H is filled in dynamically from 4987H to be the next extent element pointer.
49CB
PUSH AF
PUSH Register AF (the gran offset) to the top of the STACK.
49CC
LD A,L
LET Register A = Register L (which is the track number).
49CD
LD (DE),A
Store the track number (held in Register A) into the DCB (the memory location pointed to by Register Pair DE).
49CE
INC DE
Bump Register Pair DE by 1 to point to the next byte.
49CF
POP AF
Restore the gran offset from the top of the STACK into Register Pair AF.
49D0
INC A
Bump Register A by 1 to indicate that one gran has been allocated.
49D1
LD (DE),A
Save the updated extent byte by storing the value held in Register A into the memory location pointed to by Register Pair DE.
49D2
XOR A
Set Register A to ZERO and clear all Flags to indicate NO ERROR.
49D3
RET
RETurn to the caller.

49D4H – Routine to scan all the extents to see if there are any available.

49D4
PUSH HL
PUSH Register HL to the top of the STACK.
49D5
PUSH BC
PUSH Register BC to the top of the STACK.
49D6
PUSH IX
PUSH Register IX to the top of the STACK.
49D8
POP HL
MOVE the value held at the top of the STACK (which was Index Register IX) into Register Pair HL.
49D9
LD DE,0010H
LET Register Pair DE = 0010H.
49DC
ADD HL,DE
ADD the value held in Register Pair DE (10H) to Register Pair HL, so that HL points to the extent elements in the DCB.
49DD
49E0
LD E,(IX+0AH)
LD D,(IX+0BH)
Fetch the value for the next sector into Register Pair DE.
49E3
LD B,0DH
LET Register B = 0DH (Decimal: 13) to scan up to 13 elements.
49E5
LD A,(HL)
Fetch the extent element (which is thg value stored at memory location pointed to by Register Pair HL) and put it into Register A.
49E6
INC A
Bump Register A by 1 to see if it is unused.
49E7
If the Z FLAG (Zero) is set, meaning that the element is used, we have an error so JUMP to 4A05H.
49E9
INC HL
Bump Register Pair HL by 1 so that it will point to the number of grans in the extent in the DCB.
49EA
LD A,(HL)
Fetch the number of grants in the extent (the value stored at memory location pointed to by Register Pair HL) into Register A.
49EB
AND 1FH
MASK the value of Register A against 1FH (0001 1111). This has the effect of turning off bits 7, 6, 5, leaving only bits 4, 3, 2, 1, 0 active.
49ED
GOSUB to 4A28H to convert that masked value into the number of sectors in the extent.
49F0
NEG
Negate Register A so that A = – sectors in the extent
49F2
PUSH DE
PUSH Register DE to the top of the STACK.

The next bunch of instructions only serves to set DE = DE – number of sectors in the extent and put that into Register A (with the CARRY FLAG set if the result is negative).

49F3
ADD A,E
ADD the value held in Register E to Register A (Results held in Register A).
49F4
LD E,A
LET Register E = Register A.
49F5
CCF
Invert the CARRY FLAG
49F6
LD A,D
LET Register A = Register D.
49F7
SBC 00H
Subtract 00H and the CARRY FLAG from Register A.
49F9
LD D,A
LET Register D = Register A.

DE now holds the number of sectors in the extent.

49FA
If the C FLAG (CARRY) is set, then the number of sectors in the extent are negative, so JUMP to 4A0AH.
49FC
POP AF
Clear the STACK.
49FD
INC HL
Bump Register Pair HL by 1 so HL points to the next element.
49FE
DJNZ 49E5H
LOOP back to 49E5H until Register B is ZERO and all the extents have been checked.

If we are here then every extent has been checked and all extents have been used. This is bad.

4A00
POP BC
Restore Register Pair BC from the STACK.
4A01
POP HL
Restore Register Pair HL from the STACK.
4A02
JUMP to 48F3H to exit with an error that ALL EXTENTS USED.

4A05H – Return a PAST END OF FILE error.

4A05
OR 1DH
OR Register A against 1DH (0001 1101). This has the effect of turning on bits 4, 3, 2, 0.
4A07
POP BC
MOVE the value held at the stop of the STACK into Register Pair BC.
4A08
POP HL
MOVE the value held at the stop of the STACK into Register Pair HL.
4A09
RET
RETurn to the caller.

4A0AH – This routine claculates the offset in grans for the track of the extent.

4A0A
POP DE
Restore Register Pair DE from the STACK.
4A0B
POP BC
MOVE the value held at the stop of the STACK into Register Pair BC.

The next few instructions get the offset (in grans) into the track of the extent.

4A0C
LD A,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL and put it into Register A.
4A0D
AND E0H
MASK the value of Register A against E0H (1110 0000). This has the effect of turning off bits 4, 3, 2, 1, 0, leaving only bits 7, 6, 5 active.
4A0F
4A10
4A11
RLCA
RLCA
RLCA
Rotate the bits in A left 3 times (with Bit 7 going into both the CARRY and Bit 0), thus moving bits 7, 6, and 5 to bits 2, 1, and 0.

Now that the offset (in grans) is in Register A, prepare to calculate it and write it.

4A12
GOSUB to 4A28H to convert that masked value (which is grans) into the sectors (by multipling them by 3).
4A15
ADD A,E
Calculate the ofset into the track by ADDing the value held in Register E to Register A (Results held in Register A).
4A16
DEC HL
DECrement Register Pair HL by 1 to point to where the track of the extent is stored.
4A17
LD D,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL (whcih is the track of the extent) and put it into Register D.
4A18
LD E,A
LET Register E = Register A (the sector offset in the track).
4A19
SUB 12H
SUBtract the value 12H (Decimal: 18) from Register A to set up for a 1 track offset.
4A1B
If the C FLAG (No Carry) is set, meaning that the offset is negative, JUMP to 4A20H.
4A1D
INC D
Bump Register D by 1 to point to the next track.
4A1E
LOOP BACK to 4A18H until we are at the proper track.

4A20H – Jumped here from 4A1BH if the offset is negative.

4A20
INC E
Bump Register E (the sector number) by 1.
4A21
LD A,(IX+06H)
Fetch the value stored at memory location IX+06H (which is the DRIVE NUMBER OF THE FILE) and put it into Register A.
4A24
LD C,A
LET Register C = Register A (the DRIVE NUMBER OF THE FILE).
4A25
XOR A
Set Register A to ZERO and clear all Flags to indicate no error.
4A26
POP HL
Restore Register Pair HL from the top of the STACK.
4A27
RET
RETurn to the caller.

4A28H – SUBROUTINE to convert the Grans (which was the masked value) into a sector.

4A28
PUSH BC
PUSH Register BC to the top of the STACK.
4A29
LD B,A
LET Register B = Register A (to preserve A).
4A2A
RLCA
Rotate the bits in A left, which effectively doubles Register A.
4A2B
ADD A,B
ADD the value held in Register B (which is Register A before it was doubled) to Register A. This results in A * 3.
4A2C
POP BC
Restore Register Pair BC from the top of the STACK.
4A2D
RET
RETurn to the caller.

4A2EH – Subroutine to check if the sought after sector is after the EOF. Called from 476CH, 479BH, and 4815H.

4A2E
PUSH DE
PUSH Register DE to the top of the STACK.

The combination of IX+08H, IX+0CH, and IX+0DH is the relative byte address of the EOF.

4A2F
LD D,(IX+0DH)
Fetch the value stored at memory location IX+0DH (MSB of the EOF sector number) and put it into Register D.
4A32
LD E,(IX+0CH)
Fetch the value stored at memory location IX+0CH (LSB of the EOF sector number) and put it into Register E.
4A35
LD A,(IX+08H)
Fetch the value stored at memory location IX+08H (the Byte Position of the EOF) and put it into Register A.
4A38
OR D
Check D vs A to see if they are empty by ORing Register D against Register A. The results are stored in Register A.
4A39
OR E
Check E vs A to see if they are empty by ORing Register E against Register A. The results are stored in Register A.
4A3A
If the Z FLAG (Zero) is set then D, E, and A are all zero meaning that the file is empty, so JUMP to 4A57H.
4A3C
LD H,(IX+0BH)
Fetch the value stored at memory location IX+0BH (MSB of the new “NEXT” sector number) and put it into Register H.
4A3F
LD L,(IX+0AH)
Fetch the value stored at memory location IX+0AH (LSB of the new “NEXT” sector number) and put it into Register L.
4A42
OR A
Since we are about to do a routine which checks the CARRY FLAG, we need to clear the CARRY FLAG.
4A43
SBC HL,DE
Subtract DE from HL to see if it the next sector (held in HL) is before or after the EOF sector (held in DE)
4A45
If the C FLAG (No Carry) is set then the next sector is BEFORE the EOF, so we are all good! JUMP to 4A5AH.
4A47
If the NZ FLAG (Not Zero) is set AND the CARRY FLAG was set, the sector is AFTER the EOF so we need to exit with an EOF error by JUMPing to 4A57H.
4A49
BIT 6,(IX+01H)
If we are here, then we had NC (sector is before EOF) and Z, so we continue by testing Bit 6 of Register (IX+01H), which is the OPTIONS byte, to see if we need to update the EOF.
4A4D
If the NZ FLAG (Not Zero) is set then we do not need to update the EOF, and we need to exit with an EOF error by JUMPing to 4A57H.
4A4F
LD A,(IX+05H)
Fetch the value stored at memory location IX+05H (which is the BYTE POSITION FOR NEXT) and put it into Register A. This will be the EOF byte number.
4A52
CP (IX+08H)
Compare the EOF Byte Number (held in Register A) against the the Byte Position of the EOF (held in the memory location (IX+08H)). Results:
  • If Register A equals (IX+08H), the Z FLAG is set.
  • If A < (IX+08H), the CARRY FLAG will be set.
  • if A >= (IX+08H), the NO CARRY FLAG will be set.
4A55
If the C FLAG (No Carry) is set then are good, so exit without an error by JUMPing to 4A5AH.
4A57
LD A,1CH
LET Register A = 1CH to set up an error for past EOF.
4A59
0EH
This byte is a mask to 4A5BH.
4A5A
XOR A
Clear all FLAGS.
4A5B
OR A
Set FLAGS based on the contents of Register A.

The combination of IX+05H, IX+0AH, and IX+0BH is the relative byte address of the “NEXT” sector.

4A5C
LD H,(IX+0BH)
Fetch the value stored at memory location IX+0BH (MSB of the new “NEXT” sector number) and put it into Register H.
4A5F
LD L,(IX+0AH)
Fetch the value stored at memory location IX+0AH (LSB of the new “NEXT” sector number) and put it into Register L.
4A62
LD C,(IX+05H)
Fetch the value stored at memory location IX+05H (which is the BYTE POSITION FOR NEXT) and put it into Register C.
4A65
POP DE
Restore Register Pair DE from the top of the STACK.
4A66
RET
RETurn to the caller.

4A67H – Subroutine to READ the Directory Entry into Buffer # 1.

4A67
PUSH BC
Preserve Register Pair BC by PUSHing it to the top of the STACK.
4A68
PUSH DE
Preserve Register Pair DE by PUSHing it to the top of the STACK.
4A69
GOSUB to 4AD8H to position for a read.
4A6C
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so JUMP to 4A76H.
4A6E
PUSH HL
Preserve Register Pair HL by PUSHing it to the top of the STACK.
4A6F
LD L,00H
LET Register L = 00H. This will form HL as pointing to Buffer # 1.
4A71
GOSUB to 4B0DH to read the sector.
4A74
POP HL
Restore Register Pair HL from the top of the STACK.
4A75
01H
Mask to 4A78H.
4A76
LD A, 11H
Set up for a DIRECTORY READ ERROR.
4A78
POP DE
Restore Register Pair DE from the top of the STACK.
4A79
POP BC
Restore Register Pair BC from the top of the STACK.
4A7A
RET
RETurn to the caller.

4A7BH – Subroutine to WRITE the Directory Entry from Buffer # 1. Called from 4981H.

4A7B
PUSH BC
Preserve Register Pair BC by PUSHing it to the top of the STACK.
4A7C
PUSH DE
Preserve Register Pair DE by PUSHing it to the top of the STACK.
4A7D
GOSUB to 4AD8H to position for a write.
4A80
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so 4A89H.
4A82
LD L,00H
LET Register L = 00H. This will form HL as pointing to Buffer # 1.
4A84
GOSUB to 45F7H to write the sector.
4A87
If the Z FLAG (Zero) is set then the prior routine had NO error, so JUMP to 4A90H.
4A89
CP 0FH
If we’re here then there was an error, so we need to deal with that. First, compare the value held in Register A against 0FH to check for a WRITE PROTECT ERROR. Results: If Register A equals 0FH, the Z FLAG is set; otherwise the NZ FLAG is set.
4A8B
If the Z FLAG (Zero) is set then we had a WRITE PROTECT ERROR so skip the next instruction that changes the error to DIRECTORY WRITE ERROR and JUMP to 4A8FH.
4A8D
LD A,12H
LET Register A = 12H to for a DIRECTORY WRITE ERROR.
4A8F
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A.
4A90
POP DE
Restore Register Pair DE from the top of the STACK.
4A91
POP BC
Restore Register Pair BC from the top of the STACK.
4A92
RET
RETurn to the caller.

4A93H – Read the GAT TABLE into a Buffer. Jumped to from 48B3H.

4A93
GOSUB to 4AADH to set up to read/write the GAT TABLE into the Buffer.
4A96
GOSUB to 4B0DH to read the GAT TABLE into the Buffer.
4A99
JUMP to 4AAAH to continue.

4A9BH – Subroutine to WRITE the GAT TABLE from the Buffer. Called from 497DH.

4A9B
GOSUB to 4AADH to set up to read/write the GAT TABLE into the Buffer.
4A9E
GOSUB to 45F7H to write the GAT TABLE from the Buffer.
4AA1
If the Z FLAG (Zero) is set then the prior routine had NO error, so JUMP to 4AAAH.
4AA3
CP 0FH
If we’re here then there was an error, so we need to deal with that. First, compare the value held in Register A against 0FH to check for a WRITE PROTECT ERROR. Results: If Register A equals 0FH, the Z FLAG is set; otherwise the NZ FLAG is set.
4AA5
If the Z FLAG (Zero) is set then we had a WRITE PROTECT ERROR so skip the next instruction that changes the error to GAT WRITE ERROR and JUMP to 4AA9H.
4AA7
LD A,15H
LET Register A = 15H for a GAT WRITE ERROR.
4AA9
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A.
4AAA
POP BC
Restore Register Pair BC from the top of the STACK.
4AAB
POP DE
Restore Register Pair DE from the top of the STACK.
4AAC
RET
RETurn to the caller.

4AADH – Subroutine to set up for a read or write of the GAT Table.

4AAD
POP HL
Restore the RETURN ADDRESS from the top of the STACK into Register Pair HL.
4AAE
PUSH DE
Preserve Register Pair DE by PUSHing it to the top of the STACK.
4AAF
PUSH BC
Preserve Register Pair BC by PUSHing it to the top of the STACK.
4AB0
PUSH HL
Preserve Register Pair HL by PUSHing it to the top of the STACK.
4AB1
GOSUB to 4B3EH to Get the Directory Track Number. Routine exits with the director track number in Register D.
4AB4
LD E,01H
LET Register E = 01H to point to the GAT sector in the directory track.
4AB6
LD HL,4D00H
LET Register Pair HL = 4D00H as the base address of BUFFER # 2
4AB9
RET
RETurn to the caller.

4ABAH – Routine to read the HASH INDEX TABLE and continue on.

4ABA
GOSUB to 4ACBH to set up to read/write the HASH INDEX TABLE.
4ABD
GOSUB to 4B0DH to read the HASH INDEX TABLE.
4AC0
Continue on by POPing BC and DE and RETurning.

4AC2H – Routine to write the HASH INDEX TABLE and continue on.

4AC2
GOSUB to 4ACBH to set up to read/write the HASH INDEX TABLE.
4AC5
GOSUB to 45F7H to write the HASH INDEX TABLE to diskette.
4AC8
POP BC
Restore Register Pair BC from the top of the STACK.
4AC9
POP DE
Restore Register Pair DE from the top of the STACK.
4ACA
RET
RETurn to the caller.

4ACBH – Subroutine to set up to read/write the HASH INDEX TABLE.

4ACB
POP HL
Restore the RETURN ADDRESS from the top of the STACK into Register Pair HL.
4ACC
PUSH DE
Preserve Register Pair DE by PUSHing it to the top of the STACK.
4ACD
PUSH BC
Preserve Register Pair BC by PUSHing it to the top of the STACK.
4ACE
PUSH HL
Preserve Register Pair HL by PUSHing it to the top of the STACK.
4ACF
GOSUB to 4B3EH to Get the Directory Track Number. Routine exits with the director track number in Register D.
4AD2
LD E,02H
LET Register E = 02H to point to the HIT sector in the directory track.
4AD4
LD HL,4300H
LET Register Pair HL = 4300H as the base address of BUFFER # 1.
4AD7
RET
RETurn to the caller.

4AD8H – Compute where the directory entry for a file is located. This routine is called to position for a READ (called from 4A69H and 4A7DH) or for a wRITE (called from 4A7DH). Register L will hold the results

4AD8
GOSUB to 4B3EH to Get the Directory Track Number. Routine exits with the director track number in Register D.
4ADB
LD A,B
LET Register A = Register B (the logical file number).
4ADC
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A.
4ADD
If the Z FLAG (Zero) is set then the logical file number is zero, and we can skip a lot of hard work, so JUMP to 4B06H.
4ADF
CP 50H
Compare the value held in Register A against 50H (Decimal: 80). Results:
  • If Register A equals 50H, the Z FLAG is set.
  • If A < 50H, the CARRY FLAG will be set.
  • if A >= 50H, the NO CARRY FLAG will be set.
4AE1
If the NC FLAG (No Carry) is set then it is simply too large so JUMP to 4B02H to set up for an ILLEGAL LOGICAL FILE NUMBER error and RETurn.
4AE3
PUSH BC
Preserve Register Pair BC by PUSHing it to the top of the STACK.
4AE4
LD L,B
Set up for HL to point to the logical file number by first setting Register L to be Register B (the logical file number).
4AE5
LD H,00H
Continue that by setting Register H to 0.
4AE7
LD A,05H
LET Register A = 05H as the divisor.
4AE9
GOSUB to 4B6FH to perform HL = HL / A leaving the remainder in Register A.
4AEC
PUSH AF
Preserve the remainder (held in Register A) by PUSHing it to the top of the STACK.

The next 3 instructions set Register A to be the appropriate directory sector by setting Register E = Register L + 3.

4AED
LD A,L
LET Register A = Register L.
4AEE
ADD 03H
ADD the value 03H to Register A (Results held in Register A).
4AF0
LD E,A
LET Register E = Register A.
4AF1
POP BC
Restore the remainder (held in Register Pair AF) from the top of the STACK into Register Pair BC.
4AF2
LD HL,4300H
LET Register Pair HL = 4300H as the base address of BUFFER # 1.

We next want to test Register B (the remainder) to see if it is ZERO or not, but we don’t necessarily want to mess with other registers, so the simplest way to do that is to increase and decrease Register B.

4AF5
INC B
Bump Register B by 1.
4AF6
DEC B
DECrement Register B by 1.
4AF7
If the Z FLAG (Zero) is set, meaning the remainder was 0, JUMP to 4AFFH.

If we are here, then the remainder was NOT zero, so we need to do some math, via a LOOP set to B interations.

4AF9
LD A,L
LET Register A = Register L. Register L was set to 00H at 4AF2H.
4AFA
ADD 30H
ADD the value 30H to Register A (Results held in Register A).
4AFC
DJNZ 4AFAH
LOOP back to the prior instruction until Register B is ZERO.
4AFE
LD L,A
Put that calculated Register A into Register L.
4AFF
POP BC
Restore Register BC from the top of the STACK.
4B00
XOR A
Set Register A to ZERO and clear all Flags to indicate NO ERROR.
4B01
RET
RETurn to the caller.

4B02H – Set up for an ILLEGAL LOGICAL FILE NUMBER error and RETurn.

4B02
LD A,10H
LET Register A = 10H to prepare for an ILLEGAL LOGICAL FILE NUMBER error.
4B04
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A. Since A was just loaded with 10H, it will be NZ – the signal for an error.
4B05
RET
RETurn to the caller.

4B06H – This is the jump point from the routine to compute where the directory entry for a file is located when the remainder is zero (meaning, no offset calculation was required).

4B06
LD E,03H
LET Register E = 03H to point to DIRECTORY SECTOR 0.
4B08
LD HL,4300H
LET Register Pair HL = 4300H as the base address of BUFFER # 1.
4B0B
XOR A
Set Register A to ZERO and clear all Flags to indicate NO ERROR.
4B0C
RET
RETurn to the caller.

4B0DH – Read a directory sector. Called from 4A71, 4A96, and 4ABD

4B0D
GOSUB to 4675H to read the sector.
4B10
RET NZ
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so RETurn to the caller.
4B11
LD A,00H
LET Register A = 00H. Note: This may not be 00H. A value is put into the 00H at 469AH. That value was the FDC Status Port on input.
4B13
BIT 5,A
Test Bit 5 of Register A (which was the FDC Status Port). Bit 5 of the FDC Status Port response is READ PROTECTED.
4B15
RET Z
If the Z FLAG (Zero) is set then the sector is not READ PROTECTED so RETurn to the caller.
4B16
PUSH DE
Preserve Register Pair DE to the top of the STACK.
4B17
LD DE,0001H
LET Register Pair DE = 0001H, which set D as 00H and E as 01H to read Sector 1, Track 0.
4B1A
GOSUB to 4675H to read Sector 1, Track 0..
4B1D
POP DE
Restore Register Pair DE from the top of the STACK.
4B1E
RET NZ
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so RETurn to the caller.
4B1F
PUSH HL
Presever Register HL (which holds the Buffer Pointer) to the top of the STACK.
4B20
LD A,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL (i.e., the first byte of the BOOT SECTOR) and put it into Register A.
4B21
CP FEH
Compare the value held in Register A against FEH. Results: If Register A equals FEH, the Z FLAG is set; otherwise the NZ FLAG is set.
4B23
If the NZ FLAG (Not Zero) is set then the first byte wasn’t 0FEH which means that it is not a TRSDOS v1.3 DOS disk, so JUMP to 4B39H to point that out.
4B25
INC HL
Bump Register Pair HL by 1 to point to the next byte of the BOOT SECTOR.
4B26
LD D,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL (which, as the 2nd byte, is the directory track on the disk) and put it into Register D.
4B27
LD A,D
LET Register A = Register D (the location of the directory track on the disk).
4B28
CP 29H
Compare the value held in Register A against 29H (Decimal: 40H). Results:
  • If Register A equals 29H, the Z FLAG is set.
  • If A < 29H, the CARRY FLAG will be set.
  • if A >= 29H, the NO CARRY FLAG will be set.
4B2A
If the NC FLAG (No Carry) is set then the Directory Track exceeds Track 40., which is the maximum track number on TRSDOS v1.3 so JUMP to 4B39H.
4B2C
LD HL,4457H
LET Register Pair HL = 4457H, to point to the directory table.
4B2F
PUSH BC
Preserve Register BC to the top of the STACK.
4B30
LD B,00H
LET Register B = 00H, which will be the drive number.
4B32
ADD HL,BC
ADD the value held in Register Pair BC (offset) to Register Pair HL (the directory table). The results are held in Register Pair HL.
4B33
POP BC
Restore Register Pair BC from the top of the STACK.
4B34
LD (HL),D
Store the value held in Register D (the directory track number) into the memory location pointed to by Register Pair HL.
4B35
POP HL
Restore the buffer pointer held at the stop of the STACK into Register Pair HL.
4B36
JUMP to 4675H to read the sector and return.

4B39H – This routine is JUMPed to from 4B23H if it didn’t find 0FEH as the first byte on the disk, meaning that there is no system!

4B39
POP HL
Clear the STACK.
4B3A
LD A,0DH
LET Register A = 0DH, which is the error for NON-SYSTEM DISK.
4B3C
OR A
Since A is not zero, ORing A sets the NZ flag to signify an error.
4B3D
RET
RETurn to the caller.

4B3EH – Get the Directory Track Number. Routine exits with the director track number in Register D.

4B3E
PUSH BC
Preserve Register BC to the top of the STACK.
4B3F
LD B,00H
LET Register B = 00H to set the drive number to :0
4B41
LD HL,4457H
LET Register Pair HL = 4457H to point to the directory track table
4B44
ADD HL,BC
ADD the value held in Register Pair BC (an offset) to Register Pair HL (the directory track table). The results are held in Register Pair HL.
4B45
POP BC
Restore Ragister Pair BC from the top of the STACK.
4B46
LD D,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL (the directory track number) and put it into Register D.
4B47
RET
RETurn to the caller.

4B48H – Subroutine to test to see if a gran is allocated.

The next few instructions create an OPCODE along the lines of BIT nn of Register B.

4B48
AND 07H
MASK the value of Register A against 07H (0000 0111). This has the effect of turning off bits 7, 6, 5, 4, 3, leaving only bits 2, 1, 0 active.
4B4A
4B4B
4B4C
RLCA
RLCA
RLCA
Rotate the bits in A left 3 times so that Bits 2, 1, 0 move to bits 5, 4, 3.
4B4D
OR 40H
OR Register A against 40H (0100 0000). This has the effect of turning on bits 6.

Put the generated OPCODE into 4B53H. If tthe value is 40H then it tests Bit 0; 48H tests Bit 1, 50H tests Bit 2, 58H tests Bit 3, and so on.

4B4F
LD (4B53H),A
Store the value held in Register A into the memory location 4B53H.
4B52
BIT 0,B
Test Bit nn of Register B. The nn was set in the prior instruction.
4B54
RET
RETurn to the caller.

4B55H – Subroutine to multiply Register Pair HL by Register A. On exit, Register A = Overflow and Register Pair HL = the result of the multiplication.

4B55
PUSH BC
Preserve Register BC to the top of the STACK.
4B56
EX DE,HL
Swap Register Pair DE and HL, so that Register Pair DE holds the multiplicand.
4B57
LD C,A
LET Register C = Register A, so that Register C holds the multiplier.
4B58
LD HL,0000H
LET Register Pair HL = 0000H to clear the accumulator.
4B5B
LD A,L
LET Register A = Register L to clear the overflow.
4B5C
LD B,08H
LET Register B = 08H to set up for 8 bits of LOOP.

Start of an 8 bit loop.

4B5E
ADD HL,HL
ADD the value held in Register Pair HL to Register Pair HL. The results are held in Register Pair HL.
4B5F
RLA
Shift the OVERFLOW to the left by rotating the bits in Register A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG being put into BIT 0.
4B60
RLC C
Shift the multiplier into the CARRY FLAG
4B62
If the NC FLAG (No Carry) is set then the multiplier is ZERO so JUMP to 4B67H.
4B64
ADD HL,DE
ADD the value held in Register Pair DE (multiplicand) to Register Pair HL (the current result). The results are held in Register Pair HL.
4B65
ADC 00H
This ADD WITH CARRY of 00H functions to add the overflow bit to the overflow.
4B67
DJNZ 4B5EH
LOOP back to 4B5EH until Register B is ZERO (i.e., all 8 bits).

End of an 8 bit loop. The next bunch of varliable exchanges are to set Register A = Overflow and Register Pair HL = the result of the multiplication

4B69
LD C,A
LET Register C = Register A (the overflow).
4B6A
LD A,L
LET Register A = Register L.
4B6B
LD L,H
LET Register L = Register H.
4B6C
LD H,C
LET Register H = Register C.
4B6D
POP BC
Restore Register Pair BC from the stop of the STACK.
4B6E
RET
RETurn to the caller.

4B6FH – Subroutine to divide Register Pair HL by Register A.

4B6F
PUSH DE
Preserve Register BC to the top of the STACK.
4B70
LD D,A
LET Register D = Register A (the divisor).
4B71
LD E,10H
LET Register E = 10H to cover 16 bits of action.
4B73
XOR A
Set Register A to ZERO and clear all Flags.

Start of a loop.

4B74
ADD HL,HL
LET HL = HL * 2.
4B75
RLA
Put the remainder, if any, into the CARRY bit by rotating the bits in Register A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG being put into BIT 0.
4B76
If the C FLAG (No Carry) is set then there is a remainder overflow, so JUMP to 4B7BH.
4B78
CP D
Compare the value held in Register A against the value held in Register D. Results:
  • If Register A equals D, the Z FLAG is set.
  • If A < D, the CARRY FLAG will be set.
  • if A >= D, the NO CARRY FLAG will be set.
4B79
If the C FLAG (No Carry) is set then the remainder does not equal the divisor so JUMP to 4B7DH.
4B7B
SUB D
If we’re here, then the remainder = the divisor so set REMAINDER = REMAINDER – DIVISOR by SUBtracting the value held in Register D from Register A.
4B7C
INC L
Bump Register L (which is tracking the result) by 1.
4B7D
DEC E
DECrement Register E by 1 as we have 1 less subtraction to do.
4B7E
If the NZ FLAG (Not Zero) is set then we have more bits to process so JUMP BACK to 4B74H.

End of the loop.

4B80
POP DE
Restore Register Pair DE from the top of the STACK.
4B81
RET
RETurn to the caller.

4B82H – RST 28H – DOS Overlay Loader. Register A to hold the requested overlay on entry.

4B82
EX (SP),HL
Clear the restart return from the stack.
4B83
POP HL
Clear the restart return from the stack.
4B84
OR A
Set FLAGS based on the contents of Register A.
4B85
If the P FLAG is set, then the overlay request which was sent was not a proper request, so JUMP to 42AEH to exit.
4B88
PUSH HL
Preserve Register Pair HL to the top of the STACK.
4B89
LD H,A
LET Register H = Register A (the requested overlay).
4B8A
LD A,(4414H)
Fetch the value stored at memory location 4414H and put it into Register A.

NOTE: 4414H is the storage location for the NUMBER OF THE OVERLAY CURRENTLY IN MEMORY.
4B8D
XOR H
We want to see if the requested overlay is the one currently in memory, and we do that with the following 2 instructions. First, eXclusive OR Register H against Register A. The results are stored in Register A.
4B8E
AND 0FH
MASK the value of Register A against 0FH (0000 1111). This has the effect of turning off bits 7, 6, 5, 4, leaving only bits 3, 2, 1, 0 active.
4B90
LD A,H
LET Register A = Register H (the new overlay number).
4B91
LD (4414H),A
Store the value held in Register A into the memory location 4414H.

NOTE: 4414H is the storage location for the NUMBER OF THE OVERLAY CURRENTLY IN MEMORY.
4B94
If the Z FLAG (Zero) is set then the requested overlay is the same one which is already in memory so there is no need to load it. Instead, JUMP to 4BD2H.
4B96
PUSH DE
Preserve Register DE to the top of the STACK.
4B97
PUSH BC
Preserve Register BC to the top of the STACK.
4B98
AND 0FH
MASK the value of Register A against 0FH (0000 1111) to isolate the overlay number. This has the effect of turning off bits 7, 6, 5, 4, leaving only bits 3, 2, 1, 0 active.
4B9A
LD B,A
LET Register B = masked Register A. As masked, it will also be the logical file number.
4B9B
LD (449EH),A
Store the logical file number held in Register A into the DCB at memory location 449EH.
4B9E
XOR A
Set Register A to ZERO and clear all Flags.
4B9F
LD (4498H),A
Store the value held in Register A into the memory location 4498H (default options byte).
4BA2
LD (449DH),A
Store the value held in Register A into the memory location 449DH (drive number byte).
4BA5
SBC HL,HL
Clear HL
4BA7
LD (44A1H),HL
Store the value held in Register HL into the memory location 44A1H (location in file).
4BAA
LD C,A
LET Register C = Register A (the drive number).
4BAB
GOSUB to 4ABAH to read the HIT SECTOR, POP BC and DE and RETurning here.
4BAE
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so JUMP to 4409H.

The next 3 instructions set B = Logical File Number * 2 + 0E0H.

4BB1
LD A,B
LET Register A = Register B.
4BB2
RLCA
Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0).
4BB3
ADD E0H
ADD the value E0H to Register A (Results held in Register A).

The next 2 instructions set HL = 4300H + B.

4BB5
LD L,A
LET Register L = Register A.
4BB6
LD H,43H
LET Register H = 43H (0100 0011).

The next 4 instructions set DE = the First Overlay Extent Element.

4BB8
LD D,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL and put it into Register D.
4BB9
INC HL
Bump Register Pair HL by 1.
4BBA
LD E,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL and put it into Register E.
4BBB
INC E
Bump Register E by 1 to test the value.
4BBC
If INC of Register E caused the Z FLAG (Zero) to be set, then the overlay does not exist, so JUMP to 4CC0H.
4BBF
DEC E
DECrement Register E (the track number) by 1 to put it back where it was.
4BC0
LD (44A7H),DE
Store the value held in Register DE (the extenbt element) into the DCB at memory location 44A7H.
4BC4
LD DE,4497H
LET Register Pair DE = 4497H to point to the overlay DCB.
4BC7
GOSUB to 4BFFH to load the overlay pointed to by Register Pair DE.
4BCA
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so JUMP to 4409H.
4BCD
LD (4BD7H),HL
Store the value held in Register HL (the execution address of the overlay) into the memory location 4BD7H, which is a JP nnnnH.
4BD0
POP BC
Restore Register Pair BC from the STACK
4BD1
POP DE
Restore Register Pair DE from the STACK
4BD2
POP HL
Restore Register Pair HL from the STACK
4BD3
LD A,(4414H)
Fetch the value stored at memory location 4414H (the requested overlay) and put it into Register A.

NOTE: 4414H is the storage location for the NUMBER OF THE OVERLAY CURRENTLY IN MEMORY.
4BD6
JUMP to nnnnH. nnnnH was set at 4BCDH

4BD9H – Routine to load and execute a program.

4BD9
PUSH HL
Preserve Register HL to the top of the STACK.
4BDA
XOR A
Set Register A to ZERO and clear all Flags.
4BDB
LD (442BH),A
Store the value held in Register A into the memory location 442BH.

NOTE: 442BH is the storage location for the LOAD/EXECUTE flag when the disk loader is called. Bit 7 – Z=EXECUTE, NZ=LOAD.
4BDE
GOSUB to 4430H.

NOTE: 4430H is DOS PROGRAM LOADER routine and does the same as the DOS LIBRARY command of LOAD. Requires Register Pair DE to contain the applicable FCB. All Registers are modified.
4BE1
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so JUMP to 4409H.
4BE4
EX (SP),HL
Get Register Pair HL from the top of the stack without removing it.
4BE5
RET
RETurn (which in this case, will execute the program).

4BE6H – Routine to load a program.

4BE6
LD B,00H
LET Register B = 00H, which is the same as 256. This is to be a LRL of a full sector.
4BE8
LD HL,4D00H
LET Register Pair HL = 4D00H as a buffer.
4BEB
GOSUB to 4424H to open the file (which just calls RST 28H with A loaded).
4BEE
RET NZ
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so RETurn to the caller.
4BEF
INC DE
Bump Register Pair DE by 1. DE will now point to the ACCESS CODE.
4BF0
LD A,(DE)
Fetch the value stored at memory location pointed to by Register Pair DE (the ACCESS CODE) and put it into Register A.
4BF1
DEC DE
DECrement Register Pair DE by 1, so now DE points back to the DCB for the file.
4BF2
AND 07H
Isolate the ACCESS CODE by MASKing the value of Register A against 07H (0000 0111). This has the effect of turning off bits 7, 6, 5, 4, 3, leaving only bits 2, 1, 0 active.
4BF4
CP 06H
Compare the value held in Register A against 06H to check for READ or lower. Results:
  • If Register A equals 06H, the Z FLAG is set.
  • If A < 06H, the CARRY FLAG will be set.
  • if A >= 06H, the NO CARRY FLAG will be set.
4BF6
If the C FLAG (No Carry) is set then we had READ or lower access, so we won’t be executing the program. Instead, JUMP to 4BFFH.
4BF8
LD A,(442BH)
Fetch the value stored at memory location 442BH and put it into Register A.

NOTE: 442BH is the storage location for the LOAD/EXECUTE flag when the disk loader is called. Bit 7 – Z=EXECUTE, NZ=LOAD.
4BFB
RLCA
Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0). This puts the EXECUTE vs LOAD bit into the CARRY FLAG
4BFC
LD A,19H
LET Register A = 19H to set up for a possible FILE ACCESS DENIED error.
4BFE
RET C
If the C FLAG (No Carry) is set then we can’t do the operation, RETurn to the caller.
4BFF
LD BC,4DFFH
LET Register Pair BC = 4DFFH (the last byte of the buffer) to set up the data pointer.
4C02
GOSUB to 4C4FH to get the byte.
4C05
CP 01H
Compare the value held in Register A against 01H (program code). Results: If Register A equals 01H, the Z FLAG is set; otherwise the NZ FLAG is set.
4C07
If the Z FLAG (Zero) is set, JUMP to 4C32H.
4C09
CP 02H
Compare the value held in Register A against 02H (execution address). Results: If Register A equals 02H, the Z FLAG is set; otherwise the NZ FLAG is set.
4C0B
If the Z FLAG (Zero) is set, JUMP to 4C25H to put the execution/transfer address into Register Pair HL and RETurn.
4C0D
CP 03H
Compare the value held in Register A against 03H (break key hit). Results: If Register A equals 03H, the Z FLAG is set; otherwise the NZ FLAG is set.
4C0F
If the Z FLAG (Zero) is set, JUMP to 402DH.
4C12
CP 20H
Compare the value held in Register A against 20H (comment block). Results:
  • If Register A equals 20H, the Z FLAG is set.
  • If A < 20H, the CARRY FLAG will be set.
  • if A >= 20H, the NO CARRY FLAG will be set.
4C14
If the C FLAG (No Carry) is set, JUMP to 4C1AH to read the number of bytes in the length byte and jump back into this loop.
4C16
LD A,22H
LET Register A = 22H to set up for a NOT A PROGRAM FILE error
4C18
OR A
Since Register A is not ZERO, this will set the NZ flag to indicate that there is an error.
4C19
RET
RETurn to the caller.

4C1AH – Read the number of bytes set out in the LENGTH BYTE.

4C1A
GOSUB to 4C4FH to get the LENGTH BYTE into Register A.
4C1D
LD B,A
Save the LENGTH BYTE into Register B.
4C1E
GOSUB to 4C4FH to read more bytes and …
4C21
DJNZ 4C1EH
… keep reading bytes until Register B (which was the length byte) is ZERO.
4C23
JUMP to 4C02H to continue the loop.

4C25H – Read the execution/transfer address into Register Pair HL.

4C25
GOSUB to 4C4FH to get the LENGTH BYTE into Register A.
4C28
GOSUB to 4C4FH to read the next byte which the LSB and store it in Register A.
4C2B
LD L,A
LET Register L = Register A (the LSB).
4C2C
GOSUB to 4C4FH to read the next byte which the MSB and store it in Register A.
4C2F
LD H,A
LET Register H = Register A (the MSB).
4C30
XOR A
Set Register A to ZERO and clear all Flags to indicate no error.
4C31
RET
RETurn to the caller.

4C32H – Read data and store in the buffer.

4C32
GOSUB to 4C4FH to read the next byte (which the LENGTH OF DATA BLOCK + 2) and store it in Register A.
4C35
LD B,A
Put the LENGTH OF DATA BLOCK + 2 into Register B.
4C36
GOSUB to 4C4FH to read the next byte (which the LSB of the LOADING ADDRESS) and store it in Register A.
4C39
LD L,A
Put the LSB of the LOADING ADDRESS into Register L.
4C3A
DEC B
DECrement Register B by 1 so that it now holds LENGTH OF DATA BLOCK + 1.
4C3B
GOSUB to 4C4FH to read the next byte (which the MSB of the LOADING ADDRESS) and store it in Register A.
4C3E
LD H,A
Put the MSB of the LOADING ADDRESS into Register H.
4C3F
DEC B
DECrement Register B by 1 so that it now holds LENGTH OF DATA BLOCK.
4C40
GOSUB to 4C4FH to read the next byte (which data) and store it in Register A.
4C43
LD (HL),A
Store the data held in Register A into the memory location pointed to by Register Pair HL.
4C44
CP (HL)
Check to make sure it was really stored by comparing the value held in Register A against the value held in the memory location (HL). Results: If Register A equals (HL), the Z FLAG is set; otherwise the NZ FLAG is set.
4C45
If the NZ FLAG (Not Zero) is set then it didn’t store properly so JUMP to 4C4CH to set up for a MEMORY FAULT.
4C47
INC HL
Bump Register Pair HL by 1 to point to the next spot in the buffer.
4C48
DJNZ 4C40H
LOOP back to 4C40H until Register B is ZERO.
4C4A
JUMP to the main loop at 4C02H.

4C4CH – Process a MEMORY FAULT error.

4C4C
LD A,23H
LET Register A = 23H for the error of MEMORY FAULT
4C4E
RET
RETurn to the caller.

4C4FH – Routine to read a sector.

4C4F
INC C
Bump Register C by 1 to point to the next location.
4C50
If the NZ FLAG (Not Zero) is set then this is not a new sector so JUMP to 4C65H.
4C52
PUSH DE
PUSH Register DE to the top of the STACK.
4C53
EX (SP),IX
Let Index Register IX = DE and put Register IX at the top of the STACK
4C55
PUSH HL
PUSH Register HL to the top of the STACK.
4C56
PUSH DE
PUSH Register DE to the top of the STACK.
4C57
PUSH BC
PUSH Register BC to the top of the STACK.
4C58
GOSUB to 4766H to read a Sector and Return With Error or Continue.
4C5B
POP BC
MOVE the value held at the stop of the STACK into Register Pair BC.
4C5C
POP DE
MOVE the value held at the stop of the STACK into Register Pair DE.
4C5D
POP HL
MOVE the value held at the stop of the STACK into Register Pair HL.
4C5E
POP IX
MOVE the value held at the stop of the STACK into Register Pair IX.
4C60
If the Z FLAG (Zero) is set then there was no error, so JUMP to 4C65H.
4C62
4C63
INC SP
INC SP
Bump Register Pair SP by 2 to clear the most recent RETURN address.
4C64
RET
RETurn to the caller.

4C65H – Continuation of the above routine when we are not at a new sector.

4C65
PUSH BC
PUSH Register BC to the top of the STACK.
4C66
LD B,4DH
LET Register B = 4DH. THis modified Register Pair BC to point to the data byte.
4C68
LD A,(BC)
Fetch the value stored at memory location pointed to by Register Pair BC (i.e., the data byte) and put it into Register A.
4C69
POP BC
Restore Register Pair BC from the top of the STACK.
4C6A
RET
RETurn to the caller.

4C6BH – Start up and check the drive. Register C holds the drive being sought.

4C6B
LD A,(4413H)
Fetch the value stored at memory location 4413H and put it into Register A.

NOTE: 4413H is the storage location for the NUMBER OF DISK DRIVES in the system.
4C6E
CP C
Compare the value held in Register A against the value held in Register C. Results:
  • If Register A equals C, the Z FLAG is set.
  • If A < C, the CARRY FLAG will be set.
  • if A >= C, the NO CARRY FLAG will be set.
4C6F
If the NC FLAG (No Carry) is set then the drive requested is on the system so continue the routine by JUMPing to 4C75H.
4C71
LD A,02H
LET Register A = 02H to set up for a DISK DRIVE NOT ON SYSTEM error.
4C73
OR A
Since Register A is not 00H, this sets the NZ FLAG to signify that an error occurred.
4C74
RET
RETurn to the caller.

4C75H – The prior routine jumps here if the requested drive id on the system.

4C75
INput a byte 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
4C77
AND 80H
MASK the value of Register A against 80H (1000 0000). This has the effect of turning off bits 6, 5, 4, 3, 2, 1, 0, leaving only bit 7 (DRIVE NOT READY) active.
4C79
GOSUB to 4566H.

NOTE: 4566H is the routine which resets the FDC.
4C7C
If the Z FLAG (Zero) is set then the disk is good to go so JUMP to 4C89H.
4C7E
GOSUB to 44D4H.

NOTE: 44D4H is the routine which handles DRIVE SELECT (Register C holds the drive number to be used).
4C81
PUSH BC
Preserve Register Pair BC to the top of the STACK.
4C82
LD BC,9000H
LET Register Pair BC = 9000H to set up for a delay of 1/2 second.
4C85
GOSUB to 0060H to do the delay.
4C88
POP BC
Restore Register Pair BC from the top of the STACK.
4C89
GOSUB to 44D4H.

NOTE: 44D4H is the routine which handles DRIVE SELECT (Register C holds the drive number to be used).
4C8C
GOSUB to 4566H.

NOTE: 4566H is the routine which resets the FDC.
4C8F
LD A,(4427H)
Fetch the value stored at memory location 4427H (previously selected drive) and put it into Register A.

NOTE: 4427H is the storage location for the currently selected disk drive number.
4C92
CP C
Compare the value held in Register A against the value held in Register C. Results: If Register A equals C, the Z FLAG is set; otherwise the NZ FLAG is set.
4C93
If the NZ FLAG (Not Zero) is set thenm the selected drive was NOT the same as the prior selected drive so JUMP to 4C97H.
4C95
XOR A
Set Register A to ZERO and clear all Flags to indicate no error.
4C96
RET
RETurn to the caller.

4C97H – Read until you get an INDEX LOW, INDEX HIGH, INDEX LOW, and then set CARRY FLAG if WRITE PROTECT is enabled.

4C97
PUSH BC
Preserve Register BC to the top of the STACK.
4C98
LD BC,62AAH
LET Register Pair BC = 62AAH to set up a counter.
4C9B
GOSUB to 4CB0H to check to see if the BC counter is zero and, if so, exit, and if not get the disk status and look for an index hole. Returns NZ if the index hole was found.
4C9E
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so JUMP to 4C9BH.
4CA0
GOSUB to 4CB0H to check to see if the BC counter is zero and, if so, exit, and if not get the disk status and look for an index hole. Returns NZ if the index hole was found.
4CA3
If the Z FLAG (Zero) is set, JUMP to 4CA0H.
4CA5
GOSUB to 4CB0H to check to see if the BC counter is zero and, if so, exit, and if not get the disk status and look for an index hole. Returns NZ if the index hole was found.
4CA8
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so JUMP to 4CA5H.
4CAA
POP BC
Restore Register Pair BC from the top of the STACK into .
4CAB
RLCA
Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0). This moves Bit 6 from the FDC (Write Protect) to Bit 7.
4CAC
AND 80H
MASK the value of Register A against 80H (1000 0000). This has the effect of turning off bits 6, 5, 4, 3, 2, 1, 0, leaving only bit 7 active.
4CAE
ADD A,A
ADD the value held in Register A to Register A (Results held in Register A) to set the CARRY FLAG if the WRITE PROTECT bit was on.
4CAF
RET
RETurn to the caller.

4CB0H – Subroutine to check to see if the BC counter is zero and, if so, exit, and if not get the disk status and look for an index hole. Returns NZ if the index hole was found.

4CB0
DEC BC
DECrement Register Pair BC by 1 to decrease the counter.
4CB1
LD A,B
Since there is no way to directly check BC for 0, a common way to handle this is to set Register A = Register B.
4CB2
OR C
and then OR Register C against Register A. The results are stored in Register A.
4CB3
If the Z FLAG (Zero) is set, then both B and C were zero, so JUMP to 4CBAH.
4CB5
INput a byte 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
4CB7
BIT 1,A
Check the INDEX FOUND by testing Bit 1 of Register A.
4CB9
RET
RETurn to the caller.

4CBAH – Clear the stack, set up for a DISK TIME OUT error, and return.

4CBA
4CBA
POP BC
POP BC
Clear the STACK.
4CBC
LD A,08H
LET Register A = 08H for a DISK TIME OUT error.
4CBE
OR A
Since Register A is not 00H, this sets the NZ FLAG to signify that an error occurred.
4CBF
RET
RETurn to the caller.

4CC0H – Display an error and return to DOS PROMPT.

4CC0
LD HL,4CCCH
LET Register Pair HL = 4CCCH to point to the error message.
4CC3
GOSUB to 021BH.

NOTE: 021BH will display the character at (HL) until a 03H is found.
4CC6
GOSUB to 0049H to wait for user action.
4CC9
JUMP to 4030H.

NOTE: 4030H is the OPERATION ABORTED routine. Routine runs, displays an error, and returns to the DOS PROMPT.

4CCCH – NO SYSTEM ERROR storage location.

4CCC
1C 1F 17 “0 NS” 0D
NS Error

4CE1H – “Dual” Driver.

4CE1
PUSH BC
Preserve Register Pair BC to the top of the STACK. Register C has the character to display.
4CE2
LD IX,401DH
LET Register Pair IX = 401DH (the VIDEO DCB).
4CE6
GOSUB to 0473H to display a character.
4CE9
POP BC
Restore Register Pair BC from the top of the STACK.
4CEA
LD A,C
LET Register A = Register C (the character to display).
4CEB
CP 0DH
Compare the value held in Register A against 0DH. Results: If Register A equals 0DH, the Z FLAG is set; otherwise the NZ FLAG is set.
4CED
If the Z FLAG (Zero) is set then we have a CARRIAGE RETURN, so JUMP to 4CF2H.
4CEF
CP 20H
Compare the value held in Register A against 20H looking for a CONTROL CHARACTER. Results:
  • If Register A equals 20H, the Z FLAG is set.
  • If A < 20H, the CARRY FLAG will be set.
  • if A >= 20H, the NO CARRY FLAG will be set.
4CF1
RET C
If the C FLAG (No Carry) is set then it is a CONTROL CHARACTER, and RETurn to the caller.
4CF2
PUSH BC
PUSH Register BC (the character to display) to the top of the STACK.
4CF3
LD IX,4025H
LET Register Pair IX = 4025H (the PRINTER DCB).
4CF7
GOSUB to 03C2H to print the character held in Register C and return.
4CFA
POP BC
Restore Register Pair BC from the top of the STACK.
4CFB
LD A,C
LET Register A = Register C (the character).
4CFC
RET
RETurn to the caller.

4CFDH – DATA STORAGE LOCATION.

nn
Is the “DO” flag active? 0=NO, 255=YES.

4E00H – OVERLAY 0 – DOS Initialization.

First we need to set up the interrupts

4E00
DI
Disable Interrupts.
4E01
IM 1
Set Interrupt Mode to 1
4E03
LD SP,409FH
Set the System STACK to start at 409FH.
4E06
XOR A
Set Register A to ZERO and clear all Flags.
4E07
OUTput the value held in Register A to port F4H to disable the non-maskable interrupts.

NOTE: Port E4H is the Non-Maskable Interrupt Latch. Sending a 0 to E4 will turn off all Non-Maskable Interrupts.
4E09
LD A,20H
LET Register A = 20H (0010 0000).
4E0B
OUTput the value held in Register A (= Bit 5 On; everything else off) to port ECH to enable VIDEO WAITS and turn off the cassette motor, set to normal screen width, set to Kana character set, disable the I/O bus, and set the system to 2MHZ speed.

Port ECH is the various controls port.
4E0D
LD A,04H
LET Register A = 04H (0000 0100).
4E0F
OUTput the value held in Register A to port E0H (the Maskable Interrupt Port). Sending Bit 2 high sets this to 4046H to enable the CLOCK INTERRUPT

Decode the TRSDOS v1.3 disk serial number from Binary Coded Decimal to Text

4E11
LD HL,43F4H
LET Register Pair HL = 43F4H, which is the location of the encoded serial number of the TRSDOS v1.3 disk.
4E14
LD IX,5079H
LET Register Pair IX = 5079H, which is the location of the serial number text.

Start of a loop to convert 8 bytes of Binary Coded Decimal into Text

4E18
LD B,08H
LET Register B = 08H to prepare to count 8 Binary Coded Decimal numbers.
4E1A
LD D,00H
LET Register D = 00H.
4E1C
GOSUB to 4FF8H to convert the BCD number pointed to by Register Pair HL to ASCII.
4E1F
INC HL
Bump Register Pair HL by 1 to point to the next character.
4E20
DJNZ 4E1CH
LOOP back to 4E1CH until Register B is ZERO.

End of loop

4E22
LD (IX+00H),00H
Store the value of nn into the memory location pointed to by the Index Register IX + dd bytes. Note: nn and dd are very likely set elsewhere, and I wouldn’t be suprised if this was to add a CARRIAGE RETURN to the end of the text of the decoded serial number.

4E26H – Determine the system’s Memory Size

4E26
LD HL,FFFFH
LET Register Pair HL = FFFFH, which is the highest memory location for 64K memory when entering this routine..
4E29
LD A,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL and put it into Register A.
4E2A
LD B,A
LET Register B = Register A.
4E2B
CPL
Invert Register A
4E2C
LD (HL),A
Store the value held in Register A (i.e., the inverted value) into the memory location pointed to by Register Pair HL.
4E2D
CP (HL)
Compare the value held in Register A against the value held in the memory location (HL). Results:
  • If Register A equals (HL), the Z FLAG is set.
  • If A < (HL), the CARRY FLAG will be set.
  • if A >= (HL), the NO CARRY FLAG will be set.
4E2E
LD (HL),B
Store the value held in Register B (i.e., the original value) into the memory location pointed to by Register Pair HL.
4E2F
If the Z FLAG (Zero) is set then we were able to write to that memory location, so JUMP to 4E37H. This is t he routine exit.
4E31
LD A,H
If we are here, then we were unable to write to that memory location, so we need to back up from 64K to 48K to 32K to 16K. To do this, we need to move the MSB into Register A for math.
4E32
SUB 40H
SUBtract the value 40H from Register A to drop down 16K.
4E34
LD H,A
LET Register H = Register A (which is the new MSB of the memory location to test).
4E35
JUMP to 4E29H.

4E37H – Continuing Routine to Determine Memory Size and Set Up to Display that Number.

4E37
LD (4411H),HL
Store the value held in Register HL into the memory location 4411H.

NOTE: 4411H is the storage location for the MEMORY PROTECT address.
4E3A
LD (40A0H),HL
Store the value held in Register HL into the memory location 40A0H.

NOTE: 40A0H is the storage location for the TOP OF FREE MEMORY
4E3D
LD (4415H),HL
Store the value held in Register HL into the memory location 4415H.

NOTE: 4415H is the storage location for the ADDRESS OF THE END OF PHYSICAL MEMORY.
4E40
GOSUB to 01C9H to clear the screen.
4E43
LD A,(4412H)
Fetch the value stored at memory location 4412H (i.e., the MSB of the memory size) and put it into Register A.
4E46
LD HL,5010H
LET Register Pair HL = 5010H to point to the message storage area of 48.
4E49
AND F0H
MASK the value of Register A (the MSB of the memory size) against F0H (1111 0000) to test for 48K. This has the effect of turning off bits 3, 2, 1, 0, leaving only bits 7, 6, 5, 4 active.
4E4B
CP F0H
Compare the value held in Register A against F0H. Results: If Register A equals F0H, the Z FLAG is set; otherwise the NZ FLAG is set.
4E4D
If the Z FLAG (Zero) is set then the memory size is 48K so stop testing by JUMPing to 4E59H.
4E4F
LD HL,5012H
LET Register Pair HL = 5012H to point to the message storage area of 32.
4E52
CP B0H
Compare the value held in Register A against B0H to test for 32K. Results: If Register A equals B0H, the Z FLAG is set; otherwise the NZ FLAG is set.
4E54
If the Z FLAG (Zero) is set then the memory size is 32K so stop testing by JUMPing to 4E59H.
4E56
LD HL,5014H
No more testing; assume its 16K. LET Register Pair HL = 5014H to point to the message storage area of 16.
4E59
LD DE,504CH
LET Register Pair DE = 504CH to point to the message storage area of 00K
4E5C
LD BC,0002H
LET Register Pair BC = 0002H to set up for a 2 byte move under a LDIR instruction.
4E5F
LDIR
Transfer a byte of data from the memory location pointed to by HL to the memory location pointed to by DE. Then HL and DE are incremented and BC is decremented. If BC is not zero, this operation is repeated. Interrupts can trigger while this instruction is processing.

4E61H – Determine the Number of Disk Drives and Set Up to Display that Number.

4E61
LD C,00H
LET Register C = 00H for drive 0.
4E63
LD B,04H
LET Register B = 04H to signify a maximum of 4 drives.
4E65
GOSUB to 44D4H.

NOTE: 44D4H is the routine which handles DRIVE SELECT (Register C holds the drive number to be used).
4E68
PUSH BC
PUSH Register BC (the drive select code) to the top of the STACK.
4E69
GOSUB to 4FDAH to test to see if the disk drive (stored in Register C) is present on the system.
4E6C
POP BC
Restore Register BC (the drive select code) from the top of the STACK.
4E6D
If the Z FLAG (Zero) is set then the drive was NOT on the system, so JUMP to 4E72H to deal with that.
4E6F
INC C
Bump Register C by 1 to point to the next disk drive.
4E70
DJNZ 4E65H
LOOP BACK to 4E65H until Register B is ZERO and 4 drives were tested.
4E72
LD A,C
LET Register A = Register C (which is the last disk drive which was tested).
4E73
DEC A
DECrement Register A by 1 to go to the drive before that one.
4E74
LD (4413H),A
Store the value held in Register A into the memory location 4413H.

NOTE: 4413H is the storage location for the NUMBER OF DISK DRIVES in the system.
4E77
INC A
Bump Register A by 1 to prepare to turn it into an ASCII code for that drive number.
4E78
ADD 30H
ADD the value 30H to Register A (Results held in Register A) to turn Register A into an ASCII rendition of the number that was in Register A.
4E7A
LD (506BH),A
Store the value held in Register A (the ASCII rendition of the number of disk drives in the system) into the memory location 506BH.

4E7DH – Display the Model III Graphic Logo and the Copyright Notices.

4E7D
LD C,00H
LET Register C = 00H to point to drive 0
4E7F
GOSUB to 4566H.

NOTE: 4566H is the routine which resets the FDC.
4E82
GOSUB to 44D4H.

NOTE: 44D4H is the routine which handles DRIVE SELECT (Register C holds the drive number to be used).
4E85
LD HL,516CH
LET Register Pair HL = 516CH which is the message storage area for the Model III logo.
4E88
GOSUB to 021BH.

NOTE: 021BH will display the character at (HL) until a 03H is found.
4E8B
LD A,(38FFH)
Fetch the value stored at memory location 38FFH (which is the keyboard) and put it into Register A.
4E8E
LD (4F3AH),A
Store the value held in Register A (the key which was hit) into the memory location 4F3AH (which is the middle of an OPCODE).
4E91
LD HL,5016H
LET Register Pair HL = 5016H which is the message storage area for the DOS version
4E94
GOSUB to 021BH.

NOTE: 021BH will display the character at (HL) until a 03H is found.
4E97
LD HL,508AH
LET Register Pair HL = 508AH which is the messagr storage area for the copyright notice.
4E9A
GOSUB to 021BH.

NOTE: 021BH will display the character at (HL) until a 03H is found.
4E9D
EI
Enable Interrupts.
4E9E
LD HL,(42B5H)
Fetch the value stored at memory location 42B5H (the DATE flag) and put it into Register HL.
4EA1
LD DE,4455H
LET Register Pair DE = 4455H to signify the valid date flag.
4EA4
PUSH HL
PUSH Register HL (the date flag) to the top of the STACK.
4EA5
OR A
Set FLAGS based on the contents of Register A; but really just to clear the CARRY FLAG (which is about to get used).
4EA6
SBC HL,DE
Subtract with Carry DE from HL to see if the date flag is valid.
4EA8
POP HL
Restore Register HL (the date flag) from the top of the STACK.
4EA9
If the result of the SBC is zero, then the date flag is valid, so skip to 4F2EH
4EAC
LD (42B5H),DE
If we are here, then the date flag wasn’t valid, so store the value held in Register DE (the valid date flag) into the memory location 42B5H.
4EB0
JUMP to 4EB8H to get the DATE and TIME from the user.

4EB2H – Get the Current Date from the System User.

4EB2
LD HL,512DH
LET Register Pair HL = 512DH to point to the message storage area of TRY AGAIN.
4EB5
GOSUB to 021BH.

NOTE: 021BH will display the character at (HL) until a 03H is found.
4EB8
LD HL,513BH
LET Register Pair HL = 513BH to point to the message storage area of the DATE PROMPT.
4EBB
GOSUB to 4F84H to display the DATE PROMPT and get the date from the user. On exit, Register A holds the number of characters entered, and HL points to the buffer location where those characters are stored.
4EBE
CP 08H
Compare the value held in Register A against 08H (meaning, 8 characters were returned from the user). Results: If Register A equals 08H, the Z FLAG is set; otherwise the NZ FLAG is set.
4EC0
If the NZ FLAG (Not Zero) is set then we didn’t get 8 characters so JUMP BACK to 4EB2H to display the TRY AGAIN message and try to get a valid date from the user.
4EC2
LD C,2FH
LET Register C = 2FH (ASCII: / so we can further try to validate the 8 characters we got into a valid date.
4EC4
GOSUB to 4FA6H to decode the date from the input and check the delimiter in Register C. Return NZ FLAG for an error.
4EC7
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so JUMP BACK to 4EB2H to display the TRY AGAIN message and try to get a valid date from the user.
4EC9
LD HL,4F81H
LET Register Pair HL = 4F81H (the TRSDOS v1.3 memory storage location for the decoded SECONDS or YEAR).
4ECC
LD DE,42B7H
LET Register Pair DE = 42B7H which is the memory location for the date which was held from boot up (i.e., the default date).
4ECF
LD BC,0003H
LET Register Pair BC = 0003H to signify a move of 3 bytes.
4ED2
LDIR
Overwrite the default (boot up) date with the one entered.
4ED4
LD A,(4F81H)
Fetch the value stored at memory location 4F81H (the TRSDOS v1.3 memory storage location for the decoded SECONDS or YEAR) and put it into Register A.
4ED7
CP 64H
Compare the value held in Register A against 64H (Decimal: 100). Results:
  • If Register A equals 64H, the Z FLAG is set.
  • If A < 64H, the CARRY FLAG will be set.
  • if A >= 64H, the NO CARRY FLAG will be set.
4ED9
If the NC FLAG (No Carry) is set, then the YEAR is 100 or higher, which is too much, so so JUMP BACK to 4EB2H to display the TRY AGAIN message and try to get a valid date from the user.
4EDB
LD B,A
If we’re here the YEAR is GOOD, so LET Register B = Register A (the year).
4EDC
LD A,(4F83H)
Fetch the value stored at memory location 4F83H (the TRSDOS v1.3 memory storage location for the decoded HOURS or MONTH) and put it into Register A.
4EDF
CP 0DH
Compare the value held in Register A against 0DH (Decimal: 13). Results:
  • If Register A equals 0DH, the Z FLAG is set.
  • If A < 0DH, the CARRY FLAG will be set.
  • if A >= 0DH, the NO CARRY FLAG will be set.
4EE1
If the NC FLAG (No Carry) is set, then the MONTH is 13 or higher, which is too much, so so JUMP BACK to 4EB2H to display the TRY AGAIN message and try to get a valid date from the user.
4EE3
OR A
Set FLAGS based on the contents of Register A to set up for a test against MONTH of ZERO.
4EE4
If the Z FLAG (Zero) is set, then the MONTH is 0, which is too low, so so JUMP BACK to 4EB2H to display the TRY AGAIN message and try to get a valid date from the user.
4EE6
ADD A,B
If we’re here then we have a valid YEAR and a valid MONTH, so to prepare to test the DAY we must first ADD the value held in Register B (YEAR) to Register A (MONTH).
4EE7
LD B,A
Save that MONTH + YEAR (held in Register A) into Register B.
4EE8
LD A,(4F82H)
Fetch the value stored at memory location 4F82H (the TRSDOS v1.3 memory storage location for the decoded MINUTES or DAY) and put it into Register A.
4EEB
CP 20H
Compare the value held in Register A against 20H (Decimal: 32). Results:
  • If Register A equals 20H, the Z FLAG is set.
  • If A < 20H, the CARRY FLAG will be set.
  • if A >= 20H, the NO CARRY FLAG will be set.
4EED
If the NC FLAG (No Carry) is set, then the DAY is 32 or higher, which is too much, so so JUMP BACK to 4EB2H to display the TRY AGAIN message and try to get a valid date from the user.
4EEF
OR A
Set FLAGS based on the contents of Register A to set up for a test against DAY of ZERO.
4EF0
If the Z FLAG (Zero) is set, then the DAY is 0, which is too low, so so JUMP BACK to 4EB2H to display the TRY AGAIN message and try to get a valid date from the user.
4EF2
ADD A,B
ADD the value held in Register B (the MONTH + YEAR) to Register A (the DAY). Results held in Register A.
4EF3
OR A
Set FLAGS based on the contents of Register A.
4EF4
If the Z FLAG (Zero) is set, then something is WAY off, so so JUMP BACK to 4EB2H to display the TRY AGAIN message and try to get a valid date from the user.
4EF6
JUMP to 4EFEH to continue checking the date.

4EF8H – Get the Current TIME from the System User.

4EF8
LD HL,512DH
LET Register Pair HL = 512DH to point to the message storage area of TRY AGAIN.
4EFB
GOSUB to 021BH.

NOTE: 021BH will display the character at (HL) until a 03H is found.
4EFE
LD HL,5154H
LET Register Pair HL = 5154H to point to the message storage area of the TIME PROMPT.
4F01
GOSUB to 4F84H to display the TIME PROMPT and get the time from the user. On exit, Register A holds the number of characters entered, and HL points to the buffer location where those characters are stored.
4F04
OR A
Set FLAGS based on the contents of Register A.
4F05
If the Z FLAG (Zero) is set then we got a null response so JUMP to 4F2EH to continue.
4F07
LD C,3AH
LET Register C = 3AH (ASCII: :).
4F09
GOSUB to 4FA6H to decode the date from the input and check the delimiter in Register C. Return NZ FLAG for an error.
4F0C
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so JUMP BACK to 4EF8H to display the TRY AGAIN message and try to get a valid time from the user.
4F0E
LD A,(4F81H)
Fetch the value stored at memory location 4F81H the TRSDOS v1.3 memory storage location for the decoded SECONDS or YEAR) and put it into Register A.
4F11
CP 3DH
Compare the value held in Register A against 3DH (Decimal: 61). Results:
  • If Register A equals 3DH, the Z FLAG is set.
  • If A < 3DH, the CARRY FLAG will be set.
  • if A >= 3DH, the NO CARRY FLAG will be set.
4F13
If the NC FLAG (No Carry) is set, then the SECONDS are 61 or higher, which is too much, so so JUMP BACK to 4EF8H to display the TRY AGAIN message and try to get a valid time from the user.
4F15
LD A,(4F82H)
Fetch the value stored at memory location 4F82H (the TRSDOS v1.3 memory storage location for the decoded MINUTES or DAY) and put it into Register A.
4F18
CP 3DH
Compare the value held in Register A against 3DH (Decimal: 61). Results:
  • If Register A equals 3DH, the Z FLAG is set.
  • If A < 3DH, the CARRY FLAG will be set.
  • if A >= 3DH, the NO CARRY FLAG will be set.
4F1A
If the NC FLAG (No Carry) is set, then the MINUTES are 61 or higher, which is too much, so so JUMP BACK to 4EF8H to display the TRY AGAIN message and try to get a valid time from the user.
4F1C
LD A,(4F83H)
Fetch the value stored at memory location 4F83H (the TRSDOS v1.3 memory storage location for the decoded HOURS or MONTH) and put it into Register A.
4F1F
CP 18H
Compare the value held in Register A against 18H (Decimal: 24). Results:
  • If Register A equals 18H, the Z FLAG is set.
  • If A < 18H, the CARRY FLAG will be set.
  • if A >= 18H, the NO CARRY FLAG will be set.
4F21
If the NC FLAG (No Carry) is set, then the HOUR is 24 or higher, which is too much, so so JUMP BACK to 4EF8H to display the TRY AGAIN message and try to get a valid time from the user.
4F23
LD HL,4F81H
LET Register Pair HL = 4F81H (the TRSDOS v1.3 memory storage location for the decoded SECONDS or YEAR).
4F26
LD DE,4217H
LET Register Pair DE = 4217H (i.e., the time in the time storage area).
4F29
LD BC,0003H
LET Register Pair BC = 0003H (i.e., the number of bytes to move).
4F2C
LDIR
Move the user entered time into the system time storage area.
4F2E
LD HL,42B7H
LET Register Pair HL = 42B7H (i.e., the memory location for the boot-up date).
4F31
LD DE,421AH
LET Register Pair DE = 421AH (i.e., the memory location for the real time date).
4F34
LD BC,0003H
LET Register Pair BC = 0003H (i.e., the number of bytes to move).
4F37
LDIR
Move the boot up date memory location into the real time date memory location.
4F39
LD A,nn
LET Register A = nn. nn was set at 4E8EH to be the key which was pressed at bootup.
4F3B
OR A
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A.
4F3C
If the NZ FLAG (Not Zero) is set then a key was pressed so JUMP to 4296H.

NOTE: 4296H is the DOS READY jump routine.
4F3F
LD C,00H
LET Register C = 00H to signify DRIVE 0.
4F41
GOSUB to 4A93H to read the GAT into Buffer # 2.
4F44
If the NZ FLAG (Not Zero) is set then the prior subroutine exited with an error, so JUMP to 4409H.
4F47
LD A,(4DE0H)
Fetch the value stored at memory location 4DE0H and put it into Register A.

NOTE: 4DE0H is the first byte of an AUTO command.
4F4A
CP 0DH
Compare the value held in Register A against 0DH. Results: If Register A equals 0DH, the Z FLAG is set; otherwise the NZ FLAG is set.
4F4C
If the Z FLAG (Zero) is set then there was no AUTO command so JUMP to 4296H.

NOTE: 4296H is the DOS READY jump routine.
4F4F
LD A,C9H
LET Register A = C9H, which is the OPCODE for RET which, if placed in a vector, would basically ignore the jump and just return.
4F51
LD (42AEH),A
Store the value held in Register A into the memory location 42AEH which is currently a JUMP VECTOR (thus, nullifying that VECTOR).
4F54
LD HL,4DE0H
LET Register Pair HL = 4DE0H, which is a pointer to the AUTO command.
4F57
LD DE,4225H
LET Register Pair DE = 4225H, which is the start of the DOS command buffer.
4F5A
LD BC,0020H
LET Register Pair BC = 0020H (Decimal: 32) for the number of bytes to move.
4F5D
LDIR
Move the 32 potential bytes for an AUTO command into the DOS command buffer, so it is as if that command was just typed in.
4F5F
LD HL,4F6BH
LET Register Pair HL = 4F6BH, which is a pointed to the AUTO FUNCTION ENGAGED message.
4F62
GOSUB to 021BH.

NOTE: 021BH will display the character at (HL) until a 03H is found.
4F65
LD HL,4225H
LET Register Pair HL = 4225H, which is the start of the DOS command buffer.
4F68
JUMP to 4299H to execute the command in the DOS command buffer and exit.

4F6BH – Message Storage Area.

4F6B
“AUTO FUNCTION ENGAGED” + 0DH
Message.
4F81
NOP
Storage location for SECONDS and YEAR
4F82
NOP
Storage location for MINUTES and DAY
4F83
NOP
Storage location for HOURS and MONTH

4F84H – Subroutine to display the DATE (or TIME) PROMPT and get the date (or time) from the user. On exit, Register A holds the number of characters entered, and HL points to the buffer location where those characters are stored.

4F84
GOSUB to 021BH.

NOTE: 021BH will display the character at (HL) until a 03H is found.
4F87
LD B,08H
LET Register B = 08H to get up to 8 characters from a ROM call to 0040H.
4F89
LD HL,4225H
LET Register Pair HL = 4225H (the keyboard buffer). HL must be set for a ROM call to 0040H.
4F8C
GOSUB to 0040H in the ROM.

NOTE: This routine gets a full line from the keyboard. The line is terminated by a carriage return or BREAK. Characters typed are echoed to the display. On entry, B must be the maximum length of line to be accepted and (HL) must be the storage buffer which should be set to B+1. On Exit, CARRY will be set if the BREAK key was hit, Register B will contain the number of characters entered, and (HL) will contain the line from the keyboard followed by the terminating character. Register paid DE is altered in this routine.

4F8F
LD A,B
Copy the number of characters received (which the ROM call of 0040H puts into Register B) into Register A.
4F90
CP 05H
Compare the value held in Register A against 05H. Results: If Register A equals 05H, the Z FLAG is set; otherwise the NZ FLAG is set.
4F92
RET NZ
If the NZ FLAG (Not Zero) is set, then we did not get exactly 5 characters, so RETurn to the caller.

This doesn’t make entire sense to me because this routine is shared by DATA and TIME, and the next instructions appear to say “Well, if we got 5 characters, then let’s just set the rest to :00”

4F93
PUSH HL
PUSH Register HL (the buffer location) to the top of the STACK.
4F94
LD HL,422BH
LET Register Pair HL = 422BH (the “:nn” [seconds] in the keyboard buffer collecting this information).
4F97
LD (HL),3AH
Store a : into the memory location pointed to by Register Pair HL.
4F99
INC HL
Bump Register Pair HL by 1.
4F9A
LD (HL),30H
Store a 0 into the memory location pointed to by Register Pair HL.
4F9C
INC HL
Bump Register Pair HL by 1.
4F9D
LD (HL),30H
Store a 0 into the memory location pointed to by Register Pair HL.
4F9F
INC HL
Bump Register Pair HL by 1.
4FA0
LD (HL),0DH
Store an ENTER into the memory location pointed to by Register Pair HL.
4FA2
POP HL
Restore the buffer location from the top of the STACK into Register Pair HL.
4FA3
LD A,08H
LET Register A = 08H to denote that now we have 8 characters (the original 5, plus “:00”).
4FA5
RET
RETurn to the caller.

4FA6H – Subroutine to read the value of HOURS/MONTHS, MINUTES/DAY, or SECONDS/YEARS from their memory location and convert that value from text.

4FA6
LD DE,4F83H
LET Register Pair DE = 4F83H (the TRSDOS v1.3 memory storage location for the decoded HOURS or MONTH).
4FA9
LD B,03H
LET Register B = 03H to set a loop for 3 numbers (i.e., memory storage locations 4F83 [HOURS/MONTH], 4F82 [MINUTES/DAY], and 4F81 [SECONDS/YEAR]).
4FAB
PUSH DE
Save the current pointer to the TIME or DATE storage area.
4FAC
GOSUB to 4FBBH to decode the number from text.
4FAF
POP DE
Restore the current pointer to the TIME or DATE storage agrea from the top of the STACK into Register Pair DE.
4FB0
RET NZ
If the NZ FLAG (Not Zero) is set then there was a conversion error reported by the routine at 4FBBH, so RETurn to the caller.
4FB1
LD (DE),A
Store the decided number (held in Register A) into the memory location pointed to by Register Pair DE.
4FB2
DEC DE
DECrement Register Pair DE by 1 (so 4F83H [H/M] changes to 4F82 [M/D] and again to 4F81 [S/Y]).
4FB3
DEC B
DECrement Register B by 1 to count down from 3 to 1.
4FB4
RET Z
If the Z FLAG (Zero) is set, meaning that we have processed all 3 memory locations, then RETurn to the caller.
4FB5
LD A,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL and put it into Register A. This should be the applicable delimeter of either / if this routine involves processing the date, or : if this routine involves processing the time.
4FB6
INC HL
Bump Register Pair HL by 1 since we just filled the memory location pointed to by HL with a delimeter.
4FB7
CP C
Compare the value held in Register A (the delimeter in memory) against the value held in Register C (the appropriate delimeter fof the routine), to make sure they match. Results: If Register A equals C, the Z FLAG is set; otherwise the NZ FLAG is set.
4FB8
If the Z FLAG (Zero) is set then the appropriate delimiter was used, so continue on with the 3 numbers via a JUMP to 4FABH.
4FBA
RET
If we are here, NZ FLAG is set because the delimiter was not appropriate, so RETurn to the caller in an error state.

4FBBH – Subroutine to decode the two digit decimal number into BINARY.

4FBB
GOSUB to 4FD2H to decode a number from ASCII. The C FLAG will be set if there was an error.
4FBE
If the NC FLAG (No Carry) is set then the digit provided was invalid, so JUMP to 4FD0H to set the error flag (via an OR A) and RETurn.
4FC0
LD E,A
LET Register E = Register A (the digit being processed).
4FC1
RLCA
Rotate the bits in A left, which is the same as A = A * 2.
4FC2
RLCA
Rotate the bits in A left, which is the same as A = A * 2 (so now the original A = A * 4).
4FC3
ADD A,E
ADD the value held in Register E to Register A (Results held in Register A, so now the original A = A * 5).
4FC4
RLCA
Rotate the bits in A left, which is the same as A = A * 2 (so now the original A = A * 10).
4FC5
LD E,A
LET Register E = Register A (which is the first digit * 10, forming the “tens place” number).
4FC6
GOSUB to 4FD2H to decode a number from ASCII. The C FLAG will be set if there was an error.
4FC9
If the NC FLAG (No Carry) is set then the digit provided was invalid, so JUMP to 4FD0H to set the error flag (via an OR A) and RETurn.
4FCB
ADD A,E
ADD the value held in Register E (the “tens place”) to Register A (Results held in Register A).
4FCC
LD E,A
LET Register E = Register A (which is the completed number).
4FCD
XOR A
Set Register A to ZERO and clear all Flags.
4FCE
LD A,E
LET Register A = Register E.
4FCF
RET
RETurn to the caller.

4FD0H – Routine JUMPed to from the prior decoding routine if there was an error. This will set the NZ FLAG to indicate an error and RETurn.

4FD0
OR A
Set FLAGS based on the contents of Register A, which will trigger a NZ FLAG set – meaning an error condition.
4FD1
RET
RETurn to the caller.

4FD2H – Decode a number from ASCII. The C FLAG will be set if there was an error.

4FD2
LD A,(HL)
Fetch the value stored at memory location pointed to by Register Pair HL and put it into Register A.
4FD3
INC HL
Bump Register Pair HL by 1 to point to the next memory location.
4FD4
SUB 30H
SUBtract the value 30H from Register A which adjusts the number to BINARY.
4FD6
RET C
If the C FLAG (No Carry) is set then the value was too low to be a number, so RETurn to the caller.
4FD7
CP 0AH
Compare the value held in Register A against 0AH to see if the value was too high to be a number. Results:
  • If Register A equals 0AH, the Z FLAG is set.
  • If A < 0AH, the CARRY FLAG will be set.
  • if A >= 0AH, the NO CARRY FLAG will be set.
4FD9
RET
RETurn to the caller.

4FDAH – Subroutine called from 4E69H to est to see if the disk drive (stored in Register C) is present on the system.

4FDA
GOSUB to 4566H.

NOTE: 4566H is the routine which resets the FDC.
4FDD
GOSUB to 4404H.

NOTE: 4404H is the routine which has a slight delay.
4FE0
LD A,0CH
LET Register A = 0CH (0000 1100).
When this bit pattern in sent to Port F0H (the Floppy Disk Control Port) and bits 7-4 are 0000, this is a RESTORE command and is a Type I command. Bits 0-1 are the stepping rate (00=6ms, 01=12ms, 10=20ms, 11=30ms), Bit 2 is the Track Number Verify Flag (0=No Verify), and Bit 3 is the Head Load Flag (1=Load Head).

When activated, the Track Register is checked, and if its 0 (meaning at Track 0) an interrupt is generated. If it is not 0, stepping pulses are issued until the TR00 goes to 0, and then an interrupt is generated.
4FE2
OUTput the value held in Register A to port F0H.

NOTE: Port F0H is the Floppy Disk Command/Status Register.
4FE4
LD BC,6000H
LET Register Pair BC = 6000H as a delay.
4FE7
GOSUB to 0060H to delay for BC iterations.
4FEA
LD BC,0000H
LET Register Pair BC = 0000H to set up for a loop of 65,535.

Start of a loop of 65,535 tries.

4FED
DEC BC
DECrement Register Pair BC by 1.
4FEE
4FEF
LD A,B
OR C
Since the Z-80 cannot test Register Pair BC against zero, the common trick is to set Register A to one, and OR the other. Only if every single bit was off in both will the Z FLAG be set.
4FF0
RET Z
If the Z FLAG (Zero) is set, then BC=0 and we have run out of retries so RETurn to the caller.
4FF1
INput a byte 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
4FF3
BIT 2,A
Test Bit 2 of Register A (=Track 0).
4FF5
RET NZ
If the NZ FLAG (Not Zero) is set then we have hit TRACK 0 and there is definitely a drive there, so RETurn to the caller.
4FF6
LOOP back to 4FEDH.

End of a loop of 65,535 tries.


4FF8H – Subroutine to convert a two-digit binary-coded-decimal number into ASCII text.

4FF8
RLD
The contents of the low-order nibble of (HL) are copied to the high-order nibble of (HL). The previous contents are copied to the low-order nibble of A. The previous contents are copied to the low-order nibble of (HL).
4FFA
GOSUB to 4FFFH to convert that RLD’d number to ASCII.
4FFD
RLD
The contents of the low-order nibble of (HL) are copied to the high-order nibble of (HL). The previous contents are copied to the low-order nibble of A. The previous contents are copied to the low-order nibble of (HL).
4FFF
AND 0FH
Since we are no interested in the high-order nibble, we need to MASK the value of Register A against 0FH (0000 1111). This has the effect of turning off bits 7, 6, 5, 4, leaving only bits 3, 2, 1, 0 active.
5001
If the NZ FLAG (Not Zero) is set, meaning that we have a non-zero leading digit, skip the next 2 instructions and JUMP to 5006H.
5003
BIT 0,D
Test Bit 0 of Register D to see if we still have leading zeroes.
5005
RET Z
If the Z FLAG (Zero) is set then we still have a leading zero, so RETurn to the caller.
5006
SET 0,D
SET (i.e., set as 1) BIT 0 of Register D to indicate that we previously had a leading zero.
5008
OR 30H
OR Register A against 30H to turn the nybble into ASCII (since 30H is “0”, 39H is “9”).
500A
LD (IX+00H),A
Store the value held in Register A (which is now ASCII text) into the memory location IX+00H.
500D
INC IX
Bump Register Pair IX by 1 to point to the next text location.
500F
RET
RETurn to the caller.

5010- RAM size text Storage Area.

5010
Storage
“64”
5012
Storage
“32”
5014
Storage
“16”

5016H – Message Storeage Area.

5016
Storage
“TRS-80 Model III TRSDOS v1.3 Wed Jul 1, 1981” + 0AH
504B
Storage
“00K System, Number of Drives = 0 Serial #: “
508A
Storage
“(c)(p) 1980 TANDY CORPORATION. All rights reserved.” + 0AH
50BF
Storage
“Unauthorized reproduction of this software is prohibited” + 0AH
50F8
Storage
“and is in violation of United States copyright laws.” + 0DH
512D
Storage
“Try Again ” + 03H
513B
Storage
“Enter Date (MM/DD/YY)? “+ 0EH + 03.
5154
Storage
“Enter Time (HH/MM/SS)? “+ 03.
516C
Storage
The Model III logo is stored here.