using BizHawk.Common; namespace BizHawk.Emulation.Cores.Nintendo.NES { //AKA mapper 67 //this may be confusing due to general chaos with the early sunsoft mappers. see docs/sunsoft.txt public sealed class Sunsoft3 : NES.NESBoardBase { //configuration int prg_bank_mask_16k, chr_bank_mask_2k; //state bool toggle; ByteBuffer prg_banks_16k = new ByteBuffer(2); ByteBuffer chr_banks_2k = new ByteBuffer(4); int irq_counter; bool irq_enable; bool irq_asserted; int clock_counter; public override void SyncState(Serializer ser) { base.SyncState(ser); ser.Sync("toggle", ref toggle); ser.Sync("prg_banks_16k", ref prg_banks_16k); ser.Sync("chr_banks_2k", ref chr_banks_2k); ser.Sync("irq_counter", ref irq_counter); ser.Sync("irq_enable", ref irq_enable); ser.Sync("irq_asserted", ref irq_asserted); ser.Sync("clock_counter", ref clock_counter); SyncIRQ(); } public override bool Configure(NES.EDetectionOrigin origin) { switch (Cart.board_type) { case "MAPPER067": break; case "SUNSOFT-3": AssertPrg(128); AssertChr(128); break; default: return false; } prg_bank_mask_16k = (Cart.prg_size / 16) - 1; chr_bank_mask_2k = (Cart.chr_size / 2) - 1; prg_banks_16k[0] = 0; prg_banks_16k[1] = 0xFF; ApplyMemoryMapMask(prg_bank_mask_16k, prg_banks_16k); return true; } void SetCHR(int block, byte value) { chr_banks_2k[block] = value; ApplyMemoryMapMask(chr_bank_mask_2k, chr_banks_2k); } void SyncIRQ() { IRQSignal = irq_asserted; } public override void WritePRG(int addr, byte value) { //Console.WriteLine("{0:X4} = {1:X2}", addr, value); switch (addr & 0xF800) { case 0x0800: //0x8800 SetCHR(0, value); break; case 0x1800: //0x9800 SetCHR(1, value); break; case 0x2800: //0xA800 SetCHR(2, value); break; case 0x3800: //0xB800 SetCHR(3, value); break; case 0x4800: //0xC800 if (!toggle) irq_counter = (irq_counter & 0xFF) | (value << 8); else irq_counter = (irq_counter & 0xFF00) | (value); toggle ^= true; break; case 0x5800: //0xD800 irq_enable = value.Bit(4); toggle = false; irq_asserted = false; SyncIRQ(); break; case 0x6800: //0xE800: switch (value & 3) { case 0: SetMirrorType(NES.NESBoardBase.EMirrorType.Vertical); break; case 1: SetMirrorType(NES.NESBoardBase.EMirrorType.Horizontal); break; case 2: SetMirrorType(NES.NESBoardBase.EMirrorType.OneScreenA); break; case 3: SetMirrorType(NES.NESBoardBase.EMirrorType.OneScreenB); break; } break; case 0x7800: //0xF800: prg_banks_16k[0] = value; ApplyMemoryMapMask(prg_bank_mask_16k, prg_banks_16k); break; } } public override void ClockCPU() { if (!irq_enable) return; if (irq_counter == 0) { irq_counter = 0xFFFF; //Console.WriteLine("IRQ!!!"); irq_asserted = true; SyncIRQ(); irq_enable = false; } else irq_counter--; } public override byte ReadPRG(int addr) { addr = ApplyMemoryMap(14, prg_banks_16k, addr); return ROM[addr]; } public override byte ReadPPU(int addr) { if (addr < 0x2000) { addr = ApplyMemoryMap(11, chr_banks_2k, addr); return base.ReadPPUChr(addr); } else return base.ReadPPU(addr); } /* public override void ClockPPU() { clock_counter++; if (clock_counter == 3) { clock_counter = 0; ClockCPU(); } }*/ } }