Refactored some N64 code

This commit is contained in:
null_ptr 2014-01-15 11:24:47 +00:00
parent 6f05084b2c
commit b79bcaf2d0
6 changed files with 279 additions and 115 deletions

View File

@ -225,7 +225,9 @@
<Compile Include="Consoles\Nintendo\GBA\Meteor.cs" />
<Compile Include="Consoles\Nintendo\N64\mupen64plusApi.cs" />
<Compile Include="Consoles\Nintendo\N64\N64.cs" />
<Compile Include="Consoles\Nintendo\N64\N64Audio.cs" />
<Compile Include="Consoles\Nintendo\N64\N64SyncSettings.cs" />
<Compile Include="Consoles\Nintendo\N64\N64VideoProvider.cs" />
<Compile Include="Consoles\Nintendo\NES\APU.cs" />
<Compile Include="Consoles\Nintendo\NES\BoardSystem.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\AVE-NINA.cs" />

View File

@ -10,7 +10,7 @@ using BizHawk.Emulation.Cores.Consoles.Nintendo.N64;
namespace BizHawk.Emulation.Cores.Nintendo.N64
{
public class N64 : IEmulator, IVideoProvider
public class N64 : IEmulator
{
public List<KeyValuePair<string, int>> GetCpuFlagsAndRegisters()
{
@ -64,24 +64,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
public string BoardName { get { return null; } }
public CoreComm CoreComm { get; private set; }
public byte[] rom;
public GameInfo game;
public IVideoProvider VideoProvider { get { return this; } }
public int[] frameBuffer;// = new int[800 * 600];
public int[] GetVideoBuffer() { return frameBuffer; }
public int VirtualWidth { get; set; }
public int BufferWidth { get; set; }
public int BufferHeight { get; set; }
public int BackgroundColor { get { return 0; } }
private N64VideoProvider videoProvider;
public IVideoProvider VideoProvider { get { return videoProvider; } }
private DisplayType _display_type = DisplayType.NTSC;
public DisplayType DisplayType { get { return _display_type; } }
public SpeexResampler resampler;
private N64Audio audioProvider;
public ISoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return resampler; } }
public ISyncSoundProvider SyncSoundProvider { get { return audioProvider.Resampler; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
@ -118,18 +110,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
}
};
public int Frame { get; set; }
public int Frame { get; private set; }
public int LagCount { get; set; }
public bool IsLagFrame { get; set; }
public bool IsLagFrame { get; private set; }
public void ResetCounters()
{
Frame = 0;
LagCount = 0;
IsLagFrame = false;
}
public void FrameAdvance(bool render, bool rendersound)
{
RefreshMemoryCallbacks();
audioProvider.RenderSound = rendersound;
if (Controller["Reset"])
{
@ -142,10 +135,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
IsLagFrame = true;
api.frame_advance();
if (IsLagFrame) LagCount++;
Frame++;
Frame++;
}
/// <summary>
/// Translates controller inputs from EmuHawk and
/// shoves them into mupen64plus
/// </summary>
public void setControllers()
{
CoreComm.InputCallback.Call();
@ -171,6 +169,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
}
}
/// <summary>
/// Read all buttons from a controller and translate them
/// into a form the N64 understands
/// </summary>
/// <param name="num">Number of controller to translate</param>
/// <returns>Bitlist of pressed buttons</returns>
public int ReadController(int num)
{
int buttons = 0;
@ -193,7 +197,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
return buttons;
}
public bool DeterministicEmulation { get; set; }
public bool DeterministicEmulation { get { return false; } }
public byte[] ReadSaveRam()
{
@ -212,12 +216,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
public bool SaveRamModified { get { return true; } set { } }
void SyncState(Serializer ser)
{
ser.BeginSection("N64");
ser.EndSection();
}
// these next 5 functions are all exact copy paste from gambatte.
// if something's wrong here, it's probably wrong there too
@ -321,11 +319,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
// we RefreshMemoryCallbacks() after the triggers in case the trigger turns itself off at that point
if (mcs.HasReads)
readcb = delegate(uint addr) { mcs.CallRead(addr); RefreshMemoryCallbacks(); };
readcb = delegate(uint addr) { mcs.CallRead(addr); };
else
readcb = null;
if (mcs.HasWrites)
writecb = delegate(uint addr) { mcs.CallWrite(addr); RefreshMemoryCallbacks(); };
writecb = delegate(uint addr) { mcs.CallWrite(addr); };
else
writecb = null;
@ -394,11 +392,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
public void Dispose()
{
videoProvider.Dispose();
audioProvider.Dispose();
api.Dispose();
}
mupen64plusApi api;
// mupen64plus DLL Api
private mupen64plusApi api;
/// <summary>
/// Create mupen64plus Emulator
/// </summary>
/// <param name="comm">Core communication object</param>
/// <param name="game">Game information of game to load</param>
/// <param name="rom">Rom that should be loaded</param>
/// <param name="SyncSettings">N64SyncSettings object</param>
public N64(CoreComm comm, GameInfo game, byte[] rom, object SyncSettings)
{
int SaveType = 0;
@ -406,8 +414,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
SaveType = 1;
CoreComm = comm;
this.rom = rom;
this.game = game;
this.SyncSettings = (N64SyncSettings)SyncSettings ?? new N64SyncSettings();
@ -439,7 +445,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
api = new mupen64plusApi(this, rom, this.SyncSettings.GetVPS(game), SaveType);
api.SetM64PInputCallback(new mupen64plusApi.InputCallback(setControllers));
audioProvider = new N64Audio(api);
videoProvider = new N64VideoProvider(api);
api.FrameFinished += videoProvider.DoVideoFrame;
api.VInterrupt += audioProvider.DoAudioFrame;
InitMemoryDomains();
RefreshMemoryCallbacks();
}
N64SyncSettings SyncSettings;

View File

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.N64
{
class N64Audio : IDisposable
{
/// <summary>
/// mupen64 DLL Api
/// </summary>
private mupen64plusApi api;
/// <summary>
/// Buffer for audio data
/// </summary>
private short[] audioBuffer = new short[0];
private uint _samplingRate = 0;
/// <summary>
/// Currently active sampling rate
/// </summary>
public uint SamplingRate
{
get
{
return _samplingRate;
}
private set
{
_samplingRate = value;
Resampler.ChangeRate(_samplingRate, 44100, _samplingRate, 44100);
}
}
/// <summary>
/// Resampler for audio output
/// </summary>
public SpeexResampler Resampler { get; private set; }
public bool RenderSound { get; set; }
/// <summary>
/// Creates a N64 Audio subsystem
/// </summary>
/// <param name="api">Mupen64 api which is used for fetching sound</param>
public N64Audio(mupen64plusApi api)
{
this.api = api;
_samplingRate = api.GetSamplingRate();
Resampler = new SpeexResampler(6, SamplingRate, 44100,
SamplingRate, 44100);
}
/// <summary>
/// Fetches the audio buffer from mupen64plus and pushes it into the
/// Resampler for audio output
/// </summary>
public void DoAudioFrame()
{
uint m64pSamplingRate = api.GetSamplingRate();
if (m64pSamplingRate != SamplingRate)
SamplingRate = m64pSamplingRate;
int audioBufferSize = api.GetAudioBufferSize();
if (audioBuffer.Length < audioBufferSize)
audioBuffer = new short[audioBufferSize];
if (audioBufferSize > 0)
{
api.GetAudioBuffer(audioBuffer);
if (RenderSound)
Resampler.EnqueueSamples(audioBuffer, audioBufferSize / 2);
}
}
public void Dispose()
{
if(Resampler != null)
Resampler.Dispose();
Resampler = null;
// Api is disposed by N64
api = null;
}
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.N64
{
class N64VideoProvider : IVideoProvider, IDisposable
{
private int[] frameBuffer;
private mupen64plusApi api;
/// <summary>
/// Creates N64 Video system with mupen64plus backend
/// </summary>
/// <param name="api">mupen64plus DLL that is used</param>
public N64VideoProvider(mupen64plusApi api)
{
this.api = api;
int width = 0;
int height = 0;
api.GetScreenDimensions(ref width, ref height);
SetBufferSize(width, height);
}
public int[] GetVideoBuffer()
{
return frameBuffer;
}
public int VirtualWidth { get { return BufferWidth; } }
public int BufferWidth { get; private set; }
public int BufferHeight { get; private set; }
public int BackgroundColor { get { return 0; } }
/// <summary>
/// Fetches current frame buffer from mupen64
/// </summary>
public void DoVideoFrame()
{
int width = 0;
int height = 0;
api.GetScreenDimensions(ref width, ref height);
if (width != BufferWidth || height != BufferHeight)
{
SetBufferSize(width, height);
}
api.Getm64pFrameBuffer(frameBuffer, ref width, ref height);
}
/// <summary>
/// Sets a new width and height for frame buffer
/// </summary>
/// <param name="width">New width in pixels</param>
/// <param name="height">New height in pixels</param>
private void SetBufferSize(int width, int height)
{
BufferHeight = height;
BufferWidth = width;
frameBuffer = new int[width * height];
}
public void Dispose()
{
// api is disposed by N64
api = null;
}
}
}

View File

@ -12,14 +12,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
{
public class mupen64plusApi : IDisposable
{
// Only left in because api needs to know the number of frames passed
// because of a bug
private readonly N64 bizhawkCore;
static mupen64plusApi AttachedCore = null;
bool disposed = false;
uint m64pSamplingRate;
short[] m64pAudioBuffer = new short[2];
Thread m64pEmulator;
AutoResetEvent m64pFrameComplete = new AutoResetEvent(false);
@ -439,12 +438,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
public mupen64plusApi(N64 bizhawkCore, byte[] rom, VideoPluginSettings video_settings, int SaveType)
{
// There can only be one core (otherwise breaks mupen64plus)
if (AttachedCore != null)
{
AttachedCore.Dispose();
AttachedCore = null;
}
this.bizhawkCore = bizhawkCore;
string VidDllName;
@ -485,7 +484,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
connectFunctionPointers();
// Start up the core
m64p_error result = m64pCoreStartup(0x20001, "", "", "Core", (IntPtr foo, int level, string Message) => { }, "", IntPtr.Zero);
m64p_error result = m64pCoreStartup(0x20001, "", "", "Core",
(IntPtr foo, int level, string Message) => { },
"", IntPtr.Zero);
// Set the savetype if needed
if (SaveType != 0)
@ -498,9 +499,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
// Pass the rom to the core
result = m64pCoreDoCommandByteArray(m64p_command.M64CMD_ROM_OPEN, rom.Length, rom);
// Resize the video to the size in bizhawk's settings
SetVideoSize(video_settings.Width, video_settings.Height);
// Open the general video settings section in the config system
IntPtr video_section = IntPtr.Zero;
m64pConfigOpenSection("Video-General", ref video_section);
@ -511,6 +509,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
set_video_parameters(video_settings);
// Order of plugin loading is important, do not change!
// Set up and connect the graphics plugin
result = GfxPluginStartup(CoreDll, "Video", (IntPtr foo, int level, string Message) => { });
result = m64pCoreAttachPlugin(m64p_plugin_type.M64PLUGIN_GFX, GfxDll);
@ -520,23 +519,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
result = m64pCoreAttachPlugin(m64p_plugin_type.M64PLUGIN_AUDIO, AudDll);
// Set up our input plugin
result = AudPluginStartup(CoreDll, "Input", (IntPtr foo, int level, string Message) => { });
result = AudPluginStartup(CoreDll, "Input",
(IntPtr foo, int level, string Message) => { });
result = m64pCoreAttachPlugin(m64p_plugin_type.M64PLUGIN_INPUT, InpDll);
// Set up and connect the RSP plugin
result = RspPluginStartup(CoreDll, "RSP", (IntPtr foo, int level, string Message) => { });
result = m64pCoreAttachPlugin(m64p_plugin_type.M64PLUGIN_RSP, RspDll);
// Set up the frame callback function
m64pFrameCallback = new FrameCallback(Getm64pFrameBuffer);
result = m64pCoreDoCommandFrameCallback(m64p_command.M64CMD_SET_FRAME_CALLBACK, 0, m64pFrameCallback);
// Set up the VI callback function
m64pVICallback = new VICallback(VI);
result = m64pCoreDoCommandVICallback(m64p_command.M64CMD_SET_VI_CALLBACK, 0, m64pVICallback);
InitSaveram();
// Initialize event invoker
m64pFrameCallback = new FrameCallback(FireFrameFinishedEvent);
result = m64pCoreDoCommandFrameCallback(m64p_command.M64CMD_SET_FRAME_CALLBACK, 0, m64pFrameCallback);
m64pVICallback = new VICallback(FireVIEvent);
result = m64pCoreDoCommandVICallback(m64p_command.M64CMD_SET_VI_CALLBACK, 0, m64pVICallback);
// Start the emulator in another thread
m64pEmulator = new Thread(ExecuteEmulator);
m64pEmulator.Start();
@ -544,14 +542,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
// Wait for the core to boot up
m64pStartupComplete.WaitOne();
// Set up the resampler
m64pSamplingRate = (uint)AudGetAudioRate();
bizhawkCore.resampler = new SpeexResampler(6, m64pSamplingRate, 44100, m64pSamplingRate, 44100, null, null);
AttachedCore = this;
}
volatile bool emulator_running = false;
/// <summary>
/// Starts execution of mupen64plus
/// Does not return until the emulator stops
/// </summary>
public void ExecuteEmulator()
{
emulator_running = true;
@ -611,16 +609,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
RspPluginShutdown = (PluginShutdown)Marshal.GetDelegateForFunctionPointer(GetProcAddress(RspDll, "PluginShutdown"), typeof(PluginShutdown));
}
public void SetVideoSize(int vidX, int vidY)
{
bizhawkCore.VirtualWidth = vidX;
bizhawkCore.BufferWidth = vidX;
bizhawkCore.BufferHeight = vidY;
bizhawkCore.frameBuffer = new int[vidX * vidY];
m64p_FrameBuffer = new int[vidX * vidY];
}
/// <summary>
/// Puts plugin settings of EmuHawk into mupen64plus
/// </summary>
/// <param name="video_settings">Settings to put into mupen64plus</param>
public void set_video_parameters(VideoPluginSettings video_settings)
{
IntPtr video_plugin_section = IntPtr.Zero;
@ -656,45 +648,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
}
}
int[] m64p_FrameBuffer;
private int[] m64pBuffer = new int[0];
/// <summary>
/// This function will be used as the frame callback. It pulls the framebuffer from mupen64plus
/// This function copies the frame buffer from mupen64plus
/// </summary>
public void Getm64pFrameBuffer()
public void Getm64pFrameBuffer(int[] buffer, ref int width, ref int height)
{
int width = 0;
int height = 0;
// Get the size of the frame buffer
GFXReadScreen2Res(IntPtr.Zero, ref width, ref height, 0);
// If it's not the same size as the current one, change the sizes
if (width != bizhawkCore.BufferWidth || height != bizhawkCore.BufferHeight)
{
SetVideoSize(width, height);
}
if(m64pBuffer.Length != width * height)
m64pBuffer = new int[width * height];
// Actually get the frame buffer
GFXReadScreen2(m64p_FrameBuffer, ref width, ref height, 0);
GFXReadScreen2(m64pBuffer, ref width, ref height, 0);
// vflip
int fromindex = bizhawkCore.BufferWidth * (bizhawkCore.BufferHeight - 1) * 4;
int fromindex = width * (height - 1) * 4;
int toindex = 0;
for (int j = 0; j < bizhawkCore.BufferHeight; j++)
for (int j = 0; j < height; j++)
{
Buffer.BlockCopy(m64p_FrameBuffer, fromindex, bizhawkCore.frameBuffer, toindex, bizhawkCore.BufferWidth * 4);
fromindex -= bizhawkCore.BufferWidth * 4;
toindex += bizhawkCore.BufferWidth * 4;
Buffer.BlockCopy(m64pBuffer, fromindex, buffer, toindex, width * 4);
fromindex -= width * 4;
toindex += width * 4;
}
// opaque
unsafe
{
fixed (int* ptr = &bizhawkCore.frameBuffer[0])
fixed (int* ptr = &buffer[0])
{
int l = bizhawkCore.frameBuffer.Length;
int l = buffer.Length;
for (int i = 0; i < l; i++)
{
ptr[i] |= unchecked((int)0xff000000);
@ -703,32 +684,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
}
}
/// <summary>
/// This function will be used as the VI callback. It checks the audio rate and updates the resampler if necessary.
/// It then polls the audio buffer for samples. It then marks the frame as complete.
/// </summary>
public void VI()
{
uint s = (uint)AudGetAudioRate();
if (s != m64pSamplingRate)
{
m64pSamplingRate = s;
bizhawkCore.resampler.ChangeRate(s, 44100, s, 44100);
//Console.WriteLine("N64 ARate Change {0}", s);
}
int m64pAudioBufferSize = AudGetBufferSize();
if (m64pAudioBuffer.Length < m64pAudioBufferSize)
m64pAudioBuffer = new short[m64pAudioBufferSize];
if (m64pAudioBufferSize > 0)
{
AudReadAudioBuffer(m64pAudioBuffer);
bizhawkCore.resampler.EnqueueSamples(m64pAudioBuffer, m64pAudioBufferSize / 2);
}
m64pFrameComplete.Set();
}
public int get_memory_size(N64_MEMORY id)
{
return m64pMemGetSize(id);
@ -777,6 +732,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
// When using the dynamic recompiler if a state is loaded too early some pointers are not set up yet, so mupen
// tries to access null pointers and the emulator crashes. It seems like it takes 2 frames to fully set up the recompiler,
// so if two frames haven't been run yet, run them, then load the state.
if (bizhawkCore.Frame < 2)
{
frame_advance();
@ -851,9 +807,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
// Backup the saveram in case bizhawk wants to get at is after we've freed the libraries
saveram_backup = SaveSaveram();
bizhawkCore.resampler.Dispose();
bizhawkCore.resampler = null;
m64pCoreDetachPlugin(m64p_plugin_type.M64PLUGIN_GFX);
m64pCoreDetachPlugin(m64p_plugin_type.M64PLUGIN_AUDIO);
m64pCoreDetachPlugin(m64p_plugin_type.M64PLUGIN_INPUT);
@ -878,6 +831,49 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
disposed = true;
}
}
public void GetScreenDimensions(ref int width, ref int height)
{
GFXReadScreen2Res(IntPtr.Zero, ref width, ref height, 0);
}
public uint GetSamplingRate()
{
return (uint)AudGetAudioRate();
}
public int GetAudioBufferSize()
{
return AudGetBufferSize();
}
public void GetAudioBuffer(short[] buffer)
{
AudReadAudioBuffer(buffer);
}
public event Action FrameFinished;
public event Action VInterrupt;
private void FireFrameFinishedEvent()
{
// Execute Frame Callback functions
if (FrameFinished != null)
FrameFinished();
}
private void FireVIEvent()
{
// Execute VI Callback functions
if (VInterrupt != null)
VInterrupt();
m64pFrameComplete.Set();
}
private void CompletedFrameCallback()
{
m64pFrameComplete.Set();
}
}
public class VideoPluginSettings

View File

@ -106,7 +106,7 @@
<TargetMachine>MachineX86</TargetMachine>
</Link>
<PostBuildEvent>
<Command>xcopy /y $(OutDir)mupen64plus.dll $(TargetDir)..\..\..\..\..\output\dll\</Command>
<Command>xcopy /y "$(OutDir)mupen64plus.dll" "$(TargetDir)..\..\..\..\..\output\dll\"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>