From 1266dfd18f1fa5ece1cc1862726372092204804a Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Thu, 21 Apr 2016 11:46:20 -0700 Subject: [PATCH] add .base directive; PC calculation --- README.md | 10 +++++--- lips/Collector.lua | 12 +++++++++- lips/Dumper.lua | 60 +++++++++++++++++++++++++++++----------------- lips/Token.lua | 1 + lips/data.lua | 2 +- lips/init.lua | 2 ++ 6 files changed, 60 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index dadeebf..bf6a8b1 100644 --- a/README.md +++ b/README.md @@ -216,9 +216,13 @@ if `fill` is omitted, no bytes are overwritten, and only the position is changed. * `.org {address}` -set the current address for writing to; seek. -until lips is a little more optimized, -be cautious of seeking to large addresses. +set the current address for writing to; seek to origin. + +* `.base {offset}` +sets a virtual offset relative to the origin (.org). +defaults to 0x80000000. +this allows you to have a PC value different from origin: +`PC = origin + base` * `HEX { ... }` write a series of bytes given in hexadecimal. diff --git a/lips/Collector.lua b/lips/Collector.lua index 34da262..3bd338d 100644 --- a/lips/Collector.lua +++ b/lips/Collector.lua @@ -83,7 +83,7 @@ function Collector:directive() local function add(kind, ...) insert(self.statements, self:statement('!'..kind, ...)) end - if name == 'ORG' then + if name == 'ORG' or name == 'BASE' then add(name, self:const(nil, 'no labels')) elseif name == 'ALIGN' or name == 'SKIP' then if self:is_EOL() and name == 'ALIGN' then @@ -199,6 +199,16 @@ function Collector:collect(tokens, fn) self.statements = {} + -- this works, but probably shouldn't be in this function specifically + if self.options.offset then + local s = Statement('(options)', 0, '!ORG', self.options.offset) + insert(self.statements, s) + end + if self.options.base then + local s = Statement('(options)', 0, '!BASE', self.options.base) + insert(self.statements, s) + end + self.i = 0 -- set up Muncher iteration self:advance() -- load up the first token while true do diff --git a/lips/Dumper.lua b/lips/Dumper.lua index 8163365..6a2d320 100644 --- a/lips/Dumper.lua +++ b/lips/Dumper.lua @@ -12,14 +12,22 @@ local Reader = require(path.."Reader") local bitrange = util.bitrange +local function label_delta(from, to) + -- TODO: consider removing the % here since .base should handle that now + to = to + from = from + return floor(to/4) - 1 - floor(from/4) +end + local Dumper = Reader:extend() function Dumper:init(writer, options) self.writer = writer self.options = options or {} self.labels = setmetatable({}, {__index=options.labels}) self.commands = {} - self.pos = options.offset or 0 self.lastcommand = nil + self.pos = 0 + self.base = 0 end function Dumper:export_labels(t) @@ -35,9 +43,7 @@ end function Dumper:desym(t) if t.tt == 'REL' then - local target = t.tok % 0x80000000 - local pos = self.pos % 0x80000000 - local rel = floor(target/4) - 1 - floor(pos/4) + local rel = label_delta(self:pc(), t.tok) if rel > 0x8000 or rel <= -0x8000 then self:error('branch too far') end @@ -62,9 +68,7 @@ function Dumper:desym(t) return label end - label = label % 0x80000000 - local pos = self.pos % 0x80000000 - local rel = floor(label/4) - 1 - floor(pos/4) + local rel = label_delta(self:pc(), label) if rel > 0x8000 or rel <= -0x8000 then self:error('branch too far') end @@ -79,7 +83,7 @@ function Dumper:validate(n, bits) self:error('value is nil') -- internal error? end if n > max or n < 0 then - self:error('value out of range', n) + self:error('value out of range', ("%X"):format(n)) end return n end @@ -267,22 +271,30 @@ function Dumper:fill(length, content) return s end +function Dumper:pc() + return self.pos + self.base +end + function Dumper:load(statements) - local pos = self.options.offset or 0 local new_statements = {} + self.pos = 0 + self.base = 0 for i=1, #statements do local s = statements[i] self.fn = s.fn self.line = s.line if s.type:sub(1, 1) == '!' then if s.type == '!LABEL' then - self.labels[s[1].tok] = pos + self.labels[s[1].tok] = self:pc() elseif s.type == '!DATA' then s.length = util.measure_data(s) -- cache for next pass - pos = pos + s.length + self.pos = self.pos + s.length insert(new_statements, s) elseif s.type == '!ORG' then - pos = s[1].tok + self.pos = s[1].tok + insert(new_statements, s) + elseif s.type == '!BASE' then + self.base = s[1].tok insert(new_statements, s) elseif s.type == '!ALIGN' or s.type == '!SKIP' then local length, content @@ -294,24 +306,24 @@ function Dumper:load(statements) else align = 2^align end - local temp = pos + align - 1 - length = temp - (temp % align) - pos + local temp = self:pc() + align - 1 + length = temp - (temp % align) - self:pc() else length = s[1] and s[1].tok or 0 content = s[2] and s[2].tok or nil end - pos = pos + length + self.pos = self.pos + length if content == nil then - local new = Statement(self.fn, self.line, '!ORG', pos) + local new = Statement(self.fn, self.line, '!ORG', self.pos) insert(new_statements, new) elseif length > 0 then insert(new_statements, self:fill(length, content)) elseif length < 0 then - local new = Statement(self.fn, self.line, '!ORG', pos) + local new = Statement(self.fn, self.line, '!ORG', self.pos) insert(new_statements, new) insert(new_statements, self:fill(length, content)) - local new = Statement(self.fn, self.line, '!ORG', pos) + local new = Statement(self.fn, self.line, '!ORG', self.pos) insert(new_statements, new) else -- length is 0, noop @@ -320,14 +332,16 @@ function Dumper:load(statements) error('Internal Error: unknown statement, got '..s.type) end else - pos = pos + 4 + self.pos = self.pos + 4 insert(new_statements, s) end end statements = new_statements + new_statements = {} - self.pos = self.options.offset or 0 + self.pos = 0 + self.base = 0 for i=1, #statements do local s = statements[i] self.fn = s.fn @@ -352,6 +366,8 @@ function Dumper:load(statements) elseif s.type == '!ORG' then self.pos = s[1].tok insert(new_statements, s) + elseif s.type == '!BASE' then + self.base = s[1].tok elseif s.type == '!LABEL' then -- noop else @@ -364,8 +380,8 @@ function Dumper:load(statements) end function Dumper:dump() - -- TODO: have options insert .org and/or .base; pos is always 0 at start - self.pos = self.options.offset or 0 + self.pos = 0 + self.base = nil for i, s in ipairs(self.statements) do if s.type == '!DATA' then for j, t in ipairs(s) do diff --git a/lips/Token.lua b/lips/Token.lua index 4b94a56..482b9f8 100644 --- a/lips/Token.lua +++ b/lips/Token.lua @@ -72,6 +72,7 @@ function Token:compute() assert(self.tt == 'NUM', 'Internal Error: cannot compute a non-number token') local n = self.tok if self.index then + -- TODO: should this still be here now that we have .base? n = n % 0x80000000 n = floor(n/4) end diff --git a/lips/data.lua b/lips/data.lua index 7d880e7..b693848 100644 --- a/lips/data.lua +++ b/lips/data.lua @@ -29,7 +29,7 @@ data.fpu_registers = { } data.all_directives = { - 'ORG', 'ALIGN', 'SKIP', + 'ORG', 'BASE', 'ALIGN', 'SKIP', 'ASCII', 'ASCIIZ', 'BYTE', 'HALFWORD', 'WORD', --'HEX', -- excluded here due to different syntax diff --git a/lips/init.lua b/lips/init.lua index 9ae6f28..ca762da 100644 --- a/lips/init.lua +++ b/lips/init.lua @@ -63,6 +63,8 @@ function lips.assemble(fn_or_asm, writer, options) end end + options.base = options.base or 0x80000000 + if options.unsafe then return main() else