2016-02-25 01:41:55 +00:00
|
|
|
|
using BizHawk.Emulation.Common;
|
2016-03-04 13:37:09 +00:00
|
|
|
|
using BizHawk.Emulation.Cores.Components;
|
2014-01-22 01:14:36 +00:00
|
|
|
|
using BizHawk.Emulation.Cores.Components.Z80;
|
2017-03-02 03:58:36 +00:00
|
|
|
|
using BizHawk.Common.NumberExtensions;
|
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
|
|
|
|
{
|
2014-04-25 01:19:57 +00:00
|
|
|
|
[CoreAttributes(
|
|
|
|
|
"ColecoHawk",
|
|
|
|
|
"Vecna",
|
|
|
|
|
isPorted: false,
|
2017-04-24 17:24:56 +00:00
|
|
|
|
isReleased: true)]
|
2014-12-12 01:58:12 +00:00
|
|
|
|
[ServiceNotApplicable(typeof(ISaveRam), typeof(IDriveLight))]
|
2017-03-01 02:44:05 +00:00
|
|
|
|
public sealed partial class ColecoVision : IEmulator, IDebuggable, IInputPollable, IStatable, ISettable<ColecoVision.ColecoSettings, ColecoVision.ColecoSyncSettings>
|
2012-03-25 01:33:05 +00:00
|
|
|
|
{
|
2014-08-23 19:06:37 +00:00
|
|
|
|
[CoreConstructor("Coleco")]
|
2017-05-06 00:05:36 +00:00
|
|
|
|
public ColecoVision(CoreComm comm, GameInfo game, byte[] rom, object syncSettings)
|
2012-03-25 01:33:05 +00:00
|
|
|
|
{
|
2014-12-04 03:38:30 +00:00
|
|
|
|
ServiceProvider = new BasicServiceProvider(this);
|
2015-01-24 21:08:20 +00:00
|
|
|
|
MemoryCallbacks = new MemoryCallbackSystem();
|
2012-12-10 00:43:43 +00:00
|
|
|
|
CoreComm = comm;
|
2017-05-06 00:05:36 +00:00
|
|
|
|
_syncSettings = (ColecoSyncSettings)syncSettings ?? new ColecoSyncSettings();
|
2016-02-25 01:41:55 +00:00
|
|
|
|
bool skipbios = _syncSettings.SkipBiosIntro;
|
2012-12-10 00:43:43 +00:00
|
|
|
|
|
2017-04-10 16:24:53 +00:00
|
|
|
|
Cpu = new Z80A
|
|
|
|
|
{
|
|
|
|
|
ReadMemory = ReadMemory,
|
|
|
|
|
WriteMemory = WriteMemory,
|
|
|
|
|
ReadHardware = ReadPort,
|
|
|
|
|
WriteHardware = WritePort,
|
|
|
|
|
MemoryCallbacks = MemoryCallbacks
|
|
|
|
|
};
|
|
|
|
|
|
2017-04-15 20:37:30 +00:00
|
|
|
|
PSG = new SN76489();
|
2016-12-11 17:33:33 +00:00
|
|
|
|
_fakeSyncSound = new FakeSyncSound(PSG, 735);
|
|
|
|
|
(ServiceProvider as BasicServiceProvider).Register<ISoundProvider>(_fakeSyncSound);
|
2012-11-17 17:39:33 +00:00
|
|
|
|
|
2017-04-10 16:24:53 +00:00
|
|
|
|
ControllerDeck = new ColecoVisionControllerDeck(_syncSettings.Port1, _syncSettings.Port2);
|
2017-03-01 02:44:05 +00:00
|
|
|
|
|
2017-03-02 03:58:36 +00:00
|
|
|
|
VDP = new TMS9918A(Cpu);
|
2017-03-01 02:44:05 +00:00
|
|
|
|
(ServiceProvider as BasicServiceProvider).Register<IVideoProvider>(VDP);
|
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
// TODO: hack to allow bios-less operation would be nice, no idea if its feasible
|
2015-03-05 01:17:43 +00:00
|
|
|
|
BiosRom = CoreComm.CoreFileProvider.GetFirmware("Coleco", "Bios", true, "Coleco BIOS file is required.");
|
2012-05-06 02:48:39 +00:00
|
|
|
|
|
2013-12-24 23:32:43 +00:00
|
|
|
|
// gamedb can overwrite the syncsettings; this is ok
|
2012-12-10 00:43:43 +00:00
|
|
|
|
if (game["NoSkip"])
|
2017-04-10 16:24:53 +00:00
|
|
|
|
{
|
2012-12-10 00:43:43 +00:00
|
|
|
|
skipbios = false;
|
2017-04-10 16:24:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-22 02:01:15 +00:00
|
|
|
|
LoadRom(rom, skipbios);
|
2017-04-10 16:24:53 +00:00
|
|
|
|
_game = game;
|
2012-11-20 00:35:22 +00:00
|
|
|
|
SetupMemoryDomains();
|
2016-02-25 01:41:55 +00:00
|
|
|
|
|
2016-02-28 13:07:02 +00:00
|
|
|
|
Tracer.Header = Cpu.TraceHeader;
|
2016-02-25 01:41:55 +00:00
|
|
|
|
var serviceProvider = ServiceProvider as BasicServiceProvider;
|
|
|
|
|
serviceProvider.Register<IDisassemblable>(new Disassembler());
|
|
|
|
|
serviceProvider.Register<ITraceable>(Tracer);
|
2012-03-25 01:33:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-06 00:05:36 +00:00
|
|
|
|
// ROM
|
|
|
|
|
private byte[] RomData;
|
|
|
|
|
private byte[] BiosRom;
|
|
|
|
|
|
|
|
|
|
// Machine
|
|
|
|
|
private Z80A Cpu;
|
|
|
|
|
private TMS9918A VDP;
|
|
|
|
|
|
|
|
|
|
private byte[] Ram = new byte[1024];
|
|
|
|
|
private readonly TraceBuffer Tracer = new TraceBuffer();
|
|
|
|
|
|
2017-04-10 16:24:53 +00:00
|
|
|
|
public IEmulatorServiceProvider ServiceProvider { get; }
|
2014-12-04 03:38:30 +00:00
|
|
|
|
|
2017-04-24 17:24:56 +00:00
|
|
|
|
public ControllerDefinition ControllerDefinition => ControllerDeck.Definition;
|
2017-04-10 16:24:53 +00:00
|
|
|
|
|
2017-04-24 17:24:56 +00:00
|
|
|
|
private readonly ColecoVisionControllerDeck ControllerDeck;
|
2017-04-10 16:24:53 +00:00
|
|
|
|
|
|
|
|
|
private const ushort RamSizeMask = 0x03FF;
|
2012-11-20 00:35:22 +00:00
|
|
|
|
|
2017-05-02 01:09:11 +00:00
|
|
|
|
private IController _controller;
|
|
|
|
|
|
|
|
|
|
public void FrameAdvance(IController controller, bool render, bool renderSound)
|
2012-11-17 21:12:51 +00:00
|
|
|
|
{
|
2017-05-02 01:09:11 +00:00
|
|
|
|
_controller = controller;
|
2016-02-25 01:41:55 +00:00
|
|
|
|
Cpu.Debug = Tracer.Enabled;
|
2012-11-18 05:22:13 +00:00
|
|
|
|
Frame++;
|
2014-12-05 03:16:08 +00:00
|
|
|
|
_isLag = true;
|
2012-11-17 21:12:51 +00:00
|
|
|
|
PSG.BeginFrame(Cpu.TotalExecutedCycles);
|
2016-02-25 01:41:55 +00:00
|
|
|
|
|
|
|
|
|
if (Cpu.Debug && Cpu.Logger == null) // TODO, lets not do this on each frame. But lets refactor CoreComm/CoreComm first
|
|
|
|
|
{
|
|
|
|
|
Cpu.Logger = (s) => Tracer.Put(s);
|
|
|
|
|
}
|
2017-03-02 03:58:36 +00:00
|
|
|
|
|
2017-05-02 01:09:11 +00:00
|
|
|
|
byte tempRet1 = ControllerDeck.ReadPort1(controller, true, true);
|
|
|
|
|
byte tempRet2 = ControllerDeck.ReadPort2(controller, true, true);
|
2017-03-23 01:26:51 +00:00
|
|
|
|
|
2017-04-24 17:24:56 +00:00
|
|
|
|
bool intPending = (!tempRet1.Bit(4)) | (!tempRet2.Bit(4));
|
2017-03-23 01:26:51 +00:00
|
|
|
|
|
2017-04-24 17:24:56 +00:00
|
|
|
|
VDP.ExecuteFrame(intPending);
|
2017-03-02 03:58:36 +00:00
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
PSG.EndFrame(Cpu.TotalExecutedCycles);
|
2012-11-18 05:22:13 +00:00
|
|
|
|
|
2014-12-05 03:16:08 +00:00
|
|
|
|
if (_isLag)
|
|
|
|
|
{
|
2015-06-11 01:23:14 +00:00
|
|
|
|
_lagCount++;
|
2014-12-05 03:16:08 +00:00
|
|
|
|
}
|
2012-11-17 21:12:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-10 16:24:53 +00:00
|
|
|
|
private void LoadRom(byte[] rom, bool skipbios)
|
2013-11-11 16:45:45 +00:00
|
|
|
|
{
|
|
|
|
|
RomData = new byte[0x8000];
|
|
|
|
|
for (int i = 0; i < 0x8000; i++)
|
2017-04-24 17:24:56 +00:00
|
|
|
|
{
|
2013-11-11 16:45:45 +00:00
|
|
|
|
RomData[i] = rom[i % rom.Length];
|
2017-04-24 17:24:56 +00:00
|
|
|
|
}
|
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
|
|
|
|
|
2017-04-10 16:24:53 +00:00
|
|
|
|
private byte ReadPort(ushort port)
|
2012-11-17 21:12:51 +00:00
|
|
|
|
{
|
|
|
|
|
port &= 0xFF;
|
|
|
|
|
|
|
|
|
|
if (port >= 0xA0 && port < 0xC0)
|
|
|
|
|
{
|
|
|
|
|
if ((port & 1) == 0)
|
2017-04-10 16:24:53 +00:00
|
|
|
|
{
|
2012-11-17 21:12:51 +00:00
|
|
|
|
return VDP.ReadData();
|
2017-04-10 16:24:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
return VDP.ReadVdpStatus();
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-11 16:45:45 +00:00
|
|
|
|
if (port >= 0xE0)
|
|
|
|
|
{
|
|
|
|
|
if ((port & 1) == 0)
|
2017-04-10 16:24:53 +00:00
|
|
|
|
{
|
2013-11-11 16:45:45 +00:00
|
|
|
|
return ReadController1();
|
2017-04-10 16:24:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-11 16:45:45 +00:00
|
|
|
|
return ReadController2();
|
|
|
|
|
}
|
2012-11-18 00:40:22 +00:00
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
return 0xFF;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-10 16:24:53 +00:00
|
|
|
|
private void WritePort(ushort port, byte value)
|
2012-11-17 21:12:51 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
2017-04-24 17:24:56 +00:00
|
|
|
|
{
|
2012-11-17 21:12:51 +00:00
|
|
|
|
VDP.WriteVdpData(value);
|
2017-04-24 17:24:56 +00:00
|
|
|
|
}
|
2012-11-17 21:12:51 +00:00
|
|
|
|
else
|
2017-04-24 17:24:56 +00:00
|
|
|
|
{
|
2012-11-17 21:12:51 +00:00
|
|
|
|
VDP.WriteVdpControl(value);
|
2017-04-24 17:24:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-17 21:12:51 +00:00
|
|
|
|
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
|
|
|
|
|
2017-04-10 16:24:53 +00:00
|
|
|
|
public bool DeterministicEmulation => true;
|
|
|
|
|
|
2017-04-24 17:24:56 +00:00
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
}
|
2013-11-11 16:45:45 +00:00
|
|
|
|
|
2013-11-03 16:29:51 +00:00
|
|
|
|
public void ResetCounters()
|
2012-11-25 15:41:40 +00:00
|
|
|
|
{
|
|
|
|
|
Frame = 0;
|
2014-12-05 03:16:08 +00:00
|
|
|
|
_lagCount = 0;
|
|
|
|
|
_isLag = false;
|
2012-11-25 15:41:40 +00:00
|
|
|
|
}
|
2012-11-17 17:39:33 +00:00
|
|
|
|
|
2017-04-10 16:24:53 +00:00
|
|
|
|
public string SystemId => "Coleco";
|
|
|
|
|
|
|
|
|
|
private GameInfo _game;
|
|
|
|
|
public CoreComm CoreComm { get; }
|
2012-03-25 01:33:05 +00:00
|
|
|
|
}
|
2012-11-23 05:51:16 +00:00
|
|
|
|
}
|