1
0
mirror of https://github.com/g012/l65.git synced 2024-12-29 16:29:15 +00:00

Sections now generate regular labels for themselves.

This commit is contained in:
g012 2017-09-30 00:35:59 +02:00
parent a400eb145e
commit 71e6f35a3c
3 changed files with 317 additions and 144 deletions

194
6502.lua
View File

@ -2,6 +2,7 @@ local M = {}
local symbols={} M.symbols=symbols
local locations={} M.locations=locations
local sections={} M.sections=sections
local stats={} M.stats=stats setmetatable(stats, stats)
M.strip = true -- set to false to disable dead stripping of relocatable sections
@ -22,6 +23,9 @@ end
symbols.__index = symbols
setmetatable(M, symbols)
local id_ = 0
local id = function() id_=id_+1 return id_ end M.id=id
M.link = function()
if stats.unused then return end
@ -34,79 +38,28 @@ M.link = function()
return val
end
end
for _,location in ipairs(locations) do
for _,section in ipairs(location.sections) do
for _,section in ipairs(sections) do
section:compute_size()
end
end
symbols.__index = symbols
stats.used = 0
stats.unused = 0
stats.cycles = 0
for _,location in ipairs(locations) do
local sections,rorg = location.sections,location.rorg
local chunk_reserve = function(chunk_ix, chunk, start, size)
local chunk_reserve = function(chunks, chunk_ix, chunk, start, size)
if start == chunk.start then
if size == chunk.size then location.chunks[chunk_ix] = nil
if size == chunk.size then chunks[chunk_ix] = nil
else chunk.start=start+size chunk.size=chunk.size-size end
else
if chunk.size - (start - chunk.start) == size then chunk.size = chunk.size - size
else
local sz = start - chunk.start
table.insert(location.chunks, chunk_ix+1, { start=start+size, size=chunk.size-(sz+size) })
table.insert(chunks, chunk_ix+1, { start=start+size, size=chunk.size-(sz+size) })
chunk.size = sz
end
end
end
-- filter sections list
local position_independent_sections = {}
local symbols_to_remove = {}
local section_count = #sections
location.cycles=0 location.used=0
for ix,section in ipairs(sections) do
location.cycles = location.cycles + section.cycles
location.used = location.used + section.size
if section.size == 0 then
sections[ix]=nil
if not section.org then table.insert(symbols_to_remove, section.label) end
elseif not section.org then
if M.strip and not section.refcount and not section.strong then
sections[ix]=nil
table.insert(symbols_to_remove, section.label)
else
table.insert(position_independent_sections, section)
end
end
end
do local j=0 for i=1,section_count do
if sections[i] ~= nil then j=j+1 sections[j],sections[i] = sections[i],sections[j] end
end end
for _,v in ipairs(symbols_to_remove) do symbols[v] = nil end
stats.cycles = stats.cycles + location.cycles
stats.used = stats.used + location.used
-- fixed position sections
for section_ix,section in ipairs(sections) do if section.org then
if section.org < location.start or section.org > location.finish then
error("ORG section " .. section.label .. " starts outside container location")
end
for chunk_ix,chunk in ipairs(location.chunks) do
if chunk.start <= section.org and chunk.size - (section.org - chunk.start) >= section.size then
chunk_reserve(chunk_ix, chunk, section.org, section.size)
symbols[section.label] = rorg(section.org)
goto chunk_located
end
end
error("ORG section " .. section.label .. " overflows its location")
::chunk_located::
end end
-- position independent sections
table.sort(position_independent_sections, function(a,b) return a.size==b.size and a.label>b.label or a.size>b.size end)
for _,section in ipairs(position_independent_sections) do
local position_section = function(section, constrain)
local location = section.location
local rorg = location.rorg
local chunks = {}
for _,chunk in ipairs(location.chunks) do
if chunk.size >= section.size then chunks[#chunks+1] = chunk end
@ -155,6 +108,7 @@ M.link = function()
if lsb_cur <= lsb_new then goto constraints_not_met end
end
::select_pos::
if constrain and not constrain(address) then goto constraints_not_met end
waste=w position=address position_end=address_end
::constraints_not_met::
end
@ -166,16 +120,94 @@ M.link = function()
usage_lowest(start, chunk.start + chunk.size - section.size)
end
if position then
chunk_reserve(chunk_ix, chunk, position, section.size)
chunk_reserve(location.chunks, chunk_ix, chunk, position, section.size)
section.org = position
symbols[section.label] = rorg(position)
--print(section.label, string.format("%04X\t%d", position, section.size))
--for k,v in ipairs(location.chunks) do print(string.format(" %04X %04X %d", v.start, v.size+v.start-1, v.size)) end
return position
end
end
end
local sibling_sections = {}
stats.used = 0
stats.unused = 0
stats.cycles = 0
for _,location in ipairs(locations) do
local sections,rorg = location.sections,location.rorg
-- filter sections list
local position_independent_sections = {}
local symbols_to_remove = {}
local section_count = #sections
location.cycles=0 location.used=0
for ix,section in ipairs(sections) do
location.cycles = location.cycles + section.cycles
location.used = location.used + section.size
if section.size == 0 then
sections[ix]=nil
if not section.org then table.insert(symbols_to_remove, section.label) end
elseif not section.org then
if M.strip and not section.refcount and not section.strong then
sections[ix]=nil
table.insert(symbols_to_remove, section.label)
elseif section.siblings then
table.insert(sibling_sections, section)
else
table.insert(position_independent_sections, section)
end
end
end
do local j=0 for i=1,section_count do
if sections[i] ~= nil then j=j+1 sections[j],sections[i] = sections[i],sections[j] end
end end
for _,v in ipairs(symbols_to_remove) do symbols[v] = nil end
location.position_independent_sections = position_independent_sections
stats.cycles = stats.cycles + location.cycles
stats.used = stats.used + location.used
-- fixed position sections
for section_ix,section in ipairs(sections) do if section.org then
if section.org < location.start or section.org > location.finish then
error("ORG section " .. section.label .. " starts outside container location")
end
for chunk_ix,chunk in ipairs(location.chunks) do
if chunk.start <= section.org and chunk.size - (section.org - chunk.start) >= section.size then
chunk_reserve(location.chunks, chunk_ix, chunk, section.org, section.size)
symbols[section.label] = rorg(section.org)
goto chunk_located
end
end
error("unable to find space for section " .. section.label)
error("ORG section " .. section.label .. " overflows its location")
::chunk_located::
end end
end
--[[
for _,section in ipairs(sibling_sections) do
local sections = section.siblings
table.insert(sections, section)
-- biggest of all siblings first
table.sort(sections, function(a,b) return a.size==b.size and a.id<b.id or a.size>b.size end)
end
table.sort(sibling_sections, function(a,b) return a[1].size==b[1].size and a[1].id<b[1].id or a[1].size>b[1].size end)
for _,section in ipairs(sibling_sections) do
local siblings = section.siblings
local relate = section.relate or function(sibling, siblings)
end
for _,sibling in siblings do
if not relate(sibling, siblings) then goto not_suitable end
end
::not_suitable::
end
]]
for _,location in ipairs(locations) do
local position_independent_sections = location.position_independent_sections
table.sort(position_independent_sections, function(a,b) return a.size==b.size and a.label>b.label or a.size>b.size end)
for _,section in ipairs(position_independent_sections) do
if not position_section(section) then
error("unable to find space for section " .. section.label)
end
end
-- unused space stats
@ -235,7 +267,6 @@ M.genbin = function(filler)
for _,section in ipairs(sections) do
assert(section.org >= #bin+of0)
for i=#bin+of0,section.org-1 do ins(bin, filler) end
M.label_current = section.label
for _,instruction in ipairs(section.instructions) do
local b,f = instruction.bin,instruction.asbin
if b then mov(b,1,#b,#bin+1,bin)
@ -323,7 +354,7 @@ M.location = function(start, finish)
location.rorg = function(x) return x+offset end
end
end
location.sections = {}
location.sections = {} -- TODO remove
if not location.rorg then location.rorg = function(x) return x end end
local size = (location.finish or math.huge) - location.start + 1
location.chunks={ { start=location.start, size=size } }
@ -334,21 +365,22 @@ end
M.section = function(t)
local section = {}
if (type(t) == 'string') then section.label = t
else
assert(type(t) == 'table')
assert(type(t[1]) == 'string' and string.len(t[1]) > 0)
section=t section.label=t[1] section[1]=nil
if section.offset and not section.align then error("section " .. section.label .. " has offset, but no align") end
local name = t or 'S'..id()
if (type(name) ~= 'string') then
assert(type(t) == 'table', "invalid arguments for section")
assert(type(t[1]) == 'string' and string.len(t[1]) > 0, "invalid name for section")
section=t name=t[1] section[1]=nil
if section.offset and not section.align then error("section " .. name .. " has offset, but no align") end
end
table.insert(M.location_current.sections, section)
if symbols[section.label] then error("duplicate symbol: " .. section.label) end
symbols[section.label] = section
M.label_current = section.label
table.insert(M.location_current.sections, section) -- TODO remove
table.insert(M.sections, section)
section.location = M.location_current
M.section_current = section
section.type = 'section'
section.id = id()
section.constraints = {}
section.instructions = {}
assert(name:sub(1,1) ~= '_', "sections can't be named with a local label")
section.label = M.label(name)
function section:compute_size()
local instructions = self.instructions
self.size=0 self.cycles=0
@ -373,12 +405,11 @@ M.section = function(t)
return section
end
M.label_gen_ix = 0
M.label = function(name)
local label,offset
local section,rorg = M.section_current,M.location_current.rorg
label = { type='label' }
if not name then name='_L'..M.label_gen_ix M.label_gen_ix=M.label_gen_ix+1 end
label = { type='label', section=section }
if not name then name='_L'..id() end
if name:sub(1,1) == '_' then -- local label
name = M.label_current .. name
else
@ -412,9 +443,18 @@ M.endpage = function()
constraint.to = #section.instructions
end
M.sleep = function(cycles, noillegal)
assert(cycles > 1, "can't sleep for less than 2 cycles")
if cycles & 1 ~= 0 then
if noillegal then bitzpg(0) else nopzpg(0) end
cycles = cycles - 3
end
for i=1,cycles/2 do nopimp() end
end
local size_ref = function(v)
if type(v) == 'string' then v=symbols[v] end
if type(v) == 'table' and v.type == 'section' then v.refcount = 1 + (v.refcount or 0) end
if type(v) == 'table' and v.type == 'label' then v.section.refcount = 1 + (v.section.refcount or 0) end
end
local size_dc = function(v)
if type(v) == 'function' then

15
samples/vcs0.l65 Normal file
View File

@ -0,0 +1,15 @@
require'vcs'
mappers['2K']()
local kernel = function()
ldx#0xb0 @_loop sta WSYNC stx COLUBK dex bne _loop
end
@@main
init()
@_frame
overscan() vblank() screen(kernel) jmp _frame
writebin(filename..'.bin')
writesym(filename..'.sym')
print(stats)

194
vcs.l65
View File

@ -84,45 +84,160 @@ do
for k,v in pairs(vcs) do symbols[k] = v end
end
NTSC = {
TIM_OVERSCAN = 38, -- TIM64T, 2432 cycles = 32 scanlines
TIM_VBLANK = 45, -- TIM64T, 2880 cycles = ~ 38 scanlines
TIM_KERNEL = 15, -- T1024T, 15360 cycles = ~202 scanlines
}
PAL = {
TIM_OVERSCAN = 50, -- TIM64T, 3200 cycles = ~ 42 scanlines
TIM_VBLANK = 61, -- TIM64T, 3904 cycles = ~ 51 scanlines
TIM_KERNEL = 17, -- T1024T, 17408 cycles = ~229 scanlines
}
TV = PAL
init = function() cld ldx#0 txa local l=label() dex tsx pha bne l end
wait = function() local l=label() lda INTIM bne l end
overscan_begin = function() sta WSYNC lda#2 sta VBLANK lda#TV.TIM_OVERSCAN sta TIM64T end
overscan_end = wait
overscan = function(f) overscan_begin() if f then f() end overscan_end() end
vblank_begin = function()
lda#0b1110 local l=label() sta WSYNC sta VSYNC lsr bne l
lda#2 sta VBLANK lda#TV.TIM_VBLANK sta TIM64T
end
vblank_end = function() wait() sta WSYNC sta VBLANK end
vblank = function(f) vblank_begin() if f then f() end vblank_end() end
screen_begin = function() lda#TV.TIM_KERNEL sta T1024T end
screen_end = wait
screen = function(f) screen_begin() if f then f() end screen_end() end
nusiz = {
MSL_SIZE_1 = 16*0,
MSL_SIZE_2 = 16*1,
MSL_SIZE_4 = 16*2,
MSL_SIZE_8 = 16*3,
ONE_COPY = 0,
TWO_COPIES_CLOSE = 1,
TWO_COPIES_MEDIUM = 2,
THREE_COPIES_CLOSE = 3,
TWO_COPIES_WIDE = 4,
DOUBLE_SIZED_PLAYER = 5,
THREE_COPIES_MEDIUM = 6,
QUAD_SIZED_PLAYER = 7,
}
ctrlpf = {
BALL_SIZE_1 = 16*0,
BALL_SIZE_2 = 16*1,
BALL_SIZE_4 = 16*2,
BALL_SIZE_8 = 16*3,
PF_MIRRORED = 1,
PF_SCOREMODE = 2,
PF_PRIO_BALLABOVE = 4,
}
enable = {
DISABLE = 0,
ENABLE = 2,
}
vdel = {
DISABLE = 0,
ENABLE = 1,
}
hm = {
LEFT_7 = 7*16,
LEFT_6 = 6*16,
LEFT_5 = 5*16,
LEFT_4 = 4*16,
LEFT_3 = 3*16,
LEFT_2 = 2*16,
LEFT_1 = 1*16,
NO_MOTION = 0,
RIGHT_1 = 15*16,
RIGHT_2 = 14*16,
RIGHT_3 = 13*16,
RIGHT_4 = 12*16,
RIGHT_5 = 11*16,
RIGHT_6 = 10*16,
RIGHT_7 = 9*16,
RIGHT_8 = 8*16,
C74_LEFT_15 = 7*16,
C74_LEFT_14 = 6*16,
C74_LEFT_13 = 5*16,
C74_LEFT_12 = 4*16,
C74_LEFT_11 = 3*16,
C74_LEFT_10 = 2*16,
C74_LEFT_9 = 1*16,
C74_LEFT_8 = 0*16,
C74_LEFT_7 = 15*16,
C74_LEFT_6 = 14*16,
C74_LEFT_5 = 13*16,
C74_LEFT_4 = 12*16,
C74_LEFT_3 = 11*16,
C74_LEFT_2 = 10*16,
C74_LEFT_1 = 9*16,
C74_NO_MOTION = 8*16,
}
-- for mappers that swap banks in place
-- call an asm function into another bank and generate the stubs if needed
local far_stubs = {} M.far_stubs=far_stubs
local far = function(dst)
local stub,loc_src,loc_dst = far_stubs[dst],location_current,dst.section.location
if not (stub and stub.stub_caller.location==loc_src and stub.stub_callee.location==loc_dst) 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)
location(loc_src) section_current=seccur
far_stubs[dst] = relate(stub_caller, stub_callee, 3)
end
jsr stub_caller
end
mappers = {}
mappers['2K'] = function()
mappers['2K'] = function(irq)
rom0 = location(0xf800, 0xffff)
section{"vectors", org=0xfffc} word(main,main)
section{"vectors", org=0xfffc} dc.w main if irq then dc.w main end
end
mappers.CV = mappers['2K']
mappers['4K'] = function()
mappers['4K'] = function(irq)
rom0 = location(0xf000, 0xffff)
section{"vectors", org=0xfffc} word(main,main)
section{"vectors", org=0xfffc} dc.w main if irq then dc.w main end
end
local bank_stubs = function(count, hotspot, entry)
local bank_stubs = function(irq, count, hotspot, entry)
function switchrom(i) assert(i>=0 and i<count) bit 0x1000+hotspot+i end
far = far
local base = 0x10000 - (count << 12)
for bi=0,count-1 do
local o = base+(bi<<12)
_ENV['rom' .. bi] = location{o, o+0xfff, rorg=0xf000}
_ENV['rom' .. bi] = location{o, o+0xfff, rorg=0xf000, rom=bi}
local start=section{"entry"..bi, org=o+(entry or hotspot-6)} switchrom(0) if bi==0 then jmp main end
section{"switchtab"..bi, org=o+hotspot} for i=1,count do byte(0) end
section{"vectors"..bi, org=o+0xffc} word(start,start)
section{"vectors"..bi, org=o+0xffc} dc.w start if irq then dc.w start end
end
end
mappers.FA = function() bank_stubs(3, 0xff8) end
mappers.F8 = function() bank_stubs(2, 0xff8) end
mappers.F6 = function() bank_stubs(4, 0xff6) end
mappers.F4 = function() bank_stubs(8, 0xff4) end
mappers.EF = function() bank_stubs(16, 0xfe0, 0xffc-6) end
mappers.FA = function(irq) bank_stubs(irq, 3, 0xff8) end
mappers.F8 = function(irq) bank_stubs(irq, 2, 0xff8) end
mappers.F6 = function(irq) bank_stubs(irq, 4, 0xff6) end
mappers.F4 = function(irq) bank_stubs(irq, 8, 0xff4) end
mappers.EF = function(irq) bank_stubs(irq, 16, 0xfe0, 0xffc-6) end
mappers.FE = function()
rom0 = location(0xd000, 0xdfff)
section{"vectors0", org=0xdffc} word(main,main)
mappers.FE = function(irq)
rom1 = location(0xd000, 0xdfff)
section{"vectors1", org=0xdffc} dc.w main if irq then dc.w main end
location{0xe000, 0xefff, nofill=true}
rom1 = location(0xf000, 0xffff)
section{"vectors1", org=0xfffc} word(main,main)
rom0 = location(0xf000, 0xffff)
section{"vectors0", org=0xfffc} dc.w main if irq then dc.w main end
end
mappers.E0 = function(map)
mappers.E0 = function(map, irq)
function switchrom0(i) assert(map[i]==0) bit 0x1fe0+i end
function switchrom1(i) assert(map[i]==1) bit 0x1fe8+i end
function switchrom2(i) assert(map[i]==2) bit 0x1ff0+i end
@ -132,12 +247,12 @@ mappers.E0 = function(map)
_ENV["rom"..bi] = location{0xe000+o, 0xe3ff+o, rorg=0xf000+map[bi]*0x400}
end
section{"switchtab", org=0xffe0} for i=1,8*3 do byte(0) end
section{"vectors", org=0xfffc} word(main,main)
section{"vectors", org=0xfffc} dc.w main if irq then dc.w main end
end
-- rom0 refers to the last one in ROM, ie the one always mapped to 0xF800-0xFFFF,
-- such that changing the rom count does not change its index
mappers['3F'] = function(count)
mappers['3F'] = function(count, irq)
function switchrom(i) assert(i>=0 and i<count-1) lda#i sta 0x3f end
local symbols=cpu.symbols for k,v in pairs(vcs) do -- remap TIA to 0x40-0x7F
if v<0x40 then v=v+0x40 vcs[k]=v symbols[k]=v end
@ -148,10 +263,10 @@ mappers['3F'] = function(count)
end
local o = 0x800 * (count-1) + 0x10000
rom0 = location{o, 0x7ff+o, rorg=0xf800}
section{"vectors", org=0xfffc} word(main,main)
section{"vectors", org=0xfffc} dc.w main if irq then dc.w main end
end
mappers.E7 = function()
mappers.E7 = function(irq)
function switchrom(i) assert(i>=0 and i<7) bit 0x1fe0+i end
function enableram() bit 0x1fe7 end
function switchram(i) assert(i>=0 and i<4) bit 0x1fe8+i end
@ -161,56 +276,58 @@ mappers.E7 = function()
end
rom7 = location{0xf800, 0xffff, rorg=0xf800}
section{"switchtab", org=0xffe0} for i=1,12 do byte(0) end
section{"vectors", org=0xfffc} word(main,main)
section{"vectors", org=0xfffc} dc.w main if irq then dc.w main end
end
mappers.F0 = function()
mappers.F0 = function(irq)
function switchrom() lda 0x1ff0 end
for bi=0,15 do
local o = bi*0x1000 + 0x1000
_ENV['rom' .. bi] = location{o, o+0xfff, rorg=0xf000}
local start=section{"entry"..bi, org=o+0xffc-8}
@_loop switchrom() bne _loop
local l=label() switchrom() bne l
if bi==0 then jmp main end
section{"switchtab"..bi, org=o+0xff0} byte(bi)
section{"vectors"..bi, org=o+0xffc} word(start,start)
section{"vectors"..bi, org=o+0xffc} dc.w start if irq then dc.w start end
end
end
local bank_stubs2 = function(hotspot0, hotspot1)
local bank_stubs2 = function(irq, hotspot0, hotspot1)
function switchrom(i)
if i==0 then bit hotspot0
elseif i==1 then bit hotspot1
else error("invalid rom index: " .. i) end
end
far = far
local base = 0xe000
for bi=0,1 do
local o = base+(bi<<12)
_ENV['rom' .. bi] = location{o, o+0xfff, rorg=0xf000}
local start=section{"entry"..bi, org=o+0xffc-6} switchrom(0) if bi==0 then jmp main end
section{"vectors"..bi, org=o+0xffc} word(start,start)
section{"vectors"..bi, org=o+0xffc} dc.w start if irq then dc.w start end
end
end
mappers.UA = function() bank_stubs2(0x220, 0x240) end
mappers['0840'] = function() bank_stubs2(0x800, 0x840) end
mappers.SB = function(count)
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
far = far
for bi=0,count-1 do
local o = bi*0x1000 + 0x1000
_ENV['rom' .. bi] = location{o, o+0xfff, rorg=0xf000}
local start=section{"entry"..bi, org=o+0xffc-6} switchrom(0) if bi==0 then jmp main end
section{"vectors"..bi, org=o+0xffc} word(start,start)
section{"vectors"..bi, org=o+0xffc} dc.w start if irq then dc.w start end
end
end
mappers['3E'] = function(rom_count, ram_count)
mappers['3F'](rom_count)
mappers['3E'] = function(rom_count, ram_count, irq)
mappers['3F'](rom_count, irq)
function switchram(i) assert(i>=0 and i<ram_count-1) lda#i sta 0x3E end
end
-- rom0 refers to the last 1k in ROM, which is mapped automatically on reset,
-- such that changing the rom count does not change its index
mappers.MC = function(rom_count, ram_count)
mappers.MC = function(rom_count, ram_count, irq)
for i=0,3 do
_ENV['switchrom'..i] = function(i) assert(i>=0 and i<128) lda#i sta 0x3c+i end
_ENV['switchram'..i] = function(i) assert(i>=0 and i<128) lda#i+0x80 sta 0x3c+i end
@ -220,17 +337,18 @@ mappers.MC = function(rom_count, ram_count)
_ENV["rom"..(count-1-bi)] = location{o, 0x3ff+o, rorg=0xf000+(o&0xfff)}
end
local start=section("entry") switchrom3(0) jmp main
section{"vectors", org=0xfffc} word(start,start)
section{"vectors", org=0xfffc} dc.w start if irq then dc.w start end
end
mappers.X07 = function()
mappers.X07 = function(irq)
function switchrom(i) assert(i>=0 and i<16) bit 0x80b+(i<<4) end
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
local o = bi*0x1000 + 0x1000
_ENV['rom' .. bi] = location{o, o+0xfff, rorg=0xf000}
local start=section{"entry"..bi, org=o+0xffc-6} switchrom(0) if bi==0 then jmp main end
section{"vectors"..bi, org=o+0xffc} word(start,start)
section{"vectors"..bi, org=o+0xffc} dc.w start if irq then dc.w start end
end
end