1
0
Fork 0
mirror of https://github.com/notwa/mm synced 2024-11-05 02:39:02 -08:00
mm/Lua/lib/lips/Dumper.lua

437 lines
15 KiB
Lua
Raw Normal View History

2016-01-13 07:56:18 -08:00
local floor = math.floor
2016-01-13 11:20:28 -08:00
local format = string.format
local insert = table.insert
2016-04-26 14:48:39 -07:00
local remove = table.remove
2016-04-21 13:04:49 -07:00
local unpack = unpack or table.unpack
2016-01-13 07:56:18 -08:00
2016-04-21 13:04:49 -07:00
local path = string.gsub(..., "[^.]+$", "")
local data = require(path.."data")
local util = require(path.."util")
local Token = require(path.."Token")
local Statement = require(path.."Statement")
local Reader = require(path.."Reader")
2016-01-13 07:56:18 -08:00
2016-01-13 11:20:28 -08:00
local bitrange = util.bitrange
2016-01-13 07:56:18 -08:00
2016-04-21 13:04:49 -07:00
local Dumper = Reader:extend()
function Dumper:init(writer, options)
2016-01-13 07:56:18 -08:00
self.writer = writer
self.options = options or {}
2016-04-10 06:07:06 -07:00
self.labels = setmetatable({}, {__index=options.labels})
2016-01-13 07:56:18 -08:00
self.commands = {}
self.lastcommand = nil
2016-04-21 13:04:49 -07:00
self.pos = 0
self.base = 0
2016-01-13 07:56:18 -08:00
end
2016-04-10 06:07:06 -07:00
function Dumper:export_labels(t)
for k, v in pairs(self.labels) do
-- only return valid labels; those that don't begin with a number
-- (relative labels are invalid)
if not tostring(k):sub(1, 1):find('%d') then
t[k] = v
end
end
return t
end
2016-04-26 14:48:39 -07:00
function Dumper:label_delta(from, to)
from = from % 0x80000000
to = to % 0x80000000
local rel = floor(to/4) - 1 - floor(from/4)
if rel > 0x8000 or rel <= -0x8000 then
self:error('branch too far', rel)
end
return rel % 0x10000
end
2016-01-13 11:20:28 -08:00
function Dumper:desym(t)
2016-04-26 14:48:39 -07:00
-- note: don't run t:compute() here; let valvar handle that
if t.tt == 'REL' and not t.fixed then
return self:label_delta(self:pc(), t.tok)
2016-04-02 08:42:22 -07:00
elseif type(t.tok) == 'number' then
2016-01-13 11:20:28 -08:00
return t.tok
elseif t.tt == 'REG' then
assert(data.all_registers[t.tok], 'Internal Error: unknown register')
return data.registers[t.tok] or data.fpu_registers[t.tok] or data.sys_registers[t.tok]
2016-04-10 06:07:06 -07:00
elseif t.tt == 'LABELSYM' or t.tt == 'LABELREL' then
2016-01-13 11:20:28 -08:00
local label = self.labels[t.tok]
2016-01-13 07:56:18 -08:00
if label == nil then
2016-04-21 13:04:49 -07:00
self:error('undefined label', t.tok)
2016-01-13 07:56:18 -08:00
end
2016-04-10 06:07:06 -07:00
if t.tt == 'LABELSYM' then
return label
end
2016-04-26 14:48:39 -07:00
return self:label_delta(self:pc(), label)
2016-01-13 07:56:18 -08:00
end
2016-01-13 11:20:28 -08:00
error('Internal Error: failed to desym')
2016-01-13 07:56:18 -08:00
end
function Dumper:validate(n, bits)
local max = 2^bits
if n == nil then
2016-04-26 14:48:39 -07:00
error('Internal Error: number to validate is nil', 2)
2016-01-13 07:56:18 -08:00
end
if n > max or n < 0 then
2016-04-21 13:04:49 -07:00
self:error('value out of range', ("%X"):format(n))
2016-01-13 07:56:18 -08:00
end
2016-04-21 13:04:49 -07:00
return n
2016-01-13 07:56:18 -08:00
end
2016-01-13 11:20:28 -08:00
function Dumper:valvar(t, bits)
2016-04-21 13:04:49 -07:00
local val = t
local err
if type(val) ~= 'number' then
t.tok = self:desym(t)
t.tt = 'NUM'
val, err = t:compute()
if err then
self:error(err, val)
end
end
return self:validate(val, bits)
2016-01-13 07:56:18 -08:00
end
function Dumper:write(t)
for _, b in ipairs(t) do
2016-04-10 08:56:42 -07:00
self.writer(self.pos, b)
2016-01-13 07:56:18 -08:00
self.pos = self.pos + 1
end
end
2016-04-21 13:04:49 -07:00
function Dumper:assemble_j(first, out)
local w = 0
w = w + self:valvar(first, 6) * 0x04000000
w = w + self:valvar(out[1], 26) * 0x00000001
local t = Token(self.fn, self.line, 'WORDS', {w})
local s = Statement(self.fn, self.line, '!DATA', t)
return s
end
function Dumper:assemble_i(first, out)
local w = 0
w = w + self:valvar(first, 6) * 0x04000000
w = w + self:valvar(out[1], 5) * 0x00200000
w = w + self:valvar(out[2], 5) * 0x00010000
w = w + self:valvar(out[3], 16) * 0x00000001
local t = Token(self.fn, self.line, 'WORDS', {w})
local s = Statement(self.fn, self.line, '!DATA', t)
return s
end
function Dumper:assemble_r(first, out)
local w = 0
w = w + self:valvar(first, 6) * 0x04000000
w = w + self:valvar(out[1], 5) * 0x00200000
w = w + self:valvar(out[2], 5) * 0x00010000
w = w + self:valvar(out[3], 5) * 0x00000800
w = w + self:valvar(out[4], 5) * 0x00000040
w = w + self:valvar(out[5], 6) * 0x00000001
local t = Token(self.fn, self.line, 'WORDS', {w})
local s = Statement(self.fn, self.line, '!DATA', t)
return s
end
function Dumper:format_in(informat)
-- see data.lua for a guide on what all these mean
local args = {}
--if #informat ~= #s then error('mismatch') end
2016-04-26 14:48:39 -07:00
self.i = 0
2016-04-21 13:04:49 -07:00
for i=1, #informat do
self.i = i
local c = informat:sub(i, i)
if c == 'd' and not args.rd then
args.rd = self:register(data.registers)
elseif c == 's' and not args.rs then
args.rs = self:register(data.registers)
elseif c == 't' and not args.rt then
args.rt = self:register(data.registers)
elseif c == 'D' and not args.fd then
args.fd = self:register(data.fpu_registers)
elseif c == 'S' and not args.fs then
args.fs = self:register(data.fpu_registers)
elseif c == 'T' and not args.ft then
args.ft = self:register(data.fpu_registers)
elseif c == 'X' and not args.rd then
args.rd = self:register(data.sys_registers)
elseif c == 'Y' and not args.rs then
args.rs = self:register(data.sys_registers)
elseif c == 'Z' and not args.rt then
args.rt = self:register(data.sys_registers)
elseif c == 'o' and not args.offset then
args.offset = self:const():set('signed')
elseif c == 'r' and not args.offset then
args.offset = self:const('relative'):set('signed')
elseif c == 'i' and not args.immediate then
args.immediate = self:const(nil, 'no label')
elseif c == 'I' and not args.index then
args.index = self:const():set('index')
elseif c == 'k' and not args.immediate then
2016-04-26 14:48:39 -07:00
args.immediate = self:const(nil, 'no label'):set('signed'):set('negate')
2016-04-21 13:04:49 -07:00
elseif c == 'K' and not args.immediate then
args.immediate = self:const(nil, 'no label'):set('signed')
elseif c == 'b' and not args.base then
args.base = self:deref():set('tt', 'REG')
else
error('Internal Error: invalid input formatting string')
end
end
return args
end
function Dumper:format_out_raw(outformat, first, args, const, formatconst)
-- see data.lua for a guide on what all these mean
local lookup = {
[1]=self.assemble_j,
[3]=self.assemble_i,
[5]=self.assemble_r,
}
local out = {}
for i=1, #outformat do
local c = outformat:sub(i, i)
if c == 'd' then out[#out+1] = args.rd
elseif c == 's' then insert(out, args.rs)
elseif c == 't' then insert(out, args.rt)
elseif c == 'D' then insert(out, args.fd)
elseif c == 'S' then insert(out, args.fs)
elseif c == 'T' then insert(out, args.ft)
elseif c == 'o' then insert(out, args.offset)
elseif c == 'i' then insert(out, args.immediate)
elseif c == 'I' then insert(out, args.index)
elseif c == 'b' then insert(out, args.base)
elseif c == '0' then insert(out, 0)
elseif c == 'C' then insert(out, const)
elseif c == 'F' then insert(out, formatconst)
end
end
local f = lookup[#outformat]
assert(f, 'Internal Error: invalid output formatting string')
return f(self, first, out)
end
function Dumper:format_out(t, args)
return self:format_out_raw(t[3], t[1], args, t[4], t[5])
end
function Dumper:assemble(s)
local name = s.type
local h = data.instructions[name]
self.s = s
if h[2] ~= nil then
local args = self:format_in(h[2])
2016-04-26 14:48:39 -07:00
if self.i ~= #s then
self:error('expected EOL; too many arguments')
end
2016-04-21 13:04:49 -07:00
return self:format_out(h, args)
else
self:error('unimplemented instruction', name)
end
end
function Dumper:fill(length, content)
self:validate(content, 8)
local bytes = {}
for i=1, length do
insert(bytes, content)
end
local t = Token(self.fn, self.line, 'BYTES', bytes)
local s = Statement(self.fn, self.line, '!DATA', t)
return s
end
function Dumper:pc()
return self.pos + self.base
end
function Dumper:load(statements)
2016-04-26 14:48:39 -07:00
local valstack = {} -- for .push/.pop directives
2016-04-21 13:04:49 -07:00
local new_statements = {}
self.pos = 0
self.base = 0
for i=1, #statements do
local s = statements[i]
self.fn = s.fn
self.line = s.line
if s.type:sub(1, 1) == '!' then
if s.type == '!LABEL' then
self.labels[s[1].tok] = self:pc()
elseif s.type == '!DATA' then
s.length = util.measure_data(s) -- cache for next pass
self.pos = self.pos + s.length
insert(new_statements, s)
elseif s.type == '!ORG' then
self.pos = s[1].tok
insert(new_statements, s)
elseif s.type == '!BASE' then
self.base = s[1].tok
insert(new_statements, s)
2016-04-26 14:48:39 -07:00
elseif s.type == '!PUSH' or s.type == '!POP' then
local thistype = s.type:sub(2):lower()
for i, t in ipairs(s) do
local name = t.tok
if type(name) ~= 'string' then
self:error('expected state to '..thistype, name)
end
name = name:lower()
local pushing = s.type == '!PUSH'
if name == 'org' then
if pushing then
insert(valstack, self.pos)
else
self.pos = remove(valstack)
end
elseif name == 'base' then
if pushing then
insert(valstack, self.base)
else
self.base = remove(valstack)
end
elseif name == 'pc' then
if pushing then
insert(valstack, self.pos)
insert(valstack, self.base)
else
self.base = remove(valstack)
self.pos = remove(valstack)
end
else
self:error('unknown state to '..thistype, name)
end
if self.pos == nil or self.base == nil then
self:error('ran out of values to pop')
end
if not pushing then
local s = Statement(self.fn, self.line, '!ORG', self.pos)
insert(new_statements, s)
local s = Statement(self.fn, self.line, '!BASE', self.base)
insert(new_statements, s)
end
end
2016-04-21 13:04:49 -07:00
elseif s.type == '!ALIGN' or s.type == '!SKIP' then
local length, content
if s.type == '!ALIGN' then
local align = s[1] and s[1].tok or 2
content = s[2] and s[2].tok or 0
if align < 0 then
2016-04-26 14:48:39 -07:00
self:error('negative alignment', align)
2016-04-21 13:04:49 -07:00
else
align = 2^align
end
local temp = self:pc() + align - 1
length = temp - (temp % align) - self:pc()
else
length = s[1] and s[1].tok or 0
content = s[2] and s[2].tok or nil
end
self.pos = self.pos + length
if content == nil then
local new = Statement(self.fn, self.line, '!ORG', self.pos)
insert(new_statements, new)
elseif length > 0 then
insert(new_statements, self:fill(length, content))
elseif length < 0 then
local new = Statement(self.fn, self.line, '!ORG', self.pos)
insert(new_statements, new)
insert(new_statements, self:fill(length, content))
local new = Statement(self.fn, self.line, '!ORG', self.pos)
insert(new_statements, new)
else
-- length is 0, noop
2016-01-13 07:56:18 -08:00
end
else
2016-04-21 13:04:49 -07:00
error('Internal Error: unknown statement, got '..s.type)
end
else
self.pos = self.pos + 4
insert(new_statements, s)
end
end
statements = new_statements
new_statements = {}
self.pos = 0
self.base = 0
for i=1, #statements do
local s = statements[i]
self.fn = s.fn
self.line = s.line
if s.type:sub(1, 1) ~= '!' then
local new = self:assemble(s)
self.pos = self.pos + 4
insert(new_statements, new)
elseif s.type == '!DATA' then
for i, t in ipairs(s) do
2016-04-26 14:48:39 -07:00
if t.tt == 'LABELSYM' then
2016-04-21 13:04:49 -07:00
local label = self.labels[t.tok]
if label == nil then
self:error('undefined label', t.tok)
end
t.tt = 'WORDS'
t.tok = {label}
2016-04-26 14:48:39 -07:00
elseif t.tt == 'NUM' then
t.tt = t.size..'S'
t.tok = {t.tok}
t.size = nil
2016-04-21 13:04:49 -07:00
end
end
self.pos = self.pos + (s.length or util.measure_data(s))
insert(new_statements, s)
elseif s.type == '!ORG' then
self.pos = s[1].tok
insert(new_statements, s)
elseif s.type == '!BASE' then
self.base = s[1].tok
elseif s.type == '!LABEL' then
-- noop
else
error('Internal Error: unknown statement, got '..s.type)
end
end
self.statements = new_statements
return self.statements
end
function Dumper:dump()
self.pos = 0
self.base = nil
for i, s in ipairs(self.statements) do
if s.type == '!DATA' then
for j, t in ipairs(s) do
if t.tt == 'WORDS' then
for _, w in ipairs(t.tok) do
local b0 = bitrange(w, 0, 7)
local b1 = bitrange(w, 8, 15)
local b2 = bitrange(w, 16, 23)
local b3 = bitrange(w, 24, 31)
self:write{b3, b2, b1, b0}
end
elseif t.tt == 'HALFWORDS' then
for _, h in ipairs(t.tok) do
local b0 = bitrange(h, 0, 7)
local b1 = bitrange(h, 8, 15)
self:write{b1, b0}
end
elseif t.tt == 'BYTES' then
for _, b in ipairs(t.tok) do
local b0 = bitrange(b, 0, 7)
self:write{b0}
end
else
error('Internal Error: unknown !DATA token')
end
2016-01-13 07:56:18 -08:00
end
2016-04-21 13:04:49 -07:00
elseif s.type == '!ORG' then
self.pos = s[1].tok
2016-01-13 07:56:18 -08:00
else
2016-04-21 13:04:49 -07:00
error('Internal Error: cannot dump unassembled statement')
2016-01-13 07:56:18 -08:00
end
end
end
return Dumper