mupen64plus-rsp-cxd4/module.c
2020-05-27 21:13:21 -07:00

762 lines
24 KiB
C

/******************************************************************************\
* Project: Module Subsystem Interface to SP Interpreter Core *
* Authors: Iconoclast *
* Release: 2018.12.18 *
* License: CC0 Public Domain Dedication *
* *
* To the extent possible under law, the author(s) have dedicated all copyright *
* and related and neighboring rights to this software to the public domain *
* worldwide. This software is distributed without any warranty. *
* *
* You should have received a copy of the CC0 Public Domain Dedication along *
* with this software. *
* If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. *
\******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* time() (for helping srand()) */
#include <time.h>
#ifdef WIN32
#include <windows.h>
#endif
#ifdef __APPLE__
#import <CoreFoundation/CoreFoundation.h>
#endif
#include "module.h"
#include "su.h"
#include "m64p_common.h"
#include <signal.h>
#include <setjmp.h>
#if defined(__GNUC__)
#define ATTR_FMT(fmtpos, attrpos) __attribute__ ((format (printf, fmtpos, attrpos)))
#else
#define ATTR_FMT(fmtpos, attrpos)
#endif
static jmp_buf CPU_state;
static void seg_av_handler(int signal_code)
{
longjmp(CPU_state, signal_code);
}
static void ISA_op_illegal(int signal_code)
{
message("Plugin built for SIMD extensions this CPU does not support!");
raise(signal_code); /* e.g., rsp.dll built with -mssse3; the CPU is SSE2. */
}
RSP_INFO RSP_INFO_NAME;
#define RSP_CXD4_VERSION 0x0101
#if defined(M64P_PLUGIN_API)
#include <m64p_frontend.h>
#include <stdarg.h>
#define RSP_PLUGIN_API_VERSION 0x020000
#define CONFIG_API_VERSION 0x020100
#define CONFIG_PARAM_VERSION 1.00
static void (*l_DebugCallback)(void *, int, const char *) = NULL;
static void *l_DebugCallContext = NULL;
static int l_PluginInit = 0;
static m64p_handle l_ConfigRsp;
#define VERSION_PRINTF_SPLIT(x) (((x) >> 16) & 0xffff), (((x) >> 8) & 0xff), ((x) & 0xff)
ptr_ConfigOpenSection ConfigOpenSection = NULL;
ptr_ConfigDeleteSection ConfigDeleteSection = NULL;
ptr_ConfigSetParameter ConfigSetParameter = NULL;
ptr_ConfigGetParameter ConfigGetParameter = NULL;
ptr_ConfigSetDefaultFloat ConfigSetDefaultFloat;
ptr_ConfigSetDefaultBool ConfigSetDefaultBool = NULL;
ptr_ConfigGetParamBool ConfigGetParamBool = NULL;
ptr_CoreDoCommand CoreDoCommand = NULL;
NOINLINE void update_conf(const char* source)
{
memset(conf, 0, 32);
m64p_rom_header ROM_HEADER;
CoreDoCommand(M64CMD_ROM_GET_HEADER, sizeof(ROM_HEADER), &ROM_HEADER);
CFG_HLE_GFX = ConfigGetParamBool(l_ConfigRsp, "DisplayListToGraphicsPlugin");
CFG_HLE_AUD = ConfigGetParamBool(l_ConfigRsp, "AudioListToAudioPlugin");
CFG_WAIT_FOR_CPU_HOST = ConfigGetParamBool(l_ConfigRsp, "WaitForCPUHost");
CFG_MEND_SEMAPHORE_LOCK = ConfigGetParamBool(l_ConfigRsp, "SupportCPUSemaphoreLock");
}
static void DebugMessage(int level, const char *message, ...) ATTR_FMT(2, 3);
void DebugMessage(int level, const char *message, ...)
{
char msgbuf[1024];
va_list args;
if (l_DebugCallback == NULL)
return;
va_start(args, message);
vsprintf(msgbuf, message, args);
(*l_DebugCallback)(l_DebugCallContext, level, msgbuf);
va_end(args);
}
EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
void (*DebugCallback)(void *, int, const char *))
{
ptr_CoreGetAPIVersions CoreAPIVersionFunc;
int ConfigAPIVersion, DebugAPIVersion, VidextAPIVersion;
float fConfigParamsVersion = 0.0f;
if (l_PluginInit)
return M64ERR_ALREADY_INIT;
/* first thing is to set the callback function for debug info */
l_DebugCallback = DebugCallback;
l_DebugCallContext = Context;
/* attach and call the CoreGetAPIVersions function, check Config API version for compatibility */
CoreAPIVersionFunc = (ptr_CoreGetAPIVersions) osal_dynlib_getproc(CoreLibHandle, "CoreGetAPIVersions");
if (CoreAPIVersionFunc == NULL)
{
DebugMessage(M64MSG_ERROR, "Core emulator broken; no CoreAPIVersionFunc() function found.");
return M64ERR_INCOMPATIBLE;
}
(*CoreAPIVersionFunc)(&ConfigAPIVersion, &DebugAPIVersion, &VidextAPIVersion, NULL);
if ((ConfigAPIVersion & 0xffff0000) != (CONFIG_API_VERSION & 0xffff0000))
{
DebugMessage(M64MSG_ERROR, "Emulator core Config API (v%i.%i.%i) incompatible with plugin (v%i.%i.%i)",
VERSION_PRINTF_SPLIT(ConfigAPIVersion), VERSION_PRINTF_SPLIT(CONFIG_API_VERSION));
return M64ERR_INCOMPATIBLE;
}
/* Get the core config function pointers from the library handle */
ConfigOpenSection = (ptr_ConfigOpenSection) osal_dynlib_getproc(CoreLibHandle, "ConfigOpenSection");
ConfigDeleteSection = (ptr_ConfigDeleteSection) osal_dynlib_getproc(CoreLibHandle, "ConfigDeleteSection");
ConfigSetParameter = (ptr_ConfigSetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigSetParameter");
ConfigGetParameter = (ptr_ConfigGetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParameter");
ConfigSetDefaultFloat = (ptr_ConfigSetDefaultFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultFloat");
ConfigSetDefaultBool = (ptr_ConfigSetDefaultBool) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultBool");
ConfigGetParamBool = (ptr_ConfigGetParamBool) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamBool");
CoreDoCommand = (ptr_CoreDoCommand) osal_dynlib_getproc(CoreLibHandle, "CoreDoCommand");
if (!ConfigOpenSection || !ConfigDeleteSection || !ConfigSetParameter || !ConfigGetParameter ||
!ConfigSetDefaultBool || !ConfigGetParamBool || !ConfigSetDefaultFloat)
return M64ERR_INCOMPATIBLE;
/* get a configuration section handle */
if (ConfigOpenSection("rsp-cxd4", &l_ConfigRsp) != M64ERR_SUCCESS)
{
DebugMessage(M64MSG_ERROR, "Couldn't open config section 'rsp-cxd4'");
return M64ERR_INPUT_NOT_FOUND;
}
/* check the section version number */
if (ConfigGetParameter(l_ConfigRsp, "Version", M64TYPE_FLOAT, &fConfigParamsVersion, sizeof(float)) != M64ERR_SUCCESS)
{
DebugMessage(M64MSG_WARNING, "No version number in 'rsp-cxd4' config section. Setting defaults.");
ConfigDeleteSection("rsp-cxd4");
ConfigOpenSection("rsp-cxd4", &l_ConfigRsp);
}
else if (((int) fConfigParamsVersion) != ((int) CONFIG_PARAM_VERSION))
{
DebugMessage(M64MSG_WARNING, "Incompatible version %.2f in 'rsp-cxd4' config section: current is %.2f. Setting defaults.", fConfigParamsVersion, (float) CONFIG_PARAM_VERSION);
ConfigDeleteSection("rsp-cxd4");
ConfigOpenSection("rsp-cxd4", &l_ConfigRsp);
}
else if ((CONFIG_PARAM_VERSION - fConfigParamsVersion) >= 0.0001f)
{
/* handle upgrades */
float fVersion = CONFIG_PARAM_VERSION;
ConfigSetParameter(l_ConfigRsp, "Version", M64TYPE_FLOAT, &fVersion);
DebugMessage(M64MSG_INFO, "Updating parameter set version in 'rsp-cxd4' config section to %.2f", fVersion);
}
#ifndef HLEVIDEO
int hlevideo = 0;
#else
int hlevideo = 1;
#endif
/* set the default values for this plugin */
ConfigSetDefaultFloat(l_ConfigRsp, "Version", CONFIG_PARAM_VERSION, "Mupen64Plus cxd4 RSP Plugin config parameter version number");
ConfigSetDefaultBool(l_ConfigRsp, "DisplayListToGraphicsPlugin", hlevideo, "Send display lists to the graphics plugin");
ConfigSetDefaultBool(l_ConfigRsp, "AudioListToAudioPlugin", 0, "Send audio lists to the audio plugin");
ConfigSetDefaultBool(l_ConfigRsp, "WaitForCPUHost", 0, "Force CPU-RSP signals synchronization");
ConfigSetDefaultBool(l_ConfigRsp, "SupportCPUSemaphoreLock", 0, "Support CPU-RSP semaphore lock");
l_PluginInit = 1;
return M64ERR_SUCCESS;
}
EXPORT m64p_error CALL PluginShutdown(void)
{
if (!l_PluginInit)
return M64ERR_NOT_INIT;
l_PluginInit = 0;
return M64ERR_SUCCESS;
}
EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)
{
/* set version info */
if (PluginType != NULL)
*PluginType = M64PLUGIN_RSP;
if (PluginVersion != NULL)
*PluginVersion = RSP_CXD4_VERSION;
if (APIVersion != NULL)
*APIVersion = RSP_PLUGIN_API_VERSION;
if (PluginNamePtr != NULL)
*PluginNamePtr = "Static Interpreter";
if (Capabilities != NULL)
{
*Capabilities = 0;
}
return M64ERR_SUCCESS;
}
EXPORT int CALL RomOpen(void)
{
if (!l_PluginInit)
return 0;
update_conf(CFG_FILE);
return 1;
}
#else
static const char DLL_about[] =
"RSP Interpreter by Iconoclast\n"\
"Thanks for test RDP: Jabo, ziggy, angrylion\n"\
"RSP driver examples: bpoint, zilmar, Ville Linde";
#endif
static void init_regs(void)
{
register size_t i, j;
for (i = 0; i < 16; i++)
if (CR[i] == NULL)
raise(SIGTERM); /* Don't proceed if plugin hasn't initialized. */
srand(time(NULL));
for (i = 0; i < N; i++) {
VACC_H[i] = ((u64)0xFFFF00000000 >> 32) & 0x0000;
VACC_M[i] = ((u64)0x0000FFFF0000 >> 16) & 0x0000;
VACC_L[i] = ((u64)0x00000000FFFF >> 0) & 0x0000;
}
#if 0
DPH = SP_DIV_PRECISION_SINGLE; /* static global maintained in vu/divide.o */
#endif
/*
* Based on krom's experiences at testing the RSP hardware with homebrew, it
* has become apparent that the bits in $vco, $vcc and $vce do NOT become
* random upon powering on the console. However, this does not say that
* previous values of these flags aren't momentarily preserved before any
* bit rot loses them overtime. Since it's not clear whether these flags are
* explicitly initialized to 0 at power-on or if they temporarily retain old
* decaying bits, we'll just make them 0 to hush krom's RSP test FAIL yells.
*/
for (i = 0; i < N; i++) {
cf_ne[i] = (rand() & (1 << 15)) ? 0*TRUE : FALSE;
cf_co[i] = (rand() & (1 << 12)) ? 0*TRUE : FALSE;
cf_clip[i] = (rand() & (1 << 9)) ? 0*TRUE : FALSE;
cf_comp[i] = (rand() & (1 << 6)) ? 0*TRUE : FALSE;
cf_vce[i] = (rand() & (1 << 0)) ? 0*TRUE : FALSE;
}
for (i = 0; i < 32; i++)
SR[i] = (u32)rand();
SR[0] = 0x00000000;
for (i = 0; i < 32; i++)
for (j = 0; j < N; j++)
VR[i][j] = (u16)((u32)rand() & 0xFFFFu);
*(RSP_INFO_NAME.SP_PC_REG) = 0x04001000;
*CR[0x0] = 0x00000000; /* DMA transfer address for SP memory cache */
*CR[0x1] = 0x00000000; /* DMA transfer address for host DRAM */
*CR[0x2] = 0x00000000; /* DMA read transfer period */
*CR[0x3] = 0x00000000; /* DMA write transfer period */
*CR[0x4] = 0x00000001; /* SP status flags */
*CR[0x5] = 0x00000000; /* read-only DMA full indicator */
*CR[0x6] = 0x00000000; /* read-only DMA busy indicator */
*CR[0x7] = 0x00000000; /* CPU-RSP synchronicity semaphore */
*CR[0x8] = (u32)rand(); /* start address of RDP command buffer */
*CR[0x9] = (u32)rand(); /* end address of RDP command buffer */
*CR[0xA] = 0x00000000; /* read-only current RDP command buffer address */
*CR[0xB] &= 0x100; /* DP status flags: DMA_BUSY flag is undefined. */
*CR[0xC] = 0x0000FFFF; /* RDP clock cycle counter */
/*
* Technically these are random at startup on the hardware, but most emulators
* fail to ever clear these locks if randomly set, causing constant warnings.
*/
#if 0
*CR[0xD] = (u32)rand(); /* read-only RDP contiguous busy buffer cycles */
*CR[0xE] = (u32)rand(); /* read-only RDP contiguous busy pipe cycles */
#endif
*CR[0xF] = (u32)rand(); /* read-only RDP contiguous TMEM import cycles */
*CR[0xB] |= 0x000000A8; /* GCLK, PIPE_BUSY and CMD_BUF_READY always set */
#if 0
*CR[0xB] |= (irand() & 1) << 8; /* DP DMA busy status bit is undefined. */
#endif
*CR[0xC] += *CR[0xD] + *CR[0xE] + *CR[0xF]; /* random total clock cycles */
}
#if !defined(M64P_PLUGIN_API)
EXPORT void CALL CloseDLL(void)
{
DRAM = NULL; /* so DllTest benchmark doesn't think ROM is still open */
return;
}
EXPORT void CALL DllAbout(p_void hParent)
{
message(DLL_about);
hParent = NULL;
if (hParent == NULL)
return; /* -Wunused-but-set-parameter */
return;
}
EXPORT void CALL DllConfig(p_void hParent)
{
system("sp_cfgui");
update_conf(CFG_FILE);
if (DMEM == IMEM || GET_RCP_REG(SP_PC_REG) % 4096 == 0x00000000)
return;
export_SP_memory();
hParent = NULL;
if (hParent == NULL)
return; /* -Wunused-but-set-parameter */
return;
}
#endif
EXPORT unsigned int CALL DoRspCycles(unsigned int cycles)
{
static char task_debug[] = "unknown task type: 0x????????";
char* task_debug_type;
OSTask_type task_type;
register unsigned int i;
if (GET_RCP_REG(SP_STATUS_REG) & 0x00000003) {
message("SP_STATUS_HALT");
return 0x00000000;
}
task_debug_type = &task_debug[strlen("unknown task type: 0x")];
#ifdef USE_CLIENT_ENDIAN
memcpy(&task_type, DMEM + 0xFC0, 4);
#else
task_type = 0x00000000
| (u32)(DMEM[0xFC0 ^ 0] & 0xFFu) << 24
| (u32)(DMEM[0xFC1 ^ 0] & 0xFFu) << 16
| (u32)(DMEM[0xFC2 ^ 0] & 0xFFu) << 8
| (u32)(DMEM[0xFC3 ^ 0] & 0xFFu) << 0
;
#endif
switch (task_type) {
case M_GFXTASK:
if (CFG_HLE_GFX == 0)
break;
if (*(pi32)(DMEM + 0xFF0) == 0x00000000)
break; /* Resident Evil 2, null task pointers */
GET_RCP_REG(SP_STATUS_REG) |=
SP_STATUS_SIG2 | SP_STATUS_BROKE | SP_STATUS_HALT
;
#if defined(M64P_PLUGIN_API)
if (GET_RSP_INFO(ProcessDlistList) == NULL)
{ /* branch */ }
else
GET_RSP_INFO(ProcessDlistList)();
#else
if (GET_RSP_INFO(ProcessDList) == NULL)
{ /* branch */ }
else
GET_RSP_INFO(ProcessDList)();
#endif
if ((GET_RCP_REG(SP_STATUS_REG) & SP_STATUS_INTR_BREAK) && (GET_RCP_REG(SP_STATUS_REG) & (SP_STATUS_SIG2 | SP_STATUS_BROKE | SP_STATUS_HALT))) {
GET_RCP_REG(MI_INTR_REG) |= 0x00000001;
GET_RSP_INFO(CheckInterrupts)();
}
GET_RCP_REG(DPC_STATUS_REG) &= ~0x00000002ul; /* DPC_STATUS_FREEZE */
return 0;
case M_AUDTASK:
if (CFG_HLE_AUD == 0)
break;
#if defined(M64P_PLUGIN_API)
if (GET_RSP_INFO(ProcessAlistList) == NULL)
{ /* branch */ }
else
GET_RSP_INFO(ProcessAlistList)();
#else
if (GET_RSP_INFO(ProcessAList) == NULL)
{ /* branch */ }
else
GET_RSP_INFO(ProcessAList)();
#endif
GET_RCP_REG(SP_STATUS_REG) |=
SP_STATUS_SIG2 | SP_STATUS_BROKE | SP_STATUS_HALT
;
if (GET_RCP_REG(SP_STATUS_REG) & SP_STATUS_INTR_BREAK) {
GET_RCP_REG(MI_INTR_REG) |= 0x00000001;
GET_RSP_INFO(CheckInterrupts)();
}
return 0;
case M_VIDTASK:
message("M_VIDTASK");
break;
case M_NJPEGTASK:
break; /* Zelda, Pokemon, others */
case M_NULTASK:
message("M_NULTASK");
break;
case M_HVQTASK:
message("M_HVQTASK");
break;
case M_HVQMTASK:
if (GET_RSP_INFO(ShowCFB) == NULL) /* Gfx #1.2 or older specs */
break;
GET_RSP_INFO(ShowCFB)(); /* forced FB refresh in case gfx plugin skip */
break;
default:
if (task_type == 0x00000000)
break; /* generic or invoked without CPU filling in OSTask struct */
if (task_type == 0x8BC43B5D)
break; /* CIC boot code sent to the RSP */
sprintf(task_debug_type, "%08lX", (unsigned long)task_type);
message(task_debug);
}
#ifdef WAIT_FOR_CPU_HOST
for (i = 0; i < NUMBER_OF_SCALAR_REGISTERS; i++)
MFC0_count[i] = 0;
#endif
run_task();
/*
* An optional EMMS when compiling with Intel SIMD or MMX support.
*
* Whether or not MMX has been executed in this emulator, here is a good time
* to finally empty the MM state, at the end of a long interpreter loop.
*/
#ifdef ARCH_MIN_SSE2
//_mm_empty();
#endif
if (*CR[0x4] & SP_STATUS_BROKE) /* normal exit, from executing BREAK */
return (cycles);
else if (GET_RCP_REG(MI_INTR_REG) & 1) /* interrupt set by MTC0 to break */
GET_RSP_INFO(CheckInterrupts)();
else if (*CR[0x7] != 0x00000000) /* semaphore lock fixes */
{}
#ifdef WAIT_FOR_CPU_HOST
else
MF_SP_STATUS_TIMEOUT = 16; /* From now on, wait 16 times, not 32767. */
#else
else { /* ??? unknown, possibly external intervention from CPU memory map */
message("SP_SET_HALT");
return (cycles);
}
#endif
*CR[0x4] &= ~SP_STATUS_HALT; /* CPU restarts with the correct SIGs. */
return (cycles);
}
EXPORT void CALL GetDllInfo(PLUGIN_INFO *PluginInfo)
{
PluginInfo -> Version = (u16) PLUGIN_API_VERSION;
PluginInfo -> Type = PLUGIN_TYPE_RSP;
strcpy(PluginInfo -> Name, "Static Interpreter");
PluginInfo -> NormalMemory = 0;
PluginInfo -> MemoryBswaped = USE_CLIENT_ENDIAN;
return;
}
p_func GBI_phase;
void no_LLE(void)
{
static int already_warned;
if (already_warned)
return;
message("RSP configured for LLE but not using LLE graphics plugin.");
already_warned = TRUE;
return;
}
EXPORT void CALL InitiateRSP(RSP_INFO Rsp_Info, pu32 CycleCount)
{
int recovered_from_exception;
if (CycleCount != NULL) /* cycle-accuracy not doable with today's hosts */
*CycleCount = 0;
update_conf(CFG_FILE);
RSP_INFO_NAME = Rsp_Info;
DRAM = GET_RSP_INFO(RDRAM);
if (Rsp_Info.DMEM == Rsp_Info.IMEM) /* usually dummy RSP data for testing */
return; /* DMA is not executed just because plugin initiates. */
DMEM = GET_RSP_INFO(DMEM);
IMEM = GET_RSP_INFO(IMEM);
CR[0x0] = &GET_RCP_REG(SP_MEM_ADDR_REG);
CR[0x1] = &GET_RCP_REG(SP_DRAM_ADDR_REG);
CR[0x2] = &GET_RCP_REG(SP_RD_LEN_REG);
CR[0x3] = &GET_RCP_REG(SP_WR_LEN_REG);
CR[0x4] = &GET_RCP_REG(SP_STATUS_REG);
CR[0x5] = &GET_RCP_REG(SP_DMA_FULL_REG);
CR[0x6] = &GET_RCP_REG(SP_DMA_BUSY_REG);
CR[0x7] = &GET_RCP_REG(SP_SEMAPHORE_REG);
CR[0x8] = &GET_RCP_REG(DPC_START_REG);
CR[0x9] = &GET_RCP_REG(DPC_END_REG);
CR[0xA] = &GET_RCP_REG(DPC_CURRENT_REG);
CR[0xB] = &GET_RCP_REG(DPC_STATUS_REG);
CR[0xC] = &GET_RCP_REG(DPC_CLOCK_REG);
CR[0xD] = &GET_RCP_REG(DPC_BUFBUSY_REG);
CR[0xE] = &GET_RCP_REG(DPC_PIPEBUSY_REG);
CR[0xF] = &GET_RCP_REG(DPC_TMEM_REG);
init_regs();
MF_SP_STATUS_TIMEOUT = 32767;
#if 1
GET_RCP_REG(SP_PC_REG) &= 0x00000FFFu; /* hack to fix Mupen64 */
#endif
GBI_phase = GET_RSP_INFO(ProcessRdpList);
if (GBI_phase == NULL)
GBI_phase = no_LLE;
signal(SIGILL, ISA_op_illegal);
#ifndef _WIN32
signal(SIGSEGV, seg_av_handler);
for (SR[ra] = 0; SR[ra] < 0x80000000ul; SR[ra] += 0x200000) {
recovered_from_exception = setjmp(CPU_state);
if (recovered_from_exception)
break;
SR[at] += DRAM[SR[ra]];
}
for (SR[at] = 0; SR[at] < 31; SR[at]++) {
SR[ra] = (SR[ra] & ~1) >> 1;
if (SR[ra] == 0)
break;
}
su_max_address = (1 << SR[at]) - 1;
#endif
if (su_max_address < 0x1FFFFFul)
su_max_address = 0x1FFFFFul; /* 2 MiB */
if (su_max_address > 0xFFFFFFul)
su_max_address = 0xFFFFFFul; /* 16 MiB */
return;
}
EXPORT void CALL RomClosed(void)
{
GET_RCP_REG(SP_PC_REG) = 0x04001000;
/*
* Sometimes the end user won't correctly install to the right directory. :(
* If the config file wasn't installed correctly, politely shut errors up.
*/
#if !defined(M64P_PLUGIN_API)
FILE* stream = fopen(CFG_FILE, "wb");
fwrite(conf, 8, 32 / 8, stream);
fclose(stream);
#endif
return;
}
#if !defined(M64P_PLUGIN_API)
NOINLINE void message(const char* body)
{
#ifdef WIN32
char* argv;
int i, j;
argv = calloc(strlen(body) + 64, 1);
strcpy(argv, "CMD /Q /D /C \"TITLE RSP Message&&ECHO ");
i = 0;
j = strlen(argv);
while (body[i] != '\0') {
if (body[i] == '\n') {
strcat(argv, "&&ECHO ");
++i;
j += 7;
continue;
}
argv[j++] = body[i++];
}
strcat(argv, "&&PAUSE&&EXIT\"");
system(argv);
free(argv);
#else
fputs(body, stdout);
putchar('\n');
puts("Press ENTER to return.");
getchar();
#endif
return;
}
#else
NOINLINE void message(const char* body)
{
#if defined(M64P_PLUGIN_API)
DebugMessage(M64MSG_ERROR, "%s", body);
#else
printf("%s\n", body);
#endif
}
#endif
#if !defined(M64P_PLUGIN_API)
NOINLINE void update_conf(const char* source)
{
FILE* stream;
register int i;
/*
* hazard adjustment
* If file not found, wipe the registry to 0's (all default settings).
*/
for (i = 0; i < 32; i++)
conf[i] = 0x00;
stream = fopen(source, "rb");
if (stream == NULL) {
message("Failed to read config.");
return;
}
fread(conf, 8, 32 / 8, stream);
fclose(stream);
return;
}
#endif
#ifdef SP_EXECUTE_LOG
void step_SP_commands(uint32_t inst)
{
unsigned char endian_swap[4];
char text[256];
char offset[4] = "";
char code[9] = "";
if (output_log == NULL)
return;
endian_swap[00] = (u8)((inst >> 24) & 0xFF);
endian_swap[01] = (u8)((inst >> 16) & 0xFF);
endian_swap[02] = (u8)((inst >> 8) & 0xFF);
endian_swap[03] = (u8)((inst >> 0) & 0xFF);
sprintf(&offset[0], "%03X", GET_RCP_REG(SP_PC_REG) & 0xFFF);
sprintf(&code[0], "%08X", inst);
strcpy(text, offset);
strcat(text, "\n");
strcat(text, code);
message(text); /* PC offset, MIPS hex. */
if (output_log != NULL)
fwrite(endian_swap, 4, 1, output_log);
}
#endif
NOINLINE void export_data_cache(void)
{
pu8 DMEM_swapped;
FILE * out;
register int i;
/* const int little_endian = GET_RSP_INFO(MemoryBswaped); */
DMEM_swapped = calloc(4096, 1);
for (i = 0; i < 4096; i++)
DMEM_swapped[i] = DMEM[BES(i)];
out = fopen("rcpcache.dhex", "wb");
fwrite(DMEM_swapped, 16, 4096 / 16, out);
fclose(out);
free(DMEM_swapped);
return;
}
NOINLINE void export_instruction_cache(void)
{
pu8 IMEM_swapped;
FILE * out;
register int i;
/* const int little_endian = GET_RSP_INFO(MemoryBswaped); */
IMEM_swapped = calloc(4096, 1);
for (i = 0; i < 4096; i++)
IMEM_swapped[i] = IMEM[BES(i)];
out = fopen("rcpcache.ihex", "wb");
fwrite(IMEM_swapped, 16, 4096 / 16, out);
fclose(out);
free(IMEM_swapped);
return;
}
void export_SP_memory(void)
{
export_data_cache();
export_instruction_cache();
return;
}
/*
* Microsoft linker defaults to an entry point of `_DllMainCRTStartup',
* which attaches several CRT dependencies. To eliminate linkage of unused
* startup CRT code, we direct the linker to use DllMain as the entry point.
*
* The same approach is taken with MinGW to get those weird MinGW-specific
* messages and unused initializer functions out of the plugin binary.
*/
#ifdef _WIN32
BOOL WINAPI
DllMain(HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
hModule = lpReserved = NULL; /* unused */
switch (ul_reason_for_call) {
case 1: /* DLL_PROCESS_ATTACH */
case 2: /* DLL_THREAD_ATTACH */
case 3: /* DLL_THREAD_DETACH */
case 0: /* DLL_PROCESS_DETACH */
break;
default:
message("Unknown reason for call.");
}
return TRUE;
}
#endif