CNN TürkÖZET | Arda Güler verdi, bu sefer Bellingham attı! Real Madrid'den rahat galibiyet...Bollywood HungamaShah Rukh Khan unveils ‘Tu Mil Jaaye’ song starring Deepika Padukone from KING on his birthday; song blends romance, action and his ‘dehshat’Sky TG24Logistica, che impatto può avere la digitalizzazionePunchChinese state TV snubs Madrid match despite Mbappé apologyInquirerEstrada seeks Senate probe into killings of environmental defendersZDF heuteEntdecken Sie das ZDF-NachrichtenstudioSözcüAvrupa kapılarını açtı, Türkleri çağırıyorRFIMidterms 2026: la candidature du démocrate Abdul El Sayed divise la communauté juive du MichiganLa NaciónJavier Milei y sus medidas, en vivo: “Los que prometieron el paraíso nos terminaron conduciendo al infierno”, dijo el PresidenteObservador DesportoBuzinão e petardos agitaram Rossio este domingoAnadolu AjansıKüresel piyasalar önemli gelişmelerin izleneceği haftaya temkinli başladıHet Laatste NieuwsHij verstopte zich als achtjarige tussen stapel banden om Lewis Hamilton te zien, nu is Kimi Antonelli zelf de posterboy van Formule 1
The Daily Newsstand · Free, Always
Monday, November 2, 2026

[Перевод] Doom, System Shock и Ultima VII на ПЛИС за $15: компьютер из 8640 логических ячеек

Translate

Процессор RISC-V на 36 МГц с кешами, 8 МБ памяти, HDMI 720×480 при 60 Гц со стереозвуком 48 кГц, два порта USB и SD-карта. Всё это собрано из 8640 таблиц поиска (LUT) отладочной платы с ПЛИС за 15 долларов. Чтобы погонять систему как следует, я портировал на неё Doom, Descent, Tyrian, System Shock и Ultima VII, и все они идут вполне прилично.

От переводчика: это объединение первой и шестой частей серии m9b. В первой автор описывает устройство компьютера, в шестой рассказывает, как на него переносили игры. Части о процессоре, контроллере памяти, HDMI и USB-хосте остались в оригинале, ссылки на них сохранены.

Видео с платы: Doom, timedemo и игра · Descent, первый уровень · Tyrian на полной скорости · System Shock

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

Семейство Tang Nano

Платы Tang Nano от Sipeed — это ПЛИС Gowin на плате размером с пластинку жвачки, с портом USB-C для прошивки и набором разъёмов, который зависит от модели. По объёму логики четыре платы различаются почти в двадцать раз:

Четыре платы Tang Nano в виде квадратов, площадь которых равна числу LUT; жёлтым показана память в корпусе

Четыре платы Tang Nano в виде квадратов, площадь которых равна числу LUT; жёлтым показана память в корпусе

Площадь каждого квадрата равна числу 4-входовых таблиц поиска (LUT4) на плате. Жёлтые прямоугольники — кристаллы памяти в корпусе. Они нарисованы одного размера, потому что на каждой плате по 8 МБ; у 9K эта память разделена на два кристалла. Cortex-M3 у 4K — аппаратное ядро: оно расположено рядом с логикой, а не собрано из неё.

Плата

ПЛИС

LUT4

Блочная память

Память в корпусе

Аппаратный CPU

Tang Nano 1K

GW1NZ-1

1152

8 КБ

нет

нет

Tang Nano 4K

GW1NSR-4C

4608

20 КБ

8 МБ HyperRAM

Cortex-M3

Tang Nano 9K

GW1NR-9

8640

52 КБ

8 МБ PSRAM (2 кристалла)

нет

Tang Nano 20K

GW2AR-18

20 736

92 КБ

8 МБ SDRAM

нет

Блочная память считается по 2 КБ на блок, без битов чётности.

Основная часть ретрокомпьютерных проектов на Tang Nano живёт на 20K: ядер под неё довольно много. 1K хватает на маленький процессор, но не на компьютер вокруг него, к тому же у неё, в отличие от остальных плат серии, нет разъёма HDMI. У 4K прямо в кремнии есть Cortex-M3, и это была бы отличная основа для небольшого компьютера, но из-за того, как там устроена шина адреса, с ним сложно работать. Про эту плату я, скорее всего, ещё напишу.

У 9K аппаратного процессора нет. Если процессор нужен, его придётся собирать из тех же 8640 ячеек, что и всё остальное. Эту задачу я и выбрал, поставив себе несколько непреложных условий:

  • Никаких дополнительных вычислителей. Соблазнительно перепрошить и задействовать BL702, но этого не будет. Работает только ПЛИС.

  • Компьютер общего назначения. Игры служат тестами, и специальных аппаратных ускорителей под какую-то одну из них я делать не хочу.

  • Настоящий ввод и вывод. HDMI со звуком, USB-клавиатуры и мыши, SD-карта с настоящей файловой системой.

  • Портировать готовое. Взять существующий исходный код на C/C++ и перенести его на плату. Немного оптимизации допустимо, но игра должна выглядеть, звучать и вести себя в точности как оригинал.

Вот и всё. Отправной точкой могла бы стать готовая система, сгенерированная LiteX: он эту плату поддерживает. Но производительность результата, увы, довольно ужасная (по крайней мере, на момент написания).

Три кристалла в одном корпусе

Микросхема на плате, GW1NR-LV9QN88P, на самом деле состоит из трёх кристаллов в одном корпусе: самой ПЛИС и двух PSRAM по 32 Мбит (4 МБ каждая). У каждой PSRAM своя 8-битная шина с удвоенной скоростью передачи данных (DDR) до ПЛИС, рассчитанная на частоту до 166 МГц. Ни один из этих проводников не выходит за пределы корпуса: на плате микросхема выглядит как обычная ПЛИС с 88 выводами.

Корпус GW1NR-9: кристалл ПЛИС и два кристалла PSRAM, у каждого своя шина

Корпус GW1NR-9: кристалл ПЛИС и два кристалла PSRAM, у каждого своя шина

Два кристалла PSRAM сидят на разных шинах, поэтому могут передавать данные одновременно. В m9b оба работают на 144 МГц синхронно, в одном такте.

  • 8640 4-входовых таблиц поиска;

  • 52 КБ блочной памяти: 26 блоков по 2 КБ;

  • 8 МБ PSRAM на двух кристаллах;

  • 664 МБ/с — пиковая пропускная способность PSRAM на бумаге: 166 МГц × 2 фронта × 2 кристалла.

Эти цифры задают рамки для всего остального. 52 КБ блочной памяти — меньше, чем было у типичного домашнего компьютера 1980-х. Один кадр VGA, какой используют наши тестовые игры, занимает 320 × 200 байт и не поместился бы даже во все 26 блоков вместе. Поэтому программы и их данные живут в PSRAM, а блочная память уходит на кеши и буферы. 664 МБ/с у PSRAM — число на бумаге: в третьей части я прослеживаю его вплоть до того, что реально достаётся процессору, а это лишь малая доля.

Никакой выделенной аппаратуры для остального нет. Сериализаторы в микросхеме достаточно быстрые для HDMI и памяти, но кодировщик собран из логики. Звук передаётся внутри сигнала HDMI, так что это тоже логика. USB-хоста на плате нет: разъёмы USB подключены прямо к выводам ввода-вывода, и протокол тоже обрабатывает логика.

Система

Вот весь компьютер, разбитый по тактовым доменам.

Схема m9b по тактовым доменам

Схема m9b по тактовым доменам

Пунктиром обведены тактовые домены, жёлтым отмечена блочная память или PSRAM, красным — то, что находится на плате, вне микросхемы. Мост и буферная память передачи связывают домены: мост — процессор и контроллер, у которых общее дерево тактирования, а буферная память — контроллер и видеотракт, у которых оно разное.

Верхний уровень устроен довольно просто. Я взял высокопроизводительное ядро RISC-V (VexiiRiscv) и отдал заметную часть блочной памяти под щедрый кеш (24 КБ из наших 52), потому что задержка DDR-памяти довольно пугающая, и обращаться к ней слишком часто совсем не хочется. Во второй части возможности процессора и устройство подсистемы разобраны гораздо подробнее.

Контроллер PSRAM работает с обоими кристаллами синхронно: он формирует один поток команд и дублирует его на оба кристалла. Физический уровень (PHY) — просто набор сериализаторов и десериализаторов. Внутреннее устройство контроллера описано в третьей части. Кроме того, контроллер PSRAM связан с графическим и звуковым блоком через буферную память передачи, в которой помещаются две строки видеоданных и два звуковых буфера. У видеоблока есть и собственная память для палитры и таблицы строк; процессор пишет в неё напрямую, чтобы сократить задержку. Кодировщик HDMI — простой TMDS-кодировщик, который превращает пиксельные данные в сигнал HDMI. Эту подсистему разбирает четвёртая часть. USB-хост — ядро на микрокоде, которое обрабатывает протокол USB и даёт процессору простой регистровый интерфейс; ему посвящена пятая часть. Набор встроенной периферии завершают UART для отладки и SPI для работы с флеш-памятью и SD-картой.

Тактирование

Все тактовые частоты получаются из единственного кварца платы на 27 МГц через две ФАПЧ (PLL) микросхемы и три её делителя. Некоторые частоты нам неподвластны, и их нужно соблюдать, иначе система не заработает. Одна из них — пиксельная частота стандартного режима VESA (для выбранного мной режима 720×480 это 27 МГц). Значит, для сериализаторов нужна частота 5 × 27 = 135 МГц. Другая — 12 МГц для USB, стандартная для full-speed USB. Все остальные частоты приходится получать делением или умножением этих, причём обе ФАПЧ уже заняты: 12 и 27 не кратны друг другу. В итоге всё оборудование я тактировал кратными частоты USB: 36, 72 и 144 МГц. Хорошо согласованные частоты очень важны, потому что каждое пересечение тактовых доменов стоит задержки.

Сигнал

МГц

Откуда

Почему такое значение

Битовая частота TMDS

135

pll_hdmi: 27 × 5

HDMI передаёт 10 бит на пиксель по каждому каналу: 270 Мбит/с, по обоим фронтам 135 МГц

Пиксельная частота

27

135 ÷ 5

720 × 480 при 59,94 Гц — это режим на 27 МГц. Получена делением битовой частоты, поэтому два такта сериализаторов синфазны по построению

Ввод-вывод PSRAM

144

pll_main: 27 × 16 ÷ 3

Номинал 166 МГц; 144 — самая высокая частота, которая ещё делится до 48 для USB

Контроллер PSRAM

72

pll_main ÷ 2

Сериализаторы ввода-вывода принимают по 4 бита на вывод за такт контроллера

Процессор

36

72 ÷ 2

Столько, сколько процессор выдерживает по таймингам (37,8 МГц в последней сборке), и синхронно с контроллером

USB

12

pll_main ÷ 3, затем ÷ 4

Low-speed USB — это 1,5 Мбит/с с выборкой 8 раз на бит: ровно 12 МГц

Куда уходят ячейки

После размещения и трассировки проект занимает 7560 из 8640 логических ячеек (88%) и 25 из 26 блоков памяти. На деле ограничение жёстче: ячейки упаковываются парами в слайсы, и заняты 97% этих пар. При таком заполнении итоговая частота сборки сильно зависит от того, куда инструменты случайно поставят логику, поэтому каждая сборка заканчивается изучением отчёта о таймингах.

Блок

Логических ячеек

Блоков памяти

Что хранится в блочной памяти

Процессор (с кешами)

≈ 4340

18

L1 данных: 8; теги L1 данных: 2; L1 выборки команд: 4; теги L1 выборки: 1; предсказатель переходов: 1; регистры: 2

Кодировщик HDMI

≈ 630

–

Видеоблок, видеопамять

≈ 490

2 + 4

палитра и таблица строк: 2; буферная память передачи (строки, звук): 4

Контроллер PSRAM, мост

≈ 560

–

строчные буферы моста — в LUT-памяти

USB-хост, два порта

≈ 490

1

ПЗУ микрокода

UART, SPI, чтение флеш-памяти

≈ 270

–

Связующая логика: декодирование адресов ввода-вывода, пересечения доменов, сбросы

≈ 690

–

Цифры по блокам взяты из синтеза и приблизительны: размещение и трассировка оптимизируют проект поверх границ блоков, итог по ним — те самые 7560.

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

Игры: ретро наоборот

Все пять игр написаны для DOS-компьютеров начала 1990-х. Их современные порты рассчитаны на современную машину. Чтобы запустить их на m9b, часть этой модернизации пришлось откатить и посадить игры на диету по памяти, тактам и звуковому оборудованию, которая местами строже, чем на тех ПК, где они вышли впервые.

Игра

Порт

Скорость на m9b

Что оказалось важнее всего

Doom (1993, shareware)

doomgeneric

≈ 22 fps (timedemo)

процедуры отрисовки столбцов и полос; кеш данных

Descent (1995, shareware)

D1X Rebirth 0.40

≈ 10 fps, было 5

деления в наложении текстур; кабина

Tyrian 2.1 (1995)

OpenTyrian

35 fps, полная скорость, было 17

одна проверка границ на каждый пиксель

System Shock (1994)

Shockolate

≈ 7,4 fps, было 5,8

память: ролики на 17 МБ; невыровненные чтения; деления

Ultima VII (1992), обе игры

Exult 1.12.1

7–13 fps

память: куча 6,2 МБ в объёме меньше 6 МБ

Ultima VII, оригинальный движок

Ultima7_Decomp

8–10 fps (предел самого движка — 10)

блоки кучи, выровненные по слову; спрайты и тайлы по слову за раз

На какую машину они попадают

m9b выполняет команды строго по порядку, не имеет блока плавающей точки, работает на 36 МГц, и 8 МБ его памяти скрыты за кешами, промах в которых стоит около 25 тактов (третья часть). Звукового чипа и MIDI-синтезатора у него нет. Зато есть 32-битные аппаратные умножитель и делитель, достаточно быстрая SD-карта и видеоблок, который бесплатно переключает страницы и масштабирует изображение.

Программы именно портированы. Каждая игра — это её исходный код из основного репозитория, урезанный до того, что нужно для сборки, с пометкой на каждом изменении, и собственный платформенный слой вместо SDL.

Одна платформа на пять игр

Все пять игр используют общую небольшую среду выполнения, изначально написанную для Doom:

  • Библиотека C и файлы. picolibc, системные вызовы которой отображены на консольный UART и на FatFs, так что open, read и lseek работают с SD-картой. Порты на C++ (физика System Shock, весь Exult) используют libstdc++ из picolibc, включая исключения.

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

  • Видео. Каждая игра рисует свой экран 320 × 200 с 8-битным цветом, как и раньше. Порт копирует его в один из трёх буферов отображения и вычищает из кеша данных, а прерывание обратного хода кадра направляет на него таблицу строк. Каждая строка удваивается по горизонтали и выводится на две или три строки экрана: получается та же картинка 4:3, что давал VGA-монитор (четвёртая часть).

  • Звуковые эффекты смешиваются в звуковом прерывании на частоте 12 или 24 кГц вместо 48, и каждое значение повторяется на 4 или 2 выходных отсчёта. Сами эффекты изначально записаны с частотой 11 или 22 кГц, так что ничего не теряется. Смешивание же стоит несколько процентов процессорного времени вместо десятой части.

  • Загрузка. Программы хранятся на SD-карте и выбираются в загрузочном меню. Драйвер SD читает последовательности секторов одной командой, по 32 бита за раз: по мере его доработки загрузка уровня в Doom сократилась с 4,6 с до 1,7 с.

Музыка: отрендерить один раз, проигрывать всегда

Музыку во всех пяти играх писали под синтезатор: FM-чип Yamaha OPL на AdLib или Sound Blaster либо Roland MT-32. Ни один из них не помещается. Открытому ядру OPL2 на этой микросхеме нужно 1337 ячеек и 4 блока памяти, а свободно примерно тысяча ячеек и один блок. Программный синтезатор отнял бы 10–15% процессора ещё до того, как игра что-нибудь нарисует, а эмуляторы в самих портах считают в плавающей точке.

Поэтому музыка один раз рендерится на ПК, а во время игры проигрывается потоком с SD-карты. Для четырёх игр это эмуляция чипа OPL3 из libADLMIDI, которая играет собственными инструментами каждой игры и по правилам громкости её звукового драйвера: банк GENMIDI у Doom, банки HMI у Descent, тембры AIL у System Shock. Tyrian рендерится собственным проигрывателем и эмулятором OPL. Для Ultima VII взяты записи Exult с настоящего Roland MT-32, того синтезатора, под который музыку и писали. Каждый трек превращается в WAV 24 кГц моно с общей громкостью и проигрывается через кольцевой буфер в PSRAM: основной цикл его пополняет, а звуковое прерывание опустошает.

Объёмы получаются немаленькие: 81 МБ для Doom, 69 для Descent, 128 для Tyrian, 95 для System Shock, 208 для Ultima VII. Но SD-карты большие, так что проблемой это не стало.

System Shock потребовал ещё одного шага. Музыка в нём адаптивная: каждая тема состоит из 54 коротких фрагментов или меньше, и музыкальный ИИ запускает их максимум в восьми потоках (основная партия и слои поверх неё), выбирая продолжение по тому, что происходит в игре. Каждый фрагмент отрендерен отдельно (227 файлов), а порт реализует интерфейс музыкального драйвера поверх них: собственный ИИ игры по-прежнему выбирает фрагменты, порт проигрывает до восьми из них одновременно и смешивает.

Doom: процедуры отрисовки

Основная работа Doom идёт в двух циклах: R_DrawColumn рисует стены и спрайты по вертикальным столбцам, а R_DrawSpan — полы и потолки по горизонтальным полосам. Вместе они занимают 40% кадра.

Изменение (процессор тогда работал на 27 МГц)

2000 кадров

fps

Исходная версия

181,0 с

11,0

Процедуры отрисовки столбцов и полос: локальные переменные, 4 пикселя за итерацию

157,2 с

12,7

Переключение страниц в прерывании обратного хода кадра

144,5 с

13,8

Отключение проверок диапазонов

141,3 с

14,2

Отрисовка прямо в буферы отображения

138,0 с

14,5

Дальше эстафету приняло железо: процессор перешёл на синхронную работу на 36 МГц, у кеша данных появился второй путь (двухпутная ассоциативность), чтение с SD ускорилось, memset и memcpy получили cbo.zero, появился делитель radix-4. Сейчас timedemo идёт примерно на 22 fps. Пока в кеше был один путь, сдвиг буфера на несколько сотен байт менял скорость Doom на 2%: один столбец экрана затрагивает 200 строк кеша, и за них дралось всё остальное, что было горячим.

Descent: одно деление на шестнадцать пикселей

Descent — настоящая трёхмерная игра с перспективно-корректным наложением текстур, и написанная на C процедура наложения в D1X делит на глубину для каждого пикселя, по 70 тактов и больше на деление. Поначалу наложение текстур занимало две трети времени, а игра шла на 5 fps.

  • Наложение текстур теперь вычисляет точные текстурные координаты раз в 16 пикселей по одной обратной величине, а между ними шагает линейно. Обе координаты упакованы в одно слово, как в процедуре отрисовки полос Doom, четыре пикселя за итерацию.

  • Деления 64 на 32 бита, которые оригинал делал на ассемблере, теперь занимают два аппаратных деления вместо побитового цикла.

  • Кабина — полноэкранный битмап с прозрачным окном под трёхмерный вид, и D1X перерисовывал её со всеми приборами каждый кадр. Теперь её непрозрачные участки рендерятся один раз, перерисовывается она только там, куда может дотянуться трёхмерный вид, а наложение текстур пропускает куски строк, которые кабина всё равно закроет. Кабина и приборы занимали 8% времени, теперь 1%.

  • Опрос клавиш при каждом запросе проверял все 256 клавиш и отнимал 6% времени; теперь проверяются только нажатые.

На первом уровне с полной кабиной выходит около 10 fps.

Tyrian: одна проверка на пиксель

Профиль Tyrian показал, что две трети времени уходят на одну функцию: она рисует три прокручивающихся слоя фона по байту за раз и сверяет каждый пиксель с краями экрана. Теперь для строк, целиком лежащих на экране, проверки пропускаются, и скорость выросла с 17 до 35 fps. Это полная скорость игры, заданная её собственным таймером, и ещё остаётся запас. Музыку Tyrian тоже пришлось рендерить: эмулятор OPL в OpenTyrian считает огибающие в плавающей точке на каждый отсчёт.

System Shock: назад к памяти 1994 года

Shockolate — это портируемая версия исходников для Macintosh, которые Night Dive выпустила в 2018 году. Версия для Mac, а вслед за ней и порт рассчитывали на гораздо больший объём памяти, чем DOS-версия 1994 года: запуск первого уровня требовал 5,3 МБ кучи, а ролик — 17 МБ. В m9b для игры есть 5,1 МБ кучи.

  • Таблица ресурсов содержала запись для каждого из 32 768 возможных идентификаторов, это 1,1 МБ. Идентификаторы System Shock доходят примерно до 5500.

  • Составные ресурсы (варианты текстуры разных размеров, кадры существа) читались целиком, даже когда нужен был один элемент, и этот элемент распаковывался в копию. Теперь читается только индекс, а каждый элемент — при первом обращении.

  • Ролики загружались целиком (14 МБ на заставку), их звук распаковывался ещё в 2,5 МБ, и всё это оставалось в памяти после просмотра. Теперь они читаются из файла по ходу воспроизведения, и используются ролики низкого разрешения из DOS-версии: декодировать их вчетверо меньше.

Теперь первый уровень занимает 2,8 МБ, ролик — около 0,2 МБ. Скорость выросла за счёт двух вещей. Распаковщик текстур читал слова по нечётным адресам, и на него приходилось 97% невыровненных обращений, которые эмулировал обработчик ловушек. А деления с фиксированной точкой шли через программную 64-битную процедуру вместо аппаратного делителя. После этих исправлений и с делителем radix-4 в процессоре сохранённая игра ускорилась с 5,8 до 7,4 fps. Двойным щелчкам понадобилось отдельное исправление: при 7 fps оба щелчка успевали произойти между двумя опросами мыши, поэтому теперь кнопки опрашиваются в звуковом прерывании вместе с временем нажатия.

Ultima VII: куча на диете

Оригинальная Ultima VII — это 16-битный код реального режима на собственном менеджере памяти Origin, и исходников у него нет. Exult заново реализует её движок на C++17 и играет на оригинальных данных. Запуск The Black Gate требовал 6,2 МБ кучи. m9b может выделить ей 5,8 МБ, а распределитель памяти из библиотеки C терял ещё 540 КБ на дырах, пока его не заменил malloc Дага Ли.

  • Usecode — скрипты игр, 1,5 МБ байт-кода — читались целиком при старте. Теперь каждая функция читается, когда до неё доходит вызов, а давно не использовавшиеся выгружаются, чтобы держать в памяти не больше 192 КБ.

  • Спрайты, прочитанные из файла фигур на 7,3 МБ, оставались в памяти навсегда. Теперь сверх 384 КБ выгружаются давно не использовавшиеся.

  • Ландшафт занимал 4 КБ на каждый вид фрагмента карты, а в Тринсике их 120; теперь это 3 байта на тайл. Отрисованные полы кешируются для 24 фрагментов вместо 100.

Через несколько минут игры в Тринсике пик потребления — 4,1 МБ, и ещё остаётся место для музыки MT-32 и звуковых эффектов. Игра идёт на 7–13 fps. На каждый объект Exult делает больше работы, чем движок 1992 года на 386-м, и это следующее, чем стоит заняться.

Оригинальный движок 1992 года тоже работает. Ultima7_Decomp — декомпиляция обеих DOS-игр в переносимый C и C++, а m9b-u7 даёт ей платформенный слой для платы. Игра получает ожидаемую DOS-память в арене на 4 МБ, и правок понадобилось немного: её дальняя куча держит блоки выровненными по слову, так что процессор не выдаёт исключений. Участки спрайтов и тайлы рисуются по слову за раз. При прогулке по Тринсику выходит 8–10 fps при собственном ограничении движка в 10 шагов в секунду. Музыка и звуковые эффекты — те же записи MT-32, что и в порте Exult.

Как тестировались порты

  • Timedemo Doom проигрывает фиксированную демозапись и считает контрольную сумму каждого кадра. Так любое изменение железа или программ можно сравнить по времени прогона и при этом доказать, что картинка та же.

  • Тестовые обвязки на хосте запускают System Shock и Ultima VII с тем же кодом порта на 32-битном x86 Linux, ограничивая кучу объёмом, как на плате, и заполняя новую память мусором, как это было бы на ней. Диеты по памяти делались там, а потом проверялись на плате.

  • Дистанционное управление. Запущенная игра отвечает на запросы скриншотов через последовательный порт и принимает оттуда нажатия клавиш, так что игрой можно управлять и проверять её автоматически, не глядя на экран.

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

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

Исходники и благодарности

Исходники, RTL и порты игр лежат на GitHub: m9b-system объединяет SoC (m9b-core) и порты игр, у каждого из которых свой репозиторий.

m9b опирается на работу многих людей. В README каждого репозитория перечислено, что именно в нём используется, в какой версии и что изменено. Оборудование и прошивка:

  • SpinalHDL/VexiiRiscv (Charles Papon): процессор, собранный из форка с опциями m9b;

  • hdl-util/hdmi (Sameer Puri): кодировщик HDMI со звуком;

  • nand2mario/usb_hid_host (на основе работы hi631): USB-хост;

  • FatFs (ChaN): файловая система FAT на SD-карте;

  • picolibc: библиотека C и скрипт компоновщика программ;

  • Wohlstand/libADLMIDI: эмуляция OPL3, которой на ПК рендерится музыка.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.