Sunday, June 28, 2026

Zevious Seven: Getting a Bit More From the ZX81 Display

Leave a Comment

In the launch post, I briefly went over the making of Zevious Seven. In this follow-up, we'll take a slightly deeper look at how I put together a ZX81 shoot-'em-up, focusing on the acrobatics required to handle graphics, shadow files, and display file manipulation on a machine never built for action.


From "Box a Drop"to Writing a ZX81 Xevious-Like

There have been quite a number of modern and classic ZX81 games that push the expected boundaries of Sinclair's diminutive computer, but as far as I know (opinions and undiscovered facts may vary), there hadn't really been a true vertical SHMUP; one with a continuously scrolling background and uninterrupted game play. 

I'd been mucking around with the ZX81 DFILE and experimenting with shadow files earlier in 2026, to see how quickly you could update screens and flip between them, with the view to possibly making an at least a playable action game. I ended up with were some boxes scrolling onto the screen, and crashing into some water at the bottom. This happened at quite a respectable frame rate.

That got me wondering, wondering about Xevious. Why? Because. 

The Box Drop demo was basic, but combined with some experiences in writing ZX81 games, it did clue me in to exactly what was going to be required to make a scrolling game look fluid:

  • Compact Level Storage: A Optimised layout to compress vertical maps.
  • Graphics: Efficient handling of tile sets.
  • Speedy Display Updates: Rendering routines fast enough to handle ZX81 slowness. Like damn speedy.

Compact Level Storage: A level in a byte (a bit more that really)

Task one was to get a map, or at least the working idea for a map, up and running. For this, I wanted to be able to store a single line of map data into one byte. Or rather, a single byte should be able to store the minimum required to build a line of map data.

To achieve this, we'll be relying on two key things:
  1. The ZX81's screen dimensions.
  2. Run-Length Encoding (RLE).
The text-mode screen of the ZX81 is 32 characters wide. Storing a standard map linearly, where every single character on every single row gets its own byte, means a modest map of 256 rows would instantly devour 8KB of RAM. All rather expensive on a 16KB system.

However, a tile with a defined width of 4 characters combined with RLE, well, that's going to give us some possibilities.

The Maths of the 4-Character Tile

By making each background tile exactly 4 characters wide, a single horizontal screen row of 32 characters is comprised of exactly 8 tile slots (32 / 4 = 8). When we pair this with a 5/3 byte split, we can fit 32 unique tile types alongside a repeat counter of up to 8 total.

Bit:  7     6     5     4     3     2     1     0
   +-----+-----+-----+-----+-----+-----+-----+-----+
   |  V  |  V  |  V  |  V  |  V  |  V  |  V  |  V  |
   +-----+-----+-----+-----+-----+-----+-----+-----+
   \___________ 5 Bits __________/\____ 3 Bits ____/
                  |                       |
           High: 0–31 Tile          Low: 0–7 Repeat

That covers the background perfectly. But if we shrink the active play area by 4 characters to add a nice border around the playfield, it leaves an active play area 28 characters wide which only requires a maximum of 7 tiles to span a row.

As a background tile now never needs to repeat 8 times, we can completely repurpose that eighth repeat state (where the lower 3 bits equal 7) to act as an inline enemy spawn indicator. This frees up the top 5 bits to select from 31 distinct enemy types, spawning them at that exact position in the level map.

That now gets us down to a minimum of 1 byte per line.


Graphics: Tile Sets and Such

There are two tiles sets, one for the background an one for sprites. Background sprites are 4x3 with the Character / Enemy sprites set at 3x3.

The Background: Writing to the Shadow File

As the background tiles are 3 characters high, a new row of terrain data only needs to be processed once every 3 vertical scrolls, meaning we only read a single line from the map data.

Instead of writing directly to the active display, the game updates a shadow file area (33x23) reserved entirely for background data. The rest of the time, the engine simply shifts the existing buffer down within this shadow file, serving as a clean terrain master template. Each scroll cycle, we copy the bottom 19 rows from this shadow area to one of two D-FILEs.

The Sprites: Height Alignment and Visual Character

The choice of a 3x3 character grid for active elements provides us with a one-part technical solution and one-part artistic requirement. By making the moving sprites exactly three characters high, they align with the vertical dimension of the 4x3 landscape tiles, keeping map handling and coordinate tracking clean.

Fortuitously, this 3x3 footprint provides just enough definition to afford each element a unique appearance, while remaining small enough to not complicate handling or steal CPU cycles when tracking later.

The underlying sprite assets are actually stored as 3x6 blocks. Either the top three or bottom three lines are drawn depending on which specific D-FILE is being synced to at that moment, providing a brilliant built-in mechanism for generating character animations.

While the initial trigger to build and position a sprite is read directly from the compressed map data, the map itself does not store the full behavioural or visual definition of the entity. Instead, the map byte simply passes a selection index over to the main enemy or character structure. This tracking structure dictates an agent's real-time coordinate variables, firing states, and movement paths, ultimately determining exactly which specific 3x6 character template is extracted from the assets table to manifest that particular threat on screen.

Speedy Display Updates: Double-Buffering the D-FILE

The ZX81’s architecture leaves a painfully small window for game logic while rendering the TV signal. To maintain a fluid frame rate, the game engine abandons drawing and erasing characters within the main system-defined D-FILE.

Instead, a double-buffering strategy utilises two distinct D-FILEs. While one is actively viewed, the rendering loop composites the scrolling landscape from the map shadow file discussed earlier and the active 3x3 sprites into a hidden D-FILE. Once assembled, the engine swaps the active D-FILE pointer, timing the flip with the system variable FRAMES to instantly shift the background to the live display.

By focusing entirely on direct memory layout, custom shadow files, and raw pointer flipping, Zevious Seven achieves a continuous vertical scroll and animated sprites on hardware that was never built for real-time graphics. It's a testament to just how much performance you can squeeze out of a 16KB Sinclair machine when you stop fighting the architecture and start working with it.


Launch a Copy of Zevious

ZX81 Versions




If You Enjoyed This Share It And Buy Me A Coffee

0 comments:

Post a Comment