2012-03-25 01:33:05 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
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;
|
2013-11-14 15:01:32 +00:00
|
|
|
|
using BizHawk.Emulation.Common.Components.Z80;
|
2012-03-25 01:33:05 +00:00
|
|
|
|
|
2013-11-13 03:32:25 +00:00
|
|
|
|
namespace BizHawk.Emulation.Cores.ColecoVision
|
2012-03-25 01:33:05 +00:00
|
|
|
|
{
|
2012-11-17 17:39:33 +00:00
|
|
|
|
public sealed partial class ColecoVision : IEmulator
|
2012-03-25 01:33:05 +00:00
|
|
|
|
{
|
2012-11-17 21:12:51 +00:00
|
|
|
|
// ROM
|
|
|
|
|
public byte[] RomData;
|
|
|
|
|
public int RomLength;
|
2012-11-17 17:39:33 +00:00
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
public byte[] BiosRom;
|
2012-11-17 17:39:33 +00:00
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
// Machine
|
|
|
|
|
public Z80A Cpu;
|
|
|
|
|
public TMS9918A VDP;
|
|
|
|
|
public SN76489 PSG;
|
|
|
|
|
public byte[] Ram = new byte[1024];
|
2012-05-06 02:48:39 +00:00
|
|
|
|
|
2012-12-10 00:43:43 +00:00
|
|
|
|
public ColecoVision(CoreComm comm, GameInfo game, byte[] rom, string biosPath, bool skipbios)
|
2012-03-25 01:33:05 +00:00
|
|
|
|
{
|
2012-12-10 00:43:43 +00:00
|
|
|
|
CoreComm = comm;
|
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
Cpu = new Z80A();
|
|
|
|
|
Cpu.ReadMemory = ReadMemory;
|
|
|
|
|
Cpu.WriteMemory = WriteMemory;
|
|
|
|
|
Cpu.ReadHardware = ReadPort;
|
|
|
|
|
Cpu.WriteHardware = WritePort;
|
2012-11-17 17:39:33 +00:00
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
VDP = new TMS9918A(Cpu);
|
|
|
|
|
PSG = new SN76489();
|
2012-11-17 17:39:33 +00:00
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
// TODO: hack to allow bios-less operation would be nice, no idea if its feasible
|
2012-11-17 21:28:09 +00:00
|
|
|
|
BiosRom = File.ReadAllBytes(biosPath);
|
2012-05-06 02:48:39 +00:00
|
|
|
|
|
2012-12-10 00:43:43 +00:00
|
|
|
|
if (game["NoSkip"])
|
|
|
|
|
skipbios = false;
|
2012-11-22 02:01:15 +00:00
|
|
|
|
LoadRom(rom, skipbios);
|
2012-11-17 21:12:51 +00:00
|
|
|
|
this.game = game;
|
2012-11-20 00:35:22 +00:00
|
|
|
|
SetupMemoryDomains();
|
2012-03-25 01:33:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-06 02:15:29 +00:00
|
|
|
|
public MemoryDomainList MemoryDomains { get { return memoryDomains; } }
|
|
|
|
|
MemoryDomainList memoryDomains;
|
2012-11-20 00:35:22 +00:00
|
|
|
|
const ushort RamSizeMask = 0x03FF;
|
|
|
|
|
void SetupMemoryDomains()
|
|
|
|
|
{
|
|
|
|
|
var domains = new List<MemoryDomain>(3);
|
2013-11-04 02:11:40 +00:00
|
|
|
|
var MainMemoryDomain = new MemoryDomain("Main RAM", Ram.Length, MemoryDomain.Endian.Little,
|
2012-11-20 00:35:22 +00:00
|
|
|
|
addr => Ram[addr & RamSizeMask],
|
|
|
|
|
(addr, value) => Ram[addr & RamSizeMask] = value);
|
2013-11-04 02:11:40 +00:00
|
|
|
|
var VRamDomain = new MemoryDomain("Video RAM", VDP.VRAM.Length, MemoryDomain.Endian.Little,
|
2012-11-20 00:35:22 +00:00
|
|
|
|
addr => VDP.VRAM[addr & 0x3FFF],
|
|
|
|
|
(addr, value) => VDP.VRAM[addr & 0x3FFF] = value);
|
2013-11-04 02:11:40 +00:00
|
|
|
|
var SystemBusDomain = new MemoryDomain("System Bus", 0x10000, MemoryDomain.Endian.Little,
|
2012-11-20 00:35:22 +00:00
|
|
|
|
addr => Cpu.ReadMemory((ushort)addr),
|
|
|
|
|
(addr, value) => Cpu.WriteMemory((ushort)addr, value));
|
|
|
|
|
|
|
|
|
|
domains.Add(MainMemoryDomain);
|
|
|
|
|
domains.Add(VRamDomain);
|
|
|
|
|
domains.Add(SystemBusDomain);
|
2013-11-06 02:15:29 +00:00
|
|
|
|
memoryDomains = new MemoryDomainList(domains);
|
2012-11-20 00:35:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
public void FrameAdvance(bool render, bool renderSound)
|
|
|
|
|
{
|
2012-11-18 05:22:13 +00:00
|
|
|
|
Frame++;
|
2012-11-24 14:17:56 +00:00
|
|
|
|
islag = true;
|
2012-11-17 21:12:51 +00:00
|
|
|
|
PSG.BeginFrame(Cpu.TotalExecutedCycles);
|
|
|
|
|
VDP.ExecuteFrame();
|
|
|
|
|
PSG.EndFrame(Cpu.TotalExecutedCycles);
|
2012-11-18 05:22:13 +00:00
|
|
|
|
|
2012-11-24 14:17:56 +00:00
|
|
|
|
if (islag)
|
2012-11-18 05:22:13 +00:00
|
|
|
|
LagCount++;
|
2012-11-17 21:12:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-11 03:20:33 +00:00
|
|
|
|
public List<KeyValuePair<string, int>> GetCpuFlagsAndRegisters()
|
|
|
|
|
{
|
2013-11-11 16:45:45 +00:00
|
|
|
|
return new List<KeyValuePair<string, int>>
|
|
|
|
|
{
|
|
|
|
|
new KeyValuePair<string, int>("A", Cpu.RegisterA),
|
|
|
|
|
new KeyValuePair<string, int>("AF", Cpu.RegisterAF),
|
|
|
|
|
new KeyValuePair<string, int>("B", Cpu.RegisterB),
|
|
|
|
|
new KeyValuePair<string, int>("BC", Cpu.RegisterBC),
|
|
|
|
|
new KeyValuePair<string, int>("C", Cpu.RegisterC),
|
|
|
|
|
new KeyValuePair<string, int>("D", Cpu.RegisterD),
|
|
|
|
|
new KeyValuePair<string, int>("DE", Cpu.RegisterDE),
|
|
|
|
|
new KeyValuePair<string, int>("E", Cpu.RegisterE),
|
|
|
|
|
new KeyValuePair<string, int>("F", Cpu.RegisterF),
|
|
|
|
|
new KeyValuePair<string, int>("H", Cpu.RegisterH),
|
|
|
|
|
new KeyValuePair<string, int>("HL", Cpu.RegisterHL),
|
|
|
|
|
new KeyValuePair<string, int>("I", Cpu.RegisterI),
|
|
|
|
|
new KeyValuePair<string, int>("IX", Cpu.RegisterIX),
|
|
|
|
|
new KeyValuePair<string, int>("IY", Cpu.RegisterIY),
|
|
|
|
|
new KeyValuePair<string, int>("L", Cpu.RegisterL),
|
|
|
|
|
new KeyValuePair<string, int>("PC", Cpu.RegisterPC),
|
|
|
|
|
new KeyValuePair<string, int>("R", Cpu.RegisterR),
|
|
|
|
|
new KeyValuePair<string, int>("Shadow AF", Cpu.RegisterShadowAF),
|
|
|
|
|
new KeyValuePair<string, int>("Shadow BC", Cpu.RegisterShadowBC),
|
|
|
|
|
new KeyValuePair<string, int>("Shadow DE", Cpu.RegisterShadowDE),
|
|
|
|
|
new KeyValuePair<string, int>("Shadow HL", Cpu.RegisterShadowHL),
|
|
|
|
|
new KeyValuePair<string, int>("SP", Cpu.RegisterSP),
|
|
|
|
|
new KeyValuePair<string, int>("Flag C", Cpu.RegisterF.Bit(0) ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag N", Cpu.RegisterF.Bit(1) ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag P/V", Cpu.RegisterF.Bit(2) ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag 3rd", Cpu.RegisterF.Bit(3) ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag H", Cpu.RegisterF.Bit(4) ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag 5th", Cpu.RegisterF.Bit(5) ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag Z", Cpu.RegisterF.Bit(6) ? 1 : 0),
|
|
|
|
|
new KeyValuePair<string, int>("Flag S", Cpu.RegisterF.Bit(7) ? 1 : 0),
|
|
|
|
|
};
|
2013-11-11 03:20:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-11 16:45:45 +00:00
|
|
|
|
void LoadRom(byte[] rom, bool skipbios)
|
|
|
|
|
{
|
|
|
|
|
RomData = new byte[0x8000];
|
|
|
|
|
for (int i = 0; i < 0x8000; i++)
|
|
|
|
|
RomData[i] = rom[i % rom.Length];
|
2012-11-18 00:40:22 +00:00
|
|
|
|
|
2012-11-22 02:01:15 +00:00
|
|
|
|
// hack to skip colecovision title screen
|
|
|
|
|
if (skipbios)
|
|
|
|
|
{
|
|
|
|
|
RomData[0] = 0x55;
|
|
|
|
|
RomData[1] = 0xAA;
|
|
|
|
|
}
|
2013-11-11 16:45:45 +00:00
|
|
|
|
}
|
2012-11-17 21:12:51 +00:00
|
|
|
|
|
|
|
|
|
byte ReadPort(ushort port)
|
|
|
|
|
{
|
|
|
|
|
port &= 0xFF;
|
|
|
|
|
|
|
|
|
|
if (port >= 0xA0 && port < 0xC0)
|
|
|
|
|
{
|
|
|
|
|
if ((port & 1) == 0)
|
|
|
|
|
return VDP.ReadData();
|
|
|
|
|
return VDP.ReadVdpStatus();
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-11 16:45:45 +00:00
|
|
|
|
if (port >= 0xE0)
|
|
|
|
|
{
|
|
|
|
|
if ((port & 1) == 0)
|
|
|
|
|
return ReadController1();
|
|
|
|
|
return ReadController2();
|
|
|
|
|
}
|
2012-11-18 00:40:22 +00:00
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
return 0xFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WritePort(ushort port, byte value)
|
|
|
|
|
{
|
|
|
|
|
port &= 0xFF;
|
|
|
|
|
|
2013-11-11 16:45:45 +00:00
|
|
|
|
if (port >= 0xA0 && port <= 0xBF)
|
2012-11-17 21:12:51 +00:00
|
|
|
|
{
|
|
|
|
|
if ((port & 1) == 0)
|
|
|
|
|
VDP.WriteVdpData(value);
|
|
|
|
|
else
|
|
|
|
|
VDP.WriteVdpControl(value);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-11 16:45:45 +00:00
|
|
|
|
if (port >= 0x80 && port <= 0x9F)
|
|
|
|
|
{
|
|
|
|
|
InputPortSelection = InputPortMode.Right;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (port >= 0xC0 && port <= 0xDF)
|
|
|
|
|
{
|
|
|
|
|
InputPortSelection = InputPortMode.Left;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (port >= 0xE0)
|
|
|
|
|
{
|
|
|
|
|
PSG.WritePsgData(value, Cpu.TotalExecutedCycles);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2012-11-17 21:12:51 +00:00
|
|
|
|
}
|
2012-03-25 01:33:05 +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-25 01:33:05 +00:00
|
|
|
|
public bool SaveRamModified { get; set; }
|
2012-09-14 22:28:38 +00:00
|
|
|
|
|
2012-10-03 15:31:04 +00:00
|
|
|
|
public bool DeterministicEmulation { get { return true; } }
|
2013-11-11 16:45:45 +00:00
|
|
|
|
|
2012-11-20 01:01:51 +00:00
|
|
|
|
public void SaveStateText(TextWriter writer)
|
|
|
|
|
{
|
|
|
|
|
writer.WriteLine("[Coleco]\n");
|
|
|
|
|
Cpu.SaveStateText(writer);
|
|
|
|
|
PSG.SaveStateText(writer);
|
2012-11-22 00:49:10 +00:00
|
|
|
|
VDP.SaveStateText(writer);
|
2012-11-20 01:01:51 +00:00
|
|
|
|
|
|
|
|
|
writer.WriteLine("Frame {0}", Frame);
|
|
|
|
|
writer.WriteLine("Lag {0}", _lagcount);
|
2012-11-24 14:17:56 +00:00
|
|
|
|
writer.WriteLine("islag {0}", islag);
|
2012-11-20 01:01:51 +00:00
|
|
|
|
writer.Write("RAM ");
|
|
|
|
|
Ram.SaveAsHex(writer);
|
|
|
|
|
writer.WriteLine("[/Coleco]");
|
|
|
|
|
}
|
2013-11-11 16:45:45 +00:00
|
|
|
|
|
2012-11-20 01:01:51 +00:00
|
|
|
|
public void LoadStateText(TextReader reader)
|
|
|
|
|
{
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
string[] args = reader.ReadLine().Split(' ');
|
|
|
|
|
if (args[0].Trim() == "") continue;
|
|
|
|
|
if (args[0] == "[Coleco]") continue;
|
|
|
|
|
if (args[0] == "[/Coleco]") break;
|
|
|
|
|
else if (args[0] == "Frame")
|
|
|
|
|
Frame = int.Parse(args[1]);
|
|
|
|
|
else if (args[0] == "Lag")
|
|
|
|
|
_lagcount = int.Parse(args[1]);
|
2012-11-24 14:17:56 +00:00
|
|
|
|
else if (args[0] == "islag")
|
|
|
|
|
islag = bool.Parse(args[1]);
|
2012-11-20 01:01:51 +00:00
|
|
|
|
else if (args[0] == "RAM")
|
|
|
|
|
Ram.ReadFromHex(args[1]);
|
|
|
|
|
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]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-25 01:33:05 +00:00
|
|
|
|
public byte[] SaveStateBinary()
|
|
|
|
|
{
|
2012-11-22 00:57:26 +00:00
|
|
|
|
var buf = new byte[24802 + 16384 + 16384];
|
|
|
|
|
var stream = new MemoryStream(buf);
|
|
|
|
|
var writer = new BinaryWriter(stream);
|
|
|
|
|
SaveStateBinary(writer);
|
|
|
|
|
writer.Close();
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-06 20:51:28 +00:00
|
|
|
|
public bool BinarySaveStatesPreferred { get { return false; } }
|
|
|
|
|
|
2012-11-22 00:57: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-24 14:17:56 +00:00
|
|
|
|
writer.Write(islag);
|
2012-11-22 00:57:26 +00:00
|
|
|
|
writer.Write(Ram);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void LoadStateBinary(BinaryReader reader)
|
|
|
|
|
{
|
|
|
|
|
Cpu.LoadStateBinary(reader);
|
|
|
|
|
PSG.LoadStateBinary(reader);
|
|
|
|
|
VDP.LoadStateBinary(reader);
|
|
|
|
|
|
|
|
|
|
Frame = reader.ReadInt32();
|
|
|
|
|
_lagcount = reader.ReadInt32();
|
2012-11-24 14:17:56 +00:00
|
|
|
|
islag = reader.ReadBoolean();
|
2012-11-22 00:57:26 +00:00
|
|
|
|
Ram = reader.ReadBytes(Ram.Length);
|
2012-03-25 01:33:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
public void Dispose() { }
|
2013-11-03 16:29:51 +00:00
|
|
|
|
public void ResetCounters()
|
2012-11-25 15:41:40 +00:00
|
|
|
|
{
|
|
|
|
|
Frame = 0;
|
|
|
|
|
_lagcount = 0;
|
|
|
|
|
islag = false;
|
|
|
|
|
}
|
2012-11-17 17:39:33 +00:00
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
public string SystemId { get { return "Coleco"; } }
|
|
|
|
|
public GameInfo game;
|
2012-12-10 00:43:43 +00:00
|
|
|
|
public CoreComm CoreComm { get; private set; }
|
2012-11-17 21:12:51 +00:00
|
|
|
|
public IVideoProvider VideoProvider { get { return VDP; } }
|
|
|
|
|
public ISoundProvider SoundProvider { get { return PSG; } }
|
2012-11-17 17:39:33 +00:00
|
|
|
|
|
2013-08-24 16:54:22 +00:00
|
|
|
|
public string BoardName { get { return null; } }
|
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
public ISyncSoundProvider SyncSoundProvider { get { return null; } }
|
|
|
|
|
public bool StartAsyncSound() { return true; }
|
|
|
|
|
public void EndAsyncSound() { }
|
2012-03-25 01:33:05 +00:00
|
|
|
|
}
|
2012-11-23 05:51:16 +00:00
|
|
|
|
}
|