From 195236a52caaea24944603d41df58e2a244d6b71 Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Fri, 15 Jan 2016 11:15:02 -0800 Subject: [PATCH] begin work on specials; big refactor --- lips/Class.lua | 14 -------- lips/Dumper.lua | 89 ++++++++++++++++++++-------------------------- lips/Lexer.lua | 20 ++++------- lips/Muncher.lua | 46 +++++++++++++++++++++++- lips/Parser.lua | 54 +++++++++++++++------------- lips/Preproc.lua | 45 ++++++++++++++++++++++- lips/init.lua | 15 ++------ lips/overrides.lua | 31 ++++++++-------- lips/util.lua | 66 ++++++++++++++++++++++++++++++++++ 9 files changed, 250 insertions(+), 130 deletions(-) create mode 100644 lips/util.lua diff --git a/lips/Class.lua b/lips/Class.lua index f03bef3..e69de29 100644 --- a/lips/Class.lua +++ b/lips/Class.lua @@ -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 diff --git a/lips/Dumper.lua b/lips/Dumper.lua index 6d90537..337fa53 100644 --- a/lips/Dumper.lua +++ b/lips/Dumper.lua @@ -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 diff --git a/lips/Lexer.lua b/lips/Lexer.lua index a834703..33f6ebd 100644 --- a/lips/Lexer.lua +++ b/lips/Lexer.lua @@ -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() diff --git a/lips/Muncher.lua b/lips/Muncher.lua index fbf774d..8266d78 100644 --- a/lips/Muncher.lua +++ b/lips/Muncher.lua @@ -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 diff --git a/lips/Parser.lua b/lips/Parser.lua index 26fcfc9..206872f 100644 --- a/lips/Parser.lua +++ b/lips/Parser.lua @@ -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) diff --git a/lips/Preproc.lua b/lips/Preproc.lua index f870459..58e3528 100644 --- a/lips/Preproc.lua +++ b/lips/Preproc.lua @@ -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 diff --git a/lips/init.lua b/lips/init.lua index d0549f2..76768bb 100644 --- a/lips/init.lua +++ b/lips/init.lua @@ -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 diff --git a/lips/overrides.lua b/lips/overrides.lua index dfbce6e..0d1c17a 100644 --- a/lips/overrides.lua +++ b/lips/overrides.lua @@ -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') diff --git a/lips/util.lua b/lips/util.lua new file mode 100644 index 0000000..bc1af31 --- /dev/null +++ b/lips/util.lua @@ -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, +}