public override void StepVideo(int scanLine, int cycle, bool showBackground, bool showSprites) { ppuRendering = scanLine >= 0 && scanLine < 240 && (showBackground || showSprites); if (cycle != 0) { return; } if (ppuRendering) { if (!inFrame) { inFrame = true; irqCounter = 0; CancelInterruptRequest?.Invoke(); } else { ++irqCounter; if (irqCounter == irqLatch) { irqPending = true; if (irqEnabled) { TriggerInterruptRequest?.Invoke(); } } } } else { inFrame = false; } }
private void AcknowledgeInterrupt() { if (irqTriggered) { CancelInterruptRequest?.Invoke(); irqTriggered = false; } }
protected void WriteIrqAcknowledge() { if (irqTriggered) { CancelInterruptRequest?.Invoke(); } irqEnable = irqEnableOnAcknowledge; irqTriggered = false; }
public override byte this[ushort address] { set { if (address == 0x8000) { // ignore mirror mode set by register $8000 for TC0190 MirrorMode oldMirrorMode = MirrorMode; base[address] = value; MirrorMode = oldMirrorMode; } else if (address == 0xC000) { // irq latch - reload value irqReload = value; irqReload ^= 0xFF; } else if (address == 0xC001) { // irq reload - set irq counter to reload value irqReloadPrimed = true; } else if (address == 0xC002) { // enable IRQ irqEnabled = true; } else if (address == 0xC003) { // disable IRQ irqEnabled = false; CancelInterruptRequest?.Invoke(); } else if (address == 0xE000) { // $E000: [.M.. ....] Mirroring: 0 = Vert, 1 = Horz MirrorMode = (value & Bin.Bit6) != 0 ? MirrorMode.Horizontal : MirrorMode.Vertical; } else { // default Taito TC0190 behaviour (mapper 33) base[address] = 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 { int bankOffset = address % characterBankSize; // character ROM if (address < 0x2000) { // handle extended mode - bank registers from extended ram page if (extendedRamMode == 1 && !AccessingSpriteCharacters) { bankOffset = address % 0x1000; int extendedCharacterBank = extendedRam[lastTileIndex]; extendedCharacterBank &= 0x3F; extendedCharacterBank |= (characterBankUpper >> 2); return(Cartridge.CharacterRom[extendedCharacterBank * 0x1000 + bankOffset]); } // handles all CHR modes // bank range CHR modes 8K: [0], 4K: [0, 1] 2K: [0..3], 1k: [0..7] int bankRange = address / characterBankSize; // determine stride between bank registers, given CHR mode: 8K: 8, 4K: 4, 2K: 2, 1K: 1 int indexStride = characterBankSize / 0x0400; // determine bank index: 8K: [7], 4k: [3, 7], 2K: [1, 3, 5, 7], 8K: [0..7] int bankIndex = (bankRange + 1) * indexStride - 1; // if sprite mode is 8x16 and chr access if for background, use upper // bank switching register indexes 8K: [11], 4k: [11], 2K: [9, 11], 8K: [8..11] // or // sprite mode is 8x8 and last register banks written are upper if ((SpriteSize == Video.SpriteSize.Size8x16 && !AccessingSpriteCharacters) || (SpriteSize == Video.SpriteSize.Size8x8 && characterBankLastWrittenUpper)) { bankIndex %= 4; bankIndex += 8; } int characterBank = characterBanks[bankIndex]; int addressBase = characterBank * characterBankSize; return(Cartridge.CharacterRom[addressBase + bankOffset]); } if (address >= 0x5000 && address <= 0x5007) { // TODO: pulse generators 1 and 2 return(0x00); } if (address == 0x5010 || address == 0x5011 || address == 0x5015) { // TODO: irq, pcm, status return(0x00); } // read-only IRQ counter - return open bus? if (address == 0x5203) { return(0x52); } if (address == 0x5204) { byte result = 0; if (irqPending) { result |= 0x80; } if (inFrame) { result |= 0x40; } irqPending = false; CancelInterruptRequest?.Invoke(); return(result); } if (address == 0x5205) { return(productLow); } if (address == 0x5206) { return(productHigh); } if (address >= 0x5C00 && address < 0x6000) { // expansion ram - all modes switch (extendedRamMode) { case 0: case 1: // expansion ram mode 0/1 - returns open bus return((byte)(address >> 8)); case 2: // expansion ram mode 2 - 1K r/w memory case 3: // expansion ram mode 3 - 1K ROM return(extendedRam[address % 0x400]); default: throw new Exception("MMC5 Invalid expansion ram mode"); } } if (address >= 0x6000 && address < 0x8000) { // all bank modes - 8K switchable RAM bank int offset = address % 0x2000; return(programRam[programRamBank * 0x2000 + offset]); } if (address >= 0x8000) { // program banks for all modes switch (programBankMode) { case 0: { // PRG mode 0 - single 32k switchable ROM bank int offset = address % 0x8000; return(Cartridge.ProgramRom[(programRomBank >> 2) * 0x8000 + offset]); } case 1: if (address < 0xC000) { // PRG mode 1 - first 16k switchable ROM/RAM bank int offset = address % 0x4000; int flatAddress = (programBank1 >> 1) * 0x4000 + offset; if (romMode1) { return(Cartridge.ProgramRom[flatAddress]); } else { return(programRam[flatAddress]); } } else // if (address >= 0xC000) { // PRG mode 1 - second 16k switchable ROM bank int offset = address % 0x4000; return(Cartridge.ProgramRom[programRomBank * 0x4000 + offset]); } case 2: if (address < 0xC000) { // PRG mode 2 - 16k switchable ROM/RAM bank int offset = address % 0x4000; int flatAddress = (programBank1 >> 1) * 0x4000 + offset; if (romMode1) { return(Cartridge.ProgramRom[flatAddress]); } else { return(programRam[flatAddress]); } } else if (address < 0xE000) { // PRG mode 2 - first 8k switchable ROM/RAM bank int offset = address % 0x2000; int flatAddress = programBank2 * 0x2000 + offset; if (romMode2) { return(Cartridge.ProgramRom[flatAddress]); } else { return(programRam[flatAddress]); } } else // if (address >= 0xE000 ) { // PRG mode 2 - second 8k switchable ROM bank int offset = address % 0x2000; return(Cartridge.ProgramRom[programRomBank * 0x2000 + offset]); } case 3: if (address < 0xA000) { // PRG mode 3 - first 8k switchable ROM/RAM bank int offset = address % 0x2000; int flatAddress = programBank0 * 0x2000 + offset; if (romMode0) { return(Cartridge.ProgramRom[flatAddress]); } else { return(programRam[flatAddress]); } } else if (address < 0xC000) { // PRG mode 3 - second 8k switchable ROM/RAM bank int offset = address % 0x2000; int flatAddress = programBank1 * 0x2000 + offset; if (romMode1) { return(Cartridge.ProgramRom[flatAddress]); } else { return(programRam[flatAddress]); } } else if (address < 0xE000) { // PRG mode 3 - third 8k switchable ROM/RAM bank int offset = address % 0x2000; int flatAddress = programBank2 * 0x2000 + offset; if (romMode2) { return(Cartridge.ProgramRom[flatAddress]); } else { return(programRam[flatAddress]); } } else // if (address >= 0xE000) { // PRG mode 3 - fourth 8k switchable ROM bank int offset = address % 0x2000; return(Cartridge.ProgramRom[programRomBank * 0x2000 + offset]); } default: throw new Exception("MMC5 Invalid program bank mode"); } } // invalid / unhandled addresses //throw new Exception("Unhandled " + Name + " mapper read at address: " + Hex.Format(address)); // return open bus for unhandled read return((byte)(address >> 8)); } set { // character ROM if (address < 0x2000) { // handles all CHR modes // bank range CHR modes 8K: [0], 4K: [0, 1] 2K: [0..3], 1k: [0..7] int bankRange = address / characterBankSize; // determine stride between bank registers, given CHR mode: 8K: 8, 4K: 4, 2K: 2, 1K: 1 int indexStride = characterBankSize / 0x0400; // determine bank index: 8K: [7], 4k: [3, 7], 2K: [1, 3, 5, 7], 8K: [0..7] int bankIndex = (bankRange + 1) * indexStride - 1; // if sprite mode is 8x16 and chr access if for background, use upper // bank switching register indexes 8K: [11], 4k: [11], 2K: [9, 11], 8K: [8..11] // or // sprite mode is 8x8 and last register banks written are upper if ((SpriteSize == Video.SpriteSize.Size8x16 && !AccessingSpriteCharacters) || (SpriteSize == Video.SpriteSize.Size8x8 && characterBankLastWrittenUpper)) { bankIndex %= 4; bankIndex += 8; } int characterBank = characterBanks[bankIndex]; int addressBase = characterBank * characterBankSize; int bankOffset = address % characterBankSize; Cartridge.CharacterRom[addressBase + bankOffset] = value; return; } // registers if (address >= 0x5000 && address <= 0x5007) { // TODO: pulse generators 1 and 2 return; } if (address == 0x5010 || address == 0x5011 || address == 0x5015) { // TODO: irq, pcm, status return; } if (address == 0x5100) { programBankMode = (byte)(value & 0x03); Debug.WriteLine("MMC5 PRG Bank Mode ($5100) = " + Hex.Format(programBankMode)); return; } if (address == 0x5101) { SetCharacterBankMode((byte)(value & 0x03)); Debug.WriteLine("MMC5 CHR Bank Mode ($5101) = " + Hex.Format(characterBankMode) + " (" + characterBankCount + " " + characterBankSize + "b Banks)"); return; } if (address == 0x5102) { programRamProtect1 = value == 2; programRamProtect = programRamProtect1 && programRamProtect2; Debug.WriteLine("MMC5 Ram Protect 1 ($5103) = " + (programRamProtect1 ? "Enabled" : "Disabled") + " (P1 && P2 = " + programRamProtect + ")"); return; } if (address == 0x5103) { programRamProtect2 = value == 1; programRamProtect = programRamProtect1 && programRamProtect2; Debug.WriteLine("MMC5 Ram Protect 2 ($5103) = " + (programRamProtect2 ? "Enabled" : "Disabled") + " (P1 && P2 = " + programRamProtect + ")"); return; } if (address == 0x5104) { extendedRamMode = (byte)(value & 0x03); Debug.WriteLine("MMC5 ExRamMode ($5104) = " + Hex.Format((byte)extendedRamMode)); return; } if (address == 0x5105) { // Mirror mode - DD CC BB AA MirrorMode = (MirrorMode)value; Debug.WriteLine("MMC5 Mirror Mode ($5105) = " + MirrorMode + " (" + Hex.Format((byte)MirrorMode) + ") (" + Bin.Format((byte)MirrorMode) + ")"); return; } if (address == 0x5106) { fillModeTile = value; Debug.WriteLine("MMC5 Fill Mode Tile ($5106) = " + Hex.Format(fillModeTile)); return; } if (address == 0x5107) { int attrbibutes = value & 0x03; // replicate bits 0,1 to rest of byte ------AB -> ABABABAB fillModeAttributes = (byte)(attrbibutes | (attrbibutes << 2) | (attrbibutes << 4) | (attrbibutes << 6)); Debug.WriteLine("MMC5 Fill Mode Attributes ($5107) = " + Hex.Format((byte)attrbibutes)); return; } if (address == 0x5113) { //---- -CBB : C: chip, BB: bank within chip (CBB can be treated as 8 banks) programRamBank = (byte)(value & 0x07); Debug.WriteLine("MMC5 PRG RAM Bank ($5113) = " + Hex.Format(programRamBank)); return; } if (address == 0x5114) { //RBBB BBBB : R - ROM mode, BBBBBBB - bank number romMode0 = (value & 0x80) != 0; programBank0 = (byte)(value & 0x7F); Debug.WriteLine("MMC5 PRG ROM/RAM Bank 0 ($5114) = " + Hex.Format(programBank0) + " in " + (romMode0 ? "ROM" : "RAM") + " mode"); return; } if (address == 0x5115) { //RBBB BBBB : R - ROM mode, BBBBBBB - bank number romMode1 = (value & 0x80) != 0; programBank1 = (byte)(value & 0x7F); Debug.WriteLine("MMC5 PRG ROM/RAM Bank 1 ($5115) = " + Hex.Format(programBank1) + " in " + (romMode1 ? "ROM" : "RAM") + " mode"); return; } if (address == 0x5116) { //RBBB BBBB : R - ROM mode, BBBBBBB - bank number romMode2 = (value & 0x80) != 0; programBank2 = (byte)(value & 0x7F); Debug.WriteLine("MMC5 PRG ROM/RAM Bank 2 ($5116) = " + Hex.Format(programBank2) + " in " + (romMode2 ? "ROM" : "RAM") + " mode"); return; } if (address == 0x5117) { //-BBB BBBB : BBBBBBB - bank number programRomBank = (byte)(value & 0x7F); Debug.WriteLine("MMC5 PRG ROM Bank Register ($5117) = " + Hex.Format(programRomBank)); return; } if (address >= 0x5120 && address <= 0x512B) { if (characterBankCount == 0) { return; } characterBankLastWrittenUpper = address > 0x5127; ushort characterBank = 0; // merge low bits characterBank |= value; // merge high bits characterBank |= characterBankUpper; // ensure within available banks characterBank %= characterBankCount; // assign to corresponding bank switch characterBanks[address - 0x5120] = characterBank; Debug.WriteLine("MMC5 CHR Bank Register (" + Hex.Format(address) + ") = " + Hex.Format(value) + " (" + Hex.Format(characterBank) + ")"); return; } if (address == 0x5130) { // upper 2 bits (bit 8, 9) for character bank selection (all banks) characterBankUpper = value; characterBankUpper &= 0x03; Debug.WriteLine("MMC5 Upper CHR Bits ($5130) = " + Bin.Format((byte)characterBankUpper)); characterBankUpper <<= 8; return; } if (address == 0x5200) { //ES-W WWWW verticalSplitModeEnabled = (value & 0x80) != 0; verticalSplitSide = (VerticalSplitSide)((value >> 6) & 0x01); verticalSplitStartStopTile = (byte)(value & 0x1F); return; } if (address == 0x5203) { irqLatch = value; Debug.WriteLine("MMC5 IRQ Counter($5203) = " + irqLatch); return; } if (address == 0x5204) { irqEnabled = (value & 0x80) != 0; Debug.WriteLine("MMC5 IRQ ($5204) = " + (irqEnabled ? "Enabled" : "Disabled")); return; } if (address == 0x5205) { factor1 = value; EvaluateProduct(); Debug.WriteLine("MMC5 Mul Factor 1 ($5205) = " + Hex.Format(factor1) + " (Result High: " + Hex.Format(productHigh) + ", Low: " + Hex.Format(productLow) + ")"); return; } if (address == 0x5206) { factor2 = value; EvaluateProduct(); Debug.WriteLine("MMC5 Mul Factor 2 ($5205) = " + Hex.Format(factor1) + " (Result High: " + Hex.Format(productHigh) + ", Low: " + Hex.Format(productLow) + ")"); return; } if (address >= 0x5C00 && address < 0x6000) { // expansion ram - all modes Debug.WriteLine("MMC5 Write to Extended RAM (" + Hex.Format(address) + ") = " + Hex.Format(value)); switch (extendedRamMode) { case 0: case 1: // expansion ram mode 0/1 - writes allowed when ppu rendering, otherwise zero written extendedRam[address % 0x400] = ppuRendering ? value : (byte)0; return; case 2: // expansion ram mode 2 - 1K r/w memory extendedRam[address % 0x400] = value; return; case 3: // expansion ram mode 3 - 1K ROM (read only - do nothing?) return; default: throw new Exception("MMC5 Invalid expansion ram mode"); } } if (address >= 0x6000 && address < 0x8000) { // all bank modes - 8K switchable RAM bank int offset = address % 0x2000; programRam[programRamBank * 0x2000 + offset] = value; return; } if (address >= 0x8000) { // program banks for all modes switch (programBankMode) { case 0: { // PRG mode 0 - single 32k switchable ROM bank return; } case 1: if (address < 0xC000) { // PRG mode 1 - first 16k switchable ROM/RAM bank if (!romMode1 && !programRamProtect) { int offset = address % 0x4000; programRam[(programBank1 >> 1) * 0x4000 + offset] = value; } return; } else // if (address >= 0xC000) { // PRG mode 1 - second 16k switchable ROM bank return; } case 2: if (address < 0xC000) { // PRG mode 2 - 16k switchable ROM/RAM bank if (!romMode1 && !programRamProtect) { int offset = address % 0x4000; programRam[(programBank1 >> 1) * 0x4000 + offset] = value; } return; } else if (address < 0xE000) { // PRG mode 2 - first 8k switchable ROM/RAM bank if (!romMode2 && !programRamProtect) { int offset = address % 0x2000; programRam[programBank2 * 0x2000 + offset] = value; } return; } else // if (address >= 0xE000 ) { // PRG mode 2 - second 8k switchable ROM bank return; } case 3: if (address < 0xA000) { // PRG mode 3 - first 8k switchable ROM/RAM bank if (!romMode0 && !programRamProtect) { int offset = address % 0x2000; programRam[programBank0 * 0x2000 + offset] = value; } return; } else if (address < 0xC000) { // PRG mode 3 - second 8k switchable ROM/RAM bank if (!romMode1 && !programRamProtect) { int offset = address % 0x2000; programRam[programBank1 * 0x2000 + offset] = value; } return; } else if (address < 0xE000) { // PRG mode 3 - third 8k switchable ROM/RAM bank if (!romMode2 && !programRamProtect) { int offset = address % 0x2000; programRam[programBank2 * 0x2000 + offset] = value; } return; } else // if (address >= 0xE000) { // PRG mode 3 - fourth 8k switchable ROM bank return; } default: throw new Exception("MMC5 Invalid program bank mode"); } } // invalid / unhandled addresses throw new Exception("Unhandled " + Name + " mapper write 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? } } }