📜 ⬆️ ⬇️

Boot CD and retro play in one tweet


A few years ago, I created a boot floppy and a retro game that fit into one tweet. Since then, Twitter has doubled the length of tweets, so I decided to create a bootable CD. It runs on a slightly improved version of 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 

The code in the tweet creates a bootable CD-ROM image: cd.iso . You can load the code into qemu or your favorite virtual machine - and play with the arrow keys. You can even burn an iso to a CD-R and boot onto a real computer.

To manually create a CD image, you first need to get a basic understanding of ISO 9660 . Unfortunately, ISO documents are usually expensive. However, ISO 9660 is the same as ECMA 119 , so the specifications can be read for free.

ISO 9660 has many extensions such as UDF, El Torito, RockRidge, Joliet, etc. For boot images, only El Torito is important to us. The El Torito specification, in my opinion, is poorly written. There are errors (for example, the last line in table 7), it is easy to forget that the values ​​are hexadecimal ( 0x prefixes are not specified), the numbers are not sorted intuitively, etc. Fortunately, the document is quite small.
')
To create a boot disk, we start by writing down 17 empty sectors, followed by a Volume Descriptor Set. Each sector is 2048 bytes.

Note. The ISO-9660 specification says that the Volume Descriptor Set begins with sector 16. El Torito specification requires the start of the boot record in sector 17. Technically, you should put a dummy volume descriptor in sector 16, but everything works fine without it.

We write the first volume descriptor:

 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 

The following sector hosts the Volume Descriptor Set Terminator:

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

The volume descriptors are followed by the Boot Catalog. El Torito supports different emulation modes. A CD-ROM can emulate a boot diskette, boot HDD, etc. I did not install the emulation, that is, the BIOS will load a certain number of sectors - and our bootloader will take it.

The checksum is calculated so that all 16-bit values ​​in the record are summed to 0 (mod 65536).

The first entry in the boot directory (check entry):

 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 

The second entry (default):

 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 

Then zeros to the end of the sector:

 1984 x 0x00 // Unused 

The next sector is our bootloader and retro game:

 ; 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 

Then I wrote a script to compile the bootloader, build the image and generate a tweet. Finally, I burned the CD and checked that everything works on real hardware .

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


All Articles