TRS-80 DOS - LDOS v5.3.1 for the Model I - BACKUP/CMD Disassembled

Page Customization

Summary:

LDOS 5.3.1 BACKUP/CMD Disassembly - The Disk Backup Utility (Model I)

BACKUP/CMD duplicates the data on one diskette onto another. It is a transient utility: DOS loads it, it runs, and it exits through @EXIT at 402DH, leaving nothing resident behind it. The command syntax is BACKUP :s [TO] :d (parm,parm) or BACKUP [-]partspec w/wcc:s TO :d (parm,parm), and the parameters are DATE, INV, MOD, MPW, NEW, OLD, QUERY, SYS and X.

The manual describes three kinds of backup, and the code implements exactly three. A mirror image is a cylinder-for-cylinder copy, attempted when the size, density and number of sides match on the two disks. A backup by class is a file-for-file copy, chosen by the operator whenever a partspec or any parameter other than X or MPW appears on the command line. A backup reconstruct is the same file-for-file copy, chosen by the utility itself when the two disks are not compatible enough for a mirror image.

What makes this file unusual is that those are not three code paths through one program. They are three separate programs carried in one load module, and two of them are stored at addresses they never execute at. The command-line front end is assembled to run at 5A00H, which is where the transfer address points, and both backup engines are also assembled to run at 5A00H. The mirror-image engine is stored at 5E00H and the file-by-file engine at 6300H. Once the front end has finished parsing and has decided which kind of backup is required, the selector at 6C00H block-moves the chosen engine down over the front end that has just finished with it, and jumps to 5A00H. The engine sections of this page are therefore annotated at the addresses the code actually runs at, with the load address stated in each section lead-in.

Everything above the moved engine is work space. After the block move, the selector rounds the end address up to the next page boundary and stores it at 5285H, and from that point on 5285H is the base of the buffers and HIGH$ at 4049H is their top. The two Granule Allocation Table copies and the general sector buffer sit at fixed addresses inside the file's own unloaded gap at 55F8H-59FFH.

BACKUP does almost all of its disk work itself rather than through the file system. It copies the drive's ten-byte Drive Control Table entry through the resident GETDCT routine at 478FH, points Register Pair IY at it, and reaches the floppy driver with a JP (IY) through the jump instruction at DCT+00, exactly as FORMAT/CMD does. Only two supervisor requests are issued, both as three-byte tail-call stubs: code 0E4H to hash a master password and code 84H to make SYS2/SYS resident before the copying starts.

The utility also suppresses the real-time clock. The mirror-image transfer is timed against the drive, so the engine saves the byte at 4012H inside the resident interrupt vector and writes a RET over it for the duration of the copy. That one byte is the entire reason the utility signs off with Note: Real time clock no longer accurate.

File Format and Load Map

BACKUP/CMD is 5880 bytes on disk: one type-1FH copyright record carrying Copyright 1991 MISOSYS, Inc., All rights reserved, then 27 type-01 load records, then a type-02 transfer address of 5A00H. The records place 5717 bytes into memory in six runs separated by five unloaded gaps. The gaps are working storage and are represented on this page as reserved space.

Address RangeBytesContents
5200H-521CH29Parameter result cells, the assembled selection flags and the partspec masks
521DH-5284H104Not loaded. The filespec build area and the date scratch fields
5285H-55F7H883The free-space pointer word, the shared subroutine block and the common message block
55F8H-59FFH1032Not loaded. The file control block, the two Granule Allocation Table copies and the sector buffer
5A00H-5D51H850The command-line front end. The transfer address points here
5D52H-5DFFH174Not loaded
5E00H-6208H1033The mirror-image engine as loaded. It runs at 5A00H-5E08H
6209H-62FFH247Not loaded
6300H-6A47H1864The file-by-file engine as loaded. It runs at 5A00H-6147H
6A48H-6BFFH440Not loaded
6C00H-7021H1058The engine selector, the two supervisor-request stubs, the drive-geometry reader, the @PARAM table and the front-end message block

The two engine blocks are moved by a fixed amount, so every address inside them is the loaded address less a constant. The mirror-image engine is moved down by 0400H and the file-by-file engine by 0900H.

Loaded AtRuns AtMoved By
5E00H-6208H5A00H-5E08HDown 0400H by the LDIR at 6C0FH, which copies 0500H bytes
6300H-6A47H5A00H-6147HDown 0900H by the LDIR at 6C5BH, which copies 0900H bytes

Variable and Buffer List

Address RangePurpose
5200H
1 byte
Date-selection mode. Bit 7 is set when a lower bound was given, bit 0 when an upper bound was given. Both clear means the DATE parameter was not used
5201H-5202H
2 bytes
The lower date bound, packed into the two-byte form an LDOS directory record uses
5203H-5204H
2 bytes
The upper date bound, in the same packed form
5205H-520CH
8 bytes
The eight-character filename mask taken from the partspec, blank filled. A 24H dollar sign in any position matches any character
520DH-520FH
3 bytes
The three-character extension mask, blank filled, with the same wildcard rule
5210H
1 byte
The attribute selection byte assembled from the switches. Bit 6 is set by SYS, bit 4 by MOD and bit 3 by INV, so that it can be tested directly against the directory record's own attribute byte
5211H
1 byte
Non-zero when the partspec was prefixed with a minus sign, which inverts the sense of the filename and extension match
5212H
1 byte
Counts the destination diskettes used, incremented each time the operator is asked for a fresh formatted disk
5213H-5214H
2 bytes
The NEW parameter result cell, filled by @PARAM. Non-zero selects only files that are not already on the destination
5215H-5216H
2 bytes
The OLD parameter result cell. Non-zero selects only files that already exist on the destination
5217H-5218H
2 bytes
The MOD parameter result cell. Non-zero selects only files whose modification flag is set
5219H-521AH
2 bytes
The QUERY parameter result cell. 521AH is also cleared at run time by a reply of C to the query prompt, which turns querying off for the rest of the backup
521BH-521CH
2 bytes
The SYS parameter result cell. Non-zero includes system files and makes the destination a SYSTEM disk
521DH-523CH
32 bytes
Not loaded. The filespec is built here for display and for @INIT, as name, slash, extension, terminated by 03H
523DH-525CH
32 bytes
Not loaded. The destination file control block
525DH-525FH
3 bytes
Not loaded. The keyboard reply buffer, and the three date fields the DATE parser builds downwards as year, day, month
527DH-5284H
8 bytes
Not loaded. The system date read from @DATE, used to stamp the destination disk
5285H-5286H
2 bytes
The free-space pointer. Ships as 0000H and is filled in by the selector at 6C5DH with the first page boundary above the moved engine. Every buffer the engines allocate is measured from here up to HIGH$ at 4049H
52C1H
1 byte
The saved copy of the resident interrupt vector byte from 4012H, put back when the backup ends
52CAH-52CBH
2 bytes
The X parameter result cell, held inside the shared subroutine block. Non-zero allows a backup with no system disk in drive 0
531BH
1 byte
The source drive number, planted as the operand of an LD A instruction
5388H
1 byte
The destination drive number, planted the same way
53D5H
1 byte
The current drive cell. Bits 2-0 are the drive number, bit 7 records that the source disk is believed to be mounted and bit 6 that the destination disk is
53DCH
1 byte
The source drive number exclusive-ORed with the destination drive number. Zero means a single-drive backup, and that one byte governs every prompt-and-swap decision the utility makes
5487H-55F7H
369 bytes
The shared message block, holding the clock note, the write-protect messages, the three insert-disk prompts, the two ALERT messages and the two closing messages
55F8H-56FFH
264 bytes
Not loaded. Geometry scratch and the source file control block
5700H-57FFH
256 bytes
Not loaded. The source disk's Granule Allocation Table sector, addressed at true GAT offsets throughout: 5760H is the lock-out map, 57CCH the configuration word, 57CEH the master-password hash and 57D0H the eight-character disk name
5800H-58FFH
256 bytes
Not loaded. The destination disk's Granule Allocation Table sector, at the same offsets
5900H-59FFH
256 bytes
Not loaded. The general sector buffer, used for directory sectors, the Hash Index Table, the boot sector and collected keyboard replies
6C01H
1 byte
The backup-by-class flag, planted by the front end at 5B2DH as the operand of the LD A that opens the selector. Non-zero means a file-by-file backup
6D25H
1 byte
The second byte of the CB-prefixed instruction at 6D24H, computed at run time so that one instruction body can set or clear bit [drive] of the resident dating flag at 475DH for any of eight drives
6D72H
1 byte
The second byte of the CB-prefixed instruction at 6D71H, computed the same way to test bit [drive] of 475DH
6D74H-6DF3H
128 bytes
The @PARAM keyword table: sixteen six-character blank-padded keywords, each followed by the two-byte address of its result cell, terminated by 00H
6DF4H-7021H
558 bytes
The front-end message block, the sign-on banner, the maximum-days-per-month table at 7006H and the date-format error message

The Parameter Table

The table at 6D74H is the standard LDOS @PARAM form: a six-character blank-padded keyword followed by the address of a two-byte result cell, repeated until a 00H byte ends the list. A long form and its abbreviation point at the same cell, and the cells are not all in one place - some are in the small data run at 5200H and some are the operand fields of instructions out in the front end.

KeywordResult CellEffect
MPW5C94HPasses the source disk's master password so that the prompt is not issued
SYS, S521BHIncludes system files, and reserves the fifteen directory slots a SYSTEM disk needs, the two FORMAT creates plus thirteen for the /SYS overlays
INV, I5AE4HIncludes files that are invisible in the directory
MOD, M5217HSelects only files modified since the last backup
QUERY, Q5219HAsks before each file is copied, showing its date and modification flag
X52CAHAllows a backup with no system disk in drive 0, subject to the residency test at 6C22H
DATE, D5AA2HSelects files by modification date, in any of the four forms the manual lists
NEW, N5213HCopies only files that are not already on the destination
OLD, O5215HCopies only files that already exist on the destination

Major Routine List

AddressRoutine
5287HNormal Ending
Displays the real-time-clock note and Backup complete, then leaves through @EXIT at 402DH. Every successful path ends here
529BHDisk Error Exit
Restores the interrupt vector, reports the error code left in Register A through @ERROR at 4409H, and falls into the abort ending
52ACHAbort Ending
Displays Backup aborted and leaves through @ABORT at 4030H
52C0HRestore The Interrupt Vector
Puts the saved byte back at 4012H, re-enables interrupts, and clears the work space from the free-space pointer up to HIGH$
5327HMount The Source Disk
Returns at once if bit 7 of the current drive cell already says the source is in place. Otherwise it prompts, waits, re-reads the Granule Allocation Table and compares the ten-byte Pack ID against the saved copy, refusing with the source ALERT message on a mismatch
5394HMount The Destination Disk
The mirror of the routine above, using bit 6 of the current drive cell, and verifying that the first byte of the directory sector is still 76H
53E0HFlashing Prompt And Wait
Displays a prompt and alternates it against a delay, calling the resident keyboard poller at 4DA6H each pass. BREAK or ENTER ends the wait; the PAUSE bit discards the loop's own return address so that the routine returns out of the whole prompt
544CHClear The Keyboard Flags
Masks KFLAG$ at 4423H with 0F8H. The resident poller only ever sets bits 0 to 2, so the caller has to clear them
5455H-5470HDisk Operation Stubs
Eight two-byte entries loading the operation code for present-test, select, restore, controller wait, write, deleted-mark write, read and verify, each falling through a jump into the common tail
5472HDisk Operation Tail
Puts the operation in Register B and the drive from the current drive cell in Register C and in LDRV$ at 4308H, loads Register A with 20H and reaches the floppy driver with the JP (IY) at 5485H
5A00HFront End Entry
The transfer address. Sets the stack to 41E0H, displays the sign-on, parses the partspec, the two drive numbers and the parameters, decides mirror image against reconstruct, and hands over to the selector
5C72HAsk About A Cylinder Count Mismatch
Displays the question, reads one character and returns with the Z flag set when the reply was Y
5C89HMaster Password Check
Accepts the disk at once when its master-password hash is 42E0H, the hash of PASSWORD. Otherwise it prompts, hashes the reply through the 0E4H stub, and accepts either the bypass constant 113DH or the disk's own hash
5CB1HFilespec Character Filter
Accepts a dollar sign as the wildcard, folds lower case to upper case, and stops the field at any character outside the digits and the upper-case letters
5CCCHDate Parameter Parser
Splits the quoted date string on the minus sign into a lower and an upper bound, validates each against the maximum-days table, and packs the result into the directory date form
5D20HSplit A Date Group
Breaks month, day and year apart on the slash and stores them downwards at 525FH, 525EH and 525DH
5D35HCollect A Two-Digit Number
Builds a decimal value from up to two ASCII digits, returning with the carry flag clear on a bad character
6C00HEngine Selector
Reads the backup-by-class flag planted in its own first instruction, block-moves the mirror-image engine or the file-by-file engine down to 5A00H, records the free-space pointer and jumps to 5A00H
6C22HX Parameter Residency Test
Reads the word at 4BDFH inside the resident supervisor-call dispatcher. If it still holds 4BF5H, nothing has been made resident and the backup is refused. Otherwise four bytes below that address are tested, one for each of the four overlays the file-by-file path needs
6C68HCollect And Hash A Password
Calls the string collector and falls into the 0E4H supervisor stub
6C6BHPassword Hash Stub
LD A,0E4H followed by RST 28H, with no RET. The dispatcher discards the address the restart pushed, so this three-byte stub is a complete subroutine
6C6EHSYS2 Pre-Load Stub
LD A,84H followed by RST 28H. Sub-function 00H is rejected by SYS2 with a RET, so the only effect is that the overlay becomes resident
6C71HCollect Eight Characters
Takes the field either from the command line or, when none was given, from the keyboard, blank fills it to eight characters and folds it to upper case
6CBFHRead A Drive's Geometry
Tests that the unit is fitted, restores and seeks the head, waits a full index-hole revolution against TIMER$ for readiness, reads the Granule Allocation Table through DIRCYL and RDSSEC, and unpacks the configuration word into the Drive Control Table copy
6D4FHPoll For The Index Hole
Reads the controller status through RSELCT and gives up when TIMER$ reaches the deadline in Register D
6D61HTest The Extended Dating Flag
Computes the second byte of a BIT instruction from the drive number, plants it at 6D72H and executes it against 475DH, so that one instruction body serves all eight drives

Cross-Reference Notes

BACKUP is a transient utility, so nothing calls into it. It calls out to the resident core and to the ROM. From the Model I ROM it uses 002BH to test the keyboard, 0033H to display one character, 0040H to collect a line and 0060H to delay.

From the resident core it uses the supervisor vectors @EXIT 402DH, @ABORT 4030H, @ERROR 4409H, @INIT 4420H, @OPEN 4424H, @CLOSE 4428H, @KILL 442CH, @READ 4436H, @VER 443CH, @REW 443FH, @POSN 4442H, @DSPLY 4467H, @DATE 4470H, @PARAM 4476H, @LOGOT 447BH, @CKDRV 44B8H and @MULT 44C1H, together with the disk primitives RSELCT 4759H, SEEK 475EH and GETDCT 478FH, the directory helpers RDSSEC 4B45H and DIRCYL 4B65H, and the keyboard-status poller at 4DA6H. All of these are documented on the SYS0/SYS page.

It reads five resident cells and writes two. It reads SFLAG$ at 430FH bit 5 to find out whether a job is in effect, KFLAG$ at 4423H bits 0 to 2 for BREAK, PAUSE and ENTER, HIGH$ at 4049H for the top of memory, TIMER$ at 4040H for the drive-ready timeout, the month cell at 4046H to see whether a date has been established, and the word at 4BDFH inside the supervisor dispatcher for the X parameter residency test. It writes the drive number into LDRV$ at 4308H before each disk operation, the per-drive extended-dating flag at 475DH once a pack's Granule Allocation Table has been read, and the interrupt vector byte at 4012H for the duration of a mirror-image transfer.

Two supervisor requests are issued, both documented on the main LDOS disassembly page: code 0E4H, the password hash serviced by SYS2/SYS, and code 84H, the pre-load that makes the same overlay resident. The file-by-file engine additionally depends on SYS3/SYS for @CLOSE, SYS8/SYS for granule allocation and SYS10/SYS for @KILL, reached through the resident vectors rather than directly, which is why the X parameter insists that those four overlays are resident before it will run.

The disk layout BACKUP works against is the one FORMAT/CMD lays down, and the two files are the two halves of one story about the Hash Index Table: FORMAT reserves two directory slots for BOOT/SYS and DIR/SYS, and BACKUP claims the remaining thirteen, for SYS0/SYS through SYS12/SYS, when it turns a formatted DATA disk into a SYSTEM disk, checking all fifteen as one table. The per-drive extended-dating flag at 475DH that BACKUP writes is the same flag DATECONV/CMD exists to make meaningful.

Disassembly:

5200H - Parameter Result Cells and Selection Flags

The first load record places twenty-nine bytes of data, not code, at 5200H. Five of them are the result cells the @PARAM service at 4476H writes into when it recognises a keyword on the command line, and the rest are the selection criteria the front end assembles from the partspec. The whole block ships as blanks and zeroes and is filled in during parsing, so the listing's rendering of these bytes as instructions is a misreading of data. Every byte here is read by the file-by-file engine when it decides whether a given directory record is to be copied.

5200
DEFB 00H 00
Date Selection Mode
Bit 7 is set when the DATE parameter supplied a lower bound and bit 0 when it supplied an upper bound. Both bits clear means no date filtering. The two bits are set by the date parser at 5AADH and 5ACBH and tested by the file-by-file engine at 649BH and 64AFH.
5201-5202
DEFW 0000H 00 00
Lower Date Bound
The earlier of the two dates from a DATE range, already packed into the two-byte form an LDOS directory record uses at offsets 01H and 02H, so that it can be compared against a record without unpacking either value. Written by the front end at 5AB5H.
5203-5204
DEFW 0000H 00 00
Upper Date Bound
The later of the two dates, in the same packed form, written by the front end at 5AD3H. When only one date was given the parser stores it in whichever of the two cells the form of the parameter calls for.
5205-520C
DEFM " " 20 20 20 20 20 20 20 20
Filename Mask
The eight-character file name taken from the partspec, blank filled. A 24H dollar sign in any position is the wildcard character and matches anything. Blanks throughout mean no name was given, which is how the front end at 5AF9H decides whether a partspec was present at all.
520D-520F
DEFM " " 20 20 20
Extension Mask
The three-character extension from the partspec, blank filled, with the same dollar-sign wildcard rule. Filled in only when a 2FH slash was seen on the command line, by the loop at 5A26H.
5210
DEFB 00H 00
Attribute Selection Byte
Assembled at 5AF6H from the three switches so that it can be tested directly against a directory record's own attribute byte at offset 00H. Bit 6 is set by SYS, bit 4 by MOD and bit 3 by INV, which are the same bit positions those meanings occupy in the record.
5211
DEFB 00H 00
Negated Partspec Flag
Set to the 2DH minus sign itself by the front end at 5A1AH when the command line began with one. Non-zero inverts the sense of the filename and extension match, so that the files which do not match are the ones copied.
5212
DEFB 0FEH FE
Date Format Conversion Flag
Ships as 0FEH and reaches zero only when the source pack is in the old date format and the destination pack is in the x.3 extended format. The front end at 5BBFH sets it to 0FFH when the test at 6D61H says bit [drive] of the resident dating flag at 475DH is clear for the source, and 5C4AH increments it when the same test says the bit is set for the destination. A value of 00H is therefore the exact case the manual describes, in which the old access password is dropped and x.3 date and time information is established on the copy, and in which system files may not be copied by class.
5213-5214
DEFW 0000H 00 00
NEW Parameter Result Cell
Written by @PARAM at 4476H when the NEW or N keyword appears. Non-zero means copy only those files which are not already present on the destination, which the engine determines from the error code @INIT returns.
5215-5216
DEFW 0000H 00 00
OLD Parameter Result Cell
Non-zero means copy only those files which already exist on the destination. NEW and OLD are tested together at 64F5H, and one of them being set is what makes the engine care about the result of @INIT rather than ignoring it.
5217-5218
DEFW 0000H 00 00
MOD Parameter Result Cell
Non-zero selects only files whose modification flag, bit 6 of directory offset 01H, is set. Folded into the attribute selection byte at 5B15H and tested against the record at 6439H.
5219-521A
DEFW 0000H 00 00
QUERY Parameter Result Cell
Non-zero asks before each file is copied. The high byte at 521AH is also cleared at run time by 65EFH when the operator answers the query prompt with C, which turns querying off for the rest of the backup exactly as the manual describes.
521B-521C
DEFW 0000H 00 00
SYS Parameter Result Cell
Non-zero includes system files in the copy and makes the file-by-file engine build a SYSTEM disk, claiming the thirteen further directory slots the /SYS overlays occupy on top of the two FORMAT/CMD already created. Tested at 5AD9H when the attribute byte is assembled and again at 6354H when the engine decides whether to read the Hash Index Table.

Unloaded Storage
The image resumes at 5285H. The 104 bytes from 521DH to 5284H are not written by any load record; they are the filespec build area at 521DH, the destination file control block at 523DH, the keyboard reply and date scratch fields at 525DH and the system date buffer at 527DH. They are working storage and hold whatever was in memory when the utility was loaded.

5285H - Endings, Disk Mounting and the Disk Operation Stubs

The second load record opens with a two-byte cell and then runs into the block of subroutines that both backup engines share. Everything here survives the block move that brings an engine down to 5A00H, which is why the endings, the prompt machinery and the disk operation stubs live in this range rather than inside either engine. The block holds the three ways the utility can finish, the routine that puts the resident interrupt vector back and clears the work space, the two routines that mount and verify the source and destination disks, the flashing prompt loop, and the eight two-byte stubs that reach the floppy driver.

5285-5286
DEFW 0000H 00 00
Free Space Pointer
The base of every buffer the engines use. It ships as 0000H and is filled in by the engine selector at 6C61H with the first page boundary above the engine that was just moved down to 5A00H. The top of the same region is HIGH$ at 4049H. The routine at 52DFH clears the whole of it, and the file-by-file engine reads it whenever it needs a sector buffer.
5287
LD HL,55D7H 21 D7 55
Point Register Pair HL at the message at 55D7H, a 0AH line feed followed by Backup complete and a 0DH terminator. This is the entry every successful backup reaches, from both engines.
528A
PUSH HL E5
PUSH the message address in Register Pair HL onto the stack so that it survives the housekeeping call on the next line, which uses Register Pair HL for its own block move.
528B
GOSUB to 52C0H to put the resident interrupt vector byte back at 4012H, re-enable interrupts and clear the work space from the free-space pointer at 5285H up to HIGH$ at 4049H.
528E
POP HL E1
POP the saved message address back into Register Pair HL, which again points at the completion message at 55D7H.
528F
GOSUB to the resident @DSPLY vector at 4467H to display the completion message that Register Pair HL points at.
5292
LD HL,5487H 21 87 54
Point Register Pair HL at the message at 5487H, a 0AH line feed followed by Note: Real time clock no longer accurate. It is displayed unconditionally because the mirror-image engine writes a RET over the resident interrupt vector byte at 4012H for the duration of its transfer.
5295
GOSUB to the resident @DSPLY vector at 4467H to display the real-time-clock note.
5298
JUMP to the resident @EXIT vector at 402DH to return to DOS. Register Pair HL is not zeroed first, so BACKUP relies on @EXIT rather than on the @CMNDR error-code convention that the library commands use.
529B
LD A,11H 3E 11
Load Register A with 11H, LDOS error 17, which is the code the engines use when a directory or system sector cannot be written. Execution falls into the next line, whose three bytes are read as one instruction from here and as a different instruction when entered one byte later.
529D
LD BC,203EH 01 3E 20
Overlapping Instruction
Entered from 529BH these three bytes are a harmless load of Register Pair BC that leaves Register A holding 11H. Entered at 529EH the same bytes are a two-byte load of Register A with 20H. Either way execution continues at 52A0H with an error number in Register A.
529E
LD A,20H 3E 20
Load Register A with 20H, LDOS error 32, illegal drive number. This is the overlapping entry into the two bytes above, reached from the front end at 5A4BH and 5A74H when a drivespec digit is 8 or greater. Execution continues at 52A0H exactly as it does from 529BH.

Overlapping Code
The three bytes 01 3E 20 at 529DH are read two ways. Entered from 529BH the leading 01H makes them a load of Register Pair BC, which changes nothing and leaves the error number 11H that 529BH put in Register A. Entered at 529EH the 01H is skipped and the remaining two bytes load Register A with 20H. One three-byte sequence therefore provides two error exits, and both of them fall into the common handler at 52A0H.

52A0
PUSH AF F5
PUSH Register A, the error number, and the flags onto the stack so that the number survives the housekeeping call on the next line. This address is the general disk-error exit and is reached from both engines with the error code already in Register A.
52A1
GOSUB to 52C0H to restore the interrupt vector byte at 4012H and clear the work space before anything is reported, so that the machine is back in a sane state whatever the error was.
52A4
POP AF F1
POP the error number back into Register A, together with the flags that were saved with it.
52A5
OR 0C0H F6 C0
OR Register A with 0C0H to set bits 7 and 6 of the error number. Bit 6 tells @ERROR to display the message only, and bit 7 tells it to return to the caller rather than aborting, so that BACKUP can print its own ending afterwards.
52A7
GOSUB to the resident @ERROR vector at 4409H, which loads SYS4/SYS and displays the text for the error number in Register A. It returns here because bit 7 was set on the previous line.
52AA
JUMP forward to 52B7H to display the real-time-clock note and abort, skipping the Backup aborted message that the next lines display, since @ERROR has already said what went wrong.
52AC
LD HL,55E8H 21 E8 55
Point Register Pair HL at the message at 55E8H, a 0AH line feed followed by Backup aborted. This address is the plain abort entry, reached whenever the operator declines a prompt or a check fails without a DOS error number.
52AF
PUSH HL E5
PUSH the message address in Register Pair HL onto the stack. This address is a second entry to the abort path: callers that have their own message load Register Pair HL themselves and jump straight here, which is how every specific refusal message in the file is issued.
52B0
GOSUB to 52C0H to restore the interrupt vector byte at 4012H and clear the work space.
52B3
POP HL E1
POP the saved message address back into Register Pair HL.
52B4
GOSUB to the resident @LOGOT vector at 447BH, which displays the 0DH-terminated string at Register Pair HL. @LOGOT rather than @DSPLY is used here because the abort messages end the line.
52B7
LD HL,5487H 21 87 54
Point Register Pair HL at the real-time-clock note at 5487H. Both the error exit and the abort exit converge here, so the note is displayed on every path out of the utility.
52BA
GOSUB to the resident @DSPLY vector at 4467H to display the clock note.
52BD
JUMP to the resident @ABORT vector at 4030H. Under a job this is what ends the job with Job aborted, which is why the utility refuses single-drive and query work under JCL rather than prompting.
52C0
LD A,00H 3E 00
Self-Modifying Code
Load Register A from the operand byte at 52C1H, which is the saved copy of the resident interrupt vector byte from 4012H. The mirror-image engine writes the original byte there at 5AD6H before it overwrites 4012H with a RET; the operand ships as 00H, meaning nothing has been saved.
52C2
OR A B7
OR Register A with itself to test the saved byte without changing it. If it is zero the interrupt vector was never replaced and there is nothing to put back.
52C3
If the Z FLAG has been set, the saved byte at 52C1H is zero and no interrupt vector was displaced, so JUMP forward to 52C9H to skip the restore.
52C5
EI FB
Enable interrupts. They were disabled at 5AD2H in the mirror-image engine before the vector byte was replaced, so this is the matching half of that.
52C6
LD (4012H),A 32 12 40
Store Register A, the original interrupt vector byte saved at 52C1H, back at 4012H inside the resident interrupt handler, so that the real-time clock task runs again.
52C9
LD DE,0000H 11 00 00
Load Register Pair DE from the operand at 52CAH. That operand is the X parameter result cell, which the resident @PARAM service at 4476H fills with 0FFFFH when the X keyword appears on the command line and leaves at 0000H when it does not.
52CC
INC E 1C
INCrement Register E by 1. If the X cell held 0FFH in its low byte the result is zero and the Z FLAG is set, which is the test for the X parameter having been given.
52CD
If the NZ FLAG has been set, the X parameter was not used, so JUMP forward to 52D8H. The system disk never left drive 0 and there is nothing to ask the operator to do.
52CF
XOR A AF
Clear Register A to zero. The X parameter was used, so the system disk was displaced and has to be asked for.
52D0
LD (53DCH),A 32 DC 53
Self-Modifying Code
Store zero at 53DCH, the operand of the load at 53DBH inside the prompt routine. That cell normally holds the source drive number exclusive-ORed with the destination drive number, and forcing it to zero makes the prompt routine behave as though this were a single-drive backup, so that it will actually stop and ask.
52D3
GOSUB to 52FBH to display Insert SYSTEM disk <ENTER> and wait until a disk that @CKDRV accepts is in the drive. This is the prompt the manual promises at the end of an X-parameter backup.
52D6
JUMP forward to 52DFH to clear the work space, skipping the non-X path.
52D8
LD A,(53DCH) 3A DC 53
Load Register A from 53DCH, the source drive number exclusive-ORed with the destination drive number. Zero means both drivespecs named the same unit.
52DB
OR A B7
OR Register A with itself to set the flags on that value without changing it.
52DC
If the Z FLAG has been set, source and destination were the same drive, so GOSUB to 52F6H to ask for the system disk back. On a two-drive backup the system disk was never removed and no prompt is issued.
52DF
LD DE,(5285H) ED 5B 85 52
Load Register Pair DE from the free-space pointer at 5285H, which the engine selector filled in at 6C61H with the first page boundary above the engine it moved down. This is the bottom of the work space.
52E3
LD A,D 7A
Load Register A from Register D, the high byte of the free-space pointer.
52E4
OR E B3
OR Register A with Register E, the low byte, so that both halves of the pointer are tested together.
52E5
RET Z C8
RETurn if the Z FLAG has been set. A pointer of 0000H means the selector never ran, which happens when the utility aborts during command-line parsing, and there is no work space to clear.
52E6
LD HL,(4049H) 2A 49 40
Load Register Pair HL from HIGH$ at 4049H, the resident cell holding the highest address available to a transient program. This is the top of the work space.
52E9
XOR A AF
Clear Register A to zero, both as the fill byte for the block move below and to clear the CARRY FLAG for the subtraction on the next line.
52EA
SBC HL,DE ED 52
SUBtract Register Pair DE, the bottom of the work space, from Register Pair HL, the top, with the CARRY FLAG cleared. Register Pair HL now holds the number of bytes to clear.
52EC
LD B,H 44
Load Register B with Register H, the high byte of the byte count, moving the count towards Register Pair BC where a block move expects it.
52ED
LD C,L 4D
Load Register C with Register L, the low byte of the byte count.
52EE
LD H,D 62
Load Register H with Register D, starting to copy the free-space pointer from Register Pair DE into Register Pair HL, which is where a block move expects its source address.
52EF
LD L,E 6B
Load Register L with Register E, completing the copy. Register Pair HL now points at the first byte of the work space and Register Pair DE still points at the same place.
52F0
INC DE 13
INCrement Register Pair DE by 1 so that the destination is one byte above the source, which is what turns the block move below into a fill.
52F1
LD (HL),A 77
Store Register A, which is zero, at the address in Register Pair HL. This seeds the fill with the byte that the block move will propagate upwards.
52F2
DEC BC 0B
DECrement Register Pair BC by 1, because the byte just written is one of the bytes the count covers.
52F3
LDIR ED B0
Block move Register Pair BC bytes from the address in Register Pair HL to the address in Register Pair DE. With the destination one above the source this copies the single zero byte all the way up, clearing the whole work space from the free-space pointer to HIGH$.
52F5
RET C9
RETurn to the caller with the interrupt vector restored and the work space cleared.
52F6
LD A,(5302H) 3A 02 53
Self-Modifying Code
Load Register A from the operand at 5302H, which is the drive number the system-disk prompt uses and which the routine below writes. A non-zero value means the system disk is already known to be mounted.
52F9
OR A B7
OR Register A with itself to set the flags on the drive number without changing it.
52FA
RET NZ C0
RETurn if the NZ FLAG has been set. The system disk is already in place, so there is no prompt to issue.
52FB
LD A,(53D5H) 3A D5 53
Load Register A from 53D5H, the current drive cell, whose low three bits are the drive number and whose bits 7 and 6 record whether the source and destination disks are believed to be mounted.
52FE
LD (5314H),A 32 14 53
Self-Modifying Code
Store the current drive cell at 5314H, the operand of the load at 5313H. That is where the routine will find the value to put back once the operator has finished with the system disk.
5301
LD A,00H 3E 00
Self-Modifying Code
Load Register A from the operand at 5302H, the system drive number, which is drive 0 on an ordinary machine. This is also the cell the test at 52F6H reads.
5303
OR 20H F6 20
OR Register A with 20H to set bit 5, the marker this file uses for the system disk, so that the prompt routine can tell a system-disk request apart from a source or destination request.
5305
PUSH HL E5
PUSH Register Pair HL onto the stack to preserve the caller's pointer across the prompt.
5306
LD HL,54D6H 21 D6 54
Point Register Pair HL at the prompt at 54D6H, which begins with 1DH to return the cursor to the start of the line and 1EH to clear to the end of it, then reads Insert SYSTEM disk <ENTER>.
5309
GOSUB to 53D4H, the common prompt entry, which compares the new drive state in Register A against the current drive cell and, if they differ, flashes the prompt until the operator presses ENTER.
530C
LD C,00H 0E 00
Load Register C with 00H, the drive number that the drive check on the next line is to test.
530E
GOSUB to the resident @CKDRV vector at 44B8H, which verifies that a formatted, readable disk is present in the drive named by Register C. It returns with the Z FLAG set when the drive is ready.
5311
POP HL E1
POP the caller's pointer back into Register Pair HL.
5312
RET Z C8
RETurn if the Z FLAG has been set. A readable system disk is now in drive 0 and the caller can carry on.
5313
LD A,00H 3E 00
Self-Modifying Code
Load Register A from the operand at 5314H, the current drive cell that was saved at 52FEH. The drive check failed, so the state is put back and the prompt is issued again.
5315
LD (53D5H),A 32 D5 53
Store Register A, the restored drive state, back at 53D5H, the current drive cell.
5318
LOOP BACK to 5301H to build the system-drive request again and re-issue the prompt, which is what makes the utility keep asking until the right disk appears.
531A
LD A,00H 3E 00
Self-Modifying Code
Load Register A from the operand at 531BH, which the front end filled in at 5A4EH with the source drive number taken from the command line.
531C
OR 80H F6 80
OR Register A with 80H to set bit 7, the marker for the source disk, giving the drive-state byte the prompt routine compares against the current drive cell.
531E
PUSH HL E5
PUSH Register Pair HL onto the stack to preserve the caller's pointer across the prompt.
531F
LD HL,54F5H 21 F5 54
Point Register Pair HL at the prompt at 54F5H, Insert SOURCE disk <ENTER>, again prefixed with 1DH and 1EH so that it overwrites whatever was on the line.
5322
GOSUB to 53D4H, the common prompt entry. If the source disk is already the mounted one it returns at once; otherwise it flashes the prompt and waits.
5325
POP HL E1
POP the caller's pointer back into Register Pair HL.
5326
RET C9
RETurn to the caller with the source disk selected.
5327
LD A,(53D5H) 3A D5 53
Load Register A from 53D5H, the current drive cell, to find out which disk the utility believes is in the drive.
532A
BIT 7,A CB 7F
Test bit 7 of Register A, the flag that says the source disk is the mounted one.
532C
If the NZ FLAG has been set, bit 7 was already set and the source disk is believed to be in place, so LOOP BACK to 531AH to select its drive without asking the operator for anything.
532E
GOSUB to 531AH to prompt for the source disk and select its drive. The disk has genuinely changed, so everything below verifies that the operator produced the right one.
5331
LD A,(53DCH) 3A DC 53
Load Register A from 53DCH, the source drive number exclusive-ORed with the destination drive number.
5334
OR A B7
OR Register A with itself to set the flags on that value.
5335
RET NZ C0
RETurn if the NZ FLAG has been set. The two drivespecs named different units, so no disk was swapped and there is nothing to verify.
5336
GOSUB to 545CH, the disk-operation stub for operation 04H, which restores the head to cylinder 0 before the directory is read.
5339
PUSH BC C5
PUSH Register Pair BC onto the stack to preserve the caller's registers across the verification read.
533A
PUSH DE D5
PUSH Register Pair DE onto the stack for the same reason.
533B
PUSH HL E5
PUSH Register Pair HL onto the stack for the same reason.
533C
LD HL,5900H 21 00 59
Point Register Pair HL at 5900H, the general sector buffer, which is where the directory sector will be read.
533F
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, so that Register D is cylinder 0 and Register E is sector 0.
5342
GOSUB to 546CH, the disk-operation stub for operation 09H, to read cylinder 0 sector 0 into the sector buffer.
5345
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H with the status-derived error number that the driver left in Register A.
5348
POP HL E1
POP the caller's pointer back into Register Pair HL.
5349
POP DE D1
POP the caller's value back into Register Pair DE.
534A
POP BC C1
POP the caller's value back into Register Pair BC.
534B
LD A,(5900H) 3A 00 59
Load Register A from 5900H, the first byte of the sector just read. On a formatted LDOS disk cylinder 0 sector 0 is the boot sector, whose first byte is zero.
534E
OR A B7
OR Register A with itself to set the flags on that byte.
534F
If the NZ FLAG has been set the first byte is not zero, so the disk in the drive is not a formatted LDOS disk at all. JUMP forward to 5381H to clear the mounted flag and ask again.
5351
PUSH BC C5
PUSH Register Pair BC onto the stack to preserve the caller's registers across the second read.
5352
PUSH DE D5
PUSH Register Pair DE onto the stack for the same reason.
5353
PUSH HL E5
PUSH Register Pair HL onto the stack for the same reason.
5354
LD D,(IY+09H) FD 56 09
Load Register D with the byte at IY+09H, the directory cylinder from this drive's Drive Control Table entry, which the geometry reader at 6CBFH filled in from the disk's own configuration.
5357
LD E,00H 1E 00
Load Register E with 00H, sector 0 of that cylinder, which is the Granule Allocation Table.
5359
LD HL,5900H 21 00 59
Point Register Pair HL at 5900H, the general sector buffer, as the destination for the read.
535C
GOSUB to 546CH, the disk-operation stub for operation 09H, to read the Granule Allocation Table sector.
535F
CP 06H FE 06
Compare Register A against 06H. Directory sectors are written with a deleted data address mark, so the driver reports error 06H on a successful read of one and anything else means a genuine failure.
5361
If the NZ FLAG has been set the read did not return the expected deleted-mark status, so JUMP to 529BH to exit with LDOS error 17.
5364
LD HL,57CEH 21 CE 57
Point Register Pair HL at 57CEH, the master-password hash inside the saved copy of the source disk's Granule Allocation Table. The ten bytes from here are the hash followed by the eight-character disk name, which together are the Pack ID.
5367
LD DE,59CEH 11 CE 59
Point Register Pair DE at 59CEH, the same offset inside the sector just read from whatever disk the operator has now put in the drive.
536A
LD B,0AH 06 0A
Load Register B with 0AH, the ten bytes of the Pack ID, as the loop counter.
536C
LD A,(DE) 1A
Fetch a byte of the new disk's Pack ID from the address in Register Pair DE into Register A.
536D
CP (HL) BE
Compare Register A against the byte at the address in Register Pair HL, the same position in the saved source Pack ID. If they match, the Z FLAG is set.
536E
If the NZ FLAG has been set the two Pack IDs differ, so JUMP forward to 5378H to tell the operator this is the wrong disk.
5370
INC DE 13
INCrement Register Pair DE by 1 to step to the next byte of the new disk's Pack ID.
5371
INC HL 23
INCrement Register Pair HL by 1 to step to the next byte of the saved source Pack ID.
5372
DECrement Register B by 1 and LOOP BACK to 536CH while it is not zero, comparing all ten bytes.
5374
POP HL E1
POP the caller's pointer back into Register Pair HL. The Pack IDs matched, so this is the right source disk.
5375
POP DE D1
POP the caller's value back into Register Pair DE.
5376
POP BC C1
POP the caller's value back into Register Pair BC.
5377
RET C9
RETurn to the caller with the correct source disk mounted and verified.
5378
LD HL,5538H 21 38 55
Point Register Pair HL at the message at 5538H, which begins with 1DH and 1EH to rewrite the line and reads * A L E R T * That's not the same source disk!.
537B
GOSUB to the resident @DSPLY vector at 4467H to display the alert.
537E
POP HL E1
POP the caller's pointer back into Register Pair HL.
537F
POP DE D1
POP the caller's value back into Register Pair DE.
5380
POP BC C1
POP the caller's value back into Register Pair BC.
5381
XOR A AF
Clear Register A to zero, ready to wipe the mounted flags.
5382
LD (53D5H),A 32 D5 53
Store zero at 53D5H, the current drive cell. Clearing bits 7 and 6 makes the utility believe that neither the source nor the destination disk is mounted, which forces the prompt to be issued again.
5385
LOOP BACK to 5327H to ask for the source disk once more. The routine will keep asking until the Pack ID matches.
5387
LD A,00H 3E 00
Self-Modifying Code
Load Register A from the operand at 5388H, which the front end filled in at 5A7CH with the destination drive number taken from the command line.
5389
OR 40H F6 40
OR Register A with 40H to set bit 6, the marker for the destination disk, giving the drive-state byte the prompt routine compares against the current drive cell.
538B
PUSH HL E5
PUSH Register Pair HL onto the stack to preserve the caller's pointer across the prompt.
538C
LD HL,5514H 21 14 55
Point Register Pair HL at the prompt at 5514H, Insert DESTINATION disk <ENTER>, prefixed with 1DH and 1EH.
538F
GOSUB to 53D4H, the common prompt entry, which returns at once if the destination disk is already the mounted one.
5392
POP HL E1
POP the caller's pointer back into Register Pair HL.
5393
RET C9
RETurn to the caller with the destination disk selected.
5394
LD A,(53D5H) 3A D5 53
Load Register A from 53D5H, the current drive cell.
5397
BIT 6,A CB 77
Test bit 6 of Register A, the flag that says the destination disk is the mounted one.
5399
If the NZ FLAG has been set the destination disk is believed to be in place, so LOOP BACK to 5387H to select its drive without asking.
539B
GOSUB to 5387H to prompt for the destination disk and select its drive.
539E
LD A,(53DCH) 3A DC 53
Load Register A from 53DCH, the source drive number exclusive-ORed with the destination drive number.
53A1
OR A B7
OR Register A with itself to set the flags on that value.
53A2
RET NZ C0
RETurn if the NZ FLAG has been set. Two different drives were named, so no disk was swapped and nothing needs verifying.
53A3
GOSUB to 545CH, the disk-operation stub for operation 04H, to restore the head to cylinder 0.
53A6
PUSH BC C5
PUSH Register Pair BC onto the stack to preserve the caller's registers.
53A7
PUSH DE D5
PUSH Register Pair DE onto the stack for the same reason.
53A8
PUSH HL E5
PUSH Register Pair HL onto the stack for the same reason.
53A9
LD HL,5900H 21 00 59
Point Register Pair HL at 5900H, the general sector buffer.
53AC
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, cylinder 0 and sector 0.
53AF
GOSUB to 546CH, the disk-operation stub for operation 09H, to read cylinder 0 sector 0.
53B2
POP HL E1
POP the caller's pointer back into Register Pair HL.
53B3
POP DE D1
POP the caller's value back into Register Pair DE.
53B4
POP BC C1
POP the caller's value back into Register Pair BC.
53B5
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H with the driver's error number in Register A.
53B8
LD A,(5900H) 3A 00 59
Load Register A from 5900H, the first byte of the sector just read.
53BB
CP 76H FE 76
Compare Register A against 76H. The destination-preparation code at 5A75H in the mirror-image engine writes 76H into that byte, so finding it again proves this is the same destination disk the backup started on.
53BD
RET Z C8
Self-Modifying Code
RETurn if the Z FLAG has been set, the marker byte matching and the right destination disk being in place. The file-by-file engine writes 0C9H over this byte at 6311H, turning the instruction into an unconditional RET, because a class backup never writes the 76H marker and must not test for it.
53BE
PUSH HL E5
PUSH Register Pair HL onto the stack to preserve the caller's pointer across the alert.
53BF
PUSH DE D5
PUSH Register Pair DE onto the stack for the same reason.
53C0
LD HL,556AH 21 6A 55
Point Register Pair HL at the message at 556AH, * A L E R T * That's not the same destination disk!.
53C3
GOSUB to the resident @DSPLY vector at 4467H to display the alert.
53C6
POP DE D1
POP the caller's value back into Register Pair DE.
53C7
POP HL E1
POP the caller's pointer back into Register Pair HL.
53C8
XOR A AF
Clear Register A to zero, ready to wipe the mounted flags.
53C9
LD (53D5H),A 32 D5 53
Store zero at 53D5H, the current drive cell, so that both the source and the destination are treated as unmounted and the prompt is issued again.
53CC
LOOP BACK to 5394H to ask for the destination disk once more.
53CE
LD A,C 79
Load Register A from Register C. This is a third entry to the prompt machinery, used by the front end at 5B5AH and 5BD5H when it already holds the drive-state byte it wants and does not need the source or destination wrapper.
53CF
LD (53D5H),A 32 D5 53
Store Register A, the requested drive state, at 53D5H, the current drive cell, adopting it without any comparison.
53D2
JUMP forward to 53E0H to display the prompt and wait, bypassing the comparison the normal entry makes.
53D4
CP 00H FE 00
Self-Modifying Code
Compare Register A, the requested drive state, against the operand byte at 53D5H, which is the current drive cell itself. Making the cell the immediate operand of the comparison is what lets one instruction test the request against the live state. If they are equal, the Z FLAG is set.
53D6
If the Z FLAG has been set the requested disk is already the mounted one, so JUMP forward to 5439H to point Register Pair IY at that drive and return without disturbing the operator.
53D8
LD (53D5H),A 32 D5 53
Store Register A, the new drive state, at 53D5H, the current drive cell, so that the utility now believes the requested disk is the mounted one.
53DB
LD A,0FFH 3E FF
Self-Modifying Code
Load Register A from the operand at 53DCH, the source drive number exclusive-ORed with the destination drive number, which the front end computed at 5A91H. Zero means both drivespecs named the same unit.
53DD
OR A B7
OR Register A with itself to set the flags on that value.
53DE
If the NZ FLAG has been set the two disks are in different drives, so no swap is needed and there is nothing to prompt for. JUMP forward to 5439H to point Register Pair IY at the requested drive and return.
53E0
PUSH BC C5
PUSH Register Pair BC onto the stack to preserve the caller's registers across the wait, which can last indefinitely.
53E1
PUSH DE D5
PUSH Register Pair DE onto the stack for the same reason.
53E2
PUSH HL E5
PUSH Register Pair HL onto the stack. Register Pair HL holds the prompt address and is needed again on every pass of the flashing loop.
53E3
LD A,0DH 3E 0D
Load Register A with 0DH, a carriage return, to move the cursor to a fresh line before the prompt is displayed.
53E5
GOSUB to the ROM character-output routine at 0033H to send the carriage return to the video display.
53E8
GOSUB to 544CH to clear bits 0, 1 and 2 of KFLAG$ at 4423H. The resident keyboard poller at 4DA6H only ever sets those bits, so any BREAK, PAUSE or ENTER left over from earlier has to be cleared here or the wait would end immediately.
53EB
LD BC,41FDH 01 FD 41
Load Register Pair BC with 41FDH as a delay count, giving roughly a second before the keyboard is looked at again.
53EE
GOSUB to the ROM delay routine at 0060H, which counts Register Pair BC down.
53F1
GOSUB to the resident keyboard-status poller at 4DA6H, which reads the keyboard matrix directly and sets bit 0 of KFLAG$ for BREAK, bit 1 for PAUSE and bit 2 for ENTER.
53F4
LD A,(4423H) 3A 23 44
Load Register A from KFLAG$ at 4423H to see what the poller found.
53F7
AND 05H E6 05
AND Register A with 05H to keep only bit 0, BREAK, and bit 2, ENTER. Either of them means the operator has responded.
53F9
If the NZ FLAG has been set a key was already down when the prompt started, which is stale, so LOOP BACK to 53E8H to clear the flags and start the wait again.
53FB
GOSUB to 544CH to clear KFLAG$ bits 0 to 2 again, so that the flashing loop below starts from a clean state.
53FE
GOSUB to the resident @DSPLY vector at 4467H to display the prompt at Register Pair HL. The prompt begins with 1DH and 1EH, so it rewrites the current line rather than scrolling.
5401
LD BC,1555H 01 55 15
Load Register Pair BC with 1555H, the length of time the prompt stays visible during each flash.
5404
GOSUB to 5414H, the delay-and-poll routine, which counts Register Pair BC down while watching the keyboard.
5407
LD A,1EH 3E 1E
Load Register A with 1EH, the Model I control code that clears from the cursor to the end of the line, which blanks the prompt.
5409
GOSUB to the ROM character-output routine at 0033H to send it, leaving the line empty.
540C
LD BC,1111H 01 11 11
Load Register Pair BC with 1111H, the shorter time the line stays blank.
540F
GOSUB to 5414H to count that delay down while watching the keyboard.
5412
LOOP BACK to 53FEH to display the prompt again. The message therefore flashes, which is exactly what the manual describes for the disk-full prompt.
5414
GOSUB to the resident keyboard-status poller at 4DA6H to refresh KFLAG$ from the keyboard matrix.
5417
LD A,(4423H) 3A 23 44
Load Register A from KFLAG$ at 4423H.
541A
RRCA 0F
Rotate Register A left circularly, moving bit 0, the BREAK flag, into the CARRY FLAG.
541B
If the CARRY FLAG has been set the operator pressed BREAK, so JUMP forward to 5448H to abandon the whole backup.
541D
BIT 1,A CB 4F
Test bit 1 of Register A. After the rotate this is the original bit 2, the ENTER flag, which is the reply the prompt is waiting for.
541F
If the NZ FLAG has been set the operator pressed ENTER, so JUMP forward to 5427H to accept the reply and unwind the flashing loop.
5421
DEC BC 0B
DECrement Register Pair BC by 1, one step of the delay the caller asked for.
5422
LD A,B 78
Load Register A from Register B, the high byte of the remaining count.
5423
OR C B1
OR Register A with Register C, the low byte, so that the whole count is tested for zero at once.
5424
If the NZ FLAG has been set the delay has not run out, so LOOP BACK to 5414H to poll the keyboard again. The keyboard is therefore sampled on every pass rather than only at the end of the delay.
5426
RET C9
RETurn to the flashing loop, which will switch the prompt on or off and call back in.
5427
POP AF F1
POP the return address of the flashing loop off the stack and discard it. This is what breaks out of the loop at 53FEH from two levels down, so that the routine can finish the prompt properly instead of returning into the flash.
5428
GOSUB to the ROM keyboard scan at 002BH, which returns the character of any key currently down or zero if none is.
542B
OR A B7
OR Register A with itself to test the character for zero.
542C
If the NZ FLAG has been set a key is still held down, so LOOP BACK to 5428H. This waits for the operator to release ENTER, which stops one press being read twice.
542E
LD A,1EH 3E 1E
Load Register A with 1EH, the clear-to-end-of-line code.
5430
GOSUB to the ROM character-output routine at 0033H to erase the prompt now that it has been answered.
5433
GOSUB to 544CH to clear KFLAG$ bits 0 to 2, leaving the flags as the caller expects to find them.
5436
POP HL E1
POP the prompt address back into Register Pair HL, undoing the save at 53E2H.
5437
POP DE D1
POP the caller's value back into Register Pair DE.
5438
POP BC C1
POP the caller's value back into Register Pair BC.
5439
LD A,(53D5H) 3A D5 53
Load Register A from 53D5H, the current drive cell, whose low three bits are the drive number and whose top two bits are the source and destination markers.
543C
AND 07H E6 07
AND Register A with 07H to keep only the drive number, discarding the two markers.
543E
LD C,A 4F
Load Register C with Register A, the drive number, which is where GETDCT expects to find it.
543F
GOSUB to the resident GETDCT routine at 478FH, which points Register Pair IY at 4700H plus ten times the drive number, that drive's Drive Control Table entry. Every disk operation this file performs goes through that pointer.
5442
GOSUB to 5460H, the disk-operation stub for operation 07H, which waits for the floppy disk controller to become ready before anything else is attempted.
5445
RLCA 07
Rotate Register A left circularly, moving bit 7 of the returned status into bit 0 and the CARRY FLAG.
5446
RLCA 07
Rotate Register A left circularly again, so that the original bits 7 and 6 now sit in bits 1 and 0. The caller tests the result to find out whether the drive is write protected.
5447
RET C9
RETurn to the caller with Register Pair IY pointing at the selected drive's Drive Control Table entry.
5448
POP AF F1
POP the flashing loop's return address off the stack and discard it, the same unwinding the ENTER path does at 5427H.
5449
JUMP to the abort exit at 52ACH. The operator pressed BREAK, so the backup ends with Backup aborted.
544C
LD A,(4423H) 3A 23 44
Load Register A from KFLAG$ at 4423H, the resident keyboard flag byte.
544F
AND 0F8H E6 F8
AND Register A with 0F8H to clear bits 0, 1 and 2, the BREAK, PAUSE and ENTER flags, while leaving the type-ahead, caps-lock and translation-table bits in the top half untouched.
5451
LD (4423H),A 32 23 44
Store the result back at KFLAG$ 4423H. The resident poller at 4DA6H only ever sets these three bits, so clearing them is the caller's responsibility.
5454
RET C9
RETurn to the caller with the three keyboard request flags cleared.
5455
XOR A AF
Clear Register A to zero, operation 00H, the drive-present test. An absent unit's Drive Control Table block starts with 0C9H rather than a jump, so it returns with Register A still holding 20H and the NZ FLAG set, while a fitted drive comes back with Register A zero.
5456
JUMP forward to the common tail at 5472H, which turns the operation code in Register A into a driver call.
5458
LD A,01H 3E 01
Load Register A with 01H, operation 01H, select the drive and start its motor.
545A
JUMP forward to the common tail at 5472H.
545C
LD A,04H 3E 04
Load Register A with 04H, operation 04H, restore the head to cylinder 0.
545E
JUMP forward to the common tail at 5472H.
5460
LD A,07H 3E 07
Load Register A with 07H, operation 07H, wait for the floppy disk controller to become ready.
5462
JUMP forward to the common tail at 5472H.
5464
LD A,0DH 3E 0D
Load Register A with 0DH, operation 0DH, write a sector with a normal data address mark. This is the operation used for ordinary data cylinders.
5466
JUMP forward to the common tail at 5472H.
5468
LD A,0EH 3E 0E
Load Register A with 0EH, operation 0EH, write a sector with a deleted data address mark. Every directory sector on an LDOS disk is written this way, which is why a clean read of one afterwards counts as a failure.
546A
JUMP forward to the common tail at 5472H.
546C
LD A,09H 3E 09
Load Register A with 09H, operation 09H, read a sector into the buffer at Register Pair HL.
546E
JUMP forward to the common tail at 5472H.
5470
LD A,0AH 3E 0A
Load Register A with 0AH, operation 0AH, verify a sector against what is already on the disk without transferring it.
5472
PUSH BC C5
PUSH Register Pair BC onto the stack. The tail uses Register Pair BC to pass the operation and the drive to the driver, so the caller's copy has to be preserved.
5473
LD B,A 47
Load Register B with Register A, the operation code the stub above selected. The driver takes its operation from Register B.
5474
LD A,(53D5H) 3A D5 53
Load Register A from 53D5H, the current drive cell.
5477
AND 07H E6 07
AND Register A with 07H to keep only the drive number.
5479
LD C,A 4F
Load Register C with Register A, the drive number, which is where the driver expects it.
547A
LD (4308H),A 32 08 43
Store Register A, the drive number, at LDRV$ 4308H, the resident cell that records the last drive an operation was issued against. The resident routines read it when they report an error.
547D
LD A,20H 3E 20
Load Register A with 20H. The driver treats a non-zero value here as the caller asking it to wait for the motor to come up to speed, and it is also the value an absent drive returns unchanged.
547F
OR A B7
OR Register A with itself to clear the CARRY FLAG, which the driver requires on entry, without changing Register A.
5480
GOSUB to 5485H, which is the single indirect jump that reaches the floppy driver through this drive's Drive Control Table entry.
5483
POP BC C1
POP the caller's value back into Register Pair BC.
5484
RET C9
RETurn to the caller with the driver's status in Register A and the flags set from it.
5485
JP (IY) FD E9
JUMP to the address in Register Pair IY. That points at the drive's Drive Control Table entry, whose first three bytes are a jump into the resident floppy driver at 45FBH on a fitted drive and a RET on an absent one. Reaching the driver this way rather than through the resident disk primitives at 4750H is the same technique FORMAT/CMD uses, and it is what lets the utility issue raw restore, step and write-track operations.

5487H - The Shared Message Block

The remainder of the second load record is text, not code. These ten messages are the ones both backup engines need, which is why they sit below 5A00H where the block move that brings an engine down cannot reach them. Three terminators are in use and they are not interchangeable. A 0DH ends the line. A 03H leaves the cursor where it is, which is what a prompt needs. A leading 1DH returns the cursor to the start of the current line and a following 1EH clears to the end of it, so a message that begins with that pair overwrites the line it is on rather than scrolling the screen, and that is how the flashing prompt loop at 53FEH alternates a prompt against a blank line without disturbing anything above it.

5487-54B0
DEFM 0AH, "Note: Real time clock no longer accurate", 0DH 0A 4E 6F 74 65 3A 20 52 65 61 6C 20 74 69 6D 65 20 63 6C 6F 63 6B 20 6E 6F 20 6C 6F 6E 67 65 72 20 61 63 63 75 72 61 74 65 0D
Displayed on every path out of the utility, by 5292H after a completed backup and by 52B7H after an error or an abort. The leading 0AH is a line feed and the 0DH ends the line. The note is unconditional because the mirror-image engine writes a RET over the resident interrupt vector byte at 4012H for the whole of its transfer, so the clock has lost time whether or not the backup got that far.
54B1-54D5
DEFM 0AH, "Destination disk is write protected", 0DH 0A 44 65 73 74 69 6E 61 74 69 6F 6E 20 64 69 73 6B 20 69 73 20 77 72 69 74 65 20 70 72 6F 74 65 63 74 65 64 0D
Displayed by the front end at 5BF2H, which loads Register Pair HL with this address and jumps to the abort entry at 52AFH. The test above it takes bit 7 of the status the drive returned and ORs it with bit 7 of the Drive Control Table byte at IY+03H, which is the write-protect flag, so either source of the information refuses the backup.
54D6-54F4
DEFM 1DH, 1EH, "Insert SYSTEM disk ", 1DH, 03H 1D 1E 49 6E 73 65 72 74 20 53 59 53 54 45 4D 20 64 69 73 6B 20 20 3C 45 4E 54 45 52 3E 1D 03
The prompt issued by 5306H at the end of a backup that used the X parameter. The leading 1DH returns the cursor to the start of the line and the 1EH clears to the end of it, so the prompt overwrites whatever was there rather than scrolling. The trailing 1DH puts the cursor back at the start of the line and the 03H leaves it there, which is what makes the message flash in place in the loop at 53FEH.
54F5-5513
DEFM 1DH, 1EH, "Insert SOURCE disk ", 1DH, 03H 1D 1E 49 6E 73 65 72 74 20 53 4F 55 52 43 45 20 64 69 73 6B 20 20 3C 45 4E 54 45 52 3E 1D 03
The prompt issued by 531FH whenever the source disk has to be put in a drive that is currently holding something else, which on a single-drive backup is every time the copy changes direction.
5514-5537
DEFM 1DH, 1EH, "Insert DESTINATION disk ", 1DH, 03H 1D 1E 49 6E 73 65 72 74 20 44 45 53 54 49 4E 41 54 49 4F 4E 20 64 69 73 6B 20 20 3C 45 4E 54 45 52 3E 1D 03
The prompt issued by 538CH, the destination counterpart of the message above. The front end also uses it directly at 5BD2H and 5C05H when it is mounting the destination for the first time.
5538-5569
DEFM 1DH, 1EH, "* A L E R T * That's not the same source disk!", 0DH 1D 1E 2A 20 41 20 4C 20 45 20 52 20 54 20 2A 20 20 54 68 61 74 27 73 20 6E 6F 74 20 74 68 65 20 73 61 6D 65 20 73 6F 75 72 63 65 20 64 69 73 6B 21 0D
Displayed by 5378H when the ten-byte Pack ID read back from the disk the operator has just inserted does not match the copy saved at 57CEH. The comparison covers the master-password hash at Granule Allocation Table offset CEH and the eight-character disk name at offset D0H. After displaying this the routine clears both mounted flags at 5382H and asks again, so the utility will not proceed until the right disk appears.
556A-55A0
DEFM 1DH, 1EH, "* A L E R T * That's not the same destination disk!", 0DH 1D 1E 2A 20 41 20 4C 20 45 20 52 20 54 20 2A 20 20 54 68 61 74 27 73 20 6E 6F 74 20 74 68 65 20 73 61 6D 65 20 64 65 73 74 69 6E 61 74 69 6F 6E 20 64 69 73 6B 21 0D
Displayed by 53C0H when the first byte of cylinder 0 sector 0 is no longer the 76H marker that the mirror-image engine wrote there at 5A75H. The destination is identified by that marker rather than by a Pack ID, because a mirror-image backup is about to overwrite the destination's identity anyway.
55A1-55D6
DEFM "Source disk is write protected, can't clear mod flags", 0DH 53 6F 75 72 63 65 20 64 69 73 6B 20 69 73 20 77 72 69 74 65 20 70 72 6F 74 65 63 74 65 64 2C 20 63 61 6E 27 74 20 63 6C 65 61 72 20 6D 6F 64 20 66 6C 61 67 73 0D
Displayed by 6136H in the mirror-image engine when the pass that clears the modification flags on the source directory comes back with status 0FH. It is not an error: the copy has already completed, and the engine goes on to the normal ending at 5287H. This message has no leading 0AH because it follows the progress line, which the engine has already terminated.
55D7-55E7
DEFM 0AH, "Backup complete", 0DH 0A 42 61 63 6B 75 70 20 63 6F 6D 70 6C 65 74 65 0D
Displayed by 5287H, the ending every successful backup reaches from either engine, immediately before the real-time-clock note and the exit through @EXIT at 402DH.
55E8-55F7
DEFM 0AH, "Backup aborted", 0DH 0A 42 61 63 6B 75 70 20 61 62 6F 72 74 65 64 0D
Displayed by 52ACH, the plain abort entry, reached when the operator answers a question in the negative or presses BREAK during a prompt. Callers with a more specific message of their own load Register Pair HL themselves and enter one instruction later at 52AFH, which is why this text appears only on the general path.

Unloaded Storage
The image resumes at 5A00H. The 1032 bytes from 55F8H to 59FFH are not written by any load record and are the largest gap in the file. They are the geometry scratch and the source file control block at 55F8H, the saved copy of the source disk's Granule Allocation Table sector at 5700H, the destination's at 5800H, and the general 256-byte sector buffer at 5900H. The two Granule Allocation Table copies are addressed at true Granule Allocation Table offsets throughout the file, so 5760H and 5860H are the lock-out maps, 57CCH and 58CCH the configuration words, 57CEH and 58CEH the master-password hashes and 57D0H and 58D0H the eight-character disk names.

5A00H - Front End Entry and Command Line Parsing

The transfer address named by the type-02 load record is 5A00H, so this is the first instruction BACKUP executes. The front end sets up its stack, signs on, and takes the command line apart: the optional leading minus sign, the file name, the extension, and the two drivespecs, prompting for either drivespec that the command line did not supply. Note that this whole block is temporary. Once the front end has decided which kind of backup to run, the selector at 6C00H moves one of the two engines down over it, and every address in this range becomes part of that engine.

5A00
LD SP,41E0H 31 E0 41
Load the Stack Pointer with 41E0H. This is the transfer address named by the type-02 record, so it is the first instruction executed. 41E0H is below the resident buffer area and above the Level II BASIC workspace, which is the region a transient utility is free to use for its stack.
5A03
PUSH HL E5
PUSH Register Pair HL onto the stack. DOS enters a transient program with Register Pair HL pointing at the first character of the command line after the program name, and that pointer has to survive the sign-on display below.
5A04
LD HL,6E58H 21 58 6E
Point Register Pair HL at the sign-on at 6E58H, which reads BACKUP - LDOS Disk Backup Utility - Version 5.3.1 followed by the MISOSYS copyright line.
5A07
GOSUB to the resident @DSPLY vector at 4467H to display the sign-on.
5A0A
POP HL E1
POP the command-line pointer back into Register Pair HL.
5A0B
LD A,(HL) 7E
Fetch the next character of the command line from the address in Register Pair HL into Register A.
5A0C
INC HL 23
INCrement Register Pair HL by 1 to step past the character just taken.
5A0D
CP 20H FE 20
Compare Register A against 20H, a blank. If Register A holds a blank, the Z FLAG is set.
5A0F
If the Z FLAG has been set the character was a blank, so LOOP BACK to 5A0BH to skip it. This strips the space between the command name and its first argument.
5A11
LD DE,5205H 11 05 52
Point Register Pair DE at 5205H, the eight-character filename mask, as the destination for the characters the filter below will store.
5A14
LD B,08H 06 08
Load Register B with 08H, the width of the filename field, as the counter the filter uses.
5A16
CP 2DH FE 2D
Compare Register A against 2DH, a minus sign. The manual's -partspec form means copy everything except the files that match.
5A18
If the NZ FLAG has been set there was no minus sign, so JUMP forward to 5A1FH to parse the name with Register A already holding its first character.
5A1A
LD (5211H),A 32 11 52
Store Register A, which is the 2DH minus sign itself, at 5211H, the negated-partspec flag. Any non-zero value there inverts the sense of the name and extension match in the file-by-file engine at 6485H.
5A1D
LD A,(HL) 7E
Fetch the next character of the command line into Register A, which is the first character of the name proper now that the minus sign has been consumed.
5A1E
INC HL 23
INCrement Register Pair HL by 1 to step past it.
5A1F
GOSUB to the filespec character filter at 5CB1H, which copies up to Register B characters from the command line at Register Pair HL into the mask at Register Pair DE, accepting a dollar sign as the wildcard, folding lower case to upper, and stopping at the first character that is not a digit or a letter. It returns with Register A holding the character that stopped it.
5A22
CP 2FH FE 2F
Compare Register A against 2FH, a slash, which is what separates a file name from its extension in an LDOS filespec.
5A24
If the NZ FLAG has been set there is no extension on the command line, so JUMP forward to 5A30H leaving the extension mask at 520DH as the three blanks it ships with.
5A26
LD DE,520DH 11 0D 52
Point Register Pair DE at 520DH, the three-character extension mask, as the destination for the next field.
5A29
LD B,03H 06 03
Load Register B with 03H, the width of the extension field.
5A2B
LD A,(HL) 7E
Fetch the next character of the command line into Register A, the first character of the extension now that the slash has been consumed.
5A2C
INC HL 23
INCrement Register Pair HL by 1 to step past it.
5A2D
GOSUB to the filespec character filter at 5CB1H again, this time filling the extension mask. It returns with the character that stopped it in Register A.
5A30
CP 3AH FE 3A
Compare Register A against 3AH, a colon, which introduces a drivespec.
5A32
If the Z FLAG has been set the command line named the source drive, so JUMP forward to 5A45H to read its digit.
5A34
LD HL,6EBEH 21 BE 6E
Point Register Pair HL at the prompt at 6EBEH, Source drive number ?, which is terminated by 03H so that the cursor stays on the line for the reply.
5A37
GOSUB to the resident @DSPLY vector at 4467H to display the prompt.
5A3A
LD HL,525DH 21 5D 52
Point Register Pair HL at 525DH, the small reply buffer inside the unloaded area, as the destination for the character the operator types.
5A3D
LD B,01H 06 01
Load Register B with 01H, so that the line-input routine accepts exactly one character before it returns.
5A3F
GOSUB to the ROM line-input routine at 0040H, which collects the reply into the buffer at Register Pair HL and returns with Register Pair HL still pointing at it. It sets the CARRY FLAG if BREAK was pressed.
5A42
If the CARRY FLAG has been set the operator pressed BREAK, so JUMP to the abort exit at 52ACH.
5A45
LD A,(HL) 7E
Fetch the source drive digit into Register A, either from the command line after the colon or from the reply buffer the prompt just filled.
5A46
INC HL 23
INCrement Register Pair HL by 1 to step past the digit.
5A47
SUB 30H D6 30
SUBtract 30H from Register A to convert the ASCII digit into the binary drive number it represents.
5A49
CP 08H FE 08
Compare Register A against 08H. LDOS supports eight drives, numbered 0 to 7, so anything from 8 upwards is invalid and leaves the CARRY FLAG clear.
5A4B
If the NO CARRY FLAG has been set the digit was 8 or greater, so JUMP to 529EH, the overlapping entry that loads LDOS error 32, illegal drive number, and falls into the error exit.
5A4E
LD (531BH),A 32 1B 53
Self-Modifying Code
Store Register A, the source drive number, at 531BH, which is the operand of the load at 531AH inside the source-mount routine. From here on that routine builds its drive-state byte from this cell.
5A51
LD A,(HL) 7E
Fetch the next character of the command line into Register A, looking for the destination drivespec.
5A52
INC HL 23
INCrement Register Pair HL by 1 to step past it.
5A53
CP 3AH FE 3A
Compare Register A against 3AH, the colon that introduces the destination drivespec.
5A55
If the Z FLAG has been set the destination drive was named, so JUMP forward to 5A70H to read its digit.
5A57
CP 30H FE 30
Compare Register A against 30H. Everything below the digits is either a blank, a control character or the end of the line, and everything at 30H or above is a character that could be part of the word TO.
5A59
If the NO CARRY FLAG has been set the character is 30H or above, so LOOP BACK to 5A51H to skip it. This is how the optional TO between the two drivespecs is discarded without being parsed.
5A5B
CP 20H FE 20
Compare Register A against 20H, a blank.
5A5D
If the Z FLAG has been set the character was a blank, so LOOP BACK to 5A51H to skip it as well.
5A5F
LD HL,6EDCH 21 DC 6E
Point Register Pair HL at the prompt at 6EDCH, Destination drive number ?, since the command line did not name one.
5A62
GOSUB to the resident @DSPLY vector at 4467H to display the prompt.
5A65
LD HL,525DH 21 5D 52
Point Register Pair HL at 525DH, the reply buffer.
5A68
LD B,01H 06 01
Load Register B with 01H, one character.
5A6A
GOSUB to the ROM line-input routine at 0040H to collect the reply.
5A6D
If the CARRY FLAG has been set the operator pressed BREAK, so JUMP to the abort exit at 52ACH.
5A70
LD A,(HL) 7E
Fetch the destination drive digit into Register A, either from the command line after the colon or from the reply buffer.
5A71
INC HL 23
INCrement Register Pair HL by 1 to step past it.
5A72
SUB 30H D6 30
SUBtract 30H from Register A to convert the ASCII digit into a binary drive number.
5A74
If the CARRY FLAG has been set the character was below 30H and is not a digit at all, so JUMP to 529EH for LDOS error 32, illegal drive number. The source digit is not given this test, because a character below 30H there has already been consumed by the loop that skips the word TO.
5A77
CP 08H FE 08
Compare Register A against 08H, the drive count.
5A79
If the NO CARRY FLAG has been set the digit was 8 or greater, so JUMP to 529EH for the same illegal drive number error.
5A7C
LD (5388H),A 32 88 53
Self-Modifying Code
Store Register A, the destination drive number, at 5388H, which is the operand of the load at 5387H inside the destination-mount routine.

5A7FH - Parameters, the Single Drive Test and the DATE Range

The parameter list in brackets is handed to the resident @PARAM service, which writes each value into the cell the table at 6D74H names. The two drive numbers are then exclusive-ORed together into 53DCH, the byte that tells the rest of the utility whether this is a single-drive backup, and a single-drive backup under a job is refused outright because there is nobody at the keyboard to swap disks. The DATE string is parsed into the two packed bounds at 5201H and 5203H, and the SYS, INV and MOD switches are folded into the attribute selection byte at 5210H in the bit positions a directory record uses.

5A7F
LD DE,6D74H 11 74 6D
Point Register Pair DE at 6D74H, the @PARAM keyword table: sixteen six-character blank-padded keywords, each followed by the two-byte address of its result cell, ended by a 00H byte at 6DF4H.
5A82
GOSUB to the resident @PARAM vector at 4476H. It reads the parameter list in brackets from the command line at Register Pair HL, matches each keyword against the table and writes the value into the cell the table names, returning with the Z FLAG set when everything parsed.
5A85
LD A,2CH 3E 2C
Load Register A with 2CH, LDOS error 44, the parameter error, ready in case the parse failed.
5A87
If the NZ FLAG has been set @PARAM did not understand something in the parameter list, so JUMP to the disk-error exit at 52A0H, which reports error 44 through @ERROR and ends the backup.
5A8A
LD A,(531BH) 3A 1B 53
Self-Modifying Code
Load Register A from 531BH, the source drive number planted at 5A4EH.
5A8D
LD HL,5388H 21 88 53
Point Register Pair HL at 5388H, the destination drive number planted at 5A7CH.
5A90
XOR (HL) AE
Exclusive-OR Register A with the byte at the address in Register Pair HL, so that Register A becomes the source drive number combined with the destination drive number. A result of zero means the two drivespecs named the same unit.
5A91
LD (53DCH),A 32 DC 53
Self-Modifying Code
Store the result at 53DCH, the operand of the load at 53DBH inside the prompt routine, and the single byte on which every prompt-and-swap decision in the file turns. Zero from here on means a single-drive backup.
5A94
If the NZ FLAG has been set the two drives are different and no disk swapping is needed, so JUMP forward to 5AA1H.
5A96
LD A,(430FH) 3A 0F 43
Load Register A from SFLAG$ at 430FH, the resident system flag byte.
5A99
BIT 5,A CB 6F
Test bit 5 of Register A, which the DOS sets while a job file is running and clears at command level.
5A9B
LD HL,6DF5H 21 F5 6D
Point Register Pair HL at the message at 6DF5H, Single drive backup invalid during <DO> processing, ready in case a job is in effect.
5A9E
If the NZ FLAG has been set a job is running, so JUMP to the abort entry at 52AFH with that message. A single-drive backup needs the operator to swap disks, and there is nobody at the keyboard during a job.
5AA1
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL from the operand at 5AA2H, which is the DATE parameter result cell. @PARAM stores the address of the quoted date string there, or leaves it at 0000H when the parameter was not used.
5AA4
LD A,H 7C
Load Register A from Register H, the high byte of that address.
5AA5
OR L B5
OR Register A with Register L, the low byte, so that the whole address is tested for zero at once.
5AA6
If the Z FLAG has been set the DATE parameter was not given, so JUMP forward to 5AD7H leaving the date-selection mode byte at 5200H as the zero it ships with.
5AA8
LD A,(HL) 7E
Fetch the first character of the date string from the address in Register Pair HL into Register A.
5AA9
CP 2DH FE 2D
Compare Register A against 2DH, a minus sign. The manual's -M1/D1/Y1 form means all files on or before that date, so a leading minus sign means there is no lower bound.
5AAB
If the Z FLAG has been set the string begins with a minus sign, so JUMP forward to 5AC2H to parse the upper bound only.
5AAD
LD A,80H 3E 80
Load Register A with 80H, the bit that marks a lower bound as present.
5AAF
LD (5200H),A 32 00 52
Store Register A at 5200H, the date-selection mode byte, so that the file-by-file engine at 649BH knows to apply a lower bound.
5AB2
GOSUB to the date parser at 5CCCH, which reads one M/D/Y group from the string at Register Pair HL and returns the packed two-byte directory form in Register Pair BC.
5AB5
LD (5201H),BC ED 43 01 52
Store Register Pair BC, the packed lower bound, at 5201H and 5202H, in exactly the form a directory record carries at its offsets 01H and 02H, so that the comparison later needs no unpacking.
5AB9
LD A,(HL) 7E
Fetch the character that stopped the parser from the address in Register Pair HL into Register A.
5ABA
CP 22H FE 22
Compare Register A against 22H, a quotation mark, which ends the parameter string.
5ABC
If the Z FLAG has been set the string ended after one date, so JUMP forward to 5ACBH to record that same date as the upper bound as well, which is the manual's M1/D1/Y1 form meaning exactly that date.
5ABE
CP 2DH FE 2D
Compare Register A against 2DH, the minus sign that separates two dates in a range.
5AC0
If the NZ FLAG has been set the string is neither ended nor continued properly, so JUMP forward to 5AD7H and leave the upper bound unset, which is the M1/D1/Y1- form meaning that date onwards.
5AC2
INC HL 23
INCrement Register Pair HL by 1 to step past the minus sign.
5AC3
LD A,(HL) 7E
Fetch the next character into Register A.
5AC4
CP 22H FE 22
Compare Register A against 22H, the closing quotation mark.
5AC6
If the Z FLAG has been set the string ends immediately after the minus sign, so JUMP forward to 5AD7H. There is no upper bound to parse.
5AC8
GOSUB to the date parser at 5CCCH to read the second M/D/Y group, returning the packed form in Register Pair BC.
5ACB
LD A,(5200H) 3A 00 52
Load Register A from 5200H, the date-selection mode byte as it stands.
5ACE
OR 01H F6 01
OR Register A with 01H to set bit 0, the marker that an upper bound is present, without disturbing bit 7.
5AD0
LD (5200H),A 32 00 52
Store the result back at 5200H. Both bits set means a range, bit 0 alone means everything on or before the date, and bit 7 alone means everything on or after it.
5AD3
LD (5203H),BC ED 43 03 52
Store Register Pair BC, the packed upper bound, at 5203H and 5204H.
5AD7
LD B,00H 06 00
Load Register B with 00H, which will be built up into the attribute selection byte from the three switches that describe a directory record.
5AD9
LD DE,(521BH) ED 5B 1B 52
Load Register Pair DE from 521BH, the SYS parameter result cell, which @PARAM fills with 0FFFFH when the SYS or S keyword appears.
5ADD
LD A,D 7A
Load Register A from Register D, the high byte of the cell.
5ADE
OR E B3
OR Register A with Register E, the low byte, so that the cell is tested as a whole.
5ADF
If the Z FLAG has been set the SYS parameter was not given, so JUMP forward to 5AE3H leaving bit 6 clear.
5AE1
SET 6,B CB F0
Set bit 6 of Register B. That is the same bit position that marks a system file in a directory record's attribute byte, which is what lets the file-by-file engine test the two against each other directly.
5AE3
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE from the operand at 5AE4H, which is the INV parameter result cell that @PARAM fills when the INV or I keyword appears.
5AE6
LD A,D 7A
Load Register A from Register D, the high byte.
5AE7
OR E B3
OR Register A with Register E, the low byte.
5AE8
If the Z FLAG has been set the INV parameter was not given, so JUMP forward to 5AECH leaving bit 3 clear.
5AEA
SET 3,B CB D8
Set bit 3 of Register B, the bit that marks an invisible file in a directory record's attribute byte.
5AEC
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE from the operand at 5AEDH, which is a second result cell for the modification switch.
5AEF
LD A,D 7A
Load Register A from Register D, the high byte.
5AF0
OR E B3
OR Register A with Register E, the low byte.
5AF1
If the Z FLAG has been set the switch was not given, so JUMP forward to 5AF5H leaving bit 4 clear.
5AF3
SET 4,B CB E0
Set bit 4 of Register B, the bit that marks an allocated record in a directory record's attribute byte.
5AF5
LD A,B 78
Load Register A from Register B, the assembled attribute selection byte.
5AF6
LD (5210H),A 32 10 52
Store Register A at 5210H, the attribute selection byte, where the file-by-file engine reads it at 6446H to decide whether a given directory record is one the operator asked for.

5AF9H - The Backup By Class Decision and Mounting the Source

The manual says a backup by class is done when the user specifies a partspec or any parameter except X or MPW. That rule is implemented literally, as one chain of ORs over the ten cells that are neither X nor MPW, and the answer is planted in the first instruction of the engine selector. The source disk is then mounted: SYS2/SYS is pre-loaded so that the open and initialise services are resident before any disk swapping begins, and the drive is selected, restored and read.

5AF9
LD A,(5205H) 3A 05 52
Load Register A from 5205H, the first character of the filename mask. The mask ships as blanks, so a blank here means no name was given on the command line.
5AFC
SUB 20H D6 20
SUBtract 20H from Register A, so that a blank becomes zero and any real character becomes non-zero. This is the first term of the backup-by-class decision.
5AFE
LD B,A 47
Load Register B with Register A, starting the running total that the next eleven lines accumulate.
5AFF
LD A,(520DH) 3A 0D 52
Load Register A from 520DH, the first character of the extension mask, which is also blank when no extension was given.
5B02
SUB 20H D6 20
SUBtract 20H from Register A so that a blank contributes nothing.
5B04
OR B B0
OR Register A with Register B, accumulating this criterion into the running total. Any non-zero bit anywhere in the total means the operator asked for something specific, which forces a backup by class.
5B05
LD B,A 47
Load Register B with Register A, keeping the running total for the next criterion.
5B06
LD A,(521BH) 3A 1B 52
Load Register A from 521BH, the low byte of the SYS parameter result cell.
5B09
OR B B0
OR Register A with Register B, accumulating this criterion into the running total. Any non-zero bit anywhere in the total means the operator asked for something specific, which forces a backup by class.
5B0A
LD B,A 47
Load Register B with Register A, keeping the running total for the next criterion.
5B0B
LD A,(5AE4H) 3A E4 5A
Self-Modifying Code
Load Register A from 5AE4H, the low byte of the INV parameter result cell, which is the operand of the load at 5AE3H.
5B0E
OR B B0
OR Register A with Register B, accumulating this criterion into the running total. Any non-zero bit anywhere in the total means the operator asked for something specific, which forces a backup by class.
5B0F
LD B,A 47
Load Register B with Register A, keeping the running total for the next criterion.
5B10
LD A,(5AEDH) 3A ED 5A
Self-Modifying Code
Load Register A from 5AEDH, the low byte of the second modification-switch result cell, the operand of the load at 5AECH.
5B13
OR B B0
OR Register A with Register B, accumulating this criterion into the running total. Any non-zero bit anywhere in the total means the operator asked for something specific, which forces a backup by class.
5B14
LD B,A 47
Load Register B with Register A, keeping the running total for the next criterion.
5B15
LD A,(5217H) 3A 17 52
Load Register A from 5217H, the low byte of the MOD parameter result cell.
5B18
OR B B0
OR Register A with Register B, accumulating this criterion into the running total. Any non-zero bit anywhere in the total means the operator asked for something specific, which forces a backup by class.
5B19
LD B,A 47
Load Register B with Register A, keeping the running total for the next criterion.
5B1A
LD A,(5AA2H) 3A A2 5A
Self-Modifying Code
Load Register A from 5AA2H, the low byte of the DATE parameter result cell, the operand of the load at 5AA1H.
5B1D
OR B B0
OR Register A with Register B, accumulating this criterion into the running total. Any non-zero bit anywhere in the total means the operator asked for something specific, which forces a backup by class.
5B1E
LD B,A 47
Load Register B with Register A, keeping the running total for the next criterion.
5B1F
LD A,(5213H) 3A 13 52
Load Register A from 5213H, the low byte of the NEW parameter result cell.
5B22
OR B B0
OR Register A with Register B, accumulating this criterion into the running total. Any non-zero bit anywhere in the total means the operator asked for something specific, which forces a backup by class.
5B23
LD B,A 47
Load Register B with Register A, keeping the running total for the next criterion.
5B24
LD A,(5215H) 3A 15 52
Load Register A from 5215H, the low byte of the OLD parameter result cell.
5B27
OR B B0
OR Register A with Register B, accumulating this criterion into the running total. Any non-zero bit anywhere in the total means the operator asked for something specific, which forces a backup by class.
5B28
LD B,A 47
Load Register B with Register A, keeping the running total for the next criterion.
5B29
LD A,(521AH) 3A 1A 52
Load Register A from 521AH, the high byte of the QUERY parameter result cell, which is also the cell a reply of C clears at 65EFH to turn querying off part way through.
5B2C
OR B B0
OR Register A with Register B, completing the running total. This is the whole of the manual's rule that a backup by class is done when the user specifies a partspec or any parameter except X or MPW, and it is written as a single chain of ORs over the ten cells that are not X and not MPW.
5B2D
LD (6C01H),A 32 01 6C
Self-Modifying Code
Store the total at 6C01H, which is the operand of the load at 6C00H, the first instruction of the engine selector. The selector therefore begins by reading the answer to the question this chain just settled.
5B30
LD HL,6F0EH 21 0E 6F
Point Register Pair HL at the message at 6F0EH, Backup by class invoked.
5B33
If the NZ FLAG has been set, the total was non-zero and a backup by class is what the operator asked for, so GOSUB to the resident @LOGOT vector at 447BH to say so. On a mirror-image backup nothing is displayed.
5B36
LD A,(4046H) 3A 46 40
Load Register A from 4046H, the resident month cell in the clock workspace. The DOS leaves it at zero until a date has been set.
5B39
OR A B7
OR Register A with itself to test the month for zero.
5B3A
LD HL,6EFAH 21 FA 6E
Point Register Pair HL at the message at 6EFAH, No date established.
5B3D
Self-Modifying Code
If the Z FLAG has been set no date has been set on this machine, so GOSUB to the resident @LOGOT vector at 447BH to warn the operator that whatever the backup stamps on the copy will be meaningless. The mirror-image engine overwrites the operand of this instruction at 5EB4H with the granules-per-cylinder count once the block move has brought it down to this address.
5B40
LD HL,527DH 21 7D 52
Point Register Pair HL at 527DH, the eight-byte buffer in the unloaded area that will hold the system date.
5B43
GOSUB to the resident @DATE vector at 4470H, which writes the current date as text into the buffer at Register Pair HL. The mirror-image engine copies it onto the destination disk at 604FH.
5B46
GOSUB to the supervisor stub at 6C6EH, which issues request code 84H. That loads SYS2/SYS and makes it resident; its sub-function 00H is rejected with a RET, so the load is the whole point. Doing it now means the open and initialise services are in memory before any disk swapping starts.
5B49
LD A,(531BH) 3A 1B 53
Self-Modifying Code
Load Register A from 531BH, the source drive number.
5B4C
OR A B7
Self-Modifying Code
OR Register A with itself to test the source drive number for zero, which means the source is the system drive. The mirror-image engine overwrites the operand region here at 5ECFH with the count of whole cylinders that will fit in the work space.
5B4D
If the NZ FLAG has been set the source is not drive 0, so JUMP forward to 5B5EH to mount it in the ordinary way.
5B4F
OR 80H F6 80
OR Register A with 80H to build the drive-state byte for drive 0 as the source, bit 7 being the source marker.
5B51
LD C,A 4F
Load Register C with Register A, where the direct prompt entry at 53CEH expects to find the drive state it is to adopt.
5B52
LD A,(52CAH) 3A CA 52
Load Register A from 52CAH, the X parameter result cell. Non-zero means the operator asked to run without a system disk in drive 0.
5B55
INC A 3C
INCrement Register A by 1. The cell holds 0FFH when X was given, so the result is zero and the Z FLAG is set exactly when the X parameter is in force.
5B56
PUSH AF F5
PUSH Register A and the flags onto the stack so that the test result survives the call below.
5B57
LD HL,54F5H 21 F5 54
Point Register Pair HL at the prompt at 54F5H, Insert SOURCE disk <ENTER>.
5B5A
If the Z FLAG has been set the X parameter is in force, so GOSUB to 53CEH, the direct prompt entry, to ask the operator to put the source disk in drive 0 in place of the system disk. Without X the system disk stays where it is and no prompt is issued.
5B5D
POP AF F1
POP the flags back so that the X test can be examined again on the next line.
5B5E
If the NZ FLAG has been set, either the source is not drive 0 or the X parameter is not in force, so GOSUB to 531AH to select the source drive in the ordinary way.
5B61
GOSUB to 545CH, the disk-operation stub for operation 04H, to restore the source drive's head to cylinder 0 before anything is read from it.
5B64
GOSUB to the drive-geometry reader at 6CBFH, which waits for the drive to come ready, reads its Granule Allocation Table and unpacks the configuration word into the Drive Control Table entry that Register Pair IY points at. It returns with the Z FLAG set when the drive answered.
5B67
If the Z FLAG has been set the source drive answered, so JUMP forward to 5B73H to read its geometry.
5B69
PUSH AF F5
PUSH Register A and the flags onto the stack, preserving the failure status across the prompt below.
5B6A
LD HL,54F5H 21 F5 54
Point Register Pair HL at the prompt at 54F5H, Insert SOURCE disk <ENTER>.
5B6D
GOSUB to 53CEH, the direct prompt entry, to ask for the source disk. The drive did not answer, so either it is empty or the operator has not swapped yet.
5B70
POP AF F1
POP the flags back off the stack.
5B71
LOOP BACK to 5B5EH to select the drive and try the geometry read again. The utility will keep asking until the source disk is readable.

5B73H - Reading the Source Geometry and Mounting the Destination

The source disk's boot sector gives up its directory cylinder and its geometry, its Granule Allocation Table is read into 5700H, and its master password is checked. The destination is then mounted and put through the same sequence, with two extra refusals that only apply to it: a rigid drive can never be a mirror-image destination, and a write-protected destination cannot be written at all. The write-protect test is deliberately made twice over, from the status the controller returned and from bit 7 of the Drive Control Table specification byte, so that either source of the information is enough to stop the backup.

5B73
LD A,(IY+03H) FD 7E 03
Load Register A with the byte at IY+03H, the drive specification byte in the source drive's Drive Control Table entry, whose bit 7 is write protect, bit 6 double density, bit 5 eight inch, bit 4 side select, bit 3 rigid, bit 2 fixed and bits 1 and 0 the stepping rate.
5B76
AND 20H E6 20
AND Register A with 20H to keep only bit 5, the flag that says this is an eight-inch drive.
5B78
LD (5C14H),A 32 14 5C
Self-Modifying Code
Store the eight-inch flag at 5C14H, which is the operand of the exclusive-OR at 5C13H in the mirror-image decision. That comparison therefore tests the destination's eight-inch bit against the source's without needing a second Drive Control Table pointer.
5B7B
GOSUB to 5455H, the disk-operation stub for operation 00H, the drive-present test. An absent unit's Drive Control Table block begins with a RET rather than a jump, so Register A comes back still holding 20H and the NZ FLAG set.
5B7E
If the NZ FLAG has been set the source drive is not fitted, so JUMP to the disk-error exit at 52A0H with the 20H that came back, LDOS error 32, illegal drive number.
5B81
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, cylinder 0 and sector 0, the boot sector.
5B84
LD HL,5900H 21 00 59
Point Register Pair HL at 5900H, the general sector buffer.
5B87
GOSUB to 546CH, the disk-operation stub for operation 09H, to read the source disk's boot sector.
5B8A
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H with the driver's error number.
5B8D
LD L,09H 2E 09
Load Register L with 09H, so that Register Pair HL points at 5909H. Register H is still 59H from the read above, and offset 09H in the boot sector is where the disk's directory cylinder is recorded.
5B8F
LD DE,55F8H 11 F8 55
Point Register Pair DE at 55F8H, the geometry scratch area at the bottom of the unloaded gap, as the destination.
5B92
LD BC,0002H 01 02 00
Load Register Pair BC with 0002H, two bytes, as the block-move count.
5B95
LDIR ED B0
Block move two bytes from the boot sector image to the scratch area, saving the source disk's directory cylinder and the byte that follows it.
5B97
LD L,16H 2E 16
Load Register L with 16H so that Register Pair HL points at 5916H, offset 16H in the boot sector.
5B99
LD C,10H 0E 10
Load Register C with 10H, sixteen bytes, as the count for the second move. Register B is already zero from the move above.
5B9B
LDIR ED B0
Block move sixteen bytes from offset 16H of the boot sector to the scratch area, which now also holds the geometry the mirror-image engine needs to reproduce on the destination.
5B9D
LD A,(5902H) 3A 02 59
Load Register A from 5902H, offset 02H of the boot sector, which on an LDOS disk is the directory cylinder number that the bootstrap uses.
5BA0
LD (IY+09H),A FD 77 09
Store Register A as the byte at IY+09H, the directory cylinder field of the source drive's Drive Control Table entry, so that every later directory read on this drive goes to the right cylinder.
5BA3
LD D,A 57
Load Register D with Register A, the directory cylinder, as the cylinder for the read below.
5BA4
LD E,00H 1E 00
Load Register E with 00H, sector 0 of that cylinder, which is the Granule Allocation Table.
5BA6
LD HL,5700H 21 00 57
Self-Modifying Code
Point Register Pair HL at 5700H, the buffer that holds the source disk's Granule Allocation Table for the rest of the run. The mirror-image engine overwrites the operand at 5BA6H with the destination directory cylinder at 5E83H once it has been moved down to this address.
5BA9
GOSUB to 546CH, the disk-operation stub for operation 09H, to read the source Granule Allocation Table.
5BAC
CP 06H FE 06
Compare Register A against 06H. Directory sectors are written with a deleted data address mark, so a successful read of one reports error 06H and anything else is a real failure.
5BAE
If the NZ FLAG has been set the read did not return the deleted-mark status, so JUMP to 529BH to exit with LDOS error 17.
5BB1
GOSUB to the master-password check at 5C89H, which accepts the disk at once if its master-password hash is that of PASSWORD and otherwise prompts for the password and hashes the reply.
5BB4
GOSUB to the drive-geometry reader at 6CBFH again, now that the Granule Allocation Table has been read, so that the configuration word is unpacked into the Drive Control Table entry and the per-drive dating flag at 475DH is set or cleared for this drive.
5BB7
LD A,(53D5H) 3A D5 53
Load Register A from 53D5H, the current drive cell, whose low three bits are the drive the geometry reader just finished with.
5BBA
GOSUB to 6D61H, which builds a BIT instruction from the drive number and executes it against 475DH, the resident per-drive extended-dating flag. It returns with the Z FLAG set when the bit is clear, meaning this pack is in the old date format.
5BBD
If the NZ FLAG has been set the source pack is already in the x.3 extended date format, so JUMP forward to 5BC4H leaving the conversion flag alone.
5BBF
LD A,0FFH 3E FF
Load Register A with 0FFH, one more than the 0FEH the conversion flag ships with.
5BC1
LD (5212H),A 32 12 52
Store Register A at 5212H, the date-format conversion flag. The source is in the old format, so the flag is now one increment away from zero and will reach it only if the destination turns out to be in the x.3 format.
5BC4
LD A,(5388H) 3A 88 53
Self-Modifying Code
Load Register A from 5388H, the destination drive number planted at 5A7CH. The mirror-image engine overwrites this region at 5EC7H with the granules-per-cylinder count once it has been moved down.
5BC7
OR A B7
Self-Modifying Code
OR Register A with itself to test the destination drive number for zero, meaning the destination is the system drive.
5BC8
If the NZ FLAG has been set the destination is not drive 0, so JUMP forward to 5BD9H to mount it in the ordinary way.
5BCA
OR 40H F6 40
OR Register A with 40H to build the drive-state byte for drive 0 as the destination, bit 6 being the destination marker.
5BCC
LD C,A 4F
Load Register C with Register A, where the direct prompt entry expects the drive state.
5BCD
LD A,(52CAH) 3A CA 52
Load Register A from 52CAH, the X parameter result cell.
5BD0
INC A 3C
INCrement Register A by 1, so that the Z FLAG is set exactly when the X parameter is in force.
5BD1
PUSH AF F5
PUSH Register A and the flags onto the stack so that the test survives the call below.
5BD2
LD HL,5514H 21 14 55
Point Register Pair HL at the prompt at 5514H, Insert DESTINATION disk <ENTER>.
5BD5
If the Z FLAG has been set the X parameter is in force, so GOSUB to 53CEH to ask the operator to put the destination disk in drive 0.
5BD8
POP AF F1
POP the flags back off the stack.
5BD9
If the NZ FLAG has been set, GOSUB to 5387H to select the destination drive in the ordinary way.
5BDC
LD A,(IY+03H) FD 7E 03
Load Register A with the byte at IY+03H, the destination drive's specification byte, now that Register Pair IY points at its Drive Control Table entry.
5BDF
BIT 3,A CB 5F
Test bit 3 of Register A, which marks a rigid drive. A hard disk can never be the destination of a mirror-image backup.
5BE1
If the NZ FLAG has been set the destination is a rigid drive, so JUMP to 5C69H to announce a backup reconstruct instead.
5BE4
GOSUB to 545CH, the disk-operation stub for operation 04H, to restore the destination drive's head to cylinder 0.
5BE7
GOSUB to 5455H, the disk-operation stub for operation 00H, the drive-present test, and pick up the write-protect status the tail at 5445H rotated into the top of Register A.
5BEA
If the NZ FLAG has been set the destination drive did not answer, so JUMP forward to 5C04H to prompt for the disk and try again.
5BEC
RLCA 07
Rotate Register A left circularly, bringing the status bit that reports write protection into bit 7.
5BED
OR (IY+03H) FD B6 03
OR Register A with the byte at IY+03H, the destination drive's specification byte, whose own bit 7 is the write-protect flag. Either source of the information is enough to refuse.
5BF0
BIT 7,A CB 7F
Test bit 7 of Register A, the combined write-protect indication.
5BF2
LD HL,54B1H 21 B1 54
Point Register Pair HL at the message at 54B1H, Destination disk is write protected.
5BF5
If the NZ FLAG has been set the destination cannot be written, so JUMP to the abort entry at 52AFH with that message.
5BF8
BIT 3,(IY+03H) FD CB 03 5E
Test bit 3 of the byte at IY+03H, the rigid-drive flag, now that the drive has answered and its Drive Control Table entry is fully populated.
5BFC
If the NZ FLAG has been set the destination is a rigid drive, so JUMP to 5C69H for a backup reconstruct.
5BFF
GOSUB to the drive-geometry reader at 6CBFH to read the destination disk's Granule Allocation Table and unpack its configuration word.
5C02
If the Z FLAG has been set the destination answered, so JUMP forward to 5C0EH to compare the two disks.
5C04
PUSH AF F5
PUSH Register A and the flags onto the stack, preserving the failure status.
5C05
LD HL,5514H 21 14 55
Point Register Pair HL at the prompt at 5514H, Insert DESTINATION disk <ENTER>.
5C08
GOSUB to 53CEH, the direct prompt entry, to ask for the destination disk.
5C0B
POP AF F1
POP the flags back off the stack.
5C0C
LOOP BACK to 5BD9H to select the drive and try again.

5C0EH - The Mirror Image Decision

This is where the manual's three kinds of backup are chosen between. A mirror image needs the size, the density and the number of sides to match, and all three live in the high byte of the Granule Allocation Table configuration word, so one exclusive-OR and one mask settle the question. A difference in cylinder count alone is not fatal and the operator is asked about it. Anything else, and any rigid destination, produces a backup reconstruct, which is reached by entering the selector one instruction past its class test so that the file-by-file engine runs whatever the class flag says.

5C0E
LD A,(IY+03H) FD 7E 03
Load Register A with the byte at IY+03H, the destination drive's specification byte.
5C11
AND 20H E6 20
AND Register A with 20H to keep only bit 5, the eight-inch flag.
5C13
XOR 00H EE 00
Self-Modifying Code
Exclusive-OR Register A with the operand at 5C14H, which is the source drive's eight-inch flag saved at 5B78H. A non-zero result means one drive is eight inch and the other is not.
5C15
If the NZ FLAG has been set the two drives are different sizes, so JUMP forward to 5C69H. The manual's rule is that a mirror image needs the size, the density and the number of sides to match, and this is the size half of it.
5C18
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, cylinder 0 and sector 0.
5C1B
GOSUB to 5470H, the disk-operation stub for operation 0AH, to verify that cylinder 0 sector 0 of the destination can be read without transferring it. A disk that has never been formatted fails here.
5C1E
LD HL,6E28H 21 28 6E
Point Register Pair HL at the message at 6E28H, Destination disk not formatted - Backup aborted.
5C21
If the NZ FLAG has been set the verify failed, so JUMP to the abort entry at 52AFH with that message. This is the manual's requirement that destination disks must be formatted before the backup begins.
5C24
LD HL,5900H 21 00 59
Point Register Pair HL at 5900H, the general sector buffer.
5C27
LD D,L 55
Self-Modifying Code
Load Register D with Register L, which is 00H, giving cylinder 0. The mirror-image engine overwrites this instruction at 5EBAH with the granules-per-cylinder count once it has been moved down to this address.
5C28
LD E,L 5D
Load Register E with Register L, which is also 00H, giving sector 0.
5C29
GOSUB to 546CH, the disk-operation stub for operation 09H, to read the destination boot sector.
5C2C
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H.
5C2F
LD A,(5902H) 3A 02 59
Self-Modifying Code
Load Register A from 5902H, the destination disk's directory cylinder as recorded in its boot sector. The mirror-image engine writes its own cylinder counter over the operand region here at 5F61H and 6031H once it has been moved down.
5C32
LD D,A 57
Load Register D with Register A, that directory cylinder, as the cylinder for the read below.
5C33
LD HL,5800H 21 00 58
Point Register Pair HL at 5800H, the buffer that holds the destination disk's Granule Allocation Table for the rest of the run.
5C36
LD E,L 5D
Load Register E with Register L, which is 00H, sector 0 of the directory cylinder.
5C37
GOSUB to 546CH, the disk-operation stub for operation 09H, to read the destination Granule Allocation Table.
5C3A
CP 06H FE 06
Compare Register A against 06H, the deleted data address mark status that a successful directory read reports.
5C3C
If the NZ FLAG has been set the read did not report the expected status, so JUMP to 529BH to exit with LDOS error 17.
5C3F
LD A,(53D5H) 3A D5 53
Load Register A from 53D5H, the current drive cell, which now names the destination drive.
5C42
GOSUB to 6D61H to test bit [drive] of the resident per-drive dating flag at 475DH for the destination. The Z FLAG comes back set when the bit is clear, meaning the destination is in the old date format.
5C45
If the Z FLAG has been set the destination is not in the x.3 extended format, so JUMP forward to 5C4BH leaving the conversion flag alone.
5C47
LD HL,5212H 21 12 52
Point Register Pair HL at 5212H, the date-format conversion flag.
5C4A
INC (HL) 34
INCrement the byte at the address in Register Pair HL by 1. The flag ships as 0FEH and was set to 0FFH at 5BC1H only if the source was in the old format, so it becomes zero here exactly when an old-format source is being copied to an x.3 destination.
5C4B
LD HL,(57CCH) 2A CC 57
Load Register Pair HL from 57CCH, the configuration word inside the source disk's Granule Allocation Table. The low byte is the cylinder count less 35 and the high byte carries the density, sided and granules-per-track fields.
5C4E
LD DE,(58CCH) ED 5B CC 58
Load Register Pair DE from 58CCH, the same word from the destination disk's Granule Allocation Table.
5C52
LD A,H 7C
Load Register A from Register H, the high byte of the source configuration word.
5C53
XOR D AA
Exclusive-OR Register A with Register D, the high byte of the destination word, so that Register A holds a one bit wherever the two disks differ.
5C54
AND 70H E6 70
AND Register A with 70H to keep only bits 6, 5 and 4 of the difference. In the high byte of the Granule Allocation Table configuration word bit 6 is the double-density flag, bit 5 is the double-sided flag and bit 4 marks a disk whose cylinder 0 is single density while the rest is double. These are exactly the attributes the manual says must match before a mirror image can be attempted.
5C56
If the NZ FLAG has been set the two disks differ in density, size or number of sides, so JUMP forward to 5C69H to announce a backup reconstruct.
5C58
LD A,L 7D
Load Register A from Register L, the low byte of the source configuration word, which is the source cylinder count less 35.
5C59
SUB E 93
SUBtract Register E, the destination cylinder count less 35, from Register A. A result of zero means the two disks have the same number of cylinders.
5C5A
If the Z FLAG has been set the cylinder counts match and a mirror image can go ahead unconditionally, so JUMP forward to 5C66H.
5C5C
LD A,(6C01H) 3A 01 6C
Self-Modifying Code
Load Register A from 6C01H, the backup-by-class flag that was assembled at 5B2DH.
5C5F
OR A B7
OR Register A with itself to test that flag for zero.
5C60
If the NZ FLAG has been set the operator has already asked for a backup by class, so JUMP to the engine selector at 6C00H. The cylinder counts do not matter, because a file-by-file copy does not care.
5C63
GOSUB to 5C72H, which displays Cylinder count differs - Attempt mirror-image backup ? and returns with the Z FLAG set when the operator answers Y.
5C66
If the Z FLAG has been set, either the cylinder counts matched or the operator agreed to a mirror image anyway, so JUMP to the engine selector at 6C00H, which will find the class flag clear and bring the mirror-image engine down.
5C69
LD HL,6F83H 21 83 6F
Point Register Pair HL at the message at 6F83H, Backup-reconstruct invoked. Every path that finds the two disks incompatible arrives here.
5C6C
GOSUB to the resident @LOGOT vector at 447BH to display it, so that the operator knows the backup will be file by file rather than cylinder by cylinder.
5C6F
JUMP to 6C13H, the entry to the engine selector that is one instruction past the class test. Entering there rather than at 6C00H forces the file-by-file engine regardless of what the class flag says, which is exactly what a reconstruct is: the same file-by-file copy, chosen by the utility rather than by the operator.

5C72H - Ask About a Cylinder Count Mismatch

A short routine that displays the question, collects a reply and reports whether it was Y. It is the only question in the front end whose answer changes which engine runs.

5C72
LD HL,6F9EH 21 9E 6F
Point Register Pair HL at the question at 6F9EH, Cylinder count differs - Attempt mirror-image backup ?, terminated by 03H so that the cursor stays on the line for the reply.
5C75
GOSUB to the resident @DSPLY vector at 4467H to display the question.
5C78
LD HL,525EH 21 5E 52
Point Register Pair HL at 525EH, the reply buffer, which is one byte above the buffer the drive prompts use.
5C7B
LD B,03H 06 03
Load Register B with 03H, so that the line-input routine will take up to three characters, which is enough for a reply of YES or NO.
5C7D
GOSUB to the ROM line-input routine at 0040H to collect the reply, returning with Register Pair HL pointing at the buffer.
5C80
If the CARRY FLAG has been set the operator pressed BREAK, so JUMP to the abort exit at 52ACH.
5C83
LD A,(HL) 7E
Fetch the first character of the reply from the address in Register Pair HL into Register A.
5C84
RES 5,A CB AF
RESet bit 5 of Register A, which folds a lower-case letter to upper case, so that y and Y are both accepted.
5C86
CP 59H FE 59
Compare Register A against 59H, the letter Y. If Register A holds Y, the Z FLAG is set.
5C88
RET C9
RETurn to the caller with the Z FLAG reflecting the reply. Anything other than Y means no, which is the manual's rule that a reply of N forces a backup reconstruct.

5C89H - The Master Password Check

A disk still carrying the default master password is accepted without a word. Otherwise the password is taken from the MPW parameter if it was given and from the keyboard if it was not, hashed by SYS2/SYS through the supervisor stub at 6C6BH, and compared twice: once against the reserved bypass constant that SYS2 itself honours, and once against the hash stored in the disk's own Granule Allocation Table. A wrong password ends the backup with no retry.

5C89
LD HL,(57CEH) 2A CE 57
Load Register Pair HL from 57CEH, the master-password hash inside the source disk's Granule Allocation Table, at Granule Allocation Table offset CEH.
5C8C
LD DE,42E0H 11 E0 42
Load Register Pair DE with 42E0H, the sixteen-bit hash of the eight characters PASSWORD, which is the default master password every formatted LDOS disk carries.
5C8F
XOR A AF
Clear Register A to zero, which also clears the CARRY FLAG ready for the subtraction on the next line.
5C90
SBC HL,DE ED 52
SUBtract Register Pair DE from Register Pair HL with the CARRY FLAG clear. A result of zero means the disk still has the default master password.
5C92
RET Z C8
RETurn if the Z FLAG has been set. The disk is not password protected in any meaningful sense, so nothing is asked.
5C93
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE from the operand at 5C94H, which is the MPW parameter result cell. @PARAM stores the address of the quoted password there, or leaves it at 0000H when MPW was not given.
5C96
LD HL,6FD6H 21 D6 6F
Point Register Pair HL at the prompt at 6FD6H, Master password ?, which will be used only if the password was not passed on the command line.
5C99
GOSUB to 6C68H, which collects eight characters either from the address in Register Pair DE or, when that address is zero, from the keyboard after displaying the prompt at Register Pair HL, and then falls into the supervisor stub that issues request code 0E4H. SYS2/SYS hashes the eight bytes and returns the sixteen-bit result in Register Pair HL.
5C9C
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, moving the returned hash into Register Pair DE where the two comparisons below can both reach it.
5C9D
LD HL,113DH 21 3D 11
Load Register Pair HL with 113DH, the constant SYS2 treats as a master override. It is the hash of the eight characters RS0LT0FF, which is the password the file-by-file engine appends to every filespec it opens from the string at 63E0H. A filespec carrying no password hashes to 4296H and can never reach it.
5CA0
XOR A AF
Clear Register A to zero, which clears the CARRY FLAG for the subtraction below.
5CA1
SBC HL,DE ED 52
SUBtract Register Pair DE, the hash of what the operator typed, from Register Pair HL.
5CA3
RET Z C8
RETurn if the Z FLAG has been set. The reply hashed to the bypass constant, so the disk is accepted.
5CA4
LD HL,(57CEH) 2A CE 57
Self-Modifying Code
Load Register Pair HL from 57CEH, the source disk's own master-password hash. The mirror-image engine overwrites the operand region here at 5EAFH with the sectors-per-cylinder count once it has been moved down to this address.
5CA7
XOR A AF
Clear Register A to zero to clear the CARRY FLAG for the subtraction below.
5CA8
SBC HL,DE ED 52
SUBtract Register Pair DE, the hash of the reply, from Register Pair HL, the disk's stored hash.
5CAA
RET Z C8
RETurn if the Z FLAG has been set. The operator gave the right master password.
5CAB
LD HL,6FEEH 21 EE 6F
Point Register Pair HL at the message at 6FEEH, Invalid master password.
5CAE
JUMP to the abort entry at 52AFH with that message. There is no retry: a wrong master password ends the backup.

5CB1H - The Filespec Character Filter

One routine fills both the file name mask and the extension mask. It accepts the digits, the letters in either case and the dollar sign that is the LDOS wildcard, folds lower case to upper so that the mask matches the form a directory record stores, and stops at the first character that is none of those, handing that character back to the caller so that the caller can see whether the field ended in a slash, a colon or something else.

5CB1
CP 24H FE 24
Compare Register A against 24H, a dollar sign, which is the LDOS wildcard character. If Register A holds it, the Z FLAG is set.
5CB3
If the Z FLAG has been set the character is the wildcard, so JUMP forward to 5CBFH to store it without any further checking. A dollar sign is not a letter or a digit but is always accepted.
5CB5
CP 41H FE 41
Compare Register A against 41H, the letter A. If Register A is below it, the CARRY FLAG is set.
5CB7
If the NO CARRY FLAG has been set the character is at 41H or above, which covers the upper-case letters and the lower-case ones, so JUMP forward to 5CBFH to fold and store it.
5CB9
CP 3AH FE 3A
Compare Register A against 3AH, the character after the digit 9. If Register A is below it, the CARRY FLAG is set.
5CBB
RET NC D0
RETurn if the NO CARRY FLAG has been set. The character lies between the digits and the letters, so it is not part of a filespec and it is the character that ends the field. It is returned to the caller in Register A so that the caller can see what stopped the scan.
5CBC
CP 30H FE 30
Compare Register A against 30H, the digit 0.
5CBE
RET C D8
RETurn if the CARRY FLAG has been set. The character is below the digits, so again it ends the field and is handed back in Register A.
5CBF
CP 61H FE 61
Compare Register A against 61H, the lower-case letter a.
5CC1
If the CARRY FLAG has been set the character is already upper case or is the wildcard, so JUMP forward to 5CC5H to store it unchanged.
5CC3
RES 5,A CB AF
RESet bit 5 of Register A, which is the single bit that separates a lower-case letter from its upper-case form, so that the mask is stored in the case a directory record uses.
5CC5
LD (DE),A 12
Store Register A, the accepted character, at the address in Register Pair DE, which points into either the filename mask at 5205H or the extension mask at 520DH.
5CC6
INC DE 13
INCrement Register Pair DE by 1 to step to the next position in the mask.
5CC7
LD A,(HL) 7E
Fetch the next character of the command line from the address in Register Pair HL into Register A.
5CC8
INC HL 23
INCrement Register Pair HL by 1 to step past it.
5CC9
DECrement Register B by 1 and LOOP BACK to 5CB1H while it is not zero, so that at most eight characters go into a name and three into an extension. Anything beyond the field width is left in the command line for the caller to look at.
5CCB
RET C9
RETurn to the caller with the field filled and the stopping character in Register A.

5CCCH - The DATE Parameter Parser

The DATE parameter accepts four forms and this block handles all of them. A group of month, day and year is split on the slash into three fields stored downwards, each field is collected as exactly two digits, the year is moved into the right century and tested for a leap year, the month is range checked and the day is checked against the maximum-days table at 7006H, and the validated result is packed into the same two bytes a directory record uses at its offsets 01H and 02H so that the selection test later is a plain sixteen-bit comparison. Note that the leap-year adjustment modifies the February entry of that table in place and never puts it back, which is harmless because at most two dates are parsed in one run of the utility.

5CCC
LD A,(HL) 7E
Fetch the first character of the date group from the address in Register Pair HL into Register A.
5CCD
LD C,2FH 0E 2F
Load Register C with 2FH, a slash, which is the separator the splitter below looks for between the month, the day and the year.
5CCF
GOSUB to 5D20H, which splits the group on the slash and stores the three two-digit values downwards at 525FH, 525EH and 525DH as month, day and year.
5CD2
If the NZ FLAG has been set a field was malformed, so JUMP forward to 5D1AH to report a bad date format.
5CD4
LD A,(DE) 1A
Fetch the year from the address in Register Pair DE, which the splitter left pointing at 525DH.
5CD5
CP 0CH FE 0C
Compare Register A against 0CH, twelve. LDOS x.3 dating treats stored years 0 to 11 as 2000 to 2011 and 12 upwards as 1980 to 1999.
5CD7
If the NO CARRY FLAG has been set the year is 12 or more, so it is already a year in the nineteen hundreds and needs no adjustment. JUMP forward to 5CDCH.
5CD9
ADD A,64H C6 64
ADD 64H, one hundred, to Register A. A two-digit year below twelve is in the two thousands, and adding a century here is what lets one comparison order dates across the boundary.
5CDB
LD (DE),A 12
Store the adjusted year back at the address in Register Pair DE.
5CDC
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, so that Register Pair HL now addresses the date fields and Register Pair DE holds the command-line pointer.
5CDD
AND 03H E6 03
AND Register A with 03H to keep the low two bits of the year, which is the year modulo four and therefore zero in a leap year.
5CDF
LD HL,7007H 21 07 70
Point Register Pair HL at 7007H, the entry for February in the twelve-byte maximum-days table that runs from 7006H to 7011H and holds 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31.
5CE2
If the NZ FLAG has been set the year is not divisible by four and is not a leap year, so JUMP forward to 5CE5H leaving February at 28 days.
5CE4
INC (HL) 34
INCrement the byte at the address in Register Pair HL by 1, making February 29 days for this leap year. The table is modified in place and is not put back, which is harmless because at most two dates are parsed in one run.
5CE5
LD A,(525FH) 3A 5F 52
Load Register A from 525FH, the month the splitter stored.
5CE8
DEC A 3D
DECrement Register A by 1 to turn the month into a zero-based index into the maximum-days table.
5CE9
CP 0CH FE 0C
Compare Register A against 0CH, twelve. A month of zero wraps to 0FFH here and a month above twelve stays above twelve, so one unsigned comparison rejects both.
5CEB
If the NO CARRY FLAG has been set the month is not in the range 1 to 12, so JUMP forward to 5D1AH to report a bad date format.
5CED
DEC HL 2B
DECrement Register Pair HL by 1 so that it points at 7006H, the first entry of the maximum-days table, rather than at February.
5CEE
ADD A,L 85
ADD Register L to Register A, so that Register A holds the low byte of the address of this month's entry.
5CEF
LD L,A 6F
Load Register L with Register A, completing the low half of the pointer.
5CF0
If the NO CARRY FLAG has been set the addition did not cross a page boundary, so JUMP forward to 5CF3H.
5CF2
INC H 24
INCrement Register H by 1 to carry into the high half of the pointer.
5CF3
LD A,(525EH) 3A 5E 52
Load Register A from 525EH, the day the splitter stored.
5CF6
DEC A 3D
DECrement Register A by 1, so that a day of zero wraps to 0FFH and will fail the comparison below.
5CF7
CP (HL) BE
Compare Register A against the byte at the address in Register Pair HL, the number of days in this month. If Register A is below it, the CARRY FLAG is set.
5CF8
If the NO CARRY FLAG has been set the day is not valid for this month, so JUMP forward to 5D1AH to report a bad date format. Because February was adjusted at 5CE4H, the twenty-ninth is accepted in a leap year and refused otherwise.
5CFA
LD HL,525FH 21 5F 52
Point Register Pair HL at 525FH, the month, ready to pack the three validated fields into the two-byte form a directory record uses.
5CFD
LD B,(HL) 46
Load Register B with the byte at the address in Register Pair HL, the month.
5CFE
LD C,00H 0E 00
Load Register C with 00H, the low half of the packed word.
5D00
SRL B CB 38
Shift Register B right logically, moving the low bit of the month into the CARRY FLAG.
5D02
RR C CB 19
Rotate Register C right through the CARRY FLAG, bringing that bit into bit 7 of Register C. The month therefore straddles the two bytes exactly as it does in a directory record.
5D04
DEC HL 2B
DECrement Register Pair HL by 1 so that it points at 525EH, the day.
5D05
LD A,(HL) 7E
Load Register A with the byte at the address in Register Pair HL, the day.
5D06
DEC HL 2B
DECrement Register Pair HL by 1 so that it points at 525DH, the year.
5D07
RLCA 07
Rotate Register A left circularly, the first of two shifts that move the day into bits 7 to 3 of the low byte.
5D08
RLCA 07
Rotate Register A left circularly again, completing the shift.
5D09
OR C B1
OR Register A with Register C, folding the low bit of the month in beside the day.
5D0A
LD C,A 4F
Load Register C with Register A, the completed low byte of the packed date.
5D0B
LD A,(HL) 7E
Load Register A with the byte at the address in Register Pair HL, the year, adjusted at 5CD9H if it was in the two thousands.
5D0C
SUB 50H D6 50
SUBtract 50H, eighty, from Register A, converting the year into an offset from 1980, which is the epoch LDOS dates count from.
5D0E
CP 70H FE 70
Compare Register A against 70H. An offset above that came from a year outside the range the format can hold.
5D10
If the CARRY FLAG has been set the offset is in range, so JUMP forward to 5D13H.
5D12
XOR A AF
Clear Register A to zero, which stands for 1980, so that an out-of-range year is clamped rather than rejected.
5D13
RLCA 07
Self-Modifying Code
Rotate Register A left circularly, the first of three shifts that move the year up beside the month. The file-by-file engine overwrites the operand region here at 642CH with the directory attribute byte of the record it is examining, once it has been moved down to this address.
5D14
RLCA 07
Rotate Register A left circularly again.
5D15
RLCA 07
Rotate Register A left circularly a third time, leaving the year in the top bits.
5D16
OR B B0
OR Register A with Register B, the remaining bits of the month, completing the high byte of the packed date.
5D17
LD B,A 47
Load Register B with Register A, so that Register Pair BC now holds the date in exactly the two-byte form a directory record carries at its offsets 01H and 02H.
5D18
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the command-line pointer back in Register Pair HL for the caller.
5D19
RET C9
RETurn to the caller with the packed date in Register Pair BC and the command-line pointer in Register Pair HL.
5D1A
LD HL,7012H 21 12 70
Point Register Pair HL at the message at 7012H, Bad date format. Every failure in the date parser arrives here.
5D1D
JUMP to the abort entry at 52AFH with that message. A malformed date ends the backup rather than being ignored.
5D20
LD DE,525FH 11 5F 52
Point Register Pair DE at 525FH, the highest of the three date fields. The fields are filled downwards, so the first one parsed is the month and the last is the year.
5D23
LD B,03H 06 03
Load Register B with 03H, the three fields of a date group.
5D25
PUSH DE D5
PUSH Register Pair DE onto the stack so that the field pointer survives the number collector below, which uses Register E for its accumulator.
5D26
GOSUB to 5D35H, which collects up to two ASCII digits from the address in Register Pair HL and returns the binary value in Register A, with the Z FLAG set on success.
5D29
POP DE D1
POP the field pointer back into Register Pair DE.
5D2A
RET NZ C0
RETurn if the NZ FLAG has been set. The field was not a number, and the caller will report a bad date format.
5D2B
LD (DE),A 12
Store Register A, the collected value, at the address in Register Pair DE.
5D2C
DEC B 05
DECrement Register B by 1, one fewer field to collect.
5D2D
RET Z C8
RETurn if the Z FLAG has been set. All three fields have been stored and Register Pair DE points at the last of them, the year, which is what the caller reads first at 5CD4H.
5D2E
DEC DE 1B
DECrement Register Pair DE by 1 to step down to the next field.
5D2F
LD A,(HL) 7E
Fetch the character that stopped the number collector from the address in Register Pair HL into Register A.
5D30
INC HL 23
INCrement Register Pair HL by 1 to step past it.
5D31
CP C B9
Compare Register A against Register C, which the caller set to 2FH, the slash that separates the fields.
5D32
If the Z FLAG has been set the separator was found, so LOOP BACK to 5D25H to collect the next field.
5D34
RET C9
RETurn with the NZ FLAG set. The separator was missing, so the group is malformed.
5D35
GOSUB to 5D4BH, which converts one ASCII character at the address in Register Pair HL into a binary digit and returns with the CARRY FLAG set when it was a digit.
5D38
If the NO CARRY FLAG has been set the first character was not a digit at all, so JUMP forward to 5D49H to return failure.
5D3A
LD E,A 5F
Load Register E with Register A, the first digit, as the accumulator.
5D3B
RLCA 07
Rotate Register A left circularly, doubling the digit.
5D3C
RLCA 07
Rotate Register A left circularly again, so that Register A holds four times the digit.
5D3D
ADD A,E 83
ADD Register E to Register A, giving five times the digit.
5D3E
RLCA 07
Rotate Register A left circularly, giving ten times the digit. Four shifts and one add are how the routine multiplies by ten without a multiply instruction.
5D3F
LD E,A 5F
Load Register E with Register A, the first digit scaled to the tens column.
5D40
GOSUB to 5D4BH to convert the second character.
5D43
If the NO CARRY FLAG has been set the second character was not a digit, so the field was a single digit. JUMP forward to 5D49H, where Register A still holds it, and return failure so that the caller rejects a one-digit field.
5D45
ADD A,E 83
ADD Register E to Register A, adding the tens to the units to give the two-digit value.
5D46
LD E,A 5F
Load Register E with Register A, keeping the value.
5D47
CP A BF
Compare Register A against itself, which always sets the Z FLAG and clears the CARRY FLAG. This is the success indication the caller tests.
5D48
RET C9
RETurn to the caller with the value in Register A and the Z FLAG set.
5D49
OR A B7
OR Register A with itself. Register A holds the character that was not a digit, and this returns with the NZ FLAG set unless that character happened to be zero.
5D4A
RET C9
RETurn to the caller with failure indicated.
5D4B
LD A,(HL) 7E
Fetch a character from the address in Register Pair HL into Register A.
5D4C
INC HL 23
INCrement Register Pair HL by 1 to step past it.
5D4D
SUB 30H D6 30
SUBtract 30H from Register A, converting an ASCII digit into its binary value and leaving anything below the digits as a large number.
5D4F
CP 0AH FE 0A
Compare Register A against 0AH, ten. If Register A is below it, the CARRY FLAG is set, which is how this routine reports that the character was a digit.
5D51
RET C9
RETurn to the caller with the binary digit in Register A and the CARRY FLAG set when it was valid.

Unloaded Storage
The image resumes at 5E00H. The 174 bytes from 5D52H to 5DFFH are not written by any load record. Nothing in the front end refers to them, and they exist only because the next load record begins on a page boundary.

5E00H - Mirror Image Engine, Pack ID and Lock Out Map Tests

The mirror-image engine is loaded here at 5E00H but is assembled to run at 5A00H, over the front end. The selector at 6C06H moves it down before it is entered, so every address in the mnemonic column of these sections is 0400H below the address in the leftmost column, and the links follow the operand to the place the instruction is shown rather than the place it runs. The engine begins with the two tests that can refuse the whole backup before a single byte moves: the Pack IDs must match, and no cylinder that carries data on the source may be locked out on the destination.

5E00
LD HL,57CEH 21 CE 57
Point Register Pair HL at 57CEH, the master-password hash inside the saved copy of the source disk's Granule Allocation Table. The ten bytes from here are the hash at offset CEH followed by the eight-character disk name at offset D0H, which together are what the manual calls the Pack ID.
5E03
LD DE,58CEH 11 CE 58
Point Register Pair DE at 58CEH, the same ten bytes inside the destination disk's Granule Allocation Table.
5E06
LD B,0AH 06 0A
Load Register B with 0AH, the ten bytes of a Pack ID, as the loop counter.
5E08
LD A,(DE) 1A
Fetch a byte of the destination Pack ID from the address in Register Pair DE into Register A.
5E09
CP (HL) BE
Compare Register A against the byte at the address in Register Pair HL, the same position in the source Pack ID. If they are equal, the Z FLAG is set.
5E0A
If the Z FLAG has been set the two bytes match, so JUMP forward to 5E2BH to advance and compare the next one.
5E0C
LD HL,5DE5H 21 E5 5D
Point Register Pair HL at the question at 61E5H, Different pack IDs! Abort backup ?. The Pack IDs differ, and the manual explains that this check is what stops the wrong pair of disks being backed up.
5E0F
GOSUB to the resident @DSPLY vector at 4467H to display the question, which is terminated by 03H so that the cursor stays on the line.
5E12
LD HL,430FH 21 0F 43
Point Register Pair HL at SFLAG$ 430FH, the resident system flag byte.
5E15
BIT 5,(HL) CB 6E
Test bit 5 of the byte at the address in Register Pair HL, which the DOS sets while a job file is running.
5E17
If the NZ FLAG has been set a job is in effect, so JUMP forward to 5E28H to abort without waiting for a reply. The manual states that a mirror image run from a job aborts on a Pack ID mismatch rather than asking.
5E19
LD HL,525DH 21 5D 52
Point Register Pair HL at 525DH, the reply buffer.
5E1C
LD B,03H 06 03
Load Register B with 03H, so that the line-input routine will accept a reply of up to three characters.
5E1E
GOSUB to the ROM line-input routine at 0040H to collect the reply.
5E21
LD A,(HL) 7E
Fetch the first character of the reply from the address in Register Pair HL into Register A.
5E22
RES 5,A CB AF
RESet bit 5 of Register A, folding a lower-case letter to upper case so that n and N are both accepted.
5E24
CP 4EH FE 4E
Compare Register A against 4EH, the letter N, which is the reply that means carry on with the backup.
5E26
If the Z FLAG has been set the operator answered N, so JUMP forward to 5E2FH to go on with the backup despite the mismatch.
5E28
JUMP to the abort exit at 52ACH. The operator answered anything other than N, or a job was in effect, so the backup ends with Backup aborted.
5E2B
INC DE 13
INCrement Register Pair DE by 1 to step to the next byte of the destination Pack ID.
5E2C
INC HL 23
INCrement Register Pair HL by 1 to step to the next byte of the source Pack ID.
5E2D
DECrement Register B by 1 and LOOP BACK to 5E08H while it is not zero, so that all ten bytes are compared.
5E2F
LD HL,5860H 21 60 58
Point Register Pair HL at 5860H, the lock-out map inside the destination Granule Allocation Table, which lives at Granule Allocation Table offset 60H and carries one byte per cylinder recording the granules that were locked out at format time.
5E32
LD DE,5760H 11 60 57
Point Register Pair DE at 5760H, the lock-out map inside the source Granule Allocation Table.
5E35
LD B,60H 06 60
Load Register B with 60H, ninety-six, the largest number of cylinders the map covers, as the loop counter.
5E37
LD A,(DE) 1A
Fetch this cylinder's source lock-out byte from the address in Register Pair DE into Register A.
5E38
CPL 2F
ComPLement Register A, so that a set bit now means a granule that is available on the source rather than one that is locked out.
5E39
LD C,A 4F
Load Register C with Register A, keeping the mask of usable source granules.
5E3A
PUSH DE D5
PUSH Register Pair DE onto the stack, because the next three lines move it back by one page to reach the allocation map.
5E3B
LD A,E 7B
Load Register A from Register E, the low byte of the pointer into the source lock-out map.
5E3C
SUB 60H D6 60
SUBtract 60H from Register A, which is the offset of the lock-out map inside the Granule Allocation Table, so that the pointer now addresses the allocation map at the top of the same sector.
5E3E
LD E,A 5F
Load Register E with Register A, completing the adjusted pointer.
5E3F
LD A,(DE) 1A
Fetch this cylinder's source allocation byte from the address in Register Pair DE into Register A. A set bit here means a granule that carries data.
5E40
POP DE D1
POP the lock-out map pointer back into Register Pair DE.
5E41
AND C A1
AND Register A with Register C, so that Register A now holds the granules that carry data on the source and are not themselves locked out.
5E42
AND (HL) A6
AND Register A with the byte at the address in Register Pair HL, the destination lock-out byte for the same cylinder. A set bit in the result is a granule that has data on the source and is locked out on the destination.
5E43
If the NZ FLAG has been set at least one such granule exists, so JUMP to 616EH, which reports Backup aborted, destination not mirror-image. This is the manual's explanation that the message appears when the destination is missing a cylinder that contains information on the source.
5E46
INC DE 13
INCrement Register Pair DE by 1 to step to the next cylinder of the source lock-out map.
5E47
INC HL 23
INCrement Register Pair HL by 1 to step to the next cylinder of the destination lock-out map.
5E48
DECrement Register B by 1 and LOOP BACK to 5E37H while it is not zero, checking all ninety-six cylinders before a single byte of data is moved.

Relocated Block
Everything from 5E00H to 6208H is loaded where it is shown and executed 0400H lower. An operand such as the jump at 5F54H reads 5AEEH because that is where the instruction it targets will be when the block move has been done; the link on that row goes to 5EEEH, which is where the same instruction is shown on this page. The two are the same instruction seen before and after the move. Only JP and CALL carry an absolute operand and therefore only those show a run-time address; the disassembler resolves JR and DJNZ from their displacements, so those operands are already the addresses shown in the leftmost column. Throughout these sections the descriptions cite the address as it appears on this page, which is also where the link goes. Note also that eight of the addresses in this range are written into by the front end before the move and mean something else entirely until then, which is why several rows below are marked as self-modifying in both directions.

5E4AH - Preparing the Destination and Marking It

The destination boot sector is read, given the source disk's directory cylinder and geometry if the disk is a dual-density one, and written back with a marker byte in its first position. That marker is how the destination is recognised for the rest of the run whenever the operator is asked to swap disks, and it is cleared again at 60B2H when the backup finishes.

5E4A
GOSUB to 5394H to mount the destination disk, prompting for it if this is a single-drive backup.
5E4D
LD HL,5900H 21 00 59
Point Register Pair HL at 5900H, the general sector buffer.
5E50
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with 0000H, cylinder 0 and sector 0. The file-by-file engine writes the top of memory over the operand at 5E51H when it is the engine that has been moved down to this address.
5E53
GOSUB to 546CH, the disk-operation stub for operation 09H, to read the destination boot sector into the buffer.
5E56
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H.
5E59
BIT 7,(IY+04H) FD CB 04 7E
Test bit 7 of the byte at IY+04H, which the geometry reader at 6D34H sets on a disk whose cylinder 0 is single density while the rest is double.
5E5D
If the Z FLAG has been set the disk is uniform, so JUMP forward to 5E72H.
5E5F
LD HL,55F8H 21 F8 55
Point Register Pair HL at 55F8H, the geometry scratch area holding the eighteen bytes taken out of the source boot sector by the front end at 5B95H and 5B9BH.
5E62
PUSH DE D5
PUSH Register Pair DE onto the stack to preserve the cylinder and sector across the two block moves below.
5E63
LD DE,5909H 11 09 59
Self-Modifying Code
Point Register Pair DE at 5909H, offset 09H of the destination boot sector image in the buffer. The file-by-file engine writes the top of memory over the operand at 5E66H when it occupies this address.
5E66
LD BC,0002H 01 02 00
Load Register Pair BC with 0002H, two bytes, as the block-move count.
5E69
LDIR ED B0
Block move the source disk's directory cylinder and the byte that follows it into the destination boot sector image, so that the copy will boot to the same directory cylinder as the original.
5E6B
LD E,16H 1E 16
Load Register E with 16H so that Register Pair DE points at 5916H, offset 16H of the boot sector image.
5E6D
LD C,10H 0E 10
Load Register C with 10H, sixteen bytes, as the count for the second move. Register B is already zero.
5E6F
LDIR ED B0
Block move sixteen bytes of the source disk's geometry into the destination boot sector image, so that the dual-density arrangement of the original is reproduced.
5E71
POP DE D1
POP the cylinder and sector back into Register Pair DE.
5E72
LD HL,5900H 21 00 59
Point Register Pair HL at 5900H, the first byte of the boot sector image.
5E75
LD (HL),76H 36 76
Store 76H at the address in Register Pair HL. That byte is the marker the destination-verification routine at 53BBH looks for whenever the operator is asked to put the destination disk back, so writing it now is what makes the disk identifiable for the rest of the backup.
5E77
GOSUB to 5464H, the disk-operation stub for operation 0DH, to write the marked boot sector back to the destination.
5E7A
If the NZ FLAG has been set the write failed, so JUMP to the disk-error exit at 52A0H.

5E7DH - Computing the Geometry and Planting It

The number of sectors in a cylinder, the number of sectors on a single-density cylinder 0, and the number of whole cylinders that will fit in the work space are all worked out once, here, and then written into eight separate immediate operands scattered through the three transfer loops. That is why the loops themselves contain no memory references for geometry: every constant they need has already been assembled into the instruction stream.

5E7D
GOSUB to 5327H to mount the source disk, prompting for it if this is a single-drive backup.
5E80
LD A,(IY+09H) FD 7E 09
Load Register A with the byte at IY+09H, the directory cylinder of the disk in the drive.
5E83
LD (5BA6H),A 32 A6 5B
Self-Modifying Code
Store the directory cylinder at 5BA6H, which after the block move is the operand of the comparison at 5FA5H. The write pass therefore tests each cylinder against the directory cylinder without a memory reference, and uses a deleted data address mark on that one cylinder alone.
5E86
LD A,(IY+07H) FD 7E 07
Load Register A with the byte at IY+07H, the allocation byte of the Drive Control Table entry, whose bits 4 to 0 are the highest sector number on a track and whose bits 7 to 5 are the head count less one.
5E89
LD B,A 47
Load Register B with Register A, keeping the whole byte while the two fields are separated.
5E8A
AND 1FH E6 1F
AND Register A with 1FH to keep only the highest sector number.
5E8C
LD C,A 4F
Load Register C with Register A, that sector number.
5E8D
INC C 0C
INCrement Register C by 1, turning the highest sector number into a count of sectors per track.
5E8E
XOR B A8
Exclusive-OR Register A with Register B, cancelling the sector field and leaving only the head count field in bits 7 to 5.
5E8F
RLCA 07
Rotate Register A left circularly, the first of three shifts that bring the head count down into the low bits.
5E90
RLCA 07
Rotate Register A left circularly again.
5E91
RLCA 07
Rotate Register A left circularly a third time, so that Register A holds the head count less one.
5E92
INC A 3C
INCrement Register A by 1, giving the number of heads.
5E93
LD B,A 47
Load Register B with Register A, the head count, as the loop counter for the multiplication below.
5E94
XOR A AF
Clear Register A to zero, the running total.
5E95
ADD A,C 81
ADD Register C, the sectors per track, to Register A.
5E96
DECrement Register B by 1 and LOOP BACK to 5E95H while it is not zero. Repeated addition is how the sectors per track are multiplied by the head count, giving the sectors per cylinder.
5E98
LD C,0AH 0E 0A
Load Register C with 0AH, ten, the number of sectors on a single-density track, which is what cylinder 0 carries on a dual-density system disk.
5E9A
BIT 5,(IY+04H) FD CB 04 6E
Test bit 5 of the byte at IY+04H, the double-sided flag the geometry reader planted at 6D40H.
5E9E
If the Z FLAG has been set the disk is single sided, so JUMP forward to 5EA3H with both counts as they stand.
5EA0
ADD A,A 87
ADD Register A to itself, doubling the sectors per cylinder because a double-sided disk has two tracks per cylinder.
5EA1
RLC C CB 01
Rotate Register C left circularly, doubling the single-density count from ten to twenty for the same reason.
5EA3
LD (5B41H),A 32 41 5B
Self-Modifying Code
Store the sectors per cylinder at 5B41H, which after the block move is the operand of the comparison at 5F40H that ends the read pass on each cylinder.
5EA6
LD (5BCBH),A 32 CB 5B
Self-Modifying Code
Store the same value at 5BCBH, the operand of the comparison at 5FCAH that ends the write pass.
5EA9
LD (5C2BH),A 32 2B 5C
Self-Modifying Code
Store it again at 5C2BH, the operand of the comparison at 602AH that ends the verify pass.
5EAC
LD (5D42H),A 32 42 5D
Self-Modifying Code
Store it again at 5D42H, the operand of the comparison at 6141H that ends the pass which clears the modification flags on the source directory.
5EAF
LD (5CA4H),A 32 A4 5C
Self-Modifying Code
Store it once more at 5CA4H, the operand of the comparison at 60A3H that ends the pass which clears the modification flags on the destination directory. Five copies of one number, each planted as an immediate operand, is what lets the three transfer loops run without a memory reference per sector.
5EB2
LD B,A 47
Load Register B with Register A, keeping the sectors per cylinder.
5EB3
LD A,C 79
Load Register A from Register C, the number of sectors on a single-density cylinder 0.
5EB4
LD (5B3DH),A 32 3D 5B
Self-Modifying Code
Store it at 5B3DH, the operand of the comparison at 5F3CH, which is the read pass's alternative end condition for cylinder 0 on a dual-density disk.
5EB7
LD (5BC7H),A 32 C7 5B
Self-Modifying Code
Store it at 5BC7H, the operand of the comparison at 5FC6H, the same condition in the write pass.
5EBA
LD (5C27H),A 32 27 5C
Self-Modifying Code
Store it at 5C27H, the operand of the comparison at 6026H, the same condition in the verify pass.
5EBD
LD HL,(4049H) 2A 49 40
Load Register Pair HL from HIGH$ at 4049H, the highest address available to a transient program.
5EC0
INC HL 23
INCrement Register Pair HL by 1 and, with the next line, round the address down to a page boundary.
5EC1
DEC H 25
DECrement Register H by 1, so that Register H is the number of the last whole page below HIGH$.
5EC2
LD DE,(5285H) ED 5B 85 52
Load Register Pair DE from the free-space pointer at 5285H, the bottom of the work space.
5EC6
LD A,H 7C
Load Register A from Register H, the top page of the work space.
5EC7
SUB D 92
Self-Modifying Code
SUBtract Register D, the bottom page, from Register A, giving the number of whole pages of buffer available. Each sector occupies one page. The front end used the operand region here as the destination drive number at 5BC4H before the block move.
5EC8
LD C,0FFH 0E FF
Load Register C with 0FFH, one less than zero, as the counter for the division below.
5ECA
INC C 0C
INCrement Register C by 1, counting one more whole cylinder that fits in the buffer.
5ECB
SUB B 90
SUBtract Register B, the sectors per cylinder, from Register A.
5ECC
If the NO CARRY FLAG has been set another whole cylinder still fits, so LOOP BACK to 5ECAH. Repeated subtraction divides the buffer size by the cylinder size.
5ECE
LD A,C 79
Load Register A from Register C, the number of whole cylinders the work space can hold at once.
5ECF
LD (5B4CH),A 32 4C 5B
Self-Modifying Code
Store it at 5B4CH, the operand of the comparison at 5F4BH, which is what decides how many cylinders the read pass takes before it stops and hands over to the write pass.

5ED2H - Suppressing the Clock

A sector transfer on a Model I is timed against the drive, and a clock interrupt in the middle of one loses data. The engine therefore saves the byte at 4012H inside the resident interrupt handler and writes a RET over it. Everything on the way out of the utility puts it back, and the note about the clock is displayed unconditionally because of it.

5ED2
DI F3
Disable interrupts. Everything from here to the end of the copy is timed against the drive rather than against the clock, and a clock interrupt in the middle of a sector transfer would lose data.
5ED3
LD A,(4012H) 3A 12 40
Load Register A from 4012H, a byte inside the resident interrupt handler.
5ED6
LD (52C1H),A 32 C1 52
Self-Modifying Code
Store it at 52C1H, the operand of the load at 52C0H, which is the routine that puts it back on every path out of the utility.
5ED9
LD A,0C9H 3E C9
Load Register A with 0C9H, the opcode of a RET.
5EDB
LD (4012H),A 32 12 40
Store it at 4012H, so that the resident interrupt handler returns immediately instead of servicing the real-time clock. This one byte is the whole reason the utility signs off with Note: Real time clock no longer accurate, and the manual describes it as merely an informative message.
5EDE
GOSUB to 5327H to mount the source disk before the first cylinder is read.
5EE1
XOR A AF
Clear Register A to zero.
5EE2
LD (5B45H),A 32 45 5B
Self-Modifying Code
Store zero at 5B45H, the operand of the load at 5F44H, which counts the cylinders sitting in the buffer. Each group of cylinders starts with the count at zero.
5EE5
LD D,00H 16 00
Load Register D with 00H, cylinder 0, so that the copy starts at the beginning of the disk.

5EE7H - The Read Pass

Cylinders are copied in groups as large as the work space will hold. For each cylinder the engine merges the source allocation byte with the two lock-out maps, which both decides whether the cylinder is worth copying at all and builds the destination Granule Allocation Table entry at the same time. A cylinder with nothing on it is skipped entirely, and the two boot sectors on cylinder 0 are never read, so that the destination keeps its own bootstrap.

5EE7
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, the base of the work space, which is where the first sector of the first cylinder will be read.
5EEA
LD A,D 7A
Load Register A from Register D, the cylinder the pass is to start at.
5EEB
LD (5B68H),A 32 68 5B
Self-Modifying Code
Store it at 5B68H, the operand of the load at 5F67H, so that the write pass will start at the same cylinder the read pass started at.
5EEE
PUSH HL E5
Loop Start
PUSH the buffer base in Register Pair HL onto the stack while the Granule Allocation Table bytes for this cylinder are worked out.
5EEF
LD H,57H 26 57
Load Register H with 57H so that Register Pair HL addresses the source Granule Allocation Table copy at 5700H.
5EF1
LD L,D 6A
Load Register L with Register D, the cylinder number, so that Register Pair HL points at this cylinder's allocation byte in the source Granule Allocation Table.
5EF2
LD C,(HL) 4E
Load Register C with the byte at the address in Register Pair HL, the source allocation byte for this cylinder. A set bit is a granule that carries data.
5EF3
LD A,D 7A
Load Register A from Register D, the cylinder number.
5EF4
ADD A,60H C6 60
ADD 60H to Register A, the offset of the lock-out map inside a Granule Allocation Table sector.
5EF6
LD L,A 6F
Load Register L with Register A, so that Register Pair HL now points at this cylinder's source lock-out byte.
5EF7
LD A,(HL) 7E
Fetch the source lock-out byte from the address in Register Pair HL into Register A.
5EF8
CPL 2F
ComPLement Register A, turning locked-out granules into a mask of usable ones.
5EF9
AND C A1
AND Register A with Register C, the source allocation byte, leaving only the granules that both carry data and are usable.
5EFA
LD H,58H 26 58
Load Register H with 58H so that Register Pair HL addresses the destination Granule Allocation Table copy at 5800H, still at the lock-out map offset.
5EFC
LD C,(HL) 4E
Load Register C with the byte at the address in Register Pair HL, the destination lock-out byte for this cylinder.
5EFD
OR C B1
OR Register A with Register C, folding the destination's locked-out granules into the value, so that the destination allocation byte will show them as unavailable as well as showing the copied data.
5EFE
LD L,D 6A
Load Register L with Register D, the cylinder number, so that Register Pair HL points at this cylinder's allocation byte in the destination Granule Allocation Table.
5EFF
LD (HL),A 77
Store Register A at the address in Register Pair HL, writing the merged allocation byte into the destination Granule Allocation Table copy.
5F00
CP C B9
Compare Register A against Register C, the destination lock-out byte. They are equal when the merge added nothing, which means this cylinder carries no data on the source and does not have to be copied at all.
5F01
POP HL E1
POP the buffer base back into Register Pair HL.
5F02
If the Z FLAG has been set this cylinder carries nothing that has to be copied, so JUMP to 5F50H to step to the next one without touching the disk. This is the manual's statement that only those cylinders which actually contain data are moved.
5F05
GOSUB to 5327H to mount the source disk, which on a single-drive backup means prompting for it and verifying its Pack ID.
5F08
PUSH HL E5
PUSH the buffer base in Register Pair HL onto the stack while the progress line is displayed.
5F09
LD E,00H 1E 00
Load Register E with 00H, sector 0, the first sector of this cylinder.
5F0B
LD A,D 7A
Load Register A from Register D, the cylinder number, as the value the formatter below will convert to three digits.
5F0C
LD HL,5DB3H 21 B3 5D
Point Register Pair HL at 61B3H, the three-character field inside the message block that holds the cylinder number.
5F0F
GOSUB to the number formatter at 6148H, which writes the value in Register A into the three characters at Register Pair HL, blanking leading zeros but always leaving at least one digit.
5F12
PUSH DE D5
PUSH Register Pair DE, the cylinder and sector, onto the stack across the two display calls.
5F13
LD HL,5D74H 21 74 5D
Point Register Pair HL at 6174H, which begins with 1DH so that the cursor returns to the start of the line, and reads Loading cylinder.
5F16
GOSUB to the resident @DSPLY vector at 4467H to display it.
5F19
LD HL,5DB3H 21 B3 5D
Point Register Pair HL at 61B3H, the three-digit cylinder number the formatter just built.
5F1C
GOSUB to the resident @DSPLY vector at 4467H to display it on the same line, which is why the number is rewritten in place as the copy proceeds.
5F1F
POP DE D1
POP the cylinder and sector back into Register Pair DE.
5F20
POP HL E1
POP the buffer address back into Register Pair HL.
5F21
LD A,0FEH 3E FE
Loop Start
Load Register A with 0FEH, a mask that clears the bottom bit of a sector number and leaves everything else.
5F23
AND E A3
AND Register A with Register E, the sector number. The result is zero only for sectors 0 and 1.
5F24
OR D B2
OR Register A with Register D, the cylinder number. The whole expression is zero only for sectors 0 and 1 of cylinder 0, which are the two boot sectors.
5F25
If the NZ FLAG has been set this is an ordinary sector, so GOSUB to 546CH, the disk-operation stub for operation 09H, to read it into the buffer. The two boot sectors are skipped, which is the manual's promise that the boot sector containing the bootstrap step rate remains untouched on the destination disk.
5F28
If the Z FLAG has been set the read was skipped, so JUMP forward to 5F2FH without examining any status.
5F2A
CP 06H FE 06
Compare Register A against 06H, the deleted data address mark status. Every directory sector reports it, so it has to be accepted here as a success.
5F2C
If the NZ FLAG has been set the read genuinely failed, so JUMP to the disk-error exit at 52A0H.
5F2F
INC H 24
INCrement Register H by 1, stepping the buffer pointer on by one page. Each sector occupies exactly one page of the work space, which is what makes the buffer arithmetic a single increment.
5F30
INC E 1C
INCrement Register E by 1 to step to the next sector of this cylinder.
5F31
LD A,E 7B
Load Register A from Register E, the new sector number, for the two comparisons below.
5F32
INC D 14
INCrement Register D by 1, the first half of a test that leaves Register D unchanged.
5F33
DEC D 15
DECrement Register D by 1, restoring it and setting the flags on the cylinder number.
5F34
If the NZ FLAG has been set this is not cylinder 0, so JUMP forward to 5F40H to use the ordinary sectors-per-cylinder count.
5F36
BIT 7,(IY+04H) FD CB 04 7E
Test bit 7 of the byte at IY+04H, the marker for a disk whose cylinder 0 is single density while the rest is double.
5F3A
If the Z FLAG has been set the disk is uniform, so JUMP forward to 5F40H and use the ordinary count for cylinder 0 as well.
5F3C
CP 00H FE 00
Self-Modifying Code
Compare Register A against the operand at 5F3DH, which the setup planted at 5EB4H with the number of sectors on a single-density cylinder. Cylinder 0 of a dual-density system disk holds fewer sectors than the rest of the disk.
5F3E
If the Z FLAG has been set the last sector of cylinder 0 has been read, so JUMP forward to 5F44H.
5F40
CP 00H FE 00
Self-Modifying Code
Compare Register A against the operand at 5F41H, the sectors per cylinder planted at 5EA3H.
5F42
If the NZ FLAG has been set there are more sectors on this cylinder, so LOOP BACK to 5F21H to read the next one.
5F44
LD A,00H 3E 00
Self-Modifying Code
Load Register A from the operand at 5F45H, the count of cylinders read into the buffer so far. It ships as zero and is reset to zero at 5EE2H before each group.
5F46
INC A 3C
INCrement Register A by 1, counting the cylinder that has just been read.
5F47
LD (5B45H),A 32 45 5B
Self-Modifying Code
Store the count back at 5B45H, which is the operand of the load above and is also read by the write pass and by the finish.
5F4A
LD B,A 47
Load Register B with Register A, the count, for the comparison below.
5F4B
LD A,00H 3E 00
Self-Modifying Code
Load Register A from the operand at 5F4CH, the number of whole cylinders the work space can hold, computed at 5ECFH.
5F4D
CP B B8
Compare Register A against Register B, the number read so far. If the buffer is full, the Z FLAG is set.
5F4E
If the Z FLAG has been set the buffer is full, so JUMP forward to 5F5EH to write everything in it to the destination.
5F50
INC D 14
INCrement Register D by 1 to step to the next cylinder.
5F51
LD A,D 7A
Load Register A from Register D, the new cylinder number.
5F52
CP 60H FE 60
Compare Register A against 60H, ninety-six, the highest cylinder the lock-out map covers.
5F54
If the NZ FLAG has been set there are cylinders left, so LOOP BACK to 5EEEH to consider the next one.
5F57
LD A,(5B45H) 3A 45 5B
Self-Modifying Code
Load Register A from 5B45H, the count of cylinders sitting in the buffer.
5F5A
OR A B7
OR Register A with itself to test the count for zero.
5F5B
If the Z FLAG has been set the buffer is empty and every cylinder has been dealt with, so JUMP to 603FH to finish the backup.

5F5EH - The Write Pass

The same cylinders are written out from the buffer, with the same merge repeated so that the pass knows which ones to skip. Only the directory cylinder is written with a deleted data address mark; everything else gets a normal one, which is the arrangement FORMAT/CMD lays down and which the file system depends on.

5F5E
LD A,(5B45H) 3A 45 5B
Self-Modifying Code
Load Register A from 5B45H, the number of cylinders now in the buffer, which is what the write pass and the verify pass both have to work through.
5F61
LD (5C2FH),A 32 2F 5C
Self-Modifying Code
Store it at 5C2FH, the operand of the load at 602FH, which is the verify pass's own copy of the count.
5F64
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, back at the base of the work space, ready to write the buffered cylinders out.
5F67
LD D,00H 16 00
Self-Modifying Code
Load Register D from the operand at 5F68H, the cylinder the read pass started at, planted there at 5EEBH. The write pass therefore covers exactly the same cylinders in the same order.
5F69
PUSH HL E5
Loop Start
PUSH the buffer base in Register Pair HL onto the stack while the Granule Allocation Table bytes for this cylinder are worked out.
5F6A
LD H,57H 26 57
Load Register H with 57H so that Register Pair HL addresses the source Granule Allocation Table copy at 5700H.
5F6C
LD L,D 6A
Load Register L with Register D, the cylinder number, so that Register Pair HL points at this cylinder's allocation byte in the source Granule Allocation Table.
5F6D
LD C,(HL) 4E
Load Register C with the byte at the address in Register Pair HL, the source allocation byte for this cylinder. A set bit is a granule that carries data.
5F6E
LD A,D 7A
Load Register A from Register D, the cylinder number.
5F6F
ADD A,60H C6 60
ADD 60H to Register A, the offset of the lock-out map inside a Granule Allocation Table sector.
5F71
LD L,A 6F
Load Register L with Register A, so that Register Pair HL now points at this cylinder's source lock-out byte.
5F72
LD A,(HL) 7E
Fetch the source lock-out byte from the address in Register Pair HL into Register A.
5F73
CPL 2F
ComPLement Register A, turning locked-out granules into a mask of usable ones.
5F74
AND C A1
AND Register A with Register C, the source allocation byte, leaving only the granules that both carry data and are usable.
5F75
LD H,58H 26 58
Load Register H with 58H so that Register Pair HL addresses the destination Granule Allocation Table copy at 5800H, still at the lock-out map offset.
5F77
LD C,(HL) 4E
Load Register C with the byte at the address in Register Pair HL, the destination lock-out byte for this cylinder.
5F78
OR C B1
OR Register A with Register C, folding the destination's locked-out granules into the value, so that the destination allocation byte will show them as unavailable as well as showing the copied data.
5F79
LD L,D 6A
Load Register L with Register D, the cylinder number, so that Register Pair HL points at this cylinder's allocation byte in the destination Granule Allocation Table.
5F7A
LD (HL),A 77
Store Register A at the address in Register Pair HL, writing the merged allocation byte into the destination Granule Allocation Table copy.
5F7B
CP C B9
Compare Register A against Register C, the destination lock-out byte. They are equal when the merge added nothing, which means this cylinder carries no data on the source and does not have to be copied at all.
5F7C
POP HL E1
POP the buffer base back into Register Pair HL.
5F7D
If the Z FLAG has been set this cylinder was never read, so JUMP to 5FD5H to step past it exactly as the read pass did.
5F80
GOSUB to 5394H to mount the destination disk, which on a single-drive backup is where the operator is asked to swap.
5F83
LD E,00H 1E 00
Load Register E with 00H, sector 0 of this cylinder.
5F85
LD A,D 7A
Load Register A from Register D, the cylinder number.
5F86
OR E B3
OR Register A with Register E, which leaves Register A holding the cylinder number and sets the flags on it.
5F87
PUSH HL E5
PUSH the buffer address in Register Pair HL onto the stack across the display calls.
5F88
LD A,D 7A
Load Register A from Register D, the cylinder number, for the formatter.
5F89
LD HL,5DB3H 21 B3 5D
Point Register Pair HL at 61B3H, the three-character cylinder-number field.
5F8C
GOSUB to the number formatter at 6148H to write the cylinder number into that field.
5F8F
PUSH DE D5
Self-Modifying Code
PUSH Register Pair DE, the cylinder and sector, onto the stack. The file-by-file engine writes the top of memory over this address at 630CH when it is the engine occupying it.
5F90
LD HL,5D89H 21 89 5D
Point Register Pair HL at 6189H, which begins with 1DH and reads Dumping cylinder.
5F93
GOSUB to the resident @DSPLY vector at 4467H to display it.
5F96
LD HL,5DB3H 21 B3 5D
Point Register Pair HL at 61B3H, the cylinder number.
5F99
GOSUB to the resident @DSPLY vector at 4467H to display it on the same line.
5F9C
POP DE D1
POP the cylinder and sector back into Register Pair DE.
5F9D
POP HL E1
POP the buffer address back into Register Pair HL.
5F9E
LD A,0FEH 3E FE
Loop Start
Load Register A with 0FEH, the mask that clears the bottom bit of a sector number.
5FA0
AND E A3
AND Register A with Register E, the sector number, giving zero only for sectors 0 and 1.
5FA1
OR D B2
OR Register A with Register D, the cylinder number, so that the whole expression is zero only for the two boot sectors on cylinder 0.
5FA2
LD A,D 7A
Load Register A from Register D, the cylinder number, without disturbing the flags the two lines above set.
5FA3
If the Z FLAG has been set this is one of the two boot sectors, so JUMP forward to 5FB9H without writing it. The destination keeps its own bootstrap, which is what the manual means by the boot sector remaining untouched.
5FA5
CP 00H FE 00
Self-Modifying Code
Compare Register A, the cylinder number, against the operand at 5FA6H, which was planted at 5E83H with the destination disk's directory cylinder.
5FA7
If the Z FLAG has been set this is the directory cylinder, so JUMP forward to 5FB1H to write it with a deleted data address mark.
5FA9
GOSUB to 5464H, the disk-operation stub for operation 0DH, to write this sector with a normal data address mark.
5FAC
If the NZ FLAG has been set the write failed, so JUMP to the disk-error exit at 52A0H.
5FAF
JUMP forward to 5FB9H to step to the next sector.
5FB1
GOSUB to 5468H, the disk-operation stub for operation 0EH, to write this sector with a deleted data address mark. Every sector of a directory cylinder carries one, which is what keeps ordinary file reads from wandering into the directory.
5FB4
LD A,12H 3E 12
Load Register A with 12H, LDOS error 18, ready in case the write failed.
5FB6
If the NZ FLAG has been set the write failed, so JUMP to the disk-error exit at 52A0H with that error number.
5FB9
INC H 24
INCrement Register H by 1, stepping the buffer pointer on by one page to the next sector image.
5FBA
INC E 1C
INCrement Register E by 1 to step to the next sector of this cylinder.
5FBB
LD A,E 7B
Load Register A from Register E, the new sector number.
5FBC
INC D 14
INCrement Register D by 1, the first half of a test that leaves Register D unchanged.
5FBD
DEC D 15
DECrement Register D by 1, restoring it and setting the flags on the cylinder number.
5FBE
If the NZ FLAG has been set this is not cylinder 0, so JUMP forward to 5FCAH.
5FC0
BIT 7,(IY+04H) FD CB 04 7E
Test bit 7 of the byte at IY+04H, the dual-density marker.
5FC4
If the Z FLAG has been set the disk is uniform, so JUMP forward to 5FCAH.
5FC6
CP 00H FE 00
Self-Modifying Code
Compare Register A against the operand at 5FC7H, the single-density sector count planted at 5EB7H, which is how many sectors cylinder 0 holds on a dual-density system disk.
5FC8
If the Z FLAG has been set cylinder 0 is finished, so JUMP forward to 5FCEH.
5FCA
CP 00H FE 00
Self-Modifying Code
Compare Register A against the operand at 5FCBH, the sectors per cylinder planted at 5EA6H.
5FCC
If the NZ FLAG has been set there are more sectors on this cylinder, so LOOP BACK to 5F9EH to write the next one.
5FCE
LD A,(5B45H) 3A 45 5B
Self-Modifying Code
Load Register A from 5B45H, the number of cylinders still to be written out of the buffer.
5FD1
DEC A 3D
DECrement Register A by 1, one cylinder fewer to write.
5FD2
LD (5B45H),A 32 45 5B
Store the count back at 5B45H.
5FD5
INC D 14
INCrement Register D by 1 to step to the next cylinder.
5FD6
LD A,(5B45H) 3A 45 5B
Self-Modifying Code
Load Register A from 5B45H, the remaining count, to decide whether the write pass has finished.
5FD9
OR A B7
OR Register A with itself to test it for zero.
5FDA
If the NZ FLAG has been set cylinders remain, so LOOP BACK to 5F69H to write the next one.

5FDCH - The Verify Pass

The third pass reads every sector back and checks its cyclic redundancy check without transferring anything, which is why it needs no buffer pointer. When it finishes the engine jumps back to the read pass to start the next group of cylinders.

5FDC
LD A,(5B68H) 3A 68 5B
Self-Modifying Code
Load Register A from 5B68H, the cylinder the group started at, planted at 5EEBH.
5FDF
LD D,A 57
Load Register D with Register A, so that the verify pass starts at the same cylinder the other two passes did.
5FE0
LD H,57H 26 57
Load Register H with 57H so that Register Pair HL addresses the source Granule Allocation Table copy at 5700H.
5FE2
LD L,D 6A
Load Register L with Register D, the cylinder number, so that Register Pair HL points at this cylinder's source allocation byte.
5FE3
LD C,(HL) 4E
Load Register C with the byte at the address in Register Pair HL, the source allocation byte for this cylinder.
5FE4
LD A,D 7A
Load Register A from Register D, the cylinder number.
5FE5
ADD A,60H C6 60
ADD 60H to Register A, the offset of the lock-out map inside a Granule Allocation Table sector.
5FE7
LD L,A 6F
Load Register L with Register A, so that Register Pair HL points at this cylinder's source lock-out byte.
5FE8
LD A,(HL) 7E
Fetch the source lock-out byte from the address in Register Pair HL into Register A.
5FE9
CPL 2F
ComPLement Register A, turning locked-out granules into a mask of usable ones.
5FEA
AND C A1
AND Register A with Register C, leaving only the granules that carry data and are usable.
5FEB
LD H,58H 26 58
Load Register H with 58H so that Register Pair HL addresses the destination Granule Allocation Table copy at 5800H.
5FED
LD C,(HL) 4E
Load Register C with the byte at the address in Register Pair HL, the destination lock-out byte for this cylinder.
5FEE
OR C B1
OR Register A with Register C, folding in the destination's locked-out granules.
5FEF
LD L,D 6A
Load Register L with Register D so that Register Pair HL points at this cylinder's destination allocation byte.
5FF0
LD (HL),A 77
Store Register A at the address in Register Pair HL, keeping the destination Granule Allocation Table copy up to date.
5FF1
CP C B9
Compare Register A against Register C, the destination lock-out byte. Equality means this cylinder carries no data on the source.
5FF2
If the Z FLAG has been set this cylinder was never written, so JUMP to 6034H to step past it.
5FF5
LD E,00H 1E 00
Load Register E with 00H, sector 0 of this cylinder.
5FF7
LD A,D 7A
Load Register A from Register D, the cylinder number, for the formatter.
5FF8
LD HL,5DB3H 21 B3 5D
Point Register Pair HL at 61B3H, the three-character cylinder-number field.
5FFB
GOSUB to the number formatter at 6148H to write the cylinder number into it.
5FFE
PUSH DE D5
PUSH Register Pair DE, the cylinder and sector, onto the stack across the display calls.
5FFF
LD HL,5D9EH 21 9E 5D
Point Register Pair HL at 619EH, which begins with 1DH and reads Verifying cylinder.
6002
GOSUB to the resident @DSPLY vector at 4467H to display it.
6005
LD HL,5DB3H 21 B3 5D
Point Register Pair HL at 61B3H, the cylinder number.
6008
GOSUB to the resident @DSPLY vector at 4467H to display it on the same line.
600B
POP DE D1
POP the cylinder and sector back into Register Pair DE.
600C
LD A,0FEH 3E FE
Loop Start
Load Register A with 0FEH, the boot-sector mask.
600E
AND E A3
AND Register A with Register E, the sector number.
600F
OR D B2
OR Register A with Register D, the cylinder number, so that the result is zero only for the two boot sectors.
6010
If the NZ FLAG has been set this is an ordinary sector, so GOSUB to 5470H, the disk-operation stub for operation 0AH, to verify it against what was written. A verify reads the sector and checks its cyclic redundancy check without transferring any data, which is why no buffer pointer is needed.
6013
If the Z FLAG has been set the verify was skipped, so JUMP forward to 601AH.
6015
CP 06H FE 06
Compare Register A against 06H, the deleted data address mark status, which is what a directory sector reports on a successful verify.
6017
If the NZ FLAG has been set the verify genuinely failed, so JUMP to the disk-error exit at 52A0H.
601A
INC E 1C
INCrement Register E by 1 to step to the next sector. Unlike the two passes above, the verify pass does not step a buffer pointer, because nothing is transferred.
601B
LD A,E 7B
Load Register A from Register E, the new sector number.
601C
INC D 14
INCrement Register D by 1, the first half of a test that leaves Register D unchanged.
601D
DEC D 15
DECrement Register D by 1, restoring it and setting the flags on the cylinder number.
601E
If the NZ FLAG has been set this is not cylinder 0, so JUMP forward to 602AH.
6020
BIT 7,(IY+04H) FD CB 04 7E
Test bit 7 of the byte at IY+04H, the dual-density marker.
6024
If the Z FLAG has been set the disk is uniform, so JUMP forward to 602AH.
6026
CP 00H FE 00
Self-Modifying Code
Compare Register A against the operand at 6027H, the single-density sector count planted at 5EBAH.
6028
If the Z FLAG has been set cylinder 0 is finished, so JUMP forward to 602EH.
602A
CP 00H FE 00
Self-Modifying Code
Compare Register A against the operand at 602BH, the sectors per cylinder planted at 5EA9H.
602C
If the NZ FLAG has been set there are more sectors on this cylinder, so LOOP BACK to 600CH to verify the next one.
602E
LD A,00H 3E 00
Self-Modifying Code
Load Register A from the operand at 602FH, the verify pass's own count of cylinders, planted at 5F61H.
6030
DEC A 3D
DECrement Register A by 1, one cylinder fewer to verify.
6031
LD (5C2FH),A 32 2F 5C
Self-Modifying Code
Store the count back at 5C2FH, which is the operand of the load above.
6034
INC D 14
INCrement Register D by 1 to step to the next cylinder.
6035
LD A,(5C2FH) 3A 2F 5C
Self-Modifying Code
Load Register A from 5C2FH, the remaining count.
6038
OR A B7
OR Register A with itself to test it for zero.
6039
If the NZ FLAG has been set cylinders remain to be verified, so LOOP BACK to 5FE0H.
603C
LOOP BACK to 5EE7H to begin the next group of cylinders. The read pass, the write pass and the verify pass together handle as many cylinders as the work space holds, and this jump starts the whole cycle again from wherever the last group stopped.

603FH - Finishing the Mirror Image

With every cylinder copied, the destination is given the source's identity and the current system date, its Granule Allocation Table is written and verified, the modification flags are cleared throughout both directories, the two boot sectors are patched to name the new directory cylinder, and the destination's old directory cylinder is erased back to the state FORMAT/CMD left it in if the new directory has landed elsewhere. Every one of these steps corresponds to a sentence in the manual.

603F
LD A,0DH 3E 0D
Load Register A with 0DH, a carriage return, to end the progress line now that every cylinder has been copied.
6041
GOSUB to the ROM character-output routine at 0033H to send it.
6044
LD HL,57CEH 21 CE 57
Point Register Pair HL at 57CEH, offset CEH of the source Granule Allocation Table, the start of the master-password hash.
6047
LD DE,58CEH 11 CE 58
Point Register Pair DE at 58CEH, the same offset in the destination Granule Allocation Table copy.
604A
LD BC,0032H 01 32 00
Load Register Pair BC with 0032H, fifty bytes, which covers the master-password hash, the eight-character disk name, the disk date and the auto-command field at offset E0H.
604D
LDIR ED B0
Block move the source disk's whole identity into the destination Granule Allocation Table copy. This is the manual's statement that after the backup the destination disk will have the same Pack ID as the source.
604F
LD HL,527DH 21 7D 52
Point Register Pair HL at 527DH, the system date the front end read through @DATE at 5B43H.
6052
LD DE,58D8H 11 D8 58
Point Register Pair DE at 58D8H, offset D8H of the destination Granule Allocation Table copy, which is the disk date field.
6055
LD BC,0008H 01 08 00
Load Register Pair BC with 0008H, the eight bytes of a disk date.
6058
LDIR ED B0
Block move the current system date over the date just copied from the source, which is the manual's statement that the destination disk date will be changed to the current system date.
605A
GOSUB to 5394H to mount the destination disk for the last time.
605D
LD A,(5BA6H) 3A A6 5B
Self-Modifying Code
Load Register A from 5BA6H, the destination directory cylinder planted at 5E83H.
6060
LD D,A 57
Load Register D with Register A, that cylinder, as the cylinder to write.
6061
LD E,00H 1E 00
Load Register E with 00H, sector 0 of it, which is the Granule Allocation Table.
6063
LD HL,5800H 21 00 58
Point Register Pair HL at 5800H, the destination Granule Allocation Table copy that the three passes have been keeping up to date.
6066
GOSUB to 5468H, the disk-operation stub for operation 0EH, to write it back with a deleted data address mark.
6069
LD A,15H 3E 15
Load Register A with 15H, LDOS error 21, ready in case the write failed.
606B
If the NZ FLAG has been set the write failed, so JUMP to 6078H, which passes the error to the disk-error exit.
606E
LD HL,5900H 21 00 59
Point Register Pair HL at 5900H, the general sector buffer, which the verify below does not actually transfer into but which has to be a valid address.
6071
GOSUB to 5470H, the disk-operation stub for operation 0AH, to verify the Granule Allocation Table sector that was just written.
6074
CP 06H FE 06
Compare Register A against 06H, the deleted data address mark status a directory sector reports on a good verify.
6076
LD A,14H 3E 14
Load Register A with 14H, LDOS error 20, ready in case it did not.
6078
If the NZ FLAG has been set the verify failed, so JUMP to the disk-error exit at 52A0H.
607B
LD A,(5BA6H) 3A A6 5B
Self-Modifying Code
Load Register A from 5BA6H, the destination directory cylinder.
607E
LD D,A 57
Load Register D with Register A, the cylinder to read.
607F
LD E,02H 1E 02
Loop Start
Load Register E with 02H. Sectors 0 and 1 of the directory cylinder are the Granule Allocation Table and the Hash Index Table, so the directory records themselves begin at sector 2.
6081
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, using the bottom page of the work space as the buffer for this sector.
6084
GOSUB to 546CH, the disk-operation stub for operation 09H, to read a directory sector.
6087
CP 06H FE 06
Compare Register A against 06H, the deleted data address mark status.
6089
If the NZ FLAG has been set the read failed, so JUMP to 529BH to exit with LDOS error 17.
608C
BIT 7,(HL) CB 7E
Loop Start
Test bit 7 of the byte at the address in Register Pair HL, offset 00H of a directory record, which marks a secondary extent entry rather than a primary one.
608E
If the NZ FLAG has been set this is a secondary entry, which carries no modification flag of its own, so JUMP forward to 6094H to step to the next record.
6090
INC L 2C
INCrement Register L by 1 so that Register Pair HL points at offset 01H of the record, the byte that carries the flags and the modification month.
6091
RES 6,(HL) CB B6
RESet bit 6 of the byte at the address in Register Pair HL, clearing the modification flag. That flag means the file has been changed since its last backup, and clearing it here on the copy is what makes a subsequent MOD backup meaningful.
6093
DEC L 2D
DECrement Register L by 1 to point back at the start of the record.
6094
LD A,L 7D
Load Register A from Register L, the offset of this record within the sector.
6095
ADD A,20H C6 20
ADD 20H to Register A, thirty-two, the size of a directory record. Eight records fit in a 256-byte sector.
6097
LD L,A 6F
Load Register L with Register A, the offset of the next record.
6098
If the NO CARRY FLAG has been set the addition stayed inside the page, so LOOP BACK to 608CH to deal with the next record. A carry means all eight records have been done.
609A
GOSUB to 5468H, the disk-operation stub for operation 0EH, to write the sector back with a deleted data address mark.
609D
LD A,12H 3E 12
Load Register A with 12H, LDOS error 18, ready in case the write failed.
609F
If the NZ FLAG has been set the write failed, so JUMP forward to 60D9H, which passes the error to the disk-error exit.
60A1
INC E 1C
INCrement Register E by 1 to step to the next directory sector.
60A2
LD A,E 7B
Load Register A from Register E, the new sector number.
60A3
CP 00H FE 00
Self-Modifying Code
Compare Register A against the operand at 60A4H, the sectors per cylinder planted at 5EAFH, which is how many sectors the directory cylinder holds.
60A5
If the NZ FLAG has been set there are more directory sectors, so LOOP BACK to 6081H.
60A7
LD HL,5900H 21 00 59
Point Register Pair HL at 5900H, the general sector buffer.
60AA
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, cylinder 0 and sector 0, the single-density boot sector.
60AD
GOSUB to 546CH, the disk-operation stub for operation 09H, to read the destination's own boot sector, which was never overwritten by the copy.
60B0
If the NZ FLAG has been set the read failed, so JUMP forward to 60D9H.
60B2
LD (HL),00H 36 00
Store 00H at the address in Register Pair HL, clearing the 76H marker that was planted at 5E75H. The backup is finished, so the disk no longer needs to be identifiable as the destination of one.
60B4
INC HL 23
INCrement Register Pair HL by 1, the first of two steps to offset 02H of the boot sector.
60B5
INC HL 23
INCrement Register Pair HL by 1 again, so that Register Pair HL points at offset 02H, the operand of the bootstrap's own comparison, which holds the directory cylinder.
60B6
LD B,(HL) 46
Load Register B with the byte at the address in Register Pair HL, the directory cylinder the destination disk had before this backup. It is needed below to free the space that old directory occupied.
60B7
LD A,(5BA6H) 3A A6 5B
Self-Modifying Code
Load Register A from 5BA6H, the source disk's directory cylinder.
60BA
LD (HL),A 77
Store Register A at the address in Register Pair HL, planting the new directory cylinder in the boot sector. This is the manual's statement that the destination disk will have its directory on the same track as the source disk even though this may not have been the case before.
60BB
DEC HL 2B
DECrement Register Pair HL by 1, stepping back towards the start of the sector.
60BC
DEC HL 2B
DECrement Register Pair HL by 1 again, so that Register Pair HL points at the start of the buffer for the write.
60BD
GOSUB to 5464H, the disk-operation stub for operation 0DH, to write the patched boot sector back.
60C0
If the Z FLAG has been set the write succeeded, so GOSUB to 5470H, the disk-operation stub for operation 0AH, to verify it.
60C3
If the NZ FLAG has been set either the write or the verify failed, so JUMP forward to 60D9H.
60C5
INC E 1C
INCrement Register E by 1 to step to sector 1, the double-density boot sector, which carries the same field and needs the same patch.
60C6
GOSUB to 546CH, the disk-operation stub for operation 09H, to read it.
60C9
If the NZ FLAG has been set the read failed, so JUMP forward to 60D9H.
60CB
LD A,(5BA6H) 3A A6 5B
Self-Modifying Code
Load Register A from 5BA6H, the directory cylinder, again.
60CE
INC HL 23
INCrement Register Pair HL by 1, the first of two steps to offset 02H.
60CF
INC HL 23
INCrement Register Pair HL by 1 again.
60D0
LD (HL),A 77
Store Register A at the address in Register Pair HL, planting the directory cylinder in the double-density boot sector as well.
60D1
DEC HL 2B
DECrement Register Pair HL by 1, stepping back towards the start of the buffer.
60D2
DEC HL 2B
DECrement Register Pair HL by 1 again.
60D3
GOSUB to 5464H, the disk-operation stub for operation 0DH, to write it back.
60D6
If the Z FLAG has been set the write succeeded, so GOSUB to 5470H to verify it.
60D9
If the NZ FLAG has been set any of the boot sector operations failed, so JUMP to the disk-error exit at 52A0H. Five separate failure paths converge on this one instruction.
60DC
LD H,58H 26 58
Load Register H with 58H so that Register Pair HL addresses the destination Granule Allocation Table copy.
60DE
LD A,B 78
Load Register A from Register B, the destination disk's old directory cylinder saved at 60B6H.
60DF
ADD A,60H C6 60
ADD 60H to Register A, the offset of the lock-out map.
60E1
LD L,A 6F
Load Register L with Register A, so that Register Pair HL points at the old directory cylinder's lock-out byte.
60E2
LD C,(HL) 4E
Load Register C with the byte at the address in Register Pair HL, that lock-out byte.
60E3
LD L,B 68
Load Register L with Register B, so that Register Pair HL points at the old directory cylinder's allocation byte.
60E4
LD A,(HL) 7E
Fetch that allocation byte into Register A.
60E5
OR C B1
OR Register A with Register C, the lock-out byte.
60E6
CP C B9
Compare Register A against Register C. They are equal when the old directory cylinder now holds nothing but locked-out granules, which means the new directory has landed somewhere else and the old one is free space.
60E7
If the NZ FLAG has been set the cylinder is still in use, so JUMP forward to 6106H and leave it alone.
60E9
LD A,B 78
Load Register A from Register B, the old directory cylinder, keeping it while Register B is reused as a counter.
60EA
LD HL,5900H 21 00 59
Point Register Pair HL at 5900H, the general sector buffer.
60ED
LD DE,5901H 11 01 59
Point Register Pair DE at 5901H, one byte above it, so that the block move below becomes a fill.
60F0
LD BC,00FFH 01 FF 00
Load Register Pair BC with 00FFH, 255 bytes, one less than a full sector.
60F3
LD (HL),0E5H 36 E5
Store 0E5H at the address in Register Pair HL. That is the byte a freshly formatted sector is filled with, so the old directory cylinder will be left looking exactly as FORMAT/CMD left it.
60F5
LDIR ED B0
Block move the single 0E5H byte up through the buffer, filling the whole sector image with it.
60F7
LD L,C 69
Load Register L with Register C, which the block move left at zero, so that Register Pair HL points at the start of the buffer.
60F8
LD D,A 57
Load Register D with Register A, the old directory cylinder, as the cylinder to write.
60F9
LD E,C 59
Load Register E with Register C, which is zero, so the fill starts at sector 0.
60FA
LD A,(5B41H) 3A 41 5B
Self-Modifying Code
Load Register A from 5B41H, the sectors per cylinder planted at 5EA3H.
60FD
LD B,A 47
Load Register B with Register A, that count, as the loop counter.
60FE
Loop Start
GOSUB to 5464H, the disk-operation stub for operation 0DH, to write one sector of 0E5H bytes over the old directory cylinder.
6101
If the NZ FLAG has been set the write failed, so JUMP back to 60D9H to report it.
6103
INC E 1C
INCrement Register E by 1 to step to the next sector.
6104
DECrement Register B by 1 and LOOP BACK to 60FEH while it is not zero, erasing the whole of the old directory cylinder.
6106
GOSUB to 545CH, the disk-operation stub for operation 04H, to restore the head to cylinder 0.
6109
GOSUB to 5327H to mount the source disk, which on a single-drive backup means one last prompt and Pack ID check.
610C
LD D,(IY+09H) FD 56 09
Load Register D with the byte at IY+09H, the source disk's directory cylinder.
610F
LD E,02H 1E 02
Loop Start
Load Register E with 02H, the first sector of directory records.
6111
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, using the bottom page of the work space as the buffer.
6114
GOSUB to 546CH, the disk-operation stub for operation 09H, to read a source directory sector.
6117
CP 06H FE 06
Compare Register A against 06H, the deleted data address mark status.
6119
If the NZ FLAG has been set the read failed, so JUMP to 529BH to exit with LDOS error 17.
611C
BIT 7,(HL) CB 7E
Loop Start
Test bit 7 of the byte at the address in Register Pair HL, offset 00H of a directory record, which marks a secondary extent entry.
611E
If the NZ FLAG has been set this is a secondary entry, so JUMP forward to 6124H.
6120
INC L 2C
INCrement Register L by 1 to point at offset 01H of the record.
6121
RES 6,(HL) CB B6
RESet bit 6 of the byte at the address in Register Pair HL, clearing the modification flag on the SOURCE disk. This is the manual's statement that one function of BACKUP is to reset a file's mod flag after it completes the backup.
6123
DEC L 2D
DECrement Register L by 1 to point back at the start of the record.
6124
LD A,L 7D
Load Register A from Register L, the offset of this record within the sector.
6125
ADD A,20H C6 20
ADD 20H to Register A, the size of a directory record.
6127
LD L,A 6F
Load Register L with Register A, the offset of the next record.
6128
If the NO CARRY FLAG has been set the addition stayed inside the page, so LOOP BACK to 611CH for the next record.
612A
GOSUB to 5468H, the disk-operation stub for operation 0EH, to write the source directory sector back with a deleted data address mark.
612D
If the Z FLAG has been set the write succeeded, so JUMP forward to 613FH to step to the next sector.
612F
CP 0FH FE 0F
Compare Register A against 0FH, LDOS error 15, which is what a write to a protected disk reports.
6131
LD A,12H 3E 12
Load Register A with 12H, LDOS error 18, ready in case it is some other failure.
6133
If the NZ FLAG has been set the write failed for some reason other than write protection, so JUMP to the disk-error exit at 52A0H.
6136
LD HL,55A1H 21 A1 55
Point Register Pair HL at the message at 55A1H, Source disk is write protected, can't clear mod flags.
6139
GOSUB to the resident @LOGOT vector at 447BH to display it. The manual is explicit that this does not indicate an error: the copy itself is complete and only the housekeeping on the source has been refused.
613C
JUMP to the normal ending at 5287H, which displays Backup complete and the real-time-clock note and exits through @EXIT. The remaining directory sectors are not attempted, because the manual says the message is not displayed after every file and no mod flags will be removed from any source file.
613F
INC E 1C
INCrement Register E by 1 to step to the next source directory sector.
6140
LD A,E 7B
Load Register A from Register E, the new sector number.
6141
CP 00H FE 00
Self-Modifying Code
Compare Register A against the operand at 6142H, the sectors per cylinder planted at 5EACH.
6143
If the NZ FLAG has been set there are more directory sectors to clear, so LOOP BACK to 6111H.
6145
JUMP to the normal ending at 5287H. The mirror image is complete, verified, re-identified and its modification flags cleared on both disks.

6148H - The Three Digit Number Formatter

A small routine that writes a value into three characters by repeated subtraction, presetting the hundreds and tens to blanks so that leading zeros are suppressed and the units to the character zero so that a value of zero still displays. It is what keeps the cylinder number on the progress line readable as it counts up.

6148
LD (HL),20H 36 20
Store 20H, a blank, at the address in Register Pair HL, the hundreds position of the three-character field. Presetting it to a blank is what suppresses a leading zero.
614A
LD B,64H 06 64
Load Register B with 64H, one hundred, as the divisor for the hundreds digit.
614C
GOSUB to 615AH, which counts how many times Register B goes into Register A, writes the digit if it is not zero and steps Register Pair HL on.
614F
LD (HL),20H 36 20
Store 20H, a blank, at the tens position, again so that a leading zero is suppressed.
6151
LD B,0AH 06 0A
Load Register B with 0AH, ten, as the divisor for the tens digit.
6153
GOSUB to 615AH to produce the tens digit.
6156
LD (HL),30H 36 30
Store 30H, the character zero, at the units position. The units digit is preset to a real character rather than a blank, so that a cylinder number of zero displays as 0 rather than as three blanks.
6158
LD B,01H 06 01
Load Register B with 01H, one, as the divisor for the units digit. Execution falls straight into the routine below.
615A
LD C,00H 0E 00
Load Register C with 00H, the digit being counted up.
615C
SUB B 90
Loop Start
SUBtract Register B, the current power of ten, from Register A.
615D
If the CARRY FLAG has been set the subtraction went below zero, so JUMP forward to 6162H to undo it.
615F
INC C 0C
INCrement Register C by 1, counting one more of this power of ten.
6160
LOOP BACK to 615CH to subtract again. Repeated subtraction is how the division is done.
6162
ADD A,B 80
ADD Register B back to Register A, undoing the subtraction that went below zero and leaving the remainder for the next digit.
6163
PUSH AF F5
PUSH Register A and the flags onto the stack, preserving the remainder while the digit is written.
6164
LD A,C 79
Load Register A from Register C, the digit just counted.
6165
OR A B7
OR Register A with itself to test the digit for zero.
6166
If the Z FLAG has been set the digit is zero, so JUMP forward to 616BH and leave the position holding whatever it was preset to, which is a blank for the hundreds and tens and the character zero for the units.
6168
ADD A,30H C6 30
ADD 30H to Register A, converting the binary digit into its ASCII character.
616A
LD (HL),A 77
Store the character at the address in Register Pair HL, overwriting the preset.
616B
POP AF F1
POP the remainder back into Register A for the next digit.
616C
INC HL 23
INCrement Register Pair HL by 1 to step to the next character position.
616D
RET C9
RETurn to the caller with the remainder in Register A and Register Pair HL pointing at the next position.

616EH - The Not Mirror Image Exit

Reached only from the lock-out map walk, before any data has been moved.

616E
LD HL,5DB7H 21 B7 5D
Point Register Pair HL at the message at 61B7H, Backup aborted, destination not mirror-image.
6171
JUMP to the abort entry at 52AFH with that message. This is reached only from the lock-out map walk at 5E43H, before any data has been moved.

6174H - The Mirror Image Engine Message Block

The last 149 bytes of the third load record are the engine's own text. They sit above the code rather than below it because the block move that brings the engine down to 5A00H carries them with it, so the engine reaches them at 5D74H onwards once it is running. The three progress lines all begin with 1DH, which is what lets the cylinder number be rewritten in place instead of the screen scrolling once per cylinder.

6174-6188
DEFM 1DH, "Loading cylinder ", 03H 1D 4C 6F 61 64 69 6E 67 20 20 20 63 79 6C 69 6E 64 65 72 20 03
The read pass's progress line, displayed by 5F13H. The leading 1DH returns the cursor to the start of the line so that the line is rewritten rather than scrolled, and the trailing 03H leaves the cursor where it is so that the cylinder number can be displayed immediately after it.
6189-619D
DEFM 1DH, "Dumping cylinder ", 03H 1D 44 75 6D 70 69 6E 67 20 20 20 63 79 6C 69 6E 64 65 72 20 03
The write pass's progress line, displayed by 5F90H. The three progress messages are padded to the same width so that the cylinder number always appears in the same screen column.
619E-61B2
DEFM 1DH, "Verifying cylinder ", 03H 1D 56 65 72 69 66 79 69 6E 67 20 63 79 6C 69 6E 64 65 72 20 03
The verify pass's progress line, displayed by 5FFFH.
61B3-61B6
DEFM "000", 03H 30 30 30 03
The three-character cylinder-number field the formatter at 6148H writes into, displayed straight after whichever progress line is current. It ships as three zeros, and the formatter blanks the hundreds and tens when they are not needed.
61B7-61E4
DEFM 0AH, "Backup aborted, destination not mirror-image", 0DH 0A 42 61 63 6B 75 70 20 61 62 6F 72 74 65 64 2C 20 64 65 73 74 69 6E 61 74 69 6F 6E 20 6E 6F 74 20 6D 69 72 72 6F 72 2D 69 6D 61 67 65 0D
Displayed by 616EH when the lock-out map walk finds a cylinder that carries data on the source and is locked out on the destination. The manual suggests using the FREE command to check the destination disk for locked out cylinders.
61E5-6208
DEFM "Different pack IDs! Abort backup ? ", 03H 44 69 66 66 65 72 65 6E 74 20 70 61 63 6B 20 49 44 73 21 20 41 62 6F 72 74 20 62 61 63 6B 75 70 20 3F 20 03
Displayed by 5E0CH when the ten-byte Pack IDs do not match. It is a question rather than a refusal, and it is terminated by 03H so that the reply is typed on the same line. Under a job it is displayed and then abandoned without waiting, because there is nobody to answer it.

Unloaded Storage
The image resumes at 6300H. The 247 bytes from 6209H to 62FFH are not written by any load record. The block move at 6C0FH copies 0500H bytes rather than the 0409H the engine actually occupies, so part of this gap is carried down with the engine and lands harmlessly above it.

6300H - File By File Engine, Setup and the System Disk Slots

The file-by-file engine serves both a backup by class and a backup reconstruct, which the manual says function identically. It is loaded here at 6300H and assembled to run at 5A00H, so every address in the mnemonic column of these sections is 0900H below the address in the leftmost column, and the links follow the operand to where the instruction is shown on this page. Only JP and CALL carry an absolute operand and therefore show a run-time address; JR and DJNZ are resolved from their displacements and already read as the addresses in the leftmost column. The engine begins by working out how much the destination disk holds, disabling a check that only a mirror image needs, and, when the SYS parameter was given, reserving the fifteen directory slots the system files must occupy.

6300
LD HL,(4049H) 2A 49 40
Load Register Pair HL from HIGH$ at 4049H, the highest address available to a transient program.
6303
INC HL 23
INCrement Register Pair HL by 1 and, with the next line, reduce the address to the number of the last whole page below it.
6304
DEC H 25
DECrement Register H by 1, leaving Register H as the top page of the work space.
6305
LD A,H 7C
Load Register A from Register H, that page number.
6306
LD (5E51H),A 32 51 5E
Self-Modifying Code
Store it at 5E51H, the operand of the load at 6751H, which is one of the three places the engine tests whether its file buffer has run to the top of memory.
6309
LD (5E66H),A 32 66 5E
Self-Modifying Code
Store it at 5E66H, the operand of the comparison at 6766H, the same test in the verify loop.
630C
LD (5F8FH),A 32 8F 5F
Self-Modifying Code
Store it at 5F8FH, the operand of the comparison at 688FH, the same test in the routine that verifies a whole buffer.
630F
LD A,0C9H 3E C9
Load Register A with 0C9H, the opcode of a RET.
6311
LD (53BDH),A 32 BD 53
Self-Modifying Code
Store it at 53BDH, over the conditional return inside the destination-verification routine, turning it into an unconditional one. A file-by-file backup never writes the 76H marker byte that a mirror image uses to identify its destination, so that test has to be disabled or every destination swap would be rejected.
6314
GOSUB to 5394H to mount the destination disk.
6317
LD A,(IY+07H) FD 7E 07
Load Register A with the byte at IY+07H, the allocation byte of the Drive Control Table entry, whose bits 4 to 0 are the highest sector number on a track and whose bits 7 to 5 are the head count less one.
631A
LD B,A 47
Load Register B with Register A, keeping the whole byte while the fields are separated.
631B
AND 1FH E6 1F
AND Register A with 1FH to keep only the highest sector number.
631D
LD C,A 4F
Load Register C with Register A, that number.
631E
INC C 0C
INCrement Register C by 1, giving the sectors per track.
631F
XOR B A8
Exclusive-OR Register A with Register B, cancelling the sector field and leaving the head count in bits 7 to 5.
6320
RLCA 07
Rotate Register A left circularly, the first of three shifts that bring the head count down.
6321
RLCA 07
Rotate Register A left circularly again.
6322
RLCA 07
Rotate Register A left circularly a third time, leaving the head count less one in Register A.
6323
INC A 3C
INCrement Register A by 1, giving the number of heads.
6324
LD B,A 47
Load Register B with Register A, the head count, as the loop counter.
6325
XOR A AF
Clear Register A to zero, the running total.
6326
ADD A,C 81
ADD Register C, the sectors per track, to Register A.
6327
DECrement Register B by 1 and LOOP BACK to 6326H while it is not zero, multiplying the sectors per track by the head count to give the sectors per cylinder.
6329
LD L,A 6F
Load Register L with Register A, the low half of the sectors-per-cylinder value.
632A
LD H,00H 26 00
Load Register H with 00H, the high half.
632C
If the NO CARRY FLAG has been set the repeated addition did not overflow eight bits, so JUMP forward to 632FH.
632E
INC H 24
INCrement Register H by 1 to carry into the high half.
632F
BIT 5,(IY+04H) FD CB 04 6E
Test bit 5 of the byte at IY+04H, the double-sided flag the geometry reader planted at 6D40H.
6333
If the Z FLAG has been set the disk is single sided, so JUMP forward to 6336H.
6335
ADD HL,HL 29
ADD Register Pair HL to itself, doubling the sectors per cylinder for a double-sided disk.
6336
LD A,(IY+06H) FD 7E 06
Load Register A with the byte at IY+06H, the highest cylinder number on the destination disk.
6339
DEC A 3D
DECrement Register A by 1. Cylinder 0 and the directory cylinder are not available for files, so the count of usable cylinders is one less than the range suggests.
633A
GOSUB to the resident @MULT vector at 44C1H, which multiplies the sixteen-bit value in Register Pair HL by the eight-bit value in Register A, returning the high sixteen bits in Register Pair HL and the low eight in Register A.
633D
LD H,L 65
Load Register H with Register L, the first half of assembling the low sixteen bits of the product.
633E
LD L,A 6F
Load Register L with Register A, completing it. Register Pair HL now holds the total number of sectors the destination disk can hold.
633F
LD (5DF4H),HL 22 F4 5D
Self-Modifying Code
Store it at 5DF4H, the operand of the load at 66F4H, which is what a file's length is compared against to decide whether it will fit on the destination at all.
6342
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, cylinder 0 and sector 0.
6345
LD HL,5800H 21 00 58
Point Register Pair HL at 5800H, the destination Granule Allocation Table buffer, used here for the boot sector.
6348
GOSUB to 546CH, the disk-operation stub for operation 09H, to read the destination boot sector.
634B
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H.
634E
LD A,(5802H) 3A 02 58
Load Register A from 5802H, offset 02H of the boot sector, which holds the destination disk's directory cylinder.
6351
LD (IY+09H),A FD 77 09
Store Register A as the byte at IY+09H, the directory cylinder field of the destination drive's Drive Control Table entry.
6354
LD A,(521BH) 3A 1B 52
Load Register A from 521BH, the low byte of the SYS parameter result cell.
6357
OR A B7
OR Register A with itself to test whether system files are to be included.
6358
If the Z FLAG has been set the SYS parameter was not given, so JUMP to 63D2H. The destination stays whatever kind of disk it already was.
635B
LD HL,6200H 21 00 62
Point Register Pair HL at 6200H, a page of the unloaded gap used here as the Hash Index Table buffer.
635E
LD E,02H 1E 02
Load Register E with 02H, which with Register D already holding the directory cylinder addresses sector 2 of it.
6360
GOSUB to 546CH, the disk-operation stub for operation 09H, to read the first directory sector.
6363
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H.
6366
LD A,(62C0H) 3A C0 62
Load Register A from 62C0H, the byte at offset C0H of that sector.
6369
INC A 3C
INCrement Register A by 1, so that the Z FLAG is set when the byte was 0FFH, the value that marks the system directory slots as already reserved.
636A
LD D,(IY+09H) FD 56 09
Load Register D with the byte at IY+09H, the directory cylinder, ready for the read below.
636D
If the Z FLAG has been set the slots are already reserved and this is already a SYSTEM disk, so JUMP to 6384H to carry on without touching them.
6370
LD E,L 5D
Load Register E with Register L, which is 00H, sector 0 of the directory cylinder, the Granule Allocation Table.
6371
GOSUB to 546CH, the disk-operation stub for operation 09H, to read it into the buffer at 6200H.
6374
CP 06H FE 06
Compare Register A against 06H, the deleted data address mark status a directory sector reports.
6376
LD A,14H 3E 14
Load Register A with 14H, LDOS error 20, ready in case the read failed.
6378
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H.
637B
LD L,0CDH 2E CD
Load Register L with 0CDH so that Register Pair HL points at 62CDH, the high byte of the Granule Allocation Table configuration word.
637D
BIT 7,(HL) CB 7E
Test bit 7 of the byte at the address in Register Pair HL, which FORMAT/CMD always sets and which marks the pack as exempt from the system-disk gate.
637F
RES 7,(HL) CB BE
RESet bit 7 of the byte at the address in Register Pair HL, clearing that exemption now that the disk is being made into a real SYSTEM disk.
6381
If the NZ FLAG has been set the bit was set and the sector has been changed, so GOSUB to 6874H to write the Granule Allocation Table back and verify it.
6384
LD A,(5215H) 3A 15 52
Load Register A from 5215H, the low byte of the OLD parameter result cell.
6387
OR A B7
OR Register A with itself to test it.
6388
If the NZ FLAG has been set only files already on the destination are to be copied, so no new system slots can be needed. JUMP forward to 63D2H.
638A
GOSUB to 68F1H, which reads sector 1 of the directory cylinder, the Hash Index Table, into the buffer at 6200H.
638D
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H.
6390
LD DE,6138H 11 38 61
Point Register Pair DE at 6A38H, a fifteen-byte table of the hash bytes the /SYS files occupy in the Hash Index Table.
6393
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, so that Register Pair HL addresses the table and Register Pair DE the Hash Index Table image.
6394
LD B,0FH 06 0F
Load Register B with 0FH, fifteen, the number of directory slots a SYSTEM disk reserves in total. The first two are BOOT/SYS and DIR/SYS, which FORMAT/CMD already created, and the remaining thirteen are SYS0/SYS through SYS12/SYS.
6396
LD A,(DE) 1A
Loop Start
Fetch the current Hash Index Table byte from the address in Register Pair DE into Register A.
6397
OR A B7
OR Register A with itself to test whether the slot is free. A zero byte in the Hash Index Table means a free directory record.
6398
If the NZ FLAG has been set the slot is occupied, so JUMP forward to 639CH to check that it is occupied by the right file.
639A
LD A,(HL) 7E
Fetch the expected hash byte from the table at the address in Register Pair HL into Register A.
639B
LD (DE),A 12
Store it at the address in Register Pair DE, claiming the free slot for that system file.
639C
CP (HL) BE
Compare Register A against the byte at the address in Register Pair HL, the hash the slot ought to carry.
639D
If the Z FLAG has been set the slot holds the right file or has just been claimed, so JUMP forward to 63A5H.
639F
LD HL,609EH 21 9E 60
Point Register Pair HL at the message at 699EH, Can't create SYSTEM disk - directory slots in use!. A slot the system files need is occupied by something else, and moving that file is not something BACKUP will do.
63A2
JUMP to the abort entry at 52AFH with that message.
63A5
INC E 1C
INCrement Register E by 1 to step to the next byte of the Hash Index Table image.
63A6
INC HL 23
INCrement Register Pair HL by 1 to step to the next expected hash byte.
63A7
LD A,08H 3E 08
Load Register A with 08H, the number of directory records in one directory sector.
63A9
CP E BB
Compare Register A against Register E, the offset reached in the Hash Index Table image.
63AA
If the NZ FLAG has been set the eighth slot has not been reached, so JUMP forward to 63AEH.
63AC
LD E,20H 1E 20
Load Register E with 20H, thirty-two. The Hash Index Table is ordered so that directory record n is slot 0 of sector 2+n for the first thirty-two records, which means the slots the system files need are not contiguous and the scan has to jump.
63AE
DECrement Register B by 1 and LOOP BACK to 6396H while it is not zero, checking all fifteen system slots.
63B0
LD D,(IY+09H) FD 56 09
Load Register D with the byte at IY+09H, the directory cylinder.
63B3
LD E,01H 1E 01
Load Register E with 01H, sector 1, which is the Hash Index Table.
63B5
LD HL,6200H 21 00 62
Point Register Pair HL at 6200H, the modified Hash Index Table image.
63B8
GOSUB to 5468H, the disk-operation stub for operation 0EH, to write it back with a deleted data address mark.
63BB
LD A,17H 3E 17
Load Register A with 17H, LDOS error 23, ready in case the write failed.
63BD
If the Z FLAG has been set the write succeeded, so GOSUB to 68F1H to read the sector back and prove it.
63C0
If the NZ FLAG has been set either the write or the read-back failed, so JUMP to the disk-error exit at 52A0H.
63C3
LD DE,0002H 11 02 00
Load Register Pair DE with 0002H, sector 2 of the directory cylinder, which is the first sector of directory records.
63C6
GOSUB to 546CH, the disk-operation stub for operation 09H, to read it.
63C9
LD L,0C0H 2E C0
Load Register L with 0C0H so that Register Pair HL points at 62C0H, the byte the check at 6366H reads.
63CB
LD (HL),0FFH 36 FF
Store 0FFH at the address in Register Pair HL, marking the disk as one whose system slots have been reserved, so that a later backup does not try to reserve them again.
63CD
LD L,00H 2E 00
Load Register L with 00H so that Register Pair HL points back at the start of the buffer for the write.
63CF
GOSUB to 5464H, the disk-operation stub for operation 0DH, to write the sector back.
63D2
GOSUB to 5327H to mount the source disk, which is the disk the directory scan is about to walk.
63D5
GOSUB to 68F1H to read the source disk's Hash Index Table into the buffer at 6200H. The scan works from the Hash Index Table rather than reading every directory sector, because a zero byte there means a free slot and can be skipped without any disk access at all.
63D8
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H.
63DB
LD HL,6200H 21 00 62
Point Register Pair HL at 6200H, the Hash Index Table image, ready for the scan.
63DE
JUMP forward to 6403H to enter the scan loop, stepping over the ten bytes of data that follow.
63E0-63E9
DEFM ".RS0LT0FF:" 2E 52 53 30 4C 54 30 46 46 3A
The Master Override Password
Ten bytes of data, jumped over by the instruction above and reached only by the block move at 681FH, which appends them to every file specification the engine builds. In an LDOS file specification a full stop introduces the PASSWORD field and a colon introduces the drivespec, so every file is opened as NAME/EXT.RS0LT0FF:d. SYS2/SYS hashes the password it is given and compares the result against the constant 113DH at its own 4FDAH, and on a match grants access without consulting the directory password word at all. Running these eight characters through that hash routine yields 113DH exactly, and the same routine reproduces the two known constants, 4296H for eight blanks and 42E0H for PASSWORD, so the identification is certain. This is the mechanism behind the manual's statement that the BACKUP utility will ignore any password protection on a file, whether doing a backup by class or a mirror image backup. The plaintext is the name of the MISOSYS author, Roy Soltoff, with each letter O written as a zero, and spelling it with real letters gives 0C6F7H instead.

Data, Not Instructions
The ten bytes at 63E0H are data and the two bytes at 63EAH and 63EBH that follow them are the two abandon-this-file exits, which the selection tests reach from a dozen places. A disassembler reading straight through this range resynchronises three bytes late, so the addresses either side of it have to be taken from the code that jumps into them rather than from the listing.

63EAH - The Directory Scan and the Selection Tests

The scan walks the Hash Index Table rather than reading every directory sector, because a zero byte there means a free record and can be rejected without touching the disk. For each occupied slot the directory record is read and put through the tests in the order the manual describes them: allocated and primary, then the SYS and INV classes, then the partspec with its wildcard and its optional negation, then the date range. Only a file that survives all of them is worth building a file specification for.

63EA
POP HL E1
POP Register Pair HL off the stack. This address and the one below it are the two abandon-this-file exits, reached from a dozen places in the selection tests; each pops back the level of nesting it needs and rejoins the scan.
63EB
POP BC C1
POP Register Pair BC off the stack, recovering the Hash Index Table offset of the file just rejected.
63EC
GOSUB to the ROM keyboard scan at 002BH, which returns the character of any key currently down.
63EF
CP 01H FE 01
Compare Register A against 01H, the code the ROM returns for BREAK.
63F1
If the Z FLAG has been set the operator pressed BREAK, so JUMP to the abort exit at 52ACH. The keyboard is tested once per file, which is what makes a long backup interruptible without slowing the copy down.
63F4
LD H,62H 26 62
Load Register H with 62H so that Register Pair HL addresses the Hash Index Table image at 6200H.
63F6
LD L,B 68
Load Register L with Register B, the offset within it that the scan has reached.
63F7
LD A,L 7D
Loop Start
Load Register A from Register L, the current Hash Index Table offset.
63F8
ADD A,20H C6 20
ADD 20H to Register A, thirty-two. Directory record n is slot 0 of sector 2 plus n, so consecutive directory records are thirty-two bytes apart in the Hash Index Table and the scan steps by that much.
63FA
LD L,A 6F
Load Register L with Register A, the next offset.
63FB
If the NO CARRY FLAG has been set the step stayed inside the table, so JUMP forward to 6403H to test the slot.
63FD
INC L 2C
INCrement Register L by 1. The step wrapped, so the scan moves on to the next column of the table, which is the next directory sector.
63FE
BIT 5,L CB 6D
Test bit 5 of Register L. The table is thirty-two columns of eight, so reaching column thirty-two means every slot has been examined.
6400
If the NZ FLAG has been set the whole table has been walked, so JUMP to the normal ending at 5287H. The backup is complete.
6403
LD A,(HL) 7E
Fetch the Hash Index Table byte at the address in Register Pair HL into Register A.
6404
OR A B7
OR Register A with itself to test it for zero. A zero byte means a free directory record, which is the cheap rejection the whole scan is built around.
6405
If the Z FLAG has been set the slot is free, so LOOP BACK to 63F7H to step to the next one without touching the disk.
6407
LD A,L 7D
Load Register A from Register L, the offset of this occupied slot.
6408
AND 0FEH E6 FE
AND Register A with 0FEH, clearing the bottom bit. Offsets 0 and 1 are directory records 0 and 1, which are BOOT/SYS and DIR/SYS.
640A
If the Z FLAG has been set this is one of those two records, so LOOP BACK to 63F7H to skip it. The manual is explicit that BOOT/SYS and DIR/SYS are never copied.
640C
LD B,L 45
Load Register B with Register L, the Hash Index Table offset of the file to be considered, which encodes both its directory sector and its record number.
640D
PUSH BC C5
PUSH Register Pair BC onto the stack, so that the offset survives everything the selection tests do.
640E
GOSUB to 5327H to mount the source disk, which on a swap prompts and re-checks the Pack ID.
6411
LD D,(IY+09H) FD 56 09
Load Register D with the byte at IY+09H, the source directory cylinder.
6414
LD A,B 78
Load Register A from Register B, the Hash Index Table offset.
6415
AND 1FH E6 1F
AND Register A with 1FH to keep the low five bits, which are the directory sector index.
6417
ADD A,02H C6 02
ADD 02H to Register A, because directory records begin at sector 2 of the directory cylinder.
6419
LD E,A 5F
Load Register E with Register A, the sector to read.
641A
LD HL,5800H 21 00 58
Point Register Pair HL at 5800H, the buffer the directory sector is read into.
641D
GOSUB to 546CH, the disk-operation stub for operation 09H, to read that directory sector.
6420
CP 06H FE 06
Compare Register A against 06H, the deleted data address mark status.
6422
If the NZ FLAG has been set the read failed, so JUMP to 529BH to exit with LDOS error 17.
6425
LD A,B 78
Load Register A from Register B, the Hash Index Table offset again.
6426
AND 0E0H E6 E0
AND Register A with 0E0H to keep the top three bits, which are the record number within the sector. Eight thirty-two-byte records fit in a sector, so those three bits multiplied by thirty-two are exactly the record's offset.
6428
LD L,A 6F
Load Register L with Register A, so that Register Pair HL points at the start of this file's directory record.
6429
LD H,58H 26 58
Load Register H with 58H, completing the pointer into the buffer at 5800H.
642B
LD A,(HL) 7E
Fetch the record's attribute byte, at directory offset 00H, into Register A.
642C
LD (5D13H),A 32 13 5D
Self-Modifying Code
Store it at 5D13H, which after the block move is an operand inside the date parser. The parser has already run and will not run again, so its instruction stream is being reused as a scratch cell.
642F
BIT 4,A CB 67
Test bit 4 of Register A, which marks an allocated record.
6431
If the Z FLAG has been set the record is not allocated, so JUMP to 63EBH to abandon this file and go on to the next.
6434
BIT 7,A CB 7F
Test bit 7 of Register A, which marks a secondary extent entry rather than a primary one.
6436
If the NZ FLAG has been set this is a secondary entry, which is not a file in its own right, so JUMP to 63EBH to skip it.
6439
LD A,(5217H) 3A 17 52
Load Register A from 5217H, the low byte of the MOD parameter result cell.
643C
OR A B7
OR Register A with itself to test whether only modified files are wanted.
643D
If the Z FLAG has been set the MOD parameter was not given, so JUMP forward to 6446H.
643F
INC L 2C
INCrement Register L by 1 so that Register Pair HL points at directory offset 01H, the byte carrying the flags and the modification month.
6440
BIT 6,(HL) CB 76
Test bit 6 of the byte at the address in Register Pair HL, the modification flag, which is set when the file has been written to since its last backup.
6442
If the Z FLAG has been set the file has not been modified, so JUMP to 63EBH to skip it.
6445
DEC L 2D
DECrement Register L by 1 to point back at the attribute byte.
6446
LD A,(5210H) 3A 10 52
Load Register A from 5210H, the attribute selection byte the front end assembled at 5AF6H, whose bit 6 is set by SYS and whose bit 3 is set by INV.
6449
BIT 6,(HL) CB 76
Test bit 6 of the byte at the address in Register Pair HL, the record's own system-file bit.
644B
If the Z FLAG has been set this is not a system file, so JUMP forward to 6451H to apply the invisible test instead.
644D
BIT 6,A CB 77
Test bit 6 of Register A, the SYS parameter. A system file is copied only when SYS was given.
644F
JUMP forward to 6457H with the result of that test.
6451
BIT 3,(HL) CB 5E
Test bit 3 of the byte at the address in Register Pair HL, the record's own invisible bit.
6453
If the Z FLAG has been set the file is an ordinary visible one, so JUMP forward to 645CH and copy it.
6455
BIT 3,A CB 5F
Test bit 3 of Register A, the INV parameter. An invisible file is copied only when INV was given.
6457
If the NZ FLAG has been set the matching parameter was given, so JUMP forward to 645CH to go on with this file.
6459
JUMP to 63EBH to skip this file. It is a system or invisible file and the operator did not ask for that class.
645C
LD A,(5205H) 3A 05 52
Load Register A from 5205H, the first character of the filename mask.
645F
CP 20H FE 20
Compare Register A against 20H, a blank, which is what the mask holds when no name was given on the command line.
6461
If the NZ FLAG has been set a name was given, so JUMP forward to 646AH to match it.
6463
LD A,(520DH) 3A 0D 52
Load Register A from 520DH, the first character of the extension mask.
6466
CP 20H FE 20
Compare Register A against 20H, a blank.
6468
If the Z FLAG has been set neither a name nor an extension was given, so there is nothing to match against and JUMP forward to 6496H with the file accepted.
646A
PUSH HL E5
PUSH Register Pair HL onto the stack, the pointer to the start of the directory record.
646B
LD A,L 7D
Load Register A from Register L, the record's offset within the sector.
646C
ADD A,05H C6 05
ADD 05H to Register A, the offset of the name field inside a directory record.
646E
LD L,A 6F
Load Register L with Register A, so that Register Pair HL points at the record's eight-character name.
646F
LD DE,5205H 11 05 52
Point Register Pair DE at 5205H, the filename mask, which is followed immediately by the extension mask so that one loop can compare both.
6472
LD B,0BH 06 0B
Load Register B with 0BH, eleven, the eight characters of a name plus the three of an extension.
6474
LD A,(DE) 1A
Loop Start
Fetch a character of the mask from the address in Register Pair DE into Register A.
6475
CP 24H FE 24
Compare Register A against 24H, a dollar sign, the LDOS wildcard character.
6477
If the Z FLAG has been set this position matches anything, so JUMP forward to 6481H to advance.
6479
CP (HL) BE
Compare Register A against the byte at the address in Register Pair HL, the corresponding character of the record's name.
647A
If the Z FLAG has been set the characters match, so JUMP forward to 6481H to advance.
647C
CP 20H FE 20
Compare Register A against 20H, a blank. A blank in the mask means the operator gave a shorter name, and the manual's examples show that a shorter partspec matches everything with that prefix.
647E
If the NZ FLAG has been set the mask has a real character here that does not match, so JUMP to 648EH, the mismatch path.
6481
INC HL 23
INCrement Register Pair HL by 1 to step to the next character of the record's name.
6482
INC DE 13
INCrement Register Pair DE by 1 to step to the next character of the mask.
6483
DECrement Register B by 1 and LOOP BACK to 6474H while it is not zero, comparing all eleven positions.
6485
LD A,(5211H) 3A 11 52
Load Register A from 5211H, the negated-partspec flag, which holds the minus sign itself when the command line began with one.
6488
OR A B7
OR Register A with itself to test it.
6489
If the NZ FLAG has been set the partspec was negated, so this file matches and must therefore be SKIPPED. JUMP to 63EAH.
648C
JUMP forward to 6495H to accept the file, which is what a match means when the partspec was not negated.
648E
LD A,(5211H) 3A 11 52
Load Register A from 5211H, the negated-partspec flag. This is the mismatch path.
6491
OR A B7
OR Register A with itself to test it.
6492
If the Z FLAG has been set the partspec was not negated, so a mismatch means the file is not wanted and JUMP to 63EAH to skip it. When the partspec was negated a mismatch is exactly what is wanted and execution falls through.
6495
POP HL E1
POP the directory record pointer back into Register Pair HL.
6496
PUSH HL E5
PUSH the directory record pointer onto the stack again, because the date test below advances it.
6497
INC HL 23
INCrement Register Pair HL by 1 so that Register Pair HL points at directory offset 01H, the first of the two packed date bytes.
6498
GOSUB to 684AH, which unpacks the record's date into Register Pair DE in the same two-byte form the DATE parameter was parsed into, taking the year from the extended field when the pack is in the x.3 format and from the three low bits of offset 02H when it is not.
649B
LD A,(5200H) 3A 00 52
Load Register A from 5200H, the date-selection mode byte.
649E
RLCA 07
Rotate Register A left circularly, moving bit 7, the lower-bound marker, into the CARRY FLAG.
649F
If the NO CARRY FLAG has been set no lower bound was given, so JUMP forward to 64AFH to test the upper one.
64A1
LD A,D 7A
Load Register A from Register D, the high byte of the file's packed date.
64A2
OR E B3
OR Register A with Register E, the low byte, so that the whole date is tested for zero.
64A3
If the Z FLAG has been set the file carries no date at all, so it cannot satisfy a lower bound. JUMP to 63EAH to skip it.
64A6
LD HL,(5201H) 2A 01 52
Load Register Pair HL from 5201H, the lower date bound.
64A9
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the bound in Register Pair DE and the file's date in Register Pair HL, which is the order the comparison below wants.
64AA
RST 18 DF
Restart at 0018H, the ROM sixteen-bit compare, which sets the CARRY FLAG when Register Pair HL is below Register Pair DE.
64AB
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL again, putting the file's date back where the upper-bound test expects it.
64AC
If the CARRY FLAG has been set the file is older than the lower bound, so JUMP to 63EAH to skip it.
64AF
LD A,(5200H) 3A 00 52
Load Register A from 5200H, the date-selection mode byte, again.
64B2
RRCA 0F
Rotate Register A right circularly, moving bit 0, the upper-bound marker, into the CARRY FLAG.
64B3
If the NO CARRY FLAG has been set no upper bound was given, so JUMP forward to 64C1H with the file accepted.
64B5
LD A,D 7A
Load Register A from Register D, the high byte of the file's packed date.
64B6
OR E B3
OR Register A with Register E, the low byte.
64B7
If the Z FLAG has been set the file carries no date, so JUMP to 63EAH to skip it.
64BA
LD HL,(5203H) 2A 03 52
Load Register Pair HL from 5203H, the upper date bound.
64BD
RST 18 DF
Restart at 0018H, the ROM sixteen-bit compare, this time with the bound in Register Pair HL and the file's date in Register Pair DE, so that the CARRY FLAG is set when the bound is below the date.
64BE
If the CARRY FLAG has been set the file is newer than the upper bound, so JUMP to 63EAH to skip it. Both bounds are inclusive, which is what the manual promises.
64C1
POP HL E1
POP the directory record pointer back into Register Pair HL.
64C2
LD A,L 7D
Load Register A from Register L, the record's offset within the sector.
64C3
AND 0E0H E6 E0
AND Register A with 0E0H to round it back to the start of the record, undoing anything the tests above left in the low bits.
64C5
LD L,A 6F
Load Register L with Register A, the corrected pointer.
64C6
PUSH HL E5
PUSH the directory record pointer onto the stack while the filespec is built.
64C7
ADD A,05H C6 05
ADD 05H to Register A, the offset of the name field.
64C9
LD L,A 6F
Load Register L with Register A, so that Register Pair HL points at the record's name.
64CA
LD DE,521DH 11 1D 52
Point Register Pair DE at 521DH, the filespec build area, as the destination.
64CD
LD B,08H 06 08
Load Register B with 08H, the eight characters of a file name.
64CF
LD A,(HL) 7E
Loop Start
Fetch a character of the name from the address in Register Pair HL into Register A.
64D0
CP 20H FE 20
Compare Register A against 20H, a blank, which pads a short name.
64D2
If the Z FLAG has been set the name has ended, so JUMP forward to 64D9H.
64D4
LD (DE),A 12
Store the character at the address in Register Pair DE, into the filespec being built.
64D5
INC HL 23
INCrement Register Pair HL by 1 to step to the next character of the name.
64D6
INC DE 13
INCrement Register Pair DE by 1 to step to the next position of the filespec.
64D7
DECrement Register B by 1 and LOOP BACK to 64CFH while it is not zero.
64D9
LD A,L 7D
Load Register A from Register L, the position reached in the record.
64DA
ADD A,B 80
ADD Register B to Register A, the count of name characters not used, so that Register A addresses the extension field whether the name was eight characters or fewer.
64DB
LD L,A 6F
Load Register L with Register A, so that Register Pair HL points at the record's three-character extension.
64DC
LD A,(HL) 7E
Fetch the first character of the extension into Register A.
64DD
CP 20H FE 20
Compare Register A against 20H, a blank, which is what an extensionless file carries.
64DF
If the Z FLAG has been set there is no extension, so JUMP forward to 64F1H to terminate the filespec.
64E1
LD A,2FH 3E 2F
Load Register A with 2FH, a slash, which separates a name from its extension in an LDOS filespec.
64E3
LD (DE),A 12
Store it at the address in Register Pair DE.
64E4
INC DE 13
INCrement Register Pair DE by 1 to step past it.
64E5
LD B,03H 06 03
Load Register B with 03H, the three characters of an extension.
64E7
LD A,(HL) 7E
Loop Start
Fetch a character of the extension into Register A.
64E8
CP 20H FE 20
Compare Register A against 20H, a blank.
64EA
If the Z FLAG has been set the extension has ended, so JUMP forward to 64F1H.
64EC
LD (DE),A 12
Store the character at the address in Register Pair DE.
64ED
INC HL 23
INCrement Register Pair HL by 1 to step to the next character of the extension.
64EE
INC DE 13
INCrement Register Pair DE by 1 to step to the next position of the filespec.
64EF
DECrement Register B by 1 and LOOP BACK to 64E7H while it is not zero.
64F1
LD A,03H 3E 03
Load Register A with 03H, the terminator that leaves the cursor on the line when a string is displayed.
64F3
LD (DE),A 12
Store it at the address in Register Pair DE, ending the filespec.

64F4H - The NEW and OLD Tests and the Query Prompt

NEW and OLD are decided by trying to open the file on the destination and looking at whether the error is file-not-found, which means the file control block has to be built and then rebuilt afterwards because the open destroys the specification it was built from. The query line is then assembled in place: the specification, a plus sign when the modification flag is set, and the date as DD-Mmm-YY, with the year taken from the extended field when the pack is in the x.3 format and from the three low bits of the old date byte when it is not.

64F4
PUSH DE D5
PUSH Register Pair DE, which points at the terminator, onto the stack. The routine that appends the password and the drive digit overwrites that terminator, so its position has to be kept.
64F5
LD A,(5215H) 3A 15 52
Load Register A from 5215H, the low byte of the OLD parameter result cell.
64F8
LD HL,5213H 21 13 52
Point Register Pair HL at 5213H, the NEW parameter result cell.
64FB
OR (HL) B6
OR Register A with the byte at the address in Register Pair HL, so that Register A is non-zero when either NEW or OLD was given.
64FC
If the Z FLAG has been set neither was given, so JUMP forward to 653EH and copy the file without asking whether it already exists.
64FE
LD HL,521DH 21 1D 52
Point Register Pair HL at 521DH, the filespec just built.
6501
LD DE,525DH 11 5D 52
Point Register Pair DE at 525DH, a scratch area, as the destination.
6504
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H, thirty-two bytes.
6507
LDIR ED B0
Block move the filespec into the scratch area, because the test below runs @INIT, which builds a file control block over the filespec and would otherwise destroy it.
6509
POP DE D1
POP the terminator position back into Register Pair DE.
650A
PUSH DE D5
PUSH it again, because the two calls below both advance it.
650B
GOSUB to 6819H, which appends the ten bytes at 63E0H, a full stop followed by the eight characters RS0LT0FF and a colon, then the source drive digit and a terminator. The full stop introduces the password field of an LDOS filespec, and this password is the one SYS2 accepts as an override, which is how BACKUP reads files whatever protection they carry.
650E
GOSUB to 68E8H, which mounts the destination disk and points Register Pair DE at the destination file control block at 523DH.
6511
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, the buffer @INIT is to use.
6514
LD DE,523DH 11 3D 52
Point Register Pair DE at 523DH, the destination file control block.
6517
GOSUB to the resident @OPEN vector at 4424H, which opens the file named in the file control block if it exists.
651A
POP DE D1
POP the terminator position back into Register Pair DE.
651B
If the Z FLAG has been set the file opened, so it already exists on the destination. JUMP forward to 652BH.
651D
CP 18H FE 18
Compare Register A against 18H, LDOS error 24, file not found, which is the only failure that means anything here.
651F
If the NZ FLAG has been set the open failed for some other reason, so JUMP to 63EAH to skip this file.
6522
LD A,(5213H) 3A 13 52
Load Register A from 5213H, the low byte of the NEW parameter result cell.
6525
OR A B7
OR Register A with itself to test it.
6526
If the NZ FLAG has been set NEW was given and the file is indeed not on the destination, so JUMP forward to 6532H to copy it.
6528
JUMP to 63EAH to skip the file. OLD was given and the file does not exist on the destination, so it is not wanted.
652B
LD A,(5215H) 3A 15 52
Load Register A from 5215H, the low byte of the OLD parameter result cell.
652E
OR A B7
OR Register A with itself to test it.
652F
If the Z FLAG has been set OLD was not given, which means NEW was, and the file already exists on the destination. JUMP to 63EAH to skip it.
6532
PUSH DE D5
PUSH the terminator position onto the stack again.
6533
LD HL,525DH 21 5D 52
Point Register Pair HL at 525DH, the saved copy of the filespec.
6536
LD DE,521DH 11 1D 52
Point Register Pair DE at 521DH, the filespec build area.
6539
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H, thirty-two bytes.
653C
LDIR ED B0
Block move the filespec back, undoing the damage @OPEN did when it built a file control block over it.
653E
LD A,(521AH) 3A 1A 52
Load Register A from 521AH, the high byte of the QUERY parameter result cell, which a reply of C clears at 65EFH.
6541
OR A B7
OR Register A with itself to test whether querying is still in force.
6542
If the Z FLAG has been set the operator is not being asked, so JUMP to 65F4H to display the file name and copy it.
6545
LD HL,6017H 21 17 60
Point Register Pair HL at the message at 6917H, Backup, the lead-in of the query line.
6548
GOSUB to the resident @DSPLY vector at 4467H to display it.
654B
POP DE D1
POP the terminator position back into Register Pair DE.
654C
POP HL E1
POP the directory record pointer back into Register Pair HL.
654D
PUSH DE D5
PUSH the terminator position onto the stack again.
654E
INC HL 23
INCrement Register Pair HL by 1 so that Register Pair HL points at directory offset 01H, the flags and modification month byte.
654F
BIT 6,(HL) CB 76
Test bit 6 of the byte at the address in Register Pair HL, the modification flag.
6551
If the Z FLAG has been set the file has not been modified, so JUMP forward to 655BH and leave the marker off.
6553
LD A,20H 3E 20
Load Register A with 20H, a blank, to separate the filespec from the marker.
6555
LD (DE),A 12
Store it at the address in Register Pair DE, over the filespec terminator.
6556
INC DE 13
INCrement Register Pair DE by 1.
6557
LD A,2BH 3E 2B
Load Register A with 2BH, a plus sign, which is the marker the manual describes as showing the file's mod flag status.
6559
LD (DE),A 12
Store it at the address in Register Pair DE.
655A
INC DE 13
INCrement Register Pair DE by 1.
655B
LD A,20H 3E 20
Load Register A with 20H, a blank, to separate the marker from the date.
655D
LD (DE),A 12
Store it at the address in Register Pair DE.
655E
INC DE 13
INCrement Register Pair DE by 1.
655F
INC HL 23
INCrement Register Pair HL by 1 so that Register Pair HL points at directory offset 02H, the second packed date byte.
6560
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, so that Register Pair HL addresses the text being built.
6561
LD (HL),7BH 36 7B
Store 7BH at the address in Register Pair HL. That is the left brace the manual shows in the query prompt.
6563
INC HL 23
INCrement Register Pair HL by 1.
6564
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL again, restoring the two pointers.
6565
LD A,(HL) 7E
Fetch directory offset 01H into Register A, the byte whose low nibble is the modification month.
6566
OR A B7
OR Register A with itself to test the whole byte for zero, which is what an undated file carries.
6567
If the Z FLAG has been set the file has no date, so JUMP forward to 65C8H to terminate the line without one.
6569
RRCA 0F
Rotate Register A right circularly, the first of three shifts that bring the day field down out of the two packed date bytes.
656A
RRCA 0F
Rotate Register A right circularly again.
656B
RRCA 0F
Rotate Register A right circularly a third time.
656C
AND 1FH E6 1F
AND Register A with 1FH to keep the five bits that are the day of the month.
656E
LD B,2FH 06 2F
Load Register B with 2FH, one below the character zero, as the tens digit being counted up.
6570
INC B 04
Loop Start
INCrement Register B by 1, counting one more ten.
6571
SUB 0AH D6 0A
SUBtract 0AH, ten, from Register A.
6573
If the NO CARRY FLAG has been set another ten can be taken, so LOOP BACK to 6570H.
6575
ADD A,3AH C6 3A
ADD 3AH to Register A, which both undoes the subtraction that went below zero and converts the remainder to its ASCII character in one step.
6577
PUSH AF F5
PUSH Register A and the flags onto the stack, holding the units digit while the tens is written.
6578
LD A,B 78
Load Register A from Register B, the tens digit.
6579
LD (DE),A 12
Store it at the address in Register Pair DE.
657A
INC DE 13
INCrement Register Pair DE by 1.
657B
POP AF F1
POP the units digit back into Register A.
657C
LD (DE),A 12
Store it at the address in Register Pair DE.
657D
INC DE 13
INCrement Register Pair DE by 1.
657E
LD A,2DH 3E 2D
Load Register A with 2DH, a hyphen, the separator in the DD-Mmm-YY form the manual shows.
6580
LD (DE),A 12
Store it at the address in Register Pair DE.
6581
INC DE 13
INCrement Register Pair DE by 1.
6582
PUSH HL E5
PUSH Register Pair HL onto the stack, the pointer into the directory record.
6583
DEC HL 2B
DECrement Register Pair HL by 1 so that Register Pair HL points back at directory offset 01H.
6584
LD A,(HL) 7E
Fetch that byte into Register A.
6585
AND 0FH E6 0F
AND Register A with 0FH to keep the low nibble, which is the month from 1 to 12.
6587
DEC A 3D
DECrement Register A by 1 to turn it into a zero-based index.
6588
LD C,A 4F
Load Register C with Register A, that index.
6589
RLCA 07
Rotate Register A left circularly, doubling the index.
658A
ADD A,C 81
ADD Register C to Register A, giving three times the index, because each month name is three characters.
658B
LD C,A 4F
Load Register C with Register A, the byte offset into the name table.
658C
LD B,00H 06 00
Load Register B with 00H, so that Register Pair BC holds that offset as a sixteen-bit value.
658E
LD HL,6114H 21 14 61
Point Register Pair HL at 6A14H, the table of twelve three-letter month abbreviations running from Jan to Dec.
6591
ADD HL,BC 09
ADD Register Pair BC to Register Pair HL, so that Register Pair HL points at this month's abbreviation.
6592
LD C,03H 0E 03
Load Register C with 03H, the three characters of an abbreviation.
6594
LDIR ED B0
Block move the abbreviation into the line being built.
6596
LD A,2DH 3E 2D
Load Register A with 2DH, the second hyphen of the date.
6598
LD (DE),A 12
Store it at the address in Register Pair DE.
6599
INC DE 13
INCrement Register Pair DE by 1.
659A
POP HL E1
POP the directory record pointer back into Register Pair HL, still addressing offset 02H.
659B
LD C,38H 0E 38
Load Register C with 38H, the character 8, as the default tens digit of the year. Every date the old format can express is in the nineteen eighties.
659D
LD A,(475DH) 3A 5D 47
Load Register A from 475DH, the resident per-drive extended-dating flag byte.
65A0
BIT 0,A CB 47
Self-Modifying Code
Test bit 0 of Register A. The second byte of this instruction is the one the routine at 6D6BH computes from the drive number, so this tests the bit belonging to whichever drive is current rather than always bit 0.
65A2
If the NZ FLAG has been set this pack is in the x.3 extended date format, so JUMP forward to 65A9H to take the year from the extended field.
65A4
LD A,(HL) 7E
Fetch directory offset 02H into Register A, whose low three bits are the year in the old format.
65A5
AND 07H E6 07
AND Register A with 07H to keep them.
65A7
JUMP forward to 65BFH with the units digit of the year in Register A and the tens still 8.
65A9
LD A,L 7D
Load Register A from Register L, the record's current offset.
65AA
ADD A,11H C6 11
ADD 11H to Register A, seventeen, so that Register A addresses directory offset 13H, the second byte of the word that under the old format held the access-password hash and under x.3 dating holds the packed time and the extended year.
65AC
LD L,A 6F
Load Register L with Register A, the adjusted pointer.
65AD
LD A,(HL) 7E
Fetch that byte into Register A.
65AE
AND 1FH E6 1F
AND Register A with 1FH to keep the five bits that are the year as an offset from 1980, which covers 1980 to 2011.
65B0
CP 14H FE 14
Compare Register A against 14H, twenty, which is the offset of the year 2000.
65B2
If the CARRY FLAG has been set the year is before 2000, so JUMP forward to 65B8H with the tens digit still 8.
65B4
SUB 14H D6 14
SUBtract 14H from Register A, reducing the year to an offset within the two thousands.
65B6
LD C,30H 0E 30
Load Register C with 30H, the character 0, as the tens digit, so that 2000 to 2009 display with a leading zero.
65B8
CP 0AH FE 0A
Compare Register A against 0AH, ten.
65BA
If the CARRY FLAG has been set the year is within the current decade, so JUMP forward to 65BFH.
65BC
INC C 0C
INCrement Register C by 1, stepping the tens digit on by one decade.
65BD
SUB 0AH D6 0A
SUBtract 0AH from Register A, reducing the year to its units digit.
65BF
LD B,A 47
Load Register B with Register A, the units digit of the year.
65C0
LD A,C 79
Load Register A from Register C, the tens digit, which is already an ASCII character.
65C1
LD (DE),A 12
Store it at the address in Register Pair DE.
65C2
INC DE 13
INCrement Register Pair DE by 1.
65C3
LD A,B 78
Load Register A from Register B, the units digit.
65C4
ADD A,30H C6 30
ADD 30H to Register A, converting it to its ASCII character.
65C6
LD (DE),A 12
Store it at the address in Register Pair DE.
65C7
INC DE 13
INCrement Register Pair DE by 1.
65C8
LD A,03H 3E 03
Load Register A with 03H, the terminator that leaves the cursor on the line.
65CA
LD (DE),A 12
Store it at the address in Register Pair DE, ending the query line.
65CB
LD HL,521DH 21 1D 52
Point Register Pair HL at 521DH, the whole line, which now reads as the filespec, an optional plus sign, the date and a left brace.
65CE
GOSUB to the resident @DSPLY vector at 4467H to display it.
65D1
LD HL,6103H 21 03 61
Point Register Pair HL at the prompt at 6A03H, } ? , which closes the brace and asks the question.
65D4
GOSUB to the resident @DSPLY vector at 4467H to display it.
65D7
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, using the bottom of the work space as the reply buffer.
65DA
LD B,03H 06 03
Load Register B with 03H, so that the line-input routine will take up to three characters.
65DC
GOSUB to the ROM line-input routine at 0040H to collect the reply.
65DF
If the CARRY FLAG has been set the operator pressed BREAK, so JUMP to the abort exit at 52ACH.
65E2
LD A,(HL) 7E
Fetch the first character of the reply from the address in Register Pair HL into Register A.
65E3
RES 5,A CB AF
RESet bit 5 of Register A, folding a lower-case letter to upper case.
65E5
CP 59H FE 59
Compare Register A against 59H, the letter Y.
65E7
POP DE D1
POP the terminator position back into Register Pair DE.
65E8
If the Z FLAG has been set the operator answered Y, so JUMP forward to 6607H to copy the file.
65EA
SUB 43H D6 43
SUBtract 43H, the letter C, from Register A. A result of zero means the reply was C, which the manual describes as copy this file and stop asking.
65EC
If the NZ FLAG has been set the reply was neither Y nor C, so JUMP to 63EBH to skip the file. That covers N, ENTER and anything else, exactly as the manual says.
65EF
LD (521AH),A 32 1A 52
Store Register A, which the subtraction left as zero, at 521AH, the high byte of the QUERY result cell. Querying is now off and every remaining file is copied without asking.
65F2
JUMP forward to 6607H to copy this file.
65F4
LD HL,6007H 21 07 60
Point Register Pair HL at the message at 6907H, Copying file: .
65F7
GOSUB to the resident @LOGOT vector at 447BH to display it.
65FA
LD HL,521DH 21 1D 52
Point Register Pair HL at 521DH, the filespec.
65FD
GOSUB to the resident @LOGOT vector at 447BH to display it.
6600
LD A,0DH 3E 0D
Load Register A with 0DH, a carriage return, to end the line.
6602
GOSUB to the ROM character-output routine at 0033H to send it.
6605
POP DE D1
POP the terminator position back into Register Pair DE.
6606
POP HL E1
POP the directory record pointer back into Register Pair HL.
6607
GOSUB to 6819H to append the override password and the source drive digit to the filespec, giving the string the source file will be opened with.

660AH - Copying a System File

A file occupying one of the fifteen reserved directory slots is not copied through the file system. Its directory record is written straight into the matching slot on the destination, and SYS0/SYS additionally carries the three system sectors on cylinder 0 with it. That is what the manual means when it says certain information about the default drive types and the state of the SYSGEN configuration parameter are moved from the source to the destination.

660A
POP BC C1
POP the Hash Index Table offset into Register Pair BC, recovering which directory record this file is.
660B
PUSH BC C5
PUSH it back, because everything below still needs it.
660C
LD A,B 78
Load Register A from Register B, that offset.
660D
AND 0D8H E6 D8
AND Register A with 0D8H, a mask that keeps the record number in the top three bits and bits 4 and 3 of the sector index.
660F
If the NZ FLAG has been set this record is not one of the fifteen reserved system slots, so JUMP to 66E2H to copy it as an ordinary file.
6612
LD A,00H 3E 00
Self-Modifying Code
Load Register A from the operand at 6613H, which is the attribute byte of this directory record, stored there at 642CH.
6614
BIT 6,A CB 77
Test bit 6 of Register A, the system-file bit.
6616
If the Z FLAG has been set this is not a system file after all, so JUMP to 66E2H and copy it normally.
6619
GOSUB to 5394H to mount the destination disk. A system file is not copied by the ordinary file machinery; its directory record is written straight into the reserved slot.
661C
LD D,(IY+09H) FD 56 09
Load Register D with the byte at IY+09H, the destination directory cylinder.
661F
LD A,B 78
Load Register A from Register B, the Hash Index Table offset.
6620
AND 1FH E6 1F
AND Register A with 1FH to keep the directory sector index.
6622
ADD A,02H C6 02
ADD 02H to Register A, because directory records begin at sector 2.
6624
LD E,A 5F
Load Register E with Register A, the sector to read.
6625
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, the buffer.
6628
GOSUB to 546CH, the disk-operation stub for operation 09H, to read the destination directory sector that holds the reserved slot.
662B
CP 06H FE 06
Compare Register A against 06H, the deleted data address mark status.
662D
If the NZ FLAG has been set the read failed, so JUMP to 529BH to exit with LDOS error 17.
6630
LD A,B 78
Load Register A from Register B, the Hash Index Table offset.
6631
AND 0E0H E6 E0
AND Register A with 0E0H to keep the record number, which multiplied by thirty-two is the record's offset in the sector.
6633
LD L,A 6F
Load Register L with Register A, so that Register Pair HL points at the destination record.
6634
BIT 4,(HL) CB 66
Test bit 4 of the byte at the address in Register Pair HL, the allocated bit.
6636
If the NZ FLAG has been set the slot already holds a system file, so JUMP forward to 6652H and just update it.
6638
LD (HL),5FH 36 5F
Store 5FH at the address in Register Pair HL, the attribute byte of a fresh system directory record: primary, system, in use, invisible, with an access level of seven.
663A
INC HL 23
INCrement Register Pair HL by 1 to point at directory offset 01H.
663B
LD (HL),00H 36 00
Store 00H there, clearing the flags and modification month.
663D
LD D,H 54
Load Register D with Register H, starting a pointer one byte above.
663E
LD E,L 5D
Load Register E with Register L, completing it.
663F
INC DE 13
INCrement Register Pair DE by 1, so that a block move from here becomes a fill.
6640
LD BC,0003H 01 03 00
Load Register Pair BC with 0003H, three bytes.
6643
LDIR ED B0
Block move the zero byte upward, clearing directory offsets 02H to 04H, the rest of the date, the end-of-file byte and the logical record length.
6645
LD A,L 7D
Load Register A from Register L, the position reached.
6646
ADD A,0CH C6 0C
ADD 0CH to Register A, twelve, so that Register A addresses directory offset 10H, the start of the two password words.
6648
LD L,A 6F
Load Register L with Register A, the adjusted pointer.
6649
INC A 3C
INCrement Register A by 1 for the destination of the fill below.
664A
LD E,A 5F
Load Register E with Register A, completing that pointer.
664B
LD (HL),0FFH 36 FF
Store 0FFH at the address in Register Pair HL, the first byte of the fill.
664D
LD BC,000FH 01 0F 00
Load Register Pair BC with 000FH, fifteen bytes.
6650
LDIR ED B0
Block move that byte upward, filling the password words, the ending record number and the four extent pairs with 0FFH, which marks them as unused.
6652
LD A,L 7D
Load Register A from Register L, the position reached.
6653
AND 0E0H E6 E0
AND Register A with 0E0H to round back to the start of the record.
6655
BIT 6,(HL) CB 76
Test bit 6 of the byte at the address in Register Pair HL, the system-file bit of the record as it now stands.
6657
If the Z FLAG has been set the slot is not a system record, so JUMP to 639FH to refuse with Can't create SYSTEM disk - directory slots in use!.
665A
ADD A,05H C6 05
ADD 05H to Register A, the offset of the name field.
665C
LD L,A 6F
Load Register L with Register A, so that Register Pair HL points at the destination record's name.
665D
LD E,A 5F
Load Register E with Register A, the same offset, for the source pointer below.
665E
LD H,58H 26 58
Load Register H with 58H so that Register Pair HL addresses the buffer at 5800H, which holds the source directory sector read at 641DH.
6660
LD A,(5286H) 3A 86 52
Load Register A from 5286H, the high byte of the free-space pointer, which is the page the destination sector was read into.
6663
LD D,A 57
Load Register D with Register A, completing the destination pointer.
6664
LD BC,000DH 01 0D 00
Load Register Pair BC with 000DH, thirteen bytes, the eight characters of a name and the three of an extension plus two more.
6667
LDIR ED B0
Block move the source record's name and extension into the destination record, so that the reserved slot now names the system file being copied.
6669
LD D,(IY+09H) FD 56 09
Load Register D with the byte at IY+09H, the destination directory cylinder.
666C
POP BC C1
POP the Hash Index Table offset into Register Pair BC.
666D
PUSH BC C5
PUSH it back.
666E
LD A,B 78
Load Register A from Register B, the offset.
666F
AND 1FH E6 1F
AND Register A with 1FH to keep the directory sector index.
6671
ADD A,02H C6 02
ADD 02H to Register A, the offset of the first directory sector.
6673
LD E,A 5F
Load Register E with Register A, the sector to write.
6674
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, the buffer holding the updated destination directory sector.
6677
GOSUB to 5468H, the disk-operation stub for operation 0EH, to write it back with a deleted data address mark.
667A
LD A,12H 3E 12
Load Register A with 12H, LDOS error 18, ready in case the write failed.
667C
If the NZ FLAG has been set the write failed, so JUMP to the disk-error exit at 52A0H.
667F
POP BC C1
POP the Hash Index Table offset into Register Pair BC.
6680
PUSH BC C5
PUSH it back.
6681
LD A,B 78
Load Register A from Register B, the offset.
6682
CP 02H FE 02
Compare Register A against 02H. Offset 2 is directory record 2, which on a system disk is SYS0/SYS, the resident core, and that one file is copied sector by sector rather than through the file system.
6684
If the NZ FLAG has been set this is some other system file, so JUMP forward to 66E2H to copy it as an ordinary file.
6686
GOSUB to 5327H to mount the source disk.
6689
LD DE,0002H 11 02 00
Load Register Pair DE with 0002H, cylinder 0 and sector 2, which FORMAT/CMD lays down as the system configuration sector.
668C
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, the buffer.
668F
GOSUB to 546CH, the disk-operation stub for operation 09H, to read it.
6692
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H.
6695
INC L 2C
INCrement Register L by 1 to point at the second byte of the configuration sector.
6696
LD (HL),0C9H 36 C9
Store 0C9H there, a RET, which disarms whatever start-up code the configuration sector carried. The manual explains that when SYS0/SYS is included in a backup by class, the SYSGEN state and the default drive configurations move with it, and this is where that transfer is made safe.
6698
LD DE,0003H 11 03 00
Load Register Pair DE with 0003H, cylinder 0 sector 3, which is FORMAT's copy of the completed Granule Allocation Table image.
669B
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H.
669E
INC H 24
INCrement Register H by 1, so that this sector is read into the page above the last one.
669F
GOSUB to 546CH, the disk-operation stub for operation 09H, to read it.
66A2
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H.
66A5
LD DE,0004H 11 04 00
Load Register Pair DE with 0004H, cylinder 0 sector 4, which the two boot sectors read expecting the resident system loader.
66A8
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H.
66AB
INC H 24
INCrement Register H by 1, the first of two steps to the third buffer page.
66AC
INC H 24
INCrement Register H by 1 again.
66AD
GOSUB to 5470H, the disk-operation stub for operation 0AH, to verify that the sector exists before trying to read it. Not every disk carries one.
66B0
If the NZ FLAG has been set there is no such sector, so JUMP forward to 66B8H and copy only the two that were read.
66B2
GOSUB to 546CH, the disk-operation stub for operation 09H, to read it.
66B5
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H.
66B8
GOSUB to 5394H to mount the destination disk, now that the system sectors are in the buffer.
66BB
LD DE,0002H 11 02 00
Load Register Pair DE with 0002H, cylinder 0 sector 2 of the destination.
66BE
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, the first buffer page.
66C1
GOSUB to 5464H, the disk-operation stub for operation 0DH, to write the configuration sector.
66C4
If the NZ FLAG has been set the write failed, so JUMP to the disk-error exit at 52A0H.
66C7
LD DE,0003H 11 03 00
Load Register Pair DE with 0003H, cylinder 0 sector 3 of the destination.
66CA
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H.
66CD
INC H 24
INCrement Register H by 1 to reach the second buffer page.
66CE
GOSUB to 5464H, the disk-operation stub for operation 0DH, to write the Granule Allocation Table image.
66D1
If the NZ FLAG has been set the write failed, so JUMP to the disk-error exit at 52A0H.
66D4
LD DE,0004H 11 04 00
Load Register Pair DE with 0004H, cylinder 0 sector 4 of the destination.
66D7
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H.
66DA
INC H 24
INCrement Register H by 1, the first of two steps to the third buffer page.
66DB
INC H 24
INCrement Register H by 1 again.
66DC
GOSUB to 5464H, the disk-operation stub for operation 0DH, to write the system loader sector.
66DF
If the NZ FLAG has been set the write failed, so JUMP to the disk-error exit at 52A0H.

66E2H - Copying an Ordinary File

The file is opened at the source and created at the destination, both with a logical record length of 256 so that the copy is byte for byte. Before any data moves the whole of the destination file is allocated by positioning to its last record, so that a full disk is discovered at once rather than half way through. The copy then fills the entire work space from the source and writes the entire work space to the destination, which is what keeps the number of disk swaps down to one per buffer on a single-drive machine.

66E2
LD B,00H 06 00
Load Register B with 00H, so that the routine below opens the source file with a logical record length of 256, which is what a byte-for-byte copy needs.
66E4
GOSUB to 68DFH, which mounts the source disk and points Register Pair DE at the source file control block at 521DH.
66E7
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, the buffer the file will be read through.
66EA
GOSUB to the resident @OPEN vector at 4424H, which opens the source file. The filespec carries the override password, so protection is ignored.
66ED
If the NZ FLAG has been set the open failed, so JUMP to the disk-error exit at 52A0H.
66F0
LD HL,(5229H) 2A 29 52
Load Register Pair HL from 5229H, the ending record number field of the source file control block, which is the length of the file in 256-byte records.
66F3
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE from the operand at 66F4H, the total number of sectors the destination disk can hold, computed at 633FH.
66F6
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, so that the capacity is in Register Pair HL and the file length in Register Pair DE.
66F7
SBC HL,DE ED 52
SUBtract Register Pair DE from Register Pair HL. The CARRY FLAG is set when the file is longer than the whole destination disk.
66F9
If the NO CARRY FLAG has been set the file will fit, so JUMP forward to 6704H to copy it.
66FB
LD HL,605EH 21 5E 60
Point Register Pair HL at the message at 695EH, File is larger than destination capacity - backup is bypassed.
66FE
GOSUB to the resident @LOGOT vector at 447BH to display it. The manual states that BACKUP will not allow a single file to be split across destination disks.
6701
JUMP to 63EBH to skip this file and go on to the next.
6704
LD B,00H 06 00
Load Register B with 00H, a logical record length of 256 again, for the destination file.
6706
GOSUB to 68E8H, which mounts the destination disk and points Register Pair DE at the destination file control block at 523DH.
6709
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, the buffer.
670C
GOSUB to the resident @INIT vector at 4420H, which creates the destination file, overwriting one of the same name if it exists.
670F
If the NZ FLAG has been set the create failed, so JUMP to the disk-error exit at 52A0H.
6712
LD A,(5244H) 3A 44 52
Load Register A from 5244H, offset 07H of the source file control block, which is the directory record number the file was found at.
6715
LD (5E83H),A 32 83 5E
Self-Modifying Code
Store it at 5E83H, an operand inside the code that has already run, which is being reused as a scratch cell to remember where the destination file's directory record is.
6718
LD BC,(5229H) ED 4B 29 52
Load Register Pair BC from 5229H, the ending record number of the source file, which is how many 256-byte records are to be moved.
671C
GOSUB to 68A8H, which pre-allocates that much space on the destination by positioning to the last record, so that a disk-full condition is discovered before any data is written rather than half way through.
671F
If the Z FLAG has been set the space was allocated, so JUMP forward to 672CH to copy the data.
6721
POP BC C1
POP the Hash Index Table offset into Register Pair BC. The destination is full and the operator has been asked for a fresh disk.
6722
LD L,B 68
Load Register L with Register B, the offset.
6723
LD H,58H 26 58
Load Register H with 58H, so that Register Pair HL addresses the source directory sector buffer.
6725
PUSH BC C5
PUSH the Hash Index Table offset back onto the stack.
6726
GOSUB to 5327H to mount the source disk again, since the operator has just been handling the destination.
6729
JUMP to 64C2H to rebuild the filespec and retry this same file on the new destination disk.
672C
LD HL,0000H 21 00 00
Point Register Pair HL at 0000H, record zero.
672F
LD (5249H),HL 22 49 52
Store it at 5249H, the next record number field of the destination file control block, so that writing starts at the beginning of the file.
6732
GOSUB to the resident @REW vector at 443FH, which rewinds the source file to its first record.
6735
LD HL,(5285H) 2A 85 52
Loop Start
Load Register Pair HL from the free-space pointer at 5285H, the bottom of the buffer.
6738
LD (5220H),HL 22 20 52
Loop Start
Store Register Pair HL at 5220H, the buffer address field of the source file control block, so that the next record is read to this page.
673B
GOSUB to 68DFH to mount the source disk and point Register Pair DE at the source file control block.
673E
GOSUB to the resident @READ vector at 4436H to read one 256-byte record.
6741
If the Z FLAG has been set the read succeeded, so JUMP forward to 674EH to fill the next page.
6743
CP 1CH FE 1C
Compare Register A against 1CH, LDOS error 28, end of file.
6745
If the Z FLAG has been set the file has ended, so JUMP forward to 676BH to write out what is in the buffer and close.
6747
CP 1DH FE 1D
Compare Register A against 1DH, LDOS error 29, past end of file.
6749
If the Z FLAG has been set the file has ended, so JUMP forward to 676BH as well.
674B
JUMP to the disk-error exit at 52A0H. The read failed for some other reason.
674E
INC H 24
INCrement Register H by 1 to step the buffer pointer on by one page, one record per page.
674F
LD A,H 7C
Load Register A from Register H, the page now reached.
6750
CP 00H FE 00
Self-Modifying Code
Compare Register A against the operand at 6751H, the top page of the work space planted at 6306H.
6752
If the NZ FLAG has been set the buffer is not full, so LOOP BACK to 6738H to read another record.
6754
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, back at the bottom of the buffer, ready to write it all out.
6757
LD (5240H),HL 22 40 52
Loop Start
Store Register Pair HL at 5240H, the buffer address field of the destination file control block.
675A
GOSUB to 68E8H to mount the destination disk and point Register Pair DE at the destination file control block.
675D
GOSUB to the resident @VER vector at 443CH, which writes one record and reads it back to check it.
6760
If the NZ FLAG has been set the write failed, so JUMP to the disk-error exit at 52A0H.
6763
INC H 24
INCrement Register H by 1 to step to the next page of the buffer.
6764
LD A,H 7C
Load Register A from Register H, the page reached.
6765
CP 00H FE 00
Self-Modifying Code
Compare Register A against the operand at 6766H, the top page of the work space planted at 6309H.
6767
If the NZ FLAG has been set there is more of the buffer to write, so LOOP BACK to 6757H.
6769
LOOP BACK to 6735H to fill the buffer from the source again. Reading a whole buffer and then writing a whole buffer is what keeps the number of disk swaps down on a single-drive machine.

676BH - Closing the File and Copying its Directory Record

Closing the destination file gives it an end-of-file byte and an ending record number, but not the original's date or attributes, so those are copied across by hand from the source directory record. This is also where an old-format date is converted to the x.3 form and where the source file's modification flag is cleared, which is the housekeeping the manual describes and the only part of the copy that writes to the source disk.

676B
GOSUB to 6889H, which writes out however much of the buffer the last partial fill left in it.
676E
LD HL,(5225H) 2A 25 52
Load Register Pair HL from 5225H, the end-of-file byte and logical record length of the source file control block.
6771
LD (5245H),HL 22 45 52
Store it at 5245H, the same fields of the destination file control block, so that the copy ends on exactly the same byte as the original rather than being rounded up to a whole record.
6774
JUMP forward to 6776H. The instruction is a jump to the next address and does nothing, which is what is left of a test that no longer has to be made.
6776
GOSUB to 68E8H to mount the destination disk and point Register Pair DE at the destination file control block.
6779
GOSUB to the resident @CLOSE vector at 4428H, which finalises the destination directory entry and releases any granules that were allocated but not used.
677C
If the NZ FLAG has been set the close failed, so JUMP to the disk-error exit at 52A0H.
677F
LD D,(IY+09H) FD 56 09
Load Register D with the byte at IY+09H, the destination directory cylinder.
6782
LD B,00H 06 00
Self-Modifying Code
Load Register B from the operand at 6783H, the destination file's directory record number, saved there at 6715H.
6784
LD A,B 78
Load Register A from Register B, that record number.
6785
AND 1FH E6 1F
AND Register A with 1FH to keep the directory sector index.
6787
ADD A,02H C6 02
ADD 02H to Register A, the offset of the first directory sector.
6789
LD E,A 5F
Load Register E with Register A, the sector to read.
678A
PUSH DE D5
PUSH Register Pair DE, the cylinder and sector, onto the stack.
678B
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, the buffer.
678E
GOSUB to 546CH, the disk-operation stub for operation 09H, to read the destination directory sector.
6791
CP 06H FE 06
Compare Register A against 06H, the deleted data address mark status.
6793
LD A,11H 3E 11
Load Register A with 11H, LDOS error 17, ready in case the read failed.
6795
If the NZ FLAG has been set the read failed, so JUMP to the disk-error exit at 52A0H.
6798
LD A,B 78
Load Register A from Register B, the directory record number.
6799
AND 0E0H E6 E0
AND Register A with 0E0H to keep the record number within the sector.
679B
LD E,A 5F
Load Register E with Register A, the destination record's offset.
679C
LD A,(5286H) 3A 86 52
Load Register A from 5286H, the high byte of the free-space pointer, the page the destination sector was read into.
679F
LD D,A 57
Load Register D with Register A, completing the destination pointer.
67A0
POP HL E1
POP the cylinder and sector back into Register Pair HL.
67A1
POP BC C1
POP the Hash Index Table offset into Register Pair BC.
67A2
PUSH BC C5
PUSH it back.
67A3
PUSH HL E5
PUSH the cylinder and sector onto the stack.
67A4
PUSH DE D5
PUSH the destination record pointer in Register Pair DE onto the stack.
67A5
LD A,B 78
Load Register A from Register B, the Hash Index Table offset.
67A6
AND 0E0H E6 E0
AND Register A with 0E0H to keep the record number within the sector.
67A8
LD L,A 6F
Load Register L with Register A, so that Register Pair HL points at the source directory record.
67A9
LD H,58H 26 58
Load Register H with 58H, addressing the buffer at 5800H that still holds the source directory sector.
67AB
INC L 2C
INCrement Register L by 1 to point at directory offset 01H of the source record.
67AC
RES 6,(HL) CB B6
RESet bit 6 of the byte at the address in Register Pair HL, clearing the modification flag in the in-memory copy of the source record. It is written back to the source disk at 67FEH.
67AE
DEC L 2D
DECrement Register L by 1 to point back at the start of the source record.
67AF
LD BC,0005H 01 05 00
Load Register Pair BC with 0005H, five bytes.
67B2
LDIR ED B0
Block move directory offsets 00H to 04H from the source record to the destination record: the attribute byte, the flags and modification month, the rest of the date, the end-of-file byte and the logical record length. The copy therefore inherits the original's date and attributes rather than being stamped with today's.
67B4
LD A,E 7B
Load Register A from Register E, the position reached in the destination record.
67B5
ADD A,0BH C6 0B
ADD 0BH to Register A, eleven, stepping over the eight-character name and the three-character extension, which @INIT has already filled in correctly.
67B7
LD E,A 5F
Load Register E with Register A, the adjusted destination pointer.
67B8
LD A,L 7D
Load Register A from Register L, the position reached in the source record.
67B9
ADD A,0BH C6 0B
ADD 0BH to Register A, the same step.
67BB
LD L,A 6F
Load Register L with Register A, the adjusted source pointer.
67BC
LD BC,0004H 01 04 00
Load Register Pair BC with 0004H, four bytes.
67BF
LDIR ED B0
Block move directory offsets 10H to 13H, the two password words, which under x.3 dating carry the modification time and the extended year.
67C1
POP HL E1
POP the destination record pointer back into Register Pair HL.
67C2
LD A,(5212H) 3A 12 52
Load Register A from 5212H, the date-format conversion flag, which is zero only when an old-format source is being copied to an x.3 destination.
67C5
OR A B7
OR Register A with itself to test it.
67C6
If the NZ FLAG has been set no conversion is needed, so JUMP forward to 67D2H.
67C8
DEC DE 1B
DECrement Register Pair DE by 1 so that Register Pair DE points at directory offset 13H of the destination record.
67C9
INC HL 23
INCrement Register Pair HL by 1, the first of two steps into the destination record.
67CA
INC HL 23
INCrement Register Pair HL by 1 again, so that Register Pair HL points at directory offset 02H, the byte whose low three bits are the year in the old format.
67CB
LD A,(HL) 7E
Fetch that byte into Register A.
67CC
AND 07H E6 07
AND Register A with 07H to keep the old three-bit year.
67CE
LD (DE),A 12
Store it at the address in Register Pair DE, at offset 13H, which is where the x.3 format keeps the extended year. This is the manual's statement that backing up from a pre-x.3 disk to an x.3 disk establishes the x.3 style directory date and time information.
67CF
DEC DE 1B
DECrement Register Pair DE by 1 so that Register Pair DE points at directory offset 12H.
67D0
XOR A AF
Clear Register A to zero.
67D1
LD (DE),A 12
Store it at offset 12H, the packed time, which an old-format disk has no value for. The old access-password hash that used to live in these two bytes is therefore destroyed, which is the other half of the manual's statement that the access password is removed.
67D2
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, the buffer holding the updated destination directory sector.
67D5
POP DE D1
POP the cylinder and sector back into Register Pair DE.
67D6
GOSUB to 5468H, the disk-operation stub for operation 0EH, to write the destination directory sector back with a deleted data address mark.
67D9
LD A,12H 3E 12
Load Register A with 12H, LDOS error 18, ready in case the write failed.
67DB
If the NZ FLAG has been set the write failed, so JUMP to the disk-error exit at 52A0H.
67DE
LD A,00H 3E 00
Self-Modifying Code
Load Register A from the operand at 67DFH, which is set to 0FFH at 680DH the first time the source turns out to be write protected. It ships as zero.
67E0
OR A B7
OR Register A with itself to test it.
67E1
If the NZ FLAG has been set the source has already been found to be write protected, so JUMP to 63EBH and go on to the next file without trying to clear the modification flag again. The manual explains that the message is not displayed after every file although the mod flags will not be removed from any source file.
67E4
POP BC C1
POP the Hash Index Table offset into Register Pair BC.
67E5
PUSH BC C5
PUSH it back.
67E6
LD A,B 78
Load Register A from Register B, the offset.
67E7
AND 0E0H E6 E0
AND Register A with 0E0H to keep the record number within the sector.
67E9
INC A 3C
INCrement Register A by 1 so that Register A addresses directory offset 01H of the source record.
67EA
LD L,A 6F
Load Register L with Register A, the pointer.
67EB
LD H,58H 26 58
Load Register H with 58H, addressing the source directory sector buffer.
67ED
RES 6,(HL) CB B6
RESet bit 6 of the byte at the address in Register Pair HL, clearing the modification flag again in case the block move above disturbed it.
67EF
GOSUB to 5327H to mount the source disk, since the directory sector is about to be written back to it.
67F2
LD D,(IY+09H) FD 56 09
Load Register D with the byte at IY+09H, the source directory cylinder.
67F5
LD A,B 78
Load Register A from Register B, the Hash Index Table offset.
67F6
AND 1FH E6 1F
AND Register A with 1FH to keep the directory sector index.
67F8
ADD A,02H C6 02
ADD 02H to Register A, the offset of the first directory sector.
67FA
LD E,A 5F
Load Register E with Register A, the sector to write.
67FB
LD HL,5800H 21 00 58
Point Register Pair HL at 5800H, the source directory sector buffer.
67FE
GOSUB to 5468H, the disk-operation stub for operation 0EH, to write it back with a deleted data address mark.
6801
If the Z FLAG has been set the write succeeded and this file is finished, so JUMP to 63EBH to go on to the next.
6804
CP 0FH FE 0F
Compare Register A against 0FH, LDOS error 15, which is what a write to a protected disk reports.
6806
LD A,12H 3E 12
Load Register A with 12H, LDOS error 18, ready in case it is some other failure.
6808
If the NZ FLAG has been set the write failed for a reason other than write protection, so JUMP to the disk-error exit at 52A0H.
680B
LD A,0FFH 3E FF
Load Register A with 0FFH.
680D
LD (5EDFH),A 32 DF 5E
Self-Modifying Code
Store it at 5EDFH, which is the operand of the load at 67DEH. From now on the modification-flag write is not even attempted, and the message below is not displayed again.
6810
LD HL,55A1H 21 A1 55
Point Register Pair HL at the message at 55A1H, Source disk is write protected, can't clear mod flags.
6813
GOSUB to the resident @LOGOT vector at 447BH to display it. The manual says this appears after the first file has been copied and is not an error.
6816
JUMP to 63EBH to go on to the next file.

6819H - Building the Two File Specifications

One routine produces both the source and the destination specification. It appends the override password and the source drive digit to the name that has already been built, copies the whole thing to the destination file control block area, and changes the one character after the colon. The two specifications are therefore identical but for the drive digit.

6819
LD HL,5AE0H 21 E0 5A
Point Register Pair HL at 63E0H, the ten-byte string 2EH 52H 53H 30H 4CH 54H 30H 46H 46H 3AH, which reads as a full stop, the eight characters RS0LT0FF and a colon.
681C
LD BC,000AH 01 0A 00
Load Register Pair BC with 000AH, those ten bytes.
681F
LDIR ED B0
Block move them onto the end of the filespec at the address in Register Pair DE, overwriting its terminator. In an LDOS filespec a full stop introduces the password field, so every file this utility opens is opened with that password. Its hash is 113DH, which SYS2 accepts at its own 4FDAH as an override that skips the directory password check entirely. This is the mechanism behind the manual's statement that BACKUP will ignore any password protection on a file.
6821
LD A,(531BH) 3A 1B 53
Load Register A from 531BH, the source drive number planted by the front end at 5A4EH.
6824
AND 07H E6 07
AND Register A with 07H to keep just the drive number.
6826
ADD A,30H C6 30
ADD 30H to Register A, converting it to its ASCII digit.
6828
LD (DE),A 12
Store it at the address in Register Pair DE, after the colon, completing the drivespec.
6829
INC DE 13
INCrement Register Pair DE by 1.
682A
LD A,03H 3E 03
Load Register A with 03H, the terminator.
682C
LD (DE),A 12
Store it at the address in Register Pair DE, ending the source filespec.
682D
LD HL,521DH 21 1D 52
Point Register Pair HL at 521DH, the completed source filespec.
6830
LD DE,523DH 11 3D 52
Point Register Pair DE at 523DH, the destination file control block area.
6833
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H, thirty-two bytes.
6836
LDIR ED B0
Block move the whole filespec across, so that the destination copy starts out identical to the source one.
6838
LD HL,523DH 21 3D 52
Point Register Pair HL at 523DH, the destination copy.
683B
LD A,(HL) 7E
Loop Start
Fetch a character of it into Register A.
683C
INC HL 23
INCrement Register Pair HL by 1 to step past it.
683D
CP 3AH FE 3A
Compare Register A against 3AH, the colon that introduces the drivespec.
683F
If the NZ FLAG has been set this is not the colon, so LOOP BACK to 683BH to keep looking.
6841
LD A,(5388H) 3A 88 53
Load Register A from 5388H, the destination drive number planted by the front end at 5A7CH.
6844
AND 07H E6 07
AND Register A with 07H to keep just the drive number.
6846
ADD A,30H C6 30
ADD 30H to Register A, converting it to its ASCII digit.
6848
LD (HL),A 77
Store it at the address in Register Pair HL, over the source drive digit, so that the destination filespec names the destination drive. The two filespecs are now identical but for that one character.
6849
RET C9
RETurn to the caller with both file control block areas prepared.

684AH - Unpacking a Directory Date

The DATE parameter was parsed into the same two-byte form a directory record carries, so the selection test is a plain sixteen-bit comparison. This routine rebuilds that form from a record, and it is where the two date formats are reconciled: the month and day come from the same place either way, and only the year is taken from a different field depending on whether the pack has been converted to x.3 dating.

684A
LD A,(HL) 7E
Fetch directory offset 01H into Register A, the byte whose low nibble is the modification month and whose bit 6 is the modification flag.
684B
AND 0FH E6 0F
AND Register A with 0FH to keep the month.
684D
LD E,00H 1E 00
Load Register E with 00H, the low half of the packed date being rebuilt.
684F
SRL A CB 3F
Shift Register A right logically, moving the low bit of the month into the CARRY FLAG.
6851
RR E CB 1B
Rotate Register E right through the CARRY FLAG, bringing that bit into bit 7 of Register E, which is where the packed form the DATE parameter was parsed into keeps it.
6853
LD D,A 57
Load Register D with Register A, the remaining bits of the month.
6854
INC HL 23
INCrement Register Pair HL by 1 to point at directory offset 02H.
6855
LD A,(HL) 7E
Fetch that byte into Register A.
6856
AND 0F8H E6 F8
AND Register A with 0F8H to keep the five bits that are the day of the month.
6858
RRCA 0F
Rotate Register A right circularly, aligning the day with the position the parser puts it in.
6859
OR E B3
OR Register A with Register E, folding in the low bit of the month.
685A
LD E,A 5F
Load Register E with Register A, the completed low byte of the packed date.
685B
LD A,(475DH) 3A 5D 47
Load Register A from 475DH, the resident per-drive extended-dating flag byte.
685E
BIT 0,A CB 47
Self-Modifying Code
Test bit 0 of Register A. The second byte of this instruction is computed from the drive number by the routine at 6D6BH, so this tests the bit belonging to the current drive.
6860
If the NZ FLAG has been set this pack is in the x.3 extended date format, so JUMP forward to 686BH to take the year from the extended field.
6862
LD A,(HL) 7E
Fetch directory offset 02H into Register A, in the old format.
6863
AND 07H E6 07
AND Register A with 07H to keep the three low bits, which are the year.
6865
RLCA 07
Rotate Register A left circularly, the first of three shifts that move the year into the top of the high byte.
6866
RLCA 07
Rotate Register A left circularly again.
6867
RLCA 07
Rotate Register A left circularly a third time.
6868
OR D B2
OR Register A with Register D, folding in the remaining bits of the month.
6869
LD D,A 57
Load Register D with Register A, the completed high byte, so that Register Pair DE now holds the file's date in the same form the DATE parameter was parsed into and the comparison at 64AAH needs no unpacking.
686A
RET C9
RETurn to the caller with the packed date in Register Pair DE.
686B
LD A,L 7D
Load Register A from Register L, the record's current offset.
686C
ADD A,11H C6 11
ADD 11H to Register A, seventeen, so that Register A addresses directory offset 13H.
686E
LD L,A 6F
Load Register L with Register A, the adjusted pointer.
686F
LD A,(HL) 7E
Fetch that byte into Register A, the second half of the word that under x.3 dating holds the packed time and the extended year.
6870
AND 1FH E6 1F
AND Register A with 1FH to keep the five bits that are the year as an offset from 1980.
6872
LOOP BACK to 6865H to shift and fold the year in exactly as the old-format path does. Only the source of the year differs between the two formats.

6874H - Write and Prove a System Sector

A short helper that writes the sector in the buffer with a deleted data address mark and then verifies it, with a distinct error number for each half so that a failure says which of the two it was.

6874
LD L,00H 2E 00
Load Register L with 00H so that Register Pair HL points at the start of the Granule Allocation Table buffer at 6200H, Register H already holding 62H.
6876
GOSUB to 5468H, the disk-operation stub for operation 0EH, to write it back with a deleted data address mark.
6879
LD A,15H 3E 15
Load Register A with 15H, LDOS error 21, ready in case the write failed.
687B
If the NZ FLAG has been set the write failed, so JUMP to the disk-error exit at 52A0H.
687E
GOSUB to 5470H, the disk-operation stub for operation 0AH, to verify the sector just written.
6881
CP 06H FE 06
Compare Register A against 06H, the deleted data address mark status a directory sector reports on a good verify.
6883
LD A,14H 3E 14
Load Register A with 14H, LDOS error 20, ready in case it did not.
6885
If the NZ FLAG has been set the verify failed, so JUMP to the disk-error exit at 52A0H.
6888
RET C9
RETurn to the caller with the Granule Allocation Table written and proved.
6889
LD A,(5286H) 3A 86 52
Load Register A from 5286H, the high byte of the free-space pointer, which is the page the buffer starts at.
688C
CP H BC
Compare Register A against Register H, the page the read loop stopped at. They are equal when nothing at all was read into the buffer.
688D
RET Z C8
RETurn if the Z FLAG has been set. There is nothing to write.
688E
LD A,00H 3E 00
Self-Modifying Code
Load Register A from the operand at 688FH, the top page of the work space planted at 630CH.
6890
CP H BC
Compare Register A against Register H. They are equal when the buffer was filled exactly and has already been written out by the main loop.
6891
RET Z C8
RETurn if the Z FLAG has been set. Again there is nothing left to do.
6892
LD B,H 44
Load Register B with Register H, the page the read loop stopped at, as the limit for the loop below.
6893
LD HL,(5285H) 2A 85 52
Loop Start
Load Register Pair HL from the free-space pointer at 5285H, the bottom of the buffer.
6896
LD (5240H),HL 22 40 52
Loop Start
Store Register Pair HL at 5240H, the buffer address field of the destination file control block.
6899
GOSUB to 68E8H to mount the destination disk and point Register Pair DE at the destination file control block.
689C
GOSUB to the resident @VER vector at 443CH, which writes one record and reads it back to check it.
689F
If the NZ FLAG has been set the write failed, so JUMP to the disk-error exit at 52A0H.
68A2
INC H 24
INCrement Register H by 1 to step to the next page of the buffer.
68A3
LD A,H 7C
Load Register A from Register H, the page reached.
68A4
CP B B8
Compare Register A against Register B, the page the read loop stopped at.
68A5
If the NZ FLAG has been set there is more to write, so LOOP BACK to 6896H.
68A7
RET C9
RETurn to the caller with the partial buffer written out.

68A8H - Allocating the Whole File in Advance

Positioning past the end of a file is what makes the DOS allocate space for it, so the engine positions to the last record the file will need and writes one sector there before it copies anything. A disk-full failure at that point costs nothing, and the partly allocated file is killed so that the disk is left tidy before the operator is asked for a fresh one.

68A8
LD A,B 78
Load Register A from Register B, the high byte of the record count the file needs.
68A9
OR C B1
OR Register A with Register C, the low byte, so that the whole count is tested for zero.
68AA
RET Z C8
RETurn if the Z FLAG has been set. An empty file needs no space allocated.
68AB
DEC BC 0B
DECrement Register Pair BC by 1, because the last record is numbered one below the count.
68AC
GOSUB to 68E8H to mount the destination disk and point Register Pair DE at the destination file control block.
68AF
PUSH DE D5
PUSH Register Pair DE, the file control block address, onto the stack.
68B0
GOSUB to the resident @POSN vector at 4442H, which positions the file to the record number in Register Pair BC. Positioning past the end of a file is what makes the DOS allocate the space for it, so this reserves the whole file in one operation rather than discovering a full disk half way through the copy.
68B3
LD HL,(5285H) 2A 85 52
Load Register Pair HL from the free-space pointer at 5285H, the bottom of the buffer.
68B6
LD D,H 54
Load Register D with Register H, starting a pointer one byte above.
68B7
LD E,L 5D
Load Register E with Register L, completing it.
68B8
INC DE 13
INCrement Register Pair DE by 1, so that the block move below becomes a fill.
68B9
LD BC,00FFH 01 FF 00
Load Register Pair BC with 00FFH, 255 bytes.
68BC
LD (HL),0E5H 36 E5
Store 0E5H at the address in Register Pair HL, the byte a freshly formatted sector carries.
68BE
LDIR ED B0
Block move it upward, filling the page so that the record about to be written holds the same value an unwritten sector would.
68C0
POP DE D1
POP the file control block address back into Register Pair DE.
68C1
GOSUB to the resident @VER vector at 443CH, which writes that record and reads it back, forcing the allocation to be made and proving the space is usable.
68C4
RET Z C8
RETurn if the Z FLAG has been set. The space was allocated and the copy can go ahead.
68C5
CP 1BH FE 1B
Compare Register A against 1BH, LDOS error 27, disk space full.
68C7
If the NZ FLAG has been set the failure was something else, so JUMP forward to 68DCH to report it.
68C9
GOSUB to the resident @KILL vector at 442CH, which deletes the partly allocated destination file and returns its granules to the free space, so that the disk is left tidy before the operator changes it.
68CC
LD A,(430FH) 3A 0F 43
Load Register A from SFLAG$ at 430FH, the resident system flag byte.
68CF
BIT 5,A CB 6F
Test bit 5 of Register A, which the DOS sets while a job file is running.
68D1
If the NZ FLAG has been set a job is in effect, so JUMP forward to 6901H. There is nobody at the keyboard to change the disk.
68D3
LD HL,601FH 21 1F 60
Point Register Pair HL at the message at 691FH, Disk is full! - Enter new formatted destination disk <ENTER>.
68D6
GOSUB to the flashing prompt loop at 53E0H, which alternates the message against a blank line until the operator presses ENTER or BREAK. The manual describes this prompt as flashing, and this is why.
68D9
OR 01H F6 01
OR Register A with 01H, forcing the NZ FLAG so that the caller knows the file has to be retried on a fresh disk.
68DB
RET C9
RETurn to the caller, which at 671FH takes the retry path.
68DC
JUMP to the disk-error exit at 52A0H with whatever error the write reported.
68DF
PUSH BC C5
PUSH Register Pair BC onto the stack, preserving the Hash Index Table offset the caller is carrying.
68E0
LD DE,521DH 11 1D 52
Point Register Pair DE at 521DH, the source file control block, which is also where the source filespec was built.
68E3
GOSUB to 5327H to mount the source disk, prompting and re-checking the Pack ID if the disk has to be changed.
68E6
POP BC C1
POP Register Pair BC back off the stack.
68E7
RET C9
RETurn to the caller with the source disk in the drive and Register Pair DE addressing its file control block.
68E8
PUSH BC C5
PUSH Register Pair BC onto the stack, preserving the Hash Index Table offset.
68E9
LD DE,523DH 11 3D 52
Point Register Pair DE at 523DH, the destination file control block.
68EC
GOSUB to 5394H to mount the destination disk.
68EF
POP BC C1
POP Register Pair BC back off the stack.
68F0
RET C9
RETurn to the caller with the destination disk in the drive and Register Pair DE addressing its file control block.
68F1
LD D,(IY+09H) FD 56 09
Load Register D with the byte at IY+09H, the directory cylinder of whichever disk is currently mounted.
68F4
LD E,01H 1E 01
Load Register E with 01H, sector 1 of that cylinder, which is the Hash Index Table.
68F6
LD HL,6200H 21 00 62
Point Register Pair HL at 6200H, the buffer the Hash Index Table is kept in for the whole of the scan.
68F9
GOSUB to 546CH, the disk-operation stub for operation 09H, to read it.
68FC
CP 06H FE 06
Compare Register A against 06H, the deleted data address mark status.
68FE
LD A,16H 3E 16
Load Register A with 16H, LDOS error 22, ready in case the read failed.
6900
RET C9
RETurn to the caller with the Z FLAG set on success and the error number in Register A on failure.
6901
LD HL,60D1H 21 D1 60
Point Register Pair HL at the message at 69D1H, Disk is full! - Can't switch while <DO> in effect.
6904
JUMP to the abort entry at 52AFH with that message. The manual states that backups with the query parameter and single-drive backups cannot be done from a job file, and a destination change is the same problem: it needs an operator.

6907H - The File By File Engine Message Block and Tables

The last 321 bytes of the fifth load record are the engine's own text and its three tables. Like the mirror-image engine's message block these travel down with the code, so the engine reaches them 0900H lower than they are shown here. Two of the tables are duplicates of tables the front end also carries, because the front end's copies are destroyed by the block move that brings this engine into place.

6907-6916
DEFM 1DH, "Copying file: ", 03H 1D 43 6F 70 79 69 6E 67 20 66 69 6C 65 3A 20 03
Displayed by 65F4H when the QUERY parameter is not in force, immediately before the filespec. The leading 1DH returns the cursor to the start of the line.
6917-691E
DEFM "Backup ", 03H 42 61 63 6B 75 70 20 03
The lead-in of the query line, displayed by 6545H before the filespec, the modification marker and the date.
691F-695D
DEFM "Disk is full! - Enter new formatted destination disk ", 1DH, 03H 44 69 73 6B 20 69 73 20 66 75 6C 6C 21 20 2D 20 45 6E 74 65 72 20 6E 65 77 20 66 6F 72 6D 61 74 74 65 64 20 64 65 73 74 69 6E 61 74 69 6F 6E 20 64 69 73 6B 20 20 3C 45 4E 54 45 52 3E 1D 03
Displayed by 68D3H through the flashing prompt loop when the destination runs out of space. The trailing 1DH and 03H are what let the loop rewrite the line, which is why the manual describes this prompt as flashing.
695E-699D
DEFM " File is larger than destination capacity - backup is bypassed", 0DH 20 20 46 69 6C 65 20 69 73 20 6C 61 72 67 65 72 20 74 68 61 6E 20 64 65 73 74 69 6E 61 74 69 6F 6E 20 63 61 70 61 63 69 74 79 20 2D 20 62 61 63 6B 75 70 20 69 73 20 62 79 70 61 73 73 65 64 0D
Displayed by 66FBH when a file needs more sectors than the whole destination disk holds. The manual states that BACKUP will not allow a single file to be split across destination disks.
699E-69D0
DEFM "Can't create SYSTEM disk - directory slots in use!", 0DH 43 61 6E 27 74 20 63 72 65 61 74 65 20 53 59 53 54 45 4D 20 64 69 73 6B 20 2D 20 64 69 72 65 63 74 6F 72 79 20 73 6C 6F 74 73 20 69 6E 20 75 73 65 21 0D
Displayed by 639FH when one of the fifteen directory slots the system files must occupy is already held by something else. This is the other half of the Hash Index Table ordering that FORMAT/CMD establishes: FORMAT/CMD creates the first two, BOOT/SYS and DIR/SYS, and BACKUP claims the remaining thirteen for the /SYS overlays, checking all fifteen against one table.
69D1-6A02
DEFM "Disk is full! - Can't switch while in effect", 0DH 44 69 73 6B 20 69 73 20 66 75 6C 6C 21 20 2D 20 43 61 6E 27 74 20 73 77 69 74 63 68 20 77 68 69 6C 65 20 3C 44 4F 3E 20 69 6E 20 65 66 66 65 63 74 0D
Displayed by 6901H when the destination fills while a job file is running. Changing a disk needs an operator, and the manual lists disk swapping among the things that cannot be done from a job.
6A03-6A07
DEFM "} ? ", 03H 7D 20 3F 20 03
The closing half of the query prompt, displayed by 65D1H. The line the engine builds opens with a left brace at 6561H, so the file specification and its date appear inside braces followed by a question mark, which is the form the manual shows.
6A08-6A13
DEFB 1FH, 1CH, 1FH, 1EH, 1FH, 1EH, 1FH, 1FH, 1EH, 1FH, 1EH, 1FH 1F 1C 1F 1E 1F 1E 1F 1F 1E 1F 1E 1F
The maximum number of days in each month, January to December, as 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31. This is the engine's own copy of the table the front end keeps at 7006H; the front end's copy is overwritten by the block move that brings this engine down, so a second one has to travel with it.
6A14-6A37
DEFM "JanFebMarAprMayJunJulAugSepOctNovDec" 4A 61 6E 46 65 62 4D 61 72 41 70 72 4D 61 79 4A 75 6E 4A 75 6C 41 75 67 53 65 70 4F 63 74 4E 6F 76 44 65 63
Twelve three-letter month abbreviations, indexed by 658EH as three times the month less one. This is what turns the four-bit month in a directory record into the middle field of the DD-Mmm-YY date the query prompt shows.
6A38-6A46
DEFB 0A2H, 0C4H, 2EH, 2FH, 2CH, 2DH, 2AH, 2BH, 28H, 29H, 26H, 27H, 27H, 0A7H, 26H A2 C4 2E 2F 2C 2D 2A 2B 28 29 26 27 27 A7 26
The fifteen Hash Index Table bytes that the system files occupy on a SYSTEM disk, walked by the loop at 6396H. The first two are BOOT/SYS and DIR/SYS, whose values are exactly the ones FORMAT/CMD writes when it creates those two records, and the remaining thirteen are the /SYS overlay files. A slot that is free is claimed by writing the expected byte into it; a slot holding any other value means the disk cannot be made into a SYSTEM disk without moving somebody else's file.
6A47-6A47
DEFB 0A6H A6
A sixteenth entry beyond the fifteen the loop at 6394H counts, so it is never read. It is the last byte of the image.

Unloaded Storage
The image resumes at 6C00H. The 440 bytes from 6A48H to 6BFFH are not written by any load record. The block move at 6C5BH copies 0900H bytes rather than the 0748H the engine actually occupies, so most of this gap is carried down with the engine and lands harmlessly above it.

6C00H - The Engine Selector

This is the hinge the whole file turns on. Both backup engines are assembled to run at 5A00H and both are stored somewhere else, so something has to choose one, move it down over the front end and jump to it. That is all this block does, plus the one check that only makes sense at this moment: if the operator asked for a file-by-file backup with no system disk in drive 0, the four overlays that path depends on have to already be resident, and there is a way to find that out without loading anything. Note that the front end enters this block at two different addresses. Entering at 6C00H lets the class flag decide; entering at 6C13H, which is what a backup reconstruct does, forces the file-by-file engine regardless.

6C00
LD A,00H 3E 00
Self-Modifying Code
Load Register A from the operand at 6C01H, the backup-by-class flag that the front end assembled at 5B2DH by ORing together the ten parameter cells that are neither X nor MPW. Zero means nothing specific was asked for and a mirror image is possible.
6C02
OR A B7
OR Register A with itself to set the flags on that value.
6C03
If the NZ FLAG has been set the operator asked for a backup by class, so JUMP forward to 6C13H to set up the file-by-file engine instead.
6C06
LD HL,5E00H 21 00 5E
Point Register Pair HL at 5E00H, where the mirror-image engine was loaded. It is assembled to run at 5A00H, so nothing in it can be executed where it sits.
6C09
LD DE,5A00H 11 00 5A
Point Register Pair DE at 5A00H, which is where the engine is assembled to run and where the front end that has just finished with it currently sits.
6C0C
LD BC,0500H 01 00 05
Load Register Pair BC with 0500H, 1280 bytes. The engine itself is 1033 bytes, from 5E00H to 6208H, so the move also carries the unloaded bytes above it, which costs nothing and saves a byte count that would otherwise have to be maintained.
6C0F
LDIR ED B0
Block move the mirror-image engine down over the front end. From this instruction onwards the code at 5A00H is no longer the command-line parser, and every address in the range 5A00H to 5E08H means something completely different.
6C11
JUMP forward to 6C5DH to record the free-space pointer and enter the engine.
6C13
LD A,(53DCH) 3A DC 53
Load Register A from 53DCH, the source drive number exclusive-ORed with the destination drive number. This address is also the entry the front end jumps to at 5C6FH when it has decided on a backup reconstruct, which is why a reconstruct runs the file-by-file engine whatever the class flag says.
6C16
OR A B7
OR Register A with itself to test that value for zero.
6C17
If the NZ FLAG has been set the two disks are in different drives, so JUMP forward to 6C22H to check the X parameter.
6C19
LD HL,6F26H 21 26 6F
Point Register Pair HL at the message at 6F26H, Single drive BACKUP invalid by files.
6C1C
GOSUB to the resident @DSPLY vector at 4467H to display it. A file-by-file copy needs both directories available at once, which one drive cannot provide, and the manual says so.
6C1F
JUMP to the abort exit at 52ACH to end the backup.
6C22
LD A,(52CAH) 3A CA 52
Load Register A from 52CAH, the X parameter result cell.
6C25
OR A B7
OR Register A with itself to test it for zero.
6C26
If the Z FLAG has been set the X parameter was not used, so the system disk is still in drive 0 and the overlays can be loaded on demand. JUMP forward to 6C52H to move the engine down.
6C28
LD DE,(4BDFH) ED 5B DF 4B
Load Register Pair DE from 4BDFH, a word inside the resident supervisor-call dispatcher at 4BCDH. On a system with no overlays made resident it still holds 4BF5H, the address of the overlay loader itself; the SYSRES= facility replaces it with the base of the resident overlay area.
6C2C
XOR A AF
Clear Register A to zero, which also clears the CARRY FLAG for the subtraction below.
6C2D
LD HL,4BF5H 21 F5 4B
Point Register Pair HL at 4BF5H, the address of the resident overlay loader, as the value to compare against.
6C30
SBC HL,DE ED 52
SUBtract Register Pair DE from Register Pair HL with the CARRY FLAG clear. A result of zero means the dispatcher word is unchanged and nothing has been made resident.
6C32
LD HL,6F4BH 21 4B 6F
Point Register Pair HL at the message at 6F4BH, This backup requires residency of SYS's: 2, 3, 8, & 10., ready for any of the five refusals below.
6C35
If the Z FLAG has been set no overlays are resident, so LOOP BACK to 6C1CH to display that message and abort. With no system disk in drive 0 and no resident overlays there is no way to service an open, a close, an allocate or a kill.
6C37
PUSH DE D5
PUSH Register Pair DE, the base of the resident overlay area, onto the stack.
6C38
POP IX DD E1
POP it back into Register Pair IX, which is the only way to get a sixteen-bit value into that register from another register pair, and which allows the four displaced tests below.
6C3A
LD A,(IX-1BH) DD 7E E5
Load Register A with the byte at IX-1BH, the presence marker for SYS2/SYS, the overlay that services @OPEN and @INIT. The four markers are read at negative displacements because the dispatcher word points at the top of the resident overlay area rather than the bottom.
6C3D
OR A B7
OR Register A with itself to test that marker for zero.
6C3E
If the Z FLAG has been set SYS2/SYS is not resident, so LOOP BACK to 6C1CH to refuse the backup.
6C40
LD A,(IX-19H) DD 7E E7
Load Register A with the byte at IX-19H, the presence marker for SYS3/SYS, the overlay that services @CLOSE.
6C43
OR A B7
OR Register A with itself to test it for zero.
6C44
If the Z FLAG has been set SYS3/SYS is not resident, so LOOP BACK to 6C1CH to refuse.
6C46
LD A,(IX-0FH) DD 7E F1
Load Register A with the byte at IX-0FH, the presence marker for SYS8/SYS, the overlay that allocates file space.
6C49
OR A B7
OR Register A with itself to test it for zero.
6C4A
If the Z FLAG has been set SYS8/SYS is not resident, so LOOP BACK to 6C1CH to refuse.
6C4C
LD A,(IX-0BH) DD 7E F5
Load Register A with the byte at IX-0BH, the presence marker for SYS10/SYS, the overlay that services @KILL.
6C4F
OR A B7
OR Register A with itself to test it for zero.
6C50
If the Z FLAG has been set SYS10/SYS is not resident, so LOOP BACK to 6C1CH to refuse. All four of the overlays the manual names have now been checked.
6C52
LD HL,6300H 21 00 63
Point Register Pair HL at 6300H, where the file-by-file engine was loaded. Like the mirror-image engine it is assembled to run at 5A00H.
6C55
LD DE,5A00H 11 00 5A
Point Register Pair DE at 5A00H, over the front end.
6C58
LD BC,0900H 01 00 09
Load Register Pair BC with 0900H, 2304 bytes. The engine is 1864 bytes, from 6300H to 6A47H, and the move again carries the unloaded bytes above it rather than tracking an exact length.
6C5B
LDIR ED B0
Block move the file-by-file engine down over the front end. From here the code at 5A00H is the class and reconstruct engine.
6C5D
DEC DE 1B
DECrement Register Pair DE by 1. The block move left Register Pair DE one past the last byte written, so this makes it point at the last byte itself.
6C5E
INC D 14
INCrement Register D by 1 and, with the load on the next line, round the address up to the next page boundary. The engine now ends somewhere inside page D, so page D plus one is the first page it does not occupy.
6C5F
LD E,00H 1E 00
Load Register E with 00H, completing the rounding so that Register Pair DE is a page-aligned address just above the engine.
6C61
LD (5285H),DE ED 53 85 52
Store Register Pair DE at 5285H, the free-space pointer. Everything from here up to HIGH$ at 4049H is the work space, and both engines measure every buffer they need from this word.
6C65
JUMP to 5A00H to enter whichever engine was moved down. This is the only transfer of control into an engine, and it is why both engines have the same entry address.

Two Entry Points, One Block
6C00H tests the backup-by-class flag that the front end assembled and takes the mirror-image path when it is clear. 6C13H is the entry a backup reconstruct uses, reached by the jump at 5C6FH after the front end has found the two disks incompatible. Everything from 6C13H onwards is therefore common to a backup by class and a backup reconstruct, which is precisely the manual's statement that the two function identically and differ only in who asked for them.

6C68H - The Two Supervisor Request Stubs

BACKUP issues only two supervisor requests in the whole file and they sit here, nose to tail, as two three-byte stubs with no RET between or after them. That is deliberate and it is correct: the dispatcher at 4BCDH discards the return address the restart pushed, so its own RET goes back to whoever called the stub. The same idiom appears in FORMAT/CMD at 7277H and in the SYS6/SYS COPY overlay at 57C5H, and it is why a three-byte sequence can be a complete subroutine.

6C68
GOSUB to 6C71H to collect eight characters, either from the command line address in Register Pair DE or, when that is zero, from the keyboard after displaying the prompt at Register Pair HL. Execution then falls into the supervisor stub below, so one call both collects a password and hashes it.
6C6B
LD A,0E4H 3E E4
Load Register A with 0E4H, the request code for the password hash serviced by SYS2/SYS as its sub-function 60H. Register Pair DE points at the eight-byte field the routine above filled.
6C6D
RST 28 EF
Restart at 0028H, the supervisor call. Tail Call Stub
There is no RET after this instruction and none is needed. The dispatcher at 4BCDH exchanges the top of the stack into Register Pair HL and immediately overwrites Register Pair HL, discarding the address the restart pushed, so its own RET returns to whoever called this stub. Three bytes are therefore a complete subroutine, and it returns the sixteen-bit hash in Register Pair HL.
6C6E
LD A,84H 3E 84
Load Register A with 84H, the request code that selects SYS2/SYS with sub-function 00H. Called by the front end at 5B46H.
6C70
RST 28 EF
Restart at 0028H, the supervisor call. Tail Call Stub
SYS2's entry dispatcher recognises sub-functions 10H, 20H, 40H, 50H and 60H and returns without doing anything for 00H, so this request performs no service at all. The only effect is that the overlay is read into 4E00H and becomes resident, which is the whole purpose: the open and initialise services are then in memory before the operator starts swapping disks.

6C71H - Collect Eight Characters

One routine fills the eight-byte field that a password hash is taken over, from the command line when the MPW parameter supplied one and from the keyboard when it did not. Either way the field is blank filled to exactly eight characters and folded to upper case, because the hash is always computed over eight bytes and eight blanks are what an unpassworded disk hashes to.

6C71
LD A,D 7A
Load Register A from Register D, the high byte of the address the caller passed, which is the address of the string on the command line.
6C72
OR E B3
OR Register A with Register E, the low byte, so that the whole address is tested for zero at once.
6C73
If the Z FLAG has been set the caller has no command-line string, so JUMP forward to 6C8FH to prompt the operator instead.
6C75
LD HL,5900H 21 00 59
Point Register Pair HL at 5900H, the general sector buffer, which is used here as the eight-byte field the hash will be taken over.
6C78
PUSH HL E5
PUSH the buffer address onto the stack so that it can be recovered after the field has been filled.
6C79
LD B,08H 06 08
Load Register B with 08H, the width of a password field.
6C7B
LD A,(DE) 1A
Fetch a character of the command-line string from the address in Register Pair DE into Register A.
6C7C
CP 0DH FE 0D
Compare Register A against 0DH, a carriage return, which ends a command line.
6C7E
If the Z FLAG has been set the string ended, so JUMP forward to 6CA9H to blank fill the rest of the field.
6C80
CP 2CH FE 2C
Compare Register A against 2CH, a comma, which separates one parameter from the next.
6C82
If the Z FLAG has been set the parameter ended, so JUMP forward to 6CA9H to blank fill.
6C84
CP 22H FE 22
Compare Register A against 22H, a quotation mark, which closes a quoted parameter value.
6C86
If the Z FLAG has been set the quoted string ended, so JUMP forward to 6CA9H to blank fill.
6C88
INC DE 13
INCrement Register Pair DE by 1 to step to the next character of the command line.
6C89
LD (HL),A 77
Store Register A, the accepted character, at the address in Register Pair HL, into the eight-byte field.
6C8A
INC HL 23
INCrement Register Pair HL by 1 to step to the next position in the field.
6C8B
DECrement Register B by 1 and LOOP BACK to 6C7BH while it is not zero, taking at most eight characters.
6C8D
JUMP forward to 6CAEH. The field is full, so there is nothing to blank fill.
6C8F
GOSUB to the resident @DSPLY vector at 4467H to display the prompt at Register Pair HL, which for the master password is Master password ? at 6FD6H.
6C92
LD B,08H 06 08
Load Register B with 08H, so that the line-input routine accepts at most eight characters.
6C94
LD HL,5900H 21 00 59
Point Register Pair HL at 5900H, the general sector buffer, as the reply buffer.
6C97
PUSH HL E5
PUSH the buffer address onto the stack so that it can be recovered below.
6C98
GOSUB to the ROM line-input routine at 0040H, which collects the reply into the buffer, returns with Register Pair HL still pointing at the buffer and Register B holding the number of characters the operator actually entered, and sets the CARRY FLAG if BREAK was pressed.
6C9B
If the CARRY FLAG has been set the operator pressed BREAK, so JUMP to the abort exit at 52ACH.
6C9E
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, so that Register Pair DE holds the buffer address the line-input routine returned.
6C9F
LD H,00H 26 00
Load Register H with 00H, starting to build the number of characters actually typed.
6CA1
LD L,B 68
Load Register L with Register B, the number of characters entered, so that Register Pair HL holds that count as a sixteen-bit value.
6CA2
ADD HL,DE 19
ADD Register Pair DE, the buffer address, to Register Pair HL, the count, so that Register Pair HL points at the first position the operator did not fill. That is where the blank fill has to start.
6CA3
LD A,08H 3E 08
Load Register A with 08H, the full width of the field.
6CA5
SUB B 90
SUBtract Register B, the number of characters entered, from Register A, giving the number of positions still to be blank filled.
6CA6
If the Z FLAG has been set the operator filled all eight positions, so there is nothing to blank fill. JUMP forward to 6CAEH.
6CA8
LD B,A 47
Load Register B with Register A, the number of positions still to fill, as the counter for the loop below. Note that the command-line path at 6C7EH reaches that same loop with Register B already holding the count it needs, so one fill loop serves both ways of collecting the field.
6CA9
LD (HL),20H 36 20
Store 20H, a blank, at the address in Register Pair HL, filling one unused position of the field.
6CAB
INC HL 23
INCrement Register Pair HL by 1 to step to the next position.
6CAC
DECrement Register B by 1 and LOOP BACK to 6CA9H while it is not zero, so that the whole field is blank filled to eight characters. A password is always hashed as exactly eight bytes, which is why eight blanks hash to 4296H and mean no password at all.
6CAE
POP HL E1
POP the field address back into Register Pair HL.
6CAF
PUSH HL E5
PUSH it again, because the case-folding loop below advances Register Pair HL and the caller needs the address once more.
6CB0
LD B,08H 06 08
Load Register B with 08H, the eight bytes of the field.
6CB2
LD A,(HL) 7E
Fetch a byte of the field from the address in Register Pair HL into Register A.
6CB3
CP 61H FE 61
Compare Register A against 61H, the lower-case letter a.
6CB5
If the CARRY FLAG has been set the character is already upper case or is not a letter, so JUMP forward to 6CBAH to leave it alone.
6CB7
RES 5,A CB AF
RESet bit 5 of Register A, folding the lower-case letter to upper case so that the password is hashed in the form the disk stores.
6CB9
LD (HL),A 77
Store the folded character back at the address in Register Pair HL.
6CBA
INC HL 23
INCrement Register Pair HL by 1 to step to the next byte of the field.
6CBB
DECrement Register B by 1 and LOOP BACK to 6CB2H while it is not zero, folding all eight bytes.
6CBD
POP DE D1
POP the field address into Register Pair DE, which is where the password hash service expects to find the eight-byte field.
6CBE
RET C9
RETurn to the caller, which for the entry at 6C68H means falling straight into the supervisor stub that hashes the field.

6CBFH - Read a Drive's Geometry

Before either engine can do anything it has to know what kind of disk is in each drive, and the disk itself is the only authority. This routine proves the unit is fitted, gets the head to a known cylinder, waits out a full index-hole revolution to prove the disk is actually turning, reads the Granule Allocation Table, and unpacks the configuration word into the Drive Control Table entry that Register Pair IY points at. Two of the bits it plants are worth naming: bit 7 of the second specification byte becomes the marker for a disk whose cylinder 0 is single density while the rest is double, and bit 5 becomes the double-sided flag. It also writes the resident per-drive extended-dating flag, and it does that with an instruction it computes at run time.

6CBF
LD A,(53D5H) 3A D5 53
Load Register A from 53D5H, the current drive cell.
6CC2
LD C,A 4F
Load Register C with Register A, keeping the drive cell for the dating-flag calculation at 6D12H, which needs the drive number in its low three bits.
6CC3
LD A,(IY+00H) FD 7E 00
Load Register A with the byte at IY+00H, the first byte of this drive's Drive Control Table entry. On a fitted drive that is 0C3H, the opcode of the jump into the resident floppy driver; on an absent drive it is 0C9H, a RET.
6CC6
SUB 0C3H D6 C3
SUBtract 0C3H from Register A. A result of zero means the drive is fitted.
6CC8
RET NZ C0
RETurn if the NZ FLAG has been set. The unit is not fitted, and Register A holds 06H, the difference between a RET and a jump, which the caller sees as a failure.
6CC9
PUSH HL E5
PUSH Register Pair HL onto the stack to preserve the caller's pointer.
6CCA
PUSH DE D5
PUSH Register Pair DE onto the stack for the same reason.
6CCB
LD A,(IY+06H) FD 7E 06
Load Register A with the byte at IY+06H, the highest cylinder on this drive as the Drive Control Table currently records it.
6CCE
CP (IY+05H) FD BE 05
Compare Register A against the byte at IY+05H, the cylinder the head is currently on. If the head is beyond the highest cylinder, the CARRY FLAG is set.
6CD1
If the NO CARRY FLAG has been set the head is within range, so JUMP forward to 6CDAH and seek directly.
6CD4
GOSUB to 545CH, the disk-operation stub for operation 04H, to restore the head to cylinder 0. The Drive Control Table entry disagreed with itself, which happens when the entry has not yet been filled in for this disk.
6CD7
If the NZ FLAG has been set the restore failed, so JUMP forward to 6D5CH to return the drive-not-ready failure.
6CDA
LD D,(IY+05H) FD 56 05
Load Register D with the byte at IY+05H, the cylinder the head is on, as the cylinder to seek to. Seeking to where the head already is is what forces the controller to load its track register from the Drive Control Table.
6CDD
LD E,00H 1E 00
Load Register E with 00H, sector 0, which the seek routine uses to resolve which side of the disk is wanted.
6CDF
GOSUB to the resident SEEK primitive at 475EH. FORMAT/CMD never uses the resident primitives at all; BACKUP uses SEEK and RSELCT here because it needs the controller in a known state before it times the index hole.
6CE2
EI FB
Enable interrupts. The clock task has to run for the timing loop below, which measures a disk revolution against TIMER$ at 4040H.
6CE3
GOSUB to the resident RSELCT primitive at 4759H, which re-selects the drive and returns the floppy disk controller's status in Register A.
6CE6
BIT 4,(IY+04H) FD CB 04 66
Test bit 4 of the byte at IY+04H, which marks a unit that is not to be touched. FDUBL/CMD sets it on any drive its own driver is not to hook.
6CEA
If the NZ FLAG has been set this unit is not to be timed, so JUMP forward to 6D01H and read its Granule Allocation Table without waiting for the index hole.
6CEC
LD A,(4040H) 3A 40 40
Load Register A from TIMER$ at 4040H, the resident tick counter the clock interrupt increments.
6CEF
ADD A,14H C6 14
ADD 14H, twenty ticks, to Register A, giving the deadline by which the drive must have shown an index hole.
6CF1
LD D,A 57
Load Register D with Register A, the deadline, where the polling routine at 6D4FH expects it.
6CF2
GOSUB to 6D4FH, which reads the controller status and returns with the Z FLAG reflecting the index-hole bit, or discards its own return address if the deadline passes.
6CF5
If the NZ FLAG has been set the index hole is still under the head, so LOOP BACK to 6CF2H to wait for it to pass. The three loops here wait for the end of one hole, then for the start of the next, then for its end, which is a full revolution and proves the disk is turning.
6CF7
GOSUB to 6D4FH again, now waiting for the next index hole to arrive.
6CFA
If the Z FLAG has been set no hole is under the head yet, so LOOP BACK to 6CF7H to keep waiting.
6CFC
GOSUB to 6D4FH a third time, waiting for that hole to pass.
6CFF
If the NZ FLAG has been set the hole is still there, so LOOP BACK to 6CFCH. A full revolution has now been observed and the drive is certainly turning.
6D01
PUSH AF F5
PUSH Register A and the flags onto the stack. Register A holds the controller status from the last selection, and its write-protect bit is wanted again at 6D45H after the Granule Allocation Table has been read.
6D02
GOSUB to the resident DIRCYL routine at 4B65H, which returns the directory cylinder for the current drive.
6D05
LD HL,4200H 21 00 42
Point Register Pair HL at SBUFF$ 4200H, the resident shared sector buffer. The Granule Allocation Table is read there rather than into one of this file's own buffers because the resident directory routine expects it.
6D08
LD E,L 5D
Load Register E with Register L, which is 00H, sector 0 of the directory cylinder, which is the Granule Allocation Table.
6D09
GOSUB to the resident RDSSEC routine at 4B45H, which reads a system sector and accepts the deleted data address mark that every directory sector carries.
6D0C
If the NZ FLAG has been set the read failed, so JUMP forward to 6D5BH to discard the saved status and return the failure.
6D0E
LD DE,(42CCH) ED 5B CC 42
Load Register Pair DE from 42CCH, the configuration word at offset CCH of the Granule Allocation Table now sitting in the resident buffer. Register E is the low byte, the cylinder count less 35, and Register D is the high byte, which carries the density, sided, dual-density and extended-dating flags.
6D12
LD A,C 79
Load Register A from Register C, the drive cell saved at 6CC2H, whose low three bits are the drive number.
6D13
RLCA 07
Rotate Register A left circularly, the first of three shifts that move the drive number from bits 2 to 0 up into bits 5 to 3.
6D14
RLCA 07
Rotate Register A left circularly again.
6D15
RLCA 07
Rotate Register A left circularly a third time. The drive number now occupies bits 5 to 3, which is exactly the field a CB-prefixed bit instruction uses to name a bit.
6D16
OR 0C6H F6 C6
OR Register A with 0C6H. That is the second byte of SET 0,(HL), and adding the drive number in bits 5 to 3 turns it into SET n,(HL) for drive n. It also masks off the two marker bits that the drive cell carries in its top half, because 0C6H already has bits 7, 6, 2 and 1 set.
6D18
BIT 3,D CB 5A
Test bit 3 of Register D, the high byte of the configuration word. That bit marks a disk already in the x.3 extended date format.
6D1A
If the NZ FLAG has been set the disk is in the x.3 format, so JUMP forward to 6D1EH leaving the instruction as a SET.
6D1C
XOR 40H EE 40
Exclusive-OR Register A with 40H. That single bit is the difference between the SET and RESet forms of a CB-prefixed bit instruction, so the instruction being built becomes RES n,(HL) instead.
6D1E
LD (6D25H),A 32 25 6D
Self-Modifying Code
Store the computed byte at 6D25H, which is the second byte of the CB-prefixed instruction at 6D24H. One instruction body therefore serves all eight drives in both directions.
6D21
LD HL,475DH 21 5D 47
Point Register Pair HL at 475DH, the resident per-drive extended-dating flag byte, which lives in one of the spare bytes of the disk primitive table and carries one bit for each drive.
6D24
RLC B CB 00
Self-Modifying Code
Execute the instruction just built, which sets bit [drive] of 475DH when this pack is in the x.3 extended date format and clears it when it is not. This is the writer's side of the flag that DATECONV/CMD exists to make meaningful, and the test at 6D61H is the reader's side.
6D26
LD A,22H 3E 22
Load Register A with 22H, thirty-four. The Granule Allocation Table records the cylinder count less thirty-five, and the highest cylinder number is one less than the count, so thirty-four is what has to be added back.
6D28
ADD A,E 83
ADD Register E, the low byte of the configuration word, to Register A, giving the highest cylinder number on this disk.
6D29
LD (IY+06H),A FD 77 06
Store Register A as the byte at IY+06H, the highest cylinder field of this drive's Drive Control Table entry, so that every later seek is bounded correctly.
6D2C
RES 7,(IY+04H) FD CB 04 BE
RESet bit 7 of the byte at IY+04H, clearing the dual-density marker before it is set from the disk.
6D30
BIT 4,D CB 62
Test bit 4 of Register D, the high byte of the configuration word, which FORMAT/CMD sets on a disk whose cylinder 0 is single density while the rest is double.
6D32
If the Z FLAG has been set the disk is not of that kind, so JUMP forward to 6D38H leaving the marker clear.
6D34
SET 7,(IY+04H) FD CB 04 FE
SET bit 7 of the byte at IY+04H. That bit of the Drive Control Table second specification byte is the dual-density marker, and the mirror-image engine tests it at 5A59H and again in each of its three passes to decide whether cylinder 0 has to be handled differently from the rest of the disk.
6D38
RES 5,(IY+04H) FD CB 04 AE
RESet bit 5 of the byte at IY+04H, clearing the double-sided flag before it is set from the disk.
6D3C
BIT 5,D CB 6A
Test bit 5 of Register D, the high byte of the configuration word, which records that the disk was formatted on both sides.
6D3E
If the Z FLAG has been set the disk is single sided, so JUMP forward to 6D44H leaving the flag clear.
6D40
SET 5,(IY+04H) FD CB 04 EE
SET bit 5 of the byte at IY+04H, the double-sided flag, which the resident seek routine reads when it resolves a sector number into a side and a physical sector.
6D44
POP AF F1
POP the controller status saved at 6D01H back into Register A.
6D45
RLCA 07
Rotate Register A left circularly, moving the status bit that reports write protection into bit 7.
6D46
OR (IY+03H) FD B6 03
OR Register A with the byte at IY+03H, this drive's specification byte, whose own bit 7 is the write-protect flag, so that either indication is enough.
6D49
AND 80H E6 80
AND Register A with 80H to keep only the combined write-protect bit and discard everything else.
6D4B
ADD A,A 87
ADD Register A to itself. That shifts the write-protect bit out into the CARRY FLAG and leaves Register A zero, so the caller sees the Z FLAG set for success and the CARRY FLAG set when the disk cannot be written.
6D4C
POP DE D1
POP the caller's value back into Register Pair DE.
6D4D
POP HL E1
POP the caller's pointer back into Register Pair HL.
6D4E
RET C9
RETurn to the caller with the Drive Control Table entry filled in from the disk itself.

6D4FH - Poll for the Index Hole

A short routine with an unusual exit. It reads the controller status and reports the index-hole bit, but if the deadline in Register D has arrived it discards its own return address, which abandons the caller's waiting loop as well as itself. That is what turns an empty drive into a device-not-available error instead of a hang.

6D4F
LD A,(4040H) 3A 40 40
Load Register A from TIMER$ at 4040H, the resident tick counter.
6D52
CP D BA
Compare Register A against Register D, the deadline the caller computed at 6CEFH as the current tick plus twenty.
6D53
If the Z FLAG has been set the deadline has arrived and the drive has not shown an index hole, so JUMP forward to 6D5BH.
6D55
GOSUB to the resident RSELCT primitive at 4759H, which re-selects the drive and returns the floppy disk controller status in Register A.
6D58
BIT 1,A CB 4F
Test bit 1 of Register A, which on the WD1771 is the index-hole line while a Type I command is in force.
6D5A
RET C9
RETurn to the caller with the Z FLAG reflecting whether the index hole is under the head.
6D5B
POP AF F1
POP the return address of the polling loop off the stack and discard it. The deadline has passed, so this abandons the caller's loop as well as this routine, which is how a drive with no disk in it produces a failure rather than hanging.
6D5C
LD A,08H 3E 08
Load Register A with 08H, LDOS error 8, device not available.
6D5E
OR A B7
OR Register A with itself to clear the Z FLAG, so that the caller sees the failure. The CARRY FLAG is cleared as well, which distinguishes this from the write-protected return.
6D5F
JUMP back to 6D4CH to restore the caller's registers and return with the failure in Register A.

6D61H - Test the Extended Dating Flag

The reader's half of the computed-instruction pair. The drive number is turned into the second byte of a bit-test instruction, planted, and executed against the resident flag byte, so that one instruction body answers the question for any of eight drives.

6D61
AND 07H E6 07
AND Register A with 07H to keep only the drive number from the drive cell the caller passed.
6D63
LD B,A 47
Load Register B with Register A, the drive number, as the loop counter.
6D64
INC B 04
INCrement Register B by 1, so that the loop below runs at least once and adds eight for drive 0.
6D65
LD A,3FH 3E 3F
Load Register A with 3FH. That is eight less than 47H, which is the second byte of BIT 0,A, so that after one pass of the loop Register A holds the instruction for drive 0.
6D67
ADD A,08H C6 08
ADD 08H to Register A. Eight is the step between one bit number and the next in a CB-prefixed bit instruction.
6D69
DECrement Register B by 1 and LOOP BACK to 6D67H while it is not zero, so that Register A ends as the second byte of BIT n,A for drive n.
6D6B
LD (6D72H),A 32 72 6D
Self-Modifying Code
Store the computed byte at 6D72H, the second byte of the CB-prefixed instruction at 6D71H.
6D6E
LD A,(475DH) 3A 5D 47
Load Register A from 475DH, the resident per-drive extended-dating flag byte, which the routine at 6D24H writes.
6D71
RLC B CB 00
Self-Modifying Code
Execute the instruction just built, testing bit [drive] of the flag byte. The Z FLAG comes back set when the bit is clear, meaning this pack is in the old date format, and clear when the pack is in the x.3 extended format.
6D73
RET C9
RETurn to the caller with the Z FLAG carrying the answer. The front end asks this question twice, at 5BBAH for the source and at 5C42H for the destination, and the two answers together drive the date-format conversion flag at 5212H.

6D74H - The Parameter Table and the Front End Message Block

The last 686 bytes of the image are data. The parameter table comes first, in the standard LDOS @PARAM form: a six-character blank-padded keyword followed by the two-byte address of the cell the value is to be written into, repeated until a zero byte ends the list. A long form and its abbreviation are separate entries pointing at the same cell, and the cells themselves are scattered - some are in the small data run at 5200H and some are the operand fields of instructions out in the front end. Everything after the table is text belonging to the front end, which is why it survives in place: unlike the two engines the front end is never moved.

6D74-6D7B
DEFM "MPW " / DEFW 5C94H 4D 50 57 20 20 20 94 5C
MPW
The source disk's master password. The cell is the operand of the load at 5C93H, so a password given here is found by the check at 5C89H and no prompt is issued.
6D7C-6D83
DEFM "SYS " / DEFW 521BH 53 59 53 20 20 20 1B 52
SYS
Include system files. Folded into the attribute selection byte at 5AD9H and, at 6354H, taken as the instruction to reserve the fifteen system directory slots on the destination.
6D84-6D8B
DEFM "S " / DEFW 521BH 53 20 20 20 20 20 1B 52
S
The abbreviation for SYS, pointing at the same cell.
6D8C-6D93
DEFM "INV " / DEFW 5AE4H 49 4E 56 20 20 20 E4 5A
INV
Include files that are invisible in the directory. The cell is the operand of the load at 5AE3H.
6D94-6D9B
DEFM "I " / DEFW 5AE4H 49 20 20 20 20 20 E4 5A
I
The abbreviation for INV, pointing at the same cell.
6D9C-6DA3
DEFM "MOD " / DEFW 5217H 4D 4F 44 20 20 20 17 52
MOD
Copy only files modified since the last backup, tested against bit 6 of directory offset 01H at 6440H.
6DA4-6DAB
DEFM "M " / DEFW 5217H 4D 20 20 20 20 20 17 52
M
The abbreviation for MOD, pointing at the same cell.
6DAC-6DB3
DEFM "QUERY " / DEFW 5219H 51 55 45 52 59 20 19 52
QUERY
Ask before each file is copied. The high half of this cell is also cleared at 65EFH when the operator answers the prompt with C, which turns querying off for the rest of the run.
6DB4-6DBB
DEFM "Q " / DEFW 5219H 51 20 20 20 20 20 19 52
Q
The abbreviation for QUERY, pointing at the same cell.
6DBC-6DC3
DEFM "X " / DEFW 52CAH 58 20 20 20 20 20 CA 52
X
Allow the backup to run with no system disk in drive 0. Tested at 52C9H and again at 6C22H, where the four overlays the file-by-file path needs are checked for residency.
6DC4-6DCB
DEFM "DATE " / DEFW 5AA2H 44 41 54 45 20 20 A2 5A
DATE
Select files by modification date. The cell is the operand of the load at 5AA1H and holds the address of the quoted string, which the parser at 5CCCH takes apart.
6DCC-6DD3
DEFM "D " / DEFW 5AA2H 44 20 20 20 20 20 A2 5A
D
The abbreviation for DATE, pointing at the same cell.
6DD4-6DDB
DEFM "NEW " / DEFW 5213H 4E 45 57 20 20 20 13 52
NEW
Copy only files that are not already on the destination, decided at 651DH by whether the open reports file not found.
6DDC-6DE3
DEFM "N " / DEFW 5213H 4E 20 20 20 20 20 13 52
N
The abbreviation for NEW, pointing at the same cell.
6DE4-6DEB
DEFM "OLD " / DEFW 5215H 4F 4C 44 20 20 20 15 52
OLD
Copy only files that already exist on the destination, the opposite test on the same open.
6DEC-6DF3
DEFM "O " / DEFW 5215H 4F 20 20 20 20 20 15 52
O
The abbreviation for OLD, pointing at the same cell.
6DF4
DEFB 00H 00
The zero byte that ends the parameter table. The resident @PARAM service at 4476H stops here, so a keyword the operator typed that matched nothing produces the parameter error the front end reports at 5A87H.
6DF5-6E27
DEFM "Single drive backup invalid during processing", 0DH 53 69 6E 67 6C 65 20 64 72 69 76 65 20 62 61 63 6B 75 70 20 69 6E 76 61 6C 69 64 20 64 75 72 69 6E 67 20 3C 44 4F 3E 20 70 72 6F 63 65 73 73 69 6E 67 0D
Displayed by 5A9EH when the two drivespecs name the same unit and a job file is running. The manual lists single drive backups among the things that cannot be done from a job, because there is nobody at the keyboard to swap the disks.
6E28-6E57
DEFM "Destination disk not formatted - Backup aborted", 0DH 44 65 73 74 69 6E 61 74 69 6F 6E 20 64 69 73 6B 20 6E 6F 74 20 66 6F 72 6D 61 74 74 65 64 20 2D 20 42 61 63 6B 75 70 20 61 62 6F 72 74 65 64 0D
Displayed by 5C1EH when cylinder 0 sector 0 of the destination cannot even be verified. The manual states that destination disks must be formatted before the backup begins.
6E58-6EBD
DEFM 0AH, "BACKUP - LDOS Disk Backup Utility - Version 5.3.1", 0AH, "Copyright 1991 MISOSYS, Inc., All rights reserved", 0AH, 0DH 0A 42 41 43 4B 55 50 20 2D 20 4C 44 4F 53 20 44 69 73 6B 20 42 61 63 6B 75 70 20 55 74 69 6C 69 74 79 20 2D 20 56 65 72 73 69 6F 6E 20 35 2E 33 2E 31 0A 43 6F 70 79 72 69 67 68 74 20 31 39 39 31 20 4D 49 53 4F 53 59 53 2C 20 49 6E 63 2E 2C 20 41 6C 6C 20 72 69 67 68 74 73 20 72 65 73 65 72 76 65 64 0A 0D
The sign-on, displayed by 5A04H as the first thing the utility does. Its last sixty-eight bytes are byte for byte the same as the tail of the FORMAT/CMD sign-on, which together with a thirty-byte run in the clock message is the only text the two files share and the only thing they have in common at the byte level.
6EBE-6EDB
DEFM "Source drive number ? ", 03H 53 6F 75 72 63 65 20 64 72 69 76 65 20 6E 75 6D 62 65 72 20 3F 20 20 20 20 20 20 20 20 03
Displayed by 5A34H when the command line carries no source drivespec. It is padded with blanks and terminated by 03H so that the reply is typed over the padding on the same line.
6EDC-6EF9
DEFM "Destination drive number ? ", 03H 44 65 73 74 69 6E 61 74 69 6F 6E 20 64 72 69 76 65 20 6E 75 6D 62 65 72 20 3F 20 20 20 03
Displayed by 5A5FH when the command line carries no destination drivespec.
6EFA-6F0D
DEFM "No date established", 0DH 4E 6F 20 64 61 74 65 20 65 73 74 61 62 6C 69 73 68 65 64 0D
Displayed by 5B3DH when the resident month cell at 4046H is still zero, warning that whatever date the backup stamps on the copy will be meaningless.
6F0E-6F25
DEFM "Backup by class invoked", 0DH 42 61 63 6B 75 70 20 62 79 20 63 6C 61 73 73 20 69 6E 76 6F 6B 65 64 0D
Displayed by 5B33H when the chain of ORs at 5AF9H finds that the operator supplied a partspec or any parameter other than X or MPW.
6F26-6F4A
DEFM "Single drive BACKUP invalid by files", 0DH 53 69 6E 67 6C 65 20 64 72 69 76 65 20 42 41 43 4B 55 50 20 69 6E 76 61 6C 69 64 20 62 79 20 66 69 6C 65 73 0D
Displayed by 6C19H when a file-by-file backup is called for and both drivespecs name the same unit. A file-by-file copy needs both directories available at once.
6F4B-6F82
DEFM "This backup requires residency of SYS's: 2, 3, 8, & 10.", 0DH 54 68 69 73 20 62 61 63 6B 75 70 20 72 65 71 75 69 72 65 73 20 72 65 73 69 64 65 6E 63 79 20 6F 66 20 53 59 53 27 73 3A 20 32 2C 20 33 2C 20 38 2C 20 26 20 31 30 2E 0D
Displayed by 6C1CH when the X parameter is given but the residency test at 6C22H finds that the four overlays the file-by-file path depends on have not been made resident with the SYSTEM (SYSRES=) command.
6F83-6F9D
DEFM "Backup-reconstruct invoked", 0DH 42 61 63 6B 75 70 2D 72 65 63 6F 6E 73 74 72 75 63 74 20 69 6E 76 6F 6B 65 64 0D
Displayed by 5C69H when the two disks differ in density, sides or granules per track, or when the destination is a rigid drive. The manual explains that a reconstruct and a backup by class function identically and differ only in who asked for them.
6F9E-6FD5
DEFM "Cylinder count differs - Attempt mirror-image backup ? ", 03H 43 79 6C 69 6E 64 65 72 20 63 6F 75 6E 74 20 64 69 66 66 65 72 73 20 2D 20 41 74 74 65 6D 70 74 20 6D 69 72 72 6F 72 2D 69 6D 61 67 65 20 62 61 63 6B 75 70 20 3F 20 03
Displayed by 5C72H when the two disks match in every respect except the number of cylinders. This is the one difference the operator is allowed to overrule.
6FD6-6FED
DEFM "Master password ? ", 03H 4D 61 73 74 65 72 20 70 61 73 73 77 6F 72 64 20 3F 20 20 20 20 20 20 03
Displayed by 6C8FH on behalf of the check at 5C89H, when the source disk carries a master password other than the default and the MPW parameter did not supply it.
6FEE-7005
DEFM "Invalid master password", 0DH 49 6E 76 61 6C 69 64 20 6D 61 73 74 65 72 20 70 61 73 73 77 6F 72 64 0D
Displayed by 5CABH when the reply hashes neither to the disk's own master-password hash nor to the override constant. There is no retry.
7006-7011
DEFB 1FH, 1CH, 1FH, 1EH, 1FH, 1EH, 1FH, 1FH, 1EH, 1FH, 1EH, 1FH 1F 1C 1F 1E 1F 1E 1F 1F 1E 1F 1E 1F
The maximum number of days in each month, January to December, as 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31. The DATE parser at 5CF7H checks the day against the entry for the month it has just read, and at 5CE4H it increments the February entry in place when the year is divisible by four. That change is never undone, which is harmless because at most two dates are parsed in one run of the utility. The file-by-file engine carries its own copy at 6A08H, because this one is destroyed by the block move that brings the engine down.
7012-7021
DEFM "Bad date format", 0DH 42 61 64 20 64 61 74 65 20 66 6F 72 6D 61 74 0D
Displayed by 5D1AH, which every failure in the DATE parser reaches: a missing separator, a field that is not two digits, a month outside 1 to 12, or a day the month does not have. A malformed date ends the backup rather than being ignored. This is the last byte of the image.