BizHawk/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs

265 lines
7.6 KiB
C#
Raw Normal View History

2017-11-28 19:28:22 +00:00
using BizHawk.Common;
using BizHawk.Emulation.Common;
2017-11-23 17:26:15 +00:00
using BizHawk.Emulation.Cores.Components.Z80A;
using System;
using System.Collections.Generic;
2017-11-23 17:26:15 +00:00
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Main properties / fields / contruction*
/// </summary>
public abstract partial class SpectrumBase
2017-12-05 10:02:57 +00:00
{
2018-03-13 13:09:44 +00:00
#region Devices
2017-11-23 17:26:15 +00:00
/// <summary>
/// The calling ZXSpectrum class (piped in via constructor)
/// </summary>
2017-11-24 18:43:04 +00:00
public ZXSpectrum Spectrum { get; set; }
2017-11-23 17:26:15 +00:00
/// <summary>
/// Reference to the instantiated Z80 cpu (piped in via constructor)
/// </summary>
2017-11-24 18:43:04 +00:00
public Z80A CPU { get; set; }
/// <summary>
/// ROM and extended info
/// </summary>
public RomData RomData { get; set; }
2017-11-23 17:26:15 +00:00
/// <summary>
/// The emulated ULA device
/// </summary>
public ULABase ULADevice { get; set; }
2017-11-23 17:26:15 +00:00
/// <summary>
/// The spectrum buzzer/beeper
/// </summary>
2018-03-07 17:40:15 +00:00
public IBeeperDevice BuzzerDevice { get; set; }
2017-11-23 17:26:15 +00:00
/// <summary>
/// Device representing the AY-3-8912 chip found in the 128k and up spectrums
/// </summary>
2017-12-07 10:49:43 +00:00
public AY38912 AYDevice { get; set; }
2017-11-23 17:26:15 +00:00
/// <summary>
/// The spectrum keyboard
/// </summary>
public virtual IKeyboard KeyboardDevice { get; set; }
/// <summary>
/// The spectrum datacorder device
/// </summary>
public virtual DatacorderDevice TapeDevice { get; set; }
2017-11-23 17:26:15 +00:00
2017-11-24 18:43:04 +00:00
/// <summary>
/// Holds the currently selected joysticks
2017-11-24 18:43:04 +00:00
/// </summary>
public virtual IJoystick[] JoystickCollection { get; set; }
2018-03-13 13:09:44 +00:00
/// <summary>
/// Signs whether the disk motor is on or off
/// </summary>
protected bool DiskMotorState;
/// <summary>
/// +3/2a printer port strobe
/// </summary>
protected bool PrinterPortStrobe;
#endregion
#region Emulator State
2017-11-23 17:26:15 +00:00
/// <summary>
/// Signs whether the frame has ended
/// </summary>
public bool FrameCompleted;
/// <summary>
/// Overflow from the previous frame (in Z80 cycles)
/// </summary>
public int OverFlow;
/// <summary>
/// The total number of frames rendered
/// </summary>
public int FrameCount;
/// <summary>
/// The current cycle (T-State) that we are at in the frame
/// </summary>
public int _frameCycles;
/// <summary>
/// Stores where we are in the frame after each CPU cycle
/// </summary>
public int LastFrameStartCPUTick;
/// <summary>
/// Gets the current frame cycle according to the CPU tick count
/// </summary>
public virtual int CurrentFrameCycle => CPU.TotalExecutedCycles - LastFrameStartCPUTick;
/// <summary>
/// Non-Deterministic bools
/// </summary>
public bool _render;
public bool _renderSound;
2018-03-13 13:09:44 +00:00
#endregion
#region Constants
2017-11-23 17:26:15 +00:00
/// <summary>
2018-03-13 13:09:44 +00:00
/// Mask constants & misc
2017-11-23 17:26:15 +00:00
/// </summary>
protected const int BORDER_BIT = 0x07;
protected const int EAR_BIT = 0x10;
protected const int MIC_BIT = 0x08;
protected const int TAPE_BIT = 0x40;
2017-12-07 10:49:43 +00:00
protected const int AY_SAMPLE_RATE = 16;
2018-03-13 13:09:44 +00:00
#endregion
#region Emulation Loop
2017-11-23 17:26:15 +00:00
/// <summary>
/// Executes a single frame
/// </summary>
public virtual void ExecuteFrame(bool render, bool renderSound)
2017-11-23 17:26:15 +00:00
{
2018-03-06 17:57:13 +00:00
InputRead = false;
_render = render;
_renderSound = renderSound;
2018-03-06 17:57:13 +00:00
2017-11-23 17:26:15 +00:00
FrameCompleted = false;
if (_renderSound)
{
BuzzerDevice.StartFrame();
if (AYDevice != null)
AYDevice.StartFrame();
}
2017-12-07 10:49:43 +00:00
PollInput();
2018-03-13 13:09:44 +00:00
while (CurrentFrameCycle < ULADevice.FrameLength) // UlaFrameCycleCount)
2017-11-23 17:26:15 +00:00
{
// check for interrupt
ULADevice.CheckForInterrupt(CurrentFrameCycle);
2017-11-23 17:26:15 +00:00
// run a single CPU instruction
CPU.ExecuteOne();
2017-12-07 10:49:43 +00:00
// update AY
if (_renderSound)
{
if (AYDevice != null)
AYDevice.UpdateSound(CurrentFrameCycle);
2018-03-13 13:09:44 +00:00
}
2017-12-07 10:49:43 +00:00
}
2018-03-13 13:09:44 +00:00
2017-11-23 17:26:15 +00:00
// we have reached the end of a frame
LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow;
// paint the buffer if needed
if (ULADevice.needsPaint && _render)
ULADevice.UpdateScreenBuffer(ULADevice.FrameLength);
2018-03-13 13:09:44 +00:00
if (_renderSound)
2018-03-13 13:09:44 +00:00
BuzzerDevice.EndFrame();
2017-11-23 17:26:15 +00:00
FrameCount++;
// setup for next frame
ULADevice.ResetInterrupt();
2018-03-05 16:12:19 +00:00
TapeDevice.EndFrame();
2017-11-23 17:26:15 +00:00
FrameCompleted = true;
2018-03-06 17:57:13 +00:00
// is this a lag frame?
Spectrum.IsLagFrame = !InputRead;
2017-11-23 17:26:15 +00:00
}
2018-03-13 13:09:44 +00:00
#endregion
#region Reset Functions
2017-11-23 17:26:15 +00:00
/// <summary>
/// Hard reset of the emulated machine
/// </summary>
public virtual void HardReset()
2018-03-13 13:09:44 +00:00
{
//ResetBorder();
2018-03-13 13:09:44 +00:00
ULADevice.ResetInterrupt();
2017-11-23 17:26:15 +00:00
}
/// <summary>
/// Soft reset of the emulated machine
/// </summary>
public virtual void SoftReset()
{
//ResetBorder();
ULADevice.ResetInterrupt();
2017-11-23 17:26:15 +00:00
}
2017-11-28 19:28:22 +00:00
2018-03-13 13:09:44 +00:00
#endregion
#region IStatable
2017-11-28 19:28:22 +00:00
public void SyncState(Serializer ser)
{
ser.BeginSection("ZXMachine");
ser.Sync("FrameCompleted", ref FrameCompleted);
ser.Sync("OverFlow", ref OverFlow);
ser.Sync("FrameCount", ref FrameCount);
ser.Sync("_frameCycles", ref _frameCycles);
2018-03-06 18:03:55 +00:00
ser.Sync("inputRead", ref inputRead);
2017-11-28 19:28:22 +00:00
ser.Sync("LastFrameStartCPUTick", ref LastFrameStartCPUTick);
2018-03-13 13:09:44 +00:00
ser.Sync("LastULAOutByte", ref LastULAOutByte);
2017-11-28 19:28:22 +00:00
ser.Sync("ROM0", ref ROM0, false);
ser.Sync("ROM1", ref ROM1, false);
ser.Sync("ROM2", ref ROM2, false);
ser.Sync("ROM3", ref ROM3, false);
ser.Sync("RAM0", ref RAM0, false);
ser.Sync("RAM1", ref RAM1, false);
ser.Sync("RAM2", ref RAM2, false);
ser.Sync("RAM3", ref RAM3, false);
ser.Sync("RAM4", ref RAM4, false);
ser.Sync("RAM5", ref RAM5, false);
ser.Sync("RAM6", ref RAM6, false);
ser.Sync("RAM7", ref RAM7, false);
2017-12-07 15:43:28 +00:00
ser.Sync("ROMPaged", ref ROMPaged);
ser.Sync("SHADOWPaged", ref SHADOWPaged);
ser.Sync("RAMPaged", ref RAMPaged);
ser.Sync("PagingDisabled", ref PagingDisabled);
ser.Sync("SpecialPagingMode", ref SpecialPagingMode);
ser.Sync("PagingConfiguration", ref PagingConfiguration);
2017-11-28 19:28:22 +00:00
RomData.SyncState(ser);
KeyboardDevice.SyncState(ser);
BuzzerDevice.SyncState(ser);
2017-12-11 14:35:27 +00:00
ULADevice.SyncState(ser);
2017-11-28 19:28:22 +00:00
if (AYDevice != null)
AYDevice.SyncState(ser);
ser.Sync("tapeMediaIndex", ref tapeMediaIndex);
TapeMediaIndex = tapeMediaIndex;
ser.Sync("diskMediaIndex", ref diskMediaIndex);
DiskMediaIndex = diskMediaIndex;
2017-12-07 10:49:43 +00:00
TapeDevice.SyncState(ser);
2017-11-28 19:28:22 +00:00
ser.EndSection();
}
2018-03-13 13:09:44 +00:00
#endregion
2017-11-23 17:26:15 +00:00
}
}