Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/genplus-gx/core/cd_hw/pcm.c
2 views
1
/***************************************************************************************
2
* Genesis Plus
3
* PCM sound chip (315-5476A) (RF5C164 compatible)
4
*
5
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
6
*
7
* Redistribution and use of this code or any derivative works are permitted
8
* provided that the following conditions are met:
9
*
10
* - Redistributions may not be sold, nor may they be used in a commercial
11
* product or activity.
12
*
13
* - Redistributions that are modified from the original source must include the
14
* complete source code, including the source code for all components used by a
15
* binary built from the modified sources. However, as a special exception, the
16
* source code distributed need not include anything that is normally distributed
17
* (in either source or binary form) with the major components (compiler, kernel,
18
* and so on) of the operating system on which the executable runs, unless that
19
* component itself accompanies the executable.
20
*
21
* - Redistributions must reproduce the above copyright notice, this list of
22
* conditions and the following disclaimer in the documentation and/or other
23
* materials provided with the distribution.
24
*
25
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35
* POSSIBILITY OF SUCH DAMAGE.
36
*
37
****************************************************************************************/
38
#include "shared.h"
39
40
#define PCM_SCYCLES_RATIO (384 * 4)
41
42
#define pcm scd.pcm_hw
43
44
static blip_t* blip[2];
45
46
void pcm_init(blip_t* left, blip_t* right)
47
{
48
/* number of SCD master clocks run per second */
49
double mclk = snd.frame_rate ? (SCYCLES_PER_LINE * (vdp_pal ? 313 : 262) * snd.frame_rate) : SCD_CLOCK;
50
51
/* PCM chips is running at original rate and is synchronized with SUB-CPU */
52
/* Chip output is resampled to desired rate using Blip Buffer. */
53
blip[0] = left;
54
blip[1] = right;
55
blip_set_rates(left, mclk / PCM_SCYCLES_RATIO, snd.sample_rate);
56
blip_set_rates(right, mclk / PCM_SCYCLES_RATIO, snd.sample_rate);
57
}
58
59
void pcm_reset(void)
60
{
61
/* reset chip & clear external RAM */
62
memset(&pcm, 0, sizeof(pcm_t));
63
64
/* reset default bank */
65
pcm.bank = pcm.ram;
66
67
/* reset channels stereo panning */
68
pcm.chan[0].pan = 0xff;
69
pcm.chan[1].pan = 0xff;
70
pcm.chan[2].pan = 0xff;
71
pcm.chan[3].pan = 0xff;
72
pcm.chan[4].pan = 0xff;
73
pcm.chan[5].pan = 0xff;
74
pcm.chan[6].pan = 0xff;
75
pcm.chan[7].pan = 0xff;
76
77
/* reset master clocks counter */
78
pcm.cycles = 0;
79
80
/* clear blip buffers */
81
blip_clear(blip[0]);
82
blip_clear(blip[1]);
83
}
84
85
int pcm_context_save(uint8 *state)
86
{
87
uint8 tmp8;
88
int bufferptr = 0;
89
90
tmp8 = (pcm.bank - pcm.ram) >> 12;
91
92
save_param(pcm.chan, sizeof(pcm.chan));
93
save_param(pcm.out, sizeof(pcm.out));
94
save_param(&tmp8, 1);
95
save_param(&pcm.enabled, sizeof(pcm.enabled));
96
save_param(&pcm.status, sizeof(pcm.status));
97
save_param(&pcm.index, sizeof(pcm.index));
98
save_param(pcm.ram, sizeof(pcm.ram));
99
100
return bufferptr;
101
}
102
103
int pcm_context_load(uint8 *state)
104
{
105
uint8 tmp8;
106
int bufferptr = 0;
107
108
load_param(pcm.chan, sizeof(pcm.chan));
109
load_param(pcm.out, sizeof(pcm.out));
110
111
load_param(&tmp8, 1);
112
pcm.bank = &pcm.ram[(tmp8 & 0x0f) << 12];
113
114
load_param(&pcm.enabled, sizeof(pcm.enabled));
115
load_param(&pcm.status, sizeof(pcm.status));
116
load_param(&pcm.index, sizeof(pcm.index));
117
load_param(pcm.ram, sizeof(pcm.ram));
118
119
return bufferptr;
120
}
121
122
void pcm_run(unsigned int length)
123
{
124
#ifdef LOG_PCM
125
error("[%d][%d]run %d PCM samples (from %d)\n", v_counter, s68k.cycles, length, pcm.cycles);
126
#endif
127
/* check if PCM chip is running */
128
if (pcm.enabled)
129
{
130
int i, j, l, r;
131
132
/* generate PCM samples */
133
for (i=0; i<length; i++)
134
{
135
/* clear output */
136
l = r = 0;
137
138
/* run eight PCM channels */
139
for (j=0; j<8; j++)
140
{
141
/* check if channel is enabled */
142
if (pcm.status & (1 << j))
143
{
144
/* read from current WAVE RAM address */
145
short data = pcm.ram[(pcm.chan[j].addr >> 11) & 0xffff];
146
147
/* loop data ? */
148
if (data == 0xff)
149
{
150
/* reset WAVE RAM address */
151
pcm.chan[j].addr = pcm.chan[j].ls.w << 11;
152
153
/* read again from WAVE RAM address */
154
data = pcm.ram[pcm.chan[j].ls.w];
155
}
156
else
157
{
158
/* increment WAVE RAM address */
159
pcm.chan[j].addr += pcm.chan[j].fd.w;
160
}
161
162
/* infinite loop should not output any data */
163
if (data != 0xff)
164
{
165
/* check sign bit (output centered around 0) */
166
if (data & 0x80)
167
{
168
/* PCM data is positive */
169
data = data & 0x7f;
170
}
171
else
172
{
173
/* PCM data is negative */
174
data = -(data & 0x7f);
175
}
176
177
/* multiply PCM data with ENV & stereo PAN data then add to L/R outputs (14.5 fixed point) */
178
l += ((data * pcm.chan[j].env * (pcm.chan[j].pan & 0x0F)) >> 5);
179
r += ((data * pcm.chan[j].env * (pcm.chan[j].pan >> 4)) >> 5);
180
}
181
}
182
}
183
184
/* limiter */
185
if (l < -32768) l = -32768;
186
else if (l > 32767) l = 32767;
187
if (r < -32768) r = -32768;
188
else if (r > 32767) r = 32767;
189
190
/* check if PCM left output changed */
191
if (pcm.out[0] != l)
192
{
193
blip_add_delta_fast(blip[0], i, l-pcm.out[0]);
194
pcm.out[0] = l;
195
}
196
197
/* check if PCM right output changed */
198
if (pcm.out[1] != r)
199
{
200
blip_add_delta_fast(blip[1], i, r-pcm.out[1]);
201
pcm.out[1] = r;
202
}
203
}
204
}
205
else
206
{
207
/* check if PCM left output changed */
208
if (pcm.out[0])
209
{
210
blip_add_delta_fast(blip[0], 0, -pcm.out[0]);
211
pcm.out[0] = 0;
212
}
213
214
/* check if PCM right output changed */
215
if (pcm.out[1])
216
{
217
blip_add_delta_fast(blip[1], 0, -pcm.out[1]);
218
pcm.out[1] = 0;
219
}
220
}
221
222
/* end of blip buffer frame */
223
blip_end_frame(blip[0], length);
224
blip_end_frame(blip[1], length);
225
226
/* update PCM master clock counter */
227
pcm.cycles += length * PCM_SCYCLES_RATIO;
228
}
229
230
void pcm_update(unsigned int samples)
231
{
232
/* get number of internal clocks (samples) needed */
233
unsigned int clocks = blip_clocks_needed(blip[0], samples);
234
235
/* run PCM chip */
236
if (clocks > 0)
237
{
238
pcm_run(clocks);
239
}
240
241
/* reset PCM master clocks counter */
242
pcm.cycles = 0;
243
}
244
245
void pcm_write(unsigned int address, unsigned char data)
246
{
247
/* synchronize PCM chip with SUB-CPU */
248
int clocks = s68k.cycles - pcm.cycles;
249
if (clocks > 0)
250
{
251
/* number of internal clocks (samples) to run */
252
clocks = (clocks + PCM_SCYCLES_RATIO - 1) / PCM_SCYCLES_RATIO;
253
pcm_run(clocks);
254
}
255
256
#ifdef LOG_PCM
257
error("[%d][%d]PCM write %x -> 0x%02x (%X)\n", v_counter, s68k.cycles, address, data, s68k.pc);
258
#endif
259
260
/* external RAM is mapped to $1000-$1FFF */
261
if (address >= 0x1000)
262
{
263
/* 4K bank access */
264
pcm.bank[address & 0xfff] = data;
265
return;
266
}
267
268
/* internal area si mapped to $0000-$0FFF */
269
switch (address)
270
{
271
case 0x00: /* ENV register */
272
{
273
/* update channel ENV multiplier */
274
pcm.chan[pcm.index].env = data;
275
return;
276
}
277
278
case 0x01: /* PAN register */
279
{
280
/* update channel stereo panning value */
281
pcm.chan[pcm.index].pan = data;
282
return;
283
}
284
285
case 0x02: /* FD register (LSB) */
286
{
287
/* update channel WAVE RAM address increment LSB */
288
pcm.chan[pcm.index].fd.byte.l = data;
289
return;
290
}
291
292
case 0x03: /* FD register (MSB) */
293
{
294
/* update channel WAVE RAM address increment MSB */
295
pcm.chan[pcm.index].fd.byte.h = data;
296
return;
297
}
298
299
case 0x04: /* LS register (LSB) */
300
{
301
/* update channel WAVE RAM loop address LSB */
302
pcm.chan[pcm.index].ls.byte.l = data;
303
return;
304
}
305
306
case 0x05: /* LS register (MSB) */
307
{
308
/* update channel WAVE RAM loop address MSB */
309
pcm.chan[pcm.index].ls.byte.h = data;
310
return;
311
}
312
313
case 0x06: /* ST register */
314
{
315
/* update channel WAVE RAM start address (16.11 fixed point) */
316
pcm.chan[pcm.index].st = data << (8 + 11);
317
318
/* reload WAVE RAM address if channel is OFF */
319
if (!(pcm.status & (1 << pcm.index)))
320
{
321
pcm.chan[pcm.index].addr = pcm.chan[pcm.index].st;
322
}
323
return;
324
}
325
326
case 0x07: /* CTRL register */
327
{
328
if (data & 0x40)
329
{
330
/* channel selection (0-7) */
331
pcm.index = data & 0x07;
332
}
333
else
334
{
335
/* external RAM bank selection (16 x 4K) */
336
pcm.bank = &pcm.ram[(data & 0x0f) << 12];
337
}
338
339
/* update PCM chip status (bit 7) */
340
pcm.enabled = data & 0x80;
341
return;
342
}
343
344
case 0x08: /* ON/OFF register */
345
{
346
/* update PCM channels status */
347
pcm.status = ~data;
348
349
/* reload WAVE RAM address pointers when channels are OFF */
350
if (data & 0x01) pcm.chan[0].addr = pcm.chan[0].st;
351
if (data & 0x02) pcm.chan[1].addr = pcm.chan[1].st;
352
if (data & 0x04) pcm.chan[2].addr = pcm.chan[2].st;
353
if (data & 0x08) pcm.chan[3].addr = pcm.chan[3].st;
354
if (data & 0x10) pcm.chan[4].addr = pcm.chan[4].st;
355
if (data & 0x20) pcm.chan[5].addr = pcm.chan[5].st;
356
if (data & 0x40) pcm.chan[6].addr = pcm.chan[6].st;
357
if (data & 0x80) pcm.chan[7].addr = pcm.chan[7].st;
358
return;
359
}
360
361
default:
362
{
363
/* illegal access */
364
return;
365
}
366
}
367
}
368
369
unsigned char pcm_read(unsigned int address)
370
{
371
/* synchronize PCM chip with SUB-CPU */
372
int clocks = s68k.cycles - pcm.cycles;
373
if (clocks > 0)
374
{
375
/* number of internal clocks (samples) to run */
376
clocks = (clocks + PCM_SCYCLES_RATIO - 1) / PCM_SCYCLES_RATIO;
377
pcm_run(clocks);
378
}
379
380
#ifdef LOG_PCM
381
error("[%d][%d]PCM read (%X)\n", v_counter, s68k.cycles, address, s68k.pc);
382
#endif
383
384
/* external RAM (TODO: verify if possible to read, some docs claim it's not !) */
385
if (address >= 0x1000)
386
{
387
/* 4K bank access */
388
return pcm.bank[address & 0xfff];
389
}
390
391
/* read WAVE RAM address pointers */
392
if ((address >= 0x10) && (address < 0x20))
393
{
394
int index = (address >> 1) & 0x07;
395
396
if (address & 1)
397
{
398
return (pcm.chan[index].addr >> (11 + 8)) & 0xff;
399
}
400
else
401
{
402
return (pcm.chan[index].addr >> 11) & 0xff;
403
}
404
}
405
406
/* illegal access */
407
return 0xff;
408
}
409
410
void pcm_ram_dma_w(unsigned int words)
411
{
412
uint16 data;
413
414
/* CDC buffer source address */
415
uint16 src_index = cdc.dac.w & 0x3ffe;
416
417
/* PCM-RAM destination address*/
418
uint16 dst_index = (scd.regs[0x0a>>1].w << 2) & 0xffe;
419
420
/* update DMA destination address */
421
scd.regs[0x0a>>1].w += (words >> 1);
422
423
/* update DMA source address */
424
cdc.dac.w += (words << 1);
425
426
/* DMA transfer */
427
while (words--)
428
{
429
/* read 16-bit word from CDC buffer */
430
data = *(uint16 *)(cdc.ram + src_index);
431
432
/* write 16-bit word to PCM RAM (endianness does not matter since PCM RAM is always accessed as byte)*/
433
*(uint16 *)(pcm.bank + dst_index) = data ;
434
435
/* increment CDC buffer source address */
436
src_index = (src_index + 2) & 0x3ffe;
437
438
/* increment PCM-RAM destination address */
439
dst_index = (dst_index + 2) & 0xffe;
440
}
441
}
442
443
444