2012-03-07 00:40:20 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
2013-11-04 00:36:15 +00:00
|
|
|
|
using BizHawk.Common;
|
2013-11-04 01:06:36 +00:00
|
|
|
|
using BizHawk.Emulation.Common;
|
2013-12-24 20:50:53 +00:00
|
|
|
|
using System.ComponentModel;
|
2013-11-04 00:36:15 +00:00
|
|
|
|
|
2013-11-13 03:32:25 +00:00
|
|
|
|
namespace BizHawk.Emulation.Cores.Atari.Atari2600
|
2012-03-07 00:40:20 +00:00
|
|
|
|
{
|
2012-04-02 20:55:29 +00:00
|
|
|
|
public partial class Atari2600 : IEmulator
|
2012-03-07 00:40:20 +00:00
|
|
|
|
{
|
|
|
|
|
public string SystemId { get { return "A26"; } }
|
2012-03-30 00:35:15 +00:00
|
|
|
|
public GameInfo game;
|
2012-03-07 00:40:20 +00:00
|
|
|
|
|
2013-08-24 16:54:22 +00:00
|
|
|
|
public string BoardName { get { return mapper.GetType().Name; } }
|
|
|
|
|
|
2012-12-10 00:43:43 +00:00
|
|
|
|
public CoreComm CoreComm { get; private set; }
|
2012-04-02 20:55:29 +00:00
|
|
|
|
public IVideoProvider VideoProvider { get { return tia; } }
|
2012-10-10 00:56:48 +00:00
|
|
|
|
public ISoundProvider SoundProvider { get { return dcfilter; } }
|
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(dcfilter, 735); } }
|
|
|
|
|
public bool StartAsyncSound() { return true; }
|
|
|
|
|
public void EndAsyncSound() { }
|
2012-03-30 00:35:15 +00:00
|
|
|
|
|
2013-12-24 20:50:53 +00:00
|
|
|
|
public Atari2600(CoreComm comm, GameInfo game, byte[] rom, object Settings, object SyncSettings)
|
2012-03-07 00:40:20 +00:00
|
|
|
|
{
|
2012-12-10 00:43:43 +00:00
|
|
|
|
CoreComm = comm;
|
2013-12-24 20:50:53 +00:00
|
|
|
|
this.Settings = (A2600Settings)Settings ?? A2600Settings.GetDefaults();
|
|
|
|
|
this.SyncSettings = (A2600SyncSettings)SyncSettings ?? A2600SyncSettings.GetDefaults();
|
|
|
|
|
|
2013-04-16 00:42:57 +00:00
|
|
|
|
var domains = new List<MemoryDomain>(1)
|
|
|
|
|
{
|
2014-02-26 20:18:48 +00:00
|
|
|
|
new MemoryDomain("Main RAM", 128, MemoryDomain.Endian.Little, addr => ram[addr], (addr, value) => ram[addr] = value),
|
2013-11-04 02:11:40 +00:00
|
|
|
|
new MemoryDomain("TIA", 16, MemoryDomain.Endian.Little, addr => tia.ReadMemory((ushort) addr, true),
|
2013-04-16 00:42:57 +00:00
|
|
|
|
(addr, value) => tia.WriteMemory((ushort) addr, value)),
|
2013-11-04 02:11:40 +00:00
|
|
|
|
new MemoryDomain("PIA", 1024, MemoryDomain.Endian.Little, addr => m6532.ReadMemory((ushort) addr, true),
|
2013-04-16 00:42:57 +00:00
|
|
|
|
(addr, value) => m6532.WriteMemory((ushort) addr, value)),
|
2013-11-04 02:11:40 +00:00
|
|
|
|
new MemoryDomain("System Bus", 8192, MemoryDomain.Endian.Little, addr => mapper.PeekMemory((ushort) addr), (addr, value) => { })
|
2013-04-16 00:42:57 +00:00
|
|
|
|
};
|
2013-11-06 02:15:29 +00:00
|
|
|
|
memoryDomains = new MemoryDomainList(domains);
|
2012-12-10 00:43:43 +00:00
|
|
|
|
CoreComm.CpuTraceAvailable = true;
|
2012-03-07 00:40:20 +00:00
|
|
|
|
this.rom = rom;
|
2012-03-30 00:35:15 +00:00
|
|
|
|
this.game = game;
|
2014-02-08 04:59:45 +00:00
|
|
|
|
|
|
|
|
|
if (!game.GetOptionsDict().ContainsKey("m"))
|
2014-04-02 21:07:55 +00:00
|
|
|
|
{
|
2014-02-08 04:59:45 +00:00
|
|
|
|
DetectMapper();
|
2014-04-02 21:07:55 +00:00
|
|
|
|
}
|
2014-02-08 04:59:45 +00:00
|
|
|
|
|
2012-03-30 00:35:15 +00:00
|
|
|
|
Console.WriteLine("Game uses mapper " + game.GetOptionsDict()["m"]);
|
2012-03-07 00:40:20 +00:00
|
|
|
|
HardReset();
|
|
|
|
|
}
|
2013-11-11 03:20:33 +00:00
|
|
|
|
|
2014-02-08 04:59:45 +00:00
|
|
|
|
void DetectMapper()
|
|
|
|
|
{
|
|
|
|
|
string m = "UNKNOWN";
|
|
|
|
|
switch (rom.Length)
|
|
|
|
|
{
|
|
|
|
|
case 2048: m = "2K"; break;
|
|
|
|
|
case 4096: m = "4K"; break;
|
2014-04-02 21:07:55 +00:00
|
|
|
|
case 8192: m = "F8"; break;
|
|
|
|
|
case 16384: m = "F6"; break;
|
|
|
|
|
case 12288: m = "FA"; break;
|
|
|
|
|
case 32768: m = "F4"; break;
|
|
|
|
|
case 65536: m = "EF"; break;
|
|
|
|
|
case 131072: m = "MC"; break;
|
|
|
|
|
case 262144: m = "3F"; break;
|
|
|
|
|
case 524288: m = "3F"; break;
|
2014-02-08 04:59:45 +00:00
|
|
|
|
}
|
|
|
|
|
game.AddOption("m", m);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-11-11 03:20:33 +00:00
|
|
|
|
public List<KeyValuePair<string, int>> GetCpuFlagsAndRegisters()
|
|
|
|
|
{
|
2013-11-11 03:44:37 +00:00
|
|
|
|
return new List<KeyValuePair<string, int>>
|
|
|
|
|
{
|
|
|
|
|
new KeyValuePair<string, int>("A", cpu.A),
|
|
|
|
|
new KeyValuePair<string, int>("X", cpu.X),
|
|
|
|
|
new KeyValuePair<string, int>("Y", cpu.Y),
|
|
|
|
|
new KeyValuePair<string, int>("S", cpu.S),
|
|
|
|
|
new KeyValuePair<string, int>("PC", cpu.PC),
|
|
|
|
|
new KeyValuePair<string, int>("Flag C", cpu.FlagC ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag Z", cpu.FlagZ ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag I", cpu.FlagI ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag D", cpu.FlagD ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag B", cpu.FlagB ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag V", cpu.FlagV ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag N", cpu.FlagN ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag T", cpu.FlagT ? 1 : 0)
|
|
|
|
|
};
|
2013-11-11 03:20:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-03 16:29:51 +00:00
|
|
|
|
public void ResetCounters()
|
2012-03-07 00:40:20 +00:00
|
|
|
|
{
|
2012-03-23 02:55:46 +00:00
|
|
|
|
_frame = 0;
|
2012-11-25 15:41:40 +00:00
|
|
|
|
_lagcount = 0;
|
|
|
|
|
_islag = false;
|
2012-03-07 00:40:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static readonly ControllerDefinition Atari2600ControllerDefinition = new ControllerDefinition
|
|
|
|
|
{
|
|
|
|
|
Name = "Atari 2600 Basic Controller",
|
|
|
|
|
BoolButtons =
|
|
|
|
|
{
|
2012-03-22 06:33:28 +00:00
|
|
|
|
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button",
|
|
|
|
|
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Button",
|
2012-03-30 16:40:52 +00:00
|
|
|
|
"Reset", "Select"
|
2012-03-07 00:40:20 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void SyncState(Serializer ser)
|
|
|
|
|
{
|
2012-11-28 18:27:14 +00:00
|
|
|
|
ser.BeginSection("A2600");
|
2012-03-07 00:40:20 +00:00
|
|
|
|
cpu.SyncState(ser);
|
|
|
|
|
ser.Sync("ram", ref ram, false);
|
2012-03-23 02:55:46 +00:00
|
|
|
|
ser.Sync("Lag", ref _lagcount);
|
|
|
|
|
ser.Sync("Frame", ref _frame);
|
2012-07-30 14:42:52 +00:00
|
|
|
|
ser.Sync("IsLag", ref _islag);
|
2012-03-30 02:34:47 +00:00
|
|
|
|
tia.SyncState(ser);
|
|
|
|
|
m6532.SyncState(ser);
|
2012-12-18 20:37:31 +00:00
|
|
|
|
ser.BeginSection("Mapper");
|
2012-03-30 00:35:15 +00:00
|
|
|
|
mapper.SyncState(ser);
|
2012-11-28 18:27:14 +00:00
|
|
|
|
ser.EndSection();
|
2012-12-18 20:37:31 +00:00
|
|
|
|
ser.EndSection();
|
2012-03-07 00:40:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ControllerDefinition ControllerDefinition { get { return Atari2600ControllerDefinition; } }
|
|
|
|
|
public IController Controller { get; set; }
|
|
|
|
|
|
2012-03-23 02:55:46 +00:00
|
|
|
|
public int Frame { get { return _frame; } set { _frame = value; } }
|
|
|
|
|
public int LagCount { get { return _lagcount; } set { _lagcount = value; } }
|
|
|
|
|
public bool IsLagFrame { get { return _islag; } }
|
|
|
|
|
private bool _islag = true;
|
2013-04-16 00:42:57 +00:00
|
|
|
|
private int _lagcount;
|
|
|
|
|
private int _frame;
|
2012-03-07 00:40:20 +00:00
|
|
|
|
|
2012-09-14 22:28:38 +00:00
|
|
|
|
public byte[] ReadSaveRam() { return null; }
|
|
|
|
|
public void StoreSaveRam(byte[] data) { }
|
|
|
|
|
public void ClearSaveRam() { }
|
2012-03-07 00:40:20 +00:00
|
|
|
|
public bool SaveRamModified { get; set; }
|
2012-09-14 22:28:38 +00:00
|
|
|
|
|
|
|
|
|
public bool DeterministicEmulation { get; set; }
|
2012-03-07 00:40:20 +00:00
|
|
|
|
public void SaveStateText(TextWriter writer) { SyncState(Serializer.CreateTextWriter(writer)); }
|
|
|
|
|
public void LoadStateText(TextReader reader) { SyncState(Serializer.CreateTextReader(reader)); }
|
|
|
|
|
public void SaveStateBinary(BinaryWriter bw) { SyncState(Serializer.CreateBinaryWriter(bw)); }
|
|
|
|
|
public void LoadStateBinary(BinaryReader br) { SyncState(Serializer.CreateBinaryReader(br)); }
|
|
|
|
|
|
|
|
|
|
public byte[] SaveStateBinary()
|
|
|
|
|
{
|
|
|
|
|
MemoryStream ms = new MemoryStream();
|
|
|
|
|
BinaryWriter bw = new BinaryWriter(ms);
|
|
|
|
|
SaveStateBinary(bw);
|
|
|
|
|
bw.Flush();
|
|
|
|
|
return ms.ToArray();
|
|
|
|
|
}
|
2012-09-14 22:28:38 +00:00
|
|
|
|
|
2013-05-06 20:51:28 +00:00
|
|
|
|
public bool BinarySaveStatesPreferred { get { return false; } }
|
|
|
|
|
|
2013-11-06 02:15:29 +00:00
|
|
|
|
private readonly MemoryDomainList memoryDomains;
|
|
|
|
|
public MemoryDomainList MemoryDomains { get { return memoryDomains; } }
|
2012-03-07 00:40:20 +00:00
|
|
|
|
public void Dispose() { }
|
2013-12-22 00:44:39 +00:00
|
|
|
|
|
2013-12-24 20:50:53 +00:00
|
|
|
|
public object GetSettings() { return Settings.Clone(); }
|
|
|
|
|
public object GetSyncSettings() { return SyncSettings.Clone(); }
|
|
|
|
|
public bool PutSettings(object o) { Settings = (A2600Settings)o; return false; }
|
|
|
|
|
public bool PutSyncSettings(object o) { SyncSettings = (A2600SyncSettings)o; return false; }
|
|
|
|
|
|
|
|
|
|
public A2600Settings Settings { get; private set; }
|
|
|
|
|
public A2600SyncSettings SyncSettings { get; private set; }
|
|
|
|
|
|
|
|
|
|
public class A2600Settings
|
|
|
|
|
{
|
|
|
|
|
// todo: descriptions
|
|
|
|
|
public bool ShowBG { get; set; }
|
|
|
|
|
public bool ShowPlayer1 { get; set; }
|
|
|
|
|
public bool ShowPlayer2 { get; set; }
|
|
|
|
|
public bool ShowMissle1 { get; set; }
|
|
|
|
|
public bool ShowMissle2 { get; set; }
|
|
|
|
|
public bool ShowBall { get; set; }
|
|
|
|
|
public bool ShowPlayfield { get; set; }
|
|
|
|
|
|
|
|
|
|
public A2600Settings Clone()
|
|
|
|
|
{
|
|
|
|
|
return (A2600Settings)MemberwiseClone();
|
|
|
|
|
}
|
|
|
|
|
public static A2600Settings GetDefaults()
|
|
|
|
|
{
|
|
|
|
|
return new A2600Settings
|
|
|
|
|
{
|
|
|
|
|
ShowBG = true,
|
|
|
|
|
ShowPlayer1 = true,
|
|
|
|
|
ShowPlayer2 = true,
|
|
|
|
|
ShowMissle1 = true,
|
|
|
|
|
ShowMissle2 = true,
|
|
|
|
|
ShowBall = true,
|
|
|
|
|
ShowPlayfield = true
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class A2600SyncSettings
|
|
|
|
|
{
|
|
|
|
|
[Description("Set the TV Type switch on the console to B&W or Color")]
|
|
|
|
|
public bool BW { get; set; }
|
|
|
|
|
[Description("Set the Left Difficulty switch on the console")]
|
|
|
|
|
public bool LeftDifficulty { get; set; }
|
|
|
|
|
[Description("Set the Right Difficulty switch on the console")]
|
|
|
|
|
public bool RightDifficulty { get; set; }
|
|
|
|
|
|
|
|
|
|
public A2600SyncSettings Clone()
|
|
|
|
|
{
|
|
|
|
|
return (A2600SyncSettings)MemberwiseClone();
|
|
|
|
|
}
|
|
|
|
|
public static A2600SyncSettings GetDefaults()
|
|
|
|
|
{
|
|
|
|
|
return new A2600SyncSettings
|
|
|
|
|
{
|
|
|
|
|
BW = false,
|
|
|
|
|
LeftDifficulty = true,
|
|
|
|
|
RightDifficulty = true
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-03-07 00:40:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|