TreeOS. 16-битная рождественская демка в загрузочном секторе

hhzwix9cqk4pl3i7hculwrvlmqk.png

В преддверии Нового Года бороздил просторы сети, и нашёл замечательную демку на ассемблере рисования новогодней ёлки. Демка не простая, а золотая, написана таким образом, чтобы работала в загрузочном секторе диска, фактически микрооперационная система, если её так можно назвать. Из-за большой любви к Новому Году и ассемблеру, решил таки её немного разобрать, и восхититься мастерством автора. Итак, поехали.

Что за демка и где её искать?


Данную демку нашёл в этом гит репозитории: TreeOS: a 16-bit bootsector Christmas tree demo.
Как гласит название:

Добро пожаловать в TreeOS (древесную операционную систему)! Это очень простая, хакерская, но работающая демка, которая рисует крутящуюся новогоднюю рождественскую ёлку и выдаёт небольшое сообщение. При этом работает на голом аппаратном обеспечении персонального компьютера, без операционной системы, используя только стандартное VGA-оборудование. С Новым Годом Рождеством.
mtg8cd9c0j75ip9z_mxixxfkezi.png

Забегая вперёд скажу, что там две версии: с надписью и без (как в картинке на заходнике). Демка — это образ загрузочный дискеты. Написана она 16-ти битным кодом, который использует BIOS для загрузки с диска и изменяет видеорежим перед работой непосредственно с буфером кадра VGA. В коде используется стандартный 256-цветный режим 320×200 (режим VGA 13h). По идее это должно работать на любом персональном компьютере, но тестировалось только на виртуальной машине (в Quemu). Лично я протестировал, что она отлично работает и в VirtualBox. Ниже я расскажу, как её запустить.

Пробуем запустить


Ну как обычно, прежде чем посмотреть код, насладиться его работой, так сказать пошуршать мозгами, сразу запустим? Конечно же да!
Поехали!

Для старта нам понадобиться компилятор nasm, виртуальная машина quemu или VirtualBox, кому что милее. Лично я решил попробовать и там и там. Потому ставим всё вместе:

sudo apt install nasm qemu-system-x86


Клонируем репозиторий, переходим в него и пробуем собрать пример.

git clone https://github.com/cfallin/treeos
cd treeos/
make qemu


И… И счастье не настало, хотя было так близко.

nasm -fbin -o treeos.img treeos.asm
treeos.asm:15: error: attempt to define a local label before any non-local labels
treeos.asm:47: error: attempt to define a local label before any non-local labels
treeos.asm:52: error: attempt to define a local label before any non-local labels
treeos.asm:60: error: attempt to define a local label before any non-local labels
treeos.asm:65: error: attempt to define a local label before any non-local labels
Makefile:5: ошибка выполнения рецепта для цели «treeos.img»
make: *** [treeos.img] Ошибка 1

Пришлось расчехлить гугл, и вспомнить в чём же проблема. А проблема оказалась очень простая. Автор использовал метки с точкой, а компилятору это не нравилось. После того, как я поправил все метки, убрав из них точки, всё получилось.
Например:

...
    jmp 0:.newseg
.newseg:
    mov ax, 0
...


изменил на

...
    jmp 0:newseg
newseg:
    mov ax, 0
...

Компиляция тоже идёт достаточно интересно. Не буду приводить Makefile, его каждый может посмотреть самостоятельно. Более интересно как идёт создание образа флоппи диска:

make qemu
nasm -fbin -o treeos.img treeos.asm
rm -f floppy.img
dd if=/dev/zero of=floppy.img bs=1024 count=1440
1440+0 записей получено
1440+0 записей отправлено
1474560 байт (1,5 MB, 1,4 MiB) скопирован, 0,00373289 s, 395 MB/s
dd if=treeos.img of=floppy.img conv=notrunc
3+1 записей получено
3+1 записей отправлено
1998 байт (2,0 kB, 2,0 KiB) скопирован, 0,000193295 s, 10,3 MB/s
qemu-system-x86_64 -fda floppy.img


Как видно, идёт компиляция ассемблеровского файла.
Затем идёт удаление возможного старого образа флоппи диска.
Команда dd создаёт чистый образ дискетки, после чего в начало записывается готовый бинарник ёлочки.
Ну и последняя команда запускает виртуальную машину quemu с этим образом. В результате получим вот такую замечательную демку.

ufjgrl278k6ggebspiwpjy5xzm0.gif


В реальности не так сильно тормозит, как гифка.

Что делать, если сижу под Windows, а посмотреть демку хочется?


Ну самый лучший вариант, это собрать всё на виртуалке, а затем записать на живую дискету и загрузиться. Но где сегодня найти флоппи дисковод и дискеты?
Поэтому рассказываю как посмотреть демку на VirtualBox. Я специально сохранил оба образа дискеты с надписью и без.
В витуалбоксе создаём новую машину, можно даже не подключать жёсткий диск, он нам не нужен. Но для работы нужно добавить флопи-контроллер, делается это следующим образом.

c2w2-hkicyoptsmygyrti6drdww.png
gegfartil8cvdgt8rfwwy7ovqba.png

После чего, скармливаем ему готовый образ TreeOS и наслаждаемся новогодним настроением.

knuqarzrw-9wrn46lovlqdhhjxw.png


Ёлка на VirtualBox.

Если очень хочется в живую запустить?


Зазгрузка с флешки


Как я уже сказал, идеально записать образ на флопи-диск. Но если его нет, можно записать на флешку. Для начала стираем флешку, забивая её нулями.

dd if=/dev/zero of=/dev/sdx


После этого копируем образ TreeOS в начальный бутсектор диска.

dd if=/mountedlocation/treeosFloppy.img of=/dev/sdx bs=512 count=4


Затем, используя утилиту CFDISK c помощью командной строки, помечаем сектор MBR как загрузочный сектор тип 0c FAT32.

Это добавит таблицу разделов к пустому пространству в секторе 0. Содержимое TreeOS останется в нулевом секторе после данный операции. Всё, можно пробовать загрузиться с флешки и загрузка не должна сильно отличаться от загрузки с дискетки.

Загрузка с жёсткого диска


Есть два пути загрузки с жёсткого диска. Первый путь, это подготовить его точно так же, как мы делали флешку, но тогда все данные на жёстком диске будут уничтожены.
Есть альтернатива этому решению. Копируем файл treeosFloppy.img в корень NTFS раздела: c:\treeos.mbr.

После этого настраиваем Windows BOOTMGR и/или NTLDR чтобы TreeOS было помещено в загрузочное меню Windows вместе с самой Windows.

В результате при загрузке можно будет выбирать что загружать, Windows или эту микроОС.

Точно так же можно настроить и GRUB.

Пару слов об ассемблере


Буду честен, в х86 ассемблере слаб. Но всё же крупными штрихами постараюсь описать то что происходит. Гуру ассемблера могут справедливо меня поправить в комментариях. Прошу не бить сильно, и не пинать. Разберём свежую (последний коммит был пять дней назад) 512 байтовую версию.

8x-l3l2zw8ixpgaydenjarxbvqk.png

Запрещаем прерывания и очищаем регистры. Инициализируем указатель стека. Разрешаем прерывания.

bpxm8x_2e3yk6z1kontdasowhyc.png

Очищаем экран, записывая «чёрные» пробелы на экран. Поясняю, что можно иметь цвет фона и текста.

puz2o2vuxys3rtu7z3urthf3jyi.png

Здесь идёт инициализация системного таймера.

fhj0aja3szpfvwssvhtpqiqybpm.png

Здесь мы переходим с помощью 13-прерывания BIOS в VGA-режим. Код 0×13 соответствует разрешению 320×200 и 256 цветовой палитре.

_6ml1qlfddzt72hiiwsugovgjjq.png

После чего попадаем в глобальный цикл. Где мы получаем текущий «тик» системного таймера в регистр BX и очищаем буфер картинки для следующего фрейма.

А вот ниже твориться настоящая магия. Так как я совершенно не знаю ассемблер математического сопроцессора, то детально рассказать что там происходит не могу. Но у нас есть математика выше.

8webhe0ntvlgnyydshmajradhh4.png

И ниже пошёл ассемблер работы с плавающей точкой. Там в памяти рассчитывается и формируется новый фрейм ёлки.
Обратите внимание на операцию сравнения регистра cx c 1000 и если он равен, то мы переходим на метку treedone. Гланем, что же на этой метке.

wgrfu2pqc7oy_9h9ysrdj6q8mlw.png

Итак, в clipped мы инкрементируем регистр cx и джампимся опять в цикл расчёта ёлки. В treedone мы копируем буфер получившейся ёлки в реальный фреймбуффер видеокарты с помощью магической инструкции rep movsw (чесслово только узнал о ней).

1salkwxejt7rqfkgrvtfwwaubly.png

Тут не самое элегантное место, мы просто тупо крутимся в цикле, пока счётчик таймера у нас не изменится.

vwxkqjztxdtidkekkvsaxbxsaz4.png

Это у нас обработчик прерывания системного таймера, относительно которого мы и синхронизируем вращение и отрисовку ёлочки.

qq-hyg1e5_rymwzasu7te5jizjw.png

В самом низу у нас находятся вспомогательные расчётные константы (я не стал все приводить сюда).

В версии от 2019 года, есть ещё дополнительная надпись. Она задана константно и выводится с помощью кода:

mmdjyh0h78bccqpxmrclbxtiobw.png

А сама надпись представляет собой просто изображение, скрытое в бинарных константах.

unrpmcqaqndklbscokh0r1it65o.png

Не буду приводить всё, и так понятно.

Как по мне, это прекрасная демка выходного дня, чтобы поупражняться в ассемблере, вспомнить молодость и, так сказать, запилить новогоднее настроение!

С Наступающим Новым Годом!!!


Дорогие коллеги, от всей души хочу поздравить вас всех с наступающим Новым Годом! Пускай в Новом Году будет больше творческих свершений, позитивных эмоций, вдохновения, сил, а так же возможностей для самореализации!
Рождественская демка под ДОС 1986 г.

8xzqbhb0at3_pjylb5c4366w_t8.png

oug5kh6sjydt9llengsiebnp40w.png

© Habrahabr.ru