summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/eeprom.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/eeprom.c')
-rw-r--r--trunk/src/emu/machine/eeprom.c563
1 files changed, 563 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/eeprom.c b/trunk/src/emu/machine/eeprom.c
new file mode 100644
index 00000000000..07ef93e25bf
--- /dev/null
+++ b/trunk/src/emu/machine/eeprom.c
@@ -0,0 +1,563 @@
+/***************************************************************************
+
+ eeprom.c
+
+ Serial eeproms.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "machine/eeprom.h"
+
+
+
+//**************************************************************************
+// DEBUGGING
+//**************************************************************************
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type EEPROM = &device_creator<eeprom_device>;
+
+const eeprom_interface eeprom_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, // enable_multi_read
+ 0 // reset_delay
+// "*10001xxxx" // write all 1 00 01xxxx dddddddddddddddd
+// "*10010xxxx" // erase all 1 00 10xxxx
+};
+
+const eeprom_interface eeprom_interface_93C46_8bit =
+{
+ 7, // address bits 7
+ 8, // data bits 8
+ "*110", // read 1 10 aaaaaa
+ "*101", // write 1 01 aaaaaa dddddddd
+ "*111", // erase 1 11 aaaaaa
+ "*10000xxxx", // lock 1 00 00xxxx
+ "*10011xxxx", // unlock 1 00 11xxxx
+ 1, // enable_multi_read
+ 0 // reset_delay
+// "*10001xxxx" // write all 1 00 01xxxx dddddddd
+// "*10010xxxx" // erase all 1 00 10xxxx
+};
+
+const eeprom_interface eeprom_interface_93C66B =
+{
+ 8, // address bits
+ 16, // data bits
+ "*110", // read command
+ "*101", // write command
+ "*111", // erase command
+ "*10000xxxxxx", // lock command
+ "*10011xxxxxx", // unlock command
+ 1, // enable_multi_read
+ 0 // reset_delay
+// "*10001xxxxxx", // write all
+// "*10010xxxxxx", // erase all
+};
+
+
+static ADDRESS_MAP_START( eeprom_map8, AS_PROGRAM, 8 )
+ AM_RANGE(0x0000, 0x0fff) AM_RAM
+ADDRESS_MAP_END
+
+
+static ADDRESS_MAP_START( eeprom_map16, AS_PROGRAM, 16 )
+ AM_RANGE(0x0000, 0x07ff) AM_RAM
+ADDRESS_MAP_END
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// eeprom_device - constructor
+//-------------------------------------------------
+
+eeprom_device::eeprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, EEPROM, "EEPROM", tag, owner, clock),
+ device_memory_interface(mconfig, *this),
+ device_nvram_interface(mconfig, *this),
+ m_default_data_size(0),
+ m_default_value(0),
+ m_serial_count(0),
+ m_data_buffer(0),
+ m_read_address(0),
+ m_clock_count(0),
+ m_latch(0),
+ m_reset_line(CLEAR_LINE),
+ m_clock_line(CLEAR_LINE),
+ m_sending(0),
+ m_locked(false),
+ m_reset_counter(0)
+{
+ m_default_data.u8 = NULL;
+ memset(downcast<eeprom_interface *>(this), 0, sizeof(eeprom_interface));
+}
+
+
+//-------------------------------------------------
+// static_set_interface - configuration helper
+// to set the interface
+//-------------------------------------------------
+
+void eeprom_device::static_set_interface(device_t &device, const eeprom_interface &interface)
+{
+ eeprom_device &eeprom = downcast<eeprom_device &>(device);
+ static_cast<eeprom_interface &>(eeprom) = interface;
+
+ // describe our address space
+ if (eeprom.m_data_bits == 8)
+ eeprom.m_space_config = address_space_config("eeprom", ENDIANNESS_BIG, 8, eeprom.m_address_bits, 0, *ADDRESS_MAP_NAME(eeprom_map8));
+ else
+ eeprom.m_space_config = address_space_config("eeprom", ENDIANNESS_BIG, 16, eeprom.m_address_bits * 2, 0, *ADDRESS_MAP_NAME(eeprom_map16));
+}
+
+
+//-------------------------------------------------
+// static_set_default_data - configuration helpers
+// to set the default data
+//-------------------------------------------------
+
+void eeprom_device::static_set_default_data(device_t &device, const UINT8 *data, UINT32 size)
+{
+ eeprom_device &eeprom = downcast<eeprom_device &>(device);
+ assert(eeprom.m_data_bits == 8);
+ eeprom.m_default_data.u8 = const_cast<UINT8 *>(data);
+ eeprom.m_default_data_size = size;
+}
+
+void eeprom_device::static_set_default_data(device_t &device, const UINT16 *data, UINT32 size)
+{
+ eeprom_device &eeprom = downcast<eeprom_device &>(device);
+ assert(eeprom.m_data_bits == 16);
+ eeprom.m_default_data.u16 = const_cast<UINT16 *>(data);
+ eeprom.m_default_data_size = size / 2;
+}
+
+
+//-------------------------------------------------
+// static_set_default_value - configuration helper
+// to set the default value
+//-------------------------------------------------
+
+void eeprom_device::static_set_default_value(device_t &device, UINT16 value)
+{
+ downcast<eeprom_device &>(device).m_default_value = 0x10000 | value;
+}
+
+
+//-------------------------------------------------
+// device_validity_check - perform validity checks
+// on this device
+//-------------------------------------------------
+
+bool eeprom_device::device_validity_check(emu_options &options, const game_driver &driver) const
+{
+ bool error = false;
+
+ if (m_data_bits != 8 && m_data_bits != 16)
+ {
+ mame_printf_error("%s: %s eeprom device '%s' specified invalid data width %d\n", driver.source_file, driver.name, tag(), m_data_bits);
+ error = true;
+ }
+
+ return error;
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void eeprom_device::device_start()
+{
+ m_locked = (m_cmd_unlock != NULL);
+
+ save_pointer(NAME(m_serial_buffer), SERIAL_BUFFER_LENGTH);
+ save_item(NAME(m_clock_line));
+ save_item(NAME(m_reset_line));
+ save_item(NAME(m_locked));
+ save_item(NAME(m_serial_count));
+ save_item(NAME(m_latch));
+ save_item(NAME(m_reset_counter));
+ save_item(NAME(m_clock_count));
+ save_item(NAME(m_data_buffer));
+ save_item(NAME(m_read_address));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void eeprom_device::device_reset()
+{
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *eeprom_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == 0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// nvram_default - called to initialize NVRAM to
+// its default state
+//-------------------------------------------------
+
+void eeprom_device::nvram_default()
+{
+ UINT32 eeprom_length = 1 << m_address_bits;
+ UINT32 eeprom_bytes = eeprom_length * m_data_bits / 8;
+
+ /* initialize to the default value */
+ UINT16 default_value = 0xffff;
+ if (m_default_value != 0)
+ default_value = m_default_value;
+ for (offs_t offs = 0; offs < eeprom_length; offs++)
+ if (m_data_bits == 8)
+ m_addrspace[0]->write_byte(offs, default_value);
+ else
+ m_addrspace[0]->write_word(offs * 2, default_value);
+
+ /* handle hard-coded data from the driver */
+ if (m_default_data.u8 != NULL)
+ for (offs_t offs = 0; offs < m_default_data_size; offs++)
+ if (m_data_bits == 8)
+ m_addrspace[0]->write_byte(offs, m_default_data.u8[offs]);
+ else
+ m_addrspace[0]->write_word(offs * 2, m_default_data.u16[offs]);
+
+ /* populate from a memory region if present */
+ if (m_region != NULL)
+ {
+ if (m_region->bytes() != eeprom_bytes)
+ fatalerror("eeprom region '%s' wrong size (expected size = 0x%X)", tag(), eeprom_bytes);
+ if (m_data_bits == 8 && m_region->width() != 1)
+ fatalerror("eeprom region '%s' needs to be an 8-bit region", tag());
+ if (m_data_bits == 16 && (m_region->width() != 2 || m_region->endianness() != ENDIANNESS_BIG))
+ fatalerror("eeprom region '%s' needs to be a 16-bit big-endian region", tag());
+
+ for (offs_t offs = 0; offs < eeprom_length; offs++)
+ if (m_data_bits == 8)
+ m_addrspace[0]->write_byte(offs, m_region->u8(offs));
+ else
+ m_addrspace[0]->write_word(offs * 2, m_region->u16(offs));
+ }
+}
+
+
+//-------------------------------------------------
+// nvram_read - called to read NVRAM from the
+// .nv file
+//-------------------------------------------------
+
+void eeprom_device::nvram_read(emu_file &file)
+{
+ UINT32 eeprom_length = 1 << m_address_bits;
+ UINT32 eeprom_bytes = eeprom_length * m_data_bits / 8;
+
+ UINT8 *buffer = auto_alloc_array(machine(), UINT8, eeprom_bytes);
+ file.read(buffer, eeprom_bytes);
+ for (offs_t offs = 0; offs < eeprom_bytes; offs++)
+ m_addrspace[0]->write_byte(offs, buffer[offs]);
+ auto_free(machine(), buffer);
+}
+
+
+//-------------------------------------------------
+// nvram_write - called to write NVRAM to the
+// .nv file
+//-------------------------------------------------
+
+void eeprom_device::nvram_write(emu_file &file)
+{
+ UINT32 eeprom_length = 1 << m_address_bits;
+ UINT32 eeprom_bytes = eeprom_length * m_data_bits / 8;
+
+ UINT8 *buffer = auto_alloc_array(machine(), UINT8, eeprom_bytes);
+ for (offs_t offs = 0; offs < eeprom_bytes; offs++)
+ buffer[offs] = m_addrspace[0]->read_byte(offs);
+ file.write(buffer, eeprom_bytes);
+ auto_free(machine(), buffer);
+}
+
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+WRITE_LINE_MEMBER( eeprom_device::write_bit )
+{
+ LOG(("write bit %d\n",state));
+ m_latch = state;
+}
+
+
+READ_LINE_DEVICE_HANDLER( eeprom_read_bit )
+{
+ return downcast<eeprom_device *>(device)->read_bit();
+}
+
+READ_LINE_MEMBER( eeprom_device::read_bit )
+{
+ int res;
+
+ if (m_sending)
+ res = (m_data_buffer >> m_data_bits) & 1;
+ else
+ {
+ if (m_reset_counter > 0)
+ {
+ /* this is needed by wbeachvl */
+ m_reset_counter--;
+ res = 0;
+ }
+ else
+ res = 1;
+ }
+
+ LOG(("read bit %d\n",res));
+
+ return res;
+}
+
+
+
+WRITE_LINE_MEMBER( eeprom_device::set_cs_line )
+{
+ LOG(("set reset line %d\n",state));
+ m_reset_line = state;
+
+ if (m_reset_line != CLEAR_LINE)
+ {
+ if (m_serial_count)
+ logerror("EEPROM %s reset, buffer = %s\n", tag(), m_serial_buffer);
+
+ m_serial_count = 0;
+ m_sending = 0;
+ m_reset_counter = m_reset_delay; /* delay a little before returning setting data to 1 (needed by wbeachvl) */
+ }
+}
+
+
+
+WRITE_LINE_MEMBER( eeprom_device::set_clock_line )
+{
+ LOG(("set clock line %d\n",state));
+ if (state == PULSE_LINE || (m_clock_line == CLEAR_LINE && state != CLEAR_LINE))
+ {
+ if (m_reset_line == CLEAR_LINE)
+ {
+ if (m_sending)
+ {
+ if (m_clock_count == m_data_bits && m_enable_multi_read)
+ {
+ m_read_address = (m_read_address + 1) & ((1 << m_address_bits) - 1);
+ if (m_data_bits == 16)
+ m_data_buffer = m_addrspace[0]->read_word(m_read_address * 2);
+ else
+ m_data_buffer = m_addrspace[0]->read_byte(m_read_address);
+ m_clock_count = 0;
+ logerror("EEPROM %s read %04x from address %02x\n", tag(), m_data_buffer, m_read_address);
+ }
+ m_data_buffer = (m_data_buffer << 1) | 1;
+ m_clock_count++;
+ }
+ else
+ write(m_latch);
+ }
+ }
+
+ m_clock_line = state;
+}
+
+
+
+//**************************************************************************
+// INTERNAL HELPERS
+//**************************************************************************
+
+void eeprom_device::write(int bit)
+{
+ LOG(("EEPROM %s write bit %d\n", tag(), bit));
+
+ if (m_serial_count >= SERIAL_BUFFER_LENGTH-1)
+ {
+ logerror("error: EEPROM %s serial buffer overflow\n", tag());
+ return;
+ }
+
+ m_serial_buffer[m_serial_count++] = (bit ? '1' : '0');
+ m_serial_buffer[m_serial_count] = 0; /* nul terminate so we can treat it as a string */
+
+ if ( (m_serial_count > m_address_bits) &&
+ command_match((char*)(m_serial_buffer),m_cmd_read,strlen((char*)(m_serial_buffer))-m_address_bits) )
+ {
+ int i,address;
+
+ address = 0;
+ for (i = m_serial_count-m_address_bits;i < m_serial_count;i++)
+ {
+ address <<= 1;
+ if (m_serial_buffer[i] == '1') address |= 1;
+ }
+ if (m_data_bits == 16)
+ m_data_buffer = m_addrspace[0]->read_word(address * 2);
+ else
+ m_data_buffer = m_addrspace[0]->read_byte(address);
+ m_read_address = address;
+ m_clock_count = 0;
+ m_sending = 1;
+ m_serial_count = 0;
+ logerror("EEPROM %s read %04x from address %02x\n", tag(), m_data_buffer,address);
+ }
+ else if ( (m_serial_count > m_address_bits) &&
+ command_match((char*)(m_serial_buffer),m_cmd_erase,strlen((char*)(m_serial_buffer))-m_address_bits) )
+ {
+ int i,address;
+
+ address = 0;
+ for (i = m_serial_count-m_address_bits;i < m_serial_count;i++)
+ {
+ address <<= 1;
+ if (m_serial_buffer[i] == '1') address |= 1;
+ }
+ logerror("EEPROM %s erase address %02x\n", tag(), address);
+ if (m_locked == 0)
+ {
+ if (m_data_bits == 16)
+ m_addrspace[0]->write_word(address * 2, 0x0000);
+ else
+ m_addrspace[0]->write_byte(address, 0x00);
+ }
+ else
+ logerror("Error: EEPROM %s is locked\n", tag());
+ m_serial_count = 0;
+ }
+ else if ( (m_serial_count > (m_address_bits + m_data_bits)) &&
+ command_match((char*)(m_serial_buffer),m_cmd_write,strlen((char*)(m_serial_buffer))-(m_address_bits + m_data_bits)) )
+ {
+ int i,address,data;
+
+ address = 0;
+ for (i = m_serial_count-m_data_bits-m_address_bits;i < (m_serial_count-m_data_bits);i++)
+ {
+ address <<= 1;
+ if (m_serial_buffer[i] == '1') address |= 1;
+ }
+ data = 0;
+ for (i = m_serial_count-m_data_bits;i < m_serial_count;i++)
+ {
+ data <<= 1;
+ if (m_serial_buffer[i] == '1') data |= 1;
+ }
+ logerror("EEPROM %s write %04x to address %02x\n", tag(), data, address);
+ if (m_locked == 0)
+ {
+ if (m_data_bits == 16)
+ m_addrspace[0]->write_word(address * 2, data);
+ else
+ m_addrspace[0]->write_byte(address, data);
+ }
+ else
+ logerror("Error: EEPROM %s is locked\n", tag());
+ m_serial_count = 0;
+ }
+ else if ( command_match((char*)(m_serial_buffer),m_cmd_lock,strlen((char*)(m_serial_buffer))) )
+ {
+ logerror("EEPROM %s lock\n", tag());
+ m_locked = 1;
+ m_serial_count = 0;
+ }
+ else if ( command_match((char*)(m_serial_buffer),m_cmd_unlock,strlen((char*)(m_serial_buffer))) )
+ {
+ logerror("EEPROM %s unlock\n", tag());
+ m_locked = 0;
+ m_serial_count = 0;
+ }
+}
+
+
+/*
+ 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.
+*/
+bool eeprom_device::command_match(const char *buf, const char *cmd, int len)
+{
+ if ( cmd == 0 ) return false;
+ if ( len == 0 ) return false;
+
+ 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 false;
+ 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 false;
+ }
+ }
+ }
+ return (*cmd==0);
+}