Create an abstract base class for waterbox cores, and adapt Virtual Boyee to use it. Not sure yet how useful this is...

This commit is contained in:
nattthebear 2017-06-17 10:07:02 -04:00
parent 02242e8997
commit 6f60eb5efc
14 changed files with 3619 additions and 3362 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1276,6 +1276,7 @@
<Compile Include="Libretro\LibretroCore_Description.cs" />
<Compile Include="Libretro\LibretroCore_InputCallbacks.cs" />
<Compile Include="Waterbox\Heap.cs" />
<Compile Include="Waterbox\LibWaterboxCore.cs" />
<Compile Include="Waterbox\MapHeap.cs" />
<Compile Include="Waterbox\MemoryBlock.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@ -1292,6 +1293,7 @@
<Compile Include="Waterbox\PeRunner.cs" />
<Compile Include="Waterbox\PeWrapper.cs" />
<Compile Include="Waterbox\Swappable.cs" />
<Compile Include="Waterbox\WaterboxCore.cs" />
<Compile Include="Waterbox\WaterboxUtils.cs" />
</ItemGroup>
<ItemGroup>

View File

@ -1,148 +1,85 @@
using BizHawk.Common.BizInvoke;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
{
public abstract class LibVirtualBoyee
{
private const CallingConvention CC = CallingConvention.Cdecl;
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int X;
public int Y;
public int W;
public int H;
}
[StructLayout(LayoutKind.Explicit)] // TODO: find out why Sequential is sometimes ignored on the native layout
public class EmulateSpec
{
// Pitch(32-bit) must be equal to width and >= the "fb_width" specified in the MDFNGI struct for the emulated system.
// Height must be >= to the "fb_height" specified in the MDFNGI struct for the emulated system.
// The framebuffer pointed to by surface->pixels is written to by the system emulation code.
[FieldOffset(0)]
public IntPtr Pixels;
// Pointer to sound buffer, set by the driver code, that the emulation code should render sound to.
// Guaranteed to be at least 500ms in length, but emulation code really shouldn't exceed 40ms or so. Additionally, if emulation code
// generates >= 100ms,
// DEPRECATED: Emulation code may set this pointer to a sound buffer internal to the emulation module.
[FieldOffset(8)]
public IntPtr SoundBuf;
// Number of cycles that this frame consumed, using MDFNGI::MasterClock as a time base.
// Set by emulation code.
[FieldOffset(16)]
public long MasterCycles;
// Set by the system emulation code every frame, to denote the horizontal and vertical offsets of the image, and the size
// of the image. If the emulated system sets the elements of LineWidths, then the width(w) of this structure
// is ignored while drawing the image.
[FieldOffset(24)]
public Rect DisplayRect;
// Maximum size of the sound buffer, in frames. Set by the driver code.
[FieldOffset(40)]
public int SoundBufMaxSize;
// Number of frames currently in internal sound buffer. Set by the system emulation code, to be read by the driver code.
[FieldOffset(44)]
public int SoundBufSize;
// 0 UDLR SelectStartBA UDLR(right dpad) LtrigRtrig 13
[FieldOffset(48)]
public Buttons Buttons;
[FieldOffset(52)]
public bool Lagged;
}
public enum MemoryArea : int
{
Wram, Sram, Rom
}
public enum Buttons : int
{
Up = 0x200,
Down = 0x100,
Left = 0x80,
Right = 0x40,
Select = 0x800,
Start = 0x400,
B = 0x2,
A = 0x1,
Up_R = 0x10,
Down_R = 0x200,
Left_R = 0x1000,
Right_R = 0x2000,
L = 0x8,
R = 0x4
}
[StructLayout(LayoutKind.Sequential)]
public class NativeSettings
{
public int InstantReadHack;
public int DisableParallax;
public int ThreeDeeMode;
public int SwapViews;
public int AnaglyphPreset;
public int AnaglyphCustomLeftColor;
public int AnaglyphCustomRightColor;
public int NonAnaglyphColor;
public int LedOnScale;
public int InterlacePrescale;
public int SideBySideSeparation;
private static int ConvertColor(Color c)
{
return c.ToArgb();
}
public static NativeSettings FromFrontendSettings(VirtualBoyee.Settings s, VirtualBoyee.SyncSettings ss)
{
return new NativeSettings
{
InstantReadHack = ss.InstantReadHack ? 1 : 0,
DisableParallax = ss.DisableParallax ? 1 : 0,
ThreeDeeMode = (int)s.ThreeDeeMode,
SwapViews = s.SwapViews ? 1 : 0,
AnaglyphPreset = (int)s.AnaglyphPreset,
AnaglyphCustomLeftColor = ConvertColor(s.AnaglyphCustomLeftColor),
AnaglyphCustomRightColor = ConvertColor(s.AnaglyphCustomRightColor),
NonAnaglyphColor = ConvertColor(s.NonAnaglyphColor),
LedOnScale = s.LedOnScale,
InterlacePrescale = s.InterlacePrescale,
SideBySideSeparation = s.SideBySideSeparation
};
}
}
[UnmanagedFunctionPointer(CC)]
public delegate void InputCallback();
[BizImport(CC)]
public abstract bool Load(byte[] rom, int length, [In]NativeSettings settings);
[BizImport(CC)]
public abstract void GetMemoryArea(MemoryArea which, ref IntPtr ptr, ref int size);
[BizImport(CC)]
public abstract void Emulate(EmulateSpec espec);
[BizImport(CC)]
public abstract void HardReset();
[BizImport(CC)]
public abstract void SetInputCallback(InputCallback callback);
}
}
using BizHawk.Common.BizInvoke;
using BizHawk.Emulation.Cores.Waterbox;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
{
public abstract class LibVirtualBoyee : LibWaterboxCore
{
public enum Buttons : int
{
Up = 0x200,
Down = 0x100,
Left = 0x80,
Right = 0x40,
Select = 0x800,
Start = 0x400,
B = 0x2,
A = 0x1,
Up_R = 0x10,
Down_R = 0x200,
Left_R = 0x1000,
Right_R = 0x2000,
L = 0x8,
R = 0x4
}
[StructLayout(LayoutKind.Sequential)]
public new class FrameInfo : LibWaterboxCore.FrameInfo
{
public Buttons Buttons;
}
[StructLayout(LayoutKind.Sequential)]
public class NativeSettings
{
public int InstantReadHack;
public int DisableParallax;
public int ThreeDeeMode;
public int SwapViews;
public int AnaglyphPreset;
public int AnaglyphCustomLeftColor;
public int AnaglyphCustomRightColor;
public int NonAnaglyphColor;
public int LedOnScale;
public int InterlacePrescale;
public int SideBySideSeparation;
private static int ConvertColor(Color c)
{
return c.ToArgb();
}
public static NativeSettings FromFrontendSettings(VirtualBoyee.Settings s, VirtualBoyee.SyncSettings ss)
{
return new NativeSettings
{
InstantReadHack = ss.InstantReadHack ? 1 : 0,
DisableParallax = ss.DisableParallax ? 1 : 0,
ThreeDeeMode = (int)s.ThreeDeeMode,
SwapViews = s.SwapViews ? 1 : 0,
AnaglyphPreset = (int)s.AnaglyphPreset,
AnaglyphCustomLeftColor = ConvertColor(s.AnaglyphCustomLeftColor),
AnaglyphCustomRightColor = ConvertColor(s.AnaglyphCustomRightColor),
NonAnaglyphColor = ConvertColor(s.NonAnaglyphColor),
LedOnScale = s.LedOnScale,
InterlacePrescale = s.InterlacePrescale,
SideBySideSeparation = s.SideBySideSeparation
};
}
}
[BizImport(CC)]
public abstract bool Load(byte[] rom, int length, [In]NativeSettings settings);
[BizImport(CC)]
public abstract void HardReset();
}
}

View File

@ -18,26 +18,31 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
{
[CoreAttributes("Virtual Boyee", "Ryphecha", true, true, "0.9.44.1",
"https://mednafen.github.io/releases/", false)]
public class VirtualBoyee : IEmulator, IVideoProvider, ISoundProvider, IStatable,
IInputPollable, ISaveRam, ISettable<VirtualBoyee.Settings, VirtualBoyee.SyncSettings>
public class VirtualBoyee : WaterboxCore, ISettable<VirtualBoyee.Settings, VirtualBoyee.SyncSettings>
{
private PeRunner _exe;
private LibVirtualBoyee _boyee;
[CoreConstructor("VB")]
public VirtualBoyee(CoreComm comm, byte[] rom, Settings settings, SyncSettings syncSettings)
:base(comm, new Configuration
{
DefaultFpsNumerator = 20000000,
DefaultFpsDenominator = 397824,
DefaultWidth = 384,
DefaultHeight = 224,
MaxWidth = 1024,
MaxHeight = 1024,
MaxSamples = 8192,
SystemId = "VB"
})
{
ServiceProvider = new BasicServiceProvider(this);
CoreComm = comm;
_settings = settings ?? new Settings();
_syncSettings = syncSettings ?? new SyncSettings();
// TODO: the way settings work in this core, changing the non-sync ones will invalidate savestates
var nativeSettings = LibVirtualBoyee.NativeSettings.FromFrontendSettings(_settings, _syncSettings);
_exe = new PeRunner(new PeRunnerOptions
_boyee = PreInit<LibVirtualBoyee>(new PeRunnerOptions
{
Path = comm.CoreFileProvider.DllPath(),
Filename = "vb.wbx",
SbrkHeapSizeKB = 256,
SealedHeapSizeKB = 4 * 1024,
@ -45,76 +50,20 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
PlainHeapSizeKB = 256
});
_boyee = BizInvoker.GetInvoker<LibVirtualBoyee>(_exe, _exe);
if (!_boyee.Load(rom, rom.Length, nativeSettings))
{
throw new InvalidOperationException("Core rejected the rom");
}
_exe.Seal();
_inputCallback = InputCallbacks.Call;
InitMemoryDomains();
InitSaveram();
throw new InvalidOperationException("Core rejected the rom");
PostInit();
}
private bool _disposed = false;
public void Dispose()
{
if (!_disposed)
{
_exe.Dispose();
_exe = null;
_disposed = true;
}
}
public IEmulatorServiceProvider ServiceProvider { get; private set; }
public unsafe void FrameAdvance(IController controller, bool render, bool rendersound = true)
{
_boyee.SetInputCallback(InputCallbacks.Count > 0 ? _inputCallback : null);
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller)
{
if (controller.IsPressed("Power"))
_boyee.HardReset();
fixed (int* vp = _videoBuffer)
fixed (short* sp = _soundBuffer)
{
var spec = new LibVirtualBoyee.EmulateSpec
{
Pixels = (IntPtr)vp,
SoundBuf = (IntPtr)sp,
SoundBufMaxSize = _soundBuffer.Length / 2,
Buttons = GetButtons(controller)
};
_boyee.Emulate(spec);
BufferWidth = spec.DisplayRect.W;
BufferHeight = spec.DisplayRect.H;
_numSamples = spec.SoundBufSize;
Frame++;
IsLagFrame = spec.Lagged;
if (IsLagFrame)
LagCount++;
}
_boyee.HardReset();
return new LibVirtualBoyee.FrameInfo { Buttons = GetButtons(controller) };
}
public int Frame { get; private set; }
public void ResetCounters()
{
Frame = 0;
}
public string SystemId { get { return "VB"; } }
public bool DeterministicEmulation { get { return true; } }
public CoreComm CoreComm { get; private set; }
#region Controller
private LibVirtualBoyee.Buttons GetButtons(IController c)
@ -165,157 +114,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
.ToList()
};
public ControllerDefinition ControllerDefinition => VirtualBoyController;
#endregion
#region IVideoProvider
private int[] _videoBuffer = new int[1024 * 1024];
public int[] GetVideoBuffer()
{
return _videoBuffer;
}
public int VirtualWidth => BufferWidth;
public int VirtualHeight => BufferWidth;
public int BufferWidth { get; private set; } = 384;
public int BufferHeight { get; private set; } = 224;
public int VsyncNumerator { get; private set; } = 20000000;
public int VsyncDenominator { get; private set; } = 397824;
public int BackgroundColor => unchecked((int)0xff000000);
#endregion
#region ISoundProvider
private short[] _soundBuffer = new short[16384];
private int _numSamples;
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = _soundBuffer;
nsamp = _numSamples;
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
public void DiscardSamples()
{
}
public bool CanProvideAsync => false;
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
#endregion
public int LagCount { get; set; }
public bool IsLagFrame { get; set; }
private LibVirtualBoyee.InputCallback _inputCallback;
public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
#region IStatable
public bool BinarySaveStatesPreferred
{
get { return true; }
}
public void SaveStateText(TextWriter writer)
{
var temp = SaveStateBinary();
temp.SaveAsHexFast(writer);
// write extra copy of stuff we don't use
writer.WriteLine("Frame {0}", Frame);
}
public void LoadStateText(TextReader reader)
{
string hex = reader.ReadLine();
byte[] state = new byte[hex.Length / 2];
state.ReadFromHexFast(hex);
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
}
public void LoadStateBinary(BinaryReader reader)
{
_exe.LoadStateBinary(reader);
// other variables
Frame = reader.ReadInt32();
LagCount = reader.ReadInt32();
IsLagFrame = reader.ReadBoolean();
// any managed pointers that we sent to the core need to be resent now!
_boyee.SetInputCallback(null);
}
public void SaveStateBinary(BinaryWriter writer)
{
_exe.SaveStateBinary(writer);
// other variables
writer.Write(Frame);
writer.Write(LagCount);
writer.Write(IsLagFrame);
}
public byte[] SaveStateBinary()
{
var ms = new MemoryStream();
var bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
ms.Close();
return ms.ToArray();
}
#endregion
#region Memory Domains
private unsafe void InitMemoryDomains()
{
var domains = new List<MemoryDomain>();
var domainInfo = new[]
{
new { name = "WRAM", area = LibVirtualBoyee.MemoryArea.Wram, writable = true },
new { name = "CARTRAM", area = LibVirtualBoyee.MemoryArea.Sram, writable = true },
new { name = "ROM", area = LibVirtualBoyee.MemoryArea.Rom, writable = false }
};
foreach (var a in domainInfo)
{
IntPtr ptr = IntPtr.Zero;
int size = 0;
_boyee.GetMemoryArea(a.area, ref ptr, ref size);
if (ptr != IntPtr.Zero && size > 0)
{
domains.Add(new MemoryDomainIntPtrMonitor(a.name, MemoryDomain.Endian.Little,
ptr, size, a.writable, 4, _exe));
}
}
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(new MemoryDomainList(domains));
}
public override ControllerDefinition ControllerDefinition => VirtualBoyController;
#endregion
@ -450,57 +249,5 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
}
#endregion
#region ISaveRam
private const int SaveramSize = 65536;
private IntPtr _saveRamPtr;
private void InitSaveram()
{
int unused = 0;
_boyee.GetMemoryArea(LibVirtualBoyee.MemoryArea.Sram, ref _saveRamPtr, ref unused);
}
public unsafe bool SaveRamModified
{
get
{
using (_exe.EnterExit())
{
int* p = (int*)_saveRamPtr;
int* pend = p + SaveramSize / sizeof(int);
while (p < pend)
{
if (*p++ != 0)
return true;
}
}
return false;
}
}
public byte[] CloneSaveRam()
{
using (_exe.EnterExit())
{
var ret = new byte[SaveramSize];
Marshal.Copy(_saveRamPtr, ret, 0, SaveramSize);
return ret;
}
}
public void StoreSaveRam(byte[] data)
{
using (_exe.EnterExit())
{
if (data.Length != SaveramSize)
throw new InvalidOperationException("Saveram size mismatch");
Marshal.Copy(data, 0, _saveRamPtr, SaveramSize);
}
}
#endregion ISaveRam
}
}

View File

@ -174,7 +174,7 @@ namespace BizHawk.Emulation.Cores
{
foreach (var typ in assy.GetTypes())
{
if (typ.GetInterfaces().Contains(typeof(IEmulator)))
if (!typ.IsAbstract && typ.GetInterfaces().Contains(typeof(IEmulator)))
{
var coreattr = typ.GetCustomAttributes(typeof(CoreAttributes), false);
if (coreattr.Length != 1)

View File

@ -0,0 +1,194 @@
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
{
/// <summary>
/// pointer to the video buffer; set by frontend, filled by backend
/// </summary>
public IntPtr VideoBuffer;
/// <summary>
/// pointer to the sound buffer; set by frontend, filled by backend
/// </summary>
public IntPtr SoundBuffer;
/// <summary>
/// total number of cycles emulated this frame; set by backend
/// </summary>
public long Cycles;
/// <summary>
/// width of the output image; set by backend
/// </summary>
public int Width;
/// <summary>
/// height of the output image; set by backend
/// </summary>
public int Height;
/// <summary>
/// total number of sample pairs produced; set by backend
/// </summary>
public int Samples;
/// <summary>
/// true if controllers were not read; set by backend
/// </summary>
public int Lagged;
}
[Flags]
public enum MemoryDomainFlags: int
{
None = 0,
/// <summary>
/// if false, the domain MUST NOT be written to.
/// in some cases, a segmentation violation might occur
/// </summary>
Writable = 1,
/// <summary>
/// if true, this memory domain should be used in saveram.
/// can be ignored if the core provides its own saveram implementation
/// </summary>
Saverammable = 2,
/// <summary>
/// if true, domain is filled with ones (FF) by default, instead of zeros.
/// used in calculating SaveRamModified
/// </summary>
OneFilled = 4,
/// <summary>
/// desginates the default memory domain
/// </summary>
Primary = 8,
/// <summary>
/// if true, the most significant bytes are first in multibyte words
/// </summary>
YugeEndian = 16,
/// <summary>
/// native wordsize. only a hint
/// </summary>
WordSize1 = 32,
/// <summary>
/// native wordsize. only a hint
/// </summary>
WordSize2 = 64,
/// <summary>
/// native wordsize. only a hint
/// </summary>
WordSize4 = 128,
/// <summary>
/// native wordsize. only a hint
/// </summary>
WordSize8 = 256
}
[StructLayout(LayoutKind.Sequential)]
public struct MemoryArea
{
/// <summary>
/// pointer to the data in memory
/// </summary>
public IntPtr Data;
/// <summary>
/// null terminated strnig naming the memory domain
/// </summary>
public IntPtr Name;
/// <summary>
/// size of the domain
/// </summary>
public long Size;
/// <summary>
///
/// </summary>
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);
}
}

View File

@ -0,0 +1,339 @@
using BizHawk.Common;
using BizHawk.Common.BizInvoke;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Waterbox
{
public abstract class WaterboxCore : IEmulator, IVideoProvider, ISoundProvider, IStatable,
IInputPollable, ISaveRam
{
protected LibWaterboxCore _core;
protected PeRunner _exe;
protected LibWaterboxCore.MemoryArea[] _memoryAreas;
private LibWaterboxCore.EmptyCallback _inputCallback;
public class Configuration
{
public int MaxWidth;
public int MaxHeight;
public int DefaultWidth;
public int DefaultHeight;
public int DefaultFpsNumerator;
public int DefaultFpsDenominator;
public int MaxSamples;
public string SystemId;
}
protected WaterboxCore(CoreComm comm, Configuration c)
{
BufferWidth = c.DefaultWidth;
BufferHeight = c.DefaultHeight;
_videoBuffer = new int[c.MaxWidth * c.MaxHeight];
_soundBuffer = new short[c.MaxSamples * 2];
VsyncNumerator = c.DefaultFpsNumerator;
VsyncDenominator = c.DefaultFpsDenominator;
_serviceProvider = new BasicServiceProvider(this);
SystemId = c.SystemId;
CoreComm = comm;
_inputCallback = InputCallbacks.Call;
}
protected T PreInit<T>(PeRunnerOptions options)
where T : LibWaterboxCore
{
if (options.Path == null)
options.Path = CoreComm.CoreFileProvider.DllPath();
_exe = new PeRunner(options);
using (_exe.EnterExit())
{
var ret = BizInvoker.GetInvoker<T>(_exe, _exe);
_core = ret;
return ret;
}
}
protected void PostInit()
{
using (_exe.EnterExit())
{
var areas = new LibWaterboxCore.MemoryArea[256];
_core.GetMemoryAreas(areas);
_memoryAreas = areas.Where(a => a.Data != IntPtr.Zero && a.Size != 0)
.ToArray();
_saveramAreas = _memoryAreas.Where(a => (a.Flags & LibWaterboxCore.MemoryDomainFlags.Saverammable) != 0)
.ToArray();
_saveramSize = (int)_saveramAreas.Sum(a => a.Size);
var memoryDomains = _memoryAreas.Select(a => new LibWaterboxCore.WaterboxMemoryDomain(a, _exe));
var primaryIndex = _memoryAreas
.Select((a, i) => new { a, i })
.Single(a => (a.a.Flags & LibWaterboxCore.MemoryDomainFlags.Primary) != 0).i;
var mdl = new MemoryDomainList(memoryDomains.Cast<MemoryDomain>().ToList());
mdl.MainMemory = mdl[primaryIndex];
_serviceProvider.Register<IMemoryDomains>(mdl);
_exe.Seal();
}
}
#region ISaveRam
private LibWaterboxCore.MemoryArea[] _saveramAreas;
private int _saveramSize;
public unsafe bool SaveRamModified
{
get
{
if (_saveramSize == 0)
return false;
using (_exe.EnterExit())
{
foreach (var area in _saveramAreas)
{
int* p = (int*)area.Data;
int* pend = p + area.Size / sizeof(int);
int cmp = (area.Flags & LibWaterboxCore.MemoryDomainFlags.OneFilled) != 0 ? -1 : 0;
while (p < pend)
{
if (*p++ != cmp)
return true;
}
}
}
return false;
}
}
public byte[] CloneSaveRam()
{
if (_saveramSize == 0)
return null;
using (_exe.EnterExit())
{
var ret = new byte[_saveramSize];
var offs = 0;
foreach (var area in _saveramAreas)
{
Marshal.Copy(area.Data, ret, offs, (int)area.Size);
offs += (int)area.Size;
}
return ret;
}
}
public void StoreSaveRam(byte[] data)
{
using (_exe.EnterExit())
{
if (data.Length != _saveramSize)
throw new InvalidOperationException("Saveram size mismatch");
using (_exe.EnterExit())
{
var offs = 0;
foreach (var area in _saveramAreas)
{
Marshal.Copy(data, offs, area.Data, (int)area.Size);
offs += (int)area.Size;
}
}
}
}
#endregion ISaveRam
#region IEmulator
protected abstract LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller);
public unsafe void FrameAdvance(IController controller, bool render, bool rendersound = true)
{
using (_exe.EnterExit())
{
_core.SetInputCallback(InputCallbacks.Count > 0 ? _inputCallback : null);
fixed (int* vp = _videoBuffer)
fixed (short* sp = _soundBuffer)
{
var frame = FrameAdvancePrep(controller);
frame.VideoBuffer = (IntPtr)vp;
frame.SoundBuffer = (IntPtr)sp;
_core.FrameAdvance(frame);
Frame++;
if (IsLagFrame = frame.Lagged != 0)
LagCount++;
BufferWidth = frame.Width;
BufferHeight = frame.Height;
_numSamples = frame.Samples;
}
}
}
private bool _disposed = false;
public virtual void Dispose()
{
if (!_disposed)
{
_exe.Dispose();
_disposed = true;
}
}
public CoreComm CoreComm { get; }
public int Frame { get; private set; }
public int LagCount { get; set; }
public bool IsLagFrame { get; set; }
public void ResetCounters()
{
Frame = 0;
}
protected readonly BasicServiceProvider _serviceProvider;
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
public string SystemId { get; }
public bool DeterministicEmulation => true;
public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
public abstract ControllerDefinition ControllerDefinition { get; }
#endregion
#region IStatable
public bool BinarySaveStatesPreferred => true;
public void SaveStateText(TextWriter writer)
{
var temp = SaveStateBinary();
temp.SaveAsHexFast(writer);
}
public void LoadStateText(TextReader reader)
{
string hex = reader.ReadLine();
byte[] state = new byte[hex.Length / 2];
state.ReadFromHexFast(hex);
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
}
public void LoadStateBinary(BinaryReader reader)
{
_exe.LoadStateBinary(reader);
// other variables
Frame = reader.ReadInt32();
LagCount = reader.ReadInt32();
IsLagFrame = reader.ReadBoolean();
BufferWidth = reader.ReadInt32();
BufferHeight = reader.ReadInt32();
// reset pointers here!
_core.SetInputCallback(null);
}
public void SaveStateBinary(BinaryWriter writer)
{
_exe.SaveStateBinary(writer);
// other variables
writer.Write(Frame);
writer.Write(LagCount);
writer.Write(IsLagFrame);
writer.Write(BufferWidth);
writer.Write(BufferHeight);
}
public byte[] SaveStateBinary()
{
var ms = new MemoryStream();
var bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
ms.Close();
return ms.ToArray();
}
/// <summary>
/// called after the base core saves state. the core must save any other
/// variables that it needs to.
/// the default implementation does nothing
/// </summary>
/// <param name="writer"></param>
protected virtual void SaveStateBinaryInternal(BinaryWriter writer)
{
}
/// <summary>
/// called after the base core loads state. the core must load any other variables
/// that were in SaveStateBinaryInternal and reset any native pointers.
/// the default implementation does nothing
/// </summary>
/// <param name="reader"></param>
protected virtual void LoadStateBinaryInternal(BinaryReader reader)
{
}
#endregion
#region ISoundProvider
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = _soundBuffer;
nsamp = _numSamples;
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
public void DiscardSamples()
{
}
protected readonly short[] _soundBuffer;
protected int _numSamples;
public bool CanProvideAsync => false;
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
#endregion
#region IVideoProvider
public int[] GetVideoBuffer()
{
return _videoBuffer;
}
protected readonly int[] _videoBuffer;
public virtual int VirtualWidth => BufferWidth;
public virtual int VirtualHeight => BufferWidth;
public int BufferWidth { get; private set; }
public int BufferHeight { get; private set; }
public virtual int VsyncNumerator { get; protected set; }
public virtual int VsyncDenominator { get; protected set; }
public int BackgroundColor => unchecked((int)0xff000000);
#endregion
}
}

Binary file not shown.

BIN
output/dll/vb.wbx.gz Normal file

Binary file not shown.

View File

@ -0,0 +1,39 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
uint32_t* VideoBuffer;
int16_t* SoundBuffer;
int64_t Cycles;
int32_t Width;
int32_t Height;
int32_t Samples;
int32_t Lagged;
} FrameInfo;
typedef struct
{
void* Data;
const char* Name;
int64_t Size;
int32_t Flags;
} MemoryArea;
#define MEMORYAREA_FLAGS_WRITABLE 1
#define MEMORYAREA_FLAGS_SAVERAMMABLE 2
#define MEMORYAREA_FLAGS_ONEFILLED 4
#define MEMORYAREA_FLAGS_PRIMARY 8
#define MEMORYAREA_FLAGS_YUGEENDIAN 16
#define MEMORYAREA_FLAGS_WORDSIZE1 32
#define MEMORYAREA_FLAGS_WORDSIZE2 64
#define MEMORYAREA_FLAGS_WORDSIZE4 128
#define MEMORYAREA_FLAGS_WORDSIZE8 256
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,114 +1,124 @@
/******************************************************************************/
/* Mednafen Virtual Boy Emulation Module */
/******************************************************************************/
/* vb.h:
** Copyright (C) 2010-2016 Mednafen Team
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation, Inc.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <algorithm>
#include <memory>
#include <cassert>
#include <cstdio>
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
#define MDFN_FASTCALL
#define INLINE inline
#define MDFN_COLD
#define NO_INLINE
//#define MDFN_ASSUME_ALIGNED(p, align) ((decltype(p))__builtin_assume_aligned((p), (align)))
#define MDFN_ASSUME_ALIGNED(p, align) (p)
#define trio_snprintf snprintf
#define TRUE true
#define FALSE false
#ifndef __alignas_is_defined
#define alignas(p)
#endif
#include "endian.h"
#include "math_ops.h"
#include "blip/Blip_Buffer.h"
#include "v810/v810_fp_ops.h"
#include "v810/v810_cpu.h"
#include "git.h"
#include "vsu.h"
#include "vip.h"
#include "timer.h"
#include "input.h"
namespace MDFN_IEN_VB
{
enum
{
VB3DMODE_ANAGLYPH = 0,
VB3DMODE_CSCOPE = 1,
VB3DMODE_SIDEBYSIDE = 2,
VB3DMODE_OVERUNDER = 3,
VB3DMODE_VLI,
VB3DMODE_HLI
};
#define VB_MASTER_CLOCK 20000000.0
enum
{
VB_EVENT_VIP = 0,
VB_EVENT_TIMER,
VB_EVENT_INPUT,
// VB_EVENT_COMM
};
#define VB_EVENT_NONONO 0x7fffffff
void VB_SetEvent(const int type, const v810_timestamp_t next_timestamp);
#define VBIRQ_SOURCE_INPUT 0
#define VBIRQ_SOURCE_TIMER 1
#define VBIRQ_SOURCE_EXPANSION 2
#define VBIRQ_SOURCE_COMM 3
#define VBIRQ_SOURCE_VIP 4
void VBIRQ_Assert(int source, bool assert);
void VB_ExitLoop(void);
void ForceEventUpdates(const v810_timestamp_t timestamp);
uint8 MDFN_FASTCALL MemRead8(v810_timestamp_t &timestamp, uint32 A);
uint16 MDFN_FASTCALL MemRead16(v810_timestamp_t &timestamp, uint32 A);
void MDFN_FASTCALL MemWrite8(v810_timestamp_t &timestamp, uint32 A, uint8 V);
void MDFN_FASTCALL MemWrite16(v810_timestamp_t &timestamp, uint32 A, uint16 V);
extern int32 VB_InDebugPeek;
}
/******************************************************************************/
/* Mednafen Virtual Boy Emulation Module */
/******************************************************************************/
/* vb.h:
** Copyright (C) 2010-2016 Mednafen Team
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation, Inc.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <algorithm>
#include <memory>
#include <cassert>
#include <cstdio>
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
#define MDFN_FASTCALL
#define INLINE inline
#define MDFN_COLD
#define NO_INLINE
//#define MDFN_ASSUME_ALIGNED(p, align) ((decltype(p))__builtin_assume_aligned((p), (align)))
#define MDFN_ASSUME_ALIGNED(p, align) (p)
#define trio_snprintf snprintf
#define TRUE true
#define FALSE false
#ifndef __alignas_is_defined
#define alignas(p)
#endif
struct MyFrameInfo
{
uint32_t* VideoBuffer;
int16_t* SoundBuffer;
int64_t Cycles;
int32_t Width;
int32_t Height;
int32_t Samples;
int32_t Lagged;
int32_t Buttons;
};
#include "endian.h"
#include "math_ops.h"
#include "blip/Blip_Buffer.h"
#include "v810/v810_fp_ops.h"
#include "v810/v810_cpu.h"
#include "git.h"
#include "vsu.h"
#include "vip.h"
#include "timer.h"
#include "input.h"
namespace MDFN_IEN_VB
{
enum
{
VB3DMODE_ANAGLYPH = 0,
VB3DMODE_CSCOPE = 1,
VB3DMODE_SIDEBYSIDE = 2,
VB3DMODE_OVERUNDER = 3,
VB3DMODE_VLI,
VB3DMODE_HLI
};
#define VB_MASTER_CLOCK 20000000.0
enum
{
VB_EVENT_VIP = 0,
VB_EVENT_TIMER,
VB_EVENT_INPUT,
// VB_EVENT_COMM
};
#define VB_EVENT_NONONO 0x7fffffff
void VB_SetEvent(const int type, const v810_timestamp_t next_timestamp);
#define VBIRQ_SOURCE_INPUT 0
#define VBIRQ_SOURCE_TIMER 1
#define VBIRQ_SOURCE_EXPANSION 2
#define VBIRQ_SOURCE_COMM 3
#define VBIRQ_SOURCE_VIP 4
void VBIRQ_Assert(int source, bool assert);
void VB_ExitLoop(void);
void ForceEventUpdates(const v810_timestamp_t timestamp);
uint8 MDFN_FASTCALL MemRead8(v810_timestamp_t &timestamp, uint32 A);
uint16 MDFN_FASTCALL MemRead16(v810_timestamp_t &timestamp, uint32 A);
void MDFN_FASTCALL MemWrite8(v810_timestamp_t &timestamp, uint32 A, uint8 V);
void MDFN_FASTCALL MemWrite16(v810_timestamp_t &timestamp, uint32 A, uint16 V);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,82 +1,82 @@
/******************************************************************************/
/* Mednafen Virtual Boy Emulation Module */
/******************************************************************************/
/* vip.h:
** Copyright (C) 2010-2016 Mednafen Team
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation, Inc.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
namespace MDFN_IEN_VB
{
void VIP_Init(void) MDFN_COLD;
void VIP_Power(void) MDFN_COLD;
void VIP_SetInstantDisplayHack(bool) MDFN_COLD;
void VIP_SetAllowDrawSkip(bool) MDFN_COLD;
void VIP_Set3DMode(uint32 mode, bool reverse, uint32 prescale, uint32 sbs_separation) MDFN_COLD;
void VIP_SetParallaxDisable(bool disabled) MDFN_COLD;
void VIP_SetDefaultColor(uint32 default_color) MDFN_COLD;
void VIP_SetAnaglyphColors(uint32 lcolor, uint32 rcolor) MDFN_COLD; // R << 16, G << 8, B << 0
void VIP_SetLEDOnScale(float coeff) MDFN_COLD;
v810_timestamp_t MDFN_FASTCALL VIP_Update(const v810_timestamp_t timestamp);
void VIP_ResetTS(void);
void VIP_StartFrame(EmulateSpecStruct *espec);
MDFN_FASTCALL uint8 VIP_Read8(v810_timestamp_t &timestamp, uint32 A);
MDFN_FASTCALL uint16 VIP_Read16(v810_timestamp_t &timestamp, uint32 A);
MDFN_FASTCALL void VIP_Write8(v810_timestamp_t &timestamp, uint32 A, uint8 V);
MDFN_FASTCALL void VIP_Write16(v810_timestamp_t &timestamp, uint32 A, uint16 V);
enum
{
VIP_GSREG_IPENDING = 0, // Current pending interrupt(bits)
VIP_GSREG_IENABLE,
VIP_GSREG_DPCTRL,
VIP_GSREG_BRTA,
VIP_GSREG_BRTB,
VIP_GSREG_BRTC,
VIP_GSREG_REST,
VIP_GSREG_FRMCYC,
VIP_GSREG_XPCTRL,
VIP_GSREG_SPT0,
VIP_GSREG_SPT1,
VIP_GSREG_SPT2,
VIP_GSREG_SPT3,
VIP_GSREG_GPLT0,
VIP_GSREG_GPLT1,
VIP_GSREG_GPLT2,
VIP_GSREG_GPLT3,
VIP_GSREG_JPLT0,
VIP_GSREG_JPLT1,
VIP_GSREG_JPLT2,
VIP_GSREG_JPLT3,
VIP_GSREG_BKCOL,
};
uint32 VIP_GetRegister(const unsigned int id, char *special, const uint32 special_len);
void VIP_SetRegister(const unsigned int id, const uint32 value);
}
/******************************************************************************/
/* Mednafen Virtual Boy Emulation Module */
/******************************************************************************/
/* vip.h:
** Copyright (C) 2010-2016 Mednafen Team
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation, Inc.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
namespace MDFN_IEN_VB
{
void VIP_Init(void) MDFN_COLD;
void VIP_Power(void) MDFN_COLD;
void VIP_SetInstantDisplayHack(bool) MDFN_COLD;
void VIP_SetAllowDrawSkip(bool) MDFN_COLD;
void VIP_Set3DMode(uint32 mode, bool reverse, uint32 prescale, uint32 sbs_separation) MDFN_COLD;
void VIP_SetParallaxDisable(bool disabled) MDFN_COLD;
void VIP_SetDefaultColor(uint32 default_color) MDFN_COLD;
void VIP_SetAnaglyphColors(uint32 lcolor, uint32 rcolor) MDFN_COLD; // R << 16, G << 8, B << 0
void VIP_SetLEDOnScale(float coeff) MDFN_COLD;
v810_timestamp_t MDFN_FASTCALL VIP_Update(const v810_timestamp_t timestamp);
void VIP_ResetTS(void);
void VIP_StartFrame(MyFrameInfo* frame);
MDFN_FASTCALL uint8 VIP_Read8(v810_timestamp_t &timestamp, uint32 A);
MDFN_FASTCALL uint16 VIP_Read16(v810_timestamp_t &timestamp, uint32 A);
MDFN_FASTCALL void VIP_Write8(v810_timestamp_t &timestamp, uint32 A, uint8 V);
MDFN_FASTCALL void VIP_Write16(v810_timestamp_t &timestamp, uint32 A, uint16 V);
enum
{
VIP_GSREG_IPENDING = 0, // Current pending interrupt(bits)
VIP_GSREG_IENABLE,
VIP_GSREG_DPCTRL,
VIP_GSREG_BRTA,
VIP_GSREG_BRTB,
VIP_GSREG_BRTC,
VIP_GSREG_REST,
VIP_GSREG_FRMCYC,
VIP_GSREG_XPCTRL,
VIP_GSREG_SPT0,
VIP_GSREG_SPT1,
VIP_GSREG_SPT2,
VIP_GSREG_SPT3,
VIP_GSREG_GPLT0,
VIP_GSREG_GPLT1,
VIP_GSREG_GPLT2,
VIP_GSREG_GPLT3,
VIP_GSREG_JPLT0,
VIP_GSREG_JPLT1,
VIP_GSREG_JPLT2,
VIP_GSREG_JPLT3,
VIP_GSREG_BKCOL,
};
uint32 VIP_GetRegister(const unsigned int id, char *special, const uint32 special_len);
void VIP_SetRegister(const unsigned int id, const uint32 value);
}