Skip to content

Instantly share code, notes, and snippets.

@thacuber2a03
Last active June 30, 2025 20:38
Show Gist options
  • Select an option

  • Save thacuber2a03/fed3d75a0516f42c35831df0334de50d to your computer and use it in GitHub Desktop.

Select an option

Save thacuber2a03/fed3d75a0516f42c35831df0334de50d to your computer and use it in GitHub Desktop.
-- an implementation of the Uxn VM in Lua, written by @thacuber2a03
-- compatible with 5.1 and JIT
-- thanks to @soxfox42 for helping on stack issues with the keep flag
-- and other things
local unpack = table.unpack or unpack
local ram, dev = {}, {}
local wst, rst = {kidx=1,idx=1}, {kidx=1,idx=1}
local function wrap(n, m)
while n > m do n = n - m end
while n < 1 do n = n + m end
return n
end
local function idxwrap(i) return wrap(i, 0x100 ) end
local function addrwrap(a) return wrap(a, 0x10000) end
local function push(stk, v, t)
if stk.kidx then stk.idx, stk.kidx = stk.kidx, nil end
if t then
stk[stk.idx] = (v >> 8) & 0xff
stk.idx = idxwrap(stk.idx + 1)
end
stk[stk.idx] = v & 0xff
stk.idx = idxwrap(stk.idx + 1)
end
local function pop(stk, t)
stk.idx = idxwrap(stk.idx - 1)
local v = stk[stk.idx] & 0xff
if t then
stk.idx = idxwrap(stk.idx - 1)
v = ((stk[stk.idx] & 0xff) << 8) | v
end
return v
end
local function popn(s, t, n)
local r = {}
for i=1, n do r[#r+1] = pop(s,t) end
return unpack(r)
end
local function pushn(s, t, ...)
for _, n in ipairs{...} do push(s, t, n) end
end
local pc
local instrs = {
[0x01] = function(s, t) push(s,t, pop(s,t)+1) end, -- INC
[0x02] = function(s, t) pop(s,t) end, -- POP
[0x03] = function(s, t) local a,b=popn(s,t,2) push (s,t, a) end, -- NIP
[0x04] = function(s, t) local a,b=popn(s,t,2) pushn(s,t, a,b) end, -- SWP
[0x05] = function(s, t) local a,b,c=popn(s,t,3) pushn(s,t, c,a,b) end, -- ROT
[0x18] = function(s, t) local a,b=popn(s,t,2) push(s,t,a+b) end, -- ADD
[0x19] = function(s, t) local a,b=popn(s,t,2) push(s,t,a-b) end, -- SUB
[0x1a] = function(s, t) local a,b=popn(s,t,2) push(s,t,a*b) end, -- MUL
[0x1b] = function(s, t) local a,b=popn(s,t,2) push(s,t,b==0 and 0 or a/b) end, -- DIV
[0x1c] = function(s, t) local a,b=popn(s,t,2) push(s,t,b&a) end, -- AND
[0x1d] = function(s, t) local a,b=popn(s,t,2) push(s,t,b|a) end, -- ORA
[0x1e] = function(s, t) local a,b=popn(s,t,2) push(s,t,b~a) end, -- EOR
[0x1f] = function(s, t) local a,b=popn(s,t,2) push(s,t,b>>(a&0xf)<<(a>>4)) end, -- SFT
}
local function jmi()
local off = ram[pc] << 8 | ram[pc+1]
pc = idxwrap(pc + 2 + off)
end
local function eval(rv)
assert(ram[1], "no program loaded")
pc = addrwrap(rv)
while true do
local byt = ram[addrwrap(pc)]
local ins = byt & 0x1f
local flg = byt & 0xe0
pc = pc + 1
local s = wst
if (flg & 0x80) ~= 0 then s = rst end
local t = (flg & 0x40) ~= 0
local k = (flg & 0x20) ~= 0
if ins == 0x00 then
if k then -- LIT[2r]
for i=1, t and 1 or 2 do
push(s, false, ram[pc]); pc = pc + 1
end
elseif byt == 0x40 then jmi()
elseif byt == 0x20 then -- JCI
if pop(wst, false) ~= 0 then jmi() else pc = pc + 2 end
elseif byt == 0x60 then
push(rst, true, pc+2); jmi() -- JSI
else return true end -- BRK
else
if k then s.kidx = s.idx end
instrs[ins](s, t)
s.kidx = nil
end
end
end
local function load(prg)
for i=1, 0x10000 do ram[i] = 0 end
for i=1, 0x100 do dev[i] = 0 end
for i, b in ipairs(prg) do ram[0x100+i] = b end
end
return {
load = load,
eval = eval,
ram = ram,
dev = dev,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment