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.IO;
|
2013-10-27 22:07:40 +00:00
|
|
|
|
|
|
|
|
|
using BizHawk.Common;
|
2014-07-03 17:23:03 +00:00
|
|
|
|
using BizHawk.Common.NumberExtensions;
|
2014-07-03 15:05:02 +00:00
|
|
|
|
using BizHawk.Common.StringExtensions;
|
2014-07-03 17:23:03 +00:00
|
|
|
|
|
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-25 01:19:57 +00:00
|
|
|
|
[CoreAttributes(
|
|
|
|
|
"SMSHawk",
|
|
|
|
|
"Vecna",
|
|
|
|
|
isPorted: false,
|
|
|
|
|
isReleased: true
|
|
|
|
|
)]
|
2014-12-12 01:58:12 +00:00
|
|
|
|
[ServiceNotApplicable(typeof(IDriveLight))]
|
2015-08-06 00:12:09 +00:00
|
|
|
|
public sealed partial class SMS : IEmulator, ISaveRam, IStatable, IInputPollable, IRegionable,
|
2015-10-30 05:00:57 +00:00
|
|
|
|
IDebuggable, ISettable<SMS.SMSSettings, SMS.SMSSyncSettings>, ICodeDataLogger
|
2011-06-27 01:24:26 +00:00
|
|
|
|
{
|
|
|
|
|
// 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;
|
|
|
|
|
|
2014-08-13 17:52:13 +00:00
|
|
|
|
public byte[] CloneSaveRam()
|
2012-09-14 23:31:10 +00:00
|
|
|
|
{
|
|
|
|
|
if (SaveRAM != null)
|
|
|
|
|
return (byte[])SaveRAM.Clone();
|
|
|
|
|
else
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
public void StoreSaveRam(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
if (SaveRAM != null)
|
|
|
|
|
Array.Copy(data, SaveRAM, data.Length);
|
|
|
|
|
}
|
2014-11-24 15:00:54 +00:00
|
|
|
|
|
2014-11-30 15:22:08 +00:00
|
|
|
|
public bool SaveRamModified { get; private set; }
|
2011-06-27 01:24:26 +00:00
|
|
|
|
|
|
|
|
|
// Machine resources
|
|
|
|
|
public Z80A Cpu;
|
|
|
|
|
public byte[] SystemRam;
|
|
|
|
|
public VDP Vdp;
|
|
|
|
|
public SN76489 PSG;
|
|
|
|
|
public YM2413 YM2413;
|
|
|
|
|
public SoundMixer SoundMixer;
|
|
|
|
|
public bool IsGameGear = false;
|
2014-05-31 23:17:39 +00:00
|
|
|
|
public bool IsSG1000 = false;
|
|
|
|
|
|
2011-06-27 01:24:26 +00:00
|
|
|
|
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; } }
|
2015-07-09 17:05:30 +00:00
|
|
|
|
public int LagCount { get { return lagCount; } set { lagCount = value; } }
|
2016-01-26 10:34:42 +00:00
|
|
|
|
public bool IsLagFrame { get { return isLag; } set { isLag = value; } }
|
2014-12-04 00:43:12 +00:00
|
|
|
|
|
|
|
|
|
private readonly InputCallbackSystem _inputCallbacks = new InputCallbackSystem();
|
|
|
|
|
public IInputCallbackSystem InputCallbacks { get { return _inputCallbacks; } }
|
2014-12-05 01:56:45 +00:00
|
|
|
|
public IMemoryCallbackSystem MemoryCallbacks { get; private set; }
|
2014-12-04 00:43:12 +00:00
|
|
|
|
|
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
|
|
|
|
|
2015-08-06 00:12:09 +00:00
|
|
|
|
public DisplayType Region { 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-08-23 19:06:37 +00:00
|
|
|
|
[CoreConstructor("SMS", "SG", "GG")]
|
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-12-04 03:38:30 +00:00
|
|
|
|
ServiceProvider = new BasicServiceProvider(this);
|
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-12-05 01:56:45 +00:00
|
|
|
|
MemoryCallbacks = new MemoryCallbackSystem();
|
2014-03-22 04:46:01 +00:00
|
|
|
|
|
2012-12-10 00:43:43 +00:00
|
|
|
|
IsGameGear = game.System == "GG";
|
2014-05-31 23:17:39 +00:00
|
|
|
|
IsSG1000 = game.System == "SG";
|
2014-10-19 01:22:47 +00:00
|
|
|
|
RomData = rom;
|
2016-02-28 13:07:02 +00:00
|
|
|
|
|
2014-10-19 01:22:47 +00:00
|
|
|
|
if (RomData.Length % BankSize != 0)
|
|
|
|
|
Array.Resize(ref RomData, ((RomData.Length / BankSize) + 1) * BankSize);
|
|
|
|
|
RomBanks = (byte)(RomData.Length / BankSize);
|
|
|
|
|
|
2015-08-06 00:12:09 +00:00
|
|
|
|
Region = DetermineDisplayType(SyncSettings.DisplayType, game.Region);
|
|
|
|
|
if (game["PAL"] && Region != DisplayType.PAL)
|
2014-03-13 04:49:39 +00:00
|
|
|
|
{
|
2015-08-06 00:12:09 +00:00
|
|
|
|
Region = 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)
|
2015-08-06 00:12:09 +00:00
|
|
|
|
Region = DisplayType.NTSC; // all game gears run at 60hz/NTSC mode
|
|
|
|
|
CoreComm.VsyncNum = Region == DisplayType.NTSC ? 60 : 50;
|
2012-12-10 00:43:43 +00:00
|
|
|
|
CoreComm.VsyncDen = 1;
|
2014-03-13 04:49:39 +00:00
|
|
|
|
|
2015-08-06 00:12:09 +00:00
|
|
|
|
RegionStr = SyncSettings.ConsoleRegion;
|
|
|
|
|
if (RegionStr == "Auto") RegionStr = DetermineRegion(game.Region);
|
2014-03-22 05:57:27 +00:00
|
|
|
|
|
2015-08-06 00:12:09 +00:00
|
|
|
|
if (game["Japan"] && RegionStr != "Japan")
|
2014-03-13 04:49:39 +00:00
|
|
|
|
{
|
2015-08-06 00:12:09 +00:00
|
|
|
|
RegionStr = "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-10-19 01:22:47 +00:00
|
|
|
|
if ((game.NotInDatabase || game["FM"]) && SyncSettings.EnableFM && !IsGameGear)
|
|
|
|
|
HasYM2413 = true;
|
2011-08-04 03:20:54 +00:00
|
|
|
|
|
2014-10-19 01:22:47 +00:00
|
|
|
|
if (Controller == null)
|
|
|
|
|
Controller = NullController.GetNullController();
|
2011-08-04 03:20:54 +00:00
|
|
|
|
|
2014-10-19 01:22:47 +00:00
|
|
|
|
Cpu = new Z80A();
|
|
|
|
|
Cpu.RegisterSP = 0xDFF0;
|
|
|
|
|
Cpu.ReadHardware = ReadPort;
|
|
|
|
|
Cpu.WriteHardware = WritePort;
|
2015-01-24 20:36:36 +00:00
|
|
|
|
Cpu.MemoryCallbacks = MemoryCallbacks;
|
2011-08-04 03:20:54 +00:00
|
|
|
|
|
2015-08-06 00:12:09 +00:00
|
|
|
|
Vdp = new VDP(this, Cpu, IsGameGear ? VdpMode.GameGear : VdpMode.SMS, Region);
|
2015-01-14 22:37:37 +00:00
|
|
|
|
(ServiceProvider as BasicServiceProvider).Register<IVideoProvider>(Vdp);
|
2014-10-19 01:22:47 +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;
|
2011-08-04 03:20:54 +00:00
|
|
|
|
|
2014-10-19 01:22:47 +00:00
|
|
|
|
SystemRam = new byte[0x2000];
|
2011-08-04 03:20:54 +00:00
|
|
|
|
|
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();
|
|
|
|
|
|
2014-10-19 01:22:47 +00:00
|
|
|
|
if (Settings.ForceStereoSeparation && !IsGameGear)
|
|
|
|
|
{
|
|
|
|
|
if (game["StereoByte"])
|
|
|
|
|
{
|
|
|
|
|
ForceStereoByte = byte.Parse(game.OptionValue("StereoByte"));
|
|
|
|
|
}
|
2014-02-27 00:28:05 +00:00
|
|
|
|
PSG.StereoPanning = ForceStereoByte;
|
2014-10-19 01:22:47 +00:00
|
|
|
|
}
|
2011-08-04 03:20:54 +00:00
|
|
|
|
|
2014-10-19 01:22:47 +00:00
|
|
|
|
if (SyncSettings.AllowOverlock && game["OverclockSafe"])
|
|
|
|
|
Vdp.IPeriod = 512;
|
2011-08-04 03:20:54 +00:00
|
|
|
|
|
2014-10-19 01:22:47 +00:00
|
|
|
|
if (Settings.SpriteLimit)
|
|
|
|
|
Vdp.SpriteLimit = true;
|
2011-09-19 00:39:28 +00:00
|
|
|
|
|
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
|
|
|
|
{
|
2015-08-06 00:12:09 +00:00
|
|
|
|
BiosRom = comm.CoreFileProvider.GetFirmware("SMS", RegionStr, 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-08-25 00:14:14 +00:00
|
|
|
|
throw new MissingFirmwareException("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();
|
2015-10-30 05:00:57 +00:00
|
|
|
|
|
|
|
|
|
//this manages the linkage between the cpu and mapper callbacks so it needs running before bootup is complete
|
|
|
|
|
((ICodeDataLogger)this).SetCDL(null);
|
|
|
|
|
|
2016-02-28 13:07:02 +00:00
|
|
|
|
Tracer = new TraceBuffer { Header = Cpu.TraceHeader };
|
|
|
|
|
|
|
|
|
|
var serviceProvider = ServiceProvider as BasicServiceProvider;
|
|
|
|
|
serviceProvider.Register<ITraceable>(Tracer);
|
|
|
|
|
serviceProvider.Register<IDisassemblable>(new Disassembler());
|
|
|
|
|
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-04 03:38:30 +00:00
|
|
|
|
public IEmulatorServiceProvider ServiceProvider { get; private set; }
|
|
|
|
|
|
2014-12-23 01:58:12 +00:00
|
|
|
|
private ITraceable Tracer { get; set; }
|
2014-12-05 00:05:40 +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
|
|
|
|
}
|
|
|
|
|
|
2015-10-30 05:00:57 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The ReadMemory callback for the mapper
|
|
|
|
|
/// </summary>
|
|
|
|
|
Func<ushort, byte> ReadMemory;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The WriteMemory callback for the wrapper
|
|
|
|
|
/// </summary>
|
|
|
|
|
Action<ushort, byte> WriteMemory;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// A dummy FetchMemory that simply reads the memory
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte FetchMemory_StubThunk(ushort address, bool first)
|
|
|
|
|
{
|
|
|
|
|
return ReadMemory(address);
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
2014-12-05 00:05:40 +00:00
|
|
|
|
Cpu.Debug = 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
|
2014-12-05 00:05:40 +00:00
|
|
|
|
Cpu.Logger = (s) => 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
|
|
|
|
|
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;
|
2015-08-06 00:12:09 +00:00
|
|
|
|
public string RegionStr
|
2011-06-27 01:24:26 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
2015-10-30 05:00:57 +00:00
|
|
|
|
|
|
|
|
|
var ROMDomain = new MemoryDomain("ROM", RomData.Length, MemoryDomain.Endian.Little,
|
|
|
|
|
addr => RomData[addr],
|
|
|
|
|
(addr, value) => RomData[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);
|
2015-10-30 05:00:57 +00:00
|
|
|
|
domains.Add(ROMDomain);
|
2011-06-27 01:24:26 +00:00
|
|
|
|
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);
|
2015-01-14 21:55:48 +00:00
|
|
|
|
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(memoryDomains);
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-20 13:16:15 +00:00
|
|
|
|
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
|
2014-03-17 03:00:07 +00:00
|
|
|
|
{
|
2014-12-20 13:16:15 +00:00
|
|
|
|
return new Dictionary<string, RegisterValue>
|
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 },
|
2014-12-20 03:19:33 +00:00
|
|
|
|
{ "Flag C", Cpu.RegisterF.Bit(0) },
|
|
|
|
|
{ "Flag N", Cpu.RegisterF.Bit(1) },
|
|
|
|
|
{ "Flag P/V", Cpu.RegisterF.Bit(2) },
|
|
|
|
|
{ "Flag 3rd", Cpu.RegisterF.Bit(3) },
|
|
|
|
|
{ "Flag H", Cpu.RegisterF.Bit(4) },
|
|
|
|
|
{ "Flag 5th", Cpu.RegisterF.Bit(5) },
|
|
|
|
|
{ "Flag Z", Cpu.RegisterF.Bit(6) },
|
|
|
|
|
{ "Flag S", Cpu.RegisterF.Bit(7) },
|
2014-03-17 03:00:07 +00:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-31 17:03:21 +00:00
|
|
|
|
public void SetCpuRegister(string register, int value)
|
|
|
|
|
{
|
2014-05-31 18:25:36 +00:00
|
|
|
|
switch (register)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
case "A":
|
|
|
|
|
Cpu.RegisterA = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "AF":
|
|
|
|
|
Cpu.RegisterAF = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "B":
|
|
|
|
|
Cpu.RegisterB = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "BC":
|
|
|
|
|
Cpu.RegisterBC = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "C":
|
|
|
|
|
Cpu.RegisterC = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "D":
|
|
|
|
|
Cpu.RegisterD = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "DE":
|
|
|
|
|
Cpu.RegisterDE = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "E":
|
|
|
|
|
Cpu.RegisterE = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "F":
|
|
|
|
|
Cpu.RegisterF = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "H":
|
|
|
|
|
Cpu.RegisterH = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "HL":
|
|
|
|
|
Cpu.RegisterHL = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "I":
|
|
|
|
|
Cpu.RegisterI = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "IX":
|
|
|
|
|
Cpu.RegisterIX = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "IY":
|
|
|
|
|
Cpu.RegisterIY = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "L":
|
|
|
|
|
Cpu.RegisterL = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "PC":
|
|
|
|
|
Cpu.RegisterPC = (ushort)value;
|
|
|
|
|
break;
|
|
|
|
|
case "R":
|
|
|
|
|
Cpu.RegisterR = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "Shadow AF":
|
|
|
|
|
Cpu.RegisterShadowAF = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "Shadow BC":
|
|
|
|
|
Cpu.RegisterShadowBC = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "Shadow DE":
|
|
|
|
|
Cpu.RegisterShadowDE = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "Shadow HL":
|
|
|
|
|
Cpu.RegisterShadowHL = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
case "SP":
|
|
|
|
|
Cpu.RegisterSP = (byte)value;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-05-31 17:03:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-20 13:29:57 +00:00
|
|
|
|
public bool CanStep(StepType type) { return false; }
|
|
|
|
|
|
2014-12-14 18:58:16 +00:00
|
|
|
|
[FeatureNotImplemented]
|
2014-12-15 22:19:10 +00:00
|
|
|
|
public void Step(StepType type) { throw new NotImplementedException(); }
|
2014-12-14 18:58:16 +00:00
|
|
|
|
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public void Dispose() { }
|
2013-12-22 00:44:39 +00:00
|
|
|
|
|
2014-10-19 01:22:47 +00:00
|
|
|
|
public SMSSettings GetSettings() { return Settings.Clone(); }
|
|
|
|
|
public SMSSyncSettings GetSyncSettings() { return SyncSettings.Clone(); }
|
|
|
|
|
public bool PutSettings(SMSSettings o)
|
2013-12-24 01:06:17 +00:00
|
|
|
|
{
|
2014-10-19 01:22:47 +00:00
|
|
|
|
bool ret = SMSSettings.RebootNeeded(Settings, o);
|
|
|
|
|
Settings = o;
|
2013-12-24 01:06:17 +00:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
2014-10-19 01:22:47 +00:00
|
|
|
|
public bool PutSyncSettings(SMSSyncSettings o)
|
2013-12-24 01:06:17 +00:00
|
|
|
|
{
|
2014-10-19 01:22:47 +00:00
|
|
|
|
bool ret = SMSSyncSettings.RebootNeeded(SyncSettings, o);
|
|
|
|
|
SyncSettings = o;
|
2013-12-24 01:06:17 +00:00
|
|
|
|
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
|
|
|
|
}
|