2013-11-22 09:33:56 +00:00
//types of messages:
2013-12-29 02:20:13 +00:00
//cmd: frontend->core: "command to core" a command from the frontend which causes emulation to proceed. when sending a command, the frontend should wait for an eMessage_BRK_Complete before proceeding, although a debugger might proceed after any BRK
//query: frontend->core: "query to core" a query from the frontend which can (and should) be satisfied immediately by the core but which does not result in emulation processes (notably, nothing resembling a CMD and nothing which can trigger a BRK)
2013-11-22 09:33:56 +00:00
//sig: core->frontend: "core signal" a synchronous operation called from the emulation process which the frontend should handle immediately without issuing any calls into the core
2013-12-29 02:20:13 +00:00
//brk: core->frontend: "core break" the emulation process has suspended. the frontend is free to do whatever it wishes.
2013-11-22 09:33:56 +00:00
2012-12-21 07:23:55 +00:00
# include <Windows.h>
2012-12-25 20:36:04 +00:00
# define LIBSNES_IMPORT
# include "snes/snes.hpp"
# include "libsnes.hpp"
2013-11-22 09:33:56 +00:00
# include <libco/libco.h>
2012-12-21 07:23:55 +00:00
# include <string.h>
# include <stdio.h>
# include <stdlib.h>
# include <string>
# include <vector>
2015-11-02 17:26:49 +00:00
extern SNES : : Interface * iface ;
2012-12-27 07:59:19 +00:00
typedef uint8 u8 ;
2017-03-06 12:00:17 +00:00
typedef uint16 u16 ;
2017-03-06 09:21:10 +00:00
typedef uint64 u64 ;
2012-12-27 07:59:19 +00:00
typedef uint32 u32 ;
2013-11-22 09:33:56 +00:00
2017-03-06 12:00:17 +00:00
typedef int32 s32 ;
2017-03-06 09:21:10 +00:00
2017-03-06 12:00:17 +00:00
typedef void ( * Action ) ( ) ;
2017-03-06 09:21:10 +00:00
2013-11-22 09:33:56 +00:00
enum eMessage : int32
{
2013-11-25 20:51:26 +00:00
eMessage_NotSet ,
2013-11-22 09:33:56 +00:00
2017-03-06 12:00:17 +00:00
eMessage_Resume ,
2013-11-22 09:33:56 +00:00
2017-03-06 09:21:10 +00:00
eMessage_QUERY_FIRST ,
2013-11-22 09:33:56 +00:00
eMessage_QUERY_get_memory_size ,
eMessage_QUERY_peek ,
eMessage_QUERY_poke ,
eMessage_QUERY_serialize_size ,
eMessage_QUERY_set_color_lut ,
eMessage_QUERY_GetMemoryIdName ,
eMessage_QUERY_state_hook_exec ,
eMessage_QUERY_state_hook_read ,
eMessage_QUERY_state_hook_write ,
eMessage_QUERY_state_hook_nmi ,
eMessage_QUERY_state_hook_irq ,
eMessage_QUERY_enable_trace ,
eMessage_QUERY_enable_scanline ,
eMessage_QUERY_enable_audio ,
eMessage_QUERY_set_layer_enable ,
eMessage_QUERY_set_backdropColor ,
eMessage_QUERY_peek_logical_register ,
eMessage_QUERY_peek_cpu_regs ,
2015-11-02 17:26:49 +00:00
eMessage_QUERY_set_cdl ,
2017-03-06 09:21:10 +00:00
eMessage_QUERY_LAST ,
2015-11-02 17:26:49 +00:00
2013-11-25 20:51:26 +00:00
eMessage_CMD_FIRST ,
2013-11-22 09:33:56 +00:00
eMessage_CMD_init ,
eMessage_CMD_power ,
eMessage_CMD_reset ,
eMessage_CMD_run ,
eMessage_CMD_serialize ,
eMessage_CMD_unserialize ,
eMessage_CMD_load_cartridge_normal ,
2017-03-06 09:21:10 +00:00
eMessage_CMD_load_cartridge_sgb ,
2013-11-22 09:33:56 +00:00
eMessage_CMD_term ,
eMessage_CMD_unload_cartridge ,
2013-11-25 20:51:26 +00:00
eMessage_CMD_LAST ,
2013-11-22 09:33:56 +00:00
eMessage_SIG_video_refresh ,
eMessage_SIG_input_poll ,
eMessage_SIG_input_state ,
eMessage_SIG_input_notify ,
eMessage_SIG_audio_flush ,
eMessage_SIG_path_request ,
eMessage_SIG_trace_callback ,
2017-03-06 09:21:10 +00:00
eMessage_SIG_allocSharedMemory ,
eMessage_SIG_freeSharedMemory ,
2013-11-22 09:33:56 +00:00
eMessage_BRK_Complete ,
eMessage_BRK_hook_exec ,
eMessage_BRK_hook_read ,
eMessage_BRK_hook_write ,
eMessage_BRK_hook_nmi ,
eMessage_BRK_hook_irq ,
2017-03-06 09:21:10 +00:00
eMessage_BRK_scanlineStart ,
2013-11-22 09:33:56 +00:00
} ;
2017-03-06 09:21:10 +00:00
enum eStatus : int32
2013-11-22 09:33:56 +00:00
{
2017-03-06 09:21:10 +00:00
eStatus_Idle ,
eStatus_CMD ,
eStatus_BRK
2013-11-22 09:33:56 +00:00
} ;
2017-03-06 09:21:10 +00:00
//watch it! the size of this struct is important!
# ifdef _MSC_VER
# pragma pack(push,1)
# endif
struct CPURegsComm {
u32 pc ;
u16 a , x , y , z , s , d , vector ; //7x
u8 p , nothing ;
u32 aa , rd ;
u8 sp , dp , db , mdr ;
}
# ifndef _MSC_VER
__attribute__ ( ( __packed__ ) )
# endif
;
# ifdef _MSC_VER
# pragma pack(pop)
# endif
struct LayerEnablesComm
{
u8 BG1_Prio0 , BG1_Prio1 ;
u8 BG2_Prio0 , BG2_Prio1 ;
u8 BG3_Prio0 , BG3_Prio1 ;
u8 BG4_Prio0 , BG4_Prio1 ;
u8 Obj_Prio0 , Obj_Prio1 , Obj_Prio2 , Obj_Prio3 ;
2013-11-22 09:33:56 +00:00
} ;
2017-03-06 09:21:10 +00:00
//TODO: do any of these need to be volatile?
struct CommStruct
2013-11-22 09:33:56 +00:00
{
2017-03-06 09:21:10 +00:00
//the cmd being executed
eMessage cmd ;
2013-11-22 09:33:56 +00:00
2017-03-06 09:21:10 +00:00
//the status of the core
eStatus status ;
2013-11-22 09:33:56 +00:00
2017-03-06 09:21:10 +00:00
//the SIG or BRK that the core is halted in
eMessage reason ;
2012-12-27 07:59:19 +00:00
2017-03-06 09:21:10 +00:00
//flexible in/out parameters
//these are all "overloaded" a little so it isn't clear what's used for what in for any particular message..
//but I think it will beat having to have some kind of extremely verbose custom layouts for every message
char * str ;
void * ptr ;
uint32 id , addr , value , size ;
int32 port , device , index , slot ;
2017-03-06 12:00:17 +00:00
int32 width , height ;
2017-03-06 09:21:10 +00:00
int32 scanline ;
2017-04-09 21:45:05 +00:00
SNES : : Input : : Device inports [ 2 ] ;
2012-12-27 07:59:19 +00:00
2017-03-06 09:21:10 +00:00
//always used in pairs
2017-04-19 03:09:04 +00:00
void * buf [ 3 ] ;
int32 buf_size [ 3 ] ;
2012-12-27 07:59:19 +00:00
2017-03-06 12:00:17 +00:00
int64 cdl_ptr [ 4 ] ;
2017-03-06 11:32:09 +00:00
int32 cdl_size [ 4 ] ;
2012-12-27 07:59:19 +00:00
2017-03-06 09:21:10 +00:00
CPURegsComm cpuregs ;
LayerEnablesComm layerEnables ;
2012-12-27 07:59:19 +00:00
2017-03-06 09:21:10 +00:00
//static configuration-type information which can be grabbed off the core at any time without even needing a QUERY command
uint32 region ;
uint32 mapper ;
2012-12-27 07:59:19 +00:00
2017-03-06 09:21:10 +00:00
//===========================================================
2012-12-27 07:59:19 +00:00
2017-03-06 09:21:10 +00:00
//private stuff
void * privbuf [ 2 ] ; //TODO remember to tidy this..
2017-04-19 03:09:04 +00:00
2017-03-06 09:21:10 +00:00
void CopyBuffer ( int id , void * ptr , int32 size )
2012-12-27 07:59:19 +00:00
{
2017-03-06 09:21:10 +00:00
if ( privbuf [ id ] ) free ( privbuf [ id ] ) ;
buf [ id ] = privbuf [ id ] = malloc ( size ) ;
memcpy ( buf [ id ] , ptr , size ) ;
buf_size [ id ] = size ;
2012-12-27 07:59:19 +00:00
}
2017-03-06 09:21:10 +00:00
void SetBuffer ( int id , void * ptr , int32 size )
2012-12-27 07:59:19 +00:00
{
2017-04-19 03:09:04 +00:00
if ( privbuf [ id ] ) free ( privbuf [ id ] ) ;
privbuf [ id ] = nullptr ;
2017-03-06 09:21:10 +00:00
buf [ id ] = ptr ;
buf_size [ id ] = size ;
2012-12-27 07:59:19 +00:00
}
2017-03-06 09:21:10 +00:00
} comm ;
2012-12-21 07:23:55 +00:00
2017-03-06 09:21:10 +00:00
//coroutines
2013-11-22 09:33:56 +00:00
cothread_t co_control , co_emu , co_emu_suspended ;
2012-12-21 07:23:55 +00:00
2017-03-06 09:21:10 +00:00
//internal state
bool audio_en = false ;
static const int AUDIOBUFFER_SIZE = 44100 * 2 ;
uint16_t audiobuffer [ AUDIOBUFFER_SIZE ] ;
int audiobuffer_idx = 0 ;
Action CMD_cb ;
2012-12-21 07:23:55 +00:00
2017-04-19 03:09:04 +00:00
void BREAK ( eMessage msg )
{
2017-03-06 09:21:10 +00:00
comm . status = eStatus_BRK ;
comm . reason = msg ;
co_emu_suspended = co_active ( ) ;
co_switch ( co_control ) ;
comm . status = eStatus_CMD ;
2012-12-21 07:23:55 +00:00
}
2015-02-04 04:31:41 +00:00
void snes_video_refresh ( const uint32_t * data , unsigned width , unsigned height )
{
2017-03-06 09:21:10 +00:00
comm . width = width ;
comm . height = height ;
comm . ptr = ( void * ) data ;
2013-11-22 09:33:56 +00:00
2017-03-06 09:21:10 +00:00
BREAK ( eMessage_SIG_video_refresh ) ;
2015-02-04 04:31:41 +00:00
}
2017-03-06 09:21:10 +00:00
void do_SIG_audio_flush ( )
2015-02-04 04:31:41 +00:00
{
2017-03-06 09:21:10 +00:00
comm . ptr = audiobuffer ;
comm . size = audiobuffer_idx ;
BREAK ( eMessage_SIG_audio_flush ) ;
audiobuffer_idx = 0 ;
2015-02-04 04:31:41 +00:00
}
//this is the raw callback from the emulator internals when a new audio sample is available
void snes_audio_sample ( uint16_t left , uint16_t right )
{
if ( ! audio_en ) return ;
//if theres no room in the audio buffer, we need to send a flush signal
2017-03-06 09:21:10 +00:00
if ( audiobuffer_idx = = AUDIOBUFFER_SIZE )
{
do_SIG_audio_flush ( ) ;
}
2015-02-04 04:31:41 +00:00
audiobuffer [ audiobuffer_idx + + ] = left ;
2013-11-22 09:33:56 +00:00
audiobuffer [ audiobuffer_idx + + ] = right ;
2015-02-04 04:31:41 +00:00
}
void snes_input_poll ( void )
{
2017-03-06 09:21:10 +00:00
BREAK ( eMessage_SIG_input_poll ) ;
2015-02-04 04:31:41 +00:00
}
2017-03-06 09:21:10 +00:00
2015-02-04 04:31:41 +00:00
int16_t snes_input_state ( unsigned port , unsigned device , unsigned index , unsigned id )
{
2017-03-06 09:21:10 +00:00
comm . port = port ;
comm . device = device ;
comm . index = index ;
comm . id = id ;
BREAK ( eMessage_SIG_input_state ) ;
return comm . value ;
2015-02-04 04:31:41 +00:00
}
void snes_input_notify ( int index )
{
2017-03-06 09:21:10 +00:00
comm . index = index ;
BREAK ( eMessage_SIG_input_notify ) ;
2015-02-04 04:31:41 +00:00
}
2012-12-21 07:23:55 +00:00
void snes_trace ( const char * msg )
{
2017-03-06 09:21:10 +00:00
comm . str = ( char * ) msg ;
BREAK ( eMessage_SIG_trace_callback ) ;
2012-12-21 07:23:55 +00:00
}
const char * snes_path_request ( int slot , const char * hint )
{
2017-03-06 09:21:10 +00:00
comm . slot = slot ;
comm . str = ( char * ) hint ;
BREAK ( eMessage_SIG_path_request ) ;
2017-04-19 03:09:04 +00:00
return ( const char * ) comm . buf [ 0 ] ;
2012-12-21 07:23:55 +00:00
}
2012-12-25 20:36:04 +00:00
2012-12-21 07:23:55 +00:00
void snes_scanlineStart ( int line )
{
2017-03-06 09:21:10 +00:00
comm . scanline = line ;
BREAK ( eMessage_BRK_scanlineStart ) ;
2012-12-25 20:36:04 +00:00
}
2017-03-06 09:21:10 +00:00
void * snes_allocSharedMemory ( const char * memtype , size_t amt )
2015-02-04 04:31:41 +00:00
{
2017-03-06 09:21:10 +00:00
//its important that this happen before the message marshaling because allocation/free attempts can happen before the marshaling is setup (or at shutdown time, in case of errors?)
//if(!running) return NULL;
2015-02-04 04:31:41 +00:00
2017-03-06 09:21:10 +00:00
comm . str = ( char * ) memtype ;
comm . size = amt ;
2015-02-04 04:31:41 +00:00
2017-03-06 09:21:10 +00:00
BREAK ( eMessage_SIG_allocSharedMemory ) ;
2012-12-25 20:36:04 +00:00
2017-03-06 09:21:10 +00:00
return comm . ptr ;
}
2012-12-25 20:36:04 +00:00
2017-03-06 09:21:10 +00:00
void snes_freeSharedMemory ( void * ptr )
{
//its important that this happen before the message marshaling because allocation/free attempts can happen before the marshaling is setup (or at shutdown time, in case of errors?)
//if(!running) return;
2015-02-04 04:31:41 +00:00
2017-03-06 09:21:10 +00:00
if ( ! ptr ) return ;
comm . ptr = ptr ;
2015-10-24 08:06:47 +00:00
2017-03-06 09:21:10 +00:00
BREAK ( eMessage_SIG_freeSharedMemory ) ;
2013-11-22 09:33:56 +00:00
}
2017-03-06 09:21:10 +00:00
static void debug_op_exec ( uint24 addr )
2015-02-04 04:31:41 +00:00
{
2017-03-06 09:21:10 +00:00
comm . addr = addr ;
BREAK ( eMessage_BRK_hook_exec ) ;
}
2015-02-04 04:31:41 +00:00
2017-03-06 09:21:10 +00:00
static void debug_op_read ( uint24 addr )
{
comm . addr = addr ;
BREAK ( eMessage_BRK_hook_read ) ;
2015-02-04 04:31:41 +00:00
}
2017-03-06 09:21:10 +00:00
static void debug_op_write ( uint24 addr , uint8 value )
2015-02-04 04:31:41 +00:00
{
2017-03-06 09:21:10 +00:00
comm . addr = addr ;
comm . value = value ;
BREAK ( eMessage_BRK_hook_write ) ;
2015-02-04 04:31:41 +00:00
}
2017-03-06 09:21:10 +00:00
static void debug_op_nmi ( )
2013-11-22 09:33:56 +00:00
{
2017-03-06 09:21:10 +00:00
BREAK ( eMessage_BRK_hook_nmi ) ;
}
2013-11-22 09:33:56 +00:00
2017-03-06 09:21:10 +00:00
static void debug_op_irq ( )
{
BREAK ( eMessage_BRK_hook_irq ) ;
2012-12-21 07:23:55 +00:00
}
2017-03-06 09:21:10 +00:00
void pwrap_init ( )
2012-12-21 07:23:55 +00:00
{
2017-03-06 09:21:10 +00:00
//bsnes's interface initialization calls into this after initializing itself, so we can get a chance to mod it for pwrap functionalities
2012-12-21 07:23:55 +00:00
snes_set_video_refresh ( snes_video_refresh ) ;
snes_set_audio_sample ( snes_audio_sample ) ;
snes_set_input_poll ( snes_input_poll ) ;
snes_set_input_state ( snes_input_state ) ;
snes_set_input_notify ( snes_input_notify ) ;
snes_set_path_request ( snes_path_request ) ;
2012-12-25 20:36:04 +00:00
snes_set_allocSharedMemory ( snes_allocSharedMemory ) ;
snes_set_freeSharedMemory ( snes_freeSharedMemory ) ;
2012-12-21 07:23:55 +00:00
}
2017-03-06 09:21:10 +00:00
static void Analyze ( )
2013-11-03 22:44:49 +00:00
{
2017-03-06 09:21:10 +00:00
//gather some "static" type information, so we dont have to poll annoyingly for it later
comm . mapper = snes_get_mapper ( ) ;
comm . region = snes_get_region ( ) ;
2013-11-03 22:44:49 +00:00
}
2017-03-06 09:21:10 +00:00
void CMD_LoadCartridgeNormal ( )
2013-11-03 22:44:49 +00:00
{
2017-04-19 03:09:04 +00:00
const char * xml = ( const char * ) comm . buf [ 0 ] ;
if ( ! xml [ 0 ] ) xml = nullptr ;
bool ret = snes_load_cartridge_normal ( xml , ( const uint8_t * ) comm . buf [ 1 ] , comm . buf_size [ 1 ] ) ;
2017-03-06 09:21:10 +00:00
comm . value = ret ? 1 : 0 ;
2013-11-03 22:44:49 +00:00
2017-03-06 09:21:10 +00:00
if ( ret )
Analyze ( ) ;
2013-11-03 22:44:49 +00:00
}
2017-03-06 09:21:10 +00:00
void CMD_LoadCartridgeSGB ( )
2013-11-03 22:44:49 +00:00
{
2017-04-19 03:09:04 +00:00
bool ret = snes_load_cartridge_super_game_boy ( ( const char * ) comm . buf [ 0 ] , ( const u8 * ) comm . buf [ 1 ] , comm . buf_size [ 1 ] , nullptr , ( const u8 * ) comm . buf [ 2 ] , comm . buf_size [ 2 ] ) ;
2017-03-06 09:21:10 +00:00
comm . value = ret ? 1 : 0 ;
if ( ret )
Analyze ( ) ;
2013-11-03 22:44:49 +00:00
}
2017-04-09 21:45:05 +00:00
void CMD_init ( )
{
snes_init ( ) ;
SNES : : input . connect ( SNES : : Controller : : Port1 , comm . inports [ 0 ] ) ;
SNES : : input . connect ( SNES : : Controller : : Port2 , comm . inports [ 1 ] ) ;
}
2017-04-19 03:09:04 +00:00
void CMD_Serialize ( )
2013-11-03 22:44:49 +00:00
{
2017-03-06 09:21:10 +00:00
int size = comm . buf_size [ 0 ] ;
char * buf = ( char * ) comm . buf [ 0 ] ;
bool ret = snes_serialize ( ( uint8_t * ) buf , size ) ;
comm . value = ret ? 1 : 0 ;
2013-11-03 22:44:49 +00:00
}
2017-04-19 03:09:04 +00:00
void CMD_Unserialize ( )
2012-12-21 07:23:55 +00:00
{
2017-03-06 09:21:10 +00:00
int size = comm . buf_size [ 0 ] ;
char * buf = ( char * ) comm . buf [ 0 ] ;
bool ret = snes_unserialize ( ( uint8_t * ) buf , size ) ;
comm . value = ret ? 1 : 0 ;
2013-11-22 09:33:56 +00:00
}
2017-04-19 03:09:04 +00:00
static void CMD_Run ( )
2013-11-22 09:33:56 +00:00
{
2017-03-06 09:21:10 +00:00
do_SIG_audio_flush ( ) ;
2012-12-25 20:36:04 +00:00
2017-03-06 09:21:10 +00:00
//we could avoid this if we saved the current thread before jumping back to co_control, instead of always jumping back to co_emu
//in effect, we're scrambling the scheduler
//EDIT - well, we changed that, but.. we still want this probably, for debugging and stuff
for ( ; ; )
{
SNES : : scheduler . sync = SNES : : Scheduler : : SynchronizeMode : : None ;
SNES : : scheduler . clearExitReason ( ) ;
SNES : : scheduler . enter ( ) ;
if ( SNES : : scheduler . exit_reason ( ) = = SNES : : Scheduler : : ExitReason : : FrameEvent )
2013-11-22 09:33:56 +00:00
{
2017-03-06 09:21:10 +00:00
SNES : : video . update ( ) ;
2012-12-21 07:23:55 +00:00
break ;
2013-11-22 09:33:56 +00:00
}
2017-03-06 09:21:10 +00:00
//not used yet
if ( SNES : : scheduler . exit_reason ( ) = = SNES : : Scheduler : : ExitReason : : DebuggerEvent )
2012-12-21 07:23:55 +00:00
break ;
2013-11-22 09:33:56 +00:00
}
2017-03-06 09:21:10 +00:00
do_SIG_audio_flush ( ) ;
}
void QUERY_get_memory_size ( ) {
comm . value = snes_get_memory_size ( comm . value ) ;
}
void QUERY_peek ( ) {
if ( comm . id = = SNES_MEMORY_SYSBUS )
comm . value = bus_read ( comm . addr ) ;
else comm . value = snes_get_memory_data ( comm . id ) [ comm . addr ] ;
}
void QUERY_poke ( ) {
if ( comm . id = = SNES_MEMORY_SYSBUS )
bus_write ( comm . addr , comm . value ) ;
else snes_get_memory_data ( comm . id ) [ comm . addr ] = comm . value ;
}
void QUERY_set_color_lut ( ) {
snes_set_color_lut ( ( uint32_t * ) comm . ptr ) ;
}
void QUERY_GetMemoryIdName ( ) {
comm . str = ( char * ) snes_get_memory_id_name ( comm . id ) ;
}
void QUERY_state_hook_exec ( ) {
SNES : : cpu . debugger . op_exec = comm . value ? debug_op_exec : hook < void ( uint24 ) > ( ) ;
}
void QUERY_state_hook_read ( ) {
SNES : : cpu . debugger . op_read = comm . value ? debug_op_read : hook < void ( uint24 ) > ( ) ;
}
void QUERY_state_hook_write ( ) {
SNES : : cpu . debugger . op_write = comm . value ? debug_op_write : hook < void ( uint24 , uint8 ) > ( ) ;
}
void QUERY_state_hook_nmi ( ) {
SNES : : cpu . debugger . op_nmi = comm . value ? debug_op_nmi : hook < void ( ) > ( ) ;
}
void QUERY_state_hook_irq ( ) {
SNES : : cpu . debugger . op_irq = comm . value ? debug_op_irq : hook < void ( ) > ( ) ;
}
void QUERY_state_enable_trace ( ) {
if ( comm . value )
snes_set_trace_callback ( snes_trace ) ;
else snes_set_trace_callback ( nullptr ) ;
}
void QUERY_state_enable_scanline ( ) {
if ( comm . value )
snes_set_scanlineStart ( snes_scanlineStart ) ;
else snes_set_scanlineStart ( nullptr ) ;
}
void QUERY_state_enable_audio ( ) {
audio_en = ! ! comm . value ;
}
void QUERY_state_set_layer_enable ( ) {
snes_set_layer_enable ( 0 , 0 , ! ! comm . layerEnables . BG1_Prio0 ) ;
snes_set_layer_enable ( 0 , 1 , ! ! comm . layerEnables . BG1_Prio1 ) ;
snes_set_layer_enable ( 1 , 0 , ! ! comm . layerEnables . BG2_Prio0 ) ;
snes_set_layer_enable ( 1 , 1 , ! ! comm . layerEnables . BG2_Prio1 ) ;
snes_set_layer_enable ( 2 , 0 , ! ! comm . layerEnables . BG3_Prio0 ) ;
snes_set_layer_enable ( 2 , 1 , ! ! comm . layerEnables . BG3_Prio1 ) ;
snes_set_layer_enable ( 3 , 0 , ! ! comm . layerEnables . BG4_Prio0 ) ;
snes_set_layer_enable ( 3 , 1 , ! ! comm . layerEnables . BG4_Prio1 ) ;
snes_set_layer_enable ( 4 , 0 , ! ! comm . layerEnables . Obj_Prio0 ) ;
snes_set_layer_enable ( 4 , 1 , ! ! comm . layerEnables . Obj_Prio1 ) ;
snes_set_layer_enable ( 4 , 2 , ! ! comm . layerEnables . Obj_Prio2 ) ;
snes_set_layer_enable ( 4 , 3 , ! ! comm . layerEnables . Obj_Prio3 ) ;
}
void QUERY_set_backdropColor ( ) {
snes_set_backdropColor ( ( s32 ) comm . value ) ;
}
void QUERY_peek_logical_register ( ) {
comm . value = snes_peek_logical_register ( comm . id ) ;
}
void QUERY_peek_cpu_regs ( ) {
comm . cpuregs . pc = ( u32 ) SNES : : cpu . regs . pc ;
comm . cpuregs . a = SNES : : cpu . regs . a ;
comm . cpuregs . x = SNES : : cpu . regs . x ;
comm . cpuregs . y = SNES : : cpu . regs . y ;
comm . cpuregs . z = SNES : : cpu . regs . z ;
comm . cpuregs . s = SNES : : cpu . regs . s ;
comm . cpuregs . d = SNES : : cpu . regs . d ;
comm . cpuregs . aa = ( u32 ) SNES : : cpu . aa ;
comm . cpuregs . rd = ( u32 ) SNES : : cpu . rd ;
comm . cpuregs . sp = SNES : : cpu . sp ;
comm . cpuregs . dp = SNES : : cpu . dp ;
comm . cpuregs . db = SNES : : cpu . regs . db ;
comm . cpuregs . mdr = SNES : : cpu . regs . mdr ;
comm . cpuregs . vector = SNES : : cpu . regs . vector ;
comm . cpuregs . p = SNES : : cpu . regs . p ;
comm . cpuregs . nothing = 0 ;
}
void QUERY_peek_set_cdl ( ) {
for ( int i = 0 ; i < eCDLog_AddrType_NUM ; i + + )
2013-12-29 02:20:13 +00:00
{
2017-03-06 09:21:10 +00:00
cdlInfo . blocks [ i ] = ( uint8 * ) comm . cdl_ptr [ i ] ;
cdlInfo . blockSizes [ i ] = comm . cdl_size [ i ] ;
2013-12-29 02:20:13 +00:00
}
2013-11-22 09:33:56 +00:00
}
2017-03-06 09:21:10 +00:00
void QUERY_serialize_size ( ) {
comm . size = snes_serialize_size ( ) ;
}
const Action kHandlers_CMD [ ] = {
2017-04-09 21:45:05 +00:00
CMD_init ,
2017-03-06 09:21:10 +00:00
snes_power ,
snes_reset ,
2017-04-19 03:09:04 +00:00
CMD_Run ,
CMD_Serialize ,
CMD_Unserialize ,
2017-03-06 09:21:10 +00:00
CMD_LoadCartridgeNormal ,
CMD_LoadCartridgeSGB ,
snes_term ,
snes_unload_cartridge ,
} ;
2013-11-22 09:33:56 +00:00
2017-03-08 08:58:50 +00:00
const Action kHandlers_QUERY [ ] = {
2017-03-06 09:21:10 +00:00
QUERY_get_memory_size , //eMessage_QUERY_get_memory_size TODO - grab during bootup (for all possible memdomains)
QUERY_peek ,
QUERY_poke ,
QUERY_serialize_size , //eMessage_QUERY_serialize_size TODO - grab during bootup/reset (for all possible memdomains)
QUERY_set_color_lut ,
QUERY_GetMemoryIdName , //snes_get_memory_id_name TODO - grab during bootup (for all possible memdomains)
QUERY_state_hook_exec , //eMessage_QUERY_state_hook_exec
QUERY_state_hook_read , //eMessage_QUERY_state_hook_read
QUERY_state_hook_write , //eMessage_QUERY_state_hook_write
QUERY_state_hook_nmi , //eMessage_QUERY_state_hook_nmi
QUERY_state_hook_irq , //eMessage_QUERY_state_hook_irq
QUERY_state_enable_trace , //eMessage_QUERY_enable_trace TODO - consolidate enable flags
QUERY_state_enable_scanline , //eMessage_QUERY_enable_scanline TODO - consolidate enable flags
QUERY_state_enable_audio , //eMessage_QUERY_enable_audio TODO - consolidate enable flags
QUERY_state_set_layer_enable , //eMessage_QUERY_set_layer_enable
QUERY_set_backdropColor , //eMessage_QUERY_set_backdropColor
QUERY_peek_logical_register , //eMessage_QUERY_peek_logical_register
QUERY_peek_cpu_regs , //eMessage_QUERY_peek_cpu_regs
QUERY_peek_set_cdl , //eMessage_QUERY_set_cdl
} ;
2013-11-22 09:33:56 +00:00
2017-03-06 09:21:10 +00:00
//all this does is run commands on the emulation thread infinitely forever
//(I should probably make a mechanism for bailing...)
void new_emuthread ( )
2013-11-22 09:33:56 +00:00
{
2017-03-06 09:21:10 +00:00
for ( ; ; )
2013-11-22 09:33:56 +00:00
{
2017-03-06 09:21:10 +00:00
//process the current CMD
CMD_cb ( ) ;
2013-11-22 09:33:56 +00:00
2017-03-06 09:21:10 +00:00
//when that returned, we're definitely done with the CMD--so we're now IDLE
comm . status = eStatus_Idle ;
2013-11-22 09:33:56 +00:00
2017-03-06 09:21:10 +00:00
co_switch ( co_control ) ;
2012-12-21 07:23:55 +00:00
}
2012-12-25 20:36:04 +00:00
}
2017-03-06 09:21:10 +00:00
//------------------------------------------------
//DLL INTERFACE
2012-12-25 20:36:04 +00:00
2017-03-06 09:21:10 +00:00
BOOL WINAPI DllMain ( _In_ HINSTANCE hinstDLL , _In_ DWORD fdwReason , _In_ LPVOID lpvReserved )
2013-11-22 09:33:56 +00:00
{
2017-03-06 09:21:10 +00:00
return TRUE ;
2013-11-25 20:51:26 +00:00
}
2017-03-06 09:21:10 +00:00
extern " C " dllexport void * __cdecl DllInit ( )
2013-11-25 20:51:26 +00:00
{
2017-04-19 03:09:04 +00:00
memset ( & comm , 0 , sizeof ( comm ) ) ;
2017-03-06 09:21:10 +00:00
//make a coroutine thread to run the emulation in. we'll switch back to this cothread when communicating with the frontend
co_control = co_active ( ) ;
co_emu = co_create ( 65536 * sizeof ( void * ) , new_emuthread ) ;
2013-11-25 20:51:26 +00:00
2017-03-06 09:21:10 +00:00
return & comm ;
2013-11-25 20:51:26 +00:00
}
2017-03-06 09:21:10 +00:00
extern " C " dllexport void __cdecl Message ( eMessage msg )
2013-11-25 20:51:26 +00:00
{
2017-03-06 12:00:17 +00:00
if ( msg = = eMessage_Resume )
2017-03-06 09:21:10 +00:00
{
cothread_t temp = co_emu_suspended ;
co_emu_suspended = NULL ;
co_switch ( temp ) ;
}
2013-11-22 09:33:56 +00:00
2017-03-06 09:21:10 +00:00
if ( msg > = eMessage_CMD_FIRST & & msg < = eMessage_CMD_LAST )
2013-11-22 09:33:56 +00:00
{
2017-03-06 09:21:10 +00:00
//CMD is only valid if status is idle
if ( comm . status ! = eStatus_Idle )
2013-11-22 09:33:56 +00:00
{
2017-03-06 09:21:10 +00:00
printf ( " ERROR: cmd during non-idle \n " ) ;
return ;
}
2013-11-25 20:51:26 +00:00
2017-03-06 09:21:10 +00:00
comm . status = eStatus_CMD ;
comm . cmd = msg ;
2013-11-25 20:51:26 +00:00
2017-03-06 09:21:10 +00:00
CMD_cb = kHandlers_CMD [ msg - eMessage_CMD_FIRST - 1 ] ;
co_switch ( co_emu ) ;
2013-11-25 20:51:26 +00:00
2017-03-06 09:21:10 +00:00
//we could be in ANY STATE when we return from here
2013-11-22 09:33:56 +00:00
}
2017-03-06 09:21:10 +00:00
//QUERY can run any time
//but... some of them might not be safe for re-entrancy.
//later, we should have metadata for messages that indicates that
if ( msg > = eMessage_QUERY_FIRST & & msg < = eMessage_QUERY_LAST )
{
Action cb = kHandlers_QUERY [ msg - eMessage_QUERY_FIRST - 1 ] ;
if ( cb ) cb ( ) ;
}
2015-10-24 08:06:47 +00:00
}
2012-12-25 20:36:04 +00:00
2017-03-06 09:21:10 +00:00
//receives the given buffer and COPIES it. use this for returning values from SIGs
extern " C " dllexport void __cdecl CopyBuffer ( int id , void * ptr , int32 size )
{
comm . CopyBuffer ( id , ptr , size ) ;
2012-12-25 20:36:04 +00:00
}
2017-03-06 09:21:10 +00:00
//receives the given buffer and STASHES IT. use this (carefully) for sending params for CMDs
extern " C " dllexport void __cdecl SetBuffer ( int id , void * ptr , int32 size )
2012-12-25 20:36:04 +00:00
{
2017-03-06 09:21:10 +00:00
comm . SetBuffer ( id , ptr , size ) ;
2012-12-21 07:23:55 +00:00
}