/*
* HalfNES by Andrew Hoffman
* Licensed under the GNU GPL Version 3. See LICENSE file
*/
package com.grapeshot.halfnes.mappers;
import com.grapeshot.halfnes.*;
/**
*
* @author Andrew
*/
public class TengenRamboMapper extends Mapper {
private int whichbank = 0;
private boolean prgconfig = false, chrconfig = false,
chrmode1k = false, irqmode = false;
private int irqctrreload = 0;
private int irqctr = 0;
private boolean irqenable = false;
private boolean irqreload = false;
private int prgreg0 = 0, prgreg1 = 0, prgreg2 = 0;
private int[] chrreg = new int[8];
private boolean interrupted = false;
@Override
public void loadrom() throws BadMapperException {
//needs to be in every mapper. Fill with initial cfg
super.loadrom();
//on startup:
for (int i = 0; i < 8; ++i) {
prg_map[i] = (1024 * i);
prg_map[i + 8] = (1024 * i);
//yes this actually matters; MMC3 does NOT start up in a random state
//(at least Smash TV and TMNT3 expect certain banks w/o even setting up mapper)
}
for (int i = 1; i <= 32; ++i) {
prg_map[32 - i] = prgsize - (1024 * i);
}
for (int i = 0; i < 8; ++i) {
chr_map[i] = 0;
}
setprgregs();
//cpuram.setPrgRAMEnable(false);
}
@Override
public final void cartWrite(int addr, int data) {
if (addr < 0x8000 || addr > 0xffff) {
super.cartWrite(addr, data);
return;
}
//bankswitches here
//different register for even/odd writes
if (((addr & (utils.BIT0)) != 0)) {
//odd registers
if ((addr >= 0x8000) && (addr <= 0x9fff)) {
//bank change
//System.err.println("setting " + whichbank + " " + data + " " + prgconfig);
switch (whichbank) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
chrreg[whichbank] = data;
setupchr();
break;
case 6:
prgreg0 = data;
setprgregs();
break;
case 7:
//bank 7 always swappable, always in same place
prgreg1 = data;
setprgregs();
break;
case 8:
case 9:
//setup extra chr banks;
chrreg[whichbank - 2] = data;
break;
case 0xf:
prgreg2 = data;
setprgregs();
break;
}
} else if ((addr >= 0xA000) && (addr <= 0xbfff)) {
//prg ram write protect
//cpuram.setPrgRAMEnable(!utils.getbit(data, 7));
} else if ((addr >= 0xc000) && (addr <= 0xdfff)) {
//any value here reloads irq counter
irqreload = true;
irqmode = ((data & (utils.BIT0)) != 0);
} else if ((addr >= 0xe000) && (addr <= 0xffff)) {
//iany value here enables interrupts
irqenable = true;
}
} else {
//even registers
if ((addr >= 0x8000) && (addr <= 0x9fff)) {
//bank select
whichbank = data & 0xf;
chrmode1k = ((data & (utils.BIT5)) != 0);
prgconfig = ((data & (utils.BIT6)) != 0);
//if bit is false, 8000-9fff swappable and c000-dfff fixed to 2nd to last bank
//if bit is true, c000-dfff swappable and 8000-9fff fixed to 2nd to last bank
chrconfig = ((data & (utils.BIT7)) != 0);
//if false: 2 2k banks @ 0000-0fff, 4 1k banks in 1000-1fff
//if true: 4 1k banks @ 0000-0fff, 2 2k banks @ 1000-1fff
setupchr();
setprgregs();
} else if ((addr >= 0xA000) && (addr <= 0xbfff)) {
//mirroring setup
if (scrolltype != MirrorType.FOUR_SCREEN_MIRROR) {
setmirroring(((data & (utils.BIT0)) != 0) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR);
}
} else if ((addr >= 0xc000) && (addr <= 0xdfff)) {
//value written here used to reload irq counter _@ end of scanline_
irqctrreload = data;
irqreload = true;
} else if ((addr >= 0xe000) && (addr <= 0xffff)) {
//any value here disables IRQ and acknowledges
if (interrupted) {
--cpu.interrupt;
}
interrupted = false;
irqenable = false;
irqctr = irqctrreload;
}
}
}
private void setupchr() {
if (chrconfig) {
if (chrmode1k) {
setppubank(1, 0, chrreg[2]);
setppubank(1, 1, chrreg[3]);
setppubank(1, 2, chrreg[4]);
setppubank(1, 3, chrreg[5]);
setppubank(1, 4, chrreg[0]);
setppubank(1, 5, chrreg[6]);
setppubank(1, 6, chrreg[1]);
setppubank(1, 7, chrreg[7]);
} else {
setppubank(1, 0, chrreg[2]);
setppubank(1, 1, chrreg[3]);
setppubank(1, 2, chrreg[4]);
setppubank(1, 3, chrreg[5]);
//Lowest bit of bank number IS IGNORED for the 2k banks
setppubank(2, 4, (chrreg[0] >> 1) << 1);
setppubank(2, 6, (chrreg[1] >> 1) << 1);
}
} else {
if (chrmode1k) {
setppubank(1, 0, chrreg[0]);
setppubank(1, 1, chrreg[6]);
setppubank(1, 2, chrreg[1]);
setppubank(1, 3, chrreg[7]);
setppubank(1, 4, chrreg[2]);
setppubank(1, 5, chrreg[3]);
setppubank(1, 6, chrreg[4]);
setppubank(1, 7, chrreg[5]);
} else {
setppubank(1, 4, chrreg[2]);
setppubank(1, 5, chrreg[3]);
setppubank(1, 6, chrreg[4]);
setppubank(1, 7, chrreg[5]);
setppubank(2, 0, (chrreg[0] >> 1) << 1);
setppubank(2, 2, (chrreg[1] >> 1) << 1);
}
}
}
private void setprgregs() {
//no matter what, c000-dfff is last bank
if (!prgconfig) {
//map r6 to first 8k, r7 to 2nd, rf to 3rd
for (int i = 0; i < 8; ++i) {
prg_map[i] = (1024 * (i + (prgreg0 * 8))) % prgsize;
prg_map[i + 8] = (1024 * (i + (prgreg1 * 8))) % prgsize;
prg_map[i + 16] = (1024 * (i + (prgreg2 * 8))) % prgsize;
}
} else {
//map rf to 1st 8k, r6 to 2nd, r7 to 3rd
for (int i = 0; i < 8; ++i) {
prg_map[i] = (1024 * (i + (prgreg2 * 8))) % prgsize;
prg_map[i + 8] = (1024 * (i + (prgreg0 * 8))) % prgsize;
prg_map[i + 16] = (1024 * (i + (prgreg1 * 8))) % prgsize;
}
}
}
@Override
public void notifyscanline(int scanline) {
if (irqmode) {
return;
}
//Scanline counter
if (scanline > 239 && scanline != 261) {
//clocked on LAST line of vblank and all lines of frame. Not on 240.
return;
}
if (!ppu.mmc3CounterClocking()) {
return;
}
clockscanlinecounter();
}
int remainder;
boolean intnextcycle = false;
@Override
public void cpucycle(int cycles) {
if (intnextcycle) {
intnextcycle = false;
if (!interrupted) {
++cpu.interrupt;
interrupted = true;
}
}
if (!irqmode) {
return;
}
remainder += cycles;
for (int i = 0; i < remainder; ++i) {
if ((i & 3) == 0) {
clockscanlinecounter();
}
}
remainder %= 4;
}
public void clockscanlinecounter() {
if (irqreload) {
irqreload = false;
irqctr = irqctrreload + 1;
} else if (irqctr == 0) {
irqctr = irqctrreload;
} else {
if (--irqctr == 0 && irqenable) {
intnextcycle = true;
}
}
}
private void setppubank(int banksize, int bankpos, int banknum) {
// System.err.println(banksize + ", " + bankpos + ", "+ banknum);
for (int i = 0; i < banksize; ++i) {
chr_map[i + bankpos] = (1024 * ((banknum) + i)) % chrsize;
}
}
}