/*
* HalfNES by Andrew Hoffman
* Licensed under the GNU GPL Version 3. See LICENSE file
*/
package com.grapeshot.halfnes.mappers;
import com.grapeshot.halfnes.utils;
import com.grapeshot.halfnes.*;
import com.grapeshot.halfnes.audio.*;
/**
*
* @author Andrew
*/
public class FME7Mapper extends Mapper {
private int commandRegister = 0;
private int soundCommand = 0;
private int[] charbanks = new int[8]; //8 1k char rom banks
private int[] prgbanks = new int[4]; //4 8k prg banks - PLUS 1 8k fixed one
private boolean ramEnable = true;
private boolean ramSelect = false;
private int irqcounter = 0xffff; //really needs to be unsigned but we'll cheese it
private boolean irqenabled;
private boolean irqclock;
private boolean hasInitSound = false;
private final ExpansionSoundChip sndchip = new Sunsoft5BSoundChip();
private boolean interrupted = false;
public void loadrom() throws BadMapperException {
//needs to be in every mapper. Fill with initial cfg
super.loadrom();
//on startup:
prg_map = new int[40]; //(trollface)
//fixed bank maps to last 8k of rom, set everything else to last chunk
//as well.
for (int i = 1; i <= 40; ++i) {
prg_map[40 - i] = prgsize - (1024 * i);
}
for (int i = 0; i < 8; ++i) {
chr_map[i] = 0;
}
}
@Override
public final int cartRead(int addr) {
//five possible rom banks.
if (addr >= 0x6000) {
if (addr < 0x8000 && ramSelect) {
if (ramEnable) {
return prgram[addr - 0x6000];
} else {
return addr >> 8; //open bus
}
}
return prg[prg_map[(addr - 0x6000) >> 10] + (addr & 1023)];
}
return addr >> 8; //open bus
}
@Override
public final void cartWrite(final int addr, final int data) {
if (addr < 0x8000 || addr > 0xffff) {
super.cartWrite(addr, data);
return;
}
if (addr == 0x8000) {
//command register
commandRegister = data & 0xf;
} else if (addr == 0xc000) {
//sound command register
soundCommand = data & 0xf;
if (!hasInitSound) {
//only initialize the sound chip if anything writes a sound command.
cpuram.apu.addExpnSound(sndchip);
hasInitSound = true;
}
} else if (addr == 0xa000) {
//mapper data register
switch (commandRegister) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
//char bank switches
charbanks[commandRegister] = data;
setbanks();
break;
case 8:
ramEnable = ((data & (utils.BIT7)) != 0);
ramSelect = ((data & (utils.BIT6)) != 0);
prgbanks[0] = data & 0x3f;
setbanks();
break;
case 9:
case 0xa:
case 0xb:
//prg bank switch
prgbanks[commandRegister - 8] = data;
setbanks();
break;
case 0xc:
//mirroring select
switch (data & 3) {
case 0:
setmirroring(Mapper.MirrorType.V_MIRROR);
break;
case 1:
setmirroring(Mapper.MirrorType.H_MIRROR);
break;
case 2:
setmirroring(Mapper.MirrorType.SS_MIRROR0);
break;
case 3:
setmirroring(Mapper.MirrorType.SS_MIRROR1);
break;
}
case 0xd:
//irq - let's put this in and hope it works
irqclock = ((data & (utils.BIT7)) != 0);
//2015-05: test by Teppples says that any value written here
//will acknowledge a pending interrupt
irqenabled = ((data & (utils.BIT0)) != 0);
if (interrupted && cpu.interrupt > 0) {
--cpu.interrupt;
}
interrupted = false;
//System.err.println(cpu.interrupt);
break;
case 0xe:
irqcounter &= 0xff00;
irqcounter |= data;
break;
case 0xf:
irqcounter &= 0xff;
irqcounter |= (data << 8);
break;
}
} else if (addr == 0xe000) {
sndchip.write(soundCommand, data);
}
}
@Override
public void cpucycle(int cycles) {
if (irqclock) {
if (irqcounter == 0) {
irqcounter = 0xffff;
if (irqenabled && !interrupted) {
interrupted = true;
++cpu.interrupt;
//System.err.println("FME7 Interrupt");
}
} else {
--irqcounter;
}
}
}
private void setbanks() {
for (int i = 0; i < 8; ++i) {
for (int j = 0; j < 4; ++j) {
prg_map[i + 8 * j] = (1024 * (i + (prgbanks[j] * 8))) % prgsize;
}
}
for (int i = 0; i < 8; ++i) {
chr_map[i] = (1024 * charbanks[i]) % chrsize;
}
}
}