Model I TRSDOS v2.3 BACKUP/CMD Explained

Introduction

This is a disassembly of TRSDOS v2.3's BACKUP/CMD File.

There are 3 main parts to this routine. The first secures the SOURCE drive, DESTINATION drive, and BACKUP DATE. The second will FORMAT the disk if it is not otherwise determined to be a proper destination disk. The third will do the actual BACKUP to the destination disk.

In order to maximize the RAM available for a backup, the BACKUP routine is relocated to 4200H which overwrites DOS, so whether there is success or failure, a REBOOT is required.

SELF-MODIFYING CODE OPERANDS

AddressSizeName/PurposeValues/FormatSet ByUsed By
Relocated: 42ADH
Original: 5648H
1Saved FDC Track PositionTrack number (00H-22H) where read phase left off42ABH during read phase42ACH during write phase (operand of LD A,nn)
Relocated: 42B2H
Original: 564DH
1Starting Track NumberFirst track number in current buffer batch42B0H during read phase42B1H during write phase (operand of LD D,nn)
Relocated: 4390H
Original: 572BH
1Original GAT Byte 0First byte of source GAT (allocation bits for Track 0)438EH (saved before 76H marker)438FH during finish (operand of LD A,nn)
Relocated: 449DH
Original: 5838H
1Destination Drive Select Code01H, 02H, 04H, or 08HInitialization (52xxH)449CH (operand of LD A,nn)
Relocated: 44E8H
Original: 5883H
1Last Prompted Drive CodeDrive code with bit 6 = dest flag; 00H = force prompt44EBH, 4497H, 44E2H44E7H (operand of CP nn for smart prompt)
Relocated: 44EFH
Original: 588AH
1Drive Difference00H = single-drive, non-zero = two-drive backupInitialization (source - dest)44EEH (operand of LD A,nn), 44BEH, 4441H
Relocated: 459CH
Original: 5937H
2COPYING Message - Track DigitsASCII "00"-"34"42DEH via 4402H conversionMessage display at 457BH
Relocated: 45A7H
Original: 5942H
2COPYING Message - Sector DigitsASCII "00"-"09"42E5H via 4402H conversionMessage display at 457BH
Relocated: 45BBH
Original: 5956H
2VERIFYING Message - Track DigitsASCII "00"-"34"4316H via 4402H conversionMessage display at 457BH
Relocated: 45C6H
Original: 5961H
2VERIFYING Message - Sector DigitsASCII "00"-"09"431DH via 4402H conversionMessage display at 457BH
Relocated: 45DAH
Original: 5975H
2LOADING Message - Track DigitsASCII "00"-"34"4264H via 4402H conversionMessage display at 457BH
Relocated: 45E5H
Original: 5980H
2LOADING Message - Sector DigitsASCII "00"-"09"426BH via 4402H conversionMessage display at 457BH
Original: 5435H1Format Track CounterCurrent track being formatted (00H-22H)Format routineFormat routine (operand of LD C,nn)

PROGRAM VARIABLES

AddressSizeName/PurposeValues/FormatSet ByUsed By
Relocated: 4709H
Original: 5AA4H
1Directory Track NumberUsually 11H (17 decimal); copied from source GAT+02HInitialization from GATDirectory operations
Relocated: 4713H
Original: 5AAEH
1Tracks Per Buffer CountNumber of tracks that fit in RAM buffer (calculated at init)RAM size calculation4289H, 429AH (buffer full check)
Relocated: 4714H
Original: 5AAFH
1Tracks Loaded CounterCount of tracks currently in buffer; 00H=empty4286H (INC), 4339H (DEC)429AH (full check), 4346H (empty check)

BUFFER AREAS

AddressSizeName/PurposeValues/FormatSet ByUsed By
Relocated: 4800H-4822H
Original: 5B9BH-5BBDH
35FDC Status ArrayOne byte per track; stores FDC status after reading each track4283H after each track read42F4H to build write commands (bits 5-6 → record type)

Source Disk GAT Buffer (4900H-49FFH) - 256 bytes

AddressSizeName/PurposeValues/FormatSet ByUsed By
Relocated: 4900H-4922H
Original: 5C9BH-5CBDH
35Source Track Allocation Table2 bits per granule: 00=free, 01=1st used, 10=2nd used, 11=both/lockedRead from source Track 0, Sector 04247H (track copy decision)
Relocated: 4902H
Original: 5C9DH
1Source Directory Track NumberUsually 11H (17 decimal)Read from source GATCopied to 4709H
Relocated: 4960H-4982H
Original: 5CFBH-5D1DH
35Source Track Lockout TableBit=1 means granule is locked out (bad sector)Read from source GAT424EH (skip bad tracks)
Relocated: 49CEH-49D7H
Original: 5D69H-5D72H
10Source Pack IDDisk identification string (for multi-disk sets)Read from source GAT4481H-4487H (Pack ID comparison loop)

Destination Disk GAT Buffer (4A00H-4AFFH) - 256 bytes

AddressSizeName/PurposeValues/FormatSet ByUsed By
Relocated: 4A00H-4A22H
Original: 5D9BH-5DBDH
35Destination Track Allocation TableSame format as source; may differ due to bad sectorsRead from dest Track 0, Sector 0Track write decisions
Relocated: 4A60H-4A82H
Original: 5DFBH-5E1DH
35Destination Track Lockout TableBit=1 means granule locked out; OR'd with source lockoutRead from dest GAT; updated by 43FDH on format errors4253H (combined lockout check)
Relocated: 4ACEH-4AD7H
Original: 5E69H-5E72H
10Destination Pack IDMust match source for non-format backupRead from dest GATPack ID verification

General Purpose Sector Buffer (4B00H-4BFFH) - 256 bytes

AddressSizeName/PurposeValues/FormatSet ByUsed By
Relocated: 4B00H
Original: 5E9BH
1GAT Byte 0 / Backup Marker76H = backup in progress (also HALT opcode for safety)4218H (write marker), 4393H (restore original)44DEH (verify dest disk), boot safety
Relocated: 4B00H-4BFFH
Original: 5E9BH-5F9AH
256General Purpose Sector BufferUsed for GAT read/write, Pack ID verification readsVarious disk I/O callsGAT operations, verification

Multi-Track Sector Buffer (4D00H+) - Variable size

AddressSizeName/PurposeValues/FormatSet ByUsed By
Relocated: 4D00H+
Original: 609BH+
VariableMulti-Track Sector BufferHolds (tracks_per_buffer × 10 × 256) bytes of sector dataSector read loop (4275H)Sector write loop (42FAH)

MESSAGE STRINGS (Read-Only except for digit placeholders)

AddressSizeName/PurposeValues/FormatSet ByUsed By
Relocated: 458BH-45A9H
Original: 5926H-5944H
31"COPYING" Message1DH + "COPYING TRACK nn, SECTOR nn" + 03HConstant42E9H (display during write)
Relocated: 45AAH-45C7H
Original: 5945H-5962H
30"VERIFYING" Message"VERIFYING TRACK nn, SECTOR nn" + 03HConstant4321H (display during verify)
Relocated: 45C8H-45E7H
Original: 5963H-5982H
32"LOADING" Message1DH + "LOADING TRACK nn, SECTOR nn" + 03HConstant426FH (display during read)
Relocated: 45E8H-4600H
Original: 5983H-599BH
25"SOURCE DISK READ ERROR!"0AH + message + 0DHConstant4455H (error handler)
Relocated: 4601H-461EH
Original: 599CH-59B9H
30"DESTINATION DISK READ ERROR!"0AH + message + 0DHConstant44D5H (error handler)
Relocated: 461FH-463DH
Original: 59BAH-59D8H
31"DESTINATION DISK WRITE ERROR!"0AH + message + 0DHConstantWrite error handler
Relocated: 463EH-4663H
Original: 59D9H-59FEH
38"DESTINATION DISK IS WRITE PROTECTED!"0AH + message + 0DHConstant44AAH (write protect check)
Relocated: 4664H-4682H
Original: 59FFH-5A1DH
31"INSERT SOURCE DISK" Prompt1DH + "INSERT SOURCE DISK (ENTER)" + 1DH + 03HConstant442AH (source prompt)
Relocated: 4683H-46A6H
Original: 5A1EH-5A41H
36"INSERT DESTINATION DISK" Prompt1DH + "INSERT DESTINATION DISK (ENTER)" + 1DH + 03HConstant44A1H (dest prompt)
Relocated: 46A7H-46DDH
Original: 5A42H-5A78H
55"WAKE UP!!!" Error Message1DH + "WAKE UP!!! THAT'S NOT THE SAME SOURCE DISK!" + 0DHConstant448DH (wrong disk error)
Relocated: 46DEH-46EEH
Original: 5A79H-5A89H
17"BACKUP COMPLETE" MessageMessage + 0DHConstantFinish routine
Relocated: 46EFH-4708H
Original: 5A8AH-5AA3H
26"HIT 'ENTER' TO CONTINUE"Message + 03HConstant43A3H (error handler wait)

GAT Sector Structure (Track 0, Sector 0)

OffsetSizeContentsDescription
+00H35Track Allocation Table2 bits per granule × 5 granules/track × 35 tracks. Each byte covers one track's 5 granules.
+02H1Directory Track NumberUsually 11H (17). Location of directory/HIT.
+23H61(Reserved/Unused)Padding to offset 60H.
+60H35Track Lockout TableOne byte per track. Bit=1 means that granule is locked out (bad).
+83H75(Reserved/Unused)Padding to offset CEH.
+CEH10Pack IDDisk identification string. Compared to verify correct source disk.
+D8H40(Reserved/Date/etc.)May contain backup date, password hash, etc.

Disassembly

5200H - BACKUP/CMD Entry Point

5200
DI F3
Disable Interrupts.
5201
LD SP,41FCH 31FC41
Set the STACK POINTER to 41FCH.
5204
PUSH HL E5
Save the contents of Register Pair HL, which is the next character on the DOS command line, to the top of the stack.
5205
LD A,(4030H) 3A3040
Fetch the OPCODE at 4030H and store it into Register A. This would normally the the opcode LD A,n. 4030H is the is the address DOS jumps to at DOS READY to load the command interpreter (SYS1/SYS).
5208
CP C7H FEC7
Compare the value held in Register A against C7H. If Register A equals C7H the Z FLAG is set; otherwise the NZ FLAG is set.
520A
If the Z FLAG (Zero) has been set, so JUMP to the next routine at 521CH to continue with the BACKUP.
520C
LD A,(430FH) 3A0F43
Fetch the byte at 430FH and put it into register A. 430FH is used by TRSDOS 2.3 to track certain system conditions.
  • If Bit 4 is set, then SYS6/SYS is loaded in RAM (and can be called by RST 28H).
  • If Bit 5 is set, then CHAINING is in effect.
  • If Bit 7 is set, then DEBUG was set to active.
520F
BIT 5,A CB6F
Check to see if BACKUP was called as part of a BATCH FILE by testing Bit Number 5 of Register A. Z FLAG will be set if that bit is 0 (meaning, no batch file), and NZ FLAG will be set if that bit is 1 (meaning, batch file call).
5211
If the Z FLAG (Zero) has been set then this was not called from a batch file, so JUMP to the next routine at 521CH to continue with the BACKUP.
5213
LD HL,5F9BH 219B5F
Point Register Pair HL to the message 'SINGLE DRIVE BACKUP INVALID DURING PROGRAM CHAINING'.
5216
GOSUB to 4467H to send the message pointed to by HL to the video display. HL is preserved.
5219
JUMP to 4030H to ABORT.
NOTE: 4030H will load and execute the SYS1/SYS overlay with a Request Code of 2, so as to get the next command

521CH - Continuing if NOT from a BATCH File

521C
LD HL,5FD0H 21D05F
Point Register Pair HL to the message CLS + "TRSDOS DISK BACKUP UTILITY VER 2.3"
521F
GOSUB to 4467H.
NOTE: 4467H is a vector call to 44CFH which sends the message pointed to by HL to the video display. HL is preserved.
5222
POP HL E1
Restore the pointer to the next character on the DOS command line into HL.
5223
LD A,(4030H) 3A3040
Fetch the OPCODE at 4030H and store it into Register A. This would normally the the opcode LD A,n. 4030H is the is the address DOS jumps to at DOS READY to load the command interpreter (SYS1/SYS).
5226
CP C7H FEC7
Compare the value held in Register A against C7H. If Register A equals C7H the Z FLAG is set; otherwise the NZ FLAG is set.
5228
If the Z FLAG (Zero) has been set, continue with the BACKUP via a JUMP to 5232H to prompt the user for the SOURCE drive
522A
LD A,(HL) 7E
Fetch the next character from the DOS command line.
522B
CP 3AH FE3A
Compare the value held in Register A against :. If Register A equals :, the Z FLAG is set; otherwise the NZ FLAG is set.
522D
If the NZ FLAG (Not Zero) has been set then we know we need to prompt the user for the source disk, so JUMP to 5232H to do that.
522F
INC HL 23
If we are here then we had a DOS command line of BACKUP :. INCrement the value stored in Register Pair HL by 1 to point to the next character on the DOS command line.
5230
JUMP to 5240H to skip over the prompt to enter in the SOURCE disk number and go directly to the code that would process the character given in answer to that question.

5232H - Determine the SOURCE disk drive number

As a pass through, we had enough of a BACKUP command to move us to trying to get the SOURCE drive number, but we need to prompt for it.

5232
LD HL,5FF8H 21F85F
Point Register Pair HL to the message 'SOURCE DRIVE NUMBER ? '
5235
GOSUB to 4467H.
NOTE: 4467H is a vector call to 44CFH which sends the message pointed to by HL to the video display. HL is preserved.
5238
LD HL,4B00H 21004B
Let Register Pair HL equal 4B00H to act as the required buffer location for a ROM call of 0040H to secure input from the user.
523B
LD B,01H 0601
Let Register B equal 01H to act as the required number of characters to fetch from the user, which must be in Register B before a CALL to 0040H to secure input from the user.
523D
GOSUB to 0040H in ROM to wait for keyboard input of B characters and store the characters at (HL).

At this point, (HL) points to a single character which we need to validate as a drive number. Either this was a pass through, in which case (HL) was populated by the ROM cal to 0040H or as a jump from 522F, where (HL) is pointing to the character on the DOS line following a :.

5240
LD A,(HL) 7E
Fetch the character at (HL) and put it into Register A.
5241
SUB 30H D630
SUBtract the value 30H from Register A. This will convert an ASCII number '0'-'9' to its decimal equivalent. The Carry Flag will be affected.
5243
If the C FLAG (Carry) has been set, then we did not get a number, so LOOP BACK to 5232H to prompt the use for SOURCE DRIVE NUMBER.
5246
CP 04H FE04
Compare the value held in Register A against 04H, which is 1 higher than the highest possible drive number. If Register A equals 04H, the Z FLAG is set. If Register A < 04H, the CARRY FLAG will be set. If Register A >= 04H, the NO CARRY FLAG will be set. This means that a valid drive number would set the CARRY FLAG.
5248
If the NC FLAG (No Carry) has been set, meaning we do not have a valid drive number, LOOP BACK to 5232H to prompt the use for SOURCE DRIVE NUMBER.

We now have a valid SOURCE disk drive number held in Register A as a number 0, 1, 2, or 3.

524B
INC HL 23
INCrement the value stored in Register Pair HL by 1. This will either move to the next character in the user input buffer, or the next character on the DOS command line, whichever was being processed.

But a 0, 1, 2, or 3 isn't usable. We need to convert this to C7H, CFH, D7H, or DFH which is a FDC Command code.

524C
RLCA 07
Rotate the bits of Register left (i.e., lower bit moves higher) one bit position. The contents of bit 7 are copied to the CARRY FLAG and to bit 0.
524D
RLCA 07
Rotate the bits of Register left (i.e., lower bit moves higher) one bit position. The contents of bit 7 are copied to the CARRY FLAG and to bit 0.
524E
RLCA 07
Rotate the bits of Register left (i.e., lower bit moves higher) one bit position. The contents of bit 7 are copied to the CARRY FLAG and to bit 0.

These 3 RLCAS turn a "0" into an 00H, "1" into an 08H, a "2" into a 10H, and a "3" into 0CH.

524F
OR C7H F6C7
OR Register A against C7H (1100 0111). This has the effect of turning on bits 7, 6, 2, 1, 0.

"0" is now C7H, "1" is now CFH, "2" is now D7H, and "3" is now DFH, which are all Floppy Disk Controller Command bytes.

5251
LD (5256H),A 325652
Store the value held in Register A into memory location 5256H, which is part of an OPCODE. This is one of two locations in the program where the SOURCE DRIVE NUMBER is stored, and this one is saved in the format which can be sent to the FDC as a command.
5254
XOR A AF
Set Register A to ZERO and clear all Flags.

The instruction at 5251H has actually replaced the OPCODE at 5255 with the SOURCE disk drive number, converted into C7H, CFH, D7H, or DFH

5255
SET 0,A CBC7
While this is coded as SET 0,A, the n actually changes to 0-3 based on the users entry of the SOURCE drive number. This means A becomes 01H for a "0", 02H for a "1", 04H for a "2", and 08H for a "3".
5257
LD (57C6H),A 32C657
Store the value held in Register A (01, 02, 04, or 08 corresponding to a choice of drive "0", "1", "2", or "3") into memory location 57C6H which is part of a routine which sends that code to the disk drive select latch address and RETurns. . This is one of two locations where the SOURCE DRIVE NUMBER is saved, and this one is saved in the format which can be sent to the FDC latch port to select the disk drive.
525A
LD A,(4030H) 3A3040
Fetch the OPCODE at 4030H and store it into Register A. This would normally the the opcode LD A,n. 4030H is the is the address DOS jumps to at DOS READY to load the command interpreter (SYS1/SYS).
525D
CP C7H FEC7
Compare the value held in Register A against C7H. If Register A equals C7H the Z FLAG is set; otherwise the NZ FLAG is set.
525F
If the Z FLAG (Zero) has been set, JUMP to 526DH prompt the user for the DESTINATION disk drive.
5261
LD A,(HL) 7E
Fetch the value held in the memory location pointed to by Register Pair HL (which is either the DOS command line or the fetched input) and store it into Register A.
5262
INC HL 23
INCrement the value stored in Register Pair HL by 1 to point to the next character location we wish to interrogate.
5263
CP 0DH FE0D
Compare the value held in Register A against 0DH (ASCII: CARRIAGE RETURN). If there was a CARRIAGE RETURN then the Z FLAG will be set, otherwise the NZ FLAG will be set.
5265
If we found a CARRIAGE RETURN, JUMP to 526DH prompt the user for the DESTINATION disk drive.

If we didn't find a ENTER here, then we need to still process the command line. The only valid character at this point would be a : or a SPACE as we now have BACKUP :n :

5267
CP 3AH FE3A
Compare the value held in Register A against :. If Register A equals :, the Z FLAG is set; otherwise the NZ FLAG is set.
5269
If we did not find a :, the NZ FLAG (Not Zero) will be set, so LOOP BACK to 5261H to read the next character on the DOS command line.
526B
JUMP to 527BH to use THAT character as the current drive number (instead of prompting) and test to see if its valid.

526DH - Determine DESTINATION Drive Number

As a pass through, we had enough of a BACKUP command to move us to trying to get the DESTINATION drive number, but we need to prompt for it.

526D
LD HL,600FH 210F60
Let Register Pair HL equal 600FH to point to the message 'DESTINATION DRIVE NUMBER ? "+03H
5270
GOSUB to 4467H to display the message 'DESTINATION DRIVE NUMBER ? "+03H on screen. HL is preserved.
5273
LD HL,4B00H 21004B
Let Register Pair HL equal 4B00H to act as the required buffer location for a ROM call of 0040H to secure input from the user.
5276
LD B,01H 0601
Let Register B equal 01H, meaning that we need the ROM routine at 0040H to fetch ONE character from the user.
5278
GOSUB to 0040H in ROM to wait for keyboard input of B characters and store the characters at (HL).

At this point (HL) is pointing to a potential drive number. Either because the user entered it on the command line and, as a result, 526BH jumped here with (HL) pointing to the DOS command line -or- because the user typed it in, in which case (HL) points to the character just typed in. Either way, the code rejoins here to test it.

527B
LD A,(HL) 7E
Fetch the value held in the memory location pointed to by Register Pair HL and store it into Register A.
527C
SUB 30H D630
SUBtract the value 30H from Register A. This will convert an ASCII number '0'-'9' to its decimal equivalent. The Carry Flag will be affected.
527E
If the C FLAG (Carry) has been set, then we did not get a number, so LOOP BACK to 526DH to prompt the use for DESTINATION DRIVE NUMBER.
5281
CP 04H FE04
Compare the value held in Register A against 04H, which is 1 higher than the highest possible drive number. If Register A equals 04H, the Z FLAG is set. If Register A < 04H, the CARRY FLAG will be set. If Register A >= 04H, the NO CARRY FLAG will be set. This means that a valid drive number would set the CARRY FLAG.
5283
If the NC FLAG (No Carry) has been set, meaning we do not have a valid drive number, LOOP BACK to 526DH to prompt the use for DESTINATION DRIVE NUMBER.

But a 0, 1, 2, or 3 isn't usable. We need to convert this to C7H, CFH, D7H, or DFH.

5286
RLCA 07
Rotate the bits of Register left (i.e., lower bit moves higher) one bit position. The contents of bit 7 are copied to the CARRY FLAG and to bit 0.
5287
RLCA 07
Rotate the bits of Register left (i.e., lower bit moves higher) one bit position. The contents of bit 7 are copied to the CARRY FLAG and to bit 0.
5288
RLCA 07
Rotate the bits of Register left (i.e., lower bit moves higher) one bit position. The contents of bit 7 are copied to the CARRY FLAG and to bit 0.

These 3 RLCAs turn a "0" into an 00H, "1" into an 08H, a "2" into a 10H, and a "3" into 0CH.

5289
OR nnH F6C7
OR Register A against nnH, which is the DESTINATION DRIVE but converted into Floppy Disk Controller Command bytes of either C7H, CFH, D7H, or DFH, corresponding to the drives 0-3 as selected by the user.

"0" is now C7H, "1" is now CFH, "2" is now D7H, and "3" is now DFH.

528B
LD (5290H),A 329052
Store the value held in Register A into memory location 5290H, which is part of an OPCODE. This is one of two locations in the program where the DESTINATION DRIVE NUMBER is stored, and this one is saved in the format which can be sent to the FDC as a command.
528E
XOR A AF
Set Register A to ZERO and clear all Flags.

The instruction at 528BH has actually replaced the OPCODE at 5290 with the DESTINATION disk drive number, converted into C7H, CFH, D7H, or DFH

528F
SET 0,A CBC7
SET (i.e., set as 1) BIT 0 of Register A.
5291
LD (5838H),A 323858
Store the value held in Register A (01, 02, 04, or 08 corresponding to a choice of drive "0", "1", "2", or "3") into memory location 57C6H which is part of a routine which sends that code to the disk drive select latch address and RETurns. This is one of two locations where the DESGINATION DRIVE NUMBER is saved, and this one is saved in the format which can be sent to the FDC latch port to select the disk drive.
5294
LD A,(4030H) 3A3040
Fetch the OPCODE at 4030H and store it into Register A. This would normally the the opcode LD A,n. 4030H is the is the address DOS jumps to at DOS READY to load the command interpreter (SYS1/SYS).
5297
CP C7H FEC7
Compare the value held in Register A against C7H. If Register A equals C7H the Z FLAG is set; otherwise the NZ FLAG is set.
5299
If the Z FLAG (Zero) has been set, JUMP to 52A9H to prompt the user for th BACKUP DATE
529B
LD A,(4046H) 3A4640
Let Register Pair A equal the contents of RAM location 4046H (Decimal: 16454) which is the storage location for the current month
529E
OR A B7
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A (and, in all events, clear the CARRY FLAG).
529F
If that RAM location held a 0, then JUMP to the next routine at 52A9H to prompt the user for th BACKUP DATE.

If we are here, then we are going to use the SYSTEM DATE held in 5AA5H as the backup date. This is put into HL similar to the above routines; either HL will be this pointer or it will be 8 fetched characters from the user. Either way, HL needs to be validated as a valid 8 character date.

52A1
LD HL,5AA5H 21A55A
Let Register Pair HL equal 5AA5H, which is the 8 character buffer set up to accept a date from the user.
52A4
GOSUB to 4470H which is the DISPLAY DATE ON VIDEO Vector.
52A7
JUMP to 52CAH to continue with the BACKUP routine after successfully processing an accepted date.

52A9H - Determine the BACKUP DATE

52A9
LD HL,602BH 212B60
Let Register Pair HL equal 602BH so as to point to the message 'BACKUP DATE (MM/DD/YY) ' + 03H
52AC
GOSUB to 4467H to display the message 'BACKUP DATE (MM/DD/YY) ' + 03H to the video display. HL is preserved.
52AF
LD HL,5AA5H 21A55A
Let Register Pair HL equal 5AA5H as the buffer for a the KEYBOARD INPUT routine in ROM at 0040H.
52B2
LD B,08H 0608
Let Register B equal 08H, to indicate that the ROM routine at 0040H is fetching 8 characters from the user.
52B4
GOSUB to 0040H.
NOTE: 0040H is the Model I ROM routine to wait for keyboard input of B characters and store the characters at (HL).
52B7
LD A,B 78
Copy the contents of Register B, which is the number of characters actually fetched from the user, into Register A.
52B8
CP 08H FE08
Compare the value held in Register A against 08H, which is the number of characters we need this date field to be. If B is not 8, then we have a problem, and the NZ FLAG is set.
52BA
If we didn't get 8 characters for the date, LOOP BACK to 52A9H to prompt for the BACKUP DATE.

If we are here, then we have 8 characters starting at (HL) which need to be vetted. We hope it will be a valid date as the BACKUP DATE.

52BC
LD A,(5AA7H) 3AA75A
Fetch the value held in memory location 5AA7H (which would be the third character in MM/DD/YY) and store it into Register A.
52BF
CP 2FH FE2F
Compare the value held in Register A against /). If it isn't a /, the NZ FLAG will be set.
52C1
If the NZ FLAG (Not Zero) has been set, we didn't get a SLASH where we needed it, so LOOP BACK to 52A9H to prompt for the BACKUP DATE.
52C3
LD A,(5AAAH) 3AAA5A
Fetch the value held in memory location 5AAAH (which would be the FIFTH character in MM/DD/YY) and store it into Register A.
52C6
CP 2FH FE2F
Compare the value held in Register A against /). If it isn't a /, the NZ FLAG will be set.
52C8
If the NZ FLAG (Not Zero) has been set, we didn't get a SLASH where we needed it, so LOOP BACK to 52A9H to prompt for the BACKUP DATE.

At this point, we know the 8 characters are at least __/__/__, which is all the BACKUP routine actually tests for. HI/TH/ER is a valid answer! Now we are going to relocate part of the BACKUP/CMD program to 4200H. This will clobber DOS, which is why the system reboots after a BACKUP.

52CA
LD HL,559BH 219B55
Let Register Pair HL equal 559BH, which is part of this BACKUP/CMD routine.
52CD
LD DE,4200H 110042
Let Register Pair DE equal 4200H which will be a buffer to hold this routine.
52D0
LD BC,0B00H 01000B
Let Register Pair BC equal 0B00H to indicate that we are going to move 2816 bytes.
52D3
LDIR EDB0
Transfers 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.
52D5
XOR A AF
Set Register A to zero and clear all flags. This zero will be used to initialize the "last prompted drive" indicator.

Things get a little rough from this point. The same code is now in 2 RAM locations. Relocated code below bears both addresses.
For a little clarity:

  • Source drive select code is at 47C6H and 442BH
  • Destination drive select code is at 449DH and 5838H
  • The CP instruction with these codes is at 44E7H and 5582H
  • The LD instruction with these codes is at 55EEH and 5589H

52D6
Store zero into 44E8H. This is the "nn" operand of the CP nn instruction at 44E7H. Initializing it to zero means: "no drive has been prompted for yet." When the 44E7H routine runs, it compares the incoming drive code against this value. If they match, it knows that drive's disk is already inserted and skips the prompt.
52D9
Load the SOURCE drive select code (01H, 02H, 04H, or 08H) into Register A. This value was placed at 57C6H earlier (at 5257H) and now also exists at 442BH after relocation.
52DC
Point HL to the location holding the DESTINATION drive select code. This value was placed at 5838H earlier (at 5291H) and now also exists at 449DH after relocation.
52DF
SUB (HL) 96
Subtract DESTINATION drive code from SOURCE drive code. Result in A = (SOURCE - DESTINATION). If source and destination are the same drive, result = 0. If they are different drives, result ≠ 0.
52E0
Store the drive difference into 44EFH. This is the "nn" operand of LD A,nn at 44EEH. The 44E7H-44EEH routine uses this to determine if this is a single-drive backup (value=0, requiring disk swaps) or a two-drive backup (value≠0, no swaps needed between source/dest).
52E3
Call the "Insert Source Disk" prompt routine. This routine: (1) loads the SOURCE drive code from 442BH, (2) ORs it with 80H to set the high bit (identifying it as source), (3) displays "INSERT SOURCE DISK (ENTER)", (4) waits for ENTER, and (5) returns.
52E6
Call the FDC RESTORE routine. This sends a RESTORE command to the Floppy Disk Controller to move the read/write head to Track 0, then polls the FDC status until the drive is no longer BUSY. This initializes the drive head position.
52E9
LD A,(37ECH) 3AEC37
Fetch the FDC's status (which is mirrored to 37ECH) and store it into Register A.
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
52EC
LD HL,6045H 214560
Let Register Pair HL equal 6045H to point to the message 'SOURCE DRIVE NOT READY!' + 0DH.
52EF
BIT 7,A CB7F
Test Bit 7 of the FDC status (the "Not Ready" flag). Z flag set = drive IS ready (bit=0). NZ flag set = drive is NOT ready (bit=1).
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
52F1
If the NZ FLAG (Not Zero) has been set, then the drive is returning NOT READY, so JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
52F4
LD HL,605DH 215D60
Let Register Pair HL equal 605DH to point to the message 'SOURCE DRIVE NOT IN SYSTEM!' + 0DH.
52F7
BIT 2,A CB57
Test Bit Number 2 of Register A, which will test to see if the Read/Write head is at Track 0 or not. Z FLAG will be set if that bit is 0 (not positioned at track 0), and NZ FLAG will be set if that bit is 1 (positioned at track 0).
52F9
If the Z FLAG (Zero) has been set, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.

Read the source disks GAT (Track 0, Sector 0) into a buffer at 4B00H

52FC
LD DE,0000H 110000
Let Register Pair DE equal 0000H.
52FF
LD BC,4B00H 01004B
Let Register Pair HL equal 4B00H to act as the required buffer location.
5302
GOSUB to 453CH (Disk I/O Executive) which performs the drive selection, seeks to the track specified by DE, issues the Read command, and runs the data transfer loop to fill the buffer pointed to by BC.
5305
LD HL,45E8H 21E845
Let Register Pair HL equal 45E8H to point to the message 0A + 'SOURCE DISK READ ERROR!' + 0DH.
5308
If the NZ FLAG (Not Zero) has been set, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.

Extract the directory track number from the +02H offset (i.e., 4B02H) and store it at 4709H

530B
LD A,(4B02H) 3A024B
Fetch the value held in memory location 4B02H, which is the GAT +02H entry, or the DIRECTORY TRACK, and store it into Register A.
530E
LD (4709H),A 320947
Store the directory track into memory location 4709H.

Read the SOURCE disks's directory track, Sector 0 (the GAT of the directory track) into a buffer at 4900H.

5311
LD A,(4709H) 3A0947
Fetch the value held in memory location 4709H (which will be the GAT in the directory) and store it into Register A.
5314
LD D,A 57
Copy the contents of Register A into Register D.
5315
LD E,00H 1E00
Let Register E equal 00H, so now DE will be a 16 bit number.
5317
LD BC,4900H 010049
Let Register Pair BC equal 4900H which will be the buffer where the GAT is stored.
531A
GOSUB to 453CH (Disk I/O Executive) which performs the drive selection, seeks to the track specified by DE, issues the Read command, and runs the data transfer loop to fill the buffer pointed to by BC.
531D
LD HL,45E8H 21E845
Let Register Pair HL equal 45E8H to point to the message 0A + 'SOURCE DISK READ ERROR!' + 0DH.
5320
If the NZ FLAG (Not Zero) has been set, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
5323
GOSUB to 449CH to turn on Bit 6 in A, prompt for the Destination Disk and make sure its not write protected, and RETurn
5326
GOSUB to 441AH to send a RESTORE/LOAD HEAD to the Floppy Drive and then keep checking the floppy drive controller status until the drive is not BUSY.
5329
LD A,(37ECH) 3AEC37
Fetch the FDC's status (which is mirrored to 37ECH) and store it into Register A.
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
532C
LD HL,6079H 217960
Let Register Pair HL equal 6079H to point to the message 'DESTINATION DRIVE NOT READY!' + 0DH.
532F
BIT 7,A CB7F
Test Bit Number 7 of Register A, which is currently holding the Floppy Drive Controller status. This then tests a drive status of NOT READY. Z FLAG will be set if that bit is 0 (meaning the drive is ready), and NZ FLAG will be set if that bit is 1 (meaning the drive is NOT ready).
5331
If the NZ FLAG (Not Zero) has been set, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
5334
LD HL,6096H 219660
Let Register Pair HL equal 6096H to point to the message 'DESTINATION DRIVE NOT IN SYSTEM!' + 0DH.
5337
BIT 2,A CB57
Test Bit Number 2 of Register A, which will test to see if the Read/Write head is at Track 0 or not. Z FLAG will be set if that bit is 0 (not positioned at track 0), and NZ FLAG will be set if that bit is 1 (positioned at track 0).
5339
If the Z FLAG (Zero) has been set, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
533C
LD HL,463EH 213E46
Let Register Pair HL equal 463EH to point to the message 0A + 'DESTINATION DISK IS WRITE PROTECTED!' + 0DH.
533F
BIT 6,A CB77
Test Bit Number 6 of Register A to see if the FDC returned a PROTECTED error code. Z FLAG will be set if that bit is 0 (=NOT write protected), and NZ FLAG will be set if that bit is 1 (=Write protected).
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
5341
If the NZ FLAG (Not Zero) has been set, then the disk is not Write Protected, so continue on via a JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.

At this point, we have a disk and it is not write protected, read the DESTINATION disks' GAT (Track 0, Sector 0) into a buffer at 4B00H.

5344
LD BC,4B00H 01004B
Let Register Pair HL equal 4B00H to act as the required buffer location.
5347
LD DE,0000H 110000
Let Register Pair DE equal 0000H.
534A
GOSUB to 453CH (Disk I/O Executive) which performs the drive selection, seeks to the track specified by DE, issues the Read command, and runs the data transfer loop to fill the buffer pointed to by BC.

This is where the FORMAT vs BACKUP decision is made. If the read was good, and exited with Z, it is going to backup. If that read routine failed and returned a NZ, then the FORMAT routines are called.

534D
If the NZ FLAG (Not Zero) has been set, JUMP to 5386H (part of the FORMATTING routine) to fill a buffer at 4A00H with 35 (representing then number of tracks) FCH's and then 203 FFH's.

If we are here, then we are NOT going to format, and instead we are going to proceed to the next step, which is to read the directory tracks, check the Pack ID's to make sure they match, and if they do match, jump to 5553H to copy.

5350
LD A,(4709H) 3A0947
Load the directory track number (typically 11H/17 decimal for TRSDOS 2.3) into Register A. This value was extracted earlier from the source disk's GAT at offset +02H.
5353
LD D,A 57
Copy the directory track number into Register D. D now holds the track number for the disk read.
5354
LD E,00H 1E00
Set Register E to 00H. E holds the sector number (Sector 0). DE together = Track/Sector pair for the directory track's first sector (the GAT sector of the directory).
5356
LD BC,4A00H 01004A
Set BC to 4A00H as the destination buffer for the sector read. This will hold the DESTINATION disk's directory GAT sector.
5359
GCall the Disk I/O Executive to read the destination disk's directory track, sector 0 into buffer at 4A00H. This reads the GAT/lockout table from the destination disk's directory track.
535C
LD HL,4601H 210146
Let Register Pair HL equal 4601H to point to the message DEFM 0A + 'DESTINATION DISK READ ERROR!' + 0DH.
535F
If the NZ FLAG (Not Zero) has been set, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
5362
LD HL,49CEH 21CE49
Point HL to offset +CEH (206 decimal) within the SOURCE directory GAT buffer (4900H). This is the location of the Pack ID/Disk Name in the GAT sector.
5365
LD DE,4ACEH 11CE4A
Point DE to offset +CEH (206 decimal) within the DESTINATION directory GAT buffer (4A00H). Same offset - the Pack ID location.
5368
LD B,0AH 060A
Set loop counter B to 10 (0AH). The Pack ID is 10 bytes long.

Top of a loop to check the diskette pack ID's, which must match. This routine will check the byte in DE against the byte in HL, and if they do not match, error out; otherwise continues to the next byte pair, for a total of 10 bytes.

536A
LD A,(DE) 1A
Fetch one byte of the DESTINATION disk's Pack ID into A.
536B
CP (HL) BE
Compare it against the corresponding byte of the SOURCE disk's Pack ID.
536C
If bytes don't match (NZ), jump to 5380H to display "BACKUP REJECTED, DISKETTES CONTAIN DIFFERENT PACK IDS!" and halt.
536E
INC DE 13
Move to next byte in destination Pack ID.
536F
INC HL 23
Move to next byte in source Pack ID.
5370
LOOP back to 536AH, reducing Register B each time, and continue to LOOP until Register B has been reduced to ZERO, in which case, continue with the next instruction.

At this point, we have identical Pack ID's so we can continue. Next we move 35 bytes from 4A60H to 4A00H

5372
LD HL,4A60H 21604A
Point HL to offset +60H within the destination directory GAT buffer (the track lockout table portion).
5375
LD DE,4A00H 11004A
Point DE to the start of the buffer at 4A00H.
5378
LD BC,0023H 012300
Set byte count to 35 (23H) - one byte per track on a 35-track disk.
537B
LDIR EDB0
Block copy 35 bytes from 4A60H to 4A00H. This copies the destination disk's track lockout table to the beginning of the buffer. This lockout info will be used during copying to know which tracks to skip.
537D
Jump to the COPYING routine at 5553H to begin the actual sector-by-sector copy process.

5380H - Process the situation where the disks contain different pack IDs

5380
LD HL,612DH 212D61
Let Register Pair HL equal 612DH to point to the message 'BACKUP REJECTED, DISKETTES CONTAIN DIFFERENT PACK IDS!' + 0DH.
5383
JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.

5386H - Part of the FORMATTING routine. This will fill a buffer at 4A00H with 35 (representing the track lockout table) FCH's and then 203 FFH's.

5386
LD HL,4A00H 21004A
Let Register Pair HL equal 4A00H to be used as a buffer to hold the track lockout status.
5389
XOR A AF
Set Register A to ZERO and clear all Flags.
538A
LD (HL),FCH 36FC
Store the value FCH into the memory location pointed to by Register Pair HL.
538C
INC HL 23
INCrement the value stored in Register Pair HL by 1.
538D
INC A 3C
INCrement the value stored in Register A by 1.
538E
CP 23H FE23
Compare the value held in Register A against 23H (Decimal: 35), which is the highest number of tracks + 1. If A is not 23H then the NZ FLAG will be set.
5390
If the NZ FLAG (Not Zero) has been set, then we have not yet hit the highest number of tracks + 1, so LOOP BACK to 538AH to keep filling with FC's.
5392
CP CBH FECB
Compare the value held in Register A against CBH (Decimal: 203). When A hits CBH the Z FLAG will be set
5394
If the Z FLAG (Zero) has been set, JUMP to 539CH to continue with the FORMATTING. This is the routine's exit.
5396
LD (HL),FFH 36FF
Store the a FFH into the memory location pointed to by Register Pair HL.
5398
INC HL 23
INCrement the value stored in Register Pair HL by 1.
5399
INC A 3C
INCrement the value stored in Register A by 1.
539A
LOOP BACK to 5392H to keep filling with FF's

539CH - Drive Ready and Disk Verification Checks

539C
LD BC,00CDH 01CD00
Let Register Pair BC equal 00CDH (Decimal: 205).
539F
LD HL,60B7H 21B760
Let Register Pair HL equal 60B7H to point to the message 'NO DISKETTE IN DESTINATION DRIVE!' + 0DH.

Set up for the next disk-in-drive-and-ready test. This will count down from 204 to 0 to test for a DISKETTE IN DRIVE.

53A2
DEC BC 0B
DECrement the value stored in Register Pair BC by 1.
53A3H
53A3
LD A,B
OR C78
Since the Z-80 cannot test Register Pair BC against zero, the common trick is to set Register A to equal to Register B, and then OR A against Register C. Only if both Register B and Register C were zero can the Z FLAG be set.
53A5
If the Z FLAG (Zero) has been set then we have unsuccessfully timed out, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
53A8
LD A,(37ECH) 3AEC37
Fetch the FDC's status (which is mirrored to 37ECH) and store it into Register A.
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
53AB
AND 02H E602
MASK the value of Register A against 02H (0000 0010). This has the effect of turning off bits 7, 6, 5, 4, 3, 2, 0, leaving only bit 1 active. Bit 1 is the INDEX FOUND response.
53AD
If the NZ FLAG (Not Zero) has been set, meaning it found the index hole, LOOP BACK to to 53A2H to DECrement the counter in BC and try again.

Set up for the next disk-in-drive-and-ready test. This will count down from 6557 to 0 to test for DOOR OPEN.

53AF
LD BC,199DH 019D19
Let Register Pair BC equal 199DH (Decimal: 6557) which will be the counter for the number if passes.
53B2
LD HL,60D9H 21D960
Let Register Pair HL equal 60D9H, to point to the message 'DOOR NOT CLOSED ON DESSTINATION DRIVE!' + 0DH.
53B5
DEC BC 0B
DECrement the counter stored in Register Pair BC by 1.
53B6H
53B6
LD A,B
OR C78
Since the Z-80 cannot test Register Pair BC against zero, the common trick is to set Register A to equal to Register B, and then OR A against Register C. Only if both Register B and Register C were zero can the Z FLAG be set.
53B8
If the Z FLAG (Zero) has been set then we ran out tries, so JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
53BB
LD A,(37ECH) 3AEC37
Fetch the FDC's status (which is mirrored to 37ECH) and store it into Register A.
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
53BE
AND 02H E602
MASK the value of Register A against 02H (0000 0010). This has the effect of turning off bits 7, 6, 5, 4, 3, 2, 0, leaving only bit 1 active. Bit 1 is the INDEX FOUND response.
53C0
If the Z FLAG (Zero) has been set, meaning it DID NOT FIND the INDEX, JUMP to 53DFH to decrement the BC counter and try again.

Set up for the next disk-in-drive-and-ready test. This will count down from 186 to 0 to test for a DISKETTE IN DRIVE.

53C2
LD DE,0000H 110000
Let Register Pair DE equal 0000H. DE will be a counter up
53C5
LD BC,00BAH 01BA00
Let Register Pair BC equal 00BAH (Decimal: 186). BC will be a try counter down..
53C8
LD HL,60B7H 21B760
Let Register Pair HL equal 60B7H to point to the message 'NO DISKETTE IN DESTINATION DRIVE!' + 0DH.
53CB
DEC BC 0B
DECrement the value stored in Register Pair BC by 1.
53CCH
53CC
LD A,B
OR C78
Since the Z-80 cannot test Register Pair BC against zero, the common trick is to set Register A to equal to Register B, and then OR A against Register C. Only if both Register B and Register C were zero can the Z FLAG be set.
53CE
If the Z FLAG (Zero) has been set then we have unsuccessfully timed out, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
53D1
INC DE 13
INCrement the value stored in Register Pair DE by 1.
53D2
LD A,(37ECH) 3AEC37
Fetch the FDC's status (which is mirrored to 37ECH) and store it into Register A.
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
53D5
AND 02H E602
MASK the value of Register A against 02H (0000 0010). This has the effect of turning off bits 7, 6, 5, 4, 3, 2, 0, leaving only bit 1 active. Bit 1 is the INDEX FOUND response.
53D7
If the NZ FLAG (Not Zero) has been set, meaning it found the index hole, LOOP BACK to to 53CBH to keep polling.

Set up for the next disk-in-drive-and-ready test. This will count down from 5920 to 0 to test for DOOR OPEN.

53D9
LD BC,1720H 012017
Let Register Pair BC equal 1720H / (Decimal: 5920).
53DC
LD HL,60D9H 21D960
Let Register Pair HL equal 60D9H to point to the message 'DOOR NOT CLOSED ON DESTINATION DRIVE!' + 0DH.
53DF
DEC BC 0B
DECrement the try counter stored in Register Pair BC by 1.
53E0H
53E0
LD A,B
OR C78
Since the Z-80 cannot test Register Pair BC against zero, the common trick is to set Register A to equal to Register B, and then OR A against Register C. Only if both Register B and Register C were zero can the Z FLAG be set.
53E2
If the Z FLAG (Zero) then we ran out tries, so JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
53E5
INC DE 13
INCrement the counter stored in Register Pair DE by 1.
53E6
LD A,(37ECH) 3AEC37
Fetch the FDC's status (which is mirrored to 37ECH) and store it into Register A.
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
53E9
AND 02H E602
MASK the value of Register A against 02H (0000 0010). This has the effect of turning off bits 7, 6, 5, 4, 3, 2, 0, leaving only bit 1 active. Bit 1 is the INDEX FOUND response.
53EB
If the Z FLAG (Zero) has been set, meaning no INDEX MARKER FOUND, LOOP BACK to 53DFH to decrement the BC counter and try again.
53ED
LD HL,EACBH 21CBEA
Let Register Pair HL equal to EACBH as the start of an offset.
53F0
ADD HL,DE 19
LET Register Pair HL = Register Pair HL + Register DE. The Carry Flag will be affected.
53F1
LD HL,60FFH 21FF60
Let Register Pair HL equal 60FFH to point to the message 'DESTINATION DISKETTE UNUSABLE! HARD SECTORED!' + 0DH.
53F4
If EACBH (held in HL) + DE does NOT trigger a CARRY, then NC FLAG (No Carry) will have been set, so JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
53F7
XOR A AF
>Set Register A to ZERO and clear all Flags. A will be holding the current track being formatted.
53F8
LD (5435H),A 323554
Store the value held in Register A, which will be the track counter, into memory location 5435H, which is a LD C,nn opcode.

Main Formatting Track Loop

RAMContentsExplanation
5435HCurrent track number (0-34)Track counter for the format loop. Also the "nn" operand of LD C,nn at 5434H
4709HDirectory track number (typically 11H/17 decimal)Read from source disk's GAT at offset +02H earlier in the program
545FHThe "nn" operand of LD C,nn at 545EHHolds the Data Address Mark (FAH or FBH) to be written into each sector
6176HThe "nn" digits in "FORMATTING TRACK nn" messageTwo ASCII bytes for track number display
6293H-629DHSector interleave table: 00,05,01,06,02,07,03,08,04,09,FFDefines the order sectors are written (2:1 interleave pattern), terminated by FFH.
6293H: 00 (Sector 0)
6294H: 05 (Sector 5)
6295H: 01 (Sector 1)
6296H: 06 (Sector 6)
6297H: 02 (Sector 2)
6298H: 07 (Sector 7)
6299H: 03 (Sector 3)
629AH: 08 (Sector 8)
629BH: 04 (Sector 4)
629CH: 09 (Sector 9)
629DH: FF (Terminator)
6300HStart of format pattern bufferWhere the track format data is built before being written to disk

53FB
LD A,(5435H) 3A3554
Fetch the value held in memory location 5435H and store it into Register A as the current track being formatted.
53FE
GOSUB to 4402H to Set Register C to the ASCII Number corresponding to the TENS digit of Register A, and Register B to the ONES digit of Register A.
5401
LD (6176H),BC ED437661
Store the value held in Register Pair BC into memory location 6176H which is the nn in the message 1DH + "FORMATTING TRACK nn' + 03H.
5405
LD HL,6164H 216461
Let Register Pair HL equal 6164H to point to the message 1DH + "FORMATTING TRACK 00' + 03H
5408
GOSUB to 457BH to display the message pointed to by HL on the screen, and RETurn. HL is not affected..
540B
LD A,(5435H) 3A3554
Fetch the value held in memory location 5435H (the current track being formatted) and store it into Register A. 5435H is the nn in an opcode LD C,nn.
540E
LD B,A 47
Copy the contents of Register A (the current track being formatted) into Register B.
540F
LD A,(4709H) 3A0947
Fetch the directory track number (typically 11H/17 decimal) from 4709H into A. This value was read from the source disk's GAT sector (offset +02H) earlier at 530B-530EH.
5412
CP B B8
Compare directory track vs. current track. If they are equal (we're formatting the directory track), the Z flag is set.
5413
LD B,FAH 06FA
Preload B with FAH (the Data Address Mark for directory sectors).
5415
If Z flag set (current track IS the directory track), skip the next instruction and keep FAH as the Data Address Mark.
5417
LD B,FBH 06FB
If Z flag NOT set (current track is NOT the directory track), change B to FBH (the Data Address Mark for normal data sectors).
5419
LD A,B 78
Copy the selected Data Address Mark (FAH or FBH) from B into A.
541A
LD (545FH),A 325F54
Store the DAM into 545FH, which is the operand byte of the LD C,nn instruction at 545EH. This self-modifying code ensures each sector's data field is written with the correct address mark.

This code sets up the FORMAT pattern in RAM. 6300H-6293H = 109 in decimal. The format track is modified many times.

541D
LD HL,6300H 210063
Point HL to 6300H, the start of the format pattern buffer. This is where we'll build the raw bytes to be written to the track.
5420
LD DE,6293H 119362
Point DE to the sector interleave table at 6293H. DE will walk through this table to get sector numbers in interleaved order.
5423
LD B,0EH 060E
Set B = 14 (0EH). This is the count for writing the initial GAP bytes.
5425
Call routine to write 14 FFH bytes (GAP 0) to the format buffer at (HL). HL advances past the written bytes.

This is the top of the sector loop.

5428
LD B,06H 0606
Set B = 6 for writing 6 bytes.
542A
LD C,00H 0E00
Set C = 00H (sync byte value).
542C
GOSUB to 4411H to write 6 x 00H bytes (sync field before ID Address Mark) to format buffer.
542F
LD C,FEH 0EFE
Set C = FEH (the ID Address Mark that precedes the sector ID field)..
5431
GOSUB to 4417H to write 1 x FEH byte (ID Address Mark) to format buffer.
5434
LD C,00H 0E00
Set C = 00H (track number placeholder - but note: 5435H gets modified!).
5436
GOSUB to 4417H to write the track number byte to format buffer. (The actual track number is at 5435H which is this instruction's operand).
5439
LD C,00H 0E00
Set C = 00H (side number - always 0 for single-sided).
543B
GOSUB to 4417H to write side number byte (00H) to format buffer.
543E
LD A,(DE) 1A
Fetch the current sector number from the interleave table pointed to by DE.
543F
LD C,A 4F
Copy sector number into C so it can be written via the next routine.
5440
GOSUB to 4417H to write the sector number byte to format buffer.
5443
LD C,01H 0E01
Set C = 01H (sector size code: 01 = 256 bytes).
5445
GOSUB to 4417H to write the sector size byte to format buffer.
5448
LD C,F7H 0EF7
Set C = F7H (CRC generator command - tells FDC to write CRC bytes here).
544A
GOSUB to 4417H to write CRC command byte to format buffer. Note, the FDC will insert the actual CRC here.
544D
LD C,FFH 0EFF
Let Register C equal FFH which is the gap after ID field.
544F
GOSUB to 4417H to write 1 x FFH byte (gap after ID field) to format buffer.
5452
LD B,0BH 060B
Let Register B equal 0BH, which is the gap between ID and data fields.
5454
GOSUB to 440FH to write 11 x FFH bytes (GAP 2 - gap between ID and data fields) to format buffer.
5457
LD B,06H 0606
Let Register B equal 06H.
5459
LD C,00H 0E00
Let Register C equal 00H.
545B
GOSUB to 4411H to write 6 x 00H bytes (sync field before Data Address Mark) to format buffer.
545E
LD C,nnH 0E00
Load C with the Data Address Mark (FAH or FBH). The "nn" at 545FH was set at 541AH.
5460
GOSUB to 4417H to write the Data Address Mark (FAH for directory track, FBH otherwise) to format buffer.
5463
LD B,00H 0600
Let Register B equal 00H to set up a loop of 256 times.
5465
LD C,E5H 0EE5
Let Register C equal E5H, which is the standard "empty sector" fill byte.
5467
GOSUB to 4411H to write 256 x E5H bytes (the actual sector data area, pre-filled with E5H) to format buffer.
546A
LD C,F7H 0EF7
Set C = F7H (CRC generator command - tells FDC to write CRC bytes here).
546C
GOSUB to 4417H to write the CRC command byte for data field to format buffer.
546F
LD C,FFH 0EFF
Let Register C equal FFH.
5471
GOSUB to 4417H to put FFH into format buffer..
5474
LD B,0BH 060B
Let Register B equal 0BH (Decimal: 11) as a counter.
5476
GOSUB to 440FH to write 11 x FFH bytes, which will be "GAP 3", the gap after data field, to the format buffer..
5479
INC DE 13
Advance to next entry in sector interleave table (next sector number)..
547A
LD A,(DE) 1A
Fetch the next sector number from the interleave table.
547B
INC A 3C
Increment A by 1 to test if it was FFH (the table terminator). If it was FFH, adding 1 makes it 00H and sets the Z flag.
547C
If NOT at end of table (not FFH), loop back to 5428H to build the next sector's format pattern.

At this point, all 10 sectors have been set up.

547E
LD B,5BH 065B
Let Register B equal 5BH (Decimal: 91).
5480
GOSUB to 440FH to write 91 x FFH bytes, which will be "GAP 4", the final gap to fill remainder of track, to format buffer.
5483
GOSUB to 54A3H to select the destination drive, issue the "Write Track" command (F4H), and write the entire format buffer to the physical disk track.
5486
LD A,(5435H) 3A3554
Fetch the current track number from track counter.
5489
INC A 3C
Increment track counter (we just finished formatting this track, move to next).
548A
LD (5435H),A 323554
Store the incremented track counter back into memory location 5435H, so now 5435H holds the NEXT track to be formatted.
548D
CP 23H FE23
Compare the track counter against 23H (decimal 35). TRSDOS 2.3 disks have 35 tracks numbered 0-34. If A=35, we've gone past the last track, meaning all tracks have been formatted. Z flag is set if A equals 35.
548F
If Z flag is set (track counter = 35), all tracks are formatted! Jump to 54EDH which is the "Last Routine of FORMAT" - it verifies the format, writes out the directory structure, and finishes up.

Next we issue a STEP IN command to move the read/write head one track inward (toward higher track numbers.

5491
LD A,5BH 3E5B
If we didn't jump away, we have more to format. Let Register A equal 5BH (Binary:0101 1011) which tells the floppy disk controller to STEP IN with HEAD LOAD, 30ms Stepping.
5493
LD (37ECH),A 32EC37
Send the command held in Register A to the FDC (37ECH is the Floppy Disk Controller's Command Register).
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Function Description
0000hVr1r0
010uhVr1r0Command=Step In
Bit 7-5: Command Code (010)
u: 1=Update Track Register, 0=No update
h: 1=Enable Head Load/Settle, 0=No delay
V: 1=Verify Destination Track ID, 0=No verification
r1, r0: Stepping Motor Rate (00=3ms, 01=6ms, 10=10ms, 11=15ms)

Standard code to delay to compensate for the slow FDC to make sure it had time to set the command.

5496
PUSH AF F5
Save the contents of Register Pair AF to the top of the stack.
5497
POP AF F1
Put the value held at the top of the STACK into Register Pair AF, and then remove the entry from the stack.
5498
PUSH AF F5
Save the contents of Register Pair AF to the top of the stack.
5499
POP AF F1
Put the value held at the top of the STACK into Register Pair AF, and then remove the entry from the stack.
549A
LD A,(37ECH) 3AEC37
Fetch the FDC's status (which is mirrored to 37ECH) and store it into Register A.
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
549D
RRCA 0F
Rotate the bits in A right (i.e., higher bit moves lower), with Bit 0 being copied to both the CARRY FLAG and Bit 7. This means that the CARRY flag will be the same as BIT 0 (which is the BUSY return code)
549E
If Carry is set (FDC was BUSY), loop back to 549AH to keep polling. The head is still stepping to the next track.
54A0
If we are here then the head has arrived at the new track. Jump back to 53FBH (the top of the main format loop) to format this next track.

54A3H - Write the format pattern to disk.

54A3
Call the write routine at 54A7H. This is a clever trick: if the routine at 54A7H returns with NZ (indicating the destination disk wasn't ready or was swapped), execution continues to 54A6 which will fall through and call 54A7H again. This allows for a retry.
54A6
RET Z C8
If Z flag is set (write completed successfully), return to caller (5486H). If NZ, execution falls through to call 54A7H again as a retry.
54A7
GOSUB to 449CH to call the "Insert Destination Disk" routine. This routine: (1) Loads the destination drive select code from 449DH, (2) ORs it with 40H to set bit 6 (identifying it as destination), (3) Displays "INSERT DESTINATION DISK (ENTER)" if needed, (4) Waits for user to press ENTER, (5) Checks that the disk is not write-protected. Returns Z if OK, NZ if there was a problem.
54AA
LD BC,6300H 010063
Point BC to the format pattern buffer at 6300H. BC will be used as the source pointer for the bytes to write to disk.
54AD
LD HL,37ECH 21EC37
Point HL to the FDC Command/Status Register at 37ECH. HL will be used to read the FDC status during the write loop.
54B0
LD (HL),F4H 36F4
Store the value held in F4H (Binary: 1111 0100) into the memory location pointed to by Register Pair HL. This sends: Write Track (1111 0n00), n = 15ms Delay (1)
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Function Description
11110100
11110100Command=Write Track (Format)
Bit 7-4: Write Command (1111)
Bit 3-0: Specific Code (0100)
0111 = Write CRC
1000-1011 = DAMs
1100=Index Address Mark
1110=ID Address Mark
54B2
LD DE,37EFH 11EF37
Let Register Pair DE equal 37EFH.
NOTE: 37EFH is the Floppy Disk Controller's Data Register (i.e., the byte to be written/read from disk).
54B5
PUSH BC C5
4 Instruction Delay
54B6
POP BC C1
4 Instruction Delay
54B7
PUSH BC C5
4 Instruction Delay
54B8
POP BC C1
4 Instruction Delay
54B9
DI F3
Disable Interrupts.
54BA
Continue via a JUMP to 54BFH which will write out all that code to the diskette.

54BCH - Check that the drive is ready, and write the byte pointed at by BC to the diskette

54BC
RRCA 0F
Rotate the bits in A right (i.e., higher bit moves lower), with Bit 0 being copied to both the CARRY FLAG and Bit 7. This will have the effect of moving the FDC's BUSY FLAG to the CARRY FLAG
54BD
If NOT busy, something's wrong, so JUMP out of this loop to the next routine 54CAH because the disk might be moving at the wrong speed. That routine will either declare drive too fast/slow, or just REturn.
54BF
LD A,(HL) 7E
Fetch the value held in the memory location pointed to by Register Pair HL and store it into Register A. HL should be pointing to the Floppy Disk Controller Status, so this fetches the status of the drive.
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
54C0
BIT 1,A CB4F
Test Bit Number 1 of Register A, which is the INDEX register. Z FLAG will be set if that bit is 0, and NZ FLAG will be set if that bit is 1.
54C2
If the Z FLAG (Zero) has been set, then there is no index found, so JUMP to 54BCH to move Bit 1 to Bit 0, and Bit 0 (the BUSY status) to the CARRY FLAG, and to then test the CARRY FLAG.
54C4
LD A,(BC) 0A
If we are here then Index was found and the FDC is busy - fetch next byte from format buffer.
54C5
LD (DE),A 12
Store the value held in Register A from the above table to the Floppy Drive by writing it to 37EFH (held in Register Pair DE).
54C6
INC BC 03
INCrement the value stored in Register Pair BC by 1 to point to the next byte in the format buffer.
54C7
KEEP WRITING via a JUMP BACK to 54BFH.

54CAH - Jumped here from 54BDH if the INDEX/BUSY status isn't quite right to either declare "TRANSFER RATE TOO FAST," "MOTOR FAST," "MOTOR SLOW," and HALT in those cases, or to just REturn.

54CA
LD A,(HL) 7E
Fetch the value from the Floppy Disk Controller Command/Status (pointed to by Register Pair HL) and store it into Register A.
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
54CB
LD HL,6179H 217961
Let Register Pair HL equal 6179H to point to the message 0AH + 'CAN'T FORMAT, DISK TRANSFER RATE TOO FAST!' + 0DH.
54CE
BIT 2,A CB57
Test Bit Number 2 of Register A to see if the disk was at TRACK 0 or not (1=Yes, 0=No). Z FLAG will be set if that bit is 0, and NZ FLAG will be set if that bit is 1.
54D0
If the disk is NOT at Track 0 then the NZ FLAG (Not Zero) will have been set, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
54D3
LD HL,9D00H 21009D
Let Register Pair HL equal 9D00H.
54D6
ADD HL,BC 09
LET Register Pair HL = Register Pair HL + Register BC. The Carry Flag will be affected.
54D7
EX DE,HL EB
EXchange the value stored in Register Pair HL with the value stored in Register Pair DE.
54D8
LD HL,F413H 2113F4
Let Register Pair HL equal F413H.
54DB
ADD HL,DE 19
LET Register Pair HL = Register Pair HL + Register DE. The Carry Flag will be affected.
54DC
LD HL,61A5H 21A561
Let Register Pair HL equal 61A5H to point to the message 0AH + 'CAN'T FORMAT, MOTOR SPEED TOO SLOW!' + 0DH.
54DF
If the NC FLAG (No Carry) has been set, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
54E2
LD HL,F397H 2197F3
Let Register Pair HL equal F397H.
54E5
ADD HL,DE 19
LET Register Pair HL = Register Pair HL + Register DE. The Carry Flag will be affected.
54E6
LD HL,61CAH 21CA61
Let Register Pair HL equal 61CAH to point to the message 0AH + 'CAN'T FORMAT, MOTOR SPEED TOO FAST!' + 0DH.
54E9
If the C FLAG (Carry) has been set, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
54EC
RET C9
RETurn to the caller.

54EDH - Last Routine of FORMAT. This routine verifies the format, writes out the directory, and finished up.

We are done formatting, so now we need to display a CARRIAGE RETURN to move off of the "FORMATTING TRACK nn" overwriting message.

The main variables are:

4A00H-4A22HTrack status bytes (35 bytes)FCH = track OK (verify it), FFH = track locked out (skip it)
45AAHStart of "VERIFYING TRACK nn, SECTOR nn" messageStatus message with embedded track/sector placeholders
45BBHTrack number position in message (2 bytes)ASCII digits for track number (tens, ones)
45C6HSector number position in message (2 bytes)ASCII digits for sector number (tens, ones)
4B00HSector read buffer256-byte buffer for reading sectors during verification
4709HDirectory track numberTypically 11H (17 decimal) for TRSDOS 2.3
DCurrent track number (0-34)
ECurrent sector number (0-9)
HLPointer into track status buffer at 4A00H
BCSector read buffer address (4B00H) and ASCII conversion results

54ED
LD A,0DH 3E0D
Load A with 0DH (ASCII carriage return). This will move the cursor to the start of the next line, leaving the "FORMATTING TRACK nn" display behind.
54EF
Call the Model I ROM character output routine at 0033H to display the carriage return. This moves the cursor down so the verify messages don't overwrite the format messages.
54F2
LD D,00H 1600
Initialize D = 0 (starting with Track 0). D will be the track counter for the verification loop.

Start of the TRACK loop.

54F4
LD L,D 6A
Copy the track number from D into L. This sets up HL to point to the track status byte.
54F5
LD H,4AH 264A
Set H = 4AH. Now HL = 4A00H + track number, pointing to the status byte for this track in the buffer.
54F7
LD A,(HL) 7E
Fetch the track status byte from the buffer at (HL). This byte indicates whether the track should be verified (FCH = OK) or skipped (FFH = locked out).
54F8
INC A 3C
INCrement the value stored in Register A by 1 to make it easier to test to see if it was FFH or not.
54F9
If Z flag set (track status was FFH = locked out), skip verification of this track entirely - jump to 553CH which increments to the next track.
54FC
LD A,D 7A
Copy the current track number from D into A for ASCII conversion.
54FD
Call the binary-to-ASCII conversion routine. Converts the track number in A to two ASCII digits: C = tens digit, B = ones digit.
5500
LD (45BBH),BC ED43BB45
Store the ASCII track digits into 45BBH (the "nn" position for track number in the "VERIFYING TRACK nn, SECTOR nn" message). Note: Z80 stores BC as C first, then B (little-endian), so tens digit goes to 45BBH, ones to 45BCH.
5504
LD E,00H 1E00
Initialize E = 0 (starting with Sector 0). E will be the sector counter for the inner verification loop.

Start of the SECTOR loop.

5506
PUSH DE D5
Save DE (track/sector) to the stack. DE will be needed for the disk read but also needs to be preserved across the message display routine.
5507
LD A,D 7A
Copy the current track number into A. (Note: This instruction seems redundant as the track number was already converted above, but it may be here for re-entry from a different code path.)

Something is really bad here. In the FORMAT/CMD program, Register A is sent to the drive select latch before the next command overwrites A. It is odd to have 2 commands, back to back, where the first command is just simply ignored.

5508
LD A,E 7B
Copy the current sector number from E into A for ASCII conversion.
5509
Call the binary-to-ASCII conversion routine. Converts the sector number in A to two ASCII digits: C = tens digit, B = ones digit.
550C
LD (45C6H),BC ED43C645
Store the ASCII sector digits into 45C6H (the "nn" position for sector number in the "VERIFYING TRACK nn, SECTOR nn" message).
5510
LD HL,45AAH 21AA45
Point HL to the start of the "VERIFYING TRACK nn, SECTOR nn" message at 45AAH.
5513
Call the display message routine at 457BH to show the verification progress message on screen. The message starts with 1DH (cursor home) so it overwrites the previous message at the same screen position.
5516
POP DE D1
Restore DE from the stack. D = track number, E = sector number - ready for the disk read.
5517
LD BC,4B00H 01004B
Point BC to the sector read buffer at 4B00H. This is where the disk read routine will store the sector data during verification.
551A
Read the sector (track D, sector E) into buffer at BC (4B00H). On exit, Register A holds the FDC Status.

Test for "Record Not Found" (bit 4) - if set, lock out the track

551D
LD HL,61EFH 21EF61
Let Register Pair HL equal 61EFH to point to the message 3AH + 'NOT FOUND! TRACK LOCKED OUT!' + 0DH
5520
BIT 4,A CB67
Test Bit Number 4 of Register A which would be the FDC status of "RECORD NOT FOUND" if high. Z FLAG will be set if that bit is 0, and NZ FLAG will be set if that bit is 1.
5522
If the NZ FLAG (Not Zero) has been set, JUMP to 43F5H to display the message pointed to by Register Pair HL, put a FFH into the in-RAM GAT at 5Cnn (nn = the contents of Register D), and then JUMP to 55BDH.

Test for "CRC Error" (bit 3) or "Lost Data" (bit 2) - display error and halt

5525
LD HL,620EH 210E62
Let Register Pair HL equal 620EH to point to the message 3AH + 'CRC ERROR! TRACK LOCKED OUT!' + 0DH
5528
BIT 3,A CB5F
Test Bit Number 3 of Register A which would be the FDC status of "CRC ERROR" if high. Z FLAG will be set if that bit is 0, and NZ FLAG will be set if that bit is 1.
552A
If the NZ FLAG (Not Zero) has been set, JUMP to 43F5H to display the message pointed to by Register Pair HL, put a FFH into the in-RAM GAT at 5Cnn (nn = the contents of Register D), and then JUMP to 55BDH.
552D
LD HL,6260H 216062
Let Register Pair HL equal 6260H to point to the message 0AH + 'CAN'T VERIFY FORMAT, DISK TRANSFER RATE TOO FAST!' + 0DH
5530
BIT 2,A CB57
Test Bit Number 2 of Register A which would be the FDC status of "LOST DATA" if high. Z FLAG will be set if that bit is 0, and NZ FLAG will be set if that bit is 1.
5532
If the NZ FLAG (Not Zero) has been set, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
5535
INC E 1C
Increment sector, loop back to 5506H if still fewer than 10 sectors.
5536
LD A,E 7B
Copy the contents of Register E into Register A for testing.
5537
CP 0AH FE0A
Compare the value held in Register A against 0AH (Decimal: 10). If Register A equals 10, the Z FLAG is set; otherwise the NZ FLAG is set.
5539
If the NZ FLAG (Not Zero) has been set then we have not yet hit 10 sectors on the track, so JUMP BACK to 5506H.

While this can be a pass through, it is also the jump point if the byte in the in-RAM GAT was FFH

553C
INC D 14
Increment track, loop back to 54F4H if fewer than 35 tracks.
553D
LD A,D 7A
Copy the contents of Register D into Register A.
553E
CP 23H FE23
Compare the value held in Register A against 23H (Decimal: 35). If Register A equals 35, the Z FLAG is set; otherwise the NZ FLAG is set.
5540
If the NZ FLAG (Not Zero) has been set, then we have not yet hit 35 tracks, so JUMP BACK to 54F4H to keep reading.

At this point, we have verified all tracks, now lets display a carriage return, copy 35 bytes from 4A00H (destination disk's track allocation status) to 4A60H (normally the track lockout table offset in the GAT buffer).

5543
LD A,0DH 3E0D
Let Register A equal CARRIAGE RETURN.
5545
GOSUB to 0033H in the Model I ROM to display the character held in Register A at the current cursor position.
5548
LD HL,4A00H 21004A
Let Register Pair HL equal 4A00H, which is the source RAM area.
554B
LD DE,4A60H 11604A
Let Register Pair DE equal 4A60H, which is the DEstination RAM area.
554E
LD BC,0023H 012300
Let Register Pair BC equal 0023H (Decimal: 35) to transfer 35 bytes of data; one byte for each track.
5551
LDIR EDB0
Transfers 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.

5553H-5571H is a Lockout Table Compatibility Check. It will [1] Compare the source and destination disk lockout tables, [2] For each track, verify that any granules USED on the source disk are NOT locked out on the destination, and [3] If the destination has bad sectors where the source needs good ones, displays "BACKUP REJECTED DUE TO FLAWS ON DESTINATION DISK!" and halt.

5553
LD HL,4A60H 21604A
Point HL to the DESTINATION disk's track lockout table at 4A60H (offset +60H in the GAT buffer at 4A00H). This table has 35 bytes, one per track, where each bit indicates if a granule is locked out.
5556
LD DE,4960H 116049
Point DE to the SOURCE disk's track lockout table at 4960H (offset +60H in the GAT buffer at 4900H). This tells us which granules the source disk needs to be good on the destination.
5559
LD B,23H 0623
Set loop counter B = 35 (23H). There are 35 tracks on a TRSDOS 2.3 disk (tracks 0-34), so we need to check all 35 lockout bytes.

Start of a loop

555B
LD A,(DE) 1A
Top of a loop to fetch the SOURCE disk's lockout byte for this track from (DE). Each bit that is SET (=1) indicates a granule that is locked out on the source disk.
555C
CPL 2F
Complement (invert) all bits in A. Now each bit that is SET indicates a granule that is GOOD (usable) on the source disk. These are the granules we need to exist on the destination.
555D
LD C,A 4F
Save the inverted source lockout mask into C. C now holds a mask where 1 = "source needs this granule to be good".
555E
PUSH DE D5
Save DE (pointer into source lockout table) to the stack, as we need to temporarily modify E.
555F
LD A,E 7B
Copy the low byte of the source pointer (E) into A. E ranges from 60H to 82H as we walk through the lockout table.
5560
SUB 60H D660
Subtract 60H from A to convert the lockout table offset to a track allocation table offset. This changes range from 60H-82H to 00H-22H.
5562
LD E,A 5F
Put the adjusted offset into E. DE now points to the SOURCE disk's track ALLOCATION byte (at 4900H-4922H) instead of the lockout byte.
5563
LD A,(DE) 1A
Fetch the SOURCE disk's track allocation byte from (DE). Each bit pattern indicates granule usage: FC=both free, FD/FE=partial, FF=both allocated or track locked out.
5564
POP DE D1
Restore DE to point back to the source lockout table position.
5565
AND C A1
AND the source allocation byte with the inverted source lockout mask (C). Result: bits set only where source has ALLOCATED granules that are NOT locked out. These are granules we actually need on the destination.
5566
AND (HL) A6
AND the result with the DESTINATION disk's lockout byte at (HL). If any bit remains set, it means the source needs a granule that is LOCKED OUT on the destination - a fatal flaw!
5567
PUSH HL E5
Save HL (destination lockout table pointer) to the stack before loading the error message pointer.
5568
LD HL,622DH 212D62
Point HL to the error message at 622DH: "BACKUP REJECTED DUE TO FLAWS ON DESTINATION DISK!" in case we need to display it.
556B
JP NZ,43A3H C2A343
If NZ (any bit was set after the AND operations), the destination disk has a locked-out granule where the source needs good space. JUMP to error handler at 43A3H to display the message and HALT.
556E
POP HL E1
Restore HL to point to the destination lockout table position.
556F
INC DE 13
Increment DE to point to the next byte in the source lockout table.
5570
INC HL 23
Increment HL to point to the next byte in the destination lockout table.
5571
DJNZ 555BH 10E8
Decrement B and loop back to 555BH if not zero. Continue checking all 35 tracks.

If we have a single-drive backup, we need to calculate the RAM Buffer Size.
If 44EFH is not 0, then this is a 2 drive backup, and we skip this routine and move right to copy.
Otherwise, we need to determine how much RAM is available. To do this, 4K pages are tested from high memory downward. Once that is determined the calculation as to how many complete tracks can be buffered in RAM at a time, and stores that count in 4713H so that the copy routine knows how many tracks to load before prompting for a disk swap.

5573
LD A,(44EFH) 3AEF44
Fetch the DRIVE DIFFERENCE value from 44EFH. This was calculated earlier as (SOURCE drive - DESTINATION drive). Zero means single-drive backup (same drive for source and destination).
5576
OR A B7
Set flags based on A. Z flag set if A=0 (single-drive backup), NZ if A≠0 (two-drive backup).
5577
JP NZ,4200H C20042
If NZ (two-drive backup), jump to 4200H to begin the actual backup copy operation. No need to calculate available RAM since we have two drives.
557A
LD HL,0FFFH 21FF0F
For single-drive backup, we need to calculate how much RAM is available for buffering tracks. Start HL at 0FFFH (4095), which will be adjusted to find the top of usable RAM.
557D
LD A,H 7C
Top of the RAM Test Loop. Copy H into A. H starts at 0FH and will be decremented by 10H each iteration to test different 4K pages.
557E
SUB 10H D610
Subtract 10H from A. This moves down by 4K (1000H) in memory. Testing if RAM exists at progressively lower addresses.
5580
LD H,A 67
Put the decremented value back into H. HL now points 4K lower in memory.
5581
LD A,(HL) 7E
Read the byte at the current test address (HL) into A. This is the original value at this memory location.
5582
LD B,A 47
Save the original byte value into B so we can restore it after the test.
5583
CPL 2F
Complement (invert) all bits in A. If we can write this inverted value and read it back, RAM exists at this address.
5584
LD (HL),A 77
Write the complemented (inverted) value to the memory location being tested.
5585
CP (HL) BE
Compare A with the value we just wrote. If RAM exists, the value should match (Z flag set). If no RAM or ROM, the value won't match (NZ flag set).
5586
LD (HL),B 70
Restore the original byte value (saved in B) back to the memory location, preserving memory contents.
5587
JR NZ,557DH 20F4
If NZ (RAM test failed - no RAM at this address), loop back to 557DH to test the next lower 4K page.
5589
LD A,H 7C
RAM was found! Copy H (the high byte of the highest usable RAM address) into A.
558A
SUB 4DH D64D
Subtract 4DH from A. 4D00H is the start of the track buffer area. This calculates how many 256-byte pages are available above the buffer area.
558C
LD B,00H 0600
Initialize B = 0. B will count how many complete tracks (10 sectors = 2560 bytes ≈ 0AH pages) can fit in available RAM.

Track Count Loop Start

558E
INC B 04
Increment track counter B.
558F
SUB 0AH D60A
Subtract 0AH (10 decimal) from A. Each track requires 10 sectors (10 x 256 = 2560 bytes ≈ 0AH pages of 256 bytes).
5591
JR NC,558EH 30FB
If No Carry (still have room for more tracks), loop back to 558EH to count another track.
5593
LD A,B 78
Copy the track count from B into A. B now holds how many complete tracks can be buffered in RAM.
5594
DEC A 3D
Decrement A by 1. We counted one too many (the loop increments before checking), so adjust the count.
5595
LD (4713H),A 321347
Store the number of tracks that can be buffered into memory location 4713H. This value will be used during the single-drive backup to know how many tracks to read before prompting for a disk swap.
5598
JP 4200H C30042
Jump to 4200H to begin the actual backup copy operation. The routine at 4200H will prompt for the destination disk, perform the copy using the calculated buffer size, and handle disk swapping for single-drive backups.

4200H - BACKUP COPY ROUTINE: Initialize Destination Disk

This is the main BACKUP copy routine, relocated from 559BH to run at 4200H. It operates in three phases: (1) Initialize destination disk with marker byte, (2) Read source tracks into RAM buffer, (3) Write buffered data to destination. For single-drive backups, phases 2 and 3 repeat with disk swaps until all 35 tracks are copied.

Key Memory Locations

AddressPurposeDescription
4713HTracks per bufferNumber of complete tracks that can fit in RAM buffer (calculated for single-drive backup)
4714HTracks loaded counterHow many tracks have been loaded into the buffer so far
44E8HLast prompted driveDrive code of disk currently in drive (used to determine if disk swap is needed)
44EFHDrive differenceSource - Destination drive; 0 = single-drive backup requiring swaps
4800H-4822HFDC status arrayStores FDC status for each track (0-34), indexed by track number
4900H-49FFHSource GAT bufferGAT sector from source disk
4A00H-4AFFHDest GAT bufferGAT sector from destination disk
4B00H-4BFFHGeneral bufferSingle-sector read/write buffer
4D00H+Multi-track bufferLarge buffer for single-drive backup (holds multiple tracks)

Key Subroutines

AddressPurpose
4402HConvert binary in A to two ASCII digits (tens in C, ones in B)
4437HPrompt for source disk if needed, verify correct source via Pack ID
43B3HWrite sector to disk (A=FDC command, BC=buffer, DE=track/sector)
44B4HPrompt for destination disk if needed, verify write-enabled
449CHPrompt for destination disk, check write protection
453CHDisk I/O Executive - read sector (BC=buffer, DE=track/sector)
457BHDisplay message pointed to by HL
4200H
559BH
Call the "Insert Destination Disk" routine. This prompts the user to insert the destination disk (if needed), sets Bit 6 in the drive code to mark it as destination, verifies the disk is not write-protected, and returns.
4203H
559EH
Issue a RESTORE command to the FDC to move the read/write head to Track 0, then poll until the drive is no longer BUSY.
4206H
55A1H
LD BC,4B00H 01 00 4B
Set BC to point to 4B00H, the general-purpose 256-byte sector buffer. This will receive the GAT sector data from the destination disk.
4209H
55A4H
LD DE,0000H 11 00 00
Set DE to Track 0, Sector 0 - the GAT (Granule Allocation Table) sector location.
420CH
55A7H
Call the Disk I/O Executive to read the destination disk's GAT sector (Track 0, Sector 0) into the buffer at 4B00H.
420FH
55AAH
LD HL,4601H 210146
Point HL to the error message "DESTINATION DISK READ ERROR!" in case the read failed.
4212H
55ADH
If the read failed (NZ flag set), jump to the error handler at 43A3H which displays the error message and HALTs.

Now we mark the destination disk's GAT with 76H in the first byte. This serves as a "backup in progress" marker. The value 76H is the HALT opcode, so if this sector is accidentally executed, the system will safely halt.

4215H
55B0H
LD A,76H 3E 76
Load A with 76H, the "backup marker" value. This will be written to the first byte of the destination GAT.
4217H
55B2H
LD (4B00H),A 32 00 4B
Store the 76H marker into the first byte of the GAT buffer. This marks the destination disk as being part of a backup set.
421AH
55B5H
LD A,A8H 3E A8
Load A with A8H (10101000 binary) - the FDC "Write Sector" command with head load enabled. This will be passed to the write routine.
421CH
55B7H
LD BC,4B00H 01 00 4B
Set BC to point to the modified GAT buffer at 4B00H (source of data to write).
421FH
55BAH
Call the Write Sector routine. A=command (A8H), BC=buffer (4B00H), DE=track/sector (still 0000H from earlier). This writes the modified GAT back to the destination disk with the 76H marker.
4222H
55BDH
LD HL,461FH 21 1F 46
Point HL to the error message "DESTINATION DISK WRITE ERROR!" in case the write failed.
4225H
55C0H
If the write failed (NZ flag set), jump to error handler to display the message and HALT.
4228H
55C3H
Call the "Swap to Source Disk" routine. If this is a single-drive backup, this prompts the user to insert the source disk, verifies it's the correct source via Pack ID comparison, and returns.
422BH
55C6H
Issue RESTORE command to move head to Track 0 on the (now) source disk.

422EH - PHASE 2: Load Source Tracks (LOADING)

This phase reads all tracks from the source disk into the RAM buffer. Register D holds the current track number (0-34), Register E holds the current sector number (0-9). For single-drive backups, 4714H counts how many tracks have been loaded; when it equals 4713H (tracks per buffer), we switch to writing.

422EH
55C9H
XOR A AF
Set A to zero and clear all flags. This zero will initialize the "tracks loaded" counter.
422FH
55CAH
LD (4714H),A 32 14 47
Initialize the "tracks loaded" counter at 4714H to zero. This counts how many complete tracks have been read into the buffer during single-drive backups.
4232H
55CDH
LD D,00H 16 00
Initialize Register D to 0 - this is the track counter. We'll read tracks 0 through 34.
4234H
55CFH
LD BC,4D00H 01 00 4D
Top of the Track loop. Set BC to 4D00H, the start of the multi-track sector buffer. Each sector is 256 bytes, so this buffer will fill with sector data as we read.
4237H
55D2H
LD A,(37EDH) 3A ED 37
Fetch the current FDC Track Register value from 37EDH. This is the physical track position of the read/write head.
423AH
55D5H
LD (42ADH),A 32 AD 42
Store the current head position into 42ADH. This is a self-modifying code location - the operand of a LD A,nn instruction at 42ACH, used later during the write phase.
423DH
55D8H
LD A,D 7A
Copy the current track number (from D) into A for self-modifying code.
423EH
55D9H
LD (42B2H),A 32 B2 42
Store the starting track number into 42B2H. This is the operand of a LD D,nn instruction at 42B1H, used during the write phase to know which track to start writing.

Top of the INNER TRACK loop. For each track, we check the source disk's track allocation to see if this track needs to be copied. We skip locked-out tracks.

4241H
55DCH
PUSH BC C5
Top of the TRACK part of the loop. Save the current buffer pointer (BC) to the stack before we modify registers.
4242H
55DDH
LD H,49H 26 49
Set H to 49H, pointing to the source disk's GAT buffer at 4900H.
4244H
55DFH
LD L,D 6A
Set L to the current track number (D). HL now points to 49xxH where xx is the track number - this is the track allocation byte in the source GAT.
4245H
55E0H
LD C,(HL) 4E
Fetch the source disk's track allocation byte for this track into C. Each bit pair indicates granule status: 11=allocated, 00=free, etc.
4246H
55E1H
LD A,D 7A
Copy the track number into A for calculating the lockout table offset.
4247H
55E2H
ADD A,60H C6 60
Add 60H to the track number to get the lockout table offset. The lockout table starts at GAT+60H.
4249H
55E4H
LD L,A 6F
Set L to the lockout table offset. HL now points to the source disk's lockout byte for this track (4960H + track).
424AH
55E5H
LD A,(HL) 7E
Fetch the source disk's lockout byte for this track. A bit=1 means that granule is locked out (bad sector).
424BH
55E6H
CPL 2F
Complement (invert) the lockout byte. Now bit=1 means the granule is GOOD and needs to be copied.
424CH
55E7H
AND C A1
AND with the track allocation byte (C). Result: bits set only where source has ALLOCATED granules that are NOT locked out.
424DH
55E8H
LD H,4AH 26 4A
Point H to the destination disk's GAT buffer at 4A00H.
424FH
55EAH
LD C,(HL) 4E
Fetch the destination disk's lockout byte for this track into C (HL points to 4A60H + track).
4250H
55EBH
OR C B1
OR with destination lockout. A now has bits set for: (1) source allocated non-locked granules OR (2) destination locked granules.
4251H
55ECH
CP C B9
Compare result against destination lockout. If equal (Z flag), this track is entirely locked out on destination - skip reading it.
4252H
55EDH
POP BC C1
Restore the buffer pointer from the stack.
4253H
55EEH
If Z flag set (track fully locked out), skip to 429BH to advance to the next track without reading this one.

Track needs to be read. Call 4437H to ensure source disk is inserted (for single-drive backup), then read all 10 sectors.

4256H
55F1H
Call the "Verify Source Disk" routine. For single-drive backups, this ensures the source disk is inserted and verifies it's the correct disk via Pack ID.
4259H
55F4H
LD E,00H 1E 00
Initialize sector counter E to 0. We'll read sectors 0-9 from this track.

Top of the SECTOR part of the loop. Read all 10 sectors from the current track, displaying "LOADING TRACK nn, SECTOR nn" for each.

425BH
55F6H
PUSH BC C5
Save the buffer pointer to the stack.
425CH
55F7H
LD A,D 7A
Copy the track number into A for conversion to ASCII.
425DH
55F8H
Convert track number in A to two ASCII digits: tens digit in C, ones digit in B.
4260H
55FBH
LD (45DAH),BC ED 43 DA 45
Store the ASCII track digits into the "LOADING TRACK nn" message at offset 45DAH (the "nn" placeholder).
4264H
55FFH
LD A,E 7B
Copy the sector number into A for conversion to ASCII.
4265H
5600H
Convert sector number in A to two ASCII digits: tens digit in C, ones digit in B.
4268H
5603H
LD (45E5H),BC ED 43 E5 45
Store the ASCII sector digits into the "LOADING" message at offset 45E5H (the second "nn" placeholder).
426CH
5607H
POP BC C1
Restore the buffer pointer from the stack.
426DH
5608H
PUSH DE D5
Save the track/sector (DE) to the stack before the display call.
426EH
5609H
LD HL,45C9H 21 C9 45
Point HL to the "LOADING TRACK nn, SECTOR nn" message at 45C9H.
4271H
560CH
Display the "LOADING TRACK nn, SECTOR nn" message. The 1DH at the start homes the cursor so each update overwrites the previous.
4274H
560FH
POP DE D1
Restore track/sector from the stack.
4275H
5610H
Call the Disk I/O Executive to read one sector from the source disk. D=track, E=sector, BC=buffer address.
4278H
5613H
LD HL,45E8H 21 E8 45
Point HL to "SOURCE DISK READ ERROR!" message in case of failure.
427BH
5616H
If read failed (NZ), jump to error handler to display message and HALT.
427EH
5619H
INC E 1C
Increment sector counter. BC automatically advanced by 256 during the read, so it now points to the next buffer slot.
427FH
561AH
LD A,E 7B
Copy sector number into A for comparison.
4280H
561BH
CP 0AH FE 0A
Compare sector number against 10 (0AH). Have we read all 10 sectors (0-9)?
4282H
561DH
If not all 10 sectors read yet (NZ), loop back to 425BH to read the next sector.

All 10 sectors of this track have been read. Store the FDC status, increment the track counter, and check if we need to switch to writing.

4284H
561FH
LD A,(37ECH) 3A EC 37
Fetch the FDC status register from 37ECH into A. This records the result of the last disk operation.
4287H
5622H
LD L,D 6A
Set L to the track number.
4288H
5623H
LD H,48H 26 48
Set H to 48H. HL now points to the FDC status array at 4800H + track. Each track has its status stored here.
428AH
5625H
LD (HL),A 77
Store the FDC status for this track into the status array. This is checked later during the write phase.
428BH
5626H
LD HL,4714H 21 14 47
Point HL to the "tracks loaded" counter at 4714H.
428EH
5629H
INC (HL) 34
Increment the tracks loaded counter. We've successfully loaded one more track into the buffer.
428FH
562AH
LD A,(44EFH) 3A EF 44
Fetch the "drive difference" value from 44EFH. Zero = single-drive backup, non-zero = two-drive backup.
4292H
562DH
OR A B7
Set flags based on A. Z flag means single-drive backup (need to check buffer capacity).
4293H
562EH
If two-drive backup (NZ), skip buffer capacity check and jump to 42A9H to continue with the write phase.

Single-drive backup: check if the buffer is full by comparing tracks loaded (4714H) against tracks per buffer (4713H).

4295H
5630H
LD A,(4713H) 3A 13 47
Fetch the "tracks per buffer" value from 4713H. This was calculated earlier based on available RAM.
4298H
5633H
CP (HL) BE
Compare tracks per buffer against tracks loaded (HL still points to 4714H). Z flag = buffer is full.
4299H
5634H
If buffer is full (Z flag), jump to 42A9H to switch to the WRITE phase (write buffered tracks to destination).

Move to the next track since the buffer is not full yet (or we have a two-drive backup).

429BH
5636H
INC D 14
429CH
5637H
LD A,D 7A
Copy track number into A for comparison.
429DH
5638H
CP 23H FE 23
Compare against 35 (23H). Have we processed all 35 tracks (0-34)?
429FH
563AH
If not all tracks done (NZ), loop back to 4241H to process the next track.

All 35 tracks have been read. Check if anything was actually loaded (for edge case where all tracks were locked out).

42A2H
563DH
LD A,(4714H) 3A 14 47
Fetch the tracks loaded counter.
42A5H
5640H
OR A B7
Set flags. Z flag = no tracks were loaded (all locked out).
42A6H
5641H
If nothing was loaded (Z), skip the write phase entirely and jump to 434FH to finish up (copy date, write final GAT).

42A9H - PHASE 3: Write to Destination (COPYING)

This phase writes the buffered sectors to the destination disk. For single-drive backups, after writing all buffered tracks, it loops back to read more from the source. Register D = track number, Register E = sector number, BC = buffer pointer.

Start of write routine

42A9H
5644H
LD BC,4D00H 01 00 4D
42ACH
5647H
LD A,00H 3E 00
Load A with the saved FDC track position. NOTE: The 00H here is self-modified - the actual value was stored at 42ADH during the read phase (at 423AH).
42AEH
5649H
LD (37EDH),A 32 ED 37
Restore the FDC Track Register to the saved head position. This ensures we seek from the correct starting point.
42B1H
564CH
LD D,00H 16 00
Load D with the starting track number. NOTE: The 00H here is self-modified - the actual value was stored at 42B2H during the read phase (at 423EH).

Top of the write track loop. For each track that was read, check if it needs to be written and perform the copy.

42B3H
564EH
PUSH BC C5
Save the buffer pointer to the stack.
42B4H
564FH
LD H,49H 26 49
Set H to 49H, pointing to the source disk's GAT buffer at 4900H.
42B6H
5651H
LD L,D 6A
Set L to the current track number. HL points to source track allocation byte.
42B7H
5652H
LD C,(HL) 4E
Fetch source track allocation byte into C.
42B8H
5653H
LD A,D 7A
Copy track number to A for lockout table calculation.
42B9H
5654H
ADD A,60H C6 60
Add 60H to get lockout table offset.
42BBH
5656H
LD L,A 6F
Point HL to source lockout byte for this track.
42BCH
5657H
LD A,(HL) 7E
Fetch source lockout byte.
42BDH
5658H
CPL 2F
Complement - now 1=good granule.
42BEH
5659H
AND C A1
AND with allocation - now 1=allocated good granule.
42BFH
565AH
LD H,4AH 26 4A
Point H to destination GAT buffer at 4A00H.
42C1H
565CH
LD C,(HL) 4E
Fetch destination lockout byte into C.
42C2H
565DH
OR C B1
OR source allocation with destination lockout.
42C3H
565EH
LD L,D 6A
Point HL to destination lockout byte (4A60H + track).
42C4H
565FH
LD (HL),A 77
Update destination lockout table with combined status.
42C5H
5660H
CP C B9
Compare result with destination lockout. Z = track fully locked, skip writing.
42C6H
5661H
POP BC C1
Restore buffer pointer from stack.
42C7H
5662H
If track fully locked out (Z), skip to 433FH to advance to next track without writing.

Track needs to be written. First ensure destination disk is inserted (for single-drive backup).

42CAH
5665H
Call "Swap to Destination Disk" routine. For single-drive backups, prompts user to insert destination disk and verifies it's correct.

Check if this is track 0 (special handling for the GAT sector that has our 76H marker).

42CDH
5668H
LD E,00H 1E 00
Initialize sector counter E to 0.
42CFH
566AH
LD A,D
OR E 7A B3
Test if both D and E are zero (Track 0, Sector 0). The Z-80 can't directly test DE=0, so we OR D with E after loading D into A.
42D1H
566CH
If not Track 0 Sector 0 (NZ), skip the special GAT handling and jump to 42DAH for normal sector write.

Special handling for Track 0, Sector 0 (the GAT sector). We need to restore the original first byte that we replaced with 76H marker.

42D3H
566EH
LD A,(BC) 0A
Fetch the original first byte of the source disk's GAT from the buffer (pointed to by BC).
42D4H
566FH
LD (4390H),A 32 90 43
Save the original GAT first byte at 4390H. This is a self-modifying location - the operand of a LD A,nn at 438FH used later to restore it.
42D7H
5672H
LD A,76H 3E 76
Load A with the 76H marker value again.
42D9H
5674H
LD (BC),A 02
Store the 76H marker as the first byte of the GAT in the buffer. This ensures the destination disk keeps the "backup in progress" marker until the final write.

Top of the sector write loop. Write all 10 sectors of this track, displaying "COPYING TRACK nn, SECTOR nn".

42DAH
5675H
PUSH BC C5
Save the buffer pointer twice - once for the display routine, once for the write.
42DBH
5676H
PUSH BC C5
Save buffer pointer again (second copy on stack).
42DCH
5677H
LD A,D 7A
Copy track number into A for ASCII conversion.
42DDH
5678H
Convert track number to ASCII digits (tens in C, ones in B).
42E0H
567BH
LD (459CH),BC ED 43 9C 45
Store ASCII track digits into the "COPYING TRACK nn" message at offset 459CH.
42E4H
567FH
LD A,E 7B
Copy sector number into A for ASCII conversion.
42E5H
5680H
Convert sector number to ASCII digits (tens in C, ones in B).
42E8H
5683H
LD (45A7H),BC ED 43 A7 45
Store ASCII sector digits into the "COPYING" message at offset 45A7H.
42ECH
5687H
POP BC C1
Restore first copy of buffer pointer.
42EDH
5688H
PUSH DE D5
Save track/sector to stack before display call.
42EEH
5689H
LD HL,458BH 21 8B 45
Point HL to the "COPYING TRACK nn, SECTOR nn" message at 458BH.
42F1H
568CH
Display the "COPYING TRACK nn, SECTOR nn" status message.
42F4H
568FH
POP DE D1
Restore track/sector from stack.

Now build the FDC write command based on the stored FDC status from when we read this track. We extract bits 5-6 (step rate info) and merge with A8H (write sector command).

42F5H
5690H
LD L,D 6A
Set L to track number to index into the FDC status array.
42F6H
5691H
LD H,48H 26 48
Point HL to the FDC status array at 4800H + track. This was stored during the read phase.
42F8H
5693H
LD A,(HL) 7E
Fetch the FDC status that was recorded when this track was read.
42F9H
5694H
AND 60H E6 60
Mask to keep only bits 5 and 6 (01100000). These bits contain drive timing/status information.
42FBH
5696H
RLCA 07
Rotate left 3 times to move bits 5-6 to bits 0-1 (via bit 7 and carry).
42FCH
5697H
RLCA 07
Second rotate left.
42FDH
5698H
RLCA 07
Third rotate left. Original bits 5-6 are now in positions 0-1.
42FEH
5699H
OR A8H F6 A8
OR with A8H (10101000) to form the complete "Write Sector" FDC command. A8H = Write Sector with head load and side 0.
4300H
569BH
Call the Write Sector routine. A=FDC command, BC=buffer (second copy from stack), DE=track/sector.
4303H
569EH
LD HL,461FH 21 1F 46
Point HL to "DESTINATION DISK WRITE ERROR!" message in case write failed.
4306H
56A1H
If write failed (NZ), jump to error handler to display message and HALT.
4309H
56A4H
INC E 1C
Increment sector counter. The write routine advanced BC by 256, so it now points to the next sector's data.
430AH
56A5H
LD A,E 7B
Copy sector number into A for comparison.
430BH
56A6H
CP 0AH FE 0A
Compare against 10. Have we written all 10 sectors (0-9)?
430DH
56A8H
If not all sectors written (NZ), loop back to 42DBH to write the next sector.

All 10 sectors of this track written. Clean up stack and prepare for next track. For VERIFYING phase display.

430FH
56AAH
POP BC C1
Pop the second buffer pointer copy that was saved at 42DAH. BC now points past all 10 sectors we just wrote.
4310H
56ABH
LD E,00H 1E 00
Reset sector counter to 0 for the VERIFY read-back loop.

Top of the VERIFY loop. Read back each sector to verify the write was successful. Display "VERIFYING TRACK nn, SECTOR nn".

4312H
56ADH
PUSH BC C5
Save buffer pointer to stack.
4313H
56AEH
LD A,D 7A
Copy track number into A for ASCII conversion.
4314H
56AFH
Convert track number to ASCII digits.
4317H
56B2H
LD (45BBH),BC ED 43 BB 45
Store ASCII track digits into the "VERIFYING TRACK nn" message at offset 45BBH.
431BH
56B6H
LD A,E 7B
Copy sector number into A for ASCII conversion.
431CH
56B7H
Convert sector number to ASCII digits.
431FH
56BAH
LD (45C6H),BC ED 43 C6 45
Store ASCII sector digits into the "VERIFYING" message at offset 45C6H.
4323H
56BEH
POP BC C1
Restore buffer pointer from stack.
4324H
56BFH
PUSH DE D5
Save track/sector before display call.
4325H
56C0H
LD HL,45AAH 21 AA 45
Point HL to the "VERIFYING TRACK nn, SECTOR nn" message at 45AAH.
4328H
56C3H
Display the "VERIFYING TRACK nn, SECTOR nn" status message.
432BH
56C6H
POP DE D1
Restore track/sector from stack.
432CH
56C7H
Call Disk I/O Executive to READ the sector back for verification. D=track, E=sector, BC=buffer.
432FH
56CAH
LD HL,4601H 210146
Point HL to "DESTINATION DISK READ ERROR!" message (verify is a read operation).
4332H
56CDH
If verify read failed (NZ), jump to error handler to display message and HALT.
4335H
56D0H
INC E 1C
Increment sector counter for next verify iteration.
4336H
56D1H
LD A,E 7B
Copy sector number into A for comparison.
4337H
56D2H
CP 0AH FE 0A
Compare against 10. Have we verified all 10 sectors?
4339H
56D4H
If not all sectors verified (NZ), loop back to 4312H to verify the next sector.

All sectors of this track verified. Decrement tracks-loaded counter and advance to next track.

433BH
56D6H
LD HL,4714H 21 14 47
Point HL to the "tracks loaded" counter.
433EH
56D9H
DEC (HL) 35
Decrement the tracks-loaded counter. One track has been written from the buffer.
433FH
56DAH
INC D 14
Increment track counter D.
4340H
56DBH
LD A,D 7A
Copy track number into A for comparison.
4341H
56DCH
CP 23H FE 23
Compare against 35. Have we processed all tracks?
4343H
56DEH
If all 35 tracks processed (Z), jump to 434FH to finish the backup (copy date, write final GAT).

Not all tracks done. For single-drive backup, check if buffer is empty (need to read more).

4345H
56E0H
LD A,(4714H) 3A 14 47
Fetch the tracks-loaded counter.
4348H
56E3H
OR A B7
Set flags. Z = buffer is empty (no more buffered tracks to write).
4349H
56E4H
If buffer still has tracks (NZ), loop back to 42B3H to write the next buffered track.
434CH
56E7H
Buffer is empty - jump back to 4234H to swap to source disk and load more tracks. The single-drive backup cycle continues until all 35 tracks are copied.

434FH - Finish Phase: Finalize Backup and Restore Boot Sector

Entry point after all tracks have been copied. The destination disk still has the 76H "backup in progress" marker at both the GAT (directory track sector 0) byte 0 and the BOOT sector (Track 0, Sector 0) byte 0. The original BOOT byte 0 was saved at self-modifying location 4390H during the copy phase. Now we finalize the backup by: (1) writing the backup date to the GAT, (2) writing the final GAT to the destination, and (3) restoring the original BOOT sector byte 0.

Memory Locations Referenced

AddressContentsPurpose
470AH8-byte dateUser-entered backup date (MM/DD/YY format + 2 spaces)
9D8HGAT+D8HDate field within source GAT buffer
4900H256 bytesSource GAT buffer (allocation, lockout, Pack ID, date)
4B00H256 bytesGeneral purpose sector buffer
4709H1 byteDirectory track number (typically 11H/17 decimal)
4390H1 byteSaved original BOOT sector byte 0 (self-modifying)
37ECHFDC Status/CommandWD1771 command/status register
37EDHFDC TrackFDC track register
37EEHFDC SectorFDC sector register
37EFHFDC DataFDC data register
3840HKeyboard Row 7ENTER, CLEAR, BREAK, arrows, SPACE
434FH
56EAH
LD HL,470AH 21 0A 47
Point HL to the 8-byte backup date buffer at 470AH. This contains the user-entered date in MM/DD/YY format (or the system date if none was entered). The date was validated earlier during program initialization.
4352H
56EDH
LD DE,49D8H 11 D8 49
Point DE to offset D8H within the source GAT buffer (4900H + D8H = 49D8H). In the GAT structure, offset D8H-DFH is the date field where the backup date is stored.
4355H
56F0H
LD BC,0008H 01 08 00
Set byte count to 8 (the length of the date field: MM/DD/YY).
4358H
56F3H
LDIR ED B0
Block transfer: Copy 8 bytes from (HL) to (DE). This transfers the backup date from the user input buffer (470AH-4711H) to the GAT buffer's date field (49D8H-49DFH). The source GAT buffer now contains the complete backup metadata: allocation table, lockout table, Pack ID, and the backup date.

The source GAT buffer at 4900H now has the backup date embedded. Next we write this complete GAT to the destination disk's directory track.

435AH
56F5H
Call the destination disk swap routine. This prompts for the destination disk if needed (single-drive backup), selects the destination drive, and verifies that the 76H marker is present (confirming this is the correct backup-in-progress disk).
435DH
56F8H
LD A,(4709H) 3A 09 47
Fetch the directory track number from 4709H into A. This is typically 11H (17 decimal) for TRSDOS 2.3, but was read from the source disk's GAT earlier to support non-standard disk formats.
4360H
56FBH
LD D,A 57
D = directory track number. This will be the track parameter for the sector write.
4361H
56FCH
LD E,00H 1E 00
E = sector 0. The GAT is always sector 0 of the directory track. DE now contains the full address: directory track, sector 0.
4363H
56FEH
LD BC,4900H 01 00 49
BC = source GAT buffer address. This 256-byte buffer contains the complete GAT with: track allocation (bytes 00-22H), lockout table (bytes 60-82H), Pack ID (bytes CE-D7H), and now the backup date (bytes D8-DFH).
4366H
5701H
LD A,A9H 3E A9
A = FDC Write Sector command with verify (A9H = 1010 1001). Bit 7-5: 101 = Write Sector command. Bit 3: 1 = enable side select compare. Bit 0: 1 = write Data Address Mark FB (normal data).
4368H
5703H
Call the Write Sector subroutine. This writes the source GAT buffer (BC=4900H) to the destination disk at directory track sector 0 (DE). The routine returns Z flag set if successful, NZ if error occurred.
436BH
5706H
LD HL,461FH 21 1F 46
Preload HL with address of error message: "DESTINATION DISK WRITE ERROR!" This is loaded before testing the result so the message is ready if needed.
436EH
5709H
If write failed (NZ), jump to the error handler at 43A3H which displays the message pointed to by HL, prompts "HIT ENTER TO CONTINUE", waits for ENTER, and HALTs.

GAT write successful. Now verify by reading it back.

4371H
570CH
LD BC,4B00H 01 00 4B
BC = general purpose buffer address for read verification. We read the sector back into a different buffer to verify the write succeeded.
4374H
570FH
Call the Disk I/O Executive to read the sector. DE still contains (directory track, sector 0) from the write operation. This reads the GAT we just wrote back into buffer 4B00H.
4377H
5712H
LD HL,4601H 21 01 46
Preload HL with address of error message: "DESTINATION DISK READ ERROR!"
437AH
5715H
If read-back failed (NZ), jump to error handler. The GAT write cannot be verified, so the backup may be corrupt.

GAT verified. Now we must restore the BOOT sector (Track 0, Sector 0). During the copy phase, we wrote 76H to byte 0 as a "backup in progress" marker and saved the original value at 4390H.

437DH
5718H
LD BC,4B00H 01 00 4B
BC = buffer address for reading the BOOT sector.
4380H
571BH
LD DE,0000H 11 00 00
DE = Track 0, Sector 0. This is the BOOT sector (BOOT/SYS) - the first sector on the disk that the ROM loads during boot.
4383H
571EH
Read the BOOT sector into buffer 4B00H. This sector currently has 76H at byte 0 (the backup-in-progress marker we wrote earlier).
4386H
5721H
LD HL,4601H 21 01 46
Preload error message: "DESTINATION DISK READ ERROR!"
4389H
5724H
If read failed, jump to error handler. We cannot restore the BOOT sector.

BOOT sector read into buffer at 4B00H. Byte 0 of the buffer contains 76H. Now restore the original byte 0 value that was saved during the copy phase.

438CH
5727H
LD BC,4B00H 01 00 4B
BC points to byte 0 of the buffer (the first byte of the BOOT sector).
438FH
572AH
LD A,00H 3E 00
The operand at 4390H was modified during the copy phase (at 42E3H-42EFH). It contains the original byte 0 from the source disk's BOOT sector. The 00H shown here is just the initial value; at runtime it holds the actual original byte.
4391H
572CH
LD (BC),A 02
Store the original byte 0 value into the buffer, overwriting the 76H backup-in-progress marker. The buffer now contains the correct BOOT sector data.
4392H
572DH
LD A,A8H 3E A8
A = FDC Write Sector command (A8H = 1010 1000). Similar to A9H but with Data Address Mark selection = 0.
4394H
572FH
Call Write Sector subroutine. Write the corrected BOOT sector (BC=4B00H) back to Track 0, Sector 0 (DE=0000H). The 76H marker is now replaced with the original byte.
4397H
5732H
LD HL,461FH 21 1F 46
Preload error message: "DESTINATION DISK WRITE ERROR!"
439AH
5735H
If write failed, jump to error handler. The destination disk will still have the 76H marker and will be recognized as an incomplete backup.

Backup is now complete! The destination disk has: all data tracks copied, the GAT with backup date, and the BOOT sector restored. Now display success message and terminate.

439DH
5738H
Send RESTORE command to the FDC to position the head at Track 0, and wait for the controller to become ready. This is a clean-up step before displaying the completion message.
43A0H
573BH
LD HL,46DEH 21 DE 46
Point HL to the success message: 0AH + "BACKUP COMPLETE" + 0DH (with line feed prefix and carriage return suffix).

43A3H - Message Display, ENTER Prompt, and HALT (Also Error Handler Entry)

This routine serves dual purpose: (1) Error handler entry point - jumped to after loading error message into HL, (2) Normal completion - falls through from 43A0H with "BACKUP COMPLETE" message. Either way, it displays the message in HL, prompts for ENTER, waits for keypress, then HALTs.

43A3H
573EH
Call the message display routine. Displays the null-terminated message pointed to by HL. For success: "BACKUP COMPLETE". For errors: the error message loaded before jumping here. HL is preserved.
43A6H
5741H
LD HL,46EFH 21 EF 46
Point HL to prompt message: 0AH + 0AH + "HIT 'ENTER' TO CONTINUE" + 0DH. The double 0AH provides blank line separation.
43A9H
5744H
Display the "HIT ENTER TO CONTINUE" prompt.

Now poll the keyboard waiting for the ENTER key. The TRS-80 keyboard is memory-mapped with each row at a specific address. Row 7 at 3840H contains ENTER (bit 0), CLEAR, BREAK, and arrow keys.

43ACH
5747H
LD A,(3840H) 3A 40 38
Read keyboard row 7 (address 3840H). Bit 0 = ENTER key (1 when pressed, 0 when not pressed). Other bits: Bit 1=CLEAR, Bit 2=BREAK, Bit 3=UP, Bit 4=DOWN, Bit 5=LEFT, Bit 6=RIGHT, Bit 7=SPACE.
43AFH
574AH
RRCA 0F
Rotate A right: Bit 0 (ENTER) shifts into the Carry flag, while Bit 7 receives Carry. After this: Carry=1 if ENTER pressed, Carry=0 if not pressed.
43B0H
574BH
If No Carry (ENTER not pressed), jump back to 43ACH to continue polling. This is a tight loop: read keyboard, test ENTER, repeat until pressed. Offset FAH = -6 bytes, target = 43B0H + 2 - 6 = 43ACH.
43B2H
574DH
HALT 76
Stop CPU execution. The backup operation is complete (or terminated due to error). The system must be reset to continue. Note: The HALT opcode is 76H, the same value used as the "backup in progress" marker!

43B3H - Write Sector Subroutine (Entry Point)

Low-level sector write routine. Called from multiple places in the backup program to write sectors to disk.
A = FDC write command byte, BC = buffer address (256 bytes), DE = track (D) and sector (E).
Z flag set if successful, NZ if error. HL = buffer address (from BC).

43B3H
574EH
LD (43D4H),A 32 D4 43
Store the FDC command byte at address 43D4H. This location is the operand of a later instruction that will send the command to the FDC. Different callers use different command bytes (A8H, A9H) for various write modes.
43B6H
5751H
PUSH BC C5
Save the buffer address (BC) on the stack. Will be retrieved into HL after the write attempt.
43B7H
5752H
Call the FDC write sequence at 43BEH. This performs the actual disk write: programs FDC registers, issues seek command, transfers data. Returns Z flag indicating success/failure.
43BAH
5755H
POP HL E1
Retrieve the buffer address from stack into HL. (Originally in BC, now in HL for the caller's convenience or for retry logic.)
43BBH
5756H
RET Z C8
If Z flag is set (write succeeded), return to caller. HL contains the buffer address. The caller can continue with verification or the next operation.

43BCH - Write Sector Retry / FDC Write Sequence

If the write at 43B7H failed (returned NZ), execution falls through here. This copies the buffer address from HL back to BC and falls into 43BEH to retry the write operation.

43BCH
5757H
LD B,H 44
Copy H to B (high byte of buffer address).
43BDH
5758H
LD C,L 4D
Copy L to C (low byte of buffer address). BC now equals HL (buffer address restored to BC for retry). Falls through to 43BEH to attempt the write again.

FDC Write Sequence begins here. Programs the FDC registers, seeks to the target track, and prepares for data transfer.

43BEH
5759H
LD (37EEH),DE ED 53 EE 37
Store DE to FDC sector register (37EEH). Note: This 16-bit store writes E to 37EEH (sector register) and D to 37EFH (data register). The data register write is harmless; the important thing is getting E (sector number) into the sector register.
43C2H
575DH
PUSH DE D5
Save track/sector values on stack for later use.
43C3H
575EH
Call drive select and wait routine: (1) Poll FDC status, (2) Fetch drive code from 44E8H, (3) Write to drive select latch, (4) If drive not ready, wait up to 1 second, (5) Return when ready.
43C6H
5761H
LD HL,37ECH 21 EC 37
HL = FDC command/status register address (37ECH). This register is used to send commands and read status from the WD1771 controller.
43C9H
5764H
LD (HL),1BH 36 1B
Send SEEK command (1BH = 0001 1011) to FDC. Bits 7-4: 0001 = Seek command. Bit 3 (h): 1 = enable head load delay. Bit 2 (V): 0 = no track verify. Bits 1-0 (r): 11 = 15ms stepping rate (slowest, most reliable). The FDC will seek to the track number in its track register.
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Function Description
0001hVr1r0
00011011Command=Seek (1BH)
Bit 7-4: Command Code (0001 = Seek)
h=1: Enable Head Load and 15ms settle delay
V=0: No destination track verification
r1,r0=11: 15ms stepping motor rate (slowest)

After issuing the SEEK command, we must wait for the FDC to complete the operation. The delay sequence below allows the command to process before we check status.

43CBH
5766H
PUSH AF F5
Delay instruction 1: Push AF to waste time (11 T-states).
43CCH
5767H
POP AF F1
Delay instruction 2: Pop AF back (10 T-states).
43CDH
5768H
PUSH AF F5
Delay instruction 3: Push AF again (11 T-states).
43CEH
5769H
POP AF F1
Delay instruction 4: Pop AF back (10 T-states). Total delay: ~42 T-states ≈ 24 microseconds at 1.77 MHz. This gives the FDC time to begin processing the seek.

Now poll the FDC status register waiting for the BUSY bit (bit 0) to clear, indicating the seek is complete.

43CFH
576AH
LD A,(HL) 7E
Read FDC status register (HL = 37ECH). Bit 0 = BUSY flag (1 = controller busy, 0 = ready).
43D0H
576BH
RRCA 0F
Rotate A right: Bit 0 (BUSY) moves to Carry flag. Carry=1 means FDC is still busy.
43D1H
576CH
If Carry set (FDC busy), loop back to 43CFH and keep polling. Offset FCH = -4 bytes, target = 43D1H + 2 - 4 = 43CFH. This tight loop waits until the seek completes.

Seek complete. Now clear any pending FDC state and prepare for the data transfer phase.

43D3H
576EH
LD (HL),00H 36 00
Write 00H to FDC command register. This is a "Force Interrupt" command (Type IV) that terminates any pending command and resets the FDC's internal state machine. Ensures clean state before write operation.
43D5H
5770H
LD DE,37EFH 11 EF 37
DE = FDC data register address (37EFH). This will be the destination for the data transfer loop that follows. Data bytes from the buffer will be written here one at a time.

Another delay sequence to let the Force Interrupt command complete.

43D8H
5773H
PUSH BC C5
Delay instruction 1: Push BC (11 T-states). Also preserves BC (buffer address) on stack.
43D9H
5774H
POP BC C1
Delay instruction 2: Pop BC back (10 T-states).
43DAH
5775H
PUSH BC C5
Delay instruction 3: Push BC again (11 T-states).
43DBH
5776H
POP BC C1
Delay instruction 4: Pop BC back (10 T-states). BC still contains the buffer address.

Now entering the critical data transfer section. Interrupts must be disabled to ensure we don't miss the FDC's DRQ (Data Request) signals.

43DCH
5777H
DI F3
Disable interrupts. Critical: The write data transfer is timing-sensitive. Missing even one DRQ signal will cause data corruption. Interrupts must stay disabled until all 256 bytes are transferred.
43DDH
5778H
Jump forward to 43E2H to enter the data transfer loop. Offset 03H → target = 43DDH + 2 + 3 = 43E2H. The transfer loop sends the write command and pumps 256 bytes to the FDC data register.

43DFH - Write Sector Data Transfer Loop

This is the critical tight loop that transfers 256 bytes from RAM to the FDC during a sector write. HL=37ECH (status), BC=buffer, DE=37EFH (data reg). Interrupts are disabled. The loop polls DRQ (bit 1) to send each byte, and checks BUSY (bit 0) to detect completion.

43DFH
577AH
RRCA 0F
Check for BUSY by rotatin FDC status right, moving Bit 0 (BUSY flag) into Carry. If BUSY=1, the FDC is still processing and we should keep polling. If BUSY=0, the transfer is complete.
43E0H
577BH
If No Carry (BUSY=0), the FDC has finished the sector write. Exit the routine via a jump to 43EDH to read final status and check for errors.
43E2H
577DH
LD A,(HL) 7E
Main entry point (from 43DDH and 43EAH). Read FDC status register (HL=37ECH) into A to check if controller is ready for next data byte.
43E3H
577EH
BIT 1,A CB 4F
est Bit 1 (Data Request). DRQ=1 means FDC is ready to accept a byte. DRQ=0 means either still processing or transfer complete.
43E5H
5780H
We need to wait for a DRQ. If Z flag (DRQ=0), FDC not ready. Jump to 43DFH to check BUSY flag. Loop: check DRQ → check BUSY → repeat until either DRQ set or BUSY clear.
43E7H
5782H
LD A,(BC) 0A
If we are here we have the DRQ, so we need to get the data byte. Fetch the next byte from the RAM buffer pointed to by BC.
43E8H
5783H
LD (DE),A 12
Write the data byte to the FDC data register (DE=37EFH). The byte will be written to disk and DRQ will clear.
43E9H
5784H
INC BC 03
Point BC to the next byte in the buffer. After 256 iterations, one full sector has been transferred.
43EAH
5785H
Jump back to poll FDC status and transfer next byte. Uses JP (10 T-states) for predictable timing in this critical loop.

43EDH - Write Completion and Error Check

The write data transfer loop has completed (FDC BUSY=0). Now check the final status for any errors. Returns Z=success, NZ=error.

43EDH
5788H
LD A,(HL) 7E
Read the FDC status register (HL=37ECH). This contains the result of the write operation including any error flags.
43EEH
5789H
AND 7CH E6 7C
AND with 0111 1100 to isolate write error flags: Bit 6 (Write Protect), Bit 5 (Write Fault), Bit 4 (Record Not Found), Bit 3 (CRC Error), Bit 2 (Lost Data). Result=0 means success.
43F0H
578BH
POP DE D1
Restore original track (D) and sector (E) values from the stack (pushed at 454BH before write).
43F1H
578CH
RET Z C8
If Z flag set (no error bits), the write succeeded. Return to caller with Z flag indicating success.

43F2H - Write Error Recovery

Error detected during write. Issue Force Interrupt to reset FDC, then return with NZ flag.

43F2H
578DH
LD (HL),D0H 36 D0
A write error occurred! Issue Force Interrupt command (D0H = 1101 0000) to the FDC to immediately terminate any pending operation and reset the controller state.
43F4H
578FH
RET C9
Return to caller with NZ flag set (from AND result) indicating write failure. Error bits remain in A for caller to examine.

43F5H - Format/Verify Error Handler - Mark Track as Bad

Called when a sector write or verify fails during formatting. Displays the error message, marks the entire track as bad in the destination lockout table (4A00H+track), and continues to the next track.

43F5H
5790H
PUSH DE D5
Preserve DE (D=track number) before calling display routine. Need track number to mark it as bad.
43F6H
5791H
Display the error message pointed to by HL (e.g., "NOT FOUND! TRACK LOCKED OUT!" or "CRC ERROR! TRACK LOCKED OUT!"). HL is preserved.
43F9H
5794H
POP DE D1
Get track number back into D. Need this to calculate lockout table offset.
43FAH
5795H
LD L,D 6A
We need to build the address. Copy track number to L. Destination lockout table is at 4A00H, so address = 4A00H + track.
43FBH
5796H
LD H,4AH 26 4A
Set H=4AH. HL now = 4A00H + track = address in destination lockout table buffer.
43FDH
5798H
LD (HL),FFH 36 FF
Mark the track bad by writing FFH to the lockout table. FFH = all 5 granules (10 sectors) locked out. This track will be skipped during backup.
43FFH
579AH
Jump to 553CH to increment track counter and continue format verification loop with next track.

4402H - SUBROUTINE to Set Register C to the ASCII Number corresponding to Register A (TENS Digit in ASCII to B).

4402H
579DH
LD C,30H 0E 30
Let Register C equal 30H (ASCII: 0).
4404H
579FH
SUB 0AH D6 0A
SUBtract the value 0AH (Decimal: 10) from Register A. The Carry Flag will be affected.
4406H
57A1H
If the C FLAG (Carry) has been set, then the subtraction of 10 from A went negative, so JUMP to 57A6H to process the ONES digit into B.
4408H
57A3H
INC C 0C
INCrement the value stored in Register C by 1 since the subtraction didn't go negative.
4409H
57A4H
JUMP to 579FH to keep subtracting 10 and bumping Register C.

440BH - SUBROUTINE to Set Register B to the ASCII Number corresponding to Register A (Ones Digit in ASCII to B).

440BH
57A6H
ADD 3AH C6 3A
At this point A is negative, so LET Register A = Register A + 3AH to turn Register A into the ASCII Equivalent of the Ones Digit.
440DH
57A8H
LD B,A 47
Copy the contents of Register A into Register B.
440EH
57A9H
RET C9
RETurn to the caller.

440FH - Part of the FORMATTING routine. Set C = FFH and then do 'put the value held in Register C into (HL) and then BUMP HL by 1' Register B times

440FH
57AAH
LD C,FFH 0E FF
Let Register C equal FFH (Decimal: 255).
4411H
57ACH
GOSUB to 4417H to store the value held in Register C into (HL) and then BUMP HL by 1
4414H
57AFH
LOOP back to 57ACH, reducing Register B each time, and continue to LOOP until Register B has been reduced to ZERO, in which case, continue with the next instruction.
4416H
57B1H
RET C9
RETurn to the caller.

4417H - Part of the FORMATTING routine. Put C into (HL) and then BUMP HL by 1

4417H
57B2H
LD (HL),C 71
Store the value held in Register C into the memory location pointed to by Register Pair HL.
4418H
57B3H
INC HL 23
INCrement the value stored in Register Pair HL by 1.
4419H
57B4H
RET C9
RETurn to the caller.

441AH - Subroutine to send a RESTORE/LOAD HEAD to the Floppy Drive and then keep checking the floppy drive controller status until the drive is not BUSY

441AH
57B5H
LD A,0BH 3E 0B
Let Register A equal 0BH (Binary:0000 1011) to represent RESTORE (0000), Load Head at the Beginning (1), No Verify (0), 30 ms stepping rate (11).
441CH
57B7H
LD (37ECH),A 32 EC 37
Send the command held in Register A to the FDC (37ECH is the Floppy Disk Controller's Command Register).
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Function Description
0000hVr1r0Command=Restore
Bit 7-4: Command Code (0000)
h: 1=Enable Head Load/Settle, 0=No delay
V: 1=Verify Track 0 ID, 0=No verification
r1, r0: Stepping Motor Rate (00=3ms, 01=6ms, 10=10ms, 11=15ms)

Standard code to delay to compensate for the slow FDC to make sure it had time to set the command.

441FH
57BAH
PUSH AF F5
Save the contents of Register Pair AF to the top of the stack.
4420H
57BBH
POP AF F1
Put the value held at the top of the STACK into Register Pair AF, and then remove the entry from the stack.
4421H
57BCH
PUSH AF F5
Save the contents of Register Pair AF to the top of the stack.
4422H
57BDH
POP AF F1
Put the value held at the top of the STACK into Register Pair AF, and then remove the entry from the stack.

Standard code to delay to compensate for the slow FDC to make sure it had time to set the command.

4423H
57BEH
LD A,(37ECH) 3A EC 37
Fetch the FDC's status (which is mirrored to 37ECH) and store it into Register A.
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
4426H
57C1H
RRCA 0F
Test Bit 0 (i.e., "BUSY") by first rotating the bits in A right (i.e., higher bit moves lower), with Bit 0 being copied to both the CARRY FLAG and Bit 7.
4427H
57C2H
If the C FLAG (Carry), which used to be Bit 0, has been set, then the drive is BUSY, so LOOP BACK to 57BEH to send the command again.
4429H
57C4H
RET C9
RETurn to the caller.

442AH - Disk Insertion Prompt. Turn on the high bit in Register A, prompt the user with the message stored in HL, and return once the user hits ENTER.

442AH
57C5H
LD A,nnH 3E 00
Let Register A equal nnH. This is where the SOURCE DRIVE has been saved in the format of 01, 02, 04, or 08 (which are drive select codes for the drive select latch).
442CH
57C7H
OR 80H F6 80
The value in A is logically OR'd with 80H (1000 0000 binary). This will set up an operation code (like clearing a specific directory bit or activating an extended function) before calling a system handler.
442EH
57C9H
PUSH HL E5
Save the contents of Register Pair HL to the top of the stack so that HL can be used in the next instruction to display a message.
442FH
57CAH
LD HL,4664H 21 64 46
Let Register Pair HL equal 4664H to point to the message 1D + 'INSERT SOURCE DISK (ENTER)' + 1D + 03H.
4432H
57CDH
GOSUB to 44E7H to displays the message pointed to by HL and wait for the user to press the ENTER key before returning.
4435H
57D0H
POP HL E1
Put the value held at the top of the STACK into Register Pair HL, and then remove the entry from the stack.
4436H
57D1H
RET C9
RETurn to the caller.

4437H - SECTION TITLE. CALLED from 4432H above with HL pointing to the message 1D + 'INSERT SOURCE DISK (ENTER)' + 1D + 03H

4437H
57D2H
LD A,(44E8H) 3A E8 44
Fetch the value held in memory location 44E8H, which is the nn in a CP nn opcode in the 44E7H routine, and store it into Register A. This was a copy of the Floppy Disk Controller status register.
443AH
57D5H
BIT 7,A CB 7F
Test Bit Number 7 of Register A, which is currently holding the Floppy Drive Controller status. This then tests a drive status of NOT READY. Z FLAG will be set if that bit is 0 (meaning the drive is ready), and NZ FLAG will be set if that bit is 1 (meaning the drive is NOT ready).
443CH
57D7H
If the NZ FLAG (Not Zero) has been set, JUMP to 57C5H which is the DISKETTE NOT READY routine (the error handling branch for the 4437H routine) ending ina message and a HALT.
443EH
57D9H
If the drive IS READY, execution proceeds to call the subroutine at 442AH (a disk I/O preparation/setup routine).
4441H
57DCH
LD A,(44EFH) 3A EF 44
Load the Accumulator (A) with a status byte stored at memory location 44EFH. This value is likely the return code or status from the 442AH call
4444H
57DFH
OR A B7
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A (and, in all events, clear the CARRY FLAG).
4445H
57E0H
RET NZ C0
. If the status byte loaded from 44EFH was NZ, then we had an error from the 442AH routine, so RETurn to the calling routine.

If we are here, then we had success on the test, and will move forward to read a block of data from a disk and performing a disk change check

4446H
57E1H
GOSUB to 441AH which issues the FDC RESTORE command (move head to Track 0) and polls the FDC status until the drive is not BUSY. This initializes the disk head position.
4449H
57E4H
PUSH BC C5
Save the contents of Register Pair BC to the top of the stack.
444AH
57E5H
PUSH DE D5
Save the contents of Register Pair DE to the top of the stack.
444BH
57E6H
PUSH HL E5
Save the contents of Register Pair HL to the top of the stack.
444CH
57E7H
LD BC,4B00H 01 00 4B
Sets BC (the Buffer Pointer) to 4B00H. This is the destination memory address where the disk data will be read into.
444FH
57EAH
LD DE,0000H 11 00 00
Sets DE to 0000H. This is the Track/Sector address to be read.
4452H
57EDH
GOSUB to 453CH (Disk I/O Executive) which performs the drive selection, seeks to the track specified by DE, issues the Read command, and runs the data transfer loop to fill the buffer pointed to by BC.
4455H
57F0H
LD HL,45E8H 21 E8 45
Let Register Pair HL equal 45E8H to point to the message 0A + 'SOURCE DISK READ ERROR!' + 0DH.
4458H
57F3H
If the NZ (Not Zero) flag is set (signaling an error from 453CH), the routine jumps to 43A3H to display the error and halt the system.
445BH
57F6H
POP HL E1
Put the value held at the top of the STACK into Register Pair HL, and then remove the entry from the stack.
445CH
57F7H
POP DE D1
Put the value held at the top of the STACK into Register Pair DE, and then remove the entry from the stack.
445DH
57F8H
POP BC C1
Put the value held at the top of the STACK into Register Pair BC, and then remove the entry from the stack.
445EH
57F9H
LD A,(4B00H) 3A 00 4B
Reads the first byte of the buffer into A. Since this is being tested for 0 or NZ, it is likely that this byte (4B00H) is a flag or counter related to the disk.
4461H
57FCH
OR A B7
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A (and, in all events, clear the CARRY FLAG).
4462H
57FDH
If the byte at 4B00H is not zero, it jumps to 5831H (4496H's XOR A) which clears a flag and redirects flow to the disk-present prompt loop (57D2H).

This block is skipped if the jump at 4462H is taken. If execution continues here, it performs a second read operation into the same buffer, possibly to read a different initial sector or as a retry mechanism, and includes its own error check.

4464H
57FFH
PUSH BC C5
Save the contents of Register Pair BC to the top of the stack.
4465H
5800H
PUSH DE D5
Save the contents of Register Pair DE to the top of the stack.
4466H
5801H
PUSH HL E5
Save the contents of Register Pair HL to the top of the stack.
4467H
5802H
LD A,(4709H) 3A 09 47
Load Track/Sector (High): Fetches a value from a memory location (4709H) into A. This value is likely the Track number or another high-byte component of the sector address.
446AH
5805H
LD D,A 57
Copies the value (Track/High-byte) into D.
446BH
5806H
LD E,00H 1E 00
Sets the low byte of the address, E, to 00H. DE is now loaded with a new Track/Sector address (e.g., Track X, Sector 0).
446DH
5808H
LD BC,4B00H 01 00 4B
Sets BC (the Buffer Pointer) back to 4B00H. The data will be read into the same buffer.
4470H
580BH
GOSUB to 453CH (Disk I/O Executive) which performs the drive selection, seeks to the track specified by DE, issues the Read command, and runs the data transfer loop to fill the buffer pointed to by BC.
4473H
580EH
LD HL,45E8H 21 E8 45
Let Register Pair HL equal 45E8H to point to the message 0A + 'SOURCE DISK READ ERROR!' + 0DH.
4476H
5811H
If the NZ flag is set (signaling an error from 453CH), the routine jumps to 43A3H to display the error and HALT.
4479H
5814H
LD HL,49CEH 21 CE 49
Sets HL to point to the Stored Reference Disk ID (49CEH).
447CH
5817H
LD DE,4BCEH 11 CE 4B
Sets DE to point to the Disk ID in the new buffer (4B00H + an offset).
447FH
581AH
LD B,0AH 06 0A
Sets B as the counter for 10 bytes to compare.
4481H
581CH
LD A,(DE) 1A
Loads a byte from the new buffer into A..
4482H
581DH
CP (HL) BE
Compares the new byte in A against the reference byte in memory pointed to by HL
4483H
581EH
If the bytes are Not Zero (they don't match), it jumps to 5828H (448DH) to display the "WAKE UP!!! THAT'S NOT THE SAME SOURCE DISK!" error and restart the disk prompt process.
4485H
5820H
INC DE 13
INCrement pointer in Pair DE by 1.
4486H
5821H
INC HL 23
INCrement pointer in Register Pair HL by 1.
4487H
5822H
LOOP back to 581CH to keep comparing the 10 bytes, reducing Register B each time, and continue to LOOP until Register B has been reduced to ZERO, in which case, continue with the next instruction.

If we are here, then all 10 bytes matched and we are at a successful exit.

4489H
5824H
POP HL E1
Put the value held at the top of the STACK into Register Pair HL, and then remove the entry from the stack.
448AH
5825H
POP DE D1
Put the value held at the top of the STACK into Register Pair DE, and then remove the entry from the stack.
448BH
5826H
POP BC C1
Put the value held at the top of the STACK into Register Pair BC, and then remove the entry from the stack.
448CH
5827H
RET C9
RETurn to the caller.

448DH - Display "WAKE UP!!!" Error, Pop BC/DE/HL, Clear (44E8H), and JUMP to 4437H to Restart Source Disk Prompt Loop

448DH
5828H
LD HL,46A7H 21 A7 46
Let Register Pair HL equal 46A7H to point to the message 1D + 'WAKE UP!!! THAT'S NOT THE SAME SOURCE DISK!' + 0DH
4490H
582BH
GOSUB to 457BH to display the message pointed to by HL on the screen, and RETurn. HL is not affected..
4493H
582EH
POP HL E1
Put the value held at the top of the STACK into Register Pair HL, and then remove the entry from the stack.
4494H
582FH
POP DE D1
Put the value held at the top of the STACK into Register Pair DE, and then remove the entry from the stack.
4495H
5830H
POP BC C1
Put the value held at the top of the STACK into Register Pair BC, and then remove the entry from the stack.
4496H
5831H
XOR A AF
Set Register A to ZERO and clear all Flags.
4497H
5832H
LD (44E8H),A 32 E8 44
Store a ZERO into memory location 44E8H which is the nn in a CP nn opcode in the 44E7H routine.
449AH
5835H
Jump back to 4437H (original address 57D2H) to restart the "Verify Source Disk" routine from the beginning. Since 44E8H was just cleared to 0 at 4497H, the smart prompt logic at 44E7H will NOT skip the prompt - the user will see "INSERT SOURCE DISK (ENTER)" again. This creates the retry loop: wrong disk → display error → clear flag → prompt again → verify Pack ID → repeat until correct disk inserted.

449CH - Turn on Bit 6 in A, prompt for the Destination Disk and make sure its not write protected, and RETurn

449CH
5837H
LD A,nnH3E 00
Load destination drive select code from self-modified operand at 449DH. Contains 01H, 02H, 04H, or 08H for drives 0-3.
449EH
5839H
OR 40H F6 40
Set bit 6 to mark this as a destination disk operation. This bit distinguishes dest from source in the smart prompt logic (44E7H). A now = drive_code OR 40H.
44A0H
583BH
PUSH HL E5
Save the contents of Register Pair HL to the top of the stack.
44A1H
583CH
LD HL,4683H 21 83 46
Let Register Pair HL equal 4683H to point to the message 1D + 'INSERT DESTINATION DISK (ENTER)' + 1DH + 03H.
44A4H
583FH
GOSUB to 44E7H to displays the message pointed to by HL and wait for the user to press the ENTER key before returning.
44A7H
5842H
LD A,(37ECH) 3A EC 37
Fetch the FDC's status (which is mirrored to 37ECH) and store it into Register A.
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
44AAH
5845H
LD HL,463EH 21 3E 46
Let Register Pair HL equal 463EH to point to the message 0A + 'DESTINATION DISK IS WRITE PROTECTED!' + 0DH.
44ADH
5848H
BIT 6,A CB 77
Test Bit Number 6 of Register A to see if the FDC returned a PROTECTED error code. Z FLAG will be set if that bit is 0 (=NOT write protected), and NZ FLAG will be set if that bit is 1 (=Write protected).
44AFH
584AH
If the NZ FLAG (Not Zero) has been set, then the disk is not Write Protected, JUMP to 43A3H to Display the message pointed to by HL and prompt for ENTER; keep polling the keyboard until ENTER, and then HALT.
44B2H
584DH
POP HL E1
Put the value held at the top of the STACK into Register Pair HL, and then remove the entry from the stack.
44B3H
584EH
RET C9
RETurn to the caller.

44B4H - Swap to Destination Disk Routine

Ensures the correct destination disk is inserted. For two-drive backup, just returns. For single-drive backup, reads the GAT and verifies the 76H backup-in-progress marker. If wrong disk, forces re-prompt.

44B4H
584FH
LD A,(44E8H) 3A E8 44
Load the drive code from last prompt. 44E8H is self-modifying storage containing drive code with bit 6 set if destination.
44B7H
5852H
BIT 6,A CB 77
Test bit 6. If set, we already prompted for destination disk and it should still be inserted.
44B9H
5854H
If NZ (bit 6 set), destination already inserted. Jump to 449CH to re-select drive without re-prompting user.
44BBH
5856H
Call destination prompt routine: sets bit 6 flag, displays "INSERT DESTINATION DISK (ENTER)", waits for ENTER, selects drive, checks write protection.
44BEH
5859H
LD A,(44EFH) 3A EF 44
Load drive difference (source - dest). Zero = single-drive backup, non-zero = two different drives.
44C1H
585CH
OR A B7
Set flags based on drive difference. Z = single-drive (same drive for source and dest), NZ = two-drive backup.
44C2H
585DH
RET NZ C0
If NZ (two drives), no GAT verification needed - destination hasn't been swapped. Return immediately.

44C3H - Single-Drive Destination Verification

For single-drive backup only. Reads GAT from destination disk and verifies 76H backup-in-progress marker. If wrong disk inserted, clears prompt flag and loops back to retry.

44C3H
585EH
Issue FDC RESTORE command to move head to Track 0 and wait for ready. Required to read GAT at Track 0, Sector 0.
44C6H
5861H
PUSH BC C5
Preserve caller's buffer pointer on stack.
44C7H
5862H
PUSH DE D5
Preserve caller's track/sector values.
44C8H
5863H
PUSH HL E5
Preserve HL.
44C9H
5864H
LD BC,4B00H 01 00 4B
Point BC to general purpose sector buffer. GAT will be read here.
44CCH
5867H
LD DE,0000H 11 00 00
D=0 (track), E=0 (sector) = GAT sector location.
44CFH
586AH
Call Disk I/O Executive to read Track 0, Sector 0 into buffer at 4B00H. Returns Z=success, NZ=error.
44D2H
586DH
POP HL E1
Restore caller's HL from stack.
44D3H
586EH
POP DE D1
Restore caller's track/sector.
44D4H
586FH
POP BC C1
Restore caller's buffer pointer.
44D5H
5870H
LD HL,4601H 21 01 46
Point HL to "DESTINATION DISK READ ERROR!" message.
44D8H
5873H
If NZ (read failed), jump to error handler to display message and HALT. Unrecoverable.
44DBH
5876H
LD A,(4B00H) 3A 00 4B
Load first byte of GAT from buffer. Should be 76H (backup-in-progress marker).
44DEH
5879H
CP 76H FE 76
Compare with 76H (HALT opcode). We wrote this at initialization to mark backup-in-progress. Protects against accidentally using wrong disk.
44E0H
587BH
RET Z C8
If Z (76H found), this is the correct destination disk. Verification complete - return to caller.
44E1H
587CH
XOR A AF
Wrong disk inserted! Set A=0 to force re-prompting.
44E2H
587DH
LD (44E8H),A 32 E8 44
Store 0 to self-modifying location at 44E8H. This clears the "destination already prompted" state.
44E5H
5880H
Loop back to 44B4H to restart destination verification. User will be prompted again for destination disk.

44E7H - Smart Disk Swap Prompt Logic

Determines whether to prompt user for disk swap. Uses self-modifying code at 44E8H to remember last prompted drive. If requested drive matches last prompted, skips the prompt entirely.

44E7H
5882H
CP nnH FE 00
Compare requested drive (A) with last prompted drive (nn at 44E8H). Self-modifying: nn is updated each time we prompt. Z flag set if disks match.
44E9H
5884H
If Z (correct disk already inserted), skip user prompt. Jump to 4527H to select drive and return. User isn't bothered with unnecessary prompt.
44EBH
5886H
LD (44E8H),A 32 E8 44
Store current drive code to 44E8H (the operand of CP instruction above). Next call will compare against this new value.
44EEH
5889H
LD A,nnH 3E 00
Load drive difference from self-modified operand at 44EFH. Value = (source_drive - dest_drive). Zero = single-drive backup, non-zero = two-drive backup.
44F0H
588BH
OR A B7
Since a LD command does not set any FLAGS, Set FLAGS based on the contents of Register A (and, in all events, clear the CARRY FLAG).
44F1H
588CH
If the NZ FLAG (Not Zero) has been set, JUMP to 58C2H to poll the FDC Status; Select the Drive based on (44E8H), if the Drive wasn't Ready wait 1 Second, and RETURN
44F3H
588EH
PUSH BC C5
Save the contents of Register Pair BC to the top of the stack.
44F4H
588FH
PUSH DE D5
Save the contents of Register Pair DE to the top of the stack.
44F5H
5890H
PUSH HL E5
Save the contents of Register Pair HL to the top of the stack.
44F6H
5891H
LD A,0DH 3E 0D
Let Register A equal CARRIAGE RETURN.
44F8H
5893H
GOSUB to 0033H in the Model I ROM to display the character held in Register A at the current cursor position.

Top of a loop

44FBH
5896H
GOSUB to 457BH to display the message pointed to by HL on the screen, and RETurn. HL is not affected.
44FEH
5899H
LD A,(3840H) 3A 40 38
Poll the keyboard row with the ENTER key (i.e., 3840H) and put the results into Register A. NOTE: 3840H is the value held at Keyboard Row 7 - which is ENTER, CLEAR, BREAK, UP, DOWN, LEFT, RIGHT, SPACE.
4501H
589CH
RRCA 0F
Make it easier to test for ENTER by rotating the bits in A right (i.e., higher bit moves lower), with Bit 0 being copied to both the CARRY FLAG and Bit 7.
4502H
589DH
If the C FLAG (Carry) has been set, the ENTER was hit, so JUMP to 5899H to poll the keyboard again.
4504H
589FH
LD BC,4000H 01 00 40
Let Register Pair BC equal 4000H / (Decimal: 16384).
4507H
58A2H
LD A,(3840H) 3A 40 38
Poll the keyboard row with the ENTER key (i.e., 3840H) and put the results into Register A. NOTE: 3840H is the value held at Keyboard Row 7 - which is ENTER, CLEAR, BREAK, UP, DOWN, LEFT, RIGHT, SPACE.
450AH
58A5H
RRCA 0F
Make it easier to test for ENTER by rotating the bits in A right (i.e., higher bit moves lower), with Bit 0 being copied to both the CARRY FLAG and Bit 7.
450BH
58A6H
If the C FLAG (Carry) has been set, JUMP to 58BAH to Restore HL, BC, and DE from the stack; Poll the FDC Status; Select the Drive based on (44E8H), if the Drive wasn't Ready wait 1 Second, and RETURN. Since this was a JUMP, that RETurn will return out of this routine
450DH
58A8H
DEC BC 0B
DECrement the value stored in Register Pair BC by 1.
58A9H
450EH
58A9H
LD A,B
OR C78
Since the Z-80 cannot test Register Pair BC against zero, the common trick is to set Register A to equal to Register B, and then OR A against Register C. Only if both Register B and Register C were zero can the Z FLAG be set.
4510H
58ABH
If BC isn't ZERO yet, LOOP BACK to 58A2H to poll the keyboard again looking for an ENTER.
4512H
58ADH
LD A,1EH 3E 1E
Let Register A equal 1EH (Decimal: 30).
4514H
58AFH
GOSUB to 0033H in the Model I ROM to display the character held in Register A at the current cursor position.
4517H
58B2H
LD BC,3333H 01 33 33
Let Register Pair BC equal 3333H (Decimal: 13107).
451AH
58B5H
GOSUB to 0060H in the Model I ROM Routine to delay for the length of BC * 14.66 microseconds (for a delay of around 1/5 of a second).
451DH
58B8H
LOOP BACK to 5896H to redisplay the message pointed to by HL and wait for an ENTER.

451FH - Restore HL, BC, and DE from the stack; Poll the FDC Status; Select the Drive based on (44E8H), if the Drive wasn't Ready wait 1 Second, and RETURN.

451FH
58BAH
LD A,1EH 3E 1E
Let Register A equal 1EH (Decimal: 30), which will be displayed on screen.
4521H
58BCH
GOSUB to 0033H in the Model I ROM to display the character held in Register A at the current cursor position.
4524H
58BFH
POP HL E1
Put the value held at the top of the STACK into Register Pair HL, and then remove the entry from the stack.
4525H
58C0H
POP DE D1
Put the value held at the top of the STACK into Register Pair DE, and then remove the entry from the stack.
4526H
58C1H
POP BC C1
Put the value held at the top of the STACK into Register Pair BC, and then remove the entry from the stack.
4527H
58C2H
LD A,(37ECH) 3A EC 37
Fetch the FDC's status (which is mirrored to 37ECH) and store it into Register A.
76543210
Not
Ready
ProtectedHead
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
452AH
58C5H
PUSH AF F5
Save the FDC status to the top of the stack so we can use Register A for the next instruction.
452BH
58C6H
LD A,(44E8H) 3A E8 44
Load the current drive select code from self-modifying storage at 44E8H. This contains the drive code (01H, 02H, 04H, or 08H) with bit 6 set if destination disk.
452EH
58C9H
LD (37E1H),A 32 E1 37
Store the value held in Register A into the Disk Drive Select Latch Address of 37E1H. 01H=Drive 0.
4531H
58CCH
POP AF F1
Restore the FDC status back into Register A.
4532H
58CDH
RLCA 07
Rotate the bits of Register left (i.e., lower bit moves higher) one bit position. The contents of bit 7 are copied to the CARRY FLAG and to bit 0, so a NOT READY status (held in Bit 7) will be reflected in the CARRY FLAG.
4533H
58CEH
PUSH BC C5
Put BC back in the stack so we can use it for a moment.
4534H
58CFH
LD BC,0000H 01 00 00
Let Register Pair BC equal 0000H for the longest possible delay.
4537H
58D2H
If the C FLAG (Carry) has been set, then the drive signalled NOT READY, so GOSUB to 0060H in the Model I ROM Routine to delay for the length of BC * 14.66 microseconds (or about 1 second).
453AH
58D5H
POP BC C1
Restore BC back from the stack.
453BH
58D6H
RET C9
RETurn to the caller.

453CH - Disk I/O Executive (Read Sector Routine)

The core disk read function. Entry: BC=buffer, D=track, E=sector, 44E8H=drive code. Exit: Z=success (buffer filled), NZ=error. This routine selects the drive, seeks to the track, issues a READ SECTOR command, and transfers 256 bytes to RAM.

453CH
58D7H
PUSH BC C5
Push buffer address to stack. Will be popped into HL at 4540H after the setup call returns.
453DH
58D8H
Call drive select/seek routine at 4544H. Note: 4544H is the NEXT instruction after the return point, so this pushes a return address and continues. When 4544H's CALL 4527H returns, we continue at 4540H.
4540H
58DBH
POP HL E1
Pop buffer address (pushed at 453CH) into HL. Temporary holding - will move to BC after error check.
4541H
58DCH
RET Z C8
If Z flag set from setup, return immediately. Caller sees Z flag (error condition).
4542H
58DDH
LD B,H 44
Copy H to B. Moving buffer address from HL back to BC.
4543H
58DEH
LD C,L 4D
Copy L to C. Now BC = buffer address for the transfer loop.

4544H - Drive Select and Seek Setup

Selects the drive, loads track/sector to FDC registers, and issues SEEK command to position the head.

4544H
58DFH
Call drive select routine: reads FDC status, loads drive code from 44E8H, writes to drive select latch (37E1H), waits 1 second if drive not ready.
4547H
58E2H
LD (37EEH),DE ED 53 EE 37
Write track (D) to FDC Track Register and sector (E) to FDC Sector Register. The FDC will seek to this track and read this sector.
454BH
58E6H
PUSH DE D5
Save DE to stack. Will be restored at 4576H after transfer. DE will be reused for data register pointer.
454CH
58E7H
LD HL,37ECH 21 EC 37
Point HL to FDC command/status register (37ECH). Used for all subsequent FDC status reads and command writes.
454FH
58EAH
LD (HL),1BH 36 1B
Write 1BH to FDC: SEEK command (0001) with head load (h=1), no verify (V=0), 15ms step rate (r1r0=11). Head moves to track specified in Track Register.
4551H-4554H
58ECH-58EFH
PUSH AF / POP AF / PUSH AF / POP AF F5 F1 F5 F1
~42 T-states ≈ 24µs at 1.77MHz. Gives FDC time to begin processing SEEK before we poll status.

4555H - Wait for Seek Complete

Polls FDC status waiting for BUSY flag to clear, indicating seek is complete.

4555H
58F0H
LD A,(HL) 7E
Read FDC status register into A. Bit 0 = BUSY (1 = seek in progress).
4556H
58F1H
RRCA 0F
Rotate A right, Bit 0 (BUSY) moves to Carry flag.
4557H
58F2H
If Carry set (BUSY=1), seek still in progress. Loop back to poll again. Exits when seek completes (BUSY=0).

4559H - Issue Read Command and Prepare Transfer

Seek complete. Issue READ SECTOR command and set up registers for the data transfer loop.

4559H
58F4H
LD (HL),88H 36 88
Write 88H to FDC: READ SECTOR command (100), single record (m=0), IBM format (S=1), no settle delay (E=0). FDC begins locating and reading the sector.
455BH
58F6H
LD DE,37EFH 11 EF 37
Point DE to FDC Data Register (37EFH). Bytes will be read from here via LD A,(DE) in the transfer loop.
455EH-4561H
58F9H-58FCH
PUSH BC / POP BC / PUSH BC / POP BC C5 C1 C5 C1
~42 T-states delay. Gives FDC time to locate sector and prepare first data byte before we enter the transfer loop.
4562H
58FDH
DI F3
Begin critical section. Must not allow interrupts during data transfer or we'll miss DRQ signals, causing Lost Data errors.
4563H
58FEH
Jump to 4568H to start polling for DRQ. Skips the BUSY check at 4565H on first entry since we just issued the command and know FDC is busy reading.

4565H - Read Transfer Loop - BUSY Check

Part of the read data transfer loop. Checks if FDC has finished the read operation. Jumped here from 456BH when DRQ was not set.

4565H
5900H
RRCA 0F
Rotate status right, moving Bit 0 (BUSY flag) into Carry. If BUSY=1, FDC still processing. If BUSY=0, read complete.
4566H
5901H
If NC (BUSY=0), the 256-byte read is complete. Jump to 4573H to check FDC status for errors and return.

This midsection code is jumped to repeatedly poll the FDC, wait until the Data Request (DRQ) flag is set, transfer a byte from the FDC data register (DE) to the memory buffer (BC), and increment the buffer pointer (BC). It continues indefinitely until the FDC stops asserting the DRQ signal.

4568H
5903H
LD A,(HL) 7E
Read FDC Status. Fetches the FDC Status/Command Register value into A. HL is set to point to this register (37ECH) from the prior routine..
4569H
5904H
BIT 1,A CB 4F
Check for Data Request (DRQ). Tests Bit 1 of the FDC status. This bit is typically the Data Request (DRQ) flag.
456BH
5906H
Wait for Data Ready. If the Z flag is set (Bit 1 is 0), the FDC is not ready to transfer data yet, so the routine jumps back to the polling entry point at 5900H (4565H) to re-read the FDC status.
456DH
5908H
LD A,(DE) 1A
Fetch Data. If the Z flag is clear (Bit 1 is 1), the FDC is ready. This instruction reads one byte of data from the FDC Data Register (pointed to by DE which was set to 37EFH in the prior routine) into A.
456EH
5909H
LD (BC),A 02
Store to Buffer. The byte in A is stored into the memory buffer pointed to by BC. BC acts as the destination memory pointer.
456FH
590AH
INC BC 03
Advance Buffer Pointer. Increments BC to point to the next byte location in the memory buffer.
4570H
590BH
Continue Loop. Jumps back to 4568H to read the FDC status again and continue transferring the next byte of the sector.

4573H - FDC Error Handler Analysis. Check the FDC for an Error, Restore track/sector to DE if not, otherwise Force Interrupt and return.

4573H
590EH
LD A,(HL)
Get the disk controller status and put it into Register A.
76543210
Not
Ready
Write
Protected
Write
Fault
Record
Not Found
CRC
Error
Lost
Data
Data RequestBusy
4574H
590FH
AND 5CH
AND 01011100
AND the controller status against 5CH (Binary: 01011100) to isolate the error flags. This represents Write Protected (Bit 6), Record Not Found (Bit 4), CRC Error (Bit 3), and Lost Data (Bit 2)
4576H
5911H
POP DE
Restore the track/sector address from the stack into Register Pair DE.
4577H
5912H
RET Z
If there was no error, then RETURN.
4578H
5913H
LD (HL),0D0H
If there WAS an error, then put a 0D0H (Decimal: 1101 0000) into the FDC command/status register to stop the controller with a force interrupt.
Bit NumberFunction
76543210
1101I3I2I1I0Command=Force Interrupt
Bit 7-4: Command Code (1101)
I3: 1=Interrupt Immediately
I2: 1=Interrupt on the next Index Pulse
I1: 1=Interrupt the next time Ready goes to Not Ready
I0: 1=Interrupt the next time Not Ready goes to Ready
457AH
5915H
RET
RETURN.

457BH - Display the message pointed to by HL on the screen.

457BH
5916H
PUSH HL E5
Save the pointer to the message being displayed to the stack.
457CH
5917H
LD A,(HL) 7E
Fetch the character of the message being displayed being pointed to at the moment into Register A.
457DH
5918H
CP 03H FE 03
Compare the character to display to a 03H, which is a standard delimeter to mean END OF MESSAGE. If it is 03H, the Z FLAG will be set
457FH
591AH
If the character was an 03H, and the Z FLAG (Zero) had been set, JUMP to 5924H commence exiting this routine.
4581H
591CH
GOSUB to 0033H in the Model I ROM to display the character held in Register A at the current cursor position.
4584H
591FH
INC HL 23
INCrement the value stored in Register Pair HL by 1 to point to the next character to display.
4585H
5920H
CP 0DH FE 0D
Compare the next character to display against 0DH which is a CARRIAGE RETURN. If it is NOT a CARRIAGE RETURN, the NZ FLAG is set.
4587H
5922H
If the NZ FLAG (Not Zero) has been set, LOOP BACK to 4917H to process the next character in the message to display.
4589H
5924H
POP HL E1
If we are here then we are done displaying the message, so restore HL from the stack.
458AH
5925H
RET C9
RETurn to the caller.

458BH-4714H - MESSAGE and BYTE STORAGE AREA

458BH-45A9H
5926H-5944H
DEFM 1D + 'COPYING   TRACK nn, SECTOR nn' + 03H
45AAH-45C7H
5945H-5962H
DEFM 1D + 'VERIFYING TRACK nn, SECTOR nn' + 03H
45C9H-45E7H
5964H-5982H
DEFM 1D + 'LOADING   TRACK nn, SECTOR nn' + 03H
45E8H-4600H
5983H-599BH
DEFM 0A + 'SOURCE DISK READ ERROR!' + 0DH
4601H-461EH
599CH-59B9H
DEFM 0A + 'DESTINATION DISK READ ERROR!' + 0DH
461FH-463DH
59BAH-59D8H
DEFM 0A + 'DESTINATION DISK WRITER ERROR!' + 0DH
463EH-4663H
59D9H-59FEH
DEFM 0A + 'DESTINATION DISK IS WRITE PROTECTED!' + 0DH
4664H-4682H
59FFH-5A1DH
DEFM 1D + 'INSERT SOURCE DISK  (ENTER)' + 1D + 03H
4683H-46A6H
5A1EH-5A41H
DEFM 1D + 'INSERT DESTINATION DISK  (ENTER)' + 1DH + 03H
46A7H-46DDH
5A42H-5A78H
DEFM 1D + 'WAKE   UP!!!  THAT'S NOT THE SAME SOURCE DISK!' + 0DH
46DEH-46EEH
5A79H-5A89H
DEFM 0A + 'BACKUP COMPLETE' + 0D
46EFH-4708H
5A8AH-5AA3H
DEFM 0A + 0A + HIT 'ENTER' TO CONTINUE + 0DH
4713H-4713H
5AAEH-5AAEH
DEFB 00H
4714H-4714H
5AAFH-5AAFH
DEFB 00H

4C00H - MESSAGE and BYTE STORAGE AREA

4C00H-4C34H
5F9BH-5FCFH
DEFM 'SINGLE DRIVE BACKUP INVALID DURING PROGRAM CHAINING' + 0DH
4C35H-4C35H
5FD0H-5FD0H
DEFB 1CH
4C36H-4C36H
5FD1H-5FD1H
DEFB 1FH
4C37H-4C5CH
5FD2H-5FF7H
DEFM 'TRSDOS DISK BACKUP UTILITY  VER 2.3' + 0AH + 0DH
4C5DH-4C73H
5FF8H-600EH
DEFM 'SOURCE DRIVE NUMBER ? ' + 03H
4C74H-4C8FH
600FH-602AH
DEFM 'DESTINATION DRIVE NUMBER ? "+03H
4C90H-4CA9H
602BH-6044H
DEFM 'BACKUP DATE (MM/DD/YY) ' + 03H
4CAAH-4CC1H
6045H-605CH
DEFM 'SOURCE DRIVE NOT READY!' + 0DH
4CC2H-4CDDH
605DH-6078H
DEFM 'SOURCE DRIVE NOT IN SYSTEM!' + 0DH
4CDEH-4CFAH
6079H-6095H
DEFM 'DESTINATION DRIVE NOT READY!' + 0DH
4CFBH-4D1BH
6096H-60B6H
DEFM 'DESTINATION DRIVE NOT IN SYSTEM!' + 0DH
4D1CH-4D3DH
60B7H-60D8H
DEFM 'NO DISKETTE IN DESTINATION DRIVE!' + 0DH
4D3EH-4D63H
60D9H-60FEH
DEFM 'DOOR NOT CLOSED ON DESSTINATION DRIVE!' + 0DH
4D64H-4D91H
60FFH-612CH
DEFM 'DESTINATION DISKETTE UNUSABLE! HARD SECTORED!' + 0DH
4D92H-4DC8H
612DH-6163H
DEFM 'BACKUP REJECTED, DISKETTES CONTAIN DIFFERENT PACK IDS!' + 0DH
4DC9H-4DDDH
6164H-6178H
DEFM 1DH + "FORMATTING TRACK 00' + 03H
4DDEH-4E09H
6179H-61A4H
DEFM 0AH + 'CAN'T FORMAT, DIS TRANSFER RATE TOO FAST!' + 0DH
4E0AH-4E2EH
61A5H-61C9H
DEFM 0AH + 'CAN'T FORMAT, MOTOR SPEED TOO SLOW!' + 0DH
4E2FH-4E53H
61CAH-61EEH
DEFM 0AH + 'CAN'T FORMAT, MOTOR SPEED TOO FAST!' + 0DH
4E54H-4E72H
61EFH-620DH
DEFM 3AH + 'NOT FOUND! TRACK LOCKED OUT!' + 0DH
4E73H-4E91H
620EH-622CH
DEFM 3AH + 'CRC ERROR! TRACK LOCKED OUT!' + 0DH
4E92H-4EC4H
622DH-625FH
DEFM 0AH + 'BACKUP REJECTED DUE TO FLAWS ON DESTINATION DISK!' + 0DH
4EC5H-4EF7H
6260H-6292H
DEFM 0AH + 'CAN'T VERIFY FORMAT, DISK TRANSFER RATE TOO FAST!' + 0DH

4EF8H - Sector Interleave Table (DATA)

This is NOT executable code - it's a data table. The bytes form a sector interleave pattern: 00, 05, 01, 06, 02, 07, 03, 08, 04, 09 (2:1 interleave for optimal sequential read performance on slow systems), followed by FF terminator.

4EF8H-4F02H
6293H-629DH
DEFB 00H, 05H, 01H, 06H, 02H, 07H, 03H, 08H, 04H, 09H, FFH

Sector interleave table for disk I/O. Pattern: 0, 5, 1, 6, 2, 7, 3, 8, 4, 9 - a 2:1 interleave that reads every other sector, then fills in the gaps. This gives the CPU time to process each sector before the next one rotates under the head. FFH terminates the table.