1 // Based on MAME sources by Olivier Galibert,Aaron Giles
2 /*********************************************************/
3 /* ricoh RF5C68(or clone) PCM controller */
4 /*********************************************************/
5
6 #include "burnint.h"
7 #include "rf5c68.h"
8
9 #define NUM_CHANNELS (8)
10
11 static UINT32 nUpdateStep;
12
13 struct pcm_channel
14 {
15 UINT8 enable;
16 UINT8 env;
17 UINT8 pan;
18 UINT8 start;
19 UINT32 addr;
20 UINT16 step;
21 UINT16 loopst;
22 };
23
24 struct rf5c68pcm
25 {
26 struct pcm_channel chan[NUM_CHANNELS];
27 UINT8 cbank;
28 UINT8 wbank;
29 UINT8 enable;
30 UINT8 data[0x10000];
31 double volume[2];
32 INT32 output_dir[2];
33 };
34
35 static struct rf5c68pcm *chip = NULL;
36
37 static INT32 *left = NULL;
38 static INT32 *right = NULL;
39
RF5C68PCMUpdate(INT16 * pSoundBuf,INT32 length)40 void RF5C68PCMUpdate(INT16* pSoundBuf, INT32 length)
41 {
42 #if defined FBA_DEBUG
43 if (!DebugSnd_RF5C68Initted) bprintf(PRINT_ERROR, _T("RF5C68PCMUpdate called without init\n"));
44 #endif
45
46 if (!chip->enable) return;
47
48 INT32 i, j;
49
50 memset(left, 0, length * sizeof(INT32));
51 memset(right, 0, length * sizeof(INT32));
52
53 for (i = 0; i < NUM_CHANNELS; i++) {
54 pcm_channel *chan = &chip->chan[i];
55
56 if (chan->enable) {
57 INT32 lv = (chan->pan & 0xf) * chan->env;
58 INT32 rv = ((chan->pan >> 4) & 0xf) * chan->env;
59
60 for (j = 0; j < length; j++) {
61 INT32 sample;
62
63 sample = chip->data[(chan->addr >> 11) & 0xffff];
64 if (sample == 0xff) {
65 chan->addr = chan->loopst << 11;
66 sample = chip->data[(chan->addr >> 11) & 0xffff];
67 if (sample == 0xff) break;
68 }
69
70 chan->addr += (chan->step * nUpdateStep) >> 15;
71
72 if (sample & 0x80) {
73 sample &= 0x7f;
74 left[j] += (sample * lv) >> 5;
75 right[j] += (sample * rv) >> 5;
76 } else {
77 left[j] -= (sample * lv) >> 5;
78 right[j] -= (sample * rv) >> 5;
79 }
80 }
81 }
82 }
83
84 for (i = 0; i < length; i++) {
85 INT32 nLeftSample = 0;
86 INT32 nRightSample = 0;
87
88 left[i] = BURN_SND_CLIP(left[i]);
89 left[i] = left[i] & ~0x3f;
90 right[i] = BURN_SND_CLIP(right[i]);
91 right[i] = right[i] & ~0x3f;
92
93 if ((chip->output_dir[BURN_SND_RF5C68PCM_ROUTE_1] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
94 nLeftSample += (INT32)(left[i] * chip->volume[BURN_SND_RF5C68PCM_ROUTE_1]);
95 }
96 if ((chip->output_dir[BURN_SND_RF5C68PCM_ROUTE_1] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
97 nRightSample += (INT32)(left[i] * chip->volume[BURN_SND_RF5C68PCM_ROUTE_1]);
98 }
99
100 if ((chip->output_dir[BURN_SND_RF5C68PCM_ROUTE_2] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
101 nLeftSample += (INT32)(right[i] * chip->volume[BURN_SND_RF5C68PCM_ROUTE_2]);
102 }
103 if ((chip->output_dir[BURN_SND_RF5C68PCM_ROUTE_2] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
104 nRightSample += (INT32)(right[i] * chip->volume[BURN_SND_RF5C68PCM_ROUTE_2]);
105 }
106
107 nLeftSample = BURN_SND_CLIP(nLeftSample);
108 nRightSample = BURN_SND_CLIP(nRightSample);
109
110 pSoundBuf[0] = nLeftSample;
111 pSoundBuf[1] = nRightSample;
112 pSoundBuf += 2;
113 }
114 }
115
RF5C68PCMReset()116 void RF5C68PCMReset()
117 {
118 #if defined FBA_DEBUG
119 if (!DebugSnd_RF5C68Initted) bprintf(PRINT_ERROR, _T("RF5C68PCMReset called without init\n"));
120 #endif
121
122 memset(chip->data, 0xff, sizeof(chip->data));
123 }
124
RF5C68PCMInit(INT32 clock)125 void RF5C68PCMInit(INT32 clock)
126 {
127 chip = (struct rf5c68pcm*)BurnMalloc(sizeof(struct rf5c68pcm));
128
129 INT32 Rate = clock / 384;
130
131 nUpdateStep = (INT32)(((float)Rate / nBurnSoundRate) * 32768);
132
133 left = (INT32*)BurnMalloc(nBurnSoundLen * sizeof(INT32));
134 right = (INT32*)BurnMalloc(nBurnSoundLen * sizeof(INT32));
135
136 chip->volume[BURN_SND_RF5C68PCM_ROUTE_1] = 1.00;
137 chip->volume[BURN_SND_RF5C68PCM_ROUTE_2] = 1.00;
138 chip->output_dir[BURN_SND_RF5C68PCM_ROUTE_1] = BURN_SND_ROUTE_LEFT;
139 chip->output_dir[BURN_SND_RF5C68PCM_ROUTE_2] = BURN_SND_ROUTE_RIGHT;
140
141 DebugSnd_RF5C68Initted = 1;
142 }
143
RF5C68PCMSetRoute(INT32 nIndex,double nVolume,INT32 nRouteDir)144 void RF5C68PCMSetRoute(INT32 nIndex, double nVolume, INT32 nRouteDir)
145 {
146 #if defined FBA_DEBUG
147 if (!DebugSnd_RF5C68Initted) bprintf(PRINT_ERROR, _T("RF5C68PCMSetRoute called without init\n"));
148 if (nIndex < 0 || nIndex > 1) bprintf(PRINT_ERROR, _T("RF5C68PCMSetRoute called with invalid index %i\n"), nIndex);
149 #endif
150
151 chip->volume[nIndex] = nVolume;
152 chip->output_dir[nIndex] = nRouteDir;
153 }
154
RF5C68PCMExit()155 void RF5C68PCMExit()
156 {
157 #if defined FBA_DEBUG
158 if (!DebugSnd_RF5C68Initted) bprintf(PRINT_ERROR, _T("RF5C68PCMExit called without init\n"));
159 #endif
160
161 BurnFree(left);
162 BurnFree(right);
163 BurnFree(chip);
164
165 DebugSnd_RF5C68Initted = 0;
166 }
167
RF5C68PCMScan(INT32 nAction,INT32 *)168 void RF5C68PCMScan(INT32 nAction, INT32 *)
169 {
170 struct BurnArea ba;
171
172 if (nAction & ACB_DRIVER_DATA) {
173 memset(&ba, 0, sizeof(ba));
174 ba.Data = chip->data;
175 ba.nLen = 0x10000;
176 ba.szName = "RF5C68PCMData";
177 BurnAcb(&ba);
178
179 SCAN_VAR(chip->cbank);
180 SCAN_VAR(chip->wbank);
181 SCAN_VAR(chip->enable);
182 SCAN_VAR(chip->chan);
183 }
184 }
185
RF5C68PCMRegWrite(UINT8 offset,UINT8 data)186 void RF5C68PCMRegWrite(UINT8 offset, UINT8 data)
187 {
188 #if defined FBA_DEBUG
189 if (!DebugSnd_RF5C68Initted) bprintf(PRINT_ERROR, _T("RF5C68PCMReqWrite called without init\n"));
190 #endif
191
192 struct pcm_channel *chan = &chip->chan[chip->cbank];
193 INT32 i;
194
195 /* force the stream to update first */
196 // stream_update(chip->stream);
197
198 switch (offset) {
199 case 0x00: {
200 chan->env = data;
201 break;
202 }
203
204 case 0x01: {
205 chan->pan = data;
206 break;
207 }
208
209 case 0x02: {
210 chan->step = (chan->step & 0xff00) | (data & 0xff);
211 break;
212 }
213
214 case 0x03: {
215 chan->step = (chan->step & 0xff) | ((data << 8) & 0xff00);
216 break;
217 }
218
219 case 0x04: {
220 chan->loopst = (chan->loopst & 0xff00) | (data & 0xff);
221 break;
222 }
223
224 case 0x05: {
225 chan->loopst = (chan->loopst & 0xff) | ((data << 8) & 0xff00);
226 break;
227 }
228
229 case 0x06: {
230 chan->start = data;
231 if (!chan->enable) chan->addr = chan->start << (8 + 11);
232 break;
233 }
234
235 case 0x07: {
236 chip->enable = (data >> 7) & 1;
237 if (data & 0x40) {
238 chip->cbank = data & 7;
239 } else {
240 chip->wbank = data & 15;
241 }
242 break;
243 }
244
245 case 0x08: {
246 for (i = 0; i < 8; i++) {
247 chip->chan[i].enable = (~data >> i) & 1;
248 if (!chip->chan[i].enable) chip->chan[i].addr = chip->chan[i].start << (8 + 11);
249 }
250 break;
251 }
252 }
253 }
254
RF5C68PCMRead(UINT16 offset)255 UINT8 RF5C68PCMRead(UINT16 offset)
256 {
257 #if defined FBA_DEBUG
258 if (!DebugSnd_RF5C68Initted) bprintf(PRINT_ERROR, _T("RF5C68PCMRead called without init\n"));
259 #endif
260
261 return chip->data[chip->wbank * 0x1000 + offset];
262 }
263
RF5C68PCMWrite(UINT16 offset,UINT8 data)264 void RF5C68PCMWrite(UINT16 offset, UINT8 data)
265 {
266 #if defined FBA_DEBUG
267 if (!DebugSnd_RF5C68Initted) bprintf(PRINT_ERROR, _T("RF5C68PCMWrite called without init\n"));
268 #endif
269
270 chip->data[(chip->wbank * 0x1000) + offset] = data;
271 }
272
273 #undef NUM_CHANNELS
274