# HG changeset patch # User Michael Pavone # Date 1683648217 25200 # Node ID 8016dbb0fcde9386d5e9b2f3205ecbf75a2b1ac2 # Parent ab3d8759da08400f6965d7982981a14bce972617 Initial work on ROM DB support for SMS/GG games diff -r ab3d8759da08 -r 8016dbb0fcde romdb.c --- a/romdb.c Sun Apr 02 23:38:21 2023 -0700 +++ b/romdb.c Tue May 09 09:03:37 2023 -0700 @@ -1136,3 +1136,140 @@ return info; } + +void *sms_sega_mapper_write(uint32_t location, void *vcontext, uint8_t value); +void sms_memmap_heuristics(rom_info *info, memmap_chunk const *base_map, uint32_t num_base_chunks) +{ + uint32_t num_chunks = num_base_chunks + (info->rom_size > 0xC000 ? 5 : 1); + memmap_chunk *chunks = calloc(num_chunks, sizeof(memmap_chunk)); + info->map = chunks; + info->map_chunks = num_chunks; + if (info->rom_size > 0xC000) { + //TODO: codemasters header + info->mapper_type = MAPPER_SMS_SEGA; + memcpy(chunks + 4, base_map, sizeof(memmap_chunk) * num_base_chunks); + chunks[0].start = 0; + chunks[0].end = 0x400; + chunks[0].mask = 0xFFFF; + chunks[0].flags = MMAP_READ; + chunks[0].buffer = info->rom; + chunks[1].start = 0x400; + chunks[1].end = 0x4000; + chunks[1].mask = 0xFFFF; + chunks[1].ptr_index = 0; + chunks[1].flags = MMAP_READ|MMAP_PTR_IDX|MMAP_CODE; + chunks[2].start = 0x4000; + chunks[2].end = 0x8000; + chunks[2].mask = 0x3FFF; + chunks[2].ptr_index = 1; + chunks[2].flags = MMAP_READ|MMAP_PTR_IDX|MMAP_CODE; + chunks[3].start = 0x8000; + chunks[3].end = 0xC000; + chunks[3].mask = 0x3FFF; + chunks[3].ptr_index = 2; + chunks[3].flags = MMAP_READ|MMAP_PTR_IDX|MMAP_CODE; + chunks[5].start = 0xFFFC; + chunks[5].end = 0x10000; + chunks[5].mask = 3; + chunks[5].flags = MMAP_READ; + chunks[5].write_8 = sms_sega_mapper_write; + if (chunks[4].end > 0xFFFC) { + //mapper regs overlap RAM from base map + chunks[4].end = 0xFFFC; + } + } else { + info->mapper_type = MAPPER_NONE; + memcpy(chunks + 1, base_map, sizeof(memmap_chunk) * num_base_chunks); + chunks[0].start = 0; + chunks[0].end = 0xC000; + chunks[0].mask = nearest_pow2(info->rom_size)-1; + chunks[0].flags = MMAP_READ; + chunks[0].buffer = info->rom; + } +} + +void configure_rom_sms_heuristics(rom_info *info, uint32_t header_offset, memmap_chunk const *base_map, uint32_t num_base_chunks) +{ + sms_memmap_heuristics(info, base_map, num_base_chunks); +} + +uint8_t check_sms_sega_header(uint8_t *rom, char *product_code, uint32_t offset) +{ + if (memcmp(rom + offset, "TMR SEGA", strlen("TMR SEGA"))) { + return 0; + } + char *cur = product_code + 4; + uint8_t begin = rom[offset + 0xE] >> 4; + if (begin < 0xA) { + *(cur++) = begin + '0'; + } else { + *(cur++) = '1'; + *(cur++) = begin - 0xA + '0'; + } + uint8_t *src = rom + offset + 0xD; + for (int i = 0; i < 2; i++, src--) + { + *(cur++) = (*src >> 4) + '0'; + *(cur++) = (*src & 0xF) + '0'; + } + *cur = 0; + return 1; +} + +rom_info configure_rom_sms(tern_node *rom_db, uint8_t *rom, uint32_t rom_size, memmap_chunk const *base_chunks, uint32_t num_base_chunks) +{ + uint32_t expanded_size = nearest_pow2(rom_size); + if (expanded_size > rom_size) { + //generally carts with odd-sized ROMs have 2 power of 2 sized ROMs with the larger one first + //TODO: Handle cases in which the 2nd ROM/part is a maller power of 2 than just half the first one + uint32_t mirror_start = expanded_size >> 1; + uint32_t mirror_size = expanded_size >> 2; + if (mirror_start + mirror_size >= rom_size) { + memcpy(rom + mirror_start + mirror_size, rom + mirror_start, mirror_size); + } + } + char product_code[] = "sms:000000"; + uint8_t found_header = 0; + uint32_t offset = 0; + if (rom_size >= 0x8000) { + offset = 0x7FF0; + found_header = check_sms_sega_header(rom, product_code, offset); + } + if (!found_header && rom_size >= 0x4000) { + offset = 0x3FF0; + found_header = check_sms_sega_header(rom, product_code, offset); + } + if (!found_header && rom_size >= 0x2000) { + offset = 0x1FF0; + found_header = check_sms_sega_header(rom, product_code, offset); + } + debug_message("Product Code: %s\n", product_code); + uint8_t raw_hash[20]; + sha1(rom, rom_size, raw_hash); + uint8_t hex_hash[41]; + bin_to_hex(hex_hash, raw_hash, 20); + debug_message("SHA1: %s\n", hex_hash); + tern_node * entry = tern_find_node(rom_db, hex_hash); + if (!entry) { + entry = tern_find_node(rom_db, product_code); + } + rom_info info = {0}; + info.rom_size = rom_size; + info.rom = rom; + if (!entry) { + debug_message("Not found in ROM DB, examining header\n\n"); + configure_rom_sms_heuristics(&info, offset, base_chunks, num_base_chunks); + return info; + } + char *dbreg = tern_find_ptr(entry, "regions"); + info.regions = 0; + if (dbreg) { + while (*dbreg != 0) + { + info.regions |= translate_region_char(*(dbreg++)); + } + } + //TODO: check for and handle map from db + sms_memmap_heuristics(&info, base_chunks, num_base_chunks); + return info; +} diff -r ab3d8759da08 -r 8016dbb0fcde romdb.h --- a/romdb.h Sun Apr 02 23:38:21 2023 -0700 +++ b/romdb.h Tue May 09 09:03:37 2023 -0700 @@ -50,7 +50,8 @@ MAPPER_MULTI_GAME, MAPPER_JCART, MAPPER_SEGA_MED_V2, - MAPPER_SFT_WUKONG + MAPPER_SFT_WUKONG, + MAPPER_SMS_SEGA }; @@ -88,6 +89,7 @@ tern_node *get_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_sms(tern_node *rom_db, uint8_t *rom, uint32_t rom_size, memmap_chunk const *base_chunks, uint32_t num_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); char const *save_type_name(uint8_t save_type); diff -r ab3d8759da08 -r 8016dbb0fcde sms.c --- a/sms.c Sun Apr 02 23:38:21 2023 -0700 +++ b/sms.c Tue May 09 09:03:37 2023 -0700 @@ -154,7 +154,7 @@ } } -static void *mapper_write(uint32_t location, void *vcontext, uint8_t value) +void *sms_sega_mapper_write(uint32_t location, void *vcontext, uint8_t value) { z80_context *z80 = vcontext; sms_context *sms = z80->system; @@ -679,24 +679,12 @@ sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t force_region) { sms_context *sms = calloc(1, sizeof(sms_context)); - uint32_t rom_size = nearest_pow2(media->size); - memmap_chunk memory_map[6]; - if (media->size > 0xC000) { - sms->header.info.map_chunks = 6; - uint8_t *ram_reg_overlap = sms->ram + sizeof(sms->ram) - 4; - memory_map[0] = (memmap_chunk){0x0000, 0x0400, 0xFFFF, .flags = MMAP_READ, .buffer = media->buffer}; - memory_map[1] = (memmap_chunk){0x0400, 0x4000, 0xFFFF, .ptr_index = 0, .flags = MMAP_READ|MMAP_PTR_IDX|MMAP_CODE}; - memory_map[2] = (memmap_chunk){0x4000, 0x8000, 0x3FFF, .ptr_index = 1, .flags = MMAP_READ|MMAP_PTR_IDX|MMAP_CODE}; - memory_map[3] = (memmap_chunk){0x8000, 0xC000, 0x3FFF, .ptr_index = 2, .flags = MMAP_READ|MMAP_PTR_IDX|MMAP_CODE, .write_8 = cart_ram_write}; - memory_map[4] = (memmap_chunk){0xC000, 0xFFFC, sizeof(sms->ram)-1, .ptr_index = 0, .flags = MMAP_READ|MMAP_WRITE|MMAP_CODE, .buffer = sms->ram}; - memory_map[5] = (memmap_chunk){0xFFFC, 0x10000, 0x0003, .ptr_index = 0, .flags = MMAP_READ, .buffer = ram_reg_overlap, .write_8 = mapper_write}; - } else { - sms->header.info.map_chunks = 2; - memory_map[0] = (memmap_chunk){0x0000, 0xC000, rom_size-1, 0, 0, .flags = MMAP_READ, .buffer = media->buffer}; - memory_map[1] = (memmap_chunk){0xC000, 0x10000, sizeof(sms->ram)-1, 0, 0, .flags = MMAP_READ|MMAP_WRITE|MMAP_CODE, .buffer = sms->ram}; + tern_node *rom_db = get_rom_db(); + const memmap_chunk base_map[] = { + {0xC000, 0x10000, sizeof(sms->ram)-1, .flags = MMAP_READ|MMAP_WRITE|MMAP_CODE, .buffer = sms->ram} }; - sms->header.info.map = malloc(sizeof(memmap_chunk) * sms->header.info.map_chunks); - memcpy(sms->header.info.map, memory_map, sizeof(memmap_chunk) * sms->header.info.map_chunks); + sms->header.info = configure_rom_sms(rom_db, media->buffer, media->size, base_map, sizeof(base_map)/sizeof(base_map[0])); + uint32_t rom_size = sms->header.info.rom_size; z80_options *zopts = malloc(sizeof(z80_options)); tern_node *model_def; uint8_t is_gamegear = !strcasecmp(media->extension, "gg");