summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--src/devices/bus/hp9845_io/98035.cpp985
-rw-r--r--src/devices/bus/hp9845_io/98035.h125
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