diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index 6de6939ee3..0876ed3bcb 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -485,6 +485,7 @@
+
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/FFE/Mapper017.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/FFE/Mapper017.cs
new file mode 100644
index 0000000000..990f3e9c15
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/FFE/Mapper017.cs
@@ -0,0 +1,174 @@
+using BizHawk.Common;
+using BizHawk.Common.NumberExtensions;
+
+namespace BizHawk.Emulation.Cores.Nintendo.NES
+{
+ public class Mapper017 : NES.NESBoardBase
+ {
+ private ByteBuffer prg_regs_8k = new ByteBuffer(4);
+ private ByteBuffer chr_regs_1k = new ByteBuffer(8);
+
+ private int prg_mask_8k;
+ private int chr_mask_1k;
+
+ private bool irq_enable;
+ private bool irq_pending;
+ private int irq_count;
+ private const int IRQ_DESTINATION = 0x10000;
+
+ public override bool Configure(NES.EDetectionOrigin origin)
+ {
+ switch (Cart.board_type)
+ {
+ case "MAPPER017":
+ break;
+ default:
+ return false;
+ }
+
+ prg_mask_8k = Cart.prg_size / 8 - 1;
+ chr_mask_1k = Cart.chr_size / 1 - 1;
+
+ prg_regs_8k[0] = 0x00;
+ prg_regs_8k[1] = 0x01;
+ prg_regs_8k[2] = 0xFE;
+ prg_regs_8k[3] = 0xFF;
+ SetMirrorType(Cart.pad_h, Cart.pad_v);
+
+ return true;
+ }
+
+ public override void WriteEXP(int addr, byte value)
+ {
+ switch (addr & 0x7FF)
+ {
+ //Mirroring:
+ case 0x2FE:
+ case 0x2FF:
+ int mirroring = ((addr << 1) & 2) | ((value >> 4) & 1);
+ switch (mirroring)
+ {
+ case 0: SetMirrorType(EMirrorType.OneScreenA); break;
+ case 1: SetMirrorType(EMirrorType.OneScreenB); break;
+ case 2: SetMirrorType(EMirrorType.Vertical); break;
+ case 3: SetMirrorType(EMirrorType.Horizontal); break;
+ }
+ break;
+
+ //IRQ
+ case 0x501:
+ irq_enable = value.Bit(0);
+ irq_pending = false;
+ irq_count = 0;
+ SyncIRQ();
+ break;
+ case 0x502:
+ irq_count &= 0xFF00;
+ irq_count |= value;
+ break;
+ case 0x503:
+ irq_count &= 0x00FF;
+ irq_count |= value << 8;
+ irq_enable = true;
+ irq_pending = false;
+ break;
+
+ //PRG
+ case 0x504:
+ case 0x505:
+ case 0x506:
+ case 0x507:
+ prg_regs_8k[addr & 3] = value;
+ break;
+
+ //CHR
+ case 0x510:
+ case 0x511:
+ case 0x512:
+ case 0x513:
+ case 0x514:
+ case 0x515:
+ case 0x516:
+ case 0x517:
+ chr_regs_1k[addr & 7] = value;
+ break;
+ }
+ }
+
+ public override void Dispose()
+ {
+ base.Dispose();
+ prg_regs_8k.Dispose();
+ chr_regs_1k.Dispose();
+ }
+
+ public override void SyncState(Serializer ser)
+ {
+ base.SyncState(ser);
+
+ ser.Sync("prg_regs_8k", ref prg_regs_8k);
+ ser.Sync("chr_regs_1k", ref chr_regs_1k);
+
+ ser.Sync("irq_enable", ref irq_enable);
+ ser.Sync("irq_pending", ref irq_pending);
+ ser.Sync("irq_count", ref irq_count);
+ }
+
+ public override byte ReadPRG(int addr)
+ {
+ int bank_8k = prg_regs_8k[addr >> 13];
+ bank_8k &= prg_mask_8k;
+ int offset = addr & 0x1FFF;
+ return ROM[bank_8k << 13 | offset];
+ }
+
+ public override void WritePPU(int addr, byte value)
+ {
+ if (addr < 0x2000 && VRAM != null)
+ {
+ VRAM[addr] = value;
+ }
+ base.WritePPU(addr, value);
+ }
+
+ public override byte ReadPPU(int addr)
+ {
+ if (addr < 0x2000)
+ {
+ if (VRAM != null) return VRAM[addr];
+
+ int bank_1k = chr_regs_1k[addr >> 10];
+ bank_1k &= chr_mask_1k;
+ int offset = addr & 0x3FF;
+ return VROM[bank_1k << 10 | offset];
+ }
+ return base.ReadPPU(addr);
+ }
+
+ public override void ClockCPU()
+ {
+ if (irq_enable)
+ {
+ ClockIRQ();
+ }
+ }
+
+ private void ClockIRQ()
+ {
+ irq_count++;
+ if (irq_count >= IRQ_DESTINATION)
+ {
+ irq_enable = false;
+ irq_pending = true;
+ }
+
+ SyncIRQ();
+ }
+
+ private void SyncIRQ()
+ {
+ SyncIRQ(irq_pending);
+ }
+
+ }
+}