mirror of
https://github.com/holub/mame
synced 2025-06-05 12:26:35 +03:00
329 lines
6.8 KiB
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);
|
|
}
|