using System; using System.Collections.Generic; using System.Linq; using System.Text; using BizHawk.Common; using BizHawk.Emulation.Common; using System.IO; using Newtonsoft.Json; using System.Runtime.InteropServices; namespace BizHawk.Emulation.Cores.WonderSwan { [CoreAttributes("Cygne/Mednafen", "Dox", true, true, "0.9.36.5", "http://mednafen.sourceforge.net/")] [ServiceNotApplicable(typeof(IDriveLight))] public partial class WonderSwan : IEmulator, IVideoProvider, ISyncSoundProvider, ISaveRam, IInputPollable, IDebuggable { [CoreConstructor("WSWAN")] public WonderSwan(CoreComm comm, byte[] file, bool deterministic, object Settings, object SyncSettings) { ServiceProvider = new BasicServiceProvider(this); CoreComm = comm; _Settings = (Settings)Settings ?? new Settings(); _SyncSettings = (SyncSettings)SyncSettings ?? new SyncSettings(); DeterministicEmulation = deterministic; // when true, remember to force the RTC flag! Core = BizSwan.bizswan_new(); if (Core == IntPtr.Zero) throw new InvalidOperationException("bizswan_new() returned NULL!"); try { var ss = _SyncSettings.GetNativeSettings(); if (deterministic) ss.userealtime = false; bool rotate = false; if (!BizSwan.bizswan_load(Core, file, file.Length, ref ss, ref rotate)) throw new InvalidOperationException("bizswan_load() returned FALSE!"); CoreComm.VsyncNum = 3072000; // master CPU clock, also pixel clock CoreComm.VsyncDen = (144 + 15) * (224 + 32); // 144 vislines, 15 vblank lines; 224 vispixels, 32 hblank pixels saverambuff = new byte[BizSwan.bizswan_saveramsize(Core)]; InitVideo(rotate); PutSettings(_Settings); SetMemoryDomains(); savebuff = new byte[BizSwan.bizswan_binstatesize(Core)]; savebuff2 = new byte[savebuff.Length + 13]; InitDebugCallbacks(); } catch { Dispose(); throw; } } public IEmulatorServiceProvider ServiceProvider { get; private set; } public void Dispose() { if (Core != IntPtr.Zero) { BizSwan.bizswan_delete(Core); Core = IntPtr.Zero; } } public void FrameAdvance(bool render, bool rendersound = true) { Frame++; IsLagFrame = true; if (Controller["Power"]) BizSwan.bizswan_reset(Core); bool rotate = false; int soundbuffsize = sbuff.Length; IsLagFrame = BizSwan.bizswan_advance(Core, GetButtons(), !render, vbuff, sbuff, ref soundbuffsize, ref rotate); if (soundbuffsize == sbuff.Length) throw new Exception(); sbuffcontains = soundbuffsize; InitVideo(rotate); if (IsLagFrame) LagCount++; } public CoreComm CoreComm { get; private set; } public void ResetCounters() { Frame = 0; IsLagFrame = false; LagCount = 0; } IntPtr Core; public int Frame { get; private set; } public int LagCount { get; set; } public bool IsLagFrame { get; private set; } public string SystemId { get { return "WSWAN"; } } public bool DeterministicEmulation { get; private set; } public string BoardName { get { return null; } } #region SaveRam byte[] saverambuff; public byte[] CloneSaveRam() { if (!BizSwan.bizswan_saveramsave(Core, saverambuff, saverambuff.Length)) throw new InvalidOperationException("bizswan_saveramsave() returned false!"); return (byte[])saverambuff.Clone(); } public void StoreSaveRam(byte[] data) { if (!BizSwan.bizswan_saveramload(Core, data, data.Length)) throw new InvalidOperationException("bizswan_saveramload() returned false!"); } public bool SaveRamModified { get { return BizSwan.bizswan_saveramsize(Core) > 0; } } #endregion #region Debugging void SetMemoryDomains() { var mmd = new List(); for (int i = 0; ; i++) { IntPtr name; int size; IntPtr data; if (!BizSwan.bizswan_getmemoryarea(Core, i, out name, out size, out data)) break; if (size == 0) continue; string sname = Marshal.PtrToStringAnsi(name); mmd.Add(MemoryDomain.FromIntPtr(sname, size, MemoryDomain.Endian.Little, data)); } MemoryDomains = new MemoryDomainList(mmd, 0); (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); } private readonly InputCallbackSystem _inputCallbacks = new InputCallbackSystem(); public IInputCallbackSystem InputCallbacks { get { return _inputCallbacks; } } private readonly MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(); public IMemoryCallbackSystem MemoryCallbacks { get { return _memorycallbacks; } } private IMemoryDomains MemoryDomains; public IDictionary GetCpuFlagsAndRegisters() { var ret = new Dictionary(); for (int i = (int)BizSwan.NecRegsMin; i <= (int)BizSwan.NecRegsMax; i++) { BizSwan.NecRegs en = (BizSwan.NecRegs)i; uint val = BizSwan.bizswan_getnecreg(Core, en); ret[Enum.GetName(typeof(BizSwan.NecRegs), en)] = (ushort)val; } return ret; } [FeatureNotImplemented] public void SetCpuRegister(string register, int value) { throw new NotImplementedException(); } public bool CanStep(StepType type) { return false; } [FeatureNotImplemented] public void Step(StepType type) { throw new NotImplementedException(); } BizSwan.MemoryCallback ReadCallbackD; BizSwan.MemoryCallback WriteCallbackD; BizSwan.MemoryCallback ExecCallbackD; BizSwan.ButtonCallback ButtonCallbackD; void ReadCallback(uint addr) { MemoryCallbacks.CallReads(addr); } void WriteCallback(uint addr) { MemoryCallbacks.CallWrites(addr); } void ExecCallback(uint addr) { MemoryCallbacks.CallExecutes(addr); } void ButtonCallback() { InputCallbacks.Call(); } void InitDebugCallbacks() { ReadCallbackD = new BizSwan.MemoryCallback(ReadCallback); WriteCallbackD = new BizSwan.MemoryCallback(WriteCallback); ExecCallbackD = new BizSwan.MemoryCallback(ExecCallback); ButtonCallbackD = new BizSwan.ButtonCallback(ButtonCallback); _inputCallbacks.ActiveChanged += SetInputCallback; _memorycallbacks.ActiveChanged += SetMemoryCallbacks; } void SetInputCallback() { BizSwan.bizswan_setbuttoncallback(Core, InputCallbacks.Any() ? ButtonCallbackD : null); } void SetMemoryCallbacks() { BizSwan.bizswan_setmemorycallbacks(Core, MemoryCallbacks.HasReads ? ReadCallbackD : null, MemoryCallbacks.HasWrites ? WriteCallbackD : null, MemoryCallbacks.HasExecutes ? ExecCallbackD : null); } #endregion #region IVideoProvider public IVideoProvider VideoProvider { get { return this; } } void InitVideo(bool rotate) { if (rotate) { BufferWidth = 144; BufferHeight = 224; } else { BufferWidth = 224; BufferHeight = 144; } } private int[] vbuff = new int[224 * 144]; public int[] GetVideoBuffer() { return vbuff; } public int VirtualWidth { get { return BufferWidth; } } public int VirtualHeight { get { return BufferHeight; } } public int BufferWidth { get; private set; } public int BufferHeight { get; private set; } public int BackgroundColor { get { return unchecked((int)0xff000000); } } #endregion #region ISoundProvider private short[] sbuff = new short[1536]; private int sbuffcontains = 0; public ISoundProvider SoundProvider { get { throw new InvalidOperationException(); } } public ISyncSoundProvider SyncSoundProvider { get { return this; } } public bool StartAsyncSound() { return false; } public void EndAsyncSound() { } public void GetSamples(out short[] samples, out int nsamp) { samples = sbuff; nsamp = sbuffcontains; } public void DiscardSamples() { sbuffcontains = 0; } #endregion } }