/* * 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(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_secure_serial_flash(mconfig, X76F100, "X76F100", tag, owner, clock) { } 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); }