/* * HalfNES by Andrew Hoffman * Licensed under the GNU GPL Version 3. See LICENSE file */ package com.grapeshot.halfnes.mappers; import com.grapeshot.halfnes.*; public class MMC1Mapper extends Mapper { private int mmc1shift = 0; private int mmc1latch = 0; private int mmc1ctrl = 0xc; private int mmc1chr0 = 0; private int mmc1chr1 = 0; private int mmc1prg = 0; private boolean soromlatch = false; private double cpucycleprev = 0; // for Bill and Ted fix private long framecountprev = 0; @Override public void loadrom() throws BadMapperException { // needs to be in every mapper. Fill with initial cfg super.loadrom(); for (int i = 0; i < 32; ++i) { prg_map[i] = (1024 * i) & (prgsize - 1); } for (int i = 0; i < 8; ++i) { chr_map[i] = (1024 * i) & (chrsize - 1); } setbanks(); } @Override public final void cartWrite(final int addr, final int data) { if (addr < 0x8000 || addr > 0xffff) { super.cartWrite(addr, data); return; } if (cpu.clocks == cpucycleprev && cpuram.apu.nes.framecount == framecountprev) { return; //bill and ted fix - prevents 2 writes too close together //from being acknowledged //if I ever go to a cycle based core instead of opcode based this needs to change. } framecountprev = cpuram.apu.nes.framecount; //and this is extremely ugly/likely to break //but is needed to prevent the Bill+Ted fix from breaking Dr Mario intro. cpucycleprev = cpu.clocks; if (((data & (utils.BIT7)) != 0)) { // reset shift register mmc1shift = 0; mmc1latch = 0; mmc1ctrl |= 0xc; setbanks(); return; } mmc1shift = (mmc1shift >> 1) + (data & 1) * 16; ++mmc1latch; // mmc1shift &= 0x1f; if (mmc1latch < 5) { return; // no need to do anything } else { if (addr >= 0x8000 && addr <= 0x9fff) { // mmc1control mmc1ctrl = mmc1shift & 0x1f; MirrorType mirtype; switch (mmc1ctrl & 3) { case 0: mirtype = MirrorType.SS_MIRROR0; break; case 1: mirtype = MirrorType.SS_MIRROR1; break; case 2: mirtype = MirrorType.V_MIRROR; break; default: mirtype = MirrorType.H_MIRROR; break; } setmirroring(mirtype); } else if (addr >= 0xa000 && addr <= 0xbfff) { // mmc1chr0 mmc1chr0 = mmc1shift & 0x1f; if (prgsize > 262144) { //SOROM boards use the high bit of CHR to switch between 1st and last //256k of the PRG ROM mmc1chr0 &= 0xf; soromlatch = ((mmc1shift & (utils.BIT4)) != 0); } } else if (addr >= 0xc000 && addr <= 0xdfff) { // mmc1chr1 mmc1chr1 = mmc1shift & 0x1f; if (prgsize > 262144) { mmc1chr1 &= 0xf; } } else if (addr >= 0xe000 && addr <= 0xffff) { // mmc1prg mmc1prg = mmc1shift & 0xf; } // System.err.println("Mapper Bankswitch: Write " + utils.hex(mmc1shift) + " @ " + utils.hex(addr)); setbanks(); mmc1latch = 0; mmc1shift = 0; } } private void setbanks() { // chr bank 0 if (((mmc1ctrl & (utils.BIT4)) != 0)) { // 4k bank mode for (int i = 0; i < 4; ++i) { chr_map[i] = (1024 * (i + 4 * mmc1chr0)) % chrsize; } for (int i = 0; i < 4; ++i) { chr_map[i + 4] = (1024 * (i + 4 * mmc1chr1)) % chrsize; } } else { // 8k bank mode for (int i = 0; i < 8; ++i) { chr_map[i] = (1024 * (i + 8 * (mmc1chr0 >> 1))) % chrsize; } } // prg bank if (!((mmc1ctrl & (utils.BIT3)) != 0)) { // 32k switch // ignore low bank bit for (int i = 0; i < 32; ++i) { prg_map[i] = (1024 * i + 32768 * (mmc1prg >> 1)) % prgsize; } } else if (!((mmc1ctrl & (utils.BIT2)) != 0)) { // fix 1st bank, 16k switch 2nd bank for (int i = 0; i < 16; ++i) { prg_map[i] = (1024 * i); } for (int i = 0; i < 16; ++i) { prg_map[i + 16] = (1024 * i + 16384 * mmc1prg) % prgsize; } } else { // fix last bank, switch 1st bank for (int i = 0; i < 16; ++i) { prg_map[i] = (1024 * i + 16384 * mmc1prg) % prgsize; } for (int i = 1; i <= 16; ++i) { prg_map[32 - i] = (prgsize - (1024 * i)); if ((prg_map[32 - i]) > 262144) { prg_map[32 - i] -= 262144; } } } //if more thn 256k ROM AND SOROM latch is on if (soromlatch && (prgsize > 262144)) { //add 256k to all of the prg bank #s for (int i = 0; i < prg_map.length; ++i) { prg_map[i] += 262144; } } //utils.printarray(prg_map); } }