libgambatte: switch the system bus read to use a much safer (100%?) deterministic peek. also implement core side stuff for scanline-based callback

This commit is contained in:
goyuken 2012-11-05 20:15:53 +00:00
parent 788591ba77
commit ac1f9a90a1
13 changed files with 148 additions and 34 deletions

View File

@ -145,25 +145,10 @@ namespace BizHawk.Emulation.Consoles.GB
tracecb = null; tracecb = null;
LibGambatte.gambatte_settracecallback(GambatteState, tracecb); 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); LibGambatte.gambatte_runfor(GambatteState, VideoBuffer, 160, soundbuff, ref nsamp);
Console.WriteLine("===");
// upload any modified data to the memory domains // upload any modified data to the memory domains
foreach (var r in MemoryRefreshers) foreach (var r in MemoryRefreshers)
@ -593,18 +578,36 @@ namespace BizHawk.Emulation.Consoles.GB
#endregion #endregion
#region ppudebug #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;
}
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="vram"></param> /// <param name="lcdc">current value of register $ff40 (LCDC)</param>
/// <param name="cgb"></param> public delegate void ScanlineCallback(int lcdc);
/// <param name="lcdc"></param>
/// <param name="bgpal"></param>
/// <param name="sppal"></param>
/// <param name="oam"></param>
public delegate void ScanlineCallback(IntPtr vram, bool cgb, int lcdc, IntPtr bgpal, IntPtr sppal, IntPtr oam);
ScanlineCallback scanlinecallback;
/// <summary> /// <summary>
/// set up callback /// set up callback
@ -613,14 +616,25 @@ namespace BizHawk.Emulation.Consoles.GB
/// <param name="line">scanline</param> /// <param name="line">scanline</param>
public void SetScanlineCallback(ScanlineCallback callback, int line) 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) if (callback == null)
this.scanlinecallback = null; scanlinecb = null;
else if (line < 0 || line > 153) else if (line < 0 || line > 153)
throw new ArgumentOutOfRangeException("line must be in [0, 153]"); throw new ArgumentOutOfRangeException("line must be in [0, 153]");
else else
this.scanlinecallback = callback; scanlinecb = delegate()
{
callback(LibGambatte.gambatte_cpuread(GambatteState, 0xff40));
//callback(0);
};
LibGambatte.gambatte_setscanlinecallback(GambatteState, scanlinecb, 0);
} }
LibGambatte.ScanlineCallback scanlinecb;
#endregion #endregion
public void Dispose() public void Dispose()

View File

@ -167,13 +167,30 @@ namespace BizHawk.Emulation.Consoles.GB
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_settracecallback(IntPtr core, TraceCallback callback); public static extern void gambatte_settracecallback(IntPtr core, TraceCallback callback);
/// <summary>
/// type of the scanline callback
/// </summary>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ScanlineCallback();
/// <summary>
/// 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
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="callback">null to clear</param>
/// <param name="sl">0-153 inclusive</param>
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_setscanlinecallback(IntPtr core, ScanlineCallback callback, int sl);
/// <summary> /// <summary>
/// Sets the directory used for storing save data. The default is the same directory as the ROM Image file. /// Sets the directory used for storing save data. The default is the same directory as the ROM Image file.
/// </summary> /// </summary>
/// <param name="core">opaque state pointer</param> /// <param name="core">opaque state pointer</param>
/// <param name="sdir"></param> /// <param name="sdir"></param>
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] //[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_setsavedir(IntPtr core, string sdir); //public static extern void gambatte_setsavedir(IntPtr core, string sdir);
/// <summary> /// <summary>
/// Returns true if the currently loaded ROM image is treated as having CGB support. /// Returns true if the currently loaded ROM image is treated as having CGB support.

View File

@ -13,6 +13,15 @@ namespace BizHawk.MultiClient.GBtools
{ {
Emulation.Consoles.GB.Gameboy gb; 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() public GBGPUView()
{ {
InitializeComponent(); InitializeComponent();
@ -30,7 +39,16 @@ namespace BizHawk.MultiClient.GBtools
if (Global.Emulator is Emulation.Consoles.GB.Gameboy) if (Global.Emulator is Emulation.Consoles.GB.Gameboy)
{ {
gb = Global.Emulator as 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; label4.Enabled = true;
else else
label4.Enabled = false; label4.Enabled = false;
@ -302,8 +320,9 @@ namespace BizHawk.MultiClient.GBtools
b.UnlockBits(lockdata); 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 // set alpha on all pixels
unsafe unsafe
{ {

View File

@ -86,6 +86,7 @@ public:
void setReadCallback(void (*callback)(unsigned)); void setReadCallback(void (*callback)(unsigned));
void setWriteCallback(void (*callback)(unsigned)); void setWriteCallback(void (*callback)(unsigned));
void setTraceCallback(void (*callback)(void *)); 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. */ /** 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); void setSaveDir(const std::string &sdir);

View File

@ -83,6 +83,12 @@ __declspec(dllexport) void gambatte_settracecallback(void *core, void (*callback
g->setTraceCallback(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) __declspec(dllexport) void gambatte_setsavedir(void *core, const char *sdir)
{ {
GB *g = (GB *) core; GB *g = (GB *) core;

View File

@ -24,6 +24,8 @@ extern "C"
__declspec(dllexport) void gambatte_settracecallback(void *core, void (*callback)(void *)); __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) void gambatte_setsavedir(void *core, const char *sdir);
__declspec(dllexport) int gambatte_iscgb(void *core); __declspec(dllexport) int gambatte_iscgb(void *core);

View File

@ -79,6 +79,10 @@ public:
tracecallback = callback; tracecallback = callback;
} }
void setScanlineCallback(void (*callback)(), int sl) {
memory.setScanlineCallback(callback, sl);
}
void setSaveDir(const std::string &sdir) { void setSaveDir(const std::string &sdir) {
memory.setSaveDir(sdir); memory.setSaveDir(sdir);
} }
@ -110,7 +114,8 @@ public:
void setGameGenie(const std::string &codes) { memory.setGameGenie(codes); } void setGameGenie(const std::string &codes) { memory.setGameGenie(codes); }
void setGameShark(const std::string &codes) { memory.setGameShark(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_); } void ExternalWrite(unsigned short addr, unsigned char val) { memory.write(addr, val, cycleCounter_); }
}; };

View File

@ -107,6 +107,10 @@ void GB::setTraceCallback(void (*callback)(void *)) {
p_->cpu.setTraceCallback(callback); p_->cpu.setTraceCallback(callback);
} }
void GB::setScanlineCallback(void (*callback)(), int sl) {
p_->cpu.setScanlineCallback(callback, sl);
}
void GB::setSaveDir(const std::string &sdir) { void GB::setSaveDir(const std::string &sdir) {
p_->cpu.setSaveDir(sdir); p_->cpu.setSaveDir(sdir);
} }

View File

@ -570,6 +570,32 @@ unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCoun
return ioamhram[P - 0xFE00]; 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) { void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) {
if (lastOamDmaUpdate != DISABLED_TIME) if (lastOamDmaUpdate != DISABLED_TIME)
updateOamDma(cycleCounter); updateOamDma(cycleCounter);

View File

@ -66,6 +66,9 @@ class Memory {
void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter); void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter);
void nontrivial_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 updateSerial(unsigned long cc);
void updateTimaIrq(unsigned long cc); void updateTimaIrq(unsigned long cc);
void updateIrqs(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); 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) { void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
if (cart.wmem(P >> 12)) { if (cart.wmem(P >> 12)) {
cart.wmem(P >> 12)[P] = data; cart.wmem(P >> 12)[P] = data;
@ -152,6 +159,10 @@ public:
this->writeCallback = callback; this->writeCallback = callback;
} }
void setScanlineCallback(void (*callback)(), int sl) {
display.setScanlineCallback(callback, sl);
}
void setEndtime(unsigned long cc, unsigned long inc); void setEndtime(unsigned long cc, unsigned long inc);
void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); } void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); }

View File

@ -71,7 +71,9 @@ LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram, con
eventTimes_(memEventRequester), eventTimes_(memEventRequester),
statReg(0), statReg(0),
m2IrqStatReg_(0), m2IrqStatReg_(0),
m1IrqStatReg_(0) m1IrqStatReg_(0),
scanlinecallback(0),
scanlinecallbacksl(0)
{ {
std::memset( bgpData, 0, sizeof bgpData); std::memset( bgpData, 0, sizeof bgpData);
std::memset(objpData, 0, sizeof objpData); std::memset(objpData, 0, sizeof objpData);
@ -772,6 +774,8 @@ inline void LCD::event() {
case LY_COUNT: case LY_COUNT:
ppu.doLyCountEvent(); ppu.doLyCountEvent();
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time()); eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
if (scanlinecallback && ppu.lyCounter().ly() == scanlinecallbacksl)
scanlinecallback();
break; break;
} }
} }

View File

@ -150,6 +150,9 @@ class LCD {
void doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter); void doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
void doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter); void doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
void (*scanlinecallback)();
int scanlinecallbacksl;
public: public:
LCD(const unsigned char *oamram, const unsigned char *vram_in, VideoInterruptRequester memEventRequester); LCD(const unsigned char *oamram, const unsigned char *vram_in, VideoInterruptRequester memEventRequester);
void reset(const unsigned char *oamram, const unsigned char *vram, bool cgb); 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 *bgPalette() { return ppu.bgPalette(); }
unsigned long *spPalette() { return ppu.spPalette(); } unsigned long *spPalette() { return ppu.spPalette(); }
void setScanlineCallback(void (*callback)(), int sl) { scanlinecallback = callback; scanlinecallbacksl = sl; }
}; };
} }