/* /* * MachineBase.cs * * Abstraction of an emulated machine. * * Copyright © 2003, 2004 Mike Murphy * */ using System; using System.IO; using System.Reflection; namespace EMU7800.Core { public abstract class MachineBase { #region Fields ILogger _Logger; FrameBuffer _FrameBuffer; bool _MachineHalt; int _FrameHZ; readonly int _VisiblePitch, _Scanlines; protected Cart Cart { get; set; } #endregion #region Internal Properties internal FrameBuffer FrameBuffer { get { AssertDebug(_FrameBuffer != null); return _FrameBuffer; } } #endregion #region Public Properties /// /// The machine's Central Processing Unit. /// public M6502 CPU { get; protected set; } /// /// The machine's Address Space. /// public AddressSpace Mem { get; protected set; } /// /// The machine's Peripheral Interface Adaptor device. /// public PIA PIA { get; protected set; } /// /// Reports whether the machine has been halted due to an internal condition or error. /// public bool MachineHalt { get { return _MachineHalt; } internal set { if (value) _MachineHalt = true; } } /// /// The machine input state. /// public InputState InputState { get; private set; } /// /// The current frame number. /// public long FrameNumber { get; private set; } /// /// The first scanline that is visible. /// public int FirstScanline { get; private set; } /// /// Frame rate. /// public int FrameHZ { get { return _FrameHZ < 1 ? 1 : _FrameHZ; } set { _FrameHZ = value < 1 ? 1 : value; } } /// /// Number of sound samples per second. /// public int SoundSampleFrequency { get; private set; } /// /// The color palette for the configured machine. /// public int[] Palette { get; internal set; } /// /// Dumps CPU registers to the log when NOP instructions are encountered. /// public bool NOPRegisterDumping { get; set; } /// /// The configured logger sink. /// public ILogger Logger { get { return _Logger ?? (_Logger = new NullLogger()); } set { _Logger = value; } } #endregion #region Public Methods /// /// Creates an instance of the specified machine. /// /// /// /// 7800 BIOS, optional. /// 7800 High Score cart, optional. /// Left controller, optional. /// Right controller, optional. /// /// Cart must not be null. /// Specified MachineType is unexpected. public static MachineBase Create(MachineType machineType, Cart cart, Bios7800 bios, HSC7800 hsc, Controller p1, Controller p2, ILogger logger) { if (cart == null) throw new ArgumentNullException("cart"); MachineBase m; switch (machineType) { case MachineType.A2600NTSC: m = new Machine2600NTSC(cart, logger); break; case MachineType.A2600PAL: m = new Machine2600PAL(cart, logger); break; case MachineType.A7800NTSC: m = new Machine7800NTSC(cart, bios, hsc, logger); break; case MachineType.A7800PAL: m = new Machine7800PAL(cart, bios, hsc, logger); break; default: throw new Emu7800Exception("Unexpected MachineType: " + machineType); } m.InputState.LeftControllerJack = p1; m.InputState.RightControllerJack = p2; m.Reset(); return m; } /// /// Deserialize a from the specified stream. /// /// /// /// public static MachineBase Deserialize(BinaryReader binaryReader) { var context = new DeserializationContext(binaryReader); MachineBase m; try { m = context.ReadMachine(); } catch (Emu7800SerializationException) { throw; } catch (TargetInvocationException ex) { // TargetInvocationException wraps exceptions that unwind an Activator.CreateInstance() frame. throw new Emu7800SerializationException("Serialization stream does not describe a valid machine.", ex.InnerException); } catch (Exception ex) { throw new Emu7800SerializationException("Serialization stream does not describe a valid machine.", ex); } return m; } /// /// Resets the state of the machine. /// public virtual void Reset() { Logger.WriteLine("Machine {0} reset ({1} HZ {2} scanlines)", this, FrameHZ, _Scanlines); FrameNumber = 0; _MachineHalt = false; InputState.ClearAllInput(); } /// /// Computes the next machine frame, updating contents of the provided . /// /// The framebuffer to contain the computed output. /// /// frameBuffer is incompatible with machine. public virtual void ComputeNextFrame(FrameBuffer frameBuffer) { if (MachineHalt) return; InputState.CaptureInputState(); _FrameBuffer = frameBuffer; FrameNumber++; for (var i = 0; i < _FrameBuffer.SoundBufferByteLength; i++) _FrameBuffer.SoundBuffer[i] = 0; } /// /// Create a with compatible dimensions for this machine. /// public FrameBuffer CreateFrameBuffer() { var fb = new FrameBuffer(_VisiblePitch, _Scanlines); return fb; } /// /// Serialize the state of the machine to the specified stream. /// /// /// /// public void Serialize(BinaryWriter binaryWriter) { var context = new SerializationContext(binaryWriter); try { context.Write(this); } catch (Emu7800SerializationException) { throw; } catch (Exception ex) { throw new Emu7800SerializationException("Problem serializing specified machine.", ex); } } #endregion #region Constructors private MachineBase() { } protected MachineBase(ILogger logger, int scanLines, int firstScanline, int fHZ, int soundSampleFreq, int[] palette, int vPitch) : this() { InputState = new InputState(); Logger = logger; _Scanlines = scanLines; FirstScanline = firstScanline; FrameHZ = fHZ; SoundSampleFrequency = soundSampleFreq; Palette = palette; _VisiblePitch = vPitch; } #endregion #region Serialization Members protected MachineBase(DeserializationContext input, int[] palette) { if (input == null) throw new ArgumentNullException("input"); if (palette == null) throw new ArgumentNullException("palette"); if (palette.Length != 0x100) throw new ArgumentException("palette incorrect size, must be 256."); input.CheckVersion(1); _MachineHalt = input.ReadBoolean(); _FrameHZ = input.ReadInt32(); _VisiblePitch = input.ReadInt32(); _Scanlines = input.ReadInt32(); FirstScanline = input.ReadInt32(); SoundSampleFrequency = input.ReadInt32(); NOPRegisterDumping = input.ReadBoolean(); InputState = input.ReadInputState(); Palette = palette; Logger = null; } public virtual void GetObjectData(SerializationContext output) { if (output == null) throw new ArgumentNullException("output"); output.WriteVersion(1); output.Write(_MachineHalt); output.Write(_FrameHZ); output.Write(_VisiblePitch); output.Write(_Scanlines); output.Write(FirstScanline); output.Write(SoundSampleFrequency); output.Write(NOPRegisterDumping); output.Write(InputState); } #endregion [System.Diagnostics.Conditional("DEBUG")] void AssertDebug(bool cond) { if (!cond) System.Diagnostics.Debugger.Break(); } } }