# HG changeset patch # User Michael Pavone # Date 1643958942 28800 # Node ID cfd53c94fffb2bdd7fe832c753566fe749c54a81 # Parent bafb757e1cd2929ca45954787ed60ad056dcb8c2 Initial stab at RF5C164 emulation diff -r bafb757e1cd2 -r cfd53c94fffb Makefile --- a/Makefile Wed Feb 02 01:10:07 2022 -0800 +++ b/Makefile Thu Feb 03 23:15:42 2022 -0800 @@ -196,7 +196,7 @@ endif endif endif -AUDIOOBJS=ym2612.o psg.o wave.o vgm.o event_log.o render_audio.o +AUDIOOBJS=ym2612.o psg.o wave.o vgm.o event_log.o render_audio.o rf5c164.o CONFIGOBJS=config.o tern.o util.o paths.o NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o RENDEROBJS=ppm.o controller_info.o diff -r bafb757e1cd2 -r cfd53c94fffb cdd_fader.c --- a/cdd_fader.c Wed Feb 02 01:10:07 2022 -0800 +++ b/cdd_fader.c Thu Feb 03 23:15:42 2022 -0800 @@ -3,7 +3,7 @@ void cdd_fader_init(cdd_fader *fader) { - fader->audio = render_audio_source(16934400, 384, 2); + fader->audio = render_audio_source("CDDA", 16934400, 384, 2); fader->cur_attenuation = 0x4000; fader->dst_attenuation = 0x4000; fader->attenuation_step = 0; diff -r bafb757e1cd2 -r cfd53c94fffb cdd_mcu.c --- a/cdd_mcu.c Wed Feb 02 01:10:07 2022 -0800 +++ b/cdd_mcu.c Thu Feb 03 23:15:42 2022 -0800 @@ -77,7 +77,7 @@ if (context->seek_pba == context->head_pba) { context->seeking = 0; } else if (context->seek_pba > context->head_pba) { - if (context->seek_pba - context->head_pba >= COARSE_SEEK) { + if (context->seek_pba - context->head_pba >= COARSE_SEEK || context->head_pba < LEADIN_SECTORS) { context->head_pba += COARSE_SEEK; } else if (context->seek_pba - context->head_pba >= FINE_SEEK) { context->head_pba += FINE_SEEK; @@ -119,6 +119,8 @@ handle_seek(context); if (!context->seeking) { context->head_pba++; + } + if (context->head_pba >= LEADIN_SECTORS) { uint8_t track = context->media->seek(context->media, context->head_pba - LEADIN_SECTORS); if (context->media->tracks[track].type == TRACK_AUDIO) { gate_array[GAO_CDD_CTRL] &= ~BIT_MUTE; @@ -471,7 +473,7 @@ next_nibble = context->cycle; context->current_status_nibble = 0; gate_array[GAO_CDD_STATUS] |= BIT_DRS; - if (context->status == DS_PLAY && !context->seeking) { + if (context->status == DS_PLAY && context->head_pba >= LEADIN_SECTORS) { context->current_sector_byte = 0; } } diff -r bafb757e1cd2 -r cfd53c94fffb psg.c --- a/psg.c Wed Feb 02 01:10:07 2022 -0800 +++ b/psg.c Thu Feb 03 23:15:42 2022 -0800 @@ -13,7 +13,7 @@ void psg_init(psg_context * context, uint32_t master_clock, uint32_t clock_div) { memset(context, 0, sizeof(*context)); - context->audio = render_audio_source(master_clock, clock_div, 1); + context->audio = render_audio_source("PSG", master_clock, clock_div, 1); context->clock_inc = clock_div; for (int i = 0; i < 4; i++) { context->volume[i] = 0xF; @@ -111,7 +111,7 @@ } int16_t accum = 0; - + for (int i = 0; i < 3; i++) { if (context->output_state[i]) { accum += volume_table[context->volume[i]]; @@ -120,7 +120,7 @@ if (context->noise_out) { accum += volume_table[context->volume[3]]; } - + render_put_mono_sample(context->audio, accum); context->cycles += context->clock_inc; diff -r bafb757e1cd2 -r cfd53c94fffb render_audio.c --- a/render_audio.c Wed Feb 02 01:10:07 2022 -0800 +++ b/render_audio.c Thu Feb 03 23:15:42 2022 -0800 @@ -139,7 +139,7 @@ void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider) { - src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider; + src->buffer_inc = ((BUFFER_INC_RES * sample_divider * (uint64_t)sample_rate) / master_clock); } void render_audio_adjust_speed(float adjust_ratio) @@ -150,13 +150,14 @@ } } -audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) +audio_source *render_audio_source(const char *name, uint64_t master_clock, uint64_t sample_divider, uint8_t channels) { audio_source *ret = NULL; uint32_t alloc_size = render_is_audio_sync() ? channels * buffer_samples : nearest_pow2(render_min_buffered() * 4 * channels); render_lock_audio(); if (num_audio_sources < 8) { ret = calloc(1, sizeof(audio_source)); + ret->name = name; ret->back = malloc(alloc_size * sizeof(int16_t)); ret->front = render_is_audio_sync() ? malloc(alloc_size * sizeof(int16_t)) : ret->back; ret->front_populated = 0; @@ -183,7 +184,7 @@ ret->gain_mult = 1.0f; } render_audio_created(ret); - + return ret; } @@ -211,7 +212,7 @@ break; } } - + render_unlock_audio(); if (found) { render_source_paused(src, remaining_sources); @@ -250,7 +251,7 @@ render_pause_source(src); num_inactive_audio_sources--; } - + free(src->front); if (render_is_audio_sync()) { free(src->back); @@ -283,7 +284,7 @@ { src->buffer_fraction -= BUFFER_INC_RES; interp_sample(src, src->last_left, value); - + if (((src->buffer_pos - base) & src->mask) >= sync_samples) { render_do_audio_ready(src); } @@ -301,10 +302,10 @@ while (src->buffer_fraction > BUFFER_INC_RES) { src->buffer_fraction -= BUFFER_INC_RES; - + interp_sample(src, src->last_left, left); interp_sample(src, src->last_right, right); - + if (((src->buffer_pos - base) & src->mask)/2 >= sync_samples) { render_do_audio_ready(src); } @@ -382,4 +383,4 @@ { update_source(inactive_audio_sources[i], rc, sync_changed); } -} \ No newline at end of file +} diff -r bafb757e1cd2 -r cfd53c94fffb render_audio.h --- a/render_audio.h Wed Feb 02 01:10:07 2022 -0800 +++ b/render_audio.h Thu Feb 03 23:15:42 2022 -0800 @@ -9,6 +9,7 @@ } render_audio_format; typedef struct { + const char *name; void *opaque; int16_t *front; int16_t *back; @@ -28,7 +29,7 @@ } audio_source; //public interface -audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels); +audio_source *render_audio_source(const char *name, uint64_t master_clock, uint64_t sample_divider, uint8_t channels); void render_audio_source_gaindb(audio_source *src, float gain); void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider); void render_put_mono_sample(audio_source *src, int16_t value); diff -r bafb757e1cd2 -r cfd53c94fffb segacd.c --- a/segacd.c Wed Feb 02 01:10:07 2022 -0800 +++ b/segacd.c Thu Feb 03 23:15:42 2022 -0800 @@ -262,7 +262,14 @@ static uint8_t pcm_read8(uint32_t address, void *vcontext) { - return 0; + m68k_context *m68k = vcontext; + segacd_context *cd = m68k->system; + if (address & 1) { + rf5c164_run(&cd->pcm, m68k->current_cycle); + return rf5c164_read(&cd->pcm, address >> 1); + } else { + return 0xFF; + } } static uint16_t pcm_read16(uint32_t address, void *vcontext) @@ -272,6 +279,12 @@ static void *pcm_write8(uint32_t address, void *vcontext, uint8_t value) { + m68k_context *m68k = vcontext; + segacd_context *cd = m68k->system; + if (address & 1) { + rf5c164_run(&cd->pcm, m68k->current_cycle); + rf5c164_write(&cd->pcm, address >> 1, value); + } return vcontext; } @@ -760,7 +773,7 @@ break; case DST_PCM_RAM: dma_addr &= (1 << 13) - 1; - //TODO: write to currently visible 8K bank of PCM RAM I guess? + rf5c164_write(&cd->pcm, 0x1000 | (dma_addr >> 1), value); dma_addr += 2; cd->cdc_dst_low = dma_addr & 7; cd->gate_array[GA_CDC_DMA_ADDR] = dma_addr >> 3; @@ -807,6 +820,7 @@ timers_run(cd, cycle); cdd_run(cd, cycle); cd_graphics_run(cd, cycle); + rf5c164_run(&cd->pcm, cycle); } static m68k_context *sync_components(m68k_context * context, uint32_t address) @@ -890,6 +904,7 @@ cd->graphics_int_cycle = 0; } } + cd->pcm.cycle -= deduction; } static uint16_t main_gate_read16(uint32_t address, void *vcontext) @@ -1175,7 +1190,7 @@ cdd_mcu_init(&cd->cdd, media); cd_graphics_init(cd); cdd_fader_init(&cd->fader); - + rf5c164_init(&cd->pcm, SCD_MCLKS, 4); return cd; } diff -r bafb757e1cd2 -r cfd53c94fffb segacd.h --- a/segacd.h Wed Feb 02 01:10:07 2022 -0800 +++ b/segacd.h Thu Feb 03 23:15:42 2022 -0800 @@ -3,6 +3,7 @@ #include #include "genesis.h" #include "cdd_mcu.h" +#include "rf5c164.h" typedef struct { m68k_context *m68k; @@ -34,6 +35,7 @@ uint8_t reset; uint8_t need_reset; uint8_t memptr_start_index; + rf5c164 pcm; lc8951 cdc; cdd_mcu cdd; cdd_fader fader; diff -r bafb757e1cd2 -r cfd53c94fffb vgm.h --- a/vgm.h Wed Feb 02 01:10:07 2022 -0800 +++ b/vgm.h Thu Feb 03 23:15:42 2022 -0800 @@ -58,21 +58,28 @@ CMD_DAC_STREAM_START, CMD_DAC_STREAM_STOP, CMD_DAC_STREAM_STARTFAST, + CMD_PCM68_REG = 0xB0, + CMD_PCM164_REG, + CMD_PCM68_RAM = 0xC1, + CMD_PCM164_RAM = 0xC2, CMD_DATA_SEEK = 0xE0 }; enum { - DATA_YM2612_PCM = 0 + DATA_YM2612_PCM = 0, + DATA_RF5C68, + DATA_RF5C164, }; #pragma pack(pop) -typedef struct { - struct data_block *next; - uint8_t *data; - uint32_t size; - uint8_t type; -} data_block; +typedef struct data_block data_block; +struct data_block { + data_block *next; + uint8_t *data; + uint32_t size; + uint8_t type; +}; typedef struct { vgm_header header; diff -r bafb757e1cd2 -r cfd53c94fffb vgmplay.c --- a/vgmplay.c Wed Feb 02 01:10:07 2022 -0800 +++ b/vgmplay.c Thu Feb 03 23:15:42 2022 -0800 @@ -14,6 +14,7 @@ #include #include "vgm.h" #include "system.h" +#include "rf5c164.h" #define MCLKS_NTSC 53693175 #define MCLKS_PAL 53203395 @@ -90,34 +91,106 @@ int headless = 0; -#define CYCLE_LIMIT MCLKS_NTSC/60 -#define MAX_SOUND_CYCLES 100000 +#include +#define ADJUST_BUFFER (12500000) +#define MAX_NO_ADJUST (UINT_MAX-ADJUST_BUFFER) +#define MAX_RUN_SAMPLES 128 tern_node * config; -void vgm_wait(ym2612_context * y_context, psg_context * p_context, uint32_t * current_cycle, uint32_t cycles) +typedef struct chip_info chip_info; +typedef void (*run_fun)(void *context, uint32_t cycle); +typedef void (*adjust_fun)(chip_info *chip); +struct chip_info { + void *context; + run_fun run; + adjust_fun adjust; + uint32_t clock; + uint32_t samples; +}; + +uint32_t cycles_to_samples(uint32_t clock_rate, uint32_t cycles) { - while (cycles > MAX_SOUND_CYCLES) - { - vgm_wait(y_context, p_context, current_cycle, MAX_SOUND_CYCLES); - cycles -= MAX_SOUND_CYCLES; + return ((uint64_t)cycles) * ((uint64_t)44100) / ((uint64_t)clock_rate); +} + +uint32_t samples_to_cycles(uint32_t clock_rate, uint32_t cycles) +{ + return ((uint64_t)cycles) * ((uint64_t)clock_rate) / ((uint64_t)44100); +} + +void ym_adjust(chip_info *chip) +{ + ym2612_context *ym = chip->context; + if (ym->current_cycle >= MAX_NO_ADJUST) { + uint32_t deduction = ym->current_cycle - ADJUST_BUFFER; + chip->samples -= cycles_to_samples(chip->clock, deduction); + ym->current_cycle -= deduction; } - *current_cycle += cycles; - psg_run(p_context, *current_cycle); - ym_run(y_context, *current_cycle); +} + +void psg_adjust(chip_info *chip) +{ + psg_context *psg = chip->context; + if (psg->cycles >= MAX_NO_ADJUST) { + uint32_t deduction = psg->cycles - ADJUST_BUFFER; + chip->samples -= cycles_to_samples(chip->clock, deduction); + psg->cycles -= deduction; + } +} - if (*current_cycle > CYCLE_LIMIT) { - *current_cycle -= CYCLE_LIMIT; - p_context->cycles -= CYCLE_LIMIT; - y_context->current_cycle -= CYCLE_LIMIT; +void pcm_adjust(chip_info *chip) +{ + rf5c164 *pcm = chip->context; + if (pcm->cycle >= MAX_NO_ADJUST) { + uint32_t deduction = pcm->cycle - ADJUST_BUFFER; + chip->samples -= cycles_to_samples(chip->clock, deduction); + pcm->cycle -= deduction; + } +} + +void vgm_wait(chip_info *chips, uint32_t num_chips, uint32_t *completed_samples, uint32_t samples) +{ + while (samples > MAX_RUN_SAMPLES) + { + vgm_wait(chips, num_chips, completed_samples, MAX_RUN_SAMPLES); + samples -= MAX_RUN_SAMPLES; + } + *completed_samples += samples; + for (uint32_t i = 0; i < num_chips; i++) + { + chips[i].samples += samples; + chips[i].run(chips[i].context, samples_to_cycles(chips[i].clock, chips[i].samples)); + chips[i].adjust(chips + i); + } + if (*completed_samples > 44100/60) { process_events(); } } +chip_info chips[64]; + +uint8_t *find_block(data_block *head, uint32_t offset, uint32_t size) +{ + if (!head) { + return NULL; + } + while (head->size < offset) { + offset -= head->size; + head = head->next; + } + if (head->size - offset < size) { + return NULL; + } + return head->data + offset; +} + int main(int argc, char ** argv) { set_exe_str(argv[0]); data_block *blocks = NULL; data_block *seek_block = NULL; + data_block *pcm68_blocks = NULL; + data_block *pcm164_blocks = NULL; uint32_t seek_offset; uint32_t block_offset; @@ -129,34 +202,90 @@ if (argc >= 3 && !strcmp(argv[2], "-y")) { opts |= YM_OPT_WAVE_LOG; } - + char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0", TVAL_PTR).ptrval; uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : 3390; - ym2612_context y_context; - ym_init(&y_context, MCLKS_NTSC, MCLKS_PER_YM, opts); - - psg_context p_context; - psg_init(&p_context, MCLKS_NTSC, MCLKS_PER_PSG); - - VGMFILE * f = vgmopen(argv[1], "rb"); + VGMFILE f = vgmopen(argv[1], "rb"); vgm_header header; vgmread(&header, sizeof(header), 1, f); if (header.version < 0x150 || !header.data_offset) { header.data_offset = 0xC; } + if (header.version <= 0x101) { + header.ym2612_clk = header.ym2413_clk; + } + uint32_t num_chips = 0; + rf5c164 *pcm68 = NULL; + if ((header.data_offset + 0x34) >= 0x44) { + uint32_t pcm68_clock = 0; + vgmseek(f, 0x40, SEEK_SET); + vgmread(&pcm68_clock, sizeof(pcm68_clock), 1, f); + if (pcm68_clock) { + pcm68 = calloc(sizeof(*pcm68), 1); + rf5c164_init(pcm68, pcm68_clock, 1); + chips[num_chips++] = (chip_info) { + .context = pcm68, + .run = (run_fun)rf5c164_run, + .adjust = pcm_adjust, + .clock = pcm68_clock, + .samples = 0 + }; + } + } + rf5c164 *pcm164 = NULL; + if ((header.data_offset + 0x34) >= 0x70) { + uint32_t pcm164_clock = 0; + vgmseek(f, 0x6C, SEEK_SET); + vgmread(&pcm164_clock, sizeof(pcm164_clock), 1, f); + if (pcm164_clock) { + pcm164 = calloc(sizeof(*pcm164), 1); + rf5c164_init(pcm164, pcm164_clock, 1); + chips[num_chips++] = (chip_info) { + .context = pcm164, + .run = (run_fun)rf5c164_run, + .adjust = pcm_adjust, + .clock = pcm164_clock, + .samples = 0 + }; + } + } + + ym2612_context y_context; + if (header.ym2612_clk) { + ym_init(&y_context, header.ym2612_clk, 1, opts); + chips[num_chips++] = (chip_info) { + .context = &y_context, + .run = (run_fun)ym_run, + .adjust = ym_adjust, + .clock = header.ym2612_clk, + .samples = 0 + }; + } + + psg_context p_context; + if (header.sn76489_clk) { + psg_init(&p_context, header.sn76489_clk, 1); + chips[num_chips++] = (chip_info) { + .context = &p_context, + .run = (run_fun)psg_run, + .adjust = psg_adjust, + .clock = header.sn76489_clk, + .samples = 0 + }; + } + vgmseek(f, header.data_offset + 0x34, SEEK_SET); uint32_t data_size = header.eof_offset + 4 - (header.data_offset + 0x34); uint8_t * data = malloc(data_size); vgmread(data, 1, data_size, f); vgmclose(f); - uint32_t mclks_sample = MCLKS_NTSC / 44100; uint32_t loop_count = 2; uint8_t * end = data + data_size; uint8_t * cur = data; - uint32_t current_cycle = 0; + uint32_t completed_samples = 0; while (cur < end) { uint8_t cmd = *(cur++); switch(cmd) @@ -179,15 +308,14 @@ case CMD_WAIT: { uint32_t wait_time = *(cur++); wait_time |= *(cur++) << 8; - wait_time *= mclks_sample; - vgm_wait(&y_context, &p_context, ¤t_cycle, wait_time); + vgm_wait(chips, num_chips, &completed_samples, wait_time); break; } case CMD_WAIT_60: - vgm_wait(&y_context, &p_context, ¤t_cycle, 735 * mclks_sample); + vgm_wait(chips, num_chips, &completed_samples, 735); break; case CMD_WAIT_50: - vgm_wait(&y_context, &p_context, ¤t_cycle, 882 * mclks_sample); + vgm_wait(chips, num_chips, &completed_samples, 882); break; case CMD_END: if (header.loop_offset && --loop_count) { @@ -197,6 +325,92 @@ return 0; } break; + case CMD_PCM_WRITE: + if (end - cur < 11) { + fatal_error("early end of stream at offset %X\n", data-cur); + } + cur++; + { + uint8_t chip_type = *(cur++); + uint32_t read_offset = *(cur++); + read_offset |= *(cur++) << 8; + read_offset |= *(cur++) << 16; + uint32_t write_offset = *(cur++); + write_offset |= *(cur++) << 8; + write_offset |= *(cur++) << 16; + uint32_t size = *(cur++); + size |= *(cur++) << 8; + size |= *(cur++) << 16; + if (chip_type == DATA_RF5C68) { + uint8_t *src = find_block(pcm68_blocks, read_offset, size); + if (!src) { + printf("Failed to find RF6C68 data offset %X with size %X\n", read_offset, size); + } + write_offset |= pcm68->ram_bank; + write_offset &= 0xFFFF; + if (size + write_offset > 0x10000) { + size = 0x10000 - write_offset; + } + memcpy(pcm68->ram + write_offset, src, size); + printf("rf5c68 PCM write read_offset %X, write_offset %X, size %X\n", read_offset, write_offset, size); + } else if (chip_type == DATA_RF5C164) { + uint8_t *src = find_block(pcm164_blocks, read_offset, size); + if (!src) { + printf("Failed to find RF6C68 data offset %X with size %X\n", read_offset, size); + } + write_offset |= pcm164->ram_bank; + write_offset &= 0xFFFF; + if (size + write_offset > 0x10000) { + size = 0x10000 - write_offset; + } + memcpy(pcm164->ram + write_offset, src, size); + printf("rf5c164 PCM write read_offset %X, write_offset %X, size %X\n", read_offset, write_offset, size); + } else { + printf("unknown PCM write read_offset %X, write_offset %X, size %X\n", read_offset, write_offset, size); + } + } + break; + case CMD_PCM68_REG: + if (pcm68) { + uint8_t reg = *(cur++); + uint8_t value = *(cur++); + + rf5c164_write(pcm68, reg, value); + } else { + printf("CMD_PCM68_REG without rf5c68 clock\n"); + cur += 2; + } + break; + case CMD_PCM164_REG: + if (pcm164) { + uint8_t reg = *(cur++); + uint8_t value = *(cur++); + rf5c164_write(pcm164, reg, value); + } else { + printf("CMD_PCM164_REG without rf5c164 clock\n"); + cur += 2; + } + break; + case CMD_PCM68_RAM: + if (pcm68) { + uint16_t address = *(cur++); + address |= *(cur++) << 8; + address &= 0xFFF; + address |= 0x1000; + uint8_t value = *(cur++); + rf5c164_write(pcm68, address, value); + } + break; + case CMD_PCM164_RAM: + if (pcm164) { + uint16_t address = *(cur++); + address |= *(cur++) << 8; + address &= 0xFFF; + address |= 0x1000; + uint8_t value = *(cur++); + rf5c164_write(pcm164, address, value); + } + break; case CMD_DATA: { cur++; //skip compat command uint8_t data_type = *(cur++); @@ -204,8 +418,16 @@ data_size |= *(cur++) << 8; data_size |= *(cur++) << 16; data_size |= *(cur++) << 24; + data_block **curblock = NULL; if (data_type == DATA_YM2612_PCM) { - data_block ** curblock = &blocks; + curblock = &blocks; + } else if (data_type == DATA_RF5C68) { + curblock = &pcm68_blocks; + } else if (data_type == DATA_RF5C164) { + curblock = &pcm164_blocks; + } + + if (curblock) { while(*curblock) { curblock = &((*curblock)->next); @@ -245,8 +467,7 @@ default: if (cmd >= CMD_WAIT_SHORT && cmd < (CMD_WAIT_SHORT + 0x10)) { uint32_t wait_time = (cmd & 0xF) + 1; - wait_time *= mclks_sample; - vgm_wait(&y_context, &p_context, ¤t_cycle, wait_time); + vgm_wait(chips, num_chips, &completed_samples, wait_time); } else if (cmd >= CMD_YM2612_DAC && cmd < CMD_DAC_STREAM_SETUP) { if (seek_block) { ym_address_write_part1(&y_context, 0x2A); @@ -262,8 +483,7 @@ uint32_t wait_time = (cmd & 0xF); if (wait_time) { - wait_time *= mclks_sample; - vgm_wait(&y_context, &p_context, ¤t_cycle, wait_time); + vgm_wait(chips, num_chips, &completed_samples, wait_time); } } else { fatal_error("unimplemented command: %X at offset %X\n", cmd, (unsigned int)(cur - data - 1)); diff -r bafb757e1cd2 -r cfd53c94fffb ym2612.c --- a/ym2612.c Wed Feb 02 01:10:07 2022 -0800 +++ b/ym2612.c Thu Feb 03 23:15:42 2022 -0800 @@ -172,9 +172,9 @@ //TODO: Confirm these on hardware context->timer_a = TIMER_A_MAX; context->timer_b = TIMER_B_MAX; - + //TODO: Reset LFO state - + //some games seem to expect that the LR flags start out as 1 for (int i = 0; i < NUM_CHANNELS; i++) { context->channels[i].lr = 0xC0; @@ -199,11 +199,11 @@ memset(context, 0, sizeof(*context)); context->clock_inc = clock_div * 6; context->busy_cycles = BUSY_CYCLES * context->clock_inc; - context->audio = render_audio_source(master_clock, context->clock_inc * NUM_OPERATORS, 2); + context->audio = render_audio_source("YM2612", master_clock, context->clock_inc * NUM_OPERATORS, 2); //TODO: pick a randomish high initial value and lower it over time context->invalid_status_decay = 225000 * context->clock_inc; context->status_address_mask = (options & YM_OPT_3834) ? 0 : 3; - + //some games seem to expect that the LR flags start out as 1 for (int i = 0; i < NUM_CHANNELS; i++) { if (options & YM_OPT_WAVE_LOG) { @@ -480,7 +480,7 @@ operator->envelope += envelope_inc << 2; //clamp to max attenuation value if ( - operator->envelope > MAX_ENVELOPE + operator->envelope > MAX_ENVELOPE || (operator->env_phase == PHASE_RELEASE && operator->envelope >= SSG_CENTER) ) { operator->envelope = MAX_ENVELOPE; @@ -524,7 +524,7 @@ phase = operator->phase_counter = 0; } if ( - (operator->env_phase == PHASE_DECAY || operator->env_phase == PHASE_SUSTAIN) + (operator->env_phase == PHASE_DECAY || operator->env_phase == PHASE_SUSTAIN) && !(operator->ssg & SSG_HOLD) ) { start_envelope(operator, chan); @@ -665,7 +665,7 @@ context->current_op = 0; ym_output_sample(context); } - + } //printf("Done running YM2612 at cycle %d\n", context->current_cycle, to_cycle); } @@ -799,7 +799,7 @@ } vgm_ym2612_part1_write(context->vgm, context->current_cycle, reg, context->part1_regs[reg - YM_PART1_START]); } - + for (uint8_t reg = YM_PART2_START; reg < YM_REG_END; reg++) { if ((reg & 3) == 3 || (reg >= REG_FNUM_LOW_CH3 && reg < REG_ALG_FEEDBACK)) { //skip invalid registers @@ -813,7 +813,7 @@ { context->write_cycle = context->current_cycle; context->busy_start = context->current_cycle + context->clock_inc; - + if (context->selected_reg >= YM_REG_END) { return; } @@ -912,7 +912,7 @@ if (channel > 2) { channel--; } - uint8_t changes = channel == 2 + uint8_t changes = channel == 2 ? (value | context->csm_keyon) ^ (context->channels[channel].keyon | context->csm_keyon) : value ^ context->channels[channel].keyon; context->channels[channel].keyon = value & 0xF0; @@ -1037,10 +1037,10 @@ //result from op2 when op3 starts executing context->operators[channel*4+1].mod_src[0] = &context->operators[channel*4+2].output; context->operators[channel*4+1].mod_src[1] = NULL; - + //operator 2 modulated by operator 1 context->operators[channel*4+2].mod_src[0] = &context->operators[channel*4+0].output; - + //operator 4 modulated by operator 3 context->operators[channel*4+3].mod_src[0] = &context->operators[channel*4+1].output; context->operators[channel*4+3].mod_src[1] = NULL; @@ -1053,10 +1053,10 @@ //this uses a special op2 result reg on HW, but that reg will have the most recent //result from op2 when op3 starts executing context->operators[channel*4+1].mod_src[1] = &context->operators[channel*4+2].output; - + //operator 2 unmodulated context->operators[channel*4+2].mod_src[0] = NULL; - + //operator 4 modulated by operator 3 context->operators[channel*4+3].mod_src[0] = &context->operators[channel*4+1].output; context->operators[channel*4+3].mod_src[1] = NULL; @@ -1067,10 +1067,10 @@ //result from op2 when op3 starts executing context->operators[channel*4+1].mod_src[0] = &context->operators[channel*4+2].output; context->operators[channel*4+1].mod_src[1] = NULL; - + //operator 2 unmodulated context->operators[channel*4+2].mod_src[0] = NULL; - + //operator 4 modulated by operator 1+3 //this uses a special op1 result reg on HW, but that reg will have the most recent //result from op1 when op4 starts executing @@ -1081,10 +1081,10 @@ //operator 3 unmodulated context->operators[channel*4+1].mod_src[0] = NULL; context->operators[channel*4+1].mod_src[1] = NULL; - + //operator 2 modulated by operator 1 context->operators[channel*4+2].mod_src[0] = &context->operators[channel*4+0].output; - + //operator 4 modulated by operator 2+3 //op2 starts executing before this, but due to pipeline length the most current result is //not available and instead the previous result is used @@ -1095,10 +1095,10 @@ //operator 3 unmodulated context->operators[channel*4+1].mod_src[0] = NULL; context->operators[channel*4+1].mod_src[1] = NULL; - + //operator 2 modulated by operator 1 context->operators[channel*4+2].mod_src[0] = &context->operators[channel*4+0].output; - + //operator 4 modulated by operator 3 context->operators[channel*4+3].mod_src[0] = &context->operators[channel*4+1].output; context->operators[channel*4+3].mod_src[1] = NULL; @@ -1109,10 +1109,10 @@ //not available and instead the previous result is used context->operators[channel*4+1].mod_src[0] = &context->channels[channel].op1_old; context->operators[channel*4+1].mod_src[1] = NULL; - + //operator 2 modulated by operator 1 context->operators[channel*4+2].mod_src[0] = &context->operators[channel*4+0].output; - + //operator 4 modulated by operator 1 //this uses a special op1 result reg on HW, but that reg will have the most recent //result from op1 when op4 starts executing @@ -1123,10 +1123,10 @@ //operator 3 unmodulated context->operators[channel*4+1].mod_src[0] = NULL; context->operators[channel*4+1].mod_src[1] = NULL; - + //operator 2 modulated by operator 1 context->operators[channel*4+2].mod_src[0] = &context->operators[channel*4+0].output; - + //operator 4 unmodulated context->operators[channel*4+3].mod_src[0] = NULL; context->operators[channel*4+3].mod_src[1] = NULL; @@ -1135,9 +1135,9 @@ //everything is an output so no modulation (except for op 1 feedback) context->operators[channel*4+1].mod_src[0] = NULL; context->operators[channel*4+1].mod_src[1] = NULL; - + context->operators[channel*4+2].mod_src[0] = NULL; - + context->operators[channel*4+3].mod_src[0] = NULL; context->operators[channel*4+3].mod_src[1] = NULL; break; @@ -1182,7 +1182,7 @@ context->last_status_cycle = cycle; } return status; - + } void ym_print_channel_info(ym2612_context *context, int channel)