RSP: Added musyx ucode suport to HLE from mupen64plus-ae
[mupen64plus-pandora.git] / source / mupen64plus-rsp-hle / src / main.c
CommitLineData
d9e74a6f 1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus-rsp-hle - main.c *
3 * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
4 * Copyright (C) 2012 Bobby Smiles *
5 * Copyright (C) 2009 Richard Goedeken *
6 * Copyright (C) 2002 Hacktarux *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
23
24#include <stdarg.h>
25#include <string.h>
26#include <stdio.h>
27
28#define M64P_PLUGIN_PROTOTYPES 1
29#include "m64p_types.h"
30#include "m64p_common.h"
31#include "m64p_plugin.h"
32#include "hle.h"
33#include "alist.h"
34#include "cicx105.h"
35#include "jpeg.h"
0a8a0368 36#include "musyx.h"
d9e74a6f 37
38#define min(a,b) (((a) < (b)) ? (a) : (b))
39
40/* some rsp status flags */
41#define RSP_STATUS_HALT 0x1
42#define RSP_STATUS_BROKE 0x2
43#define RSP_STATUS_INTR_ON_BREAK 0x40
44#define RSP_STATUS_TASKDONE 0x200
45
46/* some rdp status flags */
47#define DP_STATUS_FREEZE 0x2
48
49/* some mips interface interrupt flags */
50#define MI_INTR_SP 0x1
51
52
53/* helper functions prototypes */
54static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size);
0a8a0368 55static void dump_binary(const char *const filename, const unsigned char *const bytes,
d9e74a6f 56 unsigned int size);
0a8a0368 57static void dump_task(const char *const filename, const OSTask_t *const task);
d9e74a6f 58
59static void handle_unknown_task(unsigned int sum);
60static void handle_unknown_non_task(unsigned int sum);
61
62/* global variables */
63RSP_INFO rsp;
64
65/* local variables */
66static const int FORWARD_AUDIO = 0, FORWARD_GFX = 1;
67static void (*l_DebugCallback)(void *, int, const char *) = NULL;
68static void *l_DebugCallContext = NULL;
69static int l_PluginInit = 0;
70
71/* local functions */
72
73
74/**
75 * Try to figure if the RSP was launched using osSpTask* functions
76 * and not run directly (in which case DMEM[0xfc0-0xfff] is meaningless).
77 *
78 * Previously, the ucode_size field was used to determine this,
79 * but it is not robust enough (hi Pokemon Stadium !) because games could write anything
80 * in this field : most ucode_boot discard the value and just use 0xf7f anyway.
81 *
82 * Using ucode_boot_size should be more robust in this regard.
83 **/
0a8a0368 84static int is_task(void)
d9e74a6f 85{
86 return (get_task()->ucode_boot_size <= 0x1000);
87}
88
89static void rsp_break(unsigned int setbits)
90{
91 *rsp.SP_STATUS_REG |= setbits | RSP_STATUS_BROKE | RSP_STATUS_HALT;
92
0a8a0368 93 if ((*rsp.SP_STATUS_REG & RSP_STATUS_INTR_ON_BREAK)) {
d9e74a6f 94 *rsp.MI_INTR_REG |= MI_INTR_SP;
95 rsp.CheckInterrupts();
96 }
97}
98
0a8a0368 99static void forward_gfx_task(void)
d9e74a6f 100{
0a8a0368 101 if (rsp.ProcessDlistList != NULL) {
d9e74a6f 102 rsp.ProcessDlistList();
103 *rsp.DPC_STATUS_REG &= ~DP_STATUS_FREEZE;
104 }
105}
106
0a8a0368 107static void forward_audio_task(void)
d9e74a6f 108{
109 if (rsp.ProcessAlistList != NULL)
d9e74a6f 110 rsp.ProcessAlistList();
d9e74a6f 111}
112
0a8a0368 113static void show_cfb(void)
d9e74a6f 114{
115 if (rsp.ShowCFB != NULL)
d9e74a6f 116 rsp.ShowCFB();
d9e74a6f 117}
118
0a8a0368 119static int try_fast_audio_dispatching(void)
d9e74a6f 120{
121 /* identify audio ucode by using the content of ucode_data */
0a8a0368 122 const OSTask_t *const task = get_task();
123 const unsigned char *const udata_ptr = rsp.RDRAM + task->ucode_data;
d9e74a6f 124
0a8a0368 125 if (*(unsigned int *)(udata_ptr + 0) == 0x00000001) {
126 if (*(unsigned int *)(udata_ptr + 0x30) == 0xf0000f00) {
d9e74a6f 127 /**
128 * Many games including:
129 * Super Mario 64, Diddy Kong Racing, BlastCorp, GoldenEye, ... (most common)
130 **/
0a8a0368 131 alist_process_ABI1();
132 return 1;
133 } else {
d9e74a6f 134 /**
135 * Mario Kart / Wave Race,
136 * LylatWars,
137 * FZeroX,
138 * Yoshi Story,
139 * 1080 Snowboarding,
140 * Zelda Ocarina of Time,
141 * Zelda Majoras Mask / Pokemon Stadium 2,
142 * Animal Crossing
143 *
144 * FIXME: in fact, all these games do not share the same ABI.
0a8a0368 145 * That's the reason of the workaround in ucode2.c with isZeldaABI and isMKABI
d9e74a6f 146 **/
0a8a0368 147 alist_process_ABI2();
148 return 1;
d9e74a6f 149 }
0a8a0368 150 } else {
151 if (*(unsigned int *)(udata_ptr + 0x10) == 0x00000001) {
d9e74a6f 152 /**
153 * Musyx ucode found in following games:
154 * RogueSquadron, ResidentEvil2, SnowCrossPolaris, TheWorldIsNotEnough,
155 * RugratsInParis, NBAShowTime, HydroThunder, Tarzan,
156 * GauntletLegend, Rush2049, IndianaJones, BattleForNaboo
157 * TODO: implement ucode
158 **/
0a8a0368 159 musyx_task();
160 return 1;
161 } else {
d9e74a6f 162 /**
163 * Many games including:
164 * Pokemon Stadium, Banjo Kazooie, Donkey Kong, Banjo Tooie, Jet Force Gemini,
165 * Mickey SpeedWay USA, Perfect Dark, Conker Bad Fur Day ...
166 **/
0a8a0368 167 alist_process_ABI3();
168 return 1;
d9e74a6f 169 }
170 }
0a8a0368 171
d9e74a6f 172 return 0;
173}
174
0a8a0368 175static int try_fast_task_dispatching(void)
d9e74a6f 176{
177 /* identify task ucode by its type */
0a8a0368 178 const OSTask_t *const task = get_task();
d9e74a6f 179
0a8a0368 180 switch (task->type) {
181 case 1:
182 if (FORWARD_GFX) {
183 forward_gfx_task();
184 return 1;
185 }
186 break;
187
188 case 2:
189 if (FORWARD_AUDIO) {
190 forward_audio_task();
191 return 1;
192 } else if (try_fast_audio_dispatching())
193 return 1;
194 break;
195
196 case 7:
197 show_cfb();
198 return 1;
d9e74a6f 199 }
200
201 return 0;
202}
203
0a8a0368 204static void normal_task_dispatching(void)
d9e74a6f 205{
0a8a0368 206 const OSTask_t *const task = get_task();
d9e74a6f 207 const unsigned int sum =
208 sum_bytes(rsp.RDRAM + task->ucode, min(task->ucode_size, 0xf80) >> 1);
209
0a8a0368 210 switch (sum) {
211 /* StoreVe12: found in Zelda Ocarina of Time [misleading task->type == 4] */
212 case 0x278:
213 /* Nothing to emulate */
214 return;
d9e74a6f 215
0a8a0368 216 /* GFX: Twintris [misleading task->type == 0] */
217 case 0x212ee:
218 if (FORWARD_GFX) {
219 forward_gfx_task();
220 return;
221 }
222 break;
d9e74a6f 223
0a8a0368 224 /* JPEG: found in Pokemon Stadium J */
225 case 0x2c85a:
226 jpeg_decode_PS0();
227 return;
d9e74a6f 228
0a8a0368 229 /* JPEG: found in Zelda Ocarina of Time, Pokemon Stadium 1, Pokemon Stadium 2 */
230 case 0x2caa6:
231 jpeg_decode_PS();
232 return;
d9e74a6f 233
0a8a0368 234 /* JPEG: found in Ogre Battle, Bottom of the 9th */
235 case 0x130de:
236 case 0x278b0:
237 jpeg_decode_OB();
238 return;
d9e74a6f 239 }
240
241 handle_unknown_task(sum);
242}
243
0a8a0368 244static void non_task_dispatching(void)
d9e74a6f 245{
246 const unsigned int sum = sum_bytes(rsp.IMEM, 0x1000 >> 1);
247
0a8a0368 248 switch (sum) {
249 /* CIC x105 ucode (used during boot of CIC x105 games) */
250 case 0x9e2: /* CIC 6105 */
251 case 0x9f2: /* CIC 7105 */
252 cicx105_ucode();
253 return;
d9e74a6f 254 }
255
256 handle_unknown_non_task(sum);
257}
258
259static void handle_unknown_task(unsigned int sum)
260{
261 char filename[256];
0a8a0368 262 const OSTask_t *const task = get_task();
d9e74a6f 263
264 DebugMessage(M64MSG_WARNING, "unknown OSTask: sum %x PC:%x", sum, *rsp.SP_PC_REG);
265
266 sprintf(&filename[0], "task_%x.log", sum);
267 dump_task(filename, task);
0a8a0368 268
269 /* dump ucode_boot */
d9e74a6f 270 sprintf(&filename[0], "ucode_boot_%x.bin", sum);
271 dump_binary(filename, rsp.RDRAM + (task->ucode_boot & 0x7fffff), task->ucode_boot_size);
272
0a8a0368 273 /* dump ucode */
274 if (task->ucode != 0) {
d9e74a6f 275 sprintf(&filename[0], "ucode_%x.bin", sum);
276 dump_binary(filename, rsp.RDRAM + (task->ucode & 0x7fffff), 0xf80);
277 }
278
0a8a0368 279 /* dump ucode_data */
280 if (task->ucode_data != 0) {
d9e74a6f 281 sprintf(&filename[0], "ucode_data_%x.bin", sum);
282 dump_binary(filename, rsp.RDRAM + (task->ucode_data & 0x7fffff), task->ucode_data_size);
283 }
284
0a8a0368 285 /* dump data */
286 if (task->data_ptr != 0) {
d9e74a6f 287 sprintf(&filename[0], "data_%x.bin", sum);
288 dump_binary(filename, rsp.RDRAM + (task->data_ptr & 0x7fffff), task->data_size);
289 }
290}
291
292static void handle_unknown_non_task(unsigned int sum)
293{
294 char filename[256];
295
296 DebugMessage(M64MSG_WARNING, "unknown RSP code: sum: %x PC:%x", sum, *rsp.SP_PC_REG);
297
0a8a0368 298 /* dump IMEM & DMEM for further analysis */
d9e74a6f 299 sprintf(&filename[0], "imem_%x.bin", sum);
300 dump_binary(filename, rsp.IMEM, 0x1000);
301
302 sprintf(&filename[0], "dmem_%x.bin", sum);
303 dump_binary(filename, rsp.DMEM, 0x1000);
304}
305
306
307/* Global functions */
308void DebugMessage(int level, const char *message, ...)
309{
310 char msgbuf[1024];
311 va_list args;
312
313 if (l_DebugCallback == NULL)
314 return;
315
316 va_start(args, message);
317 vsprintf(msgbuf, message, args);
318
319 (*l_DebugCallback)(l_DebugCallContext, level, msgbuf);
320
321 va_end(args);
322}
323
324/* DLL-exported functions */
325EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
0a8a0368 326 void (*DebugCallback)(void *, int, const char *))
d9e74a6f 327{
328 if (l_PluginInit)
329 return M64ERR_ALREADY_INIT;
330
331 /* first thing is to set the callback function for debug info */
332 l_DebugCallback = DebugCallback;
333 l_DebugCallContext = Context;
334
335 /* this plugin doesn't use any Core library functions (ex for Configuration), so no need to keep the CoreLibHandle */
336
337 l_PluginInit = 1;
338 return M64ERR_SUCCESS;
339}
340
341EXPORT m64p_error CALL PluginShutdown(void)
342{
343 if (!l_PluginInit)
344 return M64ERR_NOT_INIT;
345
346 /* reset some local variable */
347 l_DebugCallback = NULL;
348 l_DebugCallContext = NULL;
349
350 l_PluginInit = 0;
351 return M64ERR_SUCCESS;
352}
353
354EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)
355{
356 /* set version info */
357 if (PluginType != NULL)
358 *PluginType = M64PLUGIN_RSP;
359
360 if (PluginVersion != NULL)
361 *PluginVersion = RSP_HLE_VERSION;
362
363 if (APIVersion != NULL)
364 *APIVersion = RSP_PLUGIN_API_VERSION;
0a8a0368 365
d9e74a6f 366 if (PluginNamePtr != NULL)
367 *PluginNamePtr = "Hacktarux/Azimer High-Level Emulation RSP Plugin";
368
369 if (Capabilities != NULL)
d9e74a6f 370 *Capabilities = 0;
0a8a0368 371
d9e74a6f 372 return M64ERR_SUCCESS;
373}
374
375EXPORT unsigned int CALL DoRspCycles(unsigned int Cycles)
376{
0a8a0368 377 if (is_task()) {
378 if (!try_fast_task_dispatching())
379 normal_task_dispatching();
d9e74a6f 380 rsp_break(RSP_STATUS_TASKDONE);
0a8a0368 381 } else {
d9e74a6f 382 non_task_dispatching();
383 rsp_break(0);
384 }
385
386 return Cycles;
387}
388
389EXPORT void CALL InitiateRSP(RSP_INFO Rsp_Info, unsigned int *CycleCount)
390{
391 rsp = Rsp_Info;
392}
393
394EXPORT void CALL RomClosed(void)
395{
396 memset(rsp.DMEM, 0, 0x1000);
397 memset(rsp.IMEM, 0, 0x1000);
398
399 init_ucode2();
400}
401
402
403/* local helper functions */
404static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size)
405{
406 unsigned int sum = 0;
0a8a0368 407 const unsigned char *const bytes_end = bytes + size;
d9e74a6f 408
409 while (bytes != bytes_end)
410 sum += *bytes++;
411
412 return sum;
413}
414
415
0a8a0368 416static void dump_binary(const char *const filename, const unsigned char *const bytes,
d9e74a6f 417 unsigned int size)
418{
419 FILE *f;
420
0a8a0368 421 /* if file already exists, do nothing */
d9e74a6f 422 f = fopen(filename, "r");
0a8a0368 423 if (f == NULL) {
424 /* else we write bytes to the file */
425 f = fopen(filename, "wb");
d9e74a6f 426 if (f != NULL) {
427 if (fwrite(bytes, 1, size, f) != size)
d9e74a6f 428 DebugMessage(M64MSG_ERROR, "Writing error on %s", filename);
d9e74a6f 429 fclose(f);
0a8a0368 430 } else
d9e74a6f 431 DebugMessage(M64MSG_ERROR, "Couldn't open %s for writing !", filename);
0a8a0368 432 } else
d9e74a6f 433 fclose(f);
d9e74a6f 434}
435
0a8a0368 436static void dump_task(const char *const filename, const OSTask_t *const task)
d9e74a6f 437{
438 FILE *f;
439
440 f = fopen(filename, "r");
0a8a0368 441 if (f == NULL) {
d9e74a6f 442 f = fopen(filename, "w");
443 fprintf(f,
0a8a0368 444 "type = %d\n"
445 "flags = %d\n"
446 "ucode_boot = %#08x size = %#x\n"
447 "ucode = %#08x size = %#x\n"
448 "ucode_data = %#08x size = %#x\n"
449 "dram_stack = %#08x size = %#x\n"
450 "output_buff = %#08x *size = %#x\n"
451 "data = %#08x size = %#x\n"
452 "yield_data = %#08x size = %#x\n",
453 task->type, task->flags,
454 task->ucode_boot, task->ucode_boot_size,
455 task->ucode, task->ucode_size,
456 task->ucode_data, task->ucode_data_size,
457 task->dram_stack, task->dram_stack_size,
458 task->output_buff, task->output_buff_size,
459 task->data_ptr, task->data_size,
460 task->yield_data_ptr, task->yield_data_size);
d9e74a6f 461 fclose(f);
0a8a0368 462 } else
d9e74a6f 463 fclose(f);
d9e74a6f 464}
465