summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/ds1207.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/ds1207.cpp')
-rw-r--r--src/devices/machine/ds1207.cpp682
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;
+ }
+}
+