From aec496b561172f9ef831cc071fd2b3693c9d3e2a Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 6 Jul 2018 14:46:07 +0100 Subject: [PATCH] CPCHawk: CRCT implementaton --- .../AmstradCPC/Hardware/CRCT/CRCT_6845.cs | 474 +++++++++++++----- .../AmstradCPC/Machine/GateArrayBase.cs | 2 +- .../Computers/AmstradCPC/Machine/PPIBase.cs | 2 +- 3 files changed, 364 insertions(+), 114 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/CRCT/CRCT_6845.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/CRCT/CRCT_6845.cs index 38511a61a0..787e3d0026 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/CRCT/CRCT_6845.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/CRCT/CRCT_6845.cs @@ -1,17 +1,13 @@ using BizHawk.Common; using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { /// /// Cathode Ray Tube Controller Chip - 6845 /// http://www.cpcwiki.eu/index.php/CRTC - /// + /// https://web.archive.org/web/20170501112330/http://www.grimware.org/doku.php/documentations/devices/crtc /// public class CRCT_6845 : IPortIODevice { @@ -32,7 +28,33 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC #endregion - #region State + #region Public Lines + + /// + /// Denotes that HSYNC is active + /// + public bool HSYNC = false; + + /// + /// Denotes that VSYNC is active + /// + public bool VSYNC = false; + + /// + /// TRUE: bits outputted to screen from video RAM + /// FALSE: current border colour is outputted + /// + public bool DISPTMG = true; + + /// + /// 16-bit memory address lines + /// The gate array uses this to grab the correct bits from video RAM + /// + public short MA; + + #endregion + + #region Internal Registers and State /* Index Register Name Range CPC Setting Notes @@ -60,6 +82,124 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC /// private byte[] Regs = new byte[18]; + // CRTC Register constants + /// + /// R0: Horizontal total character number + /// Unit: Character + /// Notes: Defines the width of a scanline + /// + public const int HOR_TOTAL = 0; + /// + /// R1: Horizontal displayed character number + /// Unit: Character + /// Notes: Defines when DISPEN goes OFF on the scanline + /// + public const int HOR_DISPLAYED = 1; + /// + /// R2: Position of horizontal sync. pulse + /// Unit: Character + /// Notes: Defines when the HSync goes ON on the scanline + /// + public const int HOR_SYNC_POS = 2; + /// + /// R3: Width of horizontal/vertical sync. pulses + /// Unit: Character + /// Notes: VSync width can only be changed on type 3 and 4 + /// + public const int HOR_AND_VER_SYNC_WIDTHS = 3; + /// + /// R4: Vertical total Line character number + /// Unit: Character + /// Notes: Defines the height of a screen + /// + public const int VER_TOTAL = 4; + /// + /// R5: Vertical raster adjust + /// Unit: Scanline + /// Notes: Defines additionnal scanlines at the end of a screen + /// can be used for smooth vertical scrolling on CPC + /// + public const int VER_TOTAL_ADJUST = 5; + /// + /// R6: Vertical displayed character number + /// Unit: Character + /// Notes: Define when DISPEN remains OFF until a new screen starts + /// Height of displayed screen in characters (Once vertical character count (VCC) matches this value, DISPTMG is set to 1) + /// + public const int VER_DISPLAYED = 6; + /// + /// R7: Position of vertical sync. pulse + /// Unit: Character + /// Notes: Define when the VSync goes ON on a screen + /// + public const int VER_SYNC_POS = 7; + /// + /// R8: Interlaced mode + /// Unit: + /// Notes: 00: No interlace; 01: Interlace Sync Raster Scan Mode; 10: No Interlace; 11: Interlace Sync and Video Raster Scan Mode + /// (crct type specific) + /// + public const int INTERLACE_SKEW = 8; + /// + /// R9: Maximum raster + /// Unit: Scanline + /// Notes: Defines the height of a CRTC-Char in scanlines + /// + public const int MAX_RASTER_ADDR = 9; + /// + /// R10: Cursor start raster + /// Unit: + /// Notes: Cursor not used on CPC. + /// (xBP00000) + /// B = Blink On/Off; + /// P = Blink Period Control (Slow/Fast). + /// Sets first raster row of character that cursor is on to invert + /// + public const int CUR_START_RASTER = 10; + /// + /// R11: Cursor end + /// Unit: + /// Notes: Sets last raster row of character that cursor is on to invert + /// + public const int CUR_END_RASTER = 11; + /// + /// R12: Display Start Address (High) + /// Unit: + /// Notes: Define the MSB of MA when a CRTC-screen starts + /// + public const int DISP_START_ADDR_H = 12; + /// + /// R13: Display Start Address (Low) + /// Unit: + /// Notes: Define the LSB of MA when a CRTC-screen starts + /// Allows you to offset the start of screen memory for hardware scrolling, and if using memory from address &0000 with the firmware. + /// + public const int DISP_START_ADDR_L = 13; + /// + /// R14: Cursor Address (High) + /// Unit: + /// Notes: Useless on the Amstrad CPC/Plus (text-mode is not wired) + /// + public const int CUR_ADDR_H = 14; + /// + /// R15: Cursor Address (Low) + /// Unit: + /// Notes: Useless on the Amstrad CPC/Plus (text-mode is not wired) + /// + public const int CUR_ADDR_L = 15; + /// + /// R16: Light Pen Address (High) + /// Unit: + /// Notes: Hold the MSB of the cursor position when the lightpen was ON + /// + public const int LPEN_ADDR_H = 16; + /// + /// R17: Light Pen Address (Low) + /// Unit: + /// Notes: Hold the LSB of the cursor position when the lightpen was ON + /// + public const int LPEN_ADDR_L = 17; + /// /// The currently selected register /// @@ -67,8 +207,10 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC /// /// CPC register default values + /// Taken from https://web.archive.org/web/20170501112330/http://www.grimware.org/doku.php/documentations/devices/crtc + /// (The defaults values given here are those programmed by the firmware ROM after a cold/warm boot of the CPC/Plus) /// - private byte[] RegDefaults = new byte[] { 63, 40, 52, 52, 20, 8, 16, 19, 0, 11, 73, 10, 0, 0, 0, 0, 0, 0 }; + private byte[] RegDefaults = new byte[] { 63, 40, 46, 0x8e, 38, 0, 25, 30, 0, 7, 0, 0, 0x20, 0x00, 0, 0, 0, 0 }; /// /// Register masks @@ -76,111 +218,166 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC private byte[] CPCMask = new byte[] { 255, 255, 255, 255, 127, 31, 127, 126, 3, 31, 31, 31, 63, 255, 63, 255, 63, 255 }; /// - /// CRTC Register constants + /// Horizontal Character Count /// - public const int HOR_TOTAL = 0; - public const int HOR_DISPLAYED = 1; - public const int HOR_SYNC_POS = 2; - public const int HOR_AND_VER_SYNC_WIDTHS = 3; - public const int VER_TOTAL = 4; - public const int VER_TOTAL_ADJUST = 5; - public const int VER_DISPLAYED = 6; - public const int VER_SYNC_POS = 7; - public const int INTERLACE_SKEW = 8; - public const int MAX_RASTER_ADDR = 9; - public const int CUR_START_RASTER = 10; - public const int CUR_END_RASTER = 11; - public const int DISP_START_ADDR_H = 12; - public const int DISP_START_ADDR_L = 13; - public const int CUR_ADDR_H = 14; - public const int CUR_ADDR_L = 15; - public const int LPEN_ADDR_H = 16; - public const int LPEN_ADDR_L = 17; + private int HCC; - public int horSyncWidth; - public int verSyncWidth; + /// + /// Vertical Character Count + /// + private int VCC; - public int currCol; - public int currLineInRow; - public int currRow; + /// + /// Vertical Scanline Count + /// + private int VLC; - public bool isHSYNC; - public bool isVSYNC; + /// + /// Internal cycle counter + /// + private int CycleCounter; - public int HSYNCcnt; - public int VSYNCcnt; + /// + /// Signs that we have finished the last character row + /// + private bool EndOfScreen; - public bool DISPTMG; + /// + /// HSYNC pulse width (in characters) + /// + private int HSYNCWidth; - private long LastTCycle; + /// + /// Internal HSYNC counter + /// + private int HSYNCCounter; + + /// + /// VSYNC pulse width (in characters) + /// + private int VSYNCWidth; + + /// + /// Internal VSYNC counter + /// + private int VSYNCCounter; #endregion #region Public Methods /// - /// The CRTC runs at 1MHz, so effectively every 4 T-States + /// Runs a CRCT clock cycle + /// This should be called at 1Mhz / 1us / every 4 uncontended CPU t-states /// - public void CycleClock() + public void ClockCycle() { - if (HSYNCcnt > 0) + if (HSYNC) { - HSYNCcnt--; - if (HSYNCcnt == 0) + // HSYNC in progress + HSYNCCounter++; + + if (HSYNCCounter == HSYNCWidth) { - // HSYNC is over - isHSYNC = false; + // end of HSYNC + HSYNCCounter = 0; + HSYNC = false; } } - // move one column - currCol++; + // move one horizontal character + HCC++; - // scanline - if (currCol == Regs[HOR_TOTAL] + 1) + // check for DISPTMG + if (HCC >= Regs[HOR_DISPLAYED] + 1) { - // we have reached the end of the current scanline - currCol = 0; - - // take care of VSYNC - if (VSYNCcnt > 0) - { - VSYNCcnt--; - if (VSYNCcnt == 0) - { - // VSYNC is over - isVSYNC = false; - } - } - - // increment row - currLineInRow++; - - if (currLineInRow == Regs[MAX_RASTER_ADDR] + 1) - { - currLineInRow = 0; - currRow++; - - if (currRow == Regs[VER_TOTAL] + 1) - { - currRow = 0; - } - } - - // check for vsync - if (!isVSYNC && currRow == Regs[VER_SYNC_POS]) - { - isVSYNC = true; - VSYNCcnt = verSyncWidth; - //vsync happening - } + DISPTMG = false; } - else if (!isHSYNC && currCol == Regs[HOR_SYNC_POS]) + else { - // HSYNC starts - isHSYNC = true; - HSYNCcnt = horSyncWidth; - // hsync happening + DISPTMG = true; + } + + // check for the end of the current scanline + if (HCC == Regs[HOR_TOTAL] + 1) + { + // end of the current scanline + HCC = 0; + + if (VSYNC) + { + // VSYNC in progress + VSYNCCounter++; + + if (VSYNCCounter == VSYNCWidth) + { + // end of VSYNC + VSYNCCounter = 0; + VSYNC = false; + } + } + + // increment line counter + VLC++; + + if (EndOfScreen) + { + // we have finished the last character row + // are their additional scanlines specified? + if (VLC < Regs[VER_TOTAL_ADJUST] + 1) + { + // still doing extra scanlines + } + else + + { + // finished doing extra scanlines + EndOfScreen = false; + VLC = 0; + VCC = 0; + } + } + else + { + // check for the completion of a vertical character + if (VLC == Regs[MAX_RASTER_ADDR] + 1) + { + // vertical character line has been completed + // increment vcc and reset vlc + VCC++; + VLC = 0; + } + + // end of screen? + if (VCC >= Regs[VER_TOTAL] + 1) + { + VCC = 0; + EndOfScreen = true; + } + } + + // does VSYNC need to be raised? + if (!VSYNC) + { + if (VCC == Regs[VER_SYNC_POS]) + { + VSYNC = true; + VSYNCCounter = 0; + } + } + } + else + { + // still processing a scanline + // check whether HSYNC needs raising + if (!HSYNC) + { + if (HCC == Regs[HOR_SYNC_POS]) + { + HSYNC = true; + HSYNCCounter = 0; + } + } } } @@ -194,6 +391,9 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC Regs[i] = RegDefaults[i]; SelectedRegister = 0; + + // populate initial MA address + MA = (short)(((Regs[DISP_START_ADDR_H]) & 0xff) << 8 | (Regs[DISP_START_ADDR_L]) & 0xff); } #endregion @@ -259,31 +459,31 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC case CRCTType.Hitachi_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. - horSyncWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 0) & 0x0F; - verSyncWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 4) & 0x0F; + HSYNCWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 0) & 0x0F; + VSYNCWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 4) & 0x0F; break; case CRCTType.UMC_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. - horSyncWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 0) & 0x0F; - verSyncWidth = 16; + HSYNCWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 0) & 0x0F; + VSYNCWidth = 16; break; case CRCTType.Motorola_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. - horSyncWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 0) & 0x0F; - if (horSyncWidth == 0) - horSyncWidth = 16; - verSyncWidth = 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: // 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. - horSyncWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 0) & 0x0F; - verSyncWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 4) & 0x0F; - if (horSyncWidth == 0) - horSyncWidth = 16; - if (verSyncWidth == 0) - verSyncWidth = 16; + HSYNCWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 0) & 0x0F; + VSYNCWidth = (Regs[HOR_AND_VER_SYNC_WIDTHS] >> 4) & 0x0F; + if (HSYNCWidth == 0) + HSYNCWidth = 16; + if (VSYNCWidth == 0) + VSYNCWidth = 16; break; } } @@ -465,19 +665,69 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { ser.BeginSection("CRTC"); ser.SyncEnum("ChipType", ref ChipType); + ser.Sync("HSYNC", ref HSYNC); + ser.Sync("VSYNC", ref VSYNC); + ser.Sync("DISPTMG", ref DISPTMG); + ser.Sync("MA", ref MA); ser.Sync("Regs", ref Regs, false); ser.Sync("SelectedRegister", ref SelectedRegister); - ser.Sync("verSyncWidth", ref verSyncWidth); - ser.Sync("currCol", ref currCol); - ser.Sync("currLineInRow", ref currLineInRow); - ser.Sync("currRow", ref currRow); - ser.Sync("isHSYNC", ref isHSYNC); - ser.Sync("isVSYNC", ref isVSYNC); - ser.Sync("HSYNCcnt", ref HSYNCcnt); - ser.Sync("VSYNCcnt", ref VSYNCcnt); - ser.Sync("LastTCycle", ref LastTCycle); - ser.Sync("DISPTMG", ref DISPTMG); + ser.Sync("HCC", ref HCC); + ser.Sync("VCC", ref VCC); + ser.Sync("VLC", ref VLC); + ser.Sync("CycleCounter", ref CycleCounter); + ser.Sync("EndOfScreen", ref EndOfScreen); + ser.Sync("HSYNCWidth", ref HSYNCWidth); + ser.Sync("HSYNCCounter", ref HSYNCCounter); + ser.Sync("VSYNCWidth", ref VSYNCWidth); + ser.Sync("VSYNCCounter", ref VSYNCCounter); ser.EndSection(); + + /* + * /// + /// Horizontal Character Count + /// + private int ; + + /// + /// Vertical Character Count + /// + private int ; + + /// + /// Vertical Scanline Count + /// + private int ; + + /// + /// Internal cycle counter + /// + private int ; + + /// + /// Signs that we have finished the last character row + /// + private bool ; + + /// + /// HSYNC pulse width (in characters) + /// + private int ; + + /// + /// Internal HSYNC counter + /// + private int ; + + /// + /// VSYNC pulse width (in characters) + /// + private int ; + + /// + /// Internal VSYNC counter + /// + private int ; + * */ } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/GateArrayBase.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/GateArrayBase.cs index f5b18280d9..768a377b67 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/GateArrayBase.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/GateArrayBase.cs @@ -250,7 +250,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { // Phase 1 case 1: - CRCT.CycleClock(); + CRCT.ClockCycle(); CPU.ExecuteOne(); break; // Phase 2 diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/PPIBase.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/PPIBase.cs index 0aefa4b236..512803d2fe 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/PPIBase.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/PPIBase.cs @@ -161,7 +161,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC BitArray rBits = new BitArray(8); // Bit0 - Vertical Sync ("1"=VSYNC active, "0"=VSYNC inactive) - if (CRTC.isVSYNC) + if (CRTC.VSYNC) rBits[0] = true; // Bits1-3 - Distributor ID. Usually set to 4=Awa, 5=Schneider, or 7=Amstrad