Pull to refresh

Comments 21

С тем же успехом всю vm можно бы было уместить в 1, 10 или 42 строки. Всего лишь нужно ещё большее число действий запихивать в нечитаемые однострочники

  1. Чтобы джампнуть по адресу n нужно выполнить jmp n-1. Это бай дизайн?

  2. Почему не реализоыана rti, она же элементарна

  3. Странная система комманд с add dst,src,0 вместо mov dst,src

  1. Для vliw-подобных инструкций с фиксированной длиной проще работать 3-операндными операциями с данными. Кстати, регистр флага состояний становится не нужен, т.к. условные переходы тоже могут прямо в себе содержать вычисления на alu. В "Turing complete" это упрощение используется в LEG-архитектуре

Вообще я бы многие уровни Turing Complete ввел в обязательную подготовку программистов. И суть не в понимании низкоуровневщены, а то что игра учит очень сильно развивать мышление нужное для решения проблем и задач. Открыл ее для себя 2 недели назад и прошел на одном дыхании. Уже 7 джунов(и даже одного биолога) усадил за нее и прямо вижу пробелы в мышлении и как это дело фиксится по мере прохождении игры

Странная система комманд с add dst,src,0 вместо mov dst,src

Что тут странного? Так делают в RISC процессорах.

Например в IBM Power

"mr rA,rS" equivalent to "or rA,rS,rS"

"li rD,value" equivalent to "addi rD,0,value"

AN2491 (nxp.com)

Ведь зачем нужен лишний опкод, если он не нужен?

Не обязательно искать экзотику, в ARM то же самое: MOV — алиас для ORR, CMP — для SUBS, и так далее.
А что такого экзотического в Power/PowerPC?
Я конкретно про сложение с нулём писал (там складывается с нулевым регистром).
CMP — для SUBS

Это в ARMv8+. В предыдущих — нет (из-за отсутствия ZR).
А что такого экзотического в Power/PowerPC?

То, что ARM у каждого в кармане, а Power очень мало кто видел ирл?

"эх, молодость...."

Очень похожую VM мы на курсаче на первом курсе делали примерно 25 лет назад. Только не на С, а на Паскале.

Система команд была немного побогаче в плане арифметических и логических операций, плюс надо было визуальный отладчик сделать и транслятор с ассемблера в "машинный код".

Такое интересно было делать в своё время. Остальные курсачи в основном более унылые были, насколько помню...

Люблю такие издевательства. Прямо воспоминания из лучших времен.

uint16_t mem[UINT16_MAX] = {0};

тут потенциальное UB, потому что из кода VM можно адресоваться к ячейке за пределами массива.
op_ex[OP(instr)](instr); // эта инструкция выполнит операцию, связанную с OP(instr) 

static inline void add(uint16_t i)  { /* код */ }
static inline void and(uint16_t i)  { /* код */ }


Для функций, вызываемых по адресу из таблицы, модификатор inline указывать бессмысленно. Мелочь, ни на что не влияет, но показывает уровень, насколько автор понимает происходящее.

UINT16_MAX=65535. Поэтому, если говорить о перспективе, то наша система довольно ограничена и не сможет выполнять/загружать программы с более, чем 65535 инструкциями.

С оценкой уровня автора согласен. Где-то с четвертого-пятого абзаца всего перевода начало создаваться впечатление, что проект является студенческой курсовой работой.

Тем не менее, как введение для новичков походит, если не брать все утверждения в статье за непогрешимую истину.

Правильное описание массива
uint16_t mem[UINT16_MAX+1];
И хранить состояние машины в глобальных переменных — плохо. А что если завтра понадобится «4-ядерный» процессор?

Про это, кажется, не думает ни один из крупных нынче языков с VM( python, js .. ). Везде одни и те же грабли глобального состояния. Причины и проявления разные, но результат один и тот же - однопоточность к VM прибита гвоздями. Кто-то даже проявляет ПТСР и доказывает, что так и надо и что так даже правильно - ведь можно просто запустить 10 копий VM.

Кажется в коде для инструкции br ошибка. Проверка выполнения условия записана как "reg[RCND] & FCND(i)". Может хотя бы так: "(reg[RCND] & FCND(i)) == FCND(i)"?

А так, для новичков вроде неплохо. За исключением названий переменных, дефайнов и прочего (кроме названий инструкций, конечно - тут классика).

Я думал, что это фича, типа, если в reg[RCND]  установлены флаги < и >, то инструкция будет переходить, если !=

trap, не содержащая проверку на соответствие границам массива, позволяет получить много интересного Undefined Behaviour. Вместо fprintf(stdout, "%c", ...) проще использовать putc. Вывод строки циклом fprintf --- тоже отдельная песня.

"Программу для генерации программы" можно было бы превратить в аналог ассемблера, добавив #define на каждый опкод и используя их внутри массива. Да еще в вызове fwrite ошибка длины.

Касательно самой архитектуры тоже есть вопросы. Хотелось бы аппаратные push/pull. И регистр флагов обновлять можно не всегда, а только по требованию (как в Aarch64).

А вообще было бы интересно увидеть, например, порт RatC ("A Book on C", R.E. Berry and B.A. Meekings, 1984), генерирующий код для этой VM.

Sign up to leave a comment.