Retrolang
Retrolang — это мой собственный SDK для PlayStation 1 и одноименный язык программирования, разработанные с полного нуля без зависимости от стороннего ПО (даже от LLVM и GCC). Комплект включает компилятор, ассемблер, дизассемблер, систему автоматизации сборки, небольшую стандартную библиотеку и набор демонстраций ее использования, в том числе движок 2D-платформера и демку с 3D-графикой. Retrolang — во многом образовательный проект: он является итогом 10 с лишним лет изучения архитектуры консоли и низкоуровневого системного программирования.
Особенностью проекта является стопроцентная юридическая чистота (clean-room дизайн). SDK разработан полностью независимо, без использования дизассемблированного кода, и не включает материалы, защищенные авторским правом. Для использования Retrolang не требуется никакое ПО, кроме того, что доступно свободно и легально. Проект принципиально самодостаточен, из стороннего софта используется только mkpsxiso для создания образов дисков.
Весь инструментарий Retrolang написан на языке D.
https://github.com/gecko0307/retrolang
В собранном виде SDK в данный момент доступен только под Windows.



См. также мою страничку Gecko’s PSX Development Corner с полезной информацией по разработке для консоли + соответствующий википроект.
История
Я начал изучать PlayStation в начале 2010-х. Началось все с того, что я научился компилировать проги при помощи утекшего в Сеть старого официального SDK PsyQ, который использует C89. Именно на нем было создано большинство хитов. С ним работать довольно удобно, несмотря на то, что это древний софт, написанный еще в эпоху Windows 95. Но мне показалось, что как-то нехорошо зависеть от пиратского ПО, пусть и заброшенного. К тому же, PsyQ закрытый, его невозможно модифицировать. Поэтому я взялся писать собственную графическую библиотеку PSXLib. Дело пошло, я довольно глубоко погрузился в детали на уровне ассемблера, но мне ни один существующий ассемблер для консоли не понравился по ряду причин, и я решил сделать свой собственный, который назвал Retrolang, попутно изучив архитектуру MIPS-I и протокол загрузки PlayStation во всех деталях. Впоследствии, в 2026 году, я расширил этот инструмент, превратив его в компилятор, и одновременно адаптировал к нему PSXLib, расширив и улучшив ее (теперь она называется Retrolib). Тогда же я добавил в комплект дополнительные утилиты, необходимые для подготовки ресурсов для PS-игр.
Язык
Retrolang можно охарактеризовать как «ANSI C из параллельной вселенной». Это простой C-подобный процедурный язык, специально спроектированный для программ, работающих в условиях крайне ограниченного объема памяти. Поддерживает базовые целочисленные типы int, short, char, uint, ushort, uchar, void, а также указатели на них. Есть приведение типов, статические массивы, строки, структуры. Реализованы все основные арифметические, логические и побитовые операторы. Операторы потока управления: if/else, while, do/while, for, break, continue, return.
Типизация в языке слабая, даже слабее, чем в C — это сделано намеренно, потому что язык представляет собой весьма тонкую семантическую надстройку над машинной логикой. Двоично совместимые типы неявно конвертируются друг в друга, в том числе и любые указатели, а также указатели и 32-битные целые. Доступ к памяти неограниченный, спецификаторов доступа (const и т. п.) нет, поэтому в языке куча способов, как говорится, выстрелить себе в ногу, но это я проблемой не считаю — Retrolang предназначен для тех, кто знает, что делает.
В Retrolang несколько нестандартная, но простая и эффективная реализация модульности, похожая на unity build. Таблиц символов и раздельных областей видимости нет, программа представляет собой единый бандл с одной глобальной областью видимости. Компилятор собирает все модули за один проход; объектных файлов и инкрементальной сборки нет. Директива #import "file.r" включает модуль в бандл, при этом каждый модуль парсится только один раз.
Пример программы:
#import "retrolib.r"
void main()
{
printf("Hello, World!\n");
}
В отличие от C, Retrolang реализует некоторые низкоуровневые возможности PlayStation в виде интринсиков — встроенных инструкций компилятора, которые разворачиваются не в обычные вызовы, а специальные блоки машинного кода. Это позволяет использовать функции BIOS и команды GTE без необходимости написания частей программы на языке ассемблера. Например, следующий код загружает в контрольные регистры GTE вектор смещения:
ctc2(GTE_TRX, tx);
ctc2(GTE_TRY, ty);
ctc2(GTE_TRZ, tz);
Компилятор может вшивать в бинарник любые файлы при помощи атрибута — это очень полезно, чтобы писать мини-игры без внешних зависимостей:
uchar data[] @("file.bin");
На PSX слишком мало памяти, чтобы использовать динамический аллокатор, как в современных ОС. Программа должна заранее распланировать память, которую она потребляет. Если доверить эту задачу компилятору, то он может все проверять и не допускать коллизий (в то время как ручная работа с диапазонами адресов чревата ошибками). Для этого в Retrolang используются все те же атрибуты:
macro Region = (0x80050000, 2048);
int someData[512] @Region;
Это статически выделит 512*4 байт в регионе MyRegion. Для этого в кодогенераторе предусмотрена специальная таблица регионов и аллокаций. Преимущество такого подхода перед выделением статической памяти в секции данных в том, что бинарник остается маленьким, а это иногда критично. Кроме того, регионы поддерживают Scratchpad:
int someData[64] @SCRATCHPAD;
Выделенную память можно затем использовать в качестве арены для создания в рантайме более мелких объектов.
Есть поддержка AST-макросов — еще одно существенное отличие от C. В Retrolang нет мощного препроцессора (только базовый). Его заменяют макросы, которые позволяют манипулировать синтаксическим деревом. Это похоже на шаблоны, с той разницей, что макросы бестиповые — они параметризуются не типами, а выражениями. Макросы позволяют писать константные выражения и переиспользуемые блоки утверждений. Это очень удобно для инлайнинга кода, чтобы не создавать на каждую мелочь вызов функции.
Например, вместо того, чтобы создавать функцию с командами GTE, можно сделать соответствующий макрос, и команды будут вставлены непосредственно в вызывающий код без оверхеда на движение по стеку:
macro gte_upload_transform(tr)
{
ctc2(GTE_TRX, tr->tx);
ctc2(GTE_TRY, tr->ty);
ctc2(GTE_TRZ, tr->tz);
gte_set_matrix(tr->r);
ctc2(GTE_H, tr->h);
ctc2(GTE_OFX, tr->ofx);
ctc2(GTE_OFY, tr->ofy);
ctc2(GTE_DQA, tr->dqa);
ctc2(GTE_DQB, tr->dqb);
}
Макросы также используются для создания удобочитаемых оберток над функциями BIOS.
Программы на Retrolang тестируются на реальном железе (чипованной PSone) и всех актуальных эмуляторах: PCSX, DuckStation, XEBRA, ePSXe. Компилятор выдает очень компактный PSX.EXE, который можно загрузить с карты памяти через эксплойт FreePSXBoot — это также было протестировано.
Retrobuild
Вместо make-файлов я решил сразу сделать отдельную программу-сборщик Retrobuild, которая использует манифесты проектов в разметке TOML. Retrobuild не только компилирует, но и может сразу создать образ диска, если установлен mkpsxiso, а также генерирует из образа файл CU2.
# Entry point passed to rlc
source = "src/main.r"
# Compiler output, also the file mkpsxiso packs (must match iso.xml)
executable = "iso/PSX.EXE"
# mkpsxiso project description
iso_xml = "iso.xml"
# CUE sheet produced by mkpsxiso (must match iso.xml)
cue = "game.cue"
# Extra arguments forwarded to rlc
rlc_args = ["-i=../../retrolib/"]
[tools]
# Explicit tool paths (highest priority in discovery)
rlc = "../../rlc/rlc.exe"
mkpsxiso = "../../mkpsxiso/mkpsxiso.exe"
[hooks]
# Optional commands run before/after stages; each is an argv array
# pre_compile = []
# post_iso = []
Библиотека
Проект включает Retrolib, системную библиотеку для работы с железом и BIOS. Она позволяет рендерить 2D и 3D-графику, загружать в видеопамять изображения TIM, воспроизводить звуки VAG, музыку XM и видео STR, работать с геймпадом, CD-приводом и картами памяти. Retrolib можно использовать как целиком (#import "retrolib.r"), так и по частям, если вам нужна не вся функциональность.
Основные модули:
core.r— ключевые определения (типы, регистры, регионы памяти)bios.r— макросы для некоторых распространенных BIOS-вызововirq.r— API для работы с прерываниямиdma.r— API для работы с DMApad.r— драйвер геймпадаgpu.r— драйвер GPU и графический APIspu.r— драйвер SPU и звуковой APIcd.r— драйвер CD-приводаxa.r— API для воспроизведения музыки XAsave.r— API для работы с картами памятиmdec.r— драйвер MDECvlc.r— декодер VLCstr.r— API для воспроизведения видео STRiso.r— API для работы с файловой системой диска.
Retrolib также поддерживает работу с отладочным API эмулятора PCSX-Redux (модуль pcsx.r).
Инструментарий
В состав SDK входят следующие программы:
- RLC — компилятор языка Retrolang, по совместительству ассемблер и линкер;
- Retrobuild — система автоматизации сборки;
- PSXDISASM — дизассемблер бинарников MIPS-I;
- PNG2TIM — конвертер изображений PNG в формат TIM;
- WAV2VAG — конвертер звуков WAV в VAG;
- XA Tool — кодировщик/интерливер потоков XA;
- DATWrite — генератор патча системной зоны диска для вставки в образ лицензионной информации. Прилагается аддон-экспортер для Blender, при помощи которого можно создать свою TMD-модель логотипа для загрузочного экрана игры;
- GPSXEmu — эмулятор PlayStation на ранней стадии разработки.