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:
parent
02242e8997
commit
6f60eb5efc
File diff suppressed because it is too large
Load Diff
|
@ -1276,6 +1276,7 @@
|
||||||
<Compile Include="Libretro\LibretroCore_Description.cs" />
|
<Compile Include="Libretro\LibretroCore_Description.cs" />
|
||||||
<Compile Include="Libretro\LibretroCore_InputCallbacks.cs" />
|
<Compile Include="Libretro\LibretroCore_InputCallbacks.cs" />
|
||||||
<Compile Include="Waterbox\Heap.cs" />
|
<Compile Include="Waterbox\Heap.cs" />
|
||||||
|
<Compile Include="Waterbox\LibWaterboxCore.cs" />
|
||||||
<Compile Include="Waterbox\MapHeap.cs" />
|
<Compile Include="Waterbox\MapHeap.cs" />
|
||||||
<Compile Include="Waterbox\MemoryBlock.cs" />
|
<Compile Include="Waterbox\MemoryBlock.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
@ -1292,6 +1293,7 @@
|
||||||
<Compile Include="Waterbox\PeRunner.cs" />
|
<Compile Include="Waterbox\PeRunner.cs" />
|
||||||
<Compile Include="Waterbox\PeWrapper.cs" />
|
<Compile Include="Waterbox\PeWrapper.cs" />
|
||||||
<Compile Include="Waterbox\Swappable.cs" />
|
<Compile Include="Waterbox\Swappable.cs" />
|
||||||
|
<Compile Include="Waterbox\WaterboxCore.cs" />
|
||||||
<Compile Include="Waterbox\WaterboxUtils.cs" />
|
<Compile Include="Waterbox\WaterboxUtils.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -1,148 +1,85 @@
|
||||||
using BizHawk.Common.BizInvoke;
|
using BizHawk.Common.BizInvoke;
|
||||||
using System;
|
using BizHawk.Emulation.Cores.Waterbox;
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
using System.Drawing;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Drawing;
|
||||||
using System.Runtime.InteropServices;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading.Tasks;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
|
|
||||||
{
|
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
|
||||||
public abstract class LibVirtualBoyee
|
{
|
||||||
{
|
public abstract class LibVirtualBoyee : LibWaterboxCore
|
||||||
private const CallingConvention CC = CallingConvention.Cdecl;
|
{
|
||||||
|
public enum Buttons : int
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
{
|
||||||
public struct Rect
|
Up = 0x200,
|
||||||
{
|
Down = 0x100,
|
||||||
public int X;
|
Left = 0x80,
|
||||||
public int Y;
|
Right = 0x40,
|
||||||
public int W;
|
Select = 0x800,
|
||||||
public int H;
|
Start = 0x400,
|
||||||
}
|
B = 0x2,
|
||||||
|
A = 0x1,
|
||||||
[StructLayout(LayoutKind.Explicit)] // TODO: find out why Sequential is sometimes ignored on the native layout
|
Up_R = 0x10,
|
||||||
public class EmulateSpec
|
Down_R = 0x200,
|
||||||
{
|
Left_R = 0x1000,
|
||||||
// Pitch(32-bit) must be equal to width and >= the "fb_width" specified in the MDFNGI struct for the emulated system.
|
Right_R = 0x2000,
|
||||||
// Height must be >= to the "fb_height" specified in the MDFNGI struct for the emulated system.
|
L = 0x8,
|
||||||
// The framebuffer pointed to by surface->pixels is written to by the system emulation code.
|
R = 0x4
|
||||||
[FieldOffset(0)]
|
}
|
||||||
public IntPtr Pixels;
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
// Pointer to sound buffer, set by the driver code, that the emulation code should render sound to.
|
public new class FrameInfo : LibWaterboxCore.FrameInfo
|
||||||
// Guaranteed to be at least 500ms in length, but emulation code really shouldn't exceed 40ms or so. Additionally, if emulation code
|
{
|
||||||
// generates >= 100ms,
|
public Buttons Buttons;
|
||||||
// DEPRECATED: Emulation code may set this pointer to a sound buffer internal to the emulation module.
|
}
|
||||||
[FieldOffset(8)]
|
|
||||||
public IntPtr SoundBuf;
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public class NativeSettings
|
||||||
// Number of cycles that this frame consumed, using MDFNGI::MasterClock as a time base.
|
{
|
||||||
// Set by emulation code.
|
public int InstantReadHack;
|
||||||
[FieldOffset(16)]
|
public int DisableParallax;
|
||||||
public long MasterCycles;
|
public int ThreeDeeMode;
|
||||||
|
public int SwapViews;
|
||||||
// Set by the system emulation code every frame, to denote the horizontal and vertical offsets of the image, and the size
|
public int AnaglyphPreset;
|
||||||
// of the image. If the emulated system sets the elements of LineWidths, then the width(w) of this structure
|
public int AnaglyphCustomLeftColor;
|
||||||
// is ignored while drawing the image.
|
public int AnaglyphCustomRightColor;
|
||||||
[FieldOffset(24)]
|
public int NonAnaglyphColor;
|
||||||
public Rect DisplayRect;
|
public int LedOnScale;
|
||||||
|
public int InterlacePrescale;
|
||||||
// Maximum size of the sound buffer, in frames. Set by the driver code.
|
public int SideBySideSeparation;
|
||||||
[FieldOffset(40)]
|
|
||||||
public int SoundBufMaxSize;
|
private static int ConvertColor(Color c)
|
||||||
|
{
|
||||||
// Number of frames currently in internal sound buffer. Set by the system emulation code, to be read by the driver code.
|
return c.ToArgb();
|
||||||
[FieldOffset(44)]
|
}
|
||||||
public int SoundBufSize;
|
|
||||||
|
public static NativeSettings FromFrontendSettings(VirtualBoyee.Settings s, VirtualBoyee.SyncSettings ss)
|
||||||
// 0 UDLR SelectStartBA UDLR(right dpad) LtrigRtrig 13
|
{
|
||||||
[FieldOffset(48)]
|
return new NativeSettings
|
||||||
public Buttons Buttons;
|
{
|
||||||
|
InstantReadHack = ss.InstantReadHack ? 1 : 0,
|
||||||
[FieldOffset(52)]
|
DisableParallax = ss.DisableParallax ? 1 : 0,
|
||||||
public bool Lagged;
|
ThreeDeeMode = (int)s.ThreeDeeMode,
|
||||||
}
|
SwapViews = s.SwapViews ? 1 : 0,
|
||||||
|
AnaglyphPreset = (int)s.AnaglyphPreset,
|
||||||
public enum MemoryArea : int
|
AnaglyphCustomLeftColor = ConvertColor(s.AnaglyphCustomLeftColor),
|
||||||
{
|
AnaglyphCustomRightColor = ConvertColor(s.AnaglyphCustomRightColor),
|
||||||
Wram, Sram, Rom
|
NonAnaglyphColor = ConvertColor(s.NonAnaglyphColor),
|
||||||
}
|
LedOnScale = s.LedOnScale,
|
||||||
|
InterlacePrescale = s.InterlacePrescale,
|
||||||
public enum Buttons : int
|
SideBySideSeparation = s.SideBySideSeparation
|
||||||
{
|
};
|
||||||
Up = 0x200,
|
}
|
||||||
Down = 0x100,
|
}
|
||||||
Left = 0x80,
|
|
||||||
Right = 0x40,
|
|
||||||
Select = 0x800,
|
[BizImport(CC)]
|
||||||
Start = 0x400,
|
public abstract bool Load(byte[] rom, int length, [In]NativeSettings settings);
|
||||||
B = 0x2,
|
|
||||||
A = 0x1,
|
[BizImport(CC)]
|
||||||
Up_R = 0x10,
|
public abstract void HardReset();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -18,26 +18,31 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
|
||||||
{
|
{
|
||||||
[CoreAttributes("Virtual Boyee", "Ryphecha", true, true, "0.9.44.1",
|
[CoreAttributes("Virtual Boyee", "Ryphecha", true, true, "0.9.44.1",
|
||||||
"https://mednafen.github.io/releases/", false)]
|
"https://mednafen.github.io/releases/", false)]
|
||||||
public class VirtualBoyee : IEmulator, IVideoProvider, ISoundProvider, IStatable,
|
public class VirtualBoyee : WaterboxCore, ISettable<VirtualBoyee.Settings, VirtualBoyee.SyncSettings>
|
||||||
IInputPollable, ISaveRam, ISettable<VirtualBoyee.Settings, VirtualBoyee.SyncSettings>
|
|
||||||
{
|
{
|
||||||
private PeRunner _exe;
|
|
||||||
private LibVirtualBoyee _boyee;
|
private LibVirtualBoyee _boyee;
|
||||||
|
|
||||||
[CoreConstructor("VB")]
|
[CoreConstructor("VB")]
|
||||||
public VirtualBoyee(CoreComm comm, byte[] rom, Settings settings, SyncSettings syncSettings)
|
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();
|
_settings = settings ?? new Settings();
|
||||||
_syncSettings = syncSettings ?? new SyncSettings();
|
_syncSettings = syncSettings ?? new SyncSettings();
|
||||||
// TODO: the way settings work in this core, changing the non-sync ones will invalidate savestates
|
// TODO: the way settings work in this core, changing the non-sync ones will invalidate savestates
|
||||||
var nativeSettings = LibVirtualBoyee.NativeSettings.FromFrontendSettings(_settings, _syncSettings);
|
var nativeSettings = LibVirtualBoyee.NativeSettings.FromFrontendSettings(_settings, _syncSettings);
|
||||||
|
|
||||||
_exe = new PeRunner(new PeRunnerOptions
|
_boyee = PreInit<LibVirtualBoyee>(new PeRunnerOptions
|
||||||
{
|
{
|
||||||
Path = comm.CoreFileProvider.DllPath(),
|
|
||||||
Filename = "vb.wbx",
|
Filename = "vb.wbx",
|
||||||
SbrkHeapSizeKB = 256,
|
SbrkHeapSizeKB = 256,
|
||||||
SealedHeapSizeKB = 4 * 1024,
|
SealedHeapSizeKB = 4 * 1024,
|
||||||
|
@ -45,76 +50,20 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
|
||||||
PlainHeapSizeKB = 256
|
PlainHeapSizeKB = 256
|
||||||
});
|
});
|
||||||
|
|
||||||
_boyee = BizInvoker.GetInvoker<LibVirtualBoyee>(_exe, _exe);
|
|
||||||
|
|
||||||
if (!_boyee.Load(rom, rom.Length, nativeSettings))
|
if (!_boyee.Load(rom, rom.Length, nativeSettings))
|
||||||
{
|
throw new InvalidOperationException("Core rejected the rom");
|
||||||
throw new InvalidOperationException("Core rejected the rom");
|
|
||||||
}
|
PostInit();
|
||||||
|
|
||||||
_exe.Seal();
|
|
||||||
|
|
||||||
_inputCallback = InputCallbacks.Call;
|
|
||||||
InitMemoryDomains();
|
|
||||||
InitSaveram();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _disposed = false;
|
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller)
|
||||||
|
{
|
||||||
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);
|
|
||||||
|
|
||||||
if (controller.IsPressed("Power"))
|
if (controller.IsPressed("Power"))
|
||||||
_boyee.HardReset();
|
_boyee.HardReset();
|
||||||
|
|
||||||
fixed (int* vp = _videoBuffer)
|
return new LibVirtualBoyee.FrameInfo { Buttons = GetButtons(controller) };
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
#region Controller
|
||||||
|
|
||||||
private LibVirtualBoyee.Buttons GetButtons(IController c)
|
private LibVirtualBoyee.Buttons GetButtons(IController c)
|
||||||
|
@ -165,157 +114,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
|
||||||
.ToList()
|
.ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
public ControllerDefinition ControllerDefinition => VirtualBoyController;
|
public override 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -450,57 +249,5 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,7 +174,7 @@ namespace BizHawk.Emulation.Cores
|
||||||
{
|
{
|
||||||
foreach (var typ in assy.GetTypes())
|
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);
|
var coreattr = typ.GetCustomAttributes(typeof(CoreAttributes), false);
|
||||||
if (coreattr.Length != 1)
|
if (coreattr.Length != 1)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
Binary file not shown.
|
@ -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
|
1598
waterbox/vb/vb.cpp
1598
waterbox/vb/vb.cpp
File diff suppressed because it is too large
Load Diff
238
waterbox/vb/vb.h
238
waterbox/vb/vb.h
|
@ -1,114 +1,124 @@
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* Mednafen Virtual Boy Emulation Module */
|
/* Mednafen Virtual Boy Emulation Module */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* vb.h:
|
/* vb.h:
|
||||||
** Copyright (C) 2010-2016 Mednafen Team
|
** Copyright (C) 2010-2016 Mednafen Team
|
||||||
**
|
**
|
||||||
** This program is free software; you can redistribute it and/or
|
** This program is free software; you can redistribute it and/or
|
||||||
** modify it under the terms of the GNU General Public License
|
** modify it under the terms of the GNU General Public License
|
||||||
** as published by the Free Software Foundation; either version 2
|
** as published by the Free Software Foundation; either version 2
|
||||||
** of the License, or (at your option) any later version.
|
** of the License, or (at your option) any later version.
|
||||||
**
|
**
|
||||||
** This program is distributed in the hope that it will be useful,
|
** This program is distributed in the hope that it will be useful,
|
||||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
** GNU General Public License for more details.
|
** GNU General Public License for more details.
|
||||||
**
|
**
|
||||||
** You should have received a copy of the GNU General Public License
|
** 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.,
|
** along with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
typedef uint8_t uint8;
|
typedef uint8_t uint8;
|
||||||
typedef uint16_t uint16;
|
typedef uint16_t uint16;
|
||||||
typedef uint32_t uint32;
|
typedef uint32_t uint32;
|
||||||
typedef uint64_t uint64;
|
typedef uint64_t uint64;
|
||||||
typedef int8_t int8;
|
typedef int8_t int8;
|
||||||
typedef int16_t int16;
|
typedef int16_t int16;
|
||||||
typedef int32_t int32;
|
typedef int32_t int32;
|
||||||
typedef int64_t int64;
|
typedef int64_t int64;
|
||||||
|
|
||||||
#define MDFN_FASTCALL
|
#define MDFN_FASTCALL
|
||||||
#define INLINE inline
|
#define INLINE inline
|
||||||
#define MDFN_COLD
|
#define MDFN_COLD
|
||||||
#define NO_INLINE
|
#define NO_INLINE
|
||||||
//#define MDFN_ASSUME_ALIGNED(p, align) ((decltype(p))__builtin_assume_aligned((p), (align)))
|
//#define MDFN_ASSUME_ALIGNED(p, align) ((decltype(p))__builtin_assume_aligned((p), (align)))
|
||||||
#define MDFN_ASSUME_ALIGNED(p, align) (p)
|
#define MDFN_ASSUME_ALIGNED(p, align) (p)
|
||||||
#define trio_snprintf snprintf
|
#define trio_snprintf snprintf
|
||||||
#define TRUE true
|
#define TRUE true
|
||||||
#define FALSE false
|
#define FALSE false
|
||||||
#ifndef __alignas_is_defined
|
#ifndef __alignas_is_defined
|
||||||
#define alignas(p)
|
#define alignas(p)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "endian.h"
|
struct MyFrameInfo
|
||||||
#include "math_ops.h"
|
{
|
||||||
#include "blip/Blip_Buffer.h"
|
uint32_t* VideoBuffer;
|
||||||
#include "v810/v810_fp_ops.h"
|
int16_t* SoundBuffer;
|
||||||
#include "v810/v810_cpu.h"
|
int64_t Cycles;
|
||||||
|
int32_t Width;
|
||||||
#include "git.h"
|
int32_t Height;
|
||||||
|
int32_t Samples;
|
||||||
#include "vsu.h"
|
int32_t Lagged;
|
||||||
#include "vip.h"
|
int32_t Buttons;
|
||||||
#include "timer.h"
|
};
|
||||||
#include "input.h"
|
|
||||||
|
#include "endian.h"
|
||||||
|
#include "math_ops.h"
|
||||||
namespace MDFN_IEN_VB
|
#include "blip/Blip_Buffer.h"
|
||||||
{
|
#include "v810/v810_fp_ops.h"
|
||||||
|
#include "v810/v810_cpu.h"
|
||||||
enum
|
|
||||||
{
|
#include "git.h"
|
||||||
VB3DMODE_ANAGLYPH = 0,
|
|
||||||
VB3DMODE_CSCOPE = 1,
|
#include "vsu.h"
|
||||||
VB3DMODE_SIDEBYSIDE = 2,
|
#include "vip.h"
|
||||||
VB3DMODE_OVERUNDER = 3,
|
#include "timer.h"
|
||||||
VB3DMODE_VLI,
|
#include "input.h"
|
||||||
VB3DMODE_HLI
|
|
||||||
};
|
|
||||||
|
namespace MDFN_IEN_VB
|
||||||
#define VB_MASTER_CLOCK 20000000.0
|
{
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
VB_EVENT_VIP = 0,
|
VB3DMODE_ANAGLYPH = 0,
|
||||||
VB_EVENT_TIMER,
|
VB3DMODE_CSCOPE = 1,
|
||||||
VB_EVENT_INPUT,
|
VB3DMODE_SIDEBYSIDE = 2,
|
||||||
// VB_EVENT_COMM
|
VB3DMODE_OVERUNDER = 3,
|
||||||
};
|
VB3DMODE_VLI,
|
||||||
|
VB3DMODE_HLI
|
||||||
#define VB_EVENT_NONONO 0x7fffffff
|
};
|
||||||
|
|
||||||
void VB_SetEvent(const int type, const v810_timestamp_t next_timestamp);
|
#define VB_MASTER_CLOCK 20000000.0
|
||||||
|
|
||||||
#define VBIRQ_SOURCE_INPUT 0
|
enum
|
||||||
#define VBIRQ_SOURCE_TIMER 1
|
{
|
||||||
#define VBIRQ_SOURCE_EXPANSION 2
|
VB_EVENT_VIP = 0,
|
||||||
#define VBIRQ_SOURCE_COMM 3
|
VB_EVENT_TIMER,
|
||||||
#define VBIRQ_SOURCE_VIP 4
|
VB_EVENT_INPUT,
|
||||||
|
// VB_EVENT_COMM
|
||||||
void VBIRQ_Assert(int source, bool assert);
|
};
|
||||||
|
|
||||||
void VB_ExitLoop(void);
|
#define VB_EVENT_NONONO 0x7fffffff
|
||||||
|
|
||||||
void ForceEventUpdates(const v810_timestamp_t timestamp);
|
void VB_SetEvent(const int type, const v810_timestamp_t next_timestamp);
|
||||||
|
|
||||||
uint8 MDFN_FASTCALL MemRead8(v810_timestamp_t ×tamp, uint32 A);
|
#define VBIRQ_SOURCE_INPUT 0
|
||||||
uint16 MDFN_FASTCALL MemRead16(v810_timestamp_t ×tamp, uint32 A);
|
#define VBIRQ_SOURCE_TIMER 1
|
||||||
|
#define VBIRQ_SOURCE_EXPANSION 2
|
||||||
void MDFN_FASTCALL MemWrite8(v810_timestamp_t ×tamp, uint32 A, uint8 V);
|
#define VBIRQ_SOURCE_COMM 3
|
||||||
void MDFN_FASTCALL MemWrite16(v810_timestamp_t ×tamp, uint32 A, uint16 V);
|
#define VBIRQ_SOURCE_VIP 4
|
||||||
|
|
||||||
extern int32 VB_InDebugPeek;
|
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 ×tamp, uint32 A);
|
||||||
|
uint16 MDFN_FASTCALL MemRead16(v810_timestamp_t ×tamp, uint32 A);
|
||||||
|
|
||||||
|
void MDFN_FASTCALL MemWrite8(v810_timestamp_t ×tamp, uint32 A, uint8 V);
|
||||||
|
void MDFN_FASTCALL MemWrite16(v810_timestamp_t ×tamp, uint32 A, uint16 V);
|
||||||
|
}
|
||||||
|
|
2787
waterbox/vb/vip.cpp
2787
waterbox/vb/vip.cpp
File diff suppressed because it is too large
Load Diff
|
@ -1,82 +1,82 @@
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* Mednafen Virtual Boy Emulation Module */
|
/* Mednafen Virtual Boy Emulation Module */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* vip.h:
|
/* vip.h:
|
||||||
** Copyright (C) 2010-2016 Mednafen Team
|
** Copyright (C) 2010-2016 Mednafen Team
|
||||||
**
|
**
|
||||||
** This program is free software; you can redistribute it and/or
|
** This program is free software; you can redistribute it and/or
|
||||||
** modify it under the terms of the GNU General Public License
|
** modify it under the terms of the GNU General Public License
|
||||||
** as published by the Free Software Foundation; either version 2
|
** as published by the Free Software Foundation; either version 2
|
||||||
** of the License, or (at your option) any later version.
|
** of the License, or (at your option) any later version.
|
||||||
**
|
**
|
||||||
** This program is distributed in the hope that it will be useful,
|
** This program is distributed in the hope that it will be useful,
|
||||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
** GNU General Public License for more details.
|
** GNU General Public License for more details.
|
||||||
**
|
**
|
||||||
** You should have received a copy of the GNU General Public License
|
** 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.,
|
** along with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace MDFN_IEN_VB
|
namespace MDFN_IEN_VB
|
||||||
{
|
{
|
||||||
void VIP_Init(void) MDFN_COLD;
|
void VIP_Init(void) MDFN_COLD;
|
||||||
void VIP_Power(void) MDFN_COLD;
|
void VIP_Power(void) MDFN_COLD;
|
||||||
|
|
||||||
void VIP_SetInstantDisplayHack(bool) MDFN_COLD;
|
void VIP_SetInstantDisplayHack(bool) MDFN_COLD;
|
||||||
void VIP_SetAllowDrawSkip(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_Set3DMode(uint32 mode, bool reverse, uint32 prescale, uint32 sbs_separation) MDFN_COLD;
|
||||||
void VIP_SetParallaxDisable(bool disabled) MDFN_COLD;
|
void VIP_SetParallaxDisable(bool disabled) MDFN_COLD;
|
||||||
void VIP_SetDefaultColor(uint32 default_color) 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_SetAnaglyphColors(uint32 lcolor, uint32 rcolor) MDFN_COLD; // R << 16, G << 8, B << 0
|
||||||
void VIP_SetLEDOnScale(float coeff) MDFN_COLD;
|
void VIP_SetLEDOnScale(float coeff) MDFN_COLD;
|
||||||
|
|
||||||
v810_timestamp_t MDFN_FASTCALL VIP_Update(const v810_timestamp_t timestamp);
|
v810_timestamp_t MDFN_FASTCALL VIP_Update(const v810_timestamp_t timestamp);
|
||||||
void VIP_ResetTS(void);
|
void VIP_ResetTS(void);
|
||||||
|
|
||||||
void VIP_StartFrame(EmulateSpecStruct *espec);
|
void VIP_StartFrame(MyFrameInfo* frame);
|
||||||
|
|
||||||
MDFN_FASTCALL uint8 VIP_Read8(v810_timestamp_t ×tamp, uint32 A);
|
MDFN_FASTCALL uint8 VIP_Read8(v810_timestamp_t ×tamp, uint32 A);
|
||||||
MDFN_FASTCALL uint16 VIP_Read16(v810_timestamp_t ×tamp, uint32 A);
|
MDFN_FASTCALL uint16 VIP_Read16(v810_timestamp_t ×tamp, uint32 A);
|
||||||
|
|
||||||
MDFN_FASTCALL void VIP_Write8(v810_timestamp_t ×tamp, uint32 A, uint8 V);
|
MDFN_FASTCALL void VIP_Write8(v810_timestamp_t ×tamp, uint32 A, uint8 V);
|
||||||
MDFN_FASTCALL void VIP_Write16(v810_timestamp_t ×tamp, uint32 A, uint16 V);
|
MDFN_FASTCALL void VIP_Write16(v810_timestamp_t ×tamp, uint32 A, uint16 V);
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
VIP_GSREG_IPENDING = 0, // Current pending interrupt(bits)
|
VIP_GSREG_IPENDING = 0, // Current pending interrupt(bits)
|
||||||
VIP_GSREG_IENABLE,
|
VIP_GSREG_IENABLE,
|
||||||
|
|
||||||
VIP_GSREG_DPCTRL,
|
VIP_GSREG_DPCTRL,
|
||||||
|
|
||||||
VIP_GSREG_BRTA,
|
VIP_GSREG_BRTA,
|
||||||
VIP_GSREG_BRTB,
|
VIP_GSREG_BRTB,
|
||||||
VIP_GSREG_BRTC,
|
VIP_GSREG_BRTC,
|
||||||
VIP_GSREG_REST,
|
VIP_GSREG_REST,
|
||||||
VIP_GSREG_FRMCYC,
|
VIP_GSREG_FRMCYC,
|
||||||
VIP_GSREG_XPCTRL,
|
VIP_GSREG_XPCTRL,
|
||||||
|
|
||||||
VIP_GSREG_SPT0,
|
VIP_GSREG_SPT0,
|
||||||
VIP_GSREG_SPT1,
|
VIP_GSREG_SPT1,
|
||||||
VIP_GSREG_SPT2,
|
VIP_GSREG_SPT2,
|
||||||
VIP_GSREG_SPT3,
|
VIP_GSREG_SPT3,
|
||||||
|
|
||||||
VIP_GSREG_GPLT0,
|
VIP_GSREG_GPLT0,
|
||||||
VIP_GSREG_GPLT1,
|
VIP_GSREG_GPLT1,
|
||||||
VIP_GSREG_GPLT2,
|
VIP_GSREG_GPLT2,
|
||||||
VIP_GSREG_GPLT3,
|
VIP_GSREG_GPLT3,
|
||||||
|
|
||||||
VIP_GSREG_JPLT0,
|
VIP_GSREG_JPLT0,
|
||||||
VIP_GSREG_JPLT1,
|
VIP_GSREG_JPLT1,
|
||||||
VIP_GSREG_JPLT2,
|
VIP_GSREG_JPLT2,
|
||||||
VIP_GSREG_JPLT3,
|
VIP_GSREG_JPLT3,
|
||||||
|
|
||||||
VIP_GSREG_BKCOL,
|
VIP_GSREG_BKCOL,
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32 VIP_GetRegister(const unsigned int id, char *special, const uint32 special_len);
|
uint32 VIP_GetRegister(const unsigned int id, char *special, const uint32 special_len);
|
||||||
void VIP_SetRegister(const unsigned int id, const uint32 value);
|
void VIP_SetRegister(const unsigned int id, const uint32 value);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue