From 314f74f357c844aaf9a729b384708a6b6b433a2f Mon Sep 17 00:00:00 2001 From: Michael Pavone Date: Fri, 13 Nov 2015 19:15:37 -0800 Subject: [PATCH] It is now possible to switch back and forth between the menu ROM and the game --- Makefile | 2 +- arena.c | 81 +++++++++++++++++++++++++++++++++ arena.h | 18 ++++++++ blastem.c | 117 +++++++++++++++++++++++++++++++----------------- blastem.h | 5 ++- io.c | 6 +-- m68k_core.c | 10 +++++ m68k_core.h | 3 +- m68k_core_x86.c | 2 +- mem.c | 9 +++- util.c | 6 +-- 11 files changed, 207 insertions(+), 52 deletions(-) create mode 100644 arena.c create mode 100644 arena.h diff --git a/Makefile b/Makefile index aad5962..fd7f004 100644 --- a/Makefile +++ b/Makefile @@ -91,7 +91,7 @@ ifndef CPU CPU:=$(shell uname -m) endif -TRANSOBJS=gen.o backend.o $(MEM) +TRANSOBJS=gen.o backend.o $(MEM) arena.o M68KOBJS=68kinst.o m68k_core.o ifeq ($(CPU),x86_64) M68KOBJS+= m68k_core_x86.o diff --git a/arena.c b/arena.c new file mode 100644 index 0000000..6bc50e0 --- /dev/null +++ b/arena.c @@ -0,0 +1,81 @@ +/* + Copyright 2015 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 +#include +#include "arena.h" + +struct arena { + void **used_blocks; + void **free_blocks; + + size_t used_count; + size_t used_storage; + size_t free_count; + size_t free_storage; +}; + +static arena *current_arena; + +arena *get_current_arena() +{ + if (!current_arena) { + current_arena = calloc(1, sizeof(arena)); + } + return current_arena; +} + +arena *set_current_arena(arena *a) +{ + arena *tmp = current_arena; + current_arena = a; + return tmp; +} + +arena *start_new_arena() +{ + arena *tmp = current_arena; + current_arena = NULL; + return tmp; +} + +void track_block(void *block) +{ + arena *cur = get_current_arena(); + if (cur->used_count == cur->used_storage) { + cur->used_storage *= 2; + cur->used_blocks = realloc(cur->used_blocks, cur->used_storage * sizeof(void *)); + } + cur->used_blocks[cur->used_count++] = block; +} + +void mark_all_free() +{ + arena *cur = get_current_arena(); + if (!cur->free_blocks) { + cur->free_blocks = cur->used_blocks; + cur->free_storage = cur->used_storage; + cur->free_count = cur->used_count; + cur->used_count = cur->used_storage = 0; + cur->used_blocks = NULL; + } else { + if (cur->free_storage < cur->used_count + cur->free_count) { + cur->free_storage = cur->used_count + cur->free_count; + cur->free_blocks = realloc(cur->free_blocks, cur->free_storage * sizeof(void*)); + } + for (; cur->used_count > 0; cur->used_count--) + { + cur->free_blocks[cur->free_count++] = cur->used_blocks[cur->used_count-1]; + } + } +} + +void *try_alloc_arena() +{ + if (!current_arena || !current_arena->free_count) { + return NULL; + } + return current_arena->free_blocks[--current_arena->free_count]; +} diff --git a/arena.h b/arena.h new file mode 100644 index 0000000..26a9555 --- /dev/null +++ b/arena.h @@ -0,0 +1,18 @@ +/* + Copyright 2015 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. +*/ +#ifndef ARENA_H_ +#define ARENA_H_ + +typedef struct arena arena; + +arena *get_current_arena(); +arena *set_current_arena(arena *a); +arena *start_new_arena(); +void track_block(void *block); +void mark_all_free(); +void *try_alloc_arena(); + +#endif //ARENA_H_ diff --git a/blastem.c b/blastem.c index 2520ef9..354197f 100644 --- a/blastem.c +++ b/blastem.c @@ -3,6 +3,11 @@ 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 +#include +#include +#include + #include "68kinst.h" #include "m68k_core.h" #include "z80_to_x86.h" @@ -15,10 +20,7 @@ #include "util.h" #include "romdb.h" #include "terminal.h" -#include -#include -#include -#include +#include "arena.h" #define BLASTEM_VERSION "0.3.X" @@ -44,7 +46,7 @@ #endif uint16_t *cart; -uint16_t ram[RAM_WORDS]; +uint16_t *ram; uint8_t z80_ram[Z80_RAM_BYTES]; int headless = 0; @@ -784,17 +786,6 @@ void set_speed_percent(genesis_context * context, uint32_t percent) psg_adjust_master_clock(context->psg, context->master_clock); } -const memmap_chunk base_map[] = { - {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, - (read_8_fun)vdp_port_read_b, (write_8_fun)vdp_port_write_b}, - {0xA00000, 0xA12000, 0x1FFFF, 0, 0, NULL, - (read_16_fun)io_read_w, (write_16_fun)io_write_w, - (read_8_fun)io_read, (write_8_fun)io_write} - }; - char * save_filename; genesis_context *genesis; genesis_context *menu_context; @@ -850,6 +841,7 @@ genesis_context *alloc_init_genesis(rom_info *rom, int fps, uint32_t ym_opts) gen->z80->mem_pointers[0] = z80_ram; gen->z80->mem_pointers[1] = gen->z80->mem_pointers[2] = (uint8_t *)cart; + gen->cart = cart; gen->work_ram = ram; gen->zram = z80_ram; setup_io_devices(config, gen->ports); @@ -1080,6 +1072,17 @@ int main(int argc, char ** argv) loaded = 1; } + ram = malloc(RAM_WORDS * sizeof(uint16_t)); + memmap_chunk base_map[] = { + {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, + (read_8_fun)vdp_port_read_b, (write_8_fun)vdp_port_write_b}, + {0xA00000, 0xA12000, 0x1FFFF, 0, 0, NULL, + (read_16_fun)io_read_w, (write_16_fun)io_write_w, + (read_8_fun)io_read, (write_8_fun)io_write} + }; tern_node *rom_db = load_rom_db(); rom_info info = configure_rom(rom_db, cart, rom_size, base_map, sizeof(base_map)/sizeof(base_map[0])); byteswap_rom(rom_size); @@ -1127,33 +1130,67 @@ int main(int argc, char ** argv) } start_genesis(genesis, menu ? NULL : statefile, menu ? NULL : debuggerfun); - if (menu && menu_context->next_rom) { - //TODO: Allow returning to menu - if (!(rom_size = load_rom(menu_context->next_rom))) { - fatal_error("Failed to open %s for reading\n", menu_context->next_rom); - } - info = configure_rom(rom_db, cart, rom_size, base_map, sizeof(base_map)/sizeof(base_map[0])); - byteswap_rom(rom_size); - set_region(&info, force_version); - update_title(info.name); - fname_size = strlen(romfname); - ext = info.save_type == SAVE_I2C ? "eeprom" : "sram"; - save_filename = malloc(fname_size+strlen(ext) + 2); - memcpy(save_filename, romfname, fname_size); - for (i = fname_size-1; fname_size >= 0; --i) { - if (save_filename[i] == '.') { - strcpy(save_filename + i + 1, ext); - break; + for(;;) + { + if (menu && menu_context->next_rom) { + if (!(rom_size = load_rom(menu_context->next_rom))) { + fatal_error("Failed to open %s for reading\n", menu_context->next_rom); + } + base_map[0].buffer = ram = malloc(RAM_WORDS * sizeof(uint16_t)); + info = configure_rom(rom_db, cart, rom_size, base_map, sizeof(base_map)/sizeof(base_map[0])); + byteswap_rom(rom_size); + set_region(&info, force_version); + update_title(info.name); + fname_size = strlen(romfname); + ext = info.save_type == SAVE_I2C ? "eeprom" : "sram"; + save_filename = malloc(fname_size+strlen(ext) + 2); + memcpy(save_filename, romfname, fname_size); + for (i = fname_size-1; fname_size >= 0; --i) { + if (save_filename[i] == '.') { + strcpy(save_filename + i + 1, ext); + break; + } } + if (i < 0) { + save_filename[fname_size] = '.'; + strcpy(save_filename + fname_size + 1, ext); + } + if (!game_context) { + //start a new arena and save old one in suspended genesis context + genesis->arena = start_new_arena(); + //allocate new genesis context + game_context = alloc_init_genesis(&info, fps, ym_log ? YM_OPT_WAVE_LOG : 0); + } else { + //TODO: hard reset of context with new ROM + } + free(menu_context->next_rom); + menu_context->next_rom = NULL; + menu = 0; + genesis = game_context; + genesis->m68k->options->address_log = address_log; + start_genesis(genesis, statefile, debuggerfun); } - if (i < 0) { - save_filename[fname_size] = '.'; - strcpy(save_filename + fname_size + 1, ext); + else if (menu && game_context) { + puts("Switching back to game context"); + genesis->arena = set_current_arena(game_context->arena); + genesis = game_context; + cart = genesis->cart; + ram = genesis->work_ram; + menu = 0; + set_keybindings(genesis->ports); + resume_68k(genesis->m68k); + } else if (!menu && menu_context) { + puts("Switching back to menu context"); + genesis->arena = set_current_arena(menu_context->arena); + genesis = menu_context; + cart = genesis->cart; + ram = genesis->work_ram; + menu = 1; + set_keybindings(genesis->ports); + resume_68k(genesis->m68k); + } else { + break; } - game_context = alloc_init_genesis(&info, fps, ym_log ? YM_OPT_WAVE_LOG : 0); - genesis->m68k->options->address_log = address_log; - genesis = game_context; - start_genesis(genesis, statefile, debuggerfun); } return 0; diff --git a/blastem.h b/blastem.h index a4e2d1f..c5150a4 100644 --- a/blastem.h +++ b/blastem.h @@ -15,6 +15,7 @@ #include "io.h" #include "config.h" #include "romdb.h" +#include "arena.h" typedef struct { m68k_context *m68k; @@ -22,9 +23,11 @@ typedef struct { vdp_context *vdp; ym2612_context *ym; psg_context *psg; + uint16_t *cart; uint16_t *work_ram; uint8_t *zram; void *extra; + arena *arena; char *next_rom; uint8_t *save_storage; eeprom_map *eeprom_map; @@ -53,7 +56,7 @@ extern tern_node * config; #define Z80_RAM_BYTES 8 * 1024 extern uint16_t *cart; -extern uint16_t ram[RAM_WORDS]; +extern uint16_t *ram; extern uint8_t z80_ram[Z80_RAM_BYTES]; uint16_t read_dma_value(uint32_t address); diff --git a/io.c b/io.c index 6199af0..0debbd2 100644 --- a/io.c +++ b/io.c @@ -293,7 +293,7 @@ void handle_binding_up(keybinding * binding) } break; case UI_EXIT: - exit(0); + genesis->m68k->should_return = 1; } break; } @@ -609,7 +609,7 @@ void setup_io_devices(tern_node * config, io_port * ports) close(ports[i].device.stream.listen_fd); ports[i].device_type = IO_NONE; } - } else + } else #endif if (ports[i].device_type == IO_GAMEPAD3 || ports[i].device_type == IO_GAMEPAD6) { printf("IO port %s connected to gamepad #%d with type '%s'\n", io_name(i), ports[i].device.pad.gamepad_num + 1, device_type_names[ports[i].device_type]); @@ -672,7 +672,7 @@ void set_keybindings(io_port *ports) if (pads) { for (int i = 0; i < 100 && i < render_num_joysticks(); i++) { - + if (i < 10) { numstr[0] = i + '0'; numstr[1] = 0; diff --git a/m68k_core.c b/m68k_core.c index 02463c9..877327e 100644 --- a/m68k_core.c +++ b/m68k_core.c @@ -983,6 +983,16 @@ void start_68k_context(m68k_context * context, uint32_t address) { code_ptr addr = get_native_address_trans(context, address); m68k_options * options = context->options; + context->should_return = 0; + options->start_context(addr, context); +} + +void resume_68k(m68k_context *context) +{ + code_ptr addr = context->resume_pc; + context->resume_pc = NULL; + m68k_options * options = context->options; + context->should_return = 0; options->start_context(addr, context); } diff --git a/m68k_core.h b/m68k_core.h index 20cd6b1..07a1fb0 100644 --- a/m68k_core.h +++ b/m68k_core.h @@ -58,7 +58,7 @@ typedef struct { uint32_t int_cycle; uint32_t int_num; uint16_t *mem_pointers[NUM_MEM_AREAS]; - void *resume_pc; + code_ptr resume_pc; native_map_slot *native_code_map; m68k_options *options; void *system; @@ -70,6 +70,7 @@ typedef struct { void translate_m68k(m68k_options * opts, struct m68kinst * inst); void translate_m68k_stream(uint32_t address, m68k_context * context); void start_68k_context(m68k_context * context, uint32_t address); +void resume_68k(m68k_context *context); void init_m68k_opts(m68k_options * opts, memmap_chunk * memmap, uint32_t num_chunks, uint32_t clock_divider); m68k_context * init_68k_context(m68k_options * opts); void m68k_reset(m68k_context * context); diff --git a/m68k_core_x86.c b/m68k_core_x86.c index fca07b5..9a43f01 100644 --- a/m68k_core_x86.c +++ b/m68k_core_x86.c @@ -2515,8 +2515,8 @@ void init_m68k_opts(m68k_options * opts, memmap_chunk * memmap, uint32_t num_chu retn(code); *do_ret = code->cur - (do_ret+1); pop_r(code, opts->gen.scratch1); - retn(code); mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, resume_pc), SZ_PTR); + retn(code); *do_int = code->cur - (do_int+1); //implement 1 instruction latency cmp_irdisp(code, 0, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B); diff --git a/mem.c b/mem.c index c9bb75a..b801bea 100644 --- a/mem.c +++ b/mem.c @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + 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 @@ -12,6 +12,7 @@ #include #include "mem.h" +#include "arena.h" #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif @@ -25,8 +26,12 @@ void * alloc_code(size_t *size) //start at the 1GB mark to allow plenty of room for sbrk based malloc implementations //while still keeping well within 32-bit displacement range for calling code compiled into the executable static uint8_t *next = (uint8_t *)0x40000000; + uint8_t *ret = try_alloc_arena(); + if (ret) { + return ret; + } *size += PAGE_SIZE - (*size & (PAGE_SIZE - 1)); - uint8_t *ret = mmap(NULL, *size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0); + ret = mmap(NULL, *size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0); if (ret == MAP_FAILED) { perror("alloc_code"); return NULL; diff --git a/util.c b/util.c index 5bb6191..697ba1b 100644 --- a/util.c +++ b/util.c @@ -23,9 +23,9 @@ #define warning_puts(msg) fputs(stderr, msg); #define fatal_puts(msg) fputs(stderr, msg); -#define info_printf(msg, args vprintf(msg, args) -#define warning_printf(msg, args vfprintf(stderr, msg, args) -#define fatal_printf(msg, args vfprintf(stderr, msg, args) +#define info_printf(msg, args) vprintf(msg, args) +#define warning_printf(msg, args) vfprintf(stderr, msg, args) +#define fatal_printf(msg, args) vfprintf(stderr, msg, args) #endif #include "blastem.h" //for headless global