diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index 8c38a41d73..16404afba4 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -340,6 +340,7 @@
+
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/CoolBoy.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/CoolBoy.cs
new file mode 100644
index 0000000000..b3cb891892
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/CoolBoy.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using BizHawk.Common;
+using BizHawk.Common.NumberExtensions;
+
+namespace BizHawk.Emulation.Cores.Nintendo.NES
+{
+ public sealed class CoolBoy : MMC3Board_Base
+ {
+ // eldritch horror pirate multicart
+ // 32MB prg rom, no prg ram, no chr rom, 128KB chr ram
+
+ // behavior directly from fceu-mm
+
+ // this could be broken down into more sensibly named variables
+ byte[] exp = new byte[4];
+
+ public override bool Configure(NES.EDetectionOrigin origin)
+ {
+ switch (Cart.board_type)
+ {
+ case "UNIF_COOLBOY":
+ AssertChr(0);
+ break;
+ default:
+ return false;
+ }
+
+ Cart.vram_size = 128;
+ Cart.wram_size = 0;
+
+ BaseSetup();
+
+ // normally base mmc3 sets this right, but it has a hardcoded assumption
+ // that no chr rom => 8K of chr ram, not 128K. i'd change the base class,
+ // but not sure if that would break something else
+ chr_mask = 127;
+
+ return true;
+ }
+
+ public override void WriteWRAM(int addr, byte value)
+ {
+ if (addr < 0x1000)
+ {
+ if (!exp[3].Bit(7))
+ {
+ exp[addr & 3] = value;
+ }
+ }
+ }
+
+ protected override int Get_PRGBank_8K(int addr)
+ {
+ int mask = 0, shift = 0;
+ int baseaddr = exp[0] & 0x07 | (exp[1] & 0x10) >> 1 | (exp[1] & 0x0c) << 2 | (exp[0] & 0x30) << 2;
+
+ switch (exp[0] & 0xc0)
+ {
+ case 0x00:
+ baseaddr >>= 2;
+ mask = 0x3f;
+ shift = 6;
+ break;
+ case 0x80:
+ baseaddr >>= 1;
+ mask = 0x1f;
+ shift = 5;
+ break;
+ case 0xc0:
+ shift = 4;
+ if (exp[3].Bit(4))
+ {
+ mask = 1 | exp[1] & 2;
+ }
+ else
+ {
+ mask = 0xf;
+ }
+ break;
+ case 0x40:
+ // ??
+ throw new InvalidOperationException();
+ }
+
+ int v = base.Get_PRGBank_8K(addr);
+
+ int ret = baseaddr << shift | v & mask;
+ if (exp[3].Bit(0))
+ {
+ ret |= exp[3] & (0x0e ^ exp[1] & 2);
+ }
+ return ret;
+ }
+
+ protected override int Get_CHRBank_1K(int addr)
+ {
+ if (exp[3].Bit(4))
+ {
+ return (exp[2] & 15) << 3 | addr >> 10 & 7;
+ }
+ else
+ {
+ return base.Get_CHRBank_1K(addr);
+ }
+ }
+
+ public override void SyncState(Serializer ser)
+ {
+ base.SyncState(ser);
+ ser.Sync("exp", ref exp, false);
+ }
+ }
+}