diff options
Diffstat (limited to 'source/mappers.js')
-rw-r--r-- | source/mappers.js | 1032 |
1 files changed, 1032 insertions, 0 deletions
diff --git a/source/mappers.js b/source/mappers.js new file mode 100644 index 0000000..58d979a --- /dev/null +++ b/source/mappers.js @@ -0,0 +1,1032 @@ +/* +JSNES, based on Jamie Sanders' vNES +Copyright (C) 2010 Ben Firshman + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +JSNES.Mappers = {}; + +JSNES.Mappers[0] = function(nes) { + this.nes = nes; +}; + +JSNES.Mappers[0].prototype = { + reset: function() { + this.joy1StrobeState = 0; + this.joy2StrobeState = 0; + this.joypadLastWrite = 0; + + this.mousePressed = false; + this.mouseX = null; + this.mouseY = null; + }, + + write: function(address, value) { + if (address < 0x2000) { + // Mirroring of RAM: + this.nes.cpu.mem[address & 0x7FF] = value; + + } + else if (address > 0x4017) { + this.nes.cpu.mem[address] = value; + if (address >= 0x6000 && address < 0x8000) { + // Write to SaveRAM. Store in file: + // TODO: not yet + //if(this.nes.rom!=null) + // this.nes.rom.writeBatteryRam(address,value); + } + } + else if (address > 0x2007 && address < 0x4000) { + this.regWrite(0x2000 + (address & 0x7), value); + } + else { + this.regWrite(address, value); + } + }, + + load: function(address) { + // Wrap around: + address &= 0xFFFF; + + // Check address range: + if (address > 0x4017) { + // ROM: + return this.nes.cpu.mem[address]; + } + else if (address >= 0x2000) { + // I/O Ports. + return this.regLoad(address); + } + else { + // RAM (mirrored) + return this.nes.cpu.mem[address & 0x7FF]; + } + }, + + regLoad: function(address) { + switch (address >> 12) { // use fourth nibble (0xF000) + case 0: + break; + + case 1: + break; + + case 2: + // Fall through to case 3 + case 3: + // PPU Registers + switch (address & 0x7) { + case 0x0: + // 0x2000: + // PPU Control Register 1. + // (the value is stored both + // in main memory and in the + // PPU as flags): + // (not in the real NES) + return this.nes.cpu.mem[0x2000]; + + case 0x1: + // 0x2001: + // PPU Control Register 2. + // (the value is stored both + // in main memory and in the + // PPU as flags): + // (not in the real NES) + return this.nes.cpu.mem[0x2001]; + + case 0x2: + // 0x2002: + // PPU Status Register. + // The value is stored in + // main memory in addition + // to as flags in the PPU. + // (not in the real NES) + return this.nes.ppu.readStatusRegister(); + + case 0x3: + return 0; + + case 0x4: + // 0x2004: + // Sprite Memory read. + return this.nes.ppu.sramLoad(); + case 0x5: + return 0; + + case 0x6: + return 0; + + case 0x7: + // 0x2007: + // VRAM read: + return this.nes.ppu.vramLoad(); + } + break; + case 4: + // Sound+Joypad registers + switch (address - 0x4015) { + case 0: + // 0x4015: + // Sound channel enable, DMC Status + return this.nes.papu.readReg(address); + + case 1: + // 0x4016: + // Joystick 1 + Strobe + return this.joy1Read(); + + case 2: + // 0x4017: + // Joystick 2 + Strobe + if (this.mousePressed) { + + // Check for white pixel nearby: + var sx = Math.max(0, this.mouseX - 4); + var ex = Math.min(256, this.mouseX + 4); + var sy = Math.max(0, this.mouseY - 4); + var ey = Math.min(240, this.mouseY + 4); + var w = 0; + + for (var y=sy; y<ey; y++) { + for (var x=sx; x<ex; x++) { + + if (this.nes.ppu.buffer[(y<<8)+x] == 0xFFFFFF) { + w |= 0x1<<3; + console.debug("Clicked on white!"); + break; + } + } + } + + w |= (this.mousePressed?(0x1<<4):0); + return (this.joy2Read()|w) & 0xFFFF; + } + else { + return this.joy2Read(); + } + + } + break; + } + return 0; + }, + + regWrite: function(address, value) { + switch (address) { + case 0x2000: + // PPU Control register 1 + this.nes.cpu.mem[address] = value; + this.nes.ppu.updateControlReg1(value); + break; + + case 0x2001: + // PPU Control register 2 + this.nes.cpu.mem[address] = value; + this.nes.ppu.updateControlReg2(value); + break; + + case 0x2003: + // Set Sprite RAM address: + this.nes.ppu.writeSRAMAddress(value); + break; + + case 0x2004: + // Write to Sprite RAM: + this.nes.ppu.sramWrite(value); + break; + + case 0x2005: + // Screen Scroll offsets: + this.nes.ppu.scrollWrite(value); + break; + + case 0x2006: + // Set VRAM address: + this.nes.ppu.writeVRAMAddress(value); + break; + + case 0x2007: + // Write to VRAM: + this.nes.ppu.vramWrite(value); + break; + + case 0x4014: + // Sprite Memory DMA Access + this.nes.ppu.sramDMA(value); + break; + + case 0x4015: + // Sound Channel Switch, DMC Status + this.nes.papu.writeReg(address, value); + break; + + case 0x4016: + // Joystick 1 + Strobe + if (value === 0 && this.joypadLastWrite === 1) { + this.joy1StrobeState = 0; + this.joy2StrobeState = 0; + } + this.joypadLastWrite = value; + break; + + case 0x4017: + // Sound channel frame sequencer: + this.nes.papu.writeReg(address, value); + break; + + default: + // Sound registers + ////System.out.println("write to sound reg"); + if (address >= 0x4000 && address <= 0x4017) { + this.nes.papu.writeReg(address,value); + } + + } + }, + + joy1Read: function() { + var ret; + + switch (this.joy1StrobeState) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + ret = this.nes.keyboard.state1[this.joy1StrobeState]; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + ret = 0; + break; + case 19: + ret = 1; + break; + default: + ret = 0; + } + + this.joy1StrobeState++; + if (this.joy1StrobeState == 24) { + this.joy1StrobeState = 0; + } + + return ret; + }, + + joy2Read: function() { + var ret; + + this.joy2StrobeState++; + if (this.joy2StrobeState == 24) { + this.joy2StrobeState = 0; + } + + switch (this.joy2StrobeState) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + ret = this.nes.keyboard.state2[this.joy2StrobeState]; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + ret = 0; + break; + case 18: + ret = 1; + break; + default: + ret = 0; + } + + return ret; + }, + + loadROM: function() { + if (!this.nes.rom.valid || this.nes.rom.romCount < 1) { + alert("NoMapper: Invalid ROM! Unable to load."); + return; + } + + // Load ROM into memory: + this.loadPRGROM(); + + // Load CHR-ROM: + this.loadCHRROM(); + + // Load Battery RAM (if present): + this.loadBatteryRam(); + + // Reset IRQ: + //nes.getCpu().doResetInterrupt(); + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); + }, + + loadPRGROM: function() { + if (this.nes.rom.romCount > 1) { + // Load the two first banks into memory. + this.loadRomBank(0, 0x8000); + this.loadRomBank(1, 0xC000); + } + else { + // Load the one bank into both memory locations: + this.loadRomBank(0, 0x8000); + this.loadRomBank(0, 0xC000); + } + }, + + loadCHRROM: function() { + ////System.out.println("Loading CHR ROM.."); + if (this.nes.rom.vromCount > 0) { + if (this.nes.rom.vromCount == 1) { + this.loadVromBank(0,0x0000); + this.loadVromBank(0,0x1000); + } + else { + this.loadVromBank(0,0x0000); + this.loadVromBank(1,0x1000); + } + } + else { + //System.out.println("There aren't any CHR-ROM banks.."); + } + }, + + loadBatteryRam: function() { + if (this.nes.rom.batteryRam) { + var ram = this.nes.rom.batteryRam; + if (ram !== null && ram.length == 0x2000) { + // Load Battery RAM into memory: + JSNES.Utils.arraycopy(ram, 0, this.nes.cpu.mem, 0x6000, 0x2000); + } + } + }, + + loadRomBank: function(bank, address) { + // Loads a ROM bank into the specified address. + bank %= this.nes.rom.romCount; + //var data = this.nes.rom.rom[bank]; + //cpuMem.write(address,data,data.length); + JSNES.Utils.arraycopy(this.nes.rom.rom[bank], 0, this.nes.cpu.mem, address, 16384); + }, + + loadVromBank: function(bank, address) { + if (this.nes.rom.vromCount === 0) { + return; + } + this.nes.ppu.triggerRendering(); + + JSNES.Utils.arraycopy(this.nes.rom.vrom[bank % this.nes.rom.vromCount], + 0, this.nes.ppu.vramMem, address, 4096); + + var vromTile = this.nes.rom.vromTile[bank % this.nes.rom.vromCount]; + JSNES.Utils.arraycopy(vromTile, 0, this.nes.ppu.ptTile,address >> 4, 256); + }, + + load32kRomBank: function(bank, address) { + this.loadRomBank((bank*2) % this.nes.rom.romCount, address); + this.loadRomBank((bank*2+1) % this.nes.rom.romCount, address+16384); + }, + + load8kVromBank: function(bank4kStart, address) { + if (this.nes.rom.vromCount === 0) { + return; + } + this.nes.ppu.triggerRendering(); + + this.loadVromBank((bank4kStart) % this.nes.rom.vromCount, address); + this.loadVromBank((bank4kStart + 1) % this.nes.rom.vromCount, + address + 4096); + }, + + load1kVromBank: function(bank1k, address) { + if (this.nes.rom.vromCount === 0) { + return; + } + this.nes.ppu.triggerRendering(); + + var bank4k = parseInt(bank1k / 4, 10) % this.nes.rom.vromCount; + var bankoffset = (bank1k % 4) * 1024; + JSNES.Utils.arraycopy(this.nes.rom.vrom[bank4k], 0, + this.nes.ppu.vramMem, bankoffset, 1024); + + // Update tiles: + var vromTile = this.nes.rom.vromTile[bank4k]; + var baseIndex = address >> 4; + for (var i = 0; i < 64; i++) { + this.nes.ppu.ptTile[baseIndex+i] = vromTile[((bank1k%4) << 6) + i]; + } + }, + + load2kVromBank: function(bank2k, address) { + if (this.nes.rom.vromCount === 0) { + return; + } + this.nes.ppu.triggerRendering(); + + var bank4k = parseInt(bank2k / 2, 10) % this.nes.rom.vromCount; + var bankoffset = (bank2k % 2) * 2048; + JSNES.Utils.arraycopy(this.nes.rom.vrom[bank4k], bankoffset, + this.nes.ppu.vramMem, address, 2048); + + // Update tiles: + var vromTile = this.nes.rom.vromTile[bank4k]; + var baseIndex = address >> 4; + for (var i = 0; i < 128; i++) { + this.nes.ppu.ptTile[baseIndex+i] = vromTile[((bank2k%2) << 7) + i]; + } + }, + + load8kRomBank: function(bank8k, address) { + var bank16k = parseInt(bank8k / 2, 10) % this.nes.rom.romCount; + var offset = (bank8k % 2) * 8192; + + //this.nes.cpu.mem.write(address,this.nes.rom.rom[bank16k],offset,8192); + JSNES.Utils.arraycopy(this.nes.rom.rom[bank16k], offset, + this.nes.cpu.mem, address, 8192); + }, + + clockIrqCounter: function() { + // Does nothing. This is used by the MMC3 mapper. + }, + + latchAccess: function(address) { + // Does nothing. This is used by MMC2. + } +}; + + +JSNES.Mappers[1] = function(nes) { + this.nes = nes; +}; + +JSNES.Mappers[1].prototype = new JSNES.Mappers[0](); + +JSNES.Mappers[1].prototype.reset = function() { + JSNES.Mappers[0].prototype.reset.apply(this); + + // 5-bit buffer: + this.regBuffer = 0; + this.regBufferCounter = 0; + + // Register 0: + this.mirroring = 0; + this.oneScreenMirroring = 0; + this.prgSwitchingArea = 1; + this.prgSwitchingSize = 1; + this.vromSwitchingSize = 0; + + // Register 1: + this.romSelectionReg0 = 0; + + // Register 2: + this.romSelectionReg1 = 0; + + // Register 3: + this.romBankSelect = 0; +}; + +JSNES.Mappers[1].prototype.write = function(address, value) { + // Writes to addresses other than MMC registers are handled by NoMapper. + if (address < 0x8000) { + JSNES.Mappers[0].prototype.write.apply(this, arguments); + return; + } + + // See what should be done with the written value: + if ((value & 128) !== 0) { + + // Reset buffering: + this.regBufferCounter = 0; + this.regBuffer = 0; + + // Reset register: + if (this.getRegNumber(address) === 0) { + + this.prgSwitchingArea = 1; + this.prgSwitchingSize = 1; + + } + } + else { + + // Continue buffering: + //regBuffer = (regBuffer & (0xFF-(1<<regBufferCounter))) | ((value & (1<<regBufferCounter))<<regBufferCounter); + this.regBuffer = (this.regBuffer & (0xFF - (1 << this.regBufferCounter))) | ((value & 1) << this.regBufferCounter); + this.regBufferCounter++; + + if (this.regBufferCounter == 5) { + // Use the buffered value: + this.setReg(this.getRegNumber(address), this.regBuffer); + + // Reset buffer: + this.regBuffer = 0; + this.regBufferCounter = 0; + } + } +}; + +JSNES.Mappers[1].prototype.setReg = function(reg, value) { + var tmp; + + switch (reg) { + case 0: + // Mirroring: + tmp = value & 3; + if (tmp !== this.mirroring) { + // Set mirroring: + this.mirroring = tmp; + if ((this.mirroring & 2) === 0) { + // SingleScreen mirroring overrides the other setting: + this.nes.ppu.setMirroring( + this.nes.rom.SINGLESCREEN_MIRRORING); + } + // Not overridden by SingleScreen mirroring. + else if ((this.mirroring & 1) !== 0) { + this.nes.ppu.setMirroring( + this.nes.rom.HORIZONTAL_MIRRORING + ); + } + else { + this.nes.ppu.setMirroring(this.nes.rom.VERTICAL_MIRRORING); + } + } + + // PRG Switching Area; + this.prgSwitchingArea = (value >> 2) & 1; + + // PRG Switching Size: + this.prgSwitchingSize = (value >> 3) & 1; + + // VROM Switching Size: + this.vromSwitchingSize = (value >> 4) & 1; + + break; + + case 1: + // ROM selection: + this.romSelectionReg0 = (value >> 4) & 1; + + // Check whether the cart has VROM: + if (this.nes.rom.vromCount > 0) { + + // Select VROM bank at 0x0000: + if (this.vromSwitchingSize === 0) { + + // Swap 8kB VROM: + if (this.romSelectionReg0 === 0) { + this.load8kVromBank((value & 0xF), 0x0000); + } + else { + this.load8kVromBank( + parseInt(this.nes.rom.vromCount / 2, 10) + + (value & 0xF), + 0x0000 + ); + } + + } + else { + // Swap 4kB VROM: + if (this.romSelectionReg0 === 0) { + this.loadVromBank((value & 0xF), 0x0000); + } + else { + this.loadVromBank( + parseInt(this.nes.rom.vromCount / 2, 10) + + (value & 0xF), + 0x0000 + ); + } + } + } + + break; + + case 2: + // ROM selection: + this.romSelectionReg1 = (value >> 4) & 1; + + // Check whether the cart has VROM: + if (this.nes.rom.vromCount > 0) { + + // Select VROM bank at 0x1000: + if (this.vromSwitchingSize === 1) { + // Swap 4kB of VROM: + if (this.romSelectionReg1 === 0) { + this.loadVromBank((value & 0xF), 0x1000); + } + else { + this.loadVromBank( + parseInt(this.nes.rom.vromCount / 2, 10) + + (value & 0xF), + 0x1000 + ); + } + } + } + break; + + default: + // Select ROM bank: + // ------------------------- + tmp = value & 0xF; + var bank; + var baseBank = 0; + + if (this.nes.rom.romCount >= 32) { + // 1024 kB cart + if (this.vromSwitchingSize === 0) { + if (this.romSelectionReg0 === 1) { + baseBank = 16; + } + } + else { + baseBank = (this.romSelectionReg0 + | (this.romSelectionReg1 << 1)) << 3; + } + } + else if (this.nes.rom.romCount >= 16) { + // 512 kB cart + if (this.romSelectionReg0 === 1) { + baseBank = 8; + } + } + + if (this.prgSwitchingSize === 0) { + // 32kB + bank = baseBank + (value & 0xF); + this.load32kRomBank(bank, 0x8000); + } + else { + // 16kB + bank = baseBank * 2 + (value & 0xF); + if (this.prgSwitchingArea === 0) { + this.loadRomBank(bank, 0xC000); + } + else { + this.loadRomBank(bank, 0x8000); + } + } + } +}; + +// Returns the register number from the address written to: +JSNES.Mappers[1].prototype.getRegNumber = function(address) { + if (address >= 0x8000 && address <= 0x9FFF) { + return 0; + } + else if (address >= 0xA000 && address <= 0xBFFF) { + return 1; + } + else if (address >= 0xC000 && address <= 0xDFFF) { + return 2; + } + else { + return 3; + } +}; + +JSNES.Mappers[1].prototype.loadROM = function(rom) { + if (!this.nes.rom.valid) { + alert("MMC1: Invalid ROM! Unable to load."); + return; + } + + // Load PRG-ROM: + this.loadRomBank(0, 0x8000); // First ROM bank.. + this.loadRomBank(this.nes.rom.romCount - 1, 0xC000); // ..and last ROM bank. + + // Load CHR-ROM: + this.loadCHRROM(); + + // Load Battery RAM (if present): + this.loadBatteryRam(); + + // Do Reset-Interrupt: + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); +}; + +JSNES.Mappers[1].prototype.switchLowHighPrgRom = function(oldSetting) { + // not yet. +}; + +JSNES.Mappers[1].prototype.switch16to32 = function() { + // not yet. +}; + +JSNES.Mappers[1].prototype.switch32to16 = function() { + // not yet. +}; + + +JSNES.Mappers[2] = function(nes) { + this.nes = nes; +}; + +JSNES.Mappers[2].prototype = new JSNES.Mappers[0](); + +JSNES.Mappers[2].prototype.write = function(address, value) { + // Writes to addresses other than MMC registers are handled by NoMapper. + if (address < 0x8000) { + JSNES.Mappers[0].prototype.write.apply(this, arguments); + return; + } + + else { + // This is a ROM bank select command. + // Swap in the given ROM bank at 0x8000: + this.loadRomBank(value, 0x8000); + } +}; + +JSNES.Mappers[2].prototype.loadROM = function(rom) { + if (!this.nes.rom.valid) { + alert("UNROM: Invalid ROM! Unable to load."); + return; + } + + // Load PRG-ROM: + this.loadRomBank(0, 0x8000); + this.loadRomBank(this.nes.rom.romCount - 1, 0xC000); + + // Load CHR-ROM: + this.loadCHRROM(); + + // Do Reset-Interrupt: + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); +}; + + +JSNES.Mappers[4] = function(nes) { + this.nes = nes; + + this.CMD_SEL_2_1K_VROM_0000 = 0; + this.CMD_SEL_2_1K_VROM_0800 = 1; + this.CMD_SEL_1K_VROM_1000 = 2; + this.CMD_SEL_1K_VROM_1400 = 3; + this.CMD_SEL_1K_VROM_1800 = 4; + this.CMD_SEL_1K_VROM_1C00 = 5; + this.CMD_SEL_ROM_PAGE1 = 6; + this.CMD_SEL_ROM_PAGE2 = 7; + + this.command = null; + this.prgAddressSelect = null; + this.chrAddressSelect = null; + this.pageNumber = null; + this.irqCounter = null; + this.irqLatchValue = null; + this.irqEnable = null; + this.prgAddressChanged = false; +}; + +JSNES.Mappers[4].prototype = new JSNES.Mappers[0](); + +JSNES.Mappers[4].prototype.write = function(address, value) { + // Writes to addresses other than MMC registers are handled by NoMapper. + if (address < 0x8000) { + JSNES.Mappers[0].prototype.write.apply(this, arguments); + return; + } + + switch (address) { + case 0x8000: + // Command/Address Select register + this.command = value & 7; + var tmp = (value >> 6) & 1; + if (tmp != this.prgAddressSelect) { + this.prgAddressChanged = true; + } + this.prgAddressSelect = tmp; + this.chrAddressSelect = (value >> 7) & 1; + break; + + case 0x8001: + // Page number for command + this.executeCommand(this.command, value); + break; + + case 0xA000: + // Mirroring select + if ((value & 1) !== 0) { + this.nes.ppu.setMirroring( + this.nes.rom.HORIZONTAL_MIRRORING + ); + } + else { + this.nes.ppu.setMirroring(this.nes.rom.VERTICAL_MIRRORING); + } + break; + + case 0xA001: + // SaveRAM Toggle + // TODO + //nes.getRom().setSaveState((value&1)!=0); + break; + + case 0xC000: + // IRQ Counter register + this.irqCounter = value; + //nes.ppu.mapperIrqCounter = 0; + break; + + case 0xC001: + // IRQ Latch register + this.irqLatchValue = value; + break; + + case 0xE000: + // IRQ Control Reg 0 (disable) + //irqCounter = irqLatchValue; + this.irqEnable = 0; + break; + + case 0xE001: + // IRQ Control Reg 1 (enable) + this.irqEnable = 1; + break; + + default: + // Not a MMC3 register. + // The game has probably crashed, + // since it tries to write to ROM.. + // IGNORE. + } +}; + +JSNES.Mappers[4].prototype.executeCommand = function(cmd, arg) { + switch (cmd) { + case this.CMD_SEL_2_1K_VROM_0000: + // Select 2 1KB VROM pages at 0x0000: + if (this.chrAddressSelect === 0) { + this.load1kVromBank(arg, 0x0000); + this.load1kVromBank(arg + 1, 0x0400); + } + else { + this.load1kVromBank(arg, 0x1000); + this.load1kVromBank(arg + 1, 0x1400); + } + break; + + case this.CMD_SEL_2_1K_VROM_0800: + // Select 2 1KB VROM pages at 0x0800: + if (this.chrAddressSelect === 0) { + this.load1kVromBank(arg, 0x0800); + this.load1kVromBank(arg + 1, 0x0C00); + } + else { + this.load1kVromBank(arg, 0x1800); + this.load1kVromBank(arg + 1, 0x1C00); + } + break; + + case this.CMD_SEL_1K_VROM_1000: + // Select 1K VROM Page at 0x1000: + if (this.chrAddressSelect === 0) { + this.load1kVromBank(arg, 0x1000); + } + else { + this.load1kVromBank(arg, 0x0000); + } + break; + + case this.CMD_SEL_1K_VROM_1400: + // Select 1K VROM Page at 0x1400: + if (this.chrAddressSelect === 0) { + this.load1kVromBank(arg, 0x1400); + } + else { + this.load1kVromBank(arg, 0x0400); + } + break; + + case this.CMD_SEL_1K_VROM_1800: + // Select 1K VROM Page at 0x1800: + if (this.chrAddressSelect === 0) { + this.load1kVromBank(arg, 0x1800); + } + else { + this.load1kVromBank(arg, 0x0800); + } + break; + + case this.CMD_SEL_1K_VROM_1C00: + // Select 1K VROM Page at 0x1C00: + if (this.chrAddressSelect === 0) { + this.load1kVromBank(arg, 0x1C00); + }else { + this.load1kVromBank(arg, 0x0C00); + } + break; + + case this.CMD_SEL_ROM_PAGE1: + if (this.prgAddressChanged) { + // Load the two hardwired banks: + if (this.prgAddressSelect === 0) { + this.load8kRomBank( + ((this.nes.rom.romCount - 1) * 2), + 0xC000 + ); + } + else { + this.load8kRomBank( + ((this.nes.rom.romCount - 1) * 2), + 0x8000 + ); + } + this.prgAddressChanged = false; + } + + // Select first switchable ROM page: + if (this.prgAddressSelect === 0) { + this.load8kRomBank(arg, 0x8000); + } + else { + this.load8kRomBank(arg, 0xC000); + } + break; + + case this.CMD_SEL_ROM_PAGE2: + // Select second switchable ROM page: + this.load8kRomBank(arg, 0xA000); + + // hardwire appropriate bank: + if (this.prgAddressChanged) { + // Load the two hardwired banks: + if (this.prgAddressSelect === 0) { + this.load8kRomBank( + ((this.nes.rom.romCount - 1) * 2), + 0xC000 + ); + } + else { + this.load8kRomBank( + ((this.nes.rom.romCount - 1) * 2), + 0x8000 + ); + } + this.prgAddressChanged = false; + } + } +}; + +JSNES.Mappers[4].prototype.loadROM = function(rom) { + if (!this.nes.rom.valid) { + alert("MMC3: Invalid ROM! Unable to load."); + return; + } + + // Load hardwired PRG banks (0xC000 and 0xE000): + this.load8kRomBank(((this.nes.rom.romCount - 1) * 2), 0xC000); + this.load8kRomBank(((this.nes.rom.romCount - 1) * 2) + 1, 0xE000); + + // Load swappable PRG banks (0x8000 and 0xA000): + this.load8kRomBank(0, 0x8000); + this.load8kRomBank(1, 0xA000); + + // Load CHR-ROM: + this.loadCHRROM(); + + // Load Battery RAM (if present): + this.loadBatteryRam(); + + // Do Reset-Interrupt: + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); +}; |