DOSBox как стенд для кода загрузочного сектора
Допустим, что перед нами стоит задача написать и запустить код загрузочного сектора для BIOS-совместимого i386 компьютера. Думаю любой нормальный человек первым делом подумает про QEMU или Bochs в качестве виртуального тестового стенда. А мне захотелось воспользоваться для этой задачи DOSBox'ом.
Небольшое уточнение: у себя на машине я использую DOSBox-X, а не "чистый" DOSBox. Но, если я ничего не путаю, то приведённые ниже команды будут работать в обеих вариациях этой программы.
Загрузочный сектор это самый первый сектор жёсткого диска (то есть нулевой), который содержит в себе загрузочный код, таблицу разделов и сигнатуру. Поэтому (для начала) нам нужен жесткий диск. Так как у нас эмулятор DOS-машины, то создаём образ жесткого диска: запускаем DOSBox и набираем команду создания файла hdd.img, в котором будет содержаться образ жесткого диска на 2 гигабайта.
IMGMAKE hdd.img -t hd -size 2048
В текущей директории процесса DOSBox появился новый файл hdd.img, который мы и будем мучить. Он уже содержит разметку диска и данные-заглушки, наша цель заменить загрузочный код первого сектора. Пока выключаем DOSBox и продолжаем работу в хост-системе.
В качестве инструментария разработки будем использовать ассемблер nasm. Он доступен на колоссальном спектре платформ, позволяет получать на выходе 16-ти битные бинарные файлы (аналоги COM файлов в DOS) и уже был установлен у меня :)
Сформируем файл boot.s, который содержит (в качестве демонстрации) посимвольный вывод (функция 0x0E прерывания 0x10 BIOS'а) строки "Hello", после чего переходит в бесконечный цикл. Исходный код (содержимое файла boot.s):
xor bx, bx
mov ah, 0x0E
mov al, 0x0D
int 0x10
mov al, 0x0A
int 0x10
mov al, "H"
int 0x10
mov al, "e"
int 0x10
mov al, "l"
int 0x10
mov al, "l"
int 0x10
mov al, "o"
int 0x10
mov al, 0x0D
int 0x10
mov al, 0x0A
int 0x10
loop:
jmp loop
Теперь из boot.s соберём 16-ти разрядный код с виде плоского бинарного файла boot.bin:
nasm boot.s -f bin -o boot.bin
У нас есть: с одной стороны образ пустого жёсткого диска (hdd.img), а с другой - код загрузочного сектора (boot.bin). Код загрузчика занимает первые 446 байт в самом первом секторе главной загрузочной записи (MBR). То есть нам нужно поместить содержимое собранного boot.bin (который должен быть меньше 447 байт) по смещению 0 в файл hdd.img, для этого можно воспользоваться утилитой dd:
dd if=boot.bin of=hdd.img bs=512 count=1 conv=notrunc
Образ жёсткого диска готов к тестированию. Снова запускаем DOSBox и монтируем подготовленный образ:
IMGMOUNT C hdd.img
Затем запускаем наш код загрузочного сектора на исполнение:
BOOT C:
Если все предыдущие шаги прошли без ошибок, то на экране должна отобразиться строка "Hello", напечатанная нашим кодом:
Booting from drive C... Hello
P.S. (добавлено 2025-02-13)
Изначально забыл упомянуть об одной довольно неприятной особенности DOSBox: этот эмулятор ожидает заполненную таблицу разделов по смещению 446 (0x1BE) байт. Это не всегда удобно, ведь для некоторых крайних случаев пространство таблицы разделов (64 байта из 512) может быть использовано кодом загрузочного сектора в собственных целях.
В качестве бонуса приведу ассемблерный листинг загрузочного сектора, из которого сразу собирается первый сектор с нужной сигнатурой (55 AA)
org 0x7C00
start:
xor bx, bx
mov ah, 0x0E
mov al, 0x0D
int 0x10
mov al, 0x0A
int 0x10
mov al, "H"
int 0x10
mov al, "e"
int 0x10
mov al, "l"
int 0x10
mov al, "l"
int 0x10
mov al, "o"
int 0x10
mov al, 0x0D
int 0x10
mov al, 0x0A
int 0x10
loop:
jmp loop
end:
resb 510-(end-start)
signature:
db 0x55
db 0xAA
Собирается он абсолютно так же:
nasm boot.s -f bin -o boot.bin
Но полученный boot.bin может быть использован в QEMU сразу в качестве жёсткого диска, с которого можно непосредственно грузиться:
$ qemu-system-i386 -nographic -hda boot.bin SeaBIOS (version rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org) iPXE (http://ipxe.org) 00:03.0 CA00 PCI2.10 PnP PMM+06FD0FC0+06F30FC0 CA00 Booting from Hard Disk... Hello