public override byte this[ushort address] { get { if (address < 0x2000) { int bankIndex = address / 0x0400; int bankOffset = address % 0x0400; int flatAddress = outerBlock * 0x20000 + (characterBankOffsets[bankIndex] % 0x20000) + bankOffset; return(Cartridge.CharacterRom[flatAddress]); } else if (address >= 0x8000) { int flatAddress = outerBlock * 0x20000; // 128k block if (programModeNormal) { address -= 0x8000; int bankIndex = address / 0x2000; int bankOffset = address % 0x2000; flatAddress += (programBankOffsets[bankIndex] % 0x20000) + bankOffset; } else { flatAddress += programBank * 0x8000 + address % 0x8000; // 32k banks } return(Cartridge.ProgramRom[flatAddress]); } else { return(base[address]); } } set { if (address >= 0x6000 && address < 0x8000) { // $6000-7FFF: [BBPP ...O] Multicart reg // B = Block // P = 32k PRG Reg // O = PRG Mode(0 = 32k mode) outerBlock = value >> 6; programBank = (value >> 4) & 0x03; programModeNormal = (value & 0x01) != 0; ProgramBankSwitch?.Invoke(0x8000, 0x8000); } else { base[address] = value; } } }
public override byte this[ushort address] { get { if (address < 0x2000) { return(Cartridge.CharacterRom[address]); } if (address >= 0xC000) { int index = programBank2 * 0x4000 + address - 0xC000; return(Cartridge.ProgramRom[index]); } if (address >= 0x8000) { int index = programBank1 * 0x4000 + address - 0x8000; return(Cartridge.ProgramRom[index]); } if (address >= 0x6000) { return(Cartridge.SaveRam[(ushort)(address - 0x6000)]); } //throw new Exception("Unhandled " + Name + " mapper read at address: " + Hex.Format(address)); return((byte)(address >> 8)); // open bus } set { if (address < 0x2000) { Cartridge.CharacterRom[address] = value; } else if (address >= 0x8000) { int oldProgramBank1 = programBank1; programBank1 = value % programBankCount; // invalidate address region if (programBank1 != oldProgramBank1) { ProgramBankSwitch?.Invoke(0x8000, 0x4000); } } else if (address >= 0x6000) { Cartridge.SaveRam[(ushort)(address - 0x6000)] = value; } else { throw new Exception("Unhandled " + Name + " mapper write at address: " + Hex.Format(address)); } } }
// PRG bank (internal, $E000-$FFFF) private void WriteProgramBank(byte value) { programBank = (byte)(value & 0x0F); UpdateOffsets(); ProgramBankSwitch?.Invoke(0xE000, 0x2000); // TODO: ram chip enable }
private void WriteBankSelect(byte value) { programBankMode = (byte)((value >> 6) & 1); characterBankMode = (byte)((value >> 7) & 1); registerIndex = (byte)(value & 7); UpdateOffsets(); // invalidate address regions CharacterBankSwitch?.Invoke(0x0000, 0x2000); ProgramBankSwitch?.Invoke(0x8000, 0x8000); }
public override byte this[ushort address] { get { if (address < 0x2000) { return(Cartridge.CharacterRom[characterBank * 0x2000 + address]); } else if (address >= 0x8000 && address < 0xC000) { return(flashMemory[(uint)(programBank * 0x4000 + address % 0x4000)]); } else if (address >= 0xC000) { return(flashMemory[(uint)(programLastAddress16k + address % 0x4000)]); } else { Debug.WriteLine(Name + ": Unexpected read from address " + Hex.Format(address)); return((byte)(address >> 8)); } } set { flashMemory[address] = value; if (address > 0x8000) { // MCCP PPPP MirrorMode = (value & 0x80) != 0 ? MirrorMode.Single1 : MirrorMode.Single0; int oldProgramBank = programBank; programBank = value & 0x1F; if (programBank != oldProgramBank) { ProgramBankSwitch?.Invoke(0x8000, 0x4000); } int oldCharacterBank = characterBank; characterBank = (value >> 5) & 0x03; if (characterBank != oldCharacterBank) { CharacterBankSwitch?.Invoke(0x0000, 0x2000); } } else { Debug.WriteLine(Name + ": Unexpected write of value " + Hex.Format(value) + " to address " + Hex.Format(address)); } } }
public override byte this[ushort address] { get { if (address < 0x2000) { return(Cartridge.CharacterRom[characterBank * 0x2000 + address]); } else if (address >= 0x8000) { return(Cartridge.ProgramRom[programBank * 0x8000 + address % 0x8000]); } else { Debug.WriteLine(Name + ": Unexpected read from address: " + Hex.Format(address)); return((byte)(address >> 8)); // open bus } } set { if (address >= 0x7000 && address < 0x8000) { // .... CCPP int oldProgramBank = programBank; programBank = value & 0x03; // invalidate address region if (programBank != oldProgramBank) { ProgramBankSwitch?.Invoke(0x8000, 0x8000); } int oldCharacterBank = characterBank; characterBank = (value >> 2) & 0x03; // invalidate address region if (characterBank != oldCharacterBank) { CharacterBankSwitch?.Invoke(0x0000, 0x2000); } } else { Debug.WriteLine(Name + ": unexpected write of value " + Hex.Format(value) + " at address: " + Hex.Format(address)); } } }
public override byte this[ushort address] { get { if (address < 0x2000) { return(Cartridge.CharacterRom[characterBank * 0x2000 + address]); } if (address >= 0x8000) { return(Cartridge.ProgramRom[programBank * 0x8000 + address - 0x8000]); } throw new Exception("Unhandled " + Name + " mapper read at address: " + Hex.Format(address)); } set { if (address < 0x2000) { Cartridge.CharacterRom[characterBank * 0x2000 + address] = value; } else if (address >= 0x8000) { int oldProgramBank = programBank; // --PP--CC programBank = (value >> 4) & 7; characterBank = value & 7; // invalidate address regions CharacterBankSwitch?.Invoke(0x0000, 0x2000); if (programBank != oldProgramBank) { ProgramBankSwitch?.Invoke(0x8000, 0x8000); } } else if (address >= 0x6000) { Cartridge.SaveRam[(ushort)(address - 0x6000)] = value; } else { throw new Exception("Unhandled " + Name + " mapper write at address: " + Hex.Format(address)); } } }
public override byte this[ushort address] { get { if (address < 0x2000) { return(Cartridge.CharacterRom[characterBank * 0x2000 + address]); } else if (address >= 0x8000 && address < 0xC000) { return(Cartridge.ProgramRom[programBank * 0x4000 + address % 0x4000]); } else if (address >= 0xC000) { return(Cartridge.ProgramRom[programLastAddress16k + address % 0x4000]); } else { Debug.WriteLine(Name + ": Unexpected read from address " + Hex.Format(address)); return((byte)(address >> 8)); } } set { if (address > 0x8000) { int oldProgramBank = programBank; programBank = (value >> 2) & 0x07; if (programBank != oldProgramBank) { ProgramBankSwitch?.Invoke(0x8000, 0x4000); } int oldCharacterBank = characterBank; characterBank = value & 0x03; if (characterBank != oldCharacterBank) { CharacterBankSwitch?.Invoke(0x0000, 0x2000); } } else { Debug.WriteLine(Name + ": Unexpected write of value " + Hex.Format(value) + " to address " + Hex.Format(address)); } } }
public override byte this[ushort address] { get { if (address < 0x2000) { return(Cartridge.CharacterRom[characterBank * 0x2000 + address]); } if (address >= 0x8000) { return(Cartridge.ProgramRom[programBank * 0x8000 + address - 0x8000]); } throw new Exception("Unhandled " + Name + " mapper read at address: " + Hex.Format(address)); } set { if (address >= 0x8000) { int oldProgramBank = programBank; // CCCCLLPP // CCCC - CHR bank, LL - CIC chip lockout defeat, PP - PRG bank programBank = value & 0x3; characterBank = (value >> 4) & 0xF; // invalidate address regions CharacterBankSwitch?.Invoke(0x0000, 0x2000); if (programBank != oldProgramBank) { ProgramBankSwitch?.Invoke(0x8000, 0x8000); } } else { throw new Exception("Unhandled " + Name + " mapper write at address: " + Hex.Format(address)); } } }
public override byte this[ushort address] { get { if (address < 0x2000) { return(Cartridge.CharacterRom[characterBank * 0x2000 + address]); } else if (address >= 0x8000) { return(Cartridge.ProgramRom[programBank * 0x8000 + address % 0x8000]); } else { Debug.WriteLine(Name + ": Unexpected read from address " + Hex.Format(address)); return((byte)(address >> 8)); // return open bus } } set { if (address >= 0x6000 && address < 0x8000) { // $6000-7FFF: [CCCC PPPP] High CHR, PRG bits int oldProgramBank = programBank; int oldCharacterBank = characterBank; programBank &= 0x01; // clear bit1 onwards (preserve PRG low bit) programBank |= (value & 0x0F) << 1; // set bit1-bit4 PRG high bits characterBank &= 0x07; // clear bit3 onwards (priserve CHR low bits) characterBank |= ((value >> 4) << 3); // set bit3-bit6 CHR high bits) // invalidate address regions if (programBank != oldProgramBank) { ProgramBankSwitch?.Invoke(0x8000, 0x8000); } if (characterBank != oldCharacterBank) { CharacterBankSwitch?.Invoke(0x0000, 0x2000); } } if (address >= 0x8000) { // $8000-FFFF: [.CCC ...P] Low CHR, PRG bits int oldProgramBank = programBank; int oldCharacterBank = characterBank; programBank &= 0xFE; // clear bit 0 programBank |= value & 0x01; // set PRG low bit characterBank &= 0xF8; // clear bits 0 - 2 characterBank |= (value >> 4) & 0x07; // set CHR low bits // invalidate address regions if (programBank != oldProgramBank) { ProgramBankSwitch?.Invoke(0x8000, 0x8000); } if (characterBank != oldCharacterBank) { CharacterBankSwitch?.Invoke(0x0000, 0x2000); } } else { Debug.WriteLine(Name + ": Unexpected write of value " + Hex.Format(value) + " at address " + Hex.Format(address)); } } }
public override byte this[ushort address] { get { if (address < 0x2000) { int bank = address / 0x400; int offset = address % 0x400; return(Cartridge.CharacterRom[characterRomBank[bank] * 0x400 + offset]); } else if (address >= 0x8000) { int bank = (address - 0x8000) / 0x2000; int offset = address % 0x2000; return(Cartridge.ProgramRom[programRomBank[bank] * 0x2000 + offset]); } else { return((byte)(address >> 8)); // assuming open bus } } set { if (address >= 0x8000 && address < 0xA000) { int offset1000 = address % 0x1000; if (offset1000 >= 4) { return; } int bankIndex1000 = (address - 0x8000) / 0x1000; int programBankIndex = bankIndex1000 * 2 + offset1000 / 2; // last bank must remain fixed if (programBankIndex > 2) { return; } int oldProgramBank = programRomBank[programBankIndex]; if (offset1000 % 2 == 0) { programRomBank[programBankIndex] = SetLowerNybble(programRomBank[programBankIndex], value); } else { programRomBank[programBankIndex] = SetHigherNybble(programRomBank[programBankIndex], value); } if (programRomBank[programBankIndex] != oldProgramBank) { Debug.WriteLine("Program Bank " + programBankIndex + " (" + Hex.Format(address) + ") = " + programRomBank[programBankIndex]); ProgramBankSwitch?.Invoke((ushort)(0x8000 + programBankIndex * 0x2000), 0x2000); } } else if (address >= 0xA000 && address < 0xE000) { int offset1000 = address % 0x1000; if (offset1000 >= 4) { return; } int bankIndex1000 = (address - 0xA000) / 0x1000; int characterBankIndex = bankIndex1000 * 2 + offset1000 / 2; int oldCharacterBank = characterRomBank[characterBankIndex]; if (offset1000 % 2 == 0) { characterRomBank[characterBankIndex] = SetLowerNybble(characterRomBank[characterBankIndex], value); } else { characterRomBank[characterBankIndex] = SetHigherNybble(characterRomBank[characterBankIndex], value); } if (characterRomBank[characterBankIndex] != oldCharacterBank) { Debug.WriteLine("Character Bank " + characterBankIndex + " (" + Hex.Format(address) + ") = " + characterRomBank[characterBankIndex]); CharacterBankSwitch?.Invoke((ushort)(characterBankIndex * 0x400), 0x400); } } else if (address == 0xE000) { // bits 0..3 of IRQ counter reload irqReload &= 0xFFF0; irqReload |= (ushort)(value & 0x0F); } else if (address == 0xE001) { // bits 4..7 of IRQ counter reload irqReload &= 0xFF0F; irqReload |= (ushort)((value & 0x0F) << 4); } else if (address == 0xE002) { // bits 8..11 of IRQ counter reload irqReload &= 0xF0FF; irqReload |= (ushort)((value & 0x0F) << 8); } else if (address == 0xE003) { // bits 12..15 of IRQ counter reload irqReload &= 0x0FFF; irqReload |= (ushort)((value & 0x0F) << 12); } else if (address == 0xF000) { irqCounter = irqReload; Debug.WriteLine("IRQ Reloaded to " + irqReload); CancelInterruptRequest?.Invoke(); irqPrimed = false; } else if (address == 0xF001) { irqCounterEnabled = (value & 0x01) != 0; int counterBits = (value >> 1) & 0x07; switch (counterBits) { case 0: irqFixedMask = 0x0000; break; case 1: irqFixedMask = 0xF000; break; case 2: case 3: irqFixedMask = 0xFF00; break; default: irqFixedMask = 0xFFF0; break; } irqCounterMask = 0xFFFF - irqFixedMask; // complement CancelInterruptRequest?.Invoke(); irqPrimed = false; } else if (address == 0xF002) { // mirror mode switch (value & 0x03) { case 0x00: MirrorMode = MirrorMode.Horizontal; break; case 0x01: MirrorMode = MirrorMode.Vertical; break; case 0x02: MirrorMode = MirrorMode.Single0; break; case 0x03: MirrorMode = MirrorMode.Single1; break; } } } }
public override byte this[ushort address] { get { if (address < 0x2000) { SetCharacterBanks(); byte value = 0; ushort index = (ushort)(address & 0x0FFF); if (address < 0x1000) { value = Cartridge.CharacterRom[selectedCharacterBank0 * 0x1000 + index]; } else // 0x1000 - 0x1FFF { value = Cartridge.CharacterRom[selectedCharacterBank1 * 0x1000 + index]; } if (address == 0x0FD8) { latch0 = 0xFD; } else if (address == 0x0FE8) { latch0 = 0xFE; } if (address >= 0x1FD8 && address <= 0x1FDF) { latch1 = 0xFD; } else if (address >= 0x1FE8 && address <= 0x1FEF) { latch1 = 0xFE; } return(value); } if (address >= 0x6000 && address < 0x7FFF) { return(programRam[address - 0x6000]); } if (address >= 0x8000) { int index = address & 0x3FFF; if (address < 0xC000) { return(Cartridge.ProgramRom[programBank * 0x4000 + index]); } else // 0xC000 - 0xFFFF { return(Cartridge.ProgramRom[(programBankCount - 1) * 0x4000 + index]); } } throw new Exception("Unhandled " + Name + " mapper read at address: " + Hex.Format(address)); } set { if (address < 0x2000) { int index = address & 0x0FFF; SetCharacterBanks(); if (address < 0x1000) { Cartridge.CharacterRom[selectedCharacterBank0 * 0x1000 + index] = value; } else { Cartridge.CharacterRom[selectedCharacterBank1 * 0x1000 + index] = value; } return; } if (address >= 0x6000 && address < 0x7FFF) { programRam[address - 0x6000] = value; return; } if (address >= 0x8000) { if (address < 0xA000) { int index = address & 0x1FFF; throw new NotImplementedException("MMC4 write to $8000 - $9FFF"); } else if (address < 0xB000) { int oldProgramBank = programBank; programBank = (byte)(value & 0x0F); // invalidate address regions if (programBank != oldProgramBank) { ProgramBankSwitch?.Invoke(0x8000, 0x8000); } } else if (address < 0xC000) { characterBank0 = (byte)(value & 0x1F); } else if (address < 0xD000) { characterBank1 = (byte)(value & 0x1F); } else if (address < 0xE000) { characterBank2 = (byte)(value & 0x1F); } else if (address < 0xF000) { characterBank3 = (byte)(value & 0x1F); } else //address >= 0xF000 { MirrorMode = ((value & 1) == 1) ? MirrorMode.Horizontal : MirrorMode.Vertical; } return; } throw new Exception("Unhandled " + Name + " mapper write at address: " + Hex.Format(address)); } }
public override byte this[ushort address] { get { if (address < 0x2000) { return(Cartridge.CharacterRom[address]); } if (address >= 0x6000 && address < 0x8000) { return(programRam[address - 0x6000]); } int index = 0; switch (bankMode) { case 0: index = address & 0x3FFF; if (address >= 0x8000 && address < 0xC000) { return(Cartridge.ProgramRom[programRomBank * 0x4000 + index]); } else if (address >= 0xC000) { return(Cartridge.ProgramRom[(programRomBank | 1) * 0x4000 + index]); } break; case 1: index = address & 0x3FFF; if (address >= 0x8000 && address < 0xC000) { return(Cartridge.ProgramRom[programRomBank * 0x4000 + index]); } else if (address >= 0xC000) { // last bank return(Cartridge.ProgramRom[Cartridge.ProgramRom.Count - 0x4000 + index]); } break; case 2: // 8k banks index = address & 0x1FFF; if (address >= 0x8000) { return(Cartridge.ProgramRom[programRomBank * 0x4000 + subBank * 0x2000 + index]); } break; case 3: // 16k banks (mirrored) index = address & 0x3FFF; if (address >= 0x8000) { return(Cartridge.ProgramRom[programRomBank * 0x4000 + index]); } break; } throw new Exception("Unhandled " + Name + " mapper read at address: " + Hex.Format(address)); } set { if (address < 0x2000) { Cartridge.CharacterRom[address] = value; return; } if (address >= 0x6000 && address < 0x8000) { programRam[address - 0x6000] = value; return; } if (address >= 0x8000) { int oldBankMode = bankMode; bankMode = address & 0x03; programRomBank = value & 0x3f; subBank = value >> 7; // invalidate address region // should refine this if (bankMode != oldBankMode) { ProgramBankSwitch?.Invoke(0x8000, 0x8000); } MirrorMode = (value & 0x40) != 0 ? MirrorMode.Horizontal : MirrorMode.Vertical; return; } throw new Exception("Unhandled " + Name + " mapper write at address: " + Hex.Format(address)); } }
public override byte this[ushort address] { get { if (address < 0x2000) { return(Cartridge.CharacterRom[address]); } if (address >= 0x8000) { byte value = Cartridge.ProgramRom[programBank * 0x8000 + address - 0x8000]; // apply ppu spin loop hack to Battletoads ROM if (Cartridge.Crc == 0x279710DC) { value = battleToadsHack.Read(address, value); } return(value); } if (address >= 0x6000) { return(Cartridge.SaveRam[(ushort)(address - 0x6000)]); } Debug.WriteLine(Name + ": Unexpected read from address " + Hex.Format(address)); return((byte)(address >> 8)); // open bus } set { if (address < 0x2000) { Cartridge.CharacterRom[address] = value; } else if (address >= 0x8000) { int oldProgramBank = programBank; // ---M-PPP programBank = value & 7; // mirror mode MirrorMode = (value & 0x10) == 0x10 ? MirrorMode.Single1 : MirrorMode.Single0; // invalidate address region if (programBank != oldProgramBank) { ProgramBankSwitch?.Invoke(0x8000, 0x8000); } } else if (address >= 0x6000) { Cartridge.SaveRam[(ushort)(address - 0x6000)] = value; } else { Debug.WriteLine(Name + ": Unexpected write of value " + Hex.Format(value) + " at address " + Hex.Format(address)); } } }
public override byte this[ushort address] { get { if (address < 0x2000) { if (characterBanksSupported) { // get data from corresponding 1K bank int bankindex = address / 0x400; int bankOffset = address % 0x400; return(Cartridge.CharacterRom[characterBank[bankindex] * 0x400 + bankOffset]); } else { // for Datach Joint ROM System - treat as flat CHR RAM return(Cartridge.CharacterRom[address]); } } if (address >= 0x6000 && address < 0x7FFF) { // return SRAM or open bus depending on variant return(saveRamSupported ? Cartridge.SaveRam[(ushort)(address - 0x6000)] : (byte)(address >> 8)); } if (address >= 0x8000 && address < 0xC000) { int bankOffset = address % 0x4000; return(Cartridge.ProgramRom[outerProgramBank + programBank * 0x4000 + bankOffset]); } if (address >= 0xC000) { // fixed at last bank int lastBankOffset = address % 0x4000; return(Cartridge.ProgramRom[outerProgramBank + lastProgramBankBase + lastBankOffset]); } //if (address >= 0x6000) // return Cartridge.SaveRam[(ushort)(address - 0x6000)]; //throw new Exception("Unhandled " + Name + " mapper read at address: " + Hex.Format(address)); // return open bus? return((byte)(address >> 8)); } set { if (address < 0x2000) { if (characterBanksSupported) { // CHR bank switches for 8 0x400 ranges // set CHR bank for corresponding 0x400 range int bankindex = address / 0x400; int bankOffset = address % 0x400; Cartridge.CharacterRom[characterBank[bankindex] * 0x400 + bankOffset] = value; } else { // for Datach Joint ROM System - treat as flat CHR RAM Cartridge.CharacterRom[address] = value; } } else if (saveRamSupported && address >= 0x6000 && address < 0x8000) { Cartridge.SaveRam[(ushort)(address - 0x6000)] = value; } else if (address >= registerBase) { // NOTE: FCG variants vary register base between $6000 and $8000 // variants lumped under mapper 16 require mirroring these bases to get most games to work int registerAddress = address % 0x10; if (outerProgramBankSupported && address >= 0x8000 && address < 0x8004) { int oldOuterProgramBank = outerProgramBank; // outer program bank (mapper 153 only) if ((value & 0x01) != 0) { outerProgramBank = 0x40000; } else { outerProgramBank = 0; } if (outerProgramBank != oldOuterProgramBank) { ProgramBankSwitch?.Invoke(0x8000, 0x8000); } } else if (characterBanksSupported && registerAddress < 0x08) { // CHR bank switch int oldCharacterBank = characterBank[registerAddress]; characterBank[registerAddress] = value; if (value != oldCharacterBank) { CharacterBankSwitch?.Invoke((ushort)(registerAddress * 0x400), 0x400); } } else if (registerAddress == 0x08) { // program bank switch int oldProgramBank = programBank; programBank = value & 0x0F; programBank %= programBankCount; // probably not needed, but anyhow // invalidate address region if (programBank != oldProgramBank) { ProgramBankSwitch?.Invoke(0x8000, 0x4000); } } else if (registerAddress == 0x09) { // mirroring mode switch (value & 0x03) { case 0: MirrorMode = MirrorMode.Vertical; break; case 1: MirrorMode = MirrorMode.Horizontal; break; case 2: MirrorMode = MirrorMode.Single0; break; case 3: MirrorMode = MirrorMode.Single1; break; } } else if (registerAddress == 0x0A) { irqEnabled = (value & 0x01) != 0; CancelInterruptRequest?.Invoke(); } else if (registerAddress == 0x0B) { irqCounter &= 0xFF00; irqCounter |= value; } else if (registerAddress == 0x0C) { irqCounter &= 0x00FF; irqCounter |= (ushort)(value << 8); } // TODO: D: eeprom/PRG ram enable // TODO: variants } else { throw new Exception("Unhandled " + Name + " mapper write at address: " + Hex.Format(address)); //ignore writes? } } }