Pull to refresh

Загрузочный CD и ретро-игра в одном твите

Reading time4 min
Views9.1K
Original author: Alok Menghrajani

Несколько лет назад я создал загрузочную дискету и ретро-игру, которые помещались в один твит. С тех пор Twitter удвоил длину твитов, поэтому я решил создать загрузочный компакт-диск. Он работает под управлением немного улучшенной версии 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

Код в твите создаёт загрузочный образ диска CD-ROM: cd.iso. Вы можете загрузить код в qemu или свою любимую виртуальную машину — и играть с помощью клавиш со стрелками. Вы даже можете записать iso на CD-R и загрузиться на реальном компьютере.

Чтобы создать вручную образ CD, сначала нужно получить базовое представление об ISO 9660. К сожалению, документы со стандартами ISO обычно дорого стоят. Однако ISO 9660 совпадает с ECMA 119, так что спецификации можно прочитать бесплатно.

У ISO 9660 множество расширений, таких как UDF, El Torito, RockRidge, Joliet и др. Для загрузочных образов нам важен только El Torito. Спецификация El Torito, на мой взгляд, плохо написана. Там есть ошибки (например, последняя строка в таблице 7), легко забыть, что значения шестнадцатеричные (не указаны префиксы 0x), цифры не отсортированы в интуитивном порядке и т.д. К счастью, документ совсем небольшой.

Чтобы создать загрузочный диск, начинаем с записи 17 пустых секторов, за которыми следует набор дескрипторов томов (Volume Descriptor Set). Каждый сектор 2048 байт.

Примечание. Спецификация ISO-9660 говорит, что Volume Descriptor Set начинается с сектора 16. Спецификация El Torito требует начало загрузочной записи в секторе 17. Технически, следует поместить фиктивный дескриптор тома в сектор 16, но и без него всё нормально работает.

Пишем первый дескриптор тома:

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

В следующем секторе размещается Volume Descriptor Set Terminator:

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

За дескрипторами томов следует загрузочный каталог (Boot Catalog). El Torito поддерживает разные режимы эмуляции. CD-ROM может эмулировать загрузочную дискету, загрузочный HDD и т.д. Я не устанавливал эмуляцию, то есть BIOS загрузит определённое количество секторов — и возьмёт наш загрузчик.

Контрольная сумма вычисляется так, что все 16-битные значения в записи суммируются до 0 (mod 65536).

Первая запись в загрузочном каталоге (проверочная запись):

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

Вторая запись (дефолтная):

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

Потом нули до конца сектора:

1984 x 0x00               // Unused

Следующий сектор — наш загрузчик и ретро-игра:

; 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

Затем я написал скрипт для компиляции загрузчика, сборки образа и генерации твита. Наконец, я прожёг CD и проверил, что всё работает на реальном оборудовании.
Tags:
Hubs:
+41
Comments6

Articles