TRS-80 DOS - LDOS 5.3.1 for the Model I - FDUBL/CMD Disassembled
Page Customization
Page Index
FDUBL/CMD
Other Navigation
Summary:
LDOS 5.3.1 FDUBL/CMD Disassembly - Double-Density Floppy Driver Installer (Model I)
FDUBL/CMD is the LDOS 5.3.1 utility that installs a resident double-density floppy disk driver on a TRS-80 Model I fitted with a double-density adapter board. The Model I Expansion Interface carries a Western Digital 1771 floppy disk controller, which can only record in single density (FM). A doubler board adds a Western Digital 1791 controller alongside it and provides a latch that decides which of the two chips answers at the memory-mapped controller addresses 37ECH to 37EFH. FDUBL supplies the software half of that arrangement: a replacement floppy driver that sets the density latch before every controller operation, understands both recording densities, and falls back to the other density automatically when a sector cannot be found.
The utility supports the two incompatible doubler designs sold for the Model I. The default is the Radio Shack Double-Density Adapter Kit (catalogue number 26-1143), whose control latch is driven by the three high-order bits of a write to the controller's sector register at 37EEH; those bits feed a one-of-eight decoder on the adapter board that latches both the density selection and the write-precompensation enable. The alternative, selected with the PERCOM parameter, is the Percom Doubler, whose latch is set by writing an otherwise meaningless command byte of 0FEH or 0FFH to the controller's command register at 37ECH and then cancelling it with a Force Interrupt. The Percom, LNW and Omikron boards do not implement write precompensation, so when the Percom variant is built FDUBL patches its own precompensation routine out of the driver entirely.
FDUBL is a transient command: it runs at 5200H, builds the requested variant of a driver module in its own image, relocates that module into high memory below HIGH$ (4049H), and lowers HIGH$ to protect it. The module carries the standard LDOS resident-module header, so it is named and discoverable through the module lookup service (RST 28H request code 0BCH, serviced by SYS10). Its name is $FDD. Once installed, FDUBL walks the Drive Control Table at 4700H and rewrites the driver address inside each drive's JP vector so that every disk operation on every fitted drive is routed through the new module instead of through the resident single-density driver at 45FBH in SYS0.
The driver module itself is a modified copy of the resident SYS0 floppy driver. Track positioning, drive selection and Type I command issue are still performed by the resident routines at 45E0H, 461CH, 462AH, 462DH and 4659H, and the controller status is still translated to an LDOS error number by the resident decoder at 46F4H. What FDUBL adds is the density latch write before each operation, a write-precompensation decision on the Radio Shack board, an unrolled data-request polling loop fast enough to keep up with the doubled MFM data rate, a density-dependent choice of the deleted-data-mark write command (0ABH on the 1771 in FM, 0A9H on the 1791 in MFM), and an error recovery path that toggles the density flag and re-seeds the drive geometry so a single-density diskette can be read on a double-density system and the other way round.
The distributed file carries the applied patch FDUBL1. The patch record set replaces the instruction at 5265H and adds a nine-byte routine at 5574H whose only job is to write 2CH into the relative-jump displacement inside the module header at 540DH. That displacement is the driver's entry point. In the Radio Shack build the header sits at 540CH and the original displacement of 41H correctly reaches the driver dispatcher at 544FH; in the Percom build the header is moved up to 5421H, 21 bytes higher, so the displacement has to be reduced by the same 21 bytes to reach the same dispatcher. Without the patch the Percom variant entered its driver 21 bytes past the dispatcher, in the middle of the operation-decoding chain. FDUBL1 is therefore the fix that makes Percom mode work at all.
Program Structure
The file loads as a single contiguous image from 5200H to 557CH, with a transfer address of 521EH. Everything from 5415H upward is either part of the resident module or install-time source material for it; everything below 5415H is install-time code, tables and messages that are discarded once the module is in place.
| Address Range | Contents |
|---|---|
| 5200H-521DH 30 bytes | Relocation address table. Fifteen 2-byte entries, each holding the address of an instruction inside the driver module whose 16-bit operand must be adjusted when the module is moved to high memory. The Percom build uses only the first fourteen. |
| 521EH-530AH 237 bytes | Install-time code. Banner display, parameter scan, optional construction of the Percom variant, duplicate-installation check, operand relocation, the move into high memory and the Drive Control Table hook-up. |
| 530BH-5332H 40 bytes | Exit paths. Success return, the JCL-aware abort test, and the two error reports. |
| 5333H-5353H 33 bytes | @PARAM keyword table for the PERCOM and TANDY parameters. |
| 5354H-535CH 9 bytes | Install-time control variables: the TANDY flag, the module length, start address, end address and relocation entry count. |
| 535DH-540BH 175 bytes | Message text: the two error messages and the sign-on banner. |
| 540CH-5414H 9 bytes | Resident module header for the module named $FDD. This is the base of the Radio Shack build. |
| 5415H-5420H 12 bytes | Radio Shack write-precompensation routine. Present in the Radio Shack build only; in the Percom build the module header is copied over the bytes above it and this routine falls outside the module. |
| 5421H-5429H 9 bytes | Radio Shack build: the tail of the precompensation routine and the start of the seek hand-off. Percom build: the relocated copy of the module header, and the base of the module. |
| 542AH-5557H 302 bytes | The driver body proper. Density toggle and geometry reseed, the operation dispatcher, read/write sector setup, the inline parameter block reader, and the command issue and data transfer engine. |
| 5558H-5563H 12 bytes | Density latch routine. Holds the Radio Shack version as assembled; the Percom version is copied over it at install time. This is the last byte of the Radio Shack module. |
| 5564H-5573H 16 bytes | Source copy of the Percom density latch routine. Install-time only; it is copied down to 5558H and the original is discarded. |
| 5574H-557CH 9 bytes | The FDUBL1 patch routine, which sets the module header's entry displacement for the Percom build. Install-time only. |
Variables and Self-Modified Cells
FDUBL modifies its own image extensively while it decides which variant to build. Every cell below is written by install-time code before the module is relocated, so the value present in the disk file is the Radio Shack default and the value the driver actually runs with may be different.
| Address | Purpose |
|---|---|
| 523CH-523DH 2 bytes | PERCOM parameter flag. This is the operand of the LD HL,0000H instruction at 523BH. @PARAM writes 0FFFFH here when PERCOM or P is supplied on the command line. Self-modifying code. |
| 5354H-5355H 2 bytes | TANDY parameter flag. @PARAM writes 0FFFFH here when TANDY or T is supplied. It is only ever tested to reject the combination of both parameters. |
| 5356H-5357H 2 bytes | Module length in bytes. Assembled as 0158H for the Radio Shack build; overwritten with 0147H for the Percom build. Used as the LDDR byte count when the module is copied to high memory. |
| 5358H-5359H 2 bytes | Module start address. Assembled as 540CH for the Radio Shack build; overwritten with 5421H for the Percom build. Held for reference only; the copy is driven from the end address. |
| 535AH-535BH 2 bytes | Module end address, the last byte to be copied. Assembled as 5563H for the Radio Shack build; overwritten with 5567H for the Percom build. Used both as the LDDR source pointer and as the base from which the relocation offset is computed. |
| 535CH 1 byte | Relocation entry count. Assembled as 0FH (15) for the Radio Shack build; decremented to 0EH (14) for the Percom build, because the precompensation routine that the fifteenth entry refers to is not part of the Percom module. |
| 540DH 1 byte | The relative-jump displacement inside the module header, which is the driver's entry point. Assembled as 41H, correct for a module based at 540CH. The FDUBL1 patch routine writes 2CH here for the Percom build, whose module is based at 5421H. Self-modifying code. |
| 540EH-540FH 2 bytes | Resident module chain link word, stored one byte below the address of the next module header. Assembled as 0000H and filled in by the module lookup chain, which is walked from HIGH$ at 4049H. |
| 542BH-542CH 2 bytes | The operand of the JP 462DH at 542AH, the hand-off into the resident seek routine. Rewritten to 462AH for the Percom build so that the resident routine is entered one step earlier, at its controller-ready wait. Self-modifying code. |
| 545DH 1 byte | The displacement of the JR Z,5415H at 545CH, which sends a seek operation to the precompensation routine. Rewritten to 0CCH for the Percom build so the jump instead reaches 542AH and skips precompensation entirely. Self-modifying code. |
| 54C3H-54C4H 2 bytes | The operand of the CALL Z,5415H at 54C2H, the implied seek performed before a read or write. Rewritten to 462AH for the Percom build. Self-modifying code. |
| 550AH-550BH 2 bytes | The two-byte body of the sector data transfer loop, planted at run time by the inline parameter block reader at 54B1H. Holds 1AH 02H (LD A,(DE) then LD (BC),A) for a read, 12H 0AH (LD (DE),A then LD A,(BC)) for a write, and 1AH 7EH (read the data register and discard it) for a verify. Self-modifying code. |
| 46F6H 1 byte | Not part of FDUBL. This is the base error number inside the resident controller status decoder in SYS0, and FDUBL writes it directly through the same inline parameter block reader. 01H for a read or verify, 09H for a write. |
Major Routines
| Address | Name and Purpose |
|---|---|
| 521EH | Program Entry Entry from DOS with Register Pair HL pointing at the command line tail. Saves the DOS stack pointer into the exit code, displays the banner, and scans the command line through @PARAM. Exits through 530BH. |
| 5243H | Percom Variant Construction Reached only when PERCOM was supplied. Rejects PERCOM together with TANDY, copies the Percom density latch routine over the Radio Shack one, rewrites three operands and one jump displacement to remove write precompensation, and re-bases the module from 540CH to 5421H. |
| 5286H | Duplicate Installation Check Walks all eight Drive Control Table entries, follows each drive's driver vector, and compares the five bytes at driver+4 against the name-length byte and name of the $FDD module. A match means the driver is already resident and the utility aborts. |
| 52B2H | Operand Relocation Computes the distance the module is about to move and walks the relocation address table at 5200H, adding that distance to the 16-bit operand of each listed instruction. |
| 52DCH | Module Installation Copies the module down into high memory ending at HIGH$, lowers HIGH$ by the module length, and points the driver vector of every fitted drive at the relocated module while marking the drive double-density capable. |
| 530BH | Exit Restores the DOS stack pointer, returns with Register Pair HL holding 0000H for success or 0FFFFH for failure, and diverts to @ABORT only when a JCL job is in effect. |
| 531DH | Both Doublers Specified Error Displays "Can't specify both Percom and Tandy!" and exits with an error. Overlaps the routine below it. |
| 5321H | Driver Already Resident Error Displays "Driver is already resident!" and exits with an error. Shares its final two instructions with the routine above it. |
| 5415H | Write Precompensation and Seek Radio Shack build only. Enables write precompensation on the inner half of the disk and hands over to the resident seek routine. Removed from the Percom build. |
| 542DH | Density Toggle and Geometry Reseed Error recovery. Inverts the drive's density flag, reloads the drive geometry bytes for the new density, sets the hardware density latch, and optionally restores the head to track zero. |
| 544FH | Driver Entry, Operation Dispatcher The address the module header's relative jump reaches and therefore the entry point of the whole driver. Register B holds the LDOS disk operation code; the routine dispatches to the appropriate handler. |
| 5477H | Read, Write and Verify Sector Setup Sets the density latch, chooses the controller command byte and the retry count for the operation, refuses a write to a write-protected drive, and installs the transfer loop body. |
| 54B1H | Inline Parameter Block Reader Called with three parameter bytes following the CALL. Plants the first byte in the resident status decoder at 46F6H and the following word in the transfer loop at 550AH, then falls through into the command issue routine rather than returning. |
| 54BFH | Command Issue and Data Transfer Performs the implied seek, writes the track and command registers, runs the unrolled data request loop that moves 256 bytes to or from the controller data register, and handles status, retries and the density fallback. |
| 5558H | Density Latch Sets the hardware density latch from the drive's density flag. Assembled as the Radio Shack version, which writes the controller sector register at 37EEH; overwritten by the Percom version, which writes the controller command register at 37ECH. |
Cross-Reference Notes
FDUBL calls into the resident SYS0 core for every service it does not implement itself. The install-time code uses @DSPLY (4467H), @PARAM (4476H), @LOGOT (447BH), @ERROR (4409H) and @ABORT (4030H), reads and rewrites HIGH$ (4049H), tests SFLAG$ (430FH) bit 5 to decide whether a JCL job is running, and rewrites the Drive Control Table at 4700H.
The resident driver module continues to depend on six SYS0 entry points. 45E0H selects the drive and writes the drive-select latch at 37E1H. 461CH waits for the controller to become not-busy, re-asserting the drive-select latch on every pass so the motor stays selected. 462AH and 462DH are the two entry points of the resident seek routine, the first including the not-busy wait and the second beginning at the track register write. 4659H issues a Type I command with the drive's stepping rate merged in, and 4662H is its second entry for a command byte that is already assembled. 46FDH is the resident interrupt-suppression cell that the SYSTEM SMOOTH option patches. 46F4H is the resident routine that converts a controller status byte to an LDOS error number, and 46F6H is the base error number inside it, which FDUBL writes directly.
Nothing calls FDUBL. Once installed, the $FDD module is reached only through the Drive Control Table vectors that FDUBL rewrote, and it can be located by name through the resident module lookup service, RST 28H request code 0BCH, which is serviced by SYS10.
Disassembly:
5200H - Relocation Address Table
Fifteen 2-byte entries. This is data, not code. Each entry holds the address of a 3-byte or 4-byte instruction inside the driver module whose 16-bit operand refers to another address inside the module. Before the module is copied to high memory the routine at 52B2H walks this table and adds the relocation distance to each of those operands. An entry points at the byte immediately BEFORE the operand, because the relocation loop performs an INC HL after loading the entry, so an entry of 547AH refers to the operand at 547BH-547CH. The last entry is used only by the Radio Shack build; the Percom build decrements the count at 535CH from 15 to 14 to drop it, because the instruction it refers to has already been rewritten to point at a resident SYS0 address that must not be relocated.
521EH - Program Entry, Sign-On Banner and Parameter Scan
The transfer address of the file. LDOS enters here with Register Pair HL pointing at the first character of the command line following the command name. The routine preserves the DOS stack pointer for the exit path, displays the sign-on banner, and hands the command line to @PARAM to pick up the PERCOM and TANDY keywords.
Store the stack pointer as supplied by DOS into 530FH-5310H. That pair of bytes is the operand of the LD SP instruction at 530EH in the exit routine, so this instruction plants the DOS stack pointer that the exit path will restore. The listing shows the initial value 0000H; the runtime value is whatever SP held when DOS transferred control here.
Load Register Pair HL with the contents of 523CH-523DH, which is this instruction's own operand and is the cell named by the PERCOM keyword in the @PARAM table at 5333H. The listing shows the assembled value 0000H, meaning PERCOM was not requested; @PARAM writes 0FFFFH here when PERCOM or P appears on the command line.
5243H - Percom Variant Construction
Reached only when PERCOM or P appeared on the command line. The Percom Doubler sets its density latch through the controller command register at 37ECH rather than through the sector register at 37EEH, and it provides no write precompensation, so this routine rebuilds the driver: it copies the Percom density latch routine over the Radio Shack one, redirects the two references to the precompensation routine at 5415H to the resident seek routine instead, moves the module header up 21 bytes so that the precompensation routine falls outside the module, and adjusts the length, base, end and relocation count accordingly.
Store Register A, holding 0CCH, into 545DH, which is the displacement byte of the JR Z instruction at 545CH in the operation dispatcher. The assembled value there is 0B7H, reaching 5415H. After this store a seek operation jumps to 542AH instead, so the Percom driver never touches write precompensation.
Store Register Pair HL, holding 462AH, into 542BH-542CH, the operand of the JP instruction at 542AH. As assembled that instruction is JP 462DH, entered after the precompensation routine has already performed the not-busy wait. With precompensation removed the wait has to be reinstated, so the target becomes 462AH.
Store Register Pair HL, holding 462AH, into 54C3H-54C4H, the operand of the CALL Z instruction at 54C2H that performs the implied seek before a read or write. As assembled that call reaches the precompensation routine at 5415H; it now calls the resident seek routine at 462AH directly. This is also why the fifteenth relocation table entry is dropped: the operand no longer refers to an address inside the module.
5286H - Duplicate Installation Check
Walks all eight Drive Control Table entries at 4700H. Each entry begins with a 3-byte JP instruction whose operand is the address of the driver serving that drive. This loop follows that operand, steps four bytes into the driver to reach where a resident module header keeps its name-length byte, and compares the five bytes found there against the pattern at 5410H, which is the name-length byte 04H followed by the four characters $FDD. A match means an FDUBL driver is already installed on that drive and the utility refuses to install a second one.
Save Register Pair HL onto the stack. Register Pair HL holds the address of the current Drive Control Table entry's driver operand, and the comparison below destroys it. Each pass of this loop examines one of the eight table entries.
Load Register A with the byte at the address in Register Pair DE, one character of the name $FDD taken from 5411H-5414H.
Falling through means all four characters matched and a $FDD module is already serving this drive.
52B2H - Relocation of the Module's Address Operands
Computes how far the module is about to move and applies that distance to every operand named in the relocation address table at 5200H. The module will be copied so that its last byte lands on HIGH$, so the distance is HIGH$ minus the module's present end address, held at 535AH.
Load Register L with the byte at the address in Register Pair IX, which addresses the current relocation table entry within 5200H-521DH. This is the low half of the address of the instruction to be fixed up.
52DCH - Module Installation and Drive Control Table Hook-Up
Copies the finished module into high memory so that its last byte occupies HIGH$, lowers HIGH$ by the module length to protect it, and then rewrites the driver address inside the Drive Control Table JP vector of every fitted drive so that all disk operations are routed through the new module.
Test bit 4 of the byte at offset 04H in the Drive Control Table entry addressed by Register Pair IY. That byte carries the drive's secondary specification, including the double-sided flag in bit 5 and the physical unit number in the low nibble. Bit 4 marks a unit that this driver must not take over. If the bit is clear the Z FLAG is set; if it is set the NZ FLAG is set.
The driver is now installed on every eligible unit.
530BH - Exit
The common exit for both the success and the failure paths. Every LDOS library command returns with the error code in Register Pair HL, zero for success and 0FFFFH for failure, and diverts to @ABORT only when a JCL job is running, which is signalled by bit 5 of SFLAG$ at 430FH.
Load the stack pointer from this instruction's own operand at 530FH-5310H. The listing shows the assembled value 0000H; at run time the instruction at 521EH has already stored the stack pointer that DOS supplied at entry, so this restores the DOS stack regardless of what the program left on it.
531DH - Error Message Display and Error Exit
Two error reports sharing one message display sequence. The entry at 531DH loads the address of the "Can't specify both Percom and Tandy!" message and then executes an LD IX instruction whose second byte begins a second, independent LD HL instruction; the entry at 5321H starts on that second byte instead and therefore loads the address of the "Driver is already resident!" message. Both then fall into the same @LOGOT call and error exit.
The four bytes at 5320H-5323H are executed two different ways. Arriving from 531DH they are read as the LD IX instruction above and the message address already in Register Pair HL survives. Arriving from 52A5H, which jumps to 5321H, execution starts on the second byte and reads them as the LD HL instruction below, which replaces the message address. The row that follows shows that second interpretation.
5333H - @PARAM Keyword Table
The parameter table handed to @PARAM at 522DH. This is data, not code. Each entry is a 6-character keyword padded with blanks followed by the 2-byte address of the cell that receives the parameter's value, and the table is terminated by a zero byte. PERCOM and its abbreviation P both deposit into 523CH, the operand of the LD HL instruction at 523BH. TANDY and its abbreviation T both deposit into 5354H. @PARAM stores 0FFFFH in the named cell when the keyword is present.
5354H - Install-Time Control Variables
Five cells that describe the module about to be installed. This is data, not code. The values shown are those assembled into the file, which describe the Radio Shack build; the Percom construction routine at 5243H overwrites four of the five.
535DH - Message Text
The two error messages and the sign-on banner. This is data, not code. Each message ends with the 0DH terminator that @DSPLY and @LOGOT stop on; the banner contains an embedded 0AH line feed to separate its two lines.
540CH - Resident Module Header
The standard LDOS resident module header, which makes the driver discoverable by name through the module lookup service, RST 28H request code 0BCH. This is data with one executable byte pair: the 18H signature is also the opcode of a relative jump, so the address a Drive Control Table vector points at is simultaneously the header and the driver's entry point. In the Radio Shack build the header stays here at 540CH-5414H; in the Percom build the LDDR at 526EH copies these nine bytes up to 5421H-5429H and the module starts there instead.
The displacement of the relative jump begun by the signature byte. 41H is decimal 65, which from a header based at 540CH reaches 544FH, the driver's operation dispatcher. The routine at 5574H added by the FDUBL1 patch overwrites this with 2CH for the Percom build, whose header sits 21 bytes higher at 5421H and therefore needs a displacement 21 smaller to arrive at the same dispatcher.
5415H - Write Precompensation and Seek (Radio Shack build)
Radio Shack Double-Density Adapter builds only. Write precompensation shifts the timing of recorded bit cells slightly to counteract the crowding that occurs on the inner, shorter tracks of a diskette, and the Radio Shack board implements it while the Percom, LNW and Omikron boards do not. The routine enables precompensation once the head has passed the midpoint of the disk and then hands over to the resident SYS0 seek routine. Its control byte reaches the adapter through the controller's sector register at 37EEH: on the Radio Shack board the three high-order bits of a write to that address drive a one-of-eight decoder that latches the density selection and the precompensation enable, and because a genuine sector number never has those bits set, ordinary sector writes pass through untouched. In the Percom build this routine is not part of the module; the header is copied over 5421H-5429H, the jump at 545CH is redirected to 542AH and the call at 54C2H is redirected to the resident routine at 462AH.
In the Percom build the install-time code at 525FH rewrites the operand at 542BH-542CH to 462AH, so the resident wait is performed here instead and the precompensation routine above is bypassed entirely. This instruction is also the target the patched jump at 545CH reaches in the Percom build.
542DH - Density Toggle and Geometry Reseed
Error recovery, called from 5540H when a sector operation fails with a record-not-found status. The most common cause on a double-density system is a diskette recorded in the other density, so this routine inverts the drive's density flag, reloads the two Drive Control Table geometry bytes with the sector and granule layout that matches the new density, and sets the hardware density latch. On alternate retries it also restores the head to track zero.
544FH - Driver Entry and Disk Operation Dispatcher
The driver's entry point. Every Drive Control Table vector that FDUBL rewrote points at the module header, whose relative jump lands here. Register B holds the LDOS disk operation code and Register Pair IY addresses the Drive Control Table entry for the drive concerned. The structure follows the resident SYS0 dispatcher at 45FBH, with the seek and read/write cases redirected into this module and the remaining cases handed straight back to the resident code.
The displacement byte at 545DH is rewritten to 0CCH at 5259H for the Percom build, which redirects this jump to 542AH so that precompensation is skipped entirely.
5477H - Read, Verify and Write Sector Setup
Reached from 5457H for every operation code above 07H. Sets the hardware density latch for the drive, chooses the controller command byte and the retry count appropriate to the operation, refuses a write to a write protected drive, and installs the correct two-byte body into the data transfer loop by way of the inline parameter block reader at 54B1H. The three parameter blocks embedded in this routine are read as data by that routine, never executed.
54B1H - Inline Parameter Block Reader
Called with three parameter bytes placed immediately after the CALL. The routine takes the return address off the stack, uses it to read those three bytes, and then discards it, so control never comes back to the caller and instead falls through into the command issue routine below. The first parameter byte sets the base error number inside the resident controller status decoder in SYS0, and the following word is planted as the two-byte body of this module's data transfer loop.
Store Register A, holding the base error number, into 46F6H. That address is not part of this module: it is the operand of the LD A instruction at 46F5H inside the resident controller status decoder in SYS0. That decoder adds the position of the lowest set bit of the controller status byte to this base to produce the LDOS error number, so writing 01H here makes it report read errors and writing 09H makes it report write errors.
Store Register Pair HL, holding the two transfer opcodes, into 550AH-550BH, the body of the data transfer loop. The listing shows those two bytes as 00H; at run time they hold LD A,(DE) and LD (BC),A for a read, LD A,(DE) and LD A,(HL) for a verify, or LD (DE),A and LD A,(BC) for a write.
54BFH - Command Issue, Data Transfer and Error Recovery
The heart of the driver, entered by falling through from the parameter block reader and re-entered at the same address for every retry. It performs the implied seek, loads the controller track register, adjusts the deleted data address mark command for the density in use, issues the command, runs the data transfer loop, and then examines the resulting status. A lost data indication retries immediately; a record not found or CRC failure toggles the density before retrying; anything else is converted to an LDOS error number by the resident decoder. The transfer loop itself is written out five times over so that the driver can keep pace with the doubled data rate of a double-density track, where a data request arrives roughly every 16 microseconds.
Save Register Pair BC onto the stack. Register B holds the retry count and Register C the controller command byte, and both are destroyed by the transfer below. Every retry re-enters at this instruction.
The operand at 54C3H-54C4H is rewritten to 462AH at 5262H for the Percom build, so that the seek is performed by the resident routine alone and no precompensation is attempted.
Enable interrupts. The write path waits here for the controller to ask for its first byte, and briefly admitting interrupts on each pass keeps the real time clock running while the head rotates to the target sector.
Compare Register A, holding the expected busy-with-no-request pattern, against the byte at the address in Register Pair HL, which holds 37ECH, the live controller status. While they match the controller has neither asked for the next byte nor finished, and the Z FLAG is set.
Falling through means the controller has raised a data request or changed state.
First opcode of the data transfer loop body, planted by the parameter block reader at 54BBH. The listing shows the assembled placeholder 00H. At run time it is 1AH, LD A,(DE), reading the controller data register at 37EFH for a read or verify, or 12H, LD (DE),A, writing the prefetched buffer byte to it for a write.
Second opcode of the data transfer loop body, planted by the same store at 54BBH. At run time it is 02H, LD (BC),A, storing the byte just read into the caller's buffer for a read; 7EH, LD A,(HL), discarding it for a verify; or 0AH, LD A,(BC), prefetching the next buffer byte for a write. This is the address the read path enters through Register Pair IX, having already fetched its first byte at 5554H.
Test bit 1, the data request, of the byte at the address in Register Pair HL, which holds 37ECH, the controller status register. If the controller wants another byte the NZ FLAG is set. The five copies of this test that follow are an unrolled loop: at double density a data request arrives about every 16 microseconds, so testing repeatedly without the cost of a loop-back on every pass is what allows the driver to keep up.
Falling through means the command has completed and the status register at 37ECH holds the final result.
Load Register A with the byte at the address in Register Pair HL, which holds 37ECH, the controller status register. This is the read path's wait for the first byte of the sector.
Parity even ends the wait, and covers both outcomes worth acting on: both bits set, meaning a byte is waiting, and both bits clear, meaning the command finished without ever producing one.
5558H - Density Latch, Radio Shack Double-Density Adapter
Sets the hardware density selection from the drive's density flag, and is the last routine of the Radio Shack module. On the Radio Shack Double-Density Adapter the three high-order bits of a write to the controller's sector register at 37EEH are decoded by a one-of-eight decoder on the adapter board and used to latch the density selection and the write-precompensation enable; because a real sector number never has those bits set, the ordinary sector writes the resident driver performs are unaffected. When the Percom variant is built, the install-time block move at 5255H overwrites these twelve bytes with the sixteen-byte Percom version from 5564H, so every caller reaches the right mechanism without any call site changing.
5564H - Density Latch, Percom Doubler (install-time source)
The Percom Doubler carries no decoder on the sector register. Its density latch is set instead by writing a command byte to the controller command register at 37ECH that no Western Digital controller implements, 0FEH or 0FFH, which the board recognises and consumes; a Force Interrupt is then issued so that the controller does not sit waiting on the meaningless command. These sixteen bytes are never executed where they are assembled: the install-time block move at 5255H copies them over the Radio Shack routine at 5558H, and this copy falls outside the module and is discarded.
5574H - FDUBL1 Patch Routine
Added to the file by the applied patch FDUBL1, whose patch records replace the instruction at 5265H with a call to this routine and append these nine bytes. Its only purpose is to correct the module header's entry displacement for the Percom build. It runs at install time and is not part of either module.
Store Register A, holding 2CH, into 540DH, the displacement byte of the relative jump in the module header. The header is copied to 5421H by the LDDR at 526EH immediately after this routine returns, so the corrected displacement travels with it. Without this store the relocated Percom module entered its driver at what would be 5464H in the assembled listing, in the middle of the dispatcher's step-code comparison, which is the defect FDUBL1 repairs.