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