2013-04-29 01:57:41 +00:00
using System ;
2014-04-20 01:44:06 +00:00
using System.Threading ;
2013-04-29 01:57:41 +00:00
using System.Collections.Generic ;
using System.IO ;
2014-07-03 18:54:53 +00:00
using BizHawk.Common.BufferExtensions ;
2013-11-04 01:06:36 +00:00
using BizHawk.Emulation.Common ;
2014-12-05 00:59:00 +00:00
using BizHawk.Emulation.Common.IEmulatorExtensions ;
2014-01-24 17:46:35 +00:00
using BizHawk.Emulation.Cores.Nintendo.N64.NativeApi ;
2013-10-27 22:07:40 +00:00
2013-11-13 03:32:25 +00:00
namespace BizHawk.Emulation.Cores.Nintendo.N64
2013-04-29 01:57:41 +00:00
{
2014-04-25 01:19:57 +00:00
[ CoreAttributes (
"Mupen64Plus" ,
2014-06-01 01:57:22 +00:00
"" ,
2014-04-25 01:19:57 +00:00
isPorted : true ,
2014-06-01 01:57:22 +00:00
isReleased : true ,
portedVersion : "2.0" ,
2015-03-03 00:23:50 +00:00
portedUrl : "https://code.google.com/p/mupen64plus/" ,
singleInstance : true
2014-04-25 01:19:57 +00:00
) ]
2014-12-12 01:58:12 +00:00
[ServiceNotApplicable(typeof(IDriveLight))]
2015-08-06 00:12:09 +00:00
public partial class N64 : IEmulator , ISaveRam , IDebuggable , IStatable , IInputPollable , IDisassemblable , IRegionable ,
2014-10-19 01:22:47 +00:00
ISettable < N64Settings , N64SyncSettings >
2013-11-11 03:20:33 +00:00
{
2014-05-13 00:31:32 +00:00
private readonly N64Input _inputProvider ;
private readonly N64VideoProvider _videoProvider ;
private readonly N64Audio _audioProvider ;
private readonly EventWaitHandle _pendingThreadEvent = new EventWaitHandle ( false , EventResetMode . AutoReset ) ;
private readonly EventWaitHandle _completeThreadEvent = new EventWaitHandle ( false , EventResetMode . AutoReset ) ;
private mupen64plusApi api ; // mupen64plus DLL Api
2014-06-01 12:06:22 +00:00
2014-05-13 00:31:32 +00:00
private N64SyncSettings _syncSettings ;
2014-06-01 12:06:22 +00:00
private N64Settings _settings ;
2014-05-13 00:31:32 +00:00
private bool _pendingThreadTerminate ;
private DisplayType _display_type = DisplayType . NTSC ;
private Action _pendingThreadAction ;
2014-09-27 12:56:55 +00:00
private bool _disableExpansionSlot = true ;
2014-05-13 00:06:33 +00:00
/// <summary>
/// Create mupen64plus Emulator
/// </summary>
/// <param name="comm">Core communication object</param>
/// <param name="game">Game information of game to load</param>
/// <param name="rom">Rom that should be loaded</param>
2014-09-07 21:48:05 +00:00
/// <param name="syncSettings">N64SyncSettings object</param>
2014-08-23 19:06:37 +00:00
[CoreConstructor("N64")]
2014-09-12 01:02:09 +00:00
public N64 ( CoreComm comm , GameInfo game , byte [ ] file , object settings , object syncSettings )
2014-05-13 00:06:33 +00:00
{
2014-12-04 03:38:30 +00:00
ServiceProvider = new BasicServiceProvider ( this ) ;
2014-12-05 02:22:41 +00:00
InputCallbacks = new InputCallbackSystem ( ) ;
2016-11-19 11:02:39 +00:00
_memorycallbacks . CallbackAdded + = AddBreakpoint ;
_memorycallbacks . CallbackRemoved + = RemoveBreakpoint ;
2014-12-04 03:38:30 +00:00
2014-05-13 00:06:33 +00:00
int SaveType = 0 ;
if ( game . OptionValue ( "SaveType" ) = = "EEPROM_16K" )
{
SaveType = 1 ;
}
CoreComm = comm ;
2014-06-01 12:06:22 +00:00
_syncSettings = ( N64SyncSettings ) syncSettings ? ? new N64SyncSettings ( ) ;
_settings = ( N64Settings ) settings ? ? new N64Settings ( ) ;
2014-05-13 00:06:33 +00:00
2014-09-27 12:56:55 +00:00
_disableExpansionSlot = _syncSettings . DisableExpansionSlot ;
2014-09-07 00:23:15 +00:00
2014-09-27 12:56:55 +00:00
// Override the user's expansion slot setting if it is mentioned in the gamedb (it is mentioned but the game MUST have this setting or else not work
2014-09-07 00:23:15 +00:00
if ( game . OptionValue ( "expansionpak" ) ! = null & & game . OptionValue ( "expansionpak" ) = = "1" )
{
2014-09-27 12:56:55 +00:00
_disableExpansionSlot = false ;
IsOverridingUserExpansionSlotSetting = true ;
2014-09-07 00:23:15 +00:00
}
2014-09-12 01:02:09 +00:00
byte country_code = file [ 0x3E ] ;
2014-05-13 00:06:33 +00:00
switch ( country_code )
{
// PAL codes
case 0x44 :
case 0x46 :
case 0x49 :
case 0x50 :
case 0x53 :
case 0x55 :
case 0x58 :
case 0x59 :
_display_type = DisplayType . PAL ;
break ;
// NTSC codes
case 0x37 :
case 0x41 :
case 0x45 :
case 0x4a :
default : // Fallback for unknown codes
_display_type = DisplayType . NTSC ;
break ;
}
2015-08-06 00:12:09 +00:00
switch ( Region )
2014-05-13 00:06:33 +00:00
{
case DisplayType . NTSC :
comm . VsyncNum = 60000 ;
comm . VsyncDen = 1001 ;
break ;
default :
comm . VsyncNum = 50 ;
comm . VsyncDen = 1 ;
break ;
}
StartThreadLoop ( ) ;
2014-06-01 12:06:22 +00:00
var videosettings = _syncSettings . GetVPS ( game , _settings . VideoSizeX , _settings . VideoSizeY ) ;
2014-07-20 16:59:03 +00:00
var coreType = _syncSettings . Core ;
2014-05-13 00:06:33 +00:00
//zero 19-apr-2014 - added this to solve problem with SDL initialization corrupting the main thread (I think) and breaking subsequent emulators (for example, NES)
//not sure why this works... if we put the plugin initializations in here, we get deadlocks in some SDL initialization. doesnt make sense to me...
RunThreadAction ( ( ) = >
{
2014-09-27 12:56:55 +00:00
api = new mupen64plusApi ( this , file , videosettings , SaveType , ( int ) coreType , _disableExpansionSlot ) ;
2014-05-13 00:06:33 +00:00
} ) ;
// Order is important because the register with the mupen core
_videoProvider = new N64VideoProvider ( api , videosettings ) ;
_audioProvider = new N64Audio ( api ) ;
2014-12-05 00:59:00 +00:00
_inputProvider = new N64Input ( this . AsInputPollable ( ) , api , comm , this . _syncSettings . Controllers ) ;
2015-01-14 22:37:37 +00:00
( ServiceProvider as BasicServiceProvider ) . Register < IVideoProvider > ( _videoProvider ) ;
2016-12-11 17:14:42 +00:00
( ServiceProvider as BasicServiceProvider ) . Register < ISoundProvider > ( _audioProvider . Resampler ) ;
2014-05-14 01:50:36 +00:00
2016-09-27 19:10:38 +00:00
string rsp ;
switch ( _syncSettings . Rsp )
{
default :
case N64SyncSettings . RspType . Rsp_Hle :
rsp = "mupen64plus-rsp-hle.dll" ;
break ;
case N64SyncSettings . RspType . Rsp_Z64_hlevideo :
rsp = "mupen64plus-rsp-z64-hlevideo.dll" ;
break ;
case N64SyncSettings . RspType . Rsp_cxd4 :
rsp = "mupen64plus-rsp-cxd4.dll" ;
break ;
}
2014-05-14 01:50:36 +00:00
api . AttachPlugin ( mupen64plusApi . m64p_plugin_type . M64PLUGIN_RSP , rsp ) ;
2014-05-13 00:06:33 +00:00
InitMemoryDomains ( ) ;
2016-08-24 16:21:16 +00:00
if ( _syncSettings . Core ! = N64SyncSettings . CoreType . Dynarec )
2016-11-19 11:02:39 +00:00
{
2016-08-24 16:21:16 +00:00
ConnectTracer ( ) ;
2016-11-19 11:02:39 +00:00
SetBreakpointHandler ( ) ;
}
2014-05-13 00:06:33 +00:00
api . AsyncExecuteEmulator ( ) ;
2015-03-17 00:11:08 +00:00
// Hack: Saving a state on frame 0 has been shown to not be sync stable. Advance past that frame to avoid the problem.
// Advancing 2 frames was chosen to deal with a problem with the dynamic recompiler. The dynarec seems to take 2 frames to set
// things up correctly. If a state is loaded on frames 0 or 1 mupen tries to access null pointers and the emulator crashes, so instead
2017-04-15 20:37:30 +00:00
// advance past both to again avoid the problem.
2015-03-17 00:11:08 +00:00
api . frame_advance ( ) ;
api . frame_advance ( ) ;
2014-06-28 13:00:53 +00:00
SetControllerButtons ( ) ;
2014-05-13 00:06:33 +00:00
}
2014-12-04 03:38:30 +00:00
public IEmulatorServiceProvider ServiceProvider { get ; private set ; }
2014-09-27 12:56:55 +00:00
public bool UsingExpansionSlot
{
get { return ! _disableExpansionSlot ; }
}
public bool IsOverridingUserExpansionSlotSetting { get ; set ; }
2014-05-13 00:31:32 +00:00
public void Dispose ( )
2013-11-11 03:20:33 +00:00
{
2014-05-13 00:31:32 +00:00
RunThreadAction ( ( ) = >
{
_videoProvider . Dispose ( ) ;
_audioProvider . Dispose ( ) ;
api . Dispose ( ) ;
} ) ;
2013-11-18 03:29:47 +00:00
2014-05-13 00:31:32 +00:00
EndThreadLoop ( ) ;
}
private void ThreadLoop ( )
{
for ( ; ; )
2013-11-18 03:29:47 +00:00
{
2014-05-13 00:31:32 +00:00
_pendingThreadEvent . WaitOne ( ) ;
_pendingThreadAction ( ) ;
if ( _pendingThreadTerminate )
{
break ;
}
_completeThreadEvent . Set ( ) ;
2013-11-18 03:29:47 +00:00
}
2014-05-13 00:31:32 +00:00
_pendingThreadTerminate = false ;
_completeThreadEvent . Set ( ) ;
}
2013-11-18 03:29:47 +00:00
2014-05-13 00:31:32 +00:00
private void RunThreadAction ( Action action )
{
_pendingThreadAction = action ;
_pendingThreadEvent . Set ( ) ;
_completeThreadEvent . WaitOne ( ) ;
}
2013-11-18 03:29:47 +00:00
2014-05-13 00:31:32 +00:00
private void StartThreadLoop ( )
{
2014-09-07 21:48:05 +00:00
var thread = new Thread ( ThreadLoop ) { IsBackground = true } ;
thread . Start ( ) ; // will this solve the hanging process problem?
2014-05-13 00:31:32 +00:00
}
2013-11-18 03:29:47 +00:00
2014-05-13 00:31:32 +00:00
private void EndThreadLoop ( )
{
RunThreadAction ( ( ) = > { _pendingThreadTerminate = true ; } ) ;
}
2013-11-18 03:29:47 +00:00
2014-05-13 00:31:32 +00:00
public void FrameAdvance ( bool render , bool rendersound )
{
2014-08-03 00:00:26 +00:00
IsVIFrame = false ;
2016-08-14 17:35:05 +00:00
2016-08-24 16:21:16 +00:00
if ( Tracer ! = null & & Tracer . Enabled )
2016-08-14 17:35:05 +00:00
{
api . setTraceCallback ( _tracecb ) ;
}
else
{
api . setTraceCallback ( null ) ;
}
2014-05-13 00:31:32 +00:00
_audioProvider . RenderSound = rendersound ;
2013-11-18 03:29:47 +00:00
2016-12-14 18:42:15 +00:00
if ( Controller . IsPressed ( "Reset" ) )
2013-11-18 03:29:47 +00:00
{
2014-05-13 00:31:32 +00:00
api . soft_reset ( ) ;
2013-11-18 03:29:47 +00:00
}
2016-12-14 18:42:15 +00:00
if ( Controller . IsPressed ( "Power" ) )
2013-11-18 03:29:47 +00:00
{
2014-05-13 00:31:32 +00:00
api . hard_reset ( ) ;
2013-11-18 03:29:47 +00:00
}
2014-05-13 00:31:32 +00:00
api . frame_advance ( ) ;
if ( IsLagFrame )
{
LagCount + + ;
}
Frame + + ;
2013-11-11 03:20:33 +00:00
}
2013-04-29 01:57:41 +00:00
public string SystemId { get { return "N64" ; } }
public CoreComm CoreComm { get ; private set ; }
2014-01-15 11:24:47 +00:00
2015-08-06 00:12:09 +00:00
public DisplayType Region { get { return _display_type ; } }
2013-05-04 02:46:37 +00:00
2014-05-13 00:31:32 +00:00
public ControllerDefinition ControllerDefinition
{
get { return _inputProvider . ControllerDefinition ; }
}
2014-01-24 17:46:35 +00:00
public IController Controller
2013-04-29 01:57:41 +00:00
{
2014-05-13 00:06:33 +00:00
get { return _inputProvider . Controller ; }
set { _inputProvider . Controller = value ; }
2014-01-24 17:46:35 +00:00
}
2013-04-29 01:57:41 +00:00
2013-11-03 16:29:51 +00:00
public void ResetCounters ( )
2013-05-05 22:53:22 +00:00
{
Frame = 0 ;
LagCount = 0 ;
IsLagFrame = false ;
}
2014-01-15 11:24:47 +00:00
public bool DeterministicEmulation { get { return false ; } }
2013-04-29 01:57:41 +00:00
}
}