switch snes core back to external process. more refined this time. support use of performance core.

This commit is contained in:
zeromus 2012-12-25 20:36:04 +00:00
parent 259364bb33
commit 8a69a4ebe0
92 changed files with 2272 additions and 1105 deletions

View File

@ -339,6 +339,7 @@
<Compile Include="Consoles\Nintendo\NES\PPU.regs.cs" />
<Compile Include="Consoles\Nintendo\NES\PPU.run.cs" />
<Compile Include="Consoles\Nintendo\NES\Unif.cs" />
<Compile Include="Consoles\Nintendo\SNES\LibsnesApi.cs" />
<Compile Include="Consoles\Nintendo\SNES\LibsnesCore.cs" />
<Compile Include="Consoles\Nintendo\SNES\SnesColors.cs" />
<Compile Include="Consoles\Nintendo\SNES\SNESGraphicsDecoder.cs" />

View File

@ -0,0 +1,722 @@
using System;
using System.Linq;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Pipes;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.IO.MemoryMappedFiles;
namespace BizHawk.Emulation.Consoles.Nintendo.SNES
{
public unsafe class LibsnesApi : IDisposable
{
//this wouldve been the ideal situation to learn protocol buffers, but since the number of messages here is so limited, it took less time to roll it by hand.
//todo - could optimize a lot of the apis once we decide to commit to this. will we? then we wont be able to debug bsnes as well
// well, we could refactor it a lot and let the debuggable static dll version be the one that does annoying workarounds
//todo - more intelligent use of buffers to avoid so many copies (especially framebuffer from bsnes? supply framebuffer to-be-used to libsnes? same for audiobuffer)
//todo - refactor to use a smarter set of pipe reader and pipe writer classes
//todo - combine messages / tracecallbacks into one system with a channel number enum additionally
//todo - consider refactoring bsnes to allocate memory blocks through the interface, and set ours up to allocate from a large arena of shared memory.
// this is a lot of work, but it will be some decent speedups. who wouldve ever thought to make an emulator this way? I will, from now on...
//todo - use a reader/writer ring buffer for communication instead of pipe
//todo - when exe wrapper is fully baked, put it into mingw so we can just have libsneshawk.exe without a separate dll. it hardly needs any debugging presently, it should be easy to maintain.
//speedups to deploy later:
//todo - convey rom data faster than pipe blob (use shared memory) (WARNING: right now our general purpose shared memory is only 1MB. maybe wait until ring buffer IPC)
//todo - collapse input messages to one IPC operation. right now theresl ike 30 of them
//todo - collect all memory block names whenever a memory block is alloc/dealloced. that way we avoid the overhead when using them for gui stuff (gfx debugger, hex editor)
string InstanceName;
Process process;
NamedPipeServerStream pipe;
BinaryWriter bwPipe;
BinaryReader brPipe;
MemoryMappedFile mmf;
MemoryMappedViewAccessor mmva;
byte* mmvaPtr;
public enum eMessage : int
{
eMessage_Complete,
eMessage_snes_library_id,
eMessage_snes_library_revision_major,
eMessage_snes_library_revision_minor,
eMessage_snes_init,
eMessage_snes_power,
eMessage_snes_reset,
eMessage_snes_run,
eMessage_snes_term,
eMessage_snes_unload_cartridge,
//snes_set_cartridge_basename, //not used
eMessage_snes_load_cartridge_normal,
eMessage_snes_load_cartridge_super_game_boy,
//incoming from bsnes
eMessage_snes_cb_video_refresh,
eMessage_snes_cb_input_poll,
eMessage_snes_cb_input_state,
eMessage_snes_cb_input_notify,
eMessage_snes_cb_audio_sample,
eMessage_snes_cb_scanlineStart,
eMessage_snes_cb_path_request,
eMessage_snes_cb_trace_callback,
eMessage_snes_get_region,
eMessage_snes_get_memory_size,
eMessage_snes_get_memory_data,
eMessage_peek,
eMessage_poke,
eMessage_snes_serialize_size,
eMessage_snes_serialize,
eMessage_snes_unserialize,
eMessage_snes_poll_message,
eMessage_snes_dequeue_message,
eMessage_snes_set_color_lut,
eMessage_snes_enable_trace,
eMessage_snes_enable_scanline,
eMessage_snes_enable_audio,
eMessage_snes_set_layer_enable,
eMessage_snes_set_backdropColor,
eMessage_snes_peek_logical_register,
eMessage_snes_allocSharedMemory,
eMessage_snes_freeSharedMemory,
eMessage_GetMemoryIdName,
};
static bool DryRun(string exePath)
{
ProcessStartInfo oInfo = new ProcessStartInfo(exePath, "Bongizong");
oInfo.WorkingDirectory = Path.GetDirectoryName(exePath);
oInfo.UseShellExecute = false;
oInfo.CreateNoWindow = true;
oInfo.RedirectStandardOutput = true;
oInfo.RedirectStandardError = true;
Process proc = System.Diagnostics.Process.Start(oInfo);
string result = proc.StandardError.ReadToEnd();
proc.WaitForExit();
//yongou chonganong nongo tong rongeadong
//pongigong chong hongi nonge songe
if (result == "Honga Wongkong" && proc.ExitCode == 0x16817)
return true;
return false;
}
static HashSet<string> okExes = new HashSet<string>();
public LibsnesApi(string exePath)
{
//make sure we've checked this exe for OKness.. the dry run should keep us from freezing up or crashing weirdly if the external process isnt correct
if (!okExes.Contains(exePath))
{
bool ok = DryRun(exePath);
if (!ok)
throw new InvalidOperationException(string.Format("Couldn't launch {0} to run SNES core. Not sure why this would have happened. Try redownloading BizHawk first.", Path.GetFileName(exePath)));
okExes.Add(exePath);
}
InstanceName = "libsneshawk_" + Guid.NewGuid().ToString();
//use this to get a debug console with libsnes output
//InstanceName = "console-" + InstanceName;
var pipeName = InstanceName;
mmf = MemoryMappedFile.CreateNew(pipeName, 1024 * 1024);
mmva = mmf.CreateViewAccessor();
mmva.SafeMemoryMappedViewHandle.AcquirePointer(ref mmvaPtr);
pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.None, 1024 * 1024, 1024);
process = new Process();
process.StartInfo.WorkingDirectory = Path.GetDirectoryName(exePath);
process.StartInfo.FileName = exePath;
process.StartInfo.Arguments = pipeName;
process.StartInfo.ErrorDialog = true;
process.Start();
//TODO - start a thread to wait for process to exit and gracefully handle errors? how about the pipe?
pipe.WaitForConnection();
bwPipe = new BinaryWriter(pipe);
brPipe = new BinaryReader(pipe);
}
public void Dispose()
{
process.Kill();
process.Dispose();
process = null;
pipe.Dispose();
mmva.Dispose();
mmf.Dispose();
}
void WritePipeString(string str)
{
WritePipeBlob(System.Text.Encoding.ASCII.GetBytes(str));
}
byte[] ReadPipeBlob()
{
int len = brPipe.ReadInt32();
var ret = new byte[len];
brPipe.Read(ret, 0, len);
return ret;
}
void WritePipeBlob(byte[] blob)
{
bwPipe.Write(blob.Length);
bwPipe.Write(blob);
}
public int MessageCounter;
void WritePipeMessage(eMessage msg)
{
MessageCounter++;
bwPipe.Write((int)msg);
}
eMessage ReadPipeMessage()
{
return (eMessage)brPipe.ReadInt32();
}
string ReadPipeString()
{
int len = brPipe.ReadInt32();
var bytes = brPipe.ReadBytes(len);
return System.Text.ASCIIEncoding.ASCII.GetString(bytes);
}
public string snes_library_id()
{
WritePipeMessage(eMessage.eMessage_snes_library_id);
return ReadPipeString();
}
public uint snes_library_revision_major()
{
WritePipeMessage(eMessage.eMessage_snes_library_revision_major);
return brPipe.ReadUInt32();
}
public uint snes_library_revision_minor()
{
WritePipeMessage(eMessage.eMessage_snes_library_revision_minor);
return brPipe.ReadUInt32();
}
public void snes_init()
{
WritePipeMessage(eMessage.eMessage_snes_init);
WaitForCompletion();
}
public void snes_power() { WritePipeMessage(eMessage.eMessage_snes_power); }
public void snes_reset() { WritePipeMessage(eMessage.eMessage_snes_reset); }
public void snes_run()
{
WritePipeMessage(eMessage.eMessage_snes_run);
WaitForCompletion();
}
public void snes_term() { WritePipeMessage(eMessage.eMessage_snes_term); }
public void snes_unload_cartridge() { WritePipeMessage(eMessage.eMessage_snes_unload_cartridge); }
public bool snes_load_cartridge_super_game_boy(string rom_xml, byte[] rom_data, uint rom_size, string dmg_xml, byte[] dmg_data, uint dmg_size)
{
WritePipeMessage(eMessage.eMessage_snes_load_cartridge_super_game_boy);
WritePipeString(rom_xml ?? "");
WritePipeBlob(rom_data);
WritePipeString(rom_xml ?? "");
WritePipeBlob(dmg_data);
//not a very obvious order.. because we do tons of work immediately after the last param goes down and need to answer messages
WaitForCompletion();
bool ret = brPipe.ReadBoolean();
return ret;
}
public bool snes_load_cartridge_normal(string rom_xml, byte[] rom_data)
{
WritePipeMessage(eMessage.eMessage_snes_load_cartridge_normal);
WritePipeString(rom_xml ?? "");
WritePipeBlob(rom_data);
//not a very obvious order.. because we do tons of work immediately after the last param goes down and need to answer messages
WaitForCompletion();
bool ret = brPipe.ReadBoolean();
return ret;
}
public SNES_REGION snes_get_region()
{
WritePipeMessage(eMessage.eMessage_snes_get_region);
return (SNES_REGION)brPipe.ReadByte();
}
public int snes_get_memory_size(SNES_MEMORY id)
{
WritePipeMessage(eMessage.eMessage_snes_get_memory_size);
bwPipe.Write((int)id);
return brPipe.ReadInt32();
}
string MemoryNameForId(SNES_MEMORY id)
{
WritePipeMessage(eMessage.eMessage_GetMemoryIdName);
bwPipe.Write((uint)id);
return ReadPipeString();
}
public byte* snes_get_memory_data(SNES_MEMORY id)
{
string name = MemoryNameForId(id);
var smb = SharedMemoryBlocks[name];
return (byte*)smb.Ptr;
}
public byte peek(SNES_MEMORY id, uint addr)
{
WritePipeMessage(eMessage.eMessage_peek);
bwPipe.Write((uint)id);
bwPipe.Write(addr);
return brPipe.ReadByte();
}
public void poke(SNES_MEMORY id, uint addr, byte val)
{
WritePipeMessage(eMessage.eMessage_poke);
bwPipe.Write((uint)id);
bwPipe.Write(addr);
bwPipe.Write(val);
}
public int snes_serialize_size()
{
WritePipeMessage(eMessage.eMessage_snes_serialize_size);
return brPipe.ReadInt32();
}
[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static unsafe extern void* CopyMemory(void* dest, void* src, ulong count);
public bool snes_serialize(IntPtr data, int size)
{
WritePipeMessage(eMessage.eMessage_snes_serialize);
bwPipe.Write(size);
bwPipe.Write(0); //mapped memory location to serialize to
WaitForCompletion(); //serialize/unserialize can cause traces to get called (because serialize can cause execution?)
bool ret = brPipe.ReadBoolean();
if (ret)
{
CopyMemory(data.ToPointer(), mmvaPtr, (ulong)size);
}
return ret;
}
public bool snes_unserialize(IntPtr data, int size)
{
WritePipeMessage(eMessage.eMessage_snes_unserialize);
CopyMemory(mmvaPtr, data.ToPointer(), (ulong)size);
bwPipe.Write(size);
bwPipe.Write(0); //mapped memory location to serialize from
WaitForCompletion(); //serialize/unserialize can cause traces to get called (because serialize can cause execution?)
bool ret = brPipe.ReadBoolean();
return ret;
}
int snes_poll_message()
{
WritePipeMessage(eMessage.eMessage_snes_poll_message);
return brPipe.ReadInt32();
}
public bool HasMessage { get { return snes_poll_message() != -1; } }
public string DequeueMessage()
{
WritePipeMessage(eMessage.eMessage_snes_dequeue_message);
return ReadPipeString();
}
public void snes_set_color_lut(IntPtr colors)
{
int len = 4 * 16 * 32768;
byte[] buf = new byte[len];
Marshal.Copy(colors, buf, 0, len);
WritePipeMessage(eMessage.eMessage_snes_set_color_lut);
WritePipeBlob(buf);
}
public void snes_set_layer_enable(int layer, int priority, bool enable)
{
WritePipeMessage(eMessage.eMessage_snes_set_layer_enable);
bwPipe.Write(layer);
bwPipe.Write(priority);
bwPipe.Write(enable);
}
public void snes_set_backdropColor(int backdropColor)
{
WritePipeMessage(eMessage.eMessage_snes_set_backdropColor);
bwPipe.Write(backdropColor);
}
public int snes_peek_logical_register(SNES_REG reg)
{
WritePipeMessage(eMessage.eMessage_snes_peek_logical_register);
bwPipe.Write((int)reg);
return brPipe.ReadInt32();
}
void WaitForCompletion()
{
for (; ; )
{
var msg = ReadPipeMessage();
MessageCounter++;
//Console.WriteLine(msg);
switch (msg)
{
case eMessage.eMessage_Complete:
return;
case eMessage.eMessage_snes_cb_video_refresh:
{
int width = brPipe.ReadInt32();
int height = brPipe.ReadInt32();
bwPipe.Write(0); //offset in mapped memory buffer
brPipe.ReadBoolean(); //dummy synchronization
if (video_refresh != null)
{
video_refresh((int*)mmvaPtr, width, height);
}
break;
}
case eMessage.eMessage_snes_cb_input_poll:
break;
case eMessage.eMessage_snes_cb_input_state:
{
int port = brPipe.ReadInt32();
int device = brPipe.ReadInt32();
int index = brPipe.ReadInt32();
int id = brPipe.ReadInt32();
ushort ret = 0;
if (input_state != null)
ret = input_state(port, device, index, id);
bwPipe.Write(ret);
break;
}
case eMessage.eMessage_snes_cb_input_notify:
{
int index = brPipe.ReadInt32();
if (input_notify != null)
input_notify(index);
break;
}
case eMessage.eMessage_snes_cb_audio_sample:
{
int nsamples = brPipe.ReadInt32();
bwPipe.Write(0); //location to store audio buffer in
brPipe.ReadInt32(); //dummy synchronization
if (audio_sample != null)
{
ushort* audiobuffer = ((ushort*)mmvaPtr);
for (int i = 0; i < nsamples; )
{
ushort left = audiobuffer[i++];
ushort right = audiobuffer[i++];
audio_sample(left, right);
}
}
bwPipe.Write(0); //dummy synchronization
brPipe.ReadInt32(); //dummy synchronization
break;
}
case eMessage.eMessage_snes_cb_scanlineStart:
{
int line = brPipe.ReadInt32();
if (scanlineStart != null)
scanlineStart(line);
//we have to notify the unmanaged process that we're done peeking thruogh its memory and whatnot so it can proceed with emulation
WritePipeMessage(eMessage.eMessage_Complete);
break;
}
case eMessage.eMessage_snes_cb_path_request:
{
int slot = brPipe.ReadInt32();
string hint = ReadPipeString();
string ret = hint;
if (pathRequest != null)
hint = pathRequest(slot, hint);
WritePipeString(hint);
break;
}
case eMessage.eMessage_snes_cb_trace_callback:
{
var trace = ReadPipeString();
if (traceCallback != null)
traceCallback(trace);
break;
}
case eMessage.eMessage_snes_allocSharedMemory:
{
var smb = new SharedMemoryBlock();
smb.Name = ReadPipeString();
smb.Size = brPipe.ReadInt32();
smb.BlockName = InstanceName + smb.Name;
smb.Allocate();
if (SharedMemoryBlocks.ContainsKey(smb.Name))
{
throw new InvalidOperationException("Re-defined a shared memory block. Check bsnes init/shutdown code. Block name: " + smb.Name);
}
SharedMemoryBlocks[smb.Name] = smb;
WritePipeString(smb.BlockName);
break;
}
case eMessage.eMessage_snes_freeSharedMemory:
{
string name = ReadPipeString();
var smb = SharedMemoryBlocks[name];
smb.Dispose();
SharedMemoryBlocks.Remove(name);
break;
}
}
}
}
class SharedMemoryBlock : IDisposable
{
public string Name;
public string BlockName;
public int Size;
public MemoryMappedFile mmf;
public MemoryMappedViewAccessor mmva;
public byte* Ptr;
public void Allocate()
{
mmf = MemoryMappedFile.CreateNew(BlockName, Size);
mmva = mmf.CreateViewAccessor();
mmva.SafeMemoryMappedViewHandle.AcquirePointer(ref Ptr);
}
public void Dispose()
{
if (mmf == null) return;
mmva.Dispose();
mmf.Dispose();
mmf = null;
}
}
Dictionary<string, SharedMemoryBlock> SharedMemoryBlocks = new Dictionary<string, SharedMemoryBlock>();
snes_video_refresh_t video_refresh;
snes_input_poll_t input_poll;
snes_input_state_t input_state;
snes_input_notify_t input_notify;
snes_audio_sample_t audio_sample;
snes_scanlineStart_t scanlineStart;
snes_path_request_t pathRequest;
snes_trace_t traceCallback;
public void snes_set_video_refresh(snes_video_refresh_t video_refresh) { this.video_refresh = video_refresh; }
public void snes_set_input_poll(snes_input_poll_t input_poll) { this.input_poll = input_poll; }
public void snes_set_input_state(snes_input_state_t input_state) { this.input_state = input_state; }
public void snes_set_input_notify(snes_input_notify_t input_notify) { this.input_notify = input_notify; }
public void snes_set_audio_sample(snes_audio_sample_t audio_sample)
{
this.audio_sample = audio_sample;
WritePipeMessage(eMessage.eMessage_snes_enable_audio);
bwPipe.Write(audio_sample != null);
}
public void snes_set_path_request(snes_path_request_t pathRequest) { this.pathRequest = pathRequest; }
public void snes_set_scanlineStart(snes_scanlineStart_t scanlineStart)
{
this.scanlineStart = scanlineStart;
WritePipeMessage(eMessage.eMessage_snes_enable_scanline);
bwPipe.Write(scanlineStart != null);
}
public void snes_set_trace_callback(snes_trace_t callback)
{
this.traceCallback = callback;
WritePipeMessage(eMessage.eMessage_snes_enable_trace);
bwPipe.Write(callback != null);
}
public delegate void snes_video_refresh_t(int* data, int width, int height);
public delegate void snes_input_poll_t();
public delegate ushort snes_input_state_t(int port, int device, int index, int id);
public delegate void snes_input_notify_t(int index);
public delegate void snes_audio_sample_t(ushort left, ushort right);
public delegate void snes_scanlineStart_t(int line);
public delegate string snes_path_request_t(int slot, string hint);
public delegate void snes_trace_t(string msg);
public enum SNES_REG : int
{
//$2105
BG_MODE = 0,
BG3_PRIORITY = 1,
BG1_TILESIZE = 2,
BG2_TILESIZE = 3,
BG3_TILESIZE = 4,
BG4_TILESIZE = 5,
//$2107
BG1_SCADDR = 10,
BG1_SCSIZE = 11,
//$2108
BG2_SCADDR = 12,
BG2_SCSIZE = 13,
//$2109
BG3_SCADDR = 14,
BG3_SCSIZE = 15,
//$210A
BG4_SCADDR = 16,
BG4_SCSIZE = 17,
//$210B
BG1_TDADDR = 20,
BG2_TDADDR = 21,
//$210C
BG3_TDADDR = 22,
BG4_TDADDR = 23,
//$2133 SETINI
SETINI_MODE7_EXTBG = 30,
SETINI_HIRES = 31,
SETINI_OVERSCAN = 32,
SETINI_OBJ_INTERLACE = 33,
SETINI_SCREEN_INTERLACE = 34,
//$2130 CGWSEL
CGWSEL_COLORMASK = 40,
CGWSEL_COLORSUBMASK = 41,
CGWSEL_ADDSUBMODE = 42,
CGWSEL_DIRECTCOLOR = 43,
//$2101 OBSEL
OBSEL_NAMEBASE = 50,
OBSEL_NAMESEL = 51,
OBSEL_SIZE = 52,
//$2131 CGADSUB
CGADSUB_MODE = 60,
CGADSUB_HALF = 61,
CGADSUB_BG4 = 62,
CGADSUB_BG3 = 63,
CGADSUB_BG2 = 64,
CGADSUB_BG1 = 65,
CGADSUB_OBJ = 66,
CGADSUB_BACKDROP = 67,
//$212C TM
TM_BG1 = 70,
TM_BG2 = 71,
TM_BG3 = 72,
TM_BG4 = 73,
TM_OBJ = 74,
//$212D TM
TS_BG1 = 80,
TS_BG2 = 81,
TS_BG3 = 82,
TS_BG4 = 83,
TS_OBJ = 84,
//Mode7 regs
M7SEL_REPEAT = 90,
M7SEL_HFLIP = 91,
M7SEL_VFLIP = 92,
M7A = 93,
M7B = 94,
M7C = 95,
M7D = 96,
M7X = 97,
M7Y = 98,
//BG scroll regs
BG1HOFS = 100,
BG1VOFS = 101,
BG2HOFS = 102,
BG2VOFS = 103,
BG3HOFS = 104,
BG3VOFS = 105,
BG4HOFS = 106,
BG4VOFS = 107,
M7HOFS = 108,
M7VOFS = 109,
}
public enum SNES_MEMORY : uint
{
CARTRIDGE_RAM = 0,
CARTRIDGE_RTC = 1,
BSX_RAM = 2,
BSX_PRAM = 3,
SUFAMI_TURBO_A_RAM = 4,
SUFAMI_TURBO_B_RAM = 5,
GAME_BOY_RAM = 6,
GAME_BOY_RTC = 7,
WRAM = 100,
APURAM = 101,
VRAM = 102,
OAM = 103,
CGRAM = 104,
SYSBUS = 200,
LOGICAL_REGS = 201
}
public enum SNES_REGION : byte
{
NTSC = 0,
PAL = 1,
}
public enum SNES_DEVICE : uint
{
NONE = 0,
JOYPAD = 1,
MULTITAP = 2,
MOUSE = 3,
SUPER_SCOPE = 4,
JUSTIFIER = 5,
JUSTIFIERS = 6,
SERIAL_CABLE = 7
}
public enum SNES_DEVICE_ID : uint
{
JOYPAD_B = 0,
JOYPAD_Y = 1,
JOYPAD_SELECT = 2,
JOYPAD_START = 3,
JOYPAD_UP = 4,
JOYPAD_DOWN = 5,
JOYPAD_LEFT = 6,
JOYPAD_RIGHT = 7,
JOYPAD_A = 8,
JOYPAD_X = 9,
JOYPAD_L = 10,
JOYPAD_R = 11
}
}
}

View File

@ -9,7 +9,6 @@
using System;
using System.Linq;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Collections.Generic;
@ -17,290 +16,6 @@ using System.Runtime.InteropServices;
namespace BizHawk.Emulation.Consoles.Nintendo.SNES
{
public unsafe static class LibsnesDll
{
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern string snes_library_id();
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int snes_library_revision_major();
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int snes_library_revision_minor();
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_init();
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_power();
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_reset();
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_run();
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_term();
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_unload_cartridge();
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_cartridge_basename(string basename);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U1)]
public static extern bool snes_load_cartridge_normal(
[MarshalAs(UnmanagedType.LPStr)]
string rom_xml,
[MarshalAs(UnmanagedType.LPArray)]
byte[] rom_data,
uint rom_size);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U1)]
public static extern bool snes_load_cartridge_super_game_boy(
[MarshalAs(UnmanagedType.LPStr)]
string rom_xml,
[MarshalAs(UnmanagedType.LPArray)]
byte[] rom_data,
uint rom_size,
[MarshalAs(UnmanagedType.LPStr)]
string dmg_xml,
[MarshalAs(UnmanagedType.LPArray)]
byte[] dmg_data,
uint dmg_size);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void snes_video_refresh_t(int* data, int width, int height);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void snes_input_poll_t();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate ushort snes_input_state_t(int port, int device, int index, int id);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void snes_input_notify_t(int index);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void snes_audio_sample_t(ushort left, ushort right);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void snes_scanlineStart_t(int line);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate string snes_path_request_t(int slot, string hint);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void snes_trace_t(string msg);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_video_refresh(snes_video_refresh_t video_refresh);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_input_poll(snes_input_poll_t input_poll);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_input_state(snes_input_state_t input_state);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_input_notify(snes_input_notify_t input_notify);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_audio_sample(snes_audio_sample_t audio_sample);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_scanlineStart(snes_scanlineStart_t scanlineStart);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_path_request(snes_path_request_t scanlineStart);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U1)]
public static extern bool snes_check_cartridge(
[MarshalAs(UnmanagedType.LPArray)] byte[] rom_data,
int rom_size);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U1)]
public static extern SNES_REGION snes_get_region();
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int snes_get_memory_size(SNES_MEMORY id);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr snes_get_memory_data(SNES_MEMORY id);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte bus_read(uint addr);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void bus_write(uint addr, byte val);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int snes_serialize_size();
[return: MarshalAs(UnmanagedType.U1)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool snes_serialize(IntPtr data, int size);
[return: MarshalAs(UnmanagedType.U1)]
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool snes_unserialize(IntPtr data, int size);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int snes_poll_message();
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_dequeue_message(IntPtr strBuffer);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_trace_callback(snes_trace_t callback);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_color_lut(IntPtr colors);
public static bool HasMessage { get { return snes_poll_message() != -1; } }
public static string DequeueMessage()
{
int len = snes_poll_message();
sbyte* temp = stackalloc sbyte[len + 1];
temp[len] = 0;
snes_dequeue_message(new IntPtr(temp));
return new string(temp);
}
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_layer_enable(int layer, int priority,
[MarshalAs(UnmanagedType.U1)]
bool enable
);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_backdropColor(int backdropColor);
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int snes_peek_logical_register(SNES_REG reg);
public enum SNES_REG : int
{
//$2105
BG_MODE = 0,
BG3_PRIORITY = 1,
BG1_TILESIZE = 2,
BG2_TILESIZE = 3,
BG3_TILESIZE = 4,
BG4_TILESIZE = 5,
//$2107
BG1_SCADDR = 10,
BG1_SCSIZE = 11,
//$2108
BG2_SCADDR = 12,
BG2_SCSIZE = 13,
//$2109
BG3_SCADDR = 14,
BG3_SCSIZE = 15,
//$210A
BG4_SCADDR = 16,
BG4_SCSIZE = 17,
//$210B
BG1_TDADDR = 20,
BG2_TDADDR = 21,
//$210C
BG3_TDADDR = 22,
BG4_TDADDR = 23,
//$2133 SETINI
SETINI_MODE7_EXTBG = 30,
SETINI_HIRES = 31,
SETINI_OVERSCAN = 32,
SETINI_OBJ_INTERLACE = 33,
SETINI_SCREEN_INTERLACE = 34,
//$2130 CGWSEL
CGWSEL_COLORMASK = 40,
CGWSEL_COLORSUBMASK = 41,
CGWSEL_ADDSUBMODE = 42,
CGWSEL_DIRECTCOLOR = 43,
//$2101 OBSEL
OBSEL_NAMEBASE = 50,
OBSEL_NAMESEL = 51,
OBSEL_SIZE = 52,
//$2131 CGADSUB
CGADSUB_MODE = 60,
CGADSUB_HALF = 61,
CGADSUB_BG4 = 62,
CGADSUB_BG3 = 63,
CGADSUB_BG2 = 64,
CGADSUB_BG1 = 65,
CGADSUB_OBJ = 66,
CGADSUB_BACKDROP = 67,
//$212C TM
TM_BG1 = 70,
TM_BG2 = 71,
TM_BG3 = 72,
TM_BG4 = 73,
TM_OBJ = 74,
//$212D TM
TS_BG1 = 80,
TS_BG2 = 81,
TS_BG3 = 82,
TS_BG4 = 83,
TS_OBJ = 84,
//Mode7 regs
M7SEL_REPEAT = 90,
M7SEL_HFLIP = 91,
M7SEL_VFLIP = 92,
M7A = 93,
M7B = 94,
M7C = 95,
M7D = 96,
M7X = 97,
M7Y = 98,
//BG scroll regs
BG1HOFS = 100,
BG1VOFS = 101,
BG2HOFS = 102,
BG2VOFS = 103,
BG3HOFS = 104,
BG3VOFS = 105,
BG4HOFS = 106,
BG4VOFS = 107,
M7HOFS = 108,
M7VOFS = 109,
}
public enum SNES_MEMORY : uint
{
CARTRIDGE_RAM = 0,
CARTRIDGE_RTC = 1,
BSX_RAM = 2,
BSX_PRAM = 3,
SUFAMI_TURBO_A_RAM = 4,
SUFAMI_TURBO_B_RAM = 5,
GAME_BOY_RAM = 6,
GAME_BOY_RTC = 7,
WRAM = 100,
APURAM = 101,
VRAM = 102,
OAM = 103,
CGRAM = 104,
}
public enum SNES_REGION : byte
{
NTSC = 0,
PAL = 1,
}
public enum SNES_DEVICE : uint
{
NONE = 0,
JOYPAD = 1,
MULTITAP = 2,
MOUSE = 3,
SUPER_SCOPE = 4,
JUSTIFIER = 5,
JUSTIFIERS = 6,
SERIAL_CABLE = 7
}
public enum SNES_DEVICE_ID : uint
{
JOYPAD_B = 0,
JOYPAD_Y = 1,
JOYPAD_SELECT = 2,
JOYPAD_START = 3,
JOYPAD_UP = 4,
JOYPAD_DOWN = 5,
JOYPAD_LEFT = 6,
JOYPAD_RIGHT = 7,
JOYPAD_A = 8,
JOYPAD_X = 9,
JOYPAD_L = 10,
JOYPAD_R = 11
}
}
public class ScanlineHookManager
{
@ -352,26 +67,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
if (disposed) return;
disposed = true;
disposedSaveRam = ReadSaveRam();
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_video_refresh(null);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_input_poll(null);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_input_state(null);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_audio_sample(null);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_scanlineStart(null);
LibsnesDll.snes_unload_cartridge();
LibsnesDll.snes_term();
api.snes_unload_cartridge();
api.snes_term();
resampler.Dispose();
api.Dispose();
}
//save the save memory before disposing the core, so we can pull from it in the future after the core is terminated
//that will be necessary to get it saving to disk
byte[] disposedSaveRam;
//we can only have one active snes core at a time, due to libsnes being so static.
//so we'll track the current one here and detach the previous one whenever a new one is booted up.
static LibsnesCore CurrLibsnesCore;
public class MyScanlineHookManager : ScanlineHookManager
{
@ -390,8 +91,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
void OnScanlineHooksChanged()
{
if (disposed) return;
if (ScanlineHookManager.HookCount == 0) LibsnesDll.snes_set_scanlineStart(null);
else LibsnesDll.snes_set_scanlineStart(scanlineStart_cb);
if (ScanlineHookManager.HookCount == 0) api.snes_set_scanlineStart(null);
else api.snes_set_scanlineStart(scanlineStart_cb);
}
void snes_scanlineStart(int line)
@ -432,48 +133,39 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
CurrPalette = pal;
int[] tmp = SnesColors.GetLUT(pal);
fixed (int* p = &tmp[0])
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_color_lut((IntPtr)p);
api.snes_set_color_lut((IntPtr)p);
}
public LibsnesApi api;
public LibsnesCore(CoreComm comm)
{
CoreComm = comm;
api = new LibsnesApi(CoreComm.SNES_ExePath);
api.snes_init();
}
LibsnesApi.snes_scanlineStart_t scanlineStart_cb;
LibsnesApi.snes_trace_t tracecb;
LibsnesApi.snes_audio_sample_t soundcb;
public void Load(GameInfo game, byte[] romData, byte[] sgbRomData, bool DeterministicEmulation)
{
//attach this core as the current
if (CurrLibsnesCore != null)
CurrLibsnesCore.Dispose();
CurrLibsnesCore = this;
ScanlineHookManager = new MyScanlineHookManager(this);
LibsnesDll.snes_init();
api.snes_init();
//LibsnesDll.snes_set_cartridge_basename(@);
api.snes_set_video_refresh(snes_video_refresh);
api.snes_set_input_poll(snes_input_poll);
api.snes_set_input_state(snes_input_state);
api.snes_set_input_notify(snes_input_notify);
api.snes_set_path_request(snes_path_request);
scanlineStart_cb = new LibsnesApi.snes_scanlineStart_t(snes_scanlineStart);
tracecb = new LibsnesApi.snes_trace_t(snes_trace);
vidcb = new LibsnesDll.snes_video_refresh_t(snes_video_refresh);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_video_refresh(vidcb);
pollcb = new LibsnesDll.snes_input_poll_t(snes_input_poll);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_input_poll(pollcb);
inputcb = new LibsnesDll.snes_input_state_t(snes_input_state);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_input_state(inputcb);
notifycb = new LibsnesDll.snes_input_notify_t(snes_input_notify);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_input_notify(notifycb);
soundcb = new LibsnesDll.snes_audio_sample_t(snes_audio_sample);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_audio_sample(soundcb);
pathRequest_cb = new LibsnesDll.snes_path_request_t(snes_path_request);
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_path_request(pathRequest_cb);
scanlineStart_cb = new LibsnesDll.snes_scanlineStart_t(snes_scanlineStart);
tracecb = new LibsnesDll.snes_trace_t(snes_trace);
soundcb = new LibsnesApi.snes_audio_sample_t(snes_audio_sample);
api.snes_set_audio_sample(soundcb);
// set default palette. Should be overridden by frontend probably
SetPalette(SnesColors.ColorType.BizHawk);
@ -494,17 +186,17 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
{
IsSGB = true;
SystemId = "SNES";
if (!LibsnesDll.snes_load_cartridge_super_game_boy(null, sgbRomData, (uint)sgbRomData.Length, null, romData, (uint)romData.Length))
if (!api.snes_load_cartridge_super_game_boy(null, sgbRomData, (uint)sgbRomData.Length, null, romData, (uint)romData.Length))
throw new Exception("snes_load_cartridge_super_game_boy() failed");
}
else
{
SystemId = "SNES";
if (!LibsnesDll.snes_load_cartridge_normal(null, romData, (uint)romData.Length))
if (!api.snes_load_cartridge_normal(null, romData))
throw new Exception("snes_load_cartridge_normal() failed");
}
if (LibsnesDll.snes_get_region() == LibsnesDll.SNES_REGION.NTSC)
if (api.snes_get_region() == LibsnesApi.SNES_REGION.NTSC)
{
//similar to what aviout reports from snes9x and seems logical from bsnes first principles. bsnes uses that numerator (ntsc master clockrate) for sure.
CoreComm.VsyncNum = 21477272;
@ -516,9 +208,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
CoreComm.VsyncDen = 1;
}
CoreComm.CpuTraceAvailable = false;
CoreComm.CpuTraceAvailable = true;
LibsnesDll.snes_power();
api.snes_power();
SetupMemoryDomains(romData);
@ -536,38 +228,28 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
}
}
//must keep references to these so that they wont get garbage collected
LibsnesDll.snes_video_refresh_t vidcb;
LibsnesDll.snes_input_poll_t pollcb;
LibsnesDll.snes_input_state_t inputcb;
LibsnesDll.snes_input_notify_t notifycb;
LibsnesDll.snes_audio_sample_t soundcb;
LibsnesDll.snes_scanlineStart_t scanlineStart_cb;
LibsnesDll.snes_path_request_t pathRequest_cb;
LibsnesDll.snes_trace_t tracecb;
ushort snes_input_state(int port, int device, int index, int id)
{
if (!nocallbacks && CoreComm.InputCallback != null) CoreComm.InputCallback();
//Console.WriteLine("{0} {1} {2} {3}", port, device, index, id);
string key = "P" + (1 + port) + " ";
if ((LibsnesDll.SNES_DEVICE)device == LibsnesDll.SNES_DEVICE.JOYPAD)
if ((LibsnesApi.SNES_DEVICE)device == LibsnesApi.SNES_DEVICE.JOYPAD)
{
switch ((LibsnesDll.SNES_DEVICE_ID)id)
switch ((LibsnesApi.SNES_DEVICE_ID)id)
{
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_A: key += "A"; break;
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_B: key += "B"; break;
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_X: key += "X"; break;
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_Y: key += "Y"; break;
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_UP: key += "Up"; break;
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_DOWN: key += "Down"; break;
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_LEFT: key += "Left"; break;
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_RIGHT: key += "Right"; break;
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_L: key += "L"; break;
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_R: key += "R"; break;
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_SELECT: key += "Select"; break;
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_START: key += "Start"; break;
case LibsnesApi.SNES_DEVICE_ID.JOYPAD_A: key += "A"; break;
case LibsnesApi.SNES_DEVICE_ID.JOYPAD_B: key += "B"; break;
case LibsnesApi.SNES_DEVICE_ID.JOYPAD_X: key += "X"; break;
case LibsnesApi.SNES_DEVICE_ID.JOYPAD_Y: key += "Y"; break;
case LibsnesApi.SNES_DEVICE_ID.JOYPAD_UP: key += "Up"; break;
case LibsnesApi.SNES_DEVICE_ID.JOYPAD_DOWN: key += "Down"; break;
case LibsnesApi.SNES_DEVICE_ID.JOYPAD_LEFT: key += "Left"; break;
case LibsnesApi.SNES_DEVICE_ID.JOYPAD_RIGHT: key += "Right"; break;
case LibsnesApi.SNES_DEVICE_ID.JOYPAD_L: key += "L"; break;
case LibsnesApi.SNES_DEVICE_ID.JOYPAD_R: key += "R"; break;
case LibsnesApi.SNES_DEVICE_ID.JOYPAD_SELECT: key += "Select"; break;
case LibsnesApi.SNES_DEVICE_ID.JOYPAD_START: key += "Start"; break;
default: return 0;
}
@ -649,6 +331,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
public void FrameAdvance(bool render, bool rendersound)
{
api.MessageCounter = 0;
// for deterministic emulation, save the state we're going to use before frame advance
// don't do this during nocallbacks though, since it's already been done
if (!nocallbacks && DeterministicEmulation)
@ -665,55 +349,58 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
}
if (!nocallbacks && CoreComm.Tracer.Enabled)
LibsnesDll.snes_set_trace_callback(tracecb);
api.snes_set_trace_callback(tracecb);
else
LibsnesDll.snes_set_trace_callback(null);
api.snes_set_trace_callback(null);
// speedup when sound rendering is not needed
if (!rendersound)
LibsnesDll.snes_set_audio_sample(null);
api.snes_set_audio_sample(null);
else
LibsnesDll.snes_set_audio_sample(soundcb);
api.snes_set_audio_sample(soundcb);
bool resetSignal = Controller["Reset"];
if (resetSignal) LibsnesDll.snes_reset();
if (resetSignal) api.snes_reset();
bool powerSignal = Controller["Power"];
if (powerSignal) LibsnesDll.snes_power();
if (powerSignal) api.snes_power();
LibsnesDll.snes_set_layer_enable(0, 0, CoreComm.SNES_ShowBG1_0);
LibsnesDll.snes_set_layer_enable(0, 1, CoreComm.SNES_ShowBG1_1);
LibsnesDll.snes_set_layer_enable(1, 0, CoreComm.SNES_ShowBG2_0);
LibsnesDll.snes_set_layer_enable(1, 1, CoreComm.SNES_ShowBG2_1);
LibsnesDll.snes_set_layer_enable(2, 0, CoreComm.SNES_ShowBG3_0);
LibsnesDll.snes_set_layer_enable(2, 1, CoreComm.SNES_ShowBG3_1);
LibsnesDll.snes_set_layer_enable(3, 0, CoreComm.SNES_ShowBG4_0);
LibsnesDll.snes_set_layer_enable(3, 1, CoreComm.SNES_ShowBG4_1);
LibsnesDll.snes_set_layer_enable(4, 0, CoreComm.SNES_ShowOBJ_0);
LibsnesDll.snes_set_layer_enable(4, 1, CoreComm.SNES_ShowOBJ_1);
LibsnesDll.snes_set_layer_enable(4, 2, CoreComm.SNES_ShowOBJ_2);
LibsnesDll.snes_set_layer_enable(4, 3, CoreComm.SNES_ShowOBJ_3);
//too many messages
api.snes_set_layer_enable(0, 0, CoreComm.SNES_ShowBG1_0);
api.snes_set_layer_enable(0, 1, CoreComm.SNES_ShowBG1_1);
api.snes_set_layer_enable(1, 0, CoreComm.SNES_ShowBG2_0);
api.snes_set_layer_enable(1, 1, CoreComm.SNES_ShowBG2_1);
api.snes_set_layer_enable(2, 0, CoreComm.SNES_ShowBG3_0);
api.snes_set_layer_enable(2, 1, CoreComm.SNES_ShowBG3_1);
api.snes_set_layer_enable(3, 0, CoreComm.SNES_ShowBG4_0);
api.snes_set_layer_enable(3, 1, CoreComm.SNES_ShowBG4_1);
api.snes_set_layer_enable(4, 0, CoreComm.SNES_ShowOBJ_0);
api.snes_set_layer_enable(4, 1, CoreComm.SNES_ShowOBJ_1);
api.snes_set_layer_enable(4, 2, CoreComm.SNES_ShowOBJ_2);
api.snes_set_layer_enable(4, 3, CoreComm.SNES_ShowOBJ_3);
// if the input poll callback is called, it will set this to false
IsLagFrame = true;
//apparently this is one frame?
timeFrameCounter++;
LibsnesDll.snes_run();
api.snes_run();
while (LibsnesDll.HasMessage)
Console.WriteLine(LibsnesDll.DequeueMessage());
while (api.HasMessage)
Console.WriteLine(api.DequeueMessage());
if (IsLagFrame)
LagCount++;
//diagnostics for IPC traffic
//Console.WriteLine(api.MessageCounter);
}
public DisplayType DisplayType
{
get
{
if (LibsnesDll.snes_get_region() == LibsnesDll.SNES_REGION.NTSC)
if (api.snes_get_region() == LibsnesApi.SNES_REGION.NTSC)
return BizHawk.DisplayType.NTSC;
else
return BizHawk.DisplayType.PAL;
@ -770,37 +457,39 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
set { }
get
{
return LibsnesDll.snes_get_memory_size(LibsnesDll.SNES_MEMORY.CARTRIDGE_RAM) != 0;
return api.snes_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM) != 0;
}
}
public byte[] ReadSaveRam()
{
if (disposedSaveRam != null) return disposedSaveRam;
return snes_get_memory_data_read(LibsnesDll.SNES_MEMORY.CARTRIDGE_RAM);
}
public static byte[] snes_get_memory_data_read(LibsnesDll.SNES_MEMORY id)
{
var size = (int)LibsnesDll.snes_get_memory_size(id);
if (size == 0) return new byte[0];
var data = LibsnesDll.snes_get_memory_data(id);
byte* buf = api.snes_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
var size = api.snes_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
var ret = new byte[size];
Marshal.Copy(data, ret, 0, size);
Marshal.Copy((IntPtr)buf, ret, 0, size);
return ret;
}
//public byte[] snes_get_memory_data_read(LibsnesApi.SNES_MEMORY id)
//{
// var size = (int)api.snes_get_memory_size(id);
// if (size == 0) return new byte[0];
// var ret = api.snes_get_memory_data(id);
// return ret;
//}
public void StoreSaveRam(byte[] data)
{
var size = (int)LibsnesDll.snes_get_memory_size(LibsnesDll.SNES_MEMORY.CARTRIDGE_RAM);
var size = api.snes_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
if (size == 0) return;
var emudata = LibsnesDll.snes_get_memory_data(LibsnesDll.SNES_MEMORY.CARTRIDGE_RAM);
Marshal.Copy(data, 0, emudata, size);
if (size != data.Length) throw new InvalidOperationException("Somehow, we got a mismatch between saveram size and what bsnes says the saveram size is");
byte* buf = api.snes_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
Marshal.Copy(data, 0, (IntPtr)buf, size);
}
public void ClearSaveRam()
{
byte[] cleardata = new byte[(int)LibsnesDll.snes_get_memory_size(LibsnesDll.SNES_MEMORY.CARTRIDGE_RAM)];
byte[] cleardata = new byte[(int)api.snes_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM)];
StoreSaveRam(cleardata);
}
@ -914,6 +603,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
temp.SaveAsHex(writer);
// write extra copy of stuff we don't use
writer.WriteLine("Frame {0}", Frame);
writer.WriteLine("Profile {0}", CoreComm.SNES_Profile);
}
public void LoadStateText(TextReader reader)
{
@ -921,6 +611,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
byte[] state = new byte[hex.Length / 2];
state.ReadFromHex(hex);
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
reader.ReadLine();
var profile = reader.ReadLine().Split(' ')[1];
ValidateLoadstateProfile(profile);
}
public void SaveStateBinary(BinaryWriter writer)
@ -934,12 +627,13 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
writer.Write(IsLagFrame);
writer.Write(LagCount);
writer.Write(Frame);
writer.Write(CoreComm.SNES_Profile);
writer.Flush();
}
public void LoadStateBinary(BinaryReader reader)
{
int size = LibsnesDll.snes_serialize_size();
int size = api.snes_serialize_size();
byte[] buf = reader.ReadBytes(size);
CoreLoadState(buf);
@ -975,7 +669,18 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
IsLagFrame = reader.ReadBoolean();
LagCount = reader.ReadInt32();
Frame = reader.ReadInt32();
var profile = reader.ReadString();
ValidateLoadstateProfile(profile);
}
void ValidateLoadstateProfile(string profile)
{
if (profile != CoreComm.SNES_Profile)
{
throw new InvalidOperationException("You've attempted to load a savestate made using a different SNES profile than your current configuration. We COULD automatically switch for you, but we havent done that yet. This error is to make sure you know that this isnt going to work right now.");
}
}
public byte[] SaveStateBinary()
{
MemoryStream ms = new MemoryStream();
@ -990,22 +695,22 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
/// </summary>
void CoreLoadState(byte[] data)
{
int size = LibsnesDll.snes_serialize_size();
int size = api.snes_serialize_size();
if (data.Length != size)
throw new Exception("Libsnes internal savestate size mismatch!");
LibsnesDll.snes_init();
api.snes_init();
fixed (byte* pbuf = &data[0])
LibsnesDll.snes_unserialize(new IntPtr(pbuf), size);
api.snes_unserialize(new IntPtr(pbuf), size);
}
/// <summary>
/// handle the unmanaged part of savestating
/// </summary>
byte[] CoreSaveState()
{
int size = LibsnesDll.snes_serialize_size();
int size = api.snes_serialize_size();
byte[] buf = new byte[size];
fixed (byte* pbuf = &buf[0])
LibsnesDll.snes_serialize(new IntPtr(pbuf), size);
api.snes_serialize(new IntPtr(pbuf), size);
return buf;
}
@ -1019,38 +724,52 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
public CoreComm CoreComm { get; private set; }
// ----- Client Debugging API stuff -----
unsafe MemoryDomain MakeMemoryDomain(string name, LibsnesDll.SNES_MEMORY id, Endian endian)
unsafe MemoryDomain MakeMemoryDomain(string name, LibsnesApi.SNES_MEMORY id, Endian endian)
{
IntPtr block = LibsnesDll.snes_get_memory_data(id);
int size = LibsnesDll.snes_get_memory_size(id);
int size = api.snes_get_memory_size(id);
int mask = size - 1;
byte* blockptr = (byte*)block.ToPointer();
MemoryDomain md;
//have to bitmask these somehow because it's unmanaged memory and we would hate to clobber things or make them nondeterministic
////have to bitmask these somehow because it's unmanaged memory and we would hate to clobber things or make them nondeterministic
//if (Util.IsPowerOfTwo(size))
//{
// //can &mask for speed
// md = new MemoryDomain(name, size, endian,
// (addr) => blockptr[addr & mask],
// (addr, value) => blockptr[addr & mask] = value);
//}
//else
//{
// //have to use % (only OAM needs this, it seems)
// //(OAM is actually two differently sized banks of memory which arent truly considered adjacent. maybe a better way to visualize it would be with an empty bus and adjacent banks)
// md = new MemoryDomain(name, size, endian,
// (addr) => blockptr[addr % size],
// (addr, value) => blockptr[addr % size] = value);
//}
//EXTERNAL PROCESS CONVERSION: MUST MAKE THIS SAFE
//speed it up later
if (Util.IsPowerOfTwo(size))
{
//can &mask for speed
md = new MemoryDomain(name, size, endian,
(addr) => blockptr[addr & mask],
(addr, value) => blockptr[addr & mask] = value);
(addr) => api.peek(id, (uint)(addr & mask)),
(addr, value) => api.poke(id, (uint)(addr & mask), value));
}
else
{
//have to use % (only OAM needs this, it seems)
//(OAM is actually two differently sized banks of memory which arent truly considered adjacent. maybe a better way to visualize it would be with an empty bus and adjacent banks)
md = new MemoryDomain(name, size, endian,
(addr) => blockptr[addr % size],
(addr, value) => blockptr[addr % size] = value);
(addr) => api.peek(id, (uint)(addr % size)),
(addr, value) => api.poke(id, (uint)(addr % size), value));
}
MemoryDomains.Add(md);
return md;
//doesnt cache the addresses. safer. slower. necessary? don't know
//return new MemoryDomain(name, size, endian,
// (addr) => Peek(LibsnesDll.SNES_MEMORY.WRAM, addr & mask),
// (addr, value) => Poke(LibsnesDll.SNES_MEMORY.WRAM, addr & mask, value));
}
void SetupMemoryDomains(byte[] romData)
@ -1061,18 +780,18 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
(addr) => romData[addr],
(addr, value) => romData[addr] = value);
MainMemory = MakeMemoryDomain("WRAM", LibsnesDll.SNES_MEMORY.WRAM, Endian.Little);
MainMemory = MakeMemoryDomain("WRAM", LibsnesApi.SNES_MEMORY.WRAM, Endian.Little);
MemoryDomains.Add(romDomain);
MakeMemoryDomain("CARTRAM", LibsnesDll.SNES_MEMORY.CARTRIDGE_RAM, Endian.Little);
MakeMemoryDomain("VRAM", LibsnesDll.SNES_MEMORY.VRAM, Endian.Little);
MakeMemoryDomain("OAM", LibsnesDll.SNES_MEMORY.OAM, Endian.Little);
MakeMemoryDomain("CGRAM", LibsnesDll.SNES_MEMORY.CGRAM, Endian.Little);
MakeMemoryDomain("APURAM", LibsnesDll.SNES_MEMORY.APURAM, Endian.Little);
MakeMemoryDomain("CARTRAM", LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM, Endian.Little);
MakeMemoryDomain("VRAM", LibsnesApi.SNES_MEMORY.VRAM, Endian.Little);
MakeMemoryDomain("OAM", LibsnesApi.SNES_MEMORY.OAM, Endian.Little);
MakeMemoryDomain("CGRAM", LibsnesApi.SNES_MEMORY.CGRAM, Endian.Little);
MakeMemoryDomain("APURAM", LibsnesApi.SNES_MEMORY.APURAM, Endian.Little);
if (!DeterministicEmulation)
MemoryDomains.Add(new MemoryDomain("BUS", 0x1000000, Endian.Little,
(addr) => LibsnesDll.bus_read((uint)addr),
(addr, val) => LibsnesDll.bus_write((uint)addr, val)));
(addr) => api.peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr),
(addr, val) => api.poke(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr, val)));
}
public IList<MemoryDomain> MemoryDomains { get; private set; }
public MemoryDomain MainMemory { get; private set; }

View File

@ -10,14 +10,14 @@ using System.Linq;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Collections.Generic;
namespace BizHawk.Emulation.Consoles.Nintendo.SNES
{
public unsafe class SNESGraphicsDecoder
public unsafe class SNESGraphicsDecoder : IDisposable
{
public class PaletteSelection
{
public PaletteSelection() { }
@ -332,15 +332,15 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
public bool M7SEL_HFLIP { private set; get; }
public bool M7SEL_VFLIP { private set; get; }
public static ScreenInfo GetScreenInfo()
public static ScreenInfo GetScreenInfo(LibsnesApi api)
{
var si = new ScreenInfo();
si.Mode1_BG3_Priority = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG3_PRIORITY) == 1;
si.Mode1_BG3_Priority = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3_PRIORITY) == 1;
si.OBSEL_Size = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.OBSEL_SIZE);
si.OBSEL_NameSel = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.OBSEL_NAMESEL);
si.OBSEL_NameBase = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.OBSEL_NAMEBASE);
si.OBSEL_Size = api.snes_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_SIZE);
si.OBSEL_NameSel = api.snes_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMESEL);
si.OBSEL_NameBase = api.snes_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMEBASE);
si.ObjSizeBounds = ObjSizes[si.OBSEL_Size,1];
int square = Math.Max(si.ObjSizeBounds.Width, si.ObjSizeBounds.Height);
@ -350,26 +350,26 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
si.OBJTable0Addr = si.OBSEL_NameBase << 14;
si.OBJTable1Addr = (si.OBJTable0Addr + ((si.OBSEL_NameSel + 1) << 13)) & 0xFFFF;
si.SETINI_Mode7ExtBG = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.SETINI_MODE7_EXTBG) == 1;
si.SETINI_HiRes = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.SETINI_HIRES) == 1;
si.SETINI_Overscan = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.SETINI_OVERSCAN) == 1;
si.SETINI_ObjInterlace = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.SETINI_OBJ_INTERLACE) == 1;
si.SETINI_ScreenInterlace = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.SETINI_SCREEN_INTERLACE) == 1;
si.SETINI_Mode7ExtBG = api.snes_peek_logical_register(LibsnesApi.SNES_REG.SETINI_MODE7_EXTBG) == 1;
si.SETINI_HiRes = api.snes_peek_logical_register(LibsnesApi.SNES_REG.SETINI_HIRES) == 1;
si.SETINI_Overscan = api.snes_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OVERSCAN) == 1;
si.SETINI_ObjInterlace = api.snes_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OBJ_INTERLACE) == 1;
si.SETINI_ScreenInterlace = api.snes_peek_logical_register(LibsnesApi.SNES_REG.SETINI_SCREEN_INTERLACE) == 1;
si.CGWSEL_ColorMask = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.CGWSEL_COLORMASK);
si.CGWSEL_ColorSubMask = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.CGWSEL_COLORSUBMASK);
si.CGWSEL_AddSubMode = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.CGWSEL_ADDSUBMODE);
si.CGWSEL_DirectColor = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.CGWSEL_DIRECTCOLOR) == 1;
si.CGWSEL_ColorMask = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORMASK);
si.CGWSEL_ColorSubMask = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORSUBMASK);
si.CGWSEL_AddSubMode = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_ADDSUBMODE);
si.CGWSEL_DirectColor = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_DIRECTCOLOR) == 1;
si.CGADSUB_AddSub = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.CGADSUB_MODE);
si.CGADSUB_Half = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.CGADSUB_HALF) == 1;
si.CGADSUB_AddSub = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_MODE);
si.CGADSUB_Half = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_HALF) == 1;
si.OBJ_MainEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.TM_OBJ) == 1;
si.OBJ_SubEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.TS_OBJ) == 1;
si.OBJ_MathEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.CGADSUB_OBJ) == 1;
si.BK_MathEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.CGADSUB_BACKDROP) == 1;
si.OBJ_MainEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TM_OBJ) == 1;
si.OBJ_SubEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TS_OBJ) == 1;
si.OBJ_MathEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_OBJ) == 1;
si.BK_MathEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BACKDROP) == 1;
si.Mode.MODE = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG_MODE);
si.Mode.MODE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG_MODE);
si.BG.BG1.Bpp = ModeBpps[si.Mode.MODE, 0];
si.BG.BG2.Bpp = ModeBpps[si.Mode.MODE, 1];
si.BG.BG3.Bpp = ModeBpps[si.Mode.MODE, 2];
@ -379,58 +379,58 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
for(int i=1;i<=4;i++)
si.BG[i].BGMode = si.BG[i].Bpp == 0 ? BGMode.Unavailable : BGMode.Text;
si.BG.BG1.TILESIZE = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG1_TILESIZE);
si.BG.BG2.TILESIZE = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG2_TILESIZE);
si.BG.BG3.TILESIZE = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG3_TILESIZE);
si.BG.BG4.TILESIZE = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG4_TILESIZE);
si.BG.BG1.TILESIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG1_TILESIZE);
si.BG.BG2.TILESIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG2_TILESIZE);
si.BG.BG3.TILESIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3_TILESIZE);
si.BG.BG4.TILESIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG4_TILESIZE);
si.BG.BG1.SCSIZE = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG1_SCSIZE);
si.BG.BG2.SCSIZE = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG2_SCSIZE);
si.BG.BG3.SCSIZE = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG3_SCSIZE);
si.BG.BG4.SCSIZE = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG4_SCSIZE);
si.BG.BG1.SCADDR = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG1_SCADDR);
si.BG.BG2.SCADDR = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG2_SCADDR);
si.BG.BG3.SCADDR = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG3_SCADDR);
si.BG.BG4.SCADDR = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG4_SCADDR);
si.BG.BG1.TDADDR = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG1_TDADDR);
si.BG.BG2.TDADDR = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG2_TDADDR);
si.BG.BG3.TDADDR = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG3_TDADDR);
si.BG.BG4.TDADDR = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG4_TDADDR);
si.BG.BG1.SCSIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCSIZE);
si.BG.BG2.SCSIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCSIZE);
si.BG.BG3.SCSIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCSIZE);
si.BG.BG4.SCSIZE = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCSIZE);
si.BG.BG1.SCADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCADDR);
si.BG.BG2.SCADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCADDR);
si.BG.BG3.SCADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCADDR);
si.BG.BG4.SCADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCADDR);
si.BG.BG1.TDADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG1_TDADDR);
si.BG.BG2.TDADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG2_TDADDR);
si.BG.BG3.TDADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3_TDADDR);
si.BG.BG4.TDADDR = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG4_TDADDR);
si.BG.BG1.MainEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.TM_BG1) == 1;
si.BG.BG2.MainEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.TM_BG2) == 1;
si.BG.BG3.MainEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.TM_BG3) == 1;
si.BG.BG4.MainEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.TM_BG4) == 1;
si.BG.BG1.SubEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.TS_BG1) == 1;
si.BG.BG2.SubEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.TS_BG2) == 1;
si.BG.BG3.SubEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.TS_BG3) == 1;
si.BG.BG4.SubEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.TS_BG4) == 1;
si.BG.BG1.MathEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.CGADSUB_BG1) == 1;
si.BG.BG2.MathEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.CGADSUB_BG2) == 1;
si.BG.BG3.MathEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.CGADSUB_BG3) == 1;
si.BG.BG4.MathEnabled = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.CGADSUB_BG4) == 1;
si.BG.BG1.MainEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TM_BG1) == 1;
si.BG.BG2.MainEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TM_BG2) == 1;
si.BG.BG3.MainEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TM_BG3) == 1;
si.BG.BG4.MainEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TM_BG4) == 1;
si.BG.BG1.SubEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TS_BG1) == 1;
si.BG.BG2.SubEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TS_BG2) == 1;
si.BG.BG3.SubEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TS_BG3) == 1;
si.BG.BG4.SubEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.TS_BG4) == 1;
si.BG.BG1.MathEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG1) == 1;
si.BG.BG2.MathEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG2) == 1;
si.BG.BG3.MathEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG3) == 1;
si.BG.BG4.MathEnabled = api.snes_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG4) == 1;
si.BG.BG1.HOFS = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG1HOFS);
si.BG.BG1.VOFS = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG1VOFS);
si.BG.BG2.HOFS = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG2HOFS);
si.BG.BG2.VOFS = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG2VOFS);
si.BG.BG3.HOFS = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG3HOFS);
si.BG.BG3.VOFS = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG3VOFS);
si.BG.BG4.HOFS = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG4HOFS);
si.BG.BG4.VOFS = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.BG4VOFS);
si.BG.BG1.HOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG1HOFS);
si.BG.BG1.VOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG1VOFS);
si.BG.BG2.HOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG2HOFS);
si.BG.BG2.VOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG2VOFS);
si.BG.BG3.HOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3HOFS);
si.BG.BG3.VOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG3VOFS);
si.BG.BG4.HOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG4HOFS);
si.BG.BG4.VOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.BG4VOFS);
si.M7HOFS = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.M7HOFS);
si.M7VOFS = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.M7VOFS);
si.M7A = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.M7A);
si.M7B = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.M7B);
si.M7C = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.M7C);
si.M7D = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.M7D);
si.M7X = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.M7X);
si.M7Y = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.M7Y);
si.M7Y = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.M7Y);
si.M7SEL_REPEAT = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.M7SEL_REPEAT);
si.M7SEL_HFLIP = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.M7SEL_HFLIP)!=0;
si.M7SEL_VFLIP = LibsnesDll.snes_peek_logical_register(LibsnesDll.SNES_REG.M7SEL_VFLIP)!=0;
si.M7HOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7HOFS);
si.M7VOFS = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7VOFS);
si.M7A = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7A);
si.M7B = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7B);
si.M7C = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7C);
si.M7D = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7D);
si.M7X = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7X);
si.M7Y = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7Y);
si.M7Y = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7Y);
si.M7SEL_REPEAT = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_REPEAT);
si.M7SEL_HFLIP = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_HFLIP)!=0;
si.M7SEL_VFLIP = api.snes_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_VFLIP)!=0;
for (int i = 1; i <= 4; i++)
{
@ -534,7 +534,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
public ScreenInfo ScanScreenInfo()
{
return ScreenInfo.GetScreenInfo();
return ScreenInfo.GetScreenInfo(api);
}
//the same basic color table that libsnes uses to convert from snes 555 to rgba32
@ -558,18 +558,27 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
int[] colortable;
public byte* vram, oam;
public ushort* cgram, vram16;
public SNESGraphicsDecoder(SnesColors.ColorType pal)
LibsnesApi api;
public SNESGraphicsDecoder(LibsnesApi api, SnesColors.ColorType pal)
{
this.api = api;
colortable = SnesColors.GetLUT(pal);
IntPtr block = LibsnesDll.snes_get_memory_data(LibsnesDll.SNES_MEMORY.VRAM);
IntPtr block = (IntPtr)api.snes_get_memory_data(LibsnesApi.SNES_MEMORY.VRAM);
vram = (byte*)block;
vram16 = (ushort*)block;
block = LibsnesDll.snes_get_memory_data(LibsnesDll.SNES_MEMORY.CGRAM);
block = (IntPtr)api.snes_get_memory_data(LibsnesApi.SNES_MEMORY.CGRAM);
cgram = (ushort*)block;
block = LibsnesDll.snes_get_memory_data(LibsnesDll.SNES_MEMORY.OAM);
block = (IntPtr)api.snes_get_memory_data(LibsnesApi.SNES_MEMORY.OAM);
oam = (byte*)block;
}
public void Dispose()
{
//todo - unhook from api?
}
public struct TileEntry
{
public ushort tilenum;
@ -783,7 +792,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
public void CacheTiles()
{
//generate 2bpp tiles
int numtiles = 8192;
int numtiles = 65536/8/2;
int[] tiles = new int[8 * 8 * numtiles];
_tileCache[2] = tiles;
for (int i = 0; i < numtiles; i++)

View File

@ -15,6 +15,9 @@ namespace BizHawk
public string SNES_FirmwaresPath;
public string C64_FirmwaresPath;
public string SNES_ExePath;
public string SNES_Profile;
public bool SNES_ShowBG1_0, SNES_ShowBG2_0, SNES_ShowBG3_0, SNES_ShowBG4_0;
public bool SNES_ShowBG1_1, SNES_ShowBG2_1, SNES_ShowBG3_1, SNES_ShowBG4_1;
public bool SNES_ShowOBJ_0, SNES_ShowOBJ_1, SNES_ShowOBJ_2, SNES_ShowOBJ_3;

View File

@ -12,6 +12,14 @@ namespace BizHawk
{
public static class Extensions
{
public static string GetDirectory(this Assembly asm)
{
string codeBase = asm.CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
public static int LowerBoundBinarySearch<T, TKey>(this IList<T> list, Func<T, TKey> keySelector, TKey key) where TKey : IComparable<TKey>
{
int min = 0;
@ -491,6 +499,18 @@ namespace BizHawk
return System.Text.Encoding.UTF8.GetString(read);
}
public static string ReadStringAsciiZ(this BinaryReader r)
{
StringBuilder sb = new StringBuilder();
for(;;)
{
int b = r.ReadByte();
if(b <= 0) break;
sb.Append((char)b);
}
return sb.ToString();
}
/// <summary>
/// conerts bytes to an uppercase string of hex numbers in upper case without any spacing or anything
/// //could be extension method

View File

@ -395,6 +395,12 @@
<Compile Include="SNESTools\SNESGraphicsViewer.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="SNESTools\SNESOptions.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="SNESTools\SNESOptions.Designer.cs">
<DependentUpon>SNESOptions.cs</DependentUpon>
</Compile>
<Compile Include="tools\HexColor.cs">
<SubType>Form</SubType>
</Compile>
@ -561,6 +567,9 @@
<EmbeddedResource Include="SNESTools\SNESGraphicsDebugger.resx">
<DependentUpon>SNESGraphicsDebugger.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="SNESTools\SNESOptions.resx">
<DependentUpon>SNESOptions.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="tools\HexColor.resx">
<DependentUpon>HexColor.cs</DependentUpon>
</EmbeddedResource>

View File

@ -743,6 +743,7 @@ namespace BizHawk.MultiClient
public SNESControllerTemplate[] SNESController = new SNESControllerTemplate[4];
public SNESControllerTemplate[] SNESAutoController = new SNESControllerTemplate[4];
public NESConsoleButtonTemplate SNESConsoleButtons = new NESConsoleButtonTemplate();
public string SNESProfile = "Compatibility";
//TI 83 settings
public TI83ControllerTemplate[] TI83Controller = new TI83ControllerTemplate[1];

File diff suppressed because it is too large Load Diff

View File

@ -1811,6 +1811,18 @@ namespace BizHawk.MultiClient
LoadSNESGraphicsDebugger();
}
private void miSnesOptions_Click(object sender, EventArgs e)
{
var so = new SNESOptions();
so.Profile = Global.Config.SNESProfile;
if (so.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
bool reboot = Global.Config.SNESProfile != so.Profile;
Global.Config.SNESProfile = so.Profile;
if (reboot) FlagNeedsReboot();
}
}
private void SNES_ToggleBG1()
{
if (Global.Emulator is LibsnesCore)

View File

@ -5,6 +5,7 @@ using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;
using System.Diagnostics;
using BizHawk.Core;
using BizHawk.DiscSystem;
using BizHawk.Emulation.Consoles.Sega;
@ -26,7 +27,7 @@ namespace BizHawk.MultiClient
public partial class MainForm : Form
{
public bool INTERIM = true;
public static bool INTERIM = true;
public const string EMUVERSION = "Version " + VersionInfo.MAINVERSION;
public const string RELEASEDATE = "December 23, 2012";
private Control renderTarget;
@ -359,6 +360,33 @@ namespace BizHawk.MultiClient
base.Dispose(disposing);
}
//contains a mapping: profilename->exepath ; or null if the exe wasnt available
Dictionary<string, string> SNES_prepared = new Dictionary<string, string>();
string SNES_Prepare(string profile)
{
SNES_Check(profile);
if (SNES_prepared[profile] == null)
{
throw new InvalidOperationException("Couldn't locate the executable for SNES emulation for profile: " + profile + ". Please make sure you're using a fresh dearchive of a BizHawk distribution.");
}
return SNES_prepared[profile];
}
void SNES_Check(string profile)
{
if (SNES_prepared.ContainsKey(profile)) return;
string exename = "libsneshawk-" + profile.ToLower() + ".exe";
string thisDir = PathManager.GetExeDirectoryAbsolute();
string exePath = Path.Combine(thisDir, exename);
if (!File.Exists(exePath))
exePath = Path.Combine(Path.Combine(thisDir, "dll"), exename);
if (!File.Exists(exePath))
exePath = null;
SNES_prepared[profile] = exePath;
}
public void SyncCoreCommInputSignals(CoreComm target)
{
target.NES_BackdropColor = Global.Config.NESBackgroundColor;
@ -390,6 +418,8 @@ namespace BizHawk.MultiClient
target.SNES_ShowOBJ_2 = Global.Config.SNES_ShowOBJ3;
target.SNES_ShowOBJ_3 = Global.Config.SNES_ShowOBJ4;
target.SNES_Profile = Global.Config.SNESProfile;
target.GG_HighlightActiveDisplayRegion = Global.Config.GGHighlightActiveDisplayRegion;
target.GG_ShowClippedRegions = Global.Config.GGShowClippedRegions;
@ -1843,6 +1873,7 @@ namespace BizHawk.MultiClient
case "SNES":
{
game.System = "SNES";
nextComm.SNES_ExePath = SNES_Prepare(Global.Config.SNESProfile);
var snes = new LibsnesCore(nextComm);
nextEmulator = snes;
snes.Load(game, rom.FileData, null, deterministicemulation);
@ -1968,6 +1999,7 @@ namespace BizHawk.MultiClient
{
game.System = "SNES";
game.AddOption("SGB");
nextComm.SNES_ExePath = SNES_Prepare(Global.Config.SNESProfile);
var snes = new LibsnesCore(nextComm);
nextEmulator = snes;
game.FirmwareHash = Util.BytesToHexString(System.Security.Cryptography.SHA1.Create().ComputeHash(sgbrom));
@ -4933,5 +4965,7 @@ namespace BizHawk.MultiClient
break;
}
}
}
}

View File

@ -34,6 +34,8 @@ namespace BizHawk.MultiClient
Application.SetCompatibleTextRenderingDefault(false);
// catch morons who mix versions
// zero 25-dec-2012 - only do for public builds. its annoying during development
if(!MainForm.INTERIM)
{
var thisversion = typeof(Program).Assembly.GetName().Version;
var utilversion = typeof(InputValidate).Assembly.GetName().Version;

View File

@ -191,7 +191,7 @@ namespace BizHawk.MultiClient
}
}
SNESGraphicsDecoder gd = new SNESGraphicsDecoder(SnesColors.ColorType.BizHawk);
SNESGraphicsDecoder gd;
SNESGraphicsDecoder.ScreenInfo si;
SNESGraphicsDecoder.TileEntry[] map;
byte[,] spriteMap = new byte[256, 224];
@ -199,6 +199,7 @@ namespace BizHawk.MultiClient
void RegenerateData()
{
if (gd != null) gd.Dispose();
gd = null;
if (currentSnesCore == null) return;
gd = NewDecoder();
@ -663,14 +664,15 @@ namespace BizHawk.MultiClient
SNESGraphicsDecoder NewDecoder()
{
//wtf to do? now we need an api all the time
if (currentSnesCore != null)
return new SNESGraphicsDecoder(currentSnesCore.CurrPalette);
else return new SNESGraphicsDecoder(SnesColors.ColorType.BizHawk);
return new SNESGraphicsDecoder(currentSnesCore.api, currentSnesCore.CurrPalette);
else return new SNESGraphicsDecoder(currentSnesCore.api, SnesColors.ColorType.BizHawk);
}
void RenderPalette()
{
var gd = NewDecoder();
//var gd = NewDecoder(); //??
lastPalette = gd.GetPalette();
int pixsize = paletteCellSize * 16 + paletteCellSpacing * 17;
@ -792,7 +794,7 @@ namespace BizHawk.MultiClient
if (lastPalette == null) return;
int rgb555 = lastPalette[lastColorNum];
var gd = NewDecoder();
//var gd = NewDecoder(); //??
int color = gd.Colorize(rgb555);
pnDetailsPaletteColor.BackColor = Color.FromArgb(color);
@ -1239,15 +1241,16 @@ namespace BizHawk.MultiClient
void SyncBackdropColor()
{
if (checkBackdropColor.Checked)
{
int col = DecodeWinformsColorToSNES(pnBackdropColor.BackColor);
LibsnesDll.snes_set_backdropColor(col);
}
else
{
LibsnesDll.snes_set_backdropColor(-1);
}
//TODO
//if (checkBackdropColor.Checked)
//{
// int col = DecodeWinformsColorToSNES(pnBackdropColor.BackColor);
// LibsnesDll.snes_set_backdropColor(col);
//}
//else
//{
// LibsnesDll.snes_set_backdropColor(-1);
//}
}
private void checkBackdropColor_CheckedChanged(object sender, EventArgs e)

View File

@ -0,0 +1,121 @@
namespace BizHawk.MultiClient
{
partial class SNESOptions
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnOk = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.rbCompatibility = new System.Windows.Forms.RadioButton();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.rbPerformance = new System.Windows.Forms.RadioButton();
this.groupBox1.SuspendLayout();
this.SuspendLayout();
//
// btnOk
//
this.btnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnOk.Location = new System.Drawing.Point(141, 102);
this.btnOk.Name = "btnOk";
this.btnOk.Size = new System.Drawing.Size(75, 23);
this.btnOk.TabIndex = 0;
this.btnOk.Text = "OK";
this.btnOk.UseVisualStyleBackColor = true;
this.btnOk.Click += new System.EventHandler(this.btnOk_Click);
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(222, 102);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 23);
this.btnCancel.TabIndex = 1;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// rbCompatibility
//
this.rbCompatibility.AutoSize = true;
this.rbCompatibility.Location = new System.Drawing.Point(6, 19);
this.rbCompatibility.Name = "rbCompatibility";
this.rbCompatibility.Size = new System.Drawing.Size(83, 17);
this.rbCompatibility.TabIndex = 2;
this.rbCompatibility.TabStop = true;
this.rbCompatibility.Text = "Compatibility";
this.rbCompatibility.UseVisualStyleBackColor = true;
//
// groupBox1
//
this.groupBox1.Controls.Add(this.rbPerformance);
this.groupBox1.Controls.Add(this.rbCompatibility);
this.groupBox1.Location = new System.Drawing.Point(12, 12);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(277, 70);
this.groupBox1.TabIndex = 3;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Core Selection";
//
// rbPerformance
//
this.rbPerformance.AutoSize = true;
this.rbPerformance.Location = new System.Drawing.Point(6, 42);
this.rbPerformance.Name = "rbPerformance";
this.rbPerformance.Size = new System.Drawing.Size(202, 17);
this.rbPerformance.TabIndex = 3;
this.rbPerformance.TabStop = true;
this.rbPerformance.Text = "Performance (only for casual gaming!)";
this.rbPerformance.UseVisualStyleBackColor = true;
//
// SNESOptions
//
this.AcceptButton = this.btnOk;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(300, 128);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.btnOk);
this.Name = "SNESOptions";
this.Text = "SNESOptions";
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button btnOk;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.RadioButton rbCompatibility;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.RadioButton rbPerformance;
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace BizHawk.MultiClient
{
public partial class SNESOptions : Form
{
public SNESOptions()
{
InitializeComponent();
}
public string Profile
{
get { return rbCompatibility.Checked ? "Compatibility" : "Performance"; }
set
{
rbCompatibility.Checked = (value == "Compatibility");
rbPerformance.Checked = (value == "Performance");
}
}
private void btnOk_Click(object sender, EventArgs e)
{
DialogResult = System.Windows.Forms.DialogResult.OK;
Close();
}
private void btnCancel_Click(object sender, EventArgs e)
{
DialogResult = System.Windows.Forms.DialogResult.Cancel;
Close();
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -151,7 +151,7 @@ namespace BizHawk.MultiClient
SetTabByPlatform();
if (!Global.MainForm.INTERIM)
if (!MainForm.INTERIM)
{
PlatformTabControl.Controls.Remove(tabPageC64);
PlatformTabControl.Controls.Remove(tabPageGBA);

View File

@ -176,7 +176,7 @@ namespace BizHawk.MultiClient
SetTabByPlatform();
if (!Global.MainForm.INTERIM)
if (!MainForm.INTERIM)
{
tabControl1.Controls.Remove(tabPageIntellivision);
tabControl1.Controls.Remove(tabPageC64);

View File

@ -26,7 +26,7 @@ namespace BizHawk.MultiClient
UpdateSoundDialog();
// to be removed
//if (Global.MainForm.INTERIM)
//if (MainForm.INTERIM)
//{
// ThrottlecheckBox.Visible = true;
//}

View File

@ -103,10 +103,10 @@ namespace BizHawk.MultiClient
}
}
newStripButton1.Visible = Global.MainForm.INTERIM;
newScriptToolStripMenuItem.Visible = Global.MainForm.INTERIM;
newStripButton1.Enabled = Global.MainForm.INTERIM;
newScriptToolStripMenuItem.Enabled = Global.MainForm.INTERIM;
newStripButton1.Visible = MainForm.INTERIM;
newScriptToolStripMenuItem.Visible = MainForm.INTERIM;
newStripButton1.Enabled = MainForm.INTERIM;
newScriptToolStripMenuItem.Enabled = MainForm.INTERIM;
}
private void StopScript(int x)
@ -708,7 +708,7 @@ namespace BizHawk.MultiClient
private void EditToolstripButton_Click(object sender, EventArgs e)
{
if (Global.MainForm.INTERIM)
if (MainForm.INTERIM)
{
DoLuaWriter();
}

View File

@ -185,7 +185,7 @@ namespace BizHawk.MultiClient
private void TAStudio_Load(object sender, EventArgs e)
{
if (!Global.MainForm.INTERIM)
if (!MainForm.INTERIM)
{
newProjectToolStripMenuItem.Enabled = false;
openProjectToolStripMenuItem.Enabled = false;

View File

@ -1,10 +1,3 @@
cd bsnes
mkdir obj
mkdir out
export BIZWINCFLAGS="-I. -O3 -masm=intel -DLIBCO_IMPORT -DLIBCO_MSVC -static-libgcc -static-libstdc++"
#for gdb debugging
#export BIZWINCFLAGS="-I. -O0 -g -masm=intel -DLIBCO_IMPORT -DLIBCO_MSVC -static-libgcc -static-libstdc++"
export TARGET_LIBSNES_LIBDEPS="-L ../libco_msvc_win32/release/ -llibco_msvc_win32 -static-libgcc -static-libstdc++"
profile=compatibility platform=win target=libsnes make -e -j 4
cd ..
cp bsnes/out/snes.dll ../BizHawk.MultiClient/output/dll/libsneshawk.dll
#!/bin/sh
./bizwinmakeone.sh compatibility

View File

@ -0,0 +1,11 @@
#this is for using the dll libco, and so the threads implementation can be used, for easier debugging
cd bsnes
mkdir obj
mkdir out
export BIZWINCFLAGS="-I. -O3 -masm=intel -DLIBCO_IMPORT -DLIBCO_MSVC -static-libgcc -static-libstdc++"
#for gdb debugging
#export BIZWINCFLAGS="-I. -O0 -g -masm=intel -DLIBCO_IMPORT -DLIBCO_MSVC -static-libgcc -static-libstdc++"
export TARGET_LIBSNES_LIBDEPS="-L ../libco_msvc_win32/release/ -llibco_msvc_win32 -static-libgcc -static-libstdc++"
profile=compatibility platform=win target=libsnes make -e -j 4
cd ..
cp bsnes/out/snes.dll ../BizHawk.MultiClient/output/dll/libsneshawk.dll

View File

@ -0,0 +1,12 @@
#!/bin/sh
#makes all bsnes binaries for distribution
#TODO - 64bit
./bizwinclean.sh
./bizwinmakeone.sh performance compress
./bizwinclean.sh
./bizwinmakeone.sh compatibility compress
#leave compatibility built as objs because thats more useful to us while devving

26
libsnes/bizwinmakeone.sh Normal file
View File

@ -0,0 +1,26 @@
#!/bin/sh -x
#this is for using the fast libco, hand-coded by byuu. the dll isnt used, and so the threads implementation wont be useful, so it cant be debugged easily
cd bsnes
mkdir obj
mkdir out
#debug:
#export BIZWINCFLAGS="-I. -O0 -g -masm=intel -DLIBCO_IMPORT -DLIBCO_MSVC -static-libgcc -static-libstdc++"
#not debug
export BIZWINCFLAGS="-I. -O3 -masm=intel -static-libgcc -static-libstdc++"
export TARGET_LIBSNES_LIBDEPS="-L ../libco_msvc_win32/release/ -llibco_msvc_win32 -static-libgcc -static-libstdc++ -Wl,--subsystem,windows"
export profile=$1
platform=win target=libsnes make -e -j 4
cd ..
filename=libsneshawk-${profile}.exe
targetdir=../BizHawk.MultiClient/output/dll
targetpath=${targetdir}/${filename}
cp bsnes/out/${filename} ${targetdir}
if [ "$2" == "compress" ]; then
upx -9 ${targetpath} ;
fi

View File

@ -0,0 +1,3 @@
#!/bin/sh
./bizwinmakeone.sh performance

View File

@ -60,7 +60,7 @@ void Cartridge::load(System::Revision revision, const string &markup, const uint
case Mapper::HuC3: mapper = &huc3; break;
}
ramdata = new uint8_t[ramsize = info.ramsize]();
ramdata = (uint8_t*)interface->allocSharedMemory("GAME_BOY_RAM",ramsize = info.ramsize);
system.load(revision);
loaded = true;
@ -71,7 +71,7 @@ void Cartridge::unload() {
if(loaded == false) return;
if(romdata) { delete[] romdata; romdata = 0; }
if(ramdata) { delete[] ramdata; ramdata = 0; }
if(ramdata) { interface->freeSharedMemory(ramdata); }
loaded = false;
}

View File

@ -7,6 +7,9 @@ struct Interface {
virtual bool inputPoll(unsigned id);
virtual void message(const string &text);
virtual void* allocSharedMemory(const char* memtype, size_t amt, int initialByte = -1) = 0;
virtual void freeSharedMemory(void* ptr) = 0;
};
extern Interface *interface;

View File

@ -74,6 +74,10 @@ namespace nall {
}
}
template<typename T> void array(T *array, int size) {
for(unsigned n = 0; n < size; n++) integer(array[n]);
}
template<typename T> void array(T &array) {
enum { size = sizeof(T) / sizeof(typename std::remove_extent<T>::type) };
for(unsigned n = 0; n < size; n++) integer(array[n]);

View File

@ -171,11 +171,20 @@ void CPU::reset() {
dma_reset();
}
CPU::CPU() : queue(512, { &CPU::queue_event, this }) {
CPU::CPU()
: queue(512, { &CPU::queue_event, this })
, wram(nullptr)
{
PPUcounter::scanline = { &CPU::scanline, this };
}
CPU::~CPU() {
interface()->freeSharedMemory(wram);
}
void CPU::initialize()
{
wram = (uint8*)interface()->allocSharedMemory("WRAM", 128 * 1024);
}
}

View File

@ -1,6 +1,6 @@
class CPU : public Processor, public CPUcore, public PPUcounter {
public:
uint8 wram[128 * 1024];
uint8* wram; //[128 * 1024];
enum : bool { Threaded = true };
array<Processor*> coprocessors;
@ -30,6 +30,7 @@ public:
void serialize(serializer&);
CPU();
~CPU();
void initialize();
private:
//cpu

View File

@ -5,7 +5,7 @@ void CPU::serialize(serializer &s) {
CPUcore::core_serialize(s);
PPUcounter::serialize(s);
s.array(wram);
s.array(wram, 128 * 1024);
queue.serialize(s);
s.array(port_data);

View File

@ -19,7 +19,7 @@ void PPU::synchronize_cpu() {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All)
co_switch(cpu.thread);
else if(clock >= 0 && scheduler.sync == Scheduler::SynchronizeMode::All)
interface->message("PPU had to advance nondeterministically!");
interface()->message("PPU had to advance nondeterministically!");
} else {
while(clock >= 0) cpu.enter();
}
@ -107,7 +107,7 @@ void PPU::scanline() {
regs.range_over = false;
}
interface->scanlineStart(line);
interface()->scanlineStart(line);
if(line == 1) {
//mosaic reset
@ -154,9 +154,9 @@ void PPU::power() {
ppu1_version = config.ppu1.version;
ppu2_version = config.ppu2.version;
for(auto &n : vram) n = 0x00;
for(auto &n : oam) n = 0x00;
for(auto &n : cgram) n = 0x00;
for(int i=0;i<128*1024;i++) vram[i] = 0;
for(int i=0;i<544;i++) oam[i] = 0;
for(int i=0;i<512;i++) cgram[i] = 0;
flush_tiledata_cache();
region = (system.region() == System::Region::NTSC ? 0 : 1); //0 = NTSC, 1 = PAL
@ -431,7 +431,20 @@ void PPU::set_frameskip(unsigned frameskip_) {
framecounter = 0;
}
PPU::PPU() {
PPU::PPU()
: vram(nullptr)
, oam(nullptr)
, cgram(nullptr)
{
}
void PPU::initialize()
{
vram = (uint8*)interface()->allocSharedMemory("VRAM",128 * 1024);
oam = (uint8*)interface()->allocSharedMemory("OAM",544);
cgram = (uint8*)interface()->allocSharedMemory("CGRAM",512);
surface = new uint32[512 * 512];
output = surface + 16 * 512;
@ -462,6 +475,9 @@ PPU::PPU() {
PPU::~PPU() {
delete[] surface;
free_tiledata_cache();
interface()->freeSharedMemory(vram);
interface()->freeSharedMemory(oam);
interface()->freeSharedMemory(cgram);
}
}

View File

@ -1,8 +1,8 @@
class PPU : public Processor, public PPUcounter {
public:
uint8 vram[128 * 1024];
uint8 oam[544];
uint8 cgram[512];
uint8* vram; //[128 * 1024]
uint8* oam; //[544]
uint8* cgram; //[512]
enum : bool { Threaded = true };
alwaysinline void step(unsigned clocks);
@ -76,6 +76,7 @@ public:
void set_frameskip(unsigned frameskip);
void serialize(serializer&);
void initialize();
PPU();
~PPU();
};

View File

@ -104,7 +104,7 @@ void PPU::flush_pixel_cache() {
uint16 main;
int backdropColor = interface->getBackdropColor();
int backdropColor = interface()->getBackdropColor();
if(backdropColor == -1)
main = get_palette(0);
else main = backdropColor;

View File

@ -16,9 +16,9 @@ void PPU::serialize(serializer &s) {
Processor::serialize(s);
PPUcounter::serialize(s);
s.array(vram);
s.array(oam);
s.array(cgram);
s.array(vram,128 * 1024);
s.array(oam,544);
s.array(cgram,512);
s.integer(ppu1_version);
s.integer(ppu2_version);
@ -156,6 +156,7 @@ void PPU::serialize(serializer &s) {
s.integer(pixel_cache[n].pri_sub);
}
//zero TODO - only on load
//better to just take a small speed hit than store all of bg_tiledata[3][] ...
flush_tiledata_cache();

View File

@ -90,9 +90,9 @@ void PPU::enable() {
}
void PPU::power() {
for(auto &n : vram) n = 0;
for(auto &n : oam) n = 0;
for(auto &n : cgram) n = 0;
for(int i=0;i<128*1024;i++) vram[i] = 0;
for(int i=0;i<544;i++) oam[i] = 0;
for(int i=0;i<512;i++) cgram[i] = 0;
reset();
}
@ -134,7 +134,11 @@ bg2(*this, Background::ID::BG2),
bg3(*this, Background::ID::BG3),
bg4(*this, Background::ID::BG4),
sprite(*this),
screen(*this) {
screen(*this),
vram(nullptr),
oam(nullptr),
cgram(nullptr)
{
surface = new uint32[512 * 512];
output = surface + 16 * 512;
display.width = 256;
@ -145,6 +149,16 @@ screen(*this) {
PPU::~PPU() {
delete[] surface;
interface()->freeSharedMemory(vram);
interface()->freeSharedMemory(oam);
interface()->freeSharedMemory(cgram);
}
void PPU::initialize()
{
vram = (uint8*)interface()->allocSharedMemory("VRAM",128 * 1024);
oam = (uint8*)interface()->allocSharedMemory("OAM",544);
cgram = (uint8*)interface()->allocSharedMemory("CGRAM",512);
}
}

View File

@ -1,8 +1,8 @@
class PPU : public Processor, public PPUcounter {
public:
uint8 vram[64 * 1024];
uint8 oam[544];
uint8 cgram[512];
uint8 *vram; //[64 * 1024];
uint8 *oam; //[544];
uint8 *cgram; //[512];
enum : bool { Threaded = true };
alwaysinline void step(unsigned clocks);
@ -26,6 +26,7 @@ public:
void serialize(serializer&);
PPU();
~PPU();
void initialize();
private:
uint32 *surface;

View File

@ -138,11 +138,21 @@ void SMP::serialize(serializer &s) {
s.integer(timer2.stage3_ticks);
}
SMP::SMP() {
apuram = new uint8[64 * 1024];
SMP::SMP() :
apuram(nullptr)
{
}
SMP::~SMP() {
interface()->freeSharedMemory(apuram);
}
void SMP::initialize()
{
apuram = (uint8*)interface()->allocSharedMemory("APURAM", 64 * 1024);
}
}

View File

@ -20,6 +20,7 @@ public:
void serialize(serializer&);
SMP();
~SMP();
void initialize();
void disassemble_opcode(char *output, uint16 addr);

View File

@ -37,7 +37,8 @@ void Cartridge::load(Mode cartridge_mode, const char *markup) {
//print(markup, "\n\n");
if(ram_size > 0) {
ram.map(allocate<uint8>(ram_size, 0xff), ram_size);
uint8* buf = (uint8*)interface()->allocSharedMemory("CARTRIDGE_RAM",ram_size,0xff);
ram.map(buf, ram_size);
nvram.append({ "program.ram", ram.data(), ram.size() });
}
@ -80,7 +81,10 @@ void Cartridge::unload() {
loaded = false;
}
Cartridge::Cartridge() {
Cartridge::Cartridge()
: rom()
, ram("CARTRIDGE_RAM")
{
loaded = false;
unload();
}

View File

@ -223,16 +223,16 @@ void Cartridge::parse_markup_necdsp(XML::Node &root) {
string firmware = root["firmware"].data;
string sha256 = root["sha256"].data;
string path = interface->path(Slot::Base, firmware);
string path = interface()->path(Slot::Base, firmware);
unsigned promsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 2048 : 16384);
unsigned dromsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 1024 : 2048);
unsigned filesize = promsize * 3 + dromsize * 2;
file fp;
if(fp.open(path, file::mode::read) == false) {
interface->message({ "Warning: NEC DSP firmware ", firmware, " is missing." });
interface()->message({ "Warning: NEC DSP firmware ", firmware, " is missing." });
} else if(fp.size() != filesize) {
interface->message({ "Warning: NEC DSP firmware ", firmware, " is of the wrong file size." });
interface()->message({ "Warning: NEC DSP firmware ", firmware, " is of the wrong file size." });
fp.close();
} else {
for(unsigned n = 0; n < promsize; n++) necdsp.programROM[n] = fp.readl(3);
@ -245,7 +245,7 @@ void Cartridge::parse_markup_necdsp(XML::Node &root) {
fp.read(data, filesize);
if(sha256 != nall::sha256(data, filesize)) {
interface->message({ "Warning: NEC DSP firmware ", firmware, " SHA256 sum is incorrect." });
interface()->message({ "Warning: NEC DSP firmware ", firmware, " SHA256 sum is incorrect." });
}
}
@ -288,12 +288,12 @@ void Cartridge::parse_markup_hitachidsp(XML::Node &root) {
string firmware = root["firmware"].data;
string sha256 = root["sha256"].data;
string path = interface->path(Slot::Base, firmware);
string path = interface()->path(Slot::Base, firmware);
file fp;
if(fp.open(path, file::mode::read) == false) {
interface->message({ "Warning: Hitachi DSP firmware ", firmware, " is missing." });
interface()->message({ "Warning: Hitachi DSP firmware ", firmware, " is missing." });
} else if(fp.size() != 1024 * 3) {
interface->message({ "Warning: Hitachi DSP firmware ", firmware, " is of the wrong file size." });
interface()->message({ "Warning: Hitachi DSP firmware ", firmware, " is of the wrong file size." });
fp.close();
} else {
for(unsigned n = 0; n < 1024; n++) hitachidsp.dataROM[n] = fp.readl(3);
@ -305,7 +305,7 @@ void Cartridge::parse_markup_hitachidsp(XML::Node &root) {
fp.read(data, 3072);
if(sha256 != nall::sha256(data, 3072)) {
interface->message({ "Warning: Hitachi DSP firmware ", firmware, " SHA256 sum is incorrect." });
interface()->message({ "Warning: Hitachi DSP firmware ", firmware, " SHA256 sum is incorrect." });
}
}
@ -338,19 +338,19 @@ void Cartridge::parse_markup_armdsp(XML::Node &root) {
string firmware = root["firmware"].data;
string sha256 = root["sha256"].data;
string path = interface->path(Slot::Base, firmware);
string path = interface()->path(Slot::Base, firmware);
file fp;
if(fp.open(path, file::mode::read) == false) {
interface->message({ "Warning: ARM DSP firmware ", firmware, " is missing." });
interface()->message({ "Warning: ARM DSP firmware ", firmware, " is missing." });
} else if(fp.size() != 160 * 1024) {
interface->message({ "Warning: ARM DSP firmware ", firmware, " is of the wrong file size." });
interface()->message({ "Warning: ARM DSP firmware ", firmware, " is of the wrong file size." });
fp.close();
} else {
fp.read(armdsp.firmware, fp.size());
if(!sha256.empty()) {
if(sha256 != nall::sha256(armdsp.firmware, fp.size())) {
interface->message({ "Warning: ARM DSP firmware ", firmware, " SHA256 sum is incorrect." });
interface()->message({ "Warning: ARM DSP firmware ", firmware, " SHA256 sum is incorrect." });
}
}
@ -520,7 +520,7 @@ void Cartridge::parse_markup_obc1(XML::Node &root) {
void Cartridge::parse_markup_msu1(XML::Node &root) {
if(root.exists() == false) {
has_msu1 = file::exists(interface->path(Cartridge::Slot::Base, "msu1.rom"));
has_msu1 = file::exists(interface()->path(Cartridge::Slot::Base, "msu1.rom"));
if(has_msu1) {
Mapping m({ &MSU1::mmio_read, &msu1 }, { &MSU1::mmio_write, &msu1 });
m.banklo = 0x00, m.bankhi = 0x3f, m.addrlo = 0x2000, m.addrhi = 0x2007;

View File

@ -2,6 +2,12 @@
BSXCartridge bsxcartridge;
BSXCartridge::BSXCartridge()
: sram("BSX_RAM")
, psram("BSX_PRAM")
{
}
void BSXCartridge::init() {
}

View File

@ -21,6 +21,8 @@ public:
void mmio_write(unsigned addr, uint8 data);
void mmio_commit();
BSXCartridge();
private:
uint8 r[16];
bool r00, r01, r02, r03;

View File

@ -38,7 +38,7 @@ uint8 BSXSatellaview::mmio_read(unsigned addr) {
if(counter == 0) {
time_t rawtime;
rawtime = SNES::interface->currentTime();
rawtime = SNES::interface()->currentTime();
tm *t = localtime(&rawtime);
regs.r2192_hour = t->tm_hour;

View File

@ -69,11 +69,15 @@ void ICD2::reset() {
joyp14lock = 0;
pulselock = true;
GameBoy::interface = this;
GameBoy::system.init();
GameBoy::system.power();
}
ICD2::ICD2()
{
GameBoy::interface = this;
}
}
#endif

View File

@ -16,6 +16,8 @@ public:
void serialize(serializer&);
ICD2();
private:
#include "interface/interface.hpp"
#include "mmio/mmio.hpp"

View File

@ -112,4 +112,7 @@ bool ICD2::inputPoll(unsigned id) {
return 0;
}
void* ICD2::allocSharedMemory(const char* memtype, size_t amt, int initialByte) { SNES::interface()->allocSharedMemory(memtype, amt, initialByte); }
void ICD2::freeSharedMemory(void* ptr) { SNES::interface()->freeSharedMemory(ptr); }
#endif

View File

@ -4,6 +4,9 @@ void videoRefresh(const uint16_t *data);
void audioSample(int16_t center, int16_t left, int16_t right);
bool inputPoll(unsigned id);
void* allocSharedMemory(const char* memtype, size_t amt, int initialByte = -1);
void freeSharedMemory(void* ptr);
struct Packet {
uint8 data[16];
uint8& operator[](unsigned addr) { return data[addr & 15]; }

View File

@ -22,7 +22,7 @@ void Link::init() {
void Link::load() {
if(opened()) close();
string basename = interface->path(Cartridge::Slot::Base, "");
string basename = interface()->path(Cartridge::Slot::Base, "");
string name = program != "" ? program : notdir(basename);
string path = dir(basename);
if(open(name, path)) {

View File

@ -52,7 +52,7 @@ void MSU1::init() {
void MSU1::load() {
if(datafile.open()) datafile.close();
datafile.open(interface->path(Cartridge::Slot::Base, "msu1.rom"), file::mode::read);
datafile.open(interface()->path(Cartridge::Slot::Base, "msu1.rom"), file::mode::read);
}
void MSU1::unload() {
@ -112,7 +112,7 @@ void MSU1::mmio_write(unsigned addr, uint8 data) {
case 4: mmio.audio_track = (mmio.audio_track & 0xff00) | (data << 0);
case 5: mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8);
if(audiofile.open()) audiofile.close();
if(audiofile.open(interface->path(Cartridge::Slot::Base, { "track-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
if(audiofile.open(interface()->path(Cartridge::Slot::Base, { "track-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
uint32 header = audiofile.readm(4);
if(header != 0x4d535531) { //verify 'MSU1' header
audiofile.close();

View File

@ -16,12 +16,12 @@ void MSU1::serialize(serializer &s) {
s.integer(mmio.audio_play);
if(datafile.open()) datafile.close();
if(datafile.open(interface->path(Cartridge::Slot::Base, "msu1.rom"), file::mode::read)) {
if(datafile.open(interface()->path(Cartridge::Slot::Base, "msu1.rom"), file::mode::read)) {
datafile.seek(mmio.data_offset);
}
if(audiofile.open()) audiofile.close();
if(audiofile.open(interface->path(Cartridge::Slot::Base, { "track-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
if(audiofile.open(interface()->path(Cartridge::Slot::Base, { "track-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
audiofile.seek(mmio.audio_offset);
}
}

View File

@ -73,7 +73,7 @@ void SPC7110::serialize(serializer &s) {
s.integer(r4841);
s.integer(r4842);
s.array(rtc);
s.array(rtc,20);
s.integer(rtc_state);
s.integer(rtc_mode);
s.integer(rtc_index);

View File

@ -101,7 +101,7 @@ void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8;
void SPC7110::update_time(int offset) {
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
time_t current_time = SNES::interface->currentTime() - offset;
time_t current_time = SNES::interface()->currentTime() - offset;
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
@ -631,9 +631,21 @@ void SPC7110::mmio_write(unsigned addr, uint8 data) {
}
}
SPC7110::SPC7110() {
SPC7110::SPC7110() :
rtc(nullptr)
{
}
SPC7110::~SPC7110()
{
interface()->freeSharedMemory(rtc);
}
void SPC7110::initialize()
{
rtc = (uint8*)interface()->allocSharedMemory("SPC7110_RTC", 20);
}
//============
//SPC7110::MCU
//============

View File

@ -3,7 +3,7 @@
class SPC7110 {
public:
uint8 rtc[20];
uint8* rtc; //[20];
unsigned data_rom_offset;
void init();
@ -41,6 +41,8 @@ public:
void serialize(serializer&);
SPC7110();
~SPC7110();
void initialize();
private:
//==================

View File

@ -1,7 +1,7 @@
#ifdef SRTC_CPP
void SRTC::serialize(serializer &s) {
s.array(rtc);
s.array(rtc,20);
s.integer(rtc_mode);
s.integer(rtc_index);
}

View File

@ -9,6 +9,21 @@ SRTC srtc;
const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
SRTC::SRTC()
: rtc(nullptr)
{
}
SRTC::~SRTC()
{
interface()->freeSharedMemory(rtc);
}
void SRTC::initialize()
{
rtc = (uint8*)interface()->allocSharedMemory("RTC",20);
}
void SRTC::init() {
}
@ -31,7 +46,7 @@ void SRTC::reset() {
void SRTC::update_time() {
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
time_t current_time = SNES::interface->currentTime();
time_t current_time = SNES::interface()->currentTime();
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
@ -225,7 +240,4 @@ void SRTC::write(unsigned addr, uint8 data) {
}
}
SRTC::SRTC() {
}
}

View File

@ -1,6 +1,6 @@
class SRTC {
public:
uint8 rtc[20];
uint8* rtc; //[20];
void init();
void load();
@ -12,7 +12,9 @@ public:
void write(unsigned addr, uint8 data);
void serialize(serializer&);
void initialize();
SRTC();
~SRTC();
private:
static const unsigned months[12];

View File

@ -6,6 +6,12 @@ namespace SNES {
#include "serialization.cpp"
SufamiTurbo sufamiturbo;
SufamiTurbo::SufamiTurbo()
{
slotA.ram.setName("SUFAMI_TURBO_A_RAM");
slotB.ram.setName("SUFAMI_TURBO_B_RAM");
}
void SufamiTurbo::load() {
slotA.ram.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
slotB.ram.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);

View File

@ -8,6 +8,8 @@ public:
void load();
void unload();
void serialize(serializer&);
SufamiTurbo();
};
extern SufamiTurbo sufamiturbo;

View File

@ -2,7 +2,7 @@
uint2 Gamepad::data() {
if(counter >= 16) return 1;
uint2 result = interface->inputPoll(port, Input::Device::Joypad, 0, counter);
uint2 result = interface()->inputPoll(port, Input::Device::Joypad, 0, counter);
if(latched == 0) counter++;
return result;
}

View File

@ -18,8 +18,8 @@ void Justifier::enter() {
}
if(next < prev) {
int nx1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::X);
int ny1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Y);
int nx1 = interface()->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::X);
int ny1 = interface()->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Y);
nx1 += player1.x;
ny1 += player1.y;
player1.x = max(-16, min(256 + 16, nx1));
@ -27,8 +27,8 @@ void Justifier::enter() {
}
if(next < prev && chained) {
int nx2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::X);
int ny2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Y);
int nx2 = interface()->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::X);
int ny2 = interface()->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Y);
nx2 += player2.x;
ny2 += player2.y;
player2.x = max(-16, min(256 + 16, nx2));
@ -44,13 +44,13 @@ uint2 Justifier::data() {
if(counter >= 32) return 1;
if(counter == 0) {
player1.trigger = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Trigger);
player1.start = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Start);
player1.trigger = interface()->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Trigger);
player1.start = interface()->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Start);
}
if(counter == 0 && chained) {
player2.trigger = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Trigger);
player2.start = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Start);
player2.trigger = interface()->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Trigger);
player2.start = interface()->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Start);
}
switch(counter++) {

View File

@ -4,8 +4,8 @@ uint2 Mouse::data() {
if(counter >= 32) return 1;
if(counter == 0) {
position_x = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
position_y = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
position_x = interface()->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
position_y = interface()->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
}
bool direction_x = position_x < 0; //0 = right, 1 = left
@ -27,8 +27,8 @@ uint2 Mouse::data() {
case 6: return 0;
case 7: return 0;
case 8: return interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Right);
case 9: return interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Left);
case 8: return interface()->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Right);
case 9: return interface()->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Left);
case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
case 11: return 0; // ||

View File

@ -18,8 +18,8 @@ uint2 Multitap::data() {
port2 = 3; //controller 4
}
bool data1 = interface->inputPoll(port, Input::Device::Multitap, port1, index);
bool data2 = interface->inputPoll(port, Input::Device::Multitap, port2, index);
bool data1 = interface()->inputPoll(port, Input::Device::Multitap, port1, index);
bool data2 = interface()->inputPoll(port, Input::Device::Multitap, port2, index);
return (data2 << 1) | (data1 << 0);
}

View File

@ -28,8 +28,8 @@ void SuperScope::enter() {
if(next < prev) {
//Vcounter wrapped back to zero; update cursor coordinates for start of new frame
int nx = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::X);
int ny = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y);
int nx = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::X);
int ny = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y);
nx += x;
ny += y;
x = max(-16, min(256 + 16, nx));
@ -47,7 +47,7 @@ uint2 SuperScope::data() {
if(counter == 0) {
//turbo is a switch; toggle is edge sensitive
bool newturbo = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Turbo);
bool newturbo = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Turbo);
if(newturbo && !turbo) {
turbo = !turbo; //toggle state
turbolock = true;
@ -58,7 +58,7 @@ uint2 SuperScope::data() {
//trigger is a button
//if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive
trigger = false;
bool newtrigger = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Trigger);
bool newtrigger = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Trigger);
if(newtrigger && (turbo || !triggerlock)) {
trigger = true;
triggerlock = true;
@ -67,11 +67,11 @@ uint2 SuperScope::data() {
}
//cursor is a button; it is always level sensitive
cursor = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Cursor);
cursor = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Cursor);
//pause is a button; it is always edge sensitive
pause = false;
bool newpause = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Pause);
bool newpause = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Pause);
if(newpause && !pauselock) {
pause = true;
pauselock = true;

View File

@ -88,7 +88,7 @@ USART::USART(bool port) : Controller(port) {
txlength = 0;
txdata = 0;
string filename = interface->path(Cartridge::Slot::Base, "usart.so");
string filename = interface()->path(Cartridge::Slot::Base, "usart.so");
if(open_absolute(filename)) {
init = sym("usart_init");
main = sym("usart_main");

View File

@ -91,12 +91,12 @@ void CPU::enter() {
void CPU::op_step() {
debugger.op_exec(regs.pc.d);
if (interface->wanttrace)
if (interface()->wanttrace)
{
char tmp[512];
disassemble_opcode(tmp, regs.pc.d);
tmp[511] = 0;
interface->cpuTrace(tmp);
interface()->cpuTrace(tmp);
}
(this->*opcode_table[op_readpc()])();
@ -128,7 +128,7 @@ void CPU::enable() {
void CPU::power() {
cpu_version = config.cpu.version;
for(auto &n : wram) n = random(config.cpu.wram_init_value);
for(int i=0;i<128*1024;i++) wram[i] = random(config.cpu.wram_init_value);
regs.a = regs.x = regs.y = 0x0000;
regs.s = 0x01ff;
@ -166,11 +166,19 @@ void CPU::reset() {
timing_reset();
}
CPU::CPU() {
CPU::CPU()
: wram(nullptr)
{
PPUcounter::scanline = { &CPU::scanline, this };
}
CPU::~CPU() {
interface()->freeSharedMemory(wram);
}
void CPU::initialize()
{
wram = (uint8*)interface()->allocSharedMemory("WRAM",128 * 1024);
}
}

View File

@ -1,5 +1,5 @@
struct CPU : public Processor, public CPUcore, public PPUcounter {
uint8 wram[128 * 1024];
uint8 *wram; //[128 * 1024];
enum : bool { Threaded = true };
array<Processor*> coprocessors;
@ -24,6 +24,7 @@ struct CPU : public Processor, public CPUcore, public PPUcounter {
void serialize(serializer&);
CPU();
~CPU();
void initialize();
privileged:
#include "dma/dma.hpp"

View File

@ -43,7 +43,7 @@ void CPU::mmio_w4016(uint8 data) {
uint8 CPU::mmio_r4016() {
uint8 r = regs.mdr & 0xfc;
r |= input.port1->data();
if (!status.auto_joypad_poll) interface->inputNotify(0x4016);
if (!status.auto_joypad_poll) interface()->inputNotify(0x4016);
return r;
}
@ -54,7 +54,7 @@ uint8 CPU::mmio_r4016() {
uint8 CPU::mmio_r4017() {
uint8 r = (regs.mdr & 0xe0) | 0x1c;
r |= input.port2->data();
if (!status.auto_joypad_poll) interface->inputNotify(0x4017);
if (!status.auto_joypad_poll) interface()->inputNotify(0x4017);
return r;
}
@ -183,7 +183,7 @@ uint8 CPU::mmio_r4212() {
//RDIO
uint8 CPU::mmio_r4213() {
// interface->inputNotify(0x4213); // if there are lag counter issues with super scope, uncomment this
// interface()->inputNotify(0x4213); // if there are lag counter issues with super scope, uncomment this
return status.pio;
}
@ -207,14 +207,14 @@ uint8 CPU::mmio_r4217() {
return status.rdmpy >> 8;
}
uint8 CPU::mmio_r4218() { interface->inputNotify(0x4218); return status.joy1 >> 0; } //JOY1L
uint8 CPU::mmio_r4219() { interface->inputNotify(0x4219); return status.joy1 >> 8; } //JOY1H
uint8 CPU::mmio_r421a() { interface->inputNotify(0x421a); return status.joy2 >> 0; } //JOY2L
uint8 CPU::mmio_r421b() { interface->inputNotify(0x421b); return status.joy2 >> 8; } //JOY2H
uint8 CPU::mmio_r421c() { interface->inputNotify(0x421c); return status.joy3 >> 0; } //JOY3L
uint8 CPU::mmio_r421d() { interface->inputNotify(0x421d); return status.joy3 >> 8; } //JOY3H
uint8 CPU::mmio_r421e() { interface->inputNotify(0x421e); return status.joy4 >> 0; } //JOY4L
uint8 CPU::mmio_r421f() { interface->inputNotify(0x421f); return status.joy4 >> 8; } //JOY4H
uint8 CPU::mmio_r4218() { interface()->inputNotify(0x4218); return status.joy1 >> 0; } //JOY1L
uint8 CPU::mmio_r4219() { interface()->inputNotify(0x4219); return status.joy1 >> 8; } //JOY1H
uint8 CPU::mmio_r421a() { interface()->inputNotify(0x421a); return status.joy2 >> 0; } //JOY2L
uint8 CPU::mmio_r421b() { interface()->inputNotify(0x421b); return status.joy2 >> 8; } //JOY2H
uint8 CPU::mmio_r421c() { interface()->inputNotify(0x421c); return status.joy3 >> 0; } //JOY3L
uint8 CPU::mmio_r421d() { interface()->inputNotify(0x421d); return status.joy3 >> 8; } //JOY3H
uint8 CPU::mmio_r421e() { interface()->inputNotify(0x421e); return status.joy4 >> 0; } //JOY4L
uint8 CPU::mmio_r421f() { interface()->inputNotify(0x421f); return status.joy4 >> 8; } //JOY4H
//DMAPx
uint8 CPU::mmio_r43x0(uint8 i) {

View File

@ -5,7 +5,7 @@ void CPU::serialize(serializer &s) {
CPUcore::core_serialize(s);
PPUcounter::serialize(s);
s.array(wram);
s.array(wram,128 * 1024);
s.integer(cpu_version);

View File

@ -2,8 +2,6 @@
namespace SNES {
Interface *interface = nullptr;
Interface::Interface()
: wanttrace(false)
{

View File

@ -19,6 +19,10 @@ struct Interface {
bool wanttrace;
virtual void cpuTrace(const char *msg);
//zero 23-dec-2012
virtual void* allocSharedMemory(const char* memtype, size_t amt, int initialByte = -1) = 0;
virtual void freeSharedMemory(void* ptr) = 0;
};
extern Interface *interface;
Interface *interface();

View File

@ -19,7 +19,8 @@ StaticRAM::~StaticRAM() { delete[] data_; }
void MappedRAM::reset() {
if(data_) {
delete[] data_;
if(name_) interface()->freeSharedMemory(data_);
else free(data_);
data_ = 0;
}
size_ = 0;
@ -35,7 +36,8 @@ void MappedRAM::map(uint8 *source, unsigned length) {
void MappedRAM::copy(const uint8 *data, unsigned size) {
if(!data_) {
size_ = (size & ~255) + ((bool)(size & 255) << 8);
data_ = new uint8[size_]();
if(name_) data_ = (uint8*)interface()->allocSharedMemory(name_, size_);
else data_ = new uint8[size_]();
}
memcpy(data_, data, min(size_, size));
}
@ -47,7 +49,7 @@ unsigned MappedRAM::size() const { return size_; }
uint8 MappedRAM::read(unsigned addr) { return data_[addr]; }
void MappedRAM::write(unsigned addr, uint8 n) { if(!write_protect_) data_[addr] = n; }
const uint8& MappedRAM::operator[](unsigned addr) const { return data_[addr]; }
MappedRAM::MappedRAM() : data_(0), size_(0), write_protect_(false) {}
MappedRAM::MappedRAM(const char* name) : data_(0), size_(0), write_protect_(false), name_(name) {}
//Bus

View File

@ -33,12 +33,15 @@ struct MappedRAM : Memory {
inline uint8 read(unsigned addr);
inline void write(unsigned addr, uint8 n);
inline const uint8& operator[](unsigned addr) const;
inline MappedRAM();
inline MappedRAM(const char* name = NULL);
inline void setName(const char* name) { name_ = name; }
private:
uint8 *data_;
unsigned size_;
bool write_protect_;
const char* name_;
};
struct Bus {

View File

@ -4,7 +4,7 @@ void SMP::serialize(serializer &s) {
Processor::serialize(s);
SMPcore::core_serialize(s);
s.array(apuram);
s.array(apuram, 64 * 1024);
s.integer(status.clock_counter);
s.integer(status.dsp_counter);

View File

@ -28,7 +28,7 @@ void SMP::synchronize_cpu_force() {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All)
co_switch(cpu.thread);
else if(clock >= 0 && scheduler.sync == Scheduler::SynchronizeMode::All)
interface->message("SMP had to advance nondeterministically!");
interface()->message("SMP had to advance nondeterministically!");
} else {
while(clock >= 0) cpu.enter();
}
@ -87,7 +87,7 @@ void SMP::reset() {
regs.s = 0xef;
regs.p = 0x02;
for(auto &n : apuram) n = random(0x00);
for(int i=0;i<64*1024;i++) apuram[i] = random(0x00);
apuram[0x00f4] = 0x00;
apuram[0x00f5] = 0x00;
apuram[0x00f6] = 0x00;
@ -140,11 +140,18 @@ void SMP::reset() {
timer2.enable = false;
}
SMP::SMP() {
SMP::SMP()
: apuram(nullptr)
{
}
SMP::~SMP() {
interface()->freeSharedMemory(apuram);
}
void SMP::initialize()
{
apuram = (uint8*)interface()->allocSharedMemory("APURAM",64 * 1024);
}
}

View File

@ -1,6 +1,6 @@
struct SMP : public Processor, public SMPcore {
static const uint8 iplrom[64];
uint8 apuram[64 * 1024];
uint8* apuram; //[64 * 1024];
enum : bool { Threaded = true };
alwaysinline void step(unsigned clocks);
@ -18,6 +18,7 @@ struct SMP : public Processor, public SMPcore {
void serialize(serializer&);
SMP();
~SMP();
void initialize();
privileged:
#include "memory/memory.hpp"

View File

@ -18,7 +18,7 @@ void Audio::coprocessor_frequency(double input_frequency) {
}
void Audio::sample(int16 lsample, int16 rsample) {
if(coprocessor == false) return interface->audioSample(lsample, rsample);
if(coprocessor == false) return interface()->audioSample(lsample, rsample);
dsp_buffer[dsp_wroffset] = ((uint16)lsample << 0) + ((uint16)rsample << 16);
dsp_wroffset = (dsp_wroffset + 1) & buffer_mask;
@ -59,7 +59,7 @@ void Audio::flush() {
signed cop_left = (int16)(cop_sample >> 0);
signed cop_right = (int16)(cop_sample >> 16);
interface->audioSample(
interface()->audioSample(
sclamp<16>((dsp_left + cop_left ) / 2),
sclamp<16>((dsp_right + cop_right) / 2)
);

View File

@ -151,7 +151,7 @@ void System::unload() {
}
void System::power() {
random.seed((unsigned)interface->randomSeed());
random.seed((unsigned)interface()->randomSeed());
region = config.region;
expansion = config.expansion_port;

View File

@ -129,7 +129,7 @@ void Video::update() {
}
}
interface->videoRefresh(ppu.surface, hires, ppu.interlace(), ppu.overscan());
interface()->videoRefresh(ppu.surface, hires, ppu.interlace(), ppu.overscan());
hires = false;
}

View File

@ -10,10 +10,11 @@ else ifeq ($(platform),win)
endif
#rules
objects := $(objects) libsnes
objects := $(objects) libsnes libsnes_pwrap
objects := $(patsubst %,obj/%.o,$(objects))
obj/libsnes.o: $(ui)/libsnes.cpp $(ui)/*
obj/libsnes_pwrap.o: $(ui)/libsnes_pwrap.cpp $(ui)/*
#targets
build: $(objects)
@ -24,7 +25,7 @@ else ifeq ($(platform),osx)
ar rcs out/libsnes.a $(objects)
$(cpp) -o out/libsnes.dylib -install_name @executable_path/../Libraries/libsnes.dylib -shared -dynamiclib $(objects)
else ifeq ($(platform),win)
$(cpp) -o out/snes.dll -shared -Wl,--out-implib,libsnes.a $(objects) $(TARGET_LIBSNES_LIBDEPS)
$(cpp) -o out/libsneshawk-$(profile).exe -Wl $(objects) $(TARGET_LIBSNES_LIBDEPS)
endif
install:

View File

@ -6,15 +6,21 @@
#include <queue>
#include <Windows.h>
using namespace nall;
struct Interface : public SNES::Interface {
typedef SNES::Interface BaseType;
snes_video_refresh_t pvideo_refresh;
snes_audio_sample_t paudio_sample;
snes_input_poll_t pinput_poll;
snes_input_state_t pinput_state;
snes_input_notify_t pinput_notify;
snes_path_request_t ppath_request;
snes_allocSharedMemory_t pallocSharedMemory;
snes_freeSharedMemory_t pfreeSharedMemory;
snes_trace_t ptrace;
string basename;
uint32_t *buffer;
@ -89,9 +95,27 @@ struct Interface : public SNES::Interface {
return path;
}
return { basename, hint };
}
//zero 23-dec-2012
void* allocSharedMemory(const char* memtype, size_t amt, int initialByte = -1)
{
void* ret;
//if pallocSharedMemory isnt set up yet, we're going to have serious problems
ret = pallocSharedMemory(memtype,amt);
if(initialByte != -1)
{
for(unsigned i = 0; i < amt; i++) ((uint8*)ret)[i] = (uint8)initialByte;
}
return ret;
}
void freeSharedMemory(void* ptr)
{
if(!pfreeSharedMemory) return; //??
pfreeSharedMemory(ptr);
}
Interface() :
pvideo_refresh(0),
paudio_sample(0),
@ -99,12 +123,14 @@ struct Interface : public SNES::Interface {
pinput_state(0),
pinput_notify(0),
ppath_request(0),
pScanlineStart(0),
pallocSharedMemory(0),
pfreeSharedMemory(0),
backdropColor(-1),
ptrace(0)
{
buffer = new uint32_t[512 * 480];
palette = new uint32_t[16 * 32768];
}
~Interface() {
@ -113,7 +139,17 @@ struct Interface : public SNES::Interface {
}
};
static Interface interface;
void pwrap_init();
static Interface *iface = nullptr;
namespace SNES {
SNES::Interface *interface()
{
if(iface != nullptr) return iface;
iface = new ::Interface();
pwrap_init();
return iface;
}
}
const char* snes_library_id(void) {
static string version = {"bsnes v", Version};
@ -128,34 +164,43 @@ unsigned snes_library_revision_minor(void) {
return 3;
}
void snes_set_allocSharedMemory(snes_allocSharedMemory_t cb)
{
iface->pallocSharedMemory = cb;
}
void snes_set_freeSharedMemory(snes_freeSharedMemory_t cb)
{
iface->pfreeSharedMemory = cb;
}
void snes_set_video_refresh(snes_video_refresh_t video_refresh) {
interface.pvideo_refresh = video_refresh;
iface->pvideo_refresh = video_refresh;
}
void snes_set_color_lut(uint32_t * colors) {
for (int i = 0; i < 16 * 32768; i++)
interface.palette[i] = colors[i];
iface->palette[i] = colors[i];
}
void snes_set_audio_sample(snes_audio_sample_t audio_sample) {
interface.paudio_sample = audio_sample;
iface->paudio_sample = audio_sample;
}
void snes_set_input_poll(snes_input_poll_t input_poll) {
interface.pinput_poll = input_poll;
iface->pinput_poll = input_poll;
}
void snes_set_input_state(snes_input_state_t input_state) {
interface.pinput_state = input_state;
iface->pinput_state = input_state;
}
void snes_set_input_notify(snes_input_notify_t input_notify) {
interface.pinput_notify = input_notify;
iface->pinput_notify = input_notify;
}
void snes_set_path_request(snes_path_request_t path_request)
{
interface.ppath_request = path_request;
iface->ppath_request = path_request;
}
void snes_set_controller_port_device(bool port, unsigned device) {
@ -163,7 +208,7 @@ void snes_set_controller_port_device(bool port, unsigned device) {
}
void snes_set_cartridge_basename(const char *basename) {
interface.basename = basename;
iface->basename = basename;
}
template<typename T> inline void reconstruct(T* t) {
@ -173,7 +218,9 @@ template<typename T> inline void reconstruct(T* t) {
}
void snes_init(void) {
SNES::interface = &interface;
//force everything to get initialized, even thuogh it probably already is
SNES::interface();
//zero 01-dec-2012 - due to systematic variable initialization fails in bsnes components, these reconstructions are necessary,
//and the previous comment here which called this paranoid has been removed.
@ -187,9 +234,9 @@ void snes_init(void) {
reconstruct(&SNES::bsxsatellaview);
reconstruct(&SNES::bsxcartridge);
reconstruct(&SNES::bsxflash);
reconstruct(&SNES::srtc);
reconstruct(&SNES::srtc); SNES::srtc.initialize();
reconstruct(&SNES::sdd1);
reconstruct(&SNES::spc7110);
reconstruct(&SNES::spc7110); SNES::spc7110.initialize();
reconstruct(&SNES::obc1);
reconstruct(&SNES::msu1);
reconstruct(&SNES::link);
@ -199,11 +246,11 @@ void snes_init(void) {
//zero 01-dec-2012 - forgot to do all these. massive desync chaos!
//remove these to make it easier to find initialization fails in the component power-ons / constructors / etc.
//or just forget about it. this snes_init gets called paranoidly frequently by bizhawk, so things should stay zeroed correctly
reconstruct(&SNES::cpu);
reconstruct(&SNES::smp);
reconstruct(&SNES::cpu); SNES::cpu.initialize();
reconstruct(&SNES::smp); SNES::smp.initialize();
reconstruct(&SNES::dsp);
reconstruct(&SNES::ppu);
SNES::ppu.initialize();
SNES::system.init();
SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Joypad);
SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Joypad);
@ -299,7 +346,7 @@ void snes_cheat_set(unsigned index, bool enable, const char *code) {
//zero 21-sep-2012
void snes_set_scanlineStart(snes_scanlineStart_t cb)
{
interface.pScanlineStart = cb;
iface->pScanlineStart = cb;
}
//zero 03-sep-2012
@ -316,6 +363,7 @@ int snes_peek_logical_register(int reg)
switch(reg)
{
//$2105
#if !defined(PROFILE_PERFORMANCE)
case SNES_REG_BG_MODE: return SNES::ppu.regs.bg_mode;
case SNES_REG_BG3_PRIORITY: return SNES::ppu.regs.bg3_priority;
case SNES_REG_BG1_TILESIZE: return SNES::ppu.regs.bg_tilesize[SNES::PPU::BG1];
@ -399,7 +447,7 @@ int snes_peek_logical_register(int reg)
case SNES_REG_BG4VOFS: return SNES::ppu.regs.bg_vofs[3] & 0x3FF;
case SNES_REG_M7HOFS: return SNES::ppu.regs.m7_hofs & 0x1FFF; //rememebr to make these signed with <<19>>19
case SNES_REG_M7VOFS: return SNES::ppu.regs.m7_vofs & 0x1FFF; //rememebr to make these signed with <<19>>19
#endif
}
return 0;
@ -533,6 +581,53 @@ uint8_t* snes_get_memory_data(unsigned id) {
return 0;
}
const char* snes_get_memory_id_name(unsigned id) {
if(SNES::cartridge.loaded() == false) return nullptr;
switch(id) {
case SNES_MEMORY_CARTRIDGE_RAM:
return "CARTRIDGE_RAM";
case SNES_MEMORY_CARTRIDGE_RTC:
if(SNES::cartridge.has_srtc()) return "RTC";
if(SNES::cartridge.has_spc7110rtc()) return "SPC7110_RTC";
return nullptr;
case SNES_MEMORY_BSX_RAM:
if(SNES::cartridge.mode() != SNES::Cartridge::Mode::Bsx) break;
return "BSX_SRAM";
case SNES_MEMORY_BSX_PRAM:
if(SNES::cartridge.mode() != SNES::Cartridge::Mode::Bsx) break;
return "BSX_PSRAM";
case SNES_MEMORY_SUFAMI_TURBO_A_RAM:
if(SNES::cartridge.mode() != SNES::Cartridge::Mode::SufamiTurbo) break;
return "SUFAMI_SLOTARAM";
case SNES_MEMORY_SUFAMI_TURBO_B_RAM:
if(SNES::cartridge.mode() != SNES::Cartridge::Mode::SufamiTurbo) break;
return "SUFAMI_SLOTBRAM";
case SNES_MEMORY_GAME_BOY_RAM:
if(SNES::cartridge.mode() != SNES::Cartridge::Mode::SuperGameBoy) break;
//return GameBoy::cartridge.ramdata;
return "SGB_CARTRAM";
//case SNES_MEMORY_GAME_BOY_RTC:
// if(SNES::cartridge.mode() != SNES::Cartridge::Mode::SuperGameBoy) break;
// return GameBoy::cartridge.rtcdata;
case SNES_MEMORY_WRAM:
//return SNES::cpu.wram;
return "WRAM";
case SNES_MEMORY_APURAM:
//return SNES::smp.apuram;
return "APURAM";
case SNES_MEMORY_VRAM:
return "VRAM";
case SNES_MEMORY_OAM:
return "OAM";
case SNES_MEMORY_CGRAM:
return "CGRAM";
}
return nullptr;
}
unsigned snes_get_memory_size(unsigned id) {
if(SNES::cartridge.loaded() == false) return 0;
unsigned size = 0;
@ -599,31 +694,31 @@ void bus_write(unsigned addr, uint8_t val) {
int snes_poll_message()
{
if(interface.messages.size() == 0) return -1;
return interface.messages.front().length();
if(iface->messages.empty()) return -1;
return iface->messages.front().length();
}
void snes_dequeue_message(char* buffer)
{
int len = interface.messages.front().length();
memcpy(buffer,(const char*)interface.messages.front(),len);
interface.messages.pop();
int len = iface->messages.front().length();
memcpy(buffer,(const char*)iface->messages.front(),len);
iface->messages.pop();
}
void snes_set_backdropColor(int color)
{
interface.backdropColor = color;
iface->backdropColor = color;
}
void snes_set_trace_callback(snes_trace_t callback)
{
if (callback)
{
interface.wanttrace = true;
interface.ptrace = callback;
iface->wanttrace = true;
iface->ptrace = callback;
}
else
{
interface.wanttrace = false;
interface.ptrace = 0;
iface->wanttrace = false;
iface->ptrace = 0;
}
}
}

View File

@ -2,6 +2,15 @@
#define LIBSNES_HPP
#include <stdint.h>
#include <stdlib.h>
#if defined(LIBSNES_IMPORT)
#define LIBSNES_IMPORTDECL __declspec(dllimport)
#elif defined(LIBSNES_EXPORT)
#define LIBSNES_IMPORTDECL __declspec(dllexport)
#else
#define LIBSNES_IMPORTDECL
#endif
#ifdef __cplusplus
extern "C" {
@ -67,12 +76,17 @@ extern "C" {
#define SNES_MEMORY_OAM 103
#define SNES_MEMORY_CGRAM 104
#define SNES_MEMORY_SYSBUS 200
#define SNES_MEMORY_LOGICAL_REGS 201
typedef void (*snes_video_refresh_t)(const uint32_t *data, unsigned width, unsigned height);
typedef void (*snes_audio_sample_t)(uint16_t left, uint16_t right);
typedef void (*snes_input_poll_t)(void);
typedef int16_t (*snes_input_state_t)(unsigned port, unsigned device, unsigned index, unsigned id);
typedef void (*snes_input_notify_t)(int index);
typedef void (*snes_trace_t)(const char *msg);
typedef void* (*snes_allocSharedMemory_t)(const char* memtype, size_t amt);
typedef void (*snes_freeSharedMemory_t)(void* ptr);
const char* snes_library_id(void);
unsigned snes_library_revision_major(void);
@ -84,6 +98,9 @@ void snes_set_input_poll(snes_input_poll_t);
void snes_set_input_state(snes_input_state_t);
void snes_set_input_notify(snes_input_notify_t);
void snes_set_allocSharedMemory(snes_allocSharedMemory_t);
void snes_set_freeSharedMemory(snes_freeSharedMemory_t);
void snes_set_controller_port_device(bool port, unsigned device);
void snes_set_cartridge_basename(const char *basename);
@ -129,6 +146,7 @@ void snes_unload_cartridge(void);
bool snes_get_region(void);
uint8_t* snes_get_memory_data(unsigned id);
const char* snes_get_memory_id_name(unsigned id);
unsigned snes_get_memory_size(unsigned id);
//zeromus additions

View File

@ -1,16 +1,22 @@
#include <Windows.h>
#define LIBSNES_IMPORT
#include "snes/snes.hpp"
#include "libsnes.hpp"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <map>
#include <string>
#include <vector>
#define LIBSNES_IMPORT
#include "../bsnes/target-libsnes/libsnes.hpp"
HANDLE hPipe, hMapFile;
void* hMapFilePtr;
static bool running = false;
enum eMessage : int
{
@ -29,20 +35,21 @@ enum eMessage : int
//snes_set_cartridge_basename, //not used
eMessage_snes_load_cartridge_normal, //10
eMessage_snes_load_cartridge_normal,
eMessage_snes_load_cartridge_super_game_boy,
eMessage_snes_cb_video_refresh,
eMessage_snes_cb_input_poll,
eMessage_snes_cb_input_state,
eMessage_snes_cb_input_notify,
eMessage_snes_cb_audio_sample,
eMessage_snes_cb_scanlineStart, //16
eMessage_snes_cb_scanlineStart,
eMessage_snes_cb_path_request,
eMessage_snes_cb_trace_callback,
eMessage_snes_get_region,
eMessage_snes_get_memory_size, //20
eMessage_snes_get_memory_size,
eMessage_snes_get_memory_data,
eMessage_peek,
eMessage_poke,
@ -62,7 +69,11 @@ enum eMessage : int
eMessage_snes_enable_audio,
eMessage_snes_set_layer_enable,
eMessage_snes_set_backdropColor,
eMessage_snes_peek_logical_register
eMessage_snes_peek_logical_register,
eMessage_snes_allocSharedMemory,
eMessage_snes_freeSharedMemory,
eMessage_GetMemoryIdName
};
void ReadPipeBuffer(void* buf, int len)
@ -90,10 +101,10 @@ template<> bool ReadPipe<bool>()
return !!ReadPipe<char>();
}
FILE* outf = NULL;
void WritePipeBuffer(const void* buf, int len)
{
//static FILE* outf = NULL;
//if(!outf) outf = fopen("c:\\trace.bin","wb"); fwrite(buf,1,len,outf); fflush(outf);
DWORD bytesWritten;
BOOL result = WriteFile(hPipe, buf, len, &bytesWritten, NULL);
@ -108,9 +119,9 @@ template<typename T> void WritePipe(const T& val)
void WritePipeString(const char* str)
{
//TODO - write string length
int len = strlen(str);
WritePipeBuffer(str,len+1);
WritePipe(len);
WritePipeBuffer(str,len);
}
std::string ReadPipeString()
@ -164,13 +175,15 @@ void FlushAudio()
WritePipe(eMessage_snes_cb_audio_sample);
int nsamples = audiobuffer_idx/2;
int nsamples = audiobuffer_idx;
WritePipe(nsamples);
int destOfs = ReadPipe<int>();
char* buf = (char*)hMapFilePtr + destOfs;
memcpy(buf,audiobuffer,nsamples*4);
char* buf = ReadPipeSharedPtr();
memcpy(buf,audiobuffer,nsamples*2);
//extra just in case we had to unexpectedly flush audio and then carry on with some other process... yeah, its rickety.
WritePipe(0); //dummy synchronization
//wait for frontend to consume data
ReadPipe<int>(); //dummy synchronization
WritePipe(0); //dummy synchronization
audiobuffer_idx = 0;
@ -222,10 +235,62 @@ const char* snes_path_request(int slot, const char* hint)
strcpy(ret,str.c_str());
return ret;
}
void RunMessageLoop();
void snes_scanlineStart(int line)
{
WritePipe(eMessage_snes_cb_scanlineStart);
WritePipe(line);
//we've got to wait for the frontend to finish processing.
//in theory we could let emulation proceed after snagging the vram and registers, and do decoding and stuff on another thread...
//but its too hard for now.
RunMessageLoop();
}
class SharedMemoryBlock
{
public:
std::string memtype;
HANDLE handle;
};
static std::map<void*,SharedMemoryBlock*> memHandleTable;
void* snes_allocSharedMemory(const char* memtype, size_t amt)
{
if(!running) return NULL;
WritePipe(eMessage_snes_allocSharedMemory);
WritePipeString(memtype);
WritePipe(amt);
std::string blockname = ReadPipeString();
auto mapfile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, blockname.c_str());
if(mapfile == INVALID_HANDLE_VALUE)
return NULL;
auto ptr = MapViewOfFile(mapfile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
auto smb = new SharedMemoryBlock();
smb->memtype = memtype;
smb->handle = mapfile;
memHandleTable[ptr] = smb;
return ptr;
}
void snes_freeSharedMemory(void* ptr)
{
if(!running) return;
if(!ptr) return;
auto smb = memHandleTable.find(ptr)->second;
UnmapViewOfFile(ptr);
CloseHandle(smb->handle);
WritePipe(eMessage_snes_freeSharedMemory);
WritePipeString(smb->memtype.c_str());
}
void InitBsnes()
@ -237,49 +302,30 @@ void InitBsnes()
snes_set_input_state(snes_input_state);
snes_set_input_notify(snes_input_notify);
snes_set_path_request(snes_path_request);
snes_set_allocSharedMemory(snes_allocSharedMemory);
snes_set_freeSharedMemory(snes_freeSharedMemory);
}
int main(int argc, char** argv)
void RunMessageLoop()
{
if(argc != 2)
{
printf("This program is run from the libsneshawk emulator core. It is useless to you directly.");
exit(1);
}
if(!strcmp(argv[1],"Bongizong"))
{
fprintf(stderr,"Honga Wongkong");
exit(0x16817);
}
InitBsnes();
char pipename[256];
sprintf(pipename, "\\\\.\\Pipe\\%s",argv[1]);
hPipe = CreateFile(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if(hPipe == INVALID_HANDLE_VALUE)
return 1;
hMapFile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, argv[1]);
if(hMapFile == INVALID_HANDLE_VALUE)
return 1;
hMapFilePtr = MapViewOfFile(hMapFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
for(;;)
{
//printf("Reading message from pipe...\n");
auto msg = ReadPipe<eMessage>();
//printf("slam %08X\n",msg);
switch(msg)
{
case eMessage_Complete:
return;
case eMessage_snes_library_id: WritePipeString(snes_library_id()); break;
case eMessage_snes_library_revision_major: WritePipe(snes_library_revision_major()); break;
case eMessage_snes_library_revision_minor: WritePipe(snes_library_revision_minor()); break;
case eMessage_snes_init:
snes_init();
WritePipe(eMessage_Complete);
break;
case eMessage_snes_power: snes_power(); break;
case eMessage_snes_reset: snes_reset(); break;
@ -304,6 +350,28 @@ int main(int argc, char** argv)
break;
}
case eMessage_snes_load_cartridge_super_game_boy:
{
std::string rom_xml = ReadPipeString();
const char* rom_xmlptr = NULL;
if(rom_xml != "") rom_xmlptr = rom_xml.c_str();
Blob rom_data = ReadPipeBlob();
uint32 rom_length = rom_data.size();
std::string dmg_xml = ReadPipeString();
const char* dmg_xmlptr = NULL;
if(dmg_xml != "") dmg_xmlptr = dmg_xml.c_str();
Blob dmg_data = ReadPipeBlob();
uint32 dmg_length = dmg_data.size();
bool ret = snes_load_cartridge_super_game_boy(rom_xmlptr,(uint8*)&rom_data[0],rom_length, dmg_xmlptr,(uint8*)&dmg_data[0], dmg_length);
WritePipe(eMessage_Complete);
WritePipe((char)(ret?1:0));
break;
}
case eMessage_snes_get_region:
WritePipe((char)snes_get_region());
break;
@ -421,9 +489,68 @@ int main(int argc, char** argv)
WritePipe(snes_peek_logical_register(ReadPipe<int>()));
break;
case eMessage_GetMemoryIdName:
{
uint32 id = ReadPipe<uint32>();
const char* ret = snes_get_memory_id_name(id);
if(!ret) ret = "";
WritePipeString(ret);
break;
}
} //switch(msg)
}
}
void OpenConsole()
{
AllocConsole();
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
freopen("CONIN$", "r", stdin);
}
int xmain(int argc, char** argv)
{
if(argc != 2)
{
printf("This program is run from the libsneshawk emulator core. It is useless to you directly.");
exit(1);
}
if(!strcmp(argv[1],"Bongizong"))
{
fprintf(stderr,"Honga Wongkong");
exit(0x16817);
}
char pipename[256];
sprintf(pipename, "\\\\.\\Pipe\\%s",argv[1]);
if(!strncmp(argv[1],"console",7))
{
OpenConsole();
}
printf("pipe: %s\n",pipename);
hPipe = CreateFile(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if(hPipe == INVALID_HANDLE_VALUE)
return 1;
hMapFile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, argv[1]);
if(hMapFile == INVALID_HANDLE_VALUE)
return 1;
hMapFilePtr = MapViewOfFile(hMapFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
running = true;
printf("running\n");
RunMessageLoop();
return 0;
}
@ -434,12 +561,17 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine
if(argc != 2)
{
if(IDCANCEL == MessageBox(0,"This program is run from the libsneshawk emulator core. It is useless to you directly. But if you're really, that curious, click cancel.","Whatfor my daddy-o",MB_OKCANCEL))
if(IDOK == MessageBox(0,"This program is run from the libsneshawk emulator core. It is useless to you directly. But if you're really, that curious, click OK.","Whatfor my daddy-o",MB_OKCANCEL))
{
ShellExecute(0,"open","http://www.youtube.com/watch?v=boanuwUMNNQ#t=98s",NULL,NULL,SW_SHOWNORMAL);
}
exit(1);
}
main(argc,argv);
xmain(argc,argv);
}
void pwrap_init()
{
InitBsnes();
}

View File

@ -0,0 +1,2 @@
gcc libco_win32threads.c -o libco.dll -Wall -shared -O0 -g
mv libco.dll ../../BizHawk.MultiClient/output/dll/libco_msvc_win32.dll

View File

@ -1,92 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{8D864AC0-4BC2-48EA-8957-53C704F4BC57}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>libsnes_pwrap</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>..\..\Bizhawk.MultiClient\output\dll</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>..\..\Bizhawk.MultiClient\output\dll</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>libsneshawk.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
<Command>lib /machine:i386 /def:..\bsnes\libsneshawk.def</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>libsneshawk.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
<Command>lib /machine:i386 /def:..\bsnes\libsneshawk.def</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="libsnes_pwrap.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="libsnes_pwrap.cpp" />
</ItemGroup>
</Project>

BIN
libsnes/upx.exe Normal file

Binary file not shown.