TRS-80 DOS - LDOS 5.3.1 for the Model I - FORMAT/CMD Disassembled

Page Customization

Summary:

LDOS 5.3.1 FORMAT/CMD Disassembly - Diskette Formatter (Model I)

FORMAT/CMD is the LDOS utility that lays down cylinders, sectors and a directory on a diskette so that the operating system can read and write it. The command syntax is FORMAT :d (parm,parm,...) with the parameters ABS, CYL, DIR, MPW, NAME, QUERY, SDEN, DDEN, SIDES, STEP, SYSTEM and WAIT. FORMAT produces a DATA disk: only two directory slots are reserved, where a SYSTEM disk configured by BACKUP reserves fifteen in total, those same two plus thirteen more for SYS0/SYS through SYS12/SYS. It is FORMAT that creates the two files DOS maintains itself and never copies, BOOT/SYS on cylinder 0 and DIR/SYS on the directory cylinder.

The program is a single LDOS /CMD load module preceded only by a type-1FH copyright record, Copyright 1991 MISOSYS, Inc., All rights reserved. Twenty-three type-01 load records place 4842 bytes into memory between 6000H and 7673H, and a type-02 record sets the transfer address to 6E00H. Six regions inside that span are deliberately left unloaded because they are working storage, and one of them, 6D00H-6DFFH, is the sector-identifier pointer table the track expander builds. The image was rebuilt from the load records and compared byte for byte against the listing on this page: 4842 of 4842 bytes covered, zero mismatches.

FORMAT runs in two phases that occupy the same memory. The transfer address 6E00H enters the FRONT END, which reads the system configuration sector, parses the command line through @PARAM, prompts for anything still missing, checks the target diskette and then does JP 6000H into the FORMAT ENGINE. The engine expands a packed track-image template into a buffer that begins at 6E00H, on top of the front end that has just finished running, and hands that buffer to the WD1771 Write Track command. This is why the front end has to test HIGH$ before it starts: the buffer it is about to be overwritten by can be as long as 10673 bytes.

FORMAT does not use the resident disk primitives at 4750H-478DH for the formatting pass. It takes a ten-byte copy of the target drive's Drive Code Table entry into 6564H, points Register Pair IY at that copy, and reaches the drive's own driver with JP (IY) through the JP instruction that occupies DCT+00H to DCT+02H. Working on a private copy lets it alter the side-select bit, the current cylinder and the geometry bytes without disturbing the DOS view of the drive. The head is positioned only by Restore and Step In; no seek is ever issued during the format pass.

Only two RST 28H supervisor requests are issued, both from three-byte stubs at 7277H and 727AH. Because the RST 28H dispatcher in SYS0 discards the return address the RST instruction pushed, a stub of the form LD A,code followed by RST 28H returns to whoever CALLed the stub, so no RET is needed and none is present. SYS6 uses the same construction, packing three such stubs nose to tail at 57C5H, 57C8H and 57CBH and CALLing each of them separately.

The first of the two requests, code 84H at 7277H, carries no sub-function. The overlay loader turns a request code into a SYS number with the low nibble less two, so 84H selects SYS2, and SYS2's own entry dispatcher at 4E00H masks the code with 70H and compares the result against 10H for @OPEN, 20H for @INIT, 40H for @CKDRV, 50H for the filename hash and 60H for the password hash. 84H masks to 00H, matches none of them and falls onto the RET at 4E16H. The request therefore does nothing except make SYS2 the resident overlay. That matters because the loader reuses an overlay whose low nibble already matches the one being asked for, so the password hash request 0E4H that FORMAT issues later, which is also SYS2, is answered out of memory without a further directory read.

Two complete boot sectors are carried in the image at 6B00H and 6C00H. Both are assembled to run at 4200H, which is where the Model I boot ROM places cylinder 0 sector 0 before jumping to it, so every internal address inside those two blocks reads 42xxH in the listing even though the bytes live at 6Bxxh and 6Cxxh in FORMAT's image. 6B00H is the single-density boot sector and 6C00H the double-density one; FORMAT patches the directory cylinder and the stepping rate into each from the table of patch addresses at 6900H and then writes them to cylinder 0 sectors 0 and 1.

Load Map

Address RangeContents
6000H-6563H
1380 bytes
Format engine: track-image expander, cylinder loop, verify and lock-out pass, GAT/HIT/directory construction, local driver call stubs, keyboard and number helpers
6564H-656DH
10 bytes
NOT LOADED. Working copy of the target drive's Drive Code Table entry, pointed to by Register Pair IY. Also used as a scratch save area by 6000H-6009H once the engine's entry code has run
656EH
1 byte
Boot-sector step rate, loaded as 03H
656FH-6570H
2 bytes
NOT LOADED. Sectors per cylinder and sectors per track, filled in by the expander at 6018H and 6023H
6571H-6588H
24 bytes
Four six-byte track-format descriptors
6589H-66D1H
329 bytes
The four packed track-image templates, each followed by its expected sector number list
66D2H-685AH
393 bytes
Engine message block, the small restore helper at 6763H, and the trailing zero fill
685BH-68FFH
165 bytes
NOT LOADED
6900H-6903H
4 bytes
Two DEFWs, the step-rate patch addresses inside the two boot-sector images
6904H-69FFH
252 bytes
NOT LOADED
6A00H-6ACAH
203 bytes
NOT LOADED. The working part of the Granule Allocation Table image: the granule allocation map at GAT offset 00H and the lock-out map at GAT offset 60H, both built at run time
6ACBH-6AFFH
53 bytes
The pre-set tail of the Granule Allocation Table image: the configuration word, the master password hash cell, the diskette name field, the date field and the trailing blanks
6B00H-6BFFH
256 bytes
Single-density boot sector image, assembled to run at 4200H
6C00H-6CFFH
256 bytes
Double-density boot sector image, assembled to run at 4200H
6D00H-6DFFH
256 bytes
NOT LOADED. Sector-identifier pointer table built by the expander, and the general 256-byte sector buffer
6E00H-7264H
1125 bytes
Front end: sign-on, configuration sector, @PARAM parsing, operator prompts, target disk checks. Overwritten by the expanded track image once the engine starts
7265H-7276H
18 bytes
NOT LOADED. Ten-byte snapshot of the working Drive Code Table copy taken by 7123H before the target diskette checks, followed by the eight-byte table of per-drive default cylinder counts
7277H-7673H
1021 bytes
The two RST 28H stubs, the @PARAM keyword table, the master password buffer, and the front-end message block

Variable and Buffer List

Address RangePurpose
6000H-6009H
10 bytes
Dual purpose. On entry these ten bytes are the engine's first three instructions. Once the format pass is under way 618FH copies the working Drive Code Table entry here so the verify pass can alter the working copy, and 630BH copies it back
6009H
1 byte
Directory cylinder, stored by 6288H into the third byte of the saved table copy
6014H-6015H
2 bytes
SELF-MODIFYING. Operand of LD DE,0000H at 6013H. Address of the packed track-image template in use, planted by 650EH
60A5H-60A6H
2 bytes
SELF-MODIFYING. Operand of LD BC,0000H at 60A4H. Running pointer into the sector interleave table, advanced by the skew after every cylinder
60FDH
1 byte
SELF-MODIFYING. Operand of LD A,00H at 60FCH. Cylinder-to-cylinder sector skew, taken from byte 1 of the template
610DH
1 byte
SELF-MODIFYING. Opcode of JR Z,612EH at 610CH. Rewritten to 18H by 61B1H so the branch becomes unconditional during the single density cylinder 0 pass
6126H-6127H
2 bytes
Operand of LD BC,0000H at 6125H. The WAIT parameter, a post-step delay count passed to ROM 0060H
6156H-6157H
2 bytes
SELF-MODIFYING. Operand of LD BC,0000H at 6155H. Address of the expected sector number list behind the template, which the verify pass walks
615AH
1 byte
SELF-MODIFYING. Operand of ADD A,00H at 6159H, the running sector-number bias in the verify pass
6164H
1 byte
SELF-MODIFYING. Operand of LD A,00H at 6163H, the verify pass sector counter
6184H-6185H
2 bytes
SELF-MODIFYING. The JR NZ,611EH at 6184H, rewritten to JR 61C4H by 61B8H
620FH-6210H
2 bytes
Operand of LD HL,0000H at 620EH. The SYSTEM parameter result cell
6256H-6257H
2 bytes
Operand of LD BC,0000H at 6255H. The DIR parameter result cell, the requested directory cylinder
6334H
1 byte
SELF-MODIFYING. Operand of OR 00H at 6333H. Set to 10H by 61ACH, contributing bit 4 to the GAT configuration word high byte to mark a dual density disk
6441H
1 byte
SELF-MODIFYING. Operand of LD A,00H at 6440H. Saved copy of the byte at 4012H, the interrupt vector byte the engine replaces with a RET
648CH
1 byte
SELF-MODIFYING. Operand of LD C,0FFH at 648BH, the target drive number passed to the driver in Register C. Planted by 6E51H or 6E98H and read back by 6449H to decide whether the format ran on drive 0
6564H-656DH
10 bytes
Working copy of the target drive's Drive Code Table entry. Register Pair IY points here for the whole of the format pass
656EH
1 byte
Boot-sector step rate, 0 to 3, patched into whichever boot sector is written
656FH
1 byte
Sectors per cylinder, which is sectors per track doubled on a two-sided disk
6570H
1 byte
Sectors per track, byte 0 of the template in use
6A00H-6A5FH
96 bytes
Granule allocation map at GAT offset 00H, one byte per cylinder, a set bit meaning the granule is not available. 70E6H seeds it with the free pattern for the geometry and 61F7H writes 0FFH into the byte of any cylinder that fails verification
6A60H-6ABFH
96 bytes
Lock-out map at GAT offset 60H, copied wholesale from 6A00H by 61DAH once verification is complete
6ACCH-6ACDH
2 bytes
GAT configuration word. 6315H writes the low byte as the highest cylinder less 22H and 6335H builds the high byte from the eight inch, double density, granules per track, step and dual density bits
6ACEH-6ACFH
2 bytes
Master password hash at GAT offset CEH, the sixteen-bit value returned by the SYS2 hash and stored by 6F6BH
6AD0H-6AD7H
8 bytes
Diskette name at GAT offset D0H, shipped as eight blanks. 6EE6H defaults it to the string LDOSDISK at 7644H when the NAME parameter was not given
6AD8H-6AE0H
9 bytes
Diskette date at GAT offset D8H, shipped as MM/DD/YY and overwritten by @DATE at 6F1BH
6AE1H-6AFFH
31 bytes
Blanks, covering the auto-command field at GAT offset E0H
6D00H-6DFFH
256 bytes
Two uses. During expansion it is the table of DEFW pointers to the identifier field of every sector in the track image, terminated by 0000H. Afterwards it is the 256-byte sector buffer used to build the GAT, the HIT and the directory records
6E00H onwards
up to 10673 bytes
The expanded track image handed to the WD1771 Write Track command
6E5FH-6E60H
2 bytes
Operand of LD DE,0000H at 6E5EH. The SDEN parameter result cell
6E64H-6E65H
2 bytes
Operand of LD DE,0000H at 6E63H. The DDEN parameter result cell
6E69H-6E6AH
2 bytes
Operand of LD DE,0000H at 6E68H. The SIDES parameter result cell
6E6EH-6E6FH
2 bytes
Operand of LD DE,0000H at 6E6DH. The CYL parameter result cell
6E73H-6E74H
2 bytes
Operand of LD DE,0FF00H at 6E72H. The STEP parameter result cell; the high half ships as 0FFH so an absent parameter can be recognised at 6E75H
6EDCH-6EDDH
2 bytes
Operand of LD HL,0000H at 6EDBH. The NAME parameter result cell
6F1FH-6F20H
2 bytes
Operand of LD HL,0000H at 6F1EH. The MPW parameter result cell
6FA7H
1 byte
SELF-MODIFYING. Operand of LD A,00H at 6FA6H. Non-zero when any of SDEN, DDEN, SIDES, CYL or STEP appeared on the command line, which suppresses every operator prompt
6FACH-6FADH
2 bytes
Operand of LD DE,0FFFFH at 6FABH. The QUERY parameter result cell
6FDAH-6FDBH
2 bytes
SELF-MODIFYING. Operand of LD HL,0000H at 6FD9H. Address of the double-density track-format descriptor for the drive size in use
6FEAH-6FEBH
2 bytes
SELF-MODIFYING. Operand of LD HL,0000H at 6FE9H. Address of the single-density track-format descriptor for the drive size in use
7031H
1 byte
SELF-MODIFYING. Operand of LD A,00H at 7030H. Default cylinder count for the drive, taken from the configuration sector by 6EC3H
7156H-7157H
2 bytes
SELF-MODIFYING. Operand of LD DE,0000H at 7155H. Bytes of memory the expanded track image requires, planted by 651FH
71C4H-71C5H
2 bytes
Operand of LD HL,0000H at 71C3H. The ABS parameter result cell
7265H-726EH
10 bytes
Snapshot of the working Drive Code Table copy. 7123H takes it before the readiness, write protect and existing data checks, which move the head and change DCT+05H, and 71E6H puts it back immediately before the engine is entered
726FH-7276H
8 bytes
Per-drive default cylinder counts lifted out of the configuration sector by 6E35H, one per unit 0 to 7. 6EC1H indexes this table by the target drive
727DH-7284H
8 bytes
Master password buffer, shipped as eight blanks. 6F29H defaults it to the string PASSWORD at 764CH and 7201H validates and hashes it

Track Format Descriptors at 6571H

Four six-byte descriptors, chosen by drive size and density. 6F75H points Register Pair HL at 6571H and adds twelve when DCT+03H bit 5 says the drive is eight inch, so 6571H and 6577H serve five and a quarter inch drives while 657DH and 6583H serve eight inch drives. Within a pair the first entry is single density and the second double density. Each descriptor is a DEFW address of the packed template, a DEFW written straight into DCT+07H and DCT+08H as the drive geometry, and a DEFW giving the memory the expanded image needs.

DescriptorTemplateGeometryMeaning
6571H6589H2409HFive and a quarter inch single density. 10 sectors per track, skew 7, expanded image 3105 bytes, memory required 0D35H
6577H65C9H4511HFive and a quarter inch double density. 18 sectors per track, skew 13, expanded image 6334 bytes, memory required 196AH
657DH6623H270FHEight inch single density. 16 sectors per track, skew 2, expanded image 5176 bytes, memory required 1558H
6583H6668H491DHEight inch double density. 30 sectors per track, skew 3, expanded image 10357 bytes, memory required 29B1H

Packed Track-Image Template Format

A template is a byte-oriented run-length stream with five escape codes. Byte 0 is the sectors per track and byte 1 the cylinder-to-cylinder skew. The sector interleave table follows immediately and is skew plus one plus sectors long: the leading entries are the physical sector numbers in the order they are to be laid down, and the trailing entries are negative wrap values that pull the running pointer back to the head of the list. The compressed stream begins after that table, and behind the stream sits the list of sector numbers the verify pass walks.

CodeAction
01H-0F0HRepeat the following single byte that many times into the track image. A count of 00H means 256, which is how the 256-byte data field of a sector is emitted in three bytes
0F1HOpen a repeat block. The repeat count is the sectors per track held at 6570H. The stream position and the count are pushed
0F2HClose a repeat block. Decrement the pushed count and go round again from the pushed position, or drop both and carry on
0F3HRecord the current track-image position as a DEFW in the sector-identifier pointer table at 6D00H. This marks the three bytes that will receive the cylinder, the side and the sector number
0F4HEnd of template. Terminate the pointer table with 0000H, save the address of the expected sector list, and pad 256 bytes of 0FFH behind the image
0F5HRepeat the following two-byte pair the given number of times. A count of 00H means 256 pairs

Local Disk Driver Call Stubs at 6460H-6494H

Eleven two-byte stubs each load an operation code into Register A and fall into the common tail at 6489H, which puts the operation in Register B, the target drive number in Register C and 20H in Register A and then does JP (IY) into the working Drive Code Table copy. On a fitted unit DCT+00H is a JP to the drive's driver, so the call reaches the resident floppy engine at 45FBH or whatever replacement driver FDUBL or a hard-disk driver has hooked into DCT+01H and DCT+02H.

StubOpEffect in the resident floppy driver
6460H00HReturns immediately with Register A zero and the Z FLAG set. Because an absent unit holds C9H rather than a JP at DCT+00H, and the tail enters with Register A holding 20H, a missing drive returns NZ. This is the drive-present test
6463H01HSelect the drive and write the drive-select and side latch at 37E1H
6467H04HRestore. Issues the WD1771 Restore command and sets the current cylinder DCT+05H to zero
646BH05HStep In. Increments the current cylinder DCT+05H and issues the WD1771 Step In command
646FH07HWait for the controller to go not busy, re-asserting the drive select latch while it waits
6473H0FHWrite Track. Issues WD1771 command 0F4H and streams the buffer at Register Pair HL to the data register
6477H0CHReached only on a unit whose DCT+04H bit 4 is set, that is a rigid drive served by its own driver. The resident floppy driver treats any code above 0AH and below 0EH as a normal Write Sector
647BH0DHWrite Sector, WD1771 command 0A8H
647FH0EHWrite Sector with a deleted data address mark, WD1771 command 0ABH. This is how every directory sector is written
6483H09HRead Sector, WD1771 command 88H
6487H0AHVerify Sector, which reads the sector and discards the data

@PARAM Keyword Table at 72A8H

Nineteen entries of a six-character blank-padded keyword followed by a DEFW result cell, terminated by 00H. Every result cell is the operand field of a LD HL or LD DE instruction somewhere in the front end, so a parameter supplied on the command line is delivered by simply rewriting an instruction.

KeywordResult cellPurpose
NAME, N6EDCHAddress of the diskette name string
MPW, M6F1FHAddress of the master password string
SDEN6E5FHFormat in single density
DDEN6E64HFormat in double density
SIDES6E69HNumber of sides, 1 or 2
CYL, C6E6EHNumber of cylinders
STEP6E73HBoot track step rate, 0 to 3
ABS, A71C4HFormat even if the disk already contains data
QUERY, Q6FACHSuppress or allow the operator prompts
SYSTEM620FHWrite system information only, without formatting tracks
WAIT, W6126HExtra delay after each track step
DIR, D6256HCylinder to place the directory on

Cross-Reference Notes

FORMAT issues two RST 28H supervisor requests, and both of them are SYS2. Request 84H at 7277H, issued once immediately after the sign-on and before the configuration sector is read, has no sub-function and SYS2 rejects it with a RET; its only effect is to bring SYS2 into memory. Request 0E4H at 727AH is the SYS2 password hash, taking the eight-byte field at Register Pair DE and returning the sixteen-bit hash in Register Pair HL. Since the two share the low nibble 4, the second is served from the copy the first loaded. Nothing else in the file loads an overlay, so SYS0 is resident throughout and every other external call is either ROM low core or a resident SYS0 vector.

ROM entry points used: 0033H the video driver, 0040H the line input routine, 0049H wait for a key, 0060H the delay loop and 01C9H the Level II clear-screen routine, the last of these only from inside the double-density boot sector image.

Resident SYS0 entry points used: @EXIT 402DH, @ABORT 4030H, @ERROR 4409H, @DSPLY 4467H, @DATE 4470H, @PARAM 4476H, @LOGOT 447BH, RDSECT 4777H, the Drive Code Table address calculator 47A5H and MULTEA 4B6CH. Resident variables read: TIMER$ 4040H, HIGH$ 4049H, SFLAG$ 430FH and MFLAG$ 442FH. The system buffer SBUFF$ 4200H receives the configuration sector.

The two boot sector images pin down how the Hash Index Table is ordered. FORMAT writes the BOOT/SYS record to sector 2 of the directory cylinder and the DIR/SYS record to sector 3, both in slot 0, while giving them Hash Index Table bytes 0 and 1. Both boot sectors then read sector 4 slot 0 and expect to find the resident system loader there. Directory record n is therefore slot 0 of sector 2 plus n for the first thirty-two records, which is why a DATA disk reserves two directory slots and a SYSTEM disk configured by BACKUP reserves fifteen in total, those two plus thirteen more for the /SYS overlays.

FORMAT is the writer of the two files that DOS maintains and never copies. The boot sector it lays down at cylinder 0 sector 0 is the subject of the BOOT/SYS disassembly; the two images carried here at 6B00H and 6C00H are that same program in its single-density and double-density forms, before SYSGEN or SOLE have had a chance to alter it.

Disassembly:

6000H - Format Engine Entry and Interrupt Lock-Out

The front end reaches this point with a JP from 71EBH. Register Pair IY holds 6564H, the ten-byte working copy of the target drive's Drive Code Table entry. Everything the operator asked for has already been resolved into that copy: DCT+03H carries the density and side bits, DCT+04H the double-sided and unit bits, DCT+05H the current cylinder, DCT+06H the highest cylinder to be written and DCT+07H with DCT+08H the geometry that matches the chosen template.

6000
BIT 4,(IY+04H) FD CB 04 66
Test bit 4 of DCT+04H in the working Drive Code Table copy at 6568H. That bit marks a unit that is not an ordinary floppy and is not to be hooked by a floppy driver, which on this system means a rigid drive.
6004
If the NZ FLAG has been set, meaning the working copy describes a rigid drive, JUMP to 61FCH, the path that writes system information onto a drive that has already been low level formatted. No track is ever written on that path.
6007
DI F3
Disable interrupts. The WD1771 Write Track command has to be fed a byte every thirty-two microseconds in single density and every sixteen in double density, so nothing may steal the processor once the transfer starts.
6008
LD A,(4012H) 3A 12 40
Fetch the byte at 4012H into Register A. 4012H is the first byte of the RST 38H interrupt hook the ROM jumps to on every timer tick, and it normally holds the opcode of a JP into the resident interrupt handler.
600B
LD (6441H),A 32 41 64
Self-Modifying Code
Store Register A, the original opcode byte from the interrupt hook, into 6441H, the operand of LD A,00H at 6440H, so the restore routine can put exactly this byte back. The listing shows the initial value 00H; at run time it holds what was found at 4012H, normally 0C3H.
600E
LD A,0C9H 3E C9
Load Register A with 0C9H, the opcode for RET.
6010
LD (4012H),A 32 12 40
Store Register A over the first byte of the RST 38H interrupt hook at 4012H. Any interrupt that does slip through now returns at once instead of running the clock and task chain.

6013H - Track Image Expander

This block turns one of the four packed templates into the literal byte stream the WD1771 Write Track command lays on the surface. It writes the track image upward from 6E00H, over the front end that has just finished running, and at the same time writes a table of DEFW pointers at 6D00H recording where in that image every sector identifier field starts. The SYSTEM path re-enters here from 61C1H, which is why the entry is a section of its own.

6013
LD DE,0000H 11 00 00
Self-Modifying Code
Point Register Pair DE at the packed track image template. The operand at 6014H is planted by 650EH out of the six-byte descriptor selected for the drive size and density, so at run time it holds 6589H, 65C9H, 6623H or 6668H. The listing shows the unpatched value 0000H.
6016
LD A,(DE) 1A
Fetch byte 0 of the template, the number of sectors per track, into Register A. This is 10 for five and a quarter inch single density, 18 for double density, 16 for eight inch single density and 30 for eight inch double density.
6017
INC DE 13
INCrement Register Pair DE by 1, advancing the template pointer to byte 1.
6018
LD (6570H),A 32 70 65
Store Register A, the sectors per track, at 6570H. The repeat escape at 606CH reloads this value as its loop count, so it is what makes one pass of the template produce one sector.
601B
LD B,A 47
Copy Register A, the sectors per track, into Register B, where it is needed to step the template pointer past the interleave table.
601C
BIT 5,(IY+04H) FD CB 04 6E
Test bit 5 of DCT+04H in the working Drive Code Table copy at 6568H, the double-sided flag.
6020
If the Z FLAG has been set, meaning the disk is single sided, JUMP to 6023H leaving Register A holding just the sectors on one track.
6022
ADD A,A 87
Double Register A, the sectors per track, because a two-sided cylinder carries that many sectors on each of its two surfaces.
6023
LD (656FH),A 32 6F 65
Store Register A, now the number of sectors in a whole cylinder, at 656FH. The verify pass at 6402H and the directory sizing code at 63B2H both read this cell.
6026
LD A,(DE) 1A
Fetch byte 1 of the template, the cylinder to cylinder sector skew, into Register A. This is 7 for five and a quarter inch single density, 13 for double density, 2 for eight inch single density and 3 for eight inch double density.
6027
INC DE 13
INCrement Register Pair DE by 1. Register Pair DE now addresses the first entry of the sector interleave table.
6028
LD (60FDH),A 32 FD 60
Self-Modifying Code
Store Register A, the skew, into 60FDH, the operand of LD A,00H at 60FCH, the instruction that advances the interleave pointer between cylinders. The listing shows the initial value 00H.
602B
LD (60A5H),DE ED 53 A5 60
Self-Modifying Code
Store Register Pair DE, addressing the head of the sector interleave table, into 60A5H, the operand of LD BC,0000H at 60A4H, which reloads the running interleave pointer at the top of every cylinder. The listing shows the initial value 0000H.
602F
INC A 3C
INCrement Register A by 1. Register A held the skew and now holds skew plus one.
6030
ADD A,B 80
ADD Register B, the sectors per track, to Register A, giving the length in bytes of the sector interleave table.
6031
ADD A,E 83
ADD Register E, the low byte of the template pointer, to Register A, forming the low byte of the address just past the interleave table.
6032
LD E,A 5F
Store that low byte back into Register E.
6033
ADC A,D 8A
ADD Register D, the high byte of the template pointer, and the carry to Register A.
6034
SUB E 93
SUBtract Register E from Register A, leaving Register A holding the corrected high byte.
6035
LD D,A 57
Store the corrected high byte into Register D. Register Pair DE now addresses the first byte of the compressed stream.
6036
LD HL,6E00H 21 00 6E
Point Register Pair HL at 6E00H, the base of the track image about to be built. This is the same memory the front end was executing in, which is why the front end has finished with itself before it jumps here.
6039
LD BC,6D00H 01 00 6D
Point Register Pair BC at 6D00H, the base of the sector identifier pointer table.
603C
LD A,(DE) 1A
Loop Start
Fetch the next control byte of the compressed stream, addressed by Register Pair DE, into Register A.
603D
INC DE 13
INCrement Register Pair DE by 1, stepping past the control byte just taken.
603E
CP 0F1H FE F1
Compare Register A, the control byte, against 0F1H, the code that opens a repeat block. If Register A equals 0F1H the Z FLAG is set.
6040
If the Z FLAG has been set, JUMP to 606CH to push the repeat count and the loop position.
6042
CP 0F2H FE F2
Compare Register A against 0F2H, the code that closes a repeat block. If Register A equals 0F2H the Z FLAG is set.
6044
If the Z FLAG has been set, JUMP to 6073H to count the repeat block down.
6046
CP 0F3H FE F3
Compare Register A against 0F3H, the code that records a sector identifier position. If Register A equals 0F3H the Z FLAG is set.
6048
If the Z FLAG has been set, JUMP to 607DH to write the current track image address into the pointer table.
604A
CP 0F4H FE F4
Compare Register A against 0F4H, the end of template code. If Register A equals 0F4H the Z FLAG is set.
604C
If the Z FLAG has been set, JUMP to 6085H to close the pointer table and pad the tail of the image.
604E
CP 0F5H FE F5
Compare Register A against 0F5H, the code that repeats a two-byte pair. If Register A equals 0F5H the Z FLAG is set. The flags survive the PUSH that follows.
6050
PUSH BC C5
Save Register Pair BC, the running pointer into the sector identifier table at 6D00H, onto the stack. Both fill loops below use Register B as their counter.
6051
If the NZ FLAG has been set, meaning the control byte is below 0F1H and is a plain repeat count, JUMP to 6062H to repeat a single byte.
6053
LD A,(DE) 1A
Fetch the repeat count that follows the 0F5H escape into Register A.
6054
INC DE 13
INCrement Register Pair DE by 1, stepping past the count.
6055
LD B,A 47
Copy Register A, the repeat count, into Register B as the DJNZ counter. A count of 00H gives 256 iterations.
6056
LD A,(DE) 1A
Fetch the first byte of the pair, addressed by Register Pair DE, into Register A.
6057
INC DE 13
INCrement Register Pair DE by 1, stepping past the first byte of the pair.
6058
LD C,A 4F
Copy Register A, the first byte of the pair, into Register C where the fill loop can reach it.
6059
LD A,(DE) 1A
Fetch the second byte of the pair, addressed by Register Pair DE, into Register A. Register Pair DE is deliberately not advanced, because the common exit at 6069H does that single increment for every branch.
605A
LD (HL),C 71
Loop Start
Store Register C, the first byte of the pair, into the track image at the address in Register Pair HL.
605B
INC HL 23
INCrement Register Pair HL by 1, advancing the track image write pointer.
605C
LD (HL),A 77
Store Register A, the second byte of the pair, into the track image at the address in Register Pair HL.
605D
INC HL 23
INCrement Register Pair HL by 1.
605E
DECrement Register B, the pair count, and LOOP BACK to 605AH while it is not zero.
Loop End
6060
JUMP to 6068H, the common exit that restores the sector identifier pointer and steps the stream pointer past the last data byte.
6062
LD B,A 47
Copy Register A, a control byte below 0F1H, into Register B as the DJNZ counter. A control byte of 00H fills 256 bytes, which is how the 256-byte data field of a sector is expressed in three bytes of template.
6063
LD A,(DE) 1A
Fetch the byte that is to be repeated, addressed by Register Pair DE, into Register A. Register Pair DE is not advanced here; the common exit does it.
6064
LD (HL),A 77
Loop Start
Store Register A, the byte being repeated, into the track image at the address in Register Pair HL.
6065
INC HL 23
INCrement Register Pair HL by 1.
6066
DECrement Register B, the repeat count, and LOOP BACK to 6064H while it is not zero.
Loop End
6068
POP BC C1
Restore Register Pair BC, the running pointer into the sector identifier table at 6D00H, from the stack.
6069
INC DE 13
INCrement Register Pair DE by 1, stepping past the last data byte that both fill branches deliberately left unconsumed.
606A
LOOP BACK to 603CH to fetch the next control byte.
606C
LD A,(6570H) 3A 70 65
Fetch the sectors per track from 6570H, stored there at 6018H, into Register A. This is the repeat count for the block the 0F1H escape has just opened, which is why one template describes one sector and the expander produces a whole track.
606F
PUSH DE D5
Save Register Pair DE, the stream position immediately behind the 0F1H escape, onto the stack. This is the address the block will be re-entered at.
6070
PUSH AF F5
Save Register A, the remaining repeat count, onto the stack above the saved position.
6071
JUMP to 603CH to expand the body of the repeat block.
6073
POP AF F1
Restore the remaining repeat count into Register A from the stack. The saved stream position is still underneath it.
6074
DEC A 3D
DECrement Register A by 1, counting off the sector just expanded.
6075
If the Z FLAG has been set, meaning every sector of the track has been expanded, JUMP to 607AH to discard the saved position and leave the block.
6077
POP DE D1
Restore Register Pair DE with the stream position saved behind the 0F1H escape, rewinding the stream to the head of the repeat block.
6078
LOOP BACK to 606FH to push the rewound position and the decremented count again and expand the next sector.
607A
POP AF F1
Discard the saved stream position by popping it into Register Pair AF. Its value is never used; the pop is only there to balance the stack.
607B
JUMP to 603CH to carry on with the stream after the closing 0F2H escape.
607D
LD A,L 7D
Copy Register L, the low byte of the current track image write pointer, into Register A. This address is where the cylinder, side and sector bytes of the next sector identifier field will sit.
607E
LD (BC),A 02
Store Register A into the sector identifier pointer table at the address in Register Pair BC.
607F
INC BC 03
INCrement Register Pair BC by 1, advancing the pointer table write position.
6080
LD A,H 7C
Copy Register H, the high byte of the current track image write pointer, into Register A.
6081
LD (BC),A 02
Store Register A into the sector identifier pointer table at the address in Register Pair BC, completing the DEFW entry.
6082
INC BC 03
INCrement Register Pair BC by 1, ready for the next sector.
6083
JUMP to 603CH to carry on with the stream. Register Pair HL is untouched, so the three placeholder bytes that follow in the template are still emitted normally and are simply overwritten later by 60B2H to 60CEH.
6085
LD (6156H),DE ED 53 56 61
Self-Modifying Code
Store Register Pair DE, which now addresses the byte just past the end of template escape, into 6156H, the operand of LD BC,0000H at 6155H, which the verify pass uses to walk the list of expected sector numbers. The listing shows the initial value 0000H.
6089
XOR A AF
Set Register A to ZERO and clear all flags.
608A
LD (BC),A 02
Store the zero in Register A into the sector identifier pointer table at the address in Register Pair BC, the low byte of the terminating 0000H entry.
608B
INC BC 03
INCrement Register Pair BC by 1.
608C
LD (BC),A 02
Store the zero in Register A into the pointer table, completing the terminator the fill-in loop at 60AEH tests for.
608D
LD B,00H 06 00
Load Register B with 00H, giving the fill loop below a count of 256.
608F
LD A,0FFH 3E FF
Load Register A with 0FFH, the byte used to pad the tail of the track image.
6091
LD (HL),A 77
Loop Start
Store Register A, holding 0FFH, into the track image at the address in Register Pair HL, just past the last byte the template produced.
6092
INC HL 23
INCrement Register Pair HL by 1.
6093
DECrement Register B and LOOP BACK to 6091H while it is not zero. The Write Track command runs from one index pulse to the next and simply stops, so this tail guarantees there is always something valid to feed it if the drive turns slightly slow.
Loop End

6095H - Cylinder Format Loop

The track image is complete apart from the three bytes of every sector identifier field that carry the cylinder, the side and the sector number. This block fills those in, writes the track, repeats both for side 1 when the disk is two sided, applies the skew and steps the head in one cylinder. Register Pair BC is the running pointer into the sector interleave table and is deliberately carried from cylinder to cylinder, so the interleave keeps rotating instead of restarting.

6095
LD HL,66D2H 21 D2 66
Point Register Pair HL at the message at 66D2H, Formatting cylinder terminated by 03H. The three trailing blanks are the field the running cylinder number is written into.
6098
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL. This is done once, outside the loop, so only the two digits are rewritten afterwards.
609B
LD A,(IY+05H) FD 7E 05
Loop Start
Fetch DCT+05H from the working Drive Code Table copy at 6569H into Register A, the current cylinder. That byte is maintained by the driver itself: Restore zeroes it and every Step In increments it. 611BH plants this address in Register Pair BC as the return point for the controller wait, so this is where every cylinder begins.
609E
GOSUB to 64B5H to convert Register A, the current cylinder number, into two ASCII digits, returning the tens digit in Register C and the units digit in Register B.
60A1
GOSUB to 64E9H, which sends two backspaces and then Register C and Register B, so the number at the end of the message is overwritten in place rather than a new line being printed for every cylinder.
60A4
LD BC,0000H 01 00 00
Self-Modifying Code
Point Register Pair BC at the sector interleave table. The operand at 60A5H is planted at 602BH with the head of the table and rewritten at 60D2H and 6103H as the interleave and the skew advance it. The listing shows the initial value 0000H.
60A7
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H, the head of the sector identifier pointer table the expander built. Side 1 of a two-sided cylinder re-enters here from 60F6H so the same table is walked again.
60AA
LD E,(HL) 5E
Loop Start
Fetch the low byte of the next DEFW in the pointer table, addressed by Register Pair HL, into Register E.
60AB
INC HL 23
INCrement Register Pair HL by 1.
60AC
LD D,(HL) 56
Fetch the high byte of that DEFW into Register D. Register Pair DE now addresses the first of the three identifier bytes of one sector inside the track image.
60AD
INC HL 23
INCrement Register Pair HL by 1, leaving Register Pair HL on the next table entry.
60AE
LD A,D 7A
Copy Register D, the high byte of the identifier address just read, into Register A.
60AF
OR E B3
OR Register E, its low byte, into Register A. If both halves were zero the Z FLAG is set, which is the terminator the expander wrote at 608AH.
60B0
If the Z FLAG has been set, meaning every sector identifier on this surface has been filled in, JUMP to 60D2H to save the interleave pointer and write the track.
60B2
LD A,(IY+05H) FD 7E 05
Fetch DCT+05H from the working Drive Code Table copy at 6569H into Register A, the cylinder the head is standing on.
60B5
LD (DE),A 12
Store Register A, the cylinder number, into the track image at the address in Register Pair DE. This is the track byte of the sector identifier field, the value the WD1771 will later match against its track register.
60B6
INC DE 13
INCrement Register Pair DE by 1, moving on to the side byte.
60B7
LD A,(IY+03H) FD 7E 03
Fetch DCT+03H from the working Drive Code Table copy at 6567H into Register A, the drive specification byte whose bit 4 is the side select line.
60BA
AND 10H E6 10
Mask Register A with 10H, isolating the side select bit that 60F1H and 60F8H set and clear as the two surfaces are formatted.
60BC
RRCA 0F
Rotate Register A right, moving the isolated side bit from bit 4 towards bit 0.
60BD
RRCA 0F
Rotate Register A right again.
60BE
RRCA 0F
Rotate Register A right again.
60BF
RRCA 0F
Rotate Register A right a fourth time. Register A now holds 00H for side 0 or 01H for side 1.
60C0
LD (DE),A 12
Store Register A, the side number, into the track image at the address in Register Pair DE.
60C1
INC DE 13
INCrement Register Pair DE by 1, moving on to the sector byte.
60C2
LD A,(BC) 0A
Fetch the next entry of the sector interleave table, addressed by Register Pair BC, into Register A. A value below 80H is a physical sector number; a value with bit 7 set is a wrap marker.
60C3
OR A B7
OR Register A with itself to set the flags. The S FLAG follows bit 7, so a wrap marker leaves the M condition true and a genuine sector number the P condition.
60C4
If the P FLAG has been set, meaning bit 7 is clear and this is a real sector number, JUMP to 60CCH to use it directly.
60C7
ADD A,C 81
ADD Register C, the low byte of the interleave pointer, to Register A. Register A held a negative eight bit displacement such as 0F6H, so this pulls the pointer back by the number of interleave entries, in that example ten.
60C8
LD C,A 4F
Store the wrapped low byte back into Register C.
60C9
If the CARRY FLAG has been set, meaning the addition did not borrow out of the high byte, JUMP to 60CCH; Register B is already correct.
60CB
DEC B 05
DECrement Register B by 1 to complete the sixteen bit subtraction, since the negative displacement borrowed out of the low byte.
60CC
LD A,(BC) 0A
Fetch the interleave entry addressed by Register Pair BC into Register A, either the real sector number or the one at the wrapped position.
60CD
LD (DE),A 12
Store Register A, the physical sector number, into the track image at the address in Register Pair DE, completing the sector identifier field.
60CE
INC DE 13
INCrement Register Pair DE by 1. Register Pair DE is reloaded from the pointer table before it is used again, so this simply leaves it past the field.
60CF
INC BC 03
INCrement Register Pair BC by 1, stepping the interleave pointer to the next sector in laydown order.
60D0
LOOP BACK to 60AAH to take the next entry from the pointer table.
Loop End
60D2
LD (60A5H),BC ED 43 A5 60
Self-Modifying Code
Store Register Pair BC, the interleave pointer as it stands after this surface, into 60A5H, the operand of LD BC,0000H at 60A4H. Carrying the pointer forward is what gives side 1 and the following cylinder their rotated sector order rather than restarting the interleave every time.

60D6H - Write The Track, Change Sides and Step In

Nothing here seeks. FORMAT positions the head only with Restore and Step In, so DCT+05H in the working copy is both the cylinder being written and the value planted in the WD1771 track register by the write engine. Register D must therefore carry the cylinder into the driver and Register Pair HL the buffer.

60D6
LD D,(IY+05H) FD 56 05
Fetch DCT+05H from the working Drive Code Table copy at 6569H, the current cylinder, into Register D. The write engine copies Register D into the WD1771 track register at 37EDH before it issues the command.
60D9
GOSUB to the local stub at 6463H, which asks the drive's own driver for operation 01H, Select Drive. The driver builds the drive select and side value from DCT+03H bit 4 and DCT+04H and writes it to the latch at 37E1H, which is what puts the head on the surface chosen a moment ago.
60DC
LD HL,6E00H 21 00 6E
Point Register Pair HL at 6E00H, the base of the expanded track image. The write engine saves this pointer and pulls it back into Register Pair BC as the source of the byte stream.
60DF
GOSUB to the local stub at 6473H, which asks the drive's own driver for operation 0FH, Write Track. The resident engine turns that into WD1771 command 0F4H and streams the whole buffer into the data register at 37EFH until the index pulse ends the operation.
60E2
If the NZ FLAG has been set, meaning the driver returned a non-zero error code, JUMP to 64A7H to restore the machine state and report the disk error through @ERROR.
60E5
BIT 5,(IY+04H) FD CB 04 6E
Test bit 5 of DCT+04H in the working Drive Code Table copy at 6568H, the double-sided flag.
60E9
If the Z FLAG has been set, meaning the disk is single sided, JUMP to 60F8H because this cylinder is finished after one surface.
60EB
BIT 4,(IY+03H) FD CB 03 66
Test bit 4 of DCT+03H in the working Drive Code Table copy at 6567H, the side select bit.
60EF
If the NZ FLAG has been set, meaning the surface just written was side 1, JUMP to 60F8H because both surfaces of this cylinder are complete.
60F1
SET 4,(IY+03H) FD CB 03 E6
Set bit 4 of DCT+03H in the working Drive Code Table copy at 6567H, selecting side 1. The next Select Drive rotates this bit into bit 3 of the latch at 37E1H, and 60B7H will write 01H rather than 00H into every side byte.
60F5
INC BC 03
INCrement Register Pair BC by 1, nudging the interleave pointer one entry so that side 1 does not begin on the same physical sector number as side 0.
60F6
LOOP BACK to 60A7H to refill every sector identifier field with the new side number and the rotated sector order, then write the same buffer again on side 1.
60F8
RES 4,(IY+03H) FD CB 03 A6
Clear bit 4 of DCT+03H in the working Drive Code Table copy at 6567H, returning the side select bit to side 0 ready for the next cylinder.
60FC
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the cylinder to cylinder sector skew. The operand at 60FDH is planted at 6028H out of byte 1 of the template, so at run time it holds 07H, 0DH, 02H or 03H. The listing shows the initial value 00H.
60FE
ADD A,C 81
ADD Register C, the low byte of the interleave pointer, to Register A.
60FF
LD C,A 4F
Store the result back into Register C.
6100
ADC A,B 88
ADD Register B, the high byte of the interleave pointer, and the carry to Register A.
6101
SUB C 91
SUBtract Register C from Register A, leaving the corrected high byte.
6102
LD B,A 47
Store the corrected high byte into Register B. Register Pair BC has now been advanced by the skew, which is what makes the first sector of the next cylinder physically offset from the first sector of this one and lets the head settle without losing a revolution.
6103
LD (60A5H),BC ED 43 A5 60
Self-Modifying Code
Store the skewed interleave pointer into 60A5H, the operand of LD BC,0000H at 60A4H, so the next cylinder starts from it.
6107
LD A,(IY+05H) FD 7E 05
Fetch DCT+05H from the working Drive Code Table copy at 6569H, the cylinder just written, into Register A.
610A
CP (IY+06H) FD BE 06
Compare Register A against DCT+06H in the working Drive Code Table copy at 656AH, the highest cylinder this format is to reach.
610D
Self-Modifying Code
If the Z FLAG has been set, meaning the last cylinder has been written, JUMP to 612EH to begin the verify and lock-out pass. The opcode byte at 610DH is rewritten to 18H by 61B1H so that on the single density cylinder 0 pass the branch is unconditional.
610F
LD BC,0090H 01 90 00
Load Register Pair BC with 0090H, the loop count for the delay routine that follows, giving the head time to settle.
6112
GOSUB to the ROM delay routine at 0060H, which decrements Register Pair BC until it reaches zero.
6115
GOSUB to the local stub at 646BH, which asks the drive's own driver for operation 05H, Step In. The driver increments DCT+05H at 6569H and issues the WD1771 Step In command with the stepping rate taken from DCT+03H bits 1 and 0.
6118
If the NZ FLAG has been set, meaning the driver returned a non-zero error code, JUMP to 64A7H to restore the machine state and report the disk error.
611B
LD BC,609BH 01 9B 60
Load Register Pair BC with 609BH, the head of the cylinder loop. The common tail below pushes this value and returns to it, so this instruction is what selects the format pass rather than the verify pass; 6181H reaches the same tail with 613EH instead.
611E
GOSUB to the local stub at 646FH, which asks the drive's own driver for operation 07H, wait for the controller to leave the busy state. The driver rewrites the latch at 37E1H while it waits so the drive stays selected and the motor keeps running.
6121
If the NZ FLAG has been set, meaning the driver returned a non-zero error code, JUMP to 64A7H to report the disk error.
6124
PUSH BC C5
Save Register Pair BC, holding 609BH on the format pass or 613EH on the verify pass, onto the stack. The RET or the ROM delay routine below returns to it, so this is a computed jump built out of a stack push.
6125
LD BC,0000H 01 00 00
Load Register Pair BC with the WAIT parameter. The operand at 6126H is the result cell named by the WAIT and W keywords in the @PARAM table at 72A8H. The manual recommends starting at 25000 for drives that lock out every track above a certain point.
6128
LD A,B 78
Copy Register B, the high byte of the WAIT count, into Register A.
6129
OR C B1
OR Register C, its low byte, into Register A. If the whole count is zero the Z FLAG is set.
612A
RET Z C8
If the Z FLAG has been set, meaning no WAIT value was given, RETurn to the address pushed at 6124H.
612B
JUMP to the ROM delay routine at 0060H, which counts Register Pair BC down and then RETurns to the address pushed at 6124H. Entering the delay with a jump rather than a call is what makes the pushed address serve as the return.

612EH - Verify and Lock-Out Pass

Every cylinder has been written. This pass reads every sector of every cylinder back and marks the ones that will not read in the map at 6A00H, one byte per cylinder, which later becomes both the granule allocation table and the lock-out table inside the Granule Allocation Table sector. The expected sector numbers come from the list the template carries behind its end of template escape, whose address the expander saved into the operand at 6156H.

612E
LD A,0DH 3E 0D
Load Register A with 0DH, a carriage return, to close off the line the format pass has been rewriting the cylinder number into.
6130
GOSUB to the ROM video driver at 0033H to display the character in Register A.
6133
GOSUB to the local stub at 6467H, which asks the drive's own driver for operation 04H, Restore. The driver issues the WD1771 Restore command and sets DCT+05H at 6569H back to zero, so the verify pass starts from cylinder 0.
6136
LD HL,66EAH 21 EA 66
Point Register Pair HL at the message at 66EAH, a 1DH video control code followed by Verifying cylinder and a 03H terminator. The control code puts the cursor back at the start of the line so this message replaces the formatting one.
6139
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL.
613C
LD D,00H 16 00
Load Register D with 00H, the cylinder the verify pass starts at. Register D is the cylinder throughout this pass and is also the value the driver plants in the WD1771 track register.
613E
LD L,D 6A
Loop Start
Copy Register D, the cylinder being verified, into Register L. 6181H plants this address in Register Pair BC as the continuation, so every cylinder re-enters here.
613F
LD H,6AH 26 6A
Load Register H with 6AH, forming 6A00H plus the cylinder number, this cylinder's byte in the granule allocation map.
6141
LD A,(HL) 7E
Fetch this cylinder's byte from the granule allocation map, addressed by Register Pair HL, into Register A.
6142
INC A 3C
INCrement Register A by 1. A map byte of 0FFH, meaning every granule on the cylinder is unavailable, becomes zero and sets the Z FLAG.
6143
If the Z FLAG has been set, meaning the cylinder is already wholly locked out, JUMP to 617CH to move on without reading it. Cylinders above the highest one being formatted were filled with 0FFH at 70F5H, which is what stops the pass running off the end of the disk.
6145
LD A,D 7A
Copy Register D, the cylinder being verified, into Register A for conversion to text.
6146
GOSUB to 64B5H to convert Register A into two ASCII digits, returning the tens digit in Register C and the units digit in Register B.
6149
PUSH DE D5
Save Register Pair DE, whose Register D holds the cylinder number, onto the stack across the display call.
614A
GOSUB to 64E9H, which sends two backspaces and then the two digits, so the number at the end of the Verifying cylinder line is overwritten in place.
614D
POP DE D1
Restore Register Pair DE, with the cylinder number back in Register D, from the stack.
614E
XOR A AF
Set Register A to ZERO and clear all flags.
614F
LD (6164H),A 32 64 61
Self-Modifying Code
Store the zero into 6164H, the operand of LD A,00H at 6163H, resetting the count of sectors verified on this cylinder.
6152
LD (615AH),A 32 5A 61
Self-Modifying Code
Store the zero into 615AH, the operand of ADD A,00H at 6159H, resetting the sector number bias. The bias is zero while side 0 is verified and becomes the sectors per track when the pass crosses to side 1, because the driver numbers sectors straight through both surfaces.
6155
LD BC,0000H 01 00 00
Self-Modifying Code
Point Register Pair BC at the list of expected physical sector numbers. The operand at 6156H was planted at 6085H with the address just past the end of template escape. The listing shows the initial value 0000H.
6158
LD A,(BC) 0A
Loop Start
Fetch the next expected physical sector number, addressed by Register Pair BC, into Register A.
6159
ADD A,00H C6 00
Self-Modifying Code
ADD the sector number bias to Register A. The operand at 615AH is zero for side 0 and is rewritten at 6176H with the sectors per track for side 1, which is how one list serves both surfaces. The listing shows the initial value 00H.
615B
LD E,A 5F
Copy Register A, the biased sector number, into Register E. The driver's seek code subtracts the sectors per track from Register E to work out which surface the sector is on.
615C
GOSUB to the local stub at 6487H, which asks the drive's own driver for operation 0AH, Verify Sector, with Register D holding the cylinder and Register E the sector. The engine reads the sector and throws the data away, so this tests the medium without needing a buffer.
615F
If the NZ FLAG has been set, meaning the driver returned a non-zero error code, JUMP to 61E3H to decide whether this is bad medium or a real disk fault.
6162
INC BC 03
INCrement Register Pair BC by 1, stepping to the next entry of the expected sector number list.
6163
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the count of sectors verified so far on this cylinder. The operand at 6164H is cleared at 614FH and rewritten two instructions below. The listing shows the initial value 00H.
6165
INC A 3C
INCrement Register A by 1 to count the sector just verified.
6166
LD (6164H),A 32 64 61
Self-Modifying Code
Store the updated count back into 6164H, the operand of LD A,00H at 6163H.
6169
LD E,A 5F
Copy Register A, the count of sectors verified, into Register E for the two comparisons that follow.
616A
LD A,(656FH) 3A 6F 65
Fetch the sectors per cylinder from 656FH, stored there at 6023H, into Register A. On a two-sided disk this is twice the sectors per track.
616D
CP E BB
Compare Register A against Register E, the count verified. If they are equal the Z FLAG is set.
616E
If the Z FLAG has been set, meaning every sector on both surfaces of this cylinder has been verified, JUMP to 617CH to move to the next cylinder.
6170
LD A,(6570H) 3A 70 65
Fetch the sectors per track from 6570H, stored there at 6018H, into Register A.
6173
CP E BB
Compare Register A against Register E, the count verified. If they are equal side 0 has just been finished on a two-sided disk.
6174
If the NZ FLAG has been set, meaning sectors remain on this surface, LOOP BACK to 6158H to verify the next one.
6176
LD (615AH),A 32 5A 61
Self-Modifying Code
Store Register A, the sectors per track, into 615AH, the operand of ADD A,00H at 6159H. Every sector number taken from the list now has the sectors per track added to it, which is the numbering the driver uses to select side 1.
6179
INC BC 03
INCrement Register Pair BC by 1. The value is immediately discarded, because the instruction jumped to below reloads Register Pair BC from its own operand.
617A
LOOP BACK to 6155H to point Register Pair BC at the head of the expected sector number list again and walk it a second time for side 1.
Loop End
617C
LD A,D 7A
Copy Register D, the cylinder just dealt with, into Register A.
617D
INC D 14
INCrement Register D by 1, moving on to the next cylinder.
617E
CP (IY+06H) FD BE 06
Compare Register A against DCT+06H in the working Drive Code Table copy at 656AH, the highest cylinder on the disk. If they are equal the whole disk has been verified.
6181
LD BC,613EH 01 3E 61
Load Register Pair BC with 613EH, the head of the verify loop. The shared tail at 611EH pushes this value and returns to it, which is what makes that code serve the verify pass rather than the format pass.
6184
Self-Modifying Code
If the NZ FLAG has been set, meaning cylinders remain, JUMP to 611EH to wait for the controller, apply any WAIT delay and return to 613EH. No Step In is issued because the Verify Sector operation seeks for itself. 61B8H rewrites these two bytes to 18H and 3EH, turning the instruction into an unconditional JR 61C4H for the single density pass over cylinder 0.
6186
LD HL,6564H 21 64 65
Point Register Pair HL at 6564H, the working Drive Code Table copy, as the source of the block move below.
6189
LD DE,6000H 11 00 60
Point Register Pair DE at 6000H as the destination. The three instructions that occupy 6000H to 600DH have already run and will not run again, so those bytes are reused as a scratch save area.
618C
LD BC,000AH 01 0A 00
Load Register Pair BC with 000AH, the ten bytes of a Drive Code Table entry.
618F
LDIR ED B0
Block move ten bytes from the working copy at 6564H to the scratch area at 6000H. The copy preserves the geometry and density of the main format while the code below alters the working copy for the single density cylinder 0 pass; 630BH copies it back.
6191
LD A,(620FH) 3A 0F 62
Fetch the low byte of the SYSTEM parameter result cell at 620FH into Register A. It is the cell named by the SYSTEM keyword in the @PARAM table at 72A8H, so it is non-zero only when the operator asked for a system disk.
6194
OR A B7
OR Register A with itself to set the flags.
6195
If the Z FLAG has been set, meaning SYSTEM was not specified, JUMP to 61CFH. An ordinary data disk keeps the density it was formatted in right across cylinder 0.
6197
LD HL,676FH 21 6F 67
Point Register Pair HL at the message at 676FH, Re-format track 0....
619A
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL.
619D
LD HL,6571H 21 71 65
Point Register Pair HL at the descriptor at 6571H, the five and a quarter inch SINGLE density track format. Cylinder 0 of a Model I system disk has to be single density because the boot ROM can only read single density, which is what makes a double density LDOS disk a dual density disk.
61A0
GOSUB to 650AH, the second entry of the descriptor applier, which plants the template address into the operand at 6014H, the geometry into DCT+07H and DCT+08H at 656BH and 656CH and the memory requirement into the operand at 7156H. Entering at 650AH rather than 64FBH skips the part that would reset the highest cylinder.
61A3
GOSUB to 6763H, which asks the drive's own driver for operation 04H, Restore, and then clears bit 4 of DCT+03H at 6567H so side 0 is selected.
61A6
RES 6,(IY+03H) FD CB 03 B6
Clear bit 6 of DCT+03H in the working Drive Code Table copy at 6567H, the double density bit, so the next Write Track goes down in single density.
61AA
LD A,10H 3E 10
Load Register A with 10H, the bit that marks the pack as having a single density cylinder 0.
61AC
LD (6334H),A 32 34 63
Self-Modifying Code
Store Register A into 6334H, the operand of OR 00H at 6333H. That instruction is one of four that build the high byte of the Granule Allocation Table configuration word written to 6ACDH, so bit 4 of that word records a dual density disk. The listing shows the initial value 00H.
61AF
LD A,18H 3E 18
Load Register A with 18H, the opcode for an unconditional JR.
61B1
LD (610DH),A 32 0D 61
Self-Modifying Code
Store Register A over the opcode at 610DH, changing JR Z,612EH into JR 612EH. The cylinder 0 pass writes exactly one cylinder, so the test against the highest cylinder must not be allowed to send it round the disk again.
61B4
LD L,18H 2E 18
Load Register L with 18H, the opcode for an unconditional JR.
61B6
LD H,3EH 26 3E
Load Register H with 3EH, the displacement that reaches 61C4H from 6186H.
61B8
LD (6184H),HL 22 84 61
Self-Modifying Code
Store Register Pair HL over the two bytes at 6184H, changing JR NZ,611EH into JR 61C4H, so the single density verify of cylinder 0 drops straight into the restore code below.
61BB
LD HL,(656FH) 2A 6F 65
Fetch the word at 656FH into Register Pair HL, the sectors per cylinder in Register L and the sectors per track in Register H. Both are about to be overwritten by the single density expansion and have to be preserved for the directory sizing code.
61BE
LD (61C5H),HL 22 C5 61
Self-Modifying Code
Store Register Pair HL into 61C5H, the operand of LD HL,0000H at 61C4H. The listing shows the initial value 0000H.
61C1
JUMP to 6013H to expand the single density template and run one complete format and verify pass over cylinder 0. The two rewritten branches bring control back to 61C4H when that pass is done.
61C4
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the sectors per cylinder and sectors per track of the main format. The operand at 61C5H was planted at 61BEH before the single density pass ran. The listing shows the initial value 0000H.
61C7
LD (656FH),HL 22 6F 65
Store Register Pair HL back into the word at 656FH, restoring the figures the main format uses in place of the single density values the cylinder 0 pass left there.
61CA
LD HL,6A00H 21 00 6A
Point Register Pair HL at 6A00H, the granule allocation map byte for cylinder 0.
61CD
LD (HL),0FCH 36 FC
Store 0FCH into that byte. The single density format just laid on cylinder 0 gives it two granules, so bits 0 and 1 are cleared to show those two are available and the remaining six stay set because there are no more granules on that cylinder.
61CF
LD HL,6A00H 21 00 6A
Point Register Pair HL at 6A00H, the head of the granule allocation map, as the source of the block move below.
61D2
LD DE,6A60H 11 60 6A
Point Register Pair DE at 6A60H, offset 60H inside the Granule Allocation Table image based at 6A00H, the lock-out map the FREE command reads.
61D5
LD B,L 45
Copy Register L, which is zero because Register Pair HL holds 6A00H, into Register B as the high byte of the block move count.
61D6
LD C,(IY+06H) FD 4E 06
Fetch DCT+06H from the working Drive Code Table copy at 656AH, the highest cylinder, into Register C as the low byte of the count.
61D9
INC C 0C
INCrement Register C by 1, turning the highest cylinder number into a count of cylinders because they are numbered from zero.
61DA
LDIR ED B0
Block move one byte per cylinder from the granule allocation map at 6A00H to the lock-out map at 6A60H. The two maps start life identical: a granule that failed verification is both unavailable and locked out.
61DC
LD A,0DH 3E 0D
Load Register A with 0DH, a carriage return, to close off the Verifying cylinder line.
61DE
GOSUB to the ROM video driver at 0033H to display the character in Register A.
61E1
JUMP to 624AH to choose the directory cylinder and write the system information. The surface is now completely laid down and checked.

61E3H - Verify Failure: Lock Out the Cylinder

Reached from 615FH when the Verify Sector operation returns an error. The read and verify path of the resident engine builds its error code as base 01H plus the position of the lowest set bit of the masked WD1771 status, so a CRC error in bit 3 becomes 04H and a record not found in bit 4 becomes 05H. Those two mean the medium under the head is bad. Anything else, such as not ready in bit 7 or write protected in bit 6, is a fault with the drive rather than the diskette and is reported instead.

61E3
CP 05H FE 05
Compare Register A, the error code the driver returned, against 05H, record not found: the sector's identifier field could not be located on the track at all.
61E5
If the Z FLAG has been set, JUMP to 61ECH to lock the cylinder out.
61E7
CP 04H FE 04
Compare Register A against 04H, a CRC error: the sector was found but its contents did not check.
61E9
If the NZ FLAG has been set, meaning the error is neither 04H nor 05H and therefore is not a defect in the recording surface, JUMP to 64A7H to restore the machine state and report the disk error through @ERROR.
61EC
PUSH DE D5
Save Register Pair DE, whose Register D holds the failing cylinder number, onto the stack across the display call.
61ED
LD HL,6702H 21 02 67
Point Register Pair HL at the message at 6702H, an asterisk followed by three blanks. This is the mark the manual describes as showing a locked out cylinder.
61F0
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL, marking the cylinder number already on the screen.
61F3
POP DE D1
Restore Register Pair DE, with the failing cylinder number back in Register D, from the stack.
61F4
LD L,D 6A
Copy Register D, the failing cylinder number, into Register L.
61F5
LD H,6AH 26 6A
Load Register H with 6AH, forming 6A00H plus the failing cylinder number, that cylinder's byte in the granule allocation map.
61F7
LD (HL),0FFH 36 FF
Store 0FFH into that byte. Every bit set means every granule on the cylinder is unavailable, so no file will ever be allocated there, and 61DAH will copy the same byte into the lock-out map at 6A60H where the FREE command can display it.
61F9
JUMP to 617CH to abandon the rest of this cylinder and carry on with the next one. The remaining sectors are not tried, because the whole cylinder has already been written off.

61FCH - Rigid Drive Confirmation and the SYSTEM Path

Reached by the JP at 6004H when DCT+04H bit 4 marks the unit as something other than an ordinary floppy, and also by falling through from the SYSTEM branch. Nothing on this path writes a track. A rigid drive has already been low level formatted by its own utility, and the SYSTEM parameter on a floppy means the surface is already good, so all that is wanted is the system information: the boot sectors, the Granule Allocation Table, the Hash Index Table and the two directory records.

61FC
LD HL,75F5H 21 F5 75
Point Register Pair HL at the message at 75F5H, *** The target drive is a hard disk! *** followed by a line feed and Are you sure you want to format it ? with a 03H terminator.
61FF
BIT 3,(IY+03H) FD CB 03 5E
Test bit 3 of DCT+03H in the working Drive Code Table copy at 6567H, the rigid drive bit. If the unit really is a hard disk the Z FLAG is cleared.
6203
If the Z FLAG has been set, meaning DCT+03H bit 3 is clear and this is not a rigid drive, JUMP to 620EH. The warning is only put to the operator for a hard disk, where a mistake destroys far more than a diskette.
6205
GOSUB to 64C2H, which displays the string addressed by Register Pair HL, reads up to three characters into the buffer at 6D00H through the ROM line input routine and folds them to upper case, returning Register Pair HL pointing at 6D00H.
6208
LD A,(HL) 7E
Fetch the first character the operator typed from 6D00H, addressed by Register Pair HL, into Register A.
6209
CP 59H FE 59
Compare Register A, the first character typed, against 59H (ASCII: Y). If Register A equals 59H the Z FLAG is set; otherwise the NZ FLAG is set.
620B
If the NZ FLAG has been set, meaning the operator did not answer Y, JUMP to 6434H to display Format aborted! and abort.
620E
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the SYSTEM parameter. The operand at 620FH is the result cell named by the SYSTEM keyword in the @PARAM table at 72A8H, so it is non-zero only when the operator asked for system information to be added. The listing shows the initial value 0000H.
6211
LD A,H 7C
Copy Register H, the high byte of the SYSTEM parameter, into Register A.
6212
OR L B5
OR Register L, the low byte of the SYSTEM parameter, into Register A. If the whole word is zero the Z FLAG is set.
6213
If the NZ FLAG has been set, meaning SYSTEM was specified, JUMP to 6221H. On that path the medium is left exactly as it is.
6215
LD HL,6707H 21 07 67
Point Register Pair HL at the message at 6707H, Formatting... terminated by 0DH.
6218
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL.
621B
GOSUB to the local stub at 6477H, which asks the drive's own driver for operation 0CH. This is the only call in the file that uses that operation and it is only ever reached on a unit whose DCT+04H bit 4 is set, so the driver answering it is the rigid drive's own driver rather than the resident floppy engine.
621E
If the NZ FLAG has been set, meaning the driver returned a non-zero error code in Register A, JUMP to 64A7H to restore the machine state and report the disk error through @ERROR.
6221
LD A,(IY+07H) FD 7E 07
Fetch DCT+07H from the working Drive Code Table copy at 656BH into Register A. Bits 4 to 0 of that byte are the highest sector number on a track and bits 7 to 5 are the head count less one, which is how a rigid drive's geometry is carried in the same ten bytes a floppy uses.
6224
LD D,A 57
Copy Register A, the whole geometry byte, into Register D so the two fields can be separated.
6225
AND 1FH E6 1F
Mask Register A with 1FH, isolating bits 4 to 0 of DCT+07H, the highest sector number on a track.
6227
LD E,A 5F
Copy Register A, the highest sector number, into Register E.
6228
INC E 1C
INCrement Register E by 1, turning the highest sector number into the number of sectors per track because sectors are numbered from zero.
6229
XOR D AA
XOR Register D, the whole geometry byte, into Register A, which holds only its low five bits. The low five bits cancel and Register A is left holding bits 7 to 5 alone, the head count field.
622A
RLCA 07
Rotate Register A left, moving the head count field down towards bit 0.
622B
RLCA 07
Rotate Register A left again.
622C
RLCA 07
Rotate Register A left a third time. Register A now holds the head count field as a value 0 to 7 in its low three bits.
622D
INC A 3C
INCrement Register A by 1, turning the head count less one into the number of recording surfaces the unit has.
622E
GOSUB to the resident MULTEA routine at 4B6CH, an eight by eight bit multiply that returns Register A holding Register A times Register E. Register A is the number of surfaces and Register E the sectors per track, so the product is the number of sectors on one cylinder of the unit.
6231
BIT 5,(IY+04H) FD CB 04 6E
Test bit 5 of DCT+04H in the working Drive Code Table copy at 6568H, the double-sided flag. If the disk is two sided the Z FLAG is cleared.
6235
If the Z FLAG has been set, meaning the unit is single sided, JUMP to 6238H with the product unchanged.
6237
ADD A,A 87
Double Register A, the sectors per cylinder, because a two-sided unit carries that many sectors on each surface.
6238
LD (656FH),A 32 6F 65
Store Register A, the sectors per cylinder, at 656FH. The directory sizing code at 63B2H and the loop that clears the rest of the directory at 6414H both read this cell.
623B
BIT 3,(IY+03H) FD CB 03 5E
Test bit 3 of DCT+03H in the working Drive Code Table copy at 6567H, the rigid drive bit, a second time.
623F
If the Z FLAG has been set, meaning this is a floppy being given system information rather than a rigid drive, JUMP to 61CFH to copy the granule allocation map into the lock-out map, which the verify pass would normally have done.

6241H - Directory Cylinder Selection

The directory has to live on a cylinder that is entirely readable, and by preference near the middle of the disk so that the average seek to it is short. The DIR parameter overrides the choice outright. Otherwise the search starts at the middle cylinder and works outward in alternating steps of one, two, three and so on, taking the first cylinder whose byte in the granule allocation map at 6A00H is not 0FFH. This code is also the landing point for the ordinary format path, which arrives at 624AH from 61E1H.

6241
LD A,(IY+06H) FD 7E 06
Fetch DCT+06H from the working Drive Code Table copy at 656AH, the highest cylinder on the disk, into Register A.
6244
LD L,A 6F
Copy Register A, the highest cylinder, into Register L.
6245
CP (IY+06H) FD BE 06
Compare Register A, the highest cylinder, against DCT+06H in the working Drive Code Table copy at 656AH, which is the same byte it was just loaded from, so the CARRY FLAG is always left clear here.
6248
If the CARRY FLAG has been set, JUMP to 624DH. The comparison above can never set it, so this branch is never taken and control always falls into the instruction below.
624A
LD L,(IY+06H) FD 6E 06
Fetch DCT+06H from the working Drive Code Table copy at 656AH, the highest cylinder on the disk, into Register L. The ordinary format path jumps straight here from 61E1H once verification has finished.
624D
LD H,6AH 26 6A
Load Register H with 6AH, forming 6A00H plus the highest cylinder number in Register Pair HL, a pointer into the granule allocation map.
624F
GOSUB to the local stub at 6467H, which asks the drive's own driver for operation 04H, Restore, bringing the head back to cylinder 0 and setting DCT+05H in the working Drive Code Table copy at 6569H to zero.
6252
If the NZ FLAG has been set, meaning the driver returned a non-zero error code in Register A, JUMP to 64A7H to restore the machine state and report the disk error.
6255
LD BC,0000H 01 00 00
Self-Modifying Code
Load Register Pair BC with the DIR parameter, the cylinder the operator asked the directory to be placed on. The operand at 6256H is the result cell named by the DIR and D keywords in the @PARAM table at 72A8H. The listing shows the initial value 0000H.
6258
LD A,C 79
Copy Register C, the low byte of the DIR parameter, into Register A.
6259
OR B B0
OR Register B, the high byte of the DIR parameter, into Register A. If the whole word is zero the Z FLAG is set.
625A
If the Z FLAG has been set, meaning DIR was not specified, JUMP to 6262H to start the search from the middle of the disk.
625C
CP L BD
Compare Register A, the low byte of the requested directory cylinder, against Register L, the highest cylinder on the disk. If the request is below the highest cylinder the CARRY FLAG is set.
625D
If the NO CARRY FLAG has been set, meaning the requested cylinder is at or beyond the highest cylinder and therefore does not exist, JUMP to 6262H and fall back on the middle of the disk.
625F
LD L,A 6F
Copy Register A, the requested directory cylinder, into Register L, so that Register Pair HL addresses that cylinder's byte in the granule allocation map at 6A00H.
6260
JUMP to 6265H to start the search at the requested cylinder. If that cylinder turns out to be locked out the search still walks outward from it.
6262
INC L 2C
INCrement Register L by 1, turning the highest cylinder number into a count of cylinders.
6263
SRL L CB 3D
Shift Register L right, dividing the cylinder count by two. Register Pair HL now addresses the middle cylinder's byte in the granule allocation map at 6A00H.
6265
LD C,00H 0E 00
Load Register C with 00H, the step counter for the outward search.
6267
LD A,(HL) 7E
Loop Start
Fetch the candidate cylinder's byte from the granule allocation map at 6A00H plus the cylinder number, addressed by Register Pair HL, into Register A.
6268
INC A 3C
INCrement Register A by 1. A map byte of 0FFH, meaning every granule on the cylinder is unavailable because verification failed there, becomes zero and sets the Z FLAG.
6269
If the NZ FLAG has been set, meaning the map byte was not 0FFH and the cylinder has usable granules, JUMP to 6284H to adopt it as the directory cylinder.
626B
INC C 0C
INCrement Register C by 1, widening the search by one cylinder.
626C
LD A,C 79
Copy Register C, the step count, into Register A.
626D
RRCA 0F
Rotate Register A right, putting bit 0 of the step count into the CARRY FLAG. Odd steps go outward one way and even steps the other, which is what makes the search alternate.
626E
LD A,L 7D
Copy Register L, the cylinder last tried, into Register A.
626F
If the NO CARRY FLAG has been set, meaning the step count is even, JUMP to 627AH to step towards cylinder 0.
6271
ADD A,C 81
ADD Register C, the step count, to Register A, moving the candidate cylinder outward towards the highest cylinder.
6272
LD L,A 6F
Copy Register A, the new candidate cylinder, into Register L, so Register Pair HL addresses its byte in the granule allocation map.
6273
CP (IY+06H) FD BE 06
Compare Register A, the new candidate cylinder, against DCT+06H in the working Drive Code Table copy at 656AH, the highest cylinder. If they are equal the Z FLAG is set and the search has run off the outer edge.
6276
If the NZ FLAG has been set, meaning the candidate is still inside the disk, LOOP BACK to 6267H to test its map byte.
6278
JUMP to 627EH because the search has reached the highest cylinder without finding a usable one.
627A
SUB C 91
SUBtract Register C, the step count, from Register A, moving the candidate cylinder inward towards cylinder 0.
627B
LD L,A 6F
Copy Register A, the new candidate cylinder, into Register L, so Register Pair HL addresses its byte in the granule allocation map.
627C
If the NZ FLAG has been set, meaning the candidate has not reached cylinder 0, LOOP BACK to 6267H to test its map byte.
Loop End
627E
LD HL,6715H 21 15 67
Point Register Pair HL at the message at 6715H, No cylinders available for directory! terminated by 0DH. Every cylinder the search reached was locked out, so the diskette cannot carry a directory at all.
6281
JUMP to 6434H to display the message addressed by Register Pair HL, restore the machine state and abort.
6284
LD (IY+09H),L FD 75 09
Store Register L, the chosen directory cylinder, into DCT+09H of the working Drive Code Table copy at 656DH. Every later sector operation on the directory takes its cylinder from this byte, and it is the same field the resident DOS reads to find the directory of a mounted disk.
6287
LD A,L 7D
Copy Register L, the chosen directory cylinder, into Register A.
6288
LD (6009H),A 32 09 60
Store Register A, the directory cylinder, into 6009H. That is the DCT+09H position inside the ten-byte copy of the working table that 618FH parked at 6000H, so when 630BH copies those ten bytes back the directory cylinder survives.
628B
GOSUB to 64B5H to convert Register A, the directory cylinder number, into two ASCII digits, returning the tens digit in Register C and the units digit in Register B.
628E
LD (6760H),BC ED 43 60 67
Store Register Pair BC, the two ASCII digits of the directory cylinder, into 6760H. That is the two-character field at the end of the message at 673BH, which ships as Directory will be placed on cylinder 00, so the message now names the cylinder actually chosen.
6292
LD HL,673BH 21 3B 67
Point Register Pair HL at the message at 673BH, Directory will be placed on cylinder nn terminated by 0DH, with the two digits just planted into it.
6295
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL.
6298
LD HL,6786H 21 86 67
Point Register Pair HL at the message at 6786H, a line feed followed by Initializing SYSTEM information and a 03H terminator. The 03H rather than 0DH leaves the cursor on the line, which is where the progress dots go.
629B
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL.

629EH - Boot Sector Patching and Cylinder 0

Two complete boot sectors are carried in the image at 6B00H and 6C00H, both assembled to run at 4200H where the Model I boot ROM places cylinder 0 sector 0. Each has to be told which cylinder the directory landed on and how fast to step, so two bytes in each are patched before it is written. Cylinder 0 sector 0 takes the single density copy and sector 1 the double density copy, and the granule allocation map is marked to keep files off the boot area and the directory cylinder.

629E
LD HL,6A00H 21 00 6A
Point Register Pair HL at 6A00H, cylinder 0's byte in the granule allocation map.
62A1
LD A,(HL) 7E
Fetch cylinder 0's map byte from 6A00H, addressed by Register Pair HL, into Register A. The bits that are clear are the granules still available on that cylinder.
62A2
OR 01H F6 01
OR 01H into Register A, setting the bit for granule 0 of cylinder 0. That granule holds the boot sectors and BOOT/SYS, so it must never be handed out to a file.
62A4
LD (HL),A 77
Store Register A, cylinder 0's map byte with granule 0 now marked as taken, back to 6A00H.
62A5
LD A,(IY+09H) FD 7E 09
Fetch DCT+09H from the working Drive Code Table copy at 656DH, the directory cylinder chosen at 6284H, into Register A.
62A8
LD (655AH),A 32 5A 65
Store Register A, the directory cylinder, into 655AH. That is offset 16H of the DIR/SYS directory record template at 6544H, the cylinder field of its first extent, so the record will describe the directory as living on that cylinder.
62AB
LD L,A 6F
Copy Register A, the directory cylinder, into Register L. Register Pair HL now addresses that cylinder's byte in the granule allocation map at 6A00H.
62AC
LD (HL),0FFH 36 FF
Store 0FFH into the directory cylinder's granule allocation map byte. The whole cylinder is taken by the directory, so every one of its granules is marked unavailable.
62AE
LD A,(IY+09H) FD 7E 09
Fetch DCT+09H from the working Drive Code Table copy at 656DH, the directory cylinder, into Register A again.
62B1
LD (6B02H),A 32 02 6B
Store Register A, the directory cylinder, into 6B02H. That is offset 02H of the single density boot sector image, which the sector reads back as 4202H once the boot ROM has loaded it to 4200H, and it is how the boot code knows which cylinder to seek to for the directory.
62B4
LD A,(656EH) 3A 6E 65
Fetch the boot track step rate from 656EH into Register A. That byte is loaded with 03H and is overwritten by the front end at 70B9H with the value 0 to 3 taken from the STEP parameter, the configuration sector or the operator's answer.
62B7
LD B,A 47
Copy Register A, the step rate, into Register B for the merge below.
62B8
LD HL,(6900H) 2A 00 69
Fetch the word at 6900H into Register Pair HL, which is 6BBCH. That is the address inside the single density boot sector image of the byte holding its WD1771 Seek command, whose two low bits are the stepping rate.
62BB
LD A,(HL) 7E
Fetch the Seek command byte from inside the boot sector image, addressed by Register Pair HL, into Register A.
62BC
AND 0FCH E6 FC
Mask Register A with 0FCH, clearing the two low bits of the Seek command and leaving the command code itself intact.
62BE
OR B B0
OR Register B, the step rate, into Register A, building the complete Seek command. The WD1771 takes the rate from those two bits as 3, 6, 10 or 15 milliseconds.
62BF
LD (HL),A 77
Store Register A, the patched Seek command, back into the single density boot sector image at the address in Register Pair HL.
62C0
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, selecting cylinder 0 in Register D and sector 0 in Register E.
62C3
GOSUB to the local stub at 6487H, which asks the drive's own driver for operation 0AH, Verify Sector, on cylinder 0 sector 0. Nothing is written yet; this only establishes that the diskette really carries a format.
62C6
LD HL,681CH 21 1C 68
Point Register Pair HL at the message at 681CH, Can't, Diskette not formatted! terminated by 0DH. Register Pair HL is loaded before the branch is tested so that one message pointer serves the failure path.
62C9
If the NZ FLAG has been set, meaning cylinder 0 sector 0 could not be read and there is no format on the diskette, JUMP to 6437H to display the message addressed by Register Pair HL, restore the machine state and abort.
62CC
LD HL,6B00H 21 00 6B
Point Register Pair HL at 6B00H, the patched single density boot sector image.
62CF
GOSUB to the local stub at 647BH, which asks the drive's own driver for operation 0DH, Write Sector, putting the image at Register Pair HL onto cylinder 0 sector 0 with a normal data address mark.
62D2
If the Z FLAG has been set, meaning the write succeeded, GOSUB to the local stub at 6487H to verify the sector just written.
62D5
If the NZ FLAG has been set, meaning either the write or the verify failed, JUMP to 64A7H to restore the machine state and report the disk error.
62D8
GOSUB to 642FH, which displays a full stop through the ROM video driver as a progress mark on the Initializing SYSTEM information line.
62DB
LD A,(IY+09H) FD 7E 09
Fetch DCT+09H from the working Drive Code Table copy at 656DH, the directory cylinder, into Register A.
62DE
LD (6C02H),A 32 02 6C
Store Register A, the directory cylinder, into 6C02H, offset 02H of the double density boot sector image, the same field that was patched into the single density copy at 62B1H.
62E1
LD A,(656EH) 3A 6E 65
Fetch the boot track step rate from 656EH into Register A.
62E4
LD B,A 47
Copy Register A, the step rate, into Register B for the merge below.
62E5
LD HL,(6902H) 2A 02 69
Fetch the word at 6902H into Register Pair HL, which is 6C10H, the address inside the double density boot sector image of its Seek command byte.
62E8
LD A,(HL) 7E
Fetch the Seek command byte from inside the double density boot sector image, addressed by Register Pair HL, into Register A.
62E9
AND 0FCH E6 FC
Mask Register A with 0FCH, clearing the two low bits of the Seek command.
62EB
OR B B0
OR Register B, the step rate, into Register A, building the complete Seek command.
62EC
LD (HL),A 77
Store Register A, the patched Seek command, back into the double density boot sector image at the address in Register Pair HL.
62ED
LD DE,0001H 11 01 00
Load Register Pair DE with 0001H, selecting cylinder 0 in Register D and sector 1 in Register E.
62F0
LD HL,6C00H 21 00 6C
Point Register Pair HL at 6C00H, the patched double density boot sector image.
62F3
GOSUB to the local stub at 647BH, which asks the drive's own driver for operation 0DH, Write Sector, putting the image at Register Pair HL onto cylinder 0 sector 1.
62F6
If the Z FLAG has been set, meaning the write succeeded, GOSUB to the local stub at 6487H to verify the sector just written.
62F9
If the NZ FLAG has been set, meaning either the write or the verify failed, JUMP to 64A7H to restore the machine state and report the disk error.
62FC
GOSUB to 642FH to display another progress full stop.

62FFH - Granule Allocation Table Construction

The Granule Allocation Table image occupies 6A00H to 6AFFH and its offsets are the offsets of the GAT sector itself: 00H the granule allocation map, 60H the lock-out map, CCH and CDH the configuration word, CEH the master password hash, D0H the diskette name and D8H the date. The maps and the hash are already in place; this block builds the configuration word out of the drive geometry and writes the whole image to cylinder 0 sector 3, then initialises the configuration sector at cylinder 0 sector 2.

62FF
LD A,(IY+05H) FD 7E 05
Fetch DCT+05H from the working Drive Code Table copy at 6569H, the cylinder the head is standing on, into Register A. The block move below is about to overwrite that byte with the value saved before the cylinder 0 pass, so it has to be kept.
6302
LD HL,6000H 21 00 60
Point Register Pair HL at 6000H, the ten-byte copy of the working Drive Code Table entry that 618FH parked there before the single density cylinder 0 pass altered the live one.
6305
LD DE,6564H 11 64 65
Point Register Pair DE at 6564H, the working Drive Code Table copy, as the destination of the block move.
6308
LD BC,000AH 01 0A 00
Load Register Pair BC with 000AH, the ten bytes of a Drive Code Table entry.
630B
LDIR ED B0
Block move ten bytes from the source at Register Pair HL, the saved copy at 6000H, to the destination at Register Pair DE, the working Drive Code Table copy at 6564H, incrementing both pointers and counting Register Pair BC down. The density and geometry of the main format are restored, undoing the single density settings the cylinder 0 pass left behind, and the directory cylinder planted at 6009H by 6288H comes across with them.
630D
LD (IY+05H),A FD 77 05
Store Register A, the current cylinder saved at 62FFH, back into DCT+05H of the working Drive Code Table copy at 6569H, since the copy just restored carries the cylinder as it stood before the cylinder 0 pass.
6310
LD A,(IY+06H) FD 7E 06
Fetch DCT+06H from the working Drive Code Table copy at 656AH, the highest cylinder on the disk, into Register A.
6313
SUB 22H D6 22
SUBtract 22H, decimal 34, from Register A. A 35 cylinder disk has a highest cylinder of 34, so this leaves zero, and every larger disk leaves the number of cylinders it has beyond 35.
6315
LD (6ACCH),A 32 CC 6A
Store Register A into 6ACCH, the low byte of the Granule Allocation Table configuration word at GAT offset CCH. The DOS reads it back as the cylinder count less 35.
6318
LD A,(IY+04H) FD 7E 04
Fetch DCT+04H from the working Drive Code Table copy at 6568H into Register A, the byte whose bit 5 is the double-sided flag.
631B
AND 20H E6 20
Mask Register A with 20H, keeping only bit 5, the double-sided flag.
631D
LD B,A 47
Copy Register A, the isolated double-sided bit, into Register B where the configuration byte is being accumulated.
631E
LD A,(IY+03H) FD 7E 03
Fetch DCT+03H from the working Drive Code Table copy at 6567H into Register A, the byte whose bit 6 is the double density flag.
6321
AND 40H E6 40
Mask Register A with 40H, keeping only bit 6, the double density flag.
6323
OR B B0
OR Register B, the double-sided bit, into Register A, so Register A now carries both flags.
6324
LD B,A 47
Copy Register A, the two flags so far, back into Register B.
6325
LD A,(IY+08H) FD 7E 08
Fetch DCT+08H from the working Drive Code Table copy at 656CH into Register A. Bits 7 to 5 of that byte are the number of granules on a track and bits 4 to 0 are the sectors in a granule.
6328
RLCA 07
Rotate Register A left, moving the granules per track field down towards bit 0.
6329
RLCA 07
Rotate Register A left again.
632A
RLCA 07
Rotate Register A left a third time.
632B
AND 07H E6 07
Mask Register A with 07H, leaving just the granules per track value in bits 2 to 0.
632D
LD (63C2H),A 32 C2 63
Self-Modifying Code
Store Register A, the granules per track, into 63C2H, the operand of LD A,00H at 63C1H. That instruction builds the extent byte of the DIR/SYS directory record, which has to say how many granules the directory occupies. The listing shows the initial value 00H.
6330
OR B B0
OR Register B, the double density and double-sided flags, into Register A.
6331
OR 88H F6 88
OR 88H into Register A, setting bits 7 and 3. These two are always set by FORMAT in the configuration word it writes.
6333
OR 00H F6 00
Self-Modifying Code
OR the operand at 6334H into Register A. That byte is 00H for an ordinary disk and is set to 10H by 61ACH when the SYSTEM parameter has caused cylinder 0 to be re-formatted in single density, so bit 4 of the configuration word records a dual density disk. The listing shows the initial value 00H.
6335
LD (6ACDH),A 32 CD 6A
Store Register A into 6ACDH, the high byte of the Granule Allocation Table configuration word at GAT offset CDH.
6338
LD HL,6A00H 21 00 6A
Point Register Pair HL at 6A00H, the base of the completed Granule Allocation Table image.
633B
LD D,00H 16 00
Load Register D with 00H, selecting cylinder 0 for the write below.
633D
LD E,03H 1E 03
Load Register E with 03H, selecting sector 3 of cylinder 0. Cylinder 0 sectors 2 and 3 are the system information sectors the DOS keeps on drive 0.
633F
GOSUB to the local stub at 647BH, which asks the drive's own driver for operation 0DH, Write Sector, putting the Granule Allocation Table image at Register Pair HL onto cylinder 0 sector 3 with a normal data address mark.
6342
If the Z FLAG has been set, meaning the write succeeded, GOSUB to the local stub at 6487H to verify the sector just written.
6345
If the NZ FLAG has been set, meaning either the write or the verify failed, JUMP to 64A7H to restore the machine state and report the disk error.
6348
GOSUB to 642FH to display another progress full stop.
634B
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H, the 256-byte sector buffer. It last held the sector-identifier pointer table the track expander built, so it has to be cleared before it can be used as a sector image.
634E
LD (HL),00H 36 00
Loop Start
Store 00H into the sector buffer at the address in Register Pair HL.
6350
INC L 2C
INCrement Register L by 1, advancing within the page only, so the pointer wraps from 6DFFH back to 6D00H and sets the Z FLAG.
6351
If the NZ FLAG has been set, meaning Register L has not yet wrapped round to zero, LOOP BACK to 634EH to clear the next byte. All 256 bytes of the buffer end up zero.
Loop End
6353
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H, the first byte of the now cleared sector buffer.
6356
LD (HL),53H 36 53
Store 53H (ASCII: S) into the first byte of the sector buffer at the address in Register Pair HL. That byte marks the sector as a system configuration sector, and it is the byte other utilities test to decide whether a pack is a bootable system disk.
6358
LD DE,0002H 11 02 00
Load Register Pair DE with 0002H, selecting cylinder 0 in Register D and sector 2 in Register E. This is the configuration sector FORMAT itself reads at 6E11H, and per-drive default cylinder counts and step rates live in it.
635B
GOSUB to the local stub at 647BH, which asks the drive's own driver for operation 0DH, Write Sector, putting the buffer at Register Pair HL onto cylinder 0 sector 2.
635E
If the Z FLAG has been set, meaning the write succeeded, GOSUB to the local stub at 6487H to verify the sector just written.
6361
If the Z FLAG has been set, meaning the verify also succeeded, GOSUB to 642FH to display another progress full stop. The Z FLAG survives the call and is tested again by the next instruction that follows.

6364H - Directory Cylinder: GAT, HIT and the Directory Records

Every sector of the directory cylinder is written with a DELETED data address mark, which is what marks a cylinder as the directory. The local stub at 647FH issues that write and the helper at 6497H checks it, and because a deleted mark reads back as status error 06H that helper treats 06H as success and a clean read as failure. Sector 0 takes the Granule Allocation Table, sector 1 the Hash Index Table, sector 2 the BOOT/SYS record, sector 3 the DIR/SYS record and every remaining sector an empty directory sector.

6364
LD HL,6A00H 21 00 6A
Point Register Pair HL at 6A00H, the base of the completed Granule Allocation Table image.
6367
LD D,(IY+09H) FD 56 09
Fetch DCT+09H from the working Drive Code Table copy at 656DH, the directory cylinder, into Register D, the cylinder for the write below.
636A
LD E,00H 1E 00
Load Register E with 00H, selecting sector 0 of the directory cylinder. Sector 0 is always the Granule Allocation Table.
636C
If the Z FLAG has been set, meaning everything up to here has succeeded, GOSUB to the local stub at 647FH, which asks the drive's own driver for operation 0EH, Write Sector with a deleted data address mark.
636F
If the Z FLAG has been set, GOSUB to 6497H, which verifies the sector and accepts only the deleted data address mark status as success.
6372
LD A,2BH 3E 2B
Load Register A with 2BH (ASCII: +), the progress mark used for the directory cylinder rather than the full stop used for cylinder 0.
6374
If the Z FLAG has been set, meaning the deleted mark verify succeeded, GOSUB to the ROM video driver at 0033H to display the character in Register A.
6377
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H, the sector buffer, which is still entirely zero apart from the 53H that 6356H put in its first byte.
637A
LD (HL),0A2H 36 A2
Store 0A2H into the first byte of the sector buffer at the address in Register Pair HL. This is the Hash Index Table entry for BOOT/SYS, the one byte hash of its packed name and extension, which occupies the slot for directory record 0.
637C
INC HL 23
INCrement Register Pair HL by 1, moving to the second byte of the sector buffer.
637D
LD (HL),0C4H 36 C4
Store 0C4H into the second byte of the sector buffer at the address in Register Pair HL, the Hash Index Table entry for DIR/SYS in the slot for directory record 1. Every other slot stays zero, which is what marks a directory slot as free.
637F
DEC HL 2B
DECrement Register Pair HL by 1, putting it back to 6D00H, the base of the sector buffer, ready for the write.
6380
LD D,(IY+09H) FD 56 09
Fetch DCT+09H from the working Drive Code Table copy at 656DH, the directory cylinder, into Register D.
6383
LD E,01H 1E 01
Load Register E with 01H, selecting sector 1 of the directory cylinder, which is always the Hash Index Table.
6385
If the Z FLAG has been set, GOSUB to the local stub at 647FH to write the Hash Index Table with a deleted data address mark.
6388
If the Z FLAG has been set, GOSUB to 6497H to verify that the deleted mark was written.
638B
If the NZ FLAG has been set, meaning any of the writes or verifies failed, JUMP to 64A7H to restore the machine state and report the disk error.
638E
LD A,2BH 3E 2B
Load Register A with 2BH (ASCII: +), the directory cylinder progress mark.
6390
GOSUB to the ROM video driver at 0033H to display the character in Register A.
6393
LD DE,6D00H 11 00 6D
Point Register Pair DE at 6D00H, the sector buffer, as the destination of the block move below.
6396
LD HL,6524H 21 24 65
Point Register Pair HL at 6524H, the 32-byte BOOT/SYS directory record template, as the source of the block move.
6399
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H, the 32 bytes of one LDOS directory record.
639C
LDIR ED B0
Block move the 32-byte BOOT/SYS directory record from the source at Register Pair HL to the destination at Register Pair DE, the head of the sector buffer, incrementing both pointers and counting Register Pair BC down. The remaining 224 bytes of the buffer are still zero, so the other seven records in the sector are free slots.
639E
LD D,(IY+09H) FD 56 09
Fetch DCT+09H from the working Drive Code Table copy at 656DH, the directory cylinder, into Register D.
63A1
LD E,02H 1E 02
Load Register E with 02H, selecting sector 2 of the directory cylinder, the first sector of directory records.
63A3
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H, the sector buffer holding the BOOT/SYS record.
63A6
GOSUB to the local stub at 647FH to write the sector with a deleted data address mark.
63A9
If the Z FLAG has been set, GOSUB to 6497H to verify that the deleted mark was written.
63AC
If the NZ FLAG has been set, meaning the write or the verify failed, JUMP to 64A7H to restore the machine state and report the disk error.
63AF
GOSUB to 642FH to display a progress full stop.
63B2
LD A,(656FH) 3A 6F 65
Fetch the sectors per cylinder from 656FH, stored there at 6023H or 6238H, into Register A. The directory occupies one whole cylinder, so this is also the number of sectors the DIR/SYS file contains.
63B5
OR A B7
OR Register A with itself to set the flags. If the sectors per cylinder is zero the Z FLAG is set.
63B6
If the Z FLAG has been set, meaning the sectors per cylinder came out as zero, JUMP to 63BCH to use the ceiling value instead.
63B8
CP 22H FE 22
Compare Register A, the sectors per cylinder, against 22H, decimal 34, the largest number of directory records LDOS will admit in one directory. If the disk has fewer the CARRY FLAG is set.
63BA
If the CARRY FLAG has been set, meaning the cylinder holds fewer than 34 sectors, JUMP to 63BEH and use the real figure.
63BC
LD A,22H 3E 22
Load Register A with 22H, decimal 34, capping the directory at 34 sectors however large the cylinder is.
63BE
LD (6558H),A 32 58 65
Store Register A into 6558H, offset 14H of the DIR/SYS directory record template at 6544H, which is the low byte of the ending record number. The record now says how many sectors long the directory file is.
63C1
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the granules per track. The operand at 63C2H was planted at 632DH out of DCT+08H bits 7 to 5. The listing shows the initial value 00H.
63C3
BIT 5,(IY+04H) FD CB 04 6E
Test bit 5 of DCT+04H in the working Drive Code Table copy at 6568H, the double-sided flag. If the disk is two sided the Z FLAG is cleared.
63C7
If the Z FLAG has been set, meaning the disk is single sided, JUMP to 63CBH with the granules per track unchanged.
63C9
ADD A,A 87
Double Register A, the granules per track, because a two-sided cylinder carries that many granules on each surface.
63CA
INC A 3C
INCrement Register A by 1. The extent byte stores the granule count less one in its low five bits, and doubling an already reduced count needs this correction to give the right total.
63CB
LD (655BH),A 32 5B 65
Store Register A into 655BH, offset 17H of the DIR/SYS directory record template at 6544H. That byte packs the starting granule in bits 7 to 5 and the granule count less one in bits 4 to 0, so the directory is described as starting at granule 0 of its cylinder and running the whole way across it.
63CE
LD A,(IY+09H) FD 7E 09
Fetch DCT+09H from the working Drive Code Table copy at 656DH, the directory cylinder, into Register A.
63D1
LD (655AH),A 32 5A 65
Store Register A into 655AH, offset 16H of the DIR/SYS directory record template at 6544H, the cylinder field of its first extent.
63D4
LD HL,6544H 21 44 65
Point Register Pair HL at 6544H, the 32-byte DIR/SYS directory record template, now with its ending record number and extent filled in, as the source of the block move below.
63D7
LD DE,6D00H 11 00 6D
Point Register Pair DE at 6D00H, the sector buffer, as the destination of the block move.
63DA
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H, the 32 bytes of one LDOS directory record.
63DD
LDIR ED B0
Block move the 32-byte DIR/SYS record from the source at Register Pair HL to the destination at Register Pair DE, the head of the sector buffer, incrementing both pointers and counting Register Pair BC down. It lands over the BOOT/SYS record left there from the previous write; the rest of the buffer is still zero.
63DF
LD D,(IY+09H) FD 56 09
Fetch DCT+09H from the working Drive Code Table copy at 656DH, the directory cylinder, into Register D.
63E2
LD E,03H 1E 03
Load Register E with 03H, selecting sector 3 of the directory cylinder, the second sector of directory records.
63E4
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H, the sector buffer holding the DIR/SYS record.
63E7
GOSUB to the local stub at 647FH to write the sector with a deleted data address mark.
63EA
If the Z FLAG has been set, GOSUB to 6497H to verify that the deleted mark was written.
63ED
If the NZ FLAG has been set, meaning the write or the verify failed, JUMP to 64A7H to restore the machine state and report the disk error.
63F0
GOSUB to 642FH to display a progress full stop.
63F3
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H, the head of the sector buffer.
63F6
LD B,20H 06 20
Load Register B with 20H, decimal 32, the length of the directory record that has to be cleared out of the buffer.
63F8
LD (HL),00H 36 00
Loop Start
Store 00H into the sector buffer at the address in Register Pair HL.
63FA
INC HL 23
INCrement Register Pair HL by 1, advancing the clear pointer.
63FB
DECrement Register B and LOOP BACK to 63F8H while it is not zero. Only the first 32 bytes need clearing because the remaining 224 have been zero since 634EH, and the buffer is now an entirely empty directory sector.
Loop End
63FD
LD D,(IY+09H) FD 56 09
Fetch DCT+09H from the working Drive Code Table copy at 656DH, the directory cylinder, into Register D.
6400
LD E,04H 1E 04
Load Register E with 04H, the first directory sector that carries no records at all.
6402
LD HL,6D00H 21 00 6D
Loop Start
Point Register Pair HL at 6D00H, the sector buffer, which now holds 256 zero bytes.
6405
GOSUB to the local stub at 647FH to write the empty directory sector with a deleted data address mark.
6408
If the Z FLAG has been set, GOSUB to 6497H to verify that the deleted mark was written.
640B
If the NZ FLAG has been set, meaning the write or the verify failed, JUMP to 64A7H to restore the machine state and report the disk error.
640E
PUSH DE D5
Save Register Pair DE, holding the directory cylinder in Register D and the sector number in Register E, onto the stack across the display call.
640F
GOSUB to 642FH to display a progress full stop.
6412
POP DE D1
Restore Register Pair DE, with the directory cylinder and sector number, from the stack.
6413
INC E 1C
INCrement Register E by 1, moving on to the next sector of the directory cylinder.
6414
LD A,(656FH) 3A 6F 65
Fetch the sectors per cylinder from 656FH into Register A, the number of sectors the directory cylinder holds.
6417
CP E BB
Compare Register A, the sectors per cylinder, against Register E, the next sector number. If they are equal the Z FLAG is set and the whole cylinder has been written.
6418
If the NZ FLAG has been set, meaning sectors remain on the directory cylinder, LOOP BACK to 6402H to write the next empty directory sector.
Loop End

641AH - Normal Exit

The diskette is complete. The machine state taken over at 6007H is handed back, the two closing messages are put out and control returns to the DOS through @EXIT with a success code. The second message is not decoration: the whole format ran with a RET planted over the interrupt vector, so the real time clock has lost every tick that fell in that period.

641A
GOSUB to 6440H, which puts the saved byte back at 4012H, re-enables interrupts and, when the format was on drive 0, asks the operator to put the system diskette back.
641D
LD HL,67CEH 21 CE 67
Point Register Pair HL at the message at 67CEH, a line feed followed by Formatting complete and a 0DH terminator.
6420
GOSUB to the resident @LOGOT vector at 447BH, which sends the carriage return terminated string addressed by Register Pair HL to the video display.
6423
LD HL,67E3H 21 E3 67
Point Register Pair HL at the message at 67E3H, a line feed followed by Note: Realtime clock no longer accurate and a 0DH terminator.
6426
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL.
6429
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H, the success code every LDOS command returns; 0FFFFH would mean an error.
642C
JUMP to the resident @EXIT vector at 402DH, returning to the DOS with the success code in Register Pair HL.

642FH - Progress Dot, Abort and Machine State Restore

Three short routines that everything else in the engine leans on. 6434H is the common abort, 6437H a second entry for callers that already have their own message in Register Pair HL, and 6440H undoes the interrupt lock-out taken at 6007H and, on drive 0, asks for the system diskette back before anything tries to read a SYS file again.

642F
LD A,2EH 3E 2E
Load Register A with 2EH (ASCII: .), the progress mark used while cylinder 0 and the system information are being written.
6431
JUMP to the ROM video driver at 0033H to display the character in Register A and RETurn to whoever called this routine.
6434
LD HL,680CH 21 0C 68
Point Register Pair HL at the message at 680CH, Format aborted! terminated by 0DH. Callers with a message of their own jump to the next instruction instead and leave their own pointer in Register Pair HL.
6437
GOSUB to the resident @LOGOT vector at 447BH, which sends the carriage return terminated string addressed by Register Pair HL to the video display.
643A
GOSUB to 6440H to put the interrupt vector back, re-enable interrupts and prompt for the system diskette if the format was on drive 0.
643D
JUMP to the resident @ABORT vector at 4030H, which returns to the DOS along the failure path and, when a job is running under JCL, ends the job.
6440
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the byte that was found at 4012H when the engine started. The operand at 6441H was planted at 600BH with the original first byte of the RST 38H interrupt hook, which is normally 0C3H. The listing shows the initial value 00H, which is also what it holds if the engine never ran.
6442
OR A B7
OR Register A with itself to set the flags. If the saved byte is zero, meaning nothing was ever saved, the Z FLAG is set.
6443
If the Z FLAG has been set, meaning there is no saved interrupt vector byte to put back, JUMP to 6449H.
6445
EI FB
Enable interrupts. From here the timer tick can run again.
6446
LD (4012H),A 32 12 40
Store Register A, the original first byte of the RST 38H interrupt hook, back at 4012H, undoing the RET that 6010H planted there and letting the clock and task chain run once more.
6449
LD A,(648CH) 3A 8C 64
Fetch the target drive number from 648CH into Register A. 648CH is the operand of LD C,0FFH at 648BH in the driver call tail, and the front end plants the drive number there at 6E51H or 6E98H.
644C
INC A 3C
INCrement Register A by 1. If the drive byte is still the shipped 0FFH the result is zero and the Z FLAG is set.
644D
RET Z C8
If the Z FLAG has been set, meaning no drive was ever selected, RETurn without prompting.
644E
DEC A 3D
DECrement Register A by 1, putting the drive number back.
644F
If the NZ FLAG has been set, meaning the format was not on drive 0, JUMP to 645EH. Only drive 0 needs the system diskette back, because that is where the DOS reads its overlays from.
6451
LD HL,67A8H 21 A8 67
Point Register Pair HL at the message at 67A8H, a line feed followed by Load SYSTEM diskette and hit <ENTER> and a 0DH terminator.
6454
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL.
6457
Loop Start
GOSUB to the ROM routine at 0049H, which waits for a key and returns it in Register A.
645A
CP 0DH FE 0D
Compare Register A, the key just pressed, against 0DH, the ENTER key. If Register A equals 0DH the Z FLAG is set.
645C
If the NZ FLAG has been set, meaning something other than ENTER was pressed, LOOP BACK to 6457H and wait again.
Loop End
645E
JUMP to the local stub at 6467H, which asks the drive's own driver for operation 04H, Restore, bringing the head back to cylinder 0 and RETurning to whoever called this routine.

6460H - Local Disk Driver Call Stubs

FORMAT never goes through the resident disk primitives at 4750H to 478DH. Instead it reaches the drive's own driver directly, through the JP instruction that occupies DCT+00H to DCT+02H of the ten-byte working copy Register Pair IY points at. Eleven two-byte stubs load an operation code and fall into the common tail, which passes the operation in Register B and the drive in Register C. Working on a private copy of the table is what lets the engine flip the side select bit, rewrite the geometry bytes and count cylinders without the DOS ever seeing it.

6460
XOR A AF
Set Register A to ZERO and clear all flags, selecting driver operation 00H. The resident dispatcher answers that operation with an immediate RET leaving Register A zero and the Z FLAG set, and an absent unit holds a RET rather than a JP at DCT+00H so it comes back with the 20H the tail loads and the NZ FLAG. The stub is therefore a drive-present test.
6461
JUMP to the common tail at 6489H.
6463
LD A,01H 3E 01
Load Register A with 01H, driver operation 01H, Select Drive. The driver builds the drive select and side value from DCT+03H bit 4 and DCT+04H and writes it to the latch at 37E1H.
6465
JUMP to the common tail at 6489H.
6467
LD A,04H 3E 04
Load Register A with 04H, driver operation 04H, Restore. The driver sets DCT+05H to zero and issues the WD1771 Restore command, which steps out until the track 0 line comes true.
6469
JUMP to the common tail at 6489H.
646B
LD A,05H 3E 05
Load Register A with 05H, driver operation 05H, Step In. The driver increments DCT+05H and issues the WD1771 Step In command with the stepping rate taken from DCT+03H bits 1 and 0.
646D
JUMP to the common tail at 6489H.
646F
LD A,07H 3E 07
Load Register A with 07H, driver operation 07H, wait for the controller to leave the busy state. The driver rewrites the drive select latch at 37E1H while it waits so the drive stays selected and the motor keeps turning.
6471
JUMP to the common tail at 6489H.
6473
LD A,0FH 3E 0F
Load Register A with 0FH, driver operation 0FH, Write Track. The driver turns it into WD1771 command 0F4H and streams the buffer at Register Pair HL into the data register at 37EFH until the index pulse ends the operation.
6475
JUMP to the common tail at 6489H.
6477
LD A,0CH 3E 0C
Load Register A with 0CH, driver operation 0CH. Only 621BH uses it, and only on a unit whose DCT+04H bit 4 marks it as a rigid drive, so the driver answering it is the hard disk's own. The resident floppy engine would treat any code above 0AH and below 0EH as a plain Write Sector.
6479
JUMP to the common tail at 6489H.
647B
LD A,0DH 3E 0D
Load Register A with 0DH, driver operation 0DH, Write Sector. The driver issues WD1771 command 0A8H, writing a normal data address mark.
647D
JUMP to the common tail at 6489H.
647F
LD A,0EH 3E 0E
Load Register A with 0EH, driver operation 0EH, Write Sector with a deleted data address mark. The driver issues WD1771 command 0ABH. Every sector of the directory cylinder is written this way, which is how the DOS recognises a directory sector.
6481
JUMP to the common tail at 6489H.
6483
LD A,09H 3E 09
Load Register A with 09H, driver operation 09H, Read Sector. The driver issues WD1771 command 88H and fills the buffer at Register Pair HL.
6485
JUMP to the common tail at 6489H.
6487
LD A,0AH 3E 0A
Load Register A with 0AH, driver operation 0AH, Verify Sector. The driver reads the sector and discards the data, so no buffer is needed.
6489
PUSH BC C5
Save Register Pair BC onto the stack. Register Pair BC is the interleave pointer during the format pass and the expected sector list pointer during the verify pass, and both halves are about to be overwritten.
648A
LD B,A 47
Copy Register A, the operation code chosen by whichever stub was entered, into Register B, which is where the driver's dispatcher expects to find it.
648B
LD C,0FFH 0E FF
Self-Modifying Code
Load Register C with the target drive number. The operand at 648CH ships as 0FFH and is planted by the front end at 6E51H or 6E98H once the drive has been parsed off the command line or asked for. The restore routine at 6449H reads the same cell to decide whether the format was on drive 0. The listing shows the shipped value 0FFH.
648D
LD A,20H 3E 20
Load Register A with 20H. The resident Select Drive routine reads bit 7 of Register A as a request to wait for the motor to come up to speed, and 20H leaves that bit clear because the motor has been running since the pass began.
648F
OR A B7
OR Register A with itself to set the flags. Register A holds 20H so this clears the CARRY FLAG and leaves the NZ FLAG, which is the state an absent unit's RET at DCT+00H hands straight back.
6490
GOSUB to 6495H, which jumps into the working Drive Code Table copy. A CALL is used rather than a jump so that the driver's RET comes back here and Register Pair BC can be restored.
6493
POP BC C1
Restore Register Pair BC from the stack, putting the interleave or expected sector pointer back.
6494
RET C9
RETurn to the caller with the driver's error code in Register A and the Z FLAG set if the operation succeeded.
6495
JP (IY) FD E9
JUMP to the address in Register Pair IY, which is 6564H, the first byte of the working Drive Code Table copy. On a fitted unit those three bytes are a JP to the drive's driver, so this reaches the resident floppy engine at 45FBH, or whatever replacement driver FDUBL or a hard disk driver has written into DCT+01H and DCT+02H.

6497H - Deleted Mark Verify, Error Exits and Small Helpers

6497H is the verify used after every directory sector write, and it is inverted with respect to the ordinary one: a directory sector is written with a DELETED data address mark, which reads back as status error 06H, so 06H is the only answer it accepts and a clean read is a failure. The rest of the block is the error exit, the decimal converter, the prompt and read routine, the two backspace cursor update, and the routine that applies a track format descriptor.

6497
GOSUB to the local stub at 6487H, which asks the drive's own driver for operation 0AH, Verify Sector, on the sector just written.
649A
If the Z FLAG has been set, meaning the sector read back cleanly with no error at all, JUMP to 64A2H. For a directory sector that is the wrong answer, because the deleted data address mark should have produced a status of its own.
649C
SUB 06H D6 06
SUBtract 06H from Register A, the driver's error code. 06H is the error the status decoder produces from bit 5 of the WD1771 status register, the record type bit, which is set when the sector carries a deleted data address mark. If Register A held 06H the result is zero and the Z FLAG is set.
649E
RET Z C8
If the Z FLAG has been set, meaning the sector read back with a deleted data address mark exactly as intended, RETurn with the Z FLAG set to report success.
649F
ADD A,06H C6 06
ADD 06H back to Register A, restoring the driver's original error code so the caller can report it. The result is non-zero, so the NZ FLAG is left set.
64A1
RET C9
RETurn with the original error code in Register A and the NZ FLAG set to report failure.
64A2
OR 01H F6 01
OR 01H into Register A, which holds zero because the verify was clean. The result is 01H, which sets the NZ FLAG and is what turns a clean read of a directory sector into a failure.
64A4
LD A,00H 3E 00
Load Register A with 00H. A load does not touch the flags, so the NZ FLAG set by the instruction above survives while Register A goes back to zero for the error reporting path.
64A6
RET C9
RETurn with Register A zero and the NZ FLAG set, reporting that the deleted data address mark did not take.
64A7
PUSH AF F5
Save Register Pair AF, holding the driver's error code, onto the stack across the restore call below.
64A8
GOSUB to 6440H to put the interrupt vector back, re-enable interrupts and prompt for the system diskette if the format was on drive 0. This has to happen before @ERROR is reached, because @ERROR may need to load the SYS4 overlay to find the message text.
64AB
POP AF F1
Restore Register Pair AF, with the error code back in Register A, from the stack.
64AC
OR 40H F6 40
OR 40H into Register A, setting bit 6 of the error code. In the LDOS error convention bits 0 to 5 are the error number, bit 6 asks for the message to be displayed, and bit 7 asks to return to the caller rather than abort.
64AE
JUMP to the resident @ERROR vector at 4409H to display the message for the error code in Register A.
64B1
LD A,2CH 3E 2C
Load Register A with 2CH, decimal 44, the LDOS parameter error. The front end jumps here when a drive specification or a parameter value cannot be used.
64B3
JUMP to 64ACH to set bit 6 of the error code in Register A and report it through @ERROR.
64B5
LD C,30H 0E 30
Load Register C with 30H (ASCII: 0), the tens digit accumulator, which counts up from the character zero.
64B7
SUB 0AH D6 0A
Loop Start
SUBtract 0AH, decimal 10, from Register A, the value being converted. If Register A goes below zero the CARRY FLAG is set.
64B9
If the CARRY FLAG has been set, meaning the last subtraction took the value below zero, JUMP to 64BEH to turn the remainder back into a digit.
64BB
INC C 0C
INCrement Register C by 1, counting another ten and stepping the tens digit on by one character.
64BC
LOOP BACK to 64B7H to take another ten off.
Loop End
64BE
ADD A,3AH C6 3A
ADD 3AH to Register A. That is 30H to make it a character plus 0AH to undo the subtraction that went below zero, so Register A becomes the units digit as ASCII.
64C0
LD B,A 47
Copy Register A, the units digit, into Register B. The routine returns the tens digit in Register C and the units digit in Register B.
64C1
RET C9
RETurn with the two ASCII digits in Register C and Register B.
64C2
GOSUB to the resident @DSPLY vector at 4467H to display the prompt addressed by Register Pair HL.
64C5
LD B,03H 06 03
Load Register B with 03H, the number of characters this entry allows, which is enough for a two digit answer or a single letter.
64C7
JUMP to 64CBH, the shared body of the prompt and read routine.
64C9
LD B,08H 06 08
Load Register B with 08H, the number of characters the other entry allows, which is the length of a diskette name or a master password. This entry does not display a prompt of its own.
64CB
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H, the 256-byte buffer, which serves as the keyboard line buffer here.
64CE
GOSUB to the ROM line input routine at 0040H with Register Pair HL addressing the buffer and Register B holding the maximum length. It returns with the CARRY FLAG set if BREAK was pressed and Register B holding the number of characters accepted.
64D1
If the CARRY FLAG has been set, meaning BREAK was pressed, JUMP to 6434H to display Format aborted! and abort.
64D4
LD A,B 78
Copy Register B, the number of characters the operator typed, into Register A.
64D5
OR A B7
OR Register A with itself to set the flags. If nothing was typed the Z FLAG is set.
64D6
RET Z C8
If the Z FLAG has been set, meaning the operator pressed ENTER alone, RETurn with the Z FLAG set so the caller can fall back on its default.
64D7
PUSH AF F5
Save Register Pair AF onto the stack so that the character count and the NZ FLAG survive the folding loop.
64D8
PUSH BC C5
Save Register Pair BC, holding the character count in Register B, onto the stack.
64D9
PUSH HL E5
Save Register Pair HL, addressing the head of the buffer at 6D00H, onto the stack.
64DA
LD A,(HL) 7E
Loop Start
Fetch the next character of the reply from the buffer, addressed by Register Pair HL, into Register A.
64DB
CP 61H FE 61
Compare Register A, the character, against 61H (ASCII: a). If it is below that the CARRY FLAG is set and the character is not a lower case letter.
64DD
If the CARRY FLAG has been set, meaning the character is below lower case a, JUMP to 64E2H and leave it alone.
64DF
SUB 20H D6 20
SUBtract 20H from Register A, folding a lower case letter to upper case.
64E1
LD (HL),A 77
Store Register A, the folded character, back into the buffer at the address in Register Pair HL.
64E2
INC HL 23
INCrement Register Pair HL by 1, moving on to the next character of the reply.
64E3
DECrement Register B, the character count, and LOOP BACK to 64DAH while it is not zero.
Loop End
64E5
POP HL E1
Restore Register Pair HL, addressing the head of the buffer at 6D00H, from the stack.
64E6
POP BC C1
Restore Register Pair BC, with the character count in Register B, from the stack.
64E7
POP AF F1
Restore Register Pair AF from the stack, which puts the NZ FLAG back so the caller knows something was typed.
64E8
RET C9
RETurn with Register Pair HL addressing the folded reply at 6D00H, Register B holding its length and the NZ FLAG set.
64E9
LD A,08H 3E 08
Load Register A with 08H, the backspace character.
64EB
GOSUB to the ROM video driver at 0033H to display the character in Register A, moving the cursor one place left.
64EE
LD A,08H 3E 08
Load Register A with 08H, the backspace character, a second time.
64F0
GOSUB to the ROM video driver at 0033H to display the character in Register A, so the cursor now stands on the first of the two digits.
64F3
LD A,C 79
Copy Register C, the tens digit produced by 64B5H, into Register A.
64F4
GOSUB to the ROM video driver at 0033H to display the character in Register A, overwriting the old tens digit.
64F7
LD A,B 78
Copy Register B, the units digit produced by 64B5H, into Register A.
64F8
JUMP to the ROM video driver at 0033H to display the character in Register A, overwriting the old units digit, and RETurn to whoever called this routine.
64FB
LD A,(7031H) 3A 31 70
Self-Modifying Code
Load Register A with the default number of cylinders for this drive. The operand at 7031H is planted by the front end at 6EC3H out of the per-drive byte lifted from the configuration sector. The listing shows the initial value 00H.
64FE
BIT 5,(IY+03H) FD CB 03 6E
Test bit 5 of DCT+03H in the working Drive Code Table copy at 6567H, the eight inch drive flag. If the drive is eight inch the Z FLAG is cleared.
6502
If the Z FLAG has been set, meaning the drive is five and a quarter inch, JUMP to 6506H with the configuration sector's cylinder count in Register A.
6504
LD A,4DH 3E 4D
Load Register A with 4DH, decimal 77. An eight inch diskette always has 77 cylinders, so the configuration sector's figure is discarded.
6506
DEC A 3D
DECrement Register A by 1, turning a count of cylinders into the highest cylinder number, because cylinders are numbered from zero.
6507
LD (IY+06H),A FD 77 06
Store Register A, the highest cylinder number, into DCT+06H of the working Drive Code Table copy at 656AH. Both the format pass and the verify pass stop when the current cylinder reaches this value.
650A
LD E,(HL) 5E
Fetch the low byte of the first DEFW of the descriptor addressed by Register Pair HL into Register E. 61A0H enters the routine here so that the highest cylinder is left alone while the template is switched to single density for cylinder 0.
650B
INC HL 23
INCrement Register Pair HL by 1, advancing the descriptor pointer.
650C
LD D,(HL) 56
Fetch the high byte of that DEFW into Register D. Register Pair DE now holds the address of the packed track image template.
650D
INC HL 23
INCrement Register Pair HL by 1, advancing the descriptor pointer to the geometry word.
650E
LD (6014H),DE ED 53 14 60
Self-Modifying Code
Store Register Pair DE, the template address, into 6014H, the operand of LD DE,0000H at 6013H. That is the instruction the expander starts from, so this is what selects which of the four templates gets laid on the disk.
6512
LD E,(HL) 5E
Fetch the low byte of the geometry word into Register E.
6513
INC HL 23
INCrement Register Pair HL by 1, advancing the descriptor pointer.
6514
LD D,(HL) 56
Fetch the high byte of the geometry word into Register D. Register Pair DE now holds the pair of geometry bytes that describe this format, such as 2409H for five and a quarter inch single density.
6515
INC HL 23
INCrement Register Pair HL by 1, advancing the descriptor pointer to the memory requirement word.
6516
LD (IY+07H),E FD 73 07
Store Register E into DCT+07H of the working Drive Code Table copy at 656BH. Bits 4 to 0 of that byte are the highest sector number on a track, which the driver's seek routine reads to work out how many sectors there are per side.
6519
LD (IY+08H),D FD 72 08
Store Register D into DCT+08H of the working Drive Code Table copy at 656CH. Bits 7 to 5 are the granules on a track and bits 4 to 0 the sectors in a granule, which is what the directory record extent byte is built from.
651C
LD E,(HL) 5E
Fetch the low byte of the memory requirement word into Register E.
651D
INC HL 23
INCrement Register Pair HL by 1, advancing the descriptor pointer.
651E
LD D,(HL) 56
Fetch the high byte of the memory requirement word into Register D. Register Pair DE now holds the number of bytes above 6E00H the expanded track image needs.
651F
LD (7156H),DE ED 53 56 71
Self-Modifying Code
Store Register Pair DE, the memory requirement, into 7156H, the operand of LD DE,0000H at 7155H. The front end adds it to 6E00H and compares the result against HIGH$ before it lets the format start.
6523
RET C9
RETurn to the caller with the descriptor applied.

6524H - BOOT/SYS and DIR/SYS Directory Record Templates

Two 32-byte LDOS directory records held ready in the image. FORMAT copies each into the sector buffer at 6D00H and writes it to the directory cylinder, so these are the only two files that exist on a freshly formatted diskette. Both are marked system and invisible and both carry the same update password hash, which is why neither shows in an ordinary directory listing and neither can be killed by accident. Three fields of the DIR/SYS record are filled in at run time once the directory cylinder and the geometry are known.

6524
DEFB 5EH 5E
Directory record attribute byte for BOOT/SYS. Bit 7 clear marks it a primary entry, bit 6 set marks it a SYSTEM file, bit 4 set marks the slot in use, bit 3 set marks it invisible, and bits 2 to 0 give it access level 6.
6525-6528
DEFB 00H, 00H, 00H, 00H 00 00 00 00
Flags and modification month, remaining date, end of file byte offset and logical record length. All zero: a newly formatted disk carries no date stamp and BOOT/SYS is a whole-sector file.
6529-6530
DEFM 'BOOT ' 42 4F 4F 54 20 20 20 20
File name, eight characters blank padded.
6531-6533
DEFM 'SYS' 53 59 53
File extension, three characters.
6534-6535
DEFW 01AEH AE 01
Update password hash. Both system records carry this same value, which is what protects them from being renamed or removed without the password.
6536-6537
DEFW 0000H 00 00
Access password hash. Under LDOS 5.3 this word is no longer a password and is left zero here, because the extended dating scheme has taken it over.
6538-6539
DEFW 0005H 05 00
Ending record number. BOOT/SYS occupies five sectors of cylinder 0, which is the boot sector plus the four the loader needs.
653A
DEFB 00H 00
First extent, cylinder field. BOOT/SYS lives on cylinder 0.
653B
DEFB 00H 00
First extent, packed granule field. Bits 7 to 5 give the starting granule, here granule 0, and bits 4 to 0 the granule count less one, here a single granule.
653C-6541
DEFB 0FFH, 0FFH, 0FFH, 0FFH, 0FFH, 0FFH FF FF FF FF FF FF
The three remaining extent slots, all unused. 0FFH in the cylinder field is what marks an extent slot as empty.
6542
DEFB 0FFH FF
Forward extended directory entry link. 0FFH means there is no continuation record; 0FEH would mean the next two bytes address one.
6543
DEFB 0FFH FF
Secondary directory entry count, unused for a file this small.
6544
DEFB 5DH 5D
Directory record attribute byte for DIR/SYS. As with BOOT/SYS it is a primary, SYSTEM, in-use, invisible entry, but its access level is 5 rather than 6.
6545-6548
DEFB 00H, 00H, 00H, 00H 00 00 00 00
Flags and modification month, remaining date, end of file byte offset and logical record length, all zero.
6549-6550
DEFM 'DIR ' 44 49 52 20 20 20 20 20
File name, eight characters blank padded.
6551-6553
DEFM 'SYS' 53 59 53
File extension, three characters.
6554-6555
DEFW 01AEH AE 01
Update password hash, the same value BOOT/SYS carries.
6556-6557
DEFW 4296H 96 42
Access password hash. 4296H is the hash of eight blanks, which is to say no password at all.
6558-6559
DEFW 000AH 0A 00
Ending record number, the number of sectors the directory occupies. 63BEH overwrites the low byte with the sectors per cylinder, capped at 34.
655A
DEFB 11H 11
First extent, cylinder field. 62A8H and 63D1H overwrite it with the directory cylinder actually chosen.
655B
DEFB 01H 01
First extent, packed granule field. 63CBH overwrites it with the starting granule in bits 7 to 5 and the granule count less one in bits 4 to 0, so the directory is described as taking its whole cylinder.
655C-6561
DEFB 0FFH, 0FFH, 0FFH, 0FFH, 0FFH, 0FFH FF FF FF FF FF FF
The three remaining extent slots, all unused.
6562
DEFB 0FFH FF
Forward extended directory entry link, none.
6563
DEFB 0FFH FF
Secondary directory entry count, unused.

6564H - Working Storage

Three small areas between the directory record templates and the track format descriptors. The ten bytes at 6564H are not loaded from the file because the front end fills them in from the drive's own Drive Code Table entry, and the two bytes at 656FH are not loaded because the track expander computes them.

6564-656D
DEFS 0AH
NOT LOADED. The working copy of the target drive's Drive Code Table entry. 6EC9H copies the ten bytes here from 4700H plus ten times the drive number and 6ED1H points Register Pair IY at them. DCT+00H to DCT+02H are the JP that 6495H uses to reach the driver, DCT+03H carries the write protect, double density, eight inch, side select and stepping rate bits, DCT+04H the double-sided and unit bits, DCT+05H the current cylinder, DCT+06H the highest cylinder, DCT+07H and DCT+08H the geometry and DCT+09H the directory cylinder.
656E
DEFB 03H 03
Boot track step rate, 0 to 3. The value shipped here is the slowest rate, and 70B9H overwrites it with the figure taken from the STEP parameter, the configuration sector or the operator's answer before the boot sectors are patched at 62B4H and 62E1H.
656F-6570
DEFS 02H
NOT LOADED. 656FH is the number of sectors in a whole cylinder, written by 6023H during expansion and by 6238H on the system information path. 6570H is the number of sectors on one track, written by 6018H out of byte 0 of the template and read back by the repeat escape at 606CH.

6571H - Track Format Descriptors

Four six-byte descriptors, taken in pairs. 6F75H points Register Pair HL at 6571H and adds twelve when DCT+03H bit 5 says the drive is eight inch, so 6571H and 6577H serve five and a quarter inch drives while 657DH and 6583H serve eight inch drives. Within a pair the first is single density and the second double density, and 6F83H and 6F88H plant the two addresses into the operands at 6FEAH and 6FDAH so the density questions can switch between them.

6571-6576
DEFW 6589H, 2409H, 0D35H 89 65 09 24 35 0D
Track format descriptor for five and a quarter inch single density, ten sectors of 256 bytes on a track. The first word is the address of the packed template, the second is planted into DCT+07H and DCT+08H of the working Drive Code Table copy as the drive geometry, and the third is the number of bytes above 6E00H the expanded track image needs. 64FBH and 650AH apply it.
6577-657C
DEFW 65C9H, 4511H, 196AH C9 65 11 45 6A 19
Track format descriptor for five and a quarter inch double density, eighteen sectors of 256 bytes on a track. The first word is the address of the packed template, the second is planted into DCT+07H and DCT+08H of the working Drive Code Table copy as the drive geometry, and the third is the number of bytes above 6E00H the expanded track image needs. 64FBH and 650AH apply it.
657D-6582
DEFW 6623H, 270FH, 1558H 23 66 0F 27 58 15
Track format descriptor for eight inch single density, sixteen sectors of 256 bytes on a track. The first word is the address of the packed template, the second is planted into DCT+07H and DCT+08H of the working Drive Code Table copy as the drive geometry, and the third is the number of bytes above 6E00H the expanded track image needs. 64FBH and 650AH apply it.
6583-6588
DEFW 6668H, 491DH, 29B1H 68 66 1D 49 B1 29
Track format descriptor for eight inch double density, thirty sectors of 256 bytes on a track. The first word is the address of the packed template, the second is planted into DCT+07H and DCT+08H of the working Drive Code Table copy as the drive geometry, and the third is the number of bytes above 6E00H the expanded track image needs. 64FBH and 650AH apply it.

6589H - The Four Packed Track Templates

Each template is a run-length stream that expands into the literal byte sequence the WD1771 Write Track command lays on the surface. Bytes 01H to 0F0H repeat the byte that follows them that many times, with a count of 00H meaning 256, and the five escapes 0F1H to 0F5H open and close the per-sector repeat block, mark an identifier field position, end the template and repeat a two byte pair. Behind each template sits the list of sector numbers the verify pass walks.

6589-658A
DEFB 0AH, 07H 0A 07
Template header for five and a quarter inch single density. Byte 0 is the sectors per track, read by 6016H into 6570H, and byte 1 is the cylinder to cylinder sector skew, planted by 6028H into the operand at 60FDH.
658B-659C
DEFB 00H, 05H, 01H, 06H, 02H, 07H, 03H, 08H, 04H, 09H, 0F6H, 0F6H, 0F6H, 0F6H, 0F6H, 0F6H, 0F6H, 0F6H 00 05 01 06 02 07 03 08 04 09 F6 F6 F6 F6 F6 F6 F6 F6
Sector interleave table, skew plus one plus sectors long. The first 10 entries are the physical sector numbers in the order they are laid on the track and the remaining 8 are negative eight bit displacements that pull the running pointer back to the head of the list, which is how 60C7H wraps it. 60A4H starts each cylinder from wherever the previous one left off and 60FCH adds the skew, so the sector order rotates from cylinder to cylinder.
659D-659E
DEFB 0EH, 0FFH 0E FF
Pre-index gap. Repeat counts and fill bytes that lay down the gap between the index pulse and the first sector identifier field.
659F
DEFB 0F1H F1
Open a repeat block. 606CH reloads the repeat count from 6570H, so the block that follows is expanded once per sector on the track.
65A0-65A3
DEFB 06H, 00H, 01H, 0FEH 06 00 01 FE
The identifier field preamble of one sector: the gap bytes and the address mark that precede the three bytes carrying cylinder, side and sector.
65A4
DEFB 0F3H F3
Record the current track image position as a DEFW in the pointer table at 6D00H. 607DH does the storing, and 60B2H to 60CEH later walks that table filling the cylinder, side and sector into every position it names.
65A5-65BA
DEFB 03H, 00H, 01H, 01H, 01H, 0F7H, 01H, 0FFH, 0BH, 0FFH, 06H, 00H, 01H, 0FBH, 00H, 0E5H, 01H, 0F7H, 01H, 0FFH, 0DH, 0FFH 03 00 01 01 01 F7 01 FF 0B FF 06 00 01 FB 00 E5 01 F7 01 FF 0D FF
The rest of one sector: the three identifier placeholder bytes, the length code, the identifier CRC, the gap, the data address mark, the 256-byte data field expressed as a repeat count of zero, the data CRC and the trailing gap.
65BB
DEFB 0F2H F2
Close the repeat block. 6073H counts the sectors down and rewinds the stream to just after the 0F1H until every sector on the track has been produced.
65BC-65BD
DEFB 47H, 0FFH 47 FF
Post-index gap, laid down once after the last sector.
65BE
DEFB 0F4H F4
End of template. 6085H saves the address of the byte that follows into the operand at 6156H, terminates the pointer table at 6D00H with 0000H and pads 256 bytes of 0FFH behind the image, which expands to 3105 bytes in all.
65BF-65C8
DEFB 00H, 01H, 02H, 03H, 04H, 05H, 06H, 07H, 08H, 09H 00 01 02 03 04 05 06 07 08 09
Expected sector number list, one entry per sector on a track. The verify pass reads it through the operand at 6156H and checks the sectors in this order rather than in numerical order, so a whole track is verified in as few revolutions as the interleave allows.
65C9-65CA
DEFB 12H, 0DH 12 0D
Template header for five and a quarter inch double density. Byte 0 is the sectors per track, read by 6016H into 6570H, and byte 1 is the cylinder to cylinder sector skew, planted by 6028H into the operand at 60FDH.
65CB-65EA
DEFB 00H, 06H, 0CH, 01H, 07H, 0DH, 02H, 08H, 0EH, 03H, 09H, 0FH, 04H, 0AH, 10H, 05H, 0BH, 11H, 0EEH, 0EEH, 0EEH, 0EEH, 0EEH, 0EEH, 0EEH, 0EEH, 0EEH, 0EEH, 0EEH, 0EEH, 0EEH, 0EEH 00 06 0C 01 07 0D 02 08 0E 03 09 0F 04 0A 10 05 0B 11 EE EE EE EE EE EE EE EE EE EE EE EE EE EE
Sector interleave table, skew plus one plus sectors long. The first 18 entries are the physical sector numbers in the order they are laid on the track and the remaining 14 are negative eight bit displacements that pull the running pointer back to the head of the list, which is how 60C7H wraps it. 60A4H starts each cylinder from wherever the previous one left off and 60FCH adds the skew, so the sector order rotates from cylinder to cylinder.
65EB-65EC
DEFB 20H, 4EH 20 4E
Pre-index gap. Repeat counts and fill bytes that lay down the gap between the index pulse and the first sector identifier field.
65ED
DEFB 0F1H F1
Open a repeat block. 606CH reloads the repeat count from 6570H, so the block that follows is expanded once per sector on the track.
65EE-65F3
DEFB 0CH, 00H, 03H, 0F5H, 01H, 0FEH 0C 00 03 F5 01 FE
The identifier field preamble of one sector: the gap bytes and the address mark that precede the three bytes carrying cylinder, side and sector.
65F4
DEFB 0F3H F3
Record the current track image position as a DEFW in the pointer table at 6D00H. 607DH does the storing, and 60B2H to 60CEH later walks that table filling the cylinder, side and sector into every position it names.
65F5-660C
DEFB 03H, 00H, 01H, 01H, 01H, 0F7H, 16H, 4EH, 0CH, 00H, 03H, 0F5H, 01H, 0FBH, 0F5H, 80H, 6DH, 0B6H, 01H, 0F7H, 01H, 4EH, 17H, 4EH 03 00 01 01 01 F7 16 4E 0C 00 03 F5 01 FB F5 80 6D B6 01 F7 01 4E 17 4E
The rest of one sector: the three identifier placeholder bytes, the length code, the identifier CRC, the gap, the data address mark, the 256-byte data field expressed as a repeat count of zero, the data CRC and the trailing gap.
660D
DEFB 0F2H F2
Close the repeat block. 6073H counts the sectors down and rewinds the stream to just after the 0F1H until every sector on the track has been produced.
660E-660F
DEFB 0B6H, 4EH B6 4E
Post-index gap, laid down once after the last sector.
6610
DEFB 0F4H F4
End of template. 6085H saves the address of the byte that follows into the operand at 6156H, terminates the pointer table at 6D00H with 0000H and pads 256 bytes of 0FFH behind the image, which expands to 6334 bytes in all.
6611-6622
DEFB 00H, 0CH, 07H, 02H, 0EH, 09H, 04H, 10H, 0BH, 06H, 01H, 0DH, 08H, 03H, 0FH, 0AH, 05H, 11H 00 0C 07 02 0E 09 04 10 0B 06 01 0D 08 03 0F 0A 05 11
Expected sector number list, one entry per sector on a track. The verify pass reads it through the operand at 6156H and checks the sectors in this order rather than in numerical order, so a whole track is verified in as few revolutions as the interleave allows.
6623-6624
DEFB 10H, 02H 10 02
Template header for eight inch single density. Byte 0 is the sectors per track, read by 6016H into 6570H, and byte 1 is the cylinder to cylinder sector skew, planted by 6028H into the operand at 60FDH.
6625-6637
DEFB 0AH, 05H, 00H, 0BH, 06H, 01H, 0CH, 07H, 02H, 0DH, 08H, 03H, 0EH, 09H, 04H, 0FH, 0F0H, 0F0H, 0F0H 0A 05 00 0B 06 01 0C 07 02 0D 08 03 0E 09 04 0F F0 F0 F0
Sector interleave table, skew plus one plus sectors long. The first 16 entries are the physical sector numbers in the order they are laid on the track and the remaining 3 are negative eight bit displacements that pull the running pointer back to the head of the list, which is how 60C7H wraps it. 60A4H starts each cylinder from wherever the previous one left off and 60FCH adds the skew, so the sector order rotates from cylinder to cylinder.
6638-6639
DEFB 28H, 0FFH 28 FF
Pre-index gap. Repeat counts and fill bytes that lay down the gap between the index pulse and the first sector identifier field.
663A
DEFB 0F1H F1
Open a repeat block. 606CH reloads the repeat count from 6570H, so the block that follows is expanded once per sector on the track.
663B-663E
DEFB 06H, 00H, 01H, 0FEH 06 00 01 FE
The identifier field preamble of one sector: the gap bytes and the address mark that precede the three bytes carrying cylinder, side and sector.
663F
DEFB 0F3H F3
Record the current track image position as a DEFW in the pointer table at 6D00H. 607DH does the storing, and 60B2H to 60CEH later walks that table filling the cylinder, side and sector into every position it names.
6640-6653
DEFB 03H, 00H, 01H, 01H, 01H, 0F7H, 0BH, 0FFH, 06H, 00H, 01H, 0FBH, 00H, 0E5H, 01H, 0F7H, 01H, 0FFH, 14H, 0FFH 03 00 01 01 01 F7 0B FF 06 00 01 FB 00 E5 01 F7 01 FF 14 FF
The rest of one sector: the three identifier placeholder bytes, the length code, the identifier CRC, the gap, the data address mark, the 256-byte data field expressed as a repeat count of zero, the data CRC and the trailing gap.
6654
DEFB 0F2H F2
Close the repeat block. 6073H counts the sectors down and rewinds the stream to just after the 0F1H until every sector on the track has been produced.
6655-6656
DEFB 0D0H, 0FFH D0 FF
Post-index gap, laid down once after the last sector.
6657
DEFB 0F4H F4
End of template. 6085H saves the address of the byte that follows into the operand at 6156H, terminates the pointer table at 6D00H with 0000H and pads 256 bytes of 0FFH behind the image, which expands to 5176 bytes in all.
6658-6667
DEFB 0AH, 00H, 06H, 0CH, 02H, 08H, 0EH, 04H, 05H, 0BH, 01H, 07H, 0DH, 03H, 09H, 0FH 0A 00 06 0C 02 08 0E 04 05 0B 01 07 0D 03 09 0F
Expected sector number list, one entry per sector on a track. The verify pass reads it through the operand at 6156H and checks the sectors in this order rather than in numerical order, so a whole track is verified in as few revolutions as the interleave allows.
6668-6669
DEFB 1EH, 03H 1E 03
Template header for eight inch double density. Byte 0 is the sectors per track, read by 6016H into 6570H, and byte 1 is the cylinder to cylinder sector skew, planted by 6028H into the operand at 60FDH.
666A-668B
DEFB 00H, 06H, 0CH, 12H, 18H, 01H, 07H, 0DH, 13H, 19H, 02H, 08H, 0EH, 14H, 1AH, 03H, 09H, 0FH, 15H, 1BH, 04H, 0AH, 10H, 16H, 1CH, 05H, 0BH, 11H, 17H, 1DH, 0E2H, 0E2H, 0E2H, 0E2H 00 06 0C 12 18 01 07 0D 13 19 02 08 0E 14 1A 03 09 0F 15 1B 04 0A 10 16 1C 05 0B 11 17 1D E2 E2 E2 E2
Sector interleave table, skew plus one plus sectors long. The first 30 entries are the physical sector numbers in the order they are laid on the track and the remaining 4 are negative eight bit displacements that pull the running pointer back to the head of the list, which is how 60C7H wraps it. 60A4H starts each cylinder from wherever the previous one left off and 60FCH adds the skew, so the sector order rotates from cylinder to cylinder.
668C-668D
DEFB 14H, 4EH 14 4E
Pre-index gap. Repeat counts and fill bytes that lay down the gap between the index pulse and the first sector identifier field.
668E
DEFB 0F1H F1
Open a repeat block. 606CH reloads the repeat count from 6570H, so the block that follows is expanded once per sector on the track.
668F-6694
DEFB 0CH, 00H, 03H, 0F5H, 01H, 0FEH 0C 00 03 F5 01 FE
The identifier field preamble of one sector: the gap bytes and the address mark that precede the three bytes carrying cylinder, side and sector.
6695
DEFB 0F3H F3
Record the current track image position as a DEFW in the pointer table at 6D00H. 607DH does the storing, and 60B2H to 60CEH later walks that table filling the cylinder, side and sector into every position it names.
6696-66AD
DEFB 03H, 00H, 01H, 01H, 01H, 0F7H, 16H, 4EH, 0CH, 00H, 03H, 0F5H, 01H, 0FBH, 0F5H, 80H, 6DH, 0B6H, 01H, 0F7H, 01H, 0FFH, 11H, 4EH 03 00 01 01 01 F7 16 4E 0C 00 03 F5 01 FB F5 80 6D B6 01 F7 01 FF 11 4E
The rest of one sector: the three identifier placeholder bytes, the length code, the identifier CRC, the gap, the data address mark, the 256-byte data field expressed as a repeat count of zero, the data CRC and the trailing gap.
66AE
DEFB 0F2H F2
Close the repeat block. 6073H counts the sectors down and rewinds the stream to just after the 0F1H until every sector on the track has been produced.
66AF-66B2
DEFB 00H, 4EH, 3DH, 4EH 00 4E 3D 4E
Post-index gap, laid down once after the last sector.
66B3
DEFB 0F4H F4
End of template. 6085H saves the address of the byte that follows into the operand at 6156H, terminates the pointer table at 6D00H with 0000H and pads 256 bytes of 0FFH behind the image, which expands to 10357 bytes in all.
66B4-66D1
DEFB 00H, 0CH, 18H, 07H, 13H, 02H, 0EH, 1AH, 09H, 15H, 04H, 10H, 1CH, 0BH, 17H, 06H, 12H, 01H, 0DH, 19H, 08H, 14H, 03H, 0FH, 1BH, 0AH, 16H, 05H, 11H, 1DH 00 0C 18 07 13 02 0E 1A 09 15 04 10 1C 0B 17 06 12 01 0D 19 08 14 03 0F 1B 0A 16 05 11 1D
Expected sector number list, one entry per sector on a track. The verify pass reads it through the operand at 6156H and checks the sectors in this order rather than in numerical order, so a whole track is verified in as few revolutions as the interleave allows.

66D2H - Engine Message Block, Part 1

Every string the format engine displays. A 03H terminator leaves the cursor where it stands, which is what lets the progress dots and the two digit cylinder counter be written on the same line; a 0DH terminator ends the line. A leading 1DH is the video control code that returns the cursor to the start of the current line, so the verifying message writes straight over the formatting one.

66D2-66E9
DEFM 1DH, 'Formatting cylinder ', 03H 1D 46 6F 72 6D 61 74 74 69 6E 67 20 63 79 6C 69 6E 64 65 72 20 20 20 03
Displayed once by 6098H before the format pass begins. The three blanks at the end are the field 64E9H rewrites with the current cylinder number after every track, using two backspaces so the number is overwritten in place rather than scrolled.
66EA-6701
DEFM 1DH, 'Verifying cylinder ', 03H 1D 56 65 72 69 66 79 69 6E 67 20 20 63 79 6C 69 6E 64 65 72 20 20 20 03
Displayed once by 6139H before the verify pass begins. The leading 1DH puts the cursor back to the start of the line so this message replaces the formatting one, and the three trailing blanks are again the cylinder number field.
6702-6706
DEFM '* ', 03H 2A 20 20 20 03
Displayed by 61EDH when a cylinder fails verification, marking the cylinder number already on the screen. The manual describes this as the asterisk that shows a locked out cylinder, and the FREE command lists the same cylinders from the lock-out map at GAT offset 60H.
6707-6714
DEFM 'Formatting...', 0DH 46 6F 72 6D 61 74 74 69 6E 67 2E 2E 2E 0D
Displayed by 6218H on the SYSTEM path, where no track is written and the only work is putting the system information onto a disk that already carries a format.
6715-673A
DEFM 'No cylinders available for directory!', 0DH 4E 6F 20 63 79 6C 69 6E 64 65 72 73 20 61 76 61 69 6C 61 62 6C 65 20 66 6F 72 20 64 69 72 65 63 74 6F 72 79 21 0D
Displayed by 627EH when the outward search from the middle of the disk reaches both edges without finding a cylinder whose granule allocation map byte is anything other than 0FFH. Every cylinder was locked out, so there is nowhere to put a directory.
673B-6762
DEFM 'Directory will be placed on cylinder 00', 0DH 44 69 72 65 63 74 6F 72 79 20 77 69 6C 6C 20 62 65 20 70 6C 61 63 65 64 20 6F 6E 20 63 79 6C 69 6E 64 65 72 20 30 30 0D
Displayed by 6292H once the directory cylinder has been chosen. The two zero characters at the end are the field 628EH overwrites with the ASCII digits of the cylinder actually selected, which is why the text ships with a cylinder number of 00.

6763H - Restore and Select Side 0

A six-instruction routine sitting inside the message block, called only from 61A3H when the SYSTEM parameter has sent the engine back to re-format cylinder 0 in single density. It brings the head home and puts the side select bit back to side 0, both of which have to be true before the single density pass starts.

6763
GOSUB to the local stub at 6467H, which asks the drive's own driver for operation 04H, Restore. The driver issues the WD1771 Restore command and sets DCT+05H in the working Drive Code Table copy at 6569H to zero.
6766
RES 4,(IY+03H) FD CB 03 A6
Clear bit 4 of DCT+03H in the working Drive Code Table copy at 6567H, the side select bit, so the cylinder 0 pass starts on side 0.
676A-676D
DEFB 00H x 4 00 00 00 00
Four unused bytes between the end of the routine and its RET.
676E
RET C9
RETurn to 61A6H, which goes on to clear the double density bit before re-entering the expander.

676FH - Engine Message Block, Part 2

The remaining engine strings, ending with thirty-two unused bytes that round the block out. Two of these are worth reading together: the format runs with a RET planted over the interrupt vector at 4012H, so the clock stops for the whole of it, and the closing note is the honest consequence of that.

676F-6785
DEFM 0AH, ' Re-format track 0...', 0DH 0A 20 52 65 2D 66 6F 72 6D 61 74 20 74 72 61 63 6B 20 30 2E 2E 2E 0D
Displayed by 619AH when the SYSTEM parameter has been given and cylinder 0 is about to be laid down again in single density, so that the Model I boot ROM, which can only read single density, is able to read it.
6786-67A7
DEFM 0AH, 'Initializing SYSTEM information ', 03H 0A 49 6E 69 74 69 61 6C 69 7A 69 6E 67 20 53 59 53 54 45 4D 20 69 6E 66 6F 72 6D 61 74 69 6F 6E 20 03
Displayed by 629BH once the directory cylinder is known. The 03H terminator leaves the cursor on the line, which is where the progress marks from 642FH and 6372H go.
67A8-67CD
DEFM 0AH, 'Load SYSTEM diskette and hit <ENTER>', 0DH 0A 4C 6F 61 64 20 53 59 53 54 45 4D 20 64 69 73 6B 65 74 74 65 20 61 6E 64 20 68 69 74 20 3C 45 4E 54 45 52 3E 0D
Displayed by 6454H when the format ran on drive 0. The system diskette has to go back before anything asks the DOS to load a SYS overlay, since drive 0 is where overlays are read from.
67CE-67E2
DEFM 0AH, 'Formatting complete', 0DH 0A 46 6F 72 6D 61 74 74 69 6E 67 20 63 6F 6D 70 6C 65 74 65 0D
Displayed by 6420H through @LOGOT on the normal exit path.
67E3-680B
DEFM 0AH, 'Note: Realtime clock no longer accurate', 0DH 0A 4E 6F 74 65 3A 20 52 65 61 6C 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 by 6426H on the normal exit path. FORMAT plants a RET over the first byte of the RST 38H interrupt hook at 4012H for the whole of the pass, so every timer tick that fell in that period was lost and the software clock is behind.
680C-681B
DEFM 'Format aborted!', 0DH 46 6F 72 6D 61 74 20 61 62 6F 72 74 65 64 21 0D
Displayed by 6437H, which is reached from 6434H and from every caller that supplies its own message pointer instead.
681C-683A
DEFM 'Can't, Diskette not formatted!', 0DH 43 61 6E 27 74 2C 20 44 69 73 6B 65 74 74 65 20 6E 6F 74 20 66 6F 72 6D 61 74 74 65 64 21 0D
Displayed by 62C9H when the verify of cylinder 0 sector 0 fails on the SYSTEM path. System information can only be added to a diskette that already carries a format.
683B-685A
DEFB 00H x 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Thirty-two unused bytes closing the engine image.

6900H - Boot Sector Step Rate Patch Addresses

Two DEFWs naming the byte inside each boot sector image that carries the WD1771 Seek command. 62B8H and 62E5H read them so that the two low bits of that command, which are the stepping rate, can be replaced with the rate the operator chose before the sector is written.

6900-6901
DEFW 6BBCH BC 6B
Address inside the single density boot sector image at 6B00H of its Seek command byte. The image runs at 4200H once the boot ROM has loaded it, so the same byte is 42BCH at run time.
6902-6903
DEFW 6C10H 10 6C
Address inside the double density boot sector image at 6C00H of its Seek command byte, which is 4310H at run time. Note that both images are assembled for 4200H, so the two addresses differ by more than the 100H that separates the images.

6ACBH - Granule Allocation Table Image Tail

The Granule Allocation Table image occupies 6A00H to 6AFFH and its offsets are the offsets of the GAT sector itself. Everything below 6ACBH is built at run time and is therefore not loaded from the file; from 6ACBH up the image ships with its fields already in place and only the configuration word, the password hash, the name and the date are filled in.

6ACB
DEFB 53H 53
The byte at GAT offset CBH, immediately below the configuration word.
6ACC
DEFB 00H 00
Low byte of the configuration word at GAT offset CCH. 6315H overwrites it with the highest cylinder less 34, which the DOS reads back as the cylinder count less 35.
6ACD
DEFB 00H 00
High byte of the configuration word at GAT offset CDH. 6335H overwrites it with the eight inch bit, the double density bit, the granules per track, the two bits FORMAT always sets and, when the SYSTEM parameter re-formatted cylinder 0 in single density, bit 4.
6ACE-6ACF
DEFW 0000H 00 00
Master password hash at GAT offset CEH. 6F6BH overwrites it with the sixteen bit hash the SYS2 password routine returns for the eight bytes at 727DH.
6AD0-6AD7
DEFM ' ' 20 20 20 20 20 20 20 20
Diskette name at GAT offset D0H, shipped as eight blanks. 6EE9H or 6F16H copies the name here, defaulting to the string at 7644H when the NAME parameter was not given.
6AD8-6AE0
DEFM 'MM/DD/YY', 0DH 4D 4D 2F 44 44 2F 59 59 0D
Diskette date at GAT offset D8H, shipped as a template. 6F1BH calls the resident @DATE vector at 4470H, which overwrites it with the current date in the same form.
6AE1-6AFF
DEFM ' ' 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
Blanks filling the rest of the sector, including the auto-command field at GAT offset E0H. A blank auto-command is what makes a freshly formatted disk come up at DOS Ready rather than running something.

6B00H - Single Density Boot Sector Image

A complete boot program, assembled to run at 4200H because that is where the Model I boot ROM places cylinder 0 sector 0 before jumping to it. Every internal address in this block therefore reads 42xxH even though the bytes live at 6Bxxh in FORMAT's image; the links below point at the row that holds each byte. Its job is to find the resident system loader in the directory and stream it into memory as an LDOS load module. FORMAT patches two bytes of it before writing it: the directory cylinder at 4202H and the stepping rate in the Seek command at 42BCH. This is the program the BOOT/SYS page documents in its installed form.

6B00
DEFB 00H 00
The first byte of the sector and the first instruction executed after the boot ROM jumps to 4200H. A NOP, so that the two byte instruction below can carry a data byte in its operand.
6B01
CP 11H FE 11
Compare Register A against the operand at 4202H. The comparison itself is meaningless and the flags are discarded; the instruction exists so that the byte at 4202H can be read as data. 62B1H patches that byte with the directory cylinder, and 4216H reads it back.
6B03
DI F3
Disable interrupts. Nothing but this program runs until the system loader has been read in.
6B04
LD SP,41E0H 31 E0 41
Load the stack pointer with 41E0H, putting the stack in the Level II BASIC workspace, which is dead memory at this point.
6B07
LD IY,0000H FD 21 00 00
Load Register Pair IY with 0000H. The single density boot works the WD1771 directly and never uses Register Pair IY, unlike the double density copy at 6C00H which drives the disk through a Drive Code Table.
6B0B
LD HL,42E4H 21 E4 42
Point Register Pair HL at the string at 42E4H, the three bytes 1CH, 1FH and 03H, which home the cursor and clear to the end of the screen.
6B0E
GOSUB to the string display routine at 429EH to clear the screen.
6B11
LD A,01H 3E 01
Load Register A with 01H, the drive 0 bit of the drive select latch.
6B13
LD (37E1H),A 32 E1 37
Store Register A at the drive select and side latch 37E1H, selecting drive 0 and starting its motor. A boot is always from drive 0.
6B16
LD A,(4202H) 3A 02 42
Fetch the directory cylinder from 4202H, the operand byte FORMAT patched at 62B1H, into Register A.
6B19
LD D,A 57
Copy Register A, the directory cylinder, into Register D, which is the cylinder for the sector read below.
6B1A
LD E,04H 1E 04
Load Register E with 04H, sector 4 of the directory cylinder. Directory records run eight to a sector from sector 2, and the Hash Index Table is indexed so that slot 0 of sector 2 is directory record 0 and slot 0 of each following sector is the next record, which puts record 2, the resident system loader, at slot 0 of sector 4.
6B1C
LD BC,5100H 01 00 51
Point Register Pair BC at 5100H, the buffer the directory sector is read into.
6B1F
GOSUB to the sector read routine at 42ACH, which returns the Z FLAG set on success.
6B22
If the NZ FLAG has been set, meaning the directory sector could not be read, JUMP to 6B94H to report a disk error.
6B24
LD A,(5100H) 3A 00 51
Fetch the attribute byte of the first directory record in the sector from 5100H into Register A.
6B27
AND 10H E6 10
Mask Register A with 10H, isolating bit 4 of the attribute byte, which is set when the directory slot is in use.
6B29
LD HL,42E7H 21 E7 42
Point Register Pair HL at the string at 42E7H, No System, ready for the branch below.
6B2C
If the Z FLAG has been set, meaning the slot is free and no system loader has ever been written to this diskette, JUMP to 6B97H to display the string addressed by Register Pair HL and stop.
6B2E
EXX D9
Switch to the alternate register set. The streamer keeps its file position and buffer pointer in the alternate registers so that the load records it is unpacking can use the primary ones freely.
6B2F
LD HL,(5116H) 2A 16 51
Fetch the word at 5116H into Register Pair HL. That is offset 16H of the directory record just read, the first extent, with the cylinder in Register L and the packed starting granule and granule count in Register H.
6B32
LD D,L 55
Copy Register L, the extent's cylinder, into Register D, the cylinder half of the running file position.
6B33
LD A,H 7C
Copy Register H, the packed granule byte, into Register A.
6B34
RLCA 07
Rotate Register A left, moving the starting granule field in bits 7 to 5 down towards bit 0.
6B35
RLCA 07
Rotate Register A left again.
6B36
RLCA 07
Rotate Register A left a third time.
6B37
AND 07H E6 07
Mask Register A with 07H, leaving the starting granule number alone.
6B39
LD H,A 67
Copy Register A, the starting granule, into Register H as a working copy.
6B3A
RLCA 07
Rotate Register A left, doubling the granule number.
6B3B
RLCA 07
Rotate Register A left again, quadrupling it.
6B3C
ADD A,H 84
ADD Register H, the original granule number, to Register A, giving five times the granule number. A single density Model I track holds ten sectors in two granules, so a granule is five sectors and this is the sector the file starts at.
6B3D
LD E,A 5F
Copy Register A, the starting sector, into Register E, the sector half of the running file position.
6B3E
LD BC,51FFH 01 FF 51
Point Register Pair BC at 51FFH, one byte below the 5100H buffer, so that the first increment in the byte fetch routine lands on 5100H and forces the first sector to be read.
6B41
EXX D9
Switch back to the primary register set.
6B42
Loop Start
GOSUB to the byte fetch routine at 4279H, which returns the next byte of the file in Register A. This one is the load record type.
6B45
DEC A 3D
DECrement Register A by 1. A record type of 01H, a load record, becomes zero and sets the Z FLAG.
6B46
If the NZ FLAG has been set, meaning this is not a load record, JUMP to 6B5FH to consider the other types.
6B48
GOSUB to the byte fetch routine at 4279H for the record length byte.
6B4B
LD B,A 47
Copy Register A, the record length, into Register B. The length counts the two address bytes as well as the data, and a length of zero means 256.
6B4C
GOSUB to the byte fetch routine at 4279H for the low byte of the load address.
6B4F
LD L,A 6F
Copy Register A, the low byte of the load address, into Register L.
6B50
DEC B 05
DECrement Register B by 1, counting the address byte just taken out of the record length.
6B51
GOSUB to the byte fetch routine at 4279H for the high byte of the load address.
6B54
LD H,A 67
Copy Register A, the high byte of the load address, into Register H. Register Pair HL now addresses where this record is to be placed.
6B55
DEC B 05
DECrement Register B by 1, counting the second address byte out of the record length. Register B is now the number of data bytes.
6B56
Loop Start
GOSUB to the byte fetch routine at 4279H for the next data byte of the record.
6B59
LD (HL),A 77
Store Register A, the data byte, into memory at the address in Register Pair HL.
6B5A
INC HL 23
INCrement Register Pair HL by 1, advancing the load address.
6B5B
DECrement Register B and LOOP BACK to 6B56H while data bytes remain in this record.
Loop End
6B5D
LOOP BACK to 6B42H to read the next record type.
6B5F
DEC A 3D
DECrement Register A by 1 again. Register A held the record type less one, so a record type of 02H, the transfer address, now becomes zero and sets the Z FLAG.
6B60
If the Z FLAG has been set, meaning this is the transfer address record and the whole loader has been read in, JUMP to 6B6DH to enter it.
6B62
GOSUB to the byte fetch routine at 4279H for the length of a record of some other type, such as the copyright record.
6B65
LD B,A 47
Copy Register A, the record length, into Register B as the skip count.
6B66
Loop Start
GOSUB to the byte fetch routine at 4279H and throw the byte away.
6B69
DECrement Register B and LOOP BACK to 6B66H until the whole record has been skipped.
Loop End
6B6B
LOOP BACK to 6B42H to read the next record type.
6B6D
GOSUB to the byte fetch routine at 4279H for the length byte of the transfer record, which is discarded because it is always two.
6B70
GOSUB to the byte fetch routine at 4279H for the low byte of the transfer address.
6B73
LD L,A 6F
Copy Register A, the low byte of the transfer address, into Register L.
6B74
GOSUB to the byte fetch routine at 4279H for the high byte of the transfer address.
6B77
LD H,A 67
Copy Register A, the high byte of the transfer address, into Register H. Register Pair HL now holds the entry point of the resident system loader.
6B78
JP (HL) E9
JUMP to the address in Register Pair HL, entering the system loader. The boot sector's work is over and it does not expect to be returned to.
6B79
EXX D9
Switch to the alternate register set, where Register Pair BC is the pointer into the 5100H sector buffer and Register Pair DE is the cylinder and sector of the sector held in it.
6B7A
INC C 0C
INCrement Register C by 1, advancing the buffer pointer. Because the buffer is page aligned at 5100H, Register C wrapping to zero is exactly the signal that the sector has been used up.
6B7B
If the NZ FLAG has been set, meaning the buffer still has bytes in it, JUMP to 6B91H to hand one back.
6B7D
PUSH BC C5
Save Register Pair BC, the buffer pointer, onto the stack across the sector read.
6B7E
LD A,01H 3E 01
Load Register A with 01H, the drive 0 bit of the drive select latch.
6B80
LD (37E1H),A 32 E1 37
Store Register A at the drive select and side latch 37E1H, re-selecting drive 0 and keeping the motor running.
6B83
GOSUB to the sector read routine at 42ACH to fill the buffer from the cylinder and sector in Register Pair DE.
6B86
If the NZ FLAG has been set, meaning the sector could not be read, JUMP to 6B94H to report a disk error.
6B88
POP BC C1
Restore Register Pair BC, the buffer pointer, from the stack.
6B89
INC E 1C
INCrement Register E by 1, moving the running file position on to the next sector.
6B8A
LD A,E 7B
Copy Register E, the new sector number, into Register A.
6B8B
SUB 0AH D6 0A
SUBtract 0AH, decimal 10, from Register A. Ten is the number of sectors on a single density Model I track, and unlike the double density copy at 6CAAH this figure is hard coded because it can never be anything else.
6B8D
If the NZ FLAG has been set, meaning the sector number has not reached ten, JUMP to 6B91H and leave the cylinder alone.
6B8F
LD E,A 5F
Copy Register A, which is now zero, into Register E, wrapping the sector number round.
6B90
INC D 14
INCrement Register D by 1, stepping the running file position on to the next cylinder.
6B91
LD A,(BC) 0A
Fetch the next byte of the file from the sector buffer, addressed by Register Pair BC, into Register A.
6B92
EXX D9
Switch back to the primary register set.
6B93
RET C9
RETurn with the file byte in Register A.
6B94
LD HL,42F3H 21 F3 42
Point Register Pair HL at the string at 42F3H, Disk Error, preceded by two video control bytes.
6B97
GOSUB to the string display routine at 429EH to put the message addressed by Register Pair HL on the screen. Both failure paths converge here.
6B9A
GOSUB to the ROM line input routine at 0040H. Interrupts are off and the machine is going no further, so this simply parks the cursor for the operator.
6B9D
HALT 76
Halt the processor. With interrupts disabled this is permanent, and the operator has to press reset.
6B9E
PUSH HL E5
Save Register Pair HL, addressing the string, onto the stack so it can be handed back unchanged.
6B9F
LD A,(HL) 7E
Loop Start
Fetch the next character of the string, addressed by Register Pair HL, into Register A.
6BA0
CP 03H FE 03
Compare Register A, the character, against 03H, the string terminator. If Register A equals 03H the Z FLAG is set.
6BA2
If the Z FLAG has been set, meaning the terminator has been reached, JUMP to 6BAAH.
6BA4
GOSUB to the ROM video driver at 0033H to display the character in Register A.
6BA7
INC HL 23
INCrement Register Pair HL by 1, moving on to the next character.
6BA8
LOOP BACK to 6B9FH.
Loop End
6BAA
POP HL E1
Restore Register Pair HL, addressing the head of the string, from the stack.
6BAB
RET C9
RETurn to the caller.
6BAC
PUSH BC C5
Save Register Pair BC, the buffer address, onto the stack.
6BAD
GOSUB to the read routine at 42B4H with the cylinder in Register D, the sector in Register E and the buffer in Register Pair BC.
6BB0
POP HL E1
Restore the buffer address from the stack into Register Pair HL.
6BB1
RET Z C8
If the Z FLAG has been set, meaning the read succeeded, RETurn to the caller.
6BB2
LD B,H 44
Copy Register H, the high byte of the buffer address, into Register B.
6BB3
LD C,L 4D
Copy Register L, the low byte of the buffer address, into Register C, restoring Register Pair BC for the retry. Control now falls into the read routine below, so a failed read is attempted exactly once more and that attempt's RETurn goes to the original caller.
6BB4
LD (37EEH),DE ED 53 EE 37
Store Register Pair DE at 37EEH, which puts the sector number from Register E into the WD1771 sector register at 37EEH and the cylinder from Register D into the data register at 37EFH, where the Seek command takes its destination from.
6BB8
LD HL,37ECH 21 EC 37
Point Register Pair HL at 37ECH, the WD1771 command and status register, which is used for both from here on.
6BBB
LD (HL),1BH 36 1B
Issue a Seek command to the WD1771 by storing 1BH at 37ECH. Bit 4 clear and bit 3 set enable the head load and settle delay, bit 2 set asks for the destination track identifier to be verified, and the two low bits are the stepping rate. Those two bits are what FORMAT patches at 62BFH out of the STEP parameter.
6BBD
EX (SP),HL E3
Exchange Register Pair HL with the top of the stack, purely to waste nineteen clock cycles while the controller raises its busy line.
6BBE
EX (SP),HL E3
Exchange Register Pair HL with the top of the stack again.
6BBF
EX (SP),HL E3
Exchange Register Pair HL with the top of the stack a third time.
6BC0
EX (SP),HL E3
Exchange Register Pair HL with the top of the stack a fourth time. Register Pair HL is back to 37ECH and about seventy-six cycles have gone by.
6BC1
LD A,(HL) 7E
Loop Start
Fetch the WD1771 status from 37ECH, addressed by Register Pair HL, into Register A.
6BC2
RRCA 0F
Rotate Register A right, putting bit 0 of the status, the busy flag, into the CARRY FLAG.
6BC3
If the CARRY FLAG has been set, meaning the controller is still seeking, LOOP BACK to 6BC1H.
Loop End
6BC5
LD (HL),88H 36 88
Issue a Read Sector command to the WD1771 by storing 88H at 37ECH. Bits 7 to 5 are the read command, bit 4 clear selects a single record and bit 3 set enables the head settle delay.
6BC7
PUSH DE D5
Save Register Pair DE, the cylinder and sector, onto the stack; Register Pair DE is about to be reused as the data register pointer.
6BC8
LD DE,37EFH 11 EF 37
Point Register Pair DE at 37EFH, the WD1771 data register, from which each byte of the sector is taken.
6BCB
EX (SP),HL E3
Exchange Register Pair HL with the top of the stack as a short delay.
6BCC
EX (SP),HL E3
Exchange Register Pair HL with the top of the stack again, leaving Register Pair HL back at 37ECH.
6BCD
JUMP to 6BDAH, which jumps straight on to the poll below. The two hops are how the loop is entered without testing the busy line first.
6BCF
RRCA 0F
Rotate Register A right, putting bit 0 of the status, the busy flag, into the CARRY FLAG.
6BD0
If the NO CARRY FLAG has been set, meaning the controller has finished the sector, JUMP to 6BDCH to check the result.
6BD2
LD A,(HL) 7E
Loop Start
Fetch the WD1771 status from 37ECH, addressed by Register Pair HL, into Register A.
6BD3
BIT 1,A CB 4F
Test bit 1 of Register A, the data request line, which the controller raises when a byte is waiting in the data register.
6BD5
If the Z FLAG has been set, meaning no byte is waiting yet, JUMP to 6BCFH to test the busy line instead.
6BD7
LD A,(DE) 1A
Fetch the next byte of the sector from the WD1771 data register at 37EFH, addressed by Register Pair DE, into Register A.
6BD8
LD (BC),A 02
Store Register A, the byte just read, into the buffer at the address in Register Pair BC.
6BD9
INC BC 03
INCrement Register Pair BC by 1, advancing the buffer pointer.
6BDA
LOOP BACK to 6BD2H for the next byte.
Loop End
6BDC
LD A,(HL) 7E
Fetch the final WD1771 status from 37ECH, addressed by Register Pair HL, into Register A.
6BDD
AND 1CH E6 1C
Mask Register A with 1CH, keeping bit 2 lost data, bit 3 CRC error and bit 4 record not found. If none of them is set the Z FLAG is set.
6BDF
POP DE D1
Restore Register Pair DE, the cylinder and sector, from the stack.
6BE0
RET Z C8
If the Z FLAG has been set, meaning the sector was read cleanly, RETurn to the caller.
6BE1
LD (HL),0D0H 36 D0
Issue a Force Interrupt command to the WD1771 by storing 0D0H at 37ECH, terminating whatever the controller is still doing.
6BE3
RET C9
RETurn with the NZ FLAG set to report the failure.
6BE4-6BE6
DEFM 1CH, 1FH, 03H 1C 1F 03
The clear screen string, displayed by 420EH. 1CH homes the cursor and 1FH clears to the end of the screen.
6BE7-6BF2
DEFM 17H, E8H, 'No System', 03H 17 E8 4E 6F 20 53 79 73 74 65 6D 03
Displayed by 4229H and 4297H when the directory slot the system loader should occupy is free. The two leading control bytes position the message on the screen.
6BF3-6BFF
DEFM 17H, E8H, 'Disk Error', 03H 17 E8 44 69 73 6B 20 45 72 72 6F 72 03
Displayed by 4294H and 4297H when a sector will not read, either the directory sector or one of the loader's own.

6C00H - Double Density Boot Sector Image

The same program written for a double density diskette, and also assembled to run at 4200H. FORMAT writes it to cylinder 0 sector 1, where it sits unused until SOLE is applied; the manual's dual density system disk is a double density pack whose cylinder 0 has been laid down in single density by the SYSTEM parameter, so that the boot ROM can read sector 0 while this copy waits for the double density driver. The differences are all in the disk access: instead of working the WD1771 directly it goes through the Drive Code Table that Register Pair IY points at, it takes the geometry from that table rather than assuming ten sectors, and it reads the Granule Allocation Table first to learn how many sectors a cylinder holds.

6C00
DEFB 00H 00
The first byte of the sector, a NOP, so that the two byte instruction below can carry a data byte in its operand.
6C01
CP 11H FE 11
Compare Register A against the operand at 4202H. The flags are discarded; the instruction exists to carry the directory cylinder, which 62DEH patches into that byte and 4229H reads back.
6C03
LD SP,4160H 31 60 41
Load the stack pointer with 4160H, lower than the single density copy's 41E0H because this program is entered with more already in memory below it.
6C06
DI F3
Disable interrupts.
6C07
GOSUB to the Level II BASIC clear screen routine in ROM at 01C9H. The single density copy sends its own control bytes instead, because it cannot assume the ROM is the one it expects.
6C0A
LD A,(IY+03H) FD 7E 03
Fetch DCT+03H from the Drive Code Table addressed by Register Pair IY into Register A, the byte carrying the write protect, density, side select and stepping rate bits.
6C0D
AND 0FCH E6 FC
Mask Register A with 0FCH, clearing the two low bits, the stepping rate.
6C0F
OR 03H F6 03
OR 03H into Register A, setting the stepping rate to the slowest the WD1771 offers. A boot has to work before anything has been tuned.
6C11
LD (IY+03H),A FD 77 03
Store Register A back into DCT+03H of the Drive Code Table addressed by Register Pair IY.
6C14
LD A,(IY+07H) FD 7E 07
Fetch DCT+07H from the Drive Code Table into Register A. Bits 4 to 0 are the highest sector number on a track and bits 7 to 5 are the head count less one.
6C17
LD D,A 57
Copy Register A, the whole geometry byte, into Register D so the two fields can be separated.
6C18
AND 1FH E6 1F
Mask Register A with 1FH, isolating the highest sector number on a track.
6C1A
LD E,A 5F
Copy Register A, the highest sector number, into Register E.
6C1B
INC E 1C
INCrement Register E by 1, turning it into the number of sectors per track.
6C1C
XOR D AA
XOR Register D, the whole geometry byte, into Register A. The low five bits cancel and Register A is left holding the head count field alone.
6C1D
RLCA 07
Rotate Register A left, moving the head count field down towards bit 0.
6C1E
RLCA 07
Rotate Register A left again.
6C1F
RLCA 07
Rotate Register A left a third time.
6C20
INC A 3C
INCrement Register A by 1, turning the head count less one into the number of recording surfaces.
6C21
GOSUB to the multiply routine at 42D7H, which returns Register A holding Register A times Register E, the number of sectors on one cylinder.
6C24
LD BC,5100H 01 00 51
Point Register Pair BC at 5100H, the buffer.
6C27
PUSH BC C5
Save Register Pair BC, the buffer address, onto the stack.
6C28
PUSH AF F5
Save Register Pair AF, holding the sectors per cylinder, onto the stack across the sector read.
6C29
LD A,(4202H) 3A 02 42
Fetch the directory cylinder from 4202H, the operand byte FORMAT patched at 62DEH, into Register A.
6C2C
LD D,A 57
Copy Register A, the directory cylinder, into Register D, the cylinder for the read below.
6C2D
LD E,C 59
Copy Register C, which is zero because Register Pair BC holds 5100H, into Register E, selecting sector 0 of the directory cylinder. Sector 0 is the Granule Allocation Table.
6C2E
GOSUB to the sector read routine at 42C9H to read the Granule Allocation Table into the buffer at 5100H.
6C31
POP AF F1
Restore Register Pair AF, with the sectors per cylinder back in Register A, from the stack.
6C32
LD HL,51CDH 21 CD 51
Point Register Pair HL at 51CDH, which is offset CDH of the Granule Allocation Table just read, the high byte of the configuration word.
6C35
BIT 5,(HL) CB 6E
Test bit 5 of the configuration word high byte addressed by Register Pair HL, the double-sided flag.
6C37
If the Z FLAG has been set, meaning the diskette is single sided, JUMP to 6C3AH with the sectors per cylinder unchanged.
6C39
ADD A,A 87
Double Register A, the sectors per cylinder, because a two-sided cylinder carries that many sectors on each surface.
6C3A
LD (42ABH),A 32 AB 42
Self-Modifying Code
Store Register A, the sectors per cylinder, into 42ABH, the operand of SUB 00H at 42AAH. That instruction is the sector wrap test inside the byte fetch routine, so the streamer now knows when to step to the next cylinder. The single density copy has no equivalent because ten sectors is written into its code at 428BH.
6C3D
LD E,04H 1E 04
Load Register E with 04H, sector 4 of the directory cylinder, which by the Hash Index Table ordering is directory record 2, the resident system loader.
6C3F
POP BC C1
Restore Register Pair BC, the buffer address, from the stack.
6C40
PUSH BC C5
Save Register Pair BC, the buffer address, onto the stack again so it survives the read.
6C41
GOSUB to the sector read routine at 42C9H to read the directory sector into the buffer at 5100H.
6C44
POP HL E1
Restore the buffer address from the stack into Register Pair HL, so Register Pair HL now addresses the first directory record in the sector.
6C45
BIT 4,(HL) CB 66
Test bit 4 of the attribute byte addressed by Register Pair HL, which is set when the directory slot is in use.
6C47
LD HL,42E6H 21 E6 42
Point Register Pair HL at the string at 42E6H, No System, ready for the branch below.
6C4A
If the Z FLAG has been set, meaning the slot is free, JUMP to 6CB6H to display the string addressed by Register Pair HL and stop.
6C4C
EXX D9
Switch to the alternate register set, where the streamer keeps its file position and buffer pointer.
6C4D
LD HL,(5116H) 2A 16 51
Fetch the word at 5116H into Register Pair HL, offset 16H of the directory record, the first extent, with the cylinder in Register L and the packed granule byte in Register H.
6C50
LD A,H 7C
Copy Register H, the packed granule byte, into Register A.
6C51
RLCA 07
Rotate Register A left, moving the starting granule field down towards bit 0.
6C52
RLCA 07
Rotate Register A left again.
6C53
RLCA 07
Rotate Register A left a third time.
6C54
AND 07H E6 07
Mask Register A with 07H, leaving the starting granule number alone.
6C56
LD E,A 5F
Copy Register A, the starting granule, into Register E ready for the multiply below.
6C57
LD A,(IY+08H) FD 7E 08
Fetch DCT+08H from the Drive Code Table addressed by Register Pair IY into Register A. Bits 4 to 0 are the sectors in a granule less one.
6C5A
AND 1FH E6 1F
Mask Register A with 1FH, isolating the sectors per granule less one.
6C5C
INC A 3C
INCrement Register A by 1, giving the sectors per granule. The single density copy multiplies by a hard coded five instead.
6C5D
GOSUB to the multiply routine at 42D7H, which returns Register A holding the starting granule times the sectors per granule, the sector the file begins at.
6C60
LD E,A 5F
Copy Register A, the starting sector, into Register E, the sector half of the running file position.
6C61
LD D,L 55
Copy Register L, the extent's cylinder, into Register D, the cylinder half of the running file position.
6C62
LD BC,51FFH 01 FF 51
Point Register Pair BC at 51FFH, one byte below the buffer, so the first increment lands on 5100H and forces a read.
6C65
EXX D9
Switch back to the primary register set.
6C66
Loop Start
GOSUB to the byte fetch routine at 429DH for the next load record type.
6C69
DEC A 3D
DECrement Register A by 1. A record type of 01H, a load record, becomes zero and sets the Z FLAG.
6C6A
If the NZ FLAG has been set, meaning this is not a load record, JUMP to 6C83H.
6C6C
GOSUB to the byte fetch routine at 429DH for the record length byte.
6C6F
LD B,A 47
Copy Register A, the record length, into Register B.
6C70
GOSUB to the byte fetch routine at 429DH for the low byte of the load address.
6C73
LD L,A 6F
Copy Register A into Register L.
6C74
DEC B 05
DECrement Register B by 1, counting the address byte out of the record length.
6C75
GOSUB to the byte fetch routine at 429DH for the high byte of the load address.
6C78
LD H,A 67
Copy Register A into Register H. Register Pair HL now addresses where the record is to be placed.
6C79
DEC B 05
DECrement Register B by 1. Register B is now the number of data bytes.
6C7A
Loop Start
GOSUB to the byte fetch routine at 429DH for the next data byte.
6C7D
LD (HL),A 77
Store Register A into memory at the address in Register Pair HL.
6C7E
INC HL 23
INCrement Register Pair HL by 1.
6C7F
DECrement Register B and LOOP BACK to 6C7AH while data bytes remain.
Loop End
6C81
LOOP BACK to 6C66H for the next record.
6C83
DEC A 3D
DECrement Register A by 1 again, so a record type of 02H, the transfer address, becomes zero.
6C84
If the Z FLAG has been set, JUMP to 6C91H to enter the loader.
6C86
GOSUB to the byte fetch routine at 429DH for the length of a record of some other type.
6C89
LD B,A 47
Copy Register A, the length, into Register B as the skip count.
6C8A
Loop Start
GOSUB to the byte fetch routine at 429DH and throw the byte away.
6C8D
DECrement Register B and LOOP BACK to 6C8AH until the record is consumed.
Loop End
6C8F
LOOP BACK to 6C66H for the next record.
6C91
GOSUB to the byte fetch routine at 429DH for the transfer record length, which is discarded.
6C94
GOSUB to the byte fetch routine at 429DH for the low byte of the transfer address.
6C97
LD L,A 6F
Copy Register A into Register L.
6C98
GOSUB to the byte fetch routine at 429DH for the high byte of the transfer address.
6C9B
LD H,A 67
Copy Register A into Register H. Register Pair HL now holds the loader's entry point.
6C9C
JP (HL) E9
JUMP to the address in Register Pair HL, entering the resident system loader.
6C9D
EXX D9
Switch to the alternate register set, where Register Pair BC is the buffer pointer and Register Pair DE the cylinder and sector.
6C9E
INC C 0C
INCrement Register C by 1, advancing the buffer pointer. A wrap to zero means the sector is used up.
6C9F
If the NZ FLAG has been set, meaning bytes remain, JUMP to 6CB0H to hand one back.
6CA1
PUSH BC C5
Save Register Pair BC, the buffer pointer, onto the stack.
6CA2
GOSUB to the sector read routine at 42C9H to fill the buffer from the cylinder and sector in Register Pair DE.
6CA5
If the NZ FLAG has been set, meaning the read failed, JUMP to 6CB3H to report a disk error.
6CA7
POP BC C1
Restore Register Pair BC, the buffer pointer, from the stack.
6CA8
INC E 1C
INCrement Register E by 1, moving on to the next sector.
6CA9
LD A,E 7B
Copy Register E, the new sector number, into Register A.
6CAA
SUB 00H D6 00
Self-Modifying Code
SUBtract the sectors per cylinder from Register A. The operand at 42ABH was planted at 423AH out of the Granule Allocation Table configuration word and the drive geometry, so this test adapts to any format. The listing shows the initial value 00H.
6CAC
If the NZ FLAG has been set, meaning the cylinder is not exhausted, JUMP to 6CB0H.
6CAE
LD E,A 5F
Copy Register A, which is now zero, into Register E, wrapping the sector number.
6CAF
INC D 14
INCrement Register D by 1, stepping on to the next cylinder.
6CB0
LD A,(BC) 0A
Fetch the next byte of the file from the buffer, addressed by Register Pair BC, into Register A.
6CB1
EXX D9
Switch back to the primary register set.
6CB2
RET C9
RETurn with the file byte in Register A.
6CB3
LD HL,42F2H 21 F2 42
Point Register Pair HL at the string at 42F2H, Disk Error.
6CB6
GOSUB to the string display routine at 42BDH to put the message addressed by Register Pair HL on the screen.
6CB9
GOSUB to the ROM line input routine at 0040H, parking the cursor.
6CBC
HALT 76
Halt the processor. With interrupts disabled the machine stops here.
6CBD
LD A,(HL) 7E
Loop Start
Fetch the next character of the string, addressed by Register Pair HL, into Register A. Unlike the single density copy this routine does not preserve Register Pair HL, because neither caller needs it back.
6CBE
CP 03H FE 03
Compare Register A against 03H, the string terminator. If Register A equals 03H the Z FLAG is set.
6CC0
If the Z FLAG has been set, JUMP to 6CC8H.
6CC2
GOSUB to the ROM video driver at 0033H to display the character in Register A.
6CC5
INC HL 23
INCrement Register Pair HL by 1.
6CC6
LOOP BACK to 6CBDH.
Loop End
6CC8
RET C9
RETurn to the caller.
6CC9
PUSH HL E5
Save Register Pair HL onto the stack; the driver does not preserve it.
6CCA
LD H,B 60
Copy Register B, the high byte of the buffer address, into Register H.
6CCB
LD L,C 69
Copy Register C, the low byte of the buffer address, into Register L, so Register Pair HL addresses the buffer as the driver expects.
6CCC
LD B,09H 06 09
Load Register B with 09H, the Read Sector operation code the Drive Code Table dispatcher understands. This is the same operation number FORMAT's own stub at 6483H uses.
6CCE
LD C,00H 0E 00
Load Register C with 00H, drive 0.
6CD0
GOSUB to 42D5H, which jumps into the Drive Code Table entry addressed by Register Pair IY and therefore reaches whatever floppy driver is installed, including a double density one.
6CD3
POP HL E1
Restore Register Pair HL from the stack.
6CD4
RET C9
RETurn with the driver's error code in Register A and the Z FLAG set on success.
6CD5
JP (IY) FD E9
JUMP to the address in Register Pair IY, the first byte of the Drive Code Table entry, which on a fitted unit is a JP to the driver.
6CD7
PUSH BC C5
Save Register Pair BC onto the stack; Register B is about to be used as the multiply counter.
6CD8
LD D,A 57
Copy Register A, the multiplicand, into Register D.
6CD9
XOR A AF
Set Register A to ZERO and clear all flags, starting the product at zero.
6CDA
LD B,08H 06 08
Load Register B with 08H, one pass per bit of the multiplier.
6CDC
ADD A,A 87
Loop Start
Double Register A, shifting the running product left one place.
6CDD
SLA E CB 23
Shift Register E, the multiplier, left one place, moving its top bit into the CARRY FLAG.
6CDF
If the NO CARRY FLAG has been set, meaning that bit of the multiplier is clear, JUMP to 6CE2H and add nothing.
6CE1
ADD A,D 82
ADD Register D, the multiplicand, to Register A.
6CE2
DECrement Register B and LOOP BACK to 6CDCH until all eight bits have been done.
Loop End
6CE4
POP BC C1
Restore Register Pair BC from the stack.
6CE5
RET C9
RETurn with the eight bit product in Register A.
6CE6-6CF1
DEFM 17H, E8H, 'No System', 03H 17 E8 4E 6F 20 53 79 73 74 65 6D 03
Displayed by 424AH and 42B6H when the directory slot the system loader should occupy is free.
6CF2-6CFE
DEFM 17H, E8H, 'Disk Error', 03H 17 E8 44 69 73 6B 20 45 72 72 6F 72 03
Displayed by 42B3H and 42B6H when a sector will not read.
6CFF
DEFB 0FFH FF
The last byte of the sector, unused.

6E00H - Front End Entry and the Configuration Sector

The transfer address. The DOS enters here with Register Pair HL addressing the rest of the command line. Before anything else FORMAT makes SYS2 resident, then reads cylinder 0 sector 2 of drive 0, the system configuration sector, and lifts out of it the default stepping rate and the default cylinder count for each of the eight units. MFLAG$ decides which of the two per-drive parameter blocks the sector carries, exactly as SYS7 SYSTEM and SYS6 RESET do.

6E00
PUSH HL E5
Save Register Pair HL, which addresses the command line the DOS has passed, onto the stack. It is wanted again at 6E3EH after the sign-on and the configuration sector have been dealt with.
6E01
LD HL,7341H 21 41 73
Point Register Pair HL at the sign-on message at 7341H, two video control bytes followed by the FORMAT banner and the MISOSYS copyright line.
6E04
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL.
6E07
GOSUB to the stub at 7277H, which issues RST 28H request 84H. The request has no sub-function and SYS2 answers it with an immediate RET, so its only effect is to make SYS2 the resident overlay before FORMAT starts using the disk. The stub carries no RET of its own; the dispatcher returns straight here.
6E0A
LD DE,0002H 11 02 00
Load Register Pair DE with 0002H, cylinder 0 in Register D and sector 2 in Register E, the system configuration sector.
6E0D
LD C,D 4A
Copy Register D, which is zero, into Register C, selecting drive 0. The configuration sector always lives on the system drive.
6E0E
LD HL,4200H 21 00 42
Point Register Pair HL at SBUFF$ 4200H, the resident system sector buffer, as the destination of the read.
6E11
GOSUB to the resident RDSECT primitive at 4777H to read cylinder 0 sector 2 of drive 0 into the buffer.
6E14
If the NZ FLAG has been set, meaning the configuration sector could not be read, JUMP to 64A7H to report the disk error through @ERROR.
6E17
LD L,26H 2E 26
Load Register L with 26H, the low byte of 4226H, which is the first per-drive parameter block of the configuration sector as laid out for a machine booted from a Lobo LX-80 expansion interface.
6E19
LD A,(442FH) 3A 2F 44
Fetch MFLAG$ from 442FH into Register A, the resident machine flag.
6E1C
RRCA 0F
Rotate Register A right, putting bit 0 of MFLAG$, the LX-80 flag, into the CARRY FLAG.
6E1D
If the CARRY FLAG has been set, meaning the machine was booted from an LX-80, JUMP to 6E21H and keep the 4220H based layout.
6E1F
LD L,76H 2E 76
Load Register L with 76H, the low byte of 4276H, the first per-drive parameter block for an ordinary Radio Shack expansion interface.
6E21
PUSH HL E5
Save Register Pair HL, addressing the first per-drive parameter block, onto the stack.
6E22
POP IY FD E1
Restore it into Register Pair IY. The instructions below index off Register Pair IY, which is why the transfer goes through the stack.
6E24
LD A,(IY-03H) FD 7E FD
Fetch the byte three below the first per-drive block, at 4223H or 4273H, into Register A. That byte holds the machine wide default boot stepping rate.
6E27
AND 03H E6 03
Mask Register A with 03H, keeping only the two bit stepping rate code the WD1771 understands.
6E29
LD (6E73H),A 32 73 6E
Store Register A into 6E73H, the low byte of the STEP result cell in the @PARAM table. A STEP parameter on the command line overwrites this, and 70B1H reads it back if the operator is never asked.
6E2C
LD B,08H 06 08
Load Register B with 08H, one pass for each of the eight units the Drive Code Table can describe.
6E2E
LD IX,726FH DD 21 6F 72
Point Register Pair IX at 726FH, the eight byte table of per-drive default cylinder counts that lives in the unloaded gap behind the module.
6E32
LD DE,000AH 11 0A 00
Load Register Pair DE with 000AH, the stride between per-drive parameter blocks in the configuration sector.
6E35
LD A,(HL) 7E
Loop Start
Fetch this unit's default cylinder count from the configuration sector, addressed by Register Pair HL, into Register A.
6E36
LD (IX+00H),A DD 77 00
Store Register A, that cylinder count, into the table at the address in Register Pair IX.
6E39
INC IX DD 23
INCrement Register Pair IX by 1, advancing the destination.
6E3B
ADD HL,DE 19
ADD Register Pair DE, the ten byte stride, to Register Pair HL, stepping to the next unit's parameter block.
6E3C
DECrement Register B and LOOP BACK to 6E35H until all eight units have been copied.
Loop End

6E3EH - Command Line: Drive Specification and Parameters

The drive comes first, as a colon followed by a digit below 8, and it is planted into the operand at 648CH where the driver call tail picks it up as Register C. Everything after it is handed to @PARAM with the keyword table at 72A8H, which writes each value straight into the operand field of the instruction that will read it. The five density, sides, cylinder and step cells are then ORed together, and the result becomes the flag that decides whether the operator is asked anything at all.

6E3E
POP HL E1
Restore Register Pair HL, addressing the command line, from the stack where 6E00H left it.
6E3F
LD A,(HL) 7E
Loop Start
Fetch the next character of the command line, addressed by Register Pair HL, into Register A.
6E40
INC HL 23
INCrement Register Pair HL by 1, advancing the command line pointer.
6E41
CP 20H FE 20
Compare Register A, the character, against 20H, a blank. If Register A equals 20H the Z FLAG is set.
6E43
If the Z FLAG has been set, meaning the character was a blank, LOOP BACK to 6E3FH to skip it.
Loop End
6E45
CP 3AH FE 3A
Compare Register A against 3AH (ASCII: :), which introduces a drive specification. If Register A equals 3AH the Z FLAG is set.
6E47
If the NZ FLAG has been set, meaning no drive was given on the command line, JUMP to 6E7CH to ask the operator for one.
6E49
LD A,(HL) 7E
Fetch the character after the colon, addressed by Register Pair HL, into Register A.
6E4A
SUB 30H D6 30
SUBtract 30H from Register A, turning an ASCII digit into its value.
6E4C
CP 08H FE 08
Compare Register A, the drive number, against 08H. If it is below eight the CARRY FLAG is set.
6E4E
If the NO CARRY FLAG has been set, meaning the digit was 8 or more or was not a digit at all, JUMP to 64B1H to report parameter error 44.
6E51
LD (648CH),A 32 8C 64
Self-Modifying Code
Store Register A, the drive number, into 648CH, the operand of LD C,0FFH at 648BH. Every call to the drive's own driver now passes that drive in Register C, and 6449H reads the same cell to decide whether the system diskette has to be asked for at the end.
6E54
INC HL 23
INCrement Register Pair HL by 1, stepping past the drive digit so that @PARAM sees only the parameter list.
6E55
LD DE,72A8H 11 A8 72
Point Register Pair DE at 72A8H, the @PARAM keyword table.
6E58
GOSUB to the resident @PARAM vector at 4476H, which matches every keyword on the command line against the table and writes each value into the DEFW cell the table names.
6E5B
If the NZ FLAG has been set, meaning @PARAM found a keyword that is not in the table or a malformed value, JUMP to 64B1H to report parameter error 44.
6E5E
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the SDEN parameter. The operand at 6E5FH is the result cell named by the SDEN keyword. The listing shows the initial value 0000H.
6E61
LD A,D 7A
Copy Register D, the high byte of the SDEN result, into Register A.
6E62
OR E B3
OR Register E, its low byte, into Register A.
6E63
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the DDEN parameter from the result cell at 6E64H.
6E66
OR D B2
OR Register D into Register A, accumulating the DDEN result.
6E67
OR E B3
OR Register E into Register A.
6E68
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the SIDES parameter from the result cell at 6E69H.
6E6B
OR D B2
OR Register D into Register A.
6E6C
OR E B3
OR Register E into Register A.
6E6D
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the CYL parameter from the result cell at 6E6EH.
6E70
OR D B2
OR Register D into Register A.
6E71
OR E B3
OR Register E into Register A.
6E72
LD DE,0FF00H 11 00 FF
Self-Modifying Code
Load Register Pair DE with the STEP parameter. The low half at 6E73H is the result cell and the high half at 6E74H ships as 0FFH so that an absent parameter can be recognised by the instruction below. 6E29H has already put the configuration sector's default into the low half.
6E75
INC D 14
INCrement Register D by 1. If STEP was never supplied Register D still holds 0FFH and becomes zero, contributing nothing; if @PARAM wrote a value the high half is zero and this makes it one.
6E76
OR D B2
OR Register D into Register A, completing the accumulation.
6E77
LD (6FA7H),A 32 A7 6F
Self-Modifying Code
Store Register A into 6FA7H, the operand of LD A,00H at 6FA6H. A non-zero value means at least one format parameter was given on the command line, and the manual's rule that supplying any one of them makes FORMAT take the defaults for the rest is enforced by every question below testing this cell.
6E7A
JUMP to 6E9BH, past the code that asks for a drive.
6E7C
LD HL,73D5H 21 D5 73
Point Register Pair HL at the question at 73D5H, Which drive is to be used ? .
6E7F
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL.
6E82
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H, the buffer the reply is read into.
6E85
LD B,01H 06 01
Load Register B with 01H, the maximum length of the reply, a single digit.
6E87
GOSUB to the ROM line input routine at 0040H, which returns the CARRY FLAG set if BREAK was pressed.
6E8A
If the CARRY FLAG has been set, meaning BREAK was pressed, JUMP to 6434H to display Format aborted! and abort.
6E8D
LD A,(HL) 7E
Fetch the digit the operator typed from 6D00H, addressed by Register Pair HL, into Register A.
6E8E
SUB 30H D6 30
SUBtract 30H from Register A, turning an ASCII digit into its value.
6E90
If the CARRY FLAG has been set, meaning the character was below the digit zero, JUMP to 6E7CH to ask again.
6E93
CP 08H FE 08
Compare Register A, the drive number, against 08H.
6E95
If the NO CARRY FLAG has been set, meaning the number is 8 or more, JUMP to 6E7CH to ask again.
6E98
LD (648CH),A 32 8C 64
Self-Modifying Code
Store Register A, the drive number, into 648CH, the operand of LD C,0FFH at 648BH, exactly as the command line path does at 6E51H.

6E9BH - Defaults, the Working Drive Table Copy and the Drive Present Test

QUERY decides what an absent NAME or MPW means. The cell ships as 0FFFFH, which is what @PARAM also writes for a keyword given without a value, and 0FFFFH in either of those cells makes the code below ask the operator. Answering Q=N puts 0000H there instead, and 0000H means take the default silently. After that the drive's Drive Code Table entry is copied into 6564H and tested.

6E9B
LD DE,(6FACH) ED 5B AC 6F
Self-Modifying Code
Load Register Pair DE with the QUERY parameter from the result cell at 6FACH, which ships as 0FFFFH. 0FFFFH means ask, and the 0000H that Q=N produces means do not.
6E9F
LD HL,(6EDCH) 2A DC 6E
Self-Modifying Code
Load Register Pair HL with the NAME parameter from the result cell at 6EDCH.
6EA2
LD A,H 7C
Copy Register H, the high byte of the NAME result, into Register A.
6EA3
OR L B5
OR Register L, its low byte, into Register A. If the NAME parameter was never mentioned the Z FLAG is set.
6EA4
If the NZ FLAG has been set, meaning a name was supplied or asked for, JUMP to 6EAAH and leave the cell alone.
6EA6
LD (6EDCH),DE ED 53 DC 6E
Store Register Pair DE, the QUERY setting, into 6EDCH, the NAME result cell, so that an unmentioned name inherits whatever QUERY says about prompting.
6EAA
LD HL,(6F1FH) 2A 1F 6F
Self-Modifying Code
Load Register Pair HL with the MPW parameter from the result cell at 6F1FH.
6EAD
LD A,H 7C
Copy Register H, the high byte of the MPW result, into Register A.
6EAE
OR L B5
OR Register L, its low byte, into Register A.
6EAF
If the NZ FLAG has been set, meaning a password was supplied or asked for, JUMP to 6EB5H.
6EB1
LD (6F1FH),DE ED 53 1F 6F
Store Register Pair DE, the QUERY setting, into 6F1FH, the MPW result cell.
6EB5
LD A,(648CH) 3A 8C 64
Fetch the target drive number from 648CH into Register A.
6EB8
LD C,A 4F
Copy Register A, the drive number, into Register C, where the Drive Code Table address routine expects it.
6EB9
LD HL,726FH 21 6F 72
Point Register Pair HL at 726FH, the head of the eight byte table of per-drive default cylinder counts that 6E35H filled in from the configuration sector.
6EBC
ADD A,L 85
ADD Register L to Register A, forming the low byte of the address of this drive's entry.
6EBD
LD L,A 6F
Copy that low byte into Register L.
6EBE
ADC A,H 8C
ADD Register H and the carry to Register A.
6EBF
SUB L 95
SUBtract Register L from Register A, leaving the corrected high byte.
6EC0
LD H,A 67
Copy the corrected high byte into Register H. Register Pair HL now addresses this drive's default cylinder count.
6EC1
LD A,(HL) 7E
Fetch this drive's default cylinder count from the table, addressed by Register Pair HL, into Register A.
6EC2
INC A 3C
INCrement Register A by 1. The configuration sector stores the highest cylinder, so adding one gives the number of cylinders.
6EC3
LD (7031H),A 32 31 70
Self-Modifying Code
Store Register A, the default cylinder count, into 7031H, the operand of LD A,00H at 7030H, and also the value 64FBH loads when it applies a track format descriptor.
6EC6
GOSUB to the resident routine at 47A5H, which returns Register Pair HL holding 4700H plus ten times the drive number in Register C, the address of that drive's Drive Code Table entry.
6EC9
LD DE,6564H 11 64 65
Point Register Pair DE at 6564H, the working Drive Code Table copy, as the destination of the block move.
6ECC
LD BC,000AH 01 0A 00
Load Register Pair BC with 000AH, the ten bytes of a Drive Code Table entry.
6ECF
LDIR ED B0
Block move the drive's Drive Code Table entry from the resident table at Register Pair HL into the working copy at Register Pair DE. Everything from here on alters the copy, so the DOS view of the drive is never disturbed.
6ED1
LD IY,6564H FD 21 64 65
Point Register Pair IY at 6564H, the working copy. Every DCT reference in the rest of the program indexes off Register Pair IY, and 6495H jumps through the JP in its first three bytes to reach the driver.
6ED5
GOSUB to the local stub at 6460H, which asks the drive's own driver for operation 00H. A fitted unit answers with Register A zero and the Z FLAG set; an absent unit holds a RET rather than a JP at DCT+00H and so hands back the 20H the tail loaded, with the NZ FLAG.
6ED8
If the NZ FLAG has been set, meaning there is no drive at that unit number, JUMP to 64A7H to report the error through @ERROR.

6EDBH - Diskette Name and Master Password

Both fields are collected the same way: 0000H in the result cell means take the shipped default, 0FFFFH means ask the operator, and anything else is the address of a string on the command line. The name is folded to upper case and written straight into the Granule Allocation Table image at 6AD0H, its GAT offset D0H; the password goes to 727DH and is validated and hashed into GAT offset CEH.

6EDB
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the NAME parameter from the result cell at 6EDCH.
6EDE
LD A,H 7C
Copy Register H, the high byte of the result, into Register A.
6EDF
OR L B5
OR Register L, its low byte, into Register A.
6EE0
INC A 3C
INCrement Register A by 1. A result of 0FFFFH gives an OR of 0FFH, which becomes zero and sets the Z FLAG.
6EE1
If the Z FLAG has been set, meaning NAME was given without a string or QUERY left it that way, JUMP to 6F05H to ask the operator.
6EE3
DEC A 3D
DECrement Register A by 1, putting back the OR of the two halves.
6EE4
If the NZ FLAG has been set, meaning the result cell holds the address of a string on the command line, JUMP to 6EE9H and use it.
6EE6
LD HL,7644H 21 44 76
Point Register Pair HL at 7644H, the default diskette name, which is the eight characters LDOSDISK.
6EE9
LD DE,6AD0H 11 D0 6A
Loop Start
Point Register Pair DE at 6AD0H, offset D0H of the Granule Allocation Table image, the diskette name field.
6EEC
LD B,08H 06 08
Load Register B with 08H, the length of the name field.
6EEE
LD A,(HL) 7E
Fetch the next character of the name, addressed by Register Pair HL, into Register A.
6EEF
CP 22H FE 22
Compare Register A against 22H (ASCII: "), which closes a quoted string on the command line. If Register A equals 22H the Z FLAG is set.
6EF1
If the Z FLAG has been set, meaning the string has ended, JUMP to 6F18H with whatever has been copied so far.
6EF3
CP 20H FE 20
Compare Register A against 20H, a blank. If the character is below a blank the CARRY FLAG is set.
6EF5
If the CARRY FLAG has been set, meaning a control character has ended the string, JUMP to 6F18H.
6EF8
CP 61H FE 61
Compare Register A against 61H (ASCII: a). If it is below that the CARRY FLAG is set.
6EFA
If the CARRY FLAG has been set, meaning the character is not a lower case letter, JUMP to 6EFEH and store it as it stands.
6EFC
XOR 20H EE 20
XOR 20H into Register A, folding a lower case letter to upper case.
6EFE
LD (DE),A 12
Store Register A, the folded character, into the name field at the address in Register Pair DE.
6EFF
INC HL 23
INCrement Register Pair HL by 1, advancing the source pointer.
6F00
INC DE 13
INCrement Register Pair DE by 1, advancing the destination pointer.
6F01
DECrement Register B and LOOP BACK to 6EEEH until eight characters have been copied.
Loop End
6F03
JUMP to 6F18H, the point both name paths converge on.
6F05
LD HL,73F2H 21 F2 73
Point Register Pair HL at the question at 73F2H, Diskette name ? .
6F08
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL.
6F0B
GOSUB to 64C9H, which reads up to eight characters into 6D00H, folds them to upper case and returns the count in Register B with the Z FLAG set if nothing was typed.
6F0E
If the Z FLAG has been set, meaning the operator pressed ENTER alone, JUMP to 6EE6H to take the default name instead.
6F10
LD C,B 48
Copy Register B, the number of characters typed, into Register C.
6F11
LD B,00H 06 00
Load Register B with 00H, making Register Pair BC the block move count.
6F13
LD DE,6AD0H 11 D0 6A
Point Register Pair DE at 6AD0H, the diskette name field in the Granule Allocation Table image.
6F16
LDIR ED B0
Block move the reply from 6D00H at Register Pair HL into the name field at Register Pair DE. The field was shipped as eight blanks, so a short name is left blank padded.
6F18
LD HL,6AD8H 21 D8 6A
Point Register Pair HL at 6AD8H, offset D8H of the Granule Allocation Table image, the diskette date field, which ships as the template MM/DD/YY.
6F1B
GOSUB to the resident @DATE vector at 4470H, which overwrites the field with today's date in the same form.
6F1E
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the MPW parameter from the result cell at 6F1FH.
6F21
LD A,H 7C
Copy Register H, the high byte of the result, into Register A.
6F22
OR L B5
OR Register L, its low byte, into Register A.
6F23
INC A 3C
INCrement Register A by 1, so that a result of 0FFFFH sets the Z FLAG.
6F24
If the Z FLAG has been set, meaning the password is to be asked for, JUMP to 6F43H.
6F26
DEC A 3D
DECrement Register A by 1, putting back the OR of the two halves.
6F27
If the NZ FLAG has been set, meaning the cell holds the address of a string, JUMP to 6F2CH and use it.
6F29
LD HL,764CH 21 4C 76
Point Register Pair HL at 764CH, the default master password, which is the eight characters PASSWORD.
6F2C
LD DE,727DH 11 7D 72
Loop Start
Point Register Pair DE at 727DH, the eight byte master password buffer.
6F2F
LD B,08H 06 08
Load Register B with 08H, the length of the password field.
6F31
LD A,(HL) 7E
Fetch the next character of the password, addressed by Register Pair HL, into Register A.
6F32
CP 30H FE 30
Compare Register A against 30H (ASCII: 0). If it is below that the CARRY FLAG is set, which covers the blank, the quotation mark and every control character.
6F34
If the CARRY FLAG has been set, meaning the string has ended, JUMP to 6F65H with whatever has been copied.
6F36
CP 61H FE 61
Compare Register A against 61H (ASCII: a).
6F38
If the CARRY FLAG has been set, meaning the character is not a lower case letter, JUMP to 6F3CH and store it as it stands.
6F3A
XOR 20H EE 20
XOR 20H into Register A, folding a lower case letter to upper case.
6F3C
LD (DE),A 12
Store Register A, the folded character, into the password buffer at the address in Register Pair DE.
6F3D
INC DE 13
INCrement Register Pair DE by 1, advancing the destination pointer.
6F3E
INC HL 23
INCrement Register Pair HL by 1, advancing the source pointer.
6F3F
DECrement Register B and LOOP BACK to 6F31H until eight characters have been copied.
Loop End
6F41
JUMP to 6F65H, the point all the password paths converge on.
6F43
LD HL,7403H 21 03 74
Point Register Pair HL at the question at 7403H, Master password ? .
6F46
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL.
6F49
LD HL,727DH 21 7D 72
Point Register Pair HL at 727DH, the master password buffer, as the destination.
6F4C
LD B,08H 06 08
Load Register B with 08H, the maximum length of the reply.
6F4E
GOSUB to 64CEH, the body of the prompt and read routine, which reads straight into the buffer at Register Pair HL rather than into 6D00H and folds the reply to upper case.
6F51
If the Z FLAG has been set, meaning the operator pressed ENTER alone, JUMP to 6F29H to take the default password instead.
6F53
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the buffer address into Register Pair DE.
6F54
ADD A,E 83
ADD Register E, the low byte of the buffer address, to Register A, which holds the number of characters typed.
6F55
LD L,A 6F
Copy the result into Register L.
6F56
LD A,D 7A
Copy Register D, the high byte of the buffer address, into Register A.
6F57
ADC A,00H CE 00
ADD 00H and the carry to Register A.
6F59
LD H,A 67
Copy the result into Register H. Register Pair HL now addresses the first unused byte of the password buffer.
6F5A
LD A,08H 3E 08
Load Register A with 08H, the full length of the password field.
6F5C
SUB B 90
SUBtract Register B, the number of characters still unused, from Register A.
6F5D
If the Z FLAG has been set, meaning the reply filled the field exactly, JUMP to 6F65H.
6F5F
LD B,A 47
Copy Register A, the number of bytes still to be padded, into Register B.
6F60
LD (HL),20H 36 20
Loop Start
Store 20H, a blank, into the password buffer at the address in Register Pair HL.
6F62
INC HL 23
INCrement Register Pair HL by 1.
6F63
DECrement Register B and LOOP BACK to 6F60H until the field is blank filled.
Loop End
6F65
LD DE,727DH 11 7D 72
Point Register Pair DE at 727DH, the completed master password buffer.
6F68
GOSUB to 7201H, which checks that every byte of the field is a blank, a digit or an upper case letter and then hashes it through SYS2, returning the sixteen bit hash in Register Pair HL.
6F6B
LD (6ACEH),HL 22 CE 6A
Store Register Pair HL, the master password hash, into 6ACEH, offset CEH of the Granule Allocation Table image.

6F6EH - Track Format Descriptor Selection and the Density Question

The four descriptors at 6571H are taken in pairs by drive size, and within a pair by density. The default density is not a guess: DCT+04H bit 6 is the bit FDUBL sets on every unit it hooks, so a machine with no doubler installed is offered no density question at all and gets single density. Each question is skipped when the answer is already known, either because a parameter supplied it or because QUERY was turned off.

6F6E
BIT 4,(IY+04H) FD CB 04 66
Test bit 4 of DCT+04H in the working Drive Code Table copy at 6568H, the flag that marks a unit which is not an ordinary floppy.
6F72
If the NZ FLAG has been set, meaning the target is a rigid drive, JUMP to 70CFH. None of the density, sides, cylinder or step questions apply to a drive whose geometry is fixed by its own low level format.
6F75
LD HL,6571H 21 71 65
Point Register Pair HL at 6571H, the first of the four six-byte track format descriptors.
6F78
LD DE,0006H 11 06 00
Load Register Pair DE with 0006H, the length of one descriptor.
6F7B
BIT 5,(IY+03H) FD CB 03 6E
Test bit 5 of DCT+03H in the working Drive Code Table copy at 6567H, the eight inch drive flag.
6F7F
If the Z FLAG has been set, meaning the drive is five and a quarter inch, JUMP to 6F83H with Register Pair HL still on the first pair.
6F81
ADD HL,DE 19
ADD Register Pair DE, one descriptor length, to Register Pair HL.
6F82
ADD HL,DE 19
ADD Register Pair DE again, so Register Pair HL now addresses 657DH, the first of the two eight inch descriptors.
6F83
LD (6FEAH),HL 22 EA 6F
Self-Modifying Code
Store Register Pair HL, the address of the single density descriptor for this drive size, into 6FEAH, the operand of LD HL,0000H at 6FE9H, which is the instruction taken when single density is chosen.
6F86
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, so Register Pair HL holds the descriptor length and Register Pair DE the descriptor address.
6F87
ADD HL,DE 19
ADD Register Pair DE to Register Pair HL, giving the address of the double density descriptor of the pair.
6F88
LD (6FDAH),HL 22 DA 6F
Self-Modifying Code
Store Register Pair HL, the address of the double density descriptor, into 6FDAH, the operand of LD HL,0000H at 6FD9H, the instruction taken when double density is chosen.
6F8B
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL again, putting the single density descriptor address back into Register Pair HL as the default.
6F8C
RES 6,(IY+03H) FD CB 03 B6
Clear bit 6 of DCT+03H in the working Drive Code Table copy at 6567H, the double density bit, defaulting the format to single density.
6F90
BIT 6,(IY+04H) FD CB 04 76
Test bit 6 of DCT+04H in the working Drive Code Table copy at 6568H. That is the bit FDUBL sets on every unit its double density driver has hooked, so it is the test for whether a doubler is present at all.
6F94
If the Z FLAG has been set, meaning no double density driver is serving this unit, JUMP to 6F9BH and stay with the single density descriptor. This is the manual's rule that the density prompt does not appear on a Model I without a doubler board and its driver.
6F96
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, so Register Pair HL now holds the double density descriptor address.
6F97
SET 6,(IY+03H) FD CB 03 F6
Set bit 6 of DCT+03H in the working Drive Code Table copy at 6567H, defaulting a doubler equipped machine to double density.
6F9B
GOSUB to 64FBH to apply the descriptor addressed by Register Pair HL, planting the template address, the geometry and the memory requirement and setting the default cylinder count.
6F9E
RES 5,(IY+04H) FD CB 04 AE
Clear bit 5 of DCT+04H in the working Drive Code Table copy at 6568H, defaulting the format to one side.
6FA2
RES 4,(IY+03H) FD CB 03 A6
Clear bit 4 of DCT+03H in the working Drive Code Table copy at 6567H, defaulting the side select line to side 0.
6FA6
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the flag 6E77H built out of the five format parameters. The operand at 6FA7H is non-zero when at least one of SDEN, DDEN, SIDES, CYL or STEP appeared on the command line. The listing shows the initial value 00H.
6FA8
OR A B7
OR Register A with itself to set the flags. If no format parameter was supplied the Z FLAG is set.
6FA9
If the NZ FLAG has been set, meaning parameters were supplied and the operator is not to be asked, JUMP to 6FB3H.
6FAB
LD DE,0FFFFH 11 FF FF
Self-Modifying Code
Load Register Pair DE with the QUERY parameter from the result cell at 6FACH, which ships as 0FFFFH.
6FAE
LD A,D 7A
Copy Register D, the high byte of the QUERY result, into Register A.
6FAF
OR E B3
OR Register E, its low byte, into Register A. If QUERY was turned off with Q=N the whole word is zero and the Z FLAG is set.
6FB0
If the Z FLAG has been set, meaning the operator asked for no prompting at all, JUMP to 70B1H, past every question, and take the defaults.
6FB3
BIT 6,(IY+04H) FD CB 04 76
Loop Start
Test bit 6 of DCT+04H in the working Drive Code Table copy at 6568H, the doubler present flag, again.
6FB7
If the Z FLAG has been set, meaning there is no double density driver, JUMP to 6FF3H because there is nothing to ask about density.
6FB9
LD A,(6FA7H) 3A A7 6F
Fetch the parameter flag from 6FA7H into Register A.
6FBC
OR A B7
OR Register A with itself to set the flags.
6FBD
If the NZ FLAG has been set, meaning parameters were supplied, JUMP to 6FD2H to read the density out of them instead of asking.
6FBF
LD HL,74DBH 21 DB 74
Point Register Pair HL at the question at 74DBH, Single or Double density <S,D> ? .
6FC2
GOSUB to 64C2H, which displays the question, reads up to three characters into 6D00H and folds them to upper case.
6FC5
If the Z FLAG has been set, meaning the operator pressed ENTER alone, JUMP to 6FF3H and keep the default density.
6FC7
LD A,(HL) 7E
Fetch the first character of the reply from 6D00H, addressed by Register Pair HL, into Register A.
6FC8
CP 53H FE 53
Compare Register A against 53H (ASCII: S). If Register A equals 53H the Z FLAG is set.
6FCA
If the Z FLAG has been set, JUMP to 6FE9H to select the single density descriptor.
6FCC
CP 44H FE 44
Compare Register A against 44H (ASCII: D). If Register A equals 44H the Z FLAG is set.
6FCE
If the Z FLAG has been set, JUMP to 6FD9H to select the double density descriptor.
6FD0
LOOP BACK to 6FB3H to ask again, because the reply was neither.
Loop End
6FD2
LD A,(6E64H) 3A 64 6E
Self-Modifying Code
Fetch the low byte of the DDEN result cell at 6E64H into Register A.
6FD5
XOR 0FFH EE FF
XOR 0FFH into Register A. @PARAM writes 0FFFFH for a keyword that is present, so a DDEN that was given leaves zero here and sets the Z FLAG.
6FD7
If the NZ FLAG has been set, meaning DDEN was not the parameter given, JUMP to 6FE2H to try SDEN.
6FD9
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the address of the double density descriptor for this drive size. The operand at 6FDAH was planted at 6F88H. The listing shows the initial value 0000H.
6FDC
SET 6,(IY+03H) FD CB 03 F6
Set bit 6 of DCT+03H in the working Drive Code Table copy at 6567H, the double density bit.
6FE0
JUMP to 6FF0H to apply the descriptor.
6FE2
LD A,(6E5FH) 3A 5F 6E
Self-Modifying Code
Fetch the low byte of the SDEN result cell at 6E5FH into Register A.
6FE5
XOR 0FFH EE FF
XOR 0FFH into Register A, so an SDEN that was given leaves zero and sets the Z FLAG.
6FE7
If the NZ FLAG has been set, meaning neither density keyword was given, JUMP to 6FF3H and keep whatever the doubler test defaulted to.
6FE9
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the address of the single density descriptor for this drive size. The operand at 6FEAH was planted at 6F83H. The listing shows the initial value 0000H.
6FEC
RES 6,(IY+03H) FD CB 03 B6
Clear bit 6 of DCT+03H in the working Drive Code Table copy at 6567H, the double density bit.
6FF0
GOSUB to 64FBH to apply the descriptor addressed by Register Pair HL, replacing the template, the geometry and the memory requirement.

6FF3H - Sides and Cylinders

Two more questions, each of which is skipped when a parameter already answered it. The cylinder question is not asked at all for an eight inch drive, which always has 77, nor for a rigid one; that is the manual's rule that the cylinder prompt does not appear for eight inch drives.

6FF3
LD A,(6FA7H) 3A A7 6F
Loop Start
Fetch the parameter flag from 6FA7H into Register A.
6FF6
OR A B7
OR Register A with itself to set the flags.
6FF7
If the NZ FLAG has been set, meaning parameters were supplied, JUMP to 700CH to read the number of sides out of them.
6FF9
LD HL,74BCH 21 BC 74
Point Register Pair HL at the question at 74BCH, Enter number of sides <1,2> ? .
6FFC
GOSUB to 64C2H to display the question and read the reply into 6D00H.
6FFF
If the Z FLAG has been set, meaning the operator pressed ENTER alone, JUMP to 7017H and keep the default of one side.
7001
LD A,(HL) 7E
Fetch the first character of the reply from 6D00H, addressed by Register Pair HL, into Register A.
7002
CP 31H FE 31
Compare Register A against 31H (ASCII: 1). If Register A equals 31H the Z FLAG is set.
7004
If the Z FLAG has been set, JUMP to 7017H, leaving DCT+04H bit 5 clear for one side.
7006
CP 32H FE 32
Compare Register A against 32H (ASCII: 2). If Register A equals 32H the Z FLAG is set.
7008
If the NZ FLAG has been set, meaning the reply was neither 1 nor 2, LOOP BACK to 6FF3H to ask again.
Loop End
700A
JUMP to 7011H to set the double-sided flag.
700C
LD A,(6E69H) 3A 69 6E
Self-Modifying Code
Fetch the low byte of the SIDES result cell at 6E69H into Register A.
700F
CP 02H FE 02
Compare Register A against 02H. If two sides were asked for the Z FLAG is set.
7011
If the NZ FLAG has been set, meaning one side, JUMP to 7017H leaving DCT+04H bit 5 clear.
7013
SET 5,(IY+04H) FD CB 04 EE
Set bit 5 of DCT+04H in the working Drive Code Table copy at 6568H, the double-sided flag. From here the expander doubles the sectors per cylinder and the format engine lays both surfaces of every cylinder. The manual warns that most TRS-80 cables do not carry the side select line, in which case every track is locked out during verification.
7017
LD A,(IY+03H) FD 7E 03
Loop Start
Fetch DCT+03H from the working Drive Code Table copy at 6567H into Register A.
701A
AND 28H E6 28
Mask Register A with 28H, keeping bit 5, the eight inch flag, and bit 3, the rigid flag.
701C
If the NZ FLAG has been set, meaning the drive is eight inch or rigid, JUMP to 704BH. An eight inch diskette always has 77 cylinders and a rigid drive's size is fixed by its own format.
701E
LD A,(6FA7H) 3A A7 6F
Fetch the parameter flag from 6FA7H into Register A.
7021
OR A B7
OR Register A with itself to set the flags.
7022
If the NZ FLAG has been set, meaning parameters were supplied, JUMP to 703CH to read the cylinder count out of them.
7024
LD HL,743DH 21 3D 74
Point Register Pair HL at the question at 743DH, Number of cylinders ? .
7027
GOSUB to 64C2H to display the question and read the reply into 6D00H, returning Register Pair HL addressing it.
702A
GOSUB to 71EEH, which turns the ASCII digits at Register Pair HL into a byte in Register A, stopping at the first character that is not a digit.
702D
OR A B7
OR Register A with itself to set the flags. If nothing was typed the result is zero and the Z FLAG is set.
702E
If the NZ FLAG has been set, meaning a number was given, JUMP to 7032H to check it.
7030
LD A,00H 3E 00
Self-Modifying Code
Load Register A with this drive's default cylinder count. The operand at 7031H was planted at 6EC3H out of the configuration sector's per-drive table. The listing shows the initial value 00H.
7032
CP 60H FE 60
Compare Register A, the cylinder count, against 60H, decimal 96, which the manual gives as the largest number of cylinders FORMAT will lay down.
7034
If the NO CARRY FLAG has been set, meaning the count is 96 or more, LOOP BACK to 701EH to ask again.
Loop End
7036
DEC A 3D
DECrement Register A by 1, turning a count of cylinders into the highest cylinder number.
7037
LD (IY+06H),A FD 77 06
Store Register A, the highest cylinder, into DCT+06H of the working Drive Code Table copy at 656AH, overwriting whatever 64FBH put there.
703A
JUMP to 704BH, past the parameter path.
703C
LD A,(6E6EH) 3A 6E 6E
Self-Modifying Code
Fetch the low byte of the CYL result cell at 6E6EH into Register A.
703F
OR A B7
OR Register A with itself to set the flags. If CYL was not among the parameters supplied the Z FLAG is set.
7040
If the Z FLAG has been set, meaning no cylinder count was given, JUMP to 704BH and keep the value 64FBH derived from the configuration sector.
7042
CP 60H FE 60
Compare Register A, the requested cylinder count, against 60H, decimal 96.
7044
If the NO CARRY FLAG has been set, meaning the request is 96 or more, JUMP to 64B1H to report parameter error 44. A number typed at the prompt merely gets the question asked again; a number on the command line is an error.
7047
DEC A 3D
DECrement Register A by 1, turning a count of cylinders into the highest cylinder number.
7048
LD (IY+06H),A FD 77 06
Store Register A, the highest cylinder, into DCT+06H of the working Drive Code Table copy at 656AH.

704BH - Boot Stepping Rate and the Dual Density Check

The last question, and the only one whose answer is in milliseconds rather than a code. The two tables of acceptable replies differ because the WD1771 steps a five and a quarter inch drive at 6, 12, 20 or 30 to 40 milliseconds and an eight inch one at 3, 6, 10 or 15 to 20. The block ends by refusing SYSTEM on any format that could not produce a bootable dual density diskette.

704B
BIT 4,(IY+04H) FD CB 04 66
Test bit 4 of DCT+04H in the working Drive Code Table copy at 6568H, the flag marking a unit that is not an ordinary floppy.
704F
If the NZ FLAG has been set, JUMP to 6FF3H. The branch cannot be taken: 6F72H has already sent every unit with that bit set to 70CFH, so a rigid drive never reaches this instruction.
7051
LD A,(6FA7H) 3A A7 6F
Fetch the parameter flag from 6FA7H into Register A.
7054
OR A B7
OR Register A with itself to set the flags.
7055
If the NZ FLAG has been set, meaning parameters were supplied, JUMP to 70B1H to take the stepping rate from the STEP cell or the configuration sector.
7057
BIT 5,(IY+03H) FD CB 03 6E
Test bit 5 of DCT+03H in the working Drive Code Table copy at 6567H, the eight inch drive flag.
705B
If the NZ FLAG has been set, meaning the drive is eight inch, JUMP to 7087H to ask the eight inch version of the question.
705D
LD HL,7454H 21 54 74
Loop Start
Point Register Pair HL at the question at 7454H, Boot strap stepping rate <6, 12, 20, 30/40 msecs> ? .
7060
GOSUB to 64C2H to display the question and read the reply into 6D00H.
7063
GOSUB to 71EEH to turn the reply into a byte in Register A.
7066
OR A B7
OR Register A with itself to set the flags.
7067
If the Z FLAG has been set, meaning nothing was typed, JUMP to 70B1H to take the default from the configuration sector.
7069
LD B,00H 06 00
Load Register B with 00H, the WD1771 code for the fastest rate.
706B
CP 06H FE 06
Compare Register A, the reply, against 06H, six milliseconds.
706D
If the Z FLAG has been set, JUMP to 70B8H with the code in Register B.
706F
LD B,01H 06 01
Load Register B with 01H, the code for twelve milliseconds.
7071
CP 0CH FE 0C
Compare Register A against 0CH, decimal 12.
7073
If the Z FLAG has been set, JUMP to 70B8H.
7075
LD B,02H 06 02
Load Register B with 02H, the code for twenty milliseconds.
7077
CP 14H FE 14
Compare Register A against 14H, decimal 20.
7079
If the Z FLAG has been set, JUMP to 70B8H.
707B
LD B,03H 06 03
Load Register B with 03H, the code for the slowest rate.
707D
CP 1EH FE 1E
Compare Register A against 1EH, decimal 30.
707F
If the Z FLAG has been set, JUMP to 70B8H.
7081
CP 28H FE 28
Compare Register A against 28H, decimal 40. The slowest rate is quoted as 30 or 40 milliseconds because the WD1771 timing depends on the clock, so both replies are accepted for code 3.
7083
If the Z FLAG has been set, JUMP to 70B8H.
7085
LOOP BACK to 705DH to ask again, because the reply was not one of the five accepted figures.
Loop End
7087
LD HL,7489H 21 89 74
Loop Start
Point Register Pair HL at the question at 7489H, Bootstrap stepping rate <3, 6, 10, 15/20 msecs> ? .
708A
GOSUB to 64C2H to display the question and read the reply into 6D00H.
708D
GOSUB to 71EEH to turn the reply into a byte in Register A.
7090
OR A B7
OR Register A with itself to set the flags.
7091
If the Z FLAG has been set, meaning nothing was typed, JUMP to 70B1H to take the default.
7093
LD B,00H 06 00
Load Register B with 00H, the code for three milliseconds on an eight inch drive.
7095
CP 03H FE 03
Compare Register A against 03H.
7097
If the Z FLAG has been set, JUMP to 70B8H.
7099
LD B,01H 06 01
Load Register B with 01H, the code for six milliseconds.
709B
CP 06H FE 06
Compare Register A against 06H.
709D
If the Z FLAG has been set, JUMP to 70B8H.
709F
LD B,02H 06 02
Load Register B with 02H, the code for ten milliseconds.
70A1
CP 0AH FE 0A
Compare Register A against 0AH, decimal 10.
70A3
If the Z FLAG has been set, JUMP to 70B8H.
70A5
LD B,03H 06 03
Load Register B with 03H, the code for the slowest eight inch rate.
70A7
CP 0FH FE 0F
Compare Register A against 0FH, decimal 15.
70A9
If the Z FLAG has been set, JUMP to 70B8H.
70AB
CP 14H FE 14
Compare Register A against 14H, decimal 20, the other figure quoted for code 3.
70AD
If the Z FLAG has been set, JUMP to 70B8H.
70AF
LOOP BACK to 7087H to ask again.
Loop End
70B1
LD A,(6E73H) 3A 73 6E
Self-Modifying Code
Fetch the low byte of the STEP result cell at 6E73H into Register A, which holds either the value the STEP parameter supplied or the machine default 6E29H took out of the configuration sector.
70B4
AND 03H E6 03
Mask Register A with 03H, keeping only the two bits the WD1771 command carries.
70B6
JUMP to 70B9H, past the instruction that takes the answer from the question instead.
70B8
LD A,B 78
Copy Register B, the code matched by whichever table was used, into Register A.
70B9
LD (656EH),A 32 6E 65
Store Register A, the stepping rate code, at 656EH. 62B4H and 62E1H merge it into the Seek command inside each of the two boot sector images before they are written.
70BC
BIT 5,(IY+03H) FD CB 03 6E
Test bit 5 of DCT+03H in the working Drive Code Table copy at 6567H, the eight inch drive flag.
70C0
If the NZ FLAG has been set, meaning the drive is eight inch, JUMP to 70C8H to test the SYSTEM parameter, because an eight inch pack can never be a Model I boot disk.
70C2
BIT 6,(IY+03H) FD CB 03 76
Test bit 6 of DCT+03H in the working Drive Code Table copy at 6567H, the double density bit.
70C6
If the NZ FLAG has been set, meaning the format is double density on a five and a quarter inch drive, JUMP to 70CFH. That is the one combination SYSTEM is allowed on, because cylinder 0 can then be laid down in single density for the boot ROM to read.
70C8
LD A,(620FH) 3A 0F 62
Self-Modifying Code
Fetch the low byte of the SYSTEM result cell at 620FH into Register A.
70CB
INC A 3C
INCrement Register A by 1. @PARAM writes 0FFFFH for a keyword that is present, so a SYSTEM that was asked for leaves zero here and sets the Z FLAG.
70CC
If the Z FLAG has been set, meaning SYSTEM was asked for on a format that cannot carry a Model I boot cylinder, JUMP to 722CH to refuse.

70CFH - Seed the Granule Map and Call for the Target Diskette

The granule allocation map at 6A00H has to start life saying which granules exist on each cylinder before the verify pass can start taking them away. A cylinder that has n granules gets a byte with its low n bits clear and the rest set, and every cylinder beyond the end of the disk gets 0FFH so that the verify pass and the directory search both stop there. After that, a format on drive 0 asks the operator to change diskettes.

70CF
LD A,(IY+08H) FD 7E 08
Fetch DCT+08H from the working Drive Code Table copy at 656CH into Register A. Bits 7 to 5 of that byte are the number of granules on a track less one.
70D2
RLCA 07
Rotate Register A left, moving the granules per track field down towards bit 0.
70D3
RLCA 07
Rotate Register A left again.
70D4
RLCA 07
Rotate Register A left a third time.
70D5
AND 07H E6 07
Mask Register A with 07H, leaving the granules per track less one alone.
70D7
INC A 3C
INCrement Register A by 1, giving the number of granules on one track.
70D8
LD B,0FFH 06 FF
Load Register B with 0FFH, every granule marked unavailable, which is the pattern the shifts below open up.
70DA
BIT 5,(IY+04H) FD CB 04 6E
Test bit 5 of DCT+04H in the working Drive Code Table copy at 6568H, the double-sided flag.
70DE
If the Z FLAG has been set, meaning one side, JUMP to 70E1H with the granules per track unchanged.
70E0
ADD A,A 87
Double Register A, because a two-sided cylinder carries that many granules on each surface.
70E1
SLA B CB 20
Loop Start
Shift Register B left one place, clearing another low bit and so declaring one more granule available.
70E3
DEC A 3D
DECrement Register A by 1, counting off one granule.
70E4
LOOP BACK to 70E1H while granules remain. Register B ends up as 0FCH for the usual two granule single density cylinder and 0F0H for a four granule one.
Loop End
70E6
LD HL,6A00H 21 00 6A
Point Register Pair HL at 6A00H, the head of the granule allocation map, one byte per cylinder.
70E9
LD A,(IY+06H) FD 7E 06
Fetch DCT+06H from the working Drive Code Table copy at 656AH into Register A, the highest cylinder on the disk.
70EC
LD (HL),B 70
Loop Start
Store Register B, the free granule pattern, into the granule allocation map at the address in Register Pair HL.
70ED
INC L 2C
INCrement Register L by 1, advancing within the page.
70EE
CP L BD
Compare Register A, the highest cylinder, against Register L, the next map position. While the map position is at or below the highest cylinder the NO CARRY condition holds.
70EF
If the NO CARRY FLAG has been set, LOOP BACK to 70ECH to seed the next cylinder. The loop covers cylinder 0 through the highest cylinder inclusive.
Loop End
70F1
LD B,0FFH 06 FF
Load Register B with 0FFH, every granule unavailable, the pattern for a cylinder that does not exist.
70F3
LD A,0CBH 3E CB
Load Register A with 0CBH, which is the low byte of 6ACBH, the first byte of the Granule Allocation Table image that is loaded from the file. The fill below stops there so the configuration word and the name and date fields are not overwritten.
70F5
LD (HL),B 70
Loop Start
Store Register B, holding 0FFH, into the granule allocation map at the address in Register Pair HL.
70F6
INC L 2C
INCrement Register L by 1.
70F7
CP L BD
Compare Register A, holding 0CBH, against Register L.
70F8
If the NZ FLAG has been set, meaning 6ACBH has not been reached, LOOP BACK to 70F5H. Every cylinder position beyond the end of the disk now reads 0FFH, which is what stops the verify pass at 6143H and the directory search at 6269H.
Loop End
70FA
LD A,(648CH) 3A 8C 64
Fetch the target drive number from 648CH into Register A.
70FD
OR A B7
OR Register A with itself to set the flags. If the format is on drive 0 the Z FLAG is set.
70FE
If the NZ FLAG has been set, meaning the target is not drive 0, JUMP to 711AH. Only drive 0 needs the system diskette taken out.
7100
LD HL,7539H 21 39 75
Loop Start
Point Register Pair HL at the message at 7539H, Load destination diskette and hit <ENTER>.
7103
GOSUB to the resident @DSPLY vector at 4467H to display the string addressed by Register Pair HL.
7106
LD A,(430FH) 3A 0F 43
Fetch SFLAG$ from 430FH into Register A, the resident system flag byte.
7109
BIT 5,A CB 6F
Test bit 5 of Register A, which is set while a job is running under JCL.
710B
If the NZ FLAG has been set, meaning a job is running, JUMP to 6434H to abort. A job cannot answer a prompt, which is why the manual tells you to use ABS in a JCL file.
710E
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H, the buffer the reply is read into.
7111
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H, so Register B, the maximum reply length, is zero and only ENTER or BREAK can end the input.
7114
GOSUB to the ROM line input routine at 0040H to wait for the operator.
7117
If the CARRY FLAG has been set, meaning BREAK was pressed, JUMP to 6434H to abort.

711AH - Target Diskette Checks

The last thing before the engine is entered. The working Drive Code Table copy is snapshotted at 7265H because the tests below drive the head about and change DCT+05H, and it is put back immediately before the JP. The tests are, in order: is a drive there, is it ready, is it write protected, is there enough memory, and does the diskette already carry data. Only the last of those can be waived, by ABS.

711A
PUSH IY FD E5
Save Register Pair IY, which addresses the working Drive Code Table copy at 6564H, onto the stack.
711C
POP HL E1
Restore it into Register Pair HL as the source of the block move.
711D
LD DE,7265H 11 65 72
Point Register Pair DE at 7265H, ten bytes of the unloaded gap behind the module, as the destination.
7120
LD BC,000AH 01 0A 00
Load Register Pair BC with 000AH, the ten bytes of a Drive Code Table entry.
7123
LDIR ED B0
Block move the working Drive Code Table copy to the save area at 7265H. The checks below move the head and change the current cylinder, and 71E6H puts this snapshot back so the engine starts from a known state.
7125
GOSUB to the local stub at 6467H, which asks the drive's own driver for operation 04H, Restore, bringing the head to cylinder 0.
7128
GOSUB to the local stub at 646FH, which asks for operation 07H and returns the WD1771 status in Register A once the controller is no longer busy.
712B
BIT 4,(IY+04H) FD CB 04 66
Test bit 4 of DCT+04H in the working Drive Code Table copy at 6568H.
712F
If the NZ FLAG has been set, meaning the target is a rigid drive, JUMP to 7167H. None of the floppy status tests below apply to it.
7131
LD HL,74FDH 21 FD 74
Point Register Pair HL at the message at 74FDH, Drive not ready!, ready for the test below.
7134
BIT 7,A CB 7F
Test bit 7 of Register A, the WD1771 not ready line.
7136
If the NZ FLAG has been set, meaning the drive reports not ready, JUMP to 6437H to display the message addressed by Register Pair HL and abort.
7139
LD HL,7524H 21 24 75
Point Register Pair HL at the message at 7524H, Drive not in system!.
713C
BIT 2,A CB 57
Test bit 2 of Register A, the WD1771 track 0 line, which should be true after the Restore just issued.
713E
If the Z FLAG has been set, meaning the head never reached track 0, JUMP to 6437H to display the message addressed by Register Pair HL and abort.
7141
GOSUB to 723EH, which watches the index pulse against the resident tick counter to decide whether a diskette is actually turning.
7144
If the NZ FLAG has been set, meaning no diskette is turning, LOOP BACK to 7100H to ask for one again.
Loop End
7146
LD HL,750EH 21 0E 75
Point Register Pair HL at the message at 750EH, Write protected disk!.
7149
RLCA 07
Rotate Register A left, moving bit 6 of the WD1771 status, the write protect line, up into bit 7.
714A
OR (IY+03H) FD B6 03
OR DCT+03H from the working Drive Code Table copy at 6567H into Register A. Bit 7 of that byte is the software write protect flag, so a diskette protected either way now shows in bit 7.
714D
AND 80H E6 80
Mask Register A with 80H, keeping only the merged write protect bit.
714F
If the NZ FLAG has been set, meaning the diskette is protected, JUMP to 6437H to display the message addressed by Register Pair HL and abort.
7152
LD HL,6E00H 21 00 6E
Point Register Pair HL at 6E00H, the base of the track image buffer the engine is about to build.
7155
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the memory the expanded track image needs. The operand at 7156H was planted by 651FH out of the third word of the chosen track format descriptor, so it is one of 0D35H, 196AH, 1558H or 29B1H. The listing shows the initial value 0000H.
7158
ADD HL,DE 19
ADD Register Pair DE to Register Pair HL, giving the highest address the track image will reach.
7159
LD D,H 54
Copy Register H into Register D.
715A
LD E,L 5D
Copy Register L into Register E, moving that address into Register Pair DE for the comparison.
715B
LD HL,(4049H) 2A 49 40
Fetch HIGH$ from 4049H into Register Pair HL, the resident top of available memory.
715E
XOR A AF
Set Register A to ZERO and clear all flags, which clears the CARRY FLAG for the subtraction below.
715F
SBC HL,DE ED 52
SUBtract Register Pair DE, the top of the track image, from Register Pair HL, the top of memory. If the image would run past the top of memory the CARRY FLAG is set.
7161
LD HL,73ABH 21 AB 73
Point Register Pair HL at the message at 73ABH, Insufficient memory for specified format!.
7164
If the CARRY FLAG has been set, meaning the machine is too small for this format, JUMP to 6437H to display the message addressed by Register Pair HL and abort.
7167
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, cylinder 0 in Register D and sector 0 in Register E.
716A
GOSUB to the local stub at 6487H, which asks the drive's own driver for operation 0AH, Verify Sector, on cylinder 0 sector 0.
716D
If the NZ FLAG has been set, meaning nothing readable is there, JUMP to 71DDH and go straight on to formatting. A blank diskette needs no warning.
716F
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H, the sector buffer.
7172
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, cylinder 0 sector 0.
7175
GOSUB to the local stub at 6483H, which asks for operation 09H, Read Sector, to bring the existing boot sector into the buffer.
7178
If the NZ FLAG has been set, meaning the read failed even though the verify succeeded, JUMP to 64A7H to report the disk error.
717B
LD HL,757EH 21 7E 75
Point Register Pair HL at the message at 757EH, Disk contains data -- , which is terminated by 03H so that one of the four descriptions below can follow it on the same line.
717E
GOSUB to the resident @LOGOT vector at 447BH to display the string addressed by Register Pair HL.
7181
LD HL,7595H 21 95 75
Point Register Pair HL at the message at 7595H, Non-standard format, which is used unless one of the tests below replaces it.
7184
LD A,(6D02H) 3A 02 6D
Fetch the byte at 6D02H into Register A, which is offset 02H of the boot sector just read and therefore the directory cylinder of the diskette that is already there.
7187
CP (IY+06H) FD BE 06
Compare Register A, that directory cylinder, against DCT+06H in the working Drive Code Table copy at 656AH, the highest cylinder of the format about to be laid down.
718A
If the NO CARRY FLAG has been set, meaning the recorded directory cylinder is at or beyond the end of the disk and so cannot be real, JUMP to 71C0H to report a non-standard format.
718C
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H, the sector buffer.
718F
LD E,L 5D
Copy Register L, which is zero, into Register E, selecting sector 0.
7190
LD D,A 57
Copy Register A, the recorded directory cylinder, into Register D.
7191
LD HL,6D00H 21 00 6D
Point Register Pair HL at 6D00H again, the buffer for the read.
7194
GOSUB to the local stub at 6483H, which asks for operation 09H, Read Sector, to bring in sector 0 of the recorded directory cylinder, which should be that diskette's Granule Allocation Table.
7197
CP 06H FE 06
Compare Register A, the driver's answer, against 06H, the error a deleted data address mark produces. Every directory sector is written with that mark, so 06H here is the proof that this really is the directory cylinder.
7199
If the Z FLAG has been set, meaning the sector carried a deleted data address mark, JUMP to 71A0H.
719B
LD HL,75A9H 21 A9 75
Point Register Pair HL at the message at 75A9H, Unreadable directory, because the recorded directory cylinder did not read at all.
719E
JUMP to 71C0H to display it.
71A0
LD HL,75BEH 21 BE 75
Point Register Pair HL at the message at 75BEH, Non-initialized directory, used unless the date field below proves otherwise.
71A3
LD A,(6DDAH) 3A DA 6D
Fetch the byte at 6DDAH into Register A, offset DAH of the Granule Allocation Table just read, which is the third character of the date field.
71A6
CP 2FH FE 2F
Compare Register A against 2FH (ASCII: /), the separator a real date carries in that position.
71A8
If the NZ FLAG has been set, meaning the date field never had a date written into it, JUMP to 71C0H to report a non-initialized directory.
71AA
LD HL,6DD0H 21 D0 6D
Point Register Pair HL at 6DD0H, offset D0H of the Granule Allocation Table just read, the eight character diskette name.
71AD
LD DE,75DDH 11 DD 75
Point Register Pair DE at 75DDH, the eight X characters in the message at 75D8H, Name=XXXXXXXX Date=MM/DD/YY.
71B0
LD BC,0008H 01 08 00
Load Register Pair BC with 0008H, the length of the name.
71B3
LDIR ED B0
Block move the diskette name out of the Granule Allocation Table into the message. Register Pair HL is left addressing 6DD8H, the date field, which is what makes the second move need no reload.
71B5
LD DE,75ECH 11 EC 75
Point Register Pair DE at 75ECH, the date template in the same message.
71B8
LD BC,0008H 01 08 00
Load Register Pair BC with 0008H, the length of the date.
71BB
LDIR ED B0
Block move the date out of the Granule Allocation Table into the message.
71BD
LD HL,75D8H 21 D8 75
Point Register Pair HL at the message at 75D8H, now carrying the name and date of the diskette that is about to be destroyed.
71C0
GOSUB to the resident @LOGOT vector at 447BH to display whichever of the four descriptions Register Pair HL addresses.
71C3
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the ABS parameter from the result cell at 71C4H. The listing shows the initial value 0000H.
71C6
INC E 1C
INCrement Register E by 1. @PARAM writes 0FFFFH for a keyword that is present, so an ABS that was given leaves zero and sets the Z FLAG.
71C7
If the Z FLAG has been set, meaning ABS was specified, JUMP to 71DDH. The manual's point is exactly this: the message is still displayed but the question is not asked, so JCL does not stall.
71C9
LD HL,430FH 21 0F 43
Point Register Pair HL at SFLAG$ 430FH, the resident system flag byte.
71CC
BIT 5,(HL) CB 6E
Test bit 5 of the byte addressed by Register Pair HL, which is set while a job is running under JCL.
71CE
If the NZ FLAG has been set, meaning a job is running and cannot answer, JUMP to 6434H to abort.
71D1
LD HL,761EH 21 1E 76
Point Register Pair HL at the question at 761EH, Are you sure you want to format it ? .
71D4
GOSUB to 64C2H to display the question and read up to three characters into 6D00H, folded to upper case.
71D7
LD A,(HL) 7E
Fetch the first character of the reply from 6D00H, addressed by Register Pair HL, into Register A.
71D8
CP 59H FE 59
Compare Register A against 59H (ASCII: Y).
71DA
If the NZ FLAG has been set, meaning the operator did not agree, JUMP to 6434H to abort.
71DD
PUSH IY FD E5
Save Register Pair IY, addressing the working Drive Code Table copy at 6564H, onto the stack.
71DF
POP DE D1
Restore it into Register Pair DE as the destination of the block move.
71E0
LD HL,7265H 21 65 72
Point Register Pair HL at 7265H, the snapshot 7123H took before the checks began.
71E3
LD BC,000AH 01 0A 00
Load Register Pair BC with 000AH, the ten bytes of a Drive Code Table entry.
71E6
LDIR ED B0
Block move the snapshot back over the working copy, undoing whatever the readiness and data checks did to the current cylinder and the side select bit.
71E8
GOSUB to the local stub at 6467H, which asks the drive's own driver for operation 04H, Restore, so the engine starts with the head on cylinder 0 and DCT+05H at zero.
71EB
JUMP to 6000H, the format engine. The front end has finished, and the track image the engine is about to build starts at this very address.

71EEH - Front End Helpers

Four short routines: a decimal string to byte converter, the master password validator, the SYSTEM refusal and the drive readiness test. The readiness test is the interesting one, because it decides whether a diskette is turning by watching the WD1771 index line against the resident tick counter, and it escapes a timeout by throwing away its caller's return address.

71EE
LD E,00H 1E 00
Load Register E with 00H, the accumulator the decimal value is built up in.
71F0
LD A,(HL) 7E
Loop Start
Fetch the next character of the reply, addressed by Register Pair HL, into Register A.
71F1
INC HL 23
INCrement Register Pair HL by 1, advancing the pointer.
71F2
SUB 30H D6 30
SUBtract 30H from Register A, turning an ASCII digit into its value.
71F4
LD B,A 47
Copy Register A, the digit value, into Register B.
71F5
CP 0AH FE 0A
Compare Register A against 0AH, decimal 10. If the character really was a digit the CARRY FLAG is set.
71F7
LD A,E 7B
Copy Register E, the accumulated value, into Register A ready for the return.
71F8
RET NC D0
If the NO CARRY FLAG has been set, meaning the character was not a digit, RETurn with the value so far in Register A.
71F9
ADD A,A 87
Double Register A, the accumulated value.
71FA
ADD A,A 87
Double Register A again, giving four times the accumulated value.
71FB
ADD A,E 83
ADD Register E, the original accumulated value, to Register A, giving five times it.
71FC
ADD A,A 87
Double Register A, giving ten times the accumulated value.
71FD
ADD A,B 80
ADD Register B, the new digit, to Register A.
71FE
LD E,A 5F
Copy Register A back into Register E as the running total.
71FF
LOOP BACK to 71F0H for the next character.
Loop End
7201
LD B,08H 06 08
Load Register B with 08H, the length of the master password field.
7203
PUSH DE D5
Save Register Pair DE, addressing the password buffer at 727DH, onto the stack.
7204
POP HL E1
Restore it into Register Pair HL, which is what the tests below index off.
7205
LD A,(HL) 7E
Fetch the first character of the password, addressed by Register Pair HL, into Register A.
7206
JUMP to 7216H, entering the test loop at the letter check. The first character therefore has to be a letter; only the characters after it may be digits.
7208
INC HL 23
Loop Start
INCrement Register Pair HL by 1, advancing to the next character.
7209
LD A,(HL) 7E
Fetch that character, addressed by Register Pair HL, into Register A.
720A
CP 20H FE 20
Compare Register A against 20H, a blank. If Register A equals 20H the Z FLAG is set.
720C
If the Z FLAG has been set, meaning the password has ended, JUMP to 7228H, which checks that every remaining byte is a blank too.
720E
CP 30H FE 30
Compare Register A against 30H (ASCII: 0). If it is below that the CARRY FLAG is set.
7210
If the CARRY FLAG has been set, meaning the character is below the digit zero, JUMP to 7235H to reject the password.
7212
CP 3AH FE 3A
Compare Register A against 3AH, one past the digit nine. If the character is a digit the CARRY FLAG is set.
7214
If the CARRY FLAG has been set, meaning the character is a digit, JUMP to 721EH to accept it.
7216
CP 41H FE 41
Compare Register A against 41H (ASCII: A). If it is below that the CARRY FLAG is set.
7218
If the CARRY FLAG has been set, meaning the character lies between the digits and the letters, JUMP to 7235H to reject the password.
721A
CP 5BH FE 5B
Compare Register A against 5BH, one past the letter Z. If the character is a letter the CARRY FLAG is set.
721C
If the NO CARRY FLAG has been set, meaning the character is beyond Z, JUMP to 7235H to reject the password. This is the manual's rule that password characters must be alphabetic or numeric.
721E
DECrement Register B and LOOP BACK to 7208H until all eight characters have been checked.
Loop End
7220
GOSUB to the stub at 727AH, which issues RST 28H request 0E4H, the SYS2 password hash, with Register Pair DE still addressing the eight byte field. The hash comes back in Register Pair HL.
7223
RET C9
RETurn with the sixteen bit password hash in Register Pair HL.
7224
INC HL 23
Loop Start
INCrement Register Pair HL by 1, advancing past the blank that ended the password.
7225
CP (HL) BE
Compare Register A, which still holds 20H, against the character addressed by Register Pair HL. If that character is also a blank the Z FLAG is set.
7226
If the NZ FLAG has been set, meaning something follows the blank, JUMP to 7235H to reject the password. A password may be short but it may not have a gap in it.
7228
DECrement Register B and LOOP BACK to 7224H until the rest of the field has been checked.
Loop End
722A
JUMP to 7220H to hash the validated password.
722C
LD HL,7286H 21 86 72
Point Register Pair HL at the message at 7286H, Can only SYSTEM a 5" DDEN floppy!.
722F
GOSUB to the resident @LOGOT vector at 447BH to display the string addressed by Register Pair HL.
7232
JUMP to the resident @ABORT vector at 4030H. Nothing has been written to the target at this point, so no clean up is needed.
7235
LD HL,7563H 21 63 75
Point Register Pair HL at the message at 7563H, Invalid Master Password!.
7238
GOSUB to the resident @LOGOT vector at 447BH to display the string addressed by Register Pair HL.
723B
JUMP to 6434H, which displays Format aborted!, restores the machine state and aborts.
723E
LD A,(4040H) 3A 40 40
Fetch TIMER$ from 4040H into Register A, the resident tick counter the real time clock interrupt advances forty times a second.
7241
ADD A,14H C6 14
ADD 14H, decimal 20, to Register A, half a second's worth of ticks.
7243
LD D,A 57
Copy Register A into Register D as the deadline. Every poll below compares the live counter against it.
7244
Loop Start
GOSUB to 7254H, which returns the Z FLAG set when the index hole is under the sensor.
7247
If the NZ FLAG has been set, meaning the index hole is not visible, LOOP BACK to 7244H. This first loop waits for the hole to come round.
Loop End
7249
Loop Start
GOSUB to 7254H again.
724C
If the Z FLAG has been set, meaning the hole is still visible, LOOP BACK to 7249H. This loop waits for it to pass.
Loop End
724E
Loop Start
GOSUB to 7254H a third time.
7251
If the NZ FLAG has been set, meaning the hole has not come round again, LOOP BACK to 724EH. Seeing a complete revolution is what proves the diskette is turning rather than merely present.
Loop End
7253
RET C9
RETurn with the Z FLAG set, meaning the drive is ready.
7254
EI FB
Enable interrupts. The real time clock has to be allowed to run or the tick counter this routine watches would never advance.
7255
LD A,(4040H) 3A 40 40
Fetch TIMER$ from 4040H into Register A, the live tick counter.
7258
CP D BA
Compare Register A against Register D, the deadline set at 7243H. If half a second has gone by the Z FLAG is set.
7259
If the Z FLAG has been set, meaning the deadline has been reached, JUMP to 7261H to give up.
725B
GOSUB to the local stub at 646FH, which asks the drive's own driver for operation 07H and returns the WD1771 status in Register A.
725E
BIT 1,A CB 4F
Test bit 1 of Register A, the WD1771 index line, which is true while the index hole is under the sensor.
7260
RET C9
RETurn with the Z FLAG reflecting the index line.
7261
POP AF F1
Pop the return address of whichever of the three polling loops called this routine into Register Pair AF and throw it away, so that the RETurn below goes to the caller of 723EH instead.
7262
OR 01H F6 01
OR 01H into Register A, forcing the NZ FLAG that the caller reads as drive not ready.
7264
RET C9
RETurn to the caller of 723EH with the NZ FLAG set. This is the last byte of the front end; 7265H onwards is working storage.

7277H - The Two RST 28H Supervisor Stubs

Two three-byte stubs, neither of which ends in a RET, and both of which are correct. The RST 28H dispatcher at 4BCDH swaps the address the RST instruction pushed into Register Pair HL and then immediately overwrites Register Pair HL, so that return address is discarded and the RET at the end of the dispatcher takes the next address off the stack instead. A stub reached by CALL therefore returns to whoever called the stub. SYS6 packs three of these nose to tail at 57C5H and calls each of them separately.

7277
LD A,84H 3E 84
Load Register A with 84H, the supervisor request code. The overlay loader turns a request code into a SYS number by taking the low nibble and subtracting two, so 84H selects SYS2, and the sub-function is the code masked with 70H, which for 84H is 00H. SYS2's own dispatcher at 4E00H compares that against 10H, 20H, 40H, 60H and 50H, matches none of them and falls onto its RET, so the overlay does nothing at all.
7279
RST 28 EF
Restart at 0028H, which reaches the resident RST 28H dispatcher through the hook at 400CH. The only effect of the request is that SYS2 is now the resident overlay, recorded in OVRLY$ 430EH, so the password hash request 0E4H issued later is answered from memory instead of being read off the disk. Control returns to 6E0AH, the instruction after the CALL that reached this stub.
727A
LD A,0E4H 3E E4
Load Register A with 0E4H, the supervisor request code for the SYS2 password hash. The low nibble again selects SYS2 and the code masked with 70H gives sub-function 60H, which SYS2's dispatcher sends to the hash routine at 4E27H.
727C
RST 28 EF
Restart at 0028H. The hash routine walks the eight byte password field addressed by Register Pair DE backwards from a seed of 0FFFFH and returns the sixteen bit result in Register Pair HL. Eight blanks hash to 4296H and the string PASSWORD hashes to 42E0H. Control returns to 7223H, the instruction after the CALL at 7220H that reached this stub.

727DH - Master Password Buffer and the Density Refusal Message

The eight bytes the master password is assembled into before it is hashed, followed by the message put out when the SYSTEM parameter is asked for on a drive that cannot carry a dual density boot cylinder.

727D-7284
DEFM ' ' 20 20 20 20 20 20 20 20
Master password buffer, eight blanks as shipped. 6F3CH copies the operator's answer or the command line string here one character at a time, folding lower case to upper, and 6F60H blank pads whatever is left. 6F65H then hands the address to 7201H, which validates the characters and hashes them into the Granule Allocation Table image at 6ACEH. When the MPW parameter is absent 6F29H points at the default string PASSWORD at 764CH.
7285-72A7
DEFM ' Can only SYSTEM a 5" DDEN floppy!', 0DH 20 43 61 6E 20 6F 6E 6C 79 20 53 59 53 54 45 4D 20 61 20 35 22 20 44 44 45 4E 20 66 6C 6F 70 70 79 21 0D
Displayed by 722CH when the SYSTEM parameter is given for a format that cannot produce a bootable dual density cylinder 0. The Model I boot ROM reads single density only, so a system diskette has to be a five and a quarter inch double density pack whose cylinder 0 is laid down in single density.

72A8H - @PARAM Keyword Table

Nineteen entries of a six character blank padded keyword followed by a DEFW naming the cell the value is to be delivered to, terminated by a zero byte. 6E58H hands the address of this table and the rest of the command line to the resident @PARAM vector at 4476H. Every result cell named here is the operand field of a LD HL or LD DE instruction somewhere in the front end, so supplying a parameter on the command line literally rewrites an instruction before it is executed.

72A8-72AF
DEFM 'NAME ', DEFW 6EDCH 4E 41 4D 45 20 20 DC 6E
Address of the diskette name string. 6EDBH loads Register Pair HL from this cell; 0000H means the parameter was absent and the default LDOSDISK at 7644H is used, 0FFFFH means it was given without a string and the operator is asked for one.
72B0-72B7
DEFM 'N ', DEFW 6EDCH 4E 20 20 20 20 20 DC 6E
Abbreviation of NAME, delivering to the same result cell.
72B8-72BF
DEFM 'MPW ', DEFW 6F1FH 4D 50 57 20 20 20 1F 6F
Address of the master password string. 6F1EH loads Register Pair HL from this cell; 0000H falls back on the default PASSWORD at 764CH and 0FFFFH prompts.
72C0-72C7
DEFM 'M ', DEFW 6F1FH 4D 20 20 20 20 20 1F 6F
Abbreviation of MPW, delivering to the same result cell.
72C8-72CF
DEFM 'SDEN ', DEFW 6E5FH 53 44 45 4E 20 20 5F 6E
Format in single density. 6FE2H tests this cell and clears DCT+03H bit 6 in the working Drive Code Table copy at 6567H.
72D0-72D7
DEFM 'DDEN ', DEFW 6E64H 44 44 45 4E 20 20 64 6E
Format in double density. 6FD2H tests this cell and sets DCT+03H bit 6 in the working Drive Code Table copy at 6567H.
72D8-72DF
DEFM 'SIDES ', DEFW 6E69H 53 49 44 45 53 20 69 6E
Number of sides, 1 or 2. 700CH tests this cell for the value 2 and sets DCT+04H bit 5 in the working Drive Code Table copy at 6568H.
72E0-72E7
DEFM 'CYL ', DEFW 6E6EH 43 59 4C 20 20 20 6E 6E
Number of cylinders, up to 96. 703CH tests this cell, rejects anything of 60H or more with the parameter error at 64B1H and stores the value less one into DCT+06H at 656AH.
72E8-72EF
DEFM 'C ', DEFW 6E6EH 43 20 20 20 20 20 6E 6E
Abbreviation of CYL, delivering to the same result cell.
72F0-72F7
DEFM 'STEP ', DEFW 6E73H 53 54 45 50 20 20 73 6E
Boot track step rate. 6E24H seeds the cell from the configuration sector and 70B1H masks it to two bits before storing it at 656EH for the boot sector patch.
72F8-72FF
DEFM 'ABS ', DEFW 71C4H 41 42 53 20 20 20 C4 71
Format even if the diskette already contains data. 71C3H loads Register Pair DE from this cell; when it is set the are you sure question at 71D1H is skipped.
7300-7307
DEFM 'A ', DEFW 71C4H 41 20 20 20 20 20 C4 71
Abbreviation of ABS, delivering to the same result cell.
7308-730F
DEFM 'QUERY ', DEFW 6FACH 51 55 45 52 59 20 AC 6F
Whether to ask the operator for the parameters that were not supplied. 6FABH loads Register Pair DE from this cell, and 6FA6H holds the flag that suppresses every prompt.
7310-7317
DEFM 'Q ', DEFW 6FACH 51 20 20 20 20 20 AC 6F
Abbreviation of QUERY, delivering to the same result cell.
7318-731F
DEFM 'SYSTEM', DEFW 620FH 53 59 53 54 45 4D 0F 62
Add system information without laying down tracks, and on a five and a quarter inch double density pack re-format cylinder 0 in single density. 620EH and 6191H both read this cell.
7320-7327
DEFM 'WAIT ', DEFW 6126H 57 41 49 54 20 20 26 61
Extra delay after each track step, a loop count handed to the ROM delay routine at 0060H by 6125H. The manual suggests starting at 25000 when a drive locks out every track above a certain point.
7328-732F
DEFM 'W ', DEFW 6126H 57 20 20 20 20 20 26 61
Abbreviation of WAIT, delivering to the same result cell.
7330-7337
DEFM 'DIR ', DEFW 6256H 44 49 52 20 20 20 56 62
Cylinder to place the directory on. 6255H loads Register Pair BC from this cell and 625CH ignores a value that is not below the highest cylinder.
7338-733F
DEFM 'D ', DEFW 6256H 44 20 20 20 20 20 56 62
Abbreviation of DIR, delivering to the same result cell.
7340
DEFB 00H 00
Table terminator. @PARAM stops scanning here, so a keyword the table does not carry is reported back to 6E5BH as a parameter error.

7341H - Front End Message Block

Every string the front end displays, ending with the two default strings and the unused tail. The operator questions all carry a 03H terminator so the cursor stays on the line for the reply, while the error messages carry 0DH.

7341-73AA
DEFM 1CH, 1FH, 'FORMAT - LDOS Disk Formatter Program - Version 5.3.1', 0AH, 'Copyright 1991 MISOSYS, Inc., All rights reserved', 0AH, 0DH 1C 1F 46 4F 52 4D 41 54 20 2D 20 4C 44 4F 53 20 44 69 73 6B 20 46 6F 72 6D 61 74 74 65 72 20 50 72 6F 67 72 61 6D 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 6E04H. The two leading control codes clear the screen and home the cursor before the two lines of banner text.
73AB-73D4
DEFM 'Insufficient memory for specified format!', 0DH 49 6E 73 75 66 66 69 63 69 65 6E 74 20 6D 65 6D 6F 72 79 20 66 6F 72 20 73 70 65 63 69 66 69 65 64 20 66 6F 72 6D 61 74 21 0D
Displayed by 7164H when HIGH$ 4049H is below 6E00H plus the memory requirement taken from the chosen track format descriptor, which is between 0D35H and 29B1H bytes depending on the drive size and density. The expanded track image would not fit.
73D5-73F1
DEFM 'Which drive is to be used ? ', 03H 57 68 69 63 68 20 64 72 69 76 65 20 69 73 20 74 6F 20 62 65 20 75 73 65 64 20 3F 20 03
The first operator question, put out by 6E7FH when the command line carried no drive specification.
73F2-7402
DEFM 'Diskette name ? ', 03H 44 69 73 6B 65 74 74 65 20 6E 61 6D 65 20 3F 20 03
Asked by 6F08H when the NAME parameter was given without a string. Pressing ENTER alone takes the default.
7403-7415
DEFM 'Master password ? ', 03H 4D 61 73 74 65 72 20 70 61 73 73 77 6F 72 64 20 3F 20 03
Asked by 6F46H when the MPW parameter was given without a string. Pressing ENTER alone takes the default.
7416-743C
DEFM 'Special non-standard format desired ? ', 03H 53 70 65 63 69 61 6C 20 6E 6F 6E 2D 73 74 61 6E 64 61 72 64 20 66 6F 72 6D 61 74 20 64 65 73 69 72 65 64 20 3F 20 03
Asked by 7027H. Answering yes opens the cylinder count question that follows; the ordinary path takes the cylinder count from the configuration sector instead.
743D-7453
DEFM 'Number of cylinders ? ', 03H 4E 75 6D 62 65 72 20 6F 66 20 63 79 6C 69 6E 64 65 72 73 20 3F 20 03
Asked by 7027H once a non standard format has been agreed to. 71EEH turns the reply into a byte and 7037H stores it less one into DCT+06H of the working Drive Code Table copy at 656AH.
7454-7488
DEFM 'Boot strap stepping rate <6, 12, 20, 30/40 msecs> ? ', 03H 42 6F 6F 74 20 73 74 72 61 70 20 73 74 65 70 70 69 6E 67 20 72 61 74 65 20 3C 36 2C 20 31 32 2C 20 32 30 2C 20 33 30 2F 34 30 20 6D 73 65 63 73 3E 20 3F 20 03
Asked by 7060H for a five and a quarter inch drive. The four rates the WD1771 offers on this machine are 6, 12, 20 and 30 or 40 milliseconds, and 7069H to 7083H turns the reply into the two bit code.
7489-74BB
DEFM 'Bootstrap stepping rate <3, 6, 10, 15/20 msecs> ? ', 03H 42 6F 6F 74 73 74 72 61 70 20 73 74 65 70 70 69 6E 67 20 72 61 74 65 20 3C 33 2C 20 36 2C 20 31 30 2C 20 31 35 2F 32 30 20 6D 73 65 63 73 3E 20 3F 20 03
Asked by 708AH for an eight inch drive, where the same two bit code means 3, 6, 10 and 15 or 20 milliseconds. 7057H chooses between this question and the one above by testing DCT+03H bit 5.
74BC-74DA
DEFM 'Enter number of sides <1,2> ? ', 03H 45 6E 74 65 72 20 6E 75 6D 62 65 72 20 6F 66 20 73 69 64 65 73 20 3C 31 2C 32 3E 20 3F 20 03
Asked by 6FFCH. A reply of 2 sets DCT+04H bit 5 in the working Drive Code Table copy at 6568H, which doubles the sectors per cylinder and makes the format engine lay both surfaces of every cylinder.
74DB-74FC
DEFM 'Single or Double density <S,D> ? ', 03H 53 69 6E 67 6C 65 20 6F 72 20 44 6F 75 62 6C 65 20 64 65 6E 73 69 74 79 20 3C 53 2C 44 3E 20 3F 20 03
Asked by 6FC2H. The reply selects between the two track format descriptors 6F83H and 6F88H planted into the operands at 6FEAH and 6FDAH.
74FD-750D
DEFM 'Drive not ready!', 0DH 44 72 69 76 65 20 6E 6F 74 20 72 65 61 64 79 21 0D
Displayed by 7136H when the drive readiness test at 723EH finds no index pulses, which means no diskette is turning.
750E-7523
DEFM 'Write protected disk!', 0DH 57 72 69 74 65 20 70 72 6F 74 65 63 74 65 64 20 64 69 73 6B 21 0D
Displayed by 714FH when the write protect notch is covered. The engine tests DCT+03H bit 7 as well, so a protected diskette is refused before anything is written.
7524-7538
DEFM 'Drive not in system!', 0DH 44 72 69 76 65 20 6E 6F 74 20 69 6E 20 73 79 73 74 65 6D 21 0D
Displayed by 713EH when the unit named on the command line has no Drive Code Table entry with a JP in it, which the drive present test at 6ED5H established.
7539-7562
DEFM 'Load destination diskette and hit <ENTER>', 0DH 4C 6F 61 64 20 64 65 73 74 69 6E 61 74 69 6F 6E 20 64 69 73 6B 65 74 74 65 20 61 6E 64 20 68 69 74 20 3C 45 4E 54 45 52 3E 0D
Displayed by 7103H when the format is to run on drive 0. The system diskette has to come out before the target goes in, and 6451H asks for it back afterwards.
7563-757D
DEFM 0AH, 'Invalid Master Password!', 0AH, 0DH 0A 49 6E 76 61 6C 69 64 20 4D 61 73 74 65 72 20 50 61 73 73 77 6F 72 64 21 0A 0D
Displayed by 7238H when the master password contains a character that is neither a blank, a digit nor a letter. The manual states the same rule, and the format is abandoned.
757E-7594
DEFM 'Disk contains data -- ', 03H 44 69 73 6B 20 63 6F 6E 74 61 69 6E 73 20 64 61 74 61 20 2D 2D 20 03
Displayed by 717EH when the first directory sector of the target reads back successfully, meaning the diskette already carries data. One of the four descriptions below follows it.
7595-75A8
DEFM 'Non-standard format', 0DH 4E 6F 6E 2D 73 74 61 6E 64 61 72 64 20 66 6F 72 6D 61 74 0D
One of the four continuations of the message above, used by 7181H when the ending record number in the directory does not agree with the geometry.
75A9-75BD
DEFM 'Unreadable directory', 0DH 55 6E 72 65 61 64 61 62 6C 65 20 64 69 72 65 63 74 6F 72 79 0D
Used by 719BH when the directory sector will not read at all.
75BE-75D7
DEFM 'Non-initialized directory', 0DH 4E 6F 6E 2D 69 6E 69 74 69 61 6C 69 7A 65 64 20 64 69 72 65 63 74 6F 72 79 0D
Used by 71A0H when the directory reads but has never been initialised.
75D8-75F4
DEFM 'Name=XXXXXXXX Date=MM/DD/YY', 0DH 4E 61 6D 65 3D 58 58 58 58 58 58 58 58 20 20 44 61 74 65 3D 4D 4D 2F 44 44 2F 59 59 0D
Used by 71BDH when the directory is intact. 71B3H and 71BBH copy the eight byte diskette name and the eight byte date out of the Granule Allocation Table just read into the two fields of Xs and the date template, so the operator sees which diskette is about to be destroyed.
75F5-7643
DEFM '*** The target drive is a hard disk! ***', 0AH, 'Are you sure you want to format it ? ', 03H 2A 2A 2A 20 54 68 65 20 74 61 72 67 65 74 20 64 72 69 76 65 20 69 73 20 61 20 68 61 72 64 20 64 69 73 6B 21 20 2A 2A 2A 0A 41 72 65 20 79 6F 75 20 73 75 72 65 20 79 6F 75 20 77 61 6E 74 20 74 6F 20 66 6F 72 6D 61 74 20 69 74 20 3F 20 03
Displayed by 6205H when DCT+03H bit 3 says the target is a rigid drive, and by 71D4H as the ordinary are you sure question. Anything other than Y at 6209H or 71D8H abandons the format.
7644-764B
DEFM 'LDOSDISK' 4C 44 4F 53 44 49 53 4B
The default diskette name, used by 6EE6H when the NAME parameter was not given.
764C-7653
DEFM 'PASSWORD' 50 41 53 53 57 4F 52 44
The default master password, used by 6F29H when the MPW parameter was not given.
7654-7673
DEFB 00H x 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Thirty-two unused bytes closing the module image. The load record that carries them ends at 7673H, which is the last byte of the file.