using BizHawk.Common; namespace BizHawk.Emulation.Cores.Nintendo.NES { // http://wiki.nesdev.com/w/index.php/User:Tepples/Multi-discrete_mapper public sealed class Mapper028 : NES.NESBoardBase { // config int chr_mask_8k; int prg_mask_16k; // state int reg; int chr; int prg; int mode; int outer; // regennable state int prglo; int prghi; public override bool Configure(NES.EDetectionOrigin origin) { switch (Cart.board_type) { case "MAPPER028": break; default: return false; } AssertPrg(32, 64, 128, 256, 512, 1024, 2048); AssertChr(0); chr_mask_8k = Cart.chr_size / 8 - 1; prg_mask_16k = Cart.prg_size / 16 - 1; Cart.wram_size = 0; Cart.vram_size = 32; // the only part of initial state that is important is that // C000:FFFF contains the tail end of the rom outer = 63; prg = 15; Sync(); return true; } void Sync() { int outb = outer << 1; // this can probably be rolled up, but i have no motivation to do so // until it's been tested switch (mode & 0x3c) { // 32K modes case 0x00: case 0x04: prglo = outb; prghi = outb | 1; break; case 0x10: case 0x14: prglo = outb & ~2 | prg << 1 & 2; prghi = outb & ~2 | prg << 1 & 2 | 1; break; case 0x20: case 0x24: prglo = outb & ~6 | prg << 1 & 6; prghi = outb & ~6 | prg << 1 & 6 | 1; break; case 0x30: case 0x34: prglo = outb & ~14 | prg << 1 & 14; prghi = outb & ~14 | prg << 1 & 14 | 1; break; // bottom fixed modes case 0x08: prglo = outb; prghi = outb | prg & 1; break; case 0x18: prglo = outb; prghi = outb & ~2 | prg & 3; break; case 0x28: prglo = outb; prghi = outb & ~6 | prg & 7; break; case 0x38: prglo = outb; prghi = outb & ~14 | prg & 15; break; // top fixed modes case 0x0c: prglo = outb | prg & 1; prghi = outb | 1; break; case 0x1c: prglo = outb & ~2 | prg & 3; prghi = outb | 1; break; case 0x2c: prglo = outb & ~6 | prg & 7; prghi = outb | 1; break; case 0x3c: prglo = outb & ~14 | prg & 15; prghi = outb | 1; break; } prglo &= prg_mask_16k; prghi &= prg_mask_16k; } void Mirror(byte value) { if ((mode & 2) == 0) { mode &= 0xfe; mode |= value >> 4 & 1; } SyncMirror(); } void SyncMirror() { switch (mode & 3) { case 0: SetMirrorType(EMirrorType.OneScreenA); break; case 1: SetMirrorType(EMirrorType.OneScreenB); break; case 2: SetMirrorType(EMirrorType.Vertical); break; case 3: SetMirrorType(EMirrorType.Horizontal); break; } } public override void WriteEXP(int addr, byte value) { if (addr >= 0x1000) reg = value & 0x81; } public override void WritePRG(int addr, byte value) { switch (reg) { case 0x00: chr = value & 3; Mirror(value); break; case 0x01: prg = value & 15; Mirror(value); Sync(); break; case 0x80: mode = value & 63; SyncMirror(); Sync(); break; case 0x81: outer = value & 63; Sync(); break; } } public override byte ReadPPU(int addr) { if (addr < 0x2000) return VRAM[addr | chr << 13]; else return base.ReadPPU(addr); } public override void WritePPU(int addr, byte value) { if (addr < 0x2000) VRAM[addr | chr << 13] = value; else base.WritePPU(addr, value); } public override byte ReadPRG(int addr) { return ROM[(addr & 0x3fff) | (addr < 0x4000 ? prglo : prghi) << 14]; } public override void SyncState(Serializer ser) { base.SyncState(ser); ser.Sync("reg", ref reg); ser.Sync("chr", ref chr); ser.Sync("prg", ref prg); ser.Sync("mode", ref mode); ser.Sync("outer", ref outer); if (!ser.IsWriter) Sync(); } } }