2011-01-11 02:55:51 +00:00
|
|
|
|
using System;
|
2011-01-21 03:59:50 +00:00
|
|
|
|
using System.Collections.Generic;
|
2011-01-11 02:55:51 +00:00
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.IO;
|
2013-10-27 22:07:40 +00:00
|
|
|
|
|
|
|
|
|
using BizHawk.Common;
|
2013-11-04 01:06:36 +00:00
|
|
|
|
using BizHawk.Emulation.Common;
|
2013-11-14 19:33:13 +00:00
|
|
|
|
using BizHawk.Emulation.Common.Components;
|
2014-01-22 01:14:36 +00:00
|
|
|
|
using BizHawk.Emulation.Cores.Components.Z80;
|
2011-01-11 02:55:51 +00:00
|
|
|
|
|
|
|
|
|
/*****************************************************
|
2011-01-21 03:59:50 +00:00
|
|
|
|
TODO:
|
2011-01-11 02:55:51 +00:00
|
|
|
|
+ HCounter
|
|
|
|
|
+ Try to clean up the organization of the source code.
|
2011-02-16 04:45:59 +00:00
|
|
|
|
+ Lightgun/Paddle/etc if I get really bored
|
2014-03-13 04:15:05 +00:00
|
|
|
|
+ Mode 1 not implemented in VDP TMS modes. (I dont have a test case in SG1000 or Coleco)
|
2012-11-26 01:44:17 +00:00
|
|
|
|
|
2011-01-11 02:55:51 +00:00
|
|
|
|
**********************************************************/
|
|
|
|
|
|
2013-11-13 23:36:21 +00:00
|
|
|
|
namespace BizHawk.Emulation.Cores.Sega.MasterSystem
|
2011-01-11 02:55:51 +00:00
|
|
|
|
{
|
2014-04-22 00:38:59 +00:00
|
|
|
|
[CoreAttributes("SMSHawk")]
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public sealed partial class SMS : IEmulator
|
|
|
|
|
{
|
|
|
|
|
// Constants
|
|
|
|
|
public const int BankSize = 16384;
|
|
|
|
|
|
|
|
|
|
// ROM
|
|
|
|
|
public byte[] RomData;
|
SMS: Korean mappers work. Fixes Cyborg Z, Dodgeball King, F1 Spirit, Jang Pang III, Knightmare II, Nemesis, Nemesis 2, Penguin Adventure, Sangokushi 3, Street Master, Wonsiin
2014-03-14 04:30:01 +00:00
|
|
|
|
public byte RomBank0, RomBank1, RomBank2, RomBank3;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public byte RomBanks;
|
|
|
|
|
|
|
|
|
|
// SaveRAM
|
2014-03-22 04:46:01 +00:00
|
|
|
|
public byte[] SaveRAM;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public byte SaveRamBank;
|
|
|
|
|
|
2014-03-05 05:09:20 +00:00
|
|
|
|
public byte[] BiosRom;
|
|
|
|
|
|
2012-09-14 23:31:10 +00:00
|
|
|
|
public byte[] ReadSaveRam()
|
|
|
|
|
{
|
|
|
|
|
if (SaveRAM != null)
|
|
|
|
|
return (byte[])SaveRAM.Clone();
|
|
|
|
|
else
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
public void StoreSaveRam(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
if (SaveRAM != null)
|
|
|
|
|
Array.Copy(data, SaveRAM, data.Length);
|
|
|
|
|
}
|
|
|
|
|
public void ClearSaveRam()
|
|
|
|
|
{
|
|
|
|
|
if (SaveRAM != null)
|
|
|
|
|
SaveRAM = new byte[SaveRAM.Length];
|
|
|
|
|
}
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public bool SaveRamModified { get; set; }
|
|
|
|
|
|
|
|
|
|
// Machine resources
|
|
|
|
|
public Z80A Cpu;
|
|
|
|
|
public byte[] SystemRam;
|
|
|
|
|
public VDP Vdp;
|
|
|
|
|
public SN76489 PSG;
|
|
|
|
|
public YM2413 YM2413;
|
|
|
|
|
public SoundMixer SoundMixer;
|
|
|
|
|
public bool IsGameGear = false;
|
|
|
|
|
public bool HasYM2413 = false;
|
|
|
|
|
|
2014-03-22 04:46:01 +00:00
|
|
|
|
int frame = 0;
|
|
|
|
|
int lagCount = 0;
|
2011-09-05 17:45:01 +00:00
|
|
|
|
bool lagged = true;
|
2014-03-22 04:46:01 +00:00
|
|
|
|
bool isLag = false;
|
|
|
|
|
public int Frame { get { return frame; } set { frame = value; } }
|
|
|
|
|
public int LagCount { get { return lagCount; } set { lagCount = value; } }
|
|
|
|
|
public bool IsLagFrame { get { return isLag; } }
|
2011-09-05 17:45:01 +00:00
|
|
|
|
byte Port01 = 0xFF;
|
|
|
|
|
byte Port02 = 0xFF;
|
|
|
|
|
byte Port3E = 0xAF;
|
|
|
|
|
byte Port3F = 0xFF;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
|
2014-02-27 00:28:05 +00:00
|
|
|
|
byte ForceStereoByte = 0xAD;
|
2014-03-05 02:20:22 +00:00
|
|
|
|
bool IsGame3D = false;
|
2014-02-27 00:28:05 +00:00
|
|
|
|
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public DisplayType DisplayType { get; set; }
|
2012-10-03 15:31:04 +00:00
|
|
|
|
public bool DeterministicEmulation { get { return true; } }
|
2011-01-11 02:55:51 +00:00
|
|
|
|
|
2014-03-06 04:43:36 +00:00
|
|
|
|
public SMS(CoreComm comm, GameInfo game, byte[] rom, object settings, object syncSettings)
|
2011-06-11 22:15:08 +00:00
|
|
|
|
{
|
2014-03-06 04:43:36 +00:00
|
|
|
|
Settings = (SMSSettings)settings ?? new SMSSettings();
|
|
|
|
|
SyncSettings = (SMSSyncSettings)syncSettings ?? new SMSSyncSettings();
|
2012-12-10 00:43:43 +00:00
|
|
|
|
CoreComm = comm;
|
2014-03-22 04:46:01 +00:00
|
|
|
|
|
2012-12-10 00:43:43 +00:00
|
|
|
|
IsGameGear = game.System == "GG";
|
2011-08-04 03:20:54 +00:00
|
|
|
|
RomData = rom;
|
2012-12-10 00:43:43 +00:00
|
|
|
|
CoreComm.CpuTraceAvailable = true;
|
2012-10-06 18:09:41 +00:00
|
|
|
|
|
2011-08-04 03:20:54 +00:00
|
|
|
|
if (RomData.Length % BankSize != 0)
|
|
|
|
|
Array.Resize(ref RomData, ((RomData.Length / BankSize) + 1) * BankSize);
|
|
|
|
|
RomBanks = (byte)(RomData.Length / BankSize);
|
2012-07-15 07:15:31 +00:00
|
|
|
|
|
2014-03-22 05:57:27 +00:00
|
|
|
|
DisplayType = DetermineDisplayType(SyncSettings.DisplayType, game.Region);
|
2014-03-13 04:49:39 +00:00
|
|
|
|
if (game["PAL"] && DisplayType != DisplayType.PAL)
|
|
|
|
|
{
|
|
|
|
|
DisplayType = DisplayType.PAL;
|
2014-03-18 03:03:53 +00:00
|
|
|
|
CoreComm.Notify("Display was forced to PAL mode for game compatibility.");
|
2014-03-13 04:49:39 +00:00
|
|
|
|
}
|
2014-03-17 03:00:07 +00:00
|
|
|
|
if (IsGameGear)
|
|
|
|
|
DisplayType = DisplayType.NTSC; // all game gears run at 60hz/NTSC mode
|
2012-12-10 00:43:43 +00:00
|
|
|
|
CoreComm.VsyncNum = DisplayType == DisplayType.NTSC ? 60 : 50;
|
|
|
|
|
CoreComm.VsyncDen = 1;
|
2014-03-13 04:49:39 +00:00
|
|
|
|
|
2014-03-22 05:57:27 +00:00
|
|
|
|
Region = SyncSettings.ConsoleRegion;
|
|
|
|
|
if (Region == "Auto") Region = DetermineRegion(game.Region);
|
|
|
|
|
|
2014-03-13 04:49:39 +00:00
|
|
|
|
if (game["Japan"] && Region != "Japan")
|
|
|
|
|
{
|
|
|
|
|
Region = "Japan";
|
2014-03-18 03:03:53 +00:00
|
|
|
|
CoreComm.Notify("Region was forced to Japan for game compatibility.");
|
2014-03-13 04:49:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-25 02:12:42 +00:00
|
|
|
|
if ((game.NotInDatabase || game["FM"]) && SyncSettings.EnableFM && !IsGameGear)
|
2011-08-04 03:20:54 +00:00
|
|
|
|
HasYM2413 = true;
|
|
|
|
|
|
|
|
|
|
if (Controller == null)
|
|
|
|
|
Controller = NullController.GetNullController();
|
|
|
|
|
|
|
|
|
|
Cpu = new Z80A();
|
|
|
|
|
Cpu.RegisterSP = 0xDFF0;
|
|
|
|
|
Cpu.ReadHardware = ReadPort;
|
|
|
|
|
Cpu.WriteHardware = WritePort;
|
|
|
|
|
|
2012-03-11 16:15:20 +00:00
|
|
|
|
Vdp = new VDP(this, Cpu, IsGameGear ? VdpMode.GameGear : VdpMode.SMS, DisplayType);
|
2011-08-04 03:20:54 +00:00
|
|
|
|
PSG = new SN76489();
|
|
|
|
|
YM2413 = new YM2413();
|
|
|
|
|
SoundMixer = new SoundMixer(YM2413, PSG);
|
|
|
|
|
if (HasYM2413 && game["WhenFMDisablePSG"])
|
|
|
|
|
SoundMixer.DisableSource(PSG);
|
|
|
|
|
ActiveSoundProvider = HasYM2413 ? (ISoundProvider)SoundMixer : PSG;
|
|
|
|
|
|
|
|
|
|
SystemRam = new byte[0x2000];
|
|
|
|
|
|
2014-03-06 04:43:36 +00:00
|
|
|
|
if (game["CMMapper"])
|
|
|
|
|
InitCodeMastersMapper();
|
2014-03-18 02:23:10 +00:00
|
|
|
|
else if (game["CMMapperWithRam"])
|
|
|
|
|
InitCodeMastersMapperRam();
|
2014-03-06 04:43:36 +00:00
|
|
|
|
else if (game["ExtRam"])
|
|
|
|
|
InitExt2kMapper(int.Parse(game.OptionValue("ExtRam")));
|
SMS: Korean mappers work. Fixes Cyborg Z, Dodgeball King, F1 Spirit, Jang Pang III, Knightmare II, Nemesis, Nemesis 2, Penguin Adventure, Sangokushi 3, Street Master, Wonsiin
2014-03-14 04:30:01 +00:00
|
|
|
|
else if (game["KoreaMapper"])
|
|
|
|
|
InitKoreaMapper();
|
|
|
|
|
else if (game["MSXMapper"])
|
|
|
|
|
InitMSXMapper();
|
|
|
|
|
else if (game["NemesisMapper"])
|
|
|
|
|
InitNemesisMapper();
|
2014-03-17 03:00:07 +00:00
|
|
|
|
else if (game["TerebiOekaki"])
|
|
|
|
|
InitTerebiOekaki();
|
2014-03-06 04:43:36 +00:00
|
|
|
|
else
|
|
|
|
|
InitSegaMapper();
|
|
|
|
|
|
|
|
|
|
if (Settings.ForceStereoSeparation && !IsGameGear)
|
2011-08-04 03:20:54 +00:00
|
|
|
|
{
|
|
|
|
|
if (game["StereoByte"])
|
|
|
|
|
{
|
2014-02-27 00:28:05 +00:00
|
|
|
|
ForceStereoByte = byte.Parse(game.OptionValue("StereoByte"));
|
2011-08-04 03:20:54 +00:00
|
|
|
|
}
|
2014-02-27 00:28:05 +00:00
|
|
|
|
PSG.StereoPanning = ForceStereoByte;
|
2011-08-04 03:20:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-06 04:43:36 +00:00
|
|
|
|
if (SyncSettings.AllowOverlock && game["OverclockSafe"])
|
2011-08-04 03:20:54 +00:00
|
|
|
|
Vdp.IPeriod = 512;
|
|
|
|
|
|
2014-03-06 04:43:36 +00:00
|
|
|
|
if (Settings.SpriteLimit)
|
2011-09-19 00:39:28 +00:00
|
|
|
|
Vdp.SpriteLimit = true;
|
|
|
|
|
|
2014-03-05 02:20:22 +00:00
|
|
|
|
if (game["3D"])
|
|
|
|
|
IsGame3D = true;
|
|
|
|
|
|
2014-03-05 05:09:20 +00:00
|
|
|
|
if (game["BIOS"])
|
|
|
|
|
{
|
|
|
|
|
Port3E = 0xF7; // Disable cartridge, enable BIOS rom
|
|
|
|
|
InitBiosMapper();
|
|
|
|
|
}
|
2014-03-05 12:49:27 +00:00
|
|
|
|
else if (game.System == "SMS")
|
2014-03-05 05:09:20 +00:00
|
|
|
|
{
|
2014-03-13 04:49:39 +00:00
|
|
|
|
BiosRom = comm.CoreFileProvider.GetFirmware("SMS", Region, false);
|
2014-03-17 03:00:07 +00:00
|
|
|
|
if (BiosRom != null && (game["RequireBios"] || SyncSettings.UseBIOS))
|
2014-03-05 05:09:20 +00:00
|
|
|
|
Port3E = 0xF7;
|
|
|
|
|
|
2014-03-17 03:00:07 +00:00
|
|
|
|
if (BiosRom == null && game["RequireBios"])
|
2014-03-18 03:03:53 +00:00
|
|
|
|
CoreComm.Notify("BIOS image not available. This game requires BIOS to function.");
|
2014-03-13 04:49:39 +00:00
|
|
|
|
if (SyncSettings.UseBIOS && BiosRom == null)
|
2014-03-18 03:03:53 +00:00
|
|
|
|
CoreComm.Notify("BIOS was selected, but rom image not available. BIOS not enabled.");
|
2014-03-13 04:49:39 +00:00
|
|
|
|
}
|
2014-03-22 04:46:01 +00:00
|
|
|
|
|
|
|
|
|
if (game["SRAM"])
|
|
|
|
|
SaveRAM = new byte[int.Parse(game.OptionValue("SRAM"))];
|
|
|
|
|
else if (game.NotInDatabase)
|
|
|
|
|
SaveRAM = new byte[0x8000];
|
|
|
|
|
|
2014-03-13 04:49:39 +00:00
|
|
|
|
SetupMemoryDomains();
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 05:57:27 +00:00
|
|
|
|
string DetermineRegion(string gameRegion)
|
|
|
|
|
{
|
2014-03-25 02:12:42 +00:00
|
|
|
|
if (gameRegion == null)
|
|
|
|
|
return "Export";
|
2014-03-22 05:57:27 +00:00
|
|
|
|
if (gameRegion.IndexOf("USA") >= 0)
|
|
|
|
|
return "Export";
|
|
|
|
|
if (gameRegion.IndexOf("Europe") >= 0)
|
|
|
|
|
return "Export";
|
|
|
|
|
if (gameRegion.IndexOf("World") >= 0)
|
|
|
|
|
return "Export";
|
|
|
|
|
if (gameRegion.IndexOf("Brazil") >= 0)
|
|
|
|
|
return "Export";
|
|
|
|
|
if (gameRegion.IndexOf("Australia") >= 0)
|
|
|
|
|
return "Export";
|
|
|
|
|
return "Japan";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DisplayType DetermineDisplayType(string display, string region)
|
|
|
|
|
{
|
|
|
|
|
if (display == "NTSC") return DisplayType.NTSC;
|
|
|
|
|
if (display == "PAL") return DisplayType.PAL;
|
2014-03-25 02:12:42 +00:00
|
|
|
|
if (region != null && region == "Europe") return DisplayType.PAL;
|
2014-03-22 05:57:27 +00:00
|
|
|
|
return DisplayType.NTSC;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-17 03:00:07 +00:00
|
|
|
|
public void ResetCounters()
|
|
|
|
|
{
|
|
|
|
|
Frame = 0;
|
2014-03-22 04:46:01 +00:00
|
|
|
|
lagCount = 0;
|
|
|
|
|
isLag = false;
|
2014-03-17 03:00:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public byte ReadPort(ushort port)
|
|
|
|
|
{
|
2014-03-17 03:56:42 +00:00
|
|
|
|
port &= 0xFF;
|
|
|
|
|
if (port < 0x40) // General IO ports
|
|
|
|
|
{
|
|
|
|
|
switch (port)
|
|
|
|
|
{
|
|
|
|
|
case 0x00: return ReadPort0();
|
|
|
|
|
case 0x01: return Port01;
|
|
|
|
|
case 0x02: return Port02;
|
|
|
|
|
case 0x03: return 0x00;
|
|
|
|
|
case 0x04: return 0xFF;
|
|
|
|
|
case 0x05: return 0x00;
|
|
|
|
|
case 0x06: return 0xFF;
|
|
|
|
|
case 0x3E: return Port3E;
|
|
|
|
|
default: return 0xFF;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (port < 0x80) // VDP Vcounter/HCounter
|
|
|
|
|
{
|
|
|
|
|
if ((port & 1) == 0)
|
|
|
|
|
return Vdp.ReadVLineCounter();
|
|
|
|
|
else
|
|
|
|
|
return 0x50; // TODO Vdp.ReadHLineCounter();
|
|
|
|
|
}
|
|
|
|
|
if (port < 0xC0) // VDP data/control ports
|
|
|
|
|
{
|
|
|
|
|
if ((port & 1) == 0)
|
|
|
|
|
return Vdp.ReadData();
|
|
|
|
|
else
|
|
|
|
|
return Vdp.ReadVdpStatus();
|
|
|
|
|
}
|
|
|
|
|
switch (port)
|
2011-06-27 01:24:26 +00:00
|
|
|
|
{
|
|
|
|
|
case 0xC0:
|
|
|
|
|
case 0xDC: return ReadControls1();
|
|
|
|
|
case 0xC1:
|
|
|
|
|
case 0xDD: return ReadControls2();
|
|
|
|
|
case 0xF2: return HasYM2413 ? YM2413.DetectionValue : (byte)0xFF;
|
2014-03-17 03:56:42 +00:00
|
|
|
|
default: return 0xFF;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void WritePort(ushort port, byte value)
|
|
|
|
|
{
|
2014-03-17 03:56:42 +00:00
|
|
|
|
port &= 0xFF;
|
|
|
|
|
if (port < 0x40) // general IO ports
|
|
|
|
|
{
|
|
|
|
|
switch (port & 0xFF)
|
|
|
|
|
{
|
|
|
|
|
case 0x01: Port01 = value; break;
|
|
|
|
|
case 0x02: Port02 = value; break;
|
|
|
|
|
case 0x06: PSG.StereoPanning = value; break;
|
|
|
|
|
case 0x3E: Port3E = value; break;
|
|
|
|
|
case 0x3F: Port3F = value; break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (port < 0x80) // PSG
|
|
|
|
|
PSG.WritePsgData(value, Cpu.TotalExecutedCycles);
|
|
|
|
|
else if (port < 0xC0) // VDP
|
2011-06-27 01:24:26 +00:00
|
|
|
|
{
|
2014-03-17 03:56:42 +00:00
|
|
|
|
if ((port & 1) == 0)
|
|
|
|
|
Vdp.WriteVdpData(value);
|
|
|
|
|
else
|
|
|
|
|
Vdp.WriteVdpControl(value);
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
2014-03-17 03:56:42 +00:00
|
|
|
|
else if (port == 0xF0 && HasYM2413) YM2413.RegisterLatch = value;
|
|
|
|
|
else if (port == 0xF1 && HasYM2413) YM2413.Write(value);
|
|
|
|
|
else if (port == 0xF2 && HasYM2413) YM2413.DetectionValue = value;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-09-20 19:52:47 +00:00
|
|
|
|
public void FrameAdvance(bool render, bool rendersound)
|
2011-06-27 01:24:26 +00:00
|
|
|
|
{
|
|
|
|
|
lagged = true;
|
2013-12-07 00:53:06 +00:00
|
|
|
|
Frame++;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
PSG.BeginFrame(Cpu.TotalExecutedCycles);
|
2013-12-07 00:53:06 +00:00
|
|
|
|
Cpu.Debug = CoreComm.Tracer.Enabled;
|
2014-02-27 00:28:05 +00:00
|
|
|
|
if (!IsGameGear)
|
|
|
|
|
PSG.StereoPanning = Settings.ForceStereoSeparation ? ForceStereoByte : (byte) 0xFF;
|
|
|
|
|
|
2013-12-07 00:53:06 +00:00
|
|
|
|
if (Cpu.Debug && Cpu.Logger == null) // TODO, lets not do this on each frame. But lets refactor CoreComm/CoreComm first
|
|
|
|
|
Cpu.Logger = (s) => CoreComm.Tracer.Put(s);
|
2011-06-27 01:24:26 +00:00
|
|
|
|
|
|
|
|
|
if (IsGameGear == false)
|
|
|
|
|
Cpu.NonMaskableInterrupt = Controller["Pause"];
|
|
|
|
|
|
2014-03-05 02:20:22 +00:00
|
|
|
|
if (IsGame3D && Settings.Fix3D)
|
|
|
|
|
Vdp.ExecFrame((Frame & 1) == 0);
|
|
|
|
|
else
|
|
|
|
|
Vdp.ExecFrame(render);
|
|
|
|
|
|
2011-06-27 01:24:26 +00:00
|
|
|
|
PSG.EndFrame(Cpu.TotalExecutedCycles);
|
|
|
|
|
if (lagged)
|
|
|
|
|
{
|
2014-03-22 04:46:01 +00:00
|
|
|
|
lagCount++;
|
|
|
|
|
isLag = true;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2014-03-22 04:46:01 +00:00
|
|
|
|
isLag = false;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 04:46:01 +00:00
|
|
|
|
public bool BinarySaveStatesPreferred { get { return false; } }
|
|
|
|
|
public void SaveStateBinary(BinaryWriter bw) { SyncState(Serializer.CreateBinaryWriter(bw)); }
|
2014-04-07 03:09:44 +00:00
|
|
|
|
public void LoadStateBinary(BinaryReader br) { SyncState(Serializer.CreateBinaryReader(br)); }
|
2014-03-22 04:46:01 +00:00
|
|
|
|
public void SaveStateText(TextWriter tw) { SyncState(Serializer.CreateTextWriter(tw)); }
|
2014-04-07 03:09:44 +00:00
|
|
|
|
public void LoadStateText(TextReader tr) { SyncState(Serializer.CreateTextReader(tr)); }
|
2014-03-22 04:46:01 +00:00
|
|
|
|
|
|
|
|
|
void SyncState(Serializer ser)
|
2011-06-27 01:24:26 +00:00
|
|
|
|
{
|
2014-03-22 04:46:01 +00:00
|
|
|
|
ser.BeginSection("SMS");
|
|
|
|
|
Cpu.SyncState(ser);
|
|
|
|
|
Vdp.SyncState(ser);
|
|
|
|
|
PSG.SyncState(ser);
|
|
|
|
|
ser.Sync("RAM", ref SystemRam, false);
|
|
|
|
|
ser.Sync("RomBank0", ref RomBank0);
|
|
|
|
|
ser.Sync("RomBank1", ref RomBank1);
|
|
|
|
|
ser.Sync("RomBank2", ref RomBank2);
|
|
|
|
|
ser.Sync("RomBank3", ref RomBank3);
|
|
|
|
|
ser.Sync("Port01", ref Port01);
|
|
|
|
|
ser.Sync("Port02", ref Port02);
|
|
|
|
|
ser.Sync("Port3E", ref Port3E);
|
|
|
|
|
ser.Sync("Port3F", ref Port3F);
|
|
|
|
|
|
|
|
|
|
if (SaveRAM != null)
|
2011-06-27 01:24:26 +00:00
|
|
|
|
{
|
2014-03-22 04:46:01 +00:00
|
|
|
|
ser.Sync("SaveRAM", ref SaveRAM, false);
|
|
|
|
|
ser.Sync("SaveRamBank", ref SaveRamBank);
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
2014-03-06 04:43:36 +00:00
|
|
|
|
if (ExtRam != null)
|
2014-03-22 04:46:01 +00:00
|
|
|
|
ser.Sync("ExtRAM", ref ExtRam, true);
|
2011-06-27 01:24:26 +00:00
|
|
|
|
if (HasYM2413)
|
2014-03-22 04:46:01 +00:00
|
|
|
|
YM2413.SyncState(ser);
|
2011-06-27 01:24:26 +00:00
|
|
|
|
|
2014-03-22 04:46:01 +00:00
|
|
|
|
ser.Sync("Frame", ref frame);
|
|
|
|
|
ser.Sync("LagCount", ref lagCount);
|
|
|
|
|
ser.Sync("IsLag", ref isLag);
|
2011-06-27 01:24:26 +00:00
|
|
|
|
|
2014-03-22 04:46:01 +00:00
|
|
|
|
ser.EndSection();
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 04:46:01 +00:00
|
|
|
|
byte[] stateBuffer;
|
|
|
|
|
public byte[] SaveStateBinary()
|
2011-06-27 01:24:26 +00:00
|
|
|
|
{
|
2014-03-22 04:46:01 +00:00
|
|
|
|
if (stateBuffer == null)
|
2011-06-27 01:24:26 +00:00
|
|
|
|
{
|
2014-03-22 04:46:01 +00:00
|
|
|
|
var stream = new MemoryStream();
|
|
|
|
|
var writer = new BinaryWriter(stream);
|
|
|
|
|
SaveStateBinary(writer);
|
|
|
|
|
stateBuffer = stream.ToArray();
|
|
|
|
|
writer.Close();
|
|
|
|
|
return stateBuffer;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var stream = new MemoryStream(stateBuffer);
|
|
|
|
|
var writer = new BinaryWriter(stream);
|
|
|
|
|
SaveStateBinary(writer);
|
|
|
|
|
writer.Close();
|
|
|
|
|
return stateBuffer;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-03-22 04:46:01 +00:00
|
|
|
|
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public IVideoProvider VideoProvider { get { return Vdp; } }
|
2012-12-10 00:43:43 +00:00
|
|
|
|
public CoreComm CoreComm { get; private set; }
|
2011-01-11 02:55:51 +00:00
|
|
|
|
|
2011-09-05 17:45:01 +00:00
|
|
|
|
ISoundProvider ActiveSoundProvider;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public ISoundProvider SoundProvider { get { return ActiveSoundProvider; } }
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
public ISyncSoundProvider SyncSoundProvider { get { return new FakeSyncSound(ActiveSoundProvider, 735); } }
|
|
|
|
|
public bool StartAsyncSound() { return true; }
|
|
|
|
|
public void EndAsyncSound() { }
|
2011-06-27 01:24:26 +00:00
|
|
|
|
|
|
|
|
|
public string SystemId { get { return "SMS"; } }
|
|
|
|
|
|
2013-08-24 16:54:22 +00:00
|
|
|
|
public string BoardName { get { return null; } }
|
|
|
|
|
|
2014-03-13 04:49:39 +00:00
|
|
|
|
string region;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public string Region
|
|
|
|
|
{
|
|
|
|
|
get { return region; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value.NotIn(validRegions))
|
|
|
|
|
throw new Exception("Passed value " + value + " is not a valid region!");
|
|
|
|
|
region = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-03-17 03:00:07 +00:00
|
|
|
|
|
2014-03-22 05:57:27 +00:00
|
|
|
|
readonly string[] validRegions = { "Export", "Japan", "Auto" };
|
2011-06-27 01:24:26 +00:00
|
|
|
|
|
2013-11-06 02:15:29 +00:00
|
|
|
|
MemoryDomainList memoryDomains;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
|
2011-09-05 17:45:01 +00:00
|
|
|
|
void SetupMemoryDomains()
|
2011-06-27 01:24:26 +00:00
|
|
|
|
{
|
|
|
|
|
var domains = new List<MemoryDomain>(3);
|
2013-11-04 02:11:40 +00:00
|
|
|
|
var MainMemoryDomain = new MemoryDomain("Main RAM", SystemRam.Length, MemoryDomain.Endian.Little,
|
2014-02-26 20:18:48 +00:00
|
|
|
|
addr => SystemRam[addr],
|
|
|
|
|
(addr, value) => SystemRam[addr] = value);
|
2013-11-04 02:11:40 +00:00
|
|
|
|
var VRamDomain = new MemoryDomain("Video RAM", Vdp.VRAM.Length, MemoryDomain.Endian.Little,
|
2014-02-26 20:18:48 +00:00
|
|
|
|
addr => Vdp.VRAM[addr],
|
|
|
|
|
(addr, value) => Vdp.VRAM[addr] = value);
|
2014-03-22 04:46:01 +00:00
|
|
|
|
|
2013-11-04 02:11:40 +00:00
|
|
|
|
var SystemBusDomain = new MemoryDomain("System Bus", 0x10000, MemoryDomain.Endian.Little,
|
2014-02-26 20:18:48 +00:00
|
|
|
|
(addr) =>
|
|
|
|
|
{
|
|
|
|
|
if (addr < 0 || addr >= 65536)
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
return Cpu.ReadMemory((ushort)addr);
|
|
|
|
|
},
|
|
|
|
|
(addr, value) =>
|
|
|
|
|
{
|
|
|
|
|
if (addr < 0 || addr >= 65536)
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
Cpu.WriteMemory((ushort)addr, value);
|
|
|
|
|
});
|
2011-06-27 01:24:26 +00:00
|
|
|
|
|
|
|
|
|
domains.Add(MainMemoryDomain);
|
|
|
|
|
domains.Add(VRamDomain);
|
|
|
|
|
domains.Add(SystemBusDomain);
|
2014-03-22 04:46:01 +00:00
|
|
|
|
|
|
|
|
|
if (SaveRAM != null)
|
|
|
|
|
{
|
|
|
|
|
var SaveRamDomain = new MemoryDomain("Save RAM", SaveRAM.Length, MemoryDomain.Endian.Little,
|
|
|
|
|
addr => SaveRAM[addr],
|
|
|
|
|
(addr, value) => { SaveRAM[addr] = value; SaveRamModified = true; });
|
|
|
|
|
domains.Add(SaveRamDomain);
|
|
|
|
|
}
|
|
|
|
|
if (ExtRam != null)
|
|
|
|
|
{
|
|
|
|
|
var ExtRamDomain = new MemoryDomain("Cart (Volatile) RAM", ExtRam.Length, MemoryDomain.Endian.Little,
|
|
|
|
|
addr => ExtRam[addr],
|
|
|
|
|
(addr, value) => { ExtRam[addr] = value; });
|
|
|
|
|
domains.Add(ExtRamDomain);
|
|
|
|
|
}
|
2013-11-06 02:15:29 +00:00
|
|
|
|
memoryDomains = new MemoryDomainList(domains);
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-06 02:15:29 +00:00
|
|
|
|
public MemoryDomainList MemoryDomains { get { return memoryDomains; } }
|
2011-06-27 01:24:26 +00:00
|
|
|
|
|
2014-04-19 22:23:13 +00:00
|
|
|
|
public Dictionary<string, int> GetCpuFlagsAndRegisters()
|
2014-03-17 03:00:07 +00:00
|
|
|
|
{
|
2014-04-19 22:23:13 +00:00
|
|
|
|
return new Dictionary<string, int>
|
2014-03-17 03:00:07 +00:00
|
|
|
|
{
|
2014-04-19 22:23:13 +00:00
|
|
|
|
{ "A", Cpu.RegisterA },
|
|
|
|
|
{ "AF", Cpu.RegisterAF },
|
|
|
|
|
{ "B", Cpu.RegisterB },
|
|
|
|
|
{ "BC", Cpu.RegisterBC },
|
|
|
|
|
{ "C", Cpu.RegisterC },
|
|
|
|
|
{ "D", Cpu.RegisterD },
|
|
|
|
|
{ "DE", Cpu.RegisterDE },
|
|
|
|
|
{ "E", Cpu.RegisterE },
|
|
|
|
|
{ "F", Cpu.RegisterF },
|
|
|
|
|
{ "H", Cpu.RegisterH },
|
|
|
|
|
{ "HL", Cpu.RegisterHL },
|
|
|
|
|
{ "I", Cpu.RegisterI },
|
|
|
|
|
{ "IX", Cpu.RegisterIX },
|
|
|
|
|
{ "IY", Cpu.RegisterIY },
|
|
|
|
|
{ "L", Cpu.RegisterL },
|
|
|
|
|
{ "PC", Cpu.RegisterPC },
|
|
|
|
|
{ "R", Cpu.RegisterR },
|
|
|
|
|
{ "Shadow AF", Cpu.RegisterShadowAF },
|
|
|
|
|
{ "Shadow BC", Cpu.RegisterShadowBC },
|
|
|
|
|
{ "Shadow DE", Cpu.RegisterShadowDE },
|
|
|
|
|
{ "Shadow HL", Cpu.RegisterShadowHL },
|
|
|
|
|
{ "SP", Cpu.RegisterSP },
|
|
|
|
|
{ "Flag C", Cpu.RegisterF.Bit(0) ? 1 : 0 },
|
|
|
|
|
{ "Flag N", Cpu.RegisterF.Bit(1) ? 1 : 0 },
|
|
|
|
|
{ "Flag P/V", Cpu.RegisterF.Bit(2) ? 1 : 0 },
|
|
|
|
|
{ "Flag 3rd", Cpu.RegisterF.Bit(3) ? 1 : 0 },
|
|
|
|
|
{ "Flag H", Cpu.RegisterF.Bit(4) ? 1 : 0 },
|
|
|
|
|
{ "Flag 5th", Cpu.RegisterF.Bit(5) ? 1 : 0 },
|
|
|
|
|
{ "Flag Z", Cpu.RegisterF.Bit(6) ? 1 : 0 },
|
|
|
|
|
{ "Flag S", Cpu.RegisterF.Bit(7) ? 1 : 0 },
|
2014-03-17 03:00:07 +00:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public void Dispose() { }
|
2013-12-22 00:44:39 +00:00
|
|
|
|
|
2013-12-24 01:06:17 +00:00
|
|
|
|
public object GetSettings() { return Settings.Clone(); }
|
|
|
|
|
public object GetSyncSettings() { return SyncSettings.Clone(); }
|
|
|
|
|
public bool PutSettings(object o)
|
|
|
|
|
{
|
|
|
|
|
SMSSettings n = (SMSSettings)o;
|
|
|
|
|
bool ret = SMSSettings.RebootNeeded(Settings, n);
|
|
|
|
|
Settings = n;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
public bool PutSyncSettings(object o)
|
|
|
|
|
{
|
|
|
|
|
SMSSyncSettings n = (SMSSyncSettings)o;
|
|
|
|
|
bool ret = SMSSyncSettings.RebootNeeded(SyncSettings, n);
|
|
|
|
|
SyncSettings = n;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SMSSettings Settings;
|
|
|
|
|
public SMSSyncSettings SyncSettings;
|
|
|
|
|
|
|
|
|
|
public class SMSSettings
|
|
|
|
|
{
|
2014-03-13 04:15:05 +00:00
|
|
|
|
// Game settings
|
2013-12-24 01:06:17 +00:00
|
|
|
|
public bool ForceStereoSeparation = false;
|
|
|
|
|
public bool SpriteLimit = false;
|
2014-03-05 02:20:22 +00:00
|
|
|
|
public bool Fix3D = true;
|
2013-12-24 01:06:17 +00:00
|
|
|
|
// GG settings
|
|
|
|
|
public bool ShowClippedRegions = false;
|
|
|
|
|
public bool HighlightActiveDisplayRegion = false;
|
|
|
|
|
// graphics settings
|
|
|
|
|
public bool DispBG = true;
|
|
|
|
|
public bool DispOBJ = true;
|
|
|
|
|
|
|
|
|
|
public SMSSettings Clone()
|
|
|
|
|
{
|
|
|
|
|
return (SMSSettings)MemberwiseClone();
|
|
|
|
|
}
|
|
|
|
|
public static bool RebootNeeded(SMSSettings x, SMSSettings y)
|
|
|
|
|
{
|
2014-02-27 00:28:05 +00:00
|
|
|
|
return false;
|
2013-12-24 01:06:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class SMSSyncSettings
|
|
|
|
|
{
|
|
|
|
|
public bool EnableFM = true;
|
|
|
|
|
public bool AllowOverlock = false;
|
2014-03-13 04:15:05 +00:00
|
|
|
|
public bool UseBIOS = false;
|
2014-03-22 05:57:27 +00:00
|
|
|
|
public string ConsoleRegion = "Export";
|
|
|
|
|
public string DisplayType = "NTSC";
|
2013-12-24 01:06:17 +00:00
|
|
|
|
|
|
|
|
|
public SMSSyncSettings Clone()
|
|
|
|
|
{
|
|
|
|
|
return (SMSSyncSettings)MemberwiseClone();
|
|
|
|
|
}
|
|
|
|
|
public static bool RebootNeeded(SMSSyncSettings x, SMSSyncSettings y)
|
|
|
|
|
{
|
2014-03-13 04:15:05 +00:00
|
|
|
|
return
|
|
|
|
|
x.EnableFM != y.EnableFM ||
|
|
|
|
|
x.AllowOverlock != y.AllowOverlock ||
|
|
|
|
|
x.UseBIOS != y.UseBIOS ||
|
2014-03-22 05:57:27 +00:00
|
|
|
|
x.ConsoleRegion != y.ConsoleRegion ||
|
|
|
|
|
x.DisplayType != y.DisplayType;
|
2013-12-24 01:06:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
2014-03-22 04:46:01 +00:00
|
|
|
|
}
|