Added optional far keyword with encapsulation for far calls of not yet declared labels.

This commit is contained in:
g012 2017-10-03 22:55:58 +02:00
parent 27887f42c6
commit 462c4a3b8d
5 changed files with 114 additions and 58 deletions

View File

@ -6,6 +6,8 @@ local sections={} M.sections=sections
local relations={} M.relations=relations
local stats={} M.stats=stats setmetatable(stats, stats)
local before_link={} M.before_link=before_link
M.strip = true -- set to false to disable dead stripping of relocatable sections
M.pcall = pcall -- set to empty function returning false to disable eval during compute_size()
-- set to pcall directly if you want to keep ldazab/x/y eval during compute_size() even if
@ -30,6 +32,8 @@ local id = function() id_=id_+1 return id_ end M.id=id
M.link = function()
if stats.unused then return end
for _,v in ipairs(before_link) do v() end
if M.strip then
symbols.__index = function(tab,key)
local val = rawget(symbols, key)

121
l65.lua
View File

@ -76,6 +76,7 @@ local function opcode_arg_encapsulate(on)
end
opcode_arg_encapsulate(true)
local opcode_encapsulate = {} -- additionnal opcode, to have basic encapsulation (function(a) return a end)
local opcode_implied = lookupify{
'asl', 'brk', 'clc', 'cld', 'cli', 'clv', 'dex', 'dey',
'inx', 'iny', 'lsr', 'nop', 'pha', 'php', 'pla', 'plp',
@ -152,6 +153,21 @@ local opcode_relative = lookupify{
'bcc', 'bcs', 'beq', 'bmi', 'bne', 'bpl', 'bvc', 'bvs',
}
local addressing_map = {
imp = opcode_implied,
imm = opcode_immediate,
zpg = opcode_zeropage,
zpx = opcode_zeropage_x,
zpy = opcode_zeropage_y,
abs = opcode_absolute,
abx = opcode_absolute_x,
aby = opcode_absolute_y,
ind = opcode_indirect,
inx = opcode_indirect_x,
iny = opcode_indirect_y,
rel = opcode_relative,
}
local Scope = {
new = function(self, parent)
local s = {
@ -459,12 +475,13 @@ local function LexLua(src)
if char == 1 and peek_n(7) == '#pragma' then
get_n(7)
local dat,opt = get_word()
local onoff = function(f)
local onoff = function(f, noerr)
opt = get_word()
if opt == 'on' then f(true)
elseif opt == 'off' then f(false)
else generateError("invalid option for pragma " .. dat .. ", expected: [on,off]")
elseif not noerr then generateError("invalid option for pragma " .. dat .. ", expected: [on,off]")
end
return opt
end
if dat == 'syntax6502' then
onoff(syntax6502)
@ -473,8 +490,23 @@ local function LexLua(src)
onoff(function() end)
toEmit = {Type = 'Keyword', Data = 'syntax6502k_' .. opt}
elseif dat == 'encapsulate' then
onoff(function() end)
toEmit = {Type = 'Keyword', Data = 'encapsulate_' .. opt}
local opcode = onoff(function() end, true)
if opcode then
opcode_encapsulate[opcode] = get_word()
table.insert(Keywords_6502, opcode)
Keywords[opcode] = syntax6502_on
toEmit = {Type = 'Symbol', Data = ';'}
else
toEmit = {Type = 'Keyword', Data = 'encapsulate_' .. opt}
end
elseif dat == 'add_opcode' then
local opcode,addressing = get_word(),get_word()
local map = addressing_map[addressing]
if not map then generateError("invalid addressing for pragma add_opcode: " .. addressing .. " (opcode: " .. opcode .. ")") end
map[opcode] = true
table.insert(Keywords_6502, opcode)
Keywords[opcode] = syntax6502_on
toEmit = {Type = 'Symbol', Data = ';'}
else generateError("unknown pragma: " .. dat)
end
@ -1271,32 +1303,44 @@ local function ParseLua(src, src_name)
end
elseif #args > 0 and ( (encapsulate and not inverse_encapsulate) or (not encapsulate and inverse_encapsulate) ) and not no_encapsulation[args[1].AstType] then
-- opcode arguments of type (late, early), where only late is to be encapsulated with _o parameter to be set to early and added to _f(late)
local inner_call_scope = CreateScope(op_var.Variable.Scope)
local fvarexpr = {
AstType='VarExpr', Name='_f', Variable={ Name='_f', Scope=CreateScope(inner_call_scope) }, Tokens = { t('Ident', '_f') }
}
local inner_add = {
AstType='BinopExpr', Op='+', OperatorPrecedence=10, Tokens={ t('Symbol', '+') },
Lhs = {
AstType='VarExpr', Name='_o', Variable={ IsGlobal=false, Name='_o', Scope=inner_call_scope }, Tokens={ t('Ident', '_o', {space}) }
},
Rhs = {
AstType = 'CallExpr', Base = fvarexpr, Arguments = {args[1]}, Tokens = { t('Symbol', '('), t('Symbol', ')') }
local inner_call_scope,inner_call = CreateScope(op_var.Variable.Scope)
if params.basic then
local inner_call_body = {
AstType='StatList', Scope=CreateScope(inner_call_scope), Tokens={}, Body={
{ AstType='ReturnStatement', Arguments={args[1]}, Tokens={ t('Keyword', 'return', {space}) } }
}
}
}
local inner_call_body = {
AstType='StatList', Scope=CreateScope(inner_call_scope), Tokens={}, Body={
{ AstType='ReturnStatement', Arguments={inner_add}, Tokens={ t('Keyword', 'return', {space}) } }
inner_call = {
AstType='Function', VarArg=false, IsLocal=true, Scope=inner_call_scope, Body=inner_call_body, Arguments={},
Tokens={ t('Keyword', 'function'), t('Symbol', '('), t('Symbol', ')'), t('Keyword', 'end', {space}) }
}
}
local inner_call = {
AstType='Function', VarArg=false, IsLocal=true, Scope=inner_call_scope, Body=inner_call_body,
Arguments={
{ IsGlobal=false, Name='_o', Scope=inner_call_scope },
{ IsGlobal=false, Name='_f', Scope=inner_call_scope },
},
Tokens={ t('Keyword', 'function'), t('Symbol', '('), t('Ident', '_o'), t('Symbol', ','), t('Ident', '_f'), t('Symbol', ')'), t('Keyword', 'end', {space}) }
}
else
local fvarexpr = {
AstType='VarExpr', Name='_f', Variable={ Name='_f', Scope=CreateScope(inner_call_scope) }, Tokens = { t('Ident', '_f') }
}
local inner_add = {
AstType='BinopExpr', Op='+', OperatorPrecedence=10, Tokens={ t('Symbol', '+') },
Lhs = {
AstType='VarExpr', Name='_o', Variable={ IsGlobal=false, Name='_o', Scope=inner_call_scope }, Tokens={ t('Ident', '_o', {space}) }
},
Rhs = {
AstType = 'CallExpr', Base = fvarexpr, Arguments = {args[1]}, Tokens = { t('Symbol', '('), t('Symbol', ')') }
}
}
local inner_call_body = {
AstType='StatList', Scope=CreateScope(inner_call_scope), Tokens={}, Body={
{ AstType='ReturnStatement', Arguments={inner_add}, Tokens={ t('Keyword', 'return', {space}) } }
}
}
inner_call = {
AstType='Function', VarArg=false, IsLocal=true, Scope=inner_call_scope, Body=inner_call_body,
Arguments={
{ IsGlobal=false, Name='_o', Scope=inner_call_scope },
{ IsGlobal=false, Name='_f', Scope=inner_call_scope },
},
Tokens={ t('Keyword', 'function'), t('Symbol', '('), t('Ident', '_o'), t('Symbol', ','), t('Ident', '_f'), t('Symbol', ')'), t('Keyword', 'end', {space}) }
}
end
args[1] = inner_call
end
local exp_call = {
@ -1484,7 +1528,14 @@ local function ParseLua(src, src_name)
local mod_st, mod_expr, inverse_encapsulate
for _,op in pairs(Keywords_6502) do
if tok:ConsumeKeyword(op, tokenList) then
if opcode_relative[op] then
if opcode_encapsulate[op] then
inverse_encapsulate = tok:ConsumeSymbol('!', tokenList)
local st, expr = ParseExpr(scope) if not st then return false, expr end
local paren_open_whites = {}
if inverse_encapsulate then for _,v in ipairs(tokenList[#tokenList-1].LeadingWhite) do table.insert(paren_open_whites, v) end end
for _,v in ipairs(tokenList[#tokenList].LeadingWhite) do table.insert(paren_open_whites, v) end
stat = emit_call{name=opcode_encapsulate[op], args={expr}, inverse_encapsulate=inverse_encapsulate, paren_open_white=paren_open_whites, basic=true} break
elseif opcode_relative[op] then
local st, expr = ParseExpr(scope) if not st then return false, expr end
if expr.AstType == 'VarExpr' and expr.Variable.IsGlobal then
expr = as_string_expr(expr, expr.Name)
@ -2490,6 +2541,8 @@ l65.report = function(success, ...)
os.exit(-1)
end
l65.msghandler = function(msg)
local o = function(s) io.stderr:write(s) end
msg = tostring(msg)
msg = msg:gsub('%[string "(.-%.l65)"%]', '%1') -- [string "xxx.l65"] -> xxx.l65
local trace_cur = debug.traceback(nil, 2)
@ -2503,7 +2556,6 @@ l65.msghandler = function(msg)
local n,v = debug.getlocal(i, j)
if not n then break end
if n == 'l65dbg' then
local o = function(s) io.stderr:write(s) end
o(string.format("%s\n", msg))
if trace_cur:find("in local 'late'") then
local lines = {}
@ -2511,15 +2563,12 @@ l65.msghandler = function(msg)
if line:find("in local 'late'") then break end
table.insert(lines, line)
end
lines = table.concat(lines,'\n')
lines = lines:gsub("^%s*\r?\n$", '')
io.stderr:write(lines .. '\n')
o(table.concat(lines,'\n'))
end
local trace = v.trace:match(".-\n(.*)\n.-'xpcall'")
trace = trace:gsub('%[string "(.-%.l65)"%]', '%1')
trace = trace:gsub('stack traceback:', '')
trace = trace:gsub("^%s*\r?\n$", '')
io.stderr:write(trace .. '\n')
o(trace .. '\n')
os.exit(-2)
end
j = j + 1
@ -2528,7 +2577,7 @@ l65.msghandler = function(msg)
end
trace_cur = trace_cur:match(".-\n(.*)\n.-'xpcall'")
io.stderr:write(string.format("%s\n%s\n", msg, trace_cur))
o(string.format("%s\n%s\n", msg, trace_cur))
os.exit(-3)
end
do

View File

@ -1,17 +1,18 @@
require'vcs'
#pragma encapsulate far farcall
mappers.F4()
local bank_core,bank_fx = rom0,rom1
location(bank_fx)
@@kernel
ldx#0xd0 @_loop sta WSYNC stx COLUBK dex bne _loop rts
location(bank_core)
@@main
init()
@_frame
overscan() vblank() screen_begin() far(kernel) screen_end() jmp _frame
overscan() vblank() screen_begin() far kernel screen_end() jmp _frame
location(bank_fx)
@@kernel
ldx#0xd0 @_loop sta WSYNC stx COLUBK dex bne _loop rts
;
writebin(filename..'.bin')

34
vcs.l65
View File

@ -184,20 +184,24 @@ screen = function(f) screen_begin() if f then f() end screen_end() end
-- for mappers that swap banks in place
-- call an asm function into another bank and generate the stubs if needed
local far_stubs = {} far_stubs=far_stubs
local far = function(dst)
dst.section.refcount = dst.section.refcount + 1
local stub,loc_src,loc_dst = far_stubs[dst],location_current,dst.section.location
if not (stub and stub[loc_src]) then
local seccur = section_current
local stub_caller = section() switchrom(loc_dst.rom) skip(6) rts
location(loc_dst)
local stub_callee = section() jsr dst switchrom(loc_src.rom)
section(seccur)
relate(stub_caller, stub_callee, 3)
if not stub then far_stubs[dst] = {} end
far_stubs[dst][loc_src] = stub_caller
local far_stubs = {} _ENV.far_stubs=far_stubs
farcall = function(dst)
local loc_src = location_current
local create_stubs = function()
if type(dst) == 'function' then dst = dst() end
local stub,loc_dst = far_stubs[dst],dst.section.location
if not (stub and stub[loc_src]) then
location(loc_src)
local stub_caller = section() switchrom(loc_dst.rom) skip(6) rts
location(loc_dst)
local stub_callee = section() jsr dst switchrom(loc_src.rom)
relate(stub_caller, stub_callee, 3)
if not stub then far_stubs[dst] = {} end
far_stubs[dst][loc_src] = stub_caller
end
end
-- create stubs at the end to get to the referenced labels
table.insert(before_link, create_stubs)
jsr far_stubs[dst][loc_src]
end
@ -216,7 +220,6 @@ end
local bank_stubs = function(irq, count, hotspot, entry)
function switchrom(i) assert(i>=0 and i<count) bit 0x1000+hotspot+i end
_ENV.far = far
local base = 0x10000 - (count << 12)
for bi=0,count-1 do
local o = base+(bi<<12)
@ -301,7 +304,6 @@ local bank_stubs2 = function(irq, hotspot0, hotspot1)
elseif i==1 then bit hotspot1
else error("invalid rom index: " .. i) end
end
_ENV.far = far
local base = 0xe000
for bi=0,1 do
local o = base+(bi<<12)
@ -314,7 +316,6 @@ mappers.UA = function(irq) bank_stubs2(irq, 0x220, 0x240) end
mappers['0840'] = function(irq) bank_stubs2(irq, 0x800, 0x840) end
mappers.SB = function(count, irq)
function switchrom(i) assert(i>=0 and i<count) bit 0x800+i end
_ENV.far = far
for bi=0,count-1 do
local o = bi*0x1000 + 0x1000
_ENV['rom' .. bi] = location{o, o+0xfff, rorg=0xf000}
@ -345,7 +346,6 @@ end
mappers.X07 = function(irq)
function switchrom(i) assert(i>=0 and i<16) bit 0x80b+(i<<4) end
_ENV.far = far
-- map TIA also to 0x40-0x7F: use VSYNC for bank 14, and VSYNC2 for bank 15
vcs.VSYNC2=0x40 cpu.symbols.VSYNC2=0x40
for bi=0,count-1 do

View File

@ -572,6 +572,8 @@ syn match l65Opcode /\<shy\>/
syn match l65Opcode /\<slo\%(.[bw]\)\=\>/
syn match l65Opcode /\<sre\%(.[bw]\)\=\>/
syn match l65Opcode /\<far\>/
" Define the default highlighting.
" For version 5.7 and earlier: only when not done already
" For version 5.8 and later: only when an item doesn't have highlighting yet