mame/src/emu/machine/eeprom.c
2010-09-18 22:22:39 +00:00

599 lines
17 KiB
C

/***************************************************************************
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
//**************************************************************************
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_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, ADDRESS_SPACE_PROGRAM, 8 )
AM_RANGE(0x0000, 0x0fff) AM_RAM
ADDRESS_MAP_END
static ADDRESS_MAP_START( eeprom_map16, ADDRESS_SPACE_PROGRAM, 16 )
AM_RANGE(0x0000, 0x07ff) AM_RAM
ADDRESS_MAP_END
//**************************************************************************
// DEVICE CONFIGURATION
//**************************************************************************
//-------------------------------------------------
// eeprom_device_config - constructor
//-------------------------------------------------
eeprom_device_config::eeprom_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock)
: device_config(mconfig, static_alloc_device_config, "EEPROM", tag, owner, clock),
device_config_memory_interface(mconfig, *this),
device_config_nvram_interface(mconfig, *this),
m_default_data_size(0),
m_default_value(0)
{
m_default_data.u8 = NULL;
}
//-------------------------------------------------
// static_alloc_device_config - allocate a new
// configuration object
//-------------------------------------------------
device_config *eeprom_device_config::static_alloc_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock)
{
return global_alloc(eeprom_device_config(mconfig, tag, owner, clock));
}
//-------------------------------------------------
// alloc_device - allocate a new device object
//-------------------------------------------------
device_t *eeprom_device_config::alloc_device(running_machine &machine) const
{
return auto_alloc(&machine, eeprom_device(machine, *this));
}
//-------------------------------------------------
// static_set_interface - configuration helper
// to set the interface
//-------------------------------------------------
void eeprom_device_config::static_set_interface(device_config *device, const eeprom_interface &interface)
{
eeprom_device_config *eeprom = downcast<eeprom_device_config *>(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_config::static_set_default_data(device_config *device, const UINT8 *data, UINT32 size)
{
eeprom_device_config *eeprom = downcast<eeprom_device_config *>(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_config::static_set_default_data(device_config *device, const UINT16 *data, UINT32 size)
{
eeprom_device_config *eeprom = downcast<eeprom_device_config *>(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_config::static_set_default_value(device_config *device, UINT16 value)
{
downcast<eeprom_device_config *>(device)->m_default_value = 0x10000 | value;
}
//-------------------------------------------------
// device_validity_check - perform validity checks
// on this device
//-------------------------------------------------
bool eeprom_device_config::device_validity_check(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;
}
//-------------------------------------------------
// memory_space_config - return a description of
// any address spaces owned by this device
//-------------------------------------------------
const address_space_config *eeprom_device_config::memory_space_config(int spacenum) const
{
return (spacenum == 0) ? &m_space_config : NULL;
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// eeprom_device - constructor
//-------------------------------------------------
eeprom_device::eeprom_device(running_machine &_machine, const eeprom_device_config &config)
: device_t(_machine, config),
device_memory_interface(_machine, config, *this),
device_nvram_interface(_machine, config, *this),
m_config(config),
m_serial_count(0),
m_data_bits(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(m_config.m_cmd_unlock != NULL),
m_reset_delay(0)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void eeprom_device::device_start()
{
state_save_register_device_item_pointer(this, 0, m_serial_buffer, SERIAL_BUFFER_LENGTH);
state_save_register_device_item(this, 0, m_clock_line);
state_save_register_device_item(this, 0, m_reset_line);
state_save_register_device_item(this, 0, m_locked);
state_save_register_device_item(this, 0, m_serial_count);
state_save_register_device_item(this, 0, m_latch);
state_save_register_device_item(this, 0, m_reset_delay);
state_save_register_device_item(this, 0, m_clock_count);
state_save_register_device_item(this, 0, m_data_bits);
state_save_register_device_item(this, 0, m_read_address);
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void eeprom_device::device_reset()
{
}
//-------------------------------------------------
// nvram_default - called to initialize NVRAM to
// its default state
//-------------------------------------------------
void eeprom_device::nvram_default()
{
UINT32 eeprom_length = 1 << m_config.m_address_bits;
UINT32 eeprom_bytes = eeprom_length * m_config.m_data_bits / 8;
/* initialize to the default value */
UINT16 default_value = 0xffff;
if (m_config.m_default_value != 0)
default_value = m_config.m_default_value;
for (offs_t offs = 0; offs < eeprom_length; offs++)
if (m_config.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_config.m_default_data.u8 != NULL)
for (offs_t offs = 0; offs < m_config.m_default_data_size; offs++)
if (m_config.m_data_bits == 8)
m_addrspace[0]->write_byte(offs, m_config.m_default_data.u8[offs]);
else
m_addrspace[0]->write_word(offs * 2, m_config.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_config.m_data_bits == 8 && m_region->width() != 1)
fatalerror("eeprom region '%s' needs to be an 8-bit region", tag());
if (m_config.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 (flags=%08x)", tag(), m_region->flags());
for (offs_t offs = 0; offs < eeprom_length; offs++)
if (m_config.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(mame_file &file)
{
UINT32 eeprom_length = 1 << m_config.m_address_bits;
UINT32 eeprom_bytes = eeprom_length * m_config.m_data_bits / 8;
UINT8 *buffer = auto_alloc_array(&m_machine, UINT8, eeprom_bytes);
mame_fread(&file, buffer, eeprom_bytes);
for (offs_t offs = 0; offs < eeprom_bytes; offs++)
m_addrspace[0]->write_byte(offs, buffer[offs]);
auto_free(&m_machine, buffer);
}
//-------------------------------------------------
// nvram_write - called to write NVRAM to the
// .nv file
//-------------------------------------------------
void eeprom_device::nvram_write(mame_file &file)
{
UINT32 eeprom_length = 1 << m_config.m_address_bits;
UINT32 eeprom_bytes = eeprom_length * m_config.m_data_bits / 8;
UINT8 *buffer = auto_alloc_array(&m_machine, UINT8, eeprom_bytes);
for (offs_t offs = 0; offs < eeprom_bytes; offs++)
buffer[offs] = m_addrspace[0]->read_byte(offs);
mame_fwrite(&file, buffer, eeprom_bytes);
auto_free(&m_machine, buffer);
}
//**************************************************************************
// READ/WRITE HANDLERS
//**************************************************************************
WRITE_LINE_DEVICE_HANDLER( eeprom_write_bit )
{
downcast<eeprom_device *>(device)->write_bit(state);
}
void eeprom_device::write_bit(int state)
{
LOG(("write bit %d\n",state));
m_latch = state;
}
READ_LINE_DEVICE_HANDLER( eeprom_read_bit )
{
return downcast<eeprom_device *>(device)->read_bit();
}
int eeprom_device::read_bit()
{
int res;
if (m_sending)
res = (m_data_bits >> m_config.m_data_bits) & 1;
else
{
if (m_reset_delay > 0)
{
/* this is needed by wbeachvl */
m_reset_delay--;
res = 0;
}
else
res = 1;
}
LOG(("read bit %d\n",res));
return res;
}
WRITE_LINE_DEVICE_HANDLER( eeprom_set_cs_line )
{
downcast<eeprom_device *>(device)->set_cs_line(state);
}
void eeprom_device::set_cs_line(int state)
{
LOG(("set reset line %d\n",state));
m_reset_line = state;
if (m_reset_line != CLEAR_LINE)
{
if (m_serial_count)
logerror("EEPROM reset, buffer = %s\n",m_serial_buffer);
m_serial_count = 0;
m_sending = 0;
m_reset_delay = m_config.m_reset_delay; /* delay a little before returning setting data to 1 (needed by wbeachvl) */
}
}
WRITE_LINE_DEVICE_HANDLER( eeprom_set_clock_line )
{
downcast<eeprom_device *>(device)->set_clock_line(state);
}
void eeprom_device::set_clock_line(int state)
{
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_config.m_data_bits && m_config.m_enable_multi_read)
{
m_read_address = (m_read_address + 1) & ((1 << m_config.m_address_bits) - 1);
if (m_config.m_data_bits == 16)
m_data_bits = m_addrspace[0]->read_word(m_read_address * 2);
else
m_data_bits = m_addrspace[0]->read_byte(m_read_address);
m_clock_count = 0;
logerror("EEPROM read %04x from address %02x\n",m_data_bits,m_read_address);
}
m_data_bits = (m_data_bits << 1) | 1;
m_clock_count++;
}
else
write(m_latch);
}
}
m_clock_line = state;
}
//**************************************************************************
// INTERNAL HELPERS
//**************************************************************************
void eeprom_device::write(int bit)
{
LOG(("EEPROM write bit %d\n",bit));
if (m_serial_count >= SERIAL_BUFFER_LENGTH-1)
{
logerror("error: EEPROM serial buffer overflow\n");
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_config.m_address_bits) &&
command_match((char*)(m_serial_buffer),m_config.m_cmd_read,strlen((char*)(m_serial_buffer))-m_config.m_address_bits) )
{
int i,address;
address = 0;
for (i = m_serial_count-m_config.m_address_bits;i < m_serial_count;i++)
{
address <<= 1;
if (m_serial_buffer[i] == '1') address |= 1;
}
if (m_config.m_data_bits == 16)
m_data_bits = m_addrspace[0]->read_word(address * 2);
else
m_data_bits = m_addrspace[0]->read_byte(address);
m_read_address = address;
m_clock_count = 0;
m_sending = 1;
m_serial_count = 0;
logerror("EEPROM read %04x from address %02x\n",m_data_bits,address);
}
else if ( (m_serial_count > m_config.m_address_bits) &&
command_match((char*)(m_serial_buffer),m_config.m_cmd_erase,strlen((char*)(m_serial_buffer))-m_config.m_address_bits) )
{
int i,address;
address = 0;
for (i = m_serial_count-m_config.m_address_bits;i < m_serial_count;i++)
{
address <<= 1;
if (m_serial_buffer[i] == '1') address |= 1;
}
logerror("EEPROM erase address %02x\n",address);
if (m_locked == 0)
{
if (m_config.m_data_bits == 16)
m_addrspace[0]->write_word(address * 2, 0x0000);
else
m_addrspace[0]->write_byte(address, 0x00);
}
else
logerror("Error: EEPROM is m_locked\n");
m_serial_count = 0;
}
else if ( (m_serial_count > (m_config.m_address_bits + m_config.m_data_bits)) &&
command_match((char*)(m_serial_buffer),m_config.m_cmd_write,strlen((char*)(m_serial_buffer))-(m_config.m_address_bits + m_config.m_data_bits)) )
{
int i,address,data;
address = 0;
for (i = m_serial_count-m_config.m_data_bits-m_config.m_address_bits;i < (m_serial_count-m_config.m_data_bits);i++)
{
address <<= 1;
if (m_serial_buffer[i] == '1') address |= 1;
}
data = 0;
for (i = m_serial_count-m_config.m_data_bits;i < m_serial_count;i++)
{
data <<= 1;
if (m_serial_buffer[i] == '1') data |= 1;
}
logerror("EEPROM write %04x to address %02x\n",data,address);
if (m_locked == 0)
{
if (m_config.m_data_bits == 16)
m_addrspace[0]->write_word(address * 2, data);
else
m_addrspace[0]->write_byte(address, data);
}
else
logerror("Error: EEPROM is m_locked\n");
m_serial_count = 0;
}
else if ( command_match((char*)(m_serial_buffer),m_config.m_cmd_lock,strlen((char*)(m_serial_buffer))) )
{
logerror("EEPROM lock\n");
m_locked = 1;
m_serial_count = 0;
}
else if ( command_match((char*)(m_serial_buffer),m_config.m_cmd_unlock,strlen((char*)(m_serial_buffer))) )
{
logerror("EEPROM unlock\n");
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);
}
const device_type EEPROM = eeprom_device_config::static_alloc_device_config;