OSDN Git Service

28339d1c965a9bac4e7061e9f9ad21ae91cae200
[csp-qt/common_source_project-fm7.git] / source / src / vm / ay_3_891x.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2017.02.02-
6
7         [ AY-3-8910 / 8912 / 8913 ]
8         History:
9           2017-02-02: Fork from YM2203.
10 */
11
12 #include "ay_3_891x.h"
13 #include <math.h>
14
15 #define EVENT_PSG_TIMER 0
16
17
18 void AY_3_891X::initialize()
19 {
20         psg = new PSG_AY_3_891X;
21
22         register_vline_event(this);
23         mute = false;
24         clock_prev = clock_accum = clock_busy = 0;
25
26         left_volume = right_volume = 256;
27         v_left_volume = v_right_volume = 256;
28 }
29
30 void AY_3_891X::release()
31 {
32         delete psg;
33 }
34
35 void AY_3_891X::reset()
36 {
37         touch_sound();
38         psg->Reset();
39         fnum2 = 0;
40         
41         // stop timer
42 #ifdef SUPPORT_AY_3_891X_PORT
43         port[0].first = port[1].first = true;
44         port[0].wreg = port[1].wreg = 0;//0xff;
45 #ifdef AY_3_891X_PORT_MODE
46         mode = AY_3_891X_PORT_MODE;
47 #else
48         mode = 0;
49 #endif
50 #endif
51         irq_prev = busy = false;
52 }
53
54 #define amask 1
55
56 void AY_3_891X::write_io8(uint32_t addr, uint32_t data)
57 {
58         switch(addr & amask) {
59         case 0:
60                 ch = data & 0x0f;
61                 break;
62         case 1:
63 #ifdef SUPPORT_AY_3_891X_PORT
64                 if(ch == 7) {
65 #ifdef AY_3_891X_PORT_MODE
66                         mode = (data & 0x3f) | AY_3_891X_PORT_MODE;
67 #else
68                         mode = data;
69 #endif
70                 } else if(ch == 14) {
71 #ifdef SUPPORT_AY_3_891X_PORT_A
72                         if(port[0].wreg != data || port[0].first) {
73                                 write_signals(&port[0].outputs, data);
74                                 port[0].wreg = data;
75                                 port[0].first = false;
76                         }
77 #endif
78                 } else if(ch == 15) {
79 #ifdef SUPPORT_AY_3_891X_PORT_B
80                         if(port[1].wreg != data || port[1].first) {
81                                 write_signals(&port[1].outputs, data);
82                                 port[1].wreg = data;
83                                 port[1].first = false;
84                         }
85 #endif
86                 }
87 #endif
88              {
89                         update_count();
90                 // XM8 version 1.20
91                         this->set_reg(ch, data);
92                 }
93                 break;
94         }
95 }
96
97 uint32_t AY_3_891X::read_io8(uint32_t addr)
98 {
99         switch(addr & amask) {
100         case 1:
101 #ifdef SUPPORT_AY_3_891X_PORT
102                 if(ch == 14) {
103 #ifdef SUPPORT_AY_3_891X_PORT_A
104                         return (mode & 0x40) ? port[0].wreg : port[0].rreg;
105 #endif
106                 } else if(ch == 15) {
107 #ifdef SUPPORT_AY_3_891X_PORT_B
108                         return (mode & 0x80) ? port[1].wreg : port[1].rreg;
109 #endif
110                 }
111 #endif
112                 return psg->GetReg(ch);
113         }
114         return 0xff;
115 }
116
117 void AY_3_891X::write_signal(int id, uint32_t data, uint32_t mask)
118 {
119         if(id == SIG_AY_3_891X_MUTE) {
120                 mute = ((data & mask) != 0);
121 #ifdef SUPPORT_AY_3_891X_PORT_A
122         } else if(id == SIG_AY_3_891X_PORT_A) {
123                 port[0].rreg = (port[0].rreg & ~mask) | (data & mask);
124 #endif
125 #ifdef SUPPORT_AY_3_891X_PORT_B
126         } else if(id == SIG_AY_3_891X_PORT_B) {
127                 port[1].rreg = (port[1].rreg & ~mask) | (data & mask);
128 #endif
129         }
130 }
131
132 void AY_3_891X::event_vline(int v, int clock)
133 {
134         update_count();
135 }
136
137 void AY_3_891X::event_callback(int event_id, int error)
138 {
139         update_count();
140         //timer_event_id = -1;
141         update_event();
142 }
143
144 void AY_3_891X::update_count()
145 {
146         clock_accum += clock_const * get_passed_clock(clock_prev);
147         uint32_t count = clock_accum >> 20;
148         if(count) {
149                 //psg->Count(count);
150                 clock_accum -= count << 20;
151         }
152         clock_prev = get_current_clock();
153 }
154
155 void AY_3_891X::update_event()
156 {
157         //if(timer_event_id != -1) {
158         //      cancel_event(this, timer_event_id);
159         //      timer_event_id = -1;
160         //}
161         
162         //int count;
163         //count = psg->GetNextEvent();
164         
165         //if(count > 0) {
166         //      register_event(this, EVENT_PSG_TIMER, 1000000.0 / (double)chip_clock * (double)count, false, &timer_event_id);
167         //}
168 }
169
170
171 inline int32_t VCALC(int32_t x, int32_t y)
172 {
173         x = x * y;
174         x = x >> 8;
175         return x;
176 }
177
178 inline int32_t SATURATION_ADD(int32_t x, int32_t y)
179 {
180         x = x + y;
181         if(x < -0x8000) x = -0x8000;
182         if(x >  0x7fff) x =  0x7fff;
183         return x;
184 }
185
186
187 void AY_3_891X::mix(int32_t* buffer, int cnt)
188 {
189         if(cnt > 0 && !mute) {
190                 int32_t *dbuffer = (int32_t *)malloc((cnt * 2 + 2) * sizeof(int32_t));
191                 memset((void *)dbuffer, 0x00, (cnt * 2 + 2) * sizeof(int32_t));
192            
193                 psg->Mix(dbuffer, cnt);
194                 int32_t *p = dbuffer;
195                 int32_t *q = buffer;
196                 int32_t tmp[8];
197                 int32_t tvol[8] = {v_left_volume, v_right_volume,
198                                  v_left_volume, v_right_volume,
199                                  v_left_volume, v_right_volume,
200                                  v_left_volume, v_right_volume};
201                 int i;
202                 // More EXCEPTS to optimize to SIMD features.
203                 for(i = 0; i < cnt / 4; i++) {
204                         tmp[0] = VCALC(p[0], tvol[0]);
205                         tmp[1] = VCALC(p[1], tvol[1]);
206                         tmp[2] = VCALC(p[2], tvol[2]);
207                         tmp[3] = VCALC(p[3], tvol[3]);
208                         tmp[4] = VCALC(p[4], tvol[4]);
209                         tmp[5] = VCALC(p[5], tvol[5]);
210                         tmp[6] = VCALC(p[6], tvol[6]);
211                         tmp[7] = VCALC(p[7], tvol[7]);
212
213                         q[0] = SATURATION_ADD(q[0], tmp[0]);
214                         q[1] = SATURATION_ADD(q[1], tmp[1]);
215                         q[2] = SATURATION_ADD(q[2], tmp[2]);
216                         q[3] = SATURATION_ADD(q[3], tmp[3]);
217                    
218                         q[4] = SATURATION_ADD(q[4], tmp[4]);
219                         q[5] = SATURATION_ADD(q[5], tmp[5]);
220                         q[6] = SATURATION_ADD(q[6], tmp[6]);
221                         q[7] = SATURATION_ADD(q[7], tmp[7]);
222                         q += 8;
223                         p += 8;
224                 }
225                 if((cnt & 3) != 0) {
226                         for(i = 0; i < (cnt & 3); i++) {
227                                 tmp[0] = VCALC(p[0], tvol[0]);
228                                 tmp[1] = VCALC(p[1], tvol[1]);
229                            
230                                 q[0] = SATURATION_ADD(q[0], tmp[0]);
231                                 q[1] = SATURATION_ADD(q[1], tmp[1]);
232                                 q += 2;
233                                 p += 2;
234                         }
235                 }
236                 free(dbuffer);
237         }
238 }
239
240 void AY_3_891X::set_volume(int ch, int decibel_l, int decibel_r)
241 {
242         v_right_volume = (int)(pow(10.0, (double)decibel_vol / 10.0) * (double)right_volume);
243         v_left_volume = (int)(pow(10.0, (double)decibel_vol / 10.0) * (double)left_volume);
244         if(ch == 0) {
245                 psg->SetVolume(base_decibel_psg + decibel_l, base_decibel_psg + decibel_r);
246         }
247 }
248
249 void AY_3_891X::initialize_sound(int rate, int clock, int samples, int decibel_fm, int decibel_psg)
250 {
251         //psg->Init(clock, rate, false, NULL);
252         psg->Init(clock, rate);
253         psg->SetVolume(decibel_psg, decibel_psg);
254         base_decibel_psg = decibel_psg;
255         chip_clock = clock;
256 }
257
258 void AY_3_891X::set_reg(uint32_t addr, uint32_t data)
259 {
260         touch_sound();
261         if((addr >= 0x2d) && (addr <= 0x2f)) {
262                 psg->SetPrescaler(addr - 0x2d);
263                 return;
264         }
265         psg->SetReg(addr, data);
266 }
267
268 void AY_3_891X::update_timing(int new_clocks, double new_frames_per_sec, int new_lines_per_frame)
269 {
270         clock_const = (uint32_t)((double)chip_clock * 1024.0 * 1024.0 / (double)new_clocks + 0.5);
271 }
272
273 #define STATE_VERSION   1
274
275 void AY_3_891X::save_state(FILEIO* state_fio)
276 {
277         state_fio->FputUint32(STATE_VERSION);
278         state_fio->FputInt32(this_device_id);
279         psg->SaveState((void *)state_fio);
280         state_fio->FputUint8(ch);
281         state_fio->FputUint8(fnum2);
282 #ifdef SUPPORT_AY_3_891X_PORT
283         for(int i = 0; i < 2; i++) {
284                 state_fio->FputUint8(port[i].wreg);
285                 state_fio->FputUint8(port[i].rreg);
286                 state_fio->FputBool(port[i].first);
287         }
288         state_fio->FputUint8(mode);
289 #endif
290         state_fio->FputInt32(chip_clock);
291         state_fio->FputBool(irq_prev);
292         state_fio->FputBool(mute);
293         state_fio->FputUint32(clock_prev);
294         state_fio->FputUint32(clock_accum);
295         state_fio->FputUint32(clock_const);
296         state_fio->FputUint32(clock_busy);
297         //state_fio->FputInt32(timer_event_id);
298         state_fio->FputBool(busy);
299         state_fio->FputInt32(decibel_vol);
300         state_fio->FputInt32(left_volume);
301         state_fio->FputInt32(right_volume);
302         state_fio->FputInt32(v_left_volume);
303         state_fio->FputInt32(v_right_volume);
304 }
305
306 bool AY_3_891X::load_state(FILEIO* state_fio)
307 {
308         if(state_fio->FgetUint32() != STATE_VERSION) {
309                 return false;
310         }
311         if(state_fio->FgetInt32() != this_device_id) {
312                 return false;
313         }
314         if(!psg->LoadState((void *)state_fio)) {
315                 return false;
316         }
317         ch = state_fio->FgetUint8();
318         fnum2 = state_fio->FgetUint8();
319 #ifdef SUPPORT_AY_3_891X_PORT
320         for(int i = 0; i < 2; i++) {
321                 port[i].wreg = state_fio->FgetUint8();
322                 port[i].rreg = state_fio->FgetUint8();
323                 port[i].first = state_fio->FgetBool();
324         }
325         mode = state_fio->FgetUint8();
326 #endif
327         chip_clock = state_fio->FgetInt32();
328         irq_prev = state_fio->FgetBool();
329         mute = state_fio->FgetBool();
330         clock_prev = state_fio->FgetUint32();
331         clock_accum = state_fio->FgetUint32();
332         clock_const = state_fio->FgetUint32();
333         clock_busy = state_fio->FgetUint32();
334         //timer_event_id = state_fio->FgetInt32();
335         busy = state_fio->FgetBool();
336
337         decibel_vol = state_fio->FgetInt32();
338         left_volume = state_fio->FgetInt32();
339         right_volume = state_fio->FgetInt32();
340         v_left_volume = state_fio->FgetInt32();
341         v_right_volume = state_fio->FgetInt32();
342         //touch_sound();
343
344         return true;
345 }
346