diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj
index da3ced6aef..28f8a16142 100644
--- a/BizHawk.Emulation/BizHawk.Emulation.csproj
+++ b/BizHawk.Emulation/BizHawk.Emulation.csproj
@@ -204,6 +204,7 @@
+
Code
diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Mapper028.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Mapper028.cs
new file mode 100644
index 0000000000..761a9e3342
--- /dev/null
+++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Mapper028.cs
@@ -0,0 +1,196 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BizHawk.Emulation.Consoles.Nintendo
+{
+ // http://wiki.nesdev.com/w/index.php/User:Tepples/Multi-discrete_mapper
+ public 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;
+ 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 & 2;
+ prghi = outb & ~2 | prg & 2 | 1;
+ break;
+ case 0x20:
+ case 0x24:
+ prglo = outb & ~6 | prg & 6;
+ prghi = outb & ~6 | prg & 6 | 1;
+ break;
+ case 0x30:
+ case 0x34:
+ prglo = outb & ~14 | prg & 14;
+ prghi = outb & ~14 | prg & 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();
+ }
+ }
+}