mirror of
https://github.com/holub/mame
synced 2025-04-16 13:34:55 +03:00
x76f041/x76f100/zs01: Implement new operations and security features (#9137)
* x76f100: Implement security features * x76f041: Implement security features * zs01: Implement security features * ksys573: Update security flash data * k573mcal: Add master calendar for initializing security cassettes * zs01: Update comment about unknown serial
This commit is contained in:
parent
e450cc7953
commit
99404ddd2d
@ -2545,6 +2545,8 @@ files {
|
||||
MAME_DIR .. "src/mame/machine/k573fpga.h",
|
||||
MAME_DIR .. "src/mame/machine/k573kara.cpp",
|
||||
MAME_DIR .. "src/mame/machine/k573kara.h",
|
||||
MAME_DIR .. "src/mame/machine/k573mcal.cpp",
|
||||
MAME_DIR .. "src/mame/machine/k573mcal.h",
|
||||
MAME_DIR .. "src/mame/machine/k573mcr.cpp",
|
||||
MAME_DIR .. "src/mame/machine/k573mcr.h",
|
||||
MAME_DIR .. "src/mame/machine/k573msu.cpp",
|
||||
|
@ -48,13 +48,15 @@ x76f041_device::x76f041_device( const machine_config &mconfig, const char *tag,
|
||||
m_bit( 0 ),
|
||||
m_byte( 0 ),
|
||||
m_command( 0 ),
|
||||
m_address( 0 )
|
||||
m_address( 0 ),
|
||||
m_is_password_accepted ( false )
|
||||
{
|
||||
}
|
||||
|
||||
void x76f041_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 );
|
||||
std::fill( std::begin( m_password_temp ), std::end( m_password_temp ), 0 );
|
||||
|
||||
save_item( NAME( m_cs ) );
|
||||
save_item( NAME( m_rst ) );
|
||||
@ -67,6 +69,7 @@ void x76f041_device::device_start()
|
||||
save_item( NAME( m_byte ) );
|
||||
save_item( NAME( m_command ) );
|
||||
save_item( NAME( m_address ) );
|
||||
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 ) );
|
||||
@ -74,6 +77,26 @@ void x76f041_device::device_start()
|
||||
save_item( NAME( m_configuration_password ) );
|
||||
save_item( NAME( m_configuration_registers ) );
|
||||
save_item( NAME( m_data ) );
|
||||
save_item( NAME( m_password_temp ) );
|
||||
}
|
||||
|
||||
void x76f041_device::device_reset()
|
||||
{
|
||||
std::fill( std::begin( m_write_buffer ), std::end( m_write_buffer ), 0 );
|
||||
std::fill( std::begin( m_password_temp ), std::end( m_password_temp ), 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_address = 0;
|
||||
m_is_password_accepted = false;
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER( x76f041_device::write_cs )
|
||||
@ -128,6 +151,15 @@ uint8_t *x76f041_device::password()
|
||||
case COMMAND_READ:
|
||||
return m_read_password;
|
||||
|
||||
case COMMAND_CONFIGURATION:
|
||||
if( m_address == CONFIGURATION_PROGRAM_WRITE_PASSWORD )
|
||||
return m_write_password;
|
||||
|
||||
if( m_address == CONFIGURATION_PROGRAM_READ_PASSWORD )
|
||||
return m_read_password;
|
||||
|
||||
return m_configuration_password;
|
||||
|
||||
default:
|
||||
return m_configuration_password;
|
||||
}
|
||||
@ -135,6 +167,9 @@ uint8_t *x76f041_device::password()
|
||||
|
||||
void x76f041_device::password_ok()
|
||||
{
|
||||
if( m_configuration_registers[ CONFIG_CR ] & CR_RETRY_COUNTER_RESET_BIT )
|
||||
m_configuration_registers[ CONFIG_RC ] = 0;
|
||||
|
||||
switch( m_command & 0xe0 )
|
||||
{
|
||||
case COMMAND_WRITE:
|
||||
@ -144,7 +179,7 @@ void x76f041_device::password_ok()
|
||||
m_state = STATE_READ_DATA;
|
||||
break;
|
||||
case COMMAND_WRITE_USE_CONFIGURATION_PASSWORD:
|
||||
m_state = STATE_WRITE_DATA;
|
||||
m_state = STATE_CONFIGURATION_WRITE_DATA;
|
||||
break;
|
||||
case COMMAND_READ_USE_CONFIGURATION_PASSWORD:
|
||||
m_state = STATE_READ_DATA;
|
||||
@ -153,14 +188,22 @@ void x76f041_device::password_ok()
|
||||
switch( m_address )
|
||||
{
|
||||
case CONFIGURATION_PROGRAM_WRITE_PASSWORD:
|
||||
m_state = STATE_PROGRAM_WRITE_PASSWORD;
|
||||
m_byte = 0;
|
||||
break;
|
||||
case CONFIGURATION_PROGRAM_READ_PASSWORD:
|
||||
m_state = STATE_PROGRAM_READ_PASSWORD;
|
||||
m_byte = 0;
|
||||
break;
|
||||
case CONFIGURATION_PROGRAM_CONFIGURATION_PASSWORD:
|
||||
m_state = STATE_PROGRAM_CONFIGURATION_PASSWORD;
|
||||
m_byte = 0;
|
||||
break;
|
||||
case CONFIGURATION_RESET_WRITE_PASSWORD:
|
||||
m_state = STATE_RESET_WRITE_PASSWORD;
|
||||
break;
|
||||
case CONFIGURATION_RESET_READ_PASSWORD:
|
||||
m_state = STATE_RESET_READ_PASSWORD;
|
||||
break;
|
||||
case CONFIGURATION_PROGRAM_CONFIGURATION_REGISTERS:
|
||||
m_state = STATE_WRITE_CONFIGURATION_REGISTERS;
|
||||
@ -171,8 +214,10 @@ void x76f041_device::password_ok()
|
||||
m_byte = 0;
|
||||
break;
|
||||
case CONFIGURATION_MASS_PROGRAM:
|
||||
m_state = STATE_MASS_PROGRAM;
|
||||
break;
|
||||
case CONFIGURATION_MASS_ERASE:
|
||||
m_state = STATE_MASS_ERASE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -182,21 +227,56 @@ void x76f041_device::password_ok()
|
||||
|
||||
void x76f041_device::load_address()
|
||||
{
|
||||
/* todo: handle other bcr bits */
|
||||
int bcr;
|
||||
|
||||
m_address = m_shift;
|
||||
|
||||
verboselog( 1, "-> address: %02x\n", m_address );
|
||||
|
||||
if( ( m_command & 1 ) == 0 )
|
||||
if( ( m_configuration_registers[ CONFIG_CR ] & CR_RETRY_COUNTER_ENABLE_BIT ) != 0 &&
|
||||
m_configuration_registers[ CONFIG_RR ] == m_configuration_registers[ CONFIG_RC ] &&
|
||||
( m_configuration_registers[ CONFIG_CR ] & CR_UNAUTHORIZED_ACCESS_BITS ) == 0x80 )
|
||||
{
|
||||
bcr = m_configuration_registers[ CONFIG_BCR1 ];
|
||||
// No commands are allowed
|
||||
verboselog( 1, "unauthorized access rejected\n" );
|
||||
m_state = STATE_STOP;
|
||||
m_sdar = 1;
|
||||
m_byte = 0;
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if( ( m_command & 0xe0 ) == COMMAND_CONFIGURATION )
|
||||
{
|
||||
bcr = m_configuration_registers[ CONFIG_BCR2 ];
|
||||
// Configuration commands can be used regardless of array control register bits
|
||||
if( m_address == CONFIGURATION_RESET_WRITE_PASSWORD ||
|
||||
m_address == CONFIGURATION_RESET_READ_PASSWORD ||
|
||||
m_address == CONFIGURATION_MASS_PROGRAM ||
|
||||
m_address == CONFIGURATION_MASS_ERASE )
|
||||
{
|
||||
verboselog( 1, "password not required\n" );
|
||||
password_ok();
|
||||
}
|
||||
else
|
||||
{
|
||||
verboselog( 1, "send password\n" );
|
||||
m_state = STATE_LOAD_PASSWORD;
|
||||
m_byte = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if( ( m_configuration_registers[ CONFIG_CR ] & CR_RETRY_COUNTER_ENABLE_BIT ) != 0 &&
|
||||
m_configuration_registers[ CONFIG_RR ] == m_configuration_registers[ CONFIG_RC ] &&
|
||||
( m_configuration_registers[ CONFIG_CR ] & CR_UNAUTHORIZED_ACCESS_BITS ) != 0x80 )
|
||||
{
|
||||
// Only configuration commands are allowed
|
||||
verboselog( 1, "unauthorized access rejected\n" );
|
||||
m_state = STATE_STOP;
|
||||
m_sdar = 1;
|
||||
m_byte = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int bcr = m_configuration_registers[ ( m_command & 1 ) ? CONFIG_BCR2 : CONFIG_BCR1 ];
|
||||
if( ( m_address & 0x80 ) != 0 )
|
||||
{
|
||||
bcr >>= 4;
|
||||
@ -208,7 +288,8 @@ void x76f041_device::load_address()
|
||||
/* todo: find out when this is really checked. */
|
||||
verboselog( 1, "command not allowed\n" );
|
||||
m_state = STATE_STOP;
|
||||
m_sdar = 0;
|
||||
m_sdar = 1;
|
||||
m_byte = 0;
|
||||
}
|
||||
else if( ( ( m_command & 0xe0 ) == COMMAND_WRITE && ( bcr & BCR_X ) == 0 ) ||
|
||||
( ( m_command & 0xe0 ) == COMMAND_READ && ( bcr & BCR_Y ) == 0 ) )
|
||||
@ -272,7 +353,15 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl )
|
||||
case STATE_LOAD_PASSWORD:
|
||||
case STATE_VERIFY_PASSWORD:
|
||||
case STATE_WRITE_DATA:
|
||||
case STATE_CONFIGURATION_WRITE_DATA:
|
||||
case STATE_WRITE_CONFIGURATION_REGISTERS:
|
||||
case STATE_PROGRAM_WRITE_PASSWORD:
|
||||
case STATE_PROGRAM_READ_PASSWORD:
|
||||
case STATE_PROGRAM_CONFIGURATION_PASSWORD:
|
||||
case STATE_RESET_WRITE_PASSWORD:
|
||||
case STATE_RESET_READ_PASSWORD:
|
||||
case STATE_MASS_PROGRAM:
|
||||
case STATE_MASS_ERASE:
|
||||
if( m_scl == 0 && state != 0 )
|
||||
{
|
||||
if( m_bit < 8 )
|
||||
@ -311,6 +400,16 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl )
|
||||
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.
|
||||
m_is_password_accepted = memcmp( password(), m_write_buffer, sizeof( m_write_buffer ) ) == 0;
|
||||
if( !m_is_password_accepted )
|
||||
{
|
||||
if( m_configuration_registers[ CONFIG_CR ] & CR_RETRY_COUNTER_ENABLE_BIT )
|
||||
m_configuration_registers[ CONFIG_RC ]++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -321,7 +420,7 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl )
|
||||
if( m_shift == 0xc0 )
|
||||
{
|
||||
/* todo: this should take 10ms before it returns ok. */
|
||||
if( memcmp( password(), m_write_buffer, sizeof( m_write_buffer ) ) == 0 )
|
||||
if( m_is_password_accepted )
|
||||
{
|
||||
password_ok();
|
||||
}
|
||||
@ -338,6 +437,36 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl )
|
||||
|
||||
if( m_byte == sizeof( m_write_buffer ) )
|
||||
{
|
||||
int bcr = m_configuration_registers[ ( m_command & 1 ) ? CONFIG_BCR2 : CONFIG_BCR1 ];
|
||||
if( ( m_address & 0x80 ) != 0 )
|
||||
{
|
||||
bcr >>= 4;
|
||||
}
|
||||
|
||||
if( ( bcr & ( BCR_Z | BCR_T ) ) == BCR_T )
|
||||
{
|
||||
// Bits in the data can only be set, not cleared, when in program only mode
|
||||
bool is_unauthorized_write = false;
|
||||
|
||||
for( m_byte = 0; m_byte < sizeof( m_write_buffer ); m_byte++ )
|
||||
{
|
||||
int offset = data_offset();
|
||||
if( m_write_buffer[ m_byte ] < m_data[ offset ] )
|
||||
{
|
||||
verboselog( 1, "tried to unset bits while in program only mode\n" );
|
||||
is_unauthorized_write = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( is_unauthorized_write )
|
||||
{
|
||||
m_sdar = 1;
|
||||
m_byte = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for( m_byte = 0; m_byte < sizeof( m_write_buffer ); m_byte++ )
|
||||
{
|
||||
int offset = data_offset();
|
||||
@ -350,6 +479,15 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl )
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case STATE_CONFIGURATION_WRITE_DATA:
|
||||
// Unlike normal writes, configuration writes aren't required to be exactly 8 bytes
|
||||
// TODO: Store data in a temporary buffer until the proper end of the command before writing
|
||||
verboselog( 2, "-> data: %02x\n", m_shift );
|
||||
m_data[ data_offset() ] = m_shift;
|
||||
m_byte++;
|
||||
break;
|
||||
|
||||
case STATE_WRITE_CONFIGURATION_REGISTERS:
|
||||
verboselog( 1, "-> configuration register[ %d ]: %02x\n", m_byte, m_shift );
|
||||
/* todo: write after all bytes received? */
|
||||
@ -360,6 +498,90 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl )
|
||||
m_byte = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_PROGRAM_WRITE_PASSWORD:
|
||||
verboselog( 1, "-> program write password[ %d ]: %02x\n", m_byte, m_shift );
|
||||
m_password_temp[ m_byte++ ] = m_shift;
|
||||
|
||||
if( m_byte == sizeof( m_password_temp ) )
|
||||
{
|
||||
// Read in the password twice and if the two copies match then write it to the password field
|
||||
if( memcmp( &m_password_temp[ 0 ], &m_password_temp[ 8 ], sizeof( m_write_password ) ) == 0 )
|
||||
{
|
||||
std::copy_n( std::begin( m_password_temp ), sizeof( m_write_password ), std::begin ( m_write_password ) );
|
||||
}
|
||||
else {
|
||||
m_sdar = 1;
|
||||
}
|
||||
|
||||
std::fill( std::begin( m_password_temp ), std::end( m_password_temp ), 0 );
|
||||
|
||||
m_byte = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_PROGRAM_READ_PASSWORD:
|
||||
verboselog( 1, "-> program read password[ %d ]: %02x\n", m_byte, m_shift );
|
||||
m_password_temp[ m_byte++ ] = m_shift;
|
||||
|
||||
if( m_byte == sizeof( m_password_temp ) )
|
||||
{
|
||||
if( memcmp( &m_password_temp[ 0 ], &m_password_temp[ 8 ], sizeof( m_read_password ) ) == 0 )
|
||||
{
|
||||
std::copy_n( std::begin( m_password_temp ), sizeof( m_read_password ), std::begin ( m_read_password ) );
|
||||
}
|
||||
else {
|
||||
m_sdar = 1;
|
||||
}
|
||||
|
||||
std::fill( std::begin( m_password_temp ), std::end( m_password_temp ), 0 );
|
||||
|
||||
m_byte = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_PROGRAM_CONFIGURATION_PASSWORD:
|
||||
verboselog( 1, "-> program configuration password[ %d ]: %02x\n", m_byte, m_shift );
|
||||
m_password_temp[ m_byte++ ] = m_shift;
|
||||
|
||||
if( m_byte == sizeof( m_password_temp ) )
|
||||
{
|
||||
if( memcmp( &m_password_temp[ 0 ], &m_password_temp[ 8 ], sizeof( m_configuration_password ) ) == 0 )
|
||||
{
|
||||
std::copy_n( std::begin( m_password_temp ), sizeof( m_configuration_password ), std::begin ( m_configuration_password ) );
|
||||
}
|
||||
else {
|
||||
m_sdar = 1;
|
||||
}
|
||||
|
||||
std::fill( std::begin( m_password_temp ), std::end( m_password_temp ), 0 );
|
||||
|
||||
m_byte = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_RESET_WRITE_PASSWORD:
|
||||
verboselog( 1, "-> reset write password\n" );
|
||||
std::fill( std::begin( m_write_password ), std::end( m_write_password ), 0 );
|
||||
break;
|
||||
|
||||
case STATE_RESET_READ_PASSWORD:
|
||||
verboselog( 1, "-> reset read password\n" );
|
||||
std::fill( std::begin( m_read_password ), std::end( m_read_password ), 0 );
|
||||
break;
|
||||
|
||||
case STATE_MASS_PROGRAM:
|
||||
case STATE_MASS_ERASE:
|
||||
{
|
||||
const uint8_t fill = m_state == STATE_MASS_ERASE ? 0xff : 0;
|
||||
verboselog( 1, "-> mass erase %02x\n", fill );
|
||||
std::fill( std::begin( m_data ), std::end( m_data ), fill );
|
||||
std::fill( std::begin( m_configuration_password ), std::end( m_configuration_password ), fill );
|
||||
std::fill( std::begin( m_configuration_registers ), std::end( m_configuration_registers ), fill );
|
||||
std::fill( std::begin( m_write_password ), std::end( m_write_password ), fill );
|
||||
std::fill( std::begin( m_read_password ), std::end( m_read_password ), fill );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_bit = 0;
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
protected:
|
||||
// device-level overrides
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
// device_nvram_interface overrides
|
||||
virtual void nvram_default() override;
|
||||
@ -44,11 +45,24 @@ private:
|
||||
|
||||
enum configuration_register_t
|
||||
{
|
||||
CONFIG_BCR1 = 0,
|
||||
CONFIG_BCR2 = 1,
|
||||
CONFIG_CR = 2,
|
||||
CONFIG_RR = 3,
|
||||
CONFIG_RC = 4
|
||||
// If set to 1, retry counter is incremented when an invalid password is provided
|
||||
CR_RETRY_COUNTER_ENABLE_BIT = 0x04,
|
||||
|
||||
// If set to 1, retry counter will be reset when a correct password is provided
|
||||
CR_RETRY_COUNTER_RESET_BIT = 0x08,
|
||||
|
||||
// 10 = If retry counter is enabled, deny all commands when retry register equals retry counter
|
||||
// 00, 01, 11 = If retry counter is enabled, allow only configuration commands when retry register equals retry counter
|
||||
CR_UNAUTHORIZED_ACCESS_BITS = 0xc0,
|
||||
};
|
||||
|
||||
enum configuration_registers_t
|
||||
{
|
||||
CONFIG_BCR1 = 0, // Array Control Register
|
||||
CONFIG_BCR2 = 1, // Array Control Register 2
|
||||
CONFIG_CR = 2, // Configuration Register
|
||||
CONFIG_RR = 3, // Retry Register
|
||||
CONFIG_RC = 4 // Reset Counter
|
||||
};
|
||||
|
||||
enum bcr_t
|
||||
@ -91,8 +105,18 @@ private:
|
||||
STATE_VERIFY_PASSWORD,
|
||||
STATE_READ_DATA,
|
||||
STATE_WRITE_DATA,
|
||||
STATE_CONFIGURATION_WRITE_DATA,
|
||||
STATE_READ_CONFIGURATION_REGISTERS,
|
||||
STATE_WRITE_CONFIGURATION_REGISTERS
|
||||
STATE_WRITE_CONFIGURATION_REGISTERS,
|
||||
|
||||
STATE_PROGRAM_WRITE_PASSWORD,
|
||||
STATE_PROGRAM_READ_PASSWORD,
|
||||
STATE_PROGRAM_CONFIGURATION_PASSWORD,
|
||||
|
||||
STATE_RESET_WRITE_PASSWORD,
|
||||
STATE_RESET_READ_PASSWORD,
|
||||
STATE_MASS_PROGRAM,
|
||||
STATE_MASS_ERASE
|
||||
};
|
||||
|
||||
optional_memory_region m_region;
|
||||
@ -109,6 +133,7 @@ private:
|
||||
int m_byte;
|
||||
int m_command;
|
||||
int m_address;
|
||||
bool m_is_password_accepted;
|
||||
uint8_t m_write_buffer[ 8 ];
|
||||
uint8_t m_response_to_reset[ 4 ];
|
||||
uint8_t m_write_password[ 8 ];
|
||||
@ -116,6 +141,7 @@ private:
|
||||
uint8_t m_configuration_password[ 8 ];
|
||||
uint8_t m_configuration_registers[ 8 ];
|
||||
uint8_t m_data[ 512 ];
|
||||
uint8_t m_password_temp[ 16 ];
|
||||
};
|
||||
|
||||
|
||||
|
@ -45,13 +45,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 +65,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,6 +74,24 @@ void x76f100_device::device_start()
|
||||
save_item( NAME( m_data ) );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER( x76f100_device::write_cs )
|
||||
{
|
||||
if( m_cs != state )
|
||||
@ -124,11 +146,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,8 +165,15 @@ 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;
|
||||
|
||||
return ( 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 offset;
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER( x76f100_device::write_scl )
|
||||
@ -223,6 +254,25 @@ WRITE_LINE_MEMBER( x76f100_device::write_scl )
|
||||
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;
|
||||
|
||||
@ -233,7 +283,7 @@ WRITE_LINE_MEMBER( x76f100_device::write_scl )
|
||||
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 )
|
||||
if( m_is_password_accepted )
|
||||
{
|
||||
password_ok();
|
||||
}
|
||||
@ -250,11 +300,31 @@ WRITE_LINE_MEMBER( x76f100_device::write_scl )
|
||||
|
||||
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;
|
||||
@ -283,8 +353,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;
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ public:
|
||||
protected:
|
||||
// device-level overrides
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
// device_nvram_interface overrides
|
||||
virtual void nvram_default() override;
|
||||
@ -74,6 +75,8 @@ private:
|
||||
int m_bit;
|
||||
int m_byte;
|
||||
int m_command;
|
||||
int m_password_retry_counter;
|
||||
bool m_is_password_accepted;
|
||||
uint8_t m_write_buffer[ 8 ];
|
||||
uint8_t m_response_to_reset[ 4 ];
|
||||
uint8_t m_write_password[ 8 ];
|
||||
|
File diff suppressed because it is too large
Load Diff
227
src/mame/machine/k573mcal.cpp
Normal file
227
src/mame/machine/k573mcal.cpp
Normal file
@ -0,0 +1,227 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:windyfairy
|
||||
/*
|
||||
* Konami 573 Master Calendar
|
||||
*
|
||||
* This device was made for development/factory use only.
|
||||
* It will override the game when connected and always boot into the master calendar-specific code.
|
||||
* Every game has a master calendar-specific boot sequence but a few that don't have code to initialize security cartridges.
|
||||
*
|
||||
* The only games that this does not work on that have a security cart are ddr2mc2, ddr2ml, and ddr2mla (all variants of 885jaa02).
|
||||
* Those games will boot into a screen that shows the game code, clock, and date with nothing else.
|
||||
* For these games it's possible to set Sys573 DIPSW 1 with the master calendar connected and it will do a checksum of the game's data
|
||||
* and attempt to write it to "c:/tmp/chksum.dat" on the host debugger PC but will crash in MAME.
|
||||
*
|
||||
* DIPSW 2 and 3 on the System 573 directly are also used to specify the "spec" of the game.
|
||||
* For example, setting DIPSW allows you to switch between GN and GE specs in earlier games.
|
||||
*
|
||||
* Some games require you to hold service/F2 (and set Sys573 DIPSW 3?) during boot to initialize the installation cartridge.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "emu.h"
|
||||
#include "k573mcal.h"
|
||||
|
||||
#include "machine/timehelp.h"
|
||||
|
||||
k573mcal_device::k573mcal_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
|
||||
jvs_device(mconfig, KONAMI_573_MASTER_CALENDAR, tag, owner, clock),
|
||||
m_in1(*this, "IN1"),
|
||||
seconds(0),
|
||||
mainId(0),
|
||||
subId(0)
|
||||
{
|
||||
}
|
||||
|
||||
void k573mcal_device::device_start()
|
||||
{
|
||||
jvs_device::device_start();
|
||||
}
|
||||
|
||||
void k573mcal_device::device_reset()
|
||||
{
|
||||
seconds = 0;
|
||||
|
||||
// Randomly picked values
|
||||
// Is rendered in-game as x41-6379 where the x is generated based on the value of the main ID
|
||||
mainId = 41; // valid range 0 - 99
|
||||
subId = 6379; // valid range 0 - 9999
|
||||
|
||||
jvs_device::device_reset();
|
||||
}
|
||||
|
||||
const char *k573mcal_device::device_id()
|
||||
{
|
||||
return "KONAMI CO.,LTD.;Master Calendar;Ver1.0;";
|
||||
}
|
||||
|
||||
uint8_t k573mcal_device::command_format_version()
|
||||
{
|
||||
return 0x11;
|
||||
}
|
||||
|
||||
uint8_t k573mcal_device::jvs_standard_version()
|
||||
{
|
||||
return 0x20;
|
||||
}
|
||||
|
||||
uint8_t k573mcal_device::comm_method_version()
|
||||
{
|
||||
return 0x10;
|
||||
}
|
||||
|
||||
int k573mcal_device::handle_message(const uint8_t* send_buffer, uint32_t send_size, uint8_t*& recv_buffer)
|
||||
{
|
||||
switch (send_buffer[0]) {
|
||||
case 0xf0:
|
||||
// msg: f0 d9
|
||||
device_reset();
|
||||
break;
|
||||
|
||||
case 0x70: {
|
||||
// msg: 70
|
||||
// Writes to RTC chip
|
||||
|
||||
system_time systime;
|
||||
machine().base_datetime(systime);
|
||||
|
||||
uint8_t resp[] = {
|
||||
0x01, // status, must be 1
|
||||
uint8_t(systime.local_time.year % 100),
|
||||
uint8_t(systime.local_time.month + 1),
|
||||
systime.local_time.mday,
|
||||
systime.local_time.weekday,
|
||||
systime.local_time.hour,
|
||||
systime.local_time.minute,
|
||||
seconds // Can't be the same value twice in a row
|
||||
};
|
||||
|
||||
seconds = (seconds + 1) % 60;
|
||||
|
||||
memcpy(recv_buffer, resp, sizeof(resp));
|
||||
recv_buffer += sizeof(resp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
case 0x71: {
|
||||
// msg: 71 ff ff 01
|
||||
|
||||
uint8_t resp[] = {
|
||||
0x01, // status, must be 1
|
||||
uint8_t(m_in1->read() & 0x0f), // Area specification
|
||||
};
|
||||
|
||||
memcpy(recv_buffer, resp, sizeof(resp));
|
||||
recv_buffer += sizeof(resp);
|
||||
return 4;
|
||||
}
|
||||
|
||||
case 0x7c: {
|
||||
// msg: 7c 7f 00 04
|
||||
const uint16_t val = (send_buffer[1] << 8) | send_buffer[2];
|
||||
|
||||
if (val == 0x7f00) {
|
||||
// Return main ID
|
||||
uint8_t resp[] = {
|
||||
0x01, // status, must be 1
|
||||
uint8_t((mainId >> 24) & 0xff), uint8_t((mainId >> 16) & 0xff), uint8_t((mainId >> 8) & 0xff), uint8_t(mainId & 0xff),
|
||||
};
|
||||
|
||||
memcpy(recv_buffer, resp, sizeof(resp));
|
||||
recv_buffer += sizeof(resp);
|
||||
}
|
||||
else if (val == 0x8000) {
|
||||
// Return sub ID
|
||||
uint8_t resp[] = {
|
||||
0x01, // status, must be 1
|
||||
'<', 'I', 'N', 'I', 'T', ' ', 'C', 'O', 'M', 'P', 'L', 'E', 'T', 'E', '!', '>',
|
||||
uint8_t((subId >> 24) & 0xff), uint8_t((subId >> 16) & 0xff), uint8_t((subId >> 8) & 0xff), uint8_t(subId & 0xff),
|
||||
uint8_t((~subId >> 24) & 0xff), uint8_t((~subId >> 16) & 0xff), uint8_t((~subId >> 8) & 0xff), uint8_t(~subId & 0xff),
|
||||
};
|
||||
|
||||
memcpy(recv_buffer, resp, sizeof(resp));
|
||||
recv_buffer += sizeof(resp);
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
case 0x7d: {
|
||||
// msg: 7d 80 10 08 00 00 00 01 ff ff ff fe
|
||||
const uint16_t val = (send_buffer[1] << 8) | send_buffer[2];
|
||||
|
||||
if (val == 0x8010) {
|
||||
// Set next sub ID
|
||||
subId = (send_buffer[4] << 24) | (send_buffer[5] << 16) | (send_buffer[6] << 8) | send_buffer[7];
|
||||
|
||||
uint8_t resp[] = {
|
||||
0x01, // status, must be 1
|
||||
};
|
||||
|
||||
memcpy(recv_buffer, resp, sizeof(resp));
|
||||
recv_buffer += sizeof(resp);
|
||||
}
|
||||
|
||||
return 12;
|
||||
}
|
||||
|
||||
case 0x7e: {
|
||||
// This builds some buffer that creates data like this: @2B0001:020304050607:BC9A78563412:000000000000B5
|
||||
// 2B0001 is ???
|
||||
// 020304050607 is the machine SID
|
||||
// BC9A78563412 is the machine XID
|
||||
// 000000000000B5 is ???
|
||||
|
||||
// msg: 7e xx
|
||||
uint8_t resp[] = {
|
||||
// 0x01 - Breaks loop, sends next byte
|
||||
// 0x04 - Resends byte
|
||||
0x01,
|
||||
};
|
||||
|
||||
memcpy(recv_buffer, resp, sizeof(resp));
|
||||
recv_buffer += sizeof(resp);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
case 0x7f:
|
||||
// TODO: Where is this used?
|
||||
// The command existed in the command list for a few games (starting at Drummania?) but was never referenced and then disappeared around DDR 3rd mix.
|
||||
break;
|
||||
}
|
||||
|
||||
// Command not recognized, pass it off to the base message handler
|
||||
return jvs_device::handle_message(send_buffer, send_size, recv_buffer);
|
||||
}
|
||||
|
||||
INPUT_PORTS_START( k573mcal )
|
||||
PORT_START("IN1")
|
||||
// Default the area to 3 because it's unused and will force you to actively select the region to initialize.
|
||||
// For all but the earliest games it will show a message saying "this game only supports regions x, y, z".
|
||||
// This is also a good way to discover new bootable variants that exist on the disc but were previously unknown.
|
||||
PORT_DIPNAME(0x0f, 0x03, "Area")
|
||||
PORT_DIPSETTING(0x00, "JA")
|
||||
PORT_DIPSETTING(0x01, "UA")
|
||||
PORT_DIPSETTING(0x02, "EA")
|
||||
PORT_DIPSETTING(0x03, "3") // Unused
|
||||
PORT_DIPSETTING(0x04, "AA")
|
||||
PORT_DIPSETTING(0x05, "KA")
|
||||
PORT_DIPSETTING(0x06, "JY/AY")
|
||||
PORT_DIPSETTING(0x07, "JR")
|
||||
PORT_DIPSETTING(0x08, "JB")
|
||||
PORT_DIPSETTING(0x09, "UB")
|
||||
PORT_DIPSETTING(0x0a, "EB")
|
||||
PORT_DIPSETTING(0x0b, "11") // Unused
|
||||
PORT_DIPSETTING(0x0c, "AB")
|
||||
PORT_DIPSETTING(0x0d, "KB")
|
||||
PORT_DIPSETTING(0x0e, "JZ/AZ")
|
||||
PORT_DIPSETTING(0x0f, "JS")
|
||||
INPUT_PORTS_END
|
||||
|
||||
ioport_constructor k573mcal_device::device_input_ports() const
|
||||
{
|
||||
return INPUT_PORTS_NAME(k573mcal);
|
||||
}
|
||||
|
||||
DEFINE_DEVICE_TYPE(KONAMI_573_MASTER_CALENDAR, k573mcal_device, "k573mcal", "Konami 573 Master Calendar")
|
52
src/mame/machine/k573mcal.h
Normal file
52
src/mame/machine/k573mcal.h
Normal file
@ -0,0 +1,52 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:windyfairy
|
||||
/*
|
||||
* Konami 573 Master Calendar
|
||||
*
|
||||
*/
|
||||
#ifndef MAME_MACHINE_K573_MCAL_H
|
||||
#define MAME_MACHINE_K573_MCAL_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "machine/jvsdev.h"
|
||||
#include "machine/timer.h"
|
||||
|
||||
class k573mcal_device : public jvs_device
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
k573mcal_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&jvs_host_tag)
|
||||
: k573mcal_device(mconfig, tag, owner, clock)
|
||||
{
|
||||
host.set_tag(std::forward<T>(jvs_host_tag));
|
||||
}
|
||||
|
||||
k573mcal_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
virtual ioport_constructor device_input_ports() const override;
|
||||
|
||||
protected:
|
||||
template <uint8_t First> void set_port_tags() { }
|
||||
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
// JVS device overrides
|
||||
virtual const char *device_id() override;
|
||||
virtual uint8_t command_format_version() override;
|
||||
virtual uint8_t jvs_standard_version() override;
|
||||
virtual uint8_t comm_method_version() override;
|
||||
virtual int handle_message(const uint8_t *send_buffer, uint32_t send_size, uint8_t *&recv_buffer) override;
|
||||
|
||||
private:
|
||||
required_ioport m_in1;
|
||||
|
||||
uint8_t seconds;
|
||||
uint32_t mainId;
|
||||
uint32_t subId;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(KONAMI_573_MASTER_CALENDAR, k573mcal_device)
|
||||
|
||||
#endif // MAME_MACHINE_K573_MCAL_H
|
@ -7,6 +7,8 @@
|
||||
*
|
||||
* This is a high level emulation of the PIC used in some of the System 573 security cartridges.
|
||||
*
|
||||
* Referred to internally in game code as "NS2K001".
|
||||
*
|
||||
*/
|
||||
|
||||
#include "emu.h"
|
||||
@ -43,7 +45,8 @@ zs01_device::zs01_device( const machine_config &mconfig, const char *tag, device
|
||||
m_state( STATE_STOP ),
|
||||
m_shift( 0 ),
|
||||
m_bit( 0 ),
|
||||
m_byte( 0 )
|
||||
m_byte( 0 ),
|
||||
m_previous_byte( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
@ -65,6 +68,7 @@ void zs01_device::device_start()
|
||||
save_item( NAME( m_shift ) );
|
||||
save_item( NAME( m_bit ) );
|
||||
save_item( NAME( m_byte ) );
|
||||
save_item( NAME( m_previous_byte ) );
|
||||
save_item( NAME( m_write_buffer ) );
|
||||
save_item( NAME( m_read_buffer ) );
|
||||
save_item( NAME( m_response_key ) );
|
||||
@ -72,6 +76,25 @@ void zs01_device::device_start()
|
||||
save_item( NAME( m_command_key ) );
|
||||
save_item( NAME( m_data_key ) );
|
||||
save_item( NAME( m_data ) );
|
||||
save_item( NAME( m_configuration_registers ) );
|
||||
}
|
||||
|
||||
void zs01_device::device_reset()
|
||||
{
|
||||
memset( m_write_buffer, 0, sizeof( m_write_buffer ) );
|
||||
memset( m_read_buffer, 0, sizeof( m_read_buffer ) );
|
||||
memset( m_response_key, 0, sizeof( m_response_key ) );
|
||||
|
||||
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_previous_byte = 0;
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER( zs01_device::write_rst )
|
||||
@ -309,9 +332,7 @@ uint16_t zs01_device::calc_crc( uint8_t *buffer, uint32_t length )
|
||||
|
||||
int zs01_device::data_offset()
|
||||
{
|
||||
int block = ( ( m_write_buffer[ 0 ] & 2 ) << 7 ) | m_write_buffer[ 1 ];
|
||||
|
||||
return block * SIZE_DATA_BUFFER;
|
||||
return m_write_buffer[ 1 ] * SIZE_DATA_BUFFER;
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER( zs01_device::write_scl )
|
||||
@ -386,50 +407,94 @@ WRITE_LINE_MEMBER( zs01_device::write_scl )
|
||||
{
|
||||
decrypt( m_write_buffer, m_write_buffer, sizeof( m_write_buffer ), m_command_key, 0xff );
|
||||
|
||||
// TODO: What is bit 1 of m_write_buffer[0]?
|
||||
|
||||
// Bit 2 seems to be set when the sector is >= 4 and the sector is not 0xfc
|
||||
if( ( m_write_buffer[ 0 ] & 4 ) != 0 )
|
||||
{
|
||||
decrypt2( &m_write_buffer[ 2 ], &m_write_buffer[ 2 ], SIZE_DATA_BUFFER, m_data_key, 0x00 );
|
||||
decrypt2( &m_write_buffer[ 2 ], &m_write_buffer[ 2 ], SIZE_DATA_BUFFER, m_data_key, m_previous_byte );
|
||||
}
|
||||
|
||||
uint16_t crc = calc_crc( m_write_buffer, 10 );
|
||||
uint16_t msg_crc = ( ( m_write_buffer[ 10 ] << 8 ) | m_write_buffer[ 11 ] );
|
||||
|
||||
if( crc == ( ( m_write_buffer[ 10 ] << 8 ) | m_write_buffer[ 11 ] ) )
|
||||
verboselog( 1, "-> command: %02x (%s)\n", m_write_buffer[ 0 ], ( m_write_buffer[ 0 ] & 1 ) ? "READ" : "WRITE" );
|
||||
verboselog( 1, "-> address: %04x (%02x)\n", data_offset(), m_write_buffer[ 1 ] );
|
||||
verboselog( 1, "-> data: %02x%02x%02x%02x%02x%02x%02x%02x\n",
|
||||
m_write_buffer[ 2 ], m_write_buffer[ 3 ], m_write_buffer[ 4 ], m_write_buffer[ 5 ],
|
||||
m_write_buffer[ 6 ], m_write_buffer[ 7 ], m_write_buffer[ 8 ], m_write_buffer[ 9 ] );
|
||||
verboselog( 1, "-> crc: %04x vs %04x %s\n", crc, msg_crc, crc == msg_crc ? "" : "(BAD)");
|
||||
|
||||
if( crc == msg_crc )
|
||||
{
|
||||
verboselog( 1, "-> command: %02x\n", m_write_buffer[ 0 ] );
|
||||
verboselog( 1, "-> address: %02x\n", m_write_buffer[ 1 ] );
|
||||
verboselog( 1, "-> data: %02x%02x%02x%02x%02x%02x%02x%02x\n",
|
||||
m_write_buffer[ 2 ], m_write_buffer[ 3 ], m_write_buffer[ 4 ], m_write_buffer[ 5 ],
|
||||
m_write_buffer[ 6 ], m_write_buffer[ 7 ], m_write_buffer[ 8 ], m_write_buffer[ 9 ] );
|
||||
verboselog( 1, "-> crc: %02x%02x\n", m_write_buffer[ 10 ], m_write_buffer[ 11 ] );
|
||||
m_configuration_registers[ CONFIG_RC ] = 0; // Reset password fail counter
|
||||
|
||||
switch( m_write_buffer[ 0 ] & 1 )
|
||||
{
|
||||
case COMMAND_WRITE:
|
||||
memcpy( &m_data[ data_offset() ], &m_write_buffer[ 2 ], SIZE_DATA_BUFFER );
|
||||
|
||||
/* todo: find out what should be returned. */
|
||||
memset( &m_read_buffer[ 0 ], 0, sizeof( m_write_buffer ) );
|
||||
m_read_buffer[ 0 ] = STATUS_OK;
|
||||
|
||||
if ( m_write_buffer[ 1 ] == 0xfd )
|
||||
{
|
||||
// Erase
|
||||
std::fill( std::begin( m_data ), std::end( m_data ), 0 );
|
||||
std::fill( std::begin( m_data_key ), std::end( m_data_key ), 0 );
|
||||
}
|
||||
else if ( m_write_buffer[ 1 ] == 0xfe )
|
||||
{
|
||||
// Configuration register
|
||||
memcpy( m_configuration_registers, &m_write_buffer[ 2 ], SIZE_DATA_BUFFER );
|
||||
}
|
||||
else if ( m_write_buffer[ 1 ] == 0xff )
|
||||
{
|
||||
// Set password
|
||||
memcpy( m_data_key, &m_write_buffer[ 2 ], SIZE_DATA_BUFFER );
|
||||
}
|
||||
else if ( data_offset() < sizeof ( m_data ) )
|
||||
{
|
||||
memcpy( &m_data[ data_offset() ], &m_write_buffer[ 2 ], SIZE_DATA_BUFFER );
|
||||
}
|
||||
else
|
||||
{
|
||||
verboselog( 1, "-> unknown write offset: %04x (%02x)\n", data_offset(), m_write_buffer[ 1 ] );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case COMMAND_READ:
|
||||
/* todo: find out what should be returned. */
|
||||
memset( &m_read_buffer[ 0 ], 0, 2 );
|
||||
m_read_buffer[ 0 ] = STATUS_OK;
|
||||
|
||||
switch( m_write_buffer[ 1 ] )
|
||||
if ( m_write_buffer[ 1 ] == 0xfc )
|
||||
{
|
||||
case 0xfd:
|
||||
// TODO: Unknown serial
|
||||
// The serial is verified by the same algorithm as the one read from 0x7e8 (DS2401 serial), but the serial is different.
|
||||
for (int i = 0; i < SIZE_DATA_BUFFER; i++)
|
||||
{
|
||||
/* TODO: use read/write to talk to the ds2401, which will require a timer. */
|
||||
for( int i = 0; i < SIZE_DATA_BUFFER; i++ )
|
||||
{
|
||||
m_read_buffer[ 2 + i ] = m_ds2401->direct_read( SIZE_DATA_BUFFER - i - 1 );
|
||||
}
|
||||
m_read_buffer[2 + i] = m_ds2401->direct_read(SIZE_DATA_BUFFER - i - 1);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
else if ( m_write_buffer[ 1 ] == 0xfd )
|
||||
{
|
||||
// DS2401 serial
|
||||
/* TODO: use read/write to talk to the ds2401, which will require a timer. */
|
||||
for( int i = 0; i < SIZE_DATA_BUFFER; i++ )
|
||||
{
|
||||
m_read_buffer[ 2 + i ] = m_ds2401->direct_read( SIZE_DATA_BUFFER - i - 1 );
|
||||
}
|
||||
}
|
||||
else if ( m_write_buffer[ 1 ] == 0xfe )
|
||||
{
|
||||
// Configuration register
|
||||
memcpy( &m_read_buffer[ 2 ], m_configuration_registers, SIZE_DATA_BUFFER );
|
||||
}
|
||||
else if ( data_offset() < sizeof ( m_data ) )
|
||||
{
|
||||
memcpy( &m_read_buffer[ 2 ], &m_data[ data_offset() ], SIZE_DATA_BUFFER );
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
verboselog( 1, "-> unknown read offset: %04x (%02x)\n", data_offset(), m_write_buffer[ 1 ] );
|
||||
}
|
||||
|
||||
memcpy( m_response_key, &m_write_buffer[ 2 ], sizeof( m_response_key ) );
|
||||
@ -439,18 +504,25 @@ WRITE_LINE_MEMBER( zs01_device::write_scl )
|
||||
else
|
||||
{
|
||||
verboselog( 0, "bad crc\n" );
|
||||
m_read_buffer[ 0 ] = STATUS_ERROR;
|
||||
|
||||
/* todo: find out what should be returned. */
|
||||
memset( &m_read_buffer[ 0 ], 0xff, 2 );
|
||||
m_configuration_registers[ CONFIG_RC ]++;
|
||||
if ( m_configuration_registers[ CONFIG_RC ] >= m_configuration_registers[ CONFIG_RR ] )
|
||||
{
|
||||
// Too many bad reads, erase data
|
||||
std::fill( std::begin( m_data ), std::end( m_data ), 0 );
|
||||
std::fill( std::begin( m_data_key ), std::end( m_data_key ), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
verboselog( 1, "<- status: %02x%02x\n",
|
||||
m_read_buffer[ 0 ], m_read_buffer[ 1 ] );
|
||||
verboselog( 1, "<- status: %02x\n", m_read_buffer[ 0 ] );
|
||||
|
||||
verboselog( 1, "<- data: %02x%02x%02x%02x%02x%02x%02x%02x\n",
|
||||
m_read_buffer[ 2 ], m_read_buffer[ 3 ], m_read_buffer[ 4 ], m_read_buffer[ 5 ],
|
||||
m_read_buffer[ 6 ], m_read_buffer[ 7 ], m_read_buffer[ 8 ], m_read_buffer[ 9 ] );
|
||||
|
||||
m_previous_byte = m_read_buffer[ 1 ];
|
||||
|
||||
crc = calc_crc( m_read_buffer, 10 );
|
||||
m_read_buffer[ 10 ] = crc >> 8;
|
||||
m_read_buffer[ 11 ] = crc & 255;
|
||||
@ -574,12 +646,25 @@ READ_LINE_MEMBER( zs01_device::read_sda )
|
||||
|
||||
void zs01_device::nvram_default()
|
||||
{
|
||||
memset( m_response_to_reset, 0, sizeof( m_response_to_reset ) );
|
||||
memset( m_command_key, 0, sizeof( m_command_key ) );
|
||||
m_response_to_reset[ 0 ] = 0x5a;
|
||||
m_response_to_reset[ 1 ] = 0x53;
|
||||
m_response_to_reset[ 2 ] = 0x00;
|
||||
m_response_to_reset[ 3 ] = 0x01;
|
||||
|
||||
m_command_key[ 0 ] = 0xed;
|
||||
m_command_key[ 1 ] = 0x68;
|
||||
m_command_key[ 2 ] = 0x50;
|
||||
m_command_key[ 3 ] = 0x4b;
|
||||
m_command_key[ 4 ] = 0xc6;
|
||||
m_command_key[ 5 ] = 0x44;
|
||||
m_command_key[ 6 ] = 0x48;
|
||||
m_command_key[ 7 ] = 0x3e;
|
||||
|
||||
memset( m_data_key, 0, sizeof( m_data_key ) );
|
||||
memset( m_configuration_registers, 0, sizeof( m_configuration_registers ) );
|
||||
memset( m_data, 0, sizeof( m_data ) );
|
||||
|
||||
int expected_bytes = sizeof( m_response_to_reset ) + sizeof( m_command_key ) + sizeof( m_data_key ) + sizeof( m_data );
|
||||
int expected_bytes = sizeof( m_response_to_reset ) + sizeof( m_command_key ) + sizeof( m_data_key ) + sizeof( m_configuration_registers ) + sizeof( m_data );
|
||||
|
||||
if (!m_region.found())
|
||||
{
|
||||
@ -596,6 +681,7 @@ void zs01_device::nvram_default()
|
||||
memcpy( m_response_to_reset, region, sizeof( m_response_to_reset ) ); region += sizeof( m_response_to_reset );
|
||||
memcpy( m_command_key, region, sizeof( m_command_key ) ); region += sizeof( m_command_key );
|
||||
memcpy( m_data_key, region, sizeof( m_data_key ) ); region += sizeof( m_data_key );
|
||||
memcpy( m_configuration_registers, region, sizeof( m_configuration_registers ) ); region += sizeof( m_configuration_registers );
|
||||
memcpy( m_data, region, sizeof( m_data ) ); region += sizeof( m_data );
|
||||
}
|
||||
}
|
||||
@ -605,6 +691,7 @@ void zs01_device::nvram_read( emu_file &file )
|
||||
file.read( m_response_to_reset, sizeof( m_response_to_reset ) );
|
||||
file.read( m_command_key, sizeof( m_command_key ) );
|
||||
file.read( m_data_key, sizeof( m_data_key ) );
|
||||
file.read( m_configuration_registers, sizeof( m_configuration_registers ) );
|
||||
file.read( m_data, sizeof( m_data ) );
|
||||
}
|
||||
|
||||
@ -613,5 +700,6 @@ void zs01_device::nvram_write( emu_file &file )
|
||||
file.write( m_response_to_reset, sizeof( m_response_to_reset ) );
|
||||
file.write( m_command_key, sizeof( m_command_key ) );
|
||||
file.write( m_data_key, sizeof( m_data_key ) );
|
||||
file.write( m_configuration_registers, sizeof( m_configuration_registers ) );
|
||||
file.write( m_data, sizeof( m_data ) );
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ public:
|
||||
protected:
|
||||
// device-level overrides
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
// device_nvram_interface overrides
|
||||
virtual void nvram_default() override;
|
||||
@ -72,6 +73,18 @@ private:
|
||||
STATE_READ_DATA
|
||||
};
|
||||
|
||||
enum status_t
|
||||
{
|
||||
STATUS_OK,
|
||||
STATUS_ERROR = 2,
|
||||
};
|
||||
|
||||
enum configuration_registers_t
|
||||
{
|
||||
CONFIG_RR = 4, // Retry Register
|
||||
CONFIG_RC = 5 // Reset Counter
|
||||
};
|
||||
|
||||
// internal state
|
||||
optional_device<ds2401_device> m_ds2401;
|
||||
optional_memory_region m_region;
|
||||
@ -85,13 +98,15 @@ private:
|
||||
int m_shift;
|
||||
int m_bit;
|
||||
int m_byte;
|
||||
int m_previous_byte;
|
||||
uint8_t m_write_buffer[ 12 ];
|
||||
uint8_t m_read_buffer[ 12 ];
|
||||
uint8_t m_response_key[ 8 ];
|
||||
uint8_t m_response_to_reset[ 4 ];
|
||||
uint8_t m_command_key[ 8 ];
|
||||
uint8_t m_data_key[ 8 ];
|
||||
uint8_t m_data[ 4096 ];
|
||||
uint8_t m_configuration_registers[ 8 ];
|
||||
uint8_t m_data[ 112 ];
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user