Atari 2600 TIA Collisions: Sticky Latches, Object Pixels, and Beam Timing
Trace Atari 2600 TIA collision latches from object-pixel overlap through CXCLR, then validate collision timing with Stella's debugger and hardware-aware tests.
The Atari 2600 Television Interface Adapter (TIA) does not build a conventional framebuffer and then run a collision detector over two finished images. It generates player, missile, ball, and playfield signals in synchrony with the television beam. The TIA records pairwise object collisions in sticky latches that software can read later. This design makes collisions both simpler and stranger than modern rectangle-overlap logic: they describe a hardware graphics event, not a physics response or a visible-color comparison.
The original Stella Programmer’s Guide documents the programmer-visible collision registers and reset operation. Stella’s current debugger provides a practical way to inspect those latches and the TIA’s queued register writes. The manual is historical hardware documentation; the emulator debugger describes modern diagnostic tooling. Keep those roles separate when using either source.
Fifteen pairwise collision results
The TIA’s six display objects are player 0, player 1, missile 0, missile 1, the ball, and the playfield. A pairwise combination of six objects yields fifteen possible collisions. The collision registers pack two one-bit latches each; software reads them through TIA read addresses and can test the defined bit for a particular object pair. A set latch means the TIA detected the corresponding overlap during its display operation.
These bits are persistent status, not one-cycle pulses. Once set, a collision remains recorded until software writes the collision-clear strobe, CXCLR. Clearing is global: it resets the collection of collision latches together rather than acknowledging just one object pair. Games commonly clear collision state at a chosen point in the frame, then read it later after the relevant playfield/player interactions have been generated.
Do not infer a collision solely from object bounding boxes. TIA objects are bit patterns emitted at beam time, and their position, size, reflection, enable state, and current scanline determine which color-clock positions assert a signal. A wide player rectangle may contain transparent pixels; those do not necessarily represent occupied object pixels. A per-frame geometry test can report collisions that the TIA would not set.
Likewise, do not make collision state depend on which color is ultimately visible. Priority controls the displayed color when multiple objects overlap, while collision flags are hardware collision outputs for object combinations. Visibility and collision are related to the same graphics signals but answer different questions. A hidden object can still participate in a collision according to the TIA’s object generation and enable behavior.
Read address, status bit, clear strobe
The collision register family uses separate addresses for object-pair status. Software reads the selected register and masks the collision bit; it then writes the clear register to reset all collision latches. The read is not a response to an interrupt automatically generated by every overlap. Game logic typically polls the latches during a known part of the frame, often after enough scanlines have passed for relevant objects to interact.
An assembly-style diagnostic can be expressed without pretending to be a complete game loop:
LDA CXPPMM ; read player/missile collision status
AND #%10000000 ; test the documented P0/P1 collision bit
BEQ no_hit
; record collision for game logic
no_hit:
STA CXCLR ; writing clears all collision latches
The exact symbolic names and bit assignment should come from the selected assembler’s hardware definitions. The pseudocode also omits the timing constraints of reading after the beam has generated the needed pixels. A collision query made before the relevant horizontal and vertical region has been scanned may correctly return zero even if the objects would overlap later in the frame.
CXCLR is a strobe: the written data value is not a mask selecting which latches to clear. Do not emulate it as ordinary writable RAM. Similarly, collision registers are read-only status, not storage for the last byte written by the CPU. Register handlers should implement their documented access effects explicitly.
TIA collision is beam-relative
The 2600’s display hardware is closely tied to scanline timing. A program may position an object by executing carefully timed writes, and an emulator must preserve the ordering between CPU bus operations and TIA pixel generation. The TIA guide describes objects and collision registers from the programming model; Stella’s debugger shows internal TIA state and queued writes, which is useful when a CPU write arrives during a line.
An emulator should ideally represent the TIA as a stream of color-clock events or as a well-defined scanline renderer with cycle-positioned state changes. If it renders the whole line from registers sampled at line start, it can miss a player graphic update that occurs midway through that line. If it redraws an entire line whenever any register changes, it can double-count collisions in the already-rendered section. Either design needs a rule for which pixels have been emitted and which object signals feed the latches.
Track the collision signal separately from final color selection. For each pixel or color-clock segment, derive the active playfield/player/missile/ball signals, set the corresponding collision latch bits when the documented pairwise combination is present, then resolve priority and color for the output. This separates gameplay-visible status from rendering composition and makes failures easier to diagnose.
The 2600’s 6507 CPU budget is constrained by the video schedule, but avoid hardcoding a generic “one frame equals N CPU instructions” assumption. The chosen television standard, console profile, and emulator’s color-clock convention determine the conversion. Use a common emulated timeline for CPU writes, TIA object shifts, collision latches, and frame timing. Do not compare host wall-clock rendering time to a software collision calculation.
Reset timing and frame discipline
Because CXCLR clears every collision latch, when it is written matters. A game that clears at the start of a gameplay interval and reads near the end is using the register as a frame-local accumulator. A game that clears midway through a frame intentionally discards earlier interactions. An emulator that clears collision state automatically at VBlank changes the contract even if its screenshots look correct.
Tests should verify both accumulation and clearing. Arrange object pixels to overlap on an early line, read the status, then arrange a second pair to overlap later. Confirm that both corresponding bits remain set until the clear strobe. Clear between the two events and confirm the first result is gone while the later event can set its own latch. Test intersections between visible playfield bits, player pixels, missiles, and ball independently; do not substitute filled rectangles for the TIA’s actual generated patterns.
Object reset and enable strobes should be tested with collisions as well. If a player is repositioned or its graphics register changes during the line, only pixels generated after the write should affect subsequent collision output. The same applies to missile enable/size, ball state, playfield registers, and reflection. Use controlled test programs whose writes occur at explicit scanline/color-clock positions.
Using Stella as a diagnostic instrument
Stella’s TIA debugger tab exposes register values and decodes collision latches. It also visualizes queued writes, which is useful for confirming that a CPU register update has not yet taken effect in the pixel pipeline. The debugger lets a developer inspect the emulated state; it does not, by itself, prove that the emulated state matches a physical console. Pair it with known test ROMs and, when the behavior is disputed, hardware observations.
For a reproducible trace, capture:
- Console/television profile and emulator version.
- Scanline and horizontal color-clock at each relevant write.
- Current player/missile/ball/playfield registers and enable/size state.
- Collision latch values before and after each tested overlap.
- The cycle position of
CXCLRand subsequent reads. - The rendered native-resolution line and the object-signal decode.
The visible output and latch trace are complementary artifacts. A black pixel can still matter if the underlying object signals overlap; a visible color blend may be caused by priority without the specific object pair the game reads. Preserve both when filing a regression.
Validation matrix
Build a small set of ROMs or test states that cover one interaction each. Exercise horizontal and vertical overlaps, a transparent gap inside a player’s nominal area, reflected player graphics, double-width sprites, missiles, ball, playfield bits, and object disable/reset strobes. Repeat the same setup with the collision clear before, during, and after the beam crosses the overlap. Include cases where a CPU write changes one object’s shape at a known point in the line.
For screenshot regressions, compare the pixel stream as well as the collision bits. For state tests, save immediately after a collision and before CXCLR; reload and verify the latch persists. If the emulator uses partial-frame state, include beam position and pending writes. A save state that restores the image but clears the latch is not behaviorally complete.
Acceptance criteria
An accurate collision subsystem has explicit object-signal inputs, pairwise sticky latches, documented read mappings, and a global clear strobe. It updates only as the emulated beam produces pixels and has deterministic interaction with CPU register writes. Its tests separate “collision was recorded” from “the output color looked right,” and they cover clear timing rather than assuming a frame reset.
The Atari 2600’s collision registers illustrate a broader preservation lesson: an emulator must reproduce what software can observe, not just what a screenshot suggests. Keeping the beam, object logic, collision latches, and visible color as separate but synchronized outputs turns a mysterious hitbox bug into a traceable hardware event.
Related:
- Sprite Limits and Scanlines: Why Accurate Emulators Preserve Flicker and Dropout
- Tile-Based Rendering: How 2D Consoles Built Scenes Without a Modern Framebuffer
Sources: