From 39808f793d04758738adb9f80c7c5c16a1cba6bb Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 1 Mar 2017 10:44:05 +0800 Subject: [PATCH] Coleco Turbo Controller --- .../Consoles/Coleco/ColecoControllerDeck.cs | 95 +++++++ .../Consoles/Coleco/ColecoControllers.cs | 239 ++++++++++++++++++ .../Consoles/Coleco/ColecoVision.ISettable.cs | 65 ++++- .../Consoles/Coleco/ColecoVision.cs | 23 +- .../Consoles/Coleco/Input.cs | 70 +---- .../Consoles/Coleco/TMS9918A.cs | 33 ++- 6 files changed, 452 insertions(+), 73 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Coleco/ColecoControllerDeck.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Coleco/ColecoControllers.cs diff --git a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoControllerDeck.cs b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoControllerDeck.cs new file mode 100644 index 0000000000..6665105726 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoControllerDeck.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Common.ReflectionExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.ColecoVision +{ + public class ColecoVisionControllerDeck + { + public ColecoVisionControllerDeck(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 = "ColecoVision Basic Controller", + BoolButtons = Port1.Definition.BoolButtons + .Concat(Port2.Definition.BoolButtons) + .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, bool left_mode) + { + return Port1.Read(c, left_mode); + } + + public byte ReadPort2(IController c, bool left_mode) + { + return Port2.Read(c, left_mode); + } + + public ControllerDefinition Definition { get; private set; } + + 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 = null; + + public static Dictionary ValidControllerTypes + { + get + { + if (_controllerTypes == null) + { + _controllerTypes = typeof(ColecoVisionControllerDeck).Assembly + .GetTypes() + .Where(t => typeof(IPort).IsAssignableFrom(t)) + .Where(t => !t.IsAbstract && !t.IsInterface) + .ToDictionary(tkey => tkey.DisplayName()); + } + + return _controllerTypes; + } + } + + public static string DefaultControllerName + { + get { return typeof(StandardController).DisplayName(); } + } + } + +} diff --git a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoControllers.cs b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoControllers.cs new file mode 100644 index 0000000000..f42d2588ee --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoControllers.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Common.ReflectionExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.ColecoVision +{ + /// + /// Represents a controller plugged into a controller port on the intellivision + /// + public interface IPort + { + byte Read(IController c, bool left_mode); + + 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, bool left_mode) + { + return 0; // needs checking + } + + public ControllerDefinition Definition { get; private set; } + + public void SyncState(Serializer ser) + { + // Do nothing + } + + public int PortNum { get; private set; } + } + + [DisplayName("ColecoVision Basic 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; private set; } + + public byte Read(IController c, bool left_mode) + { + if (left_mode) + { + byte retval = 0x7F; + if (c.IsPressed(Definition.BoolButtons[0])) retval &= 0xFE; + if (c.IsPressed(Definition.BoolButtons[1])) retval &= 0xFD; + if (c.IsPressed(Definition.BoolButtons[2])) retval &= 0xFB; + if (c.IsPressed(Definition.BoolButtons[3])) retval &= 0xF7; + if (c.IsPressed(Definition.BoolButtons[4])) retval &= 0x3F; + return retval; + } + else + { + byte retval = 0xF; + // 0x00; + if (c.IsPressed(Definition.BoolButtons[14])) retval = 0x01; + if (c.IsPressed(Definition.BoolButtons[10])) retval = 0x02; + if (c.IsPressed(Definition.BoolButtons[11])) retval = 0x03; + // 0x04; + if (c.IsPressed(Definition.BoolButtons[13])) retval = 0x05; + if (c.IsPressed(Definition.BoolButtons[16])) retval = 0x06; + if (c.IsPressed(Definition.BoolButtons[8])) retval = 0x07; + // 0x08; + if (c.IsPressed(Definition.BoolButtons[17])) retval = 0x09; + if (c.IsPressed(Definition.BoolButtons[6])) retval = 0x0A; + if (c.IsPressed(Definition.BoolButtons[15])) retval = 0x0B; + if (c.IsPressed(Definition.BoolButtons[9])) retval = 0x0C; + if (c.IsPressed(Definition.BoolButtons[7])) retval = 0x0D; + if (c.IsPressed(Definition.BoolButtons[12])) retval = 0x0E; + + if (c.IsPressed(Definition.BoolButtons[5]) == false) retval |= 0x40; + retval |= 0x30; // always set these bits + return retval; + } + } + + public ControllerDefinition Definition { get; private set; } + + + public void SyncState(Serializer ser) + { + // Nothing todo, I think + } + + private static readonly string[] BaseDefinition = + { + "Up", "Right", "Down", "Left", "L", "R", + "Key 0", "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", + "Key 6", "Key 7", "Key 8", "Key 9", "Pound", "Star" + }; + } + + [DisplayName("Turbo Controller")] + public class ColecoTurboController : IPort + { + public ColecoTurboController(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + BoolButtons = BaseBoolDefinition + .Select(b => "P" + PortNum + " " + b) + .ToList(), + FloatControls = { "P" + PortNum + " Disc X", "P" + PortNum + " Disc Y" }, + FloatRanges = { new[] { -127.0f, 0, 127.0f }, new[] { -127.0f, 0, 127.0f } } + }; + } + + public int PortNum { get; private set; } + + public ControllerDefinition Definition { get; private set; } + + public byte Read(IController c, bool left_mode) + { + if (left_mode) + { + byte retval = 0x7B; + + if (c.IsPressed(Definition.BoolButtons[0])) retval &= 0x3F; + /* + if (c.IsPressed(Definition.BoolButtons[1])) retval &= 0xF7; + if (c.IsPressed(Definition.BoolButtons[2])) retval &= 0xFB; + if (c.IsPressed(Definition.BoolButtons[3])) retval &= 0xFD; + if (c.IsPressed(Definition.BoolButtons[4])) retval &= 0xFE; + if (c.IsPressed(Definition.BoolButtons[5])) retval &= 0x7F; + if (c.IsPressed(Definition.BoolButtons[6])) retval &= 0xDF; + if (c.IsPressed(Definition.BoolButtons[7])) retval &= 0xEF; + */ + int x = (int)c.GetFloat(Definition.FloatControls[0]); + int y = (int)c.GetFloat(Definition.FloatControls[1]); + retval &= CalcDirection(x, y); + + //Console.WriteLine(retval); + return retval; + } else + { + byte retval = 0x7B; + if (c.IsPressed(Definition.BoolButtons[0])) retval &= 0x3F; + /* + if (c.IsPressed(Definition.BoolButtons[1])) retval &= 0xF7; + if (c.IsPressed(Definition.BoolButtons[2])) retval &= 0xFB; + if (c.IsPressed(Definition.BoolButtons[3])) retval &= 0xFD; + if (c.IsPressed(Definition.BoolButtons[4])) retval &= 0xFE; + if (c.IsPressed(Definition.BoolButtons[5])) retval &= 0x7F; + if (c.IsPressed(Definition.BoolButtons[6])) retval &= 0xDF; + if (c.IsPressed(Definition.BoolButtons[7])) retval &= 0xEF; + */ + int x = (int)c.GetFloat(Definition.FloatControls[0]); + int y = (int)c.GetFloat(Definition.FloatControls[1]); + retval &= CalcDirection(x, y); + + //Console.WriteLine(retval); + return retval; + } + } + + public void SyncState(Serializer ser) + { + // Nothing todo, I think + } + + private static readonly string[] BaseBoolDefinition = + { + "Pedal", "T1", "T2", "T3", "T4", "T5", "T6", "T7" + }; + + // x and y are both assumed to be in [-127, 127] + // x increases from left to right + // y increases from top to bottom + private static byte CalcDirection(int x, int y) + { + y = -y; // vflip to match the arrangement of FloatControllerButtons + + // deadzone: if we're less than ? units from the origin, return no direction + if (x * x + y * y < Deadzone * Deadzone) + { + return 0x7F; // nothing pressed + } + + double t = Math.Atan2(y, x) * 8.0 / Math.PI; + int i = (int)Math.Round(t); + return FloatControllerButtons[i & 15]; + } + + private const int Deadzone = 50; + + private static byte[] FloatControllerButtons = new byte[] + { + 0x6F, // E + 0x4F, // ENE + 0x4F, // NE + 0x4F, // NNE + + 0x4F, // N + 0x5F, // NNW + 0x5F, // NW + 0x5F, // WNW + + 0x5F, // W + 0x7F, // WSW + 0x7F, // SW + 0x7F, // SSW + + 0x7F, // S + 0x6F, // SSE + 0x6F, // SE + 0x6F, // ESE + }; + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.ISettable.cs index 29297e0964..3579934547 100644 --- a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.ISettable.cs @@ -1,12 +1,16 @@ -using BizHawk.Emulation.Common; +using System; +using Newtonsoft.Json; + +using BizHawk.Common; +using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.ColecoVision { - public partial class ColecoVision : ISettable + public partial class ColecoVision : IEmulator, IStatable, ISettable { - public object GetSettings() + public ColecoSettings GetSettings() { - return null; + return Settings.Clone(); } public ColecoSyncSettings GetSyncSettings() @@ -14,28 +18,77 @@ namespace BizHawk.Emulation.Cores.ColecoVision return _syncSettings.Clone(); } - public bool PutSettings(object o) + public bool PutSettings(ColecoSettings o) { + Settings = o; return false; } public bool PutSyncSettings(ColecoSyncSettings o) { bool ret = o.SkipBiosIntro != _syncSettings.SkipBiosIntro; + ret |= ColecoSyncSettings.NeedsReboot(_syncSettings, o); _syncSettings = o; return ret; } - private ColecoSyncSettings _syncSettings; + public class ColecoSettings + { + public ColecoSettings Clone() + { + return (ColecoSettings)MemberwiseClone(); + } + } + + public ColecoSettings Settings = new ColecoSettings(); + public ColecoSyncSettings _syncSettings = new ColecoSyncSettings(); public class ColecoSyncSettings { public bool SkipBiosIntro { get; set; } + private string _port1 = ColecoVisionControllerDeck.DefaultControllerName; + private string _port2 = ColecoVisionControllerDeck.DefaultControllerName; + + [JsonIgnore] + public string Port1 + { + get { return _port1; } + set + { + if (!ColecoVisionControllerDeck.ValidControllerTypes.ContainsKey(value)) + { + throw new InvalidOperationException("Invalid controller type: " + value); + } + + _port1 = value; + } + } + + [JsonIgnore] + public string Port2 + { + get { return _port2; } + set + { + if (!ColecoVisionControllerDeck.ValidControllerTypes.ContainsKey(value)) + { + throw new InvalidOperationException("Invalid controller type: " + value); + } + + _port2 = value; + } + } + public ColecoSyncSettings Clone() { return (ColecoSyncSettings)MemberwiseClone(); } + + public static bool NeedsReboot(ColecoSyncSettings x, ColecoSyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs index a2511fb6df..f1609a02ca 100644 --- a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs +++ b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs @@ -1,6 +1,7 @@ using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Components; using BizHawk.Emulation.Cores.Components.Z80; +using System; namespace BizHawk.Emulation.Cores.ColecoVision { @@ -11,7 +12,7 @@ namespace BizHawk.Emulation.Cores.ColecoVision isReleased: true )] [ServiceNotApplicable(typeof(ISaveRam), typeof(IDriveLight))] - public sealed partial class ColecoVision : IEmulator, IDebuggable, IInputPollable, IStatable, ISettable + public sealed partial class ColecoVision : IEmulator, IDebuggable, IInputPollable, IStatable, ISettable { // ROM public byte[] RomData; @@ -21,7 +22,7 @@ namespace BizHawk.Emulation.Cores.ColecoVision // Machine public Z80A Cpu; public TMS9918A VDP; - + public byte[] Ram = new byte[1024]; private readonly TraceBuffer Tracer = new TraceBuffer(); @@ -41,12 +42,15 @@ namespace BizHawk.Emulation.Cores.ColecoVision Cpu.WriteHardware = WritePort; Cpu.MemoryCallbacks = MemoryCallbacks; - VDP = new TMS9918A(Cpu); - (ServiceProvider as BasicServiceProvider).Register(VDP); PSG = new SN76489(); _fakeSyncSound = new FakeSyncSound(PSG, 735); (ServiceProvider as BasicServiceProvider).Register(_fakeSyncSound); + ControllerDeck = new ColecoVisionControllerDeck(this._syncSettings.Port1, this._syncSettings.Port2); + + VDP = new TMS9918A(Cpu, ControllerDeck); + (ServiceProvider as BasicServiceProvider).Register(VDP); + // TODO: hack to allow bios-less operation would be nice, no idea if its feasible BiosRom = CoreComm.CoreFileProvider.GetFirmware("Coleco", "Bios", true, "Coleco BIOS file is required."); @@ -61,10 +65,19 @@ namespace BizHawk.Emulation.Cores.ColecoVision var serviceProvider = ServiceProvider as BasicServiceProvider; serviceProvider.Register(new Disassembler()); serviceProvider.Register(Tracer); + + VDP.Controller = Controller; } public IEmulatorServiceProvider ServiceProvider { get; private set; } + public ControllerDefinition ControllerDefinition + { + get { return ControllerDeck.Definition; } + } + public ColecoVisionControllerDeck ControllerDeck { get; private set; } + public IController Controller { get; set; } + const ushort RamSizeMask = 0x03FF; public void FrameAdvance(bool render, bool renderSound) @@ -78,7 +91,7 @@ namespace BizHawk.Emulation.Cores.ColecoVision { Cpu.Logger = (s) => Tracer.Put(s); } - + VDP.Controller = Controller; VDP.ExecuteFrame(); PSG.EndFrame(Cpu.TotalExecutedCycles); diff --git a/BizHawk.Emulation.Cores/Consoles/Coleco/Input.cs b/BizHawk.Emulation.Cores/Consoles/Coleco/Input.cs index e052c3e6d7..378b42f850 100644 --- a/BizHawk.Emulation.Cores/Consoles/Coleco/Input.cs +++ b/BizHawk.Emulation.Cores/Consoles/Coleco/Input.cs @@ -5,7 +5,7 @@ namespace BizHawk.Emulation.Cores.ColecoVision { public partial class ColecoVision { - public static readonly ControllerDefinition ColecoVisionControllerDefinition = new ControllerDefinition +/* public static readonly ControllerDefinition ColecoVisionControllerDefinition = new ControllerDefinition { Name = "ColecoVision Basic Controller", BoolButtons = @@ -21,53 +21,25 @@ namespace BizHawk.Emulation.Cores.ColecoVision "P2 Key 6", "P2 Key 7", "P2 Key 8", "P2 Key 9", "P2 Star", "P2 Pound" } }; - - public ControllerDefinition ControllerDefinition { get { return ColecoVisionControllerDefinition; } } - public IController Controller { get; set; } - - enum InputPortMode { Left, Right } + */ + public enum InputPortMode { Left, Right } InputPortMode InputPortSelection; byte ReadController1() { _isLag = false; - + byte retval; if (InputPortSelection == InputPortMode.Left) { - byte retval = 0x7F; - if (Controller.IsPressed("P1 Up")) retval &= 0xFE; - if (Controller.IsPressed("P1 Right")) retval &= 0xFD; - if (Controller.IsPressed("P1 Down")) retval &= 0xFB; - if (Controller.IsPressed("P1 Left")) retval &= 0xF7; - if (Controller.IsPressed("P1 L")) retval &= 0x3F; + retval = ControllerDeck.ReadPort1(Controller, true); return retval; } if (InputPortSelection == InputPortMode.Right) { - byte retval = 0xF; - // 0x00; - if (Controller.IsPressed("P1 Key 8")) retval = 0x01; - if (Controller.IsPressed("P1 Key 4")) retval = 0x02; - if (Controller.IsPressed("P1 Key 5")) retval = 0x03; - // 0x04; - if (Controller.IsPressed("P1 Key 7")) retval = 0x05; - if (Controller.IsPressed("P1 Pound")) retval = 0x06; - if (Controller.IsPressed("P1 Key 2")) retval = 0x07; - // 0x08; - if (Controller.IsPressed("P1 Star")) retval = 0x09; - if (Controller.IsPressed("P1 Key 0")) retval = 0x0A; - if (Controller.IsPressed("P1 Key 9")) retval = 0x0B; - if (Controller.IsPressed("P1 Key 3")) retval = 0x0C; - if (Controller.IsPressed("P1 Key 1")) retval = 0x0D; - if (Controller.IsPressed("P1 Key 6")) retval = 0x0E; - - if (Controller.IsPressed("P1 R") == false) retval |= 0x40; - retval |= 0x30; // always set these bits + retval = ControllerDeck.ReadPort1(Controller, false); return retval; } - - return 0x7F; } @@ -75,42 +47,18 @@ namespace BizHawk.Emulation.Cores.ColecoVision byte ReadController2() { _isLag = false; - + byte retval; if (InputPortSelection == InputPortMode.Left) { - byte retval = 0x7F; - if (Controller.IsPressed("P2 Up")) retval &= 0xFE; - if (Controller.IsPressed("P2 Right")) retval &= 0xFD; - if (Controller.IsPressed("P2 Down")) retval &= 0xFB; - if (Controller.IsPressed("P2 Left")) retval &= 0xF7; - if (Controller.IsPressed("P2 L")) retval &= 0x3F; + retval = ControllerDeck.ReadPort2(Controller, true); return retval; } if (InputPortSelection == InputPortMode.Right) { - byte retval = 0xF; - // 0x00; - if (Controller.IsPressed("P2 Key 8")) retval = 0x01; - if (Controller.IsPressed("P2 Key 4")) retval = 0x02; - if (Controller.IsPressed("P2 Key 5")) retval = 0x03; - // 0x04; - if (Controller.IsPressed("P2 Key 7")) retval = 0x05; - if (Controller.IsPressed("P2 Pound")) retval = 0x06; - if (Controller.IsPressed("P2 Key 2")) retval = 0x07; - // 0x08; - if (Controller.IsPressed("P2 Star")) retval = 0x09; - if (Controller.IsPressed("P2 Key 0")) retval = 0x0A; - if (Controller.IsPressed("P2 Key 9")) retval = 0x0B; - if (Controller.IsPressed("P2 Key 3")) retval = 0x0C; - if (Controller.IsPressed("P2 Key 1")) retval = 0x0D; - if (Controller.IsPressed("P2 Key 6")) retval = 0x0E; - - if (Controller.IsPressed("P2 R") == false) retval |= 0x40; - retval |= 0x30; // always set these bits + retval = ControllerDeck.ReadPort2(Controller, false); return retval; } - return 0x7F; } diff --git a/BizHawk.Emulation.Cores/Consoles/Coleco/TMS9918A.cs b/BizHawk.Emulation.Cores/Consoles/Coleco/TMS9918A.cs index 3151db7a9b..e20cb0c604 100644 --- a/BizHawk.Emulation.Cores/Consoles/Coleco/TMS9918A.cs +++ b/BizHawk.Emulation.Cores/Consoles/Coleco/TMS9918A.cs @@ -5,6 +5,7 @@ using System.IO; using BizHawk.Common; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Components.Z80; +using BizHawk.Common.NumberExtensions; namespace BizHawk.Emulation.Cores.ColecoVision { @@ -20,6 +21,9 @@ namespace BizHawk.Emulation.Cores.ColecoVision byte VdpBuffer; int TmsMode; + // interrupt control for quadrature reads + bool spin_on1, spin_on2; + bool Mode1Bit { get { return (Registers[1] & 16) > 0; } } bool Mode2Bit { get { return (Registers[0] & 2) > 0; } } bool Mode3Bit { get { return (Registers[1] & 8) > 0; } } @@ -50,12 +54,35 @@ namespace BizHawk.Emulation.Cores.ColecoVision if (scanLine == 192) { + InterruptPending = true; + if (EnableInterrupts) Cpu.NonMaskableInterrupt = true; } Cpu.ExecuteCycles(228); + + byte temp_ret1 = Deck.ReadPort1(Controller, true); + byte temp_ret2 = Deck.ReadPort2(Controller, true); + + if (((temp_ret1.Bit(4) && !spin_on1) | ( temp_ret2.Bit(4) && !spin_on2)) && scanLine == 50) + { + + if (EnableInterrupts) + Cpu.NonMaskableInterrupt = true; + + if (temp_ret1.Bit(4) && !spin_on1) + spin_on1 = true; + + if (temp_ret2.Bit(4) && !spin_on2) + spin_on2 = true; + } + if (!temp_ret1.Bit(4)) + spin_on1 = false; + if (!temp_ret2.Bit(4)) + spin_on2 = false; + } } @@ -437,9 +464,13 @@ namespace BizHawk.Emulation.Cores.ColecoVision } Z80A Cpu; - public TMS9918A(Z80A cpu) + ColecoVisionControllerDeck Deck; + public IController Controller; + + public TMS9918A(Z80A cpu, ColecoVisionControllerDeck deck) { this.Cpu = cpu; + this.Deck = deck; } public int[] FrameBuffer = new int[256 * 192];