comparison pico_pcm.c @ 2431:61c0bfe10887

Somewhat busted support for Pico ADPCM
author Michael Pavone <pavone@retrodev.com>
date Tue, 06 Feb 2024 21:47:11 -0800
parents
children 18816c5c11d4
comparison
equal deleted inserted replaced
2430:fb8d6ebf9d5f 2431:61c0bfe10887
1 #include "pico_pcm.h"
2 #include "backend.h"
3
4 #define PCM_RESET 0x8000
5 #define PCM_INT_EN 0x4000
6 #define PCM_ENABLED 0x0800
7 #define PCM_FILTER 0x00C0
8 #define PCM_VOLUME 0x0007
9
10 void pico_pcm_reset(pico_pcm *pcm)
11 {
12 pcm->fifo_read = sizeof(pcm->fifo);
13 pcm->fifo_write = 0;
14 pcm->adpcm_state = 0;
15 pcm->output = 0;
16 pcm->nibble_store = 0;
17 pcm->counter = 0;
18 pcm->samples = 0;
19 pcm->rate = 0;
20 pcm->ctrl &= 0x7FFF;
21 }
22
23 void pico_pcm_init(pico_pcm *pcm, uint32_t master_clock, uint32_t divider)
24 {
25 pcm->audio = render_audio_source("PICO ADPCM", master_clock, divider * 4, 1);
26 pcm->scope = NULL;
27 pcm->scope_channel = 0;
28 pcm->clock_inc = divider * 4;
29 pico_pcm_reset(pcm);
30 }
31
32 void pico_pcm_free(pico_pcm *pcm)
33 {
34 render_free_source(pcm->audio);
35 }
36
37 void pico_pcm_enable_scope(pico_pcm *pcm, oscilloscope *scope, uint32_t master_clock)
38 {
39 #ifndef IS_LIB
40 pcm->scope = scope;
41 pcm->scope_channel = scope_add_channel(scope, "PICO ADPCM", master_clock / pcm->clock_inc);
42 #endif
43 }
44
45 static uint8_t pcm_fifo_read(pico_pcm *pcm)
46 {
47 if (pcm->fifo_read == sizeof(pcm->fifo)) {
48 return 0;
49 }
50 uint8_t ret = pcm->fifo[pcm->fifo_read++];
51 pcm->fifo_read &= sizeof(pcm->fifo) - 1;
52 if (pcm->fifo_read == pcm->fifo_write) {
53 pcm->fifo_read = sizeof(pcm->fifo);
54 }
55 return ret;
56 }
57
58 int16_t upd7755_calc_sample(uint8_t sample, uint8_t *state)
59 {
60 //Tables from MAME
61 static const int16_t sample_delta[256] = {
62 0, 0, 1, 2, 3, 5, 7, 10, 0, 0, -1, -2, -3, -5, -7, -10,
63 0, 1, 2, 3, 4, 6, 8, 13, 0, -1, -2, -3, -4, -6, -8, -13,
64 0, 1, 2, 4, 5, 7, 10, 15, 0, -1, -2, -4, -5, -7, -10, -15,
65 0, 1, 3, 4, 6, 9, 13, 19, 0, -1, -3, -4, -6, -9, -13, -19,
66 0, 2, 3, 5, 8, 11, 15, 23, 0, -2, -3, -5, -8, -11, -15, -23,
67 0, 2, 4, 7, 10, 14, 19, 29, 0, -2, -4, -7, -10, -14, -19, -29,
68 0, 3, 5, 8, 12, 16, 22, 33, 0, -3, -5, -8, -12, -16, -22, -33,
69 1, 4, 7, 10, 15, 20, 29, 43, -1, -4, -7, -10, -15, -20, -29, -43,
70 1, 4, 8, 13, 18, 25, 35, 53, -1, -4, -8, -13, -18, -25, -35, -53,
71 1, 6, 10, 16, 22, 31, 43, 64, -1, -6, -10, -16, -22, -31, -43, -64,
72 2, 7, 12, 19, 27, 37, 51, 76, -2, -7, -12, -19, -27, -37, -51, -76,
73 2, 9, 16, 24, 34, 46, 64, 96, -2, -9, -16, -24, -34, -46, -64, -96,
74 3, 11, 19, 29, 41, 57, 79, 117, -3, -11, -19, -29, -41, -57, -79, -117,
75 4, 13, 24, 36, 50, 69, 96, 143, -4, -13, -24, -36, -50, -69, -96, -143,
76 4, 16, 29, 44, 62, 85, 118, 175, -4, -16, -29, -44, -62, -85, -118, -175,
77 6, 20, 36, 54, 76, 104, 144, 214, -6, -20, -36, -54, -76, -104, -144, -214
78 };
79 static const int state_delta[16] = {-1, -1, 0, 0, 1, 2, 2, 3, -1, -1, 0, 0, 1, 2, 2, 3};
80 int16_t ret = sample_delta[(*state << 4) + sample];
81 int diff = state_delta[*state];
82 if (diff >= 0 || *state > 0) {
83 *state += diff;
84 if (*state > 15) {
85 *state = 15;
86 }
87 }
88 return ret;
89 }
90
91 void pico_pcm_run(pico_pcm *pcm, uint32_t cycle)
92 {
93 while (pcm->cycle < cycle)
94 {
95 pcm->cycle += pcm->clock_inc;
96 //TODO: Figure out actual attenuation
97 int16_t shift = pcm->ctrl & PCM_VOLUME;
98 #ifndef IS_LIB
99 if (pcm->scope) {
100 scope_add_sample(pcm->scope, pcm->scope_channel, (pcm->output >> shift) * 128, 0);
101 }
102 #endif
103 render_put_mono_sample(pcm->audio, (pcm->output >> shift) * 128);
104 if (!(pcm->ctrl & PCM_ENABLED)) {
105 continue;
106 }
107 if (pcm->counter) {
108 pcm->counter--;
109 } else if (pcm->samples) {
110 pcm->samples--;
111 uint8_t sample;
112 if (pcm->nibble_store) {
113 sample = pcm->nibble_store & 0xF;
114 pcm->nibble_store = 0;
115 } else {
116 uint8_t byte = pcm_fifo_read(pcm);
117 sample = byte >> 4;
118 pcm->nibble_store = 0x80 | (byte & 0xF);
119 }
120 uint8_t old_state = pcm->adpcm_state;
121 pcm->output += upd7755_calc_sample(sample, &pcm->adpcm_state);
122 if (pcm->output > 255) {
123 pcm->output = 255;
124 } else if (pcm->output < -256) {
125 pcm->output = -256;
126 }
127 //printf("Sample %d, old_state %d, new_state %d, output %d\n", sample, old_state, pcm->adpcm_state, pcm->output);
128 pcm->counter = pcm->rate;
129 } else {
130 uint8_t cmd = pcm_fifo_read(pcm);
131 if (cmd) {
132 pcm->ctrl |= 0x8000;
133 } else {
134 pcm->ctrl &= 0x7FFF;
135 }
136 switch (cmd & 0xC0)
137 {
138 case 0:
139 pcm->output = 0;
140 pcm->adpcm_state = 0;
141 pcm->counter = (cmd & 0x3F) * 160;
142 break;
143 case 0x40:
144 pcm->rate = (cmd & 0x3F);
145 pcm->samples = 256;
146 break;
147 case 0x80:
148 pcm->rate = (cmd & 0x3F);
149 //FIXME: this probably does not happen instantly
150 pcm->samples = pcm_fifo_read(pcm) + 1;
151 break;
152 case 0xC0:
153 //FIXME: this probably does not happen instantly
154 //TODO: Does repeat mode even work on this chip?
155 // Does it work on a uPD7759 in slave mode?
156 // Is this correct behavior if it does work?
157 pcm->counter = pcm->rate = pcm_fifo_read(pcm) & 0x3F;
158 pcm->samples = (pcm_fifo_read(pcm) + 1) * ((cmd & 7) + 1);
159 break;
160 }
161 }
162 }
163 }
164
165 // RI??E???FF???VVV
166 // R: 1 = Reset request, 0 = normal operation
167 // I: 1 = interrupts enabled, 0 = disabled
168 // E: 1 = Enabled? Sega code always sets this to 1 outside of reset
169 // F: Low-pass Filter 1 = 6 kHz, 2 = 12 kHz 3 = 16 kHz
170 // V: volume, probably attenuation value since converter defaults to "0"
171 void pico_pcm_ctrl_write(pico_pcm *pcm, uint16_t value)
172 {
173 if (value & PCM_RESET) {
174 pico_pcm_reset(pcm);
175 }
176 pcm->ctrl &= 0x8000;
177 pcm->ctrl |= value & ~PCM_RESET;
178 //TODO: update low-pass filter
179 }
180
181 void pico_pcm_data_write(pico_pcm *pcm, uint16_t value)
182 {
183 if (pcm->fifo_read == sizeof(pcm->fifo)) {
184 pcm->fifo_read = pcm->fifo_write;
185 }
186 pcm->fifo[pcm->fifo_write++] = value >> 8;
187 pcm->fifo_write &= sizeof(pcm->fifo)-1;
188 pcm->fifo[pcm->fifo_write++] = value;
189 pcm->fifo_write &= sizeof(pcm->fifo)-1;
190 }
191
192 uint16_t pico_pcm_ctrl_read(pico_pcm *pcm)
193 {
194 return pcm->ctrl;
195 }
196
197 uint16_t pico_pcm_data_read(pico_pcm *pcm)
198 {
199 if (pcm->fifo_read == sizeof(pcm->fifo)) {
200 return sizeof(pcm->fifo) - 1;
201 }
202 return (pcm->fifo_read - pcm->fifo_write) & (sizeof(pcm->fifo)-1);
203 }
204
205 #define FIFO_THRESHOLD 48
206 uint32_t pico_pcm_next_int(pico_pcm *pcm)
207 {
208 if (!(pcm->ctrl & PCM_INT_EN)) {
209 return CYCLE_NEVER;
210 }
211 uint32_t fifo_bytes;
212 if (pcm->fifo_read == sizeof(pcm->fifo)) {
213 fifo_bytes = 0;
214 } else if (pcm->fifo_read == pcm->fifo_write) {
215 fifo_bytes = sizeof(pcm->fifo);
216 } else {
217 fifo_bytes = (pcm->fifo_write - pcm->fifo_read) & (sizeof(pcm->fifo) - 1);
218 }
219 if (fifo_bytes < FIFO_THRESHOLD) {
220 return pcm->cycle;
221 }
222 uint32_t cycles_to_threshold = pcm->counter + 1;
223 if (pcm->samples) {
224 uint16_t samples = pcm->samples;
225 if (pcm->nibble_store) {
226 cycles_to_threshold += pcm->rate + 1;
227 samples--;
228 }
229 uint16_t bytes = (samples >> 1) + (samples & 1);
230 if (bytes > (fifo_bytes - FIFO_THRESHOLD)) {
231 cycles_to_threshold += (fifo_bytes - FIFO_THRESHOLD + 1) * (pcm->rate + 1) * 2;
232 fifo_bytes = 0;
233 } else {
234 cycles_to_threshold += bytes * (pcm->rate + 1) * 2;
235 fifo_bytes -= bytes;
236 }
237 }
238 uint8_t fifo_read = pcm->fifo_read;
239 uint8_t cmd = 0;
240 while (fifo_bytes >= FIFO_THRESHOLD)
241 {
242 if (cmd) {
243 switch(cmd & 0xC0)
244 {
245 case 0:
246 cycles_to_threshold += 640 * (cmd & 0x3F) + 1;
247 break;
248 case 0x40:
249 cycles_to_threshold += (fifo_bytes - FIFO_THRESHOLD + 1) * ((cmd & 0x3F) + 1) * 2;
250 fifo_bytes = 0;
251 break;
252 case 0x80: {
253 uint32_t samples = pcm->fifo[fifo_read++];
254 fifo_bytes--;
255 fifo_read &= sizeof(pcm->fifo) - 1;
256 if (fifo_bytes < FIFO_THRESHOLD) {
257 break;
258 }
259 uint32_t bytes = (samples +1) >> 1;
260 if (bytes > (fifo_bytes - FIFO_THRESHOLD)) {
261 cycles_to_threshold += (fifo_bytes - FIFO_THRESHOLD + 1) * ((cmd & 0x3F) + 1) * 2;
262 fifo_bytes = 0;
263 }
264 break; }
265 case 0xC0: {
266 uint32_t rate = pcm->fifo[fifo_read++] & 0x3F;
267 fifo_bytes--;
268 fifo_read &= sizeof(pcm->fifo) - 1;
269 uint32_t samples = pcm->fifo[fifo_read++] & 0x3F;
270 fifo_bytes--;
271 fifo_read &= sizeof(pcm->fifo) - 1;
272 if (fifo_bytes < FIFO_THRESHOLD) {
273 break;
274 }
275 samples++;
276 samples *= (cmd & 7) + 1;
277 uint32_t bytes = (samples + 1) >> 1;
278 if (bytes > (fifo_bytes - FIFO_THRESHOLD)) {
279 cycles_to_threshold += (fifo_bytes - FIFO_THRESHOLD + 1) * ((cmd & 0x3F) + 1) * 2;
280 fifo_bytes = 0;
281 }
282 break; }
283 }
284 cmd = 0;
285 } else {
286 cycles_to_threshold++;
287 cmd = pcm->fifo[fifo_read++];
288 fifo_bytes--;
289 fifo_read &= sizeof(pcm->fifo) - 1;
290 }
291 }
292
293 return pcm->cycle + cycles_to_threshold * pcm->clock_inc;
294 }