Make ngp core use WaterboxCore infrastructure

This commit is contained in:
nattthebear 2017-06-17 10:47:10 -04:00
parent f538ad31fe
commit 3e731ae0b3
8 changed files with 593 additions and 811 deletions

View File

@ -56,7 +56,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
PostInit();
}
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller)
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
{
if (controller.IsPressed("Power"))
_boyee.HardReset();

View File

@ -1,58 +1,43 @@
using BizHawk.Common.BizInvoke;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Consoles.SNK
{
public abstract class LibNeoGeoPort
{
private const CallingConvention CC = CallingConvention.Cdecl;
[UnmanagedFunctionPointer(CC)]
public delegate void InputCallback();
[StructLayout(LayoutKind.Sequential)]
public class EmulateSpec
{
public IntPtr Pixels;
public IntPtr SoundBuff;
public long MasterCycles;
public long FrontendTime;
public int SoundBufMaxSize;
public int SoundBufSize;
public int SkipRendering;
public int Buttons;
public int Lagged;
}
public enum Language
{
Japanese, English
}
[UnmanagedFunctionPointer(CC)]
public delegate void SaveRamCallback(IntPtr data, int length);
[BizImport(CC)]
public abstract bool LoadSystem(byte[] rom, int romlength, Language language);
[BizImport(CC)]
public abstract void SetLayers(int enable); // 1, 2, 4 bg,fg,sprites
[BizImport(CC)]
public abstract void FrameAdvance([In, Out]EmulateSpec espec);
[BizImport(CC)]
public abstract void HardReset();
[BizImport(CC)]
public abstract void SetInputCallback(InputCallback callback);
[BizImport(CC)]
public abstract void GetMemoryArea(int which, ref IntPtr ptr, ref int size, ref bool writable);
[BizImport(CC)]
public abstract void SetCommsCallbacks(IntPtr readcb, IntPtr pollcb, IntPtr writecb);
[BizImport(CC)]
public abstract bool HasSaveRam();
[BizImport(CC)]
public abstract bool PutSaveRam(byte[] data, int length);
[BizImport(CC)]
public abstract void GetSaveRam(SaveRamCallback callback);
}
}
using BizHawk.Common.BizInvoke;
using BizHawk.Emulation.Cores.Waterbox;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Consoles.SNK
{
public abstract class LibNeoGeoPort : LibWaterboxCore
{
[StructLayout(LayoutKind.Sequential)]
public new class FrameInfo : LibWaterboxCore.FrameInfo
{
public long FrontendTime;
public int SkipRendering;
public int Buttons;
}
public enum Language
{
Japanese, English
}
[UnmanagedFunctionPointer(CC)]
public delegate void SaveRamCallback(IntPtr data, int length);
[BizImport(CC)]
public abstract bool LoadSystem(byte[] rom, int romlength, Language language);
[BizImport(CC)]
public abstract void SetLayers(int enable); // 1, 2, 4 bg,fg,sprites
[BizImport(CC)]
public abstract void HardReset();
[BizImport(CC)]
public abstract void SetCommsCallbacks(IntPtr readcb, IntPtr pollcb, IntPtr writecb);
[BizImport(CC)]
public abstract bool HasSaveRam();
[BizImport(CC)]
public abstract bool PutSaveRam(byte[] data, int length);
[BizImport(CC)]
public abstract void GetSaveRam(SaveRamCallback callback);
}
}

View File

@ -1,404 +1,240 @@
using BizHawk.Common;
using BizHawk.Common.BizInvoke;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Waterbox;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Consoles.SNK
{
[CoreAttributes("NeoPop", "Thomas Klausner", true, false, "0.9.44.1",
"https://mednafen.github.io/releases/", false)]
public class NeoGeoPort : IEmulator, IVideoProvider, ISoundProvider, IStatable, IInputPollable,
ISettable<object, NeoGeoPort.SyncSettings>, ISaveRam
{
private PeRunner _exe;
internal LibNeoGeoPort _neopop;
private long _clockTime;
private int _clockDen;
[CoreConstructor("NGP")]
public NeoGeoPort(CoreComm comm, byte[] rom, SyncSettings syncSettings, bool deterministic)
: this(comm, rom, syncSettings, deterministic, PeRunner.CanonicalStart)
{
}
internal NeoGeoPort(CoreComm comm, byte[] rom, SyncSettings syncSettings, bool deterministic, ulong startAddress)
{
if (rom.Length > 4 * 1024 * 1024)
throw new InvalidOperationException("ROM too big!");
ServiceProvider = new BasicServiceProvider(this);
CoreComm = comm;
_syncSettings = syncSettings ?? new SyncSettings();
_exe = new PeRunner(new PeRunnerOptions
{
Path = comm.CoreFileProvider.DllPath(),
Filename = "ngp.wbx",
SbrkHeapSizeKB = 256,
SealedHeapSizeKB = 5 * 1024, // must be a bit larger than the ROM size
InvisibleHeapSizeKB = 4,
PlainHeapSizeKB = 5 * 1024, // must be a bit larger than the ROM size
StartAddress = startAddress
});
_neopop = BizInvoker.GetInvoker<LibNeoGeoPort>(_exe, _exe);
if (!_neopop.LoadSystem(rom, rom.Length, _syncSettings.Language))
{
throw new InvalidOperationException("Core rejected the rom");
}
_exe.Seal();
_inputCallback = InputCallbacks.Call;
InitMemoryDomains();
DeterministicEmulation = deterministic || !_syncSettings.UseRealTime;
_clockTime = (long)((_syncSettings.InitialTime - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds);
}
public unsafe void FrameAdvance(IController controller, bool render, bool rendersound = true)
{
_clockDen += VsyncDenominator;
if (_clockDen >= VsyncNumerator)
{
_clockDen -= VsyncNumerator;
_clockTime++;
}
long clockTime = DeterministicEmulation ? _clockTime : (long)((_syncSettings.InitialTime - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds);
_neopop.SetInputCallback(InputCallbacks.Count > 0 ? _inputCallback : null);
if (controller.IsPressed("Power"))
_neopop.HardReset();
fixed (int* vp = _videoBuffer)
fixed (short* sp = _soundBuffer)
{
var spec = new LibNeoGeoPort.EmulateSpec
{
Pixels = (IntPtr)vp,
SoundBuff = (IntPtr)sp,
SoundBufMaxSize = _soundBuffer.Length / 2,
Buttons = GetButtons(controller),
SkipRendering = render ? 0 : 1,
FrontendTime = clockTime
};
_neopop.FrameAdvance(spec);
_numSamples = spec.SoundBufSize;
Frame++;
IsLagFrame = spec.Lagged != 0;
if (IsLagFrame)
LagCount++;
}
}
private bool _disposed = false;
public void Dispose()
{
if (!_disposed)
{
_exe.Dispose();
_exe = null;
_disposed = true;
}
}
public int Frame { get; private set; }
public int LagCount { get; set; }
public bool IsLagFrame { get; set; }
public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
private LibNeoGeoPort.InputCallback _inputCallback;
public void ResetCounters()
{
Frame = 0;
}
public IEmulatorServiceProvider ServiceProvider { get; private set; }
public string SystemId { get { return "NGP"; } }
public bool DeterministicEmulation { get; private set; }
public CoreComm CoreComm { get; }
#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!
_neopop.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 Controller
private static int GetButtons(IController c)
{
var ret = 0;
var val = 1;
foreach (var s in CoreButtons)
{
if (c.IsPressed(s))
ret |= val;
val <<= 1;
}
return ret;
}
private static readonly string[] CoreButtons =
{
"Up", "Down", "Left", "Right", "A", "B", "Option"
};
private static readonly Dictionary<string, int> ButtonOrdinals = new Dictionary<string, int>
{
["Up"] = 1,
["Down"] = 2,
["Left"] = 3,
["Right"] = 4,
["B"] = 9,
["A"] = 10,
["R"] = 11,
["L"] = 12,
["Option"] = 13
};
private static readonly ControllerDefinition NeoGeoPortableController = new ControllerDefinition
{
Name = "NeoGeo Portable Controller",
BoolButtons = CoreButtons
.OrderBy(b => ButtonOrdinals[b])
.Concat(new[] { "Power" })
.ToList()
};
public ControllerDefinition ControllerDefinition => NeoGeoPortableController;
#endregion
#region IVideoProvider
private int[] _videoBuffer = new int[160 * 152];
public int[] GetVideoBuffer()
{
return _videoBuffer;
}
public int VirtualWidth => 160;
public int VirtualHeight => 152;
public int BufferWidth => 160;
public int BufferHeight => 152;
public int VsyncNumerator { get; private set; } = 6144000;
public int VsyncDenominator { get; private set; } = 515 * 198;
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
#region ISettable
private SyncSettings _syncSettings;
public class SyncSettings
{
[DisplayName("Language")]
[Description("Language of the system. Only affects some games.")]
[DefaultValue(LibNeoGeoPort.Language.Japanese)]
public LibNeoGeoPort.Language Language { get; set; }
[DisplayName("Initial Time")]
[Description("Initial time of emulation. Only relevant when UseRealTime is false.")]
[DefaultValue(typeof(DateTime), "2010-01-01")]
public DateTime InitialTime { get; set; }
[DisplayName("Use RealTime")]
[Description("If true, RTC clock will be based off of real time instead of emulated time. Ignored (set to false) when recording a movie.")]
[DefaultValue(false)]
public bool UseRealTime { get; set; }
public SyncSettings Clone()
{
return (SyncSettings)MemberwiseClone();
}
public static bool NeedsReboot(SyncSettings x, SyncSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
public SyncSettings()
{
SettingsUtil.SetDefaultValues(this);
}
}
public object GetSettings()
{
return null;
}
public SyncSettings GetSyncSettings()
{
return _syncSettings.Clone();
}
public bool PutSettings(object o)
{
return false;
}
public bool PutSyncSettings(SyncSettings o)
{
var ret = SyncSettings.NeedsReboot(_syncSettings, o);
_syncSettings = o;
return ret;
}
#endregion
#region Memory Domains
private unsafe void InitMemoryDomains()
{
var domains = new List<MemoryDomain>();
var domainNames = new[] { "RAM", "ROM", "ORIGINAL ROM" };
foreach (var a in domainNames.Select((s, i) => new { s, i }))
{
IntPtr ptr = IntPtr.Zero;
int size = 0;
bool writable = false;
_neopop.GetMemoryArea(a.i, ref ptr, ref size, ref writable);
if (ptr != IntPtr.Zero && size > 0)
{
domains.Add(new MemoryDomainIntPtrMonitor(a.s, MemoryDomain.Endian.Little,
ptr, size, writable, 4, _exe));
}
}
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(new MemoryDomainList(domains));
}
#endregion
#region ISaveram
public bool SaveRamModified => _neopop.HasSaveRam();
public byte[] CloneSaveRam()
{
byte[] ret = null;
_neopop.GetSaveRam((data, size) =>
{
ret = new byte[size];
Marshal.Copy(data, ret, 0, size);
});
return ret;
}
public void StoreSaveRam(byte[] data)
{
if (!_neopop.PutSaveRam(data, data.Length))
throw new InvalidOperationException("Core rejected the saveram");
}
#endregion
}
}
using BizHawk.Common;
using BizHawk.Common.BizInvoke;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Waterbox;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Consoles.SNK
{
[CoreAttributes("NeoPop", "Thomas Klausner", true, false, "0.9.44.1",
"https://mednafen.github.io/releases/", false)]
public class NeoGeoPort : WaterboxCore,
ISaveRam, // NGP provides its own saveram interface
ISettable<object, NeoGeoPort.SyncSettings>
{
internal LibNeoGeoPort _neopop;
private long _clockTime;
private int _clockDen;
[CoreConstructor("NGP")]
public NeoGeoPort(CoreComm comm, byte[] rom, SyncSettings syncSettings, bool deterministic)
: this(comm, rom, syncSettings, deterministic, PeRunner.CanonicalStart)
{
}
internal NeoGeoPort(CoreComm comm, byte[] rom, SyncSettings syncSettings, bool deterministic, ulong startAddress)
:base(comm, new Configuration
{
DefaultFpsNumerator = 6144000,
DefaultFpsDenominator = 515 * 198,
DefaultWidth = 160,
DefaultHeight = 152,
MaxWidth = 160,
MaxHeight = 152,
MaxSamples = 8192,
SystemId = "NGP"
})
{
if (rom.Length > 4 * 1024 * 1024)
throw new InvalidOperationException("ROM too big!");
_syncSettings = syncSettings ?? new SyncSettings();
_neopop = PreInit<LibNeoGeoPort>(new PeRunnerOptions
{
Filename = "ngp.wbx",
SbrkHeapSizeKB = 256,
SealedHeapSizeKB = 5 * 1024, // must be a bit larger than the ROM size
InvisibleHeapSizeKB = 4,
PlainHeapSizeKB = 5 * 1024, // must be a bit larger than the ROM size
StartAddress = startAddress
});
if (!_neopop.LoadSystem(rom, rom.Length, _syncSettings.Language))
throw new InvalidOperationException("Core rejected the rom");
PostInit();
DeterministicEmulation = deterministic || !_syncSettings.UseRealTime;
_clockTime = (long)((_syncSettings.InitialTime - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds);
}
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
{
_clockDen += VsyncDenominator;
if (_clockDen >= VsyncNumerator)
{
_clockDen -= VsyncNumerator;
_clockTime++;
}
long clockTime = DeterministicEmulation ? _clockTime : (long)((_syncSettings.InitialTime - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds);
if (controller.IsPressed("Power"))
_neopop.HardReset();
return new LibNeoGeoPort.FrameInfo
{
FrontendTime = clockTime,
Buttons = GetButtons(controller),
SkipRendering = render ? 0 : 1,
};
}
#region IStatable
protected override void SaveStateBinaryInternal(BinaryWriter writer)
{
writer.Write(_clockTime);
writer.Write(_clockDen);
}
protected override void LoadStateBinaryInternal(BinaryReader reader)
{
_clockTime = reader.ReadInt64();
_clockDen = reader.ReadInt32();
}
#endregion
#region Controller
private static int GetButtons(IController c)
{
var ret = 0;
var val = 1;
foreach (var s in CoreButtons)
{
if (c.IsPressed(s))
ret |= val;
val <<= 1;
}
return ret;
}
private static readonly string[] CoreButtons =
{
"Up", "Down", "Left", "Right", "A", "B", "Option"
};
private static readonly Dictionary<string, int> ButtonOrdinals = new Dictionary<string, int>
{
["Up"] = 1,
["Down"] = 2,
["Left"] = 3,
["Right"] = 4,
["B"] = 9,
["A"] = 10,
["R"] = 11,
["L"] = 12,
["Option"] = 13
};
private static readonly ControllerDefinition NeoGeoPortableController = new ControllerDefinition
{
Name = "NeoGeo Portable Controller",
BoolButtons = CoreButtons
.OrderBy(b => ButtonOrdinals[b])
.Concat(new[] { "Power" })
.ToList()
};
public override ControllerDefinition ControllerDefinition => NeoGeoPortableController;
#endregion
#region ISettable
private SyncSettings _syncSettings;
public class SyncSettings
{
[DisplayName("Language")]
[Description("Language of the system. Only affects some games.")]
[DefaultValue(LibNeoGeoPort.Language.Japanese)]
public LibNeoGeoPort.Language Language { get; set; }
[DisplayName("Initial Time")]
[Description("Initial time of emulation. Only relevant when UseRealTime is false.")]
[DefaultValue(typeof(DateTime), "2010-01-01")]
public DateTime InitialTime { get; set; }
[DisplayName("Use RealTime")]
[Description("If true, RTC clock will be based off of real time instead of emulated time. Ignored (set to false) when recording a movie.")]
[DefaultValue(false)]
public bool UseRealTime { get; set; }
public SyncSettings Clone()
{
return (SyncSettings)MemberwiseClone();
}
public static bool NeedsReboot(SyncSettings x, SyncSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
public SyncSettings()
{
SettingsUtil.SetDefaultValues(this);
}
}
public object GetSettings()
{
return null;
}
public SyncSettings GetSyncSettings()
{
return _syncSettings.Clone();
}
public bool PutSettings(object o)
{
return false;
}
public bool PutSyncSettings(SyncSettings o)
{
var ret = SyncSettings.NeedsReboot(_syncSettings, o);
_syncSettings = o;
return ret;
}
#endregion
#region ISaveram
public new bool SaveRamModified => _neopop.HasSaveRam();
public new byte[] CloneSaveRam()
{
byte[] ret = null;
_neopop.GetSaveRam((data, size) =>
{
ret = new byte[size];
Marshal.Copy(data, ret, 0, size);
});
return ret;
}
public new void StoreSaveRam(byte[] data)
{
if (!_neopop.PutSaveRam(data, data.Length))
throw new InvalidOperationException("Core rejected the saveram");
}
#endregion
}
}

View File

@ -152,7 +152,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
#region IEmulator
protected abstract LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller);
protected abstract LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound);
public unsafe void FrameAdvance(IController controller, bool render, bool rendersound = true)
{
@ -163,7 +163,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
fixed (int* vp = _videoBuffer)
fixed (short* sp = _soundBuffer)
{
var frame = FrameAdvancePrep(controller);
var frame = FrameAdvancePrep(controller, render, rendersound);
frame.VideoBuffer = (IntPtr)vp;
frame.SoundBuffer = (IntPtr)sp;
@ -204,7 +204,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
protected readonly BasicServiceProvider _serviceProvider;
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
public string SystemId { get; }
public bool DeterministicEmulation => true;
public bool DeterministicEmulation { get; protected set; } = true;
public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
public abstract ControllerDefinition ControllerDefinition { get; }

Binary file not shown.

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

Binary file not shown.

View File

@ -1,88 +1,62 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <algorithm>
#include <cassert>
#include <cstring>
#include <cstdlib>
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
//#define MDFN_FASTCALL
#define INLINE inline
#define MDFN_COLD
#define NO_INLINE
//#define MDFN_ASSUME_ALIGNED(p, align) ((decltype(p))__builtin_assume_aligned((p), (align)))
#define MDFN_ASSUME_ALIGNED(p, align) (p)
#define trio_snprintf snprintf
#define TRUE true
#define FALSE false
#ifndef __alignas_is_defined
#define alignas(p)
#endif
struct MDFN_Surface
{
uint32 *pixels;
int pitch32;
};
struct EmulateSpecStruct
{
// 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.
uint32 *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.
int16 *SoundBuf;
// Number of cycles that this frame consumed, using MDFNGI::MasterClock as a time base.
// Set by emulation code.
int64 MasterCycles;
// unix time for RTC
int64 FrontendTime;
// Maximum size of the sound buffer, in frames. Set by the driver code.
int32 SoundBufMaxSize;
// Number of frames currently in internal sound buffer. Set by the system emulation code, to be read by the driver code.
int32 SoundBufSize;
// true to skip rendering
int32 skip;
int32 Buttons;
// set by core, true if lagged
int32 Lagged;
};
#define MDFN_printf(...)
#define MDFN_PrintError(...)
#define require assert
#include "endian.h"
inline char* strdup(const char* p)
{
char* ret = (char*)malloc(strlen(p) + 1);
if (ret)
strcpy(ret, p);
return ret;
}
#include <emulibc.h>
#define EXPORT extern "C" ECL_EXPORT
#pragma once
#include <cstdint>
#include <cstddef>
#include <algorithm>
#include <cassert>
#include <cstring>
#include <cstdlib>
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
//#define MDFN_FASTCALL
#define INLINE inline
#define MDFN_COLD
#define NO_INLINE
//#define MDFN_ASSUME_ALIGNED(p, align) ((decltype(p))__builtin_assume_aligned((p), (align)))
#define MDFN_ASSUME_ALIGNED(p, align) (p)
#define trio_snprintf snprintf
#define TRUE true
#define FALSE false
#ifndef __alignas_is_defined
#define alignas(p)
#endif
#include <emulibc.h>
#define EXPORT extern "C" ECL_EXPORT
#include <waterboxcore.h>
struct MyFrameInfo: public FrameInfo
{
int64_t FrontendTime;
int32_t SkipRendering;
int32_t Buttons;
};
struct MDFN_Surface
{
uint32 *pixels;
int pitch32;
};
#define MDFN_printf(...)
#define MDFN_PrintError(...)
#define require assert
#include "endian.h"
inline char* strdup(const char* p)
{
char* ret = (char*)malloc(strlen(p) + 1);
if (ret)
strcpy(ret, p);
return ret;
}

View File

@ -1,257 +1,244 @@
//---------------------------------------------------------------------------
// NEOPOP : Emulator as in Dreamland
//
// Copyright (c) 2001-2002 by neopop_uk
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version. See also the license.txt file for
// additional informations.
//---------------------------------------------------------------------------
#include "neopop.h"
#include "Z80_interface.h"
#include "interrupt.h"
#include "mem.h"
#include "gfx.h"
#include "sound.h"
#include "dma.h"
#include "bios.h"
#include "flash.h"
#include <algorithm>
namespace MDFN_IEN_NGP
{
bool lagged;
void (*inputcallback)();
extern uint8 CPUExRAM[16384];
NGPGFX_CLASS *NGPGfx = NULL;
COLOURMODE system_colour = COLOURMODE_AUTO;
uint8 NGPJoyLatch;
uint8 settings_language;
time_t frontend_time;
int (*comms_read_cb)(uint8* buffer);
int (*comms_poll_cb)(uint8* buffer);
void (*comms_write_cb)(uint8 data);
bool system_comms_read(uint8 *buffer)
{
if (comms_read_cb)
return comms_read_cb(buffer);
else
return false;
}
bool system_comms_poll(uint8 *buffer)
{
if (comms_poll_cb)
return comms_poll_cb(buffer);
else
return false;
}
void system_comms_write(uint8 data)
{
if (comms_write_cb)
comms_write_cb(data);
}
void instruction_error(char *vaMessage, ...)
{
/*char message[1000];
va_list vl;
va_start(vl, vaMessage);
vsprintf(message, vaMessage, vl);
va_end(vl);
MDFN_printf("[PC %06X] %s\n", pc, message);*/
}
bool NGPFrameSkip;
int32 ngpc_soundTS = 0;
//static int32 main_timeaccum;
static int32 z80_runtime;
static void Emulate(EmulateSpecStruct *espec)
{
lagged = true;
bool MeowMeow = 0;
MDFN_Surface surface;
surface.pixels = espec->pixels;
surface.pitch32 = 160;
frontend_time = espec->FrontendTime;
storeB(0x6f82, espec->Buttons);
ngpc_soundTS = 0;
NGPFrameSkip = espec->skip;
do
{
int32 timetime = (uint8)TLCS900h_interpret(); // This is sooo not right, but it's replicating the old behavior(which is necessary
// now since I've fixed the TLCS900h core and other places not to truncate cycle counts
// internally to 8-bits). Switch to the #if 0'd block of code once we fix cycle counts in the
// TLCS900h core(they're all sorts of messed up), and investigate if certain long
// instructions are interruptable(by interrupts) and later resumable, RE Rockman Battle
// & Fighters voice sample playback.
//if(timetime > 255)
// printf("%d\n", timetime);
// Note: Don't call updateTimers with a time/tick/cycle/whatever count greater than 255.
MeowMeow |= updateTimers(&surface, timetime);
z80_runtime += timetime;
while (z80_runtime > 0)
{
int z80rantime = Z80_RunOP();
if (z80rantime < 0) // Z80 inactive, so take up all run time!
{
z80_runtime = 0;
break;
}
z80_runtime -= z80rantime << 1;
}
} while (!MeowMeow);
espec->MasterCycles = ngpc_soundTS;
espec->SoundBufSize = MDFNNGPCSOUND_Flush(espec->SoundBuf, espec->SoundBufMaxSize);
espec->Lagged = lagged;
}
static MDFN_COLD bool Load(const uint8* romdata, int32 romlength)
{
const uint64 fp_size = romlength;
if (fp_size > 1024 * 1024 * 8) // 4MiB maximum ROM size, 2* to be a little tolerant of garbage.
return false;
//throw MDFN_Error(0, _("NGP/NGPC ROM image is too large."));
ngpc_rom.length = fp_size;
ngpc_rom.data = (uint8*)alloc_plain(ngpc_rom.length);
memcpy(ngpc_rom.data, romdata, romlength);
rom_loaded();
//if (!FLASH_LoadNV())
// return false;
//MDFNMP_Init(1024, 1024 * 1024 * 16 / 1024);
NGPGfx = new NGPGFX_CLASS();
//MDFNGameInfo->fps = (uint32)((uint64)6144000 * 65536 * 256 / 515 / 198); // 3072000 * 2 * 10000 / 515 / 198
MDFNNGPCSOUND_Init();
//MDFNMP_AddRAM(16384, 0x4000, CPUExRAM);
SetFRM(); // Set up fast read memory mapping
bios_install();
//main_timeaccum = 0;
z80_runtime = 0;
reset();
MDFNNGPC_SetSoundRate(44100);
return true;
}
}
using namespace MDFN_IEN_NGP;
int main(void)
{
return 0;
}
EXPORT int LoadSystem(const uint8* rom, int romlength, int language)
{
settings_language = language;
return Load(rom, romlength);
}
EXPORT void SetLayers(int enable) // 1, 2, 4 bg,fg,sprites
{
NGPGfx->SetLayerEnableMask(enable);
}
EXPORT void FrameAdvance(EmulateSpecStruct *espec)
{
Emulate(espec);
}
EXPORT void HardReset()
{
reset();
}
EXPORT void SetInputCallback(void (*callback)())
{
inputcallback = callback;
}
EXPORT void SetCommsCallbacks(int (*read_cb)(uint8* buffer), int (*poll_cb)(uint8* buffer), void (*write_cb)(uint8 data))
{
comms_read_cb = read_cb;
comms_poll_cb = poll_cb;
comms_write_cb = write_cb;
}
EXPORT void GetMemoryArea(int which, void **ptr, int *size, int *writable)
{
switch (which)
{
case 0:
*ptr = CPUExRAM;
*size = 16384;
*writable = 1;
break;
case 1:
*ptr = ngpc_rom.data;
*size = ngpc_rom.length;
*writable = 0;
break;
case 2:
*ptr = ngpc_rom.orig_data;
*size = ngpc_rom.length;
*writable = 0;
break;
default:
*ptr = nullptr;
*size = 0;
*writable = 0;
break;
}
}
EXPORT bool HasSaveRam()
{
return FLASH_IsModified();
}
EXPORT bool PutSaveRam(const uint8* data, uint32 length)
{
return FLASH_LoadNV(data, length);
}
EXPORT void GetSaveRam(void (*callback)(const uint8* data, uint32 length))
{
FLASH_SaveNV(callback);
}
//---------------------------------------------------------------------------
// NEOPOP : Emulator as in Dreamland
//
// Copyright (c) 2001-2002 by neopop_uk
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version. See also the license.txt file for
// additional informations.
//---------------------------------------------------------------------------
#include "neopop.h"
#include "Z80_interface.h"
#include "interrupt.h"
#include "mem.h"
#include "gfx.h"
#include "sound.h"
#include "dma.h"
#include "bios.h"
#include "flash.h"
#include <algorithm>
namespace MDFN_IEN_NGP
{
bool lagged;
void (*inputcallback)();
extern uint8 CPUExRAM[16384];
NGPGFX_CLASS *NGPGfx = NULL;
COLOURMODE system_colour = COLOURMODE_AUTO;
uint8 NGPJoyLatch;
uint8 settings_language;
time_t frontend_time;
int (*comms_read_cb)(uint8* buffer);
int (*comms_poll_cb)(uint8* buffer);
void (*comms_write_cb)(uint8 data);
bool system_comms_read(uint8 *buffer)
{
if (comms_read_cb)
return comms_read_cb(buffer);
else
return false;
}
bool system_comms_poll(uint8 *buffer)
{
if (comms_poll_cb)
return comms_poll_cb(buffer);
else
return false;
}
void system_comms_write(uint8 data)
{
if (comms_write_cb)
comms_write_cb(data);
}
void instruction_error(char *vaMessage, ...)
{
/*char message[1000];
va_list vl;
va_start(vl, vaMessage);
vsprintf(message, vaMessage, vl);
va_end(vl);
MDFN_printf("[PC %06X] %s\n", pc, message);*/
}
bool NGPFrameSkip;
int32 ngpc_soundTS = 0;
//static int32 main_timeaccum;
static int32 z80_runtime;
EXPORT void FrameAdvance(MyFrameInfo* frame)
{
lagged = true;
bool MeowMeow = 0;
MDFN_Surface surface;
surface.pixels = frame->VideoBuffer;
surface.pitch32 = 160;
frame->Width = 160;
frame->Height = 152;
frontend_time = frame->FrontendTime;
storeB(0x6f82, frame->Buttons);
ngpc_soundTS = 0;
NGPFrameSkip = frame->SkipRendering;
do
{
int32 timetime = (uint8)TLCS900h_interpret(); // This is sooo not right, but it's replicating the old behavior(which is necessary
// now since I've fixed the TLCS900h core and other places not to truncate cycle counts
// internally to 8-bits). Switch to the #if 0'd block of code once we fix cycle counts in the
// TLCS900h core(they're all sorts of messed up), and investigate if certain long
// instructions are interruptable(by interrupts) and later resumable, RE Rockman Battle
// & Fighters voice sample playback.
//if(timetime > 255)
// printf("%d\n", timetime);
// Note: Don't call updateTimers with a time/tick/cycle/whatever count greater than 255.
MeowMeow |= updateTimers(&surface, timetime);
z80_runtime += timetime;
while (z80_runtime > 0)
{
int z80rantime = Z80_RunOP();
if (z80rantime < 0) // Z80 inactive, so take up all run time!
{
z80_runtime = 0;
break;
}
z80_runtime -= z80rantime << 1;
}
} while (!MeowMeow);
frame->Cycles = ngpc_soundTS;
frame->Samples = MDFNNGPCSOUND_Flush(frame->SoundBuffer, 8192);
frame->Lagged = lagged;
}
static MDFN_COLD bool Load(const uint8* romdata, int32 romlength)
{
const uint64 fp_size = romlength;
if (fp_size > 1024 * 1024 * 8) // 4MiB maximum ROM size, 2* to be a little tolerant of garbage.
return false;
//throw MDFN_Error(0, _("NGP/NGPC ROM image is too large."));
ngpc_rom.length = fp_size;
ngpc_rom.data = (uint8*)alloc_plain(ngpc_rom.length);
memcpy(ngpc_rom.data, romdata, romlength);
rom_loaded();
//if (!FLASH_LoadNV())
// return false;
//MDFNMP_Init(1024, 1024 * 1024 * 16 / 1024);
NGPGfx = new NGPGFX_CLASS();
//MDFNGameInfo->fps = (uint32)((uint64)6144000 * 65536 * 256 / 515 / 198); // 3072000 * 2 * 10000 / 515 / 198
MDFNNGPCSOUND_Init();
//MDFNMP_AddRAM(16384, 0x4000, CPUExRAM);
SetFRM(); // Set up fast read memory mapping
bios_install();
//main_timeaccum = 0;
z80_runtime = 0;
reset();
MDFNNGPC_SetSoundRate(44100);
return true;
}
}
using namespace MDFN_IEN_NGP;
int main(void)
{
return 0;
}
EXPORT int LoadSystem(const uint8* rom, int romlength, int language)
{
settings_language = language;
return Load(rom, romlength);
}
EXPORT void SetLayers(int enable) // 1, 2, 4 bg,fg,sprites
{
NGPGfx->SetLayerEnableMask(enable);
}
EXPORT void HardReset()
{
reset();
}
EXPORT void SetInputCallback(void (*callback)())
{
inputcallback = callback;
}
EXPORT void SetCommsCallbacks(int (*read_cb)(uint8* buffer), int (*poll_cb)(uint8* buffer), void (*write_cb)(uint8 data))
{
comms_read_cb = read_cb;
comms_poll_cb = poll_cb;
comms_write_cb = write_cb;
}
EXPORT void GetMemoryAreas(MemoryArea* m)
{
m[0].Data = CPUExRAM;
m[0].Name = "RAM";
m[0].Size = 16384;
m[0].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY | MEMORYAREA_FLAGS_WORDSIZE4;
m[1].Data = ngpc_rom.data;
m[1].Name = "ROM";
m[1].Size = ngpc_rom.length;
m[1].Flags = MEMORYAREA_FLAGS_WORDSIZE4;
m[2].Data = ngpc_rom.orig_data;
m[2].Name = "ORIGINAL ROM";
m[2].Size = ngpc_rom.length;
m[2].Flags = MEMORYAREA_FLAGS_WORDSIZE4;
}
EXPORT bool HasSaveRam()
{
return FLASH_IsModified();
}
EXPORT bool PutSaveRam(const uint8* data, uint32 length)
{
return FLASH_LoadNV(data, length);
}
EXPORT void GetSaveRam(void (*callback)(const uint8* data, uint32 length))
{
FLASH_SaveNV(callback);
}