diff --git a/src/emu/machine/upd1990a.c b/src/emu/machine/upd1990a.c index aacf5de338d..7e79f88d697 100644 --- a/src/emu/machine/upd1990a.c +++ b/src/emu/machine/upd1990a.c @@ -12,9 +12,8 @@ /* TODO: - - - set tp = 64 Hz when out of test mode - - test mode is mostly untested (is used by MS-DOS 6.2x in PC-98xx, and neogeo) + - test mode is mostly untested + - how does timer-interval differ from timer-pulse? */ @@ -53,9 +52,6 @@ upd1990a_device::upd1990a_device(const machine_config &mconfig, device_type type device_rtc_interface(mconfig, *this), m_write_data(*this), m_write_tp(*this), - m_data_out(0), - m_c(0), - m_clk(0), m_variant(variant) { } @@ -65,9 +61,6 @@ upd1990a_device::upd1990a_device(const machine_config &mconfig, const char *tag, device_rtc_interface(mconfig, *this), m_write_data(*this), m_write_tp(*this), - m_data_out(0), - m_c(0), - m_clk(0), m_variant(TYPE_1990A) { } @@ -76,6 +69,12 @@ upd4990a_device::upd4990a_device(const machine_config &mconfig, const char *tag, : upd1990a_device(mconfig, UPD4990A, "uPD4990A", tag, owner, clock, TYPE_4990A, "upd4990a", __FILE__) { } +bool upd1990a_device::is_serial_mode() +{ + // uPD4990A is in serial mode if c0/1/2 = high/VDD + return (m_variant == TYPE_4990A && m_c_unlatched == 7); +} + //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- @@ -87,9 +86,26 @@ void upd1990a_device::device_start() m_write_data.resolve_safe(); m_write_tp.resolve_safe(); + // initialize + set_current_time(machine()); + + for (int i = 0; i < 7; i++) + m_shift_reg[i] = 0; + + m_oe = 0; + m_cs = 0; + m_stb = 0; + m_data_in = 0; + m_data_out = 0; + m_c = 0; + m_clk = 0; + m_tp = 0; + m_c_unlatched = 0; + m_testmode = false; + // allocate timers m_timer_clock = timer_alloc(TIMER_CLOCK); - m_timer_clock->adjust(attotime::from_hz(clock() / 32768), 0, attotime::from_hz(clock() / 32768)); + m_timer_clock->adjust(attotime::from_hz(clock() / 32768.0), 0, attotime::from_hz(clock() / 32768.0)); // 1 second on XTAL_32_768kHz m_timer_tp = timer_alloc(TIMER_TP); m_timer_data_out = timer_alloc(TIMER_DATA_OUT); m_timer_test_mode = timer_alloc(TIMER_TEST_MODE); @@ -106,18 +122,22 @@ void upd1990a_device::device_start() save_item(NAME(m_clk)); save_item(NAME(m_tp)); save_item(NAME(m_c_unlatched)); + save_item(NAME(m_testmode)); } //------------------------------------------------- -// device_reset - device-specific reset +// rtc_clock_updated - //------------------------------------------------- -void upd1990a_device::device_reset() +void upd1990a_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) { - m_tp = 0; - m_c_unlatched = 0; - set_current_time(machine()); + m_time_counter[0] = convert_to_bcd(second); + m_time_counter[1] = convert_to_bcd(minute); + m_time_counter[2] = convert_to_bcd(hour); + m_time_counter[3] = convert_to_bcd(day); + m_time_counter[4] = (month << 4) | (day_of_week - 1); + m_time_counter[5] = convert_to_bcd(year); } @@ -135,50 +155,42 @@ void upd1990a_device::device_timer(emu_timer &timer, device_timer_id id, int par case TIMER_TP: m_tp = !m_tp; - - if (LOG) logerror("uPD1990A '%s' TP %u\n", tag(), m_tp); - m_write_tp(m_tp); break; case TIMER_DATA_OUT: m_data_out = !m_data_out; - - if (LOG) logerror("uPD1990A '%s' DATA OUT TICK %u\n", tag(), m_data_out); - - m_write_data(m_data_out); + m_write_data(get_data_out()); break; case TIMER_TEST_MODE: if (m_oe) { /* TODO: completely untested */ - /* time counter is advanced at 1024 Hz from "Second" counter input */ - int i; - - m_data_out = (m_time_counter[4] == 0); - - for(i=0;i<5;i++) + /* time counter is advanced from "Second" counter input */ + int max_shift = is_serial_mode() ? 6 : 5; + m_data_out = (m_time_counter[max_shift - 1] == 0); + m_write_data(get_data_out()); + + for (int i = 0; i < max_shift; i++) { m_time_counter[i]++; - if(m_time_counter[i] != 0) + if (m_time_counter[i] != 0) return; } } - else // parallel + else { - int i; - /* each counter is advanced at 1024 Hz in parallel, overflow carry does not affect next counter */ - m_time_counter[0]++; - m_time_counter[1]++; - m_time_counter[2]++; - m_time_counter[3]++; - m_time_counter[4]++; - + /* each counter is advanced in parallel, overflow carry does not affect next counter */ m_data_out = 0; - for(i=0;i<5;i++) + int max_shift = is_serial_mode() ? 6 : 5; + for (int i = 0; i < max_shift; i++) + { + m_time_counter[i]++; m_data_out |= (m_time_counter[i] == 0); + } + m_write_data(get_data_out()); } break; @@ -186,189 +198,189 @@ void upd1990a_device::device_timer(emu_timer &timer, device_timer_id id, int par } -//------------------------------------------------- -// rtc_clock_updated - -//------------------------------------------------- - -void upd1990a_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) -{ - m_time_counter[0] = convert_to_bcd(second); - m_time_counter[1] = convert_to_bcd(minute); - m_time_counter[2] = convert_to_bcd(hour); - m_time_counter[3] = convert_to_bcd(day); - m_time_counter[4] = (month << 4) | day_of_week; -} - - -//------------------------------------------------- -// oe_w - -//------------------------------------------------- - -WRITE_LINE_MEMBER( upd1990a_device::oe_w ) -{ - if (LOG) logerror("uPD1990A '%s' OE %u\n", tag(), state); - - m_oe = state; -} - - -//------------------------------------------------- -// cs_w - -//------------------------------------------------- - -WRITE_LINE_MEMBER( upd1990a_device::cs_w ) -{ - if (LOG) logerror("uPD1990A '%s' CS %u\n", tag(), state); - - m_cs = state; -} - - //------------------------------------------------- // stb_w - //------------------------------------------------- WRITE_LINE_MEMBER( upd1990a_device::stb_w ) { + if (!m_cs) + return; + if (LOG) logerror("uPD1990A '%s' STB %u\n", tag(), state); - m_stb = state; - - if (m_cs && m_stb && !m_clk) + // rising edge + if (!m_stb && state) { - m_c = m_c_unlatched; // if STB = 1, latch in the command bits + // read command + if (is_serial_mode()) + m_c = m_shift_reg[6]; + else + { + m_c = m_c_unlatched; + if (m_c == 7) + m_c = MODE_TEST; + } + + if (LOG) logerror("uPD1990A '%s' Command %x\n", tag(), m_c); + + // common functions + if (m_c == MODE_REGISTER_HOLD || (m_c >= MODE_TP_64HZ && m_c < MODE_TEST)) + { + // enable time counter + m_timer_clock->enable(1); + // disable testmode + m_testmode = false; + m_timer_test_mode->enable(0); + } + switch (m_c) { case MODE_REGISTER_HOLD: - if (LOG) logerror("uPD1990A '%s' Register Hold Mode\n", tag()); + // 1Hz data out pulse + m_timer_data_out->adjust(attotime::zero, 0, attotime::from_hz((clock() / 32768.0) * 2.0)); - /* enable time counter */ - m_timer_clock->enable(1); - - m_timer_test_mode->enable(0); - - /* 1 Hz data out pulse */ - m_data_out = 1; - m_timer_data_out->adjust(attotime::zero, 0, attotime::from_hz(1*2)); - - /* 64 Hz time pulse */ - m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(64*2)); + // 64Hz time pulse + m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz((clock() / 512.0) * 2.0)); break; case MODE_SHIFT: - if (LOG) logerror("uPD1990A '%s' Shift Mode\n", tag()); + // enable time counter + if (!m_testmode) + m_timer_clock->enable(1); - /* enable time counter */ - m_timer_clock->enable(1); - - /* disable data out pulse */ + // data out LSB of shift register m_timer_data_out->enable(0); + m_data_out = m_shift_reg[0] & 1; + m_write_data(get_data_out()); - m_timer_test_mode->enable(0); + // 32Hz time pulse in testmode + if (m_testmode) + m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz((clock() / 1024.0) * 2.0)); - /* output LSB of shift register */ - m_data_out = BIT(m_shift_reg[0], 0); - m_write_data(m_data_out); - - /* 32 Hz time pulse */ - m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(32*2)); break; case MODE_TIME_SET: - if (LOG) logerror("uPD1990A '%s' Time Set Mode\n", tag()); - if (LOG) logerror("uPD1990A '%s' Shift Register %02x%02x%02x%02x%02x\n", tag(), m_shift_reg[4], m_shift_reg[3], m_shift_reg[2], m_shift_reg[1], m_shift_reg[0]); - - /* disable time counter */ + { + // disable time counter m_timer_clock->enable(0); - /* disable data out pulse */ + // data out LSB of shift register m_timer_data_out->enable(0); + m_data_out = m_shift_reg[0] & 1; + m_write_data(get_data_out()); - m_timer_test_mode->enable(0); - - /* output LSB of shift register */ - m_data_out = BIT(m_shift_reg[0], 0); - m_write_data(m_data_out); - - /* load shift register data into time counter */ - for (int i = 0; i < 5; i++) - { + // load shift register data into time counter + int max_shift = is_serial_mode() ? 6 : 5; + for (int i = 0; i < max_shift; i++) m_time_counter[i] = m_shift_reg[i]; - set_time(false, 0, m_time_counter[4] >> 4, m_time_counter[4] & 0x0f, m_time_counter[3], m_time_counter[2], m_time_counter[1], m_time_counter[0]); + set_time(false, + bcd_to_integer(m_time_counter[5]), + m_time_counter[4] >> 4, + bcd_to_integer(m_time_counter[3]), + (m_time_counter[4] & 0xf) + 1, + bcd_to_integer(m_time_counter[2]), + bcd_to_integer(m_time_counter[1]), + bcd_to_integer(m_time_counter[0]) + ); + + // reset stage 10-15 of clock divider + m_timer_clock->adjust(attotime::from_ticks(m_timer_clock->remaining().as_ticks(clock()) % (clock() / 512), clock()), 0, attotime::from_hz(clock() / 32768.0)); + + // disable(low) time pulse in testmode + if (m_testmode) + { + m_timer_tp->enable(0); + m_tp = 0; + m_write_tp(m_tp); } - /* 32 Hz time pulse */ - m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(32*2)); break; + } case MODE_TIME_READ: - if (LOG) logerror("uPD1990A '%s' Time Read Mode\n", tag()); + { + // enable time counter + if (!m_testmode) + m_timer_clock->enable(1); - /* enable time counter */ - m_timer_clock->enable(1); - - m_timer_test_mode->enable(0); - - /* load time counter data into shift register */ - for (int i = 0; i < 5; i++) - { + // load time counter data into shift register + int max_shift = is_serial_mode() ? 6 : 5; + for (int i = 0; i < max_shift; i++) m_shift_reg[i] = m_time_counter[i]; - } - if (LOG) logerror("uPD1990A '%s' Shift Register %02x%02x%02x%02x%02x\n", tag(), m_shift_reg[4], m_shift_reg[3], m_shift_reg[2], m_shift_reg[1], m_shift_reg[0]); + // data out pulse: uPD4990A: 1Hz, uPD1990A: 512Hz in testmode, 0.5Hz in normal mode + float div; + if (m_variant == TYPE_4990A) + div = 32768.0; + else if (m_testmode) + div = 64.0; + else div = 65536.0; - /* 512 Hz data out pulse */ - m_data_out = 1; - m_timer_data_out->adjust(attotime::zero, 0, attotime::from_hz(512*2)); + m_timer_data_out->adjust(attotime::zero, 0, attotime::from_hz((clock() / div) * 2.0)); + + // 32Hz time pulse in testmode + if (m_testmode) + m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz((clock() / 1024.0) * 2.0)); - /* 32 Hz time pulse */ - m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(32*2)); break; + } - case MODE_TP_64HZ_SET: - if (LOG) logerror("uPD1990A '%s' TP = 64 Hz Set Mode\n", tag()); - - m_timer_test_mode->enable(0); - - /* 64 Hz time pulse */ - m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(64*2)); + case MODE_TP_64HZ: + case MODE_TP_256HZ: + case MODE_TP_2048HZ: + case MODE_TP_4096HZ: + { + // set timer pulse + const float div[4] = { 512.0, 128.0, 16.0, 8.0 }; + m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz((clock() / div[m_c - MODE_TP_64HZ]) * 2.0)); + break; + } - case MODE_TP_256HZ_SET: - if (LOG) logerror("uPD1990A '%s' TP = 256 Hz Set Mode\n", tag()); - - m_timer_test_mode->enable(0); - - /* 256 Hz time pulse */ - m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(256*2)); + case MODE_TP_1S_INT: + case MODE_TP_10S_INT: + case MODE_TP_30S_INT: + case MODE_TP_60S_INT: + { + // set timer pulse + attotime one_second = attotime::from_hz(clock() / 32768.0); + const float mul[4] = { 1.0, 10.0, 30.0, 60.0 }; + m_timer_tp->adjust(attotime::zero, 0, one_second * mul[m_c - MODE_TP_1S_INT] / 2.0); + break; + } - case MODE_TP_2048HZ_SET: - if (LOG) logerror("uPD1990A '%s' TP = 2048 Hz Set Mode\n", tag()); - - m_timer_test_mode->enable(0); - - /* 2048 Hz time pulse */ - m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(2048*2)); + case MODE_INT_RESET_OUTPUT: + case MODE_INT_RUN_CLOCK: + case MODE_INT_STOP_CLOCK: + // TODO break; case MODE_TEST: - if (LOG) logerror("uPD1990A '%s' Test Mode\n", tag()); - - /* disable time counter */ + { + // disable time counter m_timer_clock->enable(0); - /* disable data out pulse */ + // disable data out pulse m_timer_data_out->enable(0); + // enable testmode + m_testmode = true; m_timer_test_mode->enable(1); - - m_timer_test_mode->adjust(attotime::zero, 0, attotime::from_hz(1024)); + const float div = (m_variant == TYPE_4990A) ? 4.0 : 32.0; // uPD4990A: 8192Hz, uPD1990A: 1024Hz + m_timer_test_mode->adjust(attotime::zero, 0, attotime::from_hz(clock() / div)); + break; + } + + default: break; } } + + m_stb = state; } @@ -378,35 +390,40 @@ WRITE_LINE_MEMBER( upd1990a_device::stb_w ) WRITE_LINE_MEMBER( upd1990a_device::clk_w ) { + if (!m_cs) + return; + if (LOG) logerror("uPD1990A '%s' CLK %u\n", tag(), state); - if (!m_clk && state) // rising edge + // rising edge + if (!m_clk && state) { + int in = m_data_in; + + if (is_serial_mode()) + { + // always clock serial command register + in = m_shift_reg[6] & 1; + m_shift_reg[6] >>= 1; + m_shift_reg[6] |= (m_data_in << 3); + } + if (m_c == MODE_SHIFT) { - m_shift_reg[0] >>= 1; - m_shift_reg[0] |= (BIT(m_shift_reg[1], 0) << 7); - - m_shift_reg[1] >>= 1; - m_shift_reg[1] |= (BIT(m_shift_reg[2], 0) << 7); - - m_shift_reg[2] >>= 1; - m_shift_reg[2] |= (BIT(m_shift_reg[3], 0) << 7); - - m_shift_reg[3] >>= 1; - m_shift_reg[3] |= (BIT(m_shift_reg[4], 0) << 7); - - m_shift_reg[4] >>= 1; - m_shift_reg[4] |= (m_data_in << 7); - - if (m_oe) + // clock shift register + int max_shift = is_serial_mode() ? 6 : 5; + for (int i = 0; i < max_shift; i++) { - m_data_out = BIT(m_shift_reg[0], 0); - - if (LOG) logerror("uPD1990A '%s' DATA OUT %u\n", tag(), m_data_out); - - m_write_data(m_data_out); + m_shift_reg[i] >>= 1; + if (i == (max_shift - 1)) + m_shift_reg[i] |= (in << 7); // shift in new bit + else + m_shift_reg[i] |= (m_shift_reg[i + 1] << 7 & 0x80); } + + // data out LSB of shift register + m_data_out = m_shift_reg[0] & 1; + m_write_data(get_data_out()); } } @@ -415,66 +432,69 @@ WRITE_LINE_MEMBER( upd1990a_device::clk_w ) //------------------------------------------------- -// c0_w - +// misc input pins //------------------------------------------------- +WRITE_LINE_MEMBER( upd1990a_device::cs_w ) +{ + // chip select + if (LOG) logerror("uPD1990A '%s' CS %u\n", tag(), state); + m_cs = state; +} + +WRITE_LINE_MEMBER( upd1990a_device::oe_w ) +{ + // output enable + if (LOG) logerror("uPD1990A '%s' OE %u\n", tag(), state); + + int prev_oe = m_oe; + m_oe = state; + + if (m_oe != prev_oe && m_c != MODE_TEST) + m_write_data(get_data_out()); +} + WRITE_LINE_MEMBER( upd1990a_device::c0_w ) { if (LOG) logerror("uPD1990A '%s' C0 %u\n", tag(), state); - m_c_unlatched = (m_c_unlatched & 0x06) | state; } - -//------------------------------------------------- -// c1_w - -//------------------------------------------------- - WRITE_LINE_MEMBER( upd1990a_device::c1_w ) { if (LOG) logerror("uPD1990A '%s' C1 %u\n", tag(), state); - m_c_unlatched = (m_c_unlatched & 0x05) | (state << 1); } - -//------------------------------------------------- -// c2_w - -//------------------------------------------------- - WRITE_LINE_MEMBER( upd1990a_device::c2_w ) { if (LOG) logerror("uPD1990A '%s' C2 %u\n", tag(), state); - m_c_unlatched = (m_c_unlatched & 0x03) | (state << 2); } - -//------------------------------------------------- -// data_in_w - -//------------------------------------------------- - WRITE_LINE_MEMBER( upd1990a_device::data_in_w ) { + // data input if (LOG) logerror("uPD1990A '%s' DATA IN %u\n", tag(), state); - m_data_in = state; } //------------------------------------------------- -// data_out_r - +// output pins //------------------------------------------------- -READ_LINE_MEMBER( upd1990a_device::data_out_r ) +int upd1990a_device::get_data_out() { - return m_data_out; + // except when in testmode, data_out is high impedance when OE is low + return (m_oe || m_testmode) ? m_data_out : 1; } -//------------------------------------------------- -// tp_r - -//------------------------------------------------- +READ_LINE_MEMBER( upd1990a_device::data_out_r ) +{ + return get_data_out(); +} READ_LINE_MEMBER( upd1990a_device::tp_r ) { diff --git a/src/emu/machine/upd1990a.h b/src/emu/machine/upd1990a.h index f5791d0c25e..47d636e7656 100644 --- a/src/emu/machine/upd1990a.h +++ b/src/emu/machine/upd1990a.h @@ -75,7 +75,6 @@ public: protected: // device-level overrides virtual void device_start(); - virtual void device_reset(); virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); // device_rtc_interface overrides @@ -102,35 +101,48 @@ private: MODE_SHIFT, MODE_TIME_SET, MODE_TIME_READ, - MODE_TP_64HZ_SET, - MODE_TP_256HZ_SET, - MODE_TP_2048HZ_SET, + MODE_TP_64HZ, + MODE_TP_256HZ, + MODE_TP_2048HZ, + MODE_TP_4096HZ, + MODE_TP_1S_INT, + MODE_TP_10S_INT, + MODE_TP_30S_INT, + MODE_TP_60S_INT, + MODE_INT_RESET_OUTPUT, + MODE_INT_RUN_CLOCK, + MODE_INT_STOP_CLOCK, MODE_TEST }; - devcb2_write_line m_write_data; - devcb2_write_line m_write_tp; + devcb2_write_line m_write_data; + devcb2_write_line m_write_tp; - UINT8 m_time_counter[5]; // time counter - UINT8 m_shift_reg[5]; // shift register + UINT8 m_time_counter[6]; // time counter + UINT8 m_shift_reg[7]; // shift register (40 bits, or 48 bits + serial command register) int m_oe; // output enable int m_cs; // chip select int m_stb; // strobe int m_data_in; // data in int m_data_out; // data out - int m_c; // command + int m_c; // latched command int m_clk; // shift clock int m_tp; // time pulse int m_c_unlatched; // command waiting for STB - int m_variant; + bool m_testmode; // testmode active + int m_variant; + // timers emu_timer *m_timer_clock; emu_timer *m_timer_tp; emu_timer *m_timer_data_out; emu_timer *m_timer_test_mode; + + bool is_serial_mode(); + int get_data_out(); };