From 5691484000c5063365c37b4ecf6a2c7b02fa8b63 Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Tue, 26 Apr 2016 14:48:39 -0700 Subject: [PATCH] update lips --- Lua/lib/lips/Collector.lua | 62 +++++++++--- Lua/lib/lips/Dumper.lua | 129 ++++++++++++++----------- Lua/lib/lips/Lexer.lua | 17 +++- Lua/lib/lips/Muncher.lua | 33 +++---- Lua/lib/lips/Parser.lua | 24 ++++- Lua/lib/lips/Preproc.lua | 28 ++++-- Lua/lib/lips/Reader.lua | 15 ++- Lua/lib/lips/Token.lua | 14 ++- Lua/lib/lips/data.lua | 108 ++++++++++++++++----- Lua/lib/lips/init.lua | 1 + Lua/lib/lips/overrides.lua | 188 ++++++++++++++++++++----------------- Lua/lib/lips/util.lua | 19 +++- 12 files changed, 410 insertions(+), 228 deletions(-) diff --git a/Lua/lib/lips/Collector.lua b/Lua/lib/lips/Collector.lua index 3bd338d..2fb21a7 100644 --- a/Lua/lib/lips/Collector.lua +++ b/Lua/lib/lips/Collector.lua @@ -35,6 +35,10 @@ function Collector:push_data(datum, size) -- TODO: consider not scrunching data statements, just their tokens + if type(datum) == 'number' then + datum = self:token(datum) + end + local last_statement = self.statements[#self.statements] local s if last_statement and last_statement.type == '!DATA' then @@ -44,16 +48,25 @@ function Collector:push_data(datum, size) insert(self.statements, s) end - if type(datum) == 'string' and size == 'WORD' then - -- labels will be assembled to words - insert(s, Token('LABEL', datum)) - return - end - if size ~= 'BYTE' and size ~= 'HALFWORD' and size ~= 'WORD' then error('Internal Error: unknown data size argument') end + if datum.tt == 'LABELSYM' then + if size == 'WORD' then + -- labels will be assembled to words + insert(s, datum) + return + else + self:error('labels are too large to be used in this directive') + end + elseif datum.tt == 'VARSYM' then + insert(s, datum:set('size', size)) + return + elseif datum.tt ~= 'NUM' then + self:error('unsupported data type', datum.tt) + end + local sizes = size..'S' local last_token = s[#s] @@ -65,7 +78,7 @@ function Collector:push_data(datum, size) insert(s, t) s:validate() end - insert(t.tok, datum) + insert(t.tok, datum.tok) end function Collector:variable() @@ -85,6 +98,12 @@ function Collector:directive() end if name == 'ORG' or name == 'BASE' then add(name, self:const(nil, 'no labels')) + elseif name == 'PUSH' or name == 'POP' then + add(name, self:const()) + while not self:is_EOL() do + self:optional_comma() + add(name, self:const()) + end elseif name == 'ALIGN' or name == 'SKIP' then if self:is_EOL() and name == 'ALIGN' then add(name) @@ -96,18 +115,31 @@ function Collector:directive() self:optional_comma() add(name, size, self:number()) end - self:expect_EOL() end elseif name == 'BYTE' or name == 'HALFWORD' or name == 'WORD' then - self:push_data(self:const().tok, name) + self:push_data(self:const(), name) while not self:is_EOL() do - self:advance() self:optional_comma() - self:push_data(self:const().tok, name) + self:push_data(self:const(), name) end - self:expect_EOL() + elseif name == 'HEX' then + if self.tt ~= 'OPEN' then + self:error('expected opening brace for hex directive', self.tt) + end + self:advance() + + while self.tt ~= 'CLOSE' do + if self.tt == 'EOL' then + self:advance() + else + self:push_data(self:const(), 'BYTE') + end + end + self:advance() elseif name == 'INC' or name == 'INCBIN' then -- noop, handled by lexer + self:string() + return -- don't expect EOL elseif name == 'ASCII' or name == 'ASCIIZ' then local bytes = self:string() for i, number in ipairs(bytes.tok) do @@ -116,12 +148,12 @@ function Collector:directive() if name == 'ASCIIZ' then self:push_data(0, 'BYTE') end - self:expect_EOL() elseif name == 'FLOAT' then self:error('unimplemented directive', name) else self:error('unknown directive', name) end + self:expect_EOL() end function Collector:basic_special() @@ -200,8 +232,8 @@ function Collector:collect(tokens, fn) self.statements = {} -- this works, but probably shouldn't be in this function specifically - if self.options.offset then - local s = Statement('(options)', 0, '!ORG', self.options.offset) + if self.options.origin then + local s = Statement('(options)', 0, '!ORG', self.options.origin) insert(self.statements, s) end if self.options.base then diff --git a/Lua/lib/lips/Dumper.lua b/Lua/lib/lips/Dumper.lua index 6a2d320..6a7c0db 100644 --- a/Lua/lib/lips/Dumper.lua +++ b/Lua/lib/lips/Dumper.lua @@ -1,6 +1,7 @@ local floor = math.floor local format = string.format local insert = table.insert +local remove = table.remove local unpack = unpack or table.unpack local path = string.gsub(..., "[^.]+$", "") @@ -12,13 +13,6 @@ local Reader = require(path.."Reader") local bitrange = util.bitrange -local function label_delta(from, to) - -- TODO: consider removing the % here since .base should handle that now - to = to - from = from - return floor(to/4) - 1 - floor(from/4) -end - local Dumper = Reader:extend() function Dumper:init(writer, options) self.writer = writer @@ -41,17 +35,21 @@ function Dumper:export_labels(t) return t end +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 + function Dumper:desym(t) - if t.tt == 'REL' then - local rel = label_delta(self:pc(), t.tok) - if rel > 0x8000 or rel <= -0x8000 then - self:error('branch too far') - end - return rel % 0x10000 + -- 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) elseif type(t.tok) == 'number' then - if t.offset then - return t.tok + t.offset - end return t.tok elseif t.tt == 'REG' then assert(data.all_registers[t.tok], 'Internal Error: unknown register') @@ -61,18 +59,11 @@ function Dumper:desym(t) if label == nil then self:error('undefined label', t.tok) end - if t.offset then - label = label + t.offset - end if t.tt == 'LABELSYM' then return label end - local rel = label_delta(self:pc(), label) - if rel > 0x8000 or rel <= -0x8000 then - self:error('branch too far') - end - return rel % 0x10000 + return self:label_delta(self:pc(), label) end error('Internal Error: failed to desym') end @@ -80,7 +71,7 @@ end function Dumper:validate(n, bits) local max = 2^bits if n == nil then - self:error('value is nil') -- internal error? + error('Internal Error: number to validate is nil', 2) end if n > max or n < 0 then self:error('value out of range', ("%X"):format(n)) @@ -109,34 +100,6 @@ function Dumper:write(t) 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') - end - - return uw, lw -end - function Dumper:assemble_j(first, out) local w = 0 w = w + self:valvar(first, 6) * 0x04000000 @@ -172,6 +135,7 @@ 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 + self.i = 0 for i=1, #informat do self.i = i local c = informat:sub(i, i) @@ -202,7 +166,7 @@ function Dumper:format_in(informat) elseif c == 'I' and not args.index then args.index = self:const():set('index') elseif c == 'k' and not args.immediate then - args.immediate = self:const(nil, 'no label'):set('negate') + args.immediate = self:const(nil, 'no label'):set('signed'):set('negate') 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 @@ -254,6 +218,9 @@ function Dumper:assemble(s) self.s = s if h[2] ~= nil then local args = self:format_in(h[2]) + if self.i ~= #s then + self:error('expected EOL; too many arguments') + end return self:format_out(h, args) else self:error('unimplemented instruction', name) @@ -276,6 +243,7 @@ function Dumper:pc() end function Dumper:load(statements) + local valstack = {} -- for .push/.pop directives local new_statements = {} self.pos = 0 self.base = 0 @@ -296,13 +264,58 @@ function Dumper:load(statements) elseif s.type == '!BASE' then self.base = s[1].tok insert(new_statements, s) + 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 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 - self:error('negative alignment') + self:error('negative alignment', align) else align = 2^align end @@ -352,13 +365,17 @@ function Dumper:load(statements) insert(new_statements, new) elseif s.type == '!DATA' then for i, t in ipairs(s) do - if t.tt == 'LABEL' then + if t.tt == 'LABELSYM' then local label = self.labels[t.tok] if label == nil then self:error('undefined label', t.tok) end t.tt = 'WORDS' t.tok = {label} + elseif t.tt == 'NUM' then + t.tt = t.size..'S' + t.tok = {t.tok} + t.size = nil end end self.pos = self.pos + (s.length or util.measure_data(s)) diff --git a/Lua/lib/lips/Lexer.lua b/Lua/lib/lips/Lexer.lua index d035991..81ee4e8 100644 --- a/Lua/lib/lips/Lexer.lua +++ b/Lua/lib/lips/Lexer.lua @@ -193,11 +193,13 @@ function Lexer:lex_hex(yield) end self:nextc() entered = true + yield('OPEN', '{') elseif self.chr == '}' then if not entered then self:error('expected opening brace') end self:nextc() + yield('CLOSE', '}') break elseif self.chr == ',' then self:error('commas are not allowed in HEX directives') @@ -208,7 +210,6 @@ function Lexer:lex_hex(yield) if self.chr:find(hexmatch) then self:error('too many hex digits to be a single byte') end - yield('DIR', 'BYTE') yield('NUM', num) elseif self.chr:find(hexmatch) then self:error('expected two hex digits to make a byte') @@ -248,7 +249,7 @@ function Lexer:lex_string(yield) local bytes = {} while true do if self.chr == '\n' then - self:error('unimplemented') + self:error('unimplemented: newlines in strings') yield('EOL', '\n') self:nextc() elseif self.ord == self.EOF then @@ -294,6 +295,8 @@ function Lexer:lex_include(_yield) self:lex_string_naive(function(tt, tok) fn = tok end) + _yield('STRING', fn, self.fn, self.line) + if self.options.path then fn = self.options.path..fn end @@ -307,15 +310,18 @@ function Lexer:lex_include_binary(_yield) self:lex_string_naive(function(tt, tok) fn = tok end) + _yield('STRING', fn, self.fn, self.line) + -- TODO: allow optional offset and size arguments if self.options.path then fn = self.options.path..fn end - -- FIXME: this allocates two tables for each byte. - -- this could easily cause performance issues on big files. local data = util.readfile(fn, true) + + -- FIXME: this allocates a table for each byte. + -- this could easily cause performance issues on big files. + _yield('DIR', 'BYTE', fn, 0) for b in string.gfind(data, '.') do - _yield('DIR', 'BYTE', fn, 0) _yield('NUM', string.byte(b), fn, 0) end end @@ -414,6 +420,7 @@ function Lexer:lex(_yield) self:nextc() yield('LABEL', buff) elseif up == 'HEX' then + yield('DIR', 'HEX') self:lex_hex(yield) elseif data.all_registers[up] then yield('REG', up) diff --git a/Lua/lib/lips/Muncher.lua b/Lua/lib/lips/Muncher.lua index b3f52fa..a9794ed 100644 --- a/Lua/lib/lips/Muncher.lua +++ b/Lua/lib/lips/Muncher.lua @@ -56,7 +56,7 @@ function Muncher:expect_EOL() self:advance() return end - self:error('expected end of line') + self:error('expected end of line', self.tt) end function Muncher:optional_comma() @@ -68,7 +68,7 @@ end function Muncher:number() if self.tt ~= 'NUM' then - self:error('expected number') + self:error('expected number', self.tt) end local t = self.t self:advance() @@ -77,7 +77,7 @@ end function Muncher:string() if self.tt ~= 'STRING' then - self:error('expected string') + self:error('expected string', self.tt) end local t = self.t self:advance() @@ -87,11 +87,11 @@ end function Muncher:register(registers) registers = registers or data.registers if self.tt ~= 'REG' then - self:error('expected register') + self:error('expected register', self.tt) end local t = self.t if not registers[t.tok] then - self:error('wrong type of register') + self:error('wrong type of register', t.tok) end self:advance() return self:token(t) @@ -99,16 +99,16 @@ end function Muncher:deref() if self.tt ~= 'OPEN' then - self:error('expected opening parenthesis for dereferencing') + self:error('expected opening parenthesis for dereferencing', self.tt) end self:advance() if self.tt ~= 'REG' then - self:error('expected register to dereference') + self:error('expected register to dereference', self.tt) end local t = self.t self:advance() if self.tt ~= 'CLOSE' then - self:error('expected closing parenthesis for dereferencing') + self:error('expected closing parenthesis for dereferencing', self.tt) end self:advance() return self:token(t) @@ -119,35 +119,28 @@ function Muncher:const(relative, no_label) self:error('expected constant', self.tt) end if no_label and self.tt == 'LABELSYM' then - self:error('labels are not allowed here') + self:error('labels are not allowed here', self.tt) end local t = self:token(self.t) - if relative then - if self.tt == 'LABELSYM' then - t.tt = 'LABELREL' - else - t.tt = 'REL' - end - end self:advance() return t end function Muncher:special() if self.tt ~= 'SPECIAL' then - self:error('expected special name to call') + self:error('expected special name to call', self.tt) end local name = self.tok self:advance() if self.tt ~= 'OPEN' then - self:error('expected opening parenthesis for special call') + self:error('expected opening parenthesis for special call', self.tt) end local args = {} while true do local arg = self:advance() if not arg_types[arg.tt] then - self:error('invalid argument type') + self:error('invalid argument type', arg.tt) else self:advance() end @@ -157,7 +150,7 @@ function Muncher:special() insert(args, arg) break else - self:error('unexpected token in argument list') + self:error('unexpected token in argument list', self.tt) end end diff --git a/Lua/lib/lips/Parser.lua b/Lua/lib/lips/Parser.lua index 78c66f6..66d2766 100644 --- a/Lua/lib/lips/Parser.lua +++ b/Lua/lib/lips/Parser.lua @@ -41,14 +41,25 @@ function Parser:tokenize(asm) end function Parser:debug_dump() + local boring = { + tt = true, + tok = true, + fn = true, + line = true, + } for i, s in ipairs(self.statements) do local values = '' - for j, v in ipairs(s) do - local tok = v.tok + for j, t in ipairs(s) do + local tok = t.tok if type(tok) == 'number' then tok = ("$%X"):format(tok) end - values = values..'\t'..v.tt..'('..tostring(tok)..')' + values = values..'\t'..t.tt..'('..tostring(tok)..')' + for k, v in pairs(t) do + if not boring[k] then + values = values..'['..k..'='..tostring(v)..']' + end + end end values = values:sub(2) print(s.line, s.type, values) @@ -62,14 +73,17 @@ function Parser:parse(asm) local preproc = Preproc(self.options) self.statements = preproc:process(self.statements) - self.statements = preproc:expand(self.statements) if self.options.debug_pre then self:debug_dump() end + self.statements = preproc:expand(self.statements) + + if self.options.debug_post then self:debug_dump() end + local dumper = Dumper(self.writer, self.options) self.statements = dumper:load(self.statements) - if self.options.debug_dump then self:debug_dump() end + if self.options.debug_asm then self:debug_dump() end if self.options.labels then dumper:export_labels(self.options.labels) diff --git a/Lua/lib/lips/Preproc.lua b/Lua/lib/lips/Preproc.lua index 7a09e22..ab68da2 100644 --- a/Lua/lib/lips/Preproc.lua +++ b/Lua/lib/lips/Preproc.lua @@ -90,7 +90,8 @@ function Preproc:check(s, i, tt) i = i or self.i local t = s[i] if t == nil then - self:error("expected another argument") + local err = ("expected another argument for %s at position %i"):format(self.s.type, self.i) + self:error(err) end self.fn = t.fn @@ -197,11 +198,9 @@ function Preproc:pop(kind) 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') + if self.s[self.i] ~= nil then + self:error('expected EOL; too many arguments') end return -- don't increment self.i past end of arguments else @@ -212,7 +211,7 @@ function Preproc:pop(kind) end function Preproc:expand(statements) - -- third pass: expand pseudo-instructions + -- third pass: expand pseudo-instructions and register arguments self.statements = {} for i=1, #statements do local s = statements[i] @@ -228,6 +227,23 @@ function Preproc:expand(statements) error('Internal Error: unknown instruction') end + if data.one_register_variants[name] then + self.i = 1 + local a = self:register(data.all_registers) + local b = s[2] + if b == nil or b.tt ~= 'REG' then + insert(s, 2, self:token(a)) + end + elseif data.two_register_variants[name] then + self.i = 1 + local a = self:register(data.all_registers) + local b = self:register(data.all_registers) + local c = s[3] + if c == nil or c.tt ~= 'REG' then + insert(s, 2, self:token(a)) + end + end + if overrides[name] then self.i = 1 overrides[name](self, name) diff --git a/Lua/lib/lips/Reader.lua b/Lua/lib/lips/Reader.lua index 8c31484..ca1468a 100644 --- a/Lua/lib/lips/Reader.lua +++ b/Lua/lib/lips/Reader.lua @@ -27,7 +27,8 @@ end function Reader:expect(tts) local t = self.s[self.i] if t == nil then - self:error("expected another argument") -- TODO: more verbose + local err = ("expected another argument for %s at position %i"):format(self.s.type, self.i) + self:error(err) end self.fn = t.fn @@ -39,8 +40,12 @@ function Reader:expect(tts) 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) + local err + if #tts == 1 then + err = ("argument %i of %s expected type %s"):format(self.i, self.s.type, tts[1]) + else + err = ("unexpected type for argument %i of %s"):format(self.i, self.s.type) + end self:error(err, t.tt) end @@ -49,7 +54,7 @@ function Reader:register(registers) local t = self.s[self.i] local numeric = registers[t.tok] if not numeric then - self:error('wrong type of register') + self:error('wrong type of register', t.tok) end local new = Token(t) return new @@ -63,7 +68,7 @@ function Reader:const(relative, no_label) self:expect{'NUM', 'LABELREL'} end local new = Token(t) - if relative then + if relative then -- you probably shouldn't use this in Preproc if t.tt == 'LABELSYM' then new.tt = 'LABELREL' elseif t.tt == 'NUM' then diff --git a/Lua/lib/lips/Token.lua b/Lua/lib/lips/Token.lua index 482b9f8..1911034 100644 --- a/Lua/lib/lips/Token.lua +++ b/Lua/lib/lips/Token.lua @@ -68,11 +68,15 @@ function Token:set(key, value) return self end -function Token:compute() - assert(self.tt == 'NUM', 'Internal Error: cannot compute a non-number token') - local n = self.tok +function Token:compute(n) + local n = n or self.tok + assert(n or self.tt == 'NUM', 'Internal Error: cannot compute a non-number token') + + if self.offset then + n = n + self.offset + end + if self.index then - -- TODO: should this still be here now that we have .base? n = n % 0x80000000 n = floor(n/4) end @@ -94,7 +98,7 @@ function Token:compute() n = upper end - if self.negate or self.signed then + if self.signed then if n >= 0x10000 or n < -0x8000 then return n, 'value out of range' end diff --git a/Lua/lib/lips/data.lua b/Lua/lib/lips/data.lua index b693848..079df18 100644 --- a/Lua/lib/lips/data.lua +++ b/Lua/lib/lips/data.lua @@ -30,6 +30,7 @@ data.fpu_registers = { data.all_directives = { 'ORG', 'BASE', 'ALIGN', 'SKIP', + 'PUSH', 'POP', -- experimental 'ASCII', 'ASCIIZ', 'BYTE', 'HALFWORD', 'WORD', --'HEX', -- excluded here due to different syntax @@ -85,7 +86,13 @@ data.fmt_double = 17 data.fmt_word = 20 data.fmt_long = 21 +-- set up dummy values for pseudo-instructions later local __ = {} +-- instructions with the first two arguments as registers, but not the third +local o1 = {} +-- instructions with all three arguments as registers +local o2 = {} + data.instructions = { --[[ data guide: @@ -247,6 +254,7 @@ data.instructions = { -- coprocessor-related instructions + -- TODO: these can take a code value TEQ = {0, 'st', 'st00C', 52}, TGE = {0, 'st', 'st00C', 48}, TGEU = {0, 'st', 'st00C', 49}, @@ -279,6 +287,7 @@ data.instructions = { CACHE = {47, 'iob', 'bio'}, -- misuses 'F' to write the initial bit +-- RFE = {16, '', 'F000C', 16, 16}, ERET = {16, '', 'F000C', 24, 16}, TLBP = {16, '', 'F000C', 8, 16}, TLBR = {16, '', 'F000C', 1, 16}, @@ -380,8 +389,11 @@ data.instructions = { CL = { 0, 'd', '00d0C', 37}, -- OR RD, R0, R0 MOV = { 0, 'ds', 's0d0C', 37}, -- OR RD, RS, R0 NEG = { 0, 'dt', '0td0C', 34}, -- SUB RD, R0, RT + NEGU = { 0, 'dt', '0td0C', 35}, -- SUBU RD, R0, RT NOP = { 0, '', '0'}, -- SLL R0, R0, 0 NOT = { 0, 'ds', 's0d0C', 39}, -- NOR RD, RS, R0 + SGT = { 0, 'dst', 'tsd0C', 42}, -- SLT RD, RT, RS + SGTU = { 0, 'dst', 'tsd0C', 43}, -- SLTU RD, RT, RS SUBI = { 8, 'tsk', 'sti'}, -- ADDI RT, RS, -immediate SUBIU = { 9, 'tsk', 'sti'}, -- ADDIU RT, RS, -immediate @@ -393,41 +405,95 @@ data.instructions = { PUSH = __, POP = __, JPOP = __, +-- CL = __, overridden to take varargs - ABS = __, -- BGEZ NOP SUBU? - MUL = __, -- MULT MFLO - --DIV = __, -- 3 arguments - REM = __, -- 3 arguments + ABS = o1, -- SRA XOR SUBU + MUL = o2, -- MULT MFLO +-- DIV = o2, -- 3 arguments + REM = o2, -- 3 arguments - NAND = __, -- AND, NOT - NANDI = __, -- ANDI, NOT - NORI = __, -- ORI, NOT - ROL = __, -- SLL, SRL, OR - ROR = __, -- SRL, SLL, OR + NAND = o2, -- AND, NOT + NANDI = o1, -- ANDI, NOT + NORI = o1, -- ORI, NOT + ROL = o1, -- SLL, SRL, OR + ROR = o1, -- SRL, SLL, OR - SEQ = __, SEQI = __, SEQIU = __, SEQU = __, - SGE = __, SGEI = __, SGEIU = __, SGEU = __, - SGT = __, SGTI = __, SGTIU = __, SGTU = __, - SLE = __, SLEI = __, SLEIU = __, SLEU = __, - SNE = __, SNEI = __, SNEIU = __, SNEU = __, + SEQ = o2, + SGE = o2, SGEU = o2, + SLE = o2, SLEU = o2, + SNE = o2, - BGE = __, - BLE = __, - BLT = __, - BGT = __, + SEQI = o1, SEQIU = o1, + SGEI = o1, SGEIU = o1, + SGTI = o1, SGTIU = o1, + SLEI = o1, SLEIU = o1, + SNEI = o1, SNEIU = o1, + + BGE = o1, BGEU = o1, + BLE = o1, BLEU = o1, + BLT = o1, BLTU = o1, + BGT = o1, BGTU = o1, BEQI = __, BEQIL = __, - BNEI = __, BNEIL = __, BGEI = __, BGEIL = __, + BGTI = __, BGTIL = __, BLEI = __, BLEIL = __, BLTI = __, BLTIL = __, - BGTI = __, BGTIL = __, + BNEI = __, BNEIL = __, + + BGEL = o1, BGEUL = o1, + BGTL = o1, BGTUL = o1, + BLEL = o1, BLEUL = o1, + BLTL = o1, BLTUL = o1, + } +local register_types = { + d = 0, + s = 0, + t = 0, + D = 1, + S = 1, + T = 1, + X = 2, + Y = 2, + Z = 2, +} + +data.one_register_variants = {} +data.two_register_variants = {} + data.all_instructions = {} local i = 1 for k, v in pairs(data.instructions) do - data.all_instructions[k:gsub('_', '.')] = i + local name = k:gsub('_', '.') + + -- if the first two args of an instructions are the same register type, + -- allow it to be used with just one argument to cover both. + -- likewise, if all three arguments are registers of the same type, + -- allow just two to be used. + local fmt = v[2] + if fmt then + local a = fmt:sub(1, 1) + local b = fmt:sub(2, 2) + local c = fmt:sub(3, 3) + a = register_types[a] + b = register_types[b] + c = register_types[c] + if a ~= nil and b ~= nil and a == b then + if c == nil then + data.one_register_variants[name] = true + elseif c == a then + data.two_register_variants[name] = true + end + end + elseif v == o1 then + data.one_register_variants[name] = true + elseif v == o2 then + data.two_register_variants[name] = true + end + + data.all_instructions[name] = i i = i + 1 end revtable(data.all_instructions) diff --git a/Lua/lib/lips/init.lua b/Lua/lib/lips/init.lua index 9e823b7..f7f6242 100644 --- a/Lua/lib/lips/init.lua +++ b/Lua/lib/lips/init.lua @@ -53,6 +53,7 @@ function lips.assemble(fn_or_asm, writer, options) options.origin = options.offset options.base = 0 else + options.origin = options.origin or 0 options.base = options.base or 0x80000000 end diff --git a/Lua/lib/lips/overrides.lua b/Lua/lib/lips/overrides.lua index 55f231d..7cdc7a5 100644 --- a/Lua/lib/lips/overrides.lua +++ b/Lua/lib/lips/overrides.lua @@ -8,7 +8,7 @@ local overrides = {} local function tob_override(self, name) -- handle all the addressing modes for lw/sw-like instructions - local rt = self:pop('CPU') + local dest = self:pop('CPU') local offset, base if self:peek('DEREF') then offset = 0 @@ -28,12 +28,12 @@ local function tob_override(self, name) 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) + local temp = self:token(o):set('portion', 'upperoff') + self:push_new('LUI', 'AT', temp) 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') + self:push_new('ADDU', 'AT', 'AT', reg) end end base = self:token('DEREF', 'AT') @@ -41,7 +41,7 @@ local function tob_override(self, name) base = self:pop('DEREF') end end - self:push_new(name, rt, offset, base) + self:push_new(name, dest, offset, base) end for k, v in pairs(data.instructions) do @@ -50,8 +50,8 @@ for k, v in pairs(data.instructions) do end end -function overrides.LI(self, name) - local rt = self:pop('CPU') +function overrides:LI(name) + local dest = self:pop('CPU') local im = self:pop('CONST') -- for us, this is just semantics. for a "real" assembler, @@ -62,40 +62,38 @@ function overrides.LI(self, name) if im.portion then -- FIXME: use appropriate instruction based on portion? - self:push_new('ADDIU', rt, 'R0', im) + self:push_new('ADDIU', dest, 'R0', im) return end im.tok = im.tok % 0x100000000 if im.tok >= 0x10000 and im.tok <= 0xFFFF8000 then - local rs = rt - local immediate = self:token(im):set('portion', 'upper') - self:push_new('LUI', rt, immediate) + local temp = self:token(im):set('portion', 'upper') + self:push_new('LUI', dest, temp) if im.tok % 0x10000 ~= 0 then - local immediate = self:token(im):set('portion', 'lower') - self:push_new('ORI', rt, rs, immediate) + local temp = self:token(im):set('portion', 'lower') + self:push_new('ORI', dest, dest, temp) end elseif im.tok >= 0x8000 and im.tok < 0x10000 then - local immediate = self:token(im):set('portion', 'lower') - self:push_new('ORI', rt, 'R0', immediate) + local temp = self:token(im):set('portion', 'lower') + self:push_new('ORI', dest, 'R0', temp) else - local immediate = self:token(im):set('portion', 'lower') - self:push_new('ADDIU', rt, 'R0', immediate) + local temp = self:token(im):set('portion', 'lower') + self:push_new('ADDIU', dest, 'R0', temp) end end -function overrides.LA(self, name) - local rt = self:pop('CPU') +function overrides:LA(name) + local dest = self:pop('CPU') local im = self:pop('CONST') - 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, rt, immediate) + local im = self:token(im):set('portion', 'upperoff') + self:push_new('LUI', dest, im) + local im = self:token(im):set('portion', 'lower') + self:push_new('ADDIU', dest, dest, im) end -function overrides.PUSH(self, name) +function overrides:PUSH(name) local w = name == 'PUSH' and 'SW' or 'LW' local stack = {} for _, t in ipairs(self.s) do @@ -115,8 +113,8 @@ function overrides.PUSH(self, name) self:error(name..' requires at least one argument') end if name == 'PUSH' then - local immediate = self:token(#stack*4):set('negate') - self:push_new('ADDIU', 'SP', 'SP', immediate) + local im = self:token(#stack*4):set('negate') + self:push_new('ADDIU', 'SP', 'SP', im) end for i, r in ipairs(stack) do if r ~= '' then @@ -128,65 +126,76 @@ function overrides.PUSH(self, name) self:push_new('JR', 'RA') end if name == 'POP' or name == 'JPOP' then - local immediate = #stack * 4 - self:push_new('ADDIU', 'SP', 'SP', immediate) + local im = #stack * 4 + self:push_new('ADDIU', 'SP', 'SP', im) end end overrides.POP = overrides.PUSH overrides.JPOP = overrides.PUSH -function overrides.NAND(self, name) - 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) +function overrides:NAND(name) + local dest = self:pop('CPU') + local src = self:pop('CPU') + local target = self:pop('CPU') + self:push_new('AND', dest, src, target) + self:push_new('NOR', dest, dest, 'R0') -- NOT end -function overrides.NANDI(self, name) - 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) +function overrides:NANDI(name) + local dest = self:pop('CPU') + local src = self:pop('CPU') + local im = self:pop('CONST') + self:push_new('ANDI', dest, src, im) + self:push_new('NOR', dest, dest, 'R0') -- NOT end -function overrides.NORI(self, name) - 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) +function overrides:NORI(name) + local dest = self:pop('CPU') + local src = self:pop('CPU') + local im = self:pop('CONST') + self:push_new('ORI', dest, src, im) + self:push_new('NOR', dest, dest, 'R0') -- NOT end -function overrides.ROL(self, name) - -- FIXME - local rd, rs, rt - local left = self:pop('CPU') - rt = self:pop('CPU') - local immediate = self:pop('CONST') - error('Internal Error: unimplemented') +function overrides:ROL(name) + local first = name == 'ROL' and 'SLL' or 'SRL' + local second = name == 'ROL' and 'SRL' or 'SLL' + local dest = self:pop('CPU') + local src = self:pop('CPU') + local im = self:pop('CONST') + if dest == 'AT' or src == 'AT' then + self:error('registers cannot be AT in this pseudo-instruction') + end + + self:push_new(first, dest, src, im) + local temp, err = im:compute() + if err then + self:error(err, temp) + end + self:push_new(second, 'AT', src, 32 - temp) + self:push_new('OR', dest, dest, 'AT') +end +overrides.ROR = overrides.ROL + +function overrides:ABS(name) + local dest = self:pop('CPU') + local src = self:pop('CPU') + self:push_new('SRA', 'AT', src, 31) + self:push_new('XOR', dest, src, 'AT') + self:push_new('SUBU', dest, dest, 'AT') end -function overrides.ROR(self, name) - -- FIXME - local right = self:pop('CPU') - local rt = self:pop('CPU') - local immediate = self:pop('CONST') - error('Internal Error: unimplemented') +function overrides:CL(name) + self:expect{'REG'} -- assert there's at least one argument + for i=1, #self.s do + local reg = self:pop('CPU') + self:push_new('CL', reg) + end end -function overrides.JR(self, name) - local rs = self:peek() and self:pop('CPU') or 'RA' - self:push_new('JR', rs) +function overrides:JR(name) + local src = self:peek() and self:pop('CPU') or 'RA' + self:push_new('JR', src) end local branch_basics = { @@ -204,17 +213,17 @@ local branch_basics = { BNEIL = 'BNEL', } -function overrides.BEQI(self, name) +function overrides:BEQI(name) local branch = branch_basics[name] local reg = self:pop('CPU') - local immediate = self:pop('CONST') - local offset = self:pop('REL'):set('signed') + local im = self:pop('CONST') + local offset = self:pop('CONST') if reg == 'AT' then self:error('register cannot be AT in this pseudo-instruction') end - self:push_new('ADDIU', 'AT', 'R0', immediate) + self:push_new('ADDIU', 'AT', 'R0', im) self:push_new(branch, reg, 'AT', offset) end @@ -222,17 +231,17 @@ overrides.BNEI = overrides.BEQI overrides.BEQIL = overrides.BEQI overrides.BNEIL = overrides.BEQI -function overrides.BLTI(self, name) +function overrides:BLTI(name) local branch = branch_basics[name] local reg = self:pop('CPU') - local immediate = self:pop('CONST') - local offset = self:pop('REL'):set('signed') + local im = self:pop('CONST') + local offset = self:pop('CONST') if reg == 'AT' then self:error('register cannot be AT in this pseudo-instruction') end - self:push_new('SLTI', 'AT', reg, immediate) + self:push_new('SLTI', 'AT', reg, im) self:push_new(branch, 'R0', 'AT', offset) end @@ -240,29 +249,32 @@ overrides.BGEI = overrides.BLTI overrides.BLTIL = overrides.BLTI overrides.BGEIL = overrides.BLTI -function overrides.BLEI(self, name) +function overrides:BLEI(name) -- TODO: this can probably be optimized + if name:sub(#name) == 'L' then + self:error('unimplemented pseudo-instruction', name) + end local branch = branch_basics[name] local reg = self:pop('CPU') - local immediate = self:pop('CONST') - local offset = self:pop('REL'):set('signed') + local im = self:pop('CONST') + local offset = self:pop('CONST') if reg == 'AT' then self:error('register cannot be AT in this pseudo-instruction') end - self:push_new('ADDIU', 'AT', 'R0', immediate) + self:push_new('ADDIU', 'AT', 'R0', im) local beq_offset - if name == 'BLEI' then + if name == 'BLEI' or name =='BLEIL' then beq_offset = offset else - -- FIXME: this probably isn't correct for branch-likely instructions - beq_offset = 2 -- branch to delay slot of the next branch + -- branch to delay slot of the next branch + beq_offset = self:token('NUM', 2):set('fixed') end - self:push_new('BEQ', reg, 'R0', beq_offset) + self:push_new('BEQ', reg, 'AT', beq_offset) - self:push_new('SLT', 'AT', reg, immediate) + self:push_new('SLT', 'AT', reg, 'AT') self:push_new(branch, 'AT', 'R0', offset) end diff --git a/Lua/lib/lips/util.lua b/Lua/lib/lips/util.lua index 4b80607..145ecca 100644 --- a/Lua/lib/lips/util.lua +++ b/Lua/lib/lips/util.lua @@ -39,12 +39,27 @@ else -- 5.2, 5.3 end end +local data_sizes = { + BYTE = 1, + HALFWORD = 2, + WORD = 4, +} + local function measure_data(s) assert(s and s.type == '!DATA', 'Internal Error: expected !DATA statement') local n = 0 for i, t in ipairs(s) do - if t.tt == 'LABEL' then + if t.tt == 'LABELSYM' then n = n + 4 + elseif t.tt == 'NUM' then + if t.size == nil then + error('Internal Error: unspecified data size in NUM') + end + local size = data_sizes[t.size] + if size == nil then + error('Internal Error: unknown data size in NUM, got '..tostring(t.size)) + end + n = n + size elseif t.tt == 'WORDS' then n = n + #t.tok * 4 elseif t.tt == 'HALFWORDS' then @@ -52,7 +67,7 @@ local function measure_data(s) elseif t.tt == 'BYTES' then n = n + #t.tok * 1 else - error('Internal Error: unknown data type in !DATA') + error('Internal Error: unknown data type in !DATA, got '..tostring(t.tt)) end end return n