The complete memory map assembled from the six section pages into one
continuous listing. Nothing here is maintained directly: each section is
pulled in from its own page, so edits made there appear here automatically.
Use the section pages to edit; use this page to read or search the whole map
in one place.
===========#==#=======#===============================================#=====
----------------------|Exception Vectors |-----
===========#==#=======#===============================================#=====
$00000000.L|R-|XPT_SPR|SSP after Reset |
$00000000 and $00000004 Reset vectors
$00000000 longword, supervisor stack pointer after reset
$00000004 longword, program counter after reset
These two are read from ROM, not RAM. At reset the memory
controller maps ROM to address zero so the 68000 fetches the
initial stack pointer and start address from there, then TOS
switches the mapping so RAM appears at zero.
Add-on ROM boards commonly claim $00000000 to $00000007 for
exactly this reason: to supply their own reset vectors and
take control before TOS.
From the Atari Compendium and Atari TOS bios/startup.S.
$00000004.L|R-|XPT_PCR|PC after Reset |
$00000008.L|RW|XPT_BUS|Bus Error |
$0000000C.L|RW|XPT_ADR|Address Error |
$00000010.L|RW|XPT_ILL|Illegal Instruction |
$00000014.L|RW|XPT_DBZ|Divide by Zero |
$00000018.L|RW|XPT_CHK|Chk, Chk2 Instruction |
$0000001C.L|RW|XPT_TRV|Trapv Instruction |
$00000020.L|RW|XPT_PRV|Privilege Violation |
$00000024.L|RW|XPT_TRC|Trace |
$00000028.L|RW|XPT_LNA|Line-A |
$00000028 and $0000002C Line-A and Line-F vectors
$00000028 longword, Line-A exception
$0000002C longword, Line-F exception
The 68000 traps any instruction whose top four bits are $A or
$F, since neither is a valid opcode.
Atari used Line-A for a set of fast low level graphics
routines, documented on the wiki as Line-A. Those were removed
on later machines, so anything using them must check the
machine type first.
Line-F is used by the 68881 and 68882 floating point
coprocessors. On a machine without an FPU it is free.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$0000002C.L|RW|XPT_LNF|Line-F |
$00000030.L|RW| - |reserved |
$00000034.L|RW|XPT_FPU|Coprocessor Protocol Violation |030+
$00000038.L|RW|XPT_FRM|Format Error |010+
$0000003C.L|RW|XPT_DSP|DSP Transfer Interrupt |*
$00000040.L|RW| - |reserved |
$00000044.L|RW| - |reserved |
$00000048.L|RW| - |reserved |
$0000004C.L|RW| - |reserved |
$00000050.L|RW| - |reserved |
$00000054.L|RW| - |reserved |
$00000058.L|RW| - |reserved |
$0000005C.L|RW| - |reserved |
$00000060.L|RW|XPT_SPU|Spurious Interrupt |
$00000064.L|RW|XPT_LV1|Level 1 - |
$00000068.L|RW|XPT_HBL|Level 2 - HBL |
$00000068 to $0000007C Auto-vector interrupts
$68 Level 2 HBL, horizontal blank
$70 Level 4 VBL, vertical blank
$78 Level 6 MFP 68901
Levels 1, 3, 5 and 7 exist in the table but are unused on a
standard ST. Level 5 is the SCC on machines that have one.
The VBL at level 4 is the one most software hooks, though the
supported route is the VBL queue at $00000456 rather than
taking the vector directly.
The MFP at level 6 is the busiest: every timer, the keyboard,
the floppy controller and the serial port all arrive through
it, dispatched by the MFP's own vector table at $00000100.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$0000006C.L|RW|XPT_LV3|Level 3 - |
$00000070.L|RW|XPT_VBL|Level 4 - VBL |
$00000074.L|RW|XPT_LV5|Level 5 - SCC |
$00000078.L|RW|XPT_LV6|Level 6 - MFP |
$0000007C.L|RW|XPT_LV7|Level 7 - |
$00000080.L|RW|XPT_T00|Trap #00 |
$00000084.L|RW|XPT_T01|Trap #01 - GEMDOS |
$00000080 to $000000BC Trap vectors
Trap #1 ($84) GEMDOS
Trap #2 ($88) AES and VDI
Trap #13 ($B4) BIOS
Trap #14 ($B8) XBIOS
The remaining twelve traps are unused by TOS and free for
application use, though some resident utilities claim them.
Function number is passed on the stack, not in a register.
Full call documentation is in tos.hyp rather than this map.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000088.L|RW|XPT_T02|Trap #02 - AES/VDI |
$0000008C.L|RW|XPT_T03|Trap #03 |
$00000090.L|RW|XPT_T04|Trap #04 |
$00000094.L|RW|XPT_T05|Trap #05 |
$00000098.L|RW|XPT_T06|Trap #06 |
$0000009C.L|RW|XPT_T07|Trap #07 |
$000000A0.L|RW|XPT_T08|Trap #08 |
$000000A4.L|RW|XPT_T09|Trap #09 |
$000000A8.L|RW|XPT_T10|Trap #10 |
$000000AC.L|RW|XPT_T11|Trap #11 |
$000000B0.L|RW|XPT_T12|Trap #12 |
$000000B4.L|RW|XPT_T13|Trap #13 - BIOS |
$000000B8.L|RW|XPT_T14|Trap #14 - XBIOS |
$000000BC.L|RW|XPT_T15|Trap #15 |
$000000C0.L|RW|FPU_BOS|FFCP Branch or Set |020+
$000000C4.L|RW|FPU_INX|FFCP Inexact Result |020+
$000000C8.L|RW|FPU_DBZ|FFCP Divide by Zero |020+
$000000CC.L|RW|FPU_UNR|FFCP Underflow |020+
$000000D0.L|RW|FPU_OPE|FFCP Operand Error |020+
$000000D4.L|RW|FPU_OVR|FFCP Overflow |020+
$000000D8.L|RW|FPU_NAN|FFCP signaling NAN |020+
$000000DC.L|RW| - |reserved |
$000000E0.L|RW|XPT_MMU|MMU Configuration Error |030+
$000000E4.L|RW| - |reserved |
$000000E8.L|RW| - |reserved |
$000000EC.L|RW| - |reserved |
$000000F0.L|RW| - |reserved |
$000000F4.L|RW| - |reserved |
$000000F8.L|RW| - |reserved |
$000000FC.L|RW| - |reserved |
$00000100.L|RW|XPT_CTR|B0 Centronics busy |*
$00000100 to $0000013C MFP interrupt vectors
The MFP 68901 supplies its own vector number, so its sixteen
interrupt sources each get a dedicated entry here rather than
sharing the level 6 auto-vector.
Which source maps to which vector is set by the MFP vector
register at $FFFFFA17, which holds the upper nibble of the
vector base. On the Atari that is $40, putting the sixteen
vectors at $40 to $4F, which are these addresses.
Priority runs from the top of the list down: B7 (FDC/HDC) is
higher priority than B0 (Centronics busy), and register A
sources outrank register B.
Enable, mask, pending and in-service for each source are
controlled by the MFP registers at $FFFFFA07 to $FFFFFA15.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000104.L|RW|XPT_DCD|B1 RS232 DCD |*
$00000108.L|RW|XPT_CTS|B2 RS232 CTS |*
$0000010C.L|RW|XPT_BLT|B3 Blitter Done |*BLT
$00000110.L|RW|XPT_T_D|B4 Timer D |*
$00000114.L|RW|XPT_T_C|B5 Timer C |*
$00000118.L|RW|XPT_KBD|B6 IKBD/MIDI |*
$0000011C.L|RW|XPT_FDC|B7 FDC/HDC |*
$00000120.L|RW|XPT_T_B|A0 Timer B |*
$00000124.L|RW|XPT_XMT|A1 Transmit Error |*
$00000128.L|RW|XPT_EMP|A2 Transmit Buffer empty |*
$0000012C.L|RW|XPT_REC|A3 Receive Error |*
$00000130.L|RW|XPT_FUL|A4 Receive Buffer full |*
$00000134.L|RW|XPT_T_A|A5 Timer A |*
$00000138.L|RW|XPT_RNG|A6 RS232 Ring Indicator |*
$0000013C.L|RW|XPT_SND|A7 Monochrome Detect/Audio Subsystem |*
$00000140.L|RW| - |User defined Vectors |
...........|RW| - |.................... |
$000003FC.L|RW| - |User defined Vectors |
| | | CHECK: $140-$17C are the TT MFP vectors and |
| | | $180-$1BC the SCC vectors on machines that |
| | | have them; $380-$3CC is the TOS exception |
| | | crash save area. Not all of this range is |
| | | genuinely user defined. |
===========#==#=======#===============================================#=====
----------------------|Random Access Memory |-----
===========#==#=======#===============================================#=====
$00000008.B|RW|RAM_TOP|RAM TOP |
...........|RW| - |....... |
$00CFFFFF.B|RW|RAM_END|RAM END |
===========#==#=======#===============================================#=====
----------------------|Alternative RAM (add-on boards) |-----
===========#==#=======#===============================================#=====
$00400000.B|RW|ALT_TOP|Alt-RAM TOP, add-on boards |*
$00400000 onward Alternative RAM
Alt-RAM on the ST range conventionally starts at the 4MB mark,
$00400000, immediately above the 4MB of ST RAM a stock machine
can address.
Common fits:
$00400000 to $007FFFFF 4MB
$00400000 to $00BFFFFF 8MB, the usual size
$00400000 to $00DFFFFF 10MB
Boards exist that go higher.
The address range above the alt-RAM area is nominally VME bus
space on machines that have it, the TT and Mega STE. That
allocation should stand. In practice, on machines with no VME
bus fitted or with VME unused, alt-RAM boards sometimes extend
into that range as well.
THE VME OVERLAP, now confirmed. On a Mega STE the VME bus
address space begins at $00A00000, so it does NOT sit above
the alt-RAM area, it sits INSIDE it:
4MB fit $00400000-$007FFFFF no overlap
8MB fit $00400000-$00BFFFFF overlaps VME from $00A00000
10MB fit $00400000-$00DFFFFF covers the whole VME range
So an 8MB or larger alt-RAM board on a Mega STE is using
address space the VME bus would otherwise claim. That is
workable when no VME card is fitted, which is the usual case,
but the two cannot coexist at those addresses.
The TT is unaffected: its VME space is at $FE000000, well
clear of alt-RAM.
Ranges confirmed by Hatari src/scu_vme.c, which cites the
Atari TT030 Hardware Reference Manual (June 1990) and the
Atari Profibuch ST-STE-TT chapter 9 (1991), and independently
by the Atari Compendium.
This is separate from TT RAM on the TT and Falcon, which lives
at $01000000 and is reported through the ramtop system
variable at $000005A4, validated by ramvalid at $000005A8.
Known to be used by: FLASHY CLOCK, SEC BOOSTER.
STE CHIP DECODE IN THE SAME REGION: Atari's own STE ASIC
schematics decode part of this range for cartridge ROM, not
RAM. The /ROM5 and /ROM6 selects cover $00D00000-$00D7FFFF,
and setting the undocumented GAMECART bit at $FFFF9000 moves
the cartridge port selects to $00D80000-$00DFFFFF. This
applies to the GST MCU machines only, meaning the STE, Mega
STE and TT; the ST and STF use separate GLUE and MMU chips
with no such decodes. Neither select is connected on a
production machine and no TOS ever sets the bit, so in
practice the range is free. It is worth knowing about before
designing a board that claims these addresses. See $FFFF9000
on the MFP, RTC and anything else page.
...........|RW| - |............ |*
$00DFFFFF.B|RW|ALT_END|Alt-RAM END, largest fit seen |*
===========#==#=======#===============================================#=====
----------------------|VME Bus Address Space |-----
===========#==#=======#===============================================#=====
$00A00000.B|RW|VME_A24|VMEbus A24:D16 addressable area TOP |ME
$00A00000 onward VME Bus Address Space
The VME bus is fitted to the Mega STE and TT only. Two address
windows are provided on each, one for 24 bit VME addressing
and a smaller one for 16 bit.
Mega STE:
$00A00000 to $00DEFFFF VMEbus A24:D16
$00DF0000 to $00DFFFFF VMEbus A16:D16
TT:
$FE000000 to $FEFEFFFF VMEbus A24:D16
$FEFF0000 to $FEFFFFFF VMEbus A16:D16, shadow image
All transfers are word based, D16. The Mega STE window is the
more limited of the two.
IMPORTANT: on the Mega STE this space overlaps the range used
by alt-RAM boards of 8MB and larger. See the alt-RAM entry
above.
The VME control registers are separate and live in the I/O
area at $FFFF8E01 to $FFFF8E0F, documented on the MFP, RTC and
anything else page. Those cover the interrupt mask, interrupt
state and the two forced interrupt registers, alongside the
SCU general purpose registers.
From the TT hardware reference, VME SYSFAIL generates a
motherboard IRQ7 to the processor but does not generate an
IRQ7 back onto the VME bus. SCU generated IRQ1 and IRQ3 are
always auto vectored; only interrupts 5 and 6 have external
IACK pins and can produce vectored interrupts.
Ranges and interrupt behaviour from Hatari src/scu_vme.c,
citing the Atari TT030 Hardware Reference Manual (June 1990)
and the Atari Profibuch ST-STE-TT chapter 9 (1991).
Cross-checked against the Atari Compendium.
...........|RW| - |............................... |ME
$00DEFFFF.B|RW| - |VMEbus A24:D16 addressable area END |ME
$00DF0000.B|RW|VME_A16|VMEbus A16:D16 addressable area TOP |ME
...........|RW| - |............................... |ME
$00DFFFFF.B|RW| - |VMEbus A16:D16 addressable area END |ME
$FE000000.B|RW|VME_T24|VMEbus A24:D16 addressable area TOP |TT
...........|RW| - |............................... |TT
$FEFEFFFF.B|RW| - |VMEbus A24:D16 addressable area END |TT
$FEFF0000.B|RW|VME_T16|VMEbus A16:D16 area TOP, shadow image |TT
...........|RW| - |............................... |TT
$FEFFFFFF.B|RW| - |VMEbus A16:D16 area END |TT
===========#==#=======#===============================================#=====
----------------------|TT RAM (Fast RAM) |-----
===========#==#=======#===============================================#=====
$01000000.B|RW|TTR_TOP|TT Fast RAM TOP |TT,F
$01000000 onward TT RAM (Fast RAM)
Present on the TT and Falcon. Physically separate from ST RAM
and connected directly to the processor rather than through
the video and DMA hardware.
$01000000 to $01FFFFFF TT Fast RAM, up to 16MB
$02000000 to $FDFFFFFF Reserved
IMPORTANT: TT RAM is UNSUITABLE for direct DMA and Shifter
transfers. It cannot be used for screen memory, and it cannot
be the target or source of a DMA transfer. Anything going to
or from disk, or being displayed, has to live in ST RAM. This
is the single most important thing to know about it.
What it is good for is code and data, where the lack of
contention with the video hardware makes it appreciably faster
than ST RAM.
The top of installed TT RAM is reported in the system variable
ramtop at $000005A4, validated by ramvalid at $000005A8
holding $1357BD13. A zero ramtop means no TT RAM is fitted.
GEMDOS tracks it through a second memory descriptor chained
from themd at $0000048E, which is why Malloc can hand out
either kind. Mxalloc lets a program ask for one specifically.
CONFLICT on the extent, now leaning one way: the Atari
Compendium gives the range as $01000000 to $01FFFFFF, 16MB.
"The Atari A to Z" (Mark S Baines, 1998) independently gives
the same, listing $01000000 as the start of TT fast RAM and
$01FFFFFF as the end of TT RAM space. The older Hardware
Register Listing page is the only source giving $01000000 to
$013FFFFF, 4MB. Two independent sources against one puts 16MB
as the architectural extent, with 4MB most likely a common
fitted size that got written down as the limit. Not confirmed
on hardware, so still flagged.
Note this is entirely separate from alt-RAM on add-on boards
for the ST and STE, which lives at $00400000. See the
alternative RAM entry above.
Range and the DMA restriction from the Atari Compendium.
...........|RW| - |............... |TT,F
$01FFFFFF.B|RW|TTR_END|TT Fast RAM END |TT,F
$02000000.B|--| - |Reserved |
...........|--| - |........ |
$FDFFFFFF.B|--| - |Reserved |
| | | CHECK: was annotated "14MB". $00CFFFFF is |
| | | 13MB. For 14MB, RAM should end at $00DFFFFF. |
| | | Either the address or the size is wrong. |
===========#==#=======#===============================================#=====
----------------------|1MB System Read Only Memory |-----
===========#==#=======#===============================================#=====
$00E00000.B|R-|ROM_TOP|1MB ROM TOP |F
$00E00000 onward System ROM
$00E00000 to $00EFFFFF 1MB ROM area, Falcon and TT
$00FC0000 to $00FEFFFF 192KB ROM area, ST and STE
The ST and STE map their 192KB ROM at $00FC0000. The TT and
Falcon use a larger 1MB region starting at $00E00000.
ADD-ON BOARDS: ROM expansion boards commonly claim
$00E00000 to $00E3FFFF for their own 256KB ROM, and some add a
flash area at $00E40000 to $00E7FFFF. Known to be used by
FLASHY CLOCK, TRUDIE and SEC BOOSTER. Such boards also take
the reset vectors at $00000000 to $00000007 and the 192KB
region at $00FC0000 to $00FEFFFF.
RESOLVED: the wiki listing used to give the 1MB ROM area as
ending at $00F0003F, 1MB plus 64 bytes. The extra 64 bytes
are the Falcon IDE registers at $00F00000 to $00F0003F,
documented on the IDE page; the ROM decode itself ends at
$00EFFFFF.
Within the 1MB area the Falcon fits 512KB of TOS ROM at
$00E00000 to $00E7FFFF. The F030/CT60 listing documents
$00E80000 to $00EFFFFF as an image (mirror) of those 512KB
ROMs. CHECK: Hatari instead treats $00E80000 to $00EFFFFF as
empty space, so the mirror claim is unconfirmed by the
emulator sources; it may be true on real hardware only.
STE CHIP DECODE, a separate matter from the Falcon mirror
above: Atari's own STE ASIC schematics show the GST MCU
decoding $00E80000-$00EBFFFF as chip select /ROM0 and
$00E40000-$00E7FFFF as /ROM1. Both pins are left unconnected
on production machines, which is why the STE ships with 256KB
of TOS on /ROM2 rather than the 768KB the decoding would
allow. This is a GST MCU feature, so it applies to the STE,
Mega STE and TT and NOT to the ST or STF, which use separate
GLUE and MMU chips. Worth noting because the add-on flash
area below sits exactly on the /ROM1 range: on a GST MCU
machine, a board putting flash at $00E40000 is using an
address the chip already decodes for ROM. Source is Keli
Hlodversson's reading of the Christian Zietz schematic
recovery, https://www.keli.dk/old-asic/ - schematic evidence
only, not confirmed against Hatari, EmuTOS, TOS or the
Compendium, none of which mention these selects.
Extended bit information is not currently available for the
add-on ROM and flash areas.
...........|R-| - |........... |F
$00E7FFFF.B|R-| - |512KB TOS ROM END (fitted ROM) |F
$00E80000.B|R-| - |Image of the 512KB TOS ROMs (see CHECK above) |F
...........|R-| - |........... |F
$00EFFFFF.B|R-|ROM_END|1MB ROM area END |F
$00F0003F.B|R-| - |IDE registers end - see the IDE page |F
$00E40000.B|R-|FLA_TOP|Flash space TOP, add-on boards |*
$00E40000 to $00E7FFFF Flash space, add-on boards
Not present on stock Atari hardware. 256KB of flash memory
fitted by some ROM expansion boards, sitting immediately above
the add-on ROM area at $00E00000 to $00E3FFFF.
Known to be used by: FLASHY CLOCK.
Extended bit information is not currently available.
...........|R-| - |............... |*
$00E7FFFF.B|R-|FLA_END|Flash space END |*
| | | The 1MB ROM extent question is resolved in |
| | | the $00E00000 entry above: ROM decode ends at |
| | | $00EFFFFF, and $00F00000-$00F0003F is IDE. |
===========#==#=======#===============================================#=====
$00F00040.B|--| - |Illegal Address Space (ST/STE/TT) |
| | | Falcon: Bus Expansion Space from $00F10000 |F
$00F00040 to $00F9FFFF
On the ST, STE and TT this range is illegal address space.
On the Falcon, $00F10000 to $00F9FFFF is the Falcon030 BUS
Expansion Space: 576KB of A24/D16 expansion decode, per the
F030/CT60 listing. This is where Falcon expansion hardware is
intended to live.
SUPERSEDED CLAIMS: the old Dan Hollis derived listing gave
five Mega STE "speed control" addresses in this range:
$00F10000 switch to 8MHz, $00F20000 high speed ROM on,
$00F30000 high speed ROM off, $00F40000 unknown, $00F50000
cache off at 16MHz. None of these appear in Hatari, EmuTOS or
any TOS source; Mega STE speed and cache control is the byte
at $FFFF8E21, documented on the MFP page. Treat the $00F1xxxx
speed claims as wrong.
CONFLICT: the CT60 accelerator claims $FFF00000 to $FFFBFFFF
in the IO mirror of this region for its own registers. See
the note in the IDE block.
...........|--| - |..................... |
$00F9FFFF.B|--| - |Illegal Address Space / Bus Expansion END |
===========#==#=======#===============================================#=====
----------------------|128KB Expansion Cartridge Port |-----
===========#==#=======#===============================================#=====
$00FA0000.L|R-|ROM_PRT|Cartridge Magic($FA52235F=Diag,$ABCDEF42=User*)|
$00FA0000 to $00FBFFFF Cartridge port, 128KB
$00FA0000 longword, cartridge magic
$00FA0004 longword, entry point for a diagnostic cartridge
Two magic values are recognised, and Atari's own TOS source
tests both:
$FA52235F diagnostic cartridge. Tested very early in
bios/startup.S, before RAM is even sized, and
jumped to immediately if found.
$ABCDEF42 normal application cartridge. Tested later, once
the system is up.
A diagnostic cartridge therefore takes control before almost
anything else, which is what makes it useful for hardware
fault finding on a machine that will not boot.
Magic values confirmed in Atari TOS bios/startup.S, which
compares against both.
$00FA0004.L|R-|ROM_CAL|Call Diagnostic Cartridge |
...........|R-| - |............ |
$00FBFFFF.B|R-| - |ROM Port END |
===========#==#=======#===============================================#=====
----------------------|192KB System Read Only Memory |-----
===========#==#=======#===============================================#=====
$00FC0000.B|--| - |192KB ROM TOP |ST
...........|--| - |............. |ST
$00FEFFFF.B|--| - |192KB ROM END |ST
===========#==#=======#===============================================#=====
----------------------|TT MFP Vectors |-----
===========#==#=======#===============================================#=====
$00000140.L|RW|TTMFP00|GPI 0 |TT
$00000144.L|RW|TTMFP01|GPI 1 |TT
$00000148.L|RW|TTMFP02|SCC-DMA Controller |TT
$0000014C.L|RW|TTMFP03|Ring Indicator SCC B |TT
$00000150.L|RW|TTMFP04|Timer D (RS232 baud rate generator) |TT
$00000154.L|RW|TTMFP05|Timer C (SCC TRxCB) |TT
$00000158.L|RW|TTMFP06|Reserved, GPI 4 |TT
$0000015C.L|RW|TTMFP07|SCSI DMA Controller |TT
$00000160.L|RW|TTMFP08|Timer B |TT
$00000164.L|RW|TTMFP09|Send Error |TT
$00000168.L|RW|TTMFP10|Send buffer empty |TT
$0000016C.L|RW|TTMFP11|Receive error |TT
$00000170.L|RW|TTMFP12|Receive buffer full |TT
$00000174.L|RW|TTMFP13|Timer A |TT
$00000178.L|RW|TTMFP14|TT Clock (MC146818A) |TT
$0000017C.L|RW|TTMFP15|TT-SCSI Drive Controller NCR 5380 |TT
===========#==#=======#===============================================#=====
----------------------|SCC Interrupt Vectors |-----
===========#==#=======#===============================================#=====
$00000180.L|RW| - |SCC Port B Transmit Buffer Empty |SCC
$00000184.L|RW| - |Unused |SCC
$00000188.L|RW| - |SCC Port B External Status Change |SCC
$0000018C.L|RW| - |Unused |SCC
$00000190.L|RW| - |SCC Port B Receive Character Available |SCC
$00000194.L|RW| - |Unused |SCC
$00000198.L|RW| - |SCC Port B Special Receive Condition |SCC
$0000019C.L|RW| - |Unused |SCC
$000001A0.L|RW| - |SCC Port A Transmit Buffer Empty |SCC
$000001A4.L|RW| - |Unused |SCC
$000001A8.L|RW| - |SCC Port A External Status Change |SCC
$000001AC.L|RW| - |Unused |SCC
$000001B0.L|RW| - |SCC Port A Receive Character Available |SCC
$000001B4.L|RW| - |Unused |SCC
$000001B8.L|RW| - |SCC Port A Special Receive Condition |SCC
$000001BC.L|RW| - |Unused |SCC
| | | CORRECTED: this table was previously built |SCC
| | | densely (4 consecutive different meanings per |SCC
| | | channel). The real layout is sparse, every |SCC
| | | other slot unused, confirmed by the Atari |SCC
| | | Compendium AND Atari's own TOS 2.06/3.06 |SCC
| | | source (tos3x bios/chardev.S, sccvect: table).|SCC
===========#==#=======#===============================================#=====
----------------------|Exception Crash Save Area |-----
===========#==#=======#===============================================#=====
$00000380.L|RW|PRCLIVE|Validates crash page, if $12345678 |
$00000380 to $000003CC Processor state save area
When TOS survives a crash it saves the processor state here
before displaying the bombs, so a debugger can recover it.
$380 proc_lives set to $12345678 if the save succeeded
$384 proc_dregs saved D0-D7, 32 bytes
$3A4 proc_aregs saved A0-A7, 32 bytes
$3C4 proc_enum the exception number that caused the crash
$3C8 proc_usp saved user stack pointer
$3CC proc_stk top 16 words from the exception stack frame
Only valid when proc_lives holds the magic value. TOS clears
it once the state has been read.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000384. |RW|PRCDREG|Saved registers D0-D7 (32 bytes) |
$000003A4. |RW|PRCAREG|Saved registers A0-A7 (32 bytes) |
$000003C4.L|RW|PRCENUM|Vector number of crash exception |
$000003C8.L|RW|PRC_USP|Saved user stack pointer |
$000003CC. |RW|PRC_STK|16 words from exception stack (32 bytes) |
===========#==#=======#===============================================#=====
----------------------|GEM Vectors |-----
===========#==#=======#===============================================#=====
$00000400.L|RW|ETVTIMR|etv_timer - GEM event timer vector |
$00000404.L|RW|ETVCRIT|etv_critic - GEM critical error handler |
$00000408.L|RW|ETVTERM|etv_term - GEM program termination vector |
$0000040C.L|RW|ETVXTRA|etv_xtra - 5 additional vectors, unused |
===========#==#=======#===============================================#=====
----------------------|System Variables |-----
===========#==#=======#===============================================#=====
$00000420.L|RW|MEMVALD|memvalid - memory conf valid if $752019F3 |
$00000420 memvalid Memory configuration valid flag
Longword. Holds $752019F3 when the memory configuration is
valid. Checked together with memval2 at $0000043A ($237698AA)
and memval3 at $0000051A ($5555AAAA).
If all three match, TOS trusts the existing memory setup and
performs a warm start. If any is wrong it runs the full memory
sizing routine, which is slower and clears RAM.
Clearing these is how a program forces a cold start.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000424.B|RW|MEMCTRL|memcntlr - copy of $FFFF8001 %____xxxx |
| | | Bank 0 size 00:128k,01:512k,10:2Mb-------+||| |
| | | Bank 1 size 00:128k,01:512k,10:2Mb---------+| |
$00000426.L|RW|RESVALD|resvalid - resvector valid if $31415926 |
$00000426 and $0000042A resvalid and resvector
$426 resvalid longword, magic number $31415926
$42A resvector longword, address to jump to
If resvalid holds $31415926 at reset time, TOS jumps through
resvector very early in the reset sequence, before most of the
system is initialised.
This is the standard hook for reset-resident code. The routine
must be extremely careful: almost nothing is set up yet.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$0000042A.L|RW|RESVECT|resvector - system reset bailout vector |
$0000042E.L|RW|PHYSTOP|phystop - physical top of ST RAM |
$0000042E to $00000436 Memory extent variables
$42E phystop longword, physical top of ST RAM
$432 _membot longword, lowest address available to the heap
$436 _memtop longword, highest address available to the heap
phystop is the real end of ST RAM as found by the memory
sizing routine. _membot and _memtop bracket the area GEMDOS
will allocate from, so they exclude the screen buffer and any
memory TOS has reserved for itself.
Alternative RAM (TT RAM, fast RAM) is tracked separately by
ramtop at $000005A4 and ramvalid at $000005A8.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000432.L|RW|_MEMBOT|_membot - bottom of TPA (user memory) |
$00000436.L|RW|_MEMTOP|_memtop - top of TPA (user memory) |
$0000043A.L|RW|MEMVAL2|memval2 - validates memcntlr if $237698AA |
$0000043E.W|RW|_FLOCK |flock - if <>0, VBL floppy routine is off |
$0000043E flock Floppy and DMA lock
Word. Set to a non-zero value before touching the DMA or FDC
registers directly, and clear it again afterwards.
While flock is non-zero the vertical blank floppy routine
leaves the hardware alone. If it is left clear, the VBL can
deselect the drive or disturb a transfer part way through.
This is the single most important variable to get right when
writing directly to the floppy hardware.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000440.W|RW|SEEKRAT|seekrate - floppy step rate %______xx |
| | | 0:6ms 1:12ms 2:2ms 3:3ms (WD1772)----------+| |
$00000440 seekrate Floppy drive step rate
Word. Sets the step rate for BOTH floppy drives.
0 = 6 ms
1 = 12 ms
2 = 2 ms
3 = 3 ms (default)
These values are written straight into the low two bits of a
WD1772 Type I command, so they are the chip's own encoding.
IMPORTANT: these are WD1772 values. The WD1770 uses 6, 12, 20
and 30 ms for the same four encodings, so values must never be
carried between the two chips.
On the Atari 3 ms is the normal setting. 2 ms works but gives
less reliable seeks in practice.
TOS declares this as _seekrate at $440 in common/tosvars.inc.
Values from the Atari Compendium, cross-checked against the
WD1772 command encoding.
$00000442.W|RW|TIMR_MS|_timr_ms - system timer calibration, ms |
$00000442 _timr_ms System timer period
Word. The interval between system timer ticks, in
milliseconds. Normally 20, giving the 50 Hz tick that drives
the GEM event timer.
Not the same as the 200 Hz counter at $000004BA, which is
driven directly by MFP Timer C.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000444.W|RW|FVERIFY|_fverify - if <>0, verify floppy writes |
$00000444 _fverify Floppy write verify flag
Word. When non-zero, every floppy write is read back and
compared. When zero, no verification is done.
Verification roughly doubles write time. TOS defaults it on.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000446.W|RW|BOOTDEV|_bootdev - default boot drive |
$00000446 _bootdev Boot device
Word. The device the system was booted from.
0 = A: 1 = B: 2 = C: and so on
Hatari writes this directly during machine setup
(src/stMemory.c: STMemory_WriteWord(0x446, nBootDrive)),
commented "Boot up on A(0) or C(2)".
TOS declares it as _bootdev at $446 in common/tosvars.inc.
Meaning from the Atari Compendium.
$00000448.W|RW|PALMODE|palmode - 0:NTSC(60Hz), else PAL(50Hz) |
$00000448 palmode Video standard flag
Word. Zero indicates NTSC (60 Hz) video, any other value
indicates PAL (50 Hz).
Reflects the state of the sync mode register at $FFFF820A
bit 1, which is set from the country code in the OS header.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$0000044A.B|RW|DEFSHFT|defshiftmd - default video resolution |
$0000044C.B|RW|SSHIFTM|sshiftmd - copy of $FFFF8260 %_____xxx |
| | | 0:low 1:medium 2:high resolution----------+|| |
$0000044C sshiftmd Shifter mode shadow
Byte. A copy of the hardware shift mode register at
$FFFF8260, kept so software can read the current resolution
without touching the hardware.
0 = low 320x200, 4 planes
1 = medium 640x200, 2 planes
2 = high 640x400, 1 plane
The related variable defshiftmd at $0000044A holds the
resolution to use at the next reset.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$0000044E.L|RW|V_BAS_A|_v_bas_ad - screen base address |
$0000044E _v_bas_ad Logical screen address
Longword. The address of the logical screen, the buffer that
drawing operations write into.
Before TOS 1.06 this had to be on a 256 byte boundary, since
the ST video base register has no low byte. From TOS 1.06 and
on an STE or later it can be any even address.
The physical screen, the one the shifter is actually reading,
is set by the video base registers at $FFFF8201, $FFFF8203 and
$FFFF820D. The two can point at different buffers, which is
how double buffering works.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000452.W|RW|_VBLSEM|vblsem - if >0, VBL routine is executed |
$00000452 to $0000045E Vertical blank control
$452 vblsem word, 0 disables all VBL processing, 1 enables
$454 nvbls word, number of slots in the VBL handler list
$456 _vblqueue longword, pointer to the list of handlers
$45A colorptr longword, palette to load at the next VBL
$45E screenpt longword, screen base to set at the next VBL
The VBL queue is an array of nvbls longword pointers. A zero
entry is skipped, so installing a handler means finding a free
slot and writing your routine's address into it.
colorptr and screenpt are one-shot: if non-zero at VBL time,
the value is used and the variable is cleared. That gives a
tear-free way to change palette or screen base.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000454.W|RW|_NVBLS |nvbls - number of VBL routines |
$00000456.L|RW|VBLQUEU|_vblqueue - pointer to list of VBL routines |
$0000045A.L|RW|COLORPT|colorptr - palette to load at next VBL |
$0000045E.L|RW|SCREENP|screenpt - video RAM base for next VBL |
$00000462.L|RW|VBCLOCK|_vbclock - count of VBL interrupts |
$00000462 and $00000466 Vertical blank counters
$462 _vbclock longword, VBLs actually processed
$466 frclock longword, VBLs that occurred
frclock counts every vertical blank. _vbclock counts only
those where processing was allowed to run, so the difference
between them is the number of VBLs blocked by vblsem.
Note the wiki label FRCLOCK matches Atari's own name; the
Atari Compendium calls the same variable _frlock.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000466.L|RW|FRCLOCK|frclock - count of unblocked VBL interrupts |
$0000046A.L|RW|HDVINIT|hdv_init - hard disk initialisation vector |
$0000046A to $0000047E Hard disk vectors
$46A hdv_init initialisation routine
$46E swv_vec called on a monitor resolution change
$472 hdv_bpb used by Getbpb()
$476 hdv_rw used by Rwabs()
$47A hdv_boot JSRed through to boot from hard disk
$47E hdv_mediach used by Mediach()
All longwords. A value of zero means no hard disk driver is
installed for that function.
These are the standard hook points a hard disk driver patches
during boot. swv_vec is the odd one out: it is called when the
system detects the monitor has been changed between colour and
monochrome.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$0000046E.L|RW|SWV_VEC|swv_vec - resolution change vector |
$00000472.L|RW|HDV_BPB|hdv_bpb - hard disk Getbpb vector |
$00000476.L|RW|HDV_RW |hdv_rw - hard disk read/write vector |
$0000047A.L|RW|HDVBOOT|hdv_boot - hard disk boot vector |
$0000047E.L|RW|HDVMEDC|hdv_mediach- hard disk media change vector |
$00000482.W|RW|CMDLOAD|_cmdload - if <>0, load COMMAND.PRG at boot |
$00000482 _cmdload Command shell load flag
Word. If non-zero at boot, the system attempts to load and run
COMMAND.PRG from the root of the boot drive instead of going
straight to the desktop.
Set by a boot sector or an AUTO folder program that wants to
replace the shell.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000484.B|RW|CONTERM|conterm - console attributes %____SBRK |
| | | Bconin returns shift status--------------+||| |
| | | System bell 0:off,1:on--------------------+|| |
| | | Key repeat 0:off,1:on----------------------+| |
| | | Key click 0:off,1:on------------------------+ |
$00000484 conterm Console attribute bits
Byte. A bit array controlling several console behaviours:
Bit 3 Cause Bconin() to return the shift status in the
high word of its return value
Bit 2 Enable system bell
Bit 1 Enable key repeat
Bit 0 Enable key click
Bit 3 is the useful one for anything reading the keyboard
directly: with it set, Bconin() returns the state of the
shift, control and alternate keys alongside the character.
The byte at $00000485 immediately after is reserved.
TOS declares this as _conterm at $484 in common/tosvars.inc,
described there as the attribute vector for console output.
Bit meanings from the Atari Compendium.
$00000485.B|RW| - |Reserved |
$00000486.L|RW|TRP14RT|trp14ret - trap #14 return address, unused |
$0000048A.L|RW|CRITRET|criticret - critical error handler return |
$0000048E. |RW|_THEMD |themd - first memory descriptor (16 bytes)|
$0000048E and $0000049E Memory descriptors
$48E themd first memory descriptor, 16 bytes
$49E _md longword, space for an additional descriptor
A memory descriptor block describes one contiguous region
GEMDOS may allocate from:
long m_link pointer to the next descriptor, 0 if last
long m_start start address of the region
long m_length length in bytes
long m_own owner process, 0 if free
On a machine with alternative RAM there are two descriptors,
one for ST RAM and one for TT RAM, chained through m_link.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$0000049E.L|RW|___MD |_md - space for additional descriptor |
$000004A2.L|RW|SAVPTR |savptr - pointer to BIOS register save area|
$000004A2 savptr BIOS register save area pointer
Longword. Points to the buffer the BIOS uses to save its
internal registers when re-entered.
Because the BIOS is not re-entrant, a routine that calls BIOS
functions from inside an interrupt must give it a separate
save area: allocate /2 bytes/, point savptr at it, make the
call, then restore savptr. Otherwise the interrupted call's
saved state is overwritten.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$000004A6.W|RW|_NFLOPS|nflops - number of floppy drives attached |
$000004A6 _nflops Number of floppy drives
Word. The number of floppy drives actually connected, 0, 1
or 2.
This is the variable to test for a second physical drive.
_drvbits at $000004C2 always shows both A and B set when any
floppy is present, because of virtual drive swapping.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$000004A8.L|RW|CONSTAT|con_state - vector for screen output |
$000004A8 and $000004AC Console state
$4A8 con_state longword, vector to the current console
output routine
$4AC save_row word, temporary store for the cursor row
con_state is switched between several internal routines as the
console works through a VT-52 escape sequence, so its value
depends on how much of a sequence has been received.
save_row holds the cursor row while an ESC-Y cursor
positioning sequence is being decoded, since the row arrives
before the column.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$000004AC.W|RW|SAVEROW|save_row - temporary cursor line storage |
$000004AE.L|RW|SAVCTXT|sav_context- exception processing save area |
$000004B2. |RW|_BUFL |bufl - 2 GEMDOS buffer list heads(8bytes)|
$000004B2 bufl GEMDOS buffer list heads
Two longwords, 8 bytes total.
$4B2 pointer to the data sector buffer list
$4B6 pointer to the FAT and directory sector buffer list
Each points to a chain of buffer control blocks GEMDOS uses to
cache disk sectors. Walking these chains is how disk cache
utilities find and flush TOS's own buffers.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$000004BA.L|RW|_HZ_200|hz_200 - 200 Hz system clock counter |
$000004BA _hz_200 200 Hz system counter
Longword. Increments 200 times a second, driven directly by
MFP Timer C. Free running from power on.
The most useful timing reference available without touching
hardware: read it, do the work, read it again and subtract.
Resolution is 5 ms.
Also commonly used as a random number seed.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$000004BE.L|RW|THE_ENV|the_env - default environment string |
$000004BE THE_ENV Default environment string
Longword holding the default environment, which on a stock
system is four zero bytes, an empty environment. A program
started with a null environment pointer inherits this.
ADDRESS CONFLICT, resolved in favour of $4BE: the Dan Hollis
lineage listings, including the F030/CT60 listing V2.1 of
March 2009, place the_env at $000004BC. That cannot be right,
because $000004BA holds the 200 Hz counter, which is a
longword and therefore occupies $4BA to $4BD. $4BC would sit
inside it. The next free longword is $4BE, and both EmuTOS
(tosvars.ld, _the_env) and the Atari Compendium give $4BE.
The following variable, _drvbits at $4C2, is the same in every
source, which leaves exactly one longword of space and
confirms the arithmetic.
From EmuTOS tosvars.ld and the Atari Compendium.
$000004C2.L|RW|DRVBITS|_drvbits - bit map of mounted drives |
$000004C2 _drvbits Connected drive bitmap
Longword. One bit per drive letter:
Bit 0 = A:, bit 1 = B:, through to bit 25 = Z:
A set bit means that drive exists.
Note: if at least ONE floppy drive is connected, BOTH the A
and B bits are always set. This is because of virtual drive
swapping, where a single physical drive answers as both A and
B with a disk change prompt. Testing bit 1 does not tell you
whether a second physical drive is fitted; use nflops at
$000004A6 for that.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$000004C6.L|RW|DSKBUFP|_dskbufp - pointer to 1K disk buffer |
$000004C6 and $000004CA Disk buffer and autoexec path
$4C6 _dskbufp longword, pointer to a 1K disk buffer
$4CA _autopath longword, pointer to the AUTO folder path
The 1K buffer at _dskbufp is used by TOS for disk operations
and is also borrowed by some graphics routines. It is safe to
use as scratch space between disk calls, but not across one.
_autopath points to the GEMDOS path specification of the
directory programs are loaded from at boot.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$000004CA.L|RW|AUTOPAT|_autopath - pointer to autoexec path |
$000004CE. |RW|VBLLIST|_vbl_list - 8 VBL routine pointers (32 bytes) |
$000004EE.W|RW|DUMPFLG|_dumpflg - screen dump flag |
$000004EE and $000004F0 Screen dump control
$4EE _dumpflg word, screen dump flag
$4F0 prtabt word, printer abort flag
_dumpflg is initialised to $FFFF. The ALT-HELP screen dump
code sets it to 0 while a dump is in progress and back to
$FFFF when finished, so setting it to 0 yourself prevents a
dump starting.
Note: the Atari Compendium calls the variable at $4EE
_prt_cnt. Atari's own TOS source calls it _dumpflg, which is
the name used here.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$000004F0.W|RW|_PRTABT|prtabt - printer abort flag |
$000004F2.L|R-|SYSBASE|_sysbase - pointer to start of OS |
$000004F2 _sysbase Pointer to the operating system
Longword. Points to the start of TOS in memory, which begins
with the OS header structure:
Offset Size Field Meaning
$00 word os_entry BRA.S to the reset handler
$02 word os_version TOS version number
$04 long reseth pointer to the reset handler
$08 long os_beg base address of the OS
$0C long os_end end of ST-RAM used by the OS
$10 long os_rsv1 reserved, default UI entry point
$14 long os_magic GEM memory usage parameter block
$18 long os_date TOS date
$1C word os_conf PAL flag and country code
$1E word os_dosdate TOS date in GEMDOS format
$20 long os_root BDOS quick pool pointer (1.2+)
$24 long os_kbshift pointer to the shift state (1.2+)
$28 long os_run pointer to current basepage(1.2+)
$2C long reserved (1.2+)
The last four fields only exist from TOS 1.2 onward. Check
os_version before reading them.
os_version is BCD: $0102 is TOS 1.02, $0404 is TOS 4.04.
CONFLICT on os_date format: the Atari Compendium gives it as
$YYYYMMDD. EmuTOS include/biosdefs.h comments it as BCD
MMDDYYYY. Not resolved here.
EmuTOS note: EmuTOS puts the ASCII signature 'ETOS' in the
final longword at offset $2C, where real TOS leaves it
reserved. That is a reliable way to detect EmuTOS.
Structure from the Atari Compendium and EmuTOS
include/biosdefs.h. Hatari reads this variable at
src/gemdos.c to locate the OS.
$000004F6.L|RW|SHELL_P|_shell_p - pointer to shell |
$000004F6 to $000004FE OS pointers
$4F6 _shell_p longword, shell process pointer
$4FA end_os longword, end of the OS in RAM
$4FE exec_os longword, OS entry point
_shell_p allows a shell process to be installed which is
entered when the current program terminates. Normally unused.
exec_os is jumped through once operating system initialisation
is complete, normally pointing at the AES startup.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$000004FA.L|R-|_END_OS|end_os - pointer to end of OS in RAM |
$000004FE.L|R-|EXEC_OS|exec_os - pointer to OS entry point |
$00000502.L|RW|SCR_DMP|scr_dump - pointer to screen dump routine |
$00000502 to $00000512 Printer and auxiliary vectors
$502 scr_dump called when ALT-HELP is pressed
$506 prt_stat check status of the PRN: device
$50A prt_vec output a byte to the PRN: device
$50E aux_stat check status of the AUX: device
$512 aux_vec output a byte to the AUX: device
All longwords. The four printer and auxiliary vectors are used
by Prtblk(), so replacing them redirects printer output
without patching GEMDOS.
Atari's own source names the last four prv_lsto, prv_lst,
prv_auxo and prv_aux.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$00000506.L|RW|PRTSTAT|prt_stat - pointer to prv_lsto |
$0000050A.L|RW|PRT_VEC|prt_vec - pointer to prv_lst |
$0000050E.L|RW|AUXSTAT|aux_stat - pointer to prv_auxo |
$00000512.L|RW|AUX_VEC|aux_vec - pointer to prv_aux |
$00000516.L|RW|PUN_PTR|pun_ptr - pointer to pun_info if AHDI |
$00000516 pun_ptr AHDI partition information
Longword. Points to a PUN_INFO structure maintained by AHDI,
Atari's hard disk driver, describing the mounted partitions.
The structure begins:
word puns number of drives supported, including
floppies (maximum 16)
then pun[16] one byte per drive:
bits 0-2 physical ACSI unit
bit 7 set if the drive is not
present
then start[16] longword starting sector of each
partition
A null pointer means AHDI is not loaded.
This is a driver structure rather than hardware, so its exact
layout depends on the AHDI version in use. Treat the above as
indicative.
TOS declares it as _pun_ptr at $516 in common/tosvars.inc with
the comment "if AHDI, pointer to pun_info". Structure from the
Atari Compendium.
$0000051A.L|RW|MEMVAL3|memval3 - memory conf valid if $5555AAAA |
$0000051E. |RW|BCONSTA|bconstat_vec - 8 input status vectors(32 bytes)|
$0000051E to $0000057E BIOS device vector tables
Four tables of eight longwords each:
$51E bconstat_vec input status routines
$53E bconin_vec input routines
$55E bcostat_vec output status routines
$57E bconout_vec output routines
Indexed by BIOS device number:
0 = PRT printer
1 = AUX RS-232
2 = CON console (screen and keyboard)
3 = MIDI
4 = IKBD keyboard controller
5 = RAW console without VT-52 processing
6 and 7 unused on most machines
Replacing an entry redirects that device's I/O. This is how
serial port drivers and screen accelerators hook in.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$0000053E. |RW|BCONIN |bconin_vec - 8 input vectors (32 bytes) |
$0000055E. |RW|BCOSTAT|bcostat_vec - 8 output status vectors(32bytes)|
$0000057E. |RW|BCONOUT|bconout_vec - 8 output vectors (32 bytes) |
$0000059E.W|R-|LNGFRAM|_longframe - if <>0, CPU uses long stack frames|
$0000059E _longframe Processor stack frame format
Word. Zero means the processor uses short (68000) exception
stack frames; any other value means long (68010 and above)
frames.
This matters to any exception handler that inspects the stack:
the offset to the saved PC differs between the two formats.
Testing this variable is the correct way to handle both,
rather than assuming the CPU type.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$000005A0.L|R-|P_COOKI|_p_cookie - pointer to cookie jar |1.06+
$000005A0 _p_cookie Cookie jar pointer
Longword. Points to the cookie jar, a table of longword pairs
describing installed hardware and software features.
Each entry is two longwords: a four character tag and a value.
The list ends with an entry whose tag is zero, where the value
gives the total number of slots allocated.
Common tags include _CPU, _FPU, _SND, _MCH, _VDO, _FLK and
_IDT. A null pointer means no cookie jar exists, which is the
case before TOS 1.06.
Present from TOS 1.06 onward.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$000005A4.L|RW|_RAMTOP|ramtop - top of alternative (Fast) RAM |TT,F
$000005A8.L|RW|RAMVALD|ramvalid - validates ramtop if $1357BD13 |TT,F
$000005AC.L|RW|BELHOOK|bell_hook - vector for system bell |1.06+
$000005AC and $000005B0 Sound hooks
$5AC bell_hook vector jumped through to sound the bell
$5B0 kcl_hook vector jumped through for key clicks
Both longwords. The key click routine is entered with the
scancode of the key just pressed, so a replacement can vary
the click by key.
Replacing these is the supported way to change or silence the
system sounds without patching the BIOS.
Present from TOS 1.06 onward.
From the Atari Compendium and Atari TOS common/tosvars.inc.
$000005B0.L|RW|KCLHOOK|kcl_hook - vector for keyclick |1.06+
===========#==#=======#===============================================#=====
----------------------|STBook |-----
===========#==#=======#===============================================#=====
$00000889.B|RW|LCDPSHD|Shadow of LcdPowerControl ($FFFF827F) |STB
===========#==#=======#===============================================#=====
Magic validation values, collected for reference:
memvalid $420 = $752019F3
resvalid $426 = $31415926
memval2 $43A = $237698AA
memval3 $51A = $5555AAAA
ramvalid $5A8 = $1357BD13
proc_lives $380 = $12345678
flock ($43E) must be non-zero before any direct FDC or DMA access, so
the VBL floppy routine does not deselect the drive or disturb the DMA
mid-transfer. Clear it again afterwards.
Sources: addresses and labels from Atari's own TOS source
(th-otto/tos1x, common/tosvars.inc), cross-checked against Hatari,
EmuTOS (bios/tosvars.S) and The Atari Compendium Appendix B. Sizes are
inferred from address spacing and the Compendium; the byte-versus-word
calls on memcntlr, defshiftmd and sshiftmd are judgement rather than
documented. Read-only marks reflect convention, not hardware
enforcement.
===========#==#=======#===============================================#=====
----------------------|IDE Bus |-----
===========#==#=======#===============================================#=====
$FFF00000.W|RW|IDE_DAT|Data Register |
$FFF00005.B|RW|IDE_ERR|Read:Error / Write:Features Register |
$FFF00005 IDE_ERR Error Register (read) / Features (write)
READ - error register, valid when the error bit is set in the
status register at $FFF0001D:
Bit 7 Bad Block Mark
Bit 6 Uncorrectable Error
Bit 5 (reserved)
Bit 4 ID Field Not Found
Bit 3 (reserved)
Bit 2 Command Aborted
Bit 1 Track 0 Not Found
Bit 0 DAM (Data Address Mark) Not Found
WRITE - features register, command specific. Used mainly by
SET FEATURES to enable or disable drive options.
Bit assignments from the Atari Compendium, matching the
standard ATA error register layout.
$FFF00009.B|RW|IDE_SCC|Sector count |
$FFF0000D.B|RW|IDE_SNR|Sector number |
$FFF00011.B|RW|IDE_CYL|Cylinder low |
$FFF00011 and $FFF00015 Cylinder Low / Cylinder High
$FFF00011 Cylinder Low bits 7-0 of the cylinder number
$FFF00015 Cylinder High bits 9-8 of the cylinder number
Together these form the ten bit cylinder number used in CHS
addressing.
In LBA mode the same two registers carry LBA bits 23-8 instead,
with the full eight bits of each used.
From the Atari Compendium, matching the standard ATA layout.
$FFF00015.B|RW|IDE_CYH|Cylinder high |
$FFF00019.B|RW|IDE_H_D|Head/Drive Register |
$FFF00019 IDE_H_D Drive / Head Register
Bit 7 (always 1 on early ATA)
Bit 6 0 = CHS addressing, 1 = LBA addressing
Bit 5 (always 1 on early ATA)
Bit 4 Drive select: 0 = master, 1 = slave
Bits 3-0 Head number, 0-15
In LBA mode bits 3-0 carry LBA bits 27-24 instead of the head
number.
Drive select and head number from the Atari Compendium. The
addressing and reserved bits follow the standard ATA layout.
$FFF0001D.B|RW|IDE_S_C|Read:Status Register/Write:Command Register |
$FFF0001D IDE_S_C Status (read) / Command (write)
READ - status register:
Bit 7 BSY busy, drive owns the registers
Bit 6 DRDY drive ready
Bit 5 DF drive fault
Bit 4 DSC drive seek complete
Bit 3 DRQ data request, drive wants a transfer
Bit 2 CORR corrected data
Bit 1 IDX index
Bit 0 ERR error, see the error register at $FFF00005
Reading this register clears a pending interrupt. Read the
alternate status at $FFF00039 instead when polling, since that
does not clear the interrupt.
WRITE - command register. Writing starts a command using the
values already loaded into the other registers.
RESOLVED: EmuTOS bios/ide.c places status and command at this
offset. The Atari Compendium places them at $FFF0001F and
marks $FFF0001A to $FFF0001D as unused. FALREG.TXT, an
independent hardware-level Falcon listing, agrees with EmuTOS:
it documents $FFF0001D as the first status register and notes
that reading it clears the pending interrupt. Two independent
sources against one settles this at $FFF0001D; the Compendium
entry is wrong.
Bit assignments follow the standard ATA status register.
$FFF00039.B|RW|IDE_ALT|Read:Alt Status/Write:Device Control |
$FFF00039 IDE_ALT Alternate Status (read) / Device Control (write)
READ - alternate status. Returns exactly the same bits as the
status register at $FFF0001D, but reading it does NOT clear a
pending interrupt. This is the register to poll with.
WRITE - device control:
Bit 2 SRST software reset
Bit 1 nIEN 1 = disable the drive's interrupt
From the Atari Compendium, which names it Alternate Status on
read and Alternate Command on write, and the standard ATA
layout.
$FFF0003D.B|R-|IDE_ACT|Active Address Register %W~~~~SM_ |F
| | | drive currently Writing---------------+|||||| |F
| | | one's complement of selected head------++++|| |F
| | | Slave active-------------------------------+| |F
| | | Master active-------------------------------+ |F
$FFF0003D IDE_ACT Active Address Register
Bit 7 (not documented)
Bit 6 1 = drive is currently writing
Bits 5-2 one's complement of the currently selected head
Bit 1 1 = slave drive active
Bit 0 1 = master drive active
This is the ATA Drive Address register, traditionally at
offset 7 of the control block, reflecting the state of the
drive select and head lines.
From FALREG.TXT (Aura), which documents it under the name
Aktive Adresse. Not referenced by EmuTOS, whose IDE structure
ends its named registers at $FFF00039, and not in the Atari
Compendium. The bit layout matches the standard ATA Drive
Address register, which supports FALREG's reading.
CONFLICT NOTE for the whole IDE block: the CT60 accelerator
claims the entire range $FFF00000 to $FFFBFFFF for its own
registers (per the F030/CT60 listing). That overlaps this IDE
register block and the TRUDIE add-on registers below. On a
CT60 equipped Falcon these addresses do not behave as listed
here.
$FFF00042.B|RW| - |Add-on control, not present on stock hardware |*
$FFF00042 and $FFF00044 Add-on control registers
Not present on stock Atari hardware. These sit in the same
address space as the IDE interface above.
Known to be used by: TRUDIE.
TRUDIE also claims $FFF00000 to $FFF00038, which is the IDE
register range itself, and $FFFFFFFF.
Extended bit information is not currently available for these
registers. The addresses are documented; what each bit does is
not.
Checked against EmuTOS: its IDE driver uses a plain ATA
register structure based at $FFF00000 and touches only the
standard offsets. It does not reference $FFF00042 or $FFF00044,
and has no concept of add-on IDE hardware in this range.
$FFF00044.B|RW| - |Add-on control, not present on stock hardware |*
===========#==#=======#===============================================#=====
----------------------|Memory Controller, System Control |-----
===========#==#=======#===============================================#=====
$FFFF8001.B|RW|MEM_CTL|Memory Controller |
$FFFF8001 MEM_CTL Memory Controller Configuration
Bits 3-2 Bank 1 size
Bits 1-0 Bank 0 size
Size encoding, same for both banks:
00 = 128k
01 = 512k
10 = 2M
11 = reserved
A shadow copy of this byte is kept in the system variable
memcntlr at $00000424, validated by memval2 at $0000043A.
Bit assignments from the Atari Compendium.
$FFFF8006.W|RW|SYS_CTL|System Control %MM______ _RS_bB_C |F
| | | Monitor Type (M0,M1)--------++ || || | |F
| | | Monochrome Monitor----------00 || || | |F
| | | RGB Monitor-----------------01 || || | |F
| | | VGA Monitor-----------------10 || || | |F
| | | TV--------------------------11 || || | |F
| | | Reset 0:ignore resetvector------------+| || | |F
| | | STE-compatible-I/O 0:off,1:on----------+ || | |F
| | | Blitterflag 0:on,1:off-------------------+| | |F
| | | Blitterspeed 0:half clock,1:full clock----+ | |F
| | | CPUspeed 0:half clock,1:full clock----------+ |F
$FFFF8006 SYS_CTL System Control / Connected Monitor Type
Bits 15-14, monitor type:
0 = Atari monochrome
1 = Atari colour (RGB)
2 = VGA colour
3 = television
Bit 9 Reset: 0 = ignore reset vector
Bit 8 STE compatible I/O: 0 = off, 1 = on
Bit 6 Blitter flag: 0 = on, 1 = off
Bit 5 Blitter speed: 0 = half clock, 1 = full clock
Bit 3 CPU speed: 0 = half clock, 1 = full clock
Bits 13-12 of the word (bits 5-4 of the byte at $FFFF8006)
report the fitted ST RAM per the F030/CT60 listing:
00 = 1MB 01 = 4MB 10 = 14MB 11 = no boot
This wiki's older ST/STe/MSTe/TT/F030 listing gives the same
field as 00 = 1MB, 01 = 4MB, 10 = 16MB, so the third value is
in dispute: 14MB is what a Falcon can actually carry with the
top of ST RAM reserved, 16MB the round figure. Unresolved.
That same older listing continues the byte with fields no
other source lists:
Bits 3-2 ROM wait states: 00 reserved, 01 two waits
(default), 10 one wait, 11 zero waits
Bit 1 video bus width: 0 = 16 bit, 1 = 32 bit (default)
Bit 0 RAM wait states: 0 = one wait (default),
1 = zero waits
UNVERIFIED: these three are not in Hatari, EmuTOS, any TOS
source or the Compendium, and Hatari treats the byte as read
only, so nothing here exercises them. Recorded because they
are plausible for a memory controller and nobody else
documents them, not because they are confirmed.
The Combel chip latches this byte during read; Hatari names it
"Monitor and memory conf" and restores the value on any write
(src/falcon/videl.c).
Monitor bits confirmed by Atari TOS 4.04, which tests
(byte >> 6) & 3 == 0 to detect a monochrome monitor, and by
the Atari Compendium value table.
Falcon only. On earlier machines monitor detection is through
MFP GPIP bit 7 at $FFFFFA01.
$FFFF8007.B|RW|SYS_FBC|Falcon Bus Control %_SB_BS__ |F
| | | Start type 0:cold,1:warm-------------+||||||| |
| | | STe Bus emulation 0:on,1:off----------+|||||| |
| | | Blitter control 0:on,1:off--------------+|||| |
| | | Blitter speed 0:8MHz,1:16MHz-------------+||| |
| | | CPU speed 0:8MHz,1:16MHz--------------------+ |
| | | Verified: EmuTOS bios/machine.c documents all |
| | | five bits and writes $25 (STe bus emulation |
| | | off, 16MHz blitter and CPU). Atari TOS |
| | | 3.06/4.04 bios/startup.S writes the same $25. |
$FFFF8007 SYS_FBC Falcon Bus Control
Bit 6 Start type: 0 = cold start, 1 = warm start
Bit 5 STe Bus emulation: 0 = on, 1 = off
Bit 3 Blitter control: 0 = on, 1 = off
Bit 2 Blitter speed: 0 = 8MHz, 1 = 16MHz
Bit 0 CPU speed: 0 = 8MHz, 1 = 16MHz
STe bus emulation has to be switched off for bus-error based
hardware detection to work on the Falcon.
EmuTOS bios/machine.c documents all five bits and writes $25,
setting STe bus emulation off with 16MHz blitter and CPU.
Atari TOS 3.06/4.04 bios/startup.S writes the same $25 and
tests bit 6.
Falcon only.
===========#==#=======#===============================================#=====
----------------------|DMA, VIDEL Controller |-----
===========#==#=======#===============================================#=====
$FFFF8201.B|RW|VDL_VBH|Video Base Hi |
$FFFF8201 / $FFFF8203 / $FFFF820D Video Base Address
Three byte registers holding the screen memory address:
$FFFF8201 bits 23-16, high
$FFFF8203 bits 15-8, middle
$FFFF820D bits 7-0, low (STE and Falcon only)
On the ST and Mega ST only the high and middle bytes exist, so
the screen base must be on a 256 byte boundary. The STE added
the low byte, allowing any even address.
Bit 0 of the low byte is ignored; the address is always even.
The current value is shadowed in the system variable
_v_bas_ad at $0000044E. Changing the base takes effect at the
next vertical blank, or immediately if written during one.
From the Atari Compendium.
$FFFF8203.B|RW|VDL_VBM|Video Base Mi |
$FFFF8205.B|Rw|VDL_VCH|Video Count Hi |
$FFFF8205 / $FFFF8207 / $FFFF8209 Video Address Counter
Three byte registers holding the address the shifter is
currently reading from:
$FFFF8205 bits 23-16, high
$FFFF8207 bits 15-8, middle
$FFFF8209 bits 7-0, low
On the ST and Mega ST these are read only; the Atari
Compendium marks them so. From the STE onward they are
writable, and Hatari implements real writes on STE, TT and
Falcon (src/video.c, src/falcon/videl.c). Writing them moves
the fetch position mid-frame, which is how some demos do
full-screen hardware scrolling without moving data.
Reading these during display gives the shifter's position in
screen memory, which is how raster-timed effects work out where
the beam is. The three bytes are not read atomically, so the
counter can advance between reads. Hatari notes that when one
byte is read, all three are latched together from the current
counter (src/video.c, fix for Braindamage demo).
From the Atari Compendium, writability from Hatari.
$FFFF8207.B|Rw|VDL_VCM|Video Count Mi |
$FFFF8209.B|Rw|VDL_VCL|Video Count Lo %xxxxxxx_ |
$FFFF820A.B|RW|VDL_SYM|Sync mode %______VS |
| | | Vfrequency 0:60hz(NTSC),1:50Hz(PAL)--------+| |
| | | Sync 0:internal,1:external------------------+ |
$FFFF820A VDL_SYM Video Shifter Sync Mode
Bit 1 Vertical frequency: 0 = 60Hz (NTSC), 1 = 50Hz (PAL)
Bit 0 Sync source: 0 = internal, 1 = external
Setting bit 0 stops the shifter generating its own sync and
makes it follow an external signal. This is the register used
for the classic sync-switching border removal tricks: writing
it at a precise point in the scanline makes the shifter miss
the border, opening the display area.
On the Falcon the register keeps the same meaning: TOS 4.04
sets bit 1 for 50Hz and clears it for 60Hz, and bits 7-2 are
hard wired to zero. Some Falcon documents (the Aura video
guide among them) claim bit 1 reads back the monitor type
instead; Hatari src/falcon/videl.c states explicitly that
those documents are wrong.
Bit assignments from the Atari Compendium; Falcon behaviour
from Hatari.
$FFFF820D.B|RW|VDL_VBL|Video Base Lo %xxxxxxx_ |STE,F
$FFFF820E.W|RW|VDL_LOF|Line Offset in Words %_______x xxxxxxxx |F
$FFFF820E / $FFFF820F / $FFFF8210 Line Offset and Line Width
$FFFF820E Falcon: Line Offset, word register. Number of
EXTRA words added to the video address at the end
of each display line. 0 = lines are contiguous.
$FFFF820F STE: LINEWID, byte register, same meaning. Number
of extra words added per line; the low byte of the
Falcon word above.
$FFFF8210 Falcon: Line Width, word register. Length of one
display line in words (for example $50 = 80 words
for ST low).
The offset lets the shifter display a window onto a screen
buffer wider than the visible display, which is how the STE
does hardware horizontal scrolling.
CORRECTION: this page previously called $FFFF820F "Line Width
minus one", following the Atari Compendium. That is wrong.
The value is the extra words to SKIP per line, not the line
width. Hatari's rendering code adds the two together on every
display line:
lineoffset = IoMem_ReadWord(0xff820e) & 0x01ff; 9 bits
linewidth = IoMem_ReadWord(0xff8210) & 0x03ff; 10 bits
videl.videoRaster += linewidth + lineoffset;
(src/falcon/videl.c, in both the line and the screen-convert
paths). That is behavioural code, not a comment, so it also
settles the register widths: 9 bits for the offset, 10 for the
width. EmuTOS bios/videl.c writes 0 to the offset for normal
screens and the true width to $FFFF8210.
On the STE, when the horizontal scroll value is non zero the
shifter fetches an extra chunk of words per line, so LINEWID
is normally programmed to compensate (reduced by the number
of planes).
How the two Falcon registers work together. This is what
Hatari's renderer actually does, and mikro's VIDEL document
states the same rule:
length of logical line = line width + line offset
So for a 512 pixel wide virtual screen displayed 320 pixels
wide in true colour:
$FFFF820E = 512 - 320 = 192 (offset)
$FFFF8210 = 320 (width)
and the width in words for any mode is
line width = (horizontal resolution / 16) x bitplanes
with bitplanes 1, 2, 4, 8 or 16 for 2, 4, 16, 256 or 65536
colours.
PRACTICAL LIMIT: the line offset is only 9 bits, masked to
$01FF in Hatari's renderer, so the largest value is 511 words.
Combined with a 320 pixel display that caps a hardware-
scrolled virtual screen at roughly 1342 pixels wide (511 x 2 +
320 in true colour). Anything wider needs multiple buffers, a
blit per frame, or a Timer B interrupt rewriting the video
address every line. This trips people up because most listings
give the bit width without spelling out what it costs (raised
on Atari-Forum, January 2024).
SOURCING NOTE for this register pair and for $FFFF8260 and
$FFFF8266 below: Hatari's source comments on these four
registers are copied verbatim from mikro's VIDEL document,
ASCII bit diagrams and all, so the two are not independent
documentary sources. What IS independent is Hatari's code -
the masks and the raster arithmetic above - and EmuTOS, which
was written separately. Where this page says a claim is
confirmed by Hatari for these registers, it means the code
path, not the comment.
$FFFF820F.B|RW|VDL_LNW|LINEWID - extra words per line |STE
$FFFF8210.W|RW|VDL_LWD|Line Width in Words %______xx xxxxxxxx |F
$FFFF8240.W|RW|VDL_STC|ST Palette Register 00 %____rRRR gGGGbBBB |
$FFFF8240 to $FFFF825E ST/STE Palette Registers 0-15
Word registers, one per palette entry.
ST layout: XXXX XRRR XGGG XBBB (3 bits per gun, 512
colours)
STE layout: XXXX RRRR GGGG BBBB (4 bits per gun, 4096
colours)
IMPORTANT: on the STE the four bits within each nibble are NOT
in natural order. The bit arrangement per nibble is 0-3-2-1.
The extra bit the STE adds is placed at the TOP of the nibble
rather than the bottom, so that an ST program writing only the
lower three bits still produces the same colour it would on an
ST. Reading an STE palette value as a plain 4 bit number gives
the wrong intensity.
Hatari masks writes to $0777 on an ST and $0FFF on an STE.
Some games write $FFFF and read the value back to detect
whether they are running on an STE.
These registers are simulated for compatibility on the TT and
Falcon, which have their own wider palettes at $FFFF8400 and
$FFFF9800.
Layout and nibble ordering from the Atari Compendium, masking
behaviour from Hatari src/video.c.
...........|RW| - |...................... |
$FFFF825E.W|RW| - |ST Palette Register 15 |
$FFFF8260.B|RW|VDL_SSM|ST-Shift-Mode %_____xxx |
| | | 320*200*4---------------------------------000 |
| | | 640*200*2---------------------------------001 |
| | | 640*400*1---------------------------------010 |
$FFFF8260 VDL_SSM ST Video Shifter Mode
Bits 1-0 resolution
00 = 320x200, 4 planes (low)
01 = 640x200, 2 planes (medium)
10 = 640x400, 1 plane (high)
11 = reserved
The TT modes were previously listed here. Atari uses a separate
TT shifter register at $FFFF8262.
A shadow copy of the current value is kept in the system
variable sshiftmd at $0000044C.
Writing 11 is not a documented mode. On real hardware it
produces the same output as one of the other modes rather than
anything useful; neither Atari TOS nor EmuTOS ever writes it.
FALCON QUIRK: writing this register on a Falcon OVERWRITES the
Line Width register at $FFFF8210 and the Video Mode register
at $FFFF82C2, loading compatibility values for the selected ST
mode. EmuTOS bios/videl.c rewrites both registers immediately
after setting the ST shifter for exactly this reason, and the
Aura video guide tabulates the values loaded:
ST shift $8210 $82C2 (RGB/TV) $82C2 (VGA)
00 low $0050 $0000 $0005
01 medium $0050 $0004 $0009
10 high $0028 $0006 $0008
11 $0050 $0000 $0000
Writing it can also drop the VIDEL back into the STE palette
when the mode bits of $FFFF8266 are clear. Set the Falcon
registers AFTER the ST shifter, never before.
This is confirmed in operating system code, not just in
documentation. EmuTOS bios/videl.c, in its mode-set routine,
writes the line width and video control registers, then writes
the ST shifter, then writes both of them AGAIN, with the
comment that "writing to the ST shifter has just overwritten
these registers":
videlword(0x10) = linewidth;
videlword(0xc2) = vctl;
...
videlregs[0x60] = 0x01;
videlword(0x10) = linewidth;
videlword(0xc2) = vctl;
Hatari implements the hardware side in
VIDEL_ST_ShiftModeWriteByte (src/falcon/videl.c): writing
$FFFF8260 sets bUseSTShifter, masks the register to bits 1-0,
and writes a line width and a video mode value into $FFFF8210
and $FFFF82C2 chosen by the shift mode and the monitor type -
exactly the table above.
EmuTOS also CLEARS $FFFF8266 (SPSHIFT) before touching the ST
shifter in the same routine, which is the practical rule
mikro's VIDEL document gives: set the timing registers for the
320x200, 640x200 or 640x400 mode first, clear $FFFF8266, then
write this register. Skipping the clear gives strange
resolutions.
Bit assignments from the Atari Compendium and Atari TOS
bios/startup.S; the clobber behaviour verified in EmuTOS
bios/videl.c and Hatari src/falcon/videl.c; the value table
cross-checked against the Aura video guide and mikro.
===========#==#=======#===============================================#=====
$FFFF8262.B|RW|VDL_TSM|TT-Shift-Mode %_____xxx |TT
| | | ST low 320*200*4----------------------000 |TT
| | | ST medium 640*200*2----------------------001 |TT
| | | ST high 640*400*1----------------------010 |TT
| | | (Falcon rez marker)-----------------------011 |TT
| | | TT medium 640*480*4----------------------100 |TT
| | | (unused)----------------------------------101 |TT
| | | TT high 1280*960*1----------------------110 |TT
| | | TT low 320*480*8----------------------111 |TT
$FFFF8262 VDL_TSM TT Video Shifter Mode
Word register. The byte at $FFFF8262 is the high half:
Bit 7 Sample/Hold mode, also called Smear Mode
Bit 4 Hyper Mono mode
Bits 2-0 resolution
000 = 320x200, 4 planes ST low
001 = 640x200, 2 planes ST medium
010 = 640x400, 1 plane ST high
011 = (Falcon rez marker)
100 = 640x480, 4 planes TT medium
101 = (unused)
110 = 1280x960, 1 plane TT high
111 = 320x480, 8 planes TT low
The byte at $FFFF8263 is the low half and holds the ST Palette
Bank, selecting which 16 entry bank of the 256 entry TT palette
the ST compatible palette registers map onto.
RESOLVED, which bit is which: the Atari Compendium names the
two special modes Smear Mode and Hyper Mono Mode but does not
say which bit carries which, and this page previously marked
that as a CHECK. Hatari settles it in code rather than in a
comment. It reads both bits together as a mask of 0x90
(src/video.c), then uses them separately:
src/conv_gen.c bTTSampleHold = (TTSpecialVideoMode & 0x80)
src/video.c if (TTSpecialVideoMode & 0x10) Hyper-mono
So bit 7 is Sample/Hold, which is the mode the Compendium
calls Smear, and bit 4 is Hyper Mono. This wiki's own older
ST/STe/MSTe/TT/F030 listing agrees, giving word bit 15 as
Sample/Hold and word bit 12 as Hypermono, which are the same
two bits.
Sample/Hold holds each pixel value across the following
pixels, smearing the image horizontally. Hyper Mono is a
256 grey level mode built by feeding the 8 bit pixel value
straight to the palette DAC.
Atari TOS 3.06 bios/startup.S declares this register as
shift_tt and defines TTMED=4, TTHIGH=6, TTLOW=7. When the
resolution is 2 or lower, Hatari mirrors the value into
$FFFF8260 so the ST shifter register stays consistent.
Resolution values from Atari TOS source, bit positions from
Hatari src/video.c, names from the Atari Compendium.
===========#==#=======#===============================================#=====
$FFFF8264.B|RW|VDL_HSH|H-Scroll (no prefetch) %____xxxx |STE,F
$FFFF8264 and $FFFF8265 Horizontal Scroll
Both hold a pixel scroll offset of 0 to 15, shifting the
display left by that many pixels. The difference is prefetch:
$FFFF8265 scroll WITH prefetch. With a non zero value the
shifter fetches an extra chunk of words at the
start of each line and the display starts 16
pixels early, so LINEWID at $FFFF820F is normally
programmed to compensate. This is the documented
STE scroll register.
$FFFF8264 scroll WITHOUT prefetch. Same shift, but no extra
fetch and no 16 pixel early start. Undocumented by
Atari; used by some STE demos (Hatari cites
Digiworld 2 by ICE).
Both exist on the STE as well as the Falcon: Hatari
src/video.c implements the pair on STE ($ff8264 no prefetch,
$ff8265 prefetch). On the Falcon the Aura video guide lists
$FFFF8264 as a shadow of $FFFF8265.
Widely circulated STE scrolling documentation adds that
writing $FFFF8265 clears LINEWID at $FFFF820F, so the scroll
value should be written before the width. That side effect is
not modelled by Hatari and is not confirmed by EmuTOS or TOS
source; treat it as unverified but harmless to respect.
$FFFF8265 is documented in the Atari Compendium as the
Horizontal Scroll Register. $FFFF8264 is not listed there.
$FFFF8265.B|RW|VDL_HSL|H-Scroll Lo (with prefetch) %____xxxx |STE,F
$FFFF8266.W|RW|VDL_FSM|Falcon Shift Mode %_____2OT _HV8PPPP |F
| | | 2 Color mode 0:off,1:on----------+|| ||||||| |F
| | | Overlay mode 0:off,1:on-----------+| ||||||| |F
| | | True(high) color 0:off,1:on--------+ ||||||| |F
| | | Hsync 0:internal,1:external-----------+|||||| |F
| | | Vsync 0:internal,1:external------------+||||| |F
| | | 8 Bitplanes 0:off,1:on------------------+|||| |F
| | | falcon Palette 16 of 256 colors----------++++ |F
$FFFF8266 VDL_FSM SPSHIFT / Falcon Shift Mode
Bit 10 Enable 2-colour mode
Bit 8 Enable truecolor mode
Bit 6 Use external HSYNC
Bit 5 Use external VSYNC
Bit 4 Enable bitplane mode (8 bitplanes)
Bits 3-0 Falcon palette bank, 16 of 256 colours
Bit 9 is the overlay mode bit.
Only one of the mode bits should be set at a time. Truecolor
mode takes the pixel value straight to the DAC and ignores the
palette entirely.
What writing this register DOES:
- activates the Falcon palette, as against the STE palette
that $FFFF8260 selects. Hatari implements exactly this:
VIDEL_Falcon_ShiftMode_WriteWord clears bUseSTShifter,
the same flag $FFFF8260 sets
- because of that flag, once this register is in use
$FFFF8260 is ignored and does not need writing at all
- with bits 10, 8 and 4 all CLEAR it selects Falcon 16
colour mode, which is not the same as ST low: the Falcon
palette is in use, and bits 3-0 pick which bank of 16 out
of the 256 palette entries
This is the mirror image of the $FFFF8260 quirk: whichever of
the two shift registers you write last decides which palette
and which mode path the VIDEL follows.
Atari TOS 4.04 (tos3x bios/vsetmode.c) writes $0100 to select
truecolor and $0010 to select 8 bitplane mode, confirming bits
8 and 4. EmuTOS bios/videl.c writes $0400 for 2 colour mode
and tests the same bit through its SPS_2COLOR define,
confirming bit 10.
Bit meanings from the Atari Compendium, confirmed against
Atari TOS 4.04 and EmuTOS. mikro's VIDEL document agrees bit
for bit including bit 9 overlay, which no code path covers.
$FFFF827E.B|RW|STY_DSP|STACY Display State |STB
| | | UNVERIFIED: Atari Compendium only. Not |
| | | referenced by EmuTOS or any available TOS |
| | | source. |
$FFFF827E STY_DSP STACY Display State
Bit 1 1 = backlight off
Bit 0 1 = display off
STACY only, the portable ST. Rare hardware.
CONFLICT on the bit positions: the Atari Compendium gives the
two bits as 1 and 0 of the byte, as shown above. This wiki's
older ST/STe/MSTe/TT/F030 listing gives them as bits 10 and 9
of a WORD at the same address, with backlight the higher of
the two in both readings. The orderings agree, only the
positions differ, and nothing else documents this register at
all. Unresolved. On such rare hardware it is worth reading the
register back after a write to see which interpretation the
machine agrees with.
UNVERIFIED beyond that: not referenced by EmuTOS, Hatari or
any available TOS source.
$FFFF8280.W|RW|VDL_HHC|Horizontal Hold Counter %_______x xxxxxxxx |F
$FFFF8280 to $FFFF82AC VIDEL Timing Registers
These define the video timing directly, replacing the fixed
timings the ST shifter used. All are word registers, Falcon
only.
Horizontal, in pixel clocks unless noted:
$FFFF8280 Horizontal Hold Counter
$FFFF8282 Horizontal Hold Timer
$FFFF8284 Horizontal Border Begin
$FFFF8286 Horizontal Border End
$FFFF8288 Horizontal Display Begin (bit 8 selects which
half line the display starts on)
$FFFF828A Horizontal Display End
$FFFF828C Horizontal Sync Start
$FFFF828E Horizontal FS
$FFFF8290 Horizontal EE
Vertical, in half lines:
$FFFF82A0 Vertical Frequency Counter
$FFFF82A2 Vertical Frequency Timer
$FFFF82A4 Vertical Border Begin
$FFFF82A6 Vertical Border End
$FFFF82A8 Vertical Display Begin
$FFFF82AA Vertical Display End
$FFFF82AC Vertical Sync Start
The FS and EE registers only have an effect when bit 4 of the
Video Control register at $FFFF82C0 is SET. They relate to the
15 half-line HSYNC pulses generated at the start of the bottom
border when bit 3 of Video Control is set: FS controls how
long the HSYNC pulses of the previous half-line are held into
the next half-line for the middle five pulses, and EE does the
same for the first and last five pulses (Aura video guide,
measured on hardware). With bit 4 clear neither register has
an observable function. An earlier version of this note said
"bit 3, clear", which was wrong on both counts.
Atari TOS 4.04 (tos3x bios/vsetmode.c) and EmuTOS both write
this whole block as a table of values per video mode rather
than computing them, so the exact meaning of each field is
best understood from those mode tables.
Names from the Atari Compendium; register set confirmed
against Atari TOS 4.04 and EmuTOS bios/videl.c.
$FFFF8282.W|RW|VDL_HHT|Horizontal Hold Timer %_______x xxxxxxxx |F
$FFFF8284.W|RW|VDL_HBB|Horizontal Border Begin %_______x xxxxxxxx |F
$FFFF8286.W|RW|VDL_HBE|Horizontal Border End %_______x xxxxxxxx |F
$FFFF8288.W|RW|VDL_HDB|Horizontal Display Begin %______Hx xxxxxxxx |F
| | | 0:1.Halfline, 1:2.Halfline--------+ |F
$FFFF828A.W|RW|VDL_HDE|Horizontal Display End %_______x xxxxxxxx |F
$FFFF828C.W|RW|VDL_HSS|Horizontal Sync Start %_______x xxxxxxxx |F
$FFFF828E.W|RW|VDL_HFS|Horizontal FS %_______x xxxxxxxx |F
$FFFF8290.W|RW|VDL_HEE|Horizontal EE %_______x xxxxxxxx |F
$FFFF82A0.W|RW|VDL_VFC|Vertical Frequency Counter %_____xxx xxxxxxxx |F
$FFFF82A2.W|RW|VDL_VFT|Vertical Frequency Timer %_____xxx xxxxxxxx |F
$FFFF82A4.W|RW|VDL_VBB|Vertical Border Begin %_____xxx xxxxxxxx |F
$FFFF82A6.W|RW|VDL_VBE|Vertical Border End %_____xxx xxxxxxxx |F
$FFFF82A8.W|RW|VDL_VDB|Vertical Display Begin %_____xxx xxxxxxxx |F
$FFFF82AA.W|RW|VDL_VDE|Vertical Display End %_____xxx xxxxxxxx |F
$FFFF82AC.W|RW|VDL_VSS|Vertical Sync Start %_____xxx xxxxxxxx |F
$FFFF82C0.W|RW|VDL_VCT|Video Control %_______O BHVUSCMM |F
| | | h-base-Offset 0:128cyc,1:64cyc-----+ |||||||| |F
| | | Buswide 0:16bit,1:32bit--------------+||||||| |F
| | | Hsync 0:negative,1:positive-----------+|||||| |F
| | | Vsync 0:negative,1:positive------------+||||| |F
| | | Use FS & EE 0:off,1:on------------------+|||| |F
| | | 15 halfline hSyncs at VBB----------------+||| |F
| | | video Clock 0:32Mhz,1:25.175Mhz-----------+|| |F
| | | Monitor 0:Mono,1:RGB,2:VGA,3:TV------------++ |F
| | | Naming settled: Hatari (VCO) and the Aura |F
| | | video guide both call $82C0 Video Control. |F
$FFFF82C0 VDL_VCT Video Control
Bit 8 h-base offset: 0 = 128 cycles, 1 = 64 cycles
Bit 7 Bus width: 0 = 16 bit, 1 = 32 bit
Bit 6 HSYNC polarity: 0 = negative, 1 = positive
Bit 5 VSYNC polarity: 0 = negative, 1 = positive
Bit 4 Use FS and EE registers: 0 = off, 1 = on
Bit 3 15 half-line HSYNCs at vertical border begin
Bit 2 Video clock: 0 = 32MHz, 1 = 25.175MHz
Bits 1-0 Monitor: 0 = mono, 1 = RGB, 2 = VGA, 3 = TV
EmuTOS writes $0080 for monochrome, $0186 for VGA, and
$0181 or $0183 for RGB and television. Decoding those confirms
the monitor field, the clock bit set only for VGA, the bus
width bit always set, and bit 8 set for all VGA modes (64
cycle base offset) and clear for monochrome (128 cycles),
matching FALREG.TXT.
CORRECTION: bit 4 was previously listed as "0 = on, 1 = off".
The Aura video guide, the origin document for this register,
states under $FFFF828E and $FFFF8290 that the FS and EE
functions apply "if Video-Control Bit 4 = 1", so the bit is
active HIGH. The old polarity came from the Hohwiller listing
this page descends from.
Note that bit 4 is the one bit in this register the sources do
not agree on. The Aura guide's own bit map for $FFFF82C0 marks
bit 4 "??Unknown??" even though its FS and EE entries name it;
mikro's VIDEL document lists the register as
%_______8765_3210, with bit 4 left out; this wiki's own older
ST/STE/MSTe/TT/F030 listing shows BIT 8 7 6 5 . 3 2 1 0, also
leaving bit 4 out; and FALREG.TXT lists it as
%_______8765__21_, leaving out bits 4, 3 and 0. Every other
bit in this register is identical across all four. Treat bit 4
as "gates FS and EE, otherwise no known function", which is
what the four documents amount to between them.
Bit 4 is also the one bit here that CANNOT be checked against
code: neither Hatari nor EmuTOS models the FS and EE registers
at all, and no TOS mode table sets bit 4. It rests on the Aura
guide alone. Every other bit in this register is confirmed by
the values EmuTOS writes, listed above.
NAMING SETTLED: this register's name was queried because
Atari TOS 4.04 (tos3x bios/vsetmode.c) and EmuTOS use the
internal variable name video_control for $FFFF82C2 and
video_clock for $FFFF82C0. Those are only source code
variable names. Three independent documents name the
registers as this page does: Hatari (VCO "Video control" at
$82C0, VMD "Video mode" at $82C2), mikro's VIDEL document
("Video Control (VCO)" at $82C0, "Video Mode" at $82C2), and
the Aura video guide (VIDEO-CONTROL at $82C0, VIDEO-MODE at
$82C2).
Hatari is the strongest of the three here because the names
are in its code, not only in a comment: src/ioMemTabFalcon.c
dispatches $ff82c0 to VIDEL_VCO_WriteWord and $ff82c2 to
VIDEL_VMD_WriteWord, so the two registers carry those names
throughout the emulator's Falcon video implementation.
The naming history is worth recording, because older listings
circulate with the labels the other way round, and the two
older pages on this wiki do not agree with each other:
"Atari ST STe MSTe TT F030 Hardware Register Listing"
$FF82C0 Video Control (VCO)
$FF82C2 VDM (labelled VDM but with the description
text "Video Control" left over from the older
version, which is part of why this got muddled)
"Atari F030 and CT60 Hardware Register Listing V1.0"
$FF82C0 ??? - Video Clock (?)
$FF82C2 VCO - Video Control
A later revision of that same document exists, V2.1 dated
March 2009, and it still carries the old names, so a newer
date does not mean a corrected document. V2.1 also still has
$FF820F as "width in words-1", which is the Compendium error
corrected above, and the_env at $4BC rather than $4BE. It is
the same lineage one revision on, with low memory added, not
an independent check.
So the older ST/STE/MSTe/TT/F030 page already carried the
corrected names, while the F030/CT60 page still has the
original Dan Hollis lineage names. These pages follow the
corrected set, which is also what Hatari, mikro and the Aura
guide use.
The same mix-up happened independently on the Atari Forum Wiki
at temlib.org, a SEPARATE wiki from this one: its copy ended
up with both registers labelled VCO, which was spotted on
Atari-Forum in January 2024 (thread "Falcon Video Registers?")
and corrected there to "Video Mode (VDM)" for $FF82C2. That is
corroboration of which naming is right, nothing more; no
content has been taken from that wiki, and what else may
differ there has not been checked.
FALREG.TXT is the remaining outlier, calling $82C0
"Clock-Control (VCO)" and $82C2 "Resolution-Control" -
descriptive of the contents, but not the names anyone else
uses.
This address is not listed in the Atari Compendium, which
only documents $FFFF82C2.
Bit assignments confirmed against Atari TOS 4.04, EmuTOS
bios/videl.c, the Aura video guide and mikro's VIDEL
document.
$FFFF82C2.W|RW|VDL_VMD|Video Mode %________ ____xxID |F
| | | Pixclock:4,Divider:4(VGA)/16(STE)/4------00|| |F
| | | Pixclock:2,Divider:2(VGA)/16(STE)/2------01|| |F
| | | Pixclock:1,Divider:2(VGA)/16(STE)/1------10|| |F
| | | (unused)---------------------------------11|| |F
| | | Interlace 0:off,1:on-----------------------+| |F
| | | Double Scan 0:off,1:on----------------------+ |F
$FFFF82C2 VDL_VMD Video Mode
Bit 3 Quarter pixel width
Bit 2 Halve pixel width
Bit 1 Interlace mode
Bit 0 Line doubling
Bits 3-2 together give the pixel clock divider:
00 = full width (divider 4 on VGA, 16 on STE)
01 = half width (divider 2 on VGA, 16 on STE)
10 = quarter width(divider 2 on VGA, 16 on STE)
11 = not used, never written by TOS or EmuTOS
Line doubling and interlace are mutually exclusive: line
doubling is used on VGA to display a 200 line mode at 400
lines, interlace on television output.
NAMING SETTLED: TOS and EmuTOS source use the internal
variable name video_control when writing this register, which
raised the question of whether the labels here were swapped.
Hatari (VMD "Video mode"), mikro's VIDEL document ("Video
Mode") and the Aura video guide (VIDEO-MODE) all name this
register Video Mode and $FFFF82C0 Video Control, so the labels
on this page stand. This wiki's older ST/STE/MSTe/TT/F030
listing already used the mnemonic VDM here, though it kept the
stale description text "Video Control" alongside it, which is
part of how the confusion spread. See the fuller note on
$FFFF82C0.
The Aura video guide adds that bits 3-2 set both the video
system divider and the pixel cycle length, with the divider
fixed at 16 in STE compatibility mode whatever the monitor.
mikro's document gives the same encoding from the programmer's
side: 00 = 4 cycles per pixel, 01 = 2, 10 = 1, 11 not
available, and notes that the pixel cycle length is what you
actually choose when designing a custom resolution, with the
divider following from it.
Bit assignments from the Atari Compendium, confirmed against
Atari TOS 4.04, EmuTOS, the Aura video guide and mikro's
VIDEL document.
$FFFF8210-$FFFF82C2 VIDEL standard mode register values (reference tables) |F
VIDEL standard mode values, from the Aura video guide
(measured with an oscilloscope and confirmed against TOS).
Word values, hexadecimal, register offsets $FFFF82xx.
RGB and TV modes, 50 Hz:
MODE | 10| 60| 66| 82| 84| 86| 88| 8A| 8C| A2| A4| A6| A8| AA| AC| C0| C2
ST-LOW: 050 000 000 03E 032 009 23F 01C 034 271 265 02F 06F 1FF 26B 081 000
ST-MED: 050 010 000 03E 032 009 23F 01C 034 271 265 02F 06F 1FF 26B 081 004
ST-HIG: 028 0x0 400 1FE 199 050 3EF 0A0 1B2 270 265 02F 07E 20E 26B 181 006
2/80: 028 0x0 400 1FE 199 050 3EF 0A0 1B2 271 265 02F 07F 20F 26B 181 004
4/40: 028 010 000 03E 030 008 239 012 034 271 265 02F 07F 20F 26B 181 000
4/80: 050 010 000 03E 030 008 002 020 034 271 265 02F 07F 20F 26B 181 004
16/40: 050 0x0 000 0FE 0CB 027 00C 06D 0D8 271 265 02F 07F 20F 26B 181 000
16/80: 0A0 0x0 000 1FE 199 050 04D 0FE 1B2 271 265 02F 07F 20F 26B 181 004
256/40: 0A0 0x0 010 0FE 0CB 027 01C 07D 0D8 271 265 02F 07F 20F 26B 181 000
256/80: 140 0x0 010 1FE 199 050 05D 10E 1B2 271 265 02F 07F 20F 26B 181 004
TRU/40: 140 0x0 100 0FE 0CB 027 02E 08F 0D8 271 265 02F 07F 20F 26B 181 000
TRU/80: 280 0x0 100 1FE 199 050 071 122 1B2 271 265 02F 07F 20F 26B 181 004
Interlace: subtract 1 from $84, $A4 and $A6, add 2 to $C2.
ST-High values are already interlaced on RGB monitors.
VGA modes, 60 Hz (59.58 Hz on the Falcon), double line on:
MODE | 10| 60| 66| 82| 84| 86| 88| 8A| 8C| A2| A4| A6| A8| AA| AC| C0| C2
ST-LOW: 050 000 000 017 012 001 20E 00D 011 419 3AF 08F 08F 3AF 415 186 005
ST-MED: 050 010 000 017 012 001 20E 00D 011 419 3AF 08F 08F 3AF 415 186 009
ST-HIG: 028 0x0 400 0C6 08D 015 273 050 096 419 3AF 08F 08F 3AF 415 186 008
2/80: 028 0x0 400 0C6 08D 015 273 050 096 419 3FF 03F 03F 3FF 415 186 009
4/40: 028 010 000 017 012 001 20A 009 011 419 3FF 03F 03F 3FF 415 186 005
4/80: 050 010 000 017 012 001 20E 00D 011 419 3FF 03F 03F 3FF 415 186 009
16/40: 050 0x0 000 0C6 08D 015 28A 06B 096 419 3FF 03F 03F 3FF 415 186 005
16/80: 0A0 0x0 000 0C6 08D 015 2A3 07C 096 419 3FF 03F 03F 3FF 415 186 009
256/40: 0A0 0x0 010 0C6 08D 015 29A 07B 096 419 3FF 03F 03F 3FF 415 186 005
256/80: 140 0x0 010 0C6 08D 015 2AB 084 096 419 3FF 03F 03F 3FF 415 186 009
TRU/40: 140 0x0 100 0C6 08D 015 2AC 091 096 419 3FF 03F 03F 3FF 415 186 005
TRU/80: officially impossible on VGA.
Double line off: subtract 1 from $C2.
SM124 monochrome, 71 Hz:
MODE | 10| 60| 66| 82| 84| 86| 88| 8A| 8C| A2| A4| A6| A8| AA| AC| C0| C2
ST-HIG: 028 020 000 01A 000 000 20F 00C 014 3E9 000 000 043 363 3E7 080 008
Mode names are compatibility mode or colours/columns. 0x0 in
the $60 column means the ST shifter value is not significant
for that mode. The XBIOS may adjust some values after setting
a mode, so values read back from a running system can differ
slightly.
===========#==#=======#===============================================#=====
----------------------|TT Palette Registers |-----
===========#==#=======#===============================================#=====
$FFFF8400.W|RW|TT__PAL|TT Palette Register 000 |TT
$FFFF8400 to $FFFF85FE TT Palette Registers 0-255
Word registers, one per palette entry, 256 entries.
Layout: XXXX RRRR GGGG BBBB
Unlike the ST and STE registers at $FFFF8240, each nibble here
is in natural order, 3-2-1-0. There is no compatibility
reordering.
The ST compatible palette registers at $FFFF8240 map onto a
16 entry bank of this palette, selected by the ST Palette Bank
field in the low byte of $FFFF8262.
TT only.
Layout from the Atari Compendium.
...........|RW| - |....................... |TT
$FFFF85FE.W|RW| - |TT Palette Register 255 |TT
===========#==#=======#===============================================#=====
----------------------|VIDEL Palette Register |-----
===========#==#=======#===============================================#=====
$FFFF9800.L|RW|VDL_PAL|Palette Register 000 %RRRRRR__ GGGGGG__ |F
$FFFF9800 to $FFFF9BFC VIDEL Palette Registers 0-255
Longword registers, one per palette entry, 256 entries.
Layout, across the four bytes of each longword:
Byte 0 RRRRRR-- red, 6 bits
Byte 1 GGGGGG-- green, 6 bits
Byte 2 -------- unused
Byte 3 BBBBBB-- blue, 6 bits
Six bits per gun, so 262144 possible colours, of which 256 can
be displayed at once. The low two bits of each byte are unused
and read back as zero.
256 longword entries starting at $FFFF9800 occupy 1024 bytes,
so the last register is at $FFFF9BFC. An earlier version of
this page gave the end as $FFFF98FC, which is wrong: that
address is only 64 registers in.
Falcon only. Ignored entirely in truecolor mode, where the
pixel value goes straight to the DAC.
Layout from the Atari Compendium, extent confirmed against
FALREG.TXT which lists $FFFF9BFC as colour 256.
...........|RW| - |.................... ________ BBBBBB__ |F
$FFFF9BFC.L|RW| - |Palette Register 255 |F
===========#==#=======#===============================================#=====
----------------------|DMA, Blitter |-----
===========#==#=======#===============================================#=====
$FFFF8A00.W|RW|BLT_HTR|Halftone-RAM 00 |BLT
$FFFF8A00 to $FFFF8A1E Blitter Halftone RAM
Sixteen word registers, one per halftone line.
Which word is used for a given blit line is chosen by the
halftone line number in bits 3-0 of the control register at
$FFFF8A3C, or by source bits 0-3 when the SMUDGE bit is set.
The halftone word feeds the halftone operation at $FFFF8A3A,
which in turn feeds the logical operation at $FFFF8A3B.
Verified against Hatari src/blitter.c.
...........|RW| - |............... |BLT
$FFFF8A1E.W|RW| - |Halftone-RAM 15 |BLT
$FFFF8A20.W|RW|BLT_SXI|Source X increment %xxxxxxxx xxxxxxx_ |BLT
$FFFF8A20 / $FFFF8A22 / $FFFF8A2E / $FFFF8A30 Increments
$FFFF8A20 Source X increment
$FFFF8A22 Source Y increment
$FFFF8A2E Destination X increment
$FFFF8A30 Destination Y increment
All four are signed word values in BYTES, added to the current
address as the blit proceeds.
X increment is added after each word within a line. Y increment
is added at the end of each line, and is applied INSTEAD of the
final X increment, not in addition to it.
Bit 0 is ignored in all four: the blitter works in words, so
increments are always even. Negative values are allowed and are
how downward or right-to-left blits are done, which matters for
overlapping copies.
Verified against Hatari src/blitter.c.
$FFFF8A22.W|RW|BLT_SYI|Source Y increment %xxxxxxxx xxxxxxx_ |BLT
$FFFF8A24.L|RW|BLT_SRC|Source Address %xxxxxxxx xxxxxxxx xxxxxxx_ |BLT
$FFFF8A24 and $FFFF8A32 Source and Destination Address
$FFFF8A24 Source address, longword
$FFFF8A32 Destination address, longword
Both are 24 bit addresses held in a longword. The Atari
Compendium notes that bits 7-0 of the first byte are bits
23-16 of the address, which is the usual 68000 24 bit layout.
Bit 0 is ignored; addresses are always even.
These registers ADVANCE during a blit. After the operation
completes they hold the address just past the last word
transferred, not the value originally written, so they must be
reloaded for each new blit.
Verified against Hatari src/blitter.c.
$FFFF8A28.W|RW|BLT_EM1|Endmask 1 |BLT
$FFFF8A28 / $FFFF8A2A / $FFFF8A2C Endmasks
$FFFF8A28 Endmask 1, applied to the FIRST word of each line
$FFFF8A2A Endmask 2, applied to all MIDDLE words
$FFFF8A2C Endmask 3, applied to the LAST word of each line
Each is a 16 bit mask. Where a mask bit is 1 the result is
written; where it is 0 the destination is left alone.
Endmask 2 is normally $FFFF. Endmasks 1 and 3 are used to clip
a blit to a pixel boundary within the first and last words.
IMPORTANT: whenever a mask is not all ones, the blitter must
read the destination before writing it, turning the operation
into a read-modify-write and roughly halving throughput. Atari
documentation states NFSR can also trigger this; Hatari's
authors state that is wrong and only the mask does.
If a line is only one word wide, endmask 1 and endmask 3 are
ANDed together and endmask 2 is not used.
Verified against Hatari src/blitter.c.
$FFFF8A2A.W|RW|BLT_EM2|Endmask 2 |BLT
$FFFF8A2C.W|RW|BLT_EM3|Endmask 3 |BLT
$FFFF8A2E.W|RW|BLT_DXI|Destination X increment %xxxxxxxx xxxxxxx_ |BLT
$FFFF8A30.W|RW|BLT_DYI|Destination Y increment %xxxxxxxx xxxxxxx_ |BLT
$FFFF8A32.L|RW|BLT_DST|Destination Adr. %xxxxxxxx xxxxxxxx xxxxxxx_ |BLT
$FFFF8A36.W|RW|BLT_WPL|Words per Line in BOB (0:65536)|BLT
$FFFF8A36 and $FFFF8A38 X Count and Y Count
$FFFF8A36 X count, words per line
$FFFF8A38 Y count, number of lines
A value of 0 means 65536, not zero.
Y count is the register the blitter decrements as it works.
When it reaches 0 the transfer is complete and the busy bit in
$FFFF8A3C clears. Reading Y count during a blit shows how many
lines remain.
X count is reloaded at the start of each line.
The Atari Compendium calls these BLiTTER X Count and Y Count.
This listing calls them Words per Line and Lines per BOB;
they are the same registers.
Quirk: with x count = 1 and NFSR set, real STE and Falcon
hardware produce results that depend on the sign of the source
X increment.
Verified against Hatari src/blitter.c.
$FFFF8A38.W|RW|BLT_LPB|Lines per BOB (0:65536)|BLT
$FFFF8A3A.B|RW|BLT_HTO|Halftone Operation %______xx |BLT
| | | 0:set all Bits, 1:HTR, 2:SRC, 3:SRC & HTR |BLT
$FFFF8A3A BLT_HTO Halftone Operation, bits 1-0
0 all ones ($FFFF)
1 halftone RAM word
2 source word
3 source word AND halftone RAM word
HOP runs first, LOP second. The HOP result is what the logical
operation sees as its "source" input.
Verified against Hatari src/blitter.c.
$FFFF8A3B.B|RW|BLT_LGO|Logical Operation %____xxxx |BLT
| | | (!S AND !D)------------------------------+||| |BLT
| | | (!S AND D)-------------------------------+|| |BLT
| | | ( S AND !D)--------------------------------+| |BLT
| | | ( S AND D)---------------------------------+ |BLT
$FFFF8A3B BLT_LGO Logical Operation, bits 3-0
S = output of the halftone operation above
D = current destination word
$0 all zeros $8 NOT S AND NOT D (NOR)
$1 S AND D $9 NOT (S XOR D) (NXOR)
$2 S AND NOT D $A NOT D
$3 S $B S OR NOT D
$4 NOT S AND D $C NOT S
$5 D $D NOT S OR D
$6 S XOR D $E NOT (S AND D) (NAND)
$7 S OR D $F all ones
The register takes a value 0-15 selecting one of sixteen
operations, not just the four AND combinations shown above.
Verified against Hatari src/blitter.c.
$FFFF8A3C.B|RW|BLT_LNM|Line Number %BHS_xxxx |BLT
| | | Busy (1:start Blitter)---------------+|| |||| |BLT
| | | HOG (1:stop CPU when Busy)------------+| |||| |BLT
| | | SMUDGE (use sourcebits 0-3 as HTR num)-+ |||| |BLT
| | | Halftone-RAM number----------------------++++ |BLT
$FFFF8A3C BLT_LNM Control, %BHS_nnnn
Bit 7 BUSY write 1 to start the blitter. Reads 1 while a
transfer is in progress, cleared when y count
reaches 0.
Bit 6 HOG 0 = share the bus with the CPU, 1 = take the
bus for the whole transfer.
Bit 5 SMUDGE use source bits 0-3 as the halftone line
number instead of bits 3-0 of this register.
Bit 4 unused. Hardware masks this bit off on write.
Bits 3-0 halftone line number, 0-15.
In non-hog mode the blitter runs for 64 bus accesses, then
hands the bus to the CPU for 64. Writing 0 to bit 7 while the
CPU owns the bus pauses the blitter; writing 1 resumes it.
Pausing does not end the transfer, and busy still reads 1.
Quirk: in non-hog mode the blitter sometimes uses only 63 bus
accesses rather than 64, if the CPU makes a bus access during
the 4-cycle latency after the busy bit is set.
Verified against Hatari src/blitter.c.
$FFFF8A3D.B|RW|BLT_SKW|SKEW %FN__xxxx |BLT
| | | FXSR (Force eXtra Source Read)-------+| |||| |BLT
| | | NFSR (No Final Source Read)-----------+ |||| |BLT
| | | SKEW (shift)-----------------------------++++ |BLT
$FFFF8A3D BLT_SKW Skew, %FN__nnnn
Bit 7 FXSR Force eXtra Source Read
Bit 6 NFSR No Final Source Read
Bits 5-4 unused
Bits 3-0 skew, 0-15 pixels
Read-modify-write happens whenever an endmask is not all ones.
Atari's own documentation states NFSR can also trigger this;
Hatari's authors state that is wrong and only the mask does.
Quirk: with x count = 1 and NFSR set, real STE and Falcon
hardware produce results that depend on whether the source X
increment is positive or negative.
Verified against Hatari src/blitter.c.
===========#==#=======#===============================================#=====
----------------------|YM2149/AY-3-8910 Sound Chip |-----
===========#==#=======#===============================================#=====
$FFFF8800.B|R-|PSG_SEL|Read Data |
|-W| |Register Select |
$FFFF8800 PSG_SEL YM2149 register select (W) / read data (R)
Write a register number 0-15 here, then read or write the
value at $FFFF8802. The register set:
0 Channel A tone period, fine (8 bits)
1 Channel A tone period, coarse (4 bits)
2 Channel B tone period, fine
3 Channel B tone period, coarse
4 Channel C tone period, fine
5 Channel C tone period, coarse
6 Noise generator period (5 bits)
7 Mixer control and I/O port direction
8 Channel A amplitude
9 Channel B amplitude
10 Channel C amplitude
11 Envelope period, fine
12 Envelope period, coarse
13 Envelope shape (4 bits)
14 I/O Port A (floppy select, printer, RS232)
15 I/O Port B (Centronics data)
Register 7, mixer control:
Bit 7 Port B direction, 1 = output
Bit 6 Port A direction, 1 = output
Bit 5 Noise off, channel C
Bit 4 Noise off, channel B
Bit 3 Noise off, channel A
Bit 2 Tone off, channel C
Bit 1 Tone off, channel B
Bit 0 Tone off, channel A
Note the enable bits are inverted: a 0 enables that source.
Registers 8-10, amplitude:
Bits 3-0 fixed volume level 0-15
Bit 4 1 = use envelope instead of fixed volume
Register 13, envelope shape, bits 3-0:
Bit 3 CONT continue after first cycle
Bit 2 ATT attack, 1 = rising
Bit 1 ALT alternate direction each cycle
Bit 0 HOLD hold at final level
The resulting waveforms (F030/CT60 listing):
00xx \____________ (falls once, silence)
01xx /|___________ (rises once, silence)
1000 \|\|\|\|\|\|\ (repeating falls)
1001 \____________ (falls once, silence)
1010 \/\/\/\/\/\/\ (falling triangle)
1011 \|----------- (falls once, holds at maximum)
1100 /|/|/|/|/|/|/ (repeating rises)
1101 /------------ (rises once, holds at maximum)
1110 /\/\/\/\/\/\/ (rising triangle)
1111 /|___________ (rises once, silence)
YM2149 versus AY-3-8910
The YM2149 is Yamaha's licensed version of General Instrument's
earlier AY-3-8910. The two are register compatible: same 16
registers, same layout, same writes. There are no extra
registers on either part. The differences are internal:
- The envelope counter is 5 bit on the YM2149 (32 steps)
against 4 bit on the AY (16 steps), giving smoother volume
ramps. The envelope clock is divided by 8 rather than 16
to keep the cycle time the same across twice the steps.
The volume registers stay 4 bit on both, so there is
nothing extra to write.
- The YM reads registers back exactly as written. The AY
returns 0 for unused bits regardless of what was written.
- The YM has a 2V DC offset on all outputs; the AY has 0.2V
on a channel only while an envelope is active. This is why
the AY sounds louder.
Nothing in TOS depends on any of this. AY documentation applies
to the ST unchanged as far as the register interface goes.
Register names verified against Hatari src/includes/psg.h.
Mixer and port-direction bit masks verified against EmuTOS
bios/psg.h (PSG_PORTB_OUTPUT 0x80, PSG_PORTA_OUTPUT 0x40,
PSG_NOISE_MASK 0x38, PSG_TONE_MASK 0x07).
$FFFF8802.B|rW|PSG_DAT|Write Data |
| | | PSG Register 14 - Port A %RICDPBAS |
| | | Reset IDE 0:no,1:reset (slow down)---+||||||| |F
| | | Internal Speaker 0:on,1:off-----------+|||||| |F
| | | Centronics Strobe----------------------+||||| |
| | | Reset DSP 0:no,1:reset------------------+|||| |F
| | | Printer Select In------------------------+||| |
| | | Drive B select 0:on,1:off-----------------+|| |
| | | Drive A select 0:on,1:off------------------+| |
| | | Side select 0:side1,1:side0-----------------+ |
| | | PSG Register 15 - Port B %xxxxxxxx |
| | | Centronics Data Port-----------------++++++++ |
$FFFF8802 PSG_DAT YM2149 data register
Reads or writes the register previously selected at $FFFF8800.
PORT A, register 14, is the one that matters on the ST. It
carries the floppy drive and side select lines:
Bit 7 Reset IDE 0 = no, 1 = reset Falcon
Bit 6 Internal speaker 0 = on, 1 = off Falcon
Bit 5 Centronics strobe
Bit 4 Reset DSP 0 = no, 1 = reset Falcon
Bit 3 Printer select in
Bit 2 Drive B select 0 = selected
Bit 1 Drive A select 0 = selected
Bit 0 Side select 1 = side 0, 0 = side 1
Note the side select polarity: the bit SET selects side 0.
This is not a typo in the listing, it is how the hardware works.
Bits 3-7 carry RS232 RTS and DTR, the Centronics strobe and a
general purpose output. Always read the port, modify only the
bits you need and write it back. Writing a whole byte will
disturb the serial and printer lines.
Disable interrupts around any access to this register, or the
floppy VBL routine can deselect the drive underneath you. Set
flock at $0000043E to a non-zero value first.
Register layout verified against Hatari and EmuTOS.
$FFFF8804.B|RW| - |Add-on sound, not present on stock hardware |*
$FFFF8804 to $FFFF8808 Add-on sound registers
Not present on stock Atari hardware. $FFFF8800 and $FFFF8802 are
the YM2149 select and data registers; some add-on boards extend
the decoded range upward to $FFFF8808 for their own sound
hardware.
Known to be used by: SEC Booster.
IMPORTANT, FALCON: these addresses do NOT behave this way on a
Falcon. On that machine the PSG registers are fixed at $FFFF8800
and $FFFF8802 only, and every other address in the range is
masked out. Any write to the shadow registers $FFFF8804 to
$FFFF88FF will cause a BUS ERROR.
So an add-on using this range works on the ST and STE, where the
PSG address decoding is incomplete and the registers repeat
through the range, but the same code will bus error on a Falcon.
Anything writing here should check the machine type first.
Source: the Atari F030 and CT60 Hardware Register Listing, which
states the shadow registers are masked out and warns game and
demo coders specifically.
Extended bit information is not currently available for these
registers. The addresses are documented; what each bit does is
not.
$FFFF8806.B|RW| - |Add-on sound, not present on stock hardware |*
$FFFF8808.B|RW| - |Add-on sound, not present on stock hardware |*
===========#==#=======#===============================================#=====
----------------------|DMA, CODEC, ADC, DAC, DSP-Transmit/Receive |-----
===========#==#=======#===============================================#=====
$FFFF8900.W|RW|SND_DMA|Sound-DMA-Control %____RPRP F_EL__EL |F,STE
| | | Timer A after Record/Play-------++|| | || || |F
| | | MFP I/O 7 after Record/Play-------++ | || || |F
| | | Frame Registers 0:play,1:record------+ || || |F
| | | DMA record Enable/Loop-----------------++ || |F
| | | DMA play Enable/Loop-----------------------++ |F,STE
$FFFF8900 SND_DMA Sound DMA control
Bits 1-0, DMA play:
Bit 1 loop: 0 = play once, 1 = repeat from frame start
Bit 0 enable: 0 = stop, 1 = start playback
Bits 5-4, DMA record (Falcon only), same enable/loop pair.
Bit 7 frame register select: 0 = play frame, 1 = record
Bits 11-8 and 15-12 select what happens at the end of a
record or play frame: Timer A event and MFP I/O 7 event.
On the STE only bits 1-0 exist. The record path and the event
bits are Falcon additions.
Playback reads from the frame start address at $FFFF8903-07
and continues to the frame end at $FFFF890F-13. The current
position can be read from $FFFF8909-0D while playing.
The two halves are separate byte registers, $FFFF8900 (event
bits) and $FFFF8901 (frame select and enable/loop bits), and
the Aura and FALREG listings document them per byte. Bit 7 of
$FFFF8901 selects whether the frame address registers at
$FFFF8903-13 access the PLAY set (0) or the RECORD set (1):
they are two separate register sets appearing at the same
addresses.
Sample data interleaving in the frame buffer, per the
F030/CT60 listing:
8 bit stereo: LRLRLRLR
16 bit stereo: LLRRLLRR (one word per channel)
2 track 16 bit stereo: LLRRllrr (track 1 then track 2)
$FFFF8903.B|RW|SND_FSH|Frame Start Hi |F,STE
$FFFF8903 / $FFFF8905 / $FFFF8907 Frame Start address
Three byte registers holding a 24 bit address:
$FFFF8903 bits 23-16, high
$FFFF8905 bits 15-8, middle
$FFFF8907 bits 7-0, low
The address of the first byte of sample data. Bit 0 of the low
byte is ignored: the DMA fetches on even addresses only, so the
frame must start word aligned.
On the Falcon, bit 7 of $FFFF8900 selects whether these
registers address the play frame or the record frame.
Loaded into the DMA counter when playback starts, and reloaded
from here on each repeat if the loop bit is set.
$FFFF8905.B|RW|SND_FSM|Frame Start Mi |F,STE
$FFFF8907.B|RW|SND_FSL|Frame Start Lo |F,STE
$FFFF8909.B|RW|SND_FCH|Frame Count Hi |F,STE
$FFFF8909 / $FFFF890B / $FFFF890D Frame Count
Three byte registers holding the current 24 bit DMA address:
$FFFF8909 bits 23-16, high
$FFFF890B bits 15-8, middle
$FFFF890D bits 7-0, low
Read these while playing to find how far through the sample the
DMA has reached. The value advances from the frame start toward
the frame end.
Reading the three bytes is not atomic, so the counter can move
between reads. Read high, middle then low and re-read if the
high byte changed, or read with interrupts disabled.
Hatari tracks this internally as frameCounterAddr, refilling
its FIFO from that address as playback advances.
$FFFF890B.B|RW|SND_FCM|Frame Count Mi |F,STE
$FFFF890D.B|RW|SND_FCL|Frame Count Lo |F,STE
$FFFF890F.B|RW|SND_FEH|Frame End Hi |F,STE
$FFFF890F / $FFFF8911 / $FFFF8913 Frame End address
Three byte registers holding a 24 bit address:
$FFFF890F bits 23-16, high
$FFFF8911 bits 15-8, middle
$FFFF8913 bits 7-0, low
The address one past the last byte of sample data. Playback
stops when the frame counter reaches this value.
If the loop bit in $FFFF8900 is set, the counter reloads from
the frame start registers and playback continues. If not,
playback stops and the enable bit clears.
As with the start address, bit 0 of the low byte is ignored.
$FFFF8911.B|RW|SND_FEM|Frame End Mi |F,STE
$FFFF8913.B|RW|SND_FEL|Frame End Lo |F,STE
$FFFF8920.W|RW|SND_SMC|Sound Mode Control %__SS__PP MB____FF |F,STE
| | | DAC to track %SS--------------++ || || || |F
| | | Play %PP+1 tracks-----------------++ || || |F
| | | 0:Stereo,1:Mono----------------------+| || |F
| | | 0:8bit,1:16bit------------------------+ || |F
| | | Falcon:(unused)--STE: 6258 Hz--------------00 |F,STE
| | | Falcon:12292 Hz--STE:12517 Hz--------------01 |F,STE
| | | Falcon:19668 Hz--STE:25033 Hz--------------10 |F,STE
| | | Falcon:49170 Hz--STE:50066 Hz--------------11 |F,STE
$FFFF8920 SND_SMC Sound mode control
This is a word register, but the two halves are separate:
$FFFF8920 is the track control byte and $FFFF8921 the mode
control byte. Hatari and EmuTOS treat them separately.
$FFFF8921, mode control:
Bit 7 0 = stereo, 1 = mono
Bit 6 0 = 8 bit, 1 = 16 bit
Bits 1-0 sample rate:
Falcon: 00 unused, 01 12292Hz, 10 19668Hz, 11 49170Hz
STE: 00 6258Hz, 01 12517Hz, 10 25033Hz, 11 50066Hz
$FFFF8920, track control (Falcon only):
Bits 5-4 which track the DAC monitors
Bits 1-0 number of tracks to play, minus one
The mono/stereo bit was previously printed on this page with
both states as 0. Corrected from EmuTOS bios/dmasound.c, which
does modectrl |= 0x80 with the comment 'Select mono', and from
Hatari src/falcon/crossbar.c, which reads isStereo as the
inverse of bit 7 and is16Bits from bit 6.
$FFFF8922.B|RW| - |Microwire Data Register |STE
$FFFF8922 Microwire Data Register (with $FFFF8924 Mask)
The STE and TT use a Microwire serial link to control an
LMC1992 audio processor, which handles master volume, left and
right balance, bass, treble, and the mix between DMA sound and
the YM2149 output. Write the command word here; $FFFF8924 is
the mask that clocks it out.
Command format: 10 CCC DDD DDD
10 chipset address, always this value
CCC command
DDD DDD data
Commands:
000 XXX XDD Mixing
00 DMA sound only
01 DMA sound + YM2149, full frequency range
10 DMA sound + YM2149 through a low pass
filter -> gives DMA sound only
11 input 3, not connected -> DMA only
001 XXD DDD Bass 0 000 = -12dB, 0 110 = 0dB,
1 100 = +12dB, 2dB steps
010 XXD DDD Treble same scale as bass
011 DDD DDD Master volume
000 000 = -80dB, 010 100 = -40dB,
101 XXX = 0dB, 2dB steps
100 XDD DDD Right channel volume
00 000 = -40dB, 01 010 = -20dB,
10 1XX = 0dB
101 XDD DDD Left channel volume, same scale as right
Any other command value is undefined.
THE STE MIXING BUG
Note the arrows on mixing modes 10 and 11 above. Mode 10 is
supposed to mix the YM2149 in through a low pass filter, which
would attenuate it relative to the DMA sound. On the STE it
does not work: input 2 is not wired, so selecting it drops the
YM output entirely and you get DMA sound only. Mode 11 selects
input 3, which is not connected at all.
The practical effect is that the STE gives you only two useful
choices, DMA alone or DMA plus YM at full volume, with no way
to balance the two in software. In games such as Xenon the
YM sound ends up considerably louder than the DMA samples.
A hardware fix for this was worked out by P. Putnik and is
documented here, along with the rest of the STE DAC work:
exxosforum.co.uk STE DAC fix, LMC section
Command set verified against Hatari src/dmaSnd.c, which
documents the same non-functional mixing modes and carries the
full LMC1992 volume tables.
$FFFF8924.B|RW| - |Microwire Mask Register |STE
$FFFF8924 Microwire Mask Register
Works with the data register at $FFFF8922. The mask marks which
bits of the data word are the command; the hardware shifts both
registers out together over the Microwire serial link to the
LMC1992.
The usual value is $07FF, an 11 bit command window matching the
10 CCC DDD DDD command format described at $FFFF8922.
Behaviour during a transfer, per Hatari src/dmaSnd.c:
- The data register shifts left one bit per step.
- The mask register ROTATES left rather than shifting, so
after a complete transfer it reads back as it started.
- One shift takes 8 CPU cycles, so a full 16 step transfer
takes 128 cycles.
Because the mask rotates back to its original value, reading it
is not a reliable way to tell whether a transfer has finished.
The data register shifting to zero is the usable indicator.
Verified against Hatari src/dmaSnd.c.
===========#==#=======#===============================================#=====
----------------------|Falcon Sound Matrix and CODEC |-----
===========#==#=======#===============================================#=====
$FFFF8930.W|RW|SND_CBO|Crossbar Output Select Controller |F
$FFFF8930 SND_CBO Crossbar source (input) select
Word register, four 4-bit fields, one per source device.
Bit layout from Hatari src/falcon/crossbar.c:
Bits 15-12 A/D Converter
Bits 13-12 clock: 00 = 25.175MHz, 01 = external,
10 = 32MHz (do not use)
Bits 11-8 External Input
Bit 11 0 = DSP IN, 1 = all others
Bits 10-9 clock, as above
Bit 8 0 = handshake on, 1 = handshake off
Bits 7-4 DSP transmit
Bit 7 0 = tristate and disconnect DSP (external SSI
use only), 1 = connect DSP to multiplexer
Bits 6-5 clock, as above
Bit 4 0 = handshake on, 1 = handshake off
Bits 3-0 DMA playback
Bit 3 0 = handshaking on, destination DSP receive
1 = destination is not DSP receive
Bits 2-1 clock, as above
Bit 0 0 = handshake on, 1 = handshake off
Verified against Hatari src/falcon/crossbar.c.
$FFFF8932.W|RW|SND_CBI|Crossbar Input Select Controller |F
$FFFF8932 SND_CBI Crossbar destination (output) select
Word register, four 4-bit fields, one per destination.
Bit layout from Hatari src/falcon/crossbar.c:
In each field the two-bit source selector means:
00 = DMA output 01 = DSP output
10 = External input 11 = ADC input
Bits 15-12 D/A Converter
Bits 13-12 source, as above
Bits 11-8 External output
Bit 11 0 = DSP out, 1 = all others
Bits 10-9 source, as above
Bit 8 0 = handshake on, 1 = handshake off
Bits 7-4 DSP receive
Bit 7 0 = tristate and disconnect DSP (external SSI
use only), 1 = connect DSP to multiplexer
Bits 6-5 source, as above
Bit 4 0 = handshake on, 1 = handshake off
Bits 3-0 DMA record
Bit 3 0 = handshaking on, destination DSP transmit
1 = all
Bits 2-1 source, as above
Bit 0 0 = handshake on, 1 = handshake off
Verified against Hatari src/falcon/crossbar.c.
$FFFF8934.B|RW|SND_FDE|Frequency Divider, External Sync |F
$FFFF8934 SND_FDE Frequency Divider, External Sync
Bits 3-0 divider for the external clock:
0000 = STe compatible mode
0001 to 1111 = divide the external clock by 256, then by
this value
(value table from the F030/CT60 listing)
Clock divider used when the crossbar is set to take an external
clock. Hatari accepts writes here but the value only matters
when an external clock source is actually connected, which on
a standard Falcon it is not.
See $FFFF8930 and $FFFF8932 for the clock and source selection
that decides whether this register is used at all.
$FFFF8935.B|RW|SND_FDI|Frequency Divider, Internal Sync |F
$FFFF8935 SND_FDI Frequency Divider, Internal Sync
Bits 3-0 clock divider, 0-15
Bits 7-4 unused
Divides the selected internal clock (25.175MHz or 32MHz, chosen
per device in $FFFF8930) down to the sample rate. Together with
the clock choice and the number of active tracks this sets the
actual playback frequency.
Sample rates with the standard 25.175MHz clock, per the
F030/CT60 listing (* = invalid for the CODEC):
0000 STe compatible mode 1000 10927Hz *
0001 49170Hz 1001 9834Hz
0010 32780Hz 1010 8940Hz *
0011 24585Hz 1011 8195Hz
0100 19668Hz 1100 7565Hz *
0101 16390Hz 1101 7024Hz *
0110 14049Hz * 1110 6556Hz *
0111 12292Hz 1111 6146Hz *
Verified against Hatari src/falcon/crossbar.c, which masks the
written value with 0x0F and recalculates the clock cycles.
$FFFF8936.B|RW|SND_RTS|Record Tracks Select |F
$FFFF8936 SND_RTS Record Tracks Select
Bits 1-0 number of tracks to record
0 = 1 track
1 = 2 tracks
2 = 3 tracks
3 = 4 tracks
Bits 7-2 unused
Verified against Hatari src/falcon/crossbar.c.
$FFFF8937.B|RW|SND_CIS|CODEC Input Source |F
$FFFF8937 SND_CIS CODEC Input Source
Bit 1 source = multiplexer (the crossbar)
Bit 0 source = A/D converter
Bits 7-2 unused
Selects what feeds the CODEC's 16 bit adder. Both bits can be
set, which sums the two sources.
Verified against Hatari src/falcon/crossbar.c.
$FFFF8938.B|RW|SND_CAD|CODEC ADC Input |F
$FFFF8938 SND_CAD CODEC ADC Input
Bit 1 Left channel 0 = microphone, 1 = PSG (YM2149)
Bit 0 Right channel 0 = microphone, 1 = PSG (YM2149)
Bits 7-2 unused
This is how the Falcon routes the YM2149 output into the ADC so
it can be mixed digitally, rather than through the analogue
path the STE uses.
Verified against Hatari src/falcon/crossbar.c.
$FFFF8939.B|RW|SND_GAI|Gain settings %LLLLRRRR |F
$FFFF8939 SND_GAI Gain settings, input amplifier
Bits 7-4 Left channel gain, 0-15
Bits 3-0 Right channel gain, 0-15
Amplification for the ADC input, in +1.5dB steps per increment.
Verified against Hatari src/falcon/crossbar.c, which indexes
its ADC volume table with (value >> 4) for left and (value & 15)
for right.
$FFFF893A.W|RW|SND_ATT|Attenuation settings %LLLLRRRR |F
$FFFF893A SND_ATT Attenuation settings, output reduction
Word register:
Bits 11-8 Left channel attenuation, 0-15
Bits 7-4 Right channel attenuation, 0-15
Bits 15-12 and 3-0 unused
Reduction for the DAC output, in -1.5dB steps per increment.
Note the field positions: this is not the same layout as the
gain register at $FFFF8939. Hatari reads left from
(value >> 8) & 0x0F and right from (value >> 4) & 0x0F.
Verified against Hatari src/falcon/crossbar.c.
$FFFF893C.W|R-|SND_CST|CODEC Status |F
$FFFF893C SND_CST CODEC Status
Bit 1 Left channel overflow
Bit 0 Right channel overflow
Bits 15-2 unused
Overflow flags set when the CODEC input clips. Read only in
practice, though Hatari accepts writes.
This register is absent from the Atari Compendium listing and
was found in Hatari and EmuTOS. EmuTOS bios/dmasound.c sets it
to $003F on reset.
FALREG.TXT additionally records bits 7-4 of the low byte
($FFFF893D) as present but of unknown function, marked "?"
even by that document's authors. The Aura FALC2.TXT listing
also notes bytes $FFFF893E/$FFFF893F reading back non zero
($81/$00) but never accessed by the XBIOS; Hatari maps
$FFFF893E as a no-bus-error filler, not a register.
Verified against Hatari src/falcon/crossbar.c.
$FFFF8940.W|RW|SND_GPD|GPIO Data Direction |F
$FFFF8940 SND_GPD GPIO Data Direction
Bits 2-0 direction for the three general purpose I/O pins
0 = input, 1 = output
Bits 15-3 unused
Three GPIO pins are brought out on the Falcon's DSP connector.
They are not used by TOS beyond initialisation and are free
for expansion hardware.
VERIFIED as a word register at the even address: Atari TOS
3.06 startup.S does move.w #7,($FFFF8940).w, with the comment
that the Sparrow GPIO pins have no pull resistors, setting
all three pins to output (%111, the default value FALREG.TXT
records). Hatari models words at $FFFF8940 and $FFFF8942.
Some listings (FALREG.TXT, the F030/CT60 page) give these
registers at the ODD addresses $FFFF8941 and $FFFF8943. That
is because the three implemented bits sit in the low byte of
each word; the registers themselves are the even words
documented here. Do not relocate them.
$FFFF8942.W|RW|SND_GPI|GPIO Data |F
$FFFF8942 SND_GPI GPIO Data
Bits 2-0 data for the three general purpose I/O pins
Bits 15-3 unused
Reads the pin state for pins set as inputs at $FFFF8940, and
drives the pin for those set as outputs.
UNVERIFIED: address confirmed by the Atari Compendium, but the
bit detail here is not corroborated by Hatari or EmuTOS.
===========#==#=======#===============================================#=====
----------------------|Realtime Clock Chip, Non Volatile Memory |-----
===========#==#=======#===============================================#=====
$FFFF8961.B|RW|NVM_CTL|Register Select |TT,F
$FFFF8961 and $FFFF8963 Real Time Clock / NVRAM
$FFFF8961 Address register: write the location to access
$FFFF8963 Data register: read or write that location
TT and Falcon. The NVRAM holds 50 bytes of user settings
(language, keyboard, boot preferences) plus the real time
clock registers, in a battery backed MC146818 compatible part.
Access is indirect: write an address to $FFFF8961, then read
or write $FFFF8963. EmuTOS bios/nvram.c wraps this in the
NVMaccess XBIOS call rather than exposing the registers.
On the Falcon the fitted part is a DS1287 (MC146818
compatible). Internal register map, per the F030/CT60
listing:
0 current second 1 alarm second
2 current minute 3 alarm minute
4 current hour 5 alarm hour
6 day of week (1 = Sunday)
7 day of month 8 month
9 year (two digits)
A bit 7: 1 = time update in progress, do not read the
time and date registers while set
B bit 7: 1 = write protect time and date
bit 6: 1 = enable alarm interrupt
bit 5: 1 = interrupt after time updated
bit 2: 1 = binary format, 0 = BCD
bit 1: 1 = 24 hour format, 0 = 12 hour
bit 0: 1 = summer time, 0 = winter time
C bit 6: ? bit 5: 1 = alarm is ringing
bit 4: 1 = date has updated
read this register on interrupt to find the source
D bit 7: 1 = BATTERY DEAD, clock contents invalid
From the Atari Compendium; DS1287 register map from the
F030/CT60 listing, matching the standard MC146818 layout.
$FFFF8963.B|RW|NVM_DAT|Data of selected Register |TT,F
$FFFF8964.B|RW| - |Add-on control, not present on stock hardware |*
$FFFF8964, $FFFF896A, $FFFF896C, $FFFF8970 Add-on control
Not present on stock Atari hardware. These addresses sit in
the gap above the TT and Falcon real time clock registers and
below the reserved area at $FFFF8A00.
Known to be used by:
$FFFF8964 FLASHY CLOCK
$FFFF896A SEC BOOSTER
$FFFF896C FLASHY CLOCK
$FFFF8970 FLASHY CLOCK
FLASHY CLOCK also uses $FFFF89F0 and $FFFF89F2 for its real
time clock, and $FFFFFFFF as a control register.
Extended bit information is not currently available for these
registers. The addresses are documented; what each bit does is
not.
No conflict found: neither EmuTOS nor Hatari references any
address in the $FFFF8964 to $FFFF8970 range.
$FFFF896A.B|RW| - |Add-on control, not present on stock hardware |*
$FFFF896C.B|RW| - |Add-on control, not present on stock hardware |*
$FFFF8970.B|RW| - |Add-on control, not present on stock hardware |*
$FFFF89F0.B|RW| - |Add-on real time clock |*
$FFFF89F0 and $FFFF89F2 Add-on real time clock
Not present on stock Atari hardware.
Known to be used by: FLASHY CLOCK.
Extended bit information is not currently available for these
registers.
No conflict found: neither EmuTOS nor Hatari references either
address.
$FFFF89F2.B|RW| - |Add-on real time clock |*
===========#==#=======#===============================================#=====
----------------------|DMA SCC |-----
===========#==#=======#===============================================#=====
$FFFF8C01.B|RW|SCC_DA0|DMA Address Pointer (Highest byte) |TT
$FFFF8C03.B|RW|SCC_DA1|DMA Address Pointer (High byte) |TT
$FFFF8C05.B|RW|SCC_DA2|DMA Address Pointer (Low byte) |TT
$FFFF8C07.B|RW|SCC_DA3|DMA Address Pointer (Lowest byte) |TT
$FFFF8C09.B|RW|SCC_BC0|DMA Byte Counter (Highest byte) |TT
$FFFF8C0B.B|RW|SCC_BC1|DMA Byte Counter (High byte) |TT
$FFFF8C0D.B|RW|SCC_BC2|DMA Byte Counter (Low byte) |TT
$FFFF8C0F.B|RW|SCC_BC3|DMA Byte Counter (Lowest byte) |TT
$FFFF8C10.W|Rw|SCC_RD0|Rest data (High Word) |TT
$FFFF8C12.W|Rw|SCC_RD1|Rest data (Low Word) |TT
$FFFF8C14.W|Rw|SCC_CTL|DMA SCC 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
| | | 0:DMA read,1:DMA write----------------------+ |TT
===========#==#=======#===============================================#=====
----------------------|SCC Y8530 - Serial Communication Controller |-----
===========#==#=======#===============================================#=====
$FFFF8C81.B|RW|SCA_CTL|Channel A Control (select/read/write Register) |SCC
$FFFF8C81 to $FFFF8C87 SCC 8530 Serial Controller
$FFFF8C81 Channel A control
$FFFF8C83 Channel A data
$FFFF8C85 Channel B control
$FFFF8C87 Channel B data
The 8530 has far more internal registers than the four
addresses above suggest. Access is indirect: write a register
number to the control address, then read or write the control
address again to reach that register. Register 0 is reached
directly without a preceding select.
The data addresses map to internal register 8.
INTERNAL REGISTER MAP (Z8530 datasheet, implemented in full by
Hatari src/scc.c):
Write registers:
WR0 register select / command (CRC reset, reset ext/status
interrupts, error reset, and so on)
WR1 interrupt and data transfer mode enables
WR2 interrupt vector base (one per chip, shared A and B)
WR3 receiver parameters: bit 0 RX enable, bits 7-6 bits
per character (00=5, 10=6, 01=7, 11=8)
WR4 bit 0 parity enable, bit 1 even parity, bits 3-2 stop
bits (00=sync, 01=1, 10=1.5, 11=2), bits 7-6 clock
mode (x1, x16, x32, x64)
WR5 transmitter parameters: bit 1 RTS, bit 3 TX enable,
bit 4 send break, bits 6-5 TX bits per character,
bit 7 DTR
WR6 sync character / SDLC address
WR7 sync character / SDLC flag (WR7' extensions reachable
via WR15 bit 0 on the 85C30)
WR8 transmit data buffer (same as the data address)
WR9 master interrupt control and chip reset: bit 0 vector
includes status, bit 1 no vector, bit 3 master int
enable, bits 7-6 reset commands
WR10 misc TX/RX control (encoding: NRZ, NRZI, FM)
WR11 clock source selection for RX/TX
WR12 baud rate generator time constant, low byte
WR13 baud rate generator time constant, high byte
WR14 misc control: bit 0 BRG enable, bit 1 BRG source,
bits 7-5 DPLL commands
WR15 external/status interrupt enables: bit 1 zero count,
bit 3 DCD, bit 4 sync/hunt, bit 5 CTS, bit 6 TX
underrun/EOM, bit 7 break/abort
Read registers:
RR0 status: bit 0 RX character available, bit 2 TX buffer
empty, bit 3 DCD, bit 4 sync/hunt, bit 5 CTS, bit 6
TX underrun/EOM, bit 7 break/abort
RR1 special receive condition status (parity, overrun,
framing errors)
RR2 interrupt vector (channel B read returns it modified
by the interrupt source)
RR3 interrupt pending bits (channel A only)
RR8 receive data buffer (same as the data address)
RR10 loop/clock status
RR12 BRG time constant low readback
RR13 BRG time constant high readback
RR15 external/status interrupt enable readback
Baud rate: on the Atari the SCC PCLK is 8.053976MHz (the
Falcon and TT feed the same master clock), and channel B's
receive/transmit clock TRxCB can instead be driven from MFP
Timer C, which is why that timer is reserved on the TT. Baud
= PCLK / (2 x (time constant + 2) x clock mode divisor) when
the BRG runs from PCLK.
Port assignment on the Atari machines: channel A drives the
LAN connector or Serial 2 (selected by YM port A bit 7 on the
TT and Falcon), channel B drives the modem/serial port.
The SCC generates interrupts through the vector table at
$00000180 to $000001BC. That table is SPARSE: only every other
longword is a real vector, alternating with an unused slot.
See the System variables and low RAM page.
Register map from the Zilog Z8530/85C30 datasheet; register
behaviour cross-checked against the define tables and handlers
in Hatari src/scc.c, which implements WR0-WR15 and RR0-RR15
for the Atari machines.
TT, Mega STE and Falcon.
$FFFF8C83.B|RW|SCA_DAT|Channel A Data (read/write Register 8) |SCC
$FFFF8C85.B|RW|SCB_CTL|Channel B Control (select/read/write Register) |SCC
$FFFF8C87.B|RW|SCB_DAT|Channel B Data (read/write Register 8) |SCC
===========#==#=======#===============================================#=====
----------------------|VME Bus |-----
===========#==#=======#===============================================#=====
$FFFF8E01.B|RW|VME_MR0|VME Mask Register 0 %EMSV_HS_ |TT,ME
| | | Enable bits for the same interrupt sources as |TT,ME
| | | VME_SR0 below, same bit positions, 1=enabled. |TT,ME
| | | Cleared to $00 on reset; TOS sets $14 (HBL + |TT,ME
| | | VBL). Per Hatari src/scu_vme.c. |TT,ME
$FFFF8E03.B|RW|VME_SR0|VME Status Register 0 %EMSV_HS_ |TT,ME
| | | Error 0:no,1:yes---------------------+||| || |TT,ME
| | | MFP-----------------------------------+|| || |TT,ME
| | | SCC------------------------------------+| || |TT,ME
| | | VBL-------------------------------------+ || |TT,ME
| | | HBL---------------------------------------+| |TT,ME
| | | Software Interrupt-------------------------+ |TT,ME
$FFFF8E05.B|RW|VME_IN0|Force Interrupt on Level 1 %_______F |TT,ME
$FFFF8E07.B|RW|VME_IN1|Force Interrupt on Level 3 %_______F |TT,ME
$FFFF8E09.B|RW|SCU_GP1|SCU General Purpose Register 1 |TT,ME
| | | Verified: EmuTOS bios/machine.h (SCU_GPR1 |
| | | 0xffff8e09) and Atari TOS 3.06 bios/startup.S,|
| | | which uses bit 0 as a memory-config-valid |
| | | flag. |
$FFFF8E0B.B|RW|SCU_GP2|SCU General Purpose Register 2 |TT,ME
| | | UNVERIFIED: Atari Compendium only. GPR1 next |
| | | door is confirmed twice, so this is very |
| | | likely real, but no source touches it. |
| | | Both GP registers are plain byte latches with |
| | | no assigned bits: cleared on cold boot only, |
| | | they keep their contents over a warm boot |
| | | (Hatari src/scu_vme.c). |
$FFFF8E0D.B|RW|VME_MR1|VME Mask Register 1 %7654321_ |TT,ME
| | | Enable bits for VME bus interrupts 1-7, same |TT,ME
| | | positions as VME_SR1 below, 1=enabled. Bit 0 |TT,ME
| | | unused. Cleared to $00 on reset; TOS sets $60 |TT,ME
| | | (IRQ6=MFP + IRQ5=SCC). Per Hatari |TT,ME
| | | src/scu_vme.c. |TT,ME
$FFFF8E0F.B|RW|VME_SR1|VME Status Register 1 %7654321_ |TT,ME
| | | VME Interrupt 1-7 0:on,1:off---------+++++++ |
===========#==#=======#===============================================#=====
$FFFF8E21.B|RW|M_E_CAC|Mega STe Cache/Processor Control %______SC |ME
| | | cpu Speed 0:8MHz,1:16MHz-------------------+| |ME
| | | Cache 0:disabled,1:enabled------------------+ |ME
| | | $FF 16MHz with cache |ME
| | | $FE 16MHz, cache off |ME
| | | $F4 8MHz |ME
| | | The cache only works at 16MHz: setting bit 0 |
| | | with bit 1 clear reads back with bit 0 forced |
| | | to 0 (write $FD, read $FC). Per Hatari |
| | | src/ioMemTabSTE.c. |
| | | CONFLICT: this wiki's older ST/STe/MSTe/TT/ |
| | | F030 listing describes bit 0 as CPU speed and |
| | | bits 15-1 as "cache enable lines, set all to |
| | | 1 to enable", the reverse assignment. Hatari |
| | | and the value table above are followed here. |
| | | CORRECTED: was listed at $FFFF8E0F, which |
| | | belongs to VME_SR1. Verified at $FFFF8E21 by |
| | | Atari TOS 3.06 bios/startup.S: ori.b |
| | | #3,($ffff8e21).w with the comment "turn on |
| | | 16MHz and cache". Value table from the Atari |
| | | Compendium. |
===========#==#=======#===============================================#=====
----------------------|GAMECART - undocumented STE register |-----
===========#==#=======#===============================================#=====
$FFFF9000.W|RW|GAMECRT|Cartridge remap control %______0G ________ |STE
| | | always 0--------------------------+| |,ME
| | | Gamecart 0:normal,1:remapped-------+ |,TT
| | | Access size disputed, see detail below |
$FFFF9000 GAMECART Cartridge address remap
Bit 9 always reads 0
Bit 8 0 = cartridge port at the normal addresses
1 = cartridge port remapped
Bit 8 of the word is bit 0 of the byte at $FFFF9000. It drives
a signal named GAMECART inside the STE's combined GLUE/MMU
(GST MCU) chip, from a flip flop, so it latches. Setting it
moves the cartridge port chip selects:
GAMECART = 0 (normal)
$00FA0000-$00FAFFFF /ROM4
$00FB0000-$00FBFFFF /ROM3
GAMECART = 1 (remapped)
$00D80000-$00DBFFFF /ROM4
$00DC0000-$00DFFFFF /ROM3
and $00FA0000-$00FBFFFF then BUS ERRORS
CONFLICT, access size: Christian Zietz describes this as a
BYTE access, while Atari's own TT_MCU_rev_B document describes
it as a WORD access. Hatari maps the address as a word. Both
descriptions put the active bit in the same place, so the
practical difference is only whether a byte write at
$FFFF9000 is honoured. Unresolved; a word write is the safer
choice since every source agrees it works.
MACHINES: the GST MCU family covers the STE, Mega STE and TT,
so the register is listed for all three. It is not present on
the ST, and the Falcon uses different chips again. Which
machines actually respond has not been tested.
ORIGIN: found by Keli Hlodversson by tracing Atari's original
hand-drawn STE ASIC schematics, which were recovered and
converted to PDF by Christian Zietz. Write-up at
https://www.keli.dk/old-asic/ . The remapped address ranges
above and the bus error behaviour are Christian Zietz's
reading of the same schematics.
WHY IT EXISTS, probably: an internal Atari memo reproduced in
"We Love Atari (Volume Two)", page 154, describes a never
released "STE Game Machine" codenamed Project Robin, built
around the GST MCU with 512KB of cartridge ROM. The remapped
range is exactly 512KB. Note that the dating is not settled:
Atari is reported to have been considering an ST based games
machine as early as 1986, which would predate the STE.
PRACTICAL LIMIT: the cartridge port only brings out address
lines up to A15, so only 2 x 64KB can be addressed directly
however the selects are mapped. Using the full 512KB would
need a hardware modification or bank switching on the
cartridge itself. No cartridge is known to do this.
VERIFICATION STATUS - read this before relying on it:
Hatari knows the ADDRESS but not the function. Its STE
and TT tables map $ff9000 as a word with void
read and void write, commented "No bus error
here", and $ff9000-$ff91ff is in its no-bus-error
list for Falcon STE compatible mode. So the
address genuinely responds rather than faulting,
which is consistent, but GAMECART itself is NOT
emulated.
EmuTOS no reference at all.
Atari TOS no reference in any version. Christian Zietz
states plainly that no TOS accesses this
register, so a cartridge cannot boot from the
remapped range.
Compendium not listed.
TT_MCU_rev_B Atari's own MCU document does cover it, and
is the source of the word access claim.
So this rests on schematic archaeology plus one Atari
document, with the address confirmed as decoded by Hatari and
nothing else. It has not been demonstrated on real hardware in
any account found so far. Treat the ranges as credible but
untested, and do not assume an emulator will reproduce them.
RELATED DECODES found in the same schematics, on page 1 of
this memory map: the STE's /ROM0 and /ROM1 selects, unused on
production machines, decode $00E80000-$00EBFFFF and
$00E40000-$00E7FFFF; /ROM5 and /ROM6 decode
$00D00000-$00D7FFFF.
===========#==#=======#===============================================#=====
----------------------|Paddle Ports |-----
===========#==#=======#===============================================#=====
$FFFF9200.W|RW|PAD_BUT|Paddle/Joy Buttons %xxxxxxxx ____3210 |STE,F
| | | Switches--------------------++++++++ |,TT
| | | On Falcon at U47: MSB on the right, closed=0 |
$FFFF9200 PAD_BUT Joypad fire buttons / DIP switches
Low nibble, read: fire buttons, per the F030/CT60 listing:
Bit 3 Option / F3
Bit 2 F2
Bit 1 F1
Bit 0 Pause / F0
High byte: on the Mega STE and Falcon this byte also returns
the state of the 8 configuration DIP switches on the
motherboard (Falcon: U47, most significant bit on the right,
closed = 0). Hatari src/joy.c implements this.
ACCESS QUIRK: on the STE and Mega STE this register can only
be read as a WORD at $FFFF9200; a byte access at $FFFF9200
does not work, though $FFFF9201 can be read as a byte. Hatari
src/joy.c implements the restriction.
$FFFF9202.W|RW|PAD_MOV|Paddle/Joy Move (R) / Read Mask (W) |STE,F
$FFFF9202 PAD_MOV Joystick inputs (read) / read mask (write)
Write: the read mask; a 0 bit selects the corresponding pin
group for reading. Read: the joystick input pins, per the
F030/CT60 listing:
Bit 15 Controller 1 pin 14 Bit 7 Controller 1 pin 4
Bit 14 Controller 1 pin 13 Bit 6 Controller 1 pin 3
Bit 13 Controller 1 pin 12 Bit 5 Controller 1 pin 2
Bit 12 Controller 1 pin 11 Bit 4 Controller 1 pin 1
Bit 11 Controller 0 pin 14 Bit 3 Controller 0 pin 4
Bit 10 Controller 0 pin 13 Bit 2 Controller 0 pin 3 /
Bit 9 Controller 0 pin 12 Paddle 1 trigger
Bit 8 Controller 0 pin 11 Bit 1 Controller 0 pin 2 /
Paddle 0 trigger
Bit 0 Controller 0 pin 1
The enhanced joystick ports carry the four directions on pins
1-4 and the extra joypad lines on pins 11-14.
$FFFF9210.W|RW|PAD_PD0|Pad0 Position / X Paddle 0 |STE,F
$FFFF9210 to $FFFF9216 Paddle position counters
Four word registers, low byte holds the position.
NAMING: this page has called them Pad0 to Pad3 Position. The
F030/CT60 listing names them X Paddle 0 ($9210), Y Paddle 0
($9212), X Paddle 1 ($9214) and Y Paddle 1 ($9216): two
paddle controllers, each with an X and a Y axis. The two
descriptions are different readings of the same four
registers; the paddle pairing follows the trigger bits at
$FFFF9202 (paddle 0 and 1), so the X/Y naming is likely the
intended one. Not settled by Hatari, EmuTOS or TOS source,
none of which touch these registers.
$FFFF9212.W|RW|PAD_PD1|Pad1 Position / Y Paddle 0 |STE,F
$FFFF9214.W|RW|PAD_PD2|Pad2 Position / X Paddle 1 |STE,F
$FFFF9216.W|RW|PAD_PD3|Pad3 Position / Y Paddle 1 |STE,F
$FFFF9220.W|RW|PAD_LPX|Lightpen X |STE,F
$FFFF9222.W|RW|PAD_LPY|Lightpen Y |STE,F
===========#==#=======#===============================================#=====
----------------------|DSP 56001 Host - Digital Signal Processor |-----
===========#==#=======#===============================================#=====
$FFFFA200.B|RW|DSP_ICR|Interrupt Control Register (%IMMHH_TR) |DSP
$FFFFA200 DSP_ICR Interrupt Control Register (DSP X:$FFE9)
Bit 7 INIT setting this forces initialisation of the
host interface
Bits 6-5 DMA mode control:
00 interrupt mode, DMA off
01 24 bit DMA mode
10 16 bit DMA mode
11 8 bit DMA mode
Bit 4 HF1 host flag 1
Bit 3 HF0 host flag 0
Bit 2 unused
Bit 1 TREQ transmit interrupt request enable
Bit 0 RREQ receive interrupt request enable
Bits 1-0 read as a pair, and what they mean depends on the
DMA mode selected in bits 6-5:
In interrupt mode (bits 6-5 = 00):
00 no interrupts, host polls the status register
01 RXDF request, interrupt when receive data is full
10 TXDE request, interrupt when transmit data is empty
11 both RXDF and TXDE requests
In DMA mode (bits 6-5 not 00):
00 no DMA
01 DSP to host request (receive)
10 host to DSP request (transmit)
11 undefined, illegal
The host flags are general purpose signalling bits readable by
the DSP; they carry no fixed meaning in hardware.
Bit positions verified against Hatari src/falcon/dsp_core.h,
which defines CPU_HOST_ICR_RREQ 0, TREQ 1, HF0 3, HF1 4,
HM0 5, HM1 6 and INIT 7. Value tables from the F030/CT60
listing.
Falcon only.
$FFFFA201.B|RW|DSP_CVR|Command Vector Register (%I__VVVVV) |DSP
$FFFFA201 DSP_CVR Command Vector Register (DSP X:$FFE9)
Bit 7 Host command bit
Bits 6-5 unused
Bits 4-0 Host vector, 0 to 31
Writing a vector with the command bit set causes the DSP to
take an interrupt to that vector, which is how the host asks
the DSP to run a routine.
Falcon only. From the Atari Compendium.
$FFFFA202.B|RW|DSP_ISR|Interrupt Status Register (%ID_HHETR) |DSP
$FFFFA202 and $FFFFA203 DSP Status and Vector
$FFFFA202 Interrupt Status Register (DSP X:$FFE8)
$FFFFA203 Interrupt Vector Register
Status register bits, read only:
Bit 7 HREQ host request pending
Bit 6 DMA DMA status
Bit 5 unused
Bit 4 HF3 host flag 3, set by the DSP
Bit 3 HF2 host flag 2, set by the DSP
Bit 2 TRDY transmitter ready
Bit 1 TXDE transmit data register empty
Bit 0 RXDF receive data register full
The normal handshake is: poll bit 1 before writing a word to
$FFFFA205-07, poll bit 0 before reading one. HF2 and HF3 are
the return path for the HF0 and HF1 flags in the control
register, so the two ends can signal each other without
moving data.
The vector register holds the 680x0 exception vector number
used when the DSP raises an interrupt to the host.
Bit positions verified against Hatari src/falcon/dsp_core.h,
which defines CPU_HOST_ISR_RXDF 0, TXDE 1, TRDY 2, HF2 3,
HF3 4, DMA 6 and HREQ 7. Names and layout also match the
F030/CT60 listing. The Atari Compendium names both registers
but gives no bit breakdown, which is why this entry previously
had none.
Falcon only.
$FFFFA203.B|RW|DSP_IVR|Interrupt Vector Register (Vector Number) |DSP
$FFFFA204.B|RW|DSP_TR0|Transfer Highest Byte (DSP56003 32bit) |DSP32
$FFFFA204 to $FFFFA207 DSP Transfer Registers
$FFFFA204 Transfer byte, highest (DSP56003 32 bit only)
$FFFFA205 Transfer byte, high
$FFFFA206 Transfer byte, middle
$FFFFA207 Transfer byte, low
The DSP56001 fitted to the Falcon is a 24 bit part, so only
the low three bytes are used. $FFFFA204 exists for the 32 bit
DSP56003, which the Falcon does not have.
Reading or writing $FFFFA207 is what actually triggers the
transfer; the other bytes are staged first.
Falcon only.
$FFFFA205.B|RW|DSP_TR1|Transfer Hi |DSP
$FFFFA206.B|RW|DSP_TR2|Transfer Mi |DSP
$FFFFA207.B|RW|DSP_TR3|Transfer Lo |DSP
===========#==#=======#===============================================#=====
----------------------|MFP 68901 - Multi Function Peripheral |-----
===========#==#=======#===============================================#=====
$FFFFFA01.B|RW|MFP_PDR|Parallel Port Data Register %SRFKBRDC |
$FFFFFA01 MFP_PDR General Purpose I/O Port
Bit 7 Monochrome monitor detect
Bit 6 RS-232 ring indicator
Bit 5 FDC / HDC interrupt
Bit 4 Keyboard / MIDI interrupt
Bit 3 Blitter done
Bit 2 RS-232 clear to send
Bit 1 RS-232 carrier detect
Bit 0 Centronics busy
All eight are inputs on a standard ST, set by the data
direction register at $FFFFFA05.
Bit 5 is the one to poll for floppy and hard disk completion:
read the byte, mask with $20, and a result of ZERO means the
controller has interrupted. This is what TOS itself does.
Bit 7 is how machines before the Falcon detect a monochrome
monitor. The Falcon uses $FFFF8006 instead.
FALCON WIRING DIFFERS: on the Falcon two GPIP pins carry
different signals (per the F030/CT60 listing):
Bit 2 MIDI ACIA interrupt (ST: RS-232 clear to send)
Bit 3 DSP connector interrupt (ST: Blitter done)
The corresponding interrupt vectors move with them: on the
Falcon vector $108 is the MIDI ACIA and $10C the DSP
connector, where the ST has RS-232 CTS and Blitter done. Both
assignments are correct for their machine.
From the Atari Compendium.
$FFFFFA03.B|RW|MFP_AER|Active Edge Register %SRFKBRDC |
| | | Interrupt on 0:High-Low,1:Low-High |||||||| |!F
| | | Interrupt on 0:Low-High,1:High-Low |||||||| |F
$FFFFFA03 MFP_AER Active Edge Register
One bit per GPIO pin, same order as $FFFFFA01:
Bit 7 Monochrome monitor detect
Bit 6 RS-232 ring indicator
Bit 5 FDC / HDC interrupt
Bit 4 Keyboard / MIDI interrupt
Bit 3 Blitter done
Bit 2 RS-232 clear to send
Bit 1 RS-232 carrier detect
Bit 0 Centronics busy
Selects which edge on that pin causes an interrupt:
On the ST and STE: 0 = high to low, 1 = low to high
On the Falcon: 0 = low to high, 1 = high to low
The polarity is inverted on the Falcon, which is why the wiki
summary carries two lines for this register with !F and F tags.
Note: on a Falcon the MFP is not actually used for serial
communications, so the RS-232 bits are not meaningful there.
From the Atari Compendium.
$FFFFFA05.B|RW|MFP_DIR|Data-direction %SRFKBRDC |
| | | I/O-7 Mono Detect/Sound--------------+||||||| |
| | | I/O-6 RS232 Ring Indicator------------+|||||| |
| | | I/O-5 FDC/HDC--------------------------+||||| |
| | | I/O-4 IKBD/MIDI-------------------------+|||| |
| | | I/O-3 Blitter Done-----------------------+||| |
| | | I/O-2 RS232 CTS---------------------------+|| |
| | | I/O-1 RS232 DCD----------------------------+| |
| | | I/O-0 Centronics Busy-----------------------+ |
$FFFFFA05 MFP_DIR Data Direction Register
One bit per GPIO pin, same order as $FFFFFA01.
Each bit is individually programmed: 0 = input, 1 = output.
On a standard ST all eight pins are inputs, so this register
reads as zero. Changing it without knowing what is wired to
the pin can drive a line that something else is also driving.
From the Atari Compendium.
$FFFFFA07.B|RW|MFP_IEA|Interrupt Enable A %76AbebeB |
$FFFFFA07 MFP_IEA Interrupt Enable Register A
Bit 7 Monochrome monitor detect
Bit 6 RS-232 ring indicator
Bit 5 Timer A (STE and TT sound)
Bit 4 Receive buffer full
Bit 3 Receive error
Bit 2 Transmit buffer empty
Bit 1 Transmit error
Bit 0 Timer B
Setting a bit enables that interrupt source. The same bit
layout applies to all four register A pairs:
$FFFFFA07 Interrupt Enable A
$FFFFFA0B Interrupt Pending A
$FFFFFA0F Interrupt In-Service A
$FFFFFA13 Interrupt Mask A
Enable decides whether the source can interrupt at all; mask
decides whether an enabled and pending interrupt is passed to
the processor. Pending is set by the hardware and cleared by
writing a ZERO to that bit, not a one.
On a Falcon the MFP is not used for serial communications.
From the Atari Compendium.
$FFFFFA09.B|RW|MFP_IEB|Interrupt Enable B %54CD3210 |
$FFFFFA09 MFP_IEB Interrupt Enable Register B
Bit 7 FDC / HDC
Bit 6 Keyboard / MIDI
Bit 5 Timer C (200 Hz system clock)
Bit 4 Timer D (USART baud rate)
Bit 3 Blitter done
Bit 2 RS-232 clear to send
Bit 1 RS-232 carrier detect
Bit 0 Centronics busy
The same bit layout applies to all four register B pairs:
$FFFFFA09 Interrupt Enable B
$FFFFFA0D Interrupt Pending B
$FFFFFA11 Interrupt In-Service B
$FFFFFA15 Interrupt Mask B
Timer C at bit 5 is the one driving the 200 Hz system clock
counter at $000004BA and the VBL queue. Disabling it stops
large parts of TOS working.
From the Atari Compendium.
$FFFFFA0B.B|RW|MFP_IPA|Interrupt Pending A %76AbebeB |
$FFFFFA0D.B|RW|MFP_IPB|Interrupt Pending B %54CD3210 |
$FFFFFA0F.B|RW|MFP_ISA|Interrupt In-Service A %76AbebeB |
$FFFFFA11.B|RW|MFP_ISB|Interrupt In-Service B %54CD3210 |
$FFFFFA13.B|RW|MFP_IMA|Interrupt Mask A %76AbebeB |
| | | I/O 7 Mono Detect/Sound--------------+||||||| |
| | | I/O 6 RS232 Ring----------------------+|||||| |
| | | Timer A--------------------------------+||||| |
| | | Receive Buffer full---------------------+|||| |
| | | Receive Error----------------------------+||| |
| | | Transmit Buffer empty---------------------+|| |
| | | Transmit Error-----------------------------+| |
| | | Timer B-------------------------------------+ |
$FFFFFA15.B|RW|MFP_IMB|Interrupt Mask B %54CD3210 |
| | | I/O 5 FDC/HDC------------------------+||||||| |
| | | I/O 4 IKBD/MIDI-----------------------+|||||| |
| | | Timer C--------------------------------+||||| |
| | | Timer D---------------------------------+|||| |
| | | I/O 3 Blitter Done-----------------------+||| |
| | | I/O 2 RS232 CTS---------------------------+|| |
| | | I/O 1 RS232 DCD----------------------------+| |
| | | I/O 0 Centronics Busy-----------------------+ |
$FFFFFA17.B|RW|MFP_VCR|Vector Register %xxxxI___ |0100
| | | End of Interrupt 0:software, 1:auto------+ |1000
| | | VCR should contain------------------%0100?000 |*
$FFFFFA17 MFP_VCR Vector Register
Bits 7-4 Interrupt vector base, upper nibble
Bit 3 End of interrupt mode:
1 = software end of interrupt
0 = automatic end of interrupt
Bits 2-0 Supplied by the MFP as the interrupt number
On the Atari the base is set to $40, so MFP interrupts use
vectors $40 to $4F, which are addresses $00000100 to
$0000013C in the exception vector table.
In software end of interrupt mode the handler must clear the
corresponding bit in the in-service register itself, otherwise
no further interrupt of that or lower priority is delivered.
The wiki summary notes the register should contain %0100?000.
From the Atari Compendium.
$FFFFFA19.B|RW|MFP_TAC|Timer A Control %____EAAA |
$FFFFFA19 and $FFFFFA1B Timer A and Timer B Control
Bits 3-0 (Timer A) and bits 3-0 (Timer B):
0000 Timer stop
0001 Delay mode, divide by 4
0010 Delay mode, divide by 10
0011 Delay mode, divide by 16
0100 Delay mode, divide by 50
0101 Delay mode, divide by 64
0110 Delay mode, divide by 100
0111 Delay mode, divide by 200
1000 Event count mode
1xxx Pulse extension mode, divider as above
The MFP clock is 2.4576 MHz. With a divide by 10 prescale the
timer ticks at 245.76 kHz, a period of about 4.069 microseconds,
which is the usual choice for measuring short intervals.
Timer A is normally free on an STF and is the usual pick for
user timing. On an STE and TT it is used for DMA sound, so
save and restore the control register there.
Event count mode counts pulses on a GPIO pin rather than the
internal clock, which is how the Timer B display-line counting
trick works.
From the Atari Compendium.
$FFFFFA1B.B|RW|MFP_TBC|Timer B Control %____EBBB |
| | | Event Count Mode-------------------------1000 |
| | | Pulse Extension--------------------------1xxx |
| | | Delay------------------------------------0xxx |
$FFFFFA1D.B|RW|MFP_TDC|Timer C+D Control %_CCC_DDD |
| | | Timer C Control-----------------------+++ ||| |
| | | Timer D Control---------------------------+++ |
| | | Stop Timer--------------------------------000 |
| | | Delay,Clockdivide 4 3200Hz--------------001 |
| | | Delay,Clockdivide 10 1280Hz--------------010 |
| | | Delay,Clockdivide 16 800Hz--------------011 |
| | | Delay,Clockdivide 50 256Hz--------------100 |
| | | Delay,Clockdivide 64 200Hz--------------101 |
| | | Delay,Clockdivide 100 128Hz--------------110 |
| | | Delay,Clockdivide 200 64Hz--------------111 |
$FFFFFA1D MFP_TDC Timer C and D Control
Bits 6-4 Timer C control
Bits 2-0 Timer D control
Bits 7 and 3 unused
Both fields use the same encoding:
000 Timer stop
001 Delay mode, divide by 4
010 Delay mode, divide by 10
011 Delay mode, divide by 16
100 Delay mode, divide by 50
101 Delay mode, divide by 64
110 Delay mode, divide by 100
111 Delay mode, divide by 200
Timers C and D have no event count or pulse extension mode,
which is why they take three bits rather than four.
Timer C drives the 200 Hz system clock. Timer D is the USART
baud rate generator. Neither should be stopped by application
code.
From the Atari Compendium.
$FFFFFA1F.B|RW|MFP_TAD|Timer A Data |
$FFFFFA1F to $FFFFFA25 Timer Data Registers
$FFFFFA1F Timer A data
$FFFFFA21 Timer B data
$FFFFFA23 Timer C data
$FFFFFA25 Timer D data
Writing loads the timer's reload value. Reading returns the
current count, which decrements from the reload value toward
zero at the rate set by the prescale in the control register.
When the count reaches zero the timer raises its interrupt and
reloads automatically. A written value of 0 means 256.
Reading a running timer is how short intervals are measured:
note the value, do the work, read again and subtract.
From the Atari Compendium.
$FFFFFA21.B|RW|MFP_TBD|Timer B Data |
$FFFFFA23.B|RW|MFP_TCD|Timer C Data |
$FFFFFA25.B|RW|MFP_TDD|Timer D Data |
$FFFFFA27.B|RW|MFP_SYC|Synchronous Character |
$FFFFFA27 MFP_SYC Synchronous Character Register
Holds the character the USART searches for when running in
synchronous mode, selected by the stop bit field in the USART
control register at $FFFFFA29 being set to 00.
Not used on a standard Atari, which runs the RS-232 port
asynchronously.
$FFFFFA29.B|RW|MFP_UCR|Usart Control %DBBSSPE_ |
| | | Divider 0:div1(sync),1:div16---------+|||||| |
| | | Databits 00:8,01:7,10:6,11:5----------++|||| |
| | | 00:sync,01:1stop,10:1.5stop,11:2stop----++|| |
| | | Parity 0:off,1:on-------------------------+| |
| | | Parity 0:odd,1:even------------------------+ |
$FFFFFA29 MFP_UCR USART Control Register
Bit 7 Clock divide: if set, divide by 16
Bits 6-5 Data bits:
00 = 8 bits 01 = 7 bits
10 = 6 bits 11 = 5 bits
Bits 4-3 Start and stop bits:
00 = synchronous
01 = 1 start, 1 stop
10 = 1 start, 1.5 stop
11 = 1 start, 2 stop
Bit 2 Parity enable: if clear, parity is ignored
Bit 1 Parity: 1 = even, 0 = odd
Bit 0 unused
Note the wiki summary line labels bits 4-3 as stop bits only;
the Compendium describes them as the combined start and stop
configuration.
From the Atari Compendium.
$FFFFFA2B.B|RW|MFP_RES|Receiver Status %BOPFSCPR |
| | | Buffer full--------------------------+||||||| |
| | | Overrun Error-------------------------+|||||| |
| | | Parity Error---------------------------+||||| |
| | | Frame Error-----------------------------+|||| |
| | | SCR found/Break--------------------------+||| |
| | | SCR received/Startbit detected------------+|| |
| | | Synchronous Strip Enable-------------------+| |
| | | Receiver Enable-----------------------------+ |
$FFFFFA2B MFP_RES Receiver Status Register
Bit 7 Buffer full
Bit 6 Overrun error
Bit 5 Parity error
Bit 4 Frame error
Bit 3 Search / break detected
Bit 2 Match / character in progress
Bit 1 Synchronous strip enable
Bit 0 Receiver enable
Bit 0 must be set before anything is received. Bits 7-4 are
status and are cleared by reading the data register.
From the Atari Compendium.
$FFFFFA2D.B|RW|MFP_TRS|Transmitter Status %BUAEBHLT |
| | | Buffer Empty-------------------------+||||||| |
| | | Underrun Error (char sent)------------+|||||| |
| | | Auto Turnaround------------------------+||||| |
| | | EOT End of Transmission-----------------+|||| |
| | | Break------------------------------------+||| |
| | | 00:High,01:Low,10:High,11:loopback,High---++| |
| | | Transmitter Enable--------------------------+ |
$FFFFFA2D MFP_TRS Transmitter Status Register
Bit 7 Buffer empty
Bit 6 Underrun error
Bit 5 Auto turnaround
Bit 4 End of transmission
Bit 3 Break
Bit 2 High bit
Bit 1 Low bit
Bit 0 Transmitter enable
Bits 2-1 together set the idle state of the transmit line:
00 = high, 01 = low, 10 = high, 11 = loopback, high
Bit 0 must be set before anything is sent. Wait for bit 7 to
be set before writing the next byte to $FFFFFA2F.
From the Atari Compendium.
$FFFFFA2F.B|RW|MFP_UAD|Usart Data |
===========#==#=======#===============================================#=====
----------------------|FPC - Floating Point Coprocessor |-----
===========#==#=======#===============================================#=====
$FFFFFA40.W|RW|FPC_STA|Status (Response) Register |ME
$FFFFFA40 to $FFFFFA5C MC68881 Floating Point Coprocessor
interface (Mega STE)
These are not ordinary control registers: they are the
MC68881's Coprocessor Interface Registers (CIR), memory
mapped because the Mega STE's 68000 has no coprocessor
instructions of its own. Software drives the FPU by writing a
command to $FFFFFA4A, polling this Status register for the
FPU's response, and moving operands through $FFFFFA50. Each
register in the block has its own entry below.
$FFFFFA40 FPC_STA Status (Response) CIR
Read to find out what the coprocessor wants next. The value is
a RESPONSE PRIMITIVE, not a set of independent status bits:
Bit 15 CA, come again. Set means the main processor must
read this register again after servicing the
current primitive.
Bit 14 PC, pass program counter. The instruction address
must be sent to $FFFFFA58.
Bit 13 DR, direction. 0 = main processor to coprocessor,
1 = coprocessor to main processor, for the
transfer this primitive requests.
Bits 12-8 primitive type: null (busy or done), transfer
operation word, transfer single or multiple
operands, evaluate effective address, transfer
status/condition, take pre- or post-instruction
exception.
Bits 7-0 parameter: for transfer primitives, the number of
bytes to move; for exception primitives, the
vector number to take.
A null primitive with CA clear means the operation is
finished. A null primitive with CA set means the FPU is still
busy, so keep polling.
The full primitive encoding table is in the MC68881/68882
User's Manual, section 7 (coprocessor interface); it is a
protocol rather than a fixed bit map, so only the framing bits
above are reproduced here.
SOURCING NOTE: none of the listings in the audit set (Hollis
v7.0, which marks the whole block "???", the Hohwiller
listing, the Compendium, FALREG or the Aura documents) carries
any detail for these registers, and neither Hatari nor EmuTOS
implements the Mega STE FPU interface. The addresses and names
on this page match the standard Motorola CIR map exactly,
which is what confirms them; the per-register descriptions
below come from that map. Where a value can be checked against
code it has been: the state frame format words on $FFFFFA44
are confirmed in Hatari src/cpu/fpp.c.
The SFP004 accessory used the same CIR-mapped scheme at
$FFFFFA40 on the Mega ST, which is why some listings tag this
block Mega ST as well.
$FFFFFA42.W|RW|FPC_CTL|Control Register %______XA |ME
| | | eXception acknowledge----------------------+| |ME
| | | Abort the current operation-----------------+ |ME
$FFFFFA44.W|RW|FPC_SAV|Save Register (state frame format word) |ME
$FFFFFA44 FPC_SAV Save CIR
Read to begin an FSAVE. Returns the state frame format word,
which tells the main processor what kind of internal state
the FPU has and how many bytes must be copied out:
$0000 NULL frame, no context to save (FPU reset or
idle with nothing pending). Nothing further to
read.
version:$18 IDLE frame, MC68881. High byte is the FPU
version number, low byte the frame size in
bytes minus four.
version:$38 IDLE frame, MC68882 (larger internal state).
other BUSY frame, mid-instruction state.
Frame sizes confirmed in Hatari src/cpu/fpp.c, which writes
$1C for a 68881 and $3C for a 68882 (frame size, with the
size-minus-four value going into the format word) and treats
format word $00 as the null frame.
Reading this register also suspends the FPU, which is the
whole point of FSAVE: it freezes the coprocessor so the
operating system can context switch.
$FFFFFA46.W|RW|FPC_RES|Restore Register |ME
$FFFFFA46 FPC_RES Restore CIR
Write the state frame format word here to begin an FRESTORE,
then write back the frame body. Same format word encoding as
the Save register above.
Writing $0000 (the null frame) resets the coprocessor to the
idle state and discards any saved context, which is how TOS
and most FPU drivers initialise the part.
Writing a format word the FPU does not recognise makes it
signal a format error, which the main processor reports as an
F-line or format exception.
$FFFFFA48.W|RW|FPC_OPW|Operation Word Register |ME
$FFFFFA48 FPC_OPW Operation Word CIR
The main processor writes the F-line instruction's FIRST word
(the operation word, $F2xx for coprocessor ID 1) here when the
FPU asks for it with a "transfer operation word" response
primitive. The FPU needs it to work out the addressing mode
the instruction used.
Not used for every instruction: only when the response
primitive in the Status register requests it.
$FFFFFA4A.W|RW|FPC_CMD|Command Register |ME
$FFFFFA4A FPC_CMD Command CIR
Writing the command word here STARTS an operation. It takes
the second word of the F-line instruction, which encodes the
FPU operation, source and destination registers and data
format (for example FADD, FMUL, FMOVE with an extended,
double, single, packed or integer operand).
After the write, poll the Status register at $FFFFFA40 for
the FPU's response primitive, which says what to do next:
transfer an operand through $FFFFFA50, transfer the operation
word, take an exception, or nothing further (operation
complete).
This is the register the whole interface revolves around: no
write here, no FPU activity.
$FFFFFA4C.W|RW| - |Reserved |ME
$FFFFFA4E.W|RW|FPC_CCR|Condition Code Register |ME
$FFFFFA4E FPC_CCR Condition CIR
Used by the conditional instructions FBcc, FScc, FDBcc and
FTRAPcc. The main processor writes the condition predicate
(the low 6 bits of the instruction's condition field, giving
the 32 IEEE conditions plus the unordered variants) and the
FPU evaluates it against the current floating point condition
codes.
The result comes back through the Status register as a "take
branch / do not take branch" response primitive, not as a bit
in this register. The main processor then acts on the branch
itself, since the FPU has no program counter.
A NaN operand makes the unordered conditions true and can
raise the BSUN exception, which is why the predicate matters
rather than a simple true/false test.
$FFFFFA50.L|RW|FPC_OPR|Operand Register |ME
$FFFFFA50 FPC_OPR Operand CIR
The 32 bit data port. Every operand and result passes through
here, a longword at a time, in the direction and count the
response primitive in the Status register specifies:
single precision 1 longword
double precision 2 longwords
extended precision 3 longwords
packed decimal 3 longwords
integer 1 longword (byte and word operands are
sign extended into it)
Transfers must be done in the order and quantity requested;
reading or writing more longwords than the primitive asked
for confuses the interface protocol.
This is the only register in the block that is a longword
port rather than a control word.
$FFFFFA54.W|RW|FPC_SEL|Register Select |ME
$FFFFFA54 FPC_SEL Register Select CIR
Read after a "transfer multiple registers" response primitive
(FMOVEM). Returns a bit mask, one bit per floating point data
register FP0 to FP7, saying which registers the main processor
must move through the Operand register and in which order.
For the control register form of FMOVEM the mask instead
selects among FPCR, FPSR and FPIAR.
Only meaningful immediately after the primitive that requests
it; reading it at any other time returns nothing useful.
$FFFFFA56.W|RW| - |Reserved |ME
$FFFFFA58.L|RW|FPC_IAR|Instruction Address Register |ME
$FFFFFA58 FPC_IAR Instruction Address CIR
The main processor writes the ADDRESS of the F-line
instruction currently being executed, when the FPU requests it
with a "transfer instruction address" response primitive.
The FPU keeps it in FPIAR so that an exception handler can
find the instruction that faulted. Without it the FPU would
have no way to report where an error happened, since it never
sees the program counter.
$FFFFFA5C.L|RW|FPC_OAR|Operand Address Register |ME
$FFFFFA5C FPC_OAR Operand Address CIR
Carries the effective ADDRESS of a memory operand, rather than
the data itself. Used by the "evaluate effective address and
transfer data" and "take pre-instruction exception" response
primitives, so the FPU can tell the main processor where to
fetch from or store to, and so that an exception frame can
record the faulting operand address.
Written by the main processor when the primitive asks for it;
read back when the FPU supplies an address.
===========#==#=======#===============================================#=====
----------------------|MFP 68901 number 2 |-----
===========#==#=======#===============================================#=====
$FFFFFA81.B|RW|MF2_PDR|Parallel Port Data Register (GPIP) %SRD_ISGG |TT
$FFFFFA81 MF2_PDR Second MFP GPIP (TT)
The TT's second MC68901. The register set and every bit layout
are identical to the first MFP at $FFFFFA01 onwards, on the
same odd-address spacing; see the entries there for the bit
detail of the AER, DDR, interrupt, timer and USART registers.
Only the GPIP pin wiring differs:
Bit 7 SCSI controller interrupt (NCR 5380)
Bit 6 RTC interrupt (MC146818A)
Bit 5 SCSI DMAC interrupt
Bit 4 Reserved
Bit 3 RS-232 Ring Indicator (SCC channel B)
Bit 2 SCC-DMA controller interrupt
Bit 1 General purpose input GPI 1
Bit 0 General purpose input GPI 0
The pin map follows the second MFP vector table at $00000140
to $0000017C (see the System variables page) and the Atari
Compendium's TT MFP list. Bits 7 and 5 are confirmed in
EmuTOS bios/scsi.c, which polls TT_MFP gpip bit 7 for the
5380 interrupt and bit 5 for the SCSI DMAC interrupt.
This MFP's interrupts use their own block of 16 vectors: TOS
programs the MF2 vector register with base $50, so vector
numbers $50-$5F land at addresses $00000140 to $0000017C.
$FFFFFA83.B|RW|MF2_AER|Active Edge Register |TT
$FFFFFA85.B|RW|MF2_DDR|Data Direction Register |TT
$FFFFFA87.B|RW|MF2_IEA|Interrupt Enable A |TT
$FFFFFA89.B|RW|MF2_IEB|Interrupt Enable B |TT
$FFFFFA8B.B|RW|MF2_IPA|Interrupt Pending A |TT
$FFFFFA8D.B|RW|MF2_IPB|Interrupt Pending B |TT
$FFFFFA8F.B|RW|MF2_ISA|Interrupt In-Service A |TT
$FFFFFA91.B|RW|MF2_ISB|Interrupt In-Service B |TT
$FFFFFA93.B|RW|MF2_IMA|Interrupt Mask A |TT
$FFFFFA95.B|RW|MF2_IMB|Interrupt Mask B |TT
$FFFFFA97.B|RW|MF2_VCR|Vector Register |TT
$FFFFFA99.B|RW|MF2_TAC|Timer A Control |TT
$FFFFFA9B.B|RW|MF2_TBC|Timer B Control |TT
$FFFFFA9D.B|RW|MF2_TDC|Timer C+D Control |TT
$FFFFFA9F.B|RW|MF2_TAD|Timer A Data |TT
$FFFFFAA1.B|RW|MF2_TBD|Timer B Data |TT
$FFFFFAA3.B|RW|MF2_TCD|Timer C Data |TT
$FFFFFAA5.B|RW|MF2_TDD|Timer D Data |TT
$FFFFFAA7.B|RW|MF2_SCR|Synchronous Character Register |TT
$FFFFFAA9.B|RW|MF2_UCR|USART Control Register |TT
$FFFFFAAB.B|RW|MF2_RSR|Receiver Status Register |TT
$FFFFFAAD.B|RW|MF2_TSR|Transmitter Status Register |TT
$FFFFFAAF.B|RW|MF2_UAD|USART Data Register |TT
===========#==#=======#===============================================#=====
----------------------|ACIA 6850 (Midi/Keyboard) |-----
===========#==#=======#===============================================#=====
$FFFFFC00.B|R-|KBD_CTL|IKBD Status %IPOFCDTR |
$FFFFFC00 KBD_CTL IKBD ACIA Status (read) / Control (write)
READ - status:
Bit 7 Interrupt request
Bit 6 Parity error
Bit 5 Receiver overrun
Bit 4 Frame error
Bit 3 Clear to send
Bit 2 Data carrier detect
Bit 1 Transmit data register full
Bit 0 Receive data register full
WRITE - control:
Bit 7 Enable receive interrupts
Bits 6-5 Transmitter interrupt configuration:
00 RTS low, interrupts disabled
01 RTS low, interrupts enabled
10 RTS high, interrupts disabled
11 RTS low, interrupts disabled, send break
Bits 4-2 Word format, data bits - parity - stop bits:
000 7-E-2 001 7-O-2
010 7-E-1 011 7-O-1
100 8-N-2 101 8-N-1
110 8-E-1 111 8-O-1
Bits 1-0 Clock divide:
00 divide by 1 (500000 cps)
01 divide by 16 (31250 cps, MIDI standard)
10 divide by 64 (7812.5 cps, IKBD)
11 master reset
The IKBD is set to $96: 7 bits, no parity, 1 stop, divide by
64, RTS low, receive interrupts on.
The MIDI ACIA at $FFFFFC04 uses the same register layout and
is set to $95, the same but divide by 16.
From the Atari Compendium.
" |-W|KBD_CTL|IKBD Control %ITTBSPDD |
| | | 7N1,7812.5,RTS low,Rec on,Send off= %10010110 |*
$FFFFFC02.B|RW|KBD_DAT|IKBD Data |
$FFFFFC04.B|R-|MID_CTL|MIDI Status %IPOFCDTR |
| | | Interrupt Request--------------------+||||||| |
| | | Parity Error--------------------------+|||||| |
| | | Receiver Overrun-----------------------+||||| |
| | | Frame Error-----------------------------+|||| |
| | | CTS Clear to Send------------------------+||| |
| | | DCD Data Carrier Detect-------------------+|| |
| | | Transmitter Data Register full-------------+| |
| | | Receiver Data Register full-----------------+ |
" |-W|MID_CTL|MIDI Control %ITTBSPDD |
| | | 7N1,31250,RTS low,Rec on,Send off = %10010101 |*
| | | Interrupt 0:off,1:on-----------------+||||||| |
| | | RTS low, TransmitIRQ off--------------00||||| |
| | | RTS low, TransmitIRQ on---------------01||||| |
| | | RTS high,TransmitIRQ off--------------10||||| |
| | | RTS low, TransmitIRQ off,send break---11||||| |
| | | 8 Databits,2 Stopbits,Parity even-------000|| |
| | | 8 Databits,2 Stopbits,Parity odd--------001|| |
| | | 8 Databits,1 Stopbits,Parity even-------010|| |
| | | 8 Databits,1 Stopbits,Parity odd--------011|| |
| | | 7 Databits,2 Stopbits,Parity off--------100|| |
| | | 7 Databits,1 Stopbits,Parity off--------101|| |
| | | 7 Databits,1 Stopbits,Parity even-------110|| |
| | | 7 Databits,1 Stopbits,Parity odd--------111|| |
| | | Clockdivide 1 - 500000.0cps---------------00 |
| | | Clockdivide 16 - 31250.0cps (MIDI Std)----01 |
| | | Clockdivide 64 - 7812.5cps (IKBD!)-------10 |
| | | Master Reset-------------------------------11 |
$FFFFFC06.B|RW|MID_DAT|MIDI Data |
===========#==#=======#===============================================#=====
----------------------|RP5C15 Real Time Clock |-----
===========#==#=======#===============================================#=====
$FFFFFC21.B|RW| - |Seconds mod 10 %____xxxx |MST
$FFFFFC21 to $FFFFFC39 RP5C15 Clock Registers, Bank 0 and Bank 1
IMPORTANT: this chip has TWO banks of registers at the same
addresses. Bit 0 of the mode register at $FFFFFC3B selects
which bank is visible. The wiki listing documents only Bank 0.
Address Bank 0 Bank 1
$FFFFFC21 Seconds ones (0-9) Clock output frequency
$FFFFFC23 Seconds tens (0-5) Reset seconds (see below)
$FFFFFC25 Minutes ones (0-9) Alarm minutes ones
$FFFFFC27 Minutes tens (0-5) Alarm minutes tens
$FFFFFC29 Hours ones (0-9) Alarm hours ones
$FFFFFC2B Hours tens (0-2) Alarm hours tens
$FFFFFC2D Day of week (0-6) Alarm day of week
$FFFFFC2F Date ones (0-9) Alarm date ones
$FFFFFC31 Date tens (0-3) Alarm date tens
$FFFFFC33 Month ones (0-9) not used
$FFFFFC35 Month tens (0-1) 12/24 hour select
$FFFFFC37 Year ones (0-9) Leap year register (0-3)
$FFFFFC39 Year tens (0-9) not used
All values are BCD digits, one digit per register, low nibble
only.
Bank 0 notes:
Day of week: 0 = Sunday.
Hours tens in 12 hour mode is 0-1, with bit 1 set for PM.
Year is stored as (year - 1980).
Bank 1 notes:
$FFFFFC21 clock output frequency:
0 = open collector CLKOUT 4 = 16 Hz
1 = 16384 Hz 5 = 1 Hz
2 = 1024 Hz 6 = 1/60 Hz
3 = 128 Hz 7 = open collector CLKOUT
$FFFFFC23: setting bit 0 resets the seconds register to zero,
and if seconds were between 30 and 59 it also increments
the minutes register. This is the "round to nearest minute"
function.
$FFFFFC35: bit 1 set = 24 hour mode, clear = 12 hour mode.
$FFFFFC37 leap year register: 0 = this is a leap year.
Mega ST only. From the Atari Compendium.
$FFFFFC23.B|RW| - |Seconds div 10 %____xxxx |MST
$FFFFFC25.B|RW| - |Minutes mod 10 %____xxxx |MST
$FFFFFC27.B|RW| - |Minutes div 10 %____xxxx |MST
$FFFFFC29.B|RW| - |Hours mod 10 %____xxxx |MST
$FFFFFC2B.B|RW| - |Hours div 10 %____xxxx |MST
$FFFFFC2D.B|RW| - |Weekday %____xxxx |MST
$FFFFFC2F.B|RW| - |Day mod 10 %____xxxx |MST
$FFFFFC31.B|RW| - |Day div 10 %____xxxx |MST
$FFFFFC33.B|RW| - |Month mod 10 %____xxxx |MST
$FFFFFC35.B|RW| - |Month div 10 %____xxxx |MST
$FFFFFC37.B|RW| - |Year mod 10 %____xxxx |MST
$FFFFFC39.B|RW| - |Year div 10 %____xxxx |MST
$FFFFFC3B.B|RW| - |Clock mode %____xxxx |MST
$FFFFFC3B Mode Register
Bit 3 Timer enable: 0 = clock stopped
Bit 2 Alarm enable: 0 = alarm off
Bits 1-0 Bank select
Bit 0 is the one that matters: it chooses whether the register
block at $FFFFFC21 to $FFFFFC39 shows Bank 0 (the clock) or
Bank 1 (the alarm and configuration). See $FFFFFC21.
Stopping the clock with bit 3 is how a consistent multi
register read is done, since the counters would otherwise roll
over mid read.
Mega ST only. From the Atari Compendium.
$FFFFFC3D.B|RW| - |Clock test %____xxxx |MST
$FFFFFC3D Test Register
Lower nibble must read as zero for the chip to be considered
working correctly. Used for factory test.
Mega ST only. From the Atari Compendium.
$FFFFFC3F.B|RW| - |Clock reset %____xxxx |MST
$FFFFFC3F Reset Register
Bit 3 1 Hz alarm pulse: 0 = enabled
Bit 2 16 Hz alarm pulse: 0 = enabled
Bit 1 Alarm reset: 1 = reset the alarm registers
Bit 0 Clock reset: 1 = reset the clock registers
Writing the reset bits clears the corresponding register bank
to zero.
Mega ST only. From the Atari Compendium.
===========#==#=======#===============================================#=====
----------------------|Add-on and expansion area |-----
===========#==#=======#===============================================#=====
$FFFFFE00.W|RW| - |Add-on control / MonSTer register |*
$FFFFFE00 and $FFFFFE0E Add-on control
Not present on stock Atari hardware.
Known to be used by: SEC BOOSTER.
CONFLICT: EmuTOS uses this same address block for the MonSTer
expansion board. From EmuTOS source:
bios/machine.h MONSTER_REG $FFFFFE00
bios/clock.c MONSTER_I2C_DIR $FFFFFE02
bios/clock.c MONSTER_I2C_SCL $FFFFFE04
bios/clock.c MONSTER_I2C_SDA $FFFFFE06
MonSTer's main register is at exactly the same address as the
first SEC BOOSTER control register, and its I2C bus lines
occupy $FFFFFE02 to $FFFFFE06 immediately above.
EmuTOS only touches these when built with CONF_WITH_MONSTER
enabled, which is off by default. A stock EmuTOS build will
not write here. A MonSTer-enabled build will.
Anyone fitting both boards, or running a MonSTer-enabled
EmuTOS alongside a SEC BOOSTER, should expect trouble.
EmuTOS also carries CONF_WITH_MAGNUM and CONF_WITH_NOVA for
two further add-on boards; their address usage has not yet
been checked against this listing.
Extended bit information is not currently available for the
SEC BOOSTER side of these registers.
$FFFFFE0E.W|RW| - |Add-on control |*
===========#==#=======#===============================================#=====
$FFFFFF82.W|R-| - |Unknown status register |F
| | | CHECK: purpose unknown. The Aura FALC3.TXT |
| | | listing records it as two new Falcon read |
| | | registers, $FFFFFF82 reading $1C and |
| | | $FFFFFF83 reading $00, with no function |
| | | identified. Not referenced by Hatari, EmuTOS |
| | | or TOS source. |
===========#==#=======#===============================================#=====
$FFFF820D.B|RW|VDL_VBL|Video Base Lo %xxxxxxx_ |STE,F
----+---- | +- ---+--- ------------------+---------------------------- --+--
| | | | | |
| | | | | Computer Type---------------+
| | | | +---Description
| | | +--------------------------Label
| | +--------------------------------Read/Write Access
| +----------------------------------Type (Byte,Word,Long)
+----------------------------------------Address (extended to 32 Bit)
Computer Type:
* |Software defined Standard
!xxx|all except xxx
0x0 |MC680x0 only
0x0+|MC680x0 or higher
BLT |Standard on TT,STE,ME,F (Blitter)
SCC |Standard on TT,ME,F
VME |Standard on TT,ME
ST |Atari ST (260/520/1040)
STE |Atari STE (520/1040)
TT |Atari TT
MST |Atari Mega ST (1/2)
ME |Atari Mega STE
F |Atari Falcon
Read/Write Access:
R/-|Read only (read register)
-/W|Write only (write register)
R/?|Read access allowed
?/W|Write access allowed
r/?|Read access allowed but not sensible
?/w|Write access allowed but not sensible
Source precedence when documents disagree:
1. Live TOS source (th-otto/tos1x, th-otto/tos3x) and EmuTOS source.
Executable and hardware-validated; TOS is Atari's own code.
2. The Atari Compendium. Broad and careful, but not perfect.
3. Everything else, including this wiki's own history and the
Dan Hollis hardware register listing.
Entries marked UNVERIFIED rest on a single source only.