diff --git a/.travis.yml b/.travis.yml index e67f384..ad3969b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ script: - cmake . -DCMAKE_BUILD_TYPE=Release - make - cd samples; for f in *.l65; do echo $f; ../l65 $f || break; done; cd .. + - cd samples; for f in *.l7801; do echo $f; ../l7801 $f || break; done; cd .. - cp l65 l65-$TRAVIS_TAG-$TRAVIS_OS_NAME deploy: provider: releases diff --git a/6502.lua b/6502.lua index d7d4548..f105857 100644 --- a/6502.lua +++ b/6502.lua @@ -1,883 +1,77 @@ -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.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 - if rorg(cstart) // 0x100 == rorg(cfinish) // 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<= 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.idb.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 section " .. section.label) - 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) - 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 not filler then filler = 0 end -- brk opcode - 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 - local f = assert(io.open(filename, "wb"), "failed to open " .. filename .. " for writing") - f:write(string.char(table.unpack(bin))) - 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 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 -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 -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 - -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 +M = require "asm" -- Return a value in rage [0x00, 0xff] if x is to use zeropage addressing mode. Defaults to range [0x0000-0x00ff]. -M.zeropage = function(x) if x >= -128 and x <= 0xff then return byte_normalize(x) end end +M.zeropage = function(x) if x >= -128 and x <= 0xff then return M.byte_normalize(x) end end -local op,cycles_def,xcross_def -op = function(code, cycles, extra_on_crosspage) - return { opc=code, cycles=cycles or cycles_def, xcross=extra_on_crosspage or xcross_def } -end -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 local op_eval_byte = function(late, early, nozp) local v = op_eval(late, early) local zpv = zeropage(v) if not nozp and zpv then return zpv end return byte_normalize(v) end +M.op_eval_byte = op_eval_byte + local op_eval_word = function(late, early) return word_normalize(op_eval(late, early)) end +M.op_eval_word = op_eval_word + +local cycles_def,xcross_def + cycles_def=2 xcross_def=0 local opimp={ - asl=op(0x0a), brk=op(0x00,7), clc=op(0x18), cld=op(0xd8), cli=op(0x58), clv=op(0xb8), dex=op(0xca), dey=op(0x88), - inx=op(0xe8), iny=op(0xc8), lsr=op(0x4a), nop=op(0xea), pha=op(0x48,3), php=op(0x08,3), pla=op(0x68,4), plp=op(0x28,4), - rol=op(0x2a), ror=op(0x6a), rti=op(0x40,6), rts=op(0x60,6), sec=op(0x38), sei=op(0x78), tax=op(0xaa), tay=op(0xa8), - tsx=op(0xba), txa=op(0x8a), txs=op(0x9a), tya=op(0x98), - jam=op(0x02,0), + asl=M.op(0x0a), brk=M.op(0x00,7), clc=M.op(0x18), cld=M.op(0xd8), cli=M.op(0x58), clv=M.op(0xb8), dex=M.op(0xca), dey=M.op(0x88), + inx=M.op(0xe8), iny=M.op(0xc8), lsr=M.op(0x4a), nop=M.op(0xea), pha=M.op(0x48,3), php=M.op(0x08,3), pla=M.op(0x68,4), plp=M.op(0x28,4), + rol=M.op(0x2a), ror=M.op(0x6a), rti=M.op(0x40,6), rts=M.op(0x60,6), sec=M.op(0x38), sei=M.op(0x78), tax=M.op(0xaa), tay=M.op(0xa8), + tsx=M.op(0xba), txa=M.op(0x8a), txs=M.op(0x9a), tya=M.op(0x98), + jam=M.op(0x02,0), } M.opimp = opimp for k,v in pairs(opimp) do M[k .. 'imp'] = function() table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc }) end end + cycles_def=2 xcross_def=0 local opimm={ - adc=op(0x69), ['and']=op(0x29), cmp=op(0xc9), cpx=op(0xe0), cpy=op(0xc0), eor=op(0x49), lda=op(0xa9), ldx=op(0xa2), - ldy=op(0xa0), ora=op(0x09), sbc=op(0xe9), - anc=op(0x0b), ane=op(0x8b), arr=op(0x6b), asr=op(0x4b), jam=op(0x12,0), lax=op(0xab), nop=op(0x80), sbx=op(0xcb), + adc=M.op(0x69), ['and']=M.op(0x29), cmp=M.op(0xc9), cpx=M.op(0xe0), cpy=M.op(0xc0), eor=M.op(0x49), lda=M.op(0xa9), ldx=M.op(0xa2), + ldy=M.op(0xa0), ora=M.op(0x09), sbc=M.op(0xe9), + anc=M.op(0x0b), ane=M.op(0x8b), arr=M.op(0x6b), asr=M.op(0x4b), jam=M.op(0x12,0), lax=M.op(0xab), nop=M.op(0x80), sbx=M.op(0xcb), } M.opimm = opimm for k,v in pairs(opimm) do M[k .. 'imm'] = function(late, early) local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } - local size = function() late,early = size_op(late,early) return 2 end - local bin = function() local l65dbg=l65dbg return { v.opc, op_eval_byte(late,early,true) } end + local size = function() late,early = M.size_op(late,early) return 2 end + local bin = function() local l65dbg=l65dbg return { v.opc, M.op_eval_byte(late,early,true) } end table.insert(M.section_current.instructions, { size=size, cycles=2, bin=bin }) end end + cycles_def=3 xcross_def=0 local opzpg={ - adc=op(0x65), ['and']=op(0x25), asl=op(0x06,5), bit=op(0x24), cmp=op(0xc5), cpx=op(0xe4), cpy=op(0xc4), dec=op(0xc6,5), - eor=op(0x45), inc=op(0xe6,5), lda=op(0xa5), ldx=op(0xa6), ldy=op(0xa4), lsr=op(0x46,5), ora=op(0x05), rol=op(0x26,5), - ror=op(0x66,5), sbc=op(0xe5), sta=op(0x85), stx=op(0x86), sty=op(0x84), - dcp=op(0xc7,5), isb=op(0xe7,5), jam=op(0x22,0), lax=op(0xa7), nop=op(0x04), rla=op(0x27,5), rra=op(0x67,5), sax=op(0x87), - slo=op(0x07,5), sre=op(0x47,5), + adc=M.op(0x65), ['and']=M.op(0x25), asl=M.op(0x06,5), bit=M.op(0x24), cmp=M.op(0xc5), cpx=M.op(0xe4), cpy=M.op(0xc4), dec=M.op(0xc6,5), + eor=M.op(0x45), inc=M.op(0xe6,5), lda=M.op(0xa5), ldx=M.op(0xa6), ldy=M.op(0xa4), lsr=M.op(0x46,5), ora=M.op(0x05), rol=M.op(0x26,5), + ror=M.op(0x66,5), sbc=M.op(0xe5), sta=M.op(0x85), stx=M.op(0x86), sty=M.op(0x84), + dcp=M.op(0xc7,5), isb=M.op(0xe7,5), jam=M.op(0x22,0), lax=M.op(0xa7), nop=M.op(0x04), rla=M.op(0x27,5), rra=M.op(0x67,5), sax=M.op(0x87), + slo=M.op(0x07,5), sre=M.op(0x47,5), } M.opzpg = opzpg for k,v in pairs(opzpg) do M[k .. 'zpg'] = function(late, early) local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } - local size = function() late,early = size_op(late,early) return 2 end - local bin = function() local l65dbg=l65dbg return { v.opc, op_eval_byte(late,early) } end + local size = function() late,early = M.size_op(late,early) return 2 end + local bin = function() local l65dbg=l65dbg return { v.opc, M.op_eval_byte(late,early) } end table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) end end + cycles_def=4 xcross_def=0 local opabs={ - adc=op(0x6d), ['and']=op(0x2d), asl=op(0x0e,6), bit=op(0x2c), cmp=op(0xcd), cpx=op(0xec), cpy=op(0xcc), dec=op(0xce,6), - eor=op(0x4d), inc=op(0xee,6), jmp=op(0x4c,3), jsr=op(0x20,6), lda=op(0xad), ldx=op(0xae), ldy=op(0xac), lsr=op(0x4e,6), - ora=op(0x0d), rol=op(0x2e,6), ror=op(0x6e,6), sbc=op(0xed), sta=op(0x8d), stx=op(0x8e), sty=op(0x8c), - dcp=op(0xcf,6), isb=op(0xef,6), jam=op(0x72,0), lax=op(0xaf), nop=op(0x0c), rla=op(0x2f,6), rra=op(0x6f,6), sax=op(0x8f), - slo=op(0x0f,6), sre=op(0x4f,6), + adc=M.op(0x6d), ['and']=M.op(0x2d), asl=M.op(0x0e,6), bit=M.op(0x2c), cmp=M.op(0xcd), cpx=M.op(0xec), cpy=M.op(0xcc), dec=M.op(0xce,6), + eor=M.op(0x4d), inc=M.op(0xee,6), jmp=M.op(0x4c,3), jsr=M.op(0x20,6), lda=M.op(0xad), ldx=M.op(0xae), ldy=M.op(0xac), lsr=M.op(0x4e,6), + ora=M.op(0x0d), rol=M.op(0x2e,6), ror=M.op(0x6e,6), sbc=M.op(0xed), sta=M.op(0x8d), stx=M.op(0x8e), sty=M.op(0x8c), + dcp=M.op(0xcf,6), isb=M.op(0xef,6), jam=M.op(0x72,0), lax=M.op(0xaf), nop=M.op(0x0c), rla=M.op(0x2f,6), rra=M.op(0x6f,6), sax=M.op(0x8f), + slo=M.op(0x0f,6), sre=M.op(0x4f,6), } M.opabs = opabs for k,v in pairs(opabs) do M[k .. 'abs'] = function(late, early) local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } - local size = function() late,early = size_op(late,early) return 3 end + local size = function() late,early = M.size_op(late,early) return 3 end local bin = function() local l65dbg=l65dbg - local x = op_eval_word(late,early) + local x = M.op_eval_word(late,early) return { v.opc, x&0xff, x>>8 } end table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) @@ -897,10 +91,10 @@ for k,_ in pairs(opzab) do local abs = opabs[k] local ins = { cycles=abs.cycles } ins.size = function() local l65dbg=l65dbg - local r,x = M.pcall_za(op_eval, late, early) + local r,x = M.pcall_za(M.op_eval, late, early) if not r then return 3 end - size_ref(x) - x = word_normalize(x) + M.size_ref(x) + x = M.word_normalize(x) local zpg,zpv = opzpg[k], zeropage(x) if zpv and zpg then ins.size = 2 @@ -913,7 +107,7 @@ for k,_ in pairs(opzab) do return 3 end ins.bin = function() local l65dbg=l65dbg - local x = word_normalize(op_eval(late, early)) + local x = M.word_normalize(M.op_eval(late, early)) -- since we assumed absolute on link phase, we must generate absolute in binary if zeropage(x) and opzpg[k] then io.stderr:write("warning: forcing abs on zpg operand for opcode " .. k .. "\n") end return { abs.opc, x&0xff, x>>8 } @@ -921,31 +115,33 @@ for k,_ in pairs(opzab) do table.insert(M.section_current.instructions, ins) end end + cycles_def=4 xcross_def=0 local opzpx={ - adc=op(0x75), ['and']=op(0x35), asl=op(0x16,6), cmp=op(0xd5), dec=op(0xd6,6), eor=op(0x55), inc=op(0xf6,6), lda=op(0xb5), - ldy=op(0xb4), lsr=op(0x56,6), ora=op(0x15), rol=op(0x36,6), ror=op(0x76,6), sbc=op(0xf5), sta=op(0x95), sty=op(0x94), - dcp=op(0xd7,6), isb=op(0xf7,6), jam=op(0x32,0), nop=op(0x14), rla=op(0x37,6), rra=op(0x77,6), slo=op(0x17,6), sre=op(0x57,6), + adc=M.op(0x75), ['and']=M.op(0x35), asl=M.op(0x16,6), cmp=M.op(0xd5), dec=M.op(0xd6,6), eor=M.op(0x55), inc=M.op(0xf6,6), lda=M.op(0xb5), + ldy=M.op(0xb4), lsr=M.op(0x56,6), ora=M.op(0x15), rol=M.op(0x36,6), ror=M.op(0x76,6), sbc=M.op(0xf5), sta=M.op(0x95), sty=M.op(0x94), + dcp=M.op(0xd7,6), isb=M.op(0xf7,6), jam=M.op(0x32,0), nop=M.op(0x14), rla=M.op(0x37,6), rra=M.op(0x77,6), slo=M.op(0x17,6), sre=M.op(0x57,6), } M.opzpx = opzpx for k,v in pairs(opzpx) do M[k .. 'zpx'] = function(late, early) local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } - local size = function() late,early = size_op(late,early) return 2 end - local bin = function() local l65dbg=l65dbg return { v.opc, op_eval_byte(late,early) } end + local size = function() late,early = M.size_op(late,early) return 2 end + local bin = function() local l65dbg=l65dbg return { v.opc, M.op_eval_byte(late,early) } end table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) end end + cycles_def=4 xcross_def=1 local opabx={ - adc=op(0x7d), ['and']=op(0x3d), asl=op(0x1e,7,0), cmp=op(0xdd), dec=op(0xde,7,0), eor=op(0x5d), inc=op(0xfe,7,0), lda=op(0xbd), - ldy=op(0xbc), lsr=op(0x5e,7,0), ora=op(0x1d), rol=op(0x3e,7,0), ror=op(0x7e,7,0), sbc=op(0xfd), sta=op(0x9d,5,0), - dcp=op(0xdf,7,0), isb=op(0xff,7,0), jam=op(0x92,0,0), nop=op(0x1c), rla=op(0x3f,7,0), rra=op(0x7f,7,0), shy=op(0x9c,5,0), slo=op(0x1f,7,0), - sre=op(0x5f,7,0), + adc=M.op(0x7d), ['and']=M.op(0x3d), asl=M.op(0x1e,7,0), cmp=M.op(0xdd), dec=M.op(0xde,7,0), eor=M.op(0x5d), inc=M.op(0xfe,7,0), lda=M.op(0xbd), + ldy=M.op(0xbc), lsr=M.op(0x5e,7,0), ora=M.op(0x1d), rol=M.op(0x3e,7,0), ror=M.op(0x7e,7,0), sbc=M.op(0xfd), sta=M.op(0x9d,5,0), + dcp=M.op(0xdf,7,0), isb=M.op(0xff,7,0), jam=M.op(0x92,0,0), nop=M.op(0x1c), rla=M.op(0x3f,7,0), rra=M.op(0x7f,7,0), shy=M.op(0x9c,5,0), slo=M.op(0x1f,7,0), + sre=M.op(0x5f,7,0), } M.opabx = opabx for k,v in pairs(opabx) do M[k .. 'abx'] = function(late, early) local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } - local size = function() late,early = size_op(late,early) return 3 end + local size = function() late,early = M.size_op(late,early) return 3 end local bin = function() local l65dbg=l65dbg - local x = op_eval_word(late,early) + local x = M.op_eval_word(late,early) return { v.opc, x&0xff, x>>8 } end table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) @@ -965,10 +161,10 @@ for k,_ in pairs(opzax) do local abx = opabx[k] local ins = { cycles=abx.cycles } ins.size = function() local l65dbg=l65dbg - local r,x = M.pcall_za(op_eval, late, early) + local r,x = M.pcall_za(M.op_eval, late, early) if not r then return 3 end - size_ref(x) - x = word_normalize(x) + M.size_ref(x) + x = M.word_normalize(x) local zpx,zpv = opzpx[k], zeropage(x) if zpv and zpx then ins.size = 2 @@ -981,7 +177,7 @@ for k,_ in pairs(opzax) do return 3 end ins.bin = function() local l65dbg=l65dbg - local x = word_normalize(op_eval(late, early)) + local x = M.word_normalize(M.op_eval(late, early)) -- since we assumed absolute on link phase, we must generate absolute in binary if zeropage(x) and opzpx[k] then io.stderr:write("warning: forcing abx on zpx operand for opcode " .. k .. "\n") end return { abx.opc, x&0xff, x>>8 } @@ -989,30 +185,32 @@ for k,_ in pairs(opzax) do table.insert(M.section_current.instructions, ins) end end + cycles_def=4 xcross_def=0 local opzpy={ - ldx=op(0xb6), stx=op(0x96), - jam=op(0x42,0), lax=op(0xb7), sax=op(0x97), + ldx=M.op(0xb6), stx=M.op(0x96), + jam=M.op(0x42,0), lax=M.op(0xb7), sax=M.op(0x97), } M.opzpy = opzpy for k,v in pairs(opzpy) do M[k .. 'zpy'] = function(late, early) local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } - local size = function() late,early = size_op(late,early) return 2 end - local bin = function() local l65dbg=l65dbg return { v.opc, op_eval_byte(late,early) } end + local size = function() late,early = M.size_op(late,early) return 2 end + local bin = function() local l65dbg=l65dbg return { v.opc, M.op_eval_byte(late,early) } end table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) end end + cycles_def=4 xcross_def=1 local opaby={ - adc=op(0x79), ['and']=op(0x39), cmp=op(0xd9), eor=op(0x59), lda=op(0xb9), ldx=op(0xbe), ora=op(0x19), sbc=op(0xf9), - sta=op(0x99,5,0), - dcp=op(0xdb,7,0), isb=op(0xfb,7,0), jam=op(0xb2,0,0), las=op(0xbb), lax=op(0xbf), rla=op(0x3b,7,0), rra=op(0x7b,7,0), sha=op(0x9f,5,0), - shs=op(0x9b,5,0), shx=op(0x9e,5,0), slo=op(0x1b,7,0), sre=op(0x5b,7,0), + adc=M.op(0x79), ['and']=M.op(0x39), cmp=M.op(0xd9), eor=M.op(0x59), lda=M.op(0xb9), ldx=M.op(0xbe), ora=M.op(0x19), sbc=M.op(0xf9), + sta=M.op(0x99,5,0), + dcp=M.op(0xdb,7,0), isb=M.op(0xfb,7,0), jam=M.op(0xb2,0,0), las=M.op(0xbb), lax=M.op(0xbf), rla=M.op(0x3b,7,0), rra=M.op(0x7b,7,0), sha=M.op(0x9f,5,0), + shs=M.op(0x9b,5,0), shx=M.op(0x9e,5,0), slo=M.op(0x1b,7,0), sre=M.op(0x5b,7,0), } M.opaby = opaby for k,v in pairs(opaby) do M[k .. 'aby'] = function(late, early) local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } - local size = function() late,early = size_op(late,early) return 3 end + local size = function() late,early = M.size_op(late,early) return 3 end local bin = function() local l65dbg=l65dbg - local x = op_eval_word(late,early) + local x = M.op_eval_word(late,early) return { v.opc, x&0xff, x>>8 } end table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) @@ -1032,10 +230,10 @@ for k,_ in pairs(opzay) do local aby = opaby[k] local ins = { cycles=aby.cycles } ins.size = function() local l65dbg=l65dbg - local r,x = M.pcall_za(op_eval, late, early) + local r,x = M.pcall_za(M.op_eval, late, early) if not r then return 3 end - size_ref(x) - x = word_normalize(x) + M.size_ref(x) + x = M.word_normalize(x) local zpy,zpv = opzpy[k], zeropage(x) if zpv and zpy then ins.size = 2 @@ -1048,7 +246,7 @@ for k,_ in pairs(opzay) do return 3 end ins.bin = function() local l65dbg=l65dbg - local x = word_normalize(op_eval(late, early)) + local x = M.word_normalize(M.op_eval(late, early)) -- since we assumed absolute on link phase, we must generate absolute in binary if zeropage(x) and opzpy[k] then io.stderr:write("warning: forcing aby on zpy operand for opcode " .. k .. "\n") end return { aby.opc, x&0xff, x>>8 } @@ -1056,8 +254,9 @@ for k,_ in pairs(opzay) do table.insert(M.section_current.instructions, ins) end end + cycles_def=2 xcross_def=0 local oprel={ - bcc=op(0x90), bcs=op(0xb0), beq=op(0xf0), bmi=op(0x30), bne=op(0xd0), bpl=op(0x10), bvc=op(0x50), bvs=op(0x70), + bcc=M.op(0x90), bcs=M.op(0xb0), beq=M.op(0xf0), bmi=M.op(0x30), bne=M.op(0xd0), bpl=M.op(0x10), bvc=M.op(0x50), bvs=M.op(0x70), } M.oprel = oprel for k,v in pairs(oprel) do M[k .. 'rel'] = function(label) @@ -1067,7 +266,7 @@ for k,v in pairs(oprel) do local op = { cycles=2 } op.size = function() offset = section.size - label = size_dc(label) + label = M.size_dc(label) return 2 end op.bin = function() local l65dbg=l65dbg @@ -1085,44 +284,47 @@ for k,v in pairs(oprel) do table.insert(M.section_current.instructions, op) end end + cycles_def=5 xcross_def=0 local opind={ - jmp=op(0x6c), - jam=op(0xd2,0), + jmp=M.op(0x6c), + jam=M.op(0xd2,0), } M.opind = opind for k,v in pairs(opind) do M[k .. 'ind'] = function(late, early) local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } - local size = function() late,early = size_op(late,early) return 3 end + local size = function() late,early = M.size_op(late,early) return 3 end local bin = function() local l65dbg=l65dbg - local x = op_eval_word(late,early) + local x = M.op_eval_word(late,early) return { v.opc, x&0xff, x>>8 } end table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) end end + cycles_def=6 xcross_def=0 local opinx={ - adc=op(0x61), ['and']=op(0x21), cmp=op(0xc1), eor=op(0x41), lda=op(0xa1), ora=op(0x01), sbc=op(0xe1), sta=op(0x81), - dcp=op(0xc3,8), isb=op(0xe3,8), jam=op(0x52,0), lax=op(0xa3), rla=op(0x23,8), rra=op(0x63,8), sax=op(0x83), slo=op(0x03,8), - sre=op(0x43,8), + adc=M.op(0x61), ['and']=M.op(0x21), cmp=M.op(0xc1), eor=M.op(0x41), lda=M.op(0xa1), ora=M.op(0x01), sbc=M.op(0xe1), sta=M.op(0x81), + dcp=M.op(0xc3,8), isb=M.op(0xe3,8), jam=M.op(0x52,0), lax=M.op(0xa3), rla=M.op(0x23,8), rra=M.op(0x63,8), sax=M.op(0x83), slo=M.op(0x03,8), + sre=M.op(0x43,8), } M.opinx = opinx for k,v in pairs(opinx) do M[k .. 'inx'] = function(late, early) local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } - local size = function() late,early = size_op(late,early) return 2 end - local bin = function() local l65dbg=l65dbg return { v.opc, op_eval_byte(late,early) } end + local size = function() late,early = M.size_op(late,early) return 2 end + local bin = function() local l65dbg=l65dbg return { v.opc, M.op_eval_byte(late,early) } end table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) end end + cycles_def=5 xcross_def=1 local opiny={ - adc=op(0x71), ['and']=op(0x31), cmp=op(0xd1), eor=op(0x51), lda=op(0xb1), ora=op(0x11), sbc=op(0xf1), sta=op(0x91,6), - dcp=op(0xd3,8), isb=op(0xf3,8), jam=op(0x62,0,0), lax=op(0xb3), rla=op(0x33,8), rra=op(0x73,8), sha=op(0x93,6), slo=op(0x13,8), - sre=op(0x53,8), + adc=M.op(0x71), ['and']=M.op(0x31), cmp=M.op(0xd1), eor=M.op(0x51), lda=M.op(0xb1), ora=M.op(0x11), sbc=M.op(0xf1), sta=M.op(0x91,6), + dcp=M.op(0xd3,8), isb=M.op(0xf3,8), jam=M.op(0x62,0,0), lax=M.op(0xb3), rla=M.op(0x33,8), rra=M.op(0x73,8), sha=M.op(0x93,6), slo=M.op(0x13,8), + sre=M.op(0x53,8), } for k,v in pairs(opiny) do M[k .. 'iny'] = function(late, early) local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } - local size = function() late,early = size_op(late,early) return 2 end - local bin = function() local l65dbg=l65dbg return { v.opc, op_eval_byte(late,early) } end + local size = function() late,early = M.size_op(late,early) return 2 end + local bin = function() local l65dbg=l65dbg return { v.opc, M.op_eval_byte(late,early) } end table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) end end diff --git a/CMakeLists.txt b/CMakeLists.txt index b16cbac..df1762b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ set(L65_HEADERS file(GLOB L65_FILES ${L65_SOURCE_DIR}/*.l65) set(L65_SCRIPTS + ${L65_SOURCE_DIR}/asm.lua ${L65_SOURCE_DIR}/6502.lua ${L65_SOURCE_DIR}/dkjson.lua ${L65_SOURCE_DIR}/l65.lua @@ -116,3 +117,42 @@ endif() target_link_libraries(embed ${LINKLIBS}) target_link_libraries(${PROJECT_NAME} ${LINKLIBS}) + +set(L7801_SOURCES + ${L65_SOURCE_DIR}/lfs.c + ${L65_SOURCE_DIR}/lpeg.c + ${L65_SOURCE_DIR}/l7801.c + ) +set(L7801_HEADERS + ${L65_HEADERS} + ) + +set(L7801_FILES ${L65_SOURCE_DIR}/scv.l7801) + +set(L7801_SCRIPTS + ${L65_SOURCE_DIR}/asm.lua + ${L65_SOURCE_DIR}/uPD7801.lua + ${L65_SOURCE_DIR}/dkjson.lua + ${L65_SOURCE_DIR}/l7801.lua + ${L65_BINARY_DIR}/l65cfg.lua + ${L65_SOURCE_DIR}/re.lua + ${L7801_FILES} + ) + +add_custom_command( + OUTPUT ${L65_BINARY_DIR}/scripts_7801.h + COMMAND embed -o ${L65_BINARY_DIR}/scripts_7801.h ${L7801_SCRIPTS} + DEPENDS embed ${L7801_SCRIPTS} +) +add_custom_target(prereq_7801 DEPENDS ${L65_BINARY_DIR}/scripts_7801.h) +add_executable(l7801 ${L7801_SOURCES} ${L7801_HEADERS} ${L7801_FILES}) +add_dependencies(l7801 prereq_7801) +set_property(TARGET l7801 PROPERTY C_STANDARD 99) +target_include_directories(l7801 PRIVATE "${L7801_SOURCE_DIR}" "${L65_BINARY_DIR}") +target_link_libraries(l7801 ${LINKLIBS}) + + +install(TARGETS l65 l7801 + RUNTIME DESTINATION bin) +install(DIRECTORY samples DESTINATION doc) +install(FILES README.md DESTINATION doc) diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..d9a746b --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,41 @@ +version: '0.1.{build}' + +os: Visual Studio 2013 +platform: x64 +configuration: Release + +before_build: + - mkdir install + - mkdir build + - cd build + - cmake -G "Visual Studio 12 2013 Win64" -DCMAKE_INSTALL_PREFIX=../install .. + +build_script: + - FOR /F "tokens=*" %%i in ('git describe') do SET COMMITNOW=%%i + - if defined APPVEYOR_REPO_TAG_NAME (set L65_RELEASE=true) else (set L65_SNAPSHOT=true) + - if defined L65_RELEASE set L65_VERSION=%APPVEYOR_REPO_TAG_NAME:~1% + - if defined L65_RELEASE echo Building l65 %L65_VERSION%... (from %COMMITNOW%) + - if defined L65_SNAPSHOT set L65_VERSION=%APPVEYOR_BUILD_VERSION% + - if defined L65_SNAPSHOT echo Building l65 snapshot %L65_VERSION%... (from %COMMITNOW%) + - cmake --build . --config Release + - cmake --build . --config Release --target install + +after_build: + - cd ../install + - 7z a ../l65.zip * -tzip + +artifacts: + - path: l65.zip + name: l65-${L65_VERSION}.zip + +deploy: + - provider: GitHub + release: l65-${L65_VERSION} + description: 'l65 msvc12 win64 build' + auth_token: + secure: xRIravp3mvMiAgogn6KGuK1yrolmSJUsum/wPXwu82bh97O7YkuQ3B178ac+WHml + artifact: /l65.*\.zip/ + draft: true + on: + appveyor_repo_tag: true + push_release: true diff --git a/asm.lua b/asm.lua new file mode 100644 index 0000000..f1e7963 --- /dev/null +++ b/asm.lua @@ -0,0 +1,826 @@ +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.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 + if rorg(cstart) // 0x100 == rorg(cfinish) // 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<= 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.idb.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 section " .. section.label) + 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) + 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 not filler then filler = 0 end -- brk opcode + 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 + local f = assert(io.open(filename, "wb"), "failed to open " .. filename .. " for writing") + f:write(string.char(table.unpack(bin))) + 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 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 \ No newline at end of file diff --git a/l65.lua b/l65.lua index 0db5ef0..a9f2dae 100644 --- a/l65.lua +++ b/l65.lua @@ -32,7 +32,7 @@ local Keywords = lookupify{ 'return', 'then', 'true', 'until', 'while', }; --- 6502 +-------------------------------------------------------- 6502::begin local Keywords_control = { -- control keywords 'samepage', 'crosspage', @@ -168,6 +168,7 @@ local addressing_map = { iny = opcode_indirect_y, rel = opcode_relative, } +-------------------------------------------------------- 6502::end local Scope = { new = function(self, parent) diff --git a/l7801.c b/l7801.c new file mode 100644 index 0000000..a403714 --- /dev/null +++ b/l7801.c @@ -0,0 +1,222 @@ +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#define STBI_ONLY_PNG +#define STBI_NO_FAILURE_STRINGS +#include "stb_image.h" + +#define LUA_IMPLEMENTATION +#include "lua.h" +#include "scripts_7801.h" + +extern int luaopen_lpeg(lua_State *L); +extern int luaopen_lfs(lua_State *L); + +// l7801 lib +static int r_s32be(uint8_t **b) { uint8_t *p = *b; int v = ((int)(p[0]))<<24 | ((int)(p[1]))<<16 | ((int)p[2])<<8 | p[3]; *b += 4; return v; } +typedef struct { int len, nam; } chunk_s; +static chunk_s r_chunk(uint8_t **b) { int len = r_s32be(b), nam = r_s32be(b); chunk_s c = { len, nam }; return c; } +static int open_image(lua_State *L) +{ + const char *filename = luaL_checkstring(L, 1); + FILE *file = fopen(filename, "rb"); + if (!file) + { + lua_pushnil(L); + lua_pushfstring(L, "failed to open file %s", filename); + return 2; + } + fseek(file, 0, SEEK_END); + size_t sz = ftell(file); + fseek(file, 0, SEEK_SET); + uint8_t *png = malloc(sz); + fread(png, sz, 1, file); + fclose(file); + static uint8_t png_sig[8] = { 137,80,78,71,13,10,26,10 }; + if (memcmp(png, png_sig, 8) != 0) + { + free(png); + lua_pushnil(L); + lua_pushfstring(L, "file %s is not a PNG", filename); + return 2; + } + uint8_t *b = png + 8; + int w, h; + uint8_t *d = 0; long d_sz = 0; +#define CHUNK_NAM(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + for (;;) + { + chunk_s chunk = r_chunk(&b); + switch (chunk.nam) + { + case CHUNK_NAM('I','H','D','R'): { + w = r_s32be(&b); h = r_s32be(&b); + if (b[0] != 8 || b[1] != 3) + { + free(png); + lua_pushnil(L); + lua_pushfstring(L, "PNG file %s must be 8b indexed", filename); + return 2; + } + b += 9; + } break; + case CHUNK_NAM('I','D','A','T'): { + d = realloc(d, d_sz + chunk.len); + memcpy(d + d_sz, b, chunk.len); + d_sz += chunk.len; + b += chunk.len+4; + } break; + case CHUNK_NAM('I','E','N','D'): { + free(png); + if (!d) + { + lua_pushnil(L); + lua_pushfstring(L, "invalid PNG file %s", filename); + return 2; + } + int px_sz; + uint8_t *px_raw = (uint8_t*)stbi_zlib_decode_malloc_guesssize_headerflag((void*)d, d_sz, (w+1) * h, &px_sz, 1); + free(d); + uint8_t *px = calloc(w,h); + uint8_t *px0 = px, *px_raw0 = px_raw; + for (int y = 0; y < h; ++y) + { + int filter = *px_raw++; + #define prev (x==0 ? 0 : px[x-1]) + #define up (px[x-w]) + #define prevup (x==0 ? 0 : px[x-w-1]) + switch (filter) + { + case 0: memcpy(px, px_raw, w); break; + case 1: for (int x = 0; x < w; ++x) { px[x] = px_raw[x] + prev; } break; + case 2: for (int x = 0; x < w; ++x) { px[x] = px_raw[x] + up; } break; + case 3: for (int x = 0; x < w; ++x) { px[x] = px_raw[x] + ((prev+up)>>1); } break; + case 4: for (int x = 0; x < w; ++x) { px[x] = px_raw[x] + stbi__paeth(prev,up,prevup); } break; + } + #undef prev + #undef up + #undef prevup + px += w; + px_raw += w; + } + STBI_FREE(px_raw0); + + lua_createtable(L, w*h, 3); + lua_pushstring(L, filename); + lua_setfield(L, -2, "filename"); + lua_pushinteger(L, w); + lua_setfield(L, -2, "width"); + lua_pushinteger(L, h); + lua_setfield(L, -2, "height"); + for (int i = 0; i < w*h; ++i) + { + lua_pushinteger(L, px0[i]); + lua_rawseti(L, -2, i+1); + } + free(px0); + return 1; + } + default: + b += chunk.len+4; + } + } +#undef CHUNK_NAM + if (d) free(d); + free(png); + lua_pushnil(L); + lua_pushfstring(L, "invalid PNG file %s", filename); + return 2; +} +static const struct luaL_Reg l7801lib[] = { + {"image", open_image}, + {NULL, NULL}, +}; +static int luaopen_l7801(lua_State *L) +{ + luaL_newlib(L, l7801lib); + return 1; +} + +#define SRC_LUA(name) { #name, 0, script_ ## name ## _lua, sizeof(script_ ## name ## _lua) } +#define SRC_L7801(name) { #name, 1, script_ ## name ## _l7801, sizeof(script_ ## name ## _l7801) } +static struct script { const char *name; int t; const char *data; size_t sz; } embedded[] = { + SRC_LUA(dkjson), + SRC_LUA(l65cfg), + SRC_LUA(re), + SRC_L7801(scv), +}; +#undef SRC_LUA +#undef SRC_L7801 + +static int getembedded(lua_State *L) +{ + const char *name = lua_tostring(L, 1); + for (struct script *s = embedded, *e = s + sizeof(embedded) / sizeof(embedded[0]); s != e; ++s) + { + if (!strcmp(s->name, name)) + { + lua_pushlstring(L, s->data, s->sz); + lua_pushboolean(L, s->t); + return 2; + } + } + return 0; +} + +static int msghandler(lua_State *L) +{ + const char *msg = lua_tostring(L, 1); + if (msg == NULL) + { + if (luaL_callmeta(L, 1, "__tostring") && lua_type(L, -1) == LUA_TSTRING) + return 1; + msg = lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1)); + } + luaL_traceback(L, L, msg, 1); + return 1; +} + +int main(int argc, char *argv[]) +{ + lua_State *L = luaL_newstate(); + luaL_openlibs(L); + luaL_requiref(L, "lpeg", luaopen_lpeg, 1); lua_pop(L, 1); + luaL_requiref(L, "lfs", luaopen_lfs, 1); lua_pop(L, 1); + luaL_requiref(L, "l7801", luaopen_l7801, 1); lua_pop(L, 1); + + // preload embedded lua scripts + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + luaL_loadbufferx(L, script_asm_lua, sizeof(script_asm_lua), "asm.lua", "b"); + lua_setfield(L, -2, "asm"); + luaL_loadbufferx(L, script_uPD7801_lua, sizeof(script_uPD7801_lua), "uPD7801.lua", "b"); + lua_setfield(L, -2, "uPD7801"); + lua_pop(L, 1); + + // error handler + lua_pushcfunction(L, msghandler); + // l65.lua script + luaL_loadbufferx(L, script_l7801_lua, sizeof(script_l7801_lua), "l7801.lua", "b"); + // arg[] table + lua_createtable(L, argc-1, 2); + lua_pushcfunction(L, getembedded); // pass embedded script lookup function as arg[-1] + lua_rawseti(L, -2, -1); + for (int i = 0; i < argc; i++) lua_pushstring(L, argv[i]), lua_rawseti(L, -2, i); + lua_pushvalue(L, -1); + lua_setglobal(L, "arg"); + // ... arguments + { int i; for (i = 1; i < argc; ++i) lua_rawgeti(L, -i, i); lua_remove(L, -i); } + // call l7801 + int status = lua_pcall(L, argc-1, 0, -argc-1); + if (status != LUA_OK) + { + const char *msg = lua_tostring(L, -1); + fprintf(stderr, "%s\n", msg); + lua_pop(L, 1); + } + lua_pop(L, 1); // remove msghandler + lua_close(L); + return status; +} + diff --git a/l7801.lua b/l7801.lua new file mode 100644 index 0000000..491c667 --- /dev/null +++ b/l7801.lua @@ -0,0 +1,2751 @@ +#!/usr/bin/env lua + +local function lookupify(tb, dst, not_val) + if not dst then dst = tb end + local val = not not_val + for _, v in pairs(tb) do + dst[v] = val + end + return tb +end + +local WhiteChars = lookupify{' ', '\n', '\t', '\r'} +local Spaces = lookupify{' ', '\t'} +local EscapeLookup = {['\r'] = '\\r', ['\n'] = '\\n', ['\t'] = '\\t', ['"'] = '\\"', ["'"] = "\\'"} +local LowerChars = lookupify{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', + 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'} +local UpperChars = lookupify{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', + 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'} +local Digits = lookupify{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'} +local HexDigits = lookupify{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f'} +local BinDigits = lookupify{'0', '1'} + +local Symbols = lookupify{'+', '-', '*', '/', '^', '%', ',', '{', '}', '[', ']', '(', ')', ';', '#', '&', '|', '!'} + +local Keywords = lookupify{ + 'and', 'break', 'do', 'else', 'elseif', + 'end', 'false', 'for', 'function', 'goto', 'if', + 'in', 'local', 'nil', 'not', 'or', 'repeat', + 'return', 'then', 'true', 'until', 'while', +}; + +local Keywords_control = { +} +local Keywords_data = { + 'dc', +} + +local Keywords_7801 = { + 'aci','adi','adinc','ani','bit0','bit1','bit2','bit3', + 'bit4','bit5','bit6','bit7','block','calb','calf','call', + 'calt','clc','ei','eqi','eqiw','daa','di','dcr', + 'dcrw','dcx','ex','exx','gti','halt','in','inr','inrw', + 'inx','jb','jmp','jr','jre','ldaw','ldax','ldaxd', + 'ldaxi','lti','lxi','mov','mvi','mviw','mvix','nei', + 'nop','offi','oni','ori','out','pen','per','pex','pop','push','ret', + 'reti','rets','rld','rll','rlr','rrd','sbi','sio','skc', + 'skit','sknit','skz','sknc','sknz','sll','slr','softi', + 'staw','stax','staxd','staxi','stc','stm','sui','suinb', + 'table','xri', + 'ana','xra','ora','addnc','gta','subnb','lta','add','adc','sub','nea','sbb','eqa', + 'ona','offa', + 'anaw','xraw','oraw','addncw','gtaw','subnbw','ltaw','addw','onaw','adcw','offaw','subw','neaw','sbbw','eqaw', + 'sspd','lspd','sbcd','lbcd','sded','lded','shld','lhld', + 'anax','xrax','orax','addncx','gtax','subnbx','ltax','addx','onax','adcx','offax','subx','neax','sbbx','eqax', + 'anaxi','xraxi','oraxi','addncxi','gtaxi','subnbxi','ltaxi','addxi','onaxi','adcxi','offaxi','subxi','neaxi','sbbxi','eqaxi', + 'anaxd','xraxd','oraxd','addncxd','gtaxd','subnbxd','ltaxd','addxd','onaxd','adcxd','offaxd','subxd','neaxd','sbbxd','eqaxd', +} +local Registers_7801 = { + a=8,b=8,c=8,d=8,e=8,h=8,l=8,v=8, + bc=16,de=16,hl=16,sp=16,va=16, + pa=8,pb=8,pc=8,mk=8,mb=8,mc=8,tm0=8,tm1=8,s=8, +} +local Interrupts_7801 = lookupify{ + 'f0','ft','f1','f2','fs' +} + +local function syntax7801(on) + syntax7801_on = on + lookupify(Keywords_control, Keywords, not on) + lookupify(Keywords_data, Keywords, not on) + lookupify(Keywords_7801, Keywords, not on) +end +syntax7801(true) + +local opcode_arg_encapsulate_on +local function opcode_arg_encapsulate(on) + opcode_arg_encapsulate_on = not 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_alias = {} -- alternate user names for opcodes +local opcode_implied = lookupify{ + 'block','calb','clc','ei','daa','dcr','di','ex','exx','halt','inr','jb','nop','pen', + 'per','pex','ret','reti','rets','rld','rrd','sio','skc','skz','sknc','sknz','softi', + 'stc','stm','table' +} +local opcode_immediate = lookupify{ + 'calf','calt','call','in','jmp','out', +} +local opcode_wa = lookupify{ + 'inrw','ldaw','dcrw','staw', + 'bit0','bit1','bit2','bit3','bit4','bit5','bit6','bit7', + 'anaw','xraw','oraw','addncw','gtaw','subnbw','ltaw','addw','onaw','adcw','offaw','subw','neaw','sbbw','eqaw', +} +local opcode_wab = lookupify{ + 'mviw','eqiw' +} +local opcode_relative = lookupify{ + 'jr','jre' +} +local opcode_reg = lookupify{ + 'dcr','dcx','inr','inx','pop','push','rll','rlr','sll','slr', +} +local opcode_mov = lookupify{ + 'mov' +} +local opcode_reg_reg = lookupify{ + 'ana','xra','ora','addnc','gta','subnb','lta','add','adc','sub','nea','sbb','eqa','ona','offa' +} +local opcode_regb = lookupify{ + 'aci','adi','adinc','ani','eqi','gti','lti','mvi','nei','offi','oni','ori','sbi','sui','suinb','xri', +} +local opcode_reg_ind = lookupify{ + 'ldax','ldaxd','ldaxi', + 'stax','staxd','staxi', + 'anax','xrax','orax','addncx','gtax','subnbx','ltax','addx','onax','adcx','offax','subx','neax','sbbx','eqax', + 'anaxi','xraxi','oraxi','addncxi','gtaxi','subnbxi','ltaxi','addxi','onaxi','adcxi','offaxi','subxi','neaxi','sbbxi','eqaxi', + 'anaxd','xraxd','oraxd','addncxd','gtaxd','subnbxd','ltaxd','addxd','onaxd','adcxd','offaxd','subxd','neaxd','sbbxd','eqaxd', +} +local opcode_reg_ind_ex = lookupify{ + 'mvix' +} +local opcode_regw = lookupify{ + 'lxi' +} +local opcode_timer = lookupify{ + 'skit','sknit', +} +local opcode_ind = lookupify{ + 'sspd','lspd','sbcd','lbcd','sded','lded','shld','lhld', +} +local opcode_reg_list = { + a = lookupify{'aci','adi','adinc','ani','dcr','inr','eqi','gti','lti','mvi','nei','offi','oni','ori','rll','rlr','sbi','sll','slr','sui','suinb','xri'}, + b = lookupify{'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi','dcr','inr','mvi'}, + c = lookupify{'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi','dcr','inr','mvi','rll','rlr','sll','slr'}, + d = lookupify{'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi','mvi'}, + e = lookupify{'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi','mvi'}, + h = lookupify{'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi','mvi'}, + l = lookupify{'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi','mvi'}, + v = lookupify{'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi', + 'inrw','ldaw','dcrw','eqiw','mvi','mviw','staw', + 'bit0','bit1','bit2','bit3','bit4','bit5','bit6','bit7', + 'anaw','xraw','oraw','addncw','gtaw','subnbw','ltaw','addw','onaw','adcw','offaw','subw','neaw','sbbw','eqaw', + }, + bc = lookupify{ + 'ldax','lxi','mvix','pop','push','stax', + 'anax','xrax','orax','addncx','gtax','subnbx','ltax','addx','onax','adcx','offax','subx','neax','sbbx','eqax', + }, + de = lookupify{ + 'ldax','ldaxd','ldaxi','lxi','mvix','pop','push','stax','staxd','staxi', + 'anax','xrax','orax','addncx','gtax','subnbx','ltax','addx','onax','adcx','offax','subx','neax','sbbx','eqax', + 'anaxi','xraxi','oraxi','addncxi','gtaxi','subnbxi','ltaxi','addxi','onaxi','adcxi','offaxi','subxi','neaxi','sbbxi','eqaxi', + 'anaxd','xraxd','oraxd','addncxd','gtaxd','subnbxd','ltaxd','addxd','onaxd','adcxd','offaxd','subxd','neaxd','sbbxd','eqaxd', + }, + hl = lookupify{ + 'dcx','inx','ldax','ldaxd','ldaxi','lxi','mvix','pop','push','stax','staxd','staxi', + 'anax','xrax','orax','addncx','gtax','subnbx','ltax','addx','onax','adcx','offax','subx','neax','sbbx','eqax', + 'anaxi','xraxi','oraxi','addncxi','gtaxi','subnbxi','ltaxi','addxi','onaxi','adcxi','offaxi','subxi','neaxi','sbbxi','eqaxi', + 'anaxd','xraxd','oraxd','addncxd','gtaxd','subnbxd','ltaxd','addxd','onaxd','adcxd','offaxd','subxd','neaxd','sbbxd','eqaxd', + }, + sp = lookupify{'dcx','inx','lxi'}, + va = lookupify{'pop','push'}, + pa = lookupify{'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi'}, + pb = lookupify{'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi'}, + pc = lookupify{'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi'}, + mk = lookupify{'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi'}, +} + +local opcode_reg_reg_list = { + a = { + a = lookupify{'ana','xra','ora','addnc','gta','subnb','lta','add','adc','sub','nea','sbb','eqa','ona','offa'}, + b = lookupify{'mov','ana','xra','ora','addnc','gta','subnb','lta','add','','adc','','sub','nea','sbb','eqa','ona','offa'}, + c = lookupify{'mov','ana','xra','ora','addnc','gta','subnb','lta','add','','adc','','sub','nea','sbb','eqa','ona','offa'}, + d = lookupify{'mov','ana','xra','ora','addnc','gta','subnb','lta','add','','adc','','sub','nea','sbb','eqa','ona','offa'}, + e = lookupify{'mov','ana','xra','ora','addnc','gta','subnb','lta','add','','adc','','sub','nea','sbb','eqa','ona','offa'}, + h = lookupify{'mov','ana','xra','ora','addnc','gta','subnb','lta','add','','adc','','sub','nea','sbb','eqa','ona','offa'}, + l = lookupify{'mov','ana','xra','ora','addnc','gta','subnb','lta','add','','adc','','sub','nea','sbb','eqa','ona','offa'}, + v = lookupify{'ana','xra','ora','addnc','gta','subnb','lta','add','','adc','','sub','nea','sbb','eqa','ona','offa'}, + pa = lookupify{'mov'}, + pb = lookupify{'mov'}, + pc = lookupify{'mov'}, + mk = lookupify{'mov'}, + mb = lookupify{'mov'}, + mc = lookupify{'mov'}, + tm0 = lookupify{'mov'}, + tm1 = lookupify{'mov'}, + s = lookupify{'mov'}, + }, + v = { a = lookupify{'ana','xra','ora','addnc','gta','subnb','lta','add','adc','sub','nea','sbb','eqa'} }, + b = { a = lookupify{'mov','ana','xra','ora','addnc','gta','subnb','lta','add','adc','sub','nea','sbb','eqa'} }, + c = { a = lookupify{'mov','ana','xra','ora','addnc','gta','subnb','lta','add','adc','sub','nea','sbb','eqa'} }, + d = { a = lookupify{'mov','ana','xra','ora','addnc','gta','subnb','lta','add','adc','sub','nea','sbb','eqa'} }, + e = { a = lookupify{'mov','ana','xra','ora','addnc','gta','subnb','lta','add','adc','sub','nea','sbb','eqa'} }, + h = { a = lookupify{'mov','ana','xra','ora','addnc','gta','subnb','lta','add','adc','sub','nea','sbb','eqa'} }, + l = { a = lookupify{'mov','ana','xra','ora','addnc','gta','subnb','lta','add','adc','sub','nea','sbb','eqa'} }, + pa = { a = lookupify{'mov'} }, + pb = { a = lookupify{'mov'} }, + pc = { a = lookupify{'mov'} }, + mk = { a = lookupify{'mov'} }, + mb = { a = lookupify{'mov'} }, + mc = { a = lookupify{'mov'} }, + tm0 = { a = lookupify{'mov'} }, + tm1 = { a = lookupify{'mov'} }, + s = { a = lookupify{'mov'} }, +} +local registers_8 = lookupify {'v','a','b','c','d','e','h','l'} +local register_names = {'v','a','b','c','d','e','h','l'} + +local addressing_map = { + imp = opcode_implied, + wa = opcode_wa, + wab = opcode_wab, + imm = opcode_immediate, + rel = opcode_relative, + reg = opcode_reg, + regb = opcode_regb, + regw = opcode_regw, + reg_ind = opcode_reg_ind, + reg_ind_ex = opcode_reg_ind_ex, + timer=opcode_timer, +} + +local Scope = { + new = function(self, parent) + local s = { + Parent = parent, + Locals = { }, + Globals = { }, + Children = { }, + } + + if parent then + table.insert(parent.Children, s) + end + + return setmetatable(s, { __index = self }) + end, + + AddLocal = function(self, v) + table.insert(self.Locals, v) + end, + + AddGlobal = function(self, v) + table.insert(self.Globals, v) + end, + + CreateLocal = function(self, name) + local v + v = self:GetLocal(name) + if v then return v end + v = { } + v.Scope = self + v.Name = name + v.IsGlobal = false + self:AddLocal(v) + return v + end, + + GetLocal = function(self, name) + for k, var in pairs(self.Locals) do + if var.Name == name then return var end + end + + if self.Parent then + return self.Parent:GetLocal(name) + end + end, + + CreateGlobal = function(self, name) + local v + v = self:GetGlobal(name) + if v then return v end + v = { } + v.Scope = self + v.Name = name + v.IsGlobal = true + self:AddGlobal(v) + return v + end, + + GetGlobal = function(self, name) + for k, v in pairs(self.Globals) do + if v.Name == name then return v end + end + + if self.Parent then + return self.Parent:GetGlobal(name) + end + end, +} + +local function LexLua(src) + --token dump + local tokens = {} + + local st, err = pcall(function() + --line / char / pointer tracking + local p = 1 + local line = 1 + local char = 1 + + --get / peek functions + local function get() + local c = src:sub(p,p) + if c == '\n' then + char = 1 + line = line + 1 + else + char = char + 1 + end + p = p + 1 + return c + end + local function get_n(count) for i=1,count do get() end end + local function peek(n) + n = n or 0 + return src:sub(p+n,p+n) + end + local function peek_n(sz) return src:sub(p, p+sz-1) end + local function consume(chars) + local c = peek() + for i = 1, #chars do + if c == chars:sub(i,i) then return get() end + end + end + local function get_word(spaces) + if not spaces then spaces = Spaces end + local c + repeat get() c = peek() until not spaces[c] + local start = p + repeat get() c = peek() until not (UpperChars[c] or LowerChars[c] or Digits[c] or c == '_') + return src:sub(start, p-1) + end + local function peek_ident(i, spaces) + if not spaces then spaces = Spaces end + local c + while true do c=peek(i) if not spaces[c] then break end i=i+1 end + if not (UpperChars[c] or LowerChars[c] or c == '_') then return end + local start = p+i + repeat i=i+1 c = peek(i) until not (UpperChars[c] or LowerChars[c] or Digits[c] or c == '_') + return src:sub(start, p+i-1),i,c + end + + --shared stuff + local function generateError(err) + return error(">> :"..line..":"..char..": "..err, 0) + end + + local function tryGetLongString() + local start = p + if peek() == '[' then + local equalsCount = 0 + local depth = 1 + while peek(equalsCount+1) == '=' do + equalsCount = equalsCount + 1 + end + if peek(equalsCount+1) == '[' then + --start parsing the string. Strip the starting bit + for _ = 0, equalsCount+1 do get() end + + --get the contents + local contentStart = p + while true do + --check for eof + if peek() == '' then + generateError("Expected ']"..string.rep('=', equalsCount).."]' near .", 3) + end + + --check for the end + local foundEnd = true + if peek() == ']' then + for i = 1, equalsCount do + if peek(i) ~= '=' then foundEnd = false end + end + if peek(equalsCount+1) ~= ']' then + foundEnd = false + end + else + --[[ + if peek() == '[' then + -- is there an embedded long string? + local embedded = true + for i = 1, equalsCount do + if peek(i) ~= '=' then + embedded = false + break + end + end + if peek(equalsCount + 1) == '[' and embedded then + -- oh look, there was + depth = depth + 1 + for i = 1, (equalsCount + 2) do + get() + end + end + end + ]] + foundEnd = false + end + -- + if foundEnd then + depth = depth - 1 + if depth == 0 then + break + else + for i = 1, equalsCount + 2 do + get() + end + end + else + get() + end + end + + --get the interior string + local contentString = src:sub(contentStart, p-1) + + --found the end. Get rid of the trailing bit + for i = 0, equalsCount+1 do get() end + + --get the exterior string + local longString = src:sub(start, p-1) + + --return the stuff + return contentString, longString + else + return nil + end + else + return nil + end + end + + --main token emitting loop + while true do + --get leading whitespace. The leading whitespace will include any comments + --preceding the token. This prevents the parser needing to deal with comments + --separately. + local leading = { } + local leadingWhite = '' + local longStr = false + while true do + local c = peek() + if c == '#' and peek(1) == '!' and line == 1 then + -- #! shebang for linux scripts + get() + get() + leadingWhite = "#!" + while peek() ~= '\n' and peek() ~= '' do + leadingWhite = leadingWhite .. get() + end + local token = { + Type = 'Comment', + CommentType = 'Shebang', + Data = leadingWhite, + Line = line, + Char = char + } + token.Print = function() + return "<"..(token.Type .. string.rep(' ', 7-#token.Type)).." "..(token.Data or '').." >" + end + leadingWhite = "" + table.insert(leading, token) + end + if c == ' ' or c == '\t' then + --whitespace + --leadingWhite = leadingWhite..get() + local c2 = get() -- ignore whitespace + table.insert(leading, { Type = 'Whitespace', Line = line, Char = char, Data = c2 }) + elseif c == '\n' or c == '\r' then + local nl = get() + if leadingWhite ~= "" then + local token = { + Type = 'Comment', + CommentType = longStr and 'LongComment' or 'Comment', + Data = leadingWhite, + Line = line, + Char = char, + } + token.Print = function() + return "<"..(token.Type .. string.rep(' ', 7-#token.Type)).." "..(token.Data or '').." >" + end + table.insert(leading, token) + leadingWhite = "" + end + table.insert(leading, { Type = 'Whitespace', Line = line, Char = char, Data = nl }) + elseif c == '-' and peek(1) == '-' then + --comment + get() + get() + leadingWhite = leadingWhite .. '--' + local _, wholeText = tryGetLongString() + if wholeText then + leadingWhite = leadingWhite..wholeText + longStr = true + else + while peek() ~= '\n' and peek() ~= '' do + leadingWhite = leadingWhite..get() + end + end + else + break + end + end + if leadingWhite ~= "" then + local token = { + Type = 'Comment', + CommentType = longStr and 'LongComment' or 'Comment', + Data = leadingWhite, + Line = line, + Char = char, + } + token.Print = function() + return "<"..(token.Type .. string.rep(' ', 7-#token.Type)).." "..(token.Data or '').." >" + end + table.insert(leading, token) + end + + --get the initial char + local thisLine = line + local thisChar = char + local errorAt = ":"..line..":"..char..":> " + local c = peek() + + --symbol to emit + local toEmit = nil + + --pragma + if char == 1 and peek_n(7) == '#pragma' then + get_n(7) + local dat,opt = get_word() + local onoff = function(f, noerr) + opt = get_word() + if opt == 'on' then f(true) + elseif opt == 'off' then f(false) + elseif not noerr then generateError("invalid option for pragma " .. dat .. ", expected: [on,off]") + end + return opt + end + if dat == 'encapsulate' then + local opcode = onoff(function() end, true) + if opcode then + opcode_encapsulate[opcode] = get_word() + table.insert(Keywords_7801, opcode) + Keywords[opcode] = syntax7801_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_7801, opcode) + Keywords[opcode] = syntax7801_on + toEmit = {Type = 'Symbol', Data = ';'} + elseif dat == 'alias' then + local org,new = get_word(),get_word() + opcode_alias[new] = org + table.insert(Keywords_7801, new) + Keywords[new] = syntax7801_on + toEmit = {Type = 'Symbol', Data = ';'} + else generateError("unknown pragma: " .. dat) + end + + --branch on type + elseif c == '' then + --eof + toEmit = { Type = 'Eof' } + + elseif UpperChars[c] or LowerChars[c] or c == '_' then + --ident or keyword + local start = p + repeat + get() + c = peek() + until not (UpperChars[c] or LowerChars[c] or Digits[c] or c == '_') + local dat = src:sub(start, p-1) + if Keywords[dat] then + toEmit = {Type = 'Keyword', Data = dat} + else + toEmit = {Type = 'Ident', Data = dat} + end + + elseif Digits[c] or (peek() == '.' and Digits[peek(1)]) then + --number const + local start = p + local data_override + if c == '0' and peek(1) == 'x' then + get();get() + while HexDigits[peek()] do get() end + if consume('Pp') then + consume('+-') + while Digits[peek()] do get() end + end + elseif c == '0' and peek(1) == 'b' then + get();get() + while BinDigits[peek()] do get() end + local pstart, val_s, val = p, src:sub(start+2, p-1), 0 + for i=1,#val_s do + if val_s:sub(i,i) == '1' then val = val + (1<<(#val_s-i)) end + end + if consume('Pp') then + consume('+-') + while Digits[peek()] do get() end + end + data_override = val .. src:sub(pstart, p-1) + else + while Digits[peek()] do get() end + if consume('.') then + while Digits[peek()] do get() end + end + if consume('Ee') then + consume('+-') + while Digits[peek()] do get() end + end + end + toEmit = {Type = 'Number', Data = data_override or src:sub(start, p-1)} + + elseif c == '\'' or c == '\"' then + local start = p + --string const + local delim = get() + local contentStart = p + while true do + local c = get() + if c == '\\' then + get() --get the escape char + elseif c == delim then + break + elseif c == '' then + generateError("Unfinished string near ") + end + end + local content = src:sub(contentStart, p-2) + local constant = src:sub(start, p-1) + toEmit = {Type = 'String', Data = constant, Constant = content} + + elseif c == '[' then + local content, wholetext = tryGetLongString() + if wholetext then + toEmit = {Type = 'String', Data = wholetext, Constant = content} + else + get() + toEmit = {Type = 'Symbol', Data = '['} + end + + elseif c == '<' and peek(1) == c then + get() get() + toEmit = {Type = 'Symbol', Data = '<<'} + elseif c == '>' and peek(1) == c then + get() get() + toEmit = {Type = 'Symbol', Data = '>>'} + + elseif consume('>=<') then + toEmit = {Type = 'Symbol', Data = consume('=') and c..'=' or c} + + elseif consume('~') then + toEmit = {Type = 'Symbol', Data = consume('=') and '~=' or '~'} + + elseif consume('.') then + if consume('.') then + if consume('.') then + toEmit = {Type = 'Symbol', Data = '...'} + else + toEmit = {Type = 'Symbol', Data = '..'} + end + else + toEmit = {Type = 'Symbol', Data = '.'} + end + + elseif consume(':') then + toEmit = {Type = 'Symbol', Data = consume(':') and '::' or ':'} + elseif consume('/') then + toEmit = {Type = 'Symbol', Data = consume('/') and '//' or '/'} + + elseif syntax7801_on and consume('@') then + if consume('@') then + toEmit = {Type = 'Symbol', Data = '@@'} + else + toEmit = {Type = 'Symbol', Data = '@'} + end + + elseif syntax7801_on and consume('\\') then + toEmit = {Type = 'Symbol', Data = '\\'} + + elseif Symbols[c] then + get() + toEmit = {Type = 'Symbol', Data = c} + + else + local contents, all = tryGetLongString() + if contents then + toEmit = {Type = 'String', Data = all, Constant = contents} + else + generateError("Unexpected Symbol '"..c.."' in source.", 2) + end + end + + if toEmit[1] then + toEmit[1].LeadingWhite = leading + for k,v in ipairs(toEmit) do + v.Line = thisLine + v.Char = thisChar + v.Print = function() + return "<"..(v.Type..string.rep(' ', 7-#v.Type)).." "..(v.Data or '').." >" + end + tokens[#tokens+1] = v + end + else + --add the emitted symbol, after adding some common data + toEmit.LeadingWhite = leading -- table of leading whitespace/comments + --for k, tok in pairs(leading) do + -- tokens[#tokens + 1] = tok + --end + + toEmit.Line = thisLine + toEmit.Char = thisChar + toEmit.Print = function() + return "<"..(toEmit.Type..string.rep(' ', 7-#toEmit.Type)).." "..(toEmit.Data or '').." >" + end + tokens[#tokens+1] = toEmit + + --halt after eof has been emitted + if toEmit.Type == 'Eof' then break end + end + end + end) + if not st then + return false, err + end + + --public interface: + local tok = {} + local savedP = {} + local p = 1 + + function tok:getp() + return p + end + + function tok:setp(n) + p = n + end + + function tok:getTokenList() + return tokens + end + + --getters + function tok:Peek(n) + n = n or 0 + return tokens[math.min(#tokens, p+n)] + end + function tok:Get(tokenList) + local t = tokens[p] + p = math.min(p + 1, #tokens) + if tokenList then + table.insert(tokenList, t) + end + return t + end + function tok:Is(t) + return tok:Peek().Type == t + end + + --save / restore points in the stream + function tok:Save() + savedP[#savedP+1] = p + end + function tok:Commit() + savedP[#savedP] = nil + end + function tok:Restore() + p = savedP[#savedP] + savedP[#savedP] = nil + end + + --either return a symbol if there is one, or return true if the requested + --symbol was gotten. + function tok:ConsumeSymbol(symb, tokenList) + local t = self:Peek() + if t.Type == 'Symbol' then + if symb then + if t.Data == symb then + self:Get(tokenList) + return true + else + return nil + end + else + self:Get(tokenList) + return t + end + else + return nil + end + end + + function tok:ConsumeKeyword(kw, tokenList) + local t = self:Peek() + if t.Type == 'Keyword' and t.Data == kw then + self:Get(tokenList) + return true + else + return nil + end + end + + function tok:IsKeyword(kw) + local t = tok:Peek() + return t.Type == 'Keyword' and t.Data == kw + end + + function tok:IsSymbol(s) + local t = tok:Peek() + return t.Type == 'Symbol' and t.Data == s + end + + function tok:IsEof() + return tok:Peek().Type == 'Eof' + end + + return true, tok +end + + +local function ParseLua(src, src_name) + local st, tok + if type(src) ~= 'table' then + st, tok = LexLua(src) + else + st, tok = true, src + end + if not st then + return false, tok + end + -- + local function GenerateError(msg) + local err = (src_name or '=(string)') .. ":"..tok:Peek().Line..":"..tok:Peek().Char..": "..msg.."\n " + --find the line + local lineNum = 0 + if type(src) == 'string' then + for line in src:gmatch("[^\n]*\n?") do + if line:sub(-1,-1) == '\n' then line = line:sub(1,-2) end + lineNum = lineNum+1 + if lineNum == tok:Peek().Line then + err = err..line:gsub('\t',' ').."\n" + for i = 1, tok:Peek().Char do + local c = line:sub(i,i) + if c == '\t' then + err = err..' ' + else + err = err..' ' + end + end + err = err.."^^^^" + break + end + end + end + return err + end + -- + local VarUid = 0 + local function CreateScope(parent) + local scope = Scope:new(parent) + scope.Print = function() return "" end + return scope + end + + local ParseExpr + local ParseStatementList + local ParseSimpleExpr, + ParseSubExpr, + ParsePrimaryExpr, + ParseSuffixedExpr + + local function ParseFunctionArgsAndBody(scope, tokenList) + local funcScope = CreateScope(scope) + if not tok:ConsumeSymbol('(', tokenList) then + return false, GenerateError("'(' expected") + end + + --arg list + local argList = {} + local isVarArg = false + while not tok:ConsumeSymbol(')', tokenList) do + if tok:Is('Ident') then + local arg = funcScope:CreateLocal(tok:Get(tokenList).Data) + argList[#argList+1] = arg + if not tok:ConsumeSymbol(',', tokenList) then + if tok:ConsumeSymbol(')', tokenList) then + break + else + return false, GenerateError("')' expected") + end + end + elseif tok:ConsumeSymbol('...', tokenList) then + isVarArg = true + if not tok:ConsumeSymbol(')', tokenList) then + return false, GenerateError("'...' must be the last argument of a function") + end + break + else + return false, GenerateError("Argument name or '...' expected") + end + end + + --body + local st, body = ParseStatementList(funcScope) + if not st then return false, body end + + --end + if not tok:ConsumeKeyword('end', tokenList) then + return false, GenerateError("'end' expected after function body") + end + local nodeFunc = {} + nodeFunc.AstType = 'Function' + nodeFunc.Scope = funcScope + nodeFunc.Arguments = argList + nodeFunc.Body = body + nodeFunc.VarArg = isVarArg + nodeFunc.Tokens = tokenList + -- + return true, nodeFunc + end + + + function ParsePrimaryExpr(scope) + local tokenList = {} + + if tok:ConsumeSymbol('(', tokenList) then + local st, ex = ParseExpr(scope) + if not st then return false, ex end + if not tok:ConsumeSymbol(')', tokenList) then + return false, GenerateError("')' expected") + end + if false then + --save the information about parenthesized expressions somewhere + ex.ParenCount = (ex.ParenCount or 0) + 1 + return true, ex + else + local parensExp = {} + parensExp.AstType = 'Parentheses' + parensExp.Inner = ex + parensExp.Tokens = tokenList + return true, parensExp + end + + elseif tok:Is('Ident') then + local id = tok:Get(tokenList) + local var = scope:GetLocal(id.Data) + if not var then + var = scope:GetGlobal(id.Data) + if not var then + var = scope:CreateGlobal(id.Data) + end + end + -- + local nodePrimExp = {} + nodePrimExp.AstType = 'VarExpr' + nodePrimExp.Name = id.Data + nodePrimExp.Variable = var + nodePrimExp.Tokens = tokenList + -- + return true, nodePrimExp + + else + return false, GenerateError("Primary expression expected") + end + end + + function ParseSuffixedExpr(scope, onlyDotColon) + --base primary expression + local st, prim = ParsePrimaryExpr(scope) + if not st then return false, prim end + -- + while true do + local tokenList = {} + + if tok:IsSymbol('.') or tok:IsSymbol(':') then + local symb = tok:Get(tokenList).Data + if not tok:Is('Ident') then + return false, GenerateError("Identifier expected") + end + local id = tok:Get(tokenList) + local nodeIndex = {} + nodeIndex.AstType = 'MemberExpr' + nodeIndex.Base = prim + nodeIndex.Indexer = symb + nodeIndex.Ident = id + nodeIndex.Tokens = tokenList + -- + prim = nodeIndex + + elseif not onlyDotColon and tok:ConsumeSymbol('[', tokenList) then + local st, ex = ParseExpr(scope) + if not st then return false, ex end + if not tok:ConsumeSymbol(']', tokenList) then + return false, GenerateError("']' expected") + end + local nodeIndex = {} + nodeIndex.AstType = 'IndexExpr' + nodeIndex.Base = prim + nodeIndex.Index = ex + nodeIndex.Tokens = tokenList + -- + prim = nodeIndex + + elseif not onlyDotColon and tok:ConsumeSymbol('(', tokenList) then + local args = {} + while not tok:ConsumeSymbol(')', tokenList) do + local st, ex = ParseExpr(scope) + if not st then return false, ex end + args[#args+1] = ex + if not tok:ConsumeSymbol(',', tokenList) then + if tok:ConsumeSymbol(')', tokenList) then + break + else + return false, GenerateError("')' expected") + end + end + end + local nodeCall = {} + nodeCall.AstType = 'CallExpr' + nodeCall.Base = prim + nodeCall.Arguments = args + nodeCall.Tokens = tokenList + -- + prim = nodeCall + + elseif not onlyDotColon and tok:Is('String') then + --string call + local nodeCall = {} + nodeCall.AstType = 'StringCallExpr' + nodeCall.Base = prim + nodeCall.Arguments = { tok:Get(tokenList) } + nodeCall.Tokens = tokenList + -- + prim = nodeCall + + elseif not onlyDotColon and tok:IsSymbol('{') then + --table call + local st, ex = ParseSimpleExpr(scope) + -- FIX: ParseExpr(scope) parses the table AND and any following binary expressions. + -- We just want the table + if not st then return false, ex end + local nodeCall = {} + nodeCall.AstType = 'TableCallExpr' + nodeCall.Base = prim + nodeCall.Arguments = { ex } + nodeCall.Tokens = tokenList + -- + prim = nodeCall + + else + break + end + end + return true, prim + end + + + function ParseSimpleExpr(scope) + local tokenList = {} + + if tok:Is('Number') then + local nodeNum = {} + nodeNum.AstType = 'NumberExpr' + nodeNum.Value = tok:Get(tokenList) + nodeNum.Tokens = tokenList + return true, nodeNum + + elseif tok:Is('String') then + local nodeStr = {} + nodeStr.AstType = 'StringExpr' + nodeStr.Value = tok:Get(tokenList) + nodeStr.Tokens = tokenList + return true, nodeStr + + elseif tok:ConsumeKeyword('nil', tokenList) then + local nodeNil = {} + nodeNil.AstType = 'NilExpr' + nodeNil.Tokens = tokenList + return true, nodeNil + + elseif tok:IsKeyword('false') or tok:IsKeyword('true') then + local nodeBoolean = {} + nodeBoolean.AstType = 'BooleanExpr' + nodeBoolean.Value = (tok:Get(tokenList).Data == 'true') + nodeBoolean.Tokens = tokenList + return true, nodeBoolean + + elseif tok:ConsumeSymbol('...', tokenList) then + local nodeDots = {} + nodeDots.AstType = 'DotsExpr' + nodeDots.Tokens = tokenList + return true, nodeDots + + elseif tok:ConsumeSymbol('{', tokenList) then + local v = {} + v.AstType = 'ConstructorExpr' + v.EntryList = {} + -- + while true do + if tok:IsSymbol('[', tokenList) then + --key + tok:Get(tokenList) + local st, key = ParseExpr(scope) + if not st then + return false, GenerateError("Key expression expected") + end + if not tok:ConsumeSymbol(']', tokenList) then + return false, GenerateError("']' expected") + end + if not tok:ConsumeSymbol('=', tokenList) then + return false, GenerateError("'=' expected") + end + local st, value = ParseExpr(scope) + if not st then + return false, GenerateError("Value expression expected") + end + v.EntryList[#v.EntryList+1] = { + Type = 'Key'; + Key = key; + Value = value; + } + + elseif tok:Is('Ident') then + --value or key + local lookahead = tok:Peek(1) + if lookahead.Type == 'Symbol' and lookahead.Data == '=' then + --we are a key + local key = tok:Get(tokenList) + if not tok:ConsumeSymbol('=', tokenList) then + return false, GenerateError("'=' expected") + end + local st, value = ParseExpr(scope) + if not st then + return false, GenerateError("Value expression expected") + end + v.EntryList[#v.EntryList+1] = { + Type = 'KeyString'; + Key = key.Data; + Value = value; + } + + else + --we are a value + local st, value = ParseExpr(scope) + if not st then + return false, GenerateError("Value expected") + end + v.EntryList[#v.EntryList+1] = { + Type = 'Value'; + Value = value; + } + + end + elseif tok:ConsumeSymbol('}', tokenList) then + break + + else + --value + local st, value = ParseExpr(scope) + v.EntryList[#v.EntryList+1] = { + Type = 'Value'; + Value = value; + } + if not st then + return false, GenerateError("Value expected") + end + end + + if tok:ConsumeSymbol(';', tokenList) or tok:ConsumeSymbol(',', tokenList) then + --all is good + elseif tok:ConsumeSymbol('}', tokenList) then + break + else + return false, GenerateError("'}' or table entry expected") + end + end + v.Tokens = tokenList + return true, v + + elseif tok:ConsumeKeyword('function', tokenList) then + local st, func = ParseFunctionArgsAndBody(scope, tokenList) + if not st then return false, func end + -- + func.IsLocal = true + return true, func + + elseif tok:ConsumeSymbol('\\', tokenList) then -- short lambdas \params(expr) + local funcScope = CreateScope(scope) + + local argList = {} + if not tok:ConsumeSymbol('(', tokenList) then while true do + if not tok:Is('Ident') then return false, GenerateError("Identifier expected") end + argList[#argList+1] = funcScope:CreateLocal(tok:Get(tokenList).Data) + if tok:ConsumeSymbol('(', tokenList) then break end + if not tok:ConsumeSymbol(',', tokenList) then return false, GenerateError("'(' expected") end + end end + local st, body = ParseExpr(funcScope) + if not st then return false, body end + if not tok:ConsumeSymbol(')', tokenList) then return false, GenerateError("')' expected after lambda body") end + + local last_tok = body.Tokens[1] + local last_char,last_line = last_tok.Char,last_tok.Line + local space = { Char=last_char, Line=last_line, Data=' ', Type='Whitespace' } + local ret = { AstType='ReturnStatement', Arguments={body}, TrailingWhite=true, Tokens={ + { Char=last_char, Line=last_line, Print=function() return '' end, LeadingWhite={space}, Type='Keyword', Data='return' } + }} + local statList = { AstType='Statlist', Scope=CreateScope(funcScope), Tokens={}, Body={ret} } + + local tok_first = tokenList[1] + tok_first.Type = 'Keyword' + tok_first.Data = 'function' + tok_first.Print=function() return '' end + + local ntokl = {} + local paren_ix = 2 + math.max(0, #argList*2-1) + table.insert(ntokl, tokenList[1]) + table.insert(ntokl, tokenList[paren_ix]) + for i=2,paren_ix-1 do table.insert(ntokl, tokenList[i]) end + table.insert(ntokl, tokenList[#tokenList]) + table.insert(ntokl, { Char=last_char, Line=last_line, Type='Keyword', Data='end', Print=function() return '' end, LeadingWhite={space} }) + + local func = { AstType='Function', Scope=funcScope, Arguments=argList, Body=statList, VarArg=false, Tokens=ntokl, isLocal=true } + return true, func + + else + return ParseSuffixedExpr(scope) + end + end + + + local unops = lookupify{'-', 'not', '#', '~'} + local unopprio = 12 + local priority = { + ['^'] = {14,13}; + ['%'] = {11,11}; + ['//'] = {11,11}; + ['/'] = {11,11}; + ['*'] = {11,11}; + ['+'] = {10,10}; + ['-'] = {10,10}; + ['..'] = {9,8}; + ['>>'] = {7,7}; + ['<<'] = {7,7}; + ['&'] = {6,6}; + ['~'] = {5,5}; + ['|'] = {4,4}; + ['=='] = {3,3}; + ['<'] = {3,3}; + ['<='] = {3,3}; + ['~='] = {3,3}; + ['>'] = {3,3}; + ['>='] = {3,3}; + ['and'] = {2,2}; + ['or'] = {1,1}; + } + function ParseSubExpr(scope, level) + --base item, possibly with unop prefix + local st, exp + if unops[tok:Peek().Data] then + local tokenList = {} + local op = tok:Get(tokenList).Data + st, exp = ParseSubExpr(scope, unopprio) + if not st then return false, exp end + local nodeEx = {} + nodeEx.AstType = 'UnopExpr' + nodeEx.Rhs = exp + nodeEx.Op = op + nodeEx.OperatorPrecedence = unopprio + nodeEx.Tokens = tokenList + exp = nodeEx + else + st, exp = ParseSimpleExpr(scope) + if not st then return false, exp end + end + + --next items in chain + while true do + local prio = priority[tok:Peek().Data] + if prio and prio[1] > level then + local tokenList = {} + local op = tok:Get(tokenList).Data + local st, rhs = ParseSubExpr(scope, prio[2]) + if not st then return false, rhs end + local nodeEx = {} + nodeEx.AstType = 'BinopExpr' + nodeEx.Lhs = exp + nodeEx.Op = op + nodeEx.OperatorPrecedence = prio[1] + nodeEx.Rhs = rhs + nodeEx.Tokens = tokenList + -- + exp = nodeEx + else + break + end + end + + return true, exp + end + + + ParseExpr = function(scope) + return ParseSubExpr(scope, 0) + end + + + local pragma_map = { + encapsulate_on = function() opcode_arg_encapsulate(true) end, + encapsulate_off = function() opcode_arg_encapsulate(false) end, + syntax7801k_on = function() syntax7801k(true) end, + syntax7801k_off = function() syntax7801k(false) end, + } + local function ParseStatement(scope) + local stat = nil + local tokenList = {} + local commaTokenList = {} + local l,c = function() return tokenList[1].Line end, function() return tokenList[1].Char end + local p = function(t,n) return function() return '<' .. t .. string.rep(' ', 7-#t) .. ' ' .. n .. ' >' end end + local t = function(t,s,w) return { Type=t, Data=s, Print=p(t,s), Char=c(), Line=l(), LeadingWhite=w or {} } end + local no_encapsulation = { Function=true, NumberExpr=true, StringExpr=true } + local reg = function(e) local t=e.Tokens[1] if t then return Registers_7801[t.Data] end end + + local function emit_call(params) + local name,args,inverse_encapsulate = params.name, params.args or {}, params.inverse_encapsulate + if not params.func_white then params.func_white = tokenList[1].LeadingWhite end + local space = { Char=c(), Line=l(), Data=' ', Type='Whitespace' } + local op_var = { + AstType='VarExpr', Name=name, Variable={ IsGlobal=true, Name=name, Scope=CreateScope(scope) }, Tokens = { t('Ident', name, params.func_white) } + } + local encapsulate = params.encapsulate + if encapsulate == nil then encapsulate = opcode_arg_encapsulate_on end + if type(inverse_encapsulate) == 'table' then + -- this is a list of arguments, where each can be encapsulated + for arg_ix,arg in ipairs(args) do + if ( (encapsulate and not inverse_encapsulate[arg_ix]) or (not encapsulate and inverse_encapsulate[arg_ix]) ) and not no_encapsulation[arg.AstType] then + local inner_call_scope = CreateScope(op_var.Variable.Scope) + local inner_call_body = { + AstType='StatList', Scope=CreateScope(inner_call_scope), Tokens={}, Body={ + { AstType='ReturnStatement', Arguments={arg}, Tokens={ t('Keyword', 'return', {space}) } } + } + } + local 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}) } + } + if #arg.Tokens[1].LeadingWhite == 0 then table.insert(arg.Tokens[1].LeadingWhite, space) end + args[arg_ix] = inner_call + end + 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,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}) } } + } + } + 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}) } + } + 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 = { + AstType = 'CallExpr', Base = op_var, Arguments = args, Tokens = { + t('Symbol', '(', params.paren_open_white), t('Symbol', ')', params.paren_close_white) + } + } + do + local j=#commaTokenList + for i=2,#args do + assert(j > 0) + table.insert(exp_call.Tokens, i, commaTokenList[j]) + j = j - 1 + end + end + return { AstType = 'CallStatement', Expression = exp_call, Tokens = {} } + end + + local function as_string_expr(expr, s) + local ss = '"'..s..'"' + local lw = expr.Tokens and #expr.Tokens > 0 and expr.Tokens[1].LeadingWhite or {} + local p = function() return '' end + local v = { LeadingWhite=lw, Type='String', Data=ss, Constant=s, Char=c(), Line=l(), Print=p } + return { AstType = 'StringExpr', Value = v, Tokens = {v} } + end + + -- parser pragmas + do + ::search_pragma:: + for k,v in pairs(pragma_map) do + if tok:ConsumeKeyword(k) then v() goto search_pragma end + end + end + + -- label declarations + if not stat then + if tok:ConsumeSymbol('@@', tokenList) then + if not tok:Is('Ident') then return false, GenerateError("Identifier expected") end + local label_name = tok:Get(tokenList) + label_name = as_string_expr(label_name, label_name.Data) + stat = emit_call{name = 'section', args = {label_name}, encapsulate=false} + elseif tok:ConsumeSymbol('@', tokenList) then + if not tok:Is('Ident') then return false, GenerateError("Identifier expected") end + local label_name = tok:Get(tokenList) + label_name = as_string_expr(label_name, label_name.Data) + stat = emit_call{name = 'label', args = {label_name}, encapsulate=false} + end end + + -- new statements + if not stat then + local pagestat = function(fpage) + local st, nodeBlock = ParseStatementList(scope) + if not st then return false, nodeBlock end + if not tok:ConsumeKeyword('end', tokenList) then + return false, GenerateError("'end' expected") + end + + local nodeDoStat = {} + nodeDoStat.AstType = 'DoStatement' + nodeDoStat.Body = nodeBlock + nodeDoStat.Tokens = tokenList + stat = nodeDoStat + + tokenList[1].Data = 'do' + + local space = {{ Char=c(), Line=l(), Data=' ', Type='Whitespace' }} + local opencall,closecall = emit_call{name=fpage,func_white=space},emit_call{name='endpage',func_white=space} + table.insert(nodeBlock.Body, 1, opencall) + table.insert(nodeBlock.Body, closecall) + end + if tok:ConsumeKeyword('samepage', tokenList) then pagestat('samepage') + elseif tok:ConsumeKeyword('crosspage', tokenList) then pagestat('crosspage') + end + end + + -- declare data + if not stat then + if tok:ConsumeKeyword('dc', tokenList) then + if not tok:ConsumeSymbol('.', tokenList) then return false, GenerateError("'.' expected") end + local suffix_list = { b='byte', w='word', l='long' } + local func = suffix_list[tok:Get(tokenList).Data] + if not func then return false, GenerateError("'b', 'w' or 'l' expected") end + local inverse_encapsulate={} + inverse_encapsulate[1] = tok:ConsumeSymbol('!', tokenList) + local st, expr = ParseExpr(scope) + if not st then return false, expr end + if inverse_encapsulate[1] then + local d = expr.Tokens[1].LeadingWhite + for _,v in ipairs(tokenList[#tokenList].LeadingWhite) do table.insert(d, v) end + end + local exprs = { expr } + while tok:ConsumeSymbol(',', tokenList) do + commaTokenList[#commaTokenList+1] = tokenList[#tokenList] + inverse_encapsulate[#exprs+1] = tok:ConsumeSymbol('!', tokenList) + local st, expr = ParseExpr(scope) + if not st then return false, expr end + if inverse_encapsulate[#exprs+1] then + local d = expr.Tokens[1].LeadingWhite + for _,v in ipairs(tokenList[#tokenList].LeadingWhite) do table.insert(d, v) end + end + exprs[#exprs+1] = expr + end + stat = emit_call{name=func, args=exprs, inverse_encapsulate=inverse_encapsulate} + end end + + -- 7801 opcodes + if not stat then + local mod_st, mod_expr, inverse_encapsulate + for _,op in pairs(Keywords_7801) do + if tok:ConsumeKeyword(op, tokenList) then + if 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) + end + stat = emit_call{name=op, args={expr}, encapsulate=false} break + end + if opcode_alias[op] then op = opcode_alias[op] end + 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 + end + if opcode_immediate[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 + if tok:ConsumeSymbol(',', tokenList) then + commaTokenList[1] = tokenList[#tokenList] + mod_st, mod_expr = ParseExpr(scope) + if not mod_st then return false, mod_expr end + end + stat = emit_call{name=op .. 'imm', args={expr, mod_expr}, inverse_encapsulate=inverse_encapsulate, paren_open_white=paren_open_whites} break + end + if opcode_ind[op] then + if not tok:ConsumeSymbol('(', tokenList) then + return false, GenerateError("Opcode " .. op .. " doesn't support this addressing mode") + end + inverse_encapsulate = tok:ConsumeSymbol('!', tokenList) + local st, expr = ParseExpr(scope) if not st then return false, expr end + local paren_open_whites,paren_close_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 + if not tok:ConsumeSymbol(')', tokenList) then return false, expr end + stat = emit_call{name=op, args={expr}, inverse_encapsulate=inverse_encapsulate, paren_open_white=paren_open_whites} break + end + if opcode_mov[op] then + tok:Save() + local r0_name, r1_name = tok:Get(tokenList).Data, '' + if Registers_7801[r0_name] then + tok:Commit() + if not tok:ConsumeSymbol(',', tokenList) then + return false, GenerateError("Opcode " .. op .. " doesn't support this addressing mode") + end + tok:Save() + r1_name = tok:Get(tokenList).Data + if Registers_7801[r1_name] then + tok:Commit() + if not (opcode_reg_reg_list[r0_name] and opcode_reg_reg_list[r0_name][r1_name] and opcode_reg_reg_list[r0_name][r1_name][op]) then + return false, GenerateError("Opcode " .. op .. " doesn't support this addressing mode") + end + stat = emit_call{name=op .. r0_name .. r1_name} break + else + r1_name = '' + end + else + r0_name = '' + end + tok:Restore() + + if not tok:ConsumeSymbol('(', tokenList) then + return false, GenerateError("Opcode " .. op .. " doesn't support this addressing mode") + end + inverse_encapsulate = tok:ConsumeSymbol('!', tokenList) + local st, expr = ParseExpr(scope) if not st then return false, expr end + local paren_open_whites,paren_close_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 + + if not tok:ConsumeSymbol(')', tokenList) then return false, expr end + + for _,v in ipairs(tokenList[#tokenList-1].LeadingWhite) do table.insert(paren_close_whites, v) end + for _,v in ipairs(tokenList[#tokenList].LeadingWhite) do table.insert(paren_close_whites, v) end + + if r0_name == '' then + if not tok:ConsumeSymbol(',', tokenList) then return false, expr end + r1_name = tok:Get(tokenList).Data + if not registers_8[r1_name] then + return false, GenerateError(r1_name .. " is not a valid register") + end + end + stat = emit_call{name=op .. r0_name .. "ind" .. r1_name, args={expr}, inverse_encapsulate=inverse_encapsulates, paren_open_white=paren_open_whites, paren_close_white=paren_close_whites} break + end + if (opcode_wa[op] or opcode_wab[op] or opcode_reg_ind[op] or opcode_reg_ind_ex[op]) and tok:ConsumeSymbol('(', tokenList) then + local paren_open_whites,paren_close_whites = {},{} + for _,v in ipairs(tokenList[#tokenList].LeadingWhite) do table.insert(paren_open_whites, v) end + + local register_name = tok:Get(tokenList).Data + if (not Registers_7801[register_name]) then + return false, GenerateError(register_name .. " is not a valid register") + end + + if not (opcode_reg_list[register_name] and opcode_reg_list[register_name][op]) then + return false, GenerateError("Opcode " .. op .. " doesn't support this addressing mode") + end + + if (opcode_wa[op] or opcode_wab[op]) and tok:ConsumeSymbol(',', tokenList) then + if not (register_name == 'v') + or not (opcode_wa[op] or opcode_wab[op]) then + return false, GenerateError("Opcode " .. op .. " doesn't support this addressing mode") + end + inverse_encapsulate = tok:ConsumeSymbol('!', tokenList) + local st, expr = ParseExpr(scope) if not st then return false, expr end + if not tok:ConsumeSymbol(')', tokenList) then + return false, GenerateError("Unexpected character") + end + if opcode_wa[op] then + for _,v in ipairs(tokenList[#tokenList-1].LeadingWhite) do table.insert(paren_close_whites, v) end + for _,v in ipairs(tokenList[#tokenList].LeadingWhite) do table.insert(paren_close_whites, v) end + stat = emit_call{name=op .. "wa", args={expr, mod_expr}, inverse_encapsulate=inverse_encapsulate, paren_open_white=paren_open_whites} break + end + if not tok:ConsumeSymbol(',', tokenList) then + return false, GenerateError("Unexpected character") + end + commaTokenList[1] = tokenList[#tokenList] + local inverse_encapsulates = { inverse_encapsulate } + local exprs = { expr } + inverse_encapsulate = tok:ConsumeSymbol('!', tokenList) + inverse_encapsulates[#inverse_encapsulates+1] = inverse_encapsulate + if inverse_encapsulate then for _,v in ipairs(tokenList[#tokenList-1].LeadingWhite) do table.insert(paren_close_whites, v) end end + for _,v in ipairs(tokenList[#tokenList].LeadingWhite) do table.insert(paren_close_whites, v) end + st, expr = ParseExpr(scope) if not st then return false, expr end + exprs[#exprs+1] = expr + stat = emit_call{name=op .. "waxx", args=exprs, inverse_encapsulate=inverse_encapsulates, paren_open_white=paren_open_whites, paren_close_white=paren_close_whites} break + end + if (opcode_reg_ind[op] or opcode_reg_ind_ex[op]) and tok:ConsumeSymbol(')', tokenList) then + if opcode_reg_ind[op] then + for _,v in ipairs(tokenList[#tokenList-1].LeadingWhite) do table.insert(paren_close_whites, v) end + for _,v in ipairs(tokenList[#tokenList].LeadingWhite) do table.insert(paren_close_whites, v) end + stat = emit_call{name=op .. register_name, paren_open_white=paren_open_whites, paren_close_white=paren_close_whites} break + end + if opcode_reg_ind_ex[op] and tok:ConsumeSymbol(',', tokenList) then + inverse_encapsulate = tok:ConsumeSymbol('!', tokenList) + local st, expr = ParseExpr(scope) if not st then return false, expr end + if inverse_encapsulate then for _,v in ipairs(tokenList[#tokenList-1].LeadingWhite) do table.insert(paren_close_whites, v) end end + for _,v in ipairs(tokenList[#tokenList].LeadingWhite) do table.insert(paren_close_whites, v) end + stat = emit_call{name=op .. register_name, args={expr}, inverse_encapsulate=inverse_encapsulate, paren_open_white=paren_open_whites, paren_close_white=paren_close_whites} break + end + return false, GenerateError("Opcode " .. op .. " doesn't support this addressing mode") + end + end + if opcode_reg_reg[op] then + local r0_name = tok:Get(tokenList).Data + if not Registers_7801[r0_name] then + return false, GenerateError(r0_name .. " is not a valid register") + end + if not tok:ConsumeSymbol(',', tokenList) then + return false, GenerateError("Opcode " .. op .. " doesn't support this addressing mode") + end + local r1_name = tok:Get(tokenList).Data + if not Registers_7801[r1_name] then + return false, GenerateError(r0_name .. " is not a valid register") + end + if not (opcode_reg_reg_list[r0_name] and opcode_reg_reg_list[r0_name][r1_name] and opcode_reg_reg_list[r0_name][r1_name][op]) then + return false, GenerateError("Opcode " .. op .. " doesn't support this addressing mode") + end + stat = emit_call{name=op .. r0_name .. r1_name} break + end + if opcode_reg[op] or opcode_regb[op] or opcode_regw[op] then + local register_name = tok:Get(tokenList).Data + local call_args = {name=op..register_name} + if not Registers_7801[register_name] then + return false, GenerateError(register_name .. " is not a valid register") + end + if (not opcode_reg_list[register_name]) and (not opcode_reg_list[register_name][op]) then + return false, GenerateError("Opcode " .. op .. " doesn't support this addressing mode") + end + if opcode_regw[op] or opcode_regb[op] then + if not tok:ConsumeSymbol(',', tokenList) then + return false, GenerateError("Opcode " .. op .. " doesn't support this addressing mode") + end + local 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 + if tok:ConsumeSymbol(',', tokenList) then + commaTokenList[1] = tokenList[#tokenList] + mod_st, mod_expr = ParseExpr(scope) + if not mod_st then + return false, mod_expr + end + end + call_args.args={expr, mod_expr} + call_args.inverse_encapsulate=inverse_encapsulate + call_args.paren_open_white=paren_open_whites + end + stat = emit_call(call_args) break + end + if opcode_timer[op] then + local timer_name = tok:Get(tokenList).Data + if not Interrupts_7801[timer_name] then + return false, GenerateError(timer_name .. " is not a valid interrupt name") + end + stat = emit_call{name=op .. timer_name} break + end + if opcode_implied[op] then stat = emit_call{name=op .. 'imp'} break end + error("internal error: unable to find addressing of valid opcode " .. op) -- should not happen + end + end end + + if stat then -- nothing + elseif tok:ConsumeKeyword('if', tokenList) then + --setup + local nodeIfStat = {} + nodeIfStat.AstType = 'IfStatement' + nodeIfStat.Clauses = {} + + --clauses + repeat + local st, nodeCond = ParseExpr(scope) + if not st then return false, nodeCond end + if not tok:ConsumeKeyword('then', tokenList) then + return false, GenerateError("'then' expected") + end + local st, nodeBody = ParseStatementList(scope) + if not st then return false, nodeBody end + nodeIfStat.Clauses[#nodeIfStat.Clauses+1] = { + Condition = nodeCond; + Body = nodeBody; + } + until not tok:ConsumeKeyword('elseif', tokenList) + + --else clause + if tok:ConsumeKeyword('else', tokenList) then + local st, nodeBody = ParseStatementList(scope) + if not st then return false, nodeBody end + nodeIfStat.Clauses[#nodeIfStat.Clauses+1] = { + Body = nodeBody; + } + end + + --end + if not tok:ConsumeKeyword('end', tokenList) then + return false, GenerateError("'end' expected") + end + + nodeIfStat.Tokens = tokenList + stat = nodeIfStat + + elseif tok:ConsumeKeyword('while', tokenList) then + --setup + local nodeWhileStat = {} + nodeWhileStat.AstType = 'WhileStatement' + + --condition + local st, nodeCond = ParseExpr(scope) + if not st then return false, nodeCond end + + --do + if not tok:ConsumeKeyword('do', tokenList) then + return false, GenerateError("'do' expected") + end + + --body + local st, nodeBody = ParseStatementList(scope) + if not st then return false, nodeBody end + + --end + if not tok:ConsumeKeyword('end', tokenList) then + return false, GenerateError("'end' expected") + end + + --return + nodeWhileStat.Condition = nodeCond + nodeWhileStat.Body = nodeBody + nodeWhileStat.Tokens = tokenList + stat = nodeWhileStat + + elseif tok:ConsumeKeyword('do', tokenList) then + --do block + local st, nodeBlock = ParseStatementList(scope) + if not st then return false, nodeBlock end + if not tok:ConsumeKeyword('end', tokenList) then + return false, GenerateError("'end' expected") + end + + local nodeDoStat = {} + nodeDoStat.AstType = 'DoStatement' + nodeDoStat.Body = nodeBlock + nodeDoStat.Tokens = tokenList + stat = nodeDoStat + + elseif tok:ConsumeKeyword('for', tokenList) then + --for block + if not tok:Is('Ident') then + return false, GenerateError("Identifier expected") + end + local baseVarName = tok:Get(tokenList) + if tok:ConsumeSymbol('=', tokenList) then + --numeric for + local forScope = CreateScope(scope) + local forVar = forScope:CreateLocal(baseVarName.Data) + -- + local st, startEx = ParseExpr(scope) + if not st then return false, startEx end + if not tok:ConsumeSymbol(',', tokenList) then + return false, GenerateError("',' expected") + end + local st, endEx = ParseExpr(scope) + if not st then return false, endEx end + local st, stepEx; + if tok:ConsumeSymbol(',', tokenList) then + st, stepEx = ParseExpr(scope) + if not st then return false, stepEx end + end + if not tok:ConsumeKeyword('do', tokenList) then + return false, GenerateError("'do' expected") + end + -- + local st, body = ParseStatementList(forScope) + if not st then return false, body end + if not tok:ConsumeKeyword('end', tokenList) then + return false, GenerateError("'end' expected") + end + -- + local nodeFor = {} + nodeFor.AstType = 'NumericForStatement' + nodeFor.Scope = forScope + nodeFor.Variable = forVar + nodeFor.Start = startEx + nodeFor.End = endEx + nodeFor.Step = stepEx + nodeFor.Body = body + nodeFor.Tokens = tokenList + stat = nodeFor + else + --generic for + local forScope = CreateScope(scope) + -- + local varList = { forScope:CreateLocal(baseVarName.Data) } + while tok:ConsumeSymbol(',', tokenList) do + if not tok:Is('Ident') then + return false, GenerateError("for variable expected.") + end + varList[#varList+1] = forScope:CreateLocal(tok:Get(tokenList).Data) + end + if not tok:ConsumeKeyword('in', tokenList) then + return false, GenerateError("'in' expected") + end + local generators = {} + local st, firstGenerator = ParseExpr(scope) + if not st then return false, firstGenerator end + generators[#generators+1] = firstGenerator + while tok:ConsumeSymbol(',', tokenList) do + local st, gen = ParseExpr(scope) + if not st then return false, gen end + generators[#generators+1] = gen + end + if not tok:ConsumeKeyword('do', tokenList) then + return false, GenerateError("'do' expected") + end + local st, body = ParseStatementList(forScope) + if not st then return false, body end + if not tok:ConsumeKeyword('end', tokenList) then + return false, GenerateError("'end' expected") + end + -- + local nodeFor = {} + nodeFor.AstType = 'GenericForStatement' + nodeFor.Scope = forScope + nodeFor.VariableList = varList + nodeFor.Generators = generators + nodeFor.Body = body + nodeFor.Tokens = tokenList + stat = nodeFor + end + + elseif tok:ConsumeKeyword('repeat', tokenList) then + local st, body = ParseStatementList(scope) + if not st then return false, body end + -- + if not tok:ConsumeKeyword('until', tokenList) then + return false, GenerateError("'until' expected") + end + -- FIX: Used to parse in parent scope + -- Now parses in repeat scope + local st, cond = ParseExpr(body.Scope) + if not st then return false, cond end + -- + local nodeRepeat = {} + nodeRepeat.AstType = 'RepeatStatement' + nodeRepeat.Condition = cond + nodeRepeat.Body = body + nodeRepeat.Tokens = tokenList + stat = nodeRepeat + + elseif tok:ConsumeKeyword('function', tokenList) then + if not tok:Is('Ident') then + return false, GenerateError("function name expected") + end + local st, name = ParseSuffixedExpr(scope, true) --true => only dots and colons + if not st then return false, name end + -- + local st, func = ParseFunctionArgsAndBody(scope, tokenList) + if not st then return false, func end + -- + func.IsLocal = false + func.Name = name + stat = func + + elseif tok:ConsumeKeyword('local', tokenList) then + if tok:Is('Ident') then + local varList = { tok:Get(tokenList).Data } + while tok:ConsumeSymbol(',', tokenList) do + if not tok:Is('Ident') then + return false, GenerateError("local var name expected") + end + varList[#varList+1] = tok:Get(tokenList).Data + end + + local initList = {} + if tok:ConsumeSymbol('=', tokenList) then + repeat + local st, ex = ParseExpr(scope) + if not st then return false, ex end + initList[#initList+1] = ex + until not tok:ConsumeSymbol(',', tokenList) + end + + --now patch var list + --we can't do this before getting the init list, because the init list does not + --have the locals themselves in scope. + for i, v in pairs(varList) do + varList[i] = scope:CreateLocal(v) + end + + local nodeLocal = {} + nodeLocal.AstType = 'LocalStatement' + nodeLocal.LocalList = varList + nodeLocal.InitList = initList + nodeLocal.Tokens = tokenList + -- + stat = nodeLocal + + elseif tok:ConsumeKeyword('function', tokenList) then + if not tok:Is('Ident') then + return false, GenerateError("function name expected") + end + local name = tok:Get(tokenList).Data + local localVar = scope:CreateLocal(name) + -- + local st, func = ParseFunctionArgsAndBody(scope, tokenList) + if not st then return false, func end + -- + func.Name = localVar + func.IsLocal = true + stat = func + + else + return false, GenerateError("local var or function def expected") + end + + elseif tok:ConsumeSymbol('::', tokenList) then + if not tok:Is('Ident') then + return false, GenerateError('Label name expected') + end + local label = tok:Get(tokenList).Data + if not tok:ConsumeSymbol('::', tokenList) then + return false, GenerateError("'::' expected") + end + local nodeLabel = {} + nodeLabel.AstType = 'LabelStatement' + nodeLabel.Label = label + nodeLabel.Tokens = tokenList + stat = nodeLabel + + elseif tok:ConsumeKeyword('return', tokenList) then + local exList = {} + if not tok:IsKeyword('end') then + local st, firstEx = ParseExpr(scope) + if st then + exList[1] = firstEx + while tok:ConsumeSymbol(',', tokenList) do + local st, ex = ParseExpr(scope) + if not st then return false, ex end + exList[#exList+1] = ex + end + end + end + + local nodeReturn = {} + nodeReturn.AstType = 'ReturnStatement' + nodeReturn.Arguments = exList + nodeReturn.Tokens = tokenList + stat = nodeReturn + + elseif tok:ConsumeKeyword('break', tokenList) then + local nodeBreak = {} + nodeBreak.AstType = 'BreakStatement' + nodeBreak.Tokens = tokenList + stat = nodeBreak + + elseif tok:ConsumeKeyword('goto', tokenList) then + if not tok:Is('Ident') then + return false, GenerateError("Label expected") + end + local label = tok:Get(tokenList).Data + local nodeGoto = {} + nodeGoto.AstType = 'GotoStatement' + nodeGoto.Label = label + nodeGoto.Tokens = tokenList + stat = nodeGoto + + elseif tok:IsSymbol(';') then + stat = { AstType='SemicolonStatement', Tokens={} } + + else + + --statementParseExpr + local st, suffixed = ParseSuffixedExpr(scope) + if not st then return false, suffixed end + + --assignment or call? + if tok:IsSymbol(',') or tok:IsSymbol('=') then + --check that it was not parenthesized, making it not an lvalue + if (suffixed.ParenCount or 0) > 0 then + return false, GenerateError("Can not assign to parenthesized expression, is not an lvalue") + end + + --more processing needed + local lhs = { suffixed } + while tok:ConsumeSymbol(',', tokenList) do + local st, lhsPart = ParseSuffixedExpr(scope) + if not st then return false, lhsPart end + lhs[#lhs+1] = lhsPart + end + + --equals + if not tok:ConsumeSymbol('=', tokenList) then + return false, GenerateError("'=' expected") + end + + --rhs + local rhs = {} + local st, firstRhs = ParseExpr(scope) + if not st then return false, firstRhs end + rhs[1] = firstRhs + while tok:ConsumeSymbol(',', tokenList) do + local st, rhsPart = ParseExpr(scope) + if not st then return false, rhsPart end + rhs[#rhs+1] = rhsPart + end + + --done + local nodeAssign = {} + nodeAssign.AstType = 'AssignmentStatement' + nodeAssign.Lhs = lhs + nodeAssign.Rhs = rhs + nodeAssign.Tokens = tokenList + stat = nodeAssign + + elseif suffixed.AstType == 'CallExpr' or + suffixed.AstType == 'TableCallExpr' or + suffixed.AstType == 'StringCallExpr' + then + --it's a call statement + local nodeCall = {} + nodeCall.AstType = 'CallStatement' + nodeCall.Expression = suffixed + nodeCall.Tokens = tokenList + stat = nodeCall + else + return false, GenerateError("Assignment statement expected") + end + end + + if tok:IsSymbol(';') then + stat.Semicolon = tok:Get( stat.Tokens ) + end + return true, stat + end + + + local statListCloseKeywords = lookupify{'end', 'else', 'elseif', 'until'} + + ParseStatementList = function(scope) + local nodeStatlist = {} + nodeStatlist.Scope = CreateScope(scope) + nodeStatlist.AstType = 'Statlist' + nodeStatlist.Body = { } + nodeStatlist.Tokens = { } + -- + --local stats = {} + -- + while not statListCloseKeywords[tok:Peek().Data] and not tok:IsEof() do + local st, nodeStatement = ParseStatement(nodeStatlist.Scope) + if not st then return false, nodeStatement end + --stats[#stats+1] = nodeStatement + nodeStatlist.Body[#nodeStatlist.Body + 1] = nodeStatement + end + + if tok:IsEof() then + local nodeEof = {} + nodeEof.AstType = 'Eof' + nodeEof.Tokens = { tok:Get() } + nodeStatlist.Body[#nodeStatlist.Body + 1] = nodeEof + end + + -- + --nodeStatlist.Body = stats + return true, nodeStatlist + end + + + local function mainfunc() + local topScope = CreateScope() + return ParseStatementList(topScope) + end + + local st, main = mainfunc() + return st, main +end + + +local function Format7801(ast) + local function splitLines(str) + if str:match("\n") then + local lines = {} + for line in str:gmatch("[^\n]*") do + table.insert(lines, line) + end + assert(#lines > 0) + return lines + else + return { str } + end + end + + local formatStatlist, formatExpr + local out = { + rope = {}, -- List of strings + line = 1, + char = 1, + + appendStr = function(self, str) + table.insert(self.rope, str) + + local lines = splitLines(str) + if #lines == 1 then + self.char = self.char + #str + else + self.line = self.line + #lines - 1 + local lastLine = lines[#lines] + self.char = #lastLine + end + end, + + appendToken = function(self, token) + self:appendWhite(token) + self:appendStr(token.Data) + end, + + appendTokens = function(self, tokens) + for _,token in ipairs(tokens) do + self:appendToken( token ) + end + end, + + appendWhite = function(self, token) + if token.LeadingWhite then + self:appendTokens( token.LeadingWhite ) + end + end + } + formatExpr = function(expr) + local tok_it = 1 + local function appendNextToken(str) + local tok = expr.Tokens[tok_it]; + if str and tok.Data ~= str then + error("Expected token '" .. str .. "'.") + end + out:appendToken( tok ) + tok_it = tok_it + 1 + end + local function appendToken(token) + out:appendToken( token ) + tok_it = tok_it + 1 + end + local function appendWhite() + local tok = expr.Tokens[tok_it]; + if not tok then error("Missing token") end + out:appendWhite( tok ) + tok_it = tok_it + 1 + end + local function appendStr(str) + appendWhite() + out:appendStr(str) + end + local function peek() + if tok_it < #expr.Tokens then + return expr.Tokens[tok_it].Data + end + end + local function appendComma(mandatory, seperators) + seperators = seperators or { "," } + seperators = lookupify( seperators ) + if not mandatory and not seperators[peek()] then + return + end + assert(seperators[peek()], "Missing comma or semicolon") + appendNextToken() + end + + if expr.AstType == 'VarExpr' then + if expr.Variable then + appendStr( expr.Variable.Name ) + else + appendStr( expr.Name ) + end + + elseif expr.AstType == 'NumberExpr' then + appendToken( expr.Value ) + + elseif expr.AstType == 'StringExpr' then + appendToken( expr.Value ) + + elseif expr.AstType == 'BooleanExpr' then + appendNextToken( expr.Value and "true" or "false" ) + + elseif expr.AstType == 'NilExpr' then + appendNextToken( "nil" ) + + elseif expr.AstType == 'BinopExpr' then + formatExpr(expr.Lhs) + appendStr( expr.Op ) + formatExpr(expr.Rhs) + + elseif expr.AstType == 'UnopExpr' then + appendStr( expr.Op ) + formatExpr(expr.Rhs) + + elseif expr.AstType == 'DotsExpr' then + appendNextToken( "..." ) + + elseif expr.AstType == 'CallExpr' then + formatExpr(expr.Base) + appendNextToken( "(" ) + for i,arg in ipairs( expr.Arguments ) do + formatExpr(arg) + appendComma( i ~= #expr.Arguments ) + end + appendNextToken( ")" ) + + elseif expr.AstType == 'TableCallExpr' then + formatExpr( expr.Base ) + formatExpr( expr.Arguments[1] ) + + elseif expr.AstType == 'StringCallExpr' then + formatExpr(expr.Base) + appendToken( expr.Arguments[1] ) + + elseif expr.AstType == 'IndexExpr' then + formatExpr(expr.Base) + appendNextToken( "[" ) + formatExpr(expr.Index) + appendNextToken( "]" ) + + elseif expr.AstType == 'MemberExpr' then + formatExpr(expr.Base) + appendNextToken() -- . or : + appendToken(expr.Ident) + + elseif expr.AstType == 'Function' then + -- anonymous function + appendNextToken( "function" ) + appendNextToken( "(" ) + if #expr.Arguments > 0 then + for i = 1, #expr.Arguments do + appendStr( expr.Arguments[i].Name ) + if i ~= #expr.Arguments then + appendNextToken(",") + elseif expr.VarArg then + appendNextToken(",") + appendNextToken("...") + end + end + elseif expr.VarArg then + appendNextToken("...") + end + appendNextToken(")") + formatStatlist(expr.Body) + appendNextToken("end") + + elseif expr.AstType == 'ConstructorExpr' then + appendNextToken( "{" ) + for i = 1, #expr.EntryList do + local entry = expr.EntryList[i] + if entry.Type == 'Key' then + appendNextToken( "[" ) + formatExpr(entry.Key) + appendNextToken( "]" ) + appendNextToken( "=" ) + formatExpr(entry.Value) + elseif entry.Type == 'Value' then + formatExpr(entry.Value) + elseif entry.Type == 'KeyString' then + appendStr(entry.Key) + appendNextToken( "=" ) + formatExpr(entry.Value) + end + appendComma( i ~= #expr.EntryList, { ",", ";" } ) + end + appendNextToken( "}" ) + + elseif expr.AstType == 'Parentheses' then + appendNextToken( "(" ) + formatExpr(expr.Inner) + appendNextToken( ")" ) + + else + error(string.format("Unknown AST Type: %s\n", tostring(statement.AstType))) + end + + assert(tok_it == #expr.Tokens + 1) + end + + + local formatStatement = function(statement) + local tok_it = 1 + local function appendNextToken(str) + local tok = statement.Tokens[tok_it]; + assert(tok, string.format("Not enough tokens for %q. First token at %i:%i", + str, statement.Tokens[1].Line, statement.Tokens[1].Char)) + assert(tok.Data == str, + string.format('Expected token %q, got %q', str, tok.Data)) + out:appendToken( tok ) + tok_it = tok_it + 1 + end + local function appendToken(token) + out:appendToken( str ) + tok_it = tok_it + 1 + end + local function appendWhite() + local tok = statement.Tokens[tok_it]; + out:appendWhite( tok ) + tok_it = tok_it + 1 + end + local function appendStr(str) + appendWhite() + out:appendStr(str) + end + local function appendComma(mandatory) + if mandatory + or (tok_it < #statement.Tokens and statement.Tokens[tok_it].Data == ",") then + appendNextToken( "," ) + end + end + + if statement.AstType == 'AssignmentStatement' then + for i,v in ipairs(statement.Lhs) do + formatExpr(v) + appendComma( i ~= #statement.Lhs ) + end + if #statement.Rhs > 0 then + appendNextToken( "=" ) + for i,v in ipairs(statement.Rhs) do + formatExpr(v) + appendComma( i ~= #statement.Rhs ) + end + end + + elseif statement.AstType == 'CallStatement' then + formatExpr(statement.Expression) + + elseif statement.AstType == 'LocalStatement' then + appendNextToken( "local" ) + for i = 1, #statement.LocalList do + appendStr( statement.LocalList[i].Name ) + appendComma( i ~= #statement.LocalList ) + end + if #statement.InitList > 0 then + appendNextToken( "=" ) + for i = 1, #statement.InitList do + formatExpr(statement.InitList[i]) + appendComma( i ~= #statement.InitList ) + end + end + + elseif statement.AstType == 'IfStatement' then + appendNextToken( "if" ) + formatExpr( statement.Clauses[1].Condition ) + appendNextToken( "then" ) + formatStatlist( statement.Clauses[1].Body ) + for i = 2, #statement.Clauses do + local st = statement.Clauses[i] + if st.Condition then + appendNextToken( "elseif" ) + formatExpr(st.Condition) + appendNextToken( "then" ) + else + appendNextToken( "else" ) + end + formatStatlist(st.Body) + end + appendNextToken( "end" ) + + elseif statement.AstType == 'WhileStatement' then + appendNextToken( "while" ) + formatExpr(statement.Condition) + appendNextToken( "do" ) + formatStatlist(statement.Body) + appendNextToken( "end" ) + + elseif statement.AstType == 'DoStatement' then + appendNextToken( "do" ) + formatStatlist(statement.Body) + appendNextToken( "end" ) + + elseif statement.AstType == 'ReturnStatement' then + appendNextToken( "return" ) + if statement.TrailingWhite then out:appendStr(' ') end + for i = 1, #statement.Arguments do + formatExpr(statement.Arguments[i]) + appendComma( i ~= #statement.Arguments ) + end + + elseif statement.AstType == 'BreakStatement' then + appendNextToken( "break" ) + + elseif statement.AstType == 'RepeatStatement' then + appendNextToken( "repeat" ) + formatStatlist(statement.Body) + appendNextToken( "until" ) + formatExpr(statement.Condition) + + elseif statement.AstType == 'Function' then + if statement.IsLocal then + appendNextToken( "local" ) + end + appendNextToken( "function" ) + + if statement.IsLocal then + appendStr(statement.Name.Name) + else + formatExpr(statement.Name) + end + + appendNextToken( "(" ) + if #statement.Arguments > 0 then + for i = 1, #statement.Arguments do + appendStr( statement.Arguments[i].Name ) + appendComma( i ~= #statement.Arguments or statement.VarArg ) + if i == #statement.Arguments and statement.VarArg then + appendNextToken( "..." ) + end + end + elseif statement.VarArg then + appendNextToken( "..." ) + end + appendNextToken( ")" ) + + formatStatlist(statement.Body) + appendNextToken( "end" ) + + elseif statement.AstType == 'GenericForStatement' then + appendNextToken( "for" ) + for i = 1, #statement.VariableList do + appendStr( statement.VariableList[i].Name ) + appendComma( i ~= #statement.VariableList ) + end + appendNextToken( "in" ) + for i = 1, #statement.Generators do + formatExpr(statement.Generators[i]) + appendComma( i ~= #statement.Generators ) + end + appendNextToken( "do" ) + formatStatlist(statement.Body) + appendNextToken( "end" ) + + elseif statement.AstType == 'NumericForStatement' then + appendNextToken( "for" ) + appendStr( statement.Variable.Name ) + appendNextToken( "=" ) + formatExpr(statement.Start) + appendNextToken( "," ) + formatExpr(statement.End) + if statement.Step then + appendNextToken( "," ) + formatExpr(statement.Step) + end + appendNextToken( "do" ) + formatStatlist(statement.Body) + appendNextToken( "end" ) + + elseif statement.AstType == 'LabelStatement' then + appendNextToken( "::" ) + appendStr( statement.Label ) + appendNextToken( "::" ) + + elseif statement.AstType == 'GotoStatement' then + appendNextToken( "goto" ) + appendStr( statement.Label ) + + elseif statement.AstType == 'Eof' then + appendWhite() + + elseif statement.AstType == 'SemicolonStatement' then + + else + error(string.format("Unknown AST Type: %s\n", tostring(statement.AstType))) + end + + if statement.Semicolon then + appendNextToken(";") + end + + assert(tok_it == #statement.Tokens + 1) + end + + formatStatlist = function(statList) + for _, stat in ipairs(statList.Body) do + formatStatement(stat) + end + end + + formatStatlist(ast) + + return table.concat(out.rope) +end + +local dirsep = package.config:sub(1,1) +local dirl7801 = (string.match(arg[0], "(.*[\\/]).*") or ''):gsub('/',dirsep) +local searchl7801 = '' +if #dirl7801 > 0 then + searchl7801 = string.format(";%s?;%s?.l7801", dirl7801, dirl7801) + package.path = package.path .. string.format(";%s?.lua", dirl7801) +end +l7801_def = { + parse = ParseLua, + format = Format7801, + searcher_index = 2, + search_path = string.format(".%s?;.%s?.l7801%s", dirsep, dirsep, searchl7801), + load_org = load, + loadfile_org = loadfile, + dofile_org = dofile, +} +if not l7801 then l7801 = l7801_def else for k,v in pairs(l7801_def) do l7801[k]=v end end +l7801.report = function(success, ...) + if success then return success,... end + local message=... io.stderr:write(tostring(message)..'\n') + os.exit(-1) +end +l7801.msghandler = function(msg) + local o = function(s) io.stderr:write(s) end + + msg = tostring(msg) + msg = msg:gsub('%[string "(.-%.l7801)"%]', '%1') -- [string "xxx.l7801"] -> xxx.l7801 + local trace_cur = debug.traceback(nil, 2) + trace_cur = trace_cur:gsub('%[string "(.-%.l7801)"%]', '%1') -- [string "xxx.l7801"] -> xxx.l7801 + trace_cur = trace_cur:gsub('stack traceback:', '') + + local i=2 + while debug.getinfo(i) do + local j = 1 + while true do + local n,v = debug.getlocal(i, j) + if not n then break end + if n == 'l7801dbg' then + o(string.format("%s\n", msg)) + if trace_cur:find("in local 'late'") then + local lines = {} + for line in trace_cur:gmatch("[^\n]+") do + if line:find("in local 'late'") then break end + table.insert(lines, line) + end + o(table.concat(lines,'\n')) + end + local trace = v.trace:match(".-\n(.*)\n.-'xpcall'") + trace = trace:gsub('%[string "(.-%.l7801)"%]', '%1') + trace = trace:gsub('stack traceback:', '') + o(trace .. '\n') + os.exit(-2) + end + j = j + 1 + end + i = i + 1 + end + + trace_cur = trace_cur:match(".-\n(.*)\n.-'xpcall'") + o(string.format("%s\n%s\n", msg, trace_cur)) + os.exit(-3) +end +do + local getembedded = type(arg[-1]) == 'function' and arg[-1] + l7801.load_embedded = function(name) + if not getembedded then return end + local src,isl7801 = getembedded(name) + if not src then return end + if isl7801 then + name = name .. '.l7801' + local st, ast = l7801.report(l7801.parse(src, name)) + src = l7801.format(ast) + else + name = name .. '.lua' + end + local bc = assert(l7801.load_org(src, name)) + return bc, name + end +end +l7801.searcher = function(name) + local filename,err = package.searchpath(name, l7801.search_path, '.', '.') + if not filename then return err end + local file = assert(io.open(filename, 'rb')) + local src = file:read('*a') + file:close() + local st, ast = l7801.report(l7801.parse(src, filename)) + local bc = assert(l7801.load_org(l7801.format(ast), filename)) + return bc, filename +end +l7801.load = function(chunk, chunkname, mode, ...) + local chunk_t,s = type(chunk) + if chunk_t == 'string' then s = chunk + elseif chunk_t == 'function' then + s={} for x in chunk do if #x==0 then break end s[#s+1]=x end + s = table.concat(s) + else return nil, string.format("invalid type for chunk %s: %s", chunkname or "=(load)", chunk_t) + end + if s:sub(1,4) == "\x1bLua" then -- a binary file + return l7801.load_org(s, chunkname, mode, ...) + end + local st, ast = l7801.report(l7801.parse(s, chunkname or "=(load)")) + return l7801.load_org(l7801.format(ast), chunkname, 't', ...) +end +l7801.loadfile = function(filename, mode, ...) + local s + if not filename then s = io.read('*a') + else + local file = io.open(filename, 'rb') + if not file then return nil,"failed to open " .. filename .. " for reading" end + s = file:read('*a') + file:close() + end + return l7801.load(s, filename, mode, ...) +end +l7801.dofile = function(filename) + local f = l7801.report(l7801.loadfile(filename)) + return f() +end +l7801.installhooks = function() + if package.searchers[l7801.searcher_index] ~= l7801.searcher then + table.insert(package.searchers, l7801.searcher_index, l7801.searcher) + end + if not l7801.hooks_installed then l7801.hooks_installed = true + load = l7801.load + loadfile = l7801.loadfile + dofile = l7801.dofile + end +end +l7801.uninstallhooks = function() + for k,v in ipairs(package.searchers) do + if v == l7801.searcher then table.remove(package.searchers, k) break end + end + if l7801.hooks_installed then l7801.hooks_installed = nil + load = l7801.load_org + loadfile = l7801.loadfile_org + dofile = l7801.dofile_org + end +end +table.insert(package.searchers, l7801.searcher_index, l7801.load_embedded) +l7801.installhooks() + +function getopt(optstring, ...) + local opts = { } + local args = { ... } + for optc, optv in optstring:gmatch"(%a)(:?)" do + opts[optc] = { hasarg = optv == ":" } + end + return coroutine.wrap(function() + local yield = coroutine.yield + local i = 1 + while i <= #args do + local arg,argi = args[i], i + i = i + 1 + if arg == "--" then + break + elseif arg:sub(1, 1) == "-" then + for j = 2, #arg do + local opt = arg:sub(j, j) + if opts[opt] then + if opts[opt].hasarg then + if j == #arg then + if args[i] then yield(opt, args[i], argi) i=i+1 + elseif optstring:sub(1, 1) == ":" then yield(':', opt, argi) + else yield('?', opt, argi) end + else yield(opt, arg:sub(j + 1), argi) end + break + else yield(opt, false, argi) end + else yield('?', opt, argi) end + end + else yield(false, arg, argi) end + end + for i = i, #args do yield(false, args[i], i) end + end) +end + +local cfg=require"l65cfg" l7801.cfg=cfg +local version = function() + print(string.format("l7801 %s", cfg.version)) +end +local usage = function(f) + if not f then f = io.stdout end + f:write(string.format([[ +Usage: %s [options] file [args] +Options: + -d Dump the Lua code after l7801 parsing into file + -h Display this information + -v Display the release version +]], arg[0])) +end +local invalid_usage = function() + io.stderr:write("Invalid usage.\n") + usage(io.stderr) +end + +local inf,dump,optix +for opt,arg,i in getopt("d:hv", ...) do + if opt == '?' then return invalid_usage() end + if opt == 'h' then return usage() end + if opt == 'v' then return version() end + if opt == 'd' then dump = arg end + if opt == false then inf=arg optix=i+1 break end +end +if not inf then return invalid_usage() end +if dump then l7801.format = function(ast) + local s=Format7801(ast) l7801.format = Format7801 + local f = assert(io.open(dump, 'wb')) f:write(s) f:close() + return s +end end + +local fn='' for i=#inf,1,-1 do local c=inf:sub(i,i) if c==dirsep or c=='/' then break end fn=c..fn if c=='.' then fn='' end end filename=fn +local f = l7801.report(l7801.loadfile(inf)) +return xpcall(f, l7801.msghandler, select(optix, ...)) diff --git a/main.c b/main.c index 16f3599..ce601b2 100644 --- a/main.c +++ b/main.c @@ -164,7 +164,7 @@ static int getembedded(lua_State *L) } } return 0; -} +} static int msghandler(lua_State *L) { @@ -189,6 +189,8 @@ int main(int argc, char *argv[]) // preload embedded lua scripts luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + luaL_loadbufferx(L, script_asm_lua, sizeof(script_asm_lua), "asm.lua", "b"); + lua_setfield(L, -2, "asm"); luaL_loadbufferx(L, script_6502_lua, sizeof(script_6502_lua), "6502.lua", "b"); lua_setfield(L, -2, "6502"); lua_pop(L, 1); diff --git a/samples/scv_hello.l7801 b/samples/scv_hello.l7801 new file mode 100644 index 0000000..51c6b4d --- /dev/null +++ b/samples/scv_hello.l7801 @@ -0,0 +1,40 @@ +require 'scv' +location(0x8000, 0x8FFF) +section{"rom", org=0x8000} + dc.b 'H' +@main + di + lxi sp,0xFFD2 + ei + calt 0x8C + + lxi hl,vdc_data + lxi de,0x3400 + mvi c,0x03 + block + + lxi hl,message + lxi de,0x3044 + lxi bc,0x01ff + +@loop_0 + block + dcr b + jr loop_0 + + -- beep + lxi hl,0x3600 + calf 0xfb0 + +@loop_1 + nop + jr loop_1 + +section{"vdc_data", org=0x8030} + dc.b 0xC0,0x00,0x00,0xF2 + +section{"message", org=0x8040} + dc.b "\t\t\t\t Hello world! \t\t\t\t" + dc.b 0x00 + +writebin(filename .. '.bin') diff --git a/samples/scv_plogue_tester.l7801 b/samples/scv_plogue_tester.l7801 new file mode 100644 index 0000000..1135397 --- /dev/null +++ b/samples/scv_plogue_tester.l7801 @@ -0,0 +1,498 @@ +---------------------------------------------------------------- +-- Plogue uPD1771C Tester +-- BY David Viens january 2010 chipsounds R&D +-- (from Hello World! Program Ver 1.1 by) Programmed By Enri +-- haxored to emit 7801 instructions using +-- Macro Assembler AS V1.42's 7810 mode +-- http://john.ccac.rwth-aachen.de:8000/as +-- Converted to l65 +---------------------------------------------------------------- + +require 'scv' +location(0x8000, 0x9fff) +section{"rom", org=0x8000} + dc.b 'H' -- Headder + +-- 0FF8X seems reserved, but not FF9X +local PARAMS = 0xFF90 +local ACTIVE_PARAM = 0xFF9A +local PLAYING = 0xFF9B + +------------------------- +-- Clear Text All Area -- +------------------------- + calt 0x8c -- bios function!!! + +------------- +-- Set VDC -- +------------- + lxi hl,VDC_DATA + lxi de,0x3400 + mvi c,0x03 + block + +--------------------- +-- Set INITIAL RAM -- +--------------------- + lxi hl,INITIAL_RAM + lxi de,PARAMS + mvi c,0x0F + block + +-------------------------------------------------------------- +----------------- END INITIALISATION POUTINE ----------------- +-------------------------------------------------------------- + +------------------------- +-- Print Screen Format -- +------------------------- + lxi hl,DISPLAY_DATA + lxi de,0x3040 -- Text RAM Address + lxi bc,0x0180 +@LOOP0 + block + dcr b + jr LOOP0 + +-------------------------------------------------------------- +------------------------- MAIN LOOP ------------------------- +-------------------------------------------------------------- +@LOOPN + -- reposition pointer to current param + lxi hl,PARAMS + mov b,(ACTIVE_PARAM) + dcx hl +@POSITIONLOOP + inx hl + dcr b + jmp POSITIONLOOP + + + lxi de,0x3090 + -- SLEEP before note + mvi c,0x10 -- wait 1 second of vblanks + call SLEEPFUNC + + + mvi a,0xFD -- we want to store that into PA so that portB can be later scanned i guess like the vic/c64 kb + mov pa,a + mov a,pb -- see which are closed upon return + + oni a,2 + jr RIGHT_DOWN + jr RIGHT_END +@RIGHT_DOWN + ldax (hl) + adi a,1 + stax (hl) + + -- if playing replay patter + mov a,(PLAYING) + eqi a,1 + jmp ALL_DONE + -- fake not already playing + mvi a,0 + mov (PLAYING),a -- ON + call PLAY_PATTERN + jmp ALL_DONE + +@RIGHT_END + + oni a,1 + jr DOWN_DOWN + jr DOWN_END +@DOWN_DOWN + mov a,(ACTIVE_PARAM) + adi a,1 + mov (ACTIVE_PARAM),a + jmp ALL_DONE +@DOWN_END + + mvi a,0xFE + mov pa,a -- fill port with voltages + mov a,pb -- see which are closed upon return + + oni a,1 + jr LEFT_DOWN + jr LEFT_END +@LEFT_DOWN + ldax (hl) + sui a,1 + stax (hl) + + -- if playing replay patter + mov a,(PLAYING) + eqi a,1 + jmp ALL_DONE + -- fake not already playing + mvi a,0 + mov (PLAYING),a -- OFF + call PLAY_PATTERN + jmp ALL_DONE +@LEFT_END + + oni a,2 + jr UP_DOWN + jr UP_END +@UP_DOWN + mov a,(ACTIVE_PARAM) + sui a,1 + mov (ACTIVE_PARAM),a + jmp ALL_DONE +@UP_END + + oni a,4 + jr BUTTON_DOWN + jr BUTTON_UP +@BUTTON_DOWN + call PLAY_PATTERN + jmp ALL_DONE +@BUTTON_UP + call STOP_PATTERN + jr BUTTON_END +@BUTTON_END + +@ALL_DONE + + -- check bounds on active param + mov a,(ACTIVE_PARAM) + lti a,0x0A + jr ACT_GREATER + jr ACT_DONE +@ACT_GREATER + mvi a,0x00 + mov (ACTIVE_PARAM),a + jr ACT_DONE +@ACT_DONE + + -- print where we at! + call DISPLAY_LOOP + + jmp LOOPN + +------------------------------------------------------------------------- +-- Display init data +------------------------------------------------------------------------- +@VDC_DATA + dc.b 0xC0,0x00,0x00,0xF1 -- 0F1 -> black + +------------------------------------------------------------------------- +-- lame sleep (with C param) +------------------------------------------------------------------------- +section{"SLEEPFUNC", org=0x8100} +-- sleep the amount of time depending on C + push bc +@SLOOP + mvi b,0xff +@SLEEP0 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + dcr b + jr SLEEP0 + dcr c + jr SLOOP + pop bc + ret + +------------------------------------------------------------------------- +-- wait on VBLANK +------------------------------------------------------------------------- +section{"WVBLANK", org=0x8200} + skit f2 -- wait until acknowledged + jr WVBLANK + ret + +------------------------------------------------------------------------- +-- wait on D1771g (wait until INT1=1) +------------------------------------------------------------------------- +section{"WD1771G", org=0x8220} + skit f1 + jr WD1771G + ret + +------------------------------------------------------------------------- +-- Print ONE UINT8_T on screen as HEX (two screen chars printed) +-- +-- A should contain the hex value to print (A returns unchanged) +-- D should contain the screen value to dump the high part (D returns unchanged) +-- V is thrashed (used as temporary val) +------------------------------------------------------------------------- +section{"PRINT_HEX", org=0x8240} + mvi v,0 -- trick to do V=A + add v,a -- trick to do V=A + + ani a,0xF0 -- a = a & 0x0f; + clc -- clear carry so not to fuck up the following right shifts + rlr a -- + rlr a -- + rlr a -- + rlr a -- ok we got the HEX value for the up 4bits + + gti a,0x09 + jr HIGH_GREATER + jr HIGH_LOWER +@HIGH_GREATER + adi a,0x30 -- a+='0' (ascii) + jr HIGH_DONE +@HIGH_LOWER + adi a,0x37 -- a+=('A'-10) (ascii) + jr HIGH_DONE +@HIGH_DONE + + staxi (de) -- + + mvi a,0 -- trick to do A=V + add a,v -- trick to do A=V + ani a,0x0F -- a = a & 0x0f + + gti a,0x09 + jr LOW_GREATER + jr LOW_LOWER +@LOW_GREATER + adi a,0x30 -- a+='0' (ascii) + jr LOW_DONE +@LOW_LOWER + adi a,0x37 -- a+=('A'-10) (ascii) + jr LOW_DONE +@LOW_DONE + + staxd (de) -- + mvi a,0 -- trick to do A=V + add a,v -- trick to do A=V + ret + +------------------------------------------------------------------------- +-- Prints all current param values +------------------------------------------------------------------------- +section{"DISPLAY_LOOP", org=0x8280} + mvi b,0x0B + lxi de,0x3090 + lxi hl,PARAMS +@DLOOP + + -- if b==ACTIVE_PARAM show arrow + -- else show space + mvi a,0x0B + mov c,(ACTIVE_PARAM) + sub a,c + + eqa a,b + jr NOCURSOR + mvi a,0x1C + stax (de) + jr CURSDONE +@NOCURSOR + mvi a,0x20 + stax (de) +@CURSDONE + -- print all ram + ldaxi (hl) + -- increase D pos to display in hex + inx de + inx de + inx de + call PRINT_HEX + dcx de + dcx de + dcx de + + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + inx de + + dcr b + jmp DLOOP + ret + +------------------------------------------------------------------------- +-- Stop current pattern +------------------------------------------------------------------------- +section{"STOP_PATTERN", org=0x82d0} + -- if (!playing) return; + mov a,(PLAYING) + eqi a,1 + jr STOP_DONE + + mvi a,0 + mov (PLAYING),a -- OFF + + di + lxi hl,0x3600 -- SOUND REGISTER in H + call WVBLANK -- cwait on intf2 + skit f1 + nop + + mvix (hl),0x00 -- note OFF + + ei + +@STOP_DONE + ret + +------------------------------------------------------------------------- +-- Plays current pattern +-- Start with beep please +------------------------------------------------------------------------- +section{"PLAY_PATTERN", org=0x82f0} + mov a,(PLAYING) + eqi a,0 + jmp PLAY_DONE + + -- setting variable true! + mvi a,1 + mov (PLAYING),a -- ON + + lxi de,PARAMS + lxi hl,0x3600 -- SOUND REGISTER in H + + + ldaxi (de) + eqi a,1 + jmp PLAY_TONE + jmp PLAY_NOISE + +@PLAY_TONE + ------------------------ Tone Start ------------------------ + di + call WVBLANK + skit f1 + nop + -- instrument params start + mvi a,2 -- tone! + stax (hl) + call WD1771G -- cwait on intf1 + ldaxi (de) + stax (hl) + call WD1771G -- cwait on intf1 + ldaxi (de) + stax (hl) + call WD1771G -- cwait on intf1 + ldaxi (de) + stax (hl) + call WVBLANK -- cwait on intf2 + -- instrument params end + ei + jmp PLAY_DONE + +@PLAY_NOISE + ------------------------ Tone Start ------------------------ + di + call WVBLANK -- cwait on intf2 + skit f1 + nop + -- instrument params start + mvi a,1 -- NOISE + stax (hl) + call WD1771G -- cwait on intf1 + ldaxi (de) + stax (hl) + call WD1771G -- cwait on intf1 + ldaxi (de) + stax (hl) + call WD1771G -- cwait on intf1 + ldaxi (de) + stax (hl) + call WD1771G -- cwait on intf1 + ldaxi (de) + stax (hl) + call WD1771G -- cwait on intf1 + ldaxi (de) + stax (hl) + call WD1771G -- cwait on intf1 + ldaxi (de) + stax (hl) + call WD1771G -- cwait on intf1 + ldaxi (de) + stax (hl) + call WD1771G -- cwait on intf1 + ldaxi (de) + stax (hl) + call WD1771G -- cwait on intf1 + ldaxi (de) + stax (hl) + call WVBLANK -- cwait on intf2 + -- instrument params end + ei + jmp PLAY_DONE + +@PLAY_DONE + ret + +------------------------------------------------------------------------- +-- Message Data1 +-- this version has left+audio changes working +------------------------------------------------------------------------- +@DISPLAY_DATA + dc.b " Plogue uPD1771C Tester " + dc.b " v0.0018 " + dc.b " Param[0]: ( ) " + dc.b " Param[1]: ( ) " + dc.b " Param[2]: ( ) " + dc.b " Param[3]: ( ) " + dc.b " Param[4]: ( ) " + dc.b " Param[5]: ( ) " + dc.b " Param[6]: ( ) " + dc.b " Param[7]: ( ) " + dc.b " Param[8]: ( ) " + dc.b " Param[9]: ( ) " + + -- go to [FF90;FF9F] +@INITIAL_RAM + -- params + dc.b 0x02 + dc.b 0x80 + dc.b 0xFF + dc.b 0x1F + + dc.b 0xAB + dc.b 0xFE + dc.b 0xCD + dc.b 0xDC + dc.b 0x45 + dc.b 0x01 + + dc.b 0x00 -- FF9A curent param + dc.b 0x00 -- FF9B BOOL note on + dc.b 0x83 -- misc + dc.b 0x39 -- misc + dc.b 0x44 -- misc + dc.b 0x36 -- misc + +writebin(filename..'.bin', genbin(0xff)) diff --git a/samples/scv_test.l7801 b/samples/scv_test.l7801 new file mode 100644 index 0000000..dab312e --- /dev/null +++ b/samples/scv_test.l7801 @@ -0,0 +1,738 @@ +require 'scv' +location(0x8000, 0x8FFF) +section{"rom", org=0x8000} + dc.b 'H' +@main + block + calb + ei + daa + di + ex + exx + halt + jb + nop + ret + reti + rets + sio + softi + stm + table + dcr a + dcr b + dcr c + inr a + inr b + inr c + dcx sp + inx sp + mvi a,0xca + mvi b,0xfe + mvi c,0xbe + mvi d,0xef + mvi e,0xf0 + mvi h,0x0d + mvi l,0x78 + mvi v,0x01 + aci a,0x0f + adi a,0x1e + adinc a,0x2d + ani a,0x3c + eqi a,0x4b + gti a,0x5a + lti a,0x69 + nei a,0x87 + offi a,0x96 + oni a,0xa5 + ori a,0xb4 + sbi a,0xc3 + sui a,0xd2 + suinb a,0xe1 + xri a,0xf0 + lxi bc,0xabcd + lxi de,0xbeef + lxi hl,0xcafe + lxi sp,0xf00d + call 0xd701 + jmp 0x8e07 + clc + pen + per + pex + rld + rrd + stc + mov a,b + mov a,c + mov a,d + mov a,e + mov a,h + mov a,l + mov b,a + mov c,a + mov d,a + mov e,a + mov h,a + mov l,a +@l0 jre l0 +@l1 nop + jre l1 +@l2 nop nop + jre l2 +@l3 nop nop nop + jre l3 +@l4 nop nop nop nop + jre l4 + jre l5 +@l5 jre l6 + nop +@l6 jre l7 + nop nop +@l7 jre l8 + nop nop nop +@l8 jre l9 + nop nop nop nop +@l9 + ldax (bc) + ldax (de) + ldax (hl) + stax (bc) + stax (de) + stax (hl) + inrw (v,0x01) + ldaw (v,0x23) + dcrw (v,0x45) + staw (v,0x67) + bit0 (v,0x89) + bit1 (v,0xab) + bit2 (v,0xcd) + bit3 (v,0xef) + bit4 (v,0xfe) + bit5 (v,0xdc) + bit6 (v,0xba) + bit7 (v,0x98) + dcx bc + dcx de + dcx hl + inx bc + inx de + inx hl + mvix (bc),0xf9 + mvix (de),0xe8 + mvix (hl),0xd7 + mviw (v,0x9a),0x3f + eqiw (v,0xc5),0x1b + ldaxd (de) + ldaxi (de) + ldaxd (hl) + ldaxi (hl) + staxd (de) + staxi (de) + staxd (hl) + staxi (hl) + push bc + push de + push hl + pop hl + pop de + pop bc + push va + pop va + rll a + rlr a + rll c + rlr c + sll a + slr a + sll c + slr c + skc + skz + sknc + sknz + skit f0 + skit ft + skit f1 + skit f2 + skit fs + sknit f0 + sknit ft + sknit f1 + sknit f2 + sknit fs + in 0x00 + in 0xa7 + in 0xbf + out 0x00 + out 0x5a + out 0xbf + mov a,pa + mov a,pb + mov a,pc + mov a,mk + mov a,mb + mov a,mc + mov a,tm0 + mov a,tm1 + mov a,s + mov pa,a + mov pb,a + mov pc,a + mov mk,a + mov mb,a + mov mc,a + mov tm0,a + mov tm1,a + mov s,a + ana v,a + ana a,a + ana b,a + ana c,a + ana d,a + ana e,a + ana h,a + ana l,a + xra v,a + xra a,a + xra b,a + xra c,a + xra d,a + xra e,a + xra h,a + xra l,a + ora v,a + ora a,a + ora b,a + ora c,a + ora d,a + ora e,a + ora h,a + ora l,a + addnc v,a + addnc a,a + addnc b,a + addnc c,a + addnc d,a + addnc e,a + addnc h,a + addnc l,a + gta v,a + gta a,a + gta b,a + gta c,a + gta d,a + gta e,a + gta h,a + gta l,a + subnb v,a + subnb a,a + subnb b,a + subnb c,a + subnb d,a + subnb e,a + subnb h,a + subnb l,a + lta v,a + lta a,a + lta b,a + lta c,a + lta d,a + lta e,a + lta h,a + lta l,a + add v,a + add a,a + add b,a + add c,a + add d,a + add e,a + add h,a + add l,a + adc v,a + adc a,a + adc b,a + adc c,a + adc d,a + adc e,a + adc h,a + adc l,a + sub v,a + sub a,a + sub b,a + sub c,a + sub d,a + sub e,a + sub h,a + sub l,a + nea v,a + nea a,a + nea b,a + nea c,a + nea d,a + nea e,a + nea h,a + nea l,a + sbb v,a + sbb a,a + sbb b,a + sbb c,a + sbb d,a + sbb e,a + sbb h,a + sbb l,a + eqa v,a + eqa a,a + eqa b,a + eqa c,a + eqa d,a + eqa e,a + eqa h,a + eqa l,a + ana a,v + ana a,a + ana a,b + ana a,c + ana a,d + ana a,e + ana a,h + ana a,l + xra a,v + xra a,a + xra a,b + xra a,c + xra a,d + xra a,e + xra a,h + xra a,l + ora a,v + ora a,a + ora a,b + ora a,c + ora a,d + ora a,e + ora a,h + ora a,l + addnc a,v + addnc a,a + addnc a,b + addnc a,c + addnc a,d + addnc a,e + addnc a,h + addnc a,l + gta a,v + gta a,a + gta a,b + gta a,c + gta a,d + gta a,e + gta a,h + gta a,l + subnb a,v + subnb a,a + subnb a,b + subnb a,c + subnb a,d + subnb a,e + subnb a,h + subnb a,l + lta a,v + lta a,a + lta a,b + lta a,c + lta a,d + lta a,e + lta a,h + lta a,l + add a,v + add a,a + add a,b + add a,c + add a,d + add a,e + add a,h + add a,l + ona a,v + ona a,a + ona a,b + ona a,c + ona a,d + ona a,e + ona a,h + ona a,l + adc a,v + adc a,a + adc a,b + adc a,c + adc a,d + adc a,e + adc a,h + adc a,l + offa a,v + offa a,a + offa a,b + offa a,c + offa a,d + offa a,e + offa a,h + offa a,l + sub a,v + sub a,a + sub a,b + sub a,c + sub a,d + sub a,e + sub a,h + sub a,l + nea a,v + nea a,a + nea a,b + nea a,c + nea a,d + nea a,e + nea a,h + nea a,l + sbb a,v + sbb a,a + sbb a,b + sbb a,c + sbb a,d + sbb a,e + sbb a,h + sbb a,l + eqa a,v + eqa a,a + eqa a,b + eqa a,c + eqa a,d + eqa a,e + eqa a,h + eqa a,l + ani v,0x00 + ani a,0x01 + ani b,0x02 + ani c,0x03 + ani d,0x04 + ani e,0x05 + ani h,0x06 + ani l,0x07 + xri v,0x08 + xri a,0x09 + xri b,0x0a + xri c,0x0b + xri d,0x0c + xri e,0x0d + xri h,0x0e + xri l,0x0f + ori v,0x10 + ori a,0x11 + ori b,0x12 + ori c,0x13 + ori d,0x14 + ori e,0x15 + ori h,0x16 + ori l,0x17 + adinc v,0x18 + adinc a,0x19 + adinc b,0x1a + adinc c,0x1b + adinc d,0x1c + adinc e,0x1d + adinc h,0x1e + adinc l,0x1f + gti v,0x20 + gti a,0x21 + gti b,0x22 + gti c,0x23 + gti d,0x24 + gti e,0x25 + gti h,0x26 + gti l,0x27 + suinb v,0x28 + suinb a,0x29 + suinb b,0x2a + suinb c,0x2b + suinb d,0x2c + suinb e,0x2d + suinb h,0x2e + suinb l,0x2f + lti v,0x30 + lti a,0x31 + lti b,0x32 + lti c,0x33 + lti d,0x34 + lti e,0x35 + lti h,0x36 + lti l,0x37 + adi v,0x38 + adi a,0x39 + adi b,0x3a + adi c,0x3b + adi d,0x3c + adi e,0x3d + adi h,0x3e + adi l,0x4f + oni v,0x40 + oni a,0x41 + oni b,0x42 + oni c,0x43 + oni d,0x44 + oni e,0x45 + oni h,0x46 + oni l,0x47 + aci v,0x48 + aci a,0x49 + aci b,0x4a + aci c,0x4b + aci d,0x4c + aci e,0x4d + aci h,0x4e + aci l,0x4f + offi v,0x50 + offi a,0x51 + offi b,0x52 + offi c,0x53 + offi d,0x54 + offi e,0x55 + offi h,0x56 + offi l,0x57 + sui v,0x58 + sui a,0x59 + sui b,0x5a + sui c,0x5b + sui d,0x5c + sui e,0x5d + sui h,0x5e + sui l,0x5f + nei v,0x60 + nei a,0x61 + nei b,0x62 + nei c,0x63 + nei d,0x64 + nei e,0x65 + nei h,0x66 + nei l,0x67 + sbi v,0x68 + sbi a,0x69 + sbi b,0x6a + sbi c,0x6b + sbi d,0x6c + sbi e,0x6d + sbi h,0x6e + sbi l,0x6f + eqi v,0x70 + eqi a,0x71 + eqi b,0x72 + eqi c,0x73 + eqi d,0x74 + eqi e,0x75 + eqi h,0x76 + eqi l,0x77 + ani pa,0x78 + ani pb,0x79 + ani pc,0x7a + ani mk,0x7b + xri pa,0x7c + xri pb,0x7d + xri pc,0x7e + xri mk,0x7f + ori pa,0x80 + ori pb,0x81 + ori pc,0x82 + ori mk,0x83 + adinc pa,0x84 + adinc pb,0x85 + adinc pc,0x86 + adinc mk,0x87 + gti pa,0x88 + gti pb,0x89 + gti pc,0x8a + gti mk,0x8b + suinb pa,0x8c + suinb pb,0x8d + suinb pc,0x8e + suinb mk,0x8f + lti pa,0x90 + lti pb,0x91 + lti pc,0x92 + lti mk,0x93 + adi pa,0x94 + adi pb,0x95 + adi pc,0x96 + adi mk,0x97 + oni pa,0x98 + oni pb,0x99 + oni pc,0x9a + oni mk,0x9b + aci pa,0x9c + aci pb,0x9d + aci pc,0x9e + aci mk,0x9f + offi pa,0xa0 + offi pb,0xa1 + offi pc,0xa2 + offi mk,0xa3 + sui pa,0xa4 + sui pb,0xa5 + sui pc,0xa6 + sui mk,0xa7 + nei pa,0xa8 + nei pb,0xa9 + nei pc,0xaa + nei mk,0xab + sbi pa,0xac + sbi pb,0xad + sbi pc,0xae + sbi mk,0xaf + eqi pa,0xb0 + eqi pb,0xb1 + eqi pc,0xb2 + eqi mk,0xb3 + anaw (v,0xf0) + xraw (v,0xe1) + oraw (v,0xd2) + addncw (v,0xc3) + gtaw (v,0xb4) + subnbw (v,0xa5) + ltaw (v,0x96) + addw (v,0x87) + onaw (v,0x78) + adcw (v,0x69) + offaw (v,0x5a) + subw (v,0x4b) + neaw (v,0x3c) + sbbw (v,0x2d) + eqaw (v,0x1e) + sspd (0x8000) + lspd (0x8001) + sbcd (0x8010) + lbcd (0x8011) + sded (0x8100) + lded (0x8101) + shld (0x8110) + lhld (0x8111) + mov (0xab00),v + mov (0xbc01),a + mov (0xde02),b + mov (0xfa03),c + mov (0xeb04),d + mov (0xdc05),e + mov (0xcd06),h + mov (0xbe07),l + mov v,(0x000f) + mov a,(0x00f0) + mov b,(0x00ff) + mov c,(0x0f00) + mov d,(0x0f0f) + mov e,(0x0ff0) + mov h,(0x0fff) + mov l,(0xf000) + anax (bc) + xrax (bc) + orax (bc) + addncx (bc) + gtax (bc) + subnbx (bc) + ltax (bc) + addx (bc) + onax (bc) + adcx (bc) + offax (bc) + subx (bc) + neax (bc) + sbbx (bc) + eqax (bc) + anax (de) + xrax (de) + orax (de) + addncx (de) + gtax (de) + subnbx (de) + ltax (de) + addx (de) + onax (de) + adcx (de) + offax (de) + subx (de) + neax (de) + sbbx (de) + eqax (de) + anax (hl) + xrax (hl) + orax (hl) + addncx (hl) + gtax (hl) + subnbx (hl) + ltax (hl) + addx (hl) + onax (hl) + adcx (hl) + offax (hl) + subx (hl) + neax (hl) + sbbx (hl) + eqax (hl) + anaxi (de) + xraxi (de) + oraxi (de) + addncxi (de) + gtaxi (de) + subnbxi (de) + ltaxi (de) + addxi (de) + onaxi (de) + adcxi (de) + offaxi (de) + subxi (de) + neaxi (de) + sbbxi (de) + eqaxi (de) + anaxi (hl) + xraxi (hl) + oraxi (hl) + addncxi (hl) + gtaxi (hl) + subnbxi (hl) + ltaxi (hl) + addxi (hl) + onaxi (hl) + adcxi (hl) + offaxi (hl) + subxi (hl) + neaxi (hl) + sbbxi (hl) + eqaxi (hl) + anaxd (de) + xraxd (de) + oraxd (de) + addncxd (de) + gtaxd (de) + subnbxd (de) + ltaxd (de) + addxd (de) + onaxd (de) + adcxd (de) + offaxd (de) + subxd (de) + neaxd (de) + sbbxd (de) + eqaxd (de) + anaxd (hl) + xraxd (hl) + oraxd (hl) + addncxd (hl) + gtaxd (hl) + subnbxd (hl) + ltaxd (hl) + addxd (hl) + onaxd (hl) + adcxd (hl) + offaxd (hl) + subxd (hl) + neaxd (hl) + sbbxd (hl) + eqaxd (hl) + +writebin(filename .. '.bin') diff --git a/scv.l7801 b/scv.l7801 new file mode 100644 index 0000000..c92d5cd --- /dev/null +++ b/scv.l7801 @@ -0,0 +1,3 @@ +cpu = require 'uPD7801' +setmetatable(_ENV, cpu) + diff --git a/uPD7801.lua b/uPD7801.lua new file mode 100644 index 0000000..4aee978 --- /dev/null +++ b/uPD7801.lua @@ -0,0 +1,638 @@ +M = require "asm" + +local op_eval_byte = function(late, early) return M.byte_normalize(M.op_eval(late, early)) end +M.op_eval_byte = op_eval_byte + +local op_eval_word = function(late, early) return M.word_normalize(M.op_eval(late, early)) end +M.op_eval_word = op_eval_word + +local opimp={ + block=M.op(0x31,13), + calb=M.op(0x63,13), + daa=M.op(0x61,4), + ex=M.op(0x10,4), + exx=M.op(0x11,4), + halt=M.op(0x01,6), + jb=M.op(0x73,4), + nop=M.op(0x00,4), + ret=M.op(0x08,11), + reti=M.op(0x62,15), + rets=M.op(0x18,11), + sio=M.op(0x09,4), + softi=M.op(0x72,19), + stm=M.op(0x19,4), + ['table']=M.op(0x21,19) +} M.opimp = opimp +for k,v in pairs(opimp) do + M[k .. 'imp' ] = function() + table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc }) + end +end + +local opa={ + dcr=M.op(0x51,4), + inr=M.op(0x41,4), +} M.opa = opa +for k,v in pairs(opa) do + M[k .. 'a'] = function() + table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc }) + end +end + +local opb={ + dcr=M.op(0x52,4), + inr=M.op(0x42,4), +} M.opb = opb +for k,v in pairs(opb) do + M[k .. 'b'] = function() + table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc }) + end +end + +local opc={ + dcr=M.op(0x53,4), + inr=M.op(0x43,4), +} M.opc = opc +for k,v in pairs(opc) do + M[k .. 'c'] = function() + table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc }) + end +end + +local opsp={ + dcx=M.op(0x03,7), + inx=M.op(0x02,7) +} M.opsp = opsp +for k,v in pairs(opsp) do + M[k .. 'sp'] = function() + table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc }) + end +end + +local opr16={ + dcxbc=M.op(0x13,7), + dcxde=M.op(0x23,7), + dcxhl=M.op(0x33,7), + inxbc=M.op(0x12,7), + inxde=M.op(0x22,7), + inxhl=M.op(0x32,7), + ldaxbc=M.op(0x29,7), + ldaxdde=M.op(0x2e,7), + ldaxde=M.op(0x2a,7), + ldaxhl=M.op(0x2b,7), + ldaxide=M.op(0x2c,7), + ldaxdhl=M.op(0x2f,7), + ldaxihl=M.op(0x2d,7), + staxbc=M.op(0x39,7), + staxde=M.op(0x3a,7), + staxdde=M.op(0x3e,7), + staxdhl=M.op(0x3f,7), + staxhl=M.op(0x3b,7), + staxide=M.op(0x3c,7), + staxihl=M.op(0x3d,7), +} M.opr16 = opr16 +for k,v in pairs(opr16) do + M[k] = function() + table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc }) + end +end + +local opregxx ={ + mvib=M.op(0x6a,7), + mvic=M.op(0x6b,7), + mvid=M.op(0x6c,7), + mvie=M.op(0x6d,7), + mvih=M.op(0x6e,7), + mvil=M.op(0x6f,7), + mviv=M.op(0x68,7), + mvixbc=M.op(0x49,10), + mvixde=M.op(0x4a,10), + mvixhl=M.op(0x4b,10), +} M.opregxx = opregxx +for k,v in pairs(opregxx) do + M[k] = function(late, early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local size = function() late,early = M.size_op(late,early) return 2 end + local bin = function() local l7801dbg=l7801dbg return { v.opc, M.op_eval_byte(late,early) } end + table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) + end +end + +local opaxx ={ + aci=M.op(0x56,7), + adi=M.op(0x46,7), + adinc=M.op(0x26,7), + ani=M.op(0x07,7), + eqi=M.op(0x77,7), + gti=M.op(0x27,7), + lti=M.op(0x37,7), + mvi=M.op(0x69,7), + nei=M.op(0x67,7), + offi=M.op(0x57,7), + oni=M.op(0x47,7), + ori=M.op(0x17,7), + sbi=M.op(0x76,7), + sui=M.op(0x66,7), + suinb=M.op(0x36,7), + xri=M.op(0x16,7) +} M.opaxx = opaxx +for k,v in pairs(opaxx) do + M[k .. 'a'] = function(late, early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local size = function() late,early = M.size_op(late,early) return 2 end + local bin = function() local l7801dbg=l7801dbg return { v.opc, M.op_eval_byte(late,early) } end + table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) + end +end + +local opr8r8 ={ + movab=M.op(0x0a,4), movac=M.op(0x0b,4), + movad=M.op(0x0c,4), movae=M.op(0x0d,4), + movah=M.op(0x0e,4), moval=M.op(0x0f,4), + movba=M.op(0x1a,4), movca=M.op(0x1b,4), + movda=M.op(0x1c,4), movea=M.op(0x1d,4), + movha=M.op(0x1e,4), movla=M.op(0x1f,4), +} M.opr8r8 = opr8r8 +for k,v in pairs(opr8r8) do + M[k] = function() + table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc }) + end +end + +local opw = { + call=M.op(0x44,16), + jmp=M.op(0x54,10), +} M.opw = opw +for k,v in pairs(opw) do + M[k .. 'imm'] = function(late, early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local size = function() late,early = M.size_op(late,early) return 3 end + local bin = function() local l7801dbg=l7801dbg + local x = M.op_eval_word(late,early) + return { v.opc, x&0xff, x>>8 } + end + table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) + end +end + +local opr16w = { + lxisp=M.op(0x04,10), + lxibc=M.op(0x14,10), + lxide=M.op(0x24,10), + lxihl=M.op(0x34,10) +} M.opr16w = opr16w +for k,v in pairs(opr16w) do + M[k] = function(late, early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local size = function() late,early = M.size_op(late,early) return 3 end + local bin = function() local l7801dbg=l7801dbg + local x = M.op_eval_word(late,early) + return { v.opc, x&0xff, x>>8 } + end + table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) + end +end + + +M['calt' .. 'imm'] = function(late, early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local op = { cycles=19 } + op.size = function() late,early = M.size_op(late,early) return 1 end + op.bin = function() + local l7801dbg=l7801dbg + local x = M.op_eval_byte(late,early) + if (x%2 == 1) then error("offset should be even : " .. x) end + if x < 0x80 or x > 0xfe then error("offset out of range : " .. x) end + x = (x>>1) + 0x40 + return x + end + table.insert(M.section_current.instructions, op) +end + +M['calf' .. 'imm'] = function(late, early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local op = { cycles=16 } + op.size = function() late,early = M.size_op(late,early) return 2 end + op.bin = function() local l7801dbg=l7801dbg + local x = 0 + M.op_eval_word(late,early) + if x < 0x0800 or x > 0xffff then error("subroutine address out of range [0x0800-0xffff]: " .. x) end + x = x - 0x0800; + return { 0x78 | ((x>>8) & 0x07), x&0xff } + end + table.insert(M.section_current.instructions, op) +end + +M.jr = function(label) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local parent,offset = M.label_current + local section,rorg = M.section_current,M.location_current.rorg + local op = { cycles=13 } + op.size = function() + offset = section.size + label = M.size_dc(label) + return 1 + end + op.bin = function() + local l7801dbg=l7801dbg + local x,l = label,label + if type(x) == 'function' then x=x() end + if type(x) == 'string' then + if x:sub(1,1) == '_' then x=parent..x l=x end + x = symbols[x] + end + if type(x) ~= 'number' then error("unresolved branch target: " .. tostring(x)) end + x = x-1 - offset - rorg(section.org) + if x < -32 or x > 0x32 then error("branch target out of range for " .. l .. ": " .. x) + elseif x >= 0 then + x = 0xc0 + x + return x + else + return x & 0xff + end + end + table.insert(M.section_current.instructions, op) +end + +M.jre = function(label) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local parent,offset = M.label_current + local section,rorg = M.section_current,M.location_current.rorg + local op = { cycles=17 } + op.size = function() + offset = section.size + label = M.size_dc(label) + return 2 + end + op.bin = function() + local l7801dbg=l7801dbg + local x,l = label,label + if type(x) == 'function' then x=x() end + if type(x) == 'string' then + if x:sub(1,1) == '_' then x=parent..x l=x end + x = symbols[x] + end + if type(x) ~= 'number' then error("unresolved branch target: " .. tostring(x)) end + x = x-2 - offset - rorg(section.org) + if x < -128 or x > 127 then error("branch target out of range for " .. l .. ": " .. x) end + local opcode = x >= 0 and 0x4e or 0x4f + return { opcode, x&0xff } + end + table.insert(M.section_current.instructions, op) +end + +local opwa = { + inrw=M.op(0x20,13), + ldaw=M.op(0x28,10), + dcrw=M.op(0x30,13), + staw=M.op(0x38,10), + bit0=M.op(0x58,10), + bit1=M.op(0x59,10), + bit2=M.op(0x5a,10), + bit3=M.op(0x5b,10), + bit4=M.op(0x5c,10), + bit5=M.op(0x5d,10), + bit6=M.op(0x5e,10), + bit7=M.op(0x5f,10) +} M.opwa = opwa +for k,v in pairs(opwa) do + M[k .. 'wa'] = function(late, early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local size = function() late,early = M.size_op(late,early) return 2 end + local bin = function() local l7801dbg=l7801dbg return { v.opc, M.op_eval_byte(late,early) } end + table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) + end +end + +local opwaxx = { + mviw=M.op(0x71,3,13), + eqiw=M.op(0x75,3,13), +} M.opwaxx = opwaxx +for k,v in pairs(opwaxx) do + M[k .. 'waxx'] = function(late_offset, late_data, early_offset, early_data) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local size = function() + late_offset,early_offset = M.size_op(late_offset,early_offset) + late_data,early_data = M.size_op(late_data,early_data) + return 3 + end + local bin = function() + local l7801dbg=l7801dbg + return { v.opc, M.op_eval_byte(late_offset,early_offset), M.op_eval_byte(late_data,early_data) } + end + table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) + end +end + +local op48imp = { + ei=M.op(0x20,8), + di=M.op(0x24,8), + clc=M.op(0x2a,8), + pen=M.op(0x2c,11), + per=M.op(0x3c,11), + pex=M.op(0x2d,11), + rld=M.op(0x38,17), + rrd=M.op(0x39,17), + skc=M.op(0x0a,8), + skz=M.op(0x0c,8), + sknc=M.op(0x1a,8), + sknz=M.op(0x1c,8), + stc=M.op(0x2b,8), +} M.op48imp = op48imp +for k,v in pairs(op48imp) do + M[k .. 'imp'] = function() + table.insert(M.section_current.instructions, { size=2, cycles=v.cycles, bin={ 0x48, v.opc } }) + end +end + +local op48r8={ + rlla=M.op(0x30,8), + rlra=M.op(0x31,8), + rllc=M.op(0x32,8), + rlrc=M.op(0x33,8), + slla=M.op(0x34,8), + slra=M.op(0x35,8), + sllc=M.op(0x36,8), + slrc=M.op(0x37,8), +} M.op48r8 = op48r8 +for k,v in pairs(op48r8) do + M[k] = function() + table.insert(M.section_current.instructions, { size=2, cycles=v.cycles, bin={ 0x48, v.opc } }) + end +end + +local op48r16={ + pushbc=M.op(0x1e,17), + pushde=M.op(0x2e,17), + pushhl=M.op(0x3e,17), + pushva=M.op(0x0e,17), + popbc=M.op(0x1f,15), + popde=M.op(0x2f,15), + pophl=M.op(0x3f,15), + popva=M.op(0x0f,15), +} M.op48r16 = op48r16 +for k,v in pairs(op48r16) do + M[k] = function() + table.insert(M.section_current.instructions, { size=2, cycles=v.cycles, bin={ 0x48, v.opc } }) + end +end + +local op48int={ + skitf0=M.op(0x00,8), + skitft=M.op(0x01,8), + skitf1=M.op(0x02,8), + skitf2=M.op(0x03,8), + skitfs=M.op(0x04,8), + sknitf0=M.op(0x10,8), + sknitft=M.op(0x11,8), + sknitf1=M.op(0x12,8), + sknitf2=M.op(0x13,8), + sknitfs=M.op(0x14,8), +} M.op48int = op48int +for k,v in pairs(op48int) do + M[k] = function() + table.insert(M.section_current.instructions, { size=2, cycles=v.cycles, bin={ 0x48, v.opc } }) + end +end + +-- IN/OUT +local opinout={ + ['in']=M.op(0x4c,10), + out=M.op(0x4d,10), +} M.opinout = op4inout +for k,v in pairs(opinout) do + M[k .. 'imm'] = function(late, early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local op = { cycles=v.cycles } + op.size = function() late,early = M.size_op(late,early) return 2 end + op.bin = function() local l7801dbg=l7801dbg + local x = 0x00 + M.op_eval_byte(late,early) + return { v.opc, 0x00, x } + end + table.insert(M.section_current.instructions, op) + end +end + + +local op4car8={ + movapa=M.op(0xc0,10), + movapb=M.op(0xc1,10), + movapc=M.op(0xc2,10), + movamk=M.op(0xc3,10), + movamb=M.op(0xc4,10), + movamc=M.op(0xc5,10), + movatm0=M.op(0xc6,10), + movatm1=M.op(0xc7,10), + movas=M.op(0xc8,10), +} M.op4car8 = op4car8 +for k,v in pairs(op4car8) do + M[k] = function() + table.insert(M.section_current.instructions, { size=2, cycles=v.cycles, bin={ 0x4c, v.opc } }) + end +end + +local op4dr8a={ + movpaa=M.op(0xc0,10), + movpba=M.op(0xc1,10), + movpca=M.op(0xc2,10), + movmka=M.op(0xc3,10), + movmba=M.op(0xc4,10), + movmca=M.op(0xc5,10), + movtm0a=M.op(0xc6,10), + movtm1a=M.op(0xc7,10), + movsa=M.op(0xc8,10), +} M.op4dr8a = op4dr8a +for k,v in pairs(op4dr8a) do + M[k] = function() + table.insert(M.section_current.instructions, { size=2, cycles=v.cycles, bin={ 0x4d, v.opc } }) + end +end + +local op60names = {'ana','xra','ora','addnc','gta','subnb','lta','add','','adc','','sub','nea','sbb','eqa'} +local register_names = {'v','a','b','c','d','e','h','l'} +local k = 0x08 +for i,o in ipairs(op60names) do + if o == '' then + k = k + #register_names + else + for j,r in ipairs(register_names) do + local l = k + M[o .. r .. 'a'] = function() + table.insert(M.section_current.instructions, { size=2, cycles=8, bin={ 0x60, l } }) + end + k = k + 1 + end + end +end + +k = 0x88 +op60names[9] = 'ona' +op60names[11] = 'offa' +for i,o in ipairs(op60names) do + if o == '' then + k = k + #register_names + else + for j,r in ipairs(register_names) do + local l = k + local name = o .. 'a' .. r + if not M[name] then + M[name] = function() + table.insert(M.section_current.instructions, { size=2, cycles=8, bin={ 0x60, l } }) + end + end + k = k + 1 + end + end +end + +k = 0x08 +local op64names = { 'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi' } +for i,o in ipairs(op64names) do + for j,r in ipairs(register_names) do + local name = o .. r + if not M[name] then + local l = k + M[name] = function(late,early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local op = { cycles=11 } + op.size = function() late,early = M.size_op(late,early) return 3 end + op.bin = function() local l7801dbg=l7801dbg + local x = 0x00 + l; + local y = 0x00 + M.op_eval_byte(late,early) + return { 0x64, x, y } + end + table.insert(M.section_current.instructions, op) + end + end + k = k + 1 + end +end + +k = 0x88 +local ex_register_names = {'pa','pb','pc','mk'} +for i,o in ipairs(op64names) do + for j,r in ipairs(ex_register_names) do + local name = o .. r + if not M[name] then + local l = k + M[name] = function(late,early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local op = { cycles=11 } + op.size = function() late,early = M.size_op(late,early) return 3 end + op.bin = function() local l7801dbg=l7801dbg + local x = 0x00 + l; + local y = 0x00 + M.op_eval_byte(late,early) + return { 0x64, x, y } + end + table.insert(M.section_current.instructions, op) + end + end + k = k + 1 + end + k = k + 4 +end + +local op74wa = { + anaw=M.op(0x88,14), + xraw=M.op(0x90,14), + oraw=M.op(0x98,14), + addncw=M.op(0xa0,14), + gtaw=M.op(0xa8,14), + subnbw=M.op(0xb0,14), + ltaw=M.op(0xb8,14), + addw=M.op(0xc0,14), + onaw=M.op(0xc8,14), + adcw=M.op(0xd0,14), + offaw=M.op(0xd8,14), + subw=M.op(0xe0,14), + neaw=M.op(0xe8,14), + sbbw=M.op(0xf0,14), + eqaw=M.op(0xf8,14), +} M.op74wa = op74wa +for k,v in pairs(op74wa) do + M[k .. 'wa'] = function(late, early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local size = function() late,early = M.size_op(late,early) return 3 end + local bin = function() local l7801dbg=l7801dbg return { 0x74, v.opc, M.op_eval_byte(late,early) } end + table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) + end +end + +local op70ind = { + sspd=M.op(0x0e,20), + lspd=M.op(0x0f,20), + sbcd=M.op(0x1e,20), + lbcd=M.op(0x1f,20), + sded=M.op(0x2e,20), + lded=M.op(0x2f,20), + shld=M.op(0x3e,20), + lhld=M.op(0x3f,20), +} M.op70ind = op70ind +for k,v in pairs(op70ind) do + M[k] = function(late, early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local size = function() late,early = M.size_op(late,early) return 4 end + local bin = function() local l7801dbg=l7801dbg + local x = M.op_eval_word(late,early) + return { 0x70, v.opc, x&0xff, x>>8 } + end + table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) + end +end + +local op70indr8 = { + movindv=M.op(0x78,17), + movinda=M.op(0x79,17), + movindb=M.op(0x7a,17), + movindc=M.op(0x7b,17), + movindd=M.op(0x7c,17), + movinde=M.op(0x7d,17), + movindh=M.op(0x7e,17), + movindl=M.op(0x7f,17), +} M.op70indr8 = op70indr8 +for k,v in pairs(op70indr8) do + M[k] = function(late, early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local size = function() late,early = M.size_op(late,early) return 4 end + local bin = function() local l7801dbg=l7801dbg + local x = M.op_eval_word(late,early) + return { 0x70, v.opc, x&0xff, x>>8 } + end + table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) + end +end + +local op70r8ind = { + movvind=M.op(0x68,17), + movaind=M.op(0x69,17), + movbind=M.op(0x6a,17), + movcind=M.op(0x6b,17), + movdind=M.op(0x6c,17), + moveind=M.op(0x6d,17), + movhind=M.op(0x6e,17), + movlind=M.op(0x6f,17), +} M.op70r8ind = op70r8ind +for k,v in pairs(op70r8ind) do + M[k] = function(late, early) + local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) } + local size = function() late,early = M.size_op(late,early) return 4 end + local bin = function() local l7801dbg=l7801dbg + local x = M.op_eval_word(late,early) + return { 0x70, v.opc, x&0xff, x>>8 } + end + table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin }) + end +end + +k = 0x89 +local op70names = {'anax','xrax','orax','addncx','gtax','subnbx','ltax','addx','onax','adcx','offax','subx','neax','sbbx','eqax'} +local op70suffixes = {'bc','de','hl','ide','ihl','dde','dhl'} +for i,o in ipairs(op70names) do + for j,s in ipairs(op70suffixes) do + local l = 0x00 + k + (j-1) + (8 * (i-1)) + M[o .. s] = function() + table.insert(M.section_current.instructions, { size=2, cycles=11, bin={ 0x70, l } }) + end + end +end + +return M