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