2011-01-11 02:55:51 +00:00
|
|
|
|
using System;
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Consoles.Sega
|
|
|
|
|
{
|
|
|
|
|
public partial class GenVDP
|
|
|
|
|
{
|
2012-08-24 02:30:20 +00:00
|
|
|
|
// Priority buffer contents have the following values:
|
|
|
|
|
// 0 = Backdrop color
|
|
|
|
|
// 1 = Plane B Low Priority
|
|
|
|
|
// 2 = Plane A Low Priority
|
|
|
|
|
// 4 = Plane B High Priority
|
|
|
|
|
// 5 = Plane A High Priority
|
|
|
|
|
// 9 = Sprite has been drawn
|
|
|
|
|
|
2011-10-18 03:48:37 +00:00
|
|
|
|
byte[] PriorityBuffer = new byte[320];
|
|
|
|
|
|
2012-08-26 14:39:06 +00:00
|
|
|
|
static readonly byte[] PalXlatTable = { 0, 0, 36, 36, 73, 73, 109, 109, 145, 145, 182, 182, 219, 219, 255, 255 };
|
|
|
|
|
|
2011-10-18 03:48:37 +00:00
|
|
|
|
// TODO, should provide startup register values.
|
2011-01-11 02:55:51 +00:00
|
|
|
|
public void RenderLine()
|
|
|
|
|
{
|
2011-10-18 03:48:37 +00:00
|
|
|
|
Array.Clear(PriorityBuffer, 0, 320);
|
|
|
|
|
int bgcolor = BackgroundColor;
|
|
|
|
|
for (int ofs = ScanLine * FrameWidth, i = 0; i < FrameWidth; i++, ofs++)
|
|
|
|
|
FrameBuffer[ofs] = bgcolor;
|
2011-01-11 02:55:51 +00:00
|
|
|
|
|
2011-10-18 03:48:37 +00:00
|
|
|
|
if (DisplayEnabled)
|
|
|
|
|
{
|
2011-01-11 02:55:51 +00:00
|
|
|
|
RenderScrollA();
|
|
|
|
|
RenderScrollB();
|
2011-10-18 03:48:37 +00:00
|
|
|
|
RenderSpritesScanline();
|
2011-01-11 02:55:51 +00:00
|
|
|
|
}
|
2011-10-18 03:48:37 +00:00
|
|
|
|
|
2012-08-24 02:30:20 +00:00
|
|
|
|
//if (ScanLine == 223) // shrug
|
|
|
|
|
//RenderPalette();
|
2011-01-11 02:55:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-10-01 17:06:25 +00:00
|
|
|
|
void RenderPalette()
|
2011-01-11 02:55:51 +00:00
|
|
|
|
{
|
|
|
|
|
for (int p = 0; p < 4; p++)
|
|
|
|
|
for (int i = 0; i < 16; i++)
|
|
|
|
|
FrameBuffer[(p*FrameWidth) + i] = Palette[(p*16) + i];
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-18 03:48:37 +00:00
|
|
|
|
void RenderBackgroundScanline(int xScroll, int yScroll, int nameTableBase, int lowPriority, int highPriority)
|
2011-01-11 02:55:51 +00:00
|
|
|
|
{
|
2011-10-18 03:48:37 +00:00
|
|
|
|
int yTile = ((ScanLine + yScroll) / 8) % NameTableHeight;
|
|
|
|
|
|
|
|
|
|
// this is hellllla slow. but not optimizing until we implement & understand
|
|
|
|
|
// all scrolling modes, shadow & hilight, etc.
|
|
|
|
|
// in thinking about this, you could convince me to optimize the PCE background renderer now.
|
|
|
|
|
// Its way simple in comparison. But the PCE sprite renderer is way worse than gen.
|
|
|
|
|
for (int x = 0; x < FrameWidth; x++)
|
2011-01-11 02:55:51 +00:00
|
|
|
|
{
|
2012-08-22 04:54:36 +00:00
|
|
|
|
int xTile = Math.Abs(((x + (1024-xScroll)) / 8) % NameTableWidth);
|
|
|
|
|
int xOfs = Math.Abs((x + (1024-xScroll)) & 7);
|
2012-08-24 00:37:54 +00:00
|
|
|
|
int yOfs = (ScanLine + yScroll) % 8;
|
2011-10-18 03:48:37 +00:00
|
|
|
|
int cellOfs = nameTableBase + (yTile * NameTableWidth * 2) + (xTile * 2);
|
|
|
|
|
int nameTableEntry = VRAM[cellOfs] | (VRAM[cellOfs+1] << 8);
|
|
|
|
|
int patternNo = nameTableEntry & 0x7FF;
|
|
|
|
|
bool hFlip = ((nameTableEntry >> 11) & 1) != 0;
|
|
|
|
|
bool vFlip = ((nameTableEntry >> 12) & 1) != 0;
|
|
|
|
|
bool priority = ((nameTableEntry >> 15) & 1) != 0;
|
|
|
|
|
int palette = (nameTableEntry >> 13) & 3;
|
|
|
|
|
|
|
|
|
|
if (priority && PriorityBuffer[x] >= highPriority) continue;
|
2012-08-30 04:29:33 +00:00
|
|
|
|
if (!priority && PriorityBuffer[x] >= lowPriority) continue;
|
2011-10-18 03:48:37 +00:00
|
|
|
|
|
|
|
|
|
if (vFlip) yOfs = 7 - yOfs;
|
|
|
|
|
if (hFlip) xOfs = 7 - xOfs;
|
|
|
|
|
|
|
|
|
|
int texel = PatternBuffer[(patternNo * 64) + (yOfs * 8) + (xOfs)];
|
|
|
|
|
if (texel == 0) continue;
|
|
|
|
|
int pixel = Palette[(palette * 16) + texel];
|
|
|
|
|
FrameBuffer[(ScanLine * FrameWidth) + x] = pixel;
|
|
|
|
|
PriorityBuffer[x] = (byte) (priority ? highPriority : lowPriority);
|
2011-01-11 02:55:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-01 17:06:25 +00:00
|
|
|
|
void RenderScrollA()
|
2011-01-11 02:55:51 +00:00
|
|
|
|
{
|
2011-10-18 03:48:37 +00:00
|
|
|
|
// todo scroll values
|
2012-08-24 04:45:09 +00:00
|
|
|
|
int hscroll = CalcHScrollPlaneA(ScanLine);
|
|
|
|
|
int vscroll = VSRAM[0] & 0x3FF;
|
2011-10-18 03:48:37 +00:00
|
|
|
|
RenderBackgroundScanline(hscroll, vscroll, NameTableAddrA, 2, 5);
|
2011-01-11 02:55:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-10-01 17:06:25 +00:00
|
|
|
|
void RenderScrollB()
|
2011-01-11 02:55:51 +00:00
|
|
|
|
{
|
2012-08-24 04:45:09 +00:00
|
|
|
|
int hscroll = CalcHScrollPlaneB(ScanLine);
|
|
|
|
|
int vscroll = VSRAM[1] & 0x3FF;
|
2011-10-18 03:48:37 +00:00
|
|
|
|
RenderBackgroundScanline(hscroll, vscroll, NameTableAddrB, 1, 4);
|
2011-01-11 02:55:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-10-16 06:23:15 +00:00
|
|
|
|
static readonly int[] SpriteSizeTable = { 8, 16, 24, 32 };
|
|
|
|
|
Sprite sprite;
|
2011-01-11 02:55:51 +00:00
|
|
|
|
|
2011-10-18 03:48:37 +00:00
|
|
|
|
void RenderSpritesScanline()
|
2011-01-11 02:55:51 +00:00
|
|
|
|
{
|
2011-10-16 06:23:15 +00:00
|
|
|
|
int scanLineBase = ScanLine * FrameWidth;
|
|
|
|
|
int processedSprites = 0;
|
2011-10-16 16:04:08 +00:00
|
|
|
|
// This is incredibly unoptimized. TODO...
|
2011-10-16 06:23:15 +00:00
|
|
|
|
|
|
|
|
|
FetchSprite(0);
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
if (sprite.Y > ScanLine || sprite.Y+sprite.HeightPixels <= ScanLine)
|
|
|
|
|
goto nextSprite;
|
|
|
|
|
if (sprite.X + sprite.WidthPixels <= 0)
|
|
|
|
|
goto nextSprite;
|
2012-08-24 02:30:20 +00:00
|
|
|
|
if (sprite.X == -128)
|
|
|
|
|
throw new Exception("bleeeh"); // masking code is not really tested
|
|
|
|
|
//break; // TODO this satisfies masking mode 1 but not masking mode 2
|
2011-10-16 06:23:15 +00:00
|
|
|
|
|
|
|
|
|
if (sprite.HeightCells == 2)
|
|
|
|
|
sprite.HeightCells = 2;
|
|
|
|
|
|
|
|
|
|
int yline = ScanLine - sprite.Y;
|
2011-10-16 16:04:08 +00:00
|
|
|
|
if (sprite.VFlip)
|
|
|
|
|
yline = sprite.HeightPixels - 1 - yline;
|
2011-10-16 06:23:15 +00:00
|
|
|
|
int paletteBase = sprite.Palette * 16;
|
2011-10-16 16:04:08 +00:00
|
|
|
|
if (sprite.HFlip == false)
|
2011-10-16 06:23:15 +00:00
|
|
|
|
{
|
2011-10-16 16:04:08 +00:00
|
|
|
|
int pattern = sprite.PatternIndex + ((yline / 8));
|
|
|
|
|
|
|
|
|
|
for (int xi = 0; xi < sprite.WidthPixels; xi++)
|
|
|
|
|
{
|
2011-10-18 03:48:37 +00:00
|
|
|
|
if (sprite.X + xi < 0 || sprite.X + xi >= FrameWidth)
|
2011-10-16 16:04:08 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
2012-08-24 02:30:20 +00:00
|
|
|
|
if (sprite.Priority == false && PriorityBuffer[sprite.X + xi] >= 3) continue;
|
|
|
|
|
if (PriorityBuffer[sprite.X + xi] == 9) continue;
|
2011-10-18 03:48:37 +00:00
|
|
|
|
|
2011-10-16 16:04:08 +00:00
|
|
|
|
int pixel = PatternBuffer[((pattern + ((xi / 8) * sprite.HeightCells)) * 64) + ((yline & 7) * 8) + (xi & 7)];
|
|
|
|
|
if (pixel != 0)
|
2011-10-18 03:48:37 +00:00
|
|
|
|
{
|
2011-10-16 16:04:08 +00:00
|
|
|
|
FrameBuffer[scanLineBase + sprite.X + xi] = Palette[paletteBase + pixel];
|
2012-08-24 02:30:20 +00:00
|
|
|
|
PriorityBuffer[sprite.X + xi] = 9;
|
2011-10-18 03:48:37 +00:00
|
|
|
|
}
|
2011-10-16 16:04:08 +00:00
|
|
|
|
}
|
|
|
|
|
} else { // HFlip
|
|
|
|
|
int pattern = sprite.PatternIndex + ((yline / 8)) + (sprite.HeightCells * (sprite.WidthCells - 1));
|
|
|
|
|
|
|
|
|
|
for (int xi = 0; xi < sprite.WidthPixels; xi++)
|
|
|
|
|
{
|
2011-10-18 03:48:37 +00:00
|
|
|
|
if (sprite.X + xi < 0 || sprite.X + xi >= FrameWidth)
|
2011-10-16 16:04:08 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
2012-08-24 02:30:20 +00:00
|
|
|
|
if (sprite.Priority == false && PriorityBuffer[sprite.X + xi] >= 3) continue;
|
2011-10-18 03:48:37 +00:00
|
|
|
|
|
2011-10-16 16:04:08 +00:00
|
|
|
|
int pixel = PatternBuffer[((pattern + ((-xi / 8) * sprite.HeightCells)) * 64) + ((yline & 7) * 8) + (7 - (xi & 7))];
|
|
|
|
|
if (pixel != 0)
|
2011-10-18 03:48:37 +00:00
|
|
|
|
{
|
2011-10-16 16:04:08 +00:00
|
|
|
|
FrameBuffer[scanLineBase + sprite.X + xi] = Palette[paletteBase + pixel];
|
2012-08-24 02:30:20 +00:00
|
|
|
|
PriorityBuffer[sprite.X + xi] = 9;
|
2011-10-18 03:48:37 +00:00
|
|
|
|
}
|
2011-10-16 16:04:08 +00:00
|
|
|
|
}
|
2011-10-16 06:23:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nextSprite:
|
|
|
|
|
if (sprite.Link == 0)
|
|
|
|
|
break;
|
|
|
|
|
if (++processedSprites > 80)
|
|
|
|
|
break;
|
|
|
|
|
FetchSprite(sprite.Link);
|
|
|
|
|
}
|
2011-01-11 02:55:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-10-16 06:23:15 +00:00
|
|
|
|
void FetchSprite(int spriteNo)
|
2011-01-11 02:55:51 +00:00
|
|
|
|
{
|
2012-08-26 03:43:35 +00:00
|
|
|
|
int SatBase = SpriteAttributeTableAddr + (spriteNo*8);
|
|
|
|
|
sprite.Y = (VRAM[SatBase + 0] | (VRAM[SatBase + 1] << 8) & 0x3FF) - 128;
|
|
|
|
|
sprite.X = (VRAM[SatBase + 6] | (VRAM[SatBase + 7] << 8) & 0x3FF) - 128;
|
|
|
|
|
sprite.WidthPixels = SpriteSizeTable[(VRAM[SatBase + 3] >> 2) & 3];
|
|
|
|
|
sprite.HeightPixels = SpriteSizeTable[VRAM[SatBase + 3] & 3];
|
|
|
|
|
sprite.WidthCells = ((VRAM[SatBase + 3] >> 2) & 3) + 1;
|
|
|
|
|
sprite.HeightCells = (VRAM[SatBase + 3] & 3) + 1;
|
|
|
|
|
sprite.Link = VRAM[SatBase + 2] & 0x7F;
|
|
|
|
|
sprite.PatternIndex = (VRAM[SatBase + 4] | (VRAM[SatBase + 5] << 8)) & 0x7FF;
|
|
|
|
|
sprite.HFlip = ((VRAM[SatBase + 5] >> 3) & 1) != 0;
|
|
|
|
|
sprite.VFlip = ((VRAM[SatBase + 5] >> 4) & 1) != 0;
|
|
|
|
|
sprite.Palette = (VRAM[SatBase + 5] >> 5) & 3;
|
|
|
|
|
sprite.Priority = ((VRAM[SatBase + 5] >> 7) & 1) != 0;
|
2011-01-11 02:55:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Sprite
|
|
|
|
|
{
|
|
|
|
|
public int X, Y;
|
2011-10-16 06:23:15 +00:00
|
|
|
|
public int WidthPixels, HeightPixels;
|
|
|
|
|
public int WidthCells, HeightCells;
|
2011-01-11 02:55:51 +00:00
|
|
|
|
public int Link;
|
|
|
|
|
public int Palette;
|
|
|
|
|
public int PatternIndex;
|
2011-10-16 16:04:08 +00:00
|
|
|
|
public bool Priority;
|
|
|
|
|
public bool HFlip;
|
|
|
|
|
public bool VFlip;
|
2011-01-11 02:55:51 +00:00
|
|
|
|
}
|
2012-08-24 04:45:09 +00:00
|
|
|
|
|
|
|
|
|
int CalcHScrollPlaneA(int line)
|
|
|
|
|
{
|
|
|
|
|
int ofs = 0;
|
|
|
|
|
switch (Registers[11] & 3)
|
|
|
|
|
{
|
|
|
|
|
case 0: ofs = HScrollTableAddr; break;
|
|
|
|
|
case 1: ofs = HScrollTableAddr + ((line & 7) * 4); break;
|
|
|
|
|
case 2: ofs = HScrollTableAddr + ((line & ~7) * 4); break;
|
|
|
|
|
case 3: ofs = HScrollTableAddr + (line * 4); break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int value = VRAM[ofs] | (VRAM[ofs + 1] << 8);
|
|
|
|
|
return value & 0x3FF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CalcHScrollPlaneB(int line)
|
|
|
|
|
{
|
|
|
|
|
int ofs = 0;
|
|
|
|
|
switch (Registers[11] & 3)
|
|
|
|
|
{
|
|
|
|
|
case 0: ofs = HScrollTableAddr; break;
|
|
|
|
|
case 1: ofs = HScrollTableAddr + ((line & 7) * 4); break;
|
|
|
|
|
case 2: ofs = HScrollTableAddr + ((line & ~7) * 4); break;
|
|
|
|
|
case 3: ofs = HScrollTableAddr + (line * 4); break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int value = VRAM[ofs + 2] | (VRAM[ofs + 3] << 8);
|
|
|
|
|
return value & 0x3FF;
|
|
|
|
|
}
|
2011-01-11 02:55:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|