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;
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;
}
/// <summary>
///
/// </summary>
/// <param name="vram"></param>
/// <param name="cgb"></param>
/// <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;
/// <param name="lcdc">current value of register $ff40 (LCDC)</param>
public delegate void ScanlineCallback(int lcdc);
/// <summary>
/// set up callback
@ -613,14 +616,25 @@ namespace BizHawk.Emulation.Consoles.GB
/// <param name="line">scanline</param>
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()

View File

@ -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);
/// <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>
/// Sets the directory used for storing save data. The default is the same directory as the ROM Image file.
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="sdir"></param>
[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);
/// <summary>
/// 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;
// 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
{

View File

@ -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);

View File

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

View File

@ -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);

View File

@ -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_); }
};

View File

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

View File

@ -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);

View File

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

View File

@ -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<LY_COUNT>(ppu.lyCounter().time());
if (scanlinecallback && ppu.lyCounter().ly() == scanlinecallbacksl)
scanlinecallback();
break;
}
}

View File

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