1
0
Fork 0
mirror of https://github.com/notwa/mm synced 2024-06-28 12:57:12 -07:00

refactor to use classes; no hardcoded stuff

This commit is contained in:
Connor Olding 2015-11-20 15:27:22 -08:00
parent ebc9987a38
commit cd8ea7b3d7

View File

@ -1,9 +1,23 @@
-- i've lost control of my life -- i've lost control of my life
-- lexer and parser somewhat based on http://chunkbake.luaforge.net/ -- instructions: https://github.com/mikeryan/n64dev/tree/master/docs/n64ops
-- CajeASM style assembly; refer to the manual included with CajeASM.
-- lexer and parser are somewhat based on http://chunkbake.luaforge.net/
-- https://github.com/mikeryan/n64dev/tree/master/docs/n64ops local Class = Class or function(inherit)
-- cajeasm style assembly local class = {}
local mt_obj = {__index = class}
local mt_class = {
__call = function(self, ...)
local obj = setmetatable({}, mt_obj)
obj:init(...)
return obj
end,
__index = inherit,
}
return setmetatable(class, mt_class)
end
-- TODO: maybe support reg# style too -- TODO: maybe support reg# style too
local registers = { local registers = {
@ -370,8 +384,8 @@ local instruction_handlers = {
} }
at = nil at = nil
Lexer = {} local Lexer = Class()
function Lexer:setup(asm) function Lexer:init(asm)
self.asm = asm self.asm = asm
self.pos = 1 self.pos = 1
self.line = 1 self.line = 1
@ -379,6 +393,19 @@ function Lexer:setup(asm)
self:nextc() self:nextc()
end end
local Dumper = Class()
function Dumper:init(writer)
self.writer = writer
self.defines = {}
self.labels = {}
self.lines = {}
end
local Parser = Class()
function Parser:init(writer)
self.dumper = Dumper(writer)
end
function Lexer:error(msg) function Lexer:error(msg)
error(string.format('%s:%d: Error: %s', 'file.asm', self.line, msg), 2) error(string.format('%s:%d: Error: %s', 'file.asm', self.line, msg), 2)
end end
@ -582,14 +609,13 @@ function Lexer:lex()
end end
end end
Parser = {}
function Parser:error(msg) function Parser:error(msg)
error(string.format('%s:%d: Error: %s', 'file.asm', self.line, msg), 2) error(string.format('%s:%d: Error: %s', 'file.asm', self.line, msg), 2)
end end
function Parser:advance() function Parser:advance()
self.tt, self.tok = Lexer:lex() self.tt, self.tok = self.lexer:lex()
self.line = Lexer.line self.line = self.lexer.line
return self.tt, self.tok return self.tt, self.tok
end end
@ -625,21 +651,21 @@ function Parser:directive()
local name = self.tok local name = self.tok
self:advance() self:advance()
if name == 'ORG' then if name == 'ORG' then
Dumper:add_directive(name, self:number()) self.dumper:add_directive(name, self:number())
elseif name == 'ALIGN' or name == 'SKIP' then elseif name == 'ALIGN' or name == 'SKIP' then
local size = self:number() local size = self:number()
if self:optional_comma() then if self:optional_comma() then
Dumper:add_directive(name, size, self:number()) self.dumper:add_directive(name, size, self:number())
else else
Dumper:add_directive(name, size) self.dumper:add_directive(name, size)
end end
self:expect_EOL() self:expect_EOL()
elseif name == 'BYTE' or name == 'HALFWORD' or name == 'WORD' then elseif name == 'BYTE' or name == 'HALFWORD' or name == 'WORD' then
Dumper:add_directive(name, self:number()) self.dumper:add_directive(name, self:number())
while not self:is_EOL() do while not self:is_EOL() do
self:advance() self:advance()
self:optional_comma() self:optional_comma()
Dumper:add_directive(name, self:number()) self.dumper:add_directive(name, self:number())
end end
self:expect_EOL() self:expect_EOL()
elseif name == 'HEX' then elseif name == 'HEX' then
@ -706,14 +732,14 @@ function Parser:instruction()
self:optional_comma() self:optional_comma()
local offset = {'LOWER', self:const()} local offset = {'LOWER', self:const()}
local base = self:deref() local base = self:deref()
Dumper:add_instruction_5_5_16(h[1], base, rt, offset) self.dumper:add_instruction_5_5_16(h[1], base, rt, offset)
elseif h[2] == argtypes.bfo then elseif h[2] == argtypes.bfo then
-- OP ft, offset(base) -- OP ft, offset(base)
local ft = self:register(fpu_registers) local ft = self:register(fpu_registers)
self:optional_comma() self:optional_comma()
local offset = {'LOWER', self:const()} local offset = {'LOWER', self:const()}
local base = self:deref() local base = self:deref()
Dumper:add_instruction_5_5_16(h[1], base, ft, offset) self.dumper:add_instruction_5_5_16(h[1], base, ft, offset)
elseif h[2] == argtypes.sti then elseif h[2] == argtypes.sti then
-- OP rt, rs, immediate -- OP rt, rs, immediate
local rt = self:register() local rt = self:register()
@ -721,7 +747,7 @@ function Parser:instruction()
local rs = self:register() local rs = self:register()
self:optional_comma() self:optional_comma()
local immediate = {'LOWER', self:const()} local immediate = {'LOWER', self:const()}
Dumper:add_instruction_5_5_16(h[1], rs, rt, immediate) self.dumper:add_instruction_5_5_16(h[1], rs, rt, immediate)
elseif h[2] == argtypes.std then elseif h[2] == argtypes.std then
-- OP rd, rs, rt -- OP rd, rs, rt
local rd = self:register() local rd = self:register()
@ -730,14 +756,14 @@ function Parser:instruction()
self:optional_comma() self:optional_comma()
local rt = self:register() local rt = self:register()
local const = h[3] or self:error('internal error: expected const') local const = h[3] or self:error('internal error: expected const')
Dumper:add_instruction_5_5_5_11(h[1], rs, rt, rd, const) self.dumper:add_instruction_5_5_5_11(h[1], rs, rt, rd, const)
elseif h[2] == argtypes.st then elseif h[2] == argtypes.st then
-- OP rs, rt -- OP rs, rt
local rs = self:register() local rs = self:register()
self:optional_comma() self:optional_comma()
local rt = self:register() local rt = self:register()
local const = h[3] or self:error('internal error: expected const') local const = h[3] or self:error('internal error: expected const')
Dumper:add_instruction_5_5_16(h[1], rs, rt, const) self.dumper:add_instruction_5_5_16(h[1], rs, rt, const)
elseif h[2] == argtypes.tds then elseif h[2] == argtypes.tds then
local rd = self:register() local rd = self:register()
self:optional_comma() self:optional_comma()
@ -752,12 +778,12 @@ function Parser:instruction()
rs = self:const() rs = self:const()
end end
local const = h[3] or self:error('internal error: expected const') local const = h[3] or self:error('internal error: expected const')
Dumper:add_instruction_5_5_5_5_6(h[1], 0, rt, rd, rs, const) self.dumper:add_instruction_5_5_5_5_6(h[1], 0, rt, rd, rs, const)
elseif h[2] == argtypes.s then elseif h[2] == argtypes.s then
-- OP rs -- OP rs
local rs = self:register() local rs = self:register()
local const = h[3] or self:error('internal error: expected const') local const = h[3] or self:error('internal error: expected const')
Dumper:add_instruction_5_5_16(h[1], rs, 0, const) self.dumper:add_instruction_5_5_16(h[1], rs, 0, const)
elseif h[2] == argtypes.sto then elseif h[2] == argtypes.sto then
-- OP rs, rt, offset -- OP rs, rt, offset
local rs = self:register() local rs = self:register()
@ -765,7 +791,7 @@ function Parser:instruction()
local rt = self:register() local rt = self:register()
self:optional_comma() self:optional_comma()
local offset = self:const('relative') local offset = self:const('relative')
Dumper:add_instruction_5_5_16(h[1], rs, rt, offset) self.dumper:add_instruction_5_5_16(h[1], rs, rt, offset)
elseif h[2] == argtypes.stc then elseif h[2] == argtypes.stc then
-- OP TEQ rs, rt -- OP TEQ rs, rt
local rs = self:register() local rs = self:register()
@ -775,45 +801,45 @@ function Parser:instruction()
-- FIXME: there's supposed to be 'code' before const -- FIXME: there's supposed to be 'code' before const
-- but i dunno what it's supposed to be -- but i dunno what it's supposed to be
-- so i'm leaving it as zero here -- so i'm leaving it as zero here
Dumper:add_instruction_5_5_16(h[1], rs, rt, const) self.dumper:add_instruction_5_5_16(h[1], rs, rt, const)
elseif h[2] == argtypes.so then elseif h[2] == argtypes.so then
-- OP rs, offset -- OP rs, offset
local rs = self:register() local rs = self:register()
self:optional_comma() self:optional_comma()
local offset = self:const('relative') local offset = self:const('relative')
local const = h[3] or self:error('internal error: expected const') local const = h[3] or self:error('internal error: expected const')
Dumper:add_instruction_5_5_16(h[1], rs, const, offset) self.dumper:add_instruction_5_5_16(h[1], rs, const, offset)
elseif h[2] == argtypes.sync then elseif h[2] == argtypes.sync then
-- OP -- OP
local const = h[3] or self:error('internal error: expected const') local const = h[3] or self:error('internal error: expected const')
Dumper:add_instruction_26(h[1], const) self.dumper:add_instruction_26(h[1], const)
elseif h[2] == argtypes.indx then elseif h[2] == argtypes.indx then
-- OP target -- OP target
local target = {'INDEX', self:const()} local target = {'INDEX', self:const()}
Dumper:add_instruction_26(h[1], target) self.dumper:add_instruction_26(h[1], target)
elseif h[2] == argtypes.lui then elseif h[2] == argtypes.lui then
-- OP rt, immediate -- OP rt, immediate
local rt = self:register() local rt = self:register()
self:optional_comma() self:optional_comma()
local immediate = {'UPPER', self:const()} local immediate = {'UPPER', self:const()}
Dumper:add_instruction_5_5_16(h[1], 0, rt, immediate) self.dumper:add_instruction_5_5_16(h[1], 0, rt, immediate)
elseif h[2] == argtypes.mf then elseif h[2] == argtypes.mf then
-- OP rd -- OP rd
local rd = self:register() local rd = self:register()
local const = h[3] or self:error('internal error: expected const') local const = h[3] or self:error('internal error: expected const')
Dumper:add_instruction_5_5_5_5_6(h[1], 0, 0, rd, 0, const) self.dumper:add_instruction_5_5_5_5_6(h[1], 0, 0, rd, 0, const)
elseif h[2] == argtypes.jalr then elseif h[2] == argtypes.jalr then
-- OP rs, rd -- OP rs, rd
local rs = self:register() local rs = self:register()
self:optional_comma() self:optional_comma()
local rd = self:register() local rd = self:register()
local const = h[3] or self:error('internal error: expected const') local const = h[3] or self:error('internal error: expected const')
Dumper:add_instruction_5_5_5_5_6(h[1], rs, 0, rd, 0, const) self.dumper:add_instruction_5_5_5_5_6(h[1], rs, 0, rd, 0, const)
local rd = self:register() local rd = self:register()
elseif h[2] == argtypes.code then elseif h[2] == argtypes.code then
-- OP -- OP
local const = h[3] or self:error('internal error: expected const') local const = h[3] or self:error('internal error: expected const')
Dumper:add_instruction_26(h[1], const) self.dumper:add_instruction_26(h[1], const)
elseif h[2] == argtypes.movf then elseif h[2] == argtypes.movf then
local rt = self:register() local rt = self:register()
self:optional_comma() self:optional_comma()
@ -826,7 +852,7 @@ function Parser:instruction()
rd = self:register(fpu_registers) rd = self:register(fpu_registers)
end end
local const = h[3] or self:error('internal error: expected const') local const = h[3] or self:error('internal error: expected const')
Dumper:add_instruction_5_5_5_5_6(h[1], const, rt, rd, 0, 0) self.dumper:add_instruction_5_5_5_5_6(h[1], const, rt, rd, 0, 0)
elseif h[2] == argtypes.tsdf then elseif h[2] == argtypes.tsdf then
-- OP fd, fs, ft -- OP fd, fs, ft
local fd = self:register(fpu_registers) local fd = self:register(fpu_registers)
@ -835,7 +861,7 @@ function Parser:instruction()
self:optional_comma() self:optional_comma()
local ft = self:register(fpu_registers) local ft = self:register(fpu_registers)
local const = h[3] or self:error('internal error: expected const') local const = h[3] or self:error('internal error: expected const')
Dumper:add_instruction_5_5_5_5_6(h[1], 16, ft, fs, fd, const) self.dumper:add_instruction_5_5_5_5_6(h[1], 16, ft, fs, fd, const)
elseif h[2] == argtypes.tsdd then elseif h[2] == argtypes.tsdd then
local fd = self:register(fpu_registers) local fd = self:register(fpu_registers)
self:optional_comma() self:optional_comma()
@ -843,7 +869,7 @@ function Parser:instruction()
self:optional_comma() self:optional_comma()
local ft = self:register(fpu_registers) local ft = self:register(fpu_registers)
local const = h[3] or self:error('internal error: expected const') local const = h[3] or self:error('internal error: expected const')
Dumper:add_instruction_5_5_5_5_6(h[1], 17, ft, fs, fd, const) self.dumper:add_instruction_5_5_5_5_6(h[1], 17, ft, fs, fd, const)
else else
self:error('TODO') self:error('TODO')
end end
@ -852,8 +878,7 @@ end
function Parser:parse(asm) function Parser:parse(asm)
self.asm = asm self.asm = asm
Lexer:setup(asm) self.lexer = Lexer(asm)
Dumper:setup()
self:advance() self:advance()
while self.tt ~= 'EOF' do while self.tt ~= 'EOF' do
@ -863,11 +888,11 @@ function Parser:parse(asm)
elseif self.tt == 'DEF' then elseif self.tt == 'DEF' then
local name = self.tok local name = self.tok
self:advance() self:advance()
Dumper:add_define(name, self:number()) self.dumper:add_define(name, self:number())
elseif self.tt == 'DIR' then elseif self.tt == 'DIR' then
self:directive() self:directive()
elseif self.tt == 'LABEL' then elseif self.tt == 'LABEL' then
Dumper:add_label(self.tok) self.dumper:add_label(self.tok)
self:advance() self:advance()
elseif self.tt == 'INSTR' then elseif self.tt == 'INSTR' then
self:instruction() self:instruction()
@ -876,18 +901,13 @@ function Parser:parse(asm)
end end
end end
return Dumper:dump() return self.dumper:dump()
end
Dumper = {}
function Dumper:setup()
self.defines = {}
self.labels = {}
self.lines = {}
end end
function Dumper:error(msg) function Dumper:error(msg)
error(string.format('Internal Error: %s', msg), 2) -- TODO: sometimes internal error, sometimes not.
-- also, we should pass line numbers down to add_instruction.
error(string.format('Dumping Error: %s', msg), 2)
end end
function Dumper:push(t) function Dumper:push(t)
@ -936,7 +956,7 @@ function Dumper:add_directive(...)
end end
function Dumper:print(uw, lw) function Dumper:print(uw, lw)
print(('%04X%04X'):format(uw, lw)) self.writer(('%04X%04X'):format(uw, lw))
end end
function Dumper:desym(tok) function Dumper:desym(tok)
@ -961,7 +981,7 @@ function Dumper:desym(tok)
end end
return val return val
end end
print(tok) --print(tok)
self:error('failed to desym') self:error('failed to desym')
end end
@ -975,7 +995,7 @@ function Dumper:toval(tok)
end end
if type(tok) == 'table' then if type(tok) == 'table' then
if #tok ~= 2 then if #tok ~= 2 then
print('toval', tok) --print('toval', tok)
self:error('invalid token') self:error('invalid token')
end end
if tok[1] == 'UPPER' then if tok[1] == 'UPPER' then
@ -995,13 +1015,13 @@ function Dumper:toval(tok)
else else
val = self:desym(tok[2])*4 val = self:desym(tok[2])*4
end end
print('(index)', val) --print('(index)', val)
return val return val
else else
return self:desym(tok) return self:desym(tok)
end end
end end
print('toval', tok) --print('toval', tok)
self:error('invalid value') self:error('invalid value')
end end
@ -1011,7 +1031,7 @@ function Dumper:validate(n, bits)
self:error('value is nil') self:error('value is nil')
end end
if n > max or n < 0 then if n > max or n < 0 then
print(("n %08X"):format(math.abs(n))) --print(("n %08X"):format(math.abs(n)))
self:error('value out of range') self:error('value out of range')
end end
end end
@ -1087,24 +1107,24 @@ function Dumper:dump()
end end
end end
function main() function assemble(fn, writer)
local asm = '' -- assemble a MIPS R4300i assembly file
-- returns error message on error, or nil on success
writer = writer or io.write
local f = io.open('Moonjump 2.asm', 'r') local asm = ''
local f = io.open(fn, 'r')
if not f then if not f then
f = io.open('inject/Moonjump 2.asm', 'r') error('could not read assembly file', 1)
if not f then
error('could not load assembly', 1)
return
end
end end
asm = f:read('*a') asm = f:read('*a')
f:close() f:close()
Parser:parse(asm) function main()
end local p = Parser(writer)
return p:parse(asm)
end
local ok, msg = pcall(main) local ok, err = pcall(main)
if not ok then return err
print(msg)
end end