diff options
Diffstat (limited to 'src/devices/machine/28fxxx.cpp')
-rw-r--r-- | src/devices/machine/28fxxx.cpp | 236 |
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; + } +} |