CD de arranque y juego retro en un solo tweet


Hace unos años, creé un disquete de arranque y un juego retro que cabía en un tweet. Desde entonces, Twitter ha duplicado la longitud de los tweets, así que decidí crear un CD de arranque. Ejecuta una versión ligeramente mejorada de tron.

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 CD-ROM de arranque: cd.iso . Puede cargar el código en qemu o su máquina virtual favorita, y jugar con las teclas de flecha. Incluso puede grabar iso en CD-R y arrancar en una computadora real.

Para crear una imagen de CD manualmente, primero debe obtener una comprensión básica de ISO 9660 . Desafortunadamente, los documentos con estándares ISO suelen ser caros. Sin embargo, ISO 9660 es lo mismo que ECMA 119 , por lo que las especificaciones se pueden leer de forma gratuita.

ISO 9660 tiene muchas extensiones, como UDF, El Torito, RockRidge, Joliet, etc. Para las imágenes de arranque, solo El Torito es importante para nosotros. La especificación de El Torito, en mi opinión, está mal escrita. Hay errores (por ejemplo, la última línea en la tabla 7), es fácil olvidar que los valores son hexadecimales (no se especifican los prefijos 0x ), los números no se ordenan intuitivamente, etc. Afortunadamente, el documento es bastante pequeño.

Para crear un disco de arranque, comenzamos escribiendo 17 sectores vacíos, seguidos de un conjunto de Conjunto de descriptores de volumen. Cada sector tiene 2048 bytes.

Nota La especificación ISO-9660 dice que el conjunto de descriptores de volumen comienza con el sector 16. La especificación de El Torito requiere el inicio de un registro de arranque en el sector 17. Técnicamente, debe colocar un descriptor de volumen ficticio en el sector 16, pero todo funciona bien sin él.

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 contiene 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 diferentes modos de emulación. El CD-ROM puede emular un disquete de arranque, HDD de arranque, etc. No instalé la emulación, es decir, el BIOS cargará una cierta cantidad de sectores y tomará nuestro gestor de arranque.

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

Primera entrada en el directorio de arranque (entrada de verificació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 

Segundo registro (predeterminado):

 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 

Luego ceros al final del sector:

 1984 x 0x00 // Unused 

El siguiente sector es nuestro cargador de arranque y 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 

Luego escribí un script para compilar el gestor de arranque, construir la imagen y generar un tweet. Finalmente, grabé el CD y verifiqué que todo funciona en hardware real .

Source: https://habr.com/ru/post/es419115/


All Articles