#include <array>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <vector>
void um(std::istream & scroll, std::istream & i, std::ostream & o)
{
std::unitbuf(o);
using Chr = std::uint8_t;
using Plt = std::uint32_t;
auto reg = std::array<Plt, 8>();
auto arr = std::vector<std::vector<Plt>>(1);
auto fng = Plt(0);
auto hlt = false;
auto alloc = [&](Plt c) { arr.emplace_back(c); return(Plt)arr.size()-1; };
auto aband = [&](Plt c) { arr[c] = decltype(arr)::value_type(); };
#define PACK(IND) ((Plt)(Chr)ch[IND] << 8*(3-IND))
for (char ch[4]; scroll.read(ch, 4);)
arr[0].push_back(PACK(0) | PACK(1) | PACK(2) | PACK(3));
#undef PACK
while (!hlt)
{
#define UNPACK(POS, SIZ) ((plt >> POS) & ((Plt(1) << SIZ) - 1))
auto plt = arr[0][fng++];
auto num = UNPACK(28, 4);
auto val = UNPACK( 0, 25);
auto & a = reg[UNPACK( 6, 3)];
auto & b = reg[UNPACK( 3, 3)];
auto & c = reg[UNPACK( 0, 3)];
auto & d = reg[UNPACK(25, 3)];
#undef UNPACK
switch (num)
{
case 0: a = c ? b : a; break; // CMOVE
case 1: a = arr[b][c]; break; // LOAD
case 2: arr[a][b] = c; break; // STOR
case 3: a = b + c; break; // ADD
case 4: a = b * c; break; // MUL
case 5: a = b / c; break; // DIV
case 6: a = ~(b & c); break; // NAND
case 7: hlt = true; break; // HALT
case 8: b = alloc(c); break; // ALLOC
case 9: aband(c); break; // ABAND
case 10: o.put(c); break; // PUT
case 11: c = i.get(); break; // GET
case 12: arr[0] = arr[b]; fng = c; break; // EXEC
case 13: d = val; break; // VALUE
}
}
}
int main(int argc, char * argv[])
{
if (argc != 2)
return std::fputs("Usage: um <scroll>\n", stderr), EXIT_FAILURE;
auto scroll = std::ifstream(argv[1], std::ios::binary);
if (scroll.fail())
return std::perror("Failed to read scroll"), EXIT_FAILURE;
if (scroll.peek(), scroll.eof())
return std::fputs("Empty scroll\n", stderr), EXIT_FAILURE;
um(scroll, std::cin, std::cout);
}