// license:BSD-3-Clause // copyright-holders:Curt Coder /********************************************************************** Dallas DS1302 Trickle-Charge Timekeeping Chip emulation **********************************************************************/ /* TODO: - 12 hour format - synchronize user buffers on falling edge of CE after write */ #include "emu.h" #include "ds1302.h" //#define VERBOSE 1 #include "logmacro.h" //************************************************************************** // MACROS / CONSTANTS //************************************************************************** #define RAM_SIZE 0x1f // 31 bytes enum { STATE_COMMAND, STATE_INPUT, STATE_OUTPUT }; enum { REGISTER_SECONDS = 0, REGISTER_MINUTES, REGISTER_HOUR, REGISTER_DATE, REGISTER_MONTH, REGISTER_DAY, REGISTER_YEAR, REGISTER_CONTROL, REGISTER_TRICKLE }; #define COMMAND_READ (m_cmd & 0x01) #define COMMAND_RAM (m_cmd & 0x40) #define COMMAND_VALID (m_cmd & 0x80) #define COMMAND_BURST (((m_cmd >> 1) & 0x1f) == 0x1f) #define CLOCK_HALT (m_reg[REGISTER_SECONDS] & 0x80) #define WRITE_PROTECT (m_reg[REGISTER_CONTROL] & 0x80) #define BURST_END (COMMAND_RAM ? 0x1f : 0x09) //************************************************************************** // LIVE DEVICE //************************************************************************** // device type definition DEFINE_DEVICE_TYPE(DS1302, ds1302_device, "ds1302", "DS1302 Timekeeper") //------------------------------------------------- // ds1302_device - constructor //------------------------------------------------- ds1302_device::ds1302_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, DS1302, tag, owner, clock), device_rtc_interface(mconfig, *this), device_nvram_interface(mconfig, *this) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void ds1302_device::device_start() { // allocate timers m_clock_timer = timer_alloc(); m_clock_timer->adjust(attotime::from_hz(clock() / 32768), 0, attotime::from_hz(clock() / 32768)); for (auto & elem : m_reg) elem = 0; // state saving save_item(NAME(m_ce)); save_item(NAME(m_clk)); save_item(NAME(m_io)); save_item(NAME(m_state)); save_item(NAME(m_bits)); save_item(NAME(m_cmd)); save_item(NAME(m_data)); save_item(NAME(m_addr)); save_item(NAME(m_reg)); save_item(NAME(m_user)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void ds1302_device::device_reset() { m_clk = 0; m_ce = 0; m_state = STATE_COMMAND; m_bits = 0; m_cmd = 0; } //------------------------------------------------- // device_timer - handler timer events //------------------------------------------------- void ds1302_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { if (!CLOCK_HALT) { advance_seconds(); } } //------------------------------------------------- // nvram_default - called to initialize NVRAM to // its default state //------------------------------------------------- void ds1302_device::nvram_default() { memset(m_ram, 0, RAM_SIZE); } //------------------------------------------------- // nvram_read - called to read NVRAM from the // .nv file //------------------------------------------------- void ds1302_device::nvram_read(emu_file &file) { file.read(m_ram, RAM_SIZE); } //------------------------------------------------- // nvram_write - called to write NVRAM to the // .nv file //------------------------------------------------- void ds1302_device::nvram_write(emu_file &file) { file.write(m_ram, RAM_SIZE); } //------------------------------------------------- // rtc_clock_updated - //------------------------------------------------- void ds1302_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) { m_reg[REGISTER_YEAR] = convert_to_bcd(year); m_reg[REGISTER_DAY] = day_of_week; m_reg[REGISTER_MONTH] = convert_to_bcd(month); m_reg[REGISTER_DATE] = convert_to_bcd(day); m_reg[REGISTER_HOUR] = convert_to_bcd(hour); m_reg[REGISTER_MINUTES] = convert_to_bcd(minute); m_reg[REGISTER_SECONDS] = (m_reg[REGISTER_SECONDS] & 0x80) | convert_to_bcd(second); } //------------------------------------------------- // ce_w - chip enable write //------------------------------------------------- WRITE_LINE_MEMBER( ds1302_device::ce_w ) { LOG("DS1302 CE: %u\n", state); if (!state && m_ce) { // synchronize user buffers for (int i = 0; i < 9; i++) { m_user[i] = m_reg[i]; } } else if (state && !m_ce) { // terminate data transfer m_state = STATE_COMMAND; m_bits = 0; } m_ce = state; } //------------------------------------------------- // load_shift_register - //------------------------------------------------- void ds1302_device::load_shift_register() { if (COMMAND_READ) { if (COMMAND_RAM) { m_data = m_ram[m_addr]; LOG("DS1302 Read RAM %u:%02x\n", m_addr, m_data); } else { m_data = m_user[m_addr]; LOG("DS1302 Read Clock %u:%02x\n", m_addr, m_data); } } else { if (COMMAND_RAM) { LOG("DS1302 Write RAM %u:%02x\n", m_addr, m_data); m_ram[m_addr] = m_data; } else if (m_addr < 9) { LOG("DS1302 Write Clock %u:%02x\n", m_addr, m_data); m_reg[m_addr] = m_data; } } } //------------------------------------------------- // input_bit - //------------------------------------------------- void ds1302_device::input_bit() { switch (m_state) { case STATE_COMMAND: m_cmd >>= 1; m_cmd |= (m_io << 7); m_bits++; if (m_bits == 8) { LOG("DS1302 Command: %02x\n", m_cmd); m_bits = 0; m_addr = (m_cmd >> 1) & 0x1f; if (COMMAND_VALID) { if (COMMAND_BURST) { m_addr = 0; } if (COMMAND_READ) { load_shift_register(); m_state = STATE_OUTPUT; } else { m_state = STATE_INPUT; } } else { m_state = STATE_COMMAND; } } break; case STATE_INPUT: m_data >>= 1; m_data |= (m_io << 7); m_bits++; if (m_bits == 8) { LOG("DS1302 Data: %02x\n", m_data); m_bits = 0; if (!WRITE_PROTECT) { load_shift_register(); } if (COMMAND_BURST) { m_addr++; if (m_addr == BURST_END) { m_state = STATE_COMMAND; } } else { m_state = STATE_COMMAND; } } break; } } //------------------------------------------------- // output_bit - //------------------------------------------------- void ds1302_device::output_bit() { if (m_state != STATE_OUTPUT) return; m_io = BIT(m_data, 0); m_data >>= 1; m_bits++; if (m_bits == 8) { m_bits = 0; if (COMMAND_BURST) { m_addr++; if (m_addr == BURST_END) { m_state = STATE_COMMAND; } else { load_shift_register(); } } else { m_state = STATE_COMMAND; } } } //------------------------------------------------- // sclk_w - serial clock write //------------------------------------------------- WRITE_LINE_MEMBER( ds1302_device::sclk_w ) { LOG("DS1302 CLK: %u\n", state); if (!m_ce) return; if (!m_clk && state) // rising edge { input_bit(); } else if (m_clk && !state) // falling edge { output_bit(); } m_clk = state; } //------------------------------------------------- // io_w - I/O write //------------------------------------------------- WRITE_LINE_MEMBER( ds1302_device::io_w ) { LOG("DS1302 I/O: %u\n", state); m_io = state; } //------------------------------------------------- // io_r - I/O read //------------------------------------------------- READ_LINE_MEMBER( ds1302_device::io_r ) { return m_io; }