41 | 41 |
|
42 | 42 |
/* local functions */
|
43 | 43 |
|
|
44 |
|
|
45 |
static void dump_binary(char *filename, unsigned char *bytes, unsigned size)
|
|
46 |
{
|
|
47 |
FILE *f;
|
|
48 |
|
|
49 |
// if file already exists, do nothing
|
|
50 |
f = fopen(filename, "r");
|
|
51 |
if (f == NULL)
|
|
52 |
{
|
|
53 |
// else we write bytes to the file
|
|
54 |
f= fopen(filename, "wb");
|
|
55 |
if (f != NULL) {
|
|
56 |
if (fwrite(bytes, 1, size, f) != size)
|
|
57 |
{
|
|
58 |
DebugMessage(M64MSG_ERROR, "Writing error on %s", filename);
|
|
59 |
}
|
|
60 |
fclose(f);
|
|
61 |
}
|
|
62 |
else
|
|
63 |
{
|
|
64 |
DebugMessage(M64MSG_ERROR, "Couldn't open %s for writing !", filename);
|
|
65 |
}
|
|
66 |
}
|
|
67 |
else
|
|
68 |
{
|
|
69 |
fclose(f);
|
|
70 |
}
|
|
71 |
}
|
|
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_run_through_task(OSTask_t* task)
|
|
85 |
{
|
|
86 |
return (task->ucode_boot_size <= 0x1000
|
|
87 |
&& task->ucode_boot_size >= 0);
|
|
88 |
}
|
|
89 |
|
|
90 |
|
44 | 91 |
/**
|
45 | 92 |
* Simulate the effect of setting the TASKDONE bit (aliased to SIG2)
|
46 | 93 |
* and executing a break instruction (setting HALT and BROKE bits).
|
|
56 | 103 |
//
|
57 | 104 |
// 0x203 = TASKDONE | BROKE | HALT
|
58 | 105 |
*rsp.SP_STATUS_REG |= 0x203;
|
|
106 |
|
|
107 |
// if INTERRUPT_ON_BREAK we generate the interrupt
|
|
108 |
if ((*rsp.SP_STATUS_REG & 0x40) != 0 )
|
|
109 |
{
|
|
110 |
*rsp.MI_INTR_REG |= 0x1;
|
|
111 |
rsp.CheckInterrupts();
|
|
112 |
}
|
59 | 113 |
}
|
60 | 114 |
|
61 | 115 |
|
|
108 | 162 |
}
|
109 | 163 |
}
|
110 | 164 |
|
111 | |
// data = (short*)(rsp.RDRAM + task->ucode_data);
|
112 | |
|
113 | 165 |
for (i = 0; i < (task->data_size/4); i += 2)
|
114 | 166 |
{
|
115 | 167 |
inst1 = p_alist[i];
|
|
192 | 244 |
|
193 | 245 |
EXPORT unsigned int CALL DoRspCycles(unsigned int Cycles)
|
194 | 246 |
{
|
195 | |
OSTask_t *task = (OSTask_t*)(rsp.DMEM + 0xFC0);
|
|
247 |
OSTask_t *task = (OSTask_t*)(rsp.DMEM + 0xfc0);
|
|
248 |
int run_through_task = is_run_through_task(task);
|
|
249 |
|
196 | 250 |
unsigned int i, sum=0;
|
197 | 251 |
|
198 | |
if( task->type == 1 && task->data_ptr != 0 && GraphicsHle)
|
199 | |
{
|
200 | |
if (rsp.ProcessDlistList != NULL)
|
201 | |
{
|
202 | |
rsp.ProcessDlistList();
|
203 | |
}
|
204 | |
taskdone();
|
205 | |
if ((*rsp.SP_STATUS_REG & 0x40) != 0 )
|
206 | |
{
|
207 | |
*rsp.MI_INTR_REG |= 0x1;
|
208 | |
rsp.CheckInterrupts();
|
209 | |
}
|
210 | |
|
211 | |
*rsp.DPC_STATUS_REG &= ~0x0002;
|
212 | |
return Cycles;
|
213 | |
}
|
214 | |
else if (task->type == 2 && AudioHle)
|
215 | |
{
|
216 | |
if (rsp.ProcessAlistList != NULL)
|
217 | |
{
|
218 | |
rsp.ProcessAlistList();
|
219 | |
}
|
220 | |
taskdone();
|
221 | |
if ((*rsp.SP_STATUS_REG & 0x40) != 0 )
|
222 | |
{
|
223 | |
*rsp.MI_INTR_REG |= 0x1;
|
224 | |
rsp.CheckInterrupts();
|
225 | |
}
|
226 | |
return Cycles;
|
227 | |
}
|
228 | |
else if (task->type == 7)
|
229 | |
{
|
230 | |
rsp.ShowCFB();
|
231 | |
}
|
232 | |
|
233 | |
taskdone();
|
234 | |
if ((*rsp.SP_STATUS_REG & 0x40) != 0 )
|
235 | |
{
|
236 | |
*rsp.MI_INTR_REG |= 0x1;
|
237 | |
rsp.CheckInterrupts();
|
238 | |
}
|
239 | |
|
240 | |
if (task->ucode_size <= 0x1000)
|
241 | |
for (i=0; i<(task->ucode_size/2); i++)
|
|
252 |
char filename[256];
|
|
253 |
|
|
254 |
if (run_through_task)
|
|
255 |
{
|
|
256 |
// most ucode_boot procedure copy 0xf80 bytes of ucode whatever the ucode_size is.
|
|
257 |
// For practical purpose we use a ucode_size = min(0xf80, task->ucode_size)
|
|
258 |
unsigned int ucode_size = (task->ucode_size > 0xf80) ? 0xf80 : task->ucode_size;
|
|
259 |
|
|
260 |
for (i=0; i<ucode_size/2; i++)
|
242 | 261 |
sum += *(rsp.RDRAM + task->ucode + i);
|
|
262 |
|
|
263 |
switch(task->type)
|
|
264 |
{
|
|
265 |
case 1: // GFX
|
|
266 |
{
|
|
267 |
if (GraphicsHle && rsp.ProcessDlistList != NULL)
|
|
268 |
{
|
|
269 |
rsp.ProcessDlistList();
|
|
270 |
taskdone();
|
|
271 |
*rsp.DPC_STATUS_REG &= ~0x0002;
|
|
272 |
return Cycles;
|
|
273 |
}
|
|
274 |
else
|
|
275 |
{
|
|
276 |
DebugMessage(M64MSG_WARNING, "GFX ucode through rsp plugin is not implemented");
|
|
277 |
}
|
|
278 |
break;
|
|
279 |
}
|
|
280 |
|
|
281 |
case 2: // AUDIO
|
|
282 |
{
|
|
283 |
if (AudioHle && rsp.ProcessAlistList != NULL)
|
|
284 |
{
|
|
285 |
rsp.ProcessAlistList();
|
|
286 |
taskdone();
|
|
287 |
return Cycles;
|
|
288 |
}
|
|
289 |
else
|
|
290 |
{
|
|
291 |
if (audio_ucode(task) == 0)
|
|
292 |
{
|
|
293 |
taskdone();
|
|
294 |
return Cycles;
|
|
295 |
}
|
|
296 |
}
|
|
297 |
break;
|
|
298 |
}
|
|
299 |
|
|
300 |
case 4: // JPEG
|
|
301 |
{
|
|
302 |
switch(sum)
|
|
303 |
{
|
|
304 |
case 0x278: // Zelda OOT during boot
|
|
305 |
taskdone();
|
|
306 |
return Cycles;
|
|
307 |
case 0x2caa6: // Zelda OOT, Pokemon Stadium {1,2} jpg decompression
|
|
308 |
ps_jpg_uncompress(task);
|
|
309 |
taskdone();
|
|
310 |
return Cycles;
|
|
311 |
case 0x130de: // Ogre Battle background decompression
|
|
312 |
ob_jpg_uncompress(task);
|
|
313 |
taskdone();
|
|
314 |
return Cycles;
|
|
315 |
}
|
|
316 |
break;
|
|
317 |
}
|
|
318 |
|
|
319 |
case 7: // CFB
|
|
320 |
{
|
|
321 |
rsp.ShowCFB();
|
|
322 |
taskdone();
|
|
323 |
return Cycles;
|
|
324 |
break;
|
|
325 |
}
|
|
326 |
}
|
|
327 |
|
|
328 |
DebugMessage(M64MSG_WARNING, "unknown OSTask: sum %x PC:%x", sum, *rsp.SP_PC_REG);
|
|
329 |
|
|
330 |
sprintf(&filename[0], "task_%x.log", sum);
|
|
331 |
|
|
332 |
|
|
333 |
// dump task
|
|
334 |
FILE *f = fopen(filename, "r");
|
|
335 |
if (f == NULL)
|
|
336 |
{
|
|
337 |
f = fopen(filename, "w");
|
|
338 |
fprintf(f,
|
|
339 |
"type = %d\n"
|
|
340 |
"flags = %d\n"
|
|
341 |
"ucode_boot = %#08x size = %#x\n"
|
|
342 |
"ucode = %#08x size = %#x\n"
|
|
343 |
"ucode_data = %#08x size = %#x\n"
|
|
344 |
"dram_stack = %#08x size = %#x\n"
|
|
345 |
"output_buff = %#08x *size = %#x\n"
|
|
346 |
"data = %#08x size = %#x\n"
|
|
347 |
"yield_data = %#08x size = %#x\n",
|
|
348 |
task->type, task->flags,
|
|
349 |
task->ucode_boot, task->ucode_boot_size,
|
|
350 |
task->ucode, task->ucode_size,
|
|
351 |
task->ucode_data, task->ucode_data_size,
|
|
352 |
task->dram_stack, task->dram_stack_size,
|
|
353 |
task->output_buff, task->output_buff_size,
|
|
354 |
task->data_ptr, task->data_size,
|
|
355 |
task->yield_data_ptr, task->yield_data_size);
|
|
356 |
fclose(f);
|
|
357 |
}
|
|
358 |
else
|
|
359 |
{
|
|
360 |
fclose(f);
|
|
361 |
}
|
|
362 |
|
|
363 |
|
|
364 |
// dump ucode_boot
|
|
365 |
sprintf(&filename[0], "ucode_boot_%x.bin", sum);
|
|
366 |
dump_binary(filename, rsp.RDRAM + (task->ucode_boot & 0x7fffff), task->ucode_boot_size);
|
|
367 |
|
|
368 |
// dump ucode
|
|
369 |
if (task->ucode != 0)
|
|
370 |
{
|
|
371 |
sprintf(&filename[0], "ucode_%x.bin", sum);
|
|
372 |
dump_binary(filename, rsp.RDRAM + (task->ucode & 0x7fffff), ucode_size);
|
|
373 |
}
|
|
374 |
|
|
375 |
// dump ucode_data
|
|
376 |
if (task->ucode_data != 0)
|
|
377 |
{
|
|
378 |
sprintf(&filename[0], "ucode_data_%x.bin", sum);
|
|
379 |
dump_binary(filename, rsp.RDRAM + (task->ucode_data & 0x7fffff), task->ucode_data_size);
|
|
380 |
}
|
|
381 |
|
|
382 |
// dump data
|
|
383 |
if (task->data_ptr != 0)
|
|
384 |
{
|
|
385 |
sprintf(&filename[0], "data_%x.bin", sum);
|
|
386 |
dump_binary(filename, rsp.RDRAM + (task->data_ptr & 0x7fffff), task->data_size);
|
|
387 |
}
|
|
388 |
}
|
243 | 389 |
else
|
|
390 |
{
|
|
391 |
// For ucodes that are not run using the osSpTask* functions
|
|
392 |
|
|
393 |
// Try to identify the RSP code we should run
|
244 | 394 |
for (i=0; i<(0x1000/2); i++)
|
245 | 395 |
sum += *(rsp.IMEM + i);
|
246 | 396 |
|
247 | |
|
248 | |
if (task->ucode_size > 0x1000)
|
249 | |
{
|
250 | 397 |
switch(sum)
|
251 | 398 |
{
|
252 | |
case 0x9E2: // banjo tooie (U) boot code
|
|
399 |
// CIC 6105 IPL3 run some code on the RSP
|
|
400 |
// We only emulate the part that modify RDRAM
|
|
401 |
//
|
|
402 |
// It is used for instance in Banjo Tooie, Zelda, Perfect Dark...
|
|
403 |
case 0x9E2: // banjo tooie (U)
|
|
404 |
case 0x9F2: // banjo tooie (E)
|
253 | 405 |
{
|
254 | 406 |
int i,j;
|
255 | |
memcpy(rsp.IMEM + 0x120, rsp.RDRAM + 0x1e8, 0x1e8);
|
|
407 |
memcpy(rsp.IMEM + 0x120, rsp.RDRAM + 0x1e8, 0x1f0);
|
256 | 408 |
for (j=0; j<0xfc; j++)
|
257 | 409 |
for (i=0; i<8; i++)
|
258 | 410 |
*(rsp.RDRAM+((0x2fb1f0+j*0xff0+i)^S8))=*(rsp.IMEM+((0x120+j*8+i)^S8));
|
259 | |
}
|
260 | 411 |
return Cycles;
|
261 | |
case 0x9F2: // banjo tooie (E) + zelda oot (E) boot code
|
262 | |
{
|
263 | |
int i,j;
|
264 | |
memcpy(rsp.IMEM + 0x120, rsp.RDRAM + 0x1e8, 0x1e8);
|
265 | |
for (j=0; j<0xfc; j++)
|
266 | |
for (i=0; i<8; i++)
|
267 | |
*(rsp.RDRAM+((0x2fb1f0+j*0xff0+i)^S8))=*(rsp.IMEM+((0x120+j*8+i)^S8));
|
268 | |
}
|
269 | |
return Cycles;
|
270 | |
}
|
271 | |
}
|
272 | |
else
|
273 | |
{
|
274 | |
switch(task->type)
|
275 | |
{
|
276 | |
case 2: // audio
|
277 | |
if (audio_ucode(task) == 0)
|
278 | |
return Cycles;
|
279 | |
break;
|
280 | |
case 4: // jpeg
|
281 | |
switch(sum)
|
282 | |
{
|
283 | |
case 0x278: // used by zelda during boot
|
284 | |
taskdone();
|
285 | |
return Cycles;
|
286 | |
case 0x2e4fc: // used by pokemon stadium {1,2} for jpg decompression
|
287 | |
ps_jpg_uncompress(task);
|
288 | |
taskdone();
|
289 | |
return Cycles;
|
290 | |
case 0x130de: // used by ogre battle for background decompression
|
291 | |
ob_jpg_uncompress(task);
|
292 | |
taskdone();
|
293 | |
return Cycles;
|
294 | |
default:
|
295 | |
DebugMessage(M64MSG_WARNING, "unknown jpeg task: sum:%x", sum);
|
296 | |
}
|
297 | |
break;
|
298 | |
}
|
299 | |
}
|
300 | |
|
301 | |
{
|
302 | |
FILE *f;
|
303 | |
DebugMessage(M64MSG_WARNING, "unknown task: type:%d sum:%x PC:%lx", (int)task->type, sum, (unsigned long) rsp.SP_PC_REG);
|
304 | |
|
305 | |
if (task->ucode_size <= 0x1000)
|
306 | |
{
|
307 | |
f = fopen("imem.dat", "wb");
|
308 | |
if (f == NULL || fwrite(rsp.RDRAM + task->ucode, 1, task->ucode_size, f) != task->ucode_size)
|
309 | |
DebugMessage(M64MSG_WARNING, "couldn't write to RSP debugging file imem.dat");
|
310 | |
fclose(f);
|
311 | |
|
312 | |
f = fopen("dmem.dat", "wb");
|
313 | |
if (f == NULL || fwrite(rsp.RDRAM + task->ucode_data, 1, task->ucode_data_size, f) != task->ucode_data_size)
|
314 | |
DebugMessage(M64MSG_WARNING, "couldn't write to RSP debugging file dmem.dat");
|
315 | |
fclose(f);
|
316 | |
}
|
317 | |
else
|
318 | |
{
|
319 | |
f = fopen("imem.dat", "wb");
|
320 | |
if (f == NULL || fwrite(rsp.IMEM, 1, 0x1000, f) != 0x1000)
|
321 | |
DebugMessage(M64MSG_WARNING, "couldn't write to RSP debugging file imem.dat");
|
322 | |
fclose(f);
|
323 | |
|
324 | |
f = fopen("dmem.dat", "wb");
|
325 | |
if (f == NULL || fwrite(rsp.DMEM, 1, 0x1000, f) != 0x1000)
|
326 | |
DebugMessage(M64MSG_WARNING, "couldn't write to RSP debugging file dmem.dat");
|
327 | |
fclose(f);
|
328 | |
}
|
|
412 |
}
|
|
413 |
}
|
|
414 |
|
|
415 |
DebugMessage(M64MSG_WARNING, "unknown RSP code: sum: %x PC:%x", sum, *rsp.SP_PC_REG);
|
|
416 |
|
|
417 |
// dump IMEM & DMEM for further analysis
|
|
418 |
sprintf(&filename[0], "imem_%x.bin", sum);
|
|
419 |
dump_binary(filename, rsp.IMEM, 0x1000);
|
|
420 |
|
|
421 |
sprintf(&filename[0], "dmem_%x.bin", sum);
|
|
422 |
dump_binary(filename, rsp.DMEM, 0x1000);
|
329 | 423 |
}
|
330 | 424 |
|
331 | 425 |
return Cycles;
|