Una cinta de casete de 1988, desmontada instrucción a instrucción. Los 40.449 bytes del juego están explicados al 100%.
Que solo el 13% sea código no significa que falte nada: este juego es 87% datos. Los gráficos, las tablas de animación y veintinueve mapas de pantalla de 512 bytes cada uno.
Si te terminas el juego habiendo hecho trampas, Topo no te deja disfrutar del final. La rutina que pinta la pantalla de victoria comprueba una bandera y, si está encendida, escribe un reproche encima:
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
Y no cae en un hueco cualquiera: pisa exactamente la línea donde el juego te invita a jugar a Alehop, el siguiente título de la casa. Al tramposo le retiran la invitación.
La primera explicación que se nos ocurrió fue que Topo había dejado la trampa «armada y esperando». Los datos dicen otra cosa.
El arranque inicializa quince variables seguidas —8F09,
8F0A… hasta 8F1C y 8F1D— y se salta justo la
siguiente. 0x8F1E es la única variable que el juego lee y nunca inicializa.
Si vale cero es porque la cinta carga relleno en esa zona: bytes sobrantes de las tablas de animación, de los que solo 3 de 160 son cero. Uno cae justo ahí, por casualidad.
O sea que esto no es una trampa astuta: es un bug con suerte. Escribieron la comprobación, se olvidaron de poner la variable a cero, y el azar del relleno les salvó de que su propio juego insultara a todos los jugadores honrados.
El único truco publicado para el juego apareció en MSX Book II
(Brasil, 1988) y dice POKE &HB4CC,0. No funciona: esa posición ya vale
cero. La correcta es &H84CC, donde está el DEC A que te
quita la vida. En la tipografía de matriz de puntos de aquellos libros, el
8 y la B son casi el mismo dibujo.
| Vidas antes | Vidas después | |
|---|---|---|
| Sin parche | 9 | 8 |
Con 0x84CC = 0 | 9 | 9 |
Comprobado en el emulador forzando la rutina de perder vida.
El nivel submarino no usa tiles distintos. En el chip de vídeo del MSX el color 0 no es negro: es transparente, y por debajo se ve un único registro de color de fondo. Al entrar al nivel 4 el juego escribe un 12 en ese registro y la pantalla entera se tiñe de golpe.
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
Cambiaron la ambientación de un nivel entero con un byte.
Los cofres y calaveras que sueltan objetos no funcionan como parece. El juego guarda para cada pantalla una lista de coordenadas, y comprueba si tu disparo cae ahí — sin mirar qué hay dibujado.
De los 30 puntos que existen en todo el juego, solo 17 están sobre algo visible. Los otros 13 son invisibles, en el aire o sobre decorado. Las alitas que permiten volar solo salen de dos de ellos, ambos sin nada que los delate: el tile de las alitas no aparece ni una sola vez en los 29 mapas.
Y hay trampa: en la pantalla 26, uno de esos puntos ocultos suelta un tile mortal. Disparar a todo tiene su precio.
Los textos guardados en el juego dicen cosas como
PVES SERES HORRIBLES o MVSICA:GOMINOLAS, y sin embargo en
pantalla se leen bien. El motivo es que los códigos de la U y la V dibujan exactamente
el mismo glifo: da igual cuál escribas.
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
El ret z está antes del ld que guarda. Con 9
vidas, coger otra no hace nada. El tope real es 9.
Al recoger algo, el juego lo borra del mapa buscándolo con
CPIR desde el principio de la pantalla, no desde donde estás.
Encuentra el primero de ese tipo, sea el que sea.
En la pantalla 27 hay seis vidas extra a la vista: cojas la que cojas, desaparece siempre la de arriba a la izquierda mientras la que acabas de tocar sigue dibujada.
El juego coloca la pila una sola vez, al arrancar. Pero el game over reinicia con un salto que entra después de esa instrucción, así que la partida nueva empieza con el puntero donde lo dejó la anterior. Medido en el emulador con ocho partidas seguidas:
| Reinicio | Puntero de pila |
|---|---|
| 1 | 0x8FFF |
| 3 | 0x8FFD |
| 5 | 0x8FF9 |
| 8 | 0x8FD7 |
Baja y no vuelve a subir. Las variables del juego empiezan en 0x8F00, así que tras suficientes partidas sin resetear la pila acabaría pisándolas. En 1988, con siete minutos de carga por partida, era difícil llegar ahí. Hoy es trivial.
Cada pantalla es un bloque de 512 bytes: 32 columnas por 16 filas, un byte de tile por casilla, sin comprimir. Debajo de cada una, su dirección en memoria.
La recompensa por superar las 28 pantallas.
Un desensamblado es fácil de hacer mal. Basta con leer unos gráficos como si fueran instrucciones y ya tienes páginas de código inventado que parece perfectamente real. De hecho nos pasó: un detector automático marcó como código del juego unos restos de otra compilación, se comprobó a ojo que parecían correctos, y hubo que dar marcha atrás al descubrir que nadie los llama.
Por eso el proyecto se apoya en cuatro comprobaciones automáticas:
| Comprobación | Qué caza |
|---|---|
| Tests | 36, incluidos unos que verifican que la documentación no miente |
| Reproducibilidad | Que el fuente vuelva a dar el binario original byte a byte |
| Sanidad del trazado | Que los gráficos no se hayan marcado como código; lo anterior no lo detecta |
| Presupuesto de bytes | Que no quede ni un byte sin explicar |
Y en algo más importante: buena parte de lo que se afirma aquí no se dedujo leyendo, sino observando el juego correr. Con el emulador openMSX se pusieron vigilantes sobre posiciones de memoria para ver qué código las tocaba, se muestreó el procesador durante la partida para saber qué se ejecuta de verdad, y se capturó la pantalla real para contrastarla con lo que dice el código.
Así se identificó el contador de vidas, por ejemplo: no por deducción, sino comprobando que una sola rutina lo lee y que esa rutina escribe el resultado en la casilla exacta del marcador donde se ve el número.