Completing a BASIC language interpreter in 2025
Разработка BASIC-интерпретатора в 2025 году: реализация строковых переменных и сборка мусора
Проект по созданию BASIC-интерпретатора для платформы Intellivision ECS 1983 года выпуска перешёл на новую стадию — добавление поддержки строковых переменных. Изначально система могла работать лишь с числовыми данными, но теперь добавлена работа со строками (A$, B$, C$), включая операции присваивания, ввода и вывода, а также конкатенацию.
Особенность реализации — использование двух отдельных стеков: один для хранения строковых переменных, другой для временных строк в процессе вычислений. Это позволило избежать излишнего усложнения управления памятью. Для обработки строк введён сборщик мусора, который, не увеличивая нагрузку на систему, эффективно управляет памятью, используя маркер 0xCAFE для обозначения свободных участков.
Реализация включает функции для работы со строками, такие как конкатенация, функции LEFT$, RIGHT$, MID$ и другие. Всё это работает на процессоре CP1610 с тактовой частотой 894 кГц, демонстрируя, что даже на ограниченных системах возможна эффективная работа со строками.
Код написан на ассемблере, но логика применима в высокоуровневых языках. Это пример того, как даже в средах с ограниченными ресурсами можно успешно реализовать сложные функции, используя продуманные алгоритмы и эффективные структуры данных.
Комментарии (13)
- В 1978 году Хэл Финни написал 2-килобайтный интерпретатор BASIC для Intellivision, который стал первым встроенным языком программирования для игровой системы.
- Участники обсуждали, что в те годы размер кода и экономия памяти были критически важны, и как это влияло на дизайн и сообщения об ошибках.
- Обсуждение затронуло вопросы раннего периода персональных компьютеров, включая такие редкие темы, как Oregon Trail и TRS-80.
- Ностальгия по тем временам, когда даже простейшие вещи, такие как строки ошибок, были предметом гордости разработчиков, и как это сравнивалось с современными стандартами.
How is Ultrassembler so fast?
Ultrassembler — библиотека RISC-V-ассемблера, встроенная в проект Chata.
В отличие от as и llvm-mc, она вызывается прямо из C++, без system() и временных файлов, что критично для встроенных систем.
Скорость
Тест на 16 тыс. инструкций:
- Ultrassembler ≈ 10× быстрее
as, 20× быстрееllvm-mc. - 1 RISC-V инструкция ≈ 1000 x86-инструкций (у конкурентов 10–20 тыс.).
Код на чистом C++; можно добавить ассемблерные вставки.
Ключевые оптимизации
Исключения
GCC-реализация «zero-overhead»: штрафа нет, пока исключений нет.
Ошибки встречаются редко и видны человеку, поэтому даже 1 с на обработку незаметна.
std::expected дал −10 %, так как нормальный путь стал дороже.
Быстрые структуры
2000+ RISC-V-инструкций требуют мгновенного поиска.
Вместо std::unordered_map используется perfect-hash таблица от gperf, генерирующая O(1) без коллизий.
Размер таблицы компактен, кэш-эффективен.
Парсинг
- Регистры идентифицируются по первым 2–3 символам через
switch. - Нет
std::string, толькоstd::string_viewи статические буферы. - Лексемы разбираются за один проход без регулярных выражений.
Кодогенерация
- Шаблоны на этапе компиляции формируют битовые маски инструкций.
- Варианты одной инструкции разворачиваются в
constexpr-таблицы, что убирает ветвления в рантайме.
Память
- Все выделения через стековые
std::array/std::string_view. - Нет
new/malloc, следовательно, нет аллокационных штрафов и кэш-промахов.
Платформенные трюки
[[likely]]/[[unlikely]]для подсказок ветвления.__builtin_expectтам, где компилятор не догадывается.- LTO + PGO дают ещё 5–7 %.
Итог
Ultrassembler показывает, что «низкоуровневый» C++ без искусственных ограничений может обгонять даже оптимизированные GNU-утилиты.
Комментарии (34)
- В обсуждении разобрали миф о «системном вызове при каждом росте контейнера» — реальные аллокаторы переиспользуют память и делают syscall лишь при нехватке.
- Участники напомнили, что исключения в C++ не «zero-overhead»; есть компромисс между временем и памятью, и g++ выбирает экономию места.
- Автор статьи подтвердил: пробовал хеширование, но дерево разбора оказалось быстрее; flex/bison тут не при чём, скорее gperf.
- Некоторые посоветовали LLVM C++ API, memory-mapped I/O и std::pmr для ускорения и упрощения кода.
- Большинство сходится: современные ассемблеры и так быстрые, задача скорее академическая, но как «посмотреть, насколько можно ускорить» — интересна.