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