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:

  • The shipped binary's SAVE macro is 3-byte, not 4-byte. The MACRO.LIB used at build time defines SAVE as PUSH BC / PUSH DE / PUSH HL (no PUSH AF) and RSTR as the symmetric 3-byte pop. This is verified by binary inspection at MOVE1 (540EH): the bytes are C5 D5 E5, not C5 D5 E5 F5. Some other TRSDOS 1.3 sources use a 4-byte form; XFERSYS uses the 3-byte form. RSTR is correspondingly 3 bytes (POP HL / POP DE / POP BC).
  • 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)