Спецификация Виртуальной Машины Uxn

Документ содержит полное техническое описание архитектуры виртуальной машины Uxn, необходимое для написания собственного эмулятора. Uxn — это 8-битная стековая виртуальная машина с 16-битной адресацией, разработанная Devine Lu Linvega (Hundred Rabbits).


1. Обзор архитектуры


2. Структура памяти

Вся адресуемая память (RAM) имеет размер ровно 0x10000 (65536) байт.

Карта памяти

Диапазон адресовОписание
0x0000 - 0x00FFZero-Page (Нулевая страница). Быстрая память для инструкций LDZ / STZ. Часто используется для хранения глобальных переменных.
0x0100Reset Vector (Вектор сброса). Точка входа в программу. При старте ВМ файл прошивки (ROM) копируется в память начиная с этого адреса, и Program Counter (PC) устанавливается в 0x0100.
0x0100 - 0xFFFFПространство пользователя. Код программы и данные.

Стеки

Машина имеет два стека — Рабочий (Working Stack, WS) и Стек возврата (Return Stack, RS).

Страница устройств (Device Page)

Устройства находятся вне основного адресного пространства в 64 КБ.


3. Кодирование инструкций (Опкоды)

Каждая инструкция занимает ровно 1 байт (вместе с ее режимами) и может сопровождаться немедленными данными (immediate data), если это инструкция LIT или переходы JCI/JMI/JSI.

Структура байта инструкции:

Бит:    7   6   5   4   3   2   1   0
      +---+---+---+---+---+---+---+---+
      | K | R | 2 |       OPCODE      |
      +---+---+---+---+---+---+---+---+

Флаги (Режимы)

Базовые 32 Опкода (Биты 0-4)

Опкоды делятся на логические, арифметические и операции с памятью.
Символом a, b, c обозначены элементы на стеке до вызова. Вершина стека — это самый правый элемент (например, b при a b).

Управление стеком и Логика

КодМнемоникаОписаниеСостояние стека (до -- после)
0x01INCИнкремент. Увеличивает верхнее значение на 1.a -- a+1
0x02POPСнять со стека. Удаляет верхнее значение.a --
0x03NIPУдалить второе значение.a b -- b
0x04SWPПоменять местами два верхних значения.a b -- b a
0x05ROTЦиклический сдвиг трех верхних значений.a b c -- b c a
0x06DUPДублировать верхнее значение.a -- a a
0x07OVRСкопировать второе значение наверх.a b -- a b a
0x08EQUРавно. Пушит 01 если a == b, иначе 00.a b -- c
0x09NEQНе равно.a b -- c
0x0AGTHБольше. Пушит 01 если a > b.a b -- c
0x0BLTHМеньше. Пушит 01 если a < b.a b -- c
0x0CJMPПереход. Изменяет PC на значение a. (Если байт — то относительный сдвиг. Если Short — абсолютный адрес).a --
0x0DJCNУсловный переход. Переход на b, если a != 0.a b --
0x0EJSRВызов подпрограммы. Пушит адрес возврата (PC) на Стек возврата, переходит по адресу a.a --
0x0FSTHПеренос (Stash). Переносит значение a на противоположный стек.a --

Память и Ввод/Вывод

КодМнемоникаОписаниеСтековые аргументы
0x10LDZЧтение из Zero-Page (8-битный адрес).addr8 -- val
0x11STZЗапись в Zero-Page.val addr8 --
0x12LDRОтносительное чтение (от PC + addr8).addr8 -- val
0x13STRОтносительная запись (по PC + addr8).val addr8 --
0x14LDAАбсолютное чтение (по 16-битному адресу).addr16 -- val
0x15STAАбсолютная запись (по 16-битному адресу).val addr16 --
0x16DEIЧтение с устройства.port8 -- val
0x17DEOЗапись в устройство.val port8 --

Арифметика и Битовые операции

КодМнемоникаОписаниеСтековые аргументы
0x18ADDСложение.a b -- a+b
0x19SUBВычитание.a b -- a-b
0x1AMULУмножение.a b -- a*b
0x1BDIVДеление. (Деление на ноль дает 0).a b -- a/b
0x1CANDБитовое И.a b -- a&b
0x1DORAБитовое ИЛИ.a b -- a|b
0x1EEORБитовое исключающее ИЛИ (XOR).a b -- a^b
0x1FSFTСдвиг. Младший полубайт b — влево, старший — вправо.a b -- a >> (b >> 4) << (b & 0x0F)

Примечание к SFT: Значение сдвигается вправо на старший ниббл b, а затем результат сдвигается влево на младший ниббл b.


4. Особенный Опкод 0 (Immediate инструкции)

Инструкция с базовым кодом 0x00 особенная. Она реализует инструкции чтения аргументов прямо из памяти (то, что идет сразу за PC), в зависимости от активных флагов.

БайтМнемоникаОписание
0x00BRKBreak. Останавливает выполнение текущего вектора / программы.
0x20JCIУсловный относительный переход (Short). Снимает 1 байт со стека; если не 0, прибавляет следующий short из памяти к PC. Иначе перескакивает этот short (PC = PC + 2).
0x40JMIБезусловный относительный переход. Безусловно прибавляет следующий short из памяти к PC.
0x60JSIВызов с относительным переходом. Кладет адрес возврата (PC + 2) на Стек возврата, затем делает JMI.
0x80LITLiteral. Читает 1 байт из памяти за PC и кладет на стек. PC инкрементируется на 1.
0xA0LIT2Literal (Short). Читает 16-битный short из памяти за PC и кладет на стек. PC инкрементируется на 2.
0xC0LITrLiteral Return. Читает 1 байт и кладет на Стек возврата.
0xE0LIT2rLiteral Return (Short). Читает short и кладет на Стек возврата.

5. Детали реализации и нюансы для разработчика

Исполнительный цикл (Execution Loop)

Эмулятор должен выполнять следующий цикл, пока не встретит BRK:

  1. Прочитать байт памяти по адресу PC (Это опкод).
  2. Инкрементировать PC.
  3. Декодировать режимы Keep, Short, Return.
  4. Выполнить логику опкода.
  5. Обновить указатели стека в зависимости от количества извлеченных/добавленных элементов. При Keep-режиме указатель стека после извлечения аргументов возвращается на место, а затем к нему добавляется результат.

Адресация 16-битных значений (Short Mode)

При включенном режиме 2 (Short Mode), инструкция забирает из стека по 2 байта на каждый аргумент (сначала H, затем L) и кладет 16-битные значения.
Например, при операции ADD2 (0x38):

  1. Читаются L2 и H2 для аргумента b.
  2. Читаются L1 и H1 для аргумента a.
  3. Производится 16-битное сложение.
  4. Вычисляются H_res и L_res и отправляются обратно в стек.

Операции переходов (JMP / JCN)

Устройства (Varvara) - Основы

Uxn как процессор ничего не знает о графике или звуке. Он только читает и пишет байты через DEI / DEO.
Если вы хотите, чтобы ВМ что-то выводила (экосистема Varvara), реализуйте минимум системный порт и консоль:

Эта спецификация полностью покрывает ядро Uxn (CPU + Memory). Реализуя логику в точности по этим правилам, ваша ВМ сможет запускать любые бинарные .rom файлы, скомпилированные стандартным ассемблером Uxntal.