mirror of
https://github.com/notwa/lips
synced 2025-03-09 19:32:49 -07:00
add .base directive; PC calculation
This commit is contained in:
parent
e25ee2013c
commit
1266dfd18f
6 changed files with 60 additions and 27 deletions
10
README.md
10
README.md
|
@ -216,9 +216,13 @@ if `fill` is omitted, no bytes are overwritten,
|
||||||
and only the position is changed.
|
and only the position is changed.
|
||||||
|
|
||||||
* `.org {address}`
|
* `.org {address}`
|
||||||
set the current address for writing to; seek.
|
set the current address for writing to; seek to origin.
|
||||||
until lips is a little more optimized,
|
|
||||||
be cautious of seeking to large addresses.
|
* `.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 { ... }`
|
* `HEX { ... }`
|
||||||
write a series of bytes given in hexadecimal.
|
write a series of bytes given in hexadecimal.
|
||||||
|
|
|
@ -83,7 +83,7 @@ function Collector:directive()
|
||||||
local function add(kind, ...)
|
local function add(kind, ...)
|
||||||
insert(self.statements, self:statement('!'..kind, ...))
|
insert(self.statements, self:statement('!'..kind, ...))
|
||||||
end
|
end
|
||||||
if name == 'ORG' then
|
if name == 'ORG' or name == 'BASE' then
|
||||||
add(name, self:const(nil, 'no labels'))
|
add(name, self:const(nil, 'no labels'))
|
||||||
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
|
||||||
|
@ -199,6 +199,16 @@ function Collector:collect(tokens, fn)
|
||||||
|
|
||||||
self.statements = {}
|
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.i = 0 -- set up Muncher iteration
|
||||||
self:advance() -- load up the first token
|
self:advance() -- load up the first token
|
||||||
while true do
|
while true do
|
||||||
|
|
|
@ -12,14 +12,22 @@ 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
|
||||||
self.options = options or {}
|
self.options = options or {}
|
||||||
self.labels = setmetatable({}, {__index=options.labels})
|
self.labels = setmetatable({}, {__index=options.labels})
|
||||||
self.commands = {}
|
self.commands = {}
|
||||||
self.pos = options.offset or 0
|
|
||||||
self.lastcommand = nil
|
self.lastcommand = nil
|
||||||
|
self.pos = 0
|
||||||
|
self.base = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function Dumper:export_labels(t)
|
function Dumper:export_labels(t)
|
||||||
|
@ -35,9 +43,7 @@ end
|
||||||
|
|
||||||
function Dumper:desym(t)
|
function Dumper:desym(t)
|
||||||
if t.tt == 'REL' then
|
if t.tt == 'REL' then
|
||||||
local target = t.tok % 0x80000000
|
local rel = label_delta(self:pc(), t.tok)
|
||||||
local pos = self.pos % 0x80000000
|
|
||||||
local rel = floor(target/4) - 1 - floor(pos/4)
|
|
||||||
if rel > 0x8000 or rel <= -0x8000 then
|
if rel > 0x8000 or rel <= -0x8000 then
|
||||||
self:error('branch too far')
|
self:error('branch too far')
|
||||||
end
|
end
|
||||||
|
@ -62,9 +68,7 @@ function Dumper:desym(t)
|
||||||
return label
|
return label
|
||||||
end
|
end
|
||||||
|
|
||||||
label = label % 0x80000000
|
local rel = label_delta(self:pc(), label)
|
||||||
local pos = self.pos % 0x80000000
|
|
||||||
local rel = floor(label/4) - 1 - floor(pos/4)
|
|
||||||
if rel > 0x8000 or rel <= -0x8000 then
|
if rel > 0x8000 or rel <= -0x8000 then
|
||||||
self:error('branch too far')
|
self:error('branch too far')
|
||||||
end
|
end
|
||||||
|
@ -79,7 +83,7 @@ function Dumper:validate(n, bits)
|
||||||
self:error('value is nil') -- internal error?
|
self:error('value is nil') -- internal error?
|
||||||
end
|
end
|
||||||
if n > max or n < 0 then
|
if n > max or n < 0 then
|
||||||
self:error('value out of range', n)
|
self:error('value out of range', ("%X"):format(n))
|
||||||
end
|
end
|
||||||
return n
|
return n
|
||||||
end
|
end
|
||||||
|
@ -267,22 +271,30 @@ function Dumper:fill(length, content)
|
||||||
return s
|
return s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Dumper:pc()
|
||||||
|
return self.pos + self.base
|
||||||
|
end
|
||||||
|
|
||||||
function Dumper:load(statements)
|
function Dumper:load(statements)
|
||||||
local pos = self.options.offset or 0
|
|
||||||
local new_statements = {}
|
local new_statements = {}
|
||||||
|
self.pos = 0
|
||||||
|
self.base = 0
|
||||||
for i=1, #statements do
|
for i=1, #statements do
|
||||||
local s = statements[i]
|
local s = statements[i]
|
||||||
self.fn = s.fn
|
self.fn = s.fn
|
||||||
self.line = s.line
|
self.line = s.line
|
||||||
if s.type:sub(1, 1) == '!' then
|
if s.type:sub(1, 1) == '!' then
|
||||||
if s.type == '!LABEL' then
|
if s.type == '!LABEL' then
|
||||||
self.labels[s[1].tok] = pos
|
self.labels[s[1].tok] = self:pc()
|
||||||
elseif s.type == '!DATA' then
|
elseif s.type == '!DATA' then
|
||||||
s.length = util.measure_data(s) -- cache for next pass
|
s.length = util.measure_data(s) -- cache for next pass
|
||||||
pos = pos + s.length
|
self.pos = self.pos + s.length
|
||||||
insert(new_statements, s)
|
insert(new_statements, s)
|
||||||
elseif s.type == '!ORG' then
|
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)
|
insert(new_statements, s)
|
||||||
elseif s.type == '!ALIGN' or s.type == '!SKIP' then
|
elseif s.type == '!ALIGN' or s.type == '!SKIP' then
|
||||||
local length, content
|
local length, content
|
||||||
|
@ -294,24 +306,24 @@ function Dumper:load(statements)
|
||||||
else
|
else
|
||||||
align = 2^align
|
align = 2^align
|
||||||
end
|
end
|
||||||
local temp = pos + align - 1
|
local temp = self:pc() + align - 1
|
||||||
length = temp - (temp % align) - pos
|
length = temp - (temp % align) - self:pc()
|
||||||
else
|
else
|
||||||
length = s[1] and s[1].tok or 0
|
length = s[1] and s[1].tok or 0
|
||||||
content = s[2] and s[2].tok or nil
|
content = s[2] and s[2].tok or nil
|
||||||
end
|
end
|
||||||
|
|
||||||
pos = pos + length
|
self.pos = self.pos + length
|
||||||
if content == nil then
|
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)
|
insert(new_statements, new)
|
||||||
elseif length > 0 then
|
elseif length > 0 then
|
||||||
insert(new_statements, self:fill(length, content))
|
insert(new_statements, self:fill(length, content))
|
||||||
elseif length < 0 then
|
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, new)
|
||||||
insert(new_statements, self:fill(length, content))
|
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)
|
insert(new_statements, new)
|
||||||
else
|
else
|
||||||
-- length is 0, noop
|
-- length is 0, noop
|
||||||
|
@ -320,14 +332,16 @@ function Dumper:load(statements)
|
||||||
error('Internal Error: unknown statement, got '..s.type)
|
error('Internal Error: unknown statement, got '..s.type)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
pos = pos + 4
|
self.pos = self.pos + 4
|
||||||
insert(new_statements, s)
|
insert(new_statements, s)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
statements = new_statements
|
statements = new_statements
|
||||||
|
|
||||||
new_statements = {}
|
new_statements = {}
|
||||||
self.pos = self.options.offset or 0
|
self.pos = 0
|
||||||
|
self.base = 0
|
||||||
for i=1, #statements do
|
for i=1, #statements do
|
||||||
local s = statements[i]
|
local s = statements[i]
|
||||||
self.fn = s.fn
|
self.fn = s.fn
|
||||||
|
@ -352,6 +366,8 @@ function Dumper:load(statements)
|
||||||
elseif s.type == '!ORG' then
|
elseif s.type == '!ORG' then
|
||||||
self.pos = s[1].tok
|
self.pos = s[1].tok
|
||||||
insert(new_statements, s)
|
insert(new_statements, s)
|
||||||
|
elseif s.type == '!BASE' then
|
||||||
|
self.base = s[1].tok
|
||||||
elseif s.type == '!LABEL' then
|
elseif s.type == '!LABEL' then
|
||||||
-- noop
|
-- noop
|
||||||
else
|
else
|
||||||
|
@ -364,8 +380,8 @@ function Dumper:load(statements)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Dumper:dump()
|
function Dumper:dump()
|
||||||
-- TODO: have options insert .org and/or .base; pos is always 0 at start
|
self.pos = 0
|
||||||
self.pos = self.options.offset or 0
|
self.base = nil
|
||||||
for i, s in ipairs(self.statements) do
|
for i, s in ipairs(self.statements) do
|
||||||
if s.type == '!DATA' then
|
if s.type == '!DATA' then
|
||||||
for j, t in ipairs(s) do
|
for j, t in ipairs(s) do
|
||||||
|
|
|
@ -72,6 +72,7 @@ function Token:compute()
|
||||||
assert(self.tt == 'NUM', 'Internal Error: cannot compute a non-number token')
|
assert(self.tt == 'NUM', 'Internal Error: cannot compute a non-number token')
|
||||||
local n = self.tok
|
local n = self.tok
|
||||||
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
|
||||||
|
|
|
@ -29,7 +29,7 @@ data.fpu_registers = {
|
||||||
}
|
}
|
||||||
|
|
||||||
data.all_directives = {
|
data.all_directives = {
|
||||||
'ORG', 'ALIGN', 'SKIP',
|
'ORG', 'BASE', 'ALIGN', 'SKIP',
|
||||||
'ASCII', 'ASCIIZ',
|
'ASCII', 'ASCIIZ',
|
||||||
'BYTE', 'HALFWORD', 'WORD',
|
'BYTE', 'HALFWORD', 'WORD',
|
||||||
--'HEX', -- excluded here due to different syntax
|
--'HEX', -- excluded here due to different syntax
|
||||||
|
|
|
@ -63,6 +63,8 @@ function lips.assemble(fn_or_asm, writer, options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
options.base = options.base or 0x80000000
|
||||||
|
|
||||||
if options.unsafe then
|
if options.unsafe then
|
||||||
return main()
|
return main()
|
||||||
else
|
else
|
||||||
|
|
Loading…
Add table
Reference in a new issue