push c# side for memtrack support

This commit is contained in:
CasualPokePlayer 2022-09-28 03:27:07 -07:00
parent 6388e4a0a8
commit 70a21ee07e
17 changed files with 121 additions and 66 deletions

View File

@ -44,7 +44,7 @@ namespace BizHawk.Client.Common
[VSystemID.Raw.N64] = "N64",
[VSystemID.Raw.SAT] = "Saturn",
[VSystemID.Raw.WSWAN] = "WonderSwan",
[VSystemID.Raw.JAG] = "Jaguar",
[VSystemID.Raw.Jaguar] = "Jaguar",
[VSystemID.Raw.Lynx] = "Lynx",
[VSystemID.Raw.AppleII] = "Apple II",
[VSystemID.Raw.Libretro] = "Libretro",
@ -227,7 +227,7 @@ namespace BizHawk.Client.Common
PalettesEntryFor(VSystemID.Raw.INTV),
},
CommonEntriesFor(VSystemID.Raw.JAG, basePath: Path.Combine(".", "Jaguar")),
CommonEntriesFor(VSystemID.Raw.Jaguar, basePath: Path.Combine(".", "Jaguar")),
new[] {
BaseEntryFor(VSystemID.Raw.Libretro, Path.Combine(".", "Libretro")),

View File

@ -391,7 +391,7 @@ namespace BizHawk.Client.Common
["Toggle Link Shift"] = 'F',
["Toggle Link Spacing"] = 'C',
},
[VSystemID.Raw.JAG] = new()
[VSystemID.Raw.Jaguar] = new()
{
["Option"] = 'O',
["Asterisk"] = '*',

View File

@ -360,7 +360,7 @@ namespace BizHawk.Emulation.Common
case ".J64":
case ".JAG":
game.System = VSystemID.Raw.JAG;
game.System = VSystemID.Raw.Jaguar;
break;
case ".LNX":

View File

@ -143,12 +143,6 @@ namespace BizHawk.Emulation.Common
FirmwareAndOption(SHA1Checksum.Dummy, 131072, "NDS", "firmwarei", "DSi_Firmware.bin", "DSi Firmware");
FirmwareAndOption(SHA1Checksum.Dummy, 251658304, "NDS", "nand", "DSi_Nand.bin", "DSi NAND");
Firmware("Jaguar", "Bios", "Bios");
var kseries = File("F8991B0C385F4E5002FA2A7E2F5E61E8C5213356", 131072, "JAG_bios_k.bin", "Bios (K Series)");
var mseries = File("AF643900FEA84234E08B616A6D27F03F0A61DB96", 131072, "JAG_bios_m.bin", "Bios (M Series)");
Option("Jaguar", "Bios", in kseries);
Option("Jaguar", "Bios", in mseries);
FirmwareAndOption("E4ED47FAE31693E016B081C6BDA48DA5B70D7CCB", 512, "Lynx", "Boot", "LYNX_boot.img", "Boot Rom");
FirmwareAndOption("5A65B922B562CB1F57DAB51B73151283F0E20C7A", 8192, "INTV", "EROM", "INTV_EROM.bin", "Executive Rom");

View File

@ -35,7 +35,7 @@ namespace BizHawk.Emulation.Common
[VSystemID.Raw.GG] = "Game Gear",
[VSystemID.Raw.GGL] = "Game Gear Link",
[VSystemID.Raw.INTV] = "Intellivision",
[VSystemID.Raw.JAG] = "Jaguar",
[VSystemID.Raw.Jaguar] = "Jaguar",
[VSystemID.Raw.Libretro] = "Libretro",
[VSystemID.Raw.Lynx] = "Lynx",
[VSystemID.Raw.MAME] = "MAME",

View File

@ -9,7 +9,7 @@ namespace BizHawk.Emulation.Common
{
new(VSystemID.Raw.A26, "Atari 2600"),
new(VSystemID.Raw.A78, "Atari 7800"),
new(VSystemID.Raw.JAG, "Atari Jaguar"),
new(VSystemID.Raw.Jaguar, "Atari Jaguar"),
new(VSystemID.Raw.Lynx, "Atari Lynx"),
new(VSystemID.Raw.NES, "NES"),
new(VSystemID.Raw.SNES, "Super NES"),

View File

@ -33,7 +33,7 @@ namespace BizHawk.Emulation.Common
public const string GG = "GG";
public const string GGL = "GGL";
public const string INTV = "INTV";
public const string JAG = "Jaguar";
public const string Jaguar = "Jaguar";
public const string Libretro = "Libretro";
public const string Lynx = "Lynx";
public const string MAME = "MAME";

View File

@ -15,7 +15,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
_m68kDisassembler.ReadByte = a => (sbyte)m.PeekByte(a);
_m68kDisassembler.ReadWord = a => (short)m.PeekUshort(a, true);
_m68kDisassembler.ReadLong = a => (int)m.PeekUint(a, true);
var info = _m68kDisassembler.Disassemble((int)addr);
var info = _m68kDisassembler.Disassemble((int)(addr & 0xFFFFFF));
length = info.Length;
return $"{info.RawBytes.Substring(0, 4):X4} {info.Mnemonic,-7} {info.Args}";
}
@ -24,33 +24,33 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
// most of this is taken from virtualjaguar's dasmjag function
public string DisassembleRISC(bool gpu, MemoryDomain m, uint addr, out int length)
{
var opcode = m.PeekUshort(addr, true);
var opcode = m.PeekUshort(addr & 0xFFFFFF, true);
var arg1 = (opcode >> 5) & 0x1F;
var arg2 = opcode & 0x1F;
length = (opcode >> 10) == 0x26 ? 6 : 2;
string argRR() => $"r{arg1:d02}, r{arg2:d02}";
string argCZIR() => $"${(arg1 == 0 ? 32 : arg1):X02}, r{arg2:d02}";
string argIR() => $"${arg1:X02}, r{arg2:d02}";
string argR2() => $"r{arg2:d02}";
string argRR() => $"r{arg1}, r{arg2}";
string argCZIR() => $"${(arg1 == 0 ? 32 : arg1):X02}, r{arg2}";
string argIR() => $"${arg1:X02}, r{arg2}";
string argR2() => $"r{arg2}";
string argSR()
{
var s1 = (short)(arg1 << 11) >> 11;
if (s1 < 0)
{
return $"-${-s1:X02}, r{arg2:d02}";
return $"-${-s1:X02}, r{arg2}";
}
else
{
return $"${s1:X02}, r{arg2:d02}";
return $"${s1:X02}, r{arg2}";
}
}
string argDRR() => $"(r{arg1:d02}), r{arg2:d02}";
string argRDR() => $"r{arg2:d02}, (r{arg1:d02})";
string argDROR(int r) => $"(r{r:d02} + ${(arg1 == 0 ? 128 : arg1 * 4):X02}), r{arg2:d02}";
string argRDRO(int r) => $"r{arg2:d02}, (r{r:d02} + ${(arg1 == 0 ? 128 : arg1 * 4):X02})";
string argDRORR(int r) => $"(r{r:d02} + r{arg1:d02}), r{arg2:d02}";
string argRDROR(int r) => $"r{arg1:d02}, (r{r:d02} + r{arg2:d02})";
string argDRR() => $"(r{arg1}), r{arg2}";
string argRDR() => $"r{arg2}, (r{arg1})";
string argDROR(int r) => $"(r{r} + ${(arg1 == 0 ? 128 : arg1 * 4):X02}), r{arg2}";
string argRDRO(int r) => $"r{arg2}, (r{r} + ${(arg1 == 0 ? 128 : arg1 * 4):X02})";
string argDRORR(int r) => $"(r{r} + r{arg1}), r{arg2}";
string argRDROR(int r) => $"r{arg1}, (r{r} + r{arg2})";
string argCC()
{
return arg2 switch
@ -115,7 +115,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
0x23 => $"moveq {argIR()}",
0x24 => $"moveta {argRR()}",
0x25 => $"movefa {argRR()}",
0x26 => $"movei ${m.PeekUshort(addr + 2, true) | (m.PeekUshort(addr + 4, true) << 16):X06}, {argR2()}",
0x26 => $"movei ${m.PeekUshort((addr + 2) & 0xFFFFFF, true) | (m.PeekUshort((addr + 4) & 0xFFFFFF, true) << 16):X06}, {argR2()}",
0x27 => $"loadb {argDRR()}",
0x28 => $"loadw {argDRR()}",
0x29 => $"load {argDRR()}",
@ -129,7 +129,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
0x31 => $"store {argRDRO(14)}",
0x32 => $"store {argRDRO(15)}",
0x33 => $"move pc, {argR2()}",
0x34 => $"jump {argCC()}(r{arg1:d02})",
0x34 => $"jump {argCC()}(r{arg1})",
0x35 => $"jr {argCC()}${addr + 2 + ((sbyte)(arg1 << 3) >> 2):X06}",
0x36 => $"mmult {argRR()}",
0x37 => $"mtoi {argRR()}",
@ -139,14 +139,14 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
0x3B => $"load {argDRORR(15)}",
0x3C => $"store {argRDROR(14)}",
0x3D => $"store {argRDROR(15)}",
0x3E => gpu ? $"sat24 {argR2()}" : $"illegal [{arg1:d02}, {arg2:d02}]",
0x3E => gpu ? $"sat24 {argR2()}" : $"illegal [{arg1}, {arg2}]",
0x3F => gpu ? $"{(arg1 == 0 ? "pack" : "unpack")} {argR2()}" : $"addqmod {argCZIR()}",
_ => throw new InvalidOperationException(),
};
if (length == 6)
{
return $"{opcode:X04} {m.PeekUshort(addr + 2, true):X04} {m.PeekUshort(addr + 4, true):X04} {disasm}";
return $"{opcode:X04} {m.PeekUshort((addr + 2) & 0xFFFFFF, true):X04} {m.PeekUshort((addr + 4) & 0xFFFFFF, true):X04} {disasm}";
}
else
{

View File

@ -92,7 +92,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
public abstract void SetCdCallbacks(CDTOCCallback cdtc, CDReadCallback cdrc);
[BizImport(CC)]
public abstract void InitWithCd(ref Settings s, IntPtr bios);
public abstract void InitWithCd(ref Settings s, IntPtr bios, IntPtr memtrack);
[BizImport(CC)]
public abstract bool SaveRamIsDirty();
@ -107,7 +107,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
public delegate void MemoryCallback(uint addr);
[BizImport(CC)]
public abstract void SetMemoryCallback(int which, MemoryCallback callback);
public abstract void SetMemoryCallbacks(MemoryCallback rcb, MemoryCallback wcb, MemoryCallback ecb);
[UnmanagedFunctionPointer(CC)]
public delegate void M68KTraceCallback(IntPtr regs);
@ -116,7 +116,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
public delegate void RISCTraceCallback(uint pc, IntPtr regs);
[BizImport(CC)]
public abstract void SetTraceCallbacks(M68KTraceCallback cpuTraceCallback, RISCTraceCallback gpuTraceCallback, RISCTraceCallback dspTraceCallback);
public abstract void SetTraceCallbacks(M68KTraceCallback ctcb, RISCTraceCallback gtcb, RISCTraceCallback dtcb);
[BizImport(CC)]
public abstract void GetRegisters(IntPtr regs);

View File

@ -14,20 +14,28 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
_core.GetRegisters((IntPtr)regs);
var ret = new Dictionary<string, RegisterValue>();
// M68K data regs
for (int i = 0; i < 8; i++)
{
ret[$"M68K D{i}"] = regs[i];
}
// M68K address regs
for (int i = 0; i < 8; i++)
{
ret[$"M68K A{i}"] = regs[8 + i];
}
ret["M68K PC"] = regs[16];
ret["M68K SR"] = regs[17];
// these registers aren't really 0-63, but it's two banks of 32 registers
for (int i = 0; i < 64; i++)
{
// registers aren't really 0-63, but it's two banks of 32 registers
ret[$"GPU R{i}"] = regs[18 + i];
ret[$"DSP R{i}"] = regs[82 + i];
}
ret["GPU PC"] = regs[146];
for (int i = 0; i < 64; i++)
{
ret[$"DSP R{i}"] = regs[82 + i];
}
ret["DSP PC"] = regs[147];
return ret;
@ -113,9 +121,10 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
private void SetMemoryCallbacks()
{
_core.SetMemoryCallback(0, MemoryCallbacks.HasReads ? _readCallback : null);
_core.SetMemoryCallback(1, MemoryCallbacks.HasWrites ? _writeCallback : null);
_core.SetMemoryCallback(2, MemoryCallbacks.HasExecutes ? _execCallback : null);
_core.SetMemoryCallbacks(
MemoryCallbacks.HasReads ? _readCallback : null,
MemoryCallbacks.HasWrites ? _writeCallback : null,
MemoryCallbacks.HasExecutes ? _execCallback : null);
}
}
}

View File

@ -6,23 +6,33 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
{
public partial class VirtualJaguar : ISaveRam
{
public new bool SaveRamModified => _core.SaveRamIsDirty();
private readonly int _saveRamSize;
public new bool SaveRamModified => _saveRamSize > 0 && _core.SaveRamIsDirty();
public new byte[] CloneSaveRam()
{
byte[] ret = new byte[128];
if (_saveRamSize == 0)
{
return null;
}
byte[] ret = new byte[_saveRamSize];
_core.GetSaveRam(ret);
return ret;
}
public new void StoreSaveRam(byte[] data)
{
if (data.Length != 128)
if (_saveRamSize > 0)
{
throw new ArgumentException(message: "buffer wrong size", paramName: nameof(data));
}
if (data.Length != _saveRamSize)
{
throw new ArgumentException(message: "buffer wrong size", paramName: nameof(data));
}
_core.PutSaveRam(data);
_core.PutSaveRam(data);
}
}
}
}

View File

@ -1,4 +1,5 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using BizHawk.Common;
using BizHawk.Emulation.Common;
@ -32,17 +33,17 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
public class VirtualJaguarSettings
{
[DisplayName("Trace M68K (CPU)")]
[Description("")]
[Description("Used for the tracelogger")]
[DefaultValue(true)]
public bool TraceCPU { get; set; }
[DisplayName("Trace TOM (GPU)")]
[Description("")]
[Description("Used for the tracelogger")]
[DefaultValue(false)]
public bool TraceGPU { get; set; }
[DisplayName("Trace JERRY (DSP)")]
[Description("")]
[Description("Used for the tracelogger")]
[DefaultValue(false)]
public bool TraceDSP { get; set; }
@ -55,31 +56,50 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
public class VirtualJaguarSyncSettings
{
[DisplayName("Player 1 Controller Connected")]
[DisplayName("Player 1 Connected")]
[Description("")]
[DefaultValue(true)]
public bool P1Active { get; set; }
[DisplayName("Player 2 Controller Connected")]
[DisplayName("Player 2 Connected")]
[Description("")]
[DefaultValue(false)]
public bool P2Active { get; set; }
[DisplayName("NTSC")]
[Description("")]
[DefaultValue(true)]
public bool NTSC { get; set; }
[DisplayName("Skip BIOS")]
[Description("BIOS file must still be present. Ignored (set to true) for Jaguar CD")]
[Description("Ignored (set to true) for Jaguar CD")]
[DefaultValue(true)]
public bool SkipBIOS { get; set; }
[DisplayName("Use Fast Blitter")]
public enum BiosRevisions
{
[Display(Name = "K Series")]
KSeries = 0,
[Display(Name = "M Series")]
MSeries = 1,
}
[DisplayName("BIOS Revision")]
[Description("")]
[DefaultValue(BiosRevisions.MSeries)]
[TypeConverter(typeof(DescribableEnumConverter))]
public BiosRevisions BiosRevision { get; set; }
[DisplayName("NTSC")]
[Description("Set this to false to emulate a PAL console")]
[DefaultValue(true)]
public bool NTSC { get; set; }
[DisplayName("Use Fast Blitter")]
[Description("If true, a faster, less compatible blitter is used")]
[DefaultValue(false)]
public bool UseFastBlitter { get; set; }
[DisplayName("Use Memory Track")]
[Description("Allows for SaveRAM creation with Jaguar CD games. Does nothing for non-CD games.")]
[DefaultValue(true)]
public bool UseMemoryTrack { get; set; }
public VirtualJaguarSyncSettings()
=> SettingsUtil.SetDefaultValues(this);

View File

@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
using BizHawk.Common.CollectionExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Properties;
using BizHawk.Emulation.Cores.Waterbox;
using BizHawk.Emulation.DiscSystem;
@ -17,7 +18,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
private readonly LibVirtualJaguar _core;
private readonly JaguarDisassembler _disassembler;
[CoreConstructor(VSystemID.Raw.JAG)]
[CoreConstructor(VSystemID.Raw.Jaguar)]
public VirtualJaguar(CoreLoadParameters<VirtualJaguarSettings, VirtualJaguarSyncSettings> lp)
: base(lp.Comm, new Configuration
{
@ -28,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
MaxSamples = 1024,
DefaultFpsNumerator = 60,
DefaultFpsDenominator = 1,
SystemId = VSystemID.Raw.JAG,
SystemId = VSystemID.Raw.Jaguar,
})
{
_settings = lp.Settings ?? new();
@ -57,11 +58,16 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
}, new Delegate[] { _readCallback, _writeCallback, _execCallback, _cpuTraceCallback, _gpuTraceCallback, _dspTraceCallback, _cdTocCallback, _cdReadCallback, });
var bios = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("Jaguar", "Bios"));
if (bios.Length != 0x20000)
// we can include the bios files / memtrack rom as they are public domain since May 14, 1999
// see https://www.atariage.com/Jaguar/archives/HasbroRights.html
var brev = _syncSettings.BiosRevision switch
{
throw new MissingFirmwareException("Jaguar Bios must be 131072 bytes!");
}
VirtualJaguarSyncSettings.BiosRevisions.KSeries => Resources.JAGUAR_KSERIES_ROM,
VirtualJaguarSyncSettings.BiosRevisions.MSeries => Resources.JAGUAR_MSERIES_ROM,
_ => throw new InvalidOperationException(),
};
var bios = Zstd.DecompressZstdStream(new MemoryStream(brev.Value)).ToArray();
var settings = new LibVirtualJaguar.Settings()
{
@ -76,6 +82,11 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
_cd = lp.Discs[0].DiscData;
_cdReader = new(_cd);
#else
if (lp.Discs.Count == 1)
{
throw new InvalidOperationException("Jaguar CD currently requires each session split into separate discs");
}
_cd = new Disc[lp.Discs.Count];
_cdReader = new DiscSectorReader[lp.Discs.Count];
for (int i = 0; i < lp.Discs.Count; i++)
@ -86,11 +97,16 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
#endif
_core.SetCdCallbacks(_cdTocCallback, _cdReadCallback);
_saveRamSize = _syncSettings.UseMemoryTrack ? 0x20000 : 0;
var memtrack = _syncSettings.UseMemoryTrack
? Zstd.DecompressZstdStream(new MemoryStream(Resources.JAGUAR_MEMTRACK_ROM.Value)).ToArray()
: null;
unsafe
{
fixed (byte* bp = bios)
fixed (byte* bp = bios, mp = memtrack)
{
_core.InitWithCd(ref settings, (IntPtr)bp);
_core.InitWithCd(ref settings, (IntPtr)bp, (IntPtr)mp);
}
}
}
@ -98,6 +114,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
{
_cdTocCallback = null;
_cdReadCallback = null;
_saveRamSize = 128;
var rom = lp.Roms[0].FileData;
unsafe
{
@ -240,7 +257,9 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar
private readonly LibVirtualJaguar.CDTOCCallback _cdTocCallback;
private readonly LibVirtualJaguar.CDReadCallback _cdReadCallback;
#if false // uh oh, we don't actually have multisession disc support, so...
// uh oh, we don't actually have multisession disc support, so...
// TODO: get rid of this hack once we have proper multisession disc support
#if false
private readonly Disc _cd;
private readonly DiscSectorReader _cdReader;

View File

@ -26,5 +26,8 @@ namespace BizHawk.Emulation.Cores.Properties {
internal static readonly Lazy<byte[]> TMDS = new(() => ReadEmbeddedByteArray("tmds.zip.zst"));
internal static readonly Lazy<byte[]> PIF_PAL_ROM = new(() => ReadEmbeddedByteArray("pif.pal.rom.zst"));
internal static readonly Lazy<byte[]> PIF_NTSC_ROM = new(() => ReadEmbeddedByteArray("pif.ntsc.rom.zst"));
internal static readonly Lazy<byte[]> JAGUAR_KSERIES_ROM = new(() => ReadEmbeddedByteArray("JAGUAR_KSERIES.ROM.zst"));
internal static readonly Lazy<byte[]> JAGUAR_MSERIES_ROM = new(() => ReadEmbeddedByteArray("JAGUAR_MSERIES.ROM.zst"));
internal static readonly Lazy<byte[]> JAGUAR_MEMTRACK_ROM = new(() => ReadEmbeddedByteArray("JAGUAR_MEMTRACK.ROM.zst"));
}
}