initial import
[picodrive.git] / platform / gp2x / 940ctl_ym2612.c
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
10 #include "940shared.h"\r
11 #include "gp2x.h"\r
12 #include "emu.h"\r
13 #include "menu.h"\r
14 #include "asmutils.h"\r
15 \r
16 /* we will need some gp2x internals here */\r
17 extern volatile unsigned short *gp2x_memregs; /* from minimal library rlyeh */\r
18 extern volatile unsigned long  *gp2x_memregl;\r
19 \r
20 static unsigned char *shared_mem = 0;\r
21 static _940_data_t *shared_data = 0;\r
22 static _940_ctl_t *shared_ctl = 0;\r
23 \r
24 int crashed_940 = 0;\r
25 \r
26 \r
27 /***********************************************************/\r
28 \r
29 #define MAXOUT          (+32767)\r
30 #define MINOUT          (-32768)\r
31 \r
32 /* limitter */\r
33 #define Limit(val, max,min) { \\r
34         if ( val > max )      val = max; \\r
35         else if ( val < min ) val = min; \\r
36 }\r
37 \r
38 /* these will be managed locally on our side */\r
39 extern int   *ym2612_dacen;\r
40 extern INT32 *ym2612_dacout;\r
41 extern void  *ym2612_regs;\r
42 \r
43 static UINT8 *REGS = 0;         /* we will also keep local copy of regs for savestates and such */\r
44 static INT32 addr_A1;           /* address line A1      */\r
45 static int       dacen;\r
46 static INT32 dacout;\r
47 static UINT8 ST_address;        /* address register     */\r
48 static UINT8 ST_status;         /* status flag          */\r
49 static UINT8 ST_mode;           /* mode  CSM / 3SLOT    */\r
50 static int   ST_TA;                     /* timer a              */\r
51 static int   ST_TAC;            /* timer a maxval       */\r
52 static int   ST_TAT;            /* timer a ticker       */\r
53 static UINT8 ST_TB;                     /* timer b              */\r
54 static int   ST_TBC;            /* timer b maxval       */\r
55 static int   ST_TBT;            /* timer b ticker       */\r
56 \r
57 static int   writebuff_ptr = 0;\r
58 \r
59 \r
60 /* OPN Mode Register Write */\r
61 static void set_timers( int v )\r
62 {\r
63         /* b7 = CSM MODE */\r
64         /* b6 = 3 slot mode */\r
65         /* b5 = reset b */\r
66         /* b4 = reset a */\r
67         /* b3 = timer enable b */\r
68         /* b2 = timer enable a */\r
69         /* b1 = load b */\r
70         /* b0 = load a */\r
71         ST_mode = v;\r
72 \r
73         /* reset Timer b flag */\r
74         if( v & 0x20 )\r
75                 ST_status &= ~2;\r
76 \r
77         /* reset Timer a flag */\r
78         if( v & 0x10 )\r
79                 ST_status &= ~1;\r
80 }\r
81 \r
82 /* YM2612 write */\r
83 /* a = address */\r
84 /* v = value   */\r
85 /* returns 1 if sample affecting state changed */\r
86 int YM2612Write_940(unsigned int a, unsigned int v)\r
87 {\r
88         int addr; //, ret=1;\r
89 \r
90         v &= 0xff;      /* adjust to 8 bit bus */\r
91         a &= 3;\r
92 \r
93         switch( a ) {\r
94         case 0: /* address port 0 */\r
95                 ST_address = v;\r
96                 addr_A1 = 0;\r
97                 //ret=0;\r
98                 break;\r
99 \r
100         case 1: /* data port 0    */\r
101                 if (addr_A1 != 0) {\r
102                         return 0;       /* verified on real YM2608 */\r
103                 }\r
104 \r
105                 addr = ST_address;\r
106                 REGS[addr] = v;\r
107 \r
108                 switch( addr & 0xf0 )\r
109                 {\r
110                 case 0x20:      /* 0x20-0x2f Mode */\r
111                         switch( addr )\r
112                         {\r
113                         case 0x24: { // timer A High 8\r
114                                         int TAnew = (ST_TA & 0x03)|(((int)v)<<2);\r
115                                         if(ST_TA != TAnew) {\r
116                                                 // we should reset ticker only if new value is written. Outrun requires this.\r
117                                                 ST_TA = TAnew;\r
118                                                 ST_TAC = (1024-TAnew)*18;\r
119                                                 ST_TAT = 0;\r
120                                         }\r
121                                         return 0;\r
122                                 }\r
123                         case 0x25: { // timer A Low 2\r
124                                         int TAnew = (ST_TA & 0x3fc)|(v&3);\r
125                                         if(ST_TA != TAnew) {\r
126                                                 ST_TA = TAnew;\r
127                                                 ST_TAC = (1024-TAnew)*18;\r
128                                                 ST_TAT = 0;\r
129                                         }\r
130                                         return 0;\r
131                                 }\r
132                         case 0x26: // timer B\r
133                                 if(ST_TB != v) {\r
134                                         ST_TB = v;\r
135                                         ST_TBC  = (256-v)<<4;\r
136                                         ST_TBC *= 18;\r
137                                         ST_TBT  = 0;\r
138                                 }\r
139                                 return 0;\r
140                         case 0x27:      /* mode, timer control */\r
141                                 set_timers( v );\r
142                                 break; // other side needs ST.mode for 3slot mode\r
143                         case 0x2a:      /* DAC data (YM2612) */\r
144                                 dacout = ((int)v - 0x80) << 6;  /* level unknown (notaz: 8 seems to be too much) */\r
145                                 return 0;\r
146                         case 0x2b:      /* DAC Sel  (YM2612) */\r
147                                 /* b7 = dac enable */\r
148                                 dacen = v & 0x80;\r
149                                 break; // other side has to know this\r
150                         default:\r
151                                 break;\r
152                         }\r
153                         break;\r
154                 }\r
155                 break;\r
156 \r
157         case 2: /* address port 1 */\r
158                 ST_address = v;\r
159                 addr_A1 = 1;\r
160                 //ret=0;\r
161                 break;\r
162 \r
163         case 3: /* data port 1    */\r
164                 if (addr_A1 != 1) {\r
165                         return 0;       /* verified on real YM2608 */\r
166                 }\r
167 \r
168                 addr = ST_address | 0x100;\r
169                 REGS[addr] = v;\r
170                 break;\r
171         }\r
172 \r
173         if(currentConfig.EmuOpt & 4) {\r
174                 /* queue this write for 940 */\r
175                 if (writebuff_ptr < 2047) {\r
176                         if (shared_ctl->writebuffsel == 1) {\r
177                                 shared_ctl->writebuff0[writebuff_ptr++] = (a<<8)|v;\r
178                         } else {\r
179                                 shared_ctl->writebuff1[writebuff_ptr++] = (a<<8)|v;\r
180                         }\r
181                 } else {\r
182                         printf("warning: writebuff_ptr > 2047\n");\r
183                 }\r
184         }\r
185 \r
186         return 0; // cause the engine to do updates once per frame only\r
187 }\r
188 \r
189 UINT8 YM2612Read_940(void)\r
190 {\r
191         return ST_status;\r
192 }\r
193 \r
194 \r
195 int YM2612PicoTick_940(int n)\r
196 {\r
197         //int ret = 0;\r
198 \r
199         // timer A\r
200         if(ST_mode & 0x01 && (ST_TAT+=64*n) >= ST_TAC) {\r
201                 ST_TAT -= ST_TAC;\r
202                 if(ST_mode & 0x04) ST_status |= 1;\r
203                 // CSM mode total level latch and auto key on\r
204 /*              FIXME\r
205                 if(ST_mode & 0x80) {\r
206                         CSMKeyControll( &(ym2612_940->CH[2]) ); // Vectorman2, etc.\r
207                         ret = 1;\r
208                 }\r
209 */\r
210         }\r
211 \r
212         // timer B\r
213         if(ST_mode & 0x02 && (ST_TBT+=64*n) >= ST_TBC) {\r
214                 ST_TBT -= ST_TBC;\r
215                 if(ST_mode & 0x08) ST_status |= 2;\r
216         }\r
217 \r
218         return 0;\r
219 }\r
220 \r
221 \r
222 static void wait_busy_940(void)\r
223 {\r
224         int i;\r
225 #if 0\r
226         printf("940 busy, entering wait loop.. (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);\r
227         for (i = 0; i < 8; i++)\r
228                 printf("%i ", shared_ctl->vstarts[i]);\r
229         printf(")\n");\r
230 \r
231         for (i = 0; shared_ctl->busy; i++)\r
232         {\r
233                 spend_cycles(1024); /* needs tuning */\r
234         }\r
235         printf("wait iterations: %i\n", i);\r
236 #else\r
237         for (i = 0; shared_ctl->busy && i < 0x10000; i++)\r
238                 spend_cycles(4*1024);\r
239         if (i < 0x10000) return;\r
240 \r
241         /* 940 crashed */\r
242         printf("940 crashed (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);\r
243         for (i = 0; i < 8; i++)\r
244                 printf("%i ", shared_ctl->vstarts[i]);\r
245         printf(")\n");\r
246         strcpy(menuErrorMsg, "940 crashed.");\r
247         engineState = PGS_Menu;\r
248         crashed_940 = 1;\r
249 #endif\r
250 }\r
251 \r
252 \r
253 static void add_job_940(int job)\r
254 {\r
255         shared_ctl->job = job;\r
256         shared_ctl->busy = 1;\r
257         gp2x_memregs[0x3B3E>>1] = 0xffff; // cause an IRQ for 940\r
258 }\r
259 \r
260 \r
261 void YM2612PicoStateLoad_940(void)\r
262 {\r
263         int i, old_A1 = addr_A1;\r
264 \r
265         if (shared_ctl->busy) wait_busy_940();\r
266 \r
267         // feed all the registers and update internal state\r
268         for(i = 0; i < 0x100; i++) {\r
269                 YM2612Write_940(0, i);\r
270                 YM2612Write_940(1, REGS[i]);\r
271         }\r
272         for(i = 0; i < 0x100; i++) {\r
273                 YM2612Write_940(2, i);\r
274                 YM2612Write_940(3, REGS[i|0x100]);\r
275         }\r
276 \r
277         addr_A1 = old_A1;\r
278 \r
279         add_job_940(JOB940_PICOSTATELOAD);\r
280 }\r
281 \r
282 \r
283 static void internal_reset(void)\r
284 {\r
285         writebuff_ptr = 0;\r
286         ST_mode   = 0;\r
287         ST_status = 0;  /* normal mode */\r
288         ST_TA     = 0;\r
289         ST_TAC    = 0;\r
290         ST_TB     = 0;\r
291         ST_TBC    = 0;\r
292         dacen = 0;\r
293 }\r
294 \r
295 \r
296 extern char **g_argv;\r
297 \r
298 /* none of the functions in this file should be called before this one */\r
299 void YM2612Init_940(int baseclock, int rate)\r
300 {\r
301         printf("YM2612Init_940()\n");\r
302         //printf("sizeof(*shared_data): %i (%x)\n", sizeof(*shared_data), sizeof(*shared_data));\r
303         //printf("sizeof(*shared_ctl): %i (%x)\n", sizeof(*shared_ctl), sizeof(*shared_ctl));\r
304 \r
305         Reset940(1);\r
306         Pause940(1);\r
307 \r
308         gp2x_memregs[0x3B46>>1] = 0xffff; // clear pending DUALCPU interrupts for 940\r
309         gp2x_memregs[0x3B42>>1] = 0xffff; // enable DUALCPU interrupts for 940\r
310 \r
311         gp2x_memregl[0x4508>>2] = ~(1<<26); // unmask DUALCPU ints in the undocumented 940's interrupt controller\r
312 \r
313         if (shared_mem == NULL)\r
314         {\r
315                 shared_mem = (unsigned char *) mmap(0, 0x210000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0x3000000);\r
316                 if(shared_mem == MAP_FAILED)\r
317                 {\r
318                         printf("mmap(shared_data) failed with %i\n", errno);\r
319                         exit(1);\r
320                 }\r
321                 shared_data = (_940_data_t *) (shared_mem+0x100000);\r
322                 /* this area must not get buffered on either side */\r
323                 shared_ctl =  (_940_ctl_t *)  (shared_mem+0x200000);\r
324                 crashed_940 = 1;\r
325         }\r
326 \r
327         if (crashed_940)\r
328         {\r
329                 unsigned char ucData[1024];\r
330                 int nRead, i, nLen = 0;\r
331                 char binpath[1024];\r
332                 FILE *fp;\r
333 \r
334                 strncpy(binpath, g_argv[0], 1023);\r
335                 binpath[1023] = 0;\r
336                 for (i = strlen(binpath); i > 0; i--)\r
337                         if (binpath[i] == '/') { binpath[i] = 0; break; }\r
338                 strcat(binpath, "/code940.bin");\r
339 \r
340                 fp = fopen(binpath, "rb");\r
341                 if(!fp)\r
342                 {\r
343                         memset(gp2x_screen, 0, 320*240);\r
344                         gp2x_text_out8(10, 100, "failed to open required file:");\r
345                         gp2x_text_out8(10, 110, "code940.bin");\r
346                         gp2x_video_flip();\r
347                         printf("failed to open %s\n", binpath);\r
348                         exit(1);\r
349                 }\r
350 \r
351                 while(1)\r
352                 {\r
353                         nRead = fread(ucData, 1, 1024, fp);\r
354                         if(nRead <= 0)\r
355                                 break;\r
356                         memcpy(shared_mem + nLen, ucData, nRead);\r
357                         nLen += nRead;\r
358                 }\r
359                 fclose(fp);\r
360                 crashed_940 = 0;\r
361         }\r
362 \r
363         memset(shared_data, 0, sizeof(*shared_data));\r
364         memset(shared_ctl,  0, sizeof(*shared_ctl));\r
365 \r
366         REGS = YM2612GetRegs();\r
367 \r
368         ym2612_dacen  = &dacen;\r
369         ym2612_dacout = &dacout;\r
370 \r
371         internal_reset();\r
372 \r
373         /* now cause 940 to init it's ym2612 stuff */\r
374         shared_ctl->baseclock = baseclock;\r
375         shared_ctl->rate = rate;\r
376         shared_ctl->job = JOB940_YM2612INIT;\r
377         shared_ctl->busy = 1;\r
378 \r
379         /* start the 940 */\r
380         Reset940(0);\r
381         Pause940(0);\r
382 \r
383         // YM2612ResetChip_940(); // will be done on JOB940_YM2612INIT\r
384 }\r
385 \r
386 \r
387 void YM2612ResetChip_940(void)\r
388 {\r
389         printf("YM2612ResetChip_940()\n");\r
390         if (shared_data == NULL) {\r
391                 printf("YM2612ResetChip_940: reset before init?\n");\r
392                 return;\r
393         }\r
394 \r
395         if (shared_ctl->busy) wait_busy_940();\r
396 \r
397         internal_reset();\r
398 \r
399         add_job_940(JOB940_YM2612RESETCHIP);\r
400 }\r
401 \r
402 \r
403 void YM2612UpdateOne_940(short *buffer, int length, int stereo)\r
404 {\r
405         int i, *mix_buffer = shared_data->mix_buffer;\r
406 \r
407         //printf("YM2612UpdateOne_940()\n");\r
408         if (shared_ctl->busy) wait_busy_940();\r
409 \r
410         //printf("940 (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);\r
411         //for (i = 0; i < 8; i++)\r
412         //      printf("%i ", shared_ctl->vstarts[i]);\r
413         //printf(")\n");\r
414 \r
415         /* mix data from previous go */\r
416         if (stereo) {\r
417                 int *mb = mix_buffer;\r
418                 for (i = length; i > 0; i--) {\r
419                         int l, r;\r
420                         l = r = *buffer;\r
421                         l += *mb++, r += *mb++;\r
422                         Limit( l, MAXOUT, MINOUT );\r
423                         Limit( r, MAXOUT, MINOUT );\r
424                         *buffer++ = l; *buffer++ = r;\r
425                 }\r
426         } else {\r
427                 for (i = 0; i < length; i++) {\r
428                         int l = mix_buffer[i];\r
429                         l += buffer[i];\r
430                         Limit( l, MAXOUT, MINOUT );\r
431                         buffer[i] = l;\r
432                 }\r
433         }\r
434 \r
435         //printf("new writes: %i\n", writebuff_ptr);\r
436         if (shared_ctl->writebuffsel == 1) {\r
437                 shared_ctl->writebuff0[writebuff_ptr] = 0xffff;\r
438         } else {\r
439                 shared_ctl->writebuff1[writebuff_ptr] = 0xffff;\r
440         }\r
441         writebuff_ptr = 0;\r
442 \r
443         /* give 940 another job */\r
444         shared_ctl->writebuffsel ^= 1;\r
445         shared_ctl->length = length;\r
446         shared_ctl->stereo = stereo;\r
447         add_job_940(JOB940_YM2612UPDATEONE);\r
448         //spend_cycles(512);\r
449         //printf("SRCPND: %08lx, INTMODE: %08lx, INTMASK: %08lx, INTPEND: %08lx\n",\r
450         //      gp2x_memregl[0x4500>>2], gp2x_memregl[0x4504>>2], gp2x_memregl[0x4508>>2], gp2x_memregl[0x4510>>2]);\r
451 }\r