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; } }; }