diff --git a/BizHawk.Emulation.Common/LibRetroEmulator.cs b/BizHawk.Emulation.Common/LibRetroEmulator.cs
index 2fa975d7fa..f98d26b792 100644
--- a/BizHawk.Emulation.Common/LibRetroEmulator.cs
+++ b/BizHawk.Emulation.Common/LibRetroEmulator.cs
@@ -212,7 +212,9 @@ namespace BizHawk.Emulation.Common
{
IsLagFrame = true;
Frame++;
+ nsamprecv = 0;
retro.retro_run();
+ Console.WriteLine("[{0}]", nsamprecv);
}
public int Frame { get; private set; }
@@ -406,8 +408,13 @@ namespace BizHawk.Emulation.Common
short[] sampbuff = new short[0];
+ // debug
+ int nsamprecv = 0;
+
void SetupResampler(double fps, double sps)
{
+ Console.WriteLine("FPS {0} SPS {1}", fps, sps);
+
// todo: more precise?
uint spsnum = (uint)sps * 1000;
uint spsden = (uint)1000;
@@ -418,6 +425,7 @@ namespace BizHawk.Emulation.Common
void retro_audio_sample(short left, short right)
{
resampler.EnqueueSample(left, right);
+ nsamprecv++;
}
uint retro_audio_sample_batch(IntPtr data, uint frames)
@@ -426,6 +434,7 @@ namespace BizHawk.Emulation.Common
sampbuff = new short[frames * 2];
Marshal.Copy(data, sampbuff, 0, (int)(frames * 2));
resampler.EnqueueSamples(sampbuff, (int)frames);
+ nsamprecv += (int)frames;
// what is the return from this used for?
return frames;
}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/LibQuickNES.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/LibQuickNES.cs
new file mode 100644
index 0000000000..b5c1c7a4f2
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/LibQuickNES.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
+{
+ public static class LibQuickNES
+ {
+ public const string dllname = "libquicknes.dll";
+
+ ///
+ /// create a new quicknes context
+ ///
+ /// NULL on failure
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr qn_new();
+ ///
+ /// destroy a quicknes context
+ ///
+ /// context previously returned from qn_new()
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern void qn_delete(IntPtr e);
+ ///
+ /// load an ines file
+ ///
+ /// context
+ /// file
+ /// length of file
+ ///
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr qn_loadines(IntPtr e, byte[] data, int length);
+ ///
+ /// set audio sample rate
+ ///
+ /// context
+ /// hz
+ /// string error
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr qn_set_sample_rate(IntPtr e, int rate);
+ ///
+ /// get required min dimensions of output video buffer (8bpp)
+ ///
+ /// context
+ /// width
+ /// height
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern void qn_get_image_dimensions(IntPtr e, ref int width, ref int height);
+ ///
+ /// set output video buffer that will be used for all subsequent renders until replaced
+ ///
+ /// context
+ /// 8bpp, at least as big as qn_get_image_dimensions()
+ /// byte pitch
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern void qn_set_pixels(IntPtr e, byte[] dest, int pitch);
+ ///
+ /// emulate a single frame
+ ///
+ /// context
+ /// pad 1 input
+ /// pad 2 input
+ /// string error
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr qn_emulate_frame(IntPtr e, int pad1, int pad2);
+ ///
+ /// get number of times joypad was read in most recent frame
+ ///
+ /// context
+ /// 0 means lag
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern int qn_get_joypad_read_count(IntPtr e);
+ ///
+ /// get audio info for most recent frame
+ ///
+ /// context
+ /// number of samples actually created
+ /// 1 for mono, 2 for stereo
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern void qn_get_audio_info(IntPtr e, ref int sample_count, ref int chan_count);
+ ///
+ /// get audio for most recent frame. must not be called more than once per frame!
+ ///
+ /// context
+ /// sample buffer
+ /// length to read into sample buffer
+ /// length actually read
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern int qn_read_audio(IntPtr e, short[] dest, int max_samples);
+ ///
+ /// reset the console
+ ///
+ /// context
+ /// true for powercycle, false for reset button
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern void qn_reset(IntPtr e, bool hard);
+ ///
+ /// get the required byte size of a savestate
+ ///
+ /// context
+ /// size is returned
+ /// string error
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr qn_state_size(IntPtr e, ref int size);
+ ///
+ /// save state to buffer
+ ///
+ /// context
+ /// buffer
+ /// length of buffer
+ /// string error
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr qn_state_save(IntPtr e, byte[] dest, int size);
+ ///
+ /// load state from buffer
+ ///
+ /// context
+ /// buffer
+ /// length of buffer
+ /// string error
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr qn_state_load(IntPtr e, byte[] src, int size);
+ ///
+ /// query battery ram state
+ ///
+ /// context
+ /// true if battery backup sram exists
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern bool qn_has_battery_ram(IntPtr e);
+ ///
+ /// query battery ram size
+ ///
+ /// context
+ /// size is returned
+ /// string error
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr qn_battery_ram_size(IntPtr e, ref int size);
+ ///
+ /// save battery ram to buffer
+ ///
+ /// context
+ /// buffer
+ /// size
+ /// string error
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr qn_battery_ram_save(IntPtr e, byte[] dest, int size);
+ ///
+ /// load battery ram from buffer
+ ///
+ /// context
+ /// buffer
+ /// size
+ /// string error
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr qn_battery_ram_load(IntPtr e, byte[] src, int size);
+ ///
+ /// clear battery ram
+ ///
+ /// context
+ /// string error
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr qn_battery_ram_clear(IntPtr e);
+ ///
+ /// set sprite limit; does not affect emulation
+ ///
+ /// context
+ /// 0 to hide, 8 for normal, 64 for all
+ [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
+ public static extern void qn_set_sprite_limit(IntPtr e, int n);
+
+ ///
+ /// handle "string error" as returned by some quicknes functions
+ ///
+ ///
+ public static void ThrowStringError(IntPtr p)
+ {
+ if (p == IntPtr.Zero)
+ return;
+ string s = Marshal.PtrToStringAnsi(p);
+ throw new InvalidOperationException("LibQuickNES error: " + s);
+ }
+ }
+}
diff --git a/quicknes/bizinterface.cpp b/quicknes/bizinterface.cpp
new file mode 100644
index 0000000000..23207a48c5
--- /dev/null
+++ b/quicknes/bizinterface.cpp
@@ -0,0 +1,162 @@
+#include
+#include
+#include "nes_emu/Nes_Emu.h"
+
+// simulate the write so we'll know how long the buffer needs to be
+class Sim_Writer : public Data_Writer
+{
+ long size_;
+public:
+ Sim_Writer():size_(0) { }
+ error_t write(const void *, long size)
+ {
+ size_ += size;
+ return 0;
+ }
+ long size() const { return size_; }
+};
+
+
+#define EXPORT extern "C" __declspec(dllexport)
+
+EXPORT Nes_Emu *qn_new()
+{
+ return new Nes_Emu();
+}
+
+EXPORT void qn_delete(Nes_Emu *e)
+{
+ delete e;
+}
+
+EXPORT const char *qn_loadines(Nes_Emu *e, const void *data, int length)
+{
+ Mem_File_Reader r = Mem_File_Reader(data, length);
+ Auto_File_Reader a = Auto_File_Reader(r);
+ return e->load_ines(a);
+}
+
+EXPORT const char *qn_set_sample_rate(Nes_Emu *e, int rate)
+{
+ return e->set_sample_rate(rate);
+}
+
+EXPORT void qn_get_image_dimensions(Nes_Emu *e, int *width, int *height)
+{
+ if (width)
+ *width = e->buffer_width;
+ if (height)
+ *height = e->buffer_height();
+}
+
+EXPORT void qn_set_pixels(Nes_Emu *e, void *dest, int pitch)
+{
+ e->set_pixels(dest, pitch);
+}
+
+EXPORT const char *qn_emulate_frame(Nes_Emu *e, int pad1, int pad2)
+{
+ return e->emulate_frame(pad1, pad2);
+}
+
+EXPORT int qn_get_joypad_read_count(Nes_Emu *e)
+{
+ return e->frame().joypad_read_count;
+}
+
+EXPORT void qn_get_audio_info(Nes_Emu *e, int *sample_count, int *chan_count)
+{
+ if (sample_count)
+ *sample_count = e->frame().sample_count;
+ if (chan_count)
+ *chan_count = e->frame().chan_count;
+}
+
+EXPORT int qn_read_audio(Nes_Emu *e, short *dest, int max_samples)
+{
+ return e->read_samples(dest, max_samples);
+}
+
+EXPORT void qn_reset(Nes_Emu *e, int hard)
+{
+ e->reset(hard);
+}
+
+EXPORT const char *qn_state_size(Nes_Emu *e, int *size)
+{
+ Sim_Writer w = Sim_Writer();
+ Auto_File_Writer a = Auto_File_Writer(w);
+ const char *ret = e->save_state(a);
+ if (size)
+ *size = w.size();
+ return ret;
+}
+
+EXPORT const char *qn_state_save(Nes_Emu *e, void *dest, int size)
+{
+ Mem_Writer w = Mem_Writer(dest, size, 0);
+ Auto_File_Writer a = Auto_File_Writer(w);
+ const char *ret = e->save_state(a);
+ if (!ret && w.size() != size)
+ return "Buffer Underrun!";
+ return ret;
+}
+
+EXPORT const char *qn_state_load(Nes_Emu *e, const void *src, int size)
+{
+ Mem_File_Reader r = Mem_File_Reader(src, size);
+ Auto_File_Reader a = Auto_File_Reader(r);
+ return e->load_state(a);
+}
+
+EXPORT int qn_has_battery_ram(Nes_Emu *e)
+{
+ return e->has_battery_ram();
+}
+
+EXPORT const char *qn_battery_ram_size(Nes_Emu *e, int *size)
+{
+ Sim_Writer w = Sim_Writer();
+ Auto_File_Writer a = Auto_File_Writer(w);
+ const char *ret = e->save_battery_ram(a);
+ if (size)
+ *size = w.size();
+ return ret;
+}
+
+EXPORT const char *qn_battery_ram_save(Nes_Emu *e, void *dest, int size)
+{
+ Mem_Writer w = Mem_Writer(dest, size, 0);
+ Auto_File_Writer a = Auto_File_Writer(w);
+ const char *ret = e->save_battery_ram(a);
+ if (!ret && w.size() != size)
+ return "Buffer Underrun!";
+ return ret;
+}
+
+EXPORT const char *qn_battery_ram_load(Nes_Emu *e, const void *src, int size)
+{
+ Mem_File_Reader r = Mem_File_Reader(src, size);
+ Auto_File_Reader a = Auto_File_Reader(r);
+ return e->load_battery_ram(a);
+}
+
+EXPORT const char *qn_battery_ram_clear(Nes_Emu *e)
+{
+ int size = 0;
+ const char *ret = qn_battery_ram_size(e, &size);
+ if (ret)
+ return ret;
+ void *data = std::malloc(size);
+ if (!data)
+ return "Out of Memory!";
+ std::memset(data, 0xff, size);
+ ret = qn_battery_ram_load(e, data, size);
+ std::free(data);
+ return ret;
+}
+
+EXPORT void qn_set_sprite_limit(Nes_Emu *e, int n)
+{
+ e->set_sprite_mode((Nes_Emu::sprite_mode_t)n);
+}
diff --git a/quicknes/libquicknes/libquicknes.vcxproj b/quicknes/libquicknes/libquicknes.vcxproj
index 1ba1106a78..63d052808a 100644
--- a/quicknes/libquicknes/libquicknes.vcxproj
+++ b/quicknes/libquicknes/libquicknes.vcxproj
@@ -11,6 +11,7 @@
+
@@ -85,7 +86,7 @@
Disabled
4244;4800;4804;4996
$(ProjectDir)\..
- _WINDLL;%(PreprocessorDefinitions);DLLEXPORT=__declspec(dllexport)
+ _WINDLL;%(PreprocessorDefinitions);DISABLE_AUTO_FILE
true
@@ -102,7 +103,7 @@
true
4244;4800;4804;4996
$(ProjectDir)\..
- _WINDLL;%(PreprocessorDefinitions);DLLEXPORT=__declspec(dllexport)
+ _WINDLL;%(PreprocessorDefinitions);DISABLE_AUTO_FILE
true
diff --git a/quicknes/libquicknes/libquicknes.vcxproj.filters b/quicknes/libquicknes/libquicknes.vcxproj.filters
index e4c5c91b74..73b372c67e 100644
--- a/quicknes/libquicknes/libquicknes.vcxproj.filters
+++ b/quicknes/libquicknes/libquicknes.vcxproj.filters
@@ -144,5 +144,8 @@
Source Files\fex
+
+ Source Files
+
\ No newline at end of file