diff options
Diffstat (limited to 'src/devices/machine/ds1207.cpp')
-rw-r--r-- | src/devices/machine/ds1207.cpp | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/src/devices/machine/ds1207.cpp b/src/devices/machine/ds1207.cpp new file mode 100644 index 00000000000..805f15c730d --- /dev/null +++ b/src/devices/machine/ds1207.cpp @@ -0,0 +1,682 @@ +// license:BSD-3-Clause +// copyright-holders:Paul-Arnold +/* + * ds1207.c + * + * Time Key + * + * Based on ds1204 by smf. + * + * File format is as follows: + * 00-01 unique command pattern + * 02-09 identification pattern + * 0a-11 security match + * 12-41 secure memory data + * 42-43 days left + * 44-4b start time (from time_t) + * 4c control + * + * Control bits: + * bit 0 - OSC ENABLED + * bit 1 - OSC RUNNING + * bit 2 - DAYS LOCKED + * bit 3 - DAYS EXPIRED + * + * The unique command pattern can be user specific but a number of off the shelf devices exist. + * For these devices the pattern should be as follows: + * DS1207-G01 0x00 0xb0 + * DS1207-G02 0x04 0xb0 + * DS1207-G03 0x08 0xb0 + * DS1207-G04 0x0c 0xb0 + * DS1207-G05 0x10 0xb0 + */ +#include "emu.h" +#include "ds1207.h" + +#include <tuple> + +#define LOG_LINES (1U << 1) +#define LOG_STATE (1U << 2) +#define LOG_DATA (1U << 3) + +//#define VERBOSE (LOG_LINES | LOG_STATE | LOG_DATA) +#include "logmacro.h" + +#define LOGLINES(...) LOGMASKED(LOG_LINES, __VA_ARGS__) +#define LOGSTATE(...) LOGMASKED(LOG_STATE, __VA_ARGS__) +#define LOGDATA(...) LOGMASKED(LOG_DATA, __VA_ARGS__) + +// device type definition +DEFINE_DEVICE_TYPE(DS1207, ds1207_device, "ds1207", "DS1207 Time Key") + +ds1207_device::ds1207_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, DS1207, tag, owner, clock), + device_nvram_interface(mconfig, *this), + device_rtc_interface(mconfig, *this), + m_region(*this, DEVICE_SELF), + m_rst(0), + m_clk(0), + m_dqw(0), m_dqr(0), m_state(0), m_bit(0) +{ +} + +void ds1207_device::device_reset() +{ + adjust_days_left(); // compensate for time machine has been turned off +} + +void ds1207_device::device_start() +{ + new_state(STATE_STOP); + m_dqr = DQ_HIGH_IMPEDANCE; + + std::fill_n(m_command, std::size(m_command), 0); + std::fill_n(m_compare_register, std::size(m_compare_register), 0); + m_last_update_time = 0; + m_startup_time = 0; + std::fill_n(m_day_clock, std::size(m_day_clock), 0); + + save_item(NAME(m_rst)); + save_item(NAME(m_clk)); + save_item(NAME(m_dqw)); + save_item(NAME(m_dqr)); + save_item(NAME(m_state)); + save_item(NAME(m_bit)); + save_item(NAME(m_command)); + save_item(NAME(m_compare_register)); + save_item(NAME(m_unique_pattern)); + save_item(NAME(m_identification)); + save_item(NAME(m_security_match)); + save_item(NAME(m_secure_memory)); + save_item(NAME(m_day_clock)); + save_item(NAME(m_days_left)); + save_item(NAME(m_start_time)); + save_item(NAME(m_device_state)); + save_item(NAME(m_startup_time)); + save_item(NAME(m_last_update_time)); +} + +void ds1207_device::nvram_default() +{ + std::fill_n(m_unique_pattern, std::size(m_unique_pattern), 0); + std::fill_n(m_identification, std::size(m_identification), 0); + std::fill_n(m_security_match, std::size(m_security_match), 0); + std::fill_n(m_secure_memory, std::size(m_secure_memory), 0); + std::fill_n(m_days_left, std::size(m_days_left), 0); + std::fill_n(m_start_time, std::size(m_start_time), 0); + m_device_state = 0; + + int expected_bytes = sizeof(m_unique_pattern) + sizeof(m_identification) + sizeof(m_security_match) + sizeof(m_secure_memory) + + sizeof(m_days_left) + sizeof(m_start_time) + sizeof(m_device_state); + + if(!m_region.found()) + { + logerror("ds1207(%s) region not found\n", tag()); + } + else if(m_region->bytes() != expected_bytes) + { + logerror("ds1207(%s) region length 0x%x expected 0x%x\n", tag(), m_region->bytes(), expected_bytes); + } + else + { + uint8_t *region = m_region->base(); + + memcpy(m_unique_pattern, region, sizeof(m_unique_pattern)); + region += sizeof(m_unique_pattern); + memcpy(m_identification, region, sizeof(m_identification)); + region += sizeof(m_identification); + memcpy(m_security_match, region, sizeof(m_security_match)); + region += sizeof(m_security_match); + memcpy(m_secure_memory, region, sizeof(m_secure_memory)); + region += sizeof(m_secure_memory); + memcpy(m_days_left, region, sizeof(m_days_left)); + region += sizeof(m_days_left); + memcpy(m_start_time, region, sizeof(m_start_time)); + region += sizeof(m_start_time); + memcpy(&m_device_state, region, sizeof(m_device_state)); + region += sizeof(m_device_state); + } +} + +bool ds1207_device::nvram_read(util::read_stream &file) +{ + std::error_condition err; + size_t actual; + + std::tie(err, actual) = read(file, m_unique_pattern, sizeof(m_unique_pattern)); + if (err || (sizeof(m_unique_pattern) != actual)) + return false; + std::tie(err, actual) = read(file, m_identification, sizeof(m_identification)); + if (err || (sizeof(m_identification) != actual)) + return false; + std::tie(err, actual) = read(file, m_security_match, sizeof(m_security_match)); + if (err || (sizeof(m_security_match) != actual)) + return false; + std::tie(err, actual) = read(file, m_secure_memory, sizeof(m_secure_memory)); + if (err || (sizeof(m_secure_memory) != actual)) + return false; + std::tie(err, actual) = read(file, m_days_left, sizeof(m_days_left)); + if (err || (sizeof(m_days_left) != actual)) + return false; + std::tie(err, actual) = read(file, m_start_time, sizeof(m_start_time)); + if (err || (sizeof(m_start_time) != actual)) + return false; + std::tie(err, actual) = read(file, &m_device_state, sizeof(m_device_state)); + if (err || (sizeof(m_device_state) != actual)) + return false; + + return true; +} + +bool ds1207_device::nvram_write(util::write_stream &file) +{ + std::error_condition err; + size_t actual; + + std::tie(err, actual) = write(file, m_unique_pattern, sizeof(m_unique_pattern)); + if (err) + return false; + std::tie(err, actual) = write(file, m_identification, sizeof(m_identification)); + if (err) + return false; + std::tie(err, actual) = write(file, m_security_match, sizeof(m_security_match)); + if (err) + return false; + std::tie(err, actual) = write(file, m_secure_memory, sizeof(m_secure_memory)); + if (err) + return false; + std::tie(err, actual) = write(file, m_days_left, sizeof(m_days_left)); + if (err) + return false; + std::tie(err, actual) = write(file, m_start_time, sizeof(m_start_time)); + if (err) + return false; + std::tie(err, actual) = write(file, &m_device_state, sizeof(m_device_state)); + if (err) + return false; + + return true; +} + +void ds1207_device::new_state(uint8_t state) +{ + m_state = state; + m_bit = 0; +} + +void ds1207_device::writebit(uint8_t *buffer) +{ + if(m_clk) + { + uint16_t index = m_bit / 8; + uint8_t mask = 1 << (m_bit % 8); + + if(m_dqw) + { + buffer[ index ] |= mask; + } + else + { + buffer[ index ] &= ~mask; + } + + m_bit++; + } +} + +void ds1207_device::readbit(uint8_t *buffer) +{ + if(!m_clk) + { + uint16_t index = m_bit / 8; + uint8_t mask = 1 << (m_bit % 8); + + if(buffer[ index ] & mask) + { + m_dqr = 1; + } + else + { + m_dqr = 0; + } + } + else + { + m_bit++; + } +} + +void ds1207_device::write_rst(int state) +{ + const uint8_t this_state = state ? 1 : 0; + if(m_rst != this_state) + { + m_rst = this_state; + LOGLINES("%s: DS1207 rst=%d\n", machine().describe_context(), m_rst); + + if(m_rst) + { + new_state(STATE_PROTOCOL); + } + else + { + switch(m_state) + { + case STATE_WRITE_IDENTIFICATION: + LOGSTATE("%s: DS1207 reset during write identification (bit=%u)\n", machine().describe_context(), m_bit); + break; + + case STATE_WRITE_SECURITY_MATCH: + LOGSTATE("%s: DS1207 reset during write security match (bit=%u)\n", machine().describe_context(), m_bit); + break; + + case STATE_WRITE_SECURE_MEMORY: + LOGSTATE("%s: DS1207 reset during write secure memory (bit=%u)\n", machine().describe_context(), m_bit); + break; + } + + new_state(STATE_STOP); + m_dqr = DQ_HIGH_IMPEDANCE; + } + } +} + +void ds1207_device::write_clk(int state) +{ + const uint8_t this_state = state ? 1 : 0; + if(m_clk != this_state) + { + m_clk = this_state; + LOGLINES("%s: DS1207 clk=%d (bit=%u)\n", machine().describe_context(), m_clk, m_bit); + + if(m_clk) + { + m_dqr = DQ_HIGH_IMPEDANCE; + } + + switch(m_state) + { + case STATE_PROTOCOL: + writebit(m_command); + + if(m_bit == 24) + { + LOGDATA("%s: DS1207 -> command %02x %02x %02x (%02x %02x)\n", machine().describe_context(), + m_command[ 0 ], m_command[ 1 ], m_command[ 2 ], m_unique_pattern[ 0 ], m_unique_pattern[ 1 ]); + + if(m_command[ 2 ] == m_unique_pattern[ 1 ] && (m_command[ 1 ] & ~3) == m_unique_pattern[ 0 ]) + { + set_start_time(); + adjust_time_into_day(); + + if(m_command[ 0 ] == COMMAND_READ && (m_command[ 1 ] & 3) == CYCLE_NORMAL) + { + new_state(STATE_READ_IDENTIFICATION); + } + else if(m_command[ 0 ] == COMMAND_READ_DAY_CLOCK && (m_command[ 1 ] & 3) == CYCLE_PROGRAM) + { + new_state(STATE_READ_DAY_CLOCK); + } + else if(m_command[ 0 ] == COMMAND_READ_DAYS_REMAINING && (m_command[ 1 ] & 3) == CYCLE_PROGRAM) + { + new_state(STATE_READ_DAYS_REMAINING); + } + else if(!(m_device_state & DAYS_EXPIRED)) + { + if(m_command[ 0 ] == COMMAND_WRITE && (m_command[ 1 ] & 3) == CYCLE_NORMAL) + { + new_state(STATE_READ_IDENTIFICATION); + } + else if((m_command[ 1 ] & 3) == CYCLE_PROGRAM) + { + if(m_command[ 0 ] == COMMAND_WRITE) + { + new_state(STATE_WRITE_IDENTIFICATION); + } + else if(m_command[ 0 ] == COMMAND_WRITE_DAYS_REMAINING) + { + if(!(m_device_state & DAYS_LOCKED)) + { + new_state(STATE_WRITE_DAYS_REMAINING); + } + else + { + new_state(STATE_STOP); + } + } + else if(m_command[ 0 ] == COMMAND_LOCK_DAYS_COUNT) + { + m_device_state |= DAYS_LOCKED; + + new_state(STATE_STOP); + } + else if(m_command[ 0 ] == COMMAND_STOP_OSCILLATOR) + { + if(!(m_device_state & DAYS_LOCKED)) + { + m_device_state &= ~(OSC_ENABLED | OSC_RUNNING); + } + + new_state(STATE_STOP); + } + else if(m_command[ 0 ] == COMMAND_ARM_OSCILLATOR) + { + m_device_state |= OSC_ENABLED; + } + else + { + new_state(STATE_STOP); + } + } + else + { + new_state(STATE_STOP); + } + } + else + { + new_state(STATE_STOP); + } + } + else + { + new_state(STATE_STOP); + } + } + break; + + case STATE_READ_IDENTIFICATION: + readbit(m_identification); + + if(m_bit == 64) + { + LOGDATA("%s: DS1207 <- identification %02x %02x %02x %02x %02x %02x %02x %02x\n", machine().describe_context(), + m_identification[ 0 ], m_identification[ 1 ], m_identification[ 2 ], m_identification[ 3 ], + m_identification[ 4 ], m_identification[ 5 ], m_identification[ 6 ], m_identification[ 7 ]); + + new_state(STATE_WRITE_COMPARE_REGISTER); + } + break; + + case STATE_WRITE_COMPARE_REGISTER: + writebit(m_compare_register); + + if(m_bit == 64) + { + LOGDATA("%s: DS1207 -> compare register %02x %02x %02x %02x %02x %02x %02x %02x (%02x %02x %02x %02x %02x %02x %02x %02x)\n", machine().describe_context(), + m_compare_register[ 0 ], m_compare_register[ 1 ], m_compare_register[ 2 ], m_compare_register[ 3 ], + m_compare_register[ 4 ], m_compare_register[ 5 ], m_compare_register[ 6 ], m_compare_register[ 7 ], + m_security_match[ 0 ], m_security_match[ 1 ], m_security_match[ 2 ], m_security_match[ 3 ], + m_security_match[ 4 ], m_security_match[ 5 ], m_security_match[ 6 ], m_security_match[ 7 ]); + + if(memcmp(m_compare_register, m_security_match, sizeof(m_compare_register)) == 0) + { + if(m_command[ 0 ] == COMMAND_READ) + { + new_state(STATE_READ_SECURE_MEMORY); + } + else + { + new_state(STATE_WRITE_SECURE_MEMORY); + } + } + else + { + new_state(STATE_OUTPUT_GARBLED_DATA); + } + } + break; + + case STATE_READ_SECURE_MEMORY: + readbit(m_secure_memory); + + if(m_bit == 384) + { + new_state(STATE_STOP); + } + break; + + case STATE_WRITE_SECURE_MEMORY: + writebit(m_secure_memory); + + if(m_bit == 384) + { + new_state(STATE_STOP); + } + break; + + case STATE_WRITE_IDENTIFICATION: + writebit(m_identification); + + if(m_bit == 64) + { + LOGDATA("%s: DS1207 -> identification %02x %02x %02x %02x %02x %02x %02x %02x\n", machine().describe_context(), + m_identification[ 0 ], m_identification[ 1 ], m_identification[ 2 ], m_identification[ 3 ], + m_identification[ 4 ], m_identification[ 5 ], m_identification[ 6 ], m_identification[ 7 ]); + + new_state(STATE_WRITE_SECURITY_MATCH); + } + break; + + case STATE_WRITE_SECURITY_MATCH: + writebit(m_security_match); + + if(m_bit == 64) + { + LOGDATA("%s: DS1207 >- security match %02x %02x %02x %02x %02x %02x %02x %02x\n", machine().describe_context(), + m_security_match[ 0 ], m_security_match[ 1 ], m_security_match[ 2 ], m_security_match[ 3 ], + m_security_match[ 4 ], m_security_match[ 5 ], m_security_match[ 6 ], m_security_match[ 7 ]); + + new_state(STATE_STOP); + } + break; + + case STATE_OUTPUT_GARBLED_DATA: + if(!m_clk && m_command[ 0 ] == COMMAND_READ) + { + m_dqr = machine().rand() & 1; + m_bit++; + } + else if(m_clk && m_command[ 0 ] == COMMAND_WRITE) + { + m_bit++; + } + + if(m_bit == 64) + { + if(m_command[ 0 ] == COMMAND_READ) + { + LOGDATA("%s: DS1207 <- random\n", machine().describe_context()); + } + else + { + LOGDATA("%s: DS1207 -> ignore\n", machine().describe_context()); + } + + new_state(STATE_STOP); + } + break; + + case STATE_READ_DAY_CLOCK: + readbit(m_day_clock); + + if(m_bit == 20) + { + new_state(STATE_STOP); + } + break; + + case STATE_READ_DAYS_REMAINING: + readbit(m_days_left); + + if(m_bit == 9) + { + new_state(STATE_STOP); + } + break; + + case STATE_WRITE_DAYS_REMAINING: + writebit(m_days_left); + + if(m_bit == 9) + { + new_state(STATE_STOP); + } + break; + } + } +} + +void ds1207_device::write_dq(int state) +{ + const uint8_t this_state = state ? 1 : 0; + if(m_dqw != this_state) + { + m_dqw = this_state; + + LOGLINES("%s: DS1207 dqw=%u\n", machine().describe_context(), m_dqw); + } +} + +int ds1207_device::read_dq() +{ + if(m_dqr == DQ_HIGH_IMPEDANCE) + { + LOGLINES("%s: DS1207 dqr=high impedance\n", machine().describe_context()); + return 0; + } + + LOGLINES("%s: DS1207 dqr=%d (bit=%u)\n", machine().describe_context(), m_dqr, m_bit); + return m_dqr; +} + +void ds1207_device::adjust_time_into_day() +{ + if(!(m_device_state & DAYS_EXPIRED) && (m_device_state & OSC_ENABLED) && (m_device_state & OSC_RUNNING)) + { + uint64_t day_clock = ((uint64_t)m_day_clock[ 0 ]) | (((uint64_t)m_day_clock[ 1 ]) << 8) | (((uint64_t)m_day_clock[ 2 ]) << 16); + + const uint64_t cur_time = machine().time().as_ticks(32768) / 2700; + const uint64_t diff_time = cur_time - m_last_update_time; + m_last_update_time = cur_time; + + day_clock += diff_time; + + m_day_clock[ 0 ] = day_clock & 0xff; + m_day_clock[ 1 ] = (day_clock >> 8) & 0xff; + m_day_clock[ 2 ] = (day_clock >> 16) & 0xff; + + if(day_clock >= 1048576) + { + adjust_days_left(); + } + } +} + +void ds1207_device::adjust_days_left() +{ + if(!(m_device_state & DAYS_EXPIRED) && (m_device_state & OSC_ENABLED) && (m_device_state & OSC_RUNNING)) + { + const uint64_t current_time = m_startup_time + machine().time().as_ticks(1); + + uint64_t start_time = 0; + + for(int i = 0; i < 8 ; i++) + { + start_time <<= 8; + start_time |= m_start_time[ 7 - i ]; + } + + if(current_time > start_time) + { + uint64_t time_diff = current_time - start_time; + + const uint16_t days_elapsed = time_diff / (24*60*60); + + time_diff %= (24*60*60);// seconds into day + + const uint32_t day_clock = (time_diff * 32768)/2700;// time into day + + m_day_clock[ 0 ] = day_clock & 0xff; + m_day_clock[ 1 ] = (day_clock >> 8) & 0xff; + m_day_clock[ 2 ] = (day_clock >> 16) & 0xff; + + if(days_elapsed > 0) + { + uint16_t days_left = m_days_left[ 0 ] | (m_days_left[ 1 ] << 8); + + if(days_elapsed > days_left) + { + days_left = 0xffff; + m_device_state |= DAYS_EXPIRED; + } + else + { + days_left -= days_elapsed; + } + + m_days_left[ 0 ] = days_left & 0xff; + m_days_left[ 1 ] = (days_left >> 8) & 0x1; + + start_time += days_elapsed * 24 * 60 * 60; + + for(int i = 0; i < 8 ; i++) + { + m_start_time[ i ] = (start_time >> (i * 8)) & 0xff; + } + } + } + } +} + +void ds1207_device::set_start_time() +{ + if(!(m_device_state & DAYS_EXPIRED) && m_device_state & OSC_ENABLED && !(m_device_state & OSC_RUNNING)) + { + const uint64_t current_time = m_startup_time + machine().time().as_ticks(1); + + for(int i = 0; i < 8 ; i++) + { + m_start_time[ i ] = (current_time >> (i * 8)) & 0xff; + } + m_day_clock [ 0 ] = m_day_clock[ 1 ] = m_day_clock[ 2 ] = 0; + + m_device_state |= OSC_RUNNING; + } +} + +void ds1207_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) +{ + const int month_to_day_conversion[ 12 ] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + // put the seconds + m_startup_time = second; + + // put the minutes + m_startup_time += minute * 60; + + // put the hours + m_startup_time += hour * 60 * 60; + + // put the days (note -1) */ + m_startup_time += (day - 1) * 60 * 60 * 24; + + // take the months - despite popular beliefs, leap years aren't just evenly divisible by 4 */ + if(((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0)) && month > 2) + { + m_startup_time += (month_to_day_conversion[ month - 1 ] + 1) * 60 * 60 * 24; + } + else + { + m_startup_time += (month_to_day_conversion[ month - 1 ]) * 60 * 60 * 24; + } + + // put the years + int year_count = (year - 1969); + + for(int i = 0; i < year_count - 1 ; i++) + { + m_startup_time += (((((i+1970) % 4) == 0) && (((i+1970) % 100) != 0)) || (((i+1970) % 400) == 0)) ? 60*60*24*366 : 60*60*24*365; + } +} + |