summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/i2cmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/machine/i2cmem.c')
-rw-r--r--src/emu/machine/i2cmem.c438
1 files changed, 438 insertions, 0 deletions
diff --git a/src/emu/machine/i2cmem.c b/src/emu/machine/i2cmem.c
new file mode 100644
index 00000000000..2764c042256
--- /dev/null
+++ b/src/emu/machine/i2cmem.c
@@ -0,0 +1,438 @@
+/*
+
+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/i2cmem.h"
+
+#define VERBOSE_LEVEL ( 0 )
+
+INLINE void verboselog( 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 );
+ if( cpu_getactivecpu() != -1 )
+ {
+ logerror( "%08x: %s", activecpu_get_pc(), buf );
+ }
+ else
+ {
+ logerror( "(timer) : %s", buf );
+ }
+ }
+}
+
+struct i2cmem_chip
+{
+ 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;
+};
+
+#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 )
+
+static struct i2cmem_chip i2cmem[ I2CMEM_MAXCHIP ];
+
+void i2cmem_init( int chip, int slave_address, int page_size, int data_size, unsigned char *data )
+{
+ struct i2cmem_chip *c;
+ unsigned char *page = NULL;
+
+ if( chip >= I2CMEM_MAXCHIP )
+ {
+ verboselog( 0, "i2cmem_init( %d ) invalid chip\n", chip );
+ return;
+ }
+
+ c = &i2cmem[ chip ];
+
+ if( data == NULL )
+ {
+ data = auto_malloc( data_size );
+ }
+
+ if( page_size > 0 )
+ {
+ page = auto_malloc( page_size );
+ }
+
+ c->slave_address = slave_address;
+ c->data_size = data_size;
+ c->page_size = page_size;
+
+ 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->data = data;
+ c->page = page;
+
+ state_save_register_item( "i2cmem", chip, c->scl );
+ state_save_register_item( "i2cmem", chip, c->sdaw );
+ state_save_register_item( "i2cmem", chip, c->e0 );
+ state_save_register_item( "i2cmem", chip, c->e1 );
+ state_save_register_item( "i2cmem", chip, c->e2 );
+ state_save_register_item( "i2cmem", chip, c->wc );
+ state_save_register_item( "i2cmem", chip, c->sdar );
+ state_save_register_item( "i2cmem", chip, c->state );
+ state_save_register_item( "i2cmem", chip, c->bits );
+ state_save_register_item( "i2cmem", chip, c->shift );
+ state_save_register_item( "i2cmem", chip, c->devsel );
+ state_save_register_item( "i2cmem", chip, c->byteaddr );
+ state_save_register_item_pointer( "i2cmem", chip, c->data, c->data_size );
+}
+
+static int select_device( struct i2cmem_chip *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( struct i2cmem_chip *c )
+{
+ return ( ( ( c->devsel << 7 ) & 0xff00 ) | ( c->byteaddr & 0xff ) ) & ( c->data_size - 1 );
+}
+
+void i2cmem_write( int chip, int line, int data )
+{
+ struct i2cmem_chip *c;
+
+ if( chip >= I2CMEM_MAXCHIP )
+ {
+ verboselog( 0, "i2cmem_write( %d, %d, %d ) invalid chip\n", chip, line, data );
+ return;
+ }
+
+ c = &i2cmem[ chip ];
+
+ data &= 1;
+
+ switch( line )
+ {
+ case I2CMEM_E0:
+ if( c->e0 != data )
+ {
+ c->e0 = data;
+ verboselog( 2, "i2cmem_write( %d, I2CMEM_E0, %d )\n", chip, c->e0 );
+ }
+ break;
+
+ case I2CMEM_E1:
+ if( c->e1 != data )
+ {
+ c->e1 = data;
+ verboselog( 2, "i2cmem_write( %d, I2CMEM_E1, %d )\n", chip, c->e1 );
+ }
+ break;
+
+ case I2CMEM_E2:
+ if( c->e2 != data )
+ {
+ c->e2 = data;
+ verboselog( 2, "i2cmem_write( %d, I2CMEM_E2, %d )\n", chip, c->e2 );
+ }
+ break;
+
+ case I2CMEM_SDA:
+ if( c->sdaw != data )
+ {
+ c->sdaw = data;
+ verboselog( 2, "i2cmem_write( %d, I2CMEM_SDA, %d )\n", chip, c->sdaw );
+
+ if( c->scl )
+ {
+ if( c->sdaw )
+ {
+ verboselog( 1, "i2cmem(%d) stop\n", chip );
+ c->state = STATE_IDLE;
+ c->byteaddr = 0;
+ }
+ else
+ {
+ verboselog( 2, "i2cmem(%d) start\n", chip );
+ c->state = STATE_DEVSEL;
+ c->bits = 0;
+ }
+
+ c->sdar = 1;
+ }
+ }
+ break;
+
+ case I2CMEM_SCL:
+ if( c->scl != data )
+ {
+ c->scl = data;
+ verboselog( 2, "i2cmem_write( %d, I2CMEM_SCL, %d )\n", chip, 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( 1, "i2cmem(%d) devsel %02x: not this device\n", chip, c->devsel );
+ c->state = STATE_IDLE;
+ }
+ else if( ( c->devsel & DEVSEL_RW ) == 0 )
+ {
+ verboselog( 1, "i2cmem(%d) devsel %02x: write\n", chip, c->devsel );
+ c->state = STATE_BYTEADDR;
+ }
+ else
+ {
+ verboselog( 1, "i2cmem(%d) devsel %02x: read\n", chip, c->devsel );
+ c->state = STATE_DATAOUT;
+ }
+ break;
+
+ case STATE_BYTEADDR:
+ c->byteaddr = c->shift;
+ c->page_offset = 0;
+
+ verboselog( 1, "i2cmem(%d) byteaddr %02x\n", chip, c->byteaddr );
+
+ c->state = STATE_DATAIN;
+ break;
+
+ case STATE_DATAIN:
+ if( c->wc )
+ {
+ verboselog( 0, "i2cmem(%d) write not enabled\n", chip );
+ c->state = STATE_IDLE;
+ }
+ else if( c->page_size > 0 )
+ {
+ c->page[ c->page_offset ] = c->shift;
+ verboselog( 1, "i2cmem(%d) page[ %04x ] <- %02x\n", chip, 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( 1, "i2cmem(%d) data[ %04x to %04x ] = page\n", chip, offset, offset + c->page_size - 1 );
+
+ c->page_offset = 0;
+ }
+ }
+ else
+ {
+ int offset = data_offset( c );
+
+ c->data[ offset ] = c->shift;
+ verboselog( 1, "i2cmem(%d) data[ %04x ] <- %02x\n", chip, 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( 1, "i2cmem(%d) data[ %04x ] -> %02x\n", chip, 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( 1, "i2cmem(%d) sleep\n", chip );
+ 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( 2, "i2cmem_write( %d, I2CMEM_WC, %d )\n", chip, c->wc );
+ }
+ break;
+
+ default:
+ verboselog( 0, "i2cmem_write( %d, %d, %d ) invalid line\n", chip, line, data );
+ break;
+ }
+}
+
+int i2cmem_read( int chip, int line )
+{
+ struct i2cmem_chip *c;
+
+ if( chip >= I2CMEM_MAXCHIP )
+ {
+ verboselog( 0, "i2cmem_read( %d, %d ) invalid chip\n", chip, line );
+ return 0;
+ }
+
+ c = &i2cmem[ chip ];
+
+ switch( line )
+ {
+ case I2CMEM_SDA:
+ verboselog( 2, "i2cmem_read( %d, I2CMEM_SDA ) %d\n", chip, c->sdar & c->sdaw );
+ return c->sdar & c->sdaw;
+
+ default:
+ verboselog( 0, "i2cmem_read( %d, %d ) invalid line\n", chip, line );
+ break;
+ }
+
+ return 0;
+}
+
+static void nvram_handler_i2cmem( int chip, running_machine *machine, mame_file *file, int read_or_write )
+{
+ struct i2cmem_chip *c;
+
+ if( chip >= I2CMEM_MAXCHIP )
+ {
+ verboselog( 0, "nvram_handler_i2cmem( %d ) invalid chip\n", chip );
+ return;
+ }
+
+ c = &i2cmem[ chip ];
+
+ if( read_or_write )
+ {
+ mame_fwrite( file, c->data, c->data_size );
+ }
+ else if( file )
+ {
+ mame_fread( file, c->data, c->data_size );
+ }
+}
+
+NVRAM_HANDLER( i2cmem_0 ) { nvram_handler_i2cmem( 0, machine, file, read_or_write ); }