Isocubes: a real Filmation-style engine
After building Raiders of the Lost Aisle, a fair question came back: that engine is isometric-looking, but is it the same technique Knight Lore, The Great Escape and Head Over Heels actually used? Honestly — no. Raiders is a flat single layer where tiles never overlap, which sidesteps the hard problem rather than solving it. Isocubes is the real thing: height-aware projection, true depth sorting, and genuine masked sprite compositing, the three ingredients of Ultimate's "Filmation" engine, built from scratch and proven to matter with an actual before/after screenshot of the bug masking exists to fix.
Height enters the projection
Raiders projected two numbers, a map x and y, onto a screen row and column. Isocubes projects three — x, y, and height — and height does something the other two don't: it moves an object up the screen without changing which diagonal it belongs to.
; col = ORIGCOL + (x-y)*2 ; row = ORIGROW + (x+y) - height
project:
ld a,c
sub d
add a,a
add a,ORIGCOL
ld l,a
ld a,c
add a,d
sub e ; <- the new bit
add a,ORIGROW
ld b,a
ld c,l
ret
A block's top face is drawn at this projected position; its left and right side faces are drawn as two more coloured rectangles filling the gap down to the ground. Three flat rectangles, three different tones, and the result reads as a solid cube — no perspective, no shading, just the same trick every one of these games used.
Depth sorting, proven by coincidence
Every object gets a depth key: x+y. Draw everything back-to-front by that key and nearer objects correctly land on top of further ones — the classic painter's algorithm, and the same one Filmation itself used. Rather than assert this abstractly, the demo scene is deliberately arranged so it's checkable: a flat tile at (0,0) and a height-2 block at (1,1) project to the exact same screen position, because the block's extra height exactly cancels its extra depth. Walk the hero to (0,0) and the block correctly rises up and covers it — only the very tip of its hat survives, exactly where the maths says it should.
Masking: the part that actually needs proof, not assertion
Here's the part worth being honest about. The first working version of this
engine's masked sprite blit produced zero visible difference from
a plain solid blit. Not "hard to see" — mathematically zero, because
every tile in the scene was a solid, unpatterned rectangle. Masking works by
computing dest = (dest AND NOT image) OR image, and when
dest is already all 1s, that expression simplifies to
NOT(image) OR image — which is 1 for every single bit,
always, regardless of what the sprite's shape is. A background with no
texture gives masking nothing to preserve.
The fix was to give the floor real bitmap texture — a dithered chequerboard instead of a solid fill — so there's an actual pattern sitting behind the sprite for masking to protect. With that in place, the difference is unmistakable:
Same sprite, same position, same background — the only difference between these two images is one routine. That's the whole case for masking, made visible rather than asserted:
; dest = (dest AND NOT image) OR image -- no separate mask table
; needed, since this sprite is one flat ink colour on a transparent
; background (mask = NOT image, computed on the fly with CPL)
mbr: ld hl,(scraddr)
ld a,(de)
inc de
ld c,a
cpl
and (hl)
or c
ld (hl),a
What this still doesn't do
In the interest of not overclaiming: attribute colour on the Spectrum is whole-cell, always — eight pixels by eight, one ink and one paper, no exceptions. Masking fixes the bitmap, letting a background's shape show through a sprite's transparent gaps correctly. It cannot give two different objects two different colours within the same shared cell. That's not a bug in this engine; it's the hardware. The real games worked around it by designing sprites and backgrounds that rarely needed to share a cell in colour-clashing ways — and where they occasionally couldn't avoid it, you can still see the seam if you know to look for it, in Knight Lore as much as anywhere else.
Try it, then build the room this doesn't have yet
- isocubes.tap — ready to run in Fuse or any .tap-capable emulator. Q A O P move, SPACE exits to BASIC anytime.
- isocubes.asm
— the full commented source: projection, depth sort, masked
blit and all. Build it yourself:
pasmo --tapbas isocubes.asm isocubes.tap - test_isocubes.py — the headless test suite, including the exact before/after masking comparison used above.
This is deliberately a small technical demo, not a game: one room, two blocks, no doors, no win condition. Everything Raiders needed — rooms, a guard, a clock — is a layer on top of what's here. The gap between "isometric-looking" and "isometric" turned out to be exactly three things: height in the projection, depth in the draw order, and a mask in the blit. All three fit comfortably in 48K, same as they always did.