War in Middle Earth

Araubi asked for three things on the forum and six came out. 1,396 bytes across 130 places, each one checked against the bytes it expects before writing: nothing shifts, and the build fails if a single byte changes outside the table. The new code lives inside the ZX beeper engine this port brought across whole and never calls, the 19 strings that finish translating the game fit without moving a single letter, and the whole map comes repainted: 122 of the 128 8×8 drawings, done in a PNG and turned into patch entries by the tool itself.

Melbourne House / Dro Soft · 1989 · MSX1 cassette · 62,261 bytes · 1,396 bytes changed, 0 shifted · IPS patch, unofficial

There is not a single screenshot here. There cannot be: the game re-uploads the screen to the VDP constantly, and two photographs of the same state three seconds apart already differ in 37 % of their pixels. What you see is drawn from the ZX Spectrum screen buffer the game keeps in RAM, dumped at a fixed instant and painted with the cartridge's own colour table. The control: two runs of the same state give a pixel-identical image, so whatever changes between «without» and «with» is the patch and nothing else.

The patch in numbers

1,396bytes changed
130places touched
0bytes shifted
0bytes outside the table
137bytes of new code
376bytes of new text
122/128map tiles repainted
1,718bytes of IPS patch
87checks passing

The six things it does

1 · Enemy units become visible

The map keeps a “someone is here” bit per cell, and RECENTRA_EL_MAPA (0x7FAC) re-plants it unit by unit. But its loop stops at unit 0x78, exactly where the enemy side begins: the enemy never gets planted, and so never gets drawn. That was the game's fog of war, and it was an oversight shaped like a cp 078h.

One byte, at 0x7FD1: the limit becomes 0x00 and the loop walks all 256 slots. Measured: 136 enemy units get planted where zero did before.

2 · Every attribute shows its number

A unit's sheet lists six qualities — Valioso, Hábil, Duro, Bravo, Enérgico, Decidido — as adverb plus adjective (“very brave”), never as a number, and you cannot compare two units with that.

A new 76-byte routine reads the six values from 0xC000-0xC300 and writes them at column 20 of each line. A three-byte trampoline at 0x708A hooks it, where the sheet used to do ld hl,0x5FBD right before painting itself. Checked against RAM: all six match.

3 · The Ring's deadline, next to the ring

This request was misread the first time. It is not the bearer's 0xC300 counter: it is a countdown of months. The game's clock decrements the operand at 0x8333 — which 0x7F4F sets to 255 at the start — prints “El Anillo corrompe al que lo usa.” and does jp z,DERROTA.

That number is literally how long you have left, and the game shows it nowhere. The ring in the sheet is character 0x5F at 0x7C46; the three columns to its left were free, and that is where it goes now.

4 · The enemy gets the Eye of Sauron

With change 1 the enemy showed up… wearing your helmet, which is half a fix: you see them, but you cannot tell which is which. And the drawing routine only ever looks at the map byte, so it cannot know which side a unit belongs to.

Bit 5 of that byte was free — measured: zero uses across all 13,260 cells — and that is now the side marker. The icon is four new tiles at the tail of the 0x9E00 table, indices 111 to 114, which were all zeros.

5 · The text finishes its translation

Animagic's conversion left the map's place names in English and three race names truncated: “Brujo”, “Elf” and “Hum”. 19 strings change — ten place names, one unit name, the three races, the sheet's four adjectives and its last line — and not a byte moves.

A place record carries the size of its signpost (width<<4 | rows) and the text fills it whole, so the new name has to measure the same: Cavada + Grande fills the 7×2 sign that held Michel /Delving. The race names live in two lists walked by counting terminator bits, so inside a list a string may change length: “Brujo ” → “Mago” frees two bytes and appears twice in each list — exactly the four that “Elf” → “Elfo” and “Hum” → “Hombre” need.

6 · The map, repainted whole

The map's 128 8×8 drawings are exported to a PNG at full size, repainted in any image editor, and 122 of the 128 go back into the cassette as patch entries, with orig and nuevo the same length, exactly like the hand-written ones. The six that do not change are the ones whose drawing comes back identical to what was there.

And that is where it turned out that the canvas was lying: each tile carries a Spectrum attribute glued behind it, but the game never sends it to the screen — ATRIBUTO_A_COLOR (0x049F) translates it first, with two eight-colour MSX tables. So only twelve of the MSX's fifteen colours can be painted, and the tool says so and substitutes if an impossible one gets in. The text now sits on the same khaki as the frames: one byte at 0x763F.

And one trap that cost a whole run

The two-by-two artwork table at 0x77B5 has six all-zero entries and they look like plenty of room. They are not: PINTA_LO_DE_ENCIMA picks its entry with an and 00fh over the terrain's low nibble, so indices 0x00-0x0F already have an owner.

Putting the Eye in slot 0x03 gave it to all 447 cells of terrain type 3. The way out was not to use an index at all: point HL at our own list of four codes and enter the stamper past its arithmetic, at 0x7720.

The same cell, without the patch and with it

Each pair is the same game, the same cell and the same instant, loading the original cassette and the patched one. The “now” ones are dumped from today's patched cassette, with the map already repainted.

WITHOUT THE PATCH, cell 036N/096E — three of Sauron's hosts are right there and not one is drawn. That was the fog of war
WITHOUT THE PATCH, cell 036N/096E — three of Sauron's hosts are right there and not one is drawn. That was the fog of war
THE FIRST PATCH — the enemy did show up, but wearing your own helmet. You see them and cannot tell them apart. Twelve character cells change, and not one colour attribute
THE FIRST PATCH — the enemy did show up, but wearing your own helmet. You see them and cannot tell them apart. Twelve character cells change, and not one colour attribute
THE EYE OF SAURON — the three hosts wear it and the two friendly units in the corner keep their helmet. Still with the cassette's own artwork
THE EYE OF SAURON — the three hosts wear it and the two friendly units in the corner keep their helmet. Still with the cassette's own artwork
NOW — the same cell, the same instant, with the map repainted. Against the original cassette 735 of the 768 character cells change, and 560 attributes: the map is another map
NOW — the same cell, the same instant, with the map repainted. Against the original cassette 735 of the 768 character cells change, and 560 attributes: the map is another map
WITHOUT THE PATCH — Gandalf's sheet:
WITHOUT THE PATCH — Gandalf's sheet: "Es muy Habil". Very... how much
WITH THE PATCH — the same sheet with a number on every line: Energico 158, Decidido 192, Firme 010, Virtuoso 008, Valiente 010, Fuerte 006. And the last line, whole:
WITH THE PATCH — the same sheet with a number on every line: Energico 158, Decidido 192, Firme 010, Virtuoso 008, Valiente 010, Fuerte 006. And the last line, whole: "Aliado a la Comunidad"
WITHOUT THE PATCH — Frodo's sheet, the Ring-bearer. The ring says who carries it, and nothing else
WITHOUT THE PATCH — Frodo's sheet, the Ring-bearer. The ring says who carries it, and nothing else
WITH THE PATCH — 255, right beside the ring: the months left before you succumb. One less every month; at zero, Sauron's screen
WITH THE PATCH — 255, right beside the ring: the months left before you succumb. One less every month; at zero, Sauron's screen
THE FRIENDLY ICON, enlarged and read off the already patched tape: tiles 81 to 84 of the 0x9E00 table, repainted as a shield
THE FRIENDLY ICON, enlarged and read off the already patched tape: tiles 81 to 84 of the 0x9E00 table, repainted as a shield
THE EYE, also off the patched tape: tiles 111 to 114, which were all zeros. Now in dark red, which is a colour the Spectrum attribute can actually ask for
THE EYE, also off the patched tape: tiles 111 to 114, which were all zeros. Now in dark red, which is a colour the Spectrum attribute can actually ask for
WITHOUT THE PATCH — the sign says
WITHOUT THE PATCH — the sign says "Dale" and the sheet "Formacion de 005 Hum", "Hum:caracter:" and "No Valioso". The conversion stopped half way
WITH THE PATCH, same cell, same unit —
WITH THE PATCH, same cell, same unit — "Valle", "Formacion de 005 Hombres", "Hombre:caracter:" and "No Virtuoso". And the text on the frames' khaki, which is a single byte
WITHOUT THE PATCH — the two-row signpost:
WITHOUT THE PATCH — the two-row signpost: "Michel" on top, "Delving" below, seven columns by two rows
WITH THE PATCH —
WITH THE PATCH — "Cavada" and "Grande" in the same 7x2 sign, which is what forces the padding with spaces. And the Shire repainted underneath
THE MAP'S 128 TILES as they come on the cassette, drawn byte by byte and with the MSX's colours — which are not the Spectrum's, even though the attribute is
THE MAP'S 128 TILES as they come on the cassette, drawn byte by byte and with the MSX's colours — which are not the Spectrum's, even though the attribute is
THE SAME ONES, REPAINTED: 122 of the 128 make it into the patch. Magenta is empty space
THE SAME ONES, REPAINTED: 122 of the 128 make it into the patch. Magenta is empty space
THE WHOLE MAP, drawn from the cassette: all 128 x 100 cells with the original tiles. Not a screenshot, nor a mosaic of screenshots: it is the game's own drawing engine repeated cell by cell
THE WHOLE MAP, drawn from the cassette: all 128 x 100 cells with the original tiles. Not a screenshot, nor a mosaic of screenshots: it is the game's own drawing engine repeated cell by cell · see it full size (2048 × 1600)
THE SAME ONE, PATCHED. The compressed map comes off the cassette (0x16ED bytes of count/value pairs at 0xCC00) and the artwork from the repainted canvas. There is not a single unit here: this is Middle Earth as a game starts
THE SAME ONE, PATCHED. The compressed map comes off the cassette (0x16ED bytes of count/value pairs at 0xCC00) and the artwork from the repainted canvas. There is not a single unit here: this is Middle Earth as a game starts · see it full size (2048 × 1600)