From 6725b4545adc3e94e0344e5f904267349ce167ba Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Thu, 28 Nov 2013 22:05:32 +0200
Subject: [PATCH 3/9] Breakpoints & debugging

---
 libgambatte/include/gambatte.h    |  23 +++++++++
 libgambatte/src/cpu.cpp           |   9 ++--
 libgambatte/src/cpu.h             |   7 +++
 libgambatte/src/gambatte.cpp      |  16 ++++++
 libgambatte/src/mem/cartridge.cpp |   6 +++
 libgambatte/src/mem/cartridge.h   |   1 +
 libgambatte/src/memory.cpp        | 100 ++++++++++++++++++++++++++++++--------
 libgambatte/src/memory.h          |  93 ++++++++++++++++++++++++++++++++---
 8 files changed, 225 insertions(+), 30 deletions(-)

diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h
index 5094906..ea2558c 100644
--- a/libgambatte/include/gambatte.h
+++ b/libgambatte/include/gambatte.h
@@ -18,6 +18,7 @@
  ***************************************************************************/
 #ifndef GAMBATTE_H
 #define GAMBATTE_H
+#define GAMBATTE_SUPPORTS_ADV_DEBUG
 
 #include "gbint.h"
 #include "inputgetter.h"
@@ -25,6 +26,8 @@
 #include <cstddef>
 #include <string>
 #include <vector>
+#include <functional>
+#include <map>
 
 //
 // Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
@@ -34,6 +37,23 @@ namespace gambatte {
 
 enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 };
 
+struct debugbuffer
+{
+	//1 => Read, 2 => Write, 4 => Execute, 8 => Cheat
+	uint8_t* wram;		//32kB, id1.
+	uint8_t* ioamhram;	//512 bytes, id2.
+	uint8_t* cart;		//As needed, id3.
+	uint8_t* sram;		//As needed, id4.
+	uint8_t* bus;		//64kB, id0
+	std::map<unsigned, uint8_t> wramcheat;
+	std::map<unsigned, uint8_t> sramcheat;
+	std::map<unsigned, uint8_t> cartcheat;
+	std::function<void(unsigned, unsigned, uint8_t, bool)> read;
+	std::function<void(unsigned, unsigned, uint8_t)> write;
+	std::function<void(uint16_t)> trace;
+	bool trace_cpu;
+};
+
 class GB {
 public:
 	GB();
@@ -259,6 +279,9 @@ public:
 	};
 	uint32_t get_cpureg(enum cpu_register reg);
 	void set_cpureg(enum cpu_register reg, uint32_t val);
+	void set_debug_buffer(debugbuffer& dbgbuf);
+	uint8_t bus_read(unsigned addr);
+	void bus_write(unsigned addr, uint8_t val);
 private:
 	void preload_common();
 	void postload_common(const unsigned flags);
diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp
index 06400c5..40a81e6 100644
--- a/libgambatte/src/cpu.cpp
+++ b/libgambatte/src/cpu.cpp
@@ -154,8 +154,8 @@ void CPU::loadOrSave(loadsave& state)
 #define de() ( d << 8 | e )
 #define hl() ( h << 8 | l )
 
-#define READ(dest, addr) do { (dest) = mem_.read(addr, cycleCounter); cycleCounter += 4; } while (0)
-#define PC_READ(dest) do { (dest) = mem_.read(pc, cycleCounter); pc = (pc + 1) & 0xFFFF; cycleCounter += 4; } while (0)
+#define READ(dest, addr) do { (dest) = mem_.read(addr, cycleCounter, false); cycleCounter += 4; } while (0)
+#define PC_READ(dest) do { (dest) = mem_.read(pc, cycleCounter, true); pc = (pc + 1) & 0xFFFF; cycleCounter += 4; } while (0)
 #define FF_READ(dest, addr) do { (dest) = mem_.ff_read(addr, cycleCounter); cycleCounter += 4; } while (0)
 
 #define WRITE(addr, data) do { mem_.write(addr, data, cycleCounter); cycleCounter += 4; } while (0)
@@ -525,7 +525,8 @@ void CPU::process(unsigned const cycles) {
 			}
 		} else while (cycleCounter < mem_.nextEventTime()) {
 			unsigned char opcode;
-
+			if(__builtin_expect(mem_.get_debug()->trace_cpu, 0))
+				mem_.get_debug()->trace(pc);
 			PC_READ(opcode);
 
 			if (skip_) {
@@ -923,7 +924,7 @@ void CPU::process(unsigned const cycles) {
 			case 0x3A:
 				{
 					unsigned addr = hl();
-					a = mem_.read(addr, cycleCounter);
+					a = mem_.read(addr, cycleCounter, false);
 					cycleCounter += 4;
 
 					addr = (addr - 1) & 0xFFFF;
diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h
index 224ba0b..e7e46ff 100644
--- a/libgambatte/src/cpu.h
+++ b/libgambatte/src/cpu.h
@@ -51,6 +51,11 @@ public:
 		mem_.setSaveDir(sdir);
 	}
 
+	void set_debug_buffer(debugbuffer& dbgbuf)
+	{
+		mem_.set_debug_buffer(dbgbuf);
+	}
+
 	std::string const saveBasePath() const {
 		return mem_.saveBasePath();
 	}
@@ -87,6 +92,8 @@ public:
 	std::pair<unsigned char*, size_t> getSaveRam() { return mem_.getSaveRam(); }
 	std::pair<unsigned char*, size_t> getIoRam() { return mem_.getIoRam(); }
 	std::pair<unsigned char*, size_t> getVideoRam() { return mem_.getVideoRam(); };
+        uint8_t bus_read(unsigned addr) { return mem_.read(addr, cycleCounter_, false); }
+        void bus_write(unsigned addr, uint8_t val) { mem_.write(addr, val, cycleCounter_); }
 
 	unsigned cycleCounter_;
 	unsigned short pc_;
diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp
index a61e177..47f894e 100644
--- a/libgambatte/src/gambatte.cpp
+++ b/libgambatte/src/gambatte.cpp
@@ -346,4 +346,20 @@ void GB::set_cpureg(enum cpu_register _reg, uint32_t val)
 		default:		break;
 	}
 }
+
+void GB::set_debug_buffer(debugbuffer& dbgbuf)
+{
+	p_->cpu.set_debug_buffer(dbgbuf);
+}
+
+uint8_t GB::bus_read(unsigned addr)
+{
+	return p_->cpu.bus_read(addr);
+}
+
+void GB::bus_write(unsigned addr, uint8_t val)
+{
+	p_->cpu.bus_write(addr, val);
+}
+
 }
diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp
index d593dc5..1775139 100644
--- a/libgambatte/src/mem/cartridge.cpp
+++ b/libgambatte/src/mem/cartridge.cpp
@@ -858,6 +858,12 @@ std::pair<unsigned char*, size_t> Cartridge::getWorkRam() {
 	return std::make_pair(memptrs_.wramdata(0), worksize);
 }
 
+std::pair<unsigned char*, size_t> Cartridge::getCartRom()
+{
+	size_t worksize = memptrs_.romdataend() - memptrs_.romdata(0);
+	return std::make_pair(memptrs_.romdata(0), worksize);
+}
+
 Cartridge::Cartridge(time_t (**_getCurrentTime)())
 	: rtc_(_getCurrentTime) {
 	memoryCartridge = true;
diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h
index dd342b6..3a0466c 100644
--- a/libgambatte/src/mem/cartridge.h
+++ b/libgambatte/src/mem/cartridge.h
@@ -81,6 +81,7 @@ public:
 	void loadOrSave(loadsave& state);
 	void setRtcBase(time_t time) { rtc_.setBaseTime(time); }
 	time_t getRtcBase() { return rtc_.getBaseTime(); }
+	std::pair<unsigned char*, size_t> getCartRom();
 	std::pair<unsigned char*, size_t> getWorkRam();
 	std::pair<unsigned char*, size_t> getSaveRam();
 	std::pair<unsigned char*, size_t> getVideoRam();
diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp
index cc76f96..c42af88 100644
--- a/libgambatte/src/memory.cpp
+++ b/libgambatte/src/memory.cpp
@@ -234,7 +234,7 @@ unsigned Memory::event(unsigned cc) {
 					unsigned const src = dmaSrc++ & 0xFFFF;
 					unsigned const data = (src & 0xE000) == 0x8000 || src > 0xFDFF
 					                    ? 0xFF
-					                    : read(src, cc);
+					                    : read(src, cc, false);
 
 					cc += 2 << doubleSpeed;
 
@@ -550,18 +550,31 @@ static bool isInOamDmaConflictArea(OamDmaSrc const oamDmaSrc, unsigned const p,
 	    && p - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth;
 }
 
-unsigned Memory::nontrivial_read(unsigned const p, unsigned const cc) {
+unsigned Memory::nontrivial_read(unsigned const p, unsigned const cc, bool exec) {
 	if (p < 0xFF80) {
 		if (lastOamDmaUpdate_ != disabled_time) {
 			updateOamDma(cc);
 
-			if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0)
+			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 < 0x8000) {
+				const unsigned char* aaddr = cart_.romdata(p >> 14) + p;
+				auto a = cart_.getCartRom();
+				if(aaddr >= a.first && aaddr < a.first + a.second)
+					if(__builtin_expect(dbg->cart[aaddr - a.first] & (exec ? 0x4C : 0x19), 0)) {
+						if(dbg->cart[aaddr - a.first] & (exec ? 0x44 : 0x11))
+							dbg->read(3, aaddr - a.first, *aaddr, exec);
+						if(dbg->cart[aaddr - a.first] & 8) {
+							auto itr = dbg->cartcheat.find(aaddr - a.first);
+							if(itr != dbg->cartcheat.end()) return itr->second;
+						}
+					}
+				return *aaddr;
+			}
 
 			if (p < 0xA000) {
 				if (!lcd_.vramAccessible(cc))
@@ -570,23 +583,52 @@ unsigned Memory::nontrivial_read(unsigned const p, unsigned const cc) {
 				return cart_.vrambankptr()[p];
 			}
 
-			if (cart_.rsrambankptr())
-				return cart_.rsrambankptr()[p];
+			if (cart_.rsrambankptr()) {
+				const unsigned char* aaddr = cart_.rsrambankptr() + p;
+				auto a = cart_.getSaveRam();
+				if(aaddr >= a.first && aaddr < a.first + a.second)
+					if(__builtin_expect(dbg->sram[aaddr - a.first] & (exec ? 0x4C : 0x19), 0)) {
+						if(dbg->sram[aaddr - a.first] & (exec ? 0x44 : 0x11))
+							dbg->read(4, aaddr - a.first, *aaddr, exec);
+						if(dbg->sram[aaddr - a.first] & 8) {
+							auto itr = dbg->sramcheat.find(aaddr - a.first);
+							if(itr != dbg->sramcheat.end()) return itr->second;
+						}
+					}
+				return *aaddr;
+			}
 
 			return cart_.rtcRead();
 		}
 
-		if (p < 0xFE00)
-			return cart_.wramdata(p >> 12 & 1)[p & 0xFFF];
+		if (p < 0xFE00) {
+			unsigned char* aaddr = cart_.wramdata(p >> 12 & 1) + (p & 0xFFF);
+			auto a = cart_.getWorkRam();
+			if(aaddr >= a.first && aaddr < a.first + a.second)
+				if(__builtin_expect(dbg->wram[aaddr - a.first] & (exec ? 0x4C : 0x19), 0)) {
+					if(dbg->wram[aaddr - a.first] & (exec ? 0x44 : 0x11))
+						dbg->read(1, aaddr - a.first, *aaddr, exec);
+					if(dbg->wram[aaddr - a.first] & 8) {
+						auto itr = dbg->wramcheat.find(aaddr - a.first);
+						if(itr != dbg->wramcheat.end()) return itr->second;
+					}
+				}
+			return *aaddr;
+		}
 
 		long const ffp = long(p) - 0xFF00;
-		if (ffp >= 0)
-			return nontrivial_ff_read(ffp, cc);
+		if (ffp >= 0) {
+			uint8_t v = nontrivial_ff_read(ffp, cc);
+			if(__builtin_expect(dbg->ioamhram[ffp + 0x100] & (exec ? 0x44 : 0x11), 0))
+				dbg->read(2, ffp + 0x100, v, exec);
+			return v;
+		}
 
 		if (!lcd_.oamReadable(cc) || oamDmaPos_ < 0xA0)
 			return 0xFF;
 	}
-
+	if(__builtin_expect(dbg->ioamhram[p - 0xFE00] & (exec ? 0x44 : 0x11), 0))
+		dbg->read(2, p - 0xFE00, ioamhram_[p - 0xFE00], exec);
 	return ioamhram_[p - 0xFE00];
 }
 
@@ -1023,18 +1065,32 @@ void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned co
 	if (p < 0xFE00) {
 		if (p < 0xA000) {
 			if (p < 0x8000) {
+				//Being a write on MBC, this is not ROM write.
 				cart_.mbcWrite(p, data);
 			} else if (lcd_.vramAccessible(cc)) {
 				lcd_.vramChange(cc);
 				cart_.vrambankptr()[p] = data;
 			}
 		} else if (p < 0xC000) {
-			if (cart_.wsrambankptr())
-				cart_.wsrambankptr()[p] = data;
-			else
+			if (cart_.wsrambankptr()) {
+				unsigned char* aaddr = cart_.wsrambankptr() + p;
+				auto a = cart_.getSaveRam();
+				if(aaddr >= a.first && aaddr < a.first + a.second)
+					if(__builtin_expect(dbg->sram[aaddr - a.first] & 0x22, 0))
+						dbg->write(4, aaddr - a.first, data);
+				*aaddr = data;
+			} else {
+				//Being I/O write, this is not write on SRAM.
 				cart_.rtcWrite(data);
-		} else
-			cart_.wramdata(p >> 12 & 1)[p & 0xFFF] = data;
+			}
+		} else {
+			unsigned char* aaddr = cart_.wramdata(p >> 12 & 1) + (p & 0xFFF);
+			auto a = cart_.getWorkRam();
+			if(aaddr >= a.first && aaddr < a.first + a.second)
+				if(__builtin_expect(dbg->wram[aaddr - a.first] & 0x22, 0))
+					dbg->write(1, aaddr - a.first, data);
+			*aaddr = data;
+		}
 	} else if (p - 0xFF80u >= 0x7Fu) {
 		long const ffp = long(p) - 0xFF00;
 		if (ffp < 0) {
@@ -1042,10 +1098,16 @@ void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned co
 				lcd_.oamChange(cc);
 				ioamhram_[p - 0xFE00] = data;
 			}
-		} else
+		} else {
+			if(__builtin_expect(dbg->ioamhram[ffp + 0x100] & 0x22, 0))
+				dbg->write(2, ffp + 0x100, data);
 			nontrivial_ff_write(ffp, data, cc);
-	} else
+		}
+	} else {
+		if(__builtin_expect(dbg->ioamhram[p - 0xFE00] & 0x22, 0))
+			dbg->write(2, p - 0xFE00, data);
 		ioamhram_[p - 0xFE00] = data;
+	}
 }
 
 void Memory::postLoadRom()
diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h
index a531930..4a252b7 100644
--- a/libgambatte/src/memory.h
+++ b/libgambatte/src/memory.h
@@ -23,6 +23,7 @@
 // Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
 //	- Make it rerecording-friendly.
 
+#include "gambatte.h"
 #include "mem/cartridge.h"
 #include "interrupter.h"
 #include "pakinfo.h"
@@ -71,22 +72,99 @@ public:
 	void ei(unsigned cycleCounter) { if (!ime()) { intreq_.ei(cycleCounter); } }
 	void di() { intreq_.di(); }
 
+	void set_debug_buffer(debugbuffer& dbgbuf)
+	{
+		dbg = &dbgbuf;
+	}
+
 	unsigned ff_read(unsigned p, unsigned cc) {
-		return p < 0x80 ? nontrivial_ff_read(p, cc) : ioamhram_[p + 0x100];
+		uint8_t v = p < 0x80 ? nontrivial_ff_read(p, cc) : ioamhram_[p + 0x100];
+		if(__builtin_expect(dbg->ioamhram[0x100 + p] & 1, 0))
+			dbg->read(2, 0x100 + p, v, false);
+		if(__builtin_expect(dbg->bus[0xFF00 + p] & 1, 0))
+			dbg->read(0, 0xFF00 + p, v, false);
+		return v;
+	}
+
+	inline uint8_t do_read_trap(const uint8_t* addr, std::pair<unsigned char*, size_t> area, unsigned clazz,
+		const uint8_t* dbgflags, std::map<unsigned, uint8_t>& cheats, uint8_t v, uint8_t mask, bool exec)
+	{
+		if(addr >= area.first && addr < area.first + area.second) {
+			if(__builtin_expect(dbgflags[addr - area.first] & mask, 0)) {
+				if(dbgflags[addr - area.first] & (mask & 77))
+					dbg->read(clazz, addr - area.first, v, exec);
+				if(__builtin_expect(dbgflags[addr - area.first] & 8, 0)) {
+					auto itr = cheats.find(addr - area.first);
+					if(itr != cheats.end()) v = itr->second;
+				}
+			}
+		}
+		return v;
 	}
 
-	unsigned read(unsigned p, unsigned cc) {
-		return cart_.rmem(p >> 12) ? cart_.rmem(p >> 12)[p] : nontrivial_read(p, cc);
+	inline void do_write_trap(const uint8_t* addr, std::pair<unsigned char*, size_t> area, unsigned clazz,
+		const uint8_t* dbgflags, uint8_t v)
+	{
+		if(addr >= area.first && addr < area.first + area.second)
+			if(__builtin_expect(dbgflags[addr - area.first] & 0x22, 0))
+				dbg->write(clazz, addr - area.first, v);
+	}
+
+	unsigned read(unsigned p, unsigned cc, bool exec) {
+		uint8_t mask = exec ? 0x4C : 0x19;
+		const unsigned char* memblock = cart_.rmem(p >> 12);
+		uint8_t v = memblock ? memblock[p] : nontrivial_read(p, cc, exec);
+		uint8_t v2 = v;
+		if(memblock) {
+			if(p >= 0xFE00) { //IOAMHRAM.
+				if(__builtin_expect(dbg->ioamhram[p - 0xFE00] & mask, 0))
+					dbg->read(2, 0x100 + p, v, false);
+			} else {
+				const uint8_t* addr = memblock + p;
+				static void* targets[8] = {&&cart, &&cart, &&cart, &&cart, &&out, &&sram, &&wram,
+					&&wram};
+				goto *targets[p >> 13];
+wram:
+				v2 = do_read_trap(addr, cart_.getWorkRam(), 1, dbg->wram, dbg->wramcheat, v, mask, exec);
+				goto out;
+sram:
+				v2 = do_read_trap(addr, cart_.getSaveRam(), 4, dbg->sram, dbg->sramcheat, v, mask, exec);
+				goto out;
+cart:
+				v2 = do_read_trap(addr, cart_.getCartRom(), 3, dbg->cart, dbg->cartcheat, v, mask, exec);
+				goto out;
+			}
+out:			;
+		}
+		if(__builtin_expect(dbg->bus[p] & mask, 0))
+			dbg->read(0, p, v, false);
+		return v2;
 	}
 
 	void write(unsigned p, unsigned data, unsigned cc) {
-		if (cart_.wmem(p >> 12)) {
-			cart_.wmem(p >> 12)[p] = data;
+		if(__builtin_expect(dbg->bus[0xFF00 + p] & 0x22, 0))
+			dbg->write(0, p, data);
+		unsigned char* memblock = cart_.wmem(p >> 12);
+		if(memblock) {
+			if(p >= 0xFE00)	//IOAMHRAM.
+				if(__builtin_expect(dbg->ioamhram[p - 0xFE00] & 2, 0))
+					dbg->write(2, 0x100 + p, data);
+			uint8_t* addr = memblock + p;
+			do_write_trap(addr, cart_.getWorkRam(), 1, dbg->wram, data);
+			do_write_trap(addr, cart_.getSaveRam(), 4, dbg->sram, data);
+			do_write_trap(addr, cart_.getCartRom(), 3, dbg->cart, data);
+		}
+		if (memblock) {
+			memblock[p] = data;
 		} else
 			nontrivial_write(p, data, cc);
 	}
 
 	void ff_write(unsigned p, unsigned data, unsigned cc) {
+		if(__builtin_expect(dbg->ioamhram[0x100 + p] & 2, 0))
+			dbg->write(2, 0x100 + p, data);
+		if(__builtin_expect(dbg->bus[0xFF00 + p] & 2, 0))
+			dbg->write(0, 0xFF00 + p, data);
 		if (p - 0x80u < 0x7Fu) {
 			ioamhram_[p + 0x100] = data;
 		} else
@@ -119,8 +197,9 @@ public:
 	void setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32);
 	void setGameGenie(std::string const &codes) { cart_.setGameGenie(codes); }
 	void setGameShark(std::string const &codes) { interrupter_.setGameShark(codes); }
-
+	debugbuffer* get_debug() { return dbg; }
 private:
+	debugbuffer* dbg;
 	Cartridge cart_;
 	unsigned char ioamhram_[0x200];
 	InputGetter *getInput_;
@@ -147,7 +226,7 @@ private:
 	void endOamDma(unsigned cycleCounter);
 	unsigned char const * oamDmaSrcPtr() const;
 	unsigned nontrivial_ff_read(unsigned p, unsigned cycleCounter);
-	unsigned nontrivial_read(unsigned p, unsigned cycleCounter);
+	unsigned nontrivial_read(unsigned p, unsigned cycleCounter, bool exec);
 	void nontrivial_ff_write(unsigned p, unsigned data, unsigned cycleCounter);
 	void nontrivial_write(unsigned p, unsigned data, unsigned cycleCounter);
 	void updateSerial(unsigned cc);
-- 
2.1.3

