diff options
Diffstat (limited to 'src/devices/machine/serflash.cpp')
-rw-r--r-- | src/devices/machine/serflash.cpp | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/src/devices/machine/serflash.cpp b/src/devices/machine/serflash.cpp new file mode 100644 index 00000000000..60ff6712a2b --- /dev/null +++ b/src/devices/machine/serflash.cpp @@ -0,0 +1,411 @@ +// license:BSD-3-Clause +// copyright-holders:David Haywood, Luca Elia +/* Serial Flash Device */ + +/* todo: cleanup, refactor etc. */ +/* ghosteo.c is similar? */ + +#include "emu.h" +#include "machine/serflash.h" + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +// device type definition +const device_type SERFLASH = &device_creator<serflash_device>; + +//------------------------------------------------- +// serflash_device - constructor +//------------------------------------------------- + +serflash_device::serflash_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, SERFLASH, "Serial Flash", tag, owner, clock, "serflash", __FILE__), + device_nvram_interface(mconfig, *this), + m_length(0) +{ +} + + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void serflash_device::device_start() +{ + m_length = machine().root_device().memregion( tag() )->bytes(); + m_region = machine().root_device().memregion( tag() )->base(); + + m_flashwritemap.resize(m_length / FLASH_PAGE_SIZE); + memset(&m_flashwritemap[0], 0, m_length / FLASH_PAGE_SIZE); +} + +void serflash_device::device_reset() +{ + m_flash_enab = 0; + flash_hard_reset(machine()); + + m_last_flash_cmd = 0x00; + m_flash_addr_seq = 0; + m_flash_addr = 0; + + m_flash_page_addr = 0; +} + +//------------------------------------------------- +// serflash_default - called to initialize SERFLASH to +// its default state +//------------------------------------------------- + +void serflash_device::nvram_default() +{ +} + + +//------------------------------------------------- +// nvram_read - called to read SERFLASH from the +// .nv file +//------------------------------------------------- + +void serflash_device::nvram_read(emu_file &file) +{ + if (m_length % FLASH_PAGE_SIZE) return; // region size must be multiple of flash page size + int size = m_length / FLASH_PAGE_SIZE; + + + if (file) + { + UINT32 page; + file.read(&page, 4); + while (page < size) + { + m_flashwritemap[page] = 1; + file.read(m_region + page * FLASH_PAGE_SIZE, FLASH_PAGE_SIZE); + file.read(&page, 4); + } + } + +} + + +//------------------------------------------------- +// nvram_write - called to write SERFLASH to the +// .nv file +//------------------------------------------------- + +void serflash_device::nvram_write(emu_file &file) +{ + if (m_length % FLASH_PAGE_SIZE) return; // region size must be multiple of flash page size + int size = m_length / FLASH_PAGE_SIZE; + + UINT32 page = 0; + while (page < size) + { + if (m_flashwritemap[page]) + { + file.write(&page, 4); + file.write(m_region + page * FLASH_PAGE_SIZE, FLASH_PAGE_SIZE); + } + page++; + } + file.write(&page, 4); +} + +void serflash_device::flash_hard_reset(running_machine &machine) +{ +// logerror("%08x FLASH: RESET\n", cpuexec_describe_context(machine)); + + m_flash_state = STATE_READ; + + m_flash_cmd_prev = -1; + m_flash_cmd_seq = 0; + + m_flash_addr_seq = 0; + m_flash_read_seq = 0; + + m_flash_row = 0; + m_flash_col = 0; + + memset(m_flash_page_data, 0, FLASH_PAGE_SIZE); + m_flash_page_addr = 0; + m_flash_page_index = 0; +} + +WRITE8_MEMBER( serflash_device::flash_enab_w ) +{ + //logerror("%08x FLASH: enab = %02X\n", m_maincpu->pc(), data); + m_flash_enab = data; +} + +void serflash_device::flash_change_state(running_machine &machine, flash_state_t state) +{ + m_flash_state = state; + + m_flash_cmd_prev = -1; + m_flash_cmd_seq = 0; + + m_flash_read_seq = 0; + m_flash_addr_seq = 0; + + //logerror("flash_change_state - FLASH: state = %s\n", m_flash_state_name[state]); +} + +WRITE8_MEMBER( serflash_device::flash_cmd_w ) +{ + if (!m_flash_enab) + return; + + //logerror("%08x FLASH: cmd = %02X (prev = %02X)\n", m_maincpu->pc(), data, m_flash_cmd_prev); + + if (m_flash_cmd_prev == -1) + { + m_flash_cmd_prev = data; + + switch (data) + { + case 0x00: // READ + m_flash_addr_seq = 0; + break; + + case 0x60: // BLOCK ERASE + m_flash_addr_seq = 0; + break; + + case 0x70: // READ STATUS + flash_change_state( space.machine(), STATE_READ_STATUS ); + break; + + case 0x80: // PAGE / CACHE PROGRAM + m_flash_addr_seq = 0; + // this actually seems to be set with the next 2 writes? + m_flash_page_addr = 0; + break; + + case 0x90: // READ ID + flash_change_state( space.machine(), STATE_READ_ID ); + break; + + case 0xff: // RESET + flash_change_state( space.machine(), STATE_IDLE ); + break; + + default: + { + //logerror("%08x FLASH: unknown cmd1 = %02X\n", cpu_get_pc(space.device()), data); + } + } + } + else + { + switch (m_flash_cmd_prev) + { + case 0x00: // READ + if (data == 0x30) + { + memcpy(m_flash_page_data, m_region + m_flash_row * FLASH_PAGE_SIZE, FLASH_PAGE_SIZE); + m_flash_page_addr = m_flash_col; + m_flash_page_index = m_flash_row; + + flash_change_state( space.machine(), STATE_READ ); + + //logerror("%08x FLASH: caching page = %04X\n", m_maincpu->pc(), m_flash_row); + } + break; + + case 0x60: // BLOCK ERASE + if (data==0xd0) + { + flash_change_state( space.machine(), STATE_BLOCK_ERASE ); + m_flashwritemap[m_flash_col] |= 1; + memset(m_region + m_flash_col * FLASH_PAGE_SIZE, 0xff, FLASH_PAGE_SIZE); + //logerror("erased block %04x (%08x - %08x)\n", m_flash_col, m_flash_col * FLASH_PAGE_SIZE, ((m_flash_col+1) * FLASH_PAGE_SIZE)-1); + } + else + { + //logerror("unexpected 2nd command after BLOCK ERASE\n"); + } + break; + case 0x80: + if (data==0x10) + { + flash_change_state( space.machine(), STATE_PAGE_PROGRAM ); + m_flashwritemap[m_flash_row] |= (memcmp(m_region + m_flash_row * FLASH_PAGE_SIZE, m_flash_page_data, FLASH_PAGE_SIZE) != 0); + memcpy(m_region + m_flash_row * FLASH_PAGE_SIZE, m_flash_page_data, FLASH_PAGE_SIZE); + //logerror("re-written block %04x (%08x - %08x)\n", m_flash_row, m_flash_row * FLASH_PAGE_SIZE, ((m_flash_row+1) * FLASH_PAGE_SIZE)-1); + + } + else + { + //logerror("unexpected 2nd command after SPAGE PROGRAM\n"); + } + break; + + + default: + { + //logerror("%08x FLASH: unknown cmd2 = %02X (cmd1 = %02X)\n", m_maincpu->pc(), data, m_flash_cmd_prev); + } + } + } +} + +WRITE8_MEMBER( serflash_device::flash_data_w ) +{ + if (!m_flash_enab) + return; + + //logerror("flash data write %04x\n", m_flash_page_addr); + m_flash_page_data[m_flash_page_addr] = data; + m_flash_page_addr++; +} + +WRITE8_MEMBER( serflash_device::flash_addr_w ) +{ + if (!m_flash_enab) + return; + + //logerror("%08x FLASH: addr = %02X (seq = %02X)\n", m_maincpu->pc(), data, m_flash_addr_seq); + + switch( m_flash_addr_seq++ ) + { + case 0: + m_flash_col = (m_flash_col & 0xff00) | data; + break; + case 1: + m_flash_col = (m_flash_col & 0x00ff) | (data << 8); + break; + case 2: + m_flash_row = (m_flash_row & 0xff00) | data; + break; + case 3: + m_flash_row = (m_flash_row & 0x00ff) | (data << 8); + m_flash_addr_seq = 0; + break; + } +} + +READ8_MEMBER( serflash_device::flash_io_r ) +{ + UINT8 data = 0x00; +// UINT32 old; + + if (!m_flash_enab) + return 0xff; + + switch (m_flash_state) + { + case STATE_READ_ID: + //old = m_flash_read_seq; + + switch( m_flash_read_seq++ ) + { + case 0: + data = 0xEC; // Manufacturer + break; + case 1: + data = 0xF1; // Device + break; + case 2: + data = 0x00; // XX + break; + case 3: + data = 0x15; // Flags + m_flash_read_seq = 0; + break; + } + + //logerror("%08x FLASH: read %02X from id(%02X)\n", m_maincpu->pc(), data, old); + break; + + case STATE_READ: + if (m_flash_page_addr > FLASH_PAGE_SIZE-1) + m_flash_page_addr = FLASH_PAGE_SIZE-1; + + //old = m_flash_page_addr; + + data = m_flash_page_data[m_flash_page_addr++]; + + //logerror("%08x FLASH: read data %02X from addr %03X (page %04X)\n", m_maincpu->pc(), data, old, m_flash_page_index); + break; + + case STATE_READ_STATUS: + // bit 7 = writeable, bit 6 = ready, bit 5 = ready/true ready, bit 1 = fail(N-1), bit 0 = fail + data = 0xe0; + //logerror("%08x FLASH: read status %02X\n", m_maincpu->pc(), data); + break; + + default: + { + // logerror("%08x FLASH: unknown read in state %s\n",0x00/*m_maincpu->pc()*/, m_flash_state_name[m_flash_state]); + } + } + + return data; +} + +READ8_MEMBER( serflash_device::flash_ready_r ) +{ + return 1; +} + + + +READ8_MEMBER(serflash_device::n3d_flash_r) +{ + if (m_last_flash_cmd==0x70) return 0xe0; + + if (m_last_flash_cmd==0x00) + { + UINT8 retdat = m_flash_page_data[m_flash_page_addr]; + + //logerror("n3d_flash_r %02x %04x\n", offset, m_flash_page_addr); + + m_flash_page_addr++; + return retdat; + } + + + logerror("n3d_flash_r %02x\n", offset); + return 0x00; + +} + + +WRITE8_MEMBER(serflash_device::n3d_flash_cmd_w) +{ + logerror("n3d_flash_cmd_w %02x %02x\n", offset, data); + m_last_flash_cmd = data; + + if (data==0x00) + { + memcpy(m_flash_page_data, m_region + m_flash_addr * FLASH_PAGE_SIZE, FLASH_PAGE_SIZE); + + } + +} + +WRITE8_MEMBER(serflash_device::n3d_flash_addr_w) +{ +// logerror("n3d_flash_addr_w %02x %02x\n", offset, data); + + m_flash_addr_seq++; + + if (m_flash_addr_seq==3) + m_flash_addr = (m_flash_addr & 0xffff00) | data; + + if (m_flash_addr_seq==4) + m_flash_addr = (m_flash_addr & 0xff00ff) | data << 8; + + if (m_flash_addr_seq==5) + m_flash_addr = (m_flash_addr & 0x00ffff) | data << 16; + + if (m_flash_addr_seq==5) + { + m_flash_addr_seq = 0; + m_flash_page_addr = 0; + logerror("set flash block to %08x\n", m_flash_addr); + } +} |