OSDN Git Service

[VM][General][WIP] Start to merge upstream 2018-10-14.Open branch upstream_20181014 .
[csp-qt/common_source_project-fm7.git] / source / src / vm / ym2151.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2009.03.08-
6
7         [ YM2151 ]
8 */
9
10 #include "ym2151.h"
11
12 #define EVENT_FM_TIMER  0
13
14 #ifdef SUPPORT_MAME_FM_DLL
15 // thanks PC8801MA\89ΓΌ
16 #include "fmdll/fmdll.h"
17 static CFMDLL* fmdll = NULL;
18 static int chip_reference_counter = 0;
19 static bool dont_create_multiple_chips = false;
20 #endif
21
22 void YM2151::initialize()
23 {
24         DEVICE::initialize();
25         opm = new FM::OPM;
26 #ifdef SUPPORT_MAME_FM_DLL
27         if(!fmdll) {
28 //              fmdll = new CFMDLL(_T("mamefm.dll"));
29                 fmdll = new CFMDLL(config.fmgen_dll_path);
30         }
31         dllchip = NULL;
32 #endif
33         register_vline_event(this);
34         mute = false;
35         clock_prev = clock_accum = clock_busy = 0;
36 }
37
38 void YM2151::release()
39 {
40         delete opm;
41 #ifdef SUPPORT_MAME_FM_DLL
42         if(dllchip) {
43                 fmdll->Release(dllchip);
44                 dllchip = NULL;
45                 chip_reference_counter--;
46         }
47         if(fmdll && !chip_reference_counter) {
48                 delete fmdll;
49                 fmdll = NULL;
50         }
51 #endif
52 }
53
54 void YM2151::reset()
55 {
56         touch_sound();
57         opm->Reset();
58 #ifdef SUPPORT_MAME_FM_DLL
59         if(dllchip) {
60                 fmdll->Reset(dllchip);
61         }
62         memset(port_log, 0, sizeof(port_log));
63 #endif
64         timer_event_id = -1;
65         irq_prev = busy = false;
66 }
67
68 void YM2151::write_io8(uint32_t addr, uint32_t data)
69 {
70         if(addr & 1) {
71                 update_count();
72                 this->set_reg(ch, data);
73                 if(ch == 0x14) {
74                         update_event();
75                 }
76                 update_interrupt();
77                 clock_busy = get_current_clock();
78                 busy = true;
79         } else {
80                 ch = data;
81         }
82 }
83
84 uint32_t YM2151::read_io8(uint32_t addr)
85 {
86         if(addr & 1) {
87                 update_count();
88                 update_interrupt();
89                 uint32_t status = opm->ReadStatus() & ~0x80;
90                 if(busy) {
91                         // FIXME: we need to investigate the correct busy period
92                         if(get_passed_usec(clock_busy) < 8) {
93                                 status |= 0x80;
94                         }
95                         busy = false;
96                 }
97                 return status;
98         }
99         return 0xff;
100 }
101
102 void YM2151::write_signal(int id, uint32_t data, uint32_t mask)
103 {
104         if(id == SIG_YM2151_MUTE) {
105                 mute = ((data & mask) != 0);
106         }
107 }
108
109 void YM2151::event_vline(int v, int clock)
110 {
111         update_count();
112         update_interrupt();
113 }
114
115 void YM2151::event_callback(int event_id, int error)
116 {
117         update_count();
118         update_interrupt();
119         timer_event_id = -1;
120         update_event();
121 }
122
123 void YM2151::update_count()
124 {
125         clock_accum += clock_const * get_passed_clock(clock_prev);
126         uint32_t count = clock_accum >> 20;
127         if(count) {
128                 opm->Count(count);
129                 clock_accum -= count << 20;
130         }
131         clock_prev = get_current_clock();
132 }
133
134 void YM2151::update_event()
135 {
136         if(timer_event_id != -1) {
137                 cancel_event(this, timer_event_id);
138                 timer_event_id = -1;
139         }
140         
141         int count = opm->GetNextEvent();
142         if(count > 0) {
143                 register_event(this, EVENT_FM_TIMER, 1000000.0 / (double)chip_clock * (double)count, false, &timer_event_id);
144         }
145 }
146
147 void YM2151::update_interrupt()
148 {
149         bool irq = opm->ReadIRQ();
150         if(!irq_prev && irq) {
151                 write_signals(&outputs_irq, 0xffffffff);
152         } else if(irq_prev && !irq) {
153                 write_signals(&outputs_irq, 0);
154         }
155         irq_prev = irq;
156 }
157
158 void YM2151::mix(int32_t* buffer, int cnt)
159 {
160         if(cnt > 0 && !mute) {
161                 opm->Mix(buffer, cnt);
162 #ifdef SUPPORT_MAME_FM_DLL
163                 if(dllchip) {
164                         fmdll->Mix(dllchip, buffer, cnt);
165                 }
166 #endif
167         }
168 }
169
170 void YM2151::set_volume(int ch, int decibel_l, int decibel_r)
171 {
172         opm->SetVolume(base_decibel + decibel_l, base_decibel + decibel_r);
173 #ifdef SUPPORT_MAME_FM_DLL
174         if(dllchip) {
175                 fmdll->SetVolumeFM(dllchip, base_decibel + decibel_l);
176         }
177 #endif
178 }
179
180 void YM2151::initialize_sound(int rate, int clock, int samples, int decibel)
181 {
182         opm->Init(clock, rate, false);
183         opm->SetVolume(decibel, decibel);
184         base_decibel = decibel;
185         
186 #ifdef SUPPORT_MAME_FM_DLL
187         if(!dont_create_multiple_chips) {
188                 fmdll->Create((LPVOID*)&dllchip, clock, rate);
189                 if(dllchip) {
190                         chip_reference_counter++;
191                         
192                         fmdll->SetVolumeFM(dllchip, decibel);
193                         
194                         DWORD mask = 0;
195                         DWORD dwCaps = fmdll->GetCaps(dllchip);
196                         if((dwCaps & SUPPORT_MULTIPLE) != SUPPORT_MULTIPLE) {
197                                 dont_create_multiple_chips = true;
198                         }
199                         if((dwCaps & SUPPORT_FM_A) == SUPPORT_FM_A) {
200                                 mask = 0x07;
201                         }
202                         if((dwCaps & SUPPORT_FM_B) == SUPPORT_FM_B) {
203                                 mask |= 0x38;
204                         }
205                         if((dwCaps & SUPPORT_FM_C) == SUPPORT_FM_C) {
206                                 mask |= 0xc0;
207                         }
208                         opm->SetChannelMask(mask);
209                         fmdll->SetChannelMask(dllchip, ~mask);
210                 }
211         }
212 #endif
213         
214         chip_clock = clock;
215 }
216
217 void YM2151::set_reg(uint32_t addr, uint32_t data)
218 {
219         touch_sound();
220         opm->SetReg(addr, data);
221 #ifdef SUPPORT_MAME_FM_DLL
222         if(dllchip) {
223                 fmdll->SetReg(dllchip, addr, data);
224         }
225         port_log[addr].written = true;
226         port_log[addr].data = data;
227 #endif
228 }
229
230 void YM2151::update_timing(int new_clocks, double new_frames_per_sec, int new_lines_per_frame)
231 {
232         clock_const = (uint32_t)((double)chip_clock * 1024.0 * 1024.0 / (double)new_clocks + 0.5);
233 }
234
235 #define STATE_VERSION   3
236
237 bool YM2151::process_state(FILEIO* state_fio, bool loading)
238 {
239         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
240                 return false;
241         }
242         if(!state_fio->StateCheckInt32(this_device_id)) {
243                 return false;
244         }
245         if(!opm->ProcessState((void *)state_fio, loading)) {
246                 return false;
247         }
248 #ifdef SUPPORT_MAME_FM_DLL
249         state_fio->StateBuffer(port_log, sizeof(port_log), 1);
250 #endif
251         state_fio->StateInt32(chip_clock);
252         state_fio->StateUint8(ch);
253         state_fio->StateBool(irq_prev);
254         state_fio->StateBool(mute);
255         state_fio->StateUint32(clock_prev);
256         state_fio->StateUint32(clock_accum);
257         state_fio->StateUint32(clock_const);
258         state_fio->StateUint32(clock_busy);
259         state_fio->StateInt32(timer_event_id);
260         state_fio->StateBool(busy);
261         
262 #ifdef SUPPORT_MAME_FM_DLL
263         // post process
264         if(loading && dllchip) {
265                 fmdll->Reset(dllchip);
266                 for(int i = 0; i < 0x100; i++) {
267                         if(port_log[i].written) {
268                                 fmdll->SetReg(dllchip, i, port_log[i].data);
269                         }
270                 }
271         }
272 #endif
273         //touch_sound();
274         return true;
275 }