mirror of
https://github.com/notwa/mm
synced 2024-11-05 00:29:02 -08:00
update lips
This commit is contained in:
parent
aabb1499a6
commit
5691484000
12 changed files with 410 additions and 228 deletions
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue