CD inicializável e jogo retrô em um tweet


Alguns anos atrás, criei um disquete inicializável e um jogo retro que se encaixava em um tweet. Desde então, o Twitter dobrou o tamanho dos tweets, então decidi criar um CD inicializável. Ele roda uma versão ligeiramente melhorada do 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 

O código no tweet cria uma imagem de CD-ROM inicializável: cd.iso . Você pode enviar o código para o qemu ou sua máquina virtual favorita - e brincar com as teclas de seta. Você pode até gravar iso em CD-R e inicializar em um computador real.

Para criar uma imagem de CD manualmente, primeiro você precisa obter um entendimento básico da ISO 9660 . Infelizmente, documentos com padrões ISO geralmente são caros. No entanto, a ISO 9660 é igual à ECMA 119 , portanto, as especificações podem ser lidas gratuitamente.

A ISO 9660 possui muitas extensões, como UDF, El Torito, RockRidge, Joliet, etc. Para imagens de inicialização, apenas El Torito é importante para nós. A especificação El Torito, na minha opinião, está mal escrita. Existem erros (por exemplo, a última linha da tabela 7), é fácil esquecer que os valores são hexadecimais (os prefixos 0x não são especificados), os números não são classificados intuitivamente etc. Felizmente, o documento é bastante pequeno.

Para criar um disco de inicialização, começamos escrevendo 17 setores vazios, seguidos por um conjunto de conjunto de descritores de volume. Cada setor tem 2048 bytes.

Nota A especificação ISO-9660 diz que o Conjunto de descritores de volume começa no setor 16. A especificação El Torito requer o início de um registro de inicialização no setor 17. Tecnicamente, você deve colocar um descritor de volume fictício no setor 16, mas tudo funciona bem sem ele.

Escrevemos o primeiro descritor de volume:

 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 

O setor a seguir contém o terminador de conjunto de descritores de volume:

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

Os descritores de volume são seguidos pelo catálogo de inicialização. El Torito suporta diferentes modos de emulação. O CD-ROM pode emular um disquete de inicialização, disco rígido inicializável etc. Não instalei a emulação, ou seja, o BIOS carregará um certo número de setores - e levará nosso carregador de inicialização.

A soma de verificação é calculada para que todos os valores de 16 bits no registro sejam somados a 0 (mod 65536).

Primeira entrada no diretório de inicialização (entrada de verificação):

 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 (padrão):

 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 

Em seguida, zeros para o final do setor:

 1984 x 0x00 // Unused 

O próximo setor é o nosso gerenciador de inicialização e jogo 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 

Depois, escrevi um script para compilar o gerenciador de inicialização, criar a imagem e gerar um tweet. Por fim, gravei o CD e verifiquei se tudo funciona em hardware real .

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


All Articles