1
0
Fork 0
mirror of https://github.com/notwa/lips synced 2024-11-14 17:39:03 -08:00
lips/Dumper.lua

278 lines
7.5 KiB
Lua
Raw Normal View History

local insert = table.insert
local floor = math.floor
local data = require "data"
local function bitrange(x, lower, upper)
return floor(x/2^lower) % 2^(upper - lower + 1)
end
local Dumper = require("Class")()
function Dumper:init(writer, fn, options)
self.writer = writer
self.fn = fn or '(string)'
self.options = options or {}
self.labels = {}
self.commands = {}
self.pos = options.offset or 0
self.lastcommand = nil
end
function Dumper:error(msg)
error(format('%s:%d: Error: %s', self.fn, self.line, msg), 2)
end
function Dumper:advance(by)
self.pos = self.pos + by
end
function Dumper:push_instruction(t)
t.kind = 'instruction'
insert(self.commands, t)
self:advance(4)
end
function Dumper:add_instruction_j(line, o, T)
self:push_instruction{line=line, o, T}
end
function Dumper:add_instruction_i(line, o, s, t, i)
self:push_instruction{line=line, o, s, t, i}
end
function Dumper:add_instruction_r(line, o, s, t, d, f, c)
self:push_instruction{line=line, o, s, t, d, f, c}
end
function Dumper:add_label(name)
self.labels[name] = self.pos
end
function Dumper:add_bytes(line, ...)
local use_last = self.lastcommand and self.lastcommand.kind == 'bytes'
local t
if use_last then
t = self.lastcommand
else
t = {}
t.kind = 'bytes'
t.size = 0
end
t.line = line
for _, b in ipairs{...} do
t.size = t.size + 1
t[t.size] = b
end
if not use_last then
insert(self.commands, t)
end
self:advance(t.size)
end
function Dumper:add_directive(line, name, a, b)
local t = {}
t.line = line
if name == 'BYTE' then
self:add_bytes(line, a % 0x100)
elseif name == 'HALFWORD' then
local b0 = bitrange(a, 0, 7)
local b1 = bitrange(a, 8, 15)
self:add_bytes(line, b1, b0)
elseif name == 'WORD' then
local b0 = bitrange(a, 0, 7)
local b1 = bitrange(a, 8, 15)
local b2 = bitrange(a, 16, 23)
local b3 = bitrange(a, 24, 31)
self:add_bytes(line, b3, b2, b1, b0)
elseif name == 'ORG' then
t.kind = 'goto'
t.addr = a
insert(self.commands, t)
self.pos = a % 0x80000000
self:advance(0)
elseif name == 'ALIGN' then
t.kind = 'ahead'
local align = a*2
if align == 0 then
align = 4
elseif align < 0 then
self:error('negative alignment')
end
local temp = self.pos + align - 1
t.skip = temp - (temp % align) - self.pos
t.fill = t.fill or 0
insert(self.commands, t)
self:advance(t.skip)
elseif name == 'SKIP' then
t.kind = 'ahead'
t.skip = a
t.fill = b
insert(self.commands, t)
self:advance(t.skip)
else
self:error('unimplemented directive')
end
end
function Dumper:desym(tok)
-- FIXME: errors can give wrong filename, also off by one
if type(tok[2]) == 'number' then
return tok[2]
elseif tok[1] == 'LABELSYM' then
local label = self.labels[tok[2]]
if label == nil then
self:error('undefined label')
end
return label
elseif tok[1] == 'LABELREL' then
local label = self.labels[tok[2]]
if label == nil then
self:error('undefined label')
end
label = label % 0x80000000
local pos = self.pos % 0x80000000
local rel = floor(label/4) - 1 - floor(pos/4)
if rel > 0x8000 or rel <= -0x8000 then
self:error('branch too far')
end
return rel % 0x10000
end
self:error('failed to desym') -- internal error?
end
function Dumper:toval(tok)
if tok == nil then
self:error('nil value')
elseif type(tok) == 'number' then
return tok
elseif data.all_registers[tok] then
return data.registers[tok] or data.fpu_registers[tok] or data.sys_registers[tok]
end
if type(tok) == 'table' then
if #tok ~= 2 then
self:error('invalid token')
end
if tok[1] == 'UPPER' then
local val = self:desym(tok[2])
return bitrange(val, 16, 31)
elseif tok[1] == 'LOWER' then
local val = self:desym(tok[2])
return bitrange(val, 0, 15)
elseif tok[1] == 'UPPEROFF' then
local val = self:desym(tok[2])
local upper = bitrange(val, 16, 31)
local lower = bitrange(val, 0, 15)
if lower >= 0x8000 then
-- accommodate for offsets being signed
upper = (upper + 1) % 0x10000
end
return upper
elseif tok[1] == 'SIGNED' then
local val = self:desym(tok[2])
if val >= 0x10000 or val < -0x8000 then
self:error('value out of range')
end
return val % 0x10000
elseif tok[1] == 'NEGATE' then
local val = -self:desym(tok[2])
if val >= 0x10000 or val < -0x8000 then
self:error('value out of range')
end
return val % 0x10000
elseif tok[1] == 'INDEX' then
local val = self:desym(tok[2]) % 0x80000000
val = floor(val/4)
return val
else
return self:desym(tok)
end
end
self:error('invalid value') -- internal error?
end
function Dumper:validate(n, bits)
local max = 2^bits
if n == nil then
self:error('value is nil') -- internal error?
end
if n > max or n < 0 then
self:error('value out of range')
end
end
function Dumper:valvar(tok, bits)
local val = self:toval(tok)
self:validate(val, bits)
return val
end
function Dumper:write(t)
for _, b in ipairs(t) do
local s = ('%02X'):format(b)
self.writer(self.pos, s)
self.pos = self.pos + 1
end
end
function Dumper:dump_instruction(t)
local uw = 0
local lw = 0
local o = t[1]
uw = uw + o*0x400
if #t == 2 then
local val = self:valvar(t[2], 26)
uw = uw + bitrange(val, 16, 25)
lw = lw + bitrange(val, 0, 15)
elseif #t == 4 then
uw = uw + self:valvar(t[2], 5)*0x20
uw = uw + self:valvar(t[3], 5)
lw = lw + self:valvar(t[4], 16)
elseif #t == 6 then
uw = uw + self:valvar(t[2], 5)*0x20
uw = uw + self:valvar(t[3], 5)
lw = lw + self:valvar(t[4], 5)*0x800
lw = lw + self:valvar(t[5], 5)*0x40
lw = lw + self:valvar(t[6], 6)
else
error('Internal Error: unknown n-size', 1)
end
return uw, lw
end
function Dumper:dump()
self.pos = self.options.offset or 0
for i, t in ipairs(self.commands) do
if t.line == nil then
error('Internal Error: no line number available')
end
self.line = t.line
if t.kind == 'instruction' then
local uw, lw = self:dump_instruction(t)
local b0 = bitrange(lw, 0, 7)
local b1 = bitrange(lw, 8, 15)
local b2 = bitrange(uw, 0, 7)
local b3 = bitrange(uw, 8, 15)
self:write{b3, b2, b1, b0}
elseif t.kind == 'bytes' then
self:write(t)
elseif t.kind == 'goto' then
self.pos = t.addr
elseif t.kind == 'ahead' then
if t.fill then
for i=1, t.skip do
self:write{t.fill}
end
else
self.pos = self.pos + t.skip
end
else
error('Internal Error: unknown command', 1)
end
end
end
return Dumper