Atari 7800 MARIA: Display Lists, Line RAM, and CPU Bus Time
Understand Atari 7800 MARIA display-list DMA, line RAM construction, CPU stalls, graphics modes, and the timing evidence accurate emulation requires.
The Atari 7800’s MARIA display processor builds each visible scanline from a display-list structure in memory instead of fetching a single conventional framebuffer. MARIA uses DMA to read the display-list instructions and graphics data, assembles a line in internal line RAM, and displays that line as raster timing advances. Those reads compete with the 6502-family SALLY CPU for system bus access. An emulator that draws the right final pixels but ignores list traversal, line boundaries, and CPU stalls can still fail software that relies on exact timing.
This architecture is why MARIA programming is both flexible and timing-sensitive. The display list describes graphics objects and their data for the line being constructed; a separate display-list list associates work with raster lines. The CPU can prepare data and update lists, but the display hardware takes bus cycles to fetch them. The cost depends on the work required to build a line, not simply the number of pixels ultimately visible.
The display-list pipeline
MARIA’s line construction and display are pipelined. During a raster line, MARIA processes the appropriate list and fetches graphics data into line RAM; that prepared line is used for the following display interval. As a result, changing a list pointer or graphics bytes at an arbitrary instant may affect a later line rather than the line currently being scanned. Software needs a frame and scanline model, not just a per-frame renderer.
The display-list list (DLL) identifies display lists for raster positions. A display list contains headers and object descriptions that direct MARIA to graphics data and specify how it is rendered. The 7800 software guide documents ordinary and extended headers; the extended form carries additional control information. The exact header interpretation depends on graphics mode, so an emulator should decode fields according to the documented modes instead of treating every object as a uniform sprite record.
Different width and color-depth modes change how many bytes MARIA must fetch and how those bits map to pixels and palette entries. Programs can choose tradeoffs between horizontal resolution and color use. List length, object placement, and mode changes all affect DMA work. Therefore, a fixed “one sprite costs N cycles” rule is not sufficient for a cycle-aware model unless it has been derived from the specific memory fetch pattern and verified against hardware documentation.
DMA and the SALLY CPU
MARIA performs DMA reads during visible display activity. The original 7800 software guide explains that the CPU is suspended while MARIA reads display-list and graphics data. This bus arbitration changes the CPU’s available instruction budget per raster line. Code that assumes every line gives the CPU the same number of cycles can drift, miss a raster deadline, or interfere with data feeding another device.
For emulation, model bus availability as part of the machine timeline. A CPU instruction that would otherwise execute during a DMA-held interval must be delayed according to the implementation’s timing model; it must not simply complete and then let the renderer retroactively fetch pixels. Likewise, MARIA should not consume data from a future CPU write before the emulated bus order permits it. Interleaving DMA and CPU events at the right granularity is essential for raster effects and mid-frame list updates.
The right abstraction is not necessarily a host thread per device. A deterministic scheduler can advance CPU and MARIA events in a shared emulated time domain, with explicit bus ownership or wait states. The video path can accumulate line RAM from MARIA’s fetches, while the CPU core observes stall time. A renderer may batch final pixel output after timing state is correct, but it cannot discard the causality of reads and writes.
It is useful to measure DMA cost from the fetch sequence itself. Log the list-header reads, graphics-byte reads, line RAM writes, and CPU cycles withheld for each visible line. That trace can distinguish a rendering-mode error from an unexpected amount of bus activity. If a list crosses an address or page boundary, check the documented hardware limits and exact data layout instead of letting a host buffer read continue transparently. Cartridge bank changes must be sampled at the same emulated event boundary as MARIA’s fetch; a later CPU write should not retroactively change bytes already consumed for the line.
The CPU-side cost is also relevant to interrupt scheduling. An interrupt can become pending while MARIA is using the bus, but instruction execution and interrupt service still follow CPU-visible timing. Treat an interrupt request as state, then let the processor accept it when its execution rules permit. Advancing the CPU through a long instruction block and subtracting a fixed number of cycles afterward can put a handler at the wrong point relative to line RAM construction.
Debug display-list failures methodically
When a 7800 title shows missing objects, wrong colors, or scanline artifacts, first identify whether the issue is list construction, bus timing, palette/mode decoding, or final host presentation. Use a deterministic frame and capture the DLL pointer, the list selected per line, decoded header fields, source addresses, mode, and the sequence of DMA reads. Compare one affected line at a time against a reference emulator or hardware capture when available.
Useful questions include:
- Was the intended display-list list entry selected for the raster line?
- Did the object header use the expected normal or extended format?
- Did the graphics fetch cross a page or list boundary in a way the hardware constrains?
- Were source bytes present in the right cartridge bank when MARIA read them?
- Was line RAM cleared and populated in the order expected for the next scanline?
- Did the CPU write a register or list entry before or after MARIA’s relevant fetch window?
Do not “fix” a missing sprite by drawing it directly from the ROM or by bypassing the list path. That can hide a bank-selection error or a timing bug, and it will usually break raster effects. Preserve the address and event trace that reproduces the issue, then compare the emulator’s reads with the hardware guide and driver implementation.
Build a repeatable test
Use legal test software or homebrew with a controlled display list. A small program can render a known background, add one object, vary its vertical position across the display, and record a CPU loop that runs near a line deadline. Then add objects or change modes and verify both pixel output and CPU progress. A timing test should include a case where the list is changed around the fetch boundary so that early/late updates are distinguishable.
For a regression, save the test ROM hash, system region, emulator version, visible frame, and expected line and cycle observations. Compare intermediate state as well as the screenshot. A screenshot alone cannot tell whether an apparent correct image was generated from the correct timing model; a cycle trace alone cannot prove the composite colors and object priority are correct.
Check PAL/NTSC and display-window differences using the documentation for the target system. Do not apply one machine’s raster constants to all regional variants without evidence. Keep analog output, overscan, scaling, and shader settings out of the first comparison so host presentation does not obscure a MARIA-level discrepancy.
Acceptance criteria and common mistakes
An implementation is not validated merely because commercial games boot. An acceptance suite should test display-list selection, normal and extended headers, multiple graphics modes, per-line DMA work, CPU suspension, banked cartridge reads, and updates near scanline boundaries. It should include reference output for both pixels and timing, and document any hardware behavior that remains uncertain.
Common mistakes include treating MARIA as a conventional framebuffer GPU, rendering all display lists once per frame, ignoring DMA bus contention, and assuming CPU time is constant for every raster line. Another is comparing against a single imperfect emulator and treating its output as hardware truth. Use the original software guide and primary emulator implementation as evidence, and label uncertain behavior rather than filling gaps with plausible guesses.
MARIA’s display list and line RAM connect graphics output directly to bus timing. Accurate emulation must preserve which bytes are fetched for which line and how those fetches affect CPU execution. Once that contract is explicit, raster glitches and performance-sensitive software become diagnosable instead of mysterious.
Related:
- Atari 2600 TIA Collisions: Sticky Latches, Object Pixels, and Beam Timing
- Cartridge Mappers and Bank Switching: How Consoles Addressed Games Larger Than Memory
Sources: