ALL: Huge upstream synch + PerRom DelaySI & CountPerOp parameters
[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
37 #define min(a,b) (((a) < (b)) ? (a) : (b))
38
39 /* some rsp status flags */
40 #define RSP_STATUS_HALT             0x1
41 #define RSP_STATUS_BROKE            0x2
42 #define RSP_STATUS_INTR_ON_BREAK    0x40
43 #define RSP_STATUS_TASKDONE         0x200
44
45 /* some rdp status flags */
46 #define DP_STATUS_FREEZE            0x2
47
48 /* some mips interface interrupt flags */
49 #define MI_INTR_SP                  0x1
50
51
52 /* helper functions prototypes */
53 static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size);
54 static void dump_binary(const char * const filename, const unsigned char * const bytes,
55                         unsigned int size);
56 static void dump_task(const char * const filename, const OSTask_t * const task);
57
58 static void handle_unknown_task(unsigned int sum);
59 static void handle_unknown_non_task(unsigned int sum);
60
61 /* global variables */
62 RSP_INFO rsp;
63
64 /* local variables */
65 static const int FORWARD_AUDIO = 0, FORWARD_GFX = 1;
66 static void (*l_DebugCallback)(void *, int, const char *) = NULL;
67 static void *l_DebugCallContext = NULL;
68 static int l_PluginInit = 0;
69
70 /* local functions */
71
72
73 /**
74  * Try to figure if the RSP was launched using osSpTask* functions
75  * and not run directly (in which case DMEM[0xfc0-0xfff] is meaningless).
76  *
77  * Previously, the ucode_size field was used to determine this,
78  * but it is not robust enough (hi Pokemon Stadium !) because games could write anything
79  * in this field : most ucode_boot discard the value and just use 0xf7f anyway.
80  *
81  * Using ucode_boot_size should be more robust in this regard.
82  **/
83 static int is_task()
84 {
85     return (get_task()->ucode_boot_size <= 0x1000);
86 }
87
88 static void rsp_break(unsigned int setbits)
89 {
90     *rsp.SP_STATUS_REG |= setbits | RSP_STATUS_BROKE | RSP_STATUS_HALT;
91
92     if ((*rsp.SP_STATUS_REG & RSP_STATUS_INTR_ON_BREAK))
93     {
94         *rsp.MI_INTR_REG |= MI_INTR_SP;
95         rsp.CheckInterrupts();
96     }
97 }
98
99 static void forward_gfx_task()
100 {
101     if (rsp.ProcessDlistList != NULL)
102     {
103         rsp.ProcessDlistList();
104         *rsp.DPC_STATUS_REG &= ~DP_STATUS_FREEZE;
105     }
106 }
107
108 static void forward_audio_task()
109 {
110     if (rsp.ProcessAlistList != NULL)
111     {
112         rsp.ProcessAlistList();
113     }
114 }
115
116 static void show_cfb()
117 {
118     if (rsp.ShowCFB != NULL)
119     {
120         rsp.ShowCFB();
121     }
122 }
123
124 static int try_fast_audio_dispatching()
125 {
126     /* identify audio ucode by using the content of ucode_data */
127     const OSTask_t * const task = get_task();
128     const unsigned char * const udata_ptr = rsp.RDRAM + task->ucode_data;
129
130     if (*(unsigned int*)(udata_ptr + 0) == 0x00000001)
131     {
132         if (*(unsigned int*)(udata_ptr + 0x30) == 0xf0000f00)
133         {
134             /**
135             * Many games including:
136             * Super Mario 64, Diddy Kong Racing, BlastCorp, GoldenEye, ... (most common)
137             **/
138             alist_process_ABI1(); return 1;
139         }
140         else
141         {
142             /**
143             * Mario Kart / Wave Race,
144             * LylatWars,
145             * FZeroX,
146             * Yoshi Story,
147             * 1080 Snowboarding,
148             * Zelda Ocarina of Time,
149             * Zelda Majoras Mask / Pokemon Stadium 2,
150             * Animal Crossing
151             *
152             * FIXME: in fact, all these games do not share the same ABI.
153             * That's the reason of the workaround in ucode2.cpp with isZeldaABI and isMKABI
154             **/
155             alist_process_ABI2(); return 1;
156         }
157     }
158     else
159     {
160         if (*(unsigned int*)(udata_ptr + 0x10) == 0x00000001)
161         {
162             /**
163              * Musyx ucode found in following games:
164              * RogueSquadron, ResidentEvil2, SnowCrossPolaris, TheWorldIsNotEnough,
165              * RugratsInParis, NBAShowTime, HydroThunder, Tarzan,
166              * GauntletLegend, Rush2049, IndianaJones, BattleForNaboo
167              * TODO: implement ucode
168              **/
169             DebugMessage(M64MSG_WARNING, "MusyX ucode not implemented.");
170             /* return 1; */
171         }
172         else
173         {
174             /**
175              * Many games including:
176              * Pokemon Stadium, Banjo Kazooie, Donkey Kong, Banjo Tooie, Jet Force Gemini,
177              * Mickey SpeedWay USA, Perfect Dark, Conker Bad Fur Day ...
178              **/
179             alist_process_ABI3(); return 1;
180         }
181     }
182     
183     return 0;
184 }
185
186 static int try_fast_task_dispatching()
187 {
188     /* identify task ucode by its type */
189     const OSTask_t * const task = get_task();
190
191     switch (task->type)
192     {
193         case 1: if (FORWARD_GFX) { forward_gfx_task(); return 1; } break;
194
195         case 2:
196             if (FORWARD_AUDIO) { forward_audio_task(); return 1; }
197             else if (try_fast_audio_dispatching()) { return 1; }
198             break;
199
200         case 7: show_cfb(); return 1;
201     }
202
203     return 0;
204 }
205
206 static void normal_task_dispatching()
207 {
208     const OSTask_t * const task = get_task();
209     const unsigned int sum =
210         sum_bytes(rsp.RDRAM + task->ucode, min(task->ucode_size, 0xf80) >> 1);
211
212     switch (sum)
213     {
214         /* StoreVe12: found in Zelda Ocarina of Time [misleading task->type == 4] */
215         case 0x278: /* Nothing to emulate */ return;
216
217         /* GFX: Twintris [misleading task->type == 0] */                                         
218         case 0x212ee:
219             if (FORWARD_GFX) { forward_gfx_task(); return; }
220             break;
221
222         /* JPEG: found in Pokemon Stadium J */ 
223         case 0x2c85a: jpeg_decode_PS0(); return;
224
225         /* JPEG: found in Zelda Ocarina of Time, Pokemon Stadium 1, Pokemon Stadium 2 */
226         case 0x2caa6: jpeg_decode_PS(); return;
227
228         /* JPEG: found in Ogre Battle, Bottom of the 9th */
229         case 0x130de:
230         case 0x278b0:
231             jpeg_decode_OB(); return;
232     }
233
234     handle_unknown_task(sum);
235 }
236
237 static void non_task_dispatching()
238 {
239     const unsigned int sum = sum_bytes(rsp.IMEM, 0x1000 >> 1);
240
241     switch(sum)
242     {
243         /* CIC x105 ucode (used during boot of CIC x105 games) */
244         case 0x9e2: /* CIC 6105 */
245         case 0x9f2: /* CIC 7105 */
246             cicx105_ucode(); return;
247     }
248
249     handle_unknown_non_task(sum);
250 }
251
252 static void handle_unknown_task(unsigned int sum)
253 {
254     char filename[256];
255     const OSTask_t * const task = get_task();
256
257     DebugMessage(M64MSG_WARNING, "unknown OSTask: sum %x PC:%x", sum, *rsp.SP_PC_REG);
258
259     sprintf(&filename[0], "task_%x.log", sum);
260     dump_task(filename, task);
261     
262     // dump ucode_boot
263     sprintf(&filename[0], "ucode_boot_%x.bin", sum);
264     dump_binary(filename, rsp.RDRAM + (task->ucode_boot & 0x7fffff), task->ucode_boot_size);
265
266     // dump ucode
267     if (task->ucode != 0)
268     {
269         sprintf(&filename[0], "ucode_%x.bin", sum);
270         dump_binary(filename, rsp.RDRAM + (task->ucode & 0x7fffff), 0xf80);
271     }
272
273     // dump ucode_data
274     if (task->ucode_data != 0)
275     {
276         sprintf(&filename[0], "ucode_data_%x.bin", sum);
277         dump_binary(filename, rsp.RDRAM + (task->ucode_data & 0x7fffff), task->ucode_data_size);
278     }
279
280     // dump data
281     if (task->data_ptr != 0)
282     {
283         sprintf(&filename[0], "data_%x.bin", sum);
284         dump_binary(filename, rsp.RDRAM + (task->data_ptr & 0x7fffff), task->data_size);
285     }
286 }
287
288 static void handle_unknown_non_task(unsigned int sum)
289 {
290     char filename[256];
291
292     DebugMessage(M64MSG_WARNING, "unknown RSP code: sum: %x PC:%x", sum, *rsp.SP_PC_REG);
293
294     // dump IMEM & DMEM for further analysis
295     sprintf(&filename[0], "imem_%x.bin", sum);
296     dump_binary(filename, rsp.IMEM, 0x1000);
297
298     sprintf(&filename[0], "dmem_%x.bin", sum);
299     dump_binary(filename, rsp.DMEM, 0x1000);
300 }
301
302
303 /* Global functions */
304 void DebugMessage(int level, const char *message, ...)
305 {
306     char msgbuf[1024];
307     va_list args;
308
309     if (l_DebugCallback == NULL)
310         return;
311
312     va_start(args, message);
313     vsprintf(msgbuf, message, args);
314
315     (*l_DebugCallback)(l_DebugCallContext, level, msgbuf);
316
317     va_end(args);
318 }
319
320 /* DLL-exported functions */
321 EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
322                                    void (*DebugCallback)(void *, int, const char *))
323 {
324     if (l_PluginInit)
325         return M64ERR_ALREADY_INIT;
326
327     /* first thing is to set the callback function for debug info */
328     l_DebugCallback = DebugCallback;
329     l_DebugCallContext = Context;
330
331     /* this plugin doesn't use any Core library functions (ex for Configuration), so no need to keep the CoreLibHandle */
332
333     l_PluginInit = 1;
334     return M64ERR_SUCCESS;
335 }
336
337 EXPORT m64p_error CALL PluginShutdown(void)
338 {
339     if (!l_PluginInit)
340         return M64ERR_NOT_INIT;
341
342     /* reset some local variable */
343     l_DebugCallback = NULL;
344     l_DebugCallContext = NULL;
345
346     l_PluginInit = 0;
347     return M64ERR_SUCCESS;
348 }
349
350 EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)
351 {
352     /* set version info */
353     if (PluginType != NULL)
354         *PluginType = M64PLUGIN_RSP;
355
356     if (PluginVersion != NULL)
357         *PluginVersion = RSP_HLE_VERSION;
358
359     if (APIVersion != NULL)
360         *APIVersion = RSP_PLUGIN_API_VERSION;
361     
362     if (PluginNamePtr != NULL)
363         *PluginNamePtr = "Hacktarux/Azimer High-Level Emulation RSP Plugin";
364
365     if (Capabilities != NULL)
366     {
367         *Capabilities = 0;
368     }
369                     
370     return M64ERR_SUCCESS;
371 }
372
373 EXPORT unsigned int CALL DoRspCycles(unsigned int Cycles)
374 {
375     if (is_task())
376     {
377         if (!try_fast_task_dispatching()) { normal_task_dispatching(); }
378         rsp_break(RSP_STATUS_TASKDONE);
379     }
380     else
381     {
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     {
425         // else we write bytes to the file
426         f= fopen(filename, "wb");
427         if (f != NULL) {
428             if (fwrite(bytes, 1, size, f) != size)
429             {
430                 DebugMessage(M64MSG_ERROR, "Writing error on %s", filename);
431             }
432             fclose(f);
433         }
434         else
435         {
436             DebugMessage(M64MSG_ERROR, "Couldn't open %s for writing !", filename);
437         }
438     }
439     else
440     {
441         fclose(f);
442     }
443 }
444
445 static void dump_task(const char * const filename, const OSTask_t * const task)
446 {
447     FILE *f;
448
449     f = fopen(filename, "r");
450     if (f == NULL)
451     {
452         f = fopen(filename, "w");
453         fprintf(f,
454             "type = %d\n"
455             "flags = %d\n"
456             "ucode_boot  = %#08x size  = %#x\n"
457             "ucode       = %#08x size  = %#x\n"
458             "ucode_data  = %#08x size  = %#x\n"
459             "dram_stack  = %#08x size  = %#x\n"
460             "output_buff = %#08x *size = %#x\n"
461             "data        = %#08x size  = %#x\n"
462             "yield_data  = %#08x size  = %#x\n",
463             task->type, task->flags,
464             task->ucode_boot, task->ucode_boot_size,
465             task->ucode, task->ucode_size,
466             task->ucode_data, task->ucode_data_size,
467             task->dram_stack, task->dram_stack_size,
468             task->output_buff, task->output_buff_size,
469             task->data_ptr, task->data_size,
470             task->yield_data_ptr, task->yield_data_size);
471         fclose(f);
472     }
473     else
474     {
475         fclose(f);
476     }
477 }
478