diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs index cfa980db49..b9eefba865 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -334,38 +334,46 @@ namespace BizHawk.Emulation.Consoles.GB IntPtr data = IntPtr.Zero; int length = 0; - int i = (int)which; - if (!LibGambatte.gambatte_getmemoryarea(GambatteState, which, ref data, ref length)) throw new Exception("gambatte_getmemoryarea() failed!"); // if length == 0, it's an empty block; (usually rambank on some carts); that's ok + // TODO: when length == 0, should we simply not add the memory domain at all? if (data == IntPtr.Zero && length > 0) throw new Exception("bad return from gambatte_getmemoryarea()"); - - MemoryRefreshers[i] = new MemoryRefresher(data, length); + var refresher = new MemoryRefresher(data, length); - MemoryDomains[i] = new MemoryDomain(which.ToString(), length, Endian.Little, MemoryRefreshers[i].Peek, MemoryRefreshers[i].Poke); + MemoryRefreshers.Add(refresher); + + MemoryDomains.Add(new MemoryDomain(which.ToString(), length, Endian.Little, refresher.Peek, refresher.Poke)); } void InitMemoryDomains() { - MemoryDomains = new MemoryDomain[6]; - MemoryRefreshers = new MemoryRefresher[6]; + MemoryDomains = new List(); + MemoryRefreshers = new List(); - CreateMemoryDomain(LibGambatte.MemoryAreas.cartram); + CreateMemoryDomain(LibGambatte.MemoryAreas.wram); CreateMemoryDomain(LibGambatte.MemoryAreas.rom); CreateMemoryDomain(LibGambatte.MemoryAreas.vram); - CreateMemoryDomain(LibGambatte.MemoryAreas.wram); + CreateMemoryDomain(LibGambatte.MemoryAreas.cartram); CreateMemoryDomain(LibGambatte.MemoryAreas.oam); CreateMemoryDomain(LibGambatte.MemoryAreas.hram); - // fixme: other code brokenly assumes that MainMemory is MemoryDomains[0] - // (here, we'd want it to be MemoryDomains[2]) - var tmp = MemoryDomains[2]; - MemoryDomains[2] = MemoryDomains[0]; - MemoryDomains[0] = tmp; + // also add a special memory domain for the system bus, where calls get sent directly to the core each time + + MemoryDomains.Add(new MemoryDomain("sysbus", 65536, Endian.Little, + delegate(int addr) + { + return LibGambatte.gambatte_cpuread(GambatteState, (ushort)addr); + }, + delegate(int addr, byte val) + { + LibGambatte.gambatte_cpuwrite(GambatteState, (ushort)addr, val); + })); + + // this is the wram area and matches the bizhawk convention for what MainMemory means MainMemory = MemoryDomains[0]; } @@ -373,7 +381,7 @@ namespace BizHawk.Emulation.Consoles.GB public MemoryDomain MainMemory { get; private set; } - MemoryRefresher[] MemoryRefreshers; + List MemoryRefreshers; #endregion diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs index c2044e935d..4cade884cd 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs @@ -283,6 +283,7 @@ namespace BizHawk.Emulation.Consoles.GB /// /// get pointer to internal memory areas, for debugging purposes + /// so long as you don't write to it, you should be completely sync-safe /// /// opaque state pointer /// which memory area to access @@ -291,5 +292,25 @@ namespace BizHawk.Emulation.Consoles.GB /// success [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] public static extern bool gambatte_getmemoryarea(IntPtr core, MemoryAreas which, ref IntPtr data, ref int length); + + /// + /// read a single byte from the cpu bus. this includes all ram, rom, mmio, etc, as it is visible to the cpu (including mappers). + /// while there is no cycle cost to these reads, there may be other side effects! use at your own risk. + /// + /// opaque state pointer + /// system bus address + /// byte read + [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern byte gambatte_cpuread(IntPtr core, ushort addr); + + /// + /// write a single byte to the cpu bus. while there is no cycle cost to these writes, there can be quite a few side effects. + /// use at your own risk. + /// + /// opaque state pointe + /// system bus address + /// byte to write + [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void gambatte_cpuwrite(IntPtr core, ushort addr, byte val); } } diff --git a/BizHawk.MultiClient/output/libgambatte.dll b/BizHawk.MultiClient/output/libgambatte.dll index d604749bd2..259a8937a1 100644 Binary files a/BizHawk.MultiClient/output/libgambatte.dll and b/BizHawk.MultiClient/output/libgambatte.dll differ diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 12807e8ee6..caa0feb84d 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -147,6 +147,10 @@ public: * @param codes Game Shark codes in format 01HHHHHH;01HHHHHH;... where H is [0-9]|[A-F] */ void setGameShark(const std::string &codes); + + unsigned char ExternalRead(unsigned short addr); + void ExternalWrite(unsigned short addr, unsigned char val); + private: struct Priv; diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index c106715861..5c91912291 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -192,3 +192,15 @@ __declspec(dllexport) int gambatte_getmemoryarea(void *core, int which, unsigned GB *g = (GB *) core; return g->getMemoryArea(which, data, length); } + +__declspec(dllexport) unsigned char gambatte_cpuread(void *core, unsigned short addr) +{ + GB *g = (GB *) core; + return g->ExternalRead(addr); +} + +__declspec(dllexport) void gambatte_cpuwrite(void *core, unsigned short addr, unsigned char val) +{ + GB *g = (GB *) core; + g->ExternalWrite(addr, val); +} diff --git a/libgambatte/src/cinterface.h b/libgambatte/src/cinterface.h index 0a7ebcffee..55748318ff 100644 --- a/libgambatte/src/cinterface.h +++ b/libgambatte/src/cinterface.h @@ -48,6 +48,10 @@ extern "C" __declspec(dllexport) void gambatte_setgameshark(void *core, const char *codes); __declspec(dllexport) int gambatte_getmemoryarea(void *core, int which, unsigned char **data, int *length); + + __declspec(dllexport) unsigned char gambatte_cpuread(void *core, unsigned short addr); + + __declspec(dllexport) void gambatte_cpuwrite(void *core, unsigned short addr, unsigned char val); } diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 87a26dbcd4..98cfd41da6 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -95,6 +95,10 @@ public: void setGameGenie(const std::string &codes) { memory.setGameGenie(codes); } void setGameShark(const std::string &codes) { memory.setGameShark(codes); } + + unsigned char ExternalRead(unsigned short addr) { return memory.read(addr, cycleCounter_); } + void ExternalWrite(unsigned short addr, unsigned char val) { memory.write(addr, val, cycleCounter_); } + }; } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 1688f68128..d445489ac0 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -149,6 +149,19 @@ bool GB::getMemoryArea(int which, unsigned char **data, int *length) { return false; } +unsigned char GB::ExternalRead(unsigned short addr) { + if (p_->cpu.loaded()) + return p_->cpu.ExternalRead(addr); + else + return 0; +} + +void GB::ExternalWrite(unsigned short addr, unsigned char val) { + if (p_->cpu.loaded()) + p_->cpu.ExternalWrite(addr, val); +} + + void GB::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) { p_->cpu.setDmgPaletteColor(palNum, colorNum, rgb32); }