diff --git a/Assets/dll/virtualjaguar.wbx.zst b/Assets/dll/virtualjaguar.wbx.zst index ddbabf85d2..5d25ddae0f 100644 Binary files a/Assets/dll/virtualjaguar.wbx.zst and b/Assets/dll/virtualjaguar.wbx.zst differ diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/JaguarDisassembler.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/JaguarDisassembler.cs new file mode 100644 index 0000000000..2ebfb7cdd8 --- /dev/null +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/JaguarDisassembler.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Components.M68000; + +namespace BizHawk.Emulation.Cores.Atari.Jaguar +{ + public partial class JaguarDisassembler : VerifiedDisassembler + { + private readonly MC68000 _disassembler = new(); + + public override string PCRegisterName => "PC"; + + public override IEnumerable AvailableCpus => new[] { "M68000" }; + + public override string Disassemble(MemoryDomain m, uint addr, out int length) + { + _disassembler.ReadByte = a => (sbyte)m.PeekByte(a); + _disassembler.ReadWord = a => (short)m.PeekUshort(a, true); + _disassembler.ReadLong = a => (int)m.PeekUint(a, true); + var info = _disassembler.Disassemble((int)addr); + length = info.Length; + return $"{info.RawBytes.Substring(0, 4):X4} {info.Mnemonic,-7} {info.Args}"; + } + } +} diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/LibVirtualJaguar.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/LibVirtualJaguar.cs index fc1975d770..439886c7a3 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/LibVirtualJaguar.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/LibVirtualJaguar.cs @@ -8,34 +8,6 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar { public abstract class LibVirtualJaguar : LibWaterboxCore { - [StructLayout(LayoutKind.Sequential)] - public struct Settings - { - public bool NTSC; - public bool UseBIOS; - public bool UseFastBlitter; - } - - [StructLayout(LayoutKind.Sequential)] - public new class FrameInfo : LibWaterboxCore.FrameInfo - { - public Buttons Player1; - public Buttons Player2; - public bool Reset; - } - - [BizImport(CC)] - public abstract bool Init(ref Settings s, IntPtr bios, IntPtr rom, int romsize); - - [BizImport(CC)] - public abstract bool SaveRamIsDirty(); - - [BizImport(CC)] - public abstract void GetSaveRam(byte[] dst); - - [BizImport(CC)] - public abstract void PutSaveRam(byte[] src); - [Flags] public enum Buttons : uint { @@ -61,5 +33,99 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar Option = 1 << 19, Pause = 1 << 20, } + + [StructLayout(LayoutKind.Sequential)] + public struct Settings + { + public bool NTSC; + public bool UseBIOS; + public bool UseFastBlitter; + } + + [StructLayout(LayoutKind.Sequential)] + public new class FrameInfo : LibWaterboxCore.FrameInfo + { + public Buttons Player1; + public Buttons Player2; + public bool Reset; + } + + [BizImport(CC)] + public abstract bool Init(ref Settings s, IntPtr bios, IntPtr rom, int romsize); + + [StructLayout(LayoutKind.Sequential)] + public struct TOC + { + public byte Padding0; + public byte Padding1; + public byte NumSessions; + public byte MinTrack; + public byte MaxTrack; + public byte LastLeadOutMins; + public byte LastLeadOutSecs; + public byte LastLeadOutFrames; + + [StructLayout(LayoutKind.Sequential)] + public struct Track + { + public byte TrackNum; + public byte StartMins; + public byte StartSecs; + public byte StartFrames; + public byte SessionNum; + public byte DurMins; + public byte DurSecs; + public byte DurFrames; + } + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 127)] + public Track[] Tracks; + } + + [UnmanagedFunctionPointer(CC)] + public delegate void CDTOCCallback(IntPtr dst); + + [UnmanagedFunctionPointer(CC)] + public delegate void CDReadCallback(int lba, IntPtr dst); + + [BizImport(CC)] + public abstract void SetCdCallbacks(CDTOCCallback cdtc, CDReadCallback cdrc); + + [BizImport(CC)] + public abstract void InitWithCd(ref Settings s, IntPtr bios); + + [BizImport(CC)] + public abstract bool SaveRamIsDirty(); + + [BizImport(CC)] + public abstract void GetSaveRam(byte[] dst); + + [BizImport(CC)] + public abstract void PutSaveRam(byte[] src); + + [UnmanagedFunctionPointer(CC)] + public delegate void MemoryCallback(uint addr); + + [BizImport(CC)] + public abstract void SetMemoryCallback(int which, MemoryCallback callback); + + [UnmanagedFunctionPointer(CC)] + public delegate void TraceCallback(IntPtr regs); + + [BizImport(CC)] + public abstract void SetTraceCallback(TraceCallback callback); + + [BizImport(CC)] + public abstract void GetRegisters(uint[] regs); + + public enum M68KRegisters : uint + { + D0, D1, D2, D3, D4, D5, D6, D7, + A0, A1, A2, A3, A4, A5, A6, A7, + PC, SR, + } + + [BizImport(CC)] + public abstract void SetRegister(M68KRegisters which, int val); } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.IDebuggable.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.IDebuggable.cs new file mode 100644 index 0000000000..550702c2a4 --- /dev/null +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.IDebuggable.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Atari.Jaguar +{ + partial class VirtualJaguar : IDebuggable + { + public IDictionary GetCpuFlagsAndRegisters() + { + uint[] regs = new uint[18]; + _core.GetRegisters(regs); + + var ret = new Dictionary(); + for (int i = 0; i < 8; i++) + { + ret[$"D{i}"] = regs[i]; + ret[$"A{i}"] = regs[8 + i]; + } + ret["PC"] = regs[16]; + ret["SR"] = regs[17]; + return ret; + } + + public void SetCpuRegister(string register, int value) + => _core.SetRegister((LibVirtualJaguar.M68KRegisters)Enum.Parse(typeof(LibVirtualJaguar.M68KRegisters), register.ToUpperInvariant()), value); + + public bool CanStep(StepType type) + => false; + + [FeatureNotImplemented] + public void Step(StepType type) + => throw new NotImplementedException(); + + [FeatureNotImplemented] + public long TotalExecutedCycles + => throw new NotImplementedException(); + + public IMemoryCallbackSystem MemoryCallbacks => _memoryCallbacks; + + private readonly MemoryCallbackSystem _memoryCallbacks = new(new[] { "System Bus" }); + + private LibVirtualJaguar.MemoryCallback _readCallback; + private LibVirtualJaguar.MemoryCallback _writeCallback; + private LibVirtualJaguar.MemoryCallback _execCallback; + + private void InitMemoryCallbacks() + { + LibVirtualJaguar.MemoryCallback CreateCallback(MemoryCallbackFlags flags, Func getHasCBOfType) + { + var rawFlags = (uint)flags; + return address => + { + if (getHasCBOfType()) + { + MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, "System Bus"); + } + }; + } + + _readCallback = CreateCallback(MemoryCallbackFlags.AccessRead, () => MemoryCallbacks.HasReads); + _writeCallback = CreateCallback(MemoryCallbackFlags.AccessWrite, () => MemoryCallbacks.HasWrites); + _execCallback = CreateCallback(MemoryCallbackFlags.AccessExecute, () => MemoryCallbacks.HasExecutes); + + _memoryCallbacks.ActiveChanged += SetMemoryCallbacks; + } + + private void SetMemoryCallbacks() + { + _core.SetMemoryCallback(0, MemoryCallbacks.HasReads ? _readCallback : null); + _core.SetMemoryCallback(1, MemoryCallbacks.HasWrites ? _writeCallback : null); + _core.SetMemoryCallback(2, MemoryCallbacks.HasExecutes ? _execCallback : null); + } + } +} diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.ITraceable.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.ITraceable.cs new file mode 100644 index 0000000000..fa946137f3 --- /dev/null +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.ITraceable.cs @@ -0,0 +1,38 @@ +using System; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Atari.Jaguar +{ + public partial class VirtualJaguar + { + private ITraceable Tracer { get; } + private readonly LibVirtualJaguar.TraceCallback _traceCallback; + + private unsafe void MakeTrace(IntPtr r) + { + uint* regs = (uint*)r; + var pc = regs[16] & 0xFFFFFF; + var disasm = _disassembler.Disassemble(this.AsMemoryDomains().SystemBus, pc, out _); + var regInfo = string.Empty; + for (int i = 0; i < 8; i++) + { + regInfo += $"D{i}:{regs[i]:X8} "; + } + for (int i = 0; i < 8; i++) + { + regInfo += $"A{i}:{regs[i + 8]:X8} "; + } + regInfo += $"SR:{regs[17]:X8} "; + var sr = regs[17]; + regInfo += string.Concat( + (sr & 16) > 0 ? "X" : "x", + (sr & 8) > 0 ? "N" : "n", + (sr & 4) > 0 ? "Z" : "z", + (sr & 2) > 0 ? "V" : "v", + (sr & 1) > 0 ? "C" : "c"); + + Tracer.Put(new(disassembly: $"{pc:X6}: {disasm}".PadRight(50), registerInfo: regInfo)); + } + } +} diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.cs index e9b4142c3b..25d82a4f16 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/jaguar/VirtualJaguar.cs @@ -1,18 +1,21 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Runtime.InteropServices; using BizHawk.Common.CollectionExtensions; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Waterbox; +using BizHawk.Emulation.DiscSystem; namespace BizHawk.Emulation.Cores.Atari.Jaguar { [PortedCore(CoreNames.VirtualJaguar, "Niels Wagenaar, Carwin Jones, Adam Green, James L. Hammons", "2.1.3", "https://icculus.org/virtualjaguar/", isReleased: false)] - [ServiceNotApplicable(new[] { typeof(IDriveLight) })] - public partial class VirtualJaguar : WaterboxCore, IRegionable + public partial class VirtualJaguar : WaterboxCore, IRegionable, IDriveLight { private readonly LibVirtualJaguar _core; + private readonly JaguarDisassembler _disassembler; [CoreConstructor(VSystemID.Raw.JAG)] public VirtualJaguar(CoreLoadParameters lp) @@ -34,6 +37,11 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar Region = _syncSettings.NTSC ? DisplayType.NTSC : DisplayType.PAL; VsyncNumerator = _syncSettings.NTSC ? 60 : 50; + InitMemoryCallbacks(); + _traceCallback = MakeTrace; + _cdTocCallback = CDTOCCallback; + _cdReadCallback = CDReadCallback; + _core = PreInit(new WaterboxOptions { Filename = "virtualjaguar.wbx", @@ -44,7 +52,7 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar MmapHeapSizeKB = 64 * 1024, SkipCoreConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck), SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck), - }); + }, new Delegate[] { _readCallback, _writeCallback, _execCallback, _traceCallback, _cdTocCallback, _cdReadCallback, }); var bios = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("Jaguar", "Bios")); if (bios.Length != 0x20000) @@ -59,19 +67,59 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar UseFastBlitter = _syncSettings.UseFastBlitter, }; - var rom = lp.Roms[0].FileData; - unsafe + if (lp.Discs.Count > 0) { - fixed (byte* rp = rom, bp = bios) +#if false + _cd = lp.Discs[0].DiscData; + _cdReader = new(_cd); +#else + _cd = new Disc[lp.Discs.Count]; + _cdReader = new DiscSectorReader[lp.Discs.Count]; + for (int i = 0; i < lp.Discs.Count; i++) { - if (!_core.Init(ref settings, (IntPtr)bp, (IntPtr)rp, rom.Length)) + _cd[i] = lp.Discs[i].DiscData; + _cdReader[i] = new(lp.Discs[i].DiscData); + } +#endif + _core.SetCdCallbacks(_cdTocCallback, _cdReadCallback); + + unsafe + { + fixed (byte* bp = bios) { - throw new Exception("Core rejected the rom!"); + _core.InitWithCd(ref settings, (IntPtr)bp); + } + } + } + else + { + _cdTocCallback = null; + _cdReadCallback = null; + var rom = lp.Roms[0].FileData; + unsafe + { + fixed (byte* rp = rom, bp = bios) + { + if (!_core.Init(ref settings, (IntPtr)bp, (IntPtr)rp, rom.Length)) + { + throw new Exception("Core rejected the rom!"); + } } } } + _core.SetCdCallbacks(null, null); + PostInit(); + + _core.SetCdCallbacks(_cdTocCallback, _cdReadCallback); + + _disassembler = new(); + _serviceProvider.Register(_disassembler); + + const string TRACE_HEADER = "M68K: PC, machine code, mnemonic, operands, registers (D0-D7, A0-A7, SR), flags (XNZVC)"; + Tracer = new TraceBuffer(TRACE_HEADER); + _serviceProvider.Register(Tracer); } private static readonly IReadOnlyList JaguarButtonsOrdered = new[] @@ -150,6 +198,9 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound) { + _core.SetTraceCallback(Tracer.IsEnabled() ? _traceCallback : null); + DriveLightOn = false; + return new LibVirtualJaguar.FrameInfo() { Player1 = GetButtons(controller, 1), @@ -158,6 +209,143 @@ namespace BizHawk.Emulation.Cores.Atari.Jaguar }; } + protected override void LoadStateBinaryInternal(BinaryReader reader) + { + SetMemoryCallbacks(); + _core.SetCdCallbacks(_cdTocCallback, _cdReadCallback); + } + public DisplayType Region { get; } + + public bool IsJaguarCD => _cd != null; + public bool DriveLightEnabled => IsJaguarCD; + public bool DriveLightOn { get; private set; } + + private readonly LibVirtualJaguar.CDTOCCallback _cdTocCallback; + private readonly LibVirtualJaguar.CDReadCallback _cdReadCallback; + +#if false // uh oh, we don't actually have multisession disc support, so... + private readonly Disc _cd; + private readonly DiscSectorReader _cdReader; + + private void CDTOCCallback(IntPtr dst) + { + var lastLeadOutTs = new Timestamp(_cd.TOC.LeadoutLBA + 150); + + var toc = new LibVirtualJaguar.TOC + { + Padding0 = 0, + Padding1 = 0, + NumSessions = (byte)(_cd.Structure.Sessions.Count - 1), + MinTrack = (byte)_cd.TOC.FirstRecordedTrackNumber, + MaxTrack = (byte)_cd.TOC.LastRecordedTrackNumber, + LastLeadOutMins = lastLeadOutTs.MIN, + LastLeadOutSecs = lastLeadOutTs.SEC, + LastLeadOutFrames = lastLeadOutTs.FRAC, + Tracks = new LibVirtualJaguar.TOC.Track[127], + }; + + var trackNum = 0; + for (int i = 1; i < _cd.Structure.Sessions.Count; i++) + { + var session = _cd.Structure.Sessions[i]; + for (int j = 1; j < session.InformationTrackCount; j++) + { + var track = session.Tracks[trackNum]; + toc.Tracks[i].TrackNum = (byte)track.Number; + var ts = new Timestamp(track.LBA + 150); + toc.Tracks[i].StartMins = ts.MIN; + toc.Tracks[i].StartSecs = ts.SEC; + toc.Tracks[i].StartFrames = ts.FRAC; + toc.Tracks[i].SessionNum = (byte)(i - 1); + var durTs = new Timestamp(track.NextTrack.LBA - track.LBA); + toc.Tracks[i].DurMins = durTs.MIN; + toc.Tracks[i].DurSecs = durTs.SEC; + toc.Tracks[i].DurFrames = durTs.FRAC; + trackNum++; + } + } + + Marshal.StructureToPtr(toc, dst, false); + } + + private void CDReadCallback(int lba, IntPtr dst) + { + var buf = new byte[2352]; + _cdReader.ReadLBA_2352(lba, buf, 0); + Marshal.Copy(buf, 0, dst, 2352); + DriveLightOn = true; + } + +#else + + private readonly Disc[] _cd; + private readonly DiscSectorReader[] _cdReader; + private int[] _cdLbaOffsets; + + private void CDTOCCallback(IntPtr dst) + { + var lastLeadOutTs = new Timestamp(_cd.Sum(c => c.TOC.LeadoutLBA) + _cd.Length * 150); + + var toc = new LibVirtualJaguar.TOC + { + Padding0 = 0, + Padding1 = 0, + NumSessions = (byte)_cd.Length, + MinTrack = (byte)_cd[0].TOC.FirstRecordedTrackNumber, + MaxTrack = (byte)(_cd[0].TOC.FirstRecordedTrackNumber + _cd.Sum(c => c.Session1.InformationTrackCount - c.TOC.FirstRecordedTrackNumber)), + LastLeadOutMins = lastLeadOutTs.MIN, + LastLeadOutSecs = lastLeadOutTs.SEC, + LastLeadOutFrames = lastLeadOutTs.FRAC, + Tracks = new LibVirtualJaguar.TOC.Track[127], + }; + + var trackNum = 0; + var lbaOffset = 0; + var trackOffset = 0; + _cdLbaOffsets = new int[_cd.Length]; + for (int i = 0; i < _cd.Length; i++) + { + var session = _cd[i].Session1; + for (int j = 0; j < session.InformationTrackCount; j++) + { + var track = session.Tracks[j + 1]; + toc.Tracks[trackNum].TrackNum = (byte)(trackOffset + track.Number); + var ts = new Timestamp(lbaOffset + track.LBA + 150); + toc.Tracks[trackNum].StartMins = ts.MIN; + toc.Tracks[trackNum].StartSecs = ts.SEC; + toc.Tracks[trackNum].StartFrames = ts.FRAC; + toc.Tracks[trackNum].SessionNum = (byte)i; + var durTs = new Timestamp(track.NextTrack.LBA - track.LBA); + toc.Tracks[trackNum].DurMins = durTs.MIN; + toc.Tracks[trackNum].DurSecs = durTs.SEC; + toc.Tracks[trackNum].DurFrames = durTs.FRAC; + trackNum++; + } + + trackOffset += session.InformationTrackCount; + lbaOffset += session.LeadoutTrack.LBA - session.FirstInformationTrack.LBA + 150; + _cdLbaOffsets[i] = lbaOffset; + } + + Marshal.StructureToPtr(toc, dst, false); + } + + private void CDReadCallback(int lba, IntPtr dst) + { + var buf = new byte[2352]; + for (int i = 0; i < _cdReader.Length; i++) + { + if (lba < _cdLbaOffsets[i]) + { + _cdReader[i].ReadLBA_2352(lba - (i == 0 ? 0 : _cdLbaOffsets[i - 1]), buf, 0); + break; + } + } + + Marshal.Copy(buf, 0, dst, 2352); + DriveLightOn = true; + } +#endif } } diff --git a/waterbox/virtualjaguar/BizInterface.cpp b/waterbox/virtualjaguar/BizInterface.cpp index 7bcfa307a7..6777c87392 100644 --- a/waterbox/virtualjaguar/BizInterface.cpp +++ b/waterbox/virtualjaguar/BizInterface.cpp @@ -4,6 +4,8 @@ #include "memory.h" #include "tom.h" #include "joystick.h" +#include "m68000/m68kinterface.h" + #include "blip_buf.h" #include @@ -36,7 +38,7 @@ static blip_t* blipL; static blip_t* blipR; static s16 latchL, latchR; -EXPORT bool Init(BizSettings* bizSettings, u8* boot, u8* rom, u32 sz) +static void InitCommon(BizSettings* bizSettings) { vjs.hardwareTypeNTSC = bizSettings->hardwareTypeNTSC; vjs.useJaguarBIOS = bizSettings->useJaguarBIOS; @@ -46,8 +48,12 @@ EXPORT bool Init(BizSettings* bizSettings, u8* boot, u8* rom, u32 sz) blipR = blip_new(1024); blip_set_rates(blipL, 48000, 44100); blip_set_rates(blipR, 48000, 44100); - JaguarInit(); +} + +EXPORT bool Init(BizSettings* bizSettings, u8* boot, u8* rom, u32 sz) +{ + InitCommon(bizSettings); if (!JaguarLoadFile(rom, sz)) { @@ -71,6 +77,26 @@ EXPORT bool Init(BizSettings* bizSettings, u8* boot, u8* rom, u32 sz) return true; } +void (*cd_toc_callback)(void * dest); +void (*cd_read_callback)(int32_t lba, void * dest); + +EXPORT void SetCdCallbacks(void (*ctc)(void * dest), void (*cdrc)(int32_t lba, void * dest)) +{ + cd_toc_callback = ctc; + cd_read_callback = cdrc; +} + +EXPORT void InitWithCd(BizSettings* bizSettings, u8* boot) +{ + InitCommon(bizSettings); + vjs.hardwareTypeAlpine = false; + + SET32(jaguarMainRAM, 0, 0x00200000); + memcpy(jagMemSpace + 0xE00000, boot, 0x20000); + + JaguarReset(); +} + extern uint16_t eeprom_ram[64]; extern bool eeprom_dirty; @@ -96,42 +122,42 @@ EXPORT void GetMemoryAreas(MemoryArea* m) m[0].Data = jaguarMainRAM; m[0].Name = "Main RAM"; m[0].Size = 0x200000; - m[0].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY; + m[0].Flags = MEMORYAREA_FLAGS_WORDSIZE2 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_PRIMARY; m[1].Data = eeprom_ram; m[1].Name = "EEPROM"; m[1].Size = sizeof(eeprom_ram); - m[1].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_SAVERAMMABLE; + m[1].Flags = MEMORYAREA_FLAGS_WORDSIZE2 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_SAVERAMMABLE; m[2].Data = gpuRAM; m[2].Name = "GPU RAM"; m[2].Size = 0x18000; - m[2].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE; + m[2].Flags = MEMORYAREA_FLAGS_WORDSIZE2 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN; m[3].Data = dspRAM; m[3].Name = "DSP RAM"; m[3].Size = 0x5000; - m[3].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE; + m[3].Flags = MEMORYAREA_FLAGS_WORDSIZE2 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN; m[4].Data = TOMGetRamPointer(); m[4].Name = "TOM RAM"; m[4].Size = 0x4000; - m[4].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE; + m[4].Flags = MEMORYAREA_FLAGS_WORDSIZE2 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN; m[5].Data = jaguarMainROM; m[5].Name = "ROM"; m[5].Size = jaguarROMSize; - m[5].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE; + m[5].Flags = MEMORYAREA_FLAGS_WORDSIZE2 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN; m[6].Data = jagMemSpace + 0xE00000; m[6].Name = "BIOS"; m[6].Size = 0x20000; - m[6].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE; + m[6].Flags = MEMORYAREA_FLAGS_WORDSIZE2 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN; m[7].Data = jagMemSpace; m[7].Name = "System Bus"; m[7].Size = 0xF20000; - m[7].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE; + m[7].Flags = MEMORYAREA_FLAGS_WORDSIZE2 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN; } struct MyFrameInfo : public FrameInfo @@ -141,7 +167,6 @@ struct MyFrameInfo : public FrameInfo }; bool lagged; -void (*inputcb)() = 0; EXPORT void FrameAdvance(MyFrameInfo* f) { @@ -195,7 +220,43 @@ EXPORT void FrameAdvance(MyFrameInfo* f) blip_read_samples(blipR, f->SoundBuffer + 1, f->Samples, 1); } +void (*InputCallback)() = 0; + EXPORT void SetInputCallback(void (*callback)()) { - inputcb = callback; + InputCallback = callback; +} + +void (*ReadCallback)(u32) = 0; +void (*WriteCallback)(u32) = 0; +void (*ExecuteCallback)(u32) = 0; + +EXPORT void SetMemoryCallback(u32 which, void (*callback)(u32)) +{ + switch (which) + { + case 0: ReadCallback = callback; break; + case 1: WriteCallback = callback; break; + case 2: ExecuteCallback = callback; break; + } +} + +void (*TraceCallback)(u32*) = 0; + +EXPORT void SetTraceCallback(void (*callback)(u32*)) +{ + TraceCallback = callback; +} + +EXPORT void GetRegisters(u32* regs) +{ + for (u32 i = 0; i < 18; i++) + { + regs[i] = m68k_get_reg(NULL, (m68k_register_t)i); + } +} + +EXPORT void SetRegister(u32 which, u32 val) +{ + m68k_set_reg((m68k_register_t)which, val); } diff --git a/waterbox/virtualjaguar/src/blitter.cpp b/waterbox/virtualjaguar/src/blitter.cpp index 0883f6de62..bfaf65d934 100644 --- a/waterbox/virtualjaguar/src/blitter.cpp +++ b/waterbox/virtualjaguar/src/blitter.cpp @@ -642,6 +642,11 @@ void blitter_generic(uint32_t cmd) uint32_t pixelSize = (size - 1) << 16; a2_x = (a2_x + pixelSize) & ~pixelSize; } + + a1_x += a1_step_x; + a1_y += a1_step_y; + a2_x += a2_step_x; + a2_y += a2_step_y; } WREG(A1_PIXEL, (a1_y & 0xFFFF0000) | ((a1_x >> 16) & 0xFFFF)); @@ -1670,20 +1675,20 @@ void BlitterMidsummer2(void) ADDBMUX(addb_x, addb_y, addbsel, a1_x, a1_y, a2_x, a2_y, a1_frac_x, a1_frac_y); ADDRADD(addq_x, addq_y, a1fracldi, adda_x, adda_y, addb_x, addb_y, modx, suba_x, suba_y); - if (a1addx == 3) - { - a1_frac_x = addq_x, a1_frac_y = addq_y; + if (a1addx == 3) + { + a1_frac_x = addq_x, a1_frac_y = addq_y; - addasel = 2, addbsel = 0, a1fracldi = false; - ADDAMUX(adda_x, adda_y, addasel, a1_step_x, a1_step_y, a1_stepf_x, a1_stepf_y, a2_step_x, a2_step_y, - a1_inc_x, a1_inc_y, a1_incf_x, a1_incf_y, adda_xconst, adda_yconst, addareg, suba_x, suba_y); - ADDBMUX(addb_x, addb_y, addbsel, a1_x, a1_y, a2_x, a2_y, a1_frac_x, a1_frac_y); - ADDRADD(addq_x, addq_y, a1fracldi, adda_x, adda_y, addb_x, addb_y, modx, suba_x, suba_y); + addasel = 2, addbsel = 0, a1fracldi = false; + ADDAMUX(adda_x, adda_y, addasel, a1_step_x, a1_step_y, a1_stepf_x, a1_stepf_y, a2_step_x, a2_step_y, + a1_inc_x, a1_inc_y, a1_incf_x, a1_incf_y, adda_xconst, adda_yconst, addareg, suba_x, suba_y); + ADDBMUX(addb_x, addb_y, addbsel, a1_x, a1_y, a2_x, a2_y, a1_frac_x, a1_frac_y); + ADDRADD(addq_x, addq_y, a1fracldi, adda_x, adda_y, addb_x, addb_y, modx, suba_x, suba_y); - a1_x = addq_x, a1_y = addq_y; - } - else - a1_x = addq_x, a1_y = addq_y; + a1_x = addq_x, a1_y = addq_y; + } + else + a1_x = addq_x, a1_y = addq_y; } if (a2_add) @@ -2054,7 +2059,7 @@ void DATA(uint64_t &wdata, uint8_t &dcomp, uint8_t &zcomp, bool &nowrite, uint8_t dech38el[2][8] = { { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; - int en = (dend & 0x3F ? 1 : 0); + int en = (dend & 0x3F ? 1 : 0); uint8_t e_coarse = decl38e[en][(dend & 0x38) >> 3]; uint8_t e_fine = decl38e[(e_coarse & 0x01) ^ 0x01][dend & 0x07]; e_fine &= 0xFE; diff --git a/waterbox/virtualjaguar/src/cdhle.cpp b/waterbox/virtualjaguar/src/cdhle.cpp new file mode 100644 index 0000000000..9ec7457a2a --- /dev/null +++ b/waterbox/virtualjaguar/src/cdhle.cpp @@ -0,0 +1,427 @@ +#include "cdhle.h" +#include "gpu.h" +#include "memory.h" +#include "jaguar.h" +#include "event.h" +#include "m68000/m68kinterface.h" + +#include +#include +#include + +#define SET_ERR() jaguarMainRAM[0x3E00] = -1 +#define NO_ERR() jaguarMainRAM[0x3E00] = 0 + +#define TOC_BASE_ADDR 0x2C00 + +static bool cd_setup; +static bool cd_initm; +static bool cd_muted; +static bool cd_paused; +static uint8_t cd_mode; +static uint8_t cd_osamp; + +static bool cd_is_reading; +static uint32_t cd_read_addr_start; +static uint32_t cd_read_addr_end; +static int32_t cd_read_lba; +static uint8_t cd_buf2352[2352 * 4]; // also 64 * 147 +static uint32_t cd_buf_block_num; + +extern void (*cd_toc_callback)(void * dest); +extern void (*cd_read_callback)(int32_t lba, void * dest); +extern bool jaguarCdInserted; + +struct Track { + uint8_t track_num; + uint8_t start_mins; + uint8_t start_secs; + uint8_t start_frames; + uint8_t session_num; + uint8_t dur_mins; + uint8_t dur_secs; + uint8_t dur_frames; +}; + +struct TOC { + uint8_t padding_0; + uint8_t padding_1; + uint8_t num_sessions; + uint8_t min_track_num; + uint8_t max_track_num; + uint8_t last_lead_out_mins; + uint8_t last_lead_out_secs; + uint8_t last_lead_out_frames; + Track tracks[127]; +}; + +static_assert(sizeof(TOC) == 1024); + +static TOC toc; +static uint32_t cd_boot_addr; +static uint32_t cd_boot_len; +static int32_t cd_boot_lba; +static uint32_t cd_boot_off; + +void CDHLEInit(void) +{ + if (cd_toc_callback && cd_read_callback) + { + jaguarCdInserted = true; + cd_toc_callback(&toc); + assert(toc.num_sessions >= 2); // need at least 2 sessions + int32_t bootTrackNum = 0; + for (uint32_t i = 0; i < 127; i++) + { + if (toc.tracks[i].session_num == 1) + { + bootTrackNum = i; + break; + } + } + assert(bootTrackNum != 0); + Track& bootTrack = toc.tracks[bootTrackNum]; + int32_t startLba = bootTrack.start_mins * 4500 + bootTrack.start_secs * 75 + bootTrack.start_frames - 150; + fprintf(stderr, "timecode: %02d:%02d:%02d, startLba %04X\n", bootTrack.start_mins, bootTrack.start_secs, bootTrack.start_frames, startLba); + int32_t numLbas = bootTrack.dur_mins * 4500 + bootTrack.dur_secs * 75 + bootTrack.dur_frames; + uint8_t buf2352[2352]; + bool foundHeader = false; + for (int32_t i = 0; i < numLbas; i++) + { + cd_read_callback(startLba + i, buf2352); + static const char* atariHeader = "ATARI APPROVED DATA HEADER ATRI\x20"; + + for (uint32_t j = 0; j < (2352 - 32 - 4 - 4); j += 2) + { + if (!memcmp(&buf2352[j], atariHeader, 32)) + { + fprintf(stderr, "startLba + i %04X\n", startLba + i); + cd_boot_addr = GET32(buf2352, j + 32); + cd_boot_len = GET32(buf2352, j + 32 + 4); + cd_boot_lba = startLba + i; + cd_boot_off = j + 32 + 4 + 4; + foundHeader = true; + break; + } + } + + if (foundHeader) break; + } + assert(foundHeader); + } + + CDHLEReset(); +} + +void CDHLEReset(void) +{ + cd_setup = false; + cd_initm = false; + cd_muted = false; + cd_paused = false; + cd_mode = 0; + cd_osamp = 0; + + if (cd_read_callback) + { + // copy TOC to RAM + memcpy(&jaguarMainRAM[TOC_BASE_ADDR], &toc, sizeof(TOC)); + + // copy bootcode to RAM + // maximum of 64KiB is allowed + uint32_t dstStart = cd_boot_addr; + uint32_t dstEnd = cd_boot_addr + (cd_boot_len > 0x10000 ? 0x10000 : cd_boot_len); + int32_t lba = cd_boot_lba; + uint8_t buf2352[2352]; + + cd_read_callback(lba++, buf2352); + for (uint32_t i = cd_boot_off; i < 2352 && dstStart < dstEnd;) + { + uint32_t end = (i + 64) > 2352 ? 2352 : (i + 64); + for (; i < end; i++, dstStart++) + { + JaguarWriteByte(dstStart, buf2352[i], GPU); + } + } + + while (dstStart < dstEnd) + { + cd_read_callback(lba++, buf2352); + for (uint32_t i = 0; i < 2352 && dstStart < dstEnd;) + { + uint32_t end = (i + 64) > 2352 ? 2352 : (i + 64); + for (; i < end; i++, dstStart++) + { + JaguarWriteByte(dstStart, buf2352[i], GPU); + } + } + } + + cd_read_addr_start = dstStart; + + SET32(jaguarMainRAM, 4, cd_boot_addr); + SET16(jaguarMainRAM, 0x3004, 0x0403); // BIOS VER + } +} + +void CDHLEDone(void) +{ +} + +static void CDSendBlock(void) +{ + if (cd_buf_block_num == 0) + { + for (uint32_t i = 0; i < 4; i++) + { + cd_read_callback(cd_read_lba + i, &cd_buf2352[2352 * i]); + } + + cd_read_lba += 4; + } + + // send one block of data + for (uint32_t i = 0; i < 64; i++) + { + JaguarWriteByte(cd_read_addr_start + i, cd_buf2352[i + 64 * cd_buf_block_num], GPU); + } + + cd_read_addr_start += 64; + cd_buf_block_num = (cd_buf_block_num + 1) % 147; + + if (cd_read_addr_start >= cd_read_addr_end) + { + cd_is_reading = false; + } +} + +static void CDHLECallback(void) +{ + RemoveCallback(CDHLECallback); + if (cd_is_reading) + { + if (!cd_paused) + { + CDSendBlock(); + } + SetCallbackTime(CDHLECallback, 180 >> (cd_mode & 1)); + } +} + +static void CD_init(void); +static void CD_mode(void); +static void CD_ack(void); +static void CD_jeri(void); +static void CD_spin(void); +static void CD_stop(void); +static void CD_mute(void); +static void CD_umute(void); +static void CD_paus(void); +static void CD_upaus(void); +static void CD_read(void); +static void CD_uread(void); +static void CD_setup(void); +static void CD_ptr(void); +static void CD_osamp(void); +static void CD_getoc(void); +static void CD_initm(void); +static void CD_initf(void); +static void CD_switch(void); + +static void (* CD_functions[19])() = +{ + CD_init, CD_mode, CD_ack, CD_jeri, + CD_spin, CD_stop, CD_mute, CD_umute, + CD_paus, CD_upaus, CD_read, CD_uread, + CD_setup, CD_ptr, CD_osamp, CD_getoc, + CD_initm, CD_initf, CD_switch, +}; + +static const char * cd_func_strs[19] = { + "CD_init", "CD_mode", "CD_ack", "CD_jeri", + "CD_spin", "CD_stop", "CD_mute", "CD_umute", + "CD_paus", "CD_upaus", "CD_read", "CD_uread", + "CD_setup", "CD_ptr", "CD_osamp", "CD_getoc", + "CD_initm", "CD_initf", "CD_switch", +}; + +void CDHLEHook(uint32_t which) +{ + //fprintf(stderr, "CD HLE Hook %s\n", cd_func_strs[which]); + CD_functions[which](); +} + +static void CD_init(void) +{ + fprintf(stderr, "do CD_init"); + cd_initm = false; +} + +static void CD_mode(void) +{ + // bit 0 = speed (0 = single, 1 = double) + // bit 1 = mode (0 = audio, 1 = data) + cd_mode = m68k_get_reg(NULL, M68K_REG_D0) & 3; + fprintf(stderr, "CD_mode mode = %d, speed = %d\n", cd_mode >> 1, cd_mode & 1); + NO_ERR(); +} + +static void CD_ack(void) +{ + if (!cd_paused) + { + while (cd_is_reading) + { + CDSendBlock(); + } + } + + NO_ERR(); +} + +static void CD_jeri(void) +{ + fprintf(stderr, "CD_jeri called %d!!!\n", m68k_get_reg(NULL, M68K_REG_D0) & 1); +} + +static void CD_spin(void) +{ + NO_ERR(); +} + +static void CD_stop(void) +{ + NO_ERR(); +} + +static void CD_mute(void) +{ + if (!(cd_mode & 2)) + { + cd_muted = true; + NO_ERR(); + } + else + { + SET_ERR(); + } +} + +static void CD_umute(void) +{ + if (!(cd_mode & 2)) + { + cd_muted = false; + NO_ERR(); + } + else + { + SET_ERR(); + } +} + +static void CD_paus(void) +{ + cd_paused = true; + NO_ERR(); +} + +static void CD_upaus(void) +{ + cd_paused = false; + NO_ERR(); +} + +static void CD_read(void) +{ + uint32_t dstStart = m68k_get_reg(NULL, M68K_REG_A0); + uint32_t dstEnd = m68k_get_reg(NULL, M68K_REG_A1); + + fprintf(stderr, "CD READ: dstStart %08X, dstEnd %08X\n", dstStart, dstEnd); + + if (dstEnd <= dstStart) + { + fprintf(stderr, "CD READ ERROR: dstEnd < dstStart\n"); + SET_ERR(); + return; + } + + uint32_t timecode = m68k_get_reg(NULL, M68K_REG_D0); + + uint32_t frames = timecode & 0xFF; + uint32_t seconds = (timecode >> 8) & 0xFF; + uint32_t minutes = (timecode >> 16) & 0xFF; + + fprintf(stderr, "CD READ: is seeking %d, mins %02d, secs %02d, frames %02d\n", !!(timecode & 0x80000000), minutes, seconds, frames); + + if (frames >= 75 || seconds >= 60 || minutes >= 73) + { + fprintf(stderr, "CD READ ERROR: timecode too large\n"); + SET_ERR(); + return; + } + + if (!(timecode & 0x80000000)) + { + cd_is_reading = true; + cd_read_addr_start = dstStart; + cd_read_addr_end = dstEnd; + cd_read_lba = (minutes * 60 + seconds) * 75 + frames - 150; + cd_buf_block_num = 0; + RemoveCallback(CDHLECallback); + SetCallbackTime(CDHLECallback, 180 >> (cd_mode & 1)); + } + + NO_ERR(); +} + +static void CD_uread(void) +{ + if (cd_is_reading) + { + cd_is_reading = false; + NO_ERR(); + } + else + { + SET_ERR(); + } +} + +static void CD_setup(void) +{ + // probaby don't really care about this + cd_setup = true; +} + +static void CD_ptr(void) +{ + m68k_set_reg(M68K_REG_A0, cd_read_addr_start); + m68k_set_reg(M68K_REG_A1, 0); +} + +static void CD_osamp(void) +{ + cd_osamp = m68k_get_reg(NULL, M68K_REG_D0) & 3; + NO_ERR(); +} + +static void CD_getoc(void) +{ + // this is for debugging only, retail games will not call this +} + +static void CD_initm(void) +{ + cd_initm = true; +} + +static void CD_initf(void) +{ + cd_initm = false; +} + +static void CD_switch(void) +{ + // not supporting CD switching, so +} diff --git a/waterbox/virtualjaguar/src/cdhle.h b/waterbox/virtualjaguar/src/cdhle.h new file mode 100644 index 0000000000..a9f056fad9 --- /dev/null +++ b/waterbox/virtualjaguar/src/cdhle.h @@ -0,0 +1,12 @@ +#ifndef __CDHLE_H__ +#define __CDHLE_H__ + +#include + +void CDHLEInit(void); +void CDHLEReset(void); +void CDHLEDone(void); + +void CDHLEHook(uint32_t which); + +#endif // __CDHLE_H__ diff --git a/waterbox/virtualjaguar/src/dsp.cpp b/waterbox/virtualjaguar/src/dsp.cpp index aec6800a40..ab4a700e36 100644 --- a/waterbox/virtualjaguar/src/dsp.cpp +++ b/waterbox/virtualjaguar/src/dsp.cpp @@ -219,14 +219,6 @@ static uint8_t dsp_ram_8[0x2000]; #define BRANCH_CONDITION(x) dsp_branch_condition_table[(x) + ((jaguar_flags & 7) << 5)] -static uint32_t dsp_in_exec = 0; -static uint32_t dsp_releaseTimeSlice_flag = 0; - -void DSPReleaseTimeslice(void) -{ - dsp_releaseTimeSlice_flag = 1; -} - void dsp_build_branch_condition_table(void) { for(int i=0; i<65536; i++) @@ -457,7 +449,6 @@ void DSPWriteLong(uint32_t offset, uint32_t data, uint32_t who) if (JERRYIRQEnabled(IRQ2_DSP)) { JERRYSetPendingIRQ(IRQ2_DSP); - DSPReleaseTimeslice(); m68k_set_irq(2); } data &= ~CPUINT; @@ -466,7 +457,6 @@ void DSPWriteLong(uint32_t offset, uint32_t data, uint32_t who) if (data & DSPINT0) { m68k_end_timeslice(); - DSPReleaseTimeslice(); DSPSetIRQLine(DSPIRQ_CPU, ASSERT_LINE); data &= ~DSPINT0; } @@ -478,8 +468,6 @@ void DSPWriteLong(uint32_t offset, uint32_t data, uint32_t who) { if (who == M68K) m68k_end_timeslice(); - else if (who == DSP) - DSPReleaseTimeslice(); } break; } @@ -505,7 +493,7 @@ void DSPUpdateRegisterBanks(void) int bank = (dsp_flags & REGPAGE); if (dsp_flags & IMASK) - bank = 0; // IMASK forces main bank to be bank 0 + bank = 0; if (bank) dsp_reg = dsp_reg_bank_1, dsp_alternate_reg = dsp_reg_bank_0; @@ -593,7 +581,6 @@ void DSPReset(void) dsp_data_organization = 0xFFFFFFFF; dsp_control = 0x00002000; dsp_div_control = 0x00000000; - dsp_in_exec = 0; dsp_reg = dsp_reg_bank_0; dsp_alternate_reg = dsp_reg_bank_1; @@ -617,9 +604,6 @@ void DSPDone(void) // void DSPExec(int32_t cycles) { - dsp_releaseTimeSlice_flag = 0; - dsp_in_exec++; - while (cycles > 0 && DSP_RUNNING) { if (IMASKCleared) @@ -636,8 +620,6 @@ void DSPExec(int32_t cycles) dsp_opcode[index](); cycles -= dsp_opcode_cycles[index]; } - - dsp_in_exec--; } // @@ -959,7 +941,7 @@ static void dsp_opcode_normi(void) static void dsp_opcode_mmult(void) { int count = dsp_matrix_control&0x0f; - uint32_t addr = dsp_pointer_to_matrix; // in the dsp ram + uint32_t addr = dsp_pointer_to_matrix; int64_t accum = 0; uint32_t res; diff --git a/waterbox/virtualjaguar/src/dsp.h b/waterbox/virtualjaguar/src/dsp.h index 7f471b919c..063cd95ddd 100644 --- a/waterbox/virtualjaguar/src/dsp.h +++ b/waterbox/virtualjaguar/src/dsp.h @@ -22,7 +22,6 @@ uint32_t DSPReadLong(uint32_t offset, uint32_t who = UNKNOWN); void DSPWriteByte(uint32_t offset, uint8_t data, uint32_t who = UNKNOWN); void DSPWriteWord(uint32_t offset, uint16_t data, uint32_t who = UNKNOWN); void DSPWriteLong(uint32_t offset, uint32_t data, uint32_t who = UNKNOWN); -void DSPReleaseTimeslice(void); bool DSPIsRunning(void); // Exported vars diff --git a/waterbox/virtualjaguar/src/gpu.cpp b/waterbox/virtualjaguar/src/gpu.cpp index bc850dfc6a..f30eeb1845 100644 --- a/waterbox/virtualjaguar/src/gpu.cpp +++ b/waterbox/virtualjaguar/src/gpu.cpp @@ -295,7 +295,7 @@ uint16_t GPUReadWord(uint32_t offset, uint32_t who) uint32_t data = GPUReadLong(offset & 0xFFFFFFFC, who); - if (offset & 0x02) // Cases 0 & 2... + if (offset & 0x02) return data & 0xFFFF; else return data >> 16; @@ -436,12 +436,13 @@ void GPUWriteWord(uint32_t offset, uint16_t data, uint32_t who) // // GPU dword access (write) // -void GPUWriteLong(uint32_t offset, uint32_t data, uint32_t who/*=UNKNOWN*/) +void GPUWriteLong(uint32_t offset, uint32_t data, uint32_t who) { if ((offset >= GPU_WORK_RAM_BASE) && (offset <= GPU_WORK_RAM_BASE + 0x0FFC)) { offset &= 0xFFF; SET32(gpu_ram_8, offset, data); + return; } else if ((offset >= GPU_CONTROL_RAM_BASE) && (offset <= GPU_CONTROL_RAM_BASE + 0x1C)) { @@ -465,7 +466,6 @@ void GPUWriteLong(uint32_t offset, uint32_t data, uint32_t who/*=UNKNOWN*/) gpu_matrix_control = data; break; case 0x08: - // This can only point to long aligned addresses gpu_pointer_to_matrix = data & 0xFFFFFFFC; break; case 0x0C: @@ -492,7 +492,6 @@ void GPUWriteLong(uint32_t offset, uint32_t data, uint32_t who/*=UNKNOWN*/) { GPUSetIRQLine(0, ASSERT_LINE); m68k_end_timeslice(); - DSPReleaseTimeslice(); data &= ~0x04; } @@ -509,7 +508,11 @@ void GPUWriteLong(uint32_t offset, uint32_t data, uint32_t who/*=UNKNOWN*/) gpu_div_control = data; break; } + + return; } + + JaguarWriteLong(offset, data, who); } // @@ -851,9 +854,9 @@ static void gpu_opcode_pack(void) { uint32_t val = RN; - if (IMM_1 == 0) // Pack + if (IMM_1 == 0) RN = ((val >> 10) & 0x0000F000) | ((val >> 5) & 0x00000F00) | (val & 0x000000FF); - else // Unpack + else RN = ((val & 0x0000F000) << 10) | ((val & 0x00000F00) << 5) | (val & 0x000000FF); } diff --git a/waterbox/virtualjaguar/src/jaguar.cpp b/waterbox/virtualjaguar/src/jaguar.cpp index a34fe0bf2e..f33045b78a 100644 --- a/waterbox/virtualjaguar/src/jaguar.cpp +++ b/waterbox/virtualjaguar/src/jaguar.cpp @@ -20,6 +20,7 @@ #include #include #include "blitter.h" +#include "cdhle.h" #include "cdrom.h" #include "dac.h" #include "dsp.h" @@ -50,10 +51,34 @@ extern uint8_t jagMemSpace[]; uint32_t jaguarMainROMCRC32, jaguarROMSize, jaguarRunAddress; bool jaguarCartInserted = false; bool lowerField = false; +bool jaguarCdInserted = false; void M68KInstructionHook(void) { - // TODO: Trace/Exec callback + if (jaguarCdInserted) + { + uint32_t pc = m68k_get_reg(NULL, M68K_REG_PC); + if (pc >= 0x3000 && pc <= 0x306C) + { + CDHLEHook((pc - 0x3000) / 6); + // return + uint32_t sp = m68k_get_reg(NULL, M68K_REG_SP); + m68k_set_reg(M68K_REG_PC, m68k_read_memory_32(sp)); + m68k_set_reg(M68K_REG_SP, sp + 4); + } + } + + if (__builtin_expect(!!TraceCallback, false)) + { + uint32_t regs[18]; + for (uint32_t i = 0; i < 18; i++) + { + regs[i] = m68k_get_reg(NULL, (m68k_register_t)i); + } + TraceCallback(regs); + } + + MAYBE_CALLBACK(ExecuteCallback, m68k_get_reg(NULL, M68K_REG_PC)); } // @@ -73,6 +98,8 @@ int irq_ack_handler(int level) unsigned int m68k_read_memory_8(unsigned int address) { + MAYBE_CALLBACK(ReadCallback, address); + address &= 0x00FFFFFF; unsigned int retVal = 0; @@ -97,6 +124,8 @@ unsigned int m68k_read_memory_8(unsigned int address) unsigned int m68k_read_memory_16(unsigned int address) { + MAYBE_CALLBACK(ReadCallback, address); + address &= 0x00FFFFFF; unsigned int retVal = 0; @@ -129,6 +158,8 @@ unsigned int m68k_read_memory_16(unsigned int address) unsigned int m68k_read_memory_32(unsigned int address) { + MAYBE_CALLBACK(ReadCallback, address); + address &= 0x00FFFFFF; uint32_t retVal = 0; @@ -148,6 +179,8 @@ unsigned int m68k_read_memory_32(unsigned int address) void m68k_write_memory_8(unsigned int address, unsigned int value) { + MAYBE_CALLBACK(WriteCallback, address); + address &= 0x00FFFFFF; if ((address >= 0x000000) && (address <= 0x1FFFFF)) @@ -164,6 +197,8 @@ void m68k_write_memory_8(unsigned int address, unsigned int value) void m68k_write_memory_16(unsigned int address, unsigned int value) { + MAYBE_CALLBACK(WriteCallback, address); + address &= 0x00FFFFFF; if ((address >= 0x000000) && (address <= 0x1FFFFE)) @@ -388,6 +423,7 @@ void JaguarInit(void) TOMInit(); JERRYInit(); CDROMInit(); + CDHLEInit(); } void HalflineCallback(void); @@ -410,7 +446,8 @@ void JaguarReset(void) GPUReset(); DSPReset(); CDROMReset(); - m68k_pulse_reset(); + CDHLEReset(); + m68k_pulse_reset(); lowerField = false; SetCallbackTime(HalflineCallback, (vjs.hardwareTypeNTSC ? 31.777777777 : 32.0)); @@ -418,6 +455,7 @@ void JaguarReset(void) void JaguarDone(void) { + CDHLEDone(); CDROMDone(); GPUDone(); DSPDone(); diff --git a/waterbox/virtualjaguar/src/jaguar.h b/waterbox/virtualjaguar/src/jaguar.h index 1c38fa0f5d..d1e474935b 100644 --- a/waterbox/virtualjaguar/src/jaguar.h +++ b/waterbox/virtualjaguar/src/jaguar.h @@ -36,4 +36,16 @@ extern bool jaguarCartInserted; #define ASSERT_LINE 1 #define CLEAR_LINE 0 +// Callbacks + +extern void (*InputCallback)(); + +extern void (*ReadCallback)(uint32_t); +extern void (*WriteCallback)(uint32_t); +extern void (*ExecuteCallback)(uint32_t); + +extern void (*TraceCallback)(uint32_t*); + +#define MAYBE_CALLBACK(callback, ...) do { if (__builtin_expect(!!callback, false)) callback(__VA_ARGS__); } while (0) + #endif // __JAGUAR_H__ diff --git a/waterbox/virtualjaguar/src/joystick.cpp b/waterbox/virtualjaguar/src/joystick.cpp index 46d33d2950..d2caf07e38 100644 --- a/waterbox/virtualjaguar/src/joystick.cpp +++ b/waterbox/virtualjaguar/src/joystick.cpp @@ -27,7 +27,6 @@ uint8_t joypad1Buttons[21]; static bool joysticksEnabled; extern bool lagged; -extern void (*inputcb)(); void JoystickInit(void) { @@ -53,8 +52,7 @@ void JoystickDone(void) uint16_t JoystickReadWord(uint32_t offset) { lagged = false; - if (__builtin_expect(!!inputcb, false)) - inputcb(); + MAYBE_CALLBACK(InputCallback); uint8_t joypad0Offset[16] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0C, 0xFF, 0xFF, 0xFF, 0x08, 0xFF, 0x04, 0x00, 0xFF diff --git a/waterbox/virtualjaguar/src/op.cpp b/waterbox/virtualjaguar/src/op.cpp index 3de2d3ef19..09d2c48419 100644 --- a/waterbox/virtualjaguar/src/op.cpp +++ b/waterbox/virtualjaguar/src/op.cpp @@ -53,7 +53,7 @@ static uint8_t op_blend_cr[0x10000]; static uint32_t op_pointer; -int32_t phraseWidthToPixels[8] = { 64, 32, 16, 8, 4, 2, 0, 0 }; +static const int32_t phraseWidthToPixels[8] = { 64, 32, 16, 8, 4, 2, 0, 0 }; // // Object Processor initialization