summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/machine/at45dbxx.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mess/machine/at45dbxx.c')
-rw-r--r--src/mess/machine/at45dbxx.c473
1 files changed, 473 insertions, 0 deletions
diff --git a/src/mess/machine/at45dbxx.c b/src/mess/machine/at45dbxx.c
new file mode 100644
index 00000000000..23b38a9f92e
--- /dev/null
+++ b/src/mess/machine/at45dbxx.c
@@ -0,0 +1,473 @@
+/*
+
+ Atmel Serial DataFlash
+
+ (c) 2001-2007 Tim Schuerewegen
+
+ AT45DB041 - 528 KByte
+ AT45DB081 - 1056 KByte
+ AT45DB161 - 2112 KByte
+
+*/
+
+#include "at45dbxx.h"
+
+enum
+{
+ TYPE_AT45DB041,
+ TYPE_AT45DB081,
+ TYPE_AT45DB161
+};
+
+#define LOG_LEVEL 1
+#define _logerror(level,x) do { if (LOG_LEVEL > level) logerror x; } while (0)
+
+#define FLASH_CMD_52 0x52
+#define FLASH_CMD_57 0x57
+#define FLASH_CMD_60 0x60
+#define FLASH_CMD_82 0x82
+
+#define FLASH_MODE_XX 0 // unknown
+#define FLASH_MODE_SI 1 // input
+#define FLASH_MODE_SO 2 // output
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct
+{
+ int cs; // chip select
+ int sck; // serial clock
+ int si; // serial input
+ int so; // serial output
+ int wp; // write protect
+ int reset; // reset
+ int busy; // busy
+} AT45DBXX_PINS;
+
+typedef struct
+{
+ UINT8 data[8], size;
+} AT45DBXX_CMD;
+
+typedef struct
+{
+ UINT8 *data;
+ UINT32 size, pos;
+} AT45DBXX_IO;
+
+typedef struct _at45dbxx_t at45dbxx_t;
+struct _at45dbxx_t
+{
+ UINT8 *data;
+ UINT32 size;
+ UINT32 pages, page_size;
+ UINT8 mode, status, devid, *buffer1, *buffer2;
+ AT45DBXX_PINS pin;
+ UINT8 si_byte, si_bits, so_byte, so_bits;
+ AT45DBXX_CMD cmd;
+ AT45DBXX_IO io;
+};
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+INLINE at45dbxx_t *get_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == AT45DB041 || device->type() == AT45DB081 || device->type() == AT45DB161);
+
+ return (at45dbxx_t *) downcast<legacy_device_base *>(device)->token();
+}
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+static void common_start(device_t *device, int device_type)
+{
+ at45dbxx_t *flash = get_token(device);
+
+ _logerror( 0, ("at45dbxx_init (%d)\n", device_type));
+ memset(flash, 0, sizeof(at45dbxx_t));
+ switch (device_type)
+ {
+ case TYPE_AT45DB041 : flash->pages = 2048; flash->page_size = 264; flash->devid = 0x18; break;
+ case TYPE_AT45DB081 : flash->pages = 4096; flash->page_size = 264; flash->devid = 0x20; break;
+ case TYPE_AT45DB161 : flash->pages = 4096; flash->page_size = 528; flash->devid = 0x28; break;
+ }
+ flash->size = flash->pages * flash->page_size;
+ flash->data = auto_alloc_array(device->machine(), UINT8, flash->size);
+ flash->buffer1 = auto_alloc_array(device->machine(), UINT8, flash->page_size);
+ flash->buffer2 = auto_alloc_array(device->machine(), UINT8, flash->page_size);
+
+ // data
+ state_save_register_item_pointer(device->machine(), "at45dbxx", device->tag(), 0, flash->data, flash->size);
+ // pins
+ state_save_register_item(device->machine(), "at45dbxx", device->tag(), 0, flash->pin.cs);
+ state_save_register_item(device->machine(), "at45dbxx", device->tag(), 0, flash->pin.sck);
+ state_save_register_item(device->machine(), "at45dbxx", device->tag(), 0, flash->pin.si);
+ state_save_register_item(device->machine(), "at45dbxx", device->tag(), 0, flash->pin.so);
+ state_save_register_item(device->machine(), "at45dbxx", device->tag(), 0, flash->pin.wp);
+ state_save_register_item(device->machine(), "at45dbxx", device->tag(), 0, flash->pin.reset);
+ state_save_register_item(device->machine(), "at45dbxx", device->tag(), 0, flash->pin.busy);
+}
+
+static DEVICE_START( at45db041 )
+{
+ common_start(device, TYPE_AT45DB041);
+}
+
+static DEVICE_START( at45db081 )
+{
+ common_start(device, TYPE_AT45DB081);
+}
+
+static DEVICE_START( at45db161 )
+{
+ common_start(device, TYPE_AT45DB161);
+}
+
+static DEVICE_RESET( at45dbxx )
+{
+ at45dbxx_t *flash = get_token(device);
+ _logerror( 1, ("at45dbxx_reset\n"));
+ // mode
+ flash->mode = FLASH_MODE_SI;
+ // command
+ memset( &flash->cmd.data[0], 0, sizeof( flash->cmd.data));
+ flash->cmd.size = 0;
+ // input/output
+ flash->io.data = NULL;
+ flash->io.size = 0;
+ flash->io.pos = 0;
+ // pins
+ flash->pin.cs = 0;
+ flash->pin.sck = 0;
+ flash->pin.si = 0;
+ flash->pin.so = 0;
+ flash->pin.wp = 0;
+ flash->pin.reset = 0;
+ flash->pin.busy = 0;
+ // output
+ flash->so_byte = 0;
+ flash->so_bits = 0;
+ // input
+ flash->si_byte = 0;
+ flash->si_bits = 0;
+}
+
+static UINT8 at45dbxx_read_byte( device_t *device)
+{
+ UINT8 data;
+ at45dbxx_t *flash = get_token(device);
+ // check mode
+ if ((flash->mode != FLASH_MODE_SO) || (!flash->io.data)) return 0;
+ // read byte
+ data = flash->io.data[flash->io.pos++];
+ _logerror( 2, ("at45dbxx_read_byte (%02X) (%03d/%03d)\n", data, flash->io.pos, flash->io.size));
+ if (flash->io.pos == flash->io.size) flash->io.pos = 0;
+ return data;
+}
+
+static void flash_set_io( device_t *device, UINT8* data, UINT32 size, UINT32 pos)
+{
+ at45dbxx_t *flash = get_token(device);
+ flash->io.data = data;
+ flash->io.size = size;
+ flash->io.pos = pos;
+}
+
+static UINT32 flash_get_page_addr( device_t *device)
+{
+ at45dbxx_t *flash = get_token(device);
+ switch (flash->devid)
+ {
+ case 0x18 : return ((flash->cmd.data[1] & 0x0F) << 7) | ((flash->cmd.data[2] & 0xFE) >> 1);
+ case 0x20 : return ((flash->cmd.data[1] & 0x1F) << 7) | ((flash->cmd.data[2] & 0xFE) >> 1);
+ case 0x28 : return ((flash->cmd.data[1] & 0x3F) << 6) | ((flash->cmd.data[2] & 0xFC) >> 2);
+ default : return 0;
+ }
+}
+
+static UINT32 flash_get_byte_addr( device_t *device)
+{
+ at45dbxx_t *flash = get_token(device);
+ switch (flash->devid)
+ {
+ case 0x18 : // fall-through
+ case 0x20 : return ((flash->cmd.data[2] & 0x01) << 8) | ((flash->cmd.data[3] & 0xFF) >> 0);
+ case 0x28 : return ((flash->cmd.data[2] & 0x03) << 8) | ((flash->cmd.data[3] & 0xFF) >> 0);
+ default : return 0;
+ }
+}
+
+static void at45dbxx_write_byte(device_t *device, UINT8 data)
+{
+ at45dbxx_t *flash = get_token(device);
+ // check mode
+ if (flash->mode != FLASH_MODE_SI) return;
+ // process byte
+ if (flash->cmd.size < 8)
+ {
+ UINT8 opcode;
+ _logerror( 2, ("at45dbxx_write_byte (%02X)\n", data));
+ // add to command buffer
+ flash->cmd.data[flash->cmd.size++] = data;
+ // check opcode
+ opcode = flash->cmd.data[0];
+ switch (opcode)
+ {
+ // status register read
+ case FLASH_CMD_57 :
+ {
+ // 8 bits command
+ if (flash->cmd.size == 1)
+ {
+ _logerror( 1, ("at45dbxx opcode %02X - status register read\n", opcode));
+ flash->status = (flash->status & 0xC7) | flash->devid; // 80 = busy / 40 = compare fail
+ flash_set_io( device, &flash->status, 1, 0);
+ flash->mode = FLASH_MODE_SO;
+ flash->cmd.size = 8;
+ }
+ }
+ break;
+ // main memory page to buffer 1 compare
+ case FLASH_CMD_60 :
+ {
+ // 8 bits command + 4 bits reserved + 11 bits page address + 9 bits don't care
+ if (flash->cmd.size == 4)
+ {
+ UINT32 page;
+ UINT8 comp;
+ page = flash_get_page_addr(device);
+ _logerror( 1, ("at45dbxx opcode %02X - main memory page to buffer 1 compare [%04X]\n", opcode, page));
+ comp = memcmp( flash->data + page * flash->page_size, flash->buffer1, flash->page_size) == 0 ? 0 : 1;
+ if (comp) flash->status |= 0x40; else flash->status &= ~0x40;
+ _logerror( 1, ("at45dbxx page compare %s\n", comp ? "failure" : "success"));
+ flash->mode = FLASH_MODE_SI;
+ flash->cmd.size = 8;
+ }
+ }
+ break;
+ // main memory page read
+ case FLASH_CMD_52 :
+ {
+ // 8 bits command + 4 bits reserved + 11 bits page address + 9 bits buffer address + 32 bits don't care
+ if (flash->cmd.size == 8)
+ {
+ UINT32 page, byte;
+ page = flash_get_page_addr(device);
+ byte = flash_get_byte_addr(device);
+ _logerror( 1, ("at45dbxx opcode %02X - main memory page read [%04X/%04X]\n", opcode, page, byte));
+ flash_set_io( device, flash->data + page * flash->page_size, flash->page_size, byte);
+ flash->mode = FLASH_MODE_SO;
+ flash->cmd.size = 8;
+ }
+ }
+ break;
+ // main memory page program through buffer 1
+ case FLASH_CMD_82 :
+ {
+ // 8 bits command + 4 bits reserved + 11 bits page address + 9 bits buffer address
+ if (flash->cmd.size == 4)
+ {
+ UINT32 page, byte;
+ page = flash_get_page_addr(device);
+ byte = flash_get_byte_addr(device);
+ _logerror( 1, ("at45dbxx opcode %02X - main memory page program through buffer 1 [%04X/%04X]\n",opcode, page, byte));
+ flash_set_io( device, flash->buffer1, flash->page_size, byte);
+ memset( flash->buffer1, 0xFF, flash->page_size);
+ flash->mode = FLASH_MODE_SI;
+ flash->cmd.size = 8;
+ }
+ }
+ break;
+ // other
+ default :
+ {
+ _logerror( 1, ("at45dbxx opcode %02X - unknown\n", opcode));
+ flash->cmd.data[0] = 0;
+ flash->cmd.size = 0;
+ }
+ break;
+ }
+ }
+ else
+ {
+ _logerror( 2, ("at45dbxx_write_byte (%02X) (%03d/%03d)\n", data, flash->io.pos + 1, flash->io.size));
+ // store byte
+ flash->io.data[flash->io.pos] = data;
+ flash->io.pos++;
+ if (flash->io.pos == flash->io.size) flash->io.pos = 0;
+ }
+}
+
+int at45dbxx_pin_so( device_t *device)
+{
+ at45dbxx_t *flash = get_token(device);
+ if (flash->pin.cs == 0) return 0;
+ return flash->pin.so;
+}
+
+void at45dbxx_pin_si(device_t *device, int data)
+{
+ at45dbxx_t *flash = get_token(device);
+ if (flash->pin.cs == 0) return;
+ flash->pin.si = data;
+}
+
+void at45dbxx_pin_cs(device_t *device, int data)
+{
+ at45dbxx_t *flash = get_token(device);
+ // check if changed
+ if (flash->pin.cs == data) return;
+ // cs low-to-high
+ if (data != 0)
+ {
+ // complete program command
+ if ((flash->cmd.size >= 4) && (flash->cmd.data[0] == FLASH_CMD_82))
+ {
+ UINT32 page, byte;
+ page = flash_get_page_addr(device);
+ byte = flash_get_byte_addr(device);
+ _logerror( 1, ("at45dbxx - program data stored in buffer 1 into selected page in main memory [%04X/%04X]\n", page, byte));
+ memcpy( flash->data + page * flash->page_size, flash->buffer1, flash->page_size);
+ }
+ // reset
+ DEVICE_RESET_CALL( at45dbxx );
+ }
+ // save cs
+ flash->pin.cs = data;
+}
+
+void at45dbxx_pin_sck(device_t *device, int data)
+{
+ at45dbxx_t *flash = get_token(device);
+ // check if changed
+ if (flash->pin.sck == data) return;
+ // sck high-to-low
+ if (data == 0)
+ {
+ // output (part 1)
+ if (flash->so_bits == 8)
+ {
+ flash->so_bits = 0;
+ flash->so_byte = at45dbxx_read_byte(device);
+ }
+ // input
+ if (flash->pin.si) flash->si_byte = flash->si_byte | (1 << flash->si_bits);
+ flash->si_bits++;
+ if (flash->si_bits == 8)
+ {
+ flash->si_bits = 0;
+ at45dbxx_write_byte( device, flash->si_byte);
+ flash->si_byte = 0;
+ }
+ // output (part 2)
+ flash->pin.so = (flash->so_byte >> flash->so_bits) & 1;
+ flash->so_bits++;
+ }
+ // save sck
+ flash->pin.sck = data;
+}
+
+void at45dbxx_load(device_t *device, emu_file *file)
+{
+ at45dbxx_t *flash = get_token(device);
+ _logerror( 0, ("at45dbxx_load (%p)\n", file));
+ file->read(flash->data, flash->size);
+}
+
+void at45dbxx_save(device_t *device, emu_file *file)
+{
+ at45dbxx_t *flash = get_token(device);
+ _logerror( 0, ("at45dbxx_save (%p)\n", file));
+ file->write(flash->data, flash->size);
+}
+
+#ifdef UNUSED_FUNCTION
+NVRAM_HANDLER( at45dbxx )
+{
+ _logerror( 0, ("nvram_handler_at45dbxx (%p/%d)\n", file, read_or_write));
+ if (read_or_write)
+ {
+ at45dbxx_save( file);
+ }
+ else
+ {
+ if (file)
+ {
+ at45dbxx_load( file);
+ }
+ else
+ {
+ memset( flash->data, 0xFF, flash->size);
+ }
+ }
+}
+#endif
+
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO( at45db041 )
+-------------------------------------------------*/
+
+DEVICE_GET_INFO( at45db041 )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(at45dbxx_t); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(at45db041); break;
+ case DEVINFO_FCT_STOP: /* Nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(at45dbxx); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "AT45DB041"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "AT45DBxx"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: /* Nothing */ break;
+ }
+}
+
+DEVICE_GET_INFO( at45db081 )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "AT45DB081"); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(at45db081); break;
+
+ default: DEVICE_GET_INFO_CALL(at45db041); break;
+ }
+}
+
+DEVICE_GET_INFO( at45db161 )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "AT45DB161"); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(at45db161); break;
+
+ default: DEVICE_GET_INFO_CALL(at45db041); break;
+ }
+}
+
+DEFINE_LEGACY_DEVICE(AT45DB041, at45db041);
+DEFINE_LEGACY_DEVICE(AT45DB081, at45db081);
+DEFINE_LEGACY_DEVICE(AT45DB161, at45db161);