diff blastem.c @ 487:c08a4efeee7f opengl

Update opengl branch from default. Fix build breakage unrelated to merge
author Mike Pavone <pavone@retrodev.com>
date Sat, 26 Oct 2013 22:38:47 -0700
parents 7696d824489d 3e1573fa22cf
children 32f053ad9b02
line wrap: on
line diff
--- a/blastem.c	Tue Jul 23 23:01:03 2013 -0700
+++ b/blastem.c	Sat Oct 26 22:38:47 2013 -0700
@@ -1,3 +1,8 @@
+/*
+ Copyright 2013 Michael Pavone
+ This file is part of BlastEm.
+ BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
+*/
 #include "68kinst.h"
 #include "m68k_to_x86.h"
 #include "z80_to_x86.h"
@@ -5,10 +10,13 @@
 #include "vdp.h"
 #include "render.h"
 #include "blastem.h"
+#include "gst.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#define BLASTEM_VERSION "0.1.0"
+
 #define CARTRIDGE_WORDS 0x200000
 #define RAM_WORDS 32 * 1024
 #define Z80_RAM_BYTES 8 * 1024
@@ -25,6 +33,8 @@
 #define LINES_NTSC 262
 #define LINES_PAL 312
 
+#define MAX_SOUND_CYCLES 100000
+
 uint32_t mclks_per_frame = MCLKS_LINE*LINES_NTSC;
 
 uint16_t cart[CARTRIDGE_WORDS];
@@ -35,6 +45,8 @@
 int z80_enabled = 1;
 int frame_limit = 0;
 
+tern_node * config;
+
 #ifndef MIN
 #define MIN(a,b) ((a) < (b) ? (a) : (b))
 #endif
@@ -50,7 +62,7 @@
 	uint8_t block[SMD_BLOCK_SIZE];
 	filesize -= SMD_HEADER_SIZE;
 	fseek(f, SMD_HEADER_SIZE, SEEK_SET);
-
+	
 	uint16_t * dst = cart;
 	while (filesize > 0) {
 		fread(block, 1, SMD_BLOCK_SIZE, f);
@@ -137,19 +149,20 @@
 				if (next_hint < context->int_cycle) {
 					context->int_cycle = next_hint;
 					context->int_num = 4;
-
+			
 				}
 			}
 		}
 	}
 
 	context->target_cycle = context->int_cycle < context->sync_cycle ? context->int_cycle : context->sync_cycle;
-	/*printf("Cyc: %d, Trgt: %d, Int Cyc: %d, Int: %d, Mask: %X, V: %d, H: %d, HICount: %d, HReg: %d, Line: %d\n",
-		context->current_cycle, context->target_cycle, context->int_cycle, context->int_num, (context->status & 0x7),
+	/*printf("Cyc: %d, Trgt: %d, Int Cyc: %d, Int: %d, Mask: %X, V: %d, H: %d, HICount: %d, HReg: %d, Line: %d\n", 
+		context->current_cycle, context->target_cycle, context->int_cycle, context->int_num, (context->status & 0x7), 
 		v_context->regs[REG_MODE_2] & 0x20, v_context->regs[REG_MODE_1] & 0x10, v_context->hint_counter, v_context->regs[REG_HINT], v_context->cycles / MCLKS_LINE);*/
 }
 
 int break_on_sync = 0;
+int save_state = 0;
 
 uint8_t reset = 1;
 uint8_t need_reset = 0;
@@ -195,9 +208,16 @@
 void sync_sound(genesis_context * gen, uint32_t target)
 {
 	//printf("YM | Cycle: %d, bpos: %d, PSG | Cycle: %d, bpos: %d\n", gen->ym->current_cycle, gen->ym->buffer_pos, gen->psg->cycles, gen->psg->buffer_pos * 2);
+	while (target > gen->psg->cycles && target - gen->psg->cycles > MAX_SOUND_CYCLES) {
+		uint32_t cur_target = gen->psg->cycles + MAX_SOUND_CYCLES;
+		//printf("Running PSG to cycle %d\n", cur_target);
+		psg_run(gen->psg, cur_target);
+		//printf("Running YM-2612 to cycle %d\n", cur_target);
+		ym_run(gen->ym, cur_target);
+	}
 	psg_run(gen->psg, target);
 	ym_run(gen->ym, target);
-
+	
 	//printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2);
 }
 
@@ -219,7 +239,7 @@
 		}
 		//printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks);
 		vdp_run_context(v_context, mclks_per_frame);
-
+		
 		if (!headless) {
 			break_on_sync |= wait_render_frame(v_context, frame_limit);
 		}
@@ -256,10 +276,20 @@
 		context->int_ack = 0;
 	}
 	adjust_int_cycle(context, v_context);
-	if (break_on_sync && address) {
+	if (address) {
+		if (break_on_sync) {
 		break_on_sync = 0;
 		debugger(context, address);
 	}
+		if (save_state) {
+			save_state = 0;
+			while (!z_context->pc)
+			{
+				sync_z80(z_context, z_context->current_cycle * MCLKS_PER_Z80 + MCLKS_PER_Z80);
+			}
+			save_gst(gen, "savestate.gst", address);
+		}
+	}
 	return context;
 }
 
@@ -282,6 +312,7 @@
 					vdp_run_dma_done(v_context, mclks_per_frame);
 					if (v_context->cycles >= mclks_per_frame) {
 						if (!headless) {
+							//printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, v_context->cycles);
 							wait_render_frame(v_context, frame_limit);
 						}
 						vdp_adjust_cycles(v_context, mclks_per_frame);
@@ -299,7 +330,7 @@
 						}
 					}
 				}
-				context->current_cycle = v_context->cycles / MCLKS_PER_68K;
+				//context->current_cycle = v_context->cycles / MCLKS_PER_68K;
 			}
 		} else if(vdp_port < 8) {
 			blocked = vdp_control_port_write(v_context, value);
@@ -332,7 +363,6 @@
 						blocked = 0;
 					}
 				}
-				context->current_cycle = v_context->cycles / MCLKS_PER_68K;
 			} else {
 				adjust_int_cycle(context, v_context);
 			}
@@ -341,6 +371,7 @@
 			exit(1);
 		}
 		if (v_context->cycles != before_cycle) {
+			//printf("68K paused for %d (%d) cycles at cycle %d (%d) for write\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle);
 			context->current_cycle = v_context->cycles / MCLKS_PER_68K;
 		}
 	} else if (vdp_port < 0x18) {
@@ -380,7 +411,7 @@
 		sync_sound(gen, context->current_cycle * MCLKS_PER_Z80);
 		psg_write(gen->psg, value);
 	} else {
-		//TODO: Implement undocumented test register(s)
+		vdp_test_port_write(gen->vdp, value);
 	}
 	return context;
 }
@@ -395,6 +426,7 @@
 	uint16_t value;
 	sync_components(context, 0);
 	vdp_context * v_context = context->video_context;
+	uint32_t before_cycle = v_context->cycles;
 	if (vdp_port < 0x10) {
 		if (vdp_port < 4) {
 			value = vdp_data_port_read(v_context);
@@ -404,10 +436,15 @@
 			value = vdp_hv_counter_read(v_context);
 			//printf("HV Counter: %X at cycle %d\n", value, v_context->cycles);
 		}
-		context->current_cycle = v_context->cycles/MCLKS_PER_68K;
+	} else if (vdp_port < 0x18){
+		printf("Illegal read from PSG  port %X\n", vdp_port);
+		exit(1);
 	} else {
-		printf("Illegal read from PSG or test register port %X\n", vdp_port);
-		exit(1);
+		value = vdp_test_port_read(v_context);
+	}
+	if (v_context->cycles != before_cycle) {
+		//printf("68K paused for %d (%d) cycles at cycle %d (%d) for read\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle);
+		context->current_cycle = v_context->cycles / MCLKS_PER_68K;
 	}
 	return value;
 }
@@ -489,20 +526,21 @@
 			}
 		} else {
 			if (location == 0x1100) {
-				sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K);
 				if (busack_cycle <= context->current_cycle) {
 					busack = new_busack;
 					busack_cycle = CYCLE_NEVER;
 				}
 				if (value & 1) {
 					dputs("bus requesting Z80");
-
+					
 					if(!reset && !busreq) {
-						busack_cycle = ((gen->z80->current_cycle + Z80_ACK_DELAY) * MCLKS_PER_Z80) / MCLKS_PER_68K;//context->current_cycle + Z80_ACK_DELAY;
+						sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K + Z80_ACK_DELAY*MCLKS_PER_Z80);
+						busack_cycle = (gen->z80->current_cycle * MCLKS_PER_Z80) / MCLKS_PER_68K;//context->current_cycle + Z80_ACK_DELAY;
 						new_busack = Z80_REQ_ACK;
 					}
 					busreq = 1;
 				} else {
+					sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K);
 					if (busreq) {
 						dputs("releasing z80 bus");
 						#ifdef DO_DEBUG_PRINT
@@ -518,7 +556,7 @@
 					}
 					//busack_cycle = CYCLE_NEVER;
 					//busack = Z80_REQ_BUSY;
-
+					
 				}
 			} else if (location == 0x1200) {
 				sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K);
@@ -1433,7 +1471,7 @@
 				//Z80 debug commands
 				switch(input_buf[1])
 				{
-				case 'b':
+				case 'b': 
 					param = find_param(input_buf);
 					if (!param) {
 						fputs("zb command requires a parameter\n", stderr);
@@ -1463,188 +1501,15 @@
 	return context;
 }
 
-#define GST_68K_REGS 0x80
-#define GST_68K_REG_SIZE (0xDA-GST_68K_REGS)
-#define GST_68K_PC_OFFSET (0xC8-GST_68K_REGS)
-#define GST_68K_SR_OFFSET (0xD0-GST_68K_REGS)
-#define GST_68K_USP_OFFSET (0xD2-GST_68K_REGS)
-#define GST_68K_SSP_OFFSET (0xD6-GST_68K_REGS)
-#define GST_68K_RAM  0x2478
-#define GST_Z80_REGS 0x404
-#define GST_Z80_REG_SIZE (0x440-GST_Z80_REGS)
-#define GST_Z80_RAM 0x474
-
-uint32_t read_le_32(uint8_t * data)
-{
-	return data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0];
-}
-
-uint16_t read_le_16(uint8_t * data)
-{
-	return data[1] << 8 | data[0];
-}
-
-uint16_t read_be_16(uint8_t * data)
-{
-	return data[0] << 8 | data[1];
-}
-
-uint32_t m68k_load_gst(m68k_context * context, FILE * gstfile)
-{
-	uint8_t buffer[4096];
-	fseek(gstfile, GST_68K_REGS, SEEK_SET);
-	if (fread(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) {
-		fputs("Failed to read 68K registers from savestate\n", stderr);
-		return 0;
-	}
-	uint8_t * curpos = buffer;
-	for (int i = 0; i < 8; i++) {
-		context->dregs[i] = read_le_32(curpos);
-		curpos += sizeof(uint32_t);
-	}
-	for (int i = 0; i < 8; i++) {
-		context->aregs[i] = read_le_32(curpos);
-		curpos += sizeof(uint32_t);
-	}
-	uint32_t pc = read_le_32(buffer + GST_68K_PC_OFFSET);
-	uint16_t sr = read_le_16(buffer + GST_68K_SR_OFFSET);
-	context->status = sr >> 8;
-	for (int flag = 4; flag >= 0; flag--) {
-		context->flags[flag] = sr & 1;
-		sr >>= 1;
-	}
-	if (context->status & (1 << 5)) {
-		context->aregs[8] = read_le_32(buffer + GST_68K_USP_OFFSET);
-	} else {
-		context->aregs[8] = read_le_32(buffer + GST_68K_SSP_OFFSET);
-	}
-	fseek(gstfile, GST_68K_RAM, SEEK_SET);
-	for (int i = 0; i < (32*1024);) {
-		if (fread(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) {
-			fputs("Failed to read 68K RAM from savestate\n", stderr);
-			return 0;
-		}
-		for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) {
-			context->mem_pointers[1][i++] = read_be_16(curpos);
-		}
-	}
-	return pc;
-}
-
-uint8_t z80_load_gst(z80_context * context, FILE * gstfile)
+void set_speed_percent(genesis_context * context, uint32_t percent)
 {
-	uint8_t regdata[GST_Z80_REG_SIZE];
-	fseek(gstfile, GST_Z80_REGS, SEEK_SET);
-	if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) {
-		fputs("Failed to read Z80 registers from savestate\n", stderr);
-		return 0;
-	}
-	uint8_t * curpos = regdata;
-	uint8_t f = *(curpos++);
-	context->flags[ZF_C] = f & 1;
-	f >>= 1;
-	context->flags[ZF_N] = f & 1;
-	f >>= 1;
-	context->flags[ZF_PV] = f & 1;
-	f >>= 2;
-	context->flags[ZF_H] = f & 1;
-	f >>= 2;
-	context->flags[ZF_Z] = f & 1;
-	f >>= 1;
-	context->flags[ZF_S] = f;
-
-	context->regs[Z80_A] = *curpos;
-	curpos += 3;
-	for (int reg = Z80_C; reg <= Z80_IYH; reg++) {
-		context->regs[reg++] = *(curpos++);
-		context->regs[reg] = *curpos;
-		curpos += 3;
-	}
-	uint16_t pc = read_le_16(curpos);
-	curpos += 4;
-	context->sp = read_le_16(curpos);
-	curpos += 4;
-	f = *(curpos++);
-	context->alt_flags[ZF_C] = f & 1;
-	f >>= 1;
-	context->alt_flags[ZF_N] = f & 1;
-	f >>= 1;
-	context->alt_flags[ZF_PV] = f & 1;
-	f >>= 2;
-	context->alt_flags[ZF_H] = f & 1;
-	f >>= 2;
-	context->alt_flags[ZF_Z] = f & 1;
-	f >>= 1;
-	context->alt_flags[ZF_S] = f;
-	context->alt_regs[Z80_A] = *curpos;
-	curpos += 3;
-	for (int reg = Z80_C; reg <= Z80_H; reg++) {
-		context->alt_regs[reg++] = *(curpos++);
-		context->alt_regs[reg] = *curpos;
-		curpos += 3;
-	}
-	context->regs[Z80_I] = *curpos;
-	curpos += 2;
-	context->iff1 = context->iff2 = *curpos;
-	curpos += 2;
-	reset = !*(curpos++);
-	busreq = *curpos;
-	curpos += 3;
-	uint32_t bank = read_le_32(curpos);
-	if (bank < 0x400000) {
-		context->mem_pointers[1] = context->mem_pointers[2] + bank;
-	} else {
-		context->mem_pointers[1] = NULL;
-	}
-	context->bank_reg = bank >> 15;
-	fseek(gstfile, GST_Z80_RAM, SEEK_SET);
-	if(fread(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) {
-		fputs("Failed to read Z80 RAM from savestate\n", stderr);
-		return 0;
-	}
-	context->native_pc =  z80_get_native_address_trans(context, pc);
-	return 1;
+	uint32_t old_clock = context->master_clock;
+	context->master_clock = ((uint64_t)context->normal_clock * (uint64_t)percent) / 100;
+	while (context->ym->current_cycle != context->psg->cycles) {
+		sync_sound(context, context->psg->cycles + MCLKS_PER_PSG);
 }
-
-uint32_t load_gst(genesis_context * gen, char * fname)
-{
-	FILE * gstfile = fopen(fname, "rb");
-	if (!gstfile) {
-		fprintf(stderr, "Could not open file %s for reading\n", fname);
-		goto error;
-	}
-	char ident[5];
-	if (fread(ident, 1, sizeof(ident), gstfile) != sizeof(ident)) {
-		fprintf(stderr, "Could not read ident code from %s\n", fname);
-		goto error_close;
-	}
-	if (memcmp(ident, "GST\xE0\x40", 3) != 0) {
-		fprintf(stderr, "%s doesn't appear to be a GST savestate. The ident code is %c%c%c\\x%X\\x%X instead of GST\\xE0\\x40.\n", fname, ident[0], ident[1], ident[2], ident[3], ident[4]);
-		goto error_close;
-	}
-	uint32_t pc = m68k_load_gst(gen->m68k, gstfile);
-	if (!pc) {
-		goto error_close;
-	}
-	if (!vdp_load_gst(gen->vdp, gstfile)) {
-		goto error_close;
-	}
-	if (!ym_load_gst(gen->ym, gstfile)) {
-		goto error_close;
-	}
-	if (!z80_load_gst(gen->z80, gstfile)) {
-		goto error_close;
-	}
-	gen->ports[0].control = 0x40;
-	gen->ports[1].control = 0x40;
-	adjust_int_cycle(gen->m68k, gen->vdp);
-	fclose(gstfile);
-	return pc;
-
-error_close:
-	fclose(gstfile);
-error:
-	return 0;
+	ym_adjust_master_clock(context->ym, context->master_clock);
+	psg_adjust_master_clock(context->psg, context->master_clock);
 }
 
 #define ROM_END   0x1A4
@@ -1658,7 +1523,7 @@
 const memmap_chunk static_map[] = {
 		{0,        0x400000,  0xFFFFFF, 0, MMAP_READ,                          cart,
 		           NULL,          NULL,         NULL,            NULL},
-		{0xE00000, 0x1000000, 0xFFFF,   0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram,
+		{0xE00000, 0x1000000, 0xFFFF,   0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram, 
 		           NULL,          NULL,         NULL,            NULL},
 		{0xC00000, 0xE00000,  0x1FFFFF, 0, 0,                                  NULL,
 		           (read_16_fun)vdp_port_read,  (write_16_fun)vdp_port_write,
@@ -1711,7 +1576,7 @@
 			memmap[0].mask = 0xFFFFFF;
 			memmap[0].flags = MMAP_READ;
 			memmap[0].buffer = cart;
-
+			
 			ram_start &= 0xFFFFFE;
 			ram_end |= 1;
 			memmap[1].start = ram_start;
@@ -1728,7 +1593,7 @@
 				size /= 2;
 			}
 			memmap[1].buffer = gen->save_ram = malloc(size);
-
+			
 			memcpy(memmap+2, static_map+1, sizeof(static_map)-sizeof(static_map[0]));
 			num_chunks = sizeof(static_map)/sizeof(memmap_chunk)+1;
 		} else {
@@ -1737,7 +1602,7 @@
 			memmap[0].mask = 0xFFFFFF;
 			memmap[0].flags = MMAP_READ;
 			memmap[0].buffer = cart;
-
+			
 			memmap[1].start = 0x200000;
 			memmap[1].end = 0x400000;
 			memmap[1].mask = 0x1FFFFF;
@@ -1757,7 +1622,7 @@
 			memmap[num_chunks].end = 0xA13100;
 			memmap[num_chunks].mask = 0xFF;
 			memmap[num_chunks].write_16 = (write_16_fun)write_bank_reg_w;
-			memmap[num_chunks].write_8 = (write_8_fun)write_bank_reg_b;
+			memmap[num_chunks].write_8 = (write_8_fun)write_bank_reg_b; 
 			num_chunks++;
 			ram_end++;
 			size = ram_end-ram_start;
@@ -1786,7 +1651,7 @@
 	init_x86_68k_opts(&opts, memmap, num_chunks);
 	opts.address_log = address_log;
 	init_68k_context(&context, opts.native_code_map, &opts);
-
+	
 	context.video_context = gen->vdp;
 	context.system = gen;
 	//cartridge ROM
@@ -1803,11 +1668,15 @@
 	if (statefile) {
 		uint32_t pc = load_gst(gen, statefile);
 		if (!pc) {
+			fprintf(stderr, "Failed to load save state %s\n", statefile);
 			exit(1);
 		}
+		printf("Loaded %s\n", statefile);
 		if (debug) {
 			insert_breakpoint(&context, pc, (uint8_t *)debugger);
 		}
+		adjust_int_cycle(gen->m68k, gen->vdp);
+		gen->z80->native_pc =  z80_get_native_address_trans(gen->z80, gen->z80->pc);
 		start_68k_context(&context, pc);
 	} else {
 		if (debug) {
@@ -1865,40 +1734,61 @@
 		version_reg = NO_DISK | JAP;
 	} else if (detect_specific_region('E') || detect_specific_region('A')) {
 		version_reg = NO_DISK | EUR;
+	} else {
+		char * def_region = tern_find_ptr(config, "default_region");
+		if (def_region) {
+			switch(*def_region)
+			{
+			case 'j':
+			case 'J':
+				version_reg = NO_DISK | JAP;
+				break;
+			case 'u':
+			case 'U':
+				version_reg = NO_DISK | USA;
+				break;
+			case 'e':
+			case 'E':
+				version_reg = NO_DISK | EUR;
+				break;
+			}
+		}
 	}
 }
 
 int main(int argc, char ** argv)
 {
 	if (argc < 2) {
-		fputs("Usage: blastem FILENAME\n", stderr);
+		fputs("Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n", stderr);
 		return 1;
 	}
-	if(!load_rom(argv[1])) {
-		fprintf(stderr, "Failed to open %s for reading\n", argv[1]);
-		return 1;
-	}
+	config = load_config(argv[0]);
 	detect_region();
 	int width = -1;
 	int height = -1;
 	int debug = 0;
 	int ym_log = 0;
+	int loaded = 0;
+	uint8_t force_version = 0;
+	char * romfname = NULL;
 	FILE *address_log = NULL;
 	char * statefile = NULL;
-	for (int i = 2; i < argc; i++) {
+	uint8_t fullscreen = 0;
+	for (int i = 1; i < argc; i++) {
 		if (argv[i][0] == '-') {
 			switch(argv[i][1]) {
 			case 'd':
 				debug = 1;
 				break;
 			case 'f':
-				frame_limit = 1;
+				fullscreen = 1;
 				break;
 			case 'l':
 				address_log = fopen("address.log", "w");
 				break;
 			case 'v':
-				headless = 1;
+				printf("blastem %s\n", BLASTEM_VERSION);
+				return 0;
 				break;
 			case 'n':
 				z80_enabled = 0;
@@ -1913,15 +1803,15 @@
 				{
 				case 'j':
 				case 'J':
-					version_reg = NO_DISK | JAP;
+					force_version = NO_DISK | JAP;
 					break;
 				case 'u':
 				case 'U':
-					version_reg = NO_DISK | USA;
+					force_version = NO_DISK | USA;
 					break;
 				case 'e':
 				case 'E':
-					version_reg = NO_DISK | EUR;
+					force_version = NO_DISK | EUR;
 					break;
 				default:
 					fprintf(stderr, "'%c' is not a valid region character for the -r option\n", argv[i][0]);
@@ -1939,18 +1829,55 @@
 			case 'y':
 				ym_log = 1;
 				break;
+			case 'h':
+				puts(
+					"Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n"
+					"Options:\n"
+					"	-h          Print this help text\n"
+					"	-r (J|U|E)  Force region to Japan, US or Europe respectively\n"
+					"	-f          Start in fullscreen mode\n"
+					"	-s FILE     Load a GST format savestate from FILE\n"
+					"	-d          Enter debugger on startup\n"
+					"	-n          Disable Z80\n"
+					"   -v          Display version number and exit\n"
+					"	-l          Log 68K code addresses (useful for assemblers)\n"
+					"	-y          Log individual YM-2612 channels to WAVE files\n"
+				);
+				return 0;
 			default:
 				fprintf(stderr, "Unrecognized switch %s\n", argv[i]);
 				return 1;
 			}
+		} else if (!loaded) {
+			if(!load_rom(argv[i])) {
+				fprintf(stderr, "Failed to open %s for reading\n", argv[i]);
+				return 1;
+			}
+			romfname = argv[i];
+			loaded = 1;
 		} else if (width < 0) {
 			width = atoi(argv[i]);
 		} else if (height < 0) {
 			height = atoi(argv[i]);
 		}
 	}
+	if (!loaded) {
+		fputs("You must specify a ROM filename!\n", stderr);
+		return 1;
+	}
+	if (force_version) {
+		version_reg = force_version;
+	}
 	update_title();
-	width = width < 320 ? 640 : width;
+	int def_width = 0;
+	char *config_width = tern_find_ptr(config, "videowidth");
+	if (config_width) {
+		def_width = atoi(config_width);
+	}
+	if (!def_width) {
+		def_width = 640;
+	}
+	width = width < 320 ? def_width : width;
 	height = height < 240 ? (width/320) * 240 : height;
 	uint32_t fps = 60;
 	if (version_reg & 0x40) {
@@ -1958,41 +1885,41 @@
 		fps = 50;
 	}
 	if (!headless) {
-		render_init(width, height, title, fps, 0);
+		render_init(width, height, title, fps, fullscreen, 0);
 	}
 	vdp_context v_context;
-
+	genesis_context gen;
+	memset(&gen, 0, sizeof(gen));
+	gen.master_clock = gen.normal_clock = fps == 60 ? MCLKS_NTSC : MCLKS_PAL;
+	
 	init_vdp_context(&v_context);
-
+	
 	ym2612_context y_context;
-	ym_init(&y_context, render_sample_rate(), fps == 60 ? MCLKS_NTSC : MCLKS_PAL, MCLKS_PER_YM, render_audio_buffer(), ym_log ? YM_OPT_WAVE_LOG : 0);
-
+	ym_init(&y_context, render_sample_rate(), gen.master_clock, MCLKS_PER_YM, render_audio_buffer(), ym_log ? YM_OPT_WAVE_LOG : 0);
+	
 	psg_context p_context;
-	psg_init(&p_context, render_sample_rate(), fps == 60 ? MCLKS_NTSC : MCLKS_PAL, MCLKS_PER_PSG, render_audio_buffer());
-
+	psg_init(&p_context, render_sample_rate(), gen.master_clock, MCLKS_PER_PSG, render_audio_buffer());
+	
 	z80_context z_context;
 	x86_z80_options z_opts;
 	init_x86_z80_opts(&z_opts);
 	init_z80_context(&z_context, &z_opts);
 
-	genesis_context gen;
-	memset(&gen, 0, sizeof(gen));
-
 	z_context.system = &gen;
 	z_context.mem_pointers[0] = z80_ram;
 	z_context.sync_cycle = z_context.target_cycle = mclks_per_frame/MCLKS_PER_Z80;
 	z_context.int_cycle = CYCLE_NEVER;
 	z_context.mem_pointers[1] = z_context.mem_pointers[2] = (uint8_t *)cart;
-
+	
 	gen.z80 = &z_context;
 	gen.vdp = &v_context;
 	gen.ym = &y_context;
 	gen.psg = &p_context;
 	genesis = &gen;
-
-	int fname_size = strlen(argv[1]);
+	
+	int fname_size = strlen(romfname);
 	sram_filename = malloc(fname_size+6);
-	memcpy(sram_filename, argv[1], fname_size);
+	memcpy(sram_filename, romfname, fname_size);
 	int i;
 	for (i = fname_size-1; fname_size >= 0; --i) {
 		if (sram_filename[i] == '.') {
@@ -2004,7 +1931,7 @@
 		strcpy(sram_filename + fname_size, ".sram");
 	}
 	set_keybindings();
-
+	
 	init_run_cpu(&gen, debug, address_log, statefile);
 	return 0;
 }