diff --git a/src/BizHawk.Emulation.Cores/Consoles/Watara/SuperVision/ASIC.cs b/src/BizHawk.Emulation.Cores/Consoles/Watara/SuperVision/ASIC.cs
index a2fd76cbf4..64692f1d71 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Watara/SuperVision/ASIC.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Watara/SuperVision/ASIC.cs
@@ -42,24 +42,11 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
public const int R_SYSTEM_CONTROL = 0x26;
public const int R_IRQ_STATUS = 0x27;
- ///
- /// Scanline length in cpu clocks
- ///
- public int CLOCK_WIDTH =>
- ((_regs[R_LCD_X_SIZE] & 0xFC) // topmost 6 bits of the X Size register
- + 4) // line latch pulse
- * 6; // 6 clocks per pixel
-
///
/// Y offset modifier used with Y_Scroll to determine the VRAM pointer
///
public int Y_OFFSET => _regs[R_LCD_X_SIZE] > 0xC0 ? 0x30 : 0x60;
- ///
- /// Number of scanlines in a field
- ///
- public int LINE_HEIGHT => _regs[R_LCD_Y_SIZE];
-
private SuperVision _sv;
private byte[] _regs = new byte[0x2000];
public byte[] Regs => _regs;
@@ -86,7 +73,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
private int _seqCounter;
private int _byteCounter;
private int _lineCounter;
- private int _field;
+ private bool _field;
private ushort _vramByteBuffer;
private int _vramPointer;
private int _vramStartAddress;
@@ -134,13 +121,80 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
AudioClock(ticks);
// video
+ for (int i = 0; i < lcdCycles; i++)
+ {
+ _byteCounter++;
+ if (_byteCounter * 4 <= (_regs[R_LCD_X_SIZE] & 0b1111_1100))
+ {
+ // still sending pixel data to the LCD
+ // 4 pixles (bits) per byte read
+ _vramPointer = (ushort) (_vramStartAddress + (_regs[R_X_SCROLL] >> 2) + _byteCounter - 1);
+ //_vramPointer = (ushort)(_vramPointer + _byteCounter - 1);
+ // read a byte of data from VRAM
+ byte data = _sv.ReadVRAM((ushort)_vramPointer);
+ // shift the last read byte in the buffer and add the new byte to the start
+ _vramByteBuffer = (ushort) ((_vramByteBuffer << 8) | data);
+
+ // get the correct byte data based on the X Scroll register lower 2 bits
+ // this simulates a delay in the bits sent to the LCD
+ byte b = (byte) ((_vramByteBuffer >> (_regs[R_X_SCROLL] & 0b0000_0011)) & 0xff);
+
+ // structure the correct bits based on the current field
+ byte lData = !_field
+ ? (byte) ((b & 0b0000_0001) | ((b & 0b0000_0100) >> 1) | ((b & 0b0001_0000) >> 2) | ((b & 0b0100_0000) >> 3))
+ : (byte) ((b & 0b0000_0010) >> 1 | ((b & 0b0000_1000) >> 2) | ((b & 0b0010_0000) >> 3) | ((b & 0b1000_0000) >> 4));
+
+ Screen.PixelClock(lData);
+ }
+ else if (_byteCounter * 4 > (_regs[R_LCD_X_SIZE] & 0b1111_1100))
+ {
+ // end of scanline
+ _lineCounter++;
+ _byteCounter = 0;
+
+ if (_lineCounter < _regs[R_LCD_Y_SIZE])
+ {
+ // still within the frame
+ // pulse line latch
+ Screen.LineLatch();
+
+ // vstart is updated every scanline
+ // it is incremented based on the X_Size register
+ int inc = _regs[R_LCD_X_SIZE] > 0xC3 ? 0x60 : 0x30;
+ _vramStartAddress = (_vramStartAddress + inc) & 0x1FFFF;
+ if (_vramStartAddress == 0x1FE0)
+ _vramStartAddress = 0;
+ }
+ else
+ {
+ // end of field
+ _lineCounter = 0;
+ // pulse the frame latch
+ Screen.FrameLatch();
+ // new field
+ _field = !_field;
+ Screen.FramePolarity = _field;
+
+ // setup memory pointer for beginning of field
+ _vramStartAddress = (_regs[R_Y_SCROLL] * 0x30) & 0x1FFF;
+ if (_vramStartAddress == 0x1FE0)
+ {
+ _vramStartAddress = 0;
+ }
+
+ // setup for the next scanline
+ _vramPointer = _vramStartAddress + (_regs[R_X_SCROLL] >> 2);
+ }
+ }
+ }
_sv.FrameClock += ticks;
}
+ /*
///
/// Runs a single clock cycle of the ASIC
///
@@ -256,7 +310,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
_sv.FrameClock++;
}
-
+ */
///
/// Interrupt management
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Watara/SuperVision/LCD.cs b/src/BizHawk.Emulation.Cores/Consoles/Watara/SuperVision/LCD.cs
index 2aa69e12dd..ade4b68a25 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Watara/SuperVision/LCD.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Watara/SuperVision/LCD.cs
@@ -89,47 +89,50 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
// 9 - Power Control
///
- /// It takes 6 cycles for each write to the LCD screen (so 1 pixelclock == 6 cpu cycles)
+ /// Pulsed at the end of a field
+ /// Clears the column and row shift registers
///
- public void PixelClock(byte data, int framePolarity = 0, bool lineLatch = false, bool frameLatch = false)
- {
- // Each scanline is composed of 246 clocks.
- // There are 40 pixel writes to the LCD, and 1 latch write, for a total of 41 writes.
- // Each write period lasts 6 clock cycles, so 41*6 = 246 cycles
- if (frameLatch)
- {
- // end of field
- // write last pixel
- if (DisplayEnable)
- WritePixels(data, framePolarity);
+ public void FrameLatch() => ResetPosition();
- // setup for next frame
- ResetPosition();
- }
- else if (lineLatch)
- {
- // end of scanline
- _hPos = 0;
- _vPos++;
- }
- else
- {
- if (DisplayEnable)
- WritePixels(data, framePolarity);
- }
+ ///
+ /// When pulsed, data from the column shift register latched into the current LCD glass column
+ /// The row shift register is then clocked
+ ///
+ public void LineLatch()
+ {
+ _vPos++;
+ _hPos = 0;
}
///
- /// This outputs 4 pixels at a time - we only use the first 4 bits of the data
- /// 2BPP, bit0 is written in the first field, bit1 in the second
+ /// The frame polarity/bright control signal is toggled every field
+ /// This inverts all the driver signals
+ /// It also darkens the display a bit so you can get a true 2 bits per pixel
+ /// The polarity toggling is done to prevent destruction of the LCD display glass via electrolytic plating action
///
- private void WritePixels(byte data, int framePolarity)
+ public bool FramePolarity
+ {
+ get { return _framePolarity; }
+ set { _framePolarity = value; }
+ }
+ private bool _framePolarity;
+
+
+ ///
+ /// Bit offset when writing to the framebuffer
+ ///
+ private int _polarityOffset => FramePolarity ? 1 : 0;
+
+ ///
+ /// It takes 6 cycles for each write to the LCD screen (so 1 pixelclock == 6 cpu cycles)
+ ///
+ public void PixelClock(byte data)
{
for (int i = 0; i < 4; i++)
{
if (_hPos < PEN_BUFFER_WIDTH && _vPos < PEN_BUFFER_HEIGHT)
{
- _penBuffer[(_vPos * 160 * 2) + (_hPos + framePolarity)] = (data >> i) & 0x01;
+ _penBuffer[(_vPos * 160 * 2) + (_hPos + _polarityOffset)] = (data >> i) & 0x01;
}
else
{
@@ -147,7 +150,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
VsyncDenominator = dom;
}
- public int VirtualWidth => (int)(BufferWidth * 1.25);
+ public int VirtualWidth => BufferWidth;
public int VirtualHeight => BufferHeight;
public int BufferWidth => 160;
public int BufferHeight => 160;
@@ -174,6 +177,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
ser.Sync(nameof(_hPos), ref _hPos);
ser.Sync(nameof(_vPos), ref _vPos);
ser.Sync(nameof(DisplayEnable), ref DisplayEnable);
+ ser.Sync(nameof(FramePolarity), ref _framePolarity);
ser.EndSection();
}
}