Авторство. Оригинальный компилятор c8080 — его архитектура, расширения языка C (
__global,__stack,__link,__address, соглашение о вызовах__a_N_<func>), подъязык CMM, форматы магнитофонных файлов RK86 и входящий в проект справочникMANUAL.md— © Алексей Фёдорович Морозов (Aleksey F. Morozov). Лицензии: GPL-3.0 на компилятор и Apache-2.0 на стандартную библиотеку. Источник: https://github.com/alexey-f-morozov/c8080.Этот репозиторий — независимый порт компилятора на TypeScript. Он воспроизводит язык и ABI; все решения, определяющие поведение, приходят из оригинального проекта. Порт наследует GPL-3.0.
Компилятор C для Intel 8080 на TypeScript. Порт c8080 — компилятора Алексея Морозова на C++ для платформ на базе i8080 / КР580ВМ80А. Работает на Node 18+ и Bun.
Русскоязычное руководство из оригинального проекта включено в виде
файла MANUAL.md: §5 описывает соглашения __global /
__stack, §11 — форматы выходного файла (rks, rk, rkr, pki,
gam), §14 — структуру магнитофонного файла RK86, которую собирают
наши обёртки.
npm install -g c8080 # ставит команду `c8080`
# либо разово, без установки:
npx c8080 file.cc8080 [-V] [-Ocpm|-Orks] [-I<dir>] [-D<name>] [-o<bin>] file.c-Ocpm (по умолчанию) — .bin для CP/M TPA (ORG 0x0100).
-Orks — магнитофонный файл Specialist / Radio-86RK.
Полный список форматов: c8080 без аргументов выводит справку.
bun install
bun test # 164 теста (сквозная компиляция + симуляция)
bun run typecheck # tsc --noEmit
bun run build # сборка бандла → dist/c8080.js (для npm publish)
bunx c8080 file.c # (после `bun run build`)Полный конвейер: C source → препроцессор → парсер → подгрузка по
запросу (__link) → кодогенерация → ассемблер
asm8080 → бинарник.
Скомпилированные программы действительно исполняются на встроенном
симуляторе 8080, который перехватывает вызовы CP/M BDOS по адресу
0x0005 и обрабатывает ввод-вывод.
#include <stdio.h>
int main(void) { puts("Hello from real c8080 stdlib!"); return 0; }$ npx c8080 -I/path/to/c8080/include demo.c
DoneКомпилятор идёт по ссылке __link("stdio_h/puts.c") из stdio.h,
парсит указанный файл и добавляет определение puts в программу.
Подгрузка по запросу означает, что printf и прочие функции
попадают в сборку только если действительно вызваны.
| Возможность | Примечания |
|---|---|
| Типы | char, short, int, long, long long, signed/unsigned, указатели, массивы, struct, enum, typedef |
| Операторы | блоки, if/else, while, do-while, for, break, continue, return, goto, switch/case/default, asm { … } с сохранением построчной структуры |
| Выражения | полная иерархия приоритетов C, присваивание и составное (+= и т. д.), тернарный ?:, унарные -!~*&++-- (pre/post), [] . ->, приведение типов, sizeof, оператор запятая, строковые и символьные литералы |
| Арифметика | + - * / % << >> & | ^ (умножение/деление/сдвиги через рантаймы; арифметика указателей масштабируется по размеру элемента) |
| Сравнения | знаковые 16-битные == != < <= > >= |
| Управление потоком | условные переходы с уникальными метками; switch — линейный диспетчер сравнений |
| Структуры | поля с байтовыми смещениями, вложенные структуры, чтение/запись по . и -> для byte/word-полей и массивов/структур (поля-массивы и поля-структуры «декадируют» в адрес) |
| Инициализаторы структур | списковая инициализация по полям, включая char[] внутри структуры ({ { "ab", 10 }, ... }); недостающие поля обнуляются |
| Ввод-вывод | встроенные putchar / puts через BDOS; пользовательские определения имеют приоритет. Строковые литералы интернированы в бинарник. Мини-printf / sprintf (%d, %s, %c, %%) автоматически линкуются при первом вызове; они делят общий слой маршрутизации вывода, поэтому можно вызывать любой из двух независимо |
| Препроцессор (полный) | #include в обеих формах, #define (объектные и функциоподобные макросы, вариативные ...), #undef, #if/#ifdef/#ifndef/#else/#endif с вычислителем целочисленных выражений C, defined(X), __has_include(...), #pragma once, #error, CLI-флаг -D |
__link("file.c") |
подгрузка по запросу: парсятся только те файлы, чьи функции достижимы из графа вызовов. Ошибки разбора в недостижимых звеньях некритичны |
__stack |
соглашение вызова с поддержкой рекурсии: вызывающая сторона сохраняет слоты __a_* / __l_* вызываемой функции на CPU-стеке перед CALL, заполняет новые аргументы, вызывает, после возврата восстанавливает. Работает и для взаимной рекурсии. Пока только для параметров и локалов размером слово |
Вариативные (...) |
на стороне вызова лишние аргументы складываются в буфер __va_args[]; используется мини-printf. Объявленные параметры по-прежнему передаются по соглашению __a_N_<func> |
| Глобальные инициализаторы | скаляры, строки (char[] → DB, char* → DW на интернированную копию), списковая инициализация массивов (как DW с заполнением) |
| Экранирования в литералах | \n \r \t \0 \\ \' \" \a \b \f \v \xNN \nnn (восьмеричное) — в char- и string-литералах |
| Рантайм-помощники | __o_mul_u16, __o_div_u16, __o_shl_u16, __o_shr_u16, putchar, puts — включаются в бинарник, только если на них есть ссылка |
Каждый тест в test/codegen/endtoend.test.ts компилирует фрагмент C, ассемблирует через asm8080 и исполняет на встроенном симуляторе 8080 — авторитетный список того, что сегодня работает.
- 8-битный путь по регистрам. Операции над
charидут через 16-битный HL, тогда как путь через регистр A был бы короче по инструкциям. - Краевые случаи знаковой арифметики. Сравнения используют знак
результата
SBB, поэтому у границ int16 возможны случаи переполнения, которые игнорируются. Рантаймы умножения/деления/сдвигов — беззнаковые. - Интеграция рантайма CP/M.
__initи зануление bss изinclude/c8080/internal.cне подключены. Программы работают, потому что CP/M сам выставляет SP и мы не полагаемся на нулевой bss. - Stdlib c8080 со вставками на sjasmplus. Большинство файлов
include/string_h/*.c— это ассемблер sjasmplus внутри блоковasm { }. sjasmplus и asm8080 расходятся по синтаксису (label = valueпротивlabel: EQU value), поэтому такие файлы не ассемблируются. Чистые C-файлы stdlib (puts.cи т. д.) работают. - Передача и возврат структур по значению. Ни то ни другое пока
не поддерживается; передавайте указатели. Обычное присваивание
a = b(включаяg = local,s = *p) копирует структуру как надо.
src/
frontend/
tokenizer.ts порт ctokenizer.cpp
preprocessor.ts #include / #define / #if
fs.ts FileSystem-интерфейс + MemoryFileSystem
node-fs.ts NodeFileSystem (вынесена, чтобы браузер-бандл
не тянул node:fs)
lex.ts поток токенов с произвольным lookahead
ast.ts CNode / CType / CVariable / CFunction
parser.ts рекурсивный парсер C + захват `__link`
symbols.ts таблица символов со скоупами
codegen/
i8080/compile.ts AST → ассемблер 8080 в Intel-синтаксисе
runtime/
printf.ts встроенный C-исходник мини-printf/sprintf
link.ts автоматическая линковка встроенных runtime-исходников
formats/
rks.ts Specialist tape envelope
wrap.ts диспетчер форматов (Specialist + asm8080's RK86)
util/dump.ts человекочитаемый дамп AST (флаг -V)
bin/c8080.ts CLI: препроцессор + парсер + обход `__link` + кодоген + asm8080
docs/ браузерный playground (index.html + playground.ts → .js)
test/
codegen/sim8080.ts минимальный симулятор 8080 + хуки BDOS
codegen/endtoend.test.ts сквозные C-программы: компиляция + запуск + проверка вывода
Препроцессор справляется с 37 из 41 реального исходника c8080 (4 сбоя
все контекстные — не хватает архитектурно-специфичных заголовков или
флага __CMM, а не баги парсера). Парсер также доходит до всех 41.
GPLv3 (унаследовано от c8080).