diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index da148d52e1..10ba63cfb3 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -272,6 +272,7 @@ + @@ -288,7 +289,6 @@ - @@ -1390,7 +1390,6 @@ - diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs deleted file mode 100644 index 1d3d1b0895..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs +++ /dev/null @@ -1,979 +0,0 @@ -using BizHawk.Common; -using BizHawk.Emulation.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /* - * Much of the SCREEN implementation has been taken from: https://github.com/Dotneteer/spectnetide - * - * MIT License - - Copyright (c) 2017 Istvan Novak - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - */ - - /* - - /// - /// The abstract class that all emulated models will inherit from - /// * Screen * - /// - public abstract partial class SpectrumBase : IVideoProvider - { - #region State - - /// - /// The main screen buffer - /// - protected int[] _frameBuffer; - - /// - /// Pixel and attribute info stored while rendering the screen - /// - protected byte _pixelByte1; - protected byte _pixelByte2; - protected byte _attrByte1; - protected byte _attrByte2; - protected int _xPos; - protected int _yPos; - protected int[] _flashOffColors; - protected int[] _flashOnColors; - protected ScreenRenderingCycle[] _renderingCycleTable; - protected bool _flashPhase; - - #endregion - - #region Statics - - /// - /// The standard ULA palette - /// - private static readonly int[] ULAPalette = - { - Colors.ARGB(0x00, 0x00, 0x00), // Black - Colors.ARGB(0x00, 0x00, 0xD7), // Blue - Colors.ARGB(0xD7, 0x00, 0x00), // Red - Colors.ARGB(0xD7, 0x00, 0xD7), // Magenta - Colors.ARGB(0x00, 0xD7, 0x00), // Green - Colors.ARGB(0x00, 0xD7, 0xD7), // Cyan - Colors.ARGB(0xD7, 0xD7, 0x00), // Yellow - Colors.ARGB(0xD7, 0xD7, 0xD7), // White - Colors.ARGB(0x00, 0x00, 0x00), // Bright Black - Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue - Colors.ARGB(0xFF, 0x00, 0x00), // Bright Red - Colors.ARGB(0xFF, 0x00, 0xFF), // Bright Magenta - Colors.ARGB(0x00, 0xFF, 0x00), // Bright Green - Colors.ARGB(0x00, 0xFF, 0xFF), // Bright Cyan - Colors.ARGB(0xFF, 0xFF, 0x00), // Bright Yellow - Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White - }; - - #endregion - - #region ScreenConfig - - /// - /// The number of displayed pixels in a display row - /// - protected int DisplayWidth = 256; - - /// - /// Number of display lines - /// - protected int DisplayLines = 192; - - /// - /// The number of frames after the flash is toggled - /// - protected int FlashToggleFrames = 25; - - /// - /// Number of lines used for vertical sync - /// - protected int VerticalSyncLines = 8; - - /// - /// The number of top border lines that are not visible - /// when rendering the screen - /// - protected int NonVisibleBorderTopLines = 8; - - /// - /// The number of border lines before the display - /// - protected int BorderTopLines = 48; - - /// - /// The number of border lines after the display - /// - protected int BorderBottomLines = 48; - - /// - /// The number of bottom border lines that are not visible - /// when rendering the screen - /// - protected int NonVisibleBorderBottomLines = 8; - - /// - /// The total number of lines in the screen - /// - protected int ScreenLines; - - /// - /// The first screen line that contains the top left display pixel - /// - protected int FirstDisplayLine; - - /// - /// The last screen line that contains the bottom right display pixel - /// - protected int LastDisplayLine; - - /// - /// The number of border pixels to the left of the display - /// - protected int BorderLeftPixels = 48; - - /// - /// The number of border pixels to the right of the display - /// - protected int BorderRightPixels = 48; - - /// - /// The total width of the screen in pixels - /// - protected int ScreenWidth; - - /// - /// Horizontal blanking time (HSync+blanking). - /// Given in Z80 clock cycles. - /// - protected int HorizontalBlankingTime = 40; - - /// - /// The time of displaying left part of the border. - /// Given in Z80 clock cycles. - /// - protected int BorderLeftTime = 24; - - /// - /// The time of displaying a pixel row. - /// Given in Z80 clock cycles. - /// - protected int DisplayLineTime = 128; - - /// - /// The time of displaying right part of the border. - /// Given in Z80 clock cycles. - /// - protected int BorderRightTime = 24; - - /// - /// The time used to render the nonvisible right part of the border. - /// Given in Z80 clock cycles. - /// - protected int NonVisibleBorderRightTime = 8; - - /// - /// The time of displaying a full screen line. - /// Given in Z80 clock cycles. - /// - protected int ScreenLineTime; - - /// - /// The time the data of a particular pixel should be prefetched - /// before displaying it. - /// Given in Z80 clock cycles. - /// - protected int PixelDataPrefetchTime = 2; - - /// - /// The time the data of a particular pixel attribute should be prefetched - /// before displaying it. - /// Given in Z80 clock cycles. - /// - protected int AttributeDataPrefetchTime = 1; - - /// - /// The tact within the line that should display the first pixel. - /// Given in Z80 clock cycles. - /// - protected int FirstPixelCycleInLine; - - /// - /// The tact in which the top left pixel should be displayed. - /// Given in Z80 clock cycles. - /// - protected int FirstDisplayPixelCycle; - - /// - /// The tact in which the top left screen pixel (border) should be displayed - /// - protected int FirstScreenPixelCycle; - - /// - /// Defines the number of Z80 clock cycles used for the full rendering - /// of the screen. - /// - public int UlaFrameCycleCount; - - /// - /// The last rendered ULA cycle - /// - public int LastRenderedULACycle; - - - /// - /// This structure defines information related to a particular T-State - /// (cycle) of ULA screen rendering - /// - [StructLayout(LayoutKind.Explicit)] - public struct ScreenRenderingCycle - { - /// - /// Tha rendering phase to be applied for the particular tact - /// - [FieldOffset(0)] - public ScreenRenderingPhase Phase; - - /// - /// Display memory contention delay - /// - [FieldOffset(1)] - public byte ContentionDelay; - - /// - /// Display memory address used in the particular tact - /// - [FieldOffset(2)] - public ushort PixelByteToFetchAddress; - - /// - /// Display memory address used in the particular tact - /// - [FieldOffset(4)] - public ushort AttributeToFetchAddress; - - /// - /// Pixel X coordinate - /// - [FieldOffset(6)] - public ushort XPos; - - /// - /// Pixel Y coordinate - /// - [FieldOffset(8)] - public ushort YPos; - } - - /// - /// This enumeration defines the particular phases of ULA rendering - /// - public enum ScreenRenderingPhase : byte - { - /// - /// The ULA does not do any rendering - /// - None, - - /// - /// The ULA simple sets the border color to display the current pixel. - /// - Border, - - /// - /// The ULA sets the border color to display the current pixel. It - /// prepares to display the fist pixel in the row with prefetching the - /// corresponding byte from the display memory. - /// - BorderAndFetchPixelByte, - - /// - /// The ULA sets the border color to display the current pixel. It has - /// already fetched the 8 pixel bits to display. It carries on - /// preparing to display the fist pixel in the row with prefetching the - /// corresponding attribute byte from the display memory. - /// - BorderAndFetchPixelAttribute, - - /// - /// The ULA displays the next two pixels of Byte1 sequentially during a - /// single Z80 clock cycle. - /// - DisplayByte1, - - /// - /// The ULA displays the next two pixels of Byte1 sequentially during a - /// single Z80 clock cycle. It prepares to display the pixels of the next - /// byte in the row with prefetching the corresponding byte from the - /// display memory. - /// - DisplayByte1AndFetchByte2, - - /// - /// The ULA displays the next two pixels of Byte1 sequentially during a - /// single Z80 clock cycle. It prepares to display the pixels of the next - /// byte in the row with prefetching the corresponding attribute from the - /// display memory. - /// - DisplayByte1AndFetchAttribute2, - - /// - /// The ULA displays the next two pixels of Byte2 sequentially during a - /// single Z80 clock cycle. - /// - DisplayByte2, - - /// - /// The ULA displays the next two pixels of Byte2 sequentially during a - /// single Z80 clock cycle. It prepares to display the pixels of the next - /// byte in the row with prefetching the corresponding byte from the - /// display memory. - /// - DisplayByte2AndFetchByte1, - - /// - /// The ULA displays the next two pixels of Byte2 sequentially during a - /// single Z80 clock cycle. It prepares to display the pixels of the next - /// byte in the row with prefetching the corresponding attribute from the - /// display memory. - /// - DisplayByte2AndFetchAttribute1 - } - - #endregion - - #region Border - - private int _borderColour; - - /// - /// Gets or sets the ULA border color - /// - public int BorderColour - { - get { return _borderColour; } - set { _borderColour = value & 0x07; } - } - - protected virtual void ResetBorder() - { - BorderColour = 0; - } - - #endregion - - #region Screen Methods - - /// - /// ULA renders the screen between two specified T-States (cycles) - /// - /// - /// - public void RenderScreen(int fromCycle, int toCycle) - { - // Adjust cycle boundaries - fromCycle = fromCycle % UlaFrameCycleCount; - toCycle = toCycle % UlaFrameCycleCount; - - // Do rendering action for cycles based on the rendering phase - for (int curr = fromCycle; curr <= toCycle; curr++) - { - var ulaCycle = _renderingCycleTable[curr]; - _xPos = ulaCycle.XPos; - _yPos = ulaCycle.YPos; - - switch (ulaCycle.Phase) - { - case ScreenRenderingPhase.None: - // --- Invisible screen area, nothing to do - break; - - case ScreenRenderingPhase.Border: - // --- Fetch the border color from ULA and set the corresponding border pixels - SetPixels(BorderColour, BorderColour); - break; - - case ScreenRenderingPhase.BorderAndFetchPixelByte: - // --- Fetch the border color from ULA and set the corresponding border pixels - SetPixels(BorderColour, BorderColour); - // --- Obtain the future pixel byte - _pixelByte1 = FetchScreenMemory(ulaCycle.PixelByteToFetchAddress); - break; - - case ScreenRenderingPhase.BorderAndFetchPixelAttribute: - // --- Fetch the border color from ULA and set the corresponding border pixels - SetPixels(BorderColour, BorderColour); - // --- Obtain the future attribute byte - _attrByte1 = FetchScreenMemory(ulaCycle.AttributeToFetchAddress); - break; - - case ScreenRenderingPhase.DisplayByte1: - // --- Display bit 7 and 6 according to the corresponding color - SetPixels( - GetColor(_pixelByte1 & 0x80, _attrByte1), - GetColor(_pixelByte1 & 0x40, _attrByte1)); - // --- Shift in the subsequent bits - _pixelByte1 <<= 2; - break; - - case ScreenRenderingPhase.DisplayByte1AndFetchByte2: - // --- Display bit 7 and 6 according to the corresponding color - SetPixels( - GetColor(_pixelByte1 & 0x80, _attrByte1), - GetColor(_pixelByte1 & 0x40, _attrByte1)); - // --- Shift in the subsequent bits - _pixelByte1 <<= 2; - // --- Obtain the next pixel byte - _pixelByte2 = FetchScreenMemory(ulaCycle.PixelByteToFetchAddress); - break; - - case ScreenRenderingPhase.DisplayByte1AndFetchAttribute2: - // --- Display bit 7 and 6 according to the corresponding color - SetPixels( - GetColor(_pixelByte1 & 0x80, _attrByte1), - GetColor(_pixelByte1 & 0x40, _attrByte1)); - // --- Shift in the subsequent bits - _pixelByte1 <<= 2; - // --- Obtain the next attribute - _attrByte2 = FetchScreenMemory(ulaCycle.AttributeToFetchAddress); - break; - - case ScreenRenderingPhase.DisplayByte2: - // --- Display bit 7 and 6 according to the corresponding color - SetPixels( - GetColor(_pixelByte2 & 0x80, _attrByte2), - GetColor(_pixelByte2 & 0x40, _attrByte2)); - // --- Shift in the subsequent bits - _pixelByte2 <<= 2; - break; - - case ScreenRenderingPhase.DisplayByte2AndFetchByte1: - // --- Display bit 7 and 6 according to the corresponding color - SetPixels( - GetColor(_pixelByte2 & 0x80, _attrByte2), - GetColor(_pixelByte2 & 0x40, _attrByte2)); - // --- Shift in the subsequent bits - _pixelByte2 <<= 2; - // --- Obtain the next pixel byte - _pixelByte1 = FetchScreenMemory(ulaCycle.PixelByteToFetchAddress); - break; - - case ScreenRenderingPhase.DisplayByte2AndFetchAttribute1: - // --- Display bit 7 and 6 according to the corresponding color - SetPixels( - GetColor(_pixelByte2 & 0x80, _attrByte2), - GetColor(_pixelByte2 & 0x40, _attrByte2)); - // --- Shift in the subsequent bits - _pixelByte2 <<= 2; - // --- Obtain the next attribute - _attrByte1 = FetchScreenMemory(ulaCycle.AttributeToFetchAddress); - break; - } - } - } - - /// - /// Tests whether the specified cycle is in the visible area of the screen. - /// - /// Line index - /// Tacts index within the line - /// - /// True, if the tact is visible on the screen; otherwise, false - /// - public virtual bool IsCycleVisible(int line, int cycleInLine) - { - var firstVisibleLine = VerticalSyncLines + NonVisibleBorderTopLines; - var lastVisibleLine = firstVisibleLine + BorderTopLines + DisplayLines + BorderBottomLines; - return - line >= firstVisibleLine - && line < lastVisibleLine - && cycleInLine >= HorizontalBlankingTime - && cycleInLine < ScreenLineTime - NonVisibleBorderRightTime; - } - - /// - /// Tests whether the cycle is in the display area of the screen. - /// - /// Line index - /// Tacts index within the line - /// - /// True, if the tact is within the display area of the screen; otherwise, false. - /// - public virtual bool IsCycleInDisplayArea(int line, int cycleInLine) - { - return line >= FirstDisplayLine - && line <= LastDisplayLine - && cycleInLine >= FirstPixelCycleInLine - && cycleInLine < FirstPixelCycleInLine + DisplayLineTime; - } - - /// - /// Sets the two adjacent screen pixels belonging to the specified cycle to the given - /// color - /// - /// Color index of the first pixel - /// Color index of the second pixel - protected virtual void SetPixels(int colorIndex1, int colorIndex2) - { - var pos = _yPos * ScreenWidth + _xPos; - _frameBuffer[pos++] = ULAPalette[colorIndex1]; - _frameBuffer[pos] = ULAPalette[colorIndex2]; - } - - /// - /// Gets the color index for the specified pixel value according - /// to the given color attribute - /// - /// 0 for paper pixel, non-zero for ink pixel - /// Color attribute - /// - protected virtual int GetColor(int pixelValue, byte attr) - { - var offset = (pixelValue == 0 ? 0 : 0x100) + attr; - return _flashPhase - ? _flashOnColors[offset] - : _flashOffColors[offset]; - } - - /// - /// Resets the ULA cycle to start screen rendering from the beginning - /// - protected virtual void ResetULACycle() - { - LastRenderedULACycle = -1; - } - - /// - /// Initializes the ULA cycle table - /// - protected virtual void InitULACycleTable() - { - _renderingCycleTable = new ScreenRenderingCycle[UlaFrameCycleCount]; - - // loop through every cycle - for (var cycle = 0; cycle < UlaFrameCycleCount; cycle++) - { - var line = cycle / ScreenLineTime; - var cycleInLine = cycle % ScreenLineTime; - - var cycleItem = new ScreenRenderingCycle - { - Phase = ScreenRenderingPhase.None, - ContentionDelay = 0 - }; - - if (IsCycleVisible(line, cycleInLine)) - { - // calculate pixel positions - cycleItem.XPos = (ushort)((cycleInLine - HorizontalBlankingTime) * 2); - cycleItem.YPos = (ushort)(line - VerticalSyncLines - NonVisibleBorderTopLines); - - if (!IsCycleInDisplayArea(line, cycleInLine)) - { - // we are in the border - cycleItem.Phase = ScreenRenderingPhase.Border; - // set the border colour - if (line >= FirstDisplayLine && - line <= LastDisplayLine) - { - if (cycleInLine == FirstPixelCycleInLine - PixelDataPrefetchTime) - { - // left or right border beside the display area - // fetch the first pixel data byte of the current line (2 cycles away) - cycleItem.Phase = ScreenRenderingPhase.BorderAndFetchPixelByte; - cycleItem.PixelByteToFetchAddress = CalculatePixelByteAddress(line, cycleInLine + 2); - cycleItem.ContentionDelay = 6; - } - else if (cycleInLine == FirstPixelCycleInLine - AttributeDataPrefetchTime) - { - // fetch the first attribute data byte of the current line (1 cycle away) - cycleItem.Phase = ScreenRenderingPhase.BorderAndFetchPixelAttribute; - cycleItem.AttributeToFetchAddress = CalculateAttributeAddress(line, cycleInLine + 1); - cycleItem.ContentionDelay = 5; - } - } - } - else - { - var pixelCycle = cycleInLine - FirstPixelCycleInLine; - // the ULA will perform a different action based on the current cycle (T-State) - switch (pixelCycle & 7) - { - case 0: - // Display the current cycle pixels - cycleItem.Phase = ScreenRenderingPhase.DisplayByte1; - cycleItem.ContentionDelay = 4; - break; - case 1: - // Display the current cycle pixels - cycleItem.Phase = ScreenRenderingPhase.DisplayByte1; - cycleItem.ContentionDelay = 3; - break; - case 2: - // While displaying the current cycle pixels, we need to prefetch the - // pixel data byte 2 cycles away - cycleItem.Phase = ScreenRenderingPhase.DisplayByte1AndFetchByte2; - cycleItem.PixelByteToFetchAddress = CalculatePixelByteAddress(line, cycleInLine + 2); - cycleItem.ContentionDelay = 2; - break; - case 3: - // While displaying the current cycle pixels, we need to prefetch the - // attribute data byte 1 cycle away - cycleItem.Phase = ScreenRenderingPhase.DisplayByte1AndFetchAttribute2; - cycleItem.AttributeToFetchAddress = CalculateAttributeAddress(line, cycleInLine + 1); - cycleItem.ContentionDelay = 1; - break; - case 4: - case 5: - // Display the current cycle pixels - cycleItem.Phase = ScreenRenderingPhase.DisplayByte2; - break; - case 6: - if (cycleInLine < FirstPixelCycleInLine + DisplayLineTime - 2) - { - // There are still more bytes to display in this line. - // While displaying the current cycle pixels, we need to prefetch the - // pixel data byte 2 cycles away - cycleItem.Phase = ScreenRenderingPhase.DisplayByte2AndFetchByte1; - cycleItem.PixelByteToFetchAddress = CalculatePixelByteAddress(line, cycleInLine + 2); - cycleItem.ContentionDelay = 6; - } - else - { - // Last byte in this line. - // Display the current cycle pixels - cycleItem.Phase = ScreenRenderingPhase.DisplayByte2; - } - break; - case 7: - if (cycleInLine < FirstPixelCycleInLine + DisplayLineTime - 1) - { - // There are still more bytes to display in this line. - // While displaying the current cycle pixels, we need to prefetch the - // attribute data byte 1 cycle away - cycleItem.Phase = ScreenRenderingPhase.DisplayByte2AndFetchAttribute1; - cycleItem.AttributeToFetchAddress = CalculateAttributeAddress(line, cycleInLine + 1); - cycleItem.ContentionDelay = 5; - } - else - { - // Last byte in this line. - // Display the current cycle pixels - cycleItem.Phase = ScreenRenderingPhase.DisplayByte2; - } - break; - } - } - } - - // Store the calulated cycle item - _renderingCycleTable[cycle] = cycleItem; - } - } - - /// - /// Calculates the pixel address for the specified line and tact within - /// the line - /// - /// Line index - /// Tacts index within the line - /// ZX spectrum screen memory address - /// - /// Memory address bits: - /// C0-C2: pixel count within a byte -- not used in address calculation - /// C3-C7: pixel byte within a line - /// V0-V7: pixel line address - /// - /// Direct Pixel Address (da) - /// ================================================================= - /// |A15|A14|A13|A12|A11|A10|A9 |A8 |A7 |A6 |A5 |A4 |A3 |A2 |A1 |A0 | - /// ================================================================= - /// | 0 | 0 | 0 |V7 |V6 |V5 |V4 |V3 |V2 |V1 |V0 |C7 |C6 |C5 |C4 |C3 | - /// ================================================================= - /// | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0xF81F - /// ================================================================= - /// | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0x0700 - /// ================================================================= - /// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0x00E0 - /// ================================================================= - /// - /// Spectrum Pixel Address - /// ================================================================= - /// |A15|A14|A13|A12|A11|A10|A9 |A8 |A7 |A6 |A5 |A4 |A3 |A2 |A1 |A0 | - /// ================================================================= - /// | 0 | 0 | 0 |V7 |V6 |V2 |V1 |V0 |V5 |V4 |V3 |C7 |C6 |C5 |C4 |C3 | - /// ================================================================= - /// - protected virtual ushort CalculatePixelByteAddress(int line, int cycleInLine) - { - var row = line - FirstDisplayLine; - var column = 2 * (cycleInLine - (HorizontalBlankingTime + BorderLeftTime)); - var da = 0x4000 | (column >> 3) | (row << 5); - return (ushort)((da & 0xF81F) // --- Reset V5, V4, V3, V2, V1 - | ((da & 0x0700) >> 3) // --- Keep V5, V4, V3 only - | ((da & 0x00E0) << 3)); // --- Exchange the V2, V1, V0 bit - // --- group with V5, V4, V3 - } - - /// - /// Calculates the pixel attribute address for the specified line and - /// tact within the line - /// - /// Line index - /// Tacts index within the line - /// ZX spectrum screen memory address - /// - /// Memory address bits: - /// C0-C2: pixel count within a byte -- not used in address calculation - /// C3-C7: pixel byte within a line - /// V0-V7: pixel line address - /// - /// Spectrum Attribute Address - /// ================================================================= - /// |A15|A14|A13|A12|A11|A10|A9 |A8 |A7 |A6 |A5 |A4 |A3 |A2 |A1 |A0 | - /// ================================================================= - /// | 0 | 1 | 0 | 1 | 1 | 0 |V7 |V6 |V5 |V4 |V3 |C7 |C6 |C5 |C4 |C3 | - /// ================================================================= - /// - protected virtual ushort CalculateAttributeAddress(int line, int cycleInLine) - { - var row = line - FirstDisplayLine; - var column = 2 * (cycleInLine - (HorizontalBlankingTime + BorderLeftTime)); - var da = (column >> 3) | ((row >> 3) << 5); - return (ushort)(0x5800 + da); - } - - #endregion - - #region Initialisation - - /// - /// Initialises the screen configuration calculations - /// - public virtual void InitScreenConfig(ZXSpectrum.BorderType border_type) - { - switch (border_type) - { - case ZXSpectrum.BorderType.Full: - BorderTopLines = 48; - BorderBottomLines = 48; - NonVisibleBorderTopLines = 8; - NonVisibleBorderBottomLines = 8; - break; - - case ZXSpectrum.BorderType.Widescreen: - BorderTopLines = 0; - BorderBottomLines = 0; - NonVisibleBorderTopLines = 8 + 48; - NonVisibleBorderBottomLines = 8 + 48; - break; - } - - ScreenLines = BorderTopLines + DisplayLines + BorderBottomLines; - FirstDisplayLine = VerticalSyncLines + NonVisibleBorderTopLines + BorderTopLines; - LastDisplayLine = FirstDisplayLine + DisplayLines - 1; - ScreenWidth = BorderLeftPixels + DisplayWidth + BorderRightPixels; - FirstPixelCycleInLine = HorizontalBlankingTime + BorderLeftTime; - ScreenLineTime = FirstPixelCycleInLine + DisplayLineTime + BorderRightTime + NonVisibleBorderRightTime; - UlaFrameCycleCount = (FirstDisplayLine + DisplayLines + BorderBottomLines + NonVisibleBorderTopLines) * ScreenLineTime; - FirstScreenPixelCycle = (VerticalSyncLines + NonVisibleBorderTopLines) * ScreenLineTime + HorizontalBlankingTime; - } - - /// - /// Inits the screen - /// - protected virtual void InitScreen() - { - //BorderDevice.Reset(); - _flashPhase = false; - - _frameBuffer = new int[ScreenWidth * ScreenLines]; - - InitULACycleTable(); - - // --- Calculate color conversion table - _flashOffColors = new int[0x200]; - _flashOnColors = new int[0x200]; - - for (var attr = 0; attr < 0x100; attr++) - { - var ink = (attr & 0x07) | ((attr & 0x40) >> 3); - var paper = ((attr & 0x38) >> 3) | ((attr & 0x40) >> 3); - _flashOffColors[attr] = paper; - _flashOffColors[0x100 + attr] = ink; - _flashOnColors[attr] = (attr & 0x80) != 0 ? ink : paper; - _flashOnColors[0x100 + attr] = (attr & 0x80) != 0 ? paper : ink; - } - - FrameCount = 0; - } - - #endregion - - #region VBLANK Interrupt - - /// - /// The longest instruction cycle count - /// - protected const int LONGEST_OP_CYCLES = 23; - - /// - /// The ULA cycle to raise the interrupt at - /// - protected int InterruptCycle = 32; - - /// - /// Signs that an interrupt has been raised in this frame. - /// - protected bool InterruptRaised; - - /// - /// Signs that the interrupt signal has been revoked - /// - protected bool InterruptRevoked; - - /// - /// Resets the interrupt - this should happen every frame in order to raise - /// the VBLANK interrupt in the proceding frame - /// - public virtual void ResetInterrupt() - { - InterruptRaised = false; - InterruptRevoked = false; - } - - /// - /// Generates an interrupt in the current phase if needed - /// - /// - protected virtual void CheckForInterrupt(int currentCycle) - { - if (InterruptRevoked) - { - // interrupt has already been handled - return; - } - - if (currentCycle < InterruptCycle) - { - // interrupt does not need to be raised yet - return; - } - - if (currentCycle > InterruptCycle + LONGEST_OP_CYCLES) - { - // interrupt should have already been raised and the cpu may or - // may not have caught it. The time has passed so revoke the signal - InterruptRevoked = true; - //CPU.IFF1 = true; - CPU.FlagI = false; - //CPU.NonMaskableInterruptPending = true; - } - - if (InterruptRaised) - { - // INT is raised but not yet revoked - // CPU has NOT handled it yet - return; - } - - // if CPU is masking the interrupt do not raise it - //if (!CPU.IFF1) - //return; - - // Raise the interrupt - InterruptRaised = true; - //CPU.IFF1 = false; - //CPU.IFF2 = false; - CPU.FlagI = true; - FrameCount++; - } - - #endregion - - #region IVideoProvider - - public int VirtualWidth => ScreenWidth; - public int VirtualHeight => ScreenLines; - public int BufferWidth => ScreenWidth; - public int BufferHeight => ScreenLines; - public int BackgroundColor => ULAPalette[BorderColour]; - - public int VsyncNumerator - { - get { return 3500000; } - set { } - } - - public int VsyncDenominator - { - get { return UlaFrameCycleCount; } - } - /* - public int VsyncNumerator => NullVideo.DefaultVsyncNum; - public int VsyncDenominator => NullVideo.DefaultVsyncDen; - - public int[] GetVideoBuffer() - { - /* - switch(Spectrum.SyncSettings.BorderType) - { - case ZXSpectrum.BorderType.Full: - return _frameBuffer; - - case ZXSpectrum.BorderType.Small: - // leave only 10 border units all around - int[] smlBuff = new int[(ScreenWidth - 30) * (DisplayLines - 30)]; - int index = 0; - int brdCount = 0; - // skip top and bottom - for (int i = ScreenWidth * 30; i < smlBuff.Length - ScreenWidth * 30; i++) - { - if (brdCount < 30) - { - brdCount++; - continue; - } - if (brdCount > ScreenWidth - 30) - { - brdCount++; - continue; - } - - smlBuff[index] = _frameBuffer[i]; - index++; - brdCount++; - } - - return smlBuff; - - case ZXSpectrum.BorderType.Medium: - break; - } - - return _frameBuffer; - - } - - #endregion - - } - */ -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index a999933720..b9fad2fe5a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -14,7 +14,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // 128 and up only protected int ROMPaged = 0; protected bool SHADOWPaged; - protected int RAMPaged; + public int RAMPaged; protected bool PagingDisabled; // +3/+2A only diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs index 9bebc31eac..9bdab42b2f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -514,7 +514,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public int BackgroundColor { - get { return ULAPalette[borderColour]; } + get { return ULAPalette[7]; } //ULAPalette[borderColour]; } } public int VirtualWidth diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs deleted file mode 100644 index 00b952614b..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - public partial class ZX128 : SpectrumBase - { - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs new file mode 100644 index 0000000000..090cd9c7be --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs @@ -0,0 +1,194 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + class ULA128 : ULABase + { + #region Construction + + public ULA128(SpectrumBase machine) + : base(machine) + { + InterruptPeriod = 36; + FrameLength = 70908; + ClockSpeed = 3546900; + + contentionTable = new byte[70930]; + floatingBusTable = new short[70930]; + for (int f = 0; f < 70930; f++) + floatingBusTable[f] = -1; + + CharRows = 24; + CharCols = 32; + ScreenWidth = 256; + ScreenHeight = 192; + BorderTopHeight = 48; + BorderBottomHeight = 56; + BorderLeftWidth = 48; + BorderRightWidth = 48; + DisplayStart = 16384; + DisplayLength = 6144; + AttributeStart = 22528; + AttributeLength = 768; + borderColour = 7; + ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; + + TstatesPerScanline = 228; + TstateAtTop = BorderTopHeight * TstatesPerScanline; + TstateAtBottom = BorderBottomHeight * TstatesPerScanline; + tstateToDisp = new short[FrameLength]; + + ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border + + ScanLineWidth * ScreenHeight //border + main + border of 192 lines + + ScanLineWidth * BorderBottomHeight]; //56 lines of border + + attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped + + BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; + BufferHeight = ScreenHeight + BorderTopHeight + BorderBottomHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + + Reset(); + } + + #endregion + + #region Misc Operations + + public override void Reset() + { + contentionStartPeriod = 14361; // + LateTiming; + contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); + screen = _machine.Memory[1]; + screenByteCtr = DisplayStart; + ULAByteCtr = 0; + actualULAStart = 14366 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; + lastTState = actualULAStart; + BuildAttributeMap(); + BuildContentionTable(); + } + + #endregion + + #region Contention Methods + + public override bool IsContended(int addr) + { + addr = addr & 0xc000; + + if (addr == 0x4000) + { + // low port contention + return true; + } + + if (addr == 0xc000) + { + // high port contention - check for contended bank paged in + switch (_machine.RAMPaged) + { + case 1: + case 3: + case 5: + case 7: + return true; + } + } + + return false; + } + + public override void BuildContentionTable() + { + int t = contentionStartPeriod; + while (t < contentionEndPeriod) + { + //for 128 t-states + for (int i = 0; i < 128; i += 8) + { + contentionTable[t++] = 6; + contentionTable[t++] = 5; + contentionTable[t++] = 4; + contentionTable[t++] = 3; + contentionTable[t++] = 2; + contentionTable[t++] = 1; + contentionTable[t++] = 0; + contentionTable[t++] = 0; + } + t += (TstatesPerScanline - 128); + } + + //build top half of tstateToDisp table + //vertical retrace period + for (t = 0; t < actualULAStart; t++) + tstateToDisp[t] = 0; + + //next 48 are actual border + while (t < actualULAStart + (TstateAtTop)) + { + for (int g = 0; g < 176; g++) + tstateToDisp[t++] = 1; + + for (int g = 176; g < TstatesPerScanline; g++) + tstateToDisp[t++] = 0; + } + + //build middle half + int _x = 0; + int _y = 0; + int scrval = 2; + while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline)) + { + for (int g = 0; g < 24; g++) + tstateToDisp[t++] = 1; + + for (int g = 24; g < 24 + 128; g++) + { + //Map screenaddr to tstate + if (g % 4 == 0) + { + scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2)); + _x += 8; + } + tstateToDisp[t++] = (short)scrval; + } + _y++; + + for (int g = 24 + 128; g < 24 + 128 + 24; g++) + tstateToDisp[t++] = 1; + + for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 52; g++) + tstateToDisp[t++] = 0; + } + + int h = contentionStartPeriod + 3; + while (h < contentionEndPeriod + 3) + { + for (int j = 0; j < 128; j += 8) + { + floatingBusTable[h] = tstateToDisp[h + 2]; + floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)]; + floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4]; + floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)]; + h += 8; + } + h += TstatesPerScanline - 128; + } + + //build bottom half + while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom)) + { + for (int g = 0; g < 176; g++) + tstateToDisp[t++] = 1; + + for (int g = 176; g < TstatesPerScanline; g++) + tstateToDisp[t++] = 0; + } + } + + + #endregion + + + } +}