SMS: implement zoomed sprites; VDP fixes; Ax Battler GG fixed

This commit is contained in:
beirich 2011-01-16 06:31:14 +00:00
parent 0d088bb15b
commit d1110de5de
11 changed files with 444 additions and 361 deletions

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -49,6 +49,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="Consoles\Sega\SMS\MemoryMap.CodeMasters.cs" /> <Compile Include="Consoles\Sega\SMS\MemoryMap.CodeMasters.cs" />
<Compile Include="Consoles\Sega\SMS\MemoryMap.Sega.cs" /> <Compile Include="Consoles\Sega\SMS\MemoryMap.Sega.cs" />
<Compile Include="Consoles\Sega\SMS\VDP.Mode4.cs" />
<Compile Include="Consoles\Sega\SMS\VDP.Tables.cs" /> <Compile Include="Consoles\Sega\SMS\VDP.Tables.cs" />
<Compile Include="CPUs\68000\Diassembler.cs" /> <Compile Include="CPUs\68000\Diassembler.cs" />
<Compile Include="CPUs\68000\Instructions\BitArithemetic.cs" /> <Compile Include="CPUs\68000\Instructions\BitArithemetic.cs" />
@ -150,7 +151,6 @@
<Content Include="Consoles\Sega\SMS\Compat.txt" /> <Content Include="Consoles\Sega\SMS\Compat.txt" />
<Content Include="Notes.txt" /> <Content Include="Notes.txt" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -1,4 +1,6 @@
namespace BizHawk.Emulation.Consoles.TurboGrafx using System;
namespace BizHawk.Emulation.Consoles.TurboGrafx
{ {
// This rendering code is only used for TurboGrafx/TurboCD Mode. // This rendering code is only used for TurboGrafx/TurboCD Mode.
// In SuperGrafx mode, separate rendering functions in the VPC class are used. // In SuperGrafx mode, separate rendering functions in the VPC class are used.
@ -104,9 +106,7 @@
private void RenderBackgroundScanline() private void RenderBackgroundScanline()
{ {
// clear priority buffer Array.Clear(PriorityBuffer, 0, FrameWidth);
for (int i = 0; i < FrameWidth; i++)
PriorityBuffer[i] = 0;
if (BackgroundEnabled == false) if (BackgroundEnabled == false)
{ {
@ -153,9 +153,7 @@
if (SpritesEnabled == false) if (SpritesEnabled == false)
return; return;
// clear inter-sprite priority buffer Array.Clear(InterSpritePriorityBuffer, 0, FrameWidth);
for (int i = 0; i < FrameWidth; i++)
InterSpritePriorityBuffer[i] = 0;
for (int i = 0; i < 64; i++) for (int i = 0; i < 64; i++)
{ {

View File

@ -124,7 +124,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
break; break;
case CR: case CR:
//if (Registers[CR] == 0) //if (Registers[CR] == 0)
Log.Note("CPU", "****************** WRITE TO CR: {0:X}", Registers[CR]); //Log.Note("CPU", "****************** WRITE TO CR: {0:X}", Registers[CR]);
break; break;
case BXR: case BXR:
Registers[BXR] &= 0x3FF; Registers[BXR] &= 0x3FF;
@ -132,7 +132,7 @@ break;
case BYR: case BYR:
Registers[BYR] &= 0x1FF; Registers[BYR] &= 0x1FF;
BackgroundY = Registers[BYR]; BackgroundY = Registers[BYR];
Console.WriteLine("Updating BYR to {0} at scanline {1}", BackgroundY, ScanLine); //Console.WriteLine("Updating BYR to {0} at scanline {1}", BackgroundY, ScanLine);
break; break;
case HDR: // Horizontal Display Register - update framebuffer size case HDR: // Horizontal Display Register - update framebuffer size
FrameWidth = RequestedFrameWidth; FrameWidth = RequestedFrameWidth;

View File

@ -221,8 +221,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
private void InitializeScanLine(int scanline) private void InitializeScanLine(int scanline)
{ {
// Clear priority buffer // Clear priority buffer
for (int i = 0; i < FrameWidth; i++) Array.Clear(PriorityBuffer, 0, FrameWidth);
PriorityBuffer[i] = 0;
// Initialize scanline to background color // Initialize scanline to background color
for (int i = 0; i < FrameWidth; i++) for (int i = 0; i < FrameWidth; i++)
@ -272,8 +271,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
return; return;
// clear inter-sprite priority buffer // clear inter-sprite priority buffer
for (int i = 0; i < FrameWidth; i++) Array.Clear(InterSpritePriorityBuffer, 0, FrameWidth);
InterSpritePriorityBuffer[i] = 0;
for (int i = 0; i < 64; i++) for (int i = 0; i < 64; i++)
{ {

View File

@ -3,16 +3,9 @@
* CodeMasters games use a custom mapper and special video modes (both implemented) * CodeMasters games use a custom mapper and special video modes (both implemented)
+ Fantastic Dizzy crashes shortly after starting a new game. Investigating. + Fantastic Dizzy crashes shortly after starting a new game. Investigating.
* F16 Fighting Falcon uses old SG-1000 video mode, and doesn't work on a GG/Genesis either. * F16 Fighting Falcon uses old SG-1000 video mode.
That said.. I will fix CodeMasters games and F16 Fighting Falcon eventually, and add full
SG-1000 support, it's just not the high priority currently.
* Raster effects are sometimes on the wrong line... but it's correct 99% of the time... :|
Update: I think this is a timing issue resulting in latching/rendering at the wrong part
of a scanline. I should play with this timing.
======= Game Gear compatibility issues ======= ======= Game Gear compatibility issues =======
* Axe Battler is broken. * Outrun has raster effect on the wrong line. I've been able to modify interrupt code to
* "SEGA" logo audio sample plays at low rate in Sonic & Tails. fix it, but so far, not without breaking other games.

View File

@ -8,12 +8,10 @@ using BizHawk.Emulation.Sound;
/***************************************************** /*****************************************************
VDP: VDP:
+ Double Size Sprites
+ HCounter + HCounter
+ Old TMS video modes (SG-1000) + Old TMS video modes (SG-1000)
GENERAL: GENERAL:
+ Debug some GameGear game issues.
+ Port 3F emulation (Japanese BIOS) + Port 3F emulation (Japanese BIOS)
+ Try to clean up the organization of the source code. + Try to clean up the organization of the source code.
+ SG-1000 support + SG-1000 support
@ -82,7 +80,7 @@ namespace BizHawk.Emulation.Consoles.Sega
Cpu.ReadHardware = ReadPort; Cpu.ReadHardware = ReadPort;
Cpu.WriteHardware = WritePort; Cpu.WriteHardware = WritePort;
Vdp = new VDP(IsGameGear ? VdpMode.GameGear : VdpMode.SMS, DisplayType); Vdp = new VDP(Cpu, IsGameGear ? VdpMode.GameGear : VdpMode.SMS, DisplayType);
PSG = new SN76489(); PSG = new SN76489();
YM2413 = new YM2413(); YM2413 = new YM2413();
SoundMixer = new SoundMixer(PSG, YM2413); SoundMixer = new SoundMixer(PSG, YM2413);
@ -98,7 +96,7 @@ namespace BizHawk.Emulation.Consoles.Sega
{ {
Cpu.Reset(); Cpu.Reset();
Cpu.RegisterSP = 0xDFF0; Cpu.RegisterSP = 0xDFF0;
Vdp = new VDP(Vdp.VdpMode, DisplayType); Vdp = new VDP(Cpu, Vdp.VdpMode, DisplayType);
PSG.Reset(); PSG.Reset();
YM2413.Reset(); YM2413.Reset();
SystemRam = new byte[0x2000]; SystemRam = new byte[0x2000];
@ -140,8 +138,8 @@ namespace BizHawk.Emulation.Consoles.Sega
case 0x06: return 0xFF; case 0x06: return 0xFF;
case 0x7E: return Vdp.ReadVLineCounter(); case 0x7E: return Vdp.ReadVLineCounter();
case 0x7F: break; // hline counter TODO case 0x7F: break; // hline counter TODO
case 0xBE: return Vdp.ReadVram(); case 0xBE: return Vdp.ReadData();
case 0xBF: Cpu.Interrupt = false; return Vdp.ReadVdpStatus(); case 0xBF: return Vdp.ReadVdpStatus();
case 0xC0: case 0xC0:
case 0xDC: return ReadControls1(); case 0xDC: return ReadControls1();
case 0xC1: case 0xC1:
@ -169,42 +167,13 @@ namespace BizHawk.Emulation.Consoles.Sega
case 0x7F: PSG.WritePsgData(value, Cpu.TotalExecutedCycles); break; case 0x7F: PSG.WritePsgData(value, Cpu.TotalExecutedCycles); break;
case 0xBE: Vdp.WriteVdpData(value); break; case 0xBE: Vdp.WriteVdpData(value); break;
case 0xBD: case 0xBD:
case 0xBF: Vdp.WriteVdpRegister(value); break; case 0xBF: Vdp.WriteVdpControl(value); break;
case 0xF0: if (HasYM2413) YM2413.RegisterLatch = value; break; case 0xF0: if (HasYM2413) YM2413.RegisterLatch = value; break;
case 0xF1: if (HasYM2413) YM2413.Write(value); break; case 0xF1: if (HasYM2413) YM2413.Write(value); break;
case 0xF2: if (HasYM2413) YM2413.DetectionValue = value; break; case 0xF2: if (HasYM2413) YM2413.DetectionValue = value; break;
} }
} }
private int lineIntLinesRemaining;
private void ProcessFrameInterrupt()
{
if (Vdp.ScanLine == Vdp.BufferHeight+1)
Vdp.StatusByte |= 0x80;
if ((Vdp.StatusByte & 0x80) != 0 && Vdp.EnableFrameInterrupts)
Cpu.Interrupt = true;
}
private void ProcessLineInterrupt()
{
if (Vdp.ScanLine <= Vdp.BufferHeight)
{
if (lineIntLinesRemaining-- <= 0)
{
if (Vdp.EnableLineInterrupts)
{
Cpu.Interrupt = true;
}
lineIntLinesRemaining = Vdp.Registers[0x0A];
}
return;
}
// else we're outside the active display period
lineIntLinesRemaining = Vdp.Registers[0x0A];
}
public void FrameAdvance(bool render) public void FrameAdvance(bool render)
{ {
Controller.FrameNumber = Frame++; Controller.FrameNumber = Frame++;
@ -213,18 +182,7 @@ namespace BizHawk.Emulation.Consoles.Sega
if (IsGameGear == false) if (IsGameGear == false)
Cpu.NonMaskableInterrupt = Controller["Pause"]; Cpu.NonMaskableInterrupt = Controller["Pause"];
for (Vdp.ScanLine = 0; Vdp.ScanLine < scanlinesPerFrame; Vdp.ScanLine++) Vdp.ExecFrame(render);
{
ProcessFrameInterrupt();
ProcessLineInterrupt();
Vdp.RenderCurrentScanline(render);
Cpu.ExecuteCycles(IPeriod);
if (Vdp.ScanLine == scanlinesPerFrame-1)
Vdp.RenderBlankingRegions();
}
PSG.EndFrame(Cpu.TotalExecutedCycles); PSG.EndFrame(Cpu.TotalExecutedCycles);
} }

View File

@ -0,0 +1,251 @@
using System;
// Contains rendering functions for TMS9918 Mode 4.
namespace BizHawk.Emulation.Consoles.Sega
{
public partial class VDP
{
internal void RenderBackgroundCurrentLine()
{
if (DisplayOn == false)
{
for (int x = 0; x < 256; x++)
FrameBuffer[(ScanLine * 256) + x] = BackdropColor;
return;
}
// Clear the priority buffer for this scanline
Array.Clear(ScanlinePriorityBuffer, 0, 256);
int mapBase = NameTableBase;
int vertOffset = ScanLine + Registers[9];
if (FrameHeight == 192)
{
if (vertOffset >= 224)
vertOffset -= 224;
}
else
{
if (vertOffset >= 256)
vertOffset -= 256;
}
byte horzOffset = (HorizScrollLock && ScanLine < 16) ? (byte)0 : Registers[8];
int yTile = vertOffset / 8;
for (int xTile = 0; xTile < 32; xTile++)
{
if (xTile == 24 && VerticalScrollLock)
{
vertOffset = ScanLine;
yTile = vertOffset / 8;
}
byte PaletteBase = 0;
int tileInfo = VRAM[mapBase + ((yTile * 32) + xTile) * 2] | (VRAM[mapBase + (((yTile * 32) + xTile) * 2) + 1] << 8);
int tileNo = tileInfo & 0x01FF;
if ((tileInfo & 0x800) != 0)
PaletteBase = 16;
bool Priority = (tileInfo & 0x1000) != 0;
bool VFlip = (tileInfo & 0x400) != 0;
bool HFlip = (tileInfo & 0x200) != 0;
int yOfs = vertOffset & 7;
if (VFlip)
yOfs = 7 - yOfs;
if (HFlip == false)
{
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 0] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 1] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 2] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 3] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 4] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 5] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 6] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 7] + PaletteBase];
if (Priority)
{
horzOffset -= 8;
for (int k = 0; k < 8; k++)
{
if (PatternBuffer[(tileNo * 64) + (yOfs * 8) + k] != 0)
ScanlinePriorityBuffer[horzOffset] = 1;
horzOffset++;
}
}
}
else // Flipped Horizontally
{
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 7] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 6] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 5] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 4] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 3] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 2] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 1] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 0] + PaletteBase];
if (Priority)
{
horzOffset -= 8;
for (int k = 7; k >= 0; k--)
{
if (PatternBuffer[(tileNo * 64) + (yOfs * 8) + k] != 0)
ScanlinePriorityBuffer[horzOffset] = 1;
horzOffset++;
}
}
}
}
}
internal void RenderSpritesCurrentLine()
{
if (DisplayOn == false) return;
int SpriteBase = SpriteAttributeTableBase;
int SpriteHeight = Enable8x16Sprites ? 16 : 8;
// Clear the sprite collision buffer for this scanline
Array.Clear(SpriteCollisionBuffer, 0, 256);
// 208 is a special terminator sprite (in 192-line mode). Lets find it...
int TerminalSprite = 64;
if (FrameHeight == 192)
for (int i = 0; i < 64; i++)
{
if (VRAM[SpriteBase + i] == 208)
{
TerminalSprite = i;
break;
}
}
// Loop through these sprites and render the current scanline
int SpritesDrawnThisScanline = 0;
for (int i = TerminalSprite - 1; i >= 0; i--)
{
if (SpritesDrawnThisScanline >= 8)
StatusByte |= 0x40; // Set Overflow bit
int x = VRAM[SpriteBase + 0x80 + (i * 2)];
if (ShiftSpritesLeft8Pixels)
x -= 8;
int y = VRAM[SpriteBase + i] + 1;
if (y >= (Enable8x16Sprites ? 240 : 248)) y -= 256;
if (y + SpriteHeight <= ScanLine || y > ScanLine)
continue;
int tileNo = VRAM[SpriteBase + 0x80 + (i * 2) + 1];
if (Enable8x16Sprites)
tileNo &= 0xFE;
tileNo += SpriteTileBase;
int ys = ScanLine - y;
for (int xs = 0; xs < 8 && x + xs < 256; xs++)
{
byte color = PatternBuffer[(tileNo * 64) + (ys * 8) + xs];
if (color != 0 && x + xs >= 0 && ScanlinePriorityBuffer[x + xs] == 0)
{
FrameBuffer[(ys + y) * 256 + x + xs] = Palette[(color + 16)];
if (SpriteCollisionBuffer[x + xs] != 0)
StatusByte |= 0x20; // Set Collision bit
SpriteCollisionBuffer[x + xs] = 1;
}
}
SpritesDrawnThisScanline++;
}
}
internal void RenderSpritesCurrentLineDoubleSize()
{
if (DisplayOn == false) return;
int SpriteBase = SpriteAttributeTableBase;
int SpriteHeight = Enable8x16Sprites ? 16 : 8;
// Clear the sprite collision buffer for this scanline
Array.Clear(SpriteCollisionBuffer, 0, 256);
// 208 is a special terminator sprite (in 192-line mode). Lets find it...
int TerminalSprite = 64;
if (FrameHeight == 192)
for (int i = 0; i < 64; i++)
{
if (VRAM[SpriteBase + i] == 208)
{
TerminalSprite = i;
break;
}
}
// Loop through these sprites and render the current scanline
int SpritesDrawnThisScanline = 0;
for (int i = TerminalSprite - 1; i >= 0; i--)
{
if (SpritesDrawnThisScanline >= 8)
StatusByte |= 0x40; // Set Overflow bit
int x = VRAM[SpriteBase + 0x80 + (i * 2)];
if (ShiftSpritesLeft8Pixels)
x -= 8;
int y = VRAM[SpriteBase + i] + 1;
if (y >= (Enable8x16Sprites ? 240 : 248)) y -= 256;
if (y + (SpriteHeight*2) <= ScanLine || y > ScanLine)
continue;
int tileNo = VRAM[SpriteBase + 0x80 + (i * 2) + 1];
if (Enable8x16Sprites)
tileNo &= 0xFE;
tileNo += SpriteTileBase;
int ys = ScanLine - y;
for (int xs = 0; xs < 16 && x + xs < 256; xs++)
{
byte color = PatternBuffer[(tileNo * 64) + ((ys/2) * 8) + (xs/2)];
if (color != 0 && x + xs >= 0 && ScanlinePriorityBuffer[x + xs] == 0)
{
FrameBuffer[(ys + y) * 256 + x + xs] = Palette[(color + 16)];
if (SpriteCollisionBuffer[x + xs] != 0)
StatusByte |= 0x20; // Set Collision bit
SpriteCollisionBuffer[x + xs] = 1;
}
}
SpritesDrawnThisScanline++;
}
}
/// <summary>
/// Performs render buffer blanking. This includes the left-column blanking as well as Game Gear blanking if requested.
/// Should be called at the end of the frame.
/// </summary>
internal void RenderBlankingRegions()
{
int blankingColor = Palette[BackdropColor];
if (LeftBlanking)
{
for (int y = 0; y < FrameHeight; y++)
{
for (int x = 0; x < 8; x++)
FrameBuffer[(y * 256) + x] = blankingColor;
}
}
if (mode == VdpMode.GameGear)
{
for (int y = 0; y < 144; y++)
for (int x = 0; x < 160; x++)
GameGearFrameBuffer[(y * 160) + x] = FrameBuffer[((y + 24) * 256) + x + 48];
}
}
}
}

View File

@ -1,25 +1,14 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using BizHawk.Emulation.CPUs.Z80;
namespace BizHawk.Emulation.Consoles.Sega namespace BizHawk.Emulation.Consoles.Sega
{ {
public enum VdpCommand public enum VdpMode { SMS, GameGear }
{
VramRead,
VramWrite,
RegisterWrite,
CramWrite
}
public enum VdpMode
{
SMS,
GameGear
}
/// <summary> /// <summary>
/// Emulates the Texas Instruments TMS9918(A) VDP. /// Emulates the Texas Instruments TMS9918 VDP.
/// </summary> /// </summary>
public sealed partial class VDP : IVideoProvider public sealed partial class VDP : IVideoProvider
{ {
@ -29,80 +18,79 @@ namespace BizHawk.Emulation.Consoles.Sega
public byte[] Registers = new byte[] { 0x06, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xF0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 }; public byte[] Registers = new byte[] { 0x06, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xF0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 };
public byte StatusByte; public byte StatusByte;
private bool vdpWaitingForLatchByte = true; private bool VdpWaitingForLatchByte = true;
private byte vdpLatch; private byte VdpLatch;
private byte vdpBuffer; private byte VdpBuffer;
private ushort vdpAddress; private ushort VdpAddress;
private VdpCommand vdpCommand; private VdpCommand vdpCommand;
private ushort vdpAddressClamp;
private bool VIntPending;
private bool HIntPending;
private VdpMode mode; private VdpMode mode;
public VdpMode VdpMode { get { return mode; } } public VdpMode VdpMode { get { return mode; } }
private DisplayType DisplayType = DisplayType.NTSC;
private Z80A Cpu;
public int ScanLine; public int ScanLine;
private int FrameHeight = 192; private int FrameHeight = 192;
public int[] FrameBuffer = new int[256*192]; public int[] FrameBuffer = new int[256*192];
public int[] GameGearFrameBuffer = new int[160*144]; public int[] GameGearFrameBuffer = new int[160*144];
private DisplayType DisplayType = DisplayType.NTSC; public bool Mode2Bit { get { return (Registers[0] & 2) > 0; } }
public bool Mode4Bit { get { return (Registers[0] & 4) > 0; } }
// preprocessed state assist stuff.
public int[] Palette = new int[32];
private static readonly byte[] SMSPalXlatTable = { 0, 85, 170, 255 };
private static readonly byte[] GGPalXlatTable = { 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255 };
public bool ShiftSpritesLeft8Pixels { get { return (Registers[0] & 8) > 0; } } public bool ShiftSpritesLeft8Pixels { get { return (Registers[0] & 8) > 0; } }
public bool EnableLineInterrupts { get { return (Registers[0] & 16) > 0; } } public bool EnableLineInterrupts { get { return (Registers[0] & 16) > 0; } }
public bool LeftBlanking { get { return (Registers[0] & 32) > 0; } } public bool LeftBlanking { get { return (Registers[0] & 32) > 0; } }
public bool HorizScrollLock { get { return (Registers[0] & 64) > 0; } } public bool HorizScrollLock { get { return (Registers[0] & 64) > 0; } }
public bool VerticalScrollLock { get { return (Registers[0] & 128) > 0; } } public bool VerticalScrollLock { get { return (Registers[0] & 128) > 0; } }
public bool DisplayOn { get { return (Registers[1] & 64) > 0; } } public bool EnableDoubledSprites { get { return (Registers[1] & 1) > 0; } }
public bool EnableFrameInterrupts { get { return (Registers[1] & 32) > 0; } }
public bool Enable8x16Sprites { get { return (Registers[1] & 2) > 0; } } public bool Enable8x16Sprites { get { return (Registers[1] & 2) > 0; } }
public byte BackdropColor { get { return (byte) (16 + (Registers[7] & 15)); } } public bool EnableFrameInterrupts { get { return (Registers[1] & 32) > 0; } }
public bool DisplayOn { get { return (Registers[1] & 64) > 0; } }
public int SpriteAttributeTableBase { get { return ((Registers[5] >> 1) << 8) & 0x3FFF; } } public int SpriteAttributeTableBase { get { return ((Registers[5] >> 1) << 8) & 0x3FFF; } }
public int SpriteTileBase { get { return (Registers[6] & 4) > 0 ? 256: 0; } } public int SpriteTileBase { get { return (Registers[6] & 4) > 0 ? 256: 0; } }
public byte BackdropColor { get { return (byte)(16 + (Registers[7] & 15)); } }
public int NameTableBase
{
get
{
if (FrameHeight == 192)
return 1024 * (Registers[2] & 0x0E);
return (1024 * (Registers[2] & 0x0C)) + 0x0700;
}
}
private int NameTableBase;
// preprocessed state assist stuff.
public int[] Palette = new int[32];
public byte[] PatternBuffer = new byte[0x8000]; public byte[] PatternBuffer = new byte[0x8000];
private byte[] ScanlinePriorityBuffer = new byte[256]; private byte[] ScanlinePriorityBuffer = new byte[256];
private byte[] SpriteCollisionBuffer = new byte[256]; private byte[] SpriteCollisionBuffer = new byte[256];
public VDP(VdpMode mode, DisplayType displayType) private static readonly byte[] SMSPalXlatTable = { 0, 85, 170, 255 };
private static readonly byte[] GGPalXlatTable = { 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255 };
public VDP(Z80A cpu, VdpMode mode, DisplayType displayType)
{ {
Cpu = cpu;
this.mode = mode; this.mode = mode;
if (mode == VdpMode.SMS) CRAM = new byte[32]; if (mode == VdpMode.SMS) CRAM = new byte[32];
if (mode == VdpMode.GameGear) CRAM = new byte[64]; if (mode == VdpMode.GameGear) CRAM = new byte[64];
DisplayType = displayType; DisplayType = displayType;
NameTableBase = CalcNameTableBase();
} }
public byte ReadVram() public byte ReadData()
{ {
vdpWaitingForLatchByte = true; VdpWaitingForLatchByte = true;
byte value = vdpBuffer; byte value = VdpBuffer;
vdpBuffer = VRAM[vdpAddress & vdpAddressClamp]; VdpBuffer = VRAM[VdpAddress & 0x3FFF];
vdpAddress++; VdpAddress++;
return value; return value;
} }
public byte ReadVdpStatus() public byte ReadVdpStatus()
{ {
vdpWaitingForLatchByte = true; VdpWaitingForLatchByte = true;
byte returnValue = StatusByte; byte returnValue = StatusByte;
StatusByte &= 0x1F; StatusByte &= 0x1F;
HIntPending = false;
VIntPending = false;
Cpu.Interrupt = false;
return returnValue; return returnValue;
} }
@ -120,63 +108,57 @@ namespace BizHawk.Emulation.Consoles.Sega
} }
} }
public void WriteVdpRegister(byte value) public void WriteVdpControl(byte value)
{ {
if (vdpWaitingForLatchByte) if (VdpWaitingForLatchByte)
{ {
vdpLatch = value; VdpLatch = value;
vdpWaitingForLatchByte = false; VdpWaitingForLatchByte = false;
vdpAddress = (ushort)((vdpAddress & 0xFF00) | value); VdpAddress = (ushort)((VdpAddress & 0xFF00) | value);
return; return;
} }
vdpWaitingForLatchByte = true; VdpWaitingForLatchByte = true;
VdpAddress = (ushort)(((value & 63) << 8) | VdpLatch);
switch (value & 0xC0) switch (value & 0xC0)
{ {
case 0x00: // read VRAM case 0x00: // read VRAM
vdpCommand = VdpCommand.VramRead; vdpCommand = VdpCommand.VramRead;
vdpAddressClamp = 0x3FFF; VdpBuffer = VRAM[VdpAddress & 0x3FFF];
vdpAddress = (ushort)(((value & 63) << 8) | vdpLatch); VdpAddress++;
vdpBuffer = VRAM[vdpAddress & vdpAddressClamp];
vdpAddress++;
break; break;
case 0x40: // write VRAM case 0x40: // write VRAM
vdpCommand = VdpCommand.VramWrite; vdpCommand = VdpCommand.VramWrite;
vdpAddressClamp = 0x3FFF;
vdpAddress = (ushort)(((value & 63) << 8) | vdpLatch);
break; break;
case 0x80: // VDP register write case 0x80: // VDP register write
vdpCommand = VdpCommand.RegisterWrite;
int reg = value & 0x0F; int reg = value & 0x0F;
Registers[reg] = vdpLatch; WriteRegister(reg, VdpLatch);
if (reg == 1 || reg == 2)
CheckVideoMode();
break; break;
case 0xC0: // write CRAM / modify palette case 0xC0: // write CRAM / modify palette
vdpCommand = VdpCommand.CramWrite; vdpCommand = VdpCommand.CramWrite;
vdpAddressClamp = (byte) (mode == VdpMode.SMS ? 0x1F : 0x3F);
vdpAddress = (ushort)(((value & 63) << 8) | vdpLatch);
break; break;
} }
} }
public void WriteVdpData(byte value) public void WriteVdpData(byte value)
{ {
vdpWaitingForLatchByte = true; VdpWaitingForLatchByte = true;
vdpBuffer = value; VdpBuffer = value;
if (vdpCommand == VdpCommand.CramWrite) if (vdpCommand == VdpCommand.CramWrite)
{ {
// Write Palette / CRAM // Write Palette / CRAM
CRAM[vdpAddress & vdpAddressClamp] = value; int mask = VdpMode == VdpMode.SMS ? 0x1F : 0x3F;
vdpAddress++; CRAM[VdpAddress & mask] = value;
UpdatePrecomputedPalette(); UpdatePrecomputedPalette();
} }
else else
{ {
// Write VRAM and update pre-computed pattern buffer. // Write VRAM and update pre-computed pattern buffer.
UpdatePatternBuffer((ushort)(vdpAddress & vdpAddressClamp), value); UpdatePatternBuffer((ushort)(VdpAddress & 0x3FFF), value);
VRAM[vdpAddress & vdpAddressClamp] = value; VRAM[VdpAddress & 0x3FFF] = value;
vdpAddress++;
} }
VdpAddress++;
} }
public void UpdatePrecomputedPalette() public void UpdatePrecomputedPalette()
@ -191,8 +173,7 @@ namespace BizHawk.Emulation.Consoles.Sega
byte b = SMSPalXlatTable[(value & 0x30) >> 4]; byte b = SMSPalXlatTable[(value & 0x30) >> 4];
Palette[i] = Colors.ARGB(r, g, b); Palette[i] = Colors.ARGB(r, g, b);
} }
} else // GameGear } else { // GameGear
{
for (int i=0; i<32; i++) for (int i=0; i<32; i++)
{ {
ushort value = (ushort) ((CRAM[(i*2) + 1] << 8) | CRAM[(i*2) + 0]); ushort value = (ushort) ((CRAM[(i*2) + 1] << 8) | CRAM[(i*2) + 0]);
@ -204,9 +185,16 @@ namespace BizHawk.Emulation.Consoles.Sega
} }
} }
public int CalcNameTableBase()
{
if (FrameHeight == 192)
return 1024 * (Registers[2] & 0x0E);
return (1024 * (Registers[2] & 0x0C)) + 0x0700;
}
private void CheckVideoMode() private void CheckVideoMode()
{ {
if ((Registers[0] & 6) == 6) // if Mode4 and Mode2 set, then check extension modes if (Mode4Bit && Mode2Bit) // if Mode4 and Mode2 set, then check extension modes
{ {
switch (Registers[1] & 0x18) switch (Registers[1] & 0x18)
{ {
@ -214,34 +202,61 @@ namespace BizHawk.Emulation.Consoles.Sega
case 0x18: // 192-line mode case 0x18: // 192-line mode
if (FrameHeight != 192) if (FrameHeight != 192)
{ {
Console.WriteLine("Change video mode to 192-line Mode4");
FrameHeight = 192; FrameHeight = 192;
FrameBuffer = new int[256*192]; FrameBuffer = new int[256*192];
NameTableBase = CalcNameTableBase();
} }
break; break;
case 0x10: // 224-line mode case 0x10: // 224-line mode
if (FrameHeight != 224) if (FrameHeight != 224)
{ {
Console.WriteLine("Change video mode to 224-line Mode4");
FrameHeight = 224; FrameHeight = 224;
FrameBuffer = new int[256*224]; FrameBuffer = new int[256*224];
NameTableBase = CalcNameTableBase();
} }
break; break;
case 0x08: // 240-line mode case 0x08: // 240-line mode
if (FrameHeight != 240) if (FrameHeight != 240)
{ {
Console.WriteLine("Change video mode to 240-line Mode4");
FrameHeight = 240; FrameHeight = 240;
FrameBuffer = new int[256 * 240]; FrameBuffer = new int[256 * 240];
NameTableBase = CalcNameTableBase();
} }
break; break;
} }
} else { // default to standard 192-line mode4 } else { // default to standard 192-line mode4
if (FrameHeight != 192) if (FrameHeight != 192)
{ {
Console.WriteLine("Change video mode to 192-line Mode4");
FrameHeight = 192; FrameHeight = 192;
FrameBuffer = new int[256*192]; FrameBuffer = new int[256*192];
NameTableBase = CalcNameTableBase();
} }
} }
} }
private void WriteRegister(int reg, byte data)
{
Registers[reg] = data;
switch(reg)
{
case 0: // Mode Control Register 1
CheckVideoMode();
Cpu.Interrupt = (EnableLineInterrupts && HIntPending);
break;
case 1: // Mode Control Register 2
CheckVideoMode();
Cpu.Interrupt = (EnableFrameInterrupts && VIntPending);
break;
case 2: // Name Table Base Address
NameTableBase = CalcNameTableBase();
break;
}
}
private static readonly byte[] pow2 = {1, 2, 4, 8, 16, 32, 64, 128}; private static readonly byte[] pow2 = {1, 2, 4, 8, 16, 32, 64, 128};
private void UpdatePatternBuffer(ushort address, byte value) private void UpdatePatternBuffer(ushort address, byte value)
@ -259,6 +274,53 @@ namespace BizHawk.Emulation.Consoles.Sega
} }
} }
private int lineIntLinesRemaining;
private void ProcessFrameInterrupt()
{
if (ScanLine == FrameHeight + 1)
{
StatusByte |= 0x80;
VIntPending = true;
}
if (VIntPending && EnableFrameInterrupts)
Cpu.Interrupt = true;
}
private void ProcessLineInterrupt()
{
if (ScanLine <= FrameHeight)
{
if (lineIntLinesRemaining-- <= 0)
{
HIntPending = true;
if (EnableLineInterrupts)
Cpu.Interrupt = true;
lineIntLinesRemaining = Registers[0x0A];
}
return;
}
// else we're outside the active display period
lineIntLinesRemaining = Registers[0x0A];
}
public void ExecFrame(bool render)
{
int scanlinesPerFrame = DisplayType == DisplayType.NTSC ? 262 : 313;
for (ScanLine = 0; ScanLine < scanlinesPerFrame; ScanLine++)
{
RenderCurrentScanline(render);
ProcessFrameInterrupt();
ProcessLineInterrupt();
Cpu.ExecuteCycles(228);
if (ScanLine == scanlinesPerFrame - 1)
RenderBlankingRegions();
}
}
internal void RenderCurrentScanline(bool render) internal void RenderCurrentScanline(bool render)
{ {
if (ScanLine >= FrameHeight) if (ScanLine >= FrameHeight)
@ -266,202 +328,21 @@ namespace BizHawk.Emulation.Consoles.Sega
// TODO: make frameskip actually skip rendering // TODO: make frameskip actually skip rendering
RenderBackgroundCurrentLine(); RenderBackgroundCurrentLine();
RenderSpritesCurrentLine();
}
internal void RenderBackgroundCurrentLine()
{
if (DisplayOn == false)
{
for (int x = 0; x < 256; x++)
FrameBuffer[(ScanLine*256) + x] = BackdropColor;
return;
}
// Clear the priority buffer for this scanline
for (int p = 0; p < 256; p++)
ScanlinePriorityBuffer[p] = 0;
int mapBase = NameTableBase;
int vertOffset = ScanLine + Registers[9];
if (FrameHeight == 192)
{
if (vertOffset >= 224)
vertOffset -= 224;
} else
{
if (vertOffset >= 256)
vertOffset -= 256;
}
byte horzOffset = (HorizScrollLock && ScanLine < 16) ? (byte) 0 : Registers[8];
int yTile = vertOffset/8;
for (int xTile = 0; xTile<32; xTile++)
{
if (xTile == 24 && VerticalScrollLock)
{
vertOffset = ScanLine;
yTile = vertOffset/8;
}
byte PaletteBase = 0;
int tileInfo = VRAM[mapBase+((yTile*32) + xTile)*2] | (VRAM[mapBase+(((yTile*32) + xTile)*2) + 1]<<8);
int tileNo = tileInfo & 0x01FF;
if ((tileInfo & 0x800) != 0)
PaletteBase = 16;
bool Priority = (tileInfo & 0x1000) != 0;
bool VFlip = (tileInfo & 0x400) != 0;
bool HFlip = (tileInfo & 0x200) != 0;
int yOfs = vertOffset & 7;
if (VFlip)
yOfs = 7 - yOfs;
if (HFlip == false)
{
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 0] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 1] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 2] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 3] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 4] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 5] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 6] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 7] + PaletteBase];
if (Priority)
{
horzOffset -= 8;
for (int k = 0; k < 8; k++)
{
if (PatternBuffer[(tileNo * 64) + (yOfs * 8) + k] != 0)
ScanlinePriorityBuffer[horzOffset] = 1;
horzOffset++;
}
}
}
else // Flipped Horizontally
{
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 7] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 6] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 5] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 4] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 3] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 2] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 1] + PaletteBase];
FrameBuffer[(ScanLine * 256) + horzOffset++] = Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 0] + PaletteBase];
if (Priority)
{
horzOffset -= 8;
for (int k = 7; k >= 0; k--)
{
if (PatternBuffer[(tileNo * 64) + (yOfs * 8) + k] != 0)
ScanlinePriorityBuffer[horzOffset] = 1;
horzOffset++;
}
}
}
}
}
internal void RenderSpritesCurrentLine()
{
if (DisplayOn == false) return;
int SpriteBase = SpriteAttributeTableBase;
int SpriteHeight = Enable8x16Sprites ? 16 : 8;
// Clear the sprite collision buffer for this scanline
for (int c = 0; c < 256; c++)
SpriteCollisionBuffer[c] = 0;
// 208 is a special terminator sprite (in 192-line mode). Lets find it...
int TerminalSprite = 64;
if (FrameHeight == 192)
for (int i = 0; i < 64; i++)
{
if (VRAM[SpriteBase + i] == 208)
{
TerminalSprite = i;
break;
}
}
// Loop through these sprites and render the current scanline if (EnableDoubledSprites)
int SpritesDrawnThisScanline = 0; RenderSpritesCurrentLineDoubleSize();
for (int i = TerminalSprite - 1; i >= 0; i--) else
{ RenderSpritesCurrentLine();
if (SpritesDrawnThisScanline >= 8)
StatusByte |= 0x40; // Set Overflow bit
int x = VRAM[SpriteBase + 0x80 + (i*2)];
if (ShiftSpritesLeft8Pixels)
x -= 8;
int y = VRAM[SpriteBase + i] + 1;
if (y >= (Enable8x16Sprites ? 240 : 248)) y -= 256;
if (y+SpriteHeight<=ScanLine || y > ScanLine)
continue;
int tileNo = VRAM[SpriteBase + 0x80 + (i*2) + 1];
if (Enable8x16Sprites)
tileNo &= 0xFE;
tileNo += SpriteTileBase;
int ys = ScanLine - y;
for (int xs = 0; xs<8 && x+xs < 256; xs++)
{
byte color = PatternBuffer[(tileNo*64) + (ys*8) + xs];
if (color != 0 && x+xs >= 0 && ScanlinePriorityBuffer[x + xs] == 0)
{
FrameBuffer[(ys + y)*256 + x + xs] = Palette[(color + 16)];
if (SpriteCollisionBuffer[x + xs] != 0)
StatusByte |= 0x20; // Set Collision bit
SpriteCollisionBuffer[x + xs] = 1;
}
}
SpritesDrawnThisScanline++;
}
}
/// <summary>
/// Performs render buffer blanking. This includes the left-column blanking as well as Game Gear blanking if requested.
/// Should be called at the end of the frame.
/// </summary>
public void RenderBlankingRegions()
{
int blankingColor = Palette[BackdropColor];
if (LeftBlanking)
{
for (int y=0; y<FrameHeight; y++)
{
for (int x=0; x<8; x++)
FrameBuffer[(y*256) + x] = blankingColor;
}
}
if (mode == VdpMode.GameGear)
{
for (int y = 0; y < 144; y++)
for (int x = 0; x < 160; x++)
GameGearFrameBuffer[(y*160) + x] = FrameBuffer[((y + 24)*256) + x + 48];
}
} }
public void SaveStateText(TextWriter writer) public void SaveStateText(TextWriter writer)
{ {
writer.WriteLine("[VDP]"); writer.WriteLine("[VDP]");
writer.WriteLine("Mode " + Enum.GetName(typeof(VdpMode), VdpMode));
writer.WriteLine("StatusByte {0:X2}", StatusByte); writer.WriteLine("StatusByte {0:X2}", StatusByte);
writer.WriteLine("WaitingForLatchByte {0}", vdpWaitingForLatchByte); writer.WriteLine("WaitingForLatchByte {0}", VdpWaitingForLatchByte);
writer.WriteLine("Latch {0:X2}", vdpLatch); writer.WriteLine("Latch {0:X2}", VdpLatch);
writer.WriteLine("ReadBuffer {0:X2}", vdpBuffer); writer.WriteLine("ReadBuffer {0:X2}", VdpBuffer);
writer.WriteLine("VdpAddress {0:X4}", vdpAddress); writer.WriteLine("VdpAddress {0:X4}", VdpAddress);
writer.WriteLine("VdpAddressMask {0:X2}", vdpAddressClamp);
writer.WriteLine("Command " + Enum.GetName(typeof(VdpCommand), vdpCommand)); writer.WriteLine("Command " + Enum.GetName(typeof(VdpCommand), vdpCommand));
writer.Write("Registers "); writer.Write("Registers ");
@ -485,15 +366,13 @@ namespace BizHawk.Emulation.Consoles.Sega
if (args[0] == "StatusByte") if (args[0] == "StatusByte")
StatusByte = byte.Parse(args[1], NumberStyles.HexNumber); StatusByte = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "WaitingForLatchByte") else if (args[0] == "WaitingForLatchByte")
vdpWaitingForLatchByte = bool.Parse(args[1]); VdpWaitingForLatchByte = bool.Parse(args[1]);
else if (args[0] == "Latch") else if (args[0] == "Latch")
vdpLatch = byte.Parse(args[1], NumberStyles.HexNumber); VdpLatch = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "ReadBuffer") else if (args[0] == "ReadBuffer")
vdpBuffer = byte.Parse(args[1], NumberStyles.HexNumber); VdpBuffer = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "VdpAddress") else if (args[0] == "VdpAddress")
vdpAddress = ushort.Parse(args[1], NumberStyles.HexNumber); VdpAddress = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "VdpAddressMask")
vdpAddressClamp = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "Command") else if (args[0] == "Command")
vdpCommand = (VdpCommand) Enum.Parse(typeof (VdpCommand), args[1]); vdpCommand = (VdpCommand) Enum.Parse(typeof (VdpCommand), args[1]);
else if (args[0] == "Registers") else if (args[0] == "Registers")
@ -513,17 +392,17 @@ namespace BizHawk.Emulation.Consoles.Sega
else else
Console.WriteLine("Skipping unrecognized identifier "+args[0]); Console.WriteLine("Skipping unrecognized identifier "+args[0]);
} }
CheckVideoMode(); for (int i=0; i<Registers.Length; i++)
WriteRegister(i, Registers[i]);
} }
public void SaveStateBinary(BinaryWriter writer) public void SaveStateBinary(BinaryWriter writer)
{ {
writer.Write(StatusByte); writer.Write(StatusByte);
writer.Write(vdpWaitingForLatchByte); writer.Write(VdpWaitingForLatchByte);
writer.Write(vdpLatch); writer.Write(VdpLatch);
writer.Write(vdpBuffer); writer.Write(VdpBuffer);
writer.Write(vdpAddress); writer.Write(VdpAddress);
writer.Write(vdpAddressClamp);
writer.Write((byte)vdpCommand); writer.Write((byte)vdpCommand);
writer.Write(Registers); writer.Write(Registers);
writer.Write(CRAM); writer.Write(CRAM);
@ -533,11 +412,10 @@ namespace BizHawk.Emulation.Consoles.Sega
public void LoadStateBinary(BinaryReader reader) public void LoadStateBinary(BinaryReader reader)
{ {
StatusByte = reader.ReadByte(); StatusByte = reader.ReadByte();
vdpWaitingForLatchByte = reader.ReadBoolean(); VdpWaitingForLatchByte = reader.ReadBoolean();
vdpLatch = reader.ReadByte(); VdpLatch = reader.ReadByte();
vdpBuffer = reader.ReadByte(); VdpBuffer = reader.ReadByte();
vdpAddress = reader.ReadUInt16(); VdpAddress = reader.ReadUInt16();
vdpAddressClamp = reader.ReadUInt16();
vdpCommand = (VdpCommand) Enum.ToObject(typeof(VdpCommand), reader.ReadByte()); vdpCommand = (VdpCommand) Enum.ToObject(typeof(VdpCommand), reader.ReadByte());
Registers = reader.ReadBytes(Registers.Length); Registers = reader.ReadBytes(Registers.Length);
CRAM = reader.ReadBytes(CRAM.Length); CRAM = reader.ReadBytes(CRAM.Length);
@ -545,7 +423,8 @@ namespace BizHawk.Emulation.Consoles.Sega
UpdatePrecomputedPalette(); UpdatePrecomputedPalette();
for (ushort i = 0; i < VRAM.Length; i++) for (ushort i = 0; i < VRAM.Length; i++)
UpdatePatternBuffer(i, VRAM[i]); UpdatePatternBuffer(i, VRAM[i]);
CheckVideoMode(); for (int i = 0; i < Registers.Length; i++)
WriteRegister(i, Registers[i]);
} }
public int[] GetVideoBuffer() public int[] GetVideoBuffer()
@ -567,5 +446,13 @@ namespace BizHawk.Emulation.Consoles.Sega
{ {
get { return Palette[BackdropColor]; } get { return Palette[BackdropColor]; }
} }
enum VdpCommand
{
VramRead,
VramWrite,
RegisterWrite,
CramWrite
}
} }
} }

View File

@ -46,7 +46,7 @@ namespace BizHawk
Game.System = items[3]; Game.System = items[3];
Game.MetaData = items.Length >= 6 ? items[5] : null; Game.MetaData = items.Length >= 6 ? items[5] : null;
db[Game.CRC32] = Game; db[Game.CRC32] = Game;
} catch (Exception e) } catch (Exception)
{ {
Console.WriteLine("Error parsing database entry: "+line); Console.WriteLine("Error parsing database entry: "+line);
} }

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -6,7 +6,7 @@
<ProductVersion>9.0.21022</ProductVersion> <ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion> <SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{DD448B37-BA3F-4544-9754-5406E8094723}</ProjectGuid> <ProjectGuid>{DD448B37-BA3F-4544-9754-5406E8094723}</ProjectGuid>
<OutputType>Exe</OutputType> <OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>BizHawk.MultiClient</RootNamespace> <RootNamespace>BizHawk.MultiClient</RootNamespace>
<AssemblyName>BizHawk.MultiClient</AssemblyName> <AssemblyName>BizHawk.MultiClient</AssemblyName>
@ -121,7 +121,6 @@
<Name>BizHawk.Util</Name> <Name>BizHawk.Util</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -116,7 +116,6 @@
<ItemGroup> <ItemGroup>
<Content Include="7z\arch\Test.txt" /> <Content Include="7z\arch\Test.txt" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.