# HG changeset patch # User Mike Pavone # Date 1369814239 25200 # Node ID 62177cc39049bdedc6fea5d7f88d9dd99cfcb593 # Parent c708dea45f8ba03de69b8f6ee05c8a9865806e17 Incredibly broken YM2612 support plus a fix to Z80 bus request diff -r c708dea45f8b -r 62177cc39049 blastem.c --- a/blastem.c Wed May 29 00:13:48 2013 -0700 +++ b/blastem.c Wed May 29 00:57:19 2013 -0700 @@ -198,7 +198,7 @@ sync_z80(z_context, mclks); if (mclks >= mclks_per_frame) { ym_run(gen->ym, context->current_cycle); - gen->ym->current_cycle -= ((mclks_per_frame/MCLKS_PER_68K) / 6) * 6; + gen->ym->current_cycle -= mclks_per_frame/MCLKS_PER_68K; //printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks); vdp_run_context(v_context, mclks_per_frame); psg_run(gen->psg, mclks/MCLKS_PER_PSG); @@ -534,8 +534,8 @@ if(!reset && !busreq) { busack_cycle = ((gen->z80->current_cycle + Z80_ACK_DELAY) * MCLKS_PER_Z80) / MCLKS_PER_68K;//context->current_cycle + Z80_ACK_DELAY; new_busack = Z80_REQ_ACK; - busreq = 1; } + busreq = 1; } else { if (busreq) { dputs("releasing z80 bus"); @@ -635,8 +635,8 @@ if(!reset && !busreq) { busack_cycle = ((gen->z80->current_cycle + Z80_ACK_DELAY) * MCLKS_PER_Z80) / MCLKS_PER_68K;//context->current_cycle + Z80_ACK_DELAY; new_busack = Z80_REQ_ACK; - busreq = 1; } + busreq = 1; } else { if (busreq) { dprintf("releasing Z80 bus @ %d\n", (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80); @@ -1420,6 +1420,8 @@ #define PSG_CLKS_NTSC (3579545/16) #define PSG_CLKS_PAL (3546893/16) +#define YM_CLKS_NTSC 7670454 +#define YM_CLKS_PAL 7600485 int main(int argc, char ** argv) { @@ -1505,7 +1507,7 @@ init_vdp_context(&v_context); ym2612_context y_context; - ym_init(&y_context); + ym_init(&y_context, render_sample_rate(), fps == 60 ? YM_CLKS_NTSC : YM_CLKS_PAL, render_audio_buffer()); psg_context p_context; psg_init(&p_context, render_sample_rate(), fps == 60 ? PSG_CLKS_NTSC : PSG_CLKS_PAL, render_audio_buffer()); diff -r c708dea45f8b -r 62177cc39049 psg.c --- a/psg.c Wed May 29 00:13:48 2013 -0700 +++ b/psg.c Wed May 29 00:57:19 2013 -0700 @@ -103,7 +103,7 @@ } context->audio_buffer[context->buffer_pos++] = acc; if (context->buffer_pos == context->samples_frame) { - render_wait_audio(context); + render_wait_psg(context); } } context->cycles++; diff -r c708dea45f8b -r 62177cc39049 psg.h --- a/psg.h Wed May 29 00:13:48 2013 -0700 +++ b/psg.h Wed May 29 00:57:19 2013 -0700 @@ -9,7 +9,6 @@ double buffer_fraction; double buffer_inc; uint32_t buffer_pos; - uint32_t back_pos; uint32_t cycles; uint32_t samples_frame; uint16_t lsfr; diff -r c708dea45f8b -r 62177cc39049 render.h --- a/render.h Wed May 29 00:13:48 2013 -0700 +++ b/render.h Wed May 29 00:57:19 2013 -0700 @@ -3,10 +3,12 @@ #include "vdp.h" #include "psg.h" +#include "ym2612.h" void render_init(int width, int height, char * title, uint32_t fps); void render_context(vdp_context * context); void render_wait_quit(vdp_context * context); -void render_wait_audio(psg_context * context); +void render_wait_psg(psg_context * context); +void render_wait_ym(ym2612_context * context); int wait_render_frame(vdp_context * context, int frame_limit); void render_fps(uint32_t fps); uint32_t render_audio_buffer(); diff -r c708dea45f8b -r 62177cc39049 render_sdl.c --- a/render_sdl.c Wed May 29 00:13:48 2013 -0700 +++ b/render_sdl.c Wed May 29 00:57:19 2013 -0700 @@ -16,15 +16,16 @@ uint32_t min_delay; uint32_t frame_delay = 1000/60; -int16_t * current_audio = NULL; -int16_t * next_audio = NULL; +int16_t * current_psg = NULL; +int16_t * current_ym = NULL; uint32_t buffer_samples, sample_rate; uint32_t missing_count; SDL_mutex * audio_mutex; SDL_cond * audio_ready; -SDL_cond * audio_cond; +SDL_cond * psg_cond; +SDL_cond * ym_cond; uint8_t quitting = 0; void audio_callback(void * userdata, uint8_t *byte_stream, int len) @@ -32,21 +33,33 @@ //puts("audio_callback"); int16_t * stream = (int16_t *)byte_stream; int samples = len/(sizeof(int16_t)*2); - int16_t * source_buf; + int16_t * psg_buf, * ym_buf; uint8_t local_quit; SDL_LockMutex(audio_mutex); - while (!current_audio && !quitting) { - SDL_CondWait(audio_ready, audio_mutex); - } + psg_buf = NULL; + ym_buf = NULL; + do { + if (!psg_buf) { + psg_buf = current_psg; + current_psg = NULL; + SDL_CondSignal(psg_cond); + } + if (!ym_buf) { + ym_buf = current_ym; + current_ym = NULL; + SDL_CondSignal(ym_cond); + } + if (!quitting && (!psg_buf || !ym_buf)) { + SDL_CondWait(audio_ready, audio_mutex); + } + } while(!quitting && (!psg_buf || !ym_buf)); + local_quit = quitting; - source_buf = current_audio; - current_audio = NULL; - SDL_CondSignal(audio_cond); SDL_UnlockMutex(audio_mutex); if (!local_quit) { for (int i = 0; i < samples; i++) { - *(stream++) = source_buf[i]; - *(stream++) = source_buf[i]; + *(stream++) = psg_buf[i] + *(ym_buf++); + *(stream++) = psg_buf[i] + *(ym_buf++); } } } @@ -113,7 +126,8 @@ frame_delay = 1000/fps; audio_mutex = SDL_CreateMutex(); - audio_cond = SDL_CreateCond(); + psg_cond = SDL_CreateCond(); + ym_cond = SDL_CreateCond(); audio_ready = SDL_CreateCond(); SDL_AudioSpec desired, actual; @@ -460,17 +474,32 @@ return ret; } -void render_wait_audio(psg_context * context) +void render_wait_psg(psg_context * context) { SDL_LockMutex(audio_mutex); - while (current_audio != NULL) { - SDL_CondWait(audio_cond, audio_mutex); + while (current_psg != NULL) { + SDL_CondWait(psg_cond, audio_mutex); } - current_audio = context->audio_buffer; + current_psg = context->audio_buffer; SDL_CondSignal(audio_ready); context->audio_buffer = context->back_buffer; - context->back_buffer = current_audio; + context->back_buffer = current_psg; + SDL_UnlockMutex(audio_mutex); + context->buffer_pos = 0; +} + +void render_wait_ym(ym2612_context * context) +{ + SDL_LockMutex(audio_mutex); + while (current_ym != NULL) { + SDL_CondWait(ym_cond, audio_mutex); + } + current_ym = context->audio_buffer; + SDL_CondSignal(audio_ready); + + context->audio_buffer = context->back_buffer; + context->back_buffer = current_ym; SDL_UnlockMutex(audio_mutex); context->buffer_pos = 0; } diff -r c708dea45f8b -r 62177cc39049 ym2612.c --- a/ym2612.c Wed May 29 00:13:48 2013 -0700 +++ b/ym2612.c Wed May 29 00:57:19 2013 -0700 @@ -1,10 +1,12 @@ #include #include #include +#include #include "ym2612.h" +#include "render.h" #define BUSY_CYCLES 17 -#define TIMERA_UPDATE_PERIOD 144 +#define OP_UPDATE_PERIOD 144 enum { REG_TIMERA_HIGH = 0x24, @@ -75,16 +77,20 @@ uint16_t rate_table[64]; #define MAX_ENVELOPE 0xFFC - +#define YM_DIVIDER 2 uint16_t round_fixed_point(double value, int dec_bits) { return value * (1 << dec_bits) + 0.5; } -void ym_init(ym2612_context * context) +void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t clock_rate, uint32_t sample_limit) { memset(context, 0, sizeof(*context)); + context->audio_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2); + context->back_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2); + context->buffer_inc = (double)sample_rate / (double)(clock_rate/OP_UPDATE_PERIOD); + context->sample_limit = sample_limit*2; for (int i = 0; i < NUM_OPERATORS; i++) { context->operators[i].envelope = MAX_ENVELOPE; context->operators[i].env_phase = PHASE_RELEASE; @@ -134,9 +140,8 @@ //printf("Running YM2612 from cycle %d to cycle %d\n", context->current_cycle, to_cycle); //TODO: Fix channel update order OR remap channels in register write for (; context->current_cycle < to_cycle; context->current_cycle += 6) { - uint32_t update_cyc = context->current_cycle % 144; //Update timers at beginning of 144 cycle period - if (!update_cyc && context->timer_control & BIT_TIMERA_ENABLE) { + if (!context->current_op && context->timer_control & BIT_TIMERA_ENABLE) { if (context->timer_a) { context->timer_a--; } else { @@ -146,7 +151,7 @@ context->timer_a = context->timer_a_load; } if (context->timer_control & BIT_TIMERB_ENABLE) { - uint32_t b_cyc = (context->current_cycle / 144) % 16; + uint32_t b_cyc = (context->current_cycle / OP_UPDATE_PERIOD) % 16; if (!b_cyc) { if (context->timer_b) { context->timer_b--; @@ -160,10 +165,9 @@ } } //Update Envelope Generator - if (update_cyc == 0 || update_cyc == 72) { - uint32_t env_cyc = context->current_cycle / 72; - uint32_t op = env_cyc % 24; - env_cyc /= 24; + if (!(context->current_op % 3)) { + uint32_t env_cyc = context->env_counter; + uint32_t op = context->current_env_op; ym_operator * operator = context->operators + op; ym_channel * channel = context->channels + op/4; uint8_t rate; @@ -214,15 +218,18 @@ operator->env_phase = PHASE_SUSTAIN; } } - - + } + context->current_env_op++; + if (context->current_env_op == NUM_OPERATORS) { + context->current_env_op = 0; + context->env_counter++; } } //Update Phase Generator - uint32_t channel = update_cyc / 24; + uint32_t channel = context->current_op / 4; if (channel != 5 || !context->dac_enable) { - uint32_t op = (update_cyc) / 6; + uint32_t op = context->current_op; //printf("updating operator %d of channel %d\n", op, channel); ym_operator * operator = context->operators + op; ym_channel * chan = context->channels + channel; @@ -303,6 +310,32 @@ } //puts("operator update done"); } + context->current_op++; + if (context->current_op == NUM_OPERATORS) { + context->current_op = 0; + context->buffer_fraction += context->buffer_inc; + if (context->buffer_fraction > 1.0) { + context->buffer_fraction -= 1.0; + context->audio_buffer[context->buffer_pos] = 0; + context->audio_buffer[context->buffer_pos + 1] = 0; + for (int i = 0; i < NUM_CHANNELS; i++) { + uint16_t value = context->channels[i].output & 0x3FE0; + if (value & 0x2000) { + value |= 0xC000; + } + if (context->channels[i].lr & 0x80) { + context->audio_buffer[context->buffer_pos] += value / 2; + } + if (context->channels[i].lr & 0x40) { + context->audio_buffer[context->buffer_pos+1] += value / 2; + } + } + context->buffer_pos += 2; + if (context->buffer_pos == context->sample_limit) { + render_wait_ym(context); + } + } + } } if (context->current_cycle >= context->write_cycle + BUSY_CYCLES) { context->status &= 0x7F; @@ -312,12 +345,14 @@ void ym_address_write_part1(ym2612_context * context, uint8_t address) { + //printf("address_write_part1: %X\n", address); context->selected_reg = address; context->selected_part = 0; } void ym_address_write_part2(ym2612_context * context, uint8_t address) { + //printf("address_write_part2: %X\n", address); context->selected_reg = address; context->selected_part = 1; } @@ -393,6 +428,7 @@ //0.5 inc >>= 1; } + operator->phase_inc = inc; } void ym_data_write(ym2612_context * context, uint8_t value) @@ -440,9 +476,11 @@ case REG_DAC: if (context->dac_enable) { context->channels[5].output = (((int16_t)value) - 0x80) << 6; + //printf("DAC Write %X(%d)\n", context->channels[5].output, context->channels[5].output); } break; case REG_DAC_ENABLE: + //printf("DAC Enable: %X\n", value); context->dac_enable = value & 0x80; break; } diff -r c708dea45f8b -r 62177cc39049 ym2612.h --- a/ym2612.h Wed May 29 00:13:48 2013 -0700 +++ b/ym2612.h Wed May 29 00:57:19 2013 -0700 @@ -35,12 +35,21 @@ } ym_channel; typedef struct { + int16_t *audio_buffer; + int16_t *back_buffer; + double buffer_fraction; + double buffer_inc; + uint32_t buffer_pos; + uint32_t sample_limit; uint32_t current_cycle; uint32_t write_cycle; ym_operator operators[NUM_OPERATORS]; ym_channel channels[NUM_CHANNELS]; uint16_t timer_a; uint16_t timer_a_load; + uint16_t env_counter; + uint8_t current_op; + uint8_t current_env_op; uint8_t timer_b; uint8_t timer_b_load; uint8_t timer_control; @@ -50,7 +59,7 @@ uint8_t selected_part; } ym2612_context; -void ym_init(ym2612_context * context); +void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t clock_rate, uint32_t sample_limit); void ym_run(ym2612_context * context, uint32_t to_cycle); void ym_address_write_part1(ym2612_context * context, uint8_t address); void ym_address_write_part2(ym2612_context * context, uint8_t address); diff -r c708dea45f8b -r 62177cc39049 ztestrun.c --- a/ztestrun.c Wed May 29 00:13:48 2013 -0700 +++ b/ztestrun.c Wed May 29 00:57:19 2013 -0700 @@ -24,6 +24,11 @@ return context; } +z80_context * z80_vdp_port_write(uint16_t location, z80_context * context, uint8_t value) +{ + return context; +} + int main(int argc, char ** argv) { long filesize;