2016-01-13 11:18:36 -08:00
|
|
|
local insert = table.insert
|
2016-01-13 07:16:41 -08:00
|
|
|
|
2016-04-14 07:33:33 -07:00
|
|
|
local path = string.gsub(..., "[^.]+$", "")
|
|
|
|
local data = require(path.."data")
|
2016-04-20 16:23:44 -07:00
|
|
|
local Base = require(path.."Base")
|
2016-04-14 07:33:33 -07:00
|
|
|
local Token = require(path.."Token")
|
|
|
|
local Lexer = require(path.."Lexer")
|
2016-04-20 16:23:44 -07:00
|
|
|
local Collector = require(path.."Collector")
|
2016-04-14 07:33:33 -07:00
|
|
|
local Preproc = require(path.."Preproc")
|
2016-04-20 16:23:44 -07:00
|
|
|
local Dumper = require(path.."Dumper")
|
2016-01-13 07:16:41 -08:00
|
|
|
|
2016-04-20 16:23:44 -07:00
|
|
|
local Parser = Base:extend()
|
2016-01-13 07:16:41 -08:00
|
|
|
function Parser:init(writer, fn, options)
|
2016-04-20 20:51:26 -07:00
|
|
|
self.writer = writer
|
2016-01-13 07:16:41 -08:00
|
|
|
self.fn = fn or '(string)'
|
|
|
|
self.main_fn = self.fn
|
|
|
|
self.options = options or {}
|
2016-01-13 11:18:36 -08:00
|
|
|
end
|
|
|
|
|
2016-04-20 16:23:44 -07:00
|
|
|
--[[
|
2016-01-13 07:16:41 -08:00
|
|
|
function Parser:instruction()
|
|
|
|
local name = self.tok
|
|
|
|
local h = data.instructions[name]
|
2016-04-20 02:11:23 -07:00
|
|
|
assert(h, 'Internal Error: undefined instruction')
|
2016-01-13 07:16:41 -08:00
|
|
|
self:advance()
|
|
|
|
|
2016-04-20 02:11:23 -07:00
|
|
|
if overrides[name] then
|
2016-01-13 07:16:41 -08:00
|
|
|
overrides[name](self, name)
|
2016-01-13 14:31:58 -08:00
|
|
|
elseif h[2] == 'tob' then -- TODO: or h[2] == 'Tob' then
|
2016-04-10 02:59:39 -07:00
|
|
|
-- handle all the addressing modes for lw/sw-like instructions
|
2016-01-13 07:16:41 -08:00
|
|
|
local lui = data.instructions['LUI']
|
2016-01-13 14:31:58 -08:00
|
|
|
local addu = data.instructions['ADDU']
|
2016-01-13 07:16:41 -08:00
|
|
|
local args = {}
|
|
|
|
args.rt = self:register()
|
|
|
|
self:optional_comma()
|
2016-01-15 07:49:43 -08:00
|
|
|
if self.tt == 'OPEN' then
|
2016-01-15 11:15:02 -08:00
|
|
|
args.offset = 0
|
2016-01-13 14:31:58 -08:00
|
|
|
args.base = self:deref()
|
|
|
|
else -- NUM or LABELSYM
|
2016-01-13 07:16:41 -08:00
|
|
|
local lui_args = {}
|
2016-01-13 14:31:58 -08:00
|
|
|
local addu_args = {}
|
|
|
|
local o = self:const()
|
2016-04-10 02:59:39 -07:00
|
|
|
if self.tt == 'NUM' then
|
|
|
|
o:set('offset', self:const().tok)
|
|
|
|
end
|
2016-01-15 20:25:41 -08:00
|
|
|
args.offset = self:token(o)
|
|
|
|
if not o.portion then
|
|
|
|
args.offset:set('portion', 'lower')
|
|
|
|
end
|
2016-04-20 02:11:23 -07:00
|
|
|
-- attempt to use the fewest possible instructions for this offset
|
2016-01-15 20:25:41 -08:00
|
|
|
if not o.portion and (o.tt == 'LABELSYM' or o.tok >= 0x80000000) then
|
|
|
|
lui_args.immediate = Token(o):set('portion', 'upperoff')
|
2016-01-13 14:31:58 -08:00
|
|
|
lui_args.rt = 'AT'
|
|
|
|
self:format_out(lui, lui_args)
|
|
|
|
if not self:is_EOL() then
|
|
|
|
addu_args.rd = 'AT'
|
|
|
|
addu_args.rs = 'AT'
|
|
|
|
addu_args.rt = self:deref()
|
|
|
|
self:format_out(addu, addu_args)
|
|
|
|
end
|
|
|
|
args.base = 'AT'
|
|
|
|
else
|
|
|
|
args.base = self:deref()
|
2016-01-13 07:16:41 -08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
self:format_out(h, args)
|
|
|
|
elseif h[2] ~= nil then
|
|
|
|
local args = self:format_in(h[2])
|
|
|
|
self:format_out(h, args)
|
|
|
|
else
|
|
|
|
self:error('unimplemented instruction')
|
|
|
|
end
|
|
|
|
self:expect_EOL()
|
|
|
|
end
|
2016-04-20 16:23:44 -07:00
|
|
|
--]]
|
2016-01-13 07:16:41 -08:00
|
|
|
|
|
|
|
function Parser:tokenize(asm)
|
2016-04-20 02:43:01 -07:00
|
|
|
local lexer = Lexer(asm, self.main_fn, self.options)
|
2016-01-14 16:01:49 -08:00
|
|
|
local tokens = {}
|
2016-01-13 07:16:41 -08:00
|
|
|
|
2016-04-20 02:43:01 -07:00
|
|
|
local loop = true
|
|
|
|
while loop do
|
|
|
|
lexer:lex(function(tt, tok, fn, line)
|
|
|
|
assert(tt, 'Internal Error: missing token')
|
|
|
|
local t = Token(fn, line, tt, tok)
|
|
|
|
insert(tokens, t)
|
|
|
|
-- don't break if this is an included file's EOF
|
|
|
|
if tt == 'EOF' and fn == self.main_fn then
|
|
|
|
loop = false
|
|
|
|
end
|
|
|
|
end)
|
2016-01-13 07:16:41 -08:00
|
|
|
end
|
|
|
|
|
2016-04-20 17:47:58 -07:00
|
|
|
-- the lexer guarantees an EOL and EOF for a blank file
|
|
|
|
assert(#tokens > 0, 'Internal Error: no tokens after preprocessing')
|
|
|
|
|
2016-04-20 16:23:44 -07:00
|
|
|
local collector = Collector(self.options)
|
2016-04-20 20:51:26 -07:00
|
|
|
self.statements = collector:collect(tokens, self.main_fn)
|
2016-01-13 07:16:41 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
function Parser:parse(asm)
|
|
|
|
self:tokenize(asm)
|
2016-04-20 16:23:44 -07:00
|
|
|
|
2016-04-20 20:51:26 -07:00
|
|
|
local preproc = Preproc(self.options)
|
|
|
|
self.statements = preproc:process(self.statements)
|
|
|
|
|
2016-04-20 17:47:58 -07:00
|
|
|
--[[ process:
|
|
|
|
- inline labels? how do you know how far they are?
|
2016-04-20 20:51:26 -07:00
|
|
|
i guess you can just offset on statements instead
|
2016-04-20 17:47:58 -07:00
|
|
|
- assemble? dumper gets passed .org .base
|
|
|
|
--]]
|
|
|
|
|
2016-04-20 20:51:26 -07:00
|
|
|
local dumper = Dumper(self.writer, self.options)
|
|
|
|
self.statements = dumper:load(self.statements)
|
|
|
|
|
2016-04-20 16:23:44 -07:00
|
|
|
-- DEBUG
|
|
|
|
for i, s in ipairs(self.statements) do
|
|
|
|
local values = ''
|
|
|
|
for j, v in ipairs(s) do
|
2016-04-20 20:51:26 -07:00
|
|
|
values = values..'\t'..v.tt..'('..tostring(v.tok)..')'
|
2016-01-13 07:16:41 -08:00
|
|
|
end
|
2016-04-20 16:23:44 -07:00
|
|
|
values = values:sub(2)
|
|
|
|
print(i, s.type, values)
|
2016-01-13 07:16:41 -08:00
|
|
|
end
|
2016-04-20 20:51:26 -07:00
|
|
|
|
|
|
|
--if self.options.labels then
|
|
|
|
-- dumper:export_labels(self.options.labels)
|
|
|
|
--end
|
|
|
|
return dumper:dump()
|
2016-01-13 07:16:41 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
return Parser
|