summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu
diff options
context:
space:
mode:
author davidhay <davidhay@localhost>2009-04-02 20:48:28 +0000
committer davidhay <davidhay@localhost>2009-04-02 20:48:28 +0000
commit10f7d87f23c726bf445fb92db179714c842ae2d1 (patch)
treea707f0a27295f5fea6f0303eb6b18fd791473f2e /src/emu
parent84233100254afb64a781709254d6b63745502b92 (diff)
Eeprom and i2c implemeted as devices [Samuele Zannoli]
The eepromdev.* files are the device impementation for the serial eeproms The i2cmemdev.* files are the device impementation for the i2c memory These aren't currently hooked up I'm committing this so that the Naomi changes don't accidentally get applied. The idea seems good, but they conflict with the current driver and I'd rather not have them accidentally slipping in and breaking things just yet.
Diffstat (limited to 'src/emu')
-rw-r--r--src/emu/emu.mak2
-rw-r--r--src/emu/machine/eepromdev.c456
-rw-r--r--src/emu/machine/eepromdev.h72
-rw-r--r--src/emu/machine/i2cmemdev.c492
-rw-r--r--src/emu/machine/i2cmemdev.h46
5 files changed, 1068 insertions, 0 deletions
diff --git a/src/emu/emu.mak b/src/emu/emu.mak
index 716014619be..0c6268a879d 100644
--- a/src/emu/emu.mak
+++ b/src/emu/emu.mak
@@ -132,10 +132,12 @@ EMUMACHINEOBJS = \
$(EMUMACHINE)/ds2401.o \
$(EMUMACHINE)/ds2404.o \
$(EMUMACHINE)/eeprom.o \
+ $(EMUMACHINE)/eepromdev.o \
$(EMUMACHINE)/f3853.o \
$(EMUMACHINE)/generic.o \
$(EMUMACHINE)/i8243.o \
$(EMUMACHINE)/i2cmem.o \
+ $(EMUMACHINE)/i2cmemdev.o \
$(EMUMACHINE)/idectrl.o \
$(EMUMACHINE)/intelfsh.o \
$(EMUMACHINE)/latch8.o \
diff --git a/src/emu/machine/eepromdev.c b/src/emu/machine/eepromdev.c
new file mode 100644
index 00000000000..71723a73b6f
--- /dev/null
+++ b/src/emu/machine/eepromdev.c
@@ -0,0 +1,456 @@
+/*
+ * Serial eeproms
+ *
+ */
+
+#include "driver.h"
+#include "machine/eepromdev.h"
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+#define SERIAL_BUFFER_LENGTH 40
+#define MEMORY_SIZE 1024
+
+typedef struct _eeprom_state eeprom_state;
+struct _eeprom_state
+{
+ const eeprom_interface *intf;
+ int serial_count;
+ UINT8 serial_buffer[SERIAL_BUFFER_LENGTH];
+ UINT8 eeprom_data[MEMORY_SIZE];
+ int eeprom_data_bits;
+ int eeprom_read_address;
+ int eeprom_clock_count;
+ int latch,reset_line,clock_line,sending;
+ int locked;
+ int reset_delay;
+};
+
+/*-------------------------------------------------
+ get_safe_token - makes sure that the passed
+ in device is, in fact, an I2C memory
+-------------------------------------------------*/
+
+INLINE eeprom_state *get_safe_token(const device_config *device)
+{
+ assert( device != NULL );
+ assert( device->token != NULL );
+ assert( device->type == EEPROM );
+
+ return (eeprom_state *)device->token;
+}
+
+/*
+ eeprom_command_match:
+
+ Try to match the first (len) digits in the EEPROM serial buffer
+ string (*buf) with an EEPROM command string (*cmd).
+ Return non zero if a match was found.
+
+ The serial buffer only contains '0' or '1' (e.g. "1001").
+ The command can contain: '0' or '1' or these wildcards:
+
+ 'x' : match both '0' and '1'
+ "*1": match "1", "01", "001", "0001" etc.
+ "*0": match "0", "10", "110", "1110" etc.
+
+ Note: (cmd) may be NULL. Return 0 (no match) in this case.
+*/
+static int eeprom_command_match(const char *buf, const char *cmd, int len)
+{
+ if ( cmd == 0 ) return 0;
+ if ( len == 0 ) return 0;
+
+ for (;len>0;)
+ {
+ char b = *buf;
+ char c = *cmd;
+
+ if ((b==0) || (c==0))
+ return (b==c);
+
+ switch ( c )
+ {
+ case '0':
+ case '1':
+ if (b != c) return 0;
+ case 'X':
+ case 'x':
+ buf++;
+ len--;
+ cmd++;
+ break;
+
+ case '*':
+ c = cmd[1];
+ switch( c )
+ {
+ case '0':
+ case '1':
+ if (b == c) { cmd++; }
+ else { buf++; len--; }
+ break;
+ default: return 0;
+ }
+ }
+ }
+ return (*cmd==0);
+}
+
+
+const eeprom_interface eepromdev_interface_93C46 =
+{
+ 6, // address bits 6
+ 16, // data bits 16
+ "*110", // read 1 10 aaaaaa
+ "*101", // write 1 01 aaaaaa dddddddddddddddd
+ "*111", // erase 1 11 aaaaaa
+ "*10000xxxx", // lock 1 00 00xxxx
+ "*10011xxxx", // unlock 1 00 11xxxx
+ 1,
+// "*10001xxxx" // write all 1 00 01xxxx dddddddddddddddd
+// "*10010xxxx" // erase all 1 00 10xxxx
+};
+
+const eeprom_interface eepromdev_interface_93C66B =
+{
+ 8, /* address bits */
+ 16, /* data bits */
+ "*110", /* read command */
+ "*101", /* write command */
+ "*111", /* erase command */
+ "*10000xxxxxx", /* lock command */
+ "*10011xxxxxx", /* unlock command */
+ 1,
+// "*10001xxxxxx", /* write all */
+// "*10010xxxxxx", /* erase all */
+};
+
+static void eeprom_write(eeprom_state *c, int bit)
+{
+ LOG(("EEPROM write bit %d\n",bit));
+
+ if (c->serial_count >= SERIAL_BUFFER_LENGTH-1)
+ {
+ logerror("error: EEPROM serial buffer overflow\n");
+ return;
+ }
+
+ c->serial_buffer[c->serial_count++] = (bit ? '1' : '0');
+ c->serial_buffer[c->serial_count] = 0; /* nul terminate so we can treat it as a string */
+
+ if ( (c->serial_count > c->intf->address_bits) &&
+ eeprom_command_match((char*)(c->serial_buffer),c->intf->cmd_read,strlen((char*)(c->serial_buffer))-c->intf->address_bits) )
+ {
+ int i,address;
+
+ address = 0;
+ for (i = c->serial_count-c->intf->address_bits;i < c->serial_count;i++)
+ {
+ address <<= 1;
+ if (c->serial_buffer[i] == '1') address |= 1;
+ }
+ if (c->intf->data_bits == 16)
+ c->eeprom_data_bits = (c->eeprom_data[2*address+0] << 8) + c->eeprom_data[2*address+1];
+ else
+ c->eeprom_data_bits = c->eeprom_data[address];
+ c->eeprom_read_address = address;
+ c->eeprom_clock_count = 0;
+ c->sending = 1;
+ c->serial_count = 0;
+logerror("EEPROM read %04x from address %02x\n",c->eeprom_data_bits,address);
+ }
+ else if ( (c->serial_count > c->intf->address_bits) &&
+ eeprom_command_match((char*)(c->serial_buffer),c->intf->cmd_erase,strlen((char*)(c->serial_buffer))-c->intf->address_bits) )
+ {
+ int i,address;
+
+ address = 0;
+ for (i = c->serial_count-c->intf->address_bits;i < c->serial_count;i++)
+ {
+ address <<= 1;
+ if (c->serial_buffer[i] == '1') address |= 1;
+ }
+logerror("EEPROM erase address %02x\n",address);
+ if (c->locked == 0)
+ {
+ if (c->intf->data_bits == 16)
+ {
+ c->eeprom_data[2*address+0] = 0x00;
+ c->eeprom_data[2*address+1] = 0x00;
+ }
+ else
+ c->eeprom_data[address] = 0x00;
+ }
+ else
+logerror("Error: EEPROM is locked\n");
+ c->serial_count = 0;
+ }
+ else if ( (c->serial_count > (c->intf->address_bits + c->intf->data_bits)) &&
+ eeprom_command_match((char*)(c->serial_buffer),c->intf->cmd_write,strlen((char*)(c->serial_buffer))-(c->intf->address_bits + c->intf->data_bits)) )
+ {
+ int i,address,data;
+
+ address = 0;
+ for (i = c->serial_count-c->intf->data_bits-c->intf->address_bits;i < (c->serial_count-c->intf->data_bits);i++)
+ {
+ address <<= 1;
+ if (c->serial_buffer[i] == '1') address |= 1;
+ }
+ data = 0;
+ for (i = c->serial_count-c->intf->data_bits;i < c->serial_count;i++)
+ {
+ data <<= 1;
+ if (c->serial_buffer[i] == '1') data |= 1;
+ }
+logerror("EEPROM write %04x to address %02x\n",data,address);
+ if (c->locked == 0)
+ {
+ if (c->intf->data_bits == 16)
+ {
+ c->eeprom_data[2*address+0] = data >> 8;
+ c->eeprom_data[2*address+1] = data & 0xff;
+ }
+ else
+ c->eeprom_data[address] = data;
+ }
+ else
+logerror("Error: EEPROM is locked\n");
+ c->serial_count = 0;
+ }
+ else if ( eeprom_command_match((char*)(c->serial_buffer),c->intf->cmd_lock,strlen((char*)(c->serial_buffer))) )
+ {
+logerror("EEPROM lock\n");
+ c->locked = 1;
+ c->serial_count = 0;
+ }
+ else if ( eeprom_command_match((char*)(c->serial_buffer),c->intf->cmd_unlock,strlen((char*)(c->serial_buffer))) )
+ {
+logerror("EEPROM unlock\n");
+ c->locked = 0;
+ c->serial_count = 0;
+ }
+}
+
+static void eeprom_reset(eeprom_state *c)
+{
+ if (c->serial_count)
+ logerror("EEPROM reset, buffer = %s\n",c->serial_buffer);
+
+ c->serial_count = 0;
+ c->sending = 0;
+ c->reset_delay = c->intf->reset_delay; /* delay a little before returning setting data to 1 (needed by wbeachvl) */
+}
+
+
+void eepromdev_write_bit(const device_config *device, int bit)
+{
+ eeprom_state *c = get_safe_token(device);
+
+ LOG(("write bit %d\n",bit));
+ c->latch = bit;
+}
+
+int eepromdev_read_bit(const device_config *device)
+{
+ eeprom_state *c = get_safe_token(device);
+ int res;
+
+ if (c->sending)
+ res = (c->eeprom_data_bits >> c->intf->data_bits) & 1;
+ else
+ {
+ if (c->reset_delay > 0)
+ {
+ /* this is needed by wbeachvl */
+ c->reset_delay--;
+ res = 0;
+ }
+ else
+ res = 1;
+ }
+
+ LOG(("read bit %d\n",res));
+
+ return res;
+}
+
+/*CUSTOM_INPUT( eeprom_bit_r )
+{
+ return eeprom_read_bit();
+}*/
+
+void eepromdev_set_cs_line(const device_config *device, int state)
+{
+ eeprom_state *c = get_safe_token(device);
+
+ LOG(("set reset line %d\n",state));
+ c->reset_line = state;
+
+ if (c->reset_line != CLEAR_LINE)
+ eeprom_reset(c);
+}
+
+void eepromdev_set_clock_line(const device_config *device, int state)
+{
+ eeprom_state *c = get_safe_token(device);
+
+ LOG(("set clock line %d\n",state));
+ if (state == PULSE_LINE || (c->clock_line == CLEAR_LINE && state != CLEAR_LINE))
+ {
+ if (c->reset_line == CLEAR_LINE)
+ {
+ if (c->sending)
+ {
+ if (c->eeprom_clock_count == c->intf->data_bits && c->intf->enable_multi_read)
+ {
+ c->eeprom_read_address = (c->eeprom_read_address + 1) & ((1 << c->intf->address_bits) - 1);
+ if (c->intf->data_bits == 16)
+ c->eeprom_data_bits = (c->eeprom_data[2*c->eeprom_read_address+0] << 8) + c->eeprom_data[2*c->eeprom_read_address+1];
+ else
+ c->eeprom_data_bits = c->eeprom_data[c->eeprom_read_address];
+ c->eeprom_clock_count = 0;
+logerror("EEPROM read %04x from address %02x\n",c->eeprom_data_bits,c->eeprom_read_address);
+ }
+ c->eeprom_data_bits = (c->eeprom_data_bits << 1) | 1;
+ c->eeprom_clock_count++;
+ }
+ else
+ eeprom_write(c,c->latch);
+ }
+ }
+
+ c->clock_line = state;
+}
+
+
+void eepromdev_load(const device_config *device, mame_file *f)
+{
+ eeprom_state *c = get_safe_token(device);
+
+ mame_fread(f, c->eeprom_data, (1 << c->intf->address_bits) * c->intf->data_bits / 8);
+}
+
+void eepromdev_save(const device_config *device, mame_file *f)
+{
+ eeprom_state *c = get_safe_token(device);
+
+ mame_fwrite(f, c->eeprom_data, (1 << c->intf->address_bits) * c->intf->data_bits / 8);
+}
+
+void eepromdev_set_data(const device_config *device, const UINT8 *data, int length)
+{
+ eeprom_state *c = get_safe_token(device);
+
+ assert(length <= ((1 << c->intf->address_bits) * c->intf->data_bits / 8));
+ memcpy(c->eeprom_data, data, length);
+}
+
+void *eepromdev_get_data_pointer(const device_config *device, UINT32 *length, UINT32 *size)
+{
+ eeprom_state *c = get_safe_token(device);
+
+ if (length != NULL && c->intf != NULL)
+ *length = 1 << c->intf->address_bits;
+ if (size != NULL && c->intf != NULL)
+ *size = c->intf->data_bits / 8;
+
+ return c->eeprom_data;
+}
+
+static DEVICE_NVRAM( eeprom )
+{
+ const eeprom_config *config = device->inline_config;
+
+ if (read_or_write)
+ eepromdev_save(device, file);
+ else
+ if (file)
+ eepromdev_load(device, file);
+ else
+ if ((config->default_data != NULL) && (config->default_data_size != 0))
+ eepromdev_set_data(device, config->default_data, config->default_data_size);
+}
+
+static DEVICE_START(eeprom)
+{
+ eeprom_state *c = get_safe_token(device);
+ const eeprom_config *config;
+
+ /* validate some basic stuff */
+ assert(device != NULL);
+ assert(device->inline_config != NULL);
+ assert(device->machine != NULL);
+ assert(device->machine->config != NULL);
+
+ config = device->inline_config;
+
+ c->intf = config->pinterface;
+
+ if ((1 << c->intf->address_bits) * c->intf->data_bits / 8 > MEMORY_SIZE)
+ {
+ fatalerror("EEPROM larger than eepromdev.c allows");
+ }
+
+ memset(c->eeprom_data, 0xff, (1 << c->intf->address_bits) * c->intf->data_bits / 8);
+ if ((config->default_data != NULL) && (config->default_data_size != 0))
+ eepromdev_set_data(device, config->default_data, config->default_data_size);
+ c->serial_count = 0;
+ c->latch = 0;
+ c->reset_line = ASSERT_LINE;
+ c->clock_line = ASSERT_LINE;
+ c->eeprom_read_address = 0;
+ c->sending = 0;
+ if (c->intf->cmd_unlock) c->locked = 1;
+ else c->locked = 0;
+
+ state_save_register_device_item_pointer( device, 0, c->eeprom_data, MEMORY_SIZE);
+ state_save_register_device_item_pointer( device, 0, c->serial_buffer, SERIAL_BUFFER_LENGTH);
+ state_save_register_device_item( device, 0, c->clock_line);
+ state_save_register_device_item( device, 0, c->reset_line);
+ state_save_register_device_item( device, 0, c->locked);
+ state_save_register_device_item( device, 0, c->serial_count);
+ state_save_register_device_item( device, 0, c->latch);
+ state_save_register_device_item( device, 0, c->reset_delay);
+ state_save_register_device_item( device, 0, c->eeprom_clock_count);
+ state_save_register_device_item( device, 0, c->eeprom_data_bits);
+ state_save_register_device_item( device, 0, c->eeprom_read_address);
+}
+
+static DEVICE_RESET(eeprom)
+{
+}
+
+static DEVICE_SET_INFO(eeprom)
+{
+ switch (state)
+ {
+ /* no parameters to set */
+ }
+}
+
+DEVICE_GET_INFO(eeprom)
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(eeprom_state); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(eeprom_config); break;
+ case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_PERIPHERAL; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_SET_INFO: info->set_info = DEVICE_SET_INFO_NAME(eeprom); break;
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(eeprom); break;
+ case DEVINFO_FCT_STOP: /* nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(eeprom); break;
+ case DEVINFO_FCT_NVRAM: info->nvram = DEVICE_NVRAM_NAME(eeprom); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "EEPROM"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "EEPROM"); 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: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
+ }
+}
diff --git a/src/emu/machine/eepromdev.h b/src/emu/machine/eepromdev.h
new file mode 100644
index 00000000000..cbb96dfb61c
--- /dev/null
+++ b/src/emu/machine/eepromdev.h
@@ -0,0 +1,72 @@
+/*
+ * Serial eeproms
+ *
+ */
+
+#if !defined( EEPROMDEV_H )
+#define EEPROMDEV_H ( 1 )
+
+typedef struct _eeprom_interface eeprom_interface;
+struct _eeprom_interface
+{
+ int address_bits; /* EEPROM has 2^address_bits cells */
+ int data_bits; /* every cell has this many bits (8 or 16) */
+ const char *cmd_read; /* read command string, e.g. "0110" */
+ const char *cmd_write; /* write command string, e.g. "0111" */
+ const char *cmd_erase; /* erase command string, or 0 if n/a */
+ const char *cmd_lock; /* lock command string, or 0 if n/a */
+ const char *cmd_unlock; /* unlock command string, or 0 if n/a */
+ int enable_multi_read; /* set to 1 to enable multiple values to be read from one read command */
+ int reset_delay; /* number of times eeprom_read_bit() should return 0 after a reset, */
+ /* before starting to return 1. */
+};
+
+typedef struct _eeprom_config eeprom_config;
+struct _eeprom_config
+{
+ eeprom_interface *pinterface;
+ UINT8 *default_data;
+ int default_data_size;
+};
+
+/* 93C46 */
+extern const eeprom_interface eepromdev_interface_93C46;
+
+/* 93C66B */
+extern const eeprom_interface eepromdev_interface_93C66B;
+
+#define EEPROM DEVICE_GET_INFO_NAME(eeprom)
+DEVICE_GET_INFO(eeprom);
+
+#define MDRV_EEPROM_ADD(_tag, _interface, _data_size, _data) \
+ MDRV_DEVICE_ADD(_tag, EEPROM, 0) \
+ MDRV_DEVICE_CONFIG_DATAPTR(eeprom_config, pinterface, &_interface) \
+ MDRV_EEPROM_DATA(_data, _data_size)
+
+#define MDRV_EEPROM_93C46_ADD(_tag, _data_size, _data) \
+ MDRV_EEPROM_ADD(_tag, eepromdev_interface_93C46, _data_size, _data)
+
+#define MDRV_EEPROM_93C66B_ADD(_tag, _data_size, _data) \
+ MDRV_EEPROM_ADD(_tag, eepromdev_interface_93C66B, _data_size, _data)
+
+#define MDRV_EEPROM_DATA(_data, _size) \
+ MDRV_DEVICE_CONFIG_DATAPTR(eeprom_config, default_data, &_data) \
+ MDRV_DEVICE_CONFIG_DATA32(eeprom_config, default_data_size, _size)
+
+#define MDRV_EEPROM_REMOVE(_tag) \
+ MDRV_DEVICE_REMOVE(_tag, EEPROM)
+
+
+void eepromdev_write_bit( const device_config *device, int bit );
+int eepromdev_read_bit( const device_config *device );
+//CUSTOM_INPUT( eeprom_bit_r );
+void eepromdev_set_cs_line( const device_config *device, int state );
+void eepromdev_set_clock_line( const device_config *device, int state );
+
+void eepromdev_load( const device_config *device, mame_file *file );
+void eepromdev_save( const device_config *device, mame_file *file );
+
+void eepromdev_set_data( const device_config *device, const UINT8 *data, int length );
+void *eepromdev_get_data_pointer( const device_config *device, UINT32 *length, UINT32 *size );
+
+#endif
diff --git a/src/emu/machine/i2cmemdev.c b/src/emu/machine/i2cmemdev.c
new file mode 100644
index 00000000000..cc0f98d3654
--- /dev/null
+++ b/src/emu/machine/i2cmemdev.c
@@ -0,0 +1,492 @@
+/*
+ I2C Memory
+
+Generic ram/rom/eeprom/flash on an i2c bus. Supports specifying the slave address,
+the data size & the page size for writing.
+
+inputs:
+ e0,e1,e2 lower 3 bits of the slave address
+ sda serial data
+ scl serial clock
+ wc write protect
+
+outputs:
+ sda serial data
+
+The memory address is only 8 bits, devices larger than this have multiple slave addresses.
+The top five address bits are set at manufacture time, two values are standard.
+Up to 4096 bytes can be addressed.
+
+*/
+
+#include "driver.h"
+#include "machine/i2cmemdev.h"
+
+#define VERBOSE_LEVEL ( 0 )
+
+INLINE void ATTR_PRINTF(3,4) verboselog( running_machine *machine, int n_level, const char *s_fmt, ... )
+{
+ if( VERBOSE_LEVEL >= n_level )
+ {
+ va_list v;
+ char buf[ 32768 ];
+ va_start( v, s_fmt );
+ vsprintf( buf, s_fmt, v );
+ va_end( v );
+ logerror( "%s: %s", cpuexec_describe_context(machine), buf );
+ }
+}
+
+#define STATE_IDLE ( 0 )
+#define STATE_DEVSEL ( 1 )
+#define STATE_BYTEADDR ( 2 )
+#define STATE_DATAIN ( 3 )
+#define STATE_DATAOUT ( 4 )
+
+#define DEVSEL_RW ( 1 )
+#define DEVSEL_ADDRESS ( 0xfe )
+
+typedef struct _i2cmem_state i2cmem_state;
+struct _i2cmem_state
+{
+ int slave_address;
+ int scl;
+ int sdaw;
+ int e0;
+ int e1;
+ int e2;
+ int wc;
+ int sdar;
+ int state;
+ int bits;
+ int shift;
+ int devsel;
+ int byteaddr;
+ unsigned char *data;
+ int data_size;
+ unsigned char *page;
+ int page_offset;
+ int page_size;
+ int readmode;
+};
+
+/*-------------------------------------------------
+ get_safe_token - makes sure that the passed
+ in device is, in fact, an I2C memory
+-------------------------------------------------*/
+
+INLINE i2cmem_state *get_safe_token( const device_config *device )
+{
+ assert( device != NULL );
+ assert( device->token != NULL );
+ assert( device->type == I2CMEM );
+
+ return (i2cmem_state *)device->token;
+}
+
+static int select_device( i2cmem_state *c )
+{
+ int device = ( c->slave_address & 0xf0 ) | ( c->e2 << 3 ) | ( c->e1 << 2 ) | ( c->e0 << 1 );
+ int mask = DEVSEL_ADDRESS & ~( ( c->data_size - 1 ) >> 7 );
+
+ if( ( c->devsel & mask ) == ( device & mask ) )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int data_offset( i2cmem_state *c )
+{
+ return ( ( ( c->devsel << 7 ) & 0xff00 ) | ( c->byteaddr & 0xff ) ) & ( c->data_size - 1 );
+}
+
+void i2cmemdev_write( const device_config *device, int line, int data )
+{
+ i2cmem_state *c = get_safe_token( device );
+
+ switch( line )
+ {
+ case I2CMEM_E0:
+ if( c->e0 != data )
+ {
+ c->e0 = data;
+ verboselog( device->machine, 2, "i2cmem_write( I2CMEM_E0, %d )\n", c->e0 );
+ }
+ break;
+
+ case I2CMEM_E1:
+ if( c->e1 != data )
+ {
+ c->e1 = data;
+ verboselog( device->machine, 2, "i2cmem_write( I2CMEM_E1, %d )\n", c->e1 );
+ }
+ break;
+
+ case I2CMEM_E2:
+ if( c->e2 != data )
+ {
+ c->e2 = data;
+ verboselog( device->machine, 2, "i2cmem_write( I2CMEM_E2, %d )\n", c->e2 );
+ }
+ break;
+
+ case I2CMEM_SDA:
+ if( c->sdaw != data )
+ {
+ c->sdaw = data;
+ verboselog( device->machine, 2, "i2cmem_write( I2CMEM_SDA, %d )\n", c->sdaw );
+
+ if( c->scl )
+ {
+ if( c->sdaw )
+ {
+ verboselog( device->machine, 1, "i2cmem stop\n");
+ c->state = STATE_IDLE;
+ c->byteaddr = 0;
+ }
+ else
+ {
+ verboselog( device->machine, 2, "i2cmem start\n");
+ c->state = STATE_DEVSEL;
+ c->bits = 0;
+ }
+
+ c->sdar = 1;
+ }
+ }
+ break;
+
+ case I2CMEM_SCL:
+ if( c->scl != data )
+ {
+ c->scl = data;
+ verboselog( device->machine, 2, "i2cmem_write( I2CMEM_SCL, %d )\n", c->scl );
+
+ switch( c->state )
+ {
+ case STATE_DEVSEL:
+ case STATE_BYTEADDR:
+ case STATE_DATAIN:
+ if( c->bits < 8 )
+ {
+ if( c->scl )
+ {
+ c->shift = ( ( c->shift << 1 ) | c->sdaw ) & 0xff;
+ c->bits++;
+ }
+ }
+ else
+ {
+ if( c->scl )
+ {
+ switch( c->state )
+ {
+ case STATE_DEVSEL:
+ c->devsel = c->shift;
+
+ if( !select_device( c ) )
+ {
+ verboselog( device->machine, 1, "i2cmem devsel %02x: not this device\n", c->devsel );
+ c->state = STATE_IDLE;
+ }
+ else if( ( c->devsel & DEVSEL_RW ) == 0 )
+ {
+ verboselog( device->machine, 1, "i2cmem devsel %02x: write\n", c->devsel );
+ c->state = STATE_BYTEADDR;
+ }
+ else
+ {
+ verboselog( device->machine, 1, "i2cmem devsel %02x: read\n", c->devsel );
+ c->state = STATE_DATAOUT;
+ }
+ break;
+
+ case STATE_BYTEADDR:
+ c->byteaddr = c->shift;
+ c->page_offset = 0;
+
+ verboselog( device->machine, 1, "i2cmem byteaddr %02x\n", c->byteaddr );
+
+ c->state = STATE_DATAIN;
+ break;
+
+ case STATE_DATAIN:
+ if( c->wc )
+ {
+ verboselog( device->machine, 0, "i2cmem write not enabled\n");
+ c->state = STATE_IDLE;
+ }
+ else if( c->page_size > 0 )
+ {
+ c->page[ c->page_offset ] = c->shift;
+ verboselog( device->machine, 1, "i2cmem page[ %04x ] <- %02x\n", c->page_offset, c->page[ c->page_offset ] );
+
+ c->page_offset++;
+ if( c->page_offset == c->page_size )
+ {
+ int offset = data_offset( c ) & ~( c->page_size - 1 );
+
+ memcpy( &c->data[ offset ], c->page, c->page_size );
+ verboselog( device->machine, 1, "i2cmem data[ %04x to %04x ] = page\n", offset, offset + c->page_size - 1 );
+
+ c->page_offset = 0;
+ }
+ }
+ else
+ {
+ int offset = data_offset( c );
+
+ c->data[ offset ] = c->shift;
+ verboselog( device->machine, 1, "i2cmem data[ %04x ] <- %02x\n", offset, c->data[ offset ] );
+
+ c->byteaddr++;
+ }
+ break;
+ }
+
+ c->bits++;
+ }
+ else
+ {
+ if( c->bits == 8 )
+ {
+ c->sdar = 0;
+ }
+ else
+ {
+ c->bits = 0;
+ c->sdar = 1;
+ }
+ }
+ }
+ break;
+
+ case STATE_DATAOUT:
+ if( c->bits < 8 )
+ {
+ if( c->scl )
+ {
+ if( c->bits == 0 )
+ {
+ int offset = data_offset( c );
+
+ c->shift = c->data[ offset ];
+ verboselog( device->machine, 1, "i2cmem data[ %04x ] -> %02x\n", offset, c->data[ offset ] );
+ c->byteaddr++;
+ }
+
+ c->sdar = ( c->shift >> 7 ) & 1;
+
+ c->shift = ( c->shift << 1 ) & 0xff;
+ c->bits++;
+ }
+ }
+ else
+ {
+ if( c->scl )
+ {
+ if( c->sdaw )
+ {
+ verboselog( device->machine, 1, "i2cmem sleep\n");
+ c->state = STATE_IDLE;
+ }
+
+ c->bits++;
+ }
+ else
+ {
+ if( c->bits == 8 )
+ {
+ c->sdar = 1;
+ }
+ else
+ {
+ c->bits = 0;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case I2CMEM_WC:
+ if( c->wc != data )
+ {
+ c->wc = data;
+ verboselog( device->machine, 2, "i2cmem_write( I2CMEM_WC, %d )\n", c->wc );
+ }
+ break;
+
+ default:
+ verboselog( device->machine, 0, "i2cmem_write( %d, %d ) invalid line\n", line, data );
+ break;
+ }
+}
+
+int i2cmemdev_read( const device_config *device, int line )
+{
+ i2cmem_state *c = get_safe_token( device );
+
+ switch( line )
+ {
+ case I2CMEM_SDA:
+ if (c->readmode == 0)
+ {
+ verboselog( device->machine, 2, "i2cmem_read( I2CMEM_SDA ) %d\n", c->sdar & c->sdaw );
+ return c->sdar & c->sdaw;
+ }
+ else
+ {
+ verboselog( device->machine, 2, "i2cmem_read( I2CMEM_SDA ) %d\n", c->sdar );
+ return c->sdar;
+ }
+
+ default:
+ verboselog( device->machine, 0, "i2cmem_read( %d ) invalid line\n", line );
+ break;
+ }
+
+ return 0;
+}
+
+void i2cmemdev_set_read_mode( const device_config *device, int mode )
+{
+ i2cmem_state *c = get_safe_token( device );
+
+ c->readmode = mode;
+}
+
+/*-------------------------------------------------
+ device start callback
+-------------------------------------------------*/
+
+static DEVICE_START( i2cmem )
+{
+ i2cmem_state *c = get_safe_token( device );
+ const i2cmem_config *config;
+ unsigned char *page = NULL;
+
+ /* validate some basic stuff */
+ assert( device != NULL );
+ assert( device->inline_config != NULL );
+ assert( device->machine != NULL );
+ assert( device->machine->config != NULL );
+
+ config = device->inline_config;
+
+ c->scl = 0;
+ c->sdaw = 0;
+ c->e0 = 0;
+ c->e1 = 0;
+ c->e2 = 0;
+ c->wc = 0;
+ c->sdar = 1;
+ c->state = STATE_IDLE;
+ c->bits = 0;
+ c->shift = 0;
+ c->devsel = 0;
+ c->byteaddr = 0;
+ c->readmode = 0;
+
+ if( config != NULL )
+ {
+ c->data = auto_malloc( config->data_size );
+ if( config->data != NULL )
+ memcpy(c->data, config->data, config->data_size);
+
+ if( config->page_size > 0 )
+ page = auto_malloc( config->page_size );
+
+ c->slave_address = config->slave_address;
+ c->data_size = config->data_size;
+ c->page_size = config->page_size;
+ c->page = page;
+ }
+
+ state_save_register_device_item( device, 0, c->scl );
+ state_save_register_device_item( device, 0, c->sdaw );
+ state_save_register_device_item( device, 0, c->e0 );
+ state_save_register_device_item( device, 0, c->e1 );
+ state_save_register_device_item( device, 0, c->e2 );
+ state_save_register_device_item( device, 0, c->wc );
+ state_save_register_device_item( device, 0, c->sdar );
+ state_save_register_device_item( device, 0, c->state );
+ state_save_register_device_item( device, 0, c->bits );
+ state_save_register_device_item( device, 0, c->shift );
+ state_save_register_device_item( device, 0, c->devsel );
+ state_save_register_device_item( device, 0, c->byteaddr );
+ state_save_register_device_item_pointer( device, 0, c->data, c->data_size );
+ state_save_register_device_item( device, 0, c->readmode );
+}
+
+/*-------------------------------------------------
+ device reset callback
+-------------------------------------------------*/
+
+static DEVICE_RESET( i2cmem )
+{
+}
+
+static DEVICE_NVRAM( i2cmem )
+{
+ const i2cmem_config *config = device->inline_config;
+ i2cmem_state *c = get_safe_token( device );
+
+ if( read_or_write )
+ {
+ mame_fwrite( file, c->data, c->data_size );
+ }
+ else if( file )
+ {
+ mame_fread( file, c->data, c->data_size );
+ }
+ else
+ {
+ if( config->data != NULL )
+ memcpy( c->data, config->data, config->data_size );
+ }
+}
+
+/*-------------------------------------------------
+ device set info callback
+-------------------------------------------------*/
+
+static DEVICE_SET_INFO( i2cmem )
+{
+ switch ( state )
+ {
+ /* no parameters to set */
+ }
+}
+
+/*-------------------------------------------------
+ device get info callback
+-------------------------------------------------*/
+
+DEVICE_GET_INFO( i2cmem )
+{
+ switch ( state )
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof( i2cmem_state ); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof( i2cmem_config ); break;
+ case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_PERIPHERAL; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_SET_INFO: info->set_info = DEVICE_SET_INFO_NAME( i2cmem ); break;
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( i2cmem ); break;
+ case DEVINFO_FCT_STOP: /* nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( i2cmem ); break;
+ case DEVINFO_FCT_NVRAM: info->nvram = DEVICE_NVRAM_NAME( i2cmem ); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy( info->s, "I2CMEM" ); break;
+ case DEVINFO_STR_FAMILY: strcpy( info->s, "EEPROM" ); 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: strcpy( info->s, "Copyright Nicola Salmoria and the MAME Team" ); break;
+ }
+}
diff --git a/src/emu/machine/i2cmemdev.h b/src/emu/machine/i2cmemdev.h
new file mode 100644
index 00000000000..bf8939f883a
--- /dev/null
+++ b/src/emu/machine/i2cmemdev.h
@@ -0,0 +1,46 @@
+/*
+ * I2C Memory
+ *
+ */
+
+#if !defined( I2CMEMDEV_H )
+#define I2CMEMDEV_H ( 1 )
+
+#define I2CMEM_E0 ( 1 )
+#define I2CMEM_E1 ( 2 )
+#define I2CMEM_E2 ( 3 )
+#define I2CMEM_SDA ( 5 )
+#define I2CMEM_SCL ( 6 )
+#define I2CMEM_WC ( 7 )
+
+#define I2CMEM_SLAVE_ADDRESS ( 0xa0 )
+#define I2CMEM_SLAVE_ADDRESS_ALT ( 0xb0 )
+
+typedef struct _i2cmem_config i2cmem_config;
+struct _i2cmem_config
+{
+ const int slave_address;
+ const int page_size;
+ const int data_size;
+ const char *data;
+};
+
+#define I2CMEM DEVICE_GET_INFO_NAME(i2cmem)
+DEVICE_GET_INFO(i2cmem);
+
+#define MDRV_I2CMEM_ADD(_tag, _slave_address, _page_size, _data_size, _data) \
+ MDRV_DEVICE_ADD(_tag, I2CMEM, 0) \
+ MDRV_DEVICE_CONFIG_DATA32(i2cmem_config, slave_address, _slave_address) \
+ MDRV_DEVICE_CONFIG_DATA32(i2cmem_config, page_size, _page_size) \
+ MDRV_DEVICE_CONFIG_DATA32(i2cmem_config, data_size, _data_size) \
+ MDRV_DEVICE_CONFIG_DATAPTR(i2cmem_config, data, _data)
+
+#define MDRV_I2CMEM_REMOVE(_tag) \
+ MDRV_DEVICE_REMOVE(_tag, I2CMEM)
+
+
+void i2cmemdev_write( const device_config *device, int line, int data );
+int i2cmemdev_read( const device_config *device, int line );
+void i2cmemdev_set_read_mode( const device_config *device, int mode );
+
+#endif