ilo-vm/source/ilo.lua

343 lines
7.8 KiB
Lua

-- ilo.lua
-- Partially derived from Ngaro in Lua
-- Copyright (c) 2010 - 2023, Charles Childers
--
-- This is only tested on Lua 5.4 and requires 32-bit
-- integers. (set `#define LUA_32BITS 1` in luaconf.h when
-- building Lua)
-- Variables
local ip = 0 -- instruction pointer
local sp = 0 -- stack pointer
local rp = 0 -- return pointer
local stack = {} -- data stack
local address = {} -- return stack
local memory = {} -- simulated ram
-- Support Code
local function file_size(filename)
local fh = assert(io.open(filename, 'rb'))
local len = assert(fh:seek('end'))
fh:close()
return len
end
local function constrain(n)
return (n > 0x7FFFFFFF) and (n - 0x100000000) or n
end
local function save_image()
local image = io.open('ilo.rom', 'wb')
local i = 0
while i < 65536 do
image:write(string.pack('<i4', memory[i]))
i = i + 1
end
image:close()
end
local function read_block()
local blocks = io.open('ilo.blocks', 'rb')
local buffer = stack[sp]
local block = stack[sp - 1]
sp = sp - 2
local i = 0
blocks:seek('set', block * 4096)
while i < 1024 do
memory[buffer] = string.unpack('<i4', blocks:read(4))
buffer = buffer + 1
i = i + 1
end
blocks:close()
end
local function write_block()
local blocks = io.open('ilo.blocks', 'r+b')
local buffer = stack[sp]
local block = stack[sp - 1]
sp = sp - 2
local i = 0
blocks:seek('set', block * 4096)
while i < 1024 do
local x = memory[buffer + i]
local b4=string.char(x%256) x=(x-x%256)/256
local b3=string.char(x%256) x=(x-x%256)/256
local b2=string.char(x%256) x=(x-x%256)/256
local b1=string.char(x%256) x=(x-x%256)/256
blocks:write(b4,b3,b2,b1)
i = i + 1
end
blocks:close()
end
local opcodes = {
[0] = function() end, -- no (nop)
[1] = function() -- li (lit)
sp = sp + 1
ip = ip + 1
stack[sp] = memory[ip]
end,
[2] = function() -- du (dup)
sp = sp + 1
stack[sp] = stack[sp - 1]
end,
[3] = function() -- dr (drop)
sp = sp - 1
end,
[4] = function() -- sw (swap)
local a = stack[sp]
stack[sp] = stack[sp - 1]
stack[sp - 1] = a
end,
[5] = function() -- pu (push)
rp = rp + 1
address[rp] = stack[sp]
sp = sp - 1
end,
[6] = function() -- po (pop)
sp = sp + 1
stack[sp] = address[rp]
rp = rp - 1
end,
[7] = function() -- ju (jump)
ip = stack[sp] - 1
sp = sp - 1
end,
[8] = function() -- ca (call)
rp = rp + 1
address[rp] = ip
ip = stack[sp] - 1
sp = sp - 1
end,
[9] = function() -- cc (cond. call)
if stack[sp - 1] ~= 0 then
rp = rp + 1
address[rp] = ip
ip = stack[sp] - 1
end
sp = sp - 2
end,
[10] = function() -- cj (cond. jump)
if stack[sp - 1] ~= 0 then
ip = stack[sp] - 1
end
sp = sp - 2
end,
[11] = function() -- re (return)
ip = address[rp]
rp = rp - 1
end,
[12] = function() -- eq (equality)
if stack[sp - 1] == stack[sp] then
stack[sp - 1] = -1
else
stack[sp - 1] = 0
end
sp = sp - 1
end,
[13] = function() -- ne (inequality)
if stack[sp - 1] == stack[sp] then
stack[sp - 1] = 0
else
stack[sp - 1] = -1
end
sp = sp - 1
end,
[14] = function() -- lt (less than)
if stack[sp - 1] < stack[sp] then
stack[sp - 1] = -1
else
stack[sp - 1] = 0
end
sp = sp - 1
end,
[15] = function() -- gt (greater than)
if stack[sp - 1] > stack[sp] then
stack[sp - 1] = -1
else
stack[sp - 1] = 0
end
sp = sp - 1
end,
[16] = function() -- fe (fetch)
stack[sp] = memory[stack[sp]]
end,
[17] = function() -- st (store)
memory[stack[sp]] = stack[sp - 1]
sp = sp - 2
end,
[18] = function() -- ad (add)
stack[sp - 1] = stack[sp - 1] + stack[sp]
sp = sp - 1
end,
[19] = function() -- su (subtract)
stack[sp - 1] = stack[sp - 1] - stack[sp]
sp = sp - 1
end,
[20] = function() -- mu (multiply)
stack[sp - 1] = stack[sp - 1] * stack[sp]
sp = sp - 1
end,
[21] = function() -- di (divide & remainder)
local b = stack[sp]
local a = stack[sp - 1]
local x = math.abs(b)
local y = math.abs(a)
local q = constrain(y // x)
local r = constrain(y % x)
if a < 0 and b < 0 then
r = r * -1
elseif a > 0 and b < 0 then
q = q * -1
elseif a < 0 and b > 0 then
r = r * -1
q = q * -1
end
stack[sp] = q
stack[sp - 1] = r
end,
[22] = function() -- an (and)
stack[sp - 1] = stack[sp - 1] & stack[sp]
sp = sp - 1
end,
[23] = function() -- or (or)
stack[sp - 1] = stack[sp - 1] | stack[sp]
sp = sp - 1
end,
[24] = function() -- xo (xor)
stack[sp - 1] = stack[sp - 1] ~ stack[sp]
sp = sp - 1
end,
[25] = function() -- sl (shift left)
stack[sp - 1] = stack[sp - 1] << stack[sp]
sp = sp - 1
end,
[26] = function() -- sr (shift right)
stack[sp - 1] = stack[sp - 1] >> stack[sp]
sp = sp - 1
end,
[27] = function() -- cp (compare)
local a = stack[sp] -- length
local b = stack[sp - 1] -- dest
local c = stack[sp - 2] -- source
local d = -1 -- flag
sp = sp - 3
while a > 0 do
if memory[b] ~= memory[c] then
d = 0
end
a = a - 1
b = b + 1
c = c + 1
end
sp = sp + 1
stack[sp] = d
end,
[28] = function() -- cy (copy)
local a = stack[sp] -- length
local b = stack[sp - 1] -- dest
local c = stack[sp - 2] -- source
sp = sp - 3
while a > 0 do
memory[b] = memory[c]
a = a - 1
b = b + 1
c = c + 1
end
end,
[29] = function() -- io
local d = stack[sp]
sp = sp - 1
if d == 0 then
io.write(string.char(stack[sp]))
sp = sp - 1
elseif d == 1 then
sp = sp + 1
stack[sp] = string.byte(io.read(1))
elseif d == 2 then
read_block()
elseif d == 3 then
write_block()
elseif d == 4 then
save_image()
elseif d == 5 then
load_image("ilo.rom")
ip = -1
sp = 0
rp = 0
elseif d == 6 then
ip = 65536
elseif d == 7 then
sp = sp + 1
stack[sp] = sp - 1
sp = sp + 1
stack[sp] = rp
end
end
}
-- -------------------------------------------------------------
-- process opcodes
local function process_opcode(opcode)
if opcode == 0 then return end -- skip NOP instructions
local op_fn = opcodes[opcode]
if op_fn then op_fn() end
end
local function process_opcodes()
local opcode = memory[ip]
process_opcode(opcode & 0xFF)
process_opcode(opcode>>8 & 0xFF)
process_opcode(opcode>>16 & 0xFF)
process_opcode(opcode>>24 & 0xFF)
end
-- Load image into memory
-- \ cells = size of image file (in cells)
-- \ image = pointer to retroImage file
local function load_image(file)
local cells = file_size(file) / 4
local image = io.open(file, 'rb')
local i = 0
while i < cells do
memory[i] = string.unpack('<i4', image:read(4))
i = i + 1
end
image:close()
while i < 65536 do
memory[i] = 0
i = i + 1
end
i = 0
while i < 33 do
stack[i] = 0
address[i] = 0
i = i + 1
end
while i < 257 do
address[i] = 0
i = i + 1
end
end
local function dump_stack()
while sp > 0 do
print(stack[sp])
sp = sp - 1
end
end
local function process_instructions()
while ip < 65536 do
process_opcodes()
ip = ip + 1
end
end
local function main()
load_image("ilo.rom")
process_instructions()
dump_stack()
end
main()