changeset 354:15dd6418fe67

Initial PSG support. Mostly works, noise channel is borked though.
author Mike Pavone <pavone@retrodev.com>
date Thu, 23 May 2013 23:42:42 -0700
parents a60e527cd21f
children fcd31d19dddd
files Makefile blastem.c blastem.h psg.c psg.h render.h render_sdl.c
diffstat 7 files changed, 381 insertions(+), 131 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Wed May 22 09:37:02 2013 -0700
+++ b/Makefile	Thu May 23 23:42:42 2013 -0700
@@ -7,8 +7,8 @@
 
 all : dis trans stateview blastem
 
-blastem : blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o render_sdl.o
-	$(CC) -o blastem blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o render_sdl.o `pkg-config --libs $(LIBS)`
+blastem : blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o psg.o render_sdl.o
+	$(CC) -o blastem blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o psg.o render_sdl.o `pkg-config --libs $(LIBS)`
 
 dis : dis.o 68kinst.o
 	$(CC) -o dis dis.o 68kinst.o
--- a/blastem.c	Wed May 22 09:37:02 2013 -0700
+++ b/blastem.c	Thu May 23 23:42:42 2013 -0700
@@ -14,6 +14,7 @@
 #define Z80_RAM_BYTES 8 * 1024
 #define MCLKS_PER_68K 7
 #define MCLKS_PER_Z80 15
+#define MCLKS_PER_PSG (MCLKS_PER_Z80*16)
 //TODO: Figure out the exact value for this
 #define CYCLE_NEVER 0xFFFFFFFF
 #define LINES_NTSC 262
@@ -200,6 +201,8 @@
 		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);
+		gen->psg->cycles -= mclks_per_frame/MCLKS_PER_PSG;
 		if (!headless) {
 			break_on_sync |= wait_render_frame(v_context, frame_limit);
 		}
@@ -228,6 +231,7 @@
 	} else {
 		//printf("running VDP for %d cycles\n", mclks - v_context->cycles);
 		vdp_run_context(v_context, mclks);
+		psg_run(gen->psg, mclks/MCLKS_PER_PSG);
 	}
 	if (context->int_ack) {
 		vdp_int_ack(v_context, context->int_ack);
@@ -318,7 +322,9 @@
 			context->current_cycle = v_context->cycles / MCLKS_PER_68K;
 		}
 	} else if (vdp_port < 0x18) {
-		//TODO: Implement PSG
+		genesis_context * gen = context->system;
+		psg_run(gen->psg, (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_PSG);
+		psg_write(gen->psg, value);
 	} else {
 		//TODO: Implement undocumented test register(s)
 	}
@@ -327,7 +333,7 @@
 
 m68k_context * vdp_port_write_b(uint32_t vdp_port, m68k_context * context, uint8_t value)
 {
-	return vdp_port_write(vdp_port, context, value | value << 8);
+	return vdp_port_write(vdp_port, context, vdp_port < 0x10 ? value | value << 8 : value);
 }
 
 uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context)
@@ -1385,6 +1391,9 @@
 	}
 }
 
+#define PSG_CLKS_NTSC (3579545/16)
+#define PSG_CLKS_PAL (3546893/16)
+
 int main(int argc, char ** argv)
 {
 	if (argc < 2) {
@@ -1456,12 +1465,13 @@
 	update_title();
 	width = width < 320 ? 320 : width;
 	height = height < 240 ? (width/320) * 240 : height;
-	if (!headless) {
-		render_init(width, height, title);
-	}
+	uint32_t fps = 60;
 	if (version_reg & 0x40) {
 		mclks_per_frame = MCLKS_LINE * LINES_PAL;
-		render_fps(50);
+		fps = 50;
+	}
+	if (!headless) {
+		render_init(width, height, title, fps);
 	}
 	vdp_context v_context;
 	
@@ -1470,6 +1480,9 @@
 	ym2612_context y_context;
 	ym_init(&y_context);
 	
+	psg_context p_context;
+	psg_init(&p_context, render_sample_rate(), fps == 60 ? PSG_CLKS_NTSC : PSG_CLKS_PAL, render_audio_buffer());
+	
 	z80_context z_context;
 	x86_z80_options z_opts;
 	init_x86_z80_opts(&z_opts);
@@ -1486,6 +1499,7 @@
 	gen.z80 = &z_context;
 	gen.vdp = &v_context;
 	gen.ym = &y_context;
+	gen.psg = &p_context;
 	genesis = &gen;
 	
 	int fname_size = strlen(argv[1]);
--- a/blastem.h	Wed May 22 09:37:02 2013 -0700
+++ b/blastem.h	Thu May 23 23:42:42 2013 -0700
@@ -6,6 +6,7 @@
 #include "z80_to_x86.h"
 #include "ym2612.h"
 #include "vdp.h"
+#include "psg.h"
 
 typedef struct {
 	uint32_t th_counter;
@@ -24,6 +25,7 @@
 	z80_context    *z80;
 	vdp_context    *vdp;
 	ym2612_context *ym;
+	psg_context    *psg;
 	uint8_t        *save_ram;
 	uint32_t       save_ram_mask;
 	uint32_t       save_flags;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/psg.c	Thu May 23 23:42:42 2013 -0700
@@ -0,0 +1,112 @@
+#include "psg.h"
+#include "render.h"
+#include <string.h>
+#include <stdlib.h>
+
+void psg_init(psg_context * context, uint32_t sample_rate, uint32_t clock_rate, uint32_t samples_frame)
+{
+	memset(context, 0, sizeof(*context));
+	context->audio_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame);
+	context->back_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame);
+	context->buffer_inc = (double)sample_rate / (double)clock_rate;
+	context->samples_frame = samples_frame;
+	for (int i = 0; i < 4; i++) {
+		context->volume[i] = 0xF;
+	}
+}
+
+void psg_write(psg_context * context, uint8_t value)
+{
+	if (value & 0x80) {
+		context->latch = value & 0x70;
+		uint8_t channel = value >> 5 & 0x3;
+		if (value & 0x10) {
+			context->volume[channel] = value & 0xF;
+		} else {
+			if (channel == 3) {
+				switch(value & 0x3)
+				{
+				case 0:
+				case 1:
+				case 2:
+					context->counter_load[3] = 0x10 << (value & 0x3);
+					context->noise_use_tone = 0;
+					break;
+				default:
+					context->counter_load[3] = context->counter_load[2];
+					context->noise_use_tone = 1;
+				}
+				context->noise_type = value & 0x4;
+				context->lsfr = 0x8000;
+			} else {
+				context->counter_load[channel] = (context->counter_load[channel] & 0x3F0) | (value & 0xF);
+				if (channel == 2 && context->noise_use_tone) {
+					context->counter_load[3] = context->counter_load[2];
+				}
+			}
+		}
+	} else {
+		if (!(context->latch & 0x10)) {
+			uint8_t channel = context->latch >> 5 & 0x3;
+			if (channel != 3) {
+				context->counter_load[channel] = (value << 4 & 0x3F0) | (context->counter_load[channel] & 0xF);
+				if (channel == 2 && context->noise_use_tone) {
+					context->counter_load[3] = context->counter_load[2];
+				}
+			}
+		}
+	}
+}
+
+#define PSG_VOL_DIV 2
+
+//table shamelessly swiped from PSG doc from smspower.org
+int16_t volume_table[16] = {
+	32767/PSG_VOL_DIV, 26028/PSG_VOL_DIV, 20675/PSG_VOL_DIV, 16422/PSG_VOL_DIV, 13045/PSG_VOL_DIV, 10362/PSG_VOL_DIV,
+	8231/PSG_VOL_DIV, 6568/PSG_VOL_DIV, 5193/PSG_VOL_DIV, 4125/PSG_VOL_DIV, 3277/PSG_VOL_DIV, 2603/PSG_VOL_DIV, 
+	2067/PSG_VOL_DIV, 1642/PSG_VOL_DIV, 1304/PSG_VOL_DIV, 0
+};
+
+void psg_run(psg_context * context, uint32_t cycles)
+{
+	while (context->cycles < cycles) {
+		for (int i = 0; i < 4; i++) {
+			if (context->counters[i]) {
+				context->counters[i] -= 1;
+			}
+			if (!context->counters[i]) {
+				context->counters[i] = context->counter_load[i];
+				context->output_state[i] = !context->output_state[i];
+				if (i == 3 && context->output_state[i]) {
+					context->noise_out = context->lsfr & 1;
+					context->lsfr = (context->lsfr >> 1) | (context->lsfr << 15);
+					if (context->noise_type) {
+						//white noise
+						if (context->lsfr & 0x40) {
+							context->lsfr ^= 0x8000;
+						}
+					}
+				}
+			}
+		}
+		context->buffer_fraction += context->buffer_inc;
+		if (context->buffer_fraction >= 1.0) {
+			context->buffer_fraction -= 1.0;
+			int16_t acc = 0;
+			for (int i = 0; i < 3; i++) {
+				if (context->output_state[i]) {
+					acc += volume_table[context->volume[i]];
+				}
+			}
+			if (context->noise_out) {
+				acc += volume_table[context->volume[3]];
+			}
+			context->audio_buffer[context->buffer_pos++] = acc;
+			if (context->buffer_pos == context->samples_frame) {
+				render_wait_audio(context);
+			}
+		}
+		context->cycles++;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/psg.h	Thu May 23 23:42:42 2013 -0700
@@ -0,0 +1,32 @@
+#ifndef PSG_CONTEXT_H_
+#define PSG_CONTEXT_H_
+
+#include <stdint.h>
+
+typedef struct {
+	int16_t  *audio_buffer;
+	int16_t  *back_buffer;
+	double   buffer_fraction;
+	double   buffer_inc;
+	uint32_t buffer_pos;
+	uint32_t back_pos;
+	uint32_t cycles;
+	uint32_t samples_frame;
+	uint16_t lsfr;
+	uint16_t counter_load[4];
+	uint16_t counters[4];
+	uint8_t  volume[4];
+	uint8_t  output_state[4];
+	uint8_t  noise_out;
+	uint8_t  noise_use_tone;
+	uint8_t  noise_type;
+	uint8_t  latch;
+} psg_context;
+
+
+void psg_init(psg_context * context, uint32_t sample_rate, uint32_t clock_rate, uint32_t samples_frame);
+void psg_write(psg_context * context, uint8_t value);
+void psg_run(psg_context * context, uint32_t cycles);
+
+#endif //PSG_CONTEXT_H_
+
--- a/render.h	Wed May 22 09:37:02 2013 -0700
+++ b/render.h	Thu May 23 23:42:42 2013 -0700
@@ -2,11 +2,15 @@
 #define RENDER_SDL_H_
 
 #include "vdp.h"
-void render_init(int width, int height, char * title);
+#include "psg.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);
 int wait_render_frame(vdp_context * context, int frame_limit);
 void render_fps(uint32_t fps);
+uint32_t render_audio_buffer();
+uint32_t render_sample_rate();
 
 #endif //RENDER_SDL_H_
 
--- a/render_sdl.c	Wed May 22 09:37:02 2013 -0700
+++ b/render_sdl.c	Thu May 23 23:42:42 2013 -0700
@@ -16,9 +16,41 @@
 uint32_t min_delay;
 uint32_t frame_delay = 1000/60;
 
-void render_init(int width, int height, char * title)
+int16_t * current_audio = NULL;
+int16_t * next_audio = NULL;
+
+uint32_t buffer_samples, sample_rate;
+uint32_t missing_count;
+
+SDL_mutex * audio_mutex;
+SDL_cond * audio_ready;
+SDL_cond * audio_cond;
+
+void audio_callback(void * userdata, uint8_t *byte_stream, int len)
 {
-	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+	//puts("audio_callback");
+	int16_t * stream = (int16_t *)byte_stream;
+	int samples = len/(sizeof(int16_t)*2);
+	int16_t * source_buf;
+	
+	SDL_LockMutex(audio_mutex);
+		while (!current_audio) {
+			SDL_CondWait(audio_ready, audio_mutex);
+		}
+		source_buf = current_audio;
+		current_audio = NULL;
+		SDL_CondSignal(audio_cond);
+	SDL_UnlockMutex(audio_mutex);
+	
+	for (int i = 0; i < samples; i++) {
+		*(stream++) = source_buf[i];
+		*(stream++) = source_buf[i];
+	}
+}
+
+void render_init(int width, int height, char * title, uint32_t fps)
+{
+	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
         fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
         exit(1);
     }
@@ -64,6 +96,29 @@
     	min_delay = 1;
     }
     printf("minimum delay: %d\n", min_delay);
+    
+    frame_delay = 1000/fps;
+    
+    audio_mutex = SDL_CreateMutex();
+    audio_cond = SDL_CreateCond();
+    audio_ready = SDL_CreateCond();
+    
+    SDL_AudioSpec desired, actual;
+    desired.freq = 48000;
+    desired.format = AUDIO_S16SYS;
+    desired.channels = 2;
+    desired.samples = 1024;
+    desired.callback = audio_callback;
+    desired.userdata = NULL;
+    
+    if (SDL_OpenAudio(&desired, &actual) < 0) {
+    	fprintf(stderr, "Unable to open SDL audio: %s\n", SDL_GetError());
+    	exit(1);
+    }
+    buffer_samples = actual.samples;
+    sample_rate = actual.freq;
+    printf("Initialized audio at frequency %d with a %d sample buffer\n", actual.freq, actual.samples);
+    SDL_PauseAudio(0);
 }
 
 void render_context(vdp_context * context)
@@ -230,135 +285,141 @@
 #define BUTTON_START 0x20
 #define BUTTON_C     0x20
 
+int32_t handle_event(SDL_Event *event)
+{
+	FILE * outfile;
+	switch (event->type) {
+	case SDL_KEYDOWN:
+		switch(event->key.keysym.sym)
+		{
+		case SDLK_LEFTBRACKET:
+			render_dbg++;
+			if (render_dbg == 4) {
+				render_dbg = 0;
+			}
+			break;
+		case SDLK_RIGHTBRACKET:
+			debug_pal++;
+			if (debug_pal == 4) {
+				debug_pal = 0;
+			}
+			break;
+		case SDLK_t:
+			/*outfile = fopen("state.gst", "wb");
+			fwrite("GST\0\0\0\xE0\x40", 1, 8, outfile);
+			vdp_save_state(context, outfile);
+			fclose(outfile);
+			puts("state saved to state.gst");*/
+			break;
+		case SDLK_u:
+			return 1;
+		case SDLK_RETURN:
+			gamepad_1.input[GAMEPAD_TH0] |= BUTTON_START;
+			break;
+		case SDLK_UP:
+			gamepad_1.input[GAMEPAD_TH0] |= DPAD_UP;
+			gamepad_1.input[GAMEPAD_TH1] |= DPAD_UP;
+			break;
+		case SDLK_DOWN:
+			gamepad_1.input[GAMEPAD_TH0] |= DPAD_DOWN;
+			gamepad_1.input[GAMEPAD_TH1] |= DPAD_DOWN;
+			break;
+		case SDLK_LEFT:
+			gamepad_1.input[GAMEPAD_TH1] |= DPAD_LEFT;
+			break;
+		case SDLK_RIGHT:
+			gamepad_1.input[GAMEPAD_TH1] |= DPAD_RIGHT;
+			break;
+		case SDLK_a:
+			gamepad_1.input[GAMEPAD_TH0] |= BUTTON_A;
+			//printf("BUTTON_A Dn | GAMEPAD_TH0: %X\n", gamepad_1.input[GAMEPAD_TH0]);
+			break;
+		case SDLK_s:
+			gamepad_1.input[GAMEPAD_TH1] |= BUTTON_B;
+			gamepad_1.input[GAMEPAD_EXTRA] |= BUTTON_B;
+			break;
+		case SDLK_d:
+			gamepad_1.input[GAMEPAD_TH1] |= BUTTON_C;
+			gamepad_1.input[GAMEPAD_EXTRA] |= BUTTON_C;
+			break;
+		case SDLK_q:
+			gamepad_1.input[GAMEPAD_EXTRA] |= BUTTON_X;
+			break;
+		case SDLK_w:
+			gamepad_1.input[GAMEPAD_EXTRA] |= BUTTON_Y;
+			break;
+		case SDLK_e:
+			gamepad_1.input[GAMEPAD_EXTRA] |= BUTTON_Z;
+			break;
+		case SDLK_f:
+			gamepad_1.input[GAMEPAD_EXTRA] |= BUTTON_MODE;
+			break;
+		}
+		break;
+	case SDL_KEYUP:
+		switch(event->key.keysym.sym)
+		{
+		case SDLK_RETURN:
+			gamepad_1.input[GAMEPAD_TH0] &= ~BUTTON_START;
+			break;
+		case SDLK_UP:
+			gamepad_1.input[GAMEPAD_TH0] &= ~DPAD_UP;
+			gamepad_1.input[GAMEPAD_TH1] &= ~DPAD_UP;
+			break;
+		case SDLK_DOWN:
+			gamepad_1.input[GAMEPAD_TH0] &= ~DPAD_DOWN;
+			gamepad_1.input[GAMEPAD_TH1] &= ~DPAD_DOWN;
+			break;
+		case SDLK_LEFT:
+			gamepad_1.input[GAMEPAD_TH1] &= ~DPAD_LEFT;
+			break;
+		case SDLK_RIGHT:
+			gamepad_1.input[GAMEPAD_TH1] &= ~DPAD_RIGHT;
+			break;
+		case SDLK_a:
+			gamepad_1.input[GAMEPAD_TH0] &= ~BUTTON_A;
+			//printf("BUTTON_A Up | GAMEPAD_TH0: %X\n", gamepad_1.input[GAMEPAD_TH0]);
+			break;
+		case SDLK_s:
+			gamepad_1.input[GAMEPAD_TH1] &= ~BUTTON_B;
+			gamepad_1.input[GAMEPAD_EXTRA] &= ~BUTTON_B;
+			break;
+		case SDLK_d:
+			gamepad_1.input[GAMEPAD_TH1] &= ~BUTTON_C;
+			gamepad_1.input[GAMEPAD_EXTRA] &= ~BUTTON_C;
+			break;
+		case SDLK_q:
+			gamepad_1.input[GAMEPAD_EXTRA] &= ~BUTTON_X;
+			break;
+		case SDLK_w:
+			gamepad_1.input[GAMEPAD_EXTRA] &= ~BUTTON_Y;
+			break;
+		case SDLK_e:
+			gamepad_1.input[GAMEPAD_EXTRA] &= ~BUTTON_Z;
+			break;
+		case SDLK_f:
+			gamepad_1.input[GAMEPAD_EXTRA] &= ~BUTTON_MODE;
+			break;
+		}
+		break;
+	case SDL_QUIT:
+		puts("");
+		exit(0);
+	}
+	return 0;
+}
+
 uint32_t frame_counter = 0;
 uint32_t start = 0;
 int wait_render_frame(vdp_context * context, int frame_limit)
 {
-	FILE * outfile;
 	SDL_Event event;
 	int ret = 0;
 	while(SDL_PollEvent(&event)) {
-		switch (event.type) {
-		case SDL_KEYDOWN:
-			switch(event.key.keysym.sym)
-			{
-			case SDLK_LEFTBRACKET:
-				render_dbg++;
-				if (render_dbg == 4) {
-					render_dbg = 0;
-				}
-				break;
-			case SDLK_RIGHTBRACKET:
-				debug_pal++;
-				if (debug_pal == 4) {
-					debug_pal = 0;
-				}
-				break;
-			case SDLK_t:
-				outfile = fopen("state.gst", "wb");
-				fwrite("GST\0\0\0\xE0\x40", 1, 8, outfile);
-				vdp_save_state(context, outfile);
-				fclose(outfile);
-				puts("state saved to state.gst");
-				break;
-			case SDLK_u:
-				ret = 1;
-				break;
-			case SDLK_RETURN:
-				gamepad_1.input[GAMEPAD_TH0] |= BUTTON_START;
-				break;
-			case SDLK_UP:
-				gamepad_1.input[GAMEPAD_TH0] |= DPAD_UP;
-				gamepad_1.input[GAMEPAD_TH1] |= DPAD_UP;
-				break;
-			case SDLK_DOWN:
-				gamepad_1.input[GAMEPAD_TH0] |= DPAD_DOWN;
-				gamepad_1.input[GAMEPAD_TH1] |= DPAD_DOWN;
-				break;
-			case SDLK_LEFT:
-				gamepad_1.input[GAMEPAD_TH1] |= DPAD_LEFT;
-				break;
-			case SDLK_RIGHT:
-				gamepad_1.input[GAMEPAD_TH1] |= DPAD_RIGHT;
-				break;
-			case SDLK_a:
-				gamepad_1.input[GAMEPAD_TH0] |= BUTTON_A;
-				//printf("BUTTON_A Dn | GAMEPAD_TH0: %X\n", gamepad_1.input[GAMEPAD_TH0]);
-				break;
-			case SDLK_s:
-				gamepad_1.input[GAMEPAD_TH1] |= BUTTON_B;
-				gamepad_1.input[GAMEPAD_EXTRA] |= BUTTON_B;
-				break;
-			case SDLK_d:
-				gamepad_1.input[GAMEPAD_TH1] |= BUTTON_C;
-				gamepad_1.input[GAMEPAD_EXTRA] |= BUTTON_C;
-				break;
-			case SDLK_q:
-				gamepad_1.input[GAMEPAD_EXTRA] |= BUTTON_X;
-				break;
-			case SDLK_w:
-				gamepad_1.input[GAMEPAD_EXTRA] |= BUTTON_Y;
-				break;
-			case SDLK_e:
-				gamepad_1.input[GAMEPAD_EXTRA] |= BUTTON_Z;
-				break;
-			case SDLK_f:
-				gamepad_1.input[GAMEPAD_EXTRA] |= BUTTON_MODE;
-				break;
-			}
-			break;
-		case SDL_KEYUP:
-			switch(event.key.keysym.sym)
-			{
-			case SDLK_RETURN:
-				gamepad_1.input[GAMEPAD_TH0] &= ~BUTTON_START;
-				break;
-			case SDLK_UP:
-				gamepad_1.input[GAMEPAD_TH0] &= ~DPAD_UP;
-				gamepad_1.input[GAMEPAD_TH1] &= ~DPAD_UP;
-				break;
-			case SDLK_DOWN:
-				gamepad_1.input[GAMEPAD_TH0] &= ~DPAD_DOWN;
-				gamepad_1.input[GAMEPAD_TH1] &= ~DPAD_DOWN;
-				break;
-			case SDLK_LEFT:
-				gamepad_1.input[GAMEPAD_TH1] &= ~DPAD_LEFT;
-				break;
-			case SDLK_RIGHT:
-				gamepad_1.input[GAMEPAD_TH1] &= ~DPAD_RIGHT;
-				break;
-			case SDLK_a:
-				gamepad_1.input[GAMEPAD_TH0] &= ~BUTTON_A;
-				//printf("BUTTON_A Up | GAMEPAD_TH0: %X\n", gamepad_1.input[GAMEPAD_TH0]);
-				break;
-			case SDLK_s:
-				gamepad_1.input[GAMEPAD_TH1] &= ~BUTTON_B;
-				gamepad_1.input[GAMEPAD_EXTRA] &= ~BUTTON_B;
-				break;
-			case SDLK_d:
-				gamepad_1.input[GAMEPAD_TH1] &= ~BUTTON_C;
-				gamepad_1.input[GAMEPAD_EXTRA] &= ~BUTTON_C;
-				break;
-			case SDLK_q:
-				gamepad_1.input[GAMEPAD_EXTRA] &= ~BUTTON_X;
-				break;
-			case SDLK_w:
-				gamepad_1.input[GAMEPAD_EXTRA] &= ~BUTTON_Y;
-				break;
-			case SDLK_e:
-				gamepad_1.input[GAMEPAD_EXTRA] &= ~BUTTON_Z;
-				break;
-			case SDLK_f:
-				gamepad_1.input[GAMEPAD_EXTRA] &= ~BUTTON_MODE;
-				break;
-			}
-			break;
-		case SDL_QUIT:
-			puts("");
-			exit(0);
-		}
+		ret = handle_event(&event);
 	}
 	if (frame_limit) {
+		puts("evil frame limit");
 		//TODO: Adjust frame delay so we actually get 60 FPS rather than 62.5 FPS
 		uint32_t current = SDL_GetTicks();
 		uint32_t desired = last_frame + frame_delay;
@@ -387,9 +448,34 @@
 	return ret;
 }
 
+void render_wait_audio(psg_context * context)
+{
+	SDL_LockMutex(audio_mutex);
+		while (current_audio != NULL) {
+			SDL_CondWait(audio_cond, audio_mutex);
+		}
+		current_audio = context->audio_buffer;
+		SDL_CondSignal(audio_ready);
+
+		context->audio_buffer = context->back_buffer;
+		context->back_buffer = current_audio;
+	SDL_UnlockMutex(audio_mutex);
+	context->buffer_pos = 0;
+}
+
 void render_fps(uint32_t fps)
 {
 	frame_delay = 1000/fps;
 }
 
+uint32_t render_audio_buffer()
+{
+	return buffer_samples;
+}
 
+uint32_t render_sample_rate()
+{
+	return sample_rate;
+}
+
+