diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/ExROM.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/ExROM.cs
index 03f5573d61..9e5f22b07a 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/ExROM.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/ExROM.cs
@@ -41,6 +41,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
 		IntBuffer b_banks_1k = new IntBuffer(8);
 		IntBuffer prg_banks_8k = new IntBuffer(4);
 		byte product_low, product_high;
+		int last_nt_read;
 
 		public override void SyncState(Serializer ser)
 		{
@@ -54,7 +55,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
 			ser.Sync("chr_mode", ref chr_mode);
 			ser.Sync("prg_mode", ref prg_mode);
 			ser.Sync("chr_reg_high", ref chr_reg_high);
-			ser.Sync("ab_mode", ref chr_reg_high);
+			ser.Sync("ab_mode", ref ab_mode);
 			ser.Sync("regs_a", ref regs_a);
 			ser.Sync("regs_b", ref regs_b);
 			ser.Sync("regs_prg", ref regs_prg);
@@ -62,6 +63,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
 			ser.Sync("nt_fill_tile", ref nt_fill_tile);
 			ser.Sync("nt_fill_attrib", ref nt_fill_attrib);
 			ser.Sync("wram_bank", ref wram_bank);
+			ser.Sync("last_nt_read", ref last_nt_read);
 			ser.Sync("EXRAM", ref EXRAM, false);
 
 			if (ser.IsReader)
@@ -91,6 +93,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
 				case "NES-ELROM": //Castlevania 3 - Dracula's Curse (U)
 					AssertPrg(128,256); AssertChr(128);
 					break;
+				case "NES-EKROM": //Gemfire (U)
+					AssertPrg(256); AssertChr(256);
+					break;
 				case "MAPPER5":
 					break;
 				default:
@@ -139,12 +144,40 @@ namespace BizHawk.Emulation.Consoles.Nintendo
 			return (bank_8k << 13) | ofs;
 		}
 
+		//this could be handy, but probably not. I did it on accident.
+		//TileCoord ComputeTXTYFromPPUTiming(int visible_scanline, int cycle)
+		//{
+		//  int py = visible_scanline;
+		//  int px = cycle;
+		//  if (cycle > 260)
+		//  {
+		//    py++;
+		//    px -= 322;
+		//  }
+		//  else px += 16;
+		//  int tx = px / 8;
+		//  int ty = py / 8;
+		//  return new TileCoord(tx, ty);
+		//}
+
 		int MapCHR(int addr)
 		{
 			int bank_1k = addr >> 10;
 			int ofs = addr & ((1 << 10) - 1);
 
+			if (exram_mode == 1 && NES.ppu.ppuphase == Nintendo.NES.PPU.PPUPHASE.BG)
+			{
+				int exram_addr = last_nt_read;
+				int bank_4k = EXRAM[exram_addr] & 0x3F;
+				
+				bank_1k = bank_4k * 4;
+				bank_1k += chr_reg_high<<2;
+				ofs = addr & (4 * 1024 - 1);
+				goto MAPPED;
+			}
+
 			//wish this logic could be smaller..
+			//how does this KNOW that its in 8x16 sprites? the pattern of reads... emulate it that way..
 			if (NES.ppu.reg_2000.obj_size_16)
 			{
 				if (NES.ppu.ppuphase == NES.PPU.PPUPHASE.OBJ)
@@ -157,11 +190,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
 					bank_1k = a_banks_1k[bank_1k];
 				else
 					bank_1k = b_banks_1k[bank_1k];
-
-			//something like this..?
-			//bool special_sel = NES.ppu.reg_2000.obj_size_16 && NES.ppu.ppuphase == NES.PPU.PPUPHASE.OBJ;
-			//bool a_sel = special_sel || (!a_sel && ab_mode == 0);
-
+		
+			MAPPED:
 			bank_1k &= chr_bank_mask_1k;
 			addr = (bank_1k<<10)|ofs;
 			return addr;
@@ -177,6 +207,33 @@ namespace BizHawk.Emulation.Consoles.Nintendo
 			else
 			{
 				addr -= 0x2000;
+				int nt_entry = addr & 0x3FF;
+				if (nt_entry < 0x3C0)
+				{
+					//track the last nametable entry read so that subsequent pattern and attribute reads will know which exram address to use
+					last_nt_read = nt_entry;
+				}
+				else
+				{
+					//attribute table
+					if (exram_mode == 1)
+					{
+						//attribute will be in the top 2 bits of the exram byte
+						int exram_addr = last_nt_read;
+						int attribute = EXRAM[exram_addr] >> 6;
+						//calculate tile address by getting x/y from last nametable
+						int tx = last_nt_read & 0x1F;
+						int ty = last_nt_read / 32;
+						//attribute table address is just these coords shifted
+						int atx = tx >> 1;
+						int aty = ty >> 1;
+						//figure out how we need to shift the attribute to fake out the ppu
+						int at_shift = ((aty & 1) << 1) + (atx & 1);
+						at_shift <<= 1;
+						attribute <<= at_shift;
+						return (byte)attribute;
+					}
+				}
 				int nt = addr >> 10;
 				int offset = addr & ((1<<10)-1);
 				nt = nt_modes[nt];
@@ -473,24 +530,28 @@ namespace BizHawk.Emulation.Consoles.Nintendo
 			//MASTER LOGIC: something like this this might be enough to work, but i'll play with it later
 			//bank_1k >> (3 - chr_mode) << chr_mode | bank_1k & ( etc.etc.
 
+			//TODO - do these need to have the last arguments multiplied by 8,4,2 to map to the right banks?
 			switch (chr_mode)
 			{
 				case 0:
-					SetBank(a_banks_1k, 0, 8, regs_a[7]);
-					SetBank(b_banks_1k, 0, 8, regs_a[7]);
+					SetBank(a_banks_1k, 0, 8, regs_a[7] * 8);
+					SetBank(b_banks_1k, 0, 8, regs_b[3] * 8);
 					break;
 				case 1:
-					SetBank(a_banks_1k, 0, 4, regs_a[3]);
-					SetBank(a_banks_1k, 4, 4, regs_a[7]);
-					SetBank(b_banks_1k, 0, 4, regs_b[3]);
+					SetBank(a_banks_1k, 0, 4, regs_a[3] * 4);
+					SetBank(a_banks_1k, 4, 4, regs_a[7] * 4);
+					SetBank(b_banks_1k, 0, 4, regs_b[3] * 4);
+					SetBank(b_banks_1k, 4, 4, regs_b[3] * 4);
 					break;
 				case 2:
-					SetBank(a_banks_1k, 0, 2, regs_a[1]);
-					SetBank(a_banks_1k, 2, 2, regs_a[3]);
-					SetBank(a_banks_1k, 4, 2, regs_a[5]);
-					SetBank(a_banks_1k, 6, 2, regs_a[7]);
-					SetBank(b_banks_1k, 0, 2, regs_b[1]);
-					SetBank(b_banks_1k, 2, 2, regs_b[3]);
+					SetBank(a_banks_1k, 0, 2, regs_a[1] * 2);
+					SetBank(a_banks_1k, 2, 2, regs_a[3] * 2);
+					SetBank(a_banks_1k, 4, 2, regs_a[5] * 2);
+					SetBank(a_banks_1k, 6, 2, regs_a[7] * 2);
+					SetBank(b_banks_1k, 0, 2, regs_b[1] * 2);
+					SetBank(b_banks_1k, 2, 2, regs_b[3] * 2);
+					SetBank(b_banks_1k, 4, 2, regs_b[1] * 2);
+					SetBank(b_banks_1k, 6, 2, regs_b[3] * 2);
 					break;
 				case 3:
 					SetBank(a_banks_1k, 0, 1, regs_a[0]);
@@ -505,12 +566,13 @@ namespace BizHawk.Emulation.Consoles.Nintendo
 					SetBank(b_banks_1k, 1, 1, regs_b[1]);
 					SetBank(b_banks_1k, 2, 1, regs_b[2]);
 					SetBank(b_banks_1k, 3, 1, regs_b[3]);
+					SetBank(b_banks_1k, 4, 1, regs_b[0]);
+					SetBank(b_banks_1k, 5, 1, regs_b[1]);
+					SetBank(b_banks_1k, 6, 1, regs_b[2]);
+					SetBank(b_banks_1k, 7, 1, regs_b[3]);
 					break;
 			}
-			b_banks_1k[4] = b_banks_1k[0];
-			b_banks_1k[5] = b_banks_1k[1];
-			b_banks_1k[6] = b_banks_1k[2];
-			b_banks_1k[7] = b_banks_1k[3];
+
 
 		}