diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs
index 33cb2ed4c3..7c14e3d3a3 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs
@@ -145,25 +145,10 @@ namespace BizHawk.Emulation.Consoles.GB
tracecb = null;
LibGambatte.gambatte_settracecallback(GambatteState, tracecb);
- // todo: have the gambatte core actually call this at an appropriate time
- if (scanlinecallback != null)
- {
- IntPtr vram = IntPtr.Zero;
- IntPtr bgpal = IntPtr.Zero;
- IntPtr sppal = IntPtr.Zero;
- IntPtr oam = IntPtr.Zero;
- int unused = 0;
- if (!LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.vram, ref vram, ref unused)
- || !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.bgpal, ref bgpal, ref unused)
- || !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.sppal, ref sppal, ref unused)
- || !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.oam, ref oam, ref unused))
- throw new Exception();
-
- scanlinecallback(vram, IsCGBMode(), LibGambatte.gambatte_cpuread(GambatteState, 0xff40), bgpal, sppal, oam);
- }
-
LibGambatte.gambatte_runfor(GambatteState, VideoBuffer, 160, soundbuff, ref nsamp);
+ Console.WriteLine("===");
+
// upload any modified data to the memory domains
foreach (var r in MemoryRefreshers)
@@ -593,18 +578,36 @@ namespace BizHawk.Emulation.Consoles.GB
#endregion
#region ppudebug
+ public bool GetGPUMemoryAreas(out IntPtr vram, out IntPtr bgpal, out IntPtr sppal, out IntPtr oam)
+ {
+ IntPtr _vram = IntPtr.Zero;
+ IntPtr _bgpal = IntPtr.Zero;
+ IntPtr _sppal = IntPtr.Zero;
+ IntPtr _oam = IntPtr.Zero;
+ int unused = 0;
+ if (!LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.vram, ref _vram, ref unused)
+ || !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.bgpal, ref _bgpal, ref unused)
+ || !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.sppal, ref _sppal, ref unused)
+ || !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.oam, ref _oam, ref unused))
+ {
+ vram = IntPtr.Zero;
+ bgpal = IntPtr.Zero;
+ sppal = IntPtr.Zero;
+ oam = IntPtr.Zero;
+ return false;
+ }
+ vram = _vram;
+ bgpal = _bgpal;
+ sppal = _sppal;
+ oam = _oam;
+ return true;
+ }
+
///
///
///
- ///
- ///
- ///
- ///
- ///
- ///
- public delegate void ScanlineCallback(IntPtr vram, bool cgb, int lcdc, IntPtr bgpal, IntPtr sppal, IntPtr oam);
-
- ScanlineCallback scanlinecallback;
+ /// current value of register $ff40 (LCDC)
+ public delegate void ScanlineCallback(int lcdc);
///
/// set up callback
@@ -613,14 +616,25 @@ namespace BizHawk.Emulation.Consoles.GB
/// scanline
public void SetScanlineCallback(ScanlineCallback callback, int line)
{
+ if (GambatteState == IntPtr.Zero)
+ // not sure how this is being reached. tried the debugger...
+ return;
if (callback == null)
- this.scanlinecallback = null;
+ scanlinecb = null;
else if (line < 0 || line > 153)
throw new ArgumentOutOfRangeException("line must be in [0, 153]");
else
- this.scanlinecallback = callback;
+ scanlinecb = delegate()
+ {
+ callback(LibGambatte.gambatte_cpuread(GambatteState, 0xff40));
+ //callback(0);
+ };
+
+ LibGambatte.gambatte_setscanlinecallback(GambatteState, scanlinecb, 0);
}
+ LibGambatte.ScanlineCallback scanlinecb;
+
#endregion
public void Dispose()
diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs
index 17e248e7a7..3877502c0d 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs
@@ -167,13 +167,30 @@ namespace BizHawk.Emulation.Consoles.GB
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_settracecallback(IntPtr core, TraceCallback callback);
+ ///
+ /// type of the scanline callback
+ ///
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void ScanlineCallback();
+
+ ///
+ /// set a callback to occur when ly reaches a particular scanline (so at the beginning of the scanline).
+ /// when the LCD is active, typically 145 will be the first callback after the beginning of frame advance,
+ /// and 144 will be the last callback right before frame advance returns
+ ///
+ /// opaque state pointer
+ /// null to clear
+ /// 0-153 inclusive
+ [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void gambatte_setscanlinecallback(IntPtr core, ScanlineCallback callback, int sl);
+
///
/// Sets the directory used for storing save data. The default is the same directory as the ROM Image file.
///
/// opaque state pointer
///
- [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern void gambatte_setsavedir(IntPtr core, string sdir);
+ //[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
+ //public static extern void gambatte_setsavedir(IntPtr core, string sdir);
///
/// Returns true if the currently loaded ROM image is treated as having CGB support.
diff --git a/BizHawk.MultiClient/GBtools/GBGPUView.cs b/BizHawk.MultiClient/GBtools/GBGPUView.cs
index 742d2216ec..dbb695a900 100644
--- a/BizHawk.MultiClient/GBtools/GBGPUView.cs
+++ b/BizHawk.MultiClient/GBtools/GBGPUView.cs
@@ -13,6 +13,15 @@ namespace BizHawk.MultiClient.GBtools
{
Emulation.Consoles.GB.Gameboy gb;
+ // gambatte doesn't modify these memory locations unless you reconstruct, so we can store
+ IntPtr vram;
+ IntPtr bgpal;
+ IntPtr sppal;
+ IntPtr oam;
+
+ bool cgb; // set once at start
+ int lcdc; // set at each callback
+
public GBGPUView()
{
InitializeComponent();
@@ -30,7 +39,16 @@ namespace BizHawk.MultiClient.GBtools
if (Global.Emulator is Emulation.Consoles.GB.Gameboy)
{
gb = Global.Emulator as Emulation.Consoles.GB.Gameboy;
- if (gb.IsCGBMode())
+ cgb = gb.IsCGBMode();
+ lcdc = 0;
+ if (!gb.GetGPUMemoryAreas(out vram, out bgpal, out sppal, out oam))
+ {
+ gb = null;
+ if (Visible)
+ Close();
+ }
+
+ if (cgb)
label4.Enabled = true;
else
label4.Enabled = false;
@@ -302,8 +320,9 @@ namespace BizHawk.MultiClient.GBtools
b.UnlockBits(lockdata);
}
- void ScanlineCallback(IntPtr vram, bool cgb, int lcdc, IntPtr bgpal, IntPtr sppal, IntPtr oam)
+ void ScanlineCallback(int lcdc)
{
+ this.lcdc = lcdc;
// set alpha on all pixels
unsafe
{
diff --git a/BizHawk.MultiClient/output/dll/libgambatte.dll b/BizHawk.MultiClient/output/dll/libgambatte.dll
index 3ba2573d65..43f431c686 100644
Binary files a/BizHawk.MultiClient/output/dll/libgambatte.dll and b/BizHawk.MultiClient/output/dll/libgambatte.dll differ
diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h
index a0729dbea9..be67840072 100644
--- a/libgambatte/include/gambatte.h
+++ b/libgambatte/include/gambatte.h
@@ -86,6 +86,7 @@ public:
void setReadCallback(void (*callback)(unsigned));
void setWriteCallback(void (*callback)(unsigned));
void setTraceCallback(void (*callback)(void *));
+ void setScanlineCallback(void (*callback)(), int sl);
/** Sets the directory used for storing save data. The default is the same directory as the ROM Image file. */
void setSaveDir(const std::string &sdir);
diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp
index 26735cf718..0858a6304d 100644
--- a/libgambatte/src/cinterface.cpp
+++ b/libgambatte/src/cinterface.cpp
@@ -83,6 +83,12 @@ __declspec(dllexport) void gambatte_settracecallback(void *core, void (*callback
g->setTraceCallback(callback);
}
+__declspec(dllexport) void gambatte_setscanlinecallback(void *core, void (*callback)(), int sl)
+{
+ GB *g = (GB *) core;
+ g->setScanlineCallback(callback, sl);
+}
+
__declspec(dllexport) void gambatte_setsavedir(void *core, const char *sdir)
{
GB *g = (GB *) core;
diff --git a/libgambatte/src/cinterface.h b/libgambatte/src/cinterface.h
index 01b0c383d7..810347fc53 100644
--- a/libgambatte/src/cinterface.h
+++ b/libgambatte/src/cinterface.h
@@ -24,6 +24,8 @@ extern "C"
__declspec(dllexport) void gambatte_settracecallback(void *core, void (*callback)(void *));
+ __declspec(dllexport) void gambatte_setscanlinecallback(void *core, void (*callback)(), int sl);
+
__declspec(dllexport) void gambatte_setsavedir(void *core, const char *sdir);
__declspec(dllexport) int gambatte_iscgb(void *core);
diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h
index 640f518460..13711eef00 100644
--- a/libgambatte/src/cpu.h
+++ b/libgambatte/src/cpu.h
@@ -78,6 +78,10 @@ public:
void setTraceCallback(void (*callback)(void *)) {
tracecallback = callback;
}
+
+ void setScanlineCallback(void (*callback)(), int sl) {
+ memory.setScanlineCallback(callback, sl);
+ }
void setSaveDir(const std::string &sdir) {
memory.setSaveDir(sdir);
@@ -110,7 +114,8 @@ 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_); }
+ //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(addr, val, cycleCounter_); }
};
diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp
index acb5a29b8c..5c295fbd98 100644
--- a/libgambatte/src/gambatte.cpp
+++ b/libgambatte/src/gambatte.cpp
@@ -107,6 +107,10 @@ void GB::setTraceCallback(void (*callback)(void *)) {
p_->cpu.setTraceCallback(callback);
}
+void GB::setScanlineCallback(void (*callback)(), int sl) {
+ p_->cpu.setScanlineCallback(callback, sl);
+}
+
void GB::setSaveDir(const std::string &sdir) {
p_->cpu.setSaveDir(sdir);
}
diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp
index 2405711180..4aaa934cf7 100644
--- a/libgambatte/src/memory.cpp
+++ b/libgambatte/src/memory.cpp
@@ -570,6 +570,32 @@ unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCoun
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);
diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h
index 536a25f53f..9b01cbf8b9 100644
--- a/libgambatte/src/memory.h
+++ b/libgambatte/src/memory.h
@@ -66,6 +66,9 @@ class Memory {
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);
@@ -119,6 +122,10 @@ public:
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(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
if (cart.wmem(P >> 12)) {
cart.wmem(P >> 12)[P] = data;
@@ -152,6 +159,10 @@ public:
this->writeCallback = callback;
}
+ void setScanlineCallback(void (*callback)(), int sl) {
+ display.setScanlineCallback(callback, sl);
+ }
+
void setEndtime(unsigned long cc, unsigned long inc);
void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); }
diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp
index 3aac3e4beb..d581304c57 100644
--- a/libgambatte/src/video.cpp
+++ b/libgambatte/src/video.cpp
@@ -71,7 +71,9 @@ LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram, con
eventTimes_(memEventRequester),
statReg(0),
m2IrqStatReg_(0),
- m1IrqStatReg_(0)
+ m1IrqStatReg_(0),
+ scanlinecallback(0),
+ scanlinecallbacksl(0)
{
std::memset( bgpData, 0, sizeof bgpData);
std::memset(objpData, 0, sizeof objpData);
@@ -772,6 +774,8 @@ inline void LCD::event() {
case LY_COUNT:
ppu.doLyCountEvent();
eventTimes_.set(ppu.lyCounter().time());
+ if (scanlinecallback && ppu.lyCounter().ly() == scanlinecallbacksl)
+ scanlinecallback();
break;
}
}
diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h
index e58c910f28..b0cfca8560 100644
--- a/libgambatte/src/video.h
+++ b/libgambatte/src/video.h
@@ -150,6 +150,9 @@ class LCD {
void doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
void doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
+ void (*scanlinecallback)();
+ int scanlinecallbacksl;
+
public:
LCD(const unsigned char *oamram, const unsigned char *vram_in, VideoInterruptRequester memEventRequester);
void reset(const unsigned char *oamram, const unsigned char *vram, bool cgb);
@@ -253,6 +256,8 @@ public:
unsigned long *bgPalette() { return ppu.bgPalette(); }
unsigned long *spPalette() { return ppu.spPalette(); }
+
+ void setScanlineCallback(void (*callback)(), int sl) { scanlinecallback = callback; scanlinecallbacksl = sl; }
};
}