summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/serflash.cpp
blob: 32f2524d5eb202b8b85bf2cbcdfb19d01e8b8b6a (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                       
                                             







                                  
                    
 
                                                





                                                                            
                                                                         




                                                   
                                                                                                                 



                                                        

                                    



                                                                                                                           










                                                   

                                                                     
 





                                                                     
 













                                            




                                    
                           
 













                                                       









                                                   

                                                                                                    

 
                           
         
                              



                                                  
                                                                                          













                                                   

                                                                                                    
 
                          




                                             
                                                                                           





                             
                                        


                                                                       
                                            









                              
                                                                         









                                                                       
                                                             
























                                                                                                      
                                           



                                                     
                                                                         

                                      
                                                  
                                                                                 

                                      
                                                           




                                                                                        
                                              
                                                                             

                                      
                                            
                                                                          



                                      
                                                                                                                  






                                         
                                           

                                                 





                                                                                                                                                      
                                                                                  







                                                                                                                      
                                                                                         





                                                                                                                                                                                








                                                                                                 
                                                                                          





                                                                                                                                                                                    





















                                                                                                                                       



                                                            


















                                                                                                      




                                                                      

                              







                                                                              






                                             

                            





                              
                                            




                                                    
                                                                       

                                              
                                                                 

                                              
                                                             

                                              
                                                                






                                                                                                        
                                         

                                                                        







                                                                                                                                              
                                                






                                                                                                                      
                                                                                                                                  


















                                                
                                                                      




















                                                                                 



                                                                                                                               
         








                                                           
         
                                                                






                                                                            
                                
         
                                                                     






                                                                            

                                
                                                                      




                                                                    
// 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"

#include <algorithm>

ALLOW_SAVE_TYPE(serflash_device::flash_state_t);

//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

// device type definition
DEFINE_DEVICE_TYPE(SERFLASH, serflash_device, "serflash", "Serial Flash")

//-------------------------------------------------
//  serflash_device - constructor
//-------------------------------------------------

serflash_device::serflash_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, SERFLASH, tag, owner, clock)
	, device_nvram_interface(mconfig, *this)
	, m_length(0)
	, m_region(nullptr)
	, m_row_num(0)
	, m_flash_page_size(2048+64)
	, m_flash_state()
	, m_flash_enab(0)
	, m_flash_cmd_seq(0), m_flash_cmd_prev(0), m_flash_addr_seq(0), m_flash_read_seq(0)
	, m_flash_row(0), m_flash_col(0), m_flash_page_addr(0), m_flash_page_index(0), m_last_flash_cmd(0), m_flash_addr(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_row_num = m_length / m_flash_page_size;

	m_flashwritemap.resize(m_row_num);
	std::fill(m_flashwritemap.begin(), m_flashwritemap.end(), 0);

	m_flash_page_data.resize(m_flash_page_size);

	save_item(NAME(m_flash_state));
	save_item(NAME(m_flash_enab));
	save_item(NAME(m_flash_cmd_seq));
	save_item(NAME(m_flash_cmd_prev));
	save_item(NAME(m_flash_addr_seq));
	save_item(NAME(m_flash_read_seq));
	save_item(NAME(m_flash_row));
	save_item(NAME(m_flash_col));
	save_item(NAME(m_flash_page_addr));
	save_item(NAME(m_flash_page_index));
	save_item(NAME(m_flashwritemap));
	save_item(NAME(m_last_flash_cmd));
	save_item(NAME(m_flash_addr));
	save_item(NAME(m_flash_page_data));
}

void serflash_device::device_reset()
{
	m_flash_enab = 0;
	flash_hard_reset();

	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 % m_flash_page_size) return; // region size must be multiple of flash page size
	int size = m_length / m_flash_page_size;


	if (file.is_open())
	{
		uint32_t page;
		file.read(&page, 4);
		while (page < size)
		{
			m_flashwritemap[page] = 1;
			file.read(m_region + page * m_flash_page_size, m_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 % m_flash_page_size) return; // region size must be multiple of flash page size
	int size = m_length / m_flash_page_size;

	uint32_t page = 0;
	while (page < size)
	{
		if (m_flashwritemap[page])
		{
			file.write(&page, 4);
			file.write(m_region + page * m_flash_page_size, m_flash_page_size);
		}
		page++;
	}
	file.write(&page, 4);
}

void serflash_device::flash_hard_reset()
{
//  logerror("%08x FLASH: RESET\n", cpuexec_describe_context(machine));

	m_flash_state = flash_state_t::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;

	std::fill(m_flash_page_data.begin(), m_flash_page_data.end(), 0);
	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(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 = 2; // row address only
				break;

			case 0x70:  // READ STATUS
				flash_change_state( flash_state_t::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( flash_state_t::READ_ID );
				break;

			case 0xff:  // RESET
				flash_change_state( flash_state_t::IDLE );
				break;

			default:
			{
				//logerror("%s FLASH: unknown cmd1 = %02X\n", machine().describe_context(), data);
			}
		}
	}
	else
	{
		switch (m_flash_cmd_prev)
		{
			case 0x00:  // READ
				if (data == 0x30)
				{
					if (m_flash_row < m_row_num)
					{
						std::copy_n(&m_region[m_flash_row * m_flash_page_size], m_flash_page_size, m_flash_page_data.begin());
						m_flash_page_addr = m_flash_col;
						m_flash_page_index = m_flash_row;
					}
					flash_change_state( flash_state_t::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( flash_state_t::BLOCK_ERASE );
					if (m_flash_row < m_row_num)
					{
						m_flashwritemap[m_flash_row] |= 1;
						std::fill_n(&m_region[m_flash_row * m_flash_page_size], m_flash_page_size, 0xff);
					}
					//logerror("erased block %04x (%08x - %08x)\n", m_flash_col, m_flash_col * m_flash_page_size,  ((m_flash_col+1) * m_flash_page_size)-1);
				}
				else
				{
					//logerror("unexpected 2nd command after BLOCK ERASE\n");
				}
				break;
			case 0x80:
				if (data==0x10)
				{
					flash_change_state( flash_state_t::PAGE_PROGRAM );
					if (m_flash_row < m_row_num)
					{
						m_flashwritemap[m_flash_row] |= (memcmp(m_region + m_flash_row * m_flash_page_size, &m_flash_page_data[0], m_flash_page_size) != 0);
						std::copy_n(m_flash_page_data.begin(), m_flash_page_size, &m_region[m_flash_row * m_flash_page_size]);
					}
					//logerror("re-written block %04x (%08x - %08x)\n", m_flash_row, m_flash_row * m_flash_page_size,  ((m_flash_row+1) * m_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);
	if (m_flash_page_addr < m_flash_page_size)
	{
		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 & 0xffff00) | data;
			if (m_row_num <= 256)
			{
				m_flash_addr_seq = 0;
			}
			break;
		case 3:
			m_flash_row = (m_flash_row & 0xff00ff) | (data << 8);
			if (m_row_num <= 65536)
			{
				m_flash_addr_seq = 0;
			}
			break;
		case 4:
			m_flash_row = (m_flash_row & 0x00ffff) | (data << 16);
			m_flash_addr_seq = 0;
			break;
	}
}

READ8_MEMBER( serflash_device::flash_io_r )
{
	uint8_t data = 0x00;
//  uint32_t old;

	if (!m_flash_enab)
		return 0xff;

	switch (m_flash_state)
	{
		case flash_state_t::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 flash_state_t::READ:
			if (m_flash_page_addr > m_flash_page_size-1)
				m_flash_page_addr = m_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 flash_state_t::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_t 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)
	{
		if (m_flash_addr < m_row_num)
		{
			std::copy_n(&m_region[m_flash_addr * m_flash_page_size], m_flash_page_size, m_flash_page_data.begin());
		}
	}
}

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_row_num <= 256)
		{
			m_flash_addr_seq = 0;
			m_flash_page_addr = 0;
			logerror("set flash block to %08x\n", m_flash_addr);
		}
	}
	if (m_flash_addr_seq==4)
	{
		m_flash_addr = (m_flash_addr & 0xff00ff) | data << 8;
		if (m_row_num <= 65536)
		{
			m_flash_addr_seq = 0;
			m_flash_page_addr = 0;
			logerror("set flash block to %08x\n", m_flash_addr);
		}
	}
	if (m_flash_addr_seq==5)
	{
		m_flash_addr = (m_flash_addr & 0x00ffff) | data << 16;
		m_flash_addr_seq = 0;
		m_flash_page_addr = 0;
		logerror("set flash block to %08x\n", m_flash_addr);
	}
}