Created
June 19, 2020 22:44
-
-
Save qookei/70506be600e41005311038e36176f2bf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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