Процессор J1 — куда уж проще

Осторожно — эта статья не содержит и теоретической, ни практической пользы, и была написана просто, как подведение черты под одним из увлечений.

Я давний фанат этого процессора. Я писал о нем на хабре, дважды, и хочется написать еще.

Для тех, кто не слышал никогда о J1 — это простенький процессор,
ориентированный на язык Форт. Для любителей подробностей — официальный сайт J1 CPU.

Так вот, я с гордостью могу сказать, что я маялся бездельем и тем временем написал эмулятор + компилятор почти-Форта для J1. Здесь я расскажу что это и зачем.

J1

Особенности:

  • 16-разрядный
  • может адресовать до 2^15 байт памяти
  • стековый, регистров нет совсем (ну разве только счетчик инструкций)
  • вместо регистров есть целых два стека — для данных и для вызовов (чтобы было понятно куда прыгать после «ret»)
  • memory mapped I/O
  • инструкция «ret» не занимает место в коде — это просто флаг в последней
    инструкции подпрограммы.
  • все команды процессора — по 16 бит, очень удобно дизассемблировать
  • очень непросто получать доступ к элементам стека (ну кроме первого и второго).
  • ячейки памяти тоже считаются двухбайтными

J1 Forth compiler (j1c)

Ассемблера у J1 как такового нет. Вместо него — почти Форт. Эти языки, оказывается, очень похожи.

Фактически, помимо 4 простых инструкций (lit, jmp, jz, call) в ассемблере нужно определить мнемоники для ALU-инструкций. Вот некоторые из них (они соответствуют форт-словам). Состо

яние стека описывается в виде «( до — после )». Так принято в Форте.

  • dup ( a — a a ) — дублирует элемент на вершине стека
  • over ( a b — a b a ) — «вытаскивает» второй элемент наверх
  • swap ( a b — b a ) — меняет местами 1й и 2й элементы
  • nip ( a b — b ) — отбрасывает 2й элемент
  • drop ( a b — a ) — отбрасывает 1й элемент
  • ; — выход их подпрограммы (return)
  • @ ( a — [a] ) — получить значение по адресу a
  • ! ( a b — ) — записать a по адресу b

Их на самом деле много, и прелесть в том, что можно на их основе создавать свои. Но это уже дело компилятора.

Разработчик J1 CPU написал кросс-компилятор на форте для своего процессора. Но у меня он так и не заработал. Да и тянуть монстроидальный полноценный форт ради игрушечного процессора не хочется. Поэтому я написал свой велосипед. Вот его особенности:

  • пока поддерживается 35 основных ALU инструкций
  • возможность создавать свои составные инструкции на их основе
  • поддержка конструкций «if .. else .. then» (да, именно в таком порядке, как в форте)
  • поддержка циклов «begin/again/until»
  • поддержка констант/переменных/массивов
  • поддержка тестов

Тесты для форта выглядят очень красиво:

\ Аналог assertEquals(2+3, 5)
{ 2 3 + -> 5 }

В отличие от полноценного форта, мой недо-форт не позволяет интерпретировать «на лету» код форта, а только компилировать его. Зато компилятор вышел очень простой и компактный.
Вообще написание Forth-машин — отличная игрушка для начинающих программировать и не только.

J1 virtual machine

Я написал две виртуальных машины, эмулирующих J1. Одна из них является частью j1tools и написана на Go, а вторая — входит в состав nikl (ущербного микрофреймворка для embedded устройств) и позволяет запускать J1 код, скажем, на восьмибитных AVR. При этом весить эта виртуальная машина будет чуть больше килобайта.

По моему замыслу применяться эта штука должна так. На AVR реализуется только доступ к портам (GPIO, UART, SPI, …) через виртуальную машину (через memory-mapped I/O). Теперь, скажем, GPIO порт соответствует адресу 0x1234 VM. Тогда дальнейшее мигание светодиодиком можно сделать уже на форте:

equ GPIO #h 1234
begin 
    0 GPIO ! \ turn off
    5 sleep  \ sleep for five sec
    1 GPIO ! \ turn on
again

Скомпилированный код виртуальной машины можно хранить во внешней памяти (EEPROM, SD-карты), а значит обновлять без перепрошивки самого AVR. Тестировать, соответственно такой код можно прямо на ПК, не имея вообще AVR. Это удобно, если, скажем, между изготовлением платы устройства и его выходом в продакшн мало времени, и писать код + тестить на реальном устройстве можно не успеть. Тогда код можно написать заранее, тестить на VM, а потом запустить на устройстве эту самую VM. Недостаток очевиден — сильно пострадает скорость. Ну и еще тот факт, что Форт — не самый приятный язык для разработки.

J1 basic

Ну а это своего рода бонус. Я решил поупражняться в написании компиляторов и написал tinybasic для J1. Как и любой порядочный tiny basic, он имеет всего 26 переменных (a..z), и примитивный синтаксис:

LET [var] = [expression]
IF [condition] THEN [statement]
GOTO [addr]
GOSUB [addr]
RET

Все. Аж самому неловко 🙂

Итоги

Вообще мне нравится J1 своим минимализмом. Это хорошая практика написания эмуляторов (почти как Chip-8). Хотя, конечно, стековая архитектура не располагает к написанию компиляторов «нормальных» языков, типа C. Хотя, Паскаль (точнее PL/0) был написан вначале под стековую машину.

Исходники j1tools находятся на bitbucket.
Про виртуальную машину из nikl узнать можно здесь.

Конечно, хочется еще добавить своего рода дизассемблер, написать компилятор чего-нибудь получше бейсика, расширить библиотеку слов (составных инструкций), сделать удобным расширение виртуальной машины и многое другое.

Но даже тем что есть уже можно играться и думать о том, как интересно было жить раньше, когда процессоры были 16-битными, памяти было мало, ассемблер был простым, а низкоуровневое программирование не считалось уделом фриков. Эх…

Реклама

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s