mirror of
https://github.com/holub/mame
synced 2025-04-22 00:11:58 +03:00
28fxxx: initial commit for new flash memory device (#2805)
Implementation of 28F010 and family flash memory devices. These are not compatible with the JEDEC-standard flash command protocol implemented in intelfsh.
This commit is contained in:
parent
62c9bcc660
commit
f87cc5c671
@ -3301,3 +3301,15 @@ if (MACHINES["ADC0844"]~=null) then
|
||||
MAME_DIR .. "src/devices/machine/adc0844.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/28fxxx.h,MACHINES["28FXXX"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (MACHINES["28FXXX"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/machine/28fxxx.cpp",
|
||||
MAME_DIR .. "src/devices/machine/28fxxx.h",
|
||||
}
|
||||
end
|
||||
|
@ -619,6 +619,7 @@ MACHINES["I82586"] = true
|
||||
MACHINES["INPUT_MERGER"] = true
|
||||
-- MACHINES["K054321"] = true
|
||||
MACHINES["ADC0844"] = true
|
||||
MACHINES["28FXXX"] = true
|
||||
|
||||
--------------------------------------------------
|
||||
-- specify available bus cores
|
||||
|
236
src/devices/machine/28fxxx.cpp
Normal file
236
src/devices/machine/28fxxx.cpp
Normal file
@ -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;
|
||||
}
|
||||
}
|
84
src/devices/machine/28fxxx.h
Normal file
84
src/devices/machine/28fxxx.h
Normal file
@ -0,0 +1,84 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Patrick Mackinlay
|
||||
|
||||
#ifndef MAME_MACHINE_28FXXX_H
|
||||
#define MAME_MACHINE_28FXXX_H
|
||||
|
||||
#pragma once
|
||||
|
||||
class base_28fxxx_device : public device_t, public device_nvram_interface
|
||||
{
|
||||
public:
|
||||
enum commands
|
||||
{
|
||||
READ_MEMORY = 0x00,
|
||||
ERASE = 0x20,
|
||||
PROGRAM = 0x40,
|
||||
READ_IDENTIFIER_ALT = 0x80, // defined in AMD datasheet, but not Intel
|
||||
READ_IDENTIFIER = 0x90,
|
||||
ERASE_VERIFY = 0xa0,
|
||||
PROGRAM_VERIFY = 0xc0,
|
||||
RESET = 0xff
|
||||
};
|
||||
|
||||
DECLARE_WRITE_LINE_MEMBER(vpp) { m_program_power = state; }
|
||||
DECLARE_READ8_MEMBER(read);
|
||||
DECLARE_WRITE8_MEMBER(write);
|
||||
|
||||
protected:
|
||||
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);
|
||||
|
||||
virtual void device_start() override;
|
||||
|
||||
virtual void nvram_default() override;
|
||||
virtual void nvram_read(emu_file &file) override;
|
||||
virtual void nvram_write(emu_file &file) override;
|
||||
|
||||
optional_memory_region m_region;
|
||||
|
||||
private:
|
||||
void erase();
|
||||
|
||||
// device specific parameters
|
||||
const u32 m_size;
|
||||
const u8 m_manufacturer_code;
|
||||
const u8 m_device_code;
|
||||
|
||||
// accessible device state
|
||||
int m_program_power;
|
||||
std::unique_ptr<u8[]> m_data;
|
||||
|
||||
// internal state
|
||||
enum state : u8
|
||||
{
|
||||
STATE_READ_MEMORY,
|
||||
STATE_READ_IDENTIFIER,
|
||||
STATE_ERASE_SETUP,
|
||||
STATE_ERASE,
|
||||
STATE_ERASE_RESET,
|
||||
STATE_ERASE_VERIFY,
|
||||
STATE_PROGRAM_SETUP,
|
||||
STATE_PROGRAM,
|
||||
STATE_PROGRAM_VERIFY
|
||||
};
|
||||
state m_state;
|
||||
|
||||
u32 m_address_latch;
|
||||
};
|
||||
|
||||
class intel_28f010_device : public base_28fxxx_device
|
||||
{
|
||||
public:
|
||||
intel_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
|
||||
};
|
||||
|
||||
class amd_28f010_device : public base_28fxxx_device
|
||||
{
|
||||
public:
|
||||
amd_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(INTEL_28F010, intel_28f010_device)
|
||||
DECLARE_DEVICE_TYPE(AMD_28F010, amd_28f010_device)
|
||||
|
||||
#endif // MAME_MACHINE_28FXXX_H
|
Loading…
Reference in New Issue
Block a user