Repository navigation
Expand file tree
/
Copy pathasm.lua
More file actions
832 lines (783 loc) · 32.5 KB
/
Copy pathasm.lua
File metadata and controls
832 lines (783 loc) · 32.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
local M = {}
local symbols,symbolsorg={},{} M.symbols,M.symbolsorg=symbols,symbolsorg
local locations={} M.locations=locations
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.bin_filler = 0 -- brk opcode on 6502
M.bin_finalizer = function() end
M.strip = true -- set to false to disable dead stripping of relocatable sections
M.strip_empty = false -- set to true to strip empty sections: their label will then not resolve
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
-- disabled for other parts (required to distinguish automatically between zp/abs addressing)
M.pcall_za = function(...) return M.pcall(...) end
M.__index = M
M.__newindex = function(t,k,v)
local kk = k
if type(k) == 'string' and k:sub(1,1) == '_' and M.label_current then
kk = M.label_current .. k
end
if symbols[kk] then error("attempt to modify symbol " .. k) end
rawset(t,k,v)
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
for _,v in ipairs(before_link) do v() end
if M.strip then
symbols.__index = function(tab,key)
local val = rawget(symbols, key)
if type(val) == 'table' and val.type == 'label' then
val.section.refcount = val.section.refcount + 1
end
return val
end
end
for _,section in ipairs(sections) do
section:compute_size()
end
symbols.__index = symbols
local chunk_reserve = function(section, chunk_ix)
local chunks = section.location.chunks
local chunk = chunks[chunk_ix]
local holes = section.holes
local new_chunks,ins = {},table.insert
local chunk1 = { id=id(), start=chunk.start, size=section.org-chunk.start }
local hole_ix = 1
local hole1 = holes[1]
if hole1 and hole1.start==0 then
chunk1.size = chunk1.size + hole1.size
hole_ix = 2
end
if chunk1.size > 0 then ins(new_chunks, chunk1) end
while hole_ix <= #holes do
local hole = holes[hole_ix]
local chunki = { id=id(), start=section.org+hole.start, size=hole.size }
ins(new_chunks, chunki)
hole_ix = hole_ix + 1
end
local chunkl = { id=id(), start=section.org+section.size, size=chunk.start+chunk.size-(section.org+section.size) }
local chunkn = new_chunks[#new_chunks]
if chunkn and chunkn.start+chunkn.size==chunkl.start then
chunkn.size = chunkn.size + chunkl.size
elseif chunkl.size > 0 then
ins(new_chunks, chunkl)
end
table.remove(chunks, chunk_ix)
for i=chunk_ix,chunk_ix+#new_chunks-1 do ins(chunks, i, new_chunks[i-chunk_ix+1]) end
end
local chunk_from_address = function(section, address)
local chunks,rorg = section.location.chunks,section.location.rorg
for i,chunk in ipairs(chunks) do
if address >= chunk.start and address+section.size <= chunk.start+chunk.size then
return chunk,i
end
end
end
local check_section_position = function(section, address, chunk)
local chunk = chunk_from_address(section, address)
if not chunk then return end
local rorg = section.location.rorg
if section.align then
local raddress = rorg(address)
if section.offset then raddress = raddress - section.offset end
if raddress % section.align ~= 0 then return end
end
for _,constraint in ipairs(section.constraints) do
local cstart, cfinish = address+constraint.start, address+constraint.finish
local s, f = rorg(cstart) // 0x100, rorg(cfinish) // 0x100
if s==f or math.ceil(s)==math.floor(s) and s+1==f and cfinish-cstart==0x100 then
if constraint.type == 'crosspage' then return end
else
if constraint.type == 'samepage' then return end
end
end
local address_end = address+section.size
local waste = math.min(address - chunk.start, chunk.size - (address_end - chunk.start))
local raddress,raddress_end = rorg(address),rorg(address_end)
local align,cross=0x100,0
repeat
local cross_count = (raddress_end+align-1)//align - (raddress+align-1)//align
if raddress&(align-1) == 0 then cross_count=cross_count+1 end
cross = cross + align * align * cross_count
align = align>>1
until align==1
local lsb=0
for i=0,15 do if raddress&(1<<i) == 0 then lsb=lsb+1 else break end end
return waste, cross, lsb
end
local position_section = function(section, constrain)
local location = section.location
local chunks,rorg = location.chunks,location.rorg
table.sort(chunks, function(a,b) if a.size==b.size then return a.id<b.id end return a.size<b.size end)
for chunk_ix,chunk in ipairs(chunks) do if chunk.size >= section.size then
local waste,cross,lsb,position = math.maxinteger,math.maxinteger,math.maxinteger
local usage_lowest = function(start, finish)
local inc=1
if section.align then
local rstart = rorg(start)
local arstart = (rstart + section.align - 1) // section.align * section.align
if section.offset then arstart = arstart + section.offset end
start = start + arstart-rstart
inc = section.align
end
for address=start,finish,inc do
local nwaste, ncross, nlsb = check_section_position(section, address, chunk)
if nwaste then
if constrain then
nwaste, ncross, nlsb = constrain(address, nwaste, ncross, nlsb)
if not nwaste then goto skip end
end
if nwaste > waste then goto skip end
if nwaste == waste then
-- if waste is the same, keep the one that uses the least amount of aligned addresses
if ncross > cross then goto skip end
if ncross == cross then
-- if cross count is same, take the one with the most set LSB count (eg. select 11 over 10)
if nlsb > lsb then goto skip end
end
end
position,waste,cross,lsb = address,nwaste,ncross,nlsb
::skip::
end
end
end
local finish = math.min(chunk.start + 0xff, chunk.start + chunk.size - section.size)
usage_lowest(chunk.start, finish)
if chunk.size ~= math.huge then
local start = math.max(chunk.start + chunk.size - section.size - 0xff, chunk.start)
usage_lowest(start, chunk.start + chunk.size - section.size)
end
if position then
section.org = position
chunk_reserve(section, chunk_ix)
--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
end
stats.used = 0
stats.unused = 0
stats.cycles = 0
local related_sections = {}
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
if M.strip_empty or section.weak then
sections[ix]=nil
if not section.org then table.insert(symbols_to_remove, section.label) end
else
section.org = location.start
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.related then
table.insert(related_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 or math.huge) 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(section, chunk_ix)
goto chunk_located
end
end
error("ORG section " .. section.label .. " overflows its location")
::chunk_located::
end end
end
table.sort(related_sections, function(a,b) if a.size==b.size then return a.id<b.id end return a.size>b.size end)
for _,section in ipairs(related_sections) do if not section.org then
local related,ins = {},table.insert
local function collect(section_parent, offset)
local relatives = relations[section_parent]
if relatives then
for relative,relative_offset in pairs(relatives) do
if not related[relative] and relative ~= section then
relative_offset = relative_offset + offset
related[relative] = relative_offset
collect(relative, relative_offset)
end
end
end
end
collect(section, 0)
local location_start = section.location.start
local position = position_section(section, function(address, waste, cross, lsb)
local waste, cross, lsb = 0, 0, 0
for section,offset in pairs(related) do
local section_address = address + (section.location.start - location_start) + offset
local nwaste, ncross, nlsb = check_section_position(section, section_address)
if not nwaste then return end
waste, cross, lsb = waste+nwaste, cross+ncross, lsb+nlsb
end
return waste, cross, lsb
end)
if not position then
error("unable to find space for related section '" .. section.label .. "' of size " .. section.size)
end
for section,offset in pairs(related) do
section.org = position + (section.location.start - location_start) + offset
local chunk,chunk_ix = chunk_from_address(section, section.org)
chunk_reserve(section, chunk_ix)
end
end end
for _,location in ipairs(locations) do
local position_independent_sections = location.position_independent_sections
table.sort(position_independent_sections, function(a,b) if a.size==b.size then return a.label>b.label end return 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 .. "' of size " .. section.size)
end
end
-- unused space stats
local unused = 0
for _,chunk in ipairs(location.chunks) do
if chunk.size ~= math.huge then
unused = unused + chunk.size
else
location.stops_at = chunk.start-1
end
end
location.unused = unused
stats.unused = stats.unused + unused
end
end
M.resolve = function()
if stats.resolved_count then return end
M.link()
stats.resolved_count = 0
repeat local count = 0
for k,v in pairs(symbols) do if k ~= '__index' then
local t = type(v)
if t == 'function' then v=v() t=type(v) symbols[k]=v count=count+1 end
if t == 'table' and type(v.resolve) == 'function' then symbols[k],symbolsorg[k]=v.resolve() count=count+1 end
if t == 'string' and symbols[v] then symbols[k]=symbols[v] count=count+1 end
stats.resolved_count = stats.resolved_count + count
end end until count == 0
-- set local label references resolver
local llresolver = { __index = function(tab,key)
if type(key) ~= 'string' or key:sub(1,1) ~= '_' or not M.label_current then return nil end
return symbols[M.label_current .. key]
end }
setmetatable(symbols, llresolver)
end
M.genbin = function(filler)
if #locations == 0 then return end
if filler == nil then filler = M.bin_filler end
M.resolve()
local bin = {}
local ins,mov = table.insert,table.move
table.sort(locations, function(a,b) return a.start < b.start end)
local of0 = locations[1].start
local fill
for _,location in ipairs(locations) do
if location.start < #bin then
error(string.format("location [%04x,%04x] overlaps another", location.start, location.finish or location.stops_at))
end
if fill then for i=#bin+of0,location.start-1 do ins(bin, filler) end end
M.size=0 M.cycles=0
local sections = location.sections
table.sort(sections, function(a,b) return a.org < b.org end)
for _,section in ipairs(sections) do
for i=#bin+of0,section.org-1 do ins(bin, filler) end
local bin_offset = math.min(#bin, section.org-of0)+1
for _,instruction in ipairs(section.instructions) do
local b,o = instruction.bin
if type(b) == 'function' then b,o = b(filler) end
if type(b) == 'table' then mov(b,1,#b,bin_offset,bin) bin_offset=bin_offset+#b
elseif b then bin[bin_offset]=b bin_offset=bin_offset+1 end
if o then
bin_offset=bin_offset+o
for i=#bin,bin_offset-1 do ins(bin, filler) end
end
M.size=#bin M.cycles=M.cycles+(instruction.cycles or 0)
end
end
fill = not location.nofill
if location.finish and fill then
for i=#bin+of0,location.finish do ins(bin, filler) end
end
end
stats.bin_size = #bin
return bin
end
M.writebin = function(filename, bin)
if not filename then filename = 'main.bin' end
if not bin then bin = M.genbin() end
M.bin_finalizer(bin)
local f = assert(io.open(filename, "wb"), "failed to open " .. filename .. " for writing")
for i = 1, #bin, 0x4000 do
f:write(string.char(table.unpack(bin, i, math.min(i + 0x3fff, #bin))))
end
f:close()
end
-- return a table of entry(address, label)
M.getsym = function(entry)
local ins = table.insert
local s,sym_rev = {},{}
for k,v in pairs(symbols) do if type(v) == 'number' then ins(sym_rev,k) end end
table.sort(sym_rev, function(a,b) local x,y=symbols[a],symbols[b] if x==y then return a<b end return x<y end)
for _,v in ipairs(sym_rev) do
local k,vorg=symbols[v],v
local u=v:match'.*()_' if u then -- change _ to . in local labels
local parent=v:sub(1,u-1) if symbols[parent] then v = parent..'.'..v:sub(u+1) end
end
local e = entry(k,v,vorg) if e then
if type(e) == 'table' then for _,ev in ipairs(e) do ins(s, ev) end
else ins(s, e) end
end
end
return s
end
M.getsym_as = {
lua = function() -- .lua
local fmt,rep = string.format,string.rep
local s = M.getsym(function(a,l) return fmt("%s = 0x%04x", l, a) end)
return table.concat(s, '\n')
end,
dasm = function() -- .sym
local fmt,rep = string.format,string.rep
local s = M.getsym(function(a,l) return fmt("%s%s %04x", l, rep(' ',24-#l), a) end)
table.insert(s, 1, '--- Symbol List')
s[#s+1] = '--- End of Symbol List.'
return table.concat(s, '\n')
end,
}
-- write a symbol file for debuggers, using specified format (defaults to DASM)
M.writesym = function(filename, format)
assert(filename)
local s = M.getsym_as[format or 'dasm'](filename)
if s then
local f = assert(io.open(filename, "wb"), "failed to open " .. filename .. " for writing")
f:write(s) f:close()
end
end
stats.__tostring = function()
local s,ins={},table.insert
ins(s, " Free Used Size Area")
for _,location in ipairs(locations) do
local name = (location.name or ''):sub(1,14)
name = string.rep(' ', 14-#name) .. name
local fmt = "%s %5d %5d %5d [%04X-%04X]"
if location.finish then
local size = location.finish-location.start+1
ins(s, string.format(fmt, name,
location.unused, size-location.unused, size, location.start, location.finish))
else
ins(s, string.format(fmt, name,
location.unused, location.used, location.stops_at-location.start+1, location.start, location.stops_at))
end
end
if #locations > 1 then
ins(s, string.format(" --- Total --- %5d %5d %5d", stats.unused, stats.used, stats.bin_size))
end
return table.concat(s, '\n')
end
M.location = function(start, finish)
local location
if type(start) ~= 'table' then
location = { start=start, finish=finish }
else
if start.type == 'location' then
for _,v in ipairs(locations) do if v == start then
M.location_current = start
return start
end end
error("unable to find reference to location [" .. (start.start or '?') .. ", " .. (start.finish or '?') .. "]")
end
location = start
location.start = start[1]
location.finish = start[2]
if type(location.rorg) == 'number' then
local offset = location.rorg - location.start
location.rorg = function(x) return x+offset end
end
end
location.type = 'location'
location.sections = {}
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={ { id=id(), start=location.start, size=size } }
locations[#locations+1] = location
M.location_current = location
return location
end
M.section = function(t)
local section = {}
local name = t or 'S'..id()
if type(name) ~= 'string' then
assert(type(t) == 'table', "invalid arguments for section")
if t.type == 'section' then
for _,v in ipairs(sections) do if v == t then
M.location_current = t.location
M.section_current = t
return t
end end
error("unable to find reference to section " .. (t.label or '?'))
end
section=t name=t[1] or 'S'..id() 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)
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)
section.holes = {}
section.refcount = 0
function section:compute_size()
local instructions = self.instructions
self.size=0 self.cycles=0
for _,instruction in ipairs(instructions) do
instruction.offset = self.size
local ins_sz = instruction.size or 0
if type(ins_sz) == 'function' then
-- evaluation is needed to get the size (distinguish zpg/abs)
-- labels and sections are not resolved at this point, so
-- evaluation will fail if the size is not explicitly stated (.b/.w);
-- in that case, assume max size
ins_sz = ins_sz()
end
self.size = self.size + ins_sz
self.cycles = self.cycles + (instruction.cycles or 0)
end
for _,constraint in ipairs(self.constraints) do
constraint.start = instructions[constraint.from].offset
constraint.finish = constraint.to==#instructions and self.size or instructions[constraint.to+1].offset
end
end
return section
end
-- relate(section1, section2 [, [offset1,] offset2])
-- Add a position relationship between 'section1' and 'section2', with 'offset1'
-- bytes from selected position for 'section2', and 'offset2' bytes from selec-
-- -ted positon for 'section1'.
-- If offset1 is omitted, -offset2 is used.
M.relate = function(section1, section2, offset, offset2)
assert(section1.type == 'section', "section1 is not a section")
assert(section2.type == 'section', "section2 is not a section")
local rel1 = relations[section1] or {}
rel1[section2] = (offset2 or offset) or 0
relations[section1] = rel1
local rel2 = relations[section2] or {}
rel2[section1] = (offset2 and offset) or -rel1[section2]
relations[section2] = rel2
section1.related = true
section2.related = true
end
M.label = function(name)
local label,offset
local section,rorg = M.section_current,M.location_current.rorg
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
M.label_current = name
label.bin = function() M.label_current = name end
end
if symbols[name] then error("duplicate symbol: " .. name) end
symbols[name] = label
label.label = name
label.size = function()
offset = section.size
label.size = 0
return 0
end
label.resolve = function()
local o = section.org + offset
return rorg(o),o
end
table.insert(section.instructions, label)
return name,label
end
M.samepage = function()
local section = M.section_current
table.insert(section.constraints, { type='samepage', from=#section.instructions+1 })
end
M.crosspage = function()
local section = M.section_current
table.insert(section.constraints, { type='crosspage', from=#section.instructions+1 })
end
M.endpage = function()
local section = M.section_current
local constraint = section.constraints[#section.constraints]
assert(constraint and not constraint.to, "closing constraint, but no constraint is open")
constraint.to = #section.instructions
end
-- skip(bytes)
-- Insert a hole in the section of 'bytes' bytes, which can be used by other
-- relocatable sections.
M.skip = function(bytes)
local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
local ins,section = {},M.section_current
ins.size = function()
table.insert(section.holes, { start=ins.offset, size=bytes })
return bytes
end
ins.bin = function(filler) return nil,bytes end
table.insert(section.instructions, ins)
end
-- sleep(cycles [, noillegal])
-- Waste 'cycles' cycles. If 'noillegal' is true, trashes NZ flags.
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 op_resolve = function(v)
if type(v) == 'function' then v=v() end
if type(v) == 'table' and v.label then v = symbols[v.label] end
if type(v) == 'string' then v = symbols[v] end
if type(v) ~= 'number' then error("unresolved symbol: " .. tostring(v)) end
return v
end M.op_resolve = op_resolve
local size_ref = function(v)
if type(v) == 'string' then v=symbols[v] end
if type(v) == 'table' and v.type == 'label' then v.section.refcount = 1 + (v.section.refcount or 0) end
end
M.size_ref = size_ref
local size_dc = function(v)
if type(v) == 'function' then
local r,x = M.pcall(v)
if not r or not x then return v end
end
size_ref(v)
return v
end
M.size_dc = size_dc
local size_op = function(late, early)
if type(late) == 'function' then
local r,x = M.pcall(late, early or 0, op_resolve)
if not r or not x then return late,early end
late=x early=nil
end
size_ref(late) size_ref(early)
return late,early
end
M.size_op = size_op
local byte_normalize = function(v)
if v < -0x80 or v > 0xFF then error("value out of byte range: " .. v) end
if v < 0 then v = v + 0x100 end
return v & 0xff
end
M.byte_normalize = byte_normalize
local word_normalize = function(v)
if v < -0x8000 or v > 0xFFFF then error("value out of word range: " .. v) end
if v < 0 then v = v + 0x10000 end
return v & 0xffff
end
M.word_normalize = word_normalize
local long_normalize = function(v)
if v < -0x80000000 or v > 0xFFFFFFFF then error("value out of word range: " .. v) end
if v < 0 then v = v + 0x100000000 end
return v & 0xffffffff
end
M.long_normalize = long_normalize
-- charset([s] [, f])
-- Set a new charset to be used for next string data in byte().
-- Without argument, revert to Lua charset.
-- s: string of all letters of charset
-- f: letter index offset or function to transform the letter index
M.charset = function(s, f)
local st = type(s)
if st == 'nil' then M.cs = nil return s end
if st == 'table' then M.cs = s return s end
if not f then f = function(v) return v end
elseif type(f) == 'number' then f = function(v) return v + f end end
local t,i={},0
for c in s:gmatch'.' do local v=i t[c]=function() return f(v) end i=i+1 end
M.cs=t
return t
end
M.byte_impl = function(args, nrm)
local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
local data,cs = {},M.cs
for k,v in ipairs(args) do
local t = type(v)
if t == 'number' or t == 'function' then data[#data+1] = v
elseif t == 'table' then table.move(v,1,#v,#data+1,data)
elseif t == 'string' then
if cs then
for c in v:gmatch'.' do
local i=cs[c]
if not i then error("character " .. c .. " is not part of current charset") end
data[#data+1]=i
end
else
local s = {v:byte(1,#v)}
table.move(s, 1, #s, #data+1, data)
end
else error("unsupported type for byte() argument: " .. t .. ", value: " .. v)
end
end
local size = function()
for i,v in ipairs(data) do data[i] = size_dc(v) end
return #data
end
local bin = function() local l65dbg=l65dbg
local b={}
for k,v in ipairs(data) do
if type(v) == 'function' then v = v() end
local vt = type(v)
if vt == 'table' and v.label then v = symbols[v.label]
elseif vt == 'string' then v = symbols[v] end
if type(v) ~= 'number' then error("unresolved symbol for dc.b, index " .. k) end
b[#b+1] = nrm(v)
end
return b
end
table.insert(M.section_current.instructions, { data=data, size=size, bin=bin })
end
-- byte(...)
-- Declare bytes to go into the binary stream.
-- Each argument can be either:
-- * a number resolving to a valid range byte
-- * a string, converted to bytes using the charset previously defined,
-- or Lua's charset if none was defined
-- * a table, with each entry resolving to a valid range byte
-- * a function, resolving to exactly one valid range byte, evaluated
-- after symbols have been resolved
M.byte = function(...)
return M.byte_impl({...}, byte_normalize)
end
local byte_encapsulate = function(args)
for k,v in ipairs(args) do
local vt = type(v)
if vt == 'string' or vt == 'table' and (v.type == 'section' or v.type == 'label') then
args[k] = function() return v end
end
end
return args
end
M.byte_hi = function(...)
return M.byte_impl(byte_encapsulate{...}, function(v) return (v>>8)&0xff end)
end
M.byte_lo = function(...)
return M.byte_impl(byte_encapsulate{...}, function(v) return v&0xff end)
end
-- word(...)
-- Declare words to go into the binary stream.
-- Each argument can be either:
-- * a section or a label
-- * a number resolving to a valid range word
-- * a table, with each entry resolving to a valid range word
-- * a function, resolving to exactly one valid range word, evaluated
-- after symbols have been resolved
M.word = function(...)
local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
local args = {...}
local data = {}
for k,v in ipairs(args) do
local t = type(v)
if t == 'number' or t == 'function' or t == 'string' then data[#data+1] = v
elseif t == 'table' then
if v.type == 'section' or v.type == 'label' then data[#data+1] = function() return v end
else table.move(v,1,#v,#data+1,data) end
else error("unsupported type for word() argument: " .. t .. ", value: " .. v)
end
end
local size = function()
for i,v in ipairs(data) do data[i] = size_dc(v) end
return #data*2
end
local bin = function() local l65dbg=l65dbg
local b={}
for k,v in ipairs(data) do
if type(v) == 'function' then v = v() end
local vt = type(v)
if vt == 'table' and v.label then v = symbols[v.label]
elseif vt == 'string' then v = symbols[v] end
if type(v) ~= 'number' then error("unresolved symbol for dc.w, index " .. k) end
v = word_normalize(v)
b[#b+1] = v&0xff
b[#b+1] = v>>8
end
return b
end
table.insert(M.section_current.instructions, { data=data, size=size, bin=bin })
end
M.long = function(...)
local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
local args = {...}
local data = {}
for k,v in ipairs(args) do
local t = type(v)
if t == 'number' or t == 'function' or t == 'string' then data[#data+1] = v
elseif t == 'table' then
if v.type == 'section' or v.type == 'label' then data[#data+1] = function() return v end
else table.move(v,1,#v,#data+1,data) end
else error("unsupported type for long() argument: " .. t .. ", value: " .. v)
end
end
local size = function()
for i,v in ipairs(data) do data[i] = size_dc(v) end
return #data*4
end
local bin = function() local l65dbg=l65dbg
local b={}
for k,v in ipairs(data) do
if type(v) == 'function' then v = v() end
local vt = type(v)
if vt == 'table' and v.label then v = symbols[v.label]
elseif vt == 'string' then v = symbols[v] end
if type(v) ~= 'number' then error("unresolved symbol for dc.l, index " .. k) end
v = long_normalize(v)
b[#b+1] = v&0xff
b[#b+1] = (v>>8)&0xff
b[#b+1] = (v>>16)&0xff
b[#b+1] = v>>24
end
return b
end
table.insert(M.section_current.instructions, { data=data, size=size, bin=bin })
end
local op = function(code, cycles, extra_on_crosspage)
return { opc=code, cycles=cycles or cycles_def, xcross=extra_on_crosspage or xcross_def }
end
M.op = op
local op_eval = function(late, early)
local x = early or 0
return type(late) == 'function' and late(x,op_resolve) or x+op_resolve(late)
end
M.op_eval = op_eval
return M