309 lines
7.9 KiB
C#
309 lines
7.9 KiB
C#
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|
{
|
|
public sealed partial class Vic
|
|
{
|
|
[SaveState.DoNotSave] private int _borderPixel;
|
|
[SaveState.DoNotSave] private int _bufferPixel;
|
|
[SaveState.DoNotSave] private int _ecmPixel;
|
|
[SaveState.DoNotSave] private int _pixel;
|
|
[SaveState.DoNotSave] private int _pixelCounter;
|
|
[SaveState.DoNotSave] private int _pixelData;
|
|
[SaveState.DoNotSave] private int _pixelOwner;
|
|
[SaveState.DoNotSave] private int _sprData;
|
|
[SaveState.DoNotSave] private int _sprIndex;
|
|
[SaveState.DoNotSave] private int _sprPixel;
|
|
private int _srSync;
|
|
private int _srColorSync;
|
|
private int _srColorIndexLatch;
|
|
private int _videoMode;
|
|
private int _borderOnShiftReg;
|
|
|
|
[SaveState.DoNotSave] private const int VideoMode000 = 0;
|
|
[SaveState.DoNotSave] private const int VideoMode001 = 1;
|
|
[SaveState.DoNotSave] private const int VideoMode010 = 2;
|
|
[SaveState.DoNotSave] private const int VideoMode011 = 3;
|
|
[SaveState.DoNotSave] private const int VideoMode100 = 4;
|
|
[SaveState.DoNotSave] private const int VideoModeInvalid = -1;
|
|
|
|
[SaveState.DoNotSave] private const int SrMask1 = 0x20000;
|
|
[SaveState.DoNotSave] private const int SrMask2 = SrMask1 << 1;
|
|
[SaveState.DoNotSave] private const int SrMask3 = SrMask1 | SrMask2;
|
|
[SaveState.DoNotSave] private const int SrColorMask = 0x8000;
|
|
[SaveState.DoNotSave] private const int SrSpriteMask = SrSpriteMask2;
|
|
[SaveState.DoNotSave] private const int SrSpriteMask1 = 0x400000;
|
|
[SaveState.DoNotSave] private const int SrSpriteMask2 = SrSpriteMask1 << 1;
|
|
[SaveState.DoNotSave] private const int SrSpriteMask3 = SrSpriteMask1 | SrSpriteMask2;
|
|
[SaveState.DoNotSave] private const int SrSpriteMaskMc = SrSpriteMask3;
|
|
|
|
private void Render()
|
|
{
|
|
if (_rasterX == _hblankEndCheckXRaster)
|
|
_hblank = false;
|
|
if (_rasterX == _hblankStartCheckXRaster)
|
|
_hblank = true;
|
|
|
|
_renderEnabled = !_hblank && !_vblank;
|
|
_pixelCounter = -1;
|
|
while (_pixelCounter++ < 3)
|
|
{
|
|
|
|
if ((_srColorSync & SrColorMask) != 0)
|
|
{
|
|
_displayC = _bufferC[_srColorIndexLatch];
|
|
_srColorIndexLatch = (_srColorIndexLatch + 1) & 0x3F;
|
|
}
|
|
|
|
#region PRE-RENDER BORDER
|
|
|
|
// check left border
|
|
if (_borderCheckLEnable && (_rasterX == _borderL))
|
|
{
|
|
if (_rasterLine == _borderB)
|
|
_borderOnVertical = true;
|
|
if (_cycle == _totalCycles && _rasterLine == _borderT && _displayEnable)
|
|
_borderOnVertical = false;
|
|
if (!_borderOnVertical)
|
|
_borderOnMain = false;
|
|
}
|
|
|
|
// check right border
|
|
if (_borderCheckREnable && (_rasterX == _borderR))
|
|
_borderOnMain = true;
|
|
|
|
#endregion
|
|
|
|
#region CHARACTER GRAPHICS
|
|
switch (_videoMode)
|
|
{
|
|
case VideoMode000:
|
|
_pixelData = _sr & SrMask2;
|
|
_pixel = _pixelData != 0 ? _displayC >> 8 : _backgroundColor0;
|
|
break;
|
|
case VideoMode001:
|
|
if ((_displayC & 0x800) != 0)
|
|
{
|
|
// multicolor 001
|
|
if ((_srSync & SrMask2) != 0)
|
|
_pixelData = _sr & SrMask3;
|
|
|
|
switch (_pixelData)
|
|
{
|
|
case 0:
|
|
_pixel = _backgroundColor0;
|
|
break;
|
|
case SrMask1:
|
|
_pixel = _backgroundColor1;
|
|
break;
|
|
case SrMask2:
|
|
_pixel = _backgroundColor2;
|
|
break;
|
|
default:
|
|
_pixel = (_displayC & 0x700) >> 8;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// standard 001
|
|
_pixelData = _sr & SrMask2;
|
|
_pixel = _pixelData != 0 ? _displayC >> 8 : _backgroundColor0;
|
|
}
|
|
break;
|
|
case VideoMode010:
|
|
_pixelData = _sr & SrMask2;
|
|
_pixel = _pixelData != 0 ? _displayC >> 4 : _displayC;
|
|
break;
|
|
case VideoMode011:
|
|
if ((_srSync & SrMask2) != 0)
|
|
_pixelData = _sr & SrMask3;
|
|
|
|
switch (_pixelData)
|
|
{
|
|
case 0:
|
|
_pixel = _backgroundColor0;
|
|
break;
|
|
case SrMask1:
|
|
_pixel = _displayC >> 4;
|
|
break;
|
|
case SrMask2:
|
|
_pixel = _displayC;
|
|
break;
|
|
default:
|
|
_pixel = _displayC >> 8;
|
|
break;
|
|
}
|
|
break;
|
|
case VideoMode100:
|
|
_pixelData = _sr & SrMask2;
|
|
if (_pixelData != 0)
|
|
{
|
|
_pixel = _displayC >> 8;
|
|
}
|
|
else
|
|
{
|
|
_ecmPixel = (_displayC & 0xC0) >> 6;
|
|
switch (_ecmPixel)
|
|
{
|
|
case 0:
|
|
_pixel = _backgroundColor0;
|
|
break;
|
|
case 1:
|
|
_pixel = _backgroundColor1;
|
|
break;
|
|
case 2:
|
|
_pixel = _backgroundColor2;
|
|
break;
|
|
default:
|
|
_pixel = _backgroundColor3;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
_pixelData = 0;
|
|
_pixel = 0;
|
|
break;
|
|
}
|
|
_pixel &= 0xF;
|
|
_sr <<= 1;
|
|
_srSync <<= 1;
|
|
_srColorSync <<= 1;
|
|
#endregion
|
|
|
|
#region SPRITES
|
|
// render sprites
|
|
_pixelOwner = -1;
|
|
_sprIndex = 0;
|
|
foreach (var spr in _sprites)
|
|
{
|
|
_sprData = 0;
|
|
_sprPixel = _pixel;
|
|
|
|
if (spr.X == _rasterX)
|
|
{
|
|
spr.ShiftEnable = spr.Display;
|
|
spr.XCrunch = !spr.XExpand;
|
|
spr.MulticolorCrunch = false;
|
|
}
|
|
else
|
|
{
|
|
spr.XCrunch |= !spr.XExpand;
|
|
}
|
|
|
|
if (spr.ShiftEnable) // sprite rule 6
|
|
{
|
|
if (spr.Multicolor)
|
|
{
|
|
_sprData = spr.Sr & SrSpriteMaskMc;
|
|
if (spr.MulticolorCrunch && spr.XCrunch && !_rasterXHold)
|
|
{
|
|
if (spr.Loaded == 0)
|
|
{
|
|
spr.ShiftEnable = false;
|
|
}
|
|
spr.Sr <<= 2;
|
|
spr.Loaded >>= 2;
|
|
}
|
|
spr.MulticolorCrunch ^= spr.XCrunch;
|
|
}
|
|
else
|
|
{
|
|
_sprData = spr.Sr & SrSpriteMask;
|
|
if (spr.XCrunch && !_rasterXHold)
|
|
{
|
|
if (spr.Loaded == 0)
|
|
{
|
|
spr.ShiftEnable = false;
|
|
}
|
|
spr.Sr <<= 1;
|
|
spr.Loaded >>= 1;
|
|
}
|
|
}
|
|
spr.XCrunch ^= spr.XExpand;
|
|
|
|
if (_sprData != 0)
|
|
{
|
|
// sprite-sprite collision
|
|
if (_pixelOwner < 0)
|
|
{
|
|
switch (_sprData)
|
|
{
|
|
case SrSpriteMask1:
|
|
_sprPixel = _spriteMulticolor0;
|
|
break;
|
|
case SrSpriteMask2:
|
|
_sprPixel = spr.Color;
|
|
break;
|
|
case SrSpriteMask3:
|
|
_sprPixel = _spriteMulticolor1;
|
|
break;
|
|
}
|
|
_pixelOwner = _sprIndex;
|
|
}
|
|
else
|
|
{
|
|
if (!_borderOnVertical)
|
|
{
|
|
spr.CollideSprite = true;
|
|
_sprites[_pixelOwner].CollideSprite = true;
|
|
}
|
|
}
|
|
|
|
// sprite-data collision
|
|
if (!_borderOnVertical && (_pixelData >= SrMask2))
|
|
{
|
|
spr.CollideData = true;
|
|
}
|
|
|
|
// sprite priority logic
|
|
if (spr.Priority)
|
|
{
|
|
_pixel = _pixelData >= SrMask2 ? _pixel : _sprPixel;
|
|
}
|
|
else
|
|
{
|
|
_pixel = _sprPixel;
|
|
}
|
|
}
|
|
}
|
|
|
|
_sprIndex++;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region POST-RENDER BORDER
|
|
|
|
// border doesn't work with the background buffer
|
|
_borderPixel = _pixBorderBuffer[_pixBufferBorderIndex];
|
|
_pixBorderBuffer[_pixBufferBorderIndex] = _borderColor;
|
|
#endregion
|
|
|
|
// plot pixel if within viewing area
|
|
if (_renderEnabled)
|
|
{
|
|
_bufferPixel = (_borderOnShiftReg & 0x80000) != 0 ? _borderPixel : _pixBuffer[_pixBufferIndex];
|
|
_buf[_bufOffset] = Palette[_bufferPixel];
|
|
_bufOffset++;
|
|
if (_bufOffset == _bufLength)
|
|
_bufOffset = 0;
|
|
}
|
|
|
|
_borderOnShiftReg <<= 1;
|
|
_borderOnShiftReg |= (_borderOnVertical || _borderOnMain) ? 1 : 0;
|
|
_pixBuffer[_pixBufferIndex] = _pixel;
|
|
_pixBufferIndex++;
|
|
_pixBufferBorderIndex++;
|
|
|
|
if (!_rasterXHold)
|
|
_rasterX++;
|
|
}
|
|
|
|
if (_pixBufferBorderIndex >= PixBorderBufferSize)
|
|
_pixBufferBorderIndex = 0;
|
|
if (_pixBufferIndex >= PixBufferSize)
|
|
_pixBufferIndex = 0;
|
|
}
|
|
}
|
|
}
|