6 * This work is licensed under the terms of MAME license.
7 * See COPYING file in the top-level directory.
9 * The following ADPCM algorithm was derived from MAME upd7759 driver.
11 * The Pico is using this chip in slave mode. In this mode there are no ROM
12 * headers, but the first byte sent to the chip is used to start the ADPCM
13 * engine. This byte is discarded, i.e. not processed by the engine.
15 * Data is fed into the chip through a FIFO. An Interrupt is created if the
16 * FIFO has been drained below the low water mark.
18 * The Pico has 2 extensions to the standard upd7759 chip:
19 * - gain control, used to control the volume of the ADPCM output
20 * - filtering, used to remove (some of) the ADPCM compression artifacts
24 #include "../pico_int.h"
27 #define Limit(val, max, min) \
28 (val > max ? max : val < min ? min : val)
30 #define ADPCM_CLOCK (1280000/4)
32 #define FIFO_IRQ_THRESHOLD 16
34 static const int step_deltas[16][16] =
36 { 0, 0, 1, 2, 3, 5, 7, 10, 0, 0, -1, -2, -3, -5, -7, -10 },
37 { 0, 1, 2, 3, 4, 6, 8, 13, 0, -1, -2, -3, -4, -6, -8, -13 },
38 { 0, 1, 2, 4, 5, 7, 10, 15, 0, -1, -2, -4, -5, -7, -10, -15 },
39 { 0, 1, 3, 4, 6, 9, 13, 19, 0, -1, -3, -4, -6, -9, -13, -19 },
40 { 0, 2, 3, 5, 8, 11, 15, 23, 0, -2, -3, -5, -8, -11, -15, -23 },
41 { 0, 2, 4, 7, 10, 14, 19, 29, 0, -2, -4, -7, -10, -14, -19, -29 },
42 { 0, 3, 5, 8, 12, 16, 22, 33, 0, -3, -5, -8, -12, -16, -22, -33 },
43 { 1, 4, 7, 10, 15, 20, 29, 43, -1, -4, -7, -10, -15, -20, -29, -43 },
44 { 1, 4, 8, 13, 18, 25, 35, 53, -1, -4, -8, -13, -18, -25, -35, -53 },
45 { 1, 6, 10, 16, 22, 31, 43, 64, -1, -6, -10, -16, -22, -31, -43, -64 },
46 { 2, 7, 12, 19, 27, 37, 51, 76, -2, -7, -12, -19, -27, -37, -51, -76 },
47 { 2, 9, 16, 24, 34, 46, 64, 96, -2, -9, -16, -24, -34, -46, -64, -96 },
48 { 3, 11, 19, 29, 41, 57, 79, 117, -3, -11, -19, -29, -41, -57, -79, -117 },
49 { 4, 13, 24, 36, 50, 69, 96, 143, -4, -13, -24, -36, -50, -69, -96, -143 },
50 { 4, 16, 29, 44, 62, 85, 118, 175, -4, -16, -29, -44, -62, -85, -118, -175 },
51 { 6, 20, 36, 54, 76, 104, 144, 214, -6, -20, -36, -54, -76, -104, -144, -214 },
54 static const int state_deltas[16] = { -1, -1, 0, 0, 1, 2, 2, 3, -1, -1, 0, 0, 1, 2, 2, 3 };
56 static s32 stepsamples; // ratio as Q16, host sound rate / chip sample rate
58 static struct xpcm_state {
59 s32 samplepos; // leftover duration for current sample wrt sndrate, Q16
60 int sample; // current sample
61 short state; // ADPCM decoder state
62 short samplegain; // programmable gain
64 char startpin; // value on the !START pin
65 char irqenable; // IRQ enabled?
67 char portstate; // ADPCM stream state
68 short silence; // silence blocks still to be played
69 short rate, nibbles; // ADPCM nibbles still to be played
70 unsigned char highlow, cache; // nibble selector and cache
72 char filter; // filter selector
73 s32 x[3], y[3]; // filter history
75 enum { RESET, START, HDR, COUNT }; // portstate
78 // SEGA Pico specific filtering
80 #define QB 16 // mantissa bits
81 #define FP(f) (int)((f)*(1<<QB)) // convert to fixpoint
83 static struct iir2 { // 2nd order Butterworth IIR coefficients
84 s32 a[2], gain; // coefficients
86 static struct iir2 *filter; // currently selected filter
89 static void PicoPicoFilterCoeff(struct iir2 *iir, int cutoff, int rate)
91 // no filter if the cutoff is above the Nyquist frequency
92 if (cutoff >= rate/2) {
93 memset(iir, 0, sizeof(*iir));
97 // compute 2nd order butterworth filter coefficients
98 double a = 1 / tan(M_PI * cutoff / rate);
100 double gain = 1/(1 + M_SQRT2*a + axa);
101 iir->gain = FP(gain);
102 iir->a[0] = FP(2 * (axa-1) * gain);
103 iir->a[1] = FP(-(1 - M_SQRT2*a + axa) * gain);
106 static int PicoPicoFilterApply(struct iir2 *iir, int sample)
111 // NB Butterworth specific!
112 xpcm.x[0] = xpcm.x[1]; xpcm.x[1] = xpcm.x[2];
113 xpcm.x[2] = sample * iir->gain; // Qb
114 xpcm.y[0] = xpcm.y[1]; xpcm.y[1] = xpcm.y[2];
115 xpcm.y[2] = (xpcm.x[0] + 2*xpcm.x[1] + xpcm.x[2]
116 + xpcm.y[0]*iir->a[1] + xpcm.y[1]*iir->a[0]) >> QB;
121 // pin functions, N designating a negated pin
123 PICO_INTERNAL void PicoPicoPCMResetN(int pin)
126 xpcm.portstate = RESET;
127 xpcm.sample = xpcm.samplepos = xpcm.state = 0;
128 xpcm.nibbles = xpcm.silence = 0;
129 } else if (xpcm.portstate == RESET)
130 xpcm.portstate = START;
133 PICO_INTERNAL void PicoPicoPCMStartN(int pin)
138 PICO_INTERNAL int PicoPicoPCMBusyN(void)
140 return (xpcm.portstate <= START);
144 // configuration functions
146 PICO_INTERNAL void PicoPicoPCMRerate(void)
148 s32 nextstep = ((u64)PicoIn.sndRate<<16)/ADPCM_CLOCK;
150 // if the sound rate changes, erase filter history to avoid freak behaviour
151 if (stepsamples != nextstep) {
152 memset(xpcm.x, 0, sizeof(xpcm.x));
153 memset(xpcm.y, 0, sizeof(xpcm.y));
156 // output samples per chip clock
157 stepsamples = nextstep;
159 // compute filter coefficients, cutoff at half the ADPCM sample rate
160 PicoPicoFilterCoeff(&filters[1], 6000/2, PicoIn.sndRate); // 5-6 KHz
161 PicoPicoFilterCoeff(&filters[2], 9000/2, PicoIn.sndRate); // 8-12 KHz
162 PicoPicoFilterCoeff(&filters[3], 15000/2, PicoIn.sndRate); // 14-16 KHz
164 PicoPicoPCMFilter(xpcm.filter);
167 PICO_INTERNAL void PicoPicoPCMGain(int gain)
169 xpcm.samplegain = gain*4;
172 PICO_INTERNAL void PicoPicoPCMFilter(int index)
174 // if the filter changes, erase the history to avoid freak behaviour
175 if (index != xpcm.filter) {
176 memset(xpcm.x, 0, sizeof(xpcm.x));
177 memset(xpcm.y, 0, sizeof(xpcm.y));
181 filter = filters+index;
182 if (filter->a[0] == 0)
186 PICO_INTERNAL void PicoPicoPCMIrqEn(int enable)
188 xpcm.irqenable = (enable ? 3 : 0);
191 // TODO need an interupt pending mask?
192 PICO_INTERNAL int PicoPicoIrqAck(int level)
194 return (PicoPicohw.fifo_bytes < FIFO_IRQ_THRESHOLD && level != xpcm.irqenable
195 ? xpcm.irqenable : 0);
201 #define apply_filter(v) PicoPicoFilterApply(filter, v)
203 // compute next ADPCM sample
204 #define do_sample(nibble) \
206 xpcm.sample += step_deltas[xpcm.state][nibble]; \
207 xpcm.state += state_deltas[nibble]; \
208 xpcm.state = (xpcm.state < 0 ? 0 : xpcm.state > 15 ? 15 : xpcm.state); \
211 // writes samples with sndRate, nearest neighbour resampling, filtering
212 #define write_sample(buffer, length, stereo) \
214 while (xpcm.samplepos > 0 && length > 0) { \
215 int val = Limit(xpcm.samplegain*xpcm.sample, 16383, -16384); \
216 xpcm.samplepos -= 1<<16; \
219 int out = apply_filter(val); \
221 if (stereo) *buffer++ += out; \
226 PICO_INTERNAL void PicoPicoPCMUpdate(short *buffer, int length, int stereo)
228 unsigned char *src = PicoPicohw.xpcm_buffer;
229 unsigned char *lim = PicoPicohw.xpcm_ptr;
232 // leftover partial sample from last run
233 write_sample(buffer, length, stereo);
235 // loop over FIFO data, generating ADPCM samples
236 while (length > 0 && src < lim)
238 // ADPCM state engine
239 if (xpcm.silence > 0) { // generate silence
242 xpcm.samplepos += stepsamples*256;
244 } else if (xpcm.nibbles > 0) { // produce samples
251 xpcm.highlow = !xpcm.highlow;
253 do_sample((xpcm.cache & 0xf0) >> 4);
254 xpcm.samplepos += stepsamples*xpcm.rate;
256 } else switch (xpcm.portstate) { // handle stream headers
259 xpcm.samplepos += length<<16;
265 else // kill 0x00 bytes at stream start
269 xpcm.samplepos += length<<16;
274 xpcm.nibbles = xpcm.silence = xpcm.rate = 0;
276 if (srcval == 0) { // terminator
277 // HACK, kill leftover odd byte to avoid restart (Minna de Odorou)
278 if (lim-src == 1) src++;
279 xpcm.portstate = START;
280 } else switch (srcval >> 6) {
281 case 0: xpcm.silence = (srcval & 0x3f) + 1; break;
282 case 1: xpcm.rate = (srcval & 0x3f) + 1; xpcm.nibbles = 256; break;
283 case 2: xpcm.rate = (srcval & 0x3f) + 1; xpcm.portstate = COUNT; break;
288 xpcm.nibbles = *src++ + 1; xpcm.portstate = HDR;
292 write_sample(buffer, length, stereo);
295 // buffer cleanup, generate irq if lowwater reached
296 if (src < lim && src != PicoPicohw.xpcm_buffer) {
298 memmove(PicoPicohw.xpcm_buffer, src, di);
299 PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer + di;
300 elprintf(EL_PICOHW, "xpcm update: over %i", di);
302 if (!irq && di < FIFO_IRQ_THRESHOLD)
303 irq = xpcm.irqenable;
304 PicoPicohw.fifo_bytes = di;
305 } else if (src == lim && src != PicoPicohw.xpcm_buffer) {
306 PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
307 elprintf(EL_PICOHW, "xpcm update: under %i", length);
310 irq = xpcm.irqenable;
311 PicoPicohw.fifo_bytes = 0;
314 // TODO need an IRQ mask somewhere to avoid loosing one in cases of HINT/VINT
315 if (irq && SekIrqLevel != irq) {
316 elprintf(EL_PICOHW, "irq%d", irq);
317 if (SekIrqLevel < irq)
321 if (buffer && length) {
322 // for underflow, use last sample to avoid clicks
323 int val = Limit(xpcm.samplegain*xpcm.sample, 16383, -16384);
325 int out = apply_filter(val);
327 if (stereo) *buffer++ += out;
332 PICO_INTERNAL int PicoPicoPCMSave(void *buffer, int length)
336 if (length < sizeof(xpcm)) {
337 elprintf(EL_ANOMALY, "save buffer too small?");
341 memcpy(bp, &xpcm, sizeof(xpcm));
343 return (bp - (u8*)buffer);
346 PICO_INTERNAL void PicoPicoPCMLoad(void *buffer, int length)
350 if (length >= sizeof(xpcm))
351 memcpy(&xpcm, bp, sizeof(xpcm));