mirror of
https://github.com/holub/mame
synced 2025-04-16 13:34:55 +03:00
New AT29 flash EEPROM circuits
This commit is contained in:
parent
44e60954cd
commit
4398fa4f66
@ -453,13 +453,13 @@ end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/emu/machine/at29040a.h,MACHINES["AT29040"] = true
|
||||
--@src/emu/machine/at29x.h,MACHINES["AT29X"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (MACHINES["AT29040"]~=null) then
|
||||
if (MACHINES["AT29X"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/emu/machine/at29040a.c",
|
||||
MAME_DIR .. "src/emu/machine/at29040a.h",
|
||||
MAME_DIR .. "src/emu/machine/at29x.c",
|
||||
MAME_DIR .. "src/emu/machine/at29x.h",
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -364,7 +364,7 @@ MACHINES["AM9517A"] = true
|
||||
MACHINES["AMIGAFDC"] = true
|
||||
--MACHINES["AT_KEYBC"] = true
|
||||
MACHINES["AT28C16"] = true
|
||||
MACHINES["AT29040"] = true
|
||||
MACHINES["AT29X"] = true
|
||||
MACHINES["AT45DBXX"] = true
|
||||
MACHINES["ATAFLASH"] = true
|
||||
MACHINES["AY31015"] = true
|
||||
@ -764,18 +764,18 @@ function createMAMEProjects(_target, _subtarget, _name)
|
||||
targetsubdir(_target .."_" .. _subtarget)
|
||||
kind (LIBTYPE)
|
||||
uuid (os.uuid("drv-" .. _target .."_" .. _subtarget .. "_" .._name))
|
||||
|
||||
|
||||
options {
|
||||
"ForceCPP",
|
||||
}
|
||||
|
||||
|
||||
includedirs {
|
||||
MAME_DIR .. "src/osd",
|
||||
MAME_DIR .. "src/emu",
|
||||
MAME_DIR .. "src/mame",
|
||||
MAME_DIR .. "src/lib",
|
||||
MAME_DIR .. "src/lib/util",
|
||||
MAME_DIR .. "src/emu/netlist",
|
||||
MAME_DIR .. "src/emu/netlist",
|
||||
MAME_DIR .. "3rdparty",
|
||||
GEN_DIR .. "mame/layout",
|
||||
}
|
||||
@ -786,7 +786,7 @@ function createMAMEProjects(_target, _subtarget, _name)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function createProjects_mame_arcade(_target, _subtarget)
|
||||
--------------------------------------------------
|
||||
-- the following files are general components and
|
||||
@ -2412,7 +2412,7 @@ files {
|
||||
MAME_DIR .. "src/mame/video/blockade.c",
|
||||
MAME_DIR .. "src/mame/drivers/calorie.c",
|
||||
MAME_DIR .. "src/mame/drivers/chihiro.c",
|
||||
MAME_DIR .. "src/mame/video/chihiro.c",
|
||||
MAME_DIR .. "src/mame/video/chihiro.c",
|
||||
MAME_DIR .. "src/mame/drivers/coolridr.c",
|
||||
MAME_DIR .. "src/mame/drivers/deniam.c",
|
||||
MAME_DIR .. "src/mame/video/deniam.c",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -476,7 +476,7 @@ void snug_high_speed_gpl_device::cartspace_write(address_space& space, offs_t of
|
||||
{
|
||||
logerror("%s: invalid write %04x <- %02x\n", tag(), offset, data);
|
||||
// feeprom is normally written to using GPL ports, and I don't know
|
||||
// writing through >6000 page is enabled
|
||||
// whether writing through >6000 page is enabled
|
||||
/*
|
||||
at29c040a_w(feeprom_rom6, 1 + 2*offset + 0x2000*hsgpl.cur_bank + 0x8000*port, data);
|
||||
at29c040a_w(feeprom_rom6, 2*offset + 0x2000*hsgpl.cur_bank + 0x8000*port, data >> 8);
|
||||
@ -640,10 +640,10 @@ void snug_high_speed_gpl_device::device_reset()
|
||||
|
||||
void snug_high_speed_gpl_device::device_config_complete(void)
|
||||
{
|
||||
m_dsr_eeprom = subdevice<at29040a_device>(DSR_EEPROM);
|
||||
m_rom6_eeprom = subdevice<at29040a_device>(ROM6_EEPROM);
|
||||
m_grom_a_eeprom = subdevice<at29040a_device>(GROM_A_EEPROM);
|
||||
m_grom_b_eeprom = subdevice<at29040a_device>(GROM_B_EEPROM);
|
||||
m_dsr_eeprom = subdevice<at29c040a_device>(DSR_EEPROM);
|
||||
m_rom6_eeprom = subdevice<at29c040a_device>(ROM6_EEPROM);
|
||||
m_grom_a_eeprom = subdevice<at29c040a_device>(GROM_A_EEPROM);
|
||||
m_grom_b_eeprom = subdevice<at29c040a_device>(GROM_B_EEPROM);
|
||||
}
|
||||
|
||||
void snug_high_speed_gpl_device::device_stop()
|
||||
@ -661,10 +661,10 @@ INPUT_PORTS_START( ti99_hsgpl)
|
||||
INPUT_PORTS_END
|
||||
|
||||
MACHINE_CONFIG_FRAGMENT( ti99_hsgpl )
|
||||
MCFG_AT29040A_ADD( DSR_EEPROM )
|
||||
MCFG_AT29040A_ADD( GROM_B_EEPROM )
|
||||
MCFG_AT29040A_ADD( GROM_A_EEPROM )
|
||||
MCFG_AT29040A_ADD( ROM6_EEPROM )
|
||||
MCFG_AT29C040A_ADD( DSR_EEPROM )
|
||||
MCFG_AT29C040A_ADD( GROM_B_EEPROM )
|
||||
MCFG_AT29C040A_ADD( GROM_A_EEPROM )
|
||||
MCFG_AT29C040A_ADD( ROM6_EEPROM )
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
machine_config_constructor snug_high_speed_gpl_device::device_mconfig_additions() const
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#include "emu.h"
|
||||
#include "peribox.h"
|
||||
#include "machine/at29040a.h"
|
||||
#include "machine/at29x.h"
|
||||
|
||||
extern const device_type TI99_HSGPL;
|
||||
|
||||
@ -42,10 +42,10 @@ protected:
|
||||
virtual machine_config_constructor device_mconfig_additions() const;
|
||||
|
||||
private:
|
||||
at29040a_device* m_dsr_eeprom;
|
||||
at29040a_device* m_rom6_eeprom;
|
||||
at29040a_device* m_grom_a_eeprom;
|
||||
at29040a_device* m_grom_b_eeprom;
|
||||
at29c040a_device* m_dsr_eeprom;
|
||||
at29c040a_device* m_rom6_eeprom;
|
||||
at29c040a_device* m_grom_a_eeprom;
|
||||
at29c040a_device* m_grom_b_eeprom;
|
||||
|
||||
UINT8* m_ram6_memory;
|
||||
UINT8* m_gram_memory;
|
||||
|
@ -1,446 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Raphael Nabet, Michael Zapf
|
||||
/*
|
||||
Atmel at29c040a flash EEPROM
|
||||
|
||||
512k*8 FEEPROM, organized in pages of 256 bytes.
|
||||
|
||||
References:
|
||||
Datasheets were found on Atmel's site (www.atmel.com)
|
||||
|
||||
Raphael Nabet 2003
|
||||
|
||||
September 2010: Rewritten as device
|
||||
February 2012: Rewritten as class
|
||||
*/
|
||||
|
||||
#include "at29040a.h"
|
||||
|
||||
#define VERBOSE 2
|
||||
#define LOG logerror
|
||||
|
||||
#define FEEPROM_SIZE 0x80000
|
||||
#define SECTOR_SIZE 0x00100
|
||||
#define BOOT_BLOCK_SIZE 0x04000
|
||||
|
||||
#define ADDRESS_MASK 0x7ffff
|
||||
#define SECTOR_ADDRESS_MASK 0x7ff00
|
||||
#define BYTE_ADDRESS_MASK 0x000ff
|
||||
|
||||
#define PRG_TIMER 1
|
||||
|
||||
#define VERSION 0
|
||||
|
||||
/*
|
||||
Constructor.
|
||||
*/
|
||||
at29040a_device::at29040a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
: device_t(mconfig, AT29040A, "ATMEL 29040A 512K*8 FEEPROM", tag, owner, clock, "at29040a", __FILE__),
|
||||
device_nvram_interface(mconfig, *this)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// nvram_default - called to initialize NVRAM to
|
||||
// its default state
|
||||
//-------------------------------------------------
|
||||
|
||||
void at29040a_device::nvram_default()
|
||||
{
|
||||
memset(m_eememory, 0, FEEPROM_SIZE+2);
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// nvram_read - called to read NVRAM from the
|
||||
// .nv file
|
||||
//-------------------------------------------------
|
||||
|
||||
void at29040a_device::nvram_read(emu_file &file)
|
||||
{
|
||||
file.read(m_eememory, FEEPROM_SIZE+2);
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// nvram_write - called to write NVRAM to the
|
||||
// .nv file
|
||||
//-------------------------------------------------
|
||||
|
||||
void at29040a_device::nvram_write(emu_file &file)
|
||||
{
|
||||
m_eememory[0] = VERSION;
|
||||
file.write(m_eememory, FEEPROM_SIZE+2);
|
||||
}
|
||||
|
||||
/*
|
||||
programming timer callback
|
||||
*/
|
||||
void at29040a_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
{
|
||||
switch (m_pgm)
|
||||
{
|
||||
case PGM_1:
|
||||
/* programming cycle timeout */
|
||||
if (VERBOSE>7) LOG("at29040a: Programming cycle timeout\n");
|
||||
m_pgm = PGM_0;
|
||||
break;
|
||||
|
||||
case PGM_2:
|
||||
/* programming cycle start */
|
||||
if (VERBOSE>7) LOG("at29040a: Sector write start\n");
|
||||
m_pgm = PGM_3;
|
||||
/* max delay 10ms, typical delay 5 to 7 ms */
|
||||
m_programming_timer->adjust(attotime::from_msec(5));
|
||||
break;
|
||||
|
||||
case PGM_3:
|
||||
/* programming cycle end */
|
||||
memcpy(m_eememory + 2 + (m_programming_last_offset & ~0xff), m_programming_buffer, SECTOR_SIZE);
|
||||
if (VERBOSE>7) LOG("at29040a: Sector write completed at location %04x + 2\n", (m_programming_last_offset & ~0xff));
|
||||
if (m_enabling_sdb)
|
||||
{
|
||||
m_sdp = true;
|
||||
}
|
||||
if (m_disabling_sdb)
|
||||
{
|
||||
m_sdp = false;
|
||||
}
|
||||
if (VERBOSE>7) LOG("at29040a: Software data protection = %d\n", m_sdp);
|
||||
|
||||
m_pgm = PGM_0;
|
||||
m_enabling_sdb = false;
|
||||
m_disabling_sdb = false;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if (VERBOSE>1) LOG("internal error in %s %d\n", __FILE__, __LINE__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void at29040a_device::sync_flags()
|
||||
{
|
||||
if (m_lower_bbl) m_eememory[1] |= 0x04;
|
||||
else m_eememory[1] &= ~0x04;
|
||||
|
||||
if (m_higher_bbl) m_eememory[1] |= 0x02;
|
||||
else m_eememory[1] &= ~0x02;
|
||||
|
||||
if (m_sdp) m_eememory[1] |= 0x01;
|
||||
else m_eememory[1] &= ~0x01;
|
||||
}
|
||||
|
||||
/*
|
||||
read a byte from FEEPROM
|
||||
*/
|
||||
READ8_MEMBER( at29040a_device::read )
|
||||
{
|
||||
int reply;
|
||||
|
||||
offset &= ADDRESS_MASK;
|
||||
|
||||
/* reading in the midst of any command sequence cancels it (right???) */
|
||||
m_cmd = CMD_0;
|
||||
m_long_sequence = false;
|
||||
// m_higher_bbl = true; // who says that?
|
||||
|
||||
sync_flags();
|
||||
|
||||
/* reading before the start of a programming cycle cancels it (right???) */
|
||||
if (m_pgm == PGM_1)
|
||||
{
|
||||
// attempt to access a locked out boot block: cancel programming
|
||||
// command if necessary
|
||||
m_pgm = PGM_0;
|
||||
m_enabling_sdb = false;
|
||||
m_disabling_sdb = false;
|
||||
m_programming_timer->adjust(attotime::never);
|
||||
}
|
||||
|
||||
if (m_id_mode)
|
||||
{
|
||||
switch (offset)
|
||||
{
|
||||
case 0x00000:
|
||||
reply = 0x1f; // Manufacturer code
|
||||
break;
|
||||
|
||||
case 0x00001:
|
||||
reply = 0xa4; // Device code
|
||||
break;
|
||||
|
||||
case 0x00002:
|
||||
reply = m_lower_bbl? 0xff : 0xfe;
|
||||
break;
|
||||
|
||||
case 0x7fff2:
|
||||
reply = m_higher_bbl? 0xff : 0xfe;
|
||||
break;
|
||||
|
||||
default:
|
||||
reply = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((m_pgm == PGM_2) || (m_pgm == PGM_3))
|
||||
{
|
||||
if (m_pgm == PGM_2)
|
||||
{ // DATA* polling starts the programming cycle (right???)
|
||||
m_pgm = PGM_3;
|
||||
/* max delay 10ms, typical delay 5 to 7 ms */
|
||||
m_programming_timer->adjust(attotime::from_msec(5));
|
||||
}
|
||||
|
||||
reply = m_toggle_bit? 0x02 : 0x00;
|
||||
m_toggle_bit = !m_toggle_bit;
|
||||
|
||||
if ((offset == m_programming_last_offset) && (! (m_programming_buffer[m_programming_last_offset & 0xff] & 0x01)))
|
||||
reply |= 0x01;
|
||||
}
|
||||
else
|
||||
reply = m_eememory[offset+2];
|
||||
|
||||
if (VERBOSE>7) LOG("at29040a: %05x -> %02x\n", offset, reply);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
/*
|
||||
Write a byte to FEEPROM
|
||||
*/
|
||||
WRITE8_MEMBER( at29040a_device::write )
|
||||
{
|
||||
offset &= ADDRESS_MASK;
|
||||
if (VERBOSE>7) LOG("at29040a: %05x <- %02x\n", offset, data);
|
||||
|
||||
/* The special CFI commands assume a smaller address space according */
|
||||
/* to the specification ("address format A14-A0") */
|
||||
offs_t cfi_offset = offset & 0x7fff;
|
||||
|
||||
if (m_enabling_bbl)
|
||||
{
|
||||
if (VERBOSE>7) LOG("at29040a: Enabling boot block lockout\n");
|
||||
m_enabling_bbl = false;
|
||||
|
||||
if ((offset == 0x00000) && (data == 0x00))
|
||||
{
|
||||
if (VERBOSE>7) LOG("at29040a: Enabling lower boot block lockout\n");
|
||||
m_lower_bbl = true;
|
||||
sync_flags();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((offset == 0x7ffff) && (data == 0xff))
|
||||
{
|
||||
if (VERBOSE>7) LOG("at29040a: Enabling higher boot block lockout\n");
|
||||
m_higher_bbl = true;
|
||||
sync_flags();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (VERBOSE>1) LOG("at29040a: Invalid boot block specification: %05x/%02x\n", offset, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (m_cmd)
|
||||
{
|
||||
case CMD_0:
|
||||
if ((cfi_offset == 0x5555) && (data == 0xaa))
|
||||
{
|
||||
if (VERBOSE>7) LOG("at29040a: Command sequence started\n");
|
||||
m_cmd = CMD_1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cmd = CMD_0;
|
||||
m_long_sequence = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_1:
|
||||
if ((cfi_offset == 0x2aaa) && (data == 0x55))
|
||||
{
|
||||
m_cmd = CMD_2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cmd = CMD_0;
|
||||
m_long_sequence = false;
|
||||
if (VERBOSE>7) LOG("at29040a: Command sequence aborted\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_2:
|
||||
if (cfi_offset == 0x5555)
|
||||
{
|
||||
if (!m_long_sequence)
|
||||
if (VERBOSE>7) LOG("at29040a: Command sequence completed\n");
|
||||
|
||||
m_pgm = PGM_0;
|
||||
m_enabling_sdb = false;
|
||||
m_disabling_sdb = false;
|
||||
m_programming_timer->adjust(attotime::never);
|
||||
|
||||
/* process command */
|
||||
switch (data)
|
||||
{
|
||||
case 0x10:
|
||||
/* Software chip erase */
|
||||
if (m_long_sequence)
|
||||
{
|
||||
if (m_lower_bbl || m_higher_bbl)
|
||||
{
|
||||
if (VERBOSE>1) LOG("at29040a: Chip erase sequence deactivated due to previous boot block lockout.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (VERBOSE>7) LOG("at29040a: Erase chip\n");
|
||||
memset(m_eememory+2, 0xff, FEEPROM_SIZE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x20:
|
||||
/* Software data protection disable */
|
||||
if (VERBOSE>7) LOG("at29040a: Software data protection disable\n");
|
||||
// The complete sequence is aa-55-80-aa-55-20
|
||||
// so we need a 80 before, else the sequence is invalid
|
||||
if (m_long_sequence)
|
||||
{
|
||||
m_pgm = PGM_1;
|
||||
m_disabling_sdb = true;
|
||||
/* set command timeout (right???) */
|
||||
//m_programming_timer->adjust(attotime::from_usec(150), id, 0.);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
/* Boot block lockout enable */
|
||||
// Complete sequence is aa-55-80-aa-55-40
|
||||
if (VERBOSE>7) LOG("at29040a: Boot block lockout enable\n");
|
||||
if (m_long_sequence) m_enabling_bbl = true;
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
m_long_sequence = true;
|
||||
break;
|
||||
|
||||
case 0x90:
|
||||
/* Software product identification entry */
|
||||
if (VERBOSE>7) LOG("at29040a: Identification mode (start)\n");
|
||||
m_id_mode = true;
|
||||
break;
|
||||
|
||||
case 0xa0:
|
||||
/* Software data protection enable */
|
||||
if (VERBOSE>7) LOG("at29040a: Software data protection enable\n");
|
||||
m_pgm = PGM_1;
|
||||
m_enabling_sdb = true;
|
||||
/* set command timeout (right???) */
|
||||
//m_programming_timer->adjust(attotime::from_usec(150), id, 0.);
|
||||
break;
|
||||
|
||||
case 0xf0:
|
||||
/* Software product identification exit */
|
||||
if (VERBOSE>7) LOG("at29040a: Identification mode (end)\n");
|
||||
m_id_mode = false;
|
||||
break;
|
||||
}
|
||||
m_cmd = CMD_0;
|
||||
if (data != 0x80) m_long_sequence = false;
|
||||
|
||||
/* return, because we don't want to write the EEPROM with the command byte */
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cmd = CMD_0;
|
||||
m_long_sequence = false;
|
||||
}
|
||||
}
|
||||
if ((m_pgm == PGM_2)
|
||||
&& ((offset & ~0xff) != (m_programming_last_offset & ~0xff)))
|
||||
{
|
||||
/* cancel current programming cycle */
|
||||
if (VERBOSE>7) LOG("at29040a: invalid sector change (from %05x to %05x); cancel programming cycle\n",(offset & ~0xff), (m_programming_last_offset & ~0xff));
|
||||
m_pgm = PGM_0;
|
||||
m_enabling_sdb = false;
|
||||
m_disabling_sdb = false;
|
||||
m_programming_timer->adjust(attotime::never);
|
||||
}
|
||||
|
||||
if (((m_pgm == PGM_0) && !m_sdp) // write directly
|
||||
|| (m_pgm == PGM_1)) // write after unlocking
|
||||
{
|
||||
if (((offset < BOOT_BLOCK_SIZE) && m_lower_bbl)
|
||||
|| ((offset >= FEEPROM_SIZE-BOOT_BLOCK_SIZE) && m_higher_bbl))
|
||||
{
|
||||
// attempt to access a locked out boot block: cancel programming
|
||||
// command if necessary
|
||||
if (VERBOSE>7) LOG("at29040a: attempt to access a locked out boot block: offset = %05x, lowblock=%d, highblock=%d\n", offset, m_lower_bbl, m_higher_bbl);
|
||||
|
||||
m_pgm = PGM_0;
|
||||
m_enabling_sdb = false;
|
||||
m_disabling_sdb = false;
|
||||
}
|
||||
else
|
||||
{ /* enter programming mode */
|
||||
if (VERBOSE>7) LOG("at29040a: enter programming mode (m_pgm=%d)\n", m_pgm);
|
||||
memset(m_programming_buffer, 0xff, SECTOR_SIZE);
|
||||
m_pgm = PGM_2;
|
||||
}
|
||||
}
|
||||
if (m_pgm == PGM_2)
|
||||
{
|
||||
/* write data to programming buffer */
|
||||
if (VERBOSE>7) LOG("at29040a: Write data to programming buffer\n");
|
||||
m_programming_buffer[offset & 0xff] = data;
|
||||
m_programming_last_offset = offset;
|
||||
m_programming_timer->adjust(attotime::from_usec(150)); // next byte must be written before the timer expires
|
||||
}
|
||||
}
|
||||
|
||||
void at29040a_device::device_start(void)
|
||||
{
|
||||
m_programming_buffer = global_alloc_array(UINT8, SECTOR_SIZE);
|
||||
m_programming_timer = timer_alloc(PRG_TIMER);
|
||||
|
||||
m_eememory = global_alloc_array(UINT8, FEEPROM_SIZE+2);
|
||||
}
|
||||
|
||||
void at29040a_device::device_stop(void)
|
||||
{
|
||||
global_free_array(m_programming_buffer);
|
||||
global_free_array(m_eememory);
|
||||
}
|
||||
|
||||
void at29040a_device::device_reset(void)
|
||||
{
|
||||
if (m_eememory[0] != VERSION)
|
||||
{
|
||||
if (VERBOSE>1) LOG("AT29040A: Warning: Version mismatch; expected %d but found %d for %s. Resetting.\n", VERSION, m_eememory[0], tag());
|
||||
m_eememory[0] = 0;
|
||||
m_eememory[1] = 0;
|
||||
}
|
||||
|
||||
m_lower_bbl = ((m_eememory[1] & 0x04)!=0);
|
||||
m_higher_bbl = ((m_eememory[1] & 0x02)!=0);
|
||||
m_sdp = ((m_eememory[1] & 0x01)!=0);
|
||||
|
||||
if (VERBOSE>7) LOG("at29040a (%s): LowerBBL = %d, HigherBBL = %d, SoftDataProt = %d\n", tag(), m_lower_bbl, m_higher_bbl, m_sdp);
|
||||
|
||||
m_id_mode = false;
|
||||
m_cmd = CMD_0;
|
||||
m_enabling_bbl = false;
|
||||
m_long_sequence = false;
|
||||
m_pgm = PGM_0;
|
||||
m_enabling_sdb = false;
|
||||
m_disabling_sdb = false;
|
||||
m_toggle_bit = false;
|
||||
m_programming_last_offset = 0;
|
||||
}
|
||||
|
||||
const device_type AT29040A = &device_creator<at29040a_device>;
|
@ -1,90 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Raphael Nabet, Michael Zapf
|
||||
/*
|
||||
ATMEL 29040a
|
||||
|
||||
Michael Zapf
|
||||
September 2010: Rewritten as device
|
||||
February 2012: Rewritten as class
|
||||
*/
|
||||
|
||||
#ifndef __AT29040__
|
||||
#define __AT29040__
|
||||
|
||||
#include "emu.h"
|
||||
|
||||
extern const device_type AT29040A;
|
||||
|
||||
/*
|
||||
at29c40a state
|
||||
|
||||
Command states (CMD_0 is the initial state):
|
||||
CMD_0: default state
|
||||
CMD_1: state after writing aa to 5555
|
||||
CMD_2: state after writing 55 to 2aaa
|
||||
|
||||
Programming states (s_programming_0 is the initial state):
|
||||
PGM_0: default state
|
||||
PGM_1: a program and enable/disable lock command has been executed, but programming has not actually started.
|
||||
PGM_2: the programming buffer is being written to
|
||||
PGM_3: the programming buffer is being burnt to flash ROM
|
||||
*/
|
||||
enum s_cmd_t
|
||||
{
|
||||
CMD_0 = 0x0,
|
||||
CMD_1 = 0x1,
|
||||
CMD_2 = 0x2
|
||||
};
|
||||
|
||||
enum s_pgm_t
|
||||
{
|
||||
PGM_0 = 0x0,
|
||||
PGM_1 = 0x1,
|
||||
PGM_2 = 0x2,
|
||||
PGM_3 = 0x3
|
||||
};
|
||||
|
||||
class at29040a_device : public device_t, public device_nvram_interface
|
||||
{
|
||||
public:
|
||||
at29040a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
DECLARE_READ8_MEMBER( read );
|
||||
DECLARE_WRITE8_MEMBER( write );
|
||||
|
||||
protected:
|
||||
virtual void device_start(void);
|
||||
virtual void device_reset(void);
|
||||
virtual void device_stop(void);
|
||||
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
|
||||
|
||||
void nvram_default();
|
||||
void nvram_read(emu_file &file);
|
||||
void nvram_write(emu_file &file);
|
||||
|
||||
private:
|
||||
void sync_flags(void);
|
||||
|
||||
UINT8* m_eememory;
|
||||
|
||||
bool m_lower_bbl; /* set when lower boot block lockout is enabled */
|
||||
bool m_higher_bbl; /* set when upper boot block lockout is enabled */
|
||||
bool m_sdp; /* set when in software data protect mode */
|
||||
|
||||
bool m_id_mode; /* set when in chip id mode */
|
||||
s_cmd_t m_cmd; /* command state */
|
||||
bool m_enabling_bbl; /* set when a boot block lockout command is expecting its parameter */
|
||||
bool m_long_sequence; /* set if 0x80 command has just been executed (some command require this prefix) */
|
||||
s_pgm_t m_pgm; /* programming state */
|
||||
bool m_enabling_sdb; /* set when a sdp enable command is in progress */
|
||||
bool m_disabling_sdb; /* set when a sdp disable command is in progress */
|
||||
//bool m_dirty; /* set when the memory contents should be set */
|
||||
bool m_toggle_bit; // indicates flashing in progress (toggles for each query)
|
||||
UINT8* m_programming_buffer;
|
||||
int m_programming_last_offset;
|
||||
emu_timer* m_programming_timer;
|
||||
};
|
||||
|
||||
#define MCFG_AT29040A_ADD(_tag ) \
|
||||
MCFG_DEVICE_ADD(_tag, AT29040A, 0)
|
||||
|
||||
#endif
|
529
src/emu/machine/at29x.c
Normal file
529
src/emu/machine/at29x.c
Normal file
@ -0,0 +1,529 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Michael Zapf
|
||||
/*
|
||||
ATMEL AT29 family of Flash EEPROM
|
||||
|
||||
References:
|
||||
[1] ATMEL: 4-megabit (512K x 8) 5-volt Only 256-byte sector Flash Memory
|
||||
[2] ATMEL: Programming Atmel's AT29 Flash Family
|
||||
|
||||
|
||||
AT29 family
|
||||
|
||||
Device Memory ID Sectors Sector Size Write Cycle Time Comments
|
||||
------------------------------------------------------------------------------------------
|
||||
AT29C256 32K x 8 DC 512 64 bytes 10 ms
|
||||
AT29LV256 32K x 8 BC 512 64 bytes 20 ms
|
||||
AT29C257 32K x 8 DC 512 64 bytes 10 ms
|
||||
AT29C512 64K x 8 5D 512 128 bytes 10 ms
|
||||
AT29LV512 64K x 8 3D 512 128 bytes 20 ms
|
||||
AT29C010A 128K x 8 D5 1024 128 bytes 10 ms
|
||||
AT29LV010A 128K x 8 35 1024 128 bytes 20 ms
|
||||
AT29BV010A 128K x 8 35 1024 128 bytes 20 ms
|
||||
AT29C1024 64K x 16 25 512 128 words 10 ms
|
||||
AT29LV1024 64K x 16 26 512 128 words 20 ms
|
||||
AT29C020 256K x 8 DA 1024 256 bytes 10 ms
|
||||
AT29LV020 256K x 8 BA 1024 256 bytes 20 ms
|
||||
AT29BV020 256K x 8 BA 1024 256 bytes 20 ms
|
||||
AT29C040 512K x 8 5B 1024 512 bytes 10 ms Use AT29C040A for new designs
|
||||
AT29LV040 512K x 8 3B 1024 512 bytes 20 ms Use AT29LV040A for new designs
|
||||
AT29BV040 512K x 8 3B 1024 512 bytes 20 ms Use AT29BV040A for new designs
|
||||
AT29C040A 512K x 8 A4 2048 256 bytes 10 ms
|
||||
AT29LV040A 512K x 8 C4 2048 256 bytes 20 ms
|
||||
AT29BV040A 512K x 8 C4 2048 256 bytes 20 ms
|
||||
|
||||
TODO: Implement remaining variants
|
||||
|
||||
MZ, Aug 2015
|
||||
*/
|
||||
|
||||
#include "at29x.h"
|
||||
|
||||
#define TRACE_PRG 1
|
||||
#define TRACE_READ 0
|
||||
#define TRACE_WRITE 1
|
||||
#define TRACE_CONFIG 1
|
||||
#define TRACE_STATE 1
|
||||
|
||||
enum
|
||||
{
|
||||
PRGTIMER = 1
|
||||
};
|
||||
|
||||
/*
|
||||
Constructor for all variants
|
||||
*/
|
||||
|
||||
at29x_device::at29x_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
|
||||
: device_t(mconfig, type, name, tag, owner, clock, shortname, source),
|
||||
device_nvram_interface(mconfig, *this),
|
||||
m_memory_size(0), // bytes
|
||||
m_word_width(8),
|
||||
m_device_id(0),
|
||||
m_sector_size(0),
|
||||
m_cycle_time(10), // ms
|
||||
m_boot_block_size(16*1024),
|
||||
m_version(0)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
Constructor for AT29C020
|
||||
*/
|
||||
at29c020_device::at29c020_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
: at29x_device(mconfig, AT29C020, "ATMEL 29C020 256K x 8 FEEPROM", tag, owner, clock, "at29c020", __FILE__)
|
||||
{
|
||||
m_memory_size = 256*1024;
|
||||
m_device_id = 0xda;
|
||||
m_sector_size = 256;
|
||||
}
|
||||
|
||||
/*
|
||||
Constructor for AT29C040
|
||||
*/
|
||||
at29c040_device::at29c040_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
: at29x_device(mconfig, AT29C040, "ATMEL 29C040 512K x 8 FEEPROM", tag, owner, clock, "at29c040", __FILE__)
|
||||
{
|
||||
m_memory_size = 512*1024;
|
||||
m_device_id = 0x5b;
|
||||
m_sector_size = 512;
|
||||
}
|
||||
|
||||
/*
|
||||
Constructor for AT29C040A
|
||||
*/
|
||||
at29c040a_device::at29c040a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
: at29x_device(mconfig, AT29C040A, "ATMEL 29C040A 512K x 8 FEEPROM", tag, owner, clock, "at29c040a", __FILE__)
|
||||
{
|
||||
m_memory_size = 512*1024;
|
||||
m_device_id = 0xa4;
|
||||
m_sector_size = 256;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// nvram_default - called to initialize NVRAM to
|
||||
// its default state
|
||||
//-------------------------------------------------
|
||||
|
||||
void at29x_device::nvram_default()
|
||||
{
|
||||
memset(m_eememory, 0, m_memory_size+2);
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// nvram_read - called to read NVRAM from the
|
||||
// .nv file
|
||||
//-------------------------------------------------
|
||||
|
||||
void at29x_device::nvram_read(emu_file &file)
|
||||
{
|
||||
file.read(m_eememory, m_memory_size+2);
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// nvram_write - called to write NVRAM to the
|
||||
// .nv file
|
||||
//-------------------------------------------------
|
||||
|
||||
void at29x_device::nvram_write(emu_file &file)
|
||||
{
|
||||
// If we don't write (because there were no changes), the file will be wiped
|
||||
if (TRACE_PRG) logerror("%s: Write to NVRAM file\n", tag());
|
||||
m_eememory[0] = m_version;
|
||||
file.write(m_eememory, m_memory_size+2);
|
||||
}
|
||||
|
||||
/*
|
||||
Programming timer callback
|
||||
*/
|
||||
void at29x_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
{
|
||||
switch (m_pgm)
|
||||
{
|
||||
case PGM_1:
|
||||
// Programming cycle timeout
|
||||
logerror("%s: Programming cycle timeout\n", tag());
|
||||
m_pgm = PGM_0;
|
||||
break;
|
||||
|
||||
case PGM_2:
|
||||
// Programming cycle start
|
||||
if (TRACE_PRG) logerror("%s: Sector write start\n", tag());
|
||||
m_pgm = PGM_3;
|
||||
// We assume a typical delay of 70% of the max value
|
||||
m_programming_timer->adjust(attotime::from_msec(m_cycle_time*7/10));
|
||||
break;
|
||||
|
||||
case PGM_3:
|
||||
// Programming cycle end; now burn the buffer into the flash EEPROM
|
||||
memcpy(m_eememory + 2 + get_sector_number(m_programming_last_offset) * m_sector_size, m_programming_buffer, m_sector_size);
|
||||
|
||||
if (TRACE_PRG) logerror("%s: Sector write completed at location %04x\n", tag(), m_programming_last_offset);
|
||||
|
||||
// Data protect state will be activated at the end of the program cycle [1]
|
||||
if (m_enabling_sdb) m_sdp = true;
|
||||
|
||||
// Data protect state will be deactivated at the end of the program period [1]
|
||||
if (m_disabling_sdb) m_sdp = false;
|
||||
|
||||
if (TRACE_PRG) logerror("%s: Software data protection = %d\n", tag(), m_sdp);
|
||||
|
||||
m_pgm = PGM_0;
|
||||
m_enabling_sdb = false;
|
||||
m_disabling_sdb = false;
|
||||
sync_flags();
|
||||
break;
|
||||
|
||||
default:
|
||||
logerror("%s: Invalid state %d during programming\n", tag(), m_pgm);
|
||||
m_pgm = PGM_0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void at29x_device::sync_flags()
|
||||
{
|
||||
if (m_lower_bbl) m_eememory[1] |= 0x04;
|
||||
else m_eememory[1] &= ~0x04;
|
||||
|
||||
if (m_higher_bbl) m_eememory[1] |= 0x02;
|
||||
else m_eememory[1] &= ~0x02;
|
||||
|
||||
if (m_sdp) m_eememory[1] |= 0x01;
|
||||
else m_eememory[1] &= ~0x01;
|
||||
}
|
||||
|
||||
/*
|
||||
read a byte from FEEPROM
|
||||
*/
|
||||
READ8_MEMBER( at29x_device::read )
|
||||
{
|
||||
int reply;
|
||||
|
||||
offset &= m_address_mask;
|
||||
|
||||
// Reading in the midst of any command sequence cancels it (not verified)
|
||||
m_cmd = CMD_0;
|
||||
m_long_sequence = false;
|
||||
|
||||
sync_flags();
|
||||
|
||||
// Reading before the start of a programming cycle cancels it (not verified)
|
||||
if (m_pgm == PGM_1)
|
||||
{
|
||||
// Attempt to access a locked out boot block: cancel programming command if necessary
|
||||
m_pgm = PGM_0;
|
||||
m_enabling_sdb = false;
|
||||
m_disabling_sdb = false;
|
||||
m_programming_timer->adjust(attotime::never);
|
||||
}
|
||||
|
||||
if (m_id_mode)
|
||||
{
|
||||
switch (offset)
|
||||
{
|
||||
case 0x00000:
|
||||
reply = 0x1f; // Manufacturer code
|
||||
break;
|
||||
|
||||
case 0x00001:
|
||||
reply = 0xa4; // Device code
|
||||
break;
|
||||
|
||||
// Boot block lockout detection [1]
|
||||
case 0x00002:
|
||||
reply = m_lower_bbl? 0xff : 0xfe;
|
||||
break;
|
||||
|
||||
case 0x7fff2:
|
||||
reply = m_higher_bbl? 0xff : 0xfe;
|
||||
break;
|
||||
|
||||
default:
|
||||
reply = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((m_pgm == PGM_2) || (m_pgm == PGM_3))
|
||||
{
|
||||
if (m_pgm == PGM_2)
|
||||
{
|
||||
// DATA* polling starts the programming cycle (not verified)
|
||||
m_pgm = PGM_3;
|
||||
|
||||
// We assume a typical delay of 70% of the max value
|
||||
m_programming_timer->adjust(attotime::from_msec(m_cycle_time*7/10));
|
||||
}
|
||||
|
||||
reply = m_toggle_bit? 0x02 : 0x00;
|
||||
m_toggle_bit = !m_toggle_bit;
|
||||
|
||||
// When we read the byte on the last position, we get the inverse of the last bit [1]
|
||||
if (offset == m_programming_last_offset)
|
||||
{
|
||||
reply |= ((~m_programming_buffer[m_programming_last_offset & m_sector_mask]) & 0x01);
|
||||
}
|
||||
}
|
||||
else
|
||||
// Simple case: just read the memory contents
|
||||
reply = m_eememory[offset+2];
|
||||
|
||||
if (TRACE_READ) logerror("%s: %05x -> %02x\n", tag(), offset, reply);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
/*
|
||||
Write a byte to FEEPROM
|
||||
*/
|
||||
WRITE8_MEMBER( at29x_device::write )
|
||||
{
|
||||
offset &= m_address_mask;
|
||||
if (TRACE_WRITE) logerror("%s: %05x <- %02x\n", tag(), offset, data);
|
||||
|
||||
// The special CFI commands assume a smaller address space according
|
||||
// to the specification ("address format A14-A0")
|
||||
offs_t cfi_offset = offset & 0x7fff;
|
||||
|
||||
if (m_enabling_bbl)
|
||||
{
|
||||
// Determine whether we lock the upper or lower boot block
|
||||
if (TRACE_STATE) logerror("%s: Enabling boot block lockout\n", tag());
|
||||
m_enabling_bbl = false;
|
||||
|
||||
if ((offset == 0x00000) && (data == 0x00))
|
||||
{
|
||||
if (TRACE_STATE) logerror("%s: Enabling lower boot block lockout\n", tag());
|
||||
m_lower_bbl = true;
|
||||
sync_flags();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((offset == 0x7ffff) && (data == 0xff))
|
||||
{
|
||||
if (TRACE_STATE) logerror("%s: Enabling higher boot block lockout\n", tag());
|
||||
m_higher_bbl = true;
|
||||
sync_flags();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
logerror("%s: Invalid boot block specification: %05x/%02x\n", tag(), offset, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (m_cmd)
|
||||
{
|
||||
case CMD_0:
|
||||
// CMD_0: start state
|
||||
if ((cfi_offset == 0x5555) && (data == 0xaa))
|
||||
{
|
||||
if (TRACE_STATE) logerror("%s: Command sequence started (aa)\n", tag());
|
||||
m_cmd = CMD_1;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cmd = CMD_0;
|
||||
m_long_sequence = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_1:
|
||||
// CMD_1: state after writing aa to 5555
|
||||
if ((cfi_offset == 0x2aaa) && (data == 0x55))
|
||||
{
|
||||
if (TRACE_STATE) logerror("%s: Command sequence continued (55)\n", tag());
|
||||
m_cmd = CMD_2;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cmd = CMD_0;
|
||||
m_long_sequence = false;
|
||||
if (TRACE_STATE) logerror("%s: Command sequence aborted\n", tag());
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_2:
|
||||
// CMD_2: state after writing 55 to 2aaa
|
||||
if (cfi_offset == 0x5555)
|
||||
{
|
||||
m_pgm = PGM_0;
|
||||
m_enabling_sdb = false;
|
||||
m_disabling_sdb = false;
|
||||
m_programming_timer->adjust(attotime::never);
|
||||
|
||||
// Process command
|
||||
if (TRACE_STATE) logerror("%s: Command sequence continued (%2x)\n", tag(), data);
|
||||
switch (data)
|
||||
{
|
||||
case 0x10:
|
||||
// Software chip erase (optional feature, see [1])
|
||||
if (m_long_sequence)
|
||||
{
|
||||
if (m_lower_bbl || m_higher_bbl)
|
||||
logerror("%s: Boot block lockout active; chip cannot be erased.\n", tag());
|
||||
else
|
||||
{
|
||||
if (TRACE_STATE) logerror("%s: Erase chip\n", tag());
|
||||
memset(m_eememory+2, 0xff, m_memory_size);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x20:
|
||||
// Software data protection disable
|
||||
// The complete sequence is aa-55-80-aa-55-20
|
||||
// so we need a 80 before, else the sequence is invalid
|
||||
if (m_long_sequence)
|
||||
{
|
||||
if (TRACE_STATE) logerror("%s: Software data protection disable\n", tag());
|
||||
m_pgm = PGM_1;
|
||||
m_disabling_sdb = true;
|
||||
// It is not clear from the specification whether the byte cycle timer
|
||||
// is already started here or when the first data byte is written
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
// Boot block lockout enable
|
||||
// Complete sequence is aa-55-80-aa-55-40
|
||||
if (TRACE_STATE) logerror("%s: Boot block lockout enable\n", tag());
|
||||
if (m_long_sequence) m_enabling_bbl = true;
|
||||
// We'll know which boot block is affected on the next write
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
// Long sequences are those that contain aa55 twice
|
||||
m_long_sequence = true;
|
||||
break;
|
||||
|
||||
case 0x90:
|
||||
// Software product identification entry
|
||||
if (TRACE_STATE) logerror("%s: Entering Identification mode\n", tag());
|
||||
m_id_mode = true;
|
||||
break;
|
||||
|
||||
case 0xa0:
|
||||
// Software data protection enable
|
||||
if (TRACE_STATE) logerror("%s: Software data protection enable\n", tag());
|
||||
m_pgm = PGM_1;
|
||||
m_enabling_sdb = true;
|
||||
// It is not clear from the specification whether the byte cycle timer
|
||||
// is already started here or when the first data byte is written
|
||||
break;
|
||||
|
||||
case 0xf0:
|
||||
// Software product identification exit
|
||||
if (TRACE_STATE) logerror("%s: Exiting Identification mode\n", tag());
|
||||
m_id_mode = false;
|
||||
break;
|
||||
}
|
||||
m_cmd = CMD_0;
|
||||
if (data != 0x80) m_long_sequence = false;
|
||||
|
||||
// Return, because we don't want to write the EEPROM with the command byte
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cmd = CMD_0;
|
||||
m_long_sequence = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_pgm == PGM_2) && (get_sector_number(offset) != get_sector_number(m_programming_last_offset)))
|
||||
{
|
||||
// cancel current programming cycle
|
||||
if (TRACE_WRITE) logerror("%s: Invalid sector change (from sector 0x%04x to 0x%04x); cancel programming cycle\n", tag(), get_sector_number(m_programming_last_offset), get_sector_number(offset));
|
||||
m_pgm = PGM_0;
|
||||
m_enabling_sdb = false;
|
||||
m_disabling_sdb = false;
|
||||
m_programming_timer->adjust(attotime::never);
|
||||
}
|
||||
|
||||
if (((m_pgm == PGM_0) && !m_sdp) // write directly
|
||||
|| (m_pgm == PGM_1)) // write after unlocking
|
||||
{
|
||||
if (((offset < m_boot_block_size) && m_lower_bbl)
|
||||
|| ((offset >= m_memory_size-m_boot_block_size) && m_higher_bbl))
|
||||
{
|
||||
// attempt to access a locked out boot block: cancel programming
|
||||
// command if necessary
|
||||
if (TRACE_WRITE) logerror("%s: Attempt to access a locked out boot block: offset = %05x, lowblock=%d, highblock=%d\n", tag(), offset, m_lower_bbl, m_higher_bbl);
|
||||
|
||||
m_pgm = PGM_0;
|
||||
m_enabling_sdb = false;
|
||||
m_disabling_sdb = false;
|
||||
}
|
||||
else
|
||||
{ // enter programming mode
|
||||
if (TRACE_STATE) logerror("%s: Enter programming mode (m_pgm=%d, m_sdp=%d)\n", tag(), m_pgm, m_sdp);
|
||||
// Clear the programming buffer
|
||||
memset(m_programming_buffer, 0xff, m_sector_size);
|
||||
m_pgm = PGM_2;
|
||||
}
|
||||
}
|
||||
// TODO: If data protection is active and bytes are written, the device
|
||||
// enters a dummy write mode
|
||||
|
||||
if (m_pgm == PGM_2)
|
||||
{
|
||||
// write data to programming buffer
|
||||
if (TRACE_PRG) logerror("%s: Write data to programming buffer: buf[%x] = %02x\n", tag(), offset & m_sector_mask, data);
|
||||
m_programming_buffer[offset & m_sector_mask] = data;
|
||||
m_programming_last_offset = offset;
|
||||
m_programming_timer->adjust(attotime::from_usec(150)); // next byte must be written before the timer expires
|
||||
}
|
||||
}
|
||||
|
||||
void at29x_device::device_start(void)
|
||||
{
|
||||
m_programming_buffer = global_alloc_array(UINT8, m_sector_size);
|
||||
m_eememory = global_alloc_array(UINT8, m_memory_size+2);
|
||||
m_programming_timer = timer_alloc(PRGTIMER);
|
||||
|
||||
// TODO: Complete 16-bit handling
|
||||
m_address_mask = m_memory_size/(m_word_width/8) - 1;
|
||||
m_sector_mask = m_sector_size - 1;
|
||||
}
|
||||
|
||||
void at29x_device::device_stop(void)
|
||||
{
|
||||
global_free_array(m_programming_buffer);
|
||||
global_free_array(m_eememory);
|
||||
}
|
||||
|
||||
void at29x_device::device_reset(void)
|
||||
{
|
||||
if (m_eememory[0] != m_version)
|
||||
{
|
||||
logerror("%s: Warning: Version mismatch; expected %d but found %d in file. Resetting.\n", tag(), m_version, m_eememory[0]);
|
||||
m_eememory[0] = 0;
|
||||
m_eememory[1] = 0;
|
||||
}
|
||||
|
||||
m_lower_bbl = ((m_eememory[1] & 0x04)!=0);
|
||||
m_higher_bbl = ((m_eememory[1] & 0x02)!=0);
|
||||
m_sdp = ((m_eememory[1] & 0x01)!=0);
|
||||
|
||||
if (TRACE_CONFIG) logerror("%s: LowerBBL = %d, HigherBBL = %d, SoftDataProt = %d\n", tag(), m_lower_bbl, m_higher_bbl, m_sdp);
|
||||
|
||||
m_id_mode = false;
|
||||
m_cmd = CMD_0;
|
||||
m_enabling_bbl = false;
|
||||
m_long_sequence = false;
|
||||
m_pgm = PGM_0;
|
||||
m_enabling_sdb = false;
|
||||
m_disabling_sdb = false;
|
||||
m_toggle_bit = false;
|
||||
m_programming_last_offset = 0;
|
||||
}
|
||||
|
||||
const device_type AT29C020 = &device_creator<at29c020_device>;
|
||||
const device_type AT29C040 = &device_creator<at29c040_device>;
|
||||
const device_type AT29C040A = &device_creator<at29c040a_device>;
|
116
src/emu/machine/at29x.h
Normal file
116
src/emu/machine/at29x.h
Normal file
@ -0,0 +1,116 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Raphael Nabet, Michael Zapf
|
||||
/*
|
||||
ATMEL AT29 family
|
||||
|
||||
Michael Zapf
|
||||
August 2015
|
||||
*/
|
||||
|
||||
#ifndef __AT29X__
|
||||
#define __AT29X__
|
||||
|
||||
#include "emu.h"
|
||||
|
||||
extern const device_type AT29C020;
|
||||
extern const device_type AT29C040;
|
||||
extern const device_type AT29C040A;
|
||||
|
||||
enum s_cmd_t
|
||||
{
|
||||
CMD_0 = 0,
|
||||
CMD_1,
|
||||
CMD_2
|
||||
};
|
||||
|
||||
enum s_pgm_t
|
||||
{
|
||||
PGM_0 = 0,
|
||||
PGM_1,
|
||||
PGM_2,
|
||||
PGM_3
|
||||
};
|
||||
|
||||
class at29x_device : public device_t, public device_nvram_interface
|
||||
{
|
||||
public:
|
||||
at29x_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
|
||||
DECLARE_READ8_MEMBER( read );
|
||||
DECLARE_WRITE8_MEMBER( write );
|
||||
|
||||
protected:
|
||||
virtual void device_start(void);
|
||||
virtual void device_reset(void);
|
||||
virtual void device_stop(void);
|
||||
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
|
||||
|
||||
void nvram_default();
|
||||
void nvram_read(emu_file &file);
|
||||
void nvram_write(emu_file &file);
|
||||
|
||||
int get_sector_number(offs_t address) { return address / m_sector_size; }
|
||||
|
||||
int m_memory_size; // bytes
|
||||
int m_word_width;
|
||||
int m_device_id;
|
||||
int m_sector_size;
|
||||
int m_cycle_time; // ms
|
||||
int m_boot_block_size;
|
||||
int m_version;
|
||||
int m_address_mask;
|
||||
int m_sector_mask;
|
||||
|
||||
private:
|
||||
void sync_flags(void);
|
||||
|
||||
UINT8* m_eememory;
|
||||
|
||||
bool m_lower_bbl; // set when lower boot block lockout is enabled
|
||||
bool m_higher_bbl; // set when upper boot block lockout is enabled
|
||||
bool m_sdp; // set when in software data protect mode
|
||||
|
||||
bool m_id_mode; // set when in chip id mode
|
||||
s_cmd_t m_cmd; // command state
|
||||
bool m_enabling_bbl; // set when a boot block lockout command is expecting its parameter
|
||||
bool m_long_sequence; // set if 0x80 command has just been executed (some command require this prefix)
|
||||
s_pgm_t m_pgm; // programming state
|
||||
bool m_enabling_sdb; // set when a sdp enable command is in progress
|
||||
bool m_disabling_sdb; // set when a sdp disable command is in progress
|
||||
bool m_toggle_bit; // indicates flashing in progress (toggles for each query)
|
||||
|
||||
UINT8* m_programming_buffer;
|
||||
int m_programming_last_offset;
|
||||
emu_timer* m_programming_timer;
|
||||
};
|
||||
|
||||
/*
|
||||
Variants
|
||||
*/
|
||||
class at29c020_device : public at29x_device
|
||||
{
|
||||
public:
|
||||
at29c020_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
};
|
||||
|
||||
class at29c040_device : public at29x_device
|
||||
{
|
||||
public:
|
||||
at29c040_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
};
|
||||
|
||||
class at29c040a_device : public at29x_device
|
||||
{
|
||||
public:
|
||||
at29c040a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
};
|
||||
|
||||
#define MCFG_AT29C020_ADD(_tag ) \
|
||||
MCFG_DEVICE_ADD(_tag, AT29C020, 0)
|
||||
|
||||
#define MCFG_AT29C040_ADD(_tag ) \
|
||||
MCFG_DEVICE_ADD(_tag, AT29C040, 0)
|
||||
|
||||
#define MCFG_AT29C040A_ADD(_tag ) \
|
||||
MCFG_DEVICE_ADD(_tag, AT29C040A, 0)
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user