diff --git a/lips/Dumper.lua b/lips/Dumper.lua index 9aa4d3f..dd4c5c8 100644 --- a/lips/Dumper.lua +++ b/lips/Dumper.lua @@ -6,14 +6,15 @@ local unpack = unpack or table.unpack local path = string.gsub(..., "[^.]+$", "") local data = require(path.."data") local util = require(path.."util") -local overrides = require(path.."overrides") +--local overrides = require(path.."overrides") local Base = require(path.."Base") local Token = require(path.."Token") local Statement = require(path.."Statement") +local Reader = require(path.."Reader") local bitrange = util.bitrange -local Dumper = Base:extend() +local Dumper = Reader:extend() function Dumper:init(writer, options) self.writer = writer self.options = options or {} @@ -23,13 +24,6 @@ function Dumper:init(writer, options) self.lastcommand = nil end -function Dumper:error(msg, got) - if got ~= nil then - msg = msg..', got '..tostring(got) - end - error(('%s:%d: Error: %s'):format(self.fn, self.line, msg), 2) -end - 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 @@ -225,62 +219,6 @@ function Dumper:dump_instruction(t) return uw, lw end -function Dumper:expect(tts) - local t = self.s[self.i] - if t == nil then - self:error("expected another argument") -- TODO: more verbose - end - - self.fn = t.fn - self.line = t.line - - for _, tt in pairs(tts) do - if t.tt == tt then - return t.ok - end - end - - --local err = ("argument %i of %s expected type %s"):format(self.i, self.s.type, tt) - local err = ("unexpected type for argument %i of %s"):format(self.i, self.s.type) - self:error(err, t.tt) -end - -function Dumper:register(registers) - self:expect{'REG'} - local t = self.s[self.i] - local numeric = registers[t.tok] - if not numeric then - self:error('wrong type of register') - end - return Token(t) -end - -function Dumper:const(relative, no_label) - if no_label then - self:expect{'NUM'} - else - self:expect{'NUM', 'LABELSYM'} - end - local t = self.s[self.i] - local new = Token(t) - if relative then - if t.tt == 'LABELSYM' then - new.t = 'LABELREL' - else - new.t = 'REL' - end - end - return new -end - -function Dumper:deref() - self:expect{'DEREF'} - local t = self.s[self.i] - local new = Token(t) - new.tt = 'REG' - return new -end - function Dumper:assemble_j(first, out) local w = 0 w = w + self:valvar(first, 6) * 0x04000000 @@ -318,7 +256,7 @@ function Dumper:format_in(informat) local args = {} --if #informat ~= #s then error('mismatch') end for i=1, #informat do - self.i = i -- FIXME: do we need this? + self.i = i local c = informat:sub(i, i) if c == 'd' and not args.rd then args.rd = self:register(data.registers) @@ -351,7 +289,7 @@ function Dumper:format_in(informat) elseif c == 'K' and not args.immediate then args.immediate = Token(self:const(nil, 'no label')):set('signed') elseif c == 'b' and not args.base then - args.base = self:deref() + args.base = self:deref():set('tt', 'REG') else error('Internal Error: invalid input formatting string') end @@ -397,14 +335,15 @@ function Dumper:assemble(s) local name = s.type local h = data.instructions[name] self.s = s - if overrides[name] then - --overrides[name](self, name) - local s = Statement(self.fn, self.line, '!DATA') -- FIXME: dummy - return s - elseif h[2] == 'tob' then -- TODO: or h[2] == 'Tob' then - local s = Statement(self.fn, self.line, '!DATA') -- FIXME: dummy - return s - elseif h[2] ~= nil then +-- if overrides[name] then +-- --overrides[name](self, name) +-- local s = Statement(self.fn, self.line, '!DATA') -- FIXME: dummy +-- return s +-- elseif h[2] == 'tob' then -- TODO: or h[2] == 'Tob' then +-- local s = Statement(self.fn, self.line, '!DATA') -- FIXME: dummy +-- return s +-- elseif h[2] ~= nil then + if h[2] ~= nil then local args = self:format_in(h[2]) return self:format_out(h, args) else @@ -446,10 +385,12 @@ function Dumper:load(statements) if s.type:sub(1, 1) ~= '!' then s = self:assemble(s) insert(new_statements, s) + self.pos = self.pos + 4 -- FIXME: assumes no pseudo-ops elseif assembled_directives[s.type] then -- FIXME: check for LABELs in !DATA -- TODO: reimplement ALIGN and SKIP here insert(new_statements, s) + self.pos = self.pos + s:length() elseif s.type == '!LABEL' then -- noop else diff --git a/lips/Parser.lua b/lips/Parser.lua index 348bae4..9264a30 100644 --- a/lips/Parser.lua +++ b/lips/Parser.lua @@ -103,12 +103,7 @@ function Parser:parse(asm) local preproc = Preproc(self.options) self.statements = preproc:process(self.statements) - - --[[ process: -- inline labels? how do you know how far they are? - i guess you can just offset on statements instead -- assemble? dumper gets passed .org .base ---]] + self.statements = preproc:expand(self.statements) -- DEBUG for i, s in ipairs(self.statements) do diff --git a/lips/Preproc.lua b/lips/Preproc.lua index e61500b..9bc5805 100644 --- a/lips/Preproc.lua +++ b/lips/Preproc.lua @@ -2,9 +2,11 @@ local insert = table.insert local path = string.gsub(..., "[^.]+$", "") local data = require(path.."data") +local overrides = require(path.."overrides") local Base = require(path.."Base") local Token = require(path.."Token") local Statement = require(path.."Statement") +local Reader = require(path.."Reader") local abs = math.abs @@ -37,18 +39,11 @@ local function RelativeLabel(index, name) } end -local Preproc = Base:extend() +local Preproc = Reader:extend() function Preproc:init(options) self.options = options or {} end -function Preproc:error(msg, got) - if got ~= nil then - msg = msg..', got '..tostring(got) - end - error(('%s:%d: Error: %s'):format(self.fn, self.line, msg), 2) -end - function Preproc:lookup(t) if t.tt == 'VARSYM' then local name = t.tok @@ -110,6 +105,8 @@ function Preproc:lookup(t) end function Preproc:check(s, i, tt) + s = s or self.s + i = i or self.i local t = s[i] if t == nil then self:error("expected another argument") @@ -187,7 +184,77 @@ function Preproc:process(statements) end end - self.statements = new_statements + return new_statements +end + +function Preproc:statement(...) + self.fn = self.s.fn + self.line = self.s.line + local s = Statement(self.fn, self.line, ...) + return s +end + +function Preproc:push(s) + s:validate() + insert(self.statements, s) +end + +function Preproc:push_new(...) + self:push(self:statement(...)) +end + +function Preproc:pop(kind) + local ret + if kind == nil then + -- noop + elseif kind == 'CPU' then + ret = self:register(data.registers) + elseif kind == 'DEREF' then + ret = self:deref() + elseif kind == 'CONST' then + ret = self:const() + elseif kind == 'REL' then + ret = self:const('REL') + elseif kind == 'END' then + if self.s[self.i + 1] ~= nil then + self:error('too many arguments') + end + return -- don't increment self.i past end of arguments + else + error('Internal Error: unknown kind, got '..tostring(kind)) + end + self.i = self.i + 1 + return ret +end + +function Preproc:expand(statements) + -- third pass: expand pseudo-instructions + self.statements = {} + for i=1, #statements do + local s = statements[i] + self.s = s + self.fn = s.fn + self.line = s.line + if s.type:sub(1, 1) == '!' then + -- TODO + self:push(s) + else + local name = s.type + local h = data.instructions[name] + if h == nil then + error('Internal Error: unknown instruction') + end + + if overrides[name] then + self.i = 1 + overrides[name](self, name) + self:pop('END') + else + self:push(s) + end + end + end + return self.statements end diff --git a/lips/Reader.lua b/lips/Reader.lua new file mode 100644 index 0000000..83b0102 --- /dev/null +++ b/lips/Reader.lua @@ -0,0 +1,93 @@ +local path = string.gsub(..., "[^.]+$", "") +local Base = require(path.."Base") +local Token = require(path.."Token") + +local Reader = Base:extend() +-- no init method + +-- Reader expects self.s to be set to a statement, and self.i to a token index + +function Reader:error(msg, got) + if got ~= nil then + msg = msg..', got '..tostring(got) + end + error(('%s:%d: Error: %s'):format(self.fn, self.line, msg), 2) +end + +function Reader:token(t, ...) + local new + if type(t) == 'table' then + new = Token(t, ...) + else + new = Token(self.fn, self.line, t, ...) + end + return new +end + +function Reader:expect(tts) + local t = self.s[self.i] + if t == nil then + self:error("expected another argument") -- TODO: more verbose + end + + self.fn = t.fn + self.line = t.line + + for _, tt in pairs(tts) do + if t.tt == tt then + return t.ok + end + end + + --local err = ("argument %i of %s expected type %s"):format(self.i, self.s.type, tt) + local err = ("unexpected type for argument %i of %s"):format(self.i, self.s.type) + self:error(err, t.tt) +end + +function Reader:register(registers) + self:expect{'REG'} + local t = self.s[self.i] + local numeric = registers[t.tok] + if not numeric then + self:error('wrong type of register') + end + local new = Token(t) + return new +end + +function Reader:const(relative, no_label) + if no_label then + self:expect{'NUM'} + else + self:expect{'NUM', 'LABELSYM'} + end + local t = self.s[self.i] + local new = Token(t) + if relative then + if t.tt == 'LABELSYM' then + new.t = 'LABELREL' + else + new.t = 'REL' + end + end + return new +end + +function Reader:deref() + self:expect{'DEREF'} + local t = self.s[self.i] + local new = Token(t) + --new.tt = 'REG' + return new +end + +function Reader:peek(tt) + local t = self.s[self.i] + local seen = t and t.tt or nil + if tt ~= nil then + return seen == tt + end + return t +end + +return Reader diff --git a/lips/Statement.lua b/lips/Statement.lua index 5554a81..7426616 100644 --- a/lips/Statement.lua +++ b/lips/Statement.lua @@ -45,7 +45,8 @@ function Statement:validate(n) end for i, v in ipairs(self) do if util.parent(v) ~= Token then - error(('Internal Error: Statement[%i] is not a Token'):format(i), n) + self[i] = Token(self.fn, self.line, v) + --error(('Internal Error: Statement[%i] is not a Token'):format(i), n) end end end diff --git a/lips/overrides.lua b/lips/overrides.lua index f02b0eb..838104d 100644 --- a/lips/overrides.lua +++ b/lips/overrides.lua @@ -3,19 +3,52 @@ local insert = table.insert local path = string.gsub(..., "[^.]+$", "") local data = require(path.."data") -local instructions = data.instructions - local overrides = {} --- note: "self" is an instance of Parser +-- note: "self" is an instance of Preproc + +local function tob_override(self, name) + -- handle all the addressing modes for lw/sw-like instructions + local rt = self:pop('CPU') + local offset, base + if self:peek('DEREF') then + offset = 0 + base = self:pop('DEREF') + else -- NUM or LABELSYM + local o = self:pop('CONST') + if self:peek('NUM') then + o:set('offset', self:pop('CONST').tok) + end + offset = self:token(o) + if not o.portion then + offset:set('portion', 'lower') + end + -- attempt to use the fewest possible instructions for this offset + if not o.portion and (o.tt == 'LABELSYM' or o.tok >= 0x80000000) then + local immediate = self:token(o):set('portion', 'upperoff') + self:push_new('LUI', 'AT', immediate) + if self.s[self.i] ~= nil then + local reg = self:pop('DEREF'):set('tt', 'REG') + if reg.tok ~= 'R0' then + self:push_new('ADDU', 'AT', 'AT', 'R0') + end + end + base = self:token('DEREF', 'AT') + else + base = self:pop('DEREF') + end + end + self:push_new(name, rt, offset, base) +end + +for k, v in pairs(data.instructions) do + if v[2] == 'tob' then + overrides[k] = tob_override + end +end function overrides.LI(self, name) - local lui = instructions['LUI'] - local ori = instructions['ORI'] - local addiu = instructions['ADDIU'] - local args = {} - args.rt = self:register() - self:optional_comma() - local im = self:const() + local rt = self:pop('CPU') + local im = self:pop('CONST') -- for us, this is just semantics. for a "real" assembler, -- LA could add appropriate RELO LUI/ADDIU directives. @@ -24,272 +57,180 @@ function overrides.LI(self, name) end if im.portion then - args.rs = 'R0' - args.immediate = im - self:format_out(addiu, args) + -- FIXME: use appropriate instruction based on portion? + self:push_new('ADDIU', rt, 'R0', im) return end im.tok = im.tok % 0x100000000 if im.tok >= 0x10000 and im.tok <= 0xFFFF8000 then - args.rs = args.rt - args.immediate = self:token(im):set('portion', 'upper') - self:format_out(lui, args) + local rs = rt + local immediate = self:token(im):set('portion', 'upper') + self:push_new('LUI', rt, immediate) if im.tok % 0x10000 ~= 0 then - args.immediate = self:token(im):set('portion', 'lower') - self:format_out(ori, args) + local immediate = self:token(im):set('portion', 'lower') + self:push_new('ORI', rt, rs, immediate) end elseif im.tok >= 0x8000 and im.tok < 0x10000 then - args.rs = 'R0' - args.immediate = self:token(im):set('portion', 'lower') - self:format_out(ori, args) + local immediate = self:token(im):set('portion', 'lower') + self:push_new('ORI', rt, 'R0', immediate) else - args.rs = 'R0' - args.immediate = self:token(im):set('portion', 'lower') - self:format_out(addiu, args) + local immediate = self:token(im):set('portion', 'lower') + self:push_new('ADDIU', rt, 'R0', immediate) end end function overrides.LA(self, name) - local lui = instructions['LUI'] - local addiu = instructions['ADDIU'] - local args = {} - args.rt = self:register() - self:optional_comma() - local im = self:const() + local rt = self:pop('CPU') + local im = self:pop('CONST') - args.rs = args.rt - args.immediate = self:token(im):set('portion', 'upperoff') - self:format_out(lui, args) - args.immediate = self:token(im):set('portion', 'lower') - self:format_out(addiu, args) + local rs = rt + local immediate = self:token(im):set('portion', 'upperoff') + self:push_new('LUI', rt, immediate) + local immediate = self:token(im):set('portion', 'lower') + self:push_new('ADDIU', rt, immediate) end function overrides.PUSH(self, name) - local addi = instructions['ADDI'] - local w = instructions[name == 'PUSH' and 'SW' or 'LW'] - local jr = instructions['JR'] + local w = name == 'PUSH' and 'SW' or 'LW' local stack = {} - while not self:is_EOL() do - if self.tt == 'NUM' then - if self.tok < 0 then - self:error("can't push a negative number of spaces") + for _, t in ipairs(self.s) do + if t.tt == 'NUM' then + if t.tok < 0 then + self:error("can't push a negative number of spaces", t.tok) end - for i=1,self.tok do + for i=1, t.tok do insert(stack, '') end - self:advance() + self:pop() else - insert(stack, self:register()) - end - if not self:is_EOL() then - self:optional_comma() + insert(stack, self:pop('CPU')) end end if #stack == 0 then self:error(name..' requires at least one argument') end - local args = {} if name == 'PUSH' then - args.rt = 'SP' - args.rs = 'SP' - args.immediate = self:token(#stack*4):set('negate') - self:format_out(addi, args) + local immediate = self:token(#stack*4):set('negate') + self:push_new('ADDIU', 'SP', 'SP', immediate) end - args.base = 'SP' for i, r in ipairs(stack) do - args.rt = r if r ~= '' then - args.offset = (i - 1)*4 - self:format_out(w, args) + local offset = (i - 1)*4 + self:push_new(w, r, offset, self:token('DEREF', 'SP')) end end if name == 'JPOP' then - args.rs = 'RA' - self:format_out(jr, args) + self:push_new('JR', 'RA') end if name == 'POP' or name == 'JPOP' then - args.rt = 'SP' - args.rs = 'SP' - args.immediate = #stack*4 - self:format_out(addi, args) + local immediate = #stack * 4 + self:push_new('ADDIU', 'SP', 'SP', immediate) end end overrides.POP = overrides.PUSH overrides.JPOP = overrides.PUSH function overrides.NAND(self, name) - local and_ = instructions['AND'] - local nor = instructions['NOR'] - local args = {} - args.rd = self:register() - self:optional_comma() - args.rs = self:register() - self:optional_comma() - args.rt = self:register() - self:format_out(and_, args) - args.rs = args.rd - args.rt = 'R0' - self:format_out(nor, args) + local rd = self:pop('CPU') + local rs = self:pop('CPU') + local rt = self:pop('CPU') + self:push_new('AND', rd, rs, rt) + local rs = rd + local rt = 'R0' + self:push_new('NOR', rd, rs, rt) end function overrides.NANDI(self, name) - local andi = instructions['ANDI'] - local nor = instructions['NOR'] - local args = {} - args.rt = self:register() - self:optional_comma() - args.rs = self:register() - self:optional_comma() - args.immediate = self:const() - self:format_out(andi[3], andi[1], args, andi[4], andi[5]) - args.rd = args.rt - args.rs = args.rt - args.rt = 'R0' - self:format_out(nor[3], nor[1], args, nor[4], nor[5]) + local rt = self:pop('CPU') + local rs = self:pop('CPU') + local immediate = self:pop('CONST') + self:push_new('ANDI', rt, rs, immediate) + local rd = rt + local rs = rt + local rt = 'R0' + self:push_new('NOR', rd, rs, rt) end function overrides.NORI(self, name) - local ori = instructions['ORI'] - local nor = instructions['NOR'] - local args = {} - args.rt = self:register() - self:optional_comma() - args.rs = self:register() - self:optional_comma() - args.immediate = self:const() - self:format_out(ori, args) - args.rd = args.rt - args.rs = args.rt - args.rt = 'R0' - self:format_out(nor, args) + local rt = self:pop('CPU') + local rs = self:pop('CPU') + local immediate = self:pop('CONST') + self:push_new('ORI', rt, rs, immediate) + local rd = rt + local rs = rt + local rt = 'R0' + self:push_new('NOR', rd, rs, rt) end function overrides.ROL(self, name) - local sll = instructions['SLL'] - local srl = instructions['SRL'] - local or_ = instructions['OR'] - local args = {} - local left = self:register() - self:optional_comma() - args.rt = self:register() - self:optional_comma() - args.immediate = self:const() - args.rd = left - if args.rd == 'AT' or args.rt == 'AT' then - self:error('registers cannot be AT in this pseudo-instruction') - end - if args.rd == args.rt and args.rd ~= 'R0' then - self:error('registers cannot be the same') - end - self:format_out(sll, args) - args.rd = 'AT' - args.immediate = 32 - args.immediate[2] - self:format_out(srl, args) - args.rd = left - args.rs = left - args.rt = 'AT' - self:format_out(or_, args) + -- FIXME + local rd, rs, rt + local left = self:pop('CPU') + rt = self:pop('CPU') + local immediate = self:pop('CONST') + error('Internal Error: unimplemented') end function overrides.ROR(self, name) - local sll = instructions['SLL'] - local srl = instructions['SRL'] - local or_ = instructions['OR'] - local args = {} - local right = self:register() - self:optional_comma() - args.rt = self:register() - self:optional_comma() - args.immediate = self:const() - args.rd = right - if args.rt == 'AT' or args.rd == 'AT' then - self:error('registers cannot be AT in a pseudo-instruction that uses AT') - end - if args.rd == args.rt and args.rd ~= 'R0' then - self:error('registers cannot be the same') - end - self:format_out(srl, args) - args.rd = 'AT' - args.immediate = 32 - args.immediate[2] - self:format_out(sll, args) - args.rd = right - args.rs = right - args.rt = 'AT' - self:format_out(or_, args) + -- FIXME + local right = self:pop('CPU') + local rt = self:pop('CPU') + local immediate = self:pop('CONST') + error('Internal Error: unimplemented') end function overrides.JR(self, name) - local jr = instructions['JR'] - local args = {} - if self:is_EOL() then - args.rs = 'RA' - else - args.rs = self:register() - end - self:format_out(jr, args) + local rs = self:peek() and self:pop('CPU') or 'RA' + self:push_new('JR', rs) end local branch_basics = { - BEQI = "BEQ", - BGEI = "BEQ", - BGTI = "BEQ", - BLEI = "BNE", - BLTI = "BNE", - BNEI = "BNE", - BEQIL = "BEQL", - BGEIL = "BEQL", - BGTIL = "BEQL", - BLEIL = "BNEL", - BLTIL = "BNEL", - BNEIL = "BNEL", + BEQI = 'BEQ', + BGEI = 'BEQ', + BGTI = 'BEQ', + BLEI = 'BNE', + BLTI = 'BNE', + BNEI = 'BNE', + BEQIL = 'BEQL', + BGEIL = 'BEQL', + BGTIL = 'BEQL', + BLEIL = 'BNEL', + BLTIL = 'BNEL', + BNEIL = 'BNEL', } function overrides.BEQI(self, name) - local addiu = instructions['ADDIU'] - local branch = instructions[branch_basics[name]] - local args = {} - local reg = self:register() - self:optional_comma() - args.immediate = self:const() - self:optional_comma() - args.offset = self:token(self:const('relative')):set('signed') + local branch = branch_basics[name] + local reg = self:pop('CPU') + local immediate = self:pop('CONST') + local offset = self:pop('REL'):set('signed') if reg == 'AT' then self:error('register cannot be AT in this pseudo-instruction') end - args.rt = 'AT' - args.rs = 'R0' - self:format_out(addiu, args) + self:push_new('ADDIU', 'AT', 'R0', immediate) - args.rs = reg - self:format_out(branch, args) + self:push_new(branch, reg, 'AT', offset) end overrides.BNEI = overrides.BEQI overrides.BEQIL = overrides.BEQI overrides.BNEIL = overrides.BEQI function overrides.BLTI(self, name) - local slti = instructions['SLTI'] - local branch = instructions[branch_basics[name]] - local args = {} - args.rs = self:register() - self:optional_comma() - args.immediate = self:const() - self:optional_comma() - args.offset = self:token(self:const('relative')):set('signed') + local branch = branch_basics[name] + local rs = self:pop('CPU') + local immediate = self:pop('CONST') + local offset = self:pop('REL'):set('signed') - if args.rs == 'AT' then + if rs == 'AT' then self:error('register cannot be AT in this pseudo-instruction') end - args.rt = 'AT' - self:format_out(slti, args) + self:push_new('SLTI', 'AT', reg) - args.rs = 'AT' - args.rt = 'R0' - self:format_out(branch, args) + self:push_new(branch, 'R0', 'AT', offset) end overrides.BGEI = overrides.BLTI overrides.BLTIL = overrides.BLTI @@ -297,40 +238,28 @@ overrides.BGEIL = overrides.BLTI function overrides.BLEI(self, name) -- TODO: this can probably be optimized - local addiu = instructions['ADDIU'] - local slt = instructions['SLT'] - local branch = instructions[branch_basics[name]] - local beq = instructions['BEQ'] - local args = {} - local reg = self:register() - self:optional_comma() - args.immediate = self:const() - self:optional_comma() - local offset = self:token(self:const('relative')):set('signed') + local branch = branch_basics[name] + local reg = self:pop('CPU') + local immediate = self:pop('CONST') + local offset = self:pop('REL'):set('signed') if reg == 'AT' then self:error('register cannot be AT in this pseudo-instruction') end - args.rt = 'AT' - args.rs = 'R0' - self:format_out(addiu, args) + self:push_new('ADDIU', 'AT', 'R0', immediate) + local beq_offset if name == 'BLEI' then - args.offset = offset + beq_offset = offset else - args.offset = 2 -- branch to delay slot of the next branch + beq_offset = 2 -- branch to delay slot of the next branch end - args.rs = reg - self:format_out(beq, args) + self:push_new('BEQ', reg, 'R0', beq_offset) - args.rd = 'AT' - self:format_out(slt, args) + self:push_new('SLT', 'AT', reg) - args.rs = 'AT' - args.rt = 'R0' - args.offset = offset - self:format_out(branch, args) + self:push_new(branch, 'AT', 'R0', offset) end overrides.BGTI = overrides.BLEI overrides.BLEIL = overrides.BLEI