-- Mutate a memory table per the operation at the current program
-- counter, and return the next program counter.
local brianvm = (function()
    local OPS = {
        function(m, pc)
            io.write(string.char(m[1+m[2+pc]]))
            return pc + 2
        end,
        function(m, pc)
            m[1+m[2+pc]] = m[1+m[3+pc]]
            return pc + 3
        end,
        function(m, pc)
            m[1+m[2+pc]] = (m[1+m[2+pc]] + m[3+pc]) % 256
            return pc + 3
        end,
        function(m, pc)
            m[1+m[2+pc]] = (m[1+m[2+pc]] - m[3+pc]) % 256
            return pc + 3
        end,
        function(m, pc)
            m[1+m[2+pc]] = (m[1+m[2+pc]] * m[3+pc]) % 256
            return pc + 3
        end,
        function(m, pc)
            m[1+m[2+pc]] = math.floor(m[1+m[2+pc]] / m[3+pc])
            return pc + 3
        end,
        function(m, pc)
            m[1+m[2+pc]] = m[1+m[2+pc]] % m[3+pc]
            return pc + 3
        end,
        function(m, pc)
            m[1+m[2+pc]] = math.floor(math.random() * 256)
            return pc + 2
        end,
        function(m, pc)
            local e = os.clock() + m[2+pc]/1000.0
            while os.clock() < e do end
            return pc + 2
        end,
        function(m, pc)
            return m[2+pc]
        end,
        function(m, pc)
            if m[1+m[2+pc]] == 0 then
                return m[3+pc]
            else
                return pc + 3
            end
        end
    }
    return function(m, pc)
        return OPS[1+m[1+pc]](m, pc)
    end
end)()

-- Demo program
local pc = 0
local m = {
    0x01, 0x04, 0x07, 0x0a, 0x00, 0x0d, 0x00, 0xf8,
    0x02, 0x07, 0x01, 0x09, 0x00, 0x04, 0x07, 0x00,
    0x02, 0x07, 0xf8, 0x07, 0xe0, 0x06, 0xe0, 0x3a,
    0x07, 0xdf, 0x06, 0xdf, 0x18, 0x01, 0xff, 0xdf,
    0x06, 0xff, 0x0a, 0x02, 0xff, 0x30, 0x01, 0xd9,
    0xff, 0x05, 0xdf, 0x0a, 0x02, 0xdf, 0x30, 0x01,
    0xd8, 0xdf, 0x01, 0xff, 0xe0, 0x06, 0xff, 0x0a,
    0x02, 0xff, 0x30, 0x01, 0xdc, 0xff, 0x05, 0xe0,
    0x0a, 0x02, 0xe0, 0x30, 0x01, 0xdb, 0xe0, 0x01,
    0x4b, 0x4e, 0x0a, 0x00, 0x54, 0x00, 0xd6, 0x02,
    0x4e, 0x01, 0x09, 0x47, 0x04, 0x4e, 0x00, 0x02,
    0x4e, 0xd6, 0x07, 0xff, 0x06, 0xff, 0x08, 0x02,
    0xff, 0x30, 0x01, 0xd1, 0xff, 0x01, 0x69, 0x6c,
    0x0a, 0x00, 0x72, 0x00, 0xce, 0x02, 0x6c, 0x01,
    0x09, 0x65, 0x04, 0x6c, 0x00, 0x02, 0x6c, 0xce,
    0x01, 0x7c, 0x7f, 0x0a, 0x00, 0x87, 0x00, 0xe1,
    0x02, 0x7f, 0x01, 0x08, 0x8c, 0x09, 0x78, 0x04,
    0x7f, 0x00, 0x02, 0x7f, 0xe1, 0x08, 0xfa, 0x08,
    0xfa, 0x09, 0x13, 0x6a, 0xba, 0x1f, 0x6d, 0xa3,
    0xe6, 0xad, 0x3b, 0xae, 0x4d, 0x6e, 0xc1, 0xfa,
    0x11, 0xed, 0x22, 0x2e, 0x6d, 0xa3, 0xed, 0x48,
    0x3d, 0xf9, 0x4d, 0xed, 0x24, 0x29, 0x2a, 0x16,
    0xe3, 0x09, 0x25, 0xc6, 0x31, 0x62, 0xb5, 0x21,
    0x2e, 0xa0, 0x47, 0x5b, 0xa2, 0x02, 0x3c, 0x82,
    0x83, 0x4f, 0x58, 0x2a, 0x65, 0x87, 0x25, 0x43,
    0x6d, 0xde, 0xa9, 0x31, 0x81, 0x09, 0x1b, 0x5b,
    0x39, 0x63, 0x3b, 0x31, 0x6d, 0x00, 0x1b, 0x5b,
    0x78, 0x78, 0x3b, 0x79, 0x79, 0x48, 0x00, 0x18,
    0x55, 0x47, 0x65, 0x74, 0x20, 0x77, 0x65, 0x6c,
    0x6c, 0x20, 0x73, 0x6f, 0x6f, 0x6e, 0x2c, 0x20,
    0x42, 0x72, 0x69, 0x61, 0x6e, 0x21, 0x0a, 0x00,
    0x1b, 0x5b, 0x32, 0x4a, 0x1b, 0x5b, 0x48, 0x00,
}
while true do
    pc = brianvm(m, pc)
end
