TRS-80 DOS - LDOS 5.3.1 for the Model I - SOLE/CMD Disassembled
Page Customization
Page Index
SOLE/CMD
Other Navigation
Summary:
LDOS 5.3.1 SOLE/CMD Disassembly - Double-Density Boot Installer (Model I)
SOLE/CMD is the MISOSYS utility that converts a single-density LDOS 5.3.1 Model I SYSTEM diskette into one that boots in double density on suitably equipped hardware. It is invoked as SOLE :d; the program loads and runs at 5200H, verifies that drive d holds a ready, single-density, write-enabled 5.25-inch system disk, then writes a double-density boot to track 0 and adjusts the directory so the disk boots in double density.
The work is done in two stages. The installer (5200H-5439H) validates the drive through the resident SYS0 services, copies the drive geometry into a template, and writes three sectors of track 0: sector 0 (the boot sector the ROM runs), sector 5 (the double-density driver, which the boot ROM later loads to 4700H) and sector 6 (the secondary loader, loaded to 4800H). Because track 0 sector N is loaded to memory 4200H plus N times 100H, these are exactly the 4200H, 4700H and 4800H regions documented on the BOOT/SYS page.
The second stage is the boot image itself (5500H-56D4H): a self-contained WD1771 double-density read engine plus the secondary loader that streams the resident system as an LDOS load module. That engine is the companion the BOOT/SYS secondary loader calls at 52D1H/53B0H/53C7H/53CFH, and it also supplies the correct loader preamble the captured single-density BOOT/SYS had lost to fill.
Variable, Buffer and Hardware List
| Address | Purpose |
|---|---|
| 37E1H 1 byte | WD1771 drive-select / density latch (write). The double-density adapter reuses spare latch bits to switch the data separator to double density. |
| 37ECH 1 byte | WD1771 Command register (write) / Status register (read). |
| 37EDH 1 byte | WD1771 Track register (target cylinder). |
| 37EEH 1 byte | WD1771 Sector register (target sector). |
| 37EFH 1 byte | WD1771 Data register (byte transfer). |
| 5290H 1 byte | Saved copy of the last value written to the drive-select / density latch (run image). |
| 5327H 1 byte | Selected drive number (0-7), parsed from the command tail; read into Register C by every disk-primitive wrapper. |
| 5700H 256 bytes | Sector work buffer: holds the boot sector and directory sectors during the write, beyond the loaded file image. |
| 547D-54FFH 131 bytes | Run-time install workspace: 547D-5486H the ten-byte DCT copy; 5481H density byte; 5482H current cylinder; 547EH boot base pointer; 54FC-54FFH the sector-5 preamble. |
| IY - | Points at the selected drive's ten-byte Drive Code Table entry (from GETDCT 478FH). |
Major Routine List
| Address | Name and Purpose |
|---|---|
| 5200H | Entry And Validation Sign on, parse the drive specifier, and confirm the disk is single density, ready and write-enabled. |
| 527FH | Build And Write The Boot Build the four-byte sector preambles and write track 0 sectors 5, 6 and 0, then update the directory and Granule Allocation Table. |
| 5311H | Disk Primitive Wrappers Load the drive number into Register C and jump to the resident SYS0 DCT primitives WRSECT (4763H), WRPROT (4768H), RDSECT (4777H) and VERSEC (4772H). |
| 533CH | Error Handlers Report a DOS error through @ERROR, or display one of the four abort messages through @LOGOT and @ABORT. |
| 5500H | Double-Density Sector Read Engine Seek, read and transfer a sector directly through the WD1771, with retries and automatic single/double density detection. |
| 5612H | Secondary Loader Streams the resident system as an LDOS load module and transfers to its entry point (the 4800H loader on a converted disk). |
Cross-Reference Notes
SOLE/CMD is listed in the LDOS command set on the Main LDOS Disassembly Page. The boot image it installs is the companion to the BOOT/SYS secondary loader; see the BOOT/SYS 4800H section for the loader side and the 4420H Drive Code Table templates. The resident SVC vectors and DCT disk primitives it calls (44xxH, 47xxH, 4B45H, 402DH, 4030H) live in SYS0/SYS; the ROM delay it uses is at 0060H.
Disassembly:
5200H - Installer: Entry, Validation And Write
SOLE.CMD loads and runs at 5200H as a DOS command. It signs on, validates that the selected drive holds a ready, single-density, write-enabled system disk, then builds and writes the double-density boot to track 0 sectors 0, 5 and 6 (which the boot ROM loads to 4200H, 4700H and 4800H) and updates the directory. All disk access is through the resident SYS0 DCT primitives; all display and error output is through the SYS0 SVC vectors.
The DOS command interpreter enters SOLE at 5200H with Register Pair HL pointing at the command line remaining after the word SOLE (the drive specifier the operator typed). Save that command-line pointer (Register Pair HL) on the stack so the sign-on display below can reuse Register Pair HL without losing it.
Fetch the next character of the command line (the byte Register Pair HL points at) into Register A.
Test bit 3 of DCT+03H, the drive-specification flag byte of the selected drive's Drive Code Table entry; bit 3 marks the drive as double density.
Test bit 7 of DCT+04H, the second specification byte; bit 7 records whether track 0 was read as single density.
Load Register L from DCT+01H, the low byte of the disk-driver entry address in the selected drive's Drive Code Table entry.
The driver hook is absent. Point Register Pair HL at 52DDH, the fallback code path SOLE will use instead.
Store the fallback address 52DDH (Register Pair HL) into the operand cell at 5504H, patching the boot image's first driver call.
Store the fallback address 52DDH (Register Pair HL) into the operand cell at 5608H, patching the second copy of that call.
Save the Drive Code Table base pointer (Register Pair IY) by pushing it, to move it into Register Pair HL.
Point Register Pair HL at 5700H, the 256-byte work buffer that will receive track 0 sector 0.
Load Register Pair HL with 5200H, SOLE's own load origin and the base used to compute the boot-image pointers.
INCrement Register H, advancing the write-buffer pointer (Register Pair HL) by 100H to the sector-6 image, one 256-byte sector further into SOLE's boot image.
Load Register Pair HL with 421CH, an address inside Boot Image A that the rebuilt boot sector needs stamped into it.
Point Register Pair HL at 5700H, the work buffer reused now for directory-track sectors.
Point Register Pair HL at 5445H, the "Your double density SYSTEM diskette can now BOOT." completion message.
Fetch the selected drive number from 5327H into Register A.
Fetch the selected drive number from 5327H into Register A.
Fetch the selected drive number from 5327H into Register A.
Load Register A with 00H.
GOSUB to 5326H, the verify-sector wrapper, which returns the controller status in Register A.
OR 40H into Register A, setting the @ERROR option that returns to the caller after display, leaving the error code in the low bits of Register A.
The four abort entries below overlap. A validation failure JUMPs one byte into an LD IX instruction so the 0DDH prefix is skipped and the bytes decode as LD HL,nnnn, loading the message address into Register Pair HL for the display at 5350H. Entering at 5341H loads the "Track 0 is not single density!" pointer; 5345H loads "Drive is write protected!"; 5349H loads "Drive must be 5" floppy!"; 534DH loads "Drive spec required!". The fall-through path harmlessly loads Register Pair IX three times before reaching 5350H.
Point Register Pair HL at 53DAH, the "Track 0 is not single density!" message string.
5356H - Sign-On And Abort Messages
The sign-on banner and the four abort messages, each terminated by a carriage return. Data, not executed.
"SOLE - Version 5.3.1 - Double Density BOOTing - Phase 2" and the MISOSYS copyright line (embedded 0AH line breaks; 0DH terminates). Displayed at 5204H.
"Drive spec required!" - displayed when the drive specifier is missing or out of range.
"Track 0 is not single density!" - the source disk is already double density.
"Drive is write protected!"
"Drive must be 5" floppy!"
542CH - Read Directory Sector Helper
Reads sector 0 of the directory cylinder (the Granule Allocation Table), marks track 0 allocated to the boot, and writes it back.
Load Register D from DCT+09H, the directory-cylinder number of the selected drive's Drive Code Table entry, so the read targets the directory track.
543AH - Completion Message And Boot Patch Data
The completion message, the boot patch template written into the new boot sector, and the run-time install workspace.
" complete. "
"Your double density SYSTEM diskette can now BOOT." - displayed on success at 5308H.
The start of a sixteen-byte template copied into the boot-sector buffer at 5716H by the LDIR at 52CDH.
An unloaded run-time gap. 547D-5486H receives the ten-byte Drive Code Table copy (LDIR at 5263H); 5481H holds the density-adjusted specification byte; 5482H holds the current-cylinder byte (0); 547EH holds the boot base pointer 5200H; 54FC-54FFH hold the four-byte sector-5 preamble built at 5289H.
5500H - Double-Density Boot Image
The boot-time image the build loop writes to track 0 sectors 5 and 6 (loaded by the boot ROM to 4700H and 4800H). It is stored here at 5500H but assembled to run in the 5200H-53FFH range, so its absolute CALL and JP operands (52xxH/53xxH) name routines that appear in this listing 300H higher. It contains the WD1771 double-density read engine (5500H-5611H) and the secondary loader (5612H-56D4H).
This routine and everything to 56D4H is the boot-time image the build loop wrote to track 0 sectors 5 and 6; it is stored here at 5500H but assembled to run in the 5200H-53FFH range, so its absolute CALL and JP operands (52xxH/53xxH) name routines that appear in this listing 300H higher. Entry reads one sector for the boot. GOSUB to the wait-for-ready routine (shown at 5589H) to let any prior controller command finish.
| 1771 FDC Command: 88H (10001000) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| 1 | 0 | 0 | m | b | E | 0 | 0 | Command=Read Sector Bit 7-5: Read Command (100) m: 1=Multiple Records, 0=Single Record b: 1=IBM format, 0=Non-IBM Format E: 1=Enable HLD/HLT/10ms Delay, 0=Assume Head Already Engaged, no Delay Remainder: Unused (00) | |
Save the retry count (Register B) and Read Sector command (Register C) on the stack for the retry path.
| 1771 Type II Status Register (read after Read Sector) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| N | W | R | F | C | L | D | B | Type II Command Status Register N: 1=Not Ready, 0=Ready W: 1=Write Protected, 0=Not Protected R: 1=Record Type/Deleted, 0=Normal F: 1=Record Not Found, 0=Found C: 1=CRC Error, 0=No Error L: 1=Lost Data, 0=No Loss D: 1=Data Request (DRQ), 0=No Request B: 1=Busy, 0=Not Busy | |
Read one data byte from the FDC Data register (37EFH, the register Pair DE points at) into Register A.
Test bit 1 (Data Request) of the FDC status (37ECH, Register Pair HL).
| 1771 Type II Status Register (read after Read Sector) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| N | W | R | F | C | L | D | B | Type II Command Status Register N: 1=Not Ready, 0=Ready W: 1=Write Protected, 0=Not Protected R: 1=Record Type/Deleted, 0=Normal F: 1=Record Not Found, 0=Found C: 1=CRC Error, 0=No Error L: 1=Lost Data, 0=No Loss D: 1=Data Request (DRQ), 0=No Request B: 1=Busy, 0=Not Busy | |
Load Register B with 0F1H, the hard-error indicator returned when all read attempts fail.
Copy the drive number (Register A) into Register B, the shift counter.
Rotate Register B right through carry; the drive number is counted down one bit at a time.
GOSUB to the wait-for-ready routine (shown at 5589H) so the latch change does not disturb an active command.
| Model I Drive-Select / Density Latch write (37E1H) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| - | - | - | - | d3 | d2 | d1 | d0 | Drive-Select Latch (37E0H-37E3H are one physical latch) Bits 0-3: Drive 0-3 select The double-density adapter reuses spare bits of this latch to switch the WD1771 data separator to double density. | |
Read the WD1771 status register (37ECH) into Register A.
| 1771 Type I Status Register (read after Seek/Restore) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| N | W | H | S | C | T | I | B | Type I Command Status Register N: 1=Not Ready, 0=Ready W: 1=Write Protected, 0=Not Protected H: 1=Head Loaded, 0=Not Loaded S: 1=Seek Error, 0=No Error C: 1=CRC Error, 0=No Error T: 1=Track 0, 0=Not Track 0 I: 1=Index Mark, 0=No Index B: 1=Busy, 0=Not Busy | |
| Model I Drive-Select / Density Latch write (37E1H) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| - | - | - | - | d3 | d2 | d1 | d0 | Drive-Select Latch (37E0H-37E3H are one physical latch) Bits 0-3: Drive 0-3 select The double-density adapter reuses spare bits of this latch to switch the WD1771 data separator to double density. | |
GOSUB to the wait-for-ready routine (shown at 5589H).
Write the assembled command (Register A) to the WD1771 Command register at 37ECH, starting the seek (or, from the read path, the read).
Fetch DCT+03H, the drive-specification byte, into Register A.
Fetch DCT+03H, the drive-specification byte, into Register A.
| 1771 FDC Command: D0H (11010000) | Function Description | ||||||||
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Summary of Bits | |
| 1 | 1 | 0 | 1 | I3 | I2 | I1 | I0 | Command=Force Interrupt Bit 7-4: Command Code (1101) I3: 1=Interrupt Immediately I2: 1=Interrupt on next Index Pulse I1: 1=Interrupt on Ready to Not Ready I0: 1=Interrupt on Not Ready to Ready All 0: Terminate with no interrupt | |
Save the caller's buffer pointer (Register Pair HL) on the stack.
This is the loader written to track 0 sector 6 (which the boot ROM loads to 4800H); it is the same streaming loader documented on the BOOT/SYS page, running here in the 5300H image space. Fetch DCT+07H, the geometry byte of the drive being booted, into Register A.
Add the sectors-per-granule value (Register E) to the running total in Register A.
Store the sectors-per-track total (Register A) at 53BFH, patching the loader's per-track sector count used when the sector number wraps to the next cylinder.
Switch to the ALTERNATE register set, which holds the streaming position (cylinder, sector, buffer index).
Add sectors-per-granule (Register D) to Register A.
GOSUB to the get-next-byte routine (shown at 56B0H) to read the next LDOS load-record type byte into Register A.
GOSUB to the get-next-byte routine (shown at 56B0H) to read one data byte into Register A.
GOSUB to the get-next-byte routine (shown at 56B0H) to read and discard one byte.
GOSUB to the get-next-byte routine (shown at 56B0H) to read and discard the transfer record's length byte.
Switch to the ALTERNATE register set, which holds the streaming position: Register D = cylinder, Register E = sector, Register C = in-buffer index into the 5100H buffer.
SUBtract the per-track sector count from Register A; the 00H operand is patched at 5639H (53BFH in the run image) with the sectors-per-track value computed at load time.
Save Register Pair HL on the stack.
JUMP through the Drive Code Table driver vector (Register Pair IY), entering the installed double-density read routine to perform the sector read.
Four bytes (02H 02H 12H 53H) that trail the loader; the head of the Drive Code Table driver-vector template carried in this image.