Сборка компьютера sap-1 (компьютер Бена Итера) в игре Turing Complete

Здравствуйте меня зовут Дмитрий. Сегодня я я хотел-бы рассказать вам про очень интересную игру Turing Complete. Я назвал бы её симулятор разработчика компьютеров. А также мы прямо в этой игре соберем компьютер SAP-1 более известный как компьютер Бена Итера.
Сразу скажу что игра мне очень понравилась. Ну вот знаете иногда про игру все говорят какая крутая игра, а ты в неё поиграешь и думаешь. Ну игра ничего особенного. А иногда встречаются игры про которые никто не говорит особо, но стоит в неё начать играть, как обнаруживаешь что ты просто не можешь от неё оторваться. Как раз эта игра относится ко второму типу. У меня такое с было Factorio я в неё тоже долго не мог перестать играть. Так что возможно я буду чуть чуть предвзятым.
Обзор
Итак про что-же эта игра. В этой игре вам придётся разработать свой компьютер, пройдя при этом все стадии его разработки (с игровыми условностями конечно). Начиная от создания элементов этого компьютера. Тут вы сможете немного освежить школьную программу по физике и вспомнить что такое Дешифратор, Мультиплексор, Регистр, Сумматор и т.д. Заканчивая архитектурой компьютера которую вы также будете разрабатывать, естественно с подсказками от разработчиков. Небольшой спойлер вы разработаете два компьютера сначала простеньки «разминочный», а потом более сложный.
Но и это ещё не все. После того как вы создадите компьютер, вам нужно будет доказать его работоспособность написав для этого компьютера программы, для решения задач поставленных перед вами разработчиками.
Например одной из таких задач является. Обыграть робота в игру которую все знают по шоу «Форт Баярд» помните там игрок и «мастер игры» поднимают по одной две или три палочки. Проигрывает тот кто поднял последнюю. Я потратил минут десять и несчитанное число попыток, только для того чтобы понять как вообще в неё выиграть. Так что теперь я понимаю что обыграть мастера игры было очень очень непросто.
Все мы помним тот триумф который мы испытали когда написали свою первую программу. А теперь представьте какой триумф вы испытаете когда напишите первую программу для своего компьютера который вы только что сделали.
Проблемы
Ну, а теперь пробежимся по проблемам которые я заметил во время игры.
Во первых я долгое время не мог понять как вращать элементы, делается это пробелом, но нам этого не говорят, также в игре есть рамка для выделения объектов, но чтобы она появилась нужно зажать Shift. Ну и когда вам скажут что вы молодец и теперь нужно создать новую архитектуру компьютера, нужно не просто создать её, а ещё войти в неё двойным щелчком. Я если честно пару минут не мог понять что делать после того как создал новую архитектуру.
Также когда вам разрешат создавать новые элементы. Эти элементы должны удовлетворять некоторым требованиям. Вот видите эти квадратики на заднем фоне.

Я с начало думал что это просто монотонная текстура для заполнения заднего фона. Но в каждом таком квадрате должен быть только один вход или выход, если будет два то будет ошибка. Кроме того в каждом кастомном элементе, должен быть хотя-бы один компонент который не является входом или выходом. То есть например поставить вход и выход и объединить их между собой нельзя
Главным недостатком игры является конечно-же, то что она находится в раннем доступе и компания заканчивается на полуслове. Но к счастью в игре есть режим песочница в котором можно создавать свои компьютеры, никак не связанные с компанией.
Компьютер SAP-1
Итак компьютер SAP-1 расшифровывается Simple As Possible (Просто как только возможно) впервые он был описан в книге «Digital Computer Electronics» авторами Albert Paul Malvino и Jerald A. Brown. Но большинству людей он известен благодаря известному (в узких кругах) ютуберу Бену Итеру (Ben Eater). Бен в цикле видеороликов собрал этот компьютер из элементов дискретной логике. Кстати рекомендую посмотреть ссылка в конце поста.

Как можно видеть SAP-1 это восьмибитный компьютер с четырехбитным адресным пространством, то есть объем памяти всего 16 байт. У компьютера всего два регистра, основной и вспомогательный. Кстати такая архитектура, когда у нас имеется регистр аккумулятор и все команды так или иначе взаимодействуют с этим единственным регистром была популярна в начале 80-х. Это позволяло сэкономить память поскольку все операнды в качестве одного аргумента всегда использовали аккумулятор и результат работы сохранялся тоже в него.
Память этого компьютера составляет 16 байт. И здесь возникли ограничения игры, дело в том что в блок программы нельзя сохранять данные, поэтому мой компьютер не может ничего сохранить в память.
Я постарался придерживаться архитектуры Бена и размещать все элементы в том-же порядке что и он.


Отдельного упоминания заслуживает блок декодирования инструкций. Если попытаться его реализовать при помощи логических элементов, то получится очень сложная, перегруженная схема в которой будет легко запутаться. Поэтому Бен предлагает использовать чипы ROM памяти. Он предлагает программу которая прошивает данные через Arduino. Но нам-то что делать как загрузить данные в игру? К счастью в файлах игры можно найти содержимое ROM файла который можно редактировать в Hex редакторе либо как Бен написать для этого программу.
#include
#include
#include
#include
#include
#include
#define HLT 0b1000000000000000
#define MI 0b0100000000000000
#define RO 0b0010000000000000
#define II 0b0001000000000000
#define IO 0b0000100000000000
#define AI 0b0000010000000000
#define AO 0b0000001000000000
#define EO 0b0000000100000000
#define SU 0b0000000010000000
#define BI 0b0000000001000000
#define OI 0b0000000000100000
#define CE 0b0000000000010000
#define CO 0b0000000000001000
#define J 0b0000000000000100
#define FI 0b0000000000000010
#define FLAGGS_Z0C0 0
#define FLAGGS_Z0C1 1
#define FLAGGS_Z1C0 2
#define FLAGGS_Z1C1 3
#define JC 0b0111
#define JZ 0b1000
uint16_t ucode_TEMP[16][8]=
{
{MI|CO, RO|II|CE, 0, 0, 0, 0, 0, 0}, // 0000 - NOP
{MI|CO, RO|II|CE, IO|MI, RO|AI, 0, 0, 0, 0}, // 0001 - LDA
{MI|CO, RO|II|CE, IO|MI, RO|BI, EO|AI|FI, 0, 0, 0}, // 0010 - ADD
{MI|CO, RO|II|CE, IO|MI, RO|BI, EO|AI|SU|FI, 0, 0, 0}, // 0011 - SUB
{MI|CO, RO|II|CE, 0, 0, 0, 0, 0, 0}, // 0100 - STA
{MI|CO, RO|II|CE, IO|AI, 0, 0, 0, 0, 0}, // 0101 - LDI
{MI|CO, RO|II|CE, IO|J, 0, 0, 0, 0, 0}, // 0110 - JMP
{MI|CO, RO|II|CE, 0, 0, 0, 0, 0, 0}, // 0111 - JC
{MI|CO, RO|II|CE, 0, 0, 0, 0, 0, 0}, // 1000 - JZ
{MI|CO, RO|II|CE, 0, 0, 0, 0, 0, 0}, // 1001
{MI|CO, RO|II|CE, 0, 0, 0, 0, 0, 0}, // 1010
{MI|CO, RO|II|CE, 0, 0, 0, 0, 0, 0}, // 1011
{MI|CO, RO|II|CE, 0, 0, 0, 0, 0, 0}, // 1100
{MI|CO, RO|II|CE, 0, 0, 0, 0, 0, 0}, // 1101
{MI|CO, RO|II|CE, AO|OI, 0, 0, 0, 0, 0}, // 1110 - OUT
{MI|CO, RO|II|CE, HLT, 0, 0, 0, 0, 0}, // 1111 - HALT
};
uint16_t ucode[4][16][8];
void initUCode()
{
// ZF = 0, CF = 0
memcpy(ucode[FLAGGS_Z0C0], ucode_TEMP, sizeof(ucode_TEMP));
// ZF = 0, CF = 1
memcpy(ucode[FLAGGS_Z0C1], ucode_TEMP, sizeof(ucode_TEMP));
ucode[FLAGGS_Z0C1][JC][2] = IO|J;
// ZF = 1, CF = 0
memcpy(ucode[FLAGGS_Z1C0], ucode_TEMP, sizeof(ucode_TEMP));
ucode[FLAGGS_Z1C0][JZ][2] = IO|J;
// ZF = 1, CF = 1
memcpy(ucode[FLAGGS_Z1C1], ucode_TEMP, sizeof(ucode_TEMP));
ucode[FLAGGS_Z1C1][JC][2] = IO|J;
ucode[FLAGGS_Z1C1][JZ][2] = IO|J;
}
std::vector Names =
{
" - NOP",
" - LDA",
" - ADD",
" - SUB",
" - STA",
" - LDI",
" - JMP",
" - JC",
" - JZ",
" - NOP",
" - NOP",
" - NOP",
" - NOP",
" - NOP",
" - OUT",
" - HALT",
"--ZF = 0, CF = 0------------------",
"--ZF = 0, CF = 1------------------",
"--ZF = 1, CF = 0------------------",
"--ZF = 1, CF = 1------------------",
};
int main()
{
initUCode();
std::ofstream fout("2929467240861350664.rom", std::ios::binary); // создаём объект класса ofstream для записи и связываем его с файлом cppstudio.txt
for (int z = 0; z < 4; z++)
{
std::cout << std::endl;
std::cout << Names[16 + z] << std::endl;
for (int j = 0; j < 16; j++)
{
for (int i = 0; i < 8; i++)
{
std::cout << std::format("{: ^10}",ucode[z][j][i]);
}
std::cout << Names[j] << std::endl;
}
}
fout.write((char*)&ucode, sizeof(ucode));
fout.close();
return 0;
}
Кстати, когда я писал эту программу, я решил воспользоваться последними достижениями в создании человеко-машинного интерфейса, по версии комиссии стандартизации С++, а именно std: format. И Visual Studio Code сказала мне что не о каком std: format она не знает. Чтобы Visual Studio о нем все таки узнала нужно в настройках расширения C++ выставить параметр Cpp Standart в gnu++20.
После выполнения программы в папке появится файл 2929467240861350664.rom который нужно скопировать в папку с пользовательскими файлами. Путь к этой папке можно посмотреть в настройках игры.
Как установить себе компьютер SAP-1
С начало нужно скачать файлы и распаковать их. После этого идете в настройки и смотрите путь к папке с пользовательскими файлами у меня это:
C:\Users\Dmitry\AppData\Roaming\Godot\app_userdata\Turing Complete
Ну и объединяете содержимое. После этого заходите в режим SandBox и выбираете sup-1.

Вывод
Игра просто бомба. Я очень долго не мог наиграться. Будем надеется что автор доделает игру до конца и она выйдет из раннего доступа.
Мой вариант SUP-1
Цикл роликов Бена Итера на youtube.