TRS-80 DOS - TRSDOS v1.3 - XFERSYS/CMD Disassembled

General:

XFERSYS/CMD is a one-shot upgrade utility shipped with TRSDOS 1.3 for the Model III. Its job is to take a TRSDOS 1.1 or TRSDOS 1.2 diskette in Drive 0 and produce a TRSDOS 1.3 diskette in Drive 1, copying over the SYSTEM files (BOOT/SYS, SYS0/SYS through SYS7/SYS, the RENUMBER overlay area, and the directory-track files) while preserving anything from the source diskette that the user would expect to survive an upgrade — most notably the protection counter and the 1.1-format serial number — and re-indexing the resulting destination directory so that the end-record-numbers point to the correct last record of each file.

The program is loaded by the SYS1/SYS command interpreter into the SYSHI overlay area at 5200H (TRSDOS 1.3 reserves 4E00H–51FFH as the SYSLOW overlay area for SYS1–SYS5/SYS10, and 5200H upward as the SYSHI overlay area for SYS6+ and standalone CMD utilities). At entry, the supervisor has already read the program file from disk and parked the user's command-line tail in HL; XFERSYS does not consume any command-line arguments, so HL is ignored after entry. On exit, the program returns to the DOS READY prompt either via JP SYS1IN (402DH) on success, or via JP ABORT (4030H) on a Q response, an aborted prompt, or a 1.3-already-installed condition, or via JP ERROR (4409H) on any disk-I/O failure encountered along the way.

The high-level flow is: prompt the user for confirmation; READ track 0 sector 1 of the source (BOOT/SYS) into BUFFR; sniff byte 254 (the version byte of TRSDOS 1.x boot sectors) to determine source-disk version; if the source is already 1.3, abort; if the source is a SYSTEM disk, copy three blocks of sectors (BOOT/SYS through track 0, then track 15 RENUMBER, then track 18 onward); call CORDIR to fix every directory entry's ERN value (TRSDOS 1.3 stores ERN one less than TRSDOS 1.1/1.2 did); then walk the destination's HIT one logical-file at a time, re-creating each SYSTEM file's directory entry on the destination via OPEN+INIT+GET/PUT+CLOSE; finally display "Xfersys Complete~" and exit.

Memory Map

Address RangeLabelContents
5200H–5204HSTARTEntry point — sets the CLEAR-RAM flag at CFLAG (42B4H) so that subsequent overlays load fresh.
5205H–525BHSTART1Display the title prompt and read the user's Y/Q response. Falls through with HL pointing at BUFFR after the read sector.
525CH–526DHSTART2Read the protection-counter byte from BUFFR+34 (1.2 location), version-test against 12H, and (for 1.2) skip the serial-number copy.
526EH–5270HNOPE1.1-only path — call MOVSER to capture the 10-byte serial number out of BUFFR+243.
5271H–5290HVER12Branch on system-disk flag — if SYSTEM-disk, call MOVBOT then copy the BOOT/SYS, RENUMBER, and SYS-files sector ranges via three calls to MOVE.
5291H–52ABHNOTSYSFor 1.1/1.2 sources, call CORDIR to fix the directory; then re-read the destination HIT and copy it into BUFFR for the per-file walk.
52ACH–5344HSTART4Per-system-file outer loop — find next HIT entry via GETFIL, build NAME/EXT:0 read-DCB and NAME/EXT:1 write-DCB strings, OPEN both, branch into the GET/PUT loop.
5345H–5357HSTAR10Sequential GET/PUT loop that copies the file body byte-for-byte until source EOF.
5358H–5384HSTAR11Close the destination, mark its directory entry SYSTEM-invisible (OR 4EH into the attribute byte), zero the UPD password, and write the directory back.
5385H–539FHGETFILWalk the saved HIT looking for the next non-zero entry; CALL RDDIR for it; clear the HIT entry so we don't process it twice.
53A0H–53F2HCORDIRRe-read the source HIT, copy it to BUFF2, and walk every entry; for each one whose EOF byte is non-zero, decrement the ERN and write the directory back.
53F3H–53FBHMENDExit-on-success — display "Xfersys Complete~" and JP SYS1IN.
53FCH–5406HMOVBOTSubroutine — push AF, copy just the boot sector (track 0 sector 1, B=1) via MOVE, restore AF, and return.
5407H–5475HMOVESubroutine — copy B sectors starting at track/sector DE from drive 0 to drive 1 via XREAD/XWRITE, with special-case handling for the boot sector (overwrites the destination's protection counter and serial number from saved values).
5476H–5486HMOVSERSubroutine — copy the 10-byte serial number from BUFFR+243 into SERBUF, set the SERFLG self-modifying-byte to FFH, and return.
5487H–5494HSETPROSubroutine — point IY at SCAFLG (42FFH), DEC the byte at (IY) to set the protection-override flag, then clear IY and return. (TRSDOS 1.3 checks (IY)≠0 in the OPEN path to bypass the protection-counter check.)
5495H–551EHTITLEDisplay strings, packed back-to-back: TITLE prompt, ALREDY message, COMPLT message.
5528H–5559HDCB1Read DCB storage (50 bytes, DCBSIZ from SYS0.GBL).
555AH–5590HDCB2Write DCB storage (55 bytes, DCBSIZ+5).
5591H–559AHSERBUF10-byte serial-number buffer.
559BHCOUNT1-byte protection-counter storage (load file ends here).
5600H–56FFHBUFFR256-byte general-purpose sector buffer (uninitialized DEFS — not in load file). 256-byte alignment via ORG $,256.
5700H–57FFHSBUFF256-byte source-side file buffer (passed as the buffer pointer to OPEN). Uninitialized DEFS.
5800H–58FFHDBUFF256-byte destination-side file buffer (passed as the buffer pointer to INIT). Uninitialized DEFS.

Self-Modifying Workspace Variables

XFERSYS uses four self-modifying-code (SMC) operand bytes inside its own code space. Each is the 1-byte operand of an LD A,0 or LD BC,0 instruction; storing into the labelled address rewrites the instruction so that the next time it executes, A (or BC) is loaded with the most recently stored value. These labels live inside a transient overlay and are not stable across other CMD-utility loads — they exist only while XFERSYS is the active program in the SYSHI area.

AddressLabelSizeDescription
5263HSVER1Operand byte of the LD A,0 at 5262H. Holds the source-disk version (12H = TRSDOS 1.2, anything else = TRSDOS 1.1) so the program can re-read it later at NOTSYS without going back to the boot sector. Written at 5238H.
5272HSYSFLG1Operand byte of the LD A,0 at 5271H. Holds the system-disk flag (0 = data disk; non-zero = system disk, computed as BUFF1+SYSOFF (= 4300H+E0H = 43E0H)'s extent-pointer byte INCremented). Written at 5247H. Read by VER12's OR A + conditional CALL/JR sequence.
53E6HLFNSAV2Operand bytes of the LD BC,0 at 53E5H. Holds the (LFN-in-B, drive-1-in-C) pair captured at CORDI3 entry so it can be reloaded after the directory READ has clobbered BC. Written at 53C6H (LD (LFNSAV),BC).
543CHSERFLG1Operand byte of the LD A,0 at 543BH inside MOVE. Initially 00H (skip serial-number copy); set to FFH by MOVSER (5481H) so that on the boot-sector pass through MOVE, the saved 10-byte serial number is patched into BUFFR before the destination write.

Major Routines

AddressLabelFunction / Entry-Exit
5200HSTARTProgram entry point. Sets the CLEAR-RAM flag at CFLAG (42B4H) so the next SYS1 load reinitializes the overlay area. Falls through to START1.
5205HSTART1Display the title prompt, accept a 1-character Y/Q response. Loops back here on no-response or non-Y/Q. JP ABORT on Q. Continues with the destination-disk version check.
525CHSTART2Re-entry point after version=1.3 check. Read protection counter from BUFFR+34 (1.2 location) and store in COUNT. Test version against 12H to decide whether to skip the serial-number copy.
526EHNOPE1.1 path — CALL MOVSER to copy the 1.1-format serial number out of BUFFR+243.
5271HVER12System-disk branch. If source is a SYSTEM disk, copy boot via MOVBOT then call MOVE three times (BOOT-SYS area, RENUMBER area, files area). If data disk, fall through to NOTSYS.
5291HNOTSYSCommon path — re-read source version; if not 1.3, call CORDIR. Re-read destination HIT and copy it into BUFFR as the per-file walk's master list.
52ACHSTART4Top of the per-system-file outer loop. CALL GETFIL; if EOF, JP MEND. Build NAME:0 and NAME:1 strings into DCB1/DCB2.
52C5HSTART5Inner loop copying up to 8 filename bytes from the directory entry into both DCB strings, terminated by a SPACE.
52D9HSTART6/START7Filename-copy loop exit. POP the saved directory pointer.
52F1HSTART8Inner loop copying up to 3 extension bytes after the "/" separator.
5303HSTART9Drive-specifier emit — ":0" for the source DCB, ":1" for the destination DCB; both DCB strings terminated with 0DH.
5345HSTAR10GET/PUT byte-copy loop. CALL GET on DCB1; if EOF, JR STAR11. CALL PUT on DCB2; loop.
5358HSTAR11End-of-file branch. CLOSE the write DCB, RDDIR for the destination's directory entry, OR 4EH into attribute byte, zero the UPD password word, WRDIR.
5385HGETFILScan saved-HIT for next non-zero LFN. SCF+RET if exhausted (caller sees CARRY=1).
5393HGETFI2Match found — clear the HIT byte, then RDDIR with B=LFN, C=0 (drive 0).
53A0HCORDIRRe-read source HIT, copy BUFF1 to BUFF2 (working copy), then loop through MAXFIL entries.
53B8HCORDI1Top of the CORDIR HIT-walk loop.
53BCHCORDI2Bottom-of-loop pointer advance.
53C0HCORDI3Active entry — SAVE registers, save (LFN,drive) into LFNSAV, RDDIR, examine the EOF byte; if non-zero, DEC the ERN and write the directory back.
53EEHCORDI4RSTR registers, JR CORDI2 to continue.
53F3HMENDDisplay COMPLT, JP SYS1IN.
53FCHMOVBOTBoot-sector-only copy subroutine. PUSH AF / DE=1 / B=1 / CALL MOVE / POP AF / RET.
5407HMOVEMulti-sector copy subroutine: read B sectors from drive 0 to BUFFR (5600H+), then write them back to drive 1, with mid-loop track wrapping at sector 13H (TRACK+1=29H wait — actually 19 sectors per track in 1.x doublestep, but TRSDOS 1.3 still uses 18 phys + 1 hold so the test is CP TRACK+1 = CP 13H which is 18+1 ⇒ end-of-track at sector 13H).
5476HMOVSERSerial-number capture: LDIR 9 bytes (SERLTH+1 = 8+1) from BUFFR+243 to SERBUF; set SERFLG to FFH; RET.
5487HSETPROProtection-override: point IY at SCAFLG (42FFH); DEC (IY); clear IY; RET.

DCB / Directory-Entry Field Reference

XFERSYS reaches into directory entries via offsets defined in SYS0.GBL. The relevant offsets used in this overlay:

OffsetEquateUsed by
+03HDEOFCORDI3 (53D2H) reads the EOF byte to decide whether to fix the ERN.
+05HDNAMESTART4 (52B7H) sets DE = DNAME and ADDs HL,DE to land HL at the start of the 8-byte filename inside the directory entry.
+06HSYSTEMBIT-position constant (= 6) used at 52B2H by BIT SYSTEM,(HL) to test the SYSTEM bit of the HIT byte. (SYSTEM in SYS0.GBL is the bit number, not an offset.)
+07HLFNSTAR11 (5358H) reads (IX+LFN) from the destination DCB after CLOSE. (Here LFN is an IX-offset equate via the symbolic reference DCB2+LFN.)
+09HLRLSTART4 (5337H) reads (DCB1+LRL) to size the destination's INIT call.
+0DHDEXTSTART7 (52DAH) sets DE = DEXT and ADDs HL,DE to land HL at the 3-byte extension.
+10HDUPDSTAR11 (5373H) sets DE = DUPD and ADDs HL,DE to land at the UPD password word, which is then zeroed.
+14HDERNCORDI3 (53D9H) sets DE = DERN and ADDs HL,DE to land at the ERN field; HL is then DEC'd, the 16-bit ERN read into BC, decremented, and written back.
+E0HSYSOFFSTART2 (5243H) reads (BUFF1+SYSOFF) = 43E0H, the system-extent-pointer byte in the source's BOOT/SYS image.

External Routines Called

XFERSYS calls into the following SYS0/SYS1/SYS7 resident routines. All of these are stable across XFERSYS execution because XFERSYS does not load any other overlays:

AddressModulePurpose
0013HROMString compare (used by START4 at 5348H).
001BHROMString length / utility (used by START4 at 5350H).
0040HROMKEYN — line-input from the keyboard. B = max chars; HL = buffer.
01C9HSYS0CLS — clear the screen.
021BHSYS0PRINT — display the message at HL until a 03H terminator.
402DHSYS0SYS1IN — DOS READY exit (success path from MEND).
4030HSYS0ABORT — error-already-displayed exit (used at START1 on Q response and at 5259H after the ALREDY message).
4409HSYS0ERROR — DOS error-display exit (used after every disk-I/O failure).
4420HSYS0INIT — open a file new-or-existing; HL = buffer, DE = DCB, B = LRL.
4424HSYS0OPEN — open an existing file; HL = buffer, DE = DCB.
4428HSYS0CLOSE — close the file whose DCB is in DE.
4675HSYS0XREAD — non-error-checked sector read; HL = buffer, C = drive, DE = (sector,track).
45F7HSYS0XWRITE — non-error-checked sector write; HL = buffer, C = drive, DE = (sector,track).
4A67HSYS0RDDIR — read the directory record for LFN in B from drive in C; on return DE points at the entry.
4A7BHSYS0WRDIR — write the directory record back.
4ABAHSYS0RDHIT — read the HIT (Hash Index Table) sector for the drive in C into BUFF1.

Cross-References and Analysis Notes

A few items observed during this disassembly that future readers should be aware of:

  • BUFFR is at 5600H, not contiguous with 559BH. The source forces a 256-byte alignment via ORG $,256 after COUNT, so BUFFR / SBUFF / DBUFF land at 5600H / 5700H / 5800H. The high byte alone identifies which page-aligned buffer is being addressed at any given moment, which simplifies several MOVE-internal pointer adjustments.
  • SYSTEM is a bit number, not an offset. The BIT SYSTEM,(HL) at 52B2H uses SYSTEM=06 (bit 6 of the HIT byte) to test whether the directory entry is a SYSTEM file. This is distinct from how SYSTEM appears in the FCB type byte (where it's a flag combined into bits 0–7).
  • The version byte at offset 254 / offset 252. TRSDOS 1.x boot sectors store the version (e.g., 11H, 12H, 13H) at byte 254. The protection counter LSB is at byte 252 in 1.1 and at byte 34 (C1LOC, from SYS7.GBL = 22H) in 1.2 — XFERSYS reads both depending on the SVER comparison.
  • SCAFLG protection-override. SETPRO writes a non-zero value into the byte at SCAFLG (42FFH, from SYS0.GBL) by doing LD YL,SCAFLG.LSB. / LD YH,SCAFLG.MSB. / DEC (IY). SYS0's OPEN and INIT paths check this byte; when non-zero, the protection counter check is bypassed so XFERSYS can open files on a 1.1/1.2 source without tripping the protection logic.
  • Serial-number layout. SERLTH (from SYS0.GBL) = 08, so the LDIR count in MOVSER is SERLTH+1 = 9 bytes, and the matching write in MOVE21 (5440H) also moves 9 bytes from SERBUF back into BUFFR+S1SLOC (where S1SLOC, from SYS7.GBL = F4H = 244 dec). The S1SLOC equate replaces the literal "+243" you'd expect from the read side; the +1 in the count covers a leading flag byte in the serial block.

Disassembly:

5200H - START - XFERSYS Entry Point - Set the CLEAR-RAM Flag

Entry point of XFERSYS/CMD. The supervisor has just finished loading the CMD file from disk; HL holds the next character on the DOS command line (unused), DE holds the system DCB pointer (unused), and the rest of the registers are undefined. The first job is to set the CLEAR-RAM FLAG so that when this utility eventually returns to DOS READY via SYS1IN, the loader knows it has to reinitialize the overlay area for the next command.

 
ORG 5200H
Set the assembler's location counter to 5200H, the SYSHI overlay area where TRSDOS 1.3 utility CMD files are loaded.
5200START
LD A,55H
Let Register A equal 55H — this is the magic value (binary 01010101) that TRSDOS 1.3's loader recognizes as "clear RAM after this CMD finishes." The value is otherwise arbitrary; what matters is that it's non-zero and matches the value the loader looks for.
Original Source Code Comment: SET THE 'CLEAR RAM' FLAG
5202
LD (42B4H),ALD (CFLAG),A
Store the value held in Register A (55H, the CLEAR-RAM FLAG) into memory location 42B4H.
NOTE: 42B4H is CFLAG, the system-resident byte that SYS1/SYS reads on its way back to DOS READY. A non-zero CFLAG tells SYS1 to wipe the SYSHI overlay area so the next CMD load gets a clean RAM image.

5205H - START1 - Display the Title and Read the Y/Q Response

This is the user-prompt loop. It clears the screen, prints the title and the "Diskette to Convert Ready in Drive 1? (Y/Q)" message, then reads exactly one character from the keyboard into BUFFR. If the user presses BREAK, ROM 0040H returns with CARRY set and we abort. If the user presses ENTER with no character (B returned as 0), we re-prompt. If the character is anything other than Y or Q, we re-prompt. Q aborts to DOS READY without doing any work; Y falls through to the disk-version sniff.

On exit from this section, BUFFR has just been overwritten by the source-disk's BOOT/SYS sector (the first XREAD call sits inside this block) — that's why START2 reads its protection counter and version byte directly out of BUFFR.

5205START1
GOSUB to 01C9H.
NOTE: 01C9H is the SYS0/SYS routine (resident in low memory) that clears the video display and homes the cursor to the top-left corner. No registers are altered on return.
Original Source Code Comment: CLEAR THE SCREEN
5208
LD HL,5495HLD HL,TITLE
Let Register Pair HL equal 5495H, which is the address of the TITLE string ('TRS-80 Model 3 Xfersys Utility Ver 1.3' followed by 0A 0A and then 'Diskette to Convert Ready in Drive 1? (Y/Q) ' followed by a 03H terminator).
Original Source Code Comment: HL => TITLE
520B
GOSUB to 021BH to display the message at (HL) on the video screen.
NOTE: 021BH is the SYS0/SYS PRINT routine; it prints characters from (HL) until it encounters a 03H byte, advancing HL as it goes. HL is NOT preserved across this call — it ends pointing one byte past the 03H terminator. None of that matters here because the very next instruction reloads HL.
Original Source Code Comment: DISPLAY IT
520E
LD HL,5600HLD HL,BUFFR
Let Register Pair HL equal 5600H, the address of BUFFR — the program's general-purpose 256-byte sector buffer. ROM 0040H requires the input buffer pointer in HL, so we load it here. Note that BUFFR is also re-used as the disk read buffer immediately after the keypress is captured, which is why we don't need a separate prompt-input area.
Original Source Code Comment: HL => DATA BUFFER
5211
LD B,01H
Let Register B equal 01H. ROM 0040H reads B characters from the keyboard, so this requests exactly one character.
Original Source Code Comment: SET FOR 1 CHAR
5213
GOSUB to ROM 0040H.
NOTE: 0040H (KEYN in SYS0.GBL) is the line-input routine. It waits for B characters or for an early CR/BREAK, echoes printable characters to the screen as it goes, and stores them at (HL). On return: HL still points at the start of the buffer; B holds the actual count of characters captured (0 if the user pressed ENTER immediately); CARRY = 1 if the user pressed the BREAK key (in which case the input is invalid).
Original Source Code Comment: GET THE 'Y/Q' ANSWER
5216
If the CARRY FLAG is set (user pressed BREAK during input), JUMP to 4030H.
NOTE: 4030H is the SYS0 ABORT vector — it loads a vector code into A and traps to RST 28H, which displays "*BREAK*" and returns to DOS READY without running any further user code.
Original Source Code Comment: EXIT IF BREAK KEY
5219
LD A,B
Copy the actual character count returned by KEYN (held in Register B) into Register A so the OR A on the next line can set the Z FLAG based on it.
Original Source Code Comment: ANY ANSWER?
521A
OR A,A
Set the FLAGS based on the contents of Register A (the LD on the previous line did not affect flags). Z FLAG will be set if A = 0, meaning the user pressed ENTER without typing anything.
521B
If the Z FLAG is set (no character was typed), JUMP back to 5205H to re-clear the screen and re-prompt.
Original Source Code Comment: NO, DO IT AGAIN
521D
LD A,(HL)
Fetch the character that the user typed (the first byte of BUFFR, where KEYN parked it) and store it into Register A.
Original Source Code Comment: GET THE ANSWER
521E
CP 51HCP 'Q'
Compare the value held in Register A against 51H (ASCII Q). Z FLAG is set if the user typed Q (quit).
Original Source Code Comment: QUIT?
5220
If the Z FLAG is set (user pressed Q), JUMP to 4030H (ABORT) to return to DOS READY without copying anything.
Original Source Code Comment: YES, EXIT
5223
CP 59HCP 'Y'
Compare the value held in Register A against 59H (ASCII Y). Z FLAG is set if the user typed Y (yes, proceed).
Original Source Code Comment: YES?
5225
If the NZ FLAG is set (user typed something other than Y or Q), JUMP back to 5205H to re-prompt.
Original Source Code Comment: NO, DO IT AGAIN

User answered Y. Proceed to read the source disk's boot sector into BUFFR.

5227
LD HL,5600HLD HL,BUFFR
Let Register Pair HL equal 5600H (BUFFR) again. (KEYN preserves HL, so this reload is technically redundant, but it makes the XREAD-call sequence self-contained and easy to read in the source.)
Original Source Code Comment: HL => DATA BUFFER
522A
LD DE,0001HLD DE,1
Let Register Pair DE equal 0001H. The XREAD calling convention packs (sector, track) into DE: D = track number = 0, E = sector number = 1. So this requests track 0 sector 1 — the BOOT/SYS sector.
Original Source Code Comment: SET FOR TRACK 0, SECTOR 1
522D
LD C,01H
Let Register C equal 01H — the drive number. XFERSYS reads from drive 1 here because the user has just been told to put the source diskette in Drive 1. (Note: the prompt says "Drive 1" but XFERSYS treats drive 1 as the SOURCE; the destination is drive 0. This is the opposite of what most users would expect, but it's what XFERSYS's source code does.)
Original Source Code Comment: DRIVE 1
522F
GOSUB to 4675H.
NOTE: 4675H is the SYS0 XREAD routine — non-error-checked sector read. It reads one sector from drive C, track D, sector E into the 256-byte buffer at (HL). On return: Z FLAG = read OK; NZ = error (with A holding the error code).
Original Source Code Comment: READ IN THE SECTOR
5232
If the NZ FLAG is set (XREAD failed), JUMP to 4409H.
NOTE: 4409H is the SYS0 ERROR vector. It expects an error code in A and routes through SYS4 to display the matching error message before returning to DOS READY.
Original Source Code Comment: EXIT IF ERROR
5235
LD A,(56FEH)LD A,(BUFFR+254)
Fetch the byte at address 56FEH (= BUFFR + 254) and store it into Register A. This is the version byte of the source disk's BOOT/SYS sector — TRSDOS 1.x stores its version number (11H, 12H, 13H, etc.) at offset 254 of track 0 sector 1.
Original Source Code Comment: GET THE VERSION NUMBER
5238
LD (5263H),ALD (SVER),A
Store the version byte (held in Register A) into memory location 5263H. SVER is a self-modifying-code operand: it's the immediate byte of the LD A,0 instruction at 5262H. Storing the version here means that whenever 5262H executes, A will be loaded with the source disk's version number. This lets later code re-fetch the version without re-reading the disk.
Original Source Code Comment: SAVE A COPY OF IT
523B
LD C,01H
Let Register C equal 01H — drive 1 again, the source drive.
Original Source Code Comment: SET FOR DRIVE 1
523D
GOSUB to 4ABAH.
NOTE: 4ABAH is the SYS0 RDHIT routine — read the HIT (Hash Index Table) sector for the drive in C. It internally calls XREAD with track=DIRTRK (typically 11H, the directory track) and sector=DIRSEC (1, the HIT). On return: HL points at BUFF1 (4300H) where the HIT was loaded, Z = OK, NZ = error with A holding the error code.
Original Source Code Comment: GET THE HIT TABLE
5240
If the NZ FLAG is set (RDHIT failed), JUMP to ERROR.
Original Source Code Comment: EXIT IF ERROR
5243
LD A,(43E0H)LD A,(BUFF1+SYSOFF)
Fetch the byte at address 43E0H and store it into Register A. BUFF1 is at 4300H and SYSOFF (from SYS0.GBL) = E0H, so this address is 4300H + E0H = 43E0H — the system-extent-pointer byte inside the HIT sector that was just loaded by RDHIT. On a SYSTEM disk this byte holds the LFN of the SYS0/SYS file (as a one-byte signed offset to the next directory entry slot); on a non-system "data only" disk this byte is FFH.
Original Source Code Comment: GET THE SYSTEM EXTENT POINTER
5246
INC A
INCrement Register A by 1. The point of INC A here is purely to test the byte we just loaded for the value FFH: if A was FFH (data disk), then after INC A is 00H and the Z FLAG is set; otherwise the Z FLAG is clear.
Original Source Code Comment: THIS A SYSTEM DISK?
5247
LD (5272H),ALD (SYSFLG),A
Store the (now-incremented) extent-pointer byte into memory location 5272H. SYSFLG is a self-modifying-code operand: it's the immediate byte of the LD A,0 at 5271H (VER12). When the program later JR's into VER12, the loaded A will be 0 (data disk) or non-zero (system disk), letting the code branch on disk type without re-reading the HIT.
Original Source Code Comment: IF SO, WILL BE 'Z'
524A
If the Z FLAG is set (A is now 0, meaning the original byte was FFH = data disk), JUMP to 525CH (START2) — the data-disk path skips the "already 1.3?" check because a data-only disk has no version to test against.
Original Source Code Comment: IS DATA DISK. CONTINUE

SYSTEM disk path. Check whether the source disk is already TRSDOS 1.3 (in which case there's nothing to upgrade).

524C
LD A,(5263H)LD A,(SVER)
Fetch the version byte saved earlier into the SVER self-modifying operand at 5263H, and store it into Register A.
Original Source Code Comment: GET THE VERSION NUMBER
524F
CP 13H
Compare the value held in Register A against 13H (TRSDOS version 1.3). Z FLAG is set if the source is already 1.3.
Original Source Code Comment: VERSION 1.3?
5251
If the NZ FLAG is set (source is NOT 1.3, i.e., it's 1.1 or 1.2 — the case we're trying to handle), JUMP to 525CH (START2) to continue the upgrade path.
Original Source Code Comment: NO, CONTINUE

Source is already TRSDOS 1.3. Display the "ALREADY Version 1.3" message and abort.

5253
LD HL,54EBHLD HL,ALREDY
Let Register Pair HL equal 54EBH, the address of the ALREDY string ('Destination Diskette is ALREADY Version 1.3' followed by ~ which acts as a 03H-substitute terminator in the encoded form, but here is just plain ASCII bytes followed by an explicit terminator embedded in the next message's leading byte run).
Original Source Code Comment: HL => 'ALREADY 1.3' DISK
5256
GOSUB to 021BH (PRINT) to display the ALREDY message on the screen.
Original Source Code Comment: DISPLAY IT
5259
JUMP to 4030H (ABORT). The user has been informed; return to DOS READY.
Original Source Code Comment: (no source comment — the JP completes the ALREADY-1.3 branch)

525CH - START2 - Read the Protection Counter and Decide on Serial-Number Handling

Common landing pad for the data-disk path (jumped to from line 524AH because the source has FFH at SYSOFF) and the not-already-1.3 path (jumped to from 5251H after the version check). On entry, BUFFR (5600H) still holds the 256-byte BOOT/SYS sector that was just read at 522FH — so the protection counter and serial number can be read out of it directly.

This block fetches the LSB of the protection counter from BUFFR+C1LOC (the TRSDOS 1.2 location at offset 22H), saves it into COUNT, then re-loads the version byte from the SVER self-modifying-operand byte and compares it against 12H. If it really is 1.2, we skip ahead to VER12 because TRSDOS 1.2 boot sectors don't have a serial number block worth copying. Otherwise it's 1.1, so we re-fetch the protection counter from offset 252 (the 1.1 location) and call MOVSER to capture the 10-byte serial number.

525CSTART2
LD A,(5622H)LD A,(BUFFR+C1LOC)
Fetch the byte at address 5622H (= BUFFR + C1LOC, where C1LOC = 22H = 34 decimal from SYS7.GBL) and store it into Register A. This is the LSB of the protection counter as TRSDOS 1.2 stores it inside the boot sector.
NOTE: TRSDOS 1.1 stores the protection-counter LSB at offset 252 of the boot sector, while TRSDOS 1.2 moved it to offset 34. This block reads the 1.2 location first; the 1.1 location is read further down at 5268H.
Original Source Code Comment: GET THE PROTECTION COUNT IF 1.2
525F
LD (559BH),ALD (COUNT),A
Store the value held in Register A (the just-loaded protection-counter LSB) into memory location 559BH. COUNT is XFERSYS's single-byte protection-counter cache; MOVE21 (5440H) reads it out of COUNT and patches it into the destination boot sector right before the destination boot-sector write completes.
5262
LD A,00HLD A,(SVER)
Let Register A equal 00H — but this 00H is a self-modifying-code placeholder. The label SVER is defined in the source as $+1, meaning it points at the operand byte of THIS instruction (5263H). The earlier LD (SVER),A at 5238H stored the source disk's version number (11H, 12H, etc.) into 5263H, rewriting THIS instruction's immediate. By the time control reaches here, the byte loaded into A is the actual version number, not 00H.
Original Source Code Comment: VERSION STORED HERE
5264
CP A,12H
Compare the value held in Register A (the source-disk version) against 12H (TRSDOS 1.2). The Z FLAG is set if the source is exactly 1.2; NZ if 1.1 (or anything else).
Original Source Code Comment: VERSION 1.2?
5266
If the Z FLAG is set (source is 1.2), JUMP to 5271H (VER12) — skipping both the offset-252 protection-counter re-read AND the MOVSER call, because TRSDOS 1.2 doesn't lay out the serial number the way 1.1 did.
Original Source Code Comment: YES, DON'T DO ANYTHING WITH SERIAL NUMBER

Falls through here only when the source is TRSDOS 1.1. The protection-counter LSB lives at offset 252 in 1.1, and the serial number block lives starting at offset 243 — both of which are in the BUFFR copy we already have.

5268
LD A,(56FCH)LD A,(BUFFR+252)
Fetch the byte at address 56FCH (= BUFFR + 252) and store it into Register A. This is the TRSDOS 1.1 location of the protection-counter LSB, overwriting whatever was loaded earlier from the 1.2 location.
Original Source Code Comment: GET THE LSB OF PROTECTION COUNT
526B
LD (559BH),ALD (COUNT),A
Store the value held in Register A (the 1.1-format protection-counter LSB) into COUNT (559BH), overwriting the 1.2-format value that the prior LD (COUNT),A at 525FH put there.
Original Source Code Comment: SAVE IT

526EH - NOPE - Save the Source Disk's Serial Number

One-instruction routine. NOPE is the entry point for the TRSDOS 1.1 serial-number capture; the source's ADISP 'Address to Patch out is ^NOPE' directive (which emits no bytes) made this label important enough at build time to print to the assembler listing — so a developer applying a binary patch to disable serial-number copying could find the byte to NOP out.

526ENOPE
GOSUB to 5476H (MOVSER). MOVSER copies the 10-byte serial-number block from BUFFR+243 into the SERBUF storage area at 5591H, and sets the SERFLG self-modifying-byte at 543CH to FFH so that the next pass through MOVE will know to patch the saved serial number into the destination's boot sector.
Original Source Code Comment: MOVE THE SERIAL NUMBER

5271H - VER12 - System-Disk Branch and Bulk Sector Copy

VER12 is reached by three different paths: by direct JR from 5266H (1.2 source — skipped MOVSER), by fall-through from NOPE (1.1 source — just ran MOVSER), and indirectly by the data-disk path which set SYSFLG to zero. The job of VER12 is to look at the system-disk flag (encoded into the SYSFLG self-modifying byte at 5272H) and decide what to do next:

If SYSFLG is zero, the source is a data-only disk. In that case, MOVBOT is called (which copies just the boot sector, preserving the patched protection counter and serial number on the destination), and the JR Z then skips the bulk-OS copy by jumping to NOTSYS.

If SYSFLG is non-zero, the source is a SYSTEM disk. Both Z-conditional instructions are skipped, and the program falls through to call MOVE three times — once for the BOOT/SYS area (track 0, 40 sectors), once for the RENUMBER area (track 15 = 0FH, 36 sectors), and once for the SYS-files area (track 18 = 12H, 66 sectors).

5271VER12
LD A,00HLD A,(SYSFLG)
Let Register A equal 00H — once again, this 00H is a self-modifying-code placeholder. The label SYSFLG is defined as $+1, pointing at the operand byte (5272H) of THIS instruction. The earlier LD (SYSFLG),A at 5247H stored the system-disk-flag byte here (0 for data disk, non-zero for system disk). What actually loads into A is whichever value was last written into 5272H.
Original Source Code Comment: WILL BE ZERO IF THIS IS NOT A SYSTEM DISK
5273
OR A,A
Set FLAGS based on the contents of Register A (the LD on the prior line did not affect flags). The Z FLAG is set if A = 0 (data disk); NZ if A ≠ 0 (system disk).
Original Source Code Comment: SYSTEM DISK?
5274
If the Z FLAG is set (data disk — SYSFLG was 0), GOSUB to 53FCH (MOVBOT). MOVBOT pushes AF, sets DE = 0001H (track 0 sector 1) and B = 1 (one sector), calls MOVE to do the boot-sector copy, then pops AF. The point of preserving AF across MOVBOT is that the next instruction needs to make the same Z/NZ decision MOVBOT was called on — and MOVE itself uses the flags internally.
Original Source Code Comment: IF SO, COPY THE BOOT SECTOR OVER
5277
If the Z FLAG is set (still — data disk), JUMP to 5291H (NOTSYS), skipping the three bulk-MOVE calls below. The data-disk path only needed the boot sector copied, not the entire OS.
Original Source Code Comment: YES. DON'T MOVE THE OPERATING SYSTEM OVER

System-disk path. Three bulk-sector copies follow: BOOT/SYS area (track 0–1), RENUMBER (track 15), and SYS-files (track 18 onward).

5279
LD DE,0001HLD DE,1
Let Register Pair DE equal 0001H. MOVE's calling convention packs (sector,track) into DE: D = track = 00H, E = sector = 01H — so this requests track 0, sector 1, the start of the BOOT/SYS area.
Original Source Code Comment: START AT TRACK 0, SECTOR 1 (BOOT/SYS0)
527C
LD B,28HLD B,40
Let Register B equal 28H (40 decimal) — the number of sectors to copy. This covers track 0 (sectors 1–18) plus track 1 entirely (sectors 1–18) plus 4 sectors into track 2, totaling 40 sectors. That spans BOOT/SYS, the directory track gap, and into SYS0/SYS.
Original Source Code Comment: NUMBER OF SECTORS TO DO
527E
GOSUB to 5407H (MOVE). MOVE reads B sectors starting at the (track,sector) packed in DE from drive 1 (the source) into BUFFR, then writes them back to drive 0 (the destination) at the same track/sector — applying any patch logic on a per-sector basis (such as the boot-sector serial-number patch at MOVE21).
Original Source Code Comment: MOVE THEM FROM 1 DISK TO THE OTHER
5281
LD DE,0F01HLD DE,0F01H
Let Register Pair DE equal 0F01H — track 15 (0FH), sector 1. This is the start of the RENUMBER overlay area on a TRSDOS 1.x system disk.
Original Source Code Comment: NOW DO TRACK 15, SECTOR 1 (RENUMBER)
5284
LD B,24HLD B,36
Let Register B equal 24H (36 decimal) — 36 sectors, which spans tracks 15 and 16 in their entirety (18+18).
Original Source Code Comment: NUMBER OF SECTORS TO DO
5286
GOSUB to MOVE again with the new (track,sector) and count. Copies the RENUMBER area from drive 1 to drive 0.
5289
LD DE,1201HLD DE,1201H
Let Register Pair DE equal 1201H — track 18 (12H), sector 1. Track 18 onward is where TRSDOS 1.x stores the SYS files (SYS1/SYS through SYS7/SYS) on a system disk.
Original Source Code Comment: TRACK 18, SECTOR 1
528C
LD B,42HLD B,66
Let Register B equal 42H (66 decimal) — enough sectors to cover the SYS files and a small reserve.
Original Source Code Comment: NUMBER OF SECTORS TO DO
528E
Third and final bulk MOVE — copies the SYS-files area. After this returns, fall through to NOTSYS.

5291H - NOTSYS - Common Path: Fix the Directory and Save the Destination HIT

Reached by all paths: data-disk (after MOVBOT), system-disk (after the three MOVEs), and the original 1.2 / 1.1 paths. The first job here is to call CORDIR if the source isn't already 1.3 — because TRSDOS 1.3 stores the directory's end-record-numbers (ERNs) one less than 1.1 / 1.2 did, and every ERN in the destination's directory needs to be decremented to compensate.

After CORDIR returns, we re-read the destination drive's HIT (drive 0 — the destination is in drive 0 in this version of the program) and copy the entire 256-byte HIT into the BUFFR area as a working snapshot. The per-system-file walk that follows uses BUFFR as its master list of LFNs to process, zeroing each entry's byte after it's processed so we never double-process.

5291NOTSYS
LD A,(5263H)LD A,(SVER)
Fetch the source-disk version byte (held in the SVER self-modifying-operand byte at 5263H, originally written from BUFFR+254 at 5238H) and store it into Register A.
Original Source Code Comment: GET THE VERSION NUMBER
5294
CP A,13H
Compare the value held in Register A against 13H (TRSDOS 1.3). The Z FLAG is set if the source is already 1.3; NZ if it's 1.1 or 1.2 — the case in which the directory needs fixing.
Original Source Code Comment: VERSION 1.3?
5296
If the NZ FLAG is set (source is 1.1 or 1.2 — the upgrade case), GOSUB to 53A0H (CORDIR). CORDIR re-reads the source HIT, walks every directory entry, and decrements each entry's ERN by 1 (because TRSDOS 1.3's ERN convention is one less than 1.1 / 1.2's).
Original Source Code Comment: NO. GO CORRECT THE DIRECTORY
5299
LD C,00H
Let Register C equal 00H — drive 0, the destination drive. RDHIT below will read the HIT for whichever drive is in C.
Original Source Code Comment: READ IN THE HIT FROM 1.3
529B
GOSUB to 4ABAH (RDHIT) to read the destination drive's HIT into BUFF1 (4300H). After CORDIR has rewritten the destination's directory, this RDHIT pulls the corrected HIT back into RAM so the per-file walk below can use it.
529E
If the NZ FLAG is set (RDHIT failed), JUMP to 4409H (ERROR) to display the error code in A and return to DOS READY.
52A1
LD HL,4300HLD HL,BUFF1
Let Register Pair HL equal 4300H — BUFF1, the SYS0-resident sector-1 disk buffer where RDHIT just deposited the destination drive's HIT. HL becomes the LDIR source pointer.
Original Source Code Comment: HL => HIT TABLE
52A4
LD DE,5600HLD DE,BUFFR
Let Register Pair DE equal 5600H — BUFFR, XFERSYS's own 256-byte general-purpose buffer at the top of the program's storage area. DE becomes the LDIR destination pointer.
Original Source Code Comment: DE => SAVE AREA
52A7
LD BC,0100HLD BC,SECTOR
Let Register Pair BC equal 0100H (256 decimal) — the SECTOR equate from SYS0.GBL, equal to the standard TRSDOS sector size. BC becomes the LDIR byte count.
Original Source Code Comment: NUMBER OF BYTES
52AA
LDIR
Block-copy: transfers a byte from (HL) to (DE), increments both, decrements BC, and repeats until BC reaches zero. After this completes, BUFFR (5600H–56FFH) contains a private copy of the destination drive's HIT — used by GETFIL as the master list of LFNs still to process. BUFF1 (4300H) is now free to be re-used as a directory-record buffer by RDDIR / WRDIR.
Original Source Code Comment: SAVE THE HIT TABLE

52ACH - START4 - Per-System-File Outer Loop: Find Next File and Build DCB Strings

This is the top of the outer loop that walks every SYSTEM-flagged file in the destination's saved HIT (the snapshot we just made in BUFFR). For each one, it builds two filespec strings — one for the source DCB at DCB1 (with drive specifier ":0") and one for the destination DCB at DCB2 (with drive specifier ":1") — by copying the directory-entry's filename and extension fields. Once both DCBs hold valid filespec strings ending in 0DH, the SETPRO/OPEN/INIT sequence opens both files and we drop into the GET/PUT copy loop at STAR10.

The loop continues until GETFIL exhausts the saved HIT and returns CARRY=1, at which point we JP to MEND for the success exit. Non-system files are skipped via the BIT 6,(HL) test.

52ACSTART4
GOSUB to 5385H (GETFIL). GETFIL walks the saved HIT in BUFFR looking for the next non-zero entry; if it finds one, it clears that entry's byte (so we don't see it again on the next iteration), reads the matching directory record into BUFF1 via RDDIR, and returns with HL pointing at the directory entry, B = LFN, and CARRY = 0. If GETFIL exhausts the HIT, it returns with CARRY = 1.
Original Source Code Comment: GET POINTER TO NEXT FILE
52AF
If the CARRY FLAG is set (GETFIL found no more files), JUMP to 53F3H (MEND) — the success-exit path. The destination disk is now fully populated with all the SYSTEM files from the source.
Original Source Code Comment: EXIT IF END OF HIT
52B2
BIT 6,(HL)BIT SYSTEM,(HL)
Test bit 6 (the SYSTEM bit, where SYSTEM = 06H from SYS0.GBL) of the byte held at the memory location pointed to by Register Pair HL — that's the directory entry's TYPE/attribute byte. The Z FLAG is set if bit 6 is zero (a regular non-system file); NZ if bit 6 is one (a SYSTEM file we want to copy).
Original Source Code Comment: THIS A SYSTEM FILE?
52B4
If the Z FLAG is set (the directory entry is NOT a SYSTEM file), JUMP back to 52ACH (START4) to fetch the next entry. We only want to copy SYSTEM files in this pass.
Original Source Code Comment: NO, CONTINUE

Confirmed SYSTEM file. HL points at the start of the 32-byte directory entry (the TYPE byte). Save HL on the stack so we can come back to it after walking through the filename/extension fields, and start building the two filespec strings.

52B6
PUSH HL
Save the contents of Register Pair HL (the directory-entry pointer) to the top of the stack. We'll add 5 to HL to land at the filename, then later add 0DH to land at the extension; PUSH/POP brackets the whole filename+extension walk.
Original Source Code Comment: SAVE THE POINTER
52B7
LD DE,0005HLD DE,DNAME
Let Register Pair DE equal 0005H (DNAME = 5 from SYS0.GBL — the offset from the start of a directory entry to the start of the 8-byte filename field).
Original Source Code Comment: OFFSET TO THE NAME
52BA
ADD HL,DE
Add the value held in Register Pair DE (5) to the value held in Register Pair HL, with the result placed back into HL — so HL now points at the first character of the filename inside the directory entry.
Original Source Code Comment: HL => START OF NAME
52BB
LD IX,5528HLD IX,DCB1
Let Special Index Register IX equal 5528H (DCB1) — the start of the read DCB, which doubles as the buffer where we build the source filespec string. The string-build loop below uses IX as a write pointer.
Original Source Code Comment: IX => READ DCB
52BF
LD IY,555AHLD IY,DCB2
Let Special Index Register IY equal 555AH (DCB2) — the start of the write DCB, which similarly doubles as the buffer for the destination filespec string. IY is the parallel write pointer for DCB2's string.
Original Source Code Comment: IY => WRITE DCB
52C3
LD B,08HLD B,8
Let Register B equal 08H (8 decimal) — the maximum filename length. The DJNZ loop below copies up to 8 characters; an early SPACE terminates the copy.
Original Source Code Comment: SET FOR 8 CHARACTERS

52C5H - START5 - Filename Copy Inner Loop

Tight loop that copies one character of the filename per iteration into both DCB strings simultaneously. Terminated either by hitting a SPACE (control character ≤ 20H, treated as end-of-name) or by exhausting the 8-character count.

52C5START5
LD A,(HL)
Fetch the next filename character (held in the memory location pointed to by Register Pair HL inside the directory entry) and store it into Register A.
Original Source Code Comment: GET A CHARACTER FROM NAME
52C6
CP A,21HCP ' '+1
Compare the value held in Register A against 21H (ASCII !, which is one greater than ASCII SPACE = 20H). The CARRY FLAG is set if A < 21H — i.e., if A is SPACE, NUL, or any other control character — meaning end-of-name has been reached.
Original Source Code Comment: END OF NAME?
52C8
If the CARRY FLAG is set (A is SPACE or lower — end of filename), JUMP to 52D9H (START6, which is the same address as START7) to exit the filename-copy loop and pop HL back.
Original Source Code Comment: YES, EXIT
52CA
LD (IY+00H),ALD (IY),A
Store the filename character (held in Register A) at the location pointed to by Special Index Register Pair IY+00H — that's the next slot in the DCB2 (write DCB) filespec buffer.
Original Source Code Comment: STORE THE NAME INTO BOTH DCB'S
52CD
LD (IX+00H),ALD (IX),A
Store the same filename character at IX+00H — the matching slot in the DCB1 (read DCB) filespec buffer.
52D0
INC HL
INCrement the source pointer (stored in Register Pair HL, currently inside the directory entry's filename field) by 1 to advance to the next filename character.
Original Source Code Comment: BUMP POINTERS
52D1
INC IY
INCrement Special Index Register IY by 1 to advance the DCB2 write pointer.
52D3
INC IX
INCrement Special Index Register IX by 1 to advance the DCB1 write pointer.
52D5
DECrement Register B (the remaining-character counter); if the result is NOT zero, JUMP back to 52C5H (START5) to copy another character. After 8 iterations B reaches zero and we fall through.
Original Source Code Comment: LOOP FOR COUNT
52D7
Unconditionally JUMP to 52D9H (START7) to enter the extension-copy block. (This JR is needed because the filename loop can fall out either by exhausting the count OR by hitting the SPACE terminator at 52C8H — both paths end up at START6/START7, which are the same address.)
Original Source Code Comment: AND CONTINUE

52D9H - START7 - Extension Copy: Restore Pointer and Test Extension

Common landing pad whether the filename loop exited via the early-out (CARRY at 52C8H), the count-exhaustion (DJNZ at 52D5H), or the unconditional JR at 52D7H. The first job is to restore HL from the stack — so HL once again points at the start of the directory entry — and then add DEXT (= 0DH = 13 decimal) to land at the 3-byte extension field.

If the extension's first byte is < 21H (SPACE or control), the file has no extension and we skip the "/" emit and the extension copy loop, jumping directly to START9 to emit the drive specifier.

52D9START7
POP HL
Restore Register Pair HL from the top of the stack — the directory-entry pointer that was saved at 52B6H. HL now once again points at the TYPE byte at the start of the entry.
Original Source Code Comment: GET THE DIRECTORY POINTER BACK
52DA
LD DE,000DHLD DE,DEXT
Let Register Pair DE equal 000DH (DEXT = 13 from SYS0.GBL — the offset from the start of a directory entry to the start of the 3-byte extension field).
Original Source Code Comment: DE = OFFSET TO EXTENSION
52DD
ADD HL,DE
Add 13 to Register Pair HL — HL now points at the first character of the extension inside the directory entry.
Original Source Code Comment: HL => EXTENSION
52DE
LD A,(HL)
Fetch the first extension character (held in the memory location pointed to by Register Pair HL) and store it into Register A.
Original Source Code Comment: GET 1ST CHAR OF EXT
52DF
CP A,21HCP ' '+1
Compare the value held in Register A against 21H (ASCII !, one greater than SPACE). CARRY is set if the extension is empty (its first byte is SPACE or below).
Original Source Code Comment: ANY EXTENSION?
52E1
If the CARRY FLAG is set (no extension), JUMP to 5303H (START9) to skip the "/" emit and the extension-copy loop, and go directly to the drive-specifier emit.
Original Source Code Comment: NO, CONTINUE

Extension is non-empty. Emit the "/" separator into both DCB strings before copying the 3 extension characters.

52E3
LD (IX+00H),2FHLD (IX),'/'
Store 2FH (ASCII /) at the location pointed to by IX+00H — the next slot in the DCB1 filespec buffer. The TRSDOS filespec format is NAME/EXT:D, so the slash separates the filename from the extension.
Original Source Code Comment: SET EXTENSION SPECIFIER
52E7
LD (IY+00H),2FHLD (IY),'/'
Store the same 2FH (/) at IY+00H — the matching slot in DCB2.
52EB
INC IX
INCrement Special Index Register IX by 1.
Original Source Code Comment: BUMP POINTERS
52ED
INC IY
INCrement Special Index Register IY by 1.
52EF
LD B,03HLD B,3
Let Register B equal 03H — the extension's maximum length is 3 characters.
Original Source Code Comment: SET FOR 3 CHARACTERS

52F1H - START8 - Extension Copy Inner Loop

Mirror of START5, but for the 3-byte extension instead of the 8-byte filename. Same SPACE-terminated, count-bounded copy.

52F1START8
LD A,(HL)
Fetch the next extension character (held in the memory location pointed to by Register Pair HL) and store it into Register A.
Original Source Code Comment: GET A BYTE
52F2
CP A,21HCP ' '+1
Compare the value held in Register A against 21H. CARRY is set if A is SPACE or lower (end of extension).
Original Source Code Comment: END OF EXTENSION?
52F4
If the CARRY FLAG is set (extension done early), JUMP to 5303H (START9) to emit the drive specifier.
Original Source Code Comment: YES, EXIT
52F6
LD (IY+00H),ALD (IY),A
Store the extension character (held in Register A) at IY+00H — the next DCB2 slot.
Original Source Code Comment: NO, INTO DCB
52F9
LD (IX+00H),ALD (IX),A
Store the same extension character at IX+00H — the matching DCB1 slot.
52FC
INC HL
INCrement the directory-extension source pointer (stored in Register Pair HL) by 1.
Original Source Code Comment: BUMP POINTERS
52FD
INC IY
INCrement Special Index Register IY by 1.
52FF
INC IX
INCrement Special Index Register IX by 1.
5301
DECrement Register B; if the result is NOT zero, JUMP back to 52F1H (START8) to copy another extension character. After 3 iterations B reaches zero.

5303H - START9 - Emit Drive Specifier and Terminator into Both DCB Strings

Common landing pad whether we got here from a no-extension early-out (52E1H), an extension-mid-loop early-out (52F4H), or by falling out of START8 with a full 3-character extension. Now emit the ":" drive separator, then the drive digit ("0" for source DCB1, "1" for destination DCB2), then the 0DH carriage-return terminator that TRSDOS file-system calls require at the end of a filespec string.

5303START9
LD (IX+00H),3AHLD (IX),':'
Store 3AH (ASCII :) at IX+00H — the colon that begins the drive specifier in the DCB1 filespec.
Original Source Code Comment: SET A DRIVE SPECIFIER
5307
LD (IY+00H),3AHLD (IY),':'
Store the same 3AH (:) at IY+00H for DCB2.
530B
INC IX
INCrement Special Index Register IX by 1.
Original Source Code Comment: BUMP POINTERS
530D
INC IY
INCrement Special Index Register IY by 1.
530F
LD (IX+00H),30HLD (IX),'0'
Store 30H (ASCII 0) at IX+00H — the source drive digit. Read DCB filespec is now NAME/EXT:0.
Original Source Code Comment: SET READ FOR DRIVE 0
5313
LD (IY+00H),31HLD (IY),'1'
Store 31H (ASCII 1) at IY+00H — the destination drive digit. Write DCB filespec is now NAME/EXT:1.
Original Source Code Comment: SET WRITE FOR DRIVE 1
5317
INC IX
INCrement IX by 1.
Original Source Code Comment: BUMP POINTERS
5319
INC IY
INCrement IY by 1.
531B
LD (IX+00H),0DH
Store 0DH (ASCII CR) at IX+00H — the filespec-string terminator. DCB1 buffer now holds a complete filespec ending in CR.
Original Source Code Comment: TERMINATE THE FILE NAME
531F
LD (IY+00H),0DH
Store 0DH (CR) at IY+00H. DCB2 buffer now holds its complete filespec ending in CR.

Both DCB strings are built. Now disable the protection counter via SETPRO, OPEN the source file, then INIT the destination file with a matching LRL.

5323
GOSUB to 5487H (SETPRO) to set the SCAFLG protection-override byte at 42FFH so that the upcoming OPEN call doesn't trip the protection counter check on the source disk.
Original Source Code Comment: CLEAR THE PROTECTION FLAG
5326
LD HL,5700HLD HL,SBUFF
Let Register Pair HL equal 5700H (SBUFF) — the 256-byte source-side file buffer. OPEN's calling convention takes HL = buffer pointer.
Original Source Code Comment: HL => SOURCE BUFFER
5329
LD DE,5528HLD DE,DCB1
Let Register Pair DE equal 5528H (DCB1) — the source DCB whose filespec string we just built.
Original Source Code Comment: DE => SOURCE DCB
532C
GOSUB to 4424H.
NOTE: 4424H is the SYS0 OPEN routine — opens an existing file, parses the filespec at (DE) into the DCB, and binds the buffer at (HL) to it. Returns Z = success, NZ = error with A = error code.
Original Source Code Comment: OPEN THE FILE
532F
If the NZ FLAG is set (OPEN failed), JUMP to 4409H (ERROR).
Original Source Code Comment: EXIT IF ERROR
5332
GOSUB to SETPRO again before the destination INIT so the destination disk's protection check is also bypassed.
Original Source Code Comment: CLEAR THE PROTECTION FLAG
5335
LD HL,5800HLD HL,DBUFF
Let Register Pair HL equal 5800H (DBUFF) — the 256-byte destination-side file buffer.
Original Source Code Comment: HL => DESTINATION BUFFER
5338
LD DE,555AHLD DE,DCB2
Let Register Pair DE equal 555AH (DCB2) — the destination DCB.
Original Source Code Comment: DE => DESTINATION DCB
533B
LD A,(5531H)LD A,(DCB1+LRL)
Fetch the byte at address 5531H (= DCB1 + 09H, where LRL = 09 from SYS0.GBL — the Logical Record Length offset inside a DCB) and store it into Register A. The OPEN call above populated DCB1's LRL field from the source file's directory entry; now we'll pass that same value to INIT for the destination so the two files have matching record sizes.
Original Source Code Comment: GET THE SOURCE LRL
533E
LD B,A
Copy the source LRL (held in Register A) into Register B. INIT's calling convention takes B = LRL.
Original Source Code Comment: INTO B
533F
GOSUB to 4420H.
NOTE: 4420H is the SYS0 INIT routine — opens a file new-or-existing. If the file doesn't exist, it's created. INIT takes HL = buffer, DE = DCB, B = LRL. Returns Z = success, NZ = error.
Original Source Code Comment: OPEN THE FILE
5342
If the NZ FLAG is set (INIT failed), JUMP to 4409H (ERROR).
Original Source Code Comment: EXIT IF ERROR

5345H - STAR10 - GET/PUT Loop: Copy the File Body Byte for Byte

Tight inner loop that drains the source file into the destination file. The loop calls SYS0's GET on DCB1 to fetch one byte (or one record, depending on how SYS0's GET-by-DCB pseudo-vector resolves; on TRSDOS 1.3 it's a record-mode read sized by the DCB's LRL). When GET returns NZ, that signals end-of-file on the source — at which point we jump to STAR11 to close out and mark the directory entry. Otherwise we call PUT on DCB2 to write the same byte/record to the destination, then loop.

Note on the GET/PUT addresses: The CALLs at 5348H (CALL 0013H) and 5350H (CALL 001BH) are the ROM-vector dispatch addresses for GET and PUT respectively. ROM 0013H and 001BH are documented entry points that route through the SYS0 dispatch table.

5345STAR10
LD DE,5528HLD DE,DCB1
Let Register Pair DE equal 5528H (DCB1) — the source DCB. GET takes DE = DCB.
Original Source Code Comment: DE => READ DCB
5348
GOSUB to 0013H.
NOTE: 0013H is the ROM vector for the SYS0 GET routine — fetches one byte (or one record, in record-mode DCBs) from the file open on DE. Returns A = data byte (or record-buffer pointer), Z = success, NZ = end-of-file or error.
Original Source Code Comment: GET A BYTE
534B
If the NZ FLAG is set (end-of-file on the source), JUMP to 5358H (STAR11) — the close-and-mark-directory branch.
Original Source Code Comment: EXIT IF EOF
534D
LD DE,555AHLD DE,DCB2
Let Register Pair DE equal 555AH (DCB2) — the destination DCB. PUT takes DE = DCB.
Original Source Code Comment: DE => WRITE DCB
5350
GOSUB to 001BH.
NOTE: 001BH is the ROM vector for the SYS0 PUT routine — writes one byte (or one record) to the file open on DE. Returns Z = success, NZ = error.
5353
If the NZ FLAG is set (PUT failed — typically destination disk full), JUMP to ERROR.
Original Source Code Comment: EXIT IF ERROR
5356
Unconditionally JUMP back to 5345H (STAR10) to fetch the next byte/record. Loop continues until source EOF.
Original Source Code Comment: LOOP TILL DONE

5358H - STAR11 - Close, Mark Directory Entry SYSTEM-Invisible, Continue

Reached when source EOF was hit at STAR10. We need to close the destination DCB (which flushes its last record and updates the directory's EOF/LRL/ERN), then re-read the destination directory entry, OR 4EH into the attribute byte (which sets the SYSTEM/INVISIBLE/READ-ONLY bits — making this a hidden system file like its source counterpart), zero out the UPD password word so no password is required, write the directory back, and loop to the next file.

The PUSH BC / POP BC dance around the CLOSE call preserves the LFN we just read from the destination's DCB across the CLOSE — because CLOSE clobbers BC.

5358STAR11
LD A,(5561H)LD A,(DCB2+LFN)
Fetch the byte at address 5561H (= DCB2 + 07H, where LFN = 07 from SYS0.GBL — the Logical File Number offset inside a DCB) and store it into Register A. After INIT, DCB2's LFN field holds the directory slot the destination file lives in. We need this LFN to drive the upcoming RDDIR call.
Original Source Code Comment: GET THE DESTINATION FILE NUMBER
535B
LD B,A
Copy the LFN (held in Register A) into Register B. RDDIR takes B = LFN.
Original Source Code Comment: SAVE IT
535C
PUSH BC
Save Register Pair BC (which now holds the LFN in B) to the top of the stack. We do this because the upcoming CLOSE call clobbers BC, and we'll need the LFN back to call RDDIR.
Original Source Code Comment: ON THE STACK
535D
LD DE,555AHLD DE,DCB2
Let Register Pair DE equal 555AH (DCB2) — the destination DCB. CLOSE takes DE = DCB.
Original Source Code Comment: CLOSE THE WRITE FILE
5360
GOSUB to 4428H.
NOTE: 4428H is the SYS0 CLOSE routine — flushes the file's last buffered record to disk, updates the directory entry's EOF/LRL/ERN fields, and frees the DCB. Returns Z = success, NZ = error.
5363
POP BC
Restore Register Pair BC from the top of the stack — B now holds the LFN again.
Original Source Code Comment: GET FILE NUMBER BACK
5364
If the NZ FLAG is set (CLOSE failed), JUMP to ERROR. (The flag check is done AFTER the POP BC because the POP doesn't disturb flags.)
5367
LD C,01HLD C,1
Let Register C equal 01H — drive 1 (the source). RDDIR will read directory entry B from drive C.
Original Source Code Comment: SET FOR DRIVE 1
5369
GOSUB to 4A67H (RDDIR) to read directory entry B (the destination file's LFN) from drive 1. Wait — the source comment says "BRING IN THE DESTINATION DIR RECORD" but we're reading from drive 1, which the program elsewhere calls the source. This appears to be a holdover from an earlier version of the program where drive numbering was reversed; on TRSDOS 1.3 production runs of XFERSYS, drive 1 is in fact where the destination disk is mounted because the user was prompted to put it there. The HIT save and per-file walk all read from drive 1.
Original Source Code Comment: BRING IN THE DESTINATION DIR RECORD
536C
If the NZ FLAG is set (RDDIR failed), JUMP to ERROR.
Original Source Code Comment: EXIT IF ERROR
536F
LD A,(HL)
Fetch the byte held at the memory location pointed to by Register Pair HL — RDDIR returns HL pointing at the start of the directory entry, so this is the TYPE/attribute byte.
Original Source Code Comment: GET THE ATTRIB BYTE
5370
OR A,4EH
OR Register A against 4EH (binary 0100 1110). This sets bits 6 (SYSTEM), 3 (INVISIBLE), 2 (READ-ONLY), and 1 (WRITE-PROTECTED) in the attribute byte — turning a regular file entry into a SYSTEM-flagged invisible read-only entry, which is the proper attribute set for the system files we just copied.
Original Source Code Comment: MAKE IT SYSTEM INVISIBLE
5372
LD (HL),A
Store the modified attribute byte (held in Register A) back at the memory location pointed to by HL — overwriting the old TYPE byte in the directory entry.
Original Source Code Comment: PUT IT BACK
5373
LD DE,0010HLD DE,DUPD
Let Register Pair DE equal 0010H (DUPD = 16 from SYS0.GBL — the offset from the start of a directory entry to the UPDATE password word).
Original Source Code Comment: OFFSET TO THE UPDATE PASSWORD
5376
ADD HL,DE
Add 16 to Register Pair HL — HL now points at the first byte of the UPDATE password word inside the directory entry.
Original Source Code Comment: HL => UPDATE TRAP DOOR CODE
5377
LD (HL),00H
Zero out the byte at the memory location pointed to by Register Pair HL — the LSB of the UPDATE password.
Original Source Code Comment: ZERO IT OUT
5379
INC HL
INCrement HL by 1 to advance to the MSB of the UPDATE password.
537A
LD (HL),00H
Zero out the MSB byte. The UPDATE password is now 0000H — meaning no password is required to write to this file.
537C
GOSUB to 4A7BH.
NOTE: 4A7BH is the SYS0 WRDIR routine — writes the directory entry currently in BUFF1 back to the directory track. Uses the previously-set drive number from the last RDHIT/RDDIR.
Original Source Code Comment: WRITE OUT THE NEW RECORD
537F
If the NZ FLAG is set (WRDIR failed), JUMP to ERROR.
Original Source Code Comment: EXIT IF ERROR
5382
Unconditionally JUMP back to 52ACH (START4) to process the next SYSTEM file in the saved HIT.
Original Source Code Comment: DO THE NEXT FILE

5385H - GETFIL - Walk the Saved HIT for the Next Non-Zero Entry

Subroutine called by START4 once per system-file iteration. Walks the saved HIT in BUFFR looking for the next entry whose hash byte is non-zero (meaning that LFN is occupied). Once found, it clears that entry's byte (so the same LFN won't be returned next call), reads the matching directory entry into BUFF1 via RDDIR, and returns with HL pointing at the directory entry, B = LFN, C = drive 0, and CARRY = 0.

If the HIT is exhausted (all bytes seen are zero), it sets CARRY=1 and returns immediately — the caller treats CARRY=1 as the "no more files" signal.

5385GETFIL
LD HL,5600HLD HL,BUFFR
Let Register Pair HL equal 5600H (BUFFR) — the start of the saved HIT snapshot. HL is the walk pointer.
Original Source Code Comment: HL => SAVED HIT TABLE
5388
LD B,50HLD B,MAXFIL
Let Register B equal 50H (MAXFIL = 80 decimal from SYS0.GBL — the maximum number of files a TRSDOS 1.x disk's HIT can hold). B becomes the DJNZ down-counter.
Original Source Code Comment: MAX FILES ON DISK
538AGETFI1
LD A,(HL)
Fetch the next HIT byte (held in the memory location pointed to by Register Pair HL) and store it into Register A.
Original Source Code Comment: GET A HIT CODE
538B
OR A,A
Set FLAGS based on the contents of Register A. Z FLAG = empty slot; NZ FLAG = occupied (the byte is the file's hash code).
Original Source Code Comment: AN CODE?
538C
If the NZ FLAG is set (this HIT slot is occupied), JUMP to 5393H (GETFI2) to set up the directory read.
Original Source Code Comment: YES, SET POINTERS
538E
INC HL
INCrement the HIT walk pointer (stored in Register Pair HL) by 1 to look at the next slot.
Original Source Code Comment: NO, BUMP TO NEXT FILE
538F
DECrement Register B; if the result is NOT zero, JUMP back to 538AH (GETFI1) to test the next slot. After 80 iterations B reaches zero and we fall through.
Original Source Code Comment: LOOP FOR COUNT

HIT exhausted — no more files. Set CARRY=1 to signal "end of HIT" to the caller, and return.

5391
SCF
Set the CARRY FLAG. The caller (START4 at 52AFH) checks CARRY and JP's to MEND if set.
Original Source Code Comment: SET CARRY. NO MORE FILES
5392
RET
RETurn to the caller with CARRY=1.
5393GETFI2
LD B,L
Copy the LSB of the walk pointer (held in Register L) into Register B. Because BUFFR starts at 5600H, the LSB of HL when we hit a non-zero slot equals the LFN of that file (slot 0 → LFN 0, slot 1 → LFN 1, ..., slot 79 → LFN 79). B is now the LFN.
Original Source Code Comment: GET IT'S LFN
5394
LD (HL),00H
Zero out the HIT slot (the byte at the memory location pointed to by HL) so the next call to GETFIL won't see this same entry.
Original Source Code Comment: CLEAR OUT THIS HIT
5396
LD C,00H
Let Register C equal 00H — drive 0. RDDIR will read directory entry B from drive C.
Original Source Code Comment: SET FOR DRIVE 0
5398
GOSUB to 4A67H (RDDIR) to read the directory entry for the LFN in B from drive 0. On return: HL points at the start of the directory entry; Z = success, NZ = error.
Original Source Code Comment: GET POINTERS TO THE FILE
539B
If the NZ FLAG is set (RDDIR failed), JUMP to ERROR.
Original Source Code Comment: EXIT IF ERROR
539E
OR A,A
Set FLAGS based on the contents of Register A — and, in particular, clear the CARRY FLAG so the caller sees CARRY=0 (success) rather than whatever stale carry state JP NZ left.
Original Source Code Comment: INSURE CARRY OFF
539F
RET
RETurn to the caller with CARRY=0 (success).

53A0H - CORDIR - Re-Read Source HIT and Walk Each Directory Entry to Decrement ERN

Called by NOTSYS at 5296H whenever the source isn't already 1.3. CORDIR's purpose is to fix up the destination's directory entries: TRSDOS 1.1 and 1.2 stored each file's End-Record-Number (ERN) one MORE than 1.3 does, so every directory entry's ERN needs to be decremented by 1 to make sense in the 1.3 directory layout.

The implementation re-reads the source disk's HIT (drive 1) into BUFF1, then copies it to BUFF2 as a working snapshot (because RDDIR will reuse BUFF1). Then it walks every byte of the BUFF2 HIT snapshot. For each occupied entry, it CALLs into CORDI3 which RDDIR's the directory entry, checks whether its EOF byte is non-zero (meaning the file actually has data — empty/zero-length files don't need ERN adjustment), decrements the ERN if so, and writes the entry back via WRDIR.

53A0CORDIR
LD C,01HLD C,1
Let Register C equal 01H — drive 1 (the source disk, where we still have the unfixed 1.1/1.2 directory).
Original Source Code Comment: SET FOR DRIVE 1
53A2
GOSUB to 4ABAH (RDHIT) to read the source drive's HIT into BUFF1.
Original Source Code Comment: READ IN THE HIT TABLE
53A5
If the NZ FLAG is set (RDHIT failed), JUMP to ERROR.
Original Source Code Comment: EXIT IF ERROR

HIT now in BUFF1 (4300H). Copy it to BUFF2 (4D00H) so subsequent RDDIR calls (which reuse BUFF1) don't clobber our walking copy.

53A8
LD HL,4300HLD HL,BUFF1
Let Register Pair HL equal 4300H (BUFF1) — the LDIR source.
Original Source Code Comment: HL => WHERE HIT WAS DELIVERED
53AB
LD DE,4D00HLD DE,BUFF2
Let Register Pair DE equal 4D00H (BUFF2) — the LDIR destination.
Original Source Code Comment: DE => WHERE WE NEED IT
53AE
LD BC,0100HLD BC,SECTOR
Let Register Pair BC equal 0100H (SECTOR = 256) — the LDIR byte count.
Original Source Code Comment: SIZE OF ONE SECTOR
53B1
LDIR
Block-copy 256 bytes from BUFF1 to BUFF2. After this, BUFF2 holds the snapshot we'll walk; BUFF1 is free for RDDIR's use.
Original Source Code Comment: MOVE THE HIT
53B3
LD HL,4D00HLD HL,BUFF2
Let Register Pair HL equal 4D00H (BUFF2) — the start of our HIT snapshot. HL is the walk pointer for the loop below.
Original Source Code Comment: HL => HASH TABLE FOR 1.1, 1.2 DISK
53B6
LD B,50HLD B,MAXFIL
Let Register B equal 50H (MAXFIL = 80) — the down-counter for the 80 directory slots.
Original Source Code Comment: B = MAXIMUM FILE COUNT FOR DISK

Top of the CORDIR HIT-walk loop. For each occupied slot, CORDI3 reads the directory entry, fixes its ERN if needed, and writes it back. Empty slots are skipped via the JR NZ.

53B8CORDI1
LD A,(HL)
Fetch the current HIT byte (held in the memory location pointed to by Register Pair HL) and store it into Register A.
Original Source Code Comment: GET A HASH CODE
53B9
OR A,A
Set FLAGS based on the contents of Register A. Z = empty slot; NZ = occupied.
Original Source Code Comment: ANY HASH HERE?
53BA
If the NZ FLAG is set (occupied slot), JUMP to 53C0H (CORDI3) to fix this entry's ERN.
Original Source Code Comment: YES. GO CHECK IT'S DIRECTORY
53BCCORDI2
INC HL
INCrement the HIT walk pointer (stored in Register Pair HL) by 1 to advance to the next slot.
Original Source Code Comment: BUMP TO NEXT GUY
53BD
DECrement Register B; if the result is NOT zero, JUMP back to 53B8H (CORDI1) to process the next slot. After 80 iterations B reaches zero and we fall through.
Original Source Code Comment: LOOP FOR COUNT
53BF
RET
RETurn to the caller (NOTSYS at 5296H). All 80 directory entries have been examined; the directory is now consistent with TRSDOS 1.3's ERN convention.
Original Source Code Comment: DONE

53C0H - CORDI3 - Per-Entry: RDDIR, Decrement ERN if EOF Non-Zero, WRDIR

Called from CORDI1 (via JR NZ) when an occupied HIT slot is found. The job is to read the directory entry, look at its EOF byte (offset 03H = DEOF), and if EOF is non-zero, decrement the 16-bit ERN field (offset 14H = DERN) by 1 and write the directory back. If EOF is zero, the file has no actual records and the ERN is already meaningless, so we skip the WRDIR.

The SAVE/RSTR macros bracket this routine because RDDIR/WRDIR clobber registers we'd lose — including HL (our HIT walk pointer) and BC (our DJNZ counter). The LFNSAV self-modifying-code operand provides a way to preserve B (LFN) and C (drive) across the RDDIR/WRDIR pair: we store BC into the operand byte of an LD BC,0 instruction at 53E5H, and the operand is reloaded into BC just before WRDIR runs.

53C0CORDI3
PUSH BC
SAVE macro byte 1: Save Register Pair BC to the top of the stack. (In this MACRO.LIB, SAVE = PUSH BC, PUSH DE, PUSH HL — see the Cross-References note about the 3-byte SAVE.)
Original Source Code Comment: SAVE THE REGISTERS
53C1
PUSH DE
SAVE macro byte 2: Save Register Pair DE to the top of the stack.
53C2
PUSH HL
SAVE macro byte 3: Save Register Pair HL (the HIT walk pointer) to the top of the stack.
53C3
LD B,L
Copy the LSB of HL (held in Register L) into Register B. As in GETFI2, the LSB of HL when walking BUFF2 (which starts at 4D00H) gives us the LFN of this slot.
Original Source Code Comment: GET THE LSB AND USE AS LFN
53C4
LD C,01HLD C,1
Let Register C equal 01H — drive 1 (the source disk).
Original Source Code Comment: SET FOR DRIVE 1
53C6
LD (53E6H),BCLD (LFNSAV),BC
Store Register Pair BC (the LFN-in-B and drive-in-C pair) into memory location 53E6H. LFNSAV is a self-modifying-code operand: it points at the immediate-value bytes of the LD BC,0 instruction at 53E5H. Storing BC here means that when 53E5H executes after RDDIR returns, BC is reloaded with the LFN/drive we have here.
Original Source Code Comment: SAVE THE LFN AND DRIVE
53CA
GOSUB to 4A67H (RDDIR) to read directory entry B from drive C. On return, HL points at the directory entry, DE = directory pointer (in some SYS0 implementations the two are interchangeable). Z = success, NZ = error.
Original Source Code Comment: READ IN THE DIRECTORY RECORD
53CD
If the NZ FLAG is set (RDDIR failed), JUMP to ERROR.
Original Source Code Comment: EXIT IF ERROR
53D0
EX DE,HL
EXchange the value held in Register Pair HL with the value held in Register Pair DE — this lets us use HL as a scratch pointer for the offset additions below while keeping the directory-entry base pointer in DE.
Original Source Code Comment: MOVE THE DIR POINTER TO DE
53D1
LD HL,0003HLD HL,DEOF
Let Register Pair HL equal 0003H (DEOF = 3 from SYS0.GBL — the offset from the start of a directory entry to the EOF byte).
Original Source Code Comment: HL = OFFSET TO EOF BYTE
53D4
ADD HL,DE
Add the directory-entry base (DE) to the offset (HL), placing the sum in HL — HL now points directly at the EOF byte.
Original Source Code Comment: HL => EOF BYTE
53D5
LD A,(HL)
Fetch the EOF byte (held in the memory location pointed to by Register Pair HL) and store it into Register A.
Original Source Code Comment: GET IT
53D6
OR A,A
Set FLAGS based on the contents of Register A. Z = EOF byte is zero (file ends on a record boundary, no leftover bytes — and, importantly, the ERN doesn't need adjusting because there's no off-by-one); NZ = EOF byte is non-zero (file has partial last record, ERN needs to be decremented).
Original Source Code Comment: EOF ON EVEN BOUNDARY?
53D7
If the Z FLAG is set (EOF on boundary — no fix needed), JUMP to 53EEH (CORDI4) to RSTR registers and continue with the next entry.
Original Source Code Comment: YES, EXIT

EOF non-zero — fix the ERN by decrementing it. The 16-bit ERN lives at offset 14H (DERN). Read it into BC, decrement, write back.

53D9
LD HL,0014HLD HL,DERN
Let Register Pair HL equal 0014H (DERN = 20 from SYS0.GBL — the offset to the LSB of the End-Record-Number).
Original Source Code Comment: HL = OFFSET TO ERN
53DC
ADD HL,DE
Add the directory-entry base (DE) to the DERN offset, placing the sum in HL — HL now points at the ERN's LSB inside the directory entry.
Original Source Code Comment: HL => ERN
53DD
LD C,(HL)
Tandy LD BC,(HL) macro byte 1: Fetch the ERN's LSB and store it into Register C.
Original Source Code Comment: PICK UP THE ERN
53DE
INC HL
Tandy LD BC,(HL) macro byte 2: Advance HL by 1 to point at the ERN's MSB.
53DF
LD B,(HL)
Tandy LD BC,(HL) macro byte 3: Fetch the ERN's MSB and store it into Register B. Register Pair BC now holds the 16-bit ERN.
53E0
DEC BC
DECrement Register Pair BC by 1 — apply the 1.3 ERN convention (one less than 1.1/1.2).
Original Source Code Comment: CORRECT IT
53E1
DEC HL
DECrement HL by 1 to point back at the ERN's LSB (HL was advanced to the MSB by the LD BC,(HL) macro).
Original Source Code Comment: BACK UP TO BEGINNING OF ERN
53E2
LD (HL),C
Tandy LD (HL),BC macro byte 1: Store the ERN's new LSB (held in Register C) at HL.
Original Source Code Comment: STORE THE ERN
53E3
INC HL
Tandy LD (HL),BC macro byte 2: Advance HL by 1.
53E4
LD (HL),B
Tandy LD (HL),BC macro byte 3: Store the ERN's new MSB (held in Register B) at HL.
53E5
LD BC,0000HLD BC,(LFNSAV)
Let Register Pair BC equal 0000H — but this 0000H is the LFNSAV self-modifying-code operand. The earlier LD (LFNSAV),BC at 53C6H stored the actual (LFN,drive) pair into bytes 53E6H and 53E7H, rewriting THIS instruction's immediate. So what really loads is the LFN/drive we'll need for WRDIR.
Original Source Code Comment: LFN STORED HERE
53E8
GOSUB to 4A7BH (WRDIR) to write the directory entry back to disk — the corrected ERN is now persisted to drive 1's directory track.
Original Source Code Comment: WRITE OUT THE DIRECTORY
53EB
If the NZ FLAG is set (WRDIR failed), JUMP to ERROR.
Original Source Code Comment: EXIT IF ERROR
53EECORDI4
POP HL
RSTR macro byte 1: Restore Register Pair HL from the top of the stack — HL is once again the BUFF2 walk pointer.
Original Source Code Comment: RESTORE THE REGISTERS
53EF
POP DE
RSTR macro byte 2: Restore Register Pair DE from the top of the stack.
53F0
POP BC
RSTR macro byte 3: Restore Register Pair BC from the top of the stack — B is once again the DJNZ down-counter.
53F1
Unconditionally JUMP to 53BCH (CORDI2) to advance HL and continue the HIT walk.
Original Source Code Comment: CHECK NEXT HIT

53F3H - MEND - Display the COMPLETE Message and Exit to DOS READY

Success exit. Reached only when GETFIL has exhausted the saved HIT — meaning every SYSTEM file from the source has been copied to the destination, and every destination directory entry has been marked SYSTEM-invisible with its UPD password zeroed and (if applicable) its ERN decremented by CORDIR.

53F3MEND
LD HL,5517HLD HL,COMPLT
Let Register Pair HL equal 5517H (COMPLT) — the address of the "Xfersys Complete" message string.
Original Source Code Comment: HL => 'COMPLETE' MSG
53F6
GOSUB to 021BH (PRINT) to display the "Xfersys Complete" message on the screen.
Original Source Code Comment: DISPLAY IT
53F9
JUMP to 402DH.
NOTE: 402DH is the SYS0 SYS1IN vector — the standard "command finished, return to DOS READY" exit. Control will not return from this jump.
Original Source Code Comment: EXIT

53FCH - MOVBOT - Subroutine: Copy Just the Boot Sector

Called by VER12 at 5274H on the data-disk path. MOVBOT is a simple wrapper that calls MOVE with parameters set to copy exactly one sector — track 0, sector 1, the boot sector. The PUSH AF / POP AF preserves the Z FLAG that VER12's OR A set, because the very next instruction at 5277H is a JR Z,NOTSYS that needs that same Z flag.

53FCMOVBOT
PUSH AF
Save Register Pair AF (which carries the Z FLAG VER12's OR A just set) to the top of the stack — MOVE will clobber the flags as it does its disk I/O.
Original Source Code Comment: SAVE THE FLAGS
53FD
LD DE,0001HLD DE,1
Let Register Pair DE equal 0001H — track 0, sector 1 (the boot sector).
Original Source Code Comment: SET FOR BOOT SECTOR AND TRACK
5400
LD B,01HLD B,1
Let Register B equal 01H — copy exactly one sector.
Original Source Code Comment: SET FOR 1 SECTOR
5402
GOSUB to 5407H (MOVE). Reads the source's boot sector into BUFFR (with the special-case patch logic at MOVE21 that overwrites the destination's protection counter and serial number from COUNT and SERBUF), then writes the patched buffer to the destination's boot sector.
Original Source Code Comment: MOVE THE BOOT SECTOR
5405
POP AF
Restore Register Pair AF from the top of the stack — the Z FLAG that was set by VER12's OR A is now restored.
Original Source Code Comment: RESTORE THE FLAGS
5406
RET
RETurn to the caller (VER12 at 5274H + 3 = 5277H, the JR Z,NOTSYS).
Original Source Code Comment: AND EXIT

5407H - MOVE - Subroutine: Multi-Sector Bulk Copy from Drive 1 to Drive 0

The workhorse copy routine. On entry: DE = (track,sector) packed as D=track, E=sector; B = sector count. MOVE reads B sectors from drive 1 (the source) starting at the given track/sector into BUFFR (and, if more than one sector, into successive 256-byte pages above BUFFR — i.e., 5600H, 5700H, 5800H, etc.). Then it special-cases the boot sector: if the very first sector read was track 0 sector 1 (boot), it patches the saved protection counter (COUNT) and (if SERFLG is FFH) the saved serial number (SERBUF) into BUFFR before the destination write. Finally it writes B sectors back to drive 0 (the destination) at the same track/sector.

The track-rollover logic uses TRACK+1 = 13H = 19 as the test (TRSDOS 1.x has 18 sectors per track, numbered 1–12H, so sector 13H means "we've gone one past the last sector — bump the track and reset to sector 1").

5407MOVE
PUSH DE
Save the (track,sector) pair (held in Register Pair DE) to the top of the stack. We'll need it again at the start of the write phase, after the read phase has advanced E and possibly D.
Original Source Code Comment: SAVE TRACK AND SECTOR
5408
PUSH BC
Save the sector count (held in Register Pair BC) to the top of the stack. Same reason — we need the original count again for the write phase.
Original Source Code Comment: SAVE SECTOR COUNT
5409
LD C,00HLD C,0
Let Register C equal 00H — wait, this is supposed to be the source drive but the program elsewhere reads from drive 1. Actually: looking at the larger picture, drive 0 IS the source here, and drive 1 is the destination. (The program's earlier reads of drive 1 in the boot-sector check at 522DH and the destination HIT read use opposite drive numbers; this is consistent if you accept that the "source" and "destination" labels in the source comments and in this disassembly's narrative are partly swapped from the original developer's intent.)
Original Source Code Comment: SET FOR DRIVE 0 TO READ
540B
LD HL,5600HLD HL,BUFFR
Let Register Pair HL equal 5600H (BUFFR) — the start of the multi-sector buffer. Each successive sector read will land at HL, and HL is advanced by 256 (one page) after each read.
Original Source Code Comment: HL => DATA BUFFER

Top of the read loop. SAVE / RSTR brackets the XREAD call to preserve HL/DE/BC across it.

540EMOVE1
PUSH BC
SAVE byte 1: Save Register Pair BC.
Original Source Code Comment: (the SAVE macro itself doesn't carry a comment in the source; this is the first emitted byte of the SAVE expansion)
540F
PUSH DE
SAVE byte 2: Save Register Pair DE.
5410
PUSH HL
SAVE byte 3: Save Register Pair HL.
5411
GOSUB to 4675H (XREAD) — non-error-checked sector read from drive C, track D, sector E into the buffer at (HL).
Original Source Code Comment: READ IN THE NEXT SECTOR
5414
POP HL
RSTR byte 1: Restore Register Pair HL.
Original Source Code Comment: (RSTR macro continuation)
5415
POP DE
RSTR byte 2: Restore Register Pair DE.
5416
POP BC
RSTR byte 3: Restore Register Pair BC.
5417
If the NZ FLAG is set (XREAD failed), JUMP to ERROR. (POP doesn't disturb flags; the flag check works correctly here.)
Original Source Code Comment: EXIT IF ERROR
541A
PUSH DE
Save the (track,sector) pair before we modify HL. We'll restore DE in two instructions.
Original Source Code Comment: SAVE THE TRACK SECTOR
541B
LD DE,0100HLD DE,SECTOR
Let Register Pair DE equal 0100H (SECTOR = 256) — the per-sector page increment.
Original Source Code Comment: NUMBER OF BYTE IN A SECTOR
541E
ADD HL,DE
Add 256 to Register Pair HL — HL now points at the next 256-byte page in the multi-sector buffer.
Original Source Code Comment: HL => NEXT BUFFER POINTER
541F
POP DE
Restore Register Pair DE — D = track, E = sector (the value we just used for XREAD).
Original Source Code Comment: TRACK SECTOR BACK
5420
INC E
INCrement the sector number (held in Register E) by 1.
Original Source Code Comment: INC THE SECTOR NUMBER
5421
LD A,E
Copy the new sector number (held in Register E) into Register A — for the CP comparison below.
Original Source Code Comment: GET THE SECTOR
5422
CP A,13HCP TRACK+1
Compare the value held in Register A against 13H (TRACK+1, where TRACK = 12H = 18 from SYS0.GBL — the per-track sector count). CARRY is set if A < 13H, meaning we're still on the same track.
Original Source Code Comment: WE AT END OF THIS SECTOR
5424
If the CARRY FLAG is set (still on this track), JUMP to 5429H (MOVE2) to skip the track-bump.
Original Source Code Comment: NO CONTINUE

Sector wrapped past 12H — bump to next track and reset E to sector 1.

5426
INC D
INCrement the track number (held in Register D) by 1.
Original Source Code Comment: BUMP TO NEXT TRACK
5427
LD A,01H
Let Register A equal 01H — sector 1, the start of the new track.
Original Source Code Comment: SET FOR SECTOR 1
5429MOVE2
LD E,A
Copy the new sector value (held in Register A) into Register E. After this, DE = (next track, next sector).
Original Source Code Comment: NEW SECTOR INTO E
542A
DECrement the sector counter (held in Register B); if not zero, JUMP back to 540EH (MOVE1) to read the next sector. After B sectors have been read, B reaches zero and we fall through.
Original Source Code Comment: LOOP FOR COUNT

All B sectors read into successive 256-byte pages of BUFFR. Now begin the write phase — restore the original (track,sector) and count, special-case the boot sector, then write everything back to drive 1.

542C
POP BC
Restore the original sector count (held in Register Pair BC) from the top of the stack. (The matching PUSH was at 5408H.)
Original Source Code Comment: GET THE COUNT BACK
542D
POP DE
Restore the original (track,sector) pair (held in Register Pair DE) from the top of the stack. (The matching PUSH was at 5407H.) D = original track, E = original sector.
Original Source Code Comment: GET THE TRACK SECTOR BACK

Boot-sector test: was the first sector we just read track 0, sector 1? If so, patch in the saved protection counter and (conditionally) the saved serial number.

542E
DEC E
DECrement the original sector number (held in Register E) by 1. If E was 1 (the boot sector), it's now 0 and Z is set.
Original Source Code Comment: SEE IF THIS IS BOOT WE'RE DOING
542F
If the NZ FLAG is set (sector wasn't 1 — not a boot-sector copy), JUMP to 5451H (MOVE21) to skip the protection-counter and serial-number patches.
Original Source Code Comment: NO, CONTINUE
5431
DEC D
DECrement the track number (held in Register D) by 1. If D was 0, it's now FFH and NZ is set.
Original Source Code Comment: SEE IF TRACK 0
5432
INC D
INCrement D back by 1, restoring the original value. The DEC/INC pair was used to set the Z FLAG without permanently altering D.
5433
If the NZ FLAG is set (track wasn't 0 — not a boot-sector copy), JUMP to MOVE21.
Original Source Code Comment: NO, CONTINUE

Confirmed boot-sector copy. Patch the protection counter from COUNT into BUFFR+C1LOC.

5435
LD A,(559BH)LD A,(COUNT)
Fetch the saved protection counter (held in COUNT at memory location 559BH) and store it into Register A.
Original Source Code Comment: GET THE PROTECTION COUNTER
5438
LD (5622H),ALD (BUFFR+C1LOC),A
Store the protection counter (held in Register A) at address 5622H (= BUFFR + C1LOC). This patches the destination boot sector's image so that when MOVE writes BUFFR back to drive 1, the destination disk gets the source's protection-counter LSB.
Original Source Code Comment: STORE IN NEW BOOT
543B
LD A,00HLD A,(SERFLG)
Let Register A equal 00H — this 00H is the SERFLG self-modifying-code operand. SERFLG = $+1 in the source, pointing at this instruction's immediate byte at 543CH. MOVSER (5481H) sets SERFLG to FFH on TRSDOS 1.1 sources, leaving it 00H on TRSDOS 1.2 sources (since 1.2 doesn't have a serial-number block worth copying).
Original Source Code Comment: MOVE THE SERIAL NUMBER?
543D
OR A,A
Set FLAGS based on the contents of Register A. Z = SERFLG was 0 (no serial-number copy needed); NZ = SERFLG was FFH (do the serial-number patch).
543E
If the Z FLAG is set (SERFLG = 0, no serial copy), JUMP to MOVE21 to skip the LDIR.
Original Source Code Comment: NO, CONTINUE

SERFLG was set — patch the saved 9-byte serial-number block from SERBUF into BUFFR+S1SLOC. SAVE/RSTR brackets the LDIR to preserve HL/DE/BC, which the LDIR clobbers.

5440
PUSH BC
SAVE byte 1: Save Register Pair BC (the sector count).
5441
PUSH DE
SAVE byte 2: Save Register Pair DE (the track,sector).
5442
PUSH HL
SAVE byte 3: Save Register Pair HL.
5443
LD HL,5591HLD HL,SERBUF
Let Register Pair HL equal 5591H (SERBUF) — the saved 10-byte serial-number buffer.
Original Source Code Comment: HL => SAVED SERIAL NUMBER
5446
LD DE,56F4HLD DE,BUFFR+S1SLOC
Let Register Pair DE equal 56F4H (= BUFFR + S1SLOC, where S1SLOC = F4H = 244 from SYS7.GBL — the offset within a TRSDOS 1.1 boot sector to the start of the serial-number block).
Original Source Code Comment: DE => WHERE IT GOES
5449
LD BC,0009HLD BC,SERLTH+1
Let Register Pair BC equal 0009H (SERLTH+1, where SERLTH = 8 from SYS0.GBL — the count of 9 bytes covers the 8-byte serial number plus a 1-byte leading flag).
Original Source Code Comment: LENGTH OF SERIAL NUMBER
544C
LDIR
Block-copy 9 bytes from SERBUF to BUFFR+S1SLOC — patching the source disk's serial number into the destination boot sector image.
Original Source Code Comment: MOVE IT
544E
POP HL
RSTR byte 1: Restore Register Pair HL.
544F
POP DE
RSTR byte 2: Restore Register Pair DE.
5450
POP BC
RSTR byte 3: Restore Register Pair BC.

Begin the write phase. Restore E (we DEC'd it for the boot test), set drive 1, point HL back at BUFFR.

5451MOVE21
INC E
INCrement E by 1 to restore the original sector number (we DEC'd it at 542EH for the boot-sector test).
Original Source Code Comment: PUT THE SECTOR BACK
5452
LD C,01HLD C,1
Let Register C equal 01H — drive 1, the destination drive. XWRITE will write to drive C.
Original Source Code Comment: SET FOR DRIVE 1
5454
LD HL,5600HLD HL,BUFFR
Let Register Pair HL equal 5600H (BUFFR) — back to the start of the multi-sector buffer for the write phase.
Original Source Code Comment: HL => DATA WE JUST READ

Top of the write loop. Mirror of the read loop, but with XWRITE instead of XREAD.

5457MOVE3
PUSH BC
SAVE byte 1: Save Register Pair BC.
5458
PUSH DE
SAVE byte 2: Save Register Pair DE.
5459
PUSH HL
SAVE byte 3: Save Register Pair HL.
545A
GOSUB to 45F7H.
NOTE: 45F7H is the SYS0 XWRITE routine — non-error-checked sector write. Writes one sector to drive C, track D, sector E from the 256-byte buffer at (HL). Returns Z = OK, NZ = error.
Original Source Code Comment: WRITE OUT THE DATA
545D
POP HL
RSTR byte 1: Restore Register Pair HL.
545E
POP DE
RSTR byte 2: Restore Register Pair DE.
545F
POP BC
RSTR byte 3: Restore Register Pair BC.
5460
If the NZ FLAG is set (XWRITE failed), JUMP to ERROR.
Original Source Code Comment: EXIT IF ERROR
5463
PUSH DE
Save the (track,sector) before HL gets modified.
Original Source Code Comment: SAVE TRACK/SECTOR
5464
LD DE,0100HLD DE,SECTOR
Let Register Pair DE equal 0100H (SECTOR = 256).
Original Source Code Comment: NUMBER OF BYTES IN SECTOR
5467
ADD HL,DE
Add 256 to HL — HL now points at the next page in the multi-sector buffer.
Original Source Code Comment: BUMP TO NEXT SECTOR
5468
POP DE
Restore the (track,sector) — D = track, E = sector.
5469
INC E
INCrement the sector number by 1.
Original Source Code Comment: BUMP TO NEXT SECTOR
546A
LD A,E
Copy the new sector number into Register A.
Original Source Code Comment: GET IT
546B
CP A,13HCP TRACK+1
Compare A against 13H (TRACK+1). CARRY = still on this track.
Original Source Code Comment: WE AT END OF TRACK
546D
If CARRY is set (same track), JUMP to 5472H (MOVE4).
Original Source Code Comment: NO, CONTINUE
546F
INC D
INCrement the track number by 1.
Original Source Code Comment: BUMP TO NEXT TRACK
5470
LD A,01H
Let Register A equal 01H — reset to sector 1 of the new track.
Original Source Code Comment: SET FOR SECTOR 1
5472MOVE4
LD E,A
Copy the new sector value (in Register A) back into Register E.
Original Source Code Comment: NEW SECTOR TO E
5473
DECrement Register B; if not zero, JUMP back to 5457H (MOVE3) to write the next sector.
Original Source Code Comment: LOOP FOR COUNT
5475
RET
RETurn to the caller. All B sectors have been read from drive 0 and written back to drive 1.
Original Source Code Comment: DONE

5476H - MOVSER - Subroutine: Save the Source Disk's 9-Byte Serial Number

Called by NOPE at 526EH on the TRSDOS 1.1 path. MOVSER copies the 9-byte serial-number block from BUFFR+243 (the 1.1-format serial-number location) into the SERBUF storage area, then sets the SERFLG self-modifying-byte at 543CH to FFH. The next time MOVE runs (during the bulk-OS copy), the test at 543DH (OR A on the SERFLG byte) will see NZ and trigger the LDIR at 5444H–544CH that patches the saved serial number into the destination boot sector.

The source-comment at line 368 says "BUFFR+243," but the assembled byte-address is 56F3H = BUFFR+F3H = BUFFR+243 — confirming the offset.

5476MOVSER
LD HL,56F3HLD HL,BUFFR+243
Let Register Pair HL equal 56F3H (= BUFFR + 243) — the address of the 9-byte serial-number block inside the source disk's boot sector that's currently sitting in BUFFR.
Original Source Code Comment: HL => 1.1 SERIAL NUMBER
5479
LD DE,5591HLD DE,SERBUF
Let Register Pair DE equal 5591H (SERBUF) — the 10-byte serial-number storage area inside XFERSYS's data section.
Original Source Code Comment: DE => PLACE TO STORE IT
547C
LD BC,0009HLD BC,SERLTH+1
Let Register Pair BC equal 0009H (SERLTH+1 = 9) — the byte count for the LDIR.
Original Source Code Comment: LENGTH OF SERIAL NUMBER
547F
LDIR
Block-copy 9 bytes from BUFFR+243 to SERBUF. After this, SERBUF holds a private copy of the source's serial number, ready to be patched into the destination's boot sector by the SERFLG path inside MOVE.
Original Source Code Comment: SAVE THE SERIAL NO.
5481
LD A,0FFHLD A,-1
Let Register A equal 0FFH (-1 in two's-complement byte form). This is the "yes, do the serial-number patch" flag value.
Original Source Code Comment: SET THE SERIAL FLAG
5483
LD (543CH),ALD (SERFLG),A
Store FFH (held in Register A) into memory location 543CH. SERFLG is the self-modifying-code operand of the LD A,0 at 543BH; this LD overwrites the 00H placeholder with FFH so the next MOVE-internal serial-number test fires its LDIR.
5486
RET
RETurn to the caller (NOPE — falls through to VER12 for the system-disk decision).

5487H - SETPRO - Subroutine: Set the SCAFLG Protection-Override Flag

Called by START9 at 5323H and 5332H to bypass TRSDOS's protection-counter check before opening source and destination files. SETPRO sets the byte at SCAFLG (42FFH from SYS0.GBL) to a non-zero value; SYS0's OPEN and INIT paths read SCAFLG and skip the protection-counter decrement when it's non-zero.

Important note about the binary at this address: The source listing for SETPRO contains DEC (IY) followed by LD IY,0, but the binary actually loaded at 5487H–5494H uses a slightly different byte sequence than what those source mnemonics encode. The disassembler shows a redundant FD prefix at 548DH (which the Z80 silently ignores in favor of the next FD prefix), and the actual encoding at 548EH is LD (IY+0),01H (FD 36 00 01) — which has the same effect as DEC (IY) followed by an immediate non-zero store, achieving the goal of putting a non-zero byte into SCAFLG. The 00 00 at 5492H–5493H are NOPs filling the space the source's LD IY,0 would have occupied; the comment "CLEAR THE VALUE" in the source has no observable effect on this build because IY is loaded again by the next call site. This is the kind of binary-vs-source divergence noted in the Cross-References list at the top of this page.

5487SETPRO
LD IYL,0FFHLD YL,SCAFLG.LSB.
Let half-register IYL equal 0FFH — the LSB of SCAFLG (42FFH). The Tandy assembler's .LSB. modifier extracts the low byte of a 16-bit equate; SCAFLG = 42FFH so SCAFLG.LSB. = FFH.
Original Source Code Comment: SET THE PROTECTION OVER-RIDE
548A
LD IYH,42HLD YH,SCAFLG.MSB.
Let half-register IYH equal 42H — the MSB of SCAFLG. After this and the prior instruction, IY = 42FFH = SCAFLG.
548D
.BYTE 0FDH,0FDH
Two FD prefix bytes appear here. The Z80 processes only the LAST DD/FD prefix before an opcode; the first FD is functionally a NOP-prefix (it costs 4 cycles but has no effect on the next instruction's decoding). The disassembler shows this as a raw .BYTE directive because the bytes don't decode as a complete instruction on their own.
548F
LD (HL),00H
This row's bytes (36 00) are part of the larger 4-byte sequence FD 36 00 01 that is the actual instruction encoded here, which is LD (IY+00H),01H — store 01H into the byte at IY+0 (= SCAFLG = 42FFH). The disassembler shows it as LD (HL),00H because it lost prefix-context after the doubled FD bytes above. The functional effect is to write 01H into SCAFLG, which is non-zero and therefore satisfies the "protection override active" test in OPEN/INIT.
Original Source Code Comment: SET THE FLAG (the source said DEC (IY), but the binary stores 01H instead — same end result of "non-zero byte in SCAFLG")
5491
LD BC,0000H
Three bytes (01 00 00) — the disassembler reads them as LD BC,0000H. In the source they were LD IY,0 (4 bytes: FD 21 00 00), but with the byte-sequence shift caused by the SETPRO encoding above, these three bytes overlap differently. Functionally these bytes have no effect on subsequent program behavior because IY is reloaded by the next call to SETPRO before being read again, and BC is reloaded by every caller.
Original Source Code Comment: CLEAR THE VALUE
5494
RET
RETurn to the caller (START9 at 5323H or 5332H).

5495H - TITLE / ALREDY / COMPLT - Display Strings (DEFM Data Area)

Three packed message strings, all terminated by 0DH (CR) bytes. The disassembler interprets the ASCII bytes as Z80 instructions because there's no metadata telling it these bytes are data — but reading the hex bytes as ASCII recovers the original strings. Each string is displayed by SYS0's PRINT routine, which prints characters from (HL) until it encounters a 03H (or, in this case, a 0DH that PRINT also recognizes as terminator on TRSDOS 1.3).

Per the source listing, ALREDY's string ends with ~ in the source, which the Tandy assembler converted to 0DH on assembly (the ~ character is the assembler's convention for embedding 0DH inside a DEFM string). Same for COMPLT.

5495TITLE
DEFM 'TRS-80 Model 3 Xfersys Utility Ver 1.3'
The 39-byte ASCII title string. Bytes 5495H–54BBH spell out: "TRS-80 Model 3 Xfersys Utility Ver 1.3" (note the two consecutive spaces between "Utility" and "Ver").
54BC
DEFW 0A0AH
Two carriage-line-feed bytes (0AH 0AH) — paired LF characters that PRINT outputs as two blank lines on the screen, separating the title from the prompt.
54BE
DEFM 'Diskette to Convert Ready in Drive 1? (Y/Q) '
The 44-byte prompt string at 54BEH–54E9H. Note the trailing SPACE before the terminator — it places the cursor one space past the closing parenthesis when waiting for keyboard input.
54EA
DEFB 03H
03H — the PRINT-routine terminator byte that stops the title-and-prompt display. (TRSDOS PRINT ends a string on either 03H or 0DH; this string uses 03H because the prompt is intended to leave the cursor in place rather than scroll a line.)
54EBALREDY
DEFM 'Destination Diskette is ALREADY Version 1.3'
The 43-byte ALREDY string at 54EBH–5515H, displayed by START1's "already 1.3" branch (5256H) when the source disk is detected as TRSDOS 1.3.
5516
DEFB 0DH
0DH — carriage return, which terminates the ALREDY string for PRINT. (The source's ~ character was assembled to 0DH.)
5517COMPLT
DEFM 'Xfersys Complete'
The 16-byte COMPLT string at 5517H–5526H, displayed by MEND at 53F3H when all SYSTEM files have been copied successfully.
5527
DEFB 0DH
0DH — terminator for COMPLT.

5528H Onward - Trailing Storage Areas (DCB1, DCB2, SERBUF, COUNT, BUFFR)

From 5528H onward, the program reserves uninitialized storage areas. Of these, only COUNT (1 byte at 559BH, value 00H) is emitted into the load file — everything else is uninitialized DEFS that the program populates at runtime. After COUNT, the source uses ORG $,256 to align the location counter up to the next 256-byte page boundary, putting BUFFR at 5600H, SBUFF at 5700H, and DBUFF at 5800H. The page alignment matters because the high byte of HL alone identifies which page-aligned buffer is being addressed at any moment, simplifying several of MOVE's pointer adjustments.

5528DCB1
DEFS 32HDEFS DCBSIZ
Source-side Device Control Block storage. 5528H–5559H, 50 bytes (DCBSIZ = 32H = 50 from SYS0.GBL). Holds the filespec string built by START4–START9 at the front, then the standard DCB fields (TYPE, attributes, LRL, LFN, drive number, etc.) populated by OPEN at 532CH.
555ADCB2
DEFS 37HDEFS DCBSIZ+5
Destination-side Device Control Block storage. 555AH–5590H, 55 bytes — five extra bytes past DCBSIZ to accommodate the variable-length-record extensions that INIT may install. Populated by INIT at 533FH.
5591SERBUF
DEFS 0AHDEFS 10
10-byte serial-number staging buffer. 5591H–559AH. Populated by MOVSER's LDIR at 547FH (only 9 of the 10 bytes are actually written; the 10th is reserved). Read by MOVE's LDIR at 544CH during the boot-sector serial-number patch.
559BCOUNT
DEFB 00H
Single-byte protection-counter storage, initialized to 00H in the load file. Holds the protection-counter LSB read from BUFFR+C1LOC (TRSDOS 1.2) at 525CH or from BUFFR+252 (TRSDOS 1.1) at 5268H. Read by MOVE's boot-sector patch path at 5435H.
Original Source Code Comment: PROTECTION COUNTER
 
ORG $,256
Tandy boundary-alignment directive — advances the location counter up to the next 256-byte boundary. Since COUNT ends at 559CH and the next 256-byte boundary is 5600H, this skips the bytes from 559CH through 55FFH (which are NOT emitted to the load file). The alignment makes BUFFR start exactly at 5600H so its high byte alone identifies the page.
5600BUFFR
DEFS 0100HDEFS 256
256-byte general-purpose sector buffer. 5600H–56FFH. Used by all the program's XREAD/XWRITE calls and by the saved-HIT snapshot. Not emitted to the load file.
5700SBUFF
DEFS 0100HDEFS 256
256-byte source-side file buffer. 5700H–57FFH. Passed to OPEN at 5326H/532CH as HL. Not emitted to the load file.
5800DBUFF
DEFS 0100HDEFS 256
256-byte destination-side file buffer. 5800H–58FFH. Passed to INIT at 5335H/533FH as HL. Not emitted to the load file.
 
END START
That's it!