From 38bae4b4198d6e8cb26cfac01fc67a9ae6b9c7be Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 11 Jun 2017 18:06:50 -0400 Subject: [PATCH] Add files via upload --- .../Atari/A7800Hawk/A7800Hawk.IDebuggable.cs | 6 +- .../Atari/A7800Hawk/A7800Hawk.IEmulator.cs | 63 ++++++++++- .../Atari/A7800Hawk/A7800Hawk.ISettable.cs | 91 +++++++++++++++ .../Atari/A7800Hawk/A7800Hawk.IStatable.cs | 2 + .../Consoles/Atari/A7800Hawk/A7800Hawk.cs | 73 +++++++++--- .../A7800Hawk/A7800HawkControllerDeck.cs | 100 +++++++++++++++++ .../Atari/A7800Hawk/A7800HawkControllers.cs | 104 ++++++++++++++++++ .../Consoles/Atari/A7800Hawk/Maria.cs | 19 ++-- .../Consoles/Atari/A7800Hawk/MemoryMap.cs | 18 ++- 9 files changed, 439 insertions(+), 37 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISettable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800HawkControllerDeck.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800HawkControllers.cs diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs index 45809e725e..056eb62a0a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs @@ -54,11 +54,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk } } - public IMemoryCallbackSystem MemoryCallbacks - { - [FeatureNotImplemented] - get { throw new NotImplementedException(); } - } + public IMemoryCallbackSystem MemoryCallbacks { get; private set; } public bool CanStep(StepType type) { diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs index c739349520..09ad452125 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs @@ -1,4 +1,5 @@ using BizHawk.Emulation.Common; +using System; namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { @@ -6,15 +7,29 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { public IEmulatorServiceProvider ServiceProvider { get; } - public ControllerDefinition ControllerDefinition { get; private set; } + public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; //Maria related variables public int cycle; public int cpu_cycle; public int scanline; + // there are 4 maria cycles in a CPU cycle (fast access, both NTSC and PAL) + // if the 6532 or TIA are accessed (PC goes to one of those addresses) the next access will be slower by 1/2 a CPU cycle + // i.e. it will take 6 Maria cycles instead of 4 + public bool slow_access = false; + public void FrameAdvance(IController controller, bool render, bool rendersound) { + if (_tracer.Enabled) + { + cpu.TraceCallback = s => _tracer.Put(s); + } + else + { + cpu.TraceCallback = null; + } + _frame++; if (controller.IsPressed("Power")) @@ -29,6 +44,9 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk _lagcount++; } + // read the controller state here for now + GetControllerState(controller); + scanline = 0; // actually execute the frame @@ -37,18 +55,61 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk maria.Execute(cycle, scanline); cycle++; cpu_cycle++; + tia._hsyncCnt++; + + // the time a cpu cycle takes depends on the status of the address bus + // any address in range of the TIA or m6532 takes an extra cycle to complete + if (cpu_cycle==(4 + (slow_access ? 2 : 0))) + { + cpu.ExecuteOne(); + cpu_cycle = 0; + } + + // determine if the next access will be fast or slow + if (cpu.PC < 0x0400) + { + if ((cpu.PC & 0xFF) < 0x20) + { + if ((A7800_control_register & 0x1) == 0 && (cpu.PC < 0x20)) + { + slow_access = false; + } + else + { + slow_access = true; + } + } + else if (cpu.PC < 0x300) + { + slow_access = true; + } + else + { + slow_access = false; + } + } if (cycle == 454) { scanline++; cycle = 0; + tia._hsyncCnt = 0; } } } + private void GetControllerState(IController controller) + { + InputCallbacks.Call(); + + ushort port1 = _controllerDeck.ReadPort1(controller); + + ushort port2 = _controllerDeck.ReadPort2(controller); + } + public int Frame => _frame; public string SystemId => "A7800"; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISettable.cs new file mode 100644 index 0000000000..7f646316af --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISettable.cs @@ -0,0 +1,91 @@ +using System; +using Newtonsoft.Json; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Atari.A7800Hawk +{ + public partial class A7800Hawk : IEmulator, IStatable, ISettable + { + public A7800Settings GetSettings() + { + return _settings.Clone(); + } + + public A7800SyncSettings GetSyncSettings() + { + return _syncSettings.Clone(); + } + + public bool PutSettings(A7800Settings o) + { + _settings = o; + return false; + } + + public bool PutSyncSettings(A7800SyncSettings o) + { + bool ret = A7800SyncSettings.NeedsReboot(_syncSettings, o); + _syncSettings = o; + return ret; + } + + private A7800Settings _settings = new A7800Settings(); + private A7800SyncSettings _syncSettings = new A7800SyncSettings(); + + public class A7800Settings + { + public A7800Settings Clone() + { + return (A7800Settings)MemberwiseClone(); + } + } + + public class A7800SyncSettings + { + private string _port1 = A7800HawkControllerDeck.DefaultControllerName; + private string _port2 = A7800HawkControllerDeck.DefaultControllerName; + + [JsonIgnore] + public string Port1 + { + get { return _port1; } + set + { + if (!A7800HawkControllerDeck.ValidControllerTypes.ContainsKey(value)) + { + throw new InvalidOperationException("Invalid controller type: " + value); + } + + _port1 = value; + } + } + + [JsonIgnore] + public string Port2 + { + get { return _port2; } + set + { + if (!A7800HawkControllerDeck.ValidControllerTypes.ContainsKey(value)) + { + throw new InvalidOperationException("Invalid controller type: " + value); + } + + _port2 = value; + } + } + + public A7800SyncSettings Clone() + { + return (A7800SyncSettings)MemberwiseClone(); + } + + public static bool NeedsReboot(A7800SyncSettings x, A7800SyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IStatable.cs index 96356abb5b..872bc6b442 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IStatable.cs @@ -53,6 +53,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk ser.Sync("Lag", ref _lagcount); ser.Sync("Frame", ref _frame); ser.Sync("IsLag", ref _islag); + _controllerDeck.SyncState(ser); + ser.EndSection(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs index 1ea8b6aea8..32c4872461 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs @@ -12,7 +12,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk isPorted: false, isReleased: true)] [ServiceNotApplicable(typeof(ISettable<,>), typeof(IDriveLight))] - public partial class A7800Hawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable + public partial class A7800Hawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable, + ISettable { // this register selects between 2600 and 7800 mode in the A7800 // however, we already have a 2600 emulator so this core will only be loading A7800 games @@ -25,14 +26,20 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk public byte[] Maria_regs = new byte[0x20]; public byte[] RAM = new byte[0x1000]; public byte[] regs_6532 = new byte[0x80]; + public byte[] hs_bios_mem = new byte[0x800]; - private readonly byte[] _rom; - private readonly byte[] _hsbios; - private readonly byte[] _bios; - private readonly byte[] _hsram = new byte[2048]; + public readonly byte[] _rom; + public readonly byte[] _hsbios; + public readonly byte[] _bios; + public readonly byte[] _hsram = new byte[2048]; private int _frame = 0; + public string s_mapper; + public MapperBase mapper; + + private readonly ITraceable _tracer; + public MOS6502X cpu; public Maria maria; private bool _isPAL; @@ -45,10 +52,14 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk maria = new Maria(); tia = new TIA(); - - ser.Register(maria); - ser.Register(tia); - ServiceProvider = ser; + cpu = new MOS6502X + { + ReadMemory = ReadMemory, + WriteMemory = WriteMemory, + PeekMemory = ReadMemory, + DummyReadMemory = ReadMemory, + OnExecFetch = ExecFetch + }; maria = new Maria { @@ -56,6 +67,9 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk }; CoreComm = comm; + + _controllerDeck = new A7800HawkControllerDeck(_syncSettings.Port1, _syncSettings.Port2); + byte[] highscoreBios = comm.CoreFileProvider.GetFirmware("A78", "Bios_HSC", false, "Some functions may not work without the high score BIOS."); byte[] palBios = comm.CoreFileProvider.GetFirmware("A78", "Bios_PAL", false, "The game will not run if the correct region BIOS is not available."); byte[] ntscBios = comm.CoreFileProvider.GetFirmware("A78", "Bios_NTSC", false, "The game will not run if the correct region BIOS is not available."); @@ -74,7 +88,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk // if none found default is zero // also check for PAL region string hash_md5 = null; - string s_mapper = null; + s_mapper = null; hash_md5 = "md5:" + rom.HashMD5(0, rom.Length); var gi = Database.CheckDatabase(hash_md5); @@ -82,22 +96,24 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk if (gi != null) { var dict = gi.GetOptionsDict(); - if (!dict.ContainsKey("PAL")) + if (dict.ContainsKey("PAL")) { _isPAL = true; } - if (!dict.ContainsKey("board")) + if (dict.ContainsKey("board")) { s_mapper = dict["board"]; } else - throw new Exception("No Board selected for this mapper"); + throw new Exception("No Board selected for this game"); } else { throw new Exception("ROM not in gamedb"); } + Reset_Mapper(s_mapper); + _rom = rom; _hsbios = highscoreBios; _bios = _isPAL ? palBios : ntscBios; @@ -111,21 +127,31 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk if (_isPAL) { maria._frameHz = 50; + maria._screen_width = 454; + maria._screen_height = 313; maria._palette = PALPalette; } else { maria._frameHz = 60; + maria._screen_width = 454; + maria._screen_height = 263; maria._palette = NTSCPalette; } + ser.Register(maria); + ser.Register(tia); + ServiceProvider = ser; + + _tracer = new TraceBuffer { Header = cpu.TraceHeader }; + ser.Register(_tracer); HardReset(); } public DisplayType Region => _isPAL ? DisplayType.PAL : DisplayType.NTSC; - public A7800HawkControl ControlAdapter { get; private set; } + private readonly A7800HawkControllerDeck _controllerDeck; private void HardReset() { @@ -142,7 +168,24 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk Maria_regs = new byte[0x20]; RAM = new byte[0x1000]; regs_6532 = new byte[0x80]; - } + + cpu_cycle = 0; + } + + private void ExecFetch(ushort addr) + { + //MemoryCallbacks.CallExecutes(addr); + } + + private void Reset_Mapper(string m) + { + if (m=="0") + { + mapper = new MapperDefault(); + } + + mapper.Core = this; + } /* * MariaTables.cs diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800HawkControllerDeck.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800HawkControllerDeck.cs new file mode 100644 index 0000000000..131ab32082 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800HawkControllerDeck.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Common.ReflectionExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Atari.A7800Hawk +{ + public class A7800HawkControllerDeck + { + public A7800HawkControllerDeck(string controller1Name, string controller2Name) + { + if (!ValidControllerTypes.ContainsKey(controller1Name)) + { + throw new InvalidOperationException("Invalid controller type: " + controller1Name); + } + + if (!ValidControllerTypes.ContainsKey(controller2Name)) + { + throw new InvalidOperationException("Invalid controller type: " + controller2Name); + } + + Port1 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller1Name], 1); + Port2 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller2Name], 2); + + Definition = new ControllerDefinition + { + Name = "A7800 Controller", + BoolButtons = Port1.Definition.BoolButtons + .Concat(Port2.Definition.BoolButtons) + .Concat(new[] + { + "Power", + "Reset", + "Select", + "BW", // should be "Color"?? + "Left Difficulty", // better not put P# on these as they might not correspond to player numbers + "Right Difficulty" + }) + .ToList() + }; + + Definition.FloatControls.AddRange(Port1.Definition.FloatControls); + Definition.FloatControls.AddRange(Port2.Definition.FloatControls); + + Definition.FloatRanges.AddRange(Port1.Definition.FloatRanges); + Definition.FloatRanges.AddRange(Port2.Definition.FloatRanges); + } + + public byte ReadPort1(IController c) + { + return Port1.Read(c); + } + + public byte ReadPort2(IController c) + { + return Port2.Read(c); + } + + public ControllerDefinition Definition { get; } + + public void SyncState(Serializer ser) + { + ser.BeginSection("Port1"); + Port1.SyncState(ser); + ser.EndSection(); + + ser.BeginSection("Port2"); + Port2.SyncState(ser); + ser.EndSection(); + } + + private readonly IPort Port1; + private readonly IPort Port2; + + private static Dictionary _controllerTypes; + + public static Dictionary ValidControllerTypes + { + get + { + if (_controllerTypes == null) + { + _controllerTypes = typeof(A7800HawkControllerDeck).Assembly + .GetTypes() + .Where(t => typeof(IPort).IsAssignableFrom(t)) + .Where(t => !t.IsAbstract && !t.IsInterface) + .ToDictionary(tkey => tkey.DisplayName()); + } + + return _controllerTypes; + } + } + + public static string DefaultControllerName => typeof(StandardController).DisplayName(); + } + +} diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800HawkControllers.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800HawkControllers.cs new file mode 100644 index 0000000000..72ca92171d --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800HawkControllers.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Atari.A7800Hawk +{ + /// + /// Represents a controller plugged into a controller port on the intellivision + /// + public interface IPort + { + byte Read(IController c); + + ControllerDefinition Definition { get; } + + void SyncState(Serializer ser); + + int PortNum { get; } + } + + [DisplayName("Unplugged Controller")] + public class UnpluggedController : IPort + { + public UnpluggedController(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + BoolButtons = new List() + }; + } + + public byte Read(IController c) + { + return 0; + } + + public ControllerDefinition Definition { get; } + + public void SyncState(Serializer ser) + { + // Do nothing + } + + public int PortNum { get; } + } + + [DisplayName("Joystick Controller")] + public class StandardController : IPort + { + public StandardController(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + BoolButtons = BaseDefinition + .Select(b => "P" + PortNum + " " + b) + .ToList() + }; + } + + public int PortNum { get; } + + public byte Read(IController c) + { + byte result = 0; + for (int i = 0; i < 5; i++) + { + if (c.IsPressed(Definition.BoolButtons[i])) + { + result |= HandControllerButtons[i]; + } + } + + return result; + } + + public ControllerDefinition Definition { get; } + + + public void SyncState(Serializer ser) + { + // Nothing todo, I think + } + + private static readonly string[] BaseDefinition = + { + "U", "D", "L", "R", "Fire" + }; + + private static byte[] HandControllerButtons = + { + 0x60, // UP + 0xC0, // Down + 0xA0, // Left + 0x48, // Right + 0x81 // Fire + }; + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs index a142a32bb7..95f136866b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs @@ -7,7 +7,9 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk // Emulates the Atari 7800 Maria graphics chip public class Maria : IVideoProvider { - public int _frameHz; + public int _frameHz = 60; + public int _screen_width = 454; + public int _screen_height = 263; public int[] _vidbuffer; public int[] _palette; @@ -17,10 +19,10 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk return _vidbuffer; } - public int VirtualWidth => 275; - public int VirtualHeight => BufferHeight; - public int BufferWidth { get; private set; } - public int BufferHeight { get; private set; } + public int VirtualWidth => 454; + public int VirtualHeight => _screen_height; + public int BufferWidth => 454; + public int BufferHeight => _screen_height; public int BackgroundColor => unchecked((int)0xff000000); public int VsyncNumerator => _frameHz; public int VsyncDenominator => 1; @@ -28,11 +30,6 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk // the Maria chip can directly access memory public Func ReadMemory; - // there are 4 maria cycles in a CPU cycle (fast access, both NTSC and PAL) - // if the 6532 or TIA are accessed (PC goes to one of those addresses) the next access will be slower by 1/2 a CPU cycle - // i.e. it will take 6 Maria cycles instead of 4 - public bool slow_access = false; - // each frame contains 263 scanlines // each scanline consists of 113.5 CPU cycles (fast access) which equates to 454 Maria cycles // In total there are 29850.5 CPU cycles (fast access) in a frame @@ -43,7 +40,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk public void Reset() { - + _vidbuffer = new int[VirtualWidth * VirtualHeight]; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/MemoryMap.cs index 64f6211966..a7541855c6 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/MemoryMap.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/MemoryMap.cs @@ -55,7 +55,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk } else if (addr < 0x480) { - return 0xFF; // cartridge space available + // cartridge space available + return mapper.ReadMemory(addr); } else if (addr < 0x500) { @@ -64,7 +65,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk } else if (addr < 0x1800) { - return 0xFF; // cartridge space available + // cartridge space available + return mapper.ReadMemory(addr); } else if (addr < 0x2800) { @@ -72,11 +74,12 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk } else if (addr < 0x4000) { - return RAM[addr - 0x2800 + 0x800]; + // could be either RAM mirror or ROM + return mapper.ReadMemory(addr); } else { - return 0xFF; // cartridge and other OPSYS + return mapper.ReadMemory(addr); } } @@ -125,6 +128,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk else if (addr < 0x480) { // cartridge space available + mapper.WriteMemory(addr, value); } else if (addr < 0x500) { @@ -134,6 +138,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk else if (addr < 0x1800) { // cartridge space available + mapper.WriteMemory(addr, value); } else if (addr < 0x2800) { @@ -141,13 +146,16 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk } else if (addr < 0x4000) { - RAM[addr - 0x2800 + 0x800] = value; + // could be either RAM mirror or ROM + mapper.WriteMemory(addr, value); } else { // cartridge and other OPSYS + mapper.WriteMemory(addr, value); } } + } }