Cosas que sólo aparecieron haciendo esto, y que leer el código no habría cazado.
Trabajo en curso: mira Lo que falta.
La construcción estaba verde. La ROM salía byte a byte como se pedía, todas las comprobaciones pasaban — y la pantalla de recuento tenía una banda cian repitiendo «LT» cruzándola de lado a lado, en la fila 8.
La celda 0x0DF parecía libre según la regla que usaba todo el reparto: es la fila del medio de una ficha, y ésas no se dibujan en el tercio central. Eso es verdad. Lo que también es verdad, y nadie había mirado, es que la pantalla de recuento la usa veinticuatro veces en la fila 8 como el borde de arriba de una rejilla de cajas. Allí no se ve:
original : patron FF 00 00 00 00 00 00 00 color 10
una raya, tinta negra sobre fondo transparente
parche : patron 00 8E 84 84 84 84 84 E4 color 17
el digrafo LT, negro sobre CIAN
Métele una letra a algo invisible y lo invisible aparece.
El arreglo inmediato fue mudar ese dígrafo a la celda 0x0FA, que ningún volcado dibuja en ese tercio — y que es segura por una razón que hubo que leer en el binario, no suponerla. El miedo era la ficha indicadora del dora: como es aleatoria, si se copiara al tercio usando sus propios números de celda, podría pisar cualquiera. No lo hace. pinta_los_dos_indicadores, en 0x7024, lee el dibujo del tercio de abajo y lo escribe en destinos fijos —ld de,02850h y ld de,028d0h, o sea las celdas 0x0A-0x0F y 0x1A-0x1F—. Sea cual sea el dora, nunca llega al reparto.
El arreglo de verdad es make pantallas, que se cuenta en Cómo funciona. Y la lección es más vieja que este parche y sigue siendo cierta: una comprobación que sólo lee los bytes no puede ver la pantalla.
Cuando una mano acaba sin que gane nadie sale un cartel, y antes de dibujarlo el cartucho borra las celdas que va a tapar. Esas celdas estaban elegidas para tapar exactamente lo que ocupaban los rótulos japoneses. En inglés los rótulos están en otro sitio, así que lo que sobrevivía al borrado era una «D» pegada a EAST 1, una «A» suelta encima de la caja y un DORA colgando debajo.
Con el borrado bueno el bloque pasa de 39 bytes a 40 y ya no cabe en su hueco, así que se reubica y se reapunta el que lo carga.
De aquí salieron dos cosas que conviene guardar. La primera: antes de "arreglar" un fallo visual, medir de dónde sale el dibujo. La segunda es la trampa que va detrás de toda reubicación — lo que siga pidiendo el bloque por su dirección vieja escribe basura. El guion del emulador que forzaba esta pantalla seguía apuntando a 0x782C, que para entonces era 0xFF, y el intérprete de formato B se llevó por delante la memoria de vídeo. Ahora los guiones leen la dirección del propio ld hl.
La ficha indicadora salió como un rectángulo blanco liso y parecía exactamente un parche que se hubiera comido una ficha. Es el dragón blanco, que es una ficha en blanco por definición.
Y la razón de que el parche no pudiera haberlo roto: pinta_los_dos_indicadores copia los seis caracteres de esa ficha de la memoria de vídeo del tercio de abajo, no de la ROM, así que parchear el tercio de arriba no puede tocarla.
La primera prueba del tutorial salió con cuadrados verdes donde iban las aes. La celda 0x030 en esa pantalla no es la A: es el verde del interior de la caja del final —dieciocho por fila al descomprimir el bloque— y el bloque de esa pantalla lo restaura desde la ROM.
No se puede arreglar ahí, porque ese verde sí se ve, durante los 64 cuadros que tarda el muro de fichas en taparlo. El tutorial carga su propia A.
Meter las fichas que faltaban en la pantalla del tutorial tarda 1,66 segundos, medidos con tools/omsx_mide_la_carga.tcl (1,24 hasta que hubo que recargar también las que ya estaban, ver más abajo). Lo bastante como para que el orden importe: primero borrar la pantalla y después cargar. La primera prueba lo hizo al revés y enseñó más de un segundo del rótulo END con los círculos escritos encima de sus letras.
Cargar en un tercio y dejar que la rutina del propio cartucho lo copie a los otros dos cuesta 864 bytes en vez de 2.592 — y esa rutina se puede llamar desde la interrupción sin miedo, porque es desde donde la llama el cartucho.
Dos arrancadas en frío dan trazas idénticas, instante a instante. Dos vueltas seguidas del mismo encendido no: volcando 0xE000-0xE3FF en el recuento de las dos salen 72 bytes distintos de 1.024 (tools/omsx_repite_la_demo.tcl) — una vuelta acaba con el marcador en 29.000 y la siguiente en 30.000, y el contador de palos de riichi vale 01 y 00.
Eso mató una idea buena. El tutorial iba a comentar la mano que el jugador acababa de ver. Comenta el flujo, que es la máquina de estados y sí es fijo, y no cita ni una cifra.
El muro se baraja con una semilla cuyo primer byte es el registro de refresco R del Z80 (0x41C6) y el segundo el contador de cuadros. R cuenta instrucciones ejecutadas, así que cualquier cambio en el código que corre antes del reparto cambia la mano que juega el demo, no sólo un cambio en el juego. Bastó con añadir la cuarta línea al menú del título, dieciséis bytes más que escribir para el intérprete de listas: el demo juega ahora otra mano distinta de la que jugaba con la construcción anterior, y las capturas de la mesa y del recuento de esta web cambiaron con ella. Dos arrancadas en frío de la misma ROM siguen dando trazas idénticas.
La pantalla del tutorial venía con veinte fichas ya dibujadas, y eran el juego que enseña el tercio de arriba de la mesa: el del rival, girado 180 grados. Al lado de los honores cargados derechos parecían recortadas por abajo. Las seis celdas de SOU1 y de PIN1 son, byte a byte, la rotación de las del jugador; ésa fue la comprobación que lo cerró. Cargar los bloques del jugador tal cual y copiar 126 celdas por la VRAM costó 54 bytes de código y ninguno de datos. El detalle, en El tutorial.
La fila de abajo del hueco vacío son las celdas 0xFE y 0xFF, y ésos son el «destino nuevo» y el «fin» del intérprete de listas. Toda ficha detrás de un hueco perdía su fila de abajo y cinco bytes iban a parar a la tabla de patrones del tercer tercio — invisible mientras las fichas estuvieron giradas. El generador rechaza ahora esos dos valores como dato. El detalle, en El tutorial.
El cartucho marca la dificultad con dos o tres caracteres apilados en vertical: las iniciales de アマチュア, プロ y セミプロ. El parche las escribe en horizontal y en rojo, y tres caracteres es lo que hay: AMA, PRO, SEM. El «SEM» de la mesa no es un fallo de recorte; es la celda entera.
Las celdas 0x08, 0x09 y 0x0A del bloque de 0x92E3 son copias byte a byte de 0x0C, 0x0D y 0x0E. Ese bloque va sin comprimir, así que meterle letras no cuesta tamaño. Es lo que te salva cuando un bloque comprimido se pasa de su hueco por unos pocos bytes.
La palabra «TSUMO» de la pantalla de recuento se quedaba a tres caracteres de un hueco de tres bytes, y llevaba tres sesiones abierta. Se cerró de rebote: SHANPON baja de seis celdas a cuatro en dígrafos, y eso liberó justo el byte que le faltaba al tsumo. Correr su ld hl una posición fue todo el arreglo.