From c306e61d5b99c11cd5253aa4ff238b692b6634f2 Mon Sep 17 00:00:00 2001 From: goyuken Date: Fri, 30 May 2014 05:09:54 +0000 Subject: [PATCH] mednafen 0.9.34.1 "cygne" (wonderswan) --- BizHawk.Client.Common/RomLoader.cs | 4 + BizHawk.Emulation.Common/Database/Database.cs | 5 + .../BizHawk.Emulation.Cores.csproj | 2 + .../Consoles/WonderSwan/BizSwan.cs | 87 ++ .../Consoles/WonderSwan/WonderSwan.cs | 263 +++++ output/dll/bizswan.dll | Bin 0 -> 231424 bytes wonderswan/bizswan/bizswan.sln | 20 + wonderswan/bizswan/bizswan.vcxproj | 110 ++ wonderswan/bizswan/bizswan.vcxproj.filters | 122 ++ wonderswan/blip/Blip_Buffer.cpp | 457 ++++++++ wonderswan/blip/Blip_Buffer.h | 498 ++++++++ wonderswan/eeprom.cpp | 208 ++++ wonderswan/eeprom.h | 32 + wonderswan/gfx.cpp | 601 ++++++++++ wonderswan/gfx.h | 91 ++ wonderswan/interrupt.cpp | 77 ++ wonderswan/interrupt.h | 48 + wonderswan/main.cpp | 94 ++ wonderswan/mednafen/state.h | 4 + wonderswan/mednafen/types.h | 201 ++++ wonderswan/memory.cpp | 390 +++++++ wonderswan/memory.h | 85 ++ wonderswan/msvc/changelog.txt | 138 +++ wonderswan/msvc/inttypes.h | 305 +++++ wonderswan/msvc/stdint.h | 247 ++++ wonderswan/rtc.cpp | 98 ++ wonderswan/rtc.h | 28 + wonderswan/sound.cpp | 386 ++++++ wonderswan/sound.h | 71 ++ wonderswan/start.inc | 269 +++++ wonderswan/system.cpp | 315 +++++ wonderswan/system.h | 68 ++ wonderswan/tcache.cpp | 290 +++++ wonderswan/v30mz-ea.inc | 0 wonderswan/v30mz-modrm.inc | 94 ++ wonderswan/v30mz-private.h | 252 ++++ wonderswan/v30mz.cpp | 1035 +++++++++++++++++ wonderswan/v30mz.h | 166 +++ wonderswan/wstech24.txt | 757 ++++++++++++ wonderswan/wswan.h | 34 + 40 files changed, 7952 insertions(+) create mode 100644 BizHawk.Emulation.Cores/Consoles/WonderSwan/BizSwan.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.cs create mode 100644 output/dll/bizswan.dll create mode 100644 wonderswan/bizswan/bizswan.sln create mode 100644 wonderswan/bizswan/bizswan.vcxproj create mode 100644 wonderswan/bizswan/bizswan.vcxproj.filters create mode 100644 wonderswan/blip/Blip_Buffer.cpp create mode 100644 wonderswan/blip/Blip_Buffer.h create mode 100644 wonderswan/eeprom.cpp create mode 100644 wonderswan/eeprom.h create mode 100644 wonderswan/gfx.cpp create mode 100644 wonderswan/gfx.h create mode 100644 wonderswan/interrupt.cpp create mode 100644 wonderswan/interrupt.h create mode 100644 wonderswan/main.cpp create mode 100644 wonderswan/mednafen/state.h create mode 100644 wonderswan/mednafen/types.h create mode 100644 wonderswan/memory.cpp create mode 100644 wonderswan/memory.h create mode 100644 wonderswan/msvc/changelog.txt create mode 100644 wonderswan/msvc/inttypes.h create mode 100644 wonderswan/msvc/stdint.h create mode 100644 wonderswan/rtc.cpp create mode 100644 wonderswan/rtc.h create mode 100644 wonderswan/sound.cpp create mode 100644 wonderswan/sound.h create mode 100644 wonderswan/start.inc create mode 100644 wonderswan/system.cpp create mode 100644 wonderswan/system.h create mode 100644 wonderswan/tcache.cpp create mode 100644 wonderswan/v30mz-ea.inc create mode 100644 wonderswan/v30mz-modrm.inc create mode 100644 wonderswan/v30mz-private.h create mode 100644 wonderswan/v30mz.cpp create mode 100644 wonderswan/v30mz.h create mode 100644 wonderswan/wstech24.txt create mode 100644 wonderswan/wswan.h diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 5f989de38a..a93abaefc7 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -23,6 +23,7 @@ using BizHawk.Emulation.Cores.Sega.Saturn; using BizHawk.Emulation.Cores.Sony.PSP; using BizHawk.Emulation.Cores.Sony.PSX; using BizHawk.Emulation.DiscSystem; +using BizHawk.Emulation.Cores.WonderSwan; namespace BizHawk.Client.Common { @@ -413,6 +414,9 @@ namespace BizHawk.Client.Common case "N64": nextEmulator = new N64(nextComm, game, rom.RomData, GetCoreSyncSettings()); break; + case "WSWAN": + nextEmulator = new WonderSwan(nextComm, rom.RomData); + break; case "DEBUG": if (VersionInfo.INTERIM) { diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index 6510129f01..01e5c62e19 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -324,6 +324,11 @@ namespace BizHawk.Emulation.Common case ".DEBUG": game.System = "DEBUG"; break; + + case ".WS": + case ".WSC": + game.System = "WSWAN"; + break; } game.Name = Path.GetFileNameWithoutExtension(fileName).Replace('_', ' '); diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index abe1a599d5..fedf87a821 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -470,6 +470,8 @@ + + diff --git a/BizHawk.Emulation.Cores/Consoles/WonderSwan/BizSwan.cs b/BizHawk.Emulation.Cores/Consoles/WonderSwan/BizSwan.cs new file mode 100644 index 0000000000..81e10ed943 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/WonderSwan/BizSwan.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace BizHawk.Emulation.Cores.WonderSwan +{ + public static class BizSwan + { + const CallingConvention cc = CallingConvention.Cdecl; + const string dd = "bizswan.dll"; + + [DllImport(dd, CallingConvention = cc)] + public static extern IntPtr bizswan_new(); + + [DllImport(dd, CallingConvention = cc)] + public static extern void bizswan_delete(IntPtr core); + + [DllImport(dd, CallingConvention = cc)] + public static extern void bizswan_reset(IntPtr core); + + [DllImport(dd, CallingConvention = cc)] + public static extern void bizswan_advance(IntPtr core, Buttons buttons, bool novideo, int[] surface, short[] soundbuff, ref int soundbuffsize); + + [DllImport(dd, CallingConvention = cc)] + public static extern bool bizswan_load(IntPtr core, byte[] data, int length, [In] ref Settings settings); + + [Flags] + public enum Buttons : ushort + { + UpX = 0x0001, + DownX = 0x0002, + LeftX = 0x0004, + RightX = 0x0008, + UpY = 0x0010, + DownY = 0x0020, + LeftY = 0x0040, + RightY = 0x0080, + Start = 0x0100, + B = 0x0200, + A = 0x0400, + } + + public enum Language : byte + { + Japanese = 0, + English = 1 + } + + public enum Bloodtype : byte + { + A = 1, + B = 2, + O = 3, + AB = 4 + } + + public enum Gender : byte + { + Male = 1, + Female = 2 + } + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Settings + { + public ushort byear; + public byte bmonth; + public byte bday; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public byte[] name; + public Language language; + public Gender sex; + public Bloodtype blood; + [MarshalAs(UnmanagedType.U1)] + public bool rotateinput; + + public void SetName(string newname) + { + byte[] data = Encoding.ASCII.GetBytes(newname); + name = new byte[17]; + Buffer.BlockCopy(data, 0, name, 0, Math.Min(data.Length, name.Length)); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.cs b/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.cs new file mode 100644 index 0000000000..54bea55575 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System.IO; + +namespace BizHawk.Emulation.Cores.WonderSwan +{ + [CoreAttributes("Mednafen/Cygne", "Dox", true, false)] + public class WonderSwan : IEmulator, IVideoProvider, ISyncSoundProvider + { + #region Controller + + public static readonly ControllerDefinition WonderSwanController = new ControllerDefinition + { + Name = "WonderSwan Controller", + BoolButtons = { "Up X, Down X, Left X, Right X, Up Y, Down Y, Left Y, Right Y, Start, B, A, Power" } + }; + public ControllerDefinition ControllerDefinition { get { return WonderSwanController; } } + public IController Controller { get; set; } + + BizSwan.Buttons GetButtons() + { + BizSwan.Buttons ret = 0; + if (Controller["Up X"]) ret |= BizSwan.Buttons.UpX; + if (Controller["Down X"]) ret |= BizSwan.Buttons.DownX; + if (Controller["Left X"]) ret |= BizSwan.Buttons.LeftX; + if (Controller["Right X"]) ret |= BizSwan.Buttons.RightX; + if (Controller["Up Y"]) ret |= BizSwan.Buttons.UpY; + if (Controller["Down Y"]) ret |= BizSwan.Buttons.DownY; + if (Controller["Left Y"]) ret |= BizSwan.Buttons.LeftY; + if (Controller["Right Y"]) ret |= BizSwan.Buttons.RightY; + if (Controller["Start"]) ret |= BizSwan.Buttons.Start; + if (Controller["B"]) ret |= BizSwan.Buttons.B; + if (Controller["A"]) ret |= BizSwan.Buttons.A; + return ret; + } + + #endregion + + public WonderSwan(CoreComm comm, byte[] rom) + { + this.CoreComm = comm; + Core = BizSwan.bizswan_new(); + if (Core == IntPtr.Zero) + throw new InvalidOperationException("bizswan_new() returned NULL!"); + try + { + var ss = new BizSwan.Settings + { + sex = BizSwan.Gender.Male, + blood = BizSwan.Bloodtype.A, + language = BizSwan.Language.Japanese, + rotateinput = false, // TODO + bday = 5, + bmonth = 12, + byear = 1968 + }; + ss.SetName("LaForge"); + + if (!BizSwan.bizswan_load(Core, rom, rom.Length, ref ss)) + 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 + } + catch + { + Dispose(); + throw; + } + } + + 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); + + int soundbuffsize = sbuff.Length; + BizSwan.bizswan_advance(Core, GetButtons(), !render, vbuff, sbuff, ref soundbuffsize); + if (soundbuffsize == sbuff.Length) + throw new Exception(); + sbuffcontains = soundbuffsize; + + IsLagFrame = false; // TODO + if (IsLagFrame) + LagCount++; + } + + 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 { return true; } } + public string BoardName { get { return null; } } + + #region SaveRam + + public byte[] ReadSaveRam() + { + return new byte[0]; + } + + public void StoreSaveRam(byte[] data) + { + + } + + public void ClearSaveRam() + { + + } + + public bool SaveRamModified + { + get + { + return false; + } + set + { + + } + } + + #endregion + + public void ResetCounters() + { + throw new NotImplementedException(); + } + + #region Savestates + + public void SaveStateText(TextWriter writer) + { + } + + public void LoadStateText(TextReader reader) + { + } + + public void SaveStateBinary(BinaryWriter writer) + { + } + + public void LoadStateBinary(BinaryReader reader) + { + } + + public byte[] SaveStateBinary() + { + return new byte[0]; + } + + public bool BinarySaveStatesPreferred + { + get { return true; } + } + + #endregion + + public CoreComm CoreComm { get; private set; } + + #region Debugging + + public MemoryDomainList MemoryDomains + { + get { throw new NotImplementedException(); } + } + + public Dictionary GetCpuFlagsAndRegisters() + { + throw new NotImplementedException(); + } + + #endregion + + #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 + + #region IVideoProvider + + public IVideoProvider VideoProvider { get { return this; } } + + private int[] vbuff = new int[224 * 144]; + + public int[] GetVideoBuffer() + { + return vbuff; + } + + public int VirtualWidth { get { return 224; } } + public int VirtualHeight { get { return 144; } } + public int BufferWidth { get { return 224; } } + public int BufferHeight { get { return 144; } } + 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 + } +} diff --git a/output/dll/bizswan.dll b/output/dll/bizswan.dll new file mode 100644 index 0000000000000000000000000000000000000000..5000718d6328ca46975533d69604198b55284c62 GIT binary patch literal 231424 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 literal 0 HcmV?d00001 diff --git a/wonderswan/bizswan/bizswan.sln b/wonderswan/bizswan/bizswan.sln new file mode 100644 index 0000000000..742e57fa9b --- /dev/null +++ b/wonderswan/bizswan/bizswan.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bizswan", "bizswan.vcxproj", "{F92A3734-EAE1-44D9-B474-FF80AE039790}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F92A3734-EAE1-44D9-B474-FF80AE039790}.Debug|Win32.ActiveCfg = Debug|Win32 + {F92A3734-EAE1-44D9-B474-FF80AE039790}.Debug|Win32.Build.0 = Debug|Win32 + {F92A3734-EAE1-44D9-B474-FF80AE039790}.Release|Win32.ActiveCfg = Release|Win32 + {F92A3734-EAE1-44D9-B474-FF80AE039790}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/wonderswan/bizswan/bizswan.vcxproj b/wonderswan/bizswan/bizswan.vcxproj new file mode 100644 index 0000000000..fd15924816 --- /dev/null +++ b/wonderswan/bizswan/bizswan.vcxproj @@ -0,0 +1,110 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {F92A3734-EAE1-44D9-B474-FF80AE039790} + bizswan + + + + DynamicLibrary + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + $(ProjectDir)\..;$(ProjectDir)\..\msvc + _WINDLL;%(PreprocessorDefinitions);LSB_FIRST + + + true + + + copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\..\output\dll\$(TargetFileName) + + + + + Level3 + MaxSpeed + true + true + $(ProjectDir)\..;$(ProjectDir)\..\msvc + _WINDLL;%(PreprocessorDefinitions);LSB_FIRST + + + true + true + true + + + copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\..\output\dll\$(TargetFileName) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wonderswan/bizswan/bizswan.vcxproj.filters b/wonderswan/bizswan/bizswan.vcxproj.filters new file mode 100644 index 0000000000..97d6f1b7b6 --- /dev/null +++ b/wonderswan/bizswan/bizswan.vcxproj.filters @@ -0,0 +1,122 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {d0b85f88-6eca-4fa8-bf29-4d75ff7471fe} + + + {fc621339-f39c-496f-8085-9f3bf7e64a80} + + + {f7aba5a2-698c-4085-a191-b0ffd3522298} + + + {4fb57465-2aae-4d75-87de-a2669a0570b4} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\blip + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\msvc + + + Header Files\msvc + + + Header Files\mednafen + + + Header Files\mednafen + + + Header Files\blip + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/wonderswan/blip/Blip_Buffer.cpp b/wonderswan/blip/Blip_Buffer.cpp new file mode 100644 index 0000000000..f04a1fc599 --- /dev/null +++ b/wonderswan/blip/Blip_Buffer.cpp @@ -0,0 +1,457 @@ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ + +#include "Blip_Buffer.h" + +#include +#include +#include +#include +#include +#include + +/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +int const silent_buf_size = 1; // size used for Silent_Blip_Buffer + +Blip_Buffer::Blip_Buffer() +{ + factor_ = (blip_u64)ULLONG_MAX; + offset_ = 0; + buffer_ = 0; + buffer_size_ = 0; + sample_rate_ = 0; + reader_accum_ = 0; + bass_shift_ = 0; + clock_rate_ = 0; + bass_freq_ = 16; + length_ = 0; + + // assumptions code makes about implementation-defined features + #ifndef NDEBUG + // right shift of negative value preserves sign + buf_t_ i = -0x7FFFFFFE; + assert( (i >> 1) == -0x3FFFFFFF ); + + // casting to short truncates to 16 bits and sign-extends + i = 0x18000; + assert( (short) i == -0x8000 ); + #endif +} + +Blip_Buffer::~Blip_Buffer() +{ + if ( buffer_size_ != silent_buf_size ) + free( buffer_ ); +} + +Silent_Blip_Buffer::Silent_Blip_Buffer() +{ + factor_ = 0; + buffer_ = buf; + buffer_size_ = silent_buf_size; + memset( buf, 0, sizeof buf ); // in case machine takes exception for signed overflow +} + +void Blip_Buffer::clear( int entire_buffer ) +{ + offset_ = 0; + reader_accum_ = 0; + modified_ = 0; + if ( buffer_ ) + { + long count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) ); + } +} + +Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return "Internal (tried to resize Silent_Blip_Buffer)"; + } + + // start with maximum length that resampled time can represent + blip_s64 new_size = (ULLONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64; + + // simple safety check, since code elsewhere may not be safe for sizes approaching (2 ^ 31). + if(new_size > ((1LL << 30) - 1)) + new_size = (1LL << 30) - 1; + + if ( msec != blip_max_length ) + { + blip_s64 s = ((blip_s64)new_rate * (msec + 1) + 999) / 1000; + if ( s < new_size ) + new_size = s; + else + assert( 0 ); // fails if requested buffer length exceeds limit + } + + if ( buffer_size_ != new_size ) + { + void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + if ( !p ) + return "Out of memory"; + + //if(new_size > buffer_size_) + // memset(buffer_ + buffer_size_, 0, (new_size + blip_buffer_extra_) * sizeof *buffer_ + + buffer_ = (buf_t_*) p; + } + + buffer_size_ = new_size; + assert( buffer_size_ != silent_buf_size ); + + // update things based on the sample rate + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + if ( msec ) + assert( length_ == msec ); // ensure length is same as that passed in + if ( clock_rate_ ) + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + + clear(); + + return 0; // success +} + +blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const +{ + double ratio = (double) sample_rate_ / rate; + blip_s64 factor = (blip_s64) floor( ratio * (1LL << BLIP_BUFFER_ACCURACY) + 0.5 ); + assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large + return (blip_resampled_time_t) factor; +} + +void Blip_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + int shift = 31; + if ( freq > 0 ) + { + shift = 13; + long f = (freq << 16) / sample_rate_; + while ( (f >>= 1) && --shift ) { } + } + bass_shift_ = shift; + //printf("%d\n", bass_shift_); +} + +void Blip_Buffer::end_frame( blip_time_t t ) +{ + offset_ += t * factor_; + assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length +} + +void Blip_Buffer::remove_silence( long count ) +{ + assert( count <= samples_avail() ); // tried to remove more samples than available + offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; +} + +long Blip_Buffer::count_samples( blip_time_t t ) const +{ + unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return (long) (last_sample - first_sample); +} + +blip_time_t Blip_Buffer::count_clocks( long count ) const +{ + if ( !factor_ ) + { + assert( 0 ); // sample rate and clock rates must be set first + return 0; + } + + if ( count > buffer_size_ ) + count = buffer_size_; + blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; + return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); +} + +void Blip_Buffer::remove_samples( long count ) +{ + if ( count ) + { + remove_silence( count ); + + // copy remaining samples to beginning and clear old samples + long remain = samples_avail() + blip_buffer_extra_; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } +} + +// Blip_Synth_ + +Blip_Synth_Fast_::Blip_Synth_Fast_() +{ + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +void Blip_Synth_Fast_::volume_unit( double new_unit ) +{ + delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); +} + +#if !BLIP_BUFFER_FAST + +Blip_Synth_::Blip_Synth_( short* p, int w ) : + impulses( p ), + width( w ) +{ + volume_unit_ = 0.0; + kernel_unit = 0; + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) +{ + if ( cutoff >= 0.999 ) + cutoff = 0.999; + + if ( treble < -300.0 ) + treble = -300.0; + if ( treble > 5.0 ) + treble = 5.0; + + double const maxh = 4096.0; + double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); + double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); + double const to_angle = PI / 2 / maxh / oversample; + for ( int i = 0; i < count; i++ ) + { + double angle = ((i - count) * 2 + 1) * to_angle; + double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle ); + double cos_nc_angle = cos( maxh * cutoff * angle ); + double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle ); + double cos_angle = cos( angle ); + + c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; + double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); + double b = 2.0 - cos_angle - cos_angle; + double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; + + out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d + } +} + +void blip_eq_t::generate( float* out, int count ) const +{ + // lower cutoff freq for narrow kernels with their wider transition band + // (8 points->1.49, 16 points->1.15) + double oversample = blip_res * 2.25 / count + 0.85; + double half_rate = sample_rate * 0.5; + if ( cutoff_freq ) + oversample = half_rate / cutoff_freq; + double cutoff = rolloff_freq * oversample / half_rate; + + gen_sinc( out, count, blip_res * oversample, treble, cutoff ); + + // apply (half of) hamming window + double to_fraction = PI / (count - 1); + for ( int i = count; i--; ) + out [i] *= 0.54f - 0.46f * (float) cos( i * to_fraction ); +} + +void Blip_Synth_::adjust_impulse() +{ + // sum pairs for each phase and add error correction to end of first half + int const size = impulses_size(); + for ( int p = blip_res; p-- >= blip_res / 2; ) + { + int p2 = blip_res - 2 - p; + long error = kernel_unit; + for ( int i = 1; i < size; i += blip_res ) + { + error -= impulses [i + p ]; + error -= impulses [i + p2]; + } + if ( p == p2 ) + error /= 2; // phase = 0.5 impulse uses same half for both sides + impulses [size - blip_res + p] += (short) error; + //printf( "error: %ld\n", error ); + } + + //for ( int i = blip_res; i--; printf( "\n" ) ) + // for ( int j = 0; j < width / 2; j++ ) + // printf( "%5ld,", impulses [j * blip_res + i + 1] ); +} + +void Blip_Synth_::treble_eq( blip_eq_t const& eq ) +{ + float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; + + int const half_size = blip_res / 2 * (width - 1); + eq.generate( &fimpulse [blip_res], half_size ); + + int i; + + // need mirror slightly past center for calculation + for ( i = blip_res; i--; ) + fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; + + // starts at 0 + for ( i = 0; i < blip_res; i++ ) + fimpulse [i] = 0.0f; + + // find rescale factor + double total = 0.0; + for ( i = 0; i < half_size; i++ ) + total += fimpulse [blip_res + i]; + + //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB + //double const base_unit = 37888.0; // allows treble to +5 dB + double const base_unit = 32768.0; // necessary for blip_unscaled to work + double rescale = base_unit / 2 / total; + kernel_unit = (long) base_unit; + + // integrate, first difference, rescale, convert to int + double sum = 0.0; + double next = 0.0; + int const impulses_size_local = this->impulses_size(); + for ( i = 0; i < impulses_size_local; i++ ) + { + impulses [i] = (short) floor( (next - sum) * rescale + 0.5 ); + sum += fimpulse [i]; + next += fimpulse [i + blip_res]; + } + adjust_impulse(); + + // volume might require rescaling + double vol = volume_unit_; + if ( vol ) + { + volume_unit_ = 0.0; + volume_unit( vol ); + } +} + +void Blip_Synth_::volume_unit( double new_unit ) +{ + if ( new_unit != volume_unit_ ) + { + // use default eq if it hasn't been set yet + if ( !kernel_unit ) + treble_eq( -8.0 ); + + volume_unit_ = new_unit; + double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; + + if ( factor > 0.0 ) + { + int shift = 0; + + // if unit is really small, might need to attenuate kernel + while ( factor < 2.0 ) + { + shift++; + factor *= 2.0; + } + + if ( shift ) + { + kernel_unit >>= shift; + assert( kernel_unit > 0 ); // fails if volume unit is too low + + // keep values positive to avoid round-towards-zero of sign-preserving + // right shift for negative values + long offset = 0x8000 + (1 << (shift - 1)); + long offset2 = 0x8000 >> shift; + for ( int i = impulses_size(); i--; ) + impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2); + adjust_impulse(); + } + } + delta_factor = (int) floor( factor + 0.5 ); + //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); + } +} +#endif + +long Blip_Buffer::read_samples( blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo ) +{ + long count = samples_avail(); + if ( count > max_samples ) + count = max_samples; + + if ( count ) + { + int const bass = BLIP_READER_BASS( *this ); + BLIP_READER_BEGIN( reader, *this ); + + if ( !stereo ) + { + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out++ = (blip_sample_t) s; + BLIP_READER_NEXT( reader, bass ); + } + } + else + { + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out = (blip_sample_t) s; + out += 2; + BLIP_READER_NEXT( reader, bass ); + } + } + BLIP_READER_END( reader, *this ); + + remove_samples( count ); + } + return count; +} + +void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return; + } + + buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; + + int const sample_shift = blip_sample_bits - 16; + int prev = 0; + while ( count-- ) + { + blip_long s = (blip_long) *in++ << sample_shift; + *out += s - prev; + prev = s; + ++out; + } + *out -= prev; +} + diff --git a/wonderswan/blip/Blip_Buffer.h b/wonderswan/blip/Blip_Buffer.h new file mode 100644 index 0000000000..a8e90ee053 --- /dev/null +++ b/wonderswan/blip/Blip_Buffer.h @@ -0,0 +1,498 @@ +// Band-limited sound synthesis buffer +// Various changes and hacks for use in Mednafen. + +#ifdef __GNUC__ + #define blip_inline inline __attribute__((always_inline)) +#else + #define blip_inline inline +#endif + +#include +#include + +// Blip_Buffer 0.4.1 +#ifndef BLIP_BUFFER_H +#define BLIP_BUFFER_H + +// Internal +typedef int32_t blip_long; +typedef uint32_t blip_ulong; +typedef int64_t blip_s64; +typedef uint64_t blip_u64; + +// Time unit at source clock rate +typedef blip_long blip_time_t; + +// Output samples are 16-bit signed, with a range of -32768 to 32767 +typedef short blip_sample_t; +enum { blip_sample_max = 32767 }; + +class Blip_Buffer { +public: + typedef const char* blargg_err_t; + + // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults + // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there + // isn't enough memory, returns error without affecting current buffer setup. + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); + + // Set number of source time units per second + void clock_rate( long ); + + // End current time frame of specified duration and make its samples available + // (along with any still-unread samples) for reading with read_samples(). Begins + // a new time frame at the end of the current frame. + void end_frame( blip_time_t time ); + + // Read at most 'max_samples' out of buffer into 'dest', removing them from from + // the buffer. Returns number of samples actually read and removed. If stereo is + // true, increments 'dest' one extra time after writing each sample, to allow + // easy interleving of two channels into a stereo output buffer. + long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); + +// Additional optional features + + // Current output sample rate + long sample_rate() const; + + // Length of buffer, in milliseconds + int length() const; + + // Number of source time units per second + long clock_rate() const; + + // Set frequency high-pass filter frequency, where higher values reduce bass more + void bass_freq( int frequency ); + + // Number of samples delay from synthesis to samples read out + int output_latency() const; + + // Remove all available samples and clear buffer to silence. If 'entire_buffer' is + // false, just clears out any samples waiting rather than the entire buffer. + void clear( int entire_buffer = 1 ); + + // Number of samples available for reading with read_samples() + long samples_avail() const; + + // Remove 'count' samples from those waiting to be read + void remove_samples( long count ); + +// Experimental features + + // Count number of clocks needed until 'count' samples will be available. + // If buffer can't even hold 'count' samples, returns number of clocks until + // buffer becomes full. + blip_time_t count_clocks( long count ) const; + + // Number of raw samples that can be mixed within frame of specified duration. + long count_samples( blip_time_t duration ) const; + + // Mix 'count' samples from 'buf' into buffer. + void mix_samples( blip_sample_t const* buf, long count ); + + // not documented yet + void set_modified() { modified_ = 1; } + int clear_modified() { int b = modified_; modified_ = 0; return b; } + typedef blip_u64 blip_resampled_time_t; + void remove_silence( long count ); + blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } + blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } + blip_resampled_time_t clock_rate_factor( long clock_rate ) const; +public: + Blip_Buffer(); + ~Blip_Buffer(); + + // Deprecated + typedef blip_resampled_time_t resampled_time_t; + blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } + blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } +private: + // noncopyable + Blip_Buffer( const Blip_Buffer& ); + Blip_Buffer& operator = ( const Blip_Buffer& ); +public: + typedef blip_time_t buf_t_; + blip_u64 factor_; + blip_resampled_time_t offset_; + buf_t_* buffer_; + blip_long buffer_size_; + blip_long reader_accum_; + int bass_shift_; +private: + long sample_rate_; + long clock_rate_; + int bass_freq_; + int length_; + int modified_; + friend class Blip_Reader; +}; + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#define BLIP_BUFFER_ACCURACY 32 +#define BLIP_PHASE_BITS 8 + +// Number of bits in resample ratio fraction. Higher values give a more accurate ratio +// but reduce maximum buffer size. +//#ifndef BLIP_BUFFER_ACCURACY +// #define BLIP_BUFFER_ACCURACY 16 +//#endif + +// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in +// noticeable broadband noise when synthesizing high frequency square waves. +// Affects size of Blip_Synth objects since they store the waveform directly. +//#ifndef BLIP_PHASE_BITS +// #if BLIP_BUFFER_FAST +// #define BLIP_PHASE_BITS 8 +// #else +// #define BLIP_PHASE_BITS 6 +// #endif +//#endif + + // Internal + typedef blip_u64 blip_resampled_time_t; + int const blip_widest_impulse_ = 16; + int const blip_buffer_extra_ = blip_widest_impulse_ + 2; + int const blip_res = 1 << BLIP_PHASE_BITS; + class blip_eq_t; + + class Blip_Synth_Fast_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_Fast_(); + void treble_eq( blip_eq_t const& ) { } + }; + + class Blip_Synth_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_( short* impulses, int width ); + void treble_eq( blip_eq_t const& ); + private: + double volume_unit_; + short* const impulses; + int const width; + blip_long kernel_unit; + int impulses_size() const { return blip_res / 2 * width + 1; } + void adjust_impulse(); + }; + +// Quality level. Start with blip_good_quality. +const int blip_med_quality = 8; +const int blip_good_quality = 12; +const int blip_high_quality = 16; + +// Range specifies the greatest expected change in amplitude. Calculate it +// by finding the difference between the maximum and minimum expected +// amplitudes (max - min). +template +class Blip_Synth { +public: + // Set overall volume of waveform + void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } + + // Configure low-pass filter (see blip_buffer.txt) + void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } + + // Get/set Blip_Buffer used for output + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Update amplitude of waveform at given time. Using this requires a separate + // Blip_Synth for each waveform. + void update( blip_time_t time, int amplitude ); + +// Low-level interface + + // Add an amplitude transition of specified delta, optionally into specified buffer + // rather than the one set with output(). Delta can be positive or negative. + // The actual change in amplitude is delta * (volume / range) + void offset( blip_time_t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Contact author for more info. + void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; + + // Same as offset(), except code is inlined for higher performance + void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } + void offset_inline( blip_time_t t, int delta ) const { + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); + } + +private: +#if BLIP_BUFFER_FAST + Blip_Synth_Fast_ impl; +#else + Blip_Synth_ impl; + typedef short imp_t; + imp_t impulses [blip_res * (quality / 2) + 1]; +public: + Blip_Synth() : impl( impulses, quality ) { } +#endif +}; + +// Low-pass equalization parameters +class blip_eq_t { +public: + // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce + // treble, small positive values (0 to 5.0) increase treble. + blip_eq_t( double treble_db = 0 ); + + // See blip_buffer.txt + blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); + +private: + double treble; + long rolloff_freq; + long sample_rate; + long cutoff_freq; + void generate( float* out, int count ) const; + friend class Blip_Synth_; +}; + +int const blip_sample_bits = 30; + +// Dummy Blip_Buffer to direct sound output to, for easy muting without +// having to stop sound code. +class Silent_Blip_Buffer : public Blip_Buffer { + buf_t_ buf [blip_buffer_extra_ + 1]; +public: + // The following cannot be used (an assertion will fail if attempted): + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); + blip_time_t count_clocks( long count ) const; + void mix_samples( blip_sample_t const* buf, long count ); + + Silent_Blip_Buffer(); +}; + + #if defined (__GNUC__) || _MSC_VER >= 1100 + #define BLIP_RESTRICT __restrict + #else + #define BLIP_RESTRICT + #endif + +// Optimized reading from Blip_Buffer, for use in custom sample output + +// Begin reading from buffer. Name should be unique to the current block. +#define BLIP_READER_BEGIN( name, blip_buffer ) \ + const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\ + blip_long name##_reader_accum = (blip_buffer).reader_accum_ + +// Get value to pass to BLIP_READER_NEXT() +#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_) + +// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal +// code at the cost of having no bass control +int const blip_reader_default_bass = 9; + +// Current sample +#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16)) + +// Current raw sample in full internal resolution +#define BLIP_READER_READ_RAW( name ) (name##_reader_accum) + +// Advance to next sample +#define BLIP_READER_NEXT( name, bass ) \ + (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) + +// End reading samples from buffer. The number of samples read must now be removed +// using Blip_Buffer::remove_samples(). +#define BLIP_READER_END( name, blip_buffer ) \ + (void) ((blip_buffer).reader_accum_ = name##_reader_accum) + + +// Compatibility with older version +const long blip_unscaled = 65535; +const int blip_low_quality = blip_med_quality; +const int blip_best_quality = blip_high_quality; + +// Deprecated; use BLIP_READER macros as follows: +// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf ); +// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf ); +// r.read() -> BLIP_READER_READ( r ) +// r.read_raw() -> BLIP_READER_READ_RAW( r ) +// r.next( bass ) -> BLIP_READER_NEXT( r, bass ) +// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass ) +// r.end( buf ) -> BLIP_READER_END( r, buf ) +class Blip_Reader { +public: + int begin( Blip_Buffer& ); + blip_long read() const { return accum >> (blip_sample_bits - 16); } + blip_long read_raw() const { return accum; } + void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } + void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } + +private: + const Blip_Buffer::buf_t_* buf; + blip_long accum; +}; + +// End of public interface + +#include + +template +blip_inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, + int delta, Blip_Buffer* blip_buf ) const +{ + // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the + // need for a longer buffer as set by set_sample_rate(). + assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); + delta *= impl.delta_factor; + blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); + int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); + +#if BLIP_BUFFER_FAST + blip_long left = buf [0] + delta; + + // Kind of crappy, but doing shift after multiply results in overflow. + // Alternate way of delaying multiply by delta_factor results in worse + // sub-sample resolution. + blip_long right = (delta >> BLIP_PHASE_BITS) * phase; + left -= right; + right += buf [1]; + + buf [0] = left; + buf [1] = right; +#else + + int const fwd = (blip_widest_impulse_ - quality) / 2; + int const rev = fwd + quality - 2; + int const mid = quality / 2 - 1; + + imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; + + #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + + // straight forward implementation resulted in better code on GCC for x86 + + #define ADD_IMP( out, in ) \ + buf [out] += (blip_long) imp [blip_res * (in)] * delta + + #define BLIP_FWD( i ) {\ + ADD_IMP( fwd + i, i );\ + ADD_IMP( fwd + 1 + i, i + 1 );\ + } + #define BLIP_REV( r ) {\ + ADD_IMP( rev - r, r + 1 );\ + ADD_IMP( rev + 1 - r, r );\ + } + + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + ADD_IMP( fwd + mid - 1, mid - 1 ); + ADD_IMP( fwd + mid , mid ); + imp = impulses + phase; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + ADD_IMP( rev , 1 ); + ADD_IMP( rev + 1, 0 ); + + #else + + // for RISC processors, help compiler by reading ahead of writes + + #define BLIP_FWD( i ) {\ + blip_long t0 = i0 * delta + buf [fwd + i];\ + blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\ + i0 = imp [blip_res * (i + 2)];\ + buf [fwd + i] = t0;\ + buf [fwd + 1 + i] = t1;\ + } + #define BLIP_REV( r ) {\ + blip_long t0 = i0 * delta + buf [rev - r];\ + blip_long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];\ + i0 = imp [blip_res * (r - 1)];\ + buf [rev - r] = t0;\ + buf [rev + 1 - r] = t1;\ + } + + blip_long i0 = *imp; + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + blip_long t0 = i0 * delta + buf [fwd + mid - 1]; + blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ]; + imp = impulses + phase; + i0 = imp [blip_res * mid]; + buf [fwd + mid - 1] = t0; + buf [fwd + mid ] = t1; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + blip_long t0 = i0 * delta + buf [rev ]; + blip_long t1 = *imp * delta + buf [rev + 1]; + buf [rev ] = t0; + buf [rev + 1] = t1; + #endif + +#endif +} + +#undef BLIP_FWD +#undef BLIP_REV + +template +#if BLIP_BUFFER_FAST + blip_inline +#endif +void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const +{ + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); +} + +template +#if BLIP_BUFFER_FAST + blip_inline +#endif +void Blip_Synth::update( blip_time_t t, int amp ) +{ + int delta = amp - impl.last_amp; + impl.last_amp = amp; + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); +} + +blip_inline blip_eq_t::blip_eq_t( double t ) : + treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } +blip_inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : + treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } + +blip_inline int Blip_Buffer::length() const { return length_; } +blip_inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } +blip_inline long Blip_Buffer::sample_rate() const { return sample_rate_; } +blip_inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } +blip_inline long Blip_Buffer::clock_rate() const { return clock_rate_; } +blip_inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } + +blip_inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) +{ + buf = blip_buf.buffer_; + accum = blip_buf.reader_accum_; + return blip_buf.bass_shift_; +} + +int const blip_max_length = 0; +int const blip_default_length = 250; + +#endif diff --git a/wonderswan/eeprom.cpp b/wonderswan/eeprom.cpp new file mode 100644 index 0000000000..087915403e --- /dev/null +++ b/wonderswan/eeprom.cpp @@ -0,0 +1,208 @@ +/* Cygne +* +* Copyright notice for this file: +* Copyright (C) 2002 Dox dox@space.pl +* +* 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "system.h" +#include +#include + +namespace MDFN_IEN_WSWAN +{ + //uint8 wsEEPROM[2048]; + //static uint8 iEEPROM[0x400]; + static const uint8 iEEPROM_Init[0x400] = + { + 255,255,255,255,255,255,192,255,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,127,0,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 0,252,255,1,255,253,255,253,255,253,255,253, + 255,253,255,253,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 0,0,3,3,0,0,0,64,128,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 135,5,140,9,5,12,139,12,144,0,0,2, + 0,76,165,0,128,0,0,0,255,127,255,127, + 255,127,255,127,255,127,255,127,255,127,255,127, + 255,127,255,127,255,127,255,127,255,127,255,127, + 255,127,255,127,255,127,255,127,255,127,255,127, + 255,127,255,127,255,127,255,127,255,127,255,127, + 255,127,255,127,255,127,255,127,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 0,0,6,6,6,6,6,0,0,0,0,0, + 1,128,15,0,1,1,1,15,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 'C'-54,'Y'-54,'G'-54,'N'-54,'E'-54,0,0,0,0,0,0,0,0,0,0, + 0,32,1,1,33,1,4,0,1, + 0,152,60,127,74,1,53,1,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255 + }; + + //static uint8 iEEPROM_Command, EEPROM_Command; + //static uint16 iEEPROM_Address, EEPROM_Address; + + uint8 EEPROM::Read(uint32 A) + { + switch(A) + { + default: Debug::printf("Read: %04x\n", A); break; + + case 0xBA: return(iEEPROM[(iEEPROM_Address << 1) & 0x3FF]); + case 0xBB: return(iEEPROM[((iEEPROM_Address << 1) | 1) & 0x3FF]); + case 0xBC: return(iEEPROM_Address >> 0); + case 0xBD: return(iEEPROM_Address >> 8); + case 0xBE: + if(iEEPROM_Command & 0x20) return iEEPROM_Command|2; + if(iEEPROM_Command & 0x10) return iEEPROM_Command|1; + return iEEPROM_Command | 3; + + + case 0xC4: return(wsEEPROM[(EEPROM_Address << 1) & (eeprom_size - 1)]); + case 0xC5: return(wsEEPROM[((EEPROM_Address << 1) | 1) & (eeprom_size - 1)]); + case 0xC6: return(EEPROM_Address >> 0); + case 0xC7: return(EEPROM_Address >> 8); + case 0xC8: if(EEPROM_Command & 0x20) return EEPROM_Command|2; + if(EEPROM_Command & 0x10) return EEPROM_Command|1; + return EEPROM_Command | 3; + } + return(0); + } + + + void EEPROM::Write(uint32 A, uint8 V) + { + switch(A) + { + case 0xBA: iEEPROM[(iEEPROM_Address << 1) & 0x3FF] = V; break; + case 0xBB: iEEPROM[((iEEPROM_Address << 1) | 1) & 0x3FF] = V; break; + case 0xBC: iEEPROM_Address &= 0xFF00; iEEPROM_Address |= (V << 0); break; + case 0xBD: iEEPROM_Address &= 0x00FF; iEEPROM_Address |= (V << 8); break; + case 0xBE: iEEPROM_Command = V; break; + + case 0xC4: wsEEPROM[(EEPROM_Address << 1) & (eeprom_size - 1)] = V; break; + case 0xC5: wsEEPROM[((EEPROM_Address << 1) | 1) & (eeprom_size - 1)] = V; break; + + case 0xC6: EEPROM_Address &= 0xFF00; EEPROM_Address |= (V << 0); break; + case 0xC7: EEPROM_Address &= 0x00FF; EEPROM_Address |= (V << 8); break; + case 0xC8: EEPROM_Command = V; break; + } + } + + void EEPROM::Reset() + { + iEEPROM_Command = EEPROM_Command = 0; + iEEPROM_Address = EEPROM_Address = 0; + } + + void EEPROM::Init(const char *Name, const uint16 BYear, const uint8 BMonth, const uint8 BDay, const uint8 Sex, const uint8 Blood) + { + std::memset(wsEEPROM, 0, 2048); + std::memcpy(iEEPROM, iEEPROM_Init, 0x400); + + for(unsigned int x = 0; x < 16; x++) + { + uint8 zechar = 0; + + if(x < std::strlen(Name)) + { + char tc = toupper(Name[x]); + if(tc == ' ') zechar = 0; + else if(tc >= '0' && tc <= '9') zechar = tc - '0' + 0x1; + else if(tc >= 'A' && tc <= 'Z') zechar = tc - 'A' + 0xB; + else if(tc >= 'a' && tc <= 'z') zechar = tc - 'a' + 0xB + 26; + } + iEEPROM[0x360 + x] = zechar; + } + +#define mBCD16(value) ( (((((value)%100) / 10) <<4)|((value)%10)) | ((((((value / 100)%100) / 10) <<4)|((value / 100)%10))<<8) ) +#define INT16_TO_BCD(A) ((((((A) % 100) / 10) * 16 + ((A) % 10))) | (((((((A) / 100) % 100) / 10) * 16 + (((A) / 100) % 10))) << 8)) // convert INT16 --> BCD + + uint16 bcd_BYear = INT16_TO_BCD(BYear); + + iEEPROM[0x370] = (bcd_BYear >> 8) & 0xFF; + iEEPROM[0x371] = (bcd_BYear >> 0) & 0xFF; + iEEPROM[0x372] = mBCD(BMonth); + iEEPROM[0x373] = mBCD(BDay); + iEEPROM[0x374] = Sex; + iEEPROM[0x375] = Blood; + } + +} diff --git a/wonderswan/eeprom.h b/wonderswan/eeprom.h new file mode 100644 index 0000000000..26d5e7b4f5 --- /dev/null +++ b/wonderswan/eeprom.h @@ -0,0 +1,32 @@ +#ifndef __WSWAN_EEPROM_H +#define __WSWAN_EEPROM_H + +#include "system.h" + +namespace MDFN_IEN_WSWAN +{ + + +class EEPROM +{ +public: + uint8 Read(uint32 A); + void Write(uint32 A, uint8 V); + void Reset(); + void Init(const char *Name, const uint16 BYear, const uint8 BMonth, const uint8 BDay, const uint8 Sex, const uint8 Blood); + +private: + uint8 wsEEPROM[2048]; + uint8 iEEPROM[0x400]; + uint8 iEEPROM_Command, EEPROM_Command; + uint16 iEEPROM_Address, EEPROM_Address; + uint32 eeprom_size; + +public: + System *sys; +}; + + +} + +#endif diff --git a/wonderswan/gfx.cpp b/wonderswan/gfx.cpp new file mode 100644 index 0000000000..d34f238a2e --- /dev/null +++ b/wonderswan/gfx.cpp @@ -0,0 +1,601 @@ +/* Cygne +* +* Copyright notice for this file: +* Copyright (C) 2002 Dox dox@space.pl +* +* 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "system.h" +//#include +//#include +#include + +namespace MDFN_IEN_WSWAN +{ + GFX::GFX() + { + SetPixelFormat(); + } + + void GFX::Init() + { + LayerEnabled = 7; // BG, FG, sprites + } + + void GFX::PaletteRAMWrite(uint32 ws_offset, uint8 data) + { + ws_offset=(ws_offset&0xfffe)-0xfe00; + wsCols[(ws_offset>>1)>>4][(ws_offset>>1)&15] = sys->memory.wsRAM[ws_offset+0xfe00] | ((sys->memory.wsRAM[ws_offset+0xfe01]&0x0f) << 8); + } + + void GFX::Write(uint32 A, uint8 V) + { + if(A >= 0x1C && A <= 0x1F) + { + wsColors[(A - 0x1C) * 2 + 0] = 0xF - (V & 0xf); + wsColors[(A - 0x1C) * 2 + 1] = 0xF - (V >> 4); + } + else if(A >= 0x20 && A <= 0x3F) + { + wsMonoPal[(A - 0x20) >> 1][((A & 0x1) << 1) + 0] = V&7; + wsMonoPal[(A - 0x20) >> 1][((A & 0x1) << 1) | 1] = (V>>4)&7; + } + else switch(A) + { + case 0x00: DispControl = V; break; + case 0x01: BGColor = V; break; + case 0x03: LineCompare = V; break; + case 0x04: SPRBase = V & 0x3F; break; + case 0x05: SpriteStart = V; break; + case 0x06: SpriteCount = V; break; + case 0x07: FGBGLoc = V; break; + case 0x08: FGx0 = V; break; + case 0x09: FGy0 = V; break; + case 0x0A: FGx1 = V; break; + case 0x0B: FGy1 = V; break; + case 0x0C: SPRx0 = V; break; + case 0x0D: SPRy0 = V; break; + case 0x0E: SPRx1 = V; break; + case 0x0F: SPRy1 = V; break; + case 0x10: BGXScroll = V; break; + case 0x11: BGYScroll = V; break; + case 0x12: FGXScroll = V; break; + case 0x13: FGYScroll = V; break; + + case 0x14: LCDControl = V; break; // if((!(wsIO[0x14]&1))&&(data&1)) { wsLine=0; }break; /* LCD off ??*/ + case 0x15: LCDIcons = V; break; + + case 0x60: VideoMode = V; + SetVideo(V>>5, false); + //printf("VideoMode: %02x, %02x\n", V, V >> 5); + break; + + case 0xa2: if((V & 0x01) && !(BTimerControl & 0x01)) + HBCounter = HBTimerPeriod; + if((V & 0x04) && !(BTimerControl & 0x04)) + VBCounter = VBTimerPeriod; + BTimerControl = V; + //printf("%04x:%02x\n", A, V); + break; + case 0xa4: HBTimerPeriod &= 0xFF00; HBTimerPeriod |= (V << 0); /*printf("%04x:%02x, %d\n", A, V, wsLine);*/ break; + case 0xa5: HBTimerPeriod &= 0x00FF; HBTimerPeriod |= (V << 8); HBCounter = HBTimerPeriod; /*printf("%04x:%02x, %d\n", A, V, wsLine);*/ break; + case 0xa6: VBTimerPeriod &= 0xFF00; VBTimerPeriod |= (V << 0); /*printf("%04x:%02x, %d\n", A, V, wsLine);*/ break; + case 0xa7: VBTimerPeriod &= 0x00FF; VBTimerPeriod |= (V << 8); VBCounter = VBTimerPeriod; /*printf("%04x:%02x, %d\n", A, V, wsLine);*/ break; + //default: printf("%04x:%02x\n", A, V); break; + } + } + + uint8 GFX::Read(uint32 A) + { + if(A >= 0x1C && A <= 0x1F) + { + uint8 ret = 0; + + ret |= 0xF - wsColors[(A - 0x1C) * 2 + 0]; + ret |= (0xF - wsColors[(A - 0x1C) * 2 + 1]) << 4; + + return(ret); + } + else if(A >= 0x20 && A <= 0x3F) + { + uint8 ret = wsMonoPal[(A - 0x20) >> 1][((A & 0x1) << 1) + 0] | (wsMonoPal[(A - 0x20) >> 1][((A & 0x1) << 1) | 1] << 4); + + return(ret); + } + else switch(A) + { + case 0x00: return(DispControl); + case 0x01: return(BGColor); + case 0x02: return(wsLine); + case 0x03: return(LineCompare); + case 0x04: return(SPRBase); + case 0x05: return(SpriteStart); + case 0x06: return(SpriteCount); + case 0x07: return(FGBGLoc); + case 0x08: return(FGx0); break; + case 0x09: return(FGy0); break; + case 0x0A: return(FGx1); break; + case 0x0B: return(FGy1); break; + case 0x0C: return(SPRx0); break; + case 0x0D: return(SPRy0); break; + case 0x0E: return(SPRx1); break; + case 0x0F: return(SPRy1); break; + case 0x10: return(BGXScroll); + case 0x11: return(BGYScroll); + case 0x12: return(FGXScroll); + case 0x13: return(FGYScroll); + case 0x14: return(LCDControl); + case 0x15: return(LCDIcons); + case 0x60: return(VideoMode); + case 0xa0: return(sys->wsc ? 0x87 : 0x86); + case 0xa2: return(BTimerControl); + case 0xa4: return((HBTimerPeriod >> 0) & 0xFF); + case 0xa5: return((HBTimerPeriod >> 8) & 0xFF); + case 0xa6: return((VBTimerPeriod >> 0) & 0xFF); + case 0xa7: return((VBTimerPeriod >> 8) & 0xFF); + case 0xa8: /*printf("%04x\n", A);*/ return((HBCounter >> 0) & 0xFF); + case 0xa9: /*printf("%04x\n", A);*/ return((HBCounter >> 8) & 0xFF); + case 0xaa: /*printf("%04x\n", A);*/ return((VBCounter >> 0) & 0xFF); + case 0xab: /*printf("%04x\n", A);*/ return((VBCounter >> 8) & 0xFF); + default: return(0); + //default: printf("GfxRead: %04x\n", A); return(0); + } + } + + bool GFX::ExecuteLine(uint32 *surface, bool skip) + { + //static const void* const WEP_Tab[3] = { &&WEP0, &&WEP1, &&WEP2 }; // The things we do for debugger step mode save states! If we ever add more entries, remember to change the mask stuff in StateAction + bool ret; + +// weppy = 0; +//WEP0: ; + + ret = FALSE; + + if(wsLine < 144) + { + if(!skip) + Scanline(surface + wsLine * 224); + } + + sys->memory.CheckSoundDMA(); + + // Update sprite data table + if(wsLine == 142) + { + SpriteCountCache = SpriteCount; + + if(SpriteCountCache > 0x80) + SpriteCountCache = 0x80; + + memcpy(SpriteTable, &sys->memory.wsRAM[(SPRBase << 9) + (SpriteStart << 2)], SpriteCountCache << 2); + } + + if(wsLine == 144) + { + ret = TRUE; + sys->interrupt.DoInterrupt(WSINT_VBLANK); + //printf("VBlank: %d\n", wsLine); + } + + + if(HBCounter && (BTimerControl & 0x01)) + { + HBCounter--; + if(!HBCounter) + { + // Loop mode? + if(BTimerControl & 0x02) + HBCounter = HBTimerPeriod; + sys->interrupt.DoInterrupt(WSINT_HBLANK_TIMER); + } + } + +// weppy = 1; + sys->cpu.execute(224); +// goto *WEP_Tab[weppy]; +//WEP1: ; + + wsLine = (wsLine + 1) % 159; + if(wsLine == LineCompare) + { + sys->interrupt.DoInterrupt(WSINT_LINE_HIT); + //printf("Line hit: %d\n", wsLine); + } + +// weppy = 2; + sys->cpu.execute(32); +// goto *WEP_Tab[weppy]; +//WEP2: ; + + sys->rtc.Clock(256); + + if(!wsLine) + { + if(VBCounter && (BTimerControl & 0x04)) + { + VBCounter--; + if(!VBCounter) + { + if(BTimerControl & 0x08) // Loop mode? + VBCounter = VBTimerPeriod; + + sys->interrupt.DoInterrupt(WSINT_VBLANK_TIMER); + } + } + wsLine = 0; + } + +// weppy = 0; + return(ret); + } + + void GFX::SetLayerEnableMask(uint64 mask) + { + LayerEnabled = mask; + } + + void GFX::SetPixelFormat() + { + for(int r = 0; r < 16; r++) + for(int g = 0; g < 16; g++) + for(int b = 0; b < 16; b++) + { + uint32 neo_r, neo_g, neo_b; + + neo_r = r * 17; + neo_g = g * 17; + neo_b = b * 17; + + ColorMap[(r << 8) | (g << 4) | (b << 0)] = 0xff000000 | neo_r << 16 | neo_g << 8 | neo_b << 0; + } + + for(int i = 0; i < 16; i++) + { + uint32 neo_r, neo_g, neo_b; + + neo_r = (i) * 17; + neo_g = (i) * 17; + neo_b = (i) * 17; + + ColorMapG[i] = 0xff000000 | neo_r << 16 | neo_g << 8 | neo_b << 0; + } + } + + void GFX::Scanline(uint32 *target) + { + uint32 start_tile_n,map_a,startindex,adrbuf,b1,b2,j,t,l; + char ys2; + uint8 b_bg[256]; + uint8 b_bg_pal[256]; + const uint8 *ram = sys->memory.wsRAM; + + if(!wsVMode) + memset(b_bg, wsColors[BGColor&0xF]&0xF, 256); + else + { + memset(&b_bg[0], BGColor & 0xF, 256); + memset(&b_bg_pal[0], (BGColor>>4) & 0xF, 256); + } + start_tile_n=(wsLine+BGYScroll)&0xff;/*First line*/ + map_a=(((uint32)(FGBGLoc&0xF))<<11)+((start_tile_n&0xfff8)<<3); + startindex = BGXScroll >> 3; /*First tile in row*/ + adrbuf = 7-(BGXScroll&7); /*Pixel in tile*/ + + if((DispControl & 0x01) && (LayerEnabled & 0x01)) /*BG layer*/ + { + for(t=0;t<29;t++) + { + b1=ram[map_a+(startindex<<1)]; + b2=ram[map_a+(startindex<<1)+1]; + uint32 palette=(b2>>1)&15; + b2=(b2<<8)|b1; + GetTile(b2&0x1ff,start_tile_n&7,b2&0x8000,b2&0x4000,b2&0x2000); + + if(wsVMode) + { + if(wsVMode & 0x2) + { + for(int x = 0; x < 8; x++) + if(wsTileRow[x]) + { + b_bg[adrbuf + x] = wsTileRow[x]; + b_bg_pal[adrbuf + x] = palette; + } + } + else + { + for(int x = 0; x < 8; x++) + if(wsTileRow[x] || !(palette & 0x4)) + { + b_bg[adrbuf + x] = wsTileRow[x]; + b_bg_pal[adrbuf + x] = palette; + } + } + } + else + { + for(int x = 0; x < 8; x++) + if(wsTileRow[x] || !(palette & 4)) + { + b_bg[adrbuf + x] = wsColors[wsMonoPal[palette][wsTileRow[x]]]; + } + } + adrbuf += 8; + startindex=(startindex + 1)&31; + } // end for(t = 0 ... + } // End BG layer drawing + + if((DispControl & 0x02) && (LayerEnabled & 0x02))/*FG layer*/ + { + uint8 windowtype = DispControl&0x30; + bool in_window[256 + 8*2]; + + if(windowtype) + { + memset(in_window, 0, sizeof(in_window)); + + if(windowtype == 0x20) // Display FG only inside window + { + if((wsLine >= FGy0) && (wsLine < FGy1)) + for(j = FGx0; j <= FGx1 && j < 224; j++) + in_window[7 + j] = 1; + } + else if(windowtype == 0x30) // Display FG only outside window + { + for(j = 0; j < 224; j++) + { + if(!(j >= FGx0 && j < FGx1) || !((wsLine >= FGy0) && (wsLine < FGy1))) + in_window[7 + j] = 1; + } + } + else + { + Debug::puts("Bad windowtype??"); + } + } + else + memset(in_window, 1, sizeof(in_window)); + + start_tile_n=(wsLine+FGYScroll)&0xff; + map_a=(((uint32)((FGBGLoc>>4)&0xF))<<11)+((start_tile_n>>3)<<6); + startindex = FGXScroll >> 3; + adrbuf = 7-(FGXScroll&7); + + for(t=0; t<29; t++) + { + b1=ram[map_a+(startindex<<1)]; + b2=ram[map_a+(startindex<<1)+1]; + uint32 palette=(b2>>1)&15; + b2=(b2<<8)|b1; + GetTile(b2&0x1ff,start_tile_n&7,b2&0x8000,b2&0x4000,b2&0x2000); + + if(wsVMode) + { + if(wsVMode & 0x2) + for(int x = 0; x < 8; x++) + { + if(wsTileRow[x] && in_window[adrbuf + x]) + { + b_bg[adrbuf + x] = wsTileRow[x] | 0x10; + b_bg_pal[adrbuf + x] = palette; + } + } + else + for(int x = 0; x < 8; x++) + { + if((wsTileRow[x] || !(palette & 0x4)) && in_window[adrbuf + x]) + { + b_bg[adrbuf + x] = wsTileRow[x] | 0x10; + b_bg_pal[adrbuf + x] = palette; + } + } + } + else + { + for(int x = 0; x < 8; x++) + if((wsTileRow[x] || !(palette & 4)) && in_window[adrbuf + x]) + { + b_bg[adrbuf + x] = wsColors[wsMonoPal[palette][wsTileRow[x]]] | 0x10; + } + } + adrbuf += 8; + startindex=(startindex + 1)&31; + } // end for(t = 0 ... + + } // end FG drawing + + if((DispControl & 0x04) && SpriteCountCache && (LayerEnabled & 0x04))/*Sprites*/ + { + int xs,ts,as,ys,ysx,h; + bool in_window[256 + 8*2]; + + if(DispControl & 0x08) + { + memset(in_window, 0, sizeof(in_window)); + if((wsLine >= SPRy0) && (wsLine < SPRy1)) + for(j = SPRx0; j < SPRx1 && j < 256; j++) + in_window[7 + j] = 1; + } + else + memset(in_window, 1, sizeof(in_window)); + + for(h = SpriteCountCache - 1; h >= 0; h--) + { + ts = SpriteTable[h][0]; + as = SpriteTable[h][1]; + ysx = SpriteTable[h][2]; + ys2 = (int8)SpriteTable[h][2]; + xs = SpriteTable[h][3]; + + if(xs >= 249) xs -= 256; + + if(ysx > 150) + ys = ys2; + else + ys = ysx; + + ys = wsLine - ys; + + if(ys >= 0 && ys < 8 && xs < 224) + { + uint32 palette = ((as >> 1) & 0x7); + + ts |= (as&1) << 8; + GetTile(ts, ys, as & 0x80, as & 0x40, 0); + + if(wsVMode) + { + if(wsVMode & 0x2) + { + for(int x = 0; x < 8; x++) + if(wsTileRow[x]) + { + if((as & 0x20) || !(b_bg[xs + x + 7] & 0x10)) + { + bool drawthis = 0; + + if(!(DispControl & 0x08)) + drawthis = TRUE; + else if((as & 0x10) && !in_window[7 + xs + x]) + drawthis = TRUE; + else if(!(as & 0x10) && in_window[7 + xs + x]) + drawthis = TRUE; + + if(drawthis) + { + b_bg[xs + x + 7] = wsTileRow[x] | (b_bg[xs + x + 7] & 0x10); + b_bg_pal[xs + x + 7] = 8 + palette; + } + } + } + } + else + { + for(int x = 0; x < 8; x++) + if(wsTileRow[x] || !(palette & 0x4)) + { + if((as & 0x20) || !(b_bg[xs + x + 7] & 0x10)) + { + bool drawthis = 0; + + if(!(DispControl & 0x08)) + drawthis = TRUE; + else if((as & 0x10) && !in_window[7 + xs + x]) + drawthis = TRUE; + else if(!(as & 0x10) && in_window[7 + xs + x]) + drawthis = TRUE; + + if(drawthis) + { + b_bg[xs + x + 7] = wsTileRow[x] | (b_bg[xs + x + 7] & 0x10); + b_bg_pal[xs + x + 7] = 8 + palette; + } + } + } + + } + + } + else + { + for(int x = 0; x < 8; x++) + if(wsTileRow[x] || !(palette & 4)) + { + if((as & 0x20) || !(b_bg[xs + x + 7] & 0x10)) + { + bool drawthis = 0; + + if(!(DispControl & 0x08)) + drawthis = TRUE; + else if((as & 0x10) && !in_window[7 + xs + x]) + drawthis = TRUE; + else if(!(as & 0x10) && in_window[7 + xs + x]) + drawthis = TRUE; + + if(drawthis) + //if((as & 0x10) || in_window[7 + xs + x]) + { + b_bg[xs + x + 7] = wsColors[wsMonoPal[8 + palette][wsTileRow[x]]] | (b_bg[xs + x + 7] & 0x10); + } + } + } + + } + } + } + + } // End sprite drawing + + if(wsVMode) + { + for(l=0;l<224;l++) + target[l] = ColorMap[wsCols[b_bg_pal[l+7]][b_bg[(l+7)]&0xf]]; + } + else + { + for(l=0;l<224;l++) + target[l] = ColorMapG[(b_bg[l+7])&15]; + } + } + + + void GFX::Reset() + { + //weppy = 0; + wsLine=145; // all frames same length + SetVideo(0,TRUE); + + memset(SpriteTable, 0, sizeof(SpriteTable)); + SpriteCountCache = 0; + DispControl = 0; + BGColor = 0; + LineCompare = 0xBB; + SPRBase = 0; + + SpriteStart = 0; + SpriteCount = 0; + FGBGLoc = 0; + + FGx0 = 0; + FGy0 = 0; + FGx1 = 0; + FGy1 = 0; + SPRx0 = 0; + SPRy0 = 0; + SPRx1 = 0; + SPRy1 = 0; + + BGXScroll = BGYScroll = 0; + FGXScroll = FGYScroll = 0; + LCDControl = 0; + LCDIcons = 0; + + BTimerControl = 0; + HBTimerPeriod = 0; + VBTimerPeriod = 0; + + HBCounter = 0; + VBCounter = 0; + + + for(int u0=0;u0<16;u0++) + for(int u1=0;u1<16;u1++) + wsCols[u0][u1]=0; + + } + +} diff --git a/wonderswan/gfx.h b/wonderswan/gfx.h new file mode 100644 index 0000000000..3974a4b8d1 --- /dev/null +++ b/wonderswan/gfx.h @@ -0,0 +1,91 @@ +#ifndef __WSWAN_GFX_H +#define __WSWAN_GFX_H + +#include "system.h" + +namespace MDFN_IEN_WSWAN +{ + +class GFX +{ +public: + GFX(); + + // TCACHE ==================================== + void InvalidByAddr(uint32); + void SetVideo(int, bool); + void MakeTiles(); + void GetTile(uint32 number,uint32 line,int flipv,int fliph,int bank); + // TCACHE/==================================== + void Scanline(uint32 *target); + void SetPixelFormat(); + + void Init(); + void Reset(); + void Write(uint32 A, uint8 V); + uint8 Read(uint32 A); + void PaletteRAMWrite(uint32 ws_offset, uint8 data); + + bool ExecuteLine(uint32 *surface, bool skip); + + void SetLayerEnableMask(uint64 mask); + +private: + // TCACHE ==================================== + uint8 tiles[256][256][2][8]; + uint8 wsTCache[512*64]; + uint8 wsTCache2[512*64]; + uint8 wsTCacheFlipped[512*64]; + uint8 wsTCacheFlipped2[512*64]; + uint8 wsTCacheUpdate[512]; + uint8 wsTCacheUpdate2[512]; + uint8 wsTileRow[8]; + int wsVMode; // doesn't belong here? + // TCACHE/==================================== + uint32 wsMonoPal[16][4]; + uint32 wsColors[8]; + uint32 wsCols[16][16]; + + uint32 ColorMapG[16]; + uint32 ColorMap[16*16*16]; + uint32 LayerEnabled; + + uint8 wsLine; /*current scanline*/ + + uint8 SpriteTable[0x80][4]; + uint32 SpriteCountCache; + uint8 DispControl; + uint8 BGColor; + uint8 LineCompare; + uint8 SPRBase; + uint8 SpriteStart, SpriteCount; + uint8 FGBGLoc; + uint8 FGx0, FGy0, FGx1, FGy1; + uint8 SPRx0, SPRy0, SPRx1, SPRy1; + + uint8 BGXScroll, BGYScroll; + uint8 FGXScroll, FGYScroll; + uint8 LCDControl, LCDIcons; + + uint8 BTimerControl; + uint16 HBTimerPeriod; + uint16 VBTimerPeriod; + + uint16 HBCounter, VBCounter; + uint8 VideoMode; + +public: + System *sys; +}; + + + +// ? +//extern uint32 dx_r,dx_g,dx_b,dx_sr,dx_sg,dx_sb; +//extern uint32 dx_bits,dx_pitch,cmov,dx_linewidth_blit,dx_buffer_line; + + + +} + +#endif diff --git a/wonderswan/interrupt.cpp b/wonderswan/interrupt.cpp new file mode 100644 index 0000000000..bff9d854fc --- /dev/null +++ b/wonderswan/interrupt.cpp @@ -0,0 +1,77 @@ +#include "system.h" +//#include + +namespace MDFN_IEN_WSWAN +{ + void Interrupt::Recalc() + { + IOn_Cache = FALSE; + IOn_Which = 0; + IVector_Cache = 0; + + for(int i = 0; i < 8; i++) + { + if(IStatus & IEnable & (1 << i)) + { + IOn_Cache = TRUE; + IOn_Which = i; + IVector_Cache = (IVectorBase + i) * 4; + break; + } + } + } + + void Interrupt::DebugForce(unsigned int level) + { + sys->cpu.interrupt((IVectorBase + level) * 4, TRUE); + } + + void Interrupt::DoInterrupt(int which) + { + if(IEnable & (1 << which)) + IStatus |= 1 << which; + + //printf("Interrupt: %d\n", which); + Recalc(); + } + + void Interrupt::Write(uint32 A, uint8 V) + { + //printf("Write: %04x %02x\n", A, V); + switch(A) + { + case 0xB0: IVectorBase = V; Recalc(); break; + case 0xB2: IEnable = V; IStatus &= IEnable; Recalc(); break; + case 0xB6: /*printf("IStatus: %02x\n", V);*/ IStatus &= ~V; Recalc(); break; + } + } + + uint8 Interrupt::Read(uint32 A) + { + //printf("Read: %04x\n", A); + switch(A) + { + case 0xB0: return(IVectorBase); + case 0xB2: return(IEnable); + case 0xB6: return(1 << IOn_Which); //return(IStatus); + } + return(0); + } + + void Interrupt::Check() + { + if(IOn_Cache) + { + sys->cpu.interrupt(IVector_Cache, FALSE); + } + } + + void Interrupt::Reset() + { + IEnable = 0x00; + IStatus = 0x00; + IVectorBase = 0x00; + Recalc(); + } + +} diff --git a/wonderswan/interrupt.h b/wonderswan/interrupt.h new file mode 100644 index 0000000000..6c41e18205 --- /dev/null +++ b/wonderswan/interrupt.h @@ -0,0 +1,48 @@ +#ifndef __WSWAN_INTERRUPT_H +#define __WSWAN_INTERRUPT_H + +#include "system.h" + +namespace MDFN_IEN_WSWAN +{ + +enum +{ + WSINT_SERIAL_SEND = 0, + WSINT_KEY_PRESS, + WSINT_RTC_ALARM, + WSINT_SERIAL_RECV, + WSINT_LINE_HIT, + WSINT_VBLANK_TIMER, + WSINT_VBLANK, + WSINT_HBLANK_TIMER +}; + +class Interrupt +{ +public: + void DoInterrupt(int); + void Write(uint32 A, uint8 V); + uint8 Read(uint32 A); + void Check(); + void Reset(); + void DebugForce(unsigned int level); + +private: + uint8 IStatus; + uint8 IEnable; + uint8 IVectorBase; + + bool IOn_Cache; + uint32 IOn_Which; + uint32 IVector_Cache; + +private: + void Recalc(); +public: + System *sys; +}; + +} + +#endif diff --git a/wonderswan/main.cpp b/wonderswan/main.cpp new file mode 100644 index 0000000000..e36fa1b575 --- /dev/null +++ b/wonderswan/main.cpp @@ -0,0 +1,94 @@ +/* Cygne + * + * Copyright notice for this file: + * Copyright (C) 2002 Dox dox@space.pl + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "system.h" +//#include +//#include +//#include + +//#include +//#include +//#include +#include +//#include + +namespace MDFN_IEN_WSWAN +{ +#if 0 + + + + + +#endif + +} + +/* +using namespace MDFN_IEN_WSWAN; + +MDFNGI EmulatedWSwan = +{ + "wswan", + "WonderSwan", + KnownExtensions, + MODPRIO_INTERNAL_HIGH, + #ifdef WANT_DEBUGGER + &DBGInfo, + #else + NULL, + #endif + &InputInfo, + Load, + TestMagic, + NULL, + NULL, + CloseGame, + WSwan_SetLayerEnableMask, + "Background\0Foreground\0Sprites\0", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + false, + StateAction, + Emulate, + SetInput, + DoSimpleCommand, + WSwanSettings, + MDFN_MASTERCLOCK_FIXED(3072000), + 0, + FALSE, // Multires possible? + + 224, // lcm_width + 144, // lcm_height + NULL, // Dummy + + 224, // Nominal width + 144, // Nominal height + + 224, // Framebuffer width + 144, // Framebuffer height + + 2, // Number of output sound channels +}; +*/ diff --git a/wonderswan/mednafen/state.h b/wonderswan/mednafen/state.h new file mode 100644 index 0000000000..d39a3ad865 --- /dev/null +++ b/wonderswan/mednafen/state.h @@ -0,0 +1,4 @@ +#ifndef _STATE_H +#define _STATE_H + +#endif diff --git a/wonderswan/mednafen/types.h b/wonderswan/mednafen/types.h new file mode 100644 index 0000000000..320aab5069 --- /dev/null +++ b/wonderswan/mednafen/types.h @@ -0,0 +1,201 @@ +#ifndef __MDFN_TYPES +#define __MDFN_TYPES + +#define __STDC_LIMIT_MACROS 1 + +// Make sure this file is included BEFORE a few common standard C header files(stdio.h, errno.h, math.h, AND OTHERS, but this is not an exhaustive check, nor +// should it be), so that any defines in config.h that change header file behavior will work properly. +#if defined(EOF) || defined(EACCES) || defined(F_LOCK) || defined(NULL) || defined(O_APPEND) || defined(M_LOG2E) + #error "Wrong include order for types.h" +#endif + +// Yes, yes, I know: There's a better place for including config.h than here, but I'm tired, and this should work fine. :b +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; + + +#if !defined(HAVE_NATIVE64BIT) && (SIZEOF_VOID_P >= 8 || defined(__x86_64__)) +#define HAVE_NATIVE64BIT 1 +#endif + +#ifdef __GNUC__ + + #define MDFN_MAKE_GCCV(maj,min,pl) (((maj)*100*100) + ((min) * 100) + (pl)) + #define MDFN_GCC_VERSION MDFN_MAKE_GCCV(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) + + #define INLINE inline __attribute__((always_inline)) + #define NO_INLINE __attribute__((noinline)) + + // + // Just avoid using fastcall with gcc before 4.1.0, as it(and similar regparm) + // tend to generate bad code on the older versions(between about 3.1.x and 4.0.x, at least) + // + // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12236 + // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=7574 + // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=17025 + // + #if MDFN_GCC_VERSION >= MDFN_MAKE_GCCV(4,1,0) + #if defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386) + #define MDFN_FASTCALL __attribute__((fastcall)) + #else + #define MDFN_FASTCALL + #endif + #else + #define MDFN_FASTCALL + #endif + + #define MDFN_ALIGN(n) __attribute__ ((aligned (n))) + #define MDFN_FORMATSTR(a,b,c) __attribute__ ((format (a, b, c))); + #define MDFN_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) + #define MDFN_NOWARN_UNUSED __attribute__((unused)) + + #define MDFN_UNLIKELY(n) __builtin_expect((n) != 0, 0) + #define MDFN_LIKELY(n) __builtin_expect((n) != 0, 1) + + #if MDFN_GCC_VERSION >= MDFN_MAKE_GCCV(4,3,0) + #define MDFN_COLD __attribute__((cold)) + #else + #define MDFN_COLD + #endif + + #undef MDFN_MAKE_GCCV + #undef MDFN_GCC_VERSION +#elif defined(_MSC_VER) + + #pragma message("Compiling with MSVC, untested") + + #define INLINE __forceinline + #define NO_INLINE __declspec(noinline) + + #define MDFN_FASTCALL __fastcall + + #define MDFN_ALIGN(n) __declspec(align(n)) + + #define MDFN_FORMATSTR(a,b,c) + + #define MDFN_WARN_UNUSED_RESULT + + #define MDFN_NOWARN_UNUSED + + #define MDFN_UNLIKELY(n) ((n) != 0) + #define MDFN_LIKELY(n) ((n) != 0) + + #define MDFN_COLD +#else + #error "Not compiling with GCC nor MSVC" + #define INLINE inline + #define NO_INLINE + + #define MDFN_FASTCALL + + #define MDFN_ALIGN(n) // hence the #error. + + #define MDFN_FORMATSTR(a,b,c) + + #define MDFN_WARN_UNUSED_RESULT + + #define MDFN_NOWARN_UNUSED + + #define MDFN_UNLIKELY(n) ((n) != 0) + #define MDFN_LIKELY(n) ((n) != 0) + + #define MDFN_COLD +#endif + + +typedef struct +{ + union + { + struct + { + #ifdef MSB_FIRST + uint8 High; + uint8 Low; + #else + uint8 Low; + uint8 High; + #endif + } Union8; + uint16 Val16; + }; +} Uuint16; + +typedef struct +{ + union + { + struct + { + #ifdef MSB_FIRST + Uuint16 High; + Uuint16 Low; + #else + Uuint16 Low; + Uuint16 High; + #endif + } Union16; + uint32 Val32; + }; +} Uuint32; + + +#if PSS_STYLE==2 + +#define PSS "\\" +#define MDFN_PS '\\' + +#elif PSS_STYLE==1 + +#define PSS "/" +#define MDFN_PS '/' + +#elif PSS_STYLE==3 + +#define PSS "\\" +#define MDFN_PS '\\' + +#elif PSS_STYLE==4 + +#define PSS ":" +#define MDFN_PS ':' + +#endif + +typedef uint32 UTF32; /* at least 32 bits */ +typedef uint16 UTF16; /* at least 16 bits */ +typedef uint8 UTF8; /* typically 8 bits */ +typedef unsigned char Boolean; /* 0 or 1 */ + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#undef require +#define require( expr ) assert( expr ) + +#if !defined(MSB_FIRST) && !defined(LSB_FIRST) + #error "Define MSB_FIRST or LSB_FIRST!" +#endif + +#include "error.h" + +#endif diff --git a/wonderswan/memory.cpp b/wonderswan/memory.cpp new file mode 100644 index 0000000000..847798ae35 --- /dev/null +++ b/wonderswan/memory.cpp @@ -0,0 +1,390 @@ +/* Cygne +* +* Copyright notice for this file: +* Copyright (C) 2002 Dox dox@space.pl +* +* 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "system.h" + +#include +#include +#include +#include + + +namespace MDFN_IEN_WSWAN +{ + + //extern uint16 WSButtonStatus; + + void Memory::Write20(uint32 A, uint8 V) + { + uint32 offset, bank; + + offset = A & 0xffff; + bank = (A>>16) & 0xF; + + if(!bank) /*RAM*/ + { + sys->sound.CheckRAMWrite(offset); + wsRAM[offset] = V; + + sys->gfx.InvalidByAddr(offset); + + if(offset>=0xfe00) /*WSC palettes*/ + sys->gfx.PaletteRAMWrite(offset, V); + } + else if(bank == 1) /* SRAM */ + { + if(sram_size) + { + wsSRAM[(offset | (BankSelector[1] << 16)) & (sram_size - 1)] = V; + } + } + } + + + + uint8 Memory::Read20(uint32 A) + { + uint32 offset, bank; + + offset = A & 0xFFFF; + bank = (A >> 16) & 0xF; + + switch(bank) + { + case 0: return wsRAM[offset]; + case 1: if(sram_size) + { + return wsSRAM[(offset | (BankSelector[1] << 16)) & (sram_size - 1)]; + } + else + return(0); + + case 2: + case 3: return wsCartROM[offset+((BankSelector[bank]&((rom_size>>16)-1))<<16)]; + + default: + { + uint8 bank_num = ((BankSelector[0] & 0xF) << 4) | (bank & 0xf); + bank_num &= (rom_size >> 16) - 1; + return(wsCartROM[(bank_num << 16) | offset]); + } + } + } + + void Memory::CheckDMA() + { + if(DMAControl & 0x80) + { + while(DMALength) + { + Write20(DMADest, Read20(DMASource)); + + DMASource++; // = ((DMASource + 1) & 0xFFFF) | (DMASource & 0xFF0000); + //if(!(DMASource & 0xFFFF)) puts("Warning: DMA source bank crossed."); + DMADest = ((DMADest + 1) & 0xFFFF) | (DMADest & 0xFF0000); + DMALength--; + } + } + DMAControl &= ~0x80; + } + + void Memory::CheckSoundDMA() + { + if(SoundDMAControl & 0x80) + { + if(SoundDMALength) + { + uint8 zebyte = Read20(SoundDMASource); + + if(SoundDMAControl & 0x08) + zebyte ^= 0x80; + + if(SoundDMAControl & 0x10) + sys->sound.Write(0x95, zebyte); // Pick a port, any port?! + else + sys->sound.Write(0x89, zebyte); + + SoundDMASource++; // = ((SoundDMASource + 1) & 0xFFFF) | (SoundDMASource & 0xFF0000); + //if(!(SoundDMASource & 0xFFFF)) puts("Warning: Sound DMA source bank crossed."); + SoundDMALength--; + } + if(!SoundDMALength) + SoundDMAControl &= ~0x80; + } + } + + uint8 Memory::readport(uint32 number) + { + number &= 0xFF; + + if(number >= 0x80 && number <= 0x9F) + return(sys->sound.Read(number)); + else if(number <= 0x3F || (number >= 0xA0 && number <= 0xAF) || (number == 0x60)) + return(sys->gfx.Read(number)); + else if((number >= 0xBA && number <= 0xBE) || (number >= 0xC4 && number <= 0xC8)) + return(sys->eeprom.Read(number)); + else if(number >= 0xCA && number <= 0xCB) + return(sys->rtc.Read(number)); + else switch(number) + { + //default: printf("Read: %04x\n", number); break; + case 0x40: return(DMASource >> 0); + case 0x41: return(DMASource >> 8); + case 0x42: return(DMASource >> 16); + + case 0x43: return(DMADest >> 16); + case 0x44: return(DMADest >> 0); + case 0x45: return(DMADest >> 8); + + case 0x46: return(DMALength >> 0); + case 0x47: return(DMALength >> 8); + + case 0x48: return(DMAControl); + + case 0xB0: + case 0xB2: + case 0xB6: return(sys->interrupt.Read(number)); + + case 0xC0: return(BankSelector[0] | 0x20); + case 0xC1: return(BankSelector[1]); + case 0xC2: return(BankSelector[2]); + case 0xC3: return(BankSelector[3]); + + case 0x4a: return(SoundDMASource >> 0); + case 0x4b: return(SoundDMASource >> 8); + case 0x4c: return(SoundDMASource >> 16); + case 0x4e: return(SoundDMALength >> 0); + case 0x4f: return(SoundDMALength >> 8); + case 0x52: return(SoundDMAControl); + + case 0xB1: return(CommData); + + case 0xb3: + { + uint8 ret = CommControl & 0xf0; + + if(CommControl & 0x80) + ret |= 0x4; // Send complete + + return(ret); + } + case 0xb5: + { + uint8 ret = (ButtonWhich << 4) | ButtonReadLatch; + return(ret); + } + } + + if(number >= 0xC8) + return language ? 0xD1 : 0xD0; + //return(0xD0 | language); // is this right? + + return(0); + } + + void Memory::writeport(uint32 IOPort, uint8 V) + { + IOPort &= 0xFF; + + if(IOPort >= 0x80 && IOPort <= 0x9F) + { + sys->sound.Write(IOPort, V); + } + else if((IOPort >= 0x00 && IOPort <= 0x3F) || (IOPort >= 0xA0 && IOPort <= 0xAF) || (IOPort == 0x60)) + { + sys->gfx.Write(IOPort, V); + } + else if((IOPort >= 0xBA && IOPort <= 0xBE) || (IOPort >= 0xC4 && IOPort <= 0xC8)) + sys->eeprom.Write(IOPort, V); + else if(IOPort >= 0xCA && IOPort <= 0xCB) + sys->rtc.Write(IOPort, V); + else switch(IOPort) + { + //default: printf("%04x %02x\n", IOPort, V); break; + + case 0x40: DMASource &= 0xFFFF00; DMASource |= (V << 0); break; + case 0x41: DMASource &= 0xFF00FF; DMASource |= (V << 8); break; + case 0x42: DMASource &= 0x00FFFF; DMASource |= ((V & 0x0F) << 16); break; + + case 0x43: DMADest &= 0x00FFFF; DMADest |= ((V & 0x0F) << 16); break; + case 0x44: DMADest &= 0xFFFF00; DMADest |= (V << 0); break; + case 0x45: DMADest &= 0xFF00FF; DMADest |= (V << 8); break; + + case 0x46: DMALength &= 0xFF00; DMALength |= (V << 0); break; + case 0x47: DMALength &= 0x00FF; DMALength |= (V << 8); break; + + case 0x48: DMAControl = V; + //if(V&0x80) + // printf("DMA%02x: %08x %08x %08x\n", V, DMASource, DMADest, DMALength); + CheckDMA(); + break; + + case 0x4a: SoundDMASource &= 0xFFFF00; SoundDMASource |= (V << 0); break; + case 0x4b: SoundDMASource &= 0xFF00FF; SoundDMASource |= (V << 8); break; + case 0x4c: SoundDMASource &= 0x00FFFF; SoundDMASource |= (V << 16); break; + //case 0x4d: break; // Unused? + case 0x4e: SoundDMALength &= 0xFF00; SoundDMALength |= (V << 0); break; + case 0x4f: SoundDMALength &= 0x00FF; SoundDMALength |= (V << 8); break; + //case 0x50: break; // Unused? + //case 0x51: break; // Unused? + case 0x52: SoundDMAControl = V; + //if(V & 0x80) printf("Sound DMA: %02x, %08x %08x\n", V, SoundDMASource, SoundDMALength); + break; + + case 0xB0: + case 0xB2: + case 0xB6: sys->interrupt.Write(IOPort, V); break; + + case 0xB1: CommData = V; break; + case 0xB3: CommControl = V & 0xF0; break; + + case 0xb5: ButtonWhich = V >> 4; + ButtonReadLatch = 0; + + if(ButtonWhich & 0x4) /*buttons*/ + ButtonReadLatch |= ((WSButtonStatus >> 8) << 1) & 0xF; + + if(ButtonWhich & 0x2) /* H/X cursors */ + ButtonReadLatch |= WSButtonStatus & 0xF; + + if(ButtonWhich & 0x1) /* V/Y cursors */ + ButtonReadLatch |= (WSButtonStatus >> 4) & 0xF; + break; + + case 0xC0: BankSelector[0] = V & 0xF; break; + case 0xC1: BankSelector[1] = V; break; + case 0xC2: BankSelector[2] = V; break; + case 0xC3: BankSelector[3] = V; break; + } + } + + Memory::~Memory() + { + if (wsCartROM) + { + std::free(wsCartROM); + wsCartROM = 0; + } + if (wsSRAM) + { + std::free(wsSRAM); + wsSRAM = 0; + } + } + + /* + void Memory::Kill() + { + if((sram_size || eeprom_size) && !SkipSL) + { + + std::vector EvilRams; + + if(eeprom_size) + EvilRams.push_back(PtrLengthPair(wsEEPROM, eeprom_size)); + + if(sram_size) + EvilRams.push_back(PtrLengthPair(wsSRAM, sram_size)); + + MDFN_DumpToFile(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav").c_str(), 6, EvilRams); + + } + + if(wsSRAM) + { + free(wsSRAM); + wsSRAM = NULL; + } + } + */ + + void Memory::Init(bool SkipSaveLoad, const Settings &settings) + { + char tmpname[17]; + std::memcpy(tmpname, settings.name, 16); + tmpname[16] = 0; + + + language = settings.language; + SkipSL = SkipSaveLoad; + + + // WSwan_EEPROMInit() will also clear wsEEPROM + sys->eeprom.Init(tmpname, settings.byear, settings.bmonth, settings.bday, settings.sex, settings.blood); + + if(sram_size) + { + wsSRAM = (uint8*)malloc(sram_size); + memset(wsSRAM, 0, sram_size); + } + + /* TODO: SAVERAM + if((sram_size || eeprom_size) && !SkipSL) + { + gzFile savegame_fp; + + savegame_fp = gzopen(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav").c_str(), "rb"); + if(savegame_fp) + { + if(eeprom_size) + gzread(savegame_fp, wsEEPROM, eeprom_size); + if(sram_size) + gzread(savegame_fp, wsSRAM, sram_size); + gzclose(savegame_fp); + } + } + */ + + //MDFNMP_AddRAM(wsRAMSize, 0x00000, wsRAM); // 65536 + + //if(sram_size) + // MDFNMP_AddRAM(sram_size, 0x10000, wsSRAM); + } + + void Memory::Reset() + { + memset(wsRAM, 0, 65536); + + wsRAM[0x75AC] = 0x41; + wsRAM[0x75AD] = 0x5F; + wsRAM[0x75AE] = 0x43; + wsRAM[0x75AF] = 0x31; + wsRAM[0x75B0] = 0x6E; + wsRAM[0x75B1] = 0x5F; + wsRAM[0x75B2] = 0x63; + wsRAM[0x75B3] = 0x31; + + std::memset(BankSelector, 0, sizeof(BankSelector)); + ButtonWhich = 0; + ButtonReadLatch = 0; + DMASource = 0; + DMADest = 0; + DMALength = 0; + DMAControl = 0; + + SoundDMASource = 0; + SoundDMALength = 0; + SoundDMAControl = 0; + + CommControl = 0; + CommData = 0; + } + +} diff --git a/wonderswan/memory.h b/wonderswan/memory.h new file mode 100644 index 0000000000..779685768e --- /dev/null +++ b/wonderswan/memory.h @@ -0,0 +1,85 @@ +#ifndef __WSWAN_MEMORY_H +#define __WSWAN_MEMORY_H + +#include "system.h" + +namespace MDFN_IEN_WSWAN +{ +class Memory +{ +public: + ~Memory(); + + uint8 Read20(uint32); + void Write20(uint32 address,uint8 data); + + void Init(bool SkipSaveLoad, const Settings &settings); + //void Kill(); + + void CheckSoundDMA(); + void Reset(); + void writeport(uint32 IOPort, uint8 V); + uint8 readport(uint32 number); + uint32 GetRegister(const unsigned int id, char *special, const uint32 special_len); + void SetRegister(const unsigned int id, uint32 value); + +private: + bool SkipSL; // Skip save and load + +public: + uint8 wsRAM[65536]; + uint8 *wsCartROM; + uint32 rom_size; + uint32 sram_size; + uint32 eeprom_size; + + uint16 WSButtonStatus; // bitfield of buttons, indeed + +private: + uint8 *wsSRAM; // = NULL; + + + uint8 ButtonWhich, ButtonReadLatch; + + uint32 DMASource, DMADest; + uint16 DMALength; + uint8 DMAControl; + + uint32 SoundDMASource; + uint16 SoundDMALength; + uint8 SoundDMAControl; + + uint8 BankSelector[4]; + + uint8 CommControl, CommData; + + bool language; + + +public: + System *sys; +private: + void CheckDMA(); + +}; + + +//extern uint8 wsRAM[65536]; +//extern uint8 *wsCartROM; +//extern uint32 eeprom_size; +//extern uint8 wsEEPROM[2048]; + + +enum +{ + MEMORY_GSREG_ROMBBSLCT = 0, + MEMORY_GSREG_BNK1SLCT, + MEMORY_GSREG_BNK2SLCT, + MEMORY_GSREG_BNK3SLCT, +}; + + + +} + +#endif diff --git a/wonderswan/msvc/changelog.txt b/wonderswan/msvc/changelog.txt new file mode 100644 index 0000000000..cf0539c253 --- /dev/null +++ b/wonderswan/msvc/changelog.txt @@ -0,0 +1,138 @@ +------------------------------------------------------------------------ +r26 | 2009-10-02 13:36:47 +0400 | 2 lines + +[Issue 5] Change to "stdint.h" to let compiler search for it in local directory. + +------------------------------------------------------------------------ +r25 | 2009-09-17 23:46:49 +0400 | 2 lines + +[Issue 4] Fix incorrect int8_t behaviour if compiled with /J flag. + +------------------------------------------------------------------------ +r24 | 2009-05-13 14:53:48 +0400 | 2 lines + +Forgot about #ifdef __cplusplus guard around 'extern "C"', so inclusion to C files has been broken. + +------------------------------------------------------------------------ +r23 | 2009-05-12 01:27:45 +0400 | 3 lines + +[Issue 2] Always wrap is included. + +------------------------------------------------------------------------ +r19 | 2007-07-04 02:14:40 +0400 | 3 lines + +Explicitly cast to appropriate type INT8_MIN, INT16_MIN, INT32_MIN and INT64_MIN constants. +Due to their unusual definition in Visual Studio headers (-_Ix_MAX-1) they are propagated to int and thus do not have expected type, causing VS6 strict compiler to claim about type inconsistency. + +------------------------------------------------------------------------ +r18 | 2007-06-26 16:53:23 +0400 | 2 lines + +Better handling of (U)INTx_C macros - now they generate constants of exact width. + +------------------------------------------------------------------------ +r17 | 2007-03-29 20:16:14 +0400 | 2 lines + +Fix typo: Miscrosoft -> Microsoft. + +------------------------------------------------------------------------ +r16 | 2007-02-24 17:32:58 +0300 | 4 lines + +Remove include, as it is not present in Visual Studio 2005 Epxress Edition and required only for INT_PTR and UINT_PTR types. + +'intptr_t' and 'uintptr_t' types now defined explicitly with #ifdef _WIN64. + +------------------------------------------------------------------------ +r15 | 2007-02-11 20:53:05 +0300 | 2 lines + +More correct fix for compilation under VS6. + +------------------------------------------------------------------------ +r14 | 2007-02-11 20:04:32 +0300 | 2 lines + +Bugfix: fix compiling under VS6, when stdint.h enclosed in 'extern "C" {}'. + +------------------------------------------------------------------------ +r13 | 2006-12-13 16:53:11 +0300 | 2 lines + +Make _inline modifier for imaxdiv default option. Use STATIC_IMAXDIV to make it static. + +------------------------------------------------------------------------ +r12 | 2006-12-13 16:42:24 +0300 | 2 lines + +Error message changed: VC6 supported from now. + +------------------------------------------------------------------------ +r11 | 2006-12-13 16:39:33 +0300 | 2 lines + +All (U)INT* types changed to (unsigned) __int*. This should make stdint.h compatible with VC6. + +------------------------------------------------------------------------ +r10 | 2006-12-13 16:20:57 +0300 | 3 lines + +Added INLINE_IMAXDIV define switch. +If INLINE_IMAXDIV is defined imaxdiv() have static modifier. If not - it is _inline. + +------------------------------------------------------------------------ +r9 | 2006-12-13 15:53:52 +0300 | 2 lines + +Error message for non-MSC compiler changed. + +------------------------------------------------------------------------ +r8 | 2006-12-13 12:47:48 +0300 | 2 lines + +Added #ifndef for SIZE_MAX (it is defined in limits.h on MSVSC 8). + +------------------------------------------------------------------------ +r7 | 2006-12-13 01:08:02 +0300 | 2 lines + +License chaged to BSD-derivative. + +------------------------------------------------------------------------ +r6 | 2006-12-13 00:53:20 +0300 | 2 lines + +Added include to avoid warnings when it is included after stdint.h. + +------------------------------------------------------------------------ +r5 | 2006-12-12 00:58:05 +0300 | 2 lines + +BUGFIX: Definitions of INTPTR_MIN, INTPTR_MAX and UINTPTR_MAX for WIN32 and WIN64 was mixed up. + +------------------------------------------------------------------------ +r4 | 2006-12-12 00:51:55 +0300 | 2 lines + +Rise #error if _MSC_VER is not defined. I.e. compiler other then Microsoft Visual C++ is used. + +------------------------------------------------------------------------ +r3 | 2006-12-11 22:54:14 +0300 | 2 lines + +Added include to stdint.h. + +------------------------------------------------------------------------ +r2 | 2006-12-11 21:39:27 +0300 | 2 lines + +Initial check in. + +------------------------------------------------------------------------ +r1 | 2006-12-11 21:30:23 +0300 | 1 line + +Initial directory structure. +------------------------------------------------------------------------ diff --git a/wonderswan/msvc/inttypes.h b/wonderswan/msvc/inttypes.h new file mode 100644 index 0000000000..25542771f5 --- /dev/null +++ b/wonderswan/msvc/inttypes.h @@ -0,0 +1,305 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + + +#endif // _MSC_INTTYPES_H_ ] diff --git a/wonderswan/msvc/stdint.h b/wonderswan/msvc/stdint.h new file mode 100644 index 0000000000..59d067302f --- /dev/null +++ b/wonderswan/msvc/stdint.h @@ -0,0 +1,247 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] diff --git a/wonderswan/rtc.cpp b/wonderswan/rtc.cpp new file mode 100644 index 0000000000..643a140637 --- /dev/null +++ b/wonderswan/rtc.cpp @@ -0,0 +1,98 @@ +/* Cygne +* +* Copyright notice for this file: +* Copyright (C) 2002 Dox dox@space.pl +* +* 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "system.h" +#include + +namespace MDFN_IEN_WSWAN +{ + + /* + static uint64 CurrentTime; + static uint32 ClockCycleCounter; + static uint8 wsCA15; + static uint8 Command, Data; + */ + + void RTC::Write(uint32 A, uint8 V) + { + switch(A) + { + case 0xca: + if(V==0x15) + wsCA15=0; + Command = V; + break; + case 0xcb: Data = V; break; + } + + } + + + uint8 RTC::Read(uint32 A) + { + switch(A) + { + case 0xca : return (Command|0x80); + case 0xcb : + if(Command == 0x15) + { + time_t long_time = CurrentTime; + struct tm *newtime = gmtime( &long_time ); + + switch(wsCA15) + { + case 0: wsCA15++;return mBCD(newtime->tm_year-100); + case 1: wsCA15++;return mBCD(newtime->tm_mon); + case 2: wsCA15++;return mBCD(newtime->tm_mday); + case 3: wsCA15++;return mBCD(newtime->tm_wday); + case 4: wsCA15++;return mBCD(newtime->tm_hour); + case 5: wsCA15++;return mBCD(newtime->tm_min); + case 6: wsCA15=0;return mBCD(newtime->tm_sec); + } + return 0; + } + else + return Data | 0x80; + + } + return(0); + } + + void RTC::Reset() + { + time_t happy_time = time(NULL); + + CurrentTime = mktime(localtime(&happy_time)); + ClockCycleCounter = 0; + wsCA15 = 0; + } + + void RTC::Clock(uint32 cycles) + { + ClockCycleCounter += cycles; + while(ClockCycleCounter >= 3072000) + { + ClockCycleCounter -= 3072000; + CurrentTime++; + } + } + +} diff --git a/wonderswan/rtc.h b/wonderswan/rtc.h new file mode 100644 index 0000000000..d4319e2c43 --- /dev/null +++ b/wonderswan/rtc.h @@ -0,0 +1,28 @@ +#ifndef __WSWAN_RTC_H +#define __WSWAN_RTC_H + +#include "system.h" + +namespace MDFN_IEN_WSWAN +{ +class RTC +{ +public: + void Write(uint32 A, uint8 V); + uint8 Read(uint32 A); + void Reset(); + void Clock(uint32 cycles); + +private: + uint64 CurrentTime; + uint32 ClockCycleCounter; + uint8 wsCA15; + uint8 Command, Data; +public: + System *sys; + +}; + +} + +#endif diff --git a/wonderswan/sound.cpp b/wonderswan/sound.cpp new file mode 100644 index 0000000000..90300a61e9 --- /dev/null +++ b/wonderswan/sound.cpp @@ -0,0 +1,386 @@ +/* Mednafen - Multi-system Emulator +* +* 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* +Noise emulation is almost certainly wrong wrong wrong. Testing on a real system is needed to determine LFSR(assuming it uses an LFSR) taps. +*/ + +#include "system.h" + +#include + + +namespace MDFN_IEN_WSWAN +{ +#define MK_SAMPLE_CACHE \ + { \ + int sample; \ + sample = (((ram[((SampleRAMPos << 6) + (sample_pos[ch] >> 1) + (ch << 4)) ] >> ((sample_pos[ch] & 1) ? 4 : 0)) & 0x0F)) - 0x8; \ + sample_cache[ch][0] = sample * ((volume[ch] >> 4) & 0x0F); \ + sample_cache[ch][1] = sample * ((volume[ch] >> 0) & 0x0F); \ + } + +#define MK_SAMPLE_CACHE_NOISE \ + { \ + int sample; \ + sample = ((nreg & 1) ? 0xF : 0x0) - 0x8; \ + sample_cache[ch][0] = sample * ((volume[ch] >> 4) & 0x0F); \ + sample_cache[ch][1] = sample * ((volume[ch] >> 0) & 0x0F); \ + } + + +#define SYNCSAMPLE(wt) \ + { \ + int32 left = sample_cache[ch][0], right = sample_cache[ch][1]; \ + WaveSynth.offset_inline(wt, left - last_val[ch][0], sbuf[0]); \ + WaveSynth.offset_inline(wt, right - last_val[ch][1], sbuf[1]); \ + last_val[ch][0] = left; \ + last_val[ch][1] = right; \ + } + +#define SYNCSAMPLE_NOISE(wt) \ + { \ + int32 left = sample_cache[ch][0], right = sample_cache[ch][1]; \ + NoiseSynth.offset_inline(wt, left - last_val[ch][0], sbuf[0]); \ + NoiseSynth.offset_inline(wt, right - last_val[ch][1], sbuf[1]); \ + last_val[ch][0] = left; \ + last_val[ch][1] = right; \ + } + + void Sound::Update() + { + int32 run_time; + const uint8 *ram = sys->memory.wsRAM; + const uint32 current_ts = sys->cpu.timestamp; + + //printf("%d\n", v30mz_timestamp); + //printf("%02x %02x\n", control, noise_control); + run_time = current_ts - last_ts; + + for(unsigned int ch = 0; ch < 4; ch++) + { + // Channel is disabled? + if(!(control & (1 << ch))) + continue; + + if(ch == 1 && (control & 0x20)) // Direct D/A mode? + { + int32 neoval = (volume[ch] - 0x80) * voice_volume; + + VoiceSynth.offset(current_ts, neoval - last_v_val, sbuf[0]); + VoiceSynth.offset(current_ts, neoval - last_v_val, sbuf[1]); + + last_v_val = neoval; + } + else if(ch == 2 && (control & 0x40) && sweep_value) // Sweep + { + uint32 tmp_pt = 2048 - period[ch]; + uint32 meow_timestamp = current_ts - run_time; + uint32 tmp_run_time = run_time; + + while(tmp_run_time) + { + int32 sub_run_time = tmp_run_time; + + if(sub_run_time > sweep_8192_divider) + sub_run_time = sweep_8192_divider; + + sweep_8192_divider -= sub_run_time; + if(sweep_8192_divider <= 0) + { + sweep_8192_divider += 8192; + sweep_counter--; + if(sweep_counter <= 0) + { + sweep_counter = sweep_step + 1; + period[ch] = (period[ch] + (int8)sweep_value) & 0x7FF; + } + } + + meow_timestamp += sub_run_time; + if(tmp_pt > 4) + { + period_counter[ch] -= sub_run_time; + while(period_counter[ch] <= 0) + { + sample_pos[ch] = (sample_pos[ch] + 1) & 0x1F; + + MK_SAMPLE_CACHE; + SYNCSAMPLE(meow_timestamp + period_counter[ch]); + period_counter[ch] += tmp_pt; + } + } + tmp_run_time -= sub_run_time; + } + } + else if(ch == 3 && (noise_control & 0x10)) //(control & 0x80)) // Noise + { + uint32 tmp_pt = 2048 - period[ch]; + + period_counter[ch] -= run_time; + while(period_counter[ch] <= 0) + { + // Yay, random numbers, so let's use totally wrong numbers to make them! + const int bstab1[8] = { 14, 13, 12, 14, 12, 13, 14, 14 }; + const int bstab2[8] = { 13, 12, 9, 12, 1, 1, 5, 11 }; + //const int bstab1[8] = { 14, 13, 12, 14, 10, 9, 8, 13 }; + //const int bstab2[8] = { 13, 12, 9, 12, 1, 6, 4, 11 }; + nreg = (~((nreg << 1) | ( ((nreg >> bstab1[noise_control & 0x7]) & 1) ^ ((nreg >> bstab2[noise_control & 0x7]) & 1)))) & 0x7FFF; + if(control & 0x80) + { + MK_SAMPLE_CACHE_NOISE; + SYNCSAMPLE_NOISE(current_ts + period_counter[ch]); + } + else if(tmp_pt > 4) + { + sample_pos[ch] = (sample_pos[ch] + 1) & 0x1F; + MK_SAMPLE_CACHE; + SYNCSAMPLE(current_ts + period_counter[ch]); + } + period_counter[ch] += tmp_pt; + } + } + else + { + uint32 tmp_pt = 2048 - period[ch]; + + if(tmp_pt > 4) + { + period_counter[ch] -= run_time; + while(period_counter[ch] <= 0) + { + sample_pos[ch] = (sample_pos[ch] + 1) & 0x1F; + + MK_SAMPLE_CACHE; + SYNCSAMPLE(current_ts + period_counter[ch]); // - period_counter[ch]); + period_counter[ch] += tmp_pt; + } + } + } + } + + { + int32 tmphv = HyperVoice; + + if(tmphv - last_hv_val) + { + WaveSynth.offset_inline(current_ts, tmphv - last_hv_val, sbuf[0]); + WaveSynth.offset_inline(current_ts, tmphv - last_hv_val, sbuf[1]); + last_hv_val = tmphv; + } + } + last_ts = current_ts; + } + + void Sound::Write(uint32 A, uint8 V) + { + Update(); + + if(A >= 0x80 && A <= 0x87) + { + int ch = (A - 0x80) >> 1; + + if(A & 1) + period[ch] = (period[ch] & 0x00FF) | ((V & 0x07) << 8); + else + period[ch] = (period[ch] & 0x0700) | ((V & 0xFF) << 0); + } + else if(A >= 0x88 && A <= 0x8B) + { + volume[A - 0x88] = V; + } + else if(A == 0x8C) + sweep_value = V; + else if(A == 0x8D) + { + sweep_step = V; + sweep_counter = sweep_step + 1; + sweep_8192_divider = 8192; + } + else if(A == 0x8E) + { + noise_control = V; + if(V & 0x8) nreg = 1; + //printf("NOISECONTROL: %02x\n", V); + } + else if(A == 0x90) + { + for(int n = 0; n < 4; n++) + if(!(control & (1 << n)) && (V & (1 << n))) + { + period_counter[n] = 0; + sample_pos[n] = 0x1F; + } + control = V; + //printf("Sound Control: %02x\n", V); + } + else if(A == 0x91) + { + output_control = V & 0xF; + //printf("%02x, %02x\n", V, (V >> 1) & 3); + } + else if(A == 0x92) + nreg = (nreg & 0xFF00) | (V << 0); + else if(A == 0x93) + nreg = (nreg & 0x00FF) | ((V & 0x7F) << 8); + else if(A == 0x94) + { + voice_volume = V & 0xF; + //printf("%02x\n", V); + } + else switch(A) + { + case 0x8F: SampleRAMPos = V; break; + case 0x95: HyperVoice = V; break; // Pick a port, any port?! + //default: printf("%04x:%02x\n", A, V); break; + } + Update(); + } + + uint8 Sound::Read(uint32 A) + { + Update(); + + if(A >= 0x80 && A <= 0x87) + { + int ch = (A - 0x80) >> 1; + + if(A & 1) + return(period[ch] >> 8); + else + return(period[ch]); + } + else if(A >= 0x88 && A <= 0x8B) + return(volume[A - 0x88]); + else switch(A) + { + default: /*printf("SoundRead: %04x\n", A);*/ return(0); + case 0x8C: return(sweep_value); + case 0x8D: return(sweep_step); + case 0x8E: return(noise_control); + case 0x8F: return(SampleRAMPos); + case 0x90: return(control); + case 0x91: return(output_control | 0x80); + case 0x92: return((nreg >> 0) & 0xFF); + case 0x93: return((nreg >> 8) & 0xFF); + case 0x94: return(voice_volume); + } + } + + + int32 Sound::Flush(int16 *SoundBuf, const int32 MaxSoundFrames) + { + int32 FrameCount = 0; + + Update(); + + if(SoundBuf) + { + for(int y = 0; y < 2; y++) + { + sbuf[y]->end_frame(sys->cpu.timestamp); + FrameCount = sbuf[y]->read_samples(SoundBuf + y, MaxSoundFrames, true); + } + } + + last_ts = 0; + + return(FrameCount); + } + + // Call before wsRAM is updated + void Sound::CheckRAMWrite(uint32 A) + { + if((A >> 6) == SampleRAMPos) + Update(); + } + + void Sound::Init() + { + } + + Sound::Sound() + { + for(int i = 0; i < 2; i++) + { + sbuf[i] = new Blip_Buffer(); + + sbuf[i]->set_sample_rate(0 ? 0 : 44100, 60); + sbuf[i]->clock_rate((long)(3072000)); + sbuf[i]->bass_freq(20); + } + + double eff_volume = 1.0 / 4; + + WaveSynth.volume(eff_volume); + NoiseSynth.volume(eff_volume); + VoiceSynth.volume(eff_volume); + + SetRate(44100); + } + + Sound::~Sound() + { + for(int i = 0; i < 2; i++) + { + if(sbuf[i]) + { + delete sbuf[i]; + sbuf[i] = 0; + } + } + + } + + bool Sound::SetRate(uint32 rate) + { + for(int i = 0; i < 2; i++) + sbuf[i]->set_sample_rate(rate?rate:44100, 60); + + return(TRUE); + } + + void Sound::Reset() + { + std::memset(period, 0, sizeof(period)); + std::memset(volume, 0, sizeof(volume)); + voice_volume = 0; + sweep_step = 0; + sweep_value = 0; + noise_control = 0; + control = 0; + output_control = 0; + + sweep_8192_divider = 8192; + sweep_counter = 0; + SampleRAMPos = 0; + std::memset(period_counter, 0, sizeof(period_counter)); + std::memset(sample_pos, 0, sizeof(sample_pos)); + nreg = 1; + + std::memset(sample_cache, 0, sizeof(sample_cache)); + std::memset(last_val, 0, sizeof(last_val)); + last_v_val = 0; + + HyperVoice = 0; + last_hv_val = 0; + + for(int y = 0; y < 2; y++) + sbuf[y]->clear(); + } + +} diff --git a/wonderswan/sound.h b/wonderswan/sound.h new file mode 100644 index 0000000000..e0c9cddd04 --- /dev/null +++ b/wonderswan/sound.h @@ -0,0 +1,71 @@ +#ifndef __WSWAN_SOUND_H +#define __WSWAN_SOUND_H + +#include "system.h" +#include + +namespace MDFN_IEN_WSWAN +{ + +class Sound +{ +public: + Sound(); + ~Sound(); + + int32 Flush(int16 *SoundBuf, const int32 MaxSoundFrames); + + void Init(); + void Kill(); + void SetMultiplier(double multiplier); + bool SetRate(uint32 rate); + + void Write(uint32, uint8); + uint8 Read(uint32); + void Reset(); + void CheckRAMWrite(uint32 A); + +private: + Blip_Synth WaveSynth; + Blip_Synth NoiseSynth; + Blip_Synth VoiceSynth; + + Blip_Buffer *sbuf[2]; // = { NULL }; + + uint16 period[4]; + uint8 volume[4]; // left volume in upper 4 bits, right in lower 4 bits + uint8 voice_volume; + + uint8 sweep_step, sweep_value; + uint8 noise_control; + uint8 control; + uint8 output_control; + + int32 sweep_8192_divider; + uint8 sweep_counter; + uint8 SampleRAMPos; + + int32 sample_cache[4][2]; + + int32 last_v_val; + + uint8 HyperVoice; + int32 last_hv_val; + + int32 period_counter[4]; + int32 last_val[4][2]; // Last outputted value, l&r + uint8 sample_pos[4]; + uint16 nreg; + uint32 last_ts; + +private: + void Update(); + +public: + System *sys; + +}; + +} + +#endif diff --git a/wonderswan/start.inc b/wonderswan/start.inc new file mode 100644 index 0000000000..54a871f986 --- /dev/null +++ b/wonderswan/start.inc @@ -0,0 +1,269 @@ +/* + =================================================================================== + Cygne WIN v 2.1a (c) Dox 2002 dox@space.pl + =================================================================================== + + NEC cpu core by Bryan McPhail,Oliver Bergmann, Fabrice Frances and David Hedley + Zlib by Jean-loup Gailly and Mark Adler + + =================================================================================== +*/ + +const uint8 startio[256]={ +0x00,//0 +0x00,//1 +0x9d,//2 +0xbb,//3 +0x00,//4 +0x00,//5 +0x00,//6 +0x26,//7 +0xfe,//8 +0xde,//9 +0xf9,//a +0xfb,//b +0xdb,//c +0xd7,//d +0x7f,//e +0xf5,//f +0x00,//10 +0x00,//11 +0x00,//12 +0x00,//13 +0x01,//14 +0x00,//15 +0x9e,//16 +0x9b,//17 +0x00,//18 +0x00,//19 +0x00,//1a +0x00,//1b +0x99,//1c +0xfd,//1d +0xb7,//1e +0xdf,//1f +0x30,//20 +0x57,//21 +0x75,//22 +0x76,//23 +0x15,//24 +0x73,//25 +0x77,//26 +0x77,//27 +0x20,//28 +0x75,//29 +0x50,//2a +0x36,//2b +0x70,//2c +0x67,//2d +0x50,//2e +0x77,//2f +0x57,//30 +0x54,//31 +0x75,//32 +0x77,//33 +0x75,//34 +0x17,//35 +0x37,//36 +0x73,//37 +0x50,//38 +0x57,//39 +0x60,//3a +0x77,//3b +0x70,//3c +0x77,//3d +0x10,//3e +0x73,//3f +0x00,//40 +0x00,//41 +0x00,//42 +0x00,//43 +0x00,//44 +0x00,//45 +0x00,//46 +0x00,//47 +0x00,//48 +0x00,//49 +0x00,//4a +0x00,//4b +0x00,//4c +0x00,//4d +0x00,//4e +0x00,//4f +0x00,//50 +0x00,//51 +0x00,//52 +0x00,//53 +0x00,//54 +0x00,//55 +0x00,//56 +0x00,//57 +0x00,//58 +0x00,//59 +0x00,//5a +0x00,//5b +0x00,//5c +0x00,//5d +0x00,//5e +0x00,//5f +0x0a,//60 +0x00,//61 +0x00,//62 +0x00,//63 +0x00,//64 +0x00,//65 +0x00,//66 +0x00,//67 +0x00,//68 +0x00,//69 +0x00,//6a +0x0f,//6b +0x00,//6c +0x00,//6d +0x00,//6e +0x00,//6f +0x00,//70 +0x00,//71 +0x00,//72 +0x00,//73 +0x00,//74 +0x00,//75 +0x00,//76 +0x00,//77 +0x00,//78 +0x00,//79 +0x00,//7a +0x00,//7b +0x00,//7c +0x00,//7d +0x00,//7e +0x00,//7f +0x00,//80 +0x00,//81 +0x00,//82 +0x00,//83 +0x00,//84 +0x00,//85 +0x00,//86 +0x00,//87 +0x00,//88 +0x00,//89 +0x00,//8a +0x00,//8b +0x00,//8c +0x1f,//8d 1d ? +0x00,//8e +0x00,//8f +0x00,//90 +0x00,//91 +0x00,//92 +0x00,//93 +0x00,//94 +0x00,//95 +0x00,//96 +0x00,//97 +0x00,//98 +0x00,//99 +0x00,//9a +0x00,//9b +0x00,//9c +0x00,//9d +0x03,//9e +0x00,//9f +0x87-2,//a0 +0x00,//a1 +0x00,//a2 +0x00,//a3 +0x0,//a4 2b +0x0,//a5 7f +0x4f,//a6 +0xff,//a7 cf ? +0x00,//a8 +0x00,//a9 +0x00,//aa +0x00,//ab +0x00,//ac +0x00,//ad +0x00,//ae +0x00,//af +0x00,//b0 +0xdb,//b1 +0x00,//b2 +0x00,//b3 +0x00,//b4 +0x40,//b5 +0x00,//b6 +0x00,//b7 +0x00,//b8 +0x00,//b9 +0x01,//ba +0x00,//bb +0x42,//bc +0x00,//bd +0x83,//be +0x00,//bf +0x2f,//c0 +0x3f,//c1 +0xff,//c2 +0xff,//c3 +0x00,//c4 +0x00,//c5 +0x00,//c6 +0x00,//c7 + +0xd1,//c8? +0xd1,//c9 +0xd1,//ca +0xd1,//cb +0xd1,//cc +0xd1,//cd +0xd1,//ce +0xd1,//cf +0xd1,//d0 +0xd1,//d1 +0xd1,//d2 +0xd1,//d3 +0xd1,//d4 +0xd1,//d5 +0xd1,//d6 +0xd1,//d7 +0xd1,//d8 +0xd1,//d9 +0xd1,//da +0xd1,//db +0xd1,//dc +0xd1,//dd +0xd1,//de +0xd1,//df +0xd1,//e0 +0xd1,//e1 +0xd1,//e2 +0xd1,//e3 +0xd1,//e4 +0xd1,//e5 +0xd1,//e6 +0xd1,//e7 +0xd1,//e8 +0xd1,//e9 +0xd1,//ea +0xd1,//eb +0xd1,//ec +0xd1,//ed +0xd1,//ee +0xd1,//ef +0xd1,//f0 +0xd1,//f1 +0xd1,//f2 +0xd1,//f3 +0xd1,//f4 +0xd1,//f5 +0xd1,//f6 +0xd1,//f7 +0xd1,//f8 +0xd1,//f9 +0xd1,//fa +0xd1,//fb +0xd1,//fc +0xd1,//fd +0xd1,//fe +0xd1};//ff diff --git a/wonderswan/system.cpp b/wonderswan/system.cpp new file mode 100644 index 0000000000..d314822362 --- /dev/null +++ b/wonderswan/system.cpp @@ -0,0 +1,315 @@ +/* Cygne +* +* Copyright notice for this file: +* Copyright (C) 2002 Dox dox@space.pl +* +* 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "system.h" +#include +#include +#include +#include + +#define EXPORT extern "C" __declspec(dllexport) + +namespace MDFN_IEN_WSWAN +{ + +#include "start.inc" + + typedef struct + { + const uint8 id; + const char *name; + } DLEntry; + + static const DLEntry Developers[] = + { + { 0x01, "Bandai" }, + { 0x02, "Taito" }, + { 0x03, "Tomy" }, + { 0x04, "Koei" }, + { 0x05, "Data East" }, + { 0x06, "Asmik" }, // Asmik Ace? + { 0x07, "Media Entertainment" }, + { 0x08, "Nichibutsu" }, + { 0x0A, "Coconuts Japan" }, + { 0x0B, "Sammy" }, + { 0x0C, "Sunsoft" }, + { 0x0D, "Mebius" }, + { 0x0E, "Banpresto" }, + { 0x10, "Jaleco" }, + { 0x11, "Imagineer" }, + { 0x12, "Konami" }, + { 0x16, "Kobunsha" }, + { 0x17, "Bottom Up" }, + { 0x18, "Naxat" }, // Mechanic Arms? Media Entertainment? Argh! + { 0x19, "Sunrise" }, + { 0x1A, "Cyberfront" }, + { 0x1B, "Megahouse" }, + { 0x1D, "Interbec" }, + { 0x1E, "NAC" }, + { 0x1F, "Emotion" }, // Bandai Visual?? + { 0x20, "Athena" }, + { 0x21, "KID" }, + { 0x24, "Omega Micott" }, + { 0x25, "Upstar" }, + { 0x26, "Kadokawa/Megas" }, + { 0x27, "Cocktail Soft" }, + { 0x28, "Squaresoft" }, + { 0x2B, "TomCreate" }, + { 0x2D, "Namco" }, + { 0x2F, "Gust" }, + { 0x36, "Capcom" }, + }; + + void System::Reset() + { + cpu.reset(); + memory.Reset(); + gfx.Reset(); + sound.Reset(); + interrupt.Reset(); + rtc.Reset(); + eeprom.Reset(); + + for(int u0=0;u0<0xc9;u0++) + { + if(u0 != 0xC4 && u0 != 0xC5 && u0 != 0xBA && u0 != 0xBB) + memory.writeport(u0,startio[u0]); + } + + cpu.set_reg(NEC_SS,0); + cpu.set_reg(NEC_SP,0x2000); + } + + + void System::Advance(uint16 buttons, bool novideo, uint32 *surface, int16 *soundbuff, int &soundbuffsize) + { + + memory.WSButtonStatus = buttons; + while (!gfx.ExecuteLine(surface, novideo)) + { + } + + soundbuffsize = sound.Flush(soundbuff, soundbuffsize); + + // cycles elapsed in the frame can be read here + // how is this OK to reset? it's only used by the sound code, so once the sound for the frame has + // been collected, it's OK to zero. indeed, it should be done as there's no rollover protection + cpu.timestamp = 0; + + } + + // Source: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + // Rounds up to the nearest power of 2. + static INLINE uint64 round_up_pow2(uint64 v) + { + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v++; + + v += (v == 0); + + return(v); + } + + bool System::Load(const uint8 *data, int length, const Settings &settings) + { + uint32 real_rom_size; + + if(length < 65536) + { + Debug::puts("Rom image is too small (<64K)"); + return false; + } + + if(!memcmp(data + length - 0x20, "WSRF", 4)) + { + Debug::puts("WSRF files not supported"); + return false; + } + + + real_rom_size = (length + 0xFFFF) & ~0xFFFF; + memory.rom_size = round_up_pow2(real_rom_size); + + memory.wsCartROM = (uint8 *)std::calloc(1, memory.rom_size); + + + if(real_rom_size < memory.rom_size) + memset(memory.wsCartROM, 0xFF, memory.rom_size - real_rom_size); + + memcpy(memory.wsCartROM + (memory.rom_size - real_rom_size), data, length); + + + uint8 header[10]; + memcpy(header, memory.wsCartROM + memory.rom_size - 10, 10); + + { + const char *developer_name = "???"; + for(unsigned int x = 0; x < sizeof(Developers) / sizeof(DLEntry); x++) + { + if(Developers[x].id == header[0]) + { + developer_name = Developers[x].name; + break; + } + } + Debug::printf("Developer: %s (0x%02x)\n", developer_name, header[0]); + } + + memory.sram_size = 0; + memory.eeprom_size = 0; + + switch(header[5]) + { + case 0x01: memory.sram_size = 8*1024; break; + case 0x02: memory.sram_size = 32*1024; break; + case 0x03: memory.sram_size = 16 * 65536; break; + case 0x04: memory.sram_size = 32 * 65536; break; // Dicing Knight! + + case 0x10: memory.eeprom_size = 128; break; + case 0x20: memory.eeprom_size = 2*1024; break; + case 0x50: memory.eeprom_size = 1024; break; + } + + //printf("%02x\n", header[5]); + + if(memory.eeprom_size) + Debug::printf("EEPROM: %d bytes\n", memory.eeprom_size); + + if(memory.sram_size) + Debug::printf("Battery-backed RAM: %d bytes\n", memory.sram_size); + + Debug::printf("Recorded Checksum: 0x%04x\n", header[8] | (header[9] << 8)); + { + uint16 real_crc = 0; + for(unsigned int i = 0; i < memory.rom_size - 2; i++) + real_crc += memory.wsCartROM[i]; + Debug::printf("Real Checksum: 0x%04x\n", real_crc); + } + + if((header[8] | (header[9] << 8)) == 0x8de1 && (header[0]==0x01)&&(header[2]==0x27)) /* Detective Conan */ + { + Debug::printf("Activating Detective Conan Hack\n"); + /* WS cpu is using cache/pipeline or there's protected ROM bank where pointing CS */ + memory.wsCartROM[0xfffe8]=0xea; + memory.wsCartROM[0xfffe9]=0x00; + memory.wsCartROM[0xfffea]=0x00; + memory.wsCartROM[0xfffeb]=0x00; + memory.wsCartROM[0xfffec]=0x20; + } + + + if(header[6] & 0x1) + { + //MDFNGameInfo->rotated = MDFN_ROTATE90; + } + + + //MDFNMP_Init(16384, (1 << 20) / 1024); + + cpu.init(); + + // TODO: control WSC setting + // TODO: rip out skipsaveload code + + memory.Init(false, settings); + + gfx.Init(); + //MDFNGameInfo->fps = (uint32)((uint64)3072000 * 65536 * 256 / (159*256)); + + sound.Init(); + + gfx.MakeTiles(); + + Reset(); + + return true; + } + + void *System::operator new(std::size_t size) + { + void *p = ::operator new(size); + std::memset(p, 0, size); + return p; + } + + System::System() + :wsc(1) + { + gfx.sys = this; + memory.sys = this; + eeprom.sys = this; + rtc.sys = this; + sound.sys = this; + cpu.sys = this; + interrupt.sys = this; + } + + System::~System() + { + } + + // maybe change? + int Debug::puts ( const char * str ) + { + return std::puts(str); + } + int Debug::printf ( const char * format, ... ) + { + va_list args; + va_start(args, format); + int ret = vprintf(format, args); + va_end(args); + return ret; + } + + + + EXPORT System *bizswan_new() + { + return new System(); + } + + EXPORT void bizswan_delete(System *s) + { + delete s; + } + + EXPORT void bizswan_reset(System *s) + { + s->Reset(); + } + + EXPORT void bizswan_advance(System *s, uint16 buttons, bool novideo, uint32 *surface, int16 *soundbuff, int *soundbuffsize) + { + s->Advance(buttons, novideo, surface, soundbuff, *soundbuffsize); + } + + EXPORT int bizswan_load(System *s, const uint8 *data, int length, const Settings *settings) + { + return s->Load(data, length, *settings); + } + +} diff --git a/wonderswan/system.h b/wonderswan/system.h new file mode 100644 index 0000000000..9638b83d66 --- /dev/null +++ b/wonderswan/system.h @@ -0,0 +1,68 @@ +#ifndef SYSTEM_H +#define SYSTEM_H + +namespace MDFN_IEN_WSWAN +{ +class System; +struct Settings; +} + +#include "wswan.h" +#include "gfx.h" +#include "memory.h" +#include "eeprom.h" +#include "rtc.h" +#include "sound.h" +#include "v30mz.h" +#include "interrupt.h" + +#include + +namespace MDFN_IEN_WSWAN +{ +class System +{ +public: + System(); + ~System(); + + static void* operator new(std::size_t size); + + void Reset(); + void Advance(uint16 buttons, bool novideo, uint32 *surface, int16 *soundbuff, int &soundbuffsize); + bool Load(const uint8 *data, int length, const Settings &s); + +public: + GFX gfx; + Memory memory; + EEPROM eeprom; + RTC rtc; + Sound sound; + V30MZ cpu; + Interrupt interrupt; +public: + int wsc; // 1 = 1; /*color/mono*/ + +}; + +struct Settings +{ + uint16 byear; // birth year, 0000-9999 + uint8 bmonth; // birth month, 1-12 + uint8 bday; // birth day, 1-31 + char name[17]; // up to 16 chars long, most chars don't work (conversion from ascii is internal) + uint8 language; // 0 = J, 1 = E; only affects "Digimon Tamers - Battle Spirit" + uint8 sex; // sex, 1 = male, 2 = female + uint8 blood; // 1 = a, 2 = b, 3 = o, 4 = ab + bool rotateinput; // true to rotate input and dpads, sync setting because of this +}; + +namespace Debug +{ +int puts ( const char * str ); +int printf ( const char * format, ... ); +} + +} + +#endif diff --git a/wonderswan/tcache.cpp b/wonderswan/tcache.cpp new file mode 100644 index 0000000000..661dcb4f3c --- /dev/null +++ b/wonderswan/tcache.cpp @@ -0,0 +1,290 @@ +/* Cygne +* +* Copyright notice for this file: +* Copyright (C) 2002 Dox dox@space.pl +* +* 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "system.h" + +#include + +namespace MDFN_IEN_WSWAN +{ + + void GFX::InvalidByAddr(uint32 ws_offset) + { + if(wsVMode && (ws_offset>=0x4000)&&(ws_offset<0x8000)) + { + wsTCacheUpdate[(ws_offset-0x4000)>>5]=FALSE; /*invalidate tile*/ + return; + } + else if((ws_offset>=0x2000)&&(ws_offset<0x4000)) + { + wsTCacheUpdate[(ws_offset-0x2000)>>4]=FALSE; /*invalidate tile*/ + return; + } + + if(wsVMode && (ws_offset>=0x8000)&&(ws_offset<0xc000)) + { + wsTCacheUpdate2[(ws_offset-0x8000)>>5]=FALSE; /*invalidate tile*/ + return; + } + else if((ws_offset>=0x4000)&&(ws_offset<0x6000)) + { + wsTCacheUpdate2[(ws_offset-0x4000)>>4]=FALSE; /*invalidate tile*/ + return; + } + } + + void GFX::SetVideo(int number,bool force) + { + if((number!=wsVMode)||(force)) + { + wsVMode=number; + std::memset(wsTCacheUpdate,0,512); + std::memset(wsTCacheUpdate2,0,512); + } + } + + void GFX::MakeTiles() + { + int x,y,b0,b1,b2,b3,b4,b5,b6,b7; + for(x=0;x<256;x++) + for(y=0;y<256;y++) + { + b0=(x&128)>>7;b1=(x&64)>>6;b2=(x&32)>>5;b3=(x&16)>>4;b4=(x&8)>>3;b5=(x&4)>>2;b6=(x&2)>>1;b7=(x&1); + b0|=(y&128)>>6;b1|=(y&64)>>5;b2|=(y&32)>>4;b3|=(y&16)>>3;b4|=(y&8)>>2;b5|=(y&4)>>1;b6|=(y&2);b7|=(y&1)<<1; + tiles[x][y][0][0]=b0; + tiles[x][y][0][1]=b1; + tiles[x][y][0][2]=b2; + tiles[x][y][0][3]=b3; + tiles[x][y][0][4]=b4; + tiles[x][y][0][5]=b5; + tiles[x][y][0][6]=b6; + tiles[x][y][0][7]=b7; + tiles[x][y][1][0]=b7; + tiles[x][y][1][1]=b6; + tiles[x][y][1][2]=b5; + tiles[x][y][1][3]=b4; + tiles[x][y][1][4]=b3; + tiles[x][y][1][5]=b2; + tiles[x][y][1][6]=b1; + tiles[x][y][1][7]=b0; + } + } + + void GFX::GetTile(uint32 number,uint32 line,int flipv,int fliph,int bank) + { + uint32 t_adr,t_index,i; + uint8 byte0,byte1,byte2,byte3; + const uint8 *ram = sys->memory.wsRAM; + + if((!bank)||(!(wsVMode &0x07))) + { + if(!wsTCacheUpdate[number]) + { + wsTCacheUpdate[number]=true; + switch(wsVMode) + { + case 7: + t_adr=0x4000+(number<<5); + t_index=number<<6; + for(i=0;i<8;i++) + { + byte0=ram[t_adr++]; + byte1=ram[t_adr++]; + byte2=ram[t_adr++]; + byte3=ram[t_adr++]; + wsTCache[t_index]=byte0>>4; + wsTCacheFlipped[t_index++]=byte3&15; + wsTCache[t_index]=byte0&15; + wsTCacheFlipped[t_index++]=byte3>>4; + wsTCache[t_index]=byte1>>4; + wsTCacheFlipped[t_index++]=byte2&15; + wsTCache[t_index]=byte1&15; + wsTCacheFlipped[t_index++]=byte2>>4; + wsTCache[t_index]=byte2>>4; + wsTCacheFlipped[t_index++]=byte1&15; + wsTCache[t_index]=byte2&15; + wsTCacheFlipped[t_index++]=byte1>>4; + wsTCache[t_index]=byte3>>4; + wsTCacheFlipped[t_index++]=byte0&15; + wsTCache[t_index]=byte3&15; + wsTCacheFlipped[t_index++]=byte0>>4; + } + break; + + case 6: + t_adr=0x4000+(number<<5); + t_index=number<<6; + for(i=0;i<8;i++) + { + byte0=ram[t_adr++]; + byte1=ram[t_adr++]; + byte2=ram[t_adr++]; + byte3=ram[t_adr++]; + wsTCache[t_index]=((byte0>>7)&1)|(((byte1>>7)&1)<<1)|(((byte2>>7)&1)<<2)|(((byte3>>7)&1)<<3); + wsTCacheFlipped[t_index++]=((byte0)&1)|(((byte1)&1)<<1)|(((byte2)&1)<<2)|(((byte3)&1)<<3); + wsTCache[t_index]=((byte0>>6)&1)|(((byte1>>6)&1)<<1)|(((byte2>>6)&1)<<2)|(((byte3>>6)&1)<<3); + wsTCacheFlipped[t_index++]=((byte0>>1)&1)|(((byte1>>1)&1)<<1)|(((byte2>>1)&1)<<2)|(((byte3>>1)&1)<<3); + wsTCache[t_index]=((byte0>>5)&1)|(((byte1>>5)&1)<<1)|(((byte2>>5)&1)<<2)|(((byte3>>5)&1)<<3); + wsTCacheFlipped[t_index++]=((byte0>>2)&1)|(((byte1>>2)&1)<<1)|(((byte2>>2)&1)<<2)|(((byte3>>2)&1)<<3); + wsTCache[t_index]=((byte0>>4)&1)|(((byte1>>4)&1)<<1)|(((byte2>>4)&1)<<2)|(((byte3>>4)&1)<<3); + wsTCacheFlipped[t_index++]=((byte0>>3)&1)|(((byte1>>3)&1)<<1)|(((byte2>>3)&1)<<2)|(((byte3>>3)&1)<<3); + wsTCache[t_index]=((byte0>>3)&1)|(((byte1>>3)&1)<<1)|(((byte2>>3)&1)<<2)|(((byte3>>3)&1)<<3); + wsTCacheFlipped[t_index++]=((byte0>>4)&1)|(((byte1>>4)&1)<<1)|(((byte2>>4)&1)<<2)|(((byte3>>4)&1)<<3); + wsTCache[t_index]=((byte0>>2)&1)|(((byte1>>2)&1)<<1)|(((byte2>>2)&1)<<2)|(((byte3>>2)&1)<<3); + wsTCacheFlipped[t_index++]=((byte0>>5)&1)|(((byte1>>5)&1)<<1)|(((byte2>>5)&1)<<2)|(((byte3>>5)&1)<<3); + wsTCache[t_index]=((byte0>>1)&1)|(((byte1>>1)&1)<<1)|(((byte2>>1)&1)<<2)|(((byte3>>1)&1)<<3); + wsTCacheFlipped[t_index++]=((byte0>>6)&1)|(((byte1>>6)&1)<<1)|(((byte2>>6)&1)<<2)|(((byte3>>6)&1)<<3); + wsTCache[t_index]=((byte0)&1)|(((byte1)&1)<<1)|(((byte2)&1)<<2)|(((byte3)&1)<<3); + wsTCacheFlipped[t_index++]=((byte0>>7)&1)|(((byte1>>7)&1)<<1)|(((byte2>>7)&1)<<2)|(((byte3>>7)&1)<<3); + } + break; + + default: + t_adr=0x2000+(number<<4); + t_index=number<<6; + for(i=0;i<8;i++) + { + byte0=ram[t_adr++]; + byte1=ram[t_adr++]; + wsTCache[t_index]=tiles[byte0][byte1][0][0]; + wsTCacheFlipped[t_index++]=tiles[byte0][byte1][1][0]; + wsTCache[t_index]=tiles[byte0][byte1][0][1]; + wsTCacheFlipped[t_index++]=tiles[byte0][byte1][1][1]; + wsTCache[t_index]=tiles[byte0][byte1][0][2]; + wsTCacheFlipped[t_index++]=tiles[byte0][byte1][1][2]; + wsTCache[t_index]=tiles[byte0][byte1][0][3]; + wsTCacheFlipped[t_index++]=tiles[byte0][byte1][1][3]; + wsTCache[t_index]=tiles[byte0][byte1][0][4]; + wsTCacheFlipped[t_index++]=tiles[byte0][byte1][1][4]; + wsTCache[t_index]=tiles[byte0][byte1][0][5]; + wsTCacheFlipped[t_index++]=tiles[byte0][byte1][1][5]; + wsTCache[t_index]=tiles[byte0][byte1][0][6]; + wsTCacheFlipped[t_index++]=tiles[byte0][byte1][1][6]; + wsTCache[t_index]=tiles[byte0][byte1][0][7]; + wsTCacheFlipped[t_index++]=tiles[byte0][byte1][1][7]; + } + } + } + if(flipv) + line=7-line; + if(fliph) + memcpy(&wsTileRow[0],&wsTCacheFlipped[(number<<6)|(line<<3)],8); + else + memcpy(&wsTileRow[0],&wsTCache[(number<<6)|(line<<3)],8); + } + else + { + + + if(!wsTCacheUpdate2[number]) + { + wsTCacheUpdate2[number]=TRUE; + switch(wsVMode) + { + case 7: + t_adr=0x8000+(number<<5); + t_index=number<<6; + for(i=0;i<8;i++) + { + byte0=ram[t_adr++]; + byte1=ram[t_adr++]; + byte2=ram[t_adr++]; + byte3=ram[t_adr++]; + wsTCache2[t_index]=byte0>>4; + wsTCacheFlipped2[t_index++]=byte3&15; + wsTCache2[t_index]=byte0&15; + wsTCacheFlipped2[t_index++]=byte3>>4; + wsTCache2[t_index]=byte1>>4; + wsTCacheFlipped2[t_index++]=byte2&15; + wsTCache2[t_index]=byte1&15; + wsTCacheFlipped2[t_index++]=byte2>>4; + wsTCache2[t_index]=byte2>>4; + wsTCacheFlipped2[t_index++]=byte1&15; + wsTCache2[t_index]=byte2&15; + wsTCacheFlipped2[t_index++]=byte1>>4; + wsTCache2[t_index]=byte3>>4; + wsTCacheFlipped2[t_index++]=byte0&15; + wsTCache2[t_index]=byte3&15; + wsTCacheFlipped2[t_index++]=byte0>>4; + } + break; + case 6: + t_adr=0x8000+(number<<5); + t_index=number<<6; + for(i=0;i<8;i++) + { + byte0=ram[t_adr++]; + byte1=ram[t_adr++]; + byte2=ram[t_adr++]; + byte3=ram[t_adr++]; + wsTCache2[t_index]=((byte0>>7)&1)|(((byte1>>7)&1)<<1)|(((byte2>>7)&1)<<2)|(((byte3>>7)&1)<<3); + wsTCacheFlipped2[t_index++]=((byte0)&1)|(((byte1)&1)<<1)|(((byte2)&1)<<2)|(((byte3)&1)<<3); + wsTCache2[t_index]=((byte0>>6)&1)|(((byte1>>6)&1)<<1)|(((byte2>>6)&1)<<2)|(((byte3>>6)&1)<<3); + wsTCacheFlipped2[t_index++]=((byte0>>1)&1)|(((byte1>>1)&1)<<1)|(((byte2>>1)&1)<<2)|(((byte3>>1)&1)<<3); + wsTCache2[t_index]=((byte0>>5)&1)|(((byte1>>5)&1)<<1)|(((byte2>>5)&1)<<2)|(((byte3>>5)&1)<<3); + wsTCacheFlipped2[t_index++]=((byte0>>2)&1)|(((byte1>>2)&1)<<1)|(((byte2>>2)&1)<<2)|(((byte3>>2)&1)<<3); + wsTCache2[t_index]=((byte0>>4)&1)|(((byte1>>4)&1)<<1)|(((byte2>>4)&1)<<2)|(((byte3>>4)&1)<<3); + wsTCacheFlipped2[t_index++]=((byte0>>3)&1)|(((byte1>>3)&1)<<1)|(((byte2>>3)&1)<<2)|(((byte3>>3)&1)<<3); + wsTCache2[t_index]=((byte0>>3)&1)|(((byte1>>3)&1)<<1)|(((byte2>>3)&1)<<2)|(((byte3>>3)&1)<<3); + wsTCacheFlipped2[t_index++]=((byte0>>4)&1)|(((byte1>>4)&1)<<1)|(((byte2>>4)&1)<<2)|(((byte3>>4)&1)<<3); + wsTCache2[t_index]=((byte0>>2)&1)|(((byte1>>2)&1)<<1)|(((byte2>>2)&1)<<2)|(((byte3>>2)&1)<<3); + wsTCacheFlipped2[t_index++]=((byte0>>5)&1)|(((byte1>>5)&1)<<1)|(((byte2>>5)&1)<<2)|(((byte3>>5)&1)<<3); + wsTCache2[t_index]=((byte0>>1)&1)|(((byte1>>1)&1)<<1)|(((byte2>>1)&1)<<2)|(((byte3>>1)&1)<<3); + wsTCacheFlipped2[t_index++]=((byte0>>6)&1)|(((byte1>>6)&1)<<1)|(((byte2>>6)&1)<<2)|(((byte3>>6)&1)<<3); + wsTCache2[t_index]=((byte0)&1)|(((byte1)&1)<<1)|(((byte2)&1)<<2)|(((byte3)&1)<<3); + wsTCacheFlipped2[t_index++]=((byte0>>7)&1)|(((byte1>>7)&1)<<1)|(((byte2>>7)&1)<<2)|(((byte3>>7)&1)<<3); + } + break; + default: + t_adr=0x4000+(number<<4); + t_index=number<<6; + for(i=0;i<8;i++) + { + byte0=ram[t_adr++]; + byte1=ram[t_adr++]; + wsTCache2[t_index]=tiles[byte0][byte1][0][0]; + wsTCacheFlipped2[t_index++]=tiles[byte0][byte1][1][0]; + wsTCache2[t_index]=tiles[byte0][byte1][0][1]; + wsTCacheFlipped2[t_index++]=tiles[byte0][byte1][1][1]; + wsTCache2[t_index]=tiles[byte0][byte1][0][2]; + wsTCacheFlipped2[t_index++]=tiles[byte0][byte1][1][2]; + wsTCache2[t_index]=tiles[byte0][byte1][0][3]; + wsTCacheFlipped2[t_index++]=tiles[byte0][byte1][1][3]; + wsTCache2[t_index]=tiles[byte0][byte1][0][4]; + wsTCacheFlipped2[t_index++]=tiles[byte0][byte1][1][4]; + wsTCache2[t_index]=tiles[byte0][byte1][0][5]; + wsTCacheFlipped2[t_index++]=tiles[byte0][byte1][1][5]; + wsTCache2[t_index]=tiles[byte0][byte1][0][6]; + wsTCacheFlipped2[t_index++]=tiles[byte0][byte1][1][6]; + wsTCache2[t_index]=tiles[byte0][byte1][0][7]; + wsTCacheFlipped2[t_index++]=tiles[byte0][byte1][1][7]; + } + } + } + if(flipv) + line=7-line; + if(fliph) + memcpy(&wsTileRow[0],&wsTCacheFlipped2[(number<<6)|(line<<3)],8); + else + memcpy(&wsTileRow[0],&wsTCache2[(number<<6)|(line<<3)],8); + } + } + +} diff --git a/wonderswan/v30mz-ea.inc b/wonderswan/v30mz-ea.inc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/wonderswan/v30mz-modrm.inc b/wonderswan/v30mz-modrm.inc new file mode 100644 index 0000000000..e0496c4441 --- /dev/null +++ b/wonderswan/v30mz-modrm.inc @@ -0,0 +1,94 @@ + +#define RegWord(ModRM) I.regs.w[Mod_RM.reg.w[ModRM]] +#define RegByte(ModRM) I.regs.b[Mod_RM.reg.b[ModRM]] + +#define GetRMWord(ModRM) \ + ((ModRM) >= 0xc0 ? I.regs.w[Mod_RM.RM.w[ModRM]] : ( (this->*GetEA[ModRM])(), ReadWord( EA ) )) + +#define PutbackRMWord(ModRM,val) \ +{ \ + if (ModRM >= 0xc0) I.regs.w[Mod_RM.RM.w[ModRM]]=val; \ + else WriteWord(EA,val); \ +} + +#define GetnextRMWord ReadWord((EA&0xf0000)|((EA+2)&0xffff)) + +#define PutRMWord(ModRM,val) \ +{ \ + if (ModRM >= 0xc0) \ + I.regs.w[Mod_RM.RM.w[ModRM]]=val; \ + else { \ + (this->*GetEA[ModRM])(); \ + WriteWord( EA ,val); \ + } \ +} + +#define PutImmRMWord(ModRM) \ +{ \ + uint16 val; \ + if (ModRM >= 0xc0) \ + FETCHuint16(I.regs.w[Mod_RM.RM.w[ModRM]]) \ + else { \ + (this->*GetEA[ModRM])(); \ + FETCHuint16(val) \ + WriteWord( EA , val); \ + } \ +} + +#define GetRMByte(ModRM) \ + ((ModRM) >= 0xc0 ? I.regs.b[Mod_RM.RM.b[ModRM]] : ReadByte( (this->*GetEA[ModRM])() )) + +#define PutRMByte(ModRM,val) \ +{ \ + if (ModRM >= 0xc0) \ + I.regs.b[Mod_RM.RM.b[ModRM]]=val; \ + else \ + WriteByte( (this->*GetEA[ModRM])() ,val); \ +} + +#define PutImmRMByte(ModRM) \ +{ \ + if (ModRM >= 0xc0) \ + I.regs.b[Mod_RM.RM.b[ModRM]]=FETCH; \ + else { \ + (this->*GetEA[ModRM])(); \ + WriteByte( EA , FETCH ); \ + } \ +} + +#define PutbackRMByte(ModRM,val) \ +{ \ + if (ModRM >= 0xc0) \ + I.regs.b[Mod_RM.RM.b[ModRM]]=val; \ + else \ + WriteByte(EA,val); \ +} + +#define DEF_br8 \ + uint32 ModRM = FETCH,src,dst; \ + src = RegByte(ModRM); \ + dst = GetRMByte(ModRM) + +#define DEF_wr16 \ + uint32 ModRM = FETCH,src,dst; \ + src = RegWord(ModRM); \ + dst = GetRMWord(ModRM) + +#define DEF_r8b \ + uint32 ModRM = FETCH,src,dst; \ + dst = RegByte(ModRM); \ + src = GetRMByte(ModRM) + +#define DEF_r16w \ + uint32 ModRM = FETCH,src,dst; \ + dst = RegWord(ModRM); \ + src = GetRMWord(ModRM) + +#define DEF_ald8 \ + uint32 src = FETCH; \ + uint32 dst = I.regs.b[AL] + +#define DEF_axd16 \ + uint32 src = FETCH; \ + uint32 dst = I.regs.w[AW]; \ + src += (FETCH << 8) diff --git a/wonderswan/v30mz-private.h b/wonderswan/v30mz-private.h new file mode 100644 index 0000000000..261b7b0c98 --- /dev/null +++ b/wonderswan/v30mz-private.h @@ -0,0 +1,252 @@ +#define cpu_readop sys->memory.Read20 +//cpu_readmem20 +#define cpu_readop_arg sys->memory.Read20 +//cpu_readmem20 +#define cpu_readmem20 sys->memory.Read20 +#define cpu_writemem20 sys->memory.Write20 +#define cpu_readport sys->memory.readport +#define cpu_writeport sys->memory.writeport + +#define NEC_NMI_INT_VECTOR 2 + +/* parameter x = result, y = source 1, z = source 2 */ + +#define SetTF(x) (I.TF = (x)) +#define SetIF(x) (I.IF = (x)) +#define SetDF(x) (I.DF = (x)) + +#define SetCFB(x) (I.CarryVal = (x) & 0x100) +#define SetCFW(x) (I.CarryVal = (x) & 0x10000) + +#define SetAF(x,y,z) (I.AuxVal = ((x) ^ ((y) ^ (z))) & 0x10) + + + + +#define SetSF(x) (I.SignVal = (x)) +#define SetZF(x) (I.ZeroVal = (x)) +#define SetPF(x) (I.ParityVal = (x)) + +#define SetSZPF_Byte(x) (I.SignVal=I.ZeroVal=I.ParityVal=(int8)(x)) +#define SetSZPF_Word(x) (I.SignVal=I.ZeroVal=I.ParityVal=(int16)(x)) + +#define SetOFW_Add(x,y,z) (I.OverVal = ((x) ^ (y)) & ((x) ^ (z)) & 0x8000) +#define SetOFB_Add(x,y,z) (I.OverVal = ((x) ^ (y)) & ((x) ^ (z)) & 0x80) +#define SetOFW_Sub(x,y,z) (I.OverVal = ((z) ^ (y)) & ((z) ^ (x)) & 0x8000) +#define SetOFB_Sub(x,y,z) (I.OverVal = ((z) ^ (y)) & ((z) ^ (x)) & 0x80) + +#define ADDB { uint32 res=dst+src; SetCFB(res); SetOFB_Add(res,src,dst); SetAF(res,src,dst); SetSZPF_Byte(res); dst=(uint8)res; } +#define ADDW { uint32 res=dst+src; SetCFW(res); SetOFW_Add(res,src,dst); SetAF(res,src,dst); SetSZPF_Word(res); dst=(uint16)res; } + +#define SUBB { uint32 res=dst-src; SetCFB(res); SetOFB_Sub(res,src,dst); SetAF(res,src,dst); SetSZPF_Byte(res); dst=(uint8)res; } +#define SUBW { uint32 res=dst-src; SetCFW(res); SetOFW_Sub(res,src,dst); SetAF(res,src,dst); SetSZPF_Word(res); dst=(uint16)res; } + +#define ORB dst|=src; I.CarryVal=I.OverVal=I.AuxVal=0; SetSZPF_Byte(dst) +#define ORW dst|=src; I.CarryVal=I.OverVal=I.AuxVal=0; SetSZPF_Word(dst) + +#define ANDB dst&=src; I.CarryVal=I.OverVal=I.AuxVal=0; SetSZPF_Byte(dst) +#define ANDW dst&=src; I.CarryVal=I.OverVal=I.AuxVal=0; SetSZPF_Word(dst) + +#define XORB dst^=src; I.CarryVal=I.OverVal=I.AuxVal=0; SetSZPF_Byte(dst) +#define XORW dst^=src; I.CarryVal=I.OverVal=I.AuxVal=0; SetSZPF_Word(dst) + +#define CF (I.CarryVal!=0) +#define SF (I.SignVal<0) +#define ZF (I.ZeroVal==0) +#define PF parity_table[(uint8)I.ParityVal] +#define AF (I.AuxVal!=0) +#define FLAG_O (I.OverVal!=0) + +/************************************************************************/ + +#define SegBase(Seg) (I.sregs[Seg] << 4) + +#define DefaultBase(Seg) ((seg_prefix && (Seg==DS0 || Seg==SS)) ? prefix_base : I.sregs[Seg] << 4) + +#define GetMemB(Seg,Off) ((uint8)cpu_readmem20((DefaultBase(Seg)+(Off)))) +#define GetMemW(Seg,Off) ((uint16) cpu_readmem20((DefaultBase(Seg)+(Off))) + (cpu_readmem20((DefaultBase(Seg)+((Off)+1)))<<8) ) + +#define PutMemB(Seg,Off,x) { cpu_writemem20((DefaultBase(Seg)+(Off)),(x)); } +#define PutMemW(Seg,Off,x) { PutMemB(Seg,Off,(x)&0xff); PutMemB(Seg,(Off)+1,(uint8)((x)>>8)); } + +/* Todo: Remove these later - plus readword could overflow */ +#define ReadByte(ea) ((uint8)cpu_readmem20((ea))) +#define ReadWord(ea) (cpu_readmem20((ea))+(cpu_readmem20(((ea)+1))<<8)) +#define WriteByte(ea,val) { cpu_writemem20((ea),val); } +#define WriteWord(ea,val) { cpu_writemem20((ea),(uint8)(val)); cpu_writemem20(((ea)+1),(val)>>8); } + +#define read_port(port) cpu_readport(port) +#define write_port(port,val) cpu_writeport(port,val) + +#define FETCH (cpu_readop_arg((I.sregs[PS]<<4)+I.pc++)) +#define FETCHOP (cpu_readop((I.sregs[PS]<<4)+I.pc++)) +#define FETCHuint16(var) { var=cpu_readop_arg((((I.sregs[PS]<<4)+I.pc)))+(cpu_readop_arg((((I.sregs[PS]<<4)+I.pc+1)))<<8); I.pc+=2; } +#define PUSH(val) { I.regs.w[SP]-=2; WriteWord((((I.sregs[SS]<<4)+I.regs.w[SP])),val); } +#define POP(var) { var = ReadWord((((I.sregs[SS]<<4)+I.regs.w[SP]))); I.regs.w[SP]+=2; } +#define PEEK(addr) ((uint8)cpu_readop_arg(addr)) +#define PEEKOP(addr) ((uint8)cpu_readop(addr)) + +#define GetModRM uint32 ModRM=cpu_readop_arg((I.sregs[PS]<<4)+I.pc++) + +/* Cycle count macros: + CLK - cycle count is the same on all processors + CLKM - cycle count for reg/mem instructions + + + Prefetch & buswait time is not emulated. + Extra cycles for PUSH'ing or POP'ing registers to odd addresses is not emulated. +*/ + +#define _REAL_CLK(cycles) { ICount -= cycles; timestamp += cycles; } +#define CLK _REAL_CLK +//#define CLK(cycles) { _REAL_CLK(cycles); if(ws_CheckDMA(cycles)) _REAL_CLK(1); } + +#define CLKM(mcount, ccount) { if(ModRM >=0xc0 ) { CLK(ccount);} else {CLK(mcount);} } + + +#define CompressFlags() (uint16)(CF | (PF << 2) | (AF << 4) | (ZF << 6) \ + | (SF << 7) | (I.TF << 8) | (I.IF << 9) \ + | (I.DF << 10) | (FLAG_O << 11) | (0xF002)) + + +#define ExpandFlags(f) \ +{ \ + I.CarryVal = (f) & 1; \ + I.ParityVal = !((f) & 4); \ + I.AuxVal = (f) & 16; \ + I.ZeroVal = !((f) & 64); \ + I.SignVal = (f) & 128 ? -1 : 0; \ + I.TF = ((f) & 256) == 256; \ + I.IF = ((f) & 512) == 512; \ + I.DF = ((f) & 1024) == 1024; \ + I.OverVal = (f) & 2048; \ +} + + + +#define IncWordReg(Reg) \ + unsigned tmp = (unsigned)I.regs.w[Reg]; \ + unsigned tmp1 = tmp+1; \ + I.OverVal = (tmp == 0x7fff); \ + SetAF(tmp1,tmp,1); \ + SetSZPF_Word(tmp1); \ + I.regs.w[Reg]=tmp1 + + + +#define DecWordReg(Reg) \ + unsigned tmp = (unsigned)I.regs.w[Reg]; \ + unsigned tmp1 = tmp-1; \ + I.OverVal = (tmp == 0x8000); \ + SetAF(tmp1,tmp,1); \ + SetSZPF_Word(tmp1); \ + I.regs.w[Reg]=tmp1 + +#define JMP(flag) \ + int tmp = (int)((int8)FETCH); \ + if (flag) \ + { \ + I.pc = (uint16)(I.pc+tmp); \ + CLK(3); \ + ADDBRANCHTRACE(I.sregs[PS], I.pc); \ + return; \ + } + +#define ADJ4(param1,param2) \ + if (AF || ((I.regs.b[AL] & 0xf) > 9)) \ + { \ + uint16 tmp; \ + tmp = I.regs.b[AL] + param1; \ + I.regs.b[AL] = tmp; \ + I.AuxVal = 1; \ + I.CarryVal |= tmp & 0x100; /*if(tmp&0x100){puts("Meow"); }*//* Correct? */ \ + } \ + if (CF || (I.regs.b[AL] > 0x9f)) \ + { \ + I.regs.b[AL] += param2; \ + I.CarryVal = 1; \ + } \ + SetSZPF_Byte(I.regs.b[AL]) + +#define ADJB(param1,param2) \ + if (AF || ((I.regs.b[AL] & 0xf) > 9)) \ + { \ + I.regs.b[AL] += param1; \ + I.regs.b[AH] += param2; \ + I.AuxVal = 1; \ + I.CarryVal = 1; \ + } \ + else \ + { \ + I.AuxVal = 0; \ + I.CarryVal = 0; \ + } \ + I.regs.b[AL] &= 0x0F + +#define BIT_NOT \ + if (tmp & (1<> 1)+(CF<<7) +#define ROR_uint16 I.CarryVal = dst & 0x1; dst = (dst >> 1)+(CF<<15) +#define ROLC_uint8 dst = (dst << 1) + CF; SetCFB(dst) +#define ROLC_uint16 dst = (dst << 1) + CF; SetCFW(dst) +#define RORC_uint8 dst = (CF<<8)+dst; I.CarryVal = dst & 0x01; dst >>= 1 +#define RORC_uint16 dst = (CF<<16)+dst; I.CarryVal = dst & 0x01; dst >>= 1 +#define SHL_uint8(c) dst <<= c; SetCFB(dst); SetSZPF_Byte(dst); PutbackRMByte(ModRM,(uint8)dst) +#define SHL_uint16(c) dst <<= c; SetCFW(dst); SetSZPF_Word(dst); PutbackRMWord(ModRM,(uint16)dst) +#define SHR_uint8(c) dst >>= c-1; I.CarryVal = dst & 0x1; dst >>= 1; SetSZPF_Byte(dst); PutbackRMByte(ModRM,(uint8)dst) +#define SHR_uint16(c) dst >>= c-1; I.CarryVal = dst & 0x1; dst >>= 1; SetSZPF_Word(dst); PutbackRMWord(ModRM,(uint16)dst) +#define SHRA_uint8(c) dst = ((int8)dst) >> (c-1); I.CarryVal = dst & 0x1; dst = ((int8)((uint8)dst)) >> 1; SetSZPF_Byte(dst); PutbackRMByte(ModRM,(uint8)dst) +#define SHRA_uint16(c) dst = ((int16)dst) >> (c-1); I.CarryVal = dst & 0x1; dst = ((int16)((uint16)dst)) >> 1; SetSZPF_Word(dst); PutbackRMWord(ModRM,(uint16)dst) + +#define DIVUB \ + uresult = I.regs.w[AW]; \ + uresult2 = uresult % tmp; \ + if ((uresult /= tmp) > 0xff) { \ + nec_interrupt(0); break; \ + } else { \ + I.regs.b[AL] = uresult; \ + I.regs.b[AH] = uresult2; \ + } + +#define DIVB \ + result = (int16)I.regs.w[AW]; \ + result2 = result % (int16)((int8)tmp); \ + if ((result /= (int16)((int8)tmp)) > 0xff) { \ + nec_interrupt(0); break; \ + } else { \ + I.regs.b[AL] = result; \ + I.regs.b[AH] = result2; \ + } + +#define DIVUW \ + uresult = (((uint32)I.regs.w[DW]) << 16) | I.regs.w[AW];\ + uresult2 = uresult % tmp; \ + if ((uresult /= tmp) > 0xffff) { \ + nec_interrupt(0); break; \ + } else { \ + I.regs.w[AW]=uresult; \ + I.regs.w[DW]=uresult2; \ + } + +#define DIVW \ + result = ((uint32)I.regs.w[DW] << 16) + I.regs.w[AW]; \ + result2 = result % (int32)((int16)tmp); \ + if ((result /= (int32)((int16)tmp)) > 0xffff) { \ + nec_interrupt(0); break; \ + } else { \ + I.regs.w[AW]=result; \ + I.regs.w[DW]=result2; \ + } + diff --git a/wonderswan/v30mz.cpp b/wonderswan/v30mz.cpp new file mode 100644 index 0000000000..490d308527 --- /dev/null +++ b/wonderswan/v30mz.cpp @@ -0,0 +1,1035 @@ +/**************************************************************************** + +NEC V30MZ emulator + +Stripped out non-V30MZ clock counts and code. + +Small changes made by dox@space.pl (Corrected bug in NEG instruction , different AUX flag handling in some opcodes) + +(Re)Written June-September 2000 by Bryan McPhail (mish@tendril.co.uk) based +on code by Oliver Bergmann (Raul_Bloodworth@hotmail.com) who based code +on the i286 emulator by Fabrice Frances which had initial work based on +David Hedley's pcemu(!). + +****************************************************************************/ + +/* This NEC V30MZ emulator may be used for purposes both commercial and noncommercial if you give the author, Bryan McPhail, +a small credit somewhere(such as in the documentation for an executable package). +*/ + +/* +TODO: +Implement bus lock fully(prevent interrupts from occuring during a REP sequence, I think...), taking into account +HLT emulation to prevent deadlocks! + +Implement better prefix emulation. It's extremely kludgey right now. + +Implement prefetch/pipeline emulation. +*/ + +#include "system.h" +#include +#include "v30mz-private.h" + +using namespace MDFN_IEN_WSWAN::V30MZEnum; + +namespace MDFN_IEN_WSWAN +{ +#define ADDBRANCHTRACE(x,y) { } +#define ADDBRANCHTRACE_INT(x,y) { } +#define SETOLDCSIP() { } + +#include "v30mz-modrm.inc" + + unsigned V30MZ::EA_000() { EO=I.regs.w[BW]+I.regs.w[IX]; EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_001() { EO=I.regs.w[BW]+I.regs.w[IY]; EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_002() { EO=I.regs.w[BP]+I.regs.w[IX]; EA=DefaultBase(SS)+EO; return EA; } + unsigned V30MZ::EA_003() { EO=I.regs.w[BP]+I.regs.w[IY]; EA=DefaultBase(SS)+EO; return EA; } + unsigned V30MZ::EA_004() { EO=I.regs.w[IX]; EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_005() { EO=I.regs.w[IY]; EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_006() { EO=FETCH; EO+=FETCH<<8; EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_007() { EO=I.regs.w[BW]; EA=DefaultBase(DS0)+EO; return EA; } + + unsigned V30MZ::EA_100() { EO=(I.regs.w[BW]+I.regs.w[IX]+(int8)FETCH); EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_101() { EO=(I.regs.w[BW]+I.regs.w[IY]+(int8)FETCH); EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_102() { EO=(I.regs.w[BP]+I.regs.w[IX]+(int8)FETCH); EA=DefaultBase(SS)+EO; return EA; } + unsigned V30MZ::EA_103() { EO=(I.regs.w[BP]+I.regs.w[IY]+(int8)FETCH); EA=DefaultBase(SS)+EO; return EA; } + unsigned V30MZ::EA_104() { EO=(I.regs.w[IX]+(int8)FETCH); EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_105() { EO=(I.regs.w[IY]+(int8)FETCH); EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_106() { EO=(I.regs.w[BP]+(int8)FETCH); EA=DefaultBase(SS)+EO; return EA; } + unsigned V30MZ::EA_107() { EO=(I.regs.w[BW]+(int8)FETCH); EA=DefaultBase(DS0)+EO; return EA; } + + unsigned V30MZ::EA_200() { E16=FETCH; E16+=FETCH<<8; EO=I.regs.w[BW]+I.regs.w[IX]+(int16)E16; EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_201() { E16=FETCH; E16+=FETCH<<8; EO=I.regs.w[BW]+I.regs.w[IY]+(int16)E16; EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_202() { E16=FETCH; E16+=FETCH<<8; EO=I.regs.w[BP]+I.regs.w[IX]+(int16)E16; EA=DefaultBase(SS)+EO; return EA; } + unsigned V30MZ::EA_203() { E16=FETCH; E16+=FETCH<<8; EO=I.regs.w[BP]+I.regs.w[IY]+(int16)E16; EA=DefaultBase(SS)+EO; return EA; } + unsigned V30MZ::EA_204() { E16=FETCH; E16+=FETCH<<8; EO=I.regs.w[IX]+(int16)E16; EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_205() { E16=FETCH; E16+=FETCH<<8; EO=I.regs.w[IY]+(int16)E16; EA=DefaultBase(DS0)+EO; return EA; } + unsigned V30MZ::EA_206() { E16=FETCH; E16+=FETCH<<8; EO=I.regs.w[BP]+(int16)E16; EA=DefaultBase(SS)+EO; return EA; } + unsigned V30MZ::EA_207() { E16=FETCH; E16+=FETCH<<8; EO=I.regs.w[BW]+(int16)E16; EA=DefaultBase(DS0)+EO; return EA; } + + void V30MZ::SetupEA() + { + for (int i = 0; i < 64; i += 8) + { + GetEA[i + 0] = &V30MZ::EA_000; + GetEA[i + 1] = &V30MZ::EA_001; + GetEA[i + 2] = &V30MZ::EA_002; + GetEA[i + 3] = &V30MZ::EA_003; + GetEA[i + 4] = &V30MZ::EA_004; + GetEA[i + 5] = &V30MZ::EA_005; + GetEA[i + 6] = &V30MZ::EA_006; + GetEA[i + 7] = &V30MZ::EA_007; + } + for (int i = 64; i < 128; i += 8) + { + GetEA[i + 0] = &V30MZ::EA_100; + GetEA[i + 1] = &V30MZ::EA_101; + GetEA[i + 2] = &V30MZ::EA_102; + GetEA[i + 3] = &V30MZ::EA_103; + GetEA[i + 4] = &V30MZ::EA_104; + GetEA[i + 5] = &V30MZ::EA_105; + GetEA[i + 6] = &V30MZ::EA_106; + GetEA[i + 7] = &V30MZ::EA_107; + } + for (int i = 128; i < 192; i += 8) + { + GetEA[i + 0] = &V30MZ::EA_200; + GetEA[i + 1] = &V30MZ::EA_201; + GetEA[i + 2] = &V30MZ::EA_202; + GetEA[i + 3] = &V30MZ::EA_203; + GetEA[i + 4] = &V30MZ::EA_204; + GetEA[i + 5] = &V30MZ::EA_205; + GetEA[i + 6] = &V30MZ::EA_206; + GetEA[i + 7] = &V30MZ::EA_207; + } + } + + INLINE void V30MZ::i_real_pushf() + { + PUSH( CompressFlags() ); + CLK(2); + } + + INLINE void V30MZ::i_real_popf() + { + uint32 tmp; + POP(tmp); + ExpandFlags(tmp); + CLK(3); + } + + /***************************************************************************/ + + V30MZ::V30MZ() + { + SetupEA(); + } + + void V30MZ::init() + { + + } + + void V30MZ::reset() + { + const BREGS reg_name[8] = { AL, CL, DL, BL, AH, CH, DH, BH }; + + ICount = 0; + timestamp = 0; + + std::memset(&I, 0, sizeof(I)); + + I.sregs[PS] = 0xffff; + + + for(unsigned int i = 0; i < 256; i++) + { + unsigned int c = 0; + + for (unsigned int j = i; j > 0; j >>= 1) + if (j & 1) c++; + + parity_table[i] = !(c & 1); + } + + I.ZeroVal = I.ParityVal = 1; + + for(unsigned int i = 0; i < 256; i++) + { + Mod_RM.reg.b[i] = reg_name[(i & 0x38) >> 3]; + Mod_RM.reg.w[i] = (WREGS) ( (i & 0x38) >> 3) ; + } + + for(unsigned int i = 0xc0; i < 0x100; i++) + { + Mod_RM.RM.w[i] = (WREGS)( i & 7 ); + Mod_RM.RM.b[i] = (BREGS)reg_name[i & 7]; + } + + prefix_base = 0; + seg_prefix = 0; + InHLT = 0; + } + + void V30MZ::interrupt(uint32 vector, bool IgnoreIF) + { + InHLT = FALSE; // This is correct! Standby mode is always exited when there is an INT signal, regardless of whether interrupt are disabled. + if(I.IF || IgnoreIF) + { + uint32 dest_seg, dest_off; + + PUSH( CompressFlags() ); + I.TF = I.IF = 0; + dest_off = ReadWord(vector); + dest_seg = ReadWord(vector+2); + PUSH(I.sregs[PS]); + PUSH(I.pc); + I.pc = (uint16)dest_off; + I.sregs[PS] = (uint16)dest_seg; + ADDBRANCHTRACE_INT(I.sregs[PS], I.pc); + CLK(32); + } + } + + void V30MZ::nec_interrupt(unsigned int_num) + { + uint32 dest_seg, dest_off; + + if (int_num == -1) + return; + + i_real_pushf(); + I.TF = I.IF = 0; + + + dest_off = ReadWord((int_num)*4); + dest_seg = ReadWord((int_num)*4+2); + + PUSH(I.sregs[PS]); + PUSH(I.pc); + I.pc = (uint16)dest_off; + I.sregs[PS] = (uint16)dest_seg; + ADDBRANCHTRACE(I.sregs[PS], I.pc); + } + + bool V30MZ::CheckInHLT() + { + if(InHLT) + { + sys->interrupt.Check(); + if(InHLT) + { + int32 tmp = ICount; + + if(tmp > 0) + CLK(tmp); + return(1); + } + } + return(0); + } + + + /****************************************************************************/ + /* OPCODES */ + /****************************************************************************/ + + INLINE void V30MZ::i_real_insb() + { + PutMemB(DS1,I.regs.w[IY], read_port(I.regs.w[DW])); + I.regs.w[IY]+= -2 * I.DF + 1; + CLK(6); + } + + INLINE void V30MZ::i_real_insw() + { + PutMemB(DS1,I.regs.w[IY],read_port(I.regs.w[DW])); + PutMemB(DS1,(I.regs.w[IY]+1)&0xffff,read_port((I.regs.w[DW]+1)&0xffff)); + I.regs.w[IY]+= -4 * I.DF + 2; + CLK(6); + } + + INLINE void V30MZ::i_real_outsb() + { + write_port(I.regs.w[DW],GetMemB(DS0,I.regs.w[IX])); + I.regs.w[IX]+= -2 * I.DF + 1; + CLK(7); + } + + INLINE void V30MZ::i_real_outsw() + { + write_port(I.regs.w[DW],GetMemB(DS0,I.regs.w[IX])); + write_port((I.regs.w[DW]+1)&0xffff,GetMemB(DS0,(I.regs.w[IX]+1)&0xffff)); + I.regs.w[IX]+= -4 * I.DF + 2; + CLK(7); + } + + INLINE void V30MZ::i_real_movsb() + { + uint32 tmp = GetMemB(DS0,I.regs.w[IX]); + PutMemB(DS1,I.regs.w[IY], tmp); + I.regs.w[IY] += -2 * I.DF + 1; + I.regs.w[IX] += -2 * I.DF + 1; + CLK(5); + } + + INLINE void V30MZ::i_real_movsw() + { + uint32 tmp = GetMemW(DS0,I.regs.w[IX]); PutMemW(DS1,I.regs.w[IY], tmp); I.regs.w[IY] += -4 * I.DF + 2; + I.regs.w[IX] += -4 * I.DF + 2; CLK(5); + } + + INLINE void V30MZ::i_real_cmpsb() + { + uint32 src = GetMemB(DS1, I.regs.w[IY]); uint32 dst = GetMemB(DS0, I.regs.w[IX]); SUBB; I.regs.w[IY] += -2 * I.DF + 1; + I.regs.w[IX] += -2 * I.DF + 1; CLK(6); + } + + INLINE void V30MZ::i_real_cmpsw() + { + uint32 src = GetMemW(DS1, I.regs.w[IY]); uint32 dst = GetMemW(DS0, I.regs.w[IX]); SUBW; I.regs.w[IY] += -4 * I.DF + 2; + I.regs.w[IX] += -4 * I.DF + 2; CLK(6); + } + + INLINE void V30MZ::i_real_stosb() + { + PutMemB(DS1,I.regs.w[IY],I.regs.b[AL]); I.regs.w[IY] += -2 * I.DF + 1; CLK(3); + } + + INLINE void V30MZ::i_real_stosw() + { + PutMemW(DS1,I.regs.w[IY],I.regs.w[AW]); I.regs.w[IY] += -4 * I.DF + 2; CLK(3); + } + + INLINE void V30MZ::i_real_lodsb() + { + I.regs.b[AL] = GetMemB(DS0,I.regs.w[IX]); I.regs.w[IX] += -2 * I.DF + 1; CLK(3); + } + + INLINE void V30MZ::i_real_lodsw() + { + I.regs.w[AW] = GetMemW(DS0,I.regs.w[IX]); I.regs.w[IX] += -4 * I.DF + 2; CLK(3); + } + + INLINE void V30MZ::i_real_scasb() + { + uint32 src = GetMemB(DS1, I.regs.w[IY]); uint32 dst = I.regs.b[AL]; SUBB; + I.regs.w[IY] += -2 * I.DF + 1; CLK(4); + } + + INLINE void V30MZ::i_real_scasw() + { + uint32 src = GetMemW(DS1, I.regs.w[IY]); uint32 dst = I.regs.w[AW]; SUBW; + I.regs.w[IY] += -4 * I.DF + 2; CLK(4); + } + + void V30MZ::DoOP(uint8 opcode) + { + //#define OP(num,func_name) static void func_name() +#define OP(num, func_name) case num: + //#define OP_RANGE(num1, num2, func_name) case num1 ... num2: + +#define OP_EPILOGUE break + + switch(opcode) + { + default: + Debug::printf("Invalid op: %02x\n", opcode); + CLK(10); + break; + + OP( 0x00, i_add_br8 ) { DEF_br8; ADDB; PutbackRMByte(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x01, i_add_wr16 ) { DEF_wr16; ADDW; PutbackRMWord(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x02, i_add_r8b ) { DEF_r8b; ADDB; RegByte(ModRM)=dst; CLKM(2,1); } OP_EPILOGUE; + OP( 0x03, i_add_r16w ) { DEF_r16w; ADDW; RegWord(ModRM)=dst; CLKM(2,1); } OP_EPILOGUE; + OP( 0x04, i_add_ald8 ) { DEF_ald8; ADDB; I.regs.b[AL]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x05, i_add_axd16) { DEF_axd16; ADDW; I.regs.w[AW]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x06, i_push_ds1 ) { PUSH(I.sregs[DS1]); CLK(2); } OP_EPILOGUE; + OP( 0x07, i_pop_ds1 ) { POP(I.sregs[DS1]); CLK(3); } OP_EPILOGUE; + + OP( 0x08, i_or_br8 ) { DEF_br8; ORB; PutbackRMByte(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x09, i_or_wr16 ) { DEF_wr16; ORW; PutbackRMWord(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x0a, i_or_r8b ) { DEF_r8b; ORB; RegByte(ModRM)=dst; CLKM(2,1); } OP_EPILOGUE; + OP( 0x0b, i_or_r16w ) { DEF_r16w; ORW; RegWord(ModRM)=dst; CLKM(2,1); } OP_EPILOGUE; + OP( 0x0c, i_or_ald8 ) { DEF_ald8; ORB; I.regs.b[AL]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x0d, i_or_axd16 ) { DEF_axd16; ORW; I.regs.w[AW]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x0e, i_push_cs ) { PUSH(I.sregs[PS]); CLK(2); } OP_EPILOGUE; + + OP( 0x10, i_adc_br8 ) { DEF_br8; src+=CF; ADDB; PutbackRMByte(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x11, i_adc_wr16 ) { DEF_wr16; src+=CF; ADDW; PutbackRMWord(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x12, i_adc_r8b ) { DEF_r8b; src+=CF; ADDB; RegByte(ModRM)=dst; CLKM(2, 1); } OP_EPILOGUE; + OP( 0x13, i_adc_r16w ) { DEF_r16w; src+=CF; ADDW; RegWord(ModRM)=dst; CLKM(2, 1); } OP_EPILOGUE; + OP( 0x14, i_adc_ald8 ) { DEF_ald8; src+=CF; ADDB; I.regs.b[AL]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x15, i_adc_axd16) { DEF_axd16; src+=CF; ADDW; I.regs.w[AW]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x16, i_push_ss ) { PUSH(I.sregs[SS]); CLK(2); } OP_EPILOGUE; + OP( 0x17, i_pop_ss ) { POP(I.sregs[SS]); CLK(3); } OP_EPILOGUE; + + OP( 0x18, i_sbb_br8 ) { DEF_br8; src+=CF; SUBB; PutbackRMByte(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x19, i_sbb_wr16 ) { DEF_wr16; src+=CF; SUBW; PutbackRMWord(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x1a, i_sbb_r8b ) { DEF_r8b; src+=CF; SUBB; RegByte(ModRM)=dst; CLKM(2,1); } OP_EPILOGUE; + OP( 0x1b, i_sbb_r16w ) { DEF_r16w; src+=CF; SUBW; RegWord(ModRM)=dst; CLKM(2,1); } OP_EPILOGUE; + OP( 0x1c, i_sbb_ald8 ) { DEF_ald8; src+=CF; SUBB; I.regs.b[AL]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x1d, i_sbb_axd16) { DEF_axd16; src+=CF; SUBW; I.regs.w[AW]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x1e, i_push_ds ) { PUSH(I.sregs[DS0]); CLK(2); } OP_EPILOGUE; + OP( 0x1f, i_pop_ds ) { POP(I.sregs[DS0]); CLK(3); } OP_EPILOGUE; + + OP( 0x20, i_and_br8 ) { DEF_br8; ANDB; PutbackRMByte(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x21, i_and_wr16 ) { DEF_wr16; ANDW; PutbackRMWord(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x22, i_and_r8b ) { DEF_r8b; ANDB; RegByte(ModRM)=dst; CLKM(2,1); } OP_EPILOGUE; + OP( 0x23, i_and_r16w ) { DEF_r16w; ANDW; RegWord(ModRM)=dst; CLKM(2,1); } OP_EPILOGUE; + OP( 0x24, i_and_ald8 ) { DEF_ald8; ANDB; I.regs.b[AL]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x25, i_and_axd16) { DEF_axd16; ANDW; I.regs.w[AW]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x26, i_ds1 ) { seg_prefix=TRUE; prefix_base=I.sregs[DS1]<<4; CLK(1); DoOP(FETCHOP); seg_prefix=FALSE; } OP_EPILOGUE; + OP( 0x27, i_daa ) { ADJ4(6,0x60); CLK(10); } OP_EPILOGUE; + + OP( 0x28, i_sub_br8 ) { DEF_br8; SUBB; PutbackRMByte(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x29, i_sub_wr16 ) { DEF_wr16; SUBW; PutbackRMWord(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x2a, i_sub_r8b ) { DEF_r8b; SUBB; RegByte(ModRM)=dst; CLKM(2,1); } OP_EPILOGUE; + OP( 0x2b, i_sub_r16w ) { DEF_r16w; SUBW; RegWord(ModRM)=dst; CLKM(2,1); } OP_EPILOGUE; + OP( 0x2c, i_sub_ald8 ) { DEF_ald8; SUBB; I.regs.b[AL]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x2d, i_sub_axd16) { DEF_axd16; SUBW; I.regs.w[AW]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x2e, i_ps ) { seg_prefix=TRUE; prefix_base=I.sregs[PS]<<4; CLK(1); DoOP(FETCHOP); seg_prefix=FALSE; } OP_EPILOGUE; + OP( 0x2f, i_das ) { ADJ4(-6,-0x60); CLK(10); } OP_EPILOGUE; + + OP( 0x30, i_xor_br8 ) { DEF_br8; XORB; PutbackRMByte(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x31, i_xor_wr16 ) { DEF_wr16; XORW; PutbackRMWord(ModRM,dst); CLKM(3,1); } OP_EPILOGUE; + OP( 0x32, i_xor_r8b ) { DEF_r8b; XORB; RegByte(ModRM)=dst; CLKM(2,1); } OP_EPILOGUE; + OP( 0x33, i_xor_r16w ) { DEF_r16w; XORW; RegWord(ModRM)=dst; CLKM(2,1); } OP_EPILOGUE; + OP( 0x34, i_xor_ald8 ) { DEF_ald8; XORB; I.regs.b[AL]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x35, i_xor_axd16) { DEF_axd16; XORW; I.regs.w[AW]=dst; CLK(1); } OP_EPILOGUE; + OP( 0x36, i_ss ) { seg_prefix=TRUE; prefix_base=I.sregs[SS]<<4; CLK(1); DoOP(FETCHOP); seg_prefix=FALSE; } OP_EPILOGUE; + OP( 0x37, i_aaa ) { ADJB(6,1); CLK(9); } OP_EPILOGUE; + + OP( 0x38, i_cmp_br8 ) { DEF_br8; SUBB; CLKM(2,1); } OP_EPILOGUE; + OP( 0x39, i_cmp_wr16 ) { DEF_wr16; SUBW; CLKM(2,1); } OP_EPILOGUE; + OP( 0x3a, i_cmp_r8b ) { DEF_r8b; SUBB; CLKM(2,1); } OP_EPILOGUE; + OP( 0x3b, i_cmp_r16w ) { DEF_r16w; SUBW; CLKM(2,1); } OP_EPILOGUE; + OP( 0x3c, i_cmp_ald8 ) { DEF_ald8; SUBB; CLK(1); } OP_EPILOGUE; + OP( 0x3d, i_cmp_axd16) { DEF_axd16; SUBW; CLK(1); } OP_EPILOGUE; + OP( 0x3e, i_ds0 ) { seg_prefix=TRUE; prefix_base=I.sregs[DS0]<<4; CLK(1); DoOP(FETCHOP); seg_prefix=FALSE; } OP_EPILOGUE; + OP( 0x3f, i_aas ) { ADJB(-6,-1); CLK(9); } OP_EPILOGUE; + + OP( 0x40, i_inc_ax ) { IncWordReg(AW); CLK(1); } OP_EPILOGUE; + OP( 0x41, i_inc_cx ) { IncWordReg(CW); CLK(1); } OP_EPILOGUE; + OP( 0x42, i_inc_dx ) { IncWordReg(DW); CLK(1); } OP_EPILOGUE; + OP( 0x43, i_inc_bx ) { IncWordReg(BW); CLK(1); } OP_EPILOGUE; + OP( 0x44, i_inc_sp ) { IncWordReg(SP); CLK(1); } OP_EPILOGUE; + OP( 0x45, i_inc_bp ) { IncWordReg(BP); CLK(1); } OP_EPILOGUE; + OP( 0x46, i_inc_si ) { IncWordReg(IX); CLK(1); } OP_EPILOGUE; + OP( 0x47, i_inc_di ) { IncWordReg(IY); CLK(1); } OP_EPILOGUE; + + OP( 0x48, i_dec_ax ) { DecWordReg(AW); CLK(1); } OP_EPILOGUE; + OP( 0x49, i_dec_cx ) { DecWordReg(CW); CLK(1); } OP_EPILOGUE; + OP( 0x4a, i_dec_dx ) { DecWordReg(DW); CLK(1); } OP_EPILOGUE; + OP( 0x4b, i_dec_bx ) { DecWordReg(BW); CLK(1); } OP_EPILOGUE; + OP( 0x4c, i_dec_sp ) { DecWordReg(SP); CLK(1); } OP_EPILOGUE; + OP( 0x4d, i_dec_bp ) { DecWordReg(BP); CLK(1); } OP_EPILOGUE; + OP( 0x4e, i_dec_si ) { DecWordReg(IX); CLK(1); } OP_EPILOGUE; + OP( 0x4f, i_dec_di ) { DecWordReg(IY); CLK(1); } OP_EPILOGUE; + + OP( 0x50, i_push_ax ) { PUSH(I.regs.w[AW]); CLK(1); } OP_EPILOGUE; + OP( 0x51, i_push_cx ) { PUSH(I.regs.w[CW]); CLK(1); } OP_EPILOGUE; + OP( 0x52, i_push_dx ) { PUSH(I.regs.w[DW]); CLK(1); } OP_EPILOGUE; + OP( 0x53, i_push_bx ) { PUSH(I.regs.w[BW]); CLK(1); } OP_EPILOGUE; + OP( 0x54, i_push_sp ) { PUSH(I.regs.w[SP]); CLK(1); } OP_EPILOGUE; + OP( 0x55, i_push_bp ) { PUSH(I.regs.w[BP]); CLK(1); } OP_EPILOGUE; + OP( 0x56, i_push_si ) { PUSH(I.regs.w[IX]); CLK(1); } OP_EPILOGUE; + OP( 0x57, i_push_di ) { PUSH(I.regs.w[IY]); CLK(1); } OP_EPILOGUE; + + OP( 0x58, i_pop_ax ) { POP(I.regs.w[AW]); CLK(1); } OP_EPILOGUE; + OP( 0x59, i_pop_cx ) { POP(I.regs.w[CW]); CLK(1); } OP_EPILOGUE; + OP( 0x5a, i_pop_dx ) { POP(I.regs.w[DW]); CLK(1); } OP_EPILOGUE; + OP( 0x5b, i_pop_bx ) { POP(I.regs.w[BW]); CLK(1); } OP_EPILOGUE; + OP( 0x5c, i_pop_sp ) { POP(I.regs.w[SP]); CLK(1); } OP_EPILOGUE; + OP( 0x5d, i_pop_bp ) { POP(I.regs.w[BP]); CLK(1); } OP_EPILOGUE; + OP( 0x5e, i_pop_si ) { POP(I.regs.w[IX]); CLK(1); } OP_EPILOGUE; + OP( 0x5f, i_pop_di ) { POP(I.regs.w[IY]); CLK(1); } OP_EPILOGUE; + + OP( 0x60, i_pusha ) { + unsigned tmp=I.regs.w[SP]; + PUSH(I.regs.w[AW]); + PUSH(I.regs.w[CW]); + PUSH(I.regs.w[DW]); + PUSH(I.regs.w[BW]); + PUSH(tmp); + PUSH(I.regs.w[BP]); + PUSH(I.regs.w[IX]); + PUSH(I.regs.w[IY]); + CLK(9); + } OP_EPILOGUE; + + OP( 0x61, i_popa ) { + unsigned tmp; + POP(I.regs.w[IY]); + POP(I.regs.w[IX]); + POP(I.regs.w[BP]); + POP(tmp); + POP(I.regs.w[BW]); + POP(I.regs.w[DW]); + POP(I.regs.w[CW]); + POP(I.regs.w[AW]); + CLK(8); + } OP_EPILOGUE; + + OP( 0x62, i_chkind ) + { + uint32 low,high,tmp; + + GetModRM; + + low = GetRMWord(ModRM); + high = GetnextRMWord; + tmp = RegWord(ModRM); + + CLK(13); + if (tmphigh) + { + nec_interrupt(5); + } + } OP_EPILOGUE; + + OP( 0x68, i_push_d16 ) { uint32 tmp; FETCHuint16(tmp); PUSH(tmp); CLK(1); } OP_EPILOGUE; + OP( 0x69, i_imul_d16 ) { uint32 tmp; DEF_r16w; FETCHuint16(tmp); dst = (int32)((int16)src)*(int32)((int16)tmp); I.CarryVal = I.OverVal = (((int32)dst) >> 15 != 0) && (((int32)dst) >> 15 != -1); RegWord(ModRM)=(uint16)dst; CLKM(4,3);} OP_EPILOGUE; + OP( 0x6a, i_push_d8 ) { uint32 tmp = (uint16)((int16)((int8)FETCH)); PUSH(tmp); CLK(1); } OP_EPILOGUE; + OP( 0x6b, i_imul_d8 ) { uint32 src2; DEF_r16w; src2= (uint16)((int16)((int8)FETCH)); dst = (int32)((int16)src)*(int32)((int16)src2); I.CarryVal = I.OverVal = (((int32)dst) >> 15 != 0) && (((int32)dst) >> 15 != -1); RegWord(ModRM)=(uint16)dst; CLKM(4,3); } OP_EPILOGUE; + OP( 0x6c, i_insb ) { i_real_insb(); } OP_EPILOGUE; + OP( 0x6d, i_insw ) { i_real_insw(); } OP_EPILOGUE; + OP( 0x6e, i_outsb ) { i_real_outsb(); } OP_EPILOGUE; + OP( 0x6f, i_outsw ) { i_real_outsw(); } OP_EPILOGUE; + + OP( 0x70, i_jo ) { JMP( FLAG_O); CLK(1); } OP_EPILOGUE; + OP( 0x71, i_jno ) { JMP(!FLAG_O); CLK(1); } OP_EPILOGUE; + OP( 0x72, i_jc ) { JMP( CF); CLK(1); } OP_EPILOGUE; + OP( 0x73, i_jnc ) { JMP(!CF); CLK(1); } OP_EPILOGUE; + OP( 0x74, i_jz ) { JMP( ZF); CLK(1); } OP_EPILOGUE; + OP( 0x75, i_jnz ) { JMP(!ZF); CLK(1); } OP_EPILOGUE; + OP( 0x76, i_jce ) { JMP(CF || ZF); CLK(1); } OP_EPILOGUE; + OP( 0x77, i_jnce ) { JMP(!(CF || ZF)); CLK(1); } OP_EPILOGUE; + OP( 0x78, i_js ) { JMP( SF); CLK(1); } OP_EPILOGUE; + OP( 0x79, i_jns ) { JMP(!SF); CLK(1); } OP_EPILOGUE; + OP( 0x7a, i_jp ) { JMP( PF); CLK(1); } OP_EPILOGUE; + OP( 0x7b, i_jnp ) { JMP(!PF); CLK(1); } OP_EPILOGUE; + OP( 0x7c, i_jl ) { JMP((SF!=FLAG_O)&&(!ZF)); CLK(1); } OP_EPILOGUE; + OP( 0x7d, i_jnl ) { JMP((ZF)||(SF==FLAG_O)); CLK(1); } OP_EPILOGUE; + OP( 0x7e, i_jle ) { JMP((ZF)||(SF!=FLAG_O)); CLK(1); } OP_EPILOGUE; + OP( 0x7f, i_jnle ) { JMP((SF==FLAG_O)&&(!ZF)); CLK(1); } OP_EPILOGUE; + + OP( 0x80, i_80pre ) { uint32 dst, src; GetModRM; dst = GetRMByte(ModRM); src = FETCH; + CLKM(3, 1); + switch (ModRM & 0x38) { + case 0x00: ADDB; PutbackRMByte(ModRM,dst); break; + case 0x08: ORB; PutbackRMByte(ModRM,dst); break; + case 0x10: src+=CF; ADDB; PutbackRMByte(ModRM,dst); break; + case 0x18: src+=CF; SUBB; PutbackRMByte(ModRM,dst); break; + case 0x20: ANDB; PutbackRMByte(ModRM,dst); break; + case 0x28: SUBB; PutbackRMByte(ModRM,dst); break; + case 0x30: XORB; PutbackRMByte(ModRM,dst); break; + case 0x38: SUBB; break; /* CMP */ + } + } OP_EPILOGUE; + + OP( 0x81, i_81pre ) { uint32 dst, src; GetModRM; dst = GetRMWord(ModRM); src = FETCH; src+= (FETCH << 8); + CLKM(3, 1); + switch (ModRM & 0x38) { + case 0x00: ADDW; PutbackRMWord(ModRM,dst); break; + case 0x08: ORW; PutbackRMWord(ModRM,dst); break; + case 0x10: src+=CF; ADDW; PutbackRMWord(ModRM,dst); break; + case 0x18: src+=CF; SUBW; PutbackRMWord(ModRM,dst); break; + case 0x20: ANDW; PutbackRMWord(ModRM,dst); break; + case 0x28: SUBW; PutbackRMWord(ModRM,dst); break; + case 0x30: XORW; PutbackRMWord(ModRM,dst); break; + case 0x38: SUBW; break; /* CMP */ + } + } OP_EPILOGUE; + + OP( 0x82, i_82pre ) { uint32 dst, src; GetModRM; dst = GetRMByte(ModRM); src = (uint8)((int8)FETCH); + CLKM(3,1); + switch (ModRM & 0x38) { + case 0x00: ADDB; PutbackRMByte(ModRM,dst); break; + case 0x08: ORB; PutbackRMByte(ModRM,dst); break; + case 0x10: src+=CF; ADDB; PutbackRMByte(ModRM,dst); break; + case 0x18: src+=CF; SUBB; PutbackRMByte(ModRM,dst); break; + case 0x20: ANDB; PutbackRMByte(ModRM,dst); break; + case 0x28: SUBB; PutbackRMByte(ModRM,dst); break; + case 0x30: XORB; PutbackRMByte(ModRM,dst); break; + case 0x38: SUBB; break; /* CMP */ + } + } OP_EPILOGUE; + + OP( 0x83, i_83pre ) { uint32 dst, src; GetModRM; dst = GetRMWord(ModRM); src = (uint16)((int16)((int8)FETCH)); + CLKM(3,1); + switch (ModRM & 0x38) { + case 0x00: ADDW; PutbackRMWord(ModRM,dst); break; + case 0x08: ORW; PutbackRMWord(ModRM,dst); break; + case 0x10: src+=CF; ADDW; PutbackRMWord(ModRM,dst); break; + case 0x18: src+=CF; SUBW; PutbackRMWord(ModRM,dst); break; + case 0x20: ANDW; PutbackRMWord(ModRM,dst); break; + case 0x28: SUBW; PutbackRMWord(ModRM,dst); break; + case 0x30: XORW; PutbackRMWord(ModRM,dst); break; + case 0x38: SUBW; break; /* CMP */ + } + } OP_EPILOGUE; + + OP( 0x84, i_test_br8 ) { DEF_br8; ANDB; CLKM(2,1); } OP_EPILOGUE; + OP( 0x85, i_test_wr16 ) { DEF_wr16; ANDW; CLKM(2,1); } OP_EPILOGUE; + OP( 0x86, i_xchg_br8 ) { DEF_br8; RegByte(ModRM)=dst; PutbackRMByte(ModRM,src); CLKM(5,3); } OP_EPILOGUE; + OP( 0x87, i_xchg_wr16 ) { DEF_wr16; RegWord(ModRM)=dst; PutbackRMWord(ModRM,src); CLKM(5,3); } OP_EPILOGUE; + + OP( 0x88, i_mov_br8 ) { uint8 src; GetModRM; src = RegByte(ModRM); PutRMByte(ModRM,src); CLK(1); } OP_EPILOGUE; + OP( 0x89, i_mov_wr16 ) { uint16 src; GetModRM; src = RegWord(ModRM); PutRMWord(ModRM,src); CLK(1); } OP_EPILOGUE; + OP( 0x8a, i_mov_r8b ) { uint8 src; GetModRM; src = GetRMByte(ModRM); RegByte(ModRM)=src; CLK(1); } OP_EPILOGUE; + OP( 0x8b, i_mov_r16w ) { uint16 src; GetModRM; src = GetRMWord(ModRM); RegWord(ModRM)=src; CLK(1); } OP_EPILOGUE; + OP( 0x8c, i_mov_wsreg ) { GetModRM; PutRMWord(ModRM,I.sregs[(ModRM & 0x38) >> 3]); CLK(1); } OP_EPILOGUE; + OP( 0x8d, i_lea ) { uint16 ModRM = FETCH; if(ModRM >= 192) { Debug::printf("LEA Error: %02x\n", ModRM);} else { (void)(this->*GetEA[ModRM])(); } RegWord(ModRM)=EO; CLK(1); } OP_EPILOGUE; + OP( 0x8e, i_mov_sregw ) { uint16 src; GetModRM; src = GetRMWord(ModRM); CLKM(3,2); + switch (ModRM & 0x38) { + case 0x00: I.sregs[DS1] = src; break; /* mov ds1,ew */ + case 0x08: I.sregs[PS] = src; break; /* mov cs,ew */ + case 0x10: I.sregs[SS] = src; break; /* mov ss,ew */ + case 0x18: I.sregs[DS0] = src; break; /* mov ds0,ew */ + } + } OP_EPILOGUE; + + OP( 0x8f, i_popw ) { uint16 tmp; GetModRM; POP(tmp); PutRMWord(ModRM,tmp); CLKM(3,1); } OP_EPILOGUE; + OP( 0x90, i_nop ) { CLK(3); } OP_EPILOGUE; + + OP( 0x91, i_xchg_axcx ) { XchgAWReg(CW); CLK(3); } OP_EPILOGUE; + OP( 0x92, i_xchg_axdx ) { XchgAWReg(DW); CLK(3); } OP_EPILOGUE; + OP( 0x93, i_xchg_axbx ) { XchgAWReg(BW); CLK(3); } OP_EPILOGUE; + OP( 0x94, i_xchg_axsp ) { XchgAWReg(SP); CLK(3); } OP_EPILOGUE; + OP( 0x95, i_xchg_axbp ) { XchgAWReg(BP); CLK(3); } OP_EPILOGUE; + OP( 0x96, i_xchg_axsi ) { XchgAWReg(IX); CLK(3); } OP_EPILOGUE; + OP( 0x97, i_xchg_axdi ) { XchgAWReg(IY); CLK(3); } OP_EPILOGUE; + + // AKA CVTBW + OP( 0x98, i_cbw ) { I.regs.b[AH] = (I.regs.b[AL] & 0x80) ? 0xff : 0; CLK(1); } OP_EPILOGUE; + + // AKA CVTWL + OP( 0x99, i_cwd ) { I.regs.w[DW] = (I.regs.b[AH] & 0x80) ? 0xffff : 0; CLK(1); } OP_EPILOGUE; + + OP( 0x9a, i_call_far ) { uint32 tmp, tmp2; FETCHuint16(tmp); FETCHuint16(tmp2); PUSH(I.sregs[PS]); PUSH(I.pc); I.pc = (uint16)tmp; I.sregs[PS] = (uint16)tmp2; ADDBRANCHTRACE(I.sregs[PS], I.pc); CLK(10); } OP_EPILOGUE; + OP( 0x9b, i_poll ) { Debug::puts("POLL"); } OP_EPILOGUE; + OP( 0x9c, i_pushf ) { i_real_pushf(); } OP_EPILOGUE; + OP( 0x9d, i_popf ) { i_real_popf(); } OP_EPILOGUE; + OP( 0x9e, i_sahf ) { uint32 tmp = (CompressFlags() & 0xff00) | (I.regs.b[AH] & 0xd5); ExpandFlags(tmp); CLK(4); } OP_EPILOGUE; + OP( 0x9f, i_lahf ) { I.regs.b[AH] = CompressFlags() & 0xff; CLK(2); } OP_EPILOGUE; + + OP( 0xa0, i_mov_aldisp ) { uint32 addr; FETCHuint16(addr); I.regs.b[AL] = GetMemB(DS0, addr); CLK(1); } OP_EPILOGUE; + OP( 0xa1, i_mov_axdisp ) { uint32 addr; FETCHuint16(addr); I.regs.b[AL] = GetMemB(DS0, addr); I.regs.b[AH] = GetMemB(DS0, (addr+1)&0xffff); CLK(1); } OP_EPILOGUE; + OP( 0xa2, i_mov_dispal ) { uint32 addr; FETCHuint16(addr); PutMemB(DS0, addr, I.regs.b[AL]); CLK(1); } OP_EPILOGUE; + OP( 0xa3, i_mov_dispax ) { uint32 addr; FETCHuint16(addr); PutMemB(DS0, addr, I.regs.b[AL]); PutMemB(DS0, (addr+1)&0xffff, I.regs.b[AH]); CLK(1); } OP_EPILOGUE; + + OP( 0xa4, i_movsb ) { i_real_movsb(); } OP_EPILOGUE; + OP( 0xa5, i_movsw ) { i_real_movsw(); } OP_EPILOGUE; + OP( 0xa6, i_cmpsb ) { i_real_cmpsb(); } OP_EPILOGUE; + OP( 0xa7, i_cmpsw ) { i_real_cmpsw(); } OP_EPILOGUE; + + OP( 0xa8, i_test_ald8 ) { DEF_ald8; ANDB; CLK(1); } OP_EPILOGUE; + OP( 0xa9, i_test_axd16 ) { DEF_axd16; ANDW; CLK(1); } OP_EPILOGUE; + + OP( 0xaa, i_stosb ) { i_real_stosb(); } OP_EPILOGUE; + OP( 0xab, i_stosw ) { i_real_stosw(); } OP_EPILOGUE; + OP( 0xac, i_lodsb ) { i_real_lodsb(); } OP_EPILOGUE; + OP( 0xad, i_lodsw ) { i_real_lodsw(); } OP_EPILOGUE; + OP( 0xae, i_scasb ) { i_real_scasb(); } OP_EPILOGUE; + OP( 0xaf, i_scasw ) { i_real_scasw(); } OP_EPILOGUE; + + OP( 0xb0, i_mov_ald8 ) { I.regs.b[AL] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xb1, i_mov_cld8 ) { I.regs.b[CL] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xb2, i_mov_dld8 ) { I.regs.b[DL] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xb3, i_mov_bld8 ) { I.regs.b[BL] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xb4, i_mov_ahd8 ) { I.regs.b[AH] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xb5, i_mov_chd8 ) { I.regs.b[CH] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xb6, i_mov_dhd8 ) { I.regs.b[DH] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xb7, i_mov_bhd8 ) { I.regs.b[BH] = FETCH; CLK(1); } OP_EPILOGUE; + + OP( 0xb8, i_mov_axd16 ) { I.regs.b[AL] = FETCH; I.regs.b[AH] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xb9, i_mov_cxd16 ) { I.regs.b[CL] = FETCH; I.regs.b[CH] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xba, i_mov_dxd16 ) { I.regs.b[DL] = FETCH; I.regs.b[DH] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xbb, i_mov_bxd16 ) { I.regs.b[BL] = FETCH; I.regs.b[BH] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xbc, i_mov_spd16 ) { I.regs.b[SPL] = FETCH; I.regs.b[SPH] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xbd, i_mov_bpd16 ) { I.regs.b[BPL] = FETCH; I.regs.b[BPH] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xbe, i_mov_sid16 ) { I.regs.b[IXL] = FETCH; I.regs.b[IXH] = FETCH; CLK(1); } OP_EPILOGUE; + OP( 0xbf, i_mov_did16 ) { I.regs.b[IYL] = FETCH; I.regs.b[IYH] = FETCH; CLK(1); } OP_EPILOGUE; + + OP( 0xc0, i_rotshft_bd8 ) { + uint32 src, dst; uint8 c; + GetModRM; src = (unsigned)GetRMByte(ModRM); dst=src; + c=FETCH; + c&=0x1f; + CLKM(5,3); + if (c) switch (ModRM & 0x38) { + case 0x00: do { ROL_uint8; c--; } while (c>0); PutbackRMByte(ModRM,(uint8)dst); break; + case 0x08: do { ROR_uint8; c--; } while (c>0); PutbackRMByte(ModRM,(uint8)dst); break; + case 0x10: do { ROLC_uint8; c--; } while (c>0); PutbackRMByte(ModRM,(uint8)dst); break; + case 0x18: do { RORC_uint8; c--; } while (c>0); PutbackRMByte(ModRM,(uint8)dst); break; + case 0x20: SHL_uint8(c); I.AuxVal = 1; break;// + case 0x28: SHR_uint8(c); I.AuxVal = 1; break;// + case 0x30: break; + case 0x38: SHRA_uint8(c); break; + } + } OP_EPILOGUE; + + OP( 0xc1, i_rotshft_wd8 ) { + uint32 src, dst; uint8 c; + GetModRM; src = (unsigned)GetRMWord(ModRM); dst=src; + c=FETCH; + c&=0x1f; + CLKM(5,3); + if (c) switch (ModRM & 0x38) { + case 0x00: do { ROL_uint16; c--; } while (c>0); PutbackRMWord(ModRM,(uint16)dst); break; + case 0x08: do { ROR_uint16; c--; } while (c>0); PutbackRMWord(ModRM,(uint16)dst); break; + case 0x10: do { ROLC_uint16; c--; } while (c>0); PutbackRMWord(ModRM,(uint16)dst); break; + case 0x18: do { RORC_uint16; c--; } while (c>0); PutbackRMWord(ModRM,(uint16)dst); break; + case 0x20: SHL_uint16(c); I.AuxVal = 1; break; + case 0x28: SHR_uint16(c); I.AuxVal = 1; break; + case 0x30: break; + case 0x38: SHRA_uint16(c); break; + } + } OP_EPILOGUE; + + OP( 0xc2, i_ret_d16 ) { uint32 count = FETCH; count += FETCH << 8; POP(I.pc); I.regs.w[SP]+=count; CLK(6); ADDBRANCHTRACE(I.sregs[PS], I.pc); } OP_EPILOGUE; + OP( 0xc3, i_ret ) { POP(I.pc); CLK(6); ADDBRANCHTRACE(I.sregs[PS], I.pc); } OP_EPILOGUE; + OP( 0xc4, i_les_dw ) { GetModRM; uint16 tmp = GetRMWord(ModRM); RegWord(ModRM)=tmp; I.sregs[DS1] = GetnextRMWord; CLK(6); } OP_EPILOGUE; + OP( 0xc5, i_lds_dw ) { GetModRM; uint16 tmp = GetRMWord(ModRM); RegWord(ModRM)=tmp; I.sregs[DS0] = GetnextRMWord; CLK(6); } OP_EPILOGUE; + OP( 0xc6, i_mov_bd8 ) { GetModRM; PutImmRMByte(ModRM); CLK(1); } OP_EPILOGUE; + OP( 0xc7, i_mov_wd16 ) { GetModRM; PutImmRMWord(ModRM); CLK(1); } OP_EPILOGUE; + + // NEC calls it "PREPARE" + OP( 0xc8, i_enter ) { + uint32 nb = FETCH; + uint32 i,level; + + CLK(19); + nb += FETCH << 8; + + level = FETCH; + level &= 0x1F; // Only lower 5 bits are valid on V30MZ + + PUSH(I.regs.w[BP]); + I.regs.w[BP]=I.regs.w[SP]; + I.regs.w[SP] -= nb; + for (i=1;i0); PutbackRMByte(ModRM,(uint8)dst); break; + case 0x08: do { ROR_uint8; c--; } while (c>0); PutbackRMByte(ModRM,(uint8)dst); break; + case 0x10: do { ROLC_uint8; c--; } while (c>0); PutbackRMByte(ModRM,(uint8)dst); break; + case 0x18: do { RORC_uint8; c--; } while (c>0); PutbackRMByte(ModRM,(uint8)dst); break; + case 0x20: SHL_uint8(c); I.AuxVal = 1; break; + case 0x28: SHR_uint8(c); I.AuxVal = 1;break; + case 0x30: break; + case 0x38: SHRA_uint8(c); break; + } + } OP_EPILOGUE; + + OP( 0xd3, i_rotshft_wcl ) { + uint32 src, dst; uint8 c; GetModRM; src = (uint32)GetRMWord(ModRM); dst=src; + c=I.regs.b[CL]; + c&=0x1f; + CLKM(5,3); + if (c) switch (ModRM & 0x38) { + case 0x00: do { ROL_uint16; c--; } while (c>0); PutbackRMWord(ModRM,(uint16)dst); break; + case 0x08: do { ROR_uint16; c--; } while (c>0); PutbackRMWord(ModRM,(uint16)dst); break; + case 0x10: do { ROLC_uint16; c--; } while (c>0); PutbackRMWord(ModRM,(uint16)dst); break; + case 0x18: do { RORC_uint16; c--; } while (c>0); PutbackRMWord(ModRM,(uint16)dst); break; + case 0x20: SHL_uint16(c); I.AuxVal = 1; break; + case 0x28: SHR_uint16(c); I.AuxVal = 1; break; + case 0x30: break; + case 0x38: SHRA_uint16(c); break; + } + } OP_EPILOGUE; + + OP( 0xd4, i_aam ) { uint32 mult=FETCH; mult=0; I.regs.b[AH] = I.regs.b[AL] / 10; I.regs.b[AL] %= 10; SetSZPF_Word(I.regs.w[AW]); CLK(17); } OP_EPILOGUE; + OP( 0xd5, i_aad ) { uint32 mult=FETCH; mult=0; I.regs.b[AL] = I.regs.b[AH] * 10 + I.regs.b[AL]; I.regs.b[AH] = 0; SetSZPF_Byte(I.regs.b[AL]); CLK(6); } OP_EPILOGUE; + OP( 0xd6, i_setalc ) { I.regs.b[AL] = (CF)?0xff:0x00; CLK(3); } OP_EPILOGUE; + OP( 0xd7, i_trans ) { uint32 dest = (I.regs.w[BW]+I.regs.b[AL])&0xffff; I.regs.b[AL] = GetMemB(DS0, dest); CLK(5); } OP_EPILOGUE; + + // + OP( 0xd8, i_fpo) + OP( 0xd9, i_fpo) + OP( 0xda, i_fpo) + OP( 0xdb, i_fpo) + OP( 0xdc, i_fpo) + OP( 0xdd, i_fpo) + OP( 0xde, i_fpo) + OP( 0xdf, i_fpo) + /*OP_RANGE(0xd8, 0xdf, i_fpo)*/ { /*printf("FPO1, Op:%02x\n", opcode);*/ GetModRM; CLK(1); } OP_EPILOGUE; + + OP( 0xe0, i_loopne ) { int8 disp = (int8)FETCH; I.regs.w[CW]--; if (!ZF && I.regs.w[CW]) { I.pc = (uint16)(I.pc+disp); CLK(6); ADDBRANCHTRACE(I.sregs[PS], I.pc); } else CLK(3); } OP_EPILOGUE; + OP( 0xe1, i_loope ) { int8 disp = (int8)FETCH; I.regs.w[CW]--; if ( ZF && I.regs.w[CW]) { I.pc = (uint16)(I.pc+disp); CLK(6); ADDBRANCHTRACE(I.sregs[PS], I.pc); } else CLK(3); } OP_EPILOGUE; + OP( 0xe2, i_loop ) { int8 disp = (int8)FETCH; I.regs.w[CW]--; if (I.regs.w[CW]) { I.pc = (uint16)(I.pc+disp); CLK(5); ADDBRANCHTRACE(I.sregs[PS], I.pc); } else CLK(2); } OP_EPILOGUE; + OP( 0xe3, i_jcxz ) { int8 disp = (int8)FETCH; if (I.regs.w[CW] == 0) { I.pc = (uint16)(I.pc+disp); CLK(4); ADDBRANCHTRACE(I.sregs[PS], I.pc); } else CLK(1); } OP_EPILOGUE; + OP( 0xe4, i_inal ) { uint8 port = FETCH; I.regs.b[AL] = read_port(port); CLK(6); } OP_EPILOGUE; + OP( 0xe5, i_inax ) { uint8 port = FETCH; I.regs.b[AL] = read_port(port); I.regs.b[AH] = read_port(port+1); CLK(6); } OP_EPILOGUE; + OP( 0xe6, i_outal ) { uint8 port = FETCH; write_port(port, I.regs.b[AL]); CLK(6); } OP_EPILOGUE; + OP( 0xe7, i_outax ) { uint8 port = FETCH; write_port(port, I.regs.b[AL]); write_port(port+1, I.regs.b[AH]); CLK(6); } OP_EPILOGUE; + + OP( 0xe8, i_call_d16 ) { uint32 tmp; FETCHuint16(tmp); PUSH(I.pc); I.pc = (uint16)(I.pc+(int16)tmp); ADDBRANCHTRACE(I.sregs[PS], I.pc); CLK(5); } OP_EPILOGUE; + OP( 0xe9, i_jmp_d16 ) { uint32 tmp; FETCHuint16(tmp); I.pc = (uint16)(I.pc+(int16)tmp); ADDBRANCHTRACE(I.sregs[PS], I.pc); CLK(4); } OP_EPILOGUE; + OP( 0xea, i_jmp_far ) { uint32 tmp,tmp1; FETCHuint16(tmp); FETCHuint16(tmp1); I.sregs[PS] = (uint16)tmp1; I.pc = (uint16)tmp; ; ADDBRANCHTRACE(I.sregs[PS], I.pc); CLK(7); } OP_EPILOGUE; + OP( 0xeb, i_jmp_d8 ) { int tmp = (int)((int8)FETCH); CLK(4);I.pc = (uint16)(I.pc+tmp); ADDBRANCHTRACE(I.sregs[PS], I.pc); } OP_EPILOGUE; + + OP( 0xec, i_inaldx ) { I.regs.b[AL] = read_port(I.regs.w[DW]); CLK(6);} OP_EPILOGUE; + OP( 0xed, i_inaxdx ) { uint32 port = I.regs.w[DW]; I.regs.b[AL] = read_port(port); I.regs.b[AH] = read_port(port+1); CLK(6); } OP_EPILOGUE; + + OP( 0xee, i_outdxal ) { write_port(I.regs.w[DW], I.regs.b[AL]); CLK(6); } OP_EPILOGUE; + OP( 0xef, i_outdxax ) { uint32 port = I.regs.w[DW]; write_port(port, I.regs.b[AL]); write_port(port+1, I.regs.b[AH]); CLK(6); } OP_EPILOGUE; + + // NEC calls it "BUSLOCK" + OP( 0xf0, i_lock ) { CLK(1); DoOP(FETCHOP); } OP_EPILOGUE; + + // We put CHK_ICOUNT *after* the first iteration has completed, to match real behavior. +#define CHK_ICOUNT(cond) if(ICount < 0 && (cond)) { I.pc -= seg_prefix ? 3 : 2; break; } + + OP( 0xf2, i_repne ) + { + uint32 next = FETCHOP; + + switch(next) { /* Segments */ + case 0x26: seg_prefix=TRUE; prefix_base=I.sregs[DS1]<<4; next = FETCHOP; CLK(2); break; + case 0x2e: seg_prefix=TRUE; prefix_base=I.sregs[PS]<<4; next = FETCHOP; CLK(2); break; + case 0x36: seg_prefix=TRUE; prefix_base=I.sregs[SS]<<4; next = FETCHOP; CLK(2); break; + case 0x3e: seg_prefix=TRUE; prefix_base=I.sregs[DS0]<<4; next = FETCHOP; CLK(2); break; + } + + switch(next) { + case 0x6c: CLK(5); if (I.regs.w[CW]) do { i_real_insb(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0x6d: CLK(5); if (I.regs.w[CW]) do { i_real_insw(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0x6e: CLK(5); if (I.regs.w[CW]) do { i_real_outsb(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0x6f: CLK(5); if (I.regs.w[CW]) do { i_real_outsw(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xa4: CLK(5); if (I.regs.w[CW]) do { i_real_movsb(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xa5: CLK(5); if (I.regs.w[CW]) do { i_real_movsw(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xa6: CLK(5); if (I.regs.w[CW]) do { i_real_cmpsb(); I.regs.w[CW]--; CLK(3); CHK_ICOUNT(I.regs.w[CW] && ZF == 0); /* 6 + 3 = 9 */ } while (I.regs.w[CW]>0 && ZF==0); break; + case 0xa7: CLK(5); if (I.regs.w[CW]) do { i_real_cmpsw(); I.regs.w[CW]--; CLK(3); CHK_ICOUNT(I.regs.w[CW] && ZF == 0); /* 6 + 3 = 9 */ } while (I.regs.w[CW]>0 && ZF==0); break; + case 0xaa: CLK(5); if (I.regs.w[CW]) do { i_real_stosb(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xab: CLK(5); if (I.regs.w[CW]) do { i_real_stosw(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xac: CLK(5); if (I.regs.w[CW]) do { i_real_lodsb(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xad: CLK(5); if (I.regs.w[CW]) do { i_real_lodsw(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xae: CLK(5); if (I.regs.w[CW]) do { i_real_scasb(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW] && ZF == 0); } while (I.regs.w[CW]>0 && ZF==0); break; + case 0xaf: CLK(5); if (I.regs.w[CW]) do { i_real_scasw(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW] && ZF == 0); } while (I.regs.w[CW]>0 && ZF==0); break; + default: DoOP(next); break; + } + seg_prefix=FALSE; + } OP_EPILOGUE; + + OP( 0xf3, i_repe) + { + uint32 next = FETCHOP; + + switch(next) { /* Segments */ + case 0x26: seg_prefix=TRUE; prefix_base=I.sregs[DS1]<<4; next = FETCHOP; CLK(2); break; + case 0x2e: seg_prefix=TRUE; prefix_base=I.sregs[PS]<<4; next = FETCHOP; CLK(2); break; + case 0x36: seg_prefix=TRUE; prefix_base=I.sregs[SS]<<4; next = FETCHOP; CLK(2); break; + case 0x3e: seg_prefix=TRUE; prefix_base=I.sregs[DS0]<<4; next = FETCHOP; CLK(2); break; + } + switch(next) { + case 0x6c: CLK(5); if (I.regs.w[CW]) do { i_real_insb(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0x6d: CLK(5); if (I.regs.w[CW]) do { i_real_insw(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0x6e: CLK(5); if (I.regs.w[CW]) do { i_real_outsb(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0x6f: CLK(5); if (I.regs.w[CW]) do { i_real_outsw(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xa4: CLK(5); if (I.regs.w[CW]) do { i_real_movsb(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xa5: CLK(5); if (I.regs.w[CW]) do { i_real_movsw(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xa6: CLK(5); if (I.regs.w[CW]) do { i_real_cmpsb(); I.regs.w[CW]--; CLK(3); CHK_ICOUNT(I.regs.w[CW] && ZF == 1); /* 6 + 3 = 9 */ } while (I.regs.w[CW]>0 && ZF==1); break; + case 0xa7: CLK(5); if (I.regs.w[CW]) do { i_real_cmpsw(); I.regs.w[CW]--; CLK(3); CHK_ICOUNT(I.regs.w[CW] && ZF == 1);/* 6 + 3 = 9 */ } while (I.regs.w[CW]>0 && ZF==1); break; + case 0xaa: CLK(5); if (I.regs.w[CW]) do { i_real_stosb(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xab: CLK(5); if (I.regs.w[CW]) do { i_real_stosw(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xac: CLK(5); if (I.regs.w[CW]) do { i_real_lodsb(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xad: CLK(5); if (I.regs.w[CW]) do { i_real_lodsw(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW]); } while (I.regs.w[CW]>0); break; + case 0xae: CLK(5); if (I.regs.w[CW]) do { i_real_scasb(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW] && ZF == 1); } while (I.regs.w[CW]>0 && ZF==1); break; + case 0xaf: CLK(5); if (I.regs.w[CW]) do { i_real_scasw(); I.regs.w[CW]--; CHK_ICOUNT(I.regs.w[CW] && ZF == 1); } while (I.regs.w[CW]>0 && ZF==1); break; + default: DoOP(next); break; + } + seg_prefix=FALSE; + } OP_EPILOGUE; + + OP( 0xf4, i_hlt ) { InHLT = TRUE; CheckInHLT(); } OP_EPILOGUE; + + OP( 0xf5, i_cmc ) { I.CarryVal = !CF; CLK(4); } OP_EPILOGUE; + OP( 0xf6, i_f6pre ) { uint32 tmp; uint32 uresult,uresult2; int32 result,result2; + GetModRM; tmp = GetRMByte(ModRM); + switch (ModRM & 0x38) { + case 0x00: tmp &= FETCH; I.CarryVal = I.OverVal = I.AuxVal=0; SetSZPF_Byte(tmp); CLKM(2,1); break; /* TEST */ + case 0x08: break; + case 0x10: PutbackRMByte(ModRM,~tmp); CLKM(3,1); break; /* NOT */ + + case 0x18: I.CarryVal=(tmp!=0);tmp=(~tmp)+1; SetSZPF_Byte(tmp); PutbackRMByte(ModRM,tmp&0xff); CLKM(3,1); break; /* NEG */ + case 0x20: uresult = I.regs.b[AL]*tmp; I.regs.w[AW]=(uint16)uresult; I.CarryVal=I.OverVal=(I.regs.b[AH]!=0); CLKM(4,3); break; /* MULU */ + case 0x28: result = (int16)((int8)I.regs.b[AL])*(int16)((int8)tmp); I.regs.w[AW]=(uint16)result; I.CarryVal=I.OverVal=(I.regs.b[AH]!=0); CLKM(4,3); break; /* MUL */ + case 0x30: if (tmp) { DIVUB; } else nec_interrupt(0); CLKM(16,15); break; + case 0x38: if (tmp) { DIVB; } else nec_interrupt(0); CLKM(18,17); break; + } + } OP_EPILOGUE; + + OP( 0xf7, i_f7pre ) { uint32 tmp,tmp2; uint32 uresult,uresult2; int32 result,result2; + GetModRM; tmp = GetRMWord(ModRM); + switch (ModRM & 0x38) { + case 0x00: FETCHuint16(tmp2); tmp &= tmp2; I.CarryVal = I.OverVal = I.AuxVal=0; SetSZPF_Word(tmp); CLKM(2,1); break; /* TEST */ + case 0x08: break; + case 0x10: PutbackRMWord(ModRM,~tmp); CLKM(3,1); break; /* NOT */ + case 0x18: I.CarryVal=(tmp!=0); tmp=(~tmp)+1; SetSZPF_Word(tmp); PutbackRMWord(ModRM,tmp&0xffff); CLKM(3,1); break; /* NEG */ + case 0x20: uresult = I.regs.w[AW]*tmp; I.regs.w[AW]=uresult&0xffff; I.regs.w[DW]=((uint32)uresult)>>16; I.CarryVal=I.OverVal=(I.regs.w[DW]!=0); CLKM(4,3); break; /* MULU */ + case 0x28: result = (int32)((int16)I.regs.w[AW])*(int32)((int16)tmp); I.regs.w[AW]=result&0xffff; I.regs.w[DW]=result>>16; I.CarryVal=I.OverVal=(I.regs.w[DW]!=0); CLKM(4,3); break; /* MUL */ + case 0x30: if (tmp) { DIVUW; } else nec_interrupt(0); CLKM(24,23); break; + case 0x38: if (tmp) { DIVW; } else nec_interrupt(0); CLKM(25,24); break; + } + } OP_EPILOGUE; + + OP( 0xf8, i_clc ) { I.CarryVal = 0; CLK(4); } OP_EPILOGUE; + OP( 0xf9, i_stc ) { I.CarryVal = 1; CLK(4); } OP_EPILOGUE; + OP( 0xfa, i_di ) { SetIF(0); CLK(4); } OP_EPILOGUE; + OP( 0xfb, i_ei ) { SetIF(1); CLK(4); } OP_EPILOGUE; + OP( 0xfc, i_cld ) { SetDF(0); CLK(4); } OP_EPILOGUE; + OP( 0xfd, i_std ) { SetDF(1); CLK(4); } OP_EPILOGUE; + OP( 0xfe, i_fepre ) { uint32 tmp, tmp1; GetModRM; tmp=GetRMByte(ModRM); + switch(ModRM & 0x38) { + case 0x00: tmp1 = tmp+1; I.OverVal = (tmp==0x7f); SetAF(tmp1,tmp,1); SetSZPF_Byte(tmp1); PutbackRMByte(ModRM,(uint8)tmp1); CLKM(3,1); break; /* INC */ + case 0x08: tmp1 = tmp-1; I.OverVal = (tmp==0x80); SetAF(tmp1,tmp,1); SetSZPF_Byte(tmp1); PutbackRMByte(ModRM,(uint8)tmp1); CLKM(3,1); break; /* DEC */ + } + } OP_EPILOGUE; + + OP( 0xff, i_ffpre ) { uint32 tmp, tmp1; GetModRM; tmp=GetRMWord(ModRM); + switch(ModRM & 0x38) { + case 0x00: tmp1 = tmp+1; I.OverVal = (tmp==0x7fff); SetAF(tmp1,tmp,1); SetSZPF_Word(tmp1); PutbackRMWord(ModRM,(uint16)tmp1); CLKM(3,1); break; /* INC */ + case 0x08: tmp1 = tmp-1; I.OverVal = (tmp==0x8000); SetAF(tmp1,tmp,1); SetSZPF_Word(tmp1); PutbackRMWord(ModRM,(uint16)tmp1); CLKM(3,1); break; /* DEC */ + case 0x10: PUSH(I.pc); I.pc = (uint16)tmp; ADDBRANCHTRACE(I.sregs[PS], I.pc); CLKM(6,5); break; /* CALL */ + case 0x18: tmp1 = I.sregs[PS]; I.sregs[PS] = GetnextRMWord; PUSH(tmp1); PUSH(I.pc); I.pc = tmp; ADDBRANCHTRACE(I.sregs[PS], I.pc); CLKM(12,1); break; /* CALL FAR */ + case 0x20: I.pc = tmp; ADDBRANCHTRACE(I.sregs[PS], I.pc); CLKM(5,4); break; /* JMP */ + case 0x28: I.pc = tmp; I.sregs[PS] = GetnextRMWord; ADDBRANCHTRACE(I.sregs[PS], I.pc); CLKM(10,1); break; /* JMP FAR */ + case 0x30: PUSH(tmp); CLKM(2,1); break; + } + } OP_EPILOGUE; + } // End switch statement + + + } // End func + + + /*****************************************************************************/ + + + unsigned V30MZ::get_reg(int regnum) + { + switch( regnum ) + { + case NEC_PC: return I.pc; + case NEC_SP: return I.regs.w[SP]; + case NEC_FLAGS: return CompressFlags(); + case NEC_AW: return I.regs.w[AW]; + case NEC_CW: return I.regs.w[CW]; + case NEC_DW: return I.regs.w[DW]; + case NEC_BW: return I.regs.w[BW]; + case NEC_BP: return I.regs.w[BP]; + case NEC_IX: return I.regs.w[IX]; + case NEC_IY: return I.regs.w[IY]; + case NEC_DS1: return I.sregs[DS1]; + case NEC_PS: return I.sregs[PS]; + case NEC_SS: return I.sregs[SS]; + case NEC_DS0: return I.sregs[DS0]; + } + return 0; + } + + void nec_set_irq_line(int irqline, int state); + + void V30MZ::set_reg(int regnum, unsigned val) + { + switch( regnum ) + { + case NEC_PC: I.pc = val; break; + case NEC_SP: I.regs.w[SP] = val; break; + case NEC_FLAGS: ExpandFlags(val); break; + case NEC_AW: I.regs.w[AW] = val; break; + case NEC_CW: I.regs.w[CW] = val; break; + case NEC_DW: I.regs.w[DW] = val; break; + case NEC_BW: I.regs.w[BW] = val; break; + case NEC_BP: I.regs.w[BP] = val; break; + case NEC_IX: I.regs.w[IX] = val; break; + case NEC_IY: I.regs.w[IY] = val; break; + case NEC_DS1: I.sregs[DS1] = val; break; + case NEC_PS: I.sregs[PS] = val; break; + case NEC_SS: I.sregs[SS] = val; break; + case NEC_DS0: I.sregs[DS0] = val; break; + } + } + + void V30MZ::execute(int cycles) + { + ICount += cycles; + if(InHLT) + { + SETOLDCSIP(); + sys->interrupt.Check(); + if(InHLT) + { + int32 tmp = ICount; + if(tmp > 0) + CLK(tmp); + return; + } + } + + while(ICount > 0) + { + SETOLDCSIP(); + sys->interrupt.Check(); + DoOP(FETCHOP); + } + + } + +} diff --git a/wonderswan/v30mz.h b/wonderswan/v30mz.h new file mode 100644 index 0000000000..761b28b721 --- /dev/null +++ b/wonderswan/v30mz.h @@ -0,0 +1,166 @@ +#ifndef __V30MZ_H_ +#define __V30MZ_H_ + +#include "system.h" + +namespace MDFN_IEN_WSWAN +{ + +typedef union +{ /* eight general registers */ + uint16 w[8]; /* viewed as 16 bits registers */ + uint8 b[16]; /* or as 8 bit registers */ +} v30mz_basicregs_t; + +typedef struct +{ + v30mz_basicregs_t regs; + uint16 sregs[4]; + + uint16 pc; + + int32 SignVal; + uint32 AuxVal, OverVal, ZeroVal, CarryVal, ParityVal; /* 0 or non-0 valued flags */ + uint8 TF, IF, DF; +} v30mz_regs_t; + +namespace V30MZEnum +{ + +typedef enum { DS1, PS, SS, DS0 } SREGS; +typedef enum { AW, CW, DW, BW, SP, BP, IX, IY } WREGS; + +#ifdef LSB_FIRST +typedef enum { AL,AH,CL,CH,DL,DH,BL,BH,SPL,SPH,BPL,BPH,IXL,IXH,IYL,IYH } BREGS; +#else +typedef enum { AH,AL,CH,CL,DH,DL,BH,BL,SPH,SPL,BPH,BPL,IXH,IXL,IYH,IYL } BREGS; +#endif +} + +class V30MZ +{ +public: + V30MZ(); + + void execute(int cycles); + void set_reg(int, unsigned); + unsigned get_reg(int regnum); + void reset(); + void init(); + + void interrupt(uint32 vector, bool IgnoreIF = FALSE); + +private: + uint16 old_CS, old_IP; + +public: + uint32 timestamp; +private: + int32 ICount; + + v30mz_regs_t I; + bool InHLT; + + uint32 prefix_base; /* base address of the latest prefix segment */ + char seg_prefix; /* prefix segment indicator */ + + uint8 parity_table[256]; + + uint32 EA; + uint16 EO; + uint16 E16; + + struct { + struct { + V30MZEnum::WREGS w[256]; + V30MZEnum::BREGS b[256]; + } reg; + struct { + V30MZEnum::WREGS w[256]; + V30MZEnum::BREGS b[256]; + } RM; + } Mod_RM; + +private: + //void (*cpu_writemem20)(uint32,uint8);// = NULL; + //uint8 (*cpu_readport)(uint32);// = NULL; + //void (*cpu_writeport)(uint32, uint8);// = NULL; + //uint8 (*cpu_readmem20)(uint32);// = NULL; + +private: + void nec_interrupt(unsigned int_num); + bool CheckInHLT(); + void DoOP(uint8 opcode); + + void i_real_pushf(); + void i_real_popf(); + + void i_real_insb(); + void i_real_insw(); + void i_real_outsb(); + void i_real_outsw(); + void i_real_movsb(); + void i_real_movsw(); + void i_real_cmpsb(); + void i_real_cmpsw(); + void i_real_stosb(); + void i_real_stosw(); + void i_real_lodsb(); + void i_real_lodsw(); + void i_real_scasb(); + void i_real_scasw(); + +private: + unsigned EA_000(); + unsigned EA_001(); + unsigned EA_002(); + unsigned EA_003(); + unsigned EA_004(); + unsigned EA_005(); + unsigned EA_006(); + unsigned EA_007(); + + unsigned EA_100(); + unsigned EA_101(); + unsigned EA_102(); + unsigned EA_103(); + unsigned EA_104(); + unsigned EA_105(); + unsigned EA_106(); + unsigned EA_107(); + + unsigned EA_200(); + unsigned EA_201(); + unsigned EA_202(); + unsigned EA_203(); + unsigned EA_204(); + unsigned EA_205(); + unsigned EA_206(); + unsigned EA_207(); + +private: + void SetupEA(); + + typedef unsigned(V30MZ::*EAFPtr)(); + EAFPtr GetEA[192]; + +public: + System *sys; +}; + + +enum { + NEC_PC=1, NEC_AW, NEC_CW, NEC_DW, NEC_BW, NEC_SP, NEC_BP, NEC_IX, NEC_IY, + NEC_FLAGS, NEC_DS1, NEC_PS, NEC_SS, NEC_DS0 +}; + +/* Public variables */ +//extern int v30mz_ICount; +//extern uint32 v30mz_timestamp; + + +/* Public functions */ + +} + +#endif diff --git a/wonderswan/wstech24.txt b/wonderswan/wstech24.txt new file mode 100644 index 0000000000..6c4a5c30f7 --- /dev/null +++ b/wonderswan/wstech24.txt @@ -0,0 +1,757 @@ + ___ ___ _ + / | \ ___ __/ \__ ___ ___ /\__ + \ / \ // __>\_ _// __|/ __\/ \ + \_____/_\__ \ \_/ \___|\___/\_/\_/ + \____/ 2.4 - 26.12.2003 + +1. ABOUT + WStech doc v2.4 made by Judge and Dox + Special thanks to -anonymous- contributor for some usefull info. + + For more info please visit http://www.pocketdomain.net + + Comments/updates/infos please send to dox@space.pl + + What's new in version 2.4: + + - corect info about Sprite Table, BG Map and FG Map locations + (ports $04 and $07 - section 10) + Special thanks to mika-n + +2. CPU + + Bandai SPGY-1001 ASWAN 9850KK003 + NEC V30 MZ - fast version of V30 with internal pipeline (16 bytes prefatch buffer) running at 3.072 MHz. + V30 MZ is aprox 4 times faster than V30. + The V30MZ performs pipeline processing internally, performing instruction fetch (prefetch), instruction decode, and + instruction execution in parallel. For this reason, it is difficult to determine what part of the program is currently being + executed by monitoring the output of the address bus for the instruction code fetch. + If there are conditional branch instructions, even in case branching does not occur, the address of the branch + destination is prefetched (only one time), so that further monitoring of the program is difficult. + The V30MZ has 8 prefetch queues (16 bytes). + + There are a few other differences between V30MZ and V30 cpu (unsupported opcodes , different flag handling after mul/div). + + Timing: + + Hblank : 256 CPU cycles + Vblank : 159 Hblank = 159*256/3072000 = 75.47Hz + + +3. MEMORY + + 20 bit addressing space = 1 Megabyte. Memory is splitted into 64KB blocks (segments/banks). + + Segments: + + 0 - RAM - 16 KB (WS) / 64 KB (WSC) internal RAM (see below) + + 1 - SRAM (cart) SRAM is BSI device BS62LV256TC - 256K(32Kx8) Static RAM - TSOP 0 - 70 c, 70 ns (http://www.bsi.com.tw/product/bs62lv256.pdf) + + 2 - ROM Bank (initial bank = last) + 3 - ROM Bank (lnitial bank = last) + + 4 - ROM Bank (initial bank = last - 11) + 5 - ROM Bank (initial bank = last - 10) + 6 - ROM Bank (initial bank = last - 9) + 7 - ROM Bank (initial bank = last - 8) + 8 - ROM Bank (initial bank = last - 7) + 9 - ROM Bank (initial bank = last - 6) + A - ROM Bank (initial bank = last - 5) + B - ROM Bank (initial bank = last - 4) + C - ROM Bank (initial bank = last - 3) + D - ROM Bank (initial bank = last - 2) + E - ROM Bank (initial bank = last - 1) + F - ROM Bank (initial bank = last) + + Segments 2-$F are switchable using ports : + + $C2 - Segment 2 (value written to port is ROM Bank number ($FF means last ROM bank (last 64 kbytes of ROM file) , $FE = last - 1 .. etc) + $C3 - Segment 3 (same as above) + $C0 - Segments 4-$F - bits 0,1,2 and 3 of port $C0 are bits 4,5,6 and 7 of ROM bank number in segments 4-$F . Bits 0-3 + are taken form segment number ( for example , IO[$C0]=$4E -> segment 9 contains ROM bank $E9). + + RAM Map : + + $0000 - $1FFF WS/WSC + $2000 - $3FFF 4 Col Tiles WS/WSC + ------------- + $4000 - $7FFF 16 Col Tiles Bank 0 WSC only + $8000 - $BFFF 16 Col Tiles Bank 1 WSC only + $C000 - $FDFF WSC only + $FE00 - $FFFF Palettes (WSC) WSC only + + Some games required initialized (?) part of RAM, for example: + + $75AC = $41 = "A" + $75AD = $5F = "_" + $75AE = $43 = "C" + $75AF = $31 = "1" + $75B0 = $6E = "n" + $75B1 = $5F = "_" + $75B2 = $63 = "c" + $75B3 = $31 = "1" + +4. VIDEO + + Screen size - 224 x 144 pixels (28 x 18 tiles) + Tile size - 8 x 8 dots , 16 bytes/tile (4 col modes) or 32 bytes/tile (16 col modes) + Map size - 256 x 256 pixels (32 x 32 tiles) + Layers - Two layers - Background and Foreground (top layer) + Maps locations - Selectable using port $07 + Tiles locations - Fixed, two banks - one at $4000 , second at $8000 + Map format - Each position in the map is defined by one word: + bits 0 - 8 - Tile number (0-511) + bits 9 - 12 - Palette number (0-15) + bit 13 - WS = unused / WSC = tile bank + bit 14 - Horizontal flip + bit 15 - Vertical flip + Tile formats - Depends on video mode (port $60) + Sprites - Max 128 sprites , limited to max 32 on scanline + sprite format: + byte 0,1 - bits + 0 - 8 - Tile number (0-511) + 9 - 11 - Palette number (0-7) + 8 -> (8-15) + 12 - Sprite window clipping on/off + 13 - Priority with respect to the layers + 0 - appear between the 2 background and foreground layers + 1 - appear on top of both layers + 14 - Horizontal flip + 15 - Vertical flip + byte 2 - Y position on the screen + byte 3 - X position on the screen + + Sprite table is buffered durning frame display. + Probably up to scanline 140 (1238-144?) + + Colors - Wonderswan (Mono) is capable of showing 16 shades of gray(only 8 can be selected at any one time) + These 8 shades form a pool from which the palette definition can select shades. There are 16 palettes. + All 16 palettes are used by BG and FG layers , the last 8 are used also by sprites. + Which 8 colors are used for palette generation is defined by ports 1C and 1E- port 1C + defines palette colors 0 - 3, port 1E defines 4 - 7. Each palette selection is 4 bits in + size: + 1C : 11110000 + 1D : 33332222 + 1E : 55554444 + 1F : 77776666 + + (where color 15 is the darkest one) + + Ports 20 - 3E are used to define the palettes themselves. + 20 : x111x000 - palette #0 + 21 : x333x222 + + In color video modes each color is defined using one word, + where bits: + 0 - 3 Blue + 4 - 7 Green + 8 - 11 Red + 12 - 14 unused + Color palettes are stored in the RAM (segment 0) , at address $FE00 + + Scrolling - Each of layers can be scrolled horizontal or vertical using ports $10 - $13 + + Transparency - Wonderswan - if bit 3 on palette number is set - color 0 of that palette is transparent + Wonderswan color - color 0 of each palette is transparent + Windows - There are two windows - rectangular areas for disabling /enabling FG layer (FG window) or sprites(Sprite window) + +5. SOUND + + 4 Audio channels. + Each channel can play short samples ( 4 bit , 16 bytes ( 32 sampels = 2 samples in byte (bits 0-3 and 4-7)) + with selectable frequency = 3,072 *10e6 / ((2048 - N) x 32 ) Hz , where N = 11 bit value. + Location of that samples is unknown. + + Volume of each audio channle is controlled by writing two 4 bit values ( for left/right output + channel) into ports $88 - $8B. Master volume is controlled by port $91 + (2 bit value = first 'used' bit in master volume output (11 bit wide) , D/A converter can + read only 8 bits , starting from bit set in port $91 , for example if first 'used' bit + is set to 2 , D/A using bits 2,3,4,5,6,7,8,9 for audio output) + + Additional (selectable) functions : + - channel 2 - voice - can play 8 bit samples writing frequently data to ch2 volume I/O port + - channel 3 - sweep - two parameters: + - step = 2.667 x (N + 1) ms , where N = 5 bit value + - value - signed byte (-128 - 127) + - channel 4 - noise - 7 selectable noise generators (probably I/O port $8E) + + For detailed info please check ports $80 - $91 in section I/O Ports. + + There's also Audio DMA (please chec ports $4a - $52). + Transfer rate is 12KHz (HBlank). + I/O ports $4A-$4B and $4E-$4F are autupdated durning data transfer . + +6. ROM HEADER + + Header taking last 10 bytes of each ROM file. + Bytes : + 0 - Developer ID + 1 - Minimum support system + 00 - WS Mono + 01 - WS Color + 2 - Cart ID number for developer defined at byte 0 + 3 - ?? + 4 - ROM Size + 01 - ? + 02 - 4Mbit + 03 - 8Mbit + 04 - 16Mbit + 05 - ? + 06 - 32Mbit + 07 - ? + 08 - 64Mbit + 09 - 128Mbit + 5 - SRAM/EEPROM Size + 00 - 0k + 01 - 64k SRAM + 02 - 256k SRAM + 03 - 1M SRAM (Taikyoku Igo Heisei Kiin) + 04 - 2M SRAM (WonderWitch) + 10 - 1k EEPROM + 20 - 16k EEPROM + 50 - 8k EEPROM + 6 - Additional capabilities(?) + - bit 0 - 1 - vertical position , 1 - horizontal position + - bit 2 - always 1 + + 7 - 1 - RTC (Real Time Clock) + 8,9 - Checksum = sum of all ROM bytes except two last ones ( where checksum is stored) + +7. INTERRUPTS + The Wonderswan CPU recognizes 7 interrupts from the hardware, these are: + 7 - HBlank Timer + 6 - VBlank + 5 - VBlank Timer + 4 - Drawing line detection + 3 - Serial Recieve + 2 - RTC Alarm (cartridge) + 1 - Key press + 0 - Serial Send + + Whether the CPU should indeed take action when one of these interrupts come in + is determined by port B2. The above mentioned interrupts correspond with the bit + numbers of port B2. When an interrupt occurs the corresponding bit of port B6 gets + set to 1 and, if enabled, an interrupt to the CPU is generated. This bit of port B6 + will have to be cleared through code when the interrupt has been handled. + + Example: + The Wonderswan is set to react to VBlank begin interrupts. Then bit 7 of B6 is set high + and keeps the interrupt line high until the CPU is able to take action upon this interrupt. + A typical VBlank interrupt routine is as follows: + + + out B6,40 + + iret + + The mentioned interrupts do not correspond with the same interrupt numbers for the vectors + in the vector table. The base for the actual interrupt numbers is set through port B0. If B0 + is set to 20h then a VBlank begin interrupt routine must be set at vector 26h. (Base is 20h + and VBlank begin interrupt is 6) + +8. CONTROLS - It's easy to check buttons status reading/writing port $B5(see below). + There's required some delay between writing and reading port $B5 ( few NOP-s) + +9. Internal EEPROM Communication(?) and 'owner' info structure + I/O Ports in range 0xBA -0xBE seems to be used for serial reading of internal + WS EEPROM (for example - 'owner' info). + + 0xBA (Word) - Data + 0xBC (Word) - Address (calculated probably Modulo EEPROM size (unknown)) + 0xBE (Byte) - Communication (?) + bit 4 set before reading data + bit 1 set by hardware , when data is ready to read + + Example : + + mov ax, $1B9 + out $BC, ax + mov al, $10 + out $BE, al + xor dx, dx + miniloop: + inc dx + cmp dl, 32 + jnc bad_data + in al, $BE + and al, 1 + jz miniloop + in ax, $BA ; Month and Day of birth + + + 'Owner' info structure : + + - Name - 16 bytes ( 0 = Space, 1 = '0' ... 0xA = '9', 0xB = 'A'... ) + - Year of birth - 2 bytes (BCD) + - Month of birth - 1 byte (BCD) + - Day of birth - 1 byte (BCD) + - Sex - 1 byte (1 - male , 2 - female) + - Blood - 1 byte (1 - A, 2 - B, 3 - 0, 4 - AB) + + + Struct size - 22 bytes = 11 reads, + Address range = 0x1B0 - 0x1BA + +10. I/O PORTS (port number /initial value / description) + +- $00 - $00 - Display control + bit 0 - background layer on/off + bit 1 - foreground layer on/off + bit 2 - sprites on/off + bit 3 - sprite window on/off (window coords defined in ports $0C - $0F) + bit 4,5 - fg win inside on/off (window coords defined in ports $08 - $0B) + Meaning of bits 4 and 5 : + 5 4 + --- + 0 0 FG layer is displayed inside and outside FG window area + 0 1 ?? + 1 0 FG layer is displayed only inside window + 1 1 FG layer is displayed outside window + - $01 - $00 - Determines the background color + bit 0-3 - background color + bit 4-7 - background palette (WSC only) + - $02 - ??? - Current Line (0 - 158) (159 ???) + - $03 - $BB - Line compare (for drawning line detection interrupt) + - $04 - $00 - Determines the base address for the sprite table. + To get the address of the table, shift this value left 9 times + and clear MSB. (bits 0..5 are effective to determines the base, + bits 6,7 are unknown) + 0 0xxxxxx0 00000000 + (Sprite Attribute Table Base can move from $00000-$07E00 with 512 bytes step) + - $05 - $00 - Determines the number of the sprite to start drawing with + - $06 - $00 - Determines the number of the sprite to stop drawing. + - $07 - $26 - Determines the location of the foreground and background screens in RAM. + Format: + bits 7-0 : ?fff?bbb + bit 7 - Unknown + bits 6-4 - Determines foreground location (address is 00fff000 00000000) + bit 3-? - Unknown + bits 2-0 - Determines background location (address is 00bbb000 00000000) + Back Ground Tile Map Base can move from $00000-$03800 (2048 bytes step) + - $08 - $FE - x0 of FG window (x0,y0) = top left corner, (x1,y1) = bottom right corner + - $09 - $DE - y0 of FG window + - $0A - $F9 - x1 of FG window + - $0B - $FB - y1 of FG window + - $0C - $DB - x0 of SPR window + - $0D - $D7 - y0 of SPR window + - $0E - $7F - x1 of SPR window + - $0F - $F5 - y1 of SPR window + - $10 - $00 - Background layer X scroll register + - $11 - $00 - Background layer Y scroll register + - $12 - $00 - Foreground layer X scroll register + - $13 - $00 - Foreground layer Y scroll register + - $14 - $01 - LCD Control (???) + bit 0 - 1 - LCD on + 0 - LCD off + - $15 - $00 - LCD Icons + bit 0 - LCD Sleep + bit 1 - Vertical Position + bit 2 - Horizontal Position + bit 3 - Dot 1 + bit 4 - Dot 2 + bit 5 - Dot 3 + bit 6 - Not Used ? + bit 7 - Not Used ? + - $16 - $9E - ??? + - $17 - $9B - ??? + - $18 - $00 - ??? + - $19 - $00 - ??? + - $1A - $00 - ??? + - $1B - $00 - ??? + - $1C - $99 - PALCOL10 + - $1D - $FD - PALCOL32 + - $1E - $B7 - PALCOL54 + - $1F - $DF - PALCOL76 + - $20 - $30 - PAL00 + - $21 - $57 - PAL01 + - $22 - $75 - PAL10 + - $23 - $76 - PAL11 + - $24 - $15 - PAL20 + - $25 - $73 - PAL21 + - $26 - $77 - PAL30 + - $27 - $77 - PAL31 + - $28 - $20 - PAL40 + - $29 - $75 - PAL41 + - $2A - $50 - PAL50 + - $2B - $36 - PAL51 + - $2C - $70 - PAL60 + - $2D - $67 - PAL61 + - $2E - $50 - PAL70 + - $2F - $77 - PAL70 + - $30 - $57 - PAL00 + - $31 - $54 - PAL01 + - $32 - $75 - PAL10 + - $33 - $77 - PAL11 + - $34 - $75 - PAL20 + - $35 - $17 - PAL21 + - $36 - $37 - PAL30 + - $37 - $73 - PAL31 + - $38 - $50 - PAL40 + - $39 - $57 - PAL41 + - $3A - $60 - PAL50 + - $3B - $77 - PAL51 + - $3C - $70 - PAL60 + - $3D - $77 - PAL61 + - $3E - $10 - PAL70 + - $3F - $73 - PAL70 + - $40 - $00 - DMA (?) copy source address + - $41 - $00 - ^^^ + - $42 - $00 - copy source bank + - $43 - $00 - copy destination bank + - $44 - $00 - copy destination address + - $45 - $00 - ^^^ + - $46 - $00 - size of copied data (in bytes) + - $47 - $00 - ^^^ + - $48 - $00 - bit 7 = 1 -> copy start + (bit 7=0 when data transfer is finished) + DMA(?) isn't immediate and not stopping + the main cpu operations (like gbc GDMA) + ports $40-$48 are updated durning copy process + - $49 - $00 - ??? + + - $4A - $00 - sound DMA source address + - $4B - $00 - ^^^ + - $4C - $00 - DMA source memory segment bank + - $4D - $00 - ??? + - $4E - $00 - DMA transfer size (in bytes) + - $4F - $00 - ^^^ + - $50 - $00 - ??? + - $51 - $00 - ??? + - $52 - $00 - bit 7 = 1 -> DMA start + - $53 - $00 - ??? + - $54 - $00 - ??? + - $55 - $00 - ??? + - $56 - $00 - ??? + - $57 - $00 - ??? + - $58 - $00 - ??? + - $59 - $00 - ??? + - $5A - $00 - ??? + - $5B - $00 - ??? + - $5C - $00 - ??? + - $5D - $00 - ??? + - $5E - $00 - ??? + - $5F - $00 - ??? + - $60 - $0A - video mode + Meaning of bits 5-7: + 765 + --- + 111 16 col/tile 'packed' mode - tiles like in Genesis, 16 col/tile + 110 16 col/tile 'layered' mode - tiles like in GameGear, 16 col/tile + 010 4 col/tile - the same as mono (below) but using color palettes, 4 cols/tile, one tile = 16 bytes, WSC only + 000 4 col/tile mono - tiles like in GameBoy, + [bit 7 = 16/4 color/tile , bit 6 - color/mono mode, bit 5 - 'packed' mode on/off] + - $61 - $00 - ??? + - $62 - $00 - ??? + - $63 - $00 - ??? + - $64 - $00 - ??? + - $65 - $00 - ??? + - $66 - $00 - ??? + - $67 - $00 - ??? + - $68 - $00 - ??? + - $69 - $00 - ??? + - $6A - $00 - ??? + - $6B - $0F - ??? + - $6C - $00 - ??? + - $6D - $00 - ??? + - $6E - $00 - ??? + - $6F - $00 - ??? + - $70 - $00 - ??? + - $71 - $00 - ??? + - $72 - $00 - ??? + - $73 - $00 - ??? + - $74 - $00 - ??? + - $75 - $00 - ??? + - $76 - $00 - ??? + - $77 - $00 - ??? + - $78 - $00 - ??? + - $79 - $00 - ??? + - $7A - $00 - ??? + - $7B - $00 - ??? + - $7C - $00 - ??? + - $7D - $00 - ??? + - $7E - $00 - ??? + - $7F - $00 - ??? + - $80 - $00 - Audio 1 Freq + - $81 - $00 - ^^^ + - $82 - $00 - Audio 2 Freq + - $83 - $00 - ^^^ + - $84 - $00 - Audio 3 Freq + - $85 - $00 - ^^^ + - $86 - $00 - Audio 4 Freq + - $87 - $00 - ^^^ + - $88 - $00 - Audio 1 volume + - $89 - $00 - Audio 2 volume + - $8A - $00 - Audio 3 volume + - $8B - $00 - Audio 4 volume + - $8C - $00 - ?? Sweep value + - $8D - $1F - ?? Sweep step + - $8E - $00 - Noise control + Bits : + 0 - Noise generator type + 1 - ^^^ + 2 - ^^^ + 3 - Reset + 4 - Enable + 5 - ??? + 6 - ??? + 7 - ??? + - $8F - $00 - Sample location + To get the address of samples, shift this value left 6 times. + 0 00xxxxxx xx000000 + - $90 - $00 - Audio control + Bits: + 0 - Audio 1 on/off + 1 - Audio 2 on/off + 2 - Audio 3 on/off + 3 - Audio 4 on/off + 4 - ??? + 5 - Audio 2 Voice + 6 - Audio 3 Sweep + 7 - Audio 4 Noise + - $91 - $00 - Audio Output + Bits : + 0 - Mono + 1 - Output Volume + 2 - ^^^ + 3 - External Stereo + 4 - ??? + 5 - ??? + 6 - ??? + 7 - External Speaker (set by hardware) + - $92 - $00 - Noise Counter Shift Register (15 bits) + - $93 - $00 - ^^^ + - $94 - $00 - Volume (4 bit) + - $95 - $00 - ??? + - $96 - $00 - ??? + - $97 - $00 - ??? + - $98 - $00 - ??? + - $99 - $00 - ??? + - $9A - $00 - ??? + - $9B - $00 - ??? + - $9C - $00 - ??? + - $9D - $00 - ??? + - $9E - $03 - ??? + - $9F - $00 - ??? + - $A0 - $87 - Hardware type + bit 1 - 1 - color + 0 - mono + - $A1 - $00 - ??? + - $A2 - $0C - Timer Control + bit 0 - Hblank Timer on/off + bit 1 - Hblank Timer Mode + 0 - One Shot + 1 - Auto Preset + bit 2 - Vblank Timer(1/75s) on/off + bit 3 - Vblank Timer Mode + 0 - One Shot + 1 - Auto Preset + - $A3 - $00 - ??? + - $A4 - $00 - Hblank Timer 'frequency' + 0 = no HBLANK Interrupt + n = HBLANK Interrupt every n lines (???) + - $A5 - $00 - ^^^ + - $A6 - $4F - Vblank Timer 'frequency' + - $A7 - $FF - ^^^ + - $A8 - $00 - Hblank Counter - 1/12000s + - $A9 - $00 - Hblank Counter - 1/(12000>>8)s + - $AA - $00 - Vblank Counter - 1/75s + - $AB - $00 - Vblank Counter - 1/(75>>8)s + - $AC - $00 - ??? + - $AD - $00 - ??? + - $AE - $00 - ??? + - $AF - $00 - ??? + - $B0 - $00 - Interrupt Base + - $B1 - $DB - Communication byte + - $B2 - $00 - Interrupt enable + bit 7 - HBlank Timer + bit 6 - VBlank begin + bit 5 - VBlank Timer + bit 4 - Drawing line detection + bit 3 - Serial receive + bit 2 - RTC Alarm + bit 1 - Key press + bit 0 - Serial transmit + - $B3 - $00 - Communication direction + bit 7 - Recieve data interrupt generation + bit 6 - Connection Speed + 0 - 9600 bps + 1 - 38400 bps + bit 5 - Send data interrupt generation + bit 4 - ??? + bit 3 - ??? + bit 2 - Send Complete + bit 1 - Error + bit 0 - Recieve Complete + + write $00-$7f = read $00 + write $80-$bf = read $84 + write $c0-$cf = read $c4 + - $B4 - $00 - ??? + - $B5 - $40 - Controls + bits 4-7 : read/write - Select line of inputs to read + 0001 - read vertical cursors + 0010 - read hozizontal cursors + 0100 - read buttons + bits 0-3 : read only - Read the current state of the input lines (positive logic) after having written 10h,20h, or 40h. + Meaning of the bits when reading cursors: + bit 0 - cursor up + bit 1 - cursor right + bit 2 - cursor down + bit 3 - cursor left + Meaning of the bits when reading buttons: + bit 0 - ??? + bit 1 - START + bit 2 - A + bit 3 - B + - $B6 - $00 - Interrupt Acknowledge + bit 7 - HBlank Timer + bit 6 - VBlank begin + bit 5 - VBlank Timer + bit 4 - Drawing line detection + bit 3 - Serial receive + bit 2 - RTC Alarm + bit 1 - Key press + bit 0 - Serial transmit + - $B7 - $00 - ??? + - $B8 - $00 - ??? + - $B9 - $00 - ??? + - $BA - $01 - Internal EEPROM (?) Data + - $BB - $00 - ^^^ + - $BC - $42 - Internal EEPROM (?) Address (calculated probably Modulo EEPROM (1kbit?) size (mirroring for read/write)) + - $BD - $00 - ^^^ + - $BE - $83 - Internal EEPROM (?) Command + bit 7 - Initialize ? + bit 6 - Protect ? + bit 5 - Write + bit 4 - Read + bit 3 - ??? + bit 2 - ??? + bit 1 - Write Complete (Read only) + bit 0 - Read Complete (Read only) + - $BF - $00 - ??? + - $C0 - $2F - ROM Bank Base Selector for segments 4-$F + - $C1 - $3F - SRAM Bank selector (???) + - $C2 - $FF - BNK2SLCT - ROM Bank selector for segment 2 + - $C3 - $FF - BNK3SLCT - ROM Bank selector for segment 3 + - $C4 - $00 - EEPROM Data + - $C5 - $00 - ^^^ + - $C6 - $00 - 1kbit EEPROM (16bit*64) : + - bits 0-5 - address + - bits 6-7 - command : + 0 - Extended Comand Address bits 4-5 + 0 - Write Disable + 1 - Write All + 2 - Erase All + 3 - Write Enable + 1 - Write + 2 - Read + 3 - Erase + - 16 kbit EEPROM (16bit*1024) - bits 0-7 - address (low) + - $C7 - $00 - 1kbit EEPROM (16bit*64) : + bit 0 - Start + - 16 kbit EEPROM (16bit*1024) : + - bits 0-1 - address (high) + - bits 2-3 - command : + 0 - Extended Comand Address bits 0-1 + 0 - Write Disable + 1 - Write All + 2 - Erase All + 3 - Write Enable + 1 - Write + 2 - Read + 3 - Erase + - bit 4 - Start + - $C8 - $D1 - EEPROM Command : + bit 7 - Initialize ??? + bit 6 - Protect ??? + bit 5 - Write + bit 4 - Read + bit 3 - ??? + bit 2 - ??? + bit 1 - Write Complete (Read only) + bit 0 - Read Complete (Read only) + - $C9 - $D1 - ??? + - $CA - $D1 - RTC Command + Write : + - $10 - Reset + - $12 - ??? Alarm ??? + - $13 - ??? + - $14 - Set Time + - $15 - Get Time + Read: + - bit 7 - Ack [HACK = 1] + - $CB - $D1 - RTC Data + Write : + Sometimes $40 , and wait for bit 7 = 1 + After Command ($CA): + - $14 - 7 writes (all BCD): + - Year ( + 2000) + - Month + - Day + - Day Of Week + - Hour + - Min + - Sec + Read + After Command ($CA) : + - $13 - bit 7 - Ack [HACK = 1] + - $15 - 7 reads (all BCD) + - Year ( + 2000) + - Month + - Day + - Day Of Week + - Hour + - Min + - Sec + - $CC - $D1 - ??? + - $CD - $D1 - ??? + - $CE - $D1 - ??? + - $CF - $D1 - ??? + - $D0 - $D1 - ??? + - $D1 - $D1 - ??? + - $D2 - $D1 - ??? + - $D3 - $D1 - ??? + - $D4 - $D1 - ??? + - $D5 - $D1 - ??? + - $D6 - $D1 - ??? + - $D7 - $D1 - ??? + - $D8 - $D1 - ??? + - $D9 - $D1 - ??? + - $DA - $D1 - ??? + - $DB - $D1 - ??? + - $DC - $D1 - ??? + - $DD - $D1 - ??? + - $DE - $D1 - ??? + - $DF - $D1 - ??? + - $E0 - $D1 - ??? + - $E1 - $D1 - ??? + - $E2 - $D1 - ??? + - $E3 - $D1 - ??? + - $E4 - $D1 - ??? + - $E5 - $D1 - ??? + - $E6 - $D1 - ??? + - $E7 - $D1 - ??? + - $E8 - $D1 - ??? + - $E9 - $D1 - ??? + - $EA - $D1 - ??? + - $EB - $D1 - ??? + - $EC - $D1 - ??? + - $ED - $D1 - ??? + - $EE - $D1 - ??? + - $EF - $D1 - ??? + - $F0 - $D1 - ??? + - $F1 - $D1 - ??? + - $F2 - $D1 - ??? + - $F3 - $D1 - ??? + - $F4 - $D1 - ??? + - $F5 - $D1 - ??? + - $F6 - $D1 - ??? + - $F7 - $D1 - ??? + - $F8 - $D1 - ??? + - $F9 - $D1 - ??? + - $FA - $D1 - ??? + - $FB - $D1 - ??? + - $FC - $D1 - ??? + - $FD - $D1 - ??? + - $FE - $D1 - ??? + - $FF - $D1 - ??? diff --git a/wonderswan/wswan.h b/wonderswan/wswan.h new file mode 100644 index 0000000000..c92937dc16 --- /dev/null +++ b/wonderswan/wswan.h @@ -0,0 +1,34 @@ +#ifndef __WSWAN_H +#define __WSWAN_H + +#include +#include + +#include "interrupt.h" + +namespace MDFN_IEN_WSWAN +{ + +#define mBCD(value) (((value)/10)<<4)|((value)%10) + +//extern uint32 rom_size; +//extern int wsc; + +/* +enum +{ + WSWAN_SEX_MALE = 1, + WSWAN_SEX_FEMALE = 2 +}; + +enum +{ + WSWAN_BLOOD_A = 1, + WSWAN_BLOOD_B = 2, + WSWAN_BLOOD_O = 3, + WSWAN_BLOOD_AB = 4 +}; +*/ +} + +#endif