OSDN Git Service

[VM][PC9801][MEMBUS] Split update_bios() to functions.
[csp-qt/common_source_project-fm7.git] / source / src / vm / msm5205.cpp
1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /*
4  *   streaming ADPCM driver
5  *   by Aaron Giles
6  *
7  *   Library to transcode from an ADPCM source to raw PCM.
8  *   Written by Buffoni Mirko in 08/06/97
9  *   References: various sources and documents.
10  *
11  *   HJB 08/31/98
12  *   modified to use an automatically selected oversampling factor
13  *   for the current sample rate
14  *
15  *   01/06/99
16  *    separate MSM5205 emulator form adpcm.c and some fix
17  *
18  *   07/29/12
19  *    added basic support for the MSM6585
20  */
21
22 #include <math.h>
23 #include "msm5205.h"
24
25 #define EVENT_TIMER     0
26
27 /*
28
29     MSM 5205 ADPCM chip:
30
31     Data is streamed from a CPU by means of a clock generated on the chip.
32
33     A reset signal is set high or low to determine whether playback (and interrupts) are occurring.
34
35   MSM6585: is an upgraded MSM5205 voice synth IC.
36    Improvements:
37     More precise internal DA converter
38     Built in low-pass filter
39     Expanded sampling frequency
40
41    Differences between MSM6585 & MSM5205:
42
43                               MSM6586          MSM5205
44     Master clock frequency    640kHz           384kHz
45     Sampling frequency        4k/8k/16k/32kHz  4k/6k/8kHz
46     ADPCM bit length          4-bit            3-bit/4-bit
47     DA converter              12-bit           10-bit
48     Low-pass filter           -40dB/oct        N/A
49     Overflow prevent circuit  Included         N/A
50
51     Timer callback at VCLK low edge on MSM5205 (at rising edge on MSM6585)
52
53    TODO:
54    - lowpass filter for MSM6585
55
56  */
57
58 void MSM5205::initialize()
59 {
60         DEVICE::initialize();
61 //      m_mod_clock = clock();
62 //      m_vclk_cb.resolve();
63
64         /* compute the difference tables */
65         compute_tables();
66
67         /* stream system initialize */
68 //      m_stream = machine().sound().stream_alloc(*this, 0, 1, clock());
69 //      m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(msm5205_device::vclk_callback), this));
70         m_timer = -1;
71 }
72
73 //-------------------------------------------------
74 //  device_reset - device-specific reset
75 //-------------------------------------------------
76
77 void MSM5205::reset()
78 {
79         /* initialize work */
80         m_data    = 0;
81         m_vclk    = false;
82         m_reset   = false;
83         m_signal  = 0;
84         m_step    = 0;
85         m_ignore = false;
86         /* initialize clock */
87         change_clock_w(m_mod_clock);
88
89         /* timer and bitwidth set */
90         playmode_w(m_select);
91 }
92
93
94 /*
95  * ADPCM lookup table
96  */
97
98 /* step size index shift table */
99 static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
100
101 /*
102  *   Compute the difference table
103  */
104
105 void MSM5205::compute_tables()
106 {
107         /* nibble to bit map */
108         static const int nbl2bit[16][4] =
109         {
110                 { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
111                 { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
112                 {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
113                 {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
114         };
115
116         int step, nib;
117
118         /* loop over all possible steps */
119         for (step = 0; step <= 48; step++)
120         {
121                 /* compute the step value */
122                 int stepval = (int)floor (16.0 * pow (11.0 / 10.0, (double)step));
123
124                 /* loop over all nibbles and compute the difference */
125                 for (nib = 0; nib < 16; nib++)
126                 {
127                         m_diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
128                                 (stepval   * nbl2bit[nib][1] +
129                                         stepval/2 * nbl2bit[nib][2] +
130                                         stepval/4 * nbl2bit[nib][3] +
131                                         stepval/8);
132                 }
133         }
134 }
135
136 /* timer callback at VCLK low edge on MSM5205 (at rising edge on MSM6585) */
137 // Change: Check edge value via m_vclk, to be half cycle.
138 void MSM5205::event_callback(int event_id, int err)
139 {
140         if(event_id == EVENT_TIMER)
141         {
142                 int val;
143                 int new_signal = m_signal;
144
145                 /* callback user handler and latch next data */
146                 m_vclk = !m_vclk;
147                 write_signals(&m_vclk_cb, (m_vclk) ? 0xffffffff : 0x00000000);
148
149                 /* reset check at last hiedge of VCLK */
150                 if (m_reset)
151                 {
152                         new_signal = 0;
153                         m_step = 0;
154                 }
155                 else {
156                         if(m_vclk) return; // If MSM6585 Invert level.
157                         /* update signal */
158                         /* !! MSM5205 has internal 12bit decoding, signal width is 0 to 8191 !! */
159                         if(!(m_ignore)) {
160                                 val = m_data;
161                                 new_signal = m_signal + m_diff_lookup[m_step * 16 + (val & 15)];
162                                 if (new_signal > 2047) new_signal = 2047;
163                                 else if (new_signal < -2048) new_signal = -2048;
164                                 
165                                 m_step += index_shift[val & 7];
166                                 
167                                 if (m_step > 48) m_step = 48;
168                                 else if (m_step < 0) m_step = 0;
169                         }
170                 }
171
172                 /* update when signal changed */
173                 if( m_signal != new_signal)
174                 {
175 //                      m_stream->update();
176                         m_signal = new_signal;
177                 }
178         }
179 }
180
181
182
183 /*
184  *    Handle an update of the vclk status of a chip (1 is reset ON, 0 is reset OFF)
185  *    This function can use selector = MSM5205_SEX only
186  */
187 void MSM5205::vclk_w(int vclk)
188 {
189         if (m_prescaler != 0)
190         {
191                 out_debug_log(_T("ERROR: msm5205_vclk_w() called, but VCLK selected master mode\n"));
192         }
193         else
194         {
195                 bool val = (vclk == 0) ? false : true;
196                 if (m_vclk != val)
197                 {
198                         m_vclk = val;
199                         if (!val)
200                                 event_callback(EVENT_TIMER, 0);
201                 }
202         }
203 }
204
205 /*
206  *    Handle an update of the reset status of a chip (1 is reset ON, 0 is reset OFF)
207  */
208
209 void MSM5205::reset_w(int reset)
210 {
211         touch_sound();
212         m_reset = (reset != 0);
213         set_realtime_render(this, (!m_reset)); // ?? 20181124 K.O
214 }
215
216 /*
217  *    Handle an update of the data to the chip
218  */
219
220 void MSM5205::data_w(int data)
221 {
222         touch_sound();
223         if (m_bitwidth == 4)
224                 m_data = data & 0x0f;
225         else
226                 m_data = (data & 0x07) << 1; /* unknown */
227 }
228
229 /*
230  *    Handle a change of the selector
231  */
232
233 void MSM5205::playmode_w(int select)
234 {
235         static const int prescaler_table[2][4] =
236         {
237                 { 96, 48, 64,  0},
238                 {160, 40, 80, 20}
239         };
240         int prescaler = prescaler_table[(select >> 3) & 1][select & 3];
241         int bitwidth = (select & 4) ? 4 : 3;
242
243         if (m_prescaler != prescaler)
244         {
245                 touch_sound();
246
247                 m_prescaler = prescaler;
248
249                 /* timer set */
250                 if (prescaler)
251                 {
252 //                      attotime period = attotime::from_hz(m_mod_clock) * prescaler;
253 //                      m_timer->adjust(period, 0, period);
254                         double period = ((1000000.0 / m_mod_clock) * prescaler) / 2.0;
255                         if (m_timer != -1) {
256                                 cancel_event(this, m_timer);
257                         }
258                         register_event(this, EVENT_TIMER, period, true, &m_timer);
259                 }
260                 else
261                 {
262 //                      m_timer->adjust(attotime::never);
263                         if (m_timer != -1) {
264                                 cancel_event(this, m_timer);
265                                 m_timer = -1;
266                         }
267                 }
268         }
269
270         if (m_bitwidth != bitwidth)
271         {
272                 touch_sound();
273 //              m_stream->update();
274                 m_bitwidth = bitwidth;
275         }
276 }
277
278 /*
279  *    PAUSE playing (hack for PCE).
280  */
281
282 void MSM5205::pause_w(int data)
283 {
284         m_ignore = (data != 0) ? true : false;
285 }
286
287 void MSM5205::set_volume(int volume)
288 {
289         touch_sound();
290         volume_m = (int)(1024.0 * (max(0, min(100, volume)) / 100.0));
291 }
292
293 void MSM5205::change_clock_w(int32_t clock)
294 {
295         m_mod_clock = clock;
296
297         if (m_prescaler != 0) {
298                 touch_sound();
299                 double period = ((1000000.0 / m_mod_clock) * m_prescaler) / 2.0;
300                 if(m_timer != -1) {
301                         cancel_event(this, m_timer);
302                 }
303                 register_event(this, EVENT_TIMER, period, true, &m_timer);
304         } else {
305                 touch_sound();
306                 if(m_timer != -1) {
307                         cancel_event(this, m_timer);
308                         m_timer = -1;
309                 }
310         }
311 }
312
313
314 //-------------------------------------------------
315 //  sound_stream_update - handle a stream update
316 //-------------------------------------------------
317
318 void MSM5205::mix(int32_t* buffer, int cnt)
319 {
320         /* if this voice is active */
321         if(m_signal)
322         {
323                 int32_t val = apply_volume((int32_t)(m_signal) * 32, volume_m); // Changed by 20190212 K.O
324                 int32_t val_l = apply_volume(val, volume_l);
325                 int32_t val_r = apply_volume(val, volume_r);
326                 
327                 for(int i = 0; i < cnt; i++)
328                 {
329                         *buffer++ += val_l; // L
330                         *buffer++ += val_r; // R
331                 }
332         }
333 }
334
335 void MSM5205::set_volume(int ch, int decibel_l, int decibel_r)
336 {
337         volume_l = decibel_to_volume(decibel_l + 6.0);
338         volume_r = decibel_to_volume(decibel_r + 6.0);
339 }
340
341 #define STATE_VERSION   3
342
343 bool MSM5205::process_state(FILEIO* state_fio, bool loading)
344 {
345         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
346                 return false;
347         }
348         if(!state_fio->StateCheckInt32(this_device_id)) {
349                 return false;
350         }
351         state_fio->StateValue(m_mod_clock);
352         state_fio->StateValue(m_timer);
353         state_fio->StateValue(m_data);
354         state_fio->StateValue(m_vclk);
355         state_fio->StateValue(m_reset);
356         state_fio->StateValue(m_prescaler);
357         state_fio->StateValue(m_bitwidth);
358         state_fio->StateValue(m_signal);
359         state_fio->StateValue(m_step);
360         state_fio->StateValue(m_select);
361         state_fio->StateValue(m_ignore);
362         state_fio->StateValue(volume_m);
363         return true;
364 }
365