1.10 release
[libpicofe.git] / gp2x / 940ctl_ym2612.c
CommitLineData
720ee7f6 1#include <stdio.h>\r
2#include <stdlib.h>\r
3#include <string.h>\r
4#include <unistd.h>\r
5#include <sys/mman.h>\r
6#include <sys/ioctl.h>\r
7#include <fcntl.h>\r
8#include <errno.h>\r
9\r
b30a8e67 10#include "code940/940shared.h"\r
720ee7f6 11#include "gp2x.h"\r
12#include "emu.h"\r
13#include "menu.h"\r
14#include "asmutils.h"\r
598e7c06 15#include "../../Pico/PicoInt.h"\r
720ee7f6 16\r
17/* we will need some gp2x internals here */\r
18extern volatile unsigned short *gp2x_memregs; /* from minimal library rlyeh */\r
19extern volatile unsigned long *gp2x_memregl;\r
20\r
21static unsigned char *shared_mem = 0;\r
22static _940_data_t *shared_data = 0;\r
23static _940_ctl_t *shared_ctl = 0;\r
598e7c06 24static unsigned char *mp3_mem = 0;\r
25\r
26#define MP3_SIZE_MAX (0x1000000 - 4*640*480)\r
720ee7f6 27\r
28int crashed_940 = 0;\r
29\r
30\r
31/***********************************************************/\r
32\r
33#define MAXOUT (+32767)\r
34#define MINOUT (-32768)\r
35\r
36/* limitter */\r
37#define Limit(val, max,min) { \\r
38 if ( val > max ) val = max; \\r
39 else if ( val < min ) val = min; \\r
40}\r
41\r
42/* these will be managed locally on our side */\r
43extern int *ym2612_dacen;\r
44extern INT32 *ym2612_dacout;\r
45extern void *ym2612_regs;\r
46\r
47static UINT8 *REGS = 0; /* we will also keep local copy of regs for savestates and such */\r
48static INT32 addr_A1; /* address line A1 */\r
49static int dacen;\r
50static INT32 dacout;\r
51static UINT8 ST_address; /* address register */\r
52static UINT8 ST_status; /* status flag */\r
53static UINT8 ST_mode; /* mode CSM / 3SLOT */\r
54static int ST_TA; /* timer a */\r
55static int ST_TAC; /* timer a maxval */\r
56static int ST_TAT; /* timer a ticker */\r
57static UINT8 ST_TB; /* timer b */\r
58static int ST_TBC; /* timer b maxval */\r
59static int ST_TBT; /* timer b ticker */\r
60\r
61static int writebuff_ptr = 0;\r
62\r
63\r
64/* OPN Mode Register Write */\r
65static void set_timers( int v )\r
66{\r
67 /* b7 = CSM MODE */\r
68 /* b6 = 3 slot mode */\r
69 /* b5 = reset b */\r
70 /* b4 = reset a */\r
71 /* b3 = timer enable b */\r
72 /* b2 = timer enable a */\r
73 /* b1 = load b */\r
74 /* b0 = load a */\r
75 ST_mode = v;\r
76\r
77 /* reset Timer b flag */\r
78 if( v & 0x20 )\r
79 ST_status &= ~2;\r
80\r
81 /* reset Timer a flag */\r
82 if( v & 0x10 )\r
83 ST_status &= ~1;\r
84}\r
85\r
86/* YM2612 write */\r
87/* a = address */\r
88/* v = value */\r
89/* returns 1 if sample affecting state changed */\r
90int YM2612Write_940(unsigned int a, unsigned int v)\r
91{\r
92 int addr; //, ret=1;\r
93\r
94 v &= 0xff; /* adjust to 8 bit bus */\r
95 a &= 3;\r
96\r
97 switch( a ) {\r
98 case 0: /* address port 0 */\r
99 ST_address = v;\r
100 addr_A1 = 0;\r
101 //ret=0;\r
102 break;\r
103\r
104 case 1: /* data port 0 */\r
105 if (addr_A1 != 0) {\r
106 return 0; /* verified on real YM2608 */\r
107 }\r
108\r
109 addr = ST_address;\r
110 REGS[addr] = v;\r
111\r
112 switch( addr & 0xf0 )\r
113 {\r
114 case 0x20: /* 0x20-0x2f Mode */\r
115 switch( addr )\r
116 {\r
117 case 0x24: { // timer A High 8\r
118 int TAnew = (ST_TA & 0x03)|(((int)v)<<2);\r
119 if(ST_TA != TAnew) {\r
120 // we should reset ticker only if new value is written. Outrun requires this.\r
121 ST_TA = TAnew;\r
122 ST_TAC = (1024-TAnew)*18;\r
123 ST_TAT = 0;\r
124 }\r
125 return 0;\r
126 }\r
127 case 0x25: { // timer A Low 2\r
128 int TAnew = (ST_TA & 0x3fc)|(v&3);\r
129 if(ST_TA != TAnew) {\r
130 ST_TA = TAnew;\r
131 ST_TAC = (1024-TAnew)*18;\r
132 ST_TAT = 0;\r
133 }\r
134 return 0;\r
135 }\r
136 case 0x26: // timer B\r
137 if(ST_TB != v) {\r
138 ST_TB = v;\r
139 ST_TBC = (256-v)<<4;\r
140 ST_TBC *= 18;\r
141 ST_TBT = 0;\r
142 }\r
143 return 0;\r
144 case 0x27: /* mode, timer control */\r
145 set_timers( v );\r
146 break; // other side needs ST.mode for 3slot mode\r
147 case 0x2a: /* DAC data (YM2612) */\r
148 dacout = ((int)v - 0x80) << 6; /* level unknown (notaz: 8 seems to be too much) */\r
149 return 0;\r
150 case 0x2b: /* DAC Sel (YM2612) */\r
151 /* b7 = dac enable */\r
152 dacen = v & 0x80;\r
153 break; // other side has to know this\r
154 default:\r
155 break;\r
156 }\r
157 break;\r
158 }\r
159 break;\r
160\r
161 case 2: /* address port 1 */\r
162 ST_address = v;\r
163 addr_A1 = 1;\r
164 //ret=0;\r
165 break;\r
166\r
167 case 3: /* data port 1 */\r
168 if (addr_A1 != 1) {\r
169 return 0; /* verified on real YM2608 */\r
170 }\r
171\r
172 addr = ST_address | 0x100;\r
173 REGS[addr] = v;\r
174 break;\r
175 }\r
176\r
177 if(currentConfig.EmuOpt & 4) {\r
178 /* queue this write for 940 */\r
179 if (writebuff_ptr < 2047) {\r
180 if (shared_ctl->writebuffsel == 1) {\r
181 shared_ctl->writebuff0[writebuff_ptr++] = (a<<8)|v;\r
182 } else {\r
183 shared_ctl->writebuff1[writebuff_ptr++] = (a<<8)|v;\r
184 }\r
185 } else {\r
186 printf("warning: writebuff_ptr > 2047\n");\r
187 }\r
188 }\r
189\r
190 return 0; // cause the engine to do updates once per frame only\r
191}\r
192\r
193UINT8 YM2612Read_940(void)\r
194{\r
195 return ST_status;\r
196}\r
197\r
198\r
199int YM2612PicoTick_940(int n)\r
200{\r
201 //int ret = 0;\r
202\r
203 // timer A\r
204 if(ST_mode & 0x01 && (ST_TAT+=64*n) >= ST_TAC) {\r
205 ST_TAT -= ST_TAC;\r
206 if(ST_mode & 0x04) ST_status |= 1;\r
207 // CSM mode total level latch and auto key on\r
208/* FIXME\r
209 if(ST_mode & 0x80) {\r
210 CSMKeyControll( &(ym2612_940->CH[2]) ); // Vectorman2, etc.\r
211 ret = 1;\r
212 }\r
213*/\r
214 }\r
215\r
216 // timer B\r
217 if(ST_mode & 0x02 && (ST_TBT+=64*n) >= ST_TBC) {\r
218 ST_TBT -= ST_TBC;\r
219 if(ST_mode & 0x08) ST_status |= 2;\r
220 }\r
221\r
222 return 0;\r
223}\r
224\r
225\r
226static void wait_busy_940(void)\r
227{\r
228 int i;\r
229#if 0\r
230 printf("940 busy, entering wait loop.. (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);\r
231 for (i = 0; i < 8; i++)\r
232 printf("%i ", shared_ctl->vstarts[i]);\r
233 printf(")\n");\r
234\r
235 for (i = 0; shared_ctl->busy; i++)\r
236 {\r
237 spend_cycles(1024); /* needs tuning */\r
238 }\r
239 printf("wait iterations: %i\n", i);\r
240#else\r
241 for (i = 0; shared_ctl->busy && i < 0x10000; i++)\r
b30a8e67 242 spend_cycles(8*1024); // tested to be best for mp3 dec\r
720ee7f6 243 if (i < 0x10000) return;\r
244\r
245 /* 940 crashed */\r
b30a8e67 246 printf("940 crashed (cnt: %i, ve: ", shared_ctl->loopc);\r
720ee7f6 247 for (i = 0; i < 8; i++)\r
248 printf("%i ", shared_ctl->vstarts[i]);\r
249 printf(")\n");\r
250 strcpy(menuErrorMsg, "940 crashed.");\r
251 engineState = PGS_Menu;\r
252 crashed_940 = 1;\r
253#endif\r
254}\r
255\r
256\r
8dfb9fd5 257static void add_job_940(int job0, int job1)\r
720ee7f6 258{\r
8dfb9fd5 259 shared_ctl->jobs[0] = job0;\r
260 shared_ctl->jobs[1] = job1;\r
720ee7f6 261 shared_ctl->busy = 1;\r
262 gp2x_memregs[0x3B3E>>1] = 0xffff; // cause an IRQ for 940\r
263}\r
264\r
265\r
266void YM2612PicoStateLoad_940(void)\r
267{\r
268 int i, old_A1 = addr_A1;\r
269\r
270 if (shared_ctl->busy) wait_busy_940();\r
271\r
272 // feed all the registers and update internal state\r
273 for(i = 0; i < 0x100; i++) {\r
274 YM2612Write_940(0, i);\r
275 YM2612Write_940(1, REGS[i]);\r
276 }\r
277 for(i = 0; i < 0x100; i++) {\r
278 YM2612Write_940(2, i);\r
279 YM2612Write_940(3, REGS[i|0x100]);\r
280 }\r
281\r
282 addr_A1 = old_A1;\r
283\r
8dfb9fd5 284 add_job_940(JOB940_PICOSTATELOAD, 0);\r
720ee7f6 285}\r
286\r
287\r
288static void internal_reset(void)\r
289{\r
290 writebuff_ptr = 0;\r
291 ST_mode = 0;\r
292 ST_status = 0; /* normal mode */\r
293 ST_TA = 0;\r
294 ST_TAC = 0;\r
295 ST_TB = 0;\r
296 ST_TBC = 0;\r
297 dacen = 0;\r
298}\r
299\r
300\r
301extern char **g_argv;\r
302\r
303/* none of the functions in this file should be called before this one */\r
304void YM2612Init_940(int baseclock, int rate)\r
305{\r
306 printf("YM2612Init_940()\n");\r
8dfb9fd5 307 printf("Mem usage: shared_data: %i, shared_ctl: %i\n", sizeof(*shared_data), sizeof(*shared_ctl));\r
720ee7f6 308\r
8dfb9fd5 309 Reset940(1, 2);\r
720ee7f6 310 Pause940(1);\r
311\r
312 gp2x_memregs[0x3B46>>1] = 0xffff; // clear pending DUALCPU interrupts for 940\r
313 gp2x_memregs[0x3B42>>1] = 0xffff; // enable DUALCPU interrupts for 940\r
314\r
315 gp2x_memregl[0x4508>>2] = ~(1<<26); // unmask DUALCPU ints in the undocumented 940's interrupt controller\r
316\r
317 if (shared_mem == NULL)\r
318 {\r
8dfb9fd5 319 shared_mem = (unsigned char *) mmap(0, 0x210000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0x2000000);\r
720ee7f6 320 if(shared_mem == MAP_FAILED)\r
321 {\r
322 printf("mmap(shared_data) failed with %i\n", errno);\r
323 exit(1);\r
324 }\r
325 shared_data = (_940_data_t *) (shared_mem+0x100000);\r
326 /* this area must not get buffered on either side */\r
327 shared_ctl = (_940_ctl_t *) (shared_mem+0x200000);\r
598e7c06 328 mp3_mem = (unsigned char *) mmap(0, MP3_SIZE_MAX, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0x3000000);\r
329 if (mp3_mem == MAP_FAILED)\r
330 {\r
331 printf("mmap(mp3_mem) failed with %i\n", errno);\r
332 exit(1);\r
333 }\r
720ee7f6 334 crashed_940 = 1;\r
335 }\r
336\r
337 if (crashed_940)\r
338 {\r
339 unsigned char ucData[1024];\r
340 int nRead, i, nLen = 0;\r
341 char binpath[1024];\r
342 FILE *fp;\r
343\r
344 strncpy(binpath, g_argv[0], 1023);\r
345 binpath[1023] = 0;\r
346 for (i = strlen(binpath); i > 0; i--)\r
347 if (binpath[i] == '/') { binpath[i] = 0; break; }\r
348 strcat(binpath, "/code940.bin");\r
349\r
350 fp = fopen(binpath, "rb");\r
351 if(!fp)\r
352 {\r
353 memset(gp2x_screen, 0, 320*240);\r
354 gp2x_text_out8(10, 100, "failed to open required file:");\r
355 gp2x_text_out8(10, 110, "code940.bin");\r
356 gp2x_video_flip();\r
357 printf("failed to open %s\n", binpath);\r
358 exit(1);\r
359 }\r
360\r
361 while(1)\r
362 {\r
363 nRead = fread(ucData, 1, 1024, fp);\r
364 if(nRead <= 0)\r
365 break;\r
366 memcpy(shared_mem + nLen, ucData, nRead);\r
367 nLen += nRead;\r
368 }\r
369 fclose(fp);\r
370 crashed_940 = 0;\r
371 }\r
372\r
373 memset(shared_data, 0, sizeof(*shared_data));\r
374 memset(shared_ctl, 0, sizeof(*shared_ctl));\r
375\r
376 REGS = YM2612GetRegs();\r
377\r
378 ym2612_dacen = &dacen;\r
379 ym2612_dacout = &dacout;\r
380\r
381 internal_reset();\r
382\r
383 /* now cause 940 to init it's ym2612 stuff */\r
384 shared_ctl->baseclock = baseclock;\r
385 shared_ctl->rate = rate;\r
b30a8e67 386 shared_ctl->jobs[0] = JOB940_INITALL;\r
8dfb9fd5 387 shared_ctl->jobs[1] = 0;\r
720ee7f6 388 shared_ctl->busy = 1;\r
389\r
390 /* start the 940 */\r
8dfb9fd5 391 Reset940(0, 2);\r
720ee7f6 392 Pause940(0);\r
393\r
394 // YM2612ResetChip_940(); // will be done on JOB940_YM2612INIT\r
395}\r
396\r
397\r
398void YM2612ResetChip_940(void)\r
399{\r
400 printf("YM2612ResetChip_940()\n");\r
401 if (shared_data == NULL) {\r
402 printf("YM2612ResetChip_940: reset before init?\n");\r
403 return;\r
404 }\r
405\r
406 if (shared_ctl->busy) wait_busy_940();\r
407\r
408 internal_reset();\r
409\r
8dfb9fd5 410 add_job_940(JOB940_YM2612RESETCHIP, 0);\r
720ee7f6 411}\r
412\r
413\r
598e7c06 414static void mix_samples(short *dest_buf, int *ym_buf, short *mp3_buf, int len, int stereo)\r
415{\r
416 if (mp3_buf)\r
417 {\r
418 if (stereo)\r
419 {\r
420 for (; len > 0; len--)\r
421 {\r
422 int l, r, lm, rm;\r
423 l = r = *dest_buf;\r
424 l += *ym_buf++; r += *ym_buf++;\r
425 lm = *mp3_buf++; rm = *mp3_buf++;\r
426 l += lm - lm/2; r += rm - rm/2;\r
427 Limit( l, MAXOUT, MINOUT );\r
428 Limit( r, MAXOUT, MINOUT );\r
429 *dest_buf++ = l; *dest_buf++ = r;\r
430 }\r
431 } else {\r
432 for (; len > 0; len--)\r
433 {\r
434 // TODO: normalize\r
435 int l = *ym_buf++;\r
436 l += *dest_buf;\r
437 l += *mp3_buf++;\r
438 Limit( l, MAXOUT, MINOUT );\r
439 *dest_buf++ = l;\r
440 }\r
441 }\r
442 }\r
443 else\r
444 {\r
445 if (stereo)\r
446 {\r
447 for (; len > 0; len--)\r
448 {\r
449 int l, r;\r
450 l = r = *dest_buf;\r
451 l += *ym_buf++, r += *ym_buf++;\r
452 Limit( l, MAXOUT, MINOUT );\r
453 Limit( r, MAXOUT, MINOUT );\r
454 *dest_buf++ = l; *dest_buf++ = r;\r
455 }\r
456 } else {\r
457 for (; len > 0; len--)\r
458 {\r
459 int l = *ym_buf++;\r
460 l += *dest_buf;\r
461 Limit( l, MAXOUT, MINOUT );\r
462 *dest_buf++ = l;\r
463 }\r
464 }\r
465 }\r
466}\r
467\r
468\r
469// here we assume that length is different between games, but constant in one game\r
470\r
471static FILE *loaded_mp3 = 0;\r
472\r
720ee7f6 473void YM2612UpdateOne_940(short *buffer, int length, int stereo)\r
474{\r
598e7c06 475 int cdda_on, *ym_buffer = shared_data->mix_buffer, mp3_job = 0;\r
476 static int mp3_samples_ready = 0, mp3_buffer_offs = 0;\r
477 static int mp3_play_bufsel = 1;\r
720ee7f6 478\r
479 //printf("YM2612UpdateOne_940()\n");\r
480 if (shared_ctl->busy) wait_busy_940();\r
481\r
482 //printf("940 (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);\r
483 //for (i = 0; i < 8; i++)\r
484 // printf("%i ", shared_ctl->vstarts[i]);\r
485 //printf(")\n");\r
486\r
598e7c06 487 // emulatind MCD, cdda enabled in config, not data track, CDC is reading, playback was started, track not ended\r
488 cdda_on = (PicoMCD & 1) && (currentConfig.EmuOpt&0x800) && !(Pico_mcd->s68k_regs[0x36] & 1) &&\r
489 (Pico_mcd->scd.Status_CDC & 1) && loaded_mp3 && shared_ctl->mp3_offs < shared_ctl->mp3_len;\r
490\r
720ee7f6 491 /* mix data from previous go */\r
598e7c06 492 if (cdda_on && mp3_samples_ready >= length)\r
493 {\r
494 if (1152 - mp3_buffer_offs >= length) {\r
495 mix_samples(buffer, ym_buffer, shared_data->mp3_buffer[mp3_play_bufsel] + mp3_buffer_offs*2, length, stereo);\r
496\r
497 mp3_buffer_offs += length;\r
498 } else {\r
499 // collect from both buffers..\r
500 int left = 1152 - mp3_buffer_offs;\r
501 mix_samples(buffer, ym_buffer, shared_data->mp3_buffer[mp3_play_bufsel] + mp3_buffer_offs*2, left, stereo);\r
502 mp3_play_bufsel ^= 1;\r
503 mp3_buffer_offs = length - left;\r
504 mix_samples(buffer + left * 2, ym_buffer + left * 2,\r
505 shared_data->mp3_buffer[mp3_play_bufsel], mp3_buffer_offs, stereo);\r
720ee7f6 506 }\r
598e7c06 507 mp3_samples_ready -= length;\r
720ee7f6 508 } else {\r
598e7c06 509 mix_samples(buffer, ym_buffer, 0, length, stereo);\r
720ee7f6 510 }\r
511\r
512 //printf("new writes: %i\n", writebuff_ptr);\r
513 if (shared_ctl->writebuffsel == 1) {\r
514 shared_ctl->writebuff0[writebuff_ptr] = 0xffff;\r
515 } else {\r
516 shared_ctl->writebuff1[writebuff_ptr] = 0xffff;\r
517 }\r
518 writebuff_ptr = 0;\r
519\r
598e7c06 520 /* give 940 ym job */\r
720ee7f6 521 shared_ctl->writebuffsel ^= 1;\r
522 shared_ctl->length = length;\r
523 shared_ctl->stereo = stereo;\r
598e7c06 524\r
525 // make sure we will have enough mp3 samples next frame\r
526 if (cdda_on && mp3_samples_ready < length)\r
527 {\r
528 shared_ctl->mp3_buffsel ^= 1;\r
529 mp3_job = JOB940_MP3DECODE;\r
530 mp3_samples_ready += 1152;\r
531 }\r
532\r
533 add_job_940(JOB940_YM2612UPDATEONE, mp3_job);\r
720ee7f6 534 //spend_cycles(512);\r
535 //printf("SRCPND: %08lx, INTMODE: %08lx, INTMASK: %08lx, INTPEND: %08lx\n",\r
536 // gp2x_memregl[0x4500>>2], gp2x_memregl[0x4504>>2], gp2x_memregl[0x4508>>2], gp2x_memregl[0x4510>>2]);\r
537}\r
598e7c06 538\r
539\r
540/***********************************************************/\r
541\r
542void mp3_start_play(FILE *f, int pos) // pos is 0-1023\r
543{\r
544 int byte_offs = 0;\r
545\r
546 if (!(currentConfig.EmuOpt&0x800)) { // cdda disabled?\r
547 return;\r
548 }\r
549\r
550 if (loaded_mp3 != f)\r
551 {\r
552 printf("loading mp3... "); fflush(stdout);\r
553 fseek(f, 0, SEEK_SET);\r
554 fread(mp3_mem, 1, MP3_SIZE_MAX, f);\r
555 if (feof(f)) printf("done.\n");\r
556 else printf("done. mp3 too large, not all data loaded.\n");\r
557 shared_ctl->mp3_len = ftell(f);\r
558 loaded_mp3 = f;\r
559 }\r
560\r
561 // seek..\r
562 if (pos) {\r
563 byte_offs = (shared_ctl->mp3_len << 6) >> 10;\r
564 byte_offs *= pos;\r
565 byte_offs >>= 6;\r
566 }\r
567 printf("mp3 pos1024: %i, byte_offs %i/%i\n", pos, byte_offs, shared_ctl->mp3_len);\r
568\r
569 shared_ctl->mp3_offs = byte_offs;\r
570}\r
571\r
572\r