#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);
}
