Что это?
Пока разрабатывается OpenComputers II, у меня уже появилась идея для OpenComputers III.
Основывается она на далеко не новом принципе: сделать компьютер, в котором все вычисления происходят в памяти/регистрах, а единственная возможная команда - MOV (скопировать значение из одного регистра в другой). Но я не встречал никаких виртуальных машин для такой архитектуры, поэтому решил написать свою.
Для чего?
Пока что - исключительно ради фана. Потом - может быть, получится развести схему для такого процессора и начать производить его, но это точно не близко.
Где потыкать?
Текущие реализации (2 штуки, не во всех случаях работают одинаково):
https://github.com/ProgramCrafter/python-mov-vm - на Python
https://github.com/ProgramCrafter/rust-mov-vm - на Rust
У меня была и реализация на Lua, но её я куда-то потерял.
Сейчас думаю написать на Scala, как дополнительную архитектуру к OpenComputers.
Что умеет?
Пока что - печатать текст в консоль и считывать там же нажатия клавиш.
Вообще, насколько я посчитал, любая инструкция x86 представляется в виде не более чем 8 MOV-инструкций, так что эта архитектура не должна быть существенно медленнее. Кроме того, каждая инструкция здесь 32-битная, поэтому предсказатель ветвлений и конвейер инструкций (в физическом варианте процессора) будут быстрее работать.
Как выглядит какая-нибудь программа?
Выводит
╔══════════════════════════════════════════════════════════╗
║ TigerOS v0.0.1 | not licensed!!! ║
╚══════════════════════════════════════════════════════════╝
Выход по нажатию пробела в консоли.
Байткод: \x80<\x00\x1c\x80\n\x00\x1b\x80\x01\x00\x04\x00\x03\x00\x14\x00\x1d\x00\x15\x80\x07\x00\x16\x00\x17\x00\x1b\x00\x1e\x00\x10\x00\x05\x00\x03\x80\x03\x00\x1b\x00\x10\x00\x03\x80 \x00\x04\x00\x05\x00\x14\x80N\x00\x15\x80\x10\x00\x16\x00\x17\x00\x1b\xa5T\x00\x10\x00\x1c\x00\x03\x80\x02\x00\x04\x00\x05\x00\x03\xa5P\x00\x1e\x80\x17\x00\x1d\x80\x02\x00\x1b\xa5W\x00\x10\x80\n\x00\x10\xa5Q\x00\x10\x80 \x00\x10\x80 \x00\x10\x80T\x00\x10\x80i\x00\x10\x80g\x00\x10\x80e\x00\x10\x80r\x00\x10\x80O\x00\x10\x80S\x00\x10\x80 \x00\x10\x80v\x00\x10\x800\x00\x10\x80.\x00\x10\x800\x00\x10\x80.\x00\x10\x801\x00\x10\x80 \x00\x10\x80|\x00\x10\x80 \x00\x10\x80n\x00\x10\x80o\x00\x10\x80t\x00\x10\x80 \x00\x10\x80l\x00\x10\x80i\x00\x10\x80c\x00\x10\x80e\x00\x10\x80n\x00\x10\x80s\x00\x10\x80e\x00\x10\x80d\x00\x10\x80!\x00\x10\x80!\x00\x10\x80!\x00\x10\x00\x1c\x00\x03\x80$\x00\x04\x00\x05\x00\x03\x80 \x00\x1e\x80B\x00\x1d\x80\x02\x00\x1b\xa5Q\x00\x10\x80\n\x00\x10\xa5Z\x00\x10\x00\x1c\x00\x03\x80\x02\x00\x04\x00\x05\x00\x03\xa5P\x00\x1e\x80K\x00\x1d\x80\x02\x00\x1b\xa5]\x00\x10\x81\x00\x00\x10\x80\n\x00\x1b\x81\x01\x00\x10
Ассемблер (программа для перевода в байткод лежит вместе со всем на Python):
Структура команд
Каждая команда является 32-битной: первые 16 бит описывают источник, следующие 16 - номер регистра назначения, куда надо записать значение источника.
Если первый (старший) бит из 16, задающих источник, равен единице, то оставшиеся 15 бит - это число, которое будет записано в регистр назначения.
Иначе эти 15 бит задают номер регистра-источника, из которого будет считано значение.
Набор регистров
Каждый регистр хранит 64-битное целое число. Всего регистров может быть не более 32768 из-за адресации.
Номера регистров:
Значение доступных на данный момент регистров:
Пока виртуальные машины несовместимы друг с другом только при переполнении чисел в регистрах. Rust упадёт с ошибкой, Python начнёт спокойно использовать длинную арифметику, а Lua начнёт спокойно терять точность, используя double.