MFP, RTC & anything else
===========#==#=======#===============================================#=====
----------------------|DMA, WD1772 Disk Controller |-----
===========#==#=======#===============================================#=====
$FFFF8604.W|RW|WDC_SEC|FDC/HDC Access/Sector Count |
$FFFF8604 WDC_SEC FDC/HDC Access / Sector Count
The FDC is NOT in the 68000 address space. Its four registers
sit behind the DMA chip and are reached through this word.
Select which one with the mode register at $FFFF8606, then
read or write here. Only the low 8 bits are used; the upper
byte is ignored on write and reads back as all ones.
Mode register values to select each FDC register:
DMA read mode DMA write mode
FDC Command (W)/Status(R) $0080 $0180
FDC Track Register $0082 $0182
FDC Sector Register $0084 $0184
FDC Data Register $0086 $0186
DMA sector count (W only) $0090 $0190
WD1772 command encodings written to the Command Register:
Type Command b7 b6 b5 b4 b3 b2 b1 b0 Base
I Restore 0 0 0 0 h v r1 r0 $00
I Seek 0 0 0 1 h v r1 r0 $10
I Step 0 0 1 u h v r1 r0 $20
I Step-in 0 1 0 u h v r1 r0 $40
I Step-out 0 1 1 u h v r1 r0 $60
II Read Sector 1 0 0 m h e 0 0 $80
II Write Sector 1 0 1 m h e p a0 $A0
III Read Address 1 1 0 0 h e 0 0 $C0
III Read Track 1 1 1 0 h e 0 0 $E0
III Write Track 1 1 1 1 h e p 0 $F0
IV Force Interrupt 1 1 0 1 i3 i2 i1 i0 $D0
Base is the opcode with all flags clear, not the only byte the
command can take. Read Track can appear as $E0, $E4, $E8 or
$EC depending on h and e. Match on the top nibble.
Flags:
u bit 4 update Track Register
m bit 4 multiple sector
h bit 3 0 = enable spin-up sequence, 1 = disable
v bit 2 verify destination track
e bit 2 add settling delay
p bit 1 0 = ENABLE write precompensation, 1 = disable
a0 bit 0 0 = normal data mark, 1 = deleted data mark
Step rates (r1,r0), WD1772 values at 8MHz:
00 = 6ms 01 = 12ms 10 = 2ms 11 = 3ms
These are WD1772 specific. The WD1770 uses 6, 12, 20 and 30ms
for the same encodings. On the Atari 3ms is the normal setting
and h must always be 0, since MO is wired straight to the drive.
Sources: Western Digital's own WD1772 data sheet, cross-checked
against EmuTOS bios/fdc.h. Read Track being 1110 confirmed by
both, correcting a misprint in DrCoolZic's edition.
$FFFF8606.W|R-|WDC_DMA|DMA Status %________ _____DSE | | | | Data request condition--------------------+|| | | | | Sector count 0:zero,1:not zero-------------+| | | | | DMA 0:error,1:no error----------------------+ | |-W| |DMA Mode %_______W FD_SHAA_ | | | | 0:read,1:write---------------------+ || |||| | | | | 0:HDC,1:FDC access-------------------+| |||| | | | | DMA 0:on,1:off (ignored STF/STE)----+ |||| | | | | 0:FDC/HDC register 1:sector count-------+||| | | | | 0:FDC,1:HDC access-----------------------+|| | | | | A1/A0 pin 0:low,1:high--------------------++ |
$FFFF8606 WDC_DMA DMA Status (read) / DMA Mode (write)
READ - status, bits 2-0:
Bit 2 state of the FDC DRQ signal
Bit 1 sector count: 0 = reached zero, 1 = not zero
Bit 0 DMA error: 1 = NO error, 0 = error
Bit 0 is inverted from the obvious reading. It was documented
the wrong way round here and in the widely mirrored Dan Hollis
listing for many years. Atari's own TOS tests it as:
move.w #$0090,(a6) examine DMA status register
move.w (a6),d0
btst #0,d0 bit zero indicates DMA ERROR
beq.s floprd6 (when its zero -- retry)
EmuTOS agrees: DMA_OK is 0x0001, commented 1=ok, 0=error.
Confirmed by ijor, July 2026.
WRITE - mode control:
Bit 8 direction, 1 = memory to controller (write)
Toggling this bit RESETS the DMA, flushing both
FIFOs and clearing the sector count register.
Bit 7 1 = acknowledge FDC DRQ, 0 = acknowledge HDC DRQ
Bit 6 nominally DMA enable, but IGNORED on STF and STE
Bit 4 0 = external controller registers, 1 = sector count
Bit 3 0 = assert FDCS*, 1 = assert HDCS*
Bit 2 CA2 output, drives FDC A1
Bit 1 CA1 output, drives FDC A0
Bit 0 unused, set 0
The DMA FIFOs are 16 bytes and are never flushed automatically,
so data only reaches memory in multiples of 16 bytes. A single
Read Address moves 6 bytes and therefore reaches memory not at
all; issue it repeatedly to push data through.
Do not poll this status register during a transfer, it can
disrupt it. Poll MFP GPIP bit 5 at $FFFFFA01 instead: mask with
$20, result zero means the FDC has interrupted.
$FFFF8609.B|RW|WDC_BSH|DMA Base and Counter Hi |
$FFFF860B.B|RW|WDC_BSM|DMA Base and Counter Mi |
$FFFF860D.B|RW|WDC_BSL|DMA Base and Counter Lo |
$FFFF860E.W|RW|WDC_FDN|Frequency and Density control %______DF |ME,TT,F | | |Density 0:DD,1:HD---------------------------+| | | | |FDC clock 0:8MHz,1:16MHz---------------------+ |
$FFFF860E WDC_FDN Frequency and Density control
Word register. Mega STE, TT and Falcon only.
Bit 1 Density 0 = DD (double), 1 = HD (high)
Bit 0 FDC clock 0 = 8MHz, 1 = 16MHz
TOS writes $0000 for DD media and $0003 for HD media, so both
bits change together in practice. The bit assignment above is
per Hatari src/fdc.c and matches FALREG.TXT (bit 0 prescaler,
bit 1 mode select). CORRECTION: this page previously gave the
two bits the other way round.
Atari TOS (tos3x bios/floppy.S, denselect) accesses it as a
word; the active bits sit in the low byte, see $FFFF860F.
EmuTOS probes this register at boot before using it: on some
machines without HD floppy support (IMP chipset STs, per
Christian Zietz) reading it causes a bus error.
Was previously labelled WDC_BSL here, duplicating the label on
$FFFF860D.
$FFFF860F.B|RW|WDC_MCT|Floppy Mode Control (860E lo) %CMDPBmdf |F | | | disk Changed flag--------------------+||||||| |F | | | Media detect, drive 2-----------------+|||||| |F | | | Density select, drive 2----------------+||||| |F | | | Prescaler (fdc clock), drive 2----------+|||| |F | | | Busy, must be 0 before write-------------+||| |F | | | media detect, drive 1---------------------+|| |F | | | density select, drive 1--------------------+| |F | | | fdc clock prescaler, drive 1----------------+ |F
$FFFF860F WDC_MCT Floppy Mode Control (low byte of $FFFF860E)
Bit 7 Disk Changed flag
Bit 6 Media detect, drive 2
Bit 5 Mode select (density), drive 2
Bit 4 Prescaler (FDC clock), drive 2
Bit 3 Busy: tested during DMA access
Bit 2 Media detect, drive 1
Bit 1 Mode select (density), drive 1
Bit 0 Prescaler (FDC clock), drive 1
Bit layout from FALREG.TXT (Aura). Two of the bits are
independently confirmed in operating system source:
Bit 3: Falcon TOS 4, and EmuTOS bios/floppy.c following it,
loop waiting for this bit to become zero before issuing a
write sector or write track command (EmuTOS bios/dma.h,
DMA_MCBIT3 "must be zero before starting write").
Bit 7: TOS 3.06 Sparrow (Falcon prototype) code reads this bit
as the disk-change signal (tos3x bios/sparrow/rwabs.c,
check_disk_change).
===========#==#=======#===============================================#=====
----------------------|DMA SCSI |-----
===========#==#=======#===============================================#=====
$FFFF8701.B|RW|SCS_DA0|DMA Address Pointer (Highest byte) |TT
$FFFF8703.B|RW|SCS_DA1|DMA Address Pointer (High byte) |TT
$FFFF8705.B|RW|SCS_DA2|DMA Address Pointer (Low byte) |TT
$FFFF8707.B|RW|SCS_DA3|DMA Address Pointer (Lowest byte) |TT
$FFFF8709.B|RW|SCS_BC0|DMA Byte Counter (Highest byte) |TT
$FFFF870B.B|RW|SCS_BC1|DMA Byte Counter (High byte) |TT
$FFFF870D.B|RW|SCS_BC2|DMA Byte Counter (Low byte) |TT
$FFFF870F.B|RW|SCS_BC3|DMA Byte Counter (Lowest byte) |TT
$FFFF8710.W|Rw|SCC_RD0|Rest data (High Word) |TT
$FFFF8712.W|Rw|SCC_RD1|Rest data (Low Word) |TT
$FFFF8714.W|Rw|SCS_CTL|DMA SCSI Control Register %________ BZ____DW |TT
| | | Bus Error 0:no,1:yes-----------------+| || |TT
| | | Byte Counter Zero 0:no,1:yes----------+ || |TT
| | | DMA 0:off,1:on-----------------------------+| |TT
| | | DMA 0:read,1:write--------------------------+ |TT
===========#==#=======#===============================================#=====
----------------------|5380 SCSI Drive Controller |-----
===========#==#=======#===============================================#=====
$FFFF8781.B|RW|SCS_CDB|Contents of SCSI-Data Buses |TT
NCR 5380 SCSI CONTROLLER - REGISTER MAP
IMPORTANT: the register names on this page were wrong for five
of the eight registers and have been corrected. The old names
are noted below so anyone who finds them elsewhere knows why
they differ. The 5380 is a standard part with a well known
register map; several registers do different things on read
and on write, which is the likely source of the confusion.
Addr Reg Read Write
$FFFF8781 0 Current SCSI Data Output Data
$FFFF8783 1 Initiator Command Initiator Command
$FFFF8785 2 Mode Register Mode Register
$FFFF8787 3 Target Command Target Command
$FFFF8789 4 Current SCSI Bus Status Select Enable
$FFFF878B 5 Bus and Status Start DMA Send
$FFFF878D 6 Input Data Start DMA Target Receive
$FFFF878F 7 Reset Parity/Interrupt Start DMA Initiator Recv
Old names on this page, now corrected:
$8785 was 'Transfer Start Register' -> Mode Register
$8789 was 'Bus Status Register' -> ID Select / Bus Status
$878B was 'Device Status Register' -> DMA Send / Bus+Status
$878D was 'SCSI-Bus Command Data' -> DMA Target Rx / Input
$878F was 'Reset Interrupts, Parity
error, Start DMA-Action' -> DMA Init Rx / Reset
Address to register number mapping, per Hatari src/ncr5380.c:
register = (address / 2) & 7
so $FFFF8781 is register 0, $FFFF8783 register 1, and so on.
Only odd addresses respond; even addresses are ignored.
CONFLICT NOTE: the Atari Compendium lists this block starting at
$00FF8780 with OB (odd byte) markers, which resolves to the same
odd addresses shown here. Its names match the standard 5380 map
and therefore agree with Hatari. This wiki's earlier names
matched neither. If you find a listing using the old names, it
most likely descends from the same source as this page did.
Verified against Hatari src/ncr5380.c and the Atari Compendium.
PER-REGISTER BIT DETAIL (standard NCR 5380 datasheet layout,
with EmuTOS bios/scsi.c usage cited where it confirms a bit):
Reg 0 $FFFF8781 Current SCSI Data (R) / Output Data (W)
Read gives the live state of the 8 SCSI data lines; write
loads the byte the chip drives onto the bus (gated by ICR
bit 0). No bit assignments, it is the data byte itself.
Reg 1 $FFFF8783 Initiator Command Register (ICR)
Bit 7 R/W assert RST (EmuTOS writes $80 to reset the
bus, "assert RST")
Bit 6 W test mode
R AIP, arbitration in progress (EmuTOS waits for
$40, "wait for AIP bit")
Bit 5 W differential enable (unused on Atari, single
ended bus)
R LA, lost arbitration
Bit 4 R/W assert ACK
Bit 3 R/W assert BSY
Bit 2 R/W assert SEL
Bit 1 R/W assert ATN
Bit 0 R/W assert data bus - gates the Output Data register
onto the SCSI data lines (EmuTOS: "gate it on to
bus")
Reg 2 $FFFF8785 Mode Register
Bit 7 block mode DMA
Bit 6 target mode (0 = initiator, which is the normal Atari
role)
Bit 5 enable parity checking
Bit 4 enable parity interrupt
Bit 3 enable EOP (end of process) interrupt
Bit 2 monitor busy - interrupt and clear outputs if BSY
drops
Bit 1 DMA mode (EmuTOS sets $02 before every DMA transfer,
"set DMA mode, we are initiator")
Bit 0 arbitrate - start bus arbitration using the ID in the
Output Data register (EmuTOS: "set ARBITRATE bit")
Reg 3 $FFFF8787 Target Command Register (TCR)
Bits 7-4 unused
Bit 3 assert REQ (target role only)
Bit 2 assert MSG \
Bit 1 assert C/D > the SCSI phase lines; as initiator,
Bit 0 assert I/O / writing the expected phase here is what
makes the phase-match bit in register 5 work. EmuTOS
writes the phase value straight into this register.
Reg 4 $FFFF8789 Current SCSI Bus Status (R) / Select
Enable (W)
Read - live state of the control lines:
Bit 7 RST Bit 6 BSY Bit 5 REQ Bit 4 MSG
Bit 3 C/D Bit 2 I/O Bit 1 SEL Bit 0 DBP (data bus
parity)
Order confirmed by EmuTOS, which reads the phase as
(reg>>2)&7 and tests $20 for REQ and $40 for BSY.
Write - Select Enable: bit mask of SCSI IDs that trigger a
reselection interrupt; 0 disables reselection.
Reg 5 $FFFF878B Bus and Status (R) / Start DMA Send (W)
Read:
Bit 7 end of DMA (EOP received)
Bit 6 DMA request
Bit 5 parity error
Bit 4 interrupt request active
Bit 3 phase match - TCR phase equals the live bus phase
(EmuTOS polls $08 during transfers, "while phases
match")
Bit 2 busy error - BSY dropped while monitor busy was set
Bit 1 ATN
Bit 0 ACK
Write: any value starts a DMA send (memory to SCSI).
Reg 6 $FFFF878D Input Data (R) / Start DMA Target
Receive (W)
Read gives the latched input byte during DMA or arbitration;
write (any value) starts a target-role DMA receive.
Reg 7 $FFFF878F Reset Parity/Interrupt (R) / Start DMA
Initiator Receive (W)
Reading clears the parity error, interrupt request and busy
error flags; writing (any value) starts an initiator-role
DMA receive (SCSI to memory).
FALCON NOTE: the Falcon also has a 5380, but not memory mapped
here. It is reached indirectly through the Falcon SCSI DMA
controller using register-select values $88-$8F written to the
DMA control register, one per 5380 register (EmuTOS
bios/scsi.c, fscsi_icr = $89 and so on). These TT addresses
bus error on a Falcon.
Bit tables from the NCR 5380 datasheet; individual bits
confirmed against EmuTOS bios/scsi.c as noted.
$FFFF8783.B|RW|SCS_ICR|Init Command Register |TT
$FFFF8785.B|RW|SCS_MR |Mode Register |TT
$FFFF8787.B|RW|SCS_TCR|Target Command Register |TT
$FFFF8789.B|RW|SCS_SEL|ID Select / Current SCSI Bus Status |TT
$FFFF8789 Register 4
READ - Current SCSI Bus Status:
Bit 7 RST
Bit 6 BSY
Bit 5 REQ
Bit 4 MSG
Bit 3 C/D (command/data)
Bit 2 I/O (input/output)
Bit 1 SEL
Bit 0 DBP (data bus parity)
Bits 4-2 together give the current bus phase:
000 Data Out 011 Status
001 Data In 110 Message Out
010 Command 111 Message In
WRITE - Select Enable Register: sets which SCSI ID the
controller responds to when selected as a target.
Verified against Hatari src/ncr5380.c, which builds this value
from the live bus signals and shifts the bus phase into bits
4-2 exactly as above.
$FFFF878B.B|RW|SCS_DMA|Start DMA Send / Bus and Status Register |TT
$FFFF878B Register 5
READ - Bus and Status Register:
Bit 7 End of DMA
Bit 6 DMA Request
Bit 5 Parity Error
Bit 4 Interrupt Request active
Bit 3 Phase Match (bus phase equals Target Command bits)
Bit 2 Busy Error
Bit 1 ATN
Bit 0 ACK
WRITE - Start DMA Send: writing here begins a DMA transfer
from memory to the SCSI bus.
Verified against Hatari src/ncr5380.c.
$FFFF878D.B|RW|SCS_TRG|Start DMA Target Rx / Input Data Register |TT
$FFFF878F.B|RW|SCS_INI|Start DMA Initiator Rx / Reset Parity-Interrupt|TT