diff options
Diffstat (limited to 'src/mame/machine/zs01.c')
-rw-r--r-- | src/mame/machine/zs01.c | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/src/mame/machine/zs01.c b/src/mame/machine/zs01.c new file mode 100644 index 00000000000..b445c05d02c --- /dev/null +++ b/src/mame/machine/zs01.c @@ -0,0 +1,697 @@ +/* + * zs01.c + * + * Secure SerialFlash + * + * This is a high level emulation of the PIC used in some of the System 573 security cartridges. + * + */ + +#include "driver.h" +#include "machine/zs01.h" + +#define VERBOSE_LEVEL ( 0 ) + +INLINE void verboselog( int n_level, const char *s_fmt, ... ) +{ + if( VERBOSE_LEVEL >= n_level ) + { + va_list v; + char buf[ 32768 ]; + va_start( v, s_fmt ); + vsprintf( buf, s_fmt, v ); + va_end( v ); + if( cpu_getactivecpu() != -1 ) + { + logerror( "%08x: %s", activecpu_get_pc(), buf ); + } + else + { + logerror( "(timer) : %s", buf ); + } + } +} + +#define SIZE_WRITE_BUFFER ( 12 ) +#define SIZE_READ_BUFFER ( 12 ) +#define SIZE_DATA_BUFFER ( 8 ) + +#define SIZE_RESPONSE_TO_RESET ( 4 ) +#define SIZE_KEY ( 8 ) +#define SIZE_DATA ( 4096 ) + +struct zs01_chip +{ + int cs; + int rst; + int scl; + int sdaw; + int sdar; + int state; + int shift; + int bit; + int byte; + UINT8 write_buffer[ SIZE_WRITE_BUFFER ]; + UINT8 read_buffer[ SIZE_READ_BUFFER ]; + UINT8 response_key[ SIZE_KEY ]; + UINT8 *response_to_reset; + UINT8 *command_key; + UINT8 *data_key; + UINT8 *data; + UINT8 *ds2401; + zs01_write_handler write; + zs01_read_handler read; +}; + +static struct zs01_chip zs01[ ZS01_MAXCHIP ]; + +#define COMMAND_WRITE ( 0x00 ) +#define COMMAND_READ ( 0x01 ) + +#define STATE_STOP ( 0 ) +#define STATE_RESPONSE_TO_RESET ( 1 ) +#define STATE_LOAD_COMMAND ( 2 ) +#define STATE_READ_DATA ( 3 ) + +void zs01_init( int chip, UINT8 *data, zs01_write_handler write, zs01_read_handler read, UINT8 *ds2401 ) +{ + int offset; + struct zs01_chip *c; + + if( chip >= ZS01_MAXCHIP ) + { + verboselog( 0, "zs01_init( %d ) chip out of range\n", chip ); + return; + } + + c = &zs01[ chip ]; + + if( data == NULL ) + { + data = auto_malloc( + SIZE_RESPONSE_TO_RESET + + SIZE_KEY + + SIZE_KEY + + SIZE_DATA ); + } + + if( ds2401 == NULL ) + { + ds2401 = auto_malloc( SIZE_DATA_BUFFER ); + } + + c->cs = 0; + c->rst = 0; + c->scl = 0; + c->sdaw = 0; + c->sdar = 0; + c->state = STATE_STOP; + c->shift = 0; + c->bit = 0; + c->byte = 0; + memset( c->write_buffer, 0, SIZE_WRITE_BUFFER ); + memset( c->read_buffer, 0, SIZE_READ_BUFFER ); + memset( c->response_key, 0, SIZE_KEY ); + + offset = 0; + c->response_to_reset = &data[ offset ]; offset += SIZE_RESPONSE_TO_RESET; + c->command_key = &data[ offset ]; offset += SIZE_KEY; + c->data_key = &data[ offset ]; offset += SIZE_KEY; + c->data = &data[ offset ]; offset += SIZE_DATA; + c->ds2401 = ds2401; + c->write = write; + c->read = read; + + state_save_register_item( "zs01", chip, c->cs ); + state_save_register_item( "zs01", chip, c->rst ); + state_save_register_item( "zs01", chip, c->scl ); + state_save_register_item( "zs01", chip, c->sdaw ); + state_save_register_item( "zs01", chip, c->sdar ); + state_save_register_item( "zs01", chip, c->state ); + state_save_register_item( "zs01", chip, c->shift ); + state_save_register_item( "zs01", chip, c->bit ); + state_save_register_item( "zs01", chip, c->byte ); + state_save_register_item_array( "zs01", chip, c->write_buffer ); + state_save_register_item_array( "zs01", chip, c->read_buffer ); + state_save_register_item_array( "zs01", chip, c->response_key ); + state_save_register_item_pointer( "zs01", chip, c->response_to_reset, SIZE_RESPONSE_TO_RESET ); + state_save_register_item_pointer( "zs01", chip, c->command_key, SIZE_KEY ); + state_save_register_item_pointer( "zs01", chip, c->data_key, SIZE_DATA ); +} + +void zs01_rst_write( int chip, int rst ) +{ + struct zs01_chip *c; + + if( chip >= ZS01_MAXCHIP ) + { + verboselog( 0, "zs01_rst_write( %d ) chip out of range\n", chip ); + return; + } + + c = &zs01[ chip ]; + + if( c->rst != rst ) + { + verboselog( 2, "zs01(%d) rst=%d\n", chip, rst ); + } + if( c->rst == 0 && rst != 0 && c->cs == 0 ) + { + verboselog( 1, "zs01(%d) goto response to reset\n", chip ); + c->state = STATE_RESPONSE_TO_RESET; + c->bit = 0; + c->byte = 0; + } + c->rst = rst; +} + +void zs01_cs_write( int chip, int cs ) +{ + struct zs01_chip *c; + + if( chip >= ZS01_MAXCHIP ) + { + verboselog( 0, "zs01_cs_write( %d ) chip out of range\n", chip ); + return; + } + + c = &zs01[ chip ]; + + if( c->cs != cs ) + { + verboselog( 2, "zs01(%d) cs=%d\n", chip, cs ); + } +// if( c->cs != 0 && cs == 0 ) +// { +// /* enable chip */ +// c->state = STATE_STOP; +// } +// if( c->cs == 0 && cs != 0 ) +// { +// /* disable chip */ +// c->state = STATE_STOP; +// /* high impendence? */ +// c->sdar = 0; +// } + c->cs = cs; +} + +static void zs01_decrypt( UINT8 *destination, UINT8 *source, int length, UINT8 *key, UINT8 previous_byte ) +{ + UINT32 a0; + UINT32 v1; + UINT32 v0; + UINT32 a1; + UINT32 t1; + UINT32 t0; + + length--; + if( length >= 0 ) + { + do + { + t1 = source[ length ]; + a1 = 7; + t0 = t1; + + do + { + v1 = key[ a1 ]; + a1--; + v0 = v1 & 0x1f; + v0 = t0 - v0; + v1 >>= 5; + v0 &= 0xff; + a0 = (signed)v0 >> v1; + v1 = 8 - v1; + v1 &= 7; + v0 = (signed)v0 << v1; + t0 = a0 | v0; + } while( a1 > 0 ); + + v1 = key[ 0 ]; + a0 = previous_byte; + v0 = t0 & 0xff; + previous_byte = t1; + v0 = v0 - v1; + v0 = v0 ^ a0; + + destination[ length ] = v0; + length--; + } + while( length >= 0 ); + } +} + +static void zs01_decrypt2( UINT8 *destination, UINT8 *source, int length, UINT8 *key, UINT8 previous_byte ) +{ + UINT32 a0; + UINT32 v1; + UINT32 v0; + UINT32 a1; + UINT32 t2; + UINT32 t1; + UINT32 t0; + + t2 = 0; + if( length >= 0 ) + { + do + { + t1 = source[ t2 ]; + a1 = 7; + t0 = t1; + + do + { + v1 = key[ a1 ]; + a1--; + v0 = v1 & 0x1f; + v0 = t0 - v0; + v1 >>= 5; + v0 &= 0xff; + a0 = (signed)v0 >> v1; + v1 = 8 - v1; + v1 &= 7; + v0 = (signed)v0 << v1; + t0 = a0 | v0; + } while( a1 > 0 ); + + v1 = key[ 0 ]; + a0 = previous_byte; + v0 = t0 & 0xff; + previous_byte = t1; + v0 = v0 - v1; + v0 = v0 ^ a0; + + destination[ t2 ] = v0; + t2++; + + } while( t2 < length ); + } +} + +static void zs01_encrypt( UINT8 *destination, UINT8 *source, int length, UINT8 *key, UINT32 previous_byte ) +{ + UINT32 t0; + UINT32 v0; + UINT32 v1; + UINT32 a0; + UINT32 a1; + + length--; + if( length >= 0 ) + { + do + { + t0 = 1; + v0 = source[ length ]; + v1 = previous_byte; + a0 = key[ 0 ]; + v0 ^= v1; + a0 += v0; + + do + { + a1 = key[ t0 ]; + t0++; + a0 &= 0xff; + v0 = a1 >> 5; + v1 = a0 << v0; + v0 = 8 - v0; + v0 &= 7; + a0 = (signed) a0 >> v0; + v1 |= a0; + v1 &= 0xff; + a1 &= 0x1f; + v1 += a1; + v0 = (signed) t0 < 8; + a0 = v1; + + } while( v0 != 0 ); + + previous_byte = v1; + + destination[ length ] = a0; + length--; + + } while( length >= 0 ); + } +} + +static UINT16 zs01_crc( UINT8 *buffer, UINT32 length ) +{ + UINT32 v1; + UINT32 a3; + UINT32 v0; + UINT32 a2; + + v1 = 0xffff; + a3 = 0; + + if( length > 0 ) + { + do + { + v0 = buffer[ a3 ]; + a2 = 7; + v0 = v0 << 8; + v1 = v1 ^ v0; + v0 = v1 & 0x8000; + + do + { + if( v0 != 0 ) + { + v0 = v1 << 1; + v1 = v0 ^ 0x1021; + } + else + { + v0 = v1 << 1; + v1 = v1 << 1; + } + + a2--; + v0 = v1 & 0x8000; + } while( (signed) a2 >= 0 ); + + a3++; + v0 = (signed) a3 < (signed) length; + } while ( v0 != 0 ); + } + + v0 = ~v1 ; + v0 = v0 & 0xffff; + + return v0; +} + +static int zs01_data_offset( struct zs01_chip *c ) +{ + int block = ( ( c->write_buffer[ 0 ] & 2 ) << 7 ) | c->write_buffer[ 1 ]; + + return block * SIZE_DATA_BUFFER; +} + +void zs01_scl_write( int chip, int scl ) +{ + struct zs01_chip *c; + + if( chip >= ZS01_MAXCHIP ) + { + verboselog( 0, "zs01_scl_write( %d ) chip out of range\n", chip ); + return; + } + + c = &zs01[ chip ]; + + if( c->scl != scl ) + { + verboselog( 2, "zs01(%d) scl=%d\n", chip, scl ); + } + if( c->cs == 0 ) + { + switch( c->state ) + { + case STATE_STOP: + break; + + case STATE_RESPONSE_TO_RESET: + if( c->scl != 0 && scl == 0 ) + { + if( c->bit == 0 ) + { + c->shift = c->response_to_reset[ c->byte ]; + verboselog( 1, "zs01(%d) <- response_to_reset[%d]: %02x\n", chip, c->byte, c->shift ); + } + + c->sdar = ( c->shift >> 7 ) & 1; + c->shift <<= 1; + c->bit++; + + if( c->bit == 8 ) + { + c->bit = 0; + c->byte++; + if( c->byte == 4 ) + { + c->sdar = 1; + verboselog( 1, "zs01(%d) goto stop\n", chip ); + c->state = STATE_STOP; + } + } + } + break; + + case STATE_LOAD_COMMAND: + if( c->scl == 0 && scl != 0 ) + { + if( c->bit < 8 ) + { + verboselog( 2, "zs01(%d) clock\n", chip ); + c->shift <<= 1; + if( c->sdaw != 0 ) + { + c->shift |= 1; + } + c->bit++; + } + else + { + c->sdar = 0; + + switch( c->state ) + { + case STATE_LOAD_COMMAND: + c->write_buffer[ c->byte ] = c->shift; + verboselog( 2, "zs01(%d) -> write_buffer[%d]: %02x\n", chip, c->byte, c->write_buffer[ c->byte ] ); + + c->byte++; + if( c->byte == SIZE_WRITE_BUFFER ) + { + UINT16 crc; + + zs01_decrypt( c->write_buffer, c->write_buffer, SIZE_WRITE_BUFFER, c->command_key, 0xff ); + + if( ( c->write_buffer[ 0 ] & 4 ) != 0 ) + { + zs01_decrypt2( &c->write_buffer[ 2 ], &c->write_buffer[ 2 ], SIZE_DATA_BUFFER, c->data_key, 0x00 ); + } + + crc = zs01_crc( c->write_buffer, 10 ); + + if( crc == ( ( c->write_buffer[ 10 ] << 8 ) | c->write_buffer[ 11 ] ) ) + { + verboselog( 1, "zs01(%d) -> command: %02x\n", chip, c->write_buffer[ 0 ] ); + verboselog( 1, "zs01(%d) -> address: %02x\n", chip, c->write_buffer[ 1 ] ); + verboselog( 1, "zs01(%d) -> data: %02x%02x%02x%02x%02x%02x%02x%02x\n", chip, + c->write_buffer[ 2 ], c->write_buffer[ 3 ], c->write_buffer[ 4 ], c->write_buffer[ 5 ], + c->write_buffer[ 6 ], c->write_buffer[ 7 ], c->write_buffer[ 8 ], c->write_buffer[ 9 ] ); + verboselog( 1, "zs01(%d) -> crc: %02x%02x\n", chip, c->write_buffer[ 10 ], c->write_buffer[ 11 ] ); + + switch( c->write_buffer[ 0 ] & 1 ) + { + case COMMAND_WRITE: + memcpy( &c->data[ zs01_data_offset( c ) ], &c->write_buffer[ 2 ], SIZE_DATA_BUFFER ); + + /* todo: find out what should be returned. */ + memset( &c->read_buffer[ 0 ], 0, SIZE_WRITE_BUFFER ); + break; + + case COMMAND_READ: + /* todo: find out what should be returned. */ + memset( &c->read_buffer[ 0 ], 0, 2 ); + + switch( c->write_buffer[ 1 ] ) + { + case 0xfd: + { + /* TODO: use read/write to talk to the ds2401, which will require a timer. */ + int i; + for( i = 0; i < SIZE_DATA_BUFFER; i++ ) + { + c->read_buffer[ 2 + i ] = c->ds2401[ SIZE_DATA_BUFFER - i - 1 ]; + } + } + break; + default: + memcpy( &c->read_buffer[ 2 ], &c->data[ zs01_data_offset( c ) ], SIZE_DATA_BUFFER ); + break; + } + + memcpy( c->response_key, &c->write_buffer[ 2 ], SIZE_KEY ); + break; + } + } + else + { + verboselog( 0, "zs01(%d) bad crc\n", chip ); + + /* todo: find out what should be returned. */ + memset( &c->read_buffer[ 0 ], 0xff, 2 ); + } + + verboselog( 1, "zs01(%d) <- status: %02x%02\n", chip, + c->read_buffer[ 0 ], c->read_buffer[ 1 ] ); + + verboselog( 1, "zs01(%d) <- data: %02x%02x%02x%02x%02x%02x%02x%02x\n", chip, + c->read_buffer[ 2 ], c->read_buffer[ 3 ], c->read_buffer[ 4 ], c->read_buffer[ 5 ], + c->read_buffer[ 6 ], c->read_buffer[ 7 ], c->read_buffer[ 8 ], c->read_buffer[ 9 ] ); + + crc = zs01_crc( c->read_buffer, 10 ); + c->read_buffer[ 10 ] = crc >> 8; + c->read_buffer[ 11 ] = crc & 255; + + zs01_encrypt( c->read_buffer, c->read_buffer, SIZE_READ_BUFFER, c->response_key, 0xff ); + + c->byte = 0; + c->state = STATE_READ_DATA; + } + break; + } + + c->bit = 0; + c->shift = 0; + } + } + break; + + case STATE_READ_DATA: + if( c->scl == 0 && scl != 0 ) + { + if( c->bit < 8 ) + { + if( c->bit == 0 ) + { + switch( c->state ) + { + case STATE_READ_DATA: + c->shift = c->read_buffer[ c->byte ]; + verboselog( 2, "zs01(%d) <- read_buffer[%d]: %02x\n", chip, c->byte, c->shift ); + break; + } + } + c->sdar = ( c->shift >> 7 ) & 1; + c->shift <<= 1; + c->bit++; + } + else + { + c->bit = 0; + c->sdar = 0; + if( c->sdaw == 0 ) + { + verboselog( 2, "zs01(%d) ack <-\n", chip ); + c->byte++; + if( c->byte == SIZE_READ_BUFFER ) + { + c->byte = 0; + c->sdar = 1; + c->state = STATE_LOAD_COMMAND; + } + } + else + { + verboselog( 2, "zs01(%d) nak <-\n", chip ); + } + } + } + break; + } + } + c->scl = scl; +} + +void zs01_sda_write( int chip, int sda ) +{ + struct zs01_chip *c; + + if( chip >= ZS01_MAXCHIP ) + { + verboselog( 0, "zs01_sda_write( %d ) chip out of range\n", chip ); + return; + } + + c = &zs01[ chip ]; + + if( c->sdaw != sda ) + { + verboselog( 2, "zs01(%d) sdaw=%d\n", chip, sda ); + } + if( c->cs == 0 && c->scl != 0 ) + { +// if( c->sdaw == 0 && sda != 0 ) +// { +// verboselog( 1, "zs01(%d) goto stop\n", chip ); +// c->state = STATE_STOP; +// c->sdar = 0; +// } + if( c->sdaw != 0 && sda == 0 ) + { + switch( c->state ) + { + case STATE_STOP: + verboselog( 1, "zs01(%d) goto start\n", chip ); + c->state = STATE_LOAD_COMMAND; + break; + +// default: +// verboselog( 1, "zs01(%d) skipped start (default)\n", chip ); +// break; + } + + c->bit = 0; + c->byte = 0; + c->shift = 0; + c->sdar = 0; + } + } + c->sdaw = sda; +} + +int zs01_sda_read( int chip ) +{ + struct zs01_chip *c; + + if( chip >= ZS01_MAXCHIP ) + { + verboselog( 0, "zs01_sda_read( %d ) chip out of range\n", chip ); + return 1; + } + + c = &zs01[ chip ]; + if( c->cs != 0 ) + { + verboselog( 2, "zs01(%d) not selected\n", chip ); + return 1; + } + verboselog( 2, "zs01(%d) sdar=%d\n", chip, c->sdar ); + + return c->sdar; +} + +static void nvram_handler_zs01( int chip, running_machine *machine, mame_file *file, int read_or_write ) +{ + struct zs01_chip *c; + + if( chip >= ZS01_MAXCHIP ) + { + verboselog( 0, "nvram_handler_zs01( %d ) chip out of range\n", chip ); + return; + } + + c = &zs01[ chip ]; + + if( read_or_write ) + { + mame_fwrite( file, c->data, SIZE_DATA ); + } + else if( file ) + { + mame_fread( file, c->data, SIZE_DATA ); + } +} + +NVRAM_HANDLER( zs01_0 ) { nvram_handler_zs01( 0, machine, file, read_or_write ); } +NVRAM_HANDLER( zs01_1 ) { nvram_handler_zs01( 1, machine, file, read_or_write ); } |