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