/ MineAssemble

Prebuilt ISO — Video

MineAssemble — это крошечный загрузочный клон Minecraft, частично написанный на сборке x86. Я сделал его в первую очередь потому, что университет задание потребовало от меня реализовать игру на сборке для курса компьютерных систем. Поскольку я никогда раньше не реализовывал ничего более сложного, чем загрузчик «Hello World», я решил, что хочу одновременно научиться писать собственный код ядра.

Обратите внимание, что целью этого проекта было не для того, чтобы писать высокоэффективный ассемблерный код, оптимизированный вручную, а для развлечения и написания кода, уравновешивающего читаемость и скорость. Это в первую очередь достигается за счет правильного комментирования и последовательного структурирования кода.

Сразу начинать сборку было бы слишком безумно, поэтому я сначала написал реализацию ссылки на C с использованием библиотеки SDL, которую можно найти в каталог reference . Я начал писать его с мыслью, что если бы он был длиннее 150 операторов, исключая шаблон, не стоило бы делать его при сборке. Как и все оценки в мире программирования, этот предел оказался сильно заниженным, достигнув примерно 134 строк до добавления текстуры или даже входного кода.

После завершения эталонного кода я написал ядро шаблонный код (настройка VGA, прерывания и т. д.) и изменил ссылочный код C для работы с этим. Затем я начал медленно портировать все на рукописную сборку.

К сожалению, это оказалось намного больше работы, чем я ожидал, в настоящее время большая часть кодовой базы все еще находится на C. Медленный перенос всего на сборку непрерывный процесс. Код также еще не полностью совместим со всеми системами. Кажется, это вызывает исключения с плавающей запятой в некоторых настройках.

Как играть

QEMU

Чтобы запустить игру с QEMU, просто запустите make test Это быстрый и простой способ поиграть с ним.

Виртуальная машина

Если вы хотите использовать программное обеспечение для виртуализации как и VirtualBox, вы можете создать образ .iso с помощью make iso и смонтировать его. Виртуальной машине не нужен жесткий диск, и ей требуется не более 4 МБ ОЗУ.

Вы также можете записать этот образ на CD или DVD, но это довольно расточительно. Используйте метод USB-накопителя, чтобы попробовать его на реальном оборудовании, если это действительно не вариант по какой-либо причине.

USB-накопитель

Загрузка с USB-накопителя — отличный способ чтобы попробовать это на реальном оборудовании, но это требует немного больше работы. Обратите внимание, что этот процесс удалит все данные с USB-накопителя. Кроме того, убедитесь, что вы правильно указали имя диска, иначе вы можете случайно стереть жесткий диск!

  1. Отформатируйте USB-накопитель в FAT32 с указанием 1 МБ свободного места перед ним.
  2. Смонтируйте его, используя mount/dev/sdx1/mnt , где sdx — имя диска.
  3. Превратите его в аварийный диск GRUB с помощью grub-install --no-floppy --root-directory =/mnt/dev/sdx.
  4. Запустите make iso и скопируйте содержимое каталога iso на USB-накопитель.
  5. Отключите с помощью umount -l/dev/sdx1.

Теперь перезагрузите компьютер и загрузитесь с USB.

Отладка с GDB и QEMU

  qemu-system-i368 -gdb tcp :: 1234 -S -kernel mineassemble.elfi686-pc-elf-gdb mineassemble.elf (gdb) target remote  : 1234 (gdb) continue  

Создание цепочки инструментов кросс-компилятора

Чтобы собрать MineAssemble, вам необходимо иметь цепочку инструментов кросс-компилятора, состоящую из GNU Binutils и компилятор GNU C (GCC). Цепочка инструментов должна быть создана для цели i686-pc-elf . По причинам устаревшего характера GNU GCC и Binutils настраиваются во время компиляции, и вам нужно будет скомпилировать их из источник для создания кросс-компилятора. Также вам понадобится NASM Assembler.

Дополнительно вам может понадобиться QEMU для тестирования MineAssemble в эмуляторе и GNU Debugger (GDB) для отладки.

Установка предварительных требований

Вам потребуется установить несколько служебных библиотек для сборки Binutils, GCC, QEMU и GDB.

Используйте эквивалент вашей операционной системы для apt-get или скомпилируйте из исходного кода.

Установите libmpc, libgmp, libmpfr (обязательно для binutils, gcc и gdb)

  sudo apt-get install libmpc-dev libgmp-dev libmpfr-dev  

Установить flex и bison (требуется для GCC)

  sudo apt-get install flex bison  

Установить libsdl (дополнительный интерфейс для qemu)

  sudo apt-get install libsdl-dev  

Установить NASM

NASM не требуется для настройки во время компиляции, вы можете установить его с помощью диспетчера пакетов.

  sudo apt-get install nasm  

Create каталоги для исходных текстов, файлов сборки и двоичных файлов

  mkdir ~/src # Source co  demkdir ~/i686-pc-elf-build # Временные файлы сборки mkdir ~/i686-pc-elf # Место установки Toolchain  

Получить исходный код для Binutils, GCC, QEMU и GDB

  git clone git://sourceware.org/git/binutils.git ~/src/binutilsgit clone git://gcc.gnu.org/git/gcc.git ~ /src/gccgit clone git://git.qemu-project.org/qemu.git ~/src/qemugit clone git://sourceware.org/git/gdb.git ~/src/gdb  

Ознакомьтесь с последними версиями выпуска

  cd ~/src/binutils;  git checkout binutils-2_23_1cd ~/src/gcc;  git checkout gcc-4_8-branchcd ~/src/qemu;  git checkout v1.5. 0cd ~/src/gdb;  git checkout gdb_7_6-branch  

Сборка binutils для целевого i686-pc-elf

  mkdir ~/i686-pc-elf-  сборка/binutils;  cd ~/i686-pc-elf-build/binutils ~/src/binutils/configure --prefix = $ HOME/i686-pc-elf --target = i686-pc-elf --disable-shared --disable-nlsmake  -j 4 # параллельная сборка для 4 cpusmake install  

Сборка GCC (только компилятор C) для целевого i686-pc-elf

  mkdir ~/i686-pc-elf-build/gcc;  cd ~/i686-pc-elf-build/gcc ~/src/gcc/configure --prefix = $ HOME/i686-pc-elf --target = i686-pc-elf --enable-languages ​​= c --disable  -shared --disable-nlsmake -j 4 all-gccmake install-gcc  

Собрать qemu с целью i386-softmmu (с интерфейсом SDL)

  mkdir ~/i686-pc-elf-build/qemu;  cd ~/i686-pc-elf-build/qemu ~/src/qemu/configure --prefix = $ HOME/i686-pc-elf --target-list = i386-softmmu --enable-sdlmake -j 4make install  

Сборка GDB для цели i686-pc-elf

  mkdir ~/i686-pc-elf-build/gdb;  cd ~/i686-pc-elf-build/gdb ~/src/gdb/configure --prefix = $ HOME/i686-pc-elf --target = i686-pc-elfmake -j 4make install  

Добавить набор инструментов в $ PATH

  export PATH = $ HOME/i686-pc-elf/bin: $ PATH  

Соглашения о стилях

С чем-то столь же низкоуровневым, как сборка, вы быстро рискуете написать нечитаемый код, если у вас нет надлежащих соглашений о стилях. В этом проекте используются следующие соглашения об именах идентификаторов:

  WORLD_SX - определение в стиле C, обычно позволяющее настраивать thingsworldSX - нелокальные переменные (локальные переменные называются смещениями стека)  init_world - подпрограмма (использует подчеркивания вместо camelCase) .main_loop - локальная метка, используется только внутри подпрограммы  

Имена переменных не содержат префикса типа, потому что почти всегда очень ясно, какой тип переменная использует от своего имени или использования. Вот несколько примеров:

  float - векторы, углы, distanceint - время, координаты блока, нормальный байт блока - цвет палитры, тип блока  

Директивы, которые применяются к сегментам файла или ко всему файлу, например

  [бит 32] section .text  

не имеют отступа. Подпрограммы и локальные метки имеют один уровень отступа, а код или данные внутри имеют два уровня отступа. Один уровень отступа равен 4 пробелам.

Код обычно разделяется на блоки с комментарием выше, описывающим, что делается в блоке. Если строка требует дополнительных пояснений, после инструкции помещается комментарий.

Математические выражения с плавающей запятой систематически преобразуются из выражения referenceinfix в RPN (обратная польская нотация) в инструкции FPU. Любые оптимизации применяются впоследствии, если это будет сочтено необходимым. Этот систематический подход делает преобразование однострочных выражений C в десятки инструкций сборки терпимым и относительно безошибочным.

Объяснение

Внутренняя работа этой демонстрации действительно довольно прямолинейна — вперед. Код можно разделить на четыре разных компонента.

World

Мир хранится в виде массива байтов без знака , где каждый блок имеет значение либо BLOCK_AIR , либо BLOCK_DIRT . Массив хранится в разделе BSS и инициализируется функцией init_world . Он проходит через каждые x, y и z и создает мир, в котором нижняя половина — грязь, а верхняя — воздух.

Во время игры другой код вызывает set_block или get_block для взаимодействия с миром. Они просто вычисляют правильный индекс и записывают в массив или читают из него.

Ввод и столкновение

Ввод с клавиатуры собирается обработчиком прерывания IRQ1. Он записывает состояние up/down в 128-байтовый массив, индексированный сканирующим кодом. Он также устанавливает верхний бит в 1 , чтобы пометить этот ключ как обновленный. Он игнорирует событие нажатия клавиши, если клавиша уже настроена, чтобы игнорировать автоматические повторы клавиш.

Поскольку обработка ввода должна быть независимой от производительности, обработчик прерывания IRQ0 увеличивает время на 1 каждую миллисекунду для отслеживания времени. Это используется для вычисления разницы времени для масштабирования движения.

Перед визуализацией каждого кадра вызывается функция handle_input , которая собирает значения из keys , записанный обработчиком прерывания. Если верхний бит ячейки установлен в 1 , то он знает, что состояние ключа изменилось, и обрабатывает его соответствующим образом. Здесь обрабатываются все клавиши, кроме клавиш перемещения (AWSD).

После этого вызывается функция обновления для перемещения игрока в соответствии с текущей скоростью. Эта скорость частично контролируется функцией handle_input и частично проверкой неактивного состояния клавиш AWSD в этой функции. Скорость Y уменьшена для имитации силы тяжести. Затем следующее положение игрока определяется путем добавления скорости, умноженной на дельта-время.

Перед назначением новой позиции код сначала запускает функцию handle_collision для головы , центр тела и ступни. Он вызывает функцию трассировки лучей из этих позиций со скоростью в качестве направления, чтобы определить, произойдет ли столкновение, если игрок переместится в новую позицию. В этом случае скорость корректируется для в основном предотвращения столкновения. (Алгоритм не идеален, но работает довольно хорошо.)

Рендеринг

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

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

Трассировка лучей алгоритм вычисляет расстояние до сторон блока, в котором начинается луч для каждого измерения. Выигрывает кратчайшее расстояние, и положение луча перемещается на это расстояние, умноженное на направление луча. Это повторяется до тех пор, пока позиция не окажется внутри BLOCK_DIRT или если она находится за пределами мира. Конечное положение используется для вычисления стороны, которая была поражена, и координат текстуры. Затем вызывается функция ray_color , чтобы позволить блоку решить, какой цвет он будет выводить. Эта функция снова вызывает функцию raytrace , чтобы решить, затенен ли пиксель или нет, используя направление sunDir для луча. Он предотвращает бесконечную рекурсию, запрашивая трассировку лучей info вместо цветовой трассировки лучей. Эта альтернатива возвращает структуру с информацией о хитах вместо цвета.

Ресурсы

Одна из деталей, с которой вы имеете дело при использовании низкоуровневого режима VGA (режим 0x13 ) заключается в том, что вы не можете просто указать 24-битный или 32-битный цвет RGB для каждого пикселя. Вместо этого вы должны выбрать 256-цветную палитру и указать индекс для каждого пикселя. Простым решением здесь является использование 3-2-3 битовых каналов и использование цвета RGB в качестве индекса в палитре. К сожалению, это вообще не работает, потому что, имея только 4 варианта для зеленого цветового канала, невозможно представить все тонкие различные оттенки травяного блока.

Поэтому я решил создать палитру, которая может точно, почти точно передать каждый цвет, который использовали текстуры. Палитра позволяет вам указывать цвета RGB, но только с 6 битами на канал вместо 8. Это означает, что цвета будут немного размытыми, но это почти незаметно.

Я написал программу на C # который взял текстуры травы, грязи и сторон вместе с зарезервированными цветами: черный, белый и небо, и автоматически сгенерировал палитру и цветное представление палитры трех текстур. В итоге это сработало отлично!

Заставка работает немного иначе. Причина, по которой это растровое изображение, а не просто использование текстового режима, состоит в том, чтобы сделать вещи немного более упорядоченными. Ifirst попытался кодировать его так же, как и текстуры, но в результате получился файл C на 6400 строк. Затем я изменил его, чтобы просто написать строку из единиц и нулей для каждой строки, что работает намного лучше. Он даже позволяет просматривать заставку с помощью текстового редактора! 🙂

Растровое изображение экрана-заставки копируется непосредственно в память VGA, где '0' вычитается из каждого байта. Затем массив keys проверяется на нажатие клавиши ENTER перед загрузкой игры.. Проблема здесь в том, что пользователь также должен нажать ENTER в меню загрузчика GRUB, что означает, что он сразу же пропустит заставку. Эта проблема в настоящее время решается двойным ожиданием нажатия клавиши ENTER. Каким-то образом это работает даже при загрузке с Ctrl-X или другой комбинацией.

Лицензия

Этот проект находится под лицензией MIT.

Также включены некоторые производные работы с совместимым лицензированием:

  • init.asm , interrupts.asm — Получено из кода Мартена де Вриса и Мориса Боса (под лицензией MIT).
  • vga.asm — получено из кода Кристоффера Бубаха (общественное достояние)

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

Оцените статью
Gamicon.ru
Добавить комментарий