OSDN Git Service

[VM][Qt] Add SANYOPHC-20, PHC-25 and SEIKO MAP-1010 .
[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 static bool dont_create_multiple_chips = false;
16 #endif
17
18 void YM2151::initialize()
19 {
20         opm = new FM::OPM;
21 #ifdef SUPPORT_MAME_FM_DLL
22 //      fmdll = new CFMDLL(_T("mamefm.dll"));
23         fmdll = new CFMDLL(config.fmgen_dll_path);
24         dllchip = NULL;
25 #endif
26         register_vline_event(this);
27         mute = false;
28         clock_prev = clock_accum = clock_busy = 0;
29 }
30
31 void YM2151::release()
32 {
33         delete opm;
34 #ifdef SUPPORT_MAME_FM_DLL
35         if(dllchip) {
36                 fmdll->Release(dllchip);
37         }
38         delete fmdll;
39 #endif
40 }
41
42 void YM2151::reset()
43 {
44         opm->Reset();
45 #ifdef SUPPORT_MAME_FM_DLL
46         if(dllchip) {
47                 fmdll->Reset(dllchip);
48         }
49         memset(port_log, 0, sizeof(port_log));
50 #endif
51         timer_event_id = -1;
52         irq_prev = busy = false;
53 }
54
55 void YM2151::write_io8(uint32 addr, uint32 data)
56 {
57         if(addr & 1) {
58                 update_count();
59                 SetReg(ch, data);
60                 if(ch == 0x14) {
61                         update_event();
62                 }
63                 update_interrupt();
64                 clock_busy = current_clock();
65                 busy = true;
66         } else {
67                 ch = data;
68         }
69 }
70
71 uint32 YM2151::read_io8(uint32 addr)
72 {
73         if(addr & 1) {
74                 update_count();
75                 update_interrupt();
76                 uint32 status = opm->ReadStatus() & ~0x80;
77                 if(busy) {
78                         // FIXME: we need to investigate the correct busy period
79                         if(passed_usec(clock_busy) < 8) {
80                                 status |= 0x80;
81                         }
82                         busy = false;
83                 }
84                 return status;
85         }
86         return 0xff;
87 }
88
89 void YM2151::write_signal(int id, uint32 data, uint32 mask)
90 {
91         if(id == SIG_YM2151_MUTE) {
92                 mute = ((data & mask) != 0);
93         }
94 }
95
96 void YM2151::event_vline(int v, int clock)
97 {
98         update_count();
99         update_interrupt();
100 }
101
102 void YM2151::event_callback(int event_id, int error)
103 {
104         update_count();
105         update_interrupt();
106         timer_event_id = -1;
107         update_event();
108 }
109
110 void YM2151::update_count()
111 {
112         clock_accum += clock_const * passed_clock(clock_prev);
113         uint32 count = clock_accum >> 20;
114         if(count) {
115                 opm->Count(count);
116                 clock_accum -= count << 20;
117         }
118         clock_prev = current_clock();
119 }
120
121 void YM2151::update_event()
122 {
123         if(timer_event_id != -1) {
124                 cancel_event(this, timer_event_id);
125                 timer_event_id = -1;
126         }
127         
128         int count = opm->GetNextEvent();
129         if(count > 0) {
130                 register_event(this, EVENT_FM_TIMER, 1000000.0 / (double)chip_clock * (double)count, false, &timer_event_id);
131         }
132 }
133
134 void YM2151::update_interrupt()
135 {
136         bool irq = opm->ReadIRQ();
137         if(!irq_prev && irq) {
138                 write_signals(&outputs_irq, 0xffffffff);
139         } else if(irq_prev && !irq) {
140                 write_signals(&outputs_irq, 0);
141         }
142         irq_prev = irq;
143 }
144
145 void YM2151::mix(int32* buffer, int cnt)
146 {
147         if(cnt > 0 && !mute) {
148                 opm->Mix(buffer, cnt);
149 #ifdef SUPPORT_MAME_FM_DLL
150                 if(dllchip) {
151                         fmdll->Mix(dllchip, buffer, cnt);
152                 }
153 #endif
154         }
155 }
156
157 void YM2151::init(int rate, int clock, int samples, int vol)
158 {
159         opm->Init(clock, rate, false);
160         opm->SetVolume(vol);
161 #ifdef SUPPORT_MAME_FM_DLL
162         if(!dont_create_multiple_chips) {
163                 fmdll->Create((LPVOID*)&dllchip, clock, rate);
164                 if(dllchip) {
165                         fmdll->SetVolumeFM(dllchip, vol);
166                         
167                         DWORD mask = 0;
168                         DWORD dwCaps = fmdll->GetCaps(dllchip);
169                         if((dwCaps & SUPPORT_MULTIPLE) != SUPPORT_MULTIPLE) {
170                                 dont_create_multiple_chips = true;
171                         }
172                         if((dwCaps & SUPPORT_FM_A) == SUPPORT_FM_A) {
173                                 mask = 0x07;
174                         }
175                         if((dwCaps & SUPPORT_FM_B) == SUPPORT_FM_B) {
176                                 mask |= 0x38;
177                         }
178                         if((dwCaps & SUPPORT_FM_C) == SUPPORT_FM_C) {
179                                 mask |= 0xc0;
180                         }
181                         opm->SetChannelMask(mask);
182                         fmdll->SetChannelMask(dllchip, ~mask);
183                 }
184         }
185 #endif
186         
187         chip_clock = clock;
188 }
189
190 void YM2151::SetReg(uint addr, uint data)
191 {
192         opm->SetReg(addr, data);
193 #ifdef SUPPORT_MAME_FM_DLL
194         if(dllchip) {
195                 fmdll->SetReg(dllchip, addr, data);
196         }
197         port_log[addr].written = true;
198         port_log[addr].data = data;
199 #endif
200 }
201
202 void YM2151::update_timing(int new_clocks, double new_frames_per_sec, int new_lines_per_frame)
203 {
204         clock_const = (uint32)((double)chip_clock * 1024.0 * 1024.0 / (double)new_clocks + 0.5);
205 }
206
207 #define STATE_VERSION   1
208
209 void YM2151::save_state(FILEIO* state_fio)
210 {
211         state_fio->FputUint32(STATE_VERSION);
212         state_fio->FputInt32(this_device_id);
213         
214         opm->SaveState((void *)state_fio);
215 #ifdef SUPPORT_MAME_FM_DLL
216         state_fio->Fwrite(port_log, sizeof(port_log), 1);
217 #endif
218         state_fio->FputInt32(chip_clock);
219         state_fio->FputUint8(ch);
220         state_fio->FputBool(irq_prev);
221         state_fio->FputBool(mute);
222         state_fio->FputUint32(clock_prev);
223         state_fio->FputUint32(clock_accum);
224         state_fio->FputUint32(clock_const);
225         state_fio->FputUint32(clock_busy);
226         state_fio->FputBool(busy);
227 }
228
229 bool YM2151::load_state(FILEIO* state_fio)
230 {
231         if(state_fio->FgetUint32() != STATE_VERSION) {
232                 return false;
233         }
234         if(state_fio->FgetInt32() != this_device_id) {
235                 return false;
236         }
237         if(!opm->LoadState((void *)state_fio)) {
238                 return false;
239         }
240 #ifdef SUPPORT_MAME_FM_DLL
241         state_fio->Fread(port_log, sizeof(port_log), 1);
242 #endif
243         chip_clock = state_fio->FgetInt32();
244         ch = state_fio->FgetUint8();
245         irq_prev = state_fio->FgetBool();
246         mute = state_fio->FgetBool();
247         clock_prev = state_fio->FgetUint32();
248         clock_accum = state_fio->FgetUint32();
249         clock_const = state_fio->FgetUint32();
250         clock_busy = state_fio->FgetUint32();
251         busy = state_fio->FgetBool();
252         
253 #ifdef SUPPORT_MAME_FM_DLL
254         // post process
255         if(dllchip) {
256                 fmdll->Reset(dllchip);
257                 for(int i = 0; i < 0x100; i++) {
258                         if(port_log[i].written) {
259                                 fmdll->SetReg(dllchip, i, port_log[i].data);
260                         }
261                 }
262         }
263 #endif
264         return true;
265 }
266