Temptations logo

A 1988 cassette tape, taken apart one instruction at a time. All 40,449 bytes of the game are accounted for.

Topo Soft · 1988Code Luis López NavarroMusic GominolasMSX only

The game in numbers

100%of the binary accounted for
137routines identified
29screen maps
5,214bytes of code
35,235bytes of data
0bytes unidentified

Only 13% being code doesn't mean anything is missing: this game is 87% data. The graphics, the animation tables and twenty-nine screen maps of 512 bytes each.

What turned up when we took it apart

The cheater's punishment

Finish the game after cheating and Topo won't let you enjoy the ending. The routine that draws the victory screen checks a flag and, if it's set, writes a reproach over it:

CASTIGO_TRAMPAS:
    ld a,(08f1eh)    ; bandera de tramposo
    cp 000h
    jp z,BUCLE_FINAL ; limpia -> final normal
    ld hl,07f94h     ; si no: "POR QUE NO PRUEBAS SIN POKES"
    ld de,019e0h     ; encima de la ultima linea del area de juego
    ld bc,00020h
    call 0005ch      ; y a la pantalla

And it doesn't land in empty space: it overwrites exactly the line where the game invites you to play Alehop, the company's next title. The cheater gets the invitation withdrawn.

Legit ending
Ending with the trap

And they got away with it by luck

Our first guess was that Topo had left the trap «armed and waiting». The data says otherwise.

Start-up initialises fifteen variables in a row —8F09, 8F0A… through 8F1C and 8F1D— and skips the very next one. 0x8F1E is the only variable the game reads and never initialises.

If it holds zero, that's because the tape loads filler into that area: leftover bytes from the animation tables, of which only 3 out of 160 are zero. One happens to land right there.

So this isn't a clever trap: it's a lucky bug. They wrote the check, forgot to clear the variable, and the luck of the filler saved them from their own game insulting every honest player.

A 1988 typo

The only published cheat for the game appeared in MSX Book II (Brazil, 1988) and reads POKE &HB4CC,0. It does nothing: that address already holds zero. The right one is &H84CC, where the DEC A that takes your life lives. In the dot-matrix typeface of those books, 8 and B are nearly the same shape.

Lives beforeLives after
Unpatched98
With 0x84CC = 099

Confirmed in the emulator by forcing the life-loss routine.

Why the level 4 water is green

The underwater level doesn't use different tiles. On the MSX video chip, colour 0 isn't black: it's transparent, and a single backdrop register shows through. On entering level 4 the game writes a 12 into that register and the whole display changes at once.

ENTRA_NIVEL4:
    call EMPIEZA_NIVEL
    ld a,00ch        ; color 12 = verde
    ld (0f3ebh),a    ; registro de color de fondo
    call 00062h      ; BIOS CHGCLR: aplicalo
    ld a,001h
    ld (08f11h),a    ; y enciende el modo flotar

They re-skinned an entire level with one byte.

Thirteen items you can't see

The chests and skulls that drop items don't work the way they look. The game keeps a list of coordinates per screen and checks whether your shot lands on one — without looking at what's drawn there.

Of the 30 spots in the whole game, only 17 sit on something visible. The other 13 are invisible, in mid-air or over scenery. The wings that let you fly come from just two of them, both over plain background: the wings tile never appears in any of the 29 maps.

And there's a trap: on screen 26, one of those hidden spots drops a lethal tile. Shooting at everything has a price.

U and V are the same drawing

The text stored in the game reads things like PVES SERES HORRIBLES or MVSICA:GOMINOLAS, and yet it reads correctly on screen. The reason is that the codes for U and V draw exactly the same glyph: it makes no difference which one you type.

Title screen

Bugs the game has carried since 1988

The lives cap never reaches 10

DA_VIDA:
    ld a,(08f12h)   ; A = vidas actuales
    inc a           ; una mas
    cp 00ah         ; ¿ha llegado a 10?
    ret z           ; SI -> se va... SIN GUARDAR
    ld (08f12h),a   ; NO -> guarda

The ret z sits before the ld that stores. With 9 lives, picking up another does nothing. The real cap is 9.

Picking up an item deletes another one

When you collect something, the game clears it from the map by searching with CPIR from the start of the screen, not from where you are. It finds the first one of that kind, whichever it is.

Screen 27 has six extra lives on display: whichever you take, the top-left one always disappears while the one you actually touched stays drawn.

Every lost game eats a piece of the stack

The game sets the stack once, at start-up. But game over restarts with a jump that lands after that instruction, so the new game begins with the pointer wherever the previous one left it. Measured in the emulator over eight games in a row:

RestartStack pointer
10x8FFF
30x8FFD
50x8FF9
80x8FD7

It goes down and never comes back up. The game's variables start at 0x8F00, so after enough games without a reset the stack would run into them. In 1988, with seven minutes of loading per game, getting there was hard. Today it's trivial.

All 29 screens

Each screen is a 512-byte block: 32 columns by 16 rows, one tile byte per cell, uncompressed. Its memory address is below each one.

Level 1·The necropolis

Level 1
10x9000
Level 1
20x9200
Level 1
30x9400
Level 1
40x9600
Level 1
50x9800
Level 1
60x9a00
Level 1
70x9c00

Level 2·The forest

Level 2
10x9e00
Level 2
20xa000
Level 2
30xa200
Level 2
40xa400
Level 2
50xa600
Level 2
60xa800
Level 2
70xaa00

Level 3·The ruins

Level 3
10xac00
Level 3
20xae00
Level 3
30xb000
Level 3
40xb200
Level 3
50xb400
Level 3
60xb600
Level 3
70xb800

Level 4·The seabed

Level 4
10xba00
Level 4
20xbc00
Level 4
30xbe00
Level 4
40xc000
Level 4
50xc200
Level 4
60xc400
Level 4
70xc600

Screen 29·The ending

The reward for clearing all 28 screens.

The ending
290xc800

How it was done, and why you can trust it

A disassembly is easy to get wrong. Read some graphics as if they were instructions and you get pages of invented code that looks perfectly real. It happened to us: an automatic detector flagged leftovers from another build as game code, a quick eyeball said they looked right, and it had to be undone once we found nothing ever calls them.

That's why the project leans on four automatic checks:

CheckWhat it catches
Tests36 of them, including a set that verifies the docs aren't lying
ReproducibilityThat the source rebuilds the original binary byte for byte
Trace sanityThat graphics weren't marked as code — the check above misses this
Byte budgetThat not a single byte is left unaccounted for

And on something more important: much of what's claimed here wasn't deduced by reading but by watching the game run. Using the openMSX emulator we set watchpoints on memory to see which code touched what, sampled the processor during play to know what actually executes, and captured the real display to check it against what the code says.

That's how the lives counter was identified, for instance: not by deduction, but by confirming that a single routine reads it and that this routine writes the result into the exact status-bar cell where the number appears.