1 /*
2 HuC6280 sound chip emulator
3 by Charles MacDonald
4 E-mail: cgfm2@hotmail.com
5 WWW: http://cgfm2.emuviews.com
6
7 Thanks to:
8
9 - Paul Clifford for his PSG documentation.
10 - Richard Bannister for the TGEmu-specific sound updating code.
11 - http://www.uspto.gov for the PSG patents.
12 - All contributors to the tghack-list.
13
14 Changes:
15
16 (03/30/2003)
17 - Removed TGEmu specific code and added support functions for MAME.
18 - Modified setup code to handle multiple chips with different clock and
19 volume settings.
20
21 Missing features / things to do:
22
23 - Add LFO support. But do any games actually use it?
24
25 - Add shared index for waveform playback and sample writes. Almost every
26 game will reset the index prior to playback so this isn't an issue.
27
28 - While the noise emulation is complete, the data for the pseudo-random
29 bitstream is calculated by machine.rand() and is not a representation of what
30 the actual hardware does.
31
32 For some background on Hudson Soft's C62 chipset:
33
34 - http://www.hudsonsoft.net/ww/about/about.html
35 - http://www.hudson.co.jp/corp/eng/coinfo/history.html
36
37 Legal information:
38
39 Copyright Charles MacDonald
40
41 This library is free software; you can redistribute it and/or
42 modify it under the terms of the GNU Lesser General Public
43 License as published by the Free Software Foundation; either
44 version 2.1 of the License, or (at your option) any later version.
45
46 This library is distributed in the hope that it will be useful,
47 but WITHOUT ANY WARRANTY; without even the implied warranty of
48 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
49 Lesser General Public License for more details.
50
51 You should have received a copy of the GNU Lesser General Public
52 License along with this library; if not, write to the Free Software
53 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
54 */
55
56 //#include "emu.h"
57 #include <stdlib.h> // for rand
58 #include <string.h> // for memset
59 #include <math.h> // for pow
60 #include "mamedef.h"
61 #include "c6280.h"
62
63 typedef struct {
64 UINT16 frequency;
65 UINT8 control;
66 UINT8 balance;
67 UINT8 waveform[32];
68 UINT8 index;
69 INT16 dda;
70 UINT8 noise_control;
71 UINT32 noise_counter;
72 UINT32 counter;
73 UINT8 Muted;
74 } t_channel;
75
76 typedef struct {
77 //sound_stream *stream;
78 //device_t *device;
79 //device_t *cpudevice;
80 UINT8 select;
81 UINT8 balance;
82 UINT8 lfo_frequency;
83 UINT8 lfo_control;
84 t_channel channel[8]; // is 8, because: p->select = data & 0x07;
85 INT16 volume_table[32];
86 UINT32 noise_freq_tab[32];
87 UINT32 wave_freq_tab[4096];
88 } c6280_t;
89
90 /*INLINE c6280_t *get_safe_token(device_t *device)
91 {
92 assert(device != NULL);
93 assert(device->type() == C6280);
94 return (c6280_t *)downcast<legacy_device_base *>(device)->token();
95 }*/
96
97
98 /* only needed for io_buffer */
99 //#include "cpu/h6280/h6280.h"
100
101
c6280_init(c6280_t * p,double clk,double rate)102 static void c6280_init(/*device_t *device,*/ c6280_t *p, double clk, double rate)
103 {
104 //const c6280_interface *intf = (const c6280_interface *)device->static_config();
105 int i;
106 double step;
107
108 /* Loudest volume level for table */
109 //double level = 65535.0 / 6.0 / 32.0;
110 double level = 65536.0 / 6.0 / 32.0;
111
112 /* Clear context */
113 memset(p, 0, sizeof(c6280_t));
114
115 //p->device = device;
116 //p->cpudevice = device->machine().device(intf->cpu);
117 //if (p->cpudevice == NULL)
118 // fatalerror("c6280_init: no CPU found with tag of '%s'\n", device->tag());
119
120 /* Make waveform frequency table */
121 for(i = 0; i < 4096; i += 1)
122 {
123 step = ((clk / rate) * 4096) / (i+1);
124 p->wave_freq_tab[(1 + i) & 0xFFF] = (UINT32)step;
125 }
126
127 /* Make noise frequency table */
128 for(i = 0; i < 32; i += 1)
129 {
130 step = ((clk / rate) * 32) / (i+1);
131 p->noise_freq_tab[i] = (UINT32)step;
132 }
133
134 /* Make volume table */
135 /* PSG has 48dB volume range spread over 32 steps */
136 step = 48.0 / 32.0;
137 for(i = 0; i < 31; i++)
138 {
139 p->volume_table[i] = (UINT16)level;
140 level /= pow(10.0, step / 20.0);
141 }
142 p->volume_table[31] = 0;
143 }
144
145
c6280_write(c6280_t * p,int offset,int data)146 static void c6280_write(c6280_t *p, int offset, int data)
147 {
148 t_channel *q = &p->channel[p->select];
149
150 /* Update stream */
151 //p->stream->update();
152
153 switch(offset & 0x0F)
154 {
155 case 0x00: /* Channel select */
156 p->select = data & 0x07;
157 break;
158
159 case 0x01: /* Global balance */
160 p->balance = data;
161 break;
162
163 case 0x02: /* Channel frequency (LSB) */
164 q->frequency = (q->frequency & 0x0F00) | data;
165 q->frequency &= 0x0FFF;
166 break;
167
168 case 0x03: /* Channel frequency (MSB) */
169 q->frequency = (q->frequency & 0x00FF) | (data << 8);
170 q->frequency &= 0x0FFF;
171 break;
172
173 case 0x04: /* Channel control (key-on, DDA mode, volume) */
174
175 /* 1-to-0 transition of DDA bit resets waveform index */
176 if((q->control & 0x40) && ((data & 0x40) == 0))
177 {
178 q->index = 0;
179 }
180 q->control = data;
181 break;
182
183 case 0x05: /* Channel balance */
184 q->balance = data;
185 break;
186
187 case 0x06: /* Channel waveform data */
188
189 switch(q->control & 0xC0)
190 {
191 case 0x00:
192 q->waveform[q->index & 0x1F] = data & 0x1F;
193 q->index = (q->index + 1) & 0x1F;
194 break;
195
196 case 0x40:
197 break;
198
199 case 0x80:
200 q->waveform[q->index & 0x1F] = data & 0x1F;
201 q->index = (q->index + 1) & 0x1F;
202 break;
203
204 case 0xC0:
205 q->dda = data & 0x1F;
206 break;
207 }
208
209 break;
210
211 case 0x07: /* Noise control (enable, frequency) */
212 q->noise_control = data;
213 break;
214
215 case 0x08: /* LFO frequency */
216 p->lfo_frequency = data;
217 break;
218
219 case 0x09: /* LFO control (enable, mode) */
220 p->lfo_control = data;
221 break;
222
223 default:
224 break;
225 }
226 }
227
228
229 //static STREAM_UPDATE( c6280_update )
c6280m_update(void * param,stream_sample_t ** outputs,int samples)230 void c6280m_update(void* param, stream_sample_t **outputs, int samples)
231 {
232 static const int scale_tab[] = {
233 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
234 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F
235 };
236 int ch;
237 int i;
238 c6280_t *p = (c6280_t *)param;
239
240 int lmal = (p->balance >> 4) & 0x0F;
241 int rmal = (p->balance >> 0) & 0x0F;
242 int vll, vlr;
243
244 lmal = scale_tab[lmal];
245 rmal = scale_tab[rmal];
246
247 /* Clear buffer */
248 for(i = 0; i < samples; i++)
249 {
250 outputs[0][i] = 0;
251 outputs[1][i] = 0;
252 }
253
254 for(ch = 0; ch < 6; ch++)
255 {
256 /* Only look at enabled channels */
257 if((p->channel[ch].control & 0x80) && ! p->channel[ch].Muted)
258 {
259 int lal = (p->channel[ch].balance >> 4) & 0x0F;
260 int ral = (p->channel[ch].balance >> 0) & 0x0F;
261 int al = p->channel[ch].control & 0x1F;
262
263 lal = scale_tab[lal];
264 ral = scale_tab[ral];
265
266 /* Calculate volume just as the patent says */
267 vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal);
268 if(vll > 0x1F) vll = 0x1F;
269
270 vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal);
271 if(vlr > 0x1F) vlr = 0x1F;
272
273 vll = p->volume_table[vll];
274 vlr = p->volume_table[vlr];
275
276 /* Check channel mode */
277 if((ch >= 4) && (p->channel[ch].noise_control & 0x80))
278 {
279 /* Noise mode */
280 UINT32 step = p->noise_freq_tab[(p->channel[ch].noise_control & 0x1F) ^ 0x1F];
281 for(i = 0; i < samples; i += 1)
282 {
283 static int data = 0;
284 p->channel[ch].noise_counter += step;
285 if(p->channel[ch].noise_counter >= 0x800)
286 {
287 //data = (p->device->machine().rand() & 1) ? 0x1F : 0;
288 data = (rand() & 1) ? 0x1F : 0;
289 }
290 p->channel[ch].noise_counter &= 0x7FF;
291 outputs[0][i] += (INT16)(vll * (data - 16));
292 outputs[1][i] += (INT16)(vlr * (data - 16));
293 }
294 }
295 else
296 if(p->channel[ch].control & 0x40)
297 {
298 /* DDA mode */
299 for(i = 0; i < samples; i++)
300 {
301 outputs[0][i] += (INT16)(vll * (p->channel[ch].dda - 16));
302 outputs[1][i] += (INT16)(vlr * (p->channel[ch].dda - 16));
303 }
304 }
305 else
306 {
307 /* Waveform mode */
308 UINT32 step = p->wave_freq_tab[p->channel[ch].frequency];
309 for(i = 0; i < samples; i += 1)
310 {
311 int offset;
312 INT16 data;
313 offset = (p->channel[ch].counter >> 12) & 0x1F;
314 p->channel[ch].counter += step;
315 p->channel[ch].counter &= 0x1FFFF;
316 data = p->channel[ch].waveform[offset];
317 outputs[0][i] += (INT16)(vll * (data - 16));
318 outputs[1][i] += (INT16)(vlr * (data - 16));
319 }
320 }
321 }
322 }
323 }
324
325
326 /*--------------------------------------------------------------------------*/
327 /* MAME specific code */
328 /*--------------------------------------------------------------------------*/
329
330 //static DEVICE_START( c6280 )
device_start_c6280m(int clock,int rate)331 void* device_start_c6280m(int clock, int rate)
332 {
333 //int rate = device->clock()/16;
334 //c6280_t *info = get_safe_token(device);
335 c6280_t *info;
336 UINT8 CurChn;
337
338 info = (c6280_t*)malloc(sizeof(c6280_t));
339 if (info == NULL)
340 return 0;
341 memset(info, 0x00, sizeof(c6280_t));
342
343 /* Initialize PSG emulator */
344 //c6280_init(device, info, device->clock(), rate);
345 c6280_init(info, clock & 0x7FFFFFFF, rate);
346
347 /* Create stereo stream */
348 //info->stream = device->machine().sound().stream_alloc(*device, 0, 2, rate, info, c6280_update);
349
350 for (CurChn = 0; CurChn < 6; CurChn ++)
351 info->channel[CurChn].Muted = 0x00;
352
353 return info;
354 }
355
device_stop_c6280m(void * chip)356 void device_stop_c6280m(void* chip)
357 {
358 c6280_t *info = (c6280_t *)chip;
359
360 free(info);
361
362 return;
363 }
364
device_reset_c6280m(void * chip)365 void device_reset_c6280m(void* chip)
366 {
367 c6280_t *info = (c6280_t *)chip;
368 UINT8 CurChn;
369 t_channel* TempChn;
370
371 info->select = 0x00;
372 info->balance = 0x00;
373 info->lfo_frequency = 0x00;
374 info->lfo_control = 0x00;
375
376 for (CurChn = 0; CurChn < 6; CurChn ++)
377 {
378 TempChn = &info->channel[CurChn];
379
380 TempChn->frequency = 0x00;
381 TempChn->control = 0x00;
382 TempChn->balance = 0x00;
383 memset(TempChn->waveform, 0x00, 0x20);
384 TempChn->index = 0x00;
385 TempChn->dda = 0x00;
386 TempChn->noise_control = 0x00;
387 TempChn->noise_counter = 0x00;
388 TempChn->counter = 0x00;
389 }
390
391 return;
392 }
393
394 //READ8_DEVICE_HANDLER( c6280_r )
c6280m_r(void * chip,offs_t offset)395 UINT8 c6280m_r(void* chip, offs_t offset)
396 {
397 //c6280_t *info = get_safe_token(device);
398 c6280_t *info = (c6280_t *)chip;
399 //return h6280io_get_buffer(info->cpudevice);
400 if (offset == 0)
401 return info->select;
402 return 0;
403 }
404
405 //WRITE8_DEVICE_HANDLER( c6280_w )
c6280m_w(void * chip,offs_t offset,UINT8 data)406 void c6280m_w(void* chip, offs_t offset, UINT8 data)
407 {
408 //c6280_t *info = get_safe_token(device);
409 c6280_t *info = (c6280_t *)chip;
410 //h6280io_set_buffer(info->cpudevice, data);
411 c6280_write(info, offset, data);
412 }
413
414
c6280m_set_mute_mask(void * chip,UINT32 MuteMask)415 void c6280m_set_mute_mask(void* chip, UINT32 MuteMask)
416 {
417 c6280_t *info = (c6280_t *)chip;
418 UINT8 CurChn;
419
420 for (CurChn = 0; CurChn < 6; CurChn ++)
421 info->channel[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
422
423 return;
424 }
425
426
427
428 /**************************************************************************
429 * Generic get_info
430 **************************************************************************/
431
432 /*DEVICE_GET_INFO( c6280 )
433 {
434 switch (state)
435 {
436 // --- the following bits of info are returned as 64-bit signed integers ---
437 case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(c6280_t); break;
438
439 // --- the following bits of info are returned as pointers to data or functions ---
440 case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( c6280 ); break;
441 case DEVINFO_FCT_STOP: // nothing // break;
442 case DEVINFO_FCT_RESET: // nothing // break;
443
444 // --- the following bits of info are returned as NULL-terminated strings ---
445 case DEVINFO_STR_NAME: strcpy(info->s, "HuC6280"); break;
446 case DEVINFO_STR_FAMILY: strcpy(info->s, "????"); break;
447 case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
448 case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
449 case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
450 }
451 }
452
453
454 DEFINE_LEGACY_SOUND_DEVICE(C6280, c6280);*/
455