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(); } }