diff nor.c @ 1414:d94855080529

Move I2C EEPROM and NOR Flash functions out of romdb.c into new files
author Michael Pavone <pavone@retrodev.com>
date Fri, 23 Jun 2017 21:48:38 -0700
parents
children 1f745318f10a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nor.c	Fri Jun 23 21:48:38 2017 -0700
@@ -0,0 +1,204 @@
+#include "genesis.h"
+#include <stdlib.h>
+#include <string.h>
+
+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);
+}