changeset 2459:cb62730d5c99

Initial work on Copera emulation
author Michael Pavone <pavone@retrodev.com>
date Wed, 21 Feb 2024 23:42:19 -0800
parents 09c9d2c6bac0
children a4f8fa24764b
files blastem.c debug.c genesis.c genesis.h system.c system.h
diffstat 6 files changed, 124 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/blastem.c	Wed Feb 21 20:34:52 2024 -0800
+++ b/blastem.c	Wed Feb 21 23:42:19 2024 -0800
@@ -615,6 +615,8 @@
 					stype = force_stype = SYSTEM_GENESIS;
 				} else if (!strcmp("pico", argv[i])) {
 					stype = force_stype = SYSTEM_PICO;
+				} else if (!strcmp("copera", argv[i])) {
+					stype = force_stype = SYSTEM_COPERA;
 				} else if (!strcmp("jag", argv[i])) {
 					stype = force_stype = SYSTEM_JAGUAR;
 				} else if (!strcmp("media", argv[i])) {
--- a/debug.c	Wed Feb 21 20:34:52 2024 -0800
+++ b/debug.c	Wed Feb 21 23:42:19 2024 -0800
@@ -4830,15 +4830,24 @@
 		{
 		case SYSTEM_GENESIS:
 		case SYSTEM_SEGACD:
+		case SYSTEM_PICO:
+		case SYSTEM_COPERA:
 			//check if this is the main CPU
 			if (context->system == current_system) {
 				genesis_context *gen = context->system;
-				root->other_roots = tern_insert_ptr(root->other_roots, "z80", find_z80_root(gen->z80));
+				if (current_system->type == SYSTEM_GENESIS || current_system->type == SYSTEM_SEGACD) {
+					root->other_roots = tern_insert_ptr(root->other_roots, "z80", find_z80_root(gen->z80));
+					root->other_roots = tern_insert_ptr(root->other_roots, "io", find_io_root(&gen->io));
+				}
 				root->other_roots = tern_insert_ptr(root->other_roots, "vdp", find_vdp_root(gen->vdp));
-				root->other_roots = tern_insert_ptr(root->other_roots, "ym", find_ym2612_root(gen->ym));
 				root->other_roots = tern_insert_ptr(root->other_roots, "psg", find_psg_root(gen->psg));
-				root->other_roots = tern_insert_ptr(root->other_roots, "io", find_io_root(&gen->io));
-				add_commands(root, genesis_commands, NUM_GENESIS);
+				uint32_t num_commands = NUM_GENESIS;
+				if (current_system->type == SYSTEM_PICO || current_system->type == SYSTEM_COPERA) {
+					num_commands -= 3;
+				} else {
+					root->other_roots = tern_insert_ptr(root->other_roots, "ym", find_ym2612_root(gen->ym));
+				}
+				add_commands(root, genesis_commands, num_commands);
 				var = calloc(1, sizeof(debug_var));
 				var->get = debug_frame_get;
 				var->ptr = gen->vdp;
--- a/genesis.c	Wed Feb 21 20:34:52 2024 -0800
+++ b/genesis.c	Wed Feb 21 23:42:19 2024 -0800
@@ -70,7 +70,7 @@
 	vdp_serialize(gen->vdp, buf);
 	end_section(buf);
 
-	if (gen->header.type != SYSTEM_PICO) {
+	if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) {
 		start_section(buf, SECTION_YM2612);
 		ym_serialize(gen->ym, buf);
 		end_section(buf);
@@ -235,7 +235,7 @@
 		register_section_handler(buf, (section_handler){.fun = zram_deserialize, .data = gen}, SECTION_SOUND_RAM);
 		register_section_handler(buf, (section_handler){.fun = tmss_deserialize, .data = gen}, SECTION_TMSS);
 	}
-	if (gen->header.type != SYSTEM_PICO) {
+	if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) {
 		register_section_handler(buf, (section_handler){.fun = ym_deserialize, .data = gen->ym}, SECTION_YM2612);
 	}
 	if (gen->expansion) {
@@ -906,7 +906,7 @@
 static m68k_context *int_ack(m68k_context *context)
 {
 	genesis_context * gen = context->system;
-	if (gen->header.type != SYSTEM_PICO || context->int_num > 4 || context->int_num < 3) {
+	if ((gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) || context->int_num > 4 || context->int_num < 3) {
 		vdp_context * v_context = gen->vdp;
 		//printf("acknowledging %d @ %d:%d, vcounter: %d, hslot: %d\n", context->int_ack, context->current_cycle, v_context->cycles, v_context->vcounter, v_context->hslot);
 		vdp_run_context(v_context, context->current_cycle);
@@ -940,7 +940,7 @@
 	//do refresh check here so we can avoid adding a penalty for a refresh that happens during a VDP access
 	gen_update_refresh_free_access(context);
 
-	if (gen->header.type == SYSTEM_PICO) {
+	if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) {
 		sync_components_pico(context, 0);
 	} else {
 		sync_components(context, 0);
@@ -963,7 +963,7 @@
 						}
 						context->current_cycle += m68k_cycle_diff;
 						gen->bus_busy = 1;
-						if (gen->header.type == SYSTEM_PICO) {
+						if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) {
 							sync_components_pico(context, 0);
 						} else {
 							sync_components(context, 0);
@@ -990,7 +990,7 @@
 							}
 							context->current_cycle += m68k_cycle_diff;
 							gen->bus_busy = 1;
-							if (gen->header.type == SYSTEM_PICO) {
+							if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) {
 								sync_components_pico(context, 0);
 							} else {
 								sync_components(context, 0);
@@ -1008,7 +1008,7 @@
 			} else {
 				context->sync_cycle = gen->frame_end = vdp_cycles_to_frame_end(v_context);
 				//printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot);
-				if (gen->header.type == SYSTEM_PICO) {
+				if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) {
 					adjust_int_cycle_pico(context, v_context);
 				} else {
 					adjust_int_cycle(context, v_context);
@@ -1097,7 +1097,7 @@
 	//do refresh check here so we can avoid adding a penalty for a refresh that happens during a VDP access
 	gen_update_refresh_free_access(context);
 
-	if (gen->header.type == SYSTEM_PICO) {
+	if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) {
 		sync_components_pico(context, 0);
 	} else {
 		sync_components(context, 0);
@@ -1513,6 +1513,13 @@
 		return gen->pico_pen_y;
 	case 6:
 		return gen->pico_page;
+	case 7:
+		//Copera titles seem to expect bit 0 to be 0 on a Copera and 1 on a Pico
+		if (gen->header.type == SYSTEM_PICO) {
+			return 1;
+		} else {
+			return 0;
+		}
 	case 8:
 		//printf("uPD7759 data read @ %u\n", m68k->current_cycle);
 		sync_sound_pico(gen, m68k->current_cycle);
@@ -1546,6 +1553,56 @@
 	return value | (value << 8);
 }
 
+static uint16_t copera_io_read_w(uint32_t location, void *vcontext)
+{
+	printf("Unhandled Copera 16-bit read %X\n", location);
+	return 0xFFFF;
+}
+
+static uint8_t copera_io_read(uint32_t location, void *vcontext)
+{
+	printf("Unhandled Copera 8-bit read %X\n", location);
+	return 0xFF;
+}
+
+static void* copera_io_write_w(uint32_t location, void *vcontext, uint16_t value)
+{
+	printf("Unhandled Copera 16-bit write %X: %X\n", location, value);
+	return vcontext;
+}
+
+static void* copera_io_write(uint32_t location, void *vcontext, uint8_t value)
+{
+	switch (location & 0xFF)
+	{
+	case 1:
+	case 5:
+		printf("Copera YMZ263 Address write - %X: %X\n", location, value);
+		break;
+	case 3:
+	case 7:
+		printf("Copera YMZ263 Channel #%d Data write - %X: %X\n", ((location & 4) >> 2) + 1, location, value);
+		break;
+	case 0x24:
+	case 0x34:
+		printf("Copera YMF263 Address Part #%d write - %X: %X\n", ((location >> 4) & 1) + 1, location, value);
+		break;
+	case 0x28:
+		printf("Copera YMF263 Data write - %X: %X\n", location, value);
+		break;
+	case 0x40:
+		//Bit 4 = SCI
+		//Bit 5 = DIN
+		//Bit 6 = A0
+		//Possible Bit 0-3 are the same but for the other YM7128B
+		printf("Copera YM7128B Write - %X: %X\n", location, value);
+		break;
+	default:
+		printf("Unhandled Copera 8-bit write %X: %X\n", location, value);
+	}
+	return vcontext;
+}
+
 static void * z80_write_ym(uint32_t location, void * vcontext, uint8_t value)
 {
 	z80_context * context = vcontext;
@@ -1790,7 +1847,7 @@
 		}
 	}
 	uint8_t is_50hz = 0;
-	if (gen->header.type == SYSTEM_PICO) {
+	if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) {
 		if (region & REGION_E) {
 			is_50hz = 1;
 			gen->version_reg = 0x20;
@@ -1865,7 +1922,7 @@
 				z80_assert_reset(gen->z80, gen->m68k->current_cycle);
 				z80_clear_busreq(gen->z80, gen->m68k->current_cycle);
 			}
-			if (gen->header.type != SYSTEM_PICO) {
+			if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) {
 				ym_reset(gen->ym);
 			}
 			//Is there any sort of VDP reset?
@@ -1880,7 +1937,7 @@
 	if (gen->header.force_release || render_should_release_on_exit()) {
 		bindings_release_capture();
 		vdp_release_framebuffer(gen->vdp);
-		if (gen->header.type != SYSTEM_PICO) {
+		if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) {
 			render_pause_source(gen->ym->audio);
 		}
 		render_pause_source(gen->psg->audio);
@@ -1912,7 +1969,7 @@
 			insert_breakpoint(gen->m68k, pc, gen->header.debugger_type == DEBUGGER_NATIVE ? debugger : gdb_debug_enter);
 #endif
 		}
-		if (gen->header.type == SYSTEM_PICO) {
+		if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) {
 			adjust_int_cycle_pico(gen->m68k, gen->vdp);
 		} else {
 			adjust_int_cycle(gen->m68k, gen->vdp);
@@ -1938,14 +1995,14 @@
 	genesis_context *gen = (genesis_context *)system;
 	if (gen->header.force_release || render_should_release_on_exit()) {
 		gen->header.force_release = 0;
-		if (gen->header.type == SYSTEM_PICO) {
+		if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) {
 			render_set_video_standard((gen->version_reg & 0x60) == 0x20 ? VID_PAL : VID_NTSC);
 		} else {
 			render_set_video_standard((gen->version_reg & HZ50) ? VID_PAL : VID_NTSC);
 		}
 		bindings_reacquire_capture();
 		vdp_reacquire_framebuffer(gen->vdp);
-		if (gen->header.type != SYSTEM_PICO) {
+		if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) {
 			render_resume_source(gen->ym->audio);
 		}
 		render_resume_source(gen->psg->audio);
@@ -2099,7 +2156,7 @@
 		free(gen->z80);
 		free(gen->zram);
 	}
-	if (gen->header.type == SYSTEM_PICO) {
+	if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) {
 		pico_pcm_free(gen->adpcm);
 		free(gen->adpcm);
 	} else {
@@ -2295,7 +2352,7 @@
 	char *config_gain;
 	config_gain = tern_find_path(config, "audio\0psg_gain\0", TVAL_PTR).ptrval;
 	render_audio_source_gaindb(gen->psg->audio, config_gain ? atof(config_gain) : 0.0f);
-	if (gen->header.type != SYSTEM_PICO) {
+	if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) {
 		config_gain = tern_find_path(config, "audio\0fm_gain\0", TVAL_PTR).ptrval;
 		render_audio_source_gaindb(gen->ym->audio, config_gain ? atof(config_gain) : 0.0f);
 
@@ -2320,7 +2377,7 @@
 	}
 	set_audio_config(gen);
 	//sample rate may have changed
-	if (gen->header.type != SYSTEM_PICO) {
+	if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) {
 		ym_adjust_master_clock(gen->ym, gen->master_clock);
 	}
 	psg_adjust_master_clock(gen->psg, gen->master_clock);
@@ -2336,7 +2393,7 @@
 	if (vgm) {
 		printf("Started logging VGM to %s\n", filename);
 		sync_sound(gen, vgm->last_cycle);
-		if (gen->header.type != SYSTEM_PICO) {
+		if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) {
 			ym_vgm_log(gen->ym, gen->normal_clock, vgm);
 		}
 		psg_vgm_log(gen->psg, gen->normal_clock, vgm);
@@ -2351,7 +2408,7 @@
 	puts("Stopped VGM log");
 	genesis_context *gen = (genesis_context *)system;
 	vgm_close(gen->ym->vgm);
-	if (gen->header.type != SYSTEM_PICO) {
+	if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) {
 		gen->ym->vgm = NULL;
 	}
 	gen->psg->vgm = NULL;
@@ -2367,7 +2424,7 @@
 	} else if (debug_view == DEBUG_OSCILLOSCOPE) {
 		if (gen->psg->scope) {
 			oscilloscope *scope = gen->psg->scope;
-			if (gen->header.type == SYSTEM_PICO) {
+			if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) {
 				gen->adpcm->scope = NULL;
 			} else {
 				gen->ym->scope = NULL;
@@ -2380,7 +2437,7 @@
 			scope_close(scope);
 		} else {
 			oscilloscope *scope = create_oscilloscope();
-			if (gen->header.type == SYSTEM_PICO) {
+			if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) {
 				pico_pcm_enable_scope(gen->adpcm, scope, gen->normal_clock);
 			} else {
 				ym_enable_scope(gen->ym, scope, gen->normal_clock);
@@ -2989,15 +3046,21 @@
 	{0xE00000, 0x1000000, 0xFFFF, .flags = MMAP_READ | MMAP_WRITE | MMAP_CODE},
 	{0xC00000, 0xE00000,  0x1FFFFF, .read_16 = (read_16_fun)vdp_port_read,  .write_16 =(write_16_fun)vdp_port_write,
 			   .read_8 = (read_8_fun)vdp_port_read_b, .write_8 = (write_8_fun)vdp_port_write_b},
-	{0x800000, 0x900000,  0xFFFFFF,  .read_16 = pico_io_read_w, .write_16 = pico_io_write_w,
-			   .read_8 = pico_io_read, .write_8 = pico_io_write}
+	{0x800000, 0x900000,  0xFFFFFF, .read_16 = pico_io_read_w, .write_16 = pico_io_write_w,
+			   .read_8 = pico_io_read, .write_8 = pico_io_write},
+	{0xB00000, 0xC00000,  0xFFFFFF, .read_16 = copera_io_read_w, .write_16 = copera_io_write_w, 
+	           .read_8 = copera_io_read, .write_8 = copera_io_write}
 };
 const size_t pico_base_chunks = sizeof(pico_base_map)/sizeof(*pico_base_map);
 
-genesis_context* alloc_config_pico(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region)
+genesis_context* alloc_config_pico(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region, system_type stype)
 {
 	tern_node *rom_db = get_rom_db();
-	rom_info info = configure_rom(rom_db, rom, rom_size, lock_on, lock_on_size, pico_base_map, pico_base_chunks);
+	uint32_t chunks = pico_base_chunks;
+	if (stype == SYSTEM_PICO) {
+		chunks--;
+	}
+	rom_info info = configure_rom(rom_db, rom, rom_size, lock_on, lock_on_size, pico_base_map, chunks);
 	rom = info.rom;
 	rom_size = info.rom_size;
 #ifndef BLASTEM_BIG_ENDIAN
@@ -3040,7 +3103,7 @@
 	gen->header.start_vgm_log = start_vgm_log;
 	gen->header.stop_vgm_log = stop_vgm_log;
 	gen->header.toggle_debug_view = toggle_debug_view;
-	gen->header.type = SYSTEM_PICO;
+	gen->header.type = stype;
 	gen->header.info = info;
 	set_region(gen, &info, force_region);
 	gen->vdp_unlocked = 1;
--- a/genesis.h	Wed Feb 21 20:34:52 2024 -0800
+++ b/genesis.h	Wed Feb 21 23:42:19 2024 -0800
@@ -94,7 +94,7 @@
 
 genesis_context *alloc_config_genesis(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t system_opts, uint8_t force_region);
 genesis_context *alloc_config_genesis_cdboot(system_media *media, uint32_t system_opts, uint8_t force_region);
-genesis_context* alloc_config_pico(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region);
+genesis_context* alloc_config_pico(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region, system_type stype);
 void genesis_serialize(genesis_context *gen, serialize_buffer *buf, uint32_t m68k_pc, uint8_t all);
 void genesis_deserialize(deserialize_buffer *buf, genesis_context *gen);
 void gen_update_refresh_free_access(m68k_context *context);
--- a/system.c	Wed Feb 21 20:34:52 2024 -0800
+++ b/system.c	Wed Feb 21 23:42:19 2024 -0800
@@ -41,6 +41,21 @@
 	) {
 		return SYSTEM_SMS;
 	}
+	if (media->size > 400) {
+		uint8_t *buffer = media->buffer;
+		if (!memcmp(buffer + 4, "\x00\x00\x04\x00", 4) && (buffer[0x80] == 0 || buffer[0x80] == 0xFF)) {
+			int i = 0x81;
+			for(; i < 0x400; i++)
+			{
+				if (buffer[i] != buffer[0x80]) {
+					break;
+				}
+			}
+			if (i == 0x400) {
+				return SYSTEM_COPERA;
+			}
+		}
+	}
 	if (safe_cmp("BLSTEL\x02", 0, media->buffer, media->size)) {
 		uint8_t *buffer = media->buffer;
 		if (media->size > 9 && buffer[7] == 0) {
@@ -119,7 +134,8 @@
 	case SYSTEM_MEDIA_PLAYER:
 		return &(alloc_media_player(media, opts))->header;
 	case SYSTEM_PICO:
-		return &(alloc_config_pico(media->buffer, media->size, lock_on, lock_on_size, opts, force_region))->header;
+	case SYSTEM_COPERA:
+		return &(alloc_config_pico(media->buffer, media->size, lock_on, lock_on_size, opts, force_region, stype))->header;
 	default:
 		return NULL;
 	}
--- a/system.h	Wed Feb 21 20:34:52 2024 -0800
+++ b/system.h	Wed Feb 21 23:42:19 2024 -0800
@@ -19,7 +19,8 @@
 	SYSTEM_JAGUAR,
 	SYSTEM_MEDIA_PLAYER,
 	SYSTEM_COLECOVISION,
-	SYSTEM_PICO
+	SYSTEM_PICO,
+	SYSTEM_COPERA
 } system_type;
 
 typedef enum {