summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/at28c64b.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/at28c64b.cpp')
-rw-r--r--src/devices/machine/at28c64b.cpp271
1 files changed, 271 insertions, 0 deletions
diff --git a/src/devices/machine/at28c64b.cpp b/src/devices/machine/at28c64b.cpp
new file mode 100644
index 00000000000..3fdc23a1c86
--- /dev/null
+++ b/src/devices/machine/at28c64b.cpp
@@ -0,0 +1,271 @@
+// license:BSD-3-Clause
+// copyright-holders:smf, R. Belmont
+/***************************************************************************
+ ATMEL AT28C64B
+
+ 64K ( 8K x 8 ) Parallel EEPROM with flash-like in-band signalling
+
+***************************************************************************/
+
+#include "emu.h"
+#include "at28c64b.h"
+
+static constexpr int AT28C64B_DATA_BYTES = 0x10000;
+static constexpr int AT28C64B_ID_BYTES = 0x40;
+static constexpr int AT28C64B_TOTAL_BYTES = AT28C64B_DATA_BYTES + AT28C64B_ID_BYTES;
+
+static constexpr int AT28C64B_ID_OFFSET = 0x1fc0;
+static constexpr int AT28C64B_SECTOR_SIZE = 0x40;
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+void at28c64b_device::at28c64b_map8(address_map &map)
+{
+ map(0x00000, 0x1003f).ram();
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+// device type definition
+DEFINE_DEVICE_TYPE(AT28C64B, at28c64b_device, "at28c64b", "AT28C64B 8Kx8 EEPROM")
+
+//-------------------------------------------------
+// at28c64b_device - constructor
+//-------------------------------------------------
+
+at28c64b_device::at28c64b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, AT28C64B, tag, owner, clock),
+ device_memory_interface(mconfig, *this),
+ device_nvram_interface(mconfig, *this),
+ m_space_config("at28c64b", ENDIANNESS_BIG, 8, 17, 0, address_map_constructor(FUNC(at28c64b_device::at28c64b_map8), this)),
+ m_a9_12v(0),
+ m_oe_12v(0),
+ m_last_write(-1),
+ m_state(0),
+ m_bytes_in_sector(0),
+ m_default_data(*this, DEVICE_SELF)
+{
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+device_memory_interface::space_config_vector at28c64b_device::memory_space_config() const
+{
+ return space_config_vector {
+ std::make_pair(0, &m_space_config)
+ };
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void at28c64b_device::device_start()
+{
+ m_write_timer = timer_alloc( FUNC( at28c64b_device::write_complete ), this );
+
+ save_item( NAME(m_a9_12v) );
+ save_item( NAME(m_oe_12v) );
+ save_item( NAME(m_last_write) );
+}
+
+
+//-------------------------------------------------
+// nvram_default - called to initialize NVRAM to
+// its default state
+//-------------------------------------------------
+
+void at28c64b_device::nvram_default()
+{
+ uint16_t const default_value = 0xff;
+ for (offs_t offs = 0; offs < AT28C64B_TOTAL_BYTES; offs++)
+ space(AS_PROGRAM).write_byte(offs, default_value);
+
+ /* populate from a memory region if present */
+ if (m_default_data.found())
+ {
+ for (offs_t offs = 0; offs < AT28C64B_DATA_BYTES; offs++)
+ space(AS_PROGRAM).write_byte(offs, m_default_data[offs]);
+ }
+}
+
+
+//-------------------------------------------------
+// nvram_read - called to read NVRAM from the
+// .nv file
+//-------------------------------------------------
+
+bool at28c64b_device::nvram_read(util::read_stream &file)
+{
+ std::vector<uint8_t> buffer(AT28C64B_TOTAL_BYTES);
+
+ auto const [err, actual] = util::read(file, &buffer[0], AT28C64B_TOTAL_BYTES);
+ if (err || (actual != AT28C64B_TOTAL_BYTES))
+ return false;
+
+ for (offs_t offs = 0; offs < AT28C64B_TOTAL_BYTES; offs++)
+ space(AS_PROGRAM).write_byte(offs, buffer[offs]);
+
+ return true;
+}
+
+//-------------------------------------------------
+// nvram_write - called to write NVRAM to the
+// .nv file
+//-------------------------------------------------
+
+bool at28c64b_device::nvram_write(util::write_stream &file)
+{
+ std::vector<uint8_t> buffer(AT28C64B_TOTAL_BYTES);
+
+ for (offs_t offs = 0; offs < AT28C64B_TOTAL_BYTES; offs++)
+ buffer[offs] = space(AS_PROGRAM).read_byte(offs);
+
+ auto const [err, actual] = util::write(file, &buffer[0], AT28C64B_TOTAL_BYTES);
+ return !err;
+}
+
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+void at28c64b_device::write(offs_t offset, uint8_t data)
+{
+ logerror("%s: AT28C64B: write( %04x, %02x ) state %d last_write %d\n", machine().describe_context(), offset, data, m_state, m_last_write);
+
+ if (m_state == STATE_SECTOR_WRITE)
+ {
+ logerror("SECTOR WRITE: %02x @ %x\n", data, offset);
+ this->space(AS_PROGRAM).write_byte(offset, data);
+ m_last_write = data;
+ m_write_timer->adjust(attotime::from_usec(10));
+ m_bytes_in_sector--;
+ if (m_bytes_in_sector == 0)
+ {
+ m_state = STATE_WRITE_PROTECT;
+ }
+ return;
+ }
+
+ if( m_last_write >= 0 )
+ {
+// logerror( "%s: AT28C64B: write( %04x, %02x ) busy\n", machine().describe_context(), offset, data );
+ }
+ else if( m_oe_12v )
+ {
+// logerror( "%s: AT28C64B: write( %04x, %02x ) erase\n", machine().describe_context(), offset, data );
+ if( m_last_write < 0 )
+ {
+ for( offs_t offs = 0; offs < AT28C64B_TOTAL_BYTES; offs++ )
+ {
+ this->space(AS_PROGRAM).write_byte( offs, 0xff );
+ }
+
+ m_last_write = 0xff;
+ m_write_timer->adjust( attotime::from_usec( 10 ) );
+ }
+ }
+ else
+ {
+ if ((offset == 0x1555) && (data == 0xaa))
+ {
+ m_state = STATE_ID_1;
+ return;
+ }
+
+ if ((m_state == STATE_ID_1) && (offset == 0xaaa) && (data == 0x55))
+ {
+ m_state = STATE_ID_2;
+ return;
+ }
+
+ if ((m_state == STATE_ID_2) && (offset == 0x1555) && (data == 0xa0))
+ {
+ m_state = STATE_SECTOR_WRITE;
+ m_bytes_in_sector = AT28C64B_SECTOR_SIZE;
+ return;
+ }
+
+ if (m_state == STATE_WRITE_PROTECT)
+ {
+ logerror("%s: write %02x to %x while write protected\n", machine().describe_context(), data, offset);
+ return;
+ }
+
+ if ((m_a9_12v) && (offset >= AT28C64B_ID_OFFSET) && (offset < (AT28C64B_ID_OFFSET + AT28C64B_ID_BYTES)))
+ {
+ offset += AT28C64B_ID_BYTES;
+ }
+
+ if( m_last_write < 0 && this->space(AS_PROGRAM).read_byte( offset ) != data )
+ {
+ this->space(AS_PROGRAM).write_byte( offset, data );
+ m_last_write = data;
+ m_write_timer->adjust( attotime::from_usec( 10 ) );
+ }
+ }
+}
+
+
+uint8_t at28c64b_device::read(offs_t offset)
+{
+ if( m_last_write >= 0 )
+ {
+ uint8_t data = m_last_write ^ 0x80;
+// logerror( "%s: AT28C64B: read( %04x ) write status %02x\n", machine().describe_context(), offset, data );
+ return data;
+ }
+ else
+ {
+ if( m_a9_12v && offset >= AT28C64B_ID_OFFSET )
+ {
+ offset += AT28C64B_ID_BYTES;
+ }
+
+ uint8_t data = this->space(AS_PROGRAM).read_byte( offset );
+// logerror( "%s: AT28C64B: read( %04x ) data %02x\n", machine().describe_context(), offset, data );
+ return data;
+ }
+}
+
+
+void at28c64b_device::set_a9_12v(int state)
+{
+ state &= 1;
+ if( m_a9_12v != state )
+ {
+// logerror( "%s: AT28C64B: set_a9_12v( %d )\n", machine().describe_context(), state );
+ m_a9_12v = state;
+ }
+}
+
+
+void at28c64b_device::set_oe_12v(int state)
+{
+ state &= 1;
+ if( m_oe_12v != state )
+ {
+// logerror( "%s: AT28C64B: set_oe_12v( %d )\n", machine().describe_context(), state );
+ m_oe_12v = state;
+ }
+}
+
+
+TIMER_CALLBACK_MEMBER( at28c64b_device::write_complete )
+{
+ m_last_write = -1;
+}