2012-03-25 01:33:05 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
2012-05-06 02:48:39 +00:00
|
|
|
|
using BizHawk.Emulation.CPUs.Z80;
|
|
|
|
|
using BizHawk.Emulation.Sound;
|
2012-03-25 01:33:05 +00:00
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Consoles.Coleco
|
|
|
|
|
{
|
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-11-22 02:01:15 +00:00
|
|
|
|
public ColecoVision(GameInfo game, byte[] rom, string biosPath, bool skipbios)
|
2012-03-25 01:33:05 +00:00
|
|
|
|
{
|
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-23 05:51:16 +00:00
|
|
|
|
Cpu.Logger = (s) => Log.Error("COL", s);
|
2012-11-17 21:12:51 +00:00
|
|
|
|
//Cpu.Debug = true;
|
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-03-25 01:33:05 +00:00
|
|
|
|
CoreOutputComm = new CoreOutputComm();
|
|
|
|
|
CoreInputComm = new CoreInputComm();
|
2012-11-23 05:51:16 +00:00
|
|
|
|
|
|
|
|
|
if (game["NoSkip"])
|
|
|
|
|
skipbios = false;
|
|
|
|
|
Console.WriteLine("skipbios = {0}", skipbios);
|
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
|
|
|
|
}
|
|
|
|
|
|
2012-11-20 00:35:22 +00:00
|
|
|
|
public IList<MemoryDomain> MemoryDomains { get { return memoryDomains; } }
|
|
|
|
|
public MemoryDomain MainMemory { get { return memoryDomains[0]; } }
|
|
|
|
|
IList<MemoryDomain> memoryDomains;
|
|
|
|
|
const ushort RamSizeMask = 0x03FF;
|
|
|
|
|
void SetupMemoryDomains()
|
|
|
|
|
{
|
|
|
|
|
var domains = new List<MemoryDomain>(3);
|
|
|
|
|
var MainMemoryDomain = new MemoryDomain("Main RAM", Ram.Length, Endian.Little,
|
|
|
|
|
addr => Ram[addr & RamSizeMask],
|
|
|
|
|
(addr, value) => Ram[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 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(SystemBusDomain);
|
|
|
|
|
memoryDomains = domains.AsReadOnly();
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
public void FrameAdvance(bool render, bool renderSound)
|
|
|
|
|
{
|
2012-11-18 05:22:13 +00:00
|
|
|
|
Frame++;
|
|
|
|
|
IsLagFrame = 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
|
|
|
|
|
|
|
|
|
if (IsLagFrame)
|
|
|
|
|
LagCount++;
|
2012-11-17 21:12:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-22 02:01:15 +00:00
|
|
|
|
void LoadRom(byte[] rom, bool skipbios)
|
2012-11-18 00:40:22 +00:00
|
|
|
|
{
|
|
|
|
|
RomData = new byte[0x8000];
|
|
|
|
|
for (int i = 0; i < 0x8000; i++)
|
|
|
|
|
RomData[i] = rom[i % rom.Length];
|
|
|
|
|
|
2012-11-22 02:01:15 +00:00
|
|
|
|
// hack to skip colecovision title screen
|
|
|
|
|
if (skipbios)
|
|
|
|
|
{
|
|
|
|
|
RomData[0] = 0x55;
|
|
|
|
|
RomData[1] = 0xAA;
|
|
|
|
|
}
|
2012-11-18 00:40:22 +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();
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-18 00:40:22 +00:00
|
|
|
|
if (port >= 0xE0)
|
|
|
|
|
{
|
|
|
|
|
if ((port & 1) == 0)
|
|
|
|
|
return ReadController1();
|
|
|
|
|
return ReadController2();
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
return 0xFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WritePort(ushort port, byte value)
|
|
|
|
|
{
|
|
|
|
|
port &= 0xFF;
|
|
|
|
|
|
2012-11-18 00:40:22 +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;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-18 00:40:22 +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; } }
|
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-22 00:57:26 +00:00
|
|
|
|
writer.WriteLine("IsLagFrame {0}", IsLagFrame);
|
2012-11-20 01:01:51 +00:00
|
|
|
|
writer.Write("RAM ");
|
|
|
|
|
Ram.SaveAsHex(writer);
|
|
|
|
|
writer.WriteLine("[/Coleco]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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-22 00:57:26 +00:00
|
|
|
|
else if (args[0] == "IsLagFrame")
|
|
|
|
|
IsLagFrame = 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SaveStateBinary(BinaryWriter writer)
|
|
|
|
|
{
|
|
|
|
|
Cpu.SaveStateBinary(writer);
|
|
|
|
|
PSG.SaveStateBinary(writer);
|
|
|
|
|
VDP.SaveStateBinary(writer);
|
|
|
|
|
|
|
|
|
|
writer.Write(Frame);
|
|
|
|
|
writer.Write(_lagcount);
|
|
|
|
|
writer.Write(IsLagFrame);
|
|
|
|
|
writer.Write(Ram);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void LoadStateBinary(BinaryReader reader)
|
|
|
|
|
{
|
|
|
|
|
Cpu.LoadStateBinary(reader);
|
|
|
|
|
PSG.LoadStateBinary(reader);
|
|
|
|
|
VDP.LoadStateBinary(reader);
|
|
|
|
|
|
|
|
|
|
Frame = reader.ReadInt32();
|
|
|
|
|
_lagcount = reader.ReadInt32();
|
|
|
|
|
IsLagFrame = reader.ReadBoolean();
|
|
|
|
|
Ram = reader.ReadBytes(Ram.Length);
|
2012-03-25 01:33:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
public void Dispose() { }
|
|
|
|
|
public void ResetFrameCounter() { }
|
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;
|
|
|
|
|
public CoreInputComm CoreInputComm { get; set; }
|
|
|
|
|
public CoreOutputComm CoreOutputComm { get; private set; }
|
|
|
|
|
public IVideoProvider VideoProvider { get { return VDP; } }
|
|
|
|
|
public ISoundProvider SoundProvider { get { return PSG; } }
|
2012-11-17 17:39:33 +00:00
|
|
|
|
|
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
|
|
|
|
}
|