PAL support for SMS

This commit is contained in:
beirich 2011-01-13 06:10:50 +00:00
parent aa784295d8
commit 3c8236fd32
8 changed files with 94 additions and 51 deletions

View File

@ -1,6 +1,4 @@
using System; namespace BizHawk.Emulation.CPUs.Z80
namespace BizHawk.Emulation.CPUs.Z80
{ {
public partial class Z80A public partial class Z80A
{ {

View File

@ -36,7 +36,6 @@ namespace BizHawk.Emulation.CPUs.Z80
totalExecutedCycles += 4; pendingCycles -= 4; totalExecutedCycles += 4; pendingCycles -= 4;
} else { } else {
++RegR; ++RegR;
//System.Console.WriteLine(State());
switch (ReadMemory(RegPC.Word++)) switch (ReadMemory(RegPC.Word++))
{ {

View File

@ -1,9 +1,6 @@
======= Sega MasterSystem Compatibility Issues ======= ======= Sega MasterSystem Compatibility Issues =======
* CodeMasters games use a custom mapper and special video modes (both implemented) * CodeMasters games use a custom mapper and special video modes (both implemented)
+ Cosmic Spacehead and Micro Machines are playable but suffer from a graphic glitch on the
top part of the frame. In my defense, Kega, Cogwheel, and other emulators do the precise
same thing.
+ 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, and doesn't work on a GG/Genesis either.

View File

@ -10,19 +10,18 @@ using BizHawk.Emulation.Sound;
VDP: VDP:
+ Double Size Sprites + Double Size Sprites
+ HCounter + HCounter
+ Possibly work on VDP tests, but they don't really affect games. + Old TMS video modes (SG-1000)
- Other video modes / CodeMasters 224 lines / old SG-1000 video modes.
GENERAL: GENERAL:
+ Debug some GameGear game issues. + 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
**********************************************************/ **********************************************************/
namespace BizHawk.Emulation.Consoles.Sega namespace BizHawk.Emulation.Consoles.Sega
{ {
public enum DisplayType { NTSC, PAL }
public sealed partial class SMS : IEmulator public sealed partial class SMS : IEmulator
{ {
@ -59,7 +58,6 @@ namespace BizHawk.Emulation.Consoles.Sega
private byte Port02 = 0xFF; private byte Port02 = 0xFF;
private byte Port3F = 0xFF; private byte Port3F = 0xFF;
private int framesPerSecond;
private int scanlinesPerFrame; private int scanlinesPerFrame;
private DisplayType displayType; private DisplayType displayType;
@ -69,15 +67,7 @@ namespace BizHawk.Emulation.Consoles.Sega
set set
{ {
displayType = value; displayType = value;
if (displayType == DisplayType.NTSC) scanlinesPerFrame = displayType == DisplayType.NTSC ? 262 : 313;
{
framesPerSecond = 60;
scanlinesPerFrame = 262;
} else // PAL
{
framesPerSecond = 50;
scanlinesPerFrame = 313;
}
} }
} }
@ -85,7 +75,6 @@ namespace BizHawk.Emulation.Consoles.Sega
public void Init() public void Init()
{ {
DisplayType = DisplayType.NTSC;
if (Controller == null) if (Controller == null)
Controller = NullController.GetNullController(); Controller = NullController.GetNullController();
@ -93,21 +82,23 @@ 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); Vdp = new VDP(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);
if (HasYM2413 && Options.Contains("WhenFMDisablePSG"))
SoundMixer.DisableSource(PSG);
ActiveSoundProvider = PSG; ActiveSoundProvider = PSG;
HardReset(); HardReset();
//PSG.StereoPanning = 0xDA;
} }
public void HardReset() public void HardReset()
{ {
Cpu.Reset(); Cpu.Reset();
Cpu.RegisterSP = 0xDFF0; Cpu.RegisterSP = 0xDFF0;
Vdp = new VDP(Vdp.VdpMode); Vdp = new VDP(Vdp.VdpMode, DisplayType);
PSG.Reset(); PSG.Reset();
YM2413.Reset(); YM2413.Reset();
SystemRam = new byte[0x2000]; SystemRam = new byte[0x2000];
@ -123,26 +114,17 @@ namespace BizHawk.Emulation.Consoles.Sega
RomData = game.GetRomData(); RomData = game.GetRomData();
RomBanks = (byte)(RomData.Length/BankSize); RomBanks = (byte)(RomData.Length/BankSize);
Options = game.GetOptions(); Options = game.GetOptions();
DisplayType = DisplayType.NTSC;
foreach (string option in Options) foreach (string option in Options)
{ {
var args = option.Split('='); var args = option.Split('=');
if (option == "FM") HasYM2413 = true; if (option == "FM") HasYM2413 = true;
else if (args[0] == "IPeriod") IPeriod = int.Parse(args[1]); else if (args[0] == "IPeriod") IPeriod = int.Parse(args[1]);
else if (args[0] == "Japan") Region = "Japan"; else if (args[0] == "Japan") Region = "Japan";
else if (args[0] == "PAL") DisplayType = DisplayType.PAL;
} }
Init(); Init();
// In Outrun FM mode, when you start a race, the PSG noise channel is left on until a "wheel squeeling"
// sound effect occurs, and this happens again every "extended play" checkpoint. Every emulator I have tested
// does this; if it's a bug, it is a bug that no emulator gets right.
// In any case, it is annoying, so this turns it off.
// My view is that because I believe it to be a game bug (not an emulation bug), this doesn't count as a game-specific hack :)
if (HasYM2413 && game.Name.StartsWith("OutRun", StringComparison.InvariantCultureIgnoreCase))
SoundMixer.DisableSource(PSG);
// TODO: at some point we need a proper Game Library construct with various metadata about emulation hints and options for games and roms.
} }
public byte ReadPort(ushort port) public byte ReadPort(ushort port)

View File

@ -2,8 +2,7 @@
{ {
public partial class VDP public partial class VDP
{ {
// TODO: PAL video modes, HCounter // TODO: HCounter
private readonly byte[] VLineCounterTableNTSC192 = private readonly byte[] VLineCounterTableNTSC192 =
{ {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
@ -25,9 +24,54 @@
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
}; };
private readonly byte[] VLineCounterTableNTSC240 = private readonly byte[] VLineCounterTableNTSC240 =
{ {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05
};
private readonly byte[] VLineCounterTablePAL192 =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2,
0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};
private readonly byte[] VLineCounterTablePAL240 =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
@ -44,7 +88,10 @@
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
}; };
} }
} }

View File

@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Consoles.Sega
} }
/// <summary> /// <summary>
/// Emulates the Texas Instruments TMS9918 VDP. /// Emulates the Texas Instruments TMS9918(A) VDP.
/// </summary> /// </summary>
public sealed partial class VDP : IVideoProvider public sealed partial class VDP : IVideoProvider
{ {
@ -46,6 +46,8 @@ namespace BizHawk.Emulation.Consoles.Sega
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;
// preprocessed state assist stuff. // preprocessed state assist stuff.
public int[] Palette = new int[32]; public int[] Palette = new int[32];
@ -79,11 +81,12 @@ namespace BizHawk.Emulation.Consoles.Sega
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) public VDP(VdpMode mode, DisplayType displayType)
{ {
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;
} }
public byte ReadVram() public byte ReadVram()
@ -105,7 +108,16 @@ namespace BizHawk.Emulation.Consoles.Sega
public byte ReadVLineCounter() public byte ReadVLineCounter()
{ {
return FrameHeight == 240 ? VLineCounterTableNTSC240[ScanLine] : VLineCounterTableNTSC192[ScanLine]; if (DisplayType == DisplayType.NTSC)
{
if (FrameHeight == 240)
return VLineCounterTableNTSC240[ScanLine];
return VLineCounterTableNTSC192[ScanLine];
} else { // PAL
if (FrameHeight == 240)
return VLineCounterTablePAL240[ScanLine];
return VLineCounterTablePAL192[ScanLine];
}
} }
public void WriteVdpRegister(byte value) public void WriteVdpRegister(byte value)

View File

@ -35,15 +35,21 @@ namespace BizHawk
while (reader.EndOfStream == false) while (reader.EndOfStream == false)
{ {
string line = reader.ReadLine(); string line = reader.ReadLine();
if (line.Trim().Length == 0) continue; try
string[] items = line.Split('\t'); {
if (line.Trim().Length == 0) continue;
var Game = new GameInfo(); string[] items = line.Split('\t');
Game.CRC32 = Int32.Parse(items[0], NumberStyles.HexNumber);
Game.Name = items[2]; var Game = new GameInfo();
Game.System = items[3]; Game.CRC32 = Int32.Parse(items[0], NumberStyles.HexNumber);
Game.MetaData = items.Length >= 6 ? items[5] : null; Game.Name = items[2];
db[Game.CRC32] = Game; Game.System = items[3];
Game.MetaData = items.Length >= 6 ? items[5] : null;
db[Game.CRC32] = Game;
} catch (Exception e)
{
Console.WriteLine("Error parsing database entry: "+line);
}
} }
} }
} }

View File

@ -29,4 +29,6 @@ namespace BizHawk
void LoadStateBinary(BinaryReader reader); void LoadStateBinary(BinaryReader reader);
byte[] SaveStateBinary(); byte[] SaveStateBinary();
} }
public enum DisplayType { NTSC, PAL }
} }