OSDN Git Service

[General] Merge from upstream version, 2015-01-28.
[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         [ AY-3-8910 / YM2203 / YM2608 ]
8 */
9
10 #include "ym2203.h"
11 #include "../fileio.h"
12
13 void YM2203::initialize()
14 {
15 #ifdef HAS_YM2608
16         if(is_ym2608) {
17                 opna = new FM::OPNA;
18         } else
19 #endif
20         opn = new FM::OPN;
21
22 #ifdef SUPPORT_MAME_FM_DLL
23
24    //   fmdll = new CFMDLL(_T("mamefm.dll"));
25         fmdll = new CFMDLL(config.fmgen_dll_path);
26         dllchip = NULL;
27 #endif
28         register_vline_event(this);
29         mute = false;
30         clock_prev = clock_accum = clock_busy = 0;
31 }
32
33 void YM2203::release()
34 {
35 #ifdef HAS_YM2608
36         if(is_ym2608) {
37                 delete opna;
38         } else
39 #endif
40         delete opn;
41 #ifdef SUPPORT_MAME_FM_DLL
42         if(dllchip) {
43                 fmdll->Release(dllchip);
44         }
45         delete fmdll;
46 #endif
47 }
48
49 void YM2203::reset()
50 {
51 #ifdef HAS_YM2608
52         if(is_ym2608) {
53                 opna->Reset();
54         } else
55 #endif
56         opn->Reset();
57 #ifdef SUPPORT_MAME_FM_DLL
58         if(dllchip) {
59                 fmdll->Reset(dllchip);
60         }
61         memset(port_log, 0, sizeof(port_log));
62 #endif
63         this->SetReg(0x27, 0); // stop timer
64         
65 #ifdef SUPPORT_YM2203_PORT
66         port[0].first = port[1].first = true;
67 #endif
68         irq_prev = busy = false;
69 }
70
71 #ifdef HAS_YM2608
72 #define amask (is_ym2608 ? 3 : 1)
73 #else
74 #define amask 1
75 #endif
76
77 void YM2203::write_io8(uint32 addr, uint32 data)
78 {
79         switch(addr & amask) {
80         case 0:
81 #ifdef HAS_YM_SERIES
82                 ch = data;
83                 // write dummy data for prescaler
84                 if(0x2d <= ch && ch <= 0x2f) {
85                         update_count();
86                         this->SetReg(ch, 0);
87                         update_interrupt();
88                         clock_busy = current_clock();
89                         busy = true;
90                 }
91 #else
92                 ch = data & 0x0f;
93 #endif
94                 break;
95         case 1:
96 #ifdef SUPPORT_YM2203_PORT
97                 if(ch == 7) {
98 #ifdef YM2203_PORT_MODE
99                         mode = (data & 0x3f) | YM2203_PORT_MODE;
100 #else
101                         mode = data;
102 #endif
103                 } else if(ch == 14) {
104 #ifdef SUPPORT_YM2203_PORT_A
105                         if(port[0].wreg != data || port[0].first) {
106                                 write_signals(&port[0].outputs, data);
107                                 port[0].wreg = data;
108                                 port[0].first = false;
109                         }
110 #endif
111                 } else if(ch == 15) {
112 #ifdef SUPPORT_YM2203_PORT_B
113                         if(port[1].wreg != data || port[1].first) {
114                                 write_signals(&port[1].outputs, data);
115                                 port[1].wreg = data;
116                                 port[1].first = false;
117                         }
118 #endif
119                 }
120 #endif
121                 // don't write again for prescaler
122                 if(!(0x2d <= ch && ch <= 0x2f)) {
123                         update_count();
124                         this->SetReg(ch, data);
125 #ifdef HAS_YM_SERIES
126                         update_interrupt();
127                         clock_busy = current_clock();
128                         busy = true;
129 #endif
130                 }
131                 break;
132 #ifdef HAS_YM2608
133         case 2:
134                 ch1 = data1 = data;
135                 break;
136         case 3:
137                 update_count();
138                 this->SetReg(0x100 | ch1, data);
139                 data1 = data;
140                 update_interrupt();
141                 break;
142 #endif
143         }
144 }
145
146 uint32 YM2203::read_io8(uint32 addr)
147 {
148         switch(addr & amask) {
149 #ifdef HAS_YM_SERIES
150         case 0:
151                 {
152                         /* BUSY : x : x : x : x : x : FLAGB : FLAGA */
153                         update_count();
154                         update_interrupt();
155                         uint32 status;
156 #ifdef HAS_YM2608
157                         if(is_ym2608) {
158                                 status = opna->ReadStatus() & ~0x80;
159                         } else
160 #endif
161                         status = opn->ReadStatus() & ~0x80;
162                         if(busy) {
163                                 // FIXME: we need to investigate the correct busy period
164                                 if(passed_usec(clock_busy) < 8) {
165                                         status |= 0x80;
166                                 }
167                                 busy = false;
168                         }
169                         return status;
170                 }
171 #endif
172         case 1:
173 #ifdef SUPPORT_YM2203_PORT
174                 if(ch == 14) {
175 #ifdef SUPPORT_YM2203_PORT_A
176                         return (mode & 0x40) ? port[0].wreg : port[0].rreg;
177 #endif
178                 } else if(ch == 15) {
179 #ifdef SUPPORT_YM2203_PORT_B
180                         return (mode & 0x80) ? port[1].wreg : port[1].rreg;
181 #endif
182                 }
183 #endif
184 #ifdef HAS_YM2608
185                 if(is_ym2608) {
186                         return opna->GetReg(ch);
187                 } else
188 #endif
189                 return opn->GetReg(ch);
190 #ifdef HAS_YM2608
191         case 2:
192                 {
193                         /* BUSY : x : PCMBUSY : ZERO : BRDY : EOS : FLAGB : FLAGA */
194                         update_count();
195                         update_interrupt();
196                         uint32 status = opna->ReadStatusEx() & ~0x80;
197                         if(busy) {
198                                 // FIXME: we need to investigate the correct busy period
199                                 if(passed_usec(clock_busy) < 8) {
200                                         status |= 0x80;
201                                 }
202                                 busy = false;
203                         }
204                         return status;
205                 }
206         case 3:
207                 if(ch1 == 8) {
208                         return opna->GetReg(0x100 | ch1);
209 //              } else if(ch1 == 0x0f) {
210 //                      return 0x80; // from mame fm.c
211                 }
212                 return data1;
213 #endif
214         }
215         return 0xff;
216 }
217
218 void YM2203::write_signal(int id, uint32 data, uint32 mask)
219 {
220         if(id == SIG_YM2203_MUTE) {
221                 mute = ((data & mask) != 0);
222 #ifdef SUPPORT_YM2203_PORT_A
223         } else if(id == SIG_YM2203_PORT_A) {
224                 port[0].rreg = (port[0].rreg & ~mask) | (data & mask);
225 #endif
226 #ifdef SUPPORT_YM2203_PORT_B
227         } else if(id == SIG_YM2203_PORT_B) {
228                 port[1].rreg = (port[1].rreg & ~mask) | (data & mask);
229 #endif
230         }
231 }
232
233 void YM2203::event_vline(int v, int clock)
234 {
235         update_count();
236 #ifdef HAS_YM_SERIES
237         update_interrupt();
238 #endif
239 }
240
241 void YM2203::update_count()
242 {
243         clock_accum += clock_const * passed_clock(clock_prev);
244         uint32 count = clock_accum >> 20;
245         if(count) {
246 #ifdef HAS_YM2608
247                 if(is_ym2608) {
248                         opna->Count(count);
249                 } else
250 #endif
251                 opn->Count(count);
252                 clock_accum -= count << 20;
253         }
254         clock_prev = current_clock();
255 }
256
257 #ifdef HAS_YM_SERIES
258 void YM2203::update_interrupt()
259 {
260         bool irq;
261 #ifdef HAS_YM2608
262         if(is_ym2608) {
263                 irq = opna->ReadIRQ();
264         } else
265 #endif
266         irq = opn->ReadIRQ();
267         if(!irq_prev && irq) {
268                 write_signals(&outputs_irq, 0xffffffff);
269         } else if(irq_prev && !irq) {
270                 write_signals(&outputs_irq, 0);
271         }
272         irq_prev = irq;
273 }
274 #endif
275
276 void YM2203::mix(int32* buffer, int cnt)
277 {
278         if(cnt > 0 && !mute) {
279 #ifdef HAS_YM2608
280                 if(is_ym2608) {
281                         opna->Mix(buffer, cnt);
282                 } else
283 #endif
284                 opn->Mix(buffer, cnt);
285 #ifdef SUPPORT_MAME_FM_DLL
286                 if(dllchip) {
287                         fmdll->Mix(dllchip, buffer, cnt);
288                 }
289 #endif
290         }
291 }
292
293 void YM2203::init(int rate, int clock, int samples, int volf, int volp)
294 {
295 #ifdef HAS_YM2608
296         if(is_ym2608) {
297                 opna->Init(clock, rate, false, emu->application_path());
298                 opna->SetVolumeFM(volf);
299                 opna->SetVolumePSG(volp);
300         } else {
301 #endif
302                 opn->Init(clock, rate, false, NULL);
303                 opn->SetVolumeFM(volf);
304                 opn->SetVolumePSG(volp);
305 #ifdef HAS_YM2608
306         }
307 #endif
308         
309 #ifdef SUPPORT_MAME_FM_DLL
310 #ifdef HAS_YM2608
311         if(is_ym2608) {
312                 fmdll->Create((LPVOID*)&dllchip, clock, rate);
313         } else
314 #endif
315         fmdll->Create((LPVOID*)&dllchip, clock * 2, rate);
316         if(dllchip) {
317                 fmdll->SetVolumeFM(dllchip, volf);
318                 fmdll->SetVolumePSG(dllchip, volp);
319                 
320                 DWORD mask = 0;
321                 DWORD dwCaps = fmdll->GetCaps(dllchip);
322                 if((dwCaps & SUPPORT_FM_A) == SUPPORT_FM_A) {
323                         mask = 0x07;
324                 }
325                 if((dwCaps & SUPPORT_FM_B) == SUPPORT_FM_B) {
326                         mask |= 0x38;
327                 }
328                 if((dwCaps & SUPPORT_PSG) == SUPPORT_PSG) {
329                         mask |= 0x1c0;
330                 }
331                 if((dwCaps & SUPPORT_ADPCM_B) == SUPPORT_ADPCM_B) {
332                         mask |= 0x200;
333                 }
334                 if((dwCaps & SUPPORT_RHYTHM) == SUPPORT_RHYTHM) {
335                         mask |= 0xfc00;
336                 }
337 #ifdef HAS_YM2608
338                 if(is_ym2608) {
339                         opna->SetChannelMask(mask);
340                 } else
341 #endif
342                 opn->SetChannelMask(mask);
343                 fmdll->SetChannelMask(dllchip, ~mask);
344         }
345 #endif
346         chip_clock = clock;
347 }
348
349 void YM2203::SetReg(uint addr, uint data)
350 {
351 #ifdef HAS_YM2608
352         if(is_ym2608) {
353                 opna->SetReg(addr, data);
354         } else
355 #endif
356         opn->SetReg(addr, data);
357 #ifdef SUPPORT_MAME_FM_DLL
358         if(dllchip) {
359                 fmdll->SetReg(dllchip, addr, data);
360         }
361         if(0x2d <= addr && addr <= 0x2f) {
362                 port_log[0x2d].written = port_log[0x2e].written = port_log[0x2f].written = false;
363         }
364         port_log[addr].written = true;
365         port_log[addr].data = data;
366 #endif
367 }
368
369 void YM2203::update_timing(int new_clocks, double new_frames_per_sec, int new_lines_per_frame)
370 {
371 #ifdef HAS_YM2608
372         if(is_ym2608) {
373                 clock_const = (uint32)((double)chip_clock * 1024.0 * 1024.0 / (double)new_clocks / 2.0 + 0.5);
374         } else
375 #endif
376         clock_const = (uint32)((double)chip_clock * 1024.0 * 1024.0 / (double)new_clocks + 0.5);
377 }
378
379 #define STATE_VERSION   2
380
381 void YM2203::save_state(FILEIO* state_fio)
382 {
383         state_fio->FputUint32(STATE_VERSION);
384         state_fio->FputInt32(this_device_id);
385         
386 #ifdef HAS_YM2608
387         if(is_ym2608) {
388                 opna->SaveState((void *)state_fio);;
389         } else
390 #endif
391         opn->SaveState((void *)state_fio);
392 #ifdef SUPPORT_MAME_FM_DLL
393         state_fio->Fwrite(port_log, sizeof(port_log), 1);
394 #endif
395         state_fio->FputUint8(ch);
396 #ifdef SUPPORT_YM2203_PORT
397         state_fio->FputUint8(mode);
398 #endif
399 #ifdef HAS_YM2608
400         state_fio->FputUint8(ch1);
401         state_fio->FputUint8(data1);
402 #endif
403 #ifdef SUPPORT_YM2203_PORT
404         for(int i = 0; i < 2; i++) {
405                 state_fio->FputUint8(port[i].wreg);
406                 state_fio->FputUint8(port[i].rreg);
407                 state_fio->FputBool(port[i].first);
408         }
409 #endif
410         state_fio->FputInt32(chip_clock);
411         state_fio->FputBool(irq_prev);
412         state_fio->FputBool(mute);
413         state_fio->FputUint32(clock_prev);
414         state_fio->FputUint32(clock_accum);
415         state_fio->FputUint32(clock_const);
416         state_fio->FputUint32(clock_busy);
417         state_fio->FputBool(busy);
418 }
419
420 bool YM2203::load_state(FILEIO* state_fio)
421 {
422         if(state_fio->FgetUint32() != STATE_VERSION) {
423                 return false;
424         }
425         if(state_fio->FgetInt32() != this_device_id) {
426                 return false;
427         }
428 #ifdef HAS_YM2608
429         if(is_ym2608) {
430                 if(!opna->LoadState((void *)state_fio)) {
431                         return false;
432                 }
433         } else {
434 #endif
435                 if(!opn->LoadState((void *)state_fio)) {
436                         return false;
437                 }
438 #ifdef HAS_YM2608
439         }
440 #endif
441 #ifdef SUPPORT_MAME_FM_DLL
442         state_fio->Fread(port_log, sizeof(port_log), 1);
443 #endif
444         ch = state_fio->FgetUint8();
445 #ifdef SUPPORT_YM2203_PORT
446         mode = state_fio->FgetUint8();
447 #endif
448 #ifdef HAS_YM2608
449         ch1 = state_fio->FgetUint8();
450         data1 = state_fio->FgetUint8();
451 #endif
452 #ifdef SUPPORT_YM2203_PORT
453         for(int i = 0; i < 2; i++) {
454                 port[i].wreg = state_fio->FgetUint8();
455                 port[i].rreg = state_fio->FgetUint8();
456                 port[i].first = state_fio->FgetBool();
457         }
458 #endif
459         chip_clock = state_fio->FgetInt32();
460         irq_prev = state_fio->FgetBool();
461         mute = state_fio->FgetBool();
462         clock_prev = state_fio->FgetUint32();
463         clock_accum = state_fio->FgetUint32();
464         clock_const = state_fio->FgetUint32();
465         clock_busy = state_fio->FgetUint32();
466         busy = state_fio->FgetBool();
467    
468 #ifdef SUPPORT_MAME_FM_DLL
469         // post process
470         if(dllchip) {
471                 fmdll->Reset(dllchip);
472                 for(int i = 0; i < 0x200; i++) {
473                         if(port_log[i].written) {
474                                 fmdll->SetReg(dllchip, i, port_log[i].data);
475                         }
476                 }
477         }
478 #endif
479         return true;
480 }
481