Pull to refresh

UNIX® на приставке Game Boy Advance

Reading time 11 min
Views 30K
Original author: Amit Singh

Введение


В этой публикации я рассказываю про gbaunix, забавный эксперимент, в ходе которого я запустил древнюю версию операционной системы UNIX в симуляторе на популярной (во время публикации, в 2004 году — прим. перев.) портативной консоли. А именно, UNIX 5 издания (вышел в 1974 году, 39 лет назад) на Nintendo Game Boy Advance. Это может быть интересно разработчикам homebrew для Геймбоя, студентам-айтишникам со специализацией по ОС, эмуляторам или компиляторам, гикам-юниксоидам.
image

Нинтендо присутствует на рынке игр с 1889 года (sic!). Геймбой — это бренд целой линейки портативных консолей, которая весьма успешно продается и по сей день. С момента выпуска в 1989 году было продано 175 млн штук. В этой статье рассматривается Game Boy Advance, сокращенно GBA, и его аналог GBA SP.

Железо:


  • 32-битный процессор ARM (RISC), работающий на частоте 16.78 МГц
  • 32-битный процессор ARM (RISC), работающий на частоте 16.78 МГц
  • 8-битный процессор Z80 (CISC), частота 4.2 или 8.4 МГц. Добавлен для совместимости со старыми Геймбоями, у них это был центральный процессор
  • 4 16-битных таймера
  • 4 канала DMA
  • Цветной TFT экран, разрешение 240x160
  • Стереозвук через наушники
  • 10 кнопок
  • Последовательный порт
  • Интерфейс для картриджа GamePak


В консоли есть несколько блоков памяти:
  • 16 KB BIOS ROM
  • 256 KB внешней RAM, распаянной на плате (EWRAM)
  • 32 KB внутренней RAM, на кристалле ЦП (IWRAM)
  • 1 KB RAM для фона и палитры спрайтов
  • 96 KB Видео RAM
  • 1 KB RAM для атрибутов объектов
  • До 32 MB ROM картриджа
  • До 64 KB SRAM картриджа, опционально


Следует заметить, что у ROM картриджа есть два дополнительных отображения в адресном пространстве консоли. Кроме того, картридж может содержать несколько банков памяти с различным объемом или таймингами.

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

Железо консоли в основном доступно через хорошо документированное адресное пространство. Различные регистры ввода-вывода мапятся на адреса памяти. Дополнительно к этому, BIOS содержит множество функций, доступных через софтовые прерывания.

Лирическое отступление про архитектуру ARM
ARM

Нинтендо — лидер на рынке приставок, а ARM — лидер на рынке RISC-процессоров. Сейчас АРМы присутствуют почти во всей электронике. Интел рулит на десктопах, это да. Но процессоры для РС составляют весьма малую долю общего производства. В 2003 году было произведено 782 млн устройств на АРМах. В целом, доля этой архитектуры в районе 75%.

Чаще всего АРМ употребляют в одном предложении со словами «встроенный», «производительный», «дешевый», «маложрущий», «RISC». АРМ лицензирует архитектуру непосредственно производителям процессоров, в том числе и таким большим, как Интел, Эппл, Самсунг.

Первый АРМ был разработан в Acorn Computers Limited в середине 80-х. Тогда это расшифровывалось как «Acorn RISC Machine». Самая первая версия архитектуры АРМ, ARMv1, поддерживала 26-битную адресацию и была весьма медленной. Первый процессор этой архитектуры, ARM1, был периферийным процессором в микрокомпьютере ВВС и прототипе рабочей станции Archimedes. И это был один из первых процессоров архитектуры RISC. Основные особенности: отложенное ветвление, регистровые окна, каждая инструкция выполняется за один такт.

В Apple пытались применить АРМы в начале 90-х. В сотрудничестве с Acorn они запустили новую компанию, Advanced RISC Machines, Limited. Еще одним соучастником стал VLSI Technology. Расшифровка аббревиатуры поменялась. Новый процессор назывался ARM6, архитектура — ARMv3. Появилась полностью 32-битная адресация. Этот процессор использовался в Apple Newton.

В Геймбое используется процессор ARM7TDMI, архитектура версии ARMv4T.

ARM в Геймбое


ARM7TDMI — это популярный встраиваемый 32-битный процессор со слегка ограниченной функциональностью. У него нету кэша и MMU. Обозначение расшифровывается так:
Процессор ARM7
Поддерживает набор 16-битных инструкций Thumb
Поддерживает Debug прямо в железе
Имеет встроенный блок 32-битного уМножения, результат — 64-битный
Есть EmbeddedICE для отладки

В ядре ARM7TDMI одна 32-битная шина для данных и инструкций. Данные могут быть размером 8, 16, 32 бита. Только инструкции загрузки, сохранения и обмена могут иметь доступ к памяти. Есть 31 32-битный регистр общего назначения, 6 регистров статуса, сдвиговый регистр, блоки арифметики и умножения. Не все регистры доступны одновременно. Например, в режиме ARM доступны 16 общих и 1 или 2 статусных регистра. Есть 7 режимов работы процессора: обычный для выполнения программ, и 6 специальных. Это быстрое прерывание, прерывание, супервизор, остановка, неопределенное состояние и системный режим. Также поддердиваются два набора инструкций — ARM и Thumb.

Геймбой поддерживает 4 канала DMA. Процессор умеет два типа прерываний — обычные IRQ и быстрые FIQ, но в консоли используются только обычные.

ARM7 имеет простой трехстадийный конвейер:
Команда извлекается из памяти и помещается в очередь
Команда декодируется
Команда выполняется. При этом происходит чтение из регистров, вычисление результатов и запись из в регистры.

Таким образом, в любой момент времени одна команда выполняется, следующая декодируется, а через одну — извлекается.

Thumb

Относительный недостаток RISC-процессоров — относительно объемный код. Это увеличивает объем программы и ведет к потере эффективности кэша, излишнему трафику памяти и потреблению энергии. Для встраиваемых приложений это особенно плохо. Для уплотнения кода разработали архитектуру Thumb.

Thumb — это сжатая до 16 бит система 32-битных команд ARM. Поддерживается набор самых ходовых инструкций. Работают они с теми же 32-битными регистрами. Процессоры с поддержкой Thumb оснащены декодером в конвеере, который преобразует их в обычные ARM-инструкции. Разница получается примерно такой:
  • Экономится 35% объема кода
  • Надо на 40% больше команд
  • Код на 40% медленней для 32-битной памяти
  • Но на 60% быстрее для 16-битной
  • Энергопотребление на 30% меньше


Обычно используется смесь команд ARM и Thumb. Тип исполняемой команды указывается специальным флагом. В Геймбое есть 32 Кб быстрой памяти прямо на кристалле. Обычно, оттуда выполняется критичный по скорости код. Все остальное уместно скомпиоировать в Thumb и выполнять из медленной памяти картриджа.

habrahabr.ru/post/92494 — полезное чтиво вообще про ARM. — прим. перев.


Высокоуровневая архитектура gbaunix


gbaunix — это UNIX 5 издания, запущенный на Геймбое. Для этого используется SIMH, симулятор разных антикварных компьютеров, написанный на С. SIMH может эмулировать много чего еще, но здесь используется только PDP-11. Для Геймбоя существует несколько тулчейнов С, поэтому получилось малой кровью портировать SIMH на Геймбой. Архитектура получившейся системы показана ниже:
image

Картридж gbaunix представляет собой конкатенацию рантайма симулятора и образа диска с ОС. Остаток места можно оставить пустым, или занять чем-нибудь полезным.

Симулятор представляет собой минимально модифицированный SIMH. Специфичные для Геймбоя особенности реализованы в отдельном уровне абстракции.

TTY вывод.

gbaunix имитирует текстовый терминал для SIMH. Вывод перенаправляется на выполняемую на Геймбое процедуру, которая форматирует его и выводит на экран. Поддерживается скроллинг. printf() разбивается на sprintf() в буфер и отображение его на экране.

TTY ввод.

Сейчас вменяемой поддержки ввода вообще нет. Можно только выполнить определенную при компиляции последовательность shell-команд. Она задается в файле gba/gba_kbd.h. При работе UNIX кнопка Start подает следующую команду из списка на выполнение. Есть возможность следить за нажатиями кнопок, опросом или через прерывание. Пример:

/* gba/gba_kbd.h */

const char *gba_kbdinput[] = {
    "unix\r",
    "root\r",
    "chdir /work\r",
    "ls -l\r",
    "./fact 100\r",
    "cat hanoi.c\r",
    "./hanoi\r",
    "./hanoi 3\r",
    "chdir /tmp\r",
    "echo 'main() { printf(\"Hello, World!\\n\"); }' \
        > hello.c\r",
    "cc hello.c\r",
    "./a.out\r",
... /* more commands */
    NULL,
};


Файловая система.

Образ диска занимает 2.5 Мб, он помещается только в ROM картриджа. Хотя эта память и read-only, необходимо как-то обрабатывать запись в нее. Для этого при каждой операции записи создается буфер в RAM. Все операции чтения и записи сначала проверяются на попадание в какой-либо буфер. По мере роста количества таких буферов происходит их слияние. Симулятору подсовывается виртуальное stdio.

Память.

Симулируемому PDP-11 доступно 128 Кб памяти из доступных в железе 256 Кб EWRAM. Есть некоторая оптимизация работы с памятью наподобие использования DMA.

Прочее.

Здесь всякий код типа инициализации среды исполнения, TTY, прерываний, файловой системы итд.

Разработка

gbaunix работает как на эмуляторе Геймбоя, так и на железной консоли. Для запуска в железе понадобится флеш-картридж и программатор для него.
Гораздо более удобно пользоваться эмулятором Гемйбоя, особенно если есть желание повозиться с исходниками. gbaunix пока очень условно оптимизирован, и очень медленно работает на железной консоли. Эмулятор позволит работать с максимально доступной скоростью, а это сэкономит много нервов.

Моя среда разработки развернута под Mac OS X. Используется эмулятор Boycott Advance и тулчейн devkitARM, собранный из сырцов. Проверял на реальной консоли с флеш-картриджем.

Лирическое отступление об истории UNIX



Здесь был большой текст, не имеющий прямого отношения к тематике статьи, но слишком любопытный, чтобы от него избавиться. Вынес в отдельный пост — habrahabr.ru/post/194160

Демонстрация работы gbaunix, с комментариями


Пятое издание, июнь 1974


Я использую пятое издание, потому что это самая старая версия, для которой существуют в электронном виде загружаемые образы системы и исходный текст ядра. Его мы попробуем перекомпилировать, потому что это труЪ.

При включении питания, gbaunix покажет немного информации о железе PDP-11 и приглашение загрузчика — @. Если нажать Start, очередь команд выдаст имя ядра для загрузки — unix. Загрузка на железном Геймбое занимает около двух минут. После загрузки появляется приглашение залогиниться.
image

Точка с запятой в приглашении (;login это еще и бумажный журнал, выпускаемый USENIX Association) необходима для популярного в начале 1970-х терминала Teletype model 37. Она переводит его в полнодуплексный режим. Все другие терминалы, включая наш GBA TTY, просто выводят символ на экран.

Даже у столь ранней версии UNIX есть файлы блочных и символьных устройств. RK0 это первый диск, /dev/mem — отображение памяти системы для отладки с дебаггером и накладывания патчей на горячую.
image

glob, сокращение от global, это внешняя по отношению к шеллу команда для раскрытия метасимволов * и ? в аргументах команд. glob раскрывает метасимволы и вызывает нужную команду. Если совпадений нет, выдается традиционная ошибка No match.
image

Любопытно, что некоторые команды наподобие mkfs спрятаны в /etc, подальше от случайного вызова кривыми руками.

dc — это калькулятор с обратной польской нотацией. Это первая программа, запущенная на PDP-11, еще до создания версии UNIX для этого компьютера.

Типичное ядро пятого издания занимает меньше 26 Кб. Шелл занимает 5738 байт, а /init — всего 1972 байта. Также есть минимально приличный скрипт /etc/rc. /etc/update обновляет суперблок файловой системы каждые 30 секунд, для уменьшения потерь при сбое. Подробный вывод команды ls включает в себя права доступа, количество ссылок, владельца, размер в байтах, время последнего изменения и имя.
image

К эпохе пятого издания уже была создана богатая инфраструктура разработки с поддержкой множества языков программирования. Например, Algol-68, APL, Ассемблер, BASIC, C, FORTRAN, M6, PASCAL, Snobol, TMG. В принципе, gbaunix позволяет программировать прямо на Геймбое на нескольких языках (это в теории, в реальности это как минимум неудобно из-за отсутствия клавиатуры). В поставку входят компиляторы/интерпретаторы для С, ассемблера PDP-11, BASIC, шелла и Фортрана. Алгол я тоже пробовал, но в образ диска его не добавлял. Для примера показываю Ханойские башни.
image

image

image
Больше скриншотов можно посмотреть на специальной странице со скриншотами

Последующие издания

Шестое издание (май 1975) оставило след в истории, потому что от него взяли свое начало BSD и Xenix. Джон Лайонс написал свои знаменитые "Комментарии Лайонса к 6-й версии UNIX, с исходным кодом". Кроме того, это самая ранняя полностью сохранившаяся версия UNIX. Документация к пятому изданию утеряна, от четвертого и более ранних не осталось почти ничего. Начиная с шестого издания, развитие UNIX-систем заметно оживилось. Потом были седьмое издание в январе 1979, восьмое в феврале 1985, девятое в сентябре 1986 и десятое в октябре 1989.

BSD



На Геймбое можно запустить еще несколько систем, поддерживаемых в SIMH. Но это все более и более сложно, и упирается в доступную оперативную память. Пара скриншотов с BSD 2.9:

image

image

Оптимизация gbaunix


У gbaunix есть предпосылки для экспериментов, влияющих на скорость работы. Но это потребует перекомпиляции.

Кртитческий код в IWRAM

В gbaunix есть примеры компиляции кода для ARM и хранения его в IWRAM. Вероятно, полезней всего это будет для кода симуляции процессора.

DMA

Геймбой позволяет работу с памятью несколькими способами, с разной производительностью и ограничениями. gbaunix использует DMA3 (общего назначения) для функций memcpy() и memset(). Более того, BIOS содержит функции для копирования и заполнения памяти через софтверные прерывания. В целом, gbaunix поддерживает тонкую настройку работы с памятью.

Кэширование

Как я уже говорил, gbaunix эмулирует stdio и виртуальный диск, представляя память картриджа как файл UNIX. Поскольку железо не позволяет писать в ROM, приходится обходиться буферами, как сказано выше. gbaunix умеет предзагружать в буферы фрагменты диска. Это позволяет заметно ускорить загрузку системы.

Перекомпиляция ядра UNIX


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

Оценим размер исходного кода пятого издания.
Заголовки: 418 строк в 13 файлах
C: 7222 строк в 43 файлах (включая драйвера для периферии)
Ассемблер: 1080 строк в 2 файлах

Я даже и не пытался компилировать ядро прямо на Геймбое. Это займет неопределенно долгое время, и у нас закончится память под буферы. Да и вообще, аппаратура для такой задачи слишком маломощная.

Следюущая последовательность команд предполагает, что есть рабочая инсталляция пятого издания, на реальном железе или на симуляторе, и исходник ядра в каталоге по умолчанию — /usr/sys

Удаляем библиотеки, возможно оставшиеся от прошлой компиляции:

# chdir /usr/sys
# rm -f lib1
# rm -f lib2


Перед началом компиляции надо изучить и правильно указать параметры в файле /usr/sys/param.h.

Если компилятор выдает ошибку "undefined KISA0", надо добавить определение в /usr/sys/seg.h:

# echo '\#define KISA 0172340' >> /usr/sys/seg.h


Собственно компиляция:

# chdir ken
# cc -c -O *.c
...
# ar vr ../lib1 main.o alloc.o iget.o prf.o rdwri.o \
slp.o subr.o text.o trap.o sig.o sysent.o clock.o fio.o \
malloc.o nami.o pipe.o sys1.o sys2.o sys3.o sys4.o
...


Компиляция драйверов и прочего. Можно частично отключить:

# chdir ../dmr
# cc -c -O *.c
...
# ar vr ../lib2 *.o


Конфигурация системы и линковка. На выходе получается бинарник ядра:

# chdir ../conf
# cc mkconf.c
# mv a.out mkconf
# echo rk | ./mkconf
# cc -c c.c
# as l.s
# mv a.out l.o
# as mch.s
# mv a.out mch.o
# ld -x l.o mch.o c.o ../lib1 ../lib2
# mv a.out rkunix


rkunix — это и есть свежескомпилированное ядро. Если положить его в корневой каталог /rkunix, и при загрузке дать команду rkunix вместо unix, то оно и загрузится.

Идеи и предложения


Краткий список перспективных идей. Интерес большей частью академический

Нативный порт UNIX на Геймбой

Портирование UNIX на Геймбой было бы хорошим упражнением для студентов в рамках курса по операционным системам. Производительность должна резко вырасти. Древние ОС достаточно малы, чтобы поместиться в голове одного человека. Шестое издание состоит из 44 файлов:
14 заголовков C
28 файлов с кодом на C
2 файла с ассемблером.

Все вместе содержит меньше 9000 строк кода. Это с драйверами устройств. Ассемблера там около 10%.

На мой взгляд, задача вполне посильная. Могут быть трудности с ассемблерным кодом и портированием компилятора.

Улучшение производительности

Хотя я и попытался ускорить работу где это возможно, но до окончательного решения вопроса еще далеко. Можно начать с кода симуляции процессора (pdp11_cpu.c) — он выполняется самую большую долю времени.

Механизм ввода

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

Эмуляция оригинального Макинтоша на Геймбое

Первый Макинтош не отличался особо продвинутым железом: 16-битный процессор с частотой 8 МГц, 128 Кб RAM, 64 Кб ROM, нет ни кэша, ни прерываний, флоппи-диск объемом 400 Кб, монохромный экран с разрешением 512х342. У Геймбоя железо все-таки помощнее, кроме экрана. В принципе, можно создать или портировать легковесный эмулятор Макинтоша. Нехватку экрана компенсировать прокруткой, имитировать запись на диск буферами итд.

Другой вариант — портировать эмулятор 8088 для запуска древней версии ДОСа.

Где скачать


gbaunix-0.0.tar.bz2

Следут заметить, что для запуска надо иметь образ диска RK05. Он не входит в комплект, но его можно скачать с сайта PDP Unix Preservation Society, если согласиться с условиями лицензионного соглашения.

http://minnie.tuhs.org/PUPS/

Использование


Если хочется просто посмотреть, то можно обойтись без перекомпиляции и слепить в одну кучу рантайм симулятора и образ диска:

% cat unixv5.tmp disks/unixv5.dsk > unixv5.gba


unixv5.gba — это готовый к запуску образ картридж. Его можно использовать и с эмулятором, и с железной консолью.

Если есть желание перекомпилировать gbaunix (очень рекомендуется, собственно это единственный способ получить fun от процесса), то понадобится среда кросс-компиляции для архитектуры ARM. Возможно, понадобится поправить Makefile. Образ RK05 должен быть под именем disks/unixv5.dsk. После этого, теоретически должно хватить одной команды make.

Что еще почитать на эту тему


Исходники, бинарники и документацию старых версий UNIX
Тексты Денниса Ритчи
Документация к SIMH
Tags:
Hubs:
+105
Comments 8
Comments Comments 8

Articles