Одной из целей проекта была возможность показать, что для создания программ, в том числе игр, для компьютера Электроника БК-0010 можно использовать современный компилятор языка Си, такой, как gcc.
Игра целиком написана на Си (стандарт gnu23); на ассемблере оставлены только точка входа,
memset для внутренних нужд компилятора и распаковщик ZX0.
Для сборки проекта понадобится кросс-компилятор gcc для архитектуры DEC PDP-11 с небольшой доработкой.
Игра состоит из двух двоичных файлов, которые собираются из общего набора исходников:
- DIGTIT_V1.1.BIN - заставка: распаковывает обложку в экранное ОЗУ (ZX0), играет мелодию Popcorn, крутит демо-панель с историей игры и описанием управления. По нажатию любой клавиши (или кнопки джойстика) через EMT 36 дочитывает с носителя файл игры по адресу 01000 и передаёт ему управление.
- DIGGER_V1.1.BIN - собственно игра: уровни, спрайты, звуковые эффекты и фоновая музыка (Popcorn в обычном режиме и галоп из увертюры «Вильгельм Телль» в режиме Bonus).
Обе программы загружаются по адресу 01000 и целиком помещаются в ОЗУ БК-0010 до начала экранной области (040000), т.е. в 15872 байта на код и данные вместе.
| Клавиша | Действие |
|---|---|
| Стрелки / джойстик | движение Диггера |
| Пробел / кнопка джойстика | выстрел файрболом |
| S | вкл/выкл звуковых эффектов |
| M | вкл/выкл фоновой музыки |
| СБР | пауза (продолжение - любой клавишей) |
| СТОП | перезапуск игры |
При сборке с -DDEBUG дополнительно доступны отладочные клавиши D (уровень сложности),
L (добавить жизнь) и N (следующий уровень).
Для настройки кросс-компилятора gcc для процессора КР1801ВМ1 понадобятся свежие исходники компилятора gcc, которые можно взять с ftp.gnu.org из папки gcc и свежие исходники binutils, которые можно найти в папке binutils
На момент написания это были версии gcc-14.2.0 и binutils-2.43.
Сначала, создаём папку, в которую будем скачивать компилятор и binutils и заходим в эту папку:
mkdir -p gcc-KP1801BM1/src
cd gcc-KP1801BM1/src
Скачиваем в эту папку архивы с исходниками gcc и binutils, например так:
wget https://ftp.gnu.org/gnu/gcc/gcc-14.2.0/gcc-14.2.0.tar.gz
wget https://ftp.gnu.org/gnu/binutils/binutils-2.43.tar.gz
Распаковываем архивы с gcc и binutils:
tar -xvzf gcc-14.2.0.tar.gz
tar -xvzf binutils-2.43.tar.gz
Заходим в папку gcc-14.2.0 и запускаем скрипт ./contrib/download_prerequisites
cd gcc-14.2.0
./contrib/download_prerequisites
Скачиваем туда же и применяем патч для получения возможности использования компилятором команд процессора sob и xor, которые присутствуют в процессоре КР1801ВМ1, но отсутствуют в DEC PDP-11/10:
wget https://github.com/prcoder-1/digger-bk0010/raw/refs/heads/main/gcc-KP1801BM1.patch
patch -p1 < gcc-KP1801BM1.patch
Выходим снова в папку gcc-KP1801BM1 и создаём там папку build, входим в неё и создаём папки для сборки компилятора и binutils:
cd ../..
mkdir build
cd build
mkdir gcc
mkdir binutils
Заходим в папку binutils и конфигурируем binutils:
cd binutils
../../src/binutils-2.43/configure --prefix $HOME/xgcc --bindir $HOME/bin --target pdp11-aout
В этой команде после параметра --prefix указан путь на папку с файлами кросс-компилятора, где будут находиться папки include, lib и т.д. кросскомпилятора. В данном случае папка кросс компилятора будет расположена в домашней директории в папке xgcc.
После параметра --bindir указан путь куда будут помещены исполняемые файлы кросс-компилятора. В данном случае использована папка bin в домашней папке пользователя.
Далее, собираем binutils и, затем, устанавливаем его по пути, указанном в предыдущей команде:
make -j7
make install
Параметр -j7 после make указывает на то, что для сборки будут использованы семь ядер компьютера. Имеет смысл указывать количество, на одно ядро меньшее, чем присутствует в компьютере - для избежания зависаний ОС во время сборки.
Далее, выходим из этой папки, и таким же образом конфигурируем gcc войдя в папку gcc:
cd ../gcc
../../src/gcc-14.2.0/configure --prefix $HOME/xgcc --bindir $HOME/bin --target pdp11-aout --enable-languages=c --with-gnu-as --with-gnu-ld --without-headers --disable-libssp
Затем, собираем gcc и устанавливаем его:
make -j7
make install
В результате этого процесса, после инсталляции, в папке bin домашней папки должны появится запускаемые файл компилятора, такие как pdp11-aout-gcc, pdp11-aout-as, а в папке xgcc (тоже в домашней папке) должны появится папки include, lib, pdp11-aout и др.
В папку include папки xgcc необходимо поместить файл stdint.h, который можно скачать отсюда: stdint.h
В нём находятся описания 8-ми, 16-ти и 32-битных целых типов данных необходимые для сборки проекта. 32-битные типы дороги по коду и скорости, поэтому применяются точечно - например, для счёта очков, который, как и в оригинале, шестизначный (при превышении 999999 обнуляется).
В проекте используется система сборки make. При наличии установленного компилятора pdp11-aout-gcc, для сборки проекта достаточно запустить утилиту make в папке с проектом:
make
В результате работы make получаются два запускаемых файла: DIGGER_V1.1.BIN (игра) и DIGTIT_V1.1.BIN (заставка).
Номер версии задаётся переменной VERSION в начале Makefile и подставляется в имена файлов и в текст на экране заставки.
Там же переменной XGCC задаётся путь к папке кросс-компилятора.
Все настройки сборки проекта находятся в файле Makefile.
Секция asm-file - отладочная. В ней из некоторых файлов на языке Си генерируются файлы на языке ассемблер (файлы с расширением .s). Сделано это с целью отладки, чтобы можно было посмотреть - какой ассемблерный код получается из сишного. Для сборки запускаемого бинарного файла эта секций не требуется.
Секция crt0 создаёт маленький объектный файл с точкой входа в программу. Этот файл нужен для того, чтобы при запуске программы с адреса загрузки запустилась функция main() языка Cи.
Секция aout2bin создаёт одноимённую вспомогательную программу для создания файла .bin из файла a.out. Она разбирает структуру файла a.out и создаёт необходимый заголовок для файла .bin. Для сборки этой утилиты необходимо, чтобы в системе был установлен компилятор gcc (обычный, не кросс).
Секция libs собирает различные процедуры (вывод спрайтов, вывод звука, случайное число, задержка, работа с памятью, распаковка ZX0) в библиотеку libs.a для дальнейшего использования по мере необходимости.
Секции digger-out-file и title-out-file линкуют, соответственно, игру и заставку, а секция bin-files превращает полученные файлы a.out в загружаемые на БК файлы .BIN.
- digger.c - основной файл проекта в котором реализована логика игры
- digger_levels.c - файл с закодированными уровнями игры
- digger_sprites.c - файл со спрайтами игры
- digger_short_font.c - усечённый шрифт (только используемые в игре символы)
- digger_music_background.c - фоновая музыка игры (Popcorn и тема режима Bonus)
- title.c - программа заставки: вывод обложки, демо-панель, запуск игры через EMT 36
- digger_title.c - обложка заставки, упакованная компрессором ZX0
- digger_credits.c - тексты демо-панели: история игры и описание управления
- digger_sprites_title.c - спрайты, используемые в демо-панели
- digger_full_font.c - полный шрифт (получен из шрифта ПЗУ БК-0010)
- digger_music_title.c - музыка заставки
- sprites.c, sprites_extra.c - процедуры вывода спрайтов
- sound.c, sound_pwm.c, sound_vibrato.c - процедуры вывода звука (обычный тон, тон с заданной шириной импульса для огибающей, вибрато)
- tools.c - дополнительные методы, такие как получение псевдо-случайного числа, задержка, конвертация числа в строку и т.д.
- memory.h - файл с описанием карты памяти БК-0010 и основных регистров управления
- memory.s - реализация подпрограммы _memset для внутренних целей компилятора
- emt.h - описания функций для вызова основных EMT
- dzx0.s - распаковщик ZX0 (92 байта) в обвязке под C ABI
- stdint.h - описания целых типов для кросс-компилятора
- crt0.s - точка входа, передающая управление в main()
- a.out.ld - скрипт для линковщика в котором описано распределение доступной памяти и расположение секций (код программы, данные, неинициализированные данные)
- aout2bin.c - утилита для преобразования файлов a.out в файлы .BIN
- png2title.py - упаковка PNG-обложки (4 цвета палитры БК) в digger_title.c; требует внешний компрессор ZX0
- bmp2sprite.cpp / sprite2bmp.cpp - конвертация спрайтов между форматом БК и BMP (скрипты-обёртки bmp2spr.sh и spr2bmp.sh)
- gcc-KP1801BM1.patch - патч компилятора для поддержки команд sob и xor процессора КР1801ВМ1
В проекте предусмотрена автоматическая сборка документации при помощи утилиты doxygen. Для генерации документации достаточно просто запустить эту утилиту:
doxygen
или собрать цель проекта:
make docs
Для удобства отладки в проекте предусмотрен запуск скомпилированного файла на компьютере БК-0010 при помощи Gryphon-MPI. Запуск осуществляется при помощи сборки цели проекта g-mpi. Цель использует утилиту curl для связи с Gryphon-MPI, передачи и запуска скомпилированного файла.
Цель состоит из трёх команд. Первые две передают на Gryphon-MPI оба файла - игру и заставку:
curl -i -o /dev/null -X POST -H "Content-Type: multipart/form-data" -F "storeas=${GMPI_UPLOAD_DIR}/${BIN_FILE_1}" -F "size=$(shell stat -c%s ${BIN_FILE_1})" -F "file=@${BIN_FILE_1}" "${GMPI_API_URL}/upload"
Где в переменной GMPI_UPLOAD_DIR содержится путь в файловой системе Gryphon-MPI по которому будет размещён файл, а BIN_FILE_1 и BIN_FILE_2 - имена двоичных файлов игры и заставки. В переменной GMPI_API_URL содержится путь на API Gryphon-MPI. Для определения размера передаваемого файла используется утилита stat.
Команда передаёт HTTP запрос POST с указанием имени передаваемого файла и места в файловой системе Gryphon-MPI в котором будет размещён бинарный файл.
Третья команда:
curl -i -s -o /dev/null "${GMPI_API_URL}/run?dev=file&emu10=no&fname=${GMPI_UPLOAD_DIR}/${BIN_FILE_1}"
Осуществляет запуск переданного файла с использованием API Gryphon-MPI. Для отладки запускается сразу игра (BIN_FILE_1), минуя заставку; чтобы проверить и заставку, достаточно подставить в этой команде BIN_FILE_2 - игру она найдёт и загрузит сама, так как оба файла уже лежат в одной папке GMPI_UPLOAD_DIR.

