TeraGames / InsurgentePress, Ciudad de México.- En Bootable CD + retro game in a tweet, Alok Menghrajani, ingeniero de seguridad en Square, explica con detalle cómo codificar una imagen ISO de disco (en formato CD-ROM) que puede inicializarse y desde el cual se puede ejecutar un vídeojuego simple tipo Snake, “el de la serpiente”.

El código resultante es lo suficientemente compacto como para caber en un tuit, en menos de 240 caracteres.

perl -E 'say"A"x46422,"BDRDAwMQFFTCBUT1JJVE8gU1BFQ0lGSUNBVElPTg","A"x54,"Ew","A"x2634,"/0NEMDAxAQ","A"x2721,"BAAAAYQ","A"x30,"SVVVqogAAAAAAAEAF","A"x2676,"LMBaACgB76gfbgTAM0Qv8D4uYAI86qqgcc+AXP45GA8SHIRPFB3DTeYSEhyBSwCa8CwicMB3rSG/sHNFbRFJjAke9rrwQ","A"x2638'|base64 -D>cd.iso

El código en el tweet crea una imagen de disco de arranque CDROM: cd.iso. Puede iniciar el código en qemu, caja virtual o su software de virtualización favorito y jugar con las teclas de flecha. Incluso puede grabar el iso en un CD en blanco y arrancar hardware real.

Para crear una imagen de disco CD, primero debe obtener una comprensión básica de ISO 9660. Desafortunadamente, las normas ISO suelen ser costosas. Sin embargo, ISO 9660 es lo mismo que ECMA 119, lo que significa que puede leer la especificación de forma gratuita.

ISO 9660 tiene muchas extensiones, como UDF, El Torito, RockRidge, Joliet, etc. Para las imágenes de arranque, solo nos importa El Torito. La especificación de El Torito es, en mi opinión, mal escrita. Hay errores (por ejemplo, figura 7, última fila), es fácil olvidar que los valores son hexadecimales (sin prefijos 0x), las figuras no están ordenadas en un orden intuitivo, etc. Afortunadamente, el documento es bastante breve.

Para crear un disco de arranque, comenzamos escribiendo 17 sectores vacíos seguidos por el Conjunto de descriptores de volumen. Un sector tiene 2048 bytes. Escribimos el primer Descriptor de Volumen:

0x00 // Type (0 = boot record)
'CD001' // Identifier
0x01 // Version
'EL TORITO SPECIFICATION' // Boot System Identifier
9 x 0x00 // Padding
32 x 0x00 // Unused
0x13 0x00 0x00 0x00 // Boot Catalog address (in absolute sectors)
1973 x 0x00 // Unused

El siguiente sector es el Terminador de conjunto de descriptores de volumen:

0xff // Type (255 = terminator)
'CD001' // Identifier
0x01 // Version
2041 x 0x00 // Unused

Los Descriptores de Volumen son seguidos por el Catálogo de Arranque. El Torito admite varios modos de emulación. El CD-ROM puede emular un disquete de arranque, un disco duro de arranque, etc. No elegí emulación, lo que implica que el BIOS cargará un número específico de sectores y saltará a nuestro gestor de arranque.

La suma de comprobación se calcula de manera que todos los valores de 16 bits en el registro se sumen a 0 (mod 65536).

Primera entrada en el catálogo de inicio (entrada de validación):

0x01 // Header ID
0x00 // Platform ID (0 = Intel x86)
0x00 0x00 // Reserved
'a' // ID string
23 x 0x00 // Padding
cksum cksum // Checksum (2 bytes)
0x55 0xaa // Key bytes

Segunda entrada (Entrada predeterminada):

0x88 // Boot Indicator (0x88 = bootable)
0x00 // Boot Media Type (0 = no emulation)
0x00 0x00 // Load segment
0x00 // System Type
0x00 // Unused
0x01 0x00 // Number of sectors to load
0x14 0x00 0x00 0x00 // Virtual disk address (in absolute sectors)
20 x 0x00 // Unused

Seguido de ceros hasta el final del sector:

1984 x 0x00 // Unused

El siguiente sector es crear un disco de arranque con el juego retro:

; to compile:
; nasm bootloader.asm -o bootloader.img
[bits 16] ; Pragma, tells the assembler that we
; are in 16 bit mode (which is the state
; of x86 when booting from a floppy).
[org 0x7C00] ; Pragma, tell the assembler where the
; code will be loaded.

      mov bl, 1                    ; Starting direction for the worm.
      push 0xa000                  ; Load address of VRAM into es.
      pop es

restart_game:
mov si, 320*100+160 ; worm's starting position, center of
; screen

      ; Set video mode. Mode 13h is VGA (1 byte per pixel with the actual
      ; color stored in a palette), 320x200 total size.
      mov       ax, 0x0013
      int       0x10

      ; Draw borders. We assume the default palette will work for us.
      ; We also assume that starting at the bottom and drawing 2176 pixels
      ; wraps around and ends up drawing the top + bottom borders.
      mov       di, 320*199
      mov       cx, 2176
      rep

draw_loop:
stosb ; draw right border
stosb ; draw left border
add di, 318
jnc draw_loop ; notice the jump in the middle of the
; rep stosb instruction.

game_loop:
; We read the keyboard input from port 0x60. This also reads bytes from
; the mouse, so we need to only handle [up (0x48), left (0x4b),
; right (0x4d), down (0x50)] in al, 0x60
cmp al, 0x48
jb kb_handle_end
cmp al, 0x50
ja kb_handle_end

      ; At the end bx contains offset displacement (+1, -1, +320, -320)
      ; based on pressed/released keypad key. I bet there are a few bytes
      ; to shave around here given the bounds check above.
      aaa
      cbw
      dec       ax
      dec       ax
      jc        kb_handle
      sub       al, 2
      imul      ax, ax, byte -0x50

kb_handle:
mov bx, ax

kb_handle_end:
add si, bx

      ; The original code used set pallete command (10h/0bh) to wait for
      ; the vertical retrace. Today's computers are however too fast, so
      ; we use int 15h 86h instead. This also shaves a few bytes.

      ; Note: you'll have to tweak cx+dx if you are running this on a virtual
      ; machine vs real hardware. Casual testing seems to show that virtual machines
      ; wait ~3-4x longer than physical hardware.
      mov       ah, 0x86
      inc       cl
      int       0x15

      ; Draw worm and check for collision with parity
      ; (even parity=collision).
      xor [es:si], ah

      ; Go back to the main game loop.
      jpo       game_loop

      ; We hit a wall or the worm. Restart the game.
      jmp       restart_game

TIMES 2048 - ($ - $$) db 0 ; Fill the rest of the sector with 0

Su tweet es una línea de comando que comienza con perl. El texto es binario con codificación base64 y si ejecuta el Tweet desde un shell, lo cual es una cosa extraña de hacer con un Tweet, le garantizamos que recibirá un archivo llamado cd.iso. Podría grabarlo en un CDROM, pero es más probable que simplemente monte en una máquina virtual e inicie eso. [Alok] dice que funciona en QEMU, VirtualBox y, sí, incluso en un CD real.

Esto puede parecer frívolo. El juego definitivamente no es nada espectacular. Sin embargo, si quiere entender cómo el estándar ISO9660 crea discos de arranque (la extensión de El Torito), disfrutará de este enfoque divertido de cómo funciona todo.

Por supuesto, podrías leer el estándar. Un buen consejo de la publicación es que, aunque comprar una copia legal de la norma es costoso, puede obtener ECMA 119, que es el mismo documento de forma gratuita. Sin embargo, la especificación de El Torito aparentemente es bastante difícil de leer y tiene errores tipográficos. Entonces, ver un ejemplo práctico es bastante útil.

Es interesante lo corto que es el código de ensamblador de NASM para el juego y el gestor de arranque. ¡Aquí no hay megabytes de bibliotecas de GUI compartidas!

Anteriormente Alok ya había codificado el mismo juego en una imagen de disco flexible de 3,5 pulgadas.

Comentarios desactivados en Programando videojuegos en un Tweet