Bootable CD dan game retro dalam satu tweet


Beberapa tahun yang lalu, saya membuat floppy disk yang dapat di - boot dan sebuah game retro yang pas dalam satu tweet. Sejak itu, Twitter telah menggandakan panjang tweet, jadi saya memutuskan untuk membuat CD bootable. Ini menjalankan versi tron ​​yang sedikit lebih baik.

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 

Kode dalam tweet ini menciptakan image CD-ROM yang dapat di-boot: cd.iso . Anda dapat mengunggah kode ke qemu atau mesin virtual favorit Anda - dan bermain dengan tombol panah. Anda bahkan dapat membakar iso ke CD-R dan mem-boot ke komputer sungguhan.

Untuk membuat gambar CD secara manual, Anda harus terlebih dahulu mendapatkan pemahaman dasar tentang ISO 9660 . Sayangnya, dokumen dengan standar ISO biasanya mahal. Namun, ISO 9660 sama dengan ECMA 119 , sehingga spesifikasinya dapat dibaca secara gratis.

ISO 9660 memiliki banyak ekstensi, seperti UDF, El Torito, RockRidge, Joliet, dll. Untuk gambar boot, hanya El Torito yang penting bagi kami. Spesifikasi El Torito, menurut saya, ditulis dengan buruk. Ada kesalahan (misalnya, baris terakhir dalam tabel 7), mudah untuk melupakan bahwa nilainya adalah heksadesimal (awalan 0x tidak ditentukan), angka tidak diurutkan secara intuitif, dll. Untungnya, dokumennya cukup kecil.

Untuk membuat boot disk, kita mulai dengan menulis 17 sektor kosong, diikuti oleh seperangkat deskriptor volume (Set Volume Descriptor). Setiap sektor adalah 2048 byte.

Catatan Spesifikasi ISO-9660 mengatakan bahwa Set Volume Descriptor dimulai dengan sektor 16. Spesifikasi El Torito mengharuskan dimulainya rekaman boot di sektor 17. Secara teknis, Anda harus meletakkan deskriptor volume dummy di sektor 16, tetapi semuanya bekerja dengan baik tanpa itu.

Kami menulis deskriptor volume pertama:

 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 

Sektor berikut ini berisi Terminator Set Descriptor Volume:

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

Deskriptor volume diikuti oleh Katalog Boot. El Torito mendukung mode emulasi yang berbeda. CD-ROM dapat meniru disket boot, bootable HDD, dll. Saya tidak menginstal emulasi, yaitu, BIOS akan memuat sejumlah sektor - dan mengambil bootloader kami.

Checksum dihitung sehingga semua nilai 16-bit dalam catatan dijumlahkan ke 0 (mod 65536).

Entri pertama di direktori boot (entri verifikasi):

 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 

Rekor kedua (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 

Lalu nol hingga akhir sektor:

 1984 x 0x00 // Unused 

Sektor selanjutnya adalah bootloader dan game retro kami:

 ; 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 

Lalu saya menulis sebuah skrip untuk mengkompilasi bootloader, membangun gambar, dan menghasilkan tweet. Akhirnya, saya membakar CD dan memeriksa bahwa semuanya berfungsi pada perangkat keras nyata .

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


All Articles