diff --git a/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs b/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs
index ab8f9ad9e6..112b5ab7f0 100644
--- a/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs
+++ b/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs
@@ -45,7 +45,8 @@ namespace BizHawk.Client.EmuHawk
{ "C64", "C64" },
{ "GEN", "Genesis" },
{ "SMS", "Sega Master System" },
- { "PSX", "Sony PlayStation" }
+ { "PSX", "Sony PlayStation" },
+ { "LYNX", "Atari Lynx" },
};
public string TargetSystem = null;
diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs
index 8dc6669566..fb4b891bf3 100644
--- a/BizHawk.Emulation.Common/Database/Database.cs
+++ b/BizHawk.Emulation.Common/Database/Database.cs
@@ -331,6 +331,10 @@ namespace BizHawk.Emulation.Common
case ".WSC":
game.System = "WSWAN";
break;
+
+ case ".LNX":
+ game.System = "LYNX";
+ break;
}
game.Name = Path.GetFileNameWithoutExtension(fileName).Replace('_', ' ');
diff --git a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs
index 6cd24b93f0..c4720fac07 100644
--- a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs
+++ b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs
@@ -39,6 +39,9 @@ namespace BizHawk.Emulation.Common
Option("GBA", "Bios", GBA_JDebug);
}
+ FirmwareAndOption("e4ed47fae31693e016b081c6bda48da5b70d7ccb", "LYNX", "Boot", "lynxboot.img", "Boot Rom");
+
+
//FirmwareAndOption("24F67BDEA115A2C847C8813A262502EE1607B7DF", "NDS", "Bios_Arm7", "biosnds7.rom", "ARM7 Bios");
//FirmwareAndOption("BFAAC75F101C135E32E2AAF541DE6B1BE4C8C62D", "NDS", "Bios_Arm9", "biosnds9.rom", "ARM9 Bios");
FirmwareAndOption("5A65B922B562CB1F57DAB51B73151283F0E20C7A", "INTV", "EROM", "erom.bin", "Executive Rom");
diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index a402b54963..0ebc656f82 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -223,6 +223,8 @@
+
+
diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/lynx/LibLynx.cs b/BizHawk.Emulation.Cores/Consoles/Atari/lynx/LibLynx.cs
new file mode 100644
index 0000000000..f2202adf2e
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Atari/lynx/LibLynx.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using System.Runtime.InteropServices;
+
+namespace BizHawk.Emulation.Cores.Atari.Lynx
+{
+ public static class LibLynx
+ {
+ const string dllname = "bizlynx.dll";
+ const CallingConvention cc = CallingConvention.Cdecl;
+
+ [DllImport(dllname, CallingConvention = cc)]
+ public static extern IntPtr Create(byte[] game, int gamesize, byte[] bios, int biossize, int pagesize0, int pagesize1, bool lowpass);
+
+ [DllImport(dllname, CallingConvention = cc)]
+ public static extern void Destroy(IntPtr s);
+
+ [DllImport(dllname, CallingConvention = cc)]
+ public static extern void Reset(IntPtr s);
+
+ [DllImport(dllname, CallingConvention = cc)]
+ public static extern void Advance(IntPtr s, int buttons, int[] vbuff, short[] sbuff, ref int sbuffsize);
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.cs b/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.cs
new file mode 100644
index 0000000000..a4d5f84d13
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.cs
@@ -0,0 +1,287 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+
+using BizHawk.Common;
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Emulation.Cores.Atari.Lynx
+{
+ [CoreAttributes("Handy", "K. Wilkins", true, false, "mednafen 0-9-34-1", "http://mednafen.sourceforge.net/")]
+ public class Lynx : IEmulator, IVideoProvider, ISyncSoundProvider
+ {
+ IntPtr Core;
+
+ [CoreConstructor("LYNX")]
+ public Lynx(byte[] file, GameInfo game, CoreComm comm)
+ {
+ CoreComm = comm;
+
+ byte[] bios = CoreComm.CoreFileProvider.GetFirmware("LYNX", "Boot", true, "Boot rom is required");
+ if (bios.Length != 512)
+ throw new MissingFirmwareException("Lynx Bootrom must be 512 bytes!");
+
+ int pagesize0 = 0;
+ int pagesize1 = 0;
+ byte[] realfile = null;
+
+ {
+ var ms = new MemoryStream(file, false);
+ var br = new BinaryReader(ms);
+ string header = Encoding.ASCII.GetString(br.ReadBytes(4));
+ int p0 = br.ReadUInt16();
+ int p1 = br.ReadUInt16();
+ int ver = br.ReadUInt16();
+ string cname = Encoding.ASCII.GetString(br.ReadBytes(32)).Trim();
+ string mname = Encoding.ASCII.GetString(br.ReadBytes(16)).Trim();
+ int rot = br.ReadByte();
+
+ ms.Position = 6;
+ string bs93 = Encoding.ASCII.GetString(br.ReadBytes(6));
+ if (bs93 == "BS93")
+ throw new InvalidOperationException("Unsupported BS93 Lynx ram image");
+
+ if (header == "LYNX" && (ver & 255) == 1)
+ {
+ Console.WriteLine("Processing Handy-Lynx header");
+ pagesize0 = p0;
+ pagesize1 = p1;
+ Console.WriteLine("TODO: Rotate {0}", rot);
+ Console.WriteLine("Cart: {0} Manufacturer: {1}", cname, mname);
+ realfile = new byte[file.Length - 64];
+ Buffer.BlockCopy(file, 64, realfile, 0, realfile.Length);
+ Console.WriteLine("Header Listed banking: {0} {1}", p0, p1);
+ }
+ else
+ {
+ Console.WriteLine("No Handy-Lynx header found! Assuming raw rom image.");
+ realfile = file;
+ }
+
+ }
+
+ if (game.OptionPresent("pagesize0"))
+ {
+ pagesize0 = int.Parse(game.OptionValue("pagesize0"));
+ pagesize1 = int.Parse(game.OptionValue("pagesize1"));
+ Console.WriteLine("Loading banking options {0} {1} from gamedb", pagesize0, pagesize1);
+ }
+
+ if (pagesize0 == 0 && pagesize1 == 0)
+ {
+ switch (realfile.Length)
+ {
+ // these are untested
+ case 0x10000: pagesize0 = 0x100; break;
+ case 0x20000: pagesize0 = 0x200; break;
+ case 0x40000: pagesize0 = 0x400; break;
+ case 0x80000: pagesize0 = 0x800; break;
+
+ case 0x30000: pagesize0 = 0x200; pagesize1 = 0x100; break;
+ case 0x60000: pagesize0 = 0x400; pagesize1 = 0x200; break;
+ case 0xc0000: pagesize0 = 0x800; pagesize1 = 0x400; break;
+ case 0x100000: pagesize0 = 0x800; pagesize1 = 0x800; break;
+
+ }
+ Console.WriteLine("Auto-guessed banking options {0} {1}", pagesize0, pagesize1);
+ }
+
+ Core = LibLynx.Create(realfile, realfile.Length, bios, bios.Length, pagesize0, pagesize1, false);
+ try
+ {
+ // ...
+ }
+ catch
+ {
+ Dispose();
+ throw;
+ }
+ }
+
+ public void FrameAdvance(bool render, bool rendersound = true)
+ {
+ Frame++;
+
+ if (Controller["Power"])
+ LibLynx.Reset(Core);
+
+ int samples = soundbuff.Length;
+ LibLynx.Advance(Core, 0, videobuff, soundbuff, ref samples);
+ numsamp = samples;
+ Console.WriteLine(numsamp);
+ }
+
+ public int Frame { get; private set; }
+ public int LagCount { get; set; }
+ public bool IsLagFrame { get; private set; }
+
+ public string SystemId { get { return "LYNX"; } }
+
+ public bool DeterministicEmulation { get { return true; } }
+
+ public void ResetCounters()
+ {
+ Frame = 0;
+ LagCount = 0;
+ IsLagFrame = false;
+ }
+
+ public string BoardName { get { return null; } }
+
+ public CoreComm CoreComm { get; private set; }
+
+ public void Dispose()
+ {
+ if (Core != IntPtr.Zero)
+ {
+ LibLynx.Destroy(Core);
+ Core = IntPtr.Zero;
+ }
+ }
+
+ #region debugging
+
+ public Dictionary GetCpuFlagsAndRegisters()
+ {
+ return new Dictionary();
+ }
+
+ public void SetCpuRegister(string register, int value)
+ {
+ }
+
+ #endregion
+
+ #region Controller
+
+ public ControllerDefinition ControllerDefinition { get { return NullEmulator.NullController; } }
+ public IController Controller { get; set; }
+
+ #endregion
+
+ #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
+
+ #region saveram
+
+ public byte[] CloneSaveRam()
+ {
+ return new byte[0];
+ }
+
+ public void StoreSaveRam(byte[] data)
+ {
+ }
+
+ public void ClearSaveRam()
+ {
+ }
+
+ public bool SaveRamModified
+ {
+ get
+ {
+ return false;
+ }
+ set
+ {
+ throw new InvalidOperationException();
+ }
+ }
+
+ #endregion
+
+ #region VideoProvider
+
+ const int WIDTH = 160;
+ const int HEIGHT = 102;
+
+ int[] videobuff = new int[WIDTH * HEIGHT];
+
+ public IVideoProvider VideoProvider { get { return this; } }
+ public int[] GetVideoBuffer() { return videobuff; }
+ public int VirtualWidth { get { return WIDTH; } }
+ public int VirtualHeight { get { return HEIGHT; } }
+ public int BufferWidth { get { return WIDTH; } }
+ public int BufferHeight { get { return HEIGHT; } }
+ public int BackgroundColor { get { return unchecked((int)0xff000000); } }
+
+ #endregion
+
+ #region SoundProvider
+
+ short[] soundbuff = new short[1000000]; // todo: make this smaller once frame loop is resolved
+ int numsamp;
+
+ public ISoundProvider SoundProvider { get { return null; } }
+ public ISyncSoundProvider SyncSoundProvider { get { return this; } }
+ public bool StartAsyncSound() { return false; }
+ public void EndAsyncSound() { }
+
+ public void GetSamples(out short[] samples, out int nsamp)
+ {
+ samples = soundbuff;
+ nsamp = numsamp;
+ }
+
+ public void DiscardSamples()
+ {
+ }
+
+ #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
+
+ }
+}
diff --git a/lynx/bizlynx/bizlynx.sln b/lynx/bizlynx/bizlynx.sln
new file mode 100644
index 0000000000..5b68aaf523
--- /dev/null
+++ b/lynx/bizlynx/bizlynx.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bizlynx", "bizlynx.vcxproj", "{C8DC23FF-551E-45DE-852D-22F66EDBACB9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C8DC23FF-551E-45DE-852D-22F66EDBACB9}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C8DC23FF-551E-45DE-852D-22F66EDBACB9}.Debug|Win32.Build.0 = Debug|Win32
+ {C8DC23FF-551E-45DE-852D-22F66EDBACB9}.Release|Win32.ActiveCfg = Release|Win32
+ {C8DC23FF-551E-45DE-852D-22F66EDBACB9}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/lynx/bizlynx/bizlynx.vcxproj b/lynx/bizlynx/bizlynx.vcxproj
new file mode 100644
index 0000000000..d28b372eed
--- /dev/null
+++ b/lynx/bizlynx/bizlynx.vcxproj
@@ -0,0 +1,104 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+ {C8DC23FF-551E-45DE-852D-22F66EDBACB9}
+ bizlynx
+
+
+
+ DynamicLibrary
+ true
+ MultiByte
+
+
+ DynamicLibrary
+ false
+ true
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ Disabled
+ ..\msvc
+
+
+ true
+
+
+ copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\..\output\dll\$(TargetFileName)
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ ..\msvc
+
+
+ true
+ true
+ true
+
+
+ copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\..\output\dll\$(TargetFileName)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lynx/bizlynx/bizlynx.vcxproj.filters b/lynx/bizlynx/bizlynx.vcxproj.filters
new file mode 100644
index 0000000000..5ce0f99bbb
--- /dev/null
+++ b/lynx/bizlynx/bizlynx.vcxproj.filters
@@ -0,0 +1,108 @@
+
+
+
+
+ {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
+
+
+ {676beda4-1fca-49a2-acc8-67df1ab69690}
+
+
+ {7790947c-e33d-4f9b-a3bd-c72d9932bd76}
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files\sound
+
+
+ Source Files\sound
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files\sound
+
+
+ Header Files\sound
+
+
+
\ No newline at end of file
diff --git a/lynx/c6502mak.h b/lynx/c6502mak.h
new file mode 100644
index 0000000000..4d3d5e9914
--- /dev/null
+++ b/lynx/c6502mak.h
@@ -0,0 +1,694 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// 65C02 Macro definitions //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This file contains all of the required address mode and operand //
+// macro definitions for the 65C02 emulation //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+//
+// Addressing mode decoding
+//
+
+#define xIMMEDIATE() {mOperand=mPC;mPC++;}
+#define xABSOLUTE() {mOperand=CPU_PEEKW(mPC);mPC+=2;}
+#define xZEROPAGE() {mOperand=CPU_PEEK(mPC);mPC++;}
+#define xZEROPAGE_X() {mOperand=CPU_PEEK(mPC)+mX;mPC++;mOperand&=0xff;}
+#define xZEROPAGE_Y() {mOperand=CPU_PEEK(mPC)+mY;mPC++;mOperand&=0xff;}
+#define xABSOLUTE_X() {mOperand=CPU_PEEKW(mPC);mPC+=2;mOperand+=mX;mOperand&=0xffff;}
+#define xABSOLUTE_Y() {mOperand=CPU_PEEKW(mPC);mPC+=2;mOperand+=mY;mOperand&=0xffff;}
+#define xINDIRECT_ABSOLUTE_X() {mOperand=CPU_PEEKW(mPC);mPC+=2;mOperand+=mX;mOperand&=0xffff;mOperand=CPU_PEEKW(mOperand);}
+#define xRELATIVE() {mOperand=CPU_PEEK(mPC);mPC++;mOperand=(mPC+mOperand)&0xffff;}
+#define xINDIRECT_X() {mOperand=CPU_PEEK(mPC);mPC++;mOperand=mOperand+mX;mOperand&=0x00ff;mOperand=CPU_PEEKW(mOperand);}
+#define xINDIRECT_Y() {mOperand=CPU_PEEK(mPC);mPC++;mOperand=CPU_PEEKW(mOperand);mOperand=mOperand+mY;mOperand&=0xffff;}
+#define xINDIRECT_ABSOLUTE() {mOperand=CPU_PEEKW(mPC);mPC+=2;mOperand=CPU_PEEKW(mOperand);}
+#define xINDIRECT() {mOperand=CPU_PEEK(mPC);mPC++;mOperand=CPU_PEEKW(mOperand);}
+
+//
+// Helper Macros
+//
+//#define SET_Z(m) { mZ=(m)?false:true; }
+//#define SET_N(m) { mN=(m&0x80)?true:false; }
+//#define SET_NZ(m) SET_Z(m) SET_N(m)
+#define SET_Z(m) { mZ=!(m); }
+#define SET_N(m) { mN=(m)&0x80; }
+#define SET_NZ(m) { mZ=!(m); mN=(m)&0x80; }
+#define PULL(m) { mSP++; mSP&=0xff; m=CPU_PEEK(mSP+0x0100); }
+#define PUSH(m) { CPU_POKE(0x0100+mSP,m); mSP--; mSP&=0xff; }
+//
+// Opcode execution
+//
+
+#define xADC()\
+{\
+ int value=CPU_PEEK(mOperand);\
+ if(mD)\
+ {\
+ int c = mC?1:0;\
+ int lo = (mA & 0x0f) + (value & 0x0f) + c;\
+ int hi = (mA & 0xf0) + (value & 0xf0);\
+ mV=0;\
+ mC=0;\
+ if (lo > 0x09)\
+ {\
+ hi += 0x10;\
+ lo += 0x06;\
+ }\
+ if (~(mA^value) & (mA^hi) & 0x80) mV=1;\
+ if (hi > 0x90) hi += 0x60;\
+ if (hi & 0xff00) mC=1;\
+ mA = (lo & 0x0f) + (hi & 0xf0);\
+ }\
+ else\
+ {\
+ int c = mC?1:0;\
+ int sum = mA + value + c;\
+ mV=0;\
+ mC=0;\
+ if (~(mA^value) & (mA^sum) & 0x80) mV=1;\
+ if (sum & 0xff00) mC=1;\
+ mA = (uint8) sum;\
+ }\
+ SET_NZ(mA)\
+}
+
+#define xAND()\
+{\
+ mA&=CPU_PEEK(mOperand);\
+ SET_NZ(mA);\
+}
+
+#define xASL()\
+{\
+ int value=CPU_PEEK(mOperand);\
+ mC=value&0x80;\
+ value<<=1;\
+ value&=0xff;\
+ SET_NZ(value);\
+ CPU_POKE(mOperand,value);\
+}
+
+#define xASLA()\
+{\
+ mC=mA&0x80;\
+ mA<<=1;\
+ mA&=0xff;\
+ SET_NZ(mA);\
+}
+
+#define xBCC()\
+{\
+ if(!mC)\
+ {\
+ int offset=(signed char)CPU_PEEK(mPC);\
+ mPC++;\
+ mPC+=offset;\
+ mPC&=0xffff;\
+ }\
+ else\
+ {\
+ mPC++;\
+ mPC&=0xffff;\
+ }\
+}
+
+#define xBCS()\
+{\
+ if(mC)\
+ {\
+ int offset=(signed char)CPU_PEEK(mPC);\
+ mPC++;\
+ mPC+=offset;\
+ mPC&=0xffff;\
+ }\
+ else\
+ {\
+ mPC++;\
+ mPC&=0xffff;\
+ }\
+}
+
+#define xBEQ()\
+{\
+ if(mZ)\
+ {\
+ int offset=(signed char)CPU_PEEK(mPC);\
+ mPC++;\
+ mPC+=offset;\
+ mPC&=0xffff;\
+ }\
+ else\
+ {\
+ mPC++;\
+ mPC&=0xffff;\
+ }\
+}
+
+// This version of bit, not setting N and V status flags in immediate, seems to be correct.
+// The same behaviour is reported on the 65C02 used in old Apple computers, at least.
+// (From a pragmatic sense, using the normal version of bit for immediate
+// mode breaks the title screen of "California Games" in a subtle way.)
+#define xBIT()\
+{\
+ int value=CPU_PEEK(mOperand);\
+ SET_Z(mA&value);\
+\
+ if(mOpcode!=0x89)\
+ {\
+ mN=value&0x80;\
+ mV=value&0x40;\
+ }\
+}
+#define xBMI()\
+{\
+ if(mN)\
+ {\
+ int offset=(signed char)CPU_PEEK(mPC);\
+ mPC++;\
+ mPC+=offset;\
+ mPC&=0xffff;\
+ }\
+ else\
+ {\
+ mPC++;\
+ mPC&=0xffff;\
+ }\
+}
+
+#define xBNE()\
+{\
+ if(!mZ)\
+ {\
+ int offset=(signed char)CPU_PEEK(mPC);\
+ mPC++;\
+ mPC+=offset;\
+ mPC&=0xffff;\
+ }\
+ else\
+ {\
+ mPC++;\
+ mPC&=0xffff;\
+ }\
+}
+
+#define xBPL()\
+{\
+ if(!mN)\
+ {\
+ int offset=(signed char)CPU_PEEK(mPC);\
+ mPC++;\
+ mPC+=offset;\
+ mPC&=0xffff;\
+ }\
+ else\
+ {\
+ mPC++;\
+ mPC&=0xffff;\
+ }\
+}
+
+#define xBRA()\
+{\
+ int offset=(signed char)CPU_PEEK(mPC);\
+ mPC++;\
+ mPC+=offset;\
+ mPC&=0xffff;\
+}
+
+#define xBRK()\
+{\
+ mPC++;\
+ PUSH(mPC>>8);\
+ PUSH(mPC&0xff);\
+ PUSH(PS()|0x10);\
+\
+ mD=FALSE;\
+ mI=TRUE;\
+\
+ mPC=CPU_PEEKW(IRQ_VECTOR);\
+}
+// KW 4/11/98 B flag needed to be set IN the stack status word = 0x10.
+
+#define xBVC()\
+{\
+ if(!mV)\
+ {\
+ int offset=(signed char)CPU_PEEK(mPC);\
+ mPC++;\
+ mPC+=offset;\
+ mPC&=0xffff;\
+ }\
+ else\
+ {\
+ mPC++;\
+ mPC&=0xffff;\
+ }\
+}
+
+#define xBVS()\
+{\
+ if(mV)\
+ {\
+ int offset=(signed char)CPU_PEEK(mPC);\
+ mPC++;\
+ mPC+=offset;\
+ mPC&=0xffff;\
+ }\
+ else\
+ {\
+ mPC++;\
+ mPC&=0xffff;\
+ }\
+}
+
+#define xCLC()\
+{\
+ mC=FALSE;\
+}
+
+#define xCLD()\
+{\
+ mD=FALSE;\
+}
+
+#define xCLI()\
+{\
+ mI=FALSE;\
+}
+
+#define xCLV()\
+{\
+ mV=FALSE;\
+}
+
+#define xCMP()\
+{\
+ int value=CPU_PEEK(mOperand);\
+ mC=0;\
+ if (mA >= value) mC=1;\
+ SET_NZ((uint8)(mA - value))\
+}
+
+#define xCPX()\
+{\
+ int value=CPU_PEEK(mOperand);\
+ mC=0;\
+ if (mX >= value) mC=1;\
+ SET_NZ((uint8)(mX - value))\
+}
+
+#define xCPY()\
+{\
+ int value=CPU_PEEK(mOperand);\
+ mC=0;\
+ if (mY >= value) mC=1;\
+ SET_NZ((uint8)(mY - value))\
+}
+
+#define xDEC()\
+{\
+ int value=CPU_PEEK(mOperand)-1;\
+ value&=0xff;\
+ CPU_POKE(mOperand,value);\
+ SET_NZ(value);\
+}
+
+#define xDECA()\
+{\
+ mA--;\
+ mA&=0xff;\
+ SET_NZ(mA);\
+}
+
+#define xDEX()\
+{\
+ mX--;\
+ mX&=0xff;\
+ SET_NZ(mX);\
+}
+
+#define xDEY()\
+{\
+ mY--;\
+ mY&=0xff;\
+ SET_NZ(mY);\
+}
+
+#define xEOR()\
+{\
+ mA^=CPU_PEEK(mOperand);\
+ SET_NZ(mA);\
+}
+
+#define xINC()\
+{\
+ int value=CPU_PEEK(mOperand)+1;\
+ value&=0xff;\
+ CPU_POKE(mOperand,value);\
+ SET_NZ(value);\
+}
+
+#define xINCA()\
+{\
+ mA++;\
+ mA&=0xff;\
+ SET_NZ(mA);\
+}
+
+#define xINX()\
+{\
+ mX++;\
+ mX&=0xff;\
+ SET_NZ(mX);\
+}
+
+#define xINY()\
+{\
+ mY++;\
+ mY&=0xff;\
+ SET_NZ(mY);\
+}
+
+#define xJMP()\
+{\
+ mPC=mOperand;\
+}
+
+#define xJSR()\
+{\
+ PUSH((mPC-1)>>8);\
+ PUSH((mPC-1)&0xff);\
+ mPC=mOperand;\
+}
+
+#define xLDA()\
+{\
+ mA=CPU_PEEK(mOperand);\
+ SET_NZ(mA);\
+}
+
+#define xLDX()\
+{\
+ mX=CPU_PEEK(mOperand);\
+ SET_NZ(mX);\
+}
+
+#define xLDY()\
+{\
+ mY=CPU_PEEK(mOperand);\
+ SET_NZ(mY);\
+}
+
+#define xLSR()\
+{\
+ int value=CPU_PEEK(mOperand);\
+ mC=value&0x01;\
+ value=(value>>1)&0x7f;\
+ CPU_POKE(mOperand,value);\
+ SET_NZ(value);\
+}
+
+#define xLSRA()\
+{\
+ mC=mA&0x01;\
+ mA=(mA>>1)&0x7f;\
+ SET_NZ(mA);\
+}
+
+#define xNOP()\
+{\
+}
+
+#define xORA()\
+{\
+ mA|=CPU_PEEK(mOperand);\
+ SET_NZ(mA);\
+}
+
+#define xPHA()\
+{\
+ PUSH(mA);\
+}
+
+#define xPHP()\
+{\
+ PUSH(PS());\
+}
+
+#define xPHX()\
+{\
+ PUSH(mX);\
+}
+
+#define xPHY()\
+{\
+ PUSH(mY);\
+}
+
+#define xPLA()\
+{\
+ PULL(mA);\
+ SET_NZ(mA);\
+}
+
+#define xPLP()\
+{\
+ int P;\
+ PULL(P);\
+ PS(P);\
+}
+
+#define xPLX()\
+{\
+ PULL(mX);\
+ SET_NZ(mX);\
+}
+
+#define xPLY()\
+{\
+ PULL(mY);\
+ SET_NZ(mY);\
+}
+
+#define xROL()\
+{\
+ int value=CPU_PEEK(mOperand);\
+ int oldC=mC;\
+ mC=value&0x80;\
+ value=(value<<1)|(oldC?1:0);\
+ value&=0xff;\
+ CPU_POKE(mOperand,value);\
+ SET_NZ(value);\
+}
+
+#define xROLA()\
+{\
+ int oldC=mC;\
+ mC=mA&0x80;\
+ mA=(mA<<1)|(oldC?1:0);\
+ mA&=0xff;\
+ SET_NZ(mA);\
+}
+
+#define xROR()\
+{\
+ int value=CPU_PEEK(mOperand);\
+ int oldC=mC;\
+ mC=value&0x01;\
+ value=((value>>1)&0x7f)|(oldC?0x80:0x00);\
+ value&=0xff;\
+ CPU_POKE(mOperand,value);\
+ SET_NZ(value);\
+}
+
+#define xRORA()\
+{\
+ int oldC=mC;\
+ mC=mA&0x01;\
+ mA=((mA>>1)&0x7f)|(oldC?0x80:0x00);\
+ mA&=0xff;\
+ SET_NZ(mA);\
+}
+
+#define xRTI()\
+{\
+ int tmp;\
+ PULL(tmp);\
+ PS(tmp);\
+ PULL(mPC);\
+ PULL(tmp);\
+ mPC|=tmp<<8;\
+}
+
+#define xRTS()\
+{\
+ int tmp;\
+ PULL(mPC);\
+ PULL(tmp);\
+ mPC|=tmp<<8;\
+ mPC++;\
+}
+
+#define xSBC()\
+{\
+ int value=CPU_PEEK(mOperand);\
+ if (mD)\
+ {\
+ int c = mC?0:1;\
+ int sum = mA - value - c;\
+ int lo = (mA & 0x0f) - (value & 0x0f) - c;\
+ int hi = (mA & 0xf0) - (value & 0xf0);\
+ mV=0;\
+ mC=0;\
+ if ((mA^value) & (mA^sum) & 0x80) mV=1;\
+ if (lo & 0xf0) lo -= 6;\
+ if (lo & 0x80) hi -= 0x10;\
+ if (hi & 0x0f00) hi -= 0x60;\
+ if ((sum & 0xff00) == 0) mC=1;\
+ mA = (lo & 0x0f) + (hi & 0xf0);\
+ }\
+ else\
+ {\
+ int c = mC?0:1;\
+ int sum = mA - value - c;\
+ mV=0;\
+ mC=0;\
+ if ((mA^value) & (mA^sum) & 0x80) mV=1;\
+ if ((sum & 0xff00) == 0) mC=1;\
+ mA = (uint8) sum;\
+ }\
+ SET_NZ(mA)\
+}
+
+#define xSEC()\
+{\
+ mC=true;\
+}
+
+#define xSED()\
+{\
+ mD=true;\
+}
+
+#define xSEI()\
+{\
+ mI=true;\
+}
+
+#define xSTA()\
+{\
+ CPU_POKE(mOperand,mA);\
+}
+
+#define xSTP()\
+{\
+ mSystem.gSystemCPUSleep=TRUE;\
+}
+
+#define xSTX()\
+{\
+ CPU_POKE(mOperand,mX);\
+}
+
+#define xSTY()\
+{\
+ CPU_POKE(mOperand,mY);\
+}
+
+#define xSTZ()\
+{\
+ CPU_POKE(mOperand,0);\
+}
+
+#define xTAX()\
+{\
+ mX=mA;\
+ SET_NZ(mX);\
+}
+
+#define xTAY()\
+{\
+ mY=mA;\
+ SET_NZ(mY);\
+}
+
+#define xTRB()\
+{\
+ int value=CPU_PEEK(mOperand);\
+ SET_Z(mA&value);\
+ value=value&(mA^0xff);\
+ CPU_POKE(mOperand,value);\
+}
+
+#define xTSB()\
+{\
+ int value=CPU_PEEK(mOperand);\
+ SET_Z(mA&value);\
+ value=value|mA;\
+ CPU_POKE(mOperand,value);\
+}
+
+#define xTSX()\
+{\
+ mX=mSP;\
+ SET_NZ(mX);\
+}
+
+#define xTXA()\
+{\
+ mA=mX;\
+ SET_NZ(mA);\
+}
+
+#define xTXS()\
+{\
+ mSP=mX;\
+}
+
+#define xTYA()\
+{\
+ mA=mY;\
+ SET_NZ(mA);\
+}
+
+#define xWAI()\
+{\
+ mSystem.gSystemCPUSleep=TRUE;\
+}
+
diff --git a/lynx/c65c02.cpp b/lynx/c65c02.cpp
new file mode 100644
index 0000000000..f63e088e26
--- /dev/null
+++ b/lynx/c65c02.cpp
@@ -0,0 +1,1046 @@
+#include "system.h"
+
+#include "c65c02.h"
+
+void C65C02::Reset()
+{
+ TRACE_CPU0("Reset()");
+ mRamPointer=mSystem.GetRamPointer();
+ mA=0;
+ mX=0;
+ mY=0;
+ mSP=0xff;
+ mOpcode=0;
+ mOperand=0;
+ mPC=CPU_PEEKW(BOOT_VECTOR);
+ mN=FALSE;
+ mV=FALSE;
+ mB=FALSE;
+ mD=FALSE;
+ mI=TRUE;
+ mZ=TRUE;
+ mC=FALSE;
+ mIRQActive=FALSE;
+
+ mSystem.gSystemNMI=FALSE;
+ mSystem.gSystemIRQ=FALSE;
+ mSystem.gSystemCPUSleep=FALSE;
+}
+
+void C65C02::SetRegs(C6502_REGS ®s)
+{
+ PS(regs.PS);
+ mA=regs.A;
+ mX=regs.X;
+ mY=regs.Y;
+ mSP=regs.SP;
+ mOpcode=regs.Opcode;
+ mOperand=regs.Operand;
+ mPC=regs.PC;
+ mSystem.gSystemCPUSleep=regs.WAIT;
+ mSystem.gSystemNMI=regs.NMI;
+ mSystem.gSystemIRQ=regs.IRQ;
+}
+
+void C65C02::GetRegs(C6502_REGS ®s)
+{
+ regs.PS=PS();
+ regs.A=mA;
+ regs.X=mX;
+ regs.Y=mY;
+ regs.SP=mSP;
+ regs.Opcode=mOpcode;
+ regs.Operand=mOperand;
+ regs.PC=mPC;
+ regs.WAIT=mSystem.gSystemCPUSleep;
+ regs.NMI=mSystem.gSystemNMI;
+ regs.IRQ=mSystem.gSystemIRQ;
+}
+
+
+void C65C02::Update(void)
+{
+ if(mSystem.gSystemCPUSleep) return;
+ if(mSystem.gSystemIRQ && !mI && !mIRQActive)
+ {
+ // Push processor status
+ PUSH(mPC>>8);
+ PUSH(mPC&0xff);
+ PUSH(PS()&0xef); // Clear B flag on stack
+
+ mI=TRUE; // Stop further interrupts
+ mD=FALSE; // Clear decimal mode
+
+ // Pick up the new PC
+ mPC=CPU_PEEKW(IRQ_VECTOR);
+ }
+ // Fetch opcode
+ mOpcode=CPU_PEEK(mPC);
+ TRACE_CPU2("Update() PC=$%04x, Opcode=%02x",mPC,mOpcode);
+ mPC++;
+
+ // Execute Opcode
+
+ switch(mOpcode)
+ {
+#define ADDCYC(x) { mSystem.gSystemCycleCount += ((x) * 4); if(mSystem.gSuzieDoneTime) mSystem.gSuzieDoneTime += ((x) * 4); }
+ //
+ // 0x00
+ //
+ case 0x00:
+ ADDCYC(7);
+ // IMPLIED
+ xBRK();
+ break;
+ case 0x01:
+ ADDCYC(6);
+ xINDIRECT_X();
+ xORA();
+ break;
+ case 0x04:
+ ADDCYC(5);
+ xZEROPAGE();
+ xTSB();
+ break;
+ case 0x05:
+ ADDCYC(3);
+ xZEROPAGE();
+ xORA();
+ break;
+ case 0x06:
+ ADDCYC(5);
+ xZEROPAGE();
+ xASL();
+ break;
+ case 0x08:
+ ADDCYC(3);
+ // IMPLIED
+ xPHP();
+ break;
+ case 0x09:
+ ADDCYC(3);
+ xIMMEDIATE();
+ xORA();
+ break;
+ case 0x0A:
+ ADDCYC(2);
+ // IMPLIED
+ xASLA();
+ break;
+ case 0x0C:
+ ADDCYC(6);
+ xABSOLUTE();
+ xTSB();
+ break;
+ case 0x0D:
+ ADDCYC(4);
+ xABSOLUTE();
+ xORA();
+ break;
+ case 0x0E:
+ ADDCYC(6);
+ mSystem.gSystemCycleCount+=(1+(5*CPU_RDWR_CYC));
+ xABSOLUTE();
+ xASL();
+ break;
+
+ //
+ // 0x10
+ //
+ case 0x10:
+ ADDCYC(2);
+ // RELATIVE (IN FUNCTION)
+ xBPL();
+ break;
+ case 0x11:
+ ADDCYC(5);
+ xINDIRECT_Y();
+ xORA();
+ break;
+ case 0x12:
+ ADDCYC(5);
+ xINDIRECT();
+ xORA();
+ break;
+ case 0x14:
+ ADDCYC(5);
+ xZEROPAGE();
+ xTRB();
+ break;
+ case 0x15:
+ ADDCYC(4);
+ xZEROPAGE_X();
+ xORA();
+ break;
+ case 0x16:
+ ADDCYC(6);
+ xZEROPAGE_X();
+ xASL();
+ break;
+ case 0x18:
+ ADDCYC(2);
+ // IMPLIED
+ xCLC();
+ break;
+ case 0x19:
+ ADDCYC(4);
+ xABSOLUTE_Y();
+ xORA();
+ break;
+ case 0x1A:
+ ADDCYC(2);
+ // IMPLIED
+ xINCA();
+ break;
+ case 0x1C:
+ ADDCYC(6);
+ xABSOLUTE();
+ xTRB();
+ break;
+ case 0x1D:
+ ADDCYC(4);
+ xABSOLUTE_X();
+ xORA();
+ break;
+ case 0x1E:
+ ADDCYC(7);
+ xABSOLUTE_X();
+ xASL();
+ break;
+
+ //
+ // 0x20
+ //
+ case 0x20:
+ ADDCYC(6);
+ xABSOLUTE();
+ xJSR();
+ break;
+ case 0x21:
+ ADDCYC(6);
+ xINDIRECT_X();
+ xAND();
+ break;
+ case 0x24:
+ ADDCYC(3);
+ xZEROPAGE();
+ xBIT();
+ break;
+ case 0x25:
+ ADDCYC(3);
+ xZEROPAGE();
+ xAND();
+ break;
+ case 0x26:
+ ADDCYC(5);
+ xZEROPAGE();
+ xROL();
+ break;
+ case 0x28:
+ ADDCYC(4);
+ // IMPLIED
+ xPLP();
+ break;
+ case 0x29:
+ ADDCYC(2);
+ xIMMEDIATE();
+ xAND();
+ break;
+ case 0x2A:
+ ADDCYC(2);
+ // IMPLIED
+ xROLA();
+ break;
+ case 0x2C:
+ ADDCYC(4);
+ xABSOLUTE();
+ xBIT();
+ break;
+ case 0x2D:
+ ADDCYC(4);
+ xABSOLUTE();
+ xAND();
+ break;
+ case 0x2E:
+ ADDCYC(6);
+ xABSOLUTE();
+ xROL();
+ break;
+ //
+ // 0x30
+ //
+ case 0x30:
+ ADDCYC(2);
+ // RELATIVE (IN FUNCTION)
+ xBMI();
+ break;
+ case 0x31:
+ ADDCYC(5);
+ xINDIRECT_Y();
+ xAND();
+ break;
+ case 0x32:
+ ADDCYC(5);
+ xINDIRECT();
+ xAND();
+ break;
+ case 0x34:
+ ADDCYC(4);
+ xZEROPAGE_X();
+ xBIT();
+ break;
+ case 0x35:
+ ADDCYC(4);
+ xZEROPAGE_X();
+ xAND();
+ break;
+ case 0x36:
+ ADDCYC(6);
+ xZEROPAGE_X();
+ xROL();
+ break;
+ case 0x38:
+ ADDCYC(2);
+ // IMPLIED
+ xSEC();
+ break;
+ case 0x39:
+ ADDCYC(4);
+ xABSOLUTE_Y();
+ xAND();
+ break;
+ case 0x3A:
+ ADDCYC(2);
+ // IMPLIED
+ xDECA();
+ break;
+ case 0x3C:
+ ADDCYC(4);
+ xABSOLUTE_X();
+ xBIT();
+ break;
+ case 0x3D:
+ ADDCYC(4);
+ xABSOLUTE_X();
+ xAND();
+ break;
+ case 0x3E:
+ ADDCYC(7);
+ xABSOLUTE_X();
+ xROL();
+ break;
+ //
+ // 0x40
+ //
+ case 0x40:
+ ADDCYC(6);
+ // IMPLIED
+ xRTI();
+ break;
+ case 0x41:
+ ADDCYC(6);
+ xINDIRECT_X();
+ xEOR();
+ break;
+ case 0x45:
+ ADDCYC(3);
+ xZEROPAGE();
+ xEOR();
+ break;
+ case 0x46:
+ ADDCYC(5);
+ xZEROPAGE();
+ xLSR();
+ break;
+ case 0x48:
+ ADDCYC(3);
+ // IMPLIED
+ xPHA();
+ break;
+ case 0x49:
+ ADDCYC(2);
+ xIMMEDIATE();
+ xEOR();
+ break;
+ case 0x4A:
+ ADDCYC(2);
+ // IMPLIED
+ xLSRA();
+ break;
+ case 0x4C:
+ ADDCYC(3);
+ xABSOLUTE();
+ xJMP();
+ break;
+ case 0x4D:
+ ADDCYC(4);
+ xABSOLUTE();
+ xEOR();
+ break;
+ case 0x4E:
+ ADDCYC(6);
+ xABSOLUTE();
+ xLSR();
+ break;
+
+ //
+ // 0x50
+ //
+ case 0x50:
+ ADDCYC(2);
+ // RELATIVE (IN FUNCTION)
+ xBVC();
+ break;
+ case 0x51:
+ ADDCYC(5);
+ xINDIRECT_Y();
+ xEOR();
+ break;
+ case 0x52:
+ ADDCYC(5);
+ xINDIRECT();
+ xEOR();
+ break;
+ case 0x55:
+ ADDCYC(4);
+ xZEROPAGE_X();
+ xEOR();
+ break;
+ case 0x56:
+ ADDCYC(6);
+ xZEROPAGE_X();
+ xLSR();
+ break;
+ case 0x58:
+ ADDCYC(2);
+ // IMPLIED
+ xCLI();
+ break;
+ case 0x59:
+ ADDCYC(4);
+ xABSOLUTE_Y();
+ xEOR();
+ break;
+ case 0x5A:
+ ADDCYC(3);
+ // IMPLIED
+ xPHY();
+ break;
+ case 0x5D:
+ ADDCYC(4);
+ xABSOLUTE_X();
+ xEOR();
+ break;
+ case 0x5E:
+ ADDCYC(7);
+ xABSOLUTE_X();
+ xLSR();
+ break;
+
+ //
+ // 0x60
+ //
+ case 0x60:
+ ADDCYC(6);
+ // IMPLIED
+ xRTS();
+ break;
+ case 0x61:
+ ADDCYC(6);
+ xINDIRECT_X();
+ xADC();
+ break;
+ case 0x64:
+ ADDCYC(3);
+ xZEROPAGE();
+ xSTZ();
+ break;
+ case 0x65:
+ ADDCYC(3);
+ xZEROPAGE();
+ xADC();
+ break;
+ case 0x66:
+ ADDCYC(5);
+ xZEROPAGE();
+ xROR();
+ break;
+ case 0x68:
+ ADDCYC(4);
+ // IMPLIED
+ xPLA();
+ break;
+ case 0x69:
+ ADDCYC(2);
+ xIMMEDIATE();
+ xADC();
+ break;
+ case 0x6A:
+ ADDCYC(2);
+ // IMPLIED
+ xRORA();
+ break;
+ case 0x6C:
+ ADDCYC(6);
+ xINDIRECT_ABSOLUTE();
+ xJMP();
+ break;
+ case 0x6D:
+ ADDCYC(4);
+ xABSOLUTE();
+ xADC();
+ break;
+ case 0x6E:
+ ADDCYC(6);
+ xABSOLUTE();
+ xROR();
+ break;
+ //
+ // 0x70
+ //
+ case 0x70:
+ ADDCYC(2);
+ // RELATIVE (IN FUNCTION)
+ xBVS();
+ break;
+ case 0x71:
+ ADDCYC(5);
+ xINDIRECT_Y();
+ xADC();
+ break;
+ case 0x72:
+ ADDCYC(5);
+ xINDIRECT();
+ xADC();
+ break;
+ case 0x74:
+ ADDCYC(4);
+ xZEROPAGE_X();
+ xSTZ();
+ break;
+ case 0x75:
+ ADDCYC(4);
+ xZEROPAGE_X();
+ xADC();
+ break;
+ case 0x76:
+ ADDCYC(6);
+ xZEROPAGE_X();
+ xROR();
+ break;
+ case 0x78:
+ ADDCYC(2);
+ // IMPLIED
+ xSEI();
+ break;
+ case 0x79:
+ mSystem.gSystemCycleCount+=(1+(3*CPU_RDWR_CYC));
+ xABSOLUTE_Y();
+ xADC();
+ break;
+ case 0x7A:
+ ADDCYC(4);
+ // IMPLIED
+ xPLY();
+ break;
+ case 0x7C:
+ ADDCYC(6);
+ xINDIRECT_ABSOLUTE_X();
+ xJMP();
+ break;
+ case 0x7D:
+ ADDCYC(4);
+ xABSOLUTE_X();
+ xADC();
+ break;
+ case 0x7E:
+ ADDCYC(7);
+ xABSOLUTE_X();
+ xROR();
+ break;
+ //
+ // 0x80
+ //
+ case 0x80:
+ ADDCYC(3);
+ // RELATIVE (IN FUNCTION)
+ xBRA();
+ break;
+ case 0x81:
+ ADDCYC(6);
+ xINDIRECT_X();
+ xSTA();
+ break;
+ case 0x84:
+ ADDCYC(3);
+ xZEROPAGE();
+ xSTY();
+ break;
+ case 0x85:
+ ADDCYC(3);
+ xZEROPAGE();
+ xSTA();
+ break;
+ case 0x86:
+ ADDCYC(3);
+ xZEROPAGE();
+ xSTX();
+ break;
+ case 0x88:
+ ADDCYC(2);
+ // IMPLIED
+ xDEY();
+ break;
+ case 0x89:
+ ADDCYC(3);
+ xIMMEDIATE();
+ xBIT();
+ break;
+ case 0x8A:
+ ADDCYC(2);
+ // IMPLIED
+ xTXA();
+ break;
+ case 0x8C:
+ ADDCYC(4);
+ xABSOLUTE();
+ xSTY();
+ break;
+ case 0x8D:
+ ADDCYC(4);
+ xABSOLUTE();
+ xSTA();
+ break;
+ case 0x8E:
+ ADDCYC(4);
+ xABSOLUTE();
+ xSTX();
+ break;
+
+ //
+ // 0x90
+ //
+ case 0x90:
+ ADDCYC(2);
+ // RELATIVE (IN FUNCTION)
+ xBCC();
+ break;
+ case 0x91:
+ ADDCYC(6);
+ xINDIRECT_Y();
+ xSTA();
+ break;
+ case 0x92:
+ ADDCYC(5);
+ xINDIRECT();
+ xSTA();
+ break;
+ case 0x94:
+ ADDCYC(4);
+ xZEROPAGE_X();
+ xSTY();
+ break;
+ case 0x95:
+ ADDCYC(4);
+ xZEROPAGE_X();
+ xSTA();
+ break;
+ case 0x96:
+ ADDCYC(4);
+ xZEROPAGE_Y();
+ xSTX();
+ break;
+ case 0x98:
+ ADDCYC(2);
+ // IMPLIED
+ xTYA();
+ break;
+ case 0x99:
+ ADDCYC(5);
+ xABSOLUTE_Y();
+ xSTA();
+ break;
+ case 0x9A:
+ ADDCYC(2);
+ // IMPLIED
+ xTXS();
+ break;
+ case 0x9C:
+ ADDCYC(4);
+ xABSOLUTE();
+ xSTZ();
+ break;
+ case 0x9D:
+ ADDCYC(5);
+ xABSOLUTE_X();
+ xSTA();
+ break;
+ case 0x9E:
+ ADDCYC(5);
+ xABSOLUTE_X();
+ xSTZ();
+ break;
+
+ //
+ // 0xA0
+ //
+ case 0xA0:
+ ADDCYC(2);
+ xIMMEDIATE();
+ xLDY();
+ break;
+ case 0xA1:
+ ADDCYC(6);
+ xINDIRECT_X();
+ xLDA();
+ break;
+ case 0xA2:
+ ADDCYC(2);
+ xIMMEDIATE();
+ xLDX();
+ break;
+ case 0xA4:
+ ADDCYC(3);
+ xZEROPAGE();
+ xLDY();
+ break;
+ case 0xA5:
+ ADDCYC(3);
+ xZEROPAGE();
+ xLDA();
+ break;
+ case 0xA6:
+ ADDCYC(3);
+ xZEROPAGE();
+ xLDX();
+ break;
+ case 0xA8:
+ ADDCYC(2);
+ // IMPLIED
+ xTAY();
+ break;
+ case 0xA9:
+ ADDCYC(2);
+ xIMMEDIATE();
+ xLDA();
+ break;
+ case 0xAA:
+ ADDCYC(2);
+ // IMPLIED
+ xTAX();
+ break;
+ case 0xAC:
+ ADDCYC(4);
+ xABSOLUTE();
+ xLDY();
+ break;
+ case 0xAD:
+ ADDCYC(4);
+ xABSOLUTE();
+ xLDA();
+ break;
+ case 0xAE:
+ ADDCYC(4);
+ xABSOLUTE();
+ xLDX();
+ break;
+
+ //
+ // 0xB0
+ //
+ case 0xB0:
+ ADDCYC(2);
+ // RELATIVE (IN FUNCTION)
+ xBCS();
+ break;
+ case 0xB1:
+ ADDCYC(5);
+ xINDIRECT_Y();
+ xLDA();
+ break;
+ case 0xB2:
+ ADDCYC(5);
+ xINDIRECT();
+ xLDA();
+ break;
+ case 0xB4:
+ ADDCYC(4);
+ xZEROPAGE_X();
+ xLDY();
+ break;
+ case 0xB5:
+ ADDCYC(4);
+ xZEROPAGE_X();
+ xLDA();
+ break;
+ case 0xB6:
+ ADDCYC(4);
+ xZEROPAGE_Y();
+ xLDX();
+ break;
+ case 0xB8:
+ ADDCYC(2);
+ // IMPLIED
+ xCLV();
+ break;
+ case 0xB9:
+ ADDCYC(4);
+ xABSOLUTE_Y();
+ xLDA();
+ break;
+ case 0xBA:
+ ADDCYC(2);
+ // IMPLIED
+ xTSX();
+ break;
+ case 0xBC:
+ ADDCYC(4);
+ xABSOLUTE_X();
+ xLDY();
+ break;
+ case 0xBD:
+ ADDCYC(4);
+ xABSOLUTE_X();
+ xLDA();
+ break;
+ case 0xBE:
+ ADDCYC(4);
+ xABSOLUTE_Y();
+ xLDX();
+ break;
+
+ //
+ // 0xC0
+ //
+ case 0xC0:
+ ADDCYC(2);
+ xIMMEDIATE();
+ xCPY();
+ break;
+ case 0xC1:
+ ADDCYC(6);
+ xINDIRECT_X();
+ xCMP();
+ break;
+ case 0xC4:
+ ADDCYC(3);
+ xZEROPAGE();
+ xCPY();
+ break;
+ case 0xC5:
+ ADDCYC(3);
+ xZEROPAGE();
+ xCMP();
+ break;
+ case 0xC6:
+ ADDCYC(5);
+ xZEROPAGE();
+ xDEC();
+ break;
+ case 0xC8:
+ ADDCYC(2);
+ // IMPLIED
+ xINY();
+ break;
+ case 0xC9:
+ ADDCYC(2);
+ xIMMEDIATE();
+ xCMP();
+ break;
+ case 0xCA:
+ ADDCYC(2);
+ // IMPLIED
+ xDEX();
+ break;
+ case 0xCB:
+ ADDCYC(2);
+ // IMPLIED
+ xWAI();
+ break;
+ case 0xCC:
+ ADDCYC(4);
+ xABSOLUTE();
+ xCPY();
+ break;
+ case 0xCD:
+ ADDCYC(4);
+ xABSOLUTE();
+ xCMP();
+ break;
+ case 0xCE:
+ ADDCYC(6);
+ xABSOLUTE();
+ xDEC();
+ break;
+ //
+ // 0xD0
+ //
+ case 0xD0:
+ ADDCYC(2);
+ // RELATIVE (IN FUNCTION)
+ xBNE();
+ break;
+ case 0xD1:
+ ADDCYC(5);
+ xINDIRECT_Y();
+ xCMP();
+ break;
+ case 0xD2:
+ ADDCYC(5);
+ xINDIRECT();
+ xCMP();
+ break;
+ case 0xD5:
+ ADDCYC(4);
+ xZEROPAGE_X();
+ xCMP();
+ break;
+ case 0xD6:
+ ADDCYC(6);
+ xZEROPAGE_X();
+ xDEC();
+ break;
+ case 0xD8:
+ ADDCYC(2);
+ // IMPLIED
+ xCLD();
+ break;
+ case 0xD9:
+ ADDCYC(4);
+ xABSOLUTE_Y();
+ xCMP();
+ break;
+ case 0xDA:
+ ADDCYC(3);
+ // IMPLIED
+ xPHX();
+ break;
+ case 0xDB:
+ ADDCYC(2);
+ // IMPLIED
+ xSTP();
+ break;
+ case 0xDD:
+ ADDCYC(4);
+ xABSOLUTE_X();
+ xCMP();
+ break;
+ case 0xDE:
+ ADDCYC(7);
+ xABSOLUTE_X();
+ xDEC();
+ break;
+ //
+ // 0xE0
+ //
+ case 0xE0:
+ ADDCYC(2);
+ xIMMEDIATE();
+ xCPX();
+ break;
+ case 0xE1:
+ ADDCYC(6);
+ xINDIRECT_X();
+ xSBC();
+ break;
+ case 0xE4:
+ ADDCYC(3);
+ xZEROPAGE();
+ xCPX();
+ break;
+ case 0xE5:
+ ADDCYC(3);
+ xZEROPAGE();
+ xSBC();
+ break;
+ case 0xE6:
+ ADDCYC(5);
+ xZEROPAGE();
+ xINC();
+ break;
+ case 0xE8:
+ ADDCYC(2);
+ // IMPLIED
+ xINX();
+ break;
+ case 0xE9:
+ ADDCYC(2);
+ xIMMEDIATE();
+ xSBC();
+ break;
+ default:
+ case 0xEA:
+ ADDCYC(2);
+ // IMPLIED
+ xNOP();
+ break;
+ case 0xEC:
+ ADDCYC(4);
+ xABSOLUTE();
+ xCPX();
+ break;
+ case 0xED:
+ ADDCYC(4);
+ xABSOLUTE();
+ xSBC();
+ break;
+ case 0xEE:
+ ADDCYC(6);
+ xABSOLUTE();
+ xINC();
+ break;
+ //
+ // 0xF0
+ //
+ case 0xF0:
+ ADDCYC(2);
+ // RELATIVE (IN FUNCTION)
+ xBEQ();
+ break;
+ case 0xF1:
+ ADDCYC(5);
+ xINDIRECT_Y();
+ xSBC();
+ break;
+ case 0xF2:
+ ADDCYC(5);
+ xINDIRECT();
+ xSBC();
+ break;
+ case 0xF5:
+ ADDCYC(4);
+ xZEROPAGE_X();
+ xSBC();
+ break;
+ case 0xF6:
+ ADDCYC(6);
+ xZEROPAGE_X();
+ xINC();
+ break;
+ case 0xF8:
+ ADDCYC(2);
+ // IMPLIED
+ xSED();
+ break;
+ case 0xF9:
+ ADDCYC(4);
+ xABSOLUTE_Y();
+ xSBC();
+ break;
+ case 0xFA:
+ ADDCYC(4);
+ // IMPLIED
+ xPLX();
+ break;
+ case 0xFD:
+ ADDCYC(4);
+ xABSOLUTE_X();
+ xSBC();
+ break;
+ case 0xFE:
+ ADDCYC(7);
+ xABSOLUTE_X();
+ xINC();
+ break;
+ }
+}
diff --git a/lynx/c65c02.h b/lynx/c65c02.h
new file mode 100644
index 0000000000..6eed1dff44
--- /dev/null
+++ b/lynx/c65c02.h
@@ -0,0 +1,229 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// 65C02 Emulation class //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This class emulates a 65C02 processor. It is interfaced to the rest of //
+// the system via the PEEK/POKE macros and a number of global variables //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef C65C02_H
+#define C65C02_H
+
+//#include
+//#define TRACE_CPU
+
+#ifdef TRACE_CPU
+
+#define TRACE_CPU0(msg) _RPT1(_CRT_WARN,"C65C02::"msg" (Time=%012d)\n",gSystemCycleCount)
+#define TRACE_CPU1(msg,arg1) _RPT2(_CRT_WARN,"C65C02::"msg" (Time=%012d)\n",arg1,gSystemCycleCount)
+#define TRACE_CPU2(msg,arg1,arg2) _RPT3(_CRT_WARN,"C65C02::"msg" (Time=%012d)\n",arg1,arg2,gSystemCycleCount)
+#define TRACE_CPU3(msg,arg1,arg2,arg3) _RPT4(_CRT_WARN,"C65C02::"msg" (Time=%012d)\n",arg1,arg2,arg3,gSystemCycleCount)
+
+#else
+
+#define TRACE_CPU0(msg)
+#define TRACE_CPU1(msg,arg1)
+#define TRACE_CPU2(msg,arg1,arg2)
+#define TRACE_CPU3(msg,arg1,arg2,arg3)
+
+#endif
+
+//
+// Handy definitions
+//
+
+#define NMI_VECTOR 0xfffa
+#define BOOT_VECTOR 0xfffc
+#define IRQ_VECTOR 0xfffe
+
+#define MAX_CPU_BREAKPOINTS 8
+
+//
+// ACCESS MACROS
+//
+
+//#define CPU_PEEK(m) (mSystem.Peek_CPU(m))
+//#define CPU_PEEKW(m) (mSystem.PeekW_CPU(m))
+//#define CPU_POKE(m1,m2) (mSystem.Poke_CPU(m1,m2))
+
+#define CPU_PEEK(m) (((m<0xfc00)?mRamPointer[m]:mSystem.Peek_CPU(m)))
+#define CPU_PEEKW(m) (((m<0xfc00)?(mRamPointer[m]+(mRamPointer[m+1]<<8)):mSystem.PeekW_CPU(m)))
+#define CPU_POKE(m1,m2) {if(m1<0xfc00) mRamPointer[m1]=m2; else mSystem.Poke_CPU(m1,m2);}
+
+
+enum { illegal=0,
+ accu,
+ imm,
+ absl,
+ zp,
+ zpx,
+ zpy,
+ absx,
+ absy,
+ iabsx,
+ impl,
+ rel,
+ zrel,
+ indx,
+ indy,
+ iabs,
+ ind
+};
+
+typedef struct
+{
+ int PS; // Processor status register 8 bits
+ int A; // Accumulator 8 bits
+ int X; // X index register 8 bits
+ int Y; // Y index register 8 bits
+ int SP; // Stack Pointer 8 bits
+ int Opcode; // Instruction opcode 8 bits
+ int Operand;// Intructions operand 16 bits
+ int PC; // Program Counter 16 bits
+ bool NMI;
+ bool IRQ;
+ bool WAIT;
+}C6502_REGS;
+
+//
+// The CPU emulation macros
+//
+#include "c6502mak.h"
+//
+// The CPU emulation macros
+//
+
+class C65C02
+{
+public:
+ C65C02(CSystem& parent)
+ :mSystem(parent)
+ {
+ TRACE_CPU0("C65C02()");
+ // Compute the BCD lookup table
+ for(uint16 t=0;t<256;++t)
+ {
+ mBCDTable[0][t]=((t >> 4) * 10) + (t & 0x0f);
+ mBCDTable[1][t]=(((t % 100) / 10) << 4) | (t % 10);
+ }
+ Reset();
+
+ }
+
+ ~C65C02()
+ {
+ TRACE_CPU0("~C65C02()");
+ }
+
+public:
+ void Reset();
+ void Update();
+
+ void SetRegs(C6502_REGS ®s);
+ void GetRegs(C6502_REGS ®s);
+
+
+ inline int GetPC() { return mPC; }
+
+private:
+ CSystem &mSystem;
+
+ // CPU Flags & status
+
+ int mA; // Accumulator 8 bits
+ int mX; // X index register 8 bits
+ int mY; // Y index register 8 bits
+ int mSP; // Stack Pointer 8 bits
+ int mOpcode; // Instruction opcode 8 bits
+ int mOperand; // Intructions operand 16 bits
+ int mPC; // Program Counter 16 bits
+
+ int mN; // N flag for processor status register
+ int mV; // V flag for processor status register
+ int mB; // B flag for processor status register
+ int mD; // D flag for processor status register
+ int mI; // I flag for processor status register
+ int mZ; // Z flag for processor status register
+ int mC; // C flag for processor status register
+
+ int mIRQActive;
+
+ uint8 *mRamPointer;
+
+ // Associated lookup tables
+
+ int mBCDTable[2][256];
+
+ //
+ // Opcode prototypes
+ //
+
+private:
+
+ // Answers value of the Processor Status register
+ INLINE int PS() const
+ {
+ uint8 ps = 0x20;
+ if(mN) ps|=0x80;
+ if(mV) ps|=0x40;
+ if(mB) ps|=0x10;
+ if(mD) ps|=0x08;
+ if(mI) ps|=0x04;
+ if(mZ) ps|=0x02;
+ if(mC) ps|=0x01;
+ return ps;
+ }
+
+
+ // Change the processor flags to correspond to the given value
+ INLINE void PS(int ps)
+ {
+ mN=ps&0x80;
+ mV=ps&0x40;
+ mB=ps&0x10;
+ mD=ps&0x08;
+ mI=ps&0x04;
+ mZ=ps&0x02;
+ mC=ps&0x01;
+ }
+
+};
+
+
+#endif
diff --git a/lynx/cart.cpp b/lynx/cart.cpp
new file mode 100644
index 0000000000..592b0b2c2d
--- /dev/null
+++ b/lynx/cart.cpp
@@ -0,0 +1,316 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// Lynx Cartridge Class //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This class emulates the Lynx cartridge interface, given a filename it //
+// will contstruct a cartridge object via the constructor. //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#define CART_CPP
+
+#include "system.h"
+
+#include
+#include
+#include "cart.h"
+
+/*
+bool CCart::TestMagic(const uint8 *data, uint32 size)
+{
+ if(size <= HEADER_RAW_SIZE)
+ return(FALSE);
+
+ if(memcmp(data, "LYNX", 4) || data[8] != 0x01)
+ return(FALSE);
+
+ return(TRUE);
+}
+*/
+
+CCart::CCart(const uint8 *gamedata, uint32 gamesize, int pagesize0, int pagesize1)
+{
+ CTYPE banktype0,banktype1;
+
+ switch(pagesize0)
+ {
+ default:
+ // warn?
+ case 0x000:
+ banktype0=UNUSED;
+ mMaskBank0=0;
+ mShiftCount0=0;
+ mCountMask0=0;
+ break;
+ case 0x100:
+ banktype0=C64K;
+ mMaskBank0=0x00ffff;
+ mShiftCount0=8;
+ mCountMask0=0x0ff;
+ break;
+ case 0x200:
+ banktype0=C128K;
+ mMaskBank0=0x01ffff;
+ mShiftCount0=9;
+ mCountMask0=0x1ff;
+ break;
+ case 0x400:
+ banktype0=C256K;
+ mMaskBank0=0x03ffff;
+ mShiftCount0=10;
+ mCountMask0=0x3ff;
+ break;
+ case 0x800:
+ banktype0=C512K;
+ mMaskBank0=0x07ffff;
+ mShiftCount0=11;
+ mCountMask0=0x7ff;
+ break;
+ }
+
+ switch(pagesize1)
+ {
+ default:
+ // warn?
+ case 0x000:
+ banktype1=UNUSED;
+ mMaskBank1=0;
+ mShiftCount1=0;
+ mCountMask1=0;
+ break;
+ case 0x100:
+ banktype1=C64K;
+ mMaskBank1=0x00ffff;
+ mShiftCount1=8;
+ mCountMask1=0x0ff;
+ break;
+ case 0x200:
+ banktype1=C128K;
+ mMaskBank1=0x01ffff;
+ mShiftCount1=9;
+ mCountMask1=0x1ff;
+ break;
+ case 0x400:
+ banktype1=C256K;
+ mMaskBank1=0x03ffff;
+ mShiftCount1=10;
+ mCountMask1=0x3ff;
+ break;
+ case 0x800:
+ banktype1=C512K;
+ mMaskBank1=0x07ffff;
+ mShiftCount1=11;
+ mCountMask1=0x7ff;
+ break;
+ }
+
+ // Make some space for the new carts
+ mCartBank0 = new uint8[mMaskBank0+1];
+ mCartBank1 = new uint8[mMaskBank1+1];
+
+ // Set default bank
+ mBank=bank0;
+
+ // Initialiase
+ std::memset(mCartBank0, DEFAULT_CART_CONTENTS, mMaskBank0 + 1);
+ std::memset(mCartBank1, DEFAULT_CART_CONTENTS, mMaskBank1 + 1);
+
+ // Read in the BANK0 bytes
+ if(mMaskBank0)
+ {
+ int size = std::min(gamesize, mMaskBank0+1);
+ std::memcpy(mCartBank0, gamedata, size);
+ gamedata += size;
+ gamesize -= size;
+ }
+
+ // Read in the BANK1 bytes
+ if(mMaskBank1)
+ {
+ int size = std::min(gamesize, mMaskBank1+1);
+ std::memcpy(mCartBank1, gamedata, size);
+ gamedata += size;
+ }
+
+ // As this is a cartridge boot unset the boot address
+ // mSystem.gCPUBootAddress=0;
+
+ // Dont allow an empty Bank1 - Use it for shadow SRAM/EEPROM
+ if(banktype1==UNUSED)
+ {
+ // Delete the single byte allocated earlier
+ delete[] mCartBank1;
+ // Allocate some new memory for us
+ banktype1=C64K;
+ mMaskBank1=0x00ffff;
+ mShiftCount1=8;
+ mCountMask1=0x0ff;
+ mCartBank1 = (uint8*) new uint8[mMaskBank1+1];
+ std::memset(mCartBank1, DEFAULT_RAM_CONTENTS, mMaskBank1 + 1);
+ mWriteEnableBank1=TRUE;
+ mCartRAM=TRUE;
+ }
+}
+
+CCart::~CCart()
+{
+ delete[] mCartBank0;
+ delete[] mCartBank1;
+}
+
+
+void CCart::Reset()
+{
+ mCounter = 0;
+ mShifter = 0;
+ mAddrData = 0;
+ mStrobe = 0;
+ last_strobe = 0;
+}
+
+INLINE void CCart::Poke(uint32 addr, uint8 data)
+{
+ if(mBank==bank0)
+ {
+ if(mWriteEnableBank0)
+ mCartBank0[addr&mMaskBank0]=data;
+ }
+ else
+ {
+ if(mWriteEnableBank1)
+ mCartBank1[addr&mMaskBank1]=data;
+ }
+}
+
+
+INLINE uint8 CCart::Peek(uint32 addr)
+{
+ if(mBank==bank0)
+ {
+ return(mCartBank0[addr&mMaskBank0]);
+ }
+ else
+ {
+ return(mCartBank1[addr&mMaskBank1]);
+ }
+}
+
+
+void CCart::CartAddressStrobe(bool strobe)
+{
+ mStrobe=strobe;
+
+ if(mStrobe) mCounter=0;
+
+ //
+ // Either of the two below seem to work OK.
+ //
+ // if(!strobe && last_strobe)
+ //
+ if(mStrobe && !last_strobe)
+ {
+ // Clock a bit into the shifter
+ mShifter=mShifter<<1;
+ mShifter+=mAddrData?1:0;
+ mShifter&=0xff;
+ }
+ last_strobe=mStrobe;
+}
+
+void CCart::CartAddressData(bool data)
+{
+ mAddrData=data;
+}
+
+
+void CCart::Poke0(uint8 data)
+{
+ if(mWriteEnableBank0)
+ {
+ uint32 address=(mShifter<
+
+#include "system.h"
+
+void *operator new(std::size_t n)
+{
+ void *p = std::malloc(n);
+ std::memset(p, 0, n);
+ return p;
+}
+
+void operator delete(void *p)
+{
+ std::free(p);
+}
+
+#define EXPORT extern "C" __declspec(dllexport)
+
+EXPORT CSystem *Create(const uint8 *game, uint32 gamesize, const uint8 *bios, uint32 biossize, int pagesize0, int pagesize1, int lowpass)
+{
+ return new CSystem(game, gamesize, bios, biossize, pagesize0, pagesize1, lowpass);
+}
+
+EXPORT void Destroy(CSystem *s)
+{
+ delete s;
+}
+
+EXPORT void Reset(CSystem *s)
+{
+ s->Reset();
+}
+
+EXPORT void Advance(CSystem *s, int buttons, uint32 *vbuff, int16 *sbuff, int *sbuffsize)
+{
+ s->Advance(buttons, vbuff, sbuff, *sbuffsize);
+}
diff --git a/lynx/license.txt b/lynx/license.txt
new file mode 100644
index 0000000000..7450f6a02f
--- /dev/null
+++ b/lynx/license.txt
@@ -0,0 +1,21 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
\ No newline at end of file
diff --git a/lynx/lynxbase.h b/lynx/lynxbase.h
new file mode 100644
index 0000000000..61ae13b1df
--- /dev/null
+++ b/lynx/lynxbase.h
@@ -0,0 +1,56 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//
+// Generic Lynx base class.
+//
+
+#ifndef LYNXBASE_H
+#define LYNXBASE_H
+
+//
+// bank0 - Cartridge bank 0
+// bank1 - Cartridge bank 1
+// ram - all ram
+// cpu - system memory as viewed by the cpu
+//
+enum EMMODE {bank0,bank1,ram,cpu};
+
+class CLynxBase
+{
+ // Function members
+
+public:
+ virtual ~CLynxBase() {};
+
+public:
+ virtual void Reset(void) {};
+
+ virtual void Poke(uint32 addr,uint8 data)=0;
+ virtual uint8 Peek(uint32 addr)=0;
+ virtual void PokeW(uint32 addr,uint16 data) {}; // ONLY mSystem overloads these, they are never use by the clients
+ virtual uint16 PeekW(uint32 addr) {return 0;};
+ virtual void BankSelect(EMMODE newbank){};
+ virtual uint32 ObjectSize(void) {return 1;};
+
+};
+#endif
diff --git a/lynx/lynxdef.h b/lynx/lynxdef.h
new file mode 100644
index 0000000000..f8b3d62b3e
--- /dev/null
+++ b/lynx/lynxdef.h
@@ -0,0 +1,287 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// Generic lyynx definition header file //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This header file provides the definition of all of the useful hardware //
+// addreses within the Lynx. //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#define TMPADR 0xfc00
+#define TMPADRL 0xfc00
+#define TMPADRH 0xfc01
+#define TILTACUM 0xfc02
+#define TILTACUML 0xfc02
+#define TILTACUMH 0xfc03
+#define HOFF 0xfc04
+#define HOFFL 0xfc04
+#define HOFFH 0xfc05
+#define VOFF 0xfc06
+#define VOFFL 0xfc06
+#define VOFFH 0xfc07
+#define VIDBAS 0xfc08
+#define VIDBASL 0xfc08
+#define VIDBASH 0xfc09
+#define COLLBAS 0xfc0a
+#define COLLBASL 0xfc0a
+#define COLLBASH 0xfc0b
+#define VIDADR 0xfc0c
+#define VIDADRL 0xfc0c
+#define VIDADRH 0xfc0d
+#define COLLADR 0xfc0e
+#define COLLADRL 0xfc0e
+#define COLLADRH 0xfc0f
+#define SCBNEXT 0xfc10
+#define SCBNEXTL 0xfc10
+#define SCBNEXTH 0xfc11
+#define SPRDLINE 0xfc12
+#define SPRDLINEL 0xfc12
+#define SPRDLINEH 0xfc13
+#define HPOSSTRT 0xfc14
+#define HPOSSTRTL 0xfc14
+#define HPOSSTRTH 0xfc15
+#define VPOSSTRT 0xfc16
+#define VPOSSTRTL 0xfc16
+#define VPOSSTRTH 0xfc17
+#define SPRHSIZ 0xfc18
+#define SPRHSIZL 0xfc18
+#define SPRHSIZH 0xfc19
+#define SPRVSIZ 0xfc1a
+#define SPRVSIZL 0xfc1a
+#define SPRVSIZH 0xfc1b
+#define STRETCH 0xfc1c
+#define STRETCHL 0xfc1c
+#define STRETCHH 0xfc1d
+#define TILT 0xfc1e
+#define TILTL 0xfc1e
+#define TILTH 0xfc1f
+#define SPRDOFF 0xfc20
+#define SPRDOFFL 0xfc20
+#define SPRDOFFH 0xfc21
+#define SPRVPOS 0xfc22
+#define SPRVPOSL 0xfc22
+#define SPRVPOSH 0xfc23
+#define COLLOFF 0xfc24
+#define COLLOFFL 0xfc24
+#define COLLOFFH 0xfc25
+#define VSIZACUM 0xfc26
+#define VSIZACUML 0xfc26
+#define VSIZACUMH 0xfc27
+#define HSIZOFF 0xfc28
+#define HSIZOFFL 0xfc28
+#define HSIZOFFH 0xfc29
+#define VSIZOFF 0xfc2a
+#define VSIZOFFL 0xfc2a
+#define VSIZOFFH 0xfc2b
+#define SCBADR 0xfc2c
+#define SCBADRL 0xfc2c
+#define SCBADRH 0xfc2d
+#define PROCADR 0xfc2e
+#define PROCADRL 0xfc2e
+#define PROCADRH 0xfc2f
+#define MATHD 0xfc52
+#define MATHC 0xfc53
+#define MATHB 0xfc54
+#define MATHA 0xfc55
+#define MATHP 0xfc56
+#define MATHN 0xfc57
+#define MATHH 0xfc60
+#define MATHG 0xfc61
+#define MATHF 0xfc62
+#define MATHE 0xfc63
+#define MATHM 0xfc6c
+#define MATHL 0xfc6d
+#define MATHK 0xfc6e
+#define MATHJ 0xfc6f
+#define SPRCTL0 0xfc80
+#define SPRCTL1 0xfc81
+#define SPRCOLL 0xfc82
+#define SPRINIT 0xfc83
+#define SUZYHREV 0xfc88
+#define SUZYSREV 0xfc89
+#define SUZYBUSEN 0xfc90
+#define SPRGO 0xfc91
+#define SPRSYS 0xfc92
+#define JOYSTICK 0xfcb0
+#define SWITCHES 0xfcb1
+#define RCART0 0xfcb2
+#define RCART1 0xfcb3
+#define LEDS 0xfcc0
+#define PPORTSTAT 0xfcc2
+#define PPORTDATA 0xfcc3
+#define HOWIE 0xfcc4
+#define TIM0BKUP 0xfd00
+#define TIM0CTLA 0xfd01
+#define TIM0CNT 0xfd02
+#define TIM0CTLB 0xfd03
+#define TIM1BKUP 0xfd04
+#define TIM1CTLA 0xfd05
+#define TIM1CNT 0xfd06
+#define TIM1CTLB 0xfd07
+#define TIM2BKUP 0xfd08
+#define TIM2CTLA 0xfd09
+#define TIM2CNT 0xfd0a
+#define TIM2CTLB 0xfd0b
+#define TIM3BKUP 0xfd0c
+#define TIM3CTLA 0xfd0d
+#define TIM3CNT 0xfd0e
+#define TIM3CTLB 0xfd0f
+#define TIM4BKUP 0xfd10
+#define TIM4CTLA 0xfd11
+#define TIM4CNT 0xfd12
+#define TIM4CTLB 0xfd13
+#define TIM5BKUP 0xfd14
+#define TIM5CTLA 0xfd15
+#define TIM5CNT 0xfd16
+#define TIM5CTLB 0xfd17
+#define TIM6BKUP 0xfd18
+#define TIM6CTLA 0xfd19
+#define TIM6CNT 0xfd1a
+#define TIM6CTLB 0xfd1b
+#define TIM7BKUP 0xfd1c
+#define TIM7CTLA 0xfd1d
+#define TIM7CNT 0xfd1e
+#define TIM7CTLB 0xfd1f
+
+#define AUD0VOL 0xfd20
+#define AUD0SHFTFB 0xfd21
+#define AUD0OUTVAL 0xfd22
+#define AUD0L8SHFT 0xfd23
+#define AUD0TBACK 0xfd24
+#define AUD0CTL 0xfd25
+#define AUD0COUNT 0xfd26
+#define AUD0MISC 0xfd27
+
+#define AUD1VOL 0xfd28
+#define AUD1SHFTFB 0xfd29
+#define AUD1OUTVAL 0xfd2a
+#define AUD1L8SHFT 0xfd2b
+#define AUD1TBACK 0xfd2c
+#define AUD1CTL 0xfd2d
+#define AUD1COUNT 0xfd2e
+#define AUD1MISC 0xfd2f
+
+#define AUD2VOL 0xfd30
+#define AUD2SHFTFB 0xfd31
+#define AUD2OUTVAL 0xfd32
+#define AUD2L8SHFT 0xfd33
+#define AUD2TBACK 0xfd34
+#define AUD2CTL 0xfd35
+#define AUD2COUNT 0xfd36
+#define AUD2MISC 0xfd37
+
+#define AUD3VOL 0xfd38
+#define AUD3SHFTFB 0xfd39
+#define AUD3OUTVAL 0xfd3a
+#define AUD3L8SHFT 0xfd3b
+#define AUD3TBACK 0xfd3c
+#define AUD3CTL 0xfd3d
+#define AUD3COUNT 0xfd3e
+#define AUD3MISC 0xfd3f
+
+#define ATTEN_A 0xFD40 //
+#define ATTEN_B 0xFD41
+#define ATTEN_C 0xFD42 // Lynx2 Regs see macros/handy.equ
+#define ATTEN_D 0xFD43
+#define MPAN 0xFD44 //
+#define MSTEREO 0xfd50
+#define INTRST 0xfd80
+#define INTSET 0xfd81
+#define MAGRDY0 0xfd84
+#define MAGRDY1 0xfd85
+#define AUDIN 0xfd86
+#define SYSCTL1 0xfd87
+#define MIKEYHREV 0xfd88
+#define MIKEYSREV 0xfd89
+#define IODIR 0xfd8a
+#define IODAT 0xfd8b
+#define SERCTL 0xfd8c
+#define SERDAT 0xfd8d
+#define SDONEACK 0xfd90
+#define CPUSLEEP 0xfd91
+#define DISPCTL 0xfd92
+#define PBKUP 0xfd93
+#define DISPADR 0xfd94
+#define DISPADRL 0xfd94
+#define DISPADRH 0xfd95
+#define Mtest0 0xfd9c
+#define Mtest1 0xfd9d
+#define Mtest2 0xfd9e
+#define GREEN0 0xfda0
+#define GREEN1 0xfda1
+#define GREEN2 0xfda2
+#define GREEN3 0xfda3
+#define GREEN4 0xfda4
+#define GREEN5 0xfda5
+#define GREEN6 0xfda6
+#define GREEN7 0xfda7
+#define GREEN8 0xfda8
+#define GREEN9 0xfda9
+#define GREENA 0xfdaa
+#define GREENB 0xfdab
+#define GREENC 0xfdac
+#define GREEND 0xfdad
+#define GREENE 0xfdae
+#define GREENF 0xfdaf
+#define BLUERED0 0xfdb0
+#define BLUERED1 0xfdb1
+#define BLUERED2 0xfdb2
+#define BLUERED3 0xfdb3
+#define BLUERED4 0xfdb4
+#define BLUERED5 0xfdb5
+#define BLUERED6 0xfdb6
+#define BLUERED7 0xfdb7
+#define BLUERED8 0xfdb8
+#define BLUERED9 0xfdb9
+#define BLUEREDA 0xfdba
+#define BLUEREDB 0xfdbb
+#define BLUEREDC 0xfdbc
+#define BLUEREDD 0xfdbd
+#define BLUEREDE 0xfdbe
+#define BLUEREDF 0xfdbf
+#define MMAPCTL 0xfff9
+#define CPUNMI 0xfffa
+#define CPUNMIL 0xfffa
+#define CPUNMIH 0xfffb
+#define CPURESET 0xfffc
+#define CPURESETL 0xfffc
+#define CPURESETH 0xfffd
+#define CPUINT 0xfffe
+#define CPUINTL 0xfffe
+#define CPUINTH 0xffff
+
diff --git a/lynx/machine.h b/lynx/machine.h
new file mode 100644
index 0000000000..fd5c811629
--- /dev/null
+++ b/lynx/machine.h
@@ -0,0 +1,65 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// Core machine definitions header file //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This header file provides the interface definition and code for the core //
+// definitions used throughout the Handy code. Additionally it provides //
+// a generic memory object definition. //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef MACHINE_H
+#define MACHINE_H
+
+#include "mednafen.h"
+
+// Read/Write Cycle definitions
+#define CPU_RDWR_CYC 5
+#define DMA_RDWR_CYC 4
+#define SPR_RDWR_CYC 3
+// Ammended to 2 on 28/04/00, 16Mhz = 62.5nS cycle
+//
+// 2 cycles is 125ns - PAGE MODE CYCLE
+// 4 cycles is 250ns - NORMAL MODE CYCLE
+//
+
+#include "lynxbase.h"
+
+#endif
+
+
diff --git a/lynx/mednafen.h b/lynx/mednafen.h
new file mode 100644
index 0000000000..b985ac03a0
--- /dev/null
+++ b/lynx/mednafen.h
@@ -0,0 +1,58 @@
+#ifndef MEDNAFEN_H
+#define MEDNAFEN_H
+
+#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;
+
+#define MDFN_COLD
+#define FALSE 0
+#define TRUE 1
+#define INLINE
+
+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;
+
+#endif
diff --git a/lynx/memmap.cpp b/lynx/memmap.cpp
new file mode 100644
index 0000000000..738e3e3eec
--- /dev/null
+++ b/lynx/memmap.cpp
@@ -0,0 +1,189 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// Lynx memory map class //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This class provides the register $FFF9 functionality to the emulator, it //
+// sets which devices can be seen by the CPU. //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#define MEMMAP_CPP
+
+//#include
+//#define TRACE_MEMMAP
+
+#include "system.h"
+#include "memmap.h"
+
+#include
+
+// IGNORE THIS TEXT, now overridden by new system
+//
+// We will hold 16 different memory maps for the "top" area which are selected
+// on the basis of mMemMap->mSelector:
+//
+// Code Vect ROM Mikie Susie
+//----------------------------------------------------
+// (Default) 0000 V R M S
+// 0001 V R M RAM
+// 0001 V R RAM S
+// 0011 V R RAM RAM
+// 0100 V RAM M S
+// ..
+// ..
+// 1111 RAM RAM RAM RAM
+//
+// Get it.....
+//
+// We can then index with mMemoryHandlers[mMemMap->mSelector][addr] for speed
+//
+
+CMemMap::CMemMap(CSystem& parent)
+ :mSystem(parent)
+{
+ Reset();
+}
+
+
+void CMemMap::Reset(void)
+{
+
+ // Initialise ALL pointers to RAM then overload to correct
+ for(int loop=0;loopUpdate() doesnt have to call it //
+// every cycle, massive speedup but big complexity headache. //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#define MIKIE_CPP
+
+//#include
+//#define TRACE_MIKIE
+
+#include "system.h"
+#include "mikie.h"
+#include "lynxdef.h"
+
+/*
+void CMikie::BlowOut()
+{
+ C6502_REGS regs;
+ mSystem.GetRegs(regs);
+ //sprintf(addr,"Runtime Error - System Halted\nCMikie::Poke() - Read/Write to counter clocks at PC=$%04x.",regs.PC);
+ //gError->Warning(addr);
+ gSystemHalt=TRUE;
+}
+*/
+
+CMikie::CMikie(CSystem& parent)
+ :mSystem(parent)
+{
+ TRACE_MIKIE0("CMikie()");
+
+ int loop;
+ for(loop=0;loop<16;loop++) mPalette[loop].Index=loop;
+
+ DisplaySetAttributes();
+
+ Reset();
+}
+
+CMikie::~CMikie()
+{
+ TRACE_MIKIE0("~CMikie()");
+}
+
+void CMikie::SetCPUSleep() { mSystem.gSystemCPUSleep = true; }
+void CMikie::ClearCPUSleep() { mSystem.gSystemCPUSleep = false; }
+
+
+void CMikie::Reset()
+{
+ TRACE_MIKIE0("Reset()");
+
+ mAudioInputComparator=FALSE; // Initialises to unknown
+ mDisplayAddress=0x00; // Initialises to unknown
+ mLynxLine=0;
+ mLynxLineDMACounter=0;
+ mLynxAddr=0;
+
+ mpDisplayCurrentLine = 0;
+
+ mTimerStatusFlags=0x00; // Initialises to ZERO, i.e No IRQ's
+ mTimerInterruptMask=0x00;
+
+ mpRamPointer=mSystem.GetRamPointer(); // Fetch pointer to system RAM
+
+ mTIM_0_BKUP=0;
+ mTIM_0_ENABLE_RELOAD=0;
+ mTIM_0_ENABLE_COUNT=0;
+ mTIM_0_LINKING=0;
+ mTIM_0_CURRENT=0;
+ mTIM_0_TIMER_DONE=0;
+ mTIM_0_LAST_CLOCK=0;
+ mTIM_0_BORROW_IN=0;
+ mTIM_0_BORROW_OUT=0;
+ mTIM_0_LAST_LINK_CARRY=0;
+ mTIM_0_LAST_COUNT=0;
+
+ mTIM_1_BKUP=0;
+ mTIM_1_ENABLE_RELOAD=0;
+ mTIM_1_ENABLE_COUNT=0;
+ mTIM_1_LINKING=0;
+ mTIM_1_CURRENT=0;
+ mTIM_1_TIMER_DONE=0;
+ mTIM_1_LAST_CLOCK=0;
+ mTIM_1_BORROW_IN=0;
+ mTIM_1_BORROW_OUT=0;
+ mTIM_1_LAST_LINK_CARRY=0;
+ mTIM_1_LAST_COUNT=0;
+
+ mTIM_2_BKUP=0;
+ mTIM_2_ENABLE_RELOAD=0;
+ mTIM_2_ENABLE_COUNT=0;
+ mTIM_2_LINKING=0;
+ mTIM_2_CURRENT=0;
+ mTIM_2_TIMER_DONE=0;
+ mTIM_2_LAST_CLOCK=0;
+ mTIM_2_BORROW_IN=0;
+ mTIM_2_BORROW_OUT=0;
+ mTIM_2_LAST_LINK_CARRY=0;
+ mTIM_2_LAST_COUNT=0;
+
+ mTIM_3_BKUP=0;
+ mTIM_3_ENABLE_RELOAD=0;
+ mTIM_3_ENABLE_COUNT=0;
+ mTIM_3_LINKING=0;
+ mTIM_3_CURRENT=0;
+ mTIM_3_TIMER_DONE=0;
+ mTIM_3_LAST_CLOCK=0;
+ mTIM_3_BORROW_IN=0;
+ mTIM_3_BORROW_OUT=0;
+ mTIM_3_LAST_LINK_CARRY=0;
+ mTIM_3_LAST_COUNT=0;
+
+ mTIM_4_BKUP=0;
+ mTIM_4_ENABLE_RELOAD=0;
+ mTIM_4_ENABLE_COUNT=0;
+ mTIM_4_LINKING=0;
+ mTIM_4_CURRENT=0;
+ mTIM_4_TIMER_DONE=0;
+ mTIM_4_LAST_CLOCK=0;
+ mTIM_4_BORROW_IN=0;
+ mTIM_4_BORROW_OUT=0;
+ mTIM_4_LAST_LINK_CARRY=0;
+ mTIM_4_LAST_COUNT=0;
+
+ mTIM_5_BKUP=0;
+ mTIM_5_ENABLE_RELOAD=0;
+ mTIM_5_ENABLE_COUNT=0;
+ mTIM_5_LINKING=0;
+ mTIM_5_CURRENT=0;
+ mTIM_5_TIMER_DONE=0;
+ mTIM_5_LAST_CLOCK=0;
+ mTIM_5_BORROW_IN=0;
+ mTIM_5_BORROW_OUT=0;
+ mTIM_5_LAST_LINK_CARRY=0;
+ mTIM_5_LAST_COUNT=0;
+
+ mTIM_6_BKUP=0;
+ mTIM_6_ENABLE_RELOAD=0;
+ mTIM_6_ENABLE_COUNT=0;
+ mTIM_6_LINKING=0;
+ mTIM_6_CURRENT=0;
+ mTIM_6_TIMER_DONE=0;
+ mTIM_6_LAST_CLOCK=0;
+ mTIM_6_BORROW_IN=0;
+ mTIM_6_BORROW_OUT=0;
+ mTIM_6_LAST_LINK_CARRY=0;
+ mTIM_6_LAST_COUNT=0;
+
+ mTIM_7_BKUP=0;
+ mTIM_7_ENABLE_RELOAD=0;
+ mTIM_7_ENABLE_COUNT=0;
+ mTIM_7_LINKING=0;
+ mTIM_7_CURRENT=0;
+ mTIM_7_TIMER_DONE=0;
+ mTIM_7_LAST_CLOCK=0;
+ mTIM_7_BORROW_IN=0;
+ mTIM_7_BORROW_OUT=0;
+ mTIM_7_LAST_LINK_CARRY=0;
+ mTIM_7_LAST_COUNT=0;
+
+ for(int y = 0; y < 4; y++)
+ {
+ mAUDIO_BKUP[y]=0;
+ mAUDIO_ENABLE_RELOAD[y]=0;
+ mAUDIO_ENABLE_COUNT[y]=0;
+ mAUDIO_LINKING[y]=0;
+ mAUDIO_CURRENT[y]=0;
+ mAUDIO_TIMER_DONE[y]=0;
+ mAUDIO_LAST_CLOCK[y]=0;
+ mAUDIO_BORROW_IN[y]=0;
+ mAUDIO_BORROW_OUT[y]=0;
+ mAUDIO_LAST_LINK_CARRY[y]=0;
+ mAUDIO_LAST_COUNT[y]=0;
+ mAUDIO_VOLUME[y]=0;
+ mAUDIO_INTEGRATE_ENABLE[y]=0;
+ mAUDIO_WAVESHAPER[y]=0;
+
+ mAUDIO_OUTPUT[y] = 0;
+ }
+ mSTEREO=0xff; // xored! All channels enabled
+ mPAN=0x00; // all channels panning OFF
+ mAUDIO_ATTEN[0]=0xff; // Full volume
+ mAUDIO_ATTEN[1]=0xff;
+ mAUDIO_ATTEN[2]=0xff;
+ mAUDIO_ATTEN[3]=0xff;
+
+ // Start with an empty palette
+
+ for(int loop=0;loop<16;loop++)
+ {
+ mPalette[loop].Index=loop;
+ }
+
+ // Initialise IODAT register
+
+ mIODAT=0x00;
+ mIODIR=0x00;
+ mIODAT_REST_SIGNAL=0x00;
+
+ // Initialise display control register vars
+ mDISPCTL_DMAEnable=FALSE;
+ mDISPCTL_Flip=FALSE;
+ mDISPCTL_FourColour=0;
+ mDISPCTL_Colour=0;
+
+ // Initialise the UART variables
+ mUART_RX_IRQ_ENABLE=0;
+ mUART_TX_IRQ_ENABLE=0;
+
+ mUART_TX_COUNTDOWN=UART_TX_INACTIVE;
+ mUART_RX_COUNTDOWN=UART_RX_INACTIVE;
+
+ mUART_Rx_input_ptr=0;
+ mUART_Rx_output_ptr=0;
+ mUART_Rx_waiting=0;
+ mUART_Rx_framing_error=0;
+ mUART_Rx_overun_error=0;
+
+ mUART_SENDBREAK=0;
+ mUART_TX_DATA=0;
+ mUART_RX_DATA=0;
+ mUART_RX_READY=0;
+
+ mUART_PARITY_ENABLE=0;
+ mUART_PARITY_EVEN=0;
+}
+
+uint32 CMikie::GetLfsrNext(uint32 current)
+{
+ // The table is built thus:
+ // Bits 0-11 LFSR (12 Bits)
+ // Bits 12-20 Feedback switches (9 Bits)
+ // (Order = 7,0,1,2,3,4,5,10,11)
+ // Order is mangled to make peek/poke easier as
+ // bit 7 is in a seperate register
+ //
+ // Total 21 bits = 2MWords @ 4 Bytes/Word = 8MB !!!!!
+ //
+ // If the index is a combination of Current LFSR+Feedback the
+ // table will give the next value.
+
+ uint32 switches,lfsr,next,swloop,result;
+ static const uint32 switchbits[9]={7,0,1,2,3,4,5,10,11};
+
+ switches=current>>12;
+ lfsr=current&0xfff;
+ result=0;
+ for(swloop=0;swloop<9;swloop++)
+ {
+ if((switches>>swloop)&0x001) result^=(lfsr>>switchbits[swloop])&0x001;
+ }
+ result=(result)?0:1;
+ next=(switches<<12)|((lfsr<<1)&0xffe)|result;
+ return next;
+}
+
+void CMikie::ComLynxCable(int status)
+{
+ mUART_CABLE_PRESENT=status;
+}
+
+void CMikie::ComLynxRxData(int data)
+{
+ TRACE_MIKIE1("ComLynxRxData() - Received %04x",data);
+ // Copy over the data
+ if(mUART_Rx_waiting>4].Index];
+ bitmap_tmp++;
+ }
+ else
+ {
+ mLynxAddr++;
+ *bitmap_tmp = mColourMap[mPalette[source>>4].Index];
+ bitmap_tmp++;
+ *bitmap_tmp = mColourMap[mPalette[source&0x0f].Index];
+ bitmap_tmp++;
+ }
+ }
+}
+
+uint32 CMikie::DisplayRenderLine()
+{
+ uint32 work_done=0;
+
+ if(!mpDisplayCurrent) return 0;
+ if(!mDISPCTL_DMAEnable) return 0;
+ // if(mLynxLine&0x80000000) return 0;
+
+ // Set the timer interrupt flag
+ if(mTimerInterruptMask&0x01)
+ {
+ TRACE_MIKIE0("Update() - TIMER0 IRQ Triggered (Line Timer)");
+ mTimerStatusFlags|=0x01;
+ }
+
+ // Logic says it should be 101 but testing on an actual lynx shows the rest
+ // persiod is between lines 102,101,100 with the new line being latched at
+ // the beginning of count==99 hence the code below !!
+
+ // Emulate REST signal
+ if(mLynxLine==mTIM_2_BKUP-2 || mLynxLine==mTIM_2_BKUP-3 || mLynxLine==mTIM_2_BKUP-4) mIODAT_REST_SIGNAL=TRUE; else mIODAT_REST_SIGNAL=FALSE;
+
+ if(mLynxLine==(mTIM_2_BKUP-3))
+ {
+ if(mDISPCTL_Flip)
+ {
+ mLynxAddr=mDisplayAddress&0xfffc;
+ mLynxAddr+=3;
+ }
+ else
+ {
+ mLynxAddr=mDisplayAddress&0xfffc;
+ }
+ // Trigger line rending to start
+ mLynxLineDMACounter=102;
+ }
+
+ // Decrement line counter logic
+ if(mLynxLine) mLynxLine--;
+
+ // Do 102 lines, nothing more, less is OK.
+ if(mLynxLineDMACounter)
+ {
+ // TRACE_MIKIE1("Update() - Screen DMA, line %03d",line_count);
+ mLynxLineDMACounter--;
+
+ // Cycle hit for a 80 RAM access in rendering a line
+ work_done+=80*DMA_RDWR_CYC;
+
+ // Mikie screen DMA can only see the system RAM....
+ // (Step through bitmap, line at a time)
+
+ if (mpDisplayCurrentLine < 102)
+ {
+ CopyLineSurface();
+ }
+ else
+ {
+ // ??
+ printf("Lynx Line Overflow: %d\n", mpDisplayCurrentLine);
+ }
+
+ mpDisplayCurrentLine++;
+ }
+ return work_done;
+}
+
+uint32 CMikie::DisplayEndOfFrame()
+{
+ // Stop any further line rendering
+ mLynxLineDMACounter=0;
+ mLynxLine=mTIM_2_BKUP;
+
+ // Set the timer status flag
+ if(mTimerInterruptMask&0x04)
+ {
+ TRACE_MIKIE0("Update() - TIMER2 IRQ Triggered (Frame Timer)");
+ mTimerStatusFlags|=0x04;
+ }
+
+ // blank remaining lines and blit to output
+ while (mpDisplayCurrentLine < 102)
+ {
+ BlankLineSurface();
+ mpDisplayCurrentLine++;
+ }
+ std::memcpy(mpDisplayCurrent, framebuffer, sizeof(framebuffer));
+
+ mpDisplayCurrent = nullptr;
+ mpDisplayCurrentLine = 0;
+ return 0;
+}
+
+// Peek/Poke memory handlers
+
+void CMikie::Poke(uint32 addr,uint8 data)
+{
+ /* Sound register area */
+ if(addr >= 0xFD20 && addr <= 0xFD3F)
+ {
+ int which = (addr - 0xFD20) >> 3; // Each channel gets 8 ports/registers
+ switch(addr & 0x7)
+ {
+ case (AUD0VOL&0x7):
+ mAUDIO_VOLUME[which]=(int8)data;
+ TRACE_MIKIE2("Poke(AUD0VOL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+ case (AUD0SHFTFB&0x7):
+ mAUDIO_WAVESHAPER[which]&=0x001fff;
+ mAUDIO_WAVESHAPER[which]|=(uint32)data<<13;
+ TRACE_MIKIE2("Poke(AUD0SHFTB,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+ case (AUD0OUTVAL&0x7):
+ mAUDIO_OUTPUT[which]=data;
+ TRACE_MIKIE2("Poke(AUD0OUTVAL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+ case (AUD0L8SHFT&0x7):
+ mAUDIO_WAVESHAPER[which]&=0x1fff00;
+ mAUDIO_WAVESHAPER[which]|=data;
+ TRACE_MIKIE2("Poke(AUD0L8SHFT,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+ case (AUD0TBACK&0x7):
+ mAUDIO_BKUP[which]=data;
+ TRACE_MIKIE2("Poke(AUD0TBACK,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+ case (AUD0CTL&0x7):
+ mAUDIO_ENABLE_RELOAD[which]=data&0x10;
+ mAUDIO_ENABLE_COUNT[which]=data&0x08;
+ mAUDIO_LINKING[which]=data&0x07;
+ mAUDIO_INTEGRATE_ENABLE[which]=data&0x20;
+ if(data&0x40) mAUDIO_TIMER_DONE[which]=0;
+ mAUDIO_WAVESHAPER[which]&=0x1fefff;
+ mAUDIO_WAVESHAPER[which]|=(data&0x80)?0x001000:0x000000;
+ if(data&0x48)
+ {
+ mAUDIO_LAST_COUNT[which]=mSystem.gSystemCycleCount;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ }
+ TRACE_MIKIE2("Poke(AUD0CTL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+ case (AUD0COUNT&0x7):
+ mAUDIO_CURRENT[which]=data;
+ TRACE_MIKIE2("Poke(AUD0COUNT,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+ case (AUD0MISC&0x7):
+ mAUDIO_WAVESHAPER[which]&=0x1ff0ff;
+ mAUDIO_WAVESHAPER[which]|=(data&0xf0)<<4;
+ mAUDIO_BORROW_IN[which]=data&0x02;
+ mAUDIO_BORROW_OUT[which]=data&0x01;
+ mAUDIO_LAST_CLOCK[which]=data&0x04;
+ TRACE_MIKIE2("Poke(AUD0MISC,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+ }
+ }
+ else switch(addr&0xff)
+ {
+ case (TIM0BKUP&0xff):
+ mTIM_0_BKUP=data;
+ TRACE_MIKIE2("Poke(TIM0BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM1BKUP&0xff):
+ mTIM_1_BKUP=data;
+ TRACE_MIKIE2("Poke(TIM1BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM2BKUP&0xff):
+ mTIM_2_BKUP=data;
+ TRACE_MIKIE2("Poke(TIM2BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM3BKUP&0xff):
+ mTIM_3_BKUP=data;
+ TRACE_MIKIE2("Poke(TIM3BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM4BKUP&0xff):
+ mTIM_4_BKUP=data;
+ TRACE_MIKIE2("Poke(TIM4BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM5BKUP&0xff):
+ mTIM_5_BKUP=data;
+ TRACE_MIKIE2("Poke(TIM5BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM6BKUP&0xff):
+ mTIM_6_BKUP=data;
+ TRACE_MIKIE2("Poke(TIM6BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM7BKUP&0xff):
+ mTIM_7_BKUP=data;
+ TRACE_MIKIE2("Poke(TIM7BKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+
+ case (TIM0CTLA&0xff):
+ mTimerInterruptMask&=(0x01^0xff);
+ mTimerInterruptMask|=(data&0x80)?0x01:0x00;
+ mTIM_0_ENABLE_RELOAD=data&0x10;
+ mTIM_0_ENABLE_COUNT=data&0x08;
+ mTIM_0_LINKING=data&0x07;
+ if(data&0x40) mTIM_0_TIMER_DONE=0;
+ if(data&0x48)
+ {
+ mTIM_0_LAST_COUNT=mSystem.gSystemCycleCount;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ }
+ TRACE_MIKIE2("Poke(TIM0CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM1CTLA&0xff):
+ mTimerInterruptMask&=(0x02^0xff);
+ mTimerInterruptMask|=(data&0x80)?0x02:0x00;
+ mTIM_1_ENABLE_RELOAD=data&0x10;
+ mTIM_1_ENABLE_COUNT=data&0x08;
+ mTIM_1_LINKING=data&0x07;
+ if(data&0x40) mTIM_1_TIMER_DONE=0;
+ if(data&0x48)
+ {
+ mTIM_1_LAST_COUNT=mSystem.gSystemCycleCount;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ }
+ TRACE_MIKIE2("Poke(TIM1CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM2CTLA&0xff):
+ mTimerInterruptMask&=(0x04^0xff);
+ mTimerInterruptMask|=(data&0x80)?0x04:0x00;
+ mTIM_2_ENABLE_RELOAD=data&0x10;
+ mTIM_2_ENABLE_COUNT=data&0x08;
+ mTIM_2_LINKING=data&0x07;
+ if(data&0x40) mTIM_2_TIMER_DONE=0;
+ if(data&0x48)
+ {
+ mTIM_2_LAST_COUNT=mSystem.gSystemCycleCount;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ }
+ TRACE_MIKIE2("Poke(TIM2CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM3CTLA&0xff):
+ mTimerInterruptMask&=(0x08^0xff);
+ mTimerInterruptMask|=(data&0x80)?0x08:0x00;
+ mTIM_3_ENABLE_RELOAD=data&0x10;
+ mTIM_3_ENABLE_COUNT=data&0x08;
+ mTIM_3_LINKING=data&0x07;
+ if(data&0x40) mTIM_3_TIMER_DONE=0;
+ if(data&0x48)
+ {
+ mTIM_3_LAST_COUNT=mSystem.gSystemCycleCount;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ }
+ TRACE_MIKIE2("Poke(TIM3CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM4CTLA&0xff):
+ // Timer 4 can never generate interrupts as its timer output is used
+ // to drive the UART clock generator
+ mTIM_4_ENABLE_RELOAD=data&0x10;
+ mTIM_4_ENABLE_COUNT=data&0x08;
+ mTIM_4_LINKING=data&0x07;
+ if(data&0x40) mTIM_4_TIMER_DONE=0;
+ if(data&0x48)
+ {
+ mTIM_4_LAST_COUNT=mSystem.gSystemCycleCount;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ }
+ TRACE_MIKIE2("Poke(TIM4CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM5CTLA&0xff):
+ mTimerInterruptMask&=(0x20^0xff);
+ mTimerInterruptMask|=(data&0x80)?0x20:0x00;
+ mTIM_5_ENABLE_RELOAD=data&0x10;
+ mTIM_5_ENABLE_COUNT=data&0x08;
+ mTIM_5_LINKING=data&0x07;
+ if(data&0x40) mTIM_5_TIMER_DONE=0;
+ if(data&0x48)
+ {
+ mTIM_5_LAST_COUNT=mSystem.gSystemCycleCount;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ }
+ TRACE_MIKIE2("Poke(TIM5CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM6CTLA&0xff):
+ mTimerInterruptMask&=(0x40^0xff);
+ mTimerInterruptMask|=(data&0x80)?0x40:0x00;
+ mTIM_6_ENABLE_RELOAD=data&0x10;
+ mTIM_6_ENABLE_COUNT=data&0x08;
+ mTIM_6_LINKING=data&0x07;
+ if(data&0x40) mTIM_6_TIMER_DONE=0;
+ if(data&0x48)
+ {
+ mTIM_6_LAST_COUNT=mSystem.gSystemCycleCount;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ }
+ TRACE_MIKIE2("Poke(TIM6CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM7CTLA&0xff):
+ mTimerInterruptMask&=(0x80^0xff);
+ mTimerInterruptMask|=(data&0x80)?0x80:0x00;
+ mTIM_7_ENABLE_RELOAD=data&0x10;
+ mTIM_7_ENABLE_COUNT=data&0x08;
+ mTIM_7_LINKING=data&0x07;
+ if(data&0x40) mTIM_7_TIMER_DONE=0;
+ if(data&0x48)
+ {
+ mTIM_7_LAST_COUNT=mSystem.gSystemCycleCount;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ }
+ TRACE_MIKIE2("Poke(TIM7CTLA,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+
+ case (TIM0CNT&0xff):
+ mTIM_0_CURRENT=data;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ TRACE_MIKIE2("Poke(TIM0CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM1CNT&0xff):
+ mTIM_1_CURRENT=data;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ TRACE_MIKIE2("Poke(TIM1CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM2CNT&0xff):
+ mTIM_2_CURRENT=data;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ TRACE_MIKIE2("Poke(TIM2CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM3CNT&0xff):
+ mTIM_3_CURRENT=data;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ TRACE_MIKIE2("Poke(TIM3CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM4CNT&0xff):
+ mTIM_4_CURRENT=data;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ TRACE_MIKIE2("Poke(TIM4CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM5CNT&0xff):
+ mTIM_5_CURRENT=data;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ TRACE_MIKIE2("Poke(TIM5CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM6CNT&0xff):
+ mTIM_6_CURRENT=data;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ TRACE_MIKIE2("Poke(TIM6CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TIM7CNT&0xff):
+ mTIM_7_CURRENT=data;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ TRACE_MIKIE2("Poke(TIM7CNT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+ case (TIM0CTLB&0xff):
+ mTIM_0_TIMER_DONE=data&0x08;
+ mTIM_0_LAST_CLOCK=data&0x04;
+ mTIM_0_BORROW_IN=data&0x02;
+ mTIM_0_BORROW_OUT=data&0x01;
+ TRACE_MIKIE2("Poke(TIM0CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ // BlowOut();
+ break;
+ case (TIM1CTLB&0xff):
+ mTIM_1_TIMER_DONE=data&0x08;
+ mTIM_1_LAST_CLOCK=data&0x04;
+ mTIM_1_BORROW_IN=data&0x02;
+ mTIM_1_BORROW_OUT=data&0x01;
+ TRACE_MIKIE2("Poke(TIM1CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ // BlowOut();
+ break;
+ case (TIM2CTLB&0xff):
+ mTIM_2_TIMER_DONE=data&0x08;
+ mTIM_2_LAST_CLOCK=data&0x04;
+ mTIM_2_BORROW_IN=data&0x02;
+ mTIM_2_BORROW_OUT=data&0x01;
+ TRACE_MIKIE2("Poke(TIM2CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ // BlowOut();
+ break;
+ case (TIM3CTLB&0xff):
+ mTIM_3_TIMER_DONE=data&0x08;
+ mTIM_3_LAST_CLOCK=data&0x04;
+ mTIM_3_BORROW_IN=data&0x02;
+ mTIM_3_BORROW_OUT=data&0x01;
+ TRACE_MIKIE2("Poke(TIM3CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ // BlowOut();
+ break;
+ case (TIM4CTLB&0xff):
+ mTIM_4_TIMER_DONE=data&0x08;
+ mTIM_4_LAST_CLOCK=data&0x04;
+ mTIM_4_BORROW_IN=data&0x02;
+ mTIM_4_BORROW_OUT=data&0x01;
+ TRACE_MIKIE2("Poke(TIM4CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ // BlowOut();
+ break;
+ case (TIM5CTLB&0xff):
+ mTIM_5_TIMER_DONE=data&0x08;
+ mTIM_5_LAST_CLOCK=data&0x04;
+ mTIM_5_BORROW_IN=data&0x02;
+ mTIM_5_BORROW_OUT=data&0x01;
+ TRACE_MIKIE2("Poke(TIM5CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ // BlowOut();
+ break;
+ case (TIM6CTLB&0xff):
+ mTIM_6_TIMER_DONE=data&0x08;
+ mTIM_6_LAST_CLOCK=data&0x04;
+ mTIM_6_BORROW_IN=data&0x02;
+ mTIM_6_BORROW_OUT=data&0x01;
+ TRACE_MIKIE2("Poke(TIM6CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ // BlowOut();
+ break;
+ case (TIM7CTLB&0xff):
+ mTIM_7_TIMER_DONE=data&0x08;
+ mTIM_7_LAST_CLOCK=data&0x04;
+ mTIM_7_BORROW_IN=data&0x02;
+ mTIM_7_BORROW_OUT=data&0x01;
+ TRACE_MIKIE2("Poke(TIM7CTLB ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ // BlowOut();
+ break;
+
+ case (ATTEN_A&0xff):
+ mAUDIO_ATTEN[0] = data;
+ TRACE_MIKIE2("Poke(ATTEN_A ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+ case (ATTEN_B&0xff):
+ mAUDIO_ATTEN[1] = data;
+ TRACE_MIKIE2("Poke(ATTEN_B ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+ case (ATTEN_C&0xff):
+ mAUDIO_ATTEN[2] = data;
+ TRACE_MIKIE2("Poke(ATTEN_C ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+ case (ATTEN_D&0xff):
+ mAUDIO_ATTEN[3] = data;
+ TRACE_MIKIE2("Poke(ATTEN_D ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+ case (MPAN&0xff):
+ TRACE_MIKIE2("Poke(MPAN ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ mPAN = data;
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+
+ case (MSTEREO&0xff):
+ TRACE_MIKIE2("Poke(MSTEREO ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ data^=0xff;
+ mSTEREO=data;
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ break;
+
+ case (INTRST&0xff):
+ data^=0xff;
+ mTimerStatusFlags&=data;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ TRACE_MIKIE2("Poke(INTRST ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+ case (INTSET&0xff):
+ TRACE_MIKIE2("Poke(INTSET ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ mTimerStatusFlags|=data;
+ mSystem.gNextTimerEvent=mSystem.gSystemCycleCount;
+ break;
+
+ case (SYSCTL1&0xff):
+ TRACE_MIKIE2("Poke(SYSCTL1 ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ if(!(data&0x02))
+ {
+ C6502_REGS regs;
+ mSystem.GetRegs(regs);
+ TRACE_MIKIE1("Runtime Alert - System Halted\nCMikie::Poke(SYSCTL1) - Lynx power down occured at PC=$%04x.\nResetting system.",regs.PC);
+ mSystem.Reset();
+ mSystem.gSystemHalt=TRUE;
+ }
+ mSystem.CartAddressStrobe((data&0x01)?TRUE:FALSE);
+ break;
+
+ case (MIKEYSREV&0xff):
+ TRACE_MIKIE2("Poke(MIKEYSREV,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+ case (IODIR&0xff):
+ TRACE_MIKIE2("Poke(IODIR ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ mIODIR=data;
+ break;
+
+ case (IODAT&0xff):
+ TRACE_MIKIE2("Poke(IODAT ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ mIODAT=data;
+ mSystem.CartAddressData((mIODAT&0x02)?TRUE:FALSE);
+ // Enable cart writes to BANK1 on AUDIN if AUDIN is set to output
+ if(mIODIR&0x10) mSystem.mCart->mWriteEnableBank1=(mIODAT&0x10)?TRUE:FALSE;
+ break;
+
+ case (SERCTL&0xff):
+ TRACE_MIKIE2("Poke(SERCTL ,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ mUART_TX_IRQ_ENABLE=(data&0x80)?true:false;
+ mUART_RX_IRQ_ENABLE=(data&0x40)?true:false;
+ mUART_PARITY_ENABLE=(data&0x10)?true:false;
+ mUART_SENDBREAK=data&0x02;
+ mUART_PARITY_EVEN=data&0x01;
+
+ // Reset all errors if required
+ if(data&0x08)
+ {
+ mUART_Rx_overun_error=0;
+ mUART_Rx_framing_error=0;
+ }
+
+ if(mUART_SENDBREAK)
+ {
+ // Trigger send break, it will self sustain as long as sendbreak is set
+ mUART_TX_COUNTDOWN=UART_TX_TIME_PERIOD;
+ // Loop back what we transmitted
+ ComLynxTxLoopback(UART_BREAK_CODE);
+ }
+ break;
+
+ case (SERDAT&0xff):
+ TRACE_MIKIE2("Poke(SERDAT ,%04x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ //
+ // Fake transmission, set counter to be decremented by Timer 4
+ //
+ // ComLynx only has one output pin, hence Rx & Tx are shorted
+ // therefore any transmitted data will loopback
+ //
+ mUART_TX_DATA=data;
+ // Calculate Parity data
+ if(mUART_PARITY_ENABLE)
+ {
+ // Calc parity value
+ // Leave at zero !!
+ }
+ else
+ {
+ // If disabled then the PAREVEN bit is sent
+ if(mUART_PARITY_EVEN) data|=0x0100;
+ }
+ // Set countdown to transmission
+ mUART_TX_COUNTDOWN=UART_TX_TIME_PERIOD;
+ // Loop back what we transmitted
+ ComLynxTxLoopback(mUART_TX_DATA);
+ break;
+
+ case (SDONEACK&0xff):
+ TRACE_MIKIE2("Poke(SDONEACK,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (CPUSLEEP&0xff):
+ mSystem.gSuzieDoneTime = mSystem.gSystemCycleCount+mSystem.PaintSprites();
+ SetCPUSleep();
+ break;
+
+ case (DISPCTL&0xff):
+ TRACE_MIKIE2("Poke(DISPCTL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ {
+ TDISPCTL tmp;
+ tmp.Byte=data;
+ mDISPCTL_DMAEnable=tmp.Bits.DMAEnable;
+ mDISPCTL_Flip=tmp.Bits.Flip;
+ mDISPCTL_FourColour=tmp.Bits.FourColour;
+ mDISPCTL_Colour=tmp.Bits.Colour;
+ }
+ break;
+ case (PBKUP&0xff):
+ TRACE_MIKIE2("Poke(PBKUP,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+ case (DISPADRL&0xff):
+ TRACE_MIKIE2("Poke(DISPADRL,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ mDisplayAddress&=0xff00;
+ mDisplayAddress+=data;
+ break;
+
+ case (DISPADRH&0xff):
+ TRACE_MIKIE2("Poke(DISPADRH,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ mDisplayAddress&=0x00ff;
+ mDisplayAddress+=(data<<8);
+ break;
+
+ case (Mtest0&0xff):
+ case (Mtest1&0xff):
+ // Test registers are unimplemented
+ // lets hope no programs use them.
+ TRACE_MIKIE2("Poke(MTEST0/1,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ case (Mtest2&0xff):
+ // Test registers are unimplemented
+ // lets hope no programs use them.
+ //gError->Warning("CMikie::Poke() - Write to MTEST2");
+ TRACE_MIKIE2("Poke(MTEST2,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+ case (GREEN0&0xff):
+ case (GREEN1&0xff):
+ case (GREEN2&0xff):
+ case (GREEN3&0xff):
+ case (GREEN4&0xff):
+ case (GREEN5&0xff):
+ case (GREEN6&0xff):
+ case (GREEN7&0xff):
+ case (GREEN8&0xff):
+ case (GREEN9&0xff):
+ case (GREENA&0xff):
+ case (GREENB&0xff):
+ case (GREENC&0xff):
+ case (GREEND&0xff):
+ case (GREENE&0xff):
+ case (GREENF&0xff):
+ TRACE_MIKIE2("Poke(GREENPAL0-F,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ mPalette[addr&0x0f].Colours.Green=data&0x0f;
+ break;
+
+ case (BLUERED0&0xff):
+ case (BLUERED1&0xff):
+ case (BLUERED2&0xff):
+ case (BLUERED3&0xff):
+ case (BLUERED4&0xff):
+ case (BLUERED5&0xff):
+ case (BLUERED6&0xff):
+ case (BLUERED7&0xff):
+ case (BLUERED8&0xff):
+ case (BLUERED9&0xff):
+ case (BLUEREDA&0xff):
+ case (BLUEREDB&0xff):
+ case (BLUEREDC&0xff):
+ case (BLUEREDD&0xff):
+ case (BLUEREDE&0xff):
+ case (BLUEREDF&0xff):
+ TRACE_MIKIE2("Poke(BLUEREDPAL0-F,%02x) at PC=%04x",data,mSystem.mCpu->GetPC());
+ mPalette[addr&0x0f].Colours.Blue=(data&0xf0)>>4;
+ mPalette[addr&0x0f].Colours.Red=data&0x0f;
+ break;
+
+ // Errors on read only register accesses
+
+ case (MAGRDY0&0xff):
+ case (MAGRDY1&0xff):
+ case (AUDIN&0xff):
+ case (MIKEYHREV&0xff):
+ TRACE_MIKIE3("Poke(%04x,%02x) - Poke to read only register location at PC=%04x",addr,data,mSystem.mCpu->GetPC());
+ break;
+
+ // Errors on illegal location accesses
+
+ default:
+ TRACE_MIKIE3("Poke(%04x,%02x) - Poke to illegal location at PC=%04x",addr,data,mSystem.mCpu->GetPC());
+ break;
+ }
+}
+
+
+
+uint8 CMikie::Peek(uint32 addr)
+{
+ /* Sound register area */
+ if(addr >= 0xFD20 && addr <= 0xFD3F)
+ {
+ int which = (addr - 0xFD20) >> 3; // Each channel gets 8 ports/registers
+ switch(addr & 0x7)
+ {
+ case (AUD0VOL&0x7):
+ return (uint8)mAUDIO_VOLUME[which];
+ break;
+ case (AUD0SHFTFB&0x7):
+ return (uint8)((mAUDIO_WAVESHAPER[which]>>13)&0xff);
+ break;
+ case (AUD0OUTVAL&0x7):
+ return (uint8)mAUDIO_OUTPUT[which];
+ break;
+ case (AUD0L8SHFT&0x7):
+ return (uint8)(mAUDIO_WAVESHAPER[which]&0xff);
+ break;
+ case (AUD0TBACK&0x7):
+ return (uint8)mAUDIO_BKUP[which];
+ break;
+ case (AUD0CTL&0x7):
+ {
+ uint8 retval=0;
+ retval|=(mAUDIO_INTEGRATE_ENABLE[which])?0x20:0x00;
+ retval|=(mAUDIO_ENABLE_RELOAD[which])?0x10:0x00;
+ retval|=(mAUDIO_ENABLE_COUNT[which])?0x08:0x00;
+ retval|=(mAUDIO_WAVESHAPER[which]&0x001000)?0x80:0x00;
+ retval|=mAUDIO_LINKING[which];
+ return retval;
+ }
+ break;
+ case (AUD0COUNT&0x7):
+ return (uint8)mAUDIO_CURRENT[which];
+ break;
+ case (AUD0MISC&0x7):
+ {
+ uint8 retval=0;
+ retval|=(mAUDIO_BORROW_OUT[which])?0x01:0x00;
+ retval|=(mAUDIO_BORROW_IN[which])?0x02:0x00;
+ retval|=(mAUDIO_LAST_CLOCK[which])?0x08:0x00;
+ retval|=(mAUDIO_WAVESHAPER[which]>>4)&0xf0;
+ return retval;
+ }
+ break;
+ }
+ }
+ else switch(addr&0xff)
+ {
+
+ // Timer control registers
+
+ case (TIM0BKUP&0xff):
+ TRACE_MIKIE2("Peek(TIM0KBUP ,%02x) at PC=%04x",mTIM_0_BKUP,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_0_BKUP;
+ break;
+ case (TIM1BKUP&0xff):
+ TRACE_MIKIE2("Peek(TIM1KBUP ,%02x) at PC=%04x",mTIM_1_BKUP,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_1_BKUP;
+ break;
+ case (TIM2BKUP&0xff):
+ TRACE_MIKIE2("Peek(TIM2KBUP ,%02x) at PC=%04x",mTIM_2_BKUP,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_2_BKUP;
+ break;
+ case (TIM3BKUP&0xff):
+ TRACE_MIKIE2("Peek(TIM3KBUP ,%02x) at PC=%04x",mTIM_3_BKUP,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_3_BKUP;
+ break;
+ case (TIM4BKUP&0xff):
+ TRACE_MIKIE2("Peek(TIM4KBUP ,%02x) at PC=%04x",mTIM_4_BKUP,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_4_BKUP;
+ break;
+ case (TIM5BKUP&0xff):
+ TRACE_MIKIE2("Peek(TIM5KBUP ,%02x) at PC=%04x",mTIM_5_BKUP,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_5_BKUP;
+ break;
+ case (TIM6BKUP&0xff):
+ TRACE_MIKIE2("Peek(TIM6KBUP ,%02x) at PC=%04x",mTIM_6_BKUP,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_6_BKUP;
+ break;
+ case (TIM7BKUP&0xff):
+ TRACE_MIKIE2("Peek(TIM7KBUP ,%02x) at PC=%04x",mTIM_7_BKUP,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_7_BKUP;
+ break;
+
+ case (TIM0CTLA&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTimerInterruptMask&0x01)?0x80:0x00;
+ retval|=(mTIM_0_ENABLE_RELOAD)?0x10:0x00;
+ retval|=(mTIM_0_ENABLE_COUNT)?0x08:0x00;
+ retval|=mTIM_0_LINKING;
+ TRACE_MIKIE2("Peek(TIM0CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ break;
+ case (TIM1CTLA&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTimerInterruptMask&0x02)?0x80:0x00;
+ retval|=(mTIM_1_ENABLE_RELOAD)?0x10:0x00;
+ retval|=(mTIM_1_ENABLE_COUNT)?0x08:0x00;
+ retval|=mTIM_1_LINKING;
+ TRACE_MIKIE2("Peek(TIM1CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ break;
+ case (TIM2CTLA&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTimerInterruptMask&0x04)?0x80:0x00;
+ retval|=(mTIM_2_ENABLE_RELOAD)?0x10:0x00;
+ retval|=(mTIM_2_ENABLE_COUNT)?0x08:0x00;
+ retval|=mTIM_2_LINKING;
+ TRACE_MIKIE2("Peek(TIM2CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ break;
+ case (TIM3CTLA&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTimerInterruptMask&0x08)?0x80:0x00;
+ retval|=(mTIM_3_ENABLE_RELOAD)?0x10:0x00;
+ retval|=(mTIM_3_ENABLE_COUNT)?0x08:0x00;
+ retval|=mTIM_3_LINKING;
+ TRACE_MIKIE2("Peek(TIM3CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ break;
+ case (TIM4CTLA&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTimerInterruptMask&0x10)?0x80:0x00;
+ retval|=(mTIM_4_ENABLE_RELOAD)?0x10:0x00;
+ retval|=(mTIM_4_ENABLE_COUNT)?0x08:0x00;
+ retval|=mTIM_4_LINKING;
+ TRACE_MIKIE2("Peek(TIM4CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ break;
+ case (TIM5CTLA&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTimerInterruptMask&0x20)?0x80:0x00;
+ retval|=(mTIM_5_ENABLE_RELOAD)?0x10:0x00;
+ retval|=(mTIM_5_ENABLE_COUNT)?0x08:0x00;
+ retval|=mTIM_5_LINKING;
+ TRACE_MIKIE2("Peek(TIM5CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ break;
+ case (TIM6CTLA&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTimerInterruptMask&0x40)?0x80:0x00;
+ retval|=(mTIM_6_ENABLE_RELOAD)?0x10:0x00;
+ retval|=(mTIM_6_ENABLE_COUNT)?0x08:0x00;
+ retval|=mTIM_6_LINKING;
+ TRACE_MIKIE2("Peek(TIM6CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ break;
+ case (TIM7CTLA&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTimerInterruptMask&0x80)?0x80:0x00;
+ retval|=(mTIM_7_ENABLE_RELOAD)?0x10:0x00;
+ retval|=(mTIM_7_ENABLE_COUNT)?0x08:0x00;
+ retval|=mTIM_7_LINKING;
+ TRACE_MIKIE2("Peek(TIM7CTLA ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ break;
+
+ case (TIM0CNT&0xff):
+ Update();
+ TRACE_MIKIE2("Peek(TIM0CNT ,%02x) at PC=%04x",mTIM_0_CURRENT,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_0_CURRENT;
+ break;
+ case (TIM1CNT&0xff):
+ Update();
+ TRACE_MIKIE2("Peek(TIM1CNT ,%02x) at PC=%04x",mTIM_1_CURRENT,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_1_CURRENT;
+ break;
+ case (TIM2CNT&0xff):
+ Update();
+ TRACE_MIKIE2("Peek(TIM2CNT ,%02x) at PC=%04x",mTIM_2_CURRENT,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_2_CURRENT;
+ break;
+ case (TIM3CNT&0xff):
+ Update();
+ TRACE_MIKIE2("Peek(TIM3CNT ,%02x) at PC=%04x",mTIM_3_CURRENT,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_3_CURRENT;
+ break;
+ case (TIM4CNT&0xff):
+ Update();
+ TRACE_MIKIE2("Peek(TIM4CNT ,%02x) at PC=%04x",mTIM_4_CURRENT,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_4_CURRENT;
+ break;
+ case (TIM5CNT&0xff):
+ Update();
+ TRACE_MIKIE2("Peek(TIM5CNT ,%02x) at PC=%04x",mTIM_5_CURRENT,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_5_CURRENT;
+ break;
+ case (TIM6CNT&0xff):
+ Update();
+ TRACE_MIKIE2("Peek(TIM6CNT ,%02x) at PC=%04x",mTIM_6_CURRENT,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_6_CURRENT;
+ break;
+ case (TIM7CNT&0xff):
+ Update();
+ TRACE_MIKIE2("Peek(TIM7CNT ,%02x) at PC=%04x",mTIM_7_CURRENT,mSystem.mCpu->GetPC());
+ return (uint8)mTIM_7_CURRENT;
+ break;
+
+ case (TIM0CTLB&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTIM_0_TIMER_DONE)?0x08:0x00;
+ retval|=(mTIM_0_LAST_CLOCK)?0x04:0x00;
+ retval|=(mTIM_0_BORROW_IN)?0x02:0x00;
+ retval|=(mTIM_0_BORROW_OUT)?0x01:0x00;
+ TRACE_MIKIE2("Peek(TIM0CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ // BlowOut();
+ break;
+ case (TIM1CTLB&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTIM_1_TIMER_DONE)?0x08:0x00;
+ retval|=(mTIM_1_LAST_CLOCK)?0x04:0x00;
+ retval|=(mTIM_1_BORROW_IN)?0x02:0x00;
+ retval|=(mTIM_1_BORROW_OUT)?0x01:0x00;
+ TRACE_MIKIE2("Peek(TIM1CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ // BlowOut();
+ break;
+ case (TIM2CTLB&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTIM_2_TIMER_DONE)?0x08:0x00;
+ retval|=(mTIM_2_LAST_CLOCK)?0x04:0x00;
+ retval|=(mTIM_2_BORROW_IN)?0x02:0x00;
+ retval|=(mTIM_2_BORROW_OUT)?0x01:0x00;
+ TRACE_MIKIE2("Peek(TIM2CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ // BlowOut();
+ break;
+ case (TIM3CTLB&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTIM_3_TIMER_DONE)?0x08:0x00;
+ retval|=(mTIM_3_LAST_CLOCK)?0x04:0x00;
+ retval|=(mTIM_3_BORROW_IN)?0x02:0x00;
+ retval|=(mTIM_3_BORROW_OUT)?0x01:0x00;
+ TRACE_MIKIE2("Peek(TIM3CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ // BlowOut();
+ break;
+ case (TIM4CTLB&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTIM_4_TIMER_DONE)?0x08:0x00;
+ retval|=(mTIM_4_LAST_CLOCK)?0x04:0x00;
+ retval|=(mTIM_4_BORROW_IN)?0x02:0x00;
+ retval|=(mTIM_4_BORROW_OUT)?0x01:0x00;
+ TRACE_MIKIE2("Peek(TIM4CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ // BlowOut();
+ break;
+ case (TIM5CTLB&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTIM_5_TIMER_DONE)?0x08:0x00;
+ retval|=(mTIM_5_LAST_CLOCK)?0x04:0x00;
+ retval|=(mTIM_5_BORROW_IN)?0x02:0x00;
+ retval|=(mTIM_5_BORROW_OUT)?0x01:0x00;
+ TRACE_MIKIE2("Peek(TIM5CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ // BlowOut();
+ break;
+ case (TIM6CTLB&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTIM_6_TIMER_DONE)?0x08:0x00;
+ retval|=(mTIM_6_LAST_CLOCK)?0x04:0x00;
+ retval|=(mTIM_6_BORROW_IN)?0x02:0x00;
+ retval|=(mTIM_6_BORROW_OUT)?0x01:0x00;
+ TRACE_MIKIE2("Peek(TIM6CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ // BlowOut();
+ break;
+ case (TIM7CTLB&0xff):
+ {
+ uint8 retval=0;
+ retval|=(mTIM_7_TIMER_DONE)?0x08:0x00;
+ retval|=(mTIM_7_LAST_CLOCK)?0x04:0x00;
+ retval|=(mTIM_7_BORROW_IN)?0x02:0x00;
+ retval|=(mTIM_7_BORROW_OUT)?0x01:0x00;
+ TRACE_MIKIE2("Peek(TIM7CTLB ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ }
+ // BlowOut();
+ break;
+
+ // Extra audio control registers
+
+ case (ATTEN_A&0xff):
+ TRACE_MIKIE1("Peek(ATTEN_A) at PC=%04x",mSystem.mCpu->GetPC());
+ return (uint8) mAUDIO_ATTEN[0];
+ break;
+ case (ATTEN_B&0xff):
+ TRACE_MIKIE1("Peek(ATTEN_B) at PC=%04x",mSystem.mCpu->GetPC());
+ return (uint8) mAUDIO_ATTEN[1];
+ break;
+ case (ATTEN_C&0xff):
+ TRACE_MIKIE1("Peek(ATTEN_C) at PC=%04x",mSystem.mCpu->GetPC());
+ return (uint8) mAUDIO_ATTEN[2];
+ break;
+ case (ATTEN_D&0xff):
+ TRACE_MIKIE1("Peek(ATTEN_D) at PC=%04x",mSystem.mCpu->GetPC());
+ return (uint8) mAUDIO_ATTEN[3];
+ break;
+ case (MPAN&0xff):
+ TRACE_MIKIE1("Peek(MPAN) at PC=%04x",mSystem.mCpu->GetPC());
+ return (uint8) mPAN;
+ break;
+
+ case (MSTEREO&0xff):
+ TRACE_MIKIE2("Peek(MSTEREO,%02x) at PC=%04x",(uint8)mSTEREO^0xff,mSystem.mCpu->GetPC());
+ return (uint8) mSTEREO^0xff;
+ break;
+
+ // Miscellaneous registers
+
+ case (SERCTL&0xff):
+ {
+ uint32 retval=0;
+ retval|=(mUART_TX_COUNTDOWN&UART_TX_INACTIVE)?0xA0:0x00; // Indicate TxDone & TxAllDone
+ retval|=(mUART_RX_READY)?0x40:0x00; // Indicate Rx data ready
+ retval|=(mUART_Rx_overun_error)?0x08:0x0; // Framing error
+ retval|=(mUART_Rx_framing_error)?0x04:0x00; // Rx overrun
+ retval|=(mUART_RX_DATA&UART_BREAK_CODE)?0x02:0x00; // Indicate break received
+ retval|=(mUART_RX_DATA&0x0100)?0x01:0x00; // Add parity bit
+ TRACE_MIKIE2("Peek(SERCTL ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return (uint8)retval;
+ }
+ break;
+
+ case (SERDAT&0xff):
+ mUART_RX_READY=0;
+ TRACE_MIKIE2("Peek(SERDAT ,%02x) at PC=%04x",(uint8)mUART_RX_DATA,mSystem.mCpu->GetPC());
+ return (uint8)(mUART_RX_DATA&0xff);
+ break;
+
+ case (IODAT&0xff):
+ {
+ uint32 retval=0;
+ retval|=(mIODIR&0x10)?mIODAT&0x10:0x10; // IODIR = output bit : input high (eeprom write done)
+ retval|=(mIODIR&0x08)?(((mIODAT&0x08)&&mIODAT_REST_SIGNAL)?0x00:0x08):0x00; // REST = output bit : input low
+ retval|=(mIODIR&0x04)?mIODAT&0x04:((mUART_CABLE_PRESENT)?0x04:0x00); // NOEXP = output bit : input low
+ retval|=(mIODIR&0x02)?mIODAT&0x02:0x00; // CARTAD = output bit : input low
+ retval|=(mIODIR&0x01)?mIODAT&0x01:0x01; // EXTPW = output bit : input high (Power connected)
+ TRACE_MIKIE2("Peek(IODAT ,%02x) at PC=%04x",retval,mSystem.mCpu->GetPC());
+ return (uint8)retval;
+ }
+ break;
+
+ case (INTRST&0xff):
+ case (INTSET&0xff):
+ TRACE_MIKIE2("Peek(INTSET ,%02x) at PC=%04x",mTimerStatusFlags,mSystem.mCpu->GetPC());
+ return (uint8)mTimerStatusFlags;
+ break;
+
+ case (MAGRDY0&0xff):
+ case (MAGRDY1&0xff):
+ TRACE_MIKIE2("Peek(MAGRDY0/1,%02x) at PC=%04x",0x00,mSystem.mCpu->GetPC());
+ return 0x00;
+ break;
+
+ case (AUDIN&0xff):
+ // TRACE_MIKIE2("Peek(AUDIN,%02x) at PC=%04x",mAudioInputComparator?0x80:0x00,mSystem.mCpu->GetPC());
+ // if(mAudioInputComparator) return 0x80; else return 0x00;
+ TRACE_MIKIE2("Peek(AUDIN,%02x) at PC=%04x",0x80,mSystem.mCpu->GetPC());
+ return 0x80;
+ break;
+
+ case (MIKEYHREV&0xff):
+ TRACE_MIKIE2("Peek(MIKEYHREV,%02x) at PC=%04x",0x01,mSystem.mCpu->GetPC());
+ return 0x01;
+ break;
+
+ // Pallette registers
+
+ case (GREEN0&0xff):
+ case (GREEN1&0xff):
+ case (GREEN2&0xff):
+ case (GREEN3&0xff):
+ case (GREEN4&0xff):
+ case (GREEN5&0xff):
+ case (GREEN6&0xff):
+ case (GREEN7&0xff):
+ case (GREEN8&0xff):
+ case (GREEN9&0xff):
+ case (GREENA&0xff):
+ case (GREENB&0xff):
+ case (GREENC&0xff):
+ case (GREEND&0xff):
+ case (GREENE&0xff):
+ case (GREENF&0xff):
+ TRACE_MIKIE2("Peek(GREENPAL0-F,%02x) at PC=%04x",mPalette[addr&0x0f].Colours.Green,mSystem.mCpu->GetPC());
+ return mPalette[addr&0x0f].Colours.Green;
+ break;
+
+ case (BLUERED0&0xff):
+ case (BLUERED1&0xff):
+ case (BLUERED2&0xff):
+ case (BLUERED3&0xff):
+ case (BLUERED4&0xff):
+ case (BLUERED5&0xff):
+ case (BLUERED6&0xff):
+ case (BLUERED7&0xff):
+ case (BLUERED8&0xff):
+ case (BLUERED9&0xff):
+ case (BLUEREDA&0xff):
+ case (BLUEREDB&0xff):
+ case (BLUEREDC&0xff):
+ case (BLUEREDD&0xff):
+ case (BLUEREDE&0xff):
+ case (BLUEREDF&0xff):
+ TRACE_MIKIE2("Peek(BLUEREDPAL0-F,%02x) at PC=%04x",(mPalette[addr&0x0f].Colours.Red | (mPalette[addr&0x0f].Colours.Blue<<4)),mSystem.mCpu->GetPC());
+ return (mPalette[addr&0x0f].Colours.Red | (mPalette[addr&0x0f].Colours.Blue<<4));
+ break;
+
+ // Errors on write only register accesses
+
+ // For easier debugging
+
+ case (DISPADRL&0xff):
+ TRACE_MIKIE2("Peek(DISPADRL,%02x) at PC=%04x",(uint8)(mDisplayAddress&0xff),mSystem.mCpu->GetPC());
+ return (uint8)(mDisplayAddress&0xff);
+ case (DISPADRH&0xff):
+ TRACE_MIKIE2("Peek(DISPADRH,%02x) at PC=%04x",(uint8)(mDisplayAddress>>8)&0xff,mSystem.mCpu->GetPC());
+ return (uint8)(mDisplayAddress>>8)&0xff;
+
+ case (DISPCTL&0xff):
+ case (SYSCTL1&0xff):
+ case (MIKEYSREV&0xff):
+ case (IODIR&0xff):
+ case (SDONEACK&0xff):
+ case (CPUSLEEP&0xff):
+ case (PBKUP&0xff):
+ case (Mtest0&0xff):
+ case (Mtest1&0xff):
+ case (Mtest2&0xff):
+ TRACE_MIKIE2("Peek(%04x) - Peek from write only register location at PC=$%04x",addr,mSystem.mCpu->GetPC());
+ break;
+
+ // Register to let programs know handy is running
+
+ case (0xfd97&0xff):
+ TRACE_MIKIE2("Peek(%04x) - **** HANDY DETECT ATTEMPTED **** at PC=$%04x",addr,mSystem.mCpu->GetPC());
+ // gError->Warning("EMULATOR DETECT REGISTER HAS BEEN READ");
+ return 0x42;
+ break;
+
+ // Errors on illegal location accesses
+
+ default:
+ TRACE_MIKIE2("Peek(%04x) - Peek from illegal location at PC=$%04x",addr,mSystem.mCpu->GetPC());
+ break;
+ }
+ return 0xff;
+}
+
+
+void CMikie::CombobulateSound(uint32 teatime)
+{
+ int cur_lsample = 0;
+ int cur_rsample = 0;
+ int x;
+
+ teatime >>= 2;
+ for(x = 0; x < 4; x++)
+ {
+ /// Assumption (seems there is no documentation for the Attenuation registers)
+ /// a) they are linear from $0 to $f
+ /// b) an attenuation of $0 is equal to channel OFF (bits in mSTEREO not set)
+ /// c) an attenuation of $f is equal to no attenuation (bits in PAN not set)
+ /// These assumptions can only checked with an oszilloscope...
+ /// the values stored in mSTEREO are bit-inverted ...
+ /// mSTEREO was found to be set like that already (why?), but unused
+
+ if(mSTEREO & (0x10 << x))
+ {
+ if(mPAN & (0x10 << x))
+ cur_lsample += (mAUDIO_OUTPUT[x]*(mAUDIO_ATTEN[x]&0xF0))/(15*16);
+ else
+ cur_lsample += mAUDIO_OUTPUT[x];
+ }
+ if(mSTEREO & (0x01 << x))
+ {
+ if(mPAN & (0x01 << x))
+ cur_rsample += (mAUDIO_OUTPUT[x]*(mAUDIO_ATTEN[x]&0x0F))/15;
+ else
+ cur_rsample += mAUDIO_OUTPUT[x];
+ }
+ }
+ if(cur_lsample != last_lsample)
+ {
+ miksynth.offset_inline(teatime, cur_lsample - last_lsample, mikbuf.left());
+ last_lsample = cur_lsample;
+ }
+ if(cur_rsample != last_rsample)
+ {
+ miksynth.offset_inline(teatime, cur_rsample - last_rsample, mikbuf.right());
+ last_rsample = cur_rsample;
+ }
+}
+
+void CMikie::CheckWrap()
+{
+ //
+ // To stop problems with cycle count wrap we will check and then correct the
+ // cycle counter.
+ //
+
+ if(mSystem.gSystemCycleCount>0xf0000000)
+ {
+ mSystem.gSystemCycleCount-=0x80000000;
+ mTIM_0_LAST_COUNT-=0x80000000;
+ mTIM_1_LAST_COUNT-=0x80000000;
+ mTIM_2_LAST_COUNT-=0x80000000;
+ mTIM_3_LAST_COUNT-=0x80000000;
+ mTIM_4_LAST_COUNT-=0x80000000;
+ mTIM_5_LAST_COUNT-=0x80000000;
+ mTIM_6_LAST_COUNT-=0x80000000;
+ mTIM_7_LAST_COUNT-=0x80000000;
+ mAUDIO_LAST_COUNT[0]-=0x80000000;
+ mAUDIO_LAST_COUNT[1]-=0x80000000;
+ mAUDIO_LAST_COUNT[2]-=0x80000000;
+ mAUDIO_LAST_COUNT[3]-=0x80000000;
+ startTS -= 0x80000000;
+ // Only correct if sleep is active
+ if(mSystem.gSuzieDoneTime)
+ {
+ mSystem.gSuzieDoneTime-=0x80000000;
+ }
+ }
+
+}
+
+void CMikie::Update()
+{
+ int32 divide;
+ int32 decval;
+ uint32 tmp;
+ uint32 mikie_work_done=0;
+
+
+ // TRACE_MIKIE0("Update()");
+
+ mSystem.gNextTimerEvent=0xffffffff;
+
+ if(mSystem.gSuzieDoneTime)
+ {
+ if(mSystem.gSystemCycleCount >= mSystem.gSuzieDoneTime)
+ {
+ ClearCPUSleep();
+ mSystem.gSuzieDoneTime = 0;
+ }
+ else if(mSystem.gSuzieDoneTime > mSystem.gSystemCycleCount) mSystem.gNextTimerEvent = mSystem.gSuzieDoneTime;
+ }
+
+ // Timer updates, rolled out flat in group order
+ //
+ // Group A:
+ // Timer 0 -> Timer 2 -> Timer 4.
+ //
+ // Group B:
+ // Timer 1 -> Timer 3 -> Timer 5 -> Timer 7 -> Audio 0 -> Audio 1-> Audio 2 -> Audio 3 -> Timer 1.
+ //
+
+ //
+ // Within each timer code block we will predict the cycle count number of
+ // the next timer event
+ //
+ // We don't need to count linked timers as the timer they are linked
+ // from will always generate earlier events.
+ //
+ // As Timer 4 (UART) will generate many events we will ignore it
+ //
+ // We set the next event to the end of time at first and let the timers
+ // overload it. Any writes to timer controls will force next event to
+ // be immediate and hence a new preidction will be done. The prediction
+ // causes overflow as opposed to zero i.e. current+1
+ // (In reality T0 line counter should always be running.)
+ //
+
+
+ //
+ // Timer 0 of Group A
+ //
+
+ //
+ // Optimisation, assume T0 (Line timer) is never in one-shot,
+ // never placed in link mode
+ //
+
+ // KW bugfix 13/4/99 added (mTIM_x_ENABLE_RELOAD || ..)
+ // if(mTIM_0_ENABLE_COUNT && (mTIM_0_ENABLE_RELOAD || !mTIM_0_TIMER_DONE))
+ if(mTIM_0_ENABLE_COUNT)
+ {
+ // Timer 0 has no linking
+ // if(mTIM_0_LINKING!=0x07)
+ {
+ // Ordinary clocked mode as opposed to linked mode
+ // 16MHz clock downto 1us == cyclecount >> 4
+ divide=(4+mTIM_0_LINKING);
+ decval=(mSystem.gSystemCycleCount-mTIM_0_LAST_COUNT)>>divide;
+
+ if(decval)
+ {
+ mTIM_0_LAST_COUNT+=decval<2x rollover in which case
+ // then CURRENT may still be negative and we can use it to
+ // calc the next timer value, we just want another update ASAP
+ tmp=(mTIM_0_CURRENT&0x80000000)?1:((mTIM_0_CURRENT+1)<> 4
+ // divide=(4+mTIM_2_LINKING);
+ // decval=(gSystemCycleCount-mTIM_2_LAST_COUNT)>>divide;
+ // }
+
+ if(decval)
+ {
+ // mTIM_2_LAST_COUNT+=decval<> 4
+ // Additional /8 (+3) for 8 clocks per bit transmit
+ divide=4+3+mTIM_4_LINKING;
+ decval=(mSystem.gSystemCycleCount-mTIM_4_LAST_COUNT)>>divide;
+ }
+
+ if(decval)
+ {
+ mTIM_4_LAST_COUNT+=decval<0)
+ {
+ mUART_RX_DATA=mUART_Rx_input_queue[mUART_Rx_output_ptr];
+ mUART_Rx_output_ptr = (mUART_Rx_output_ptr + 1) % UART_MAX_RX_QUEUE;
+ mUART_Rx_waiting--;
+ TRACE_MIKIE2("Update() - RX Byte output ptr=%02d waiting=%02d",mUART_Rx_output_ptr,mUART_Rx_waiting);
+ }
+ else
+ {
+ TRACE_MIKIE0("Update() - RX Byte but no data waiting ????");
+ }
+
+ // Retrigger input if more bytes waiting
+ if(mUART_Rx_waiting>0)
+ {
+ mUART_RX_COUNTDOWN=UART_RX_TIME_PERIOD+UART_RX_NEXT_DELAY;
+ TRACE_MIKIE1("Update() - RX Byte retriggered, %d waiting",mUART_Rx_waiting);
+ }
+ else
+ {
+ mUART_RX_COUNTDOWN=UART_RX_INACTIVE;
+ TRACE_MIKIE0("Update() - RX Byte nothing waiting, deactivated");
+ }
+
+ // If RX_READY already set then we have an overrun
+ // as previous byte hasnt been read
+ if(mUART_RX_READY) mUART_Rx_overun_error=1;
+
+ // Flag byte as being recvd
+ mUART_RX_READY=1;
+ }
+ else if(!(mUART_RX_COUNTDOWN&UART_RX_INACTIVE))
+ {
+ mUART_RX_COUNTDOWN--;
+ }
+
+ if(!mUART_TX_COUNTDOWN)
+ {
+ if(mUART_SENDBREAK)
+ {
+ mUART_TX_DATA=UART_BREAK_CODE;
+ // Auto-Respawn new transmit
+ mUART_TX_COUNTDOWN=UART_TX_TIME_PERIOD;
+ // Loop back what we transmitted
+ ComLynxTxLoopback(mUART_TX_DATA);
+ }
+ else
+ {
+ // Serial activity finished
+ mUART_TX_COUNTDOWN=UART_TX_INACTIVE;
+ }
+
+ // If a networking object is attached then use its callback to send the data byte.
+ if(mpUART_TX_CALLBACK)
+ {
+ TRACE_MIKIE0("Update() - UART_TX_CALLBACK");
+ (*mpUART_TX_CALLBACK)(mUART_TX_DATA,mUART_TX_CALLBACK_OBJECT);
+ }
+
+ }
+ else if(!(mUART_TX_COUNTDOWN&UART_TX_INACTIVE))
+ {
+ mUART_TX_COUNTDOWN--;
+ }
+
+ // Set the timer status flag
+ // Timer 4 is the uart timer and doesn't generate IRQ's using this method
+
+ // 16 Clocks = 1 bit transmission. Hold separate Rx & Tx counters
+
+ // Reload if neccessary
+ // if(mTIM_4_ENABLE_RELOAD)
+ // {
+ mTIM_4_CURRENT+=mTIM_4_BKUP+1;
+ // The low reload values on TIM4 coupled with a longer
+ // timer service delay can sometimes cause
+ // an underun, check and fix
+ if(mTIM_4_CURRENT&0x80000000)
+ {
+ mTIM_4_CURRENT=mTIM_4_BKUP;
+ mTIM_4_LAST_COUNT=mSystem.gSystemCycleCount;
+ }
+ // }
+ // else
+ // {
+ // mTIM_4_CURRENT=0;
+ // }
+ // mTIM_4_TIMER_DONE=TRUE;
+ }
+ // else
+ // {
+ // mTIM_4_BORROW_OUT=FALSE;
+ // }
+ // // Set carry in as we did a count
+ // mTIM_4_BORROW_IN=TRUE;
+ }
+ // else
+ // {
+ // // Clear carry in as we didn't count
+ // mTIM_4_BORROW_IN=FALSE;
+ // // Clear carry out
+ // mTIM_4_BORROW_OUT=FALSE;
+ // }
+ //
+ // // Prediction for next timer event cycle number
+ //
+ // if(mTIM_4_LINKING!=7)
+ // {
+ // Sometimes timeupdates can be >2x rollover in which case
+ // then CURRENT may still be negative and we can use it to
+ // calc the next timer value, we just want another update ASAP
+ tmp=(mTIM_4_CURRENT&0x80000000)?1:((mTIM_4_CURRENT+1)<> 4
+ divide=(4+mTIM_1_LINKING);
+ decval=(mSystem.gSystemCycleCount-mTIM_1_LAST_COUNT)>>divide;
+
+ if(decval)
+ {
+ mTIM_1_LAST_COUNT+=decval<2x rollover in which case
+ // then CURRENT may still be negative and we can use it to
+ // calc the next timer value, we just want another update ASAP
+ tmp=(mTIM_1_CURRENT&0x80000000)?1:((mTIM_1_CURRENT+1)<> 4
+ divide=(4+mTIM_3_LINKING);
+ decval=(mSystem.gSystemCycleCount-mTIM_3_LAST_COUNT)>>divide;
+ }
+
+ if(decval)
+ {
+ mTIM_3_LAST_COUNT+=decval<2x rollover in which case
+ // then CURRENT may still be negative and we can use it to
+ // calc the next timer value, we just want another update ASAP
+ tmp=(mTIM_3_CURRENT&0x80000000)?1:((mTIM_3_CURRENT+1)<> 4
+ divide=(4+mTIM_5_LINKING);
+ decval=(mSystem.gSystemCycleCount-mTIM_5_LAST_COUNT)>>divide;
+ }
+
+ if(decval)
+ {
+ mTIM_5_LAST_COUNT+=decval<2x rollover in which case
+ // then CURRENT may still be negative and we can use it to
+ // calc the next timer value, we just want another update ASAP
+ tmp=(mTIM_5_CURRENT&0x80000000)?1:((mTIM_5_CURRENT+1)<> 4
+ divide=(4+mTIM_7_LINKING);
+ decval=(mSystem.gSystemCycleCount-mTIM_7_LAST_COUNT)>>divide;
+ }
+
+ if(decval)
+ {
+ mTIM_7_LAST_COUNT+=decval<2x rollover in which case
+ // then CURRENT may still be negative and we can use it to
+ // calc the next timer value, we just want another update ASAP
+ tmp=(mTIM_7_CURRENT&0x80000000)?1:((mTIM_7_CURRENT+1)<> 4
+ divide=(4+mTIM_6_LINKING);
+ decval=(mSystem.gSystemCycleCount-mTIM_6_LAST_COUNT)>>divide;
+
+ if(decval)
+ {
+ mTIM_6_LAST_COUNT+=decval<2x rollover in which case
+ // then CURRENT may still be negative and we can use it to
+ // calc the next timer value, we just want another update ASAP
+ tmp=(mTIM_6_CURRENT&0x80000000)?1:((mTIM_6_CURRENT+1)<> 4
+ divide=(4+mAUDIO_LINKING[y]);
+ decval=(mSystem.gSystemCycleCount-mAUDIO_LAST_COUNT[y])>>divide;
+ }
+
+ if(decval)
+ {
+ mAUDIO_LAST_COUNT[y] += decval<127) temp=127;
+ if(temp<-128) temp=-128;
+ mAUDIO_OUTPUT[y]=(int8)temp;
+ }
+ else
+ {
+ if(mAUDIO_WAVESHAPER[y]&0x0001) mAUDIO_OUTPUT[y]=mAUDIO_VOLUME[y]; else mAUDIO_OUTPUT[y]=-mAUDIO_VOLUME[y];
+ }
+ CombobulateSound(mSystem.gSystemCycleCount - startTS);
+ }
+ else
+ {
+ mAUDIO_BORROW_OUT[y]=FALSE;
+ }
+ // Set carry in as we did a count
+ mAUDIO_BORROW_IN[y]=TRUE;
+ }
+ else
+ {
+ // Clear carry in as we didn't count
+ mAUDIO_BORROW_IN[y]=FALSE;
+ // Clear carry out
+ mAUDIO_BORROW_OUT[y]=FALSE;
+ }
+
+ // Prediction for next timer event cycle number
+
+ if(mAUDIO_LINKING[y]!=7)
+ {
+ // Sometimes timeupdates can be >2x rollover in which case
+ // then CURRENT may still be negative and we can use it to
+ // calc the next timer value, we just want another update ASAP
+ tmp=(mAUDIO_CURRENT[y]&0x80000000)?1:((mAUDIO_CURRENT[y]+1)<Warning("CMikie::Update() - gSystemCycleCount==gNextTimerEvent, system lock likely");
+ // TRACE_MIKIE1("Update() - NextTimerEvent = %012d",gNextTimerEvent);
+
+ // Update system IRQ status as a result of timer activity
+ // OR is required to ensure serial IRQ's are not masked accidentally
+
+ mSystem.gSystemIRQ=(mTimerStatusFlags)?TRUE:FALSE;
+ if(mSystem.gSystemIRQ && mSystem.gSystemCPUSleep) { ClearCPUSleep(); /*puts("ARLARM"); */ }
+ //else if(gSuzieDoneTime) SetCPUSleep();
+
+ // Now all the timer updates are done we can increment the system
+ // counter for any work done within the Update() function, gSystemCycleCounter
+ // cannot be updated until this point otherwise it screws up the counters.
+ mSystem.gSystemCycleCount+=mikie_work_done;
+}
diff --git a/lynx/mikie.h b/lynx/mikie.h
new file mode 100644
index 0000000000..1872e41a3e
--- /dev/null
+++ b/lynx/mikie.h
@@ -0,0 +1,406 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// Mikey class header file //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This header file provides the interface definition and some of the code //
+// for the Mikey chip within the Lynx. The most crucial code is the //
+// Update() function which as you can probably guess updates all of the //
+// Mikey hardware counters and screen DMA from the prevous time it was //
+// called. Yes I know how to spell Mikey but I cant be bothered to change //
+// it everywhere. //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef MIKIE_H
+#define MIKIE_H
+
+//#include
+//#define TRACE_MIKIE
+#include
+
+#ifdef TRACE_MIKIE
+
+#define TRACE_MIKIE0(msg) _RPT1(_CRT_WARN,"CMikie::"msg" (Time=%012d)\n",gSystemCycleCount)
+#define TRACE_MIKIE1(msg,arg1) _RPT2(_CRT_WARN,"CMikie::"msg" (Time=%012d)\n",arg1,gSystemCycleCount)
+#define TRACE_MIKIE2(msg,arg1,arg2) _RPT3(_CRT_WARN,"CMikie::"msg" (Time=%012d)\n",arg1,arg2,gSystemCycleCount)
+#define TRACE_MIKIE3(msg,arg1,arg2,arg3) _RPT4(_CRT_WARN,"CMikie::"msg" (Time=%012d)\n",arg1,arg2,arg3,gSystemCycleCount)
+
+#else
+
+#define TRACE_MIKIE0(msg)
+#define TRACE_MIKIE1(msg,arg1)
+#define TRACE_MIKIE2(msg,arg1,arg2)
+#define TRACE_MIKIE3(msg,arg1,arg2,arg3)
+
+#endif
+
+class CSystem;
+
+#define MIKIE_START 0xfd00
+#define MIKIE_SIZE 0x100
+
+//
+// Define counter types and defines
+//
+
+#define CTRL_A_IRQEN 0x80
+#define CTRL_A_RTD 0x40
+#define CTRL_A_RELOAD 0x10
+#define CTRL_A_COUNT 0x08
+#define CTRL_A_DIVIDE 0x07
+
+#define CTRL_B_TDONE 0x08
+#define CTRL_B_LASTCK 0x04
+#define CTRL_B_CIN 0x02
+#define CTRL_B_COUT 0x01
+
+#define LINE_TIMER 0x00
+#define SCREEN_TIMER 0x02
+
+#define LINE_WIDTH 160
+#define LINE_SIZE 80
+
+#define UART_TX_INACTIVE 0x80000000
+#define UART_RX_INACTIVE 0x80000000
+#define UART_BREAK_CODE 0x00008000
+#define UART_MAX_RX_QUEUE 32
+#define UART_TX_TIME_PERIOD (11)
+#define UART_RX_TIME_PERIOD (11)
+#define UART_RX_NEXT_DELAY (44)
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+#ifdef MSB_FIRST
+ uint8 unused:4;
+ uint8 Colour:1;
+ uint8 FourColour:1;
+ uint8 Flip:1;
+ uint8 DMAEnable:1;
+#else
+
+ uint8 DMAEnable:1;
+ uint8 Flip:1;
+ uint8 FourColour:1;
+ uint8 Colour:1;
+ uint8 unused:4;
+#endif
+ }Bits;
+ uint8 Byte;
+ };
+}TDISPCTL;
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+#ifdef MSB_FIRST
+ uint8 unused:8;
+ uint8 unused2:8;
+ uint8 unused3:4;
+ uint8 Blue:4;
+ uint8 Red:4;
+ uint8 Green:4;
+#else
+ uint8 Green:4;
+ uint8 Red:4;
+ uint8 Blue:4;
+#endif
+ }Colours;
+ uint32 Index;
+ };
+}TPALETTE;
+
+
+//
+// Emumerated types for possible mikie windows independant modes
+//
+enum
+{
+ MIKIE_BAD_MODE=0,
+ MIKIE_NO_ROTATE,
+ MIKIE_ROTATE_L,
+ MIKIE_ROTATE_R
+};
+
+enum
+{
+ MIKIE_PIXEL_FORMAT_8BPP=0,
+ MIKIE_PIXEL_FORMAT_16BPP_555,
+ MIKIE_PIXEL_FORMAT_16BPP_565,
+ MIKIE_PIXEL_FORMAT_24BPP,
+ MIKIE_PIXEL_FORMAT_32BPP,
+};
+
+#include "sound/Stereo_Buffer.h"
+
+typedef Blip_Synth Synth;
+
+class CMikie : public CLynxBase
+{
+public:
+ CMikie(CSystem& parent) MDFN_COLD;
+ ~CMikie() MDFN_COLD;
+
+ uint32 startTS;
+ Synth miksynth;
+ Stereo_Buffer mikbuf;
+
+ void Reset() MDFN_COLD;
+
+ uint8 Peek(uint32 addr);
+ void Poke(uint32 addr,uint8 data);
+ uint32 ReadCycle() {return 5;};
+ uint32 WriteCycle() {return 5;};
+ uint32 ObjectSize() {return MIKIE_SIZE;};
+ void PresetForHomebrew();
+ uint32 GetLfsrNext(uint32 current);
+
+ void ComLynxCable(int status);
+ void ComLynxRxData(int data);
+ void ComLynxTxLoopback(int data);
+ void ComLynxTxCallback(void (*function)(int data,uint32 objref),uint32 objref);
+
+ void DisplaySetAttributes();
+
+ void BlowOut();
+
+ uint32 DisplayRenderLine();
+ uint32 DisplayEndOfFrame();
+
+ inline void SetCPUSleep();
+ inline void ClearCPUSleep();
+
+ void CombobulateSound(uint32 teatime);
+ void Update();
+ void CheckWrap();
+
+ uint32* mpDisplayCurrent;
+ uint32 mpDisplayCurrentLine;
+ uint32 framebuffer[SCREEN_WIDTH * SCREEN_HEIGHT];
+
+private:
+ CSystem &mSystem;
+
+ int last_lsample;
+ int last_rsample;
+
+ // Hardware storage
+
+ uint32 mDisplayAddress;
+ uint32 mAudioInputComparator;
+ uint32 mTimerStatusFlags;
+ uint32 mTimerInterruptMask;
+
+ TPALETTE mPalette[16];
+ uint32 mColourMap[4096];
+
+ uint32 mIODAT;
+ uint32 mIODIR;
+ uint32 mIODAT_REST_SIGNAL;
+
+ uint32 mDISPCTL_DMAEnable;
+ uint32 mDISPCTL_Flip;
+ uint32 mDISPCTL_FourColour;
+ uint32 mDISPCTL_Colour;
+
+ uint32 mTIM_0_BKUP;
+ uint32 mTIM_0_ENABLE_RELOAD;
+ uint32 mTIM_0_ENABLE_COUNT;
+ uint32 mTIM_0_LINKING;
+ uint32 mTIM_0_CURRENT;
+ uint32 mTIM_0_TIMER_DONE;
+ uint32 mTIM_0_LAST_CLOCK;
+ uint32 mTIM_0_BORROW_IN;
+ uint32 mTIM_0_BORROW_OUT;
+ uint32 mTIM_0_LAST_LINK_CARRY;
+ uint32 mTIM_0_LAST_COUNT;
+
+ uint32 mTIM_1_BKUP;
+ uint32 mTIM_1_ENABLE_RELOAD;
+ uint32 mTIM_1_ENABLE_COUNT;
+ uint32 mTIM_1_LINKING;
+ uint32 mTIM_1_CURRENT;
+ uint32 mTIM_1_TIMER_DONE;
+ uint32 mTIM_1_LAST_CLOCK;
+ uint32 mTIM_1_BORROW_IN;
+ uint32 mTIM_1_BORROW_OUT;
+ uint32 mTIM_1_LAST_LINK_CARRY;
+ uint32 mTIM_1_LAST_COUNT;
+
+ uint32 mTIM_2_BKUP;
+ uint32 mTIM_2_ENABLE_RELOAD;
+ uint32 mTIM_2_ENABLE_COUNT;
+ uint32 mTIM_2_LINKING;
+ uint32 mTIM_2_CURRENT;
+ uint32 mTIM_2_TIMER_DONE;
+ uint32 mTIM_2_LAST_CLOCK;
+ uint32 mTIM_2_BORROW_IN;
+ uint32 mTIM_2_BORROW_OUT;
+ uint32 mTIM_2_LAST_LINK_CARRY;
+ uint32 mTIM_2_LAST_COUNT;
+
+ uint32 mTIM_3_BKUP;
+ uint32 mTIM_3_ENABLE_RELOAD;
+ uint32 mTIM_3_ENABLE_COUNT;
+ uint32 mTIM_3_LINKING;
+ uint32 mTIM_3_CURRENT;
+ uint32 mTIM_3_TIMER_DONE;
+ uint32 mTIM_3_LAST_CLOCK;
+ uint32 mTIM_3_BORROW_IN;
+ uint32 mTIM_3_BORROW_OUT;
+ uint32 mTIM_3_LAST_LINK_CARRY;
+ uint32 mTIM_3_LAST_COUNT;
+
+ uint32 mTIM_4_BKUP;
+ uint32 mTIM_4_ENABLE_RELOAD;
+ uint32 mTIM_4_ENABLE_COUNT;
+ uint32 mTIM_4_LINKING;
+ uint32 mTIM_4_CURRENT;
+ uint32 mTIM_4_TIMER_DONE;
+ uint32 mTIM_4_LAST_CLOCK;
+ uint32 mTIM_4_BORROW_IN;
+ uint32 mTIM_4_BORROW_OUT;
+ uint32 mTIM_4_LAST_LINK_CARRY;
+ uint32 mTIM_4_LAST_COUNT;
+
+ uint32 mTIM_5_BKUP;
+ uint32 mTIM_5_ENABLE_RELOAD;
+ uint32 mTIM_5_ENABLE_COUNT;
+ uint32 mTIM_5_LINKING;
+ uint32 mTIM_5_CURRENT;
+ uint32 mTIM_5_TIMER_DONE;
+ uint32 mTIM_5_LAST_CLOCK;
+ uint32 mTIM_5_BORROW_IN;
+ uint32 mTIM_5_BORROW_OUT;
+ uint32 mTIM_5_LAST_LINK_CARRY;
+ uint32 mTIM_5_LAST_COUNT;
+
+ uint32 mTIM_6_BKUP;
+ uint32 mTIM_6_ENABLE_RELOAD;
+ uint32 mTIM_6_ENABLE_COUNT;
+ uint32 mTIM_6_LINKING;
+ uint32 mTIM_6_CURRENT;
+ uint32 mTIM_6_TIMER_DONE;
+ uint32 mTIM_6_LAST_CLOCK;
+ uint32 mTIM_6_BORROW_IN;
+ uint32 mTIM_6_BORROW_OUT;
+ uint32 mTIM_6_LAST_LINK_CARRY;
+ uint32 mTIM_6_LAST_COUNT;
+
+ uint32 mTIM_7_BKUP;
+ uint32 mTIM_7_ENABLE_RELOAD;
+ uint32 mTIM_7_ENABLE_COUNT;
+ uint32 mTIM_7_LINKING;
+ uint32 mTIM_7_CURRENT;
+ uint32 mTIM_7_TIMER_DONE;
+ uint32 mTIM_7_LAST_CLOCK;
+ uint32 mTIM_7_BORROW_IN;
+ uint32 mTIM_7_BORROW_OUT;
+ uint32 mTIM_7_LAST_LINK_CARRY;
+ uint32 mTIM_7_LAST_COUNT;
+
+ uint32 mAUDIO_BKUP[4];
+ uint32 mAUDIO_ENABLE_RELOAD[4];
+ uint32 mAUDIO_ENABLE_COUNT[4];
+ uint32 mAUDIO_LINKING[4];
+ uint32 mAUDIO_CURRENT[4];
+ uint32 mAUDIO_TIMER_DONE[4];
+ uint32 mAUDIO_LAST_CLOCK[4];
+ uint32 mAUDIO_BORROW_IN[4];
+ uint32 mAUDIO_BORROW_OUT[4];
+ uint32 mAUDIO_LAST_LINK_CARRY[4];
+ uint32 mAUDIO_LAST_COUNT[4];
+ int8 mAUDIO_VOLUME[4];
+ uint32 mAUDIO_INTEGRATE_ENABLE[4];
+ uint32 mAUDIO_WAVESHAPER[4];
+
+ int8 mAUDIO_OUTPUT[4];
+ uint8 mAUDIO_ATTEN[4];
+ uint32 mSTEREO;
+ uint32 mPAN;
+
+ //
+ // Serial related variables
+ //
+ uint32 mUART_RX_IRQ_ENABLE;
+ uint32 mUART_TX_IRQ_ENABLE;
+
+ uint32 mUART_RX_COUNTDOWN;
+ uint32 mUART_TX_COUNTDOWN;
+
+ uint32 mUART_SENDBREAK;
+ uint32 mUART_TX_DATA;
+ uint32 mUART_RX_DATA;
+ uint32 mUART_RX_READY;
+
+ uint32 mUART_PARITY_ENABLE;
+ uint32 mUART_PARITY_EVEN;
+
+ int mUART_CABLE_PRESENT;
+ void (*mpUART_TX_CALLBACK)(int data,uint32 objref);
+ uint32 mUART_TX_CALLBACK_OBJECT;
+
+ int mUART_Rx_input_queue[UART_MAX_RX_QUEUE];
+ unsigned int mUART_Rx_input_ptr;
+ unsigned int mUART_Rx_output_ptr;
+ int mUART_Rx_waiting;
+ int mUART_Rx_framing_error;
+ int mUART_Rx_overun_error;
+
+ //
+ // Screen related
+ //
+
+ uint8 *mpRamPointer;
+ uint32 mLynxLine;
+ uint32 mLynxLineDMACounter;
+ uint32 mLynxAddr;
+
+ void CopyLineSurface();
+ void BlankLineSurface();
+};
+
+
+#endif
+
diff --git a/lynx/msvc/changelog.txt b/lynx/msvc/changelog.txt
new file mode 100644
index 0000000000..cf0539c253
--- /dev/null
+++ b/lynx/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/lynx/msvc/inttypes.h b/lynx/msvc/inttypes.h
new file mode 100644
index 0000000000..25542771f5
--- /dev/null
+++ b/lynx/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/lynx/msvc/stdint.h b/lynx/msvc/stdint.h
new file mode 100644
index 0000000000..59d067302f
--- /dev/null
+++ b/lynx/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/lynx/ram.cpp b/lynx/ram.cpp
new file mode 100644
index 0000000000..1ba8c77ff1
--- /dev/null
+++ b/lynx/ram.cpp
@@ -0,0 +1,64 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// RAM emulation class //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This class emulates the system RAM (64KB), the interface is pretty //
+// simple: constructor, reset, peek, poke. //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+#define RAM_CPP
+
+#include "system.h"
+#include "ram.h"
+
+CRam::CRam()
+{
+ Reset();
+}
+
+CRam::~CRam()
+{
+}
+
+void CRam::Reset(void)
+{
+ //MDFNMP_AddRAM(65536, 0x0000, mRamData);
+ std::memset(mRamData, DEFAULT_RAM_CONTENTS, RAM_SIZE);
+}
diff --git a/lynx/ram.h b/lynx/ram.h
new file mode 100644
index 0000000000..f543bd4f87
--- /dev/null
+++ b/lynx/ram.h
@@ -0,0 +1,87 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// RAM object header file //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This header file provides the interface definition for the RAM class //
+// that emulates the Handy system RAM (64K) //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef RAM_H
+#define RAM_H
+
+#define RAM_SIZE 65536
+#define RAM_ADDR_MASK 0xffff
+#define DEFAULT_RAM_CONTENTS 0xff
+
+struct HOME_HEADER
+{
+ uint16 jump;
+ uint16 load_address;
+ uint16 size;
+ uint8 magic[4];
+};
+
+class CRam : public CLynxBase
+{
+
+ // Function members
+
+public:
+ CRam() MDFN_COLD;
+ ~CRam() MDFN_COLD;
+
+public:
+
+ void Reset(void) MDFN_COLD;
+
+ void Poke(uint32 addr, uint8 data){ mRamData[addr]=data;};
+ uint8 Peek(uint32 addr){ return(mRamData[addr]);};
+ uint32 ReadCycle(void) {return 5;};
+ uint32 WriteCycle(void) {return 5;};
+ uint32 ObjectSize(void) {return RAM_SIZE;};
+ uint8* GetRamPointer(void) { return mRamData; };
+
+ // Data members
+
+private:
+ uint8 mRamData[RAM_SIZE];
+};
+
+#endif
+
diff --git a/lynx/rom.cpp b/lynx/rom.cpp
new file mode 100644
index 0000000000..67e50a4ff6
--- /dev/null
+++ b/lynx/rom.cpp
@@ -0,0 +1,60 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// ROM emulation class //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This class emulates the system ROM (512B), the interface is pretty //
+// simple: constructor, reset, peek, poke. //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#include "system.h"
+#include "rom.h"
+
+CRom::CRom(const uint8 *romfile, uint32 length)
+{
+ mWriteEnable=FALSE;
+ Reset();
+
+ std::memset(mRomData, DEFAULT_ROM_CONTENTS, ROM_SIZE);
+
+ std::memcpy(mRomData, romfile, std::min(ROM_SIZE, length));
+}
+
+void CRom::Reset(void)
+{
+}
diff --git a/lynx/rom.h b/lynx/rom.h
new file mode 100644
index 0000000000..5c4109c760
--- /dev/null
+++ b/lynx/rom.h
@@ -0,0 +1,79 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// ROM object header file //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This header file provides the interface definition and inline code for //
+// the class the emulates the internal 512 byte ROM embedded in Mikey //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef ROM_H
+#define ROM_H
+
+#define ROM_SIZE 0x200
+#define ROM_ADDR_MASK 0x01ff
+#define DEFAULT_ROM_CONTENTS 0x88
+
+#define BROM_START 0xfe00
+#define BROM_SIZE 0x200
+#define VECTOR_START 0xfffa
+#define VECTOR_SIZE 0x6
+
+class CRom : public CLynxBase
+{
+public:
+ CRom(const uint8 *, uint32) MDFN_COLD;
+
+public:
+ void Reset(void) MDFN_COLD;
+ void Poke(uint32 addr,uint8 data) { if(mWriteEnable) mRomData[addr&ROM_ADDR_MASK]=data;};
+ uint8 Peek(uint32 addr) { return(mRomData[addr&ROM_ADDR_MASK]);};
+ uint32 ReadCycle(void) {return 5;};
+ uint32 WriteCycle(void) {return 5;};
+ uint32 ObjectSize(void) {return ROM_SIZE;};
+
+ // Data members
+
+public:
+ bool mWriteEnable;
+private:
+ uint8 mRomData[ROM_SIZE];
+};
+
+#endif
+
diff --git a/lynx/sound/Blip_Buffer.cpp b/lynx/sound/Blip_Buffer.cpp
new file mode 100644
index 0000000000..f04a1fc599
--- /dev/null
+++ b/lynx/sound/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/lynx/sound/Blip_Buffer.h b/lynx/sound/Blip_Buffer.h
new file mode 100644
index 0000000000..a8e90ee053
--- /dev/null
+++ b/lynx/sound/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/lynx/sound/Stereo_Buffer.cpp b/lynx/sound/Stereo_Buffer.cpp
new file mode 100644
index 0000000000..6e65464685
--- /dev/null
+++ b/lynx/sound/Stereo_Buffer.cpp
@@ -0,0 +1,146 @@
+
+// Blip_Buffer 0.3.0. http://www.slack.net/~ant/nes-emu/
+
+#include "Stereo_Buffer.h"
+
+/* Library Copyright (C) 2004 Shay Green. Blip_Buffer 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.
+Stereo_Buffer 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 Stereo_Buffer; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+Stereo_Buffer::Stereo_Buffer() {
+ stereo_added = false;
+ was_stereo = false;
+}
+
+Stereo_Buffer::~Stereo_Buffer() {
+}
+
+bool Stereo_Buffer::set_sample_rate( long rate, int msec )
+{
+ for ( int i = 0; i < buf_count; i++ ) {
+ if ( bufs [i].set_sample_rate( rate, msec ) )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void Stereo_Buffer::clock_rate( long rate )
+{
+ for ( int i = 0; i < buf_count; i++ )
+ bufs [i].clock_rate( rate );
+}
+
+void Stereo_Buffer::bass_freq( int bass )
+{
+ for ( unsigned i = 0; i < buf_count; i++ )
+ bufs [i].bass_freq( bass );
+}
+
+void Stereo_Buffer::clear()
+{
+ stereo_added = false;
+ was_stereo = false;
+ for ( int i = 0; i < buf_count; i++ )
+ bufs [i].clear();
+}
+
+void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo )
+{
+ for ( unsigned i = 0; i < buf_count; i++ )
+ {
+ bufs [i].end_frame( clock_count );
+ }
+ stereo_added |= stereo;
+}
+
+
+
+long Stereo_Buffer::read_samples( blip_sample_t* out, long max_samples )
+{
+ long count = bufs [0].samples_avail();
+ if ( count > max_samples / 2 )
+ count = max_samples / 2;
+ if ( count )
+ {
+ if ( stereo_added || was_stereo )
+ {
+ mix_stereo( out, count );
+
+ bufs [0].remove_samples( count );
+ bufs [1].remove_samples( count );
+ bufs [2].remove_samples( count );
+ }
+ else
+ {
+ mix_mono( out, count );
+
+ bufs [0].remove_samples( count );
+
+ bufs [1].remove_silence( count );
+ bufs [2].remove_silence( count );
+ }
+
+ // to do: this might miss opportunities for optimization
+ if ( !bufs [0].samples_avail() ) {
+ was_stereo = stereo_added;
+ stereo_added = false;
+ }
+ }
+
+ return count * 2;
+}
+
+void Stereo_Buffer::mix_stereo( blip_sample_t* out, long count )
+{
+ Blip_Reader l_left;
+ Blip_Reader l_right;
+ Blip_Reader l_center;
+
+ l_left.begin( bufs [1] );
+ l_right.begin( bufs [2] );
+ int bass = l_center.begin( bufs [0] );
+
+ while ( count-- )
+ {
+ int c = l_center.read();
+ out [0] = c + l_left.read();
+ out [1] = c + l_right.read();
+ out += 2;
+
+ l_center.next( bass );
+ l_left.next( bass );
+ l_right.next( bass );
+ }
+
+ l_center.end( bufs [0] );
+ l_right.end( bufs [2] );
+ l_left.end( bufs [1] );
+}
+
+void Stereo_Buffer::mix_mono( blip_sample_t* out, long count )
+{
+ Blip_Reader in;
+ int bass = in.begin( bufs [0] );
+
+ while ( count-- )
+ {
+ int sample = in.read();
+ out [0] = sample;
+ out [1] = sample;
+ out += 2;
+ in.next( bass );
+ }
+
+ in.end( bufs [0] );
+}
+
diff --git a/lynx/sound/Stereo_Buffer.h b/lynx/sound/Stereo_Buffer.h
new file mode 100644
index 0000000000..3d907fac54
--- /dev/null
+++ b/lynx/sound/Stereo_Buffer.h
@@ -0,0 +1,69 @@
+
+// Simple stereo Blip_Buffer for sound emulators whose oscillators output
+// either on the left only, center, or right only.
+
+// Blip_Buffer 0.3.0. Copyright (C) 2003-2004 Shay Green. GNU GPL license.
+
+#ifndef STEREO_BUFFER_H
+#define STEREO_BUFFER_H
+
+#include "Blip_Buffer.h"
+
+class Stereo_Buffer {
+public:
+ Stereo_Buffer();
+ ~Stereo_Buffer();
+
+ // Same as in Blip_Buffer (see Blip_Buffer.h)
+ bool set_sample_rate( long, int msec = 0 );
+ void clock_rate( long );
+ void bass_freq( int );
+ void clear();
+
+ // Buffers to output synthesis to
+ Blip_Buffer* left();
+ Blip_Buffer* center();
+ Blip_Buffer* right();
+
+ // Same as in Blip_Buffer. For more efficient operation, pass false
+ // for was_stereo if the left and right buffers had nothing added
+ // to them for this frame.
+ void end_frame( blip_time_t, bool was_stereo = true );
+
+ // Output is stereo with channels interleved, left before right. Counts
+ // are in samples, *not* pairs.
+ long samples_avail() const;
+ long read_samples( blip_sample_t*, long );
+
+private:
+ // noncopyable
+ Stereo_Buffer( const Stereo_Buffer& );
+ Stereo_Buffer& operator = ( const Stereo_Buffer& );
+
+ enum { buf_count = 3 };
+ Blip_Buffer bufs [buf_count];
+ bool stereo_added;
+ bool was_stereo;
+
+ void mix_stereo( blip_sample_t*, long );
+ void mix_mono( blip_sample_t*, long );
+};
+
+ inline Blip_Buffer* Stereo_Buffer::left() {
+ return &bufs [1];
+ }
+
+ inline Blip_Buffer* Stereo_Buffer::center() {
+ return &bufs [0];
+ }
+
+ inline Blip_Buffer* Stereo_Buffer::right() {
+ return &bufs [2];
+ }
+
+ inline long Stereo_Buffer::samples_avail() const {
+ return bufs [0].samples_avail();
+ }
+
+#endif
+
diff --git a/lynx/susie.cpp b/lynx/susie.cpp
new file mode 100644
index 0000000000..9615efc292
--- /dev/null
+++ b/lynx/susie.cpp
@@ -0,0 +1,2129 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// Suzy emulation class //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This class emulates the Suzy chip within the lynx. This provides math //
+// and sprite painting facilities. SpritePaint() is called from within //
+// the Mikey POKE functions when SPRGO is set and is called via the system //
+// object to keep the interface clean. //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#define SUSIE_CPP
+
+//#include
+//#define TRACE_SUSIE
+
+#include "system.h"
+#include "susie.h"
+#include "lynxdef.h"
+
+//
+// As the Susie sprite engine only ever sees system RAM
+// wa can access this directly without the hassle of
+// going through the system object, much faster
+//
+//#define RAM_PEEK(m) (mSystem.Peek_RAM((m)))
+//#define RAM_POKE(m1,m2) (mSystem.Poke_RAM((m1),(m2)))
+//#define RAM_PEEKW(m) (mSystem.PeekW_RAM((m)))
+
+#define RAM_PEEK(m) (mRamPointer[(m)])
+#define RAM_PEEKW(m) (mRamPointer[(m)]+(mRamPointer[(m)+1]<<8))
+#define RAM_POKE(m1,m2) {mRamPointer[(m1)]=(m2);}
+
+CSusie::CSusie(CSystem& parent)
+ :mSystem(parent)
+{
+ TRACE_SUSIE0("CSusie()");
+ Reset();
+}
+
+CSusie::~CSusie()
+{
+ TRACE_SUSIE0("~CSusie()");
+}
+
+void CSusie::Reset(void)
+{
+ TRACE_SUSIE0("Reset()");
+
+ // Fetch pointer to system RAM, faster than object access
+ // and seeing as Susie only ever sees RAM.
+
+ mRamPointer=mSystem.GetRamPointer();
+
+ // Reset ALL variables
+
+ mTMPADR.Val16=0;
+ mTILTACUM.Val16=0;
+ mHOFF.Val16=0;
+ mVOFF.Val16=0;
+ mVIDBAS.Val16=0;
+ mCOLLBAS.Val16=0;
+ mVIDADR.Val16=0;
+ mCOLLADR.Val16=0;
+ mSCBNEXT.Val16=0;
+ mSPRDLINE.Val16=0;
+ mHPOSSTRT.Val16=0;
+ mVPOSSTRT.Val16=0;
+ mSPRHSIZ.Val16=0;
+ mSPRVSIZ.Val16=0;
+ mSTRETCH.Val16=0;
+ mTILT.Val16=0;
+ mSPRDOFF.Val16=0;
+ mSPRVPOS.Val16=0;
+ mCOLLOFF.Val16=0;
+ mVSIZACUM.Val16=0;
+ mHSIZACUM.Val16=0;
+ mHSIZOFF.Val16=0x007f;
+ mVSIZOFF.Val16=0x007f;
+ mSCBADR.Val16=0;
+ mPROCADR.Val16=0;
+
+ // Must be initialised to this due to
+ // stun runner math initialisation bug
+ // see whatsnew for 0.7
+ mMATHABCD.Long=0xffffffff;
+ mMATHEFGH.Long=0xffffffff;
+ mMATHJKLM.Long=0xffffffff;
+ mMATHNP.Long=0xffff;
+
+ mMATHAB_sign=1;
+ mMATHCD_sign=1;
+ mMATHEFGH_sign=1;
+
+ mSPRCTL0_Type=0;
+ mSPRCTL0_Vflip=0;
+ mSPRCTL0_Hflip=0;
+ mSPRCTL0_PixelBits=0;
+
+ mSPRCTL1_StartLeft=0;
+ mSPRCTL1_StartUp=0;
+ mSPRCTL1_SkipSprite=0;
+ mSPRCTL1_ReloadPalette=0;
+ mSPRCTL1_ReloadDepth=0;
+ mSPRCTL1_Sizing=0;
+ mSPRCTL1_Literal=0;
+
+ mSPRCOLL_Number=0;
+ mSPRCOLL_Collide=0;
+
+ mSPRSYS_StopOnCurrent=0;
+ mSPRSYS_LeftHand=0;
+ mSPRSYS_VStretch=0;
+ mSPRSYS_NoCollide=0;
+ mSPRSYS_Accumulate=0;
+ mSPRSYS_SignedMath=0;
+ mSPRSYS_Status=0;
+ mSPRSYS_UnsafeAccess=0;
+ mSPRSYS_LastCarry=0;
+ mSPRSYS_Mathbit=0;
+ mSPRSYS_MathInProgress=0;
+
+ mSUZYBUSEN=FALSE;
+
+ mSPRINIT.Byte=0;
+
+ mSPRGO=FALSE;
+ mEVERON=FALSE;
+
+ for(int loop=0;loop<16;loop++) mPenIndex[loop]=loop;
+
+ hquadoff = vquadoff = 0;
+
+ mJOYSTICK.Byte=0;
+ mSWITCHES.Byte=0;
+}
+
+
+void CSusie::DoMathMultiply(void)
+{
+ mSPRSYS_Mathbit=FALSE;
+
+ // Multiplies with out sign or accumulate take 44 ticks to complete.
+ // Multiplies with sign and accumulate take 54 ticks to complete.
+ //
+ // AB EFGH
+ // * CD / NP
+ // ------- -----------
+ // EFGH ABCD
+ // Accumulate in JKLM Remainder in (JK)LM
+ //
+
+
+ uint32 result;
+
+ // Basic multiply is ALWAYS unsigned, sign conversion is done later
+ result=(uint32)mMATHABCD.Words.AB*(uint32)mMATHABCD.Words.CD;
+ mMATHEFGH.Long=result;
+
+ if(mSPRSYS_SignedMath)
+ {
+ TRACE_SUSIE0("DoMathMultiply() - SIGNED");
+ // Add the sign bits, only >0 is +ve result
+ mMATHEFGH_sign=mMATHAB_sign+mMATHCD_sign;
+ if(!mMATHEFGH_sign)
+ {
+ mMATHEFGH.Long^=0xffffffff;
+ mMATHEFGH.Long++;
+ }
+ }
+ else
+ {
+ TRACE_SUSIE0("DoMathMultiply() - UNSIGNED");
+ }
+
+ TRACE_SUSIE2("DoMathMultiply() AB=$%04x * CD=$%04x",mMATHABCD.Words.AB,mMATHABCD.Words.CD);
+
+ // Check overflow, if B31 has changed from 1->0 then its overflow time
+ if(mSPRSYS_Accumulate)
+ {
+ TRACE_SUSIE0("DoMathMultiply() - ACCUMULATED JKLM+=EFGH");
+ uint32 tmp=mMATHJKLM.Long+mMATHEFGH.Long;
+ // Let sign change indicate overflow
+ if((tmp&0x80000000)!=(mMATHJKLM.Long&0x80000000))
+ {
+ TRACE_SUSIE0("DoMathMultiply() - OVERFLOW DETECTED");
+ // mSPRSYS_Mathbit=TRUE;
+ }
+ else
+ {
+ // mSPRSYS_Mathbit=FALSE;
+ }
+ // Save accumulated result
+ mMATHJKLM.Long=tmp;
+ }
+
+ TRACE_SUSIE1("DoMathMultiply() Results (raw - no sign) Result=$%08x",result);
+ TRACE_SUSIE1("DoMathMultiply() Results (Multi) EFGH=$%08x",mMATHEFGH.Long);
+ TRACE_SUSIE1("DoMathMultiply() Results (Accum) JKLM=$%08x",mMATHJKLM.Long);
+}
+
+void CSusie::DoMathDivide(void)
+{
+ mSPRSYS_Mathbit=FALSE;
+
+ //
+ // Divides take 176 + 14*N ticks
+ // (N is the number of most significant zeros in the divisor.)
+ //
+ // AB EFGH
+ // * CD / NP
+ // ------- -----------
+ // EFGH ABCD
+ // Accumulate in JKLM Remainder in (JK)LM
+ //
+
+ // Divide is ALWAYS unsigned arithmetic...
+ if(mMATHNP.Long)
+ {
+ TRACE_SUSIE0("DoMathDivide() - UNSIGNED");
+ mMATHABCD.Long=mMATHEFGH.Long/mMATHNP.Long;
+ mMATHJKLM.Long=mMATHEFGH.Long%mMATHNP.Long;
+ }
+ else
+ {
+ TRACE_SUSIE0("DoMathDivide() - DIVIDE BY ZERO ERROR");
+ mMATHABCD.Long=0xffffffff;
+ mMATHJKLM.Long=0;
+ mSPRSYS_Mathbit=TRUE;
+ }
+ TRACE_SUSIE2("DoMathDivide() EFGH=$%08x / NP=%04x",mMATHEFGH.Long,mMATHNP.Long);
+ TRACE_SUSIE1("DoMathDivide() Results (div) ABCD=$%08x",mMATHABCD.Long);
+ TRACE_SUSIE1("DoMathDivide() Results (mod) JKLM=$%08x",mMATHJKLM.Long);
+}
+
+
+uint32 CSusie::PaintSprites(void)
+{
+ int sprcount=0;
+ int data=0;
+ int everonscreen=0;
+
+ TRACE_SUSIE0(" ");
+ TRACE_SUSIE0(" ");
+ TRACE_SUSIE0(" ");
+ TRACE_SUSIE0("**************************************************************");
+ TRACE_SUSIE0("********************** PaintSprites **************************");
+ TRACE_SUSIE0("**************************************************************");
+ TRACE_SUSIE0(" ");
+
+ TRACE_SUSIE1("PaintSprites() VIDBAS $%04x",mVIDBAS.Val16);
+ TRACE_SUSIE1("PaintSprites() COLLBAS $%04x",mCOLLBAS.Val16);
+ TRACE_SUSIE1("PaintSprites() SPRSYS $%02x",Peek(SPRSYS));
+
+ if(!mSUZYBUSEN || !mSPRGO)
+ {
+ TRACE_SUSIE0("PaintSprites() Returned !mSUZYBUSEN || !mSPRGO");
+ return 0;
+ }
+
+ cycles_used=0;
+
+ do
+ {
+ everonscreen = 0;
+
+ TRACE_SUSIE1("PaintSprites() ************ Rendering Sprite %03d ************",sprcount);
+
+ // Step 1 load up the SCB params into Susie
+
+ // And thus it is documented that only the top byte of SCBNEXT is used.
+ // Its mentioned under the bits that are broke section in the bluebook
+ if(!(mSCBNEXT.Val16&0xff00))
+ {
+ TRACE_SUSIE0("PaintSprites() mSCBNEXT==0 - FINISHED");
+ mSPRSYS_Status=0; // Engine has finished
+ mSPRGO=FALSE;
+ break;
+ }
+ else
+ {
+ mSPRSYS_Status=1;
+ }
+
+ mTMPADR.Val16=mSCBNEXT.Val16; // Copy SCB pointer
+ mSCBADR.Val16=mSCBNEXT.Val16; // Copy SCB pointer
+ TRACE_SUSIE1("PaintSprites() SCBADDR $%04x",mSCBADR.Val16);
+
+ data=RAM_PEEK(mTMPADR.Val16); // Fetch control 0
+ TRACE_SUSIE1("PaintSprites() SPRCTL0 $%02x",data);
+ mSPRCTL0_Type=data&0x0007;
+ mSPRCTL0_Vflip=data&0x0010;
+ mSPRCTL0_Hflip=data&0x0020;
+ mSPRCTL0_PixelBits=((data&0x00c0)>>6)+1;
+ mTMPADR.Val16+=1;
+
+ data=RAM_PEEK(mTMPADR.Val16); // Fetch control 1
+ TRACE_SUSIE1("PaintSprites() SPRCTL1 $%02x",data);
+ mSPRCTL1_StartLeft=data&0x0001;
+ mSPRCTL1_StartUp=data&0x0002;
+ mSPRCTL1_SkipSprite=data&0x0004;
+ mSPRCTL1_ReloadPalette=data&0x0008;
+ mSPRCTL1_ReloadDepth=(data&0x0030)>>4;
+ mSPRCTL1_Sizing=data&0x0040;
+ mSPRCTL1_Literal=data&0x0080;
+ mTMPADR.Val16+=1;
+
+ data=RAM_PEEK(mTMPADR.Val16); // Collision num
+ TRACE_SUSIE1("PaintSprites() SPRCOLL $%02x",data);
+ mSPRCOLL_Number=data&0x000f;
+ mSPRCOLL_Collide=data&0x0020;
+ mTMPADR.Val16+=1;
+
+ mSCBNEXT.Val16=RAM_PEEKW(mTMPADR.Val16); // Next SCB
+ TRACE_SUSIE1("PaintSprites() SCBNEXT $%04x",mSCBNEXT.Val16);
+ mTMPADR.Val16+=2;
+
+ cycles_used+=5*SPR_RDWR_CYC;
+
+ // Initialise the collision depositary
+
+ // Although Tom Schenck says this is correct, it doesnt appear to be
+ // if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide)
+ // {
+ // mCollision=RAM_PEEK((mSCBADR.Val16+mCOLLOFF.Val16)&0xffff);
+ // mCollision&=0x0f;
+ // }
+ mCollision=0;
+
+ // Check if this is a skip sprite
+
+ if(!mSPRCTL1_SkipSprite)
+ {
+
+ mSPRDLINE.Val16=RAM_PEEKW(mTMPADR.Val16); // Sprite pack data
+ TRACE_SUSIE1("PaintSprites() SPRDLINE $%04x",mSPRDLINE.Val16);
+ mTMPADR.Val16+=2;
+
+ mHPOSSTRT.Val16=RAM_PEEKW(mTMPADR.Val16); // Sprite horizontal start position
+ TRACE_SUSIE1("PaintSprites() HPOSSTRT $%04x",mHPOSSTRT.Val16);
+ mTMPADR.Val16+=2;
+
+ mVPOSSTRT.Val16=RAM_PEEKW(mTMPADR.Val16); // Sprite vertical start position
+ TRACE_SUSIE1("PaintSprites() VPOSSTRT $%04x",mVPOSSTRT.Val16);
+ mTMPADR.Val16+=2;
+
+ cycles_used+=6*SPR_RDWR_CYC;
+
+ bool enable_sizing=FALSE;
+ bool enable_stretch=FALSE;
+ bool enable_tilt=FALSE;
+
+ // Optional section defined by reload type in Control 1
+
+ TRACE_SUSIE1("PaintSprites() mSPRCTL1.Bits.ReloadDepth=%d",mSPRCTL1_ReloadDepth);
+ switch(mSPRCTL1_ReloadDepth)
+ {
+ case 1:
+ TRACE_SUSIE0("PaintSprites() Sizing Enabled");
+ enable_sizing=TRUE;
+
+ mSPRHSIZ.Val16=RAM_PEEKW(mTMPADR.Val16); // Sprite Horizontal size
+ mTMPADR.Val16+=2;
+
+ mSPRVSIZ.Val16=RAM_PEEKW(mTMPADR.Val16); // Sprite Verticalal size
+ mTMPADR.Val16+=2;
+
+ cycles_used+=4*SPR_RDWR_CYC;
+ break;
+
+ case 2:
+ TRACE_SUSIE0("PaintSprites() Sizing Enabled");
+ TRACE_SUSIE0("PaintSprites() Stretch Enabled");
+ enable_sizing=TRUE;
+ enable_stretch=TRUE;
+
+ mSPRHSIZ.Val16=RAM_PEEKW(mTMPADR.Val16); // Sprite Horizontal size
+ mTMPADR.Val16+=2;
+
+ mSPRVSIZ.Val16=RAM_PEEKW(mTMPADR.Val16); // Sprite Verticalal size
+ mTMPADR.Val16+=2;
+
+ mSTRETCH.Val16=RAM_PEEKW(mTMPADR.Val16); // Sprite stretch
+ mTMPADR.Val16+=2;
+
+ cycles_used+=6*SPR_RDWR_CYC;
+ break;
+
+ case 3:
+ TRACE_SUSIE0("PaintSprites() Sizing Enabled");
+ TRACE_SUSIE0("PaintSprites() Stretch Enabled");
+ TRACE_SUSIE0("PaintSprites() Tilt Enabled");
+ enable_sizing=TRUE;
+ enable_stretch=TRUE;
+ enable_tilt=TRUE;
+
+ mSPRHSIZ.Val16=RAM_PEEKW(mTMPADR.Val16); // Sprite Horizontal size
+ mTMPADR.Val16+=2;
+
+ mSPRVSIZ.Val16=RAM_PEEKW(mTMPADR.Val16); // Sprite Verticalal size
+ mTMPADR.Val16+=2;
+
+ mSTRETCH.Val16=RAM_PEEKW(mTMPADR.Val16); // Sprite stretch
+ mTMPADR.Val16+=2;
+
+ mTILT.Val16=RAM_PEEKW(mTMPADR.Val16); // Sprite tilt
+ mTMPADR.Val16+=2;
+
+ cycles_used+=8*SPR_RDWR_CYC;
+ break;
+
+ default:
+ break;
+ }
+
+ TRACE_SUSIE1("PaintSprites() SPRHSIZ $%04x",mSPRHSIZ.Val16);
+ TRACE_SUSIE1("PaintSprites() SPRVSIZ $%04x",mSPRVSIZ.Val16);
+ TRACE_SUSIE1("PaintSprites() STRETCH $%04x",mSTRETCH.Val16);
+ TRACE_SUSIE1("PaintSprites() TILT $%04x",mTILT.Val16);
+
+
+ // Optional Palette reload
+
+ if(!mSPRCTL1_ReloadPalette)
+ {
+ TRACE_SUSIE0("PaintSprites() Palette reloaded");
+ for(int loop=0;loop<8;loop++)
+ {
+ uint8 data_tmp = RAM_PEEK(mTMPADR.Val16++);
+ mPenIndex[loop*2]=(data_tmp>>4)&0x0f;
+ mPenIndex[(loop*2)+1]=data_tmp&0x0f;
+ }
+ // Increment cycle count for the reads
+ cycles_used+=8*SPR_RDWR_CYC;
+ }
+
+ // Now we can start painting
+
+ // Quadrant drawing order is: SE,NE,NW,SW
+ // start quadrant is given by sprite_control1:0 & 1
+
+ // Setup screen start end variables
+
+ int screen_h_start=(int16)mHOFF.Val16;
+ int screen_h_end=(int16)mHOFF.Val16+SCREEN_WIDTH;
+ int screen_v_start=(int16)mVOFF.Val16;
+ int screen_v_end=(int16)mVOFF.Val16+SCREEN_HEIGHT;
+
+ int world_h_mid=screen_h_start+0x8000+(SCREEN_WIDTH/2);
+ int world_v_mid=screen_v_start+0x8000+(SCREEN_HEIGHT/2);
+
+ TRACE_SUSIE2("PaintSprites() screen_h_start $%04x screen_h_end $%04x",screen_h_start,screen_h_end);
+ TRACE_SUSIE2("PaintSprites() screen_v_start $%04x screen_v_end $%04x",screen_v_start,screen_v_end);
+ TRACE_SUSIE2("PaintSprites() world_h_mid $%04x world_v_mid $%04x",world_h_mid,world_v_mid);
+
+ bool superclip=FALSE;
+ int quadrant=0;
+ int hsign,vsign;
+
+ if(mSPRCTL1_StartLeft)
+ {
+ if(mSPRCTL1_StartUp) quadrant=2; else quadrant=3;
+ }
+ else
+ {
+ if(mSPRCTL1_StartUp) quadrant=1; else quadrant=0;
+ }
+ TRACE_SUSIE1("PaintSprites() Quadrant=%d",quadrant);
+
+ // Check ref is inside screen area
+
+ //if((int16)mHPOSSTRT.Val16=screen_h_end ||
+ // (int16)mVPOSSTRT.Val16=screen_v_end) superclip=TRUE;
+
+ TRACE_SUSIE1("PaintSprites() Superclip=%d",superclip);
+
+
+ // Quadrant mapping is: SE NE NW SW
+ // 0 1 2 3
+ // hsign +1 +1 -1 -1
+ // vsign +1 -1 -1 +1
+ //
+ //
+ // 2 | 1
+ // -------
+ // 3 | 0
+ //
+
+ // Loop for 4 quadrants
+
+ for(int loop=0;loop<4;loop++)
+ {
+ TRACE_SUSIE1("PaintSprites() -------- Rendering Quadrant %03d --------",quadrant);
+
+ int sprite_v=mVPOSSTRT.Val16;
+ int sprite_h=mHPOSSTRT.Val16;
+
+ bool render=FALSE;
+
+ // Set quadrand multipliers
+ hsign=(quadrant==0 || quadrant==1)?1:-1;
+ vsign=(quadrant==0 || quadrant==3)?1:-1;
+
+ // Preflip TRACE_SUSIE2("PaintSprites() hsign=%d vsign=%d",hsign,vsign);
+
+ //Use h/v flip to invert v/hsign
+
+ if(mSPRCTL0_Vflip) vsign=-vsign;
+ if(mSPRCTL0_Hflip) hsign=-hsign;
+
+ TRACE_SUSIE2("PaintSprites() Hflip=%d Vflip=%d",mSPRCTL0_Hflip,mSPRCTL0_Vflip);
+ TRACE_SUSIE2("PaintSprites() Hsign=%d Vsign=%d",hsign,vsign);
+ TRACE_SUSIE2("PaintSprites() Hpos =%04x Vpos =%04x",mHPOSSTRT.Val16,mVPOSSTRT.Val16);
+ TRACE_SUSIE2("PaintSprites() Hsizoff =%04x Vsizoff =%04x",mHSIZOFF.Val16,mVSIZOFF.Val16);
+
+ // Two different rendering algorithms used, on-screen & superclip
+ // when on screen we draw in x until off screen then skip to next
+ // line, BUT on superclip we draw all the way to the end of any
+ // given line checking each pixel is on screen.
+
+ if(superclip)
+ {
+ // Check on the basis of each quad, we only render the quad
+ // IF the screen is in the quad, relative to the centre of
+ // the screen which is calculated below.
+
+ // Quadrant mapping is: SE NE NW SW
+ // 0 1 2 3
+ // hsign +1 +1 -1 -1
+ // vsign +1 -1 -1 +1
+ //
+ //
+ // 2 | 1
+ // -------
+ // 3 | 0
+ //
+ // Quadrant mapping for superclipping must also take into account
+ // the hflip, vflip bits & negative tilt to be able to work correctly
+ //
+ int modquad=quadrant;
+ static const int vquadflip[4]={1,0,3,2};
+ static const int hquadflip[4]={3,2,1,0};
+
+ if(mSPRCTL0_Vflip) modquad=vquadflip[modquad];
+ if(mSPRCTL0_Hflip) modquad=hquadflip[modquad];
+
+ // This is causing Eurosoccer to fail!!
+ //if(enable_tilt && mTILT.Val16&0x8000) modquad=hquadflip[modquad];
+ //if(quadrant == 0 && sprite_v == 219 && sprite_h == 890)
+ //printf("%d:%d %d %d\n", quadrant, modquad, sprite_h, sprite_v);
+
+ switch(modquad)
+ {
+ case 3:
+ if((sprite_h>=screen_h_start || sprite_hworld_v_mid)) render=TRUE;
+ break;
+ case 2:
+ if((sprite_h>=screen_h_start || sprite_h=screen_v_start || sprite_vworld_h_mid) && (sprite_v>=screen_v_start || sprite_vworld_h_mid) && (sprite_vworld_v_mid)) render=TRUE;
+ break;
+ }
+ }
+ else
+ {
+ render=TRUE;
+ }
+
+ // Is this quad to be rendered ??
+
+ TRACE_SUSIE1("PaintSprites() Render status %d",render);
+
+ int pixel_height;
+ int pixel_width;
+ int pixel;
+ int hoff,voff;
+ int hloop,vloop;
+ bool onscreen;
+
+ if(render)
+ {
+ // Set the vertical position & offset
+ voff=(int16)mVPOSSTRT.Val16-screen_v_start;
+
+ // Zero the stretch,tilt & acum values
+ mTILTACUM.Val16=0;
+
+ // Perform the SIZOFF
+ if(vsign==1) mVSIZACUM.Val16=mVSIZOFF.Val16; else mVSIZACUM.Val16=0;
+
+ // Take the sign of the first quad (0) as the basic
+ // sign, all other quads drawing in the other direction
+ // get offset by 1 pixel in the other direction, this
+ // fixes the squashed look on the multi-quad sprites.
+ // if(vsign==-1 && loop>0) voff+=vsign;
+ if(loop==0) vquadoff=vsign;
+ if(vsign!=vquadoff) voff+=vsign;
+
+ for(;;)
+ {
+ // Vertical scaling is done here
+ mVSIZACUM.Val16+=mSPRVSIZ.Val16;
+ pixel_height=mVSIZACUM.Union8.High;
+ mVSIZACUM.Union8.High=0;
+
+ // Update the next data line pointer and initialise our line
+ mSPRDOFF.Val16=(uint16)LineInit(0);
+
+ // If 1 == next quad, ==0 end of sprite, anyways its END OF LINE
+ if(mSPRDOFF.Val16==1) // End of quad
+ {
+ mSPRDLINE.Val16+=mSPRDOFF.Val16;
+ break;
+ }
+
+ if(mSPRDOFF.Val16==0) // End of sprite
+ {
+ loop=4; // Halt the quad loop
+ break;
+ }
+
+ // Draw one horizontal line of the sprite
+ for(vloop=0;vloop=SCREEN_HEIGHT) break;
+ if(vsign==-1 && voff<0) break;
+
+ // Only allow the draw to take place if the line is visible
+ if(voff>=0 && voff>8);
+ mTILTACUM.Union8.High=0;
+ hoff=(int)((int16)mHPOSSTRT.Val16)-screen_h_start;
+
+ // Zero/Force the horizontal scaling accumulator
+ if(hsign==1) mHSIZACUM.Val16=mHSIZOFF.Val16; else mHSIZACUM.Val16=0;
+
+ // Take the sign of the first quad (0) as the basic
+ // sign, all other quads drawing in the other direction
+ // get offset by 1 pixel in the other direction, this
+ // fixes the squashed look on the multi-quad sprites.
+ // if(hsign==-1 && loop>0) hoff+=hsign;
+ if(loop==0) hquadoff=hsign;
+ if(hsign!=hquadoff) hoff+=hsign;
+
+ // Initialise our line
+ LineInit(voff);
+ onscreen=FALSE;
+
+ // Now render an individual destination line
+ while((pixel=LineGetPixel())!=LINE_END)
+ {
+ // This is allowed to update every pixel
+ mHSIZACUM.Val16+=mSPRHSIZ.Val16;
+ pixel_width=mHSIZACUM.Union8.High;
+ mHSIZACUM.Union8.High=0;
+
+ for(hloop=0;hloop=0 && hoff4096)
+ {
+ // Stop the system, otherwise we may just come straight back in.....
+ mSystem.gSystemHalt=TRUE;
+ // Display warning message
+ //gError->Warning("CSusie:PaintSprites(): Single draw sprite limit exceeded (>4096). The SCB is most likely looped back on itself. Reset/Exit is recommended");
+ // Signal error to the caller
+ return 0;
+ }
+ }
+ while(1);
+
+ // Fudge factor to fix many flickering issues, also the keypress
+ // problem with Hard Drivin and the strange pause in Dirty Larry.
+ //cycles_used>>=2;
+ return cycles_used;
+}
+
+
+INLINE void CSusie::WritePixel(uint32 hoff,uint32 pixel)
+{
+ uint32 scr_addr=mLineBaseAddress+(hoff/2);
+
+ uint8 dest=RAM_PEEK(scr_addr);
+ if(!(hoff&0x01))
+ {
+ // Upper nibble screen write
+ dest&=0x0f;
+ dest|=pixel<<4;
+ }
+ else
+ {
+ // Lower nibble screen write
+ dest&=0xf0;
+ dest|=pixel;
+ }
+ RAM_POKE(scr_addr,dest);
+
+ // Increment cycle count for the read/modify/write
+ cycles_used+=2*SPR_RDWR_CYC;
+}
+
+INLINE uint32 CSusie::ReadPixel(uint32 hoff)
+{
+ uint32 scr_addr=mLineBaseAddress+(hoff/2);
+
+ uint32 data=RAM_PEEK(scr_addr);
+ if(!(hoff&0x01))
+ {
+ // Upper nibble read
+ data>>=4;
+ }
+ else
+ {
+ // Lower nibble read
+ data&=0x0f;
+ }
+
+ // Increment cycle count for the read/modify/write
+ cycles_used+=SPR_RDWR_CYC;
+
+ return data;
+}
+
+INLINE void CSusie::WriteCollision(uint32 hoff,uint32 pixel)
+{
+ uint32 col_addr=mLineCollisionAddress+(hoff/2);
+
+ uint8 dest=RAM_PEEK(col_addr);
+ if(!(hoff&0x01))
+ {
+ // Upper nibble screen write
+ dest&=0x0f;
+ dest|=pixel<<4;
+ }
+ else
+ {
+ // Lower nibble screen write
+ dest&=0xf0;
+ dest|=pixel;
+ }
+ RAM_POKE(col_addr,dest);
+
+ // Increment cycle count for the read/modify/write
+ cycles_used+=2*SPR_RDWR_CYC;
+}
+
+INLINE uint32 CSusie::ReadCollision(uint32 hoff)
+{
+ uint32 col_addr=mLineCollisionAddress+(hoff/2);
+
+ uint32 data=RAM_PEEK(col_addr);
+ if(!(hoff&0x01))
+ {
+ // Upper nibble read
+ data>>=4;
+ }
+ else
+ {
+ // Lower nibble read
+ data&=0x0f;
+ }
+
+ // Increment cycle count for the read/modify/write
+ cycles_used+=SPR_RDWR_CYC;
+
+ return data;
+}
+
+
+INLINE uint32 CSusie::LineGetBits(uint32 bits)
+{
+ uint32 retval;
+
+ // Sanity, not really needed
+ // if(bits>32) return 0;
+
+ // Only return data IF there is enought bits left in the packet
+
+ //if(mLinePacketBitsLeft>(mLineShiftRegCount-bits);
+ retval&=(1<mCollision)
+ {
+ mCollision=collision;
+ }
+ // 01/05/00 V0.7 if(mSPRCOLL_Number>collision)
+ {
+ WriteCollision(hoff,mSPRCOLL_Number);
+ }
+ }
+ }
+ break;
+
+ // NORMAL
+ // 1 F is opaque
+ // 1 E is collideable
+ // 0 0 is opaque and collideable
+ // 1 allow collision detect
+ // 1 allow coll. buffer access
+ // 0 exclusive-or the data
+ case sprite_normal:
+ if(pixel!=0x00)
+ {
+ WritePixel(hoff,pixel);
+ if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide)
+ {
+ int collision=ReadCollision(hoff);
+ if(collision>mCollision)
+ {
+ mCollision=collision;
+ }
+ // 01/05/00 V0.7 if(mSPRCOLL_Number>collision)
+ {
+ WriteCollision(hoff,mSPRCOLL_Number);
+ }
+ }
+ }
+ break;
+
+ // BOUNDARY_SHADOW
+ // 0 F is opaque
+ // 0 E is collideable
+ // 0 0 is opaque and collideable
+ // 1 allow collision detect
+ // 1 allow coll. buffer access
+ // 0 exclusive-or the data
+ case sprite_boundary_shadow:
+ if(pixel!=0x00 && pixel!=0x0e && pixel!=0x0f)
+ {
+ WritePixel(hoff,pixel);
+ }
+ if(pixel!=0x00 && pixel!=0x0e)
+ {
+ if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide)
+ {
+ int collision=ReadCollision(hoff);
+ if(collision>mCollision)
+ {
+ mCollision=collision;
+ }
+ // 01/05/00 V0.7 if(mSPRCOLL_Number>collision)
+ {
+ WriteCollision(hoff,mSPRCOLL_Number);
+ }
+ }
+ }
+ break;
+
+ // SHADOW
+ // 1 F is opaque
+ // 0 E is collideable
+ // 0 0 is opaque and collideable
+ // 1 allow collision detect
+ // 1 allow coll. buffer access
+ // 0 exclusive-or the data
+ case sprite_shadow:
+ if(pixel!=0x00)
+ {
+ WritePixel(hoff,pixel);
+ }
+ if(pixel!=0x00 && pixel!=0x0e)
+ {
+ if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide)
+ {
+ int collision=ReadCollision(hoff);
+ if(collision>mCollision)
+ {
+ mCollision=collision;
+ }
+ // 01/05/00 V0.7 if(mSPRCOLL_Number>collision)
+ {
+ WriteCollision(hoff,mSPRCOLL_Number);
+ }
+ }
+ }
+ break;
+
+ // XOR SHADOW
+ // 1 F is opaque
+ // 0 E is collideable
+ // 0 0 is opaque and collideable
+ // 1 allow collision detect
+ // 1 allow coll. buffer access
+ // 1 exclusive-or the data
+ case sprite_xor_shadow:
+ if(pixel!=0x00)
+ {
+ WritePixel(hoff,ReadPixel(hoff)^pixel);
+ }
+ if(pixel!=0x00 && pixel!=0x0e)
+ {
+ if(!mSPRCOLL_Collide && !mSPRSYS_NoCollide && pixel!=0x0e)
+ {
+ int collision=ReadCollision(hoff);
+ if(collision>mCollision)
+ {
+ mCollision=collision;
+ }
+ // 01/05/00 V0.7 if(mSPRCOLL_Number>collision)
+ {
+ WriteCollision(hoff,mSPRCOLL_Number);
+ }
+ }
+ }
+ break;
+ default:
+ // _asm int 3;
+ break;
+ }
+}
+
+uint32 CSusie::LineInit(uint32 voff)
+{
+ // TRACE_SUSIE0("LineInit()");
+
+ mLineShiftReg=0;
+ mLineShiftRegCount=0;
+ mLineRepeatCount=0;
+ mLinePixel=0;
+ mLineType=line_error;
+ mLinePacketBitsLeft=0xffff;
+
+ // Initialise the temporary pointer
+
+ mTMPADR=mSPRDLINE;
+
+ // First read the Offset to the next line
+
+ uint32 offset=LineGetBits(8);
+ // TRACE_SUSIE1("LineInit() Offset=%04x",offset);
+
+ // Specify the MAXIMUM number of bits in this packet, it
+ // can terminate early but can never use more than this
+ // without ending the current packet, we count down in LineGetBits()
+
+ mLinePacketBitsLeft=(offset-1)*8;
+
+ // Literals are a special case and get their count set on a line basis
+
+ if(mSPRCTL1_Literal)
+ {
+ mLineType=line_abs_literal;
+ mLineRepeatCount=((offset-1)*8)/mSPRCTL0_PixelBits;
+ // Why is this necessary, is this compensating for the 1,1 offset bug
+ // mLineRepeatCount--;
+ }
+ // TRACE_SUSIE1("LineInit() mLineRepeatCount=$%04x",mLineRepeatCount);
+
+ // Set the line base address for use in the calls to pixel painting
+
+ if(voff>101)
+ {
+ //gError->Warning("CSusie::LineInit() Out of bounds (voff)");
+ voff=0;
+ }
+
+ mLineBaseAddress=mVIDBAS.Val16+(voff*(SCREEN_WIDTH/2));
+ mLineCollisionAddress=mCOLLBAS.Val16+(voff*(SCREEN_WIDTH/2));
+ // TRACE_SUSIE1("LineInit() mLineBaseAddress=$%04x",mLineBaseAddress);
+ // TRACE_SUSIE1("LineInit() mLineCollisionAddress=$%04x",mLineCollisionAddress);
+
+ // Return the offset to the next line
+
+ return offset;
+}
+
+uint32 CSusie::LineGetPixel()
+{
+ if(!mLineRepeatCount)
+ {
+ // Normal sprites fetch their counts on a packet basis
+ if(mLineType!=line_abs_literal)
+ {
+ uint32 literal=LineGetBits(1);
+ if(literal) mLineType=line_literal; else mLineType=line_packed;
+ }
+
+ // Pixel store is empty what should we do
+ switch(mLineType)
+ {
+ case line_abs_literal:
+ // This means end of line for us
+ mLinePixel=LINE_END;
+ return mLinePixel; // SPEEDUP
+ break;
+ case line_literal:
+ mLineRepeatCount=LineGetBits(4);
+ mLineRepeatCount++;
+ break;
+ case line_packed:
+ //
+ // From reading in between the lines only a packed line with
+ // a zero size i.e 0b00000 as a header is allowable as a packet end
+ //
+ mLineRepeatCount=LineGetBits(4);
+ if(!mLineRepeatCount)
+ {
+ mLinePixel=LINE_END;
+ }
+ else
+ {
+ mLinePixel=mPenIndex[LineGetBits(mSPRCTL0_PixelBits)];
+ }
+ mLineRepeatCount++;
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ if(mLinePixel!=LINE_END)
+ {
+ mLineRepeatCount--;
+
+ switch(mLineType)
+ {
+ case line_abs_literal:
+ mLinePixel=LineGetBits(mSPRCTL0_PixelBits);
+ // Check the special case of a zero in the last pixel
+ if(!mLineRepeatCount && !mLinePixel)
+ mLinePixel=LINE_END;
+ else
+ mLinePixel=mPenIndex[mLinePixel];
+ break;
+ case line_literal:
+ mLinePixel=mPenIndex[LineGetBits(mSPRCTL0_PixelBits)];
+ break;
+ case line_packed:
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ return mLinePixel;
+}
+
+
+void CSusie::Poke(uint32 addr,uint8 data)
+{
+ switch(addr&0xff)
+ {
+ case (TMPADRL&0xff):
+ mTMPADR.Union8.Low=data;
+ mTMPADR.Union8.High=0;
+ TRACE_SUSIE2("Poke(TMPADRL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TMPADRH&0xff):
+ mTMPADR.Union8.High=data;
+ TRACE_SUSIE2("Poke(TMPADRH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TILTACUML&0xff):
+ mTILTACUM.Union8.Low=data;
+ mTILTACUM.Union8.High=0;
+ TRACE_SUSIE2("Poke(TILTACUML,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TILTACUMH&0xff):
+ mTILTACUM.Union8.High=data;
+ TRACE_SUSIE2("Poke(TILTACUMH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (HOFFL&0xff):
+ mHOFF.Union8.Low=data;
+ mHOFF.Union8.High=0;
+ TRACE_SUSIE2("Poke(HOFFL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (HOFFH&0xff):
+ mHOFF.Union8.High=data;
+ TRACE_SUSIE2("Poke(HOFFH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (VOFFL&0xff):
+ mVOFF.Union8.Low=data;
+ mVOFF.Union8.High=0;
+ TRACE_SUSIE2("Poke(VOFFL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (VOFFH&0xff):
+ mVOFF.Union8.High=data;
+ TRACE_SUSIE2("Poke(VOFFH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (VIDBASL&0xff):
+ mVIDBAS.Union8.Low=data;
+ mVIDBAS.Union8.High=0;
+ TRACE_SUSIE2("Poke(VIDBASL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (VIDBASH&0xff):
+ mVIDBAS.Union8.High=data;
+ TRACE_SUSIE2("Poke(VIDBASH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (COLLBASL&0xff):
+ mCOLLBAS.Union8.Low=data;
+ mCOLLBAS.Union8.High=0;
+ TRACE_SUSIE2("Poke(COLLBASL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (COLLBASH&0xff):
+ mCOLLBAS.Union8.High=data;
+ TRACE_SUSIE2("Poke(COLLBASH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (VIDADRL&0xff):
+ mVIDADR.Union8.Low=data;
+ mVIDADR.Union8.High=0;
+ TRACE_SUSIE2("Poke(VIDADRL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (VIDADRH&0xff):
+ mVIDADR.Union8.High=data;
+ TRACE_SUSIE2("Poke(VIDADRH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (COLLADRL&0xff):
+ mCOLLADR.Union8.Low=data;
+ mCOLLADR.Union8.High=0;
+ TRACE_SUSIE2("Poke(COLLADRL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (COLLADRH&0xff):
+ mCOLLADR.Union8.High=data;
+ TRACE_SUSIE2("Poke(COLLADRH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SCBNEXTL&0xff):
+ mSCBNEXT.Union8.Low=data;
+ mSCBNEXT.Union8.High=0;
+ TRACE_SUSIE2("Poke(SCBNEXTL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SCBNEXTH&0xff):
+ mSCBNEXT.Union8.High=data;
+ TRACE_SUSIE2("Poke(SCBNEXTH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SPRDLINEL&0xff):
+ mSPRDLINE.Union8.Low=data;
+ mSPRDLINE.Union8.High=0;
+ TRACE_SUSIE2("Poke(SPRDLINEL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SPRDLINEH&0xff):
+ mSPRDLINE.Union8.High=data;
+ TRACE_SUSIE2("Poke(SPRDLINEH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (HPOSSTRTL&0xff):
+ mHPOSSTRT.Union8.Low=data;
+ mHPOSSTRT.Union8.High=0;
+ TRACE_SUSIE2("Poke(HPOSSTRTL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (HPOSSTRTH&0xff):
+ mHPOSSTRT.Union8.High=data;
+ TRACE_SUSIE2("Poke(HPOSSTRTH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (VPOSSTRTL&0xff):
+ mVPOSSTRT.Union8.Low=data;
+ mVPOSSTRT.Union8.High=0;
+ TRACE_SUSIE2("Poke(VPOSSTRTL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (VPOSSTRTH&0xff):
+ mVPOSSTRT.Union8.High=data;
+ TRACE_SUSIE2("Poke(VPOSSTRTH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SPRHSIZL&0xff):
+ mSPRHSIZ.Union8.Low=data;
+ mSPRHSIZ.Union8.High=0;
+ TRACE_SUSIE2("Poke(SPRHSIZL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SPRHSIZH&0xff):
+ mSPRHSIZ.Union8.High=data;
+ TRACE_SUSIE2("Poke(SPRHSIZH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SPRVSIZL&0xff):
+ mSPRVSIZ.Union8.Low=data;
+ mSPRVSIZ.Union8.High=0;
+ TRACE_SUSIE2("Poke(SPRVSIZL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SPRVSIZH&0xff):
+ mSPRVSIZ.Union8.High=data;
+ TRACE_SUSIE2("Poke(SPRVSIZH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (STRETCHL&0xff):
+ mSTRETCH.Union8.Low=data;
+ mSTRETCH.Union8.High=0;
+ TRACE_SUSIE2("Poke(STRETCHL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (STRETCHH&0xff):
+ TRACE_SUSIE2("Poke(STRETCHH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ mSTRETCH.Union8.High=data;
+ break;
+ case (TILTL&0xff):
+ mTILT.Union8.Low=data;
+ mTILT.Union8.High=0;
+ TRACE_SUSIE2("Poke(TILTL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (TILTH&0xff):
+ mTILT.Union8.High=data;
+ TRACE_SUSIE2("Poke(TILTH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SPRDOFFL&0xff):
+ TRACE_SUSIE2("Poke(SPRDOFFL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ mSPRDOFF.Union8.Low=data;
+ mSPRDOFF.Union8.High=0;
+ break;
+ case (SPRDOFFH&0xff):
+ TRACE_SUSIE2("Poke(SPRDOFFH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ mSPRDOFF.Union8.High=data;
+ break;
+ case (SPRVPOSL&0xff):
+ TRACE_SUSIE2("Poke(SPRVPOSL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ mSPRVPOS.Union8.Low=data;
+ mSPRVPOS.Union8.High=0;
+ break;
+ case (SPRVPOSH&0xff):
+ mSPRVPOS.Union8.High=data;
+ TRACE_SUSIE2("Poke(SPRVPOSH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (COLLOFFL&0xff):
+ mCOLLOFF.Union8.Low=data;
+ mCOLLOFF.Union8.High=0;
+ TRACE_SUSIE2("Poke(COLLOFFL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (COLLOFFH&0xff):
+ mCOLLOFF.Union8.High=data;
+ TRACE_SUSIE2("Poke(COLLOFFH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (VSIZACUML&0xff):
+ mVSIZACUM.Union8.Low=data;
+ mVSIZACUM.Union8.High=0;
+ TRACE_SUSIE2("Poke(VSIZACUML,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (VSIZACUMH&0xff):
+ mVSIZACUM.Union8.High=data;
+ TRACE_SUSIE2("Poke(VSIZACUMH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (HSIZOFFL&0xff):
+ mHSIZOFF.Union8.Low=data;
+ mHSIZOFF.Union8.High=0;
+ TRACE_SUSIE2("Poke(HSIZOFFL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (HSIZOFFH&0xff):
+ mHSIZOFF.Union8.High=data;
+ TRACE_SUSIE2("Poke(HSIZOFFH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (VSIZOFFL&0xff):
+ mVSIZOFF.Union8.Low=data;
+ mVSIZOFF.Union8.High=0;
+ TRACE_SUSIE2("Poke(VSIZOFFL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (VSIZOFFH&0xff):
+ mVSIZOFF.Union8.High=data;
+ TRACE_SUSIE2("Poke(VSIZOFFH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SCBADRL&0xff):
+ mSCBADR.Union8.Low=data;
+ mSCBADR.Union8.High=0;
+ TRACE_SUSIE2("Poke(SCBADRL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SCBADRH&0xff):
+ mSCBADR.Union8.High=data;
+ TRACE_SUSIE2("Poke(SCBADRH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (PROCADRL&0xff):
+ mPROCADR.Union8.Low=data;
+ mPROCADR.Union8.High=0;
+ TRACE_SUSIE2("Poke(PROCADRL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (PROCADRH&0xff):
+ mPROCADR.Union8.High=data;
+ TRACE_SUSIE2("Poke(PROCADRH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+ case (MATHD&0xff):
+ TRACE_SUSIE2("Poke(MATHD,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ mMATHABCD.Bytes.D=data;
+ // mMATHABCD.Bytes.C=0;
+ // The hardware manual says that the sign shouldnt change
+ // but if I dont do this then stun runner will hang as it
+ // does the init in the wrong order and if the previous
+ // calc left a zero there then we'll get a sign error
+ Poke(MATHC,0);
+ break;
+ case (MATHC&0xff):
+ TRACE_SUSIE2("Poke(MATHC,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ mMATHABCD.Bytes.C=data;
+ // Perform sign conversion if required
+ if(mSPRSYS_SignedMath)
+ {
+ // Account for the math bug that 0x8000 is +ve & 0x0000 is -ve by subracting 1
+ if((mMATHABCD.Words.CD-1)&0x8000)
+ {
+ uint16 conv;
+ conv=mMATHABCD.Words.CD^0xffff;
+ conv++;
+ mMATHCD_sign=-1;
+ TRACE_SUSIE2("MATH CD signed conversion complete %04x to %04x",mMATHABCD.Words.CD,conv);
+ mMATHABCD.Words.CD=conv;
+ }
+ else
+ {
+ mMATHCD_sign=1;
+ }
+ }
+ break;
+ case (MATHB&0xff):
+ TRACE_SUSIE2("Poke(MATHB,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ mMATHABCD.Bytes.B=data;
+ mMATHABCD.Bytes.A=0;
+ break;
+ case (MATHA&0xff):
+ TRACE_SUSIE2("Poke(MATHA,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ mMATHABCD.Bytes.A=data;
+ // Perform sign conversion if required
+ if(mSPRSYS_SignedMath)
+ {
+ // Account for the math bug that 0x8000 is +ve & 0x0000 is -ve by subracting 1
+ if((mMATHABCD.Words.AB-1)&0x8000)
+ {
+ uint16 conv;
+ conv=mMATHABCD.Words.AB^0xffff;
+ conv++;
+ mMATHAB_sign=-1;
+ TRACE_SUSIE2("MATH AB signed conversion complete %04x to %04x",mMATHABCD.Words.AB,conv);
+ mMATHABCD.Words.AB=conv;
+ }
+ else
+ {
+ mMATHAB_sign=1;
+ }
+ }
+ DoMathMultiply();
+ break;
+
+ case (MATHP&0xff):
+ mMATHNP.Bytes.P=data;
+ mMATHNP.Bytes.N=0;
+ TRACE_SUSIE2("Poke(MATHP,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (MATHN&0xff):
+ mMATHNP.Bytes.N=data;
+ TRACE_SUSIE2("Poke(MATHN,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+ case (MATHH&0xff):
+ mMATHEFGH.Bytes.H=data;
+ mMATHEFGH.Bytes.G=0;
+ TRACE_SUSIE2("Poke(MATHH,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (MATHG&0xff):
+ mMATHEFGH.Bytes.G=data;
+ TRACE_SUSIE2("Poke(MATHG,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (MATHF&0xff):
+ mMATHEFGH.Bytes.F=data;
+ mMATHEFGH.Bytes.E=0;
+ TRACE_SUSIE2("Poke(MATHF,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (MATHE&0xff):
+ mMATHEFGH.Bytes.E=data;
+ TRACE_SUSIE2("Poke(MATHE,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ DoMathDivide();
+ break;
+
+ case (MATHM&0xff):
+ mMATHJKLM.Bytes.M=data;
+ mMATHJKLM.Bytes.L=0;
+ mSPRSYS_Mathbit=FALSE;
+ TRACE_SUSIE2("Poke(MATHM,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (MATHL&0xff):
+ mMATHJKLM.Bytes.L=data;
+ TRACE_SUSIE2("Poke(MATHL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (MATHK&0xff):
+ mMATHJKLM.Bytes.K=data;
+ mMATHJKLM.Bytes.J=0;
+ TRACE_SUSIE2("Poke(MATHK,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (MATHJ&0xff):
+ mMATHJKLM.Bytes.J=data;
+ TRACE_SUSIE2("Poke(MATHJ,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+ case (SPRCTL0&0xff):
+ mSPRCTL0_Type=data&0x0007;
+ mSPRCTL0_Vflip=data&0x0010;
+ mSPRCTL0_Hflip=data&0x0020;
+ mSPRCTL0_PixelBits=((data&0x00c0)>>6)+1;
+ TRACE_SUSIE2("Poke(SPRCTL0,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SPRCTL1&0xff):
+ mSPRCTL1_StartLeft=data&0x0001;
+ mSPRCTL1_StartUp=data&0x0002;
+ mSPRCTL1_SkipSprite=data&0x0004;
+ mSPRCTL1_ReloadPalette=data&0x0008;
+ mSPRCTL1_ReloadDepth=(data&0x0030)>>4;
+ mSPRCTL1_Sizing=data&0x0040;
+ mSPRCTL1_Literal=data&0x0080;
+ TRACE_SUSIE2("Poke(SPRCTL1,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SPRCOLL&0xff):
+ mSPRCOLL_Number=data&0x000f;
+ mSPRCOLL_Collide=data&0x0020;
+ TRACE_SUSIE2("Poke(SPRCOLL,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SPRINIT&0xff):
+ mSPRINIT.Byte=data;
+ TRACE_SUSIE2("Poke(SPRINIT,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SUZYBUSEN&0xff):
+ mSUZYBUSEN=data&0x01;
+ TRACE_SUSIE2("Poke(SUZYBUSEN,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SPRGO&0xff):
+ mSPRGO=data&0x01;
+ mEVERON=data&0x04;
+ TRACE_SUSIE2("Poke(SPRGO,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (SPRSYS&0xff):
+ mSPRSYS_StopOnCurrent=data&0x0002;
+ if(data&0x0004) mSPRSYS_UnsafeAccess=0;
+ mSPRSYS_LeftHand=data&0x0008;
+ mSPRSYS_VStretch=data&0x0010;
+ mSPRSYS_NoCollide=data&0x0020;
+ mSPRSYS_Accumulate=data&0x0040;
+ mSPRSYS_SignedMath=data&0x0080;
+ TRACE_SUSIE2("Poke(SPRSYS,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+ // Cartridge writing ports
+
+ case (RCART0&0xff):
+ mSystem.Poke_CARTB0(data);
+ TRACE_SUSIE2("Poke(RCART0,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+ case (RCART1&0xff):
+ mSystem.Poke_CARTB1(data);
+ TRACE_SUSIE2("Poke(RCART1,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+ // These are not so important, so lets ignore them for the moment
+
+ case (LEDS&0xff):
+ case (PPORTSTAT&0xff):
+ case (PPORTDATA&0xff):
+ case (HOWIE&0xff):
+ TRACE_SUSIE2("Poke(LEDS/PPORTSTST/PPORTDATA/HOWIE,%02x) at PC=$%04x",data,mSystem.mCpu->GetPC());
+ break;
+
+ // Errors on read only register accesses
+
+ case (SUZYHREV&0xff):
+ case (JOYSTICK&0xff):
+ case (SWITCHES&0xff):
+ TRACE_SUSIE3("Poke(%04x,%02x) - Poke to read only register location at PC=%04x",addr,data,mSystem.mCpu->GetPC());
+ break;
+
+ // Errors on illegal location accesses
+
+ default:
+ TRACE_SUSIE3("Poke(%04x,%02x) - Poke to illegal location at PC=%04x",addr,data,mSystem.mCpu->GetPC());
+ break;
+ }
+}
+
+uint8 CSusie::Peek(uint32 addr)
+{
+ uint8 retval=0;
+
+ switch(addr&0xff)
+ {
+ case (TMPADRL&0xff):
+ retval=mTMPADR.Union8.Low;
+ TRACE_SUSIE2("Peek(TMPADRL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (TMPADRH&0xff):
+ retval=mTMPADR.Union8.High;
+ TRACE_SUSIE2("Peek(TMPADRH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (TILTACUML&0xff):
+ retval=mTILTACUM.Union8.Low;
+ TRACE_SUSIE2("Peek(TILTACUML)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (TILTACUMH&0xff):
+ retval=mTILTACUM.Union8.High;
+ TRACE_SUSIE2("Peek(TILTACUMH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (HOFFL&0xff):
+ retval=mHOFF.Union8.Low;
+ TRACE_SUSIE2("Peek(HOFFL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (HOFFH&0xff):
+ retval=mHOFF.Union8.High;
+ TRACE_SUSIE2("Peek(HOFFH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (VOFFL&0xff):
+ retval=mVOFF.Union8.Low;
+ TRACE_SUSIE2("Peek(VOFFL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (VOFFH&0xff):
+ retval=mVOFF.Union8.High;
+ TRACE_SUSIE2("Peek(VOFFH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (VIDBASL&0xff):
+ retval=mVIDBAS.Union8.Low;
+ TRACE_SUSIE2("Peek(VIDBASL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (VIDBASH&0xff):
+ retval=mVIDBAS.Union8.High;
+ TRACE_SUSIE2("Peek(VIDBASH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (COLLBASL&0xff):
+ retval=mCOLLBAS.Union8.Low;
+ TRACE_SUSIE2("Peek(COLLBASL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (COLLBASH&0xff):
+ retval=mCOLLBAS.Union8.High;
+ TRACE_SUSIE2("Peek(COLLBASH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (VIDADRL&0xff):
+ retval=mVIDADR.Union8.Low;
+ TRACE_SUSIE2("Peek(VIDADRL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (VIDADRH&0xff):
+ retval=mVIDADR.Union8.High;
+ TRACE_SUSIE2("Peek(VIDADRH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (COLLADRL&0xff):
+ retval=mCOLLADR.Union8.Low;
+ TRACE_SUSIE2("Peek(COLLADRL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (COLLADRH&0xff):
+ retval=mCOLLADR.Union8.High;
+ TRACE_SUSIE2("Peek(COLLADRH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SCBNEXTL&0xff):
+ retval=mSCBNEXT.Union8.Low;
+ TRACE_SUSIE2("Peek(SCBNEXTL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SCBNEXTH&0xff):
+ retval=mSCBNEXT.Union8.High;
+ TRACE_SUSIE2("Peek(SCBNEXTH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SPRDLINEL&0xff):
+ retval=mSPRDLINE.Union8.Low;
+ TRACE_SUSIE2("Peek(SPRDLINEL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SPRDLINEH&0xff):
+ retval=mSPRDLINE.Union8.High;
+ TRACE_SUSIE2("Peek(SPRDLINEH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (HPOSSTRTL&0xff):
+ retval=mHPOSSTRT.Union8.Low;
+ TRACE_SUSIE2("Peek(HPOSSTRTL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (HPOSSTRTH&0xff):
+ retval=mHPOSSTRT.Union8.High;
+ TRACE_SUSIE2("Peek(HPOSSTRTH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (VPOSSTRTL&0xff):
+ retval=mVPOSSTRT.Union8.Low;
+ TRACE_SUSIE2("Peek(VPOSSTRTL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (VPOSSTRTH&0xff):
+ retval=mVPOSSTRT.Union8.High;
+ TRACE_SUSIE2("Peek(VPOSSTRTH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SPRHSIZL&0xff):
+ retval=mSPRHSIZ.Union8.Low;
+ TRACE_SUSIE2("Peek(SPRHSIZL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SPRHSIZH&0xff):
+ retval=mSPRHSIZ.Union8.High;
+ TRACE_SUSIE2("Peek(SPRHSIZH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SPRVSIZL&0xff):
+ retval=mSPRVSIZ.Union8.Low;
+ TRACE_SUSIE2("Peek(SPRVSIZL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SPRVSIZH&0xff):
+ retval=mSPRVSIZ.Union8.High;
+ TRACE_SUSIE2("Peek(SPRVSIZH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (STRETCHL&0xff):
+ retval=mSTRETCH.Union8.Low;
+ TRACE_SUSIE2("Peek(STRETCHL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (STRETCHH&0xff):
+ retval=mSTRETCH.Union8.High;
+ TRACE_SUSIE2("Peek(STRETCHH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (TILTL&0xff):
+ retval=mTILT.Union8.Low;
+ TRACE_SUSIE2("Peek(TILTL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (TILTH&0xff):
+ retval=mTILT.Union8.High;
+ TRACE_SUSIE2("Peek(TILTH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SPRDOFFL&0xff):
+ retval=mSPRDOFF.Union8.Low;
+ TRACE_SUSIE2("Peek(SPRDOFFL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SPRDOFFH&0xff):
+ retval=mSPRDOFF.Union8.High;
+ TRACE_SUSIE2("Peek(SPRDOFFH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SPRVPOSL&0xff):
+ retval=mSPRVPOS.Union8.Low;
+ TRACE_SUSIE2("Peek(SPRVPOSL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SPRVPOSH&0xff):
+ retval=mSPRVPOS.Union8.High;
+ TRACE_SUSIE2("Peek(SPRVPOSH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (COLLOFFL&0xff):
+ retval=mCOLLOFF.Union8.Low;
+ TRACE_SUSIE2("Peek(COLLOFFL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (COLLOFFH&0xff):
+ retval=mCOLLOFF.Union8.High;
+ TRACE_SUSIE2("Peek(COLLOFFH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (VSIZACUML&0xff):
+ retval=mVSIZACUM.Union8.Low;
+ TRACE_SUSIE2("Peek(VSIZACUML)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (VSIZACUMH&0xff):
+ retval=mVSIZACUM.Union8.High;
+ TRACE_SUSIE2("Peek(VSIZACUMH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (HSIZOFFL&0xff):
+ retval=mHSIZOFF.Union8.Low;
+ TRACE_SUSIE2("Peek(HSIZOFFL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (HSIZOFFH&0xff):
+ retval=mHSIZOFF.Union8.High;
+ TRACE_SUSIE2("Peek(HSIZOFFH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (VSIZOFFL&0xff):
+ retval=mVSIZOFF.Union8.Low;
+ TRACE_SUSIE2("Peek(VSIZOFFL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (VSIZOFFH&0xff):
+ retval=mVSIZOFF.Union8.High;
+ TRACE_SUSIE2("Peek(VSIZOFFH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SCBADRL&0xff):
+ retval=mSCBADR.Union8.Low;
+ TRACE_SUSIE2("Peek(SCBADRL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (SCBADRH&0xff):
+ retval=mSCBADR.Union8.High;
+ TRACE_SUSIE2("Peek(SCBADRH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (PROCADRL&0xff):
+ retval=mPROCADR.Union8.Low;
+ TRACE_SUSIE2("Peek(PROCADRL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (PROCADRH&0xff):
+ retval=mPROCADR.Union8.High;
+ TRACE_SUSIE2("Peek(PROCADRH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+
+ case (MATHD&0xff):
+ retval=mMATHABCD.Bytes.D;
+ TRACE_SUSIE2("Peek(MATHD)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (MATHC&0xff):
+ retval=mMATHABCD.Bytes.C;
+ TRACE_SUSIE2("Peek(MATHC)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (MATHB&0xff):
+ retval=mMATHABCD.Bytes.B;
+ TRACE_SUSIE2("Peek(MATHB)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (MATHA&0xff):
+ retval=mMATHABCD.Bytes.A;
+ TRACE_SUSIE2("Peek(MATHA)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+
+ case (MATHP&0xff):
+ retval=mMATHNP.Bytes.P;
+ TRACE_SUSIE2("Peek(MATHP)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (MATHN&0xff):
+ retval=mMATHNP.Bytes.N;
+ TRACE_SUSIE2("Peek(MATHN)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+
+ case (MATHH&0xff):
+ retval=mMATHEFGH.Bytes.H;
+ TRACE_SUSIE2("Peek(MATHH)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (MATHG&0xff):
+ retval=mMATHEFGH.Bytes.G;
+ TRACE_SUSIE2("Peek(MATHG)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (MATHF&0xff):
+ retval=mMATHEFGH.Bytes.F;
+ TRACE_SUSIE2("Peek(MATHF)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (MATHE&0xff):
+ retval=mMATHEFGH.Bytes.E;
+ TRACE_SUSIE2("Peek(MATHE)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+
+ case (MATHM&0xff):
+ retval=mMATHJKLM.Bytes.M;
+ TRACE_SUSIE2("Peek(MATHM)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (MATHL&0xff):
+ retval=mMATHJKLM.Bytes.L;
+ TRACE_SUSIE2("Peek(MATHL)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (MATHK&0xff):
+ retval=mMATHJKLM.Bytes.K;
+ TRACE_SUSIE2("Peek(MATHK)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (MATHJ&0xff):
+ retval=mMATHJKLM.Bytes.J;
+ TRACE_SUSIE2("Peek(MATHJ)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+
+ case (SUZYHREV&0xff):
+ retval=0x01;
+ TRACE_SUSIE2("Peek(SUZYHREV)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+
+ case (SPRSYS&0xff):
+ retval=0x0000;
+ // retval+=(mSPRSYS_Status)?0x0001:0x0000;
+ retval+= (mSystem.gSuzieDoneTime)?0x0001:0x0000;
+ retval+=(mSPRSYS_StopOnCurrent)?0x0002:0x0000;
+ retval+=(mSPRSYS_UnsafeAccess)?0x0004:0x0000;
+ retval+=(mSPRSYS_LeftHand)?0x0008:0x0000;
+ retval+=(mSPRSYS_VStretch)?0x0010:0x0000;
+ retval+=(mSPRSYS_LastCarry)?0x0020:0x0000;
+ retval+=(mSPRSYS_Mathbit)?0x0040:0x0000;
+ retval+=(mSPRSYS_MathInProgress)?0x0080:0x0000;
+ TRACE_SUSIE2("Peek(SPRSYS)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+
+ case (JOYSTICK&0xff):
+ if(mSPRSYS_LeftHand)
+ {
+ retval= mJOYSTICK.Byte;
+ }
+ else
+ {
+ TJOYSTICK Modified=mJOYSTICK;
+ Modified.Bits.Left=mJOYSTICK.Bits.Right;
+ Modified.Bits.Right=mJOYSTICK.Bits.Left;
+ Modified.Bits.Down=mJOYSTICK.Bits.Up;
+ Modified.Bits.Up=mJOYSTICK.Bits.Down;
+ retval= Modified.Byte;
+ }
+ // TRACE_SUSIE2("Peek(JOYSTICK)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+
+
+ case (SWITCHES&0xff):
+ retval=mSWITCHES.Byte;
+ // TRACE_SUSIE2("Peek(SWITCHES)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+
+ // Cartridge reading ports
+
+ case (RCART0&0xff):
+ retval=mSystem.Peek_CARTB0();
+ // TRACE_SUSIE2("Peek(RCART0)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+ case (RCART1&0xff):
+ retval=mSystem.Peek_CARTB1();
+ // TRACE_SUSIE2("Peek(RCART1)=$%02x at PC=$%04x",retval,mSystem.mCpu->GetPC());
+ return retval;
+ break;
+
+ // These are no so important so lets ignore them for the moment
+
+ case (LEDS&0xff):
+ case (PPORTSTAT&0xff):
+ case (PPORTDATA&0xff):
+ case (HOWIE&0xff):
+ TRACE_SUSIE1("Peek(LEDS/PPORTSTAT/PPORTDATA) at PC=$%04x",mSystem.mCpu->GetPC());
+ break;
+
+ // Errors on write only register accesses
+
+ case (SPRCTL0&0xff):
+ case (SPRCTL1&0xff):
+ case (SPRCOLL&0xff):
+ case (SPRINIT&0xff):
+ case (SUZYBUSEN&0xff):
+ case (SPRGO&0xff):
+ TRACE_SUSIE2("Peek(%04x) - Peek from write only register location at PC=$%04x",addr,mSystem.mCpu->GetPC());
+ break;
+
+ // Errors on illegal location accesses
+
+ default:
+ TRACE_SUSIE2("Peek(%04x) - Peek from illegal location at PC=$%04x",addr,mSystem.mCpu->GetPC());
+ break;
+ }
+
+ return 0xff;
+}
diff --git a/lynx/susie.h b/lynx/susie.h
new file mode 100644
index 0000000000..ccd03132d3
--- /dev/null
+++ b/lynx/susie.h
@@ -0,0 +1,445 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// Susie object header file //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This header file provides the interface definition for the Suzy class //
+// which provides math and sprite support to the emulator //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef SUSIE_H
+#define SUSIE_H
+
+#ifdef TRACE_SUSIE
+
+#define TRACE_SUSIE0(msg) _RPT1(_CRT_WARN,"CSusie::"msg" (Time=%012d)\n",gSystemCycleCount)
+#define TRACE_SUSIE1(msg,arg1) _RPT2(_CRT_WARN,"CSusie::"msg" (Time=%012d)\n",arg1,gSystemCycleCount)
+#define TRACE_SUSIE2(msg,arg1,arg2) _RPT3(_CRT_WARN,"CSusie::"msg" (Time=%012d)\n",arg1,arg2,gSystemCycleCount)
+#define TRACE_SUSIE3(msg,arg1,arg2,arg3) _RPT4(_CRT_WARN,"CSusie::"msg" (Time=%012d)\n",arg1,arg2,arg3,gSystemCycleCount)
+
+#else
+
+#define TRACE_SUSIE0(msg)
+#define TRACE_SUSIE1(msg,arg1)
+#define TRACE_SUSIE2(msg,arg1,arg2)
+#define TRACE_SUSIE3(msg,arg1,arg2,arg3)
+
+#endif
+
+class CSystem;
+
+#define SUSIE_START 0xfc00
+#define SUSIE_SIZE 0x100
+
+#define SCREEN_WIDTH 160
+#define SCREEN_HEIGHT 102
+
+#define LINE_END 0x80
+
+//
+// Define button values
+//
+
+#define BUTTON_A 0x0001
+#define BUTTON_B 0x0002
+#define BUTTON_OPT2 0x0004
+#define BUTTON_OPT1 0x0008
+#define BUTTON_LEFT 0x0010
+#define BUTTON_RIGHT 0x0020
+#define BUTTON_UP 0x0040
+#define BUTTON_DOWN 0x0080
+#define BUTTON_PAUSE 0x0100
+
+
+enum {line_error=0,line_abs_literal,line_literal,line_packed};
+enum {math_finished=0,math_divide,math_multiply,math_init_divide,math_init_multiply};
+
+enum {sprite_background_shadow=0,
+ sprite_background_noncollide,
+ sprite_boundary_shadow,
+ sprite_boundary,
+ sprite_normal,
+ sprite_noncollide,
+ sprite_xor_shadow,
+ sprite_shadow};
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+#ifdef MSB_FIRST
+ uint8 Fc1:1;
+ uint8 Fc2:1;
+ uint8 Fc3:1;
+ uint8 reserved:1;
+ uint8 Ac1:1;
+ uint8 Ac2:1;
+ uint8 Ac3:1;
+ uint8 Ac4:1;
+#else
+ uint8 Ac4:1;
+ uint8 Ac3:1;
+ uint8 Ac2:1;
+ uint8 Ac1:1;
+ uint8 reserved:1;
+ uint8 Fc3:1;
+ uint8 Fc2:1;
+ uint8 Fc1:1;
+#endif
+ }Bits;
+ uint8 Byte;
+ };
+}TSPRINIT;
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+#ifdef MSB_FIRST
+ uint8 Up:1;
+ uint8 Down:1;
+ uint8 Left:1;
+ uint8 Right:1;
+ uint8 Option1:1;
+ uint8 Option2:1;
+ uint8 Inside:1;
+ uint8 Outside:1;
+#else
+ uint8 Outside:1;
+ uint8 Inside:1;
+ uint8 Option2:1;
+ uint8 Option1:1;
+ uint8 Right:1;
+ uint8 Left:1;
+ uint8 Down:1;
+ uint8 Up:1;
+#endif
+ }Bits;
+ uint8 Byte;
+ };
+}TJOYSTICK;
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+#ifdef MSB_FIRST
+ uint8 spare:5;
+ uint8 Cart1IO:1;
+ uint8 Cart0IO:1;
+ uint8 Pause:1;
+#else
+ uint8 Pause:1;
+ uint8 Cart0IO:1;
+ uint8 Cart1IO:1;
+ uint8 spare:5;
+#endif
+ }Bits;
+ uint8 Byte;
+ };
+}TSWITCHES;
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+#ifdef MSB_FIRST
+ uint8 A;
+ uint8 B;
+ uint8 C;
+ uint8 D;
+#else
+ uint8 D;
+ uint8 C;
+ uint8 B;
+ uint8 A;
+#endif
+ }Bytes;
+ struct
+ {
+#ifdef MSB_FIRST
+ uint16 AB;
+ uint16 CD;
+#else
+ uint16 CD;
+ uint16 AB;
+#endif
+ }Words;
+ uint32 Long;
+ };
+}TMATHABCD;
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+#ifdef MSB_FIRST
+ uint8 E;
+ uint8 F;
+ uint8 G;
+ uint8 H;
+#else
+ uint8 H;
+ uint8 G;
+ uint8 F;
+ uint8 E;
+#endif
+ }Bytes;
+ struct
+ {
+#ifdef MSB_FIRST
+ uint16 EF;
+ uint16 GH;
+#else
+ uint16 GH;
+ uint16 EF;
+#endif
+ }Words;
+ uint32 Long;
+ };
+}TMATHEFGH;
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+#ifdef MSB_FIRST
+ uint8 J;
+ uint8 K;
+ uint8 L;
+ uint8 M;
+#else
+ uint8 M;
+ uint8 L;
+ uint8 K;
+ uint8 J;
+#endif
+ }Bytes;
+ struct
+ {
+#ifdef MSB_FIRST
+ uint16 JK;
+ uint16 LM;
+#else
+ uint16 LM;
+ uint16 JK;
+#endif
+ }Words;
+ uint32 Long;
+ };
+}TMATHJKLM;
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+#ifdef MSB_FIRST
+ uint8 xx2;
+ uint8 xx1;
+ uint8 N;
+ uint8 P;
+#else
+ uint8 P;
+ uint8 N;
+ uint8 xx1;
+ uint8 xx2;
+#endif
+ }Bytes;
+ struct
+ {
+#ifdef MSB_FIRST
+ uint16 xx1;
+ uint16 NP;
+#else
+ uint16 NP;
+ uint16 xx1;
+#endif
+ }Words;
+ uint32 Long;
+ };
+}TMATHNP;
+
+
+class CSusie : public CLynxBase
+{
+ public:
+ CSusie(CSystem& parent) MDFN_COLD;
+ ~CSusie() MDFN_COLD;
+
+ void Reset(void) MDFN_COLD;
+
+ uint8 Peek(uint32 addr);
+ void Poke(uint32 addr,uint8 data);
+ uint32 ReadCycle(void) {return 9;};
+ uint32 WriteCycle(void) {return 5;};
+ uint32 ObjectSize(void) {return SUSIE_SIZE;};
+
+ void SetButtonData(uint32 data) {mJOYSTICK.Byte=(uint8)data;mSWITCHES.Byte=(uint8)(data>>8);};
+ uint32 GetButtonData(void) {return mJOYSTICK.Byte+(mSWITCHES.Byte<<8);};
+
+ uint32 PaintSprites(void);
+
+ private:
+ void DoMathDivide(void);
+ void DoMathMultiply(void);
+ uint32 LineInit(uint32 voff);
+ uint32 LineGetPixel(void);
+ uint32 LineGetBits(uint32 bits);
+
+ void ProcessPixel(uint32 hoff,uint32 pixel);
+ void WritePixel(uint32 hoff,uint32 pixel);
+ uint32 ReadPixel(uint32 hoff);
+ void WriteCollision(uint32 hoff,uint32 pixel);
+ uint32 ReadCollision(uint32 hoff);
+
+ private:
+ CSystem& mSystem;
+
+ uint32 cycles_used;
+
+ Uuint16 mTMPADR; // ENG
+ Uuint16 mTILTACUM; // ENG
+ Uuint16 mHOFF; // CPU
+ Uuint16 mVOFF; // CPU
+ Uuint16 mVIDBAS; // CPU
+ Uuint16 mCOLLBAS; // CPU
+ Uuint16 mVIDADR; // ENG
+ Uuint16 mCOLLADR; // ENG
+ Uuint16 mSCBNEXT; // SCB
+ Uuint16 mSPRDLINE; // SCB
+ Uuint16 mHPOSSTRT; // SCB
+ Uuint16 mVPOSSTRT; // SCB
+ Uuint16 mSPRHSIZ; // SCB
+ Uuint16 mSPRVSIZ; // SCB
+ Uuint16 mSTRETCH; // ENG
+ Uuint16 mTILT; // ENG
+ Uuint16 mSPRDOFF; // ENG
+ Uuint16 mSPRVPOS; // ENG
+ Uuint16 mCOLLOFF; // CPU
+ Uuint16 mVSIZACUM; // ENG
+ Uuint16 mHSIZACUM; // K.s creation
+ Uuint16 mHSIZOFF; // CPU
+ Uuint16 mVSIZOFF; // CPU
+ Uuint16 mSCBADR; // ENG
+ Uuint16 mPROCADR; // ENG
+
+ TMATHABCD mMATHABCD; // ENG
+ TMATHEFGH mMATHEFGH; // ENG
+ TMATHJKLM mMATHJKLM; // ENG
+ TMATHNP mMATHNP; // ENG
+ int mMATHAB_sign;
+ int mMATHCD_sign;
+ int mMATHEFGH_sign;
+
+ int mSPRCTL0_Type; // SCB
+ int mSPRCTL0_Vflip;
+ int mSPRCTL0_Hflip;
+ int mSPRCTL0_PixelBits;
+
+ int mSPRCTL1_StartLeft; // SCB
+ int mSPRCTL1_StartUp;
+ int mSPRCTL1_SkipSprite;
+ int mSPRCTL1_ReloadPalette;
+ int mSPRCTL1_ReloadDepth;
+ int mSPRCTL1_Sizing;
+ int mSPRCTL1_Literal;
+
+ int mSPRCOLL_Number; //CPU
+ int mSPRCOLL_Collide;
+
+ int mSPRSYS_StopOnCurrent; //CPU
+ int mSPRSYS_LeftHand;
+ int mSPRSYS_VStretch;
+ int mSPRSYS_NoCollide;
+ int mSPRSYS_Accumulate;
+ int mSPRSYS_SignedMath;
+ int mSPRSYS_Status;
+ int mSPRSYS_UnsafeAccess;
+ int mSPRSYS_LastCarry;
+ int mSPRSYS_Mathbit;
+ int mSPRSYS_MathInProgress;
+
+ uint32 mSUZYBUSEN; // CPU
+
+ TSPRINIT mSPRINIT; // CPU
+
+ uint32 mSPRGO; // CPU
+ int mEVERON;
+
+ uint8 mPenIndex[16]; // SCB
+
+ // Line rendering related variables
+
+ uint32 mLineType;
+ uint32 mLineShiftRegCount;
+ uint32 mLineShiftReg;
+ uint32 mLineRepeatCount;
+ uint32 mLinePixel;
+ uint32 mLinePacketBitsLeft;
+
+ int mCollision;
+
+ uint8 *mRamPointer;
+
+ uint32 mLineBaseAddress;
+ uint32 mLineCollisionAddress;
+
+ int hquadoff, vquadoff;
+
+ // Joystick switches
+
+ TJOYSTICK mJOYSTICK;
+ TSWITCHES mSWITCHES;
+};
+
+#endif
+
diff --git a/lynx/sysbase.h b/lynx/sysbase.h
new file mode 100644
index 0000000000..cd19f157b2
--- /dev/null
+++ b/lynx/sysbase.h
@@ -0,0 +1,73 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// Systembase object class definition //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This header file provides the interface definition for the systembase //
+// class that is required to get around cross dependencies between //
+// cpu/mikie/system classes //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef SYSBASE_H
+#define SYSBASE_H
+
+
+class CSystemBase
+{
+ // Function members
+
+ public:
+ virtual ~CSystemBase() {};
+
+ public:
+ virtual void Reset()=0;
+ virtual void Poke_CPU(uint32 addr,uint8 data)=0;
+ virtual uint8 Peek_CPU(uint32 addr)=0;
+ virtual void PokeW_CPU(uint32 addr,uint16 data)=0;
+ virtual uint16 PeekW_CPU(uint32 addr)=0;
+
+ virtual void Poke_RAM(uint32 addr,uint8 data)=0;
+ virtual uint8 Peek_RAM(uint32 addr)=0;
+ virtual void PokeW_RAM(uint32 addr,uint16 data)=0;
+ virtual uint16 PeekW_RAM(uint32 addr)=0;
+
+ virtual uint8* GetRamPointer()=0;
+
+};
+
+#endif
diff --git a/lynx/system.cpp b/lynx/system.cpp
new file mode 100644
index 0000000000..e8ba339ed4
--- /dev/null
+++ b/lynx/system.cpp
@@ -0,0 +1,264 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// System object class //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This class provides the glue to bind of of the emulation objects //
+// together via peek/poke handlers and pass thru interfaces to lower //
+// objects, all control of the emulator is done via this class. Update() //
+// does most of the work and each call emulates one CPU instruction and //
+// updates all of the relevant hardware if required. It must be remembered //
+// that if that instruction involves setting SPRGO then, it will cause a //
+// sprite painting operation and then a corresponding update of all of the //
+// hardware which will usually involve recursive calls to Update, see //
+// Mikey SPRGO code for more details. //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#define SYSTEM_CPP
+
+//#include
+//#define TRACE_SYSTEM
+
+#include "system.h"
+
+CSystem::CSystem(const uint8 *game, uint32 gamesize, const uint8 *bios, uint32 biossize, int pagesize0, int pagesize1, bool lowpass)
+{
+ // load lynxboot.img
+ mRom = new CRom(bios, biossize);
+
+ mCart = new CCart(game, gamesize, pagesize0, pagesize1);
+ mRam = new CRam();
+
+ mMikie = new CMikie(*this);
+ mSusie = new CSusie(*this);
+
+ // Instantiate the memory map handler
+ mMemMap = new CMemMap(*this);
+
+ // Now the handlers are set we can instantiate the CPU as is will use handlers on reset
+ mCpu = new C65C02(*this);
+
+ mMikie->mikbuf.set_sample_rate(44100, 60);
+ mMikie->mikbuf.clock_rate((long int)(16000000 / 4));
+ mMikie->mikbuf.bass_freq(60);
+ mMikie->miksynth.volume(0.50);
+ mMikie->miksynth.treble_eq(lowpass ? -35 : 0);
+
+ // Now init is complete do a reset, this will cause many things to be reset twice
+ Reset();
+}
+
+CSystem::~CSystem()
+{
+ delete mCart;
+ delete mRom;
+ delete mRam;
+ delete mCpu;
+ delete mMikie;
+ delete mSusie;
+ delete mMemMap;
+}
+
+void CSystem::Reset()
+{
+ gSystemCycleCount=0;
+ gNextTimerEvent=0;
+ // gCPUBootAddress=0;
+ gSystemIRQ=FALSE;
+ gSystemNMI=FALSE;
+ gSystemCPUSleep=FALSE;
+ gSystemHalt=FALSE;
+ gSuzieDoneTime = 0;
+
+ mMemMap->Reset();
+ mCart->Reset();
+ mRom->Reset();
+ mRam->Reset();
+ mMikie->Reset();
+ mSusie->Reset();
+ mCpu->Reset();
+}
+
+/*
+static int Load(MDFNFILE *fp)
+{
+ try
+ {
+ lynxie = new CSystem(fp->data, fp->size);
+
+ switch(lynxie->CartGetRotate())
+ {
+ case CART_ROTATE_LEFT:
+ MDFNGameInfo->rotated = MDFN_ROTATE270;
+ break;
+
+ case CART_ROTATE_RIGHT:
+ MDFNGameInfo->rotated = MDFN_ROTATE90;
+ break;
+ }
+
+ memcpy(MDFNGameInfo->MD5, lynxie->mCart->MD5, 16);
+ MDFNGameInfo->GameSetMD5Valid = FALSE;
+
+ MDFN_printf(_("ROM: %dKiB\n"), (lynxie->mCart->InfoROMSize + 1023) / 1024);
+ MDFN_printf(_("ROM CRC32: 0x%08x\n"), lynxie->mCart->CRC32());
+ MDFN_printf(_("ROM MD5: 0x%s\n"), md5_context::asciistr(MDFNGameInfo->MD5, 0).c_str());
+
+ MDFNGameInfo->fps = (uint32)(59.8 * 65536 * 256);
+
+ if(MDFN_GetSettingB("lynx.lowpass"))
+ {
+ lynxie->mMikie->miksynth.treble_eq(-35);
+ }
+ else
+ {
+ lynxie->mMikie->miksynth.treble_eq(0);
+ }
+
+}
+*/
+
+void CSystem::Advance(int buttons, uint32 *vbuff, int16 *sbuff, int &sbuffsize)
+{
+ // this check needs to occur at least once every 250 million cycles or better
+ mMikie->CheckWrap();
+
+
+ SetButtonData(buttons);
+
+ uint32 start = gSystemCycleCount;
+
+ // audio start frame
+ mMikie->startTS = start;
+
+ mMikie->mpDisplayCurrent = vbuff;
+
+ // go to next frame end, or no more than 200,000 cycles to avoid exploding the output buffer (was set at 60ms limit)
+ while (mMikie->mpDisplayCurrent && gSystemCycleCount - start < 200000)
+ // while (gSystemCycleCount - start < 700000) // what's the magic significance?
+ {
+ Update();
+ }
+
+ // total cycles executed is now gSystemCycleCount - start
+ mMikie->mikbuf.end_frame((gSystemCycleCount - start) >> 2);
+ sbuffsize = mMikie->mikbuf.read_samples(sbuff, sbuffsize) / 2;
+
+}
+
+
+/*
+static MDFNSetting LynxSettings[] =
+{
+ { "lynx.rotateinput", MDFNSF_NOFLAGS, gettext_noop("Virtually rotate D-pad along with screen."), NULL, MDFNST_BOOL, "1" },
+ { "lynx.lowpass", MDFNSF_CAT_SOUND, gettext_noop("Enable sound output lowpass filter."), NULL, MDFNST_BOOL, "1" },
+ { NULL }
+};
+*/
+
+/*
+static const InputDeviceInputInfoStruct IDII[] =
+{
+ { "a", "A (outer)", 8, IDIT_BUTTON_CAN_RAPID, NULL },
+ { "b", "B (inner)", 7, IDIT_BUTTON_CAN_RAPID, NULL },
+ { "option_2", "Option 2 (lower)", 5, IDIT_BUTTON_CAN_RAPID, NULL },
+ { "option_1", "Option 1 (upper)", 4, IDIT_BUTTON_CAN_RAPID, NULL },
+
+ { "left", "LEFT ←", 2, IDIT_BUTTON, "right", { "up", "right", "down" } },
+ { "right", "RIGHT →", 3, IDIT_BUTTON, "left", { "down", "left", "up" } },
+ { "up", "UP ↑", 0, IDIT_BUTTON, "down", { "right", "down", "left" } },
+ { "down", "DOWN ↓", 1, IDIT_BUTTON, "up", { "left", "up", "right" } },
+ { "pause", "PAUSE", 6, IDIT_BUTTON, NULL },
+};
+*/
+
+/*
+static const FileExtensionSpecStruct KnownExtensions[] =
+{
+ { ".lnx", gettext_noop("Atari Lynx ROM Image") },
+ { NULL, NULL }
+};
+*/
+
+/*
+MDFNGI EmulatedLynx =
+{
+ "lynx",
+ "Atari Lynx",
+ KnownExtensions,
+ MODPRIO_INTERNAL_HIGH,
+ NULL,
+ &InputInfo,
+ Load,
+ TestMagic,
+ NULL,
+ NULL,
+ CloseGame,
+ SetLayerEnableMask,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ false,
+ StateAction,
+ Emulate,
+ SetInput,
+ DoSimpleCommand,
+ LynxSettings,
+ MDFN_MASTERCLOCK_FIXED(16000000),
+ 0,
+
+ false, // Multires possible?
+
+ 160, // lcm_width
+ 102, // lcm_height
+ NULL, // Dummy
+
+
+ 160, // Nominal width
+ 102, // Nominal height
+
+ 160, // Framebuffer width
+ 102, // Framebuffer height
+
+ 2, // Number of output sound channels
+};
+*/
diff --git a/lynx/system.h b/lynx/system.h
new file mode 100644
index 0000000000..a3ce564eca
--- /dev/null
+++ b/lynx/system.h
@@ -0,0 +1,220 @@
+//
+// Copyright (c) 2004 K. Wilkins
+//
+// This software is provided 'as-is', without any express or implied warranty.
+// In no event will the authors be held liable for any damages arising from
+// the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+//////////////////////////////////////////////////////////////////////////////
+// Handy - An Atari Lynx Emulator //
+// Copyright (c) 1996,1997 //
+// K. Wilkins //
+//////////////////////////////////////////////////////////////////////////////
+// System object header file //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// This header file provides the interface definition and inline code for //
+// the system object, this object if what binds together all of the Handy //
+// hardware enmulation objects, its the glue that holds the system together //
+// //
+// K. Wilkins //
+// August 1997 //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// Revision History: //
+// ----------------- //
+// //
+// 01Aug1997 KW Document header added & class documented. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef SYSTEM_H
+#define SYSTEM_H
+
+#include "machine.h"
+
+#include
+
+#define HANDY_SYSTEM_FREQ 16000000
+#define HANDY_TIMER_FREQ 20
+
+#define HANDY_FILETYPE_LNX 0
+#define HANDY_FILETYPE_HOMEBREW 1
+#define HANDY_FILETYPE_SNAPSHOT 2
+#define HANDY_FILETYPE_ILLEGAL 3
+
+#define HANDY_SCREEN_WIDTH 160
+#define HANDY_SCREEN_HEIGHT 102
+//
+// Define the global variable list
+//
+
+/*
+#ifdef SYSTEM_CPP
+uint32 gSuzieDoneTime = 0;
+uint32 gSystemCycleCount=0;
+uint32 gNextTimerEvent=0;
+uint32 gCPUBootAddress=0;
+uint32 gSystemIRQ=FALSE;
+uint32 gSystemNMI=FALSE;
+uint32 gSystemCPUSleep=FALSE;
+uint32 gSystemHalt=FALSE;
+#else
+extern uint32 gSystemCycleCount;
+extern uint32 gSuzieDoneTime;
+extern uint32 gNextTimerEvent;
+extern uint32 gCPUBootAddress;
+extern uint32 gSystemIRQ;
+extern uint32 gSystemNMI;
+extern uint32 gSystemCPUSleep;
+extern uint32 gSystemHalt;
+#endif
+*/
+
+//
+// Define the interfaces before we start pulling in the classes
+// as many classes look for articles from the interfaces to
+// allow compilation
+
+#include "sysbase.h"
+
+class CSystem;
+
+//
+// Now pull in the parts that build the system
+//
+#include "lynxbase.h"
+#include "ram.h"
+#include "rom.h"
+#include "memmap.h"
+#include "cart.h"
+#include "susie.h"
+#include "mikie.h"
+#include "c65c02.h"
+
+#define TOP_START 0xfc00
+#define TOP_MASK 0x03ff
+#define TOP_SIZE 0x400
+#define SYSTEM_SIZE 65536
+
+class CSystem : public CSystemBase
+{
+public:
+ CSystem(const uint8 *, uint32, const uint8*, uint32, int, int, bool) MDFN_COLD;
+ ~CSystem() MDFN_COLD;
+
+public:
+ void Reset() MDFN_COLD;
+
+ inline void Update()
+ {
+ // Only update if there is a predicted timer event
+ if(gSystemCycleCount>=gNextTimerEvent)
+ {
+ mMikie->Update();
+ }
+
+ // Step the processor through 1 instruction
+ mCpu->Update();
+
+ // If the CPU is asleep then skip to the next timer event
+ if(gSystemCPUSleep)
+ {
+ gSystemCycleCount=gNextTimerEvent;
+ }
+ }
+
+ void Advance(int buttons, uint32 *vbuff, int16 *sbuff, int &sbuffsize);
+
+ //
+ // We MUST have separate CPU & RAM peek & poke handlers as all CPU accesses must
+ // go thru the address generator at $FFF9
+ //
+ // BUT, Mikie video refresh & Susie see the whole system as RAM
+ //
+ // Complete and utter wankers, its taken me 1 week to find the 2 lines
+ // in all the documentation that mention this fact, the mother of all
+ // bugs has been found and FIXED.......
+
+ // CPU
+ inline void Poke_CPU(uint32 addr, uint8 data) { mMemoryHandlers[addr]->Poke(addr,data);};
+ inline uint8 Peek_CPU(uint32 addr) { return mMemoryHandlers[addr]->Peek(addr);};
+ inline void PokeW_CPU(uint32 addr,uint16 data) { mMemoryHandlers[addr]->Poke(addr,data&0xff);addr++;mMemoryHandlers[addr]->Poke(addr,data>>8);};
+ inline uint16 PeekW_CPU(uint32 addr) {return ((mMemoryHandlers[addr]->Peek(addr))+(mMemoryHandlers[addr]->Peek(addr+1)<<8));};
+
+ // RAM
+ inline void Poke_RAM(uint32 addr, uint8 data) { mRam->Poke(addr,data);};
+ inline uint8 Peek_RAM(uint32 addr) { return mRam->Peek(addr);};
+ inline void PokeW_RAM(uint32 addr,uint16 data) { mRam->Poke(addr,data&0xff);addr++;mRam->Poke(addr,data>>8);};
+ inline uint16 PeekW_RAM(uint32 addr) {return ((mRam->Peek(addr))+(mRam->Peek(addr+1)<<8));};
+
+ // High level cart access for debug etc
+ inline void Poke_CART(uint32 addr, uint8 data) {mCart->Poke(addr,data);};
+ inline uint8 Peek_CART(uint32 addr) {return mCart->Peek(addr);};
+ inline void CartBank(EMMODE bank) {mCart->BankSelect(bank);};
+ inline uint32 CartSize() {return mCart->ObjectSize();};
+
+ // Low level cart access for Suzy, Mikey
+ inline void Poke_CARTB0(uint8 data) {mCart->Poke0(data);};
+ inline void Poke_CARTB1(uint8 data) {mCart->Poke1(data);};
+ inline uint8 Peek_CARTB0() {return mCart->Peek0();}
+ inline uint8 Peek_CARTB1() {return mCart->Peek1();}
+ inline void CartAddressStrobe(bool strobe) {mCart->CartAddressStrobe(strobe);};
+ inline void CartAddressData(bool data) {mCart->CartAddressData(data);};
+
+ // Low level CPU access
+ void SetRegs(C6502_REGS ®s) {mCpu->SetRegs(regs);};
+ void GetRegs(C6502_REGS ®s) {mCpu->GetRegs(regs);};
+
+ // Mikey system interfacing
+ void ComLynxCable(int status) { mMikie->ComLynxCable(status); };
+ void ComLynxRxData(int data) { mMikie->ComLynxRxData(data); };
+ void ComLynxTxCallback(void (*function)(int data,uint32 objref),uint32 objref) { mMikie->ComLynxTxCallback(function,objref); };
+
+ // Suzy system interfacing
+ uint32 PaintSprites() {return mSusie->PaintSprites();};
+
+ // Miscellaneous
+ void SetButtonData(uint32 data) {mSusie->SetButtonData(data);};
+ uint32 GetButtonData() {return mSusie->GetButtonData();};
+ void SetCycleBreakpoint(uint32 breakpoint) {mCycleCountBreakpoint=breakpoint;};
+ uint8* GetRamPointer() {return mRam->GetRamPointer();};
+
+public:
+ uint32 mCycleCountBreakpoint;
+ CLynxBase *mMemoryHandlers[SYSTEM_SIZE];
+ CCart *mCart;
+ CRom *mRom;
+ CMemMap *mMemMap;
+ CRam *mRam;
+ C65C02 *mCpu;
+ CMikie *mMikie;
+ CSusie *mSusie;
+
+ // old globals
+ uint32 gSuzieDoneTime;
+ uint32 gSystemCycleCount;
+ uint32 gNextTimerEvent;
+ //uint32 gCPUBootAddress;
+ uint32 gSystemIRQ;
+ uint32 gSystemNMI;
+ uint32 gSystemCPUSleep;
+ uint32 gSystemHalt;
+};
+
+#endif