2012-09-30 05:17:08 +00:00
//TODO - add serializer, add interlace field variable to serializer
//http://wiki.superfamicom.org/snes/show/Backgrounds
2012-09-06 08:32:25 +00:00
//TODO
2012-09-04 19:12:16 +00:00
//libsnes needs to be modified to support multiple instances - THIS IS NECESSARY - or else loading one game and then another breaks things
2012-09-22 05:03:52 +00:00
// edit - this is a lot of work
2012-09-04 07:09:00 +00:00
//wrap dll code around some kind of library-accessing interface so that it doesnt malfunction if the dll is unavailable
using System ;
2012-09-04 00:20:36 +00:00
using System.Linq ;
using System.Diagnostics ;
using System.Globalization ;
using System.IO ;
using System.Collections.Generic ;
using System.Runtime.InteropServices ;
namespace BizHawk.Emulation.Consoles.Nintendo.SNES
{
public unsafe static class LibsnesDll
{
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 00:20:36 +00:00
public static extern string snes_library_id ( ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 18:04:06 +00:00
public static extern int snes_library_revision_major ( ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 18:04:06 +00:00
public static extern int snes_library_revision_minor ( ) ;
2012-09-04 00:20:36 +00:00
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 00:20:36 +00:00
public static extern void snes_init ( ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 00:20:36 +00:00
public static extern void snes_power ( ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-16 17:15:53 +00:00
public static extern void snes_reset ( ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 00:20:36 +00:00
public static extern void snes_run ( ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 00:20:36 +00:00
public static extern void snes_term ( ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-09 21:19:54 +00:00
public static extern void snes_unload_cartridge ( ) ;
2012-09-04 00:20:36 +00:00
2012-09-27 07:22:31 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_cartridge_basename ( string basename ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-26 15:59:14 +00:00
[return: MarshalAs(UnmanagedType.U1)]
public static extern bool snes_load_cartridge_normal (
2012-09-04 00:20:36 +00:00
[MarshalAs(UnmanagedType.LPStr)]
2012-09-30 18:21:32 +00:00
string rom_xml ,
2012-09-04 00:20:36 +00:00
[MarshalAs(UnmanagedType.LPArray)]
2012-09-30 18:21:32 +00:00
byte [ ] rom_data ,
2012-09-26 15:59:14 +00:00
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 ) ;
2012-09-04 00:20:36 +00:00
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
2012-09-30 18:21:32 +00:00
public delegate void snes_video_refresh_t ( int * data , int width , int height ) ;
2012-09-04 00:20:36 +00:00
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
2012-09-04 01:21:14 +00:00
public delegate void snes_input_poll_t ( ) ;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
2012-09-04 00:20:36 +00:00
public delegate ushort snes_input_state_t ( int port , int device , int index , int id ) ;
2012-09-04 01:21:14 +00:00
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
2012-09-23 15:57:01 +00:00
public delegate void snes_input_notify_t ( int index ) ;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
2012-09-04 01:21:14 +00:00
public delegate void snes_audio_sample_t ( ushort left , ushort right ) ;
2012-09-22 05:03:52 +00:00
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void snes_scanlineStart_t ( int line ) ;
2012-09-27 07:22:31 +00:00
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate string snes_path_request_t ( int slot , string hint ) ;
2012-09-04 00:20:36 +00:00
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 00:20:36 +00:00
public static extern void snes_set_video_refresh ( snes_video_refresh_t video_refresh ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 00:20:36 +00:00
public static extern void snes_set_input_poll ( snes_input_poll_t input_poll ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 00:20:36 +00:00
public static extern void snes_set_input_state ( snes_input_state_t input_state ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-23 15:57:01 +00:00
public static extern void snes_set_input_notify ( snes_input_notify_t input_notify ) ;
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 01:21:14 +00:00
public static extern void snes_set_audio_sample ( snes_audio_sample_t audio_sample ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_scanlineStart ( snes_scanlineStart_t scanlineStart ) ;
2012-09-27 07:22:31 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_path_request ( snes_path_request_t scanlineStart ) ;
2012-09-04 00:20:36 +00:00
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 06:08:46 +00:00
[return: MarshalAs(UnmanagedType.U1)]
public static extern bool snes_check_cartridge (
[MarshalAs(UnmanagedType.LPArray)] byte [ ] rom_data ,
int rom_size ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 07:09:00 +00:00
[return: MarshalAs(UnmanagedType.U1)]
public static extern SNES_REGION snes_get_region ( ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 17:29:20 +00:00
public static extern int snes_get_memory_size ( SNES_MEMORY id ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 07:09:00 +00:00
public static extern IntPtr snes_get_memory_data ( SNES_MEMORY id ) ;
2012-09-04 08:21:01 +00:00
2012-10-03 14:54:32 +00:00
[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 ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-05 01:30:09 +00:00
public static extern int snes_serialize_size ( ) ;
2012-09-30 18:21:32 +00:00
2012-09-05 01:30:09 +00:00
[return: MarshalAs(UnmanagedType.U1)]
2012-09-30 18:21:32 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool snes_serialize ( IntPtr data , int size ) ;
2012-09-04 08:21:01 +00:00
[return: MarshalAs(UnmanagedType.U1)]
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 08:21:01 +00:00
public static extern bool snes_unserialize ( IntPtr data , int size ) ;
2012-09-04 19:12:16 +00:00
2012-09-27 01:38:27 +00:00
[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 ) ;
2012-11-16 21:29:23 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_color_lut ( IntPtr colors ) ;
2012-09-27 01:38:27 +00:00
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 ) ;
}
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-04 19:12:16 +00:00
public static extern void snes_set_layer_enable ( int layer , int priority ,
[MarshalAs(UnmanagedType.U1)]
bool enable
) ;
2012-09-06 08:32:25 +00:00
2012-10-17 18:39:44 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void snes_set_backdropColor ( int backdropColor ) ;
2012-09-22 05:03:52 +00:00
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
2012-09-06 08:32:25 +00:00
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 ,
2012-09-24 06:47:34 +00:00
BG4_TDADDR = 23 ,
//$2133 SETINI
2012-09-26 15:59:14 +00:00
SETINI_MODE7_EXTBG = 30 ,
SETINI_HIRES = 31 ,
SETINI_OVERSCAN = 32 ,
SETINI_OBJ_INTERLACE = 33 ,
2012-09-24 07:46:54 +00:00
SETINI_SCREEN_INTERLACE = 34 ,
2012-09-26 15:59:14 +00:00
//$2130 CGWSEL
CGWSEL_COLORMASK = 40 ,
CGWSEL_COLORSUBMASK = 41 ,
CGWSEL_ADDSUBMODE = 42 ,
2012-09-24 07:46:54 +00:00
CGWSEL_DIRECTCOLOR = 43 ,
2012-11-25 15:41:40 +00:00
//$2101 OBSEL
OBSEL_NAMEBASE = 50 ,
OBSEL_NAMESEL = 51 ,
2012-11-23 09:10:18 +00:00
OBSEL_SIZE = 52 ,
2012-09-06 08:32:25 +00:00
}
2012-09-30 18:21:32 +00:00
2012-09-04 07:09:00 +00:00
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 ,
}
2012-09-30 18:21:32 +00:00
public enum SNES_REGION : byte
2012-09-04 00:20:36 +00:00
{
2012-09-04 07:09:00 +00:00
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
2012-09-04 00:20:36 +00:00
}
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
}
}
2012-09-22 05:03:52 +00:00
public class ScanlineHookManager
{
public void Register ( object tag , Action < int > callback )
{
var rr = new RegistrationRecord ( ) ;
rr . tag = tag ;
rr . callback = callback ;
Unregister ( tag ) ;
records . Add ( rr ) ;
OnHooksChanged ( ) ;
}
2012-09-06 08:32:25 +00:00
2012-09-22 05:03:52 +00:00
public int HookCount { get { return records . Count ; } }
public virtual void OnHooksChanged ( ) { }
public void Unregister ( object tag )
{
records . RemoveAll ( ( r ) = > r . tag = = tag ) ;
}
public void HandleScanline ( int scanline )
{
foreach ( var rr in records ) rr . callback ( scanline ) ;
}
List < RegistrationRecord > records = new List < RegistrationRecord > ( ) ;
class RegistrationRecord
{
public object tag ;
public int scanline ;
public Action < int > callback ;
}
}
2012-09-30 18:21:32 +00:00
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
public unsafe class LibsnesCore : IEmulator , IVideoProvider
2012-09-04 00:20:36 +00:00
{
2012-10-05 04:47:45 +00:00
public bool IsSGB { get ; private set ; }
2012-10-08 14:37:42 +00:00
/// <summary>disable all external callbacks. the front end should not even know the core is frame advancing</summary>
bool nocallbacks = false ;
2012-09-05 23:16:08 +00:00
bool disposed = false ;
2012-09-04 00:20:36 +00:00
public void Dispose ( )
{
2012-09-05 23:16:08 +00:00
if ( disposed ) return ;
disposed = true ;
2012-09-16 17:15:53 +00:00
disposedSaveRam = ReadSaveRam ( ) ;
2012-09-05 23:16:08 +00:00
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 ) ;
2012-09-22 05:03:52 +00:00
BizHawk . Emulation . Consoles . Nintendo . SNES . LibsnesDll . snes_set_scanlineStart ( null ) ;
2012-09-05 23:16:08 +00:00
2012-09-09 21:19:54 +00:00
LibsnesDll . snes_unload_cartridge ( ) ;
2012-09-04 00:20:36 +00:00
LibsnesDll . snes_term ( ) ;
2012-09-08 01:15:16 +00:00
resampler . Dispose ( ) ;
2012-09-04 00:20:36 +00:00
}
2012-09-16 17:15:53 +00:00
//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 ;
2012-09-05 23:16:08 +00:00
//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 ;
2012-09-22 05:03:52 +00:00
public class MyScanlineHookManager : ScanlineHookManager
{
public MyScanlineHookManager ( LibsnesCore core )
{
this . core = core ;
}
LibsnesCore core ;
public override void OnHooksChanged ( )
{
core . OnScanlineHooksChanged ( ) ;
}
}
public MyScanlineHookManager ScanlineHookManager ;
void OnScanlineHooksChanged ( )
{
if ( disposed ) return ;
if ( ScanlineHookManager . HookCount = = 0 ) LibsnesDll . snes_set_scanlineStart ( null ) ;
else LibsnesDll . snes_set_scanlineStart ( scanlineStart_cb ) ;
}
void snes_scanlineStart ( int line )
{
ScanlineHookManager . HandleScanline ( line ) ;
}
2012-10-05 04:47:45 +00:00
string snes_path_request ( int slot , string hint )
2012-09-27 07:22:31 +00:00
{
//every rom requests this byuu homemade rom
if ( hint = = "msu1.rom" ) return "" ;
//build romfilename
2012-09-27 11:58:04 +00:00
string test = Path . Combine ( CoreInputComm . SNES_FirmwaresPath ? ? "" , hint ) ;
2012-09-27 07:22:31 +00:00
//does it exist?
if ( ! File . Exists ( test ) )
{
2012-10-05 04:47:45 +00:00
System . Windows . Forms . MessageBox . Show ( "The SNES core is referencing a firmware file which could not be found. Please make sure it's in your configured SNES firmwares folder. The referenced filename is: " + hint ) ;
2012-09-27 07:22:31 +00:00
return "" ;
}
2012-10-28 23:42:04 +00:00
Console . WriteLine ( "Served libsnes request for firmware \"{0}\" with \"{1}\"" , hint , test ) ;
2012-09-27 07:22:31 +00:00
//return the path we built
return test ;
}
public LibsnesCore ( )
{
}
2012-10-03 15:31:04 +00:00
public void Load ( GameInfo game , byte [ ] romData , byte [ ] sgbRomData , bool DeterministicEmulation )
2012-09-04 00:20:36 +00:00
{
2012-09-05 23:16:08 +00:00
//attach this core as the current
2012-09-30 18:21:32 +00:00
if ( CurrLibsnesCore ! = null )
2012-09-05 23:16:08 +00:00
CurrLibsnesCore . Dispose ( ) ;
CurrLibsnesCore = this ;
2012-09-22 05:03:52 +00:00
ScanlineHookManager = new MyScanlineHookManager ( this ) ;
2012-09-04 18:04:06 +00:00
LibsnesDll . snes_init ( ) ;
2012-09-27 07:22:31 +00:00
//LibsnesDll.snes_set_cartridge_basename(@);
2012-09-05 23:16:08 +00:00
vidcb = new LibsnesDll . snes_video_refresh_t ( snes_video_refresh ) ;
2012-09-04 00:20:36 +00:00
BizHawk . Emulation . Consoles . Nintendo . SNES . LibsnesDll . snes_set_video_refresh ( vidcb ) ;
2012-09-05 23:16:08 +00:00
pollcb = new LibsnesDll . snes_input_poll_t ( snes_input_poll ) ;
2012-09-04 00:20:36 +00:00
BizHawk . Emulation . Consoles . Nintendo . SNES . LibsnesDll . snes_set_input_poll ( pollcb ) ;
2012-09-05 23:16:08 +00:00
inputcb = new LibsnesDll . snes_input_state_t ( snes_input_state ) ;
2012-09-04 00:20:36 +00:00
BizHawk . Emulation . Consoles . Nintendo . SNES . LibsnesDll . snes_set_input_state ( inputcb ) ;
2012-09-04 01:21:14 +00:00
2012-09-23 15:57:01 +00:00
notifycb = new LibsnesDll . snes_input_notify_t ( snes_input_notify ) ;
BizHawk . Emulation . Consoles . Nintendo . SNES . LibsnesDll . snes_set_input_notify ( notifycb ) ;
2012-09-05 23:16:08 +00:00
soundcb = new LibsnesDll . snes_audio_sample_t ( snes_audio_sample ) ;
2012-09-04 01:21:14 +00:00
BizHawk . Emulation . Consoles . Nintendo . SNES . LibsnesDll . snes_set_audio_sample ( soundcb ) ;
2012-10-05 04:47:45 +00:00
pathRequest_cb = new LibsnesDll . snes_path_request_t ( snes_path_request ) ;
2012-09-27 07:22:31 +00:00
BizHawk . Emulation . Consoles . Nintendo . SNES . LibsnesDll . snes_set_path_request ( pathRequest_cb ) ;
2012-09-22 05:03:52 +00:00
scanlineStart_cb = new LibsnesDll . snes_scanlineStart_t ( snes_scanlineStart ) ;
2012-11-16 21:29:23 +00:00
// set palette
int [ ] tmp = SnesColors . GetLUT ( SnesColors . ColorType . Bizhawk ) ;
fixed ( int * p = & tmp [ 0 ] )
BizHawk . Emulation . Consoles . Nintendo . SNES . LibsnesDll . snes_set_color_lut ( ( IntPtr ) p ) ;
2012-09-05 21:21:35 +00:00
// start up audio resampler
2012-09-07 20:12:47 +00:00
InitAudio ( ) ;
2012-09-05 21:21:35 +00:00
2012-09-04 17:29:20 +00:00
//strip header
if ( ( romData . Length & 0x7FFF ) = = 512 )
{
var newData = new byte [ romData . Length - 512 ] ;
Array . Copy ( romData , 512 , newData , 0 , newData . Length ) ;
romData = newData ;
}
2012-09-26 15:59:14 +00:00
if ( game [ "SGB" ] )
{
2012-10-05 04:47:45 +00:00
IsSGB = true ;
SystemId = "SNES" ;
2012-09-26 15:59:14 +00:00
if ( ! LibsnesDll . 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 ) )
throw new Exception ( "snes_load_cartridge_normal() failed" ) ;
}
2012-09-30 18:21:32 +00:00
if ( LibsnesDll . snes_get_region ( ) = = LibsnesDll . SNES_REGION . NTSC )
2012-10-01 04:15:21 +00:00
{
//similar to what aviout reports from snes9x and seems logical from bsnes first principles. bsnes uses that numerator (ntsc master clockrate) for sure.
CoreOutputComm . VsyncNum = 21477272 ;
CoreOutputComm . VsyncDen = 4 * 341 * 262 ;
}
2012-09-30 18:21:32 +00:00
else
2012-10-01 04:15:21 +00:00
{
2012-09-30 18:21:32 +00:00
CoreOutputComm . VsyncNum = 50 ;
2012-10-01 04:15:21 +00:00
CoreOutputComm . VsyncDen = 1 ;
}
2012-09-30 18:21:32 +00:00
2012-09-09 21:19:54 +00:00
LibsnesDll . snes_power ( ) ;
2012-09-04 17:29:20 +00:00
SetupMemoryDomains ( romData ) ;
2012-09-30 19:22:54 +00:00
2012-10-03 15:31:04 +00:00
this . DeterministicEmulation = DeterministicEmulation ;
if ( DeterministicEmulation ) // save frame-0 savestate now
2012-10-08 14:37:42 +00:00
{
MemoryStream ms = new MemoryStream ( ) ;
BinaryWriter bw = new BinaryWriter ( ms ) ;
bw . Write ( CoreSaveState ( ) ) ;
bw . Write ( true ) ; // framezero, so no controller follows and don't frameadvance on load
2012-10-08 18:18:43 +00:00
// hack: write fake dummy controller info
bw . Write ( new byte [ 536 ] ) ;
2012-10-08 14:37:42 +00:00
bw . Close ( ) ;
savestatebuff = ms . ToArray ( ) ;
}
2012-09-04 00:20:36 +00:00
}
2012-09-05 23:16:08 +00:00
//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 ;
2012-09-23 15:57:01 +00:00
LibsnesDll . snes_input_notify_t notifycb ;
2012-09-05 23:16:08 +00:00
LibsnesDll . snes_audio_sample_t soundcb ;
2012-09-22 05:03:52 +00:00
LibsnesDll . snes_scanlineStart_t scanlineStart_cb ;
2012-09-27 07:22:31 +00:00
LibsnesDll . snes_path_request_t pathRequest_cb ;
2012-09-05 23:16:08 +00:00
2012-09-04 00:20:36 +00:00
ushort snes_input_state ( int port , int device , int index , int id )
{
2012-10-08 14:37:42 +00:00
if ( ! nocallbacks & & CoreInputComm . InputCallback ! = null ) CoreInputComm . InputCallback ( ) ;
2012-09-04 00:20:36 +00:00
//Console.WriteLine("{0} {1} {2} {3}", port, device, index, id);
string key = "P" + ( 1 + port ) + " " ;
2012-09-04 07:09:00 +00:00
if ( ( LibsnesDll . SNES_DEVICE ) device = = LibsnesDll . SNES_DEVICE . JOYPAD )
2012-09-04 00:20:36 +00:00
{
2012-09-05 18:52:17 +00:00
switch ( ( LibsnesDll . SNES_DEVICE_ID ) id )
2012-09-04 00:20:36 +00:00
{
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 ;
2012-10-08 18:55:25 +00:00
default : return 0 ;
2012-09-04 00:20:36 +00:00
}
return ( ushort ) ( Controller [ key ] ? 1 : 0 ) ;
}
return 0 ;
}
void snes_input_poll ( )
2012-09-23 15:57:01 +00:00
{
2012-10-06 13:34:04 +00:00
// this doesn't actually correspond to anything in the underlying bsnes;
// it gets called once per frame with video_refresh() and has nothing to do with anything
2012-09-23 15:57:01 +00:00
}
void snes_input_notify ( int index )
2012-09-04 00:20:36 +00:00
{
2012-09-11 01:36:12 +00:00
IsLagFrame = false ;
2012-09-04 00:20:36 +00:00
}
2012-09-30 05:17:08 +00:00
int field = 0 ;
2012-09-04 19:25:09 +00:00
void snes_video_refresh ( int * data , int width , int height )
2012-09-04 00:20:36 +00:00
{
vidWidth = width ;
vidHeight = height ;
2012-09-30 05:17:08 +00:00
2012-09-24 08:00:42 +00:00
//if we are in high-res mode, we get double width. so, lets double the height here to keep it square.
//TODO - does interlacing have something to do with the correct way to handle this? need an example that turns it on.
int yskip = 1 ;
if ( width = = 512 )
{
vidHeight * = 2 ;
yskip = 2 ;
}
2012-09-30 05:17:08 +00:00
int srcPitch = 1024 ;
int srcStart = 0 ;
//for interlaced mode, we're gonna alternate fields. you know, like we're supposed to
bool interlaced = ( height = = 478 | | height = = 448 ) ;
if ( interlaced )
{
srcPitch = 1024 ;
if ( field = = 1 )
srcStart = 512 ; //start on second field
//really only half as high as the video output
vidHeight / = 2 ;
height / = 2 ;
//alternate fields
field ^ = 1 ;
}
2012-09-04 00:20:36 +00:00
int size = vidWidth * vidHeight ;
if ( vidBuffer . Length ! = size )
vidBuffer = new int [ size ] ;
2012-09-24 08:00:42 +00:00
2012-09-30 05:17:08 +00:00
2012-09-04 00:20:36 +00:00
for ( int y = 0 ; y < height ; y + + )
for ( int x = 0 ; x < width ; x + + )
{
2012-09-30 05:17:08 +00:00
int si = y * srcPitch + x + srcStart ;
2012-09-24 08:00:42 +00:00
int di = y * vidWidth * yskip + x ;
2012-09-04 19:25:09 +00:00
int rgb = data [ si ] ;
vidBuffer [ di ] = rgb ;
2012-09-04 00:20:36 +00:00
}
2012-09-24 08:00:42 +00:00
//alternate scanlines
if ( width = = 512 )
for ( int y = 0 ; y < height ; y + + )
for ( int x = 0 ; x < width ; x + + )
{
int si = y * 1024 + x ;
int di = y * vidWidth * yskip + x + 512 ;
int rgb = data [ si ] ;
vidBuffer [ di ] = rgb ;
}
2012-09-04 00:20:36 +00:00
}
2012-09-20 19:52:47 +00:00
public void FrameAdvance ( bool render , bool rendersound )
2012-09-04 00:20:36 +00:00
{
2012-10-08 14:37:42 +00:00
// 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 )
{
MemoryStream ms = new MemoryStream ( ) ;
BinaryWriter bw = new BinaryWriter ( ms ) ;
bw . Write ( CoreSaveState ( ) ) ;
bw . Write ( false ) ; // not framezero
SnesSaveController ssc = new SnesSaveController ( ) ;
ssc . CopyFrom ( Controller ) ;
ssc . Serialize ( bw ) ;
bw . Close ( ) ;
savestatebuff = ms . ToArray ( ) ;
}
2012-09-20 20:25:40 +00:00
// speedup when sound rendering is not needed
2012-09-20 20:36:44 +00:00
if ( ! rendersound )
LibsnesDll . snes_set_audio_sample ( null ) ;
else
LibsnesDll . snes_set_audio_sample ( soundcb ) ;
2012-09-20 20:25:40 +00:00
2012-09-16 17:15:53 +00:00
bool resetSignal = Controller [ "Reset" ] ;
if ( resetSignal ) LibsnesDll . snes_reset ( ) ;
bool powerSignal = Controller [ "Power" ] ;
if ( powerSignal ) LibsnesDll . snes_power ( ) ;
2012-09-04 19:12:16 +00:00
LibsnesDll . snes_set_layer_enable ( 0 , 0 , CoreInputComm . SNES_ShowBG1_0 ) ;
LibsnesDll . snes_set_layer_enable ( 0 , 1 , CoreInputComm . SNES_ShowBG1_1 ) ;
LibsnesDll . snes_set_layer_enable ( 1 , 0 , CoreInputComm . SNES_ShowBG2_0 ) ;
LibsnesDll . snes_set_layer_enable ( 1 , 1 , CoreInputComm . SNES_ShowBG2_1 ) ;
LibsnesDll . snes_set_layer_enable ( 2 , 0 , CoreInputComm . SNES_ShowBG3_0 ) ;
LibsnesDll . snes_set_layer_enable ( 2 , 1 , CoreInputComm . SNES_ShowBG3_1 ) ;
LibsnesDll . snes_set_layer_enable ( 3 , 0 , CoreInputComm . SNES_ShowBG4_0 ) ;
LibsnesDll . snes_set_layer_enable ( 3 , 1 , CoreInputComm . SNES_ShowBG4_1 ) ;
LibsnesDll . snes_set_layer_enable ( 4 , 0 , CoreInputComm . SNES_ShowOBJ_0 ) ;
LibsnesDll . snes_set_layer_enable ( 4 , 1 , CoreInputComm . SNES_ShowOBJ_1 ) ;
LibsnesDll . snes_set_layer_enable ( 4 , 2 , CoreInputComm . SNES_ShowOBJ_2 ) ;
LibsnesDll . snes_set_layer_enable ( 4 , 3 , CoreInputComm . SNES_ShowOBJ_3 ) ;
2012-09-11 01:36:12 +00:00
// if the input poll callback is called, it will set this to false
IsLagFrame = true ;
2012-09-04 00:20:36 +00:00
//apparently this is one frame?
2012-09-07 20:06:57 +00:00
timeFrameCounter + + ;
2012-09-04 00:20:36 +00:00
LibsnesDll . snes_run ( ) ;
2012-09-08 20:03:04 +00:00
2012-09-27 01:38:27 +00:00
while ( LibsnesDll . HasMessage )
Console . WriteLine ( LibsnesDll . DequeueMessage ( ) ) ;
2012-09-11 01:36:12 +00:00
if ( IsLagFrame )
LagCount + + ;
2012-09-30 19:22:54 +00:00
2012-09-04 00:20:36 +00:00
}
2012-10-06 16:28:42 +00:00
public DisplayType DisplayType
{
get
{
if ( LibsnesDll . snes_get_region ( ) = = LibsnesDll . SNES_REGION . NTSC )
return BizHawk . DisplayType . NTSC ;
else
return BizHawk . DisplayType . PAL ;
}
}
2012-09-04 00:20:36 +00:00
//video provider
int IVideoProvider . BackgroundColor { get { return 0 ; } }
int [ ] IVideoProvider . GetVideoBuffer ( ) { return vidBuffer ; }
int IVideoProvider . VirtualWidth { get { return vidWidth ; } }
2012-09-05 18:52:17 +00:00
int IVideoProvider . BufferWidth { get { return vidWidth ; } }
2012-09-04 00:20:36 +00:00
int IVideoProvider . BufferHeight { get { return vidHeight ; } }
2012-09-30 14:08:50 +00:00
int [ ] vidBuffer = new int [ 256 * 224 ] ;
int vidWidth = 256 , vidHeight = 224 ;
2012-09-04 00:20:36 +00:00
public IVideoProvider VideoProvider { get { return this ; } }
public ControllerDefinition ControllerDefinition { get { return SNESController ; } }
IController controller ;
public IController Controller
{
get { return controller ; }
set { controller = value ; }
}
public static readonly ControllerDefinition SNESController =
new ControllerDefinition
{
Name = "SNES Controller" ,
BoolButtons = {
2012-09-16 17:15:53 +00:00
"P1 Up" , "P1 Down" , "P1 Left" , "P1 Right" , "P1 Select" , "P1 Start" , "P1 B" , "P1 A" , "P1 X" , "P1 Y" , "P1 L" , "P1 R" , "Reset" , "Power" ,
2012-09-10 18:40:39 +00:00
"P2 Up" , "P2 Down" , "P2 Left" , "P2 Right" , "P2 Select" , "P2 Start" , "P2 B" , "P2 A" , "P2 X" , "P2 Y" , "P2 L" , "P2 R" ,
"P3 Up" , "P3 Down" , "P3 Left" , "P3 Right" , "P3 Select" , "P3 Start" , "P3 B" , "P3 A" , "P3 X" , "P3 Y" , "P3 L" , "P3 R" ,
"P4 Up" , "P4 Down" , "P4 Left" , "P4 Right" , "P4 Select" , "P4 Start" , "P4 B" , "P4 A" , "P4 X" , "P4 Y" , "P4 L" , "P4 R" ,
2012-09-04 00:20:36 +00:00
}
} ;
2012-09-05 18:52:17 +00:00
2012-09-04 00:20:36 +00:00
int timeFrameCounter ;
2012-09-07 20:31:05 +00:00
public int Frame { get { return timeFrameCounter ; } set { timeFrameCounter = value ; } }
2012-09-04 00:20:36 +00:00
public int LagCount { get ; set ; }
public bool IsLagFrame { get ; private set ; }
2012-09-26 15:59:14 +00:00
public string SystemId { get ; private set ; }
2012-09-30 19:22:54 +00:00
public bool DeterministicEmulation
{
2012-10-03 15:31:04 +00:00
get ;
private set ;
2012-09-30 19:22:54 +00:00
}
2012-09-04 07:09:00 +00:00
public bool SaveRamModified
{
set { }
get
{
return LibsnesDll . snes_get_memory_size ( LibsnesDll . SNES_MEMORY . CARTRIDGE_RAM ) ! = 0 ;
}
}
2012-09-05 18:52:17 +00:00
2012-09-14 22:28:38 +00:00
public byte [ ] ReadSaveRam ( )
2012-09-16 17:15:53 +00:00
{
if ( disposedSaveRam ! = null ) return disposedSaveRam ;
2012-09-14 22:28:38 +00:00
return snes_get_memory_data_read ( LibsnesDll . SNES_MEMORY . CARTRIDGE_RAM ) ;
}
2012-09-04 07:09:00 +00:00
public static byte [ ] snes_get_memory_data_read ( LibsnesDll . SNES_MEMORY id )
{
var size = ( int ) LibsnesDll . snes_get_memory_size ( id ) ;
2012-09-04 08:21:01 +00:00
if ( size = = 0 ) return new byte [ 0 ] ;
2012-09-04 07:09:00 +00:00
var data = LibsnesDll . snes_get_memory_data ( id ) ;
var ret = new byte [ size ] ;
2012-09-05 18:52:17 +00:00
Marshal . Copy ( data , ret , 0 , size ) ;
2012-09-04 07:09:00 +00:00
return ret ;
}
public void StoreSaveRam ( byte [ ] data )
{
var size = ( int ) LibsnesDll . snes_get_memory_size ( LibsnesDll . SNES_MEMORY . CARTRIDGE_RAM ) ;
2012-09-04 08:21:01 +00:00
if ( size = = 0 ) return ;
2012-09-04 07:09:00 +00:00
var emudata = LibsnesDll . snes_get_memory_data ( LibsnesDll . SNES_MEMORY . CARTRIDGE_RAM ) ;
Marshal . Copy ( data , 0 , emudata , size ) ;
}
2012-09-04 00:20:36 +00:00
2012-09-14 22:28:38 +00:00
public void ClearSaveRam ( )
{
byte [ ] cleardata = new byte [ ( int ) LibsnesDll . snes_get_memory_size ( LibsnesDll . SNES_MEMORY . CARTRIDGE_RAM ) ] ;
StoreSaveRam ( cleardata ) ;
}
2012-11-25 15:41:40 +00:00
public void ResetFrameCounter ( )
{
timeFrameCounter = 0 ;
LagCount = 0 ;
IsLagFrame = false ;
}
2012-10-08 14:37:42 +00:00
#region savestates
/// <summary>
/// can freeze a copy of a controller input set and serialize\deserialize it
/// </summary>
class SnesSaveController : IController
{
// this is all rather general, so perhaps should be moved out of LibsnesCore
ControllerDefinition def ;
public SnesSaveController ( )
{
this . def = null ;
}
public SnesSaveController ( ControllerDefinition def )
{
this . def = def ;
}
WorkingDictionary < string , float > buttons = new WorkingDictionary < string , float > ( ) ;
/// <summary>
/// invalid until CopyFrom has been called
/// </summary>
public ControllerDefinition Type
{
get { return def ; }
}
public void Serialize ( BinaryWriter b )
{
b . Write ( buttons . Keys . Count ) ;
foreach ( var k in buttons . Keys )
{
b . Write ( k ) ;
b . Write ( buttons [ k ] ) ;
}
}
/// <summary>
/// no checking to see if the deserialized controls match any definition
/// </summary>
/// <param name="b"></param>
public void DeSerialize ( BinaryReader b )
{
buttons . Clear ( ) ;
int numbuttons = b . ReadInt32 ( ) ;
for ( int i = 0 ; i < numbuttons ; i + + )
{
string k = b . ReadString ( ) ;
float v = b . ReadSingle ( ) ;
buttons . Add ( k , v ) ;
}
}
/// <summary>
/// this controller's definition changes to that of source
/// </summary>
/// <param name="source"></param>
public void CopyFrom ( IController source )
{
this . def = source . Type ;
buttons . Clear ( ) ;
foreach ( var k in def . BoolButtons )
buttons . Add ( k , source . IsPressed ( k ) ? 1.0f : 0 ) ;
foreach ( var k in def . FloatControls )
{
if ( buttons . Keys . Contains ( k ) )
throw new Exception ( "name collision between bool and float lists!" ) ;
buttons . Add ( k , source . GetFloat ( k ) ) ;
}
}
public bool this [ string button ]
{
get { return buttons [ button ] ! = 0 ; }
}
public bool IsPressed ( string button )
{
return buttons [ button ] ! = 0 ;
}
public float GetFloat ( string name )
{
return buttons [ name ] ;
}
public void UpdateControls ( int frame )
{
throw new NotImplementedException ( ) ;
}
}
2012-09-04 08:21:01 +00:00
public void SaveStateText ( TextWriter writer )
{
var temp = SaveStateBinary ( ) ;
temp . SaveAsHex ( writer ) ;
2012-09-23 16:59:44 +00:00
// write extra copy of stuff we don't use
writer . WriteLine ( "Frame {0}" , Frame ) ;
2012-09-04 08:21:01 +00:00
}
public void LoadStateText ( TextReader reader )
{
string hex = reader . ReadLine ( ) ;
byte [ ] state = new byte [ hex . Length / 2 ] ;
state . ReadFromHex ( hex ) ;
LoadStateBinary ( new BinaryReader ( new MemoryStream ( state ) ) ) ;
}
2012-09-05 18:52:17 +00:00
2012-09-04 08:21:01 +00:00
public void SaveStateBinary ( BinaryWriter writer )
{
2012-10-08 14:37:42 +00:00
if ( ! DeterministicEmulation )
writer . Write ( CoreSaveState ( ) ) ;
else
writer . Write ( savestatebuff ) ;
2012-09-11 01:50:55 +00:00
// other variables
writer . Write ( IsLagFrame ) ;
writer . Write ( LagCount ) ;
writer . Write ( Frame ) ;
2012-09-04 08:21:01 +00:00
writer . Flush ( ) ;
}
public void LoadStateBinary ( BinaryReader reader )
{
int size = LibsnesDll . snes_serialize_size ( ) ;
2012-09-11 01:50:55 +00:00
byte [ ] buf = reader . ReadBytes ( size ) ;
2012-09-30 19:22:54 +00:00
CoreLoadState ( buf ) ;
2012-09-11 01:50:55 +00:00
2012-10-08 14:37:42 +00:00
if ( DeterministicEmulation ) // deserialize controller and fast-foward now
{
// reconstruct savestatebuff at the same time to avoid a costly core serialize
MemoryStream ms = new MemoryStream ( ) ;
BinaryWriter bw = new BinaryWriter ( ms ) ;
bw . Write ( buf ) ;
bool framezero = reader . ReadBoolean ( ) ;
bw . Write ( framezero ) ;
if ( ! framezero )
{
SnesSaveController ssc = new SnesSaveController ( ControllerDefinition ) ;
ssc . DeSerialize ( reader ) ;
IController tmp = this . Controller ;
this . Controller = ssc ;
nocallbacks = true ;
FrameAdvance ( false , false ) ;
nocallbacks = false ;
this . Controller = tmp ;
ssc . Serialize ( bw ) ;
}
2012-10-08 18:18:43 +00:00
else // hack: dummy controller info
{
bw . Write ( reader . ReadBytes ( 536 ) ) ;
}
2012-10-08 14:37:42 +00:00
bw . Close ( ) ;
savestatebuff = ms . ToArray ( ) ;
}
2012-09-11 01:50:55 +00:00
// other variables
IsLagFrame = reader . ReadBoolean ( ) ;
LagCount = reader . ReadInt32 ( ) ;
Frame = reader . ReadInt32 ( ) ;
2012-09-04 08:21:01 +00:00
}
public byte [ ] SaveStateBinary ( )
{
MemoryStream ms = new MemoryStream ( ) ;
BinaryWriter bw = new BinaryWriter ( ms ) ;
SaveStateBinary ( bw ) ;
bw . Flush ( ) ;
return ms . ToArray ( ) ;
}
2012-09-04 00:20:36 +00:00
2012-09-30 19:22:54 +00:00
/// <summary>
/// handle the unmanaged part of loadstating
/// </summary>
void CoreLoadState ( byte [ ] data )
{
int size = LibsnesDll . snes_serialize_size ( ) ;
if ( data . Length ! = size )
throw new Exception ( "Libsnes internal savestate size mismatch!" ) ;
fixed ( byte * pbuf = & data [ 0 ] )
LibsnesDll . snes_unserialize ( new IntPtr ( pbuf ) , size ) ;
}
/// <summary>
/// handle the unmanaged part of savestating
/// </summary>
byte [ ] CoreSaveState ( )
{
2012-10-08 14:37:42 +00:00
int size = LibsnesDll . snes_serialize_size ( ) ;
byte [ ] buf = new byte [ size ] ;
fixed ( byte * pbuf = & buf [ 0 ] )
LibsnesDll . snes_serialize ( new IntPtr ( pbuf ) , size ) ;
return buf ;
2012-09-30 19:22:54 +00:00
}
/// <summary>
2012-10-08 14:37:42 +00:00
/// most recent internal savestate, for deterministic mode ONLY
2012-09-30 19:22:54 +00:00
/// </summary>
byte [ ] savestatebuff ;
2012-10-08 14:37:42 +00:00
#endregion
2012-09-30 19:22:54 +00:00
2012-09-04 00:20:36 +00:00
// Arbitrary extensible core comm mechanism
2012-09-04 19:12:16 +00:00
public CoreInputComm CoreInputComm { get ; set ; }
public CoreOutputComm CoreOutputComm { get { return _CoreOutputComm ; } }
CoreOutputComm _CoreOutputComm = new CoreOutputComm ( ) ;
2012-09-04 00:20:36 +00:00
// ----- Client Debugging API stuff -----
2012-09-04 17:29:20 +00:00
unsafe MemoryDomain MakeMemoryDomain ( string name , LibsnesDll . SNES_MEMORY id , Endian endian )
{
2012-09-21 06:03:27 +00:00
IntPtr block = LibsnesDll . snes_get_memory_data ( id ) ;
2012-09-04 17:29:20 +00:00
int size = LibsnesDll . snes_get_memory_size ( id ) ;
int mask = size - 1 ;
byte * blockptr = ( byte * ) block . ToPointer ( ) ;
MemoryDomain md ;
2012-09-05 18:52:17 +00:00
2012-09-04 17:29:20 +00:00
//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)
md = new MemoryDomain ( name , size , endian ,
( addr ) = > blockptr [ addr % size ] ,
( addr , value ) = > blockptr [ 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 )
{
MemoryDomains = new List < MemoryDomain > ( ) ;
2012-09-05 18:52:17 +00:00
2012-09-04 19:12:16 +00:00
var romDomain = new MemoryDomain ( "CARTROM" , romData . Length , Endian . Little ,
2012-09-04 17:29:20 +00:00
( addr ) = > romData [ addr ] ,
( addr , value ) = > romData [ addr ] = value ) ;
MainMemory = MakeMemoryDomain ( "WRAM" , LibsnesDll . 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 ) ;
2012-10-03 14:54:32 +00:00
2012-10-03 15:11:21 +00:00
if ( ! DeterministicEmulation )
MemoryDomains . Add ( new MemoryDomain ( "BUS" , 0x1000000 , Endian . Little ,
( addr ) = > LibsnesDll . bus_read ( ( uint ) addr ) ,
( addr , val ) = > LibsnesDll . bus_write ( ( uint ) addr , val ) ) ) ;
2012-09-04 17:29:20 +00:00
}
public IList < MemoryDomain > MemoryDomains { get ; private set ; }
public MemoryDomain MainMemory { get ; private set ; }
2012-09-04 01:21:14 +00:00
2012-09-05 18:52:17 +00:00
#region audio stuff
2012-09-04 01:21:14 +00:00
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
Sound . Utilities . SpeexResampler resampler ;
2012-09-07 20:12:47 +00:00
void InitAudio ( )
{
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
resampler = new Sound . Utilities . SpeexResampler ( 6 , 64081 , 88200 , 32041 , 44100 ) ;
2012-09-07 20:12:47 +00:00
}
2012-09-04 01:21:14 +00:00
2012-09-05 18:52:17 +00:00
void snes_audio_sample ( ushort left , ushort right )
{
2012-09-30 18:21:32 +00:00
resampler . EnqueueSample ( ( short ) left , ( short ) right ) ;
2012-09-05 18:52:17 +00:00
}
2012-09-04 01:21:14 +00:00
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
public ISoundProvider SoundProvider { get { return null ; } }
public ISyncSoundProvider SyncSoundProvider { get { return resampler ; } }
public bool StartAsyncSound ( ) { return false ; }
public void EndAsyncSound ( ) { }
2012-09-05 18:52:17 +00:00
#endregion audio stuff
2012-09-04 00:20:36 +00:00
}
2012-09-30 18:21:32 +00:00
}