1
0
Fork 0
mirror of https://github.com/notwa/lips synced 2024-11-14 10:09:03 -08:00

implement basic specials; more refactoring

This commit is contained in:
Connor Olding 2016-01-15 20:25:41 -08:00
parent 195236a52c
commit 07c68b6229
8 changed files with 171 additions and 131 deletions

View file

View file

@ -127,20 +127,20 @@ function Dumper:add_directive(fn, line, name, a, b)
end
end
function Dumper:desym(tok)
if type(tok[2]) == 'number' then
return tok[2]
elseif tok[1] == 'REG' then
assert(data.all_registers[tok[2]], 'Internal Error: unknown register')
return data.registers[tok[2]] or data.fpu_registers[tok[2]] or data.sys_registers[tok[2]]
elseif tok[1] == 'LABELSYM' then
local label = self.labels[tok[2]]
function Dumper:desym(t)
if type(t.tok) == 'number' then
return t.tok
elseif t.tt == 'REG' then
assert(data.all_registers[t.tok], 'Internal Error: unknown register')
return data.registers[t.tok] or data.fpu_registers[t.tok] or data.sys_registers[t.tok]
elseif t.tt == 'LABELSYM' then
local label = self.labels[t.tok]
if label == nil then
self:error('undefined label')
end
return label
elseif tok[1] == 'LABELREL' then
local label = self.labels[tok[2]]
elseif t.tt == 'LABELREL' then
local label = self.labels[t.tok]
if label == nil then
self:error('undefined label')
end
@ -155,31 +155,30 @@ function Dumper:desym(tok)
error('Internal Error: failed to desym')
end
function Dumper:toval(tok)
assert(type(tok) == 'table', 'Internal Error: invalid value')
assert(#tok == 2, 'Internal Error: invalid token')
function Dumper:toval(t)
assert(type(t) == 'table', 'Internal Error: invalid value')
local val = self:desym(tok)
local val = self:desym(t)
if tok.index then
if t.index then
val = val % 0x80000000
val = floor(val/4)
end
if tok.negate then
if t.negate then
val = -val
end
if tok.negate or tok.signed then
if t.negate or t.signed then
if val >= 0x10000 or val < -0x8000 then
self:error('value out of range')
end
val = val % 0x10000
end
if tok.portion == 'upper' then
if t.portion == 'upper' then
val = bitrange(val, 16, 31)
elseif tok.portion == 'lower' then
elseif t.portion == 'lower' then
val = bitrange(val, 0, 15)
elseif tok.portion == 'upperoff' then
elseif t.portion == 'upperoff' then
local upper = bitrange(val, 16, 31)
local lower = bitrange(val, 0, 15)
if lower >= 0x8000 then
@ -202,8 +201,8 @@ function Dumper:validate(n, bits)
end
end
function Dumper:valvar(tok, bits)
local val = self:toval(tok)
function Dumper:valvar(t, bits)
local val = self:toval(t)
self:validate(val, bits)
return val
end

View file

@ -3,6 +3,7 @@ local insert = table.insert
local data = require "lips.data"
local util = require "lips.util"
local Token = require "lips.Token"
local arg_types = {
NUM = true,
@ -19,14 +20,25 @@ function Muncher:error(msg)
error(format('%s:%d: Error: %s', self.fn, self.line, msg), 2)
end
function Muncher:token(t, val)
-- note: call Token directly if you want to specify fn and line manually
if type(t) == 'table' then
t.fn = self.fn
t.line = self.line
return Token(t)
else
return Token(self.fn, self.line, t, val)
end
end
function Muncher:advance()
self.i = self.i + 1
local t = self.tokens[self.i]
self.tt = t.tt
self.tok = t.tok
self.fn = t.fn
self.line = t.line
return t
self.t = self.tokens[self.i]
self.tt = self.t.tt
self.tok = self.t.tok
self.fn = self.t.fn
self.line = self.t.line
return self.t
end
function Muncher:is_EOL()
@ -52,31 +64,31 @@ function Muncher:number()
if self.tt ~= 'NUM' then
self:error('expected number')
end
local value = self.tok
local t = self.t
self:advance()
return value
return self:token(t)
end
function Muncher:string()
if self.tt ~= 'STRING' then
self:error('expected string')
end
local value = self.tok
local t = self.t
self:advance()
return value
return self:token(t)
end
function Muncher:register(t)
t = t or data.registers
function Muncher:register(registers)
registers = registers or data.registers
if self.tt ~= 'REG' then
self:error('expected register')
end
local reg = self.tok
if not t[reg] then
local t = self.t
if not registers[t.tok] then
self:error('wrong type of register')
end
self:advance()
return reg
return self:token(t)
end
function Muncher:deref()
@ -87,13 +99,13 @@ function Muncher:deref()
if self.tt ~= 'REG' then
self:error('expected register to dereference')
end
local reg = self.tok
local t = self.t
self:advance()
if self.tt ~= 'CLOSE' then
self:error('expected closing parenthesis for dereferencing')
end
self:advance()
return reg
return self:token(t)
end
function Muncher:const(relative, no_label)
@ -106,9 +118,9 @@ function Muncher:const(relative, no_label)
if relative and self.tt == 'LABELSYM' then
self.tt = 'LABELREL'
end
local t = {self.tt, self.tok}
local t = self.t
self:advance()
return t
return self:token(t)
end
function Muncher:special()
@ -133,7 +145,6 @@ function Muncher:special()
insert(args, arg)
elseif self.tt == 'CLOSE' then
insert(args, arg)
self:advance()
break
else
self:error('unexpected token in argument list')

View file

@ -3,14 +3,12 @@ local insert = table.insert
local data = require "lips.data"
local util = require "lips.util"
local overrides = require "lips.overrides"
local Token = require "lips.Token"
local Lexer = require "lips.Lexer"
local Dumper = require "lips.Dumper"
local Muncher = require "lips.Muncher"
local Preproc = require "lips.Preproc"
local construct = util.construct
local withflag = util.withflag
local Parser = util.Class(Muncher)
function Parser:init(writer, fn, options)
self.fn = fn or '(string)'
@ -26,7 +24,7 @@ function Parser:directive()
self.dumper:add_directive(self.fn, self.line, ...)
end
if name == 'ORG' then
add(name, self:number())
add(name, self:number().tok)
elseif name == 'ALIGN' or name == 'SKIP' then
if self:is_EOL() and name == 'ALIGN' then
add(name, 0)
@ -36,24 +34,24 @@ function Parser:directive()
add(name, size)
else
self:optional_comma()
add(name, size, self:number())
add(name, size, self:number().tok)
end
self:expect_EOL()
end
elseif name == 'BYTE' or name == 'HALFWORD' then
add(name, self:number())
add(name, self:number().tok)
while not self:is_EOL() do
self:advance()
self:optional_comma()
add(name, self:number())
add(name, self:number().tok)
end
self:expect_EOL()
elseif name == 'WORD' then -- allow labels in word directives
add(name, self:const()[2])
add(name, self:const().tok)
while not self:is_EOL() do
self:advance()
self:optional_comma()
add(name, self:const()[2])
add(name, self:const().tok)
end
self:expect_EOL()
elseif name == 'INC' then
@ -100,17 +98,17 @@ function Parser:format_in(informat)
elseif c == 'Z' and not args.rt then
args.rt = self:register(data.sys_registers)
elseif c == 'o' and not args.offset then
args.offset = withflag(self:const(), 'signed')
args.offset = Token(self:const()):set('signed')
elseif c == 'r' and not args.offset then
args.offset = withflag(self:const('relative'), 'signed')
args.offset = Token(self:const('relative')):set('signed')
elseif c == 'i' and not args.immediate then
args.immediate = self:const(nil, 'no label')
elseif c == 'I' and not args.index then
args.index = withflag(self:const(), 'index')
args.index = Token(self:const()):set('index')
elseif c == 'k' and not args.immediate then
args.immediate = withflag(self:const(nil, 'no label'), 'negate')
args.immediate = Token(self:const(nil, 'no label')):set('negate')
elseif c == 'K' and not args.immediate then
args.immediate = withflag(self:const(nil, 'no label'), 'signed')
args.immediate = Token(self:const(nil, 'no label')):set('signed')
elseif c == 'b' and not args.base then
args.base = self:deref()
else
@ -133,31 +131,31 @@ function Parser:format_out_raw(outformat, first, args, const, formatconst)
for i=1,#outformat do
local c = outformat:sub(i, i)
if c == 'd' then
out[#out+1] = construct(args.rd)
out[#out+1] = self:token(args.rd)
elseif c == 's' then
out[#out+1] = construct(args.rs)
out[#out+1] = self:token(args.rs)
elseif c == 't' then
out[#out+1] = construct(args.rt)
out[#out+1] = self:token(args.rt)
elseif c == 'D' then
out[#out+1] = construct(args.fd)
out[#out+1] = self:token(args.fd)
elseif c == 'S' then
out[#out+1] = construct(args.fs)
out[#out+1] = self:token(args.fs)
elseif c == 'T' then
out[#out+1] = construct(args.ft)
out[#out+1] = self:token(args.ft)
elseif c == 'o' then
out[#out+1] = construct(args.offset)
out[#out+1] = self:token(args.offset)
elseif c == 'i' then
out[#out+1] = construct(args.immediate)
out[#out+1] = self:token(args.immediate)
elseif c == 'I' then
out[#out+1] = construct(args.index)
out[#out+1] = self:token(args.index)
elseif c == 'b' then
out[#out+1] = construct(args.base)
out[#out+1] = self:token(args.base)
elseif c == '0' then
out[#out+1] = construct(0)
out[#out+1] = self:token(0)
elseif c == 'C' then
out[#out+1] = construct(const)
out[#out+1] = self:token(const)
elseif c == 'F' then
out[#out+1] = construct(formatconst)
out[#out+1] = self:token(formatconst)
end
end
local f = lookup[#outformat]
@ -193,9 +191,12 @@ function Parser:instruction()
local lui_args = {}
local addu_args = {}
local o = self:const()
args.offset = withflag({o[1], o[2]}, 'portion', 'lower')
if o[1] == 'LABELSYM' or o[2] >= 0x80000000 then
lui_args.immediate = withflag({o[1], o[2]}, 'portion', 'upperoff')
args.offset = self:token(o)
if not o.portion then
args.offset:set('portion', 'lower')
end
if not o.portion and (o.tt == 'LABELSYM' or o.tok >= 0x80000000) then
lui_args.immediate = Token(o):set('portion', 'upperoff')
lui_args.rt = 'AT'
self:format_out(lui, lui_args)
if not self:is_EOL() then
@ -236,11 +237,7 @@ function Parser:tokenize(asm)
end
assert(a, 'Internal Error: missing token')
local t = {}
t.tt = a
t.tok = b
t.fn = c
t.line = d
local t = Token(c, d, a, b)
insert(tokens, t)
if t.tt == 'EOF' and t.fn == self.main_fn then

View file

@ -3,6 +3,7 @@ local insert = table.insert
local data = require "lips.data"
local util = require "lips.util"
local Muncher = require "lips.Muncher"
local Token = require "lips.Token"
local Preproc = util.Class(Muncher)
function Preproc:init(options)
@ -35,7 +36,7 @@ function Preproc:process(tokens)
if tok == nil then
self:error('undefined define') -- uhhh nice wording
end
insert(new_tokens, {fn=t.fn, line=t.line, tt=tt, tok=tok})
insert(new_tokens, self:token(tt, tok))
elseif t.tt == 'RELLABEL' then
if t.tok == '+' then
insert(plus_labels, #new_tokens + 1)
@ -99,8 +100,6 @@ function Preproc:process(tokens)
self.i = 0
while self.i < #self.tokens do
local t = self:advance()
self.fn = t.fn
self.line = t.line
if t.tt == 'SPECIAL' then
local name, args = self:special()
-- TODO: split to its own file, not unlike overrides.lua
@ -108,22 +107,19 @@ function Preproc:process(tokens)
if #args ~= 1 then
self:error('%hi expected exactly one argument')
end
--local tnew = {fn=t.fn, line=t.line, tt='UPPEROFF', tok=args[1]}
self:error('unimplemented special')
local tnew = self:token(args[1]):set('portion', 'upperoff')
insert(new_tokens, tnew)
elseif name == 'up' then
if #args ~= 1 then
self:error('%up expected exactly one argument')
end
--local tnew = {fn=t.fn, line=t.line, tt='UPPER', tok=args[1]}
self:error('unimplemented special')
local tnew = self:token(args[1]):set('portion', 'upper')
insert(new_tokens, tnew)
elseif name == 'lo' then
if #args ~= 1 then
self:error('%lo expected exactly one argument')
end
self:error('unimplemented special')
--local tnew = {fn=t.fn, line=t.line, tt='LOWER', tok=args[1]}
local tnew = self:token(args[1]):set('portion', 'lower')
insert(new_tokens, tnew)
else
self:error('unknown special')

60
lips/Token.lua Normal file
View file

@ -0,0 +1,60 @@
local util = require "lips.util"
local Token = util.Class()
function Token:init(...)
local args = {...}
if #args == 1 then
local t = args[1]
if type(t) == 'table' then
for k, v in pairs(t) do
self[k] = v
end
end
elseif #args == 3 then
self.fn = args[1]
self.line = args[2]
local t = args[3]
if type(t) == 'table' then
self.tt = t[1]
self.tok = t[2]
elseif type(t) == 'string' then
self.tt = 'REG'
self.tok = t
elseif type(t) == 'number' then
self.tt = 'NUM'
self.tok = t
else
error('Internal Error: unknown type to construct', 3)
end
elseif #args == 4 then
self.fn = args[1]
self.line = args[2]
self.tt = args[3]
self.tok = args[4]
else
error('Internal Error: init takes 1, 3 or 4 arguments', 3)
end
if not self.fn then
error('Internal Error: tokens require a filename', 3)
end
if not self.line then
error('Internal Error: tokens require a line number', 3)
end
if not self.tt then
error('Internal Error: token is missing a type', 3)
end
if not self.tok then
error('Internal Error: token is missing a value', 3)
end
return self
end
function Token:set(key, value)
if value == nil then
value = true
end
self[key] = value
return self
end
return Token

View file

@ -4,7 +4,6 @@ local data = require "lips.data"
local util = require "lips.util"
local instructions = data.instructions
local withflag = util.withflag
local overrides = {}
-- note: "self" is an instance of Parser
@ -20,26 +19,33 @@ function overrides.LI(self, name)
-- for us, this is just semantics. for a "real" assembler,
-- LA could add appropriate RELO LUI/ADDIU directives.
if im[1] == 'LABELSYM' then
if im.tt == 'LABELSYM' then
self:error('use LA for labels')
end
im[2] = im[2] % 0x100000000
if im[2] >= 0x10000 and im[2] <= 0xFFFF8000 then
if im.portion then
args.rs = 'R0'
args.immediate = im
self:format_out(addiu, args)
return
end
im.tok = im.tok % 0x100000000
if im.tok >= 0x10000 and im.tok <= 0xFFFF8000 then
args.rs = args.rt
args.immediate = withflag(im, 'portion', 'upper')
args.immediate = self:token(im):set('portion', 'upper')
self:format_out(lui, args)
if im[2] % 0x10000 ~= 0 then
args.immediate = withflag(im, 'portion', 'lower')
if im.tok % 0x10000 ~= 0 then
args.immediate = self:token(im):set('portion', 'lower')
self:format_out(ori, args)
end
elseif im[2] >= 0x8000 and im[2] < 0x10000 then
elseif im.tok >= 0x8000 and im.tok < 0x10000 then
args.rs = 'R0'
args.immediate = withflag(im, 'portion', 'lower')
args.immediate = self:token(im):set('portion', 'lower')
self:format_out(ori, args)
else
args.rs = 'R0'
args.immediate = withflag(im, 'portion', 'lower')
args.immediate = self:token(im):set('portion', 'lower')
self:format_out(addiu, args)
end
end
@ -53,9 +59,9 @@ function overrides.LA(self, name)
local im = self:const()
args.rs = args.rt
args.immediate = withflag(im, 'portion', 'upperoff')
args.immediate = self:token(im):set('portion', 'upperoff')
self:format_out(lui, args)
args.immediate = withflag(im, 'portion', 'lower')
args.immediate = self:token(im):set('portion', 'lower')
self:format_out(addiu, args)
end
@ -87,7 +93,7 @@ function overrides.PUSH(self, name)
if name == 'PUSH' then
args.rt = 'SP'
args.rs = 'SP'
args.immediate = withflag(#stack*4, 'negate')
args.immediate = self:token(#stack*4):set('negate')
self:format_out(addi, args)
end
args.base = 'SP'
@ -241,7 +247,7 @@ function overrides.BEQI(self, name)
self:optional_comma()
args.immediate = self:const()
self:optional_comma()
args.offset = withflag(self:const('relative'), 'signed')
args.offset = self:token(self:const('relative')):set('signed')
if reg == 'AT' then
self:error('register cannot be AT in this pseudo-instruction')
@ -264,7 +270,7 @@ function overrides.BLTI(self, name)
self:optional_comma()
args.immediate = self:const()
self:optional_comma()
args.offset = withflag(self:const('relative'), 'signed')
args.offset = self:token(self:const('relative')):set('signed')
if args.rs == 'AT' then
self:error('register cannot be AT in this pseudo-instruction')
@ -290,7 +296,7 @@ function overrides.BLEI(self, name)
self:optional_comma()
args.immediate = self:const()
self:optional_comma()
local offset = withflag(self:const('relative'), 'signed')
local offset = self:token(self:const('relative')):set('signed')
if reg == 'AT' then
self:error('register cannot be AT in this pseudo-instruction')

View file

@ -16,33 +16,6 @@ local function Class(inherit)
return setmetatable(class, mt_class)
end
local function construct(t)
if type(t) == 'table' then
return t
elseif type(t) == 'string' then
return {'REG', t}
elseif type(t) == 'number' then
return {'NUM', t}
else
error('Internal Error: unknown type to construct')
end
end
local function withflag(t, key, value)
if type(t) == 'table' then
t = {t[1], t[2]}
else
t = construct(t)
end
if value == nil then
value = true
end
if key ~= nil then
t[key] = value
end
return t
end
local function readfile(fn)
local f = open(fn, 'r')
if not f then
@ -59,8 +32,6 @@ end
return {
Class = Class,
construct = construct,
withflag = withflag,
readfile = readfile,
bitrange = bitrange,
}