changeset 2321:2eda5f81f91e

More fully baked ROM db support for SMS
author Michael Pavone <pavone@retrodev.com>
date Thu, 15 Jun 2023 09:36:11 -0700
parents 8016dbb0fcde
children 1a3991ada927 26597f9805b4
files romdb.c sms.c
diffstat 2 files changed, 236 insertions(+), 104 deletions(-) [+]
line wrap: on
line diff
--- a/romdb.c	Tue May 09 09:03:37 2023 -0700
+++ b/romdb.c	Thu Jun 15 09:36:11 2023 -0700
@@ -703,9 +703,8 @@
 	state->info->num_eeprom++;
 }
 
-void map_iter_fun(char *key, tern_val val, uint8_t valtype, void *data)
+char *map_node_common(char *key, tern_val val, uint8_t valtype, map_iter_state *state)
 {
-	map_iter_state *state = data;
 	if (valtype != TVAL_NODE) {
 		fatal_error("ROM DB map entry %d with address %s is not a node\n", state->index, key);
 	}
@@ -720,6 +719,7 @@
 	memmap_chunk *map = state->info->map + state->index;
 	map->start = start;
 	map->end = end + 1;
+	
 	if (!strcmp(dtype, "ROM")) {
 		uint32_t expanded_size = nearest_pow2(state->rom_size);
 		if (offset >= expanded_size) {
@@ -732,13 +732,100 @@
 		} else {
 			map->flags = MMAP_READ | MMAP_WRITE | MMAP_CODE;
 		}
-	} else if (!strcmp(dtype, "LOCK-ON")) {
+		return NULL;
+	}
+	if (!strcmp(dtype, "EEPROM")) {
+		process_eeprom_def(key, state);
+		add_eeprom_map(node, start, end, state);
+
+		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;
+		return NULL;
+	}
+	if (!strcmp(dtype, "SRAM")) {
+		process_sram_def(key, state);
+		map->buffer = state->info->save_buffer + offset;
+		map->flags = MMAP_READ | MMAP_WRITE;
+		uint32_t save_size_mask = state->info->save_size;
+		if (state->info->save_type == RAM_FLAG_ODD) {
+			map->flags |= MMAP_ONLY_ODD;
+			save_size_mask *= 2;
+		} else if(state->info->save_type == RAM_FLAG_EVEN) {
+			map->flags |= MMAP_ONLY_EVEN;
+			save_size_mask *= 2;
+		} else {
+			map->flags |= MMAP_CODE;
+		}
+		map->mask = calc_mask(save_size_mask, start, end);
+		return NULL;
+	}
+	if (!strcmp(dtype, "RAM")) {
+		uint32_t size = strtol(tern_find_ptr_default(node, "size", "0"), NULL, 16);
+		if (!size || size > map->end - map->start) {
+			size = map->end - map->start;
+		}
+		map->buffer = calloc(size, 1);
+		map->flags = MMAP_READ | MMAP_WRITE;
+		char *bus = tern_find_ptr_default(node, "bus", "both");
+		if (!strcmp(bus, "odd")) {
+			map->flags |= MMAP_ONLY_ODD;
+			size *= 2;
+		} else if (!strcmp(bus, "even")) {
+			map->flags |= MMAP_ONLY_EVEN;
+			size *= 2;
+		} else {
+			map->flags |= MMAP_CODE;
+		}
+		map->mask = calc_mask(size, start, end);
+		return NULL;
+	}
+	if (!strcmp(dtype, "NOR")) {
+		process_nor_def(key, state);
+
+		map->write_16 = nor_flash_write_w;
+		map->write_8 = nor_flash_write_b;
+		map->read_16 = nor_flash_read_w;
+		map->read_8 = nor_flash_read_b;
+		if (state->info->save_bus == RAM_FLAG_BOTH) {
+			map->flags |= MMAP_READ_CODE | MMAP_CODE;
+			map->buffer = state->info->save_buffer;
+		}
+		map->mask = 0xFFFFFF;
+		return NULL;
+	}
+	if (!strcmp(dtype, "fixed")) {
+		uint16_t *value =  malloc(2);
+		map->buffer = value;
+		map->mask = 0;
+		map->flags = MMAP_READ;
+		*value = strtol(tern_find_ptr_default(node, "value", "0"), NULL, 16);
+		return NULL;
+	}
+	return dtype;
+}
+
+void map_iter_fun(char *key, tern_val val, uint8_t valtype, void *data)
+{
+	map_iter_state *state = data;
+	char *dtype = map_node_common(key, val, valtype, state);
+	if (!dtype) {
+		//entry was handled by common function
+		state->index++;
+		return;
+	}
+	tern_node *node = val.ptrval;
+	memmap_chunk *map = state->info->map + state->index;
+	uint32_t offset = strtol(tern_find_ptr_default(node, "offset", "0"), NULL, 16);
+	if (!strcmp(dtype, "LOCK-ON")) {
 		rom_info lock_info;
 		if (state->lock_on) {
 			lock_info = configure_rom(state->rom_db, state->lock_on, state->lock_on_size, NULL, 0, NULL, 0);
-		} else if (state->rom_size > start) {
+		} else if (state->rom_size > map->start) {
 			//This is a bit of a hack to deal with pre-combined S3&K/S2&K ROMs and S&K ROM hacks
-			lock_info = configure_rom(state->rom_db, state->rom + start, state->rom_size - start, NULL, 0, NULL, 0);
+			lock_info = configure_rom(state->rom_db, state->rom + map->start, state->rom_size - map->start, NULL, 0, NULL, 0);
 		} else {
 			//skip this entry if there is no lock on cartridge attached
 			return;
@@ -793,60 +880,6 @@
 		}
 		free_rom_info(&lock_info);
 		return;
-	} else if (!strcmp(dtype, "EEPROM")) {
-		process_eeprom_def(key, state);
-		add_eeprom_map(node, start, end, state);
-
-		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")) {
-		process_sram_def(key, state);
-		map->buffer = state->info->save_buffer + offset;
-		map->flags = MMAP_READ | MMAP_WRITE;
-		uint32_t save_size_mask = state->info->save_size;
-		if (state->info->save_type == RAM_FLAG_ODD) {
-			map->flags |= MMAP_ONLY_ODD;
-			save_size_mask *= 2;
-		} else if(state->info->save_type == RAM_FLAG_EVEN) {
-			map->flags |= MMAP_ONLY_EVEN;
-			save_size_mask *= 2;
-		} else {
-			map->flags |= MMAP_CODE;
-		}
-		map->mask = calc_mask(save_size_mask, start, end);
-	} else if (!strcmp(dtype, "RAM")) {
-		uint32_t size = strtol(tern_find_ptr_default(node, "size", "0"), NULL, 16);
-		if (!size || size > map->end - map->start) {
-			size = map->end - map->start;
-		}
-		map->buffer = calloc(size, 1);
-		map->flags = MMAP_READ | MMAP_WRITE;
-		char *bus = tern_find_ptr_default(node, "bus", "both");
-		if (!strcmp(bus, "odd")) {
-			map->flags |= MMAP_ONLY_ODD;
-			size *= 2;
-		} else if (!strcmp(bus, "even")) {
-			map->flags |= MMAP_ONLY_EVEN;
-			size *= 2;
-		} else {
-			map->flags |= MMAP_CODE;
-		}
-		map->mask = calc_mask(size, start, end);
-	} else if (!strcmp(dtype, "NOR")) {
-		process_nor_def(key, state);
-
-		map->write_16 = nor_flash_write_w;
-		map->write_8 = nor_flash_write_b;
-		map->read_16 = nor_flash_read_w;
-		map->read_8 = nor_flash_read_b;
-		if (state->info->save_bus == RAM_FLAG_BOTH) {
-			map->flags |= MMAP_READ_CODE | MMAP_CODE;
-			map->buffer = state->info->save_buffer;
-		}
-		map->mask = 0xFFFFFF;
 	} else if (!strcmp(dtype, "Sega mapper")) {
 		state->info->mapper_type = MAPPER_SEGA;
 		state->info->mapper_start_index = state->ptr_index++;
@@ -854,7 +887,7 @@
 		char *save_device = tern_find_path(node, "save\0device\0", TVAL_PTR).ptrval;
 		if (save_device && !strcmp(save_device, "EEPROM")) {
 			process_eeprom_def(key, state);
-			add_eeprom_map(node, start & map->mask, end & map->mask, state);
+			add_eeprom_map(node, map->start & map->mask, (map->end - 1) & map->mask, state);
 		} else if (save_device && !strcmp(save_device, "SRAM")) {
 			process_sram_def(key, state);
 		} else if(has_ram_header(state->rom, state->rom_size)) {
@@ -868,11 +901,9 @@
 			state->info->map = realloc(state->info->map, sizeof(memmap_chunk) * state->info->map_chunks);
 			memset(state->info->map + state->info->map_chunks - 1, 0, sizeof(memmap_chunk) * 1);
 			map = state->info->map + state->index;
-			map->start = start;
-			map->end = end;
 			offset &= nearest_pow2(state->rom_size) - 1;
 			map->buffer = state->rom + offset;
-			map->mask = calc_mask(state->rom_size - offset, start, end);
+			map->mask = calc_mask(state->rom_size - offset, map->start, map->end - 1);
 			map->ptr_index = state->info->mapper_start_index;
 			map->flags = MMAP_READ | MMAP_PTR_IDX | MMAP_CODE | MMAP_FUNC_NULL;
 			if (save_device && !strcmp(save_device, "EEPROM")) {
@@ -893,6 +924,7 @@
 			state->info->map = realloc(state->info->map, sizeof(memmap_chunk) * state->info->map_chunks);
 			memset(state->info->map + state->info->map_chunks - 7, 0, sizeof(memmap_chunk) * 7);
 			map = state->info->map + state->index;
+			uint32_t start = map->start;
 			for (int i = 0; i < 7; i++, state->index++, map++)
 			{
 				map->start = start + i * 0x80000;
@@ -931,12 +963,6 @@
 		map->write_16 = menu_write_w;
 		map->read_16 = menu_read_w;
 #endif
-	} else if (!strcmp(dtype, "fixed")) {
-		uint16_t *value =  malloc(2);
-		map->buffer = value;
-		map->mask = 0;
-		map->flags = MMAP_READ;
-		*value = strtol(tern_find_ptr_default(node, "value", "0"), NULL, 16);
 	} else if (!strcmp(dtype, "multi-game")) {
 		state->info->mapper_type = MAPPER_MULTI_GAME;
 		state->info->mapper_start_index = state->ptr_index++;
@@ -950,7 +976,7 @@
 		memset(state->info->map + state->info->map_chunks - 1, 0, sizeof(memmap_chunk) * 1);
 		map = state->info->map + state->index;
 		map->buffer = state->rom;
-		map->mask = calc_mask(state->rom_size, start, end);
+		map->mask = calc_mask(state->rom_size, map->start, map->end - 1);
 		map->flags = MMAP_READ | MMAP_PTR_IDX | MMAP_CODE;
 		map->ptr_index = state->info->mapper_start_index;
 		map++;
@@ -988,7 +1014,7 @@
 			fatal_error("offset of %X is invalid for ROM size of %X in map entry %d with addess %s\n", offset, state->rom_size, state->index, key);
 		}
 		map->buffer = state->rom + offset;
-		map->mask = calc_mask(nearest_pow2(state->rom_size) - offset, start, end);
+		map->mask = calc_mask(nearest_pow2(state->rom_size) - offset, map->start, map->end - 1);
 		map->write_8 = sft_wukong_write_b;
 		map->write_16 = sft_wukong_write_w;
 		if (!strcmp(dtype, "sft-wukong-remap")) {
@@ -1003,6 +1029,31 @@
 	state->index++;
 }
 
+void handle_io_overrides(tern_node *entry, rom_info *info)
+{
+	tern_node *device_overrides = tern_find_node(entry, "device_overrides");
+	if (device_overrides) {
+		info->port1_override = tern_find_ptr(device_overrides, "1");
+		info->port2_override = tern_find_ptr(device_overrides, "2");
+		info->ext_override = tern_find_ptr(device_overrides, "ext");
+		if (
+			info->save_type == SAVE_NONE
+			&& (
+				(info->port1_override && startswith(info->port1_override, "heartbeat_trainer."))
+				|| (info->port2_override && startswith(info->port2_override, "heartbeat_trainer."))
+				|| (info->ext_override && startswith(info->ext_override, "heartbeat_trainer."))
+			)
+		) {
+			info->save_type = SAVE_HBPT;
+			info->save_size = atoi(tern_find_path_default(entry, "HeartbeatTrainer\0size\0", (tern_val){.ptrval="512"}, TVAL_PTR).ptrval);
+			info->save_buffer = calloc(info->save_size + 5 + 8, 1);
+			memset(info->save_buffer, 0xFF, info->save_size);
+		}
+	} else {
+		info->port1_override = info->port2_override = info->ext_override = NULL;
+	}
+}
+
 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)
 {
 	uint8_t product_id[GAME_ID_LEN+1];
@@ -1110,27 +1161,7 @@
 		add_memmap_header(&info, rom, rom_size, base_map, base_chunks);
 	}
 
-	tern_node *device_overrides = tern_find_node(entry, "device_overrides");
-	if (device_overrides) {
-		info.port1_override = tern_find_ptr(device_overrides, "1");
-		info.port2_override = tern_find_ptr(device_overrides, "2");
-		info.ext_override = tern_find_ptr(device_overrides, "ext");
-		if (
-			info.save_type == SAVE_NONE
-			&& (
-				(info.port1_override && startswith(info.port1_override, "heartbeat_trainer."))
-				|| (info.port2_override && startswith(info.port2_override, "heartbeat_trainer."))
-				|| (info.ext_override && startswith(info.ext_override, "heartbeat_trainer."))
-			)
-		) {
-			info.save_type = SAVE_HBPT;
-			info.save_size = atoi(tern_find_path_default(entry, "HeartbeatTrainer\0size\0", (tern_val){.ptrval="512"}, TVAL_PTR).ptrval);
-			info.save_buffer = calloc(info.save_size + 5 + 8, 1);
-			memset(info.save_buffer, 0xFF, info.save_size);
-		}
-	} else {
-		info.port1_override = info.port2_override = info.ext_override = NULL;
-	}
+	handle_io_overrides(entry, &info);
 	info.mouse_mode = tern_find_ptr(entry, "mouse_mode");
 	info.wants_cd = !strcmp(tern_find_ptr_default(entry, "wants_cd", "no"), "yes");
 
@@ -1138,6 +1169,65 @@
 }
 
 void *sms_sega_mapper_write(uint32_t location, void *vcontext, uint8_t value);
+void *sms_cart_ram_write(uint32_t location, void *vcontext, uint8_t value);
+void map_iter_fun_sms(char *key, tern_val val, uint8_t valtype, void *data)
+{
+	map_iter_state *state = data;
+	char *dtype = map_node_common(key, val, valtype, state);
+	if (!dtype) {
+		//entry was handled by common function
+		state->index++;
+		return;
+	}
+	tern_node *node = val.ptrval;
+	memmap_chunk *map = state->info->map + state->index;
+	uint32_t offset = strtol(tern_find_ptr_default(node, "offset", "0"), NULL, 16);
+	if (!strcmp(dtype, "Sega mapper")) {
+		state->info->mapper_type = MAPPER_SMS_SEGA;
+		state->info->mapper_start_index = state->ptr_index++;
+		char *save_device = tern_find_path(node, "save\0device\0", TVAL_PTR).ptrval;
+		if (save_device && !strcmp(save_device, "EEPROM")) {
+			process_eeprom_def(key, state);
+			add_eeprom_map(node, map->start & map->mask, (map->end - 1) & map->mask, state);
+		} else if (save_device && !strcmp(save_device, "SRAM")) {
+			process_sram_def(key, state);
+		}
+		state->info->map_chunks += 4;
+		state->info->map = realloc(state->info->map, sizeof(memmap_chunk) * state->info->map_chunks);
+		map = state->info->map + state->index;
+		map[0].start = 0;
+		map[0].end = 0x400;
+		map[0].mask = 0xFFFF;
+		map[0].flags = MMAP_READ;
+		map[0].buffer = state->info->rom;
+		map[1].start = 0x400;
+		map[1].end = 0x4000;
+		map[1].mask = 0x3FFF;
+		map[1].ptr_index = 0;
+		map[1].flags = MMAP_READ|MMAP_PTR_IDX|MMAP_CODE;
+		map[2].start = 0x4000;
+		map[2].end = 0x8000;
+		map[2].mask = 0x3FFF;
+		map[2].ptr_index = 1;
+		map[2].flags = MMAP_READ|MMAP_PTR_IDX|MMAP_CODE;
+		map[3].start = 0x8000;
+		map[3].end = 0xC000;
+		map[3].mask = 0x3FFF;
+		map[3].ptr_index = 2;
+		if (state->info->save_type == RAM_FLAG_ODD || state->info->save_type == RAM_FLAG_EVEN) {
+			map[3].write_8 = sms_cart_ram_write;
+		}
+		map[3].flags = MMAP_READ|MMAP_PTR_IDX|MMAP_CODE;
+		map[4].start = 0xFFFC;
+		map[4].end = 0x10000;
+		map[4].mask = 3;
+		map[4].flags = MMAP_READ;
+		map[4].write_8 = sms_sega_mapper_write;
+	} else {
+		fatal_error("Invalid device type %s for ROM DB map entry %d with address %s\n", dtype, state->index, key);
+	}
+}
+
 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);
@@ -1155,7 +1245,7 @@
 		chunks[0].buffer = info->rom;
 		chunks[1].start = 0x400;
 		chunks[1].end = 0x4000;
-		chunks[1].mask = 0xFFFF;
+		chunks[1].mask = 0x3FFF;
 		chunks[1].ptr_index = 0;
 		chunks[1].flags = MMAP_READ|MMAP_PTR_IDX|MMAP_CODE;
 		chunks[2].start = 0x4000;
@@ -1168,14 +1258,18 @@
 		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;
+		chunks[3].write_8 = sms_cart_ram_write;
+		chunks[num_chunks - 1].start = 0xFFFC;
+		chunks[num_chunks - 1].end = 0x10000;
+		chunks[num_chunks - 1].mask = 3;
+		chunks[num_chunks - 1].flags = MMAP_READ;
+		chunks[num_chunks - 1].write_8 = sms_sega_mapper_write;
+		for (uint32_t i = 4; i < num_chunks - 1; i++)
+		{
+			if (chunks[i].end > 0xFFFC) {
+				//mapper regs overlap RAM from base map
+				chunks[i].end = 0xFFFC;
+			}
 		}
 	} else {
 		info->mapper_type = MAPPER_NONE;
@@ -1269,7 +1363,38 @@
 			info.regions |= translate_region_char(*(dbreg++));
 		}
 	}
-	//TODO: check for and handle map from db
-	sms_memmap_heuristics(&info, base_chunks, num_base_chunks);
+	tern_node *map = tern_find_node(entry, "map");
+	if (map) {
+		info.save_type = SAVE_NONE;
+		info.map_chunks = tern_count(map);
+		if (info.map_chunks) {
+			info.map_chunks += num_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);
+			map_iter_state state = {
+				.info = &info,
+				.rom = rom,
+				.root = entry,
+				.rom_db = rom_db,
+				.rom_size = rom_size,
+				.index = 0,
+				.num_els = info.map_chunks - num_base_chunks,
+				.ptr_index = 0
+			};
+			tern_foreach(map, map_iter_fun_sms, &state);
+			memcpy(info.map + state.index, base_chunks, sizeof(memmap_chunk) * num_base_chunks);
+			info.rom = state.rom;
+			info.rom_size = state.rom_size;
+		} else {
+			sms_memmap_heuristics(&info, base_chunks, num_base_chunks);
+		}
+	} else {
+		sms_memmap_heuristics(&info, base_chunks, num_base_chunks);
+	}
+	handle_io_overrides(entry, &info);
 	return info;
 }
--- a/sms.c	Tue May 09 09:03:37 2023 -0700
+++ b/sms.c	Thu Jun 15 09:36:11 2023 -0700
@@ -165,7 +165,7 @@
 	return vcontext;
 }
 
-static void *cart_ram_write(uint32_t location, void *vcontext, uint8_t value)
+void *sms_cart_ram_write(uint32_t location, void *vcontext, uint8_t value)
 {
 	z80_context *z80 = vcontext;
 	sms_context *sms = z80->system;
@@ -712,6 +712,13 @@
 			warning("Unrecognized VDP type %s\n", vdp_str);
 		}
 	}
+	for (uint32_t i = 0; i < sms->header.info.map_chunks; i++)
+	{
+		memmap_chunk *chunk = sms->header.info.map + i;
+		if ((chunk->flags == MMAP_READ) && !chunk->buffer && chunk->start > 0xC000) {
+			chunk->buffer = sms->ram + ((chunk->start - 0xC000) & 0x1FFF);
+		}
+	}
 	if (is_gamegear) {
 		init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_gg, 6, 15, 0xFF);
 		sms->start_button_region = 0xC0;