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;
|
|
|
|
|
using BizHawk.Emulation.CPUs.Z80;
|
|
|
|
|
using BizHawk.Emulation.Sound;
|
|
|
|
|
|
|
|
|
|
/*****************************************************
|
|
|
|
|
|
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
|
2011-01-11 02:55:51 +00:00
|
|
|
|
|
2012-11-26 01:44:17 +00:00
|
|
|
|
+ SG-1000 TMS does not fire sprite collision bit
|
|
|
|
|
+ SG-1000 TMS sprite doubling most likely does not work!
|
|
|
|
|
+ Maybe try to unify the Coleco TMS with SG-1000? Or at least pull in other TMS fixes.
|
|
|
|
|
|
2011-01-11 02:55:51 +00:00
|
|
|
|
**********************************************************/
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Consoles.Sega
|
|
|
|
|
{
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public sealed partial class SMS : IEmulator
|
|
|
|
|
{
|
|
|
|
|
// Constants
|
|
|
|
|
public const int BankSize = 16384;
|
|
|
|
|
|
|
|
|
|
// ROM
|
|
|
|
|
public byte[] RomData;
|
|
|
|
|
public byte RomBank0, RomBank1, RomBank2;
|
|
|
|
|
public byte RomBanks;
|
|
|
|
|
|
|
|
|
|
// SaveRAM
|
|
|
|
|
public byte[] SaveRAM = new byte[BankSize * 2];
|
|
|
|
|
public byte SaveRamBank;
|
|
|
|
|
|
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;
|
|
|
|
|
|
2011-09-05 17:45:01 +00:00
|
|
|
|
int _lagcount = 0;
|
|
|
|
|
bool lagged = true;
|
|
|
|
|
bool islag = false;
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public int Frame { get; set; }
|
2011-07-30 20:49:36 +00:00
|
|
|
|
|
|
|
|
|
public void ResetFrameCounter()
|
|
|
|
|
{
|
|
|
|
|
Frame = 0;
|
2012-11-25 15:41:40 +00:00
|
|
|
|
_lagcount = 0;
|
|
|
|
|
islag = false;
|
2011-07-30 20:49:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-06-27 01:24:26 +00:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2012-12-10 00:43:43 +00:00
|
|
|
|
public SMS(CoreComm comm, GameInfo game, byte[] rom)
|
2011-06-11 22:15:08 +00:00
|
|
|
|
{
|
2012-12-10 00:43:43 +00:00
|
|
|
|
CoreComm = comm;
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2011-08-04 03:20:54 +00:00
|
|
|
|
DisplayType = DisplayType.NTSC;
|
2012-07-15 07:15:31 +00:00
|
|
|
|
if (game["PAL"]) DisplayType = DisplayType.PAL;
|
2012-12-10 00:43:43 +00:00
|
|
|
|
CoreComm.VsyncNum = DisplayType == DisplayType.NTSC ? 60 : 50;
|
|
|
|
|
CoreComm.VsyncDen = 1;
|
2012-07-15 07:15:31 +00:00
|
|
|
|
|
2011-08-04 03:20:54 +00:00
|
|
|
|
if (game["Japan"]) Region = "Japan";
|
|
|
|
|
if (game.NotInDatabase || game["FM"] && game["UseFM"])
|
|
|
|
|
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];
|
|
|
|
|
if (game["CMMapper"] == false)
|
|
|
|
|
InitSegaMapper();
|
|
|
|
|
else
|
|
|
|
|
InitCodeMastersMapper();
|
|
|
|
|
|
|
|
|
|
if (game["ForceStereo"])
|
|
|
|
|
{
|
|
|
|
|
byte stereoByte = 0xAD;
|
|
|
|
|
if (game["StereoByte"])
|
|
|
|
|
{
|
|
|
|
|
stereoByte = byte.Parse(game.OptionValue("StereoByte"));
|
|
|
|
|
}
|
|
|
|
|
PSG.StereoPanning = stereoByte;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (game["AllowOverclock"] && game["OverclockSafe"])
|
|
|
|
|
Vdp.IPeriod = 512;
|
|
|
|
|
|
2011-09-19 00:39:28 +00:00
|
|
|
|
if (game["SpriteLimit"])
|
|
|
|
|
Vdp.SpriteLimit = true;
|
|
|
|
|
|
2011-08-04 03:20:54 +00:00
|
|
|
|
if (game["BIOS"])
|
|
|
|
|
{
|
|
|
|
|
Port3E = 0xF7; // Disable cartridge, enable BIOS rom
|
|
|
|
|
InitBiosMapper();
|
|
|
|
|
}
|
|
|
|
|
SetupMemoryDomains();
|
2011-06-27 01:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte ReadPort(ushort port)
|
|
|
|
|
{
|
|
|
|
|
switch (port & 0xFF)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
case 0x7E: return Vdp.ReadVLineCounter();
|
|
|
|
|
case 0x7F: break; // hline counter TODO
|
|
|
|
|
case 0xBE: return Vdp.ReadData();
|
|
|
|
|
case 0xBF: return Vdp.ReadVdpStatus();
|
|
|
|
|
case 0xC0:
|
|
|
|
|
case 0xDC: return ReadControls1();
|
|
|
|
|
case 0xC1:
|
|
|
|
|
case 0xDD: return ReadControls2();
|
|
|
|
|
case 0xF2: return HasYM2413 ? YM2413.DetectionValue : (byte)0xFF;
|
|
|
|
|
}
|
|
|
|
|
return 0xFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void WritePort(ushort port, byte value)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
case 0x7E:
|
|
|
|
|
case 0x7F: PSG.WritePsgData(value, Cpu.TotalExecutedCycles); break;
|
|
|
|
|
case 0xBE: Vdp.WriteVdpData(value); break;
|
|
|
|
|
case 0xBD:
|
|
|
|
|
case 0xBF: Vdp.WriteVdpControl(value); break;
|
|
|
|
|
case 0xF0: if (HasYM2413) YM2413.RegisterLatch = value; break;
|
|
|
|
|
case 0xF1: if (HasYM2413) YM2413.Write(value); break;
|
|
|
|
|
case 0xF2: if (HasYM2413) YM2413.DetectionValue = value; break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
Controller.UpdateControls(Frame++);
|
|
|
|
|
PSG.BeginFrame(Cpu.TotalExecutedCycles);
|
2012-12-10 00:43:43 +00:00
|
|
|
|
Cpu.Debug = CoreComm.Tracer.Enabled;
|
|
|
|
|
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"];
|
|
|
|
|
|
|
|
|
|
Vdp.ExecFrame(render);
|
|
|
|
|
PSG.EndFrame(Cpu.TotalExecutedCycles);
|
|
|
|
|
if (lagged)
|
|
|
|
|
{
|
|
|
|
|
_lagcount++;
|
|
|
|
|
islag = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
islag = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SaveStateText(TextWriter writer)
|
|
|
|
|
{
|
|
|
|
|
writer.WriteLine("[SMS]\n");
|
|
|
|
|
Cpu.SaveStateText(writer);
|
|
|
|
|
PSG.SaveStateText(writer);
|
|
|
|
|
Vdp.SaveStateText(writer);
|
|
|
|
|
|
|
|
|
|
writer.WriteLine("Frame {0}", Frame);
|
|
|
|
|
writer.WriteLine("Lag {0}", _lagcount);
|
2012-11-24 14:19:59 +00:00
|
|
|
|
writer.WriteLine("IsLag {0}", islag);
|
2011-06-27 01:24:26 +00:00
|
|
|
|
writer.WriteLine("Bank0 {0}", RomBank0);
|
|
|
|
|
writer.WriteLine("Bank1 {0}", RomBank1);
|
|
|
|
|
writer.WriteLine("Bank2 {0}", RomBank2);
|
|
|
|
|
writer.Write("RAM ");
|
|
|
|
|
SystemRam.SaveAsHex(writer);
|
|
|
|
|
writer.WriteLine("Port01 {0:X2}", Port01);
|
|
|
|
|
writer.WriteLine("Port02 {0:X2}", Port02);
|
|
|
|
|
writer.WriteLine("Port3F {0:X2}", Port3F);
|
|
|
|
|
int SaveRamLen = Util.SaveRamBytesUsed(SaveRAM);
|
|
|
|
|
if (SaveRamLen > 0)
|
|
|
|
|
{
|
|
|
|
|
writer.Write("SaveRAM ");
|
|
|
|
|
SaveRAM.SaveAsHex(writer, SaveRamLen);
|
|
|
|
|
}
|
|
|
|
|
if (HasYM2413)
|
|
|
|
|
{
|
|
|
|
|
writer.Write("FMRegs ");
|
|
|
|
|
YM2413.opll.reg.SaveAsHex(writer);
|
|
|
|
|
}
|
|
|
|
|
writer.WriteLine("[/SMS]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void LoadStateText(TextReader reader)
|
|
|
|
|
{
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
string[] args = reader.ReadLine().Split(' ');
|
|
|
|
|
if (args[0].Trim() == "") continue;
|
|
|
|
|
if (args[0] == "[SMS]") continue;
|
|
|
|
|
if (args[0] == "[/SMS]") break;
|
|
|
|
|
if (args[0] == "Bank0")
|
|
|
|
|
RomBank0 = byte.Parse(args[1]);
|
|
|
|
|
else if (args[0] == "Bank1")
|
|
|
|
|
RomBank1 = byte.Parse(args[1]);
|
|
|
|
|
else if (args[0] == "Bank2")
|
|
|
|
|
RomBank2 = byte.Parse(args[1]);
|
|
|
|
|
else if (args[0] == "Frame")
|
|
|
|
|
Frame = int.Parse(args[1]);
|
|
|
|
|
else if (args[0] == "Lag")
|
|
|
|
|
_lagcount = int.Parse(args[1]);
|
2012-11-24 14:19:59 +00:00
|
|
|
|
else if (args[0] == "IsLag")
|
2012-11-22 01:01:39 +00:00
|
|
|
|
islag = bool.Parse(args[1]);
|
2011-06-27 01:24:26 +00:00
|
|
|
|
else if (args[0] == "RAM")
|
|
|
|
|
SystemRam.ReadFromHex(args[1]);
|
|
|
|
|
else if (args[0] == "SaveRAM")
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < SaveRAM.Length; i++) SaveRAM[i] = 0;
|
|
|
|
|
SaveRAM.ReadFromHex(args[1]);
|
|
|
|
|
}
|
|
|
|
|
else if (args[0] == "FMRegs")
|
|
|
|
|
{
|
|
|
|
|
byte[] regs = new byte[YM2413.opll.reg.Length];
|
|
|
|
|
regs.ReadFromHex(args[1]);
|
|
|
|
|
for (byte i = 0; i < regs.Length; i++)
|
|
|
|
|
YM2413.Write(i, regs[i]);
|
|
|
|
|
}
|
|
|
|
|
else if (args[0] == "Port01")
|
|
|
|
|
Port01 = byte.Parse(args[1], NumberStyles.HexNumber);
|
|
|
|
|
else if (args[0] == "Port02")
|
|
|
|
|
Port02 = byte.Parse(args[1], NumberStyles.HexNumber);
|
|
|
|
|
else if (args[0] == "Port3F")
|
|
|
|
|
Port3F = byte.Parse(args[1], NumberStyles.HexNumber);
|
|
|
|
|
else if (args[0] == "[Z80]")
|
|
|
|
|
Cpu.LoadStateText(reader);
|
|
|
|
|
else if (args[0] == "[PSG]")
|
|
|
|
|
PSG.LoadStateText(reader);
|
|
|
|
|
else if (args[0] == "[VDP]")
|
|
|
|
|
Vdp.LoadStateText(reader);
|
|
|
|
|
else
|
|
|
|
|
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] SaveStateBinary()
|
|
|
|
|
{
|
2012-12-14 16:59:23 +00:00
|
|
|
|
var buf = new byte[24802 + 1 + 16384 + 16384];
|
2011-06-27 01:24:26 +00:00
|
|
|
|
var stream = new MemoryStream(buf);
|
|
|
|
|
var writer = new BinaryWriter(stream);
|
|
|
|
|
SaveStateBinary(writer);
|
2012-12-14 16:59:23 +00:00
|
|
|
|
if (stream.Length != buf.Length)
|
|
|
|
|
throw new Exception(string.Format("savestate buffer underrun: {0} < {1}", stream.Length, buf.Length));
|
2011-06-27 01:24:26 +00:00
|
|
|
|
writer.Close();
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-06 20:51:28 +00:00
|
|
|
|
public bool BinarySaveStatesPreferred { get { return false; } }
|
|
|
|
|
|
2011-06-27 01:24:26 +00:00
|
|
|
|
public void SaveStateBinary(BinaryWriter writer)
|
|
|
|
|
{
|
|
|
|
|
Cpu.SaveStateBinary(writer);
|
|
|
|
|
PSG.SaveStateBinary(writer);
|
|
|
|
|
Vdp.SaveStateBinary(writer);
|
|
|
|
|
|
|
|
|
|
writer.Write(Frame);
|
|
|
|
|
writer.Write(_lagcount);
|
2012-11-22 01:01:39 +00:00
|
|
|
|
writer.Write(islag);
|
2011-06-27 01:24:26 +00:00
|
|
|
|
writer.Write(RomBank0);
|
|
|
|
|
writer.Write(RomBank1);
|
|
|
|
|
writer.Write(RomBank2);
|
|
|
|
|
writer.Write(SystemRam);
|
|
|
|
|
writer.Write(SaveRAM);
|
|
|
|
|
writer.Write(Port01);
|
|
|
|
|
writer.Write(Port02);
|
|
|
|
|
writer.Write(Port3F);
|
|
|
|
|
writer.Write(YM2413.opll.reg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void LoadStateBinary(BinaryReader reader)
|
|
|
|
|
{
|
|
|
|
|
Cpu.LoadStateBinary(reader);
|
|
|
|
|
PSG.LoadStateBinary(reader);
|
|
|
|
|
Vdp.LoadStateBinary(reader);
|
|
|
|
|
|
|
|
|
|
Frame = reader.ReadInt32();
|
|
|
|
|
_lagcount = reader.ReadInt32();
|
2012-11-22 01:01:39 +00:00
|
|
|
|
islag = reader.ReadBoolean();
|
2011-06-27 01:24:26 +00:00
|
|
|
|
RomBank0 = reader.ReadByte();
|
|
|
|
|
RomBank1 = reader.ReadByte();
|
|
|
|
|
RomBank2 = reader.ReadByte();
|
|
|
|
|
SystemRam = reader.ReadBytes(SystemRam.Length);
|
|
|
|
|
reader.Read(SaveRAM, 0, SaveRAM.Length);
|
|
|
|
|
Port01 = reader.ReadByte();
|
|
|
|
|
Port02 = reader.ReadByte();
|
|
|
|
|
Port3F = reader.ReadByte();
|
|
|
|
|
if (HasYM2413)
|
|
|
|
|
{
|
|
|
|
|
byte[] regs = new byte[YM2413.opll.reg.Length];
|
|
|
|
|
reader.Read(regs, 0, regs.Length);
|
|
|
|
|
for (byte i = 0; i < regs.Length; i++)
|
|
|
|
|
YM2413.Write(i, regs[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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; } }
|
|
|
|
|
|
2011-09-05 17:45:01 +00:00
|
|
|
|
string region = "Export";
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-05 17:45:01 +00:00
|
|
|
|
readonly string[] validRegions = { "Export", "Japan" };
|
2011-06-27 01:24:26 +00:00
|
|
|
|
|
2011-09-05 17:45:01 +00:00
|
|
|
|
IList<MemoryDomain> 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);
|
|
|
|
|
var MainMemoryDomain = new MemoryDomain("Main RAM", SystemRam.Length, Endian.Little,
|
|
|
|
|
addr => SystemRam[addr & RamSizeMask],
|
|
|
|
|
(addr, value) => SystemRam[addr & RamSizeMask] = value);
|
|
|
|
|
var VRamDomain = new MemoryDomain("Video RAM", Vdp.VRAM.Length, Endian.Little,
|
|
|
|
|
addr => Vdp.VRAM[addr & 0x3FFF],
|
|
|
|
|
(addr, value) => Vdp.VRAM[addr & 0x3FFF] = value);
|
|
|
|
|
var SaveRamDomain = new MemoryDomain("Save RAM", SaveRAM.Length, Endian.Little,
|
|
|
|
|
addr => SaveRAM[addr % SaveRAM.Length],
|
|
|
|
|
(addr, value) => { SaveRAM[addr % SaveRAM.Length] = value; SaveRamModified = true; });
|
|
|
|
|
var SystemBusDomain = new MemoryDomain("System Bus", 0x10000, Endian.Little,
|
|
|
|
|
addr => Cpu.ReadMemory((ushort)addr),
|
|
|
|
|
(addr, value) => Cpu.WriteMemory((ushort)addr, value));
|
|
|
|
|
|
|
|
|
|
domains.Add(MainMemoryDomain);
|
|
|
|
|
domains.Add(VRamDomain);
|
|
|
|
|
domains.Add(SaveRamDomain);
|
|
|
|
|
domains.Add(SystemBusDomain);
|
|
|
|
|
memoryDomains = domains.AsReadOnly();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IList<MemoryDomain> MemoryDomains { get { return memoryDomains; } }
|
|
|
|
|
public MemoryDomain MainMemory { get { return memoryDomains[0]; } }
|
|
|
|
|
|
|
|
|
|
public void Dispose() { }
|
|
|
|
|
}
|
2012-03-11 16:15:20 +00:00
|
|
|
|
}
|