ALL: Huge upstream synch + PerRom DelaySI & CountPerOp parameters
[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"
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 */
53static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size);
54static void dump_binary(const char * const filename, const unsigned char * const bytes,
55 unsigned int size);
56static void dump_task(const char * const filename, const OSTask_t * const task);
57
58static void handle_unknown_task(unsigned int sum);
59static void handle_unknown_non_task(unsigned int sum);
60
61/* global variables */
62RSP_INFO rsp;
63
64/* local variables */
65static const int FORWARD_AUDIO = 0, FORWARD_GFX = 1;
66static void (*l_DebugCallback)(void *, int, const char *) = NULL;
67static void *l_DebugCallContext = NULL;
68static 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 **/
83static int is_task()
84{
85 return (get_task()->ucode_boot_size <= 0x1000);
86}
87
88static 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
99static 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
108static void forward_audio_task()
109{
110 if (rsp.ProcessAlistList != NULL)
111 {
112 rsp.ProcessAlistList();
113 }
114}
115
116static void show_cfb()
117{
118 if (rsp.ShowCFB != NULL)
119 {
120 rsp.ShowCFB();
121 }
122}
123
124static 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
186static 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
206static 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 */
2d262872 229 case 0x130de:
230 case 0x278b0:
231 jpeg_decode_OB(); return;
d9e74a6f 232 }
233
234 handle_unknown_task(sum);
235}
236
237static 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
252static 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
288static 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 */
304void 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 */
321EXPORT 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
337EXPORT 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
350EXPORT 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
373EXPORT 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
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;
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
416static 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
445static 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