# HG changeset patch # User Michael Pavone # Date 1487837317 28800 # Node ID 2e6dcb5c11a28a27a251493a36bbec77c792d8db # Parent 262c0ce8f58661cf0c585f9ec7308e8e097efead WIP support for XBAND mapper hardware diff -r 262c0ce8f586 -r 2e6dcb5c11a2 Makefile --- 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 diff -r 262c0ce8f586 -r 2e6dcb5c11a2 m68k_core.c --- 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); diff -r 262c0ce8f586 -r 2e6dcb5c11a2 m68k_core.h --- 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_ diff -r 262c0ce8f586 -r 2e6dcb5c11a2 m68k_core_x86.c --- 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); } diff -r 262c0ce8f586 -r 2e6dcb5c11a2 romdb.c --- 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; diff -r 262c0ce8f586 -r 2e6dcb5c11a2 romdb.h --- 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); diff -r 262c0ce8f586 -r 2e6dcb5c11a2 xband.c --- /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 +#include +#include +#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; +} diff -r 262c0ce8f586 -r 2e6dcb5c11a2 xband.h --- /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 + +#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_