1
0
Fork 0
mirror of https://github.com/notwa/mm synced 2024-11-05 00:29:02 -08:00

update lips

This commit is contained in:
Connor Olding 2016-04-26 14:48:39 -07:00
parent aabb1499a6
commit 5691484000
12 changed files with 410 additions and 228 deletions

View file

@ -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

View file

@ -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:desym(t)
if t.tt == 'REL' then
local rel = label_delta(self:pc(), t.tok)
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')
self:error('branch too far', rel)
end
return rel % 0x10000
elseif type(t.tok) == 'number' then
if t.offset then
return t.tok + t.offset
end
function Dumper:desym(t)
-- 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
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))

View file

@ -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)
for b in string.gfind(data, '.') do
-- 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('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)

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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
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')
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.JR(self, name)
local rs = self:peek() and self:pop('CPU') or 'RA'
self:push_new('JR', rs)
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(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

View file

@ -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