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