# HG changeset patch # User Michael Pavone # Date 1437241335 25200 # Node ID 13d3744b170ec40d4ed1f6f4e02e9f3bb828a80f # Parent 1b82b282b829921d3930eb94ed86a1f8b319a9e3# Parent 2a25ea5d94f76536c97611724219a6b57af7fb06 Merge diff -r 2a25ea5d94f7 -r 13d3744b170e Makefile --- a/Makefile Sun Jun 28 19:23:38 2015 -0700 +++ b/Makefile Sat Jul 18 10:42:15 2015 -0700 @@ -99,7 +99,7 @@ AUDIOOBJS=ym2612.o psg.o wave.o CONFIGOBJS=config.o tern.o util.o -MAINOBJS=blastem.o debug.o gdb_remote.o vdp.o render_sdl.o io.o $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) +MAINOBJS=blastem.o debug.o gdb_remote.o vdp.o render_sdl.o io.o romdb.o $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) ifeq ($(CPU),x86_64) CFLAGS+=-DX86_64 -m64 diff -r 2a25ea5d94f7 -r 13d3744b170e blastem.c --- a/blastem.c Sun Jun 28 19:23:38 2015 -0700 +++ b/blastem.c Sat Jul 18 10:42:15 2015 -0700 @@ -13,6 +13,7 @@ #include "gdb_remote.h" #include "gst.h" #include "util.h" +#include "romdb.h" #include #include #include @@ -61,14 +62,23 @@ fseek(f, SMD_HEADER_SIZE, SEEK_SET); uint16_t * dst = cart; + int rom_size = filesize; while (filesize > 0) { fread(block, 1, SMD_BLOCK_SIZE, f); for (uint8_t *low = block, *high = (block+SMD_BLOCK_SIZE/2), *end = block+SMD_BLOCK_SIZE; high < end; high++, low++) { - *(dst++) = *high << 8 | *low; + *(dst++) = *low << 8 | *high; } filesize -= SMD_BLOCK_SIZE; } - return 1; + return filesize; +} + +void byteswap_rom() +{ + for(unsigned short * cur = cart; cur - cart < CARTRIDGE_WORDS; ++cur) + { + *cur = (*cur >> 8) | (*cur << 8); + } } int load_rom(char * filename) @@ -103,12 +113,7 @@ } fread(cart, 2, filesize/2, f); fclose(f); - for(unsigned short * cur = cart; cur - cart < (filesize/2); ++cur) - { - *cur = (*cur >> 8) | (*cur << 8); - } - //TODO: Mirror ROM - return 1; + return filesize; } uint16_t read_dma_value(uint32_t address) @@ -762,127 +767,6 @@ return context; } -uint16_t read_sram_w(uint32_t address, m68k_context * context) -{ - genesis_context * gen = context->system; - address &= gen->save_ram_mask; - switch(gen->save_flags) - { - case RAM_FLAG_BOTH: - return gen->save_ram[address] << 8 | gen->save_ram[address+1]; - case RAM_FLAG_EVEN: - return gen->save_ram[address >> 1] << 8 | 0xFF; - case RAM_FLAG_ODD: - return gen->save_ram[address >> 1] | 0xFF00; - } - return 0xFFFF;//We should never get here -} - -uint8_t read_sram_b(uint32_t address, m68k_context * context) -{ - genesis_context * gen = context->system; - address &= gen->save_ram_mask; - switch(gen->save_flags) - { - case RAM_FLAG_BOTH: - return gen->save_ram[address]; - case RAM_FLAG_EVEN: - if (address & 1) { - return 0xFF; - } else { - return gen->save_ram[address >> 1]; - } - case RAM_FLAG_ODD: - if (address & 1) { - return gen->save_ram[address >> 1]; - } else { - return 0xFF; - } - } - return 0xFF;//We should never get here -} - -m68k_context * write_sram_area_w(uint32_t address, m68k_context * context, uint16_t value) -{ - genesis_context * gen = context->system; - if ((gen->bank_regs[0] & 0x3) == 1) { - address &= gen->save_ram_mask; - switch(gen->save_flags) - { - case RAM_FLAG_BOTH: - gen->save_ram[address] = value >> 8; - gen->save_ram[address+1] = value; - break; - case RAM_FLAG_EVEN: - gen->save_ram[address >> 1] = value >> 8; - break; - case RAM_FLAG_ODD: - gen->save_ram[address >> 1] = value; - break; - } - } - return context; -} - -m68k_context * write_sram_area_b(uint32_t address, m68k_context * context, uint8_t value) -{ - genesis_context * gen = context->system; - if ((gen->bank_regs[0] & 0x3) == 1) { - address &= gen->save_ram_mask; - switch(gen->save_flags) - { - case RAM_FLAG_BOTH: - gen->save_ram[address] = value; - break; - case RAM_FLAG_EVEN: - if (!(address & 1)) { - gen->save_ram[address >> 1] = value; - } - break; - case RAM_FLAG_ODD: - if (address & 1) { - gen->save_ram[address >> 1] = value; - } - break; - } - } - return context; -} - -m68k_context * write_bank_reg_w(uint32_t address, m68k_context * context, uint16_t value) -{ - genesis_context * gen = context->system; - address &= 0xE; - address >>= 1; - gen->bank_regs[address] = value; - if (!address) { - if (value & 1) { - context->mem_pointers[2] = NULL; - } else { - context->mem_pointers[2] = cart + 0x200000/2; - } - } - return context; -} - -m68k_context * write_bank_reg_b(uint32_t address, m68k_context * context, uint8_t value) -{ - if (address & 1) { - genesis_context * gen = context->system; - address &= 0xE; - address >>= 1; - gen->bank_regs[address] = value; - if (!address) { - if (value & 1) { - context->mem_pointers[2] = NULL; - } else { - context->mem_pointers[2] = cart + 0x200000/2; - } - } - } - return context; -} - void set_speed_percent(genesis_context * context, uint32_t percent) { uint32_t old_clock = context->master_clock; @@ -894,17 +778,9 @@ psg_adjust_master_clock(context->psg, context->master_clock); } -#define ROM_END 0x1A4 -#define RAM_ID 0x1B0 -#define RAM_FLAGS 0x1B2 -#define RAM_START 0x1B4 -#define RAM_END 0x1B8 #define MAX_MAP_CHUNKS (4+7+1) -#define RAM_FLAG_MASK 0x1800 -const memmap_chunk static_map[] = { - {0, 0x400000, 0xFFFFFF, 0, MMAP_READ, cart, - NULL, NULL, NULL, NULL}, +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, @@ -915,120 +791,49 @@ (read_8_fun)io_read, (write_8_fun)io_write} }; -char * sram_filename; +char * save_filename; genesis_context * genesis; -void save_sram() +void persist_save() { - FILE * f = fopen(sram_filename, "wb"); + FILE * f = fopen(save_filename, "wb"); if (!f) { - fprintf(stderr, "Failed to open SRAM file %s for writing\n", sram_filename); + fprintf(stderr, "Failed to open %s file %s for writing\n", genesis->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename); return; } - uint32_t size = genesis->save_ram_mask+1; - if (genesis->save_flags != RAM_FLAG_BOTH) { - size/= 2; - } - fwrite(genesis->save_ram, 1, size, f); + fwrite(genesis->save_storage, 1, genesis->save_size, f); fclose(f); - printf("Saved SRAM to %s\n", sram_filename); + printf("Saved %s to %s\n", genesis->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename); } -void init_run_cpu(genesis_context * gen, FILE * address_log, char * statefile, uint8_t * debugger) +void init_run_cpu(genesis_context * gen, rom_info *rom, FILE * address_log, char * statefile, uint8_t * debugger) { m68k_options opts; - memmap_chunk memmap[MAX_MAP_CHUNKS]; - uint32_t num_chunks; - void * initial_mapped = NULL; - gen->save_ram = NULL; - //TODO: Handle carts larger than 4MB - //TODO: Handle non-standard mappers - uint32_t size; - if ((cart[RAM_ID/2] & 0xFF) == 'A' && (cart[RAM_ID/2] >> 8) == 'R') { - //Cart has save RAM - uint32_t rom_end = ((cart[ROM_END/2] << 16) | cart[ROM_END/2+1]) + 1; - uint32_t ram_start = (cart[RAM_START/2] << 16) | cart[RAM_START/2+1]; - uint32_t ram_end = (cart[RAM_END/2] << 16) | cart[RAM_END/2+1]; - uint16_t ram_flags = cart[RAM_FLAGS/2]; - gen->save_flags = ram_flags & RAM_FLAG_MASK; - memset(memmap, 0, sizeof(memmap_chunk)*2); - if (ram_start >= rom_end) { - memmap[0].end = rom_end; - memmap[0].mask = 0xFFFFFF; - memmap[0].flags = MMAP_READ; - memmap[0].buffer = cart; - - ram_start &= 0xFFFFFE; - ram_end |= 1; - memmap[1].start = ram_start; - gen->save_ram_mask = memmap[1].mask = ram_end-ram_start; - ram_end += 1; - memmap[1].end = ram_end; - memmap[1].flags = MMAP_READ | MMAP_WRITE; - size = ram_end-ram_start; - if ((ram_flags & RAM_FLAG_MASK) == RAM_FLAG_ODD) { - memmap[1].flags |= MMAP_ONLY_ODD; - size /= 2; - } else if((ram_flags & RAM_FLAG_MASK) == RAM_FLAG_EVEN) { - memmap[1].flags |= MMAP_ONLY_EVEN; - size /= 2; + + gen->save_type = rom->save_type; + if (gen->save_type != SAVE_NONE) { + gen->save_ram_mask = rom->save_mask; + gen->save_size = rom->save_size; + gen->save_storage = rom->save_buffer; + gen->eeprom_map = rom->eeprom_map; + gen->num_eeprom = rom->num_eeprom; + memset(gen->save_storage, 0, rom->save_size); + FILE * f = fopen(save_filename, "rb"); + if (f) { + uint32_t read = fread(gen->save_storage, 1, rom->save_size, f); + fclose(f); + if (read > 0) { + printf("Loaded %s from %s\n", rom->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename); } - memmap[1].buffer = gen->save_ram = malloc(size); - - memcpy(memmap+2, static_map+1, sizeof(static_map)-sizeof(static_map[0])); - num_chunks = sizeof(static_map)/sizeof(memmap_chunk)+1; - } else { - //Assume the standard Sega mapper for now - memmap[0].end = 0x200000; - memmap[0].mask = 0xFFFFFF; - memmap[0].flags = MMAP_READ; - memmap[0].buffer = cart; - - memmap[1].start = 0x200000; - memmap[1].end = 0x400000; - memmap[1].mask = 0x1FFFFF; - ram_start &= 0xFFFFFE; - ram_end |= 1; - gen->save_ram_mask = ram_end-ram_start; - memmap[1].flags = MMAP_READ | MMAP_PTR_IDX | MMAP_FUNC_NULL; - memmap[1].ptr_index = 2; - memmap[1].read_16 = (read_16_fun)read_sram_w;//these will only be called when mem_pointers[2] == NULL - memmap[1].read_8 = (read_8_fun)read_sram_b; - memmap[1].write_16 = (write_16_fun)write_sram_area_w;//these will be called all writes to the area - memmap[1].write_8 = (write_8_fun)write_sram_area_b; - memcpy(memmap+2, static_map+1, sizeof(static_map)-sizeof(static_map[0])); - num_chunks = sizeof(static_map)/sizeof(memmap_chunk)+1; - memset(memmap+num_chunks, 0, sizeof(memmap[num_chunks])); - memmap[num_chunks].start = 0xA13000; - memmap[num_chunks].end = 0xA13100; - memmap[num_chunks].mask = 0xFF; - memmap[num_chunks].write_16 = (write_16_fun)write_bank_reg_w; - memmap[num_chunks].write_8 = (write_8_fun)write_bank_reg_b; - num_chunks++; - ram_end++; - size = ram_end-ram_start; - if ((ram_flags & RAM_FLAG_MASK) != RAM_FLAG_BOTH) { - size /= 2; - } - gen->save_ram = malloc(size); - memmap[1].buffer = initial_mapped = cart + 0x200000/2; + } + atexit(persist_save); + if (gen->save_type == SAVE_I2C) { + eeprom_init(&gen->eeprom, gen->save_storage, gen->save_size); } } else { - memcpy(memmap, static_map, sizeof(static_map)); - num_chunks = sizeof(static_map)/sizeof(memmap_chunk); + gen->save_storage = NULL; } - if (gen->save_ram) { - memset(gen->save_ram, 0, size); - FILE * f = fopen(sram_filename, "rb"); - if (f) { - uint32_t read = fread(gen->save_ram, 1, size, f); - fclose(f); - if (read > 0) { - printf("Loaded SRAM from %s\n", sram_filename); - } - } - atexit(save_sram); - } - init_m68k_opts(&opts, memmap, num_chunks, MCLKS_PER_68K); + + init_m68k_opts(&opts, rom->map, rom->map_chunks, MCLKS_PER_68K); opts.address_log = address_log; m68k_context *context = init_68k_context(&opts); gen->m68k = context; @@ -1041,8 +846,8 @@ //work RAM context->mem_pointers[1] = ram; //save RAM/map - context->mem_pointers[2] = initial_mapped; - context->mem_pointers[3] = (uint16_t *)gen->save_ram; + context->mem_pointers[2] = rom->map[1].buffer; + context->mem_pointers[3] = (uint16_t *)gen->save_storage; if (statefile) { uint32_t pc = load_gst(gen, statefile); @@ -1065,75 +870,36 @@ } } -char title[64]; - -#define TITLE_START 0x150 -#define TITLE_END (TITLE_START+48) +char *title; -void update_title() +void update_title(char *rom_name) { - uint16_t *last = cart + TITLE_END/2 - 1; - while(last > cart + TITLE_START/2 && *last == 0x2020) - { - last--; + if (title) { + free(title); + title = NULL; } - uint16_t *start = cart + TITLE_START/2; - char *cur = title; - char last_char = ' '; - for (; start != last; start++) - { - if ((last_char != ' ' || (*start >> 8) != ' ') && (*start >> 8) < 0x80) { - *(cur++) = *start >> 8; - last_char = *start >> 8; - } - if (last_char != ' ' || (*start & 0xFF) != ' ' && (*start & 0xFF) < 0x80) { - *(cur++) = *start; - last_char = *start & 0xFF; - } - } - *(cur++) = *start >> 8; - if ((*start & 0xFF) != ' ') { - *(cur++) = *start; - } - strcpy(cur, " - BlastEm"); + title = alloc_concat(rom_name, " - BlastEm"); } -#define REGION_START 0x1F0 - -int detect_specific_region(char region) +void set_region(rom_info *info, uint8_t region) { - return (cart[REGION_START/2] & 0xFF) == region || (cart[REGION_START/2] >> 8) == region || (cart[REGION_START/2+1] & 0xFF) == region; + if (!region) { + char * def_region = tern_find_ptr(config, "default_region"); + if (def_region && (!info->regions || (info->regions & translate_region_char(toupper(*def_region))))) { + region = translate_region_char(toupper(*def_region)); + } else { + region = info->regions; + } + } + if (region & REGION_E) { + version_reg = NO_DISK | EUR; + } else if (region & REGION_J) { + version_reg = NO_DISK | JAP; + } else { + version_reg = NO_DISK | USA; + } } -void detect_region() -{ - if (detect_specific_region('U')|| detect_specific_region('B') || detect_specific_region('4')) { - version_reg = NO_DISK | USA; - } else if (detect_specific_region('J')) { - version_reg = NO_DISK | JAP; - } else if (detect_specific_region('E') || detect_specific_region('A')) { - version_reg = NO_DISK | EUR; - } else { - char * def_region = tern_find_ptr(config, "default_region"); - if (def_region) { - switch(*def_region) - { - case 'j': - case 'J': - version_reg = NO_DISK | JAP; - break; - case 'u': - case 'U': - version_reg = NO_DISK | USA; - break; - case 'e': - case 'E': - version_reg = NO_DISK | EUR; - break; - } - } - } -} #ifndef NO_Z80 const memmap_chunk z80_map[] = { { 0x0000, 0x4000, 0x1FFF, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, z80_ram, NULL, NULL, NULL, NULL }, @@ -1161,6 +927,7 @@ char * romfname = NULL; FILE *address_log = NULL; char * statefile = NULL; + int rom_size; uint8_t * debuggerfun = NULL; uint8_t fullscreen = 0, use_gl = 1; for (int i = 1; i < argc; i++) { @@ -1204,21 +971,8 @@ fputs("-r must be followed by region (J, U or E)\n", stderr); return 1; } - switch (argv[i][0]) - { - case 'j': - case 'J': - force_version = NO_DISK | JAP; - break; - case 'u': - case 'U': - force_version = NO_DISK | USA; - break; - case 'e': - case 'E': - force_version = NO_DISK | EUR; - break; - default: + force_version = translate_region_char(toupper(argv[i][0])); + if (!force_version) { fprintf(stderr, "'%c' is not a valid region character for the -r option\n", argv[i][0]); return 1; } @@ -1255,7 +1009,7 @@ return 1; } } else if (!loaded) { - if(!load_rom(argv[i])) { + if (!(rom_size = load_rom(argv[i]))) { fprintf(stderr, "Failed to open %s for reading\n", argv[i]); return 1; } @@ -1271,14 +1025,13 @@ fputs("You must specify a ROM filename!\n", stderr); return 1; } - if (force_version) { - version_reg = force_version; - } else { - detect_region(); - } - update_title(); + 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(); + set_region(&info, force_version); + update_title(info.name); int def_width = 0; - char *config_width = tern_find_ptr(config, "videowidth"); + char *config_width = tern_find_path(config, "video\0width\0").ptrval; if (config_width) { def_width = atoi(config_width); } @@ -1301,7 +1054,7 @@ init_vdp_context(&v_context, version_reg & 0x40); gen.frame_end = vdp_cycles_to_frame_end(&v_context); - char * config_cycles = tern_find_ptr(config, "clocksmax_cycles"); + char * config_cycles = tern_find_path(config, "clocks\0max_cycles\0").ptrval; gen.max_cycles = config_cycles ? atoi(config_cycles) : 10000000; ym2612_context y_context; @@ -1330,20 +1083,22 @@ setup_io_devices(config, gen.ports); int fname_size = strlen(romfname); - sram_filename = malloc(fname_size+6); - memcpy(sram_filename, romfname, fname_size); + char * ext = info.save_type == SAVE_I2C ? "eeprom" : "sram"; + save_filename = malloc(fname_size+strlen(ext) + 2); + memcpy(save_filename, romfname, fname_size); int i; for (i = fname_size-1; fname_size >= 0; --i) { - if (sram_filename[i] == '.') { - strcpy(sram_filename + i + 1, "sram"); + if (save_filename[i] == '.') { + strcpy(save_filename + i + 1, ext); break; } } if (i < 0) { - strcpy(sram_filename + fname_size, ".sram"); + save_filename[fname_size] = '.'; + strcpy(save_filename + fname_size + 1, ext); } set_keybindings(gen.ports); - init_run_cpu(&gen, address_log, statefile, debuggerfun); + init_run_cpu(&gen, &info, address_log, statefile, debuggerfun); return 0; } diff -r 2a25ea5d94f7 -r 13d3744b170e blastem.h --- a/blastem.h Sun Jun 28 19:23:38 2015 -0700 +++ b/blastem.h Sat Jul 18 10:42:15 2015 -0700 @@ -14,10 +14,7 @@ #include "psg.h" #include "io.h" #include "config.h" - -#define RAM_FLAG_ODD 0x1800 -#define RAM_FLAG_EVEN 0x1000 -#define RAM_FLAG_BOTH 0x0000 +#include "romdb.h" typedef struct { m68k_context *m68k; @@ -25,16 +22,20 @@ vdp_context *vdp; ym2612_context *ym; psg_context *psg; - uint8_t *save_ram; + uint8_t *save_storage; + eeprom_map *eeprom_map; + uint32_t num_eeprom; + uint32_t save_size; uint32_t save_ram_mask; - uint32_t save_flags; uint32_t master_clock; //Current master clock value uint32_t normal_clock; //Normal master clock (used to restore master clock after turbo mode) uint32_t frame_end; uint32_t max_cycles; uint8_t bank_regs[8]; + uint8_t save_type; io_port ports[3]; uint8_t bus_busy; + eeprom_state eeprom; } genesis_context; extern genesis_context * genesis; diff -r 2a25ea5d94f7 -r 13d3744b170e config.c --- a/config.c Sun Jun 28 19:23:38 2015 -0700 +++ b/config.c Sat Jul 18 10:42:15 2015 -0700 @@ -9,8 +9,6 @@ #include #include -#define MAX_NEST 30 //way more than I'll ever need - #ifdef _WIN32 char * strtok_r(char * input, char * sep, char ** state) { @@ -32,69 +30,59 @@ } #endif -tern_node * parse_config(char * config_data) +tern_node * parse_config_int(char **state, int started, int *line) { - char *state, *curline; - char *prefix = NULL; - int nest_level = 0; - char * prefix_parts[MAX_NEST]; - int line = 1; + char *config_data, *curline; tern_node * head = NULL; - while ((curline = strtok_r(config_data, "\n", &state))) + config_data = started ? NULL : *state; + while ((curline = strtok_r(config_data, "\n", state))) { + config_data = NULL; curline = strip_ws(curline); int len = strlen(curline); if (!len) { + *line++; continue; } if (curline[0] == '#') { + *line++; continue; } if (curline[0] == '}') { - if (!nest_level) { - fprintf(stderr, "unexpected } on line %d\n", line); - exit(1); + if (started) { + return head; } - if (prefix) { - free(prefix); - prefix = NULL; - } - nest_level--; - curline = strip_ws(curline+1); + fprintf(stderr, "unexpected } on line %d\n", *line); + exit(1); } + char * end = curline + len - 1; if (*end == '{') { *end = 0; curline = strip_ws(curline); - prefix_parts[nest_level++] = curline; - if (prefix) { - free(prefix); - prefix = NULL; - } + *line++; + head = tern_insert_node(head, curline, parse_config_int(state, 1, line)); } else { - if (nest_level && !prefix) { - prefix = alloc_concat_m(nest_level, prefix_parts); - } char * val = strip_ws(split_keyval(curline)); char * key = curline; - if (*key) { - if (prefix) { - key = alloc_concat(prefix, key); - } + if (*val) { head = tern_insert_ptr(head, key, strdup(val)); - if (prefix) { - free(key); - } + } else { + fprintf(stderr, "Key %s is missing a value on line %d\n", key, *line); } + *line++; } } - if (prefix) { - free(prefix); - } return head; } +tern_node * parse_config(char * config_data) +{ + int line = 1; + return parse_config_int(&config_data, 0, &line); +} + tern_node * parse_config_file(char * config_path) { tern_node * ret = NULL; diff -r 2a25ea5d94f7 -r 13d3744b170e config.h --- a/config.h Sun Jun 28 19:23:38 2015 -0700 +++ b/config.h Sat Jul 18 10:42:15 2015 -0700 @@ -7,6 +7,7 @@ #define CONFIG_H_ #include "tern.h" +tern_node * parse_config_file(char * config_path); tern_node * load_config(); #endif //CONFIG_H_ diff -r 2a25ea5d94f7 -r 13d3744b170e io.c --- a/io.c Sun Jun 28 19:23:38 2015 -0700 +++ b/io.c Sat Jul 18 10:42:15 2015 -0700 @@ -248,7 +248,7 @@ { case UI_DEBUG_MODE_INC: ui_debug_mode++; - if (ui_debug_mode == 4) { + if (ui_debug_mode == 7) { ui_debug_mode = 0; } genesis->vdp->debug = ui_debug_mode; @@ -536,7 +536,7 @@ void setup_io_devices(tern_node * config, io_port * ports) { - tern_node *io_nodes = tern_find_prefix(config, "iodevices"); + tern_node *io_nodes = tern_get_node(tern_find_path(config, "io\0devices\0")); char * io_1 = tern_find_ptr(io_nodes, "1"); char * io_2 = tern_find_ptr(io_nodes, "2"); char * io_ext = tern_find_ptr(io_nodes, "ext"); @@ -550,7 +550,7 @@ #ifndef _WIN32 if (ports[i].device_type == IO_SEGA_PARALLEL) { - char *pipe_name = tern_find_ptr(config, "ioparallel_pipe"); + char *pipe_name = tern_find_path(config, "io\0parallel_pipe\0").ptrval; if (!pipe_name) { fprintf(stderr, "IO port %s is configured to use the sega parallel board, but no paralell_pipe is set!\n", io_name(i)); @@ -576,7 +576,7 @@ } } } else if (ports[i].device_type == IO_GENERIC) { - char *sock_name = tern_find_ptr(config, "iosocket"); + char *sock_name = tern_find_path(config, "io\0socket\0").ptrval; if (!sock_name) { fprintf(stderr, "IO port %s is configured to use generic IO, but no socket is set!\n", io_name(i)); @@ -665,64 +665,73 @@ padbuttons = tern_insert_int(padbuttons, ".start", BUTTON_START); padbuttons = tern_insert_int(padbuttons, ".mode", BUTTON_MODE); - tern_node * keys = tern_find_prefix(config, "bindingskeys"); + tern_node * keys = tern_get_node(tern_find_path(config, "bindings\0keys\0")); process_keys(keys, special, padbuttons, NULL); - char prefix[] = "bindingspads00"; - for (int i = 0; i < 100 && i < render_num_joysticks(); i++) - { - if (i < 10) { - prefix[strlen("bindingspads")] = i + '0'; - prefix[strlen("bindingspads")+1] = 0; - } else { - prefix[strlen("bindingspads")] = i/10 + '0'; - prefix[strlen("bindingspads")+1] = i%10 + '0'; - } - tern_node * pad = tern_find_prefix(config, prefix); - if (pad) { - char dprefix[] = "dpads0"; - for (int dpad = 0; dpad < 10 && dpad < render_joystick_num_hats(i); dpad++) - { - dprefix[strlen("dpads")] = dpad + '0'; - tern_node * pad_dpad = tern_find_prefix(pad, dprefix); - char * dirs[] = {"up", "down", "left", "right"}; - int dirnums[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT}; - for (int dir = 0; dir < sizeof(dirs)/sizeof(dirs[0]); dir++) { - char * target = tern_find_ptr(pad_dpad, dirs[dir]); - if (target) { - int ui_func, padnum, button; - int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button); - if (bindtype == 1) { - bind_dpad_gamepad(i, dpad, dirnums[dir], padnum, button); - } else if (bindtype == 2) { - bind_dpad_ui(i, dpad, dirnums[dir], ui_func, button); + char numstr[] = "00"; + tern_node * pads = tern_get_node(tern_find_path(config, "bindings\0pads\0")); + if (pads) { + for (int i = 0; i < 100 && i < render_num_joysticks(); i++) + { + + if (i < 10) { + numstr[0] = i + '0'; + numstr[1] = 0; + } else { + numstr[0] = i/10 + '0'; + numstr[1] = i%10 + '0'; + } + tern_node * pad = tern_find_ptr(pads, numstr); + if (pad) { + tern_node * dpad_node = tern_find_ptr(pad, "dpads"); + if (dpad_node) { + for (int dpad = 0; dpad < 10 && dpad < render_joystick_num_hats(i); dpad++) + { + numstr[0] = dpad + '0'; + numstr[1] = 0; + tern_node * pad_dpad = tern_find_ptr(dpad_node, numstr); + char * dirs[] = {"up", "down", "left", "right"}; + int dirnums[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT}; + for (int dir = 0; dir < sizeof(dirs)/sizeof(dirs[0]); dir++) { + char * target = tern_find_ptr(pad_dpad, dirs[dir]); + if (target) { + int ui_func, padnum, button; + int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button); + if (bindtype == 1) { + bind_dpad_gamepad(i, dpad, dirnums[dir], padnum, button); + } else if (bindtype == 2) { + bind_dpad_ui(i, dpad, dirnums[dir], ui_func, button); + } + } } } } - } - char bprefix[] = "buttons00"; - for (int but = 0; but < 100 && but < render_joystick_num_buttons(i); but++) - { - if (but < 10) { - bprefix[strlen("buttons")] = but + '0'; - bprefix[strlen("buttons")+1] = 0; - } else { - bprefix[strlen("buttons")] = but/10 + '0'; - bprefix[strlen("buttons")+1] = but%10 + '0'; - } - char * target = tern_find_ptr(pad, bprefix); - if (target) { - int ui_func, padnum, button; - int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button); - if (bindtype == 1) { - bind_button_gamepad(i, but, padnum, button); - } else if (bindtype == 2) { - bind_button_ui(i, but, ui_func, button); + tern_node *button_node = tern_find_ptr(pad, "buttons"); + if (button_node) { + for (int but = 0; but < 100 && but < render_joystick_num_buttons(i); but++) + { + if (but < 10) { + numstr[0] = but + '0'; + numstr[1] = 0; + } else { + numstr[0] = but/10 + '0'; + numstr[1] = but%10 + '0'; + } + char * target = tern_find_ptr(button_node, numstr); + if (target) { + int ui_func, padnum, button; + int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button); + if (bindtype == 1) { + bind_button_gamepad(i, but, padnum, button); + } else if (bindtype == 2) { + bind_button_ui(i, but, ui_func, button); + } + } } } } } } - tern_node * speed_nodes = tern_find_prefix(config, "clocksspeeds"); + tern_node * speed_nodes = tern_get_node(tern_find_path(config, "clocks\0speeds\0")); speeds = malloc(sizeof(uint32_t)); speeds[0] = 100; process_speeds(speed_nodes, NULL); diff -r 2a25ea5d94f7 -r 13d3744b170e render_sdl.c --- a/render_sdl.c Sun Jun 28 19:23:38 2015 -0700 +++ b/render_sdl.c Sat Jul 18 10:42:15 2015 -0700 @@ -173,8 +173,10 @@ glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW); - vshader = load_shader(tern_find_ptr_default(config, "videovertex_shader", "default.v.glsl"), GL_VERTEX_SHADER); - fshader = load_shader(tern_find_ptr_default(config, "videofragment_shader", "default.f.glsl"), GL_FRAGMENT_SHADER); + tern_val def = {.ptrval = "default.v.glsl"}; + vshader = load_shader(tern_find_path_default(config, "video\0vertex_shader\0", def).ptrval, GL_VERTEX_SHADER); + def.ptrval = "default.f.glsl"; + fshader = load_shader(tern_find_path_default(config, "video\0fragment_shader\0", def).ptrval, GL_FRAGMENT_SHADER); program = glCreateProgram(); glAttachShader(program, vshader); glAttachShader(program, fshader); @@ -239,7 +241,8 @@ exit(1); } float aspect = (float)width / height; - if (fabs(aspect - 4.0/3.0) > 0.01 && strcmp(tern_find_ptr_default(config, "videoaspect", "normal"), "stretch")) { + tern_val def = {.ptrval = "normal"}; + if (fabs(aspect - 4.0/3.0) > 0.01 && strcmp(tern_find_path_default(config, "video\0aspect\0", def).ptrval, "stretch")) { for (int i = 0; i < 4; i++) { if (aspect > 4.0/3.0) { @@ -272,7 +275,7 @@ audio_ready = SDL_CreateCond(); SDL_AudioSpec desired, actual; - char * rate_str = tern_find_ptr(config, "audiorate"); + char * rate_str = tern_find_path(config, "audio\0rate\0").ptrval; int rate = rate_str ? atoi(rate_str) : 0; if (!rate) { rate = 48000; @@ -280,7 +283,7 @@ desired.freq = rate; desired.format = AUDIO_S16SYS; desired.channels = 2; - char * samples_str = tern_find_ptr(config, "audiobuffer"); + char * samples_str = tern_find_path(config, "audio\0buffer\0").ptrval; int samples = samples_str ? atoi(samples_str) : 0; if (!samples) { samples = 512; diff -r 2a25ea5d94f7 -r 13d3744b170e rom.db --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rom.db Sat Jul 18 10:42:15 2015 -0700 @@ -0,0 +1,168 @@ +T-081326 { + name NBA Jam + EEPROM { + type i2c + size 256 + } + map { + 0 { + device ROM + last 1FFFFF + } + 200000 { + device EEPROM + last 3FFFFF + bits_read { + 1 sda + } + bits_write { + 0 sda + 1 scl + } + } + } +} +T-81033 { + name NBA Jam + EEPROM { + type i2c + size 256 + } + map { + 0 { + device ROM + last 1FFFFF + } + 200000 { + device EEPROM + last 3FFFFF + bits_read { + 1 sda + } + bits_write { + 0 sda + 1 scl + } + } + } +} +T-081276 { + name NFL Quarterback Club + EEPROM { + type i2c + size 256 + } + map { + 0 { + device ROM + last 1FFFFF + } + 200000 { + device EEPROM + last 3FFFFF + bits_read { + 0 sda + } + bits_write { + 0 sda + 8 scl + } + } + } +} +T-81406 { + name NBA Jam TE + EEPROM { + type i2c + size 512 + } + map { + 0 { + device ROM + last 1FFFFF + } + 200000 { + device EEPROM + last 3FFFFF + bits_read { + 0 sda + } + bits_write { + 0 sda + 8 scl + } + } + } +} +T-081586 { + name NFL Quarterback Club '96 + EEPROM { + type i2c + size 2048 + } + map { + 0 { + device ROM + last 1FFFFF + } + 200000 { + device EEPROM + last 3FFFFF + bits_read { + 0 sda + } + bits_write { + 0 sda + 8 scl + } + } + } +} +T-81576 { + name College Slam + EEPROM { + type i2c + size 8192 + } + map { + 0 { + device ROM + last 1FFFFF + } + 200000 { + device EEPROM + last 3FFFFF + bits_read { + 0 sda + } + bits_write { + 0 sda + 8 scl + } + } + } +} +T-81476 { + name Frank Thomas Big Hurt Baseball + EEPROM { + type i2c + size 8192 + } + map { + 0 { + device ROM + last 1FFFFF + } + 200000 { + device EEPROM + last 3FFFFF + bits_read { + 0 sda + } + bits_write { + 0 sda + 8 scl + } + } + } +} \ No newline at end of file diff -r 2a25ea5d94f7 -r 13d3744b170e romdb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/romdb.c Sat Jul 18 10:42:15 2015 -0700 @@ -0,0 +1,780 @@ +#include +#include +#include "config.h" +#include "romdb.h" +#include "util.h" +#include "blastem.h" + +#define TITLE_START 0x150 +#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 +#define RAM_START 0x1B4 +#define RAM_END 0x1B8 +#define REGION_START 0x1F0 + +enum { + I2C_IDLE, + I2C_START, + I2C_DEVICE_ACK, + I2C_ADDRESS_HI, + I2C_ADDRESS_HI_ACK, + I2C_ADDRESS, + I2C_ADDRESS_ACK, + I2C_READ, + I2C_READ_ACK, + I2C_WRITE, + I2C_WRITE_ACK +}; + +char * i2c_states[] = { + "idle", + "start", + "device ack", + "address hi", + "address hi ack", + "address", + "address ack", + "read", + "read_ack", + "write", + "write_ack" +}; + +void eeprom_init(eeprom_state *state, uint8_t *buffer, uint32_t size) +{ + state->slave_sda = 1; + state->host_sda = state->scl = 0; + state->buffer = buffer; + state->size = size; + state->state = I2C_IDLE; +} + +void set_host_sda(eeprom_state *state, uint8_t val) +{ + if (state->scl) { + if (val & ~state->host_sda) { + //low to high, stop condition + state->state = I2C_IDLE; + state->slave_sda = 1; + } else if (~val & state->host_sda) { + //high to low, start condition + state->state = I2C_START; + state->slave_sda = 1; + state->counter = 8; + } + } + state->host_sda = val; +} + +void set_scl(eeprom_state *state, uint8_t val) +{ + if (val & ~state->scl) { + //low to high transition + switch (state->state) + { + case I2C_START: + case I2C_ADDRESS_HI: + case I2C_ADDRESS: + case I2C_WRITE: + state->latch = state->host_sda | state->latch << 1; + state->counter--; + if (!state->counter) { + switch (state->state & 0x7F) + { + case I2C_START: + state->state = I2C_DEVICE_ACK; + break; + case I2C_ADDRESS_HI: + state->address = state->latch << 8; + state->state = I2C_ADDRESS_HI_ACK; + break; + case I2C_ADDRESS: + state->address |= state->latch; + state->state = I2C_ADDRESS_ACK; + break; + case I2C_WRITE: + state->buffer[state->address] = state->latch; + state->state = I2C_WRITE_ACK; + state->address++; + //TODO: page mask + state->address &= state->size-1; + break; + } + } + break; + case I2C_DEVICE_ACK: + if (state->latch & 1) { + state->state = I2C_READ; + state->counter = 8; + state->latch = state->buffer[state->address]; + state->address++; + //TODO: page mask + state->address &= state->size-1; + } else { + if (state->size < 256) { + state->address = state->latch >> 1; + state->state = I2C_WRITE; + } else if (state->size < 8192) { + state->address = state->latch << 8; + state->state = I2C_ADDRESS; + } else { + state->state = I2C_ADDRESS_HI; + } + state->counter = 8; + } + break; + case I2C_ADDRESS_HI_ACK: + state->state = I2C_ADDRESS; + state->counter = 8; + break; + case I2C_ADDRESS_ACK: + state->state = I2C_WRITE; + state->counter = 8; + break; + case I2C_READ: + state->counter--; + if (!state->counter) { + state->state = I2C_READ_ACK; + } + break; + case I2C_READ_ACK: + state->state = I2C_READ; + state->counter = 8; + break; + case I2C_WRITE_ACK: + state->state = I2C_WRITE; + state->counter = 8; + break; + } + } else if (~val & state->scl) { + //high to low transition + switch (state->state & 0x7F) + { + case I2C_DEVICE_ACK: + case I2C_ADDRESS_HI_ACK: + case I2C_ADDRESS_ACK: + case I2C_READ_ACK: + case I2C_WRITE_ACK: + state->slave_sda = 0; + break; + case I2C_READ: + state->slave_sda = state->latch >> 7; + state->latch = state->latch << 1; + break; + default: + state->slave_sda = 1; + break; + } + } + state->scl = val; +} + +uint8_t get_sda(eeprom_state *state) +{ + return state->host_sda & state->slave_sda; +} + +uint16_t read_sram_w(uint32_t address, m68k_context * context) +{ + genesis_context * gen = context->system; + address &= gen->save_ram_mask; + switch(gen->save_type) + { + case RAM_FLAG_BOTH: + return gen->save_storage[address] << 8 | gen->save_storage[address+1]; + case RAM_FLAG_EVEN: + return gen->save_storage[address >> 1] << 8 | 0xFF; + case RAM_FLAG_ODD: + return gen->save_storage[address >> 1] | 0xFF00; + } + return 0xFFFF;//We should never get here +} + +uint8_t read_sram_b(uint32_t address, m68k_context * context) +{ + genesis_context * gen = context->system; + address &= gen->save_ram_mask; + switch(gen->save_type) + { + case RAM_FLAG_BOTH: + return gen->save_storage[address]; + case RAM_FLAG_EVEN: + if (address & 1) { + return 0xFF; + } else { + return gen->save_storage[address >> 1]; + } + case RAM_FLAG_ODD: + if (address & 1) { + return gen->save_storage[address >> 1]; + } else { + return 0xFF; + } + } + return 0xFF;//We should never get here +} + +m68k_context * write_sram_area_w(uint32_t address, m68k_context * context, uint16_t value) +{ + genesis_context * gen = context->system; + if ((gen->bank_regs[0] & 0x3) == 1) { + address &= gen->save_ram_mask; + switch(gen->save_type) + { + case RAM_FLAG_BOTH: + gen->save_storage[address] = value >> 8; + gen->save_storage[address+1] = value; + break; + case RAM_FLAG_EVEN: + gen->save_storage[address >> 1] = value >> 8; + break; + case RAM_FLAG_ODD: + gen->save_storage[address >> 1] = value; + break; + } + } + return context; +} + +m68k_context * write_sram_area_b(uint32_t address, m68k_context * context, uint8_t value) +{ + genesis_context * gen = context->system; + if ((gen->bank_regs[0] & 0x3) == 1) { + address &= gen->save_ram_mask; + switch(gen->save_type) + { + case RAM_FLAG_BOTH: + gen->save_storage[address] = value; + break; + case RAM_FLAG_EVEN: + if (!(address & 1)) { + gen->save_storage[address >> 1] = value; + } + break; + case RAM_FLAG_ODD: + if (address & 1) { + gen->save_storage[address >> 1] = value; + } + break; + } + } + return context; +} + +m68k_context * write_bank_reg_w(uint32_t address, m68k_context * context, uint16_t value) +{ + genesis_context * gen = context->system; + address &= 0xE; + address >>= 1; + gen->bank_regs[address] = value; + if (!address) { + if (value & 1) { + context->mem_pointers[2] = NULL; + } else { + context->mem_pointers[2] = cart + 0x200000/2; + } + } + return context; +} + +m68k_context * write_bank_reg_b(uint32_t address, m68k_context * context, uint8_t value) +{ + if (address & 1) { + genesis_context * gen = context->system; + address &= 0xE; + address >>= 1; + gen->bank_regs[address] = value; + if (!address) { + if (value & 1) { + context->mem_pointers[2] = NULL; + } else { + context->mem_pointers[2] = cart + 0x200000/2; + } + } + } + return context; +} +eeprom_map *find_eeprom_map(uint32_t address, genesis_context *gen) +{ + for (int i = 0; i < gen->num_eeprom; i++) + { + if (address >= gen->eeprom_map[i].start && address <= gen->eeprom_map[i].end) { + return gen->eeprom_map + i; + } + } + return NULL; +} + +void * write_eeprom_i2c_w(uint32_t address, void * context, uint16_t value) +{ + genesis_context *gen = ((m68k_context *)context)->system; + eeprom_map *map = find_eeprom_map(address, gen); + if (!map) { + fprintf(stderr, "Could not find EEPROM map for address %X\n", address); + exit(1); + } + printf("EEPROM word write: %X - %X\n", address, value); + if (map->scl_mask) { + set_scl(&gen->eeprom, (value & map->scl_mask) != 0); + printf("scl: %d, state: %s, counter: %d, latch: %X\n", (value & map->scl_mask) != 0, i2c_states[gen->eeprom.state], gen->eeprom.counter, gen->eeprom.latch); + } + if (map->sda_write_mask) { + printf("sda: %d\n", (value & map->sda_write_mask) != 0); + set_host_sda(&gen->eeprom, (value & map->sda_write_mask) != 0); + } + return context; +} + +void * write_eeprom_i2c_b(uint32_t address, void * context, uint8_t value) +{ + genesis_context *gen = ((m68k_context *)context)->system; + eeprom_map *map = find_eeprom_map(address, gen); + if (!map) { + fprintf(stderr, "Could not find EEPROM map for address %X\n", address); + exit(1); + } + + uint16_t expanded, mask; + if (address & 1) { + expanded = value; + mask = 0xFF; + } else { + expanded = value << 8; + mask = 0xFF00; + } + printf("EEPROM byte write: %X - %X (using mask %X and expanded val %X)\n", address, value, mask, expanded); + if (map->scl_mask & mask) { + printf("scl: %d, state: %s\n", (expanded & map->scl_mask) != 0, i2c_states[gen->eeprom.state]); + set_scl(&gen->eeprom, (expanded & map->scl_mask) != 0); + } + if (map->sda_write_mask & mask) { + printf("sda: %d\n", (expanded & map->sda_write_mask) != 0); + set_host_sda(&gen->eeprom, (expanded & map->sda_write_mask) != 0); + } + return context; +} + +uint16_t read_eeprom_i2c_w(uint32_t address, void * context) +{ + genesis_context *gen = ((m68k_context *)context)->system; + eeprom_map *map = find_eeprom_map(address, gen); + if (!map) { + fprintf(stderr, "Could not find EEPROM map for address %X\n", address); + exit(1); + } + uint16_t ret = 0; + if (map->sda_read_bit < 16) { + ret = get_sda(&gen->eeprom) << map->sda_read_bit; + } + printf("EEPROM word read: %X - %X\n", address, ret); + return ret; +} + +uint8_t read_eeprom_i2c_b(uint32_t address, void * context) +{ + genesis_context *gen = ((m68k_context *)context)->system; + eeprom_map *map = find_eeprom_map(address, gen); + if (!map) { + fprintf(stderr, "Could not find EEPROM map for address %X\n", address); + exit(1); + } + uint8_t bit = address & 1 ? map->sda_read_bit : map->sda_read_bit - 8; + uint8_t ret = 0; + if (bit < 8) { + ret = get_sda(&gen->eeprom) << bit; + } + printf("EEPROM byte read: %X - %X\n", address, ret); + return ret; +} + +tern_node *load_rom_db() +{ + char *exe_dir = get_exe_dir(); + if (!exe_dir) { + fputs("Failed to find executable path\n", stderr); + exit(1); + } + char *path = alloc_concat(exe_dir, "/rom.db"); + tern_node *db = parse_config_file(path); + free(path); + if (!db) { + fputs("Failed to load ROM DB\n", stderr); + } + return db; +} + +char *get_header_name(uint8_t *rom) +{ + uint8_t *last = rom + TITLE_END - 1; + uint8_t *src = rom + TITLE_START; + + while (last > src && (*last <= 0x20 || *last >= 0x80)) + { + last--; + } + if (last == src) { + //TODO: Use other name field + return strdup("UNKNOWN"); + } else { + last++; + char *ret = malloc(last - (rom + TITLE_START) + 1); + uint8_t *dst; + for (dst = ret; src < last; src++) + { + if (*src >= 0x20 && *src < 0x80) { + *(dst++) = *src; + } + } + *dst = 0; + return ret; + } +} + +char *region_chars = "UB4JEA"; +uint8_t region_bits[] = {REGION_U, REGION_U, REGION_U, REGION_J, REGION_E, REGION_E}; + +uint8_t translate_region_char(uint8_t c) +{ + for (int i = 0; i < sizeof(region_bits); i++) + { + if (c == region_chars[i]) { + return region_bits[i]; + } + } + return 0; +} + +uint8_t get_header_regions(uint8_t *rom) +{ + uint8_t regions = 0; + for (int i = 0; i < 3; i++) + { + regions |= translate_region_char(rom[REGION_START + i]); + } + return regions; +} + +uint32_t get_u32be(uint8_t *data) +{ + return *data << 24 | data[1] << 16 | data[2] << 8 | data[3]; +} + +uint32_t calc_mask(uint32_t src_size, uint32_t start, uint32_t end) +{ + uint32_t map_size = end-start+1; + if (src_size < map_size) { + return nearest_pow2(src_size)-1; + } else if (!start) { + return 0xFFFFFF; + } else { + return nearest_pow2(map_size)-1; + } +} + +void add_memmap_header(rom_info *info, uint8_t *rom, uint32_t size, memmap_chunk const *base_map, int base_chunks) +{ + if (rom[RAM_ID] == 'R' && rom[RAM_ID+1] == 'A') { + uint32_t rom_end = get_u32be(rom + ROM_END) + 1; + uint32_t ram_start = get_u32be(rom + RAM_START); + uint32_t ram_end = get_u32be(rom + RAM_END); + uint32_t ram_flags = info->save_type = rom[RAM_FLAGS] & RAM_FLAG_MASK; + ram_start &= 0xFFFFFE; + ram_end |= 1; + info->save_mask = ram_end - ram_start; + uint32_t size = info->save_mask + 1; + if (ram_flags != RAM_FLAG_BOTH) { + size /= 2; + } + info->save_size = size; + info->save_buffer = malloc(size); + + info->map_chunks = base_chunks + (ram_start >= rom_end ? 2 : 3); + info->map = malloc(sizeof(memmap_chunk) * info->map_chunks); + memset(info->map, 0, sizeof(memmap_chunk)*2); + memcpy(info->map+2, base_map, sizeof(memmap_chunk) * base_chunks); + + if (ram_start >= rom_end) { + info->map[0].end = rom_end; + //TODO: ROM mirroring + info->map[0].mask = 0xFFFFFF; + info->map[0].flags = MMAP_READ; + info->map[0].buffer = rom; + + info->map[1].start = ram_start; + info->map[1].mask = info->save_mask; + info->map[1].end = ram_end + 1; + info->map[1].flags = MMAP_READ | MMAP_WRITE; + + if (ram_flags == RAM_FLAG_ODD) { + info->map[1].flags |= MMAP_ONLY_ODD; + } else if (ram_flags == RAM_FLAG_EVEN) { + info->map[1].flags |= MMAP_ONLY_EVEN; + } + info->map[1].buffer = info->save_buffer; + } else { + //Assume the standard Sega mapper + info->map[0].end = 0x200000; + info->map[0].mask = 0xFFFFFF; + info->map[0].flags = MMAP_READ; + info->map[0].buffer = rom; + + info->map[1].start = 0x200000; + info->map[1].end = 0x400000; + info->map[1].mask = 0x1FFFFF; + info->map[1].flags = MMAP_READ | MMAP_PTR_IDX | MMAP_FUNC_NULL; + info->map[1].ptr_index = 2; + info->map[1].read_16 = (read_16_fun)read_sram_w;//these will only be called when mem_pointers[2] == NULL + info->map[1].read_8 = (read_8_fun)read_sram_b; + info->map[1].write_16 = (write_16_fun)write_sram_area_w;//these will be called all writes to the area + info->map[1].write_8 = (write_8_fun)write_sram_area_b; + info->map[1].buffer = cart + 0x200000; + + memmap_chunk *last = info->map + info->map_chunks - 1; + memset(last, 0, sizeof(memmap_chunk)); + last->start = 0xA13000; + last->end = 0xA13100; + last->mask = 0xFF; + last->write_16 = (write_16_fun)write_bank_reg_w; + last->write_8 = (write_8_fun)write_bank_reg_b; + } + } else { + info->map_chunks = base_chunks + 1; + info->map = malloc(sizeof(memmap_chunk) * info->map_chunks); + memset(info->map, 0, sizeof(memmap_chunk)); + memcpy(info->map+1, base_map, sizeof(memmap_chunk) * base_chunks); + + info->map[0].end = 0x400000; + info->map[0].mask = 0xFFFFFF; + info->map[0].flags = MMAP_READ; + info->map[0].buffer = rom; + info->save_type = SAVE_NONE; + } +} + +rom_info configure_rom_heuristics(uint8_t *rom, uint32_t rom_size, memmap_chunk const *base_map, uint32_t base_chunks) +{ + rom_info info; + info.name = get_header_name(rom); + info.regions = get_header_regions(rom); + add_memmap_header(&info, rom, rom_size, base_map, base_chunks); + return info; +} + +typedef struct { + rom_info *info; + uint8_t *rom; + tern_node *root; + uint32_t rom_size; + int index; + int num_els; +} map_iter_state; + +void eeprom_read_fun(char *key, tern_val val, void *data) +{ + int bit = atoi(key); + if (bit < 0 || bit > 15) { + fprintf(stderr, "bit %s is out of range", key); + return; + } + char *pin = val.ptrval; + if (strcmp(pin, "sda")) { + fprintf(stderr, "bit %s is connected to unrecognized read pin %s", key, pin); + return; + } + eeprom_map *map = data; + map->sda_read_bit = bit; +} + +void eeprom_write_fun(char *key, tern_val val, void *data) +{ + int bit = atoi(key); + if (bit < 0 || bit > 15) { + fprintf(stderr, "bit %s is out of range", key); + return; + } + char *pin = val.ptrval; + eeprom_map *map = data; + if (!strcmp(pin, "sda")) { + map->sda_write_mask = 1 << bit; + return; + } + if (!strcmp(pin, "scl")) { + map->scl_mask = 1 << bit; + return; + } + fprintf(stderr, "bit %s is connected to unrecognized write pin %s", key, pin); +} + +void map_iter_fun(char *key, tern_val val, void *data) +{ + map_iter_state *state = data; + tern_node *node = tern_get_node(val); + if (!node) { + fprintf(stderr, "ROM DB map entry %d with address %s is not a node\n", state->index, key); + exit(1); + } + uint32_t start = strtol(key, NULL, 16); + uint32_t end = strtol(tern_find_ptr_default(node, "last", "0"), NULL, 16); + if (!end || end < start) { + fprintf(stderr, "'last' value is missing or invalid for ROM DB map entry %d with address %s\n", state->index, key); + exit(1); + } + char * dtype = tern_find_ptr_default(node, "device", "ROM"); + uint32_t offset = strtol(tern_find_ptr_default(node, "offset", "0"), NULL, 0); + memmap_chunk *map = state->info->map + state->index; + map->start = start; + map->end = end; + if (!strcmp(dtype, "ROM")) { + map->buffer = state->rom + offset; + map->flags = MMAP_READ; + map->mask = calc_mask(state->rom_size, start, end); + } else if (!strcmp(dtype, "EEPROM")) { + if (!state->info->save_size) { + char * size = tern_find_path(state->root, "EEPROM\0size\0").ptrval; + if (!size) { + fprintf(stderr, "ROM DB map entry %d with address %s has device type EEPROM, but the EEPROM size is not defined\n", state->index, key); + exit(1); + } + state->info->save_size = atoi(size); + if (!state->info->save_size) { + fprintf(stderr, "EEPROM size %s is invalid\n", size); + exit(1); + } + char *etype = tern_find_path(state->root, "EEPROM\0type\0").ptrval; + if (!etype) { + etype = "i2c"; + } + if (!strcmp(etype, "i2c")) { + state->info->save_type = SAVE_I2C; + } else { + fprintf(stderr, "EEPROM type %s is invalid\n", etype); + exit(1); + } + state->info->save_buffer = malloc(state->info->save_size); + state->info->eeprom_map = malloc(sizeof(eeprom_map) * state->num_els); + memset(state->info->eeprom_map, 0, sizeof(eeprom_map) * state->num_els); + } + eeprom_map *eep_map = state->info->eeprom_map + state->info->num_eeprom; + eep_map->start = start; + eep_map->end = end; + eep_map->sda_read_bit = 0xFF; + tern_node * bits_read = tern_find_ptr(node, "bits_read"); + if (bits_read) { + tern_foreach(bits_read, eeprom_read_fun, eep_map); + } + tern_node * bits_write = tern_find_ptr(node, "bits_write"); + if (bits_write) { + tern_foreach(bits_write, eeprom_write_fun, eep_map); + } + printf("EEPROM address %X: sda read: %X, sda write: %X, scl: %X\n", start, eep_map->sda_read_bit, eep_map->sda_write_mask, eep_map->scl_mask); + state->info->num_eeprom++; + map->write_16 = write_eeprom_i2c_w; + map->write_8 = write_eeprom_i2c_b; + map->read_16 = read_eeprom_i2c_w; + map->read_8 = read_eeprom_i2c_b; + map->mask = 0xFFFFFF; + } else if (!strcmp(dtype, "SRAM")) { + if (!state->info->save_size) { + char * size = tern_find_path(state->root, "SRAM\0size\0").ptrval; + if (!size) { + fprintf(stderr, "ROM DB map entry %d with address %s has device type SRAM, but the SRAM size is not defined\n", state->index, key); + exit(1); + } + state->info->save_size = atoi(size); + if (!state->info->save_size) { + fprintf(stderr, "SRAM size %s is invalid\n", size); + exit(1); + } + state->info->save_buffer = malloc(state->info->save_size); + char *bus = tern_find_path(state->root, "SRAM\0bus\0").ptrval; + if (!strcmp(bus, "odd")) { + state->info->save_type = RAM_FLAG_ODD; + } else if(!strcmp(bus, "even")) { + state->info->save_type = RAM_FLAG_EVEN; + } else { + state->info->save_type = RAM_FLAG_BOTH; + } + } + map->buffer = state->info->save_buffer + offset; + map->flags = MMAP_READ | MMAP_WRITE; + if (state->info->save_type == RAM_FLAG_ODD) { + map->flags |= MMAP_ONLY_ODD; + } else if(state->info->save_type == RAM_FLAG_EVEN) { + map->flags |= MMAP_ONLY_EVEN; + } + map->mask = calc_mask(state->info->save_size, start, end); + } else { + fprintf(stderr, "Invalid device type for ROM DB map entry %d with address %s\n", state->index, key); + exit(1); + } + state->index++; +} + +rom_info configure_rom(tern_node *rom_db, void *vrom, uint32_t rom_size, memmap_chunk const *base_map, uint32_t base_chunks) +{ + uint8_t product_id[GAME_ID_LEN+1]; + uint8_t *rom = vrom; + product_id[GAME_ID_LEN] = 0; + for (int i = 0; i < GAME_ID_LEN; i++) + { + if (rom[GAME_ID_OFF + i] <= ' ') { + product_id[i] = 0; + break; + } + product_id[i] = rom[GAME_ID_OFF + i]; + + } + printf("Product ID: %s\n", product_id); + tern_node * entry = tern_find_ptr(rom_db, product_id); + if (!entry) { + puts("Not found in ROM DB, examining header\n"); + return configure_rom_heuristics(rom, rom_size, base_map, base_chunks); + } + rom_info info; + info.name = tern_find_ptr(entry, "name"); + if (info.name) { + printf("Found name: %s\n", info.name); + info.name = strdup(info.name); + } else { + info.name = get_header_name(rom); + } + + char *dbreg = tern_find_ptr(entry, "regions"); + info.regions = 0; + if (dbreg) { + while (*dbreg != 0) + { + info.regions |= translate_region_char(*(dbreg++)); + } + } + if (!info.regions) { + info.regions = get_header_regions(rom); + } + + tern_node *map = tern_find_ptr(entry, "map"); + if (map) { + info.map_chunks = tern_count(map); + if (info.map_chunks) { + info.map_chunks += base_chunks; + info.save_buffer = NULL; + info.save_size = 0; + info.map = malloc(sizeof(memmap_chunk) * info.map_chunks); + info.eeprom_map = NULL; + info.num_eeprom = 0; + memset(info.map, 0, sizeof(memmap_chunk) * (info.map_chunks - base_chunks)); + map_iter_state state = {&info, rom, entry, rom_size, 0, info.map_chunks - base_chunks}; + tern_foreach(map, map_iter_fun, &state); + memcpy(info.map + state.index, base_map, sizeof(memmap_chunk) * base_chunks); + } else { + add_memmap_header(&info, rom, rom_size, base_map, base_chunks); + } + } else { + add_memmap_header(&info, rom, rom_size, base_map, base_chunks); + } + + return info; +} diff -r 2a25ea5d94f7 -r 13d3744b170e romdb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/romdb.h Sat Jul 18 10:42:15 2015 -0700 @@ -0,0 +1,57 @@ +#ifndef ROMDB_H_ +#define ROMDB_H_ + +#define REGION_J 1 +#define REGION_U 2 +#define REGION_E 4 + +#define RAM_FLAG_ODD 0x18 +#define RAM_FLAG_EVEN 0x10 +#define RAM_FLAG_BOTH 0x00 +#define RAM_FLAG_MASK RAM_FLAG_ODD +#define SAVE_I2C 0x01 +#define SAVE_NONE 0xFF + +#include "tern.h" +#include "backend.h" + +typedef struct { + uint32_t start; + uint32_t end; + uint16_t sda_write_mask; + uint16_t scl_mask; + uint8_t sda_read_bit; +} eeprom_map; + +typedef struct { + char *buffer; + uint32_t size; + uint16_t address; + uint8_t host_sda; + uint8_t slave_sda; + uint8_t scl; + uint8_t state; + uint8_t counter; + uint8_t latch; +} eeprom_state; + +typedef struct { + char *name; + memmap_chunk *map; + uint8_t *save_buffer; + eeprom_map *eeprom_map; + uint32_t num_eeprom; + uint32_t map_chunks; + uint32_t save_size; + uint32_t save_mask; + uint8_t save_type; + uint8_t regions; +} rom_info; + +tern_node *load_rom_db(); +rom_info configure_rom(tern_node *rom_db, void *vrom, uint32_t rom_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); +uint8_t translate_region_char(uint8_t c); +void eeprom_init(eeprom_state *state, uint8_t *buffer, uint32_t size); + +#endif //ROMDB_H_ diff -r 2a25ea5d94f7 -r 13d3744b170e tern.c --- a/tern.c Sun Jun 28 19:23:38 2015 -0700 +++ b/tern.c Sat Jul 18 10:42:15 2015 -0700 @@ -6,6 +6,8 @@ #include "tern.h" #include #include +#include +#include tern_node * tern_insert(tern_node * head, char * key, tern_val value) { @@ -105,7 +107,11 @@ { tern_val ret; if (tern_find(head, key, &ret)) { - return ret.ptrval; + if (ret.intval & 1) { + return (void *)(ret.intval & ~1); + } else { + return ret.ptrval; + } } return def; } @@ -115,6 +121,32 @@ return tern_find_ptr_default(head, key, NULL); } +tern_val tern_find_path_default(tern_node *head, char *key, tern_val def) +{ + tern_val ret; + while (*key) + { + if (!tern_find(head, key, &ret)) { + return def; + } + key = key + strlen(key) + 1; + if (*key) { + head = tern_get_node(ret); + if (!head) { + return def; + } + } + } + return ret; +} + +tern_val tern_find_path(tern_node *head, char *key) +{ + tern_val def; + def.ptrval = NULL; + return tern_find_path_default(head, key, def); +} + tern_node * tern_insert_ptr(tern_node * head, char * key, void * value) { tern_val val; @@ -122,6 +154,60 @@ return tern_insert(head, key, val); } +tern_node * tern_insert_node(tern_node *head, char *key, tern_node *value) +{ + tern_val val; + val.intval = ((intptr_t)value) | 1; + return tern_insert(head, key, val); +} + +uint32_t tern_count(tern_node *head) +{ + uint32_t count = 0; + if (head->left) { + count += tern_count(head->left); + } + if (head->right) { + count += tern_count(head->right); + } + if (!head->el) { + count++; + } else if (head->straight.next) { + count += tern_count(head->straight.next); + } + return count; +} + +#define MAX_ITER_KEY 127 +void tern_foreach_int(tern_node *head, iter_fun fun, void *data, char *keybuf, int pos) +{ + if (!head->el) { + keybuf[pos] = 0; + fun(keybuf, head->straight.value, data); + } + if (head->left) { + tern_foreach_int(head->left, fun, data, keybuf, pos); + } + if (head->el) { + if (pos == MAX_ITER_KEY) { + fputs("exceeded maximum key size", stderr); + exit(1); + } + keybuf[pos] = head->el; + tern_foreach_int(head->straight.next, fun, data, keybuf, pos+1); + } + if (head->right) { + tern_foreach_int(head->right, fun, data, keybuf, pos); + } +} + +void tern_foreach(tern_node *head, iter_fun fun, void *data) +{ + //lame, but good enough for my purposes + char key[MAX_ITER_KEY+1]; + tern_foreach_int(head, fun, data, key, 0); +} + char * tern_int_key(uint32_t key, char * buf) { char * cur = buf; @@ -133,3 +219,8 @@ *cur = 0; return buf; } + +tern_node * tern_get_node(tern_val value) +{ + return value.intval & 1 ? (tern_node *)(value.intval & ~1) : NULL; +} diff -r 2a25ea5d94f7 -r 13d3744b170e tern.h --- a/tern.h Sun Jun 28 19:23:38 2015 -0700 +++ b/tern.h Sat Jul 18 10:42:15 2015 -0700 @@ -25,6 +25,8 @@ char el; } tern_node; +typedef void (*iter_fun)(char *key, tern_val val, void *data); + tern_node * tern_insert(tern_node * head, char * key, tern_val value); int tern_find(tern_node * head, char * key, tern_val *ret); tern_node * tern_find_prefix(tern_node * head, char * key); @@ -32,7 +34,13 @@ tern_node * tern_insert_int(tern_node * head, char * key, intptr_t value); void * tern_find_ptr_default(tern_node * head, char * key, void * def); void * tern_find_ptr(tern_node * head, char * key); +tern_val tern_find_path_default(tern_node *head, char *key, tern_val def); +tern_val tern_find_path(tern_node *head, char *key); tern_node * tern_insert_ptr(tern_node * head, char * key, void * value); +tern_node * tern_insert_node(tern_node *head, char *key, tern_node *value); +uint32_t tern_count(tern_node *head); +void tern_foreach(tern_node *head, iter_fun fun, void *data); char * tern_int_key(uint32_t key, char * buf); +tern_node * tern_get_node(tern_val value); #endif //TERN_H_ diff -r 2a25ea5d94f7 -r 13d3744b170e util.c --- a/util.c Sun Jun 28 19:23:38 2015 -0700 +++ b/util.c Sat Jul 18 10:42:15 2015 -0700 @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -68,6 +69,16 @@ return text+1; } +uint32_t nearest_pow2(uint32_t val) +{ + uint32_t ret = 1; + while (ret < val) + { + ret = ret << 1; + } + return ret; +} + static char * exe_str; void set_exe_str(char * str) diff -r 2a25ea5d94f7 -r 13d3744b170e util.h --- a/util.h Sun Jun 28 19:23:38 2015 -0700 +++ b/util.h Sat Jul 18 10:42:15 2015 -0700 @@ -15,6 +15,8 @@ char * strip_ws(char * text); //Inserts a null after the first word, returns a pointer to the second word char * split_keyval(char * text); +//Gets the smallest power of two that is >= a certain value, won't work for values > 0x80000000 +uint32_t nearest_pow2(uint32_t val); //Should be called by main with the value of argv[0] for use by get_exe_dir void set_exe_str(char * str); //Returns the directory the executable is in diff -r 2a25ea5d94f7 -r 13d3744b170e vdp.c --- a/vdp.c Sun Jun 28 19:23:38 2015 -0700 +++ b/vdp.c Sat Jul 18 10:42:15 2015 -0700 @@ -910,15 +910,16 @@ } } } else { - uint32_t cell = (line / 8) * (context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32) + col; - uint32_t address = cell * 32 + (line % 8) * 4; + uint32_t base = (context->debug - 3) * 0x200; + uint32_t cell = base + (line / 8) * (context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32) + col; + uint32_t address = (cell * 32 + (line % 8) * 4) & 0xFFFF; for (int32_t i = 0; i < 4; i ++) { *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] >> 4)]; *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] & 0xF)]; address++; } cell++; - address = cell * 32 + (line % 8) * 4; + address = (cell * 32 + (line % 8) * 4) & 0xFFFF; for (int32_t i = 0; i < 4; i ++) { *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] >> 4)]; *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] & 0xF)]; @@ -1739,7 +1740,7 @@ cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY; cur->address = context->address; cur->value = value; - if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { + if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80 && (context->regs[REG_MODE_2] & BIT_DMA_ENABLE)) { context->flags |= FLAG_DMA_RUN; } cur->cd = context->cd;