summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--blastem.c2
-rw-r--r--genesis.c8
-rw-r--r--genesis.h1
-rw-r--r--rom.db26
-rw-r--r--romdb.c257
-rw-r--r--romdb.h20
6 files changed, 310 insertions, 4 deletions
diff --git a/blastem.c b/blastem.c
index f7e7e6c..561623a 100644
--- a/blastem.c
+++ b/blastem.c
@@ -154,7 +154,7 @@ void setup_saves(char *fname, rom_info *info, system_header *context)
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
diff --git a/genesis.c b/genesis.c
index ebc01bd..391cbd6 100644
--- a/genesis.c
+++ b/genesis.c
@@ -967,12 +967,12 @@ static void persist_save(system_header *system)
}
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 @@ static void load_save(system_header *system)
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 @@ genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on
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;
diff --git a/genesis.h b/genesis.h
index 60e9f49..80536ec 100644
--- a/genesis.h
+++ b/genesis.h
@@ -50,6 +50,7 @@ struct genesis_context {
uint8_t bus_busy;
uint8_t reset_requested;
eeprom_state eeprom;
+ nor_state nor;
};
#define RAM_WORDS 32 * 1024
diff --git a/rom.db b/rom.db
index 5895937..94b27c7 100644
--- a/rom.db
+++ b/rom.db
@@ -891,3 +891,29 @@ BlstMenu {
}
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
+ }
+ }
+}
diff --git a/romdb.c b/romdb.c
index 7756185..25f29cc 100644
--- a/romdb.c
+++ b/romdb.c
@@ -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 @@ uint8_t get_sda(eeprom_state *state)
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_eeprom_def(char * key, map_iter_state *state)
}
}
+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 @@ void map_iter_fun(char *key, tern_val val, uint8_t valtype, void *data)
} 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;
diff --git a/romdb.h b/romdb.h
index a87bd2f..02145aa 100644
--- a/romdb.h
+++ b/romdb.h
@@ -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 @@ typedef struct {
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 @@ struct rom_info {
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(tern_node *rom_db, void *vrom, uint32_t rom_size, void *l
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_