Marching onward

This commit is contained in:
nattthebear 2020-05-22 18:01:48 -04:00
parent 2de9c1d117
commit 0f65351e13
10 changed files with 613 additions and 19 deletions

View File

@ -287,7 +287,7 @@ namespace BizHawk.BizInvoke
var paramTypes = paramInfos.Select(p => p.ParameterType).ToArray();
var nativeParamTypes = new List<Type>();
var returnType = baseMethod.ReturnType;
if (returnType != typeof(void) && !returnType.IsPrimitive)
if (returnType != typeof(void) && !returnType.IsPrimitive && !returnType.IsPointer)
{
throw new InvalidOperationException("Only primitive return types are supported");
}
@ -527,7 +527,7 @@ namespace BizHawk.BizInvoke
return typeof(IntPtr);
}
if (type.IsPrimitive || type.IsEnum)
if (type.IsPrimitive || type.IsEnum || type.IsPointer)
{
il.Emit(OpCodes.Ldarg, (short)idx);
return type;

View File

@ -0,0 +1,19 @@
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Waterbox;
namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
{
[Core("Terbo Grafix", "Mednafen Team", true, false, "", "", false)]
public class TerboGrafix : NymaCore, IRegionable
{
[CoreConstructor("PCE")]
public TerboGrafix(GameInfo game, byte[] rom, CoreComm comm)
: base(game, rom, comm, new Configuration
{
// TODO: This stuff isn't used so much
})
{
DoInit<LibNymaCore>(game, rom, "pce.wbx");
}
}
}

View File

@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
public sealed partial class PCEngine : IEmulator, ISaveRam, IInputPollable, IVideoLogicalOffsets, IRomInfo,
IDebuggable, ISettable<PCEngine.PCESettings, PCEngine.PCESyncSettings>, IDriveLight, ICodeDataLogger
{
[CoreConstructor(new[] { "PCE", "SGX" })]
// [CoreConstructor(new[] { "PCE", "SGX" })]
public PCEngine(GameInfo game, byte[] rom, object settings, object syncSettings)
{
switch (game.System)

View File

@ -0,0 +1,270 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using BizHawk.BizInvoke;
namespace BizHawk.Emulation.Cores.Waterbox
{
public abstract unsafe class LibNymaCore : LibWaterboxCore
{
[StructLayout(LayoutKind.Sequential)]
public class InitData
{
/// <summary>
/// Filename without extension. Used in autodetect
/// </summary>
public string FileNameBase;
/// <summary>
/// Just the extension. Used in autodetect. LOWERCASE PLEASE.
/// </summary>
public string FileNameExt;
/// <summary>
/// Full filename. This will be fopen()ed by the core
/// </summary>
public string FileNameFull;
}
[BizImport(CC, Compatibility = true)]
public abstract bool Init([In]InitData data);
public enum CommandType : int
{
NONE = 0x00,
RESET = 0x01,
POWER = 0x02,
INSERT_COIN = 0x07,
TOGGLE_DIP0 = 0x10,
TOGGLE_DIP1,
TOGGLE_DIP2,
TOGGLE_DIP3,
TOGGLE_DIP4,
TOGGLE_DIP5,
TOGGLE_DIP6,
TOGGLE_DIP7,
TOGGLE_DIP8,
TOGGLE_DIP9,
TOGGLE_DIP10,
TOGGLE_DIP11,
TOGGLE_DIP12,
TOGGLE_DIP13,
TOGGLE_DIP14,
TOGGLE_DIP15,
}
[StructLayout(LayoutKind.Sequential)]
public new class FrameInfo : LibWaterboxCore.FrameInfo
{
/// <summary>
/// true to skip video rendering
/// </summary>
public int SkipRendering;
/// <summary>
/// a single command to run at the start of this frame
/// </summary>
public CommandType Command;
/// <summary>
/// raw data for each input port, assumed to be MAX_PORTS * MAX_PORT_DATA long
/// </summary>
public byte* InputPortData;
}
[BizImport(CC)]
public abstract byte* GetLayerData();
/// <summary>
/// Gets a string array of valid layers to pass to SetLayers, or null if that method should not be called
/// TODO: This needs to be in NymaCore under a monitor lock
/// </summary>
public string[] GetLayerDataReal()
{
var p = GetLayerData();
if (p == null)
return null;
var ret = new List<string>();
var q = p;
while (true)
{
if (*q == 0)
{
if (q > p)
ret.Add(Marshal.PtrToStringAnsi((IntPtr)p));
else
break;
p = ++q;
}
q++;
}
return ret.ToArray();
}
/// <summary>
/// Set enabled layers (or is it disabled layers?). Only call if GetLayerDataReal() returned non null
/// </summary>
/// <param name="layers">bitmask in order defined by GetLayerDataReal</param>
[BizImport(CC)]
public abstract void SetLayers(ulong layers);
public enum InputType : byte
{
PADDING = 0, // n-bit, zero
BUTTON, // 1-bit
BUTTON_CAN_RAPID, // 1-bit
SWITCH, // ceil(log2(n))-bit
// Current switch position(default 0).
// Persistent, and bidirectional communication(can be modified driver side, and Mednafen core and emulation module side)
STATUS, // ceil(log2(n))-bit
// emulation module->driver communication
AXIS, // 16-bits; 0 through 65535; 32768 is centered position
POINTER_X, // mouse pointer, 16-bits, signed - in-screen/window range before scaling/offseting normalized coordinates: [0.0, 1.0)
POINTER_Y, // see: mouse_scale_x, mouse_scale_y, mouse_offs_x, mouse_offs_y
AXIS_REL, // mouse relative motion, 16-bits, signed
BYTE_SPECIAL,
RESET_BUTTON, // 1-bit
BUTTON_ANALOG, // 16-bits, 0 - 65535
RUMBLE, // 16-bits, lower 8 bits are weak rumble(0-255), next 8 bits are strong rumble(0-255), 0=no rumble, 255=max rumble. Somewhat subjective, too...
}
[Flags]
public enum AxisFlags: byte
{
NONE = 0,
// Denotes analog data that may need to be scaled to ensure a more squareish logical range(for emulated analog sticks)
SQLR = 0x01,
// Invert config order of the two components(neg,pos) of the axis
INVERT_CO = 0x02,
SETTINGS_UNDOC = 0x80,
}
[Flags]
public enum DeviceFlags: uint
{
NONE = 0,
KEYBOARD = 1
}
[StructLayout(LayoutKind.Sequential)]
public class NPortInfos
{
[MarshalAs(UnmanagedType.LPArray, SizeConst = 16)]
public NPortInfo[] Infos;
}
[StructLayout(LayoutKind.Sequential)]
public class NPortInfo
{
public string ShortName;
public string FullName;
public string DefaultDeviceShortName;
[StructLayout(LayoutKind.Sequential)]
public class NDeviceInfo
{
public string ShortName;
public string FullName;
public string Description;
public DeviceFlags Flags;
public uint ByteLength;
[StructLayout(LayoutKind.Sequential)]
public class NInput
{
public string SettingName;
public string Name;
public short ConfigOrder;
public ushort BitOffset;
public InputType Type;
public AxisFlags Flags;
public byte BitSize;
public byte _Padding;
[StructLayout(LayoutKind.Sequential)]
public class Button
{
public string ExcludeName;
}
[StructLayout(LayoutKind.Sequential)]
public class Axis
{
public string SettingsNameNeg;
public string SettingsNamePos;
public string NameNeg;
public string NamePos;
}
[StructLayout(LayoutKind.Sequential)]
public class Switch
{
[StructLayout(LayoutKind.Sequential)]
public class Position
{
public string SettingName;
public string Name;
public string Description;
}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public Position[] Positions;
public uint NumPositions;
public uint DefaultPosition;
}
[StructLayout(LayoutKind.Sequential)]
public class Status
{
public class State
{
public IntPtr ShortName;
public IntPtr Name;
public int Color; // (msb)0RGB(lsb), -1 for unused.
public int _Padding;
}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public State[] States;
public uint NumStates;
}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 400)]
public byte[] UnionData;
}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public NInput[] Inputs;
}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public NDeviceInfo[] Devices;
}
[BizImport(CC, Compatibility = true)]
public abstract NPortInfos GetInputDevices();
[BizImport(CC, Compatibility = true)]
public abstract void SetInputDevices(string[] devices);
public enum VideoSystem : int
{
NONE,
PAL,
PAL_M,
NTSC,
SECAM
}
[StructLayout(LayoutKind.Sequential)]
public class SystemInfo
{
public int MaxWidth;
public int MaxHeight;
public int NominalWidth;
public int NominalHeight;
public VideoSystem VideoSystem;
public int FpsFixed;
}
[BizImport(CC, Compatibility = true)]
public abstract SystemInfo GetSystemInfo();
}
}

View File

@ -0,0 +1,247 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Waterbox
{
public unsafe abstract class NymaCore : WaterboxCore
{
protected NymaCore(GameInfo game, byte[] rom, CoreComm comm, Configuration c)
: base(comm, c)
{
}
private LibNymaCore _nyma;
private ControllerAdapter _controllerAdapter;
private readonly byte[] _inputPortData = new byte[16 * 16];
protected T DoInit<T>(GameInfo game, byte[] rom, string filename)
where T : LibNymaCore
{
var t = PreInit<T>(new WaterboxOptions
{
// TODO cfg and stuff
Filename = filename,
SbrkHeapSizeKB = 1024 * 16,
SealedHeapSizeKB = 1024 * 16,
InvisibleHeapSizeKB = 1024 * 16,
PlainHeapSizeKB = 1024 * 16,
StartAddress = WaterboxHost.CanonicalStart,
SkipCoreConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck),
SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
});
_nyma = t;
using (_exe.EnterExit())
{
var fn = game.FilesystemSafeName();
_exe.AddReadonlyFile(rom, fn);
var didInit = _nyma.Init(new LibNymaCore.InitData
{
// TODO: Set these as some cores need them
FileNameBase = "",
FileNameExt = "",
FileNameFull = fn
});
if (!didInit)
throw new InvalidOperationException("Core rejected the rom!");
_exe.RemoveReadonlyFile(fn);
var info = _nyma.GetSystemInfo();
_videoBuffer = new int[info.MaxWidth * info.MaxHeight];
BufferWidth = info.NominalWidth;
BufferHeight = info.NominalHeight;
switch (info.VideoSystem)
{
// TODO: There seriously isn't any region besides these?
case LibNymaCore.VideoSystem.PAL:
case LibNymaCore.VideoSystem.SECAM:
Region = DisplayType.PAL;
break;
case LibNymaCore.VideoSystem.PAL_M:
Region = DisplayType.Dendy; // sort of...
break;
default:
Region = DisplayType.NTSC;
break;
}
VsyncNumerator = info.FpsFixed;
VsyncDenominator = 1 << 24;
_controllerAdapter = new ControllerAdapter(_nyma.GetInputDevices().Infos, new string[0]);
_nyma.SetInputDevices(_controllerAdapter.Devices);
PostInit();
}
return t;
}
// todo: bleh
private GCHandle _frameAdvanceInputLock;
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
{
_controllerAdapter.SetBits(controller, _inputPortData);
_frameAdvanceInputLock = GCHandle.Alloc(_inputPortData, GCHandleType.Pinned);
var ret = new LibNymaCore.FrameInfo
{
SkipRendering = render ? 0 : 1,
Command = LibNymaCore.CommandType.NONE,
InputPortData = (byte*)_frameAdvanceInputLock.AddrOfPinnedObject()
};
return ret;
}
protected override void FrameAdvancePost()
{
_frameAdvanceInputLock.Free();
}
private static T ControllerData<T>(byte[] data)
{
fixed(byte *p = data)
{
return (T)Marshal.PtrToStructure((IntPtr)p, typeof(T));
}
}
protected delegate void ControllerThunk(IController c, byte[] b);
protected class ControllerAdapter
{
public string[] Devices { get; }
public ControllerDefinition Definition { get; }
public ControllerAdapter(LibNymaCore.NPortInfo[] portInfos, string[] devices)
{
var ret = new ControllerDefinition
{
Name = "TODO"
};
var finalDevices = new List<string>();
for (int i = 0, devByteStart = 0; i < portInfos.Length && portInfos[i].ShortName != null; i++)
{
var port = portInfos[i];
var devName = i < devices.Length ? devices[i] : port.DefaultDeviceShortName;
finalDevices.Add(devName);
var dev = port.Devices.SingleOrDefault(d => d.ShortName == devName);
var category = port.FullName + " - " + dev.FullName;
foreach (var input in dev.Inputs.OrderBy(i => i.ConfigOrder))
{
var bitSize = (int)input.BitSize;
var bitOffset = (int)input.BitOffset;
var byteStart = devByteStart + bitOffset / 8;
bitOffset %= 8;
var name = input.Name;
switch (input.Type)
{
case LibNymaCore.InputType.PADDING:
{
break;
}
case LibNymaCore.InputType.BUTTON:
case LibNymaCore.InputType.BUTTON_CAN_RAPID:
{
var data = ControllerData<LibNymaCore.NPortInfo.NDeviceInfo.NInput.Button>(input.UnionData);
// TODO: Wire up data.ExcludeName
ret.BoolButtons.Add(name);
_thunks.Add((c, b) =>
{
if (c.IsPressed(name))
b[byteStart] |= (byte)(1 << bitOffset);
});
break;
}
case LibNymaCore.InputType.SWITCH:
{
var data = ControllerData<LibNymaCore.NPortInfo.NDeviceInfo.NInput.Switch>(input.UnionData);
// TODO: Possibly bulebutton for 2 states?
ret.AxisControls.Add(name);
ret.AxisRanges.Add(new ControllerDefinition.AxisRange(
0, (int)data.DefaultPosition, (int)data.NumPositions - 1));
_thunks.Add((c, b) =>
{
var val = (int)Math.Round(c.AxisValue(name));
b[byteStart] |= (byte)(1 << bitOffset);
});
break;
}
case LibNymaCore.InputType.AXIS:
{
var data = ControllerData<LibNymaCore.NPortInfo.NDeviceInfo.NInput.Axis>(input.UnionData);
ret.AxisControls.Add(name);
ret.AxisRanges.Add(new ControllerDefinition.AxisRange(
0, 0x8000, 0xffff, (input.Flags & LibNymaCore.AxisFlags.INVERT_CO) != 0
));
_thunks.Add((c, b) =>
{
var val = (ushort)Math.Round(c.AxisValue(name));
b[byteStart] = (byte)val;
b[byteStart + 1] = (byte)(val >> 8);
});
break;
}
case LibNymaCore.InputType.AXIS_REL:
{
var data = ControllerData<LibNymaCore.NPortInfo.NDeviceInfo.NInput.Axis>(input.UnionData);
ret.AxisControls.Add(name);
ret.AxisRanges.Add(new ControllerDefinition.AxisRange(
-0x8000, 0, 0x7fff, (input.Flags & LibNymaCore.AxisFlags.INVERT_CO) != 0
));
_thunks.Add((c, b) =>
{
var val = (short)Math.Round(c.AxisValue(name));
b[byteStart] = (byte)val;
b[byteStart + 1] = (byte)(val >> 8);
});
break;
}
case LibNymaCore.InputType.POINTER_X:
{
throw new Exception("TODO: Axis ranges are ints????");
// ret.AxisControls.Add(name);
// ret.AxisRanges.Add(new ControllerDefinition.AxisRange(0, 0.5, 1));
// break;
}
case LibNymaCore.InputType.POINTER_Y:
{
throw new Exception("TODO: Axis ranges are ints????");
// ret.AxisControls.Add(name);
// ret.AxisRanges.Add(new ControllerDefinition.AxisRange(0, 0.5, 1, true));
// break;
}
default:
throw new NotImplementedException($"Unimplemented button type {input.Type}");
}
ret.CategoryLabels[name] = category;
}
devByteStart += (int)dev.ByteLength;
}
Definition = ret;
Devices = finalDevices.ToArray();
}
private readonly List<Action<IController, byte[]>> _thunks = new List<Action<IController, byte[]>>();
public void SetBits(IController src, byte[] dest)
{
Array.Clear(dest, 0, dest.Length);
foreach (var t in _thunks)
t(src, dest);
}
}
public DisplayType Region { get; protected set; }
}
}

View File

@ -329,7 +329,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
return _videoBuffer;
}
protected readonly int[] _videoBuffer;
protected int[] _videoBuffer;
public virtual int VirtualWidth => BufferWidth;
public virtual int VirtualHeight => BufferHeight;
public int BufferWidth { get; protected set; }

View File

@ -54,7 +54,7 @@ namespace Mednafen
va_start(argp, format);
vfprintf(t == MDFN_NOTICE_ERROR ? stderr : stdout, format, argp);
}
void MDFND_MidSync(EmulateSpecStruct *espec, const unsigned flags)
void MDFN_MidSync(EmulateSpecStruct *espec, const unsigned flags)
{}
bool MDFNSS_StateAction(StateMem *sm, const unsigned load, const bool data_only, const SFORMAT *sf, const char *sname, const bool optional) noexcept
@ -100,6 +100,8 @@ namespace Mednafen
{}
void MDFNMP_ApplyPeriodicCheats(void)
{}
std::vector<SUBCHEAT> SubCheats[8];
bool SubCheatsOn;
// player.h
void Player_Init(int tsongs, const std::string &album, const std::string &artist, const std::string &copyright, const std::vector<std::string> &snames, bool override_gi)

View File

@ -113,6 +113,33 @@ ECL_EXPORT void FrameAdvance(MyFrameInfo& frame)
}
}
struct SystemInfo
{
int32_t MaxWidth;
int32_t MaxHeight;
int32_t NominalWidth;
int32_t NominalHeight;
int32_t VideoSystem;
int32_t FpsFixed;
};
SystemInfo SI;
ECL_EXPORT SystemInfo* GetSystemInfo()
{
SI.MaxWidth = Game->fb_width;
SI.MaxHeight = Game->fb_height;
SI.NominalWidth = Game->nominal_width;
SI.NominalHeight = Game->nominal_height;
SI.VideoSystem = Game->VideoSystem;
SI.FpsFixed = Game->fps;
return &SI;
}
ECL_EXPORT const char* GetLayerData()
{
return Game->LayerNames;
}
ECL_EXPORT void SetLayers(uint64_t layers)
{
Game->SetLayerEnableMask(layers);
@ -127,20 +154,20 @@ ECL_EXPORT void SetInputCallback(void (*cb)())
// same information as PortInfo, but easier to marshal
struct NPortInfo
{
const char *ShortName;
const char *FullName;
const char *DefaultDeviceShortName;
const char* ShortName;
const char* FullName;
const char* DefaultDeviceShortName;
struct NDeviceInfo
{
const char *ShortName;
const char *FullName;
const char *Description;
const char* ShortName;
const char* FullName;
const char* Description;
uint32_t Flags;
uint32_t ByteLength;
struct NInput
{
const char *SettingName;
const char *Name;
const char* SettingName;
const char* Name;
int16_t ConfigOrder;
uint16_t BitOffset;
InputDeviceInputType Type; // uint8_t
@ -166,7 +193,7 @@ struct NPortInfo
const char* SettingName;
const char* Name;
const char* Description;
}* Positions;
} Positions[16];
uint32_t NumPositions;
uint32_t DefaultPosition;
} Switch;
@ -178,9 +205,10 @@ struct NPortInfo
const char* Name;
int32_t Color; // (msb)0RGB(lsb), -1 for unused.
int32_t _Padding;
}* States;
} States[16];
uint32_t NumStates;
} Status;
uint8_t _Padding2[400];
};
} Inputs[256];
} Devices[32];
@ -226,8 +254,7 @@ ECL_EXPORT NPortInfo* GetInputDevices()
case IDIT_SWITCH:
c.Switch.NumPositions = z.Switch.NumPos;
c.Switch.DefaultPosition = z.Switch.DefPos;
c.Switch.Positions = (decltype(c.Switch.Positions))calloc(z.Switch.NumPos, sizeof(*c.Switch.Positions));
for (unsigned i = 0; i < z.Switch.NumPos; i++)
for (unsigned i = 0; i < 16 && i < z.Switch.NumPos; i++)
{
c.Switch.Positions[i].SettingName = z.Switch.Pos[i].SettingName;
c.Switch.Positions[i].Name = z.Switch.Pos[i].Name;
@ -236,8 +263,7 @@ ECL_EXPORT NPortInfo* GetInputDevices()
break;
case IDIT_STATUS:
c.Status.NumStates = z.Status.NumStates;
c.Status.States = (decltype(c.Status.States))calloc(z.Status.NumStates, sizeof(*c.Status.States));
for (unsigned i = 0; i < z.Status.NumStates; i++)
for (unsigned i = 0; i < 16 && i < z.Status.NumStates; i++)
{
c.Status.States[i].ShortName = z.Status.States[i].ShortName;
c.Status.States[i].Name = z.Status.States[i].Name;

13
waterbox/nyma/pce.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "mednafen/src/types.h"
#include "nyma.h"
#include <emulibc.h>
#include "mednafen/src/pce/pce.h"
using namespace MDFN_IEN_PCE;
extern Mednafen::MDFNGI EmulatedPCE;
void SetupMDFNGameInfo()
{
Mednafen::MDFNGameInfo = &EmulatedPCE;
}

17
waterbox/nyma/pce.mak Normal file
View File

@ -0,0 +1,17 @@
include common.mak
# $(call cppdir,hw_video/huc6270)
# $(call cppdir,hw_sound/pce_psg)
SRCS += \
$(filter-out %debug.cpp,$(call cppdir,pce)) \
$(filter-out %CDAFReader_SF.cpp,$(call cppdir,cdrom)) \
$(call cdir,tremor) \
$(call cdir,mpcdec) \
mednafen/src/mthreading/MThreading_POSIX.cpp \
$(call cppdir,hw_sound/pce_psg) \
$(call cppdir,hw_misc/arcade_card) \
$(call cppdir,hw_video/huc6270) \
pce.cpp
include ../common.mak