BizHawk/BizHawk.Emulation/Consoles/PC Engine/VDC.Render.cs

354 lines
14 KiB
C#

using System;
namespace BizHawk.Emulation.Consoles.TurboGrafx
{
// This rendering code is only used for TurboGrafx/TurboCD Mode.
// In SuperGrafx mode, separate rendering functions in the VPC class are used.
public partial class VDC
{
/* There are many line-counters here. Here is a breakdown of what they each are:
+ ScanLine is the current NTSC scanline. It has a range from 0 to 262.
+ ActiveLine is the current offset into the framebuffer. 0 is the first
line of active display, and the last value will be BufferHeight-1.
+ BackgroundY is the current offset into the scroll plane. It is set with BYR
register at certain sync points and incremented every scanline.
Its values range from 0 - $1FF.
+ RCRCounter is set to $40 at the first line of active display, and incremented each
scanline thereafter.
*/
public int ScanLine;
public int BackgroundY;
public int RCRCounter;
public int ActiveLine;
public int HBlankCycles = 79;
public bool PerformSpriteLimit;
byte[] PriorityBuffer = new byte[512];
byte[] InterSpritePriorityBuffer = new byte[512];
public void ExecFrame(bool render)
{
if (MultiResHack > 0 && render)
Array.Clear(FrameBuffer, 0, FrameBuffer.Length);
while (true)
{
int ActiveDisplayStartLine = DisplayStartLine;
int VBlankLine = ActiveDisplayStartLine + Registers[VDW] + 1;
if (VBlankLine > 261)
VBlankLine = 261;
ActiveLine = ScanLine - ActiveDisplayStartLine;
bool InActiveDisplay = (ScanLine >= ActiveDisplayStartLine) && (ScanLine < VBlankLine);
if (ScanLine == ActiveDisplayStartLine)
RCRCounter = 0x40;
if (ScanLine == VBlankLine)
UpdateSpriteAttributeTable();
if (RCRCounter == (Registers[RCR] & 0x3FF))
{
if (RasterCompareInterruptEnabled)
{
StatusByte |= StatusRasterCompare;
cpu.IRQ1Assert = true;
}
}
cpu.Execute(HBlankCycles);
if (InActiveDisplay)
{
if (ScanLine == ActiveDisplayStartLine)
BackgroundY = Registers[BYR];
else
{
BackgroundY++;
BackgroundY &= 0x01FF;
}
if (render) RenderScanLine();
}
if (ScanLine == VBlankLine && VBlankInterruptEnabled)
StatusByte |= StatusVerticalBlanking;
if (ScanLine == VBlankLine + 4 && SatDmaPerformed)
{
SatDmaPerformed = false;
if ((Registers[DCR] & 1) > 0)
StatusByte |= StatusVramSatDmaComplete;
}
cpu.Execute(2);
if ((StatusByte & (StatusVerticalBlanking | StatusVramSatDmaComplete)) != 0)
cpu.IRQ1Assert = true;
cpu.Execute(455 - HBlankCycles - 2);
if (InActiveDisplay == false && DmaRequested)
RunDmaForScanline();
ScanLine++;
RCRCounter++;
if (ScanLine == vce.NumberOfScanlines)
{
ScanLine = 0;
break;
}
}
}
public void RenderScanLine()
{
if (ActiveLine >= FrameHeight)
return;
RenderBackgroundScanline(pce.CoreComm.PCE_ShowBG1);
RenderSpritesScanline(pce.CoreComm.PCE_ShowOBJ1);
}
void RenderBackgroundScanline(bool show)
{
Array.Clear(PriorityBuffer, 0, FrameWidth);
if (BackgroundEnabled == false)
{
for (int i = 0; i < FrameWidth; i++)
FrameBuffer[(ActiveLine * FramePitch) + i] = vce.Palette[256];
return;
}
int batHeight = BatHeight * 8;
int batWidth = BatWidth * 8;
int vertLine = BackgroundY;
vertLine %= batHeight;
int yTile = (vertLine / 8);
int yOfs = vertLine % 8;
// This is not optimized. But it seems likely to remain that way.
int xScroll = Registers[BXR] & 0x3FF;
for (int x = 0; x < FrameWidth; x++)
{
int xTile = ((x + xScroll) / 8) % BatWidth;
int xOfs = (x + xScroll) & 7;
int tileNo = VRAM[(ushort)(((yTile * BatWidth) + xTile))] & 2047;
int paletteNo = VRAM[(ushort)(((yTile * BatWidth) + xTile))] >> 12;
int paletteBase = paletteNo * 16;
byte c = PatternBuffer[(tileNo * 64) + (yOfs * 8) + xOfs];
if (c == 0)
FrameBuffer[(ActiveLine * FramePitch) + x] = vce.Palette[0];
else
{
FrameBuffer[(ActiveLine * FramePitch) + x] = show ? vce.Palette[paletteBase + c] : vce.Palette[0];
PriorityBuffer[x] = 1;
}
}
}
byte[] heightTable = { 16, 32, 64, 64 };
public void RenderSpritesScanline(bool show)
{
if (SpritesEnabled == false)
return;
Array.Clear(InterSpritePriorityBuffer, 0, FrameWidth);
bool Sprite4ColorMode = Sprite4ColorModeEnabled;
int activeSprites = 0;
for (int i = 0; i < 64; i++)
{
if (activeSprites >= 16 && PerformSpriteLimit)
break;
int y = (SpriteAttributeTable[(i * 4) + 0] & 1023) - 64;
int x = (SpriteAttributeTable[(i * 4) + 1] & 1023) - 32;
ushort flags = SpriteAttributeTable[(i * 4) + 3];
int height = heightTable[(flags >> 12) & 3];
int width = (flags & 0x100) == 0 ? 16 : 32;
if (y + height <= ActiveLine || y > ActiveLine)
continue;
activeSprites += width == 16 ? 1 : 2;
int patternNo = (((SpriteAttributeTable[(i * 4) + 2]) >> 1) & 0x1FF);
int paletteBase = 256 + ((flags & 15) * 16);
bool priority = (flags & 0x80) != 0;
bool hflip = (flags & 0x0800) != 0;
bool vflip = (flags & 0x8000) != 0;
int colorMask = 0xFF;
if (Sprite4ColorMode)
{
if ((SpriteAttributeTable[(i * 4) + 2] & 1) == 0)
colorMask = 0x03;
else
colorMask = 0x0C;
}
if (width == 32)
patternNo &= 0x1FE;
int yofs = 0;
if (vflip == false)
{
yofs = (ActiveLine - y) & 15;
if (height == 32)
{
patternNo &= 0x1FD;
if (ActiveLine - y >= 16)
{
y += 16;
patternNo += 2;
}
}
else if (height == 64)
{
patternNo &= 0x1F9;
if (ActiveLine - y >= 48)
{
y += 48;
patternNo += 6;
}
else if (ActiveLine - y >= 32)
{
y += 32;
patternNo += 4;
}
else if (ActiveLine - y >= 16)
{
y += 16;
patternNo += 2;
}
}
}
else // vflip == true
{
yofs = 15 - ((ActiveLine - y) & 15);
if (height == 32)
{
patternNo &= 0x1FD;
if (ActiveLine - y < 16)
{
y += 16;
patternNo += 2;
}
}
else if (height == 64)
{
patternNo &= 0x1F9;
if (ActiveLine - y < 16)
{
y += 48;
patternNo += 6;
}
else if (ActiveLine - y < 32)
{
y += 32;
patternNo += 4;
}
else if (ActiveLine - y < 48)
{
y += 16;
patternNo += 2;
}
}
}
if (hflip == false)
{
if (x + width > 0 && y + height > 0)
{
for (int xs = x >= 0 ? x : 0; xs < x + 16 && xs >= 0 && xs < FrameWidth; xs++)
{
byte pixel = (byte)(SpriteBuffer[(patternNo * 256) + (yofs * 16) + (xs - x)] & colorMask);
if (colorMask == 0x0C)
pixel >>= 2;
if (pixel != 0 && InterSpritePriorityBuffer[xs] == 0)
{
InterSpritePriorityBuffer[xs] = 1;
if ((priority || PriorityBuffer[xs] == 0) && show)
FrameBuffer[(ActiveLine * FramePitch) + xs] = vce.Palette[paletteBase + pixel];
}
}
}
if (width == 32)
{
patternNo++;
x += 16;
for (int xs = x >= 0 ? x : 0; xs < x + 16 && xs >= 0 && xs < FrameWidth; xs++)
{
byte pixel = (byte)(SpriteBuffer[(patternNo * 256) + (yofs * 16) + (xs - x)] & colorMask);
if (colorMask == 0x0C)
pixel >>= 2;
if (pixel != 0 && InterSpritePriorityBuffer[xs] == 0)
{
InterSpritePriorityBuffer[xs] = 1;
if ((priority || PriorityBuffer[xs] == 0) && show)
FrameBuffer[(ActiveLine * FramePitch) + xs] = vce.Palette[paletteBase + pixel];
}
}
}
}
else
{ // hflip = true
if (x + width > 0 && y + height > 0)
{
if (width == 32)
patternNo++;
for (int xs = x >= 0 ? x : 0; xs < x + 16 && xs >= 0 && xs < FrameWidth; xs++)
{
byte pixel = (byte)(SpriteBuffer[(patternNo * 256) + (yofs * 16) + 15 - (xs - x)] & colorMask);
if (colorMask == 0x0C)
pixel >>= 2;
if (pixel != 0 && InterSpritePriorityBuffer[xs] == 0)
{
InterSpritePriorityBuffer[xs] = 1;
if ((priority || PriorityBuffer[xs] == 0) && show)
FrameBuffer[(ActiveLine * FramePitch) + xs] = vce.Palette[paletteBase + pixel];
}
}
if (width == 32)
{
patternNo--;
x += 16;
for (int xs = x >= 0 ? x : 0; xs < x + 16 && xs >= 0 && xs < FrameWidth; xs++)
{
byte pixel = (byte)(SpriteBuffer[(patternNo * 256) + (yofs * 16) + 15 - (xs - x)] & colorMask);
if (colorMask == 0x0C)
pixel >>= 2;
if (pixel != 0 && InterSpritePriorityBuffer[xs] == 0)
{
InterSpritePriorityBuffer[xs] = 1;
if ((priority || PriorityBuffer[xs] == 0) && show)
FrameBuffer[(ActiveLine * FramePitch) + xs] = vce.Palette[paletteBase + pixel];
}
}
}
}
}
}
}
int FramePitch = 256;
int FrameWidth = 256;
int FrameHeight = 240;
int[] FrameBuffer = new int[256 * 240];
public int[] GetVideoBuffer() { return FrameBuffer; }
public int VirtualWidth { get { return FramePitch; } }
public int BufferWidth { get { return FramePitch; } }
public int BufferHeight { get { return FrameHeight; } }
public int BackgroundColor { get { return vce.Palette[256]; } }
}
}