using BizHawk.Common; using BizHawk.Common.BizInvoke; using BizHawk.Emulation.Common; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Waterbox { public abstract class LibWaterboxCore { public const CallingConvention CC = CallingConvention.Cdecl; [StructLayout(LayoutKind.Sequential)] public class FrameInfo { /// /// pointer to the video buffer; set by frontend, filled by backend /// public IntPtr VideoBuffer; /// /// pointer to the sound buffer; set by frontend, filled by backend /// public IntPtr SoundBuffer; /// /// total number of cycles emulated this frame; set by backend /// public long Cycles; /// /// width of the output image; set by backend /// public int Width; /// /// height of the output image; set by backend /// public int Height; /// /// total number of sample pairs produced; set by backend /// public int Samples; /// /// true if controllers were not read; set by backend /// public int Lagged; } [Flags] public enum MemoryDomainFlags: int { None = 0, /// /// if false, the domain MUST NOT be written to. /// in some cases, a segmentation violation might occur /// Writable = 1, /// /// if true, this memory domain should be used in saveram. /// can be ignored if the core provides its own saveram implementation /// Saverammable = 2, /// /// if true, domain is filled with ones (FF) by default, instead of zeros. /// used in calculating SaveRamModified /// OneFilled = 4, /// /// desginates the default memory domain /// Primary = 8, /// /// if true, the most significant bytes are first in multibyte words /// YugeEndian = 16, /// /// native wordsize. only a hint /// WordSize1 = 32, /// /// native wordsize. only a hint /// WordSize2 = 64, /// /// native wordsize. only a hint /// WordSize4 = 128, /// /// native wordsize. only a hint /// WordSize8 = 256 } [StructLayout(LayoutKind.Sequential)] public struct MemoryArea { /// /// pointer to the data in memory /// public IntPtr Data; /// /// null terminated strnig naming the memory domain /// public IntPtr Name; /// /// size of the domain /// public long Size; /// /// /// public MemoryDomainFlags Flags; } [UnmanagedFunctionPointer(CC)] public delegate void EmptyCallback(); public unsafe class WaterboxMemoryDomain : MemoryDomain { private readonly IntPtr _data; private readonly IMonitor _monitor; private readonly long _addressMangler; public override byte PeekByte(long addr) { if ((ulong)addr < (ulong)Size) { using (_monitor.EnterExit()) { return ((byte*)_data)[addr ^ _addressMangler]; } } throw new ArgumentOutOfRangeException(nameof(addr)); } public override void PokeByte(long addr, byte val) { if (Writable) { if ((ulong)addr < (ulong)Size) { using (_monitor.EnterExit()) { ((byte*)_data)[addr ^ _addressMangler] = val; } } else { throw new ArgumentOutOfRangeException(nameof(addr)); } } } public WaterboxMemoryDomain(MemoryArea m, IMonitor monitor) { Name = Marshal.PtrToStringAnsi(m.Name); EndianType = (m.Flags & MemoryDomainFlags.YugeEndian) != 0 ? Endian.Big : Endian.Little; _data = m.Data; Size = m.Size; Writable = (m.Flags & MemoryDomainFlags.Writable) != 0; if ((m.Flags & MemoryDomainFlags.WordSize1) != 0) WordSize = 1; else if((m.Flags & MemoryDomainFlags.WordSize2) != 0) WordSize = 2; else if((m.Flags & MemoryDomainFlags.WordSize4) != 0) WordSize = 4; else if((m.Flags & MemoryDomainFlags.WordSize8) != 0) WordSize = 8; else throw new InvalidOperationException("Unknown word size for memory domain"); _monitor = monitor; if (EndianType == Endian.Big) { _addressMangler = WordSize - 1; } else { _addressMangler = 0; } } } [BizImport(CC)] public abstract void FrameAdvance([In, Out] FrameInfo frame); [BizImport(CC)] public abstract void GetMemoryAreas([In, Out] MemoryArea[] areas); [BizImport(CC)] public abstract void SetInputCallback(EmptyCallback callback); } }