changeset 2320:8016dbb0fcde

Initial work on ROM DB support for SMS/GG games
author Michael Pavone <pavone@retrodev.com>
date Tue, 09 May 2023 09:03:37 -0700
parents ab3d8759da08
children 2eda5f81f91e
files romdb.c romdb.h sms.c
diffstat 3 files changed, 146 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- 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;
+}
--- 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);
--- 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");