RSP: Added musyx ucode suport to HLE from mupen64plus-ae
[mupen64plus-pandora.git] / source / mupen64plus-rsp-hle / src / main.c
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"
36 #include "musyx.h"
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 */
54 static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size);
55 static void dump_binary(const char *const filename, const unsigned char *const bytes,
56                         unsigned int size);
57 static void dump_task(const char *const filename, const OSTask_t *const task);
58
59 static void handle_unknown_task(unsigned int sum);
60 static void handle_unknown_non_task(unsigned int sum);
61
62 /* global variables */
63 RSP_INFO rsp;
64
65 /* local variables */
66 static const int FORWARD_AUDIO = 0, FORWARD_GFX = 1;
67 static void (*l_DebugCallback)(void *, int, const char *) = NULL;
68 static void *l_DebugCallContext = NULL;
69 static 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  **/
84 static int is_task(void)
85 {
86     return (get_task()->ucode_boot_size <= 0x1000);
87 }
88
89 static void rsp_break(unsigned int setbits)
90 {
91     *rsp.SP_STATUS_REG |= setbits | RSP_STATUS_BROKE | RSP_STATUS_HALT;
92
93     if ((*rsp.SP_STATUS_REG & RSP_STATUS_INTR_ON_BREAK)) {
94         *rsp.MI_INTR_REG |= MI_INTR_SP;
95         rsp.CheckInterrupts();
96     }
97 }
98
99 static void forward_gfx_task(void)
100 {
101     if (rsp.ProcessDlistList != NULL) {
102         rsp.ProcessDlistList();
103         *rsp.DPC_STATUS_REG &= ~DP_STATUS_FREEZE;
104     }
105 }
106
107 static void forward_audio_task(void)
108 {
109     if (rsp.ProcessAlistList != NULL)
110         rsp.ProcessAlistList();
111 }
112
113 static void show_cfb(void)
114 {
115     if (rsp.ShowCFB != NULL)
116         rsp.ShowCFB();
117 }
118
119 static int try_fast_audio_dispatching(void)
120 {
121     /* identify audio ucode by using the content of ucode_data */
122     const OSTask_t *const task = get_task();
123     const unsigned char *const udata_ptr = rsp.RDRAM + task->ucode_data;
124
125     if (*(unsigned int *)(udata_ptr + 0) == 0x00000001) {
126         if (*(unsigned int *)(udata_ptr + 0x30) == 0xf0000f00) {
127             /**
128             * Many games including:
129             * Super Mario 64, Diddy Kong Racing, BlastCorp, GoldenEye, ... (most common)
130             **/
131             alist_process_ABI1();
132             return 1;
133         } else {
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.
145             * That's the reason of the workaround in ucode2.c with isZeldaABI and isMKABI
146             **/
147             alist_process_ABI2();
148             return 1;
149         }
150     } else {
151         if (*(unsigned int *)(udata_ptr + 0x10) == 0x00000001) {
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              **/
159             musyx_task();
160             return 1;
161         } else {
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              **/
167             alist_process_ABI3();
168             return 1;
169         }
170     }
171
172     return 0;
173 }
174
175 static int try_fast_task_dispatching(void)
176 {
177     /* identify task ucode by its type */
178     const OSTask_t *const task = get_task();
179
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;
199     }
200
201     return 0;
202 }
203
204 static void normal_task_dispatching(void)
205 {
206     const OSTask_t *const task = get_task();
207     const unsigned int sum =
208         sum_bytes(rsp.RDRAM + task->ucode, min(task->ucode_size, 0xf80) >> 1);
209
210     switch (sum) {
211     /* StoreVe12: found in Zelda Ocarina of Time [misleading task->type == 4] */
212     case 0x278:
213         /* Nothing to emulate */
214         return;
215
216     /* GFX: Twintris [misleading task->type == 0] */
217     case 0x212ee:
218         if (FORWARD_GFX) {
219             forward_gfx_task();
220             return;
221         }
222         break;
223
224     /* JPEG: found in Pokemon Stadium J */
225     case 0x2c85a:
226         jpeg_decode_PS0();
227         return;
228
229     /* JPEG: found in Zelda Ocarina of Time, Pokemon Stadium 1, Pokemon Stadium 2 */
230     case 0x2caa6:
231         jpeg_decode_PS();
232         return;
233
234     /* JPEG: found in Ogre Battle, Bottom of the 9th */
235     case 0x130de:
236     case 0x278b0:
237         jpeg_decode_OB();
238         return;
239     }
240
241     handle_unknown_task(sum);
242 }
243
244 static void non_task_dispatching(void)
245 {
246     const unsigned int sum = sum_bytes(rsp.IMEM, 0x1000 >> 1);
247
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;
254     }
255
256     handle_unknown_non_task(sum);
257 }
258
259 static void handle_unknown_task(unsigned int sum)
260 {
261     char filename[256];
262     const OSTask_t *const task = get_task();
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);
268
269     /* dump ucode_boot */
270     sprintf(&filename[0], "ucode_boot_%x.bin", sum);
271     dump_binary(filename, rsp.RDRAM + (task->ucode_boot & 0x7fffff), task->ucode_boot_size);
272
273     /* dump ucode */
274     if (task->ucode != 0) {
275         sprintf(&filename[0], "ucode_%x.bin", sum);
276         dump_binary(filename, rsp.RDRAM + (task->ucode & 0x7fffff), 0xf80);
277     }
278
279     /* dump ucode_data */
280     if (task->ucode_data != 0) {
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
285     /* dump data */
286     if (task->data_ptr != 0) {
287         sprintf(&filename[0], "data_%x.bin", sum);
288         dump_binary(filename, rsp.RDRAM + (task->data_ptr & 0x7fffff), task->data_size);
289     }
290 }
291
292 static 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
298     /* dump IMEM & DMEM for further analysis */
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 */
308 void 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 */
325 EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
326                                      void (*DebugCallback)(void *, int, const char *))
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
341 EXPORT 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
354 EXPORT 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;
365
366     if (PluginNamePtr != NULL)
367         *PluginNamePtr = "Hacktarux/Azimer High-Level Emulation RSP Plugin";
368
369     if (Capabilities != NULL)
370         *Capabilities = 0;
371
372     return M64ERR_SUCCESS;
373 }
374
375 EXPORT unsigned int CALL DoRspCycles(unsigned int Cycles)
376 {
377     if (is_task()) {
378         if (!try_fast_task_dispatching())
379             normal_task_dispatching();
380         rsp_break(RSP_STATUS_TASKDONE);
381     } else {
382         non_task_dispatching();
383         rsp_break(0);
384     }
385
386     return Cycles;
387 }
388
389 EXPORT void CALL InitiateRSP(RSP_INFO Rsp_Info, unsigned int *CycleCount)
390 {
391     rsp = Rsp_Info;
392 }
393
394 EXPORT 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 */
404 static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size)
405 {
406     unsigned int sum = 0;
407     const unsigned char *const bytes_end = bytes + size;
408
409     while (bytes != bytes_end)
410         sum += *bytes++;
411
412     return sum;
413 }
414
415
416 static void dump_binary(const char *const filename, const unsigned char *const bytes,
417                         unsigned int size)
418 {
419     FILE *f;
420
421     /* if file already exists, do nothing */
422     f = fopen(filename, "r");
423     if (f == NULL) {
424         /* else we write bytes to the file */
425         f = fopen(filename, "wb");
426         if (f != NULL) {
427             if (fwrite(bytes, 1, size, f) != size)
428                 DebugMessage(M64MSG_ERROR, "Writing error on %s", filename);
429             fclose(f);
430         } else
431             DebugMessage(M64MSG_ERROR, "Couldn't open %s for writing !", filename);
432     } else
433         fclose(f);
434 }
435
436 static void dump_task(const char *const filename, const OSTask_t *const task)
437 {
438     FILE *f;
439
440     f = fopen(filename, "r");
441     if (f == NULL) {
442         f = fopen(filename, "w");
443         fprintf(f,
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);
461         fclose(f);
462     } else
463         fclose(f);
464 }
465