From b85d9d9d916c88829b976985e06111487ead06d4 Mon Sep 17 00:00:00 2001 From: goyuken Date: Sun, 5 Jan 2014 20:58:36 +0000 Subject: [PATCH] WARNING: use NES core only at low volume and no headphones --- BizHawk.Client.Common/RomLoader.cs | 20 +- .../BizHawk.Emulation.Cores.csproj | 2 + .../Consoles/Nintendo/QuickNES/LibQuickNES.cs | 9 +- .../Consoles/Nintendo/QuickNES/QuickNES.cs | 362 ++++++++++++++++++ output/dll/libquicknes.dll | Bin 0 -> 78336 bytes quicknes/bizinterface.cpp | 25 ++ quicknes/libquicknes/libquicknes.vcxproj | 4 +- 7 files changed, 413 insertions(+), 9 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs create mode 100644 output/dll/libquicknes.dll diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 57348573ba..03a63e22bd 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -14,6 +14,7 @@ using BizHawk.Emulation.Cores.Nintendo.Gameboy; using BizHawk.Emulation.Cores.Nintendo.GBA; using BizHawk.Emulation.Cores.Nintendo.N64; using BizHawk.Emulation.Cores.Nintendo.NES; +using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES; using BizHawk.Emulation.Cores.Nintendo.SNES; using BizHawk.Emulation.Cores.PCEngine; using BizHawk.Emulation.Cores.Sega.MasterSystem; @@ -350,12 +351,19 @@ namespace BizHawk.Client.Common nextEmulator = new TI83(nextComm, game, rom.RomData); break; case "NES": - nextEmulator = new NES( - nextComm, - game, - rom.FileData, - GetCoreSettings(), - GetCoreSyncSettings()); + if (false) + { + nextEmulator = new NES( + nextComm, + game, + rom.FileData, + GetCoreSettings(), + GetCoreSyncSettings()); + } + else + { + nextEmulator = new QuickNES(nextComm, rom.FileData); + } break; case "GB": case "GBC": diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index b8f7f9f388..f502281ff3 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -392,6 +392,8 @@ + + diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/LibQuickNES.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/LibQuickNES.cs index b5c1c7a4f2..909e793572 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/LibQuickNES.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/LibQuickNES.cs @@ -54,7 +54,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES /// 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); + public static extern void qn_set_pixels(IntPtr e, IntPtr dest, int pitch); /// /// emulate a single frame /// @@ -65,6 +65,13 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr qn_emulate_frame(IntPtr e, int pad1, int pad2); /// + /// blit to rgb32 + /// + /// Context + /// rgb32 256x240 packed + [DllImport(dllname, CallingConvention = CallingConvention.Cdecl)] + public static extern void qn_blit(IntPtr e, IntPtr dest); + /// /// get number of times joypad was read in most recent frame /// /// context diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs new file mode 100644 index 0000000000..0e342e6fd8 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.cs @@ -0,0 +1,362 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BizHawk.Emulation.Common; +using System.Runtime.InteropServices; + +namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES +{ + public class QuickNES : IEmulator, IVideoProvider, ISyncSoundProvider + { + public QuickNES(CoreComm nextComm, byte[] Rom) + { + CoreComm = nextComm; + + Context = LibQuickNES.qn_new(); + if (Context == IntPtr.Zero) + throw new InvalidOperationException("qn_new() returned NULL"); + try + { + LibQuickNES.ThrowStringError(LibQuickNES.qn_loadines(Context, Rom, Rom.Length)); + + InitSaveRamBuff(); + InitSaveStateBuff(); + InitVideo(); + InitAudio(); + } + catch + { + Dispose(); + throw; + } + } + + #region Controller + + public ControllerDefinition ControllerDefinition { get { return Emulation.Cores.Nintendo.NES.NES.NESController; } } + public IController Controller { get; set; } + + void SetPads(out int j1, out int j2) + { + j1 = 0; + j2 = 0; + if (Controller["P1 A"]) + j1 |= 1; + if (Controller["P1 B"]) + j1 |= 2; + if (Controller["P1 Select"]) + j1 |= 4; + if (Controller["P1 Start"]) + j1 |= 8; + if (Controller["P1 Up"]) + j1 |= 16; + if (Controller["P1 Down"]) + j1 |= 32; + if (Controller["P1 Left"]) + j1 |= 64; + if (Controller["P1 Right"]) + j1 |= 128; + if (Controller["P2 A"]) + j2 |= 1; + if (Controller["P2 B"]) + j2 |= 2; + if (Controller["P2 Select"]) + j2 |= 4; + if (Controller["P2 Start"]) + j2 |= 8; + if (Controller["P2 Up"]) + j2 |= 16; + if (Controller["P2 Down"]) + j2 |= 32; + if (Controller["P2 Left"]) + j2 |= 64; + if (Controller["P2 Right"]) + j2 |= 128; + } + + #endregion + + public void FrameAdvance(bool render, bool rendersound = true) + { + if (Controller["Power"]) + LibQuickNES.qn_reset(Context, true); + if (Controller["Reset"]) + LibQuickNES.qn_reset(Context, false); + + int j1, j2; + SetPads(out j1, out j2); + + Frame++; + LibQuickNES.ThrowStringError(LibQuickNES.qn_emulate_frame(Context, j1, j2)); + IsLagFrame = LibQuickNES.qn_get_joypad_read_count(Context) == 0; + if (IsLagFrame) + LagCount++; + + Blit(); + DrainAudio(); + } + + #region state + + IntPtr Context; + + public int Frame { get; private set; } + public int LagCount { get; set; } + public bool IsLagFrame { get; private set; } + + #endregion + + public string SystemId { get { return "NES"; } } + public bool DeterministicEmulation { get { return true; } } + public string BoardName { get { return null; } } // TODO + + #region saveram + + byte[] SaveRamBuff; + + void InitSaveRamBuff() + { + int size = 0; + LibQuickNES.ThrowStringError(LibQuickNES.qn_battery_ram_size(Context, ref size)); + SaveRamBuff = new byte[size]; + } + + public byte[] ReadSaveRam() + { + LibQuickNES.ThrowStringError(LibQuickNES.qn_battery_ram_save(Context, SaveRamBuff, SaveRamBuff.Length)); + return SaveRamBuff; + } + + public void StoreSaveRam(byte[] data) + { + LibQuickNES.ThrowStringError(LibQuickNES.qn_battery_ram_load(Context, data, data.Length)); + } + + public void ClearSaveRam() + { + LibQuickNES.ThrowStringError(LibQuickNES.qn_battery_ram_clear(Context)); + } + + public bool SaveRamModified + { + get + { + return LibQuickNES.qn_has_battery_ram(Context); + } + set + { + throw new Exception(); + } + } + + #endregion + + public void ResetCounters() + { + Frame = 0; + IsLagFrame = false; + LagCount = 0; + } + + #region savestates + + byte[] SaveStateBuff; + byte[] SaveStateBuff2; + + void InitSaveStateBuff() + { + int size = 0; + LibQuickNES.ThrowStringError(LibQuickNES.qn_state_size(Context, ref size)); + SaveStateBuff = new byte[size]; + SaveStateBuff2 = new byte[size + 13]; + } + + public void SaveStateText(System.IO.TextWriter writer) + { + throw new NotImplementedException(); + } + + public void LoadStateText(System.IO.TextReader reader) + { + throw new NotImplementedException(); + } + + public void SaveStateBinary(System.IO.BinaryWriter writer) + { + LibQuickNES.ThrowStringError(LibQuickNES.qn_state_save(Context, SaveStateBuff, SaveStateBuff.Length)); + writer.Write(SaveStateBuff.Length); + writer.Write(SaveStateBuff); + // other variables + writer.Write(IsLagFrame); + writer.Write(LagCount); + writer.Write(Frame); + } + + public void LoadStateBinary(System.IO.BinaryReader reader) + { + int len = reader.ReadInt32(); + if (len != SaveStateBuff.Length) + throw new InvalidOperationException("Unexpected savestate buffer length!"); + reader.Read(SaveStateBuff, 0, SaveStateBuff.Length); + LibQuickNES.ThrowStringError(LibQuickNES.qn_state_load(Context, SaveStateBuff, SaveStateBuff.Length)); + // other variables + IsLagFrame = reader.ReadBoolean(); + LagCount = reader.ReadInt32(); + Frame = reader.ReadInt32(); + } + + public byte[] SaveStateBinary() + { + var ms = new System.IO.MemoryStream(SaveStateBuff2, true); + var bw = new System.IO.BinaryWriter(ms); + SaveStateBinary(bw); + bw.Flush(); + if (ms.Position != SaveStateBuff2.Length) + throw new InvalidOperationException("Unexpected savestate length!"); + bw.Close(); + return SaveStateBuff2; + } + + public bool BinarySaveStatesPreferred { get { return true; } } + + #endregion + + public CoreComm CoreComm + { + get; + private set; + } + + public MemoryDomainList MemoryDomains + { + get { return MemoryDomainList.GetDummyList(); } + } + + public List> GetCpuFlagsAndRegisters() + { + return new List>(); + } + + #region settings + + public object GetSettings() + { + return null; + } + + public object GetSyncSettings() + { + return null; + } + + public bool PutSettings(object o) + { + return false; + } + + public bool PutSyncSettings(object o) + { + return false; + } + + #endregion + + public void Dispose() + { + if (Context != IntPtr.Zero) + { + LibQuickNES.qn_delete(Context); + Context = IntPtr.Zero; + } + if (VideoInput != null) + { + VideoInputH.Free(); + VideoInput = null; + } + if (VideoOutput != null) + { + VideoOutputH.Free(); + VideoOutput = null; + } + } + + #region VideoProvider + + int[] VideoOutput; + byte[] VideoInput; + GCHandle VideoInputH; + GCHandle VideoOutputH; + + void InitVideo() + { + int w = 0, h = 0; + LibQuickNES.qn_get_image_dimensions(Context, ref w, ref h); + VideoInput = new byte[w * h]; + VideoInputH = GCHandle.Alloc(VideoInput, GCHandleType.Pinned); + LibQuickNES.qn_set_pixels(Context, VideoInputH.AddrOfPinnedObject(), w); + VideoOutput = new int[256 * 240]; + VideoOutputH = GCHandle.Alloc(VideoOutput, GCHandleType.Pinned); + } + + void Blit() + { + LibQuickNES.qn_blit(Context, VideoOutputH.AddrOfPinnedObject()); + } + + public IVideoProvider VideoProvider { get { return this; } } + public int[] GetVideoBuffer() { return VideoOutput; } + public int VirtualWidth { get { return 292; } } // probably different on pal + public int BufferWidth { get { return 256; } } + public int BufferHeight { get { return 240; } } + public int BackgroundColor { get { return unchecked((int)0xff000000); } } + + #endregion + + #region SoundProvider + + public ISoundProvider SoundProvider { get { return null; } } + public ISyncSoundProvider SyncSoundProvider { get { return this; } } + public bool StartAsyncSound() { return false; } + public void EndAsyncSound() { } + + void InitAudio() + { + LibQuickNES.ThrowStringError(LibQuickNES.qn_set_sample_rate(Context, 44100)); + } + + void DrainAudio() + { + NumSamples = LibQuickNES.qn_read_audio(Context, MonoBuff, 1024); + unsafe + { + fixed (short *_src = &MonoBuff[0], _dst = &StereoBuff[0]) + { + short* src = _src; + short* dst = _dst; + for (int i = 0; i < NumSamples; i++) + { + *dst++ = *src; + *dst++ = *src++; + } + } + } + } + + short[] MonoBuff = new short[1024]; + short[] StereoBuff = new short[2048]; + int NumSamples = 0; + + public void GetSamples(out short[] samples, out int nsamp) + { + samples = StereoBuff; + nsamp = NumSamples; + } + + public void DiscardSamples() + { + } + + #endregion + } +} diff --git a/output/dll/libquicknes.dll b/output/dll/libquicknes.dll new file mode 100644 index 0000000000000000000000000000000000000000..1e9392ab7b5ebde115110f9dd03c1f68782fc184 GIT binary patch literal 78336 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�m4kKUZb7d=aAkJE(B`k^9eG`7GR%|9Lb_gki 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 LMuGoZ6!`xDnlmLA literal 0 HcmV?d00001 diff --git a/quicknes/bizinterface.cpp b/quicknes/bizinterface.cpp index 23207a48c5..2f3b29aaf5 100644 --- a/quicknes/bizinterface.cpp +++ b/quicknes/bizinterface.cpp @@ -59,6 +59,31 @@ EXPORT const char *qn_emulate_frame(Nes_Emu *e, int pad1, int pad2) return e->emulate_frame(pad1, pad2); } +EXPORT void qn_blit(Nes_Emu *e, char *dest) +{ + // what is the point of the 256 color bitmap and the dynamic color allocation to it? + // why not just render directly to a 512 color bitmap with static palette positions? + + const char *src = (const char *)e->frame().pixels; + const int srcpitch = e->frame().pitch; + const char *srcend = src + e->image_height * srcpitch; + + const short *lut = e->frame().palette; + const Nes_Emu::rgb_t *colors = e->nes_colors; + + for (; src < srcend; src += srcpitch) + { + for (int i = 0; i < 256; i++) + { + const Nes_Emu::rgb_t *c = colors + lut[src[i]]; + *dest++ = c->blue; + *dest++ = c->green; + *dest++ = c->red; + *dest++ = 0xff; + } + } +} + EXPORT int qn_get_joypad_read_count(Nes_Emu *e) { return e->frame().joypad_read_count; diff --git a/quicknes/libquicknes/libquicknes.vcxproj b/quicknes/libquicknes/libquicknes.vcxproj index 63d052808a..e07d809c69 100644 --- a/quicknes/libquicknes/libquicknes.vcxproj +++ b/quicknes/libquicknes/libquicknes.vcxproj @@ -86,7 +86,7 @@ Disabled 4244;4800;4804;4996 $(ProjectDir)\.. - _WINDLL;%(PreprocessorDefinitions);DISABLE_AUTO_FILE + _WINDLL;%(PreprocessorDefinitions);DISABLE_AUTO_FILE;__LIBRETRO__ true @@ -103,7 +103,7 @@ true 4244;4800;4804;4996 $(ProjectDir)\.. - _WINDLL;%(PreprocessorDefinitions);DISABLE_AUTO_FILE + _WINDLL;%(PreprocessorDefinitions);DISABLE_AUTO_FILE;__LIBRETRO__ true