Cargador de signo de exclamación en Atari – Decodificado

Viernes, 14 de marzo de 2014 Dejar un comentario Ir a comentarios

El famoso signo de exclamación

Cuando los computadores Atari recién llegaron a Chile, practicamente todos los juegos que se podían encontrar en cassette usaban el famoso cargador del signo de exclamación.  Se iniciaba la carga de cassete encendiendo el computador con las teclas OPTION + START presionadas, sonaba un beep, se presionaba una tecla y después de unos 10 segundos sonaban unos 6 “pitos” y aparecía un signo de exclamación en la parte inferior derecha de la pantalla, luego pasaban unos 10 segundos más y comenzaba a sonar la carga del juego en sí, a veces amenizada por alguna simple pantalla de presentación.

Si algo fallaba durante la carga, se debía comenzar desde cero, por lo que toda la ansiedad acumulada durante los varios minutos de carga se veía absolutamente recompensada cuando finalmente aparecía el juego en pantalla.  Posteriormente aparecieron mejores sistemas de carga aplicando “turbo” (mayor bitrate) y un mecanismo de recuperación de errores que permitía continuar la carga desde el punto de falla y así no comenzar todo desde cero.  Estos sistemas de carga avanzados merecen un artículo propio, lo que vamos a ver acá es el cargador original del signo de exclamación.

Básicamente estos sistemas de carga funcionaban así: Al iniciar el sistema, la tecla OPTION presionada indicaba al sistema operativo del ATARI que desconectara la ROM del lenguaje BASIC (su espacio era ocupado por muchos juegos), y la tecla START indicaba que se quería hacer un boot desde cassette.  Cuando el usuario presionaba una tecla después del beep, el sistema operativo del ATARI cargaba un archivo y lo ejecutaba.  En el caso de practicamente todos los juegos, este archivo era un cargador un poco más inteligente que se encargaba de cargar el juego en un formato más flexible (XEX).  Si vieramos la cinta como un esquema, sería así:

| ------- CARGADOR EN FORMATO BOOT DE ATARI -------|---------- JUEGO EN FORMATO XEX ----------|
| CARRIER - BLK1 - BLK2 - BLK3 - BLK4 - BLK5 - EOF | CARRIER - BLK1 - BLK2 - ... - BLKn - EOF |

Siempre quise saber exactamente qué hacía el código de este cargador tan simple, y por qué era tan grande (unos 640 bytes). Gracias al usuario AsCrNet de AtariWare que me envió un dump del cargador pude finalmente decodificarlo y ver qué hacía.

Formato XEX

Para que sea más fácil entender el cargador conviene entender primero cuál era el formato del archivo que procesaba el cargador, el formato “del juego”.  Este formato es conocido con el nombre XEX y tiene más o menos la siguiente estructura

|---------------- Archivo en formato XEX ------------- ...
|------ ENCABEZADO ------ | DATA |------ ENCABEZADO -- ...
|FF FF INIL INIH ENDL ENDH| DATA |FF FF INIL INIH ENDL ...

Un archivo XEX son varios bloques de datos.  Cada bloque comienza opcionalmente con un marcador con la secuencia FF FF (hexadecimal), luego viene la dirección de inicio expresada como 2 bytes (INIL INIH) y la dirección de término expresada también como 2 bytes (ENDL ENDH).  Estos 6 bytes son la cabecera del bloque, luego vienen los datos propiamente tal.  Por ejemplo si el juego tiene dos bloques, uno desde 32768 hasta 40959 y otro desde 3072 a 4096 veríamos la siguiente secuencia hexadecimal

|---------------------------- Archivo en formato XEX ----------------------------|
|--------------- BLOQUE 1 ----------------|------------- BLOQUE 2 ---------------|
|--- ENCABEZADO ---|-------DATOS----------|--- ENCABEZADO --|-------DATOS--------|
|FF FF 00 80 FF 99 | 16384 bytes de datos |FF FF 00 0C FF 0F| 1024 bytes de datos|

Después de cargar cada bloque, el cargador ejecuta una llamada a la dirección apuntada por el par de direcciones 738 y 739 ($02E2, $02E3).  Si el cargador quisiese ejecutar un código durante la carga, por ejemplo para mostrar una presentación, debía poner un bloque que escribiera en esas direcciones.  Supongamos que el código que muestra la presentación está en la direcció $0A3B, entonces aparecería un bloque así:

FF FF E2 02 E3 02 3B 0A

Cuando se llega al final de la carga, se transfiere el control al código apuntado por las direcciones 736 y 737 ($02E0, $02E1) utilizando el mismo esquema recién explicado.

El cargador

El cargador tiene el formato de BOOT propio de Atari, obviamente ya que de otro modo el sistema no lo podría ejecutar.  El sistema de BOOT de Atari comienza con 6 bytes para definir el número de bloques de datos, la dirección de inicio de carga y la dirección de ejecución.  En este cargador vienen los valores (hex) : 20  05  00 07 20 07 que son interpretados como : 5 bloques cargados a partir de $0700, inicio de ejecución en $0720. Por qué suenan 6 bloques de carga y acá dice que son 5? Porque son 5 de datos y el 6to es el EOF (End of File).

Si bien la dirección de ejecución es $0720, el sistema operativo de Atari primero ejecuta lo que está a continuación de este encabezado, en este caso es lo que está en $0706.  Y ahí comienzan las sorpresas!!!

Lo primero que me llamó la atención es que eset vil cargador está protegido.  Si uno trata de ver el código directamente, no se puede a menos que se vaya revisando la ejecución paso a paso.  Lo primero que hace el cargador es automodificarse para comenzar a ejecutarse en una dirección diferente a la que viene cargada originalmente. De la siguiente forma (ordenado por legibilidad):

BOOT:
706  32 244   8  JSR STOPANDPATCH ; $8F4

[...]

STOPANDPATCH:
8f4  32 207   8 JSR MOTOROFF
8f7  32 213   8 JSR PATCHJSR
8fa  96         RTS

MOTOROFF:
8cf 169  60     LDA #60
8d1 141   2 211 STA 54018
8d4 96          RTS

PATCHJSR:
8d5 169  32     LDA #32   ; JSR
8d7 141   9   7 STA $709
8da 169 157     LDA #157  ; $9D
8dc 141  10   7 STA $70A
8df 169   8     LDA #8    ; $08
8e1 141  11   7 STA #70B
8e4  96         RTS

Cuando se ejecuta PATCHJSR se modifica el código que se ejecutará justo después de volver via RTS en $8D4, para que salte a $089D, y qué encontramos ahí? La rutina que desprotege el resto del código invirtiendo todos los bits mediante XOR #$FF, una ténica que podríamos considerar milenaria.

A partir de ese momento, el código queda legible completamente.

No voy a analizar el detalle del cargador, pero sí me interesa destacar las siguientes curiosidades:

  • No logré identificar la parte en donde se pone el signo de interrogación en la pantalla.  Sólo veo la impresión de un caracter que limpia la pantalla.  Será el signo de exclamación un bug del sistema operativo del Atari?
  • El código de carga XEX en este cargador es HO-RRI-BLE.  Esto se debe principalmene a que está mezclada la llamada a las rutinas del sistema operativo para cargar datos desde cassette, con la interpretación de los encabezados XEX. En comparación, cargadores más modernos como CAIN (ahem!) usan una abstracción tipo stream, en donde los bytes se procesan uno a uno y la carga física de los bloques se procesa en forma independiente, como si se tratara de un sistema opaco subyacente (le puse color ah).  En todo caso, se entiende que sea así, hay que pensar que ese código debe ser de principios de los ’80, cuando muchos de nosotros no sabíamos ni ir a comprar el pan.
  • Si la carga falla en algún momento, se invoca a la rutina del sistema operativo que imprime BOOT ERROR.  Por algún motivo, en los computadores que conocí esto nunca fue así, y el sistema simplemente se colgaba.  Es probable que la dirección de rutina haya sido válida sólo para equipos antiguos (400/800) y no para sistemas operativos más modernos (XL/XE).
  • Se nota que el código fue parchado a nivel binario, ya que usaron NOP en varias partes, desactivando código.
  • Derivado de lo anterior, hay muchos accesos de 16 bit a direcciones que están en la página cero, en donde se pueden usar instrucciones especiales que direccionan con sólo 8 bits (ej, 133, 2 en vez de 141, 2, 0).
  • Si hay un cartridge insertado, aparece un mensaje en pantalla que lo advierte.  Este mensaje nunca lo había visto, y es muy probable de que hasta el momento nadie lo haya visto tampoco!  Les adjunto una captura al final de este artículo foryourpleasure.

Para quién quiera ver el detalle de cargador, les dejo los siguientes archivos en una carpeta de DropBox:

  • casboot.asm : Archivo de texto con el código del cargador. Lo decodifiqué a mano, por lo que el código no es compatible con ningún assembler.
  • dump.c : Programa en C para hacer un dump del código que me envió AsCrNet.  Al cambiar UNPROTECT a 1, se aplica el XOR #$255 para desproteger el código
  • casboot.atr : Imagen de disco que contiene un pequeño programa en BASIC que hice para saber cómo se vería el mensaje del cartridge insertado (cartmsg.bas)

PD1 : La imagen del cargador que encabeza este artículo es un fake.  Fue lo más rápido que pude hacer.

PD2 : Por algún motivo se borró la mitad de este artículo mientras lo estaba creando. Lo tuve que hacer de nuevo. NOT FUN.

PD3 : Ningún juego fue dañado durante la creación de este artículo.


ACTUALIZACIÓN: Gracias a los aportes de Guillermo Fuenzalida y ArCrNet finalmente fue resuelto el enigma del signo de exclamación.  Cuando el cargador imprime el caracter de limpiar pantalla, lo hace con el siguiente código:

765 169 125     LDA #125   ; CLEAR SCREEN
767 32  164 246 JSR PRINT  ; $F6A4

Sin embargo la rutina del SO para imprimir en $F6A4 sólo es válida para la ROM de la generación Atari 400/800, porque en la ROM de los Atari XL/XE se encuentra en otro lugar.  Según Mapping the XL/XE:

Many 400/800 programs made direct jumps to keyboard ‘get’ and ‘put’ routines rather than through the proper vectors, which makes them incompatible with XL/XE machines. The get routines in the XL/XE begin at 62026 ($F24A). This was 63038 ($F63E) in the 800. The put routines begin at 62128 ($F2B0). or 63140 ($F6A4) in the 800. If you have a program which won’t work on your XL/XE, try finding if it uses these locations and change them.

Por lo tanto, el famoso caracter “!” de este cargador aparece debido a un bug sin mayores consecuencias.  Para comprobarlo, modifiqué la llamada a $F6A4 y la cambié por $F2B0 y efectivamente, se limpia la pantalla y no aparece el caracter “!”.  Lo mismo demostró ArCrNet usando el cargador en un emulador con la ROM del 400/800, nada de signo de exclamación.

Cartridge insertado

Mensaje que aparece si hay un cartridge insertado

 

Tags: ,






  1. Sábado, 15 de marzo de 2014 a las 18:08 | #1

    El formato XEX permite que se carge una porción de código y data, se ejecute ese código para poner, por ejemplo, un pantallazo de prersentación, para luego retornar el control al cargador, quien continúa cargando el resto del XEX. El tiempo que requiere esa rutina de inicialización puede ser tanto que se puede perder el siguiente bloque de la cinta, y se aborte la carga. Para esos casos, lo más seguro que que el utilitario que grabe el cargador y el juego use lo que se llamó “pitos lentos”.

    Buen artículo, pero creo que por falta de tiempo, no puedo ayudarte a descifrar dónde se imprimía el signo de exclamación. ;-)

  2. Domingo, 16 de marzo de 2014 a las 09:07 | #2

    Atento Vitoco que viene la actualización del artículo con el misterio resuelto

  3. Freddy Urbina
    Lunes, 17 de marzo de 2014 a las 09:06 | #3

    Muchas gracias!! Disfruté el artículo de principio a fin. Muchas dudas se me aclararon aquí y lo que quiero puntualizar es el mensaje de BOOT ERROR sí me apareció algunas veces en mi ATARI 800XL sólo cuando fallaba el primer bloque de la carga del cargador… O sea, aparecía cuando toda mi experiencia de carga de juego se iba a la mierda inmediatamente (sorry por el improperio pues así me sentia con las tremendas ansias de jugar frustradas ajajaja) Saludos!!

  4. Lunes, 17 de marzo de 2014 a las 10:14 | #4

    yo me acuerdo que mi primer juego que había sido el alien ambush venia con este tipo de cargador al principio y después el juego, como era corto nunca me dio problemas en carga, no así cuando después tuve el montezuma con el mismo signo, rogaba que se cargaba entero, bueno después con el tiempo logre tenerlos con el famoso nhp y si se caiga lo retrocedía no mas, tenia muchas falencias este tipo de principio.

  5. Lunes, 17 de marzo de 2014 a las 13:04 | #5

    El boot error puede haber aparecido si se cayó al principio, cuando está cargando el cargador, ya que esa carga la hace el sistema operativo del Atari

  6. AtariUser01
    Lunes, 17 de marzo de 2014 a las 19:50 | #6

    Comentas lo de cambiar la llamada de $F6A4 a $F2B0. ¿Sería posible ver un CAS con esto?

  7. Martes, 18 de marzo de 2014 a las 12:21 | #7

    No es tan simple, porque al parchar el CAS tienes que recalcular los checksums. Otra cosa sería cargar en memoria el CAS, parcharlo y luego volverlo a grabar en el Atari, pero es más pega.

    Una forma rápida de comprobarlo, es usar el mismo CAS en un Atari 400/800

  8. Martes, 25 de marzo de 2014 a las 06:33 | #8

    fcatrin :
    Una forma rápida de comprobarlo, es usar el mismo CAS en un Atari 400/800

    ¿Tal vez usando el Translator o el FixXL? Si no me equivoco, circulaba una versión para cassette de este último.

  9. Martes, 1 de abril de 2014 a las 09:57 | #9

    Sí, se podría usar ese en un atari real, pero sale más a cuenta configurar el emulador para que emule 400/800

Debes estar registrado para dejar un comentario.