diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index 8bdf4f1068..e36d02411e 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -290,6 +290,7 @@ + diff --git a/BizHawk.Emulation/CPUs/68000/MC68000.cs b/BizHawk.Emulation/CPUs/68000/MC68000.cs index 0eaacf3b58..4c6970b8d4 100644 --- a/BizHawk.Emulation/CPUs/68000/MC68000.cs +++ b/BizHawk.Emulation/CPUs/68000/MC68000.cs @@ -96,6 +96,8 @@ namespace BizHawk.Emulation.CPUs.M68000 public Action WriteWord; public Action WriteLong; + public Action IrqCallback; + // Initialization public MC68000() @@ -141,6 +143,7 @@ namespace BizHawk.Emulation.CPUs.M68000 PC = ReadLong((24 + Interrupt) * 4); // Jump to interrupt vector InterruptMaskLevel = Interrupt; // Set interrupt mask to level currently being entered Interrupt = 0; // "ack" interrupt. Note: this is wrong. + IrqCallback(InterruptMaskLevel); // Invoke the "Interrupt accepted" callback handler } int prevCycles = PendingCycles; diff --git a/BizHawk.Emulation/CPUs/Native68000/Musashi.cs b/BizHawk.Emulation/CPUs/Native68000/Musashi.cs new file mode 100644 index 0000000000..4664494f60 --- /dev/null +++ b/BizHawk.Emulation/CPUs/Native68000/Musashi.cs @@ -0,0 +1,73 @@ +using System; +using System.Runtime.InteropServices; + +namespace Native68000 +{ + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate int VdpCallback(int i); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate uint ReadCallback(uint a); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate void WriteCallback(uint a, uint v); + + public class Musashi + { + [DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void RegisterVdpCallback(IntPtr callback); + + [DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void RegisterRead8(IntPtr callback); + + [DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void RegisterRead16(IntPtr callback); + + [DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void RegisterRead32(IntPtr callback); + + [DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void RegisterWrite8(IntPtr callback); + + [DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void RegisterWrite16(IntPtr callback); + + [DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void RegisterWrite32(IntPtr callback); + + [DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void Init(); + + [DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void Reset(); + + [DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void SetIRQ(int level); + + [DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern int Execute(int cycles); + + [DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern int QueryCpuState(int regcode); + + public static int D0 { get { return QueryCpuState(0); } } + public static int D1 { get { return QueryCpuState(1); } } + public static int D2 { get { return QueryCpuState(2); } } + public static int D3 { get { return QueryCpuState(3); } } + public static int D4 { get { return QueryCpuState(4); } } + public static int D5 { get { return QueryCpuState(5); } } + public static int D6 { get { return QueryCpuState(6); } } + public static int D7 { get { return QueryCpuState(7); } } + + public static int A0 { get { return QueryCpuState(8); } } + public static int A1 { get { return QueryCpuState(9); } } + public static int A2 { get { return QueryCpuState(10); } } + public static int A3 { get { return QueryCpuState(11); } } + public static int A4 { get { return QueryCpuState(12); } } + public static int A5 { get { return QueryCpuState(13); } } + public static int A6 { get { return QueryCpuState(14); } } + public static int A7 { get { return QueryCpuState(15); } } + + public static int PC { get { return QueryCpuState(16); } } + public static int SR { get { return QueryCpuState(17); } } + public static int SP { get { return QueryCpuState(18); } } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.Render.cs b/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.Render.cs index ff257aace6..3ffa15927b 100644 --- a/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.Render.cs +++ b/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.Render.cs @@ -14,6 +14,8 @@ namespace BizHawk.Emulation.Consoles.Sega byte[] PriorityBuffer = new byte[320]; + static readonly byte[] PalXlatTable = { 0, 0, 36, 36, 73, 73, 109, 109, 145, 145, 182, 182, 219, 219, 255, 255 }; + // TODO, should provide startup register values. public void RenderLine() { diff --git a/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.cs b/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.cs index b4971483f1..b91192bca8 100644 --- a/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.cs +++ b/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.cs @@ -37,7 +37,7 @@ namespace BizHawk.Emulation.Consoles.Sega bool ControlWordPending; ushort VdpDataAddr; byte VdpDataCode; - + const int CommandVramRead = 0; const int CommandVramWrite = 1; const int CommandCramWrite = 3; @@ -45,8 +45,14 @@ namespace BizHawk.Emulation.Consoles.Sega const int CommandVsramWrite = 5; const int CommandCramRead = 7; - static readonly byte[] PalXlatTable = { 0, 0, 36, 36, 73, 73, 109, 109, 145, 145, 182, 182, 219, 219, 255, 255 }; - + public ushort VdpStatusWord = 0x3400; + public const int StatusVerticalInterruptPending = 0x80; + public const int StatusSpriteOverflow = 0x40; + public const int StatusSpriteCollision = 0x20; + public const int StatusOddFrame = 0x10; + public const int StatusVerticalBlanking = 0x08; + public const int StatusHorizBlanking = 0x04; + public ushort ReadVdp(int addr) { switch (addr) @@ -58,7 +64,8 @@ namespace BizHawk.Emulation.Consoles.Sega case 6: return ReadVdpControl(); default: - throw new Exception("HV Counter read...."); + //throw new Exception("HV Counter read...."); + return 0; } } @@ -135,10 +142,8 @@ namespace BizHawk.Emulation.Consoles.Sega public ushort ReadVdpControl() { - ushort value = 0x3400; // fixed bits per genvdp.txt TODO test on everdrive, I guess. - value |= 0x0200; // Fifo empty - //Log.Note("VDP", "VDP: Control Read {0:X4}", value); - return value; + VdpStatusWord |= 0x0200; // Fifo empty + return VdpStatusWord; } public void WriteVdpData(ushort data) @@ -210,7 +215,7 @@ namespace BizHawk.Emulation.Consoles.Sega { case 0x00: // Mode Set Register 1 Registers[register] = data; - //Log.Note("VDP", "HINT enabled: " + HInterruptsEnabled); + Log.Error("VDP", "HINT enabled: " + HInterruptsEnabled); break; case 0x01: // Mode Set Register 2 diff --git a/BizHawk.Emulation/Consoles/Sega/Genesis/Genesis.cs b/BizHawk.Emulation/Consoles/Sega/Genesis/Genesis.cs index 2dbf2ed402..cbbd5b144f 100644 --- a/BizHawk.Emulation/Consoles/Sega/Genesis/Genesis.cs +++ b/BizHawk.Emulation/Consoles/Sega/Genesis/Genesis.cs @@ -1,9 +1,13 @@ -using System; +#define MUSASHI + +using System; using System.Collections.Generic; using System.IO; using BizHawk.Emulation.CPUs.M68000; using BizHawk.Emulation.CPUs.Z80; using BizHawk.Emulation.Sound; +using Native68000; +using System.Runtime.InteropServices; namespace BizHawk.Emulation.Consoles.Sega { @@ -58,6 +62,16 @@ namespace BizHawk.Emulation.Consoles.Sega // 320 are active display, the remaining 160 are horizontal blanking. // A total of 3420 mclks per line, but 2560 mclks are active display and 860 mclks are blanking. +#if MUSASHI + VdpCallback _vdp; + ReadCallback read8; + ReadCallback read16; + ReadCallback read32; + WriteCallback write8; + WriteCallback write16; + WriteCallback write32; +#endif + public Genesis(GameInfo game, byte[] rom) { CoreOutputComm = new CoreOutputComm(); @@ -75,6 +89,27 @@ namespace BizHawk.Emulation.Consoles.Sega MainCPU.WriteByte = WriteByte; MainCPU.WriteWord = WriteWord; MainCPU.WriteLong = WriteLong; + MainCPU.IrqCallback = InterruptCallback; + + // ---------------------- musashi ----------------------- +#if MUSASHI + _vdp = vdpcallback; + read8 = Read8; + read16 = Read16; + read32 = Read32; + write8 = Write8; + write16 = Write16; + write32 = Write32; + + Musashi.RegisterVdpCallback(Marshal.GetFunctionPointerForDelegate(_vdp)); + Musashi.RegisterRead8(Marshal.GetFunctionPointerForDelegate(read8)); + Musashi.RegisterRead16(Marshal.GetFunctionPointerForDelegate(read16)); + Musashi.RegisterRead32(Marshal.GetFunctionPointerForDelegate(read32)); + Musashi.RegisterWrite8(Marshal.GetFunctionPointerForDelegate(write8)); + Musashi.RegisterWrite16(Marshal.GetFunctionPointerForDelegate(write16)); + Musashi.RegisterWrite32(Marshal.GetFunctionPointerForDelegate(write32)); +#endif + // ---------------------- musashi ----------------------- SoundCPU.ReadMemory = ReadMemoryZ80; SoundCPU.WriteMemory = WriteMemoryZ80; @@ -87,14 +122,19 @@ namespace BizHawk.Emulation.Consoles.Sega RomData[i] = rom[i]; SetupMemoryDomains(); - MainCPU.Reset(); - } +#if MUSASHI + Musashi.Init(); + Musashi.Reset(); +#else + MainCPU.Reset(); +#endif + } public void FrameAdvance(bool render) { lagged = true; - Frame++; + Controller.UpdateControls(Frame++); PSG.BeginFrame(SoundCPU.TotalExecutedCycles); YM2612.BeginFrame(SoundCPU.TotalExecutedCycles); for (VDP.ScanLine = 0; VDP.ScanLine < 262; VDP.ScanLine++) @@ -104,22 +144,23 @@ namespace BizHawk.Emulation.Consoles.Sega if (VDP.ScanLine < 224) VDP.RenderLine(); - MainCPU.ExecuteCycles(487); // 488?? + Exec68k(487); if (Z80Runnable) { - //Console.WriteLine("running z80"); SoundCPU.ExecuteCycles(228); SoundCPU.Interrupt = false; } else { SoundCPU.TotalExecutedCycles += 228; // I emulate the YM2612 synced to Z80 clock, for better or worse. Keep the timer going even if Z80 isn't running. } - if (VDP.ScanLine == 224) + if (VDP.ScanLine == 224) { - MainCPU.ExecuteCycles(16);// stupid crap to sync with genesis plus for log testing + VDP.VdpStatusWord |= GenVDP.StatusVerticalInterruptPending; + VDP.VdpStatusWord |= GenVDP.StatusVerticalBlanking; + Exec68k(16); // this is stupidly wrong. // End-frame stuff - if (VDP.VInterruptEnabled) - MainCPU.Interrupt = 6; + if (VDP.VInterruptEnabled) + Set68kIrq(6); if (Z80Runnable) SoundCPU.Interrupt = true; @@ -128,7 +169,8 @@ namespace BizHawk.Emulation.Consoles.Sega PSG.EndFrame(SoundCPU.TotalExecutedCycles); YM2612.EndFrame(SoundCPU.TotalExecutedCycles); - Controller.UpdateControls(Frame++); + unchecked { VDP.VdpStatusWord &= (ushort)~GenVDP.StatusVerticalBlanking; } + if (lagged) { _lagcount++; @@ -138,6 +180,35 @@ namespace BizHawk.Emulation.Consoles.Sega islag = false; } + void Exec68k(int cycles) + { +#if MUSASHI + Musashi.Execute(cycles); +#else + MainCPU.ExecuteCycles(cycles); +#endif + } + + void Set68kIrq(int irq) + { +#if MUSASHI + Musashi.SetIRQ(irq); +#else + MainCPU.Interrupt = irq; +#endif + } + + int vdpcallback(int level) // Musashi handler + { + InterruptCallback(level); + return -1; + } + + void InterruptCallback(int level) + { + unchecked { VDP.VdpStatusWord &= (ushort)~GenVDP.StatusVerticalInterruptPending; } + } + public CoreInputComm CoreInputComm { get; set; } public CoreOutputComm CoreOutputComm { get; private set; } diff --git a/BizHawk.MultiClient/output/MusashiDLL.dll b/BizHawk.MultiClient/output/MusashiDLL.dll new file mode 100644 index 0000000000..7b947296df Binary files /dev/null and b/BizHawk.MultiClient/output/MusashiDLL.dll differ