Una cinta de cassette de 1987, desmontada línea a línea. Los 34.239 bytes que trae están explicados, y el juego resultó estar escrito en BASIC.
Esos bytes de código no son lo que uno espera. Solo 997 son código máquina; los otros 3.935 son un programa MSX-BASIC tokenizado, que es el juego. Y los 1.566 sin identificar no son un hueco en el mapa: todos los bytes de la cinta tienen asignado su papel. Son aquellos cuyo contenido no hemos sabido nombrar, y está demostrado que el juego no los enseña nunca. Se buscan, vivos o muertos: lo descartado, con qué medida, y cómo intentarlo.
El bloque que trae el juego no es código máquina: es un programa MSX-BASIC tokenizado de 63 líneas, con solo 45 bytes de Z80 al final para arrancar el intérprete. Todo lo que tenía que ir rápido lo hacen cuatro rutinas que se llaman con USR, y la más importante de ellas mide diecisiete bytes.
Así que aquí la garantía de siempre no vale. En vez de reensamblar, el listado comentado se vuelve a tokenizar y tiene que dar los bytes de la cinta exactos. Los da.
El marcador viene dibujado con seis ceros, y la subrutina que escribe la puntuación solo pone cuatro cifras: las columnas 5 y 6 se quedan con el cero que traen de fábrica. Abatir a un bandido suma 20 al contador y en pantalla se lee 2000.
La línea 730 hace sonar algo y vuelve al bucle, y en todo el programa no hay nada que salte ahí: ni un GOTO, ni un GOSUB, ni un THEN, y tampoco está en el reparto de la línea 640. La línea de antes acaba en un salto, así que tampoco se cae dentro. Es un resto de una versión anterior que se quedó grabado.
Al perder una vida se vuelve a la línea 600, no a la 599 — y la 599 es la que recarga las cuarenta balas. Son por nivel, no por vida. Si te quedas sin ellas, la línea 300 se limita a hacer RETURN sin sonar siquiera: ya no puedes abatir a nadie, y las vidas que te queden se van una detrás de otra mientras miras.
En la cinta, la primera línea viene numerada 65535, por encima del mayor número de línea que MSX-BASIC admite, y el arranque en Z80 le escribe un 4 encima. Sin ese parche el programa se rompe: comprobado en el emulador, el primer GOSUB aborta con Undefined line number in 520. Y lo que hace esa línea es POKE &HFBB1,1, que desactiva CTRL+STOP: el juego en marcha no se puede parar y, por tanto, tampoco listar.
En el área de variables del intérprete, la cinta trae once bytes con la forma exacta de una entrada de variable de MSX-BASIC: una I de doble precisión que vale 37025. Este programa no usa ninguna: usa I%, entera. Detrás hay 285 bytes de RAM sin inicializar que siguen el patrón de encendido de la máquina.
Y ese patrón delata otra cosa. Depende de la dirección absoluta, y encaja en 282 de los 285 (98,9 %) en 0x8000 pero solo en 226 (79,3 %) en 0x83E8: en la máquina del programador el juego ya vivía en 0x8000, y la dirección de carga de la cinta es solo un desvío para no pisar al cargador mientras carga.
No son capturas de pantalla. Están dibujadas a partir de los datos del propio juego, siguiendo paso a paso lo que hace el programa para montar la pantalla. Cada nivel es un mapa de 32 columnas más alto que la pantalla: el juego enseña una ventana de 16 filas y lo que mueve es esa ventana.
Aquí no hay carga turbo: la cinta son bloques estándar, así que los bytes salen directamente del fichero y no hace falta ningún emulador para decodificarlos. Para lo que sí se usa el emulador es para demostrar. La imagen de memoria del juego se reconstruye a mano —uno de los bloques se recoloca solo al arrancar— y después openMSX carga la cinta de verdad y se vuelca su RAM en el instante exacto en que el juego arranca. Las dos coinciden byte a byte.
A partir de ahí, el código se traza siguiendo el flujo desde los puntos de entrada conocidos, nunca desensamblando en línea recta: 18 KB de gráficos leídos como instrucciones desalinearían todo lo demás. Las zonas que se sabe que son datos se declaran prohibidas, porque una sola semilla mala mete al trazador en los dibujos e infla la cobertura con basura.
Dos comprobaciones distintas guardan el resultado. Reproducibilidad: cada listado tiene que devolver el binario original —reensamblando, en el código máquina; volviendo a tokenizar, en el BASIC—. Y un presupuesto: cada byte tiene que ser o código trazado, o un rango de datos con nombre en las notas. La segunda existe porque la primera no ve los errores de interpretación: si unos gráficos se marcan como código, los bytes siguen saliendo idénticos y el único que miente es el listado.