diff options
-rw-r--r-- | src/devices/bus/hp9845_io/98035.cpp | 985 | ||||
-rw-r--r-- | src/devices/bus/hp9845_io/98035.h | 125 |
2 files changed, 533 insertions, 577 deletions
diff --git a/src/devices/bus/hp9845_io/98035.cpp b/src/devices/bus/hp9845_io/98035.cpp index c3338add89a..f47ef125a10 100644 --- a/src/devices/bus/hp9845_io/98035.cpp +++ b/src/devices/bus/hp9845_io/98035.cpp @@ -26,20 +26,6 @@ For further info on this module see also: http://www.hp9825.com/html/real-time_clock.html - This driver tries to reproduce in C++ the behaviour of the - nanoprocessor because I couldn't find any dump of the firmware ROM - (HP part-no 1818-0469). - The following commands are recognized in my "imitation": - A Halt all timer units - B Warm reset - E Read & clear interface errors - F Activate all timer units - R Read real time clock - S Set real time clock - T Read trigger status - U Control timer units - W Read lost interrupts - The main reference for this module is this manual: HP, 98035A Real Time Clock Installation and Operation Manual @@ -49,7 +35,7 @@ #include "coreutil.h" // Debugging -#define VERBOSE 0 +#define VERBOSE 1 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) #define BIT_MASK(n) (1U << (n)) @@ -58,26 +44,59 @@ #define BIT_CLR(w , n) ((w) &= ~BIT_MASK(n)) #define BIT_SET(w , n) ((w) |= BIT_MASK(n)) -// Possible delimiters -#define DELIM_CH_0 '/' -#define DELIM_CH_1 '\n' +// Frequency of digit multiplexing in clock chip +#define DIGIT_MUX_FREQ (XTAL_32_768kHz / 64) -// Error masks in m_error -#define ERR_MASK_INT_MISSED BIT_MASK(0) -#define ERR_MASK_WRONG_INS BIT_MASK(1) -#define ERR_MASK_WRONG_UNIT BIT_MASK(2) -#define ERR_MASK_CANT_EXEC BIT_MASK(3) +// Duration of key presses +#define KEY_PRESS_SHORT 1 // 1.95 ms +#define KEY_PRESS_LONG 512 // 1 s -// Empty date/time fields -#define EMPTY_FIELD 0xff +// Mask of keys in m_clock_keys +#define KEY_READ_MASK 1 +#define KEY_SET_MASK 2 +#define KEY_CHG_MASK 4 // Timers enum { - MSEC_TMR_ID + MSEC_TMR_ID, + CLOCK_TMR_ID +}; + +// 7-segment display +// Mapping of 7 segments on NP input port is as follows: +// Bit Segment +// ============ +// 7 N/U (1) +// 6 N/U (1) +// 5 Seg "G" +// 4 Seg "F" +// 3 Seg "E" +// 2 Seg "C" +// 1 Seg "B" +// 0 Seg "A" +// +// Segment "D" is not mapped as it's not needed to tell decimal digits apart. +// A segment is ON when its bit is "0". +#define SEVEN_SEG_OFF 0xff // All segments off +#define SEVEN_SEG_A 0xc0 // "A" +#define SEVEN_SEG_P 0xc4 // "P" +static const uint8_t dec_2_seven_segs[] = { + 0xe0, // 0 + 0xf9, // 1 + 0xd4, // 2 + 0xd8, // 3 + 0xc9, // 4 + 0xca, // 5 + 0xc2, // 6 + 0xf8, // 7 + 0xc0, // 8 + 0xc8 // 9 }; hp98035_io_card::hp98035_io_card(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : hp9845_io_card_device(mconfig , HP98035_IO_CARD , "HP98035 card" , tag , owner , clock , "hp98035" , __FILE__) + : hp9845_io_card_device(mconfig , HP98035_IO_CARD , "HP98035 card" , tag , owner , clock , "hp98035" , __FILE__), + device_rtc_interface(mconfig , *this), + m_cpu(*this , "np") { } @@ -96,7 +115,29 @@ ioport_constructor hp98035_io_card::device_input_ports() const void hp98035_io_card::device_start() { + save_item(NAME(m_np_ram)); + save_item(NAME(m_ram_addr)); + save_item(NAME(m_ram_data_in)); + save_item(NAME(m_dc)); + save_item(NAME(m_np_irq)); + save_item(NAME(m_flg)); + save_item(NAME(m_inten)); + save_item(NAME(m_intflag)); + save_item(NAME(m_irq)); + save_item(NAME(m_idr_full)); + save_item(NAME(m_idr)); + save_item(NAME(m_odr)); + save_item(NAME(m_clock_1s_div)); + //save_item(NAME(m_clock_state)); + save_item(NAME(m_clock_digits)); + save_item(NAME(m_clock_mux)); + save_item(NAME(m_clock_segh)); + save_item(NAME(m_clock_keys)); + save_item(NAME(m_prev_clock_keys)); + save_item(NAME(m_clock_key_cnt)); + m_msec_timer = timer_alloc(MSEC_TMR_ID); + m_clock_timer = timer_alloc(CLOCK_TMR_ID); } void hp98035_io_card::device_reset() @@ -107,112 +148,62 @@ void hp98035_io_card::device_reset() m_idr_full = false; m_idr = 0; m_odr = 0; - m_ibuffer_ptr = 0; - m_obuffer_len = 0; - m_obuffer_ptr = 0; sts_w(true); set_flg(true); - // Set real time from the real world - system_time systime; - machine().base_datetime(systime); - m_msec = 0; - m_sec = systime.local_time.second; - m_min = systime.local_time.minute; - m_hrs = systime.local_time.hour; - m_dom = systime.local_time.mday; - m_mon = systime.local_time.month + 1; - attotime period(attotime::from_msec(1)); m_msec_timer->adjust(period , 0 , period); + period = attotime::from_hz(DIGIT_MUX_FREQ); + m_clock_timer->adjust(period , 0 , period); + half_init(); } void hp98035_io_card::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { if (id == MSEC_TMR_ID) { - // Update real time - m_msec++; - if (m_msec >= 1000) { - m_msec = 0; - m_sec++; - if (m_sec >= 60) { - m_sec = 0; - m_min++; - if (m_min >= 60) { - m_min = 0; - m_hrs++; - if (m_hrs >= 24) { - m_hrs = 0; - m_dom++; - // Use year 1: this RTC doesn't know the year and so it never advances - // the date to february 29th - if (m_dom > gregorian_days_in_month(m_mon , 1)) { - m_dom = 1; - m_mon++; - if (m_mon > 12) { - m_mon = 1; - } - } - } - } - } - - // Every second: check if new real time matches in any active output unit - for (timer_unit_t& unit : m_units) { - if (!unit.m_input && unit.m_state == UNIT_ACTIVE && - m_sec == unit.m_match_datetime[ 3 ] && - (unit.m_match_datetime[ 2 ] == EMPTY_FIELD || - (m_min == unit.m_match_datetime[ 2 ] && - (unit.m_match_datetime[ 1 ] == EMPTY_FIELD || - (m_hrs == unit.m_match_datetime[ 1 ] && - (unit.m_match_datetime[ 0 ] == EMPTY_FIELD || - m_dom == unit.m_match_datetime[ 0 ])))))) { - // Matched - unit.adv_state(); - LOG(("matched %02u:%03u %p %d %u\n" , m_sec , m_msec , &unit , unit.m_state , unit.m_value)); - } + // On real hw there's a full 4-bit decimal counter, but only the LSB is used to + // generate interrupts + m_np_irq = !m_np_irq; + update_dc(); + } else if (id == CLOCK_TMR_ID) { + // Update digit multiplexer + if (m_clock_state == CLOCK_OFF) { + m_clock_mux = 0; + } else { + m_clock_mux <<= 1; + if ((m_clock_mux & 7) == 0) { + m_clock_mux = 1; } } - - // Every ms: advance active units - uint8_t triggered = 0; - for (timer_unit_t& unit : m_units) { - if (unit.m_state != UNIT_IDLE) { - if (unit.m_input && unit.m_state == UNIT_ACTIVE) { - // Input unit: it just counts msecs - // In the real 98035 there is a limit value of 1E+10: not simulated here - unit.m_value++; - } else if (!unit.m_input && unit.m_state == UNIT_WAIT_FOR_TO && - (unit.m_value == 0 || --unit.m_value == 0)) { - // Output unit that is not waiting for real time match and that just reached timeout - // Triggered! - LOG(("triggered %02u:%03u %p\n" , m_sec , m_msec , &unit)); - BIT_SET(triggered, unit.m_port - 1); - unit.adv_state(); - if (unit.m_value == 0) { - LOG(("deact %p\n" , &unit)); - unit.deactivate(); - } - } + // Act on clock chip "keys" + if (m_clock_keys == 0 || m_clock_keys != m_prev_clock_keys) { + m_clock_key_cnt = 0; + if (m_clock_keys == 0 && m_clock_state == CLOCK_HHMM) { + // Keys released in HHMM state -> turn display off + // In real hw there is probably 1 s delay + m_clock_state = CLOCK_OFF; + regen_clock_image(); } - } - // Generate IRQ - if (triggered) { - // Store which output(s) triggered - m_triggered = triggered; - if (m_inten) { - m_irq = true; - update_irq(); - LOG(("IRQ %02x\n" , m_triggered)); - } else if (m_intflag) { - set_error(ERR_MASK_INT_MISSED); - // Record lost interrupts - m_lost_irq = triggered; - LOG(("lost %02x\n" , m_triggered)); + } else if (m_clock_key_cnt < KEY_PRESS_LONG) { + m_clock_key_cnt++; + if (m_clock_key_cnt == KEY_PRESS_SHORT) { + /// Short key press + clock_short_press(); + } else if (m_clock_key_cnt == KEY_PRESS_LONG) { + // Long key press + clock_long_press(); } } + m_prev_clock_keys = m_clock_keys; + // Count seconds + m_clock_1s_div++; + if (m_clock_1s_div >= DIGIT_MUX_FREQ) { + m_clock_1s_div = 0; + advance_seconds(); + regen_clock_image(); + } } } @@ -223,7 +214,7 @@ READ16_MEMBER(hp98035_io_card::reg_r) switch (offset) { case 0: // R4: ODR - res = m_odr; + res = ~m_odr & 0xff; break; case 1: @@ -235,7 +226,7 @@ READ16_MEMBER(hp98035_io_card::reg_r) if (m_intflag) { BIT_SET(res , 1); } - if (m_error) { + if (!BIT(m_dc , 5)) { BIT_SET(res , 0); } break; @@ -256,9 +247,8 @@ WRITE16_MEMBER(hp98035_io_card::reg_w) switch (offset) { case 0: // R4: IDR - m_idr = (uint8_t)data; + m_idr = (uint8_t)(~data); m_idr_full = true; - update_ibuffer(); break; case 1: @@ -274,503 +264,456 @@ WRITE16_MEMBER(hp98035_io_card::reg_w) case 3: // R7: trigger set_flg(false); - update_ibuffer(); - update_obuffer(); break; } LOG(("write R%u=%04x\n" , offset + 4 , data)); } -void hp98035_io_card::half_init(void) +WRITE8_MEMBER(hp98035_io_card::ram_addr_w) { - m_inten = false; - m_intflag = false; - m_irq = false; - m_error = 0; - m_triggered = 0; - m_lost_irq = 0; - update_irq(); + m_ram_addr = data; +} - for (timer_unit_t& unit : m_units) { - unit.init(); +READ8_MEMBER(hp98035_io_card::ram_data_r) +{ + return m_np_ram[ m_ram_addr ]; +} + +WRITE8_MEMBER(hp98035_io_card::ram_addr_data_w) +{ + m_ram_addr = data; + m_np_ram[ m_ram_addr ] = m_ram_data_in; +} + +WRITE8_MEMBER(hp98035_io_card::ram_data_w) +{ + m_ram_data_in = data; +} + +WRITE8_MEMBER(hp98035_io_card::clock_key_w) +{ + m_clock_keys = data & 7; +} + +READ8_MEMBER(hp98035_io_card::clock_digit_r) +{ + switch (m_clock_mux) { + case 1: + return m_clock_digits[ 0 ]; + + case 2: + return m_clock_digits[ 1 ]; + + case 4: + return m_clock_digits[ 2 ]; + + default: + return SEVEN_SEG_OFF; } - // Unit 1 defaults to output 1 - // Unit 2 defaults to input 1 - m_units[ 0 ].m_input = false; - m_units[ 0 ].m_port = 1; - m_units[ 1 ].m_input = true; - m_units[ 1 ].m_port = 1; +} - set_obuffer('\n'); +WRITE8_MEMBER(hp98035_io_card::odr_w) +{ + m_odr = data; + set_flg(true); } -void hp98035_io_card::set_flg(bool value) +READ8_MEMBER(hp98035_io_card::idr_r) { - m_flg = value; - flg_w(m_flg); + set_flg(true); + m_idr_full = false; + return m_idr; } -void hp98035_io_card::update_irq(void) +READ8_MEMBER(hp98035_io_card::np_status_r) { + // Bit 2 = 0: use US date format + uint8_t res = 0x03; + + if (!m_intflag) { + BIT_SET(res, 7); + } if (!m_inten) { - m_irq = false; + BIT_SET(res, 6); } - irq_w(m_inten && m_irq); + if (!m_flg) { + BIT_SET(res, 5); + } + if (m_idr_full) { + BIT_SET(res, 4); + } + if (!m_irq) { + BIT_SET(res, 3); + } + return res; } -void hp98035_io_card::update_ibuffer(void) +WRITE8_MEMBER(hp98035_io_card::clear_np_irq_w) { - if (m_idr_full && !m_flg) { - // New input byte, put in ibuffer if it's a valid char (A-Z 0-9 = / \n) - if (m_idr == DELIM_CH_0 || m_idr == DELIM_CH_1) { - process_ibuffer(); - } else if (((m_idr >= 'A' && m_idr <= 'Z') || (m_idr >= '0' && m_idr <= '9') || m_idr == '=') && - m_ibuffer_ptr < HP98035_IBUFFER_LEN) { - m_ibuffer[ m_ibuffer_ptr++ ] = m_idr; - } - m_idr_full = false; - set_flg(true); + m_np_irq = false; + update_dc(); +} + +READ8_MEMBER(hp98035_io_card::clock_mux_r) +{ + // External input lines are always active (bits 7-4) + uint8_t res = 0xf0 | m_clock_mux; + + if (m_clock_mux == 4 && m_clock_segh) { + BIT_SET(res, 3); } + return res; } -void hp98035_io_card::process_ibuffer(void) +WRITE8_MEMBER(hp98035_io_card::set_irq_w) { - m_ibuffer[ m_ibuffer_ptr ] = '\0'; - const uint8_t *p = &m_ibuffer[ 0 ]; + m_irq = true; + update_irq(); +} - clear_obuffer(); +READ8_MEMBER(hp98035_io_card::clr_inten_r) +{ + m_intflag = false; + m_inten = false; + update_irq(); - bool get_out = false; + return 0xff; +} - while (*p != '\0' && !get_out) { - std::ostringstream out; - uint8_t datetime[ 5 ]; - unsigned unit_no; +WRITE8_MEMBER(hp98035_io_card::clr_inten_w) +{ + m_intflag = false; + m_inten = false; + update_irq(); +} - switch (*p++) { - case 'A': - // Halt all timer units - for (timer_unit_t& unit : m_units) { - unit.deactivate(); - } - m_inten = false; - m_intflag = false; - update_irq(); - break; - - case 'B': - // Warm reset - half_init(); - get_out = true; - break; - - case 'E': - // Read and clear errors - set_obuffer(m_error); - m_error = 0; - break; - - case 'F': - // Activate all timer units - for (timer_unit_t& unit : m_units) { - if (unit.m_port) { - unit.adv_state(true); - } - } - break; - - case 'R': - // Read time - // Assume US format of dates - util::stream_format(out , "%02u:%02u:%02u:%02u:%02u" , m_mon , m_dom , m_hrs , m_min , m_sec); - set_obuffer(out.str().c_str()); - break; - - case 'S': - // Set time - if (parse_datetime(p , datetime)) { - // Cannot set time when there's one or more active output units - if (std::any_of(std::begin(m_units) , std::end(m_units) , [](const timer_unit_t& u) { return u.m_state != UNIT_IDLE && !u.m_input; })) { - set_error(ERR_MASK_CANT_EXEC); - } else { - m_msec = 0; - m_sec = datetime[ 4 ]; - if (datetime[ 3 ] != EMPTY_FIELD) { - m_min = datetime[ 3 ]; - if (datetime[ 2 ] != EMPTY_FIELD) { - m_hrs = datetime[ 2 ]; - if (datetime[ 1 ] != EMPTY_FIELD) { - m_dom = datetime[ 1 ]; - if (datetime[ 0 ] != EMPTY_FIELD) { - m_mon = datetime[ 0 ]; - } - } - } - } - } - } else { - set_error(ERR_MASK_WRONG_INS); - get_out = true; - } - break; - - case 'T': - // Read triggered outputs - set_obuffer(m_triggered); - m_triggered = 0; - break; - - case 'U': - // Control timer units - if (parse_unit_no(p , unit_no)) { - get_out = parse_unit_command(p , unit_no); - } else { - set_error(ERR_MASK_WRONG_INS); - get_out = true; - } - break; - - case 'W': - // Read unserviced interrupts - set_obuffer(m_lost_irq); - m_lost_irq = 0; - break; - - default: - set_error(ERR_MASK_WRONG_INS); - get_out = true; - break; - } +WRITE8_MEMBER(hp98035_io_card::dc_w) +{ + if (data != m_dc) { + //LOG(("DC=%02x\n" , data)); + m_dc = data; + update_dc(); } +} + +void hp98035_io_card::half_init(void) +{ + m_inten = false; + m_intflag = false; + m_irq = false; + update_irq(); + m_np_irq = false; + update_dc(); - m_ibuffer_ptr = 0; + m_clock_1s_div = 0; + m_clock_state = CLOCK_OFF; + m_clock_mux = 0; + regen_clock_image(); } -bool hp98035_io_card::assign_unit(timer_unit_t& unit , const uint8_t*& p , bool input) +void hp98035_io_card::set_flg(bool value) { - unsigned port_no; + m_flg = value; + flg_w(m_flg); +} - if (parse_unit_no(p , port_no)) { - if (unit.m_state == UNIT_IDLE) { - unit.init(); +void hp98035_io_card::update_irq(void) +{ + if (!m_inten) { + m_irq = false; + } + irq_w(m_inten && m_irq); +} - if (std::any_of(std::begin(m_units) , std::end(m_units) , [input , port_no](const timer_unit_t& u) { return u.m_input == input && u.m_port == port_no; })) { - // I/O port already assigned - set_error(ERR_MASK_WRONG_UNIT); - return false; - } +void hp98035_io_card::update_dc(void) +{ + m_cpu->set_input_line(0 , m_np_irq && BIT(m_dc , HP_NANO_IE_DC)); +} - unit.m_input = input; - unit.m_port = port_no; - } else { - set_error(ERR_MASK_CANT_EXEC); - } - return false; +void hp98035_io_card::set_lhs_digits(unsigned v) +{ + if (v < 10) { + m_clock_segh = false; } else { - set_error(ERR_MASK_WRONG_INS); - return true; + v -= 10; + m_clock_segh = true; } + m_clock_digits[ 2 ] = dec_2_seven_segs[ v ]; } -bool hp98035_io_card::parse_unit_command(const uint8_t*& p, unsigned unit_no) +void hp98035_io_card::set_rhs_digits(unsigned v) { - unit_no--; - timer_unit_t& unit = m_units[ unit_no ]; - bool get_out = false; - unsigned msec; - uint8_t to_match[ 5 ]; - std::ostringstream out; + m_clock_digits[ 0 ] = dec_2_seven_segs[ v % 10 ]; + m_clock_digits[ 1 ] = dec_2_seven_segs[ v / 10 ]; +} - LOG(("U %c %u %p\n" , *p , unit_no , &unit)); +void hp98035_io_card::regen_clock_image(void) +{ + int tmp; + bool pm; + + switch (m_clock_state) { + case CLOCK_OFF: + m_clock_digits[ 0 ] = SEVEN_SEG_OFF; + m_clock_digits[ 1 ] = SEVEN_SEG_OFF; + m_clock_digits[ 2 ] = SEVEN_SEG_OFF; + m_clock_segh = false; + break; - switch (*p++) { - case '=': - // Assign unit - if (*p == 'I') { - p++; - get_out = assign_unit(unit , p , true); - } else if (*p == 'O') { - p++; - get_out = assign_unit(unit , p , false); + case CLOCK_HHMM: + tmp = get_clock_register(RTC_HOUR); + if (tmp == 0) { + tmp = 12; + } else if (tmp > 12) { + tmp -= 12; } + set_lhs_digits(tmp); + set_rhs_digits(get_clock_register(RTC_MINUTE)); break; - case 'C': - // Clear input unit - if (unit.m_input && unit.m_port) { - unit.m_value = 0; - } else { - set_error(ERR_MASK_WRONG_UNIT); - } + case CLOCK_SS: + m_clock_segh = false; + m_clock_digits[ 2 ] = SEVEN_SEG_OFF; + set_rhs_digits(get_clock_register(RTC_SECOND)); break; - case 'D': - // Set delay on output unit - if (parse_msec(p , msec)) { - if (!unit.m_input && unit.m_port) { - if (unit.m_state == UNIT_IDLE) { - unit.m_delay = msec; - } else { - set_error(ERR_MASK_CANT_EXEC); - } - } else { - set_error(ERR_MASK_WRONG_UNIT); - } - } else { - set_error(ERR_MASK_WRONG_INS); + case CLOCK_HH: + tmp = get_clock_register(RTC_HOUR); + pm = tmp >= 12; + if (tmp == 0) { + tmp = 12; + } else if (tmp > 12) { + tmp -= 12; } - get_out = true; + set_lhs_digits(tmp); + m_clock_digits[ 1 ] = SEVEN_SEG_OFF; + m_clock_digits[ 0 ] = pm ? SEVEN_SEG_P : SEVEN_SEG_A; break; - case 'G': - // Activate unit - if (unit.m_port && unit.m_state == UNIT_IDLE) { - unit.adv_state(true); + case CLOCK_MIN: + m_clock_segh = false; + m_clock_digits[ 2 ] = SEVEN_SEG_OFF; + set_rhs_digits(get_clock_register(RTC_MINUTE)); + break; - LOG(("act %p %d %d %u %02u:%02u:%02u:%02u %u %u %u\n" , &unit , unit.m_state , unit.m_input , unit.m_port , unit.m_match_datetime[ 0 ] , unit.m_match_datetime[ 1 ] , unit.m_match_datetime[ 2 ] , unit.m_match_datetime[ 3 ] , unit.m_delay , unit.m_period , unit.m_value)); - } else { - set_error(ERR_MASK_WRONG_UNIT); - } + case CLOCK_MON: + tmp = get_clock_register(RTC_MONTH); + set_lhs_digits(tmp); + m_clock_digits[ 0 ] = SEVEN_SEG_OFF; + m_clock_digits[ 1 ] = SEVEN_SEG_OFF; break; - case 'H': - // Halt unit - if (unit.m_port) { - unit.deactivate(); - } else { - set_error(ERR_MASK_WRONG_UNIT); + case CLOCK_DOM: + m_clock_segh = false; + m_clock_digits[ 2 ] = SEVEN_SEG_OFF; + set_rhs_digits(get_clock_register(RTC_DAY)); + break; + + default: + m_clock_state = CLOCK_OFF; + break; + } + logerror("St=%d segh=%d %02x:%02x:%02x\n" , m_clock_state , m_clock_segh , m_clock_digits[ 2 ] , + m_clock_digits[ 1 ] , m_clock_digits[ 0 ]); +} + +void hp98035_io_card::clock_short_press(void) +{ + logerror("Short press:%u\n" , m_clock_keys); + + bool regen = false; + int tmp; + + switch (m_clock_state) { + case CLOCK_OFF: + if (m_clock_keys == KEY_READ_MASK) { + m_clock_state = CLOCK_HHMM; + regen = true; + } else if (m_clock_keys == KEY_SET_MASK) { + m_clock_state = CLOCK_HH; + regen = true; } break; - case 'M': - // Set date/time to match on output unit - if (!unit.m_input && unit.m_port) { - if (unit.m_state == UNIT_IDLE) { - if (*p == '\0') { - unit.m_match_datetime[ 0 ] = EMPTY_FIELD; - unit.m_match_datetime[ 1 ] = EMPTY_FIELD; - unit.m_match_datetime[ 2 ] = EMPTY_FIELD; - unit.m_match_datetime[ 3 ] = EMPTY_FIELD; - } else if (parse_datetime(p , to_match) && *p == '\0') { - unit.m_match_datetime[ 0 ] = to_match[ 1 ]; - unit.m_match_datetime[ 1 ] = to_match[ 2 ]; - unit.m_match_datetime[ 2 ] = to_match[ 3 ]; - unit.m_match_datetime[ 3 ] = to_match[ 4 ]; - } else { - set_error(ERR_MASK_WRONG_INS); - } - } else { - set_error(ERR_MASK_CANT_EXEC); + case CLOCK_SS: + if (m_clock_keys == KEY_SET_MASK) { + m_clock_state = CLOCK_HH; + regen = true; + } else if (m_clock_keys == (KEY_CHG_MASK | KEY_READ_MASK)) { + tmp = get_clock_register(RTC_SECOND); + tmp++; + if (tmp >= 60) { + tmp = 0; } - } else { - set_error(ERR_MASK_WRONG_UNIT); + set_clock_register(RTC_SECOND , tmp); + log_current_time(); + //m_clock_1s_div = 0; + regen = true; } - get_out = true; break; - case 'P': - // Set period on output unit - if (parse_msec(p , msec)) { - if (!unit.m_input && unit.m_port) { - if (unit.m_state == UNIT_IDLE) { - unit.m_period = msec; - } else { - set_error(ERR_MASK_CANT_EXEC); - } - } else { - set_error(ERR_MASK_WRONG_UNIT); + case CLOCK_HH: + if (m_clock_keys == KEY_SET_MASK) { + m_clock_state = CLOCK_MIN; + regen = true; + } else if (m_clock_keys == (KEY_CHG_MASK | KEY_READ_MASK)) { + tmp = get_clock_register(RTC_HOUR); + tmp++; + if (tmp >= 24) { + tmp = 0; } - } else { - set_error(ERR_MASK_WRONG_INS); + set_clock_register(RTC_HOUR , tmp); + log_current_time(); + regen = true; } - get_out = true; break; - case 'V': - // Get value of input unit - if (unit.m_input && unit.m_port) { - util::stream_format(out , "%010u" , unit.m_value); - set_obuffer(out.str().c_str()); - } else { - set_error(ERR_MASK_WRONG_UNIT); + case CLOCK_MIN: + if (m_clock_keys == KEY_SET_MASK) { + m_clock_state = CLOCK_MON; + regen = true; + } else if (m_clock_keys == (KEY_CHG_MASK | KEY_READ_MASK)) { + tmp = get_clock_register(RTC_MINUTE); + tmp++; + if (tmp >= 60) { + tmp = 0; + } + set_clock_register(RTC_MINUTE , tmp); + set_clock_register(RTC_SECOND , 0); + //m_clock_1s_div = 0; + log_current_time(); + regen = true; } break; - } - - return get_out; -} -void hp98035_io_card::clear_obuffer(void) -{ - m_obuffer_len = 0; - m_obuffer_ptr = 0; -} - -void hp98035_io_card::set_obuffer(uint8_t b) -{ - m_obuffer[ 0 ] = b; - m_obuffer_len = 1; - m_obuffer_ptr = 0; -} - -void hp98035_io_card::set_obuffer(const char* s) -{ - unsigned s_len = std::min((unsigned)strlen(s) , (unsigned)(HP98035_OBUFFER_LEN - 1)); + case CLOCK_MON: + if (m_clock_keys == KEY_SET_MASK) { + m_clock_state = CLOCK_DOM; + regen = true; + } else if (m_clock_keys == (KEY_CHG_MASK | KEY_READ_MASK)) { + tmp = get_clock_register(RTC_MONTH); + tmp++; + if (tmp >= 13) { + tmp = 1; + } + set_clock_register(RTC_MONTH , tmp); + log_current_time(); + regen = true; + } + break; - memcpy(&m_obuffer[ 0 ] , s , s_len); - m_obuffer[ s_len++ ] = '\n'; - m_obuffer_len = s_len; - m_obuffer_ptr = 0; -} + case CLOCK_DOM: + if (m_clock_keys == KEY_SET_MASK) { + m_clock_state = CLOCK_OFF; + regen = true; + } else if (m_clock_keys == (KEY_CHG_MASK | KEY_READ_MASK)) { + tmp = get_clock_register(RTC_DAY); + tmp++; + if (tmp > gregorian_days_in_month(get_clock_register(RTC_MONTH) , 1)) { + tmp = 1; + } + set_clock_register(RTC_DAY , tmp); + log_current_time(); + regen = true; + } + break; -void hp98035_io_card::update_obuffer(void) -{ - if (!m_idr_full && !m_flg && m_obuffer_ptr < m_obuffer_len) { - m_odr = m_obuffer[ m_obuffer_ptr++ ]; - set_flg(true); + default: + break; } -} -void hp98035_io_card::set_error(uint8_t mask) -{ - m_error |= mask; + if (regen) { + regen_clock_image(); + } } -bool hp98035_io_card::parse_datetime(const uint8_t*& p, uint8_t *out) const +void hp98035_io_card::clock_long_press(void) { - unsigned n_fields = 0; + logerror("Long press:%u\n" , m_clock_keys); - // Fill all fields with EMPTY_FIELD - for (unsigned i = 0; i < 5; i++) { - out[ i ] = EMPTY_FIELD; - } + bool regen = false; - while (n_fields < 5 && *p >= '0' && *p <= '9') { - unsigned tmp = *p - '0'; - p++; - if (*p < '0' || *p > '9') { - return false; + switch (m_clock_state) { + case CLOCK_HHMM: + if (m_clock_keys == KEY_READ_MASK) { + m_clock_state = CLOCK_SS; + regen = true; } - // Shift all fields one position to the left - memmove(&out[ 0 ] , &out[ 1 ] , 4 * sizeof(out[ 0 ])); - // Put the new field in the last position - out[ 4 ] = (uint8_t)(tmp * 10 + *p - '0'); - n_fields++; - p++; - } - - if (n_fields == 0) { - return false; - } + break; - // Seconds - if (out[ 4 ] >= 60) { - return false; - } - if (n_fields == 1) { - return true; - } - // Minutes - if (out[ 3 ] >= 60) { - return false; - } - if (n_fields == 2) { - return true; - } - // Hours - if (out[ 2 ] >= 24) { - return false; - } - if (n_fields == 3) { - return true; - } - // Month - uint8_t month; - if (n_fields == 4) { - month = m_mon; - } else { - if (out[ 0 ] < 1 || out[ 0 ] > 12) { - return false; + case CLOCK_SS: + case CLOCK_HH: + case CLOCK_MIN: + case CLOCK_MON: + case CLOCK_DOM: + if (m_clock_keys == KEY_SET_MASK) { + m_clock_state = CLOCK_OFF; + regen = true; } - month = out[ 0 ]; - } - // Day of month - // Use year 0 here to allow for February 29th - if (out[ 1 ] < 1 || out[ 1 ] > gregorian_days_in_month(month , 0)) { - return false; - } - return true; -} + break; -bool hp98035_io_card::parse_unit_no(const uint8_t*& p, unsigned& unit) const -{ - if (*p < '1' || *p > '0' + HP98035_UNIT_COUNT) { - return false; + default: + break; } - unit = *p - '0'; - do { - p++; - } while (*p >= '0' && *p <= '9'); - - return true; + if (regen) { + regen_clock_image(); + } } -bool hp98035_io_card::parse_msec(const uint8_t*& p, unsigned& msec) const +void hp98035_io_card::log_current_time(void) { - msec = 0; - - while (*p >= '0' && *p <= '9') { - msec = msec * 10 + *p - '0'; - p++; - } - - return *p == '\0'; + logerror("Time = %d:%d:%d:%d:%d\n" , get_clock_register(RTC_MONTH) , get_clock_register(RTC_DAY) , get_clock_register(RTC_HOUR) , get_clock_register(RTC_MINUTE) , get_clock_register(RTC_SECOND)); } -void hp98035_io_card::timer_unit_t::init(void) +void hp98035_io_card::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) { - m_state = UNIT_IDLE; - m_port = 0; - m_match_datetime[ 0 ] = EMPTY_FIELD; - m_match_datetime[ 1 ] = EMPTY_FIELD; - m_match_datetime[ 2 ] = EMPTY_FIELD; - m_match_datetime[ 3 ] = EMPTY_FIELD; - m_delay = 0; - m_period = 0; - m_value = 0; + // Do nothing, time is kept in "device_rtc_interface" registers } -void hp98035_io_card::timer_unit_t::deactivate(void) +ROM_START(hp98035) + ROM_REGION(0x800 , "np" , 0) + ROM_LOAD("1818-0469.bin" , 0 , 0x800 , CRC(e16ab3bc) SHA1(34e89a37a2822f27af21969941201317dbff615b)) +ROM_END + +static ADDRESS_MAP_START(np_program_map , AS_PROGRAM , 8 , hp98035_io_card) +ADDRESS_MAP_UNMAP_HIGH +AM_RANGE(0x000 , 0x7ff) AM_ROM AM_REGION("np" , 0) +ADDRESS_MAP_END + +static ADDRESS_MAP_START(np_io_map , AS_IO , 8 , hp98035_io_card) +ADDRESS_MAP_UNMAP_HIGH +AM_RANGE(0x0 , 0x0) AM_WRITE(ram_addr_w) +AM_RANGE(0x1 , 0x1) AM_READ(ram_data_r) +AM_RANGE(0x2 , 0x2) AM_WRITE(ram_addr_data_w) +AM_RANGE(0x3 , 0x3) AM_WRITE(ram_data_w) + AM_RANGE(0x5 , 0x5) AM_WRITE(clock_key_w) + AM_RANGE(0x7 , 0x7) AM_READ(clock_digit_r) +AM_RANGE(0x8 , 0x8) AM_WRITE(odr_w) +AM_RANGE(0x9 , 0x9) AM_READ(idr_r) +AM_RANGE(0xa , 0xa) AM_READ(np_status_r) + AM_RANGE(0xb , 0xb) AM_WRITE(clear_np_irq_w) +AM_RANGE(0xc , 0xc) AM_READ(clock_mux_r) + AM_RANGE(0xd , 0xd) AM_WRITE(set_irq_w) + AM_RANGE(0xe , 0xe) AM_READWRITE(clr_inten_r , clr_inten_w) +ADDRESS_MAP_END + +static MACHINE_CONFIG_FRAGMENT(hp98035) +MCFG_CPU_ADD("np" , HP_NANOPROCESSOR , XTAL_1MHz) +MCFG_CPU_PROGRAM_MAP(np_program_map) +MCFG_CPU_IO_MAP(np_io_map) +MCFG_HP_NANO_DC_CHANGED(WRITE8(hp98035_io_card , dc_w)) +MACHINE_CONFIG_END + +const tiny_rom_entry *hp98035_io_card::device_rom_region() const { - m_state = UNIT_IDLE; + return ROM_NAME(hp98035); } -void hp98035_io_card::timer_unit_t::adv_state(bool reset) +machine_config_constructor hp98035_io_card::device_mconfig_additions() const { - if (reset) { - m_state = UNIT_IDLE; - } - - if (m_input) { - m_state = UNIT_ACTIVE; - m_value = 0; - } else { - if (m_state == UNIT_IDLE) { - m_state = UNIT_ACTIVE; - if (m_match_datetime[ 3 ] != EMPTY_FIELD) { - // Seconds field is not empty -> there's a date/time to be matched first - return; - } - } - if (m_state == UNIT_ACTIVE) { - m_value = m_delay; - } else { - m_value = m_period; - } - m_state = UNIT_WAIT_FOR_TO; - } + return MACHINE_CONFIG_NAME(hp98035); } // device type definition diff --git a/src/devices/bus/hp9845_io/98035.h b/src/devices/bus/hp9845_io/98035.h index d684b62dedf..e0b034c80ea 100644 --- a/src/devices/bus/hp9845_io/98035.h +++ b/src/devices/bus/hp9845_io/98035.h @@ -14,12 +14,11 @@ #define _98035_H_ #include "hp9845_io.h" +#include "cpu/nanoprocessor/nanoprocessor.h" +#include "dirtc.h" -#define HP98035_IBUFFER_LEN 16 // Totally arbitrary -#define HP98035_OBUFFER_LEN 16 // Totally arbitrary -#define HP98035_UNIT_COUNT 4 // Count of counter/timer units - -class hp98035_io_card : public hp9845_io_card_device +class hp98035_io_card : public hp9845_io_card_device, + public device_rtc_interface { public: // construction/destruction @@ -31,11 +30,48 @@ public: virtual void device_start() override; virtual void device_reset() override; virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + virtual const tiny_rom_entry *device_rom_region() const override; + virtual machine_config_constructor device_mconfig_additions() const override; DECLARE_READ16_MEMBER(reg_r); DECLARE_WRITE16_MEMBER(reg_w); + DECLARE_WRITE8_MEMBER(ram_addr_w); + DECLARE_READ8_MEMBER(ram_data_r); + DECLARE_WRITE8_MEMBER(ram_addr_data_w); + DECLARE_WRITE8_MEMBER(ram_data_w); + + DECLARE_WRITE8_MEMBER(clock_key_w); + DECLARE_READ8_MEMBER(clock_digit_r); + + DECLARE_WRITE8_MEMBER(odr_w); + DECLARE_READ8_MEMBER(idr_r); + DECLARE_READ8_MEMBER(np_status_r); + DECLARE_WRITE8_MEMBER(clear_np_irq_w); + DECLARE_READ8_MEMBER(clock_mux_r); + DECLARE_WRITE8_MEMBER(set_irq_w); + DECLARE_READ8_MEMBER(clr_inten_r); + DECLARE_WRITE8_MEMBER(clr_inten_w); + + DECLARE_WRITE8_MEMBER(dc_w); + private: + required_device<hp_nanoprocessor_device> m_cpu; + + // Internal RAM & I/F + uint8_t m_np_ram[ 256 ]; + uint8_t m_ram_addr; + uint8_t m_ram_data_in; + + // DC lines + uint8_t m_dc; + + // NP interrupt + bool m_np_irq; + + // Periodic interrupt + emu_timer *m_msec_timer; + // Interface state bool m_flg; bool m_inten; @@ -44,63 +80,40 @@ private: bool m_idr_full; uint8_t m_idr; // Input Data Register uint8_t m_odr; // Output Data Register - uint8_t m_error; - uint8_t m_triggered; - uint8_t m_lost_irq; - uint8_t m_ibuffer[ HP98035_IBUFFER_LEN + 1 ]; - unsigned m_ibuffer_ptr; - uint8_t m_obuffer[ HP98035_OBUFFER_LEN ]; - unsigned m_obuffer_len; - unsigned m_obuffer_ptr; - - // Clock/timer state - unsigned m_msec; // Milliseconds - uint8_t m_sec; // Seconds - uint8_t m_min; // Minutes - uint8_t m_hrs; // Hours - uint8_t m_dom; // Day of month - uint8_t m_mon; // Month - // Strangely enough this RTC has no notion of current year - emu_timer *m_msec_timer; - // Timer units + // Clock chip emulation typedef enum { - UNIT_IDLE, // Not active - UNIT_ACTIVE, // Active (output units: waiting for date/time match) - UNIT_WAIT_FOR_TO // Active, output units only: waiting for timeout - } unit_state_t; - - typedef struct { - unit_state_t m_state; // State - bool m_input; // Input or output - uint8_t m_port; // Assigned port # (0 if not assigned) - uint8_t m_match_datetime[ 4 ]; // Date&time to match (month is not included) - unsigned m_delay; // Timer delay - unsigned m_period; // Timer period (when != 0) - unsigned m_value; // Current counter value - - void init(void); - void deactivate(void); - void adv_state(bool reset = false); - } timer_unit_t; - - timer_unit_t m_units[ HP98035_UNIT_COUNT ]; + CLOCK_OFF, // Display OFF + CLOCK_HHMM, // Show HH:mm + CLOCK_SS, // Show :SS + CLOCK_HH, // Show HH: A/P + CLOCK_MIN, // Show :mm + CLOCK_MON, // Show MM: + CLOCK_DOM, // Show :DD + } clock_state_t; + + emu_timer *m_clock_timer; + unsigned m_clock_1s_div; + clock_state_t m_clock_state; + uint8_t m_clock_digits[ 3 ]; + uint8_t m_clock_mux; + bool m_clock_segh; + uint8_t m_clock_keys; + uint8_t m_prev_clock_keys; + unsigned m_clock_key_cnt; void half_init(void); void set_flg(bool value); void update_irq(void); - void update_ibuffer(void); - void process_ibuffer(void); - bool assign_unit(timer_unit_t& unit , const uint8_t*& p , bool input); - bool parse_unit_command(const uint8_t*& p, unsigned unit_no); - void clear_obuffer(void); - void set_obuffer(uint8_t b); - void set_obuffer(const char* s); - void update_obuffer(void); - void set_error(uint8_t mask); - bool parse_datetime(const uint8_t*& p, uint8_t *out) const; - bool parse_unit_no(const uint8_t*& p, unsigned& unit) const; - bool parse_msec(const uint8_t*& p, unsigned& msec) const; + void update_dc(void); + + void set_lhs_digits(unsigned v); + void set_rhs_digits(unsigned v); + void regen_clock_image(void); + void clock_short_press(void); + void clock_long_press(void); + void log_current_time(void); + virtual void rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) override; }; // device type definition |