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
|
@ -420,9 +420,9 @@ namespace BizHawk.Common.BizInvoke
|
|||
if (type.IsArray)
|
||||
{
|
||||
var et = type.GetElementType();
|
||||
if (!et.IsPrimitive && !et.IsEnum)
|
||||
if (!et.IsValueType)
|
||||
{
|
||||
throw new InvalidOperationException("Only arrays of primitive or enum types are supported!");
|
||||
throw new InvalidOperationException("Only arrays of value types are supported!");
|
||||
}
|
||||
|
||||
// these two cases aren't too hard to add
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using BizHawk.Common.BizInvoke;
|
||||
using BizHawk.Emulation.Cores.Waterbox;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
@ -9,67 +10,8 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
|
||||
{
|
||||
public abstract class LibVirtualBoyee
|
||||
public abstract class LibVirtualBoyee : LibWaterboxCore
|
||||
{
|
||||
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,
|
||||
|
@ -88,6 +30,12 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
|
|||
R = 0x4
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public new class FrameInfo : LibWaterboxCore.FrameInfo
|
||||
{
|
||||
public Buttons Buttons;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class NativeSettings
|
||||
{
|
||||
|
@ -127,22 +75,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
|
|||
}
|
||||
}
|
||||
|
||||
[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",
|
||||
"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,75 +50,19 @@ 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");
|
||||
|
||||
PostInit();
|
||||
}
|
||||
|
||||
_exe.Seal();
|
||||
|
||||
_inputCallback = InputCallbacks.Call;
|
||||
InitMemoryDomains();
|
||||
InitSaveram();
|
||||
}
|
||||
|
||||
private bool _disposed = false;
|
||||
|
||||
public void Dispose()
|
||||
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller)
|
||||
{
|
||||
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"))
|
||||
_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++;
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -20,7 +20,8 @@
|
|||
*/
|
||||
|
||||
#include "vb.h"
|
||||
#include <emulibc.h>
|
||||
#include "../emulibc/emulibc.h"
|
||||
#include "../emulibc/waterboxcore.h"
|
||||
#define EXPORT extern "C" ECL_EXPORT
|
||||
|
||||
namespace MDFN_IEN_VB
|
||||
|
@ -65,8 +66,6 @@ static const uint32 AnaglyphPreset_Colors[][2] =
|
|||
{0xFFFF00, 0x0000FF},
|
||||
};
|
||||
|
||||
int32 VB_InDebugPeek;
|
||||
|
||||
static uint32 VB3DMode;
|
||||
|
||||
static uint8 *WRAM = NULL;
|
||||
|
@ -558,8 +557,6 @@ EXPORT int Load(const uint8 *rom, int length, const NativeSettings* settings)
|
|||
const uint64 rom_size = length;
|
||||
V810_Emu_Mode cpu_mode = V810_EMU_MODE_ACCURATE;
|
||||
|
||||
VB_InDebugPeek = 0;
|
||||
|
||||
if (rom_size != round_up_pow2(rom_size))
|
||||
{
|
||||
return 0;
|
||||
|
@ -734,49 +731,44 @@ EXPORT int Load(const uint8 *rom, int length, const NativeSettings* settings)
|
|||
return 1;
|
||||
}
|
||||
|
||||
EXPORT void GetMemoryArea(int which, void **ptr, int *size)
|
||||
EXPORT void GetMemoryAreas(MemoryArea* m)
|
||||
{
|
||||
switch (which)
|
||||
{
|
||||
case 0:
|
||||
*ptr = WRAM;
|
||||
*size = 65536;
|
||||
break;
|
||||
case 1:
|
||||
*ptr = GPRAM;
|
||||
*size = GPRAM_Mask + 1;
|
||||
break;
|
||||
case 2:
|
||||
*ptr = GPROM;
|
||||
*size = GPROM_Mask + 1;
|
||||
break;
|
||||
default:
|
||||
*ptr = nullptr;
|
||||
*size = 0;
|
||||
break;
|
||||
}
|
||||
m[0].Data = WRAM;
|
||||
m[0].Name = "WRAM";
|
||||
m[0].Size = 65536;
|
||||
m[0].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY | MEMORYAREA_FLAGS_WORDSIZE4;
|
||||
|
||||
m[1].Data = GPRAM;
|
||||
m[1].Name = "CARTRAM";
|
||||
m[1].Size = GPRAM_Mask + 1;
|
||||
m[1].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_SAVERAMMABLE | MEMORYAREA_FLAGS_WORDSIZE4;
|
||||
|
||||
m[2].Data = GPROM;
|
||||
m[2].Name = "ROM";
|
||||
m[2].Size = GPROM_Mask + 1;
|
||||
m[2].Flags = MEMORYAREA_FLAGS_WORDSIZE4;
|
||||
}
|
||||
|
||||
EXPORT void Emulate(EmulateSpecStruct *espec)
|
||||
EXPORT void FrameAdvance(MyFrameInfo* frame)
|
||||
{
|
||||
v810_timestamp_t v810_timestamp;
|
||||
lagged = true;
|
||||
|
||||
VBINPUT_Frame(&espec->Buttons);
|
||||
VBINPUT_Frame(&frame->Buttons);
|
||||
|
||||
VIP_StartFrame(espec);
|
||||
VIP_StartFrame(frame);
|
||||
|
||||
v810_timestamp = VB_V810->Run(EventHandler);
|
||||
|
||||
FixNonEvents();
|
||||
ForceEventUpdates(v810_timestamp);
|
||||
|
||||
espec->SoundBufSize = VB_VSU->EndFrame((v810_timestamp + VSU_CycleFix) >> 2, espec->SoundBuf, espec->SoundBufMaxSize);
|
||||
frame->Samples = VB_VSU->EndFrame((v810_timestamp + VSU_CycleFix) >> 2, frame->SoundBuffer, 8192);
|
||||
|
||||
VSU_CycleFix = (v810_timestamp + VSU_CycleFix) & 3;
|
||||
|
||||
espec->MasterCycles = v810_timestamp;
|
||||
espec->Lagged = lagged;
|
||||
frame->Cycles = v810_timestamp;
|
||||
frame->Lagged = lagged;
|
||||
|
||||
TIMER_ResetTS();
|
||||
VBINPUT_ResetTS();
|
||||
|
|
|
@ -51,6 +51,18 @@ typedef int64_t int64;
|
|||
#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"
|
||||
|
@ -109,6 +121,4 @@ 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);
|
||||
|
||||
extern int32 VB_InDebugPeek;
|
||||
}
|
||||
|
|
|
@ -864,7 +864,7 @@ MDFN_FASTCALL void VIP_Write16(int32 ×tamp, uint32 A, uint16 V)
|
|||
static MDFN_Surface real_surface;
|
||||
static MDFN_Surface *surface;
|
||||
|
||||
void VIP_StartFrame(EmulateSpecStruct *espec)
|
||||
void VIP_StartFrame(MyFrameInfo* frame)
|
||||
{
|
||||
// puts("Start frame");
|
||||
|
||||
|
@ -876,40 +876,37 @@ void VIP_StartFrame(EmulateSpecStruct *espec)
|
|||
VidSettingsDirty = false;
|
||||
}
|
||||
|
||||
espec->DisplayRect.x = 0;
|
||||
espec->DisplayRect.y = 0;
|
||||
|
||||
switch (VB3DMode)
|
||||
{
|
||||
default:
|
||||
espec->DisplayRect.w = 384;
|
||||
espec->DisplayRect.h = 224;
|
||||
frame->Width = 384;
|
||||
frame->Height = 224;
|
||||
break;
|
||||
|
||||
case VB3DMODE_VLI:
|
||||
espec->DisplayRect.w = 768 * VBPrescale;
|
||||
espec->DisplayRect.h = 224;
|
||||
frame->Width = 768 * VBPrescale;
|
||||
frame->Height = 224;
|
||||
break;
|
||||
|
||||
case VB3DMODE_HLI:
|
||||
espec->DisplayRect.w = 384;
|
||||
espec->DisplayRect.h = 448 * VBPrescale;
|
||||
frame->Width = 384;
|
||||
frame->Height = 448 * VBPrescale;
|
||||
break;
|
||||
|
||||
case VB3DMODE_CSCOPE:
|
||||
espec->DisplayRect.w = 512;
|
||||
espec->DisplayRect.h = 384;
|
||||
frame->Width = 512;
|
||||
frame->Height = 384;
|
||||
break;
|
||||
|
||||
case VB3DMODE_SIDEBYSIDE:
|
||||
espec->DisplayRect.w = 768 + VBSBS_Separation;
|
||||
espec->DisplayRect.h = 224;
|
||||
frame->Width = 768 + VBSBS_Separation;
|
||||
frame->Height = 224;
|
||||
break;
|
||||
}
|
||||
|
||||
surface = &real_surface;
|
||||
real_surface.pixels = espec->pixels;
|
||||
real_surface.pitch32 = espec->DisplayRect.w;
|
||||
real_surface.pixels = frame->VideoBuffer;
|
||||
real_surface.pitch32 = frame->Width;
|
||||
}
|
||||
|
||||
void VIP_ResetTS(void)
|
||||
|
|
|
@ -37,7 +37,7 @@ 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);
|
||||
void VIP_StartFrame(MyFrameInfo* frame);
|
||||
|
||||
MDFN_FASTCALL uint8 VIP_Read8(v810_timestamp_t ×tamp, uint32 A);
|
||||
MDFN_FASTCALL uint16 VIP_Read16(v810_timestamp_t ×tamp, uint32 A);
|
||||
|
|
Loading…
Reference in New Issue