diff --git a/.gitignore b/.gitignore index 4e4d028..0a850a3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /tmp /zig-* *.bin +*.cdl *.nes *.nes.deb *.sym diff --git a/CMakeLists.txt b/CMakeLists.txt index d8761cb..03b049a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,7 +63,7 @@ else () add_definitions(-D_FILE_OFFSET_BITS=64) add_compile_options(-fno-strict-aliasing -fomit-frame-pointer -ffast-math -fvisibility=hidden) - add_compile_options(-Wsign-compare -Wno-missing-braces -Wno-unused-result -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-parentheses) + add_compile_options(-Wsign-compare -Wno-missing-braces -Wno-unused-result -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-parentheses -Wno-string-plus-int) endif() diff --git a/nes.l65 b/nes.l65 index 972e1db..ca3bfc8 100644 --- a/nes.l65 +++ b/nes.l65 @@ -1,817 +1,850 @@ --- set cpu to 6502 -cpu = require "6502" -setmetatable(_ENV, cpu) - -nes = { - OAM = 0x200, -- 0x100 bytes - RAM = 0x300, -- 0x500 bytes + ZP 0x100 bytes + Stack 0x100 bytes + OAM 0x100 bytes = 0x800 bytes - - -- 2C02 / 2C07 PPU - PPUCTRL = 0x2000, - PPUMASK = 0x2001, - PPUSTAT = 0x2002, - OAMADDR = 0x2003, - OAMDATA = 0x2004, - BGSCROL = 0x2005, - PPUADDR = 0x2006, - PPUDATA = 0x2007, - - -- 2A03 / 2A07 CPU+APU - SQ1VOL = 0x4000, - SQ1SWEEP = 0x4001, - SQ1LO = 0x4002, - SQ1HI = 0x4003, - SQ2VOL = 0x4004, - SQ2SWEEP = 0x4005, - SQ2LO = 0x4006, - SQ2HI = 0x4007, - TRILINEAR = 0x4008, - TRILO = 0x400A, - TRIHI = 0x400B, - NOISEVOL = 0x400C, - NOISELO = 0x400E, - NOISEHI = 0x400F, - DMCFREQ = 0x4010, - DMCRAW = 0x4011, - DMCSTART = 0x4012, - DMCLEN = 0x4013, - OAMDMA = 0x4014, - SNDCNT = 0x4015, - SPECIO1 = 0x4016, - SPECIO2 = 0x4017, - - SRAM = 0x6000, -- 0x2000 bytes - ROM = 0x8000, -- 0x8000 bytes - - -- PPU Memory declarations - CHAR0 = 0x0000, -- 0x1000 bytes - CHAR1 = 0x1000, -- 0x1000 bytes - SCREEN0 = 0x2000, -- 0x400 bytes - SCREEN1 = 0x2400, -- 0x400 bytes - SCREEN2 = 0x2800, -- 0x400 bytes - SCREEN3 = 0x2C00, -- 0x400 bytes - BGPAL = 0x3F00, -- 0x10 bytes - OBJPAL = 0x3F10, -- 0x10 bytes -} -do - local symbols = cpu.symbols - for k,v in pairs(nes) do symbols[k] = v end -end - --- add some symbol file formats for NES debuggers -cpu.getsym_as.mesen = function() -- .mlb - local ins,fmt = table.insert,string.format - local s = getsym(function(a,l,lorg) - if a >= 0x10000 then return end - local prefix = {} - if a < 0x2000 then prefix[1]='R' - elseif a >= 0x6000 and a < 0x8000 then prefix[1]='S' prefix[2]='W' a=a-0x6000 - elseif a >= 0x8000 then prefix[1]='P' a=(symbolsorg[lorg] or a)-0x8000 - else prefix[1]='G' end - local s = {} - for _,p in ipairs(prefix) do ins(s, fmt("%s:%04x:%s", p, a, l)) end - return s - end) - return table.concat(s, '\n') -end -cpu.getsym_as.fceux = function(filename) -- .nl, multiple files - local ins,fmt = table.insert,string.format - local ram,rom = {},{} - local s = getsym(function(a,l,lorg) - local s = fmt("$%04x#%s#", a, l) - if a < 0x8000 then ins(ram, s) - elseif a < 0x10000 then - local a_org = symbolsorg[lorg] or a - local romstart = locations[2].start -- header location should always be defined first, skip it - if a_org >= romstart then - local bank = math.floor((a_org - romstart) / 0x4000) - if not rom[bank] then rom[bank] = {} end - ins(rom[bank], s) - end - end - end) - local fn = filename - if not fn:find('%.') then fn = fn .. '.nes' end - local fni = fn .. '.ram.nl' - local f = assert(io.open(fni, "wb"), "failed to open " .. fni .. " for writing") - f:write(table.concat(ram, '\n')) f:close() - for k,v in pairs(rom) do - fni = fn .. '.' .. k .. '.nl' - f = assert(io.open(fni, "wb"), "failed to open " .. fni .. " for writing") - f:write(table.concat(v, '\n')) f:close() - end -end - -mappers = {} - -vblank_waitbegin = function() - local l=label() bit PPUSTAT bpl l -end -vblank_waitend = function() - local l=label() bit PPUSTAT bmi l -end - -ppu_addr = function(addr) - lda #addr>>8 sta PPUADDR - if addr&0xff ~= addr>>8 then lda #addr&0xff end - sta PPUADDR -end - -oam_bytes = function(t) - return { - t.y - 1, - t.tile, - (t.palette or 0)&3 | ((t.behind or t.priority==1) and 0x20 or 0) | (t.flipx and 0x40 or 0) | (t.flipy and 0x80 or 0), - t.x - } -end -oam_set = function(t) - local b = oam_bytes(t) - lda #t[1]*4 sta OAMADDR - for _,v in ipairs(b) do lda #v sta OAMDATA end -end - -oamcache = OAM -- change it to set other location -oamcache_clear = function() - local oam = oamcache - ldx #0 lda #0xff - local l=label() sta oam,x inx inx inx inx bne l -end -oamcache_flush = function() - local oam = oamcache - lda #0 sta OAMADDR lda #oam>>8 sta OAMDMA -end -oamcache_set = function(t) - local oam = oamcache - local b = oam_bytes(t) - ldx #t[1]*4 lda #b[1] sta oam,x - inx lda #b[2] sta oam,x - inx lda #b[3] sta oam,x - inx lda #b[4] sta oam,x -end - ---[[ button state: - bit: 7 6 5 4 3 2 1 0 - button: A B Select Start Up Down Left Right - https://wiki.nesdev.com/w/index.php/Controller_Reading -]] --- fast reading of just the A button --- return C if A is pressed, c otherwise -read_joy_a = function(joy_index) - local joy = joy_index == 2 and SPECIO2 or SPECIO1 - lda #1 sta joy lsr sta joy lda joy lsr -end --- read one joypad state into dst -read_joy = function(dst, joy_index) - local joy = joy_index == 2 and SPECIO2 or SPECIO1 - lda #1 sta joy sta dst lsr sta joy - @_readbutton lda joy lsr rol dst bcc _readbutton -end --- read both joypad states and Famicom's DA15 expansion port -read_joys = function(dst1, dst2) - lda #1 sta SPECIO1 sta dst2 lsr sta SPECIO1 -@_readbuttons - lda SPECIO1 and #3 cmp #1 rol dst1 - lda SPECIO2 and #3 cmp #1 rol dst2 - bcc _readbuttons -end --- read both joypad states on even cycles only, to safely work with DPCM --- must be called right after oamcache_flush or any other sta OAMDMA -read_joys_even = function(dst1, dst2) - ldx #1 stx dst1 stx SPECIO1 dex stx SPECIO1 -@_readbuttons - lda SPECIO2 and #3 cmp #1 rol dst2,x - lda SPECIO1 and #3 cmp #1 rol dst1 - bcc _readbuttons -end - -init = function() - sei -- cld not needed, no BCD support - ldx #0x40 stx SPECIO2 -- disable APU frame IRQ - ldx #0xff txs inx stx PPUCTRL stx PPUMASK stx DMCFREQ -- disable NMI, rendering, DMC IRQs - bit PPUSTAT -- clear remnant VBlank PPU status flag on reset - vblank_waitbegin() - lda #0 sta SNDCNT -- stop APU channels - -- clear CPU RAM -@_zeroram - sta 0x0000,x sta 0x0100,x sta 0x0200,x sta 0x0300,x - sta 0x0400,x sta 0x0500,x sta 0x0600,x sta 0x0700,x - inx bne _zeroram - vblank_waitbegin() - -- clear OAM - oamcache_clear() oamcache_flush() - -- clear PPU RAM - bit PPUSTAT ppu_addr(0x2000) tax ldy #0x10 -@_zeroppu - sta PPUDATA dex bne _zeroppu dey bne _zeroppu - bit PPUSTAT -- reset latch - if mappers.init then mappers.init() end -end - --- NES 2.0 (backward compatible with iNES) --- https://wiki.nesdev.com/w/index.php/NES_2.0 -header = function(t) - if not t then t = {} end - local logsz = function(sz) - assert(sz >= 0 and sz <= 1048576, "invalid size: " .. sz .. ", expected [0, 1048576]") - if sz < 1 then return 0 end - if sz <= 128 then return 1 end - return math.ceil(math.log(sz/64, 2)) - end - -- mapper - local mi1 = t.mapperid or 0 - assert(mi1 >= 0 and mi1 < 4096, "invalid mapper id: " .. mi1 .. ", expected [0, 4095]") - local ms1 = t.submapperid or 0 - assert(ms1 >= 0 and ms1 < 16, "invalid submapper id: " .. ms1 .. ", expected [0, 15]") - local mapper6 = (mi1 & 0xf) << 4 - local mapper7 = mi1 & 0xf0 - local mapper8 = (mi1 >> 8) | (ms1 << 4) - -- prgsize - local prgsize = math.tointeger((t.prgsize or 16384) / 16384) - assert(prgsize, "prgsize must be a multiple of 16384") - -- chrsize - local chrsize = math.tointeger((t.chrsize or 0) / 8192) - assert(chrsize, "chrsize must be a multiple of 8192") - -- wramsize (not battery-backed) - local wramsize = logsz(t.wramsize or 0) - -- bramsize (battery-backed) - local bramsize = logsz(t.bramsize or 0) - -- chrbramsize (battery-backed) - local chrbramsize = logsz(t.chrbramsize or 0) - -- chrramsize (not battery-backed) - local chrramsize = logsz(t.chrramsize or (chrbramsize==0 and chrsize==0 and 8192 or 0)) - local battery_bit = bramsize == 0 and chrbramsize == 0 and 0 or 2 - -- mirror: 'H' for horizontal mirroring, 'V' for vertical mirroring - -- '4' for four-screen VRAM, 218 for four-screen and vertical - local mirror = (t.mirror or 'h'):lower() - mirror = ({ h=0, v=1, ['4']=8, [218]=9 })[mirror] - assert(mirror, "invalid mirror mode: " .. mirror .. ", expected 'H', 'V', '4', or 218") - -- tv: 'N' for NTSC, 'P' for PAL, 'NP' for both preferring NTSC, 'PN' for both preferring PAL - local tv, tvm = 0, (t.tv or 'n'):lower() - assert(tvm=='n' or tvm=='p' or tvm=='np' or tvm=='pn', "invalid tv mode: " .. tostring(t.tv) .. ", expected 'N', 'P', 'NP' or 'PN'") - if tvm[1] == 'p' then tv = 1 end - if #tvm > 1 then tv = tv + 2 end - - @@header -- size: 16 bytes - dc.b 0x4e, 0x45, 0x53 -- 'NES' - dc.b 0x1a - dc.b prgsize, chrsize - dc.b mapper6 | mirror | battery_bit - dc.b mapper7 | 8 - dc.b mapper8 - dc.b ((chrsize >> 4) & 0xF0) | ((prgsize >> 8) & 0x0F) - dc.b (bramsize << 4) | wramsize - dc.b (chrbramsize << 4) | chrramsize - dc.b tv, 0, 0, 0 - - -- update table with defaulted values - t.prgsize = prgsize * 16384 - t.chrsize = chrsize * 8192 - t.wramsize = math.tointeger(2^wramsize*64) - t.bramsize = math.tointeger(2^bramsize*64) - t.chrbramsize = math.tointeger(2^chrbramsize*64) - t.chrramsize = math.tointeger(2^chrramsize*64) - mappers.header=t -end - -local n0ne = function(x) return not x or x == 0 end -local val0 = function(x) return x and x or 0 end - ---[[ For all mappers, where relevant: - -prg rom banks: - prgroms are numbered from last (0) to first (#-1), so that adding more does not change - prgrom0, which must contain the reset vector (main). The switchprgrom* functions expect - a bank index according to this reversed numbering (0 is last physical bank in ROM). - - t.prgmap is an optional function taking a prgrom bank index and returning its rorg value. - Default is to map banks in sequence according to their size, eg.: - * for sizes 32kB: always 0x8000. - * for sizes 16kB+16kB: repeat sequence 0x8000, 0xa000. - * for sizes 16kB+8kB+8kB: repeat sequence 0x8000, 0xc000, 0xe000. - * for sizes 8kB+8kB+8kB+8kB: repeat sequence 0x8000, 0xa000, 0xc000, 0xe000. - If last bank is fixed, it is not part of the sequence and its mapping value is skipped: - * for sizes 8kB+8kB+ last fixed 16kB: repeat sequence 0x8000, 0xa000, set last bank to 0xc000. - -chr rom banks: - chrroms are numbered in physical ROM address order, unlike prgroms. - - Banks are created with the smallest switchable size, maximum being 4kB by default. - t.chrmap is an optional function taking a chrrom bank index and returning its offset in - ROM from the beginning of the first chrrom, its size (defaults to everything remaining), - and its relocation origin (defaults to 0). - So if a mapper allows switching chrroms in any of 1-1-1-1-1-1-1-1kB / 2-2-2-2kB, etc., - the default is to create only 1kB chrroms. Conversely, with a mapper switching only in 8kB - slices, 4kB chrrom are created, not 8kB (but you can specify any size using t.chrmap). -]] - ---[[ - https://wiki.nesdev.com/w/index.php/NROM -]] -mappers.NROM = function(t) - if not t then t = {} end - if not t.prgsize then t.prgsize = 16384 end - assert(t.prgsize == 16384 or t.prgsize == 32768, "prgsize must be 16 or 32kB") - if n0ne(t.chrsize) and n0ne(t.chrramsize) and n0ne(t.chrbramsize) then t.chrsize = 8192 end - assert(val0(t.chrsize) + val0(t.chrramsize) + val0(t.chrbramsize) == 8192, "combined chrrom size must be 8kB") - assert(not t.mirror or ({ h=1, v=1 })[t.mirror:lower()], "only H and V mirroring are supported") - local prgstart = 0x10000 - t.prgsize - hdrrom = location{prgstart - 16, prgstart - 1, name='header'} - header(t) - prgrom0 = location{prgstart, 0xffff, name='prgrom'} - prgrom = prgrom0 - section{"vectors", org=0xfffa} dc.w nmi, main, irq - if (t.chrsize > 0) then - local ci = 0 - local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end - repeat - local off, sz, rorg = chrmap(ci) - sz = sz or t.chrsize - off - local o = off + 0x10000 - _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} - ci = ci+1 - until off + sz >= t.chrsize - chrrom = chrrom0 - end -end -mappers[0] = mappers.NROM - ---[[ - https://wiki.nesdev.com/w/index.php/UxROM - Has bus conflicts. -]] -mappers.UxROM = function(t) - if not t then t = {} end - t.mapperid = 2 - if not t.prgsize then t.prgsize = 32768 end - assert(t.prgsize >= 0x8000 and t.prgsize <= 0x40000, "prgsize must be at least 32kB and at most 256kB") - if not t.chrsize then t.chrsize = 8192 end - assert(t.chrsize == 0x2000, "chrsize must be 8kB") - assert(not t.mirror or ({ h=1, v=1 })[t.mirror:lower()], "only H and V mirroring are supported") - hdrrom = location{0x7ff0, 0x7fff, name='header'} - header(t) - local bc = t.prgsize//0x4000 - for bi=0,bc-2 do - local o,ix = 0x8000 + bi*0x4000, bc-1-bi - _ENV['prgrom'..ix] = location{o, o+0x3fff, rorg=0x8000, name='prgrom'..ix} - end - local prglast = 0x8000 + (bc-1)*0x4000 - prgrom0 = location{prglast, prglast+0x3fff, rorg=0xc000, name='prgrom0'} - prgrom = prgrom0 - section{"vectors", org=prglast+0x3ffa} dc.w nmi, main, irq - @@bankbytes -- for handling bus conflicts - samepage for i=bc-1,0,-1 do byte(i) end end - local ci, chrstart = 0, 0x8000 + bc*0x4000 - local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end - repeat - local off, sz, rorg = chrmap(ci) - sz = sz or t.chrsize - off - local o = off + chrstart - _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} - ci = ci+1 - until off + sz >= t.chrsize - chrrom = chrrom0 - function switchprgrom(bankix) - if bankix then assert(bankix < bc-1) lda #bankix end - -- lda to reverse map [n..0] to [0..n] - tax lda bankbytes,x sta bankbytes,x - end - mappers.init = function() - switchprgrom(1) - end -end -mappers[2] = mappers.UxROM - ---[[ - https://wiki.nesdev.com/w/index.php/CNROM - Has bus conflicts. -]] -mappers.CNROM = function(t) - if not t then t = {} end - t.mapperid = 3 - if not t.prgsize then t.prgsize = 16384 end - assert(t.prgsize == 16384 or t.prgsize == 32768, "prgsize must be 16 or 32kB") - if not t.chrsize then t.chrsize = 8192 end - assert(t.chrsize >= 0x2000 and t.chrsize <= 0x8000, "chrsize must be at least 8kB and at most 32kB") - assert(not t.mirror or ({ h=1, v=1 })[t.mirror:lower()], "only H and V mirroring are supported") - local prgstart = 0x10000 - t.prgsize - hdrrom = location{prgstart - 16, prgstart - 1, name='header'} - header(t) - prgrom = location{prgstart, 0xffff, name='prgrom'} - section{"vectors", org=0xfffa} dc.w nmi, main, irq - @@bankbytes samepage -- for handling bus conflicts - dc.b 0x00, 0x01, 0x02, 0x03 - dc.b 0x10, 0x11, 0x12, 0x13 - dc.b 0x20, 0x21, 0x22, 0x23 - dc.b 0x30, 0x31, 0x32, 0x33 - end - local ci, cc = 0, t.chrsize//0x2000 - local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end - repeat - local off, sz, rorg = chrmap(ci) - sz = sz or t.chrsize - off - local o = off + 0x10000 - _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} - ci = ci+1 - until off + sz >= t.chrsize - chrrom = chrrom0 - securitydiodes = 0 -- set to actual value, eg 0x20 - function switchchrrom(bankix) - assert(securitydiodes < 0x40) - if bankix then - assert(bankix < cc) - ldx #bankix|securitydiodes>>2 lda #bankix|securitydiodes - else - ora #securitydiodes>>2 tax and #3 ora #securitydiodes - end - sta bankbytes,x - end - mappers.init = function() - switchchrrom(0) - end -end -mappers[3] = mappers.CNROM - ---[[ - https://wiki.nesdev.com/w/index.php/GxROM - Has bus conflicts. -]] -mappers.GxROM = function(t) - if not t then t = {} end - t.mapperid = 66 - if not t.prgsize then t.prgsize = 32768 end - assert(t.prgsize >= 0x8000 and t.prgsize <= 0x20000, "prgsize must be at least 32kB and at most 128kB") - if not t.chrsize then t.chrsize = 8192 end - assert(t.chrsize >= 0x2000 and t.chrsize <= 0x8000, "chrsize must be at least 8kB and at most 32kB") - assert(not t.mirror or ({ h=1, v=1 })[t.mirror:lower()], "only H and V mirroring are supported") - hdrrom = location{0x7ff0, 0x7fff, name='header'} - header(t) - local bc = t.prgsize//0x8000 - local cc = t.chrsize//0x2000 - local ci, chrstart = 0, 0x8000 + bc*0x8000 - local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end - repeat - local off, sz, rorg = chrmap(ci) - sz = sz or t.chrsize - off - local o = off + chrstart - _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} - ci = ci+1 - until off + sz >= t.chrsize - chrrom = chrrom0 - -- RAM address of bank register copy, to switch using A instead of immediate - -- if bankregister_shadow is negative, only immediate bankswitching is available - bankregister_shadow = -1 - -- otherwise, just set the value directly: xxPPxxCC in A, xxxxPPCC in X with sta bankbytes0,x - function switchroms(prgbankix, chrbankix) - if prgbankix then - assert(prgbankix < bc) prgbankix = bc-1-prgbankix - assert(chrbankix, "GxROM must specify both PRG and CHR roms to switch simultaneously") - assert(chrbankix < cc) - local br = prgbankix<<4 | chrbankix - ldx #br&3|br>>2 lda #br sta bankbytes0,x - else -- A contains 00PP00CC - if bankregister_shadow >= 0 then - sta bankregister_shadow -- bankregister_shadow = 00PP00CC - lsr lsr -- A = 0000PP00, c - -- compute bc-1-prgbankix (reverse indexing) - eor #0xff adc #(bc-1<<2)+1 -- negate and +bc-1 combined, NN = bc-1-PP: A = 0000NN00 - ora bankregister_shadow ;and #0xf tax -- X = 0000NNCC - else -- use stack instead - tsx sta 0x100,x lsr lsr -- A = 0000PP00, s[0] = 00PP00CC, c - -- compute bc-1-prgbankix (reverse indexing) - eor #0xff adc #(bc-1<<2)+1 -- negate and +bc-1 combined, NN = bc-1-PP: A = 0000NN00 - ora 0x100,x ;and #0xf tax -- X = 0000NNCC - end - lda bankbytes0,x sta bankbytes0,x -- A = 00NN00CC, from table - end - end - for bi=0,bc-1 do - local o,ix = 0x8000 + bi*0x8000, bc-1-bi - _ENV['prgrom'..ix] = location{o, o+0x7fff, rorg=0x8000, name='prgrom'..ix} - local start=section{"entry"..ix, org=o+0x8000-6-16-10} switchroms(0,0) if ix==0 then jmp main end - section{"vectors"..ix, org=o+0x8000-6} dc.w "nmi"..ix, start, "irq"..ix - section{"bankbytes"..ix, org=o+0x8000-6-16} samepage -- for handling bus conflicts - dc.b 0x00, 0x01, 0x02, 0x03 - dc.b 0x10, 0x11, 0x12, 0x13 - dc.b 0x20, 0x21, 0x22, 0x23 - dc.b 0x30, 0x31, 0x32, 0x33 - end - end - prgrom = prgrom0 - function switchprgrom(bankix) - if bankix then - assert(bankix < bc) - lda #bc-1-bankix - else - clc eor #0xff adc #bc -- bc-1 - A - end - assert(bankregister_shadow >= 0, "no RAM slot assigned to bankregister_shadow") - tay lda bankregister_shadow ;and #3 sta bankregister_shadow - tya asl asl ora bankregister_shadow tax - lda bankbytes0,x sta bankregister_shadow sta bankbytes0,x - end - function switchchrrom(bankix) - if bankix then - assert(bankix < cc) - lda #bankix - end - assert(bankregister_shadow >= 0, "no RAM slot assigned to bankregister_shadow") - tay lda bankregister_shadow lsr lsr sta bankregister_shadow - tya ora bankregister_shadow tax - lda bankbytes0,x sta bankregister_shadow sta bankbytes0,x - end -end -mappers[66] = mappers.GxROM - ---[[ - https://wiki.nesdev.com/w/index.php/MMC1 - - t.prgmap is an optional function taking a prgrom bank index and returning its rorg value. - Default is to create 16kB banks if prgswitchmode is not all, 32kB otherwise, and rorg them - accordingly. - Same goes for chrrom sizes. - - t.prgswitchmode: - 'first' makes 0x8000-0xbfff switchable (default) - 'last' makes 0xc000-0xffff switchable - 'all' makes 0x8000-0xffff switchable - t.chrswitchmode: - 'all' switches whole 8kB at a time - 'half' switches 2 separate 4kB banks (default) -]] -mappers.MMC1 = function(t) - if not t then t = {} end - t.mapperid = 1 - if not t.prgswitchmode then t.prgswitchmode = 'first' end - if not t.chrswitchmode then t.chrswitchmode = 'half' end - if not t.wramsize then t.wramsize = 0 end - if not t.bramsize and t.wramsize == 0 then t.bramsize = 8192 end - local prgram = t.bramsize + t.wramsize - assert(prgram >= 0x2000 and prgram <= 0x8000, "bramsize or wramsize must be at least 8kB and at most 32kB") - if not t.prgsize then t.prgsize = 32768 end - assert(t.prgsize >= 0x8000 and t.prgsize <= 0x80000, "prgsize must be at least 32kB and at most 512kB") - if not t.chrsize then t.chrsize = 8192 end - assert(t.chrsize >= 0x2000 and t.chrsize <= 0x20000, "chrsize must be at least 8kB and at most 128kB") - hdrrom = location{0x7FF0, 0x7FFF, name='header'} - header(t) - local bsz = t.prgswitchmode=='all' and 0x8000 or 0x4000 - local prgmap = t.prgmap or function(bi, bc) return t.prgswitchmode=='last' and 0xc000 or 0x8000 end - local bc = t.prgsize//bsz - for bi=0,bc-1 do - local o,ix = 0x8000 + bi*bsz, bc-1-bi - _ENV['prgrom'..ix] = location{o, o+bsz-1, rorg=prgmap(ix,bc), name='prgrom'..ix} - end - section{"vectors", org=0x8000+bc*bsz-6} dc.w nmi, main, irq - prgrom = prgrom0 - local ci, chrstart = 0, 0x8000 + bc*bsz - local csz = t.chrswitchmode=='all' and 0x2000 or 0x1000 - local cc = t.chrsize//csz - local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end - repeat - local off, sz, rorg = chrmap(ci) - sz = sz or t.chrsize - off - local o = off + chrstart - _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} - ci = ci+1 - until off + sz >= t.chrsize - chrrom = chrrom0 - - local prgswitchmodemap = { all=0, first=3<<2, last=2<<2 } - local chrswitchmodemap = { all=0, half=1<<4 } - mmc1ctrl = (mappers.header.mirror==1 and 2 or 3) | prgswitchmodemap[t.prgswitchmode] | chrswitchmodemap[t.chrswitchmode] - function mmc1write(reg) - sta reg lsr sta reg lsr sta reg lsr sta reg lsr sta reg - end - -- Can be turned into a function, A must contain the bank index. - -- eg: - -- @@switchprgrom_func switchprgrom_f() - -- switchprgrom_f = function() jsr switchprgrom_func rts end - function switchprgrom_f() mmc1write(0xe000) end - function switchprgrom(bankix) - assert(bankix < t.prgsize//0x4000) - lda #bankix - switchprgrom_f() - end - function switchchrrom_f(slot) assert(slot<2) mmc1write(slot==0 and 0xa000 or 0xc000) end - function switchchrrom(bankix, slot) - assert(bankix < t.chrsize//0x1000) - lda #bankix - switchchrrom_f(slot or 0) - end - function setmirror(mirror) - mirror = assert(({ h=3, v=2, hi=1, lo=0 })[mirror:lower()]) - mmc1ctrl = (mmc1ctrl & ~3) | mirror - lda #mmc1ctrl mmc1write(0x8000) - end - function setprgswitchmode(mode) - mode = assert(prgswitchmodemap[mode]) - mmc1ctrl = (mmc1ctrl & ~0xc) | mode - lda #mmc1ctrl mmc1write(0x8000) - end - function setchrswitchmode(mode) - mode = assert(chrswitchmodemap[mode]) - mmc1ctrl = (mmc1ctrl & ~0x10) | mode - lda #mmc1ctrl mmc1write(0x8000) - end - mappers.init = function() - lda #0x80 sta 0x8000 - lda #mmc1ctrl mmc1write(0x8000) - if t.prgswitchmode ~= 'all' then switchprgrom(1) end - switchchrrom(0) if t.chrswitchmode ~= 'all' then switchchrrom(1,1) end - end -end -mappers[1] = mappers.MMC1 - ---[[ - https://wiki.nesdev.com/w/index.php/MMC3 - - prgroms are numbered from last (0) to first (#-1), so that adding more does not change - prgrom0, which must contain the reset vector (main). - Last 2 prg banks are merged into 1 16kB bank, to allow linker optimization - hence, 0 must - always be set on bit 6 of bank select, even; also, there is no prgrom1 as a consequence. - - chrroms are all 1kB, so they can work with chr A12 inversion enabled or not. - - With default submapper id of 0, this defaults to revision MM3C, which generates a scanline - interrupt at each scanline when counter is loaded with 0. -]] -mappers.MMC3 = function(t) - if not t then t = {} end - t.mapperid = 4 - if not t.bramsize then t.bramsize = 8192 end - assert(t.bramsize == 8192, "bramsize must be 8kB") - if not t.prgsize then t.prgsize = 32768 end - assert(t.prgsize >= 0x8000 and t.prgsize <= 0x80000, "prgsize must be at least 32kB and at most 512kB") - if not t.chrsize then t.chrsize = 8192 end - assert(t.chrsize >= 0x2000 and t.chrsize <= 0x40000, "chrsize must be at least 8kB and at most 256kB") - hdrrom = location{0x7FF0, 0x7FFF, name='header'} - header(t) - local prgmap = t.prgmap or function(bi, bc) return 0x8000+(bi&1)*0x2000 end - local bc = t.prgsize//0x2000 - for bi=0,bc-3 do - local o,ix = 0x8000 + bi*0x2000, bc-bi-1 - _ENV['prgrom'..ix] = location{o, o+0x1fff, rorg=prgmap(ix,bc), name='prgrom'..ix} - end - do - local o = 0x8000 + (bc-2)*0x2000 - prgrom0 = location{o, o+0x3fff, rorg=0xc000, name='prgrom0'} - section{"vectors", org=o+0x3ffa} dc.w nmi, main, irq - end - prgrom = prgrom0 - local ci, chrstart = 0, 0x8000 + bc*0x2000 - local chrmap = t.chrmap or function(ci) return ci*0x400, 0x400, (ci&7)*0x400 end - local cc = t.chrsize//0x400 - repeat - local off, sz, rorg = chrmap(ci) - sz = sz or t.chrsize - off - local o = off + chrstart - _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} - ci = ci+1 - until off + sz >= t.chrsize - chrrom = chrrom0 - local a12inv = false - function seta12inv(enabled) a12inv = enabled end - function switchprgrom(bankix, slot) - assert(slot<2) - slot = slot + 6 - if a12inv then slot = slot | 0x80 end - lda #slot sta 0x8000 - assert(bankix < bc) - bankix = bc-1-bankix -- reverse index order, since we set 0 as last - lda #bankix sta 0x8001 - end - -- slot [0, 7], each slot is 1kB counting in order, regardless of a12inv state - function switchchrrom(bankix, slot) - assert(slot<8) - if a12inv then - assert(slot ~= 5 and slot ~= 7) - if slot == 6 then slot = 1 - elseif slot == 4 then slot = 0 - else slot = slot + 2 end - slot = slot | 0x80 - else - assert(slot ~= 1 and slot ~= 3) - if slot == 2 then slot = 1 - elseif slot > 3 then slot = slot - 2 end - end - lda #slot sta 0x8000 - assert(bankix < cc) - lda #bankix sta 0x8001 - end - function setmirror(mirror) - mirror = assert(({ h=1, v=0 })[mirror:lower()]) - lda #mirror sta 0xa000 - end - function protectsram() lda 0x40 sta 0xa001 end - function scanlineirq(count) ldx #1 stx 0xe000 lda #count-1 sta 0xc000 sta 0xc001 stx 0xe001 end - mappers.init = function() - switchprgrom(2, 0) switchprgrom(3, 1) - switchchrrom(0, 0) switchchrrom(2, 2) - switchchrrom(4, 4) switchchrrom(5, 5) switchchrrom(6, 6) switchchrrom(7, 7) - local mirror = mappers.header.mirror - if mirror==0 or mirror==1 then lda #mirror~1 sta 0xa000 end - lda #0x80 sta 0xa001 - end -end -mappers[4] = mappers.MMC3 - ---[[ - https://wiki.nesdev.com/w/index.php/MMC5 - https://forums.nesdev.com/viewtopic.php?f=9&t=16789 - - main MUST be in 0xe000-0xfff9 of last prgrom. - prgroms are numbered from last (0) to first (#-1), so that adding more does not change - prgrom0, which must contain the reset vector (main). - - chrroms are all 1kB. -]] -mappers.MMC5 = function(t) - if not t then t = {} end - t.mapperid = 5 - assert(val0(t.bramsize) + val0(t.wramsize) <= 0x10000, "bramsize + wramsize must be at most 64kB") - if not t.prgsize then t.prgsize = 8192 end - assert(t.prgsize <= 0x100000, "prgsize must be at most 1MB") - if not t.chrsize then t.chrsize = 8192 end - assert(t.chrsize <= 0x100000, "chrsize must be at most 1MB") - hdrrom = location{0x7FF0, 0x7FFF, name='header'} - header(t) - local prgmap = t.prgmap or function(bi, bc) return 0x8000+(bi&3)*0x2000 end - local bc = t.prgsize//0x2000 - for bi=0,bc-2 do - local o,ix = 0x8000 + bi*0x2000, bc-bi-1 - _ENV['prgrom'..ix] = location{o, o+0x1fff, rorg=prgmap(ix,bc), name='prgrom'..ix} - end - do - local o = 0x8000 + (bc-1)*0x2000 - prgrom0 = location{o, o+0x1fff, rorg=0xe000, name='prgrom0'} - section{"vectors", org=o+0x1ffa} dc.w nmi, main, irq - section{"main", org=o} -- enforce entry point in last bank - end - prgrom = prgrom0 - local ci, chrstart = 0, 0x8000 + bc*0x2000 - local chrmap = t.chrmap or function(ci) return ci*0x400, 0x400, (ci&7)*0x400 end - local cc = t.chrsize//0x400 - repeat - local off, sz, rorg = chrmap(ci) - sz = sz or t.chrsize - off - local o = off + chrstart - _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} - ci = ci+1 - until off + sz >= t.chrsize - chrrom = chrrom0 - function prgbankmode(mode) if mode then lda #mode end sta 0x5100 end - function chrbankmode(mode) if mode then lda #mode end sta 0x5101 end - function wrammode(mode) if mode then lda #mode end sta 0x5104 end - function protectsram() lda #0 sta 0x5102 sta 0x5103 end - function screenmap(map) if map then lda #map end sta 0x5105 end - function filltile(tile) if tile then lda #tile end sta 0x5106 end - function fillcolor(col) if col then lda #col end sta 0x5107 end - function switchprgram(chip, bank) - assert(chip == 0 or chip == 1) - assert(bank < 4) - lda #chip<<2|bank sta 0x5113 - end - function switchprgrom0(bank, isram) lda #bank|(isram and 0 or 0x80) sta 0x5114 end - function switchprgrom1(bank, isram) lda #bank|(isram and 0 or 0x80) sta 0x5115 end - function switchprgrom2(bank, isram) lda #bank|(isram and 0 or 0x80) sta 0x5116 end - function switchprgrom3(bank, isram) lda #bank|(isram and 0 or 0x80) sta 0x5117 end - function switchchrrom(bank, slot) -- 1kB mode - assert(slot < 12) - assert(bank < cc) - lda #bank>>7 sta 0x5130 - lda #bank sta slot+0x5120 - end - vsplitmode = 0 - function vsplitstart(starttile) - assert(starttile < 32) - vsplitmode = (vsplitmode & ~31) | starttile - lda #vsplitmode sta 0x5200 - end - function vsplitside(side) - assert(side == 'left' or side == 'right') - vsplitmode = (vsplitmode & ~0x40) | (side == 'right' and 1 or 0) - lda #vsplitmode sta 0x5200 - end - function vsplitenable(enabled) - vsplitmode = (vsplitmode & ~0x80) | (enabled and 1 or 0) - lda #vsplitmode sta 0x5200 - end - function vsplitscroll(vscroll) if vscroll then lda #vscroll end sta 0x5201 end - function vsplitbank(chrbank) if chrbank then lda #chrbank end sta 0x5202 end - function scanlineirq(atscanline) lda #0 sta 0x5204 lda #atscanline sta 0x5203 lda #0x80 sta 0x5204 end - function irqenable(enabled) lda #enabled and 0x80 or 0 sta 0x5204 end - -- 0x40 set: PPU is rendering visible scanlines - -- 0x80 set: a scanlineirq is pending (internal counter reach atscanline value set with scanlineirq()) - function irqstatus() lda 0x5204 end - -- multiplies a*x = x:a (x hi, a lo) - function mul() sta 0x5205 stx 0x5206 lda 0x5205 ldx 0x5206 end - mappers.init = function() - ldx #2 stx 0x5102 dex stx 0x5103 - lda #vsplitmode sta 0x5200 - prgbankmode(2) - switchprgrom1(3) -- map prg rom 3, 2, x, 0 - switchprgrom2(1) -- map prg rom 3, 2, 1, 0 - chrbankmode(0) switchchrrom(0, 7) chrbankmode(3) -- map chr rom 0 to the 8kB, and set mode to 1kB slices - end -end -mappers[5] = mappers.MMC5 +-- set cpu to 6502 +cpu = require "6502" +setmetatable(_ENV, cpu) + +nes = { + OAM = 0x200, -- 0x100 bytes + RAM = 0x300, -- 0x500 bytes + ZP 0x100 bytes + Stack 0x100 bytes + OAM 0x100 bytes = 0x800 bytes + + -- 2C02 / 2C07 PPU + PPUCTRL = 0x2000, + PPUMASK = 0x2001, + PPUSTAT = 0x2002, + OAMADDR = 0x2003, + OAMDATA = 0x2004, + BGSCROL = 0x2005, + PPUADDR = 0x2006, + PPUDATA = 0x2007, + + -- 2A03 / 2A07 CPU+APU + SQ1VOL = 0x4000, + SQ1SWEEP = 0x4001, + SQ1LO = 0x4002, + SQ1HI = 0x4003, + SQ2VOL = 0x4004, + SQ2SWEEP = 0x4005, + SQ2LO = 0x4006, + SQ2HI = 0x4007, + TRILINEAR = 0x4008, + TRILO = 0x400A, + TRIHI = 0x400B, + NOISEVOL = 0x400C, + NOISELO = 0x400E, + NOISEHI = 0x400F, + DMCFREQ = 0x4010, + DMCRAW = 0x4011, + DMCSTART = 0x4012, + DMCLEN = 0x4013, + OAMDMA = 0x4014, + SNDCNT = 0x4015, + SPECIO1 = 0x4016, + SPECIO2 = 0x4017, + + SRAM = 0x6000, -- 0x2000 bytes + ROM = 0x8000, -- 0x8000 bytes + + -- PPU Memory declarations + CHAR0 = 0x0000, -- 0x1000 bytes + CHAR1 = 0x1000, -- 0x1000 bytes + SCREEN0 = 0x2000, -- 0x400 bytes + SCREEN1 = 0x2400, -- 0x400 bytes + SCREEN2 = 0x2800, -- 0x400 bytes + SCREEN3 = 0x2C00, -- 0x400 bytes + BGPAL = 0x3F00, -- 0x10 bytes + OBJPAL = 0x3F10, -- 0x10 bytes +} +do + local symbols = cpu.symbols + for k,v in pairs(nes) do symbols[k] = v end +end + +NTSC = { + CLOCK = 1789773 +} +PAL = { + CLOCK = 1662607 +} + +-- add some symbol file formats for NES debuggers +cpu.getsym_as.mesen = function() -- .mlb + local ins,fmt = table.insert,string.format + local s = getsym(function(a,l,lorg) + if a >= 0x10000 then return end + local prefix = {} + if a < 0x2000 then prefix[1]='R' + elseif a >= 0x6000 and a < 0x8000 then prefix[1]='S' prefix[2]='W' a=a-0x6000 + elseif a >= 0x8000 then prefix[1]='P' a=(symbolsorg[lorg] or a)-0x8000 + else prefix[1]='G' end + local s = {} + for _,p in ipairs(prefix) do ins(s, fmt("%s:%04x:%s", p, a, l)) end + return s + end) + return table.concat(s, '\n') +end +cpu.getsym_as.fceux = function(filename) -- .nl, multiple files + local ins,fmt = table.insert,string.format + local ram,rom = {},{} + local s = getsym(function(a,l,lorg) + local s = fmt("$%04x#%s#", a, l) + if a < 0x8000 then ins(ram, s) + elseif a < 0x10000 then + local a_org = symbolsorg[lorg] or a + local romstart = locations[2].start -- header location should always be defined first, skip it + if a_org >= romstart then + local bank = math.floor((a_org - romstart) / 0x4000) + if not rom[bank] then rom[bank] = {} end + ins(rom[bank], s) + end + end + end) + local fn = filename + if not fn:find('%.') then fn = fn .. '.nes' end + local fni = fn .. '.ram.nl' + local f = assert(io.open(fni, "wb"), "failed to open " .. fni .. " for writing") + f:write(table.concat(ram, '\n')) f:close() + for k,v in pairs(rom) do + fni = fn .. '.' .. k .. '.nl' + f = assert(io.open(fni, "wb"), "failed to open " .. fni .. " for writing") + f:write(table.concat(v, '\n')) f:close() + end +end + +mappers = {} + +vblank_waitbegin = function() + local l=label() bit PPUSTAT bpl l +end +vblank_waitend = function() + local l=label() bit PPUSTAT bmi l +end + +ppu_addr = function(addr) + lda #addr>>8 sta PPUADDR + if addr&0xff ~= addr>>8 then lda #addr&0xff end + sta PPUADDR +end + +oam_bytes = function(t) + return { + t.y - 1, + t.tile, + (t.palette or 0)&3 | ((t.behind or t.priority==1) and 0x20 or 0) | (t.flipx and 0x40 or 0) | (t.flipy and 0x80 or 0), + t.x + } +end +oam_set = function(t) + local b = oam_bytes(t) + lda #t[1]*4 sta OAMADDR + for _,v in ipairs(b) do lda #v sta OAMDATA end +end + +oamcache = OAM -- change it to set other location +oamcache_clear = function() + local oam = oamcache + ldx #0 lda #0xff + local l=label() sta oam,x inx inx inx inx bne l +end +oamcache_flush = function() + local oam = oamcache + lda #0 sta OAMADDR lda #oam>>8 sta OAMDMA +end +oamcache_set = function(t) + local oam = oamcache + local b = oam_bytes(t) + ldx #t[1]*4 lda #b[1] sta oam,x + inx lda #b[2] sta oam,x + inx lda #b[3] sta oam,x + inx lda #b[4] sta oam,x +end + +--[[ button state: + bit: 7 6 5 4 3 2 1 0 + button: A B Select Start Up Down Left Right + https://wiki.nesdev.com/w/index.php/Controller_Reading +]] +-- fast reading of just the A button +-- return C if A is pressed, c otherwise +read_joy_a = function(joy_index) + local joy = joy_index == 2 and SPECIO2 or SPECIO1 + lda #1 sta joy lsr sta joy lda joy lsr +end +-- read one joypad state into dst +read_joy = function(dst, joy_index) + local joy = joy_index == 2 and SPECIO2 or SPECIO1 + lda #1 sta joy sta dst lsr sta joy + @_readbutton lda joy lsr rol dst bcc _readbutton +end +-- read both joypad states and Famicom's DA15 expansion port +read_joys = function(dst1, dst2) + lda #1 sta SPECIO1 sta dst2 lsr sta SPECIO1 +@_readbuttons + lda SPECIO1 and #3 cmp #1 rol dst1 + lda SPECIO2 and #3 cmp #1 rol dst2 + bcc _readbuttons +end +-- read both joypad states on even cycles only, to safely work with DPCM +-- must be called right after oamcache_flush or any other sta OAMDMA +read_joys_even = function(dst1, dst2) + ldx #1 stx dst1 stx SPECIO1 dex stx SPECIO1 +@_readbuttons + lda SPECIO2 and #3 cmp #1 rol dst2,x + lda SPECIO1 and #3 cmp #1 rol dst1 + bcc _readbuttons +end + +init = function() + sei -- cld not needed, no BCD support + ldx #0x40 stx SPECIO2 -- disable APU frame IRQ + ldx #0xff txs inx stx PPUCTRL stx PPUMASK stx DMCFREQ -- disable NMI, rendering, DMC IRQs + bit PPUSTAT -- clear remnant VBlank PPU status flag on reset + vblank_waitbegin() + lda #0 sta SNDCNT -- stop APU channels + -- clear CPU RAM +@_zeroram + sta 0x0000,x sta 0x0100,x sta 0x0200,x sta 0x0300,x + sta 0x0400,x sta 0x0500,x sta 0x0600,x sta 0x0700,x + inx bne _zeroram + vblank_waitbegin() + -- clear OAM + oamcache_clear() oamcache_flush() + -- clear PPU RAM + bit PPUSTAT ppu_addr(0x2000) tax ldy #0x10 +@_zeroppu + sta PPUDATA dex bne _zeroppu dey bne _zeroppu + bit PPUSTAT -- reset latch + if mappers.init then mappers.init() end +end + +-- NES 2.0 (backward compatible with iNES) +-- https://wiki.nesdev.com/w/index.php/NES_2.0 +header = function(t) + if not t then t = {} end + local logsz = function(sz) + assert(sz >= 0 and sz <= 1048576, "invalid size: " .. sz .. ", expected [0, 1048576]") + if sz < 1 then return 0 end + if sz <= 128 then return 1 end + return math.ceil(math.log(sz/64, 2)) + end + -- mapper + local mi1 = t.mapperid or 0 + assert(mi1 >= 0 and mi1 < 4096, "invalid mapper id: " .. mi1 .. ", expected [0, 4095]") + local ms1 = t.submapperid or 0 + assert(ms1 >= 0 and ms1 < 16, "invalid submapper id: " .. ms1 .. ", expected [0, 15]") + local mapper6 = (mi1 & 0xf) << 4 + local mapper7 = mi1 & 0xf0 + local mapper8 = (mi1 >> 8) | (ms1 << 4) + -- prgsize + local prgsize = math.tointeger((t.prgsize or 16384) / 16384) + assert(prgsize, "prgsize must be a multiple of 16384") + -- chrsize + local chrsize = math.tointeger((t.chrsize or 0) / 8192) + assert(chrsize, "chrsize must be a multiple of 8192") + -- wramsize (not battery-backed) + local wramsize = logsz(t.wramsize or 0) + -- bramsize (battery-backed) + local bramsize = logsz(t.bramsize or 0) + -- chrbramsize (battery-backed) + local chrbramsize = logsz(t.chrbramsize or 0) + -- chrramsize (not battery-backed) + local chrramsize = logsz(t.chrramsize or (chrbramsize==0 and chrsize==0 and 8192 or 0)) + local battery_bit = bramsize == 0 and chrbramsize == 0 and 0 or 2 + -- mirror: 'H' for horizontal mirroring, 'V' for vertical mirroring + -- '4' for four-screen VRAM, 218 for four-screen and vertical + local mirror = (t.mirror or 'h'):lower() + mirror = ({ h=0, v=1, ['4']=8, [218]=9 })[mirror] + assert(mirror, "invalid mirror mode: " .. mirror .. ", expected 'H', 'V', '4', or 218") + -- tv: 'N' for NTSC, 'P' for PAL, 'NP' for both preferring NTSC, 'PN' for both preferring PAL + local tv, tvm = 0, (t.tv or 'n'):lower() + assert(tvm=='n' or tvm=='p' or tvm=='np' or tvm=='pn', "invalid tv mode: " .. tostring(t.tv) .. ", expected 'N', 'P', 'NP' or 'PN'") + if tvm:sub(1,1) == 'p' then tv = 1 end + if #tvm > 1 then tv = tv + 2 end + + @@header -- size: 16 bytes + dc.b 0x4e, 0x45, 0x53 -- 'NES' + dc.b 0x1a + dc.b prgsize, chrsize + dc.b mapper6 | mirror | battery_bit + dc.b mapper7 | 8 + dc.b mapper8 + dc.b ((chrsize >> 4) & 0xF0) | ((prgsize >> 8) & 0x0F) + dc.b (bramsize << 4) | wramsize + dc.b (chrbramsize << 4) | chrramsize + dc.b tv, 0, 0, 0 + + -- update table with defaulted values + t.prgsize = prgsize * 16384 + t.chrsize = chrsize * 8192 + t.wramsize = math.tointeger(2^wramsize*64) + t.bramsize = math.tointeger(2^bramsize*64) + t.chrbramsize = math.tointeger(2^chrbramsize*64) + t.chrramsize = math.tointeger(2^chrramsize*64) + mappers.header=t +end + +local n0ne = function(x) return not x or x == 0 end +local val0 = function(x) return x and x or 0 end + +-- 2a03 APU emulator +apuemu = { + --a=0,x=0,y=0,s=0,p=0,pc=0,xjam=false, + --reset = function(self) + -- self.a,self.x,self.y,self.s,self.p,self.pc,self.xjam = 0,0,0,0xff, + --end +} + +-- plays a NSF tune and record its register writes for replay +-- stops at C00 +-- FamiTracker must be in the path +-- Ported from Shiru's nsf2vgm +nsf = function(filename, song) + local f, nsf = assert(io.open(filename,'rb')) nsf=f:read('*all') f:close() + assert(nsf[0x7a] == 0, 'expansion chips are not supported') + nsf.songs = nsf[7] + nsf.loadadr=nsf[9]+(nsf[10]<<8) nsf.initadr=nsf[11]+(nsf[12]<<8) nsf.playadr=nsf[13]+(nsf[14]<<8) + local bs = false for i=1,8 do local p=nsf[0x70+i] nsf.page[i]=p if p~=0 then bs=true end end + if not bs then for i=1,8 do nsf.page[i]=i-1 end end + local ram,rom,page = {},{},{} + for i=1,#nsf-0x80 do rom[nsf.loadadr-0x7FFF] = nsf[0x81] end + local reg = { 0x13f, 0x108, 0x100, 0x100, 0x13f, 0x108, 0x100, 0x100, 0x180, 0x100, 0x100, 0x100, 0x13f, 0x100, 0x100, 0x100, + 0x100, 0x100, 0x100, 0x100, 0x100, 0x11f, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100 } + +end + +--[[ For all mappers, where relevant: + +prg rom banks: + prgroms are numbered from last (0) to first (#-1), so that adding more does not change + prgrom0, which must contain the reset vector (main). The switchprgrom* functions expect + a bank index according to this reversed numbering (0 is last physical bank in ROM). + + t.prgmap is an optional function taking a prgrom bank index and returning its rorg value. + Default is to map banks in sequence according to their size, eg.: + * for sizes 32kB: always 0x8000. + * for sizes 16kB+16kB: repeat sequence 0x8000, 0xa000. + * for sizes 16kB+8kB+8kB: repeat sequence 0x8000, 0xc000, 0xe000. + * for sizes 8kB+8kB+8kB+8kB: repeat sequence 0x8000, 0xa000, 0xc000, 0xe000. + If last bank is fixed, it is not part of the sequence and its mapping value is skipped: + * for sizes 8kB+8kB+ last fixed 16kB: repeat sequence 0x8000, 0xa000, set last bank to 0xc000. + +chr rom banks: + chrroms are numbered in physical ROM address order, unlike prgroms. + + Banks are created with the smallest switchable size, maximum being 4kB by default. + t.chrmap is an optional function taking a chrrom bank index and returning its offset in + ROM from the beginning of the first chrrom, its size (defaults to everything remaining), + and its relocation origin (defaults to 0). + So if a mapper allows switching chrroms in any of 1-1-1-1-1-1-1-1kB / 2-2-2-2kB, etc., + the default is to create only 1kB chrroms. Conversely, with a mapper switching only in 8kB + slices, 4kB chrrom are created, not 8kB (but you can specify any size using t.chrmap). +]] + +--[[ + https://wiki.nesdev.com/w/index.php/NROM +]] +mappers.NROM = function(t) + if not t then t = {} end + if not t.prgsize then t.prgsize = 16384 end + assert(t.prgsize == 16384 or t.prgsize == 32768, "prgsize must be 16 or 32kB") + if n0ne(t.chrsize) and n0ne(t.chrramsize) and n0ne(t.chrbramsize) then t.chrsize = 8192 end + assert(val0(t.chrsize) + val0(t.chrramsize) + val0(t.chrbramsize) == 8192, "combined chrrom size must be 8kB") + assert(not t.mirror or ({ h=1, v=1 })[t.mirror:lower()], "only H and V mirroring are supported") + local prgstart = 0x10000 - t.prgsize + hdrrom = location{prgstart - 16, prgstart - 1, name='header'} + header(t) + prgrom0 = location{prgstart, 0xffff, name='prgrom'} + prgrom = prgrom0 + section{"vectors", org=0xfffa} dc.w nmi, main, irq + if (t.chrsize > 0) then + local ci = 0 + local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end + repeat + local off, sz, rorg = chrmap(ci) + sz = sz or t.chrsize - off + local o = off + 0x10000 + _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} + ci = ci+1 + until off + sz >= t.chrsize + chrrom = chrrom0 + end +end +mappers[0] = mappers.NROM + +--[[ + https://wiki.nesdev.com/w/index.php/UxROM + Has bus conflicts. +]] +mappers.UxROM = function(t) + if not t then t = {} end + t.mapperid = 2 + if not t.prgsize then t.prgsize = 32768 end + assert(t.prgsize >= 0x8000 and t.prgsize <= 0x40000, "prgsize must be at least 32kB and at most 256kB") + if not t.chrsize then t.chrsize = 8192 end + assert(t.chrsize == 0x2000, "chrsize must be 8kB") + assert(not t.mirror or ({ h=1, v=1 })[t.mirror:lower()], "only H and V mirroring are supported") + hdrrom = location{0x7ff0, 0x7fff, name='header'} + header(t) + local bc = t.prgsize//0x4000 + for bi=0,bc-2 do + local o,ix = 0x8000 + bi*0x4000, bc-1-bi + _ENV['prgrom'..ix] = location{o, o+0x3fff, rorg=0x8000, name='prgrom'..ix} + end + local prglast = 0x8000 + (bc-1)*0x4000 + prgrom0 = location{prglast, prglast+0x3fff, rorg=0xc000, name='prgrom0'} + prgrom = prgrom0 + section{"vectors", org=prglast+0x3ffa} dc.w nmi, main, irq + @@bankbytes -- for handling bus conflicts + samepage for i=bc-1,0,-1 do byte(i) end end + local ci, chrstart = 0, 0x8000 + bc*0x4000 + local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end + repeat + local off, sz, rorg = chrmap(ci) + sz = sz or t.chrsize - off + local o = off + chrstart + _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} + ci = ci+1 + until off + sz >= t.chrsize + chrrom = chrrom0 + function switchprgrom(bankix) + if bankix then assert(bankix < bc-1) lda #bankix end + -- lda to reverse map [n..0] to [0..n] + tax lda bankbytes,x sta bankbytes,x + end + mappers.init = function() + switchprgrom(1) + end +end +mappers[2] = mappers.UxROM + +--[[ + https://wiki.nesdev.com/w/index.php/CNROM + Has bus conflicts. +]] +mappers.CNROM = function(t) + if not t then t = {} end + t.mapperid = 3 + if not t.prgsize then t.prgsize = 16384 end + assert(t.prgsize == 16384 or t.prgsize == 32768, "prgsize must be 16 or 32kB") + if not t.chrsize then t.chrsize = 8192 end + assert(t.chrsize >= 0x2000 and t.chrsize <= 0x8000, "chrsize must be at least 8kB and at most 32kB") + assert(not t.mirror or ({ h=1, v=1 })[t.mirror:lower()], "only H and V mirroring are supported") + local prgstart = 0x10000 - t.prgsize + hdrrom = location{prgstart - 16, prgstart - 1, name='header'} + header(t) + prgrom = location{prgstart, 0xffff, name='prgrom'} + section{"vectors", org=0xfffa} dc.w nmi, main, irq + @@bankbytes samepage -- for handling bus conflicts + dc.b 0x00, 0x01, 0x02, 0x03 + dc.b 0x10, 0x11, 0x12, 0x13 + dc.b 0x20, 0x21, 0x22, 0x23 + dc.b 0x30, 0x31, 0x32, 0x33 + end + local ci, cc = 0, t.chrsize//0x2000 + local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end + repeat + local off, sz, rorg = chrmap(ci) + sz = sz or t.chrsize - off + local o = off + 0x10000 + _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} + ci = ci+1 + until off + sz >= t.chrsize + chrrom = chrrom0 + securitydiodes = 0 -- set to actual value, eg 0x20 + function switchchrrom(bankix) + assert(securitydiodes < 0x40) + if bankix then + assert(bankix < cc) + ldx #bankix|securitydiodes>>2 lda #bankix|securitydiodes + else + ora #securitydiodes>>2 tax and #3 ora #securitydiodes + end + sta bankbytes,x + end + mappers.init = function() + switchchrrom(0) + end +end +mappers[3] = mappers.CNROM + +--[[ + https://wiki.nesdev.com/w/index.php/GxROM + Has bus conflicts. +]] +mappers.GxROM = function(t) + if not t then t = {} end + t.mapperid = 66 + if not t.prgsize then t.prgsize = 32768 end + assert(t.prgsize >= 0x8000 and t.prgsize <= 0x20000, "prgsize must be at least 32kB and at most 128kB") + if not t.chrsize then t.chrsize = 8192 end + assert(t.chrsize >= 0x2000 and t.chrsize <= 0x8000, "chrsize must be at least 8kB and at most 32kB") + assert(not t.mirror or ({ h=1, v=1 })[t.mirror:lower()], "only H and V mirroring are supported") + hdrrom = location{0x7ff0, 0x7fff, name='header'} + header(t) + local bc = t.prgsize//0x8000 + local cc = t.chrsize//0x2000 + local ci, chrstart = 0, 0x8000 + bc*0x8000 + local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end + repeat + local off, sz, rorg = chrmap(ci) + sz = sz or t.chrsize - off + local o = off + chrstart + _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} + ci = ci+1 + until off + sz >= t.chrsize + chrrom = chrrom0 + -- RAM address of bank register copy, to switch using A instead of immediate + -- if bankregister_shadow is negative, only immediate bankswitching is available + bankregister_shadow = -1 + -- otherwise, just set the value directly: xxPPxxCC in A, xxxxPPCC in X with sta bankbytes0,x + function switchroms(prgbankix, chrbankix) + if prgbankix then + assert(prgbankix < bc) prgbankix = bc-1-prgbankix + assert(chrbankix, "GxROM must specify both PRG and CHR roms to switch simultaneously") + assert(chrbankix < cc) + local br = prgbankix<<4 | chrbankix + ldx #br&3|br>>2 lda #br sta bankbytes0,x + else -- A contains 00PP00CC + if bankregister_shadow >= 0 then + sta bankregister_shadow -- bankregister_shadow = 00PP00CC + lsr lsr -- A = 0000PP00, c + -- compute bc-1-prgbankix (reverse indexing) + eor #0xff adc #(bc-1<<2)+1 -- negate and +bc-1 combined, NN = bc-1-PP: A = 0000NN00 + ora bankregister_shadow ;and #0xf tax -- X = 0000NNCC + else -- use stack instead + tsx sta 0x100,x lsr lsr -- A = 0000PP00, s[0] = 00PP00CC, c + -- compute bc-1-prgbankix (reverse indexing) + eor #0xff adc #(bc-1<<2)+1 -- negate and +bc-1 combined, NN = bc-1-PP: A = 0000NN00 + ora 0x100,x ;and #0xf tax -- X = 0000NNCC + end + lda bankbytes0,x sta bankbytes0,x -- A = 00NN00CC, from table + end + end + for bi=0,bc-1 do + local o,ix = 0x8000 + bi*0x8000, bc-1-bi + _ENV['prgrom'..ix] = location{o, o+0x7fff, rorg=0x8000, name='prgrom'..ix} + local start=section{"entry"..ix, org=o+0x8000-6-16-10} switchroms(0,0) if ix==0 then jmp main end + section{"vectors"..ix, org=o+0x8000-6} dc.w "nmi"..ix, start, "irq"..ix + section{"bankbytes"..ix, org=o+0x8000-6-16} samepage -- for handling bus conflicts + dc.b 0x00, 0x01, 0x02, 0x03 + dc.b 0x10, 0x11, 0x12, 0x13 + dc.b 0x20, 0x21, 0x22, 0x23 + dc.b 0x30, 0x31, 0x32, 0x33 + end + end + prgrom = prgrom0 + function switchprgrom(bankix) + if bankix then + assert(bankix < bc) + lda #bc-1-bankix + else + clc eor #0xff adc #bc -- bc-1 - A + end + assert(bankregister_shadow >= 0, "no RAM slot assigned to bankregister_shadow") + tay lda bankregister_shadow ;and #3 sta bankregister_shadow + tya asl asl ora bankregister_shadow tax + lda bankbytes0,x sta bankregister_shadow sta bankbytes0,x + end + function switchchrrom(bankix) + if bankix then + assert(bankix < cc) + lda #bankix + end + assert(bankregister_shadow >= 0, "no RAM slot assigned to bankregister_shadow") + tay lda bankregister_shadow lsr lsr sta bankregister_shadow + tya ora bankregister_shadow tax + lda bankbytes0,x sta bankregister_shadow sta bankbytes0,x + end +end +mappers[66] = mappers.GxROM + +--[[ + https://wiki.nesdev.com/w/index.php/MMC1 + + t.prgmap is an optional function taking a prgrom bank index and returning its rorg value. + Default is to create 16kB banks if prgswitchmode is not all, 32kB otherwise, and rorg them + accordingly. + Same goes for chrrom sizes. + + t.prgswitchmode: + 'first' makes 0x8000-0xbfff switchable (default) + 'last' makes 0xc000-0xffff switchable + 'all' makes 0x8000-0xffff switchable + t.chrswitchmode: + 'all' switches whole 8kB at a time + 'half' switches 2 separate 4kB banks (default) +]] +mappers.MMC1 = function(t) + if not t then t = {} end + t.mapperid = 1 + if not t.prgswitchmode then t.prgswitchmode = 'first' end + if not t.chrswitchmode then t.chrswitchmode = 'half' end + if not t.wramsize then t.wramsize = 0 end + if not t.bramsize and t.wramsize == 0 then t.bramsize = 8192 end + local prgram = t.bramsize + t.wramsize + assert(prgram >= 0x2000 and prgram <= 0x8000, "bramsize or wramsize must be at least 8kB and at most 32kB") + if not t.prgsize then t.prgsize = 32768 end + assert(t.prgsize >= 0x8000 and t.prgsize <= 0x80000, "prgsize must be at least 32kB and at most 512kB") + if not t.chrsize then t.chrsize = 8192 end + assert(t.chrsize >= 0x2000 and t.chrsize <= 0x20000, "chrsize must be at least 8kB and at most 128kB") + hdrrom = location{0x7FF0, 0x7FFF, name='header'} + header(t) + local bsz = t.prgswitchmode=='all' and 0x8000 or 0x4000 + local prgmap = t.prgmap or function(bi, bc) return t.prgswitchmode=='last' and 0xc000 or 0x8000 end + local bc = t.prgsize//bsz + for bi=0,bc-1 do + local o,ix = 0x8000 + bi*bsz, bc-1-bi + _ENV['prgrom'..ix] = location{o, o+bsz-1, rorg=prgmap(ix,bc), name='prgrom'..ix} + end + section{"vectors", org=0x8000+bc*bsz-6} dc.w nmi, main, irq + prgrom = prgrom0 + local ci, chrstart = 0, 0x8000 + bc*bsz + local csz = t.chrswitchmode=='all' and 0x2000 or 0x1000 + local cc = t.chrsize//csz + local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end + repeat + local off, sz, rorg = chrmap(ci) + sz = sz or t.chrsize - off + local o = off + chrstart + _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} + ci = ci+1 + until off + sz >= t.chrsize + chrrom = chrrom0 + + local prgswitchmodemap = { all=0, first=3<<2, last=2<<2 } + local chrswitchmodemap = { all=0, half=1<<4 } + mmc1ctrl = (mappers.header.mirror==1 and 2 or 3) | prgswitchmodemap[t.prgswitchmode] | chrswitchmodemap[t.chrswitchmode] + function mmc1write(reg) + sta reg lsr sta reg lsr sta reg lsr sta reg lsr sta reg + end + -- Can be turned into a function, A must contain the bank index. + -- eg: + -- @@switchprgrom_func switchprgrom_f() + -- switchprgrom_f = function() jsr switchprgrom_func rts end + function switchprgrom_f() mmc1write(0xe000) end + function switchprgrom(bankix) + assert(bankix < t.prgsize//0x4000) + lda #bankix + switchprgrom_f() + end + function switchchrrom_f(slot) assert(slot<2) mmc1write(slot==0 and 0xa000 or 0xc000) end + function switchchrrom(bankix, slot) + assert(bankix < t.chrsize//0x1000) + lda #bankix + switchchrrom_f(slot or 0) + end + function setmirror(mirror) + mirror = assert(({ h=3, v=2, hi=1, lo=0 })[mirror:lower()]) + mmc1ctrl = (mmc1ctrl & ~3) | mirror + lda #mmc1ctrl mmc1write(0x8000) + end + function setprgswitchmode(mode) + mode = assert(prgswitchmodemap[mode]) + mmc1ctrl = (mmc1ctrl & ~0xc) | mode + lda #mmc1ctrl mmc1write(0x8000) + end + function setchrswitchmode(mode) + mode = assert(chrswitchmodemap[mode]) + mmc1ctrl = (mmc1ctrl & ~0x10) | mode + lda #mmc1ctrl mmc1write(0x8000) + end + mappers.init = function() + lda #0x80 sta 0x8000 + lda #mmc1ctrl mmc1write(0x8000) + if t.prgswitchmode ~= 'all' then switchprgrom(1) end + switchchrrom(0) if t.chrswitchmode ~= 'all' then switchchrrom(1,1) end + end +end +mappers[1] = mappers.MMC1 + +--[[ + https://wiki.nesdev.com/w/index.php/MMC3 + + prgroms are numbered from last (0) to first (#-1), so that adding more does not change + prgrom0, which must contain the reset vector (main). + Last 2 prg banks are merged into 1 16kB bank, to allow linker optimization - hence, 0 must + always be set on bit 6 of bank select, even; also, there is no prgrom1 as a consequence. + + chrroms are all 1kB, so they can work with chr A12 inversion enabled or not. + + With default submapper id of 0, this defaults to revision MM3C, which generates a scanline + interrupt at each scanline when counter is loaded with 0. +]] +mappers.MMC3 = function(t) + if not t then t = {} end + t.mapperid = 4 + if not t.bramsize then t.bramsize = 8192 end + assert(t.bramsize == 8192, "bramsize must be 8kB") + if not t.prgsize then t.prgsize = 32768 end + assert(t.prgsize >= 0x8000 and t.prgsize <= 0x80000, "prgsize must be at least 32kB and at most 512kB") + if not t.chrsize then t.chrsize = 8192 end + assert(t.chrsize >= 0x2000 and t.chrsize <= 0x40000, "chrsize must be at least 8kB and at most 256kB") + hdrrom = location{0x7FF0, 0x7FFF, name='header'} + header(t) + local prgmap = t.prgmap or function(bi, bc) return 0x8000+(bi&1)*0x2000 end + local bc = t.prgsize//0x2000 + for bi=0,bc-3 do + local o,ix = 0x8000 + bi*0x2000, bc-bi-1 + _ENV['prgrom'..ix] = location{o, o+0x1fff, rorg=prgmap(ix,bc), name='prgrom'..ix} + end + do + local o = 0x8000 + (bc-2)*0x2000 + prgrom0 = location{o, o+0x3fff, rorg=0xc000, name='prgrom0'} + section{"vectors", org=o+0x3ffa} dc.w nmi, main, irq + end + prgrom = prgrom0 + local ci, chrstart = 0, 0x8000 + bc*0x2000 + local chrmap = t.chrmap or function(ci) return ci*0x400, 0x400, (ci&7)*0x400 end + local cc = t.chrsize//0x400 + repeat + local off, sz, rorg = chrmap(ci) + sz = sz or t.chrsize - off + local o = off + chrstart + _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} + ci = ci+1 + until off + sz >= t.chrsize + chrrom = chrrom0 + local a12inv = false + function seta12inv(enabled) a12inv = enabled end + function switchprgrom(bankix, slot) + assert(slot<2) + slot = slot + 6 + if a12inv then slot = slot | 0x80 end + lda #slot sta 0x8000 + assert(bankix < bc) + bankix = bc-1-bankix -- reverse index order, since we set 0 as last + lda #bankix sta 0x8001 + end + -- slot [0, 7], each slot is 1kB counting in order, regardless of a12inv state + function switchchrrom(bankix, slot) + assert(slot<8) + if a12inv then + assert(slot ~= 5 and slot ~= 7) + if slot == 6 then slot = 1 + elseif slot == 4 then slot = 0 + else slot = slot + 2 end + slot = slot | 0x80 + else + assert(slot ~= 1 and slot ~= 3) + if slot == 2 then slot = 1 + elseif slot > 3 then slot = slot - 2 end + end + lda #slot sta 0x8000 + assert(bankix < cc) + lda #bankix sta 0x8001 + end + function setmirror(mirror) + mirror = assert(({ h=1, v=0 })[mirror:lower()]) + lda #mirror sta 0xa000 + end + function protectsram() lda 0x40 sta 0xa001 end + function scanlineirq(count) ldx #1 stx 0xe000 lda #count-1 sta 0xc000 sta 0xc001 stx 0xe001 end + mappers.init = function() + switchprgrom(2, 0) switchprgrom(3, 1) + switchchrrom(0, 0) switchchrrom(2, 2) + switchchrrom(4, 4) switchchrrom(5, 5) switchchrrom(6, 6) switchchrrom(7, 7) + local mirror = mappers.header.mirror + if mirror==0 or mirror==1 then lda #mirror~1 sta 0xa000 end + lda #0x80 sta 0xa001 + end +end +mappers[4] = mappers.MMC3 + +--[[ + https://wiki.nesdev.com/w/index.php/MMC5 + https://forums.nesdev.com/viewtopic.php?f=9&t=16789 + + main MUST be in 0xe000-0xfff9 of last prgrom. + prgroms are numbered from last (0) to first (#-1), so that adding more does not change + prgrom0, which must contain the reset vector (main). + + chrroms are all 1kB. +]] +mappers.MMC5 = function(t) + if not t then t = {} end + t.mapperid = 5 + assert(val0(t.bramsize) + val0(t.wramsize) <= 0x10000, "bramsize + wramsize must be at most 64kB") + if not t.prgsize then t.prgsize = 8192 end + assert(t.prgsize <= 0x100000, "prgsize must be at most 1MB") + if not t.chrsize then t.chrsize = 8192 end + assert(t.chrsize <= 0x100000, "chrsize must be at most 1MB") + hdrrom = location{0x7FF0, 0x7FFF, name='header'} + header(t) + local prgmap = t.prgmap or function(bi, bc) return 0x8000+(bi&3)*0x2000 end + local bc = t.prgsize//0x2000 + for bi=0,bc-2 do + local o,ix = 0x8000 + bi*0x2000, bc-bi-1 + _ENV['prgrom'..ix] = location{o, o+0x1fff, rorg=prgmap(ix,bc), name='prgrom'..ix} + end + do + local o = 0x8000 + (bc-1)*0x2000 + prgrom0 = location{o, o+0x1fff, rorg=0xe000, name='prgrom0'} + section{"vectors", org=o+0x1ffa} dc.w nmi, main, irq + section{"main", org=o} -- enforce entry point in last bank + end + prgrom = prgrom0 + local ci, chrstart = 0, 0x8000 + bc*0x2000 + local chrmap = t.chrmap or function(ci) return ci*0x400, 0x400, (ci&7)*0x400 end + local cc = t.chrsize//0x400 + repeat + local off, sz, rorg = chrmap(ci) + sz = sz or t.chrsize - off + local o = off + chrstart + _ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci} + ci = ci+1 + until off + sz >= t.chrsize + chrrom = chrrom0 + function prgbankmode(mode) if mode then lda #mode end sta 0x5100 end + function chrbankmode(mode) if mode then lda #mode end sta 0x5101 end + function wrammode(mode) if mode then lda #mode end sta 0x5104 end + function protectsram() lda #0 sta 0x5102 sta 0x5103 end + function screenmap(map) if map then lda #map end sta 0x5105 end + function filltile(tile) if tile then lda #tile end sta 0x5106 end + function fillcolor(col) if col then lda #col end sta 0x5107 end + function switchprgram(chip, bank) + assert(chip == 0 or chip == 1) + assert(bank < 4) + lda #chip<<2|bank sta 0x5113 + end + function switchprgrom0(bank, isram) lda #bank|(isram and 0 or 0x80) sta 0x5114 end + function switchprgrom1(bank, isram) lda #bank|(isram and 0 or 0x80) sta 0x5115 end + function switchprgrom2(bank, isram) lda #bank|(isram and 0 or 0x80) sta 0x5116 end + function switchprgrom3(bank, isram) lda #bank|(isram and 0 or 0x80) sta 0x5117 end + function switchchrrom(bank, slot) -- 1kB mode + assert(slot < 12) + assert(bank < cc) + lda #bank>>7 sta 0x5130 + lda #bank sta slot+0x5120 + end + vsplitmode = 0 + function vsplitstart(starttile) + assert(starttile < 32) + vsplitmode = (vsplitmode & ~31) | starttile + lda #vsplitmode sta 0x5200 + end + function vsplitside(side) + assert(side == 'left' or side == 'right') + vsplitmode = (vsplitmode & ~0x40) | (side == 'right' and 1 or 0) + lda #vsplitmode sta 0x5200 + end + function vsplitenable(enabled) + vsplitmode = (vsplitmode & ~0x80) | (enabled and 1 or 0) + lda #vsplitmode sta 0x5200 + end + function vsplitscroll(vscroll) if vscroll then lda #vscroll end sta 0x5201 end + function vsplitbank(chrbank) if chrbank then lda #chrbank end sta 0x5202 end + function scanlineirq(atscanline) lda #0 sta 0x5204 lda #atscanline sta 0x5203 lda #0x80 sta 0x5204 end + function irqenable(enabled) lda #enabled and 0x80 or 0 sta 0x5204 end + -- 0x40 set: PPU is rendering visible scanlines + -- 0x80 set: a scanlineirq is pending (internal counter reach atscanline value set with scanlineirq()) + function irqstatus() lda 0x5204 end + -- multiplies a*x = x:a (x hi, a lo) + function mul() sta 0x5205 stx 0x5206 lda 0x5205 ldx 0x5206 end + mappers.init = function() + ldx #2 stx 0x5102 dex stx 0x5103 + lda #vsplitmode sta 0x5200 + prgbankmode(2) + switchprgrom1(3) -- map prg rom 3, 2, x, 0 + switchprgrom2(1) -- map prg rom 3, 2, 1, 0 + chrbankmode(0) switchchrrom(0, 7) chrbankmode(3) -- map chr rom 0 to the 8kB, and set mode to 1kB slices + end +end +mappers[5] = mappers.MMC5 diff --git a/vim/indent/l65.vim b/vim/indent/l65.vim index 45f6eec..06a061e 100644 --- a/vim/indent/l65.vim +++ b/vim/indent/l65.vim @@ -1,122 +1,122 @@ -" Vim indent file -" Language: l65 -" URL: modified from https://github.com/tbastos/vim-lua - -" Initialization ------------------------------------------{{{1 - -if exists("b:did_indent") - finish -endif -let b:did_indent = 1 - -setlocal autoindent -setlocal nosmartindent - -setlocal indentexpr=GetL65Indent() -setlocal indentkeys+=0=end,0=until,0=elseif,0=else - -" Only define the function once. -if exists("*GetL65Indent") - finish -endif - -" Variables -----------------------------------------------{{{1 - -let s:open_patt = '\C\%(\<\%(function\|if\|repeat\|do\)\>\|(\|{\)' -let s:middle_patt = '\C\<\%(else\|elseif\)\>' -let s:close_patt = '\C\%(\<\%(end\|until\)\>\|)\|}\)' - -let s:anon_func_start = '\S\+\s*[({].*\ 0 - let i += num_pairs - endif - - " special case: call(with, {anon = function() -- should indent only once - if num_pairs > 1 && contents_prev =~# s:anon_func_start - let i = 1 - endif - - " check if current line closes blocks - call cursor(prev_line, col([prev_line,'$'])) - let num_pairs = searchpair(s:open_patt, s:middle_patt, s:close_patt, - \ 'mr', s:skip_expr, v:lnum) - if num_pairs > 0 - let i -= num_pairs - endif - - " special case: end}) -- end of call with anon func should unindent once - if num_pairs > 1 && contents_cur =~# s:anon_func_end - let i = -1 - endif - - " if the previous line closed a paren, unindent (except with anon funcs) - call cursor(prev_line - 1, col([prev_line - 1, '$'])) - let num_pairs = searchpair('(', '', ')', 'mr', s:skip_expr, prev_line) - if num_pairs > 0 && contents_prev !~ s:anon_func_end - let i -= 1 - endif - - " if this line closed a paren, indent (except with anon funcs) - call cursor(prev_line, col([prev_line, '$'])) - let num_pairs = searchpair('(', '', ')', 'mr', s:skip_expr, v:lnum) - if num_pairs > 0 && contents_cur !~ s:anon_func_end - let i += 1 - endif - - " restore cursor - call setpos(".", original_cursor_pos) - - return indent(prev_line) + (&sw * i) - -endfunction +" Vim indent file +" Language: l65 +" URL: modified from https://github.com/tbastos/vim-lua + +" Initialization ------------------------------------------{{{1 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal autoindent +setlocal nosmartindent + +setlocal indentexpr=GetL65Indent() +setlocal indentkeys+=0=end,0=until,0=elseif,0=else + +" Only define the function once. +if exists("*GetL65Indent") + finish +endif + +" Variables -----------------------------------------------{{{1 + +let s:open_patt = '\C\%(\<\%(function\|if\|repeat\|do\)\>\|(\|{\)' +let s:middle_patt = '\C\<\%(else\|elseif\)\>' +let s:close_patt = '\C\%(\<\%(end\|until\)\>\|)\|}\)' + +let s:anon_func_start = '\S\+\s*[({].*\ 0 + let i += num_pairs + endif + + " special case: call(with, {anon = function() -- should indent only once + if num_pairs > 1 && contents_prev =~# s:anon_func_start + let i = 1 + endif + + " check if current line closes blocks + call cursor(prev_line, col([prev_line,'$'])) + let num_pairs = searchpair(s:open_patt, s:middle_patt, s:close_patt, + \ 'mr', s:skip_expr, v:lnum) + if num_pairs > 0 + let i -= num_pairs + endif + + " special case: end}) -- end of call with anon func should unindent once + if num_pairs > 1 && contents_cur =~# s:anon_func_end + let i = -1 + endif + + " if the previous line closed a paren, unindent (except with anon funcs) + call cursor(prev_line - 1, col([prev_line - 1, '$'])) + let num_pairs = searchpair('(', '', ')', 'mr', s:skip_expr, prev_line) + if num_pairs > 0 && contents_prev !~ s:anon_func_end + let i -= 1 + endif + + " if this line closed a paren, indent (except with anon funcs) + call cursor(prev_line, col([prev_line, '$'])) + let num_pairs = searchpair('(', '', ')', 'mr', s:skip_expr, v:lnum) + if num_pairs > 0 && contents_cur !~ s:anon_func_end + let i += 1 + endif + + " restore cursor + call setpos(".", original_cursor_pos) + + return indent(prev_line) + (&sw * i) + +endfunction diff --git a/vim/syntax/l65.vim b/vim/syntax/l65.vim index 6852454..321c9ef 100644 --- a/vim/syntax/l65.vim +++ b/vim/syntax/l65.vim @@ -1,642 +1,642 @@ -" Vim syntax file -" Language: l65 - -" For version 5.x: Clear all syntax items -" For version 6.x: Quit when a syntax file was already loaded -if version < 600 - syntax clear -elseif exists("b:current_syntax") - finish -endif - -let s:cpo_save = &cpo -set cpo&vim - -syn case match - -" syncing method -syntax sync fromstart - -" Comments -syn keyword luaTodo contained TODO FIXME XXX -syn match luaComment "--.*$" contains=luaTodo,@Spell -syn region luaComment matchgroup=luaComment start="--\[\z(=*\)\[" end="\]\z1\]" contains=luaTodo,@Spell - -" First line may start with #! -syn match luaComment "\%^#!.*" - -" catch errors caused by wrong parenthesis and wrong curly brackets or -" keywords placed outside their respective blocks -syn region luaParen transparent start='(' end=')' contains=ALLBUT,luaParenError,luaTodo,luaSpecial,luaIfThen,luaElseifThen,luaElse,luaThenEnd,luaBlock,luaLoopBlock,luaIn,luaStatement -syn region luaTableBlock transparent matchgroup=luaTable start="{" end="}" contains=ALLBUT,luaBraceError,luaTodo,luaSpecial,luaIfThen,luaElseifThen,luaElse,luaThenEnd,luaBlock,luaLoopBlock,luaIn,luaStatement - -syn match luaParenError ")" -syn match luaBraceError "}" -syn match luaError "\<\%(end\|else\|elseif\|then\|until\|in\)\>" - -" function ... end -syn region luaFunctionBlock transparent matchgroup=luaFunction start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn - -" if ... then -syn region luaIfThen transparent matchgroup=luaCond start="\" end="\"me=e-4 contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaIn nextgroup=luaThenEnd skipwhite skipempty - -" then ... end -syn region luaThenEnd contained transparent matchgroup=luaCond start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaThenEnd,luaIn - -" elseif ... then -syn region luaElseifThen contained transparent matchgroup=luaCond start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn - -" else -syn keyword luaElse contained else - -" do ... end -syn region luaBlock transparent matchgroup=luaStatement start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn - -" samepage ... end -syn region luaBlock transparent matchgroup=luaStatement start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn -" crosspage ... end -syn region luaBlock transparent matchgroup=luaStatement start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn - -" repeat ... until -syn region luaLoopBlock transparent matchgroup=luaRepeat start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn - -" while ... do -syn region luaLoopBlock transparent matchgroup=luaRepeat start="\" end="\"me=e-2 contains=ALLBUT,luaTodo,luaSpecial,luaIfThen,luaElseifThen,luaElse,luaThenEnd,luaIn nextgroup=luaBlock skipwhite skipempty - -" for ... do and for ... in ... do -syn region luaLoopBlock transparent matchgroup=luaRepeat start="\" end="\"me=e-2 contains=ALLBUT,luaTodo,luaSpecial,luaIfThen,luaElseifThen,luaElse,luaThenEnd nextgroup=luaBlock skipwhite skipempty - -syn keyword luaIn contained in - -" other keywords -syn keyword luaStatement return local break goto -syn match luaLabel "::\I\i*::" -syn match l65Label "@@\k\+" -syn match l65Label "@\k\+" -syn match l65SChar "[#!\\]" -syn match l65SFunc "[\\]" -syn keyword luaOperator and or not -syn keyword luaConstant nil true false - -syn region l65PreProc start="^#\s*\(pragma\>\)" skip="\\$" end="$" keepend - -" Strings -syn match luaSpecial contained #\\[\\abfnrtvz'"]\|\\b[01]\{2}\|\\x[[:xdigit:]]\{2}\|\\[[:digit:]]\{,3}# -syn region luaString2 matchgroup=luaString start="\[\z(=*\)\[" end="\]\z1\]" contains=@Spell -syn region luaString start=+'+ end=+'+ skip=+\\\\\|\\'+ contains=luaSpecial,@Spell -syn region luaString start=+"+ end=+"+ skip=+\\\\\|\\"+ contains=luaSpecial,@Spell - -" integer number -syn match luaNumber "\<\d\+\>" -" hex numbers -syn match luaNumber "\<0[xX][[:xdigit:].]\+\%([pP][-+]\=\d\+\)\=\>" -" bin numbers -syn match luaNumber "\<0[bB][01]\+\>" -" floating point number, with dot, optional exponent -syn match luaNumber "\<\d\+\.\d*\%([eE][-+]\=\d\+\)\=\>" -" floating point number, starting with a dot, optional exponent -syn match luaNumber "\.\d\+\%([eE][-+]\=\d\+\)\=\>" -" floating point number, without dot, with exponent -syn match luaNumber "\<\d\+[eE][-+]\=\d\+\>" - -syn keyword luaFunc assert collectgarbage dofile error next -syn keyword luaFunc print rawget rawset tonumber tostring type _VERSION - -syn keyword luaFunc getmetatable setmetatable -syn keyword luaFunc ipairs pairs -syn keyword luaFunc pcall xpcall -syn keyword luaFunc _G loadfile rawequal require -syn keyword luaFunc load select -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn keyword luaFunc _ENV rawlen -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ -syn match luaFunc /\/ - -synt match luaSemiCol ";" - -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ - -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ - -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ - - -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ - -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ - -" These are only if defined by user for VCS cross bank calls. -" Remove these lines if desired. -syn match l65Opcode /\/ -syn match l65Opcode /\/ -syn match l65Opcode /\/ -" Common alias, reomve if desired. -syn match l65Opcode /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ -syn match l65Keyword /\/ - -" Define the default highlighting. -" For version 5.7 and earlier: only when not done already -" For version 5.8 and later: only when an item doesn't have highlighting yet -if version >= 508 || !exists("did_l65_syntax_inits") - if version < 508 - let did_l65_syntax_inits = 1 - command -nargs=+ HiLink hi link - else - command -nargs=+ HiLink hi def link - endif - - HiLink luaStatement Statement - HiLink luaRepeat Repeat - HiLink luaFor Repeat - HiLink luaString String - HiLink luaString2 String - HiLink luaNumber Number - HiLink luaOperator Operator - HiLink luaIn Operator - HiLink luaConstant Constant - HiLink luaCond Conditional - HiLink luaElse Conditional - HiLink luaFunction Function - HiLink luaComment Comment - HiLink luaTodo Todo - HiLink luaTable Structure - HiLink luaError Error - HiLink luaParenError Error - HiLink luaBraceError Error - HiLink luaSpecial SpecialChar - HiLink luaFunc Identifier - HiLink luaLabel Label - - HiLink luaSemiCol Delimiter - - HiLink l65PreProc PreProc - HiLink l65Label Special - HiLink l65Keyword Identifier - HiLink l65Opcode Type - HiLink l65SChar Operator - HiLink l65SFunc Function - - delcommand HiLink -endif - -let b:current_syntax = "l65" - -let &cpo = s:cpo_save -unlet s:cpo_save -" vim: et ts=8 sw=2 +" Vim syntax file +" Language: l65 + +" For version 5.x: Clear all syntax items +" For version 6.x: Quit when a syntax file was already loaded +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +syn case match + +" syncing method +syntax sync fromstart + +" Comments +syn keyword luaTodo contained TODO FIXME XXX +syn match luaComment "--.*$" contains=luaTodo,@Spell +syn region luaComment matchgroup=luaComment start="--\[\z(=*\)\[" end="\]\z1\]" contains=luaTodo,@Spell + +" First line may start with #! +syn match luaComment "\%^#!.*" + +" catch errors caused by wrong parenthesis and wrong curly brackets or +" keywords placed outside their respective blocks +syn region luaParen transparent start='(' end=')' contains=ALLBUT,luaParenError,luaTodo,luaSpecial,luaIfThen,luaElseifThen,luaElse,luaThenEnd,luaBlock,luaLoopBlock,luaIn,luaStatement +syn region luaTableBlock transparent matchgroup=luaTable start="{" end="}" contains=ALLBUT,luaBraceError,luaTodo,luaSpecial,luaIfThen,luaElseifThen,luaElse,luaThenEnd,luaBlock,luaLoopBlock,luaIn,luaStatement + +syn match luaParenError ")" +syn match luaBraceError "}" +syn match luaError "\<\%(end\|else\|elseif\|then\|until\|in\)\>" + +" function ... end +syn region luaFunctionBlock transparent matchgroup=luaFunction start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn + +" if ... then +syn region luaIfThen transparent matchgroup=luaCond start="\" end="\"me=e-4 contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaIn nextgroup=luaThenEnd skipwhite skipempty + +" then ... end +syn region luaThenEnd contained transparent matchgroup=luaCond start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaThenEnd,luaIn + +" elseif ... then +syn region luaElseifThen contained transparent matchgroup=luaCond start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn + +" else +syn keyword luaElse contained else + +" do ... end +syn region luaBlock transparent matchgroup=luaStatement start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn + +" samepage ... end +syn region luaBlock transparent matchgroup=luaStatement start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn +" crosspage ... end +syn region luaBlock transparent matchgroup=luaStatement start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn + +" repeat ... until +syn region luaLoopBlock transparent matchgroup=luaRepeat start="\" end="\" contains=ALLBUT,luaTodo,luaSpecial,luaElseifThen,luaElse,luaThenEnd,luaIn + +" while ... do +syn region luaLoopBlock transparent matchgroup=luaRepeat start="\" end="\"me=e-2 contains=ALLBUT,luaTodo,luaSpecial,luaIfThen,luaElseifThen,luaElse,luaThenEnd,luaIn nextgroup=luaBlock skipwhite skipempty + +" for ... do and for ... in ... do +syn region luaLoopBlock transparent matchgroup=luaRepeat start="\" end="\"me=e-2 contains=ALLBUT,luaTodo,luaSpecial,luaIfThen,luaElseifThen,luaElse,luaThenEnd nextgroup=luaBlock skipwhite skipempty + +syn keyword luaIn contained in + +" other keywords +syn keyword luaStatement return local break goto +syn match luaLabel "::\I\i*::" +syn match l65Label "@@\k\+" +syn match l65Label "@\k\+" +syn match l65SChar "[#!\\]" +syn match l65SFunc "[\\]" +syn keyword luaOperator and or not +syn keyword luaConstant nil true false + +syn region l65PreProc start="^#\s*\(pragma\>\)" skip="\\$" end="$" keepend + +" Strings +syn match luaSpecial contained #\\[\\abfnrtvz'"]\|\\b[01]\{2}\|\\x[[:xdigit:]]\{2}\|\\[[:digit:]]\{,3}# +syn region luaString2 matchgroup=luaString start="\[\z(=*\)\[" end="\]\z1\]" contains=@Spell +syn region luaString start=+'+ end=+'+ skip=+\\\\\|\\'+ contains=luaSpecial,@Spell +syn region luaString start=+"+ end=+"+ skip=+\\\\\|\\"+ contains=luaSpecial,@Spell + +" integer number +syn match luaNumber "\<\d\+\>" +" hex numbers +syn match luaNumber "\<0[xX][[:xdigit:].]\+\%([pP][-+]\=\d\+\)\=\>" +" bin numbers +syn match luaNumber "\<0[bB][01]\+\>" +" floating point number, with dot, optional exponent +syn match luaNumber "\<\d\+\.\d*\%([eE][-+]\=\d\+\)\=\>" +" floating point number, starting with a dot, optional exponent +syn match luaNumber "\.\d\+\%([eE][-+]\=\d\+\)\=\>" +" floating point number, without dot, with exponent +syn match luaNumber "\<\d\+[eE][-+]\=\d\+\>" + +syn keyword luaFunc assert collectgarbage dofile error next +syn keyword luaFunc print rawget rawset tonumber tostring type _VERSION + +syn keyword luaFunc getmetatable setmetatable +syn keyword luaFunc ipairs pairs +syn keyword luaFunc pcall xpcall +syn keyword luaFunc _G loadfile rawequal require +syn keyword luaFunc load select +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn keyword luaFunc _ENV rawlen +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ +syn match luaFunc /\/ + +synt match luaSemiCol ";" + +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ + +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ + +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ + + +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ + +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ + +" These are only if defined by user for VCS cross bank calls. +" Remove these lines if desired. +syn match l65Opcode /\/ +syn match l65Opcode /\/ +syn match l65Opcode /\/ +" Common alias, reomve if desired. +syn match l65Opcode /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ +syn match l65Keyword /\/ + +" Define the default highlighting. +" For version 5.7 and earlier: only when not done already +" For version 5.8 and later: only when an item doesn't have highlighting yet +if version >= 508 || !exists("did_l65_syntax_inits") + if version < 508 + let did_l65_syntax_inits = 1 + command -nargs=+ HiLink hi link + else + command -nargs=+ HiLink hi def link + endif + + HiLink luaStatement Statement + HiLink luaRepeat Repeat + HiLink luaFor Repeat + HiLink luaString String + HiLink luaString2 String + HiLink luaNumber Number + HiLink luaOperator Operator + HiLink luaIn Operator + HiLink luaConstant Constant + HiLink luaCond Conditional + HiLink luaElse Conditional + HiLink luaFunction Function + HiLink luaComment Comment + HiLink luaTodo Todo + HiLink luaTable Structure + HiLink luaError Error + HiLink luaParenError Error + HiLink luaBraceError Error + HiLink luaSpecial SpecialChar + HiLink luaFunc Identifier + HiLink luaLabel Label + + HiLink luaSemiCol Delimiter + + HiLink l65PreProc PreProc + HiLink l65Label Special + HiLink l65Keyword Identifier + HiLink l65Opcode Type + HiLink l65SChar Operator + HiLink l65SFunc Function + + delcommand HiLink +endif + +let b:current_syntax = "l65" + +let &cpo = s:cpo_save +unlet s:cpo_save +" vim: et ts=8 sw=2