diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp
index c224fdf725..6549307c85 100644
--- a/libgambatte/src/cinterface.cpp
+++ b/libgambatte/src/cinterface.cpp
@@ -29,9 +29,9 @@ GBEXPORT void gambatte_destroy(GB *g)
 	delete g;
 }
 
-GBEXPORT int gambatte_load(GB *g, const char *romfiledata, unsigned romfilelength, long long now, unsigned flags)
+GBEXPORT int gambatte_load(GB *g, const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, long long now, unsigned flags)
 {
-	int ret = g->load(romfiledata, romfilelength, now, flags);
+	int ret = g->load(romfiledata, romfilelength, biosfiledata, biosfilelength, now, flags);
 	return ret;
 }
 
diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h
index 02fabe979e..1310257e86 100644
--- a/libgambatte/src/cpu.h
+++ b/libgambatte/src/cpu.h
@@ -1,132 +1,136 @@
-/***************************************************************************
- *   Copyright (C) 2007 by Sindre Aamås                                    *
- *   aamas@stud.ntnu.no                                                    *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License version 2 as     *
- *   published by the Free Software Foundation.                            *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License version 2 for more details.                *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   version 2 along with this program; if not, write to the               *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-#ifndef CPU_H
-#define CPU_H
-
-#include "memory.h"
+/***************************************************************************
+ *   Copyright (C) 2007 by Sindre Aamås                                    *
+ *   aamas@stud.ntnu.no                                                    *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License version 2 as     *
+ *   published by the Free Software Foundation.                            *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License version 2 for more details.                *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   version 2 along with this program; if not, write to the               *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef CPU_H
+#define CPU_H
+
+#include "memory.h"
 #include "newstate.h"
-
-namespace gambatte {
-
-class CPU {
-	Memory memory;
-	
-	unsigned long cycleCounter_;
-
-	unsigned short PC;
-	unsigned short SP;
-	
-	unsigned HF1, HF2, ZF, CF;
-
-	unsigned char A, B, C, D, E, /*F,*/ H, L;
-
-	bool skip;
-	
-	void process(unsigned long cycles);
-	
-	void (*tracecallback)(void *);
-
-public:
-	
-	CPU();
-// 	void halt();
-
-// 	unsigned interrupt(unsigned address, unsigned cycleCounter);
-	
-	long runFor(unsigned long cycles);
-	void setStatePtrs(SaveState &state);
-	void loadState(const SaveState &state);
-	void setLayers(unsigned mask) { memory.setLayers(mask); }
-	
-	void loadSavedata(const char *data) { memory.loadSavedata(data); }
-	int saveSavedataLength() {return memory.saveSavedataLength(); }
-	void saveSavedata(char *dest) { memory.saveSavedata(dest); }
-	
-	bool getMemoryArea(int which, unsigned char **data, int *length) { return memory.getMemoryArea(which, data, length); }
-
-	void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
-		memory.setVideoBuffer(videoBuf, pitch);
-	}
-	
-	void setInputGetter(unsigned (*getInput)()) {
-		memory.setInputGetter(getInput);
-	}
-
-	void setReadCallback(void (*callback)(unsigned)) {
-		memory.setReadCallback(callback);
-	}
-
-	void setWriteCallback(void (*callback)(unsigned)) {
-		memory.setWriteCallback(callback);
-	}
-
-	void setExecCallback(void (*callback)(unsigned)) {
-		memory.setExecCallback(callback);
-	}
-
-	void setCDCallback(CDCallback cdc) {
-		memory.setCDCallback(cdc);
-	}
-
-	void setTraceCallback(void (*callback)(void *)) {
-		tracecallback = callback;
-	}
-
-	void setScanlineCallback(void (*callback)(), int sl) {
-		memory.setScanlineCallback(callback, sl);
-	}
-
-	void setRTCCallback(std::uint32_t (*callback)()) {
-		memory.setRTCCallback(callback);
-	}
-	
-	int load(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat) {
-		return memory.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat);
-	}
-	
-	bool loaded() const { return memory.loaded(); }
-	const char * romTitle() const { return memory.romTitle(); }
-	
-	void setSoundBuffer(uint_least32_t *const buf) { memory.setSoundBuffer(buf); }
-	unsigned fillSoundBuffer() { return memory.fillSoundBuffer(cycleCounter_); }
-	
-	bool isCgb() const { return memory.isCgb(); }
-	
-	void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) {
-		memory.setDmgPaletteColor(palNum, colorNum, rgb32);
-	}
-
-	void setCgbPalette(unsigned *lut) {
-		memory.setCgbPalette(lut);
-	}
-	
-	//unsigned char ExternalRead(unsigned short addr) { return memory.read(addr, cycleCounter_); }
-	unsigned char ExternalRead(unsigned short addr) { return memory.peek(addr); }
-	void ExternalWrite(unsigned short addr, unsigned char val) { memory.write_nocb(addr, val, cycleCounter_); }
-
-	int LinkStatus(int which) { return memory.LinkStatus(which); }
-
-	void GetRegs(int *dest);
-
+
+namespace gambatte {
+
+class CPU {
+	Memory memory;
+	
+	unsigned long cycleCounter_;
+
+	unsigned short PC;
+	unsigned short SP;
+	
+	unsigned HF1, HF2, ZF, CF;
+
+	unsigned char A, B, C, D, E, /*F,*/ H, L;
+
+	bool skip;
+	
+	void process(unsigned long cycles);
+	
+	void (*tracecallback)(void *);
+
+public:
+	
+	CPU();
+// 	void halt();
+
+// 	unsigned interrupt(unsigned address, unsigned cycleCounter);
+	
+	long runFor(unsigned long cycles);
+	void setStatePtrs(SaveState &state);
+	void loadState(const SaveState &state);
+	void setLayers(unsigned mask) { memory.setLayers(mask); }
+	
+	void loadSavedata(const char *data) { memory.loadSavedata(data); }
+	int saveSavedataLength() {return memory.saveSavedataLength(); }
+	void saveSavedata(char *dest) { memory.saveSavedata(dest); }
+	
+	bool getMemoryArea(int which, unsigned char **data, int *length) { return memory.getMemoryArea(which, data, length); }
+
+	void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
+		memory.setVideoBuffer(videoBuf, pitch);
+	}
+	
+	void setInputGetter(unsigned (*getInput)()) {
+		memory.setInputGetter(getInput);
+	}
+
+	void setReadCallback(void (*callback)(unsigned)) {
+		memory.setReadCallback(callback);
+	}
+
+	void setWriteCallback(void (*callback)(unsigned)) {
+		memory.setWriteCallback(callback);
+	}
+
+	void setExecCallback(void (*callback)(unsigned)) {
+		memory.setExecCallback(callback);
+	}
+
+	void setCDCallback(CDCallback cdc) {
+		memory.setCDCallback(cdc);
+	}
+
+	void setTraceCallback(void (*callback)(void *)) {
+		tracecallback = callback;
+	}
+
+	void setScanlineCallback(void (*callback)(), int sl) {
+		memory.setScanlineCallback(callback, sl);
+	}
+
+	void setRTCCallback(std::uint32_t (*callback)()) {
+		memory.setRTCCallback(callback);
+	}
+
+	void reset_bios(int setting) {
+		memory.bios_reset(setting);
+	}
+	
+	int load(const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, bool forceDmg, bool multicartCompat) {
+		return memory.loadROM(romfiledata, romfilelength, biosfiledata, biosfilelength, forceDmg, multicartCompat);
+	}
+	
+	bool loaded() const { return memory.loaded(); }
+	const char * romTitle() const { return memory.romTitle(); }
+	
+	void setSoundBuffer(uint_least32_t *const buf) { memory.setSoundBuffer(buf); }
+	unsigned fillSoundBuffer() { return memory.fillSoundBuffer(cycleCounter_); }
+	
+	bool isCgb() const { return memory.isCgb(); }
+	
+	void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) {
+		memory.setDmgPaletteColor(palNum, colorNum, rgb32);
+	}
+
+	void setCgbPalette(unsigned *lut) {
+		memory.setCgbPalette(lut);
+	}
+	
+	//unsigned char ExternalRead(unsigned short addr) { return memory.read(addr, cycleCounter_); }
+	unsigned char ExternalRead(unsigned short addr) { return memory.peek(addr); }
+	void ExternalWrite(unsigned short addr, unsigned char val) { memory.write_nocb(addr, val, cycleCounter_); }
+
+	int LinkStatus(int which) { return memory.LinkStatus(which); }
+
+	void GetRegs(int *dest);
+
 	template<bool isReader>void SyncState(NewState *ns);
-};
-
-}
-
-#endif
+};
+
+}
+
+#endif
diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp
index d56632d8f5..9a4fa62bf2 100644
--- a/libgambatte/src/gambatte.cpp
+++ b/libgambatte/src/gambatte.cpp
@@ -94,7 +94,15 @@ void GB::reset(const std::uint32_t now) {
 		
 		SaveState state;
 		p_->cpu.setStatePtrs(state);
-		setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode, now);
+		if (use_bios) 
+		{
+			p_->cpu.reset_bios(0);
+		}
+		else 
+		{
+
+		}
+		setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode, now, use_bios);
 		p_->cpu.loadState(state);
 		if (length > 0)
 		{
@@ -136,16 +144,17 @@ void GB::setRTCCallback(std::uint32_t (*callback)()) {
 	p_->cpu.setRTCCallback(callback);
 }
 
-int GB::load(const char *romfiledata, unsigned romfilelength, const std::uint32_t now, const unsigned flags) {
+int GB::load(const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, const std::uint32_t now, const unsigned flags) {
 	//if (p_->cpu.loaded())
 	//	p_->cpu.saveSavedata();
 	
-	const int failed = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT);
+	const int failed = p_->cpu.load(romfiledata, romfilelength, biosfiledata, biosfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT);
+	use_bios = biosfilelength > 0 ? true : false;
 	
 	if (!failed) {
 		SaveState state;
 		p_->cpu.setStatePtrs(state);
-		setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB, now);
+		setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB, now, use_bios);
 		p_->cpu.loadState(state);
 		//p_->cpu.loadSavedata();
 	}
diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp
index 6430061789..e2e1892a76 100644
--- a/libgambatte/src/initstate.cpp
+++ b/libgambatte/src/initstate.cpp
@@ -1146,7 +1146,7 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) {
 
 } // anon namespace
 
-void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, const std::uint32_t now) {
+void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, const std::uint32_t now, bool boot_bios) {
 	static const unsigned char cgbObjpDump[0x40] = {
 		0x00, 0x00, 0xF2, 0xAB, 
 		0x61, 0xC2, 0xD9, 0xBA, 
@@ -1166,22 +1166,47 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
 		0x83, 0x40, 0x0B, 0x77
 	};
 	
-	state.cpu.cycleCounter = cgb ? 0x102A0 : 0x102A0 + 0x8D2C;
-	state.cpu.PC = 0x100;
-	state.cpu.SP = 0xFFFE;
-	state.cpu.A = cgb * 0x10 | 0x01;
-	state.cpu.B = cgb & gbaCgbMode;
-	state.cpu.C = 0x13;
-	state.cpu.D = 0x00;
-	state.cpu.E = 0xD8;
-	state.cpu.F = 0xB0;
-	state.cpu.H = 0x01;
-	state.cpu.L = 0x4D;
-	state.cpu.skip = false;
+	if (boot_bios) 
+	{
+		state.cpu.PC = 0x00;
+		state.cpu.SP = 0xFFFF;
+		state.cpu.A = 0;
+		state.cpu.B = 0;
+		state.cpu.C = 0x0;
+		state.cpu.D = 0x0;
+		state.cpu.E = 0x0;
+		state.cpu.F = 0x0;
+		state.cpu.H = 0x0;
+		state.cpu.L = 0x0;
+		state.cpu.skip = false;
+		state.cpu.cycleCounter = 0;
+		state.mem.ioamhram.ptr[0x140] = 0x00;
+		state.mem.ioamhram.ptr[0x104] = 0x00;
+
+	}
+	else
+	{
+		state.cpu.PC = 0x100;
+		state.cpu.SP = 0xFFFE;
+		state.cpu.A = cgb * 0x10 | 0x01;
+		state.cpu.B = cgb & gbaCgbMode;
+		state.cpu.C = 0x13;
+		state.cpu.D = 0x00;
+		state.cpu.E = 0xD8;
+		state.cpu.F = 0xB0;
+		state.cpu.H = 0x01;
+		state.cpu.L = 0x4D;
+		state.cpu.skip = false;
+		setInitialVram(state.mem.vram.ptr, cgb);
+		state.cpu.cycleCounter = cgb ? 0x102A0 : 0x102A0 + 0x8D2C;
+		state.mem.ioamhram.ptr[0x140] = 0x91;
+		state.mem.ioamhram.ptr[0x104] = 0x1C;
+
+	}
 	
 	std::memset(state.mem.sram.ptr, 0xFF, state.mem.sram.getSz());
 	
-	setInitialVram(state.mem.vram.ptr, cgb);
+	
 
 	if (cgb) {
 		setInitialCgbWram(state.mem.wram.ptr);
@@ -1191,8 +1216,6 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
 		setInitialDmgIoamhram(state.mem.ioamhram.ptr);
 	}
 	
-	state.mem.ioamhram.ptr[0x104] = 0x1C;
-	state.mem.ioamhram.ptr[0x140] = 0x91;
 	state.mem.ioamhram.ptr[0x144] = 0x00;
 	
 	state.mem.divLastUpdate = 0;
@@ -1258,7 +1281,6 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
 	state.ppu.oldWy = state.mem.ioamhram.get()[0x14A];
 	state.ppu.pendingLcdstatIrq = false;
 	
-	
 	state.spu.cycleCounter = 0x1000 | (state.cpu.cycleCounter >> 1 & 0xFFF); // spu.cycleCounter >> 12 & 7 represents the frame sequencer position.
 	
 	state.spu.ch1.sweep.counter = SoundUnit::COUNTER_DISABLED;
diff --git a/libgambatte/src/initstate.h b/libgambatte/src/initstate.h
index 8d8ed5aaf6..ec56fae045 100644
--- a/libgambatte/src/initstate.h
+++ b/libgambatte/src/initstate.h
@@ -22,7 +22,7 @@
 #include <cstdint>
 
 namespace gambatte {
-void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, std::uint32_t now);
+void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, std::uint32_t now, bool boot_bios);
 }
 
 #endif
diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp
index 4bae856679..79d9820e16 100644
--- a/libgambatte/src/memory.cpp
+++ b/libgambatte/src/memory.cpp
@@ -1,361 +1,361 @@
-/***************************************************************************
- *   Copyright (C) 2007 by Sindre Aamås                                    *
- *   aamas@stud.ntnu.no                                                    *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License version 2 as     *
- *   published by the Free Software Foundation.                            *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License version 2 for more details.                *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   version 2 along with this program; if not, write to the               *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-#include "memory.h"
-#include "video.h"
-#include "sound.h"
-#include "savestate.h"
-#include <cstring>
-
-namespace gambatte {
-
-Memory::Memory(const Interrupter &interrupter_in)
-: readCallback(0),
-  writeCallback(0),
-  execCallback(0),
-  cdCallback(0),
-  getInput(0),
-  divLastUpdate(0),
-  lastOamDmaUpdate(DISABLED_TIME),
-  display(ioamhram, 0, VideoInterruptRequester(&intreq)),
-  interrupter(interrupter_in),
-  dmaSource(0),
-  dmaDestination(0),
-  oamDmaPos(0xFE),
-  serialCnt(0),
-  blanklcd(false),
-  LINKCABLE(false),
-  linkClockTrigger(false)
-{
-	intreq.setEventTime<BLIT>(144*456ul);
-	intreq.setEventTime<END>(0);
-}
-
-void Memory::setStatePtrs(SaveState &state) {
-	state.mem.ioamhram.set(ioamhram, sizeof ioamhram);
-
-	cart.setStatePtrs(state);
-	display.setStatePtrs(state);
-	sound.setStatePtrs(state);
-}
-
-
-static inline int serialCntFrom(const unsigned long cyclesUntilDone, const bool cgbFast) {
-	return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9;
-}
-
-void Memory::loadState(const SaveState &state) {
-	sound.loadState(state);
-	display.loadState(state, state.mem.oamDmaPos < 0xA0 ? cart.rdisabledRam() : ioamhram);
-	tima.loadState(state, TimaInterruptRequester(intreq));
-	cart.loadState(state);
-	intreq.loadState(state);
-
-	divLastUpdate = state.mem.divLastUpdate;
-	intreq.setEventTime<SERIAL>(state.mem.nextSerialtime > state.cpu.cycleCounter ? state.mem.nextSerialtime : state.cpu.cycleCounter);
-	intreq.setEventTime<UNHALT>(state.mem.unhaltTime);
-	lastOamDmaUpdate = state.mem.lastOamDmaUpdate;
-	dmaSource = state.mem.dmaSource;
-	dmaDestination = state.mem.dmaDestination;
-	oamDmaPos = state.mem.oamDmaPos;
-	serialCnt = intreq.eventTime(SERIAL) != DISABLED_TIME
-			? serialCntFrom(intreq.eventTime(SERIAL) - state.cpu.cycleCounter, ioamhram[0x102] & isCgb() * 2)
-			: 8;
-
-	cart.setVrambank(ioamhram[0x14F] & isCgb());
-	cart.setOamDmaSrc(OAM_DMA_SRC_OFF);
-	cart.setWrambank(isCgb() && (ioamhram[0x170] & 0x07) ? ioamhram[0x170] & 0x07 : 1);
-
-	if (lastOamDmaUpdate != DISABLED_TIME) {
-		oamDmaInitSetup();
-
-		const unsigned oamEventPos = oamDmaPos < 0xA0 ? 0xA0 : 0x100;
-
-		intreq.setEventTime<OAM>(lastOamDmaUpdate + (oamEventPos - oamDmaPos) * 4);
-	}
-
-	intreq.setEventTime<BLIT>((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : state.cpu.cycleCounter);
-	blanklcd = false;
-	
-	if (!isCgb())
-		std::memset(cart.vramdata() + 0x2000, 0, 0x2000);
-}
-
-void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) {
-	if (intreq.eventTime(BLIT) <= cycleCounter)
-		intreq.setEventTime<BLIT>(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed()));
-	
-	intreq.setEventTime<END>(cycleCounter + (inc << isDoubleSpeed()));
-}
-
-void Memory::updateSerial(const unsigned long cc) {
-	if (!LINKCABLE) {
-		if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
-			if (intreq.eventTime(SERIAL) <= cc) {
-				ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF;
-				ioamhram[0x102] &= 0x7F;
-				intreq.setEventTime<SERIAL>(DISABLED_TIME);
-				intreq.flagIrq(8);
-			} else {
-				const int targetCnt = serialCntFrom(intreq.eventTime(SERIAL) - cc, ioamhram[0x102] & isCgb() * 2);
-				ioamhram[0x101] = (((ioamhram[0x101] + 1) << (serialCnt - targetCnt)) - 1) & 0xFF;
-				serialCnt = targetCnt;
-			}
-		}
-	}
-	else {
-		if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
-			if (intreq.eventTime(SERIAL) <= cc) {
-				linkClockTrigger = true;
-				intreq.setEventTime<SERIAL>(DISABLED_TIME);
-			}
-		}
-	}
-}
-
-void Memory::updateTimaIrq(const unsigned long cc) {
-	while (intreq.eventTime(TIMA) <= cc)
-		tima.doIrqEvent(TimaInterruptRequester(intreq));
-}
-
-void Memory::updateIrqs(const unsigned long cc) {
-	updateSerial(cc);
-	updateTimaIrq(cc);
-	display.update(cc);
-}
-
-unsigned long Memory::event(unsigned long cycleCounter) {
-	if (lastOamDmaUpdate != DISABLED_TIME)
-		updateOamDma(cycleCounter);
-
-	switch (intreq.minEventId()) {
-	case UNHALT:
-		intreq.unhalt();
-		intreq.setEventTime<UNHALT>(DISABLED_TIME);
-		break;
-	case END:
-		intreq.setEventTime<END>(DISABLED_TIME - 1);
-
-		while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(END) != DISABLED_TIME)
-			cycleCounter = event(cycleCounter);
-		
-		intreq.setEventTime<END>(DISABLED_TIME);
-
-		break;
-	case BLIT:
-		{
-			const bool lcden = ioamhram[0x140] >> 7 & 1;
-			unsigned long blitTime = intreq.eventTime(BLIT);
-			
-			if (lcden | blanklcd) {
-				display.updateScreen(blanklcd, cycleCounter);
-				intreq.setEventTime<BLIT>(DISABLED_TIME);
-				intreq.setEventTime<END>(DISABLED_TIME);
-				
-				while (cycleCounter >= intreq.minEventTime())
-					cycleCounter = event(cycleCounter);
-			} else
-				blitTime += 70224 << isDoubleSpeed();
-			
-			blanklcd = lcden ^ 1;
-			intreq.setEventTime<BLIT>(blitTime);
-		}
-		break;
-	case SERIAL:
-		updateSerial(cycleCounter);
-		break;
-	case OAM:
-		intreq.setEventTime<OAM>(lastOamDmaUpdate == DISABLED_TIME ?
-				static_cast<unsigned long>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4);
-		break;
-	case DMA:
-		{
-			const bool doubleSpeed = isDoubleSpeed();
-			unsigned dmaSrc = dmaSource;
-			unsigned dmaDest = dmaDestination;
-			unsigned dmaLength = ((ioamhram[0x155] & 0x7F) + 0x1) * 0x10;
-			unsigned length = hdmaReqFlagged(intreq) ? 0x10 : dmaLength;
-			
-			ackDmaReq(&intreq);
-
-			if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) {
-				length = 0x10000 - dmaDest;
-				ioamhram[0x155] |= 0x80;
-			}
-
-			dmaLength -= length;
-
-			if (!(ioamhram[0x140] & 0x80))
-				dmaLength = 0;
-
-			{
-				unsigned long lOamDmaUpdate = lastOamDmaUpdate;
-				lastOamDmaUpdate = DISABLED_TIME;
-
-				while (length--) {
-					const unsigned src = dmaSrc++ & 0xFFFF;
-					const unsigned data = ((src & 0xE000) == 0x8000 || src > 0xFDFF) ? 0xFF : read(src, cycleCounter);
-
-					cycleCounter += 2 << doubleSpeed;
-
-					if (cycleCounter - 3 > lOamDmaUpdate) {
-						oamDmaPos = (oamDmaPos + 1) & 0xFF;
-						lOamDmaUpdate += 4;
-
-						if (oamDmaPos < 0xA0) {
-							if (oamDmaPos == 0)
-								startOamDma(lOamDmaUpdate - 1);
-
-							ioamhram[src & 0xFF] = data;
-						} else if (oamDmaPos == 0xA0) {
-							endOamDma(lOamDmaUpdate - 1);
-							lOamDmaUpdate = DISABLED_TIME;
-						}
-					}
-
-					nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cycleCounter);
-				}
-
-				lastOamDmaUpdate = lOamDmaUpdate;
-			}
-
-			cycleCounter += 4;
-
-			dmaSource = dmaSrc;
-			dmaDestination = dmaDest;
-			ioamhram[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram[0x155] & 0x80);
-
-			if ((ioamhram[0x155] & 0x80) && display.hdmaIsEnabled()) {
-				if (lastOamDmaUpdate != DISABLED_TIME)
-					updateOamDma(cycleCounter);
-				
-				display.disableHdma(cycleCounter);
-			}
-		}
-
-		break;
-	case TIMA:
-		tima.doIrqEvent(TimaInterruptRequester(intreq));
-		break;
-	case VIDEO:
-		display.update(cycleCounter);
-		break;
-	case INTERRUPTS:
-		if (halted()) {
-			if (isCgb())
-				cycleCounter += 4;
-			
-			intreq.unhalt();
-			intreq.setEventTime<UNHALT>(DISABLED_TIME);
-		}
-		
-		if (ime()) {
-			unsigned address;
-			const unsigned pendingIrqs = intreq.pendingIrqs();
-			const unsigned n = pendingIrqs & -pendingIrqs;
-			
-			if (n < 8) {
-				static const unsigned char lut[] = { 0x40, 0x48, 0x48, 0x50 };
-				address = lut[n-1];
-			} else
-				address = 0x50 + n;
-			
-			intreq.ackIrq(n);
-			cycleCounter = interrupter.interrupt(address, cycleCounter, *this);
-		}
-		
-		break;
-	}
-
-	return cycleCounter;
-}
-
-unsigned long Memory::stop(unsigned long cycleCounter) {
-	cycleCounter += 4 << isDoubleSpeed();
-	
-	if (ioamhram[0x14D] & isCgb()) {
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		
-		display.speedChange(cycleCounter);
-		ioamhram[0x14D] ^= 0x81;
-
-		intreq.setEventTime<BLIT>((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : cycleCounter + (70224 << isDoubleSpeed()));
-		
-		if (intreq.eventTime(END) > cycleCounter) {
-			intreq.setEventTime<END>(cycleCounter + (isDoubleSpeed() ?
-					(intreq.eventTime(END) - cycleCounter) << 1 : (intreq.eventTime(END) - cycleCounter) >> 1));
-		}
-		// when switching speed, it seems that the CPU spontaneously restarts soon?
-		// otherwise, the cpu should be allowed to stay halted as long as needed
-		// so only execute this line when switching speed
-		intreq.setEventTime<UNHALT>(cycleCounter + 0x20000 + isDoubleSpeed() * 8);
-	}
-	
-	intreq.halt();
-
-	return cycleCounter;
-}
-
-static void decCycles(unsigned long &counter, const unsigned long dec) {
-	if (counter != DISABLED_TIME)
-		counter -= dec;
-}
-
-void Memory::decEventCycles(const MemEventId eventId, const unsigned long dec) {
-	if (intreq.eventTime(eventId) != DISABLED_TIME)
-		intreq.setEventTime(eventId, intreq.eventTime(eventId) - dec);
-}
-
-unsigned long Memory::resetCounters(unsigned long cycleCounter) {
-	if (lastOamDmaUpdate != DISABLED_TIME)
-		updateOamDma(cycleCounter);
-
-	updateIrqs(cycleCounter);
-
-	const unsigned long oldCC = cycleCounter;
-
-	{
-		const unsigned long divinc = (cycleCounter - divLastUpdate) >> 8;
-		ioamhram[0x104] = (ioamhram[0x104] + divinc) & 0xFF;
-		divLastUpdate += divinc << 8;
-	}
-
-	const unsigned long dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000;
-
-	decCycles(divLastUpdate, dec);
-	decCycles(lastOamDmaUpdate, dec);
-	decEventCycles(SERIAL, dec);
-	decEventCycles(OAM, dec);
-	decEventCycles(BLIT, dec);
-	decEventCycles(END, dec);
-	decEventCycles(UNHALT, dec);
-
-	cycleCounter -= dec;
-	
-	intreq.resetCc(oldCC, cycleCounter);
-	tima.resetCc(oldCC, cycleCounter, TimaInterruptRequester(intreq));
-	display.resetCc(oldCC, cycleCounter);
-	sound.resetCounter(cycleCounter, oldCC, isDoubleSpeed());
-
-	return cycleCounter;
-}
-
-void Memory::updateInput() {
+/***************************************************************************
+ *   Copyright (C) 2007 by Sindre Aamås                                    *
+ *   aamas@stud.ntnu.no                                                    *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License version 2 as     *
+ *   published by the Free Software Foundation.                            *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License version 2 for more details.                *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   version 2 along with this program; if not, write to the               *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "memory.h"
+#include "video.h"
+#include "sound.h"
+#include "savestate.h"
+#include <cstring>
+
+namespace gambatte {
+
+Memory::Memory(const Interrupter &interrupter_in)
+: readCallback(0),
+  writeCallback(0),
+  execCallback(0),
+  cdCallback(0),
+  getInput(0),
+  divLastUpdate(0),
+  lastOamDmaUpdate(DISABLED_TIME),
+  display(ioamhram, 0, VideoInterruptRequester(&intreq)),
+  interrupter(interrupter_in),
+  dmaSource(0),
+  dmaDestination(0),
+  oamDmaPos(0xFE),
+  serialCnt(0),
+  blanklcd(false),
+  LINKCABLE(false),
+  linkClockTrigger(false)
+{
+	intreq.setEventTime<BLIT>(144*456ul);
+	intreq.setEventTime<END>(0);
+}
+
+void Memory::setStatePtrs(SaveState &state) {
+	state.mem.ioamhram.set(ioamhram, sizeof ioamhram);
+
+	cart.setStatePtrs(state);
+	display.setStatePtrs(state);
+	sound.setStatePtrs(state);
+}
+
+
+static inline int serialCntFrom(const unsigned long cyclesUntilDone, const bool cgbFast) {
+	return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9;
+}
+
+void Memory::loadState(const SaveState &state) {
+	sound.loadState(state);
+	display.loadState(state, state.mem.oamDmaPos < 0xA0 ? cart.rdisabledRam() : ioamhram);
+	tima.loadState(state, TimaInterruptRequester(intreq));
+	cart.loadState(state);
+	intreq.loadState(state);
+
+	divLastUpdate = state.mem.divLastUpdate;
+	intreq.setEventTime<SERIAL>(state.mem.nextSerialtime > state.cpu.cycleCounter ? state.mem.nextSerialtime : state.cpu.cycleCounter);
+	intreq.setEventTime<UNHALT>(state.mem.unhaltTime);
+	lastOamDmaUpdate = state.mem.lastOamDmaUpdate;
+	dmaSource = state.mem.dmaSource;
+	dmaDestination = state.mem.dmaDestination;
+	oamDmaPos = state.mem.oamDmaPos;
+	serialCnt = intreq.eventTime(SERIAL) != DISABLED_TIME
+			? serialCntFrom(intreq.eventTime(SERIAL) - state.cpu.cycleCounter, ioamhram[0x102] & isCgb() * 2)
+			: 8;
+
+	cart.setVrambank(ioamhram[0x14F] & isCgb());
+	cart.setOamDmaSrc(OAM_DMA_SRC_OFF);
+	cart.setWrambank(isCgb() && (ioamhram[0x170] & 0x07) ? ioamhram[0x170] & 0x07 : 1);
+
+	if (lastOamDmaUpdate != DISABLED_TIME) {
+		oamDmaInitSetup();
+
+		const unsigned oamEventPos = oamDmaPos < 0xA0 ? 0xA0 : 0x100;
+
+		intreq.setEventTime<OAM>(lastOamDmaUpdate + (oamEventPos - oamDmaPos) * 4);
+	}
+
+	intreq.setEventTime<BLIT>((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : state.cpu.cycleCounter);
+	blanklcd = false;
+	
+	if (!isCgb())
+		std::memset(cart.vramdata() + 0x2000, 0, 0x2000);
+}
+
+void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) {
+	if (intreq.eventTime(BLIT) <= cycleCounter)
+		intreq.setEventTime<BLIT>(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed()));
+	
+	intreq.setEventTime<END>(cycleCounter + (inc << isDoubleSpeed()));
+}
+
+void Memory::updateSerial(const unsigned long cc) {
+	if (!LINKCABLE) {
+		if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
+			if (intreq.eventTime(SERIAL) <= cc) {
+				ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF;
+				ioamhram[0x102] &= 0x7F;
+				intreq.setEventTime<SERIAL>(DISABLED_TIME);
+				intreq.flagIrq(8);
+			} else {
+				const int targetCnt = serialCntFrom(intreq.eventTime(SERIAL) - cc, ioamhram[0x102] & isCgb() * 2);
+				ioamhram[0x101] = (((ioamhram[0x101] + 1) << (serialCnt - targetCnt)) - 1) & 0xFF;
+				serialCnt = targetCnt;
+			}
+		}
+	}
+	else {
+		if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
+			if (intreq.eventTime(SERIAL) <= cc) {
+				linkClockTrigger = true;
+				intreq.setEventTime<SERIAL>(DISABLED_TIME);
+			}
+		}
+	}
+}
+
+void Memory::updateTimaIrq(const unsigned long cc) {
+	while (intreq.eventTime(TIMA) <= cc)
+		tima.doIrqEvent(TimaInterruptRequester(intreq));
+}
+
+void Memory::updateIrqs(const unsigned long cc) {
+	updateSerial(cc);
+	updateTimaIrq(cc);
+	display.update(cc);
+}
+
+unsigned long Memory::event(unsigned long cycleCounter) {
+	if (lastOamDmaUpdate != DISABLED_TIME)
+		updateOamDma(cycleCounter);
+
+	switch (intreq.minEventId()) {
+	case UNHALT:
+		intreq.unhalt();
+		intreq.setEventTime<UNHALT>(DISABLED_TIME);
+		break;
+	case END:
+		intreq.setEventTime<END>(DISABLED_TIME - 1);
+
+		while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(END) != DISABLED_TIME)
+			cycleCounter = event(cycleCounter);
+		
+		intreq.setEventTime<END>(DISABLED_TIME);
+
+		break;
+	case BLIT:
+		{
+			const bool lcden = ioamhram[0x140] >> 7 & 1;
+			unsigned long blitTime = intreq.eventTime(BLIT);
+			
+			if (lcden | blanklcd) {
+				display.updateScreen(blanklcd, cycleCounter);
+				intreq.setEventTime<BLIT>(DISABLED_TIME);
+				intreq.setEventTime<END>(DISABLED_TIME);
+				
+				while (cycleCounter >= intreq.minEventTime())
+					cycleCounter = event(cycleCounter);
+			} else
+				blitTime += 70224 << isDoubleSpeed();
+			
+			blanklcd = lcden ^ 1;
+			intreq.setEventTime<BLIT>(blitTime);
+		}
+		break;
+	case SERIAL:
+		updateSerial(cycleCounter);
+		break;
+	case OAM:
+		intreq.setEventTime<OAM>(lastOamDmaUpdate == DISABLED_TIME ?
+				static_cast<unsigned long>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4);
+		break;
+	case DMA:
+		{
+			const bool doubleSpeed = isDoubleSpeed();
+			unsigned dmaSrc = dmaSource;
+			unsigned dmaDest = dmaDestination;
+			unsigned dmaLength = ((ioamhram[0x155] & 0x7F) + 0x1) * 0x10;
+			unsigned length = hdmaReqFlagged(intreq) ? 0x10 : dmaLength;
+			
+			ackDmaReq(&intreq);
+
+			if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) {
+				length = 0x10000 - dmaDest;
+				ioamhram[0x155] |= 0x80;
+			}
+
+			dmaLength -= length;
+
+			if (!(ioamhram[0x140] & 0x80))
+				dmaLength = 0;
+
+			{
+				unsigned long lOamDmaUpdate = lastOamDmaUpdate;
+				lastOamDmaUpdate = DISABLED_TIME;
+
+				while (length--) {
+					const unsigned src = dmaSrc++ & 0xFFFF;
+					const unsigned data = ((src & 0xE000) == 0x8000 || src > 0xFDFF) ? 0xFF : read(src, cycleCounter);
+
+					cycleCounter += 2 << doubleSpeed;
+
+					if (cycleCounter - 3 > lOamDmaUpdate) {
+						oamDmaPos = (oamDmaPos + 1) & 0xFF;
+						lOamDmaUpdate += 4;
+
+						if (oamDmaPos < 0xA0) {
+							if (oamDmaPos == 0)
+								startOamDma(lOamDmaUpdate - 1);
+
+							ioamhram[src & 0xFF] = data;
+						} else if (oamDmaPos == 0xA0) {
+							endOamDma(lOamDmaUpdate - 1);
+							lOamDmaUpdate = DISABLED_TIME;
+						}
+					}
+
+					nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cycleCounter);
+				}
+
+				lastOamDmaUpdate = lOamDmaUpdate;
+			}
+
+			cycleCounter += 4;
+
+			dmaSource = dmaSrc;
+			dmaDestination = dmaDest;
+			ioamhram[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram[0x155] & 0x80);
+
+			if ((ioamhram[0x155] & 0x80) && display.hdmaIsEnabled()) {
+				if (lastOamDmaUpdate != DISABLED_TIME)
+					updateOamDma(cycleCounter);
+				
+				display.disableHdma(cycleCounter);
+			}
+		}
+
+		break;
+	case TIMA:
+		tima.doIrqEvent(TimaInterruptRequester(intreq));
+		break;
+	case VIDEO:
+		display.update(cycleCounter);
+		break;
+	case INTERRUPTS:
+		if (halted()) {
+			if (isCgb())
+				cycleCounter += 4;
+			
+			intreq.unhalt();
+			intreq.setEventTime<UNHALT>(DISABLED_TIME);
+		}
+		
+		if (ime()) {
+			unsigned address;
+			const unsigned pendingIrqs = intreq.pendingIrqs();
+			const unsigned n = pendingIrqs & -pendingIrqs;
+			
+			if (n < 8) {
+				static const unsigned char lut[] = { 0x40, 0x48, 0x48, 0x50 };
+				address = lut[n-1];
+			} else
+				address = 0x50 + n;
+			
+			intreq.ackIrq(n);
+			cycleCounter = interrupter.interrupt(address, cycleCounter, *this);
+		}
+		
+		break;
+	}
+
+	return cycleCounter;
+}
+
+unsigned long Memory::stop(unsigned long cycleCounter) {
+	cycleCounter += 4 << isDoubleSpeed();
+	
+	if (ioamhram[0x14D] & isCgb()) {
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		
+		display.speedChange(cycleCounter);
+		ioamhram[0x14D] ^= 0x81;
+
+		intreq.setEventTime<BLIT>((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : cycleCounter + (70224 << isDoubleSpeed()));
+		
+		if (intreq.eventTime(END) > cycleCounter) {
+			intreq.setEventTime<END>(cycleCounter + (isDoubleSpeed() ?
+					(intreq.eventTime(END) - cycleCounter) << 1 : (intreq.eventTime(END) - cycleCounter) >> 1));
+		}
+		// when switching speed, it seems that the CPU spontaneously restarts soon?
+		// otherwise, the cpu should be allowed to stay halted as long as needed
+		// so only execute this line when switching speed
+		intreq.setEventTime<UNHALT>(cycleCounter + 0x20000 + isDoubleSpeed() * 8);
+	}
+	
+	intreq.halt();
+
+	return cycleCounter;
+}
+
+static void decCycles(unsigned long &counter, const unsigned long dec) {
+	if (counter != DISABLED_TIME)
+		counter -= dec;
+}
+
+void Memory::decEventCycles(const MemEventId eventId, const unsigned long dec) {
+	if (intreq.eventTime(eventId) != DISABLED_TIME)
+		intreq.setEventTime(eventId, intreq.eventTime(eventId) - dec);
+}
+
+unsigned long Memory::resetCounters(unsigned long cycleCounter) {
+	if (lastOamDmaUpdate != DISABLED_TIME)
+		updateOamDma(cycleCounter);
+
+	updateIrqs(cycleCounter);
+
+	const unsigned long oldCC = cycleCounter;
+
+	{
+		const unsigned long divinc = (cycleCounter - divLastUpdate) >> 8;
+		ioamhram[0x104] = (ioamhram[0x104] + divinc) & 0xFF;
+		divLastUpdate += divinc << 8;
+	}
+
+	const unsigned long dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000;
+
+	decCycles(divLastUpdate, dec);
+	decCycles(lastOamDmaUpdate, dec);
+	decEventCycles(SERIAL, dec);
+	decEventCycles(OAM, dec);
+	decEventCycles(BLIT, dec);
+	decEventCycles(END, dec);
+	decEventCycles(UNHALT, dec);
+
+	cycleCounter -= dec;
+	
+	intreq.resetCc(oldCC, cycleCounter);
+	tima.resetCc(oldCC, cycleCounter, TimaInterruptRequester(intreq));
+	display.resetCc(oldCC, cycleCounter);
+	sound.resetCounter(cycleCounter, oldCC, isDoubleSpeed());
+
+	return cycleCounter;
+}
+
+void Memory::updateInput() {
 	unsigned state = 0xF;
 
 	if ((ioamhram[0x100] & 0x30) != 0x30 && getInput) {
@@ -371,720 +371,725 @@ void Memory::updateInput() {
 	if (state != 0xF && (ioamhram[0x100] & 0xF) == 0xF)
 		intreq.flagIrq(0x10);
 
-	ioamhram[0x100] = (ioamhram[0x100] & -0x10u) | state;
-}
-
-void Memory::updateOamDma(const unsigned long cycleCounter) {
-	const unsigned char *const oamDmaSrc = oamDmaSrcPtr();
-	unsigned cycles = (cycleCounter - lastOamDmaUpdate) >> 2;
-
-	while (cycles--) {
-		oamDmaPos = (oamDmaPos + 1) & 0xFF;
-		lastOamDmaUpdate += 4;
-
-		if (oamDmaPos < 0xA0) {
-			if (oamDmaPos == 0)
-				startOamDma(lastOamDmaUpdate - 1);
-
-			ioamhram[oamDmaPos] = oamDmaSrc ? oamDmaSrc[oamDmaPos] : cart.rtcRead();
-		} else if (oamDmaPos == 0xA0) {
-			endOamDma(lastOamDmaUpdate - 1);
-			lastOamDmaUpdate = DISABLED_TIME;
-			break;
-		}
-	}
-}
-
-void Memory::oamDmaInitSetup() {
-	if (ioamhram[0x146] < 0xA0) {
-		cart.setOamDmaSrc(ioamhram[0x146] < 0x80 ? OAM_DMA_SRC_ROM : OAM_DMA_SRC_VRAM);
-	} else if (ioamhram[0x146] < 0xFE - isCgb() * 0x1E) {
-		cart.setOamDmaSrc(ioamhram[0x146] < 0xC0 ? OAM_DMA_SRC_SRAM : OAM_DMA_SRC_WRAM);
-	} else
-		cart.setOamDmaSrc(OAM_DMA_SRC_INVALID);
-}
-
-static const unsigned char * oamDmaSrcZero() {
-	static unsigned char zeroMem[0xA0];
-	return zeroMem;
-}
-
-const unsigned char * Memory::oamDmaSrcPtr() const {
-	switch (cart.oamDmaSrc()) {
-	case OAM_DMA_SRC_ROM:  return cart.romdata(ioamhram[0x146] >> 6) + (ioamhram[0x146] << 8);
-	case OAM_DMA_SRC_SRAM: return cart.rsrambankptr() ? cart.rsrambankptr() + (ioamhram[0x146] << 8) : 0;
-	case OAM_DMA_SRC_VRAM: return cart.vrambankptr() + (ioamhram[0x146] << 8);
-	case OAM_DMA_SRC_WRAM: return cart.wramdata(ioamhram[0x146] >> 4 & 1) + (ioamhram[0x146] << 8 & 0xFFF);
-	case OAM_DMA_SRC_INVALID:
-	case OAM_DMA_SRC_OFF:  break;
-	}
-	
-	return ioamhram[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart.rdisabledRam();
-}
-
-void Memory::startOamDma(const unsigned long cycleCounter) {
-	display.oamChange(cart.rdisabledRam(), cycleCounter);
-}
-
-void Memory::endOamDma(const unsigned long cycleCounter) {
-	oamDmaPos = 0xFE;
-	cart.setOamDmaSrc(OAM_DMA_SRC_OFF);
-	display.oamChange(ioamhram, cycleCounter);
-}
-
-unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) {
-	if (lastOamDmaUpdate != DISABLED_TIME)
-		updateOamDma(cycleCounter);
-
-	switch (P & 0x7F) {
-	case 0x00:
-		updateInput();
-		break;
-	case 0x01:
-	case 0x02:
-		updateSerial(cycleCounter);
-		break;
-	case 0x04:
-		{
-			const unsigned long divcycles = (cycleCounter - divLastUpdate) >> 8;
-			ioamhram[0x104] = (ioamhram[0x104] + divcycles) & 0xFF;
-			divLastUpdate += divcycles << 8;
-		}
-
-		break;
-	case 0x05:
-		ioamhram[0x105] = tima.tima(cycleCounter);
-		break;
-	case 0x0F:
-		updateIrqs(cycleCounter);
-		ioamhram[0x10F] = intreq.ifreg();
-		break;
-	case 0x26:
-		if (ioamhram[0x126] & 0x80) {
-			sound.generate_samples(cycleCounter, isDoubleSpeed());
-			ioamhram[0x126] = 0xF0 | sound.getStatus();
-		} else
-			ioamhram[0x126] = 0x70;
-
-		break;
-	case 0x30:
-	case 0x31:
-	case 0x32:
-	case 0x33:
-	case 0x34:
-	case 0x35:
-	case 0x36:
-	case 0x37:
-	case 0x38:
-	case 0x39:
-	case 0x3A:
-	case 0x3B:
-	case 0x3C:
-	case 0x3D:
-	case 0x3E:
-	case 0x3F:
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		return sound.waveRamRead(P & 0xF);
-	case 0x41:
-		return ioamhram[0x141] | display.getStat(ioamhram[0x145], cycleCounter);
-	case 0x44:
-		return display.getLyReg(cycleCounter/*+4*/);
-	case 0x69:
-		return display.cgbBgColorRead(ioamhram[0x168] & 0x3F, cycleCounter);
-	case 0x6B:
-		return display.cgbSpColorRead(ioamhram[0x16A] & 0x3F, cycleCounter);
-	default: break;
-	}
-
-	return ioamhram[P - 0xFE00];
-}
-
-static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned addr, const bool cgb) {
-	struct Area { unsigned short areaUpper, exceptAreaLower, exceptAreaWidth, pad; };
-	
-	static const Area cgbAreas[] = {
-		{ 0xC000, 0x8000, 0x2000, 0 },
-		{ 0xC000, 0x8000, 0x2000, 0 },
-		{ 0xA000, 0x0000, 0x8000, 0 },
-		{ 0xFE00, 0x0000, 0xC000, 0 },
-		{ 0xC000, 0x8000, 0x2000, 0 },
-		{ 0x0000, 0x0000, 0x0000, 0 }
-	};
-	
-	static const Area dmgAreas[] = {
-		{ 0xFE00, 0x8000, 0x2000, 0 },
-		{ 0xFE00, 0x8000, 0x2000, 0 },
-		{ 0xA000, 0x0000, 0x8000, 0 },
-		{ 0xFE00, 0x8000, 0x2000, 0 },
-		{ 0xFE00, 0x8000, 0x2000, 0 },
-		{ 0x0000, 0x0000, 0x0000, 0 }
-	};
-	
-	const Area *const a = cgb ? cgbAreas : dmgAreas;
-
-	return addr < a[oamDmaSrc].areaUpper && addr - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth;
-}
-
-unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCounter) {
-	if (P < 0xFF80) {
-		if (lastOamDmaUpdate != DISABLED_TIME) {
-			updateOamDma(cycleCounter);
-			
-			if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0)
-				return ioamhram[oamDmaPos];
-		}
-
-		if (P < 0xC000) {
-			if (P < 0x8000)
-				return cart.romdata(P >> 14)[P];
-
-			if (P < 0xA000) {
-				if (!display.vramAccessible(cycleCounter))
-					return 0xFF;
-
-				return cart.vrambankptr()[P];
-			}
-
-			if (cart.rsrambankptr())
-				return cart.rsrambankptr()[P];
-
-			return cart.rtcRead();
-		}
-
-		if (P < 0xFE00)
-			return cart.wramdata(P >> 12 & 1)[P & 0xFFF];
-
-		if (P >= 0xFF00)
-			return nontrivial_ff_read(P, cycleCounter);
-
-		if (!display.oamReadable(cycleCounter) || oamDmaPos < 0xA0)
-			return 0xFF;
-	}
-
-	return ioamhram[P - 0xFE00];
-}
-
-unsigned Memory::nontrivial_peek(const unsigned P) {
-	if (P < 0xC000) {
-		if (P < 0x8000)
-			return cart.romdata(P >> 14)[P];
-
-		if (P < 0xA000) {
-			return cart.vrambankptr()[P];
-		}
-
-		if (cart.rsrambankptr())
-			return cart.rsrambankptr()[P];
-
-		return cart.rtcRead(); // verified side-effect free
-	}
-	if (P < 0xFE00)
-		return cart.wramdata(P >> 12 & 1)[P & 0xFFF];
-	if (P >= 0xFF00 && P < 0xFF80)
-		return nontrivial_ff_peek(P);
-	return ioamhram[P - 0xFE00];
-}
-
-unsigned Memory::nontrivial_ff_peek(const unsigned P) {
-	// some regs may be somewhat wrong with this
-	return ioamhram[P - 0xFE00];
-}
-
-void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) {
-	if (lastOamDmaUpdate != DISABLED_TIME)
-		updateOamDma(cycleCounter);
-
-	switch (P & 0xFF) {
+	ioamhram[0x100] = (ioamhram[0x100] & -0x10u) | state;
+}
+
+void Memory::updateOamDma(const unsigned long cycleCounter) {
+	const unsigned char *const oamDmaSrc = oamDmaSrcPtr();
+	unsigned cycles = (cycleCounter - lastOamDmaUpdate) >> 2;
+
+	while (cycles--) {
+		oamDmaPos = (oamDmaPos + 1) & 0xFF;
+		lastOamDmaUpdate += 4;
+
+		if (oamDmaPos < 0xA0) {
+			if (oamDmaPos == 0)
+				startOamDma(lastOamDmaUpdate - 1);
+
+			ioamhram[oamDmaPos] = oamDmaSrc ? oamDmaSrc[oamDmaPos] : cart.rtcRead();
+		} else if (oamDmaPos == 0xA0) {
+			endOamDma(lastOamDmaUpdate - 1);
+			lastOamDmaUpdate = DISABLED_TIME;
+			break;
+		}
+	}
+}
+
+void Memory::oamDmaInitSetup() {
+	if (ioamhram[0x146] < 0xA0) {
+		cart.setOamDmaSrc(ioamhram[0x146] < 0x80 ? OAM_DMA_SRC_ROM : OAM_DMA_SRC_VRAM);
+	} else if (ioamhram[0x146] < 0xFE - isCgb() * 0x1E) {
+		cart.setOamDmaSrc(ioamhram[0x146] < 0xC0 ? OAM_DMA_SRC_SRAM : OAM_DMA_SRC_WRAM);
+	} else
+		cart.setOamDmaSrc(OAM_DMA_SRC_INVALID);
+}
+
+static const unsigned char * oamDmaSrcZero() {
+	static unsigned char zeroMem[0xA0];
+	return zeroMem;
+}
+
+const unsigned char * Memory::oamDmaSrcPtr() const {
+	switch (cart.oamDmaSrc()) {
+	case OAM_DMA_SRC_ROM:  return cart.romdata(ioamhram[0x146] >> 6) + (ioamhram[0x146] << 8);
+	case OAM_DMA_SRC_SRAM: return cart.rsrambankptr() ? cart.rsrambankptr() + (ioamhram[0x146] << 8) : 0;
+	case OAM_DMA_SRC_VRAM: return cart.vrambankptr() + (ioamhram[0x146] << 8);
+	case OAM_DMA_SRC_WRAM: return cart.wramdata(ioamhram[0x146] >> 4 & 1) + (ioamhram[0x146] << 8 & 0xFFF);
+	case OAM_DMA_SRC_INVALID:
+	case OAM_DMA_SRC_OFF:  break;
+	}
+	
+	return ioamhram[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart.rdisabledRam();
+}
+
+void Memory::startOamDma(const unsigned long cycleCounter) {
+	display.oamChange(cart.rdisabledRam(), cycleCounter);
+}
+
+void Memory::endOamDma(const unsigned long cycleCounter) {
+	oamDmaPos = 0xFE;
+	cart.setOamDmaSrc(OAM_DMA_SRC_OFF);
+	display.oamChange(ioamhram, cycleCounter);
+}
+
+unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) {
+	if (lastOamDmaUpdate != DISABLED_TIME)
+		updateOamDma(cycleCounter);
+
+	switch (P & 0x7F) {
+	case 0x00:
+		updateInput();
+		break;
+	case 0x01:
+	case 0x02:
+		updateSerial(cycleCounter);
+		break;
+	case 0x04:
+		{
+			const unsigned long divcycles = (cycleCounter - divLastUpdate) >> 8;
+			ioamhram[0x104] = (ioamhram[0x104] + divcycles) & 0xFF;
+			divLastUpdate += divcycles << 8;
+		}
+
+		break;
+	case 0x05:
+		ioamhram[0x105] = tima.tima(cycleCounter);
+		break;
+	case 0x0F:
+		updateIrqs(cycleCounter);
+		ioamhram[0x10F] = intreq.ifreg();
+		break;
+	case 0x26:
+		if (ioamhram[0x126] & 0x80) {
+			sound.generate_samples(cycleCounter, isDoubleSpeed());
+			ioamhram[0x126] = 0xF0 | sound.getStatus();
+		} else
+			ioamhram[0x126] = 0x70;
+
+		break;
+	case 0x30:
+	case 0x31:
+	case 0x32:
+	case 0x33:
+	case 0x34:
+	case 0x35:
+	case 0x36:
+	case 0x37:
+	case 0x38:
+	case 0x39:
+	case 0x3A:
+	case 0x3B:
+	case 0x3C:
+	case 0x3D:
+	case 0x3E:
+	case 0x3F:
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		return sound.waveRamRead(P & 0xF);
+	case 0x41:
+		return ioamhram[0x141] | display.getStat(ioamhram[0x145], cycleCounter);
+	case 0x44:
+		return display.getLyReg(cycleCounter/*+4*/);
+	case 0x69:
+		return display.cgbBgColorRead(ioamhram[0x168] & 0x3F, cycleCounter);
+	case 0x6B:
+		return display.cgbSpColorRead(ioamhram[0x16A] & 0x3F, cycleCounter);
+	default: break;
+	}
+
+	return ioamhram[P - 0xFE00];
+}
+
+static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned addr, const bool cgb) {
+	struct Area { unsigned short areaUpper, exceptAreaLower, exceptAreaWidth, pad; };
+	
+	static const Area cgbAreas[] = {
+		{ 0xC000, 0x8000, 0x2000, 0 },
+		{ 0xC000, 0x8000, 0x2000, 0 },
+		{ 0xA000, 0x0000, 0x8000, 0 },
+		{ 0xFE00, 0x0000, 0xC000, 0 },
+		{ 0xC000, 0x8000, 0x2000, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0 }
+	};
+	
+	static const Area dmgAreas[] = {
+		{ 0xFE00, 0x8000, 0x2000, 0 },
+		{ 0xFE00, 0x8000, 0x2000, 0 },
+		{ 0xA000, 0x0000, 0x8000, 0 },
+		{ 0xFE00, 0x8000, 0x2000, 0 },
+		{ 0xFE00, 0x8000, 0x2000, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0 }
+	};
+	
+	const Area *const a = cgb ? cgbAreas : dmgAreas;
+
+	return addr < a[oamDmaSrc].areaUpper && addr - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth;
+}
+
+unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCounter) {
+	if (P < 0xFF80) {
+		if (lastOamDmaUpdate != DISABLED_TIME) {
+			updateOamDma(cycleCounter);
+			
+			if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0)
+				return ioamhram[oamDmaPos];
+		}
+
+		if (P < 0xC000) {
+			if (P < 0x8000)
+				return cart.romdata(P >> 14)[P];
+
+			if (P < 0xA000) {
+				if (!display.vramAccessible(cycleCounter))
+					return 0xFF;
+
+				return cart.vrambankptr()[P];
+			}
+
+			if (cart.rsrambankptr())
+				return cart.rsrambankptr()[P];
+
+			return cart.rtcRead();
+		}
+
+		if (P < 0xFE00)
+			return cart.wramdata(P >> 12 & 1)[P & 0xFFF];
+
+		if (P >= 0xFF00)
+			return nontrivial_ff_read(P, cycleCounter);
+
+		if (!display.oamReadable(cycleCounter) || oamDmaPos < 0xA0)
+			return 0xFF;
+	}
+
+	return ioamhram[P - 0xFE00];
+}
+
+unsigned Memory::nontrivial_peek(const unsigned P) {
+	if (P < 0xC000) {
+		if (P < 0x8000)
+			return cart.romdata(P >> 14)[P];
+
+		if (P < 0xA000) {
+			return cart.vrambankptr()[P];
+		}
+
+		if (cart.rsrambankptr())
+			return cart.rsrambankptr()[P];
+
+		return cart.rtcRead(); // verified side-effect free
+	}
+	if (P < 0xFE00)
+		return cart.wramdata(P >> 12 & 1)[P & 0xFFF];
+	if (P >= 0xFF00 && P < 0xFF80)
+		return nontrivial_ff_peek(P);
+	return ioamhram[P - 0xFE00];
+}
+
+unsigned Memory::nontrivial_ff_peek(const unsigned P) {
+	// some regs may be somewhat wrong with this
+	return ioamhram[P - 0xFE00];
+}
+
+void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) {
+	if (lastOamDmaUpdate != DISABLED_TIME)
+		updateOamDma(cycleCounter);
+
+	switch (P & 0xFF) {
 	case 0x00:
 		if ((data ^ ioamhram[0x100]) & 0x30) {
 			ioamhram[0x100] = (ioamhram[0x100] & ~0x30u) | (data & 0x30);
 			updateInput();
 		}
-		return;
-	case 0x01:
-		updateSerial(cycleCounter);
-		break;
-	case 0x02:
-		updateSerial(cycleCounter);
-
-		serialCnt = 8;
-		intreq.setEventTime<SERIAL>((data & 0x81) == 0x81
-				? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8)
-				: static_cast<unsigned long>(DISABLED_TIME));
-
-		data |= 0x7E - isCgb() * 2;
-		break;
-	case 0x04:
-		ioamhram[0x104] = 0;
-		divLastUpdate = cycleCounter;
-		return;
-	case 0x05:
-		tima.setTima(data, cycleCounter, TimaInterruptRequester(intreq));
-		break;
-	case 0x06:
-		tima.setTma(data, cycleCounter, TimaInterruptRequester(intreq));
-		break;
-	case 0x07:
-		data |= 0xF8;
-		tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq));
-		break;
-	case 0x0F:
-		updateIrqs(cycleCounter);
-		intreq.setIfreg(0xE0 | data);
-		return;
-	case 0x10:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr10(data);
-		data |= 0x80;
-		break;
-	case 0x11:
-		if (!sound.isEnabled()) {
-			if (isCgb())
-				return;
-
-			data &= 0x3F;
-		}
-
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr11(data);
-		data |= 0x3F;
-		break;
-	case 0x12:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr12(data);
-		break;
-	case 0x13:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr13(data);
-		return;
-	case 0x14:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr14(data);
-		data |= 0xBF;
-		break;
-	case 0x16:
-		if (!sound.isEnabled()) {
-			if (isCgb())
-				return;
-
-			data &= 0x3F;
-		}
-
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr21(data);
-		data |= 0x3F;
-		break;
-	case 0x17:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr22(data);
-		break;
-	case 0x18:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr23(data);
-		return;
-	case 0x19:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr24(data);
-		data |= 0xBF;
-		break;
-	case 0x1A:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr30(data);
-		data |= 0x7F;
-		break;
-	case 0x1B:
-		if (!sound.isEnabled() && isCgb()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr31(data);
-		return;
-	case 0x1C:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr32(data);
-		data |= 0x9F;
-		break;
-	case 0x1D:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr33(data);
-		return;
-	case 0x1E:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr34(data);
-		data |= 0xBF;
-		break;
-	case 0x20:
-		if (!sound.isEnabled() && isCgb()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr41(data);
-		return;
-	case 0x21:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr42(data);
-		break;
-	case 0x22:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr43(data);
-		break;
-	case 0x23:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_nr44(data);
-		data |= 0xBF;
-		break;
-	case 0x24:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.set_so_volume(data);
-		break;
-	case 0x25:
-		if (!sound.isEnabled()) return;
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.map_so(data);
-		break;
-	case 0x26:
-		if ((ioamhram[0x126] ^ data) & 0x80) {
-			sound.generate_samples(cycleCounter, isDoubleSpeed());
-
-			if (!(data & 0x80)) {
-				for (unsigned i = 0xFF10; i < 0xFF26; ++i)
-					ff_write(i, 0, cycleCounter);
-
-				sound.setEnabled(false);
-			} else {
-				sound.reset();
-				sound.setEnabled(true);
-			}
-		}
-
-		data = (data & 0x80) | (ioamhram[0x126] & 0x7F);
-		break;
-	case 0x30:
-	case 0x31:
-	case 0x32:
-	case 0x33:
-	case 0x34:
-	case 0x35:
-	case 0x36:
-	case 0x37:
-	case 0x38:
-	case 0x39:
-	case 0x3A:
-	case 0x3B:
-	case 0x3C:
-	case 0x3D:
-	case 0x3E:
-	case 0x3F:
-		sound.generate_samples(cycleCounter, isDoubleSpeed());
-		sound.waveRamWrite(P & 0xF, data);
-		break;
-	case 0x40:
-		if (ioamhram[0x140] != data) {
-			if ((ioamhram[0x140] ^ data) & 0x80) {
-				const unsigned lyc = display.getStat(ioamhram[0x145], cycleCounter) & 4;
-				const bool hdmaEnabled = display.hdmaIsEnabled();
-				
-				display.lcdcChange(data, cycleCounter);
-				ioamhram[0x144] = 0;
-				ioamhram[0x141] &= 0xF8;
-
-				if (data & 0x80) {
-					intreq.setEventTime<BLIT>(display.nextMode1IrqTime() + (blanklcd ? 0 : 70224 << isDoubleSpeed()));
-				} else {
-					ioamhram[0x141] |= lyc;
-					intreq.setEventTime<BLIT>(cycleCounter + (456 * 4 << isDoubleSpeed()));
-
-					if (hdmaEnabled)
-						flagHdmaReq(&intreq);
-				}
-			} else
-				display.lcdcChange(data, cycleCounter);
-
-			ioamhram[0x140] = data;
-		}
-
-		return;
-	case 0x41:
-		display.lcdstatChange(data, cycleCounter);
-		data = (ioamhram[0x141] & 0x87) | (data & 0x78);
-		break;
-	case 0x42:
-		display.scyChange(data, cycleCounter);
-		break;
-	case 0x43:
-		display.scxChange(data, cycleCounter);
-		break;
-	case 0x45:
-		display.lycRegChange(data, cycleCounter);
-		break;
-	case 0x46:
-		if (lastOamDmaUpdate != DISABLED_TIME)
-			endOamDma(cycleCounter);
-
-		lastOamDmaUpdate = cycleCounter;
-		intreq.setEventTime<OAM>(cycleCounter + 8);
-		ioamhram[0x146] = data;
-		oamDmaInitSetup();
-		return;
-	case 0x47:
-		if (!isCgb())
-			display.dmgBgPaletteChange(data, cycleCounter);
-
-		break;
-	case 0x48:
-		if (!isCgb())
-			display.dmgSpPalette1Change(data, cycleCounter);
-
-		break;
-	case 0x49:
-		if (!isCgb())
-			display.dmgSpPalette2Change(data, cycleCounter);
-
-		break;
-	case 0x4A:
-		display.wyChange(data, cycleCounter);
-		break;
-	case 0x4B:
-		display.wxChange(data, cycleCounter);
-		break;
-
-	case 0x4D:
+		return;
+	case 0x01:
+		updateSerial(cycleCounter);
+		break;
+	case 0x02:
+		updateSerial(cycleCounter);
+
+		serialCnt = 8;
+		intreq.setEventTime<SERIAL>((data & 0x81) == 0x81
+				? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8)
+				: static_cast<unsigned long>(DISABLED_TIME));
+
+		data |= 0x7E - isCgb() * 2;
+		break;
+	case 0x04:
+		ioamhram[0x104] = 0;
+		divLastUpdate = cycleCounter;
+		return;
+	case 0x05:
+		tima.setTima(data, cycleCounter, TimaInterruptRequester(intreq));
+		break;
+	case 0x06:
+		tima.setTma(data, cycleCounter, TimaInterruptRequester(intreq));
+		break;
+	case 0x07:
+		data |= 0xF8;
+		tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq));
+		break;
+	case 0x0F:
+		updateIrqs(cycleCounter);
+		intreq.setIfreg(0xE0 | data);
+		return;
+	case 0x10:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr10(data);
+		data |= 0x80;
+		break;
+	case 0x11:
+		if (!sound.isEnabled()) {
+			if (isCgb())
+				return;
+
+			data &= 0x3F;
+		}
+
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr11(data);
+		data |= 0x3F;
+		break;
+	case 0x12:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr12(data);
+		break;
+	case 0x13:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr13(data);
+		return;
+	case 0x14:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr14(data);
+		data |= 0xBF;
+		break;
+	case 0x16:
+		if (!sound.isEnabled()) {
+			if (isCgb())
+				return;
+
+			data &= 0x3F;
+		}
+
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr21(data);
+		data |= 0x3F;
+		break;
+	case 0x17:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr22(data);
+		break;
+	case 0x18:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr23(data);
+		return;
+	case 0x19:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr24(data);
+		data |= 0xBF;
+		break;
+	case 0x1A:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr30(data);
+		data |= 0x7F;
+		break;
+	case 0x1B:
+		if (!sound.isEnabled() && isCgb()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr31(data);
+		return;
+	case 0x1C:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr32(data);
+		data |= 0x9F;
+		break;
+	case 0x1D:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr33(data);
+		return;
+	case 0x1E:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr34(data);
+		data |= 0xBF;
+		break;
+	case 0x20:
+		if (!sound.isEnabled() && isCgb()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr41(data);
+		return;
+	case 0x21:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr42(data);
+		break;
+	case 0x22:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr43(data);
+		break;
+	case 0x23:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_nr44(data);
+		data |= 0xBF;
+		break;
+	case 0x24:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.set_so_volume(data);
+		break;
+	case 0x25:
+		if (!sound.isEnabled()) return;
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.map_so(data);
+		break;
+	case 0x26:
+		if ((ioamhram[0x126] ^ data) & 0x80) {
+			sound.generate_samples(cycleCounter, isDoubleSpeed());
+
+			if (!(data & 0x80)) {
+				for (unsigned i = 0xFF10; i < 0xFF26; ++i)
+					ff_write(i, 0, cycleCounter);
+
+				sound.setEnabled(false);
+			} else {
+				sound.reset();
+				sound.setEnabled(true);
+			}
+		}
+
+		data = (data & 0x80) | (ioamhram[0x126] & 0x7F);
+		break;
+	case 0x30:
+	case 0x31:
+	case 0x32:
+	case 0x33:
+	case 0x34:
+	case 0x35:
+	case 0x36:
+	case 0x37:
+	case 0x38:
+	case 0x39:
+	case 0x3A:
+	case 0x3B:
+	case 0x3C:
+	case 0x3D:
+	case 0x3E:
+	case 0x3F:
+		sound.generate_samples(cycleCounter, isDoubleSpeed());
+		sound.waveRamWrite(P & 0xF, data);
+		break;
+	case 0x40:
+		if (ioamhram[0x140] != data) {
+			if ((ioamhram[0x140] ^ data) & 0x80) {
+				const unsigned lyc = display.getStat(ioamhram[0x145], cycleCounter) & 4;
+				const bool hdmaEnabled = display.hdmaIsEnabled();
+				
+				display.lcdcChange(data, cycleCounter);
+				ioamhram[0x144] = 0;
+				ioamhram[0x141] &= 0xF8;
+
+				if (data & 0x80) {
+					intreq.setEventTime<BLIT>(display.nextMode1IrqTime() + (blanklcd ? 0 : 70224 << isDoubleSpeed()));
+				} else {
+					ioamhram[0x141] |= lyc;
+					intreq.setEventTime<BLIT>(cycleCounter + (456 * 4 << isDoubleSpeed()));
+
+					if (hdmaEnabled)
+						flagHdmaReq(&intreq);
+				}
+			} else
+				display.lcdcChange(data, cycleCounter);
+
+			ioamhram[0x140] = data;
+		}
+
+		return;
+	case 0x41:
+		display.lcdstatChange(data, cycleCounter);
+		data = (ioamhram[0x141] & 0x87) | (data & 0x78);
+		break;
+	case 0x42:
+		display.scyChange(data, cycleCounter);
+		break;
+	case 0x43:
+		display.scxChange(data, cycleCounter);
+		break;
+	case 0x45:
+		display.lycRegChange(data, cycleCounter);
+		break;
+	case 0x46:
+		if (lastOamDmaUpdate != DISABLED_TIME)
+			endOamDma(cycleCounter);
+
+		lastOamDmaUpdate = cycleCounter;
+		intreq.setEventTime<OAM>(cycleCounter + 8);
+		ioamhram[0x146] = data;
+		oamDmaInitSetup();
+		return;
+	case 0x47:
+		if (!isCgb())
+			display.dmgBgPaletteChange(data, cycleCounter);
+
+		break;
+	case 0x48:
+		if (!isCgb())
+			display.dmgSpPalette1Change(data, cycleCounter);
+
+		break;
+	case 0x49:
+		if (!isCgb())
+			display.dmgSpPalette2Change(data, cycleCounter);
+
+		break;
+	case 0x4A:
+		display.wyChange(data, cycleCounter);
+		break;
+	case 0x4B:
+		display.wxChange(data, cycleCounter);
+		break;
+
+	case 0x4D:
 		if (isCgb())
-			ioamhram[0x14D] = (ioamhram[0x14D] & ~1u) | (data & 1);		return;
-	case 0x4F:
-		if (isCgb()) {
-			cart.setVrambank(data & 1);
-			ioamhram[0x14F] = 0xFE | data;
-		}
-
-		return;
-	case 0x51:
-		dmaSource = data << 8 | (dmaSource & 0xFF);
-		return;
-	case 0x52:
-		dmaSource = (dmaSource & 0xFF00) | (data & 0xF0);
-		return;
-	case 0x53:
-		dmaDestination = data << 8 | (dmaDestination & 0xFF);
-		return;
-	case 0x54:
-		dmaDestination = (dmaDestination & 0xFF00) | (data & 0xF0);
-		return;
-	case 0x55:
-		if (isCgb()) {
-			ioamhram[0x155] = data & 0x7F;
-
-			if (display.hdmaIsEnabled()) {
-				if (!(data & 0x80)) {
-					ioamhram[0x155] |= 0x80;
-					display.disableHdma(cycleCounter);
-				}
-			} else {
-				if (data & 0x80) {
-					if (ioamhram[0x140] & 0x80) {
-						display.enableHdma(cycleCounter);
-					} else
-						flagHdmaReq(&intreq);
-				} else
-					flagGdmaReq(&intreq);
-			}
-		}
-
-		return;
-	case 0x56:
-		if (isCgb())
-			ioamhram[0x156] = data | 0x3E;
-
-		return;
-	case 0x68:
-		if (isCgb())
-			ioamhram[0x168] = data | 0x40;
-
-		return;
-	case 0x69:
-		if (isCgb()) {
-			const unsigned index = ioamhram[0x168] & 0x3F;
-
-			display.cgbBgColorChange(index, data, cycleCounter);
-
-			ioamhram[0x168] = (ioamhram[0x168] & ~0x3F) | ((index + (ioamhram[0x168] >> 7)) & 0x3F);
-		}
-
-		return;
-	case 0x6A:
-		if (isCgb())
-			ioamhram[0x16A] = data | 0x40;
-
-		return;
-	case 0x6B:
-		if (isCgb()) {
-			const unsigned index = ioamhram[0x16A] & 0x3F;
-
-			display.cgbSpColorChange(index, data, cycleCounter);
-
-			ioamhram[0x16A] = (ioamhram[0x16A] & ~0x3F) | ((index + (ioamhram[0x16A] >> 7)) & 0x3F);
-		}
-
-		return;
-	case 0x6C:
-		if (isCgb())
-			ioamhram[0x16C] = data | 0xFE;
-
-		return;
-	case 0x70:
-		if (isCgb()) {
-			cart.setWrambank((data & 0x07) ? (data & 0x07) : 1);
-			ioamhram[0x170] = data | 0xF8;
-		}
-
-		return;
-	case 0x72:
-	case 0x73:
-	case 0x74:
-		if (isCgb())
-			break;
-
-		return;
-	case 0x75:
-		if (isCgb())
-			ioamhram[0x175] = data | 0x8F;
-
-		return;
-	case 0xFF:
-		intreq.setIereg(data);
-		break;
-	default:
-		return;
-	}
-
-	ioamhram[P - 0xFE00] = data;
-}
-
-void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
-	if (lastOamDmaUpdate != DISABLED_TIME) {
-		updateOamDma(cycleCounter);
-		
-		if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0) {
-			ioamhram[oamDmaPos] = data;
-			return;
-		}
-	}
-
-	if (P < 0xFE00) {
-		if (P < 0xA000) {
-			if (P < 0x8000) {
-				cart.mbcWrite(P, data);
-			} else if (display.vramAccessible(cycleCounter)) {
-				display.vramChange(cycleCounter);
-				cart.vrambankptr()[P] = data;
-			}
-		} else if (P < 0xC000) {
-			if (cart.wsrambankptr())
-				cart.wsrambankptr()[P] = data;
-			else
-				cart.rtcWrite(data);
-		} else
-			cart.wramdata(P >> 12 & 1)[P & 0xFFF] = data;
-	} else if (P - 0xFF80u >= 0x7Fu) {
-		if (P < 0xFF00) {
-			if (display.oamWritable(cycleCounter) && oamDmaPos >= 0xA0 && (P < 0xFEA0 || isCgb())) {
-				display.oamChange(cycleCounter);
-				ioamhram[P - 0xFE00] = data;
-			}
-		} else
-			nontrivial_ff_write(P, data, cycleCounter);
-	} else
-		ioamhram[P - 0xFE00] = data;
-}
-
-int Memory::loadROM(const char *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) {
-	if (const int fail = cart.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat))
-		return fail;
-
-	sound.init(cart.isCgb());
-	display.reset(ioamhram, cart.vramdata(), cart.isCgb());
-
-	return 0;
-}
-
-unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) {
-	sound.generate_samples(cycleCounter, isDoubleSpeed());
-	return sound.fillBuffer();
-}
-
-void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32) {
-	display.setDmgPaletteColor(palNum, colorNum, rgb32);
-}
-
-void Memory::setCgbPalette(unsigned *lut) {
-	display.setCgbPalette(lut);
-}
-
-bool Memory::getMemoryArea(int which, unsigned char **data, int *length) {
-	if (!data || !length)
-		return false;
-
-	switch (which)
-	{
-	case 4: // oam
-		*data = &ioamhram[0];
-		*length = 160;
-		return true;
-	case 5: // hram
-		*data = &ioamhram[384];
-		*length = 128;
-		return true;
-	case 6: // bgpal
-		*data = (unsigned char *)display.bgPalette();
-		*length = 32;
-		return true;
-	case 7: // sppal
-		*data = (unsigned char *)display.spPalette();
-		*length = 32;
-		return true;
-	default: // pass to cartridge
-		return cart.getMemoryArea(which, data, length);
-	}
-}
-
-int Memory::LinkStatus(int which)
-{
-	switch (which)
-	{
-	case 256: // ClockSignaled
-		return linkClockTrigger;
-	case 257: // AckClockSignal
-		linkClockTrigger = false;
-		return 0;
-	case 258: // GetOut
-		return ioamhram[0x101] & 0xff;
-	case 259: // connect link cable
-		LINKCABLE = true;
-		return 0;
-	default: // ShiftIn
-		if (ioamhram[0x102] & 0x80) // was enabled
-		{
-			ioamhram[0x101] = which;
-			ioamhram[0x102] &= 0x7F;
-			intreq.flagIrq(8);
-		}
-		return 0;
-	}
-
-	return -1;
-}
-
+			ioamhram[0x14D] = (ioamhram[0x14D] & ~1u) | (data & 1);		return;
+	case 0x4F:
+		if (isCgb()) {
+			cart.setVrambank(data & 1);
+			ioamhram[0x14F] = 0xFE | data;
+		}
+
+		return;
+	case 0x50:
+		// this is the register that turns off the bootrom
+		// it can only ever be written to once (with 1) once boot rom finishes
+		cart.bios_remap(data);
+		return;
+	case 0x51:
+		dmaSource = data << 8 | (dmaSource & 0xFF);
+		return;
+	case 0x52:
+		dmaSource = (dmaSource & 0xFF00) | (data & 0xF0);
+		return;
+	case 0x53:
+		dmaDestination = data << 8 | (dmaDestination & 0xFF);
+		return;
+	case 0x54:
+		dmaDestination = (dmaDestination & 0xFF00) | (data & 0xF0);
+		return;
+	case 0x55:
+		if (isCgb()) {
+			ioamhram[0x155] = data & 0x7F;
+
+			if (display.hdmaIsEnabled()) {
+				if (!(data & 0x80)) {
+					ioamhram[0x155] |= 0x80;
+					display.disableHdma(cycleCounter);
+				}
+			} else {
+				if (data & 0x80) {
+					if (ioamhram[0x140] & 0x80) {
+						display.enableHdma(cycleCounter);
+					} else
+						flagHdmaReq(&intreq);
+				} else
+					flagGdmaReq(&intreq);
+			}
+		}
+
+		return;
+	case 0x56:
+		if (isCgb())
+			ioamhram[0x156] = data | 0x3E;
+
+		return;
+	case 0x68:
+		if (isCgb())
+			ioamhram[0x168] = data | 0x40;
+
+		return;
+	case 0x69:
+		if (isCgb()) {
+			const unsigned index = ioamhram[0x168] & 0x3F;
+
+			display.cgbBgColorChange(index, data, cycleCounter);
+
+			ioamhram[0x168] = (ioamhram[0x168] & ~0x3F) | ((index + (ioamhram[0x168] >> 7)) & 0x3F);
+		}
+
+		return;
+	case 0x6A:
+		if (isCgb())
+			ioamhram[0x16A] = data | 0x40;
+
+		return;
+	case 0x6B:
+		if (isCgb()) {
+			const unsigned index = ioamhram[0x16A] & 0x3F;
+
+			display.cgbSpColorChange(index, data, cycleCounter);
+
+			ioamhram[0x16A] = (ioamhram[0x16A] & ~0x3F) | ((index + (ioamhram[0x16A] >> 7)) & 0x3F);
+		}
+
+		return;
+	case 0x6C:
+		if (isCgb())
+			ioamhram[0x16C] = data | 0xFE;
+
+		return;
+	case 0x70:
+		if (isCgb()) {
+			cart.setWrambank((data & 0x07) ? (data & 0x07) : 1);
+			ioamhram[0x170] = data | 0xF8;
+		}
+
+		return;
+	case 0x72:
+	case 0x73:
+	case 0x74:
+		if (isCgb())
+			break;
+
+		return;
+	case 0x75:
+		if (isCgb())
+			ioamhram[0x175] = data | 0x8F;
+
+		return;
+	case 0xFF:
+		intreq.setIereg(data);
+		break;
+	default:
+		return;
+	}
+
+	ioamhram[P - 0xFE00] = data;
+}
+
+void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
+	if (lastOamDmaUpdate != DISABLED_TIME) {
+		updateOamDma(cycleCounter);
+		
+		if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0) {
+			ioamhram[oamDmaPos] = data;
+			return;
+		}
+	}
+
+	if (P < 0xFE00) {
+		if (P < 0xA000) {
+			if (P < 0x8000) {
+				cart.mbcWrite(P, data);
+			} else if (display.vramAccessible(cycleCounter)) {
+				display.vramChange(cycleCounter);
+				cart.vrambankptr()[P] = data;
+			}
+		} else if (P < 0xC000) {
+			if (cart.wsrambankptr())
+				cart.wsrambankptr()[P] = data;
+			else
+				cart.rtcWrite(data);
+		} else
+			cart.wramdata(P >> 12 & 1)[P & 0xFFF] = data;
+	} else if (P - 0xFF80u >= 0x7Fu) {
+		if (P < 0xFF00) {
+			if (display.oamWritable(cycleCounter) && oamDmaPos >= 0xA0 && (P < 0xFEA0 || isCgb())) {
+				display.oamChange(cycleCounter);
+				ioamhram[P - 0xFE00] = data;
+			}
+		} else
+			nontrivial_ff_write(P, data, cycleCounter);
+	} else
+		ioamhram[P - 0xFE00] = data;
+}
+
+int Memory::loadROM(const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, const bool forceDmg, const bool multicartCompat) {
+	if (const int fail = cart.loadROM(romfiledata, romfilelength, biosfiledata, biosfilelength, forceDmg, multicartCompat))
+		return fail;
+
+	sound.init(cart.isCgb());
+	display.reset(ioamhram, cart.vramdata(), cart.isCgb());
+
+	return 0;
+}
+
+unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) {
+	sound.generate_samples(cycleCounter, isDoubleSpeed());
+	return sound.fillBuffer();
+}
+
+void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32) {
+	display.setDmgPaletteColor(palNum, colorNum, rgb32);
+}
+
+void Memory::setCgbPalette(unsigned *lut) {
+	display.setCgbPalette(lut);
+}
+
+bool Memory::getMemoryArea(int which, unsigned char **data, int *length) {
+	if (!data || !length)
+		return false;
+
+	switch (which)
+	{
+	case 4: // oam
+		*data = &ioamhram[0];
+		*length = 160;
+		return true;
+	case 5: // hram
+		*data = &ioamhram[384];
+		*length = 128;
+		return true;
+	case 6: // bgpal
+		*data = (unsigned char *)display.bgPalette();
+		*length = 32;
+		return true;
+	case 7: // sppal
+		*data = (unsigned char *)display.spPalette();
+		*length = 32;
+		return true;
+	default: // pass to cartridge
+		return cart.getMemoryArea(which, data, length);
+	}
+}
+
+int Memory::LinkStatus(int which)
+{
+	switch (which)
+	{
+	case 256: // ClockSignaled
+		return linkClockTrigger;
+	case 257: // AckClockSignal
+		linkClockTrigger = false;
+		return 0;
+	case 258: // GetOut
+		return ioamhram[0x101] & 0xff;
+	case 259: // connect link cable
+		LINKCABLE = true;
+		return 0;
+	default: // ShiftIn
+		if (ioamhram[0x102] & 0x80) // was enabled
+		{
+			ioamhram[0x101] = which;
+			ioamhram[0x102] &= 0x7F;
+			intreq.flagIrq(8);
+		}
+		return 0;
+	}
+
+	return -1;
+}
+
 SYNCFUNC(Memory)
 {
 	SSS(cart);
@@ -1107,5 +1112,5 @@ SYNCFUNC(Memory)
 	NSS(LINKCABLE);
 	NSS(linkClockTrigger);
 }
-
-}
+
+}
diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h
index bbf9e89c6d..78a89a0ee4 100644
--- a/libgambatte/src/memory.h
+++ b/libgambatte/src/memory.h
@@ -1,293 +1,297 @@
-/***************************************************************************
- *   Copyright (C) 2007 by Sindre Aamås                                    *
- *   aamas@stud.ntnu.no                                                    *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License version 2 as     *
- *   published by the Free Software Foundation.                            *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License version 2 for more details.                *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   version 2 along with this program; if not, write to the               *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-#ifndef MEMORY_H
-#define MEMORY_H
-
-#include "mem/cartridge.h"
-#include "video.h"
-#include "sound.h"
-#include "interrupter.h"
-#include "tima.h"
+/***************************************************************************
+ *   Copyright (C) 2007 by Sindre Aamås                                    *
+ *   aamas@stud.ntnu.no                                                    *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License version 2 as     *
+ *   published by the Free Software Foundation.                            *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License version 2 for more details.                *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   version 2 along with this program; if not, write to the               *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#include "mem/cartridge.h"
+#include "video.h"
+#include "sound.h"
+#include "interrupter.h"
+#include "tima.h"
 #include "newstate.h"
 #include "gambatte.h"
-
-namespace gambatte {
-class InputGetter;
-class FilterInfo;
-
-class Memory {
-	Cartridge cart;
-	unsigned char ioamhram[0x200];
-	
-	void (*readCallback)(unsigned);
-	void (*writeCallback)(unsigned);
-	void (*execCallback)(unsigned);
-	CDCallback cdCallback;
-
-	unsigned (*getInput)();
-	unsigned long divLastUpdate;
-	unsigned long lastOamDmaUpdate;
-	
-	InterruptRequester intreq;
-	Tima tima;
-	LCD display;
-	PSG sound;
-	Interrupter interrupter;
-	
-	unsigned short dmaSource;
-	unsigned short dmaDestination;
-	unsigned char oamDmaPos;
-	unsigned char serialCnt;
-	bool blanklcd;
-
-	bool LINKCABLE;
-	bool linkClockTrigger;
-
-	void decEventCycles(MemEventId eventId, unsigned long dec);
-
-	void oamDmaInitSetup();
-	void updateOamDma(unsigned long cycleCounter);
-	void startOamDma(unsigned long cycleCounter);
-	void endOamDma(unsigned long cycleCounter);
-	const unsigned char * oamDmaSrcPtr() const;
-	
-	unsigned nontrivial_ff_read(unsigned P, unsigned long cycleCounter);
-	unsigned nontrivial_read(unsigned P, unsigned long cycleCounter);
-	void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter);
-	void nontrivial_write(unsigned P, unsigned data, unsigned long cycleCounter);
-	
-	unsigned nontrivial_peek(unsigned P);
-	unsigned nontrivial_ff_peek(unsigned P);
-
-	void updateSerial(unsigned long cc);
-	void updateTimaIrq(unsigned long cc);
-	void updateIrqs(unsigned long cc);
-	
-	bool isDoubleSpeed() const { return display.isDoubleSpeed(); }
-
-public:
-	explicit Memory(const Interrupter &interrupter);
-	
-	bool loaded() const { return cart.loaded(); }
-	const char * romTitle() const { return cart.romTitle(); }
-
-	int debugGetLY() const { return display.debugGetLY(); }
-
-	void setStatePtrs(SaveState &state);
-	void loadState(const SaveState &state/*, unsigned long oldCc*/);
-	void loadSavedata(const char *data) { cart.loadSavedata(data); }
-	int saveSavedataLength() {return cart.saveSavedataLength(); }
-	void saveSavedata(char *dest) { cart.saveSavedata(dest); }
-	void updateInput();
-
-	bool getMemoryArea(int which, unsigned char **data, int *length); // { return cart.getMemoryArea(which, data, length); }
-
-	unsigned long stop(unsigned long cycleCounter);
-	bool isCgb() const { return display.isCgb(); }
-	bool ime() const { return intreq.ime(); }
-	bool halted() const { return intreq.halted(); }
-	unsigned long nextEventTime() const { return intreq.minEventTime(); }
-
-	void setLayers(unsigned mask) { display.setLayers(mask); }
-	
-	bool isActive() const { return intreq.eventTime(END) != DISABLED_TIME; }
-	
-	long cyclesSinceBlit(const unsigned long cc) const {
-		return cc < intreq.eventTime(BLIT) ? -1 : static_cast<long>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed());
-	}
-
-	void halt() { intreq.halt(); }
-	void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } }
-
-	void di() { intreq.di(); }
-
-	unsigned ff_read(const unsigned P, const unsigned long cycleCounter) {
-		return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00];
-	}
-
-	struct CDMapResult
-	{
-		eCDLog_AddrType type;
-		unsigned addr;
-	};
-
-	CDMapResult CDMap(const unsigned P) const
-	{
-		if(P<0x4000)
-		{
-			CDMapResult ret = { eCDLog_AddrType_ROM, P };
-			return ret;
-		}
-		else if(P<0x8000) 
-		{
-			unsigned bank = cart.rmem(P>>12) - cart.rmem(0);
-			unsigned addr = P+bank;
-			CDMapResult ret = { eCDLog_AddrType_ROM, addr };
-			return ret;
-		}
-		else if(P<0xA000) {}
-		else if(P<0xC000)
-		{
-			if(cart.wsrambankptr())
-			{
-				//not bankable. but. we're not sure how much might be here
-				unsigned char *data;
-				int length;
-				bool has = cart.getMemoryArea(3,&data,&length);
-				unsigned addr = P&(length-1);
-				if(has && length!=0)
-				{
-					CDMapResult ret = { eCDLog_AddrType_CartRAM, addr };
-					return ret;
-				}
-			}
-		}
-		else if(P<0xE000)
-		{
-			unsigned bank = cart.wramdata(P >> 12 & 1) - cart.wramdata(0);
-			unsigned addr = (P&0xFFF)+bank;
-			CDMapResult ret = { eCDLog_AddrType_WRAM, addr };
-			return ret;
-		}
-		else if(P<0xFF80) {}
-		else 
-		{
-			////this is just for debugging, really, it's pretty useless
-			//CDMapResult ret = { eCDLog_AddrType_HRAM, (P-0xFF80) };
-			//return ret;
-		}
-
-		CDMapResult ret = { eCDLog_AddrType_None };
-		return ret;
-	}
-
-
-	unsigned read(const unsigned P, const unsigned long cycleCounter) {
-		if (readCallback)
-			readCallback(P);
-		if(cdCallback)
-		{
-			CDMapResult map = CDMap(P);
-			if(map.type != eCDLog_AddrType_None)
-				cdCallback(map.addr,map.type,eCDLog_Flags_Data);
-		}
-		return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter);
-	}
-
-	unsigned read_excb(const unsigned P, const unsigned long cycleCounter, bool first) {
-		if (execCallback)
-			execCallback(P);
-		if(cdCallback)
-		{
-			CDMapResult map = CDMap(P);
-			if(map.type != eCDLog_AddrType_None)
-				cdCallback(map.addr,map.type,first?eCDLog_Flags_ExecFirst : eCDLog_Flags_ExecOperand);
-		}
-		return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter);
-	}
-
-	unsigned peek(const unsigned P) {
-		return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_peek(P);
-	}
-
-	void write_nocb(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
-		if (cart.wmem(P >> 12)) {
-			cart.wmem(P >> 12)[P] = data;
-		} else
-			nontrivial_write(P, data, cycleCounter);
-	}
-	
-	void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
-		if (cart.wmem(P >> 12)) {
-			cart.wmem(P >> 12)[P] = data;
-		} else
-			nontrivial_write(P, data, cycleCounter);
-		if (writeCallback)
-			writeCallback(P);
-		if(cdCallback)
-		{
-			CDMapResult map = CDMap(P);
-			if(map.type != eCDLog_AddrType_None)
-				cdCallback(map.addr,map.type,eCDLog_Flags_Data);
-		}
-	}
-	
-	void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
-		if (P - 0xFF80u < 0x7Fu) {
-			ioamhram[P - 0xFE00] = data;
-		} else
-			nontrivial_ff_write(P, data, cycleCounter);
-		if(cdCallback)
-		{
-			CDMapResult map = CDMap(P);
-			if(map.type != eCDLog_AddrType_None)
-				cdCallback(map.addr,map.type,eCDLog_Flags_Data);
-		}
-	}
-
-	unsigned long event(unsigned long cycleCounter);
-	unsigned long resetCounters(unsigned long cycleCounter);
-
-	int loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat);
-
-	void setInputGetter(unsigned (*getInput)()) {
-		this->getInput = getInput;
-	}
-
-	void setReadCallback(void (*callback)(unsigned)) {
-		this->readCallback = callback;
-	}
-	void setWriteCallback(void (*callback)(unsigned)) {
-		this->writeCallback = callback;
-	}
-	void setExecCallback(void (*callback)(unsigned)) {
-		this->execCallback = callback;
-	}
-	void setCDCallback(CDCallback cdc) {
-		this->cdCallback = cdc;
-	}
-
-	void setScanlineCallback(void (*callback)(), int sl) {
-		display.setScanlineCallback(callback, sl);
-	}
-
-	void setRTCCallback(std::uint32_t (*callback)()) {
-		cart.setRTCCallback(callback);
-	}
-
-	void setEndtime(unsigned long cc, unsigned long inc);
-	
-	void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); }
-	unsigned fillSoundBuffer(unsigned long cc);
-	
-	void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
-		display.setVideoBuffer(videoBuf, pitch);
-	}
-	
-	void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
-	void setCgbPalette(unsigned *lut);
-
-	int LinkStatus(int which);
-
+
+namespace gambatte {
+class InputGetter;
+class FilterInfo;
+
+class Memory {
+	Cartridge cart;
+	unsigned char ioamhram[0x200];
+	
+	void (*readCallback)(unsigned);
+	void (*writeCallback)(unsigned);
+	void (*execCallback)(unsigned);
+	CDCallback cdCallback;
+
+	unsigned (*getInput)();
+	unsigned long divLastUpdate;
+	unsigned long lastOamDmaUpdate;
+	
+	InterruptRequester intreq;
+	Tima tima;
+	LCD display;
+	PSG sound;
+	Interrupter interrupter;
+	
+	unsigned short dmaSource;
+	unsigned short dmaDestination;
+	unsigned char oamDmaPos;
+	unsigned char serialCnt;
+	bool blanklcd;
+
+	bool LINKCABLE;
+	bool linkClockTrigger;
+
+	void decEventCycles(MemEventId eventId, unsigned long dec);
+
+	void oamDmaInitSetup();
+	void updateOamDma(unsigned long cycleCounter);
+	void startOamDma(unsigned long cycleCounter);
+	void endOamDma(unsigned long cycleCounter);
+	const unsigned char * oamDmaSrcPtr() const;
+	
+	unsigned nontrivial_ff_read(unsigned P, unsigned long cycleCounter);
+	unsigned nontrivial_read(unsigned P, unsigned long cycleCounter);
+	void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter);
+	void nontrivial_write(unsigned P, unsigned data, unsigned long cycleCounter);
+	
+	unsigned nontrivial_peek(unsigned P);
+	unsigned nontrivial_ff_peek(unsigned P);
+
+	void updateSerial(unsigned long cc);
+	void updateTimaIrq(unsigned long cc);
+	void updateIrqs(unsigned long cc);
+	
+	bool isDoubleSpeed() const { return display.isDoubleSpeed(); }
+
+public:
+	explicit Memory(const Interrupter &interrupter);
+	
+	bool loaded() const { return cart.loaded(); }
+	const char * romTitle() const { return cart.romTitle(); }
+
+	void bios_reset(int setting) {
+		nontrivial_ff_write(0x50, setting, 0);
+	}
+
+	int debugGetLY() const { return display.debugGetLY(); }
+
+	void setStatePtrs(SaveState &state);
+	void loadState(const SaveState &state/*, unsigned long oldCc*/);
+	void loadSavedata(const char *data) { cart.loadSavedata(data); }
+	int saveSavedataLength() {return cart.saveSavedataLength(); }
+	void saveSavedata(char *dest) { cart.saveSavedata(dest); }
+	void updateInput();
+
+	bool getMemoryArea(int which, unsigned char **data, int *length); // { return cart.getMemoryArea(which, data, length); }
+
+	unsigned long stop(unsigned long cycleCounter);
+	bool isCgb() const { return display.isCgb(); }
+	bool ime() const { return intreq.ime(); }
+	bool halted() const { return intreq.halted(); }
+	unsigned long nextEventTime() const { return intreq.minEventTime(); }
+
+	void setLayers(unsigned mask) { display.setLayers(mask); }
+	
+	bool isActive() const { return intreq.eventTime(END) != DISABLED_TIME; }
+	
+	long cyclesSinceBlit(const unsigned long cc) const {
+		return cc < intreq.eventTime(BLIT) ? -1 : static_cast<long>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed());
+	}
+
+	void halt() { intreq.halt(); }
+	void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } }
+
+	void di() { intreq.di(); }
+
+	unsigned ff_read(const unsigned P, const unsigned long cycleCounter) {
+		return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00];
+	}
+
+	struct CDMapResult
+	{
+		eCDLog_AddrType type;
+		unsigned addr;
+	};
+
+	CDMapResult CDMap(const unsigned P) const
+	{
+		if(P<0x4000)
+		{
+			CDMapResult ret = { eCDLog_AddrType_ROM, P };
+			return ret;
+		}
+		else if(P<0x8000) 
+		{
+			unsigned bank = cart.rmem(P>>12) - cart.rmem(0);
+			unsigned addr = P+bank;
+			CDMapResult ret = { eCDLog_AddrType_ROM, addr };
+			return ret;
+		}
+		else if(P<0xA000) {}
+		else if(P<0xC000)
+		{
+			if(cart.wsrambankptr())
+			{
+				//not bankable. but. we're not sure how much might be here
+				unsigned char *data;
+				int length;
+				bool has = cart.getMemoryArea(3,&data,&length);
+				unsigned addr = P&(length-1);
+				if(has && length!=0)
+				{
+					CDMapResult ret = { eCDLog_AddrType_CartRAM, addr };
+					return ret;
+				}
+			}
+		}
+		else if(P<0xE000)
+		{
+			unsigned bank = cart.wramdata(P >> 12 & 1) - cart.wramdata(0);
+			unsigned addr = (P&0xFFF)+bank;
+			CDMapResult ret = { eCDLog_AddrType_WRAM, addr };
+			return ret;
+		}
+		else if(P<0xFF80) {}
+		else 
+		{
+			////this is just for debugging, really, it's pretty useless
+			//CDMapResult ret = { eCDLog_AddrType_HRAM, (P-0xFF80) };
+			//return ret;
+		}
+
+		CDMapResult ret = { eCDLog_AddrType_None };
+		return ret;
+	}
+
+
+	unsigned read(const unsigned P, const unsigned long cycleCounter) {
+		if (readCallback)
+			readCallback(P);
+		if(cdCallback)
+		{
+			CDMapResult map = CDMap(P);
+			if(map.type != eCDLog_AddrType_None)
+				cdCallback(map.addr,map.type,eCDLog_Flags_Data);
+		}
+		return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter);
+	}
+
+	unsigned read_excb(const unsigned P, const unsigned long cycleCounter, bool first) {
+		if (execCallback)
+			execCallback(P);
+		if(cdCallback)
+		{
+			CDMapResult map = CDMap(P);
+			if(map.type != eCDLog_AddrType_None)
+				cdCallback(map.addr,map.type,first?eCDLog_Flags_ExecFirst : eCDLog_Flags_ExecOperand);
+		}
+		return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter);
+	}
+
+	unsigned peek(const unsigned P) {
+		return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_peek(P);
+	}
+
+	void write_nocb(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
+		if (cart.wmem(P >> 12)) {
+			cart.wmem(P >> 12)[P] = data;
+		} else
+			nontrivial_write(P, data, cycleCounter);
+	}
+	
+	void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
+		if (cart.wmem(P >> 12)) {
+			cart.wmem(P >> 12)[P] = data;
+		} else
+			nontrivial_write(P, data, cycleCounter);
+		if (writeCallback)
+			writeCallback(P);
+		if(cdCallback)
+		{
+			CDMapResult map = CDMap(P);
+			if(map.type != eCDLog_AddrType_None)
+				cdCallback(map.addr,map.type,eCDLog_Flags_Data);
+		}
+	}
+	
+	void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
+		if (P - 0xFF80u < 0x7Fu) {
+			ioamhram[P - 0xFE00] = data;
+		} else
+			nontrivial_ff_write(P, data, cycleCounter);
+		if(cdCallback)
+		{
+			CDMapResult map = CDMap(P);
+			if(map.type != eCDLog_AddrType_None)
+				cdCallback(map.addr,map.type,eCDLog_Flags_Data);
+		}
+	}
+
+	unsigned long event(unsigned long cycleCounter);
+	unsigned long resetCounters(unsigned long cycleCounter);
+
+	int loadROM(const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, bool forceDmg, bool multicartCompat);
+
+	void setInputGetter(unsigned (*getInput)()) {
+		this->getInput = getInput;
+	}
+
+	void setReadCallback(void (*callback)(unsigned)) {
+		this->readCallback = callback;
+	}
+	void setWriteCallback(void (*callback)(unsigned)) {
+		this->writeCallback = callback;
+	}
+	void setExecCallback(void (*callback)(unsigned)) {
+		this->execCallback = callback;
+	}
+	void setCDCallback(CDCallback cdc) {
+		this->cdCallback = cdc;
+	}
+
+	void setScanlineCallback(void (*callback)(), int sl) {
+		display.setScanlineCallback(callback, sl);
+	}
+
+	void setRTCCallback(std::uint32_t (*callback)()) {
+		cart.setRTCCallback(callback);
+	}
+
+	void setEndtime(unsigned long cc, unsigned long inc);
+	
+	void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); }
+	unsigned fillSoundBuffer(unsigned long cc);
+	
+	void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
+		display.setVideoBuffer(videoBuf, pitch);
+	}
+	
+	void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
+	void setCgbPalette(unsigned *lut);
+
+	int LinkStatus(int which);
+
 	template<bool isReader>void SyncState(NewState *ns);
-};
-
-}
-
-#endif
+};
+
+}
+
+#endif
diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h
index 4110161ca0..e0726764aa 100644
--- a/libgambatte/src/savestate.h
+++ b/libgambatte/src/savestate.h
@@ -38,7 +38,7 @@ struct SaveState {
 		void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; }
 		
 		friend class SaverList;
-		friend void setInitState(SaveState &, bool, bool, std::uint32_t);
+		friend void setInitState(SaveState &, bool, bool, std::uint32_t, bool);
 	};
 
 	struct CPU {