Western Digital WD1770 FDC

Floppy-disc controller chip used on B+ and Master series. Replaces the original Model B’s 8271 (which was expensive and couldn’t do double-density). The 1770/1772 family supports both FM (single-density) and MFM (double-density) at 250 or 500 kbit/s.

Not present on Electron or Model B (Model B can be upgraded with a 1770 board, replacing the 8271).

Register addresses

The 1770 has 4 registers + 1 external drive-control register:

RegisterB+ addrMaster addrReadWrite
Status / Command&FE84&FE28StatusCommand
Track&FE85&FE29TrackTrack
Sector&FE86&FE2ASectorSector
Data&FE87&FE2BDataData
Drive control&FE80&FE24Drive ctrl

The drive-control register is a separate latch outside the 1770 itself. Bit layouts differ between B+ and Master.

Master drive control (&FE24)

BitField
0DS0 — drive 0 select
1DS1 — drive 1 select
2DS2 — drive 2 select
3RES — pulse low ≥50 µs to reset 1770
4SEL — side 0 / side 1
5DDEN — 0=double-density (MFM), 1=single-density (FM)
6-7unused

Set exactly one of DS0/DS1/DS2 at a time. Actual drive selection only takes effect when the motor turns on.

B+ drive control (&FE80)

Different bit assignment — refer to source page naug-ch16-filing §16.4.2 for the B+ layout.

Command types

Commands are written to &FE28 (or &FE84). Four families:

TypeCommandsPurpose
IRestore, Seek, Step, Step-in, Step-outHead positioning
IIRead sector, Write sectorBulk data transfer
IIIRead address, Read track, Write trackFormat / diagnostic
IVForce interruptAbort + state-machine reset

Type I — head positioning

CmdHexAction
Restore0000 hvrrSeek to track 0
Seek0001 hvrrSeek to track in data register
Step001u hvrrStep one track in same direction
Step-in010u hvrrStep toward centre
Step-out011u hvrrStep toward edge

Common flags:

  • h (bit 3): 0 = enable motor spin-up sequence (6 index pulses ≈ 1.2 s at 300 rpm); 1 = disable spin-up (motor already running).
  • v (bit 2): 1 = verify destination by reading next ID field.
  • r1, r0 (bits 1-0): stepping rate.

Stepping rates differ between 1770 and 1772:

r1 r017701772
006 ms6 ms
0112 ms12 ms
1020 ms2 ms
1130 ms3 ms

u (bit 4 on Step variants): update Track register from internal counter.

Type II — sector R/W

CmdEncoding
Read sector100m he00
Write sector101m hepa0
  • m (bit 4): 0 = single sector, 1 = multi-sector until end-of-track or Type IV abort.
  • e (bit 2): 1 = wait 15 ms after head settle.
  • p (bit 1, write only): 0 = enable write precompensation.
  • a0 (bit 0, write only): 0 = normal data address mark, 1 = deleted.

The 1770 generates an NMI for every byte transferred — your NMI handler must read/write the data register within 32 µs (DD) or 64 µs (SD) per byte or the LD (lost data) status bit sets.

Type III — track-level

  • Read address (1100 he00): reads next 6-byte ID field (track / side / sector / sector-length / 2× CRC).
  • Read track (1110 he00): dump entire track including gaps/headers — for diagnostics.
  • Write track (1111 hep0): format track — full sector layout written from data stream provided by CPU.

Type IV — force interrupt

1101 i3 i2 i1 i0. Common values:

  • &D0 — terminate current command without generating an NMI.
  • &D4 — NMI on next index pulse (i2=1).
  • &D8 — immediate NMI (i3=1).

Use &D0 to abort gracefully (e.g. canceling a multi-sector read mid-way). Immediate-NMI form (&D8) must be cleared with &D0 before issuing the next command, and you must wait ≥16 µs (SD) or ≥32 µs (DD) before the next command write.

Status register bits

LDA &FE28 (or &FE84). Bit meanings depend on command type:

BitType IType II/III
7MON — motor onMON
6WRP — write protectWRP
5SU — spin-up completeRT — record type (0 = data, 1 = deleted)
4RNF — record not foundRNF
3CRC — CRC errorCRC
2LD — lost dataLD
1DRQ — index holeDRQ — data request
0BUSY — command in progressBUSY

Critical rule (§16.4.3 p273): don’t poll the status register to check command completion. A read of the status register resets a pending NMI — if you poll right when a byte becomes available, you lose the NMI and the byte. Use the NMI itself as the completion signal.

The NMI handler

The 1770 raises NMI for every byte during a sector read/write. The handler must be tight enough to service it within 32 µs (DD) ≈ 64 CPU cycles at 2 MHz.

Standard pattern (from NAUG §16.4.4 p282-283 example):

                ; NMI handler at &0D00 (NMI vector at &FFFA points via OS to here)
                PHA                  ; save A — only the regs you use
                LDA &FE28            ; read status (or &FE84 on B+)
                AND #&1F             ; mask top bits
                CMP #&03             ; DRQ + BUSY both = byte available
                BNE exit             ; not our NMI
                LDA &FE2B            ; read data register (or &FE87 on B+)
.save           STA &2000            ; store — address self-modified
                INC save+1           ; advance dest LSB
                BNE exit
                INC save+2           ; carry into MSB
.exit           PLA
                RTI

Filing systems (DFS / ADFS) install handlers like this at NMI time and yank back ownership when finished — see paged-roms service calls &0B (NMI release) and &0C (NMI claim).

Programming sequence — read one sector

; Assumes drive 0, side 0, double-density, motor already running
LDA #&05         : STA &FE24         ; drive ctrl: DS0=1 DDEN=0
LDA #target_track : STA &FE2B        ; load track into data register
LDA #&19         : STA &FE28         ; Seek (h=1, v=1, r=00)
.wait_seek
                  BIT seek_flag      ; (set in NMI handler)
                  BPL wait_seek
LDA #target_track : STA &FE29        ; update Track register (Seek doesn't auto-update reliably)
LDA #target_sector : STA &FE2A       ; sector register
LDA #&84         : STA &FE28         ; Read Sector (h=1, m=0)
 
; Each byte arrives via NMI; handler reads &FE2B and stores at next dest address.
; Final NMI (with BUSY clear): check status for errors.

Densities and capacities

DensityBits/trackBytes/sectorSectors/trackCapacity per side (80-track)
Single (FM)125 kbit25610200 KB
Double (MFM) — DFS250 kbit25618400 KB
Double (MFM) — ADFS250 kbit25616320 KB
HD (1772 only, rarely used)500 kbit512181.44 MB

DFS = 18 sectors × 256 bytes per track. ADFS uses different sector layout (16/track, 256 bytes).

Bypass-MOS use cases

  • Custom disc image formats — IBM PC discs (NAUG §16.4.4 example), CP/M, Amiga ADF copies.
  • Disc repair / sector editor — read raw sectors with CRC errors and patch.
  • Format checking — Type III “read track” dumps entire raw track for analysis.
  • Faster sequential I/O — DFS’s per-call overhead is significant; for ROM-image work or bulk transfer, direct sector reads can be 2-3× faster.

The trade-off: you take over the NMI vector and risk crashing the machine if your handler is slow or buggy. The 8 KB NMI page (&0D00) only has room for the tight inner loop.

See also


This wiki is curated by Claude following the LLM-Wiki methodology — a human curates source documents, the LLM compiles structured cross-linked markdown. Content may contain errors, omissions, or stale claims. For authoritative information refer to the original source documents in the bbc-documents GitHub archive.