diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index e2050b3c10..5fb0c7bd6e 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -140,7 +140,6 @@
-
@@ -152,6 +151,7 @@
+
diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRCTChip.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRCTChip.cs
deleted file mode 100644
index 2cda19e091..0000000000
--- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRCTChip.cs
+++ /dev/null
@@ -1,832 +0,0 @@
-using BizHawk.Common;
-using BizHawk.Common.NumberExtensions;
-using System.Collections;
-
-namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
-{
- ///
- /// 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
- ///
- 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
- ///
- /// 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.
- ///
- public bool VSYNC { get { return _VSYNC; } }
- ///
- /// 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.
- ///
- public bool HSYNC { get { return _HSYNC; } }
- ///
- /// This TTL compatible output is an active high signal which indicates the CRTC is providing addressing in the active Display Area.
- ///
- public bool DisplayEnable { get { return DisplayEnable; } }
- ///
- /// This TTL compatible output indicates Cursor Display to external Video Processing Logic.Active high signal.
- ///
- 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
-
- ///
- /// 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
-
- */
- ///
- 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
-
- ///
- /// This TTL compatible output indicates Cursor Display to external Video Processing Logic.Active high signal.
- ///
- public bool CLK { get { return _CLK; } }
- ///
- /// 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.
- ///
- public bool RESET { get { return _RESET; } }
- ///
- /// 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.
- ///
- public bool LPSTB { get { return _LPSTB; } }
-
- private bool _CLK;
- private bool _RESET;
- private bool _LPSTB;
-
- #endregion
-
- #region Internal Registers
-
- ///
- /// The currently selected register
- ///
- private byte AddressRegister;
-
- ///
- /// 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.
- ///
- private byte[] Register = new byte[18];
-
- // Horizontal timing register constants
- ///
- /// 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.
- ///
- private const int H_TOTAL = 0;
- ///
- /// This 8 bit write-only register determines the number of displayed characters per horizontal line.
- ///
- private const int H_DISPLAYED = 1;
- ///
- /// This 8 bit write-only register determines the horizontal sync postiion on the horizontal line.
- ///
- private const int H_SYNC_POS = 2;
- ///
- /// 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
- ///
- private const int SYNC_WIDTHS = 3;
-
- // Vertical timing register constants
- ///
- /// 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.
- ///
- private const int V_TOTAL = 4;
- private const int V_TOTAL_ADJUST = 5;
- ///
- /// This 7 bit write-only register determines the number of displayed character rows on the CRT screen, and is programmed in character row times.
- ///
- private const int V_DISPLAYED = 6;
- ///
- /// This 7 bit write-only register determines the vertical sync position with respect to the reference.It is programmed in character row times.
- ///
- private const int V_SYNC_POS = 7;
- ///
- /// 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.
- ///
- private const int INTERLACE_MODE = 8;
- ///
- /// 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.
- ///
- private const int MAX_SL_ADDRESS = 9;
-
- // Other register constants
- ///
- /// 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.
- ///
- private const int CURSOR_START = 10;
- ///
- /// This 5 bit write-only register sets the cursor end scan line
- ///
- private const int CURSOR_END = 11;
- ///
- /// 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.
- ///
- private const int START_ADDR_H = 12;
- private const int START_ADDR_L = 13;
- ///
- /// This 14 bit read/write register stores the cursor location.This register consists of an 8 bit lower and 6 bit higher register.
- ///
- private const int CURSOR_H = 14;
- private const int CURSOR_L = 15;
- ///
- /// 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.
- ///
- private const int LIGHT_PEN_H = 16;
- private const int LIGHT_PEN_L = 17;
-
- #endregion
-
- #region Internal Fields & Properties
-
- ///
- /// Calculated when set based on R3
- ///
- private int HSYNCWidth;
- ///
- /// Calculated when set based on R3
- ///
- private int VSYNCWidth;
-
- ///
- /// Character pos address (0 index).
- /// Feeds the MA lines
- ///
- private int LinearAddress;
-
- ///
- /// The currently selected Interlace Mode (based on R8)
- ///
- 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;
- }
- }
-
- ///
- /// The current cursor display mode (based on R14 & R15)
- ///
- 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 -
- */
-
- ///
- /// CPU (or other device) reads from the 8-bit databus
- ///
- ///
- ///
- 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;
- }
-
- ///
- /// CPU (or other device) writes to the 8-bit databus
- ///
- ///
- ///
- 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
-
- ///
- /// Selects a specific register
- ///
- ///
- private void SelectRegister(int value)
- {
- var v = (byte)((byte)value & 0x1F);
- if (v > 0 && v < 18)
- {
- AddressRegister = v;
- }
- }
-
- ///
- /// Writes to the currently latched address register
- ///
- ///
- 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;
- }
- }
-
- ///
- /// Reads from the currently selected register
- ///
- ///
- 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;
- }
-
- ///
- /// Updates the V and H SYNC widths
- ///
- 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;
- }
- }
-
-
-
- ///
- /// Reads from the status register
- ///
- ///
- ///
- 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
-
- ///
- /// Performs a CRCT clock cycle.
- /// On CPC this is called at 1Mhz == 1 Character cycle (2 bytes)
- ///
- 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
-
- ///
- /// The types of CRCT chip found in the CPC range
- ///
- public enum CRCTType
- {
- HD6845S = 0,
- UM6845 = 0,
- UM6845R = 1,
- MC6845 = 2,
- AMS40489 = 3,
- AMS40226 = 4
- }
-
- ///
- /// The available interlace modes in the CRCT
- ///
- private enum InterlaceMode
- {
- NormalSyncMode,
- InterlaceSyncMode,
- InterlaceSyncAndVideoMode
- }
-
- ///
- /// Cursor display modes
- ///
- private enum CursorControl
- {
- NonBlink,
- CursorNonDisplay,
- Blink1_16,
- Blink1_32
- }
-
- #endregion
- }
-}
diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRTC6845.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRTC6845.cs
new file mode 100644
index 0000000000..6e2fa4c2cf
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRTC6845.cs
@@ -0,0 +1,2371 @@
+using BizHawk.Common;
+using BizHawk.Common.NumberExtensions;
+using System;
+using System.Collections;
+
+namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
+{
+ ///
+ /// CATHODE RAY TUBE CONTROLLER (CRTC) IMPLEMENTATION
+ /// http://www.cpcwiki.eu/index.php/CRTC
+ /// http://cpctech.cpc-live.com/docs/cpcplus.html
+ /// This implementation aims to emulate all the various CRTC chips that appear within
+ /// the CPC, CPC+ and GX4000 ranges. The CPC community have assigned them type numbers.
+ /// If different implementations share the same type number it indicates that they are functionally identical:
+ ///
+ /// Part No. Manufacturer Type No. Info.
+ /// ------------------------------------------------------------------------------------------------------
+ /// HD6845S Hitachi 0
+ /// Datasheet: http://www.cpcwiki.eu/imgs/c/c0/Hd6845.hitachi.pdf
+ /// ------------------------------------------------------------------------------------------------------
+ /// UM6845 UMC 0
+ /// Datasheet: http://www.cpcwiki.eu/imgs/1/13/Um6845.umc.pdf
+ /// ------------------------------------------------------------------------------------------------------
+ /// UM6845R UMC 1
+ /// Datasheet: http://www.cpcwiki.eu/imgs/b/b5/Um6845r.umc.pdf
+ /// ------------------------------------------------------------------------------------------------------
+ /// MC6845 Motorola 2
+ /// Datasheet: http://www.cpcwiki.eu/imgs/d/da/Mc6845.motorola.pdf & http://bitsavers.trailing-edge.com/components/motorola/_dataSheets/6845.pdf
+ /// ------------------------------------------------------------------------------------------------------
+ /// AMS40489 Amstrad 3 Only exists in the CPC464+, CPC6128+ and GX4000 and is integrated into a single CPC+ ASIC chip (along with the gatearray)
+ /// Datasheet: {none}
+ /// ------------------------------------------------------------------------------------------------------
+ /// AMS40041 Amstrad 4 'Pre-ASIC' IC. The CRTC is integrated into a aingle ASIC IC with functionality being almost identical to the AMS40489
+ /// (or 40226) Used in the 'Cost-Down' range of CPC464 and CPC6128 systems
+ /// Datasheet: {none}
+ ///
+ ///
+ public class CRTC6845
+ {
+ ///
+ /// The type of CRTC we are emulating
+ /// (passed in through contructor)
+ ///
+ private CRTCType ChipType;
+
+ #region Construction
+
+ ///
+ /// The only constructor
+ ///
+ ///
+ public CRTC6845(CRTCType type)
+ {
+ ChipType = type;
+ }
+
+ #endregion
+
+ #region Input Lines
+
+ ///
+ /// The ClK isaTTUMOS-compatible input used to synchronize all CRT' functions except for the processor interface.
+ /// An external dot counter is used to derive this signal which is usually the character rate in an alphanumeric CRT.
+ /// The active transition is high-to-low
+ ///
+ public bool CLK { get { return _CLK; } }
+ private bool _CLK;
+ ///
+ /// 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.
+ ///
+ public bool RESET { get { return _RESET; } }
+ private bool _RESET;
+ ///
+ /// 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.
+ ///
+ public bool LPSTB { get { return _LPSTB; } }
+ private bool _LPSTB;
+
+ #endregion
+
+ #region Output Lines
+
+ // State output lines
+ ///
+ /// 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.
+ ///
+ public bool VSYNC { get { return _VSYNC; } }
+ private bool _VSYNC;
+ ///
+ /// 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.
+ ///
+ public bool HSYNC { get { return _HSYNC; } }
+ private bool _HSYNC;
+ ///
+ /// This TTL compatible output is an active high signal which indicates the CRTC is providing addressing in the active Display Area.
+ ///
+ public bool DISPTMG { get { return _DISPTMG; } }
+ private bool _DISPTMG;
+ ///
+ /// This TTL compatible output indicates Cursor Display to external Video Processing Logic.Active high signal.
+ ///
+ public bool CUDISP { get { return _CUDISP; } }
+ private bool _CUDISP;
+
+ // 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 RowSelects.Bit(0); } }
+ public bool RA1 { get { return RowSelects.Bit(1); } }
+ public bool RA2 { get { return RowSelects.Bit(2); } }
+ public bool RA3 { get { return RowSelects.Bit(3); } } // cpcwiki would suggest that this isnt connected in the CPC range
+ public bool RA4 { get { return RowSelects.Bit(4); } } // cpcwiki would suggest that this isnt connected in the CPC range
+
+ ///
+ /// This 16-bit property emulates how the Amstrad CPC Gate Array is connected up to the CRTC
+ /// Built from R12, R13 and CLK
+ /*
+ 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
+ */
+ ///
+ public ushort AddressLineCPC
+ {
+ 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 Internal State
+
+ ///
+ /// Character pos address (0 index).
+ /// Feeds the MA lines
+ ///
+ private int LinearAddress;
+
+ ///
+ /// Generated by the Vertical Control
+ /// Feeds the RA lines
+ ///
+ private int RowSelects;
+
+ ///
+ /// Horizontal Counter
+ ///
+ private int _CharacterCTR;
+ private int CharacterCTR
+ {
+ get { return _CharacterCTR; }
+ set
+ {
+ if (value > 255)
+ _CharacterCTR = value - 255;
+ }
+ }
+
+ ///
+ /// HSYNC Counter
+ ///
+ private int _HorizontalSyncWidthCTR;
+ private int HorizontalSyncWidthCTR
+ {
+ get { return _HorizontalSyncWidthCTR; }
+ set
+ {
+ if (value > 15)
+ _HorizontalSyncWidthCTR = value - 15;
+ }
+ }
+
+ ///
+ /// VSYNC Counter
+ ///
+ private int _VerticalSyncWidthCTR;
+ private int VerticalSyncWidthCTR
+ {
+ get { return _VerticalSyncWidthCTR; }
+ set
+ {
+ if (value > 15)
+ _VerticalSyncWidthCTR = value - 15;
+ }
+ }
+
+ ///
+ /// Vertical Character Counter
+ ///
+ private int _LineCTR;
+ private int LineCTR
+ {
+ get { return _LineCTR; }
+ set
+ {
+ if (value > 127)
+ _LineCTR = value - 127;
+ }
+ }
+
+ ///
+ /// Vertical Raster Counter
+ ///
+ private int _RasterCTR;
+ private int RasterCTR
+ {
+ get { return _RasterCTR; }
+ set
+ {
+ if (value > 31)
+ _RasterCTR = value - 31;
+ }
+ }
+
+ ///
+ /// The CRTC latches the Display Start H & L address at different times
+ /// (depending on the chip type)
+ ///
+ private int StartAddressLatch;
+
+ #endregion
+
+ #region Internal Registers
+
+ ///
+ /// The currently selected register
+ ///
+ private byte AddressRegister;
+
+ ///
+ /// 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.
+ ///
+ private byte[] Register = new byte[18];
+
+ ///
+ /// Internal Status Register specific to the Type 1 UM6845R
+ ///
+ private byte StatusRegister;
+
+ ///
+ /// Not really a true status register, but values are returned on types 3 and 4
+ /// depending on the current CRTC status
+ ///
+ private byte AsicStatusRegister1;
+ ///
+ /// Not really a true status register, but values are returned on types 3 and 4
+ /// depending on the current CRTC status
+ ///
+ private byte AsicStatusRegister2;
+
+ #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 -
+
+ The Read/Write functions below are geared toward Amstrad CPC only
+ They could be overridden for a different implementation if needs be
+ */
+
+ ///
+ /// CPU (or other device) reads from the 8-bit databus
+ ///
+ ///
+ ///
+ public virtual 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;
+ }
+
+ ///
+ /// CPU (or other device) writes to the 8-bit databus
+ ///
+ ///
+ ///
+ public virtual 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 Type-Specific Logic
+
+ ///
+ /// Runs a clock cycle for the current chip type
+ /// CPC will call this every 1Mhz
+ /// Equates to 1 generated character (2 bytes of data)
+ /// Based on the various CRCT FUNCTIONAL BLOCK DIAGRAMs in the datasheets
+ ///
+ public void CycleClock()
+ {
+ switch ((int)ChipType)
+ {
+ case 0:
+ ClockCycle_Type0();
+ break;
+ case 2:
+ ClockCycle_Type2();
+ break;
+ default:
+ ClockCycle_Generic();
+ break;
+ }
+ }
+
+ ///
+ /// Type dependent
+ /// Either a static value or calculated from R3
+ ///
+ private int HSYNCWidth
+ {
+ get
+ {
+ switch ((int)ChipType)
+ {
+ case 0:
+ case 1:
+ return HSYNCWidth_Type0_1;
+ default:
+ return HSYNCWidth_Type2_3_4;
+ }
+ }
+ }
+
+ ///
+ /// Type dependent
+ /// Either a static value or calculated from R3
+ ///
+ private int VSYNCWidth
+ {
+ get
+ {
+ switch ((int)ChipType)
+ {
+ case 1:
+ case 2:
+ return VSYNCWidth_Type1_2;
+ default:
+ return VSYNCWidth_Type0_3_4;
+ }
+ }
+ }
+
+ ///
+ /// Selects a specific register
+ ///
+ ///
+ private void SelectRegister(int value)
+ {
+ var v = (byte)((byte)value & 0x1F);
+ if (v > 0 && v < 18)
+ {
+ AddressRegister = v;
+ }
+ }
+
+ ///
+ /// Attempts to read from the currently selected register
+ ///
+ ///
+ ///
+ private bool ReadRegister(ref int data)
+ {
+ switch ((int)ChipType)
+ {
+ case 0: return ReadRegister_Type0(ref data);
+ case 1: return ReadRegister_Type1(ref data);
+ case 2: return ReadRegister_Type2(ref data);
+ case 3:
+ case 4: return ReadRegister_Type3_4(ref data);
+ default: return false;
+ }
+ }
+
+ ///
+ /// Attempts to write to the currently selected register
+ ///
+ ///
+ ///
+ private void WriteRegister(int data)
+ {
+ switch ((int)ChipType)
+ {
+ case 0: WriteRegister_Type0(data); break;
+ case 1: WriteRegister_Type1(data); break;
+ case 2: WriteRegister_Type2(data); break;
+ case 3:
+ case 4: WriteRegister_Type3_4(data); break;
+ }
+ }
+
+ ///
+ /// Attempts to read from the internal status register (if present)
+ ///
+ ///
+ ///
+ private bool ReadStatus(ref int data)
+ {
+ switch ((int)ChipType)
+ {
+ case 1: return ReadStatus_Type1(ref data);
+ case 3:
+ case 4: return ReadStatus_Type3_4(ref data);
+ default: return false;
+ }
+ }
+
+ ///
+ /// The status of the DisplayEnableSkew bit(s) in R8
+ ///
+ private int DISPTMGSkew
+ {
+ get
+ {
+ var val = Register[INTERLACE_MODE];
+ int res = 0;
+ switch ((int)ChipType)
+ {
+ // HD6845 & UM6845
+ case 0:
+ // Bits 5 and 4 determine the skew
+ res = (val & 0x30) >> 4;
+ if (res > 2)
+ return -1;
+ break;
+
+ // UMR6845R
+ case 1:
+ return 0;
+
+ // MC6845
+ case 2:
+ return 0;
+
+ // AMS chips
+ case 3:
+ case 4:
+ break;
+ }
+
+ return res;
+ }
+ }
+
+ ///
+ /// The status of the CursorSkew bit(s) in R8
+ ///
+ private int CUDISPSkew
+ {
+ get
+ {
+ var val = Register[INTERLACE_MODE];
+ int res = 0;
+ switch ((int)ChipType)
+ {
+ // HD6845 & UM6845
+ case 0:
+ // Bits 5 and 4 determine the skew
+ res = (val & 0xC0) >> 6;
+ if (res > 2)
+ return -1;
+ break;
+
+ // UMR6845R
+ case 1:
+ return 0;
+
+ // MC6845
+ case 2:
+ return 0;
+
+ // AMS chips
+ case 3:
+ case 4:
+ break;
+ }
+
+ return res;
+ }
+ }
+
+ ///
+ /// The currently selected Interlace Mode (based on R8)
+ /// Looks to be the same for all chip types
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Gets the combined value of R12 & R13
+ ///
+ private int StartAddressRegisterValue
+ {
+ get
+ {
+ var Reg13 = Register[START_ADDR_L];
+ var Reg12 = (byte)(Register[START_ADDR_H] & 0x3f);
+ return (Reg12 << 8) + Reg13;
+ }
+ }
+
+ ///
+ /// Gets the combined value of R14 & R15
+ ///
+ private int CursorAddressRegisterValue
+ {
+ get
+ {
+ var reg15 = Register[CURSOR_L];
+ var reg14 = (byte)(Register[CURSOR_H] & 0x3f);
+ return (reg14 << 8) + reg15;
+ }
+ }
+
+ #endregion
+
+ #region Type-Specific Internal Methods
+
+ #region Sync Widths
+
+ ///
+ /// Current programmed HSYNC width for Type 0 (HD6845S & UM6845) & Type 1 (UM6845R)
+ ///
+ private int HSYNCWidth_Type0_1
+ {
+ get
+ {
+ // Bits 3..0 define Horizontal Sync Width.
+ // If 0 is programmed no HSYNC is generated.
+ return (Register[SYNC_WIDTHS] >> 0) & 0x0F;
+ }
+ }
+
+ ///
+ /// Current programmed HSYNC width for Type 2 (MC6845), 3 (AMS40489) & 4 (pre-ASIC)
+ ///
+ private int HSYNCWidth_Type2_3_4
+ {
+ get
+ {
+ // Bits 3..0 define Horizontal Sync Width.
+ // If 0 is programmed this gives a HSYNC width of 16
+ var width = (Register[SYNC_WIDTHS] >> 0) & 0x0F;
+ if (width == 0)
+ width = 16;
+ return width;
+ }
+ }
+
+ ///
+ /// Current programmed VSYNC width for Type 0 (HD6845S & UM6845), 3 (AMS40489) & 4 (pre-ASIC)
+ ///
+ private int VSYNCWidth_Type0_3_4
+ {
+ get
+ {
+ // Bits 7..4 define Vertical Sync Width
+ // If 0 is programmed this gives 16 lines of VSYNC
+ var width = (Register[SYNC_WIDTHS] >> 4) & 0x0F;
+ if (width == 0)
+ width = 16;
+ return width;
+ }
+ }
+
+ ///
+ /// Current programmed VSYNC width for Type 1 (UM6845R) & 2 (MC6845)
+ ///
+ private int VSYNCWidth_Type1_2
+ {
+ get
+ {
+ // Bits 7..4 are ignored.
+ // Vertical Sync is fixed at 16 lines.
+ return 16;
+ }
+ }
+
+ #endregion
+
+ #region Register Access
+
+ ///
+ /// Read Register (HD6845S & UM6845)
+ ///
+ ///
+ ///
+ private bool ReadRegister_Type0(ref int data)
+ {
+ // Type 0 - write only register returns 0 when it is read from
+ switch (AddressRegister)
+ {
+ // read-only registers
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ data = 0;
+ break;
+ case 12: // Start Address H (6-bit)
+ case 14: // Cursor H (6-bit)
+ case 16: // Light Pen H (6-bit)
+ data = Register[AddressRegister] & 0x3F;
+ break;
+ case 13: // Start Address L (8-bit)
+ case 15: // Cursor L (8-bit)
+ case 17: // Light Pen L (8-bit)
+ data = Register[AddressRegister];
+ break;
+ default:
+ if (AddressRegister > 17 && AddressRegister < 32)
+ {
+ data = 0;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ return true;
+ }
+
+ ///
+ /// Read Register (UM6845R)
+ ///
+ ///
+ ///
+ private bool ReadRegister_Type1(ref int data)
+ {
+ // Type 1 - write only register returns 0 when it is read from
+ switch (AddressRegister)
+ {
+ // read-only registers
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ data = 0;
+ break;
+ case 14: // Cursor H (6-bit)
+ data = Register[AddressRegister] & 0x3F;
+ break;
+ case 16: // Light Pen H (6-bit)
+ data = Register[AddressRegister] & 0x3F;
+ // reading from R16 resets bit6 of the status register
+ StatusRegister &= byte.MaxValue ^ (1 << 6);
+ break;
+ case 15: // Cursor L (8-bit)
+ data = Register[AddressRegister];
+ break;
+ case 17: // Light Pen L (8-bit)
+ data = Register[AddressRegister];
+ // reading from R17 resets bit6 of the status register
+ StatusRegister &= byte.MaxValue ^ (1 << 6);
+ break;
+ case 31: // Dummy Register. Datasheet describes this as N/A but CPCWIKI suggests that reading from it return 0x0FF;
+ data = 0xff;
+ break;
+ default:
+ if (AddressRegister > 17 && AddressRegister < 31)
+ {
+ data = 0;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ return true;
+ }
+
+ ///
+ /// Read Register (MC6845)
+ ///
+ ///
+ ///
+ private bool ReadRegister_Type2(ref int data)
+ {
+ switch (AddressRegister)
+ {
+ // read-only registers - type 2 does not respond
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ return false;
+ case 14: // Cursor H (6-bit)
+ data = Register[AddressRegister] & 0x3F;
+ break;
+ case 16: // Light Pen H (6-bit)
+ data = Register[AddressRegister] & 0x3F;
+ break;
+ case 15: // Cursor L (8-bit)
+ data = Register[AddressRegister];
+ break;
+ case 17: // Light Pen L (8-bit)
+ data = Register[AddressRegister];
+ break;
+ default:
+ if (AddressRegister > 17 && AddressRegister < 32)
+ {
+ data = 0;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ return true;
+ }
+
+ ///
+ /// Read Register (AMS40489 & pre-ASIC)
+ ///
+ ///
+ ///
+ private bool ReadRegister_Type3_4(ref int data)
+ {
+ // unsure of the register sizes at the moment
+ // for now we will just read and write 8-bit values
+ switch (AddressRegister)
+ {
+ case 6:
+ case 7:
+ case 14:
+ case 15:
+ case 22:
+ case 23:
+ case 30:
+ case 31:
+ // returns 0
+ data = 0;
+ break;
+ case 0:
+ case 8:
+ case 16:
+ case 24:
+ // returns R16
+ data = Register[16];
+ break;
+ case 1:
+ case 9:
+ case 17:
+ case 25:
+ // returns R17
+ data = Register[17];
+ break;
+ case 4:
+ case 12:
+ case 20:
+ case 28:
+ // returns R12
+ data = Register[12];
+ break;
+ case 5:
+ case 13:
+ case 21:
+ case 29:
+ // returns R13
+ data = Register[13];
+ break;
+ case 2:
+ case 10:
+ case 18:
+ case 26:
+ // ASIC status 1
+ data = AsicStatusRegister1;
+ break;
+ case 3:
+ case 11:
+ case 19:
+ case 27:
+ // ASIC status 2
+ data = AsicStatusRegister2;
+ break;
+ }
+ return true;
+ }
+
+ ///
+ /// Write Active Register (HD6845S & UM6845)
+ ///
+ ///
+ private void WriteRegister_Type0(int data)
+ {
+ byte v = (byte)data;
+ switch (AddressRegister)
+ {
+ case 0: // 8-bit registers
+ case 1:
+ case 2:
+ case 3:
+ case 13:
+ case 15:
+ Register[AddressRegister] = v;
+ break;
+ case 4: // 7-bit registers
+ case 6:
+ case 7:
+ case 10:
+ Register[AddressRegister] = (byte)(v & 0x7F);
+ break;
+ case 12: // 6-bit registers
+ case 14:
+ Register[AddressRegister] = (byte)(v & 0x3F);
+ break;
+ case 5: // 5-bit registers
+ case 9:
+ case 11:
+ Register[AddressRegister] = (byte)(v & 0x1F);
+ break;
+ case 8: // Interlace & skew masks bits 2 & 3
+ Register[AddressRegister] = (byte)(v & 0xF3);
+ break;
+ }
+ }
+
+ ///
+ /// Write Active Register (HD6845S & UM6845)
+ ///
+ ///
+ private void WriteRegister_Type1(int data)
+ {
+ byte v = (byte)data;
+ switch (AddressRegister)
+ {
+ case 0: // 8-bit registers
+ case 1:
+ case 2:
+ case 13:
+ case 15:
+ Register[AddressRegister] = v;
+ break;
+ case 4: // 7-bit registers
+ case 6:
+ case 7:
+ case 10:
+ Register[AddressRegister] = (byte)(v & 0x7F);
+ break;
+ case 12: // 6-bit registers
+ case 14:
+ Register[AddressRegister] = (byte)(v & 0x3F);
+ break;
+ case 5: // 5-bit registers
+ case 9:
+ case 11:
+ Register[AddressRegister] = (byte)(v & 0x1F);
+ break;
+ case 3: // 4-bit register
+ Register[AddressRegister] = (byte)(v & 0x0F);
+ break;
+ case 8: // Interlace & skew - 2bit
+ Register[AddressRegister] = (byte)(v & 0x03);
+ break;
+ }
+ }
+
+ ///
+ /// Write Active Register (MC6845)
+ ///
+ ///
+ private void WriteRegister_Type2(int data)
+ {
+ byte v = (byte)data;
+ switch (AddressRegister)
+ {
+ case 0: // 8-bit registers
+ case 1:
+ case 2:
+ case 13:
+ case 15:
+ Register[AddressRegister] = v;
+ break;
+ case 4: // 7-bit registers
+ case 6:
+ case 7:
+ case 10:
+ Register[AddressRegister] = (byte)(v & 0x7F);
+ break;
+ case 12: // 6-bit registers
+ case 14:
+ Register[AddressRegister] = (byte)(v & 0x3F);
+ break;
+ case 5: // 5-bit registers
+ case 9:
+ case 11:
+ Register[AddressRegister] = (byte)(v & 0x1F);
+ break;
+ case 3: // 4-bit register
+ Register[AddressRegister] = (byte)(v & 0x0F);
+ break;
+ case 8: // Interlace & skew - 2bit
+ Register[AddressRegister] = (byte)(v & 0x03);
+ break;
+ }
+ }
+
+ ///
+ /// Write Active Register (MC6845)
+ ///
+ ///
+ private void WriteRegister_Type3_4(int data)
+ {
+ // unsure of the register sizes at the moment
+ // for now we will just read and write 8-bit values
+ byte v = (byte)data;
+ switch (AddressRegister)
+ {
+ case 16:
+ case 17:
+ // read only registers
+ return;
+ default:
+ if (AddressRegister < 16)
+ {
+ Register[AddressRegister] = v;
+ }
+ else
+ {
+ // read only dummy registers
+ return;
+ }
+ break;
+ }
+ }
+
+ ///
+ /// Read Status Register (UM6845R)
+ /// This is fully implemented
+ ///
+ ///
+ ///
+ private bool ReadStatus_Type1(ref int data)
+ {
+ // Bit6: Set when latched LPEN strobe input is received / Reset when R17 or R16 is read from
+ // Bit5: Set when CRTC is in vblank / Reset when frame is started (VCC = 0)
+ data = StatusRegister & 0x60;
+ return true;
+ }
+
+ ///
+ /// Read Status Register (AMS40489 and costdown pre-ASIC)
+ /// Status Register is unavailable but attempts to read will return the currently
+ /// selected register data instead
+ ///
+ ///
+ ///
+ private bool ReadStatus_Type3_4(ref int data)
+ {
+ return ReadRegister(ref data);
+ }
+
+ ///
+ /// Read Status Register (HD6845S & UM6845)
+ /// No status register available
+ ///
+ ///
+ ///
+ private bool ReadStatus_Unavailable(ref int data)
+ {
+ return false;
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Clock Cycles
+
+ /* persistent switch signals */
+ bool s_VS;
+ bool s_HDISP;
+ bool s_VDISP;
+ bool s_HSYNC;
+
+ /* Other chip counters */
+ ///
+ /// Linear Address Generator counter latch
+ ///
+ int LAG_Counter_Latch;
+
+ ///
+ /// Linear Address Generator row counter latch
+ ///
+ int LAG_Counter_RowLatch;
+
+ ///
+ /// Linear Address Generator counter
+ ///
+ int LAG_Counter;
+
+ int DISPTMG_Delay_Counter;
+ int CUDISP_Delay_Counter;
+ int CUR_Field_Counter;
+
+ ///
+ /// Runs a generic CRTC cycle
+ ///
+ private void ClockCycle_Generic()
+ {
+
+ }
+
+ ///
+ /// Runs a Type0 Clock Cycle
+ /// Type 0 is the only chip that implements display and cursor skew
+ /// http://www.cpcwiki.eu/imgs/c/c0/Hd6845.hitachi.pdf
+ ///
+ private void ClockCycle_Type0()
+ {
+ /* non-persistent clock signals */
+ bool c_VT = false;
+ bool c_VTOTAL = false;
+ bool c_HMAX = false;
+ bool c_HDISP = false;
+ bool c_RASMAX = false;
+ bool c_VTAMAX = false;
+ bool c_HALFHTOTAL = false;
+ bool c_CURMATCH = false;
+ bool c_CURSKEW = false;
+
+ // we are going to clock everything individually but simulate everything
+ // happeneing at once (exactly like the chip would do)
+
+ /* Character Counter */
+ CharacterCTR++;
+ if (CharacterCTR == Register[H_DISPLAYED])
+ {
+ c_HDISP = true;
+ s_HDISP = false;
+ }
+ if (CharacterCTR == Register[H_TOTAL])
+ {
+ c_HMAX = true;
+ s_HDISP = true;
+ }
+ if (c_HMAX)
+ {
+ CharacterCTR = 0;
+ }
+ if (CharacterCTR == Register[H_SYNC_POS])
+ {
+ s_HSYNC = true;
+ }
+ if (CharacterCTR == Register[H_TOTAL] / 2)
+ {
+ c_HALFHTOTAL = true;
+ }
+
+ /* Horizontal Sync Width Counter */
+ if (s_HSYNC)
+ {
+ HorizontalSyncWidthCTR++;
+ if (HorizontalSyncWidthCTR == HSYNCWidth)
+ {
+ s_HSYNC = false;
+ HorizontalSyncWidthCTR = 0;
+ }
+ }
+
+ /* Raster Counter */
+ if (c_HMAX)
+ {
+ RasterCTR++;
+ if (RasterCTR == Register[MAX_SL_ADDRESS])
+ {
+ c_RASMAX = true;
+ RasterCTR = 0;
+ }
+ if (RasterCTR == Register[V_DISPLAYED])
+ {
+ // this will probably never happen, but the Hd6845 block diagram
+ // suggests that it is actually wired up this way
+ s_VDISP = false;
+ }
+ if (RasterCTR == Register[V_TOTAL_ADJUST])
+ {
+ c_VTAMAX = true;
+ }
+ }
+
+ /* Line Counter */
+ if (c_RASMAX)
+ {
+ LineCTR++;
+ if (LineCTR == Register[MAX_SL_ADDRESS])
+ {
+ // again this seems unneccessary, but the Hd6845 block diagram suggests that
+ // it is indeed wired this way
+ c_RASMAX = true;
+ RasterCTR = 0;
+ }
+ if (LineCTR == Register[V_DISPLAYED])
+ {
+ s_VDISP = false;
+ }
+ if (LineCTR == Register[V_TOTAL])
+ {
+ c_VTOTAL = true;
+ }
+ if (LineCTR == Register[V_SYNC_POS])
+ {
+ s_VS = true;
+ VerticalSyncWidthCTR = 0;
+ }
+ }
+
+ /* Vertical Sync Width Counter */
+ if (c_RASMAX && s_VS)
+ {
+ VerticalSyncWidthCTR++;
+ if (VerticalSyncWidthCTR == VSYNCWidth)
+ {
+ s_VS = false;
+ }
+ }
+
+ /* VTOTAL Control */
+ // todo - interlace logic
+ if (c_VTOTAL)
+ {
+ // vertical total has been reached
+ LineCTR = 0;
+ RasterCTR = 0;
+ // reload start address
+ StartAddressLatch = StartAddressRegisterValue;
+ c_VT = true;
+ s_VDISP = true;
+ }
+ if (c_VTAMAX)
+ {
+ // extra adjust rasterlines have all been outputted (or there were none)
+ // activate the Vdisplay switch
+ LineCTR = 0;
+ RasterCTR = 0;
+ c_VT = true;
+ s_VDISP = true;
+ }
+
+ /* Interlace Control */
+ // the interlace control generates the RA0-RA4 row selects
+ // this is based on the VT clock and VS switch,
+ // the value of the Raster Counter and the current interlace mode
+ // it is also clocked by a CO circuit when the horizontal character counter == HALF of the H_Total register value
+ // (this is for the interlace sync & video mode I believe)
+ // It also responsible for generating the signal on the VSYNC pin
+ if (s_VS)
+ {
+ _VSYNC = true;
+ }
+ if (c_VT)
+ {
+ _VSYNC = false;
+ }
+ if (c_HMAX)
+ {
+ RowSelects = RasterCTR;
+ }
+ if (c_HALFHTOTAL)
+ {
+ // we are half way horizontally across the screen
+ // interlace sync and video logic should go here (todo)
+ }
+
+ /* Linear Address Generator */
+ // counter is incremented with every CLK signal
+ LAG_Counter++;
+
+ if (c_HDISP)
+ {
+ // horizontal displayed reached - latch this address
+ // this is needed when moving to the next vertical character
+ // (the CRTC continues counting during the border and HSYNC periods)
+ LAG_Counter_Latch = LAG_Counter;
+ }
+ if (c_HMAX)
+ {
+ // end of the current raster line
+ if (c_RASMAX)
+ {
+ // this is last raster in the current line
+ // counter will continue on from the last row latch
+ LAG_Counter = LAG_Counter_Latch;
+ // latch this value to be used in all the raster lines in the following line
+ LAG_Counter_RowLatch = LAG_Counter;
+ }
+ else
+ {
+ // still within the line
+ // every raster will generate the same address sequence
+ LAG_Counter = LAG_Counter_RowLatch;
+ }
+ }
+ if (c_VT)
+ {
+ // the VT clock has been received from the VCONTROL
+ // LAG counters reset
+ LAG_Counter_Latch = 0;
+ LAG_Counter_RowLatch = 0;
+ LAG_Counter = 0;
+ }
+
+ // setup MA0-MA13 outputs based on internal counters and programmed start address
+ // (this is latched elsewhere and the timing of this is CRTC-type dependent)
+ LinearAddress = StartAddressLatch + LAG_Counter;
+
+ if (LinearAddress == CursorAddressRegisterValue)
+ {
+ c_CURMATCH = true;
+ }
+
+ /* HSYNC */
+ _HSYNC = s_HSYNC;
+
+ /* DISPTMG skew control */
+ if (DISPTMGSkew < 0)
+ {
+ // no output
+ _DISPTMG = false;
+ DISPTMG_Delay_Counter = 0;
+ }
+ else if (s_HDISP && s_VDISP)
+ {
+ // AND gate feeding the skew control is active
+ if (DISPTMGSkew > 0)
+ {
+ // skew value is set
+ if (DISPTMG_Delay_Counter >= DISPTMGSkew)
+ {
+ // we have finished the start skew
+ _DISPTMG = true;
+ }
+ else
+ {
+ // start skew still happening
+ DISPTMG_Delay_Counter++;
+ _DISPTMG = false;
+ }
+ }
+ else
+ {
+ // no skew set
+ _DISPTMG = true;
+ }
+ }
+ else
+ {
+ // AND gate is inactive - process any possible skew leadout
+ if (DISPTMGSkew > 0)
+ {
+ // skew value is set
+ if (DISPTMG_Delay_Counter > 0)
+ {
+ // leadout skew is still in effect
+ DISPTMG_Delay_Counter--;
+ _DISPTMG = true;
+ }
+ else
+ {
+ // leadout skew has finished
+ DISPTMG_Delay_Counter = 0;
+ _DISPTMG = false;
+ }
+ }
+ else
+ {
+ // no skew programmed
+ DISPTMG_Delay_Counter = 0;
+ _DISPTMG = false;
+ }
+ }
+
+ /* Cursor Control */
+ if (s_HDISP && s_VDISP)
+ {
+ // AND gate is active - cursor control is clocked
+ CUR_Field_Counter++;
+ bool curOutput = false;
+
+ // info from registers
+ var curStartRaster = Register[CURSOR_START] & 0x1f;
+ var curEndRaster = Register[CURSOR_END] & 0x1f;
+ var curDisplayMode = (Register[CURSOR_START] & 0x60) >> 5;
+
+ switch (curDisplayMode)
+ {
+ // Non-blink
+ case 0:
+ if (RasterCTR >= curStartRaster && RasterCTR <= curEndRaster && c_CURMATCH)
+ curOutput = true;
+ break;
+ // Cursor non-display
+ case 1:
+ curOutput = false;
+ break;
+ // Blink 1/16 field rate
+ case 2:
+ // not yet implemented
+ if (RasterCTR >= curStartRaster && RasterCTR <= curEndRaster && c_CURMATCH)
+ curOutput = true;
+ break;
+ // Blink 1/32 field rate
+ case 3:
+ // not yet implemented
+ if (RasterCTR >= curStartRaster && RasterCTR <= curEndRaster && c_CURMATCH)
+ curOutput = true;
+ break;
+ }
+
+ if (curOutput)
+ {
+ c_CURSKEW = true;
+ }
+ }
+ else
+ {
+ // end of the display
+ CUR_Field_Counter = 0;
+ }
+
+ /* Cursor Skew Control */
+ if (c_CURSKEW)
+ {
+ if (CUDISPSkew < 0)
+ {
+ // no output
+ _CUDISP = false;
+ CUDISP_Delay_Counter = 0;
+ }
+ else
+ {
+ if (CUDISPSkew > 0)
+ {
+ // skew value is set
+ if (CUDISP_Delay_Counter >= CUDISPSkew)
+ {
+ // we have finished the start skew
+ _CUDISP = true;
+ }
+ else
+ {
+ // start skew still happening
+ CUDISP_Delay_Counter++;
+ _CUDISP = false;
+ }
+ }
+ else
+ {
+ // no skew set
+ _CUDISP = true;
+ }
+ }
+ }
+ else
+ {
+ // process any possible skew leadout
+ if (CUDISPSkew > 0)
+ {
+ // skew value is set
+ if (CUDISP_Delay_Counter > 0)
+ {
+ // leadout skew is still in effect
+ CUDISP_Delay_Counter--;
+ _CUDISP = true;
+ }
+ else
+ {
+ // leadout skew has finished
+ CUDISP_Delay_Counter = 0;
+ _CUDISP = false;
+ }
+ }
+ else
+ {
+ // no skew programmed
+ CUDISP_Delay_Counter = 0;
+ _CUDISP = false;
+ }
+ }
+
+ /* Light Pen Control */
+ if (LPSTB)
+ {
+ // strobe has been detected
+ // latch the current address into the light pen registers (R16 & R17)
+ Register[LIGHT_PEN_L] = (byte)(LinearAddress & 0xff);
+ Register[LIGHT_PEN_H] = (byte)((LinearAddress >> 8) & 0x3f);
+
+ _LPSTB = false;
+ }
+ }
+
+ ///
+ /// Runs a Type1 Clock Cycle
+ /// There doesnt seem to be a block diagram in the datasheets for this, so will use the type0
+ /// implementation with a few changes
+ /// Type 1 has no skew program bit functionality
+ /// However, it does implement a status register
+ ///
+ private void ClockCycle_Type1()
+ {
+ /* non-persistent clock signals */
+ bool c_VT = false;
+ bool c_VTOTAL = false;
+ bool c_HMAX = false;
+ bool c_HDISP = false;
+ bool c_RASMAX = false;
+ bool c_VTAMAX = false;
+ bool c_HALFHTOTAL = false;
+ bool c_CURMATCH = false;
+
+ // we are going to clock everything individually but simulate everything
+ // happeneing at once (exactly like the chip would do)
+
+ /* Character Counter */
+ CharacterCTR++;
+ if (CharacterCTR == Register[H_DISPLAYED])
+ {
+ c_HDISP = true;
+ s_HDISP = false;
+ }
+ if (CharacterCTR == Register[H_TOTAL])
+ {
+ c_HMAX = true;
+ s_HDISP = true;
+ }
+ if (c_HMAX)
+ {
+ CharacterCTR = 0;
+ }
+ if (CharacterCTR == Register[H_SYNC_POS])
+ {
+ s_HSYNC = true;
+ }
+ if (CharacterCTR == Register[H_TOTAL] / 2)
+ {
+ c_HALFHTOTAL = true;
+ }
+
+ /* Horizontal Sync Width Counter */
+ if (s_HSYNC)
+ {
+ HorizontalSyncWidthCTR++;
+ if (HorizontalSyncWidthCTR == HSYNCWidth)
+ {
+ s_HSYNC = false;
+ HorizontalSyncWidthCTR = 0;
+ }
+ }
+
+ /* Raster Counter */
+ if (c_HMAX)
+ {
+ RasterCTR++;
+ if (RasterCTR == Register[MAX_SL_ADDRESS])
+ {
+ c_RASMAX = true;
+ RasterCTR = 0;
+ }
+ if (RasterCTR == Register[V_DISPLAYED])
+ {
+ // this will probably never happen, but the Hd6845 block diagram
+ // suggests that it is actually wired up this way
+ s_VDISP = false;
+ }
+ if (RasterCTR == Register[V_TOTAL_ADJUST])
+ {
+ c_VTAMAX = true;
+ }
+ }
+
+ /* Line Counter */
+ if (c_RASMAX)
+ {
+ LineCTR++;
+ if (LineCTR == Register[MAX_SL_ADDRESS])
+ {
+ // again this seems unneccessary, but the Hd6845 block diagram suggests that
+ // it is indeed wired this way
+ c_RASMAX = true;
+ RasterCTR = 0;
+ }
+ if (LineCTR == Register[V_DISPLAYED])
+ {
+ s_VDISP = false;
+ }
+ if (LineCTR == Register[V_TOTAL])
+ {
+ c_VTOTAL = true;
+ }
+ if (LineCTR == Register[V_SYNC_POS])
+ {
+ s_VS = true;
+ VerticalSyncWidthCTR = 0;
+ }
+ }
+
+ /* Vertical Sync Width Counter */
+ if (c_RASMAX && s_VS)
+ {
+ VerticalSyncWidthCTR++;
+ if (VerticalSyncWidthCTR == VSYNCWidth)
+ {
+ s_VS = false;
+ }
+ }
+
+ /* VTOTAL Control */
+ // todo - interlace logic
+ if (c_VTOTAL)
+ {
+ // vertical total has been reached
+ LineCTR = 0;
+ RasterCTR = 0;
+ // reload start address
+ StartAddressLatch = StartAddressRegisterValue;
+ c_VT = true;
+ s_VDISP = true;
+ }
+ if (c_VTAMAX)
+ {
+ // extra adjust rasterlines have all been outputted (or there were none)
+ // activate the Vdisplay switch
+ LineCTR = 0;
+ RasterCTR = 0;
+ c_VT = true;
+ s_VDISP = true;
+ }
+
+ /* Interlace Control */
+ // the interlace control generates the RA0-RA4 row selects
+ // this is based on the VT clock and VS switch,
+ // the value of the Raster Counter and the current interlace mode
+ // it is also clocked by a CO circuit when the horizontal character counter == HALF of the H_Total register value
+ // (this is for the interlace sync & video mode I believe)
+ // It also responsible for generating the signal on the VSYNC pin
+ if (s_VS)
+ {
+ _VSYNC = true;
+ // status register bit 5 'vertical blanking'
+ StatusRegister |= 1 << 5;
+ }
+ if (c_VT)
+ {
+ _VSYNC = false;
+ // status register bit 5 'vertical blanking'
+ StatusRegister &= byte.MaxValue ^ (1 << 5);
+ }
+ if (c_HMAX)
+ {
+ RowSelects = RasterCTR;
+ }
+ if (c_HALFHTOTAL)
+ {
+ // we are half way horizontally across the screen
+ // interlace sync and video logic should go here (todo)
+ }
+
+ /* Linear Address Generator */
+ // counter is incremented with every CLK signal
+ LAG_Counter++;
+
+ if (c_HDISP)
+ {
+ // horizontal displayed reached - latch this address
+ // this is needed when moving to the next vertical character
+ // (the CRTC continues counting during the border and HSYNC periods)
+ LAG_Counter_Latch = LAG_Counter;
+ }
+ if (c_HMAX)
+ {
+ // end of the current raster line
+ if (c_RASMAX)
+ {
+ // this is last raster in the current line
+ // counter will continue on from the last row latch
+ LAG_Counter = LAG_Counter_Latch;
+ // latch this value to be used in all the raster lines in the following line
+ LAG_Counter_RowLatch = LAG_Counter;
+ }
+ else
+ {
+ // still within the line
+ // every raster will generate the same address sequence
+ LAG_Counter = LAG_Counter_RowLatch;
+ }
+ }
+ if (c_VT)
+ {
+ // the VT clock has been received from the VCONTROL
+ // LAG counters reset
+ LAG_Counter_Latch = 0;
+ LAG_Counter_RowLatch = 0;
+ LAG_Counter = 0;
+ }
+
+ // setup MA0-MA13 outputs based on internal counters and programmed start address
+ // (this is latched elsewhere and the timing of this is CRTC-type dependent)
+ LinearAddress = StartAddressLatch + LAG_Counter;
+
+ if (LinearAddress == CursorAddressRegisterValue)
+ {
+ c_CURMATCH = true;
+ }
+
+ /* HSYNC */
+ _HSYNC = s_HSYNC;
+
+ /* DISPTMG */
+ if (s_HDISP && s_VDISP)
+ {
+ _DISPTMG = true;
+ }
+ else
+ {
+ _DISPTMG = false;
+ }
+
+ /* Cursor Control */
+ if (s_HDISP && s_VDISP)
+ {
+ // AND gate is active - cursor control is clocked
+ CUR_Field_Counter++;
+ bool curOutput = false;
+
+ // info from registers
+ var curStartRaster = Register[CURSOR_START] & 0x1f;
+ var curEndRaster = Register[CURSOR_END] & 0x1f;
+ var curDisplayMode = (Register[CURSOR_START] & 0x60) >> 5;
+
+ switch (curDisplayMode)
+ {
+ // Non-blink
+ case 0:
+ if (RasterCTR >= curStartRaster && RasterCTR <= curEndRaster && c_CURMATCH)
+ curOutput = true;
+ break;
+ // Cursor non-display
+ case 1:
+ curOutput = false;
+ break;
+ // Blink 1/16 field rate
+ case 2:
+ // not yet implemented
+ if (RasterCTR >= curStartRaster && RasterCTR <= curEndRaster && c_CURMATCH)
+ curOutput = true;
+ break;
+ // Blink 1/32 field rate
+ case 3:
+ // not yet implemented
+ if (RasterCTR >= curStartRaster && RasterCTR <= curEndRaster && c_CURMATCH)
+ curOutput = true;
+ break;
+ }
+
+ if (curOutput)
+ {
+ _CUDISP = true;
+ }
+ else
+ {
+ _CUDISP = false;
+ }
+ }
+ else
+ {
+ // end of the display
+ CUR_Field_Counter = 0;
+ }
+
+ /* Light Pen Control */
+ if (LPSTB)
+ {
+ // strobe has been detected
+ // latch the current address into the light pen registers (R16 & R17)
+ Register[LIGHT_PEN_L] = (byte)(LinearAddress & 0xff);
+ Register[LIGHT_PEN_H] = (byte)((LinearAddress >> 8) & 0x3f);
+
+ // set the 'LPEN register full' bit in the status register
+ StatusRegister |= 1 << 6;
+
+ _LPSTB = false;
+ }
+ }
+
+ ///
+ /// Runs a Type2 Clock Cycle
+ /// The MC6845 does have a functional block diagram
+ /// http://www.cpcwiki.eu/imgs/d/da/Mc6845.motorola.pdf
+ /// The implementation looks a little simpler than the type 0
+ /// It has NO status register and NO skew program bit support
+ /// HOWEVER, there are some glaring ommissions in the block diagram,
+ /// so I am using a modified type 0/1 implementation for now
+ ///
+ private void ClockCycle_Type2()
+ {
+ /* non-persistent clock signals */
+ bool c_VT = false;
+ bool c_VTOTAL = false;
+ bool c_HMAX = false;
+ bool c_HDISP = false;
+ bool c_RASMAX = false;
+ bool c_VTAMAX = false;
+ bool c_HALFHTOTAL = false;
+ bool c_CURMATCH = false;
+
+ // we are going to clock everything individually but simulate everything
+ // happeneing at once (exactly like the chip would do)
+
+ /* Character Counter */
+ CharacterCTR++;
+ if (CharacterCTR == Register[H_DISPLAYED])
+ {
+ c_HDISP = true;
+ s_HDISP = false;
+ }
+ if (CharacterCTR == Register[H_TOTAL])
+ {
+ c_HMAX = true;
+ s_HDISP = true;
+ }
+ if (c_HMAX)
+ {
+ CharacterCTR = 0;
+ }
+ if (CharacterCTR == Register[H_SYNC_POS])
+ {
+ s_HSYNC = true;
+ }
+ if (CharacterCTR == Register[H_TOTAL] / 2)
+ {
+ c_HALFHTOTAL = true;
+ }
+
+ /* Horizontal Sync Width Counter */
+ if (s_HSYNC)
+ {
+ HorizontalSyncWidthCTR++;
+ if (HorizontalSyncWidthCTR == HSYNCWidth)
+ {
+ s_HSYNC = false;
+ HorizontalSyncWidthCTR = 0;
+ }
+ }
+
+ /* Raster Counter */
+ if (c_HMAX)
+ {
+ RasterCTR++;
+ if (RasterCTR == Register[MAX_SL_ADDRESS])
+ {
+ c_RASMAX = true;
+ RasterCTR = 0;
+ }
+ if (RasterCTR == Register[V_DISPLAYED])
+ {
+ // this will probably never happen, but the Hd6845 block diagram
+ // suggests that it is actually wired up this way
+ s_VDISP = false;
+ }
+ if (RasterCTR == Register[V_TOTAL_ADJUST])
+ {
+ c_VTAMAX = true;
+ }
+ }
+
+ /* Line Counter */
+ if (c_RASMAX)
+ {
+ LineCTR++;
+ if (LineCTR == Register[MAX_SL_ADDRESS])
+ {
+ // again this seems unneccessary, but the Hd6845 block diagram suggests that
+ // it is indeed wired this way
+ c_RASMAX = true;
+ RasterCTR = 0;
+ }
+ if (LineCTR == Register[V_DISPLAYED])
+ {
+ s_VDISP = false;
+ }
+ if (LineCTR == Register[V_TOTAL])
+ {
+ c_VTOTAL = true;
+ }
+ if (LineCTR == Register[V_SYNC_POS])
+ {
+ s_VS = true;
+ VerticalSyncWidthCTR = 0;
+ }
+ }
+
+ /* Vertical Sync Width Counter */
+ if (c_RASMAX && s_VS)
+ {
+ VerticalSyncWidthCTR++;
+ if (VerticalSyncWidthCTR == VSYNCWidth)
+ {
+ s_VS = false;
+ }
+ }
+
+ /* VTOTAL Control */
+ // todo - interlace logic
+ if (c_VTOTAL)
+ {
+ // vertical total has been reached
+ LineCTR = 0;
+ RasterCTR = 0;
+ // reload start address
+ StartAddressLatch = StartAddressRegisterValue;
+ c_VT = true;
+ s_VDISP = true;
+ }
+ if (c_VTAMAX)
+ {
+ // extra adjust rasterlines have all been outputted (or there were none)
+ // activate the Vdisplay switch
+ LineCTR = 0;
+ RasterCTR = 0;
+ c_VT = true;
+ s_VDISP = true;
+ }
+
+ /* Interlace Control */
+ // the interlace control generates the RA0-RA4 row selects
+ // this is based on the VT clock and VS switch,
+ // the value of the Raster Counter and the current interlace mode
+ // it is also clocked by a CO circuit when the horizontal character counter == HALF of the H_Total register value
+ // (this is for the interlace sync & video mode I believe)
+ // It also responsible for generating the signal on the VSYNC pin
+ if (s_VS)
+ {
+ _VSYNC = true;
+ }
+ if (c_VT)
+ {
+ _VSYNC = false;
+ }
+ if (c_HMAX)
+ {
+ RowSelects = RasterCTR;
+ }
+ if (c_HALFHTOTAL)
+ {
+ // we are half way horizontally across the screen
+ // interlace sync and video logic should go here (todo)
+ }
+
+ /* Linear Address Generator */
+ // counter is incremented with every CLK signal
+ LAG_Counter++;
+
+ if (c_HDISP)
+ {
+ // horizontal displayed reached - latch this address
+ // this is needed when moving to the next vertical character
+ // (the CRTC continues counting during the border and HSYNC periods)
+ LAG_Counter_Latch = LAG_Counter;
+ }
+ if (c_HMAX)
+ {
+ // end of the current raster line
+ if (c_RASMAX)
+ {
+ // this is last raster in the current line
+ // counter will continue on from the last row latch
+ LAG_Counter = LAG_Counter_Latch;
+ // latch this value to be used in all the raster lines in the following line
+ LAG_Counter_RowLatch = LAG_Counter;
+ }
+ else
+ {
+ // still within the line
+ // every raster will generate the same address sequence
+ LAG_Counter = LAG_Counter_RowLatch;
+ }
+ }
+ if (c_VT)
+ {
+ // the VT clock has been received from the VCONTROL
+ // LAG counters reset
+ LAG_Counter_Latch = 0;
+ LAG_Counter_RowLatch = 0;
+ LAG_Counter = 0;
+ }
+
+ // setup MA0-MA13 outputs based on internal counters and programmed start address
+ // (this is latched elsewhere and the timing of this is CRTC-type dependent)
+ LinearAddress = StartAddressLatch + LAG_Counter;
+
+ if (LinearAddress == CursorAddressRegisterValue)
+ {
+ c_CURMATCH = true;
+ }
+
+ /* HSYNC */
+ _HSYNC = s_HSYNC;
+
+ /* DISPTMG */
+ if (s_HDISP && s_VDISP)
+ {
+ _DISPTMG = true;
+ }
+ else
+ {
+ _DISPTMG = false;
+ }
+
+ /* Cursor Control */
+ if (s_HDISP && s_VDISP)
+ {
+ // AND gate is active - cursor control is clocked
+ CUR_Field_Counter++;
+ bool curOutput = false;
+
+ // info from registers
+ var curStartRaster = Register[CURSOR_START] & 0x1f;
+ var curEndRaster = Register[CURSOR_END] & 0x1f;
+ var curDisplayMode = (Register[CURSOR_START] & 0x60) >> 5;
+
+ switch (curDisplayMode)
+ {
+ // Non-blink
+ case 0:
+ if (RasterCTR >= curStartRaster && RasterCTR <= curEndRaster && c_CURMATCH)
+ curOutput = true;
+ break;
+ // Cursor non-display
+ case 1:
+ curOutput = false;
+ break;
+ // Blink 1/16 field rate
+ case 2:
+ // not yet implemented
+ if (RasterCTR >= curStartRaster && RasterCTR <= curEndRaster && c_CURMATCH)
+ curOutput = true;
+ break;
+ // Blink 1/32 field rate
+ case 3:
+ // not yet implemented
+ if (RasterCTR >= curStartRaster && RasterCTR <= curEndRaster && c_CURMATCH)
+ curOutput = true;
+ break;
+ }
+
+ if (curOutput)
+ {
+ _CUDISP = true;
+ }
+ else
+ {
+ _CUDISP = false;
+ }
+ }
+ else
+ {
+ // end of the display
+ CUR_Field_Counter = 0;
+ }
+
+ /* Light Pen Control */
+ if (LPSTB)
+ {
+ // strobe has been detected
+ // latch the current address into the light pen registers (R16 & R17)
+ Register[LIGHT_PEN_L] = (byte)(LinearAddress & 0xff);
+ Register[LIGHT_PEN_H] = (byte)((LinearAddress >> 8) & 0x3f);
+
+ _LPSTB = false;
+ }
+ }
+
+ ///
+ /// Runs a Type3or4 Clock Cycle
+ ///
+ private void ClockCycle_Type3_4()
+ {
+
+ }
+
+ #endregion
+
+ #region Enums & Constants
+
+ /* Horizontal Timing Register Constants */
+ ///
+ /// 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.
+ ///
+ private const int H_TOTAL = 0;
+ ///
+ /// This 8 bit write-only register determines the number of displayed characters per horizontal line.
+ ///
+ private const int H_DISPLAYED = 1;
+ ///
+ /// This 8 bit write-only register determines the horizontal sync postiion on the horizontal line.
+ ///
+ private const int H_SYNC_POS = 2;
+ ///
+ /// 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
+ ///
+ private const int SYNC_WIDTHS = 3;
+
+ /* Vertical Timing Register Constants */
+ ///
+ /// 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.
+ ///
+ private const int V_TOTAL = 4;
+ private const int V_TOTAL_ADJUST = 5;
+ ///
+ /// This 7 bit write-only register determines the number of displayed character rows on the CRT screen, and is programmed in character row times.
+ ///
+ private const int V_DISPLAYED = 6;
+ ///
+ /// This 7 bit write-only register determines the vertical sync position with respect to the reference.It is programmed in character row times.
+ ///
+ private const int V_SYNC_POS = 7;
+ ///
+ /// 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.
+ ///
+ private const int INTERLACE_MODE = 8;
+ ///
+ /// 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.
+ ///
+ private const int MAX_SL_ADDRESS = 9;
+
+ /* Other Main Register Constants */
+ ///
+ /// 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.
+ ///
+ private const int CURSOR_START = 10;
+ ///
+ /// This 5 bit write-only register sets the cursor end scan line
+ ///
+ private const int CURSOR_END = 11;
+ ///
+ /// 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.
+ ///
+ private const int START_ADDR_H = 12;
+ private const int START_ADDR_L = 13;
+ ///
+ /// This 14 bit read/write register stores the cursor location.This register consists of an 8 bit lower and 6 bit higher register.
+ ///
+ private const int CURSOR_H = 14;
+ private const int CURSOR_L = 15;
+ ///
+ /// 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.
+ ///
+ private const int LIGHT_PEN_H = 16;
+ private const int LIGHT_PEN_L = 17;
+
+ ///
+ /// The types of CRCT chip found in the CPC range
+ ///
+ public enum CRTCType
+ {
+ HD6845S = 0,
+ UM6845 = 0,
+ UM6845R = 1,
+ MC6845 = 2,
+ AMS40489 = 3,
+ AMS40226 = 4
+ }
+
+ ///
+ /// The available interlace modes in the CRCT
+ ///
+ private enum InterlaceMode
+ {
+ NormalSyncMode,
+ InterlaceSyncMode,
+ InterlaceSyncAndVideoMode
+ }
+
+ ///
+ /// Cursor display modes
+ ///
+ private enum CursorControl
+ {
+ NonBlink,
+ CursorNonDisplay,
+ Blink1_16,
+ Blink1_32
+ }
+
+ ///
+ /// Vid Display RAM Addressing Mode
+ ///
+ private enum VideoDisplayRAMAddressing
+ {
+ StraightBinary = 0,
+ RowColumn = 1
+ }
+
+ ///
+ /// Vid Display RAM Acccess Mode
+ ///
+ private enum VideoDisplayRAMAccess
+ {
+ SharedMemory,
+ TransparentMemory
+ }
+
+ #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("_DISPTMG", ref _DISPTMG);
+ ser.Sync("_CUDISP", ref _CUDISP);
+ 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("StatusRegister", ref StatusRegister);
+ ser.Sync("_CharacterCTR", ref _CharacterCTR);
+ ser.Sync("_HorizontalSyncWidthCTR", ref _HorizontalSyncWidthCTR);
+ ser.Sync("_LineCTR", ref _LineCTR);
+ ser.Sync("_RasterCTR", ref _RasterCTR);
+ ser.Sync("StartAddressLatch)", ref StartAddressLatch);
+ //ser.Sync("VDisplay", ref VDisplay);
+ //ser.Sync("HDisplay", ref HDisplay);
+ ser.Sync("RowSelects", ref RowSelects);
+ ser.Sync("DISPTMG_Delay_Counter", ref DISPTMG_Delay_Counter);
+ ser.Sync("CUDISP_Delay_Counter", ref CUDISP_Delay_Counter);
+ ser.Sync("AsicStatusRegister1", ref AsicStatusRegister1);
+ ser.Sync("AsicStatusRegister2", ref AsicStatusRegister2);
+ ser.Sync("LAG_Counter", ref LAG_Counter);
+ ser.Sync("LAG_Counter_Latch", ref LAG_Counter_Latch);
+ ser.Sync("LAG_Counter_RowLatch", ref LAG_Counter_RowLatch);
+ ser.Sync("s_VS", ref s_VS);
+ ser.Sync("s_HDISP", ref s_VS);
+ ser.Sync("s_VDISP", ref s_VDISP);
+ ser.Sync("s_HSYNC", ref s_HSYNC);
+ ser.Sync("CUR_Field_Counter", ref CUR_Field_Counter);
+ //ser.Sync("VS", ref VS);
+ ser.EndSection();
+
+ /*
+ * int ;
+ int ;
+ * */
+ }
+
+ #endregion
+ }
+}