...

вторник, 17 сентября 2013 г.

[Перевод] UNIX® на приставке Game Boy Advance

Введение




В этой публикации я рассказываю про gbaunix, забавный эксперимент, в ходе которого я запустил древнюю версию операционной системы UNIX в симуляторе на популярной (во время публикации, в 2004 году — прим. перев.) портативной консоли. А именно, UNIX 5 издания на 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

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий