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