Stardust (1987)

A 1987 cassette tape, taken apart block by block. Inside, it's a ZX Spectrum conversion that brought the tape system across with it, not just the graphics.

Topo Soft · 1987 · A ZX Spectrum conversion · Multiload from tape · MSX1 · 64K

The game in numbers

100%of the binary owned
106routines identified
7+1ship zones, plus one on foot
20,076bytes of code
73,785bytes of data
0bytes unidentified

What turned up when we took it apart

This isn't an MSX tape

MSX games are recorded in KCS blocks, the system's own format. Not Stardust: its four data blocks are ZX Spectrum blocks, with a flag byte, the data and an XOR check at the end, and all four carry it correctly.

The loader isn't native either: it's a reimplementation of LD-BYTES, the Spectrum ROM's own load routine, with the same register interface.

The loader ships with a back door for trainers

Before starting the game, the loader saves 94 bytes to high memory and takes a look at them: if they begin with three 0xC9, it treats them as a list of patches and applies them to the game it just loaded, address and value, one after another.

And the arithmetic works out on its own: three bytes of signature, one counter byte, and thirty patches of three bytes each, ninety-four in total. It's sized for exactly thirty pokes, which happens to be exactly what the magazine loaders of the day were using.

Two games on one tape

Clearing the last ship zone sends the game back to the cassette for a second part where the character carries on on foot. And it doesn't reuse the loader's routine for that, even though it's still alive in memory: it brings its own, which starts the tape motor and reads the data bit off the sound chip.

The two programs don't even share an engine. In the first part each object carries an 8-byte structure with a pointer to its governing routine; in the second, the enemies live in light 5-byte tables moved by fixed loops —four walkers at most, flyers kept apart. There are 46-byte objects in the second part too, but they're not enemies: they're the sound interpreter's three channels.

What the MSX forced them to change

The Spectrum writes straight into its screen memory, which is ordinary RAM. On the MSX, video memory sits behind the graphics chip, so it has to go out through a port, byte by byte.

Which is why this version carries a screen buffer the original never needed: 3840 bytes at 0x4000-0x4EFF, 24 wide by 160 tall, sent to VRAM in three bands, column by column. Twenty-four bytes are 192 pixels, narrower than the screen, and that's why the frame down the sides never moves: the surplus is vertical, which is exactly where it scrolls.

And those axes are easy to read backwards. The dump's ld b,028h looks like it's saying "40 columns", but it's the inner loop, the one collecting 40 bytes from a single column in steps of 24. Drawing it settles it: split 24 at a time and the high-score table comes out legible; 40 at a time, noise.

Sprites drawn by hand

The MSX has hardware sprites, but they're not used here: sprites get drawn in software, the Spectrum way, shifting the image bit by bit and compositing it with AND and OR.

Both halves of the game carry the same routine for that, copied and relocated from one to the other. The sprite painter is 198 bytes long, and pair its two halves correctly and the only differences are ten relocated addresses and one loose byte, the bottom clip.

No entry point falls inside a picture

Seed the tracer with badly anchored routines and coverage can inflate in one go without anyone noticing: the binary still reassembles, because it's the same bytes and only the reading changes; the budget still closes; and a trace check that only looks at a file of exceptions won't catch it either. So there's a rule for exactly this, running in the gate alongside the rest: no entry point may fall inside a range declared as data.

And then there's the other proof, actually playing the game. Replaying a complete 38-minute recording —courtesy of Araubi— and noting where the processor goes, it turns out that of the 1489 addresses the game actually executes, the tracer already reaches 1444 on its own; the ones it misses become entry points, each with its sample count beside it. The ship block's real coverage lands at 23.0%, and the second part's at 28.6%.

The graphics

None of this is a screen capture: it's drawn straight from the binary's own data, using the same geometry the game itself uses. Which makes it a check as much as an illustration, because if the block's layout were wrong, what would come out is noise, not a recognisable tileset.

The 111 scenery tiles
The 111 scenery tiles
The 83 sprites
The 83 sprites
The loading screen, signed CANO
The loading screen, signed CANO
The charset: 59 characters
The charset: 59 characters