private void SetCharacterBanks() { selectedCharacterBank0 = latch0 == 0xFD ? characterBank0 : characterBank1; selectedCharacterBank1 = latch1 == 0xFD ? characterBank2 : characterBank3; // invalidate address regions CharacterBankSwitch?.Invoke(0x0000, 0x2000); }
// CHR bank 0 (internal, $A000-$BFFF) private void WriteCharacterBank0(byte value) { characterBank0 = value; UpdateOffsets(); CharacterBankSwitch?.Invoke(0x0000, 0x1000); // both chr modes HandleSxRomVariants(value); }
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)); } } }
// CHR bank 1 (internal, $C000-$DFFF) private void WriteCharacterBank1(byte value) { characterBank1 = value; UpdateOffsets(); CharacterBankSwitch?.Invoke(0x1000, 0x1000); // ignored in 8k CHAR mode if (characterBankMode == 1) { HandleSxRomVariants(value); } }
public override byte this[ushort address] { get { if (address < 0x2000) { return(Cartridge.CharacterRom[characterBank * 0x2000 + address]); } if (address >= 0xC000) { return(Cartridge.ProgramRom[programBank2 * 0x4000 + address - 0xC000]); } if (address >= 0x8000) { return(Cartridge.ProgramRom[programBank1 * 0x4000 + address - 0x8000]); } if (address >= 0x6000) { return(Cartridge.SaveRam[(ushort)(address - 0x6000)]); } throw new Exception("Unhandled CNROM mapper read at address: " + Hex.Format(address)); } set { if (address < 0x2000) { Cartridge.CharacterRom[characterBank * 0x2000 + address] = value; } else if (address >= 0x8000) { characterBank = value % 3; // invalidate address region CharacterBankSwitch?.Invoke(0x0000, 0x2000); } else if (address >= 0x6000) { Cartridge.SaveRam[(ushort)(address - 0x6000)] = value; } else { throw new Exception("Unhandled CNROM 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)); // 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 / 0x1000; int offset = address % 0x1000; return(Cartridge.CharacterRom[characterBankOffsets[bank] + offset]); } else if (address >= 0x8000) { address -= 0x8000; int bank = address / 0x4000; int offset = address % 0x4000; return(Cartridge.ProgramRom[outerProgramRomBank + programBankOffsets[bank] + offset]); } else if (address >= 0x6000) { ushort offset = (ushort)(address - 0x6000); if (programRamBanksSupported) { return(programRam[programRamBank * 0x2000 + offset]); } else { return(Cartridge.SaveRam[offset]); } } else { return((byte)(address >> 8)); // open bus } } set { if (address < 0x2000) { int bank = address / 0x1000; int offset = address % 0x1000; Cartridge.CharacterRom[characterBankOffsets[bank] + offset] = value; CharacterBankSwitch?.Invoke((ushort)characterBankOffsets[bank], 0x1000); } else if (address >= 0x8000) { LoadRegister(address, value); } else if (address >= 0x6000) { ushort offset = (ushort)(address - 0x6000); if (programRamBanksSupported) { programRam[programRamBank * 0x2000 + offset] = value; } else { Cartridge.SaveRam[offset] = value; } } } }
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) { 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? } } }
public override byte this[ushort address] { get { if (address < 0x1000) { return(Cartridge.CharacterRom[address]); } if (address < 0x2000) { int index = address & 0xFFF; if (characterBank < 2) { return(Cartridge.CharacterRom[characterBank * 0x1000 + index]); } else { return(characterRam[(characterBank - 2) * 0x1000 + index]); } } if (address >= 0x8000) { return(Cartridge.ProgramRom[address - 0x8000]); } throw new Exception("Unhandled " + Name + " mapper read at address: " + Hex.Format(address)); } set { if (address < 0x1000) { Cartridge.CharacterRom[address] = value; } else if (address < 0x2000) { int index = address & 0xFFF; if (characterBank < 2) { Cartridge.CharacterRom[characterBank * 0x1000 + index] = value; } else { characterRam[(characterBank - 2) * 0x1000 + index] = value; } } else if (address >= 0x8000) { characterBank = value & 0x03; // invalidate address regions CharacterBankSwitch?.Invoke(0x0000, 0x1000); } else { throw new Exception("Unhandled " + Name + " mapper write at address: " + Hex.Format(address)); } } }