void InitMemoryDomains() { CreateMemoryDomain(LibGambatte.MemoryAreas.wram, "WRAM"); CreateMemoryDomain(LibGambatte.MemoryAreas.rom, "ROM"); CreateMemoryDomain(LibGambatte.MemoryAreas.vram, "VRAM"); CreateMemoryDomain(LibGambatte.MemoryAreas.cartram, "Cart RAM"); CreateMemoryDomain(LibGambatte.MemoryAreas.oam, "OAM"); CreateMemoryDomain(LibGambatte.MemoryAreas.hram, "HRAM"); // also add a special memory domain for the system bus, where calls get sent directly to the core each time _MemoryDomains.Add(new MemoryDomain("System Bus", 65536, MemoryDomain.Endian.Little, delegate(int addr) { if (addr < 0 || addr >= 65536) { throw new ArgumentOutOfRangeException(); } return(LibGambatte.gambatte_cpuread(GambatteState, (ushort)addr)); }, delegate(int addr, byte val) { if (addr < 0 || addr >= 65536) { throw new ArgumentOutOfRangeException(); } LibGambatte.gambatte_cpuwrite(GambatteState, (ushort)addr, val); })); MemoryDomains = new MemoryDomainList(_MemoryDomains); }
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); }
private void ProcessMbcSound(bool processSound) { int nsamp = LibGambatte.gambatte_generatembcsamples(GambatteState, _mbcsoundbuff); if (nsamp == 0) { return; } for (uint i = 0; i < nsamp; i++) { int ls = _mbcsoundbuff[i * 2] - _mbcLatchL; int rs = _mbcsoundbuff[(i * 2) + 1] - _mbcLatchR; if (ls != 0 && processSound) { _blipL.AddDelta(i, ls); } if (rs != 0 && processSound) { _blipR.AddDelta(i, rs); } _mbcLatchL = _mbcsoundbuff[i * 2]; _mbcLatchR = _mbcsoundbuff[(i * 2) + 1]; } }
public void Disconnect() { if (gb.GambatteState != IntPtr.Zero) { LibGambatte.gambatte_setlinkcallback(gb.GambatteState, null); } }
private void ProcessSgbSound(int nsamp, bool processSound) { int remainder = LibGambatte.gambatte_generatesgbsamples(GambatteState, _sgbsoundbuff, out uint samples); if (remainder < 0) { throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_generatesgbsamples)}() returned negative (spc error???)"); } uint t = 65 - (uint)remainder; for (int i = 0; i < samples; i++, t += 65) { int ls = _sgbsoundbuff[i * 2] - _sgbLatchL; int rs = _sgbsoundbuff[(i * 2) + 1] - _sgbLatchR; if (ls != 0 && processSound) { _blipL.AddDelta(t, ls); } if (rs != 0 && processSound) { _blipR.AddDelta(t, rs); } _sgbLatchL = _sgbsoundbuff[i * 2]; _sgbLatchR = _sgbsoundbuff[(i * 2) + 1]; } }
public IDictionary <string, RegisterValue> GetCpuFlagsAndRegisters() { int[] data = new int[10]; LibGambatte.gambatte_getregs(GambatteState, data); return(new Dictionary <string, RegisterValue> { ["PC"] = (ushort)(data[(int)LibGambatte.RegIndices.PC] & 0xffff), ["SP"] = (ushort)(data[(int)LibGambatte.RegIndices.SP] & 0xffff), ["A"] = (byte)(data[(int)LibGambatte.RegIndices.A] & 0xff), ["B"] = (byte)(data[(int)LibGambatte.RegIndices.B] & 0xff), ["C"] = (byte)(data[(int)LibGambatte.RegIndices.C] & 0xff), ["D"] = (byte)(data[(int)LibGambatte.RegIndices.D] & 0xff), ["E"] = (byte)(data[(int)LibGambatte.RegIndices.E] & 0xff), ["F"] = (byte)(data[(int)LibGambatte.RegIndices.F] & 0xff), ["H"] = (byte)(data[(int)LibGambatte.RegIndices.H] & 0xff), ["L"] = (byte)(data[(int)LibGambatte.RegIndices.L] & 0xff), // banks ["ROM0 BANK"] = (ushort)LibGambatte.gambatte_getbank(GambatteState, LibGambatte.BankType.ROM0), ["ROMX BANK"] = (ushort)LibGambatte.gambatte_getbank(GambatteState, LibGambatte.BankType.ROMX), ["VRAM BANK"] = (byte)LibGambatte.gambatte_getbank(GambatteState, LibGambatte.BankType.VRAM), ["SRAM BANK"] = (byte)LibGambatte.gambatte_getbank(GambatteState, LibGambatte.BankType.SRAM), ["WRAM BANK"] = (byte)LibGambatte.gambatte_getbank(GambatteState, LibGambatte.BankType.WRAM), // todo: maybe do [bc]/[de]/[hl]? }); }
/// <summary> /// set up callback /// </summary> /// <param name="line">scanline. -1 = end of frame, -2 = RIGHT NOW</param> public void SetScanlineCallback(ScanlineCallback callback, int line) { if (GambatteState == IntPtr.Zero) { return; // not sure how this is being reached. tried the debugger... } endofframecallback = null; if (callback == null || line == -1 || line == -2) { scanlinecb = null; LibGambatte.gambatte_setscanlinecallback(GambatteState, null, 0); if (line == -1) { endofframecallback = callback; } else if (line == -2) { callback(LibGambatte.gambatte_cpuread(GambatteState, 0xff40)); } } else if (line >= 0 && line <= 153) { scanlinecb = delegate { callback(LibGambatte.gambatte_cpuread(GambatteState, 0xff40)); }; LibGambatte.gambatte_setscanlinecallback(GambatteState, scanlinecb, line); } else { throw new ArgumentOutOfRangeException(nameof(line), "line must be in [0, 153]"); } }
private void MakeTrace(IntPtr _s) { int[] s = new int[14]; System.Runtime.InteropServices.Marshal.Copy(_s, s, 0, 14); ushort unused; Tracer.Put(new TraceInfo { Disassembly = LR35902.Disassemble((ushort)s[1], addr => LibGambatte.gambatte_cpuread(GambatteState, addr), out unused) .PadRight(36), RegisterInfo = string.Format( "A:{3:x2} B:{4:x2} C:{5:x2} D:{6:x2} E:{7:x2} F:{8:x2} H:{9:x2} L:{10:x2} LY:{13:x2} SP:{2:x2} {11} Cy:{0}", s[0], s[1] & 0xffff, s[2] & 0xffff, s[3] & 0xff, s[4] & 0xff, s[5] & 0xff, s[6] & 0xff, s[7] & 0xff, s[8] & 0xff, s[9] & 0xff, s[10] & 0xff, s[11] != 0 ? "skip" : "", s[12] & 0xff, s[13] & 0xff) }); }
/// <summary> /// update gambatte core's internal colors /// </summary> public void ChangeDMGColors(int[] colors) { for (int i = 0; i < 12; i++) { LibGambatte.gambatte_setdmgpalettecolor(GambatteState, (LibGambatte.PalType)(i / 4), (uint)i % 4, (uint)colors[i]); } }
public void SaveStateBinary(BinaryWriter writer) { #if USE_UPSTREAM_STATES int size = LibGambatte.gambatte_savestate(GambatteState, null, 160, _stateBuf); if (size != _stateBuf.Length) { throw new InvalidOperationException("Savestate buffer size mismatch!"); } #else if (!LibGambatte.gambatte_newstatesave(GambatteState, _stateBuf, _stateBuf.Length)) { throw new Exception($"{nameof(LibGambatte.gambatte_newstatesave)}() returned false"); } #endif writer.Write(_stateBuf.Length); writer.Write(_stateBuf); // other variables writer.Write(IsLagFrame); writer.Write(LagCount); writer.Write(Frame); writer.Write(frameOverflow); writer.Write(_cycleCount); writer.Write(IsCgb); writer.Write(IsSgb); }
public void LoadStateBinary(BinaryReader reader) { int length = reader.ReadInt32(); if (length != _stateBuf.Length) { throw new InvalidOperationException("Savestate buffer size mismatch!"); } reader.Read(_stateBuf, 0, _stateBuf.Length); #if USE_UPSTREAM_STATES if (!LibGambatte.gambatte_loadstate(GambatteState, _stateBuf, _stateBuf.Length)) { throw new Exception($"{nameof(LibGambatte.gambatte_loadstate)}() returned false"); } #else if (!LibGambatte.gambatte_newstateload(GambatteState, _stateBuf, _stateBuf.Length)) { throw new Exception($"{nameof(LibGambatte.gambatte_newstateload)}() returned false"); } #endif // other variables IsLagFrame = reader.ReadBoolean(); LagCount = reader.ReadInt32(); Frame = reader.ReadInt32(); frameOverflow = reader.ReadUInt32(); _cycleCount = reader.ReadUInt64(); IsCgb = reader.ReadBoolean(); IsSgb = reader.ReadBoolean(); }
public GambatteLink(CoreComm comm, GameInfo leftinfo, byte[] leftrom, GameInfo rightinfo, byte[] rightrom, object settings, object syncSettings, bool deterministic) { ServiceProvider = new BasicServiceProvider(this); GambatteLinkSettings linkSettings = (GambatteLinkSettings)settings ?? new GambatteLinkSettings(); GambatteLinkSyncSettings linkSyncSettings = (GambatteLinkSyncSettings)syncSettings ?? new GambatteLinkSyncSettings(); L = new Gameboy(comm, leftinfo, leftrom, linkSettings.L, linkSyncSettings.L, deterministic); R = new Gameboy(comm, rightinfo, rightrom, linkSettings.R, linkSyncSettings.R, deterministic); // connect link cable LibGambatte.gambatte_linkstatus(L.GambatteState, 259); LibGambatte.gambatte_linkstatus(R.GambatteState, 259); L.ConnectInputCallbackSystem(_inputCallbacks); R.ConnectInputCallbackSystem(_inputCallbacks); L.ConnectMemoryCallbackSystem(_memorycallbacks); R.ConnectMemoryCallbackSystem(_memorycallbacks); RomDetails = "LEFT:\r\n" + L.RomDetails + "RIGHT:\r\n" + R.RomDetails; LinkConnected = true; Frame = 0; LagCount = 0; IsLagFrame = false; _blipLeft = new BlipBuffer(1024); _blipRight = new BlipBuffer(1024); _blipLeft.SetRates(2097152 * 2, 44100); _blipRight.SetRates(2097152 * 2, 44100); SetMemoryDomains(); }
private void InitMemoryCallbacks() { LibGambatte.MemoryCallback CreateCallback(MemoryCallbackFlags flags, Func <bool> getHasCBOfType) { var rawFlags = (uint)flags; return((address, cycleOffset) => { callbackCycleCount = _cycleCount + cycleOffset; if (getHasCBOfType()) { MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, systemBusScope); } }); } _readcb = CreateCallback(MemoryCallbackFlags.AccessRead, () => MemoryCallbacks.HasReads); _writecb = CreateCallback(MemoryCallbackFlags.AccessWrite, () => MemoryCallbacks.HasWrites); _execcb = CreateCallback(MemoryCallbackFlags.AccessExecute, () => MemoryCallbacks.HasExecutes); _memorycallbacks.ActiveChanged += () => { LibGambatte.gambatte_setreadcallback(GambatteState, MemoryCallbacks.HasReads ? _readcb : null); LibGambatte.gambatte_setwritecallback(GambatteState, MemoryCallbacks.HasWrites ? _writecb : null); LibGambatte.gambatte_setexeccallback(GambatteState, MemoryCallbacks.HasExecutes ? _execcb : null); }; }
internal void FrameAdvancePrep() { Frame++; // update our local copy of the controller data CurrentButtons = 0; if (Controller["Up"]) { CurrentButtons |= LibGambatte.Buttons.UP; } if (Controller["Down"]) { CurrentButtons |= LibGambatte.Buttons.DOWN; } if (Controller["Left"]) { CurrentButtons |= LibGambatte.Buttons.LEFT; } if (Controller["Right"]) { CurrentButtons |= LibGambatte.Buttons.RIGHT; } if (Controller["A"]) { CurrentButtons |= LibGambatte.Buttons.A; } if (Controller["B"]) { CurrentButtons |= LibGambatte.Buttons.B; } if (Controller["Select"]) { CurrentButtons |= LibGambatte.Buttons.SELECT; } if (Controller["Start"]) { CurrentButtons |= LibGambatte.Buttons.START; } // the controller callback will set this to false if it actually gets called during the frame IsLagFrame = true; if (Controller["Power"]) { LibGambatte.gambatte_reset(GambatteState, GetCurrentTime()); } if (Tracer.Enabled) { tracecb = MakeTrace; } else { tracecb = null; } LibGambatte.gambatte_settracecallback(GambatteState, tracecb); LibGambatte.gambatte_setlayers(GambatteState, (_settings.DisplayBG ? 1 : 0) | (_settings.DisplayOBJ ? 2 : 0) | (_settings.DisplayWindow ? 4 : 0)); }
void NewLoadCoreBinary(byte[] data) { if (!LibGambatte.gambatte_newstateload(GambatteState, data, data.Length)) { throw new Exception("gambatte_newstateload() returned false"); } }
public bool FrameAdvance(IController controller, bool render, bool rendersound) { FrameAdvancePrep(controller); if (_syncSettings.EqualLengthFrames) { while (true) { // target number of samples to emit: length of 1 frame minus whatever overflow uint samplesEmitted = TICKSINFRAME - frameOverflow; Debug.Assert(samplesEmitted * 2 <= _soundbuff.Length); if (LibGambatte.gambatte_runfor(GambatteState, _soundbuff, ref samplesEmitted) > 0) { LibGambatte.gambatte_blitto(GambatteState, VideoBuffer, 160); } // account for actual number of samples emitted _cycleCount += samplesEmitted; frameOverflow += samplesEmitted; if (rendersound && !Muted) { ProcessSound((int)samplesEmitted); } if (frameOverflow >= TICKSINFRAME) { frameOverflow -= TICKSINFRAME; break; } } } else { // target number of samples to emit: always 59.7fps // runfor() always ends after creating a video frame, so sync-up is guaranteed // when the display has been off, some frames can be markedly shorter than expected uint samplesEmitted = TICKSINFRAME; if (LibGambatte.gambatte_runfor(GambatteState, _soundbuff, ref samplesEmitted) > 0) { LibGambatte.gambatte_blitto(GambatteState, VideoBuffer, 160); } _cycleCount += samplesEmitted; frameOverflow = 0; if (rendersound && !Muted) { ProcessSound((int)samplesEmitted); } } if (rendersound && !Muted) { ProcessSoundEnd(); } FrameAdvancePost(); return(true); }
private void RefreshMemoryCallbacks() { var mcs = MemoryCallbacks; LibGambatte.gambatte_setreadcallback(GambatteState, mcs.HasReads ? _readcb : null); LibGambatte.gambatte_setwritecallback(GambatteState, mcs.HasWrites ? _writecb : null); LibGambatte.gambatte_setexeccallback(GambatteState, mcs.HasExecutes ? _execcb : null); }
private void NewSaveCoreSetBuff() { #if USE_UPSTREAM_STATES _stateBuf = new byte[LibGambatte.gambatte_savestate(GambatteState, null, 160, null)]; #else _stateBuf = new byte[LibGambatte.gambatte_newstatelen(GambatteState)]; #endif }
byte[] NewSaveCoreBinary() { if (!LibGambatte.gambatte_newstatesave(GambatteState, newsavebuff, newsavebuff.Length)) { throw new Exception("gambatte_newstatesave() returned false"); } return(newsavebuff); }
public void StoreSaveRam(byte[] data) { if (data.Length != LibGambatte.gambatte_savesavedatalength(GambatteState)) { throw new ArgumentException("Size of saveram data does not match expected!"); } LibGambatte.gambatte_loadsavedata(GambatteState, data); }
public void SetCpuRegister(string register, int value) { int[] data = new int[10]; LibGambatte.gambatte_getregs(GambatteState, data); LibGambatte.RegIndices index = (LibGambatte.RegIndices)Enum.Parse(typeof(LibGambatte.RegIndices), register); data[(int)index] = value & (index <= LibGambatte.RegIndices.SP ? 0xffff : 0xff); LibGambatte.gambatte_setregs(GambatteState, data); }
internal void FrameAdvancePost() { if (IsLagFrame) { LagCount++; } endofframecallback?.Invoke(LibGambatte.gambatte_cpuread(GambatteState, 0xff40)); }
public void Dispose() { if (GambatteState != IntPtr.Zero) { LibGambatte.gambatte_destroy(GambatteState); GambatteState = IntPtr.Zero; } DisposeSound(); }
void OnSerial() { if (LibGambatte.gambatte_linkstatus(gb.GambatteState, 256) != 0) // ClockTrigger { LibGambatte.gambatte_linkstatus(gb.GambatteState, 257); // ack byte output = HandleSerial((byte)LibGambatte.gambatte_linkstatus(gb.GambatteState, 258)); // GetOut LibGambatte.gambatte_linkstatus(gb.GambatteState, output); // ShiftIn } }
public GambattePrinter(Gameboy gb, PrinterCallback callback) { this.gb = gb; this.callback = callback; linkCallback = OnSerial; LibGambatte.gambatte_setlinkcallback(gb.GambatteState, linkCallback); // connect the cable LibGambatte.gambatte_linkstatus(gb.GambatteState, 259); }
void ICodeDataLogger.SetCDL(CodeDataLog cdl) { CDL = cdl; if (cdl == null) { LibGambatte.gambatte_setcdcallback(GambatteState, null); } else { LibGambatte.gambatte_setcdcallback(GambatteState, CDCallback); } }
internal void FrameAdvancePost() { if (IsLagFrame) { LagCount++; } if (endofframecallback != null) { endofframecallback(LibGambatte.gambatte_cpuread(GambatteState, 0xff40)); } }
internal void LoadState(TextState <TextStateData> s) { s.Prepare(); var ff = s.GetFunctionPointersLoad(); LibGambatte.gambatte_newstateload_ex(GambatteState, ref ff); IsLagFrame = s.ExtraData.IsLagFrame; LagCount = s.ExtraData.LagCount; Frame = s.ExtraData.Frame; frameOverflow = s.ExtraData.frameOverflow; _cycleCount = s.ExtraData._cycleCount; }
void CreateMemoryDomain(LibGambatte.MemoryAreas which, string name) { IntPtr data = IntPtr.Zero; int length = 0; 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 if (data != IntPtr.Zero && length > 0) _memoryDomains.Add(MemoryDomain.FromIntPtr(name, length, MemoryDomain.Endian.Little, data)); }
public byte[] CloneSaveRam() { int length = LibGambatte.gambatte_savesavedatalength(GambatteState, DeterministicEmulation); if (length > 0) { byte[] ret = new byte[length]; LibGambatte.gambatte_savesavedata(GambatteState, ret, DeterministicEmulation); return(ret); } return(new byte[0]); }
public GPUMemoryAreas GetGPU() { 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 InvalidOperationException("Unexpected error in gambatte_getmemoryarea"); } return(new GPUMemoryAreas(_vram, _oam, _sppal, _bgpal)); }
void CDCallbackProc(int addr, LibGambatte.CDLog_AddrType addrtype, LibGambatte.CDLog_Flags flags) { if (CDL == null) return; if (!CDL.Active) return; string key; switch (addrtype) { case LibGambatte.CDLog_AddrType.ROM: key = "ROM"; break; case LibGambatte.CDLog_AddrType.HRAM: key = "HRAM"; break; case LibGambatte.CDLog_AddrType.WRAM: key = "WRAM"; break; case LibGambatte.CDLog_AddrType.CartRAM: key = "CartRAM"; break; default: throw new InvalidOperationException("Juniper lightbulb proxy"); } CDL[key][addr] |= (byte)flags; }
unsafe void CreateMemoryDomain(LibGambatte.MemoryAreas which, string name) { IntPtr data = IntPtr.Zero; int length = 0; 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()"); byte* ptr = (byte*)data; _MemoryDomains.Add(new MemoryDomain(name, length, MemoryDomain.Endian.Little, delegate(int addr) { if (addr < 0 || addr >= length) throw new ArgumentOutOfRangeException(); return ptr[addr]; }, delegate(int addr, byte val) { if (addr < 0 || addr >= length) throw new ArgumentOutOfRangeException(); ptr[addr] = val; })); }