mirror of
https://github.com/notwa/lips
synced 2025-03-09 19:32:49 -07:00
get a bunch of overrides working again
This commit is contained in:
parent
acb5a3a6df
commit
14e397a7a8
6 changed files with 336 additions and 310 deletions
|
@ -6,14 +6,15 @@ local unpack = unpack or table.unpack
|
||||||
local path = string.gsub(..., "[^.]+$", "")
|
local path = string.gsub(..., "[^.]+$", "")
|
||||||
local data = require(path.."data")
|
local data = require(path.."data")
|
||||||
local util = require(path.."util")
|
local util = require(path.."util")
|
||||||
local overrides = require(path.."overrides")
|
--local overrides = require(path.."overrides")
|
||||||
local Base = require(path.."Base")
|
local Base = require(path.."Base")
|
||||||
local Token = require(path.."Token")
|
local Token = require(path.."Token")
|
||||||
local Statement = require(path.."Statement")
|
local Statement = require(path.."Statement")
|
||||||
|
local Reader = require(path.."Reader")
|
||||||
|
|
||||||
local bitrange = util.bitrange
|
local bitrange = util.bitrange
|
||||||
|
|
||||||
local Dumper = Base:extend()
|
local Dumper = Reader:extend()
|
||||||
function Dumper:init(writer, options)
|
function Dumper:init(writer, options)
|
||||||
self.writer = writer
|
self.writer = writer
|
||||||
self.options = options or {}
|
self.options = options or {}
|
||||||
|
@ -23,13 +24,6 @@ function Dumper:init(writer, options)
|
||||||
self.lastcommand = nil
|
self.lastcommand = nil
|
||||||
end
|
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)
|
function Dumper:export_labels(t)
|
||||||
for k, v in pairs(self.labels) do
|
for k, v in pairs(self.labels) do
|
||||||
-- only return valid labels; those that don't begin with a number
|
-- only return valid labels; those that don't begin with a number
|
||||||
|
@ -225,62 +219,6 @@ function Dumper:dump_instruction(t)
|
||||||
return uw, lw
|
return uw, lw
|
||||||
end
|
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)
|
function Dumper:assemble_j(first, out)
|
||||||
local w = 0
|
local w = 0
|
||||||
w = w + self:valvar(first, 6) * 0x04000000
|
w = w + self:valvar(first, 6) * 0x04000000
|
||||||
|
@ -318,7 +256,7 @@ function Dumper:format_in(informat)
|
||||||
local args = {}
|
local args = {}
|
||||||
--if #informat ~= #s then error('mismatch') end
|
--if #informat ~= #s then error('mismatch') end
|
||||||
for i=1, #informat do
|
for i=1, #informat do
|
||||||
self.i = i -- FIXME: do we need this?
|
self.i = i
|
||||||
local c = informat:sub(i, i)
|
local c = informat:sub(i, i)
|
||||||
if c == 'd' and not args.rd then
|
if c == 'd' and not args.rd then
|
||||||
args.rd = self:register(data.registers)
|
args.rd = self:register(data.registers)
|
||||||
|
@ -351,7 +289,7 @@ function Dumper:format_in(informat)
|
||||||
elseif c == 'K' and not args.immediate then
|
elseif c == 'K' and not args.immediate then
|
||||||
args.immediate = Token(self:const(nil, 'no label')):set('signed')
|
args.immediate = Token(self:const(nil, 'no label')):set('signed')
|
||||||
elseif c == 'b' and not args.base then
|
elseif c == 'b' and not args.base then
|
||||||
args.base = self:deref()
|
args.base = self:deref():set('tt', 'REG')
|
||||||
else
|
else
|
||||||
error('Internal Error: invalid input formatting string')
|
error('Internal Error: invalid input formatting string')
|
||||||
end
|
end
|
||||||
|
@ -397,14 +335,15 @@ function Dumper:assemble(s)
|
||||||
local name = s.type
|
local name = s.type
|
||||||
local h = data.instructions[name]
|
local h = data.instructions[name]
|
||||||
self.s = s
|
self.s = s
|
||||||
if overrides[name] then
|
-- if overrides[name] then
|
||||||
--overrides[name](self, name)
|
-- --overrides[name](self, name)
|
||||||
local s = Statement(self.fn, self.line, '!DATA') -- FIXME: dummy
|
-- local s = Statement(self.fn, self.line, '!DATA') -- FIXME: dummy
|
||||||
return s
|
-- return s
|
||||||
elseif h[2] == 'tob' then -- TODO: or h[2] == 'Tob' then
|
-- elseif h[2] == 'tob' then -- TODO: or h[2] == 'Tob' then
|
||||||
local s = Statement(self.fn, self.line, '!DATA') -- FIXME: dummy
|
-- local s = Statement(self.fn, self.line, '!DATA') -- FIXME: dummy
|
||||||
return s
|
-- return s
|
||||||
elseif h[2] ~= nil then
|
-- elseif h[2] ~= nil then
|
||||||
|
if h[2] ~= nil then
|
||||||
local args = self:format_in(h[2])
|
local args = self:format_in(h[2])
|
||||||
return self:format_out(h, args)
|
return self:format_out(h, args)
|
||||||
else
|
else
|
||||||
|
@ -446,10 +385,12 @@ function Dumper:load(statements)
|
||||||
if s.type:sub(1, 1) ~= '!' then
|
if s.type:sub(1, 1) ~= '!' then
|
||||||
s = self:assemble(s)
|
s = self:assemble(s)
|
||||||
insert(new_statements, s)
|
insert(new_statements, s)
|
||||||
|
self.pos = self.pos + 4 -- FIXME: assumes no pseudo-ops
|
||||||
elseif assembled_directives[s.type] then
|
elseif assembled_directives[s.type] then
|
||||||
-- FIXME: check for LABELs in !DATA
|
-- FIXME: check for LABELs in !DATA
|
||||||
-- TODO: reimplement ALIGN and SKIP here
|
-- TODO: reimplement ALIGN and SKIP here
|
||||||
insert(new_statements, s)
|
insert(new_statements, s)
|
||||||
|
self.pos = self.pos + s:length()
|
||||||
elseif s.type == '!LABEL' then
|
elseif s.type == '!LABEL' then
|
||||||
-- noop
|
-- noop
|
||||||
else
|
else
|
||||||
|
|
|
@ -103,12 +103,7 @@ function Parser:parse(asm)
|
||||||
|
|
||||||
local preproc = Preproc(self.options)
|
local preproc = Preproc(self.options)
|
||||||
self.statements = preproc:process(self.statements)
|
self.statements = preproc:process(self.statements)
|
||||||
|
self.statements = preproc:expand(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
|
|
||||||
--]]
|
|
||||||
|
|
||||||
-- DEBUG
|
-- DEBUG
|
||||||
for i, s in ipairs(self.statements) do
|
for i, s in ipairs(self.statements) do
|
||||||
|
|
|
@ -2,9 +2,11 @@ local insert = table.insert
|
||||||
|
|
||||||
local path = string.gsub(..., "[^.]+$", "")
|
local path = string.gsub(..., "[^.]+$", "")
|
||||||
local data = require(path.."data")
|
local data = require(path.."data")
|
||||||
|
local overrides = require(path.."overrides")
|
||||||
local Base = require(path.."Base")
|
local Base = require(path.."Base")
|
||||||
local Token = require(path.."Token")
|
local Token = require(path.."Token")
|
||||||
local Statement = require(path.."Statement")
|
local Statement = require(path.."Statement")
|
||||||
|
local Reader = require(path.."Reader")
|
||||||
|
|
||||||
local abs = math.abs
|
local abs = math.abs
|
||||||
|
|
||||||
|
@ -37,18 +39,11 @@ local function RelativeLabel(index, name)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local Preproc = Base:extend()
|
local Preproc = Reader:extend()
|
||||||
function Preproc:init(options)
|
function Preproc:init(options)
|
||||||
self.options = options or {}
|
self.options = options or {}
|
||||||
end
|
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)
|
function Preproc:lookup(t)
|
||||||
if t.tt == 'VARSYM' then
|
if t.tt == 'VARSYM' then
|
||||||
local name = t.tok
|
local name = t.tok
|
||||||
|
@ -110,6 +105,8 @@ function Preproc:lookup(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Preproc:check(s, i, tt)
|
function Preproc:check(s, i, tt)
|
||||||
|
s = s or self.s
|
||||||
|
i = i or self.i
|
||||||
local t = s[i]
|
local t = s[i]
|
||||||
if t == nil then
|
if t == nil then
|
||||||
self:error("expected another argument")
|
self:error("expected another argument")
|
||||||
|
@ -187,7 +184,77 @@ function Preproc:process(statements)
|
||||||
end
|
end
|
||||||
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
|
return self.statements
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
93
lips/Reader.lua
Normal file
93
lips/Reader.lua
Normal file
|
@ -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
|
|
@ -45,7 +45,8 @@ function Statement:validate(n)
|
||||||
end
|
end
|
||||||
for i, v in ipairs(self) do
|
for i, v in ipairs(self) do
|
||||||
if util.parent(v) ~= Token then
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,19 +3,52 @@ local insert = table.insert
|
||||||
local path = string.gsub(..., "[^.]+$", "")
|
local path = string.gsub(..., "[^.]+$", "")
|
||||||
local data = require(path.."data")
|
local data = require(path.."data")
|
||||||
|
|
||||||
local instructions = data.instructions
|
|
||||||
|
|
||||||
local overrides = {}
|
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)
|
function overrides.LI(self, name)
|
||||||
local lui = instructions['LUI']
|
local rt = self:pop('CPU')
|
||||||
local ori = instructions['ORI']
|
local im = self:pop('CONST')
|
||||||
local addiu = instructions['ADDIU']
|
|
||||||
local args = {}
|
|
||||||
args.rt = self:register()
|
|
||||||
self:optional_comma()
|
|
||||||
local im = self:const()
|
|
||||||
|
|
||||||
-- for us, this is just semantics. for a "real" assembler,
|
-- for us, this is just semantics. for a "real" assembler,
|
||||||
-- LA could add appropriate RELO LUI/ADDIU directives.
|
-- LA could add appropriate RELO LUI/ADDIU directives.
|
||||||
|
@ -24,272 +57,180 @@ function overrides.LI(self, name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if im.portion then
|
if im.portion then
|
||||||
args.rs = 'R0'
|
-- FIXME: use appropriate instruction based on portion?
|
||||||
args.immediate = im
|
self:push_new('ADDIU', rt, 'R0', im)
|
||||||
self:format_out(addiu, args)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
im.tok = im.tok % 0x100000000
|
im.tok = im.tok % 0x100000000
|
||||||
if im.tok >= 0x10000 and im.tok <= 0xFFFF8000 then
|
if im.tok >= 0x10000 and im.tok <= 0xFFFF8000 then
|
||||||
args.rs = args.rt
|
local rs = rt
|
||||||
args.immediate = self:token(im):set('portion', 'upper')
|
local immediate = self:token(im):set('portion', 'upper')
|
||||||
self:format_out(lui, args)
|
self:push_new('LUI', rt, immediate)
|
||||||
if im.tok % 0x10000 ~= 0 then
|
if im.tok % 0x10000 ~= 0 then
|
||||||
args.immediate = self:token(im):set('portion', 'lower')
|
local immediate = self:token(im):set('portion', 'lower')
|
||||||
self:format_out(ori, args)
|
self:push_new('ORI', rt, rs, immediate)
|
||||||
end
|
end
|
||||||
elseif im.tok >= 0x8000 and im.tok < 0x10000 then
|
elseif im.tok >= 0x8000 and im.tok < 0x10000 then
|
||||||
args.rs = 'R0'
|
local immediate = self:token(im):set('portion', 'lower')
|
||||||
args.immediate = self:token(im):set('portion', 'lower')
|
self:push_new('ORI', rt, 'R0', immediate)
|
||||||
self:format_out(ori, args)
|
|
||||||
else
|
else
|
||||||
args.rs = 'R0'
|
local immediate = self:token(im):set('portion', 'lower')
|
||||||
args.immediate = self:token(im):set('portion', 'lower')
|
self:push_new('ADDIU', rt, 'R0', immediate)
|
||||||
self:format_out(addiu, args)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function overrides.LA(self, name)
|
function overrides.LA(self, name)
|
||||||
local lui = instructions['LUI']
|
local rt = self:pop('CPU')
|
||||||
local addiu = instructions['ADDIU']
|
local im = self:pop('CONST')
|
||||||
local args = {}
|
|
||||||
args.rt = self:register()
|
|
||||||
self:optional_comma()
|
|
||||||
local im = self:const()
|
|
||||||
|
|
||||||
args.rs = args.rt
|
local rs = rt
|
||||||
args.immediate = self:token(im):set('portion', 'upperoff')
|
local immediate = self:token(im):set('portion', 'upperoff')
|
||||||
self:format_out(lui, args)
|
self:push_new('LUI', rt, immediate)
|
||||||
args.immediate = self:token(im):set('portion', 'lower')
|
local immediate = self:token(im):set('portion', 'lower')
|
||||||
self:format_out(addiu, args)
|
self:push_new('ADDIU', rt, immediate)
|
||||||
end
|
end
|
||||||
|
|
||||||
function overrides.PUSH(self, name)
|
function overrides.PUSH(self, name)
|
||||||
local addi = instructions['ADDI']
|
local w = name == 'PUSH' and 'SW' or 'LW'
|
||||||
local w = instructions[name == 'PUSH' and 'SW' or 'LW']
|
|
||||||
local jr = instructions['JR']
|
|
||||||
local stack = {}
|
local stack = {}
|
||||||
while not self:is_EOL() do
|
for _, t in ipairs(self.s) do
|
||||||
if self.tt == 'NUM' then
|
if t.tt == 'NUM' then
|
||||||
if self.tok < 0 then
|
if t.tok < 0 then
|
||||||
self:error("can't push a negative number of spaces")
|
self:error("can't push a negative number of spaces", t.tok)
|
||||||
end
|
end
|
||||||
for i=1,self.tok do
|
for i=1, t.tok do
|
||||||
insert(stack, '')
|
insert(stack, '')
|
||||||
end
|
end
|
||||||
self:advance()
|
self:pop()
|
||||||
else
|
else
|
||||||
insert(stack, self:register())
|
insert(stack, self:pop('CPU'))
|
||||||
end
|
|
||||||
if not self:is_EOL() then
|
|
||||||
self:optional_comma()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if #stack == 0 then
|
if #stack == 0 then
|
||||||
self:error(name..' requires at least one argument')
|
self:error(name..' requires at least one argument')
|
||||||
end
|
end
|
||||||
local args = {}
|
|
||||||
if name == 'PUSH' then
|
if name == 'PUSH' then
|
||||||
args.rt = 'SP'
|
local immediate = self:token(#stack*4):set('negate')
|
||||||
args.rs = 'SP'
|
self:push_new('ADDIU', 'SP', 'SP', immediate)
|
||||||
args.immediate = self:token(#stack*4):set('negate')
|
|
||||||
self:format_out(addi, args)
|
|
||||||
end
|
end
|
||||||
args.base = 'SP'
|
|
||||||
for i, r in ipairs(stack) do
|
for i, r in ipairs(stack) do
|
||||||
args.rt = r
|
|
||||||
if r ~= '' then
|
if r ~= '' then
|
||||||
args.offset = (i - 1)*4
|
local offset = (i - 1)*4
|
||||||
self:format_out(w, args)
|
self:push_new(w, r, offset, self:token('DEREF', 'SP'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if name == 'JPOP' then
|
if name == 'JPOP' then
|
||||||
args.rs = 'RA'
|
self:push_new('JR', 'RA')
|
||||||
self:format_out(jr, args)
|
|
||||||
end
|
end
|
||||||
if name == 'POP' or name == 'JPOP' then
|
if name == 'POP' or name == 'JPOP' then
|
||||||
args.rt = 'SP'
|
local immediate = #stack * 4
|
||||||
args.rs = 'SP'
|
self:push_new('ADDIU', 'SP', 'SP', immediate)
|
||||||
args.immediate = #stack*4
|
|
||||||
self:format_out(addi, args)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
overrides.POP = overrides.PUSH
|
overrides.POP = overrides.PUSH
|
||||||
overrides.JPOP = overrides.PUSH
|
overrides.JPOP = overrides.PUSH
|
||||||
|
|
||||||
function overrides.NAND(self, name)
|
function overrides.NAND(self, name)
|
||||||
local and_ = instructions['AND']
|
local rd = self:pop('CPU')
|
||||||
local nor = instructions['NOR']
|
local rs = self:pop('CPU')
|
||||||
local args = {}
|
local rt = self:pop('CPU')
|
||||||
args.rd = self:register()
|
self:push_new('AND', rd, rs, rt)
|
||||||
self:optional_comma()
|
local rs = rd
|
||||||
args.rs = self:register()
|
local rt = 'R0'
|
||||||
self:optional_comma()
|
self:push_new('NOR', rd, rs, rt)
|
||||||
args.rt = self:register()
|
|
||||||
self:format_out(and_, args)
|
|
||||||
args.rs = args.rd
|
|
||||||
args.rt = 'R0'
|
|
||||||
self:format_out(nor, args)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function overrides.NANDI(self, name)
|
function overrides.NANDI(self, name)
|
||||||
local andi = instructions['ANDI']
|
local rt = self:pop('CPU')
|
||||||
local nor = instructions['NOR']
|
local rs = self:pop('CPU')
|
||||||
local args = {}
|
local immediate = self:pop('CONST')
|
||||||
args.rt = self:register()
|
self:push_new('ANDI', rt, rs, immediate)
|
||||||
self:optional_comma()
|
local rd = rt
|
||||||
args.rs = self:register()
|
local rs = rt
|
||||||
self:optional_comma()
|
local rt = 'R0'
|
||||||
args.immediate = self:const()
|
self:push_new('NOR', rd, rs, rt)
|
||||||
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])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function overrides.NORI(self, name)
|
function overrides.NORI(self, name)
|
||||||
local ori = instructions['ORI']
|
local rt = self:pop('CPU')
|
||||||
local nor = instructions['NOR']
|
local rs = self:pop('CPU')
|
||||||
local args = {}
|
local immediate = self:pop('CONST')
|
||||||
args.rt = self:register()
|
self:push_new('ORI', rt, rs, immediate)
|
||||||
self:optional_comma()
|
local rd = rt
|
||||||
args.rs = self:register()
|
local rs = rt
|
||||||
self:optional_comma()
|
local rt = 'R0'
|
||||||
args.immediate = self:const()
|
self:push_new('NOR', rd, rs, rt)
|
||||||
self:format_out(ori, args)
|
|
||||||
args.rd = args.rt
|
|
||||||
args.rs = args.rt
|
|
||||||
args.rt = 'R0'
|
|
||||||
self:format_out(nor, args)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function overrides.ROL(self, name)
|
function overrides.ROL(self, name)
|
||||||
local sll = instructions['SLL']
|
-- FIXME
|
||||||
local srl = instructions['SRL']
|
local rd, rs, rt
|
||||||
local or_ = instructions['OR']
|
local left = self:pop('CPU')
|
||||||
local args = {}
|
rt = self:pop('CPU')
|
||||||
local left = self:register()
|
local immediate = self:pop('CONST')
|
||||||
self:optional_comma()
|
error('Internal Error: unimplemented')
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function overrides.ROR(self, name)
|
function overrides.ROR(self, name)
|
||||||
local sll = instructions['SLL']
|
-- FIXME
|
||||||
local srl = instructions['SRL']
|
local right = self:pop('CPU')
|
||||||
local or_ = instructions['OR']
|
local rt = self:pop('CPU')
|
||||||
local args = {}
|
local immediate = self:pop('CONST')
|
||||||
local right = self:register()
|
error('Internal Error: unimplemented')
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function overrides.JR(self, name)
|
function overrides.JR(self, name)
|
||||||
local jr = instructions['JR']
|
local rs = self:peek() and self:pop('CPU') or 'RA'
|
||||||
local args = {}
|
self:push_new('JR', rs)
|
||||||
if self:is_EOL() then
|
|
||||||
args.rs = 'RA'
|
|
||||||
else
|
|
||||||
args.rs = self:register()
|
|
||||||
end
|
|
||||||
self:format_out(jr, args)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local branch_basics = {
|
local branch_basics = {
|
||||||
BEQI = "BEQ",
|
BEQI = 'BEQ',
|
||||||
BGEI = "BEQ",
|
BGEI = 'BEQ',
|
||||||
BGTI = "BEQ",
|
BGTI = 'BEQ',
|
||||||
BLEI = "BNE",
|
BLEI = 'BNE',
|
||||||
BLTI = "BNE",
|
BLTI = 'BNE',
|
||||||
BNEI = "BNE",
|
BNEI = 'BNE',
|
||||||
BEQIL = "BEQL",
|
BEQIL = 'BEQL',
|
||||||
BGEIL = "BEQL",
|
BGEIL = 'BEQL',
|
||||||
BGTIL = "BEQL",
|
BGTIL = 'BEQL',
|
||||||
BLEIL = "BNEL",
|
BLEIL = 'BNEL',
|
||||||
BLTIL = "BNEL",
|
BLTIL = 'BNEL',
|
||||||
BNEIL = "BNEL",
|
BNEIL = 'BNEL',
|
||||||
}
|
}
|
||||||
|
|
||||||
function overrides.BEQI(self, name)
|
function overrides.BEQI(self, name)
|
||||||
local addiu = instructions['ADDIU']
|
local branch = branch_basics[name]
|
||||||
local branch = instructions[branch_basics[name]]
|
local reg = self:pop('CPU')
|
||||||
local args = {}
|
local immediate = self:pop('CONST')
|
||||||
local reg = self:register()
|
local offset = self:pop('REL'):set('signed')
|
||||||
self:optional_comma()
|
|
||||||
args.immediate = self:const()
|
|
||||||
self:optional_comma()
|
|
||||||
args.offset = self:token(self:const('relative')):set('signed')
|
|
||||||
|
|
||||||
if reg == 'AT' then
|
if reg == 'AT' then
|
||||||
self:error('register cannot be AT in this pseudo-instruction')
|
self:error('register cannot be AT in this pseudo-instruction')
|
||||||
end
|
end
|
||||||
|
|
||||||
args.rt = 'AT'
|
self:push_new('ADDIU', 'AT', 'R0', immediate)
|
||||||
args.rs = 'R0'
|
|
||||||
self:format_out(addiu, args)
|
|
||||||
|
|
||||||
args.rs = reg
|
self:push_new(branch, reg, 'AT', offset)
|
||||||
self:format_out(branch, args)
|
|
||||||
end
|
end
|
||||||
overrides.BNEI = overrides.BEQI
|
overrides.BNEI = overrides.BEQI
|
||||||
overrides.BEQIL = overrides.BEQI
|
overrides.BEQIL = overrides.BEQI
|
||||||
overrides.BNEIL = overrides.BEQI
|
overrides.BNEIL = overrides.BEQI
|
||||||
|
|
||||||
function overrides.BLTI(self, name)
|
function overrides.BLTI(self, name)
|
||||||
local slti = instructions['SLTI']
|
local branch = branch_basics[name]
|
||||||
local branch = instructions[branch_basics[name]]
|
local rs = self:pop('CPU')
|
||||||
local args = {}
|
local immediate = self:pop('CONST')
|
||||||
args.rs = self:register()
|
local offset = self:pop('REL'):set('signed')
|
||||||
self:optional_comma()
|
|
||||||
args.immediate = self:const()
|
|
||||||
self:optional_comma()
|
|
||||||
args.offset = self:token(self:const('relative')):set('signed')
|
|
||||||
|
|
||||||
if args.rs == 'AT' then
|
if rs == 'AT' then
|
||||||
self:error('register cannot be AT in this pseudo-instruction')
|
self:error('register cannot be AT in this pseudo-instruction')
|
||||||
end
|
end
|
||||||
|
|
||||||
args.rt = 'AT'
|
self:push_new('SLTI', 'AT', reg)
|
||||||
self:format_out(slti, args)
|
|
||||||
|
|
||||||
args.rs = 'AT'
|
self:push_new(branch, 'R0', 'AT', offset)
|
||||||
args.rt = 'R0'
|
|
||||||
self:format_out(branch, args)
|
|
||||||
end
|
end
|
||||||
overrides.BGEI = overrides.BLTI
|
overrides.BGEI = overrides.BLTI
|
||||||
overrides.BLTIL = overrides.BLTI
|
overrides.BLTIL = overrides.BLTI
|
||||||
|
@ -297,40 +238,28 @@ overrides.BGEIL = overrides.BLTI
|
||||||
|
|
||||||
function overrides.BLEI(self, name)
|
function overrides.BLEI(self, name)
|
||||||
-- TODO: this can probably be optimized
|
-- TODO: this can probably be optimized
|
||||||
local addiu = instructions['ADDIU']
|
local branch = branch_basics[name]
|
||||||
local slt = instructions['SLT']
|
local reg = self:pop('CPU')
|
||||||
local branch = instructions[branch_basics[name]]
|
local immediate = self:pop('CONST')
|
||||||
local beq = instructions['BEQ']
|
local offset = self:pop('REL'):set('signed')
|
||||||
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')
|
|
||||||
|
|
||||||
if reg == 'AT' then
|
if reg == 'AT' then
|
||||||
self:error('register cannot be AT in this pseudo-instruction')
|
self:error('register cannot be AT in this pseudo-instruction')
|
||||||
end
|
end
|
||||||
|
|
||||||
args.rt = 'AT'
|
self:push_new('ADDIU', 'AT', 'R0', immediate)
|
||||||
args.rs = 'R0'
|
|
||||||
self:format_out(addiu, args)
|
|
||||||
|
|
||||||
|
local beq_offset
|
||||||
if name == 'BLEI' then
|
if name == 'BLEI' then
|
||||||
args.offset = offset
|
beq_offset = offset
|
||||||
else
|
else
|
||||||
args.offset = 2 -- branch to delay slot of the next branch
|
beq_offset = 2 -- branch to delay slot of the next branch
|
||||||
end
|
end
|
||||||
args.rs = reg
|
self:push_new('BEQ', reg, 'R0', beq_offset)
|
||||||
self:format_out(beq, args)
|
|
||||||
|
|
||||||
args.rd = 'AT'
|
self:push_new('SLT', 'AT', reg)
|
||||||
self:format_out(slt, args)
|
|
||||||
|
|
||||||
args.rs = 'AT'
|
self:push_new(branch, 'AT', 'R0', offset)
|
||||||
args.rt = 'R0'
|
|
||||||
args.offset = offset
|
|
||||||
self:format_out(branch, args)
|
|
||||||
end
|
end
|
||||||
overrides.BGTI = overrides.BLEI
|
overrides.BGTI = overrides.BLEI
|
||||||
overrides.BLEIL = overrides.BLEI
|
overrides.BLEIL = overrides.BLEI
|
||||||
|
|
Loading…
Add table
Reference in a new issue