mirror of
https://github.com/holub/mame
synced 2025-07-05 01:48:29 +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",
|
MAME_DIR .. "src/devices/machine/adc0844.h",
|
||||||
}
|
}
|
||||||
end
|
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["INPUT_MERGER"] = true
|
||||||
-- MACHINES["K054321"] = true
|
-- MACHINES["K054321"] = true
|
||||||
MACHINES["ADC0844"] = true
|
MACHINES["ADC0844"] = true
|
||||||
|
MACHINES["28FXXX"] = true
|
||||||
|
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
-- specify available bus cores
|
-- 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