1
0
Fork 0
mirror of https://github.com/notwa/lips synced 2024-11-14 09:59:03 -08:00

begin work on specials; big refactor

This commit is contained in:
Connor Olding 2016-01-15 11:15:02 -08:00
parent c0c4d81b5a
commit 195236a52c
9 changed files with 250 additions and 130 deletions

View file

@ -1,14 +0,0 @@
return function(inherit)
local class = {}
local mt_obj = {__index = class}
local mt_class = {
__call = function(self, ...)
local obj = setmetatable({}, mt_obj)
obj:init(...)
return obj
end,
__index = inherit,
}
return setmetatable(class, mt_class)
end

View file

@ -3,12 +3,11 @@ local format = string.format
local insert = table.insert
local data = require "lips.data"
local util = require "lips.util"
local function bitrange(x, lower, upper)
return floor(x/2^lower) % 2^(upper - lower + 1)
end
local bitrange = util.bitrange
local Dumper = require("lips.Class")()
local Dumper = util.Class()
function Dumper:init(writer, fn, options)
self.writer = writer
self.fn = fn or '(string)'
@ -131,6 +130,9 @@ end
function Dumper:desym(tok)
if type(tok[2]) == 'number' then
return tok[2]
elseif tok[1] == 'REG' then
assert(data.all_registers[tok[2]], 'Internal Error: unknown register')
return data.registers[tok[2]] or data.fpu_registers[tok[2]] or data.sys_registers[tok[2]]
elseif tok[1] == 'LABELSYM' then
local label = self.labels[tok[2]]
if label == nil then
@ -154,53 +156,40 @@ function Dumper:desym(tok)
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]
assert(type(tok) == 'table', 'Internal Error: invalid value')
assert(#tok == 2, 'Internal Error: invalid token')
local val = self:desym(tok)
if tok.index then
val = val % 0x80000000
val = floor(val/4)
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
if tok.negate then
val = -val
end
self:error('invalid value') -- internal error?
if tok.negate or tok.signed then
if val >= 0x10000 or val < -0x8000 then
self:error('value out of range')
end
val = val % 0x10000
end
if tok.portion == 'upper' then
val = bitrange(val, 16, 31)
elseif tok.portion == 'lower' then
val = bitrange(val, 0, 15)
elseif tok.portion == 'upperoff' then
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
val = upper
end
return val
end
function Dumper:validate(n, bits)
@ -249,7 +238,7 @@ function Dumper:dump_instruction(t)
lw = lw + self:valvar(t[5], 5)*0x40
lw = lw + self:valvar(t[6], 6)
else
error('Internal Error: unknown n-size', 1)
error('Internal Error: unknown n-size')
end
return uw, lw

View file

@ -3,9 +3,9 @@ local char = string.char
local find = string.find
local format = string.format
local insert = table.insert
local open = io.open
local data = require "lips.data"
local util = require "lips.util"
local simple_escapes = {
['0'] = 0x00,
@ -20,17 +20,7 @@ local simple_escapes = {
['v'] = 0x0B,
}
local function readfile(fn)
local f = open(fn, 'r')
if not f then
error('could not open assembly file for reading: '..tostring(fn), 2)
end
local asm = f:read('*a')
f:close()
return asm
end
local Lexer = require("lips.Class")()
local Lexer = util.Class()
function Lexer:init(asm, fn, options)
self.asm = asm
self.fn = fn or '(string)'
@ -278,7 +268,7 @@ function Lexer:lex_include(_yield)
if self.options.path then
fn = self.options.path..fn
end
local sublexer = Lexer(readfile(fn), fn, self.options)
local sublexer = Lexer(util.readfile(fn), fn, self.options)
sublexer:lex(_yield)
end
@ -348,6 +338,10 @@ function Lexer:lex(_yield)
self:nextc()
local buff = self:read_chars('[%w_]')
yield('DEFSYM', buff)
elseif self.chr == '%' then
self:nextc()
local call = self:read_chars('[%w_]')
yield('SPECIAL', call)
elseif self.chr:find('[%a_]') then
local buff = self:read_chars('[%w_.]')
local up = buff:upper()

View file

@ -1,7 +1,19 @@
local format = string.format
local insert = table.insert
local data = require "lips.data"
local Muncher = require("lips.Class")()
local util = require "lips.util"
local arg_types = {
NUM = true,
REG = true,
DEFSYM = true,
LABELSYM = true,
RELLABELSYM = true,
}
local Muncher = util.Class()
-- no base init method
function Muncher:error(msg)
error(format('%s:%d: Error: %s', self.fn, self.line, msg), 2)
@ -99,4 +111,36 @@ function Muncher:const(relative, no_label)
return t
end
function Muncher:special()
if self.tt ~= 'SPECIAL' then
self:error('expected special name to call')
end
local name = self.tok
self:advance()
if self.tt ~= 'OPEN' then
self:error('expected opening parenthesis for special call')
end
local args = {}
while true do
local arg = self:advance()
if not arg_types[arg.tt] then
self:error('invalid argument type')
else
self:advance()
end
if self.tt == 'SEP' then
insert(args, arg)
elseif self.tt == 'CLOSE' then
insert(args, arg)
self:advance()
break
else
self:error('unexpected token in argument list')
end
end
return name, args
end
return Muncher

View file

@ -1,13 +1,17 @@
local insert = table.insert
local data = require "lips.data"
local util = require "lips.util"
local overrides = require "lips.overrides"
local Lexer = require "lips.Lexer"
local Dumper = require "lips.Dumper"
local Muncher = require "lips.Muncher"
local Preproc = require "lips.Preproc"
local Parser = require("lips.Class")(Muncher)
local construct = util.construct
local withflag = util.withflag
local Parser = util.Class(Muncher)
function Parser:init(writer, fn, options)
self.fn = fn or '(string)'
self.main_fn = self.fn
@ -96,21 +100,21 @@ function Parser:format_in(informat)
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 = {'SIGNED', self:const()}
args.offset = withflag(self:const(), 'signed')
elseif c == 'r' and not args.offset then
args.offset = {'SIGNED', self:const('relative')}
args.offset = withflag(self:const('relative'), '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 = {'INDEX', self:const()}
args.index = withflag(self:const(), 'index')
elseif c == 'k' and not args.immediate then
args.immediate = {'NEGATE', self:const(nil, 'no label')}
args.immediate = withflag(self:const(nil, 'no label'), 'negate')
elseif c == 'K' and not args.immediate then
args.immediate = {'SIGNED', self:const(nil, 'no label')}
args.immediate = withflag(self:const(nil, 'no label'), 'signed')
elseif c == 'b' and not args.base then
args.base = self:deref()
else
error('Internal Error: invalid input formatting string', 1)
error('Internal Error: invalid input formatting string')
end
if c2:find('[dstDSTorIikKXYZ]') then
self:optional_comma()
@ -129,36 +133,36 @@ function Parser:format_out_raw(outformat, first, args, const, formatconst)
for i=1,#outformat do
local c = outformat:sub(i, i)
if c == 'd' then
out[#out+1] = args.rd
out[#out+1] = construct(args.rd)
elseif c == 's' then
out[#out+1] = args.rs
out[#out+1] = construct(args.rs)
elseif c == 't' then
out[#out+1] = args.rt
out[#out+1] = construct(args.rt)
elseif c == 'D' then
out[#out+1] = args.fd
out[#out+1] = construct(args.fd)
elseif c == 'S' then
out[#out+1] = args.fs
out[#out+1] = construct(args.fs)
elseif c == 'T' then
out[#out+1] = args.ft
out[#out+1] = construct(args.ft)
elseif c == 'o' then
out[#out+1] = args.offset
out[#out+1] = construct(args.offset)
elseif c == 'i' then
out[#out+1] = args.immediate
out[#out+1] = construct(args.immediate)
elseif c == 'I' then
out[#out+1] = args.index
out[#out+1] = construct(args.index)
elseif c == 'b' then
out[#out+1] = args.base
out[#out+1] = construct(args.base)
elseif c == '0' then
out[#out+1] = 0
out[#out+1] = construct(0)
elseif c == 'C' then
out[#out+1] = const
out[#out+1] = construct(const)
elseif c == 'F' then
out[#out+1] = formatconst
out[#out+1] = construct(formatconst)
end
end
local f = lookup[#outformat]
if f == nil then
error('Internal Error: invalid output formatting string', 1)
error('Internal Error: invalid output formatting string')
end
f(self.dumper, self.fn, self.line, first, out[1], out[2], out[3], out[4], out[5])
end
@ -183,15 +187,15 @@ function Parser:instruction()
args.rt = self:register()
self:optional_comma()
if self.tt == 'OPEN' then
args.offset = {'NUM', 0}
args.offset = 0
args.base = self:deref()
else -- NUM or LABELSYM
local lui_args = {}
local addu_args = {}
local o = self:const()
args.offset = {'LOWER', o}
args.offset = withflag({o[1], o[2]}, 'portion', 'lower')
if o[1] == 'LABELSYM' or o[2] >= 0x80000000 then
lui_args.immediate = {'UPPEROFF', o}
lui_args.immediate = withflag({o[1], o[2]}, 'portion', 'upperoff')
lui_args.rt = 'AT'
self:format_out(lui, lui_args)
if not self:is_EOL() then
@ -246,6 +250,8 @@ function Parser:tokenize(asm)
local preproc = Preproc(self.options)
self.tokens = preproc:process(tokens)
assert(#self.tokens > 0, 'Internal Error: no tokens after preprocessing')
end
function Parser:parse(asm)

View file

@ -1,8 +1,10 @@
local insert = table.insert
local data = require "lips.data"
local util = require "lips.util"
local Muncher = require "lips.Muncher"
local Preproc = require("lips.Class")(Muncher)
local Preproc = util.Class(Muncher)
function Preproc:init(options)
self.options = options or {}
end
@ -90,6 +92,47 @@ function Preproc:process(tokens)
end
end
self.tokens = new_tokens
new_tokens = {}
-- third pass: resolve specials
self.i = 0
while self.i < #self.tokens do
local t = self:advance()
self.fn = t.fn
self.line = t.line
if t.tt == 'SPECIAL' then
local name, args = self:special()
-- TODO: split to its own file, not unlike overrides.lua
if name == 'hi' then
if #args ~= 1 then
self:error('%hi expected exactly one argument')
end
--local tnew = {fn=t.fn, line=t.line, tt='UPPEROFF', tok=args[1]}
self:error('unimplemented special')
insert(new_tokens, tnew)
elseif name == 'up' then
if #args ~= 1 then
self:error('%up expected exactly one argument')
end
--local tnew = {fn=t.fn, line=t.line, tt='UPPER', tok=args[1]}
self:error('unimplemented special')
insert(new_tokens, tnew)
elseif name == 'lo' then
if #args ~= 1 then
self:error('%lo expected exactly one argument')
end
self:error('unimplemented special')
--local tnew = {fn=t.fn, line=t.line, tt='LOWER', tok=args[1]}
insert(new_tokens, tnew)
else
self:error('unknown special')
end
else
insert(new_tokens, t)
end
end
self.tokens = new_tokens
return self.tokens

View file

@ -10,20 +10,9 @@ local lips = {
]],
}
local open = io.open
local util = require "lips.util"
local Parser = require "lips.Parser"
local function readfile(fn)
local f = open(fn, 'r')
if not f then
error('could not open assembly file for reading: '..tostring(fn), 2)
end
local asm = f:read('*a')
f:close()
return asm
end
function lips.word_writer()
local buff = {}
local max = -1
@ -61,7 +50,7 @@ function lips.assemble(fn_or_asm, writer, options)
asm = fn_or_asm
else
fn = fn_or_asm
asm = readfile(fn)
asm = util.readfile(fn)
options.path = fn:match(".*/")
end

View file

@ -1,7 +1,10 @@
local insert = table.insert
local data = require "lips.data"
local util = require "lips.util"
local instructions = data.instructions
local withflag = util.withflag
local overrides = {}
-- note: "self" is an instance of Parser
@ -24,19 +27,19 @@ function overrides.LI(self, name)
im[2] = im[2] % 0x100000000
if im[2] >= 0x10000 and im[2] <= 0xFFFF8000 then
args.rs = args.rt
args.immediate = {'UPPER', im}
args.immediate = withflag(im, 'portion', 'upper')
self:format_out(lui, args)
if im[2] % 0x10000 ~= 0 then
args.immediate = {'LOWER', im}
args.immediate = withflag(im, 'portion', 'lower')
self:format_out(ori, args)
end
elseif im[2] >= 0x8000 and im[2] < 0x10000 then
args.rs = 'R0'
args.immediate = {'LOWER', im}
args.immediate = withflag(im, 'portion', 'lower')
self:format_out(ori, args)
else
args.rs = 'R0'
args.immediate = {'LOWER', im}
args.immediate = withflag(im, 'portion', 'lower')
self:format_out(addiu, args)
end
end
@ -50,9 +53,9 @@ function overrides.LA(self, name)
local im = self:const()
args.rs = args.rt
args.immediate = {'UPPEROFF', im}
args.immediate = withflag(im, 'portion', 'upperoff')
self:format_out(lui, args)
args.immediate = {'LOWER', im}
args.immediate = withflag(im, 'portion', 'lower')
self:format_out(addiu, args)
end
@ -84,14 +87,14 @@ function overrides.PUSH(self, name)
if name == 'PUSH' then
args.rt = 'SP'
args.rs = 'SP'
args.immediate = {'NEGATE', {'NUM', #stack*4}}
args.immediate = withflag(#stack*4, 'negate')
self:format_out(addi, args)
end
args.base = 'SP'
for i, r in ipairs(stack) do
args.rt = r
if r ~= '' then
args.offset = {'NUM', (i - 1)*4}
args.offset = (i - 1)*4
self:format_out(w, args)
end
end
@ -102,7 +105,7 @@ function overrides.PUSH(self, name)
if name == 'POP' or name == 'JPOP' then
args.rt = 'SP'
args.rs = 'SP'
args.immediate = {'NUM', #stack*4}
args.immediate = #stack*4
self:format_out(addi, args)
end
end
@ -175,7 +178,7 @@ function overrides.ROL(self, name)
end
self:format_out(sll, args)
args.rd = 'AT'
args.immediate = {'NUM', 32 - args.immediate[2]}
args.immediate = 32 - args.immediate[2]
self:format_out(srl, args)
args.rd = left
args.rs = left
@ -202,7 +205,7 @@ function overrides.ROR(self, name)
end
self:format_out(srl, args)
args.rd = 'AT'
args.immediate = {'NUM', 32 - args.immediate[2]}
args.immediate = 32 - args.immediate[2]
self:format_out(sll, args)
args.rd = right
args.rs = right
@ -238,7 +241,7 @@ function overrides.BEQI(self, name)
self:optional_comma()
args.immediate = self:const()
self:optional_comma()
args.offset = {'SIGNED', self:const('relative')}
args.offset = withflag(self:const('relative'), 'signed')
if reg == 'AT' then
self:error('register cannot be AT in this pseudo-instruction')
@ -261,7 +264,7 @@ function overrides.BLTI(self, name)
self:optional_comma()
args.immediate = self:const()
self:optional_comma()
args.offset = {'SIGNED', self:const('relative')}
args.offset = withflag(self:const('relative'), 'signed')
if args.rs == 'AT' then
self:error('register cannot be AT in this pseudo-instruction')
@ -287,7 +290,7 @@ function overrides.BLEI(self, name)
self:optional_comma()
args.immediate = self:const()
self:optional_comma()
local offset = {'SIGNED', self:const('relative')}
local offset = withflag(self:const('relative'), 'signed')
if reg == 'AT' then
self:error('register cannot be AT in this pseudo-instruction')

66
lips/util.lua Normal file
View file

@ -0,0 +1,66 @@
local floor = math.floor
local open = io.open
local function Class(inherit)
local class = {}
local mt_obj = {__index = class}
local mt_class = {
__call = function(self, ...)
local obj = setmetatable({}, mt_obj)
obj:init(...)
return obj
end,
__index = inherit,
}
return setmetatable(class, mt_class)
end
local function construct(t)
if type(t) == 'table' then
return t
elseif type(t) == 'string' then
return {'REG', t}
elseif type(t) == 'number' then
return {'NUM', t}
else
error('Internal Error: unknown type to construct')
end
end
local function withflag(t, key, value)
if type(t) == 'table' then
t = {t[1], t[2]}
else
t = construct(t)
end
if value == nil then
value = true
end
if key ~= nil then
t[key] = value
end
return t
end
local function readfile(fn)
local f = open(fn, 'r')
if not f then
error('could not open assembly file for reading: '..tostring(fn), 2)
end
local asm = f:read('*a')
f:close()
return asm
end
local function bitrange(x, lower, upper)
return floor(x/2^lower) % 2^(upper - lower + 1)
end
return {
Class = Class,
construct = construct,
withflag = withflag,
readfile = readfile,
bitrange = bitrange,
}