Atari ST Floppy DMA: WD1772 Commands, Sector Counts, and Completion
Follow an Atari ST floppy request through the shared DMA/FDC interface, from drive selection and buffer setup to MFP completion and error checks.
An Atari ST floppy transfer is a coordinated operation between the WD1772 floppy-disk controller (FDC), the DMA controller, the floppy drive, and the MFP interrupt input. The 68000 does not simply read a sector register and copy bytes from the drive. It prepares a DMA address and direction, programs a sector count, issues an FDC command through the DMA interface, and waits for the controller’s completion path before interpreting status.
This article follows the original ST-family engineering contract for sector reads and writes. It does not generalize over every ST, STE, TT, Falcon, replacement controller, or accelerated board. The cited Atari engineering specification is dated January 7, 1986; the exact board, WD1772 revision, drive, and TOS/EmuTOS version still matter. Normal applications should use the operating system’s floppy services. Raw register ownership belongs in a driver, emulator, or controlled hardware diagnostic that can exclude TOS and other DMA clients.
The FDC and DMA form one shared transaction path
Atari’s hardware specification describes the on-board WD1772 as being accessed through the DMA controller. The DMA path is shared with the hard-disk interface and provides one base/counter channel, so only one DMA operation can be active at a time. A read request consequently needs a single owner from setup through completion; issuing a second disk request or another shared-DMA operation in the middle is a resource conflict, not safe parallelism.
The DMA address is held in high, middle, and low byte registers, but the Atari specification explicitly says to load the base/counter low, then middle, then high. Preserve that documented order. The controller’s byte and word access rules also matter: the FDC register is selected through the DMA mode-control register, and a later write to the disk-controller register supplies the command or data byte. A correct value written to the wrong selected register is still a malformed transaction.
The disk-select and side signals are configured through the system’s PSG I/O path before the transfer. That register has other platform-visible uses, so a raw driver must preserve unrelated state and hold the same ownership discipline described in Atari 520ST YM2149: PSG Register Access Without Port Collisions. Drive selection is not a property inferred from the FDC’s track register; it is part of the outer bus setup.
Follow the documented setup sequence
The 1986 Atari specification lays out the floppy DMA sequence in a specific order:
- Select the drive and side.
- Load the DMA base address/counter.
- Toggle the DMA read/write control to clear transfer state, then select the desired direction.
- Select the DMA sector-count register and write the count, which triggers DMA setup.
- Select the FDC command register and issue the read or write command through the disk-controller register.
- Let the DMA/FDC operation run to completion; do not poll the DMA sector-count-zero status while the transfer is active.
- Observe the completion interrupt and then check the DMA error status and FDC result.
The example below is a sequencing model for a driver test, not code for an ordinary application to poke into a live TOS system. select_drive_side, write_dma_base_low_mid_high, and the other helpers stand for width-correct, serialized register operations; they intentionally hide machine-specific interrupt masking and resource-locking details.
bool read_sector(struct drive *d, unsigned track, unsigned side,
unsigned sector, uint8_t *buffer)
{
if (!dma_buffer_is_valid_for_target(buffer, 512))
return false;
if (!acquire_shared_disk_dma_owner())
return false;
select_drive_side(d, side);
write_dma_base_low_mid_high(buffer);
reset_dma_transfer_state();
select_dma_read();
select_dma_sector_count();
write_dma_sector_count(1);
select_fdc_command_register();
issue_read_sector(track, side, sector);
bool completed = wait_for_mfp_fdc_completion_with_timeout();
if (!completed) {
issue_fdc_force_interrupt();
wait_for_force_interrupt_to_settle_and_clear_pending_fdc_state();
release_shared_disk_dma_owner();
return false;
}
select_dma_sector_count();
bool dma_ok = read_dma_error_status() == 0;
bool fdc_ok = inspect_fdc_command_result();
release_shared_disk_dma_owner();
return dma_ok && fdc_ok;
}
The buffer check is a deliberate placeholder for the target driver’s DMA-accessibility and alignment rules; do not copy the routine as if it were a complete register implementation. The Atari specification says floppy sectors are 512 bytes, but does not make an arbitrary C pointer a valid DMA address. A driver must use the platform’s DMA-safe memory contract, avoid buffer overruns, and account for the actual target’s RAM map.
The FDC command and DMA do not report success through one interchangeable flag. Atari’s specification says to detect WD1772 command completion through the disk-drive-controller interrupt on the MFP’s general-purpose I/O register, and explicitly warns not to poll the FDC Busy bit or DMA Sector Count Zero while active. It also notes that the DMA error status is nondestructive, but the Sector Count Register must be selected before testing its error bit. Keep the register-selection operation in the status path, not only in setup.
Model completion, timeout, and shared state explicitly
For an emulator, represent the request as a transaction with separate phases: selected drive/side, programmed DMA address, sector count, selected FDC register, active FDC command, byte movement, DMA status, FDC result, and MFP completion. Do not make “sector count reached zero” the completion event just because a simple DMA model naturally decrements it. The Atari document says software should observe the MFP interrupt path and warns against polling that status while active.
The pinned Hatari fdc.c implementation is a useful implementation cross-check. It models FDC command states and a DMA structure separately, including the ST DMA’s internal FIFO, transfer progress, and status/control register selection. It is not a substitute for the Atari specification or a physical measurement, and its comments call out compatibility and timing behavior as emulator-specific. EmuTOS’s pinned bios/floppy.c shows the higher software layer coordinating DMA/FDC ownership, preparing sector requests, waiting with a timeout, forcing an interrupt on timeout, and decoding DMA and FDC errors. These are implementation references for software behavior, not proof that every TOS release or machine revision is identical.
The Atari engineering notes add recovery constraints. Every FDC command should have a bounded timeout; after a timeout, the documented note calls for a force-interrupt command. The pseudocode’s settling helper must wait for Force Interrupt to take effect and account for the resulting FDC/MFP state before shared DMA ownership is released; pinned EmuTOS delays explicitly after issuing it. The WD1772 data sheet warns that another command within 16 microseconds in double-density mode or 32 microseconds in single-density mode can nullify the interrupt. Do not deselect the drive until the WD1772 Motor On status is low. Maintain a per-drive configuration table rather than assuming one stepping-rate and geometry works for all 3.5-inch drives. The specification also states that the ST hardware does not detect disk removal, so software must not treat an unchanged selected drive as proof that media stayed present.
Multisector commands need particular care because command completion, track boundaries, and sector-count handling interact. Atari’s example sequence includes an FDC force-interrupt step for multi-sector transfers except at track boundaries. Preserve the precise sequence and conditions from the target hardware reference; do not add an unconditional force interrupt after each sector or transplant a single-sector test into a multisector routine without updating its expected status and interrupt behavior.
Use a bounded verification matrix
In a disposable emulator profile with a known disk image, read one known 512-byte sector into a DMA-safe buffer and compare the bytes with an independent image extraction. Then test an absent sector, write-protected media, a missing drive, and a deliberately held command path that reaches the timeout. Record the selected drive/side, track/sector, DMA address bytes in write order, direction, count, MFP signal, DMA status, FDC status, and whether the driver released its shared-DMA lock. Use a copied image for any write test and verify the image afterward; never use an archival original as the first write fixture.
For a hardware or ROM integration test, first confirm that no resident driver or operating-system floppy routine owns the DMA/FDC. Check that the hardware variant matches the reference, the boot image is recoverable, and the test has a bounded abort path. Compare emulator traces with the Atari specification and the selected Hatari/EmuTOS revisions, while labeling any behavior supported only by those implementations as implementation evidence rather than universal hardware law.
A robust Atari ST floppy path treats the DMA registers, WD1772 command state, MFP completion signal, drive motor, and shared-DMA owner as one lifecycle. That model makes a failed read diagnosable as address setup, command issue, transfer, controller status, interrupt delivery, or timeout recovery instead of reducing every symptom to “the floppy is slow.”
Related:
- Atari ST MC68901 MFP: Timer Modes, Interrupt Priority, and In-Service State
- Atari ST Shifter Screen Fetching: Base Registers, Address Counter, and MMU Timing
Sources:
- Atari Corporation, Engineering Hardware Specification of the Atari ST Computer System (January 7, 1986; archival scan)
- Atari Corporation, Engineering Hardware Specification of the Atari ST Computer System (January 7, 1986; primary scan)
- Atari ST Internals, programmer’s reference with STFM/STE peripheral register material at immutable commit
80a3ebd8a7d5876d62d51571d48b3ac81a92713a - Western Digital WD1770/1772 Floppy Disk Controller/Formatter data sheet, included in Atari’s 1986 GEM programming archive
- Hatari FDC/DMA implementation at immutable commit
32cf7d1a8267b87bbb19b173945d632d3ab4934e - EmuTOS floppy BIOS implementation at immutable commit
02c2db75fc875265c1449efea014450466cf20b7