[NES] Added MMC3 mapper.

This commit is contained in:
g012 2017-12-22 01:42:54 +01:00
parent 99deb2b875
commit d740492e8f
2 changed files with 112 additions and 25 deletions

View File

@ -47,7 +47,7 @@ Table of Contents
* [resolve()](#resolve)
* [genbin([filler])](#genbinfiller)
* [writebin(filename)](#writebinfilename)
* [writesym(filename)](#writesymfilename)
* [writesym(filename [, format])](#writesymfilename--format)
* [Parser Functions](#parser-functions)
* [Pragmas](#pragmas)
* [syntax6502 on|off](#syntax6502-onoff)
@ -466,9 +466,13 @@ Return a table, where each entry is a byte.
Write the final binary into `filename`.
#### writesym(filename)
#### writesym(filename [, format])
Write a DASM symbol file into `filename` for debuggers. The last `_` in a label is turned into a `.`, to get stripping of the prefixed global label working in Stella. As such, it's best to avoid using `_` in local label names, after the initial one.
Write one or more symbol files into `filename` (prefix in case of multiple outputs) for debuggers. The last `_` in a label is turned into a `.`, to get stripping of the prefixed global label working in Stella. As such, it's best to avoid using `_` in local label names, after the initial one.
`format` defaults to 'dasm'.
All platforms: 'dasm', 'lua'.
NES: 'mesen', 'fceux'.
### Parser Functions

127
nes.l65
View File

@ -62,10 +62,11 @@ end
cpu.getsym_as.mesen = function() -- .mlb
local ins,fmt = table.insert,string.format
local s = getsym(function(a,l)
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 and a < 0x10000 then prefix[1]='P' a=a-0x8000
elseif a >= 0x8000 then prefix[1]='P' a=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
@ -79,7 +80,7 @@ cpu.getsym_as.fceux = function(filename) -- .nl, multiple files
local s = getsym(function(a,l)
local s = fmt("$%04x#%s#", a, l)
if a < 0x8000 then ins(ram, s)
else
elseif a < 0x10000 then
local a_org = symbolsorg[l] or a
local bank = math.floor((a_org - 0x8000) / 0x4000)
if not rom[bank] then rom[bank] = {} end
@ -98,11 +99,13 @@ cpu.getsym_as.fceux = function(filename) -- .nl, multiple files
end
end
mappers = {}
vblank_waitbegin = function()
local l=label() lda PPUSTAT bpl l
local l=label() bit PPUSTAT bpl l
end
vblank_waitend = function()
local l=label() lda PPUSTAT bmi l
local l=label() bit PPUSTAT bmi l
end
ppu_addr = function(addr)
@ -180,12 +183,12 @@ read_joys_even = function(dst1, dst2)
end
init = function()
sei cld
ldx #0x40 stx SPECIO2
ldx #0xff txs inx stx PPUCTRL stx PPUMASK stx DMCFREQ
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()
-- stop APU channels
lda #0 sta SNDCNT
lda #0 sta SNDCNT -- stop APU channels
-- clear CPU RAM
@_zeroram
sta 0x0000,x sta 0x0100,x sta 0x0200,x sta 0x0300,x
@ -195,11 +198,11 @@ init = function()
-- clear OAM
oamcache_clear() oamcache_flush()
-- clear PPU RAM
lda PPUSTAT ppu_addr(0x2000) tax ldy #0x10
bit PPUSTAT ppu_addr(0x2000) tax ldy #0x10
@_zeroppu
sta PPUDATA dex bne _zeroppu dey bne _zeroppu
-- reset latch
lda PPUSTAT
bit PPUSTAT -- reset latch
if mappers.init then mappers.init() end
end
-- NES 2.0 (backward compatible with iNES)
@ -259,25 +262,24 @@ header = function(t)
dc.b tv, 0, 0, 0
-- update table with defaulted values
t.prgsize = prgsize
t.chrsize = chrsize
t.wramsize = wramsize
t.bramsize = bramsize
t.chrbramsize = chrbramsize
t.chrramsize = chrramsize
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
mappers = {}
local n0ne = function(x) return not x or x == 0 end
local val0 = function(x) return x and x or 0 end
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, "prgrom must be 16 or 32kB")
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, "chrrom/ram must be 8kB")
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'}
@ -292,6 +294,87 @@ mappers.MMC1 = function(t)
end
mappers[1] = mappers.MMC1
--[[
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.
t.prgmap is an optional function taking a prgrom bank index and returning its rorg value.
Default is to map even banks to 0x8000, and odd to 0xa000.
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
local chrstart = 0x8000 + bc*0x2000
local cc = t.chrsize//0x400
for ci=0,cc-1 do
local o = chrstart + ci*0x400
_ENV['chrrom'..ci] = location{o, o+0x3ff, name='chrrom'..ci}
end
function switchprgrom(slot, bankix)
assert(slot<2)
lda #6+slot sta 0x8000
assert(bankix < bc)
bankix = bc-1-bankix -- reverse index order, since we set 0 as last
lda #bankix sta 0x8001
end
local a12inv = false
function seta12inv(enabled) a12inv = enabled end
-- slot [0, 7], each slot is 1kB counting in order, regardless of a12inv state
function switchchrrom(slot, bankix)
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(0, 2) switchprgrom(1, 3)
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