Mercurial > repos > blastem
comparison vgmplay.c @ 2081:cfd53c94fffb
Initial stab at RF5C164 emulation
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Thu, 03 Feb 2022 23:15:42 -0800 |
parents | 460e14497120 |
children |
comparison
equal
deleted
inserted
replaced
2080:bafb757e1cd2 | 2081:cfd53c94fffb |
---|---|
12 #include <stdio.h> | 12 #include <stdio.h> |
13 #include <stdlib.h> | 13 #include <stdlib.h> |
14 #include <string.h> | 14 #include <string.h> |
15 #include "vgm.h" | 15 #include "vgm.h" |
16 #include "system.h" | 16 #include "system.h" |
17 #include "rf5c164.h" | |
17 | 18 |
18 #define MCLKS_NTSC 53693175 | 19 #define MCLKS_NTSC 53693175 |
19 #define MCLKS_PAL 53203395 | 20 #define MCLKS_PAL 53203395 |
20 | 21 |
21 #define MCLKS_PER_68K 7 | 22 #define MCLKS_PER_68K 7 |
88 { | 89 { |
89 } | 90 } |
90 | 91 |
91 int headless = 0; | 92 int headless = 0; |
92 | 93 |
93 #define CYCLE_LIMIT MCLKS_NTSC/60 | 94 #include <limits.h> |
94 #define MAX_SOUND_CYCLES 100000 | 95 #define ADJUST_BUFFER (12500000) |
96 #define MAX_NO_ADJUST (UINT_MAX-ADJUST_BUFFER) | |
97 #define MAX_RUN_SAMPLES 128 | |
95 tern_node * config; | 98 tern_node * config; |
96 | 99 |
97 void vgm_wait(ym2612_context * y_context, psg_context * p_context, uint32_t * current_cycle, uint32_t cycles) | 100 typedef struct chip_info chip_info; |
98 { | 101 typedef void (*run_fun)(void *context, uint32_t cycle); |
99 while (cycles > MAX_SOUND_CYCLES) | 102 typedef void (*adjust_fun)(chip_info *chip); |
103 struct chip_info { | |
104 void *context; | |
105 run_fun run; | |
106 adjust_fun adjust; | |
107 uint32_t clock; | |
108 uint32_t samples; | |
109 }; | |
110 | |
111 uint32_t cycles_to_samples(uint32_t clock_rate, uint32_t cycles) | |
112 { | |
113 return ((uint64_t)cycles) * ((uint64_t)44100) / ((uint64_t)clock_rate); | |
114 } | |
115 | |
116 uint32_t samples_to_cycles(uint32_t clock_rate, uint32_t cycles) | |
117 { | |
118 return ((uint64_t)cycles) * ((uint64_t)clock_rate) / ((uint64_t)44100); | |
119 } | |
120 | |
121 void ym_adjust(chip_info *chip) | |
122 { | |
123 ym2612_context *ym = chip->context; | |
124 if (ym->current_cycle >= MAX_NO_ADJUST) { | |
125 uint32_t deduction = ym->current_cycle - ADJUST_BUFFER; | |
126 chip->samples -= cycles_to_samples(chip->clock, deduction); | |
127 ym->current_cycle -= deduction; | |
128 } | |
129 } | |
130 | |
131 void psg_adjust(chip_info *chip) | |
132 { | |
133 psg_context *psg = chip->context; | |
134 if (psg->cycles >= MAX_NO_ADJUST) { | |
135 uint32_t deduction = psg->cycles - ADJUST_BUFFER; | |
136 chip->samples -= cycles_to_samples(chip->clock, deduction); | |
137 psg->cycles -= deduction; | |
138 } | |
139 } | |
140 | |
141 void pcm_adjust(chip_info *chip) | |
142 { | |
143 rf5c164 *pcm = chip->context; | |
144 if (pcm->cycle >= MAX_NO_ADJUST) { | |
145 uint32_t deduction = pcm->cycle - ADJUST_BUFFER; | |
146 chip->samples -= cycles_to_samples(chip->clock, deduction); | |
147 pcm->cycle -= deduction; | |
148 } | |
149 } | |
150 | |
151 void vgm_wait(chip_info *chips, uint32_t num_chips, uint32_t *completed_samples, uint32_t samples) | |
152 { | |
153 while (samples > MAX_RUN_SAMPLES) | |
100 { | 154 { |
101 vgm_wait(y_context, p_context, current_cycle, MAX_SOUND_CYCLES); | 155 vgm_wait(chips, num_chips, completed_samples, MAX_RUN_SAMPLES); |
102 cycles -= MAX_SOUND_CYCLES; | 156 samples -= MAX_RUN_SAMPLES; |
103 } | 157 } |
104 *current_cycle += cycles; | 158 *completed_samples += samples; |
105 psg_run(p_context, *current_cycle); | 159 for (uint32_t i = 0; i < num_chips; i++) |
106 ym_run(y_context, *current_cycle); | 160 { |
107 | 161 chips[i].samples += samples; |
108 if (*current_cycle > CYCLE_LIMIT) { | 162 chips[i].run(chips[i].context, samples_to_cycles(chips[i].clock, chips[i].samples)); |
109 *current_cycle -= CYCLE_LIMIT; | 163 chips[i].adjust(chips + i); |
110 p_context->cycles -= CYCLE_LIMIT; | 164 } |
111 y_context->current_cycle -= CYCLE_LIMIT; | 165 if (*completed_samples > 44100/60) { |
112 process_events(); | 166 process_events(); |
113 } | 167 } |
168 } | |
169 | |
170 chip_info chips[64]; | |
171 | |
172 uint8_t *find_block(data_block *head, uint32_t offset, uint32_t size) | |
173 { | |
174 if (!head) { | |
175 return NULL; | |
176 } | |
177 while (head->size < offset) { | |
178 offset -= head->size; | |
179 head = head->next; | |
180 } | |
181 if (head->size - offset < size) { | |
182 return NULL; | |
183 } | |
184 return head->data + offset; | |
114 } | 185 } |
115 | 186 |
116 int main(int argc, char ** argv) | 187 int main(int argc, char ** argv) |
117 { | 188 { |
118 set_exe_str(argv[0]); | 189 set_exe_str(argv[0]); |
119 data_block *blocks = NULL; | 190 data_block *blocks = NULL; |
120 data_block *seek_block = NULL; | 191 data_block *seek_block = NULL; |
192 data_block *pcm68_blocks = NULL; | |
193 data_block *pcm164_blocks = NULL; | |
121 uint32_t seek_offset; | 194 uint32_t seek_offset; |
122 uint32_t block_offset; | 195 uint32_t block_offset; |
123 | 196 |
124 uint32_t fps = 60; | 197 uint32_t fps = 60; |
125 config = load_config(argv[0]); | 198 config = load_config(argv[0]); |
127 | 200 |
128 uint32_t opts = 0; | 201 uint32_t opts = 0; |
129 if (argc >= 3 && !strcmp(argv[2], "-y")) { | 202 if (argc >= 3 && !strcmp(argv[2], "-y")) { |
130 opts |= YM_OPT_WAVE_LOG; | 203 opts |= YM_OPT_WAVE_LOG; |
131 } | 204 } |
132 | 205 |
133 char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0", TVAL_PTR).ptrval; | 206 char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0", TVAL_PTR).ptrval; |
134 uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : 3390; | 207 uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : 3390; |
135 | 208 |
136 ym2612_context y_context; | 209 VGMFILE f = vgmopen(argv[1], "rb"); |
137 ym_init(&y_context, MCLKS_NTSC, MCLKS_PER_YM, opts); | |
138 | |
139 psg_context p_context; | |
140 psg_init(&p_context, MCLKS_NTSC, MCLKS_PER_PSG); | |
141 | |
142 VGMFILE * f = vgmopen(argv[1], "rb"); | |
143 vgm_header header; | 210 vgm_header header; |
144 vgmread(&header, sizeof(header), 1, f); | 211 vgmread(&header, sizeof(header), 1, f); |
145 if (header.version < 0x150 || !header.data_offset) { | 212 if (header.version < 0x150 || !header.data_offset) { |
146 header.data_offset = 0xC; | 213 header.data_offset = 0xC; |
147 } | 214 } |
215 if (header.version <= 0x101) { | |
216 header.ym2612_clk = header.ym2413_clk; | |
217 } | |
218 uint32_t num_chips = 0; | |
219 rf5c164 *pcm68 = NULL; | |
220 if ((header.data_offset + 0x34) >= 0x44) { | |
221 uint32_t pcm68_clock = 0; | |
222 vgmseek(f, 0x40, SEEK_SET); | |
223 vgmread(&pcm68_clock, sizeof(pcm68_clock), 1, f); | |
224 if (pcm68_clock) { | |
225 pcm68 = calloc(sizeof(*pcm68), 1); | |
226 rf5c164_init(pcm68, pcm68_clock, 1); | |
227 chips[num_chips++] = (chip_info) { | |
228 .context = pcm68, | |
229 .run = (run_fun)rf5c164_run, | |
230 .adjust = pcm_adjust, | |
231 .clock = pcm68_clock, | |
232 .samples = 0 | |
233 }; | |
234 } | |
235 } | |
236 rf5c164 *pcm164 = NULL; | |
237 if ((header.data_offset + 0x34) >= 0x70) { | |
238 uint32_t pcm164_clock = 0; | |
239 vgmseek(f, 0x6C, SEEK_SET); | |
240 vgmread(&pcm164_clock, sizeof(pcm164_clock), 1, f); | |
241 if (pcm164_clock) { | |
242 pcm164 = calloc(sizeof(*pcm164), 1); | |
243 rf5c164_init(pcm164, pcm164_clock, 1); | |
244 chips[num_chips++] = (chip_info) { | |
245 .context = pcm164, | |
246 .run = (run_fun)rf5c164_run, | |
247 .adjust = pcm_adjust, | |
248 .clock = pcm164_clock, | |
249 .samples = 0 | |
250 }; | |
251 } | |
252 } | |
253 | |
254 ym2612_context y_context; | |
255 if (header.ym2612_clk) { | |
256 ym_init(&y_context, header.ym2612_clk, 1, opts); | |
257 chips[num_chips++] = (chip_info) { | |
258 .context = &y_context, | |
259 .run = (run_fun)ym_run, | |
260 .adjust = ym_adjust, | |
261 .clock = header.ym2612_clk, | |
262 .samples = 0 | |
263 }; | |
264 } | |
265 | |
266 psg_context p_context; | |
267 if (header.sn76489_clk) { | |
268 psg_init(&p_context, header.sn76489_clk, 1); | |
269 chips[num_chips++] = (chip_info) { | |
270 .context = &p_context, | |
271 .run = (run_fun)psg_run, | |
272 .adjust = psg_adjust, | |
273 .clock = header.sn76489_clk, | |
274 .samples = 0 | |
275 }; | |
276 } | |
277 | |
148 vgmseek(f, header.data_offset + 0x34, SEEK_SET); | 278 vgmseek(f, header.data_offset + 0x34, SEEK_SET); |
149 uint32_t data_size = header.eof_offset + 4 - (header.data_offset + 0x34); | 279 uint32_t data_size = header.eof_offset + 4 - (header.data_offset + 0x34); |
150 uint8_t * data = malloc(data_size); | 280 uint8_t * data = malloc(data_size); |
151 vgmread(data, 1, data_size, f); | 281 vgmread(data, 1, data_size, f); |
152 vgmclose(f); | 282 vgmclose(f); |
153 | 283 |
154 uint32_t mclks_sample = MCLKS_NTSC / 44100; | |
155 uint32_t loop_count = 2; | 284 uint32_t loop_count = 2; |
156 | 285 |
157 uint8_t * end = data + data_size; | 286 uint8_t * end = data + data_size; |
158 uint8_t * cur = data; | 287 uint8_t * cur = data; |
159 uint32_t current_cycle = 0; | 288 uint32_t completed_samples = 0; |
160 while (cur < end) { | 289 while (cur < end) { |
161 uint8_t cmd = *(cur++); | 290 uint8_t cmd = *(cur++); |
162 switch(cmd) | 291 switch(cmd) |
163 { | 292 { |
164 case CMD_PSG_STEREO: | 293 case CMD_PSG_STEREO: |
177 ym_data_write(&y_context, *(cur++)); | 306 ym_data_write(&y_context, *(cur++)); |
178 break; | 307 break; |
179 case CMD_WAIT: { | 308 case CMD_WAIT: { |
180 uint32_t wait_time = *(cur++); | 309 uint32_t wait_time = *(cur++); |
181 wait_time |= *(cur++) << 8; | 310 wait_time |= *(cur++) << 8; |
182 wait_time *= mclks_sample; | 311 vgm_wait(chips, num_chips, &completed_samples, wait_time); |
183 vgm_wait(&y_context, &p_context, ¤t_cycle, wait_time); | |
184 break; | 312 break; |
185 } | 313 } |
186 case CMD_WAIT_60: | 314 case CMD_WAIT_60: |
187 vgm_wait(&y_context, &p_context, ¤t_cycle, 735 * mclks_sample); | 315 vgm_wait(chips, num_chips, &completed_samples, 735); |
188 break; | 316 break; |
189 case CMD_WAIT_50: | 317 case CMD_WAIT_50: |
190 vgm_wait(&y_context, &p_context, ¤t_cycle, 882 * mclks_sample); | 318 vgm_wait(chips, num_chips, &completed_samples, 882); |
191 break; | 319 break; |
192 case CMD_END: | 320 case CMD_END: |
193 if (header.loop_offset && --loop_count) { | 321 if (header.loop_offset && --loop_count) { |
194 cur = data + header.loop_offset + 0x1C - (header.data_offset + 0x34); | 322 cur = data + header.loop_offset + 0x1C - (header.data_offset + 0x34); |
195 } else { | 323 } else { |
196 //TODO: fade out | 324 //TODO: fade out |
197 return 0; | 325 return 0; |
326 } | |
327 break; | |
328 case CMD_PCM_WRITE: | |
329 if (end - cur < 11) { | |
330 fatal_error("early end of stream at offset %X\n", data-cur); | |
331 } | |
332 cur++; | |
333 { | |
334 uint8_t chip_type = *(cur++); | |
335 uint32_t read_offset = *(cur++); | |
336 read_offset |= *(cur++) << 8; | |
337 read_offset |= *(cur++) << 16; | |
338 uint32_t write_offset = *(cur++); | |
339 write_offset |= *(cur++) << 8; | |
340 write_offset |= *(cur++) << 16; | |
341 uint32_t size = *(cur++); | |
342 size |= *(cur++) << 8; | |
343 size |= *(cur++) << 16; | |
344 if (chip_type == DATA_RF5C68) { | |
345 uint8_t *src = find_block(pcm68_blocks, read_offset, size); | |
346 if (!src) { | |
347 printf("Failed to find RF6C68 data offset %X with size %X\n", read_offset, size); | |
348 } | |
349 write_offset |= pcm68->ram_bank; | |
350 write_offset &= 0xFFFF; | |
351 if (size + write_offset > 0x10000) { | |
352 size = 0x10000 - write_offset; | |
353 } | |
354 memcpy(pcm68->ram + write_offset, src, size); | |
355 printf("rf5c68 PCM write read_offset %X, write_offset %X, size %X\n", read_offset, write_offset, size); | |
356 } else if (chip_type == DATA_RF5C164) { | |
357 uint8_t *src = find_block(pcm164_blocks, read_offset, size); | |
358 if (!src) { | |
359 printf("Failed to find RF6C68 data offset %X with size %X\n", read_offset, size); | |
360 } | |
361 write_offset |= pcm164->ram_bank; | |
362 write_offset &= 0xFFFF; | |
363 if (size + write_offset > 0x10000) { | |
364 size = 0x10000 - write_offset; | |
365 } | |
366 memcpy(pcm164->ram + write_offset, src, size); | |
367 printf("rf5c164 PCM write read_offset %X, write_offset %X, size %X\n", read_offset, write_offset, size); | |
368 } else { | |
369 printf("unknown PCM write read_offset %X, write_offset %X, size %X\n", read_offset, write_offset, size); | |
370 } | |
371 } | |
372 break; | |
373 case CMD_PCM68_REG: | |
374 if (pcm68) { | |
375 uint8_t reg = *(cur++); | |
376 uint8_t value = *(cur++); | |
377 | |
378 rf5c164_write(pcm68, reg, value); | |
379 } else { | |
380 printf("CMD_PCM68_REG without rf5c68 clock\n"); | |
381 cur += 2; | |
382 } | |
383 break; | |
384 case CMD_PCM164_REG: | |
385 if (pcm164) { | |
386 uint8_t reg = *(cur++); | |
387 uint8_t value = *(cur++); | |
388 rf5c164_write(pcm164, reg, value); | |
389 } else { | |
390 printf("CMD_PCM164_REG without rf5c164 clock\n"); | |
391 cur += 2; | |
392 } | |
393 break; | |
394 case CMD_PCM68_RAM: | |
395 if (pcm68) { | |
396 uint16_t address = *(cur++); | |
397 address |= *(cur++) << 8; | |
398 address &= 0xFFF; | |
399 address |= 0x1000; | |
400 uint8_t value = *(cur++); | |
401 rf5c164_write(pcm68, address, value); | |
402 } | |
403 break; | |
404 case CMD_PCM164_RAM: | |
405 if (pcm164) { | |
406 uint16_t address = *(cur++); | |
407 address |= *(cur++) << 8; | |
408 address &= 0xFFF; | |
409 address |= 0x1000; | |
410 uint8_t value = *(cur++); | |
411 rf5c164_write(pcm164, address, value); | |
198 } | 412 } |
199 break; | 413 break; |
200 case CMD_DATA: { | 414 case CMD_DATA: { |
201 cur++; //skip compat command | 415 cur++; //skip compat command |
202 uint8_t data_type = *(cur++); | 416 uint8_t data_type = *(cur++); |
203 uint32_t data_size = *(cur++); | 417 uint32_t data_size = *(cur++); |
204 data_size |= *(cur++) << 8; | 418 data_size |= *(cur++) << 8; |
205 data_size |= *(cur++) << 16; | 419 data_size |= *(cur++) << 16; |
206 data_size |= *(cur++) << 24; | 420 data_size |= *(cur++) << 24; |
421 data_block **curblock = NULL; | |
207 if (data_type == DATA_YM2612_PCM) { | 422 if (data_type == DATA_YM2612_PCM) { |
208 data_block ** curblock = &blocks; | 423 curblock = &blocks; |
424 } else if (data_type == DATA_RF5C68) { | |
425 curblock = &pcm68_blocks; | |
426 } else if (data_type == DATA_RF5C164) { | |
427 curblock = &pcm164_blocks; | |
428 } | |
429 | |
430 if (curblock) { | |
209 while(*curblock) | 431 while(*curblock) |
210 { | 432 { |
211 curblock = &((*curblock)->next); | 433 curblock = &((*curblock)->next); |
212 } | 434 } |
213 *curblock = malloc(sizeof(data_block)); | 435 *curblock = malloc(sizeof(data_block)); |
243 } | 465 } |
244 | 466 |
245 default: | 467 default: |
246 if (cmd >= CMD_WAIT_SHORT && cmd < (CMD_WAIT_SHORT + 0x10)) { | 468 if (cmd >= CMD_WAIT_SHORT && cmd < (CMD_WAIT_SHORT + 0x10)) { |
247 uint32_t wait_time = (cmd & 0xF) + 1; | 469 uint32_t wait_time = (cmd & 0xF) + 1; |
248 wait_time *= mclks_sample; | 470 vgm_wait(chips, num_chips, &completed_samples, wait_time); |
249 vgm_wait(&y_context, &p_context, ¤t_cycle, wait_time); | |
250 } else if (cmd >= CMD_YM2612_DAC && cmd < CMD_DAC_STREAM_SETUP) { | 471 } else if (cmd >= CMD_YM2612_DAC && cmd < CMD_DAC_STREAM_SETUP) { |
251 if (seek_block) { | 472 if (seek_block) { |
252 ym_address_write_part1(&y_context, 0x2A); | 473 ym_address_write_part1(&y_context, 0x2A); |
253 ym_data_write(&y_context, seek_block->data[block_offset++]); | 474 ym_data_write(&y_context, seek_block->data[block_offset++]); |
254 seek_offset++; | 475 seek_offset++; |
260 fputs("Encountered DAC write command but data seek pointer is invalid!\n", stderr); | 481 fputs("Encountered DAC write command but data seek pointer is invalid!\n", stderr); |
261 } | 482 } |
262 uint32_t wait_time = (cmd & 0xF); | 483 uint32_t wait_time = (cmd & 0xF); |
263 if (wait_time) | 484 if (wait_time) |
264 { | 485 { |
265 wait_time *= mclks_sample; | 486 vgm_wait(chips, num_chips, &completed_samples, wait_time); |
266 vgm_wait(&y_context, &p_context, ¤t_cycle, wait_time); | |
267 } | 487 } |
268 } else { | 488 } else { |
269 fatal_error("unimplemented command: %X at offset %X\n", cmd, (unsigned int)(cur - data - 1)); | 489 fatal_error("unimplemented command: %X at offset %X\n", cmd, (unsigned int)(cur - data - 1)); |
270 } | 490 } |
271 } | 491 } |