Skip to content
RetrogamingDeep Dive Published Updated 9 min readViews unavailable

Atari Lynx Suzy: Object Lists, Scaling, and Collision Buffers

Follow the Lynx Suzy engine from linked sprite control blocks through packed pixels, clipping, scaling, framebuffer writes, collision state, and timing.

The Atari Lynx does not have a conventional video chip that fetches a fixed tile map and a short list of hardware sprites for every scanline. Its Suzy engine reads a linked list of sprite control blocks (SCBs) from RAM, unpacks and transforms each source image, writes pixels into a bitmapped video buffer, and can test painted pixels against a separate collision buffer. Games build a frame by asking Suzy to paint objects into memory; Mikey’s display hardware later scans the resulting buffer. That distinction changes how an emulator should model ordering, collisions, and access to the framebuffer.

The Lynx hardware reference and archived development material describe this as a programmable rendering engine, not merely a fixed count of sprite slots. The reference documents significant chip errata, so a clean abstract description is not always a promise that every silicon revision behaves ideally. Preserve the documented behavior and make known deviations testable rather than silently correcting them.

From linked control blocks to painted pixels

An SCB describes one sprite operation and points to its source data and, when appropriate, the next control block. Suzy follows the list and interprets per-object fields such as position, dimensions, source format, scaling increments, palette, drawing type, and collision behavior. The control-block chain is a program for the rendering device: list order can determine which object overwrites another, which pixels collide, and how much work is performed before the display buffer is ready.

The engine’s data path is pipelined. Address logic fetches packed source bytes into a small FIFO. An unpacker extracts source pen indices, a palette maps them to pen values, and a pixel builder groups values into output bytes. A merger combines those bytes with existing framebuffer contents according to transparency and sprite type. Collision logic consumes per-pixel collisionability information and updates the parallel collision buffer. Source fetch, unpacking, painting, and collision work overlap; rendering the whole sprite as a single instantaneous host operation loses intermediate timing and memory effects.

Model an SCB as state consumed by Suzy, not as an object in a modern scene graph. Direct CPU writes to the video buffer are possible, but they do not perform the sprite engine’s collision processing. Similarly, drawing an overlay with a host graphics API may produce a convincing picture while failing game code that reads back buffer bytes or collision results.

The display window and coordinate world

The display hardware reads a 160 by 102 pixel bitmap. Suzy paints into that window, which is positioned by offsets in a larger 512 by 512 coordinate world. The sprite start fields use 9 meaningful coordinate bits; unused bits should be initialized as documented instead of being repurposed by software. Hardware clipping keeps pixels outside the current window from being written.

Window offsets affect where later sprites are painted. Moving the offsets does not retroactively relocate pixels already in the buffer. A frame builder that scrolls by changing the window must therefore account for which image data has already been rendered and whether the buffer needs to be redrawn. Test the relationship among world coordinates, window offsets, signed or wrapped positions, reference point, and clipping rather than assuming an ordinary top-left sprite origin.

The reference describes regular clipping and a faster super-clipping path for objects whose reference points lie outside the window. The engine can skip image data that cannot reach the visible window, but the documentation also records limitations and unresolved behavior for some full-range coordinate cases. Avoid replacing this with a generic rectangle intersection unless that approximation is explicitly scoped: clipping influences both which source bytes are fetched and the time charged to the engine.

Packed source pixels, palette, and pixel types

Source graphics are packed at selectable depths of one, two, three, or four bits per source pixel. The resulting pen index is mapped through a sixteen-entry palette to a four-bit pen value. The pen is more than a color index: depending on the sprite type, it can be transparent, opaque, collideable, a boundary marker, a shadow, or part of an XOR operation. Two output pixels share a byte, so unaligned sprite edges require byte-level merge behavior that preserves neighboring pixels.

Pens 1 through 13 are the general-purpose opaque and collideable range in the documented model. Pen zero and the upper special pens vary according to the SCB’s drawing type. A normal sprite treats zero as transparent; boundary and shadow variants change the way special pens affect display and collision state. The hardware reference calls out a silicon erratum in which some shadow-related behavior differs from the intuitive type name. An emulator should implement the documented actual behavior for the target model and keep errata evidence close to the relevant tests.

Literal source runs are useful when software modifies graphics at runtime, while packed or compressed source forms reduce memory use. Do not confuse source compression with scaling: the engine still expands source values, maps them to pens, and applies transforms before writing. Validate a small image with known packed bits and palette entries, then inspect raw framebuffer bytes including both halves of pixels that share a byte.

Scaling, stretch, tilt, and scanline work

Suzy can scale horizontally and vertically as it paints, and those rates can differ. Horizontal stretch and tilt can vary the horizontal size and position across successive lines; vertical stretch can also be enabled. With those operations, a rectangular source may be painted into a quadrilateral. These are hardware address-generation and sampling behaviors, not a post-render texture transform.

A useful emulator trace records source coordinates, destination line and pixel, scale accumulator, clipping decision, palette result, framebuffer byte, collision byte, and emulated time. Edge cases matter: zero or extreme increments, flip direction, reference points, a line that clips at its first pixel, and a line whose final pixel shares a byte with an existing neighbor. Keep any filtering or host scaling outside the emulated pen-generation path.

The reference gives a maximum of 254 bytes of source data per line, roughly 508 unscaled pixels, while vertical extent is described differently. This is a source/data constraint, not a guarantee that every very large object finishes within a single video interval. Suzy competes for memory bandwidth with CPU activity, and a long list of expensive objects can change when the final buffer becomes available. Do not convert the maximum encoded width into an unlimited-sprite claim.

Collision is a second memory surface

For collision-capable sprite types, painted pixels are checked against a parallel collision buffer, and the result is reported through per-sprite state. The collision buffer is not identical to the visible image: transparency, pen semantics, drawing type, and collision enable all matter. Some sprite types avoid collision-buffer access, and software can omit the collision buffer to free memory when collision detection is not needed.

CPU writes directly to the display bitmap do not automatically update collision information. A host-side hit-test based on sprite rectangles is also not equivalent: it cannot reproduce pen transparency, scaled pixel boundaries, draw ordering, or special pens. Treat collision reads and writes as part of the rendering transaction. A practical test uses two small sprites with deliberately sparse opaque pixels, then checks collision results as one sprite is moved across transparent and collideable positions.

Because the collision surface occupies RAM and can be initialized with particular background drawing modes, stale bytes can affect later tests if software does not clear the expected region. Record whether a frame begins with a cleared or intentionally preserved collision buffer. Do not unconditionally clear it in the emulator unless the game or documented hardware sequence does so.

CPU interaction, list safety, and state restoration

The CPU can prepare SCBs, source data, palettes, offsets, and buffers in RAM. The programming reference documents restrictions on accessing Suzy’s registers while its engine is active; implementations should respect the documented wait or synchronization protocol. Avoid allowing host threads to observe a half-updated SCB unless the target’s bus behavior permits it. Conversely, do not snapshot the entire list at start if software can legally modify data during an operation and the hardware fetches it later.

Represent progress sufficiently to resume a partially rendered list. Save state may need the current SCB address, source address, FIFO contents, unpacking state, palette and pen state, destination byte, scaling accumulators, collision pipeline, and pending memory cycles. A frame-boundary save is not enough for determinism if the engine is in the middle of a long list. After load, rebuild derived pointers from saved addresses and mapping state.

Keep a list of trace events rather than only a final screenshot: SCB fetch, source fetch, decoded pen, clipped or accepted pixel, framebuffer merge, collision comparison, and completion. These records expose the difference between incorrect source decoding and incorrect buffer order.

Focused conformance tests

Build tests around observable contracts and record the Lynx model or revision assumptions:

  • Paint a one-pixel source at aligned and unaligned X positions and verify both pixels in affected destination bytes.
  • Exercise each source depth with a known packed pattern and palette mapping.
  • Compare transparent, normal, boundary, shadow, background, non-collideable, and XOR drawing types using raw buffer bytes.
  • Place the same object inside, partially outside, and fully outside the window; verify clipping and work skipped.
  • Vary horizontal and vertical scaling independently, then test flip, tilt, and per-line stretch with a coordinate grid.
  • Render two sparse sprites and verify collision status for opaque, transparent, special, and collision-disabled pens.
  • Save and restore while the SCB list is active, then compare the remaining writes and collision results with an uninterrupted run.
  • Stress long lists while tracing CPU-visible memory timing; distinguish a rendering delay from a display scaling or refresh problem.

Use original development documents, hardware-reference errata, and more than one emulator implementation as evidence, but identify which source supports each claim. A screenshot can confirm a broad visual result; it cannot establish correct collision-buffer contents, clipping work, or save-state continuation.

Suzy is best understood as a small, memory-connected graphics processor whose output is ordinary RAM plus collision state. Emulating the SCB chain, pixel pipeline, and memory-visible timing explains both the Lynx’s flexible object system and the bugs that a sprite-count abstraction hides.

Related:

Sources:

Comments