2017-07-02 17:47:43 +00:00
|
|
|
|
using BizHawk.Emulation.Common;
|
|
|
|
|
using BizHawk.Emulation.Cores.Waterbox;
|
2017-07-03 00:46:01 +00:00
|
|
|
|
using BizHawk.Emulation.DiscSystem;
|
2017-07-02 17:47:43 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2017-07-03 00:46:01 +00:00
|
|
|
|
using System.Runtime.InteropServices;
|
2017-07-02 17:47:43 +00:00
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
2017-07-03 00:46:01 +00:00
|
|
|
|
using System.IO;
|
2017-07-22 14:55:17 +00:00
|
|
|
|
using BizHawk.Common;
|
|
|
|
|
using System.ComponentModel;
|
2017-07-02 17:47:43 +00:00
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Cores.Consoles.Sega.PicoDrive
|
|
|
|
|
{
|
2017-07-12 19:10:55 +00:00
|
|
|
|
[Core("PicoDrive", "notaz", true, true,
|
2017-07-02 17:47:43 +00:00
|
|
|
|
"0e352905c7aa80b166933970abbcecfce96ad64e", "https://github.com/notaz/picodrive", false)]
|
2017-07-22 14:55:17 +00:00
|
|
|
|
public class PicoDrive : WaterboxCore, IDriveLight, IRegionable, ISettable<object, PicoDrive.SyncSettings>
|
2017-07-02 17:47:43 +00:00
|
|
|
|
{
|
|
|
|
|
private LibPicoDrive _core;
|
2017-07-03 00:46:01 +00:00
|
|
|
|
private LibPicoDrive.CDReadCallback _cdcallback;
|
|
|
|
|
private Disc _cd;
|
|
|
|
|
private DiscSectorReader _cdReader;
|
2017-07-09 15:56:36 +00:00
|
|
|
|
private bool _isPal;
|
2017-07-02 17:47:43 +00:00
|
|
|
|
|
|
|
|
|
[CoreConstructor("GEN")]
|
2017-07-22 14:55:17 +00:00
|
|
|
|
public PicoDrive(CoreComm comm, GameInfo game, byte[] rom, bool deterministic, SyncSettings syncSettings)
|
|
|
|
|
: this(comm, game, rom, null, deterministic, syncSettings)
|
2017-07-03 00:46:01 +00:00
|
|
|
|
{ }
|
|
|
|
|
|
2017-07-22 14:55:17 +00:00
|
|
|
|
public PicoDrive(CoreComm comm, GameInfo game, Disc cd, bool deterministic, SyncSettings syncSettings)
|
|
|
|
|
: this(comm, game, null, cd, deterministic, syncSettings)
|
2017-07-03 00:46:01 +00:00
|
|
|
|
{ }
|
|
|
|
|
|
2017-07-22 14:55:17 +00:00
|
|
|
|
private PicoDrive(CoreComm comm, GameInfo game, byte[] rom, Disc cd, bool deterministic, SyncSettings syncSettings)
|
2017-07-02 17:47:43 +00:00
|
|
|
|
: base(comm, new Configuration
|
|
|
|
|
{
|
|
|
|
|
MaxSamples = 2048,
|
|
|
|
|
DefaultWidth = 320,
|
|
|
|
|
DefaultHeight = 224,
|
|
|
|
|
MaxWidth = 320,
|
|
|
|
|
MaxHeight = 480,
|
|
|
|
|
SystemId = "GEN"
|
|
|
|
|
})
|
|
|
|
|
{
|
2017-07-02 22:16:38 +00:00
|
|
|
|
var biosg = comm.CoreFileProvider.GetFirmware("32X", "G", false);
|
|
|
|
|
var biosm = comm.CoreFileProvider.GetFirmware("32X", "M", false);
|
|
|
|
|
var bioss = comm.CoreFileProvider.GetFirmware("32X", "S", false);
|
|
|
|
|
var has32xBios = biosg != null && biosm != null && bioss != null;
|
|
|
|
|
if (deterministic && !has32xBios)
|
|
|
|
|
throw new InvalidOperationException("32X BIOS files are required for deterministic mode");
|
|
|
|
|
deterministic |= has32xBios;
|
|
|
|
|
|
2017-07-22 14:55:17 +00:00
|
|
|
|
_syncSettings = syncSettings ?? new SyncSettings();
|
|
|
|
|
|
2017-07-02 17:47:43 +00:00
|
|
|
|
_core = PreInit<LibPicoDrive>(new PeRunnerOptions
|
|
|
|
|
{
|
|
|
|
|
Filename = "picodrive.wbx",
|
2017-07-03 14:50:24 +00:00
|
|
|
|
SbrkHeapSizeKB = 64,
|
|
|
|
|
SealedHeapSizeKB = 18 * 1024,
|
|
|
|
|
InvisibleHeapSizeKB = 1024,
|
|
|
|
|
MmapHeapSizeKB = 4096,
|
|
|
|
|
PlainHeapSizeKB = 64,
|
2017-07-02 17:47:43 +00:00
|
|
|
|
});
|
|
|
|
|
|
2017-07-02 22:16:38 +00:00
|
|
|
|
if (has32xBios)
|
|
|
|
|
{
|
|
|
|
|
_exe.AddReadonlyFile(biosg, "32x.g");
|
|
|
|
|
_exe.AddReadonlyFile(biosm, "32x.m");
|
|
|
|
|
_exe.AddReadonlyFile(bioss, "32x.s");
|
|
|
|
|
Console.WriteLine("Using supplied 32x BIOS files");
|
|
|
|
|
}
|
2017-07-03 00:46:01 +00:00
|
|
|
|
if (cd != null)
|
|
|
|
|
{
|
|
|
|
|
_exe.AddReadonlyFile(comm.CoreFileProvider.GetFirmware("GEN", "CD_BIOS_EU", true), "cd.eu");
|
|
|
|
|
_exe.AddReadonlyFile(comm.CoreFileProvider.GetFirmware("GEN", "CD_BIOS_US", true), "cd.us");
|
|
|
|
|
_exe.AddReadonlyFile(comm.CoreFileProvider.GetFirmware("GEN", "CD_BIOS_JP", true), "cd.jp");
|
2017-07-09 14:21:03 +00:00
|
|
|
|
_exe.AddReadonlyFile(gpgx.GPGX.GetCDData(cd), "toc");
|
2017-07-03 00:46:01 +00:00
|
|
|
|
_cd = cd;
|
|
|
|
|
_cdReader = new DiscSectorReader(_cd);
|
|
|
|
|
_cdcallback = CDRead;
|
|
|
|
|
_core.SetCDReadCallback(_cdcallback);
|
|
|
|
|
DriveLightEnabled = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_exe.AddReadonlyFile(rom, "romfile.md");
|
|
|
|
|
}
|
2017-07-02 22:16:38 +00:00
|
|
|
|
|
2017-07-22 14:55:17 +00:00
|
|
|
|
var regionAutoOrder = (LibPicoDrive.Region)(
|
|
|
|
|
(int)_syncSettings.FirstChoice |
|
|
|
|
|
(int)_syncSettings.SecondChoice << 4 |
|
|
|
|
|
(int)_syncSettings.ThirdChoice << 8);
|
|
|
|
|
|
|
|
|
|
if (!_core.Init(cd != null, game["32X"], regionAutoOrder, _syncSettings.RegionOverride))
|
2017-07-03 00:46:01 +00:00
|
|
|
|
throw new InvalidOperationException("Core rejected the file!");
|
2017-07-02 22:16:38 +00:00
|
|
|
|
|
2017-07-03 00:46:01 +00:00
|
|
|
|
if (cd != null)
|
|
|
|
|
{
|
|
|
|
|
_exe.RemoveReadonlyFile("cd.eu");
|
|
|
|
|
_exe.RemoveReadonlyFile("cd.us");
|
|
|
|
|
_exe.RemoveReadonlyFile("cd.jp");
|
|
|
|
|
_exe.RemoveReadonlyFile("toc");
|
|
|
|
|
_core.SetCDReadCallback(null);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_exe.RemoveReadonlyFile("romfile.md");
|
|
|
|
|
}
|
2017-07-02 22:16:38 +00:00
|
|
|
|
if (has32xBios)
|
|
|
|
|
{
|
|
|
|
|
_exe.RemoveReadonlyFile("32x.g");
|
|
|
|
|
_exe.RemoveReadonlyFile("32x.m");
|
|
|
|
|
_exe.RemoveReadonlyFile("32x.s");
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-02 17:47:43 +00:00
|
|
|
|
PostInit();
|
2017-07-02 20:15:54 +00:00
|
|
|
|
ControllerDefinition = PicoDriveController;
|
2017-07-02 22:16:38 +00:00
|
|
|
|
DeterministicEmulation = deterministic;
|
2017-07-03 00:46:01 +00:00
|
|
|
|
_core.SetCDReadCallback(_cdcallback);
|
2017-07-09 15:56:18 +00:00
|
|
|
|
|
2017-07-09 15:56:36 +00:00
|
|
|
|
_isPal = _core.IsPal();
|
|
|
|
|
VsyncNumerator = _isPal ? 53203424 : 53693175;
|
|
|
|
|
VsyncDenominator = _isPal ? 3420 * 313 : 3420 * 262;
|
2017-07-02 17:47:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-07-09 16:14:02 +00:00
|
|
|
|
public bool Is32xActive => _core.Is32xActive();
|
2017-07-09 15:56:18 +00:00
|
|
|
|
|
2017-07-02 20:15:54 +00:00
|
|
|
|
public static readonly ControllerDefinition PicoDriveController = new ControllerDefinition
|
|
|
|
|
{
|
|
|
|
|
Name = "PicoDrive Genesis Controller",
|
|
|
|
|
BoolButtons =
|
|
|
|
|
{
|
|
|
|
|
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 A", "P1 B", "P1 C", "P1 Start", "P1 X", "P1 Y", "P1 Z", "P1 Mode",
|
|
|
|
|
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 A", "P2 B", "P2 C", "P2 Start", "P2 X", "P2 Y", "P2 Z", "P2 Mode",
|
|
|
|
|
"Power", "Reset"
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private static readonly string[] ButtonOrders =
|
|
|
|
|
{
|
|
|
|
|
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 B", "P1 C", "P1 A", "P1 Start", "P1 Z", "P1 Y", "P1 X", "P1 Mode",
|
|
|
|
|
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 B", "P2 C", "P2 A", "P2 Start", "P2 Z", "P2 Y", "P2 X", "P2 Mode",
|
|
|
|
|
"Power", "Reset"
|
|
|
|
|
};
|
|
|
|
|
|
2017-07-02 17:47:43 +00:00
|
|
|
|
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
|
|
|
|
|
{
|
2017-07-02 20:15:54 +00:00
|
|
|
|
var b = 0;
|
|
|
|
|
var v = 1;
|
|
|
|
|
foreach (var s in ButtonOrders)
|
|
|
|
|
{
|
|
|
|
|
if (controller.IsPressed(s))
|
|
|
|
|
b |= v;
|
|
|
|
|
v <<= 1;
|
|
|
|
|
}
|
2017-07-03 00:46:01 +00:00
|
|
|
|
DriveLightOn = false;
|
2017-07-02 20:15:54 +00:00
|
|
|
|
return new LibPicoDrive.FrameInfo { Buttons = b };
|
2017-07-02 17:47:43 +00:00
|
|
|
|
}
|
2017-07-03 00:46:01 +00:00
|
|
|
|
|
|
|
|
|
private void CDRead(int lba, IntPtr dest, bool audio)
|
|
|
|
|
{
|
|
|
|
|
if (audio)
|
|
|
|
|
{
|
|
|
|
|
byte[] data = new byte[2352];
|
2017-07-03 12:16:34 +00:00
|
|
|
|
if (lba < _cd.Session1.LeadoutLBA && lba >= _cd.Session1.Tracks[2].LBA)
|
2017-07-03 00:46:01 +00:00
|
|
|
|
{
|
|
|
|
|
_cdReader.ReadLBA_2352(lba, data, 0);
|
|
|
|
|
}
|
|
|
|
|
Marshal.Copy(data, 0, dest, 2352);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
byte[] data = new byte[2048];
|
|
|
|
|
_cdReader.ReadLBA_2048(lba, data, 0);
|
|
|
|
|
Marshal.Copy(data, 0, dest, 2048);
|
|
|
|
|
DriveLightOn = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void LoadStateBinaryInternal(BinaryReader reader)
|
|
|
|
|
{
|
|
|
|
|
_core.SetCDReadCallback(_cdcallback);
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-22 14:55:17 +00:00
|
|
|
|
#region ISettable
|
|
|
|
|
|
|
|
|
|
public class SyncSettings
|
|
|
|
|
{
|
|
|
|
|
[DefaultValue(LibPicoDrive.Region.Auto)]
|
|
|
|
|
[Description("If set, force the console to this region")]
|
|
|
|
|
public LibPicoDrive.Region RegionOverride { get; set; }
|
|
|
|
|
|
|
|
|
|
[DefaultValue(LibPicoDrive.Region.Auto)]
|
|
|
|
|
[Description("When region is set to automatic, highest priority region to use if the game supports multiple regions")]
|
|
|
|
|
public LibPicoDrive.Region FirstChoice { get; set; }
|
|
|
|
|
|
|
|
|
|
[DefaultValue(LibPicoDrive.Region.Auto)]
|
|
|
|
|
[Description("When region is set to automatic, second highest priority region to use if the game supports multiple regions")]
|
|
|
|
|
public LibPicoDrive.Region SecondChoice { get; set; }
|
|
|
|
|
|
|
|
|
|
[DefaultValue(LibPicoDrive.Region.Auto)]
|
|
|
|
|
[Description("When region is set to automatic, lowest priority region to use if the game supports multiple regions")]
|
|
|
|
|
public LibPicoDrive.Region ThirdChoice { get; set; }
|
|
|
|
|
|
|
|
|
|
public SyncSettings Clone()
|
|
|
|
|
{
|
|
|
|
|
return (SyncSettings)MemberwiseClone();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool NeedsReboot(SyncSettings x, SyncSettings y)
|
|
|
|
|
{
|
|
|
|
|
return !DeepEquality.DeepEquals(x, y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SyncSettings()
|
|
|
|
|
{
|
|
|
|
|
SettingsUtil.SetDefaultValues(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private SyncSettings _syncSettings;
|
|
|
|
|
|
|
|
|
|
public object GetSettings()
|
|
|
|
|
{
|
|
|
|
|
return new object();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SyncSettings GetSyncSettings()
|
|
|
|
|
{
|
|
|
|
|
return _syncSettings.Clone();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool PutSettings(object o)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool PutSyncSettings(SyncSettings o)
|
|
|
|
|
{
|
|
|
|
|
var ret = SyncSettings.NeedsReboot(_syncSettings, o);
|
|
|
|
|
_syncSettings = o;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2017-07-03 00:46:01 +00:00
|
|
|
|
#region IDriveLight
|
|
|
|
|
|
|
|
|
|
public bool DriveLightEnabled { get; private set; }
|
|
|
|
|
public bool DriveLightOn { get; private set; }
|
|
|
|
|
|
|
|
|
|
#endregion
|
2017-07-09 15:56:36 +00:00
|
|
|
|
|
|
|
|
|
#region IRegionable
|
|
|
|
|
|
|
|
|
|
public DisplayType Region => _isPal ? DisplayType.PAL : DisplayType.NTSC;
|
|
|
|
|
|
|
|
|
|
#endregion
|
2017-07-02 17:47:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|