changeset 288:a8ee7934a1f8

Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
author Mike Pavone <pavone@retrodev.com>
date Sun, 05 May 2013 22:56:42 -0700
parents fb840e0a48cd
children 1cc0850ab6bc
files Makefile blastem.c blastem.h m68k_to_x86.h ym2612.c ym2612.h
diffstat 6 files changed, 198 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sun May 05 11:17:37 2013 -0700
+++ b/Makefile	Sun May 05 22:56: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 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 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 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)`
 
 dis : dis.o 68kinst.o
 	$(CC) -o dis dis.o 68kinst.o
--- a/blastem.c	Sun May 05 11:17:37 2013 -0700
+++ b/blastem.c	Sun May 05 22:56:42 2013 -0700
@@ -165,11 +165,14 @@
 m68k_context * sync_components(m68k_context * context, uint32_t address)
 {
 	//TODO: Handle sync targets smaller than a single frame
-	vdp_context * v_context = context->video_context;
-	z80_context * z_context = context->next_cpu;
+	genesis_context * gen = context->system;
+	vdp_context * v_context = gen->vdp;
+	z80_context * z_context = gen->z80;
 	uint32_t mclks = context->current_cycle * MCLKS_PER_68K;
 	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;
 		//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) {
@@ -354,6 +357,7 @@
 
 m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value)
 {
+	genesis_context * gen = context->system;
 	if (location < 0x10000) {
 		if (busack_cycle > context->current_cycle) {
 			busack = new_busack;
@@ -363,7 +367,16 @@
 			location &= 0x7FFF;
 			if (location < 0x4000) {
 				z80_ram[location & 0x1FFF] = value;
-				z80_handle_code_write(location & 0x1FFF, context->next_cpu);
+				z80_handle_code_write(location & 0x1FFF, gen->z80);
+			} else if (location < 0x6000) {
+				ym_run(gen->ym, context->current_cycle);
+				if (location & 1) {
+					ym_data_write(gen->ym, value);
+				} else if(location & 2) {
+					ym_address_write_part2(gen->ym, value);
+				} else {
+					ym_address_write_part1(gen->ym, value);
+				}
 			}
 		}
 	} else {
@@ -388,7 +401,7 @@
 			}
 		} else {
 			if (location == 0x1100) {
-				sync_z80(context->next_cpu, context->current_cycle * MCLKS_PER_68K);
+				sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K);
 				if (busack_cycle > context->current_cycle) {
 					busack = new_busack;
 					busack_cycle = CYCLE_NEVER;
@@ -410,16 +423,15 @@
 						fwrite(z80_ram, 1, sizeof(z80_ram), f);
 						fclose(f);
 						#endif
-						z80_context * z_context = context->next_cpu;
 						//TODO: Add necessary delay between release of busreq and resumption of execution
-						z_context->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
+						gen->z80->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
 					}
 					busreq = 0;
 					busack_cycle = CYCLE_NEVER;
 					busack = 1;
 				}
 			} else if (location == 0x1200) {
-				sync_z80(context->next_cpu, context->current_cycle * MCLKS_PER_68K);
+				sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K);
 				if (value & 1) {
 					if (reset && busreq) {
 						new_busack = 0;
@@ -427,10 +439,9 @@
 					}
 					//TODO: Deal with the scenario in which reset is not asserted long enough
 					if (reset) {
-						z80_context * z_context = context->next_cpu;
 						need_reset = 1;
 						//TODO: Add necessary delay between release of reset and start of execution
-						z_context->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
+						gen->z80->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
 					}
 					reset = 0;
 				} else {
@@ -444,6 +455,7 @@
 
 m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t value)
 {
+	genesis_context * gen = context->system;
 	if (location < 0x10000) {
 		if (busack_cycle > context->current_cycle) {
 			busack = new_busack;
@@ -453,7 +465,16 @@
 			location &= 0x7FFF;
 			if (location < 0x4000) {
 				z80_ram[location & 0x1FFE] = value >> 8;
-				z80_handle_code_write(location & 0x1FFE, context->next_cpu);
+				z80_handle_code_write(location & 0x1FFE, gen->z80);
+			} else if (location < 0x6000) {
+				ym_run(gen->ym, context->current_cycle);
+				if (location & 1) {
+					ym_data_write(gen->ym, value >> 8);
+				} else if(location & 2) {
+					ym_address_write_part2(gen->ym, value >> 8);
+				} else {
+					ym_address_write_part1(gen->ym, value >> 8);
+				}
 			}
 		}
 	} else {
@@ -479,7 +500,7 @@
 		} else {
 			//printf("IO Write of %X to %X @ %d\n", value, location, context->current_cycle);
 			if (location == 0x1100) {
-				sync_z80(context->next_cpu, context->current_cycle * MCLKS_PER_68K);
+				sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K);
 				if (busack_cycle > context->current_cycle) {
 					busack = new_busack;
 					busack_cycle = CYCLE_NEVER;
@@ -501,16 +522,15 @@
 						fwrite(z80_ram, 1, sizeof(z80_ram), f);
 						fclose(f);
 						#endif
-						z80_context * z_context = context->next_cpu;
 						//TODO: Add necessary delay between release of busreq and resumption of execution
-						z_context->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
+						gen->z80->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
 					}
 					busreq = 0;
 					busack_cycle = CYCLE_NEVER;
 					busack = 1;
 				}
 			} else if (location == 0x1200) {
-				sync_z80(context->next_cpu, context->current_cycle * MCLKS_PER_68K);
+				sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K);
 				if (value & 0x100) {
 					if (reset && busreq) {
 						new_busack = 0;
@@ -518,10 +538,9 @@
 					}
 					//TODO: Deal with the scenario in which reset is not asserted long enough
 					if (reset) {
-						z80_context * z_context = context->next_cpu;
 						need_reset = 1;
 						//TODO: Add necessary delay between release of reset and start of execution
-						z_context->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
+						gen->z80->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
 					}
 					reset = 0;
 				} else {
@@ -541,6 +560,7 @@
 
 m68k_context * io_read(uint32_t location, m68k_context * context)
 {
+	genesis_context *gen = context->system;
 	if (location < 0x10000) {
 		if (busack_cycle > context->current_cycle) {
 			busack = new_busack;
@@ -550,6 +570,9 @@
 			location &= 0x7FFF;
 			if (location < 0x4000) {
 				context->value = z80_ram[location & 0x1FFF];
+			} else if (location < 0x6000) {
+				ym_run(gen->ym, context->current_cycle);
+				context->value = ym_read_status(gen->ym);
 			} else {
 				context->value = 0xFF;
 			}
@@ -601,6 +624,7 @@
 
 m68k_context * io_read_w(uint32_t location, m68k_context * context)
 {
+	genesis_context * gen = context->system;
 	if (location < 0x10000) {
 		if (busack_cycle > context->current_cycle) {
 			busack = new_busack;
@@ -608,12 +632,16 @@
 		}
 		if (!(busack || reset)) {
 			location &= 0x7FFF;
+			uint16_t value;
 			if (location < 0x4000) {
-				context->value = z80_ram[location & 0x1FFE];
-				context->value |= context->value << 8;
+				value = z80_ram[location & 0x1FFE];
+			} else if (location < 0x6000) {
+				ym_run(gen->ym, context->current_cycle);
+				value = ym_read_status(gen->ym);	
 			} else {
-				context->value = 0xFFFF;
+				value = 0xFF;
 			}
+			context->value = value | (value << 8);
 		} else {
 			context->value = 0xFFFF;
 		}
@@ -894,16 +922,17 @@
 	return context;
 }
 
-void init_run_cpu(vdp_context * vcontext, z80_context * zcontext, int debug, FILE * address_log)
+void init_run_cpu(genesis_context * gen, int debug, FILE * address_log)
 {
 	m68k_context context;
 	x86_68k_options opts;
+	gen->m68k = &context;
 	init_x86_68k_opts(&opts);
 	opts.address_log = address_log;
 	init_68k_context(&context, opts.native_code_map, &opts);
 	
-	context.video_context = vcontext;
-	context.next_cpu = zcontext;
+	context.video_context = gen->vdp;
+	context.system = gen;
 	//cartridge ROM
 	context.mem_pointers[0] = cart;
 	context.target_cycle = context.sync_cycle = MCLKS_PER_FRAME/MCLKS_PER_68K;
@@ -972,6 +1001,9 @@
 	
 	init_vdp_context(&v_context);
 	
+	ym2612_context y_context;
+	ym_init(&y_context);
+	
 	z80_context z_context;
 	x86_z80_options z_opts;
 	init_x86_z80_opts(&z_opts);
@@ -982,6 +1014,11 @@
 	z_context.int_cycle = CYCLE_NEVER;
 	z_context.mem_pointers[1] = z_context.mem_pointers[2] = (uint8_t *)cart;
 	
-	init_run_cpu(&v_context, &z_context, debug, address_log);
+	genesis_context gen;
+	gen.z80 = &z_context;
+	gen.vdp = &v_context;
+	gen.ym = &y_context;
+	
+	init_run_cpu(&gen, debug, address_log);
 	return 0;
 }
--- a/blastem.h	Sun May 05 11:17:37 2013 -0700
+++ b/blastem.h	Sun May 05 22:56:42 2013 -0700
@@ -3,6 +3,9 @@
 
 #include <stdint.h>
 #include "m68k_to_x86.h"
+#include "z80_to_x86.h"
+#include "ym2612.h"
+#include "vdp.h"
 
 typedef struct {
 	uint32_t th_counter;
@@ -12,6 +15,13 @@
 	uint8_t input[3];
 } io_port;
 
+typedef struct {
+	m68k_context   *m68k;
+	z80_context    *z80;
+	vdp_context    *vdp;
+	ym2612_context *ym;
+} genesis_context;
+
 #define GAMEPAD_TH0 0
 #define GAMEPAD_TH1 1
 #define GAMEPAD_EXTRA 2
--- a/m68k_to_x86.h	Sun May 05 11:17:37 2013 -0700
+++ b/m68k_to_x86.h	Sun May 05 22:56:42 2013 -0700
@@ -43,7 +43,7 @@
 	native_map_slot *native_code_map;
 	void            *options;
 	uint8_t         ram_code_flags[32/8];
-	void            *next_cpu;
+	void            *system;
 } m68k_context;
 
 uint8_t * translate_m68k(uint8_t * dst, struct m68kinst * inst, x86_68k_options * opts);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ym2612.c	Sun May 05 22:56:42 2013 -0700
@@ -0,0 +1,96 @@
+#include <string.h>
+#include "ym2612.h"
+
+#define BUSY_CYCLES 17
+#define TIMERA_UPDATE_PERIOD 144
+
+#define REG_TIMERA_HIGH 0x3 // 0x24
+#define REG_TIMERA_LOW 0x4  // 0x25
+#define REG_TIMERB 0x5      // 0x26
+#define REG_TIME_CTRL 0x6   // 0x27
+
+#define BIT_TIMERA_ENABLE 0x1
+#define BIT_TIMERB_ENABLE 0x2
+#define BIT_TIMERA_OVEREN 0x4
+#define BIT_TIMERB_OVEREN 0x8
+#define BIT_TIMERA_RESET  0x10
+#define BIT_TIMERB_RESET  0x20
+
+#define BIT_STATUS_TIMERA 0x1
+#define BIT_STATUS_TIMERB 0x2
+
+void ym_init(ym2612_context * context)
+{
+	memset(context, 0, sizeof(*context));
+}
+
+void ym_run(ym2612_context * context, uint32_t to_cycle)
+{
+	uint32_t delta = to_cycle - context->current_cycle;
+	//Timers won't be perfect with this, but it's good enough for now
+	//once actual FM emulation is in place the timers should just be 
+	//decremented/reloaded on the appropriate ticks
+	uint32_t timer_delta = to_cycle / TIMERA_UPDATE_PERIOD;
+	if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_ENABLE) {
+		if (timer_delta > context->timer_a) {
+			if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_OVEREN) {
+				context->status |= BIT_STATUS_TIMERA;
+			}
+			uint32_t rem_delta = timer_delta - (context->timer_a+1);
+			uint16_t timer_val = (context->part1_regs[REG_TIMERA_HIGH] << 2) | (context->part1_regs[REG_TIMERA_LOW] & 0x3);
+			context->timer_a = timer_val - (rem_delta % (timer_val + 1));
+		} else {
+			context->timer_a -= timer_delta;
+		}
+	}
+	timer_delta /= 16; //Timer B runs at 1/16th the speed of Timer A
+	if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_ENABLE) {
+		if (timer_delta > context->timer_b) {
+			if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_OVEREN) {
+				context->status |= BIT_STATUS_TIMERB;
+			}
+			uint32_t rem_delta = timer_delta - (context->timer_b+1);
+			uint8_t timer_val = context->part1_regs[REG_TIMERB];
+			context->timer_b = timer_val - (rem_delta % (timer_val + 1));
+		} else {
+			context->timer_a -= timer_delta;
+		}
+	}
+	context->current_cycle = to_cycle;
+	if (to_cycle >= context->write_cycle + BUSY_CYCLES) {
+		context->status &= 0x7F;
+	}
+}
+
+void ym_address_write_part1(ym2612_context * context, uint8_t address)
+{
+	if (address >= 0x21 && address < 0xB7) {
+		context->selected_reg = context->part1_regs + address - 0x21;
+	} else {
+		context->selected_reg = NULL;
+	}
+}
+
+void ym_address_write_part2(ym2612_context * context, uint8_t address)
+{
+	if (address >= 0x30 && address < 0xB7) {
+		context->selected_reg = context->part1_regs + address - 0x30;
+	} else {
+		context->selected_reg = NULL;
+	}
+}
+
+void ym_data_write(ym2612_context * context, uint8_t value)
+{
+	if (context->selected_reg && !(context->status & 0x80)) {
+		*context->selected_reg = value;
+		context->write_cycle = context->current_cycle;
+		context->selected_reg = NULL;//TODO: Verify this
+	}
+}
+
+uint8_t ym_read_status(ym2612_context * context)
+{
+	return context->status;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ym2612.h	Sun May 05 22:56:42 2013 -0700
@@ -0,0 +1,29 @@
+#ifndef YM2612_H_
+#define YM2612_H_
+
+#include <stdint.h>
+
+#define NUM_SHARED_REGS (0x30-0x21)
+#define NUM_PART_REGS (0xB7-0x30)
+
+typedef struct {
+	uint32_t current_cycle;
+	uint32_t write_cycle;
+	uint8_t  *selected_reg;
+	uint16_t timer_a;
+	uint8_t  timer_b;
+	uint8_t  reg_num;
+	uint8_t  status;
+	uint8_t  part1_regs[NUM_SHARED_REGS+NUM_PART_REGS];
+	uint8_t  part2_regs[NUM_PART_REGS];
+} ym2612_context;
+
+void ym_init(ym2612_context * context);
+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);
+void ym_data_write(ym2612_context * context, uint8_t value);
+uint8_t ym_read_status(ym2612_context * context);
+
+#endif //YM2612_H_
+