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
+
+
+ }
+}