changeset 1395:efa7225e0f07

Initial work to support parallel NOR flash and the Magistr 16
author Michael Pavone <pavone@retrodev.com>
date Wed, 07 Jun 2017 23:06:14 -0700
parents ae3b1721b226
children aca496957999
files blastem.c genesis.c genesis.h rom.db romdb.c romdb.h
diffstat 6 files changed, 310 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/blastem.c	Mon Jun 05 23:03:46 2017 -0700
+++ b/blastem.c	Wed Jun 07 23:06:14 2017 -0700
@@ -154,7 +154,7 @@
 	if (!ensure_dir_exists(save_dir)) {
 		warning("Failed to create save directory %s\n", save_dir);
 	}
-	char const *parts[] = {save_dir, PATH_SEP, info->save_type == SAVE_I2C ? "save.eeprom" : "save.sram"};
+	char const *parts[] = {save_dir, PATH_SEP, info->save_type == SAVE_I2C ? "save.eeprom" : info->save_type == SAVE_NOR ? "save.nor" : "save.sram"};
 	free(save_filename);
 	save_filename = alloc_concat_m(3, parts);
 	//TODO: make quick save filename dependent on system type
--- a/genesis.c	Mon Jun 05 23:03:46 2017 -0700
+++ b/genesis.c	Wed Jun 07 23:06:14 2017 -0700
@@ -967,12 +967,12 @@
 	}
 	FILE * f = fopen(save_filename, "wb");
 	if (!f) {
-		fprintf(stderr, "Failed to open %s file %s for writing\n", gen->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
+		fprintf(stderr, "Failed to open %s file %s for writing\n", save_type_name(gen->save_type), save_filename);
 		return;
 	}
 	fwrite(gen->save_storage, 1, gen->save_size, f);
 	fclose(f);
-	printf("Saved %s to %s\n", gen->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
+	printf("Saved %s to %s\n", save_type_name(gen->save_type), save_filename);
 }
 
 static void load_save(system_header *system)
@@ -983,7 +983,7 @@
 		uint32_t read = fread(gen->save_storage, 1, gen->save_size, f);
 		fclose(f);
 		if (read > 0) {
-			printf("Loaded %s from %s\n", gen->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
+			printf("Loaded %s from %s\n", save_type_name(gen->save_type), save_filename);
 		}
 	}
 }
@@ -1115,6 +1115,8 @@
 		gen->num_eeprom = rom->num_eeprom;
 		if (gen->save_type == SAVE_I2C) {
 			eeprom_init(&gen->eeprom, gen->save_storage, gen->save_size);
+		} else if (gen->save_type == SAVE_NOR) {
+			nor_flash_init(&gen->nor, gen->save_storage, gen->save_size, rom->save_page_size, rom->save_product_id, rom->save_bus);
 		}
 	} else {
 		gen->save_storage = NULL;
--- a/genesis.h	Mon Jun 05 23:03:46 2017 -0700
+++ b/genesis.h	Wed Jun 07 23:06:14 2017 -0700
@@ -50,6 +50,7 @@
 	uint8_t         bus_busy;
 	uint8_t         reset_requested;
 	eeprom_state    eeprom;
+	nor_state       nor;
 };
 
 #define RAM_WORDS 32 * 1024
--- a/rom.db	Mon Jun 05 23:03:46 2017 -0700
+++ b/rom.db	Wed Jun 07 23:06:14 2017 -0700
@@ -891,3 +891,29 @@
 	}
 	mouse_mode absolute
 }
+
+6568b3a4e096159776ef8687a80d43589741fd60 {
+	name Magistr 16 BIOS
+	NOR {
+		size 262144
+		page_size 128
+		product_id DA45
+		bus even
+	}
+	map {
+		0 {
+			device ROM
+			last 3FFFFF
+		}
+		400000 {
+			device NOR
+			last 7FFFFF
+		}
+		E00000 {
+			device RAM
+			size 40000
+			last FFFFFF
+			bus both
+		}
+	}
+}
--- a/romdb.c	Mon Jun 05 23:03:46 2017 -0700
+++ b/romdb.c	Wed Jun 07 23:06:14 2017 -0700
@@ -20,6 +20,16 @@
 #define RAM_END   0x1B8
 #define REGION_START 0x1F0
 
+char const *save_type_name(uint8_t save_type)
+{
+	if (save_type == SAVE_I2C) {
+		return "EEPROM";
+	} else if(save_type == SAVE_NOR) {
+		return "NOR Flash";
+	}
+	return "SRAM";
+}
+
 enum {
 	I2C_IDLE,
 	I2C_START,
@@ -187,6 +197,207 @@
 	return state->host_sda & state->slave_sda;
 }
 
+enum {
+	NOR_NORMAL,
+	NOR_PRODUCTID,
+	NOR_BOOTBLOCK
+};
+
+enum {
+	NOR_CMD_IDLE,
+	NOR_CMD_AA,
+	NOR_CMD_55
+};
+
+//Technically this value shoudl be slightly different between NTSC and PAL
+//as it's defined as 200 micro-seconds, not in clock cycles
+#define NOR_WRITE_PAUSE 10690 
+
+void nor_flash_init(nor_state *state, uint8_t *buffer, uint32_t size, uint32_t page_size, uint16_t product_id, uint8_t bus_flags)
+{
+	state->buffer = buffer;
+	state->page_buffer = malloc(page_size);
+	memset(state->page_buffer, 0xFF, page_size);
+	state->size = size;
+	state->page_size = page_size;
+	state->product_id = product_id;
+	state->last_write_cycle = 0xFFFFFFFF;
+	state->mode = NOR_NORMAL;
+	state->cmd_state = NOR_CMD_IDLE;
+	state->alt_cmd = 0;
+	state->bus_flags = bus_flags;
+}
+
+void nor_run(nor_state *state, uint32_t cycle)
+{
+	if (state->last_write_cycle == 0xFFFFFFFF) {
+		return;
+	}
+	if (cycle - state->last_write_cycle >= NOR_WRITE_PAUSE) {
+		state->last_write_cycle = 0xFFFFFFFF;
+		for (uint32_t i = 0; i < state->page_size; i++) {
+			state->buffer[state->current_page + i] = state->page_buffer[i];
+		}
+		memset(state->page_buffer, 0xFF, state->page_size);
+	}
+}
+
+uint8_t nor_flash_read_b(uint32_t address, void *vcontext)
+{
+	m68k_context *m68k = vcontext;
+	genesis_context *gen = m68k->system;
+	nor_state *state = &gen->nor;
+	if (
+		((address & 1) && state->bus_flags == RAM_FLAG_EVEN) ||
+		(!(address & 1) && state->bus_flags == RAM_FLAG_ODD)
+	) {
+		return 0xFF;
+	}
+	if (state->bus_flags != RAM_FLAG_BOTH) {
+		address = address >> 1;
+	}
+	
+	nor_run(state, m68k->current_cycle);
+	switch (state->mode)
+	{
+	case NOR_NORMAL:
+		return state->buffer[address & (state->size-1)];
+		break;
+	case NOR_PRODUCTID:
+		switch (address & (state->size - 1))
+		{
+		case 0:
+			return state->product_id >> 8;
+		case 1:
+			return state->product_id;
+		case 2:
+			//TODO: Implement boot block protection
+			return 0xFE;
+		default:
+			return 0xFE;
+		}
+		break;
+	case NOR_BOOTBLOCK:
+		break;
+	}
+	return 0xFF;
+}
+
+uint16_t nor_flash_read_w(uint32_t address, void *context)
+{
+	uint16_t value = nor_flash_read_b(address, context) << 8;
+	value |= nor_flash_read_b(address+1, context);
+	return value;
+}
+
+void nor_write_byte(nor_state *state, uint32_t address, uint8_t value, uint32_t cycle)
+{
+	switch(state->mode)
+	{
+	case NOR_NORMAL:
+		if (state->last_write_cycle != 0xFFFFFFFF) {
+			state->current_page = address & (state->size - 1) & ~(state->page_size - 1);
+		}
+		state->page_buffer[address & (state->page_size - 1)] = value;
+		break;
+	case NOR_PRODUCTID:
+		break;
+	case NOR_BOOTBLOCK:
+		//TODO: Implement boot block protection
+		state->mode = NOR_NORMAL;
+		break;
+	}
+}
+
+void *nor_flash_write_b(uint32_t address, void *vcontext, uint8_t value)
+{
+	m68k_context *m68k = vcontext;
+	genesis_context *gen = m68k->system;
+	nor_state *state = &gen->nor;
+	if (
+		((address & 1) && state->bus_flags == RAM_FLAG_EVEN) ||
+		(!(address & 1) && state->bus_flags == RAM_FLAG_ODD)
+	) {
+		return vcontext;
+	}
+	if (state->bus_flags != RAM_FLAG_BOTH) {
+		address = address >> 1;
+	}
+	
+	nor_run(state, m68k->current_cycle);
+	switch (state->cmd_state)
+	{
+	case NOR_CMD_IDLE:
+		if (value == 0xAA && (address & (state->size - 1)) == 0x5555) {
+			state->cmd_state = NOR_CMD_AA;
+		} else {
+			nor_write_byte(state, address, value, m68k->current_cycle);
+			state->cmd_state = NOR_CMD_IDLE;
+		}
+		break;
+	case NOR_CMD_AA:
+		if (value == 0x55 && (address & (state->size - 1)) == 0x2AAA) {
+			state->cmd_state = NOR_CMD_55;
+		} else {
+			nor_write_byte(state, 0x5555, 0xAA, m68k->current_cycle);
+			nor_write_byte(state, address, value, m68k->current_cycle);
+			state->cmd_state = NOR_CMD_IDLE;
+		}
+		break;
+	case NOR_CMD_55:
+		if ((address & (state->size - 1)) == 0x5555) {
+			if (state->alt_cmd) {
+				switch(value)
+				{
+				case 0x10:
+					puts("UNIMPLEMENTED: NOR flash erase");
+					break;
+				case 0x20:
+					puts("UNIMPLEMENTED: NOR flash disable protection");
+					break;
+				case 0x40:
+					state->mode = NOR_BOOTBLOCK;
+					break;
+				case 0x60:
+					state->mode = NOR_PRODUCTID;
+					break;
+				}
+			} else {
+				switch(value)
+				{
+				case 0x80:
+					state->alt_cmd = 1;
+					break;
+				case 0x90:
+					state->mode = NOR_PRODUCTID;
+					break;
+				case 0xA0:
+					puts("UNIMPLEMENTED: NOR flash enable protection");
+					break;
+				case 0xF0:
+					state->mode = NOR_NORMAL;
+					break;
+				default:
+					printf("Unrecognized unshifted NOR flash command %X\n", value);
+				}
+			}
+		} else {
+			nor_write_byte(state, 0x5555, 0xAA, m68k->current_cycle);
+			nor_write_byte(state, 0x2AAA, 0x55, m68k->current_cycle);
+			nor_write_byte(state, address, value, m68k->current_cycle);
+		}
+		state->cmd_state = NOR_CMD_IDLE;
+		break;
+	}
+	return vcontext;
+}
+
+void *nor_flash_write_w(uint32_t address, void *vcontext, uint16_t value)
+{
+	nor_flash_write_b(address, vcontext, value >> 8);
+	return nor_flash_write_b(address + 1, vcontext, value);
+}
+
 uint16_t read_sram_w(uint32_t address, m68k_context * context)
 {
 	genesis_context * gen = context->system;
@@ -739,6 +950,44 @@
 	}
 }
 
+void process_nor_def(char *key, map_iter_state *state)
+{
+	if (!state->info->save_size) {
+		char *size = tern_find_path(state->root, "NOR\0size\0", TVAL_PTR).ptrval;
+		if (!size) {
+			fatal_error("ROM DB map entry %d with address %s has device type NOR, but the NOR size is not defined\n", state->index, key);
+		}
+		state->info->save_size = atoi(size);
+		if (!state->info->save_size) {
+			fatal_error("NOR size %s is invalid\n", size);
+		}
+		char *page_size = tern_find_path(state->root, "NOR\0page_size\0", TVAL_PTR).ptrval;
+		if (!page_size) {
+			fatal_error("ROM DB map entry %d with address %s has device type NOR, but the NOR page size is not defined\n", state->index, key);
+		}
+		state->info->save_page_size = atoi(size);
+		if (!state->info->save_page_size) {
+			fatal_error("NOR page size %s is invalid\n", size);
+		}
+		char *product_id = tern_find_path(state->root, "NOR\0product_id\0", TVAL_PTR).ptrval;
+		if (!product_id) {
+			fatal_error("ROM DB map entry %d with address %s has device type NOR, but the NOR product ID is not defined\n", state->index, key);
+		}
+		state->info->save_product_id = strtol(product_id, NULL, 16);
+		char *bus = tern_find_path(state->root, "NOR\0bus\0", TVAL_PTR).ptrval;
+		if (!strcmp(bus, "odd")) {
+			state->info->save_bus = RAM_FLAG_ODD;
+		} else if(!strcmp(bus, "even")) {
+			state->info->save_bus = RAM_FLAG_EVEN;
+		} else {
+			state->info->save_bus = RAM_FLAG_BOTH;
+		}
+		state->info->save_type = SAVE_NOR;
+		state->info->save_buffer = malloc(state->info->save_size);
+		memset(state->info->save_buffer, 0xFF, state->info->save_size);
+	}
+}
+
 void add_eeprom_map(tern_node *node, uint32_t start, uint32_t end, map_iter_state *state)
 {
 	eeprom_map *eep_map = state->info->eeprom_map + state->info->num_eeprom;
@@ -821,6 +1070,14 @@
 		} else {
 			map->flags |= MMAP_CODE;
 		}
+	} 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;
+		map->mask = 0xFFFFFF;
 	} else if (!strcmp(dtype, "Sega mapper")) {
 		state->info->mapper_start_index = state->ptr_index++;
 		state->info->map_chunks+=7;
--- a/romdb.h	Mon Jun 05 23:03:46 2017 -0700
+++ b/romdb.h	Wed Jun 07 23:06:14 2017 -0700
@@ -10,6 +10,7 @@
 #define RAM_FLAG_BOTH 0x00
 #define RAM_FLAG_MASK RAM_FLAG_ODD
 #define SAVE_I2C      0x01
+#define SAVE_NOR      0x02
 #define SAVE_NONE     0xFF
 
 #include "tern.h"
@@ -34,6 +35,20 @@
 	uint8_t     latch;
 } eeprom_state;
 
+typedef struct {
+	uint8_t     *buffer;
+	uint8_t     *page_buffer;
+	uint32_t    size;
+	uint32_t    page_size;
+	uint32_t    current_page;
+	uint32_t    last_write_cycle;
+	uint16_t    product_id;
+	uint8_t     mode;
+	uint8_t     cmd_state;
+	uint8_t     alt_cmd;
+	uint8_t     bus_flags;
+} nor_state;
+
 
 typedef struct rom_info rom_info;
 
@@ -52,8 +67,11 @@
 	uint32_t      map_chunks;
 	uint32_t      save_size;
 	uint32_t      save_mask;
+	uint32_t      save_page_size;
+	uint16_t      save_product_id;
 	uint16_t      mapper_start_index;
 	uint8_t       save_type;
+	uint8_t       save_bus; //only used for NOR currently
 	uint8_t       regions;
 };
 
@@ -65,5 +83,7 @@
 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);
+void nor_flash_init(nor_state *state, uint8_t *buffer, uint32_t size, uint32_t page_size, uint16_t product_id, uint8_t bus_flags);
+char const *save_type_name(uint8_t save_type);
 
 #endif //ROMDB_H_