2 Skelton for retropc emulator
4 Author : Takeda.Toshiya
13 #define EVENT_FM_TIMER 0
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;
23 void YM2203::initialize()
26 _HAS_YM2608 = osd->check_feature(_T("HAS_YM2608"));
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"));
37 _SUPPORT_YM2203_PORT_A = true;
39 _SUPPORT_YM2203_PORT_A = true;
40 _SUPPORT_YM2203_PORT_B = true;
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"));
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;
57 //#ifdef HAS_YM_SERIES
58 // if(_HAS_YM_SERIES) {
59 // initialize_output_signals(&outputs_irq);
65 _amask = (is_ym2608) ? 3 : 1;
75 set_device_name(_T("YM2608 OPNA"));
78 set_device_name(_T("YM2203 OPN"));
82 #ifdef SUPPORT_MAME_FM_DLL
85 // fmdll = new CFMDLL(_T("mamefm.dll"));
86 fmdll = new CFMDLL(config.fmgen_dll_path);
90 register_vline_event(this);
92 clock_prev = clock_accum = clock_busy = 0;
94 left_volume = right_volume = 256;
95 v_left_volume = v_right_volume = 256;
98 void YM2203::release()
101 if(is_ym2608 && _HAS_YM2608) {
106 #ifdef SUPPORT_MAME_FM_DLL
108 fmdll->Release(dllchip);
110 chip_reference_counter--;
112 if(fmdll && !chip_reference_counter) {
123 if(is_ym2608 && _HAS_YM2608) {
128 #ifdef SUPPORT_MAME_FM_DLL
130 fmdll->Reset(dllchip);
132 memset(port_log, 0, sizeof(port_log));
141 this->set_reg(0x27, 0);
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;
157 irq_prev = busy = false;
161 //#define amask (is_ym2608 ? 3 : 1)
166 void YM2203::write_io8(uint32_t addr, uint32_t data)
169 _amask = (is_ym2608) ? 3 : 1;
170 switch(addr & _amask) {
172 //#ifdef HAS_YM_SERIES
175 // write dummy data for prescaler
176 if(0x2d <= ch && ch <= 0x2f) {
178 this->set_reg(ch, 0);
180 clock_busy = get_current_clock();
190 //#ifdef SUPPORT_YM2203_PORT
191 if(_SUPPORT_YM2203_PORT) {
193 //#ifdef YM2203_PORT_MODE
194 if(_IS_YM2203_PORT_MODE) {
195 mode = (data & 0x3f) | _YM2203_PORT_MODE;
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);
207 port[0].first = false;
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);
217 port[1].first = false;
224 if(0x2d <= ch && ch <= 0x2f) {
225 // don't write again for prescaler
226 } else if(0xa4 <= ch && ch <= 0xa6) {
232 if(0xa0 <= ch && ch <= 0xa2) {
233 this->set_reg(ch + 4, fnum2);
235 this->set_reg(ch, data);
239 //#ifdef HAS_YM_SERIES
242 clock_busy = get_current_clock();
256 if(0xa4 <= ch1 && ch1 <= 0xa6) {
262 if(0xa0 <= ch1 && ch1 <= 0xa2) {
263 this->set_reg(0x100 | (ch1 + 4), fnum21);
265 this->set_reg(0x100 | ch1, data);
268 clock_busy = get_current_clock();
279 uint32_t YM2203::read_io8(uint32_t addr)
282 _amask = (is_ym2608) ? 3 : 1;
283 switch(addr & _amask) {
284 //#ifdef HAS_YM_SERIES
287 /* BUSY : x : x : x : x : x : FLAGB : FLAGA */
292 if(is_ym2608 && _HAS_YM2608) {
293 status = opna->ReadStatus() & ~0x80;
296 status = opn->ReadStatus() & ~0x80;
299 // from PC-88 machine language master bible (XM8 version 1.00)
302 if (get_passed_usec(clock_busy) < (is_ym2608 ? 4.25 : 2.13)) {
309 if (get_passed_usec(clock_busy) < 2.13) {
322 //#ifdef SUPPORT_YM2203_PORT
323 if(_SUPPORT_YM2203_PORT) {
325 //#ifdef SUPPORT_YM2203_PORT_A
326 if(_SUPPORT_YM2203_PORT_A) {
327 return (mode & 0x40) ? port[0].wreg : port[0].rreg;
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;
340 if(is_ym2608 && _HAS_YM2608) {
341 return opna->GetReg(ch);
344 return opn->GetReg(ch);
350 /* BUSY : x : PCMBUSY : ZERO : BRDY : EOS : FLAGB : FLAGA */
353 uint32_t status = opna->ReadStatusEx() & ~0x80;
355 // FIXME: we need to investigate the correct busy period
356 if(get_passed_usec(clock_busy) < 8) {
368 return opna->GetReg(0x100 | ch1);
369 // } else if(ch1 == 0x0f) {
370 // return 0x80; // from mame fm.c
383 void YM2203::write_signal(int id, uint32_t data, uint32_t mask)
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);
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);
398 uint32_t YM2203::read_signal(int id)
400 if((id == SIG_YM2203_PORT_A) && _SUPPORT_YM2203_PORT_A) {
401 return (uint32_t)port[0].rreg;
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;
411 void YM2203::event_vline(int v, int clock)
414 //#ifdef HAS_YM_SERIES
421 void YM2203::event_callback(int event_id, int error)
424 //#ifdef HAS_YM_SERIES
433 void YM2203::update_count()
435 clock_accum += clock_const * get_passed_clock(clock_prev);
436 uint32_t count = clock_accum >> 20;
439 if(is_ym2608 && _HAS_YM2608) {
445 clock_accum -= count << 20;
447 clock_prev = get_current_clock();
450 void YM2203::update_event()
452 if(timer_event_id != -1) {
453 cancel_event(this, timer_event_id);
459 if(is_ym2608 && _HAS_YM2608) {
460 count = opna->GetNextEvent();
463 count = opn->GetNextEvent();
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);
471 register_event(this, EVENT_FM_TIMER, 1000000.0 / (double)chip_clock * (double)count, false, &timer_event_id);
476 //#ifdef HAS_YM_SERIES
477 void YM2203::update_interrupt()
482 if(is_ym2608 && _HAS_YM2608) {
483 irq = opna->ReadIRQ();
486 irq = opn->ReadIRQ();
488 if(!irq_prev && irq) {
489 write_signals(&outputs_irq, 0xffffffff);
490 } else if(irq_prev && !irq) {
491 write_signals(&outputs_irq, 0);
498 inline int32_t VCALC(int32_t x, int32_t y)
505 inline int32_t SATURATION_ADD(int32_t x, int32_t y)
508 if(x < -0x8000) x = -0x8000;
509 if(x > 0x7fff) x = 0x7fff;
514 void YM2203::mix(int32_t* buffer, int cnt)
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));
521 if(is_ym2608 && _HAS_YM2608) {
522 opna->Mix(dbuffer, cnt);
525 opn->Mix(dbuffer, cnt);
527 #ifdef SUPPORT_MAME_FM_DLL
529 fmdll->Mix(dllchip, dbuffer, cnt);
532 int32_t *p = dbuffer;
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};
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]);
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]);
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]);
564 for(i = 0; i < (cnt & 3); i++) {
565 tmp[0] = VCALC(p[0], tvol[0]);
566 tmp[1] = VCALC(p[1], tvol[1]);
568 q[0] = SATURATION_ADD(q[0], tmp[0]);
569 q[1] = SATURATION_ADD(q[1], tmp[1]);
578 void YM2203::set_volume(int ch, int decibel_l, int decibel_r)
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);
584 if(is_ym2608 && _HAS_YM2608) {
585 opna->SetVolumeFM(base_decibel_fm + decibel_l, base_decibel_fm + decibel_r);
588 opn->SetVolumeFM(base_decibel_fm + decibel_l, base_decibel_fm + decibel_r);
590 #ifdef SUPPORT_MAME_FM_DLL
592 fmdll->SetVolumeFM(dllchip, base_decibel_fm + decibel_l);
597 if(is_ym2608 && _HAS_YM2608) {
598 opna->SetVolumePSG(base_decibel_psg + decibel_l, base_decibel_psg + decibel_r);
601 opn->SetVolumePSG(base_decibel_psg + decibel_l, base_decibel_psg + decibel_r);
603 #ifdef SUPPORT_MAME_FM_DLL
605 fmdll->SetVolumePSG(dllchip, base_decibel_psg + decibel_l);
610 if(is_ym2608 && _HAS_YM2608) {
611 opna->SetVolumeADPCM(decibel_l, decibel_r);
614 if(is_ym2608 && _HAS_YM2608) {
615 opna->SetVolumeRhythmTotal(decibel_l, decibel_r);
621 void YM2203::initialize_sound(int rate, int clock, int samples, int decibel_fm, int decibel_psg)
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);
630 opn->Init(clock, rate, false, NULL);
631 opn->SetVolumeFM(decibel_fm, decibel_fm);
632 opn->SetVolumePSG(decibel_psg, decibel_psg);
636 base_decibel_fm = decibel_fm;
637 base_decibel_psg = decibel_psg;
639 #ifdef SUPPORT_MAME_FM_DLL
640 if(!dont_create_multiple_chips) {
642 if(is_ym2608 && _HAS_YM2608) {
643 fmdll->Create((LPVOID*)&dllchip, clock, rate);
646 fmdll->Create((LPVOID*)&dllchip, clock * 2, rate);
648 chip_reference_counter++;
650 fmdll->SetVolumeFM(dllchip, decibel_fm);
651 fmdll->SetVolumePSG(dllchip, decibel_psg);
654 DWORD dwCaps = fmdll->GetCaps(dllchip);
655 if((dwCaps & SUPPORT_MULTIPLE) != SUPPORT_MULTIPLE) {
656 dont_create_multiple_chips = true;
658 if((dwCaps & SUPPORT_FM_A) == SUPPORT_FM_A) {
661 if((dwCaps & SUPPORT_FM_B) == SUPPORT_FM_B) {
664 if((dwCaps & SUPPORT_PSG) == SUPPORT_PSG) {
667 if((dwCaps & SUPPORT_ADPCM_B) == SUPPORT_ADPCM_B) {
670 if((dwCaps & SUPPORT_RHYTHM) == SUPPORT_RHYTHM) {
674 if(is_ym2608 && _HAS_YM2608) {
675 opna->SetChannelMask(mask);
678 opn->SetChannelMask(mask);
679 fmdll->SetChannelMask(dllchip, ~mask);
686 void YM2203::set_reg(uint32_t addr, uint32_t data)
690 if(is_ym2608 && _HAS_YM2608) {
691 opna->SetReg(addr, data);
694 if((addr & 0xf0) == 0x10) {
699 } else if(addr == 0x29) {
701 } else if(addr >= 0xb4) {
704 opn->SetReg(addr, data);
708 #ifdef SUPPORT_MAME_FM_DLL
710 fmdll->SetReg(dllchip, addr, data);
712 if(0x2d <= addr && addr <= 0x2f) {
713 port_log[0x2d].written = port_log[0x2e].written = port_log[0x2f].written = false;
715 port_log[addr].written = true;
716 port_log[addr].data = data;
720 void YM2203::update_timing(int new_clocks, double new_frames_per_sec, int new_lines_per_frame)
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);
727 clock_const = (uint32_t)((double)chip_clock * 1024.0 * 1024.0 / (double)new_clocks + 0.5);
730 #define STATE_VERSION 4
732 void YM2203::save_state(FILEIO* state_fio)
734 state_fio->FputUint32(STATE_VERSION);
735 state_fio->FputInt32(this_device_id);
738 if(is_ym2608 && _HAS_YM2608) {
739 opna->SaveState((void *)state_fio);;
742 opn->SaveState((void *)state_fio);
744 #ifdef SUPPORT_MAME_FM_DLL
745 state_fio->Fwrite(port_log, sizeof(port_log), 1);
747 state_fio->FputUint8(ch);
748 state_fio->FputUint8(fnum2);
751 state_fio->FputUint8(ch1);
752 state_fio->FputUint8(data1);
753 state_fio->FputUint8(fnum21);
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);
763 state_fio->FputUint8(mode);
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);
782 bool YM2203::load_state(FILEIO* state_fio)
784 if(state_fio->FgetUint32() != STATE_VERSION) {
787 if(state_fio->FgetInt32() != this_device_id) {
791 if(is_ym2608 && _HAS_YM2608) {
792 if(!opna->LoadState((void *)state_fio)) {
797 if(!opn->LoadState((void *)state_fio)) {
803 #ifdef SUPPORT_MAME_FM_DLL
804 state_fio->Fread(port_log, sizeof(port_log), 1);
806 ch = state_fio->FgetUint8();
807 fnum2 = state_fio->FgetUint8();
810 ch1 = state_fio->FgetUint8();
811 data1 = state_fio->FgetUint8();
812 fnum21 = state_fio->FgetUint8();
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();
822 mode = state_fio->FgetUint8();
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();
835 #ifdef SUPPORT_MAME_FM_DLL
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);
848 BYTE *dest = fmdll->GetADPCMBuffer(dllchip);
850 memcpy(dest, opna->GetADPCMBuffer(), 0x40000);
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();