OSDN Git Service

[VM] Add EMU::set_vm_screen_lines() to some VMs.
[csp-qt/common_source_project-fm7.git] / source / src / vm / ym2203.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2006.09.15-
6
7         [ YM2203 / YM2608 ]
8 */
9
10 #include "ym2203.h"
11 #include <math.h>
12
13 #define EVENT_FM_TIMER  0
14
15 #ifdef SUPPORT_MAME_FM_DLL
16 // thanks PC8801MA\89ΓΌ
17 #include "fmdll/fmdll.h"
18 static CFMDLL* fmdll = NULL;
19 static int chip_reference_counter = 0;
20 static bool dont_create_multiple_chips = false;
21 #endif
22
23 void YM2203::initialize()
24 {
25         DEVICE::initialize();
26         _HAS_YM2608 = osd->check_feature(_T("HAS_YM2608"));
27         if(_HAS_YM2608) {
28                 is_ym2608 = true;
29         } else {
30                 is_ym2608 = false;
31         }
32         _HAS_AY_3_8910 = osd->check_feature(_T("HAS_AY_3_8910"));
33         _HAS_AY_3_8912 = osd->check_feature(_T("HAS_AY_3_8912"));
34         _HAS_AY_3_8913 = osd->check_feature(_T("HAS_AY_3_8913"));
35         if(!_HAS_AY_3_8913) {
36                 if(_HAS_AY_3_8912) {
37                         _SUPPORT_YM2203_PORT_A = true;
38                 } else {
39                         _SUPPORT_YM2203_PORT_A = true;
40                         _SUPPORT_YM2203_PORT_B = true;
41                 }                       
42         }
43         if(!(_HAS_AY_3_8910 || _HAS_AY_3_8912 || _HAS_AY_3_8913)) _HAS_YM_SERIES = true;
44         if(_SUPPORT_YM2203_PORT_A || _SUPPORT_YM2203_PORT_B) _SUPPORT_YM2203_PORT = true;
45         _IS_YM2203_PORT_MODE = osd->check_feature(_T("YM2203_PORT_MODE"));
46         if(_IS_YM2203_PORT_MODE) {
47                 _YM2203_PORT_MODE = osd->get_feature_uint8_value(_T("YM2203_PORT_MODE"));
48         }
49 //#ifdef SUPPORT_YM2203_PORT
50 //      if(_SUPPORT_YM2203_PORT) {
51 //              for(int i = 0; i < 2; i++) {
52 //                      initialize_output_signals(&port[i].outputs);
53 //                      port[i].wreg = port[i].rreg = 0;//0xff;
54 //              }
55 //      }
56 //#endif
57 //#ifdef HAS_YM_SERIES
58 //      if(_HAS_YM_SERIES) {
59 //              initialize_output_signals(&outputs_irq);
60 //      }
61 //#endif
62 //#ifdef HAS_YM2608
63 //              is_ym2608 = true;
64 //#endif
65         _amask = (is_ym2608) ? 3 : 1;
66 //#ifdef HAS_YM2608
67         if(is_ym2608) {
68                 opna = new FM::OPNA;
69         } else {
70 //#endif
71                 opn = new FM::OPN;
72         }
73 //#ifdef HAS_YM2608
74         if(_HAS_YM2608) {
75                 set_device_name(_T("YM2608 OPNA"));
76         } else {
77 //#else         
78                 set_device_name(_T("YM2203 OPN"));
79         }
80 //#endif                
81
82 #ifdef SUPPORT_MAME_FM_DLL
83
84         if(!fmdll) {
85 //              fmdll = new CFMDLL(_T("mamefm.dll"));
86                 fmdll = new CFMDLL(config.fmgen_dll_path);
87         }
88         dllchip = NULL;
89 #endif
90         register_vline_event(this);
91         mute = false;
92         clock_prev = clock_accum = clock_busy = 0;
93
94         left_volume = right_volume = 256;
95         v_left_volume = v_right_volume = 256;
96 }
97
98 void YM2203::release()
99 {
100 //#ifdef HAS_YM2608
101         if(is_ym2608 && _HAS_YM2608) {
102                 delete opna;
103         } else
104 //#endif
105         delete opn;
106 #ifdef SUPPORT_MAME_FM_DLL
107         if(dllchip) {
108                 fmdll->Release(dllchip);
109                 dllchip = NULL;
110                 chip_reference_counter--;
111         }
112         if(fmdll && !chip_reference_counter) {
113                 delete fmdll;
114                 fmdll = NULL;
115         }
116 #endif
117 }
118
119 void YM2203::reset()
120 {
121         touch_sound();
122 //#ifdef HAS_YM2608
123         if(is_ym2608 && _HAS_YM2608) {
124                 opna->Reset();
125         } else
126 //#endif
127         opn->Reset();
128 #ifdef SUPPORT_MAME_FM_DLL
129         if(dllchip) {
130                 fmdll->Reset(dllchip);
131         }
132         memset(port_log, 0, sizeof(port_log));
133 #endif
134         fnum2 = 0;
135 //#ifdef HAS_YM2608
136         fnum21 = 0;
137 //#endif
138         
139         // stop timer
140         timer_event_id = -1;
141         this->set_reg(0x27, 0);
142         
143 //#ifdef SUPPORT_YM2203_PORT
144         if(_SUPPORT_YM2203_PORT) {
145                 port[0].first = port[1].first = true;
146                 port[0].wreg = port[1].wreg = 0;//0xff;
147 //#ifdef YM2203_PORT_MODE
148                 if(_IS_YM2203_PORT_MODE) {
149                         mode = _YM2203_PORT_MODE;
150                 } else {
151 //#else
152                         mode = 0;
153                 }
154         }
155 //#endif
156 //#endif
157         irq_prev = busy = false;
158 }
159
160 //#ifdef HAS_YM2608
161 //#define amask (is_ym2608 ? 3 : 1)
162 //#else
163 //#define amask 1
164 //#endif
165
166 void YM2203::write_io8(uint32_t addr, uint32_t data)
167 {
168         
169         _amask = (is_ym2608) ? 3 : 1;
170         switch(addr & _amask) {
171         case 0:
172 //#ifdef HAS_YM_SERIES
173                 if(_HAS_YM_SERIES) {
174                         ch = data;
175                         // write dummy data for prescaler
176                         if(0x2d <= ch && ch <= 0x2f) {
177                                 update_count();
178                                 this->set_reg(ch, 0);
179                                 update_interrupt();
180                                 clock_busy = get_current_clock();
181                                 busy = true;
182                         }
183                 } else {
184 //#else
185                         ch = data & 0x0f;
186                 }
187 //#endif
188                 break;
189         case 1:
190 //#ifdef SUPPORT_YM2203_PORT
191                 if(_SUPPORT_YM2203_PORT) {
192                         if(ch == 7) {
193 //#ifdef YM2203_PORT_MODE
194                                 if(_IS_YM2203_PORT_MODE) {
195                                         mode = (data & 0x3f) | _YM2203_PORT_MODE;
196                                 } else {
197 //#else
198                                         mode = data;
199                                 }
200 //#endif
201                         } else if(ch == 14) {
202 //#ifdef SUPPORT_YM2203_PORT_A
203                                 if(_SUPPORT_YM2203_PORT_A) {
204                                         if(port[0].wreg != data || port[0].first) {
205                                                 write_signals(&port[0].outputs, data);
206                                                 port[0].wreg = data;
207                                                 port[0].first = false;
208                                         }
209                                 }
210 //#endif
211                         } else if(ch == 15) {
212 //#ifdef SUPPORT_YM2203_PORT_B
213                                 if(_SUPPORT_YM2203_PORT_B) {
214                                         if(port[1].wreg != data || port[1].first) {
215                                                 write_signals(&port[1].outputs, data);
216                                                 port[1].wreg = data;
217                                                 port[1].first = false;
218                                         }
219                                 }
220                         }
221 //#endif
222                 }
223 //#endif
224                 if(0x2d <= ch && ch <= 0x2f) {
225                         // don't write again for prescaler
226                 } else if(0xa4 <= ch && ch <= 0xa6) {
227                         // XM8 version 1.20
228                         fnum2 = data;
229                 } else {
230                         update_count();
231                         // XM8 version 1.20
232                         if(0xa0 <= ch && ch <= 0xa2) {
233                                 this->set_reg(ch + 4, fnum2);
234                         }
235                         this->set_reg(ch, data);
236                         if(ch == 0x27) {
237                                 update_event();
238                         }
239 //#ifdef HAS_YM_SERIES
240                         if(_HAS_YM_SERIES) {
241                                 update_interrupt();
242                                 clock_busy = get_current_clock();
243                                 busy = true;
244                         }
245 //#endif
246                 }
247                 break;
248 //#ifdef HAS_YM2608
249         case 2:
250                 if(_HAS_YM2608) {
251                         ch1 = data1 = data;
252                 }
253                 break;
254         case 3:
255                 if(_HAS_YM2608) {
256                         if(0xa4 <= ch1 && ch1 <= 0xa6) {
257                                 // XM8 version 1.20
258                                 fnum21 = data;
259                         } else {
260                                 update_count();
261                                 // XM8 version 1.20
262                                 if(0xa0 <= ch1 && ch1 <= 0xa2) {
263                                         this->set_reg(0x100 | (ch1 + 4), fnum21);
264                                 }
265                                 this->set_reg(0x100 | ch1, data);
266                                 data1 = data;
267                                 update_interrupt();
268                                 clock_busy = get_current_clock();
269                                 busy = true;
270                         }
271                 }
272                 break;
273         default:
274                 break;
275 //#endif
276         }
277 }
278
279 uint32_t YM2203::read_io8(uint32_t addr)
280 {
281         if(_HAS_YM_SERIES) {
282                 _amask = (is_ym2608) ? 3 : 1;
283                 switch(addr & _amask) {
284 //#ifdef HAS_YM_SERIES
285                 case 0:
286                         {
287                                 /* BUSY : x : x : x : x : x : FLAGB : FLAGA */
288                                 update_count();
289                                 update_interrupt();
290                                 uint32_t status;
291 //#ifdef HAS_YM2608
292                                 if(is_ym2608 && _HAS_YM2608) {
293                                         status = opna->ReadStatus() & ~0x80;
294                                 } else {
295 //#endif
296                                         status = opn->ReadStatus() & ~0x80;
297                                 }
298                                 if(busy) {
299                                         // from PC-88 machine language master bible (XM8 version 1.00)
300 //#ifdef HAS_YM2608
301                                         if(_HAS_YM2608) {
302                                                 if (get_passed_usec(clock_busy) < (is_ym2608 ? 4.25 : 2.13)) {
303                                                         status |= 0x80;
304                                                 } else {
305                                                         busy = false;
306                                                 }
307                                         } else {
308 //#else
309                                                 if (get_passed_usec(clock_busy) < 2.13) {
310                                                         status |= 0x80;
311                                                 } else {
312                                                         busy = false;
313                                                 }
314                                         }
315 //#endif
316                                 }
317                                 return status;
318                         }
319 //#endif
320                         break;
321                 case 1:
322 //#ifdef SUPPORT_YM2203_PORT
323                         if(_SUPPORT_YM2203_PORT) {
324                                 if(ch == 14) {
325 //#ifdef SUPPORT_YM2203_PORT_A
326                                         if(_SUPPORT_YM2203_PORT_A) {
327                                                 return (mode & 0x40) ? port[0].wreg : port[0].rreg;
328                                         }
329 //#endif
330                                 } else if(ch == 15) {
331 //#ifdef SUPPORT_YM2203_PORT_B
332                                         if(_SUPPORT_YM2203_PORT_B) {
333                                                 return (mode & 0x80) ? port[1].wreg : port[1].rreg;
334                                         }
335 //#endif
336                                 }
337                         }
338 //#endif
339 //#ifdef HAS_YM2608
340                         if(is_ym2608 && _HAS_YM2608) {
341                                 return opna->GetReg(ch);
342                         } else {
343 //#endif
344                                 return opn->GetReg(ch);
345                         }
346                         break;
347 //#ifdef HAS_YM2608
348                 case 2:
349                         if(_HAS_YM2608) {
350                                 /* BUSY : x : PCMBUSY : ZERO : BRDY : EOS : FLAGB : FLAGA */
351                                 update_count();
352                                 update_interrupt();
353                                 uint32_t status = opna->ReadStatusEx() & ~0x80;
354                                 if(busy) {
355                                         // FIXME: we need to investigate the correct busy period
356                                         if(get_passed_usec(clock_busy) < 8) {
357                                                 status |= 0x80;
358                                         } else {
359                                                 busy = false;
360                                         }
361                                 }
362                                 return status;
363                         }
364                         break;
365                 case 3:
366                         if(_HAS_YM2608) {
367                                 if(ch1 == 8) {
368                                         return opna->GetReg(0x100 | ch1);
369 //              } else if(ch1 == 0x0f) {
370 //                      return 0x80; // from mame fm.c
371                                 }
372                                 return data1;
373                         }
374                         break;
375 //#endif
376                 default:
377                         break;
378                 }
379         }
380         return 0xff;
381 }
382
383 void YM2203::write_signal(int id, uint32_t data, uint32_t mask)
384 {
385         if(id == SIG_YM2203_MUTE) {
386                 mute = ((data & mask) != 0);
387 //#ifdef SUPPORT_YM2203_PORT_A
388         } else if((id == SIG_YM2203_PORT_A) && _SUPPORT_YM2203_PORT_A) {
389                 port[0].rreg = (port[0].rreg & ~mask) | (data & mask);
390 //#endif
391 //#ifdef SUPPORT_YM2203_PORT_B
392         } else if((id == SIG_YM2203_PORT_B) && _SUPPORT_YM2203_PORT_B) {
393                 port[1].rreg = (port[1].rreg & ~mask) | (data & mask);
394 //#endif
395         }
396 }
397
398 uint32_t YM2203::read_signal(int id)
399 {
400         if((id == SIG_YM2203_PORT_A) && _SUPPORT_YM2203_PORT_A) {
401                 return (uint32_t)port[0].rreg;
402 //#endif
403 //#ifdef SUPPORT_YM2203_PORT_B
404         } else if((id == SIG_YM2203_PORT_B) && _SUPPORT_YM2203_PORT_B) {
405                 return (uint32_t)port[1].rreg;
406 //#endif
407         }
408         return 0x00;
409 }       
410
411 void YM2203::event_vline(int v, int clock)
412 {
413         update_count();
414 //#ifdef HAS_YM_SERIES
415         if(_HAS_YM_SERIES) {
416                 update_interrupt();
417         }
418 //#endif
419 }
420
421 void YM2203::event_callback(int event_id, int error)
422 {
423         update_count();
424 //#ifdef HAS_YM_SERIES
425         if(_HAS_YM_SERIES) {
426                 update_interrupt();
427         }
428 //#endif
429         timer_event_id = -1;
430         update_event();
431 }
432
433 void YM2203::update_count()
434 {
435         clock_accum += clock_const * get_passed_clock(clock_prev);
436         uint32_t count = clock_accum >> 20;
437         if(count) {
438 //#ifdef HAS_YM2608
439                 if(is_ym2608 && _HAS_YM2608) {
440                         opna->Count(count);
441                 } else {
442 //#endif
443                         opn->Count(count);
444                 }
445                 clock_accum -= count << 20;
446         }
447         clock_prev = get_current_clock();
448 }
449
450 void YM2203::update_event()
451 {
452         if(timer_event_id != -1) {
453                 cancel_event(this, timer_event_id);
454                 timer_event_id = -1;
455         }
456         
457         int count;
458 //#ifdef HAS_YM2608
459         if(is_ym2608 && _HAS_YM2608) {
460                 count = opna->GetNextEvent();
461         } else {
462 //#endif
463                 count = opn->GetNextEvent();
464         }
465         if(count > 0) {
466 //#ifdef HAS_YM2608
467                 if(is_ym2608 && _HAS_YM2608) {
468                         register_event(this, EVENT_FM_TIMER, 1000000.0 / (double)chip_clock * (double)count * 2.0, false, &timer_event_id);
469                 } else {
470 //#endif
471                         register_event(this, EVENT_FM_TIMER, 1000000.0 / (double)chip_clock * (double)count, false, &timer_event_id);
472                 }
473         }
474 }
475
476 //#ifdef HAS_YM_SERIES
477 void YM2203::update_interrupt()
478 {
479         if(_HAS_YM_SERIES) {
480                 bool irq;
481 //#ifdef HAS_YM2608
482                 if(is_ym2608 && _HAS_YM2608) {
483                         irq = opna->ReadIRQ();
484                 } else {
485 //#endif
486                         irq = opn->ReadIRQ();
487                 }
488                 if(!irq_prev && irq) {
489                         write_signals(&outputs_irq, 0xffffffff);
490                 } else if(irq_prev && !irq) {
491                         write_signals(&outputs_irq, 0);
492                 }
493                 irq_prev = irq;
494         }
495 }
496 //#endif
497
498 inline int32_t VCALC(int32_t x, int32_t y)
499 {
500         x = x * y;
501         x = x >> 8;
502         return x;
503 }
504
505 inline int32_t SATURATION_ADD(int32_t x, int32_t y)
506 {
507         x = x + y;
508         if(x < -0x8000) x = -0x8000;
509         if(x >  0x7fff) x =  0x7fff;
510         return x;
511 }
512
513
514 void YM2203::mix(int32_t* buffer, int cnt)
515 {
516         if(cnt > 0 && !mute) {
517                 int32_t *dbuffer = (int32_t *)malloc((cnt * 2 + 2) * sizeof(int32_t));
518                 memset((void *)dbuffer, 0x00, (cnt * 2 + 2) * sizeof(int32_t));
519            
520 //#ifdef HAS_YM2608
521                 if(is_ym2608 && _HAS_YM2608) {
522                         opna->Mix(dbuffer, cnt);
523                 } else {
524 //#endif
525                         opn->Mix(dbuffer, cnt);
526                 }
527 #ifdef SUPPORT_MAME_FM_DLL
528                 if(dllchip) {
529                         fmdll->Mix(dllchip, dbuffer, cnt);
530                 }
531 #endif
532                 int32_t *p = dbuffer;
533                 int32_t *q = buffer;
534                 int32_t tmp[8];
535                 int32_t tvol[8] = {v_left_volume, v_right_volume,
536                                  v_left_volume, v_right_volume,
537                                  v_left_volume, v_right_volume,
538                                  v_left_volume, v_right_volume};
539                 int i;
540                 // More EXCEPTS to optimize to SIMD features.
541                 for(i = 0; i < cnt / 4; i++) {
542                         tmp[0] = VCALC(p[0], tvol[0]);
543                         tmp[1] = VCALC(p[1], tvol[1]);
544                         tmp[2] = VCALC(p[2], tvol[2]);
545                         tmp[3] = VCALC(p[3], tvol[3]);
546                         tmp[4] = VCALC(p[4], tvol[4]);
547                         tmp[5] = VCALC(p[5], tvol[5]);
548                         tmp[6] = VCALC(p[6], tvol[6]);
549                         tmp[7] = VCALC(p[7], tvol[7]);
550
551                         q[0] = SATURATION_ADD(q[0], tmp[0]);
552                         q[1] = SATURATION_ADD(q[1], tmp[1]);
553                         q[2] = SATURATION_ADD(q[2], tmp[2]);
554                         q[3] = SATURATION_ADD(q[3], tmp[3]);
555                    
556                         q[4] = SATURATION_ADD(q[4], tmp[4]);
557                         q[5] = SATURATION_ADD(q[5], tmp[5]);
558                         q[6] = SATURATION_ADD(q[6], tmp[6]);
559                         q[7] = SATURATION_ADD(q[7], tmp[7]);
560                         q += 8;
561                         p += 8;
562                 }
563                 if((cnt & 3) != 0) {
564                         for(i = 0; i < (cnt & 3); i++) {
565                                 tmp[0] = VCALC(p[0], tvol[0]);
566                                 tmp[1] = VCALC(p[1], tvol[1]);
567                            
568                                 q[0] = SATURATION_ADD(q[0], tmp[0]);
569                                 q[1] = SATURATION_ADD(q[1], tmp[1]);
570                                 q += 2;
571                                 p += 2;
572                         }
573                 }
574                 free(dbuffer);
575         }
576 }
577
578 void YM2203::set_volume(int ch, int decibel_l, int decibel_r)
579 {
580         v_right_volume = (int)(pow(10.0, (double)decibel_vol / 10.0) * (double)right_volume);
581         v_left_volume = (int)(pow(10.0, (double)decibel_vol / 10.0) * (double)left_volume);
582         if(ch == 0) {
583 //#ifdef HAS_YM2608
584                 if(is_ym2608 && _HAS_YM2608) {
585                         opna->SetVolumeFM(base_decibel_fm + decibel_l, base_decibel_fm + decibel_r);
586                 } else {
587 //#endif
588                         opn->SetVolumeFM(base_decibel_fm + decibel_l, base_decibel_fm + decibel_r);
589                 }
590 #ifdef SUPPORT_MAME_FM_DLL
591                 if(dllchip) {
592                         fmdll->SetVolumeFM(dllchip, base_decibel_fm + decibel_l);
593                 }
594 #endif
595         } else if(ch == 1) {
596 //#ifdef HAS_YM2608
597                 if(is_ym2608 && _HAS_YM2608) {
598                         opna->SetVolumePSG(base_decibel_psg + decibel_l, base_decibel_psg + decibel_r);
599                 } else {
600 //#endif
601                         opn->SetVolumePSG(base_decibel_psg + decibel_l, base_decibel_psg + decibel_r);
602                 }
603 #ifdef SUPPORT_MAME_FM_DLL
604                 if(dllchip) {
605                         fmdll->SetVolumePSG(dllchip, base_decibel_psg + decibel_l);
606                 }
607 #endif
608 //#ifdef HAS_YM2608
609         } else if(ch == 2) {
610                 if(is_ym2608 && _HAS_YM2608) {
611                         opna->SetVolumeADPCM(decibel_l, decibel_r);
612                 }
613         } else if(ch == 3) {
614                 if(is_ym2608 && _HAS_YM2608) {
615                         opna->SetVolumeRhythmTotal(decibel_l, decibel_r);
616                 }
617 //#endif
618         }
619 }
620
621 void YM2203::initialize_sound(int rate, int clock, int samples, int decibel_fm, int decibel_psg)
622 {
623 //#ifdef HAS_YM2608
624         if(is_ym2608 && _HAS_YM2608) {
625                 opna->Init(clock, rate, false, get_application_path());
626                 opna->SetVolumeFM(decibel_fm, decibel_fm);
627                 opna->SetVolumePSG(decibel_psg, decibel_psg);
628         } else {
629 //#endif
630                 opn->Init(clock, rate, false, NULL);
631                 opn->SetVolumeFM(decibel_fm, decibel_fm);
632                 opn->SetVolumePSG(decibel_psg, decibel_psg);
633 //#ifdef HAS_YM2608
634         }
635 //#endif
636         base_decibel_fm = decibel_fm;
637         base_decibel_psg = decibel_psg;
638         
639 #ifdef SUPPORT_MAME_FM_DLL
640         if(!dont_create_multiple_chips) {
641 //#ifdef HAS_YM2608
642                 if(is_ym2608 && _HAS_YM2608) {
643                         fmdll->Create((LPVOID*)&dllchip, clock, rate);
644                 } else
645 //#endif
646                 fmdll->Create((LPVOID*)&dllchip, clock * 2, rate);
647                 if(dllchip) {
648                         chip_reference_counter++;
649                         
650                         fmdll->SetVolumeFM(dllchip, decibel_fm);
651                         fmdll->SetVolumePSG(dllchip, decibel_psg);
652                         
653                         DWORD mask = 0;
654                         DWORD dwCaps = fmdll->GetCaps(dllchip);
655                         if((dwCaps & SUPPORT_MULTIPLE) != SUPPORT_MULTIPLE) {
656                                 dont_create_multiple_chips = true;
657                         }
658                         if((dwCaps & SUPPORT_FM_A) == SUPPORT_FM_A) {
659                                 mask = 0x07;
660                         }
661                         if((dwCaps & SUPPORT_FM_B) == SUPPORT_FM_B) {
662                                 mask |= 0x38;
663                         }
664                         if((dwCaps & SUPPORT_PSG) == SUPPORT_PSG) {
665                                 mask |= 0x1c0;
666                         }
667                         if((dwCaps & SUPPORT_ADPCM_B) == SUPPORT_ADPCM_B) {
668                                 mask |= 0x200;
669                         }
670                         if((dwCaps & SUPPORT_RHYTHM) == SUPPORT_RHYTHM) {
671                                 mask |= 0xfc00;
672                         }
673 //#ifdef HAS_YM2608
674                         if(is_ym2608 && _HAS_YM2608) {
675                                 opna->SetChannelMask(mask);
676                         } else
677 //#endif
678                         opn->SetChannelMask(mask);
679                         fmdll->SetChannelMask(dllchip, ~mask);
680                 }
681         }
682 #endif
683         chip_clock = clock;
684 }
685
686 void YM2203::set_reg(uint32_t addr, uint32_t data)
687 {
688         touch_sound();
689 //#ifdef HAS_YM2608
690         if(is_ym2608 && _HAS_YM2608) {
691                 opna->SetReg(addr, data);
692         } else {
693 //#endif
694                 if((addr & 0xf0) == 0x10) {
695                         return;
696                 }
697                 if(addr == 0x22) {
698                         data = 0x00;
699                 } else if(addr == 0x29) {
700                         data = 0x03;
701                 } else if(addr >= 0xb4) {
702                         data = 0xc0;
703                 }
704                 opn->SetReg(addr, data);
705 //#ifdef HAS_YM2608
706         }
707 //#endif
708 #ifdef SUPPORT_MAME_FM_DLL
709         if(dllchip) {
710                 fmdll->SetReg(dllchip, addr, data);
711         }
712         if(0x2d <= addr && addr <= 0x2f) {
713                 port_log[0x2d].written = port_log[0x2e].written = port_log[0x2f].written = false;
714         }
715         port_log[addr].written = true;
716         port_log[addr].data = data;
717 #endif
718 }
719
720 void YM2203::update_timing(int new_clocks, double new_frames_per_sec, int new_lines_per_frame)
721 {
722 //#ifdef HAS_YM2608
723         if(is_ym2608 && _HAS_YM2608) {
724                 clock_const = (uint32_t)((double)chip_clock * 1024.0 * 1024.0 / (double)new_clocks / 2.0 + 0.5);
725         } else
726 //#endif
727         clock_const = (uint32_t)((double)chip_clock * 1024.0 * 1024.0 / (double)new_clocks + 0.5);
728 }
729
730 #define STATE_VERSION   4
731
732 void YM2203::save_state(FILEIO* state_fio)
733 {
734         state_fio->FputUint32(STATE_VERSION);
735         state_fio->FputInt32(this_device_id);
736         
737 //#ifdef HAS_YM2608
738         if(is_ym2608 && _HAS_YM2608) {
739                 opna->SaveState((void *)state_fio);;
740         } else {
741 //#endif
742                 opn->SaveState((void *)state_fio);
743         }
744 #ifdef SUPPORT_MAME_FM_DLL
745         state_fio->Fwrite(port_log, sizeof(port_log), 1);
746 #endif
747         state_fio->FputUint8(ch);
748         state_fio->FputUint8(fnum2);
749 //#ifdef HAS_YM2608
750         if(_HAS_YM2608) {
751                 state_fio->FputUint8(ch1);
752                 state_fio->FputUint8(data1);
753                 state_fio->FputUint8(fnum21);
754         }
755 //#endif
756 //#ifdef SUPPORT_YM2203_PORT
757         if(_SUPPORT_YM2203_PORT) {
758                 for(int i = 0; i < 2; i++) {
759                         state_fio->FputUint8(port[i].wreg);
760                         state_fio->FputUint8(port[i].rreg);
761                         state_fio->FputBool(port[i].first);
762                 }
763                 state_fio->FputUint8(mode);
764         }
765 //#endif
766         state_fio->FputInt32(chip_clock);
767         state_fio->FputBool(irq_prev);
768         state_fio->FputBool(mute);
769         state_fio->FputUint32(clock_prev);
770         state_fio->FputUint32(clock_accum);
771         state_fio->FputUint32(clock_const);
772         state_fio->FputUint32(clock_busy);
773         state_fio->FputInt32(timer_event_id);
774         state_fio->FputBool(busy);
775         state_fio->FputInt32(decibel_vol);
776         state_fio->FputInt32(left_volume);
777         state_fio->FputInt32(right_volume);
778         state_fio->FputInt32(v_left_volume);
779         state_fio->FputInt32(v_right_volume);
780 }
781
782 bool YM2203::load_state(FILEIO* state_fio)
783 {
784         if(state_fio->FgetUint32() != STATE_VERSION) {
785                 return false;
786         }
787         if(state_fio->FgetInt32() != this_device_id) {
788                 return false;
789         }
790 //#ifdef HAS_YM2608
791         if(is_ym2608 && _HAS_YM2608) {
792                 if(!opna->LoadState((void *)state_fio)) {
793                         return false;
794                 }
795         } else {
796 //#endif
797                 if(!opn->LoadState((void *)state_fio)) {
798                         return false;
799                 }
800 //#ifdef HAS_YM2608
801         }
802 //#endif
803 #ifdef SUPPORT_MAME_FM_DLL
804         state_fio->Fread(port_log, sizeof(port_log), 1);
805 #endif
806         ch = state_fio->FgetUint8();
807         fnum2 = state_fio->FgetUint8();
808 //#ifdef HAS_YM2608
809         if(_HAS_YM2608) {
810                 ch1 = state_fio->FgetUint8();
811                 data1 = state_fio->FgetUint8();
812                 fnum21 = state_fio->FgetUint8();
813         }
814 //#endif
815 //#ifdef SUPPORT_YM2203_PORT
816         if(_SUPPORT_YM2203_PORT) {
817                 for(int i = 0; i < 2; i++) {
818                         port[i].wreg = state_fio->FgetUint8();
819                         port[i].rreg = state_fio->FgetUint8();
820                         port[i].first = state_fio->FgetBool();
821                 }
822                 mode = state_fio->FgetUint8();
823         }
824 //#endif
825         chip_clock = state_fio->FgetInt32();
826         irq_prev = state_fio->FgetBool();
827         mute = state_fio->FgetBool();
828         clock_prev = state_fio->FgetUint32();
829         clock_accum = state_fio->FgetUint32();
830         clock_const = state_fio->FgetUint32();
831         clock_busy = state_fio->FgetUint32();
832         timer_event_id = state_fio->FgetInt32();
833         busy = state_fio->FgetBool();
834
835 #ifdef SUPPORT_MAME_FM_DLL
836         // post process
837         if(dllchip) {
838                 fmdll->Reset(dllchip);
839                 for(int i = 0; i < 0x200; i++) {
840                         // write fnum2 before fnum1
841                         int ch = ((i >= 0xa0 && i <= 0xaf) || (i >= 0x1a0 && i <= 0x1a7)) ? (i ^ 4) : i;
842                         if(port_log[ch].written) {
843                                 fmdll->SetReg(dllchip, ch, port_log[ch].data);
844                         }
845                 }
846 #ifdef HAS_YM2608
847                 if(is_ym2608) {
848                         BYTE *dest = fmdll->GetADPCMBuffer(dllchip);
849                         if(dest != NULL) {
850                                 memcpy(dest, opna->GetADPCMBuffer(), 0x40000);
851                         }
852                 }
853 #endif
854         }
855 #endif
856         decibel_vol = state_fio->FgetInt32();
857         left_volume = state_fio->FgetInt32();
858         right_volume = state_fio->FgetInt32();
859         v_left_volume = state_fio->FgetInt32();
860         v_right_volume = state_fio->FgetInt32();
861         //touch_sound();
862
863         return true;
864 }
865