summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/28fxxx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/28fxxx.cpp')
-rw-r--r--src/devices/machine/28fxxx.cpp236
1 files changed, 236 insertions, 0 deletions
diff --git a/src/devices/machine/28fxxx.cpp b/src/devices/machine/28fxxx.cpp
new file mode 100644
index 00000000000..9ef279dcd44
--- /dev/null
+++ b/src/devices/machine/28fxxx.cpp
@@ -0,0 +1,236 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * An implementation of the 28Fxxx flash memory devices, fabricated by many
+ * suppliers including Intel, Atmel, AMD, SGS, TI and Toshiba. Range includes:
+ *
+ * Part Bits Org.
+ * ------ ----- ------
+ * 28F256 256K 32Kx8
+ * 28F512 512K 64Kx8
+ * 28F010 1024K 128Kx8
+ * 28F020 2048K 256Kx8
+ * 28F040 4096K 512Kx8
+ *
+ * These devices use a JEDEC-standard pinout allowing them to be fitted to a
+ * standard EPROM socket, but have their own command set which is not
+ * compatible with the set implemented by the intelfsh devices. It appears the
+ * command set used here is a variation on the one defined by JEDEC standard
+ * 21-C, as "dual power supply eeprom command set", in figure 3.5.1-11 on page
+ * 23, here: https://www.jedec.org/system/files/docs/3_05_01R14.pdf
+ *
+ * This implementation has been developed primarily against Intel's datasheet,
+ * with some minor adjustments to match details in the AMD equivalent.
+ *
+ * TODO
+ * - implement more variants
+ * - testing in systems other than InterPro
+ *
+ */
+
+#include "emu.h"
+#include "28fxxx.h"
+
+#define VERBOSE 0
+#include "logmacro.h"
+
+// manufacturer codes defined by JEDEC: https://www.jedec.org/system/files/docs/JEP106AV.pdf
+enum manufacturer_codes
+{
+ MFG_AMD = 0x01,
+ MFG_INTEL = 0x89,
+};
+
+DEFINE_DEVICE_TYPE(INTEL_28F010, intel_28f010_device, "intel_28f010", "Intel 28F010 1024K (128K x 8) CMOS Flash Memory")
+DEFINE_DEVICE_TYPE(AMD_28F010, amd_28f010_device, "amd_28f010", "Am28F010 1 Megabit (128K x 8-Bit) CMOS 12.0 Volt, Bulk Erase Flash Memory")
+
+ALLOW_SAVE_TYPE(base_28fxxx_device::state);
+
+base_28fxxx_device::base_28fxxx_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 size, u8 manufacturer_code, u8 device_code)
+ : device_t(mconfig, type, tag, owner, clock)
+ , device_nvram_interface(mconfig, *this)
+ , m_region(*this, DEVICE_SELF)
+ , m_size(size)
+ , m_manufacturer_code(manufacturer_code)
+ , m_device_code(device_code)
+ , m_program_power(CLEAR_LINE)
+ , m_state(STATE_READ_MEMORY)
+{
+ assert_always((m_size & (m_size - 1)) == 0, "memory size must be an exact power of two");
+}
+
+intel_28f010_device::intel_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : base_28fxxx_device(mconfig, INTEL_28F010, tag, owner, clock, 0x20000, MFG_INTEL, 0xb4)
+{
+}
+
+amd_28f010_device::amd_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : base_28fxxx_device(mconfig, AMD_28F010, tag, owner, clock, 0x20000, MFG_AMD, 0xa7)
+{
+}
+
+void base_28fxxx_device::device_start()
+{
+ m_data = std::make_unique<u8[]>(m_size);
+
+ save_item(NAME(m_program_power));
+ save_pointer(NAME(m_data.get()), m_size);
+
+ save_item(NAME(m_state));
+ save_item(NAME(m_address_latch));
+}
+
+void base_28fxxx_device::nvram_default()
+{
+ if (m_region.found())
+ {
+ u32 bytes = m_region->bytes();
+ if (bytes > m_size)
+ bytes = m_size;
+
+ for (offs_t offs = 0; offs < bytes; offs++)
+ m_data[offs] = m_region->as_u8(offs);
+ }
+ else
+ erase();
+}
+
+void base_28fxxx_device::nvram_read(emu_file &file)
+{
+ file.read(m_data.get(), m_size);
+}
+
+void base_28fxxx_device::nvram_write(emu_file &file)
+{
+ file.write(m_data.get(), m_size);
+}
+
+void base_28fxxx_device::erase()
+{
+ memset(m_data.get(), 0xff, m_size);
+}
+
+READ8_MEMBER(base_28fxxx_device::read)
+{
+ switch (m_state)
+ {
+ case STATE_READ_MEMORY:
+ return m_data.get()[offset & (m_size - 1)];
+
+ case STATE_READ_IDENTIFIER:
+ switch (offset)
+ {
+ case 0: return m_manufacturer_code;
+ case 1: return m_device_code;
+ }
+ LOG("unknown read identifier offset 0x%08x (%s)\n", offset, machine().describe_context());
+ return space.unmap();
+
+ case STATE_ERASE_VERIFY:
+ return m_data.get()[m_address_latch];
+
+ case STATE_PROGRAM_VERIFY:
+ return m_data.get()[m_address_latch];
+
+ default:
+ LOG("unexpected read offset 0x%08x mask 0x%08x state %d (%s)\n", offset, mem_mask, m_state, machine().describe_context());
+ return space.unmap();
+ }
+}
+
+WRITE8_MEMBER(base_28fxxx_device::write)
+{
+ // writes are ignored unless Vpp is asserted
+ if (!m_program_power)
+ return;
+
+ switch (m_state)
+ {
+ case STATE_ERASE_SETUP:
+ if (data == ERASE)
+ {
+ LOG("erase command initiated (%s)\n", machine().describe_context());
+ erase();
+ m_state = STATE_ERASE;
+ }
+ else if (data == RESET)
+ {
+ LOG("erase command reset (%s)\n", machine().describe_context());
+ m_state = STATE_READ_MEMORY;
+ }
+ else
+ LOG("unexpected command 0x%02x in erase setup state (%s)\n", data, machine().describe_context());
+ break;
+
+ case STATE_ERASE:
+ if (data == ERASE_VERIFY)
+ {
+ m_address_latch = offset & (m_size - 1);
+ m_state = STATE_ERASE_VERIFY;
+ }
+ else
+ LOG("unexpected command 0x%02x in erase state (%s)\n", data, machine().describe_context());
+ break;
+
+ case STATE_PROGRAM_SETUP:
+ m_address_latch = offset & (m_size - 1);
+
+ LOG("programming address 0x%08x data 0x%02x\n", m_address_latch, data);
+
+ // bits can only be written from uncharged (logical 1) to charged (logical 0) state
+ m_data.get()[m_address_latch] &= data;
+
+ m_state = STATE_PROGRAM;
+ break;
+
+ case STATE_PROGRAM:
+ if (data == PROGRAM_VERIFY)
+ m_state = STATE_PROGRAM_VERIFY;
+ else if (data == RESET)
+ {
+ LOG("program command reset (%s)\n", machine().describe_context());
+
+ m_state = STATE_READ_MEMORY;
+ }
+ else
+ LOG("unexpected command 0x%02x in program state (%s)\n", data, machine().describe_context());
+ break;
+
+ default:
+ // start a new command
+ switch (data)
+ {
+ case READ_MEMORY:
+ m_state = STATE_READ_MEMORY;
+ break;
+
+ case READ_IDENTIFIER:
+ case READ_IDENTIFIER_ALT:
+ m_state = STATE_READ_IDENTIFIER;
+ break;
+
+ case ERASE:
+ m_state = STATE_ERASE_SETUP;
+ break;
+
+ case ERASE_VERIFY:
+ m_address_latch = offset & (m_size - 1);
+ m_state = STATE_ERASE_VERIFY;
+ break;
+
+ case PROGRAM:
+ m_state = STATE_PROGRAM_SETUP;
+ break;
+
+ case RESET:
+ m_state = STATE_READ_MEMORY;
+ break;
+
+ default:
+ LOG("unexpected command 0x%02x in state %d (%s)\n", data, m_state, machine().describe_context());
+ break;
+ }
+ break;
+ }
+}