Update SGX with new timing & support frameskip... huge success!

100% SGX compatibility (not that this is difficult)
This commit is contained in:
beirich 2011-07-20 03:57:41 +00:00
parent 47def064fa
commit 3bedad1e95
5 changed files with 139 additions and 78 deletions

View File

@ -9,44 +9,41 @@ General:
Right now the emulator is simply displaying a framebuffer. Eventually, we will do an update that emulates NTSC.
But for now we're letting this be. There's no intermediate step between emulating a framebuffer and emulating a TV.
Air Zonk - Seems OK now - previous problem game
Battle Ace - Some gfx glitches
Cadash - Minor raster issue. Timing sensitive game.
Air Zonk - OK now - previous problem game
Cadash - Req HBlank hack to eliminate minor visual artifact
Chase HQ - Press start -"O" sprite gets left on screen. probably timing on SATB DMA
Coryoon - Seems OK now - previous problem game
Coryoon - OK now - previous problem game
Cross Wiber - Minor; Raster on wrong line
Davis Cup Tennis - Some timing issue, splash screen is too slow
Dungeon Explorer - Seems OK now - previous problem game
Legend of Hero Ton- Slight gfx- check top of screen
Lode Runner - Seems OK now - previous problem game
Madoo Granzort - Graphics issues because SGX VPC renderer is not using new frame timing
Dungeon Explorer - OK now - previous problem game
Lode Runner - OK now - previous problem game
MML Demo - Echo channels are too loud (equal volume!)
Outrun - Seems OK now - previous problem game
Power Drift - Timing glitch... starting new game runs slower than it should
Power Tennis - Elaborate intro screen doesnt display right
Sinistron - Much less bad raster effect errors now
Outrun - Req HBlank hack to eliminate visual artifact
Tiger Road - On second level, sprites should be getting masked from the top status area somehow
===================================
Games that need TV Emulation (to varying degrees)
===================================
Greater degrees:
Final Blaster - Intro does crazy shit with video modes; not sure what if anything to do about it
Griffon - Screen goes black because game changes video mode mid-frame
===================================
Games with 'Frame Too Tall' issues:
===================================
Lesser degrees:
Aero Blaster - Bottom of Screen extends too many lines - like 3 extra or so
Alice - Screen too tall; glitches when scrolling up, but in pcejin also,
but not ootake; ergo probably a timing artifact
Dead Moon - Screen is too tall
Jack Nicholas Golf- Some screens are too tall and reveal bad gfx below the intended visible screen
Legend of Hero Ton- Slight gfx issue on top of screen
Madoo Granzort - Screen ~5 pixels too tall
Metal Stoker - Tearing when scrolling vertically at bottom of screen - screen too tall?
Side Arms - Screen is like 4 pixels too tall
===================================
Stuff I Fixed That's Not In Other Docs:
===================================
+ Street Fighter II special mapper
+ Populous has special SaveRAM, 32k starting at page $40.
@ -63,4 +60,4 @@ Stuff I Fixed That's Not In Other Docs:
4 color modes. It appears to be the only game that requires this, and it's a shitty game. :(
+ The ROM of Paranoia (J) that is commonly labeled as the authoritative dump - I assume its a bad
dump, because it dies during the 3rd stage on ALL EMULATORS. Alternative versions of the rom
work fine.
work fine.

View File

@ -120,6 +120,18 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
Cpu.WriteMemory21 = WriteMemoryPopulous;
}
// Ok, yes, HBlankPeriod's only purpose is game-specific hax.
// 1) At least they're not coded directly into the emulator, but instead data-driven.
// 2) The games (2) which have custom HBlankPeriods work without it, the override only
// serves to clean up minor gfx anomalies.
// 3) There's no point in haxing the timing with incorrect values in an attempt to avoid this.
// The proper fix is cycle-accurate/bus-accurate timing. That isn't coming to the C#
// version of this core. Lets just acknolwedge that the timing is imperfect and fix
// it in the least intrusive and most honest way we can.
if (game.GetOptions().ContainsStartsWith("HBlankPeriod"))
VDC1.HBlankCycles = int.Parse(game.GetOptions().GetOptionValue("HBlankPeriod"));
Cpu.ResetPC();
SetupMemoryDomains();
}
@ -139,7 +151,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
PSG.BeginFrame(Cpu.TotalExecutedCycles);
if (SuperGrafx)
VPC.ExecFrame(); // TODO supergrafx frameskipping (waiting on a larger update of VPC frame timing, once I get VDC timing correct)
VPC.ExecFrame(render);
else
VDC1.ExecFrame(render);

View File

@ -21,10 +21,11 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
public int ScanLine;
public int BackgroundY;
public int RCRCounter;
private int ActiveLine;
public int ActiveLine;
private byte[] PriorityBuffer = new byte[512];
private byte[] InterSpritePriorityBuffer = new byte[512];
public int HBlankCycles = 79;
public void ExecFrame(bool render)
{
@ -40,8 +41,6 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
if (ScanLine == ActiveDisplayStartLine)
RCRCounter = 0x40;
const int hblankCycles = 79;
if (ScanLine == VBlankLine)
UpdateSpriteAttributeTable();
@ -54,7 +53,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
}
}
cpu.Execute(hblankCycles);
cpu.Execute(HBlankCycles);
if (InActiveDisplay)
{
@ -83,7 +82,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
if ((StatusByte & (StatusVerticalBlanking | StatusVramSatDmaComplete)) != 0)
cpu.IRQ1Assert = true;
cpu.Execute(455 - hblankCycles - 2);
cpu.Execute(455 - HBlankCycles - 2);
if (InActiveDisplay == false && DmaRequested)
RunDmaForScanline();

View File

@ -10,10 +10,6 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
// Responsible for merging VDC1 and VDC2 data on the SuperGrafx.
// Pretty much all documentation on the SuperGrafx courtesy of Charles MacDonald.
// VPC Frame timing has not been updated with new stuff. I expect Madoo Granzort to be fixed once the timing is updated.
// However, 1) The new frame timing is not working 100% yet, 2) I haven't decided if I want to continue having separate
// rendering in the VPC and the VDC.
public sealed class VPC : IVideoProvider
{
public VDC VDC1;
@ -87,6 +83,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
public void LoadStateBinary(BinaryReader reader)
{
Registers = reader.ReadBytes(7);
WriteVPC(0x0E, Registers[6]);
}
public void SaveStateText(TextWriter writer)
@ -109,14 +106,19 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
else
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
}
WriteVPC(0x0E, Registers[6]);
}
// We use a single priority mode for the whole frame.
// No commercial SGX games really use the 'window' features AFAIK.
// And there are no homebrew SGX games I know of.
// Maybe we'll emulate it in the native-code version.
private const int RCR = 6;
private const int BXR = 7;
private const int BYR = 8;
private const int VDW = 13;
private const int DCR = 15;
private int EffectivePriorityMode = 0;
@ -127,7 +129,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
private byte[] PriorityBuffer = new byte[512];
private byte[] InterSpritePriorityBuffer = new byte[512];
public void ExecFrame()
public void ExecFrame(bool render)
{
// Determine the effective priority mode.
if (Window1Width < 0x40 && Window2Width < 0x40)
@ -145,12 +147,33 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
FrameHeight = VDC1.BufferHeight;
FrameBuffer = VDC1.GetVideoBuffer();
for (int ScanLine = 0; ScanLine < 262; ScanLine++)
int ScanLine = 0;
while (true)
{
VDC1.ScanLine = ScanLine;
VDC2.ScanLine = ScanLine;
if ((ScanLine + 64) == (VDC1.Registers[6] & 0x3FF))
int ActiveDisplayStartLine = VDC1.DisplayStartLine;
int VBlankLine = ActiveDisplayStartLine + VDC1.Registers[VDW] + 1;
if (VBlankLine > 261)
VBlankLine = 261;
VDC1.ActiveLine = ScanLine - ActiveDisplayStartLine;
VDC2.ActiveLine = VDC1.ActiveLine;
bool InActiveDisplay = (ScanLine >= ActiveDisplayStartLine) && (ScanLine < VBlankLine);
if (ScanLine == ActiveDisplayStartLine)
{
VDC1.RCRCounter = 0x40;
VDC2.RCRCounter = 0x40;
}
if (ScanLine == VBlankLine)
{
VDC1.UpdateSpriteAttributeTable();
VDC2.UpdateSpriteAttributeTable();
}
if (VDC1.RCRCounter == (VDC1.Registers[RCR] & 0x3FF))
{
if (VDC1.RasterCompareInterruptEnabled)
{
@ -159,7 +182,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
}
}
if ((ScanLine + 64) == (VDC2.Registers[6] & 0x3FF))
if (VDC2.RCRCounter == (VDC2.Registers[RCR] & 0x3FF))
{
if (VDC2.RasterCompareInterruptEnabled)
{
@ -168,34 +191,76 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
}
}
if (ScanLine == 240 && VDC1.VBlankInterruptEnabled)
CPU.Execute(VDC1.HBlankCycles);
if (InActiveDisplay)
{
if (ScanLine == ActiveDisplayStartLine)
{
VDC1.BackgroundY = VDC1.Registers[BYR];
VDC2.BackgroundY = VDC2.Registers[BYR];
}
else
{
VDC1.BackgroundY++;
VDC1.BackgroundY &= 0x01FF;
VDC2.BackgroundY++;
VDC2.BackgroundY &= 0x01FF;
}
if (render) RenderScanLine();
}
if (ScanLine == VBlankLine && VDC1.VBlankInterruptEnabled)
VDC1.StatusByte |= VDC.StatusVerticalBlanking;
CPU.IRQ1Assert = true;
}
if (ScanLine == 240 && VDC2.VBlankInterruptEnabled)
{
if (ScanLine == VBlankLine && VDC2.VBlankInterruptEnabled)
VDC2.StatusByte |= VDC.StatusVerticalBlanking;
CPU.IRQ1Assert = true;
if (ScanLine == VBlankLine + 4 && VDC1.SatDmaPerformed)
{
VDC1.SatDmaPerformed = false;
if ((VDC1.Registers[DCR] & 1) > 0)
VDC1.StatusByte |= VDC.StatusVramSatDmaComplete;
}
CPU.Execute(455);
if (ScanLine == VBlankLine + 4 && VDC2.SatDmaPerformed)
{
VDC2.SatDmaPerformed = false;
if ((VDC2.Registers[DCR] & 1) > 0)
VDC2.StatusByte |= VDC.StatusVramSatDmaComplete;
}
if (ScanLine < FrameHeight)
RenderScanLine();
CPU.Execute(2);
if ((VDC1.StatusByte & (VDC.StatusVerticalBlanking | VDC.StatusVramSatDmaComplete)) != 0)
CPU.IRQ1Assert = true;
if ((VDC2.StatusByte & (VDC.StatusVerticalBlanking | VDC.StatusVramSatDmaComplete)) != 0)
CPU.IRQ1Assert = true;
CPU.Execute(455 - VDC1.HBlankCycles - 2);
if (InActiveDisplay == false && VDC1.DmaRequested)
VDC1.RunDmaForScanline();
if (InActiveDisplay == false && VDC2.DmaRequested)
VDC2.RunDmaForScanline();
VDC1.RCRCounter++;
VDC2.RCRCounter++;
ScanLine++;
if (ScanLine == VCE.NumberOfScanlines)
break;
}
}
private void RenderScanLine()
{
if (VDC1.ScanLine == 0)
{
VDC1.BackgroundY = VDC1.Registers[BYR];
VDC2.BackgroundY = VDC2.Registers[BYR];
}
if (VDC1.ActiveLine >= FrameHeight)
return;
InitializeScanLine(VDC1.ScanLine);
InitializeScanLine(VDC1.ActiveLine);
switch (EffectivePriorityMode)
{
@ -212,12 +277,6 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
RenderSpritesScanline(VDC2, 1, 13);
break;
}
if (VDC1.ScanLine == FrameHeight - 1)
{
VDC1.UpdateSpriteAttributeTable();
VDC2.UpdateSpriteAttributeTable();
}
}
private void InitializeScanLine(int scanline)
@ -233,18 +292,12 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
private void RenderBackgroundScanline(VDC vdc, byte priority)
{
if (vdc.BackgroundEnabled == false)
{
vdc.BackgroundY++;
vdc.BackgroundY &= 0x01FF;
return;
}
int vertLine = vdc.BackgroundY;
vertLine %= vdc.BatHeight * 8;
int yTile = (vertLine / 8);
int yOfs = vertLine % 8;
vdc.BackgroundY++;
vdc.BackgroundY &= 0x01FF;
int xScroll = vdc.Registers[BXR] & 0x3FF;
for (int x = 0; x < FrameWidth; x++)
@ -259,7 +312,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
byte c = vdc.PatternBuffer[(tileNo * 64) + (yOfs * 8) + xOfs];
if (c != 0)
{
FrameBuffer[(vdc.ScanLine * FrameWidth) + x] = VCE.Palette[paletteBase + c];
FrameBuffer[(vdc.ActiveLine * FrameWidth) + x] = VCE.Palette[paletteBase + c];
PriorityBuffer[x] = priority;
}
}
@ -282,7 +335,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
ushort flags = vdc.SpriteAttributeTable[(i * 4) + 3];
int height = heightTable[(flags >> 12) & 3];
if (y + height <= vdc.ScanLine || y > vdc.ScanLine)
if (y + height <= vdc.ActiveLine || y > vdc.ActiveLine)
continue;
int patternNo = (((vdc.SpriteAttributeTable[(i * 4) + 2]) >> 1) & 0x1FF);
@ -298,11 +351,11 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
int yofs;
if (vflip == false)
{
yofs = (vdc.ScanLine - y) & 15;
yofs = (vdc.ActiveLine - y) & 15;
if (height == 32)
{
patternNo &= 0x1FD;
if (vdc.ScanLine - y >= 16)
if (vdc.ActiveLine - y >= 16)
{
y += 16;
patternNo += 2;
@ -311,17 +364,17 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
else if (height == 64)
{
patternNo &= 0x1F9;
if (vdc.ScanLine - y >= 48)
if (vdc.ActiveLine - y >= 48)
{
y += 48;
patternNo += 6;
}
else if (vdc.ScanLine - y >= 32)
else if (vdc.ActiveLine - y >= 32)
{
y += 32;
patternNo += 4;
}
else if (vdc.ScanLine - y >= 16)
else if (vdc.ActiveLine - y >= 16)
{
y += 16;
patternNo += 2;
@ -330,11 +383,11 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
}
else // vflip == true
{
yofs = 15 - ((vdc.ScanLine - y) & 15);
yofs = 15 - ((vdc.ActiveLine - y) & 15);
if (height == 32)
{
patternNo &= 0x1FD;
if (vdc.ScanLine - y < 16)
if (vdc.ActiveLine - y < 16)
{
y += 16;
patternNo += 2;
@ -343,17 +396,17 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
else if (height == 64)
{
patternNo &= 0x1F9;
if (vdc.ScanLine - y < 16)
if (vdc.ActiveLine - y < 16)
{
y += 48;
patternNo += 6;
}
else if (vdc.ScanLine - y < 32)
else if (vdc.ActiveLine - y < 32)
{
y += 32;
patternNo += 4;
}
else if (vdc.ScanLine - y < 48)
else if (vdc.ActiveLine - y < 48)
{
y += 16;
patternNo += 2;
@ -373,7 +426,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
byte myPriority = priority ? highPriority : lowPriority;
if (PriorityBuffer[xs] < myPriority)
{
FrameBuffer[(vdc.ScanLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
FrameBuffer[(vdc.ActiveLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
PriorityBuffer[xs] = myPriority;
}
}
@ -392,7 +445,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
byte myPriority = priority ? highPriority : lowPriority;
if (PriorityBuffer[xs] < myPriority)
{
FrameBuffer[(vdc.ScanLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
FrameBuffer[(vdc.ActiveLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
PriorityBuffer[xs] = myPriority;
}
}
@ -414,7 +467,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
byte myPriority = priority ? highPriority : lowPriority;
if (PriorityBuffer[xs] < myPriority)
{
FrameBuffer[(vdc.ScanLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
FrameBuffer[(vdc.ActiveLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
PriorityBuffer[xs] = myPriority;
}
}
@ -432,7 +485,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
byte myPriority = priority ? highPriority : lowPriority;
if (PriorityBuffer[xs] < myPriority)
{
FrameBuffer[(vdc.ScanLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
FrameBuffer[(vdc.ActiveLine * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel];
PriorityBuffer[xs] = myPriority;
}
}

View File

@ -2031,7 +2031,7 @@ C9D7426A Break In PCE
5C4D1991 Bullfight Ring no Haja PCE
D233C05A Burning Angels PCE
47AFE6D7 Bloody Wolf PCE
BB0B3AEF Cadash PCE
BB0B3AEF Cadash PCE HBlankPeriod=83
3F9F95A4 CD-ROM System Card V1.00 (J) PCE BRAM
FF2A5EC3 CD-ROM System Card V2.00 (U) PCE BRAM
283B74E0 CD-ROM System Card V2.10 (J) PCE BRAM
@ -2104,7 +2104,7 @@ BF3E2CC7 Hani in the Sky PCE
9897FA86 Hani on the Road PCE
44E7DF53 Hatris PCE
EB923DE5 Heavy Unit PCE
1CAB1EE6 Hisou Kihei Serd PCE
1CAB1EE6 Hisou Kihei Serd PCE BRAM
7ACB60C8 Hit the Ice PCE
B01EE703 Hono no Tataka Tamako Dodge Danpei PCE
9EC6FC6C Idol Hanafuda Fan Club PCE
@ -2165,7 +2165,7 @@ C159761B Night Creatures PCE
4D3B0BC9 Obocchama Kun PCE
8C565CB6 Ordyne PCE
5CDB3F5B Outlive PCE
E203F223 Out Run PCE
E203F223 Out Run PCE HBlankPeriod=88
B74EC562 Override PCE
7632DB90 P-47 - The Freedom Fighter PCE
D6E30CCD Pac-Land PCE
@ -2227,7 +2227,7 @@ DDC3E809 Thunder Blade PCE
9107BCC8 Tiger Shark PCE
CFEC1D6A Time Cruise 2 PCE
53B7784B Toilet Kids PCE
FE451C22 Tongueman's Logic PCE
FE451C22 D Tongueman's Logic PCE BRAM
72E00BC4 The Tower of Druaga PCE
EB045EDF Turrican PCE
97FE5BCF TV Sports Hockey PCE