changeset 364:62177cc39049

Incredibly broken YM2612 support plus a fix to Z80 bus request
author Mike Pavone <pavone@retrodev.com>
date Wed, 29 May 2013 00:57:19 -0700
parents c708dea45f8b
children 3ba3b6656fff
files blastem.c psg.c psg.h render.h render_sdl.c ym2612.c ym2612.h ztestrun.c
diffstat 8 files changed, 124 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- 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());
--- 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++;
--- 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;
--- 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();
--- 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;
 }
--- 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 <string.h>
 #include <math.h>
 #include <stdio.h>
+#include <stdlib.h>
 #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;
 		}
--- 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);
--- 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;