changeset 1228:2e6dcb5c11a2

WIP support for XBAND mapper hardware
author Michael Pavone <pavone@retrodev.com>
date Thu, 23 Feb 2017 00:08:37 -0800
parents 262c0ce8f586
children a8313793216a
files Makefile m68k_core.c m68k_core.h m68k_core_x86.c romdb.c romdb.h xband.c xband.h
diffstat 8 files changed, 389 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Thu Feb 23 00:08:17 2017 -0800
+++ b/Makefile	Thu Feb 23 00:08:37 2017 -0800
@@ -125,7 +125,7 @@
 AUDIOOBJS=ym2612.o psg.o wave.o
 CONFIGOBJS=config.o tern.o util.o
 
-MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o render_sdl.o io.o romdb.o menu.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
+MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o render_sdl.o io.o romdb.o menu.o xband.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
 
 ifeq ($(CPU),x86_64)
 CFLAGS+=-DX86_64 -m64
--- a/m68k_core.c	Thu Feb 23 00:08:17 2017 -0800
+++ b/m68k_core.c	Thu Feb 23 00:08:37 2017 -0800
@@ -617,10 +617,10 @@
 	m68k_options * opts = context->options;
 	native_map_slot * native_code_map = opts->gen.native_code_map;
 	uint32_t meta_off;
-	memmap_chunk const *mem_chunk = find_map_chunk(address, &opts->gen, MMAP_WRITE | MMAP_CODE, &meta_off);
+	memmap_chunk const *mem_chunk = find_map_chunk(address, &opts->gen, MMAP_CODE, &meta_off);
 	if (mem_chunk) {
-		if ((mem_chunk->flags & (MMAP_WRITE | MMAP_CODE)) == (MMAP_WRITE | MMAP_CODE)) {
-			uint32_t masked = (address & mem_chunk->mask);
+		if (mem_chunk->flags & MMAP_CODE) {
+			uint32_t masked = (address - mem_chunk->start) & mem_chunk->mask;
 			uint32_t final_off = masked + meta_off;
 			uint32_t ram_flags_off = final_off >> (opts->gen.ram_flags_shift + 3);
 			context->ram_code_flags[ram_flags_off] |= 1 << ((final_off >> opts->gen.ram_flags_shift) & 7);
@@ -670,7 +670,7 @@
 static uint8_t get_native_inst_size(m68k_options * opts, uint32_t address)
 {
 	uint32_t meta_off;
-	memmap_chunk const *chunk = find_map_chunk(address, &opts->gen, MMAP_WRITE | MMAP_CODE, &meta_off);
+	memmap_chunk const *chunk = find_map_chunk(address, &opts->gen, MMAP_CODE, &meta_off);
 	if (chunk) {
 		meta_off += (address - chunk->start) & chunk->mask;
 	}
@@ -882,8 +882,10 @@
 		do {
 			encoded = get_native_pointer(address, (void **)context->mem_pointers, &opts->gen);
 			if (!encoded) {
-				map_native_address(context, address, code->cur, 2, 1);
+				code_ptr start = code->cur;
 				translate_out_of_bounds(opts, address);
+				code_ptr after = code->cur;
+				map_native_address(context, address, code->cur, 2, after-start);
 				break;
 			}
 			code_ptr existing = get_native_address(opts, address);
--- a/m68k_core.h	Thu Feb 23 00:08:17 2017 -0800
+++ b/m68k_core.h	Thu Feb 23 00:08:37 2017 -0800
@@ -86,6 +86,7 @@
 uint32_t get_instruction_start(m68k_options *opts, uint32_t address);
 uint16_t m68k_get_ir(m68k_context *context);
 void m68k_print_regs(m68k_context * context);
+void m68k_invalidate_code_range(m68k_context *context, uint32_t start, uint32_t end);
 
 #endif //M68K_CORE_H_
 
--- a/m68k_core_x86.c	Thu Feb 23 00:08:17 2017 -0800
+++ b/m68k_core_x86.c	Thu Feb 23 00:08:37 2017 -0800
@@ -2309,6 +2309,7 @@
 void translate_out_of_bounds(m68k_options *opts, uint32_t address)
 {
 	code_info *code = &opts->gen.code;
+	check_cycles_int(&opts->gen, address);
 	mov_ir(code, address, opts->gen.scratch1, SZ_D);
 	call_args(code, (code_ptr)m68k_out_of_bounds_execution, 1, opts->gen.scratch1);
 }
@@ -2340,23 +2341,47 @@
 		code_ptr dst = get_native_address(context->options, inst_start);
 		code_info orig = {dst, dst + 128, 0};
 		mov_ir(&orig, inst_start, options->gen.scratch2, SZ_D);
-
-		if (!options->retrans_stub) {
-			options->retrans_stub = code->cur;
-			call(code, options->gen.save_context);
-			push_r(code, options->gen.context_reg);
-			call_args(code,(code_ptr)m68k_retranslate_inst, 2, options->gen.scratch2, options->gen.context_reg);
-			pop_r(code, options->gen.context_reg);
-			mov_rr(code, RAX, options->gen.scratch1, SZ_PTR);
-			call(code, options->gen.load_context);
-			jmp_r(code, options->gen.scratch1);
-		}
 		jmp(&orig, options->retrans_stub);
 		inst_start = get_instruction_start(options, inst_start - 2);
 	}
 	return context;
 }
 
+void m68k_invalidate_code_range(m68k_context *context, uint32_t start, uint32_t end)
+{
+	m68k_options *opts = context->options;
+	native_map_slot *native_code_map = opts->gen.native_code_map;
+	memmap_chunk const *mem_chunk = find_map_chunk(start, &opts->gen, 0, NULL);
+	if (mem_chunk) {
+		//calculate the lowest alias for this address
+		start = mem_chunk->start + ((start - mem_chunk->start) & mem_chunk->mask);
+	}
+	mem_chunk = find_map_chunk(end, &opts->gen, 0, NULL);
+	if (mem_chunk) {
+		//calculate the lowest alias for this address
+		end = mem_chunk->start + ((end - mem_chunk->start) & mem_chunk->mask);
+	}
+	uint32_t start_chunk = start / NATIVE_CHUNK_SIZE, end_chunk = end / NATIVE_CHUNK_SIZE;
+	for (uint32_t chunk = start_chunk; chunk <= end_chunk; chunk++)
+	{
+		if (native_code_map[chunk].base) {
+			uint32_t start_offset = chunk == start_chunk ? start % NATIVE_CHUNK_SIZE : 0;
+			uint32_t end_offset = chunk == end_chunk ? end % NATIVE_CHUNK_SIZE : NATIVE_CHUNK_SIZE;
+			for (uint32_t offset = start_offset; offset < end_offset; offset++)
+			{
+				if (native_code_map[chunk].offsets[offset] != INVALID_OFFSET && native_code_map[chunk].offsets[offset] != EXTENSION_WORD) {
+					code_info code;
+					code.cur = native_code_map[chunk].base + native_code_map[chunk].offsets[offset];
+					code.last = code.cur + 32;
+					code.stack_off = 0;
+					mov_ir(&code, chunk * NATIVE_CHUNK_SIZE + offset, opts->gen.scratch2, SZ_D);
+					jmp(&code, opts->retrans_stub);
+				}
+			}
+		}
+	}
+}
+
 void insert_breakpoint(m68k_context * context, uint32_t address, m68k_debug_handler bp_handler)
 {
 	static code_ptr bp_stub = NULL;
@@ -2948,4 +2973,13 @@
 	call(code, opts->native_addr_and_sync);
 	cycles(&opts->gen, 18);
 	jmp_r(code, opts->gen.scratch1);
+	
+	opts->retrans_stub = code->cur;
+	call(code, opts->gen.save_context);
+	push_r(code, opts->gen.context_reg);
+	call_args(code,(code_ptr)m68k_retranslate_inst, 2, opts->gen.scratch2, opts->gen.context_reg);
+	pop_r(code, opts->gen.context_reg);
+	mov_rr(code, RAX, opts->gen.scratch1, SZ_PTR);
+	call(code, opts->gen.load_context);
+	jmp_r(code, opts->gen.scratch1);
 }
--- a/romdb.c	Thu Feb 23 00:08:17 2017 -0800
+++ b/romdb.c	Thu Feb 23 00:08:37 2017 -0800
@@ -5,13 +5,12 @@
 #include "util.h"
 #include "genesis.h"
 #include "menu.h"
+#include "xband.h"
 
 #define DOM_TITLE_START 0x120
 #define DOM_TITLE_END 0x150
 #define TITLE_START DOM_TITLE_END
 #define TITLE_END (TITLE_START+48)
-#define GAME_ID_OFF 0x183
-#define GAME_ID_LEN 8
 #define ROM_END   0x1A4
 #define RAM_ID    0x1B0
 #define RAM_FLAGS 0x1B2
@@ -848,6 +847,9 @@
 	tern_node * entry = tern_find_ptr(rom_db, product_id);
 	if (!entry) {
 		puts("Not found in ROM DB, examining header\n");
+			if (xband_detect(rom, rom_size)) {
+				return xband_configure_rom(rom_db, rom, rom_size, lock_on, lock_on_size, base_map, base_chunks);
+			}
 		return configure_rom_heuristics(rom, rom_size, base_map, base_chunks);
 	}
 	rom_info info;
--- a/romdb.h	Thu Feb 23 00:08:17 2017 -0800
+++ b/romdb.h	Thu Feb 23 00:08:37 2017 -0800
@@ -57,6 +57,9 @@
 	uint8_t       regions;
 };
 
+#define GAME_ID_OFF 0x183
+#define GAME_ID_LEN 8
+
 tern_node *load_rom_db();
 rom_info configure_rom(tern_node *rom_db, void *vrom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, memmap_chunk const *base_map, uint32_t base_chunks);
 rom_info configure_rom_heuristics(uint8_t *rom, uint32_t rom_size, memmap_chunk const *base_map, uint32_t base_chunks);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xband.c	Thu Feb 23 00:08:37 2017 -0800
@@ -0,0 +1,311 @@
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include "romdb.h"
+#include "genesis.h"
+#include "tern.h"
+#include "xband.h"
+#include "util.h"
+
+#define BIT_ROM_HI 4
+
+uint8_t xband_detect(uint8_t *rom, uint32_t rom_size)
+{
+	//Internal ROM is 512KB, accept larger ones for overdumps and custom firmware
+	if (rom_size < (512*1024)) {
+		return 0;
+	}
+	
+	//ROM has no standard header, but does have a jump at $100
+	if (rom[0x100] != 0x4E || rom[0x101] != 0xF9) {
+		return 0;
+	}
+	
+	//product ID is all NULL
+	for (int i = GAME_ID_OFF; i <= (GAME_ID_OFF + GAME_ID_LEN); i++)
+	{
+		if (rom[i]) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static xband *get_xband(genesis_context *gen)
+{
+	if (!gen->extra) {
+		gen->extra = gen->m68k->options->gen.memmap[0].buffer;
+		gen->m68k->mem_pointers[2] = (uint16_t *)gen->save_storage;
+	}
+	return gen->extra;
+}
+
+static void update_control(genesis_context *gen, uint8_t value)
+{
+	xband *x = gen->extra;
+	if ((x->control ^ value) & BIT_ROM_HI) {
+		if (value & BIT_ROM_HI) {
+			gen->m68k->mem_pointers[0] = (uint16_t *)gen->save_storage;
+			gen->m68k->mem_pointers[1] = NULL;
+			gen->m68k->mem_pointers[2] = x->cart_space;
+			gen->m68k->mem_pointers[3] = x->cart_space - 0x100000;
+		} else {
+			gen->m68k->mem_pointers[0] = x->cart_space;
+			gen->m68k->mem_pointers[1] = x->cart_space;
+			gen->m68k->mem_pointers[2] = (uint16_t *)gen->save_storage;
+			gen->m68k->mem_pointers[3] = NULL;
+		}
+		m68k_invalidate_code_range(gen->m68k, 0, 0x3BC000);
+	}
+	x->control = value;
+}
+
+static void *xband_write_b(uint32_t address, void *context, uint8_t value)
+{
+	m68k_context *m68k = context;
+	genesis_context *gen = m68k->system;
+	xband *x = get_xband(gen);
+	if (address == 0x181) {
+		x->kill = value;
+		printf("Write to \"soft\" kill register %X\n", value);
+	} else if (address == 0x183) {
+		update_control(gen, value);
+		printf("Write to \"soft\" control register %X\n", value);
+	} else if ((x->control & BIT_ROM_HI && address < 0x200000) || (address >= 0x200000 && !(x->control & BIT_ROM_HI))) {
+		gen->save_storage[(address & 0xFFFF) ^ 1] = value;
+		m68k_handle_code_write(address, m68k);
+		//TODO: handle code at mirror addresses
+	} else {
+		printf("Unhandled write to cartridge area %X: %X\n", address, value);
+	}
+	return context;
+}
+
+static void *xband_write_hi_b(uint32_t address, void *context, uint8_t value)
+{
+	return xband_write_b(address | 0x200000, context, value);
+}
+
+static void *xband_write_w(uint32_t address, void *context, uint16_t value)
+{
+	m68k_context *m68k = context;
+	genesis_context *gen = m68k->system;
+	xband *x = get_xband(gen);
+	if (address == 0x180 || address == 0x182) {
+		return xband_write_b(address | 1, context, value);
+	} else if ((x->control & BIT_ROM_HI && address < 0x200000) || (address >= 0x200000 && !(x->control & BIT_ROM_HI))) {
+		gen->save_storage[address & 0xFFFE] = value;
+		gen->save_storage[(address & 0xFFFE) | 1] = value >> 8;
+		m68k_handle_code_write(address, m68k);
+		//TODO: handle code at mirror addresses
+		return context;
+	}
+	printf("Unhandled write to %X: %X\n", address, value);
+	return context;
+}
+
+static void *xband_write_hi_w(uint32_t address, void *context, uint16_t value)
+{
+	return xband_write_w(address | 0x200000, context, value);
+}
+
+static uint16_t xband_read_w(uint32_t address, void *context)
+{
+	m68k_context *m68k = context;
+	genesis_context *gen = m68k->system;
+	xband *x = get_xband(gen);
+	//TODO: actually do something intelligent here
+	return x->cart_space[address >> 1];
+}
+
+static uint16_t xband_read_hi_w(uint32_t address, void *context)
+{
+	return xband_read_w(address | 0x200000, context);
+}
+
+static uint8_t xband_read_b(uint32_t address, void *context)
+{
+	uint16_t val = xband_read_w(address, context);
+	return address & 1 ? val : val >> 8;
+}
+
+static uint8_t xband_read_hi_b(uint32_t address, void *context)
+{
+	return xband_read_b(address | 0x200000, context);
+}
+
+static void *xband_reg_write_b(uint32_t address, void *context, uint8_t value)
+{
+	m68k_context *m68k = context;
+	genesis_context *gen = m68k->system;
+	if (!(address & 1)) {
+		printf("Ignoring write to even address %X: %X\n", address, value);
+		return context;
+	}
+	xband *x = get_xband(gen);
+	if (address < 0x3BFE00) {
+		uint32_t offset = (address - 0x3BC001) / 2;
+		if (offset < XBAND_REGS) {
+			x->regs[offset] = value;
+			printf("Write to register %X: %X\n", address, value);
+		} else {
+			printf("Unhandled register write %X: %X\n", address, value);
+		}
+	} else {
+		if (address == 0x3BFE01) {
+			x->kill = value;
+			printf("Write to kill register %X\n", value);
+		} else if (address == 0x3BFE03) {
+			update_control(gen, value);
+			printf("Write to control register %X\n", value);
+		} else {
+			printf("Unhandled register write %X: %X\n", address, value);
+		}
+	}
+	return context;
+}
+
+static void *xband_reg_write_w(uint32_t address, void *context, uint16_t value)
+{
+	return xband_reg_write_b(address | 1, context, value);
+}
+
+static uint8_t xband_reg_read_b(uint32_t address, void *context)
+{
+	m68k_context *m68k = context;
+	genesis_context *gen = m68k->system;
+	if (!(address & 1)) {
+		printf("Read from even address %X\n", address);
+		return gen->header.get_open_bus_value(&gen->header) >> 8;
+	}
+	xband *x = get_xband(gen);
+	if (address < 0x3BFE00) {
+		uint32_t offset = (address - 0x3BC001) / 2;
+		if (offset < XBAND_REGS) {
+			return x->regs[offset];
+		} else {
+			printf("Unhandled register read from address %X\n", address);
+			return 0x5D;
+		}
+	} else {
+		if (address == 0x3BFE01) {
+			return x->kill;
+		} else if (address == 0x3BFE03) {
+			return x->control;
+		} else {
+			printf("Unhandled register read from address %X\n", address);
+			return 0x5D;
+		}
+	}
+}
+
+static uint16_t xband_reg_read_w(uint32_t address, void *context)
+{
+	m68k_context *m68k = context;
+	genesis_context *gen = m68k->system;
+	uint16_t value = xband_reg_read_b(address | 1, context);
+	value |= gen->header.get_open_bus_value(&gen->header) & 0xFF00;
+	return value;
+}
+
+rom_info xband_configure_rom(tern_node *rom_db, void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, memmap_chunk const *base_map, uint32_t base_chunks)
+{
+	rom_info info;
+	if (lock_on && lock_on_size) {
+		rom_info lock_on_info = configure_rom(rom_db, lock_on, lock_on_size, NULL, 0, base_map, base_chunks);
+		info.name = alloc_concat("XBAND - ", lock_on_info.name);
+		info.regions = lock_on_info.regions;
+		//TODO: Move this to a shared function in romdbc.h
+		free(lock_on_info.name);
+		if (lock_on_info.save_type != SAVE_NONE) {
+			free(lock_on_info.save_buffer);
+			if (lock_on_info.save_type == SAVE_I2C) {
+				free(lock_on_info.eeprom_map);
+			}
+		}
+		free(lock_on_info.map);
+		free(lock_on_info.port1_override);
+		free(lock_on_info.port2_override);
+		free(lock_on_info.ext_override);
+		free(lock_on_info.mouse_mode);
+	} else {
+		info.name = strdup("XBAND");
+		info.regions = REGION_J|REGION_U|REGION_E;
+	}
+	info.save_size = 64*1024;
+	info.save_buffer = malloc(info.save_size);
+	info.save_mask = info.save_size-1;
+	info.save_type = RAM_FLAG_BOTH;
+	info.port1_override = info.port2_override = info.ext_override = info.mouse_mode = NULL;
+	info.eeprom_map = NULL;
+	info.num_eeprom = 0;
+	xband *x = calloc(sizeof(xband), 1);
+	rom_size = nearest_pow2(rom_size);
+	for (int i = 0; (i + rom_size) <= sizeof(x->cart_space) / 2; i += rom_size)
+	{
+		memcpy(x->cart_space + i/2, rom, rom_size);
+	}
+	if (lock_on && lock_on_size >= 0x200) {
+		memcpy(x->cart_space + 0x80, ((uint16_t *)lock_on) + 0x80, 0x100);
+	}
+	byteswap_rom(0x400000, x->cart_space);
+	
+	info.map_chunks = base_chunks + 5;
+	info.map = calloc(sizeof(memmap_chunk), info.map_chunks);
+	info.map[0].mask = 0xFFFFFF;
+	info.map[0].aux_mask = 0xFFFFFF;
+	info.map[0].flags = MMAP_READ|MMAP_CODE|MMAP_PTR_IDX|MMAP_FUNC_NULL|MMAP_AUX_BUFF;
+	info.map[0].start = 0;
+	info.map[0].end = 0x10000;
+	info.map[0].ptr_index = 0;
+	info.map[0].buffer = x->cart_space;
+	info.map[0].write_16 = xband_write_w;
+	info.map[0].write_8 = xband_write_b;
+	info.map[0].read_16 = xband_read_w;
+	info.map[0].read_8 = xband_read_b;
+	info.map[1].mask = 0xFFFFFF;
+	info.map[1].aux_mask = 0xFFFFFF;
+	info.map[1].flags = MMAP_READ|MMAP_CODE|MMAP_PTR_IDX|MMAP_FUNC_NULL|MMAP_AUX_BUFF;
+	info.map[1].start = 0x10000;
+	info.map[1].end = 0x200000;
+	info.map[1].ptr_index = 1;
+	info.map[1].buffer = x->cart_space;
+	info.map[1].write_16 = xband_write_w;
+	info.map[1].write_8 = xband_write_b;
+	info.map[1].read_16 = xband_read_w;
+	info.map[1].read_8 = xband_read_b;
+	info.map[2].mask = 0xFFFF;
+	info.map[2].aux_mask = 0xFFFF;
+	info.map[2].flags = MMAP_READ|MMAP_CODE|MMAP_PTR_IDX|MMAP_FUNC_NULL;
+	info.map[2].start = 0x200000;
+	info.map[2].end = 0x210000;
+	info.map[2].ptr_index = 2;
+	info.map[2].buffer = NULL;
+	info.map[2].write_16 = xband_write_hi_w;
+	info.map[2].write_8 = xband_write_hi_b;
+	info.map[2].read_16 = xband_read_hi_w;
+	info.map[2].read_8 = xband_read_hi_b;
+	info.map[3].mask = 0xFFFFFF;
+	info.map[3].aux_mask = 0xFFFFFF;
+	info.map[3].flags = MMAP_READ|MMAP_CODE|MMAP_PTR_IDX|MMAP_FUNC_NULL;
+	info.map[3].start = 0x210000;
+	info.map[3].end = 0x3BC000;
+	info.map[3].ptr_index = 3;
+	info.map[3].buffer = NULL;
+	info.map[3].write_16 = xband_write_w;
+	info.map[3].write_8 = xband_write_b;
+	info.map[3].read_16 = xband_read_w;
+	info.map[3].read_8 = xband_read_b;
+	info.map[4].mask = 0xFFFFFF;
+	info.map[4].flags = MMAP_READ|MMAP_CODE|MMAP_PTR_IDX|MMAP_FUNC_NULL;
+	info.map[4].start = 0x3BC000;
+	info.map[4].end = 0x3C0000;
+	info.map[4].ptr_index = 4;
+	info.map[4].write_16 = xband_reg_write_w;
+	info.map[4].write_8 = xband_reg_write_b;
+	info.map[4].read_16 = xband_reg_read_w;
+	info.map[4].read_8 = xband_reg_read_b;
+	memcpy(info.map + 5, base_map, base_chunks * sizeof(memmap_chunk));
+	
+	return info;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xband.h	Thu Feb 23 00:08:37 2017 -0800
@@ -0,0 +1,17 @@
+#ifndef XBAND_H_
+#define XBAND_H_
+#include <stdint.h>
+
+#define XBAND_REGS 0xB6
+
+typedef struct {
+	uint16_t cart_space[0x200000];
+	uint8_t regs[XBAND_REGS];
+	uint8_t kill;
+	uint8_t control;
+} xband;
+
+uint8_t xband_detect(uint8_t *rom, uint32_t rom_size);
+rom_info xband_configure_rom(tern_node *rom_db, void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, memmap_chunk const *base_map, uint32_t base_chunks);
+
+#endif //XBAND_H_