Skip to content

Instantly share code, notes, and snippets.

@qookei
Created June 19, 2020 22:44
Show Gist options
  • Save qookei/70506be600e41005311038e36176f2bf to your computer and use it in GitHub Desktop.
Save qookei/70506be600e41005311038e36176f2bf to your computer and use it in GitHub Desktop.
#include <iostream>
#include <string_view>
#include <cctype>
#include <array>
#include <cassert>
struct insn {
uint8_t op = 0;
uint8_t arg = 0;
static insn from_string(std::string_view &str) {
insn i;
i.op = str[0];
str.remove_prefix(1);
size_t n;
for (n = 0; n < str.size() && std::isdigit(str[n]); n++) {
i.arg *= 10;
i.arg += str[n] - '0';
}
str.remove_prefix(n);
return i;
}
static insn from_bytes(uint8_t *data) {
return insn{data[0], data[1]};
}
bool valid() const {
using namespace std::string_view_literals;
return "GORBITSAgorbitsa"sv.find(op)
!= std::string_view::npos;
}
static constexpr bool needs_arg(uint8_t c) {
return c == 'T' || c == 'R';
}
};
struct vm {
std::array<uint8_t, 256> ram;
std::array<insn, 256> rom;
int loaded_size;
uint8_t x;
int pc;
enum class space {
ram, rom
} pc_space;
enum class state {
ok,
invalid_insn,
unexpected_end,
halt
};
state step_once(bool debug = false) {
if (pc >= loaded_size)
return state::halt;
insn i;
if (pc_space == space::ram) {
if (pc > loaded_size - 2 && insn::needs_arg(ram[pc]))
return state::unexpected_end;
i = insn::from_bytes(&ram[pc]);
pc += 2;
} else {
i = rom[pc++];
}
if (!i.valid())
return state::invalid_insn;
if (debug)
print_insn(i);
switch(i.op) {
case 'g':
x = ram[ram[i.arg]];
break;
case 'G':
x = ram[i.arg];
break;
case 'o':
ram[ram[i.arg]] = x;
break;
case 'O':
ram[i.arg] = x;
break;
case 'r':
ram[i.arg] = std::getchar();
break;
case 'R':
x = std::getchar();
break;
case 'b':
if (!x) pc = ram[i.arg];
break;
case 'B':
if (!x) pc = i.arg;
break;
case 'i':
ram[i.arg] += x;
break;
case 'I':
x += i.arg;
break;
case 't':
std::putchar(ram[i.arg]);
break;
case 'T':
std::putchar(x);
break;
case 's':
x ^= ram[i.arg];
break;
case 'S':
x = i.arg;
break;
case 'a':
x += ram[ram[i.arg]];
break;
case 'A':
x += ram[i.arg];
break;
default:
return state::invalid_insn;
}
return state::ok;
}
void print_diag(state s) {
constexpr const char *states[] = {
"ok", "invalid instruction", "unexpected end of input", "halted"
};
uint8_t o = pc_space == space::ram ? ram[pc] : rom[pc].op;
uint8_t a = pc_space == space::ram ? (pc < 255 ? ram[pc + 1] : 0) : rom[pc].arg;
std::printf("state: %s\n", states[static_cast<int>(s)]);
std::printf("ip: %s:%02x ('%c'/%02x %02x) x: %02x\n",
pc_space == space::ram ? "ram" : "rom",
pc, o, o, a, x);
}
void print_insn(insn i) const {
std::printf("%s:%02x: ", pc_space == space::ram ? "ram" : "rom", pc - 1);
switch(i.op) {
case 'g':
std::printf("x = ram[ram[%u] (%u)] (%u)\n", i.arg, ram[i.arg], ram[ram[i.arg]]);
break;
case 'G':
std::printf("x = ram[%u] (%u)\n", i.arg, ram[i.arg]);
break;
case 'o':
std::printf("ram[%u] = x (%u)\n", i.arg, x);
break;
case 'O':
std::printf("ram[ram[%u] (%u)] = x (%u)\n", i.arg, ram[i.arg], x);
break;
case 'r':
std::printf("ram[%u] = getchar()\n", i.arg);
break;
case 'R':
std::printf("x = getchar()\n");
break;
case 'b':
std::printf("if (!x (%u)) pc = ram[%u] (%u)\n", x, i.arg, ram[i.arg]);
break;
case 'B':
std::printf("if (!x (%u)) pc = %u\n", x, i.arg);
break;
case 'i':
std::printf("ram[%u] (%u) += x (%u)\n", i.arg, ram[i.arg], x);
break;
case 'I':
std::printf("x (%u) += %u\n", x, i.arg);
break;
case 't':
std::printf("putchar(ram[%u] (%u))\n", i.arg, ram[i.arg]);
break;
case 'T':
std::printf("putchar(x (%u))\n", x);
break;
case 's':
std::printf("x (%u) ^= ram[%u] (%u)\n", x, i.arg, ram[i.arg]);
break;
case 'S':
std::printf("x = %u\n", i.arg);
break;
case 'a':
std::printf("x (%u) += ram[%u] (%u)\n", x, i.arg, ram[i.arg]);
break;
case 'A':
std::printf("x (%u) += ram[ram[%u] (%u)] (%u)\n", x, i.arg, ram[i.arg], ram[ram[i.arg]]);
break;
default:
std::printf("unknown\n");
}
}
static vm rom_from_string(std::string_view s) {
vm v{};
std::fill_n(v.ram.begin(), 256, 0);
std::fill_n(v.rom.begin(), 256, insn{0, 0});
v.loaded_size = 0;
int i = 0;
while (s.size() && i < 256) {
v.rom[i++] = insn::from_string(s);
if (s.size()) {
s.remove_prefix(1);
}
v.loaded_size++;
}
v.pc_space = space::rom;
v.pc = 0;
return v;
}
static vm ram_from_string(std::string_view s) {
vm v{};
std::fill_n(v.ram.begin(), 256, 0);
std::fill_n(v.rom.begin(), 256, insn{0, 0});
v.loaded_size = 0;
int i = 0;
while (s.size() && i < 128) {
auto in = insn::from_string(s);
if (s.size()) {
s.remove_prefix(1);
}
v.ram[i * 2] = in.op;
v.ram[i++ * 2 + 1] = in.arg;
v.loaded_size += 2;
}
v.pc_space = space::ram;
v.pc = 0;
return v;
}
};
int main(int argc, char **argv) {
// Hello World
//vm v = vm::rom_from_string("S72 T S101 T S108 T T S111 T S32 T S87 T S111 T S114 T S108 T S100 T");
// Simple Stairs (By Megarev)
//vm v = vm::rom_from_string("S10 O10 S0 O1 S6 O0 G0 I255 O0 B24 t10 G1 I1 O1 O2 G2 I255 O2 S35 T G2 B6 S0 B15 t10");
// A 10x10 Box (By Megarev)
vm v = vm::rom_from_string("S10 O0 G0 I255 O0 B19 S10 T S10 O1 G1 I255 O1 S35 T G1 B2 S0 B10 S10 T");
// Cheaty Quine
//vm v = vm::ram_from_string("S0 O255 g255 B22 g255 T G255 I1 O255 S0 B4 G255 I1 O240 g240 B36 S0 B10 B200");
vm::state s;
while((s = v.step_once()) == vm::state::ok)
;
putchar('\n');
if (s != vm::state::halt)
v.print_diag(s);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment