Path: blob/master/libmupen64plus/mupen64plus-rsp-hle/src/main.c
2 views
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1* Mupen64plus-rsp-hle - main.c *2* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *3* Copyright (C) 2012 Bobby Smiles *4* Copyright (C) 2009 Richard Goedeken *5* Copyright (C) 2002 Hacktarux *6* *7* This program is free software; you can redistribute it and/or modify *8* it under the terms of the GNU General Public License as published by *9* the Free Software Foundation; either version 2 of the License, or *10* (at your option) any later version. *11* *12* This program is distributed in the hope that it will be useful, *13* but WITHOUT ANY WARRANTY; without even the implied warranty of *14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *15* GNU General Public License for more details. *16* *17* You should have received a copy of the GNU General Public License *18* along with this program; if not, write to the *19* Free Software Foundation, Inc., *20* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *21* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */2223#include <stdarg.h>24#include <string.h>25#include <stdio.h>2627#define M64P_PLUGIN_PROTOTYPES 128#include "m64p_types.h"29#include "m64p_common.h"30#include "m64p_plugin.h"31#include "hle.h"32#include "alist.h"33#include "cicx105.h"34#include "jpeg.h"3536#define min(a,b) (((a) < (b)) ? (a) : (b))3738/* some rsp status flags */39#define RSP_STATUS_HALT 0x140#define RSP_STATUS_BROKE 0x241#define RSP_STATUS_INTR_ON_BREAK 0x4042#define RSP_STATUS_TASKDONE 0x2004344/* some rdp status flags */45#define DP_STATUS_FREEZE 0x24647/* some mips interface interrupt flags */48#define MI_INTR_SP 0x1495051/* helper functions prototypes */52static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size);53static void dump_binary(const char * const filename, const unsigned char * const bytes,54unsigned int size);55static void dump_task(const char * const filename, const OSTask_t * const task);5657static void handle_unknown_task(unsigned int sum);58static void handle_unknown_non_task(unsigned int sum);5960/* global variables */61RSP_INFO rsp;6263/* local variables */64static const int FORWARD_AUDIO = 0, FORWARD_GFX = 1;65static void (*l_DebugCallback)(void *, int, const char *) = NULL;66static void *l_DebugCallContext = NULL;67static int l_PluginInit = 0;6869/* local functions */707172/**73* Try to figure if the RSP was launched using osSpTask* functions74* and not run directly (in which case DMEM[0xfc0-0xfff] is meaningless).75*76* Previously, the ucode_size field was used to determine this,77* but it is not robust enough (hi Pokemon Stadium !) because games could write anything78* in this field : most ucode_boot discard the value and just use 0xf7f anyway.79*80* Using ucode_boot_size should be more robust in this regard.81**/82static int is_task()83{84return (get_task()->ucode_boot_size <= 0x1000);85}8687static void rsp_break(unsigned int setbits)88{89*rsp.SP_STATUS_REG |= setbits | RSP_STATUS_BROKE | RSP_STATUS_HALT;9091if ((*rsp.SP_STATUS_REG & RSP_STATUS_INTR_ON_BREAK))92{93*rsp.MI_INTR_REG |= MI_INTR_SP;94rsp.CheckInterrupts();95}96}9798static void forward_gfx_task()99{100if (rsp.ProcessDlistList != NULL)101{102rsp.ProcessDlistList();103*rsp.DPC_STATUS_REG &= ~DP_STATUS_FREEZE;104}105}106107static void forward_audio_task()108{109if (rsp.ProcessAlistList != NULL)110{111rsp.ProcessAlistList();112}113}114115static void show_cfb()116{117if (rsp.ShowCFB != NULL)118{119rsp.ShowCFB();120}121}122123static int try_fast_audio_dispatching()124{125/* identify audio ucode by using the content of ucode_data */126const OSTask_t * const task = get_task();127const unsigned char * const udata_ptr = rsp.RDRAM + task->ucode_data;128129if (*(unsigned int*)(udata_ptr + 0) == 0x00000001)130{131if (*(unsigned int*)(udata_ptr + 0x30) == 0xf0000f00)132{133/**134* Many games including:135* Super Mario 64, Diddy Kong Racing, BlastCorp, GoldenEye, ... (most common)136**/137alist_process_ABI1(); return 1;138}139else140{141/**142* Mario Kart / Wave Race,143* LylatWars,144* FZeroX,145* Yoshi Story,146* 1080 Snowboarding,147* Zelda Ocarina of Time,148* Zelda Majoras Mask / Pokemon Stadium 2,149* Animal Crossing150*151* FIXME: in fact, all these games do not share the same ABI.152* That's the reason of the workaround in ucode2.cpp with isZeldaABI and isMKABI153**/154alist_process_ABI2(); return 1;155}156}157else158{159if (*(unsigned int*)(udata_ptr + 0x10) == 0x00000001)160{161/**162* Musyx ucode found in following games:163* RogueSquadron, ResidentEvil2, SnowCrossPolaris, TheWorldIsNotEnough,164* RugratsInParis, NBAShowTime, HydroThunder, Tarzan,165* GauntletLegend, Rush2049, IndianaJones, BattleForNaboo166* TODO: implement ucode167**/168DebugMessage(M64MSG_WARNING, "MusyX ucode not implemented.");169/* return 1; */170}171else172{173/**174* Many games including:175* Pokemon Stadium, Banjo Kazooie, Donkey Kong, Banjo Tooie, Jet Force Gemini,176* Mickey SpeedWay USA, Perfect Dark, Conker Bad Fur Day ...177**/178alist_process_ABI3(); return 1;179}180}181182return 0;183}184185static int try_fast_task_dispatching()186{187/* identify task ucode by its type */188const OSTask_t * const task = get_task();189190switch (task->type)191{192case 1: if (FORWARD_GFX) { forward_gfx_task(); return 1; } break;193194case 2:195if (FORWARD_AUDIO) { forward_audio_task(); return 1; }196else if (try_fast_audio_dispatching()) { return 1; }197break;198199case 7: show_cfb(); return 1;200}201202return 0;203}204205static void normal_task_dispatching()206{207const OSTask_t * const task = get_task();208const unsigned int sum =209sum_bytes(rsp.RDRAM + task->ucode, min(task->ucode_size, 0xf80) >> 1);210211switch (sum)212{213/* StoreVe12: found in Zelda Ocarina of Time [misleading task->type == 4] */214case 0x278: /* Nothing to emulate */ return;215216/* GFX: Twintris [misleading task->type == 0] */217case 0x212ee:218if (FORWARD_GFX) { forward_gfx_task(); return; }219break;220221/* JPEG: found in Pokemon Stadium J */222case 0x2c85a: jpeg_decode_PS0(); return;223224/* JPEG: found in Zelda Ocarina of Time, Pokemon Stadium 1, Pokemon Stadium 2 */225case 0x2caa6: jpeg_decode_PS(); return;226227/* JPEG: found in Ogre Battle, Bottom of the 9th */228case 0x130de: jpeg_decode_OB(); return;229}230231handle_unknown_task(sum);232}233234static void non_task_dispatching()235{236const unsigned int sum = sum_bytes(rsp.IMEM, 0x1000 >> 1);237238switch(sum)239{240/* CIC x105 ucode (used during boot of CIC x105 games) */241case 0x9e2: /* CIC 6105 */242case 0x9f2: /* CIC 7105 */243cicx105_ucode(); return;244}245246handle_unknown_non_task(sum);247}248249static void handle_unknown_task(unsigned int sum)250{251char filename[256];252const OSTask_t * const task = get_task();253254DebugMessage(M64MSG_WARNING, "unknown OSTask: sum %x PC:%x", sum, *rsp.SP_PC_REG);255256sprintf(&filename[0], "task_%x.log", sum);257dump_task(filename, task);258259// dump ucode_boot260sprintf(&filename[0], "ucode_boot_%x.bin", sum);261dump_binary(filename, rsp.RDRAM + (task->ucode_boot & 0x7fffff), task->ucode_boot_size);262263// dump ucode264if (task->ucode != 0)265{266sprintf(&filename[0], "ucode_%x.bin", sum);267dump_binary(filename, rsp.RDRAM + (task->ucode & 0x7fffff), 0xf80);268}269270// dump ucode_data271if (task->ucode_data != 0)272{273sprintf(&filename[0], "ucode_data_%x.bin", sum);274dump_binary(filename, rsp.RDRAM + (task->ucode_data & 0x7fffff), task->ucode_data_size);275}276277// dump data278if (task->data_ptr != 0)279{280sprintf(&filename[0], "data_%x.bin", sum);281dump_binary(filename, rsp.RDRAM + (task->data_ptr & 0x7fffff), task->data_size);282}283}284285static void handle_unknown_non_task(unsigned int sum)286{287char filename[256];288289DebugMessage(M64MSG_WARNING, "unknown RSP code: sum: %x PC:%x", sum, *rsp.SP_PC_REG);290291// dump IMEM & DMEM for further analysis292sprintf(&filename[0], "imem_%x.bin", sum);293dump_binary(filename, rsp.IMEM, 0x1000);294295sprintf(&filename[0], "dmem_%x.bin", sum);296dump_binary(filename, rsp.DMEM, 0x1000);297}298299300/* Global functions */301void DebugMessage(int level, const char *message, ...)302{303char msgbuf[1024];304va_list args;305306if (l_DebugCallback == NULL)307return;308309va_start(args, message);310vsprintf(msgbuf, message, args);311312(*l_DebugCallback)(l_DebugCallContext, level, msgbuf);313314va_end(args);315}316317/* DLL-exported functions */318EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,319void (*DebugCallback)(void *, int, const char *))320{321if (l_PluginInit)322return M64ERR_ALREADY_INIT;323324/* first thing is to set the callback function for debug info */325l_DebugCallback = DebugCallback;326l_DebugCallContext = Context;327328/* this plugin doesn't use any Core library functions (ex for Configuration), so no need to keep the CoreLibHandle */329330l_PluginInit = 1;331return M64ERR_SUCCESS;332}333334EXPORT m64p_error CALL PluginShutdown(void)335{336if (!l_PluginInit)337return M64ERR_NOT_INIT;338339/* reset some local variable */340l_DebugCallback = NULL;341l_DebugCallContext = NULL;342343l_PluginInit = 0;344return M64ERR_SUCCESS;345}346347EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)348{349/* set version info */350if (PluginType != NULL)351*PluginType = M64PLUGIN_RSP;352353if (PluginVersion != NULL)354*PluginVersion = RSP_HLE_VERSION;355356if (APIVersion != NULL)357*APIVersion = RSP_PLUGIN_API_VERSION;358359if (PluginNamePtr != NULL)360*PluginNamePtr = "Hacktarux/Azimer High-Level Emulation RSP Plugin";361362if (Capabilities != NULL)363{364*Capabilities = 0;365}366367return M64ERR_SUCCESS;368}369370EXPORT unsigned int CALL DoRspCycles(unsigned int Cycles)371{372if (is_task())373{374if (!try_fast_task_dispatching()) { normal_task_dispatching(); }375rsp_break(RSP_STATUS_TASKDONE);376}377else378{379non_task_dispatching();380rsp_break(0);381}382383return Cycles;384}385386EXPORT void CALL InitiateRSP(RSP_INFO Rsp_Info, unsigned int *CycleCount)387{388rsp = Rsp_Info;389}390391EXPORT void CALL RomClosed(void)392{393memset(rsp.DMEM, 0, 0x1000);394memset(rsp.IMEM, 0, 0x1000);395396init_ucode2();397}398399400/* local helper functions */401static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size)402{403unsigned int sum = 0;404const unsigned char * const bytes_end = bytes + size;405406while (bytes != bytes_end)407sum += *bytes++;408409return sum;410}411412413static void dump_binary(const char * const filename, const unsigned char * const bytes,414unsigned int size)415{416FILE *f;417418// if file already exists, do nothing419f = fopen(filename, "r");420if (f == NULL)421{422// else we write bytes to the file423f= fopen(filename, "wb");424if (f != NULL) {425if (fwrite(bytes, 1, size, f) != size)426{427DebugMessage(M64MSG_ERROR, "Writing error on %s", filename);428}429fclose(f);430}431else432{433DebugMessage(M64MSG_ERROR, "Couldn't open %s for writing !", filename);434}435}436else437{438fclose(f);439}440}441442static void dump_task(const char * const filename, const OSTask_t * const task)443{444FILE *f;445446f = fopen(filename, "r");447if (f == NULL)448{449f = fopen(filename, "w");450fprintf(f,451"type = %d\n"452"flags = %d\n"453"ucode_boot = %#08x size = %#x\n"454"ucode = %#08x size = %#x\n"455"ucode_data = %#08x size = %#x\n"456"dram_stack = %#08x size = %#x\n"457"output_buff = %#08x *size = %#x\n"458"data = %#08x size = %#x\n"459"yield_data = %#08x size = %#x\n",460task->type, task->flags,461task->ucode_boot, task->ucode_boot_size,462task->ucode, task->ucode_size,463task->ucode_data, task->ucode_data_size,464task->dram_stack, task->dram_stack_size,465task->output_buff, task->output_buff_size,466task->data_ptr, task->data_size,467task->yield_data_ptr, task->yield_data_size);468fclose(f);469}470else471{472fclose(f);473}474}475476477478