War in Middle Earth

A ZX Spectrum conversion that brought across the whole tape system, the colour attributes glued to every tile, and even the beeper sound engine… which here nothing ever calls. The four places that ask for a sound effect all land on a bare ret, and of the MSX's PSG only the two joystick registers are ever touched: this game is silent.

Melbourne House / Dro Soft · 1989 · Tape, 62,261 bytes · MSX1 · 64 KB, no BIOS · Converted by Animagic S.A.

There is not a single screenshot here. Every picture is drawn from the tape: the bytes are read in the ranges the listing delimits and developed the way the game develops them, with their masks and with the colour attribute each drawing carries glued to it. That drawings come out instead of noise is the proof the ranges have been read correctly. The listing and the numbers come from the binary and are reproduced with make.

The tape in numbers

100%of the tape explained
5listings
767routines identified
11,814bytes of code
50,191bytes of data
0bytes unidentified
29.7%comment density
0routines below 10%

What turned up when we took it apart

This game is silent, and you can point at where the sound stopped

The conversion brought the Spectrum's whole sound engine across: it sits at 0x6600, drives the notes through out (0xFE) with bit 4, and behind it are five twenty-one-byte effects at 0x636F-0x63D7.

Nothing ever calls it. Not one instruction in the five listings points at 0x6600. And the four places that ask for an effect — 0x5F90, 0x647A, 0x6AA1 and 0x833D — call 0x65FF, which is a bare ret.

Of the MSX's PSG only two registers are ever written, 7 and 14, and both are for reading the joystick (0x046E). The one routine that would know how to write a note to the PSG, 0x04F2, is never called either. There is no third path: the game makes no sound.

The loading screen, whole, pulled out of the tape

The 12,388 bytes of block [08] are a complete SCREEN 2 picture: 6,144 of patterns and 6,144 of colours, and the sums close on their own (0x88B8 + 100 + 6,144 + 6,144 = 0xB91C, the exact end of the block).

Drawn, it says what the game says about itself: MAELSTROM GAMES LTD. PRESENTS, War in Middle Earth, Mike Singleton and, bottom right, CONVERSION by ANIMAGIC sa. The menu adds the rest: “Programado por C.J.Pink”.

The map tiles are nine bytes long, and that is the conversion's fingerprint

An MSX tile takes eight bytes. This game's map tiles take nine: the eight lines of the drawing and, glued behind them, a ZX Spectrum attribute — ink in bits 0-2, paper in 3-5, bright in 6.

0x75C7-0x75EB read them when the grid code has bit 7 set. The conversion did not redraw the artwork: it brought the Spectrum's across with its colour already attached and translates it on the fly, at 0x049F.

0x62FF is not the cursor's artwork: it is what the cursor covers up

Those 24 bytes were documented as the cursor mark's drawing. They are not. 0x6580 loads 0x62FF into the alternate HL and the loop at 0x65B4, for each of the column's three bytes, first reads the screen (ld a,(iy+n)), copies it there, and only then composites the cursor on top.

0x64DC walks it back to erase. The real artwork is at 0x6345, with its mask behind it (ld ix,0x6345 at 0x657B). And the 0xAD bytes the tape carries there are not a drawing: they are whatever was under the cursor the day it was saved.

The battle board is built on top of the menu, and it is a draughtboard

Battle uses 0x5E00-0x62FF, which is where the menu's code lives: once a game has started, the menu and its text are scrap paper. 0x8E08 says so with its ld b,0x5E, and the ldir at 0x904D wipes the lot before every battle.

And the board is a draughtboard: the four movement routines (0x893E and friends) always change both coordinates at once, so the parity of x+y never changes. Deployment rejects any pair whose parities differ, and the obstacles go on exactly the squares of the other colour.

The friend-or-foe filter is an opcode switch

To walk the other side's units the game does not use a flag: it rewrites the instruction. 0x8980-0x8982 writes a 0xD0 into 0x8AF3, and 0x8991-0x8993 writes a 0xD8.

0xD0 is ret nc and 0xD8 is ret c. The same routine, with the same threshold, hands back one side's units or the other's depending on which opcode was written over it a moment earlier.

The map travels compressed, and gets recompressed before every battle

0x9366 moves the map's 0x16ED compressed bytes out to 0x4000 with an ldir and expands them into the 0x33CD at 0xCC00, reading count/value pairs. 0x5E28 calls it at boot: the map arrives from tape already compressed.

And 0x9394 does the reverse before every battle, packing it back down to 0x16EC bytes. That is not to save tape: it is to make room. What it frees, 0xE2EC-0xFFFF, is exactly where the battle buffers live.

And Spectrum leftovers that mean nothing on an MSX

The menu's control mode 2 — the Spectrum's Interface Two — does not exist here: its pointer, at 0x06D7, is 0x0000, so the menu jumps from 1 to 3.

The ZX keyboard block at 0x5F75-0x5FB7, which reads port 0xFE, is dead code. And in the save-game routine the break-key check was never converted: 0x0930 does in a,(0xFE), which on an MSX is not the keyboard.

What the tape draws

Under each caption is the address it comes from. They are all rebuilt by make imagenes and no emulator is needed.

DRAWN from 0x891C and 0xA11C — the whole loading screen: 6,144 bytes of patterns and 6,144 of colour, exactly as the loader uploads them. This is the game saying who made it
DRAWN from 0x891C and 0xA11C — the whole loading screen: 6,144 bytes of patterns and 6,144 of colour, exactly as the loader uploads them. This is the game saying who made it
DRAWN from 0x9E00 — the 128 map tiles, in the colour their Spectrum attribute names. They are NINE bytes each: eight of drawing and the attribute glued behind
DRAWN from 0x9E00 — the 128 map tiles, in the colour their Spectrum attribute names. They are NINE bytes each: eight of drawing and the attribute glued behind
DRAWN from 0xA2E8 — the battle sprites stacked in pairs. Measured: each one is 32 bytes of 16x8 in mask/drawing pairs. That consecutive entries fit together into 16x16 figures is a reading of the picture, not a routine anyone has found
DRAWN from 0xA2E8 — the battle sprites stacked in pairs. Measured: each one is 32 bytes of 16x8 in mask/drawing pairs. That consecutive entries fit together into 16x16 figures is a reading of the picture, not a routine anyone has found
DRAWN from 0xA2E8 — the same 176 sprites, one by one. The byte order is not obvious: they go in mask/drawing pairs and in zig-zag, because the routine at 0x887B writes left, right, down a line and back
DRAWN from 0xA2E8 — the same 176 sprites, one by one. The byte order is not obvious: they go in mask/drawing pairs and in zig-zag, because the routine at 0x887B writes left, right, down a line and back
DRAWN from 0xC800 — the 128 eight-byte characters. The first 33 are all zero: the first with any drawing is 0x21, and from there come frames, arrows, digits, upper and lower case
DRAWN from 0xC800 — the 128 eight-byte characters. The first 33 are all zero: the first with any drawing is 0x21, and from there come frames, arrows, digits, upper and lower case