summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author R. Belmont <rb6502@users.noreply.github.com>2016-11-05 14:22:46 -0400
committer GitHub <noreply@github.com>2016-11-05 14:22:46 -0400
commitf05e7ffad0fce71293f65d142b7c13084ad48215 (patch)
tree197bc76b15349e8cc3d3be04e6f070ad4aa7a639
parentaff3588a0458ec6d538375f5a235c82a934020ad (diff)
parentf60621bbd443493b0e24aad355482ec77f781a9c (diff)
Merge pull request #1637 from fulivi/hp9845_dev7
Hp9845: re-implemented 98035 module with a nanoprocessor driver
-rw-r--r--scripts/src/cpu.lua16
-rw-r--r--scripts/target/mame/mess.lua1
-rw-r--r--src/devices/bus/hp9845_io/98035.cpp1031
-rw-r--r--src/devices/bus/hp9845_io/98035.h125
-rw-r--r--src/devices/cpu/nanoprocessor/nanoprocessor.cpp525
-rw-r--r--src/devices/cpu/nanoprocessor/nanoprocessor.h138
-rw-r--r--src/devices/cpu/nanoprocessor/nanoprocessor_dasm.cpp135
-rw-r--r--src/mame/drivers/hp9845.cpp1
-rw-r--r--src/tools/unidasm.cpp2
9 files changed, 1399 insertions, 575 deletions
diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua
index c472fd4d8e0..c9639d1d7a1 100644
--- a/scripts/src/cpu.lua
+++ b/scripts/src/cpu.lua
@@ -704,6 +704,22 @@ if (CPUS["HPHYBRID"]~=null or _OPTIONS["with-tools"]) then
end
--------------------------------------------------
+-- HP Nanoprocessor
+--@src/devices/cpu/nanoprocessor/nanoprocessor.h,CPUS["NANOPROCESSOR"] = true
+--------------------------------------------------
+
+if (CPUS["NANOPROCESSOR"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/cpu/nanoprocessor/nanoprocessor.cpp",
+ MAME_DIR .. "src/devices/cpu/nanoprocessor/nanoprocessor.h",
+ }
+end
+
+if (CPUS["NANOPROCESSOR"]~=null or _OPTIONS["with-tools"]) then
+ table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/nanoprocessor/nanoprocessor_dasm.cpp")
+end
+
+--------------------------------------------------
-- Hudsonsoft 6280
--@src/devices/cpu/h6280/h6280.h,CPUS["H6280"] = true
--------------------------------------------------
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index a9607474e80..fe26b9885c8 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -133,6 +133,7 @@ CPUS["MELPS4"] = true
CPUS["HPHYBRID"] = true
CPUS["SM510"] = true
CPUS["MB86901"] = true
+CPUS["NANOPROCESSOR"] = true
--------------------------------------------------
-- specify available sound cores; some of these are
diff --git a/src/devices/bus/hp9845_io/98035.cpp b/src/devices/bus/hp9845_io/98035.cpp
index c3338add89a..28004e6d04f 100644
--- a/src/devices/bus/hp9845_io/98035.cpp
+++ b/src/devices/bus/hp9845_io/98035.cpp
@@ -26,19 +26,53 @@
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
+ Here's what I know about the clock chip.
+ * Packaged in a 24-pin DIP
+ * Runs on a 2.4V NiCd battery
+ * Keeps time with a standard 32.768 kHz oscillator
+ * Drives a 3 1/2 digit LED display with 7-segment digits
+ * Multiplexes digits with a 32768 Hz / 64 clock
+ * Has 3 buttons to read/set time (READ, SET & CHG)
+ * Counts month, day, hour, minute & seconds (no year)
+ * Doesn't support leap years
+ * On Tony Duell's schematics the chip is marked "AC5954"
+
+ All my attempts to find something like a datasheet of this chip
+ failed.
+
+ This driver emulates the clock chip with a FSM that reacts to
+ "short" and "long" pressings of keys. Here's a summary of the FSM.
+
+ | State | Key pressed | New state | Display |
+ |-------+-------------+-----------+--------------------------------|
+ | OFF | | | Blank, no multiplexing |
+ | | Short READ | HHMM | |
+ | | Short SET | HH | |
+ | HHMM | | | "HH:mm" (Hours 1-12 and mins.) |
+ | | | | On real chip it probably |
+ | | | | returns to OFF after a couple |
+ | | | | of seconds. |
+ | | Long READ | SS | |
+ | SS | | | " :SS" (just seconds) |
+ | | Short SET | HH | |
+ | | Long SET | OFF | |
+ | | READ + CHG | | seconds++ |
+ | HH | | | "HH: A/P" (hours & AM/PM) |
+ | | Short SET | MIN | |
+ | | Long SET | OFF | |
+ | | READ + CHG | | hours++ |
+ | MIN | | | " :mm" (just minutes) |
+ | | Short SET | MON | |
+ | | Long SET | OFF | |
+ | | READ + CHG | | minutes++ |
+ | MON | | | "MM: " (just month) |
+ | | Short SET | DOM | |
+ | | Long SET | OFF | |
+ | | READ + CHG | | month++ |
+ | DOM | | | " :DD" (just day of month) |
+ | | Short SET | OFF | |
+ | | Long SET | OFF | |
+ | | READ + CHG | | day++ |
The main reference for this module is this manual:
HP, 98035A Real Time Clock Installation and Operation Manual
@@ -58,26 +92,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 +163,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 +196,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 +262,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 +274,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 +295,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 +312,458 @@ 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;
+}
+
+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;
+}
- for (timer_unit_t& unit : m_units) {
- unit.init();
+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;
+ }
+ LOG(("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)
+{
+ LOG(("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) , 0)) {
+ 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;
+ LOG(("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';
+ LOG(("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
diff --git a/src/devices/cpu/nanoprocessor/nanoprocessor.cpp b/src/devices/cpu/nanoprocessor/nanoprocessor.cpp
new file mode 100644
index 00000000000..273f73f2c48
--- /dev/null
+++ b/src/devices/cpu/nanoprocessor/nanoprocessor.cpp
@@ -0,0 +1,525 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+
+#include "emu.h"
+#include "debugger.h"
+#include "nanoprocessor.h"
+
+// Index of state variables
+enum {
+ NANO_REG_A,
+ NANO_REG_R0,
+ NANO_REG_R1,
+ NANO_REG_R2,
+ NANO_REG_R3,
+ NANO_REG_R4,
+ NANO_REG_R5,
+ NANO_REG_R6,
+ NANO_REG_R7,
+ NANO_REG_R8,
+ NANO_REG_R9,
+ NANO_REG_R10,
+ NANO_REG_R11,
+ NANO_REG_R12,
+ NANO_REG_R13,
+ NANO_REG_R14,
+ NANO_REG_R15,
+ NANO_REG_PA,
+ NANO_REG_SSR,
+ NANO_REG_ISR,
+ NANO_REG_FLAGS
+};
+
+#define BIT_MASK(n) (1U << (n))
+
+// Macros to clear/set single bits
+#define BIT_CLR(w , n) ((w) &= ~BIT_MASK(n))
+#define BIT_SET(w , n) ((w) |= BIT_MASK(n))
+
+// Bits in m_flags
+#define NANO_DC0_BIT 0 // DC0
+#define NANO_E_BIT (NANO_DC0_BIT + HP_NANO_DC_NO) // Extend flag
+#define NANO_I_BIT (NANO_E_BIT + 1) // Interrupt flag
+
+const device_type HP_NANOPROCESSOR = &device_creator<hp_nanoprocessor_device>;
+
+hp_nanoprocessor_device::hp_nanoprocessor_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ cpu_device(mconfig , HP_NANOPROCESSOR , "HP-Nanoprocessor" , tag , owner , clock , "nanoprocessor" , __FILE__),
+ m_dc_changed_func(*this),
+ m_read_dc_func(*this),
+ m_program_config("program" , ENDIANNESS_BIG , 8 , 11),
+ m_io_config("io" , ENDIANNESS_BIG , 8 , 4)
+{
+}
+
+void hp_nanoprocessor_device::device_start()
+{
+ state_add(NANO_REG_A , "A" , m_reg_A);
+ state_add(NANO_REG_R0 , "R0" , m_reg_R[ 0 ]);
+ state_add(NANO_REG_R1 , "R1" , m_reg_R[ 1 ]);
+ state_add(NANO_REG_R2 , "R2" , m_reg_R[ 2 ]);
+ state_add(NANO_REG_R3 , "R3" , m_reg_R[ 3 ]);
+ state_add(NANO_REG_R4 , "R4" , m_reg_R[ 4 ]);
+ state_add(NANO_REG_R5 , "R5" , m_reg_R[ 5 ]);
+ state_add(NANO_REG_R6 , "R6" , m_reg_R[ 6 ]);
+ state_add(NANO_REG_R7 , "R7" , m_reg_R[ 7 ]);
+ state_add(NANO_REG_R8 , "R8" , m_reg_R[ 8 ]);
+ state_add(NANO_REG_R9 , "R9" , m_reg_R[ 9 ]);
+ state_add(NANO_REG_R10 , "R10" , m_reg_R[ 10 ]);
+ state_add(NANO_REG_R11 , "R11" , m_reg_R[ 11 ]);
+ state_add(NANO_REG_R12 , "R12" , m_reg_R[ 12 ]);
+ state_add(NANO_REG_R13 , "R13" , m_reg_R[ 13 ]);
+ state_add(NANO_REG_R14 , "R14" , m_reg_R[ 14 ]);
+ state_add(NANO_REG_R15 , "R15" , m_reg_R[ 15 ]);
+ state_add(NANO_REG_PA , "PA" , m_reg_PA).formatstr("%03X");
+ state_add(STATE_GENPC , "GENPC" , m_reg_PA).noshow();
+ state_add(STATE_GENPCBASE , "GENPCBASE" , m_reg_PA).noshow();
+ state_add(NANO_REG_SSR , "SSR" , m_reg_SSR).formatstr("%03X");
+ state_add(NANO_REG_ISR , "ISR" , m_reg_ISR).formatstr("%03X");
+ state_add(STATE_GENFLAGS , "GENFLAGS" , m_flags).noshow().formatstr("%10s");
+
+ m_program = &space(AS_PROGRAM);
+ m_direct = &m_program->direct();
+ m_io = &space(AS_IO);
+
+ save_item(NAME(m_reg_A));
+ save_item(NAME(m_reg_R));
+ save_item(NAME(m_reg_PA));
+ save_item(NAME(m_reg_SSR));
+ save_item(NAME(m_reg_ISR));
+ save_item(NAME(m_flags));
+
+ m_icountptr = &m_icount;
+
+ m_dc_changed_func.resolve_safe();
+ m_read_dc_func.resolve_safe(0xff);
+}
+
+void hp_nanoprocessor_device::device_reset()
+{
+ m_reg_A = 0;
+ for (auto& reg : m_reg_R) {
+ reg = 0;
+ }
+ m_reg_PA = 0;
+ m_reg_SSR = 0;
+ m_reg_ISR = 0;
+ m_flags = 0;
+ dc_update();
+}
+
+void hp_nanoprocessor_device::execute_run()
+{
+ do {
+ // Check for interrupts (interrupt line is always enabled. Masking is done
+ // outside of the NP, usually by ANDing the DC7 line with the interrupt
+ // request signal)
+ if (BIT(m_flags , NANO_I_BIT)) {
+ m_reg_ISR = m_reg_PA;
+ m_reg_PA = (uint16_t)(standard_irq_callback(0) & 0xff);
+ dc_clr(HP_NANO_IE_DC);
+ // Vector fetching takes 1 cycle
+ m_icount -= 1;
+ } else {
+ debugger_instruction_hook(this , m_reg_PA);
+
+ uint8_t opcode = fetch();
+ execute_one(opcode);
+ // All opcodes execute in 2 cycles
+ m_icount -= 2;
+ }
+ } while (m_icount > 0);
+}
+
+void hp_nanoprocessor_device::execute_set_input(int linenum, int state)
+{
+ if (linenum == 0) {
+ if (state) {
+ BIT_SET(m_flags, NANO_I_BIT);
+ } else {
+ BIT_CLR(m_flags, NANO_I_BIT);
+ }
+ }
+}
+
+void hp_nanoprocessor_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
+ if (entry.index() == STATE_GENFLAGS) {
+ // DC7 is reported as "I" because it is usually used as interrupt enable
+ str = string_format("%c %c%c%c%c%c%c%c%c" , BIT(m_flags , NANO_E_BIT) ? 'E' : ' ',
+ BIT(m_flags , NANO_DC0_BIT + 7) ? 'I' : ' ',
+ BIT(m_flags , NANO_DC0_BIT + 6) ? '6' : ' ',
+ BIT(m_flags , NANO_DC0_BIT + 5) ? '5' : ' ',
+ BIT(m_flags , NANO_DC0_BIT + 4) ? '4' : ' ',
+ BIT(m_flags , NANO_DC0_BIT + 3) ? '3' : ' ',
+ BIT(m_flags , NANO_DC0_BIT + 2) ? '2' : ' ',
+ BIT(m_flags , NANO_DC0_BIT + 1) ? '1' : ' ',
+ BIT(m_flags , NANO_DC0_BIT + 0) ? '0' : ' ');
+ }
+
+}
+
+offs_t hp_nanoprocessor_device::disasm_disassemble(char *buffer, offs_t pc, const uint8_t *oprom, const uint8_t *opram, uint32_t options)
+{
+ extern CPU_DISASSEMBLE(hp_nanoprocessor);
+ return CPU_DISASSEMBLE_NAME(hp_nanoprocessor)(this , buffer , pc , oprom , opram , options);
+}
+
+void hp_nanoprocessor_device::execute_one(uint8_t opcode)
+{
+ // Instructions without mask
+ switch (opcode) {
+ case 0x00:
+ // INB
+ m_reg_A++;
+ if (m_reg_A == 0) {
+ BIT_SET(m_flags, NANO_E_BIT);
+ }
+ break;
+
+ case 0x01:
+ // DEB
+ m_reg_A--;
+ if (m_reg_A == 0xff) {
+ BIT_SET(m_flags, NANO_E_BIT);
+ }
+ break;
+
+ case 0x02:
+ // IND
+ // Handling of non-decimal digits is entirely arbitrary
+ m_reg_A++;
+ if ((m_reg_A & 0x0f) >= 10) {
+ m_reg_A += 6;
+ if (m_reg_A >= 0xa0) {
+ m_reg_A += 0x60;
+ BIT_SET(m_flags, NANO_E_BIT);
+ }
+ }
+ break;
+
+ case 0x03:
+ // DED
+ // Handling of non-decimal digits is entirely arbitrary
+ m_reg_A--;
+ if ((m_reg_A & 0x0f) >= 10) {
+ m_reg_A -= 6;
+ if (m_reg_A >= 0xa0) {
+ m_reg_A -= 0x60;
+ BIT_SET(m_flags, NANO_E_BIT);
+ }
+ }
+ break;
+
+ case 0x04:
+ // CLA
+ m_reg_A = 0;
+ break;
+
+ case 0x05:
+ // CMA
+ m_reg_A = ~m_reg_A;
+ break;
+
+ case 0x06:
+ // RSA
+ m_reg_A >>= 1;
+ break;
+
+ case 0x07:
+ // LSA
+ m_reg_A <<= 1;
+ break;
+
+ case 0x08:
+ // SGT
+ if (m_reg_A > m_reg_R[ 0 ]) {
+ skip();
+ }
+ break;
+
+ case 0x09:
+ // SLT
+ if (m_reg_A < m_reg_R[ 0 ]) {
+ skip();
+ }
+ break;
+
+ case 0x0a:
+ // SEQ
+ if (m_reg_A == m_reg_R[ 0 ]) {
+ skip();
+ }
+ break;
+
+ case 0x0b:
+ // SAZ
+ if (m_reg_A == 0) {
+ skip();
+ }
+ break;
+
+ case 0x0c:
+ // SLE
+ if (m_reg_A <= m_reg_R[ 0 ]) {
+ skip();
+ }
+ break;
+
+ case 0x0d:
+ // SGE
+ if (m_reg_A >= m_reg_R[ 0 ]) {
+ skip();
+ }
+ break;
+
+ case 0x0e:
+ // SNE
+ if (m_reg_A != m_reg_R[ 0 ]) {
+ skip();
+ }
+ break;
+
+ case 0x0f:
+ // SAN
+ if (m_reg_A != 0) {
+ skip();
+ }
+ break;
+
+ case 0x1f:
+ // SES
+ if (BIT(m_flags , NANO_E_BIT)) {
+ skip();
+ }
+ break;
+
+ case 0x3f:
+ // SEZ
+ if (!BIT(m_flags , NANO_E_BIT)) {
+ skip();
+ }
+ break;
+
+ case 0x5f:
+ // NOP
+ break;
+
+ case 0xb1:
+ // RTE
+ dc_set(HP_NANO_IE_DC);
+ // Intentional fall-through to RTI!
+
+ case 0xb0:
+ // RTI
+ m_reg_PA = m_reg_ISR;
+ break;
+
+ case 0xb4:
+ // STE
+ BIT_SET(m_flags, NANO_E_BIT);
+ break;
+
+ case 0xb5:
+ // CLE
+ BIT_CLR(m_flags, NANO_E_BIT);
+ break;
+
+ case 0xb9:
+ // RSE
+ dc_set(HP_NANO_IE_DC);
+ // Intentional fall-through to RTS!
+
+ case 0xb8:
+ // RTS
+ {
+ uint16_t tmp = m_reg_SSR;
+ m_reg_SSR = pa_offset(1);
+ m_reg_PA = tmp;
+ }
+ break;
+
+ case 0xcf:
+ // LDR
+ m_reg_A = fetch();
+ break;
+
+ default:
+ // Instructions with 0xf8 mask
+ switch (opcode & 0xf8) {
+ case 0x10:
+ // SBS
+ if (BIT(m_reg_A , opcode & 7)) {
+ skip();
+ }
+ break;
+
+ case 0x18:
+ // SFS
+ {
+ uint8_t tmp = m_read_dc_func();
+ tmp &= (uint8_t)(m_flags >> NANO_DC0_BIT);
+ if (BIT(tmp , opcode & 7)) {
+ skip();
+ }
+ }
+ break;
+
+ case 0x20:
+ // SBN
+ BIT_SET(m_reg_A, opcode & 7);
+ break;
+
+ case 0x28:
+ // STC
+ dc_set(opcode & 7);
+ break;
+
+ case 0x30:
+ // SBZ
+ if (!BIT(m_reg_A , opcode & 7)) {
+ skip();
+ }
+ break;
+
+ case 0x38:
+ // SFZ
+ {
+ uint8_t tmp = m_read_dc_func();
+ tmp &= (uint8_t)(m_flags >> NANO_DC0_BIT);
+ if (!BIT(tmp , opcode & 7)) {
+ skip();
+ }
+ }
+ break;
+
+ case 0x80:
+ // JMP
+ m_reg_PA = ((uint16_t)(opcode & 7) << 8) | fetch();
+ break;
+
+ case 0x88:
+ // JSB
+ {
+ uint16_t tmp = ((uint16_t)(opcode & 7) << 8) | fetch();
+ m_reg_SSR = m_reg_PA;
+ m_reg_PA = tmp;
+ }
+ break;
+
+ case 0x98:
+ // JAS
+ m_reg_SSR = pa_offset(1);
+ // Intentional fall-through to JAI!
+
+ case 0x90:
+ // JAI
+ // On HP doc there's a mysterious warning about JAI:
+ // "Due to the indexing structure, a JAI instruction executed with
+ // R03 set will be executed as a JAS instruction"
+ // My idea on the meaning: NP recycles the instruction register to form
+ // the bitwise OR of bits 3-0 of R0 and of opcode (see LDI/STI
+ // instructions). Presumably this was done to save on flip-flop count.
+ // So, if bit 3 of R0 (R03) is set when executing JAI the instruction
+ // register turns JAI into JAS.
+ // This effect is not simulated here at the moment.
+ {
+ uint16_t tmp = (uint16_t)((m_reg_R[ 0 ] | opcode) & 7) << 8;
+ m_reg_PA = tmp | m_reg_A;
+ }
+ break;
+
+ case 0xa0:
+ // CBN
+ BIT_CLR(m_reg_A, opcode & 7);
+ break;
+
+ case 0xa8:
+ // CLC
+ dc_clr(opcode & 7);
+ break;
+
+ default:
+ // Instructions with 0xf0 mask
+ switch (opcode & 0xf0) {
+ case 0x40:
+ // INA
+ m_reg_A = m_io->read_byte(opcode & 0xf);
+ break;
+
+ case 0x50:
+ // OTA
+ m_io->write_byte(opcode & 0xf , m_reg_A);
+ break;
+
+ case 0x60:
+ // LDA
+ m_reg_A = m_reg_R[ opcode & 0xf ];
+ break;
+
+ case 0x70:
+ // STA
+ m_reg_R[ opcode & 0xf ] = m_reg_A;
+ break;
+
+ case 0xc0:
+ // OTR
+ m_io->write_byte(opcode & 0xf , fetch());
+ break;
+
+ case 0xd0:
+ // STR
+ m_reg_R[ opcode & 0xf ] = fetch();
+ break;
+
+ case 0xe0:
+ // LDI
+ m_reg_A = m_reg_R[ (m_reg_R[ 0 ] | opcode) & 0xf ];
+ break;
+
+ case 0xf0:
+ // STI
+ m_reg_R[ (m_reg_R[ 0 ] | opcode) & 0xf ] = m_reg_A;
+ break;
+
+ default:
+ logerror("Unknown opcode %02x @ 0x03x\n" , opcode , m_reg_PA);
+ break;
+ }
+ }
+ }
+}
+
+uint16_t hp_nanoprocessor_device::pa_offset(unsigned off) const
+{
+ return (uint16_t)((m_reg_PA + off) & HP_NANO_PC_MASK);
+}
+
+uint8_t hp_nanoprocessor_device::fetch(void)
+{
+ uint8_t res = m_direct->read_byte(m_reg_PA);
+ m_reg_PA = pa_offset(1);
+ return res;
+}
+
+void hp_nanoprocessor_device::skip(void)
+{
+ m_reg_PA = pa_offset(2);
+}
+
+void hp_nanoprocessor_device::dc_update(void)
+{
+ m_dc_changed_func((uint8_t)(m_flags & ((1U << HP_NANO_DC_NO) - 1)));
+}
+
+void hp_nanoprocessor_device::dc_set(unsigned bit_no)
+{
+ BIT_SET(m_flags, NANO_DC0_BIT + bit_no);
+ dc_update();
+}
+
+void hp_nanoprocessor_device::dc_clr(unsigned bit_no)
+{
+ BIT_CLR(m_flags, NANO_DC0_BIT + bit_no);
+ dc_update();
+}
diff --git a/src/devices/cpu/nanoprocessor/nanoprocessor.h b/src/devices/cpu/nanoprocessor/nanoprocessor.h
new file mode 100644
index 00000000000..9193e1b5302
--- /dev/null
+++ b/src/devices/cpu/nanoprocessor/nanoprocessor.h
@@ -0,0 +1,138 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+//
+// *****************************
+// Emulator for HP nanoprocessor
+// *****************************
+//
+// Nanoprocessor is a very simple microcontroller developed by HP in 1974 for
+// its own internal use. It's used, for example, in HP9845 built-in printer, in
+// 98034 & 98035 modules, etc.
+// Here's a summary of its features:
+// * 8-bit word size
+// * 1 Accumulator
+// * 16 general purpose registers
+// * 11-bit Program Counter
+// * 2048 bytes of program space
+// * 16 input 8-bit ports (DS0-DS15)
+// * 15 output 8-bit ports (DS0-DS14)
+// * 8 "direct control" I/O lines that can be individually tested/set/cleared
+// * 1 interrupt input with 8-bit vector
+// * One level nesting of subroutine call through a 11-bit subroutine stack register (SSR)
+// * One level nesting of interruption through a 11-bit interrupt stack register (ISR)
+// * No built-in ROM or RAM
+// * All instructions execute in 2 clock cycles
+// * NO arithmetic capabilities, except binary/BCD increment/decrement, bit
+// manipulation, left/right shift and comparison between A and R0
+// * Register access can be indexed to use GP registers as a "poor man's RAM"
+// _____ _____
+// PA0 1 |* \_/ | 40 VGG
+// PA1 2 | | 39 VDD
+// PA2 3 | | 38 VBG
+// PA3 4 | | 37 DC0
+// PA4 5 | | 36 DC1
+// PA5 6 | | 35 DC2
+// PA6 7 | | 34 DC3
+// PA7 8 | | 33 DC4
+// PA8 9 | | 32 DC5
+// PA9 10 |Nanoprocessor| 31 DC6
+// PA10 11 | | 30 DC7/INT ENA
+// DS3 12 | | 29 _INT
+// DS2 13 | | 28 ACK
+// DS1 14 | | 27 CLK
+// DS0 15 | | 26 STM
+// _R/W 16 | | 25 DB7
+// GND 17 | | 24 DB6
+// DB0 18 | | 23 DB5
+// DB1 19 | | 22 DB4
+// DB2 20 |_____________| 21 DB3
+//
+// The HP manual of Nanoprocessor is available here:
+// http://www.hp9845.net/9845/downloads/manuals/Nanoprocessor.pdf
+// Thanks to anyone who made the manual available.
+#ifndef _NANOPROCESSOR_H_
+#define _NANOPROCESSOR_H_
+
+#define HP_NANO_REGS 16 // Number of GP registers
+#define HP_NANO_PC_MASK 0x7ff // Mask of PC meaningful bits: 11 bits available
+#define HP_NANO_DC_NO 8 // Number of direct control lines (DC7 is typically used as interrupt mask)
+#define HP_NANO_IE_DC 7 // DC line used as interrupt enable/mask (DC7)
+
+// DC changed callback
+// The callback receives a 8-bit word holding the state of all DC lines.
+// DC0 is in bit 0, DC1 in bit 1 and so on.
+// Keep in mind that DC7 usually masks the interrupt signal.
+#define MCFG_HP_NANO_DC_CHANGED(_devcb) \
+ hp_nanoprocessor_device::set_dc_changed_func(*device , DEVCB_##_devcb);
+
+// Callback to read the input state of DC lines
+// All lines that are not in input are to be reported at "1"
+#define MCFG_HP_NANO_READ_DC_CB(_devcb) \
+ hp_nanoprocessor_device::set_read_dc_func(*device , DEVCB_##_devcb);
+
+class hp_nanoprocessor_device : public cpu_device
+{
+public:
+ hp_nanoprocessor_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ template<class _Object> static devcb_base &set_dc_changed_func(device_t &device, _Object object) { return downcast<hp_nanoprocessor_device &>(device).m_dc_changed_func.set_callback(object); }
+ template<class _Object> static devcb_base &set_read_dc_func(device_t &device, _Object object) { return downcast<hp_nanoprocessor_device &>(device).m_read_dc_func.set_callback(object); }
+
+ // device_t overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_execute_interface overrides
+ virtual uint32_t execute_min_cycles() const override { return 2; }
+ // 3 cycles is for int. acknowledge + 1 instruction
+ virtual uint32_t execute_max_cycles() const override { return 3; }
+ virtual uint32_t execute_input_lines() const override { return 1; }
+ virtual uint32_t execute_default_irq_vector() const override { return 0xff; }
+ virtual void execute_run() override;
+ virtual void execute_set_input(int linenum, int state) override;
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum) const override {
+ return (spacenum == AS_PROGRAM) ? &m_program_config : ((spacenum == AS_IO) ? &m_io_config : nullptr);
+ }
+
+ // device_state_interface overrides
+ virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
+
+ // device_disasm_interface overrides
+ virtual uint32_t disasm_min_opcode_bytes() const override { return 1; }
+ virtual uint32_t disasm_max_opcode_bytes() const override { return 2; }
+ virtual offs_t disasm_disassemble(char *buffer, offs_t pc, const uint8_t *oprom, const uint8_t *opram, uint32_t options) override;
+
+private:
+ devcb_write8 m_dc_changed_func;
+ devcb_read8 m_read_dc_func;
+ int m_icount;
+
+ // State of processor
+ uint8_t m_reg_A; // Accumulator
+ uint8_t m_reg_R[ HP_NANO_REGS ]; // General purpose registers
+ uint16_t m_reg_PA; // Program counter ("Program Address" in HP doc)
+ uint16_t m_reg_SSR; // Subroutine stack register
+ uint16_t m_reg_ISR; // Interrupt stack register
+ uint16_t m_flags; // Flags: extend flag (E) & direct control lines (DC0-7)
+
+ address_space_config m_program_config;
+ address_space_config m_io_config;
+
+ address_space *m_program;
+ direct_read_data *m_direct;
+ address_space *m_io;
+
+ void execute_one(uint8_t opcode);
+ uint16_t pa_offset(unsigned off) const;
+ uint8_t fetch(void);
+ void skip(void);
+ void dc_update(void);
+ void dc_set(unsigned bit_no);
+ void dc_clr(unsigned bit_no);
+};
+
+extern const device_type HP_NANOPROCESSOR;
+
+#endif /* _NANOPROCESSOR_H_ */
diff --git a/src/devices/cpu/nanoprocessor/nanoprocessor_dasm.cpp b/src/devices/cpu/nanoprocessor/nanoprocessor_dasm.cpp
new file mode 100644
index 00000000000..4c38d248936
--- /dev/null
+++ b/src/devices/cpu/nanoprocessor/nanoprocessor_dasm.cpp
@@ -0,0 +1,135 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+// *******************************
+// * HP nanoprocessor disassembler
+// *******************************
+
+#include "emu.h"
+#include "debugger.h"
+#include "nanoprocessor.h"
+
+typedef void (*fn_dis_param)(std::ostream& stream , uint8_t opcode , const uint8_t* opram);
+
+typedef struct {
+ uint8_t m_op_mask;
+ uint8_t m_opcode;
+ const char *m_mnemonic;
+ fn_dis_param m_param_fn;
+ uint32_t m_dasm_flags;
+} dis_entry_t;
+
+static void param_bitno(std::ostream& stream , uint8_t opcode , const uint8_t* opram)
+{
+ stream << (char)('0' + (opcode & 7));
+}
+
+static void param_ds(std::ostream& stream , uint8_t opcode , const uint8_t* opram)
+{
+ util::stream_format(stream , "DS%u" , opcode & 0xf);
+}
+
+static void param_reg(std::ostream& stream , uint8_t opcode , const uint8_t* opram)
+{
+ util::stream_format(stream , "R%u" , opcode & 0xf);
+}
+
+static void param_11bit(std::ostream& stream , uint8_t opcode , const uint8_t* opram)
+{
+ unsigned tmp = ((unsigned)(opcode & 7) << 8) | *opram;
+ util::stream_format(stream , "$%03x" , tmp);
+}
+
+static void param_page_no(std::ostream& stream , uint8_t opcode , const uint8_t* opram)
+{
+ stream << (char)('0' + (opcode & 7));
+}
+
+static void param_byte(std::ostream& stream , uint8_t opcode , const uint8_t* opram)
+{
+ util::stream_format(stream , "$%02x" , *opram);
+}
+
+static void param_ds_byte(std::ostream& stream , uint8_t opcode , const uint8_t* opram)
+{
+ util::stream_format(stream , "DS%u,$%02x" , opcode & 0xf , *opram);
+}
+
+static void param_reg_byte(std::ostream& stream , uint8_t opcode , const uint8_t* opram)
+{
+ util::stream_format(stream , "R%u,$%02x" , opcode & 0xf , *opram);
+}
+
+static const dis_entry_t dis_table[] = {
+ {0xff , 0x00 , "INB" , nullptr , 1},
+ {0xff , 0x01 , "DEB" , nullptr , 1},
+ {0xff , 0x02 , "IND" , nullptr , 1},
+ {0xff , 0x03 , "DED" , nullptr , 1},
+ {0xff , 0x04 , "CLA" , nullptr , 1},
+ {0xff , 0x05 , "CMA" , nullptr , 1},
+ {0xff , 0x06 , "RSA" , nullptr , 1},
+ {0xff , 0x07 , "LSA" , nullptr , 1},
+ {0xff , 0x08 , "SGT" , nullptr , 1},
+ {0xff , 0x09 , "SLT" , nullptr , 1},
+ {0xff , 0x0a , "SEQ" , nullptr , 1},
+ {0xff , 0x0b , "SAZ" , nullptr , 1},
+ {0xff , 0x0c , "SLE" , nullptr , 1},
+ {0xff , 0x0d , "SGE" , nullptr , 1},
+ {0xff , 0x0e , "SNE" , nullptr , 1},
+ {0xff , 0x0f , "SAN" , nullptr , 1},
+ {0xf8 , 0x10 , "SBS" , param_bitno , 1},
+ {0xff , 0x1f , "SES" , nullptr , 1},
+ {0xf8 , 0x18 , "SFS" , param_bitno , 1},
+ {0xf8 , 0x20 , "SBN" , param_bitno , 1},
+ {0xff , 0x2f , "ENI" , nullptr , 1},
+ {0xf8 , 0x28 , "STC" , param_bitno , 1},
+ {0xf8 , 0x30 , "SBZ" , param_bitno , 1},
+ {0xff , 0x3f , "SEZ" , nullptr , 1},
+ {0xf8 , 0x38 , "SFZ" , param_bitno , 1},
+ {0xf0 , 0x40 , "INA" , param_ds , 1},
+ {0xff , 0x5f , "NOP" , nullptr , 1},
+ {0xf0 , 0x50 , "OTA" , param_ds , 1},
+ {0xf0 , 0x60 , "LDA" , param_reg , 1},
+ {0xf0 , 0x70 , "STA" , param_reg , 1},
+ {0xf8 , 0x80 , "JMP" , param_11bit , 2},
+ {0xf8 , 0x88 , "JSB" , param_11bit , 2 | DASMFLAG_STEP_OVER},
+ {0xf8 , 0x90 , "JAI" , param_page_no , 1},
+ {0xf8 , 0x98 , "JAS" , param_page_no , 1 | DASMFLAG_STEP_OVER},
+ {0xf8 , 0xa0 , "CBN" , param_bitno , 1},
+ {0xff , 0xaf , "DSI" , nullptr , 1},
+ {0xf8 , 0xa8 , "CLC" , param_bitno , 1},
+ {0xff , 0xb0 , "RTI" , nullptr , 1 | DASMFLAG_STEP_OUT},
+ {0xff , 0xb1 , "RTE" , nullptr , 1 | DASMFLAG_STEP_OUT},
+ {0xff , 0xb4 , "STE" , nullptr , 1},
+ {0xff , 0xb5 , "CLE" , nullptr , 1},
+ {0xff , 0xb8 , "RTS" , nullptr , 1 | DASMFLAG_STEP_OUT},
+ {0xff , 0xb9 , "RSE" , nullptr , 1 | DASMFLAG_STEP_OUT},
+ {0xff , 0xcf , "LDR" , param_byte , 2},
+ {0xf0 , 0xc0 , "OTR" , param_ds_byte , 2},
+ {0xf0 , 0xd0 , "STR" , param_reg_byte , 2},
+ {0xf0 , 0xe0 , "LDI" , param_reg , 1},
+ {0xf0 , 0xf0 , "STI" , param_reg , 1},
+ // Catchall for undefined opcodes
+ {0x00 , 0x00 , "???" , nullptr , 1}
+};
+
+CPU_DISASSEMBLE(hp_nanoprocessor)
+{
+ std::ostringstream stream;
+ const uint8_t opcode = *oprom;
+
+ opram++;
+
+ for (const dis_entry_t& ent : dis_table) {
+ if ((opcode & ent.m_op_mask) == ent.m_opcode) {
+ stream << ent.m_mnemonic << ' ';
+ if (ent.m_param_fn != nullptr) {
+ ent.m_param_fn(stream , opcode , opram);
+ }
+ std::string stream_str = stream.str();
+ strcpy(buffer, stream_str.c_str());
+ return ent.m_dasm_flags | DASMFLAG_SUPPORTED;
+ }
+ }
+ // Should never ever happen
+ return 0;
+}
diff --git a/src/mame/drivers/hp9845.cpp b/src/mame/drivers/hp9845.cpp
index b6cff8a6343..dbccd3757e7 100644
--- a/src/mame/drivers/hp9845.cpp
+++ b/src/mame/drivers/hp9845.cpp
@@ -315,6 +315,7 @@ void hp9845b_state::machine_reset()
m_pa = 0;
sts_w(GVIDEO_PA , true);
+ update_graphic_bits();
memset(&m_kb_state[ 0 ] , 0 , sizeof(m_kb_state));
m_kb_scancode = 0x7f;
diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp
index 98d6a4f2c91..f72a5734f23 100644
--- a/src/tools/unidasm.cpp
+++ b/src/tools/unidasm.cpp
@@ -104,6 +104,7 @@ CPU_DISASSEMBLE( hd63701 );
CPU_DISASSEMBLE( hmcs40 );
CPU_DISASSEMBLE( hp_hybrid );
CPU_DISASSEMBLE( hp_5061_3001 );
+CPU_DISASSEMBLE( hp_nanoprocessor );
CPU_DISASSEMBLE( hyperstone_generic );
CPU_DISASSEMBLE( i4004 );
CPU_DISASSEMBLE( i8008 );
@@ -313,6 +314,7 @@ static const dasm_table_entry dasm_table[] =
{ "mips3be", _32be, 0, CPU_DISASSEMBLE_NAME(mips3be) },
{ "mips3le", _32le, 0, CPU_DISASSEMBLE_NAME(mips3le) },
{ "mn10200", _16le, 0, CPU_DISASSEMBLE_NAME(mn10200) },
+ { "nanoprocessor",_8bit, 0, CPU_DISASSEMBLE_NAME(hp_nanoprocessor) },
{ "nec", _8bit, 0, CPU_DISASSEMBLE_NAME(nec) },
{ "nsc8105", _8bit, 0, CPU_DISASSEMBLE_NAME(nsc8105) },
{ "pdp1", _32be, 0, CPU_DISASSEMBLE_NAME(pdp1) },