mame/src/emu/machine/x76f100.c

329 lines
6.8 KiB
C

/*
* x76f100.c
*
* Secure SerialFlash
*
* The X76F100 is a Password Access Security Supervisor, containing one 896-bit Secure SerialFlash array.
* Access to the memory array can be controlled by two 64-bit passwords. These passwords protect read and
* write operations of the memory array.
*
*/
#include "emu.h"
#include "machine/x76f100.h"
#define VERBOSE_LEVEL 0
inline void ATTR_PRINTF(3,4) x76f100_device::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);
logerror("x76f100 %s %s: %s", tag(), machine().describe_context(), buf);
}
}
// device type definition
const device_type X76F100 = &device_creator<x76f100_device>;
x76f100_device::x76f100_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: device_secure_serial_flash(mconfig, X76F100, "X76F100", tag, owner, clock, "x76f100", __FILE__)
{
}
void x76f100_device::device_start()
{
device_secure_serial_flash::device_start();
save_item(NAME(state));
save_item(NAME(shift));
save_item(NAME(bit));
save_item(NAME(byte));
save_item(NAME(command));
save_item(NAME(write_buffer));
save_item(NAME(response_to_reset));
save_item(NAME(write_password));
save_item(NAME(read_password));
save_item(NAME(data));
}
void x76f100_device::device_reset()
{
device_secure_serial_flash::device_reset();
state = STATE_STOP;
shift = 0;
bit = 0;
byte = 0;
command = 0;
memset(write_buffer, 0, SIZE_WRITE_BUFFER);
}
void x76f100_device::nvram_default()
{
// region always wins
if(m_region)
{
// Ensure the size is correct though
if(m_region->bytes() != SIZE_RESPONSE_TO_RESET+SIZE_WRITE_PASSWORD+SIZE_READ_PASSWORD+SIZE_DATA)
logerror("x76f100 %s: Wrong region length for initialization data, expected 0x%x, got 0x%x\n",
tag(),
SIZE_RESPONSE_TO_RESET+SIZE_WRITE_PASSWORD+SIZE_READ_PASSWORD+SIZE_DATA,
m_region->bytes());
else {
UINT8 *rb = m_region->base();
int offset = 0;
memcpy(response_to_reset, rb + offset, SIZE_RESPONSE_TO_RESET); offset += SIZE_RESPONSE_TO_RESET;
memcpy(write_password, rb + offset, SIZE_WRITE_PASSWORD); offset += SIZE_WRITE_PASSWORD;
memcpy(read_password, rb + offset, SIZE_READ_PASSWORD); offset += SIZE_READ_PASSWORD;
memcpy(data, rb + offset, SIZE_DATA); offset += SIZE_DATA;
return;
}
}
// That chip isn't really usable without the passwords, so bitch
// if there's no region
logerror("x76f100 %s: Warning, no default data provided, chip is unusable.\n", tag());
memset(response_to_reset, 0, SIZE_RESPONSE_TO_RESET);
memset(write_password, 0, SIZE_WRITE_PASSWORD);
memset(read_password, 0, SIZE_READ_PASSWORD);
memset(data, 0, SIZE_DATA);
}
void x76f100_device::cs_0()
{
/* enable chip */
state = STATE_STOP;
}
void x76f100_device::cs_1()
{
/* disable chip */
state = STATE_STOP;
/* high impendence? */
sdar = 0;
}
void x76f100_device::rst_0()
{
}
void x76f100_device::rst_1()
{
if(!cs) {
verboselog(1, "goto response to reset\n");
state = STATE_RESPONSE_TO_RESET;
bit = 0;
byte = 0;
}
}
UINT8 *x76f100_device::password()
{
if((command & 0xe1) == COMMAND_READ)
{
return read_password;
}
return write_password;
}
void x76f100_device::password_ok()
{
if((command & 0xe1) == COMMAND_READ)
{
state = STATE_READ_DATA;
}
else if((command & 0xe1) == COMMAND_WRITE)
{
state = STATE_WRITE_DATA;
}
else
{
/* TODO: */
}
}
int x76f100_device::data_offset()
{
int block_offset = (command >> 1) & 0x0f;
return block_offset * SIZE_WRITE_BUFFER + byte;
}
void x76f100_device::scl_0()
{
if(cs)
return;
switch(state) {
case STATE_RESPONSE_TO_RESET:
if(bit == 0) {
shift = response_to_reset[byte];
verboselog(1, "<- response_to_reset[%d]: %02x\n", byte, shift);
}
sdar = shift & 1;
shift >>= 1;
bit++;
if(bit == 8) {
bit = 0;
byte++;
if(byte == 4)
byte = 0;
}
break;
}
}
void x76f100_device::scl_1()
{
if(cs)
return;
switch(state) {
case STATE_RESPONSE_TO_RESET:
break;
case STATE_LOAD_COMMAND:
case STATE_LOAD_PASSWORD:
case STATE_VERIFY_PASSWORD:
case STATE_WRITE_DATA:
if(bit < 8) {
verboselog(2, "clock\n");
shift <<= 1;
if(sdaw)
shift |= 1;
bit++;
} else {
switch(state) {
case STATE_LOAD_COMMAND:
verboselog(1, "-> command: %02x\n", command);
sdar = false;
command = shift;
/* todo: verify command is valid? */
state = STATE_LOAD_PASSWORD;
bit = 0;
shift = 0;
break;
case STATE_LOAD_PASSWORD:
verboselog(1, "-> password: %02x\n", shift);
sdar = false;
write_buffer[byte++] = shift;
if(byte == SIZE_WRITE_BUFFER)
state = STATE_VERIFY_PASSWORD;
bit = 0;
shift = 0;
break;
case STATE_VERIFY_PASSWORD:
verboselog(1, "-> verify password: %02x\n", shift);
sdar = false;
/* todo: this should probably be handled as a command */
if(shift == COMMAND_ACK_PASSWORD) {
/* todo: this should take 10ms before it returns ok. */
if(!memcmp(password(), write_buffer, SIZE_WRITE_BUFFER))
password_ok();
else
sdar = true;
}
bit = 0;
shift = 0;
break;
case STATE_WRITE_DATA:
verboselog(1, "-> data: %02x\n", shift );
sdar = false;
write_buffer[byte++] = shift;
if(byte == SIZE_WRITE_BUFFER) {
for(byte = 0; byte < SIZE_WRITE_BUFFER; byte++)
data[data_offset()] = write_buffer[byte];
byte = 0;
}
bit = 0;
shift = 0;
break;
}
}
break;
case STATE_READ_DATA:
if(bit < 8) {
if(bit == 0) {
shift = data[data_offset()];
verboselog(1, "<- data: %02x\n", shift );
}
sdar = (shift >> 7) & 1;
shift <<= 1;
bit++;
} else {
bit = 0;
sdar = false;
if(!sdaw) {
verboselog(2, "ack <-\n");
byte++;
} else {
verboselog(2, "nak <-\n");
}
}
break;
}
}
void x76f100_device::sda_0()
{
if(cs || !scl)
return;
switch(state) {
case STATE_STOP:
state = STATE_LOAD_COMMAND;
break;
case STATE_LOAD_PASSWORD:
/* todo: this will be the 0xc0 command, but it's not handled as a command yet. */
break;
case STATE_READ_DATA:
// c->state = STATE_LOAD_ADDRESS;
break;
default:
break;
}
bit = 0;
byte = 0;
shift = 0;
sdar = false;
}
void x76f100_device::sda_1()
{
if(cs || !scl)
return;
state = STATE_STOP;
sdar = false;
}
void x76f100_device::nvram_read(emu_file &file)
{
file.read(response_to_reset, SIZE_RESPONSE_TO_RESET);
file.read(write_password, SIZE_WRITE_PASSWORD);
file.read(read_password, SIZE_READ_PASSWORD);
file.read(data, SIZE_DATA);
}
void x76f100_device::nvram_write(emu_file &file)
{
file.write(response_to_reset, SIZE_RESPONSE_TO_RESET);
file.write(write_password, SIZE_WRITE_PASSWORD);
file.write(read_password, SIZE_READ_PASSWORD);
file.write(data, SIZE_DATA);
}