diff options
Diffstat (limited to 'src/devices/machine/x76f100.cpp')
-rw-r--r-- | src/devices/machine/x76f100.cpp | 201 |
1 files changed, 159 insertions, 42 deletions
diff --git a/src/devices/machine/x76f100.cpp b/src/devices/machine/x76f100.cpp index 4c74bb5b7ad..b64bbdf32c2 100644 --- a/src/devices/machine/x76f100.cpp +++ b/src/devices/machine/x76f100.cpp @@ -12,7 +12,11 @@ */ #include "emu.h" -#include "machine/x76f100.h" +#include "x76f100.h" + +#include <cstdarg> +#include <tuple> + #define VERBOSE_LEVEL ( 0 ) @@ -21,7 +25,7 @@ inline void ATTR_PRINTF( 3, 4 ) x76f100_device::verboselog( int n_level, const c if( VERBOSE_LEVEL >= n_level ) { va_list v; - char buf[ 32768 ]; + char buf[32768]; va_start( v, s_fmt ); vsprintf( buf, s_fmt, v ); va_end( v ); @@ -29,11 +33,13 @@ inline void ATTR_PRINTF( 3, 4 ) x76f100_device::verboselog( int n_level, const c } } + // device type definition DEFINE_DEVICE_TYPE(X76F100, x76f100_device, "x76f100", "X76F100 Secure SerialFlash") -x76f100_device::x76f100_device( const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock ) - : device_t( mconfig, X76F100, tag, owner, clock ), + +x76f100_device::x76f100_device( const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock ) : + device_t( mconfig, X76F100, tag, owner, clock ), device_nvram_interface(mconfig, *this), m_region(*this, DEVICE_SELF), m_cs( 0 ), @@ -45,13 +51,15 @@ x76f100_device::x76f100_device( const machine_config &mconfig, const char *tag, m_shift( 0 ), m_bit( 0 ), m_byte( 0 ), - m_command( 0 ) + m_command( 0 ), + m_password_retry_counter( 0 ), + m_is_password_accepted( false ) { } void x76f100_device::device_start() { - memset( m_write_buffer, 0, sizeof( m_write_buffer ) ); + std::fill( std::begin( m_write_buffer ), std::end( m_write_buffer ), 0 ); save_item( NAME( m_cs ) ); save_item( NAME( m_rst ) ); @@ -63,6 +71,8 @@ void x76f100_device::device_start() save_item( NAME( m_bit ) ); save_item( NAME( m_byte ) ); save_item( NAME( m_command ) ); + save_item( NAME( m_password_retry_counter ) ); + save_item( NAME( m_is_password_accepted ) ); save_item( NAME( m_write_buffer ) ); save_item( NAME( m_response_to_reset ) ); save_item( NAME( m_write_password ) ); @@ -70,7 +80,25 @@ void x76f100_device::device_start() save_item( NAME( m_data ) ); } -WRITE_LINE_MEMBER( x76f100_device::write_cs ) +void x76f100_device::device_reset() +{ + std::fill( std::begin( m_write_buffer ), std::end( m_write_buffer ), 0 ); + + m_cs = 0; + m_rst = 0; + m_scl = 0; + m_sdaw = 0; + m_sdar = 0; + m_state = STATE_STOP; + m_shift = 0; + m_bit = 0; + m_byte = 0; + m_command = 0; + m_password_retry_counter = 0; + m_is_password_accepted = false; +} + +void x76f100_device::write_cs(int state) { if( m_cs != state ) { @@ -94,7 +122,7 @@ WRITE_LINE_MEMBER( x76f100_device::write_cs ) m_cs = state; } -WRITE_LINE_MEMBER( x76f100_device::write_rst ) +void x76f100_device::write_rst(int state) { if( m_rst != state ) { @@ -124,11 +152,13 @@ uint8_t *x76f100_device::password() void x76f100_device::password_ok() { - if( ( m_command & 0xe1 ) == COMMAND_READ ) + m_password_retry_counter = 0; + + if( ( m_command & 0x81 ) == COMMAND_READ ) { m_state = STATE_READ_DATA; } - else if( ( m_command & 0xe1 ) == COMMAND_WRITE ) + else if( ( m_command & 0x81 ) == COMMAND_WRITE ) { m_state = STATE_WRITE_DATA; } @@ -141,11 +171,18 @@ void x76f100_device::password_ok() int x76f100_device::data_offset() { int block_offset = ( m_command >> 1 ) & 0x0f; + int offset = ( block_offset * sizeof( m_write_buffer ) ) + m_byte; + + // Technically there are 4 bits assigned to sector values but since the data array is only 112 bytes, + // it will try reading out of bounds when the sector is 14 (= starts at 112) or 15 (= starts at 120). + // TODO: Verify what happens on real hardware when reading/writing sectors 14 and 15 + if( offset >= sizeof ( m_data ) ) + return -1; - return ( block_offset * sizeof( m_write_buffer ) ) + m_byte; + return offset; } -WRITE_LINE_MEMBER( x76f100_device::write_scl ) +void x76f100_device::write_scl(int state) { if( m_scl != state ) { @@ -164,7 +201,7 @@ WRITE_LINE_MEMBER( x76f100_device::write_scl ) { if( m_bit == 0 ) { - m_shift = m_response_to_reset[ m_byte ]; + m_shift = m_response_to_reset[m_byte]; verboselog( 1, "<- response_to_reset[%d]: %02x\n", m_byte, m_shift ); } @@ -189,6 +226,9 @@ WRITE_LINE_MEMBER( x76f100_device::write_scl ) case STATE_LOAD_PASSWORD: case STATE_VERIFY_PASSWORD: case STATE_WRITE_DATA: + // FIXME: Processing on the rising edge of the clock causes sda to change state while clock is high + // which is not allowed. Also need to ensure that only valid device-id's and commands + // are acknowledged. if( m_scl == 0 && state != 0 ) { if( m_bit < 8 ) @@ -212,28 +252,47 @@ WRITE_LINE_MEMBER( x76f100_device::write_scl ) case STATE_LOAD_COMMAND: m_command = m_shift; verboselog( 1, "-> command: %02x\n", m_command ); - /* todo: verify command is valid? */ + /* TODO: verify command is valid? */ m_state = STATE_LOAD_PASSWORD; break; case STATE_LOAD_PASSWORD: verboselog( 1, "-> password: %02x\n", m_shift ); - m_write_buffer[ m_byte++ ] = m_shift; + m_write_buffer[m_byte++] = m_shift; if( m_byte == sizeof( m_write_buffer ) ) { m_state = STATE_VERIFY_PASSWORD; + + // Perform the password acceptance check before verify password because + // password verify ack is spammed and will quickly overflow the + // retry counter. This becomes an issue with System 573 games that use the + // X76F100 as an install cartridge. The boot process first tries to use the + // game cartridge password and if not accepted will try the install cartridge + // password and then enter installation mode if accepted. + m_is_password_accepted = memcmp( password(), m_write_buffer, sizeof( m_write_buffer ) ) == 0; + if( !m_is_password_accepted ) + { + m_password_retry_counter++; + if( m_password_retry_counter >= 8 ) + { + std::fill( std::begin( m_read_password ), std::end( m_read_password ), 0 ); + std::fill( std::begin( m_write_password ), std::end( m_write_password ), 0 ); + std::fill( std::begin( m_data ), std::end( m_data ), 0 ); + m_password_retry_counter = 0; + } + } } break; case STATE_VERIFY_PASSWORD: verboselog( 1, "-> verify password: %02x\n", m_shift ); - /* todo: this should probably be handled as a command */ + /* TODO: this should probably be handled as a command */ if( m_shift == COMMAND_ACK_PASSWORD ) { - /* todo: this should take 10ms before it returns ok. */ - if( memcmp( password(), m_write_buffer, sizeof( m_write_buffer ) ) == 0 ) + /* TODO: this should take 10ms before it returns ok. */ + if( m_is_password_accepted ) { password_ok(); } @@ -246,15 +305,35 @@ WRITE_LINE_MEMBER( x76f100_device::write_scl ) case STATE_WRITE_DATA: verboselog( 2, "-> data: %02x\n", m_shift ); - m_write_buffer[ m_byte++ ] = m_shift; + m_write_buffer[m_byte++] = m_shift; if( m_byte == sizeof( m_write_buffer ) ) { - for( m_byte = 0; m_byte < sizeof( m_write_buffer ); m_byte++ ) + if( m_command == COMMAND_CHANGE_WRITE_PASSWORD ) { - int offset = data_offset(); - verboselog( 1, "-> data[ %03x ]: %02x\n", offset, m_write_buffer[ m_byte ] ); - m_data[ offset ] = m_write_buffer[ m_byte ]; + std::copy( std::begin( m_write_buffer ), std::end( m_write_buffer ), std::begin( m_write_password ) ); + } + else if( m_command == COMMAND_CHANGE_READ_PASSWORD ) + { + std::copy( std::begin( m_write_buffer ), std::end( m_write_buffer ), std::begin( m_read_password ) ); + } + else + { + for( m_byte = 0; m_byte < sizeof( m_write_buffer ); m_byte++ ) + { + int offset = data_offset(); + + if( offset != -1 ) + { + verboselog( 1, "-> data[%03x]: %02x\n", offset, m_write_buffer[m_byte] ); + m_data[offset] = m_write_buffer[m_byte]; + } + else + { + verboselog( 1, "-> attempted to write %02x out of bounds\n", m_write_buffer[m_byte] ); + break; + } + } } m_byte = 0; @@ -271,6 +350,8 @@ WRITE_LINE_MEMBER( x76f100_device::write_scl ) break; case STATE_READ_DATA: + // FIXME: Processing on the rising edge of the clock causes sda to change state while clock is high + // which is not allowed. if( m_scl == 0 && state != 0 ) { if( m_bit < 8 ) @@ -283,8 +364,18 @@ WRITE_LINE_MEMBER( x76f100_device::write_scl ) { case STATE_READ_DATA: offset = data_offset(); - m_shift = m_data[ offset ]; - verboselog( 1, "<- data[ %02x ]: %02x\n", offset, m_shift ); + + if( offset != -1 ) + { + m_shift = m_data[offset]; + verboselog( 1, "<- data[%02x]: %02x\n", offset, m_shift ); + } + else + { + m_shift = 0; + verboselog( 1, "<- attempted to read out of bounds\n" ); + } + break; } } @@ -316,7 +407,7 @@ WRITE_LINE_MEMBER( x76f100_device::write_scl ) m_scl = state; } -WRITE_LINE_MEMBER( x76f100_device::write_sda ) +void x76f100_device::write_sda(int state) { if( m_sdaw != state ) { @@ -342,7 +433,7 @@ WRITE_LINE_MEMBER( x76f100_device::write_sda ) break; case STATE_LOAD_PASSWORD: - /* todo: this will be the 0xc0 command, but it's not handled as a command yet. */ + /* TODO: this will be the 0xc0 command, but it's not handled as a command yet. */ verboselog( 1, "goto start\n" ); break; @@ -367,7 +458,7 @@ WRITE_LINE_MEMBER( x76f100_device::write_sda ) m_sdaw = state; } -READ_LINE_MEMBER( x76f100_device::read_sda ) +int x76f100_device::read_sda() { if( m_cs != 0 ) { @@ -381,10 +472,10 @@ READ_LINE_MEMBER( x76f100_device::read_sda ) void x76f100_device::nvram_default() { - m_response_to_reset[ 0 ] = 0x19; - m_response_to_reset[ 1 ] = 0x00; - m_response_to_reset[ 2 ] = 0xaa; - m_response_to_reset[ 3 ] = 0x55, + m_response_to_reset[0] = 0x19; + m_response_to_reset[1] = 0x00; + m_response_to_reset[2] = 0xaa; + m_response_to_reset[3] = 0x55, memset( m_write_password, 0, sizeof( m_write_password ) ); memset( m_read_password, 0, sizeof( m_read_password ) ); @@ -411,18 +502,44 @@ void x76f100_device::nvram_default() } } -void x76f100_device::nvram_read( emu_file &file ) +bool x76f100_device::nvram_read( util::read_stream &file ) { - file.read( m_response_to_reset, sizeof( m_response_to_reset ) ); - file.read( m_write_password, sizeof( m_write_password ) ); - file.read( m_read_password, sizeof( m_read_password ) ); - file.read( m_data, sizeof( m_data ) ); + std::error_condition err; + size_t actual; + + std::tie( err, actual ) = read( file, m_response_to_reset, sizeof( m_response_to_reset ) ); + if( err || ( sizeof( m_response_to_reset ) != actual ) ) + return false; + std::tie( err, actual ) = read( file, m_write_password, sizeof( m_write_password ) ); + if( err || ( sizeof( m_write_password ) != actual ) ) + return false; + std::tie( err, actual ) = read( file, m_read_password, sizeof( m_read_password ) ); + if( err || ( sizeof( m_read_password ) != actual ) ) + return false; + std::tie( err, actual ) = read( file, m_data, sizeof( m_data ) ); + if( err || ( sizeof( m_data ) != actual ) ) + return false; + + return true; } -void x76f100_device::nvram_write( emu_file &file ) +bool x76f100_device::nvram_write( util::write_stream &file ) { - file.write( m_response_to_reset, sizeof( m_response_to_reset ) ); - file.write( m_write_password, sizeof( m_write_password ) ); - file.write( m_read_password, sizeof( m_read_password ) ); - file.write( m_data, sizeof( m_data ) ); + std::error_condition err; + size_t actual; + + std::tie( err, actual ) = write( file, m_response_to_reset, sizeof( m_response_to_reset ) ); + if ( err ) + return false; + std::tie( err, actual ) = write( file, m_write_password, sizeof( m_write_password ) ); + if ( err ) + return false; + std::tie( err, actual ) = write( file, m_read_password, sizeof( m_read_password ) ); + if ( err ) + return false; + std::tie( err, actual ) = write( file, m_data, sizeof( m_data ) ); + if ( err ) + return false; + + return true; } |