2012-09-04 07:09:00 +00:00
using System ;
2012-09-04 00:20:36 +00:00
using System.Linq ;
2013-04-24 22:09:11 +00:00
using System.Xml ;
2012-09-04 00:20:36 +00:00
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 ;
2017-04-19 16:40:41 +00:00
using BizHawk.Emulation.Cores.Components.W65816 ;
// TODO - add serializer (?)
2013-10-27 22:07:40 +00:00
2017-04-19 16:40:41 +00:00
// http://wiki.superfamicom.org/snes/show/Backgrounds
// TODO
// libsnes needs to be modified to support multiple instances - THIS IS NECESSARY - or else loading one game and then another breaks things
// edit - this is a lot of work
// wrap dll code around some kind of library-accessing interface so that it doesnt malfunction if the dll is unavailablecd
2013-11-13 23:36:21 +00:00
namespace BizHawk.Emulation.Cores.Nintendo.SNES
2012-09-04 00:20:36 +00:00
{
2017-07-12 19:10:55 +00:00
[ Core (
2014-04-25 01:19:57 +00:00
"BSNES" ,
"byuu" ,
isPorted : true ,
2014-06-01 01:57:22 +00:00
isReleased : true ,
portedVersion : "v87" ,
2017-04-19 16:40:41 +00:00
portedUrl : "http://byuu.org/" ) ]
2014-12-12 01:58:12 +00:00
[ServiceNotApplicable(typeof(IDriveLight))]
2017-04-19 13:31:48 +00:00
public unsafe partial class LibsnesCore : IEmulator , IVideoProvider , ISaveRam , IStatable , IInputPollable , IRegionable , ICodeDataLogger ,
2014-10-19 01:22:47 +00:00
IDebuggable , ISettable < LibsnesCore . SnesSettings , LibsnesCore . SnesSyncSettings >
2012-09-04 00:20:36 +00:00
{
2017-06-30 00:56:17 +00:00
public LibsnesCore ( GameInfo game , byte [ ] romData , byte [ ] xmlData , CoreComm comm , object settings , object syncSettings )
2014-11-18 23:44:42 +00:00
{
2017-04-19 16:40:41 +00:00
var ser = new BasicServiceProvider ( this ) ;
2017-04-20 00:34:30 +00:00
ServiceProvider = ser ;
2017-04-19 16:40:41 +00:00
_tracer = new TraceBuffer
2016-08-15 22:39:26 +00:00
{
Header = "65816: PC, mnemonic, operands, registers (A, X, Y, S, D, DB, flags (NVMXDIZC), V, H)"
} ;
2014-12-05 01:56:45 +00:00
2017-04-19 16:40:41 +00:00
ser . Register < IDisassemblable > ( new W65816_DisassemblerService ( ) ) ;
2015-01-26 00:20:01 +00:00
2014-11-19 00:32:51 +00:00
_game = game ;
2014-11-18 23:44:42 +00:00
CoreComm = comm ;
byte [ ] sgbRomData = null ;
2017-04-19 16:40:41 +00:00
2014-11-18 23:44:42 +00:00
if ( game [ "SGB" ] )
{
if ( ( romData [ 0x143 ] & 0xc0 ) = = 0xc0 )
2017-04-19 16:40:41 +00:00
{
2014-11-18 23:44:42 +00:00
throw new CGBNotSupportedException ( ) ;
2017-04-19 16:40:41 +00:00
}
2014-11-18 23:44:42 +00:00
sgbRomData = CoreComm . CoreFileProvider . GetFirmware ( "SNES" , "Rom_SGB" , true , "SGB Rom is required for SGB emulation." ) ;
game . FirmwareHash = sgbRomData . HashSHA1 ( ) ;
}
2017-05-06 00:05:36 +00:00
_settings = ( SnesSettings ) settings ? ? new SnesSettings ( ) ;
_syncSettings = ( SnesSyncSettings ) syncSettings ? ? new SnesSyncSettings ( ) ;
2014-11-18 23:44:42 +00:00
2017-06-10 17:43:03 +00:00
// TODO: pass profile here
Api = new LibsnesApi ( CoreComm . CoreFileProvider . DllPath ( ) )
2017-04-19 16:40:41 +00:00
{
ReadHook = ReadHook ,
ExecHook = ExecHook ,
2017-08-23 14:08:43 +00:00
WriteHook = WriteHook ,
ReadHook_SMP = ReadHook_SMP ,
ExecHook_SMP = ExecHook_SMP ,
WriteHook_SMP = WriteHook_SMP ,
2017-04-19 16:40:41 +00:00
} ;
2014-11-18 23:44:42 +00:00
ScanlineHookManager = new MyScanlineHookManager ( this ) ;
2017-04-20 01:30:42 +00:00
_controllerDeck = new LibsnesControllerDeck ( _syncSettings ) ;
2017-04-19 17:19:16 +00:00
_controllerDeck . NativeInit ( Api ) ;
2017-04-15 23:34:12 +00:00
2017-04-19 17:19:16 +00:00
Api . CMD_init ( ) ;
2014-11-18 23:44:42 +00:00
2017-04-19 17:19:16 +00:00
Api . QUERY_set_path_request ( snes_path_request ) ;
2014-11-18 23:44:42 +00:00
2017-04-19 17:19:16 +00:00
_scanlineStartCb = new LibsnesApi . snes_scanlineStart_t ( snes_scanlineStart ) ;
_tracecb = new LibsnesApi . snes_trace_t ( snes_trace ) ;
2014-11-18 23:44:42 +00:00
2017-04-19 17:19:16 +00:00
_soundcb = new LibsnesApi . snes_audio_sample_t ( snes_audio_sample ) ;
2014-11-18 23:44:42 +00:00
// start up audio resampler
InitAudio ( ) ;
2017-04-19 17:19:16 +00:00
ser . Register < ISoundProvider > ( _resampler ) ;
2014-11-18 23:44:42 +00:00
2017-04-19 16:40:41 +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 ;
}
2014-11-18 23:44:42 +00:00
2017-04-19 17:19:16 +00:00
if ( game [ "SGB" ] )
2014-11-18 23:44:42 +00:00
{
IsSGB = true ;
SystemId = "SNES" ;
2017-05-01 02:01:37 +00:00
ser . Register < IBoardInfo > ( new SGBBoardInfo ( ) ) ;
2014-11-18 23:44:42 +00:00
2017-04-19 17:19:16 +00:00
_currLoadParams = new LoadParams ( )
2014-11-18 23:44:42 +00:00
{
type = LoadParamType . SuperGameBoy ,
rom_xml = null ,
rom_data = sgbRomData ,
rom_size = ( uint ) sgbRomData . Length ,
dmg_data = romData ,
} ;
if ( ! LoadCurrent ( ) )
2017-04-19 16:40:41 +00:00
{
2014-11-18 23:44:42 +00:00
throw new Exception ( "snes_load_cartridge_normal() failed" ) ;
2017-04-19 16:40:41 +00:00
}
2014-11-18 23:44:42 +00:00
}
else
{
2017-04-19 16:40:41 +00:00
// we may need to get some information out of the cart, even during the following bootup/load process
2014-11-18 23:44:42 +00:00
if ( xmlData ! = null )
{
2017-04-19 17:19:16 +00:00
_romxml = new XmlDocument ( ) ;
_romxml . Load ( new MemoryStream ( xmlData ) ) ;
2014-11-18 23:44:42 +00:00
2017-04-19 16:40:41 +00:00
// bsnes wont inspect the xml to load the necessary sfc file.
// so, we have to do that here and pass it in as the romData :/
2017-04-19 17:33:05 +00:00
if ( _romxml [ "cartridge" ] ? [ "rom" ] ! = null )
2017-04-19 16:40:41 +00:00
{
2017-04-19 17:19:16 +00:00
romData = File . ReadAllBytes ( CoreComm . CoreFileProvider . PathSubfile ( _romxml [ "cartridge" ] [ "rom" ] . Attributes [ "name" ] . Value ) ) ;
2017-04-19 16:40:41 +00:00
}
2014-11-18 23:44:42 +00:00
else
2017-04-19 16:40:41 +00:00
{
2014-11-18 23:44:42 +00:00
throw new Exception ( "Could not find rom file specification in xml file. Please check the integrity of your xml file" ) ;
2017-04-19 16:40:41 +00:00
}
2014-11-18 23:44:42 +00:00
}
SystemId = "SNES" ;
2017-04-19 17:33:05 +00:00
_currLoadParams = new LoadParams
2014-11-18 23:44:42 +00:00
{
type = LoadParamType . Normal ,
xml_data = xmlData ,
rom_data = romData
} ;
if ( ! LoadCurrent ( ) )
2017-04-19 16:40:41 +00:00
{
2014-11-18 23:44:42 +00:00
throw new Exception ( "snes_load_cartridge_normal() failed" ) ;
2017-04-19 16:40:41 +00:00
}
2014-11-18 23:44:42 +00:00
}
2017-04-19 17:19:16 +00:00
if ( Api . Region = = LibsnesApi . SNES_REGION . NTSC )
2014-11-18 23:44:42 +00:00
{
2017-04-19 16:40:41 +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.
2017-05-05 16:25:38 +00:00
VsyncNumerator = 21477272 ;
VsyncDenominator = 4 * 341 * 262 ;
2014-11-18 23:44:42 +00:00
}
else
{
2017-04-19 17:33:05 +00:00
// http://forums.nesdev.com/viewtopic.php?t=5367&start=19
2017-05-05 16:25:38 +00:00
VsyncNumerator = 21281370 ;
VsyncDenominator = 4 * 341 * 312 ;
2014-11-18 23:44:42 +00:00
}
2017-04-19 17:19:16 +00:00
Api . CMD_power ( ) ;
2014-11-18 23:44:42 +00:00
SetupMemoryDomains ( romData , sgbRomData ) ;
2017-05-27 14:54:53 +00:00
if ( CurrentProfile = = "Compatibility" )
{
ser . Register < ITraceable > ( _tracer ) ;
}
2017-06-10 19:20:06 +00:00
Api . QUERY_set_path_request ( null ) ;
Api . QUERY_set_video_refresh ( snes_video_refresh ) ;
Api . QUERY_set_input_poll ( snes_input_poll ) ;
Api . QUERY_set_input_state ( snes_input_state ) ;
Api . QUERY_set_input_notify ( snes_input_notify ) ;
Api . QUERY_set_audio_sample ( _soundcb ) ;
2017-06-10 19:53:38 +00:00
Api . Seal ( ) ;
RefreshPalette ( ) ;
2014-11-18 23:44:42 +00:00
}
2017-04-19 17:19:16 +00:00
private readonly GameInfo _game ;
private readonly LibsnesControllerDeck _controllerDeck ;
private readonly ITraceable _tracer ;
private readonly XmlDocument _romxml ;
private readonly LibsnesApi . snes_scanlineStart_t _scanlineStartCb ;
private readonly LibsnesApi . snes_trace_t _tracecb ;
private readonly LibsnesApi . snes_audio_sample_t _soundcb ;
2017-05-02 01:09:11 +00:00
private IController _controller ;
2017-04-19 17:19:16 +00:00
private LoadParams _currLoadParams ;
private SpeexResampler _resampler ;
private int _timeFrameCounter ;
private bool _disposed ;
public bool IsSGB { get ; }
2014-11-19 00:32:51 +00:00
2017-05-01 02:01:37 +00:00
private class SGBBoardInfo : IBoardInfo
{
public string BoardName = > "SGB" ;
}
2017-06-13 01:56:16 +00:00
public string CurrentProfile = > "Compatibility" ; // We no longer support performance, and accuracy isn't worth the effort so we shall just hardcode this one
2014-11-19 00:32:51 +00:00
2017-06-13 21:54:26 +00:00
public LibsnesApi Api { get ; }
2017-04-15 22:27:04 +00:00
2017-04-19 17:19:16 +00:00
public SnesColors . ColorType CurrPalette { get ; private set ; }
2012-10-08 14:37:42 +00:00
2017-04-19 17:19:16 +00:00
public MyScanlineHookManager ScanlineHookManager { get ; }
2017-01-10 01:23:05 +00:00
2012-09-22 05:03:52 +00:00
public class MyScanlineHookManager : ScanlineHookManager
{
2017-04-19 17:19:16 +00:00
private readonly LibsnesCore _core ;
2012-09-22 05:03:52 +00:00
public MyScanlineHookManager ( LibsnesCore core )
{
2017-04-19 17:19:16 +00:00
_core = core ;
2012-09-22 05:03:52 +00:00
}
2017-04-19 16:40:41 +00:00
2017-04-19 17:25:22 +00:00
protected override void OnHooksChanged ( )
2012-09-22 05:03:52 +00:00
{
2017-04-19 17:19:16 +00:00
_core . OnScanlineHooksChanged ( ) ;
2012-09-22 05:03:52 +00:00
}
}
2017-04-19 14:41:52 +00:00
2017-04-19 16:40:41 +00:00
private void OnScanlineHooksChanged ( )
2012-09-22 05:03:52 +00:00
{
2017-04-19 17:19:16 +00:00
if ( _disposed )
{
return ;
}
2017-04-19 17:33:05 +00:00
Api . QUERY_set_scanlineStart ( ScanlineHookManager . HookCount = = 0 ? null : _scanlineStartCb ) ;
2012-09-22 05:03:52 +00:00
}
2017-04-19 16:40:41 +00:00
private void snes_scanlineStart ( int line )
2012-09-22 05:03:52 +00:00
{
ScanlineHookManager . HandleScanline ( line ) ;
}
2017-04-19 16:40:41 +00:00
private string snes_path_request ( int slot , string hint )
2012-09-27 07:22:31 +00:00
{
2017-04-19 16:40:41 +00:00
// every rom requests msu1.rom... why? who knows.
// also handle msu-1 pcm files here
2017-04-19 17:19:16 +00:00
bool isMsu1Rom = hint = = "msu1.rom" ;
bool isMsu1Pcm = Path . GetExtension ( hint ) . ToLower ( ) = = ".pcm" ;
if ( isMsu1Rom | | isMsu1Pcm )
2013-04-24 22:09:11 +00:00
{
2017-04-19 16:40:41 +00:00
// well, check if we have an msu-1 xml
2017-04-19 17:33:05 +00:00
if ( _romxml ? [ "cartridge" ] ? [ "msu1" ] ! = null )
2013-04-24 22:09:11 +00:00
{
2017-04-19 17:19:16 +00:00
var msu1 = _romxml [ "cartridge" ] [ "msu1" ] ;
2017-04-19 17:33:05 +00:00
if ( isMsu1Rom & & msu1 [ "rom" ] ? . Attributes [ "name" ] ! = null )
2017-04-19 16:40:41 +00:00
{
2013-08-10 01:17:06 +00:00
return CoreComm . CoreFileProvider . PathSubfile ( msu1 [ "rom" ] . Attributes [ "name" ] . Value ) ;
2017-04-19 16:40:41 +00:00
}
2017-04-19 17:19:16 +00:00
if ( isMsu1Pcm )
2013-04-24 22:09:11 +00:00
{
2017-04-19 16:40:41 +00:00
// return @"D:\roms\snes\SuperRoadBlaster\SuperRoadBlaster-1.pcm";
// return "";
2017-05-10 11:45:23 +00:00
int wantsTrackNumber = int . Parse ( hint . Replace ( "track-" , "" ) . Replace ( ".pcm" , "" ) ) ;
2013-04-24 22:09:11 +00:00
wantsTrackNumber + + ;
string wantsTrackString = wantsTrackNumber . ToString ( ) ;
foreach ( var child in msu1 . ChildNodes . Cast < XmlNode > ( ) )
{
if ( child . Name = = "track" & & child . Attributes [ "number" ] . Value = = wantsTrackString )
2017-04-19 16:40:41 +00:00
{
2013-08-10 01:17:06 +00:00
return CoreComm . CoreFileProvider . PathSubfile ( child . Attributes [ "name" ] . Value ) ;
2017-04-19 16:40:41 +00:00
}
2013-04-24 22:09:11 +00:00
}
}
}
2013-04-24 17:17:25 +00:00
2017-04-19 16:40:41 +00:00
// not found.. what to do? (every rom will get here when msu1.rom is requested)
2017-05-10 11:45:23 +00:00
return "" ;
2013-04-24 22:09:11 +00:00
}
2012-09-27 07:22:31 +00:00
2013-12-09 21:41:18 +00:00
// not MSU-1. ok.
2017-04-19 17:33:05 +00:00
string firmwareId ;
2012-09-27 07:22:31 +00:00
2013-12-09 21:41:18 +00:00
switch ( hint )
2012-09-27 07:22:31 +00:00
{
2017-04-19 17:33:05 +00:00
case "cx4.rom" : firmwareId = "CX4" ; break ;
case "dsp1.rom" : firmwareId = "DSP1" ; break ;
case "dsp1b.rom" : firmwareId = "DSP1b" ; break ;
case "dsp2.rom" : firmwareId = "DSP2" ; break ;
case "dsp3.rom" : firmwareId = "DSP3" ; break ;
case "dsp4.rom" : firmwareId = "DSP4" ; break ;
case "st010.rom" : firmwareId = "ST010" ; break ;
case "st011.rom" : firmwareId = "ST011" ; break ;
case "st018.rom" : firmwareId = "ST018" ; break ;
2013-12-09 21:41:18 +00:00
default :
2017-04-19 16:40:41 +00:00
CoreComm . ShowMessage ( $"Unrecognized SNES firmware request \" { hint } \ "." ) ;
2017-05-10 11:45:23 +00:00
return "" ;
2012-09-27 07:22:31 +00:00
}
2017-07-02 01:02:52 +00:00
string ret ;
var data = CoreComm . CoreFileProvider . GetFirmware ( "SNES" , firmwareId , false , "Game may function incorrectly without the requested firmware." ) ;
if ( data ! = null )
{
ret = hint ;
Api . AddReadonlyFile ( data , hint ) ;
}
else
{
ret = "" ;
}
2014-05-23 00:13:04 +00:00
2017-07-02 01:02:52 +00:00
Console . WriteLine ( "Served libsnes request for firmware \"{0}\"" , hint ) ;
2012-10-28 23:42:04 +00:00
2017-04-19 16:40:41 +00:00
// return the path we built
2017-07-02 01:02:52 +00:00
return ret ;
2012-09-27 07:22:31 +00:00
}
2017-05-14 18:51:02 +00:00
private void snes_trace ( uint which , string msg )
2012-12-03 01:48:18 +00:00
{
2016-02-21 22:34:14 +00:00
// TODO: get them out of the core split up and remove this hackery
2017-05-14 18:51:02 +00:00
const string splitStr = "A:" ;
2016-02-21 22:34:14 +00:00
2017-05-14 18:51:02 +00:00
if ( which = = ( uint ) LibsnesApi . eTRACE . CPU )
{
var split = msg . Split ( new [ ] { splitStr } , 2 , StringSplitOptions . None ) ;
2016-02-21 22:34:14 +00:00
2017-05-14 18:51:02 +00:00
_tracer . Put ( new TraceInfo
{
Disassembly = split [ 0 ] . PadRight ( 34 ) ,
RegisterInfo = splitStr + split [ 1 ]
} ) ;
}
else if ( which = = ( uint ) LibsnesApi . eTRACE . SMP )
{
int idx = msg . IndexOf ( "YA:" ) ;
string dis = msg . Substring ( 0 , idx ) . TrimEnd ( ) ;
string regs = msg . Substring ( idx ) ;
_tracer . Put ( new TraceInfo
{
Disassembly = dis ,
RegisterInfo = regs
} ) ;
}
else if ( which = = ( uint ) LibsnesApi . eTRACE . GB )
2016-02-21 22:34:14 +00:00
{
2017-05-14 18:51:02 +00:00
int idx = msg . IndexOf ( "AF:" ) ;
string dis = msg . Substring ( 0 , idx ) . TrimEnd ( ) ;
string regs = msg . Substring ( idx ) ;
_tracer . Put ( new TraceInfo
{
Disassembly = dis ,
RegisterInfo = regs
} ) ;
}
2012-12-03 01:48:18 +00:00
}
2017-04-19 17:19:16 +00:00
private void SetPalette ( SnesColors . ColorType pal )
2012-11-25 20:06:31 +00:00
{
CurrPalette = pal ;
int [ ] tmp = SnesColors . GetLUT ( pal ) ;
fixed ( int * p = & tmp [ 0 ] )
2017-04-19 17:19:16 +00:00
Api . QUERY_set_color_lut ( ( IntPtr ) p ) ;
2012-11-25 20:06:31 +00:00
}
2017-04-19 17:19:16 +00:00
private void ReadHook ( uint addr )
2013-11-12 02:34:56 +00:00
{
2017-08-03 23:08:07 +00:00
MemoryCallbacks . CallReads ( addr , "System Bus" ) ;
2017-04-19 16:40:41 +00:00
// we RefreshMemoryCallbacks() after the trigger in case the trigger turns itself off at that point
// EDIT: for now, theres some IPC re-entrancy problem
// RefreshMemoryCallbacks();
2013-11-12 02:34:56 +00:00
}
2017-04-19 16:40:41 +00:00
private void ExecHook ( uint addr )
2013-11-12 02:34:56 +00:00
{
2017-08-03 23:08:07 +00:00
MemoryCallbacks . CallExecutes ( addr , "System Bus" ) ;
2017-04-19 16:40:41 +00:00
// we RefreshMemoryCallbacks() after the trigger in case the trigger turns itself off at that point
// EDIT: for now, theres some IPC re-entrancy problem
// RefreshMemoryCallbacks();
2013-11-12 02:34:56 +00:00
}
2017-04-19 16:40:41 +00:00
private void WriteHook ( uint addr , byte val )
2013-11-12 02:34:56 +00:00
{
2017-08-03 23:08:07 +00:00
MemoryCallbacks . CallWrites ( addr , "System Bus" ) ;
2017-04-19 16:40:41 +00:00
// we RefreshMemoryCallbacks() after the trigger in case the trigger turns itself off at that point
// EDIT: for now, theres some IPC re-entrancy problem
// RefreshMemoryCallbacks();
2012-09-27 07:22:31 +00:00
}
2017-08-23 14:08:43 +00:00
private void ReadHook_SMP ( uint addr )
{
MemoryCallbacks . CallReads ( addr , "SMP" ) ;
}
private void ExecHook_SMP ( uint addr )
{
MemoryCallbacks . CallExecutes ( addr , "SMP" ) ;
}
private void WriteHook_SMP ( uint addr , byte val )
{
MemoryCallbacks . CallWrites ( addr , "SMP" ) ;
}
2017-04-19 16:40:41 +00:00
private enum LoadParamType
2014-05-23 00:13:04 +00:00
{
Normal , SuperGameBoy
}
2017-04-19 16:40:41 +00:00
private struct LoadParams
2014-05-23 00:13:04 +00:00
{
public LoadParamType type ;
public byte [ ] xml_data ;
public string rom_xml ;
public byte [ ] rom_data ;
public uint rom_size ;
public byte [ ] dmg_data ;
}
2017-04-19 16:40:41 +00:00
private bool LoadCurrent ( )
2014-05-23 00:13:04 +00:00
{
2017-04-19 17:19:16 +00:00
bool result = _currLoadParams . type = = LoadParamType . Normal
? Api . CMD_load_cartridge_normal ( _currLoadParams . xml_data , _currLoadParams . rom_data )
: Api . CMD_load_cartridge_super_game_boy ( _currLoadParams . rom_xml , _currLoadParams . rom_data , _currLoadParams . rom_size , _currLoadParams . dmg_data ) ;
2016-08-08 11:40:06 +00:00
2017-04-19 17:19:16 +00:00
_mapper = Api . Mapper ;
2016-08-08 11:40:06 +00:00
return result ;
2014-05-23 00:13:04 +00:00
}
2012-12-29 02:43:00 +00:00
/// <summary>
/// </summary>
/// <param name="port">0 or 1, corresponding to L and R physical ports on the snes</param>
/// <param name="device">LibsnesApi.SNES_DEVICE enum index specifying type of device</param>
/// <param name="index">meaningless for most controllers. for multitap, 0-3 for which multitap controller</param>
/// <param name="id">button ID enum; in the case of a regular controller, this corresponds to shift register position</param>
/// <returns>for regular controllers, one bit D0 of button status. for other controls, varying ranges depending on id</returns>
2017-04-19 16:40:41 +00:00
private short snes_input_state ( int port , int device , int index , int id )
2012-09-04 00:20:36 +00:00
{
2017-05-02 01:09:11 +00:00
return _controllerDeck . CoreInputState ( _controller , port , device , index , id ) ;
2012-09-04 00:20:36 +00:00
}
2017-04-19 16:40:41 +00:00
private 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
}
2017-04-19 16:40:41 +00:00
private void snes_input_notify ( int index )
2012-09-04 00:20:36 +00:00
{
2016-03-01 02:22:30 +00:00
// gets called with the following numbers:
// 4xxx : lag frame related
// 0: signifies latch bit going to 0. should be reported as oninputpoll
// 1: signifies latch bit going to 1. should be reported as oninputpoll
if ( index > = 0x4000 )
2017-04-19 17:19:16 +00:00
{
2016-03-01 02:22:30 +00:00
IsLagFrame = false ;
2017-04-19 17:19:16 +00:00
}
2012-09-04 00:20:36 +00:00
}
2017-04-19 16:40:41 +00:00
private void snes_video_refresh ( int * data , int width , int height )
2012-09-04 00:20:36 +00:00
{
2017-04-19 13:31:48 +00:00
bool doubleSize = _settings . AlwaysDoubleSize ;
2013-04-22 22:34:18 +00:00
bool lineDouble = doubleSize , dotDouble = doubleSize ;
2017-04-19 14:41:52 +00:00
_videoWidth = width ;
_videoHeight = height ;
2012-09-30 05:17:08 +00:00
2013-04-22 22:34:18 +00:00
int yskip = 1 , xskip = 1 ;
2017-04-19 16:40:41 +00:00
// if we are in high-res mode, we get double width. so, lets double the height here to keep it square.
2012-09-24 08:00:42 +00:00
if ( width = = 512 )
2013-04-22 22:34:18 +00:00
{
2017-04-19 14:41:52 +00:00
_videoHeight * = 2 ;
2013-04-22 22:34:18 +00:00
yskip = 2 ;
lineDouble = true ;
2017-04-19 16:40:41 +00:00
// we dont dot double here because the user wanted double res and the game provided double res
2013-04-22 22:34:18 +00:00
dotDouble = false ;
}
else if ( lineDouble )
2012-09-24 08:00:42 +00:00
{
2017-04-19 14:41:52 +00:00
_videoHeight * = 2 ;
2012-09-24 08:00:42 +00:00
yskip = 2 ;
}
2012-09-30 05:17:08 +00:00
int srcPitch = 1024 ;
int srcStart = 0 ;
2017-04-19 16:40:41 +00:00
bool interlaced = height = = 478 | | height = = 448 ;
2012-09-30 05:17:08 +00:00
if ( interlaced )
{
2017-04-19 16:40:41 +00:00
// from bsnes in interlaced mode we have each field side by side
// so we will come in with a dimension of 512x448, say
// but the fields are side by side, so it's actually 1024x224.
// copy the first scanline from row 0, then the 2nd scanline from row 0 (offset 512)
// EXAMPLE: yu yu hakushu legal screens
// EXAMPLE: World Class Service Super Nintendo Tester (double resolution vertically but not horizontally, in character test the stars should shrink)
2015-09-24 01:28:38 +00:00
lineDouble = false ;
srcPitch = 512 ;
yskip = 1 ;
2017-04-19 14:41:52 +00:00
_videoHeight = height ;
2012-09-30 05:17:08 +00:00
}
2013-04-22 22:34:18 +00:00
if ( dotDouble )
{
2017-04-19 14:41:52 +00:00
_videoWidth * = 2 ;
2013-04-22 22:34:18 +00:00
xskip = 2 ;
}
2017-07-14 22:55:21 +00:00
if ( _settings . CropSGBFrame & & IsSGB )
2017-06-11 23:20:55 +00:00
{
_videoWidth = 160 ;
_videoHeight = 144 ;
}
2017-04-19 14:41:52 +00:00
int size = _videoWidth * _videoHeight ;
if ( _videoBuffer . Length ! = size )
2017-04-19 16:40:41 +00:00
{
2017-04-19 14:41:52 +00:00
_videoBuffer = new int [ size ] ;
2017-04-19 16:40:41 +00:00
}
2012-09-24 08:00:42 +00:00
2017-07-14 22:55:21 +00:00
if ( _settings . CropSGBFrame & & IsSGB )
2017-06-11 23:20:55 +00:00
{
int di = 0 ;
for ( int y = 0 ; y < 144 ; y + + )
{
int si = ( ( y + 39 ) * srcPitch ) + 48 ;
for ( int x = 0 ; x < 160 ; x + + )
_videoBuffer [ di + + ] = data [ si + + ] ;
}
return ;
}
2013-04-22 22:34:18 +00:00
for ( int j = 0 ; j < 2 ; j + + )
{
2017-04-19 16:40:41 +00:00
if ( j = = 1 & & ! dotDouble )
{
break ;
}
2013-04-22 22:34:18 +00:00
int xbonus = j ;
for ( int i = 0 ; i < 2 ; i + + )
2012-09-04 00:20:36 +00:00
{
2017-04-19 16:40:41 +00:00
// potentially do this twice, if we need to line double
if ( i = = 1 & & ! lineDouble )
{
break ;
}
2013-04-22 22:34:18 +00:00
2017-04-19 17:19:16 +00:00
int bonus = ( i * _videoWidth ) + xbonus ;
2013-04-22 22:34:18 +00:00
for ( int y = 0 ; y < height ; y + + )
2017-04-19 17:19:16 +00:00
{
2013-04-22 22:34:18 +00:00
for ( int x = 0 ; x < width ; x + + )
{
2017-04-19 17:33:05 +00:00
int si = ( y * srcPitch ) + x + srcStart ;
2017-04-19 14:41:52 +00:00
int di = y * _videoWidth * yskip + x * xskip + bonus ;
2013-04-22 22:34:18 +00:00
int rgb = data [ si ] ;
2017-04-19 14:41:52 +00:00
_videoBuffer [ di ] = rgb ;
2013-04-22 22:34:18 +00:00
}
2017-04-19 17:19:16 +00:00
}
2012-09-04 00:20:36 +00:00
}
2013-04-22 22:34:18 +00:00
}
2017-06-11 11:15:51 +00:00
VirtualHeight = BufferHeight ;
VirtualWidth = BufferWidth ;
if ( VirtualHeight * 2 < VirtualWidth )
VirtualHeight * = 2 ;
if ( VirtualHeight > 240 )
VirtualWidth = 512 ;
VirtualWidth = ( int ) Math . Round ( VirtualWidth * 1.146 ) ;
2012-09-04 00:20:36 +00:00
}
2017-04-19 16:40:41 +00:00
private void RefreshMemoryCallbacks ( bool suppress )
2013-11-12 02:34:56 +00:00
{
2014-12-05 01:56:45 +00:00
var mcs = MemoryCallbacks ;
2017-08-23 14:08:43 +00:00
Api . QUERY_set_state_hook_exec ( ! suppress & & mcs . HasExecutesForScope ( "System Bus" ) ) ;
Api . QUERY_set_state_hook_read ( ! suppress & & mcs . HasReadsForScope ( "System Bus" ) ) ;
Api . QUERY_set_state_hook_write ( ! suppress & & mcs . HasWritesForScope ( "System Bus" ) ) ;
2013-11-12 02:34:56 +00:00
}
2012-12-25 20:36:04 +00:00
//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;
//}
2017-04-19 17:19:16 +00:00
private void InitAudio ( )
{
2017-05-02 22:15:33 +00:00
_resampler = new SpeexResampler ( ( SpeexResampler . Quality ) 6 , 64081 , 88200 , 32041 , 44100 ) ;
2017-04-19 17:19:16 +00:00
}
private void snes_audio_sample ( ushort left , ushort right )
{
_resampler . EnqueueSample ( ( short ) left , ( short ) right ) ;
}
private void RefreshPalette ( )
{
SetPalette ( ( SnesColors . ColorType ) Enum . Parse ( typeof ( SnesColors . ColorType ) , _settings . Palette , false ) ) ;
}
2012-09-04 00:20:36 +00:00
}
2012-09-30 18:21:32 +00:00
}