CPCHawk: Start of new CRCT and Gatearray implementations
This commit is contained in:
parent
a7e0e728a7
commit
95e565c545
|
@ -686,8 +686,8 @@ namespace BizHawk.Client.Common
|
|||
nextComm,
|
||||
xmlGame.Assets.Select(a => a.Value), //.First(),
|
||||
cpcGI, // GameInfo.NullInstance,
|
||||
(AmstradCPC.AmstradCPCSettings)GetCoreSettings<ZXSpectrum>(),
|
||||
(AmstradCPC.AmstradCPCSyncSettings)GetCoreSyncSettings<ZXSpectrum>());
|
||||
(AmstradCPC.AmstradCPCSettings)GetCoreSettings<AmstradCPC>(),
|
||||
(AmstradCPC.AmstradCPCSyncSettings)GetCoreSyncSettings<AmstradCPC>());
|
||||
break;
|
||||
case "PSX":
|
||||
var entries = xmlGame.AssetFullPaths;
|
||||
|
|
|
@ -38,12 +38,15 @@
|
|||
this.determEmucheckBox1 = new System.Windows.Forms.CheckBox();
|
||||
this.lblAutoLoadText = new System.Windows.Forms.Label();
|
||||
this.autoLoadcheckBox1 = new System.Windows.Forms.CheckBox();
|
||||
this.lblBorderInfo = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.borderTypecomboBox1 = new System.Windows.Forms.ComboBox();
|
||||
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(247, 375);
|
||||
this.OkBtn.Location = new System.Drawing.Point(249, 432);
|
||||
this.OkBtn.Name = "OkBtn";
|
||||
this.OkBtn.Size = new System.Drawing.Size(60, 23);
|
||||
this.OkBtn.TabIndex = 3;
|
||||
|
@ -55,7 +58,7 @@
|
|||
//
|
||||
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(313, 375);
|
||||
this.CancelBtn.Location = new System.Drawing.Point(315, 432);
|
||||
this.CancelBtn.Name = "CancelBtn";
|
||||
this.CancelBtn.Size = new System.Drawing.Size(60, 23);
|
||||
this.CancelBtn.TabIndex = 4;
|
||||
|
@ -80,7 +83,7 @@
|
|||
this.MachineSelectionComboBox.FormattingEnabled = true;
|
||||
this.MachineSelectionComboBox.Location = new System.Drawing.Point(12, 62);
|
||||
this.MachineSelectionComboBox.Name = "MachineSelectionComboBox";
|
||||
this.MachineSelectionComboBox.Size = new System.Drawing.Size(361, 21);
|
||||
this.MachineSelectionComboBox.Size = new System.Drawing.Size(363, 21);
|
||||
this.MachineSelectionComboBox.TabIndex = 13;
|
||||
this.MachineSelectionComboBox.SelectionChangeCommitted += new System.EventHandler(this.MachineSelectionComboBox_SelectionChangeCommitted);
|
||||
//
|
||||
|
@ -105,7 +108,7 @@
|
|||
// determEmucheckBox1
|
||||
//
|
||||
this.determEmucheckBox1.AutoSize = true;
|
||||
this.determEmucheckBox1.Location = new System.Drawing.Point(15, 302);
|
||||
this.determEmucheckBox1.Location = new System.Drawing.Point(12, 373);
|
||||
this.determEmucheckBox1.Name = "determEmucheckBox1";
|
||||
this.determEmucheckBox1.Size = new System.Drawing.Size(135, 17);
|
||||
this.determEmucheckBox1.TabIndex = 21;
|
||||
|
@ -115,7 +118,7 @@
|
|||
// lblAutoLoadText
|
||||
//
|
||||
this.lblAutoLoadText.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.lblAutoLoadText.Location = new System.Drawing.Point(175, 319);
|
||||
this.lblAutoLoadText.Location = new System.Drawing.Point(172, 390);
|
||||
this.lblAutoLoadText.Name = "lblAutoLoadText";
|
||||
this.lblAutoLoadText.Size = new System.Drawing.Size(196, 30);
|
||||
this.lblAutoLoadText.TabIndex = 27;
|
||||
|
@ -126,20 +129,53 @@
|
|||
// autoLoadcheckBox1
|
||||
//
|
||||
this.autoLoadcheckBox1.AutoSize = true;
|
||||
this.autoLoadcheckBox1.Location = new System.Drawing.Point(15, 325);
|
||||
this.autoLoadcheckBox1.Location = new System.Drawing.Point(12, 396);
|
||||
this.autoLoadcheckBox1.Name = "autoLoadcheckBox1";
|
||||
this.autoLoadcheckBox1.Size = new System.Drawing.Size(128, 17);
|
||||
this.autoLoadcheckBox1.TabIndex = 26;
|
||||
this.autoLoadcheckBox1.Text = "Auto Tape Start/Stop";
|
||||
this.autoLoadcheckBox1.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// lblBorderInfo
|
||||
//
|
||||
this.lblBorderInfo.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.lblBorderInfo.Location = new System.Drawing.Point(175, 331);
|
||||
this.lblBorderInfo.Name = "lblBorderInfo";
|
||||
this.lblBorderInfo.Size = new System.Drawing.Size(196, 21);
|
||||
this.lblBorderInfo.TabIndex = 30;
|
||||
this.lblBorderInfo.Text = "null";
|
||||
this.lblBorderInfo.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(12, 315);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(118, 13);
|
||||
this.label2.TabIndex = 29;
|
||||
this.label2.Text = "Rendered Border Type:";
|
||||
//
|
||||
// borderTypecomboBox1
|
||||
//
|
||||
this.borderTypecomboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.borderTypecomboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.borderTypecomboBox1.FormattingEnabled = true;
|
||||
this.borderTypecomboBox1.Location = new System.Drawing.Point(12, 331);
|
||||
this.borderTypecomboBox1.Name = "borderTypecomboBox1";
|
||||
this.borderTypecomboBox1.Size = new System.Drawing.Size(159, 21);
|
||||
this.borderTypecomboBox1.TabIndex = 28;
|
||||
//
|
||||
// AmstradCPCCoreEmulationSettings
|
||||
//
|
||||
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(385, 410);
|
||||
this.ClientSize = new System.Drawing.Size(387, 467);
|
||||
this.Controls.Add(this.lblBorderInfo);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.borderTypecomboBox1);
|
||||
this.Controls.Add(this.lblAutoLoadText);
|
||||
this.Controls.Add(this.autoLoadcheckBox1);
|
||||
this.Controls.Add(this.determEmucheckBox1);
|
||||
|
@ -170,5 +206,8 @@
|
|||
private System.Windows.Forms.CheckBox determEmucheckBox1;
|
||||
private System.Windows.Forms.Label lblAutoLoadText;
|
||||
private System.Windows.Forms.CheckBox autoLoadcheckBox1;
|
||||
private System.Windows.Forms.Label lblBorderInfo;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.ComboBox borderTypecomboBox1;
|
||||
}
|
||||
}
|
|
@ -31,6 +31,15 @@ namespace BizHawk.Client.EmuHawk
|
|||
MachineSelectionComboBox.SelectedItem = _syncSettings.MachineType.ToString();
|
||||
UpdateMachineNotes((MachineType)Enum.Parse(typeof(MachineType), MachineSelectionComboBox.SelectedItem.ToString()));
|
||||
|
||||
// border selecton
|
||||
var borderTypes = Enum.GetNames(typeof(AmstradCPC.BorderType));
|
||||
foreach (var val in borderTypes)
|
||||
{
|
||||
borderTypecomboBox1.Items.Add(val);
|
||||
}
|
||||
borderTypecomboBox1.SelectedItem = _syncSettings.BorderType.ToString();
|
||||
UpdateBorderNotes((AmstradCPC.BorderType)Enum.Parse(typeof(AmstradCPC.BorderType), borderTypecomboBox1.SelectedItem.ToString()));
|
||||
|
||||
// deterministic emulation
|
||||
determEmucheckBox1.Checked = _syncSettings.DeterministicEmulation;
|
||||
|
||||
|
@ -42,12 +51,14 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
bool changed =
|
||||
_syncSettings.MachineType.ToString() != MachineSelectionComboBox.SelectedItem.ToString()
|
||||
|| _syncSettings.BorderType.ToString() != borderTypecomboBox1.SelectedItem.ToString()
|
||||
|| _syncSettings.DeterministicEmulation != determEmucheckBox1.Checked
|
||||
|| _syncSettings.AutoStartStopTape != autoLoadcheckBox1.Checked;
|
||||
|
||||
if (changed)
|
||||
{
|
||||
_syncSettings.MachineType = (MachineType)Enum.Parse(typeof(MachineType), MachineSelectionComboBox.SelectedItem.ToString());
|
||||
_syncSettings.BorderType = (AmstradCPC.BorderType)Enum.Parse(typeof(AmstradCPC.BorderType), borderTypecomboBox1.SelectedItem.ToString());
|
||||
_syncSettings.DeterministicEmulation = determEmucheckBox1.Checked;
|
||||
_syncSettings.AutoStartStopTape = autoLoadcheckBox1.Checked;
|
||||
|
||||
|
@ -80,5 +91,28 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
lblMachineNotes.Text = CPCMachineMetaData.GetMetaString(type);
|
||||
}
|
||||
|
||||
private void borderTypecomboBox1_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
ComboBox cb = sender as ComboBox;
|
||||
UpdateBorderNotes((AmstradCPC.BorderType)Enum.Parse(typeof(AmstradCPC.BorderType), cb.SelectedItem.ToString()));
|
||||
}
|
||||
|
||||
private void UpdateBorderNotes(AmstradCPC.BorderType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case AmstradCPC.BorderType.Uniform:
|
||||
lblBorderInfo.Text = "Attempts to equalise the border areas";
|
||||
break;
|
||||
case AmstradCPC.BorderType.Uncropped:
|
||||
lblBorderInfo.Text = "Pretty much the signal the gate array is generating (looks pants)";
|
||||
break;
|
||||
|
||||
case AmstradCPC.BorderType.Widescreen:
|
||||
lblBorderInfo.Text = "Top and bottom border removed so that the result is *almost* 16:9";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,6 +140,7 @@
|
|||
<Compile Include="Computers\AmstradCPC\Hardware\Abstraction\IKeyboard.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Abstraction\IPortIODevice.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Abstraction\IPSG.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Display\CRCTChip.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Display\CRCT_6845.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Datacorder\DatacorderDevice.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Disk\CHRN.cs" />
|
||||
|
@ -171,6 +172,7 @@
|
|||
<Compile Include="Computers\AmstradCPC\Hardware\PPI\PPI_8255.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Media\Disk\CPCExtendedFloppyDisk.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Media\Disk\CPCFloppyDisk.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Media\Disk\DiskHandler.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Media\Disk\DiskType.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Media\Disk\FloppyDisk.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Media\MediaConverter.cs" />
|
||||
|
|
|
@ -101,6 +101,11 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
[DefaultValue(true)]
|
||||
public bool AutoStartStopTape { get; set; }
|
||||
|
||||
[DisplayName("Border type")]
|
||||
[Description("Select how to show the border area")]
|
||||
[DefaultValue(BorderType.Uniform)]
|
||||
public BorderType BorderType { get; set; }
|
||||
|
||||
public AmstradCPCSyncSettings Clone()
|
||||
{
|
||||
return (AmstradCPCSyncSettings)MemberwiseClone();
|
||||
|
@ -299,5 +304,26 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The size of the Spectrum border
|
||||
/// </summary>
|
||||
public enum BorderType
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to equalise the border areas
|
||||
/// </summary>
|
||||
Uniform,
|
||||
|
||||
/// <summary>
|
||||
/// Pretty much the signal the gate array is generating (looks shit)
|
||||
/// </summary>
|
||||
Uncropped,
|
||||
|
||||
/// <summary>
|
||||
/// Top and bottom border removed so that the result is *almost* 16:9
|
||||
/// </summary>
|
||||
Widescreen,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,11 +46,12 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
{
|
||||
case MachineType.CPC464:
|
||||
ControllerDefinition = AmstradCPCControllerDefinition;
|
||||
Init(MachineType.CPC464, _files, ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).AutoStartStopTape);
|
||||
Init(MachineType.CPC464, _files, ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).AutoStartStopTape,
|
||||
((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).BorderType);
|
||||
break;
|
||||
case MachineType.CPC6128:
|
||||
ControllerDefinition = AmstradCPCControllerDefinition;
|
||||
Init(MachineType.CPC6128, _files, ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).AutoStartStopTape);
|
||||
Init(MachineType.CPC6128, _files, ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).AutoStartStopTape, ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).BorderType);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException("Machine not yet emulated");
|
||||
|
@ -72,7 +73,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
ser.Register<ITraceable>(_tracer);
|
||||
ser.Register<IDisassemblable>(_cpu);
|
||||
ser.Register<IVideoProvider>(_machine.CRT);
|
||||
ser.Register<IVideoProvider>(_machine.GateArray);
|
||||
|
||||
// initialize sound mixer and attach the various ISoundProvider devices
|
||||
SoundMixer = new SoundProviderMixer((int)(32767 / 10), "Tape Audio", (ISoundProvider)_machine.TapeBuzzer);
|
||||
|
@ -158,7 +159,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
private MachineType _machineType;
|
||||
|
||||
private void Init(MachineType machineType, List<byte[]> files, bool autoTape)
|
||||
private void Init(MachineType machineType, List<byte[]> files, bool autoTape, BorderType bType)
|
||||
{
|
||||
_machineType = machineType;
|
||||
|
||||
|
@ -166,7 +167,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
switch (machineType)
|
||||
{
|
||||
case MachineType.CPC464:
|
||||
_machine = new CPC464(this, _cpu, files, autoTape);
|
||||
_machine = new CPC464(this, _cpu, files, autoTape, bType);
|
||||
List<RomData> roms64 = new List<RomData>();
|
||||
roms64.Add(RomData.InitROM(MachineType.CPC464, GetFirmware(0x4000, "OS464ROM"), RomData.ROMChipType.Lower));
|
||||
roms64.Add(RomData.InitROM(MachineType.CPC464, GetFirmware(0x4000, "BASIC1-0ROM"), RomData.ROMChipType.Upper, 0));
|
||||
|
@ -174,7 +175,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
break;
|
||||
|
||||
case MachineType.CPC6128:
|
||||
_machine = new CPC6128(this, _cpu, files, autoTape);
|
||||
_machine = new CPC6128(this, _cpu, files, autoTape, bType);
|
||||
List<RomData> roms128 = new List<RomData>();
|
||||
roms128.Add(RomData.InitROM(MachineType.CPC6128, GetFirmware(0x4000, "OS6128ROM"), RomData.ROMChipType.Lower));
|
||||
roms128.Add(RomData.InitROM(MachineType.CPC6128, GetFirmware(0x4000, "BASIC1-1ROM"), RomData.ROMChipType.Upper, 0));
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,832 @@
|
|||
using BizHawk.Common;
|
||||
using BizHawk.Common.NumberExtensions;
|
||||
using System.Collections;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||
{
|
||||
/// <summary>
|
||||
/// CRT CONTROLLER (CRTC)
|
||||
/// http://archive.pcjs.org/pubs/pc/datasheets/MC6845-CRT.pdf
|
||||
/// http://www.cpcwiki.eu/imgs/c/c0/Hd6845.hitachi.pdf
|
||||
/// http://www.cpcwiki.eu/imgs/1/13/Um6845.umc.pdf
|
||||
/// http://www.cpcwiki.eu/imgs/b/b5/Um6845r.umc.pdf
|
||||
/// http://www.cpcwiki.eu/index.php/CRTC
|
||||
/// </summary>
|
||||
public class CRCTChip
|
||||
{
|
||||
#region Devices
|
||||
|
||||
private CPCBase _machine { get; set; }
|
||||
private CRCTType ChipType;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
public CRCTChip(CRCTType chipType, CPCBase machine)
|
||||
{
|
||||
_machine = machine;
|
||||
ChipType = chipType;
|
||||
//Reset();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Output Lines
|
||||
|
||||
// State output lines
|
||||
/// <summary>
|
||||
/// This TTL compatible output is an active high signal which drives the monitor directly or is fed to Video Processing Logic for composite generation.
|
||||
/// This signal determines the vertical position of the displayed text.
|
||||
/// </summary>
|
||||
public bool VSYNC { get { return _VSYNC; } }
|
||||
/// <summary>
|
||||
/// This TTL compatible output is an active high signal which drives the monitor directly or is fed to Video Processing Logic for composite generation.
|
||||
/// This signal determines the horizontal position of the displayed text.
|
||||
/// </summary>
|
||||
public bool HSYNC { get { return _HSYNC; } }
|
||||
/// <summary>
|
||||
/// This TTL compatible output is an active high signal which indicates the CRTC is providing addressing in the active Display Area.
|
||||
/// </summary>
|
||||
public bool DisplayEnable { get { return DisplayEnable; } }
|
||||
/// <summary>
|
||||
/// This TTL compatible output indicates Cursor Display to external Video Processing Logic.Active high signal.
|
||||
/// </summary>
|
||||
public bool Cursor { get { return _Cursor; } }
|
||||
|
||||
private bool _VSYNC;
|
||||
private bool _HSYNC;
|
||||
private bool _DisplayEnable;
|
||||
private bool _Cursor;
|
||||
|
||||
// Refresh memory addresses
|
||||
/*
|
||||
Refresh Memory Addresses (MAO-MA13) -These 14 outputs are used to refresh the CRT screen with pages of
|
||||
data located within a 16K block of refresh memory. These outputs drive a TTL load and 30pF. A high level on
|
||||
MAO-MA 13 is a logical "1."
|
||||
*/
|
||||
public bool MA0 { get { return LinearAddress.Bit(0); } }
|
||||
public bool MA1 { get { return LinearAddress.Bit(1); } }
|
||||
public bool MA2 { get { return LinearAddress.Bit(2); } }
|
||||
public bool MA3 { get { return LinearAddress.Bit(3); } }
|
||||
public bool MA4 { get { return LinearAddress.Bit(4); } }
|
||||
public bool MA5 { get { return LinearAddress.Bit(5); } }
|
||||
public bool MA6 { get { return LinearAddress.Bit(6); } }
|
||||
public bool MA7 { get { return LinearAddress.Bit(7); } }
|
||||
public bool MA8 { get { return LinearAddress.Bit(8); } }
|
||||
public bool MA9 { get { return LinearAddress.Bit(9); } }
|
||||
public bool MA10 { get { return LinearAddress.Bit(10); } } // cpcwiki would suggest that this isnt connected in the CPC range
|
||||
public bool MA11 { get { return LinearAddress.Bit(11); } } // cpcwiki would suggest that this isnt connected in the CPC range
|
||||
public bool MA12 { get { return LinearAddress.Bit(12); } } // cpcwiki would suggest that this is connected in the CPC range but not used
|
||||
public bool MA13 { get { return LinearAddress.Bit(13); } } // cpcwiki would suggest that this is connected in the CPC range but not used
|
||||
|
||||
// Row addresses for character generators
|
||||
/*
|
||||
Raster Addresses (RAO-RA4) - These 5 outputs from the internal Raster Counter address the Character ROM
|
||||
for the row of a character. These outputs drive a TTL load and 30pF. A high level (on RAO-RA4) is a logical "1."
|
||||
*/
|
||||
public bool RA0 { get { return ScanLineCTR.Bit(0); } }
|
||||
public bool RA1 { get { return ScanLineCTR.Bit(1); } }
|
||||
public bool RA2 { get { return ScanLineCTR.Bit(2); } }
|
||||
public bool RA3 { get { return ScanLineCTR.Bit(3); } } // cpcwiki would suggest that this isnt connected in the CPC range
|
||||
public bool RA4 { get { return ScanLineCTR.Bit(4); } } // cpcwiki would suggest that this isnt connected in the CPC range
|
||||
|
||||
/// <summary>
|
||||
/// Built from R12, R13 and CLK
|
||||
/// This is a logical emulator output and is how the CPC gatearray would translate the lines
|
||||
/*
|
||||
Memory Address Signal Signal source Signal name
|
||||
A15 6845 MA13
|
||||
A14 6845 MA12
|
||||
A13 6845 RA2
|
||||
A12 6845 RA1
|
||||
A11 6845 RA0
|
||||
A10 6845 MA9
|
||||
A9 6845 MA8
|
||||
A8 6845 MA7
|
||||
A7 6845 MA6
|
||||
A6 6845 MA5
|
||||
A5 6845 MA4
|
||||
A4 6845 MA3
|
||||
A3 6845 MA2
|
||||
A2 6845 MA1
|
||||
A1 6845 MA0
|
||||
A0 Gate-Array CLK
|
||||
|
||||
*/
|
||||
/// </summary>
|
||||
public ushort AddressLine
|
||||
{
|
||||
get
|
||||
{
|
||||
BitArray MA = new BitArray(16);
|
||||
MA[0] = _CLK;
|
||||
MA[1] = MA0;
|
||||
MA[2] = MA1;
|
||||
MA[3] = MA2;
|
||||
MA[4] = MA3;
|
||||
MA[5] = MA4;
|
||||
MA[6] = MA5;
|
||||
MA[7] = MA6;
|
||||
MA[8] = MA7;
|
||||
MA[9] = MA8;
|
||||
MA[10] = MA9;
|
||||
MA[11] = RA0;
|
||||
MA[12] = RA1;
|
||||
MA[13] = RA2;
|
||||
MA[14] = MA12;
|
||||
MA[15] = MA13;
|
||||
ushort[] array = new ushort[1];
|
||||
MA.CopyTo(array, 0);
|
||||
return array[0];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Input Lines
|
||||
|
||||
/// <summary>
|
||||
/// This TTL compatible output indicates Cursor Display to external Video Processing Logic.Active high signal.
|
||||
/// </summary>
|
||||
public bool CLK { get { return _CLK; } }
|
||||
/// <summary>
|
||||
/// The RES input is used to Reset the CRTC. An input low level on RES forces CRTC into following status:
|
||||
/// (A) All the counters in CRTC are cleared and the device stops the display operation.
|
||||
/// (C) Control registers in CRTC are not affected and remain unchanged.
|
||||
/// This signal is different from other M6800 family in the following functions:
|
||||
/// (A) RES signal has capability of reset function only. when LPSTB is at low level.
|
||||
/// (B) After RES has gone down to low level, output s ignals of MAO -MA13 and RAO - RA4, synchronizing with CLK low level, goes down to low level.
|
||||
/// (At least 1 cycle CLK signal is necessary for reset.)
|
||||
/// (C) The CRTC starts the Display operation immediately after the release of RES signal.
|
||||
/// </summary>
|
||||
public bool RESET { get { return _RESET; } }
|
||||
/// <summary>
|
||||
/// Light Pen Strobe (LPSTR) - This high impedance TTLIMOS compatible input latches the cu rrent Refresh Addresses in the Register File.
|
||||
/// Latching is on the low to high edge and is synchronized internally to character clock.
|
||||
/// </summary>
|
||||
public bool LPSTB { get { return _LPSTB; } }
|
||||
|
||||
private bool _CLK;
|
||||
private bool _RESET;
|
||||
private bool _LPSTB;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Registers
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected register
|
||||
/// </summary>
|
||||
private byte AddressRegister;
|
||||
|
||||
/// <summary>
|
||||
/// The internal register
|
||||
/// The Address Register is a 5 bit write-only register used as an "indirect" or "pointer" register.
|
||||
/// Its contents are the address of one of the other 18 registers in the file.When RS and CS are low,
|
||||
/// the Address Register itself is addressed.When RS is high, the Register File is accessed.
|
||||
/// </summary>
|
||||
private byte[] Register = new byte[18];
|
||||
|
||||
// Horizontal timing register constants
|
||||
/// <summary>
|
||||
/// This 8 bit write-only register determines the horizontal frequency of HS.
|
||||
/// It is the total of displayed plus non-displayed character time units minus one.
|
||||
/// </summary>
|
||||
private const int H_TOTAL = 0;
|
||||
/// <summary>
|
||||
/// This 8 bit write-only register determines the number of displayed characters per horizontal line.
|
||||
/// </summary>
|
||||
private const int H_DISPLAYED = 1;
|
||||
/// <summary>
|
||||
/// This 8 bit write-only register determines the horizontal sync postiion on the horizontal line.
|
||||
/// </summary>
|
||||
private const int H_SYNC_POS = 2;
|
||||
/// <summary>
|
||||
/// This 4 bit write-only register determines the width of the HS pulse. It may not be apparent why this width needs to be programmed.However,
|
||||
/// consider that all timing widths must be programmed as multiples of the character clock period which varies.If HS width were fixed as an integral
|
||||
/// number of character times, it would vary with character rate and be out of tolerance for certain monitors.
|
||||
/// The rate programmable feature allows compensating HS width.
|
||||
/// NOTE: Dependent on chiptype this also may include VSYNC width - check the UpdateWidths() method
|
||||
/// </summary>
|
||||
private const int SYNC_WIDTHS = 3;
|
||||
|
||||
// Vertical timing register constants
|
||||
/// <summary>
|
||||
/// The vertical frequency of VS is determined by both R4 and R5.The calculated number of character I ine times is usual I y an integer plus a fraction to
|
||||
/// get exactly a 50 or 60Hz vertical refresh rate. The integer number of character line times minus one is programmed in the 7 bit write-only Vertical Total Register;
|
||||
/// the fraction is programmed in the 5 bit write-only Vertical Scan Adjust Register as a number of scan line times.
|
||||
/// </summary>
|
||||
private const int V_TOTAL = 4;
|
||||
private const int V_TOTAL_ADJUST = 5;
|
||||
/// <summary>
|
||||
/// This 7 bit write-only register determines the number of displayed character rows on the CRT screen, and is programmed in character row times.
|
||||
/// </summary>
|
||||
private const int V_DISPLAYED = 6;
|
||||
/// <summary>
|
||||
/// This 7 bit write-only register determines the vertical sync position with respect to the reference.It is programmed in character row times.
|
||||
/// </summary>
|
||||
private const int V_SYNC_POS = 7;
|
||||
/// <summary>
|
||||
/// This 2 bit write-only register controls the raster scan mode(see Figure 11 ). When bit 0 and bit 1 are reset, or bit 0 is reset and bit 1 set,
|
||||
/// the non· interlace raster scan mode is selected.Two interlace modes are available.Both are interlaced 2 fields per frame.When bit 0 is set and bit 1 is reset,
|
||||
/// the interlace sync raster scan mode is selected.Also when bit 0 and bit 1 are set, the interlace sync and video raster scan mode is selected.
|
||||
/// </summary>
|
||||
private const int INTERLACE_MODE = 8;
|
||||
/// <summary>
|
||||
/// This 5 bit write·only register determines the number of scan lines per character row including spacing.
|
||||
/// The programmed value is a max address and is one less than the number of scan l1nes.
|
||||
/// </summary>
|
||||
private const int MAX_SL_ADDRESS = 9;
|
||||
|
||||
// Other register constants
|
||||
/// <summary>
|
||||
/// This 7 bit write-only register controls the cursor format(see Figure 10). Bit 5 is the blink timing control.When bit 5 is low, the blink frequency is 1/16 of the
|
||||
/// vertical field rate, and when bit 5 is high, the blink frequency is 1/32 of the vertical field rate.Bit 6 is used to enable a blink.
|
||||
/// The cursor start scan line is set by the lower 5 bits.
|
||||
/// </summary>
|
||||
private const int CURSOR_START = 10;
|
||||
/// <summary>
|
||||
/// This 5 bit write-only register sets the cursor end scan line
|
||||
/// </summary>
|
||||
private const int CURSOR_END = 11;
|
||||
/// <summary>
|
||||
/// Start Address Register is a 14 bit write-only register which determines the first address put out as a refresh address after vertical blanking.
|
||||
/// It consists of an 8 bit lower register, and a 6 bit higher register.
|
||||
/// </summary>
|
||||
private const int START_ADDR_H = 12;
|
||||
private const int START_ADDR_L = 13;
|
||||
/// <summary>
|
||||
/// This 14 bit read/write register stores the cursor location.This register consists of an 8 bit lower and 6 bit higher register.
|
||||
/// </summary>
|
||||
private const int CURSOR_H = 14;
|
||||
private const int CURSOR_L = 15;
|
||||
/// <summary>
|
||||
/// This 14 bit read -only register is used to store the contents of the Address Register(H & L) when the LPSTB input pulses high.
|
||||
/// This register consists of an 8 bit lower and 6 bit higher register.
|
||||
/// </summary>
|
||||
private const int LIGHT_PEN_H = 16;
|
||||
private const int LIGHT_PEN_L = 17;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Fields & Properties
|
||||
|
||||
/// <summary>
|
||||
/// Calculated when set based on R3
|
||||
/// </summary>
|
||||
private int HSYNCWidth;
|
||||
/// <summary>
|
||||
/// Calculated when set based on R3
|
||||
/// </summary>
|
||||
private int VSYNCWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Character pos address (0 index).
|
||||
/// Feeds the MA lines
|
||||
/// </summary>
|
||||
private int LinearAddress;
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected Interlace Mode (based on R8)
|
||||
/// </summary>
|
||||
private InterlaceMode CurrentInterlaceMode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Register[INTERLACE_MODE].Bit(0))
|
||||
{
|
||||
return InterlaceMode.NormalSyncMode;
|
||||
}
|
||||
else if (Register[INTERLACE_MODE].Bit(0))
|
||||
{
|
||||
if (Register[INTERLACE_MODE].Bit(1))
|
||||
{
|
||||
return InterlaceMode.InterlaceSyncAndVideoMode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return InterlaceMode.InterlaceSyncMode;
|
||||
}
|
||||
}
|
||||
|
||||
return InterlaceMode.NormalSyncMode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current cursor display mode (based on R14 & R15)
|
||||
/// </summary>
|
||||
private CursorControl CurrentCursorMode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Register[CURSOR_START].Bit(6) && !Register[CURSOR_START].Bit(5))
|
||||
{
|
||||
return CursorControl.NonBlink;
|
||||
}
|
||||
else if (!Register[CURSOR_START].Bit(6) && Register[CURSOR_START].Bit(5))
|
||||
{
|
||||
return CursorControl.CursorNonDisplay;
|
||||
}
|
||||
else if (Register[CURSOR_START].Bit(6) && !Register[CURSOR_START].Bit(5))
|
||||
{
|
||||
return CursorControl.Blink1_16;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CursorControl.Blink1_32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Counter microchips
|
||||
private int HorizontalCTR { get { return _HorizontalCTR; }
|
||||
set
|
||||
{
|
||||
if (value > 255)
|
||||
_HorizontalCTR = value - 255;
|
||||
}
|
||||
}
|
||||
|
||||
private int HorizontalSyncWidthCTR
|
||||
{
|
||||
get { return _HorizontalSyncWidthCTR; }
|
||||
set
|
||||
{
|
||||
if (value > 15)
|
||||
_HorizontalSyncWidthCTR = value - 15;
|
||||
}
|
||||
}
|
||||
|
||||
private int CharacterRowCTR
|
||||
{
|
||||
get { return CharacterRowCTR; }
|
||||
set
|
||||
{
|
||||
if (value > 127)
|
||||
_CharacterRowCTR = value - 127;
|
||||
}
|
||||
}
|
||||
|
||||
private int ScanLineCTR
|
||||
{
|
||||
get { return ScanLineCTR; }
|
||||
set
|
||||
{
|
||||
if (value > 31)
|
||||
_ScanLineCTR = value - 31;
|
||||
}
|
||||
}
|
||||
|
||||
private int _HorizontalCTR;
|
||||
private int _HorizontalSyncWidthCTR;
|
||||
private int _CharacterRowCTR;
|
||||
private int _ScanLineCTR;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Databus Interface
|
||||
/*
|
||||
RegIdx Register Name Type
|
||||
0 1 2 3 4
|
||||
0 Horizontal Total Write Only Write Only Write Only (note 2) (note 3)
|
||||
1 Horizontal Displayed Write Only Write Only Write Only (note 2) (note 3)
|
||||
2 Horizontal Sync Position Write Only Write Only Write Only (note 2) (note 3)
|
||||
3 H and V Sync Widths Write Only Write Only Write Only (note 2) (note 3)
|
||||
4 Vertical Total Write Only Write Only Write Only (note 2) (note 3)
|
||||
5 Vertical Total Adjust Write Only Write Only Write Only (note 2) (note 3)
|
||||
6 Vertical Displayed Write Only Write Only Write Only (note 2) (note 3)
|
||||
7 Vertical Sync position Write Only Write Only Write Only (note 2) (note 3)
|
||||
8 Interlace and Skew Write Only Write Only Write Only (note 2) (note 3)
|
||||
9 Maximum Raster Address Write Only Write Only Write Only (note 2) (note 3)
|
||||
10 Cursor Start Raster Write Only Write Only Write Only (note 2) (note 3)
|
||||
11 Cursor End Raster Write Only Write Only Write Only (note 2) (note 3)
|
||||
12 Disp. Start Address (High) Read/Write Write Only Write Only Read/Write (note 2) (note 3)
|
||||
13 Disp. Start Address (Low) Read/Write Write Only Write Only Read/Write (note 2) (note 3)
|
||||
14 Cursor Address (High) Read/Write Read/Write Read/Write Read/Write (note 2) (note 3)
|
||||
15 Cursor Address (Low) Read/Write Read/Write Read/Write Read/Write (note 2) (note 3)
|
||||
16 Light Pen Address (High) Read Only Read Only Read Only Read Only (note 2) (note 3)
|
||||
17 Light Pen Address (Low) Read Only Read Only Read Only Read Only (note 2) (note 3)
|
||||
|
||||
1. On type 0 and 1, if a Write Only register is read from, "0" is returned.
|
||||
2. See the document "Extra CPC Plus Hardware Information" for more details.
|
||||
3. CRTC type 4 is the same as CRTC type 3. The registers also repeat as they do on the type 3.
|
||||
*/
|
||||
|
||||
/* CPC:
|
||||
#BCXX %x0xxxx00 xxxxxxxx 6845 CRTC Index - Write
|
||||
#BDXX %x0xxxx01 xxxxxxxx 6845 CRTC Data Out - Write
|
||||
#BEXX %x0xxxx10 xxxxxxxx 6845 CRTC Status (as far as supported) Read -
|
||||
#BFXX %x0xxxx11 xxxxxxxx 6845 CRTC Data In (as far as supported) Read -
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// CPU (or other device) reads from the 8-bit databus
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="result"></param>
|
||||
public bool ReadPort(ushort port, ref int result)
|
||||
{
|
||||
byte portUpper = (byte)(port >> 8);
|
||||
byte portLower = (byte)(port & 0xff);
|
||||
|
||||
bool accessed = false;
|
||||
|
||||
// The 6845 is selected when bit 14 of the I/O port address is set to "0"
|
||||
if (portUpper.Bit(6))
|
||||
return accessed;
|
||||
|
||||
// Bit 9 and 8 of the I/O port address define the function to access
|
||||
if (portUpper.Bit(1) && !portUpper.Bit(0))
|
||||
{
|
||||
// read status register
|
||||
accessed = ReadStatus(ref result);
|
||||
}
|
||||
else if ((portUpper & 3) == 3)
|
||||
{
|
||||
// read data register
|
||||
accessed = ReadRegister(ref result);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return accessed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CPU (or other device) writes to the 8-bit databus
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="result"></param>
|
||||
public bool WritePort(ushort port, int value)
|
||||
{
|
||||
byte portUpper = (byte)(port >> 8);
|
||||
byte portLower = (byte)(port & 0xff);
|
||||
|
||||
bool accessed = false;
|
||||
|
||||
// The 6845 is selected when bit 14 of the I/O port address is set to "0"
|
||||
if (portUpper.Bit(6))
|
||||
return accessed;
|
||||
|
||||
var func = portUpper & 3;
|
||||
|
||||
switch (func)
|
||||
{
|
||||
// reg select
|
||||
case 0:
|
||||
SelectRegister(value);
|
||||
break;
|
||||
|
||||
// data write
|
||||
case 1:
|
||||
WriteRegister(value);
|
||||
break;
|
||||
}
|
||||
|
||||
return accessed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal IO Methods
|
||||
|
||||
/// <summary>
|
||||
/// Selects a specific register
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
private void SelectRegister(int value)
|
||||
{
|
||||
var v = (byte)((byte)value & 0x1F);
|
||||
if (v > 0 && v < 18)
|
||||
{
|
||||
AddressRegister = v;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes to the currently latched address register
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
private void WriteRegister(int value)
|
||||
{
|
||||
byte val = (byte)value;
|
||||
|
||||
// lightpen regs are readonly on all models
|
||||
if (AddressRegister == 16 || AddressRegister == 17)
|
||||
return;
|
||||
|
||||
// all other models can be written to
|
||||
switch (AddressRegister)
|
||||
{
|
||||
case H_TOTAL:
|
||||
case H_DISPLAYED:
|
||||
case H_SYNC_POS:
|
||||
case START_ADDR_L:
|
||||
Register[AddressRegister] = val;
|
||||
break;
|
||||
|
||||
case SYNC_WIDTHS:
|
||||
Register[AddressRegister] = val;
|
||||
UpdateWidths();
|
||||
break;
|
||||
|
||||
case V_TOTAL_ADJUST:
|
||||
case CURSOR_END:
|
||||
case MAX_SL_ADDRESS:
|
||||
Register[AddressRegister] = (byte)(val & 0x1F);
|
||||
break;
|
||||
|
||||
case START_ADDR_H:
|
||||
case CURSOR_H:
|
||||
Register[AddressRegister] = (byte)(val & 0x3F);
|
||||
break;
|
||||
|
||||
case V_TOTAL:
|
||||
case V_DISPLAYED:
|
||||
case V_SYNC_POS:
|
||||
case CURSOR_START:
|
||||
Register[AddressRegister] = (byte)(val & 0x7F);
|
||||
break;
|
||||
|
||||
case INTERLACE_MODE:
|
||||
Register[AddressRegister] = (byte)(val & 0x3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads from the currently selected register
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
private bool ReadRegister(ref int data)
|
||||
{
|
||||
bool addressed = false;
|
||||
switch (AddressRegister)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
if ((int)ChipType == 0 || (int)ChipType == 1)
|
||||
{
|
||||
addressed = true;
|
||||
data = 0;
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
addressed = true;
|
||||
if ((int)ChipType == 0)
|
||||
data = Register[AddressRegister];
|
||||
else if ((int)ChipType == 1)
|
||||
data = 0;
|
||||
break;
|
||||
case 14:
|
||||
case 15:
|
||||
case 16:
|
||||
case 17:
|
||||
addressed = true;
|
||||
data = Register[AddressRegister];
|
||||
break;
|
||||
|
||||
default:
|
||||
// registers 18-31 read as 0, on type 0 and 2. registers 18-30 read as 0 on type1, register 31 reads as 0x0ff.
|
||||
if (AddressRegister >= 18 && AddressRegister <= 30)
|
||||
{
|
||||
switch ((int)ChipType)
|
||||
{
|
||||
case 0:
|
||||
case 2:
|
||||
case 1:
|
||||
addressed = true;
|
||||
data = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (AddressRegister == 31)
|
||||
{
|
||||
if ((int)ChipType == 1)
|
||||
{
|
||||
addressed = true;
|
||||
data = 0x0ff;
|
||||
}
|
||||
else if ((int)ChipType == 0 || (int)ChipType == 2)
|
||||
{
|
||||
addressed = true;
|
||||
data = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return addressed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the V and H SYNC widths
|
||||
/// </summary>
|
||||
private void UpdateWidths()
|
||||
{
|
||||
switch (ChipType)
|
||||
{
|
||||
case CRCTType.HD6845S:
|
||||
// Bits 7..4 define Vertical Sync Width. If 0 is programmed this gives 16 lines of VSYNC. Bits 3..0 define Horizontal Sync Width.
|
||||
// If 0 is programmed no HSYNC is generated.
|
||||
HSYNCWidth = (Register[SYNC_WIDTHS] >> 0) & 0x0F;
|
||||
VSYNCWidth = (Register[SYNC_WIDTHS] >> 4) & 0x0F;
|
||||
break;
|
||||
case CRCTType.UM6845R:
|
||||
// Bits 7..4 are ignored. Vertical Sync is fixed at 16 lines. Bits 3..0 define Horizontal Sync Width. If 0 is programmed no HSYNC is generated.
|
||||
HSYNCWidth = (Register[SYNC_WIDTHS] >> 0) & 0x0F;
|
||||
VSYNCWidth = 16;
|
||||
break;
|
||||
case CRCTType.MC6845:
|
||||
// Bits 7..4 are ignored. Vertical Sync is fixed at 16 lines. Bits 3..0 define Horizontal Sync Width. If 0 is programmed this gives a HSYNC width of 16.
|
||||
HSYNCWidth = (Register[SYNC_WIDTHS] >> 0) & 0x0F;
|
||||
if (HSYNCWidth == 0)
|
||||
HSYNCWidth = 16;
|
||||
VSYNCWidth = 16;
|
||||
break;
|
||||
case CRCTType.AMS40489:
|
||||
case CRCTType.AMS40226:
|
||||
// Bits 7..4 define Vertical Sync Width. If 0 is programmed this gives 16 lines of VSYNC.Bits 3..0 define Horizontal Sync Width.
|
||||
// If 0 is programmed this gives a HSYNC width of 16.
|
||||
HSYNCWidth = (Register[SYNC_WIDTHS] >> 0) & 0x0F;
|
||||
VSYNCWidth = (Register[SYNC_WIDTHS] >> 4) & 0x0F;
|
||||
if (HSYNCWidth == 0)
|
||||
HSYNCWidth = 16;
|
||||
if (VSYNCWidth == 0)
|
||||
VSYNCWidth = 16;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads from the status register
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
private bool ReadStatus(ref int data)
|
||||
{
|
||||
bool addressed = false;
|
||||
switch ((int)ChipType)
|
||||
{
|
||||
case 1:
|
||||
// read status
|
||||
//todo!!
|
||||
addressed = true;
|
||||
break;
|
||||
case 0:
|
||||
case 2:
|
||||
// status reg not available
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
// read from internal register instead
|
||||
addressed = ReadRegister(ref data);
|
||||
break;
|
||||
}
|
||||
return addressed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Performs a CRCT clock cycle.
|
||||
/// On CPC this is called at 1Mhz == 1 Character cycle (2 bytes)
|
||||
/// </summary>
|
||||
public void ClockCycle()
|
||||
{
|
||||
// H clock
|
||||
HorizontalCTR++;
|
||||
|
||||
if (HorizontalCTR == Register[H_TOTAL])
|
||||
{
|
||||
// end of current scanline
|
||||
HorizontalCTR = 0;
|
||||
// CRCT starts its scalines at the display area
|
||||
_DisplayEnable = true;
|
||||
|
||||
ScanLineCTR++;
|
||||
|
||||
if (ScanLineCTR > Register[MAX_SL_ADDRESS])
|
||||
{
|
||||
// end of vertical character
|
||||
ScanLineCTR = 0;
|
||||
CharacterRowCTR++;
|
||||
|
||||
if (CharacterRowCTR == Register[V_TOTAL])
|
||||
{
|
||||
// check for vertical adjust
|
||||
if (Register[V_TOTAL_ADJUST] > 0)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// end of CRCT frame
|
||||
CharacterRowCTR = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (HorizontalCTR == Register[H_DISPLAYED] + 1)
|
||||
{
|
||||
// end of display area
|
||||
_DisplayEnable = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Functions
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization
|
||||
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
ser.BeginSection("CRCT");
|
||||
ser.SyncEnum("ChipType", ref ChipType);
|
||||
ser.Sync("_VSYNC", ref _VSYNC);
|
||||
ser.Sync("_HSYNC", ref _HSYNC);
|
||||
ser.Sync("_DisplayEnable", ref _DisplayEnable);
|
||||
ser.Sync("_Cursor", ref _Cursor);
|
||||
ser.Sync("_CLK", ref _CLK);
|
||||
ser.Sync("_RESET", ref _RESET);
|
||||
ser.Sync("_LPSTB", ref _LPSTB);
|
||||
ser.Sync("AddressRegister", ref AddressRegister);
|
||||
ser.Sync("Register", ref Register, false);
|
||||
ser.Sync("HSYNCWidth", ref HSYNCWidth);
|
||||
ser.Sync("VSYNCWidth", ref VSYNCWidth);
|
||||
ser.Sync("_HorizontalCTR", ref _HorizontalCTR);
|
||||
ser.Sync("_HorizontalSyncWidthCTR", ref _HorizontalSyncWidthCTR);
|
||||
ser.Sync("_CharacterRowCTR", ref _CharacterRowCTR);
|
||||
ser.Sync("_ScanLineCTR", ref _ScanLineCTR);
|
||||
ser.EndSection();
|
||||
|
||||
/*
|
||||
|
||||
* */
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enums
|
||||
|
||||
/// <summary>
|
||||
/// The types of CRCT chip found in the CPC range
|
||||
/// </summary>
|
||||
public enum CRCTType
|
||||
{
|
||||
HD6845S = 0,
|
||||
UM6845 = 0,
|
||||
UM6845R = 1,
|
||||
MC6845 = 2,
|
||||
AMS40489 = 3,
|
||||
AMS40226 = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The available interlace modes in the CRCT
|
||||
/// </summary>
|
||||
private enum InterlaceMode
|
||||
{
|
||||
NormalSyncMode,
|
||||
InterlaceSyncMode,
|
||||
InterlaceSyncAndVideoMode
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cursor display modes
|
||||
/// </summary>
|
||||
private enum CursorControl
|
||||
{
|
||||
NonBlink,
|
||||
CursorNonDisplay,
|
||||
Blink1_16,
|
||||
Blink1_32
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -19,16 +19,37 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
#endregion
|
||||
|
||||
#region CallBacks
|
||||
|
||||
public delegate void CallBack();
|
||||
|
||||
private CallBack HSYNC_Callbacks;
|
||||
private CallBack VSYNC_Callbacks;
|
||||
|
||||
public void AttachVSYNCCallback(CallBack vCall)
|
||||
{
|
||||
VSYNC_Callbacks += vCall;
|
||||
}
|
||||
|
||||
public void AttachHSYNCCallback(CallBack hCall)
|
||||
{
|
||||
HSYNC_Callbacks += hCall;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
public CRCT_6845(CRCTType chipType, CPCBase machine)
|
||||
{
|
||||
_machine = machine;
|
||||
ChipType = chipType;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
private const int WRITE = 0;
|
||||
private const int READ = 1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Lines
|
||||
|
@ -55,15 +76,25 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
/// </summary>
|
||||
public short MA;
|
||||
|
||||
/// <summary>
|
||||
/// Vertical Character Count
|
||||
/// </summary>
|
||||
public int VCC;
|
||||
|
||||
/// <summary>
|
||||
/// Vertical Scanline Count (within the current vertical character)
|
||||
/// </summary>
|
||||
public int VLC;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Public Lookups
|
||||
|
||||
/*
|
||||
* These are not accessible directlyon real hardware
|
||||
* It just makes screen generation easier to have these accessbile from the gate array
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The total frame width (in characters)
|
||||
/// </summary>
|
||||
|
@ -86,6 +117,17 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The total frame height (in scanlines)
|
||||
/// </summary>
|
||||
public int FrameHeightInChars
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((int)Regs[VER_TOTAL] + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The width of the display area (in characters)
|
||||
/// </summary>
|
||||
|
@ -108,6 +150,17 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The width of the display area (in scanlines)
|
||||
/// </summary>
|
||||
public int DisplayHeightInChars
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int)Regs[VER_DISPLAYED];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The character at which to start HSYNC
|
||||
/// </summary>
|
||||
|
@ -153,7 +206,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of scanlines in one character
|
||||
/// The number of scanlines in one character (MAXRASTER)
|
||||
/// </summary>
|
||||
public int ScanlinesPerCharacter
|
||||
{
|
||||
|
@ -183,6 +236,12 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
}
|
||||
}
|
||||
|
||||
public int DStartHigh
|
||||
{ get { return Regs[DISP_START_ADDR_H]; } }
|
||||
|
||||
public int DStartLow
|
||||
{ get { return Regs[DISP_START_ADDR_L]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the video buffer size as specified within R12
|
||||
/// </summary>
|
||||
|
@ -394,12 +453,15 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
/// <summary>
|
||||
/// CPC register default values
|
||||
/// Taken from https://web.archive.org/web/20170501112330/http://www.grimware.org/doku.php/documentations/devices/crtc
|
||||
/// http://www.cantrell.org.uk/david/tech/cpc/cpc-firmware/firmware.pdf
|
||||
/// (The defaults values given here are those programmed by the firmware ROM after a cold/warm boot of the CPC/Plus)
|
||||
/// </summary>
|
||||
private byte[] RegDefaults = new byte[] { 63, 40, 46, 0x8e, 38, 0, 25, 30, 0, 7, 0, 0, 0x20, 0x00, 0, 0, 0, 0 };
|
||||
private byte[] RegDefaults = new byte[] { 63, 40, 46, 112, 38, 0, 25, 30, 0, 7, 0, 0, 48, 0, 192, 7, 0, 0 };
|
||||
|
||||
/// <summary>
|
||||
/// Register masks
|
||||
/// 0 = WRITE
|
||||
/// 1 = READ
|
||||
/// </summary>
|
||||
private byte[] CPCMask = new byte[] { 255, 255, 255, 255, 127, 31, 127, 126, 3, 31, 31, 31, 63, 255, 63, 255, 63, 255 };
|
||||
|
||||
|
@ -408,16 +470,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
/// </summary>
|
||||
private int HCC;
|
||||
|
||||
/// <summary>
|
||||
/// Vertical Character Count
|
||||
/// </summary>
|
||||
public int VCC;
|
||||
|
||||
/// <summary>
|
||||
/// Vertical Scanline Count
|
||||
/// </summary>
|
||||
public int VLC;
|
||||
|
||||
/// <summary>
|
||||
/// Internal cycle counter
|
||||
/// </summary>
|
||||
|
@ -452,11 +504,99 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
#region Public Methods
|
||||
|
||||
public void ClockCycle()
|
||||
{
|
||||
CheckHSYNCOff();
|
||||
|
||||
HCC++;
|
||||
|
||||
if (HCC == Regs[HOR_TOTAL] + 1)
|
||||
{
|
||||
// end of scanline
|
||||
HCC = 0;
|
||||
|
||||
if (VSYNCCounter > 0)
|
||||
{
|
||||
VSYNCCounter--;
|
||||
if (VSYNCCounter == 0)
|
||||
{
|
||||
VSYNC = false;
|
||||
}
|
||||
}
|
||||
|
||||
VLC++;
|
||||
|
||||
if (VLC == Regs[MAX_RASTER_ADDR] + 1)
|
||||
{
|
||||
// end of rasterline
|
||||
VLC = 0;
|
||||
VCC++;
|
||||
|
||||
if (VCC == Regs[VER_TOTAL] + 1)
|
||||
{
|
||||
// end of screen
|
||||
VCC = 0;
|
||||
}
|
||||
|
||||
if (VCC == Regs[VER_SYNC_POS] && !VSYNC)
|
||||
{
|
||||
VSYNC = true;
|
||||
VSYNCCounter = VSYNCWidth;
|
||||
VSYNC_Callbacks();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// still on the current scanline
|
||||
if (HCC == Regs[HOR_SYNC_POS] && !HSYNC)
|
||||
{
|
||||
HSYNC = true;
|
||||
HSYNCCounter = HSYNCWidth;
|
||||
HSYNC_Callbacks();
|
||||
ByteCounter = 0;
|
||||
}
|
||||
|
||||
if (HCC >= Regs[HOR_DISPLAYED] + 1 || VCC >= Regs[VER_DISPLAYED])
|
||||
{
|
||||
DISPTMG = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
DISPTMG = true;
|
||||
|
||||
var line = VCC;
|
||||
var row = VLC;
|
||||
var addrX = (LatchedRAMOffset * 2) + ((VCC * LatchedScreenWidthBytes) & 0x7ff) + ByteCounter;
|
||||
// remove artifacts caused by certain hardware scrolling addresses
|
||||
addrX &= 0x7ff;
|
||||
var addrY = LatchedRAMStartAddress + (2048 * VLC);
|
||||
|
||||
//var addr = VideoPageBase + (line * (0x50)) + (row * 0x800) + (ByteCounter);
|
||||
CurrentByteAddress = (ushort)(addrX + addrY);
|
||||
|
||||
ByteCounter += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckHSYNCOff()
|
||||
{
|
||||
if (HSYNCCounter > 0)
|
||||
{
|
||||
HSYNCCounter--;
|
||||
if (HSYNCCounter == 0)
|
||||
{
|
||||
HSYNC = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a CRCT clock cycle
|
||||
/// This should be called at 1Mhz / 1us / every 4 uncontended CPU t-states
|
||||
/// </summary>
|
||||
public void ClockCycle()
|
||||
public void ClockCycle2()
|
||||
{
|
||||
if (HSYNC)
|
||||
{
|
||||
|
@ -475,7 +615,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
if (HSYNC && HSYNCCounter == 1)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
// move one horizontal character
|
||||
|
@ -493,7 +633,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
else
|
||||
{
|
||||
DISPTMG = true;
|
||||
|
||||
|
||||
var line = VCC;
|
||||
var row = VLC;
|
||||
var addrX = (LatchedRAMOffset * 2) + ((VCC * LatchedScreenWidthBytes) & 0x7ff) + ByteCounter;
|
||||
|
@ -503,8 +643,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
//var addr = VideoPageBase + (line * (0x50)) + (row * 0x800) + (ByteCounter);
|
||||
CurrentByteAddress = (ushort)(addrX + addrY);
|
||||
|
||||
|
||||
|
||||
ByteCounter += 2;
|
||||
}
|
||||
|
@ -515,7 +653,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
// end of the current scanline
|
||||
HCC = 0;
|
||||
|
||||
if (ChipType == CRCTType.UMC_UM6845R && VLC <= Regs[MAX_RASTER_ADDR])
|
||||
|
||||
if (ChipType == (CRCTType)1 && VLC <= Regs[MAX_RASTER_ADDR])
|
||||
{
|
||||
// https://web.archive.org/web/20170501112330/http://www.grimware.org/doku.php/documentations/devices/crtc
|
||||
// The MA is reloaded with the value from R12 and R13 when VCC=0 and VLC=0 (that's when a new CRTC screen begin).
|
||||
|
@ -548,7 +687,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
// still doing extra scanlines
|
||||
}
|
||||
else
|
||||
|
||||
{
|
||||
// finished doing extra scanlines
|
||||
EndOfScreen = false;
|
||||
|
@ -585,8 +723,9 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
{
|
||||
VSYNC = true;
|
||||
VSYNCCounter = 0;
|
||||
VSYNC_Callbacks();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -598,6 +737,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
{
|
||||
HSYNC = true;
|
||||
HSYNCCounter = 0;
|
||||
HSYNC_Callbacks();
|
||||
lineCounter++;
|
||||
|
||||
LatchedRAMStartAddress = VideoPageBase;
|
||||
LatchedRAMOffset = VideoRAMOffset;
|
||||
|
@ -608,6 +749,95 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a CRCT clock cycle
|
||||
/// This should be called at 1Mhz / 1us / every 4 uncontended CPU t-states
|
||||
/// </summary>
|
||||
public void ClockCycle1()
|
||||
{
|
||||
// HSYNC processing
|
||||
if (HSYNCCounter > 0)
|
||||
{
|
||||
HSYNCCounter--;
|
||||
if (HSYNCCounter == 0)
|
||||
HSYNC = false;
|
||||
}
|
||||
|
||||
HCC++;
|
||||
|
||||
if (HCC == FrameWidth)
|
||||
{
|
||||
// we have finished the current scanline
|
||||
HCC = 0;
|
||||
|
||||
if (VSYNCCounter > 0)
|
||||
{
|
||||
VSYNCCounter--;
|
||||
if (VSYNCCounter == 0)
|
||||
VSYNC = false;
|
||||
}
|
||||
|
||||
VLC++;
|
||||
|
||||
if (VLC == ScanlinesPerCharacter)
|
||||
{
|
||||
// completed a vertical character
|
||||
VLC = 0;
|
||||
VCC++;
|
||||
|
||||
if (VCC == FrameHeight)
|
||||
{
|
||||
// screen has completed
|
||||
VCC = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// check whether VSYNC should be raised
|
||||
if (VCC == VerticalSyncPos && !VSYNC)
|
||||
{
|
||||
VSYNC = true;
|
||||
VSYNCCounter = VSYNCWidth;
|
||||
VSYNC_Callbacks();
|
||||
}
|
||||
}
|
||||
else if (HCC == HorizontalSyncPos && !HSYNC)
|
||||
{
|
||||
// start of HSYNC period
|
||||
HSYNC = true;
|
||||
HSYNCCounter = HSYNCWidth;
|
||||
HSYNC_Callbacks();
|
||||
}
|
||||
|
||||
// DISPTMG
|
||||
if (HCC >= Regs[HOR_DISPLAYED] || VCC >= Regs[VER_DISPLAYED])
|
||||
{
|
||||
DISPTMG = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
DISPTMG = true;
|
||||
}
|
||||
/*
|
||||
// check for DISPTMG
|
||||
if (HCC >= Regs[HOR_DISPLAYED] + 1)
|
||||
{
|
||||
DISPTMG = false;
|
||||
}
|
||||
else if (VCC >= Regs[VER_DISPLAYED])
|
||||
{
|
||||
DISPTMG = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
DISPTMG = true;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public int lineCounter = 0;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resets the chip
|
||||
/// </summary>
|
||||
|
@ -624,6 +854,16 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
// updates widths
|
||||
UpdateWidths();
|
||||
|
||||
HSYNC = false;
|
||||
VSYNC = false;
|
||||
|
||||
HSYNCCounter = 0;
|
||||
VSYNCCounter = 0;
|
||||
|
||||
HCC = 0;
|
||||
VCC = 0;
|
||||
VLC = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -690,6 +930,18 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
}
|
||||
|
||||
if (SelectedRegister == HOR_TOTAL)
|
||||
{
|
||||
// always 63
|
||||
if (data != 63)
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedRegister == 1)
|
||||
{
|
||||
var d = data;
|
||||
}
|
||||
|
||||
Regs[SelectedRegister] = (byte)(data & CPCMask[SelectedRegister]);
|
||||
|
||||
if (SelectedRegister == HOR_AND_VER_SYNC_WIDTHS)
|
||||
|
@ -809,26 +1061,26 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
{
|
||||
switch (ChipType)
|
||||
{
|
||||
case CRCTType.Hitachi_HD6845S:
|
||||
case CRCTType.HD6845S:
|
||||
// Bits 7..4 define Vertical Sync Width. If 0 is programmed this gives 16 lines of VSYNC. Bits 3..0 define Horizontal Sync Width.
|
||||
// If 0 is programmed no HSYNC is generated.
|
||||
HSYNCWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 0) & 0x0F;
|
||||
VSYNCWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 4) & 0x0F;
|
||||
break;
|
||||
case CRCTType.UMC_UM6845R:
|
||||
case CRCTType.UM6845R:
|
||||
// Bits 7..4 are ignored. Vertical Sync is fixed at 16 lines. Bits 3..0 define Horizontal Sync Width. If 0 is programmed no HSYNC is generated.
|
||||
HSYNCWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 0) & 0x0F;
|
||||
VSYNCWidth = 16;
|
||||
break;
|
||||
case CRCTType.Motorola_MC6845:
|
||||
case CRCTType.MC6845:
|
||||
// Bits 7..4 are ignored. Vertical Sync is fixed at 16 lines. Bits 3..0 define Horizontal Sync Width. If 0 is programmed this gives a HSYNC width of 16.
|
||||
HSYNCWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 0) & 0x0F;
|
||||
if (HSYNCWidth == 0)
|
||||
HSYNCWidth = 16;
|
||||
VSYNCWidth = 16;
|
||||
break;
|
||||
case CRCTType.Amstrad_AMS40489:
|
||||
case CRCTType.Amstrad_40226:
|
||||
case CRCTType.AMS40489:
|
||||
case CRCTType.AMS40226:
|
||||
// Bits 7..4 define Vertical Sync Width. If 0 is programmed this gives 16 lines of VSYNC.Bits 3..0 define Horizontal Sync Width.
|
||||
// If 0 is programmed this gives a HSYNC width of 16.
|
||||
HSYNCWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 0) & 0x0F;
|
||||
|
@ -960,11 +1212,12 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
/// </summary>
|
||||
public enum CRCTType
|
||||
{
|
||||
Hitachi_HD6845S = 0,
|
||||
UMC_UM6845R = 1,
|
||||
Motorola_MC6845 = 2,
|
||||
Amstrad_AMS40489 = 3,
|
||||
Amstrad_40226 = 4
|
||||
HD6845S = 0,
|
||||
UM6845 = 0,
|
||||
UM6845R = 1,
|
||||
MC6845 = 2,
|
||||
AMS40489 = 3,
|
||||
AMS40226 = 4
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -28,12 +28,15 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
{
|
||||
_machine = machine;
|
||||
CurrentLine = new ScanLine(this);
|
||||
|
||||
CRCT.AttachHSYNCCallback(OnHSYNC);
|
||||
CRCT.AttachVSYNCCallback(OnVSYNC);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Palettes
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The standard CPC Pallete (ordered by firmware #)
|
||||
/// http://www.cpcwiki.eu/index.php/CPC_Palette
|
||||
|
@ -108,6 +111,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
Colors.ARGB(0x80, 0x80, 0x00), // Yellow
|
||||
Colors.ARGB(0x80, 0x80, 0xFF), // Pastel Blue
|
||||
};
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -154,6 +158,22 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
ScreenBuffer = new int[BufferWidth * BufferHeight];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the CRCT flags HSYNC
|
||||
/// </summary>
|
||||
public void OnHSYNC()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the CRCT flags VSYNC
|
||||
/// </summary>
|
||||
public void OnVSYNC()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IVideoProvider
|
||||
|
@ -370,11 +390,37 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
// 4 bits per pixel - 2 bytes - 4 pixels (8 CRT pixels)
|
||||
// RECT
|
||||
case 0:
|
||||
Characters[charIndex].Pixels = new int[8];
|
||||
Characters[charIndex].Pixels = new int[16];
|
||||
|
||||
int m0Count = 0;
|
||||
|
||||
int m0B0P0i = vid1 & 170;
|
||||
int pix = vid1 & 0xaa;
|
||||
pix = ((pix & 0x80) >> 7) | ((pix & 0x08) >> 2) | ((pix & 0x20) >> 3) | ((pix & 0x02 << 2));
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
pix = vid1 & 0x55;
|
||||
pix = (((pix & 0x40) >> 6) | ((pix & 0x04) >> 1) | ((pix & 0x10) >> 2) | ((pix & 0x01 << 3)));
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
|
||||
pix = vid2 & 0xaa;
|
||||
pix = ((pix & 0x80) >> 7) | ((pix & 0x08) >> 2) | ((pix & 0x20) >> 3) | ((pix & 0x02 << 2));
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
pix = vid2 & 0x55;
|
||||
pix = (((pix & 0x40) >> 6) | ((pix & 0x04) >> 1) | ((pix & 0x10) >> 2) | ((pix & 0x01 << 3)));
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[pix]];
|
||||
/*
|
||||
int m0B0P0i = vid1 & 0xaa;
|
||||
int m0B0P0 = ((m0B0P0i & 0x80) >> 7) | ((m0B0P0i & 0x08) >> 2) | ((m0B0P0i & 0x20) >> 3) | ((m0B0P0i & 0x02 << 2));
|
||||
int m0B0P1i = vid1 & 85;
|
||||
int m0B0P1 = ((m0B0P1i & 0x40) >> 6) | ((m0B0P1i & 0x04) >> 1) | ((m0B0P1i & 0x10) >> 2) | ((m0B0P1i & 0x01 << 3));
|
||||
|
@ -393,6 +439,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[m0B1P0]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[m0B1P1]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[m0B1P1]];
|
||||
*/
|
||||
break;
|
||||
|
||||
// 2 bits per pixel - 2 bytes - 8 pixels (16 CRT pixels)
|
||||
|
@ -500,6 +547,9 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
switch (ScreenMode)
|
||||
{
|
||||
case 0:
|
||||
hScale = 1;
|
||||
vScale = 2;
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
hScale = 2;
|
||||
|
|
|
@ -16,15 +16,15 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
/// </summary>
|
||||
/// <param name="spectrum"></param>
|
||||
/// <param name="cpu"></param>
|
||||
public CPC464(AmstradCPC cpc, Z80A cpu, List<byte[]> files, bool autoTape)
|
||||
public CPC464(AmstradCPC cpc, Z80A cpu, List<byte[]> files, bool autoTape, AmstradCPC.BorderType borderType)
|
||||
{
|
||||
CPC = cpc;
|
||||
CPU = cpu;
|
||||
|
||||
FrameLength = 79872;
|
||||
|
||||
CRCT = new CRCT_6845(CRCT_6845.CRCTType.Motorola_MC6845, this);
|
||||
CRT = new CRTDevice(this);
|
||||
CRCT = new CRCT_6845(CRCT_6845.CRCTType.MC6845, this);
|
||||
//CRT = new CRTDevice(this);
|
||||
GateArray = new AmstradGateArray(this, AmstradGateArray.GateArrayType.Amstrad40007);
|
||||
PPI = new PPI_8255(this);
|
||||
|
||||
|
|
|
@ -16,15 +16,15 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
/// </summary>
|
||||
/// <param name="spectrum"></param>
|
||||
/// <param name="cpu"></param>
|
||||
public CPC6128(AmstradCPC cpc, Z80A cpu, List<byte[]> files, bool autoTape)
|
||||
public CPC6128(AmstradCPC cpc, Z80A cpu, List<byte[]> files, bool autoTape, AmstradCPC.BorderType borderType)
|
||||
{
|
||||
CPC = cpc;
|
||||
CPU = cpu;
|
||||
|
||||
FrameLength = 79872;
|
||||
|
||||
CRCT = new CRCT_6845(CRCT_6845.CRCTType.Motorola_MC6845, this);
|
||||
CRT = new CRTDevice(this);
|
||||
CRCT = new CRCT_6845(CRCT_6845.CRCTType.MC6845, this);
|
||||
//CRT = new CRTDevice(this);
|
||||
GateArray = new AmstradGateArray(this, AmstradGateArray.GateArrayType.Amstrad40007);
|
||||
PPI = new PPI_8255(this);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
@ -95,7 +96,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
diskMediaIndex = result;
|
||||
|
||||
// fire osd message
|
||||
//Spectrum.OSD_DiskInserted();
|
||||
CPC.OSD_DiskInserted();
|
||||
|
||||
LoadDiskMedia();
|
||||
}
|
||||
|
@ -132,6 +133,63 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
diskImages.Add(m);
|
||||
CPC._diskInfo.Add(CPC._gameInfo[cnt]);
|
||||
break;
|
||||
case CPCMediaType.DiskDoubleSided:
|
||||
// this is a bit tricky. we will attempt to parse the double sided disk image byte array,
|
||||
// then output two separate image byte arrays
|
||||
List<byte[]> working = new List<byte[]>();
|
||||
foreach (DiskType type in Enum.GetValues(typeof(DiskType)))
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DiskType.CPCExtended:
|
||||
found = CPCExtendedFloppyDisk.SplitDoubleSided(m, working);
|
||||
break;
|
||||
case DiskType.CPC:
|
||||
found = CPCFloppyDisk.SplitDoubleSided(m, working);
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
// add side 1
|
||||
diskImages.Add(working[0]);
|
||||
// add side 2
|
||||
diskImages.Add(working[1]);
|
||||
|
||||
Common.GameInfo one = new Common.GameInfo();
|
||||
Common.GameInfo two = new Common.GameInfo();
|
||||
var gi = CPC._gameInfo[cnt];
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Common.GameInfo work = new Common.GameInfo();
|
||||
if (i == 0)
|
||||
{
|
||||
work = one;
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
work = two;
|
||||
}
|
||||
|
||||
work.FirmwareHash = gi.FirmwareHash;
|
||||
work.Hash = gi.Hash;
|
||||
work.Name = gi.Name + " (Parsed Side " + (i + 1) + ")";
|
||||
work.Region = gi.Region;
|
||||
work.NotInDatabase = gi.NotInDatabase;
|
||||
work.Status = gi.Status;
|
||||
work.System = gi.System;
|
||||
|
||||
CPC._diskInfo.Add(work);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cnt++;
|
||||
|
@ -179,7 +237,12 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
if (hdr.ToUpper().Contains("EXTENDED CPC DSK") || hdr.ToUpper().Contains("MV - CPC"))
|
||||
{
|
||||
// amstrad .dsk disk file
|
||||
return CPCMediaType.Disk;
|
||||
// check for number of sides
|
||||
var sides = data[0x31];
|
||||
if (sides == 1)
|
||||
return CPCMediaType.Disk;
|
||||
else
|
||||
return CPCMediaType.DiskDoubleSided;
|
||||
}
|
||||
|
||||
// tape checking
|
||||
|
@ -198,7 +261,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
{
|
||||
None,
|
||||
Tape,
|
||||
Disk
|
||||
Disk,
|
||||
DiskDoubleSided
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
/// <summary>
|
||||
/// Renders pixels to the screen
|
||||
/// </summary>
|
||||
public CRTDevice CRT { get; set; }
|
||||
//public CRTDevice CRT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The PPI contoller chip
|
||||
|
@ -140,6 +140,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
public virtual void ExecuteFrame(bool render, bool renderSound)
|
||||
{
|
||||
GateArray.FrameEnd = false;
|
||||
CRCT.lineCounter = 0;
|
||||
|
||||
InputRead = false;
|
||||
_render = render;
|
||||
|
@ -157,8 +158,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
PollInput();
|
||||
|
||||
CRT.SetupVideo();
|
||||
CRT.ScanlineCounter = 0;
|
||||
//CRT.SetupVideo();
|
||||
//CRT.ScanlineCounter = 0;
|
||||
|
||||
while (!GateArray.FrameEnd)
|
||||
{
|
||||
|
@ -168,9 +169,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
|
||||
TapeDevice.TapeCycle();
|
||||
}
|
||||
|
||||
//OverFlow = (int)CurrentFrameCycle - GateArray.FrameLength;
|
||||
|
||||
// we have reached the end of a frame
|
||||
LastFrameStartCPUTick = CPU.TotalExecutedCycles; // - OverFlow;
|
||||
|
||||
|
@ -346,7 +344,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
ser.Sync("RAM64KBank", ref RAM64KBank);
|
||||
|
||||
CRCT.SyncState(ser);
|
||||
CRT.SyncState(ser);
|
||||
//CRT.SyncState(ser);
|
||||
GateArray.SyncState(ser);
|
||||
KeyboardDevice.SyncState(ser);
|
||||
TapeBuzzer.SyncState(ser);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Text;
|
||||
using BizHawk.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||
{
|
||||
|
@ -144,6 +146,88 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="results"></param>
|
||||
/// <returns></returns>
|
||||
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
|
||||
{
|
||||
// look for standard magic string
|
||||
string ident = Encoding.ASCII.GetString(data, 0, 16);
|
||||
if (!ident.ToUpper().Contains("EXTENDED CPC DSK"))
|
||||
{
|
||||
// incorrect format
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] S0 = new byte[data.Length];
|
||||
byte[] S1 = new byte[data.Length];
|
||||
|
||||
// disk info block
|
||||
Array.Copy(data, 0, S0, 0, 0x100);
|
||||
Array.Copy(data, 0, S1, 0, 0x100);
|
||||
// change side number
|
||||
S0[0x31] = 1;
|
||||
S1[0x31] = 1;
|
||||
|
||||
// extended format has different track sizes
|
||||
int[] trkSizes = new int[data[0x30] * data[0x31]];
|
||||
|
||||
int pos = 0x34;
|
||||
for (int i = 0; i < data[0x30] * data[0x31]; i++)
|
||||
{
|
||||
trkSizes[i] = data[pos] * 256;
|
||||
// clear destination trk sizes (will be added later)
|
||||
S0[pos] = 0;
|
||||
S1[pos] = 0;
|
||||
pos++;
|
||||
}
|
||||
|
||||
// start at track info blocks
|
||||
int mPos = 0x100;
|
||||
int s0Pos = 0x100;
|
||||
int s0tCount = 0;
|
||||
int s1tCount = 0;
|
||||
int s1Pos = 0x100;
|
||||
int tCount = 0;
|
||||
|
||||
while (tCount < data[0x30] * data[0x31])
|
||||
{
|
||||
// which side is this?
|
||||
var side = data[mPos + 0x11];
|
||||
if (side == 0)
|
||||
{
|
||||
// side 1
|
||||
Array.Copy(data, mPos, S0, s0Pos, trkSizes[tCount]);
|
||||
s0Pos += trkSizes[tCount];
|
||||
// trk size table
|
||||
S0[0x34 + s0tCount++] = (byte)(trkSizes[tCount] / 256);
|
||||
}
|
||||
else if (side == 1)
|
||||
{
|
||||
// side 2
|
||||
Array.Copy(data, mPos, S1, s1Pos, trkSizes[tCount]);
|
||||
s1Pos += trkSizes[tCount];
|
||||
// trk size table
|
||||
S1[0x34 + s1tCount++] = (byte)(trkSizes[tCount] / 256);
|
||||
}
|
||||
|
||||
mPos += trkSizes[tCount++];
|
||||
}
|
||||
|
||||
byte[] s0final = new byte[s0Pos];
|
||||
byte[] s1final = new byte[s1Pos];
|
||||
Array.Copy(S0, 0, s0final, 0, s0Pos);
|
||||
Array.Copy(S1, 0, s1final, 0, s1Pos);
|
||||
|
||||
results.Add(s0final);
|
||||
results.Add(s1final);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State serlialization
|
||||
/// </summary>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Text;
|
||||
using BizHawk.Common;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||
{
|
||||
|
@ -149,6 +151,70 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="results"></param>
|
||||
/// <returns></returns>
|
||||
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
|
||||
{
|
||||
// look for standard magic string
|
||||
string ident = Encoding.ASCII.GetString(data, 0, 16);
|
||||
if (!ident.ToUpper().Contains("MV - CPC"))
|
||||
{
|
||||
// incorrect format
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] S0 = new byte[data.Length];
|
||||
byte[] S1 = new byte[data.Length];
|
||||
|
||||
// disk info block
|
||||
Array.Copy(data, 0, S0, 0, 0x100);
|
||||
Array.Copy(data, 0, S1, 0, 0x100);
|
||||
// change side number
|
||||
S0[0x31] = 1;
|
||||
S1[0x31] = 1;
|
||||
|
||||
int trkSize = MediaConverter.GetWordValue(data, 0x32);
|
||||
|
||||
// start at track info blocks
|
||||
int mPos = 0x100;
|
||||
int s0Pos = 0x100;
|
||||
int s1Pos = 0x100;
|
||||
|
||||
while (mPos < trkSize * data[0x30] * data[0x31])
|
||||
{
|
||||
// which side is this?
|
||||
var side = data[mPos + 0x11];
|
||||
if (side == 0)
|
||||
{
|
||||
// side 1
|
||||
Array.Copy(data, mPos, S0, s0Pos, trkSize);
|
||||
s0Pos += trkSize;
|
||||
}
|
||||
else if (side == 1)
|
||||
{
|
||||
// side 2
|
||||
Array.Copy(data, mPos, S1, s1Pos, trkSize);
|
||||
s1Pos += trkSize;
|
||||
}
|
||||
|
||||
mPos += trkSize;
|
||||
}
|
||||
|
||||
byte[] s0final = new byte[s0Pos];
|
||||
byte[] s1final = new byte[s1Pos];
|
||||
Array.Copy(S0, 0, s0final, 0, s0Pos);
|
||||
Array.Copy(S1, 0, s1final, 0, s1Pos);
|
||||
|
||||
results.Add(s0final);
|
||||
results.Add(s1final);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State serlialization
|
||||
/// </summary>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||
{
|
||||
/// <summary>
|
||||
/// This is called first when importing disk images
|
||||
/// Disk images can be single or double-sided, so we need to handle that
|
||||
/// </summary>
|
||||
public class DiskHandler
|
||||
{
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue