Merge pull request #1218 from TASVideos/ZXHawk-new-ULA

Zx hawk new ula
This commit is contained in:
alyosha-tas 2018-06-13 08:40:26 -04:00 committed by GitHub
commit a992e988b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 4192 additions and 1449 deletions

View File

@ -509,6 +509,12 @@
<Compile Include="config\ZXSpectrum\ZXSpectrumAudioSettings.Designer.cs">
<DependentUpon>ZXSpectrumAudioSettings.cs</DependentUpon>
</Compile>
<Compile Include="config\ZXSpectrum\ZXSpectrumPokeMemory.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="config\ZXSpectrum\ZXSpectrumPokeMemory.Designer.cs">
<DependentUpon>ZXSpectrumPokeMemory.cs</DependentUpon>
</Compile>
<Compile Include="config\ZXSpectrum\ZXSpectrumNonSyncSettings.cs">
<SubType>Form</SubType>
</Compile>
@ -1431,6 +1437,9 @@
<EmbeddedResource Include="config\ZXSpectrum\ZXSpectrumAudioSettings.resx">
<DependentUpon>ZXSpectrumAudioSettings.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="config\ZXSpectrum\ZXSpectrumPokeMemory.resx">
<DependentUpon>ZXSpectrumPokeMemory.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="config\ZXSpectrum\ZXSpectrumNonSyncSettings.resx">
<DependentUpon>ZXSpectrumNonSyncSettings.cs</DependentUpon>
</EmbeddedResource>

View File

@ -384,7 +384,12 @@
this.zXSpectrumToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ZXSpectrumCoreEmulationSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ZXSpectrumControllerConfigurationMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ZXSpectrumAudioSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ZXSpectrumNonSyncSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ZXSpectrumPokeMemoryMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ZXSpectrumMediaMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ZXSpectrumTapesSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ZXSpectrumDisksSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.MainStatusBar = new StatusStripEx();
this.DumpStatusButton = new System.Windows.Forms.ToolStripDropDownButton();
@ -457,7 +462,8 @@
this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator();
this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.timerMouseIdle = new System.Windows.Forms.Timer(this.components);
this.ZXSpectrumAudioSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.zxt1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.zxt2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.MainformMenu.SuspendLayout();
this.MainStatusBar.SuspendLayout();
this.MainFormContextMenu.SuspendLayout();
@ -3401,7 +3407,9 @@
this.ZXSpectrumCoreEmulationSettingsMenuItem,
this.ZXSpectrumControllerConfigurationMenuItem,
this.ZXSpectrumAudioSettingsMenuItem,
this.ZXSpectrumNonSyncSettingsMenuItem});
this.ZXSpectrumNonSyncSettingsMenuItem,
this.ZXSpectrumPokeMemoryMenuItem,
this.ZXSpectrumMediaMenuItem});
this.zXSpectrumToolStripMenuItem.Name = "zXSpectrumToolStripMenuItem";
this.zXSpectrumToolStripMenuItem.Size = new System.Drawing.Size(87, 19);
this.zXSpectrumToolStripMenuItem.Text = "ZX Spectrum";
@ -3421,6 +3429,13 @@
this.ZXSpectrumControllerConfigurationMenuItem.Text = "Joystick Configuration";
this.ZXSpectrumControllerConfigurationMenuItem.Click += new System.EventHandler(this.ZXSpectrumControllerConfigurationMenuItem_Click);
//
// ZXSpectrumAudioSettingsMenuItem
//
this.ZXSpectrumAudioSettingsMenuItem.Name = "ZXSpectrumAudioSettingsMenuItem";
this.ZXSpectrumAudioSettingsMenuItem.Size = new System.Drawing.Size(201, 22);
this.ZXSpectrumAudioSettingsMenuItem.Text = "Audio Settings";
this.ZXSpectrumAudioSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumAudioSettingsMenuItem_Click);
//
// ZXSpectrumNonSyncSettingsMenuItem
//
this.ZXSpectrumNonSyncSettingsMenuItem.Name = "ZXSpectrumNonSyncSettingsMenuItem";
@ -3428,6 +3443,41 @@
this.ZXSpectrumNonSyncSettingsMenuItem.Text = "Non-Sync Settings";
this.ZXSpectrumNonSyncSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumNonSyncSettingsMenuItem_Click);
//
// ZXSpectrumPokeMemoryMenuItem
//
this.ZXSpectrumPokeMemoryMenuItem.Name = "ZXSpectrumPokeMemoryMenuItem";
this.ZXSpectrumPokeMemoryMenuItem.Size = new System.Drawing.Size(201, 22);
this.ZXSpectrumPokeMemoryMenuItem.Text = "POKE Memory";
this.ZXSpectrumPokeMemoryMenuItem.Click += new System.EventHandler(this.ZXSpectrumPokeMemoryMenuItem_Click);
//
// ZXSpectrumMediaMenuItem
//
this.ZXSpectrumMediaMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ZXSpectrumTapesSubMenu,
this.ZXSpectrumDisksSubMenu});
this.ZXSpectrumMediaMenuItem.Name = "ZXSpectrumMediaMenuItem";
this.ZXSpectrumMediaMenuItem.Size = new System.Drawing.Size(201, 22);
this.ZXSpectrumMediaMenuItem.Text = "Media";
this.ZXSpectrumMediaMenuItem.DropDownOpened += new System.EventHandler(this.ZXSpectrumMediaMenuItem_DropDownOpened);
//
// ZXSpectrumTapesSubMenu
//
this.ZXSpectrumTapesSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.zxt1ToolStripMenuItem});
this.ZXSpectrumTapesSubMenu.Name = "ZXSpectrumTapesSubMenu";
this.ZXSpectrumTapesSubMenu.Size = new System.Drawing.Size(152, 22);
this.ZXSpectrumTapesSubMenu.Text = "Tapes";
this.ZXSpectrumTapesSubMenu.DropDownOpened += new System.EventHandler(this.ZXSpectrumTapesSubMenu_DropDownOpened);
//
// ZXSpectrumDisksSubMenu
//
this.ZXSpectrumDisksSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.zxt2ToolStripMenuItem});
this.ZXSpectrumDisksSubMenu.Name = "ZXSpectrumDisksSubMenu";
this.ZXSpectrumDisksSubMenu.Size = new System.Drawing.Size(152, 22);
this.ZXSpectrumDisksSubMenu.Text = "Disks";
this.ZXSpectrumDisksSubMenu.DropDownOpened += new System.EventHandler(this.ZXSpectrumDisksSubMenu_DropDownOpened);
//
// Atari7800HawkCoreMenuItem
//
this.Atari7800HawkCoreMenuItem.Name = "Atari7800HawkCoreMenuItem";
@ -4054,12 +4104,17 @@
this.timerMouseIdle.Interval = 2000;
this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick);
//
// ZXSpectrumAudioSettingsMenuItem
// zxt1ToolStripMenuItem
//
this.ZXSpectrumAudioSettingsMenuItem.Name = "ZXSpectrumAudioSettingsMenuItem";
this.ZXSpectrumAudioSettingsMenuItem.Size = new System.Drawing.Size(201, 22);
this.ZXSpectrumAudioSettingsMenuItem.Text = "Audio Settings";
this.ZXSpectrumAudioSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumAudioSettingsMenuItem_Click);
this.zxt1ToolStripMenuItem.Name = "zxt1ToolStripMenuItem";
this.zxt1ToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.zxt1ToolStripMenuItem.Text = "zxt1";
//
// zxt2ToolStripMenuItem
//
this.zxt2ToolStripMenuItem.Name = "zxt2ToolStripMenuItem";
this.zxt2ToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.zxt2ToolStripMenuItem.Text = "zxt2";
//
// MainForm
//
@ -4530,5 +4585,11 @@
private System.Windows.Forms.ToolStripMenuItem ZXSpectrumCoreEmulationSettingsMenuItem;
private System.Windows.Forms.ToolStripMenuItem ZXSpectrumNonSyncSettingsMenuItem;
private System.Windows.Forms.ToolStripMenuItem ZXSpectrumAudioSettingsMenuItem;
private System.Windows.Forms.ToolStripMenuItem ZXSpectrumPokeMemoryMenuItem;
private System.Windows.Forms.ToolStripMenuItem ZXSpectrumMediaMenuItem;
private System.Windows.Forms.ToolStripMenuItem ZXSpectrumTapesSubMenu;
private System.Windows.Forms.ToolStripMenuItem ZXSpectrumDisksSubMenu;
private System.Windows.Forms.ToolStripMenuItem zxt1ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem zxt2ToolStripMenuItem;
}
}

View File

@ -25,6 +25,7 @@ using BizHawk.Emulation.Cores.Computers.AppleII;
using BizHawk.Client.ApiHawk;
using BizHawk.Emulation.Cores.Computers.Commodore64;
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
using BizHawk.Emulation.Cores.Computers.SinclairSpectrum;
namespace BizHawk.Client.EmuHawk
{
@ -2487,6 +2488,82 @@ namespace BizHawk.Client.EmuHawk
new ZXSpectrumAudioSettings().ShowDialog();
}
private void ZXSpectrumPokeMemoryMenuItem_Click(object sender, EventArgs e)
{
new ZXSpectrumPokeMemory().ShowDialog();
}
private void ZXSpectrumMediaMenuItem_DropDownOpened(object sender, EventArgs e)
{
if (Emulator is ZXSpectrum)
{
ZXSpectrumTapesSubMenu.Enabled = ((ZXSpectrum)Emulator)._tapeInfo.Count > 0;
ZXSpectrumDisksSubMenu.Enabled = ((ZXSpectrum)Emulator)._diskInfo.Count > 0;
}
}
private void ZXSpectrumTapesSubMenu_DropDownOpened(object sender, EventArgs e)
{
ZXSpectrumTapesSubMenu.DropDownItems.Clear();
if (Emulator is ZXSpectrum)
{
var speccy = (ZXSpectrum)Emulator;
var currSel = speccy._machine.TapeMediaIndex;
for (int i = 0; i < speccy._tapeInfo.Count; i++)
{
string name = speccy._tapeInfo[i].Name;
var menuItem = new ToolStripMenuItem
{
Name = i + "_" + name,
Text = i + ": " + name,
Checked = currSel == i
};
int dummy = i;
menuItem.Click += (o, ev) =>
{
speccy._machine.TapeMediaIndex = dummy;
};
ZXSpectrumTapesSubMenu.DropDownItems.Add(menuItem);
}
}
}
private void ZXSpectrumDisksSubMenu_DropDownOpened(object sender, EventArgs e)
{
ZXSpectrumDisksSubMenu.DropDownItems.Clear();
if (Emulator is ZXSpectrum)
{
var speccy = (ZXSpectrum)Emulator;
var currSel = speccy._machine.DiskMediaIndex;
for (int i = 0; i < speccy._diskInfo.Count; i++)
{
string name = speccy._diskInfo[i].Name;
var menuItem = new ToolStripMenuItem
{
Name = i + "_" + name,
Text = i + ": " + name,
Checked = currSel == i
};
int dummy = i;
menuItem.Click += (o, ev) =>
{
speccy._machine.DiskMediaIndex = dummy;
};
ZXSpectrumDisksSubMenu.DropDownItems.Add(menuItem);
}
}
}
#endregion
#region Help

View File

@ -2752,8 +2752,16 @@ namespace BizHawk.Client.EmuHawk
var attributes = Emulator.Attributes();
CoreNameStatusBarButton.Text = Emulator.DisplayName();
CoreNameStatusBarButton.Image = Emulator.Icon();
CoreNameStatusBarButton.ToolTipText = attributes.Ported ? "(ported) " : "";
CoreNameStatusBarButton.Image = Emulator.Icon();
CoreNameStatusBarButton.ToolTipText = attributes.Ported ? "(ported) " : "";
if (Emulator.SystemId == "ZXSpectrum")
{
var core = (Emulation.Cores.Computers.SinclairSpectrum.ZXSpectrum)Emulator as Emulation.Cores.Computers.SinclairSpectrum.ZXSpectrum;
CoreNameStatusBarButton.ToolTipText = core.GetMachineType();
}
}
private void ToggleKeyPriority()
@ -4304,6 +4312,7 @@ namespace BizHawk.Client.EmuHawk
{
GenericCoreConfig.DoDialog(this, "PC-FX Settings");
}
private bool Rewind(ref bool runFrame, long currentTimestamp, out bool returnToRecording)
{

View File

@ -0,0 +1,165 @@
namespace BizHawk.Client.EmuHawk
{
partial class ZXSpectrumPokeMemory
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ZXSpectrumPokeMemory));
this.OkBtn = new System.Windows.Forms.Button();
this.CancelBtn = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.numericUpDownAddress = new System.Windows.Forms.NumericUpDown();
this.label3 = new System.Windows.Forms.Label();
this.numericUpDownByte = new System.Windows.Forms.NumericUpDown();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownAddress)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownByte)).BeginInit();
this.SuspendLayout();
//
// OkBtn
//
this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.OkBtn.Location = new System.Drawing.Point(150, 109);
this.OkBtn.Name = "OkBtn";
this.OkBtn.Size = new System.Drawing.Size(60, 23);
this.OkBtn.TabIndex = 3;
this.OkBtn.Text = "&OK";
this.OkBtn.UseVisualStyleBackColor = true;
this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click);
//
// CancelBtn
//
this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.CancelBtn.Location = new System.Drawing.Point(216, 109);
this.CancelBtn.Name = "CancelBtn";
this.CancelBtn.Size = new System.Drawing.Size(60, 23);
this.CancelBtn.TabIndex = 4;
this.CancelBtn.Text = "&Cancel";
this.CancelBtn.UseVisualStyleBackColor = true;
this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 14);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(273, 13);
this.label1.TabIndex = 17;
this.label1.Text = "Enter an address to POKE along with a single byte value";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(12, 52);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(93, 13);
this.label4.TabIndex = 27;
this.label4.Text = "Address (0-65535)";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 27);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(266, 13);
this.label2.TabIndex = 29;
this.label2.Text = "(Address values that target ROM space will be ignored)";
//
// numericUpDownAddress
//
this.numericUpDownAddress.Location = new System.Drawing.Point(15, 69);
this.numericUpDownAddress.Maximum = new decimal(new int[] {
65535,
0,
0,
0});
this.numericUpDownAddress.Name = "numericUpDownAddress";
this.numericUpDownAddress.Size = new System.Drawing.Size(90, 20);
this.numericUpDownAddress.TabIndex = 30;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(123, 52);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(70, 13);
this.label3.TabIndex = 31;
this.label3.Text = "Value (0-255)";
//
// numericUpDownByte
//
this.numericUpDownByte.Location = new System.Drawing.Point(126, 68);
this.numericUpDownByte.Maximum = new decimal(new int[] {
255,
0,
0,
0});
this.numericUpDownByte.Name = "numericUpDownByte";
this.numericUpDownByte.Size = new System.Drawing.Size(67, 20);
this.numericUpDownByte.TabIndex = 32;
//
// ZXSpectrumPokeMemory
//
this.AcceptButton = this.OkBtn;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.CancelBtn;
this.ClientSize = new System.Drawing.Size(288, 144);
this.Controls.Add(this.numericUpDownByte);
this.Controls.Add(this.label3);
this.Controls.Add(this.numericUpDownAddress);
this.Controls.Add(this.label2);
this.Controls.Add(this.label4);
this.Controls.Add(this.label1);
this.Controls.Add(this.CancelBtn);
this.Controls.Add(this.OkBtn);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "ZXSpectrumPokeMemory";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Poke Memory";
((System.ComponentModel.ISupportInitialize)(this.numericUpDownAddress)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownByte)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button OkBtn;
private System.Windows.Forms.Button CancelBtn;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.NumericUpDown numericUpDownAddress;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.NumericUpDown numericUpDownByte;
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Linq;
using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Emulation.Cores.Computers.SinclairSpectrum;
using System.Text;
namespace BizHawk.Client.EmuHawk
{
public partial class ZXSpectrumPokeMemory : Form
{
private ZXSpectrum.ZXSpectrumSettings _settings;
public ZXSpectrumPokeMemory()
{
InitializeComponent();
}
private void OkBtn_Click(object sender, EventArgs e)
{
var speccy = (ZXSpectrum)Global.Emulator;
var addr = (ushort)numericUpDownAddress.Value;
var val = (byte)numericUpDownByte.Value;
speccy.PokeMemory(addr, val);
DialogResult = DialogResult.OK;
Close();
}
private void CancelBtn_Click(object sender, EventArgs e)
{
GlobalWin.OSD.AddMessage("POKE memory aborted");
DialogResult = DialogResult.Cancel;
Close();
}
}
}

View File

@ -0,0 +1,624 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAwAMDAQAAAABABoBgAAxgAAACAgEAAAAAQA6AIAAC4HAAAYGBAAAAAEAOgBAAAWCgAAEBAQAAAA
BAAoAQAA/gsAADAwAAAAAAgAqA4AACYNAAAgIAAAAAAIAKgIAADOGwAAGBgAAAAACADIBgAAdiQAABAQ
AAAAAAgAaAUAAD4rAAAwMAAAAAAgAKglAACmMAAAICAAAAAAIACoEAAATlYAABgYAAAAACAAiAkAAPZm
AAAQEAAAAAAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAQAAAAEAAAAAAA
AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP//
/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAHR3AAAAAAAAAAAAAAAAAAAAAAAAAAAAdHdEcAAAAAAAAAAAAAAAAA
AAAAAAAAAHd0d3QAAAAAAAAAAAAAAAAAAAAAAAAAAEd8d3UAAAAAAAAAAAAAAAAAAAAAAAAAB3yHfHZw
AAAAAAAAAAAAAAAAAAAAAAAAd3fIyHVwAAAAAAAAAAAAAAAAAAAAAAAAfHh3jIxwAAAAAAAAAAAAAAAA
AAAAAAAHd8jIyHdgAAAAAAAAAAAAAAAAAAAAAAAHd4yHfIdAAAAAAAAAAAAAAAAAAAAAAAAHyMjIyMhQ
AAAAAAAAAAAAAAAAAAAAAAB3d3eMh4dgAAAAAAAAAAAAAAAAAAAAAAB8jIyIfIdQAAAAAAAAAAAAAAAA
AAAAAAB3h4jIiMh3AAAAAAAAAAAAAAAAAAAAAAB8jIeHeIjHAAAAAAAAAAAAAAAAAAAAAAeIiHh4eMiE
AAAAAAAAAAAAB0dHcAAAAAd8h4eIiIiHcAAAAAAAAAB0d3d3RwAAAAeIeIiIiIh3RwAAAAAAAHR3d8h3
dAAAAAfIh4iIiHiIx0cAAAAAdHh3eIeHhwAAAAeHiIiIiIiId3R3dHR0eHd4h4eHhAAAAAd4eIiIiIiH
x3d2d3eId4iIiIiIhwAAAAd4eIiI+IiIh3d3eHh3iIiIiIeHwAAAAAfIjHeIiIiIyIeHh4iIiIiIiIiI
cAAAAAeIQ0R3h3iIiMiIiIiIiIiIiIiEAAAAAAfIR3d3d0iIiIh4iIeIiIiIiHhAAAAAAAB4d3d3SHiI
h4fTiIi3iIiIeIwAAAAAAAB3h4d3eIeIiHiJiIuIiIh4jHAAAAAAAAAHyId3h3h4iIh4iIiIiIiHeAAA
AAAAAAAAB8iMiMjIiIiIh4h3aMjHAAAAAAAAAAAAAAdYyIeIiIiMjId6d4eAAAAAAAAAAAAAAAAHdsjH
eIeH6MiId3AAAAAAAAAAAAAAAIiIh4V8jIh4eIfHcAAAAAAAAAAAAACIiIh3AAAHd3h3fHcAAAAAAAAA
AAAAAAiIjHgAAAAAAHx8eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA////////
AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A
H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP////
AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA
AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/
AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP//
/////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC
AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/
AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwAAAAAAAAAAAAAAAA
AAd0dAAAAAAAAAAAAAAAAAB3x3cAAAAAAAAAAAAAAAAAd3fHcAAAAAAAAAAAAAAAB3yMh3AAAAAAAAAA
AAAAAAfIeMdwAAAAAAAAAAAAAAAHjIyHQAAAAAAAAAAAAAAAfId4yHAAAAAAAAAAAAAAAHjIyIdQAAAA
AAAAAAAAAAB3iId4YAAAAAAAAAdwAAAAjIiIiIUAAAAAAHd3dAAAB4iIiHh8cAAAAHd3x4dwAAd4iIiI
h3Z3d3R3yIh4cAAHh4iIiIfHd3d4iIiIh3AAB3jHiIiIiHeHiIiIiIwAAAh3dXh4iMiIiIiIiIhwAAAA
yGd0d4iIeIi4iIiMAAAAAIeHd4iIh32IiIiIcAAAAAAAd4jIyIiIiHeHyAAAAAAAAAB3h4iIh8h3dwAA
AAAAAAAIh8fIh4eIaAAAAAAAAACIiHAAB8jIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////
////////////////////n////g////wP///8B///+Af///gH///4B///8Af///AH///wB//n8AP/A+AB
/AHgAAAB4AAAAeAAAAPgAAAH8AAAD/AAAB/8AAA//wAA//4AA//weA//////////////////////////
//8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAA
AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRwAAAAAAAAAAAAB3dAAAAAAAAAAAAA
d8dwAAAAAAAAAAAAfId3AAAAAAAAAAAHeMjHAAAAAAAAAAAHyHh3AAAAAAAAAAAHh3eEAAAAAAAAAAAI
yIiHAAAAAHd2cAAIiIiIQAAAd3d4UACHiIiId3d3eHiIcACHh4iIyHeHiIiIcAAIR3d4iIiIiIiMAAAH
d3eIh3iIiIhwAAAAeMh4iIiHiMAAAAAAAHfIiMh4aAAAAAAAiIgHyIfIAAAAAAAIgAAAAIAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD8f/8A+H//APB/
/wDwP/8A4D//AOA//wDgP/8A4D/BAOAfAQDAAAEAwAABAOAAAwDgAAcA8AAfAPwAPwDwgP8A5/f/AP//
/wD///8A////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA
AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAd1AAAAAAAAB8cAAAAAAAB4eAAAAAAAAHyMgAAAAAAAiIhwAAAHcACI
iHcAd3hwAIz4jIeIiIAAd3eIiIiIAACHeIiIiHAAAACMeMh4AAAAiAAIgAAAAAAAAAAAAAAAAAAAAAAA
AAD//wAA//8AAP//AADj/wAA4/8AAMP/AADB/wAAwfkAAMDBAADAAQAAwAMAAMAHAADwDwAAzn8AAP//
AAD//wAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA9OzsAZD8/AGg8
PABtPj4AQkNDAEZIRwBWQkIAV0REAF5AQABbRkYAVklJAFxPTwBTU1MAXFJSAF5ZWQBkQEAAYUREAGZF
RQBqQkEAYEtLAGNPTwBwQUEAfUZGAHJKSgB2SUkAfU9PAGBRUQBgVFQAZlZWAGZYWABqWVkAclZWAHpU
VAB9W1oAbmJiAGtoaABtaWkAcWdnAHdnZwB8Y2MAe2pqAHJxcQB+dHQAd3l5AHl6egCGT08AiU9PAIFP
UACGU1MAjVFRAIlWVgCMV1cAg1xbAIxaWQCQUlIAlVJSAJFXVgCXVVUAmVVVAJZaWQCSXV0AlV9eAJpZ
WgCeW1sAml5eAKBZWgCgXFwAql9fAIRmZQCIZWQAhWtrAI5ragCTYmEAnGBhAJ9kYwCaZmYAk25uAJ1s
awCFdHQAiXd3AIt+fgCWd3cAmHR0AJV5eQCbfHwAo2JhAKZhYQChZWUApGVkAKplZACsZGQAqmhnAKZr
agCnbGsAqmloAKlubQCsbW0AtGZnALhsbACxb3AAv29wAKVxcACrc3IAr35+ALN0cwC5c3MAvXBxALR4
dgC1fHsAunt6AMNtbgDGb3AAw3FyAMZwcQDGdXUAyHR1AMp3eADBeXkAxnt7AMB/fgDLensANLBSAEWf
TgBBtFwAPMdnADHkdgDciiIAvoF/AISrdwDln0sA35lhAN2XfADgmmEA8LdlAO61cAArWPIALWT+AEh5
+gDOf4AAfoCAAHiA1ABZv9wAZrnUAGK+2ABxnv4Ad6P/ADPX/QBw0OcAW+D7AIKEgwCPgoIAjI2NAJuC
ggCUiIgAmYqKAJGSkgCjhIQAqoKCAKKLiwC+hIMAsoqKALaSgQCum5sAsZubALqqlQCdgr4Ar6ytALGh
oAC6pKQAwoSDAMyBggDGiIYAyYiHAMWMigDMjIoA0ISFANKHiADUjIwA2Y6NAMCUjQDIk44A0JCPANaP
kADHlZQAzpSSAMScmwDUkpIA2ZSVANWYlgDampcA2ZeYANWcnADam5sA4p2cAMChjwDeoJ4A5aCFAOaj
jQDlpJoA2p6hAMOkowDOoaEAy62tANegoADdoqEA2aGpANGsrwDdq6kAwbG4ANGysQDdtLQA2ri3AOGk
owDjqKYA66ylAOGnqADjq6oA6a2rAOOwrwDssK4A5K+wAOaztADttLIA57i2AO24tgDmurgA6rq6APC1
swDyuLYA9Ly5APi+uwD1wL0A+cC9AKKMwACkk8QAqprMALSayACptsEAlaDkAOy/wACRxtQAgOv9AJnr
9wDEwsoA5sbGAOzCwgDuyMcA7MzMAPPEwgDxy8oA9dPTAPja2gAAAAAAAAAAAP///wAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
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
AAAAAAAAAADLytDi9tOemQAAAAAAUy9EecLEsa1uPTUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPj11Mme
VakAAAAAAAAAAAAATS84M0akAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA////////
AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A
H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP////
AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA
AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/
AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP//
/////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAE
AAAAAAAAAAAAAAABAAAAAQAAAAAAAFFNTQBRUlIAU1RUAGJHRwBiT08Aa0lIAGJTUwBrVlYAYllZAGZc
XABpWloAb1xbAHNTUwB7V1YAc1hXAHFbWwBkZWUAaWFhAG5kZABpamkAcGFhAHlubgB2cHAAf3V1AH55
eQB8fX0AgUpKAI1PTwCLWFcAhlhYAI9ZWQCKXFsAm1ZWAJJZWQCWWVgAmlpbAJtcWwCiXFwAl2BfAIBg
YACAZ2YAgG9vAI9oaACWZWQAmGBhAJ5kZACcaWoAmm9vAIV0dACNcHAAiXZ2AIB8fACac3IAm3V0AJ51
dQCZfHwAnHx8AKNmZgCnZmYAqmJiAK5jYwCvb24AtWVmALBtbgC5bW0AvmxtAKx+fQCxcnIAtHBwALZz
dACydXQAtnd2ALlwcAC5dnYAt3p5ALh5eAC8fHsAun18ALx+fQDGb3AAxnBxAMdzdADAd3YAyHJzAMlz
dADJdXYAynd4AMd/fwDMe3wAzXx9AHunbwBhvHIAYsN4ANuLOwC2hn4A4Zt5APC3ZABte9sAX47+AHWM
5QAl0foAY+P8AIeDgwCFhoYAioSEAJOIiACWi4sAmpKRAKGCgQCmhYUAqYGBAKuDhACniooApYyMAKiO
jQCyhYMAvoWEALeNjQCrj5AAr5eXALSVlAC9lJMAmbCEAK6RugDBgYAAwoSCAMWDhADChoQAxYeFAM6A
gQDFiIYAxoqIAMqIiQDMi4oAy4yKAMiPjQDPj44A0ISFANKJigDUi4wA04+NANWNjgDKkY8A0JCOANud
iQDWj5AAzJSTAM2XlgDGm5oA1pGSANOUkgDVl5EA1pOUANiVlgDYmJUA2ZeYANKenADbmpsA3pmYANuc
mgDbn5wA1aacAN6gngDqqZoA3Z+gAMyjowDCra0AxqysAMqpqQDboaAA3qKiAN6logDbp6UA3aWkANer
qgDWsbMA0rW0ANe0tADfs7IA4aSiAOGlpQDkp6UA46imAOWopgDsraIA6qimAOGoqADhrqwA6a2rAOqv
rADpsK4A7LGuAOGzswDlsbEA7bKxAO+1sgDotrYA5rm3AO+4twDot7sA6bq5AOu9uwDrv70A8bazAPG2
tADxuLUA9Lm2APC9uwD2vboA9L+9APi+uwD4v7wA8sC+APXAvgD5wL0AkILJAKqXzACsu8cAqr/LALLV
3QDawMIA48XFAOvDwQDswMAA7cTDAO/ExQDgxsgA8cbEAPTGxADwyskA9MvJAPLNzQD21dYA+NjZAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAMEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHCEcBQAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAayU9PSYbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdQlBSQiJpAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAM0pSUlJQPRcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnUlJSUlJGFQAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAFJSUlJSUkoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzUlJSWVJZfxAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5XWYqKioqGDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASoqMkpqa
mqAsAAAAAAAAAAAAAAAAAABoNAAAAAAAAACMjJyuvLy2toYHAAAAAAAAAAAAABcOIDouBgAAAAAAc4yc
tsHKysPAriIKAAAAAAAAABYgRk1LTX+DEAAAAABukqXB4ejo4dHPQCIEChcXEwggTXV/k66unKMpAAAA
AG6Srsro6ero0dN/Rk1NRk2Dg4STrsbh4cHAt2sAAAAAbpKuOXPe6ajW15KGg4OGk528yuHo5eHPz882
AAAAAAB4jCkDAxSoMabXt5yjt8ro3ePo5dbT09HTdAAAAAAAAABGcBFoGgFwdtfDwHxi2dpmZcrX09HP
z0MAAAAAAAAAAHh/qWwaOa6cz9PNZGPYsdzbzc3DwLk2AAAAAAAAAAAAAAAvhpKakoyg19HNyKS5wHtb
orZ/cwAAAAAAAAAAAAAAAAAANkaKWVm5zb1gYV6cXVxfNgAAAAAAAAAAAAAAAAAAALGvlTIuP1K5tqCR
l4xfLwAAAAAAAAAAAAAAAAAAsbPBenkAAAAAcCVYjE0scwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////////+f///+D////A////wH
///4B///+Af///gH///wB///8Af///AH/+fwA/8D4AH8AeAAAAHgAAAB4AAAA+AAAAfwAAAP8AAAH/wA
AD//AAD//gAD//B4D////////////////////////////ygAAAAYAAAAMAAAAAEACAAAAAAAQAIAAAAA
AAAAAAAAAAEAAAABAAAAAAAAWlJSAHBJSQB1SEgAe1dXAHdYWAB5WlkAel1dAGBiYgB1bGwAfWtrAHh2
dgB9fn4Ag01NAIRXVwCIV1cAhV9eAItbWgCgX14ApV1dAJhgXwCNYGAAnWtqAJhtbQCCdnYAh3x8AI15
eACeensAqGBgAKhoZwCga2oArGpqALNqagCzb28AtG1tALltbQCxb3AApnVzAKlzcwCqdHMApnp6AKd+
fgCpensAq3x7ALZ3dgC8dHQAvH59AMZvcADGcHEAxXN0AMhycwDJdncAynh5AMx5egDNfn8Ajo1wAOek
VgDGgH8A4p53AEZ2+gB8u4AAd8PaAIuEhACOh4cAjo6OAJ+DggCejo4Ao4SEAKSIiACsi4sAqo2MAK6P
jgC+gYAAvoaGAL+KiACskJAAtJeXALWenQC5np4At6iOAKmyjgC9nroAwYSDAMaGhADOhoYAxomHAMiK
iQDJjYwA0oeIANOOjwDUjY0A2ZiPANaPkADGkZEAx5eXAMySkADGnZwA1ZOSANeTlADWl5YA2JSVANGZ
mADan50A3J6dAOCcmwDVoJ8A7K2fAMOtrQDXo6IA3aCgAN+kpADVq6oA3ay3AMu0tADPtrYA3L+/AOCi
oQDhpqUA5KelAOinpgDlq6gA46usAOOvrQDqrqwA7LGuAOayswDjtrQA5re1AOqysQDts7EA57y6AO+8
ugDrvL0A8LOwAPC1sgDwtrQA87q3APS6twD2vboA8b69APi/vAD2wb4A+cC9AJmTzwDHqMMAu8PMAIHf
8QDByNAA7cLCAO3FwwDvxsQA5cjIAOzOzgDwxcQA9cbEAPPP0AD10tIAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
BQMJAAAAAAAAAAAAAAAAAAAAAAAAAAAPHBMNAAAAAAAAAAAAAAAAAAAAAAAAABojLy8TAAAAAAAAAAAA
AAAAAAAAAAAAAB0wMDAiPgAAAAAAAAAAAAAAAAAAAAAAQjAwMDAtGAAAAAAAAAAAAAAAAAAAAAAAFzIy
NTU5CgAAAAAAAAAAAAAAAAAAAAAAIjZYWFxcBwAAAAAAAAAAAAAAAAAAAAAANlxtdW11JQAAAAAAAAAA
PgcRDgkAAAAAXG1/lISAZgMAAAAAABkVLC5SVhcAAABNY3WWnJuLfB8UBAcQHkhWaX91dSsAAABNY2BM
mJeCiVJSVl9laX+WloSJgEIAAAAAXAEIC0tGjnR0dJaRk5qNjIyJQwAAAAAAJkNADBtdjIaPO1GSPYuJ
hnVEAAAAAAAAAClISWRcd4xwkGp8UE90VwAAAAAAAAAAAAAAKSQ1NYZ7OjhbPDdGAAAAAAAAAAAAAHNv
YGsAKyJoXFYmRwAAAAAAAAAAAAAAcnIAAAAAAAAATgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP//
/wD///8A////APx//wD4f/8A8H//APA//wDgP/8A4D//AOA//wDgP8EA4B8BAMAAAQDAAAEA4AADAOAA
BwDwAB8A/AA/APCA/wDn9/8A////AP///wD///8AKAAAABAAAAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAA
AAAAAQAAAAEAAAAAAABjZGQAdmRjAHtpaQB/eHgAgU9PAKBaWgCFbm0AlWtqAKptbgCwZ2cAsGhoAKxw
cACteHkAvnJyAMZvcADGcHEAy3l5AMx9fgCFmXQAwIB/ANeUfQDhoX8AlIqJAJWMjACYiIgAoIaGAK2K
igCxh4cAvoGAALKKigC4iYgAuJWVAL2cnACss50AuqKhAL+mpgDLgoIAxImHAMeNjADLkI8AxpWTANCS
kQDYlZUA1J6dANqZmgDdnp4A1J+oAMaiogDOr68AzLKyANi5uADhpaIA4qypAOWtqADrrqsA4bKwAOay
sgDtuLYA57++AOy4uADxtLIA8be0APa9ugDswL4A9sG+ALCcxwC5ncIA06zBALnH0QC2ytQA7sPDAPLS
0gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAZBgUAAAAAAAAAAAAAAAAACw8KAAAAAAAAAAAAAAAAGhAQDgAAAAAAAAAAAAAAAAkRESUYAAAA
AAAAAAAAAAAlKy4uBwAAAAAAAAcDAAAAKzlHPCYCAAAYCB0oKgAAAC0wSDs0FB0nLDlAOiwAAAANAQQb
Pi9DRkVBPzUAAAAAJB4cKz5EQjMiNSkAAAAAAAAAHwwRNxYVEyQAAAAAAAAxMgAAACEgAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AAD//wAA4/8AAOP/AADD/wAAwf8AAMH5
AADAwQAAwAEAAMADAADABwAA8A8AAM5/AAD//wAA//8AACgAAAAwAAAAYAAAAAEAIAAAAAAAgCUAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAkAAAAJAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAUAAAAOAEBAVUAAABUAAAANQAAABAAAAABAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAkFBSUvGRl5TCkpwlYuLtxDJCTQFw0NmQAA
AEkAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGAwMKE8rK6V6RET2klJR/5ZS
U/+OT0//ZDc38B0QEJoAAAAyAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYDAwYVzAwoopP
T/ygXVz/oFtb/55ZWf+bWFf/k1NT/1UvL9wGAwNcAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AARNKipxhk5O+adkY/+uZWX/tWdo/7VmZ/+qYWH/nltb/3hERPcfERGCAAAAFgAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAADEZGS1zQ0LXqGdm/7ptbf/Fb3D/x3Bx/8hwcf/BbW7/q2Vl/4hPT/82HR2gAAAAIAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAB1gxMYyYXl3/vXFx/8Zwcf/HcHH/x3Bx/8dwcf/HcHH/uG1t/5NY
V/9EJia2AAAAKQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPB8fNH1MS+K4cnH/x3Fy/8dwcf/HcHH/x3Bx/8dw
cf/HcHH/wHBx/51gX/9PLCzGAAAAMwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
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
AAMAAAAAAAAAAAAAAAAAAAAALhAQFIZXVs/RjIz/1Y2O/9qYmP/eoaL/46qr/+aysv/ot7f/6rm5/+m4
uf/otbX/5q+v/+uvrf/jqab/wYeF/28/P/QhEhKvAAAAXwAAACgAAAANAAAABQAAAAMAAAACAAAAAwAA
AAUAAAAKAAAAFQAAADAdDg9oSSkptHZHRu2dYmL+t3Z1/758e/+6enn/tnh3/7d5eP+8fn3/w4SD/7Z6
ef9eODfbBgICTgAAAAgAAAAAAAAAAAAAAAAAAAAAPhwcJJVjYuPXkZH/2JOU/92fn//iqqr/57O0/+u8
vP/uwsL/78XG/+/Exf/twMD/67i4/+60sv/wtrP/zZKQ/5taWv9xQED2MRsaxAgEBIcAAABaAAAAQQAA
ADcAAAA2AAAAOwAAAEUEAgJZHA4OfUcnJ7l5SkntqGxr/8CAfv/DgoH/vH59/7p+ff/DiIb/zZGP/9GT
kf/UlJP/1peV/9eZl/+GVlbuGQsLVwAAAAcAAAAAAAAAAAAAAAAAAAAARiIiLZ9rauvZk5P/2peY/+Ck
pP/lsLD/6ru7/+/Fxf/yzMz/9NDQ//PPz//xycr/7sDA//K5tv/1u7j/36Kg/6dmZf+mZWX/j1ZW/WM6
OutDJSXQNBwcvDAaGrQ0HBy1PiIivUwsLMtkPDzfh1VU9a1xcP/EhIP/xIWE/7+Cgf/Ch4b/zZST/9mk
ov/grq3/4a6t/96lo//eoJ7/36Kg/+Cjof+IWVjnGwwMQwAAAAIAAAAAAAAAAAAAAAAAAAAARyQkL6Br
auzZk5P/25qb/+GnqP/ntLT/7cDA//LLy//209T/+NjY//fX1//00ND/8cbG//W9u//4vrz/46ak/7d0
c/+vb27/s3Jy/7d2df+ucXD/pWpp/6Npaf+nbWz/sHVz/7p9fP/EhYT/yImI/8WIhv/DiIb/ypGP/9eg
n//hr63/57q5/+rCwP/rwsD/6bq4/+evrf/nq6n/6q6r/9qgnv9wRkbDBwAAHgAAAAAAAAAAAAAAAAAA
AAAAAAAASCQkLZ1nZuvYkpP/25uc/+Opqv/qtrf/7cHB//TOzv/52Nj/+tzc//na2v/xz9D/8MfH//fA
vv/6wb7/6a6r/8OBgP/DgoD/vX58/7h7ev+8fn3/woOC/8aHhv/HiYj/xoqJ/8aLif/Ijoz/zZST/9eg
nv/hrav/6Lm3/+zCwf/uyMf/78nH/+/Dwf/uvLr/7ba0/+60sf/vtLL/8ri1/7J+fflMKSltAAAABAAA
AAAAAAAAAAAAAAAAAAAAAAAAQyEhI5JcXOPWj5D/3Juc/8qVlf+BZmb/bl5e/4l4eP/AqKj/8tPT//LO
zv+5p6b/w6qq//fBv//7wr//8LWy/86Ojf/Ojoz/0ZGP/9GSkP/OkY//zpOR/9GamP/VoJ//2qel/+Gv
rf/nt7X/6727/+3Dwf/wycf/8czL//LLyf/yxsT/8cC+//G7uf/yubf/87m3//S7uP/4vrv/1J6c/3JH
RrAdCgsWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANRcXEYJNTcvPiIn/15aW/2VNTf85Ojr/Q0VF/0JF
RP9dXFz/n5GR/+S/v/+bh4f/hXp6/+25uP/7wr//9bu4/9qcmv/Zmpj/252b/96gnf/ipKH/5q+s/+u+
vP/vycf/8srI/+3Hxv/wysj/9c7M//TNy//0ysj/9MbE//TBv//1vrz/9r26//e9u//4vrv/+L+8//vB
vv/hqqf/g1ZVzDwcHC4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW4+Ppq/env/05OT/2ZX
V/9rbm7/fX9//3l6ev99f3//cHJy/5F9ff+ff3//XFhY/9eop//8wr//+L+8/+Wppv/ipaP/5qil/96i
pP/Kmaz/1qi1//LGxP/tyMf/qb3J/23E3P9kw9//vMTN//jDwP/3wb//+MC9//i/vf/5v73/+b+8//i/
vP/3vrv/+L68/92mo/+IWlnRRSMjOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFcv
L0mbX1/y15GS/6GAgP9XV1b/iYuL/4CBgf98fX3/cnR0/1dPT/++j4//km9w/9Sfnv/6wL3/+cC9/+6z
sP/ssK3/0Z+u/4OH1P9YffD/QGPs/7KYyv/Ct7z/Ytrz/3Ts//8s2f//cbvU//m+u//4v7z/+L67//e9
uv/1vLn/9Lq3//O5tv/zuLX/0puZ/4RVVctGIyM4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAADIXFwdrPDySq2ts/diZmf/ApKT/sKur/4CBgP95enr/iYiI/49zdP/do6P/36Ch/96e
nv/zuLX/+sK///W7uP/1ubT/qZC//2qY+/9tnf//MGT6/56FxP/esK//nMbS/57n8/9+z+T/ybG3//a6
t//zubb/8re0//C1s//utLH/7rKw/+qvrP++iIb9dklJtkMgISoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHIyMSazw8kZ5hYvXNjI3/2aSk/7OMjP+bd3f/sIKC/9KV
lv/cnJz/2peY/9aRkf/koqL/+sG+//nAvf/5v7z/4amw/6qZx/+aouP/qpvP/+mxtv/2urj/6rGv/+S6
u//ptrX/466n/+Ovqf/ssK7/6q6s/+isqv/oq6n/2J2b/6JubfFoPT2NOxoaFwAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBoaCFowMFd7SEjAomZm9sWC
gv/XkZL/25SV/9iSk//Wj5D/1IyN/9KHiP/UiIj/8bOx//rCv//3vbv/9ru4//O3s//xuLX/7q6e/+ej
hf/npIn/7bCp/+Otp/+KsX3/ULdm/1WjWv+7oYz/5KWk/9uenP+4gH79glJRzVYuLlQgCAkGAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAA8HBwQVy4uS3FBQaCPV1fjsG5v/cmAgf/ShYb/0YKD/85+f//LeXr/2I2M//e8uf/1vLn/7rOx/+2y
sP/lpJX/5qFY/+6xXP/djS3/35h9/86gl/9SwW7/Nd90/0WxXP+vlH//wYSE/49cW+VlOTmBQR4eHAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGk7OhqIWFd8oG5u8J5qav+eX2D/tmts/8Z0df/KdHX/yXJz/92T
k//3vLn/7LGu/+Snpf/dm5L/4Z1q/+61dP/fmmX/15WM/9eYlv/Bm43/r6uR/6uNgP+WYWDtbkBAnUwn
JzQVAQECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiFJSBnhC
QgpqNDQJWSUlB08dHQdfKisKfENDFJJWViinbGtRvYOCjtOcm8/pt7X157y6/7eOjfhxRUW7aTk5m4RK
StehWlr6uGdo/8Zwcf/dkpH/8bSx/+OnpP/YmZj/1ZWT/9ealP/Vl5X/0JCP/8eIhv+zdnb/lFtc6nA/
QKRSKio/JQwNBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AADTn6AB2qioDMuUlCHBhYU8voCAWcCBgXTEhoaLzZGQqdeensngrKvn47Sz/NOop/+yiIfyi2Bgs2k+
PlZXKysPAAAAAUYlJRxcMTFYcj4+pYpMTeWmXF3+xnl5/9+Zl//dnJr/z46M/8KCgf+vc3L/ll9e831L
S8hlOTl/TigoMy0REQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAABzQUIDnmprDriGhifHlpZMzp6eeNCgoZ7On5+2yJqaybuPj9WnfHzVj2RkunVJ
SYNbLy8/PRQUCgAAAAAAAAAAAAAAAAAAAAAAAAAAKRUVBU0pKSphNDRtd0BAsotNTd2ZW1vrkVlY4HtJ
Sb5lOTmCUysrQTsbGxEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWCwsA2Y4OA5xQkImdkhIRHhKSll0R0dibUBAWWI2
NkNUKCgoOhISDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMhkZB0km
Jh5LJiYsRSEhITATFAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP//
/////wAA////////AAD///////8AAP///////wAA////////AAD/+H////8AAP/gH////wAA/8Af////
AAD/gA////8AAP+AD////wAA/wAP////AAD/AA////8AAP4AB////wAA/gAH////AAD8AAf///8AAPwA
B////wAA/AAH////AAD8AAf///8AAPgAB////wAA+AAH//4HAAD4AAP/8AEAAPgAAf/AAQAA8AAA/wAA
AADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAEAAPAAAAAAAQAA8AAAAAADAADwAAAAAAcAAPAA
AAAADwAA+AAAAAAfAAD4AAAAAD8AAPwAAAAAfwAA/gAAAAD/AAD/gAAAA/8AAP/gAAAH/wAAgAAAAB//
AAAAAAAAf/8AAAAD4AP//wAAgB/8H///AAD///////8AAP///////wAA////////AAD///////8AAP//
/////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAYAAAAZAAAAGQAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAARCQkYOh8fb0ooKK80HByiCQUFTAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAIhERFmA2Np2ITUz3lVNT/4dLS/5IKCi9AAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAANjODiBllhY+61kZP+vY2P/pV5e/3xHRvEhEhJfAAAAAgAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAASSgoN41VVeS6bW3/xW9w/8dwcf+9bG3/klZW/jogIIEAAAAGAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ1RkWcs2xs/8dxcv/HcHH/x3Bx/8Zwcf+iYWH/SSkpmAAA
AAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUC0tMZtgX+fGcnP/x3Bx/8dwcf/HcHH/x3Fy/61q
av9UMTGqAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxRER1tm9v/8hxcv/HcHH/x3Bx/8dw
cf/HcnP/tnRz/185OboAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAACIxXV7TEdHT/yHJz/8l1
dv/Kd3j/ynd4/8p4eP/Bf37/bURDywAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNKysjo2Zm4Mt4
ef/NfH3/z4GC/9GFhf/RhYb/0YWF/82Mi/9+UVHeCAICOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAJAAAACwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAGc+
Pkm1c3P30IGC/9OJiv/XkZL/2ZaW/9mWl//YlJX/2JmY/5hnZfMeEBBrAAAABwAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA0FAgItHhAQWzAbG4IqFxeHDQcHWwAAABkAAAAAAAAAAAAA
AAAAAAAAek1MdMN/f//VjI3/2piZ/9+io//hqKn/4qmp/+Clpf/jpqT/wImH/04xMLwAAAA6AAAABQAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABEbDg5GRygokW5CQs+MVlbxnGJh/JdfXvxnPz7hHA8PbgAA
AAwAAAAAAAAAAAAAAACMW1qbz4qK/9qXl//gpqb/5rKz/+q6u//rvLz/6La2/+qxr//epKL/j1lZ+DUc
HLACAQFPAAAAHQAAAA8AAAAPAAAAEwAAACIbDg5MVDExnYZUU+SpbWz+uXl4/7x+fP/AgoD/xoeF/72A
f/9fOzu1AAAAHAAAAAAAAAAAAAAABJhkZK/VkZH/3Z+g/+axsf/twMD/8svL//LNzf/vxcX/8Lq4/+6z
sf+1dHP/j1VU+144N9g7IiKqMhwclDcfH5RGKSmiYTw7v4tZWOiydXT+woOC/8aKiP/Ol5X/2aWj/9ui
of/cnpz/2pyb/35TUrgAAAAVAAAAAAAAAAAAAAAFmmVkstaTk//hpaX/7Lm6//TLy//419f/+NnZ//TP
z//1wb//9Lq3/8aGhP+1dHP/s3Rz/6xwb/+pb27+rnNy/7Z7ev/BhIL/yY2L/8+WlP/apqT/5be2/+vB
v//rvrz/6bKw/+uvrf/Um5n/bUVEgAAAAAMAAAAAAAAAAAAAAAOTXV2q1ZGR/9CYmP+dfX7/o4yM/9e8
vP/z0tL/zLOz/+u8u//5v7z/1peV/8uLif/Ki4r/yoyL/86Ukv/TnJv/2qSi/+Gtq//nuLb/7cPB//DJ
x//xxsT/8b+9//G6t//zubf/77az/6d1dM89Hx8lAAAAAAAAAAAAAAAAAAAAAIJOTojNiIn/jGlp/01O
Tv9UVlb/dnNz/7uhof+Pfn7/xJ+e//zCv//lqKb/3J2b/+Chnv/hpaT/7Ly5/+vHxv/MxMn/0MjN//LK
yf/1x8X/9sLA//a/vP/3vrv/+L+8//S7uP+5hoXhYTo5RwAAAAAAAAAAAAAAAAAAAAAAAAAAaTs7RrVz
dPKmfn7/cXJx/4SGhv97fX3/b2Zm/516ev+7kJD/+sG+//C2s//lqqr/rpbA/3aB2/+ql83/tMHK/2jc
9P9OzOz/2r3B//q/vP/3vrv/9ry6//a8uf/ss7D/tYGA32c+Pk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAvEhIHg01Njbp9fvrCn5//nI+P/4R7ev+fgID/2Jyd/9ybnP/ytrT/+b+8/+ewtf+Mld3/ZI36/5eI
zv/Ttrn/sNLc/6/Czv/stLT/8re0/++0sf/tsq//2qCe/6Rxb8phODg+AAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAABCIB8MeUZGbqRpata8gYH8x4mJ/9eTk//YkpP/04qL/+Cbmv/5wL3/9726/+Sw
t//Zrrn/56qY/+2smf/lr6n/nLWJ/4Gtdf/Pppn/3qGf/7yEg/KJWViYTyoqIAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQh0dGXJAQGOXXl7NtnR1/8V7fP/MfH3/znt8/+il
o//0urj/7LCu/+Whg//rq13/35VX/9Kek/9yvXz/ZbNv/6iCdfqYY2O/aj4+TCUJCgcAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAACcamsBjFRVB4FERAh9PT0JjU1ND6VnZx+/hINF0JqZiNOjoty0iIf2hFBQw5lX
V8+wY2P4xXR0/+aioP/oq6j/2pqT/92fif/Vlor/yYqJ/7N8efiVZmPGdERFYkEfHxIAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAALiFhgXFkJEdx5CQSMqSknbNlZWbz5uaws2cnOXBlJPnqH18r4dc
XFFULy8OSCUlFm07O0+FSUmeoV1d3sF9fPrGhoX/snZ295xkZNiFUlKbbD4+T0UdHxIAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc0JDA5FgYRKdbm46onR0Zp9ycnuWampzhFlZVmY6
OikvDAwHAAAAAAAAAAAAAAAAAAAAAB0ODgRULCwhbjo7UXhERGVrPDxHTCYmGxAAAQMAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAgAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAP//////////////////////D////gf///wH///4A///+AP///AD///wA///8AP//+AD
///gA//D4AH+AeAA+ADgAAAAwAAAAMAAAADAAAAB4AAAA+AAAAfgAAAP8AAAH/wAAD8AAAD/AAAD/wB4
D//H////////////////////KAAAABgAAAAwAAAAAQAgAAAAAABgCQAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAABMAAAAtAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAgIO1cwMM1qOjrsHhAQmwAA
ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAATCgogfUhI6ahgYP6lXV3+f0hI9wIBAT0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGBgFPLy6kuW1t/sZv
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
</value>
</data>
</root>

View File

@ -261,6 +261,9 @@
<Compile Include="Computers\SinclairSpectrum\Hardware\Abstraction\IFDDHost.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\SoundOuput\AY38912.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\SoundOuput\Beeper.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\CPUMonitor.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ULA.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128KPlus2a\ZX128Plus2a.Screen.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Disk\FloppyDisk.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Abstraction\IJoystick.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Abstraction\IPortIODevice.cs" />
@ -1434,6 +1437,8 @@
<ItemGroup>
<Compile Include="Computers\SinclairSpectrum\Hardware\Input\StandardKeyboard.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum48K\ZX48.Port.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum48K\ZX48.Screen.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128K\ZX128.Screen.cs" />
<None Include="Computers\SinclairSpectrum\readme.md" />
<Compile Include="Computers\SinclairSpectrum\Machine\SpectrumBase.Media.cs" />
<None Include="Consoles\Atari\docs\stella.pdf" />

View File

@ -18,15 +18,19 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
// variables for executing instructions
public int instr_pntr = 0;
public int bus_pntr = 0;
public int mem_pntr = 0;
public ushort instr_swap;
public ushort[] cur_instr;
public int opcode;
public ushort[] BUSRQ;
public ushort[] MEMRQ;
public byte opcode;
public bool NO_prefix, CB_prefix, IX_prefix, EXTD_prefix, IY_prefix, IXCB_prefix, IYCB_prefix;
public bool IXCB_prefetch, IYCB_prefetch; // value is fetched before opcode
public bool halted;
public ushort PF;
public void FetchInstruction(byte opcode)
public void FetchInstruction()
{
if (NO_prefix)
{
@ -102,7 +106,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
case 0x43: REG_OP(TR, B, E); break; // LD B, E
case 0x44: REG_OP(TR, B, H); break; // LD B, H
case 0x45: REG_OP(TR, B, L); break; // LD B, L
case 0x46: REG_OP_IND(TR, B, L, H); break; // LD B, (HL)
case 0x46: REG_OP_IND_HL(TR, B); break; // LD B, (HL)
case 0x47: REG_OP(TR, B, A); break; // LD B, A
case 0x48: REG_OP(TR, C, B); break; // LD C, B
case 0x49: REG_OP(TR, C, C); break; // LD C, C
@ -110,7 +114,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
case 0x4B: REG_OP(TR, C, E); break; // LD C, E
case 0x4C: REG_OP(TR, C, H); break; // LD C, H
case 0x4D: REG_OP(TR, C, L); break; // LD C, L
case 0x4E: REG_OP_IND(TR, C, L, H); break; // LD C, (HL)
case 0x4E: REG_OP_IND_HL(TR, C); break; // LD C, (HL)
case 0x4F: REG_OP(TR, C, A); break; // LD C, A
case 0x50: REG_OP(TR, D, B); break; // LD D, B
case 0x51: REG_OP(TR, D, C); break; // LD D, C
@ -118,7 +122,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
case 0x53: REG_OP(TR, D, E); break; // LD D, E
case 0x54: REG_OP(TR, D, H); break; // LD D, H
case 0x55: REG_OP(TR, D, L); break; // LD D, L
case 0x56: REG_OP_IND(TR, D, L, H); break; // LD D, (HL)
case 0x56: REG_OP_IND_HL(TR, D); break; // LD D, (HL)
case 0x57: REG_OP(TR, D, A); break; // LD D, A
case 0x58: REG_OP(TR, E, B); break; // LD E, B
case 0x59: REG_OP(TR, E, C); break; // LD E, C
@ -126,7 +130,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
case 0x5B: REG_OP(TR, E, E); break; // LD E, E
case 0x5C: REG_OP(TR, E, H); break; // LD E, H
case 0x5D: REG_OP(TR, E, L); break; // LD E, L
case 0x5E: REG_OP_IND(TR, E, L, H); break; // LD E, (HL)
case 0x5E: REG_OP_IND_HL(TR, E); break; // LD E, (HL)
case 0x5F: REG_OP(TR, E, A); break; // LD E, A
case 0x60: REG_OP(TR, H, B); break; // LD H, B
case 0x61: REG_OP(TR, H, C); break; // LD H, C
@ -134,7 +138,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
case 0x63: REG_OP(TR, H, E); break; // LD H, E
case 0x64: REG_OP(TR, H, H); break; // LD H, H
case 0x65: REG_OP(TR, H, L); break; // LD H, L
case 0x66: REG_OP_IND(TR, H, L, H); break; // LD H, (HL)
case 0x66: REG_OP_IND_HL(TR, H); break; // LD H, (HL)
case 0x67: REG_OP(TR, H, A); break; // LD H, A
case 0x68: REG_OP(TR, L, B); break; // LD L, B
case 0x69: REG_OP(TR, L, C); break; // LD L, C
@ -142,23 +146,23 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
case 0x6B: REG_OP(TR, L, E); break; // LD L, E
case 0x6C: REG_OP(TR, L, H); break; // LD L, H
case 0x6D: REG_OP(TR, L, L); break; // LD L, L
case 0x6E: REG_OP_IND(TR, L, L, H); break; // LD L, (HL)
case 0x6E: REG_OP_IND_HL(TR, L); break; // LD L, (HL)
case 0x6F: REG_OP(TR, L, A); break; // LD L, A
case 0x70: LD_8_IND(L, H, B); break; // LD (HL), B
case 0x71: LD_8_IND(L, H, C); break; // LD (HL), C
case 0x72: LD_8_IND(L, H, D); break; // LD (HL), D
case 0x73: LD_8_IND(L, H, E); break; // LD (HL), E
case 0x74: LD_8_IND(L, H, H); break; // LD (HL), H
case 0x75: LD_8_IND(L, H, L); break; // LD (HL), L
case 0x70: LD_8_IND_HL(B); break; // LD (HL), B
case 0x71: LD_8_IND_HL(C); break; // LD (HL), C
case 0x72: LD_8_IND_HL(D); break; // LD (HL), D
case 0x73: LD_8_IND_HL(E); break; // LD (HL), E
case 0x74: LD_8_IND_HL(H); break; // LD (HL), H
case 0x75: LD_8_IND_HL(L); break; // LD (HL), L
case 0x76: HALT_(); break; // HALT
case 0x77: LD_8_IND(L, H, A); break; // LD (HL), A
case 0x77: LD_8_IND_HL( A); break; // LD (HL), A
case 0x78: REG_OP(TR, A, B); break; // LD A, B
case 0x79: REG_OP(TR, A, C); break; // LD A, C
case 0x7A: REG_OP(TR, A, D); break; // LD A, D
case 0x7B: REG_OP(TR, A, E); break; // LD A, E
case 0x7C: REG_OP(TR, A, H); break; // LD A, H
case 0x7D: REG_OP(TR, A, L); break; // LD A, L
case 0x7E: REG_OP_IND(TR, A, L, H); break; // LD A, (HL)
case 0x7E: REG_OP_IND_HL(TR, A); break; // LD A, (HL)
case 0x7F: REG_OP(TR, A, A); break; // LD A, A
case 0x80: REG_OP(ADD8, A, B); break; // ADD A, B
case 0x81: REG_OP(ADD8, A, C); break; // ADD A, C
@ -856,7 +860,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
case 0xC8: RET_COND(FlagZ); break; // RET Z
case 0xC9: RET_(); break; // RET
case 0xCA: JP_COND(FlagZ); break; // JP Z
case 0xCB: PREFIX_(IXCBpre); break; // PREFIX IXCB
case 0xCB: PREFETCH_(IXCBpre); break; // PREFIX IXCB
case 0xCC: CALL_COND(FlagZ); break; // CALL Z
case 0xCD: CALL_COND(true); break; // CALL
case 0xCE: REG_OP_IND_INC(ADC8, A, PCl, PCh); break; // ADC A, n
@ -1121,7 +1125,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
case 0xC8: RET_COND(FlagZ); break; // RET Z
case 0xC9: RET_(); break; // RET
case 0xCA: JP_COND(FlagZ); break; // JP Z
case 0xCB: PREFIX_(IYCBpre); break; // PREFIy IyCB
case 0xCB: PREFETCH_(IYCBpre); break; // PREFIX IyCB
case 0xCC: CALL_COND(FlagZ); break; // CALL Z
case 0xCD: CALL_COND(true); break; // CALL
case 0xCE: REG_OP_IND_INC(ADC8, A, PCl, PCh); break; // ADC A, n
@ -1181,24 +1185,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
// the first byte fetched is the prefetch value to use with the instruction
// we pick Ix or Iy here, the indexed value is stored in WZ
// In this way, we don't need to pass them as an argument to the I_Funcs.
if (IXCB_prefetch)
{
IXCB_prefetch = false;
PF = opcode;
Regs[ALU] = PF;
PREFETCH_(Ixl, Ixh);
return;
}
if (IYCB_prefetch)
{
IYCB_prefetch = false;
PF = opcode;
Regs[ALU] = PF;
PREFETCH_(Iyl, Iyh);
return;
}
IXCB_prefix = false;
IYCB_prefix = false;
NO_prefix = true;

View File

@ -33,17 +33,20 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
private void NMI_()
{
cur_instr = new ushort[]
{IDLE,
DEC16, SPl, SPh,
WR, SPl, SPh, PCh,
IDLE,
DEC16, SPl, SPh,
WR, SPl, SPh, PCl,
IDLE,
ASGN, PCl, 0x66,
ASGN, PCh, 0,
IDLE,
{DEC16, SPl, SPh,
TR, ALU, PCl,
WAIT,
WR_DEC, SPl, SPh, PCh,
TR16, PCl, PCh, NMI_V, ZERO,
WAIT,
WR, SPl, SPh, ALU,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 };
}
// Mode 0 interrupts only take effect if a CALL or RST is on the data bus
@ -55,56 +58,65 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD_INC, ALU, PCl, PCh,
IDLE,
RD, ALU, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 };
}
// Just jump to $0038
private void INTERRUPT_1()
{
cur_instr = new ushort[]
{DEC16, SPl, SPh,
IDLE,
WR, SPl, SPh, PCh,
IDLE,
{IDLE,
TR, ALU, PCl,
DEC16, SPl, SPh,
IDLE,
WR, SPl, SPh, PCl,
IDLE,
ASGN, PCl, 0x38,
IDLE,
ASGN, PCh, 0,
WAIT,
WR_DEC, SPl, SPh, PCh,
TR16, PCl, PCh, IRQ_V, ZERO,
WAIT,
WR, SPl, SPh, ALU,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 };
}
// Interrupt mode 2 uses the I vector combined with a byte on the data bus
private void INTERRUPT_2()
{
cur_instr = new ushort[]
{IDLE,
IDLE,
FTCH_DB,
TR, Z, DB,
TR, W, I,
{FTCH_DB,
IDLE,
DEC16, SPl, SPh,
WR, SPl, SPh, PCh,
TR16, Z, W, DB, I,
WAIT,
WR_DEC, SPl, SPh, PCh,
IDLE,
DEC16, SPl, SPh,
WR, SPl, SPh, PCl,
WAIT,
WR, SPl, SPh, PCl,
IDLE,
RD, PCl, Z, W,
INC16, Z, W,
WAIT,
RD_INC, PCl, Z, W,
IDLE,
WAIT,
RD, PCh, Z, W,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, W, 0 ,0 ,PCh, 0, 0, 0 };
MEMRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
}
private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60};

View File

@ -11,10 +11,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
Regs[DB] = Regs[dest];
}
public void I_Read_Func(ushort dest, ushort src_l, ushort src_h, ushort inc)
public void Read_INC_Func(ushort dest, ushort src_l, ushort src_h)
{
Regs[dest] = ReadMemory((ushort)((Regs[src_l] | (Regs[src_h] << 8)) + inc));
Regs[dest] = ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8));
Regs[DB] = Regs[dest];
INC16_Func(src_l, src_h);
}
public void Write_Func(ushort dest_l, ushort dest_h, ushort src)
@ -23,10 +24,18 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)Regs[src]);
}
public void I_Write_Func(ushort dest_l, ushort dest_h, ushort inc, ushort src)
public void Write_INC_Func(ushort dest_l, ushort dest_h, ushort src)
{
Regs[DB] = Regs[src];
WriteMemory((ushort)((Regs[dest_l] | (Regs[dest_h] << 8)) + inc), (byte)Regs[src]);
WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)Regs[src]);
INC16_Func(dest_l, dest_h);
}
public void Write_DEC_Func(ushort dest_l, ushort dest_h, ushort src)
{
Regs[DB] = Regs[src];
WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)Regs[src]);
DEC16_Func(dest_l, dest_h);
}
public void OUT_Func(ushort dest_l, ushort dest_h, ushort src)
@ -748,7 +757,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
FlagZ = Regs[A] == 0;
FlagS = Regs[A] > 127;
FlagP = iff2;
Flag5 = (Regs[A] & 0x02) != 0;
Flag5 = (Regs[A] & 0x20) != 0;
Flag3 = (Regs[A] & 0x08) != 0;
}
}

View File

@ -40,10 +40,25 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
public ushort H_s = 30;
public ushort L_s = 31;
public ushort DB = 32;
public ushort scratch = 33;
public ushort IRQ_V = 34; // IRQ mode 1 vector
public ushort NMI_V = 35; // NMI vector
public ushort[] Regs = new ushort[36];
public bool FlagI;
// IO Contention Constants. Need to distinguish port access and normal memory accesses for zx spectrum
public const ushort BIO1 = 100;
public const ushort BIO2 = 101;
public const ushort BIO3 = 102;
public const ushort BIO4 = 103;
public const ushort WIO1 = 105;
public const ushort WIO2 = 106;
public const ushort WIO3 = 107;
public const ushort WIO4 = 108;
public bool FlagI, FlagI1, FlagI2, FlagI3, FlagI4, FlagI5, FlagI6;
public bool FlagW; // wait flag, when set to true reads / writes will be delayed
@ -112,6 +127,17 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
Regs[i] = 0;
}
// These registers are set as part of the reset process
Regs[A] = 0xFF;
Regs[F] = 0xFF;
Regs[SPl] = 0xFF;
Regs[SPh] = 0xFF;
// the IRQ1 vector is 0x38
Regs[IRQ_V] = 0x38;
// The NMI vector is constant 0x66
Regs[NMI_V] = 0x66;
FlagI = false;
FlagW = false;
}

View File

@ -10,10 +10,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
private void NOP_()
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
OP };
{ IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
// NOTE: In a real Z80, this operation just flips a switch to choose between 2 registers
@ -22,18 +25,24 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
{
cur_instr = new ushort[]
{EXCH,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
private void EXX_()
{
cur_instr = new ushort[]
{EXX,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
// this exchanges 2 16 bit registers
@ -41,32 +50,41 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
{
cur_instr = new ushort[]
{EXCH_16, dest_l, dest_h, src_l, src_h,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
private void INC_16(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
{INC16, src_l, src_h,
IDLE,
IDLE,
INC16, src_l, src_h,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, I, PCh, 0, 0, 0};
MEMRQ = new ushort[] { 0, 0, PCh, 0, 0, 0 };
}
private void DEC_16(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
DEC16, src_l, src_h,
{DEC16, src_l, src_h,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, I, PCh, 0, 0, 0};
MEMRQ = new ushort[] { 0, 0, PCh, 0, 0, 0 };
}
// this is done in two steps technically, but the flags don't work out using existing funcitons
@ -74,26 +92,32 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
private void ADD_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
{TR16, Z, W, dest_l, dest_h,
IDLE,
IDLE,
TR16, Z, W, dest_l, dest_h,
INC16, Z, W,
IDLE,
IDLE,
ADD16, dest_l, dest_h, src_l, src_h,
IDLE,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, I, I, I, I, I, I, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 };
}
private void REG_OP(ushort operation, ushort dest, ushort src)
{
cur_instr = new ushort[]
{operation, dest, src,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
// Operations using the I and R registers take one T-cycle longer
@ -101,10 +125,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
{
cur_instr = new ushort[]
{operation, dest, src,
IDLE,
IDLE,
SET_FL_IR, dest,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, PCh, 0, 0, 0 };
}
// note: do not use DEC here since no flags are affected by this operation
@ -114,30 +141,36 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
{
cur_instr = new ushort[]
{IDLE,
ASGN, B, (ushort)((Regs[B] - 1) & 0xFF),
WAIT,
RD_INC, Z, PCl, PCh,
IDLE,
ASGN, B, (ushort)((Regs[B] - 1) & 0xFF),
IDLE,
RD, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
ASGN, W, 0,
IDLE,
ADDS, PCl, PCh, Z, W,
TR16, Z, W, PCl, PCh,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, PCh, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 };
}
else
{
cur_instr = new ushort[]
{IDLE,
ASGN, B, (ushort)((Regs[B] - 1) & 0xFF),
WAIT,
RD_INC, ALU, PCl, PCh,
IDLE,
RD, ALU, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, PCh, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, PCh, 0, 0, PCh, 0, 0, 0 };
}
}
@ -148,6 +181,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
IDLE,
IDLE,
HALT };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
private void JR_COND(bool cond)
@ -156,28 +192,34 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, PCl, PCh,
INC16, PCl, PCh,
IDLE,
WAIT,
RD_INC, Z, PCl, PCh,
IDLE,
ASGN, W, 0,
IDLE,
ADDS, PCl, PCh, Z, W,
TR16, Z, W, PCl, PCh,
IDLE,
ADDS, PCl, PCh, Z, W,
TR16, Z, W, PCl, PCh,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 };
}
else
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD_INC, ALU, PCl, PCh,
IDLE,
IDLE,
RD, ALU, PCl, PCh,
IDLE,
INC16, PCl, PCh,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 };
}
}
@ -187,29 +229,35 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD_INC, Z, PCl, PCh,
IDLE,
RD, Z, PCl, PCh,
INC16, PCl, PCh,
RD, W, PCl, PCh,
IDLE,
INC16, PCl, PCh,
WAIT,
RD_INC, W, PCl, PCh,
TR16, PCl, PCh, Z, W,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, 0 };
}
else
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD_INC, Z, PCl, PCh,
IDLE,
RD, Z, PCl, PCh,
INC16, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
INC16, PCl, PCh,
IDLE,
WAIT,
RD_INC, W, PCl, PCh,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 };
}
}
@ -217,45 +265,54 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD_INC, Z, SPl, SPh,
IDLE,
RD, Z, SPl, SPh,
INC16, SPl, SPh,
IDLE,
IDLE,
RD, W, SPl, SPh,
INC16, SPl, SPh,
TR16, PCl, PCh, Z, W,
WAIT,
RD_INC, W, SPl, SPh,
TR16, PCl, PCh, Z, W,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 };
MEMRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 };
}
private void RETI_()
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD_INC, Z, SPl, SPh,
IDLE,
RD, Z, SPl, SPh,
INC16, SPl, SPh,
IDLE,
IDLE,
RD, W, SPl, SPh,
INC16, SPl, SPh,
WAIT,
RD_INC, W, SPl, SPh,
TR16, PCl, PCh, Z, W,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 };
MEMRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 };
}
private void RETN_()
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, SPl, SPh,
INC16, SPl, SPh,
IDLE,
RD, W, SPl, SPh,
INC16, SPl, SPh,
WAIT,
RD_INC, Z, SPl, SPh,
EI_RETN,
WAIT,
RD_INC, W, SPl, SPh,
TR16, PCl, PCh, Z, W,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 };
MEMRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 };
}
@ -266,24 +323,30 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, SPl, SPh,
INC16, SPl, SPh,
WAIT,
RD_INC, Z, SPl, SPh,
IDLE,
IDLE,
RD, W, SPl, SPh,
INC16, SPl, SPh,
IDLE,
WAIT,
RD_INC, W, SPl, SPh,
TR16, PCl, PCh, Z, W,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 };
MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 };
}
else
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, PCh, 0, 0, 0 };
}
}
@ -293,36 +356,42 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD_INC, Z, PCl, PCh,
IDLE,
RD, Z, PCl, PCh,
INC16, PCl, PCh,
IDLE,
WAIT,
RD, W, PCl, PCh,
INC16, PCl, PCh,
IDLE,
DEC16, SPl, SPh,
WAIT,
WR_DEC, SPl, SPh, PCh,
IDLE,
WR, SPl, SPh, PCh,
DEC16, SPl, SPh,
WAIT,
WR, SPl, SPh, PCl,
IDLE,
TR, PCl, Z,
TR, PCh, W,
TR16, PCl, PCh, Z, W,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 };
}
else
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD_INC, Z, PCl, PCh,
IDLE,
RD, Z, PCl, PCh,
WAIT,
RD_INC, W, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
IDLE,
INC16, PCl, PCh,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 };
}
}
@ -330,34 +399,43 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
{
cur_instr = new ushort[]
{operation, src,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
private void BIT_OP(ushort operation, ushort bit, ushort src)
{
cur_instr = new ushort[]
{operation, bit, src,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
private void PUSH_(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
{DEC16, SPl, SPh,
IDLE,
DEC16, SPl, SPh,
IDLE,
WR, SPl, SPh, src_h,
IDLE,
DEC16, SPl, SPh,
WAIT,
WR_DEC, SPl, SPh, src_h,
IDLE,
WAIT,
WR, SPl, SPh, src_l,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 };
}
@ -365,76 +443,112 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
{
cur_instr = new ushort[]
{IDLE,
RD, src_l, SPl, SPh,
WAIT,
RD_INC, src_l, SPl, SPh,
IDLE,
INC16, SPl, SPh,
IDLE,
RD, src_h, SPl, SPh,
IDLE,
INC16, SPl, SPh,
WAIT,
RD_INC, src_h, SPl, SPh,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 };
}
private void RST_(ushort n)
{
cur_instr = new ushort[]
{IDLE,
{DEC16, SPl, SPh,
IDLE,
DEC16, SPl, SPh,
WR, SPl, SPh, PCh,
DEC16, SPl, SPh,
WR, SPl, SPh, PCl,
IDLE,
ASGN, Z, n,
ASGN, W, 0,
WAIT,
WR_DEC, SPl, SPh, PCh,
RST, n,
WAIT,
WR, SPl, SPh, PCl,
TR16, PCl, PCh, Z, W,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 };
MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 };
}
private void PREFIX_(ushort src)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
WAIT,
OP_F,
PREFIX, src};
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
private void PREFETCH_(ushort src_l, ushort src_h)
private void PREFETCH_(ushort src)
{
if (src == IXCBpre)
{
Regs[W] = Regs[Ixh];
Regs[Z] = Regs[Ixl];
}
else
{
Regs[W] = Regs[Iyh];
Regs[Z] = Regs[Iyl];
}
cur_instr = new ushort[]
{TR16, Z, W, src_l, src_h,
{IDLE,
WAIT,
RD_INC, ALU, PCl, PCh,
ADDS, Z, W, ALU, ZERO,
WAIT,
OP_F,
IDLE,
PREFIX, IXYprefetch };
PREFIX, src,};
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, PCh };
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0, 0 };
}
private void DI_()
{
cur_instr = new ushort[]
{DI,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
private void EI_()
{
cur_instr = new ushort[]
{EI,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
private void JP_16(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{TR, PCl, src_l,
IDLE,
TR, PCh, src_h,
{TR16, PCl, PCh, src_l, src_h,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { src_h, 0, 0, 0 };
MEMRQ = new ushort[] { src_h, 0, 0, 0 };
}
private void LD_SP_16(ushort src_l, ushort src_h)
@ -442,68 +556,83 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
cur_instr = new ushort[]
{IDLE,
IDLE,
TR, SPl, src_l,
TR, SPh, src_h,
IDLE,
TR16, SPl, SPh, src_l, src_h,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { I, I, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, 0, PCh, 0, 0, 0 };
}
private void OUT_()
{
cur_instr = new ushort[]
{IDLE,
RD, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
TR, W, A,
{TR, W, A,
WAIT,
RD_INC, Z, PCl, PCh,
IDLE,
WAIT,
WAIT,
OUT, Z, W, A,
IDLE,
INC16, Z, W,
IDLE,
IDLE,
INC16, Z, ALU,
WAIT,
OP_F,
OP};
BUSRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 };
}
private void OUT_REG_(ushort dest, ushort src)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
TR16, Z, W, C, B,
OUT, Z, W, src,
{TR16, Z, W, C, B,
IDLE,
INC16, Z, W,
IDLE,
OUT, Z, W, src,
INC16, Z, W,
WAIT,
OP_F,
OP};
BUSRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 };
}
private void IN_()
{
cur_instr = new ushort[]
{IDLE,
RD, Z, PCl, PCh,
{TR, W, A,
WAIT,
RD_INC, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
TR, W, A,
WAIT,
WAIT,
IN, A, Z, W,
IDLE,
INC16, Z, W,
IDLE,
IDLE,
WAIT,
OP_F,
OP};
BUSRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 };
}
private void IN_REG_(ushort dest, ushort src)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IN, dest, src, B,
IDLE,
TR16, Z, W, C, B,
{TR16, Z, W, C, B,
WAIT,
WAIT,
IN, dest, Z, W,
INC16, Z, W,
IDLE,
WAIT,
OP_F,
OP};
BUSRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 };
}
private void REG_OP_16_(ushort op, ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
@ -511,62 +640,74 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
TR16, Z, W, dest_l, dest_h,
INC16, Z, W,
IDLE,
IDLE,
op, dest_l, dest_h, src_l, src_h,
IDLE,
IDLE,
WAIT,
OP_F,
OP};
BUSRQ = new ushort[] { I, I, I, I, I, I, I, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 };
}
private void INT_MODE_(ushort src)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
INT_MODE, src,
{INT_MODE, src,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
private void RRD_()
{
cur_instr = new ushort[]
{IDLE,
IDLE,
TR16, Z, W, L, H,
IDLE,
{TR16, Z, W, L, H,
WAIT,
RD, ALU, Z, W,
IDLE,
RRD, ALU, A,
IDLE,
WR, Z, W, ALU,
IDLE,
INC16, Z, W,
IDLE,
IDLE,
WAIT,
WR_INC, Z, W, ALU,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { H, 0, 0, H, H, H, H, W, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
}
private void RLD_()
{
cur_instr = new ushort[]
{IDLE,
IDLE,
TR16, Z, W, L, H,
IDLE,
{TR16, Z, W, L, H,
WAIT,
RD, ALU, Z, W,
IDLE,
RLD, ALU, A,
IDLE,
WR, Z, W, ALU,
IDLE,
INC16, Z, W,
IDLE,
IDLE,
WAIT,
WR_INC, Z, W, ALU,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { H, 0, 0, H, H, H, H, W, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
}
}
}

View File

@ -6,32 +6,38 @@
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
WAIT,
RD, ALU, src_l, src_h,
IDLE,
operation, ALU,
IDLE,
WAIT,
WR, src_l, src_h, ALU,
IDLE,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { src_h, 0, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 };
}
private void BIT_OP_IND(ushort operation, ushort bit, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
WAIT,
RD, ALU, src_l, src_h,
IDLE,
operation, bit, ALU,
IDLE,
WAIT,
WR, src_l, src_h, ALU,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { src_h, 0, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 };
}
// Note that this operation uses I_BIT, same as indexed BIT.
@ -42,214 +48,272 @@
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
WAIT,
RD, ALU, src_l, src_h,
IDLE,
I_BIT, bit, ALU,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { src_h, 0, 0, src_h, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { src_h, 0, 0, 0, PCh, 0, 0, 0 };
}
private void REG_OP_IND_INC(ushort operation, ushort dest, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, ALU, src_l, src_h,
IDLE,
WAIT,
RD_INC, ALU, src_l, src_h,
operation, dest, ALU,
INC16, src_l, src_h,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 };
}
private void REG_OP_IND(ushort operation, ushort dest, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
TR16, Z, W, src_l, src_h,
RD, ALU, Z, W,
INC16, Z, W,
operation, dest, ALU,
{TR16, Z, W, src_l, src_h,
WAIT,
RD_INC, ALU, Z, W,
operation, dest, ALU,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 };
}
// different because HL doesn't effect WZ
private void REG_OP_IND_HL(ushort operation, ushort dest)
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD, ALU, L, H,
operation, dest, ALU,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 };
}
private void LD_16_IND_nn(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
RD, Z, PCl, PCh,
WAIT,
RD_INC, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
WAIT,
RD_INC, W, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
WR, Z, W, src_l,
IDLE,
INC16, Z, W,
WAIT,
WR_INC, Z, W, src_l,
IDLE,
WAIT,
WR, Z, W, src_h,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
}
private void LD_IND_16_nn(ushort dest_l, ushort dest_h)
{
cur_instr = new ushort[]
{IDLE,
RD, Z, PCl, PCh,
WAIT,
RD_INC, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
WAIT,
RD_INC, W, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
RD, dest_l, Z, W,
IDLE,
INC16, Z, W,
WAIT,
RD_INC, dest_l, Z, W,
IDLE,
WAIT,
RD, dest_h, Z, W,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
}
private void LD_8_IND_nn(ushort src)
{
cur_instr = new ushort[]
{IDLE,
RD, Z, PCl, PCh,
WAIT,
RD_INC, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
WAIT,
RD_INC, W, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
WR, Z, W, src,
INC16, Z, W,
WAIT,
WR_INC, Z, W, src,
TR, W, A,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
}
private void LD_IND_8_nn(ushort dest)
{
cur_instr = new ushort[]
{IDLE,
RD, Z, PCl, PCh,
WAIT,
RD_INC, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
WAIT,
RD_INC, W, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
WAIT,
RD_INC, dest, Z, W,
IDLE,
INC16, PCl, PCh,
IDLE,
RD, dest, Z, W,
IDLE,
INC16, Z, W,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
}
private void LD_8_IND(ushort dest_l, ushort dest_h, ushort src)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
TR16, Z, W, dest_l, dest_h,
WR, Z, W, src,
INC16, Z, W,
TR, W, A,
{TR16, Z, W, dest_l, dest_h,
WAIT,
WR_INC, Z, W, src,
TR, W, A,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { dest_h, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { dest_h, 0, 0, PCh, 0, 0, 0 };
}
// seperate HL needed since it doesn't effect the WZ pair
private void LD_8_IND_HL(ushort src)
{
cur_instr = new ushort[]
{IDLE,
WAIT,
WR, L, H, src,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 };
}
private void LD_8_IND_IND(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD_INC, ALU, src_l, src_h,
IDLE,
IDLE,
RD, ALU, src_l, src_h,
IDLE,
INC16, src_l, src_h,
IDLE,
WAIT,
WR, dest_l, dest_h, ALU,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { src_h, 0, 0, dest_h, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { src_h, 0, 0, dest_h, 0, 0, PCh, 0, 0, 0 };
}
private void LD_IND_8_INC(ushort dest, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD_INC, dest, src_l, src_h,
IDLE,
IDLE,
RD, dest, src_l, src_h,
IDLE,
INC16, src_l, src_h,
WAIT,
OP_F,
OP };
}
private void LD_IND_8_DEC(ushort dest, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
RD, dest, src_l, src_h,
IDLE,
DEC16, src_l, src_h,
IDLE,
OP };
BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 };
}
private void LD_IND_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD_INC, dest_l, src_l, src_h,
IDLE,
WAIT,
RD_INC, dest_h, src_l, src_h,
IDLE,
RD, dest_l, src_l, src_h,
IDLE,
INC16, src_l, src_h,
RD, dest_h, src_l, src_h,
IDLE,
INC16, src_l, src_h,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 };
}
private void INC_8_IND(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
WAIT,
RD, ALU, src_l, src_h,
IDLE,
INC8, ALU,
IDLE,
WAIT,
WR, src_l, src_h, ALU,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { src_h, 0, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 };
}
private void DEC_8_IND(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
WAIT,
RD, ALU, src_l, src_h,
IDLE,
DEC8, ALU,
IDLE,
WAIT,
WR, src_l, src_h, ALU,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { src_h, 0, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 };
}
// NOTE: WZ implied for the wollowing 3 functions
@ -257,197 +321,214 @@
{
cur_instr = new ushort[]
{IDLE,
IDLE,
WAIT,
RD, ALU, Z, W,
IDLE,
operation, ALU,
IDLE,
WAIT,
WR, Z, W, ALU,
IDLE,
TR, dest, ALU,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { W, 0, 0, W, W, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { W, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
}
private void I_BIT_OP(ushort operation, ushort bit, ushort dest)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
WAIT,
RD, ALU, Z, W,
IDLE,
operation, bit, ALU,
IDLE,
WAIT,
WR, Z, W, ALU,
IDLE,
TR, dest, ALU,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { W, 0, 0, W, W, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { W, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
}
private void I_BIT_TE(ushort bit)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
WAIT,
RD, ALU, Z, W,
IDLE,
I_BIT, bit, ALU,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { W, 0, 0, W, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { W, 0, 0, 0, PCh, 0, 0, 0 };
}
private void I_OP_n(ushort operation, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
WAIT,
RD, ALU, PCl, PCh,
INC16, PCl, PCh,
IDLE,
IDLE,
TR16, Z, W, src_l, src_h,
ADDS, Z, W, ALU, ZERO,
IDLE,
ADDS, Z, W, ALU, ZERO,
IDLE,
INC16, PCl, PCh,
WAIT,
RD, ALU, Z, W,
IDLE,
IDLE,
operation, ALU,
IDLE,
IDLE,
IDLE,
WAIT,
WR, Z, W, ALU,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, W, 0, 0, W, W, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, W, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
}
private void I_OP_n_n(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, ALU, PCl, PCh,
INC16, PCl, PCh,
IDLE,
TR16, Z, W, src_l, src_h,
IDLE,
{TR16, Z, W, src_l, src_h,
WAIT,
RD_INC, ALU, PCl, PCh,
ADDS, Z, W, ALU, ZERO,
IDLE,
WAIT,
RD, ALU, PCl, PCh,
INC16, PCl, PCh,
IDLE,
IDLE,
INC16, PCl, PCh,
WAIT,
WR, Z, W, ALU,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, PCh, W, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
}
private void I_REG_OP_IND_n(ushort operation, ushort dest, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
WAIT,
RD, ALU, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
TR16, Z, W, src_l, src_h,
IDLE,
ADDS, Z, W, ALU, ZERO,
IDLE,
INC16, PCl, PCh,
WAIT,
RD, ALU, Z, W,
IDLE,
operation, dest, ALU,
IDLE,
operation, dest, ALU,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, W, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
}
private void I_LD_8_IND_n(ushort dest_l, ushort dest_h, ushort src)
{
cur_instr = new ushort[]
{IDLE,
WAIT,
RD, ALU, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
TR16, Z, W, dest_l, dest_h,
IDLE,
ADDS, Z, W, ALU, ZERO,
IDLE,
WR, Z, W, src,
IDLE,
IDLE,
IDLE,
INC16, PCl, PCh,
WAIT,
WR, Z, W, src,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, Z, 0, 0, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, Z, 0, 0, PCh, 0, 0, 0 };
}
private void LD_OP_R(ushort operation, ushort repeat_instr)
{
cur_instr = new ushort[]
{RD, ALU, L, H,
IDLE,
WR, E, D, ALU,
IDLE,
operation, L, H,
IDLE,
operation, E, D,
IDLE,
DEC16, C, B,
SET_FL_LD,
IDLE,
OP_R, 0, operation, repeat_instr };
{IDLE,
WAIT,
RD, ALU, L, H,
operation, L, H,
WAIT,
WR, E, D, ALU,
IDLE,
SET_FL_LD_R, 0, operation, repeat_instr};
BUSRQ = new ushort[] { H, 0, 0, D, 0, 0, D, D };
MEMRQ = new ushort[] { H, 0, 0, D, 0, 0, 0, 0 };
}
private void CP_OP_R(ushort operation, ushort repeat_instr)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
{IDLE,
WAIT,
RD, ALU, L, H,
operation, L, H,
IDLE,
IDLE,
DEC16, C, B,
SET_FL_CP,
IDLE,
operation, Z, W,
IDLE,
OP_R, 1, operation, repeat_instr };
SET_FL_CP_R, 1, operation, repeat_instr};
BUSRQ = new ushort[] { H, 0, 0, H, H, H, H, H };
MEMRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, 0 };
}
private void IN_OP_R(ushort operation, ushort repeat_instr)
{
cur_instr = new ushort[]
{IN, ALU, C, B,
{IDLE,
IDLE,
WR, L, H, ALU,
WAIT,
WAIT,
IN, ALU, C, B,
IDLE,
operation, L, H,
IDLE,
TR16, Z, W, C, B,
operation, Z, W,
IDLE,
DEC8, B,
IDLE,
OP_R, 2, operation, repeat_instr };
WAIT,
REP_OP_I, L, H, ALU, operation, 2, operation, repeat_instr };
BUSRQ = new ushort[] { I, BIO1, BIO2, BIO3, BIO4, H, 0, 0};
MEMRQ = new ushort[] { 0, BIO1, BIO2, BIO3, BIO4, H, 0, 0 };
}
private void OUT_OP_R(ushort operation, ushort repeat_instr)
{
cur_instr = new ushort[]
{RD, ALU, L, H,
{IDLE,
IDLE,
OUT, C, B, ALU,
WAIT,
RD, ALU, L, H,
IDLE,
IDLE,
operation, L, H,
DEC8, B,
IDLE,
TR16, Z, W, C, B,
operation, Z, W,
IDLE,
OP_R, 3, operation, repeat_instr };
WAIT,
WAIT,
REP_OP_O, C, B, ALU, operation, 3, operation, repeat_instr };
BUSRQ = new ushort[] { I, H, 0, 0, BIO1, BIO2, BIO3, BIO4 };
MEMRQ = new ushort[] { 0, H, 0, 0, BIO1, BIO2, BIO3, BIO4 };
}
// this is an indirect change of a a 16 bit register with memory
@ -455,24 +536,27 @@
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, dest_l, dest_h,
WAIT,
RD_INC, Z, dest_l, dest_h,
IDLE,
IDLE,
I_RD, W, dest_l, dest_h, 1,
WAIT,
RD, W, dest_l, dest_h,
IDLE,
IDLE,
WR, dest_l, dest_h, src_l,
IDLE,
IDLE,
I_WR, dest_l, dest_h, 1, src_h,
WAIT,
WR_DEC, dest_l, dest_h, src_h,
IDLE,
WAIT,
WR, dest_l, dest_h, src_l,
IDLE,
IDLE,
TR16, src_l, src_h, Z, W,
IDLE,
IDLE,
IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { dest_h, 0, 0, dest_h, 0, 0, dest_h, dest_h, 0, 0, dest_h, 0, 0, dest_h, dest_h, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { dest_h, 0, 0, dest_h, 0, 0, 0, dest_h, 0, 0, dest_h, 0, 0, 0, 0, PCh, 0, 0, 0 };
}
}
}

View File

@ -14,69 +14,75 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
// operations that can take place in an instruction
public const ushort IDLE = 0;
public const ushort OP = 1;
public const ushort OP_R = 2; // used for repeating operations
public const ushort OP_F = 2; // used for repeating operations
public const ushort HALT = 3;
public const ushort RD = 4;
public const ushort WR = 5;
public const ushort I_RD = 6;
public const ushort I_WR = 7;
public const ushort TR = 8;
public const ushort TR16 = 9;
public const ushort ADD16 = 10;
public const ushort ADD8 = 11;
public const ushort SUB8 = 12;
public const ushort ADC8 = 13;
public const ushort SBC8 = 14;
public const ushort SBC16 = 15;
public const ushort ADC16 = 16;
public const ushort INC16 = 17;
public const ushort INC8 = 18;
public const ushort DEC16 = 19;
public const ushort DEC8 = 20;
public const ushort RLC = 21;
public const ushort RL = 22;
public const ushort RRC = 23;
public const ushort RR = 24;
public const ushort CPL = 25;
public const ushort DA = 26;
public const ushort SCF = 27;
public const ushort CCF = 28;
public const ushort AND8 = 29;
public const ushort XOR8 = 30;
public const ushort OR8 = 31;
public const ushort CP8 = 32;
public const ushort SLA = 33;
public const ushort SRA = 34;
public const ushort SRL = 35;
public const ushort SLL = 36;
public const ushort BIT = 37;
public const ushort RES = 38;
public const ushort SET = 39;
public const ushort EI = 40;
public const ushort DI = 41;
public const ushort EXCH = 42;
public const ushort EXX = 43;
public const ushort EXCH_16 = 44;
public const ushort PREFIX = 45;
public const ushort PREFETCH = 46;
public const ushort ASGN = 47;
public const ushort ADDS = 48; // signed 16 bit operation used in 2 instructions
public const ushort INT_MODE = 49;
public const ushort EI_RETN = 50;
public const ushort EI_RETI = 51; // reti has no delay in interrupt enable
public const ushort OUT = 52;
public const ushort IN = 53;
public const ushort NEG = 54;
public const ushort RRD = 55;
public const ushort RLD = 56;
public const ushort SET_FL_LD = 57;
public const ushort SET_FL_CP = 58;
public const ushort SET_FL_IR = 59;
public const ushort I_BIT = 60;
public const ushort HL_BIT = 61;
public const ushort FTCH_DB = 62;
public const ushort WAIT = 63; // enterred when readin or writing and FlagW is true
public const ushort RD_INC = 6; // read and increment
public const ushort WR_INC = 7; // write and increment
public const ushort WR_DEC = 8; // write and increment (for stack pointer)
public const ushort TR = 9;
public const ushort TR16 = 10;
public const ushort ADD16 = 11;
public const ushort ADD8 = 12;
public const ushort SUB8 = 13;
public const ushort ADC8 = 14;
public const ushort SBC8 = 15;
public const ushort SBC16 = 16;
public const ushort ADC16 = 17;
public const ushort INC16 = 18;
public const ushort INC8 = 19;
public const ushort DEC16 = 20;
public const ushort DEC8 = 21;
public const ushort RLC = 22;
public const ushort RL = 23;
public const ushort RRC = 24;
public const ushort RR = 25;
public const ushort CPL = 26;
public const ushort DA = 27;
public const ushort SCF = 28;
public const ushort CCF = 29;
public const ushort AND8 = 30;
public const ushort XOR8 = 31;
public const ushort OR8 = 32;
public const ushort CP8 = 33;
public const ushort SLA = 34;
public const ushort SRA = 35;
public const ushort SRL = 36;
public const ushort SLL = 37;
public const ushort BIT = 38;
public const ushort RES = 39;
public const ushort SET = 40;
public const ushort EI = 41;
public const ushort DI = 42;
public const ushort EXCH = 43;
public const ushort EXX = 44;
public const ushort EXCH_16 = 45;
public const ushort PREFIX = 46;
public const ushort PREFETCH = 47;
public const ushort ASGN = 48;
public const ushort ADDS = 49; // signed 16 bit operation used in 2 instructions
public const ushort INT_MODE = 50;
public const ushort EI_RETN = 51;
public const ushort EI_RETI = 52; // reti has no delay in interrupt enable
public const ushort OUT = 53;
public const ushort IN = 54;
public const ushort NEG = 55;
public const ushort RRD = 56;
public const ushort RLD = 57;
public const ushort SET_FL_LD_R = 58;
public const ushort SET_FL_CP_R = 59;
public const ushort SET_FL_IR = 60;
public const ushort I_BIT = 61;
public const ushort HL_BIT = 62;
public const ushort FTCH_DB = 63;
public const ushort WAIT = 64; // enterred when readin or writing and FlagW is true
public const ushort RST = 65;
public const ushort REP_OP_I = 66;
public const ushort REP_OP_O = 67;
// non-state variables
public ushort Ztemp1, Ztemp2, Ztemp3, Ztemp4;
public byte temp_R;
public Z80A()
@ -90,8 +96,15 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
ResetRegisters();
ResetInterrupts();
TotalExecutedCycles = 0;
cur_instr = new ushort[] { OP };
instr_pntr = 0;
cur_instr = new ushort[]
{ IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
instr_pntr = 0; bus_pntr = 0; mem_pntr = 0;
NO_prefix = true;
}
@ -147,6 +160,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
// Execute instructions
public void ExecuteOne()
{
//FlagI6 = FlagI5;
FlagI5 = FlagI4;
FlagI4 = FlagI3;
FlagI3 = FlagI2;
FlagI2 = FlagI1;
FlagI1 = FlagI;
bus_pntr++; mem_pntr++;
switch (cur_instr[instr_pntr++])
{
case IDLE:
@ -174,9 +195,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
iff1 = false;
NMI_();
NMICallback();
instr_pntr = 0;
instr_pntr = 0; bus_pntr = 0; mem_pntr = 0;
}
else if (iff1 && FlagI)
else if (iff1 && FlagI5)
{
iff1 = iff2 = false;
EI_pending = 0;
@ -201,23 +222,15 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
break;
}
IRQCallback();
instr_pntr = 0;
instr_pntr = 0; bus_pntr = 0; mem_pntr = 0;
}
else
{
if(!FlagW)
{
if (OnExecFetch != null) OnExecFetch(RegPC);
if (TraceCallback != null) TraceCallback(State());
FetchInstruction(FetchMemory(RegPC++));
instr_pntr = 0;
}
else
{
instr_pntr--;
instr_swap = OP;
cur_instr[instr_pntr] = WAIT;
}
if (OnExecFetch != null) OnExecFetch(RegPC);
if (TraceCallback != null) TraceCallback(State());
RegPC++;
FetchInstruction();
instr_pntr = 0; bus_pntr = 0; mem_pntr = 0;
}
temp_R = (byte)(Regs[R] & 0x7F);
@ -225,143 +238,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
temp_R &= 0x7F;
Regs[R] = (byte)((Regs[R] & 0x80) | temp_R);
break;
case OP_R:
// determine if we repeat based on what operation we are doing
// single execution versions also come here, but never repeat
ushort temp1 = cur_instr[instr_pntr++];
ushort temp2 = cur_instr[instr_pntr++];
ushort temp3 = cur_instr[instr_pntr++];
bool repeat = false;
int Reg16_d = Regs[C] | (Regs[B] << 8);
switch (temp1)
{
case 0:
repeat = Reg16_d != 0;
break;
case 1:
repeat = (Reg16_d != 0) && !FlagZ;
break;
case 2:
repeat = Regs[B] != 0;
break;
case 3:
repeat = Regs[B] != 0;
break;
}
// if we repeat, we do a 5 cycle refresh which decrements PC by 2
// if we don't repeat, continue on as a normal opcode fetch
if (repeat && temp3 > 0)
{
cur_instr = new ushort[]
{IDLE,
DEC16, PCl, PCh,
IDLE,
DEC16, PCl, PCh,
OP };
instr_pntr = 0;
// adjust WZ register accordingly
switch (temp1)
{
case 0:
// TEST: PC before or after the instruction?
Regs[Z] = Regs[PCl];
Regs[W] = Regs[PCh];
INC16_Func(Z, W);
break;
case 1:
// TEST: PC before or after the instruction?
Regs[Z] = Regs[PCl];
Regs[W] = Regs[PCh];
INC16_Func(Z, W);
break;
case 2:
// Nothing
break;
case 3:
// Nothing
break;
}
}
else
{
// Interrupts can occur at this point, so process them accordingly
// Read the opcode of the next instruction
if (EI_pending > 0)
{
EI_pending--;
if (EI_pending == 0) { IFF1 = IFF2 = true; }
}
// Process interrupt requests.
if (nonMaskableInterruptPending)
{
nonMaskableInterruptPending = false;
if (TraceCallback != null)
{
TraceCallback(new TraceInfo{Disassembly = "====NMI====", RegisterInfo = ""});
}
iff2 = iff1;
iff1 = false;
NMI_();
NMICallback();
instr_pntr = 0;
}
else if (iff1 && FlagI)
{
iff1 = iff2 = false;
EI_pending = 0;
if (TraceCallback != null)
{
TraceCallback(new TraceInfo{Disassembly = "====IRQ====", RegisterInfo = ""});
}
switch (interruptMode)
{
case 0:
// Requires something to be pushed onto the data bus
// we'll assume it's a zero for now
INTERRUPT_0(0);
break;
case 1:
INTERRUPT_1();
break;
case 2:
INTERRUPT_2();
break;
}
IRQCallback();
instr_pntr = 0;
}
else
{
if (!FlagW)
{
if (OnExecFetch != null) OnExecFetch(RegPC);
if (TraceCallback != null) TraceCallback(State());
FetchInstruction(FetchMemory(RegPC++));
instr_pntr = 0;
}
else
{
instr_pntr--;
instr_swap = OP;
cur_instr[instr_pntr] = WAIT;
}
}
temp_R = (byte)(Regs[R] & 0x7F);
temp_R++;
temp_R &= 0x7F;
Regs[R] = (byte)((Regs[R] & 0x80) | temp_R);
}
case OP_F:
opcode = FetchMemory(RegPC);
break;
case HALT:
halted = true;
// NOTE: Check how halt state effects the DB
@ -389,7 +268,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
NMICallback();
halted = false;
}
else if (iff1 && FlagI)
else if (iff1 && FlagI5)
{
iff1 = iff2 = false;
EI_pending = 0;
@ -416,69 +295,28 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
IRQCallback();
halted = false;
}
else
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
HALT };
}
temp_R = (byte)(Regs[R] & 0x7F);
temp_R++;
temp_R &= 0x7F;
Regs[R] = (byte)((Regs[R] & 0x80) | temp_R);
instr_pntr = 0;
instr_pntr = 0; bus_pntr = 0; mem_pntr = 0;
break;
case RD:
if (!FlagW)
{
Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
}
else
{
instr_pntr--;
instr_swap = RD;
cur_instr[instr_pntr] = WAIT;
}
Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case WR:
if (!FlagW)
{
Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
}
else
{
instr_pntr--;
instr_swap = WR;
cur_instr[instr_pntr] = WAIT;
}
Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case I_RD:
if (!FlagW)
{
I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
}
else
{
instr_pntr--;
instr_swap = I_RD;
cur_instr[instr_pntr] = WAIT;
}
case RD_INC:
Read_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case I_WR:
if (!FlagW)
{
I_Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
}
else
{
instr_pntr--;
instr_swap =I_WR;
cur_instr[instr_pntr] = WAIT;
}
case WR_INC:
Write_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case WR_DEC:
Write_DEC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case TR:
TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
@ -603,11 +441,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
if (prefix_src == EXTDpre) { EXTD_prefix = true; }
if (prefix_src == IXpre) { IX_prefix = true; }
if (prefix_src == IYpre) { IY_prefix = true; }
if (prefix_src == IXCBpre) { IXCB_prefix = true; IXCB_prefetch = true; }
if (prefix_src == IYCBpre) { IYCB_prefix = true; IYCB_prefetch = true; }
if (prefix_src == IXCBpre) { IXCB_prefix = true; }
if (prefix_src == IYCBpre) { IYCB_prefix = true; }
FetchInstruction(FetchMemory(RegPC++));
instr_pntr = 0;
RegPC++;
FetchInstruction();
instr_pntr = 0; bus_pntr = 0; mem_pntr = 0;
// only the first prefix in a double prefix increases R, although I don't know how / why
if (prefix_src < 4)
{
@ -632,28 +471,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
iff1 = iff2;
break;
case OUT:
if (!FlagW)
{
OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
}
else
{
instr_pntr--;
instr_swap = OUT;
cur_instr[instr_pntr] = WAIT;
}
OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case IN:
if (!FlagW)
{
IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
}
else
{
instr_pntr--;
instr_swap = IN;
cur_instr[instr_pntr] = WAIT;
}
IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case NEG:
NEG_8_Func(cur_instr[instr_pntr++]);
@ -667,11 +488,78 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
case RLD:
RLD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case SET_FL_LD:
case SET_FL_LD_R:
DEC16_Func(C, B);
SET_FL_LD_Func();
Ztemp1 = cur_instr[instr_pntr++];
Ztemp2 = cur_instr[instr_pntr++];
Ztemp3 = cur_instr[instr_pntr++];
if (((Regs[C] | (Regs[B] << 8)) != 0) && (Ztemp3 > 0))
{
cur_instr = new ushort[]
{DEC16, PCl, PCh,
DEC16, PCl, PCh,
TR16, Z, W, PCl, PCh,
INC16, Z, W,
IDLE,
Ztemp2, E, D,
WAIT,
OP_F,
OP};
BUSRQ = new ushort[] { D, D, D, D, D, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 };
}
else
{
cur_instr = new ushort[]
{ Ztemp2, E, D,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
instr_pntr = 0; bus_pntr = 0; mem_pntr = 0;
break;
case SET_FL_CP:
case SET_FL_CP_R:
SET_FL_CP_Func();
Ztemp1 = cur_instr[instr_pntr++];
Ztemp2 = cur_instr[instr_pntr++];
Ztemp3 = cur_instr[instr_pntr++];
if (((Regs[C] | (Regs[B] << 8)) != 0) && (Ztemp3 > 0) && !FlagZ)
{
cur_instr = new ushort[]
{DEC16, PCl, PCh,
DEC16, PCl, PCh,
TR16, Z, W, PCl, PCh,
INC16, Z, W,
IDLE,
Ztemp2, L, H,
WAIT,
OP_F,
OP};
BUSRQ = new ushort[] { H, H, H, H, H, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 };
}
else
{
cur_instr = new ushort[]
{ Ztemp2, L, H,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
instr_pntr = 0; bus_pntr = 0; mem_pntr = 0;
break;
case SET_FL_IR:
SET_FL_IR_Func(cur_instr[instr_pntr++]);
@ -682,38 +570,135 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
case WAIT:
if (FlagW)
{
instr_pntr--;
instr_pntr--; bus_pntr--; mem_pntr--;
}
break;
case RST:
Regs[Z] = cur_instr[instr_pntr++];
Regs[W] = 0;
break;
case REP_OP_I:
Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
Ztemp4 = cur_instr[instr_pntr++];
if (Ztemp4 == DEC16)
{
TR16_Func(Z, W, C, B);
DEC16_Func(Z, W);
DEC8_Func(B);
// take care of other flags
// taken from 'undocumented z80 documented' and Fuse
FlagN = Regs[ALU].Bit(7);
FlagH = FlagC = ((Regs[ALU] + Regs[C] - 1) & 0xFF) < Regs[ALU];
FlagP = TableParity[((Regs[ALU] + Regs[C] - 1) & 7) ^ Regs[B]];
}
else
{
TR16_Func(Z, W, C, B);
INC16_Func(Z, W);
DEC8_Func(B);
// take care of other flags
// taken from 'undocumented z80 documented' and Fuse
FlagN = Regs[ALU].Bit(7);
FlagH = FlagC = ((Regs[ALU] + Regs[C] + 1) & 0xFF) < Regs[ALU];
FlagP = TableParity[((Regs[ALU] + Regs[C] + 1) & 7) ^ Regs[B]];
}
Ztemp1 = cur_instr[instr_pntr++];
Ztemp2 = cur_instr[instr_pntr++];
Ztemp3 = cur_instr[instr_pntr++];
if ((Regs[B] != 0) && (Ztemp3 > 0))
{
cur_instr = new ushort[]
{IDLE,
IDLE,
DEC16, PCl, PCh,
DEC16, PCl, PCh,
IDLE,
Ztemp2, L, H,
WAIT,
OP_F,
OP};
BUSRQ = new ushort[] { H, H, H, H, H, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 };
}
else
{
switch (instr_swap)
{
case OP:
if (OnExecFetch != null) OnExecFetch(RegPC);
if (TraceCallback != null) TraceCallback(State());
FetchInstruction(FetchMemory(RegPC++));
instr_pntr = 0;
break;
case RD:
Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case WR:
Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case I_RD:
I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case I_WR:
I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case IN:
IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case OUT:
OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
}
cur_instr = new ushort[]
{ Ztemp2, L, H,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
instr_pntr = 0; bus_pntr = 0; mem_pntr = 0;
break;
case REP_OP_O:
OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
Ztemp4 = cur_instr[instr_pntr++];
if (Ztemp4 == DEC16)
{
DEC16_Func(L, H);
DEC8_Func(B);
TR16_Func(Z, W, C, B);
DEC16_Func(Z, W);
}
else
{
INC16_Func(L, H);
DEC8_Func(B);
TR16_Func(Z, W, C, B);
INC16_Func(Z, W);
}
// take care of other flags
// taken from 'undocumented z80 documented'
FlagN = Regs[ALU].Bit(7);
FlagH = FlagC = (Regs[ALU] + Regs[L]) > 0xFF;
FlagP = TableParity[((Regs[ALU] + Regs[L]) & 7) ^ (Regs[B])];
Ztemp1 = cur_instr[instr_pntr++];
Ztemp2 = cur_instr[instr_pntr++];
Ztemp3 = cur_instr[instr_pntr++];
if ((Regs[B] != 0) && (Ztemp3 > 0))
{
cur_instr = new ushort[]
{IDLE,
IDLE,
DEC16, PCl, PCh,
DEC16, PCl, PCh,
IDLE,
IDLE,//DEC16, B, ALU,
WAIT,
OP_F,
OP};
//Regs[B] = (ushort)((Regs[B] + 1) & 0xFF);
BUSRQ = new ushort[] { B, B, B, B, B, PCh, 0, 0, 0 };
MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 };
}
else
{
cur_instr = new ushort[]
{ IDLE,
WAIT,
OP_F,
OP };
BUSRQ = new ushort[] { PCh, 0, 0, 0 };
MEMRQ = new ushort[] { PCh, 0, 0, 0 };
}
instr_pntr = 0; bus_pntr = 0; mem_pntr = 0;
break;
}
TotalExecutedCycles++;
@ -787,10 +772,20 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
ser.Sync("EI_pending", ref EI_pending);
ser.Sync("instr_pntr", ref instr_pntr);
ser.Sync("bus_pntr", ref bus_pntr);
ser.Sync("mem_pntr", ref mem_pntr);
ser.Sync("cur_instr", ref cur_instr, false);
ser.Sync("BUSRQ", ref BUSRQ, false);
ser.Sync("MEMRQ", ref MEMRQ, false);
ser.Sync("instr_swap", ref instr_swap);
ser.Sync("opcode", ref opcode);
ser.Sync("FlagI", ref FlagI);
ser.Sync("FlagI1", ref FlagI1);
ser.Sync("FlagI2", ref FlagI2);
ser.Sync("FlagI3", ref FlagI3);
ser.Sync("FlagI4", ref FlagI4);
ser.Sync("FlagI5", ref FlagI5);
ser.Sync("FlagI6", ref FlagI6);
ser.Sync("FlagW", ref FlagW);
ser.Sync("NO Preifx", ref NO_prefix);

View File

@ -0,0 +1,409 @@
using BizHawk.Common;
using BizHawk.Emulation.Cores.Components.Z80A;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
public class CPUMonitor
{
#region Devices
private SpectrumBase _machine;
private Z80A _cpu;
public MachineType machineType = MachineType.ZXSpectrum48;
#endregion
#region Lookups
/// <summary>
/// CPU total executes t-states
/// </summary>
public long TotalExecutedCycles => _cpu.TotalExecutedCycles;
/// <summary>
/// Current BUSRQ line array
/// </summary>
public ushort BUSRQ
{
get
{
switch (machineType)
{
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
return _cpu.MEMRQ[_cpu.bus_pntr];
default:
return _cpu.BUSRQ[_cpu.mem_pntr];
}
}
}
#endregion
#region Construction
public CPUMonitor(SpectrumBase machine)
{
_machine = machine;
_cpu = _machine.CPU;
}
#endregion
#region State
/// <summary>
/// The last 16-bit port address that was detected
/// </summary>
public ushort lastPortAddr;
/// <summary>
/// If true, the next read memory operation has been contended
/// </summary>
public bool NextMemReadContended;
#endregion
#region Methods
/// <summary>
/// Handles the ULA and CPU cycle clocks, along with any memory and port contention
/// </summary>
public void ExecuteCycle()
{
// simulate the ULA clock cycle before the CPU cycle
_machine.ULADevice.CycleClock(TotalExecutedCycles);
// is the next CPU cycle causing a BUSRQ or IORQ?
if (BUSRQ > 0)
{
// check for IORQ
if (!CheckIO())
{
// is the memory address of the BUSRQ potentially contended?
if (_machine.IsContended(AscertainBUSRQAddress()))
{
var cont = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle);
if (cont > 0)
{
_cpu.TotalExecutedCycles += cont;
NextMemReadContended = true;
}
}
}
}
_cpu.ExecuteOne();
}
/// <summary>
/// Looks up the current BUSRQ address that is about to be signalled on the upcoming cycle
/// </summary>
/// <returns></returns>
private ushort AscertainBUSRQAddress()
{
ushort addr = 0;
switch (BUSRQ)
{
// PCh
case 1:
addr = (ushort)(_cpu.Regs[_cpu.PCl] | _cpu.Regs[_cpu.PCh] << 8);
break;
// SPh
case 3:
addr = (ushort)(_cpu.Regs[_cpu.SPl] | _cpu.Regs[_cpu.SPh] << 8);
break;
// A
case 4:
addr = (ushort)(_cpu.Regs[_cpu.F] | _cpu.Regs[_cpu.A] << 8);
break;
// B
case 6:
addr = (ushort)(_cpu.Regs[_cpu.C] | _cpu.Regs[_cpu.B] << 8);
break;
// D
case 8:
addr = (ushort)(_cpu.Regs[_cpu.E] | _cpu.Regs[_cpu.D] << 8);
break;
// H
case 10:
addr = (ushort)(_cpu.Regs[_cpu.L] | _cpu.Regs[_cpu.H] << 8);
break;
// W
case 12:
addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8);
break;
// Ixh
case 16:
addr = (ushort)(_cpu.Regs[_cpu.Ixl] | _cpu.Regs[_cpu.Ixh] << 8);
break;
// Iyh
case 18:
addr = (ushort)(_cpu.Regs[_cpu.Iyl] | _cpu.Regs[_cpu.Iyh] << 8);
break;
// I
case 21:
addr = (ushort)(_cpu.Regs[_cpu.R] | _cpu.Regs[_cpu.I] << 8);
break;
// BC
case Z80A.BIO1:
case Z80A.BIO2:
case Z80A.BIO3:
case Z80A.BIO4:
addr = (ushort)(_cpu.Regs[_cpu.C] | _cpu.Regs[_cpu.B] << 8);
break;
// WZ
case Z80A.WIO1:
case Z80A.WIO2:
case Z80A.WIO3:
case Z80A.WIO4:
addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8);
break;
}
return addr;
}
/// <summary>
/// Running every cycle, this determines whether the upcoming BUSRQ is for an IO operation
/// Also processes any contention
/// </summary>
/// <returns></returns>
private bool CheckIO()
{
bool isIO = false;
switch (BUSRQ)
{
// BC: T1
case Z80A.BIO1:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(1))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// BC: T2
case Z80A.BIO2:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(2))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// BC: T3
case Z80A.BIO3:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(3))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// BC: T4
case Z80A.BIO4:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(4))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// WZ: T1
case Z80A.WIO1:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(1))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// WZ: T2
case Z80A.WIO2:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(2))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// WZ: T3
case Z80A.WIO3:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(3))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// WZ: T4
case Z80A.WIO4:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(4))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
}
return isIO;
}
/// <summary>
/// Returns TRUE if the supplied T-cycle within an IO operation has the possibility of being contended
/// This can be different based on the emulated ZX Spectrum model
/// </summary>
/// <param name="T"></param>
/// <returns></returns>
private bool IsIOCycleContended(int T)
{
bool lowBitSet = (lastPortAddr & 0x0001) != 0;
bool highByte407f = false;
switch (machineType)
{
case MachineType.ZXSpectrum16:
case MachineType.ZXSpectrum48:
if ((lastPortAddr & 0xc000) == 0x4000)
highByte407f = true;
if (highByte407f)
{
// high byte 40-7f
if (lowBitSet)
{
// high byte 40-7f
// low bit set
// C:1, C:1, C:1, C:1
switch (T)
{
case 1:
case 2:
case 3:
case 4:
return true;
}
}
else
{
// high byte 40-7f
// low bit reset
// C:1, C:3
switch (T)
{
case 1:
case 2:
return true;
}
}
}
else
{
// high byte not 40-7f
if (lowBitSet)
{
// high byte not 40-7f
// low bit set
// N:4
}
else
{
// high byte not 40-7f
// low bit reset
// N:1, C:3
switch (T)
{
case 2:
return true;
}
}
}
break;
case MachineType.ZXSpectrum128:
case MachineType.ZXSpectrum128Plus2:
if ((lastPortAddr & 0xc000) == 0x4000 || (lastPortAddr & 0xc000) == 0xc000 && _machine.ContendedBankPaged())
highByte407f = true;
if (highByte407f)
{
// high byte 40-7f
if (lowBitSet)
{
// high byte 40-7f
// low bit set
// C:1, C:1, C:1, C:1
switch (T)
{
case 1:
case 2:
case 3:
case 4:
return true;
}
}
else
{
// high byte 40-7f
// low bit reset
// C:1, C:3
switch (T)
{
case 1:
case 2:
return true;
}
}
}
else
{
// high byte not 40-7f
if (lowBitSet)
{
// high byte not 40-7f
// low bit set
// N:4
}
else
{
// high byte not 40-7f
// low bit reset
// N:1, C:3
switch (T)
{
case 2:
return true;
}
}
}
break;
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
// No contention occurs as the ULA only applies contention when the Z80 MREQ line is active
// (which is not during an IO operation)
break;
}
return false;
}
/// <summary>
/// Called when the first byte of an instruction is fetched
/// </summary>
/// <param name="firstByte"></param>
public void OnExecFetch(ushort firstByte)
{
// fetch instruction without incrementing pc
//_cpu.FetchInstruction(_cpu.FetchMemory(firstByte));
}
#endregion
#region Serialization
public void SyncState(Serializer ser)
{
ser.BeginSection("CPUMonitor");
ser.Sync("lastPortAddr", ref lastPortAddr);
ser.Sync("NextMemReadContended", ref NextMemReadContended);
ser.EndSection();
}
#endregion
}
}

View File

@ -50,7 +50,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// Signs that all paging is disabled
/// If this is TRUE, then 128k and above machines need a hard reset before paging is allowed again
/// </summary>
protected bool PagingDisabled;
public bool PagingDisabled;
/// <summary>
/// Index of the currently paged ROM
@ -87,10 +87,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
protected int PagingConfiguration;
/// <summary>
/// The last byte that was read after contended cycles
/// </summary>
public byte LastContendedReadByte;
#endregion
#region Memory Related Methods
/// <summary>
@ -153,9 +156,23 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
public virtual byte FetchScreenMemory(ushort addr)
{
var value = ReadBus((ushort)((addr & 0x3FFF) + 0x4000));
//var value = ReadBus(addr);
return value;
}
/// <summary>
/// Checks whether supplied address is in a potentially contended bank
/// </summary>
/// <param name="addr"></param>
public abstract bool IsContended(ushort addr);
/// <summary>
/// Returns TRUE if there is a contended bank paged in
/// </summary>
/// <returns></returns>
public abstract bool ContendedBankPaged();
#endregion
#region Helper Methods

View File

@ -40,88 +40,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// Increments the CPU totalCycles counter by the tStates value specified
/// </summary>
/// <param name="tStates"></param>
public virtual void PortContention(int tStates)
{
CPU.TotalExecutedCycles += tStates;
}
//public virtual void PortContention(int tStates)
//{
// CPU.TotalExecutedCycles += tStates;
//}
/// <summary>
/// Simulates IO port contention based on the supplied address
/// This method is for 48k and 128k/+2 machines only and should be overridden for other models
/// </summary>
/// <param name="addr"></param>
public virtual void ContendPortAddress(ushort addr)
{
return;
/*
It takes four T states for the Z80 to read a value from an I/O port, or write a value to a port. As is the case with memory access,
this can be lengthened by the ULA. There are two effects which occur here:
If the port address being accessed has its low bit reset, the ULA is required to supply the result, which leads to a delay if it is
currently busy handling the screen.
The address of the port being accessed is placed on the data bus. If this is in the range 0x4000 to 0x7fff, the ULA treats this as an
attempted access to contended memory and therefore introduces a delay. If the port being accessed is between 0xc000 and 0xffff,
this effect does not apply, even on a 128K machine if a contended memory bank is paged into the range 0xc000 to 0xffff.
These two effects combine to lead to the following contention patterns:
High byte | |
in 40 - 7F? | Low bit | Contention pattern
------------+---------+-------------------
No | Reset | N:1, C:3
No | Set | N:4
Yes | Reset | C:1, C:3
Yes | Set | C:1, C:1, C:1, C:1
The 'Contention pattern' column should be interpreted from left to right. An "N:n" entry means that no delay is applied at this cycle, and the Z80 continues uninterrupted for 'n' T states. A "C:n" entry means that the ULA halts the Z80; the delay is exactly the same as would occur for a contended memory access at this cycle (eg 6 T states at cycle 14335, 5 at 14336, etc on the 48K machine). After this delay, the Z80 then continues for 'n' cycles.
*/
// is the low bit reset (i.e. is this addressing the ULA)?
bool lowBit = (addr & 0x0001) != 0;
if ((addr & 0xc000) == 0x4000 || (addr & 0xc000) == 0xC000)
{
// high byte is in 40 - 7F
if (lowBit)
{
// lowbit is set
// C:1, C:1, C:1, C:1
for (int i = 0; i < 4; i++)
{
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
CPU.TotalExecutedCycles++;
}
}
else
{
// low bit is reset
// C:1, C:3
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
CPU.TotalExecutedCycles++;
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
CPU.TotalExecutedCycles += 3;
}
}
else
{
// high byte is NOT in 40 - 7F
if (lowBit)
{
// lowbit is set
// C:1, C:1, C:1, C:1
CPU.TotalExecutedCycles += 4;
}
else
{
// lowbit is reset
// N:1, C:3
CPU.TotalExecutedCycles++;
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
CPU.TotalExecutedCycles += 3;
}
}
}
//public abstract void ContendPort(ushort addr);
}
}

View File

@ -32,7 +32,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <summary>
/// The emulated ULA device
/// </summary>
public ULABase ULADevice { get; set; }
//public ULABase ULADevice { get; set; }
public ULA ULADevice { get; set; }
/// <summary>
/// Monitors CPU cycles
/// </summary>
public CPUMonitor CPUMon { get; set; }
/// <summary>
/// The spectrum buzzer/beeper
@ -141,6 +147,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public virtual void ExecuteFrame(bool render, bool renderSound)
{
ULADevice.FrameEnd = false;
ULADevice.ULACycleCounter = CurrentFrameCycle;
InputRead = false;
_render = render;
_renderSound = renderSound;
@ -152,49 +161,42 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (_renderSound)
{
//BuzzerDevice.StartFrame();
//TapeBuzzer.StartFrame();
if (AYDevice != null)
AYDevice.StartFrame();
}
PollInput();
while (CurrentFrameCycle < ULADevice.FrameLength)
for (;;)
{
// check for interrupt
ULADevice.CheckForInterrupt(CurrentFrameCycle);
// run a single CPU instruction
CPU.ExecuteOne();
// run the CPU Monitor cycle
CPUMon.ExecuteCycle();
// cycle the tape device
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.TapeCycle();
// has frame end been reached?
if (ULADevice.FrameEnd)
break;
}
OverFlow = (int)CurrentFrameCycle - ULADevice.FrameLength;
// 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);
// paint the buffer at end of frame
if (_render)
ULADevice.RenderScreen(ULADevice.FrameLength);
if (_renderSound)
{
//BuzzerDevice.EndFrame();
//TapeBuzzer.EndFrame();
}
ULADevice.LastTState = 0;
if (AYDevice != null)
AYDevice.EndFrame();
FrameCount++;
// setup for next frame
ULADevice.ResetInterrupt();
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.EndFrame();
@ -203,8 +205,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// is this a lag frame?
Spectrum.IsLagFrame = !InputRead;
// FDC debug
// FDC debug
if (UPDDiskDevice != null && UPDDiskDevice.writeDebug)
{
// only write UPD log every second
@ -223,7 +224,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public virtual void HardReset()
{
ULADevice.ResetInterrupt();
//ULADevice.ResetInterrupt();
ROMPaged = 0;
SpecialPagingMode = false;
RAMPaged = 0;
@ -275,7 +276,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public virtual void SoftReset()
{
ULADevice.ResetInterrupt();
//ULADevice.ResetInterrupt();
ROMPaged = 0;
SpecialPagingMode = false;
RAMPaged = 0;
@ -356,11 +357,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
ser.Sync("PagingConfiguration", ref PagingConfiguration);
ser.Sync("ROMhigh", ref ROMhigh);
ser.Sync("ROMlow", ref ROMlow);
ser.Sync("LastContendedReadByte", ref LastContendedReadByte);
KeyboardDevice.SyncState(ser);
BuzzerDevice.SyncState(ser);
TapeBuzzer.SyncState(ser);
ULADevice.SyncState(ser);
CPUMon.SyncState(ser);
if (AYDevice != null)
{

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/*
/// <summary>
/// ULA (Uncommitted Logic Array) implementation
/// </summary>
@ -78,6 +79,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public byte[] contentionTable;
/// <summary>
/// Contention offset (used for testing)
/// </summary>
public int contentionOffset = 0; // -5;
#endregion
#region Screen Rendering
@ -86,6 +92,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// Video output buffer
/// </summary>
public int[] ScreenBuffer;
/// <summary>
/// Screen rendering T-State info
/// </summary>
public RenderCycle[] RenderTable;
/// <summary>
/// Display memory
/// </summary>
@ -246,14 +258,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
#region Interrupt
/// <summary>
/// The number of T-States that the INT pin is simulated to be held low
/// The t-state within the frame that an interrupt is raised
/// </summary>
public int InterruptPeriod;
public int InterruptStart;
/// <summary>
/// The longest instruction cycle count
/// The number of T-States that the INT pin is simulated to be held low
/// </summary>
protected int LongestOperationCycles = 23;
public int InterruptDuration = 32;
/// <summary>
/// Signs that an interrupt has been raised in this frame.
@ -287,13 +299,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
return;
}
if (currentCycle < LongestOperationCycles)// InterruptPeriod)
if (currentCycle < InterruptStart)
{
// interrupt does not need to be raised yet
return;
}
if (currentCycle >= InterruptPeriod + LongestOperationCycles)
if (currentCycle > InterruptStart + InterruptDuration)
{
// interrupt should have already been raised and the cpu may or
// may not have caught it. The time has passed so revoke the signal
@ -350,31 +362,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="addr"></param>
/// <returns></returns>
public abstract bool IsContended(int addr);
/// <summary>
/// Contends the machine for a given address
/// </summary>
/// <param name="addr"></param>
public virtual void Contend(ushort addr)
{
if (IsContended(addr) && !(_machine is ZX128Plus3))
{
_machine.CPU.TotalExecutedCycles += contentionTable[CurrentTStateInFrame];
}
}
public virtual void Contend(int addr, int time, int count)
{
if (IsContended(addr) && !(_machine is ZX128Plus3))
{
for (int f = 0; f < count; f++)
{
_machine.CPU.TotalExecutedCycles += contentionTable[CurrentTStateInFrame] + time;
}
}
else
_machine.CPU.TotalExecutedCycles += count * time;
}
/// <summary>
/// Resets render state once interrupt is generated
@ -401,6 +389,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
}
/// <summary>
/// Builds the T-State to attribute map used with the floating bus
/// </summary>
@ -426,6 +415,32 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
}
/// <summary>
/// Returns the floating bus value
/// </summary>
public virtual void ReadFloatingBus(ref int result)
{
// Floating bus is read on the previous cycle
long _tStates = _machine.CurrentFrameCycle - 1;
// if we are on the top or bottom border return 0xff
if ((_tStates < contentionStartPeriod) || (_tStates > contentionEndPeriod))
{
result = 0xff;
}
else
{
if (floatingBusTable[_tStates] < 0)
{
result = 0xff;
}
else
{
result = _machine.ReadBus((ushort)floatingBusTable[_tStates]);
}
}
}
/// <summary>
/// Updates the screen buffer based on the number of T-States supplied
/// </summary>
@ -444,7 +459,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
//the additional 1 tstate is required to get correct number of bytes to output in ircontention.sna
elapsedTStates = (_tstates + 1 - lastTState) - 1;
elapsedTStates = (_tstates + 1 - lastTState);
//It takes 4 tstates to write 1 byte. Or, 2 pixels per t-state.
@ -755,6 +770,27 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
#endregion
}
/// <summary>
/// T-State display mapping
/// </summary>
public class RenderCycle
{
public RenderType Type { get; set; }
public short DisplayAddress { get; set; }
public short AttributeAddress { get; set; }
public int ContentionValue { get; set; }
public short FloatingBusAddress { get; set; }
}
public enum RenderType
{
None,
Border,
Display
}
*/
}

View File

@ -134,6 +134,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7)
case 1:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM5[addr % 0x4000] = value;
break;
@ -162,6 +163,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
RAM4[addr % 0x4000] = value;
break;
case 5:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM5[addr % 0x4000] = value;
break;
case 6:
@ -175,10 +177,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
default:
break;
}
// update ULA screen buffer if necessary
if ((addr & 49152) == 16384 && _render)
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
}
/// <summary>
@ -189,9 +187,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadMemory(ushort addr)
{
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
var data = ReadBus(addr);
return data;
}
@ -204,13 +199,57 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WriteMemory(ushort addr, byte value)
{
// apply contention if necessary
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
WriteBus(addr, value);
}
/// <summary>
/// Checks whether supplied address is in a potentially contended bank
/// </summary>
/// <param name="addr"></param>
public override bool IsContended(ushort addr)
{
var a = addr & 0xc000;
if (a == 0x4000)
{
// low port contention
return true;
}
if (a == 0xc000)
{
// high port contention - check for contended bank paged in
switch (RAMPaged)
{
case 1:
case 3:
case 5:
case 7:
return true;
}
}
return false;
}
/// <summary>
/// Returns TRUE if there is a contended bank paged in
/// </summary>
/// <returns></returns>
public override bool ContendedBankPaged()
{
switch (RAMPaged)
{
case 1:
case 3:
case 5:
case 7:
return true;
}
return false;
}
/// <summary>
/// ULA reads the memory at the specified address
/// (No memory contention)

View File

@ -18,18 +18,36 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
bool deviceAddressed = true;
// process IO contention
ContendPortAddress(port);
int result = 0xFF;
// ports 0x3ffd & 0x7ffd
// traditionally thought to be write-only
if (port == 0x3ffd || port == 0x7ffd)
{
// https://faqwiki.zxnet.co.uk/wiki/ZX_Spectrum_128
// HAL bugs
// Reads from port 0x7ffd cause a crash, as the 128's HAL10H8 chip does not distinguish between reads and writes to this port,
// resulting in a floating data bus being used to set the paging registers.
// -asni (2018-06-08) - need this to pass the final portread tests from fusetest.tap
// get the floating bus value
ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port);
// use this to set the paging registers
WritePort(port, (byte)result);
// return the floating bus value
return (byte)result;
}
// check AY
if (AYDevice.ReadPort(port, ref result))
return (byte)result;
// Kempston joystick input takes priority over all other input
byte lowByte = (byte)(port & 0xff);
// Kempston joystick input takes priority over keyboard input
// if this is detected just return the kempston byte
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
if (lowByte == 0x1f)
{
if (LocateUniqueJoystick(JoystickType.Kempston) != null)
return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine;
@ -53,25 +71,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (!deviceAddressed)
{
// If this is an unused port the floating memory bus should be returned
// Floating bus is read on the previous cycle
long _tStates = CurrentFrameCycle - 1;
// if we are on the top or bottom border return 0xff
if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod))
{
result = 0xff;
}
else
{
if (ULADevice.floatingBusTable[_tStates] < 0)
{
result = 0xff;
}
else
{
result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]);
}
}
ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port);
}
return (byte)result;
@ -84,9 +84,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WritePort(ushort port, byte value)
{
// process IO contention
ContendPortAddress(port);
// get a BitArray of the port
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
// get a BitArray of the value byte
@ -142,7 +139,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
// store the last OUT byte
LastULAOutByte = value;
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
/*
Bit 7 6 5 4 3 2 1 0
@ -152,10 +148,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
*/
// Border - LSB 3 bits hold the border colour
if (ULADevice.borderColour != (value & BORDER_BIT))
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
ULADevice.borderColour = value & BORDER_BIT;
if (ULADevice.BorderColor != (value & BORDER_BIT))
{
ULADevice.RenderScreen((int)CurrentFrameCycle);
ULADevice.BorderColor = value & BORDER_BIT;
}
// Buzzer
BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0);

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
class Screen128 : ULA
{
#region Construction
public Screen128(SpectrumBase machine)
: base(machine)
{
// interrupt
InterruptStartTime = 3;
InterruptLength = 36;
// offsets
RenderTableOffset = 58;
ContentionOffset = 6;
FloatingBusOffset = 1;
// timing
ClockSpeed = 3546900;
FrameCycleLength = 70908;
ScanlineTime = 228;
BorderLeftTime = 24;
BorderRightTime = 24;
FirstPaperLine = 63;
FirstPaperTState = 64;
// screen layout
Border4T = true;
Border4TStage = 2;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48; // 55; // 48;
BorderBottomHeight = 48; // 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum128);
SetupScreenSize();
}
#endregion
}
}

View File

@ -1,6 +1,7 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/*
class ULA128 : ULABase
{
#region Construction
@ -8,8 +9,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
public ULA128(SpectrumBase machine)
: base(machine)
{
InterruptPeriod = 36;
LongestOperationCycles = 64 + 2;
InterruptStart = 0;
//LongestOperationCycles = 64 + 2;
FrameLength = 70908;
ClockSpeed = 3546900;
@ -189,4 +190,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
*/
}

View File

@ -21,12 +21,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
Spectrum = spectrum;
CPU = cpu;
CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.ZXSpectrum128;
ROMPaged = 0;
SHADOWPaged = false;
RAMPaged = 0;
PagingDisabled = false;
ULADevice = new ULA128(this);
//ULADevice = new ULA128(this);
ULADevice = new Screen128(this); // still todo
BuzzerDevice = new Beeper(this);
BuzzerDevice.Init(44100, ULADevice.FrameLength);

View File

@ -236,9 +236,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
break;
case 1:
case 2:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM5[addr % 0x4000] = value;
break;
case 3:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM7[addr % 0x4000] = value;
break;
}
@ -265,6 +267,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
RAM3[addr % 0x4000] = value;
break;
case 1:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM7[addr % 0x4000] = value;
break;
}
@ -299,6 +302,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// RAM 0x4000 (RAM5 - Bank5 only)
case 1:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM5[addr % 0x4000] = value;
break;
@ -327,12 +331,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
RAM4[addr % 0x4000] = value;
break;
case 5:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM5[addr % 0x4000] = value;
break;
case 6:
RAM6[addr % 0x4000] = value;
break;
case 7:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM7[addr % 0x4000] = value;
break;
}
@ -341,10 +347,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
break;
}
}
// update ULA screen buffer if necessary
if ((addr & 49152) == 16384 && _render)
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
}
/// <summary>
@ -355,10 +357,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadMemory(ushort addr)
{
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
var data = ReadBus(addr);
if (CPUMon.NextMemReadContended)
{
LastContendedReadByte = data;
CPUMon.NextMemReadContended = false;
}
return data;
}
@ -370,13 +375,80 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WriteMemory(ushort addr, byte value)
{
// apply contention if necessary
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
// update ULA screen buffer if necessary BEFORE T1 write
/*
if (!SpecialPagingMode)
{
if (((addr & 49152) == 16384 || ((addr & 0xc000) == 0xc000) && (RAMPaged == 5 || RAMPaged == 7)) && _render)
ULADevice.RenderScreen((int)CurrentFrameCycle);
}
else
{
switch (PagingConfiguration)
{
case 2:
case 3:
if ((addr & 49152) == 16384)
ULADevice.RenderScreen((int)CurrentFrameCycle);
break;
case 1:
if ((addr & 49152) == 16384 || addr >= 0xc000)
ULADevice.RenderScreen((int)CurrentFrameCycle);
break;
}
}
*/
WriteBus(addr, value);
}
/// <summary>
/// Checks whether supplied address is in a potentially contended bank
/// </summary>
/// <param name="addr"></param>
public override bool IsContended(ushort addr)
{
var a = addr & 0xc000;
if (a == 0x4000)
{
// low port contention
return true;
}
if (a == 0xc000)
{
// high port contention - check for contended bank paged in
switch (RAMPaged)
{
case 4:
case 5:
case 6:
case 7:
return true;
}
}
return false;
}
/// <summary>
/// Returns TRUE if there is a contended bank paged in
/// </summary>
/// <returns></returns>
public override bool ContendedBankPaged()
{
switch (RAMPaged)
{
case 4:
case 5:
case 6:
case 7:
return true;
}
return false;
}
/// <summary>
/// ULA reads the memory at the specified address
/// (No memory contention)

View File

@ -18,18 +18,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
bool deviceAddressed = true;
// process IO contention
ContendPortAddress(port);
int result = 0xFF;
// check AY
if (AYDevice.ReadPort(port, ref result))
return (byte)result;
// Kempston joystick input takes priority over all other input
byte lowByte = (byte)(port & 0xff);
// Kempston joystick input takes priority over all keyboard input
// if this is detected just return the kempston byte
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
if (lowByte == 0x1f)
{
if (LocateUniqueJoystick(JoystickType.Kempston) != null)
return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine;
@ -53,99 +52,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (!deviceAddressed)
{
// If this is an unused port the floating memory bus should be returned
// Floating bus is read on the previous cycle
long _tStates = CurrentFrameCycle - 1;
// if we are on the top or bottom border return 0xff
if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod))
{
result = 0xff;
}
else
{
if (ULADevice.floatingBusTable[_tStates] < 0)
{
result = 0xff;
}
else
{
result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]);
}
}
ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port);
}
/*
// Check whether the low bit is reset
// Technically the ULA should respond to every even I/O address
bool lowBitReset = (port & 0x0001) == 0;
// Kempston joystick input takes priority over all other input
// if this is detected just return the kempston byte
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
{
if (LocateUniqueJoystick(JoystickType.Kempston) != null)
return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine;
InputRead = true;
}
else if (lowBitReset)
{
// Even I/O address so get input from keyboard
KeyboardDevice.ReadPort(port, ref result);
TapeDevice.MonitorRead();
// not a lagframe
InputRead = true;
// tape loading monitor cycle
//TapeDevice.MonitorRead();
// process tape INs
TapeDevice.ReadPort(port, ref result);
}
else
{
// devices other than the ULA will respond here
// (e.g. the AY sound chip in a 128k spectrum
// AY register activate - on +3/2a both FFFD and BFFD active AY
if ((port & 0xc002) == 0xc000)
{
result = (int)AYDevice.PortRead();
}
else if ((port & 0xc002) == 0x8000)
{
result = (int)AYDevice.PortRead();
}
// Kempston Mouse
/*
else if ((port & 0xF002) == 0x2000) //Is bit 12 set and bits 13,14,15 and 1 reset?
{
//result = udpDrive.DiskStatusRead();
// disk drive is not yet implemented - return a max status byte for the menu to load
result = 255;
}
else if ((port & 0xF002) == 0x3000)
{
//result = udpDrive.DiskReadByte();
result = 0;
}
else if ((port & 0xF002) == 0x0)
{
if (PagingDisabled)
result = 0x1;
else
result = 0xff;
}
*//*
// if unused port the floating memory bus should be returned (still todo)
}
*/
return (byte)result;
}
@ -157,9 +65,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WritePort(ushort port, byte value)
{
// process IO contention
ContendPortAddress(port);
// get a BitArray of the port
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
// get a BitArray of the value byte
@ -241,10 +146,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
*/
// Border - LSB 3 bits hold the border colour
if (ULADevice.borderColour != (value & BORDER_BIT))
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
ULADevice.borderColour = value & BORDER_BIT;
if (ULADevice.BorderColor != (value & BORDER_BIT))
{
ULADevice.RenderScreen((int)CurrentFrameCycle);
ULADevice.BorderColor = value & BORDER_BIT;
}
// Buzzer
BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0);
@ -279,15 +185,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
set { ROMPaged = value; }
}
/// <summary>
/// Override port contention
/// +3/2a does not have the same ULA IO contention
/// </summary>
/// <param name="addr"></param>
public override void ContendPortAddress(ushort addr)
{
//CPU.TotalExecutedCycles += 4;
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
class Screen128Plus2a : ULA
{
#region Construction
public Screen128Plus2a(SpectrumBase machine)
: base(machine)
{
// interrupt
InterruptStartTime = 0;
InterruptLength = 32;
// offsets
RenderTableOffset = 58;
ContentionOffset = 9;
FloatingBusOffset = 0;
// timing
ClockSpeed = 3546900;
FrameCycleLength = 70908;
ScanlineTime = 228;
BorderLeftTime = 24;
BorderRightTime = 24;
FirstPaperLine = 63;
FirstPaperTState = 64;
// screen layout
Border4T = true;
Border4TStage = 2;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48;// 55;
BorderBottomHeight = 48; // 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum128Plus2a);
SetupScreenSize();
GenerateP3PortTable();
}
#endregion
}
}

View File

@ -1,6 +1,7 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/*
class ULAPlus2a : ULABase
{
#region Construction
@ -8,8 +9,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
public ULAPlus2a(SpectrumBase machine)
: base(machine)
{
InterruptPeriod = 36;
LongestOperationCycles = 64 + 2;
InterruptStart = 0;
//LongestOperationCycles = 64 + 2;
FrameLength = 70908;
ClockSpeed = 3546900;
@ -193,4 +194,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
*/
}

View File

@ -21,12 +21,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
Spectrum = spectrum;
CPU = cpu;
CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.ZXSpectrum128Plus2a;
ROMPaged = 0;
SHADOWPaged = false;
RAMPaged = 0;
PagingDisabled = false;
ULADevice = new ULAPlus2a(this);
//ULADevice = new ULAPlus2a(this);
ULADevice = new Screen128Plus2a(this); // still todo
BuzzerDevice = new Beeper(this);
BuzzerDevice.Init(44100, ULADevice.FrameLength);

View File

@ -236,10 +236,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
break;
case 1:
case 2:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM5[addr % 0x4000] = value;
break;
case 3:
RAM7[addr % 0x4000] = value;
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM7[addr % 0x4000] = value;
break;
}
break;
@ -265,6 +267,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
RAM3[addr % 0x4000] = value;
break;
case 1:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM7[addr % 0x4000] = value;
break;
}
@ -299,6 +302,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// RAM 0x4000 (RAM5 - Bank5 only)
case 1:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM5[addr % 0x4000] = value;
break;
@ -327,12 +331,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
RAM4[addr % 0x4000] = value;
break;
case 5:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM5[addr % 0x4000] = value;
break;
case 6:
RAM6[addr % 0x4000] = value;
break;
case 7:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM7[addr % 0x4000] = value;
break;
}
@ -341,10 +347,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
break;
}
}
// update ULA screen buffer if necessary
if ((addr & 49152) == 16384 && _render)
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
}
/// <summary>
@ -355,10 +357,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadMemory(ushort addr)
{
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
var data = ReadBus(addr);
if (CPUMon.NextMemReadContended)
{
LastContendedReadByte = data;
CPUMon.NextMemReadContended = false;
}
return data;
}
@ -370,13 +375,81 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WriteMemory(ushort addr, byte value)
{
// apply contention if necessary
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
/*
// update ULA screen buffer if necessary BEFORE T1 write
if (!SpecialPagingMode)
{
if (((addr & 49152) == 16384 || ((addr & 0xc000) == 0xc000) && (RAMPaged == 5 || RAMPaged == 7)) && _render)
ULADevice.RenderScreen((int)CurrentFrameCycle);
}
else
{
switch (PagingConfiguration)
{
case 2:
case 3:
if ((addr & 49152) == 16384)
ULADevice.RenderScreen((int)CurrentFrameCycle);
break;
case 1:
if ((addr & 49152) == 16384 || addr >= 0xc000)
ULADevice.RenderScreen((int)CurrentFrameCycle);
break;
}
}
*/
WriteBus(addr, value);
}
/// <summary>
/// Checks whether supplied address is in a potentially contended bank
/// </summary>
/// <param name="addr"></param>
public override bool IsContended(ushort addr)
{
var a = addr & 0xc000;
if (a == 0x4000)
{
// low port contention
return true;
}
if (a == 0xc000)
{
// high port contention - check for contended bank paged in
switch (RAMPaged)
{
case 4:
case 5:
case 6:
case 7:
return true;
}
}
return false;
}
/// <summary>
/// Returns TRUE if there is a contended bank paged in
/// </summary>
/// <returns></returns>
public override bool ContendedBankPaged()
{
switch (RAMPaged)
{
case 4:
case 5:
case 6:
case 7:
return true;
}
return false;
}
/// <summary>
/// ULA reads the memory at the specified address
/// (No memory contention)

View File

@ -18,18 +18,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
bool deviceAddressed = true;
// process IO contention
ContendPortAddress(port);
int result = 0xFF;
// check AY
if (AYDevice.ReadPort(port, ref result))
return (byte)result;
// Kempston joystick input takes priority over all other input
byte lowByte = (byte)(port & 0xff);
// Kempston joystick input takes priority over all keyboard input
// if this is detected just return the kempston byte
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
if (lowByte == 0x1f)
{
if (LocateUniqueJoystick(JoystickType.Kempston) != null)
return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine;
@ -57,25 +56,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (!deviceAddressed)
{
// If this is an unused port the floating memory bus should be returned
// Floating bus is read on the previous cycle
long _tStates = CurrentFrameCycle - 1;
// if we are on the top or bottom border return 0xff
if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod))
{
result = 0xff;
}
else
{
if (ULADevice.floatingBusTable[_tStates] < 0)
{
result = 0xff;
}
else
{
result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]);
}
}
ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port);
}
return (byte)result;
@ -88,9 +69,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WritePort(ushort port, byte value)
{
// process IO contention
ContendPortAddress(port);
// get a BitArray of the port
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
// get a BitArray of the value byte
@ -174,10 +152,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
*/
// Border - LSB 3 bits hold the border colour
if (ULADevice.borderColour != (value & BORDER_BIT))
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
ULADevice.borderColour = value & BORDER_BIT;
if (ULADevice.BorderColor != (value & BORDER_BIT))
{
ULADevice.RenderScreen((int)CurrentFrameCycle);
ULADevice.BorderColor = value & BORDER_BIT;
}
// Buzzer
BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0);
@ -212,15 +191,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
set { ROMPaged = value; }
}
/// <summary>
/// Override port contention
/// +3/2a does not have the same ULA IO contention
/// </summary>
/// <param name="addr"></param>
public override void ContendPortAddress(ushort addr)
{
//CPU.TotalExecutedCycles += 4;
}
}
}

View File

@ -1,6 +1,7 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/*
class ULAPlus3 : ULABase
{
#region Construction
@ -8,8 +9,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
public ULAPlus3(SpectrumBase machine)
: base(machine)
{
InterruptPeriod = 36;
LongestOperationCycles = 64 + 2;
InterruptStart = 0;
//LongestOperationCycles = 64 + 2;
FrameLength = 70908;
ClockSpeed = 3546900;
@ -193,4 +194,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
*/
}

View File

@ -20,13 +20,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
Spectrum = spectrum;
CPU = cpu;
CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.ZXSpectrum128Plus3;
ROMPaged = 0;
SHADOWPaged = false;
RAMPaged = 0;
PagingDisabled = false;
ULADevice = new ULAPlus3(this);
// ULADevice = new ULAPlus3(this);
ULADevice = new Screen128Plus2a(this); // still todo
BuzzerDevice = new Beeper(this);
BuzzerDevice.Init(44100, ULADevice.FrameLength);

View File

@ -24,7 +24,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
#endregion
#region Memory
/* 48K Spectrum has NO memory paging
@ -86,13 +85,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// cannot write to ROM
break;
case 1:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM0[index] = value;
break;
}
// update ULA screen buffer if necessary
if ((addr & 49152) == 16384 && _render)
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
}
}
/// <summary>
@ -103,9 +99,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadMemory(ushort addr)
{
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
var data = ReadBus(addr);
return data;
}
@ -118,10 +111,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WriteMemory(ushort addr, byte value)
{
// apply contention if necessary
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
WriteBus(addr, value);
}

View File

@ -77,19 +77,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// cannot write to ROM
break;
case 1:
ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM0[index] = value;
break;
case 2:
RAM1[index] = value;
RAM1[index] = value;
break;
case 3:
RAM2[index] = value;
break;
}
// update ULA screen buffer if necessary
if ((addr & 49152) == 16384 && _render)
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
}
/// <summary>
@ -100,9 +97,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadMemory(ushort addr)
{
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
var data = ReadBus(addr);
return data;
}
@ -114,14 +108,30 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="addr"></param>
/// <param name="value"></param>
public override void WriteMemory(ushort addr, byte value)
{
// apply contention if necessary
if (ULADevice.IsContended(addr))
CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle];
{
WriteBus(addr, value);
}
/// <summary>
/// Checks whether supplied address is in a potentially contended bank
/// </summary>
/// <param name="addr"></param>
public override bool IsContended(ushort addr)
{
if ((addr & 49152) == 16384)
return true;
return false;
}
/// <summary>
/// Returns TRUE if there is a contended bank paged in
/// </summary>
/// <returns></returns>
public override bool ContendedBankPaged()
{
return false;
}
/// <summary>
/// Sets up the ROM
/// </summary>

View File

@ -15,18 +15,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public override byte ReadPort(ushort port)
{
// process IO contention
ContendPortAddress(port);
int result = 0xFF;
// Check whether the low bit is reset
// Technically the ULA should respond to every even I/O address
bool lowBitReset = (port & 0x0001) == 0;
// Kempston joystick input takes priority over all other input
byte lowByte = (byte)(port & 0xff);
// Kempston joystick input takes priority over keyboard input
// if this is detected just return the kempston byte
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
if (lowByte == 0x1f)
{
if (LocateUniqueJoystick(JoystickType.Kempston) != null)
return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine;
@ -34,6 +32,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// not a lag frame
InputRead = true;
}
// Even ports always address the ULA
else if (lowBitReset)
{
// Even I/O address so get input from keyboard
@ -56,25 +55,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// If this is an unused port the floating memory bus should be returned
// Floating bus is read on the previous cycle
long _tStates = CurrentFrameCycle - 1;
// if we are on the top or bottom border return 0xff
if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod))
{
result = 0xff;
}
else
{
if (ULADevice.floatingBusTable[_tStates] < 0)
{
result = 0xff;
}
else
{
result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]);
}
}
ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port);
}
return (byte)result;
@ -87,9 +68,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WritePort(ushort port, byte value)
{
// process IO contention
ContendPortAddress(port);
// Check whether the low bit is reset
// Technically the ULA should respond to every even I/O address
if ((port & 0x0001) != 0)
@ -106,14 +84,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
*/
// Border - LSB 3 bits hold the border colour
if (ULADevice.borderColour != (value & BORDER_BIT))
if (ULADevice.BorderColor != (value & BORDER_BIT))
{
// border value has changed - update the screen buffer
ULADevice.UpdateScreenBuffer(CurrentFrameCycle);
ULADevice.RenderScreen((int)CurrentFrameCycle);
ULADevice.BorderColor = value & BORDER_BIT;
}
ULADevice.borderColour = value & BORDER_BIT;
// Buzzer
BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0);
@ -124,6 +100,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
//TapeDevice.ProcessMicBit((value & MIC_BIT) != 0);
}
}
}

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
class Screen48 : ULA
{
#region Construction
public Screen48(SpectrumBase machine)
: base(machine)
{
// interrupt
InterruptStartTime = 3;
InterruptLength = 32;
// offsets
RenderTableOffset = 56;
ContentionOffset = 6;
FloatingBusOffset = 1;
// timing
ClockSpeed = 3500000;
FrameCycleLength = 69888;
ScanlineTime = 224;
BorderLeftTime = 24;
BorderRightTime = 24;
FirstPaperLine = 64;
FirstPaperTState = 64;
// screen layout
Border4T = true;
Border4TStage = 0;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48;// 55;// 48;
BorderBottomHeight = 48;// 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum48);
SetupScreenSize();
}
#endregion
}
}

View File

@ -1,6 +1,9 @@

using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/*
class ULA48 : ULABase
{
#region Construction
@ -8,8 +11,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
public ULA48(SpectrumBase machine)
: base(machine)
{
InterruptPeriod = 32;
LongestOperationCycles = 64;
InterruptStart = 0;// 5; // 0; // 3; // 0;// 32;
//LongestOperationCycles = 32;
FrameLength = 69888;
ClockSpeed = 3500000;
@ -55,7 +58,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
public override void Reset()
{
contentionStartPeriod = 14335; // + LateTiming;
contentionOffset = 0;
InterruptStart = 0;
contentionStartPeriod = 14335;// + contentionOffset; // + LateTiming;
contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline);
screen = _machine.RAM0;
screenByteCtr = DisplayStart;
@ -160,6 +166,30 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
h += TstatesPerScanline - 128;
}
// offset floating bus table
var offset = 0;
if (offset != 0)
{
var fbt = floatingBusTable.ToArray();
for (int i = 0; i < FrameLength; i++)
{
var off = i + offset;
if (off < 0)
{
off = FrameLength - 1 + off;
}
else if (off >= FrameLength)
{
off = off - FrameLength;
}
fbt[off] = floatingBusTable[i];
}
floatingBusTable = fbt.ToArray();
}
//build bottom border
while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom))
{
@ -177,4 +207,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
*/
}

View File

@ -21,7 +21,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
Spectrum = spectrum;
CPU = cpu;
ULADevice = new ULA48(this);
CPUMon = new CPUMonitor(this);
//ULADevice = new ULA48(this);
ULADevice = new Screen48(this);
BuzzerDevice = new Beeper(this);
BuzzerDevice.Init(44100, ULADevice.FrameLength);

View File

@ -410,6 +410,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
sb.Append(p.ToString("N0") + "%");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
sb.Clear();
// get position within the tape itself
sb.Append("Tape Pos: ");
var ind = _machine.TapeDevice.CurrentDataBlockIndex;
int cnt = 0;
for (int i = 0; i < ind; i++)
{
cnt += _machine.TapeDevice.DataBlocks[i].DataPeriods.Count;
}
// now we are at our current block
int ourPos = cnt + pos;
cnt += end;
// count periods in the remaining blocks
for (int i = ind + 1; i < _machine.TapeDevice.DataBlocks.Count; i++)
{
cnt += _machine.TapeDevice.DataBlocks[i].DataPeriods.Count;
}
// work out overall position within the tape
p = 0;
p = ((double)ourPos / (double)cnt) * (double)100;
sb.Append(p.ToString("N0") + "%");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
#endregion

View File

@ -7,249 +7,11 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Utilities
/// </summary>
public partial class ZXSpectrum
{
/*
* CPU Helper Methods
*/
public ushort RegPC
{
get { return (ushort)((_cpu.Regs[0] << 8 | _cpu.Regs[1])); }
set
{
_cpu.Regs[1] = (ushort)(value & 0xFF);
_cpu.Regs[0] = (ushort)((value >> 8) & 0xFF);
}
}
public ushort RegIX
{
get { return (ushort)((_cpu.Regs[15] << 8 | _cpu.Regs[16] )); }
set
{
_cpu.Regs[16] = (ushort)(value & 0xFF);
_cpu.Regs[15] = (ushort)((value >> 8) & 0xFF);
}
}
public ushort RegDE
{
get { return (ushort)((_cpu.Regs[8] << 8 | _cpu.Regs[9] )); }
set
{
_cpu.Regs[9] = (ushort)(value & 0xFF);
_cpu.Regs[8] = (ushort)((value >> 8) & 0xFF);
}
}
public ushort RegAF
{
get { return (ushort)((_cpu.Regs[4] << 8 | _cpu.Regs[5])); }
set
{
_cpu.Regs[5] = (ushort)(value & 0xFF);
_cpu.Regs[4] = (ushort)((value >> 8) & 0xFF);
}
}
/// <summary>
/// Gets the IX word value
/// </summary>
/// <returns></returns>
public ushort Get16BitIX()
{
return Convert.ToUInt16(_cpu.Regs[_cpu.Ixh] | _cpu.Regs[_cpu.Ixl] << 8);
}
/// <summary>
/// Set the IX word value
/// </summary>
/// <param name="Ixh"></param>
/// <param name="Ixl"></param>
public void Set16BitIX(ushort IX)
{
_cpu.Regs[_cpu.Ixh] = (ushort)(IX & 0xFF);
_cpu.Regs[_cpu.Ixl] = (ushort)((IX >> 8) & 0xff);
}
/// <summary>
/// Gets the AF word value
/// </summary>
/// <returns></returns>
public ushort Get16BitAF()
{
return Convert.ToUInt16(_cpu.Regs[_cpu.A] | _cpu.Regs[_cpu.F] << 8);
}
/// <summary>
/// Set the AF word value
/// </summary>
/// <param name="Ixh"></param>
/// <param name="Ixl"></param>
public void Set16BitAF(ushort AF)
{
_cpu.Regs[_cpu.A] = (ushort)(AF & 0xFF);
_cpu.Regs[_cpu.F] = (ushort)((AF >> 8) & 0xff);
}
/// <summary>
/// Gets the AF shadow word value
/// </summary>
/// <returns></returns>
public ushort Get16BitAF_()
{
return Convert.ToUInt16(_cpu.Regs[_cpu.A_s] | _cpu.Regs[_cpu.F_s] << 8);
}
/// <summary>
/// Set the AF shadow word value
/// </summary>
/// <param name="Ixh"></param>
/// <param name="Ixl"></param>
public void Set16BitAF_(ushort AF_)
{
_cpu.Regs[_cpu.A_s] = (ushort)(AF_ & 0xFF);
_cpu.Regs[_cpu.F_s] = (ushort)((AF_ >> 8) & 0xff);
}
/// <summary>
/// Gets the DE word value
/// </summary>
/// <returns></returns>
public ushort Get16BitDE()
{
return Convert.ToUInt16(_cpu.Regs[_cpu.E] | _cpu.Regs[_cpu.D] << 8);
}
/// <summary>
/// Set the DE word value
/// </summary>
/// <param name="Ixh"></param>
/// <param name="Ixl"></param>
public void Set16BitDE(ushort DE)
{
_cpu.Regs[_cpu.D] = (ushort)(DE & 0xFF);
_cpu.Regs[_cpu.E] = (ushort)((DE >> 8) & 0xff);
}
/// <summary>
/// Z80 Status Indicator Flag Reset masks
/// </summary>
/// <seealso cref="FlagsSetMask"/>
[Flags]
public enum FlagsResetMask : byte
{
/// <summary>Sign Flag</summary>
S = 0x7F,
/// <summary>Zero Flag</summary>
Z = 0xBF,
/// <summary>This flag is not used.</summary>
R5 = 0xDF,
/// <summary>Half Carry Flag</summary>
H = 0xEF,
/// <summary>This flag is not used.</summary>
R3 = 0xF7,
/// <summary>Parity/Overflow Flag</summary>
PV = 0xFB,
/// <summary>Add/Subtract Flag</summary>
N = 0xFD,
/// <summary>Carry Flag</summary>
C = 0xFE,
}
/// <summary>
/// Z80 Status Indicator Flag Set masks
/// </summary>
/// <seealso cref="FlagsResetMask"/>
[Flags]
public enum FlagsSetMask : byte
{
/// <summary>Sign Flag</summary>
/// <remarks>
/// The Sign Flag (S) stores the state of the most-significant bit of
/// the Accumulator (bit 7). When the Z80 CPU performs arithmetic
/// operations on signed numbers, the binary twos complement notation
/// is used to represent and process numeric information.
/// </remarks>
S = 0x80,
/// <summary>
/// Zero Flag
/// </summary>
/// <remarks>
/// The Zero Flag is set (1) or cleared (0) if the result generated by
/// the execution of certain instructions is 0. For 8-bit arithmetic and
/// logical operations, the Z flag is set to a 1 if the resulting byte in
/// the Accumulator is 0. If the byte is not 0, the Z flag is reset to 0.
/// </remarks>
Z = 0x40,
/// <summary>This flag is not used.</summary>
R5 = 0x20,
/// <summary>Half Carry Flag</summary>
/// <remarks>
/// The Half Carry Flag (H) is set (1) or cleared (0) depending on the
/// Carry and Borrow status between bits 3 and 4 of an 8-bit arithmetic
/// operation. This flag is used by the Decimal Adjust Accumulator (DAA)
/// instruction to correct the result of a packed BCD add or subtract operation.
/// </remarks>
H = 0x10,
/// <summary>This flag is not used.</summary>
R3 = 0x08,
/// <summary>Parity/Overflow Flag</summary>
/// <remarks>
/// The Parity/Overflow (P/V) Flag is set to a specific state depending on
/// the operation being performed. For arithmetic operations, this flag
/// indicates an overflow condition when the result in the Accumulator is
/// greater than the maximum possible number (+127) or is less than the
/// minimum possible number (128). This overflow condition is determined by
/// examining the sign bits of the operands.
/// </remarks>
PV = 0x04,
/// <summary>Add/Subtract Flag</summary>
/// <remarks>
/// The Add/Subtract Flag (N) is used by the Decimal Adjust Accumulator
/// instruction (DAA) to distinguish between the ADD and SUB instructions.
/// For ADD instructions, N is cleared to 0. For SUB instructions, N is set to 1.
/// </remarks>
N = 0x02,
/// <summary>Carry Flag</summary>
/// <remarks>
/// The Carry Flag (C) is set or cleared depending on the operation being performed.
/// </remarks>
C = 0x01,
/// <summary>
/// Combination of S, Z, and PV
/// </summary>
SZPV = S | Z | PV,
/// <summary>
/// Combination of N, and H
/// </summary>
NH = N | H,
/// <summary>
/// Combination of R3, and R5
/// </summary>
R3R5 = R3 | R5
}
/// <summary>
/// Helper method that returns a single INT32 from a BitArray
/// </summary>
@ -264,5 +26,44 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
bitArray.CopyTo(array, 0);
return array[0];
}
/// <summary>
/// POKEs a memory bus address
/// </summary>
/// <param name="addr"></param>
/// <param name="value"></param>
public void PokeMemory(ushort addr, byte value)
{
_machine.WriteBus(addr, value);
}
public string GetMachineType()
{
string m = "";
switch (SyncSettings.MachineType)
{
case MachineType.ZXSpectrum16:
m = "(Sinclair) ZX Spectrum 16K";
break;
case MachineType.ZXSpectrum48:
m = "(Sinclair) ZX Spectrum 48K";
break;
case MachineType.ZXSpectrum128:
m = "(Sinclair) ZX Spectrum 128K";
break;
case MachineType.ZXSpectrum128Plus2:
m = "(Amstrad) ZX Spectrum 128K +2";
break;
case MachineType.ZXSpectrum128Plus2a:
m = "(Amstrad) ZX Spectrum 128K +2a";
break;
case MachineType.ZXSpectrum128Plus3:
m = "(Amstrad) ZX Spectrum 128K +3";
break;
}
return m;
}
}
}

View File

@ -91,6 +91,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
_cpu.ReadHardware = _machine.ReadPort;
_cpu.WriteHardware = _machine.WritePort;
_cpu.FetchDB = _machine.PushBus;
_cpu.OnExecFetch = _machine.CPUMon.OnExecFetch;
ser.Register<ITraceable>(_tracer);
ser.Register<IDisassemblable>(_cpu);