summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author fenugrec <fenugrec@users.noreply.github.com>2018-11-29 05:44:49 -0500
committer Robert <Robbbert@users.noreply.github.com>2018-11-29 21:44:49 +1100
commit63dd1ba82f63b014bd4e2d628783036e583eb6f7 (patch)
tree84991ed53b54295c0a092a95698f3b6c948ed56c
parent84fdea71c8178ccdc588215e9912beff9ecdc354 (diff)
New driver : HP 3478A Digital Multimeter (#4342)
* Added new driver: HP 3478A Multimeter WIP, machine not working, skeleton, highly incomplete. Compiles, that's about all. * hp3478a: implement ROM banking code runs "properly" at least to the CAL RAM check (fails, RAM not implemented) * hp3478a: some IO work Interpret CS lines for external accesses (GPIB, CAL RAM, DIP switches). Also, remove MCFG_ stuff Also, use logmacro.h stuff * hp3478a: partial emulation of LCD The main CPU has a serial link to the LCD module. This WIP splits commands and data mostly successfully (still some bogus shifting which would be fairly easy to ignore). None of the commands are implemented yet, and no actual display is generated yet. Includes unrelated tweak : only change bank when the A12 line changes. * hp3478a: added LCD rendering ! code shamelessly stolen from tranz330 and roc10937 drivers. Not clickable yet. * hp3478a: implement CAL NVRAM Finally. IO mapping has provisions for the DIP switches as well as i8291 GPIB interface registers. * hp3478a: improve LCD rendering Remove some artifacts: with the LCD "not selected", some data is sent on IWA (probably to purge a shift register ?) but was parsed with the last m_lcdiwa state. Reset this everytime PWO is deselected. Also parse decimal point, comma and "all segments". * hp3478a: implement keypad * hp3478a: CAL switch to write-protect NVRAM * hp3478a: implement DIP switches * hp3478a: fix self-test reset freeze (missing WDT) There is an external WDT counter that is periodically reset by the CPU in normal operation. When forcing a reset from the front panel, this counter is allowed to overflow (20th bit, clocked at Xtal / 15), giving a reset time of about 1.3s.
-rw-r--r--scripts/target/mame/mess.lua1
-rw-r--r--src/emu/xtal.cpp1
-rw-r--r--src/mame/drivers/hp3478a.cpp630
-rw-r--r--src/mame/includes/hp3478a.h103
-rw-r--r--src/mame/layout/hp3478a.lay156
-rw-r--r--src/mame/mame.lst3
6 files changed, 894 insertions, 0 deletions
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index e3f60b783ad..1e6bccdea86 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -3759,6 +3759,7 @@ files {
MAME_DIR .. "src/mame/drivers/harriet.cpp",
MAME_DIR .. "src/mame/drivers/hazeltin.cpp",
MAME_DIR .. "src/mame/drivers/hazl1420.cpp",
+ MAME_DIR .. "src/mame/drivers/hp3478a.cpp",
MAME_DIR .. "src/mame/drivers/hprot1.cpp",
MAME_DIR .. "src/mame/drivers/hpz80unk.cpp",
MAME_DIR .. "src/mame/drivers/ht68k.cpp",
diff --git a/src/emu/xtal.cpp b/src/emu/xtal.cpp
index cc7dc8bd282..0f12b65c60d 100644
--- a/src/emu/xtal.cpp
+++ b/src/emu/xtal.cpp
@@ -110,6 +110,7 @@ const double XTAL::known_xtals[] = {
5'626'000, /* 5.626_MHz_XTAL RCA CDP1869 PAL dot clock */
5'670'000, /* 5.67_MHz_XTAL RCA CDP1869 NTSC dot clock */
5'714'300, /* 5.7143_MHz_XTAL Cidelsa Destroyer, TeleVideo serial keyboards */
+ 5'856'000, /* 5.856_MHz_XTAL HP 3478A Multimeter */
5'911'000, /* 5.911_MHz_XTAL Philips Videopac Plus G7400 */
5'990'400, /* 5.9904_MHz_XTAL Luxor ABC 800 keyboard (Keytronic custom part #48-300-008 is equivalent) */
6'000'000, /* 6_MHz_XTAL American Poker II, Taito SJ System */
diff --git a/src/mame/drivers/hp3478a.cpp b/src/mame/drivers/hp3478a.cpp
new file mode 100644
index 00000000000..dab6612bd5e
--- /dev/null
+++ b/src/mame/drivers/hp3478a.cpp
@@ -0,0 +1,630 @@
+// license:BSD-3-Clause
+// copyright-holders:fenugrec
+/******************************************************************************
+* HP 3478A Digital Multimeter
+*
+* Emulating test equipment is not very meaningful except for developping ROM patches.
+* This aims to be the minimal emulation sufficient to run the UI (keypad and display).
+* Ideally, faking ADC readings could be useful too.
+*
+* Some of this will probably be applicable to HP 3468A units too.
+*
+* Current status : runs, AD LINK ERROR on stock ROM due to unimplemented AD link
+* - patching the AD comms, we get to a mostly functional state (for patch examples,
+* see https://github.com/fenugrec/hp3478a_rompatch )
+*
+*
+* TODO next level
+* * do something for analog CPU serial link (not quite uart), or dump+emulate CPU
+* * better display render and layout (annunciators + show button subtext)
+*
+* TODO level 9000
+* * Connect this with the existing i8291.cpp driver
+* * dump + add analog CPU (8049)
+* * validate one single chipselect active when doing external access (movx)
+
+
+**** Hardware details (refer to service manual for schematics)
+Main CPU : i8039 , no internal ROM
+Analog (floating) CPU : i8049, internal ROM (never dumped ?)
+ROM : 2764 (64kbit, org 8kB)
+RAM : 5101 , 256 * 4bit (!), battery-backed calibration data
+GPIB: i8291
+Display : unknown; similar protocol for HP 3457A documented on
+ http://www.eevblog.com/forum/projects/led-display-for-hp-3457a-multimeter-i-did-it-)/25/
+
+
+
+Main cpu I/O ports:
+Port1
+P14-P17 : keypad out (cols)
+P10-P13 : keypad in (rows)
+
+P20 : disp.clk1
+P21 : !CS for GPIB, and disp.IWA
+P22 : !CS for DIPswitch; disp.ISA (for instructions)
+P23 = !OE for RAM ; disp.sync (enable instruction)
+P24 = disp.PWO (enable)
+P25 = disp.clk2
+P26 : address bit12 ! (0x1000) => hardware banking
+P27 : data out thru isol, to analog CPU
+
+T1 : data in thru isol, from analog CPU (opcodes jt1 / jnt1)
+*/
+
+#include "emu.h"
+#include "includes/hp3478a.h"
+#include "hp3478a.lh"
+
+
+#define CPU_CLOCK XTAL(5'856'000)
+
+#define A12_PIN P26
+#define CALRAM_CS P23
+#define DIPSWITCH_CS P22
+#define GPIB_CS P21
+
+#define DISP_PWO P24
+#define DISP_SYNC P23
+#define DISP_ISA P22
+#define DISP_IWA P21
+#define DISP_CK1 P20
+ //don't care about CK2 since it's supposed to be a delayed copy of CK1
+#define DISP_MASK (DISP_PWO | DISP_SYNC | DISP_ISA | DISP_IWA | DISP_CK1) //used for edge detection
+
+// IO banking : indexes of m_iobank maps
+#define CALRAM_ENTRY 0
+#define GPIB_ENTRY 1
+#define DIP_ENTRY 2
+
+/**** optional debug outputs, must be before #include logmacro.*/
+#define DEBUG_PORTS (LOG_GENERAL << 1)
+#define DEBUG_BANKING (LOG_GENERAL << 2)
+#define DEBUG_BUS (LOG_GENERAL << 3) //not used after all
+#define DEBUG_KEYPAD (LOG_GENERAL << 4)
+#define DEBUG_LCD (LOG_GENERAL << 5) //low level
+#define DEBUG_LCD2 (LOG_GENERAL << 6)
+#define DEBUG_CAL (LOG_GENERAL << 7)
+
+#define VERBOSE (DEBUG_BUS) //can be combined, like (DEBUG_CAL | DEBUG_KEYPAD)
+
+#include "logmacro.h"
+
+
+
+/***** callbacks */
+/* port1 manages the keypad matrix */
+
+READ8_MEMBER( hp3478a_state::p1read )
+{
+ unsigned i;
+ uint8_t data = m_maincpu->p1_r() | 0x0F; //P10-P13 "pull-up"
+
+ // for each column, set Px=0 for pressed buttons (active low)
+ for (i = 0; i < 4; i++) {
+ if (!(data & (0x10 << i))) {
+ data &= (0xF0 | m_keypad[i]->read()); //not sure if the undefined upper bits will read as 1 ?
+ }
+ }
+ LOGMASKED(DEBUG_KEYPAD, "port1 read: 0x%02X\n", data);
+ return data;
+}
+
+/* pin P17 rising edges also reset the external WDT counter */
+WRITE8_MEMBER( hp3478a_state::p1write )
+{
+ if (~p1_oldstate & data & 0x80) {
+ //P17 rising edge
+ m_watchdog->watchdog_reset();
+ }
+ p1_oldstate = data;
+}
+
+/** a lot of stuff multiplexed on the P2 pins.
+ * parse the chipselect lines, A12 line, and LCD interface.
+ */
+WRITE8_MEMBER( hp3478a_state::p2write )
+{
+ LOGMASKED(DEBUG_PORTS, "port2 write: %02X\n", data);
+
+ // check which CS line is active. No collision checking is done here
+ // because the LCD interface reuses those pins and we'd get spurious errors.
+ // So the last evaluated condition will be kept.
+
+ if (!(data & CALRAM_CS)) {
+ //will read lower 4 bits from calram
+ m_iobank->set_bank(CALRAM_ENTRY);
+ }
+ if (!(data & DIPSWITCH_CS)) {
+ m_iobank->set_bank(DIP_ENTRY);
+ }
+ if (!(data & GPIB_CS)) {
+ m_iobank->set_bank(GPIB_ENTRY);
+ }
+
+ if ((p2_oldstate ^ data) & A12_PIN) {
+ /* A12 pin state changed */
+ if (data & A12_PIN) {
+ m_bank0->set_entry(1);
+ LOGMASKED(DEBUG_BANKING, "changed to bank1\n");
+ } else {
+ m_bank0->set_entry(0);
+ LOGMASKED(DEBUG_BANKING, "changed to bank0\n");
+ }
+ }
+
+ if ((p2_oldstate ^ data) & DISP_MASK) {
+ /* display signals changed */
+ lcd_interface(data);
+ }
+
+ p2_oldstate = data;
+}
+
+
+/* CAL RAM write handler, to implement "CAL enable" front panel switch
+*/
+WRITE8_MEMBER( hp3478a_state::nvwrite ) {
+ if (m_calenable->read()) {
+ m_nvram_raw[offset] = data;
+ LOGMASKED(DEBUG_CAL, "write %02X to cal[%02X]\n", data, offset);
+ } else {
+ LOGMASKED(DEBUG_CAL, "write %02X to cal[%02X]:dropped\n", data, offset);
+ }
+}
+
+
+/**** LCD emulation
+ *
+ * Yuck. Emulate serial LCD module interface. don't really want to make a separate driver for this...
+ * The protocol is common to many HP products of the era. Some sources have the instruction words written as 10-bit
+ * words, but it would appear more consistent (and matches the intent guessed from the disassembled functions)
+ * that they are actually 8-bit bytes. The 2-bit difference is a "bogus" 2 clock cycles for when SYNC or PWO changes ?
+ *
+*/
+
+/** charset copied from roc10937 driver. Some special chars are wrong.
+ * Interestingly, the 3478a usually doesn't use "0x30" for the number 0, but instead
+ * maps it to the character 'O' ! It does use 0x30 when printing the GPIB address however.
+ */
+static const uint16_t hpcharset[]=
+{ // FEDC BA98 7654 3210
+ 0x507F, // 0101 0000 0111 1111 @.
+ 0x44CF, // 0100 0100 1100 1111 A.
+ 0x153F, // 0001 0101 0011 1111 B.
+ 0x00F3, // 0000 0000 1111 0011 C.
+ 0x113F, // 0001 0001 0011 1111 D.
+ 0x40F3, // 0100 0000 1111 0011 E.
+ 0x40C3, // 0100 0000 1100 0011 F.
+ 0x04FB, // 0000 0100 1111 1011 G.
+ 0x44CC, // 0100 0100 1100 1100 H.
+ 0x1133, // 0001 0001 0011 0011 I.
+ 0x007C, // 0000 0000 0111 1100 J.
+ 0x4AC0, // 0100 1010 1100 0000 K.
+ 0x00F0, // 0000 0000 1111 0000 L.
+ 0x82CC, // 1000 0010 1100 1100 M.
+ 0x88CC, // 1000 1000 1100 1100 N.
+ 0x00FF, // 0000 0000 1111 1111 O.
+ 0x44C7, // 0100 0100 1100 0111 P.
+ 0x08FF, // 0000 1000 1111 1111 Q.
+ 0x4CC7, // 0100 1100 1100 0111 R.
+ 0x44BB, // 0100 0100 1011 1011 S.
+ 0x1103, // 0001 0001 0000 0011 T.
+ 0x00FC, // 0000 0000 1111 1100 U.
+ 0x22C0, // 0010 0010 1100 0000 V.
+ 0x28CC, // 0010 1000 1100 1100 W.
+ 0xAA00, // 1010 1010 0000 0000 X.
+ 0x9200, // 1001 0010 0000 0000 Y.
+ 0x2233, // 0010 0010 0011 0011 Z.
+ 0x00E1, // 0000 0000 1110 0001 [.
+ 0x8800, // 1000 1000 0000 0000 \.
+ 0x001E, // 0000 0000 0001 1110 ].
+ 0x2800, // 0010 1000 0000 0000 ^.
+ 0x0030, // 0000 0000 0011 0000 _.
+ 0x0000, // 0000 0000 0000 0000 [space] , 0x20
+ 0x8121, // 1000 0001 0010 0001 !.
+ 0x0180, // 0000 0001 1000 0000 ".
+ 0x553C, // 0101 0101 0011 1100 #.
+ 0x55BB, // 0101 0101 1011 1011 $.
+ 0x7799, // 0111 0111 1001 1001 %.
+ 0xC979, // 1100 1001 0111 1001 &.
+ 0x0200, // 0000 0010 0000 0000 '.
+ 0x0A00, // 0000 1010 0000 0000 (.
+ 0xA050, // 1010 0000 0000 0000 ).
+ 0xFF00, // 1111 1111 0000 0000 *.
+ 0x5500, // 0101 0101 0000 0000 +.
+ 0x0000, // 0000 0000 0000 0000 //XXX (0x2C)
+ 0x4400, // 0100 0100 0000 0000 --.
+ 0x0000, // 0000 0000 0000 0000 //XXX (0x2E)
+ 0x2200, // 0010 0010 0000 0000 /.
+ 0x22FF, // 0010 0010 1111 1111 0. (0x30)
+ 0x1100, // 0001 0001 0000 0000 1.
+ 0x4477, // 0100 0100 0111 0111 2.
+ 0x443F, // 0100 0100 0011 1111 3.
+ 0x448C, // 0100 0100 1000 1100 4.
+ 0x44BB, // 0100 0100 1011 1011 5.
+ 0x44FB, // 0100 0100 1111 1011 6.
+ 0x000F, // 0000 0000 0000 1111 7.
+ 0x44FF, // 0100 0100 1111 1111 8.
+ 0x44BF, // 0100 0100 1011 1111 9.
+ 0xFFFF, // 1111 1111 1111 1111 [all segs] (0x3A)
+ 0x2001, // 0010 0000 0000 0001 //XXX
+ 0x2230, // 0010 0010 0011 0000 <.
+ 0x4430, // 0100 0100 0011 0000 =.
+ 0x8830, // 1000 1000 0011 0000 >.
+ 0x1407, // 0001 0100 0000 0111 ?.
+};
+
+/** copy data in shiftreg to the high nibble of each digit in chrbuf */
+static void lcd_update_hinib(uint8_t *chrbuf, uint64_t shiftreg)
+{
+ int i;
+ for (i=11; i >= 0; i--) {
+ chrbuf[i] &= 0x0F;
+ chrbuf[i] |= (shiftreg & 0x0F) << 4;
+ shiftreg >>= 4;
+ }
+}
+
+/** copy data in shiftreg to the low nibble of each digit in chrbuf */
+static void lcd_update_lonib(uint8_t *chrbuf, uint64_t shiftreg)
+{
+ int i;
+ for (i=11; i >= 0; i--) {
+ chrbuf[i] &= 0xF0;
+ chrbuf[i] |= (shiftreg & 0x0F);
+ shiftreg >>= 4;
+ }
+}
+
+/** map LCD char to ASCII and segment data + update
+ *
+ * discards extra bits
+ */
+void hp3478a_state::lcd_map_chars()
+{
+ int i;
+ LOGMASKED(DEBUG_LCD2, "LCD : map ");
+ for (i=0; i < 12; i++) {
+ bool dp = lcd_chrbuf[i] & 0x40; //check decimal point. Needs to be mapped to seg_bit16
+ bool comma = lcd_chrbuf[i] & 0x80; //check comma, maps to seg17
+ lcd_text[i] = (lcd_chrbuf[i] & 0x3F) + 0x40;
+ lcd_segdata[i] = hpcharset[lcd_chrbuf[i] & 0x3F] | (dp << 16) | (comma << 17);
+ LOGMASKED(DEBUG_LCD2, "[%02X>%04X] ", lcd_chrbuf[i] & 0x3F, lcd_segdata[i]);
+ }
+ LOGMASKED(DEBUG_LCD2, "\n");
+}
+
+/** ?? from roc10937 */
+static uint32_t lcd_set_display(uint32_t segin)
+{
+ return bitswap<32>(segin, 31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,11,9,15,13,12,8,10,14,7,6,5,4,3,2,1,0);
+}
+
+// ISA command bytes
+#define DISP_ISA_WANNUN 0xBC //annunciators
+#define DISP_ISA_WA 0x0A //low nibbles
+#define DISP_ISA_WB 0x1A //hi nib
+#define DISP_ISA_WC 0x2A // "extended bit" ?
+
+/** LCD serial interface state machine. I cheat and don't implement all commands.
+ * Also, it's not clear when exactly the display should be updated. After each regA/regB write
+ * seems to generate some glitches. After PWO deselect causes some half-written text to appear sometimes.
+ */
+void hp3478a_state::lcd_interface(uint8_t p2new)
+{
+ bool pwo_state, sync_state, isa_state, iwa_state;
+
+ pwo_state = p2new & DISP_PWO;
+ sync_state = p2new & DISP_SYNC;
+ isa_state = p2new & DISP_ISA;
+ iwa_state = p2new & DISP_IWA;
+
+ if (!((p2new ^ p2_oldstate) & DISP_CK1)) {
+ // no clock edge : boring.
+ //LOGMASKED(DEBUG_LCD, "LCD : pwo(%d), sync(%d), isa(%d), iwa(%d)\n",
+ // pwo_state, sync_state, isa_state, iwa_state);
+ return;
+ }
+
+ if (!(p2new & DISP_CK1)) {
+ //neg edge
+ return;
+ }
+
+ // CK1 clock positive edge
+ if (!pwo_state) {
+ //not selected, reset everything
+ LOGMASKED(DEBUG_LCD, "LCD : state=IDLE, PWO deselected, %d stray bits(0x...%02X)\n",lcd_bitcount, lcd_bitbuf & 0xFF);
+ m_lcdstate = lcd_state::IDLE;
+ m_lcdiwa = lcd_iwatype::DISCARD;
+ std::transform(std::begin(lcd_segdata), std::end(lcd_segdata), std::begin(*m_outputs), lcd_set_display);
+ lcd_bitcount = 0;
+ lcd_bitbuf = 0;
+ return;
+ }
+ switch (m_lcdstate) {
+ case lcd_state::IDLE:
+ lcd_want = 8;
+ m_lcdstate = lcd_state::SYNC_SKIP;
+ break;
+ case lcd_state::SYNC_SKIP:
+ // if SYNC changed, we need to ignore two clock pulses.
+ lcd_bitcount++;
+ if (lcd_bitcount < 1) {
+ break;
+ }
+ lcd_bitcount = 0;
+ lcd_bitbuf = 0;
+ if (sync_state) {
+ m_lcdstate = lcd_state::SELECTED_ISA;
+ lcd_want = 8;
+ LOGMASKED(DEBUG_LCD, "LCD : state=SELECTED_ISA\n");
+ } else {
+ //don't touch lcd_want since it was possibly set in the ISA stage
+ m_lcdstate = lcd_state::SELECTED_IWA;
+ LOGMASKED(DEBUG_LCD, "LCD : state=SELECTED_IWA, want %d\n", lcd_want);
+ }
+ break;
+ case lcd_state::SELECTED_ISA:
+ if (!sync_state) {
+ //changing to SELECTED_IWA
+ m_lcdstate = lcd_state::SYNC_SKIP;
+ if (lcd_bitcount) {
+ LOGMASKED(DEBUG_LCD, "LCD : ISA->IWA, %d stray bits (0x%0X)\n", lcd_bitcount, lcd_bitbuf);
+ } else {
+ LOGMASKED(DEBUG_LCD, "LCD : ISA->IWA\n");
+ }
+ lcd_bitcount = 0;
+ lcd_bitbuf = 0;
+ break;
+ }
+ lcd_bitbuf |= (isa_state << lcd_bitcount);
+ lcd_bitcount++;
+ if (lcd_bitcount != lcd_want) {
+ break;
+ }
+ LOGMASKED(DEBUG_LCD, "LCD : Instruction 0x%02X\n", lcd_bitbuf & 0xFF);
+ //shouldn't get extra bits, but we have nothing better to do so just reset the shiftreg.
+ lcd_bitcount = 0;
+ switch (lcd_bitbuf & 0xFF) {
+ case DISP_ISA_WANNUN:
+ lcd_want = 44;
+ m_lcdiwa = lcd_iwatype::ANNUNS;
+ break;
+ case DISP_ISA_WA:
+ lcd_want = 100; //no, doesn't fit in a uint64, but only the first 36 bits are significant.
+ m_lcdiwa = lcd_iwatype::REG_A;
+ break;
+ case DISP_ISA_WB:
+ lcd_want = 100;
+ m_lcdiwa = lcd_iwatype::REG_B;
+ break;
+ case DISP_ISA_WC:
+ lcd_want = 44;
+ m_lcdiwa = lcd_iwatype::REG_C;
+ break;
+ default:
+ lcd_want = 44;
+ m_lcdiwa = lcd_iwatype::DISCARD;
+ break;
+ }
+ lcd_bitbuf = 0;
+ break;
+ case lcd_state::SELECTED_IWA:
+ if (sync_state) {
+ //changing to SELECTED_ISA
+ m_lcdstate = lcd_state::SYNC_SKIP;
+ if (lcd_bitcount) {
+ LOGMASKED(DEBUG_LCD, "LCD : IWA->ISA, %d stray bits (0x%I64X)\n", lcd_bitcount, lcd_bitbuf);
+ } else {
+ LOGMASKED(DEBUG_LCD, "LCD : IWA->ISA\n");
+ }
+ lcd_bitcount = 0;
+ lcd_bitbuf = 0;
+ break;
+ }
+ if (lcd_bitcount <= 0x3F) {
+ //clamp to bit 63;
+ lcd_bitbuf |= ((uint64_t) iwa_state << lcd_bitcount);
+ }
+ lcd_bitcount++;
+ if (lcd_bitcount != lcd_want) {
+ break;
+ }
+ LOGMASKED(DEBUG_LCD, "LCD : data 0x%I64X\n", lcd_bitbuf);
+ switch (m_lcdiwa) {
+ case lcd_iwatype::ANNUNS:
+ LOGMASKED(DEBUG_LCD2, "LCD : write annuns 0x%02X\n", lcd_bitbuf & 0xFF);
+ break;
+ case lcd_iwatype::REG_A:
+ lcd_update_lonib(lcd_chrbuf, lcd_bitbuf);
+ lcd_map_chars();
+ LOGMASKED(DEBUG_LCD2, "LCD : write reg A (lonib) %I64X, text=%s\n", lcd_bitbuf, (char *) lcd_text);
+ break;
+ case lcd_iwatype::REG_B:
+ lcd_update_hinib(lcd_chrbuf, lcd_bitbuf);
+ lcd_map_chars();
+ LOGMASKED(DEBUG_LCD2, "LCD : write reg B (lonib) %I64X, text=%s\n", lcd_bitbuf, (char *) lcd_text);
+ break;
+ default:
+ //discard
+ break;
+ }
+ //shouldn't get extra bits, but we have nothing better to do so just reset the shiftreg.
+ lcd_bitcount = 0;
+ lcd_bitbuf = 0;
+ break; //case SELECTED_IWA
+ }
+
+ return;
+}
+
+
+
+
+
+void hp3478a_state::machine_start()
+{
+ m_bank0->configure_entries(0, 2, memregion("maincpu")->base(), 0x1000);
+
+ m_outputs = std::make_unique<output_finder<16> >(*this, "vfd%u", (unsigned) 0);
+ m_outputs->resolve();
+
+ m_watchdog->watchdog_enable();
+
+}
+
+/******************************************************************************
+ Address Maps
+******************************************************************************/
+
+void hp3478a_state::i8039_map(address_map &map)
+{
+ map(0x0000, 0x0fff).bankr("bank0"); // CPU address space (4kB), banked according to P26 pin
+}
+
+void hp3478a_state::i8039_io(address_map &map)
+{
+ map.global_mask(0xff);
+ map(0x00, 0xff).m(m_iobank, FUNC(address_map_bank_device::amap8));
+}
+
+/* depending on the P2 port state, different chipselect lines are activated, which
+ * affect the subsequent external accesses (movx)
+ * The addresses in here have nothing to do with the mcs48 address space.
+ */
+void hp3478a_state::io_bank(address_map &map)
+{
+ map.unmap_value_high();
+ map(0x000, 0x0ff).ram().region("nvram", 0).share("nvram").w(FUNC(hp3478a_state::nvwrite));
+ map(0x100, 0x107).ram().share("gpibregs"); //XXX TODO : connect to i8291.cpp
+ map(0x200, 0x2ff).portr("DIP");
+}
+
+
+/******************************************************************************
+ Input Ports
+******************************************************************************/
+static INPUT_PORTS_START( hp3478a )
+/* keypad bit matrix:
+ 0x08|0x04|0x02|0x01
+ col.0 : (nc)|shift|ACA|DCA
+ col.1 : 4W|2W|ACV|DCV
+ col.2 : int|dn|up|auto
+ col.3 : (nc)|loc|srq|sgl
+*/
+ PORT_START("COL.0")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("DCA")
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("ACA")
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("SHIFT")
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) //nothing on 0x08
+ PORT_START("COL.1")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("DCV")
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("ACV")
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("2W")
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_NAME("4W")
+ PORT_START("COL.2")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON8 ) PORT_NAME("AUTO")
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON9 ) PORT_NAME("UP")
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON10 ) PORT_NAME("DN")
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON11 ) PORT_NAME("INT")
+ PORT_START("COL.3")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON12 ) PORT_NAME("SGL")
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON13 ) PORT_NAME("SRQ")
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON14 ) PORT_NAME("LOC")
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) //nothing on 0x08
+
+ PORT_START("CAL_EN")
+ PORT_CONFNAME(1, 0, "CAL")
+ PORT_CONFSETTING(0x00, "disabled")
+ PORT_CONFSETTING(0x01, "enabled")
+
+ PORT_START("DIP")
+ PORT_DIPNAME( 0x1f, 0x17, "HP-IB Bus Address" ) PORT_DIPLOCATION("DIP:1,2,3,4,5")
+ PORT_DIPSETTING( 0x00, "0" )
+ PORT_DIPSETTING( 0x01, "1" )
+ PORT_DIPSETTING( 0x02, "2" )
+ PORT_DIPSETTING( 0x03, "3" )
+ PORT_DIPSETTING( 0x04, "4" )
+ PORT_DIPSETTING( 0x05, "5" )
+ PORT_DIPSETTING( 0x06, "6" )
+ PORT_DIPSETTING( 0x07, "7" )
+ PORT_DIPSETTING( 0x08, "8" )
+ PORT_DIPSETTING( 0x09, "9" )
+ PORT_DIPSETTING( 0x0a, "10" )
+ PORT_DIPSETTING( 0x0b, "11" )
+ PORT_DIPSETTING( 0x0c, "12" )
+ PORT_DIPSETTING( 0x0d, "13" )
+ PORT_DIPSETTING( 0x0e, "14" )
+ PORT_DIPSETTING( 0x0f, "15" )
+ PORT_DIPSETTING( 0x10, "16" )
+ PORT_DIPSETTING( 0x11, "17" )
+ PORT_DIPSETTING( 0x12, "18" )
+ PORT_DIPSETTING( 0x13, "19" )
+ PORT_DIPSETTING( 0x14, "20" )
+ PORT_DIPSETTING( 0x15, "21" )
+ PORT_DIPSETTING( 0x16, "22" )
+ PORT_DIPSETTING( 0x17, "23" )
+ PORT_DIPSETTING( 0x18, "24" )
+ PORT_DIPSETTING( 0x19, "25" )
+ PORT_DIPSETTING( 0x1a, "26" )
+ PORT_DIPSETTING( 0x1b, "27" )
+ PORT_DIPSETTING( 0x1c, "28" )
+ PORT_DIPSETTING( 0x1d, "29" )
+ PORT_DIPSETTING( 0x1e, "30" )
+ PORT_DIPSETTING( 0x1f, "31" )
+ PORT_DIPNAME( 0x20, 0x00, "PWR ON SRQ" ) PORT_DIPLOCATION("DIP:6")
+ PORT_DIPSETTING( 0x00, "Disabled" )
+ PORT_DIPSETTING( 0x20, "Enabled" )
+ //0x40 unused
+ PORT_DIPNAME( 0x80, 0x00, "50/60Hz AC" ) PORT_DIPLOCATION("DIP:8")
+ PORT_DIPSETTING( 0x00, "60Hz" )
+ PORT_DIPSETTING( 0x80, "50Hz" )
+
+INPUT_PORTS_END
+
+/******************************************************************************
+ Machine Drivers
+******************************************************************************/
+
+void hp3478a_state::hp3478a(machine_config &config)
+{
+ auto &mcu(I8039(config, "maincpu", CPU_CLOCK));
+ mcu.set_addrmap(AS_PROGRAM, &hp3478a_state::i8039_map);
+ mcu.set_addrmap(AS_IO, &hp3478a_state::i8039_io);
+ mcu.p1_in_cb().set(FUNC(hp3478a_state::p1read));
+ mcu.p1_out_cb().set(FUNC(hp3478a_state::p1write));
+ mcu.p2_out_cb().set(FUNC(hp3478a_state::p2write));
+
+ NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
+
+ ADDRESS_MAP_BANK(config, m_iobank, 0);
+ m_iobank->set_map(&hp3478a_state::io_bank);
+ m_iobank->set_data_width(8);
+ m_iobank->set_addr_width(18);
+ m_iobank->set_stride(0x100);
+
+ WATCHDOG_TIMER(config, m_watchdog).set_time(attotime::from_ticks(3*5*(1<<19),CPU_CLOCK));
+
+ // video
+ config.set_default_layout(layout_hp3478a);
+}
+
+/******************************************************************************
+ ROM Definitions
+******************************************************************************/
+ROM_START( hp3478a )
+ ROM_REGION( 0x2000, "maincpu", 0 )
+ ROM_LOAD("rom_dc118.bin", 0, 0x2000, CRC(10097ced) SHA1(bd665cf7e07e63f825b2353c8322ed8a4376b3bd)) //main CPU ROM, can match other datecodes too
+
+ ROM_REGION( 0x100, "nvram", 0 ) /* Calibration RAM, battery-backed */
+ ROM_LOAD_OPTIONAL( "calram.bin", 0, 0x100, CRC(0))
+ROM_END
+
+/******************************************************************************
+ Drivers
+******************************************************************************/
+
+// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
+COMP( 1983, hp3478a, 0, 0, hp3478a, hp3478a,hp3478a_state, empty_init, "HP", "HP 3478A Multimeter", MACHINE_IS_INCOMPLETE | MACHINE_NO_SOUND_HW | MACHINE_TYPE_OTHER)
diff --git a/src/mame/includes/hp3478a.h b/src/mame/includes/hp3478a.h
new file mode 100644
index 00000000000..e327d0a925b
--- /dev/null
+++ b/src/mame/includes/hp3478a.h
@@ -0,0 +1,103 @@
+// license:BSD-3-Clause
+// copyright-holders:fenugrec
+/***************************************************************************
+ HP 3478A Digital Multimeter
+
+ XXX Not sure why I need this file, can this all go in hp3478a.cpp ?
+****************************************************************************/
+
+#pragma once
+
+#ifndef MAME_INCLUDES_HP3478A_H
+#define MAME_INCLUDES_HP3478A_H
+
+#include "cpu/mcs48/mcs48.h"
+#include "machine/bankdev.h"
+#include "machine/nvram.h"
+#include "machine/watchdog.h"
+
+/* port pin/bit defs */
+#define P20 (1 << 0)
+#define P21 (1 << 1)
+#define P22 (1 << 2)
+#define P23 (1 << 3)
+#define P24 (1 << 4)
+#define P25 (1 << 5)
+#define P26 (1 << 6)
+#define P27 (1 << 7)
+
+#define VFD_TAG "vfd"
+
+class hp3478a_state : public driver_device
+{
+public:
+ hp3478a_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag)
+ , m_maincpu(*this, "maincpu")
+ , m_nvram(*this, "nvram")
+ , m_nvram_raw(*this, "nvram")
+ , m_watchdog(*this, "watchdog")
+ , m_bank0(*this, "bank0")
+ , m_iobank(*this, "iobank")
+ , m_keypad(*this, "COL.%u", 0)
+ , m_calenable(*this, "CAL_EN")
+ {
+ }
+
+ void hp3478a(machine_config &config);
+
+protected:
+ virtual void machine_start() override;
+ //virtual void machine_reset() override; //not needed?
+
+ DECLARE_READ8_MEMBER(p1read);
+ DECLARE_WRITE8_MEMBER(p1write);
+ DECLARE_WRITE8_MEMBER(p2write);
+ DECLARE_WRITE8_MEMBER(nvwrite);
+
+ void io_bank(address_map &map);
+ void i8039_io(address_map &map);
+ void i8039_map(address_map &map);
+
+ required_device<i8039_device> m_maincpu;
+ required_device<nvram_device> m_nvram;
+ required_shared_ptr<uint8_t> m_nvram_raw;
+ required_device<watchdog_timer_device> m_watchdog;
+ required_memory_bank m_bank0;
+ required_device<address_map_bank_device> m_iobank;
+ required_ioport_array<4> m_keypad;
+ required_ioport m_calenable;
+
+ /////////////// stuff for internal LCD emulation
+ // could be split to a separate driver ?
+ std::unique_ptr<output_finder<16> > m_outputs;
+
+ void lcd_interface(uint8_t p2new);
+ void lcd_map_chars(void);
+ uint8_t lcd_bitcount;
+ uint8_t lcd_want;
+ uint64_t lcd_bitbuf;
+ enum class lcd_state : uint8_t {
+ IDLE,
+ SYNC_SKIP,
+ SELECTED_ISA,
+ SELECTED_IWA
+ } m_lcdstate;
+ enum class lcd_iwatype : uint8_t {
+ ANNUNS,
+ REG_A,
+ REG_B,
+ REG_C,
+ DISCARD
+ } m_lcdiwa;
+ uint8_t lcd_chrbuf[12]; //raw digits (not ASCII)
+ uint8_t lcd_text[13]; //mapped to ASCII
+ uint32_t lcd_segdata[12];
+
+ uint8_t p2_oldstate; //used to detect edges on Port2 IO pins. Should be saveable ?
+ uint8_t p1_oldstate; //for P17 edge detection (WDT reset)
+
+};
+
+
+#endif // MAME_INCLUDES_HP3478A_H
diff --git a/src/mame/layout/hp3478a.lay b/src/mame/layout/hp3478a.lay
new file mode 100644
index 00000000000..fcd807df3af
--- /dev/null
+++ b/src/mame/layout/hp3478a.lay
@@ -0,0 +1,156 @@
+<?xml version="1.0"?>
+<!-- loosely based on tranz330.lay -->
+<!-- note, the led16seg renderer is hardcoded to have OFF segments darker than ON, which is good for LEDs but not LCDs. -->
+<!-- TODO : fix that -->
+<!-- TODO : add button subtext for shifted functions -->
+<!-- TODO : make buttons clickable ? all the COL / input stuff is wrong-->
+<mamelayout version="2">
+ <element name="vfd0"><led16segsc><color red="0.8" green="0.8" blue="0.8" /></led16segsc></element>
+
+ <element name="front"><rect><color red="0.6" green="0.63" blue="0.65" /></rect></element>
+ <element name="vfd_back"><rect><color red="0.29" green="0.33" blue="0.32" /></rect></element>
+ <element name="button_digit_back"><rect><color red="0.75" green="0.75" blue="0.75" /></rect></element>
+
+ <element name="hl" defstate="0">
+ <text string="">
+ <bounds x="0.0" y="0.0" width="1.0" height="1.0" />
+ <color red="0.0" green="0.0" blue="0.0" />
+ </text>
+ <rect state="1">
+ <bounds x="0.0" y="0.0" width="1.0" height="1.0" />
+ <color red="0.0" green="0.0" blue="0.5" />
+ </rect>
+ </element>
+
+ <element name="button_dcv_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="DCV"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_acv_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="ACV"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_2w_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="2W"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_4w_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="4W"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_dca_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="DCA"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_aca_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="ACA"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_shift_text">
+ <rect><color red="0.3" green="0.35" blue="0.85" /></rect>
+ <text string=""><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_auto_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="AUTO"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_up_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="UP"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_dn_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="DN"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_int_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="INT"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_sgl_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="SGL"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_srq_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="SRQ"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+ <element name="button_local_text">
+ <rect><color red="0.75" green="0.75" blue="0.75" /></rect>
+ <text string="LCL"><color red="0.0" green="0.0" blue="0.0" /></text>
+ </element>
+
+
+ <view name="Internal Layout">
+ <backdrop name="case_bg" element="front"> <bounds x=" 0" y=" 0" width="126" height="70"/></backdrop>
+ <!-- 2 rows of 7 buttons , but electrical switch matrix is different -->
+ <backdrop name="button_dcv" element="button_digit_back"><bounds x="5" y="35" width="10" height="10"/></backdrop>
+ <backdrop name="button_acv" element="button_digit_back"><bounds x="20" y="35" width="10" height="10"/></backdrop>
+ <backdrop name="button_2w" element="button_digit_back"><bounds x="35" y="35" width="10" height="10"/></backdrop>
+ <backdrop name="button_4w" element="button_digit_back"><bounds x="50" y="35" width="10" height="10"/></backdrop>
+ <backdrop name="button_dca" element="button_digit_back"><bounds x="65" y="35" width="10" height="10"/></backdrop>
+ <backdrop name="button_aca" element="button_digit_back"><bounds x="80" y="35" width="10" height="10"/></backdrop>
+ <backdrop name="button_shift" element="button_digit_back"><bounds x="95" y="35" width="10" height="10"/></backdrop>
+
+ <backdrop name="button_auto" element="button_digit_back"><bounds x="5" y="50" width="10" height="10"/></backdrop>
+ <backdrop name="button_up" element="button_digit_back"><bounds x="20" y="50" width="10" height="10"/></backdrop>
+ <backdrop name="button_dn" element="button_digit_back"><bounds x="35" y="50" width="10" height="10"/></backdrop>
+ <backdrop name="button_int" element="button_digit_back"><bounds x="50" y="50" width="10" height="10"/></backdrop>
+ <backdrop name="button_sgl" element="button_digit_back"><bounds x="65" y="50" width="10" height="10"/></backdrop>
+ <backdrop name="button_srq" element="button_digit_back"><bounds x="80" y="50" width="10" height="10"/></backdrop>
+ <backdrop name="button_local" element="button_digit_back"><bounds x="95" y="50" width="10" height="10"/></backdrop>
+
+ <backdrop name="vfd_backdrop" element="vfd_back"> <bounds x="0" y="0" width="126" height="32"/></backdrop>
+
+ <bezel name="vfd0" element="vfd0" state="0"><bounds x=" 9" y="9" width="9" height="14"/></bezel>
+ <bezel name="vfd1" element="vfd0" state="0"><bounds x="18" y="9" width="9" height="14"/></bezel>
+ <bezel name="vfd2" element="vfd0" state="0"><bounds x="27" y="9" width="9" height="14"/></bezel>
+ <bezel name="vfd3" element="vfd0" state="0"><bounds x="36" y="9" width="9" height="14"/></bezel>
+ <bezel name="vfd4" element="vfd0" state="0"><bounds x="45" y="9" width="9" height="14"/></bezel>
+ <bezel name="vfd5" element="vfd0" state="0"><bounds x="54" y="9" width="9" height="14"/></bezel>
+ <bezel name="vfd6" element="vfd0" state="0"><bounds x="63" y="9" width="9" height="14"/></bezel>
+ <bezel name="vfd7" element="vfd0" state="0"><bounds x="72" y="9" width="9" height="14"/></bezel>
+ <bezel name="vfd8" element="vfd0" state="0"><bounds x="81" y="9" width="9" height="14"/></bezel>
+ <bezel name="vfd9" element="vfd0" state="0"><bounds x="90" y="9" width="9" height="14"/></bezel>
+ <bezel name="vfd10" element="vfd0" state="0"><bounds x="99" y="9" width="9" height="14"/></bezel>
+ <bezel name="vfd11" element="vfd0" state="0"><bounds x="108" y="9" width="9" height="14"/></bezel>
+
+ <bezel name="button_dcv_text1" element="button_dcv_text"> <bounds x="5" y="37" width="10" height="8"/></bezel>
+ <bezel name="button_acv_text1" element="button_acv_text"> <bounds x="20" y="37" width="10" height="8"/></bezel>
+ <bezel name="button_2w_text1" element="button_2w_text"> <bounds x="35" y="37" width="10" height="8"/></bezel>
+ <bezel name="button_4w_text1" element="button_4w_text"> <bounds x="50" y="37" width="10" height="8"/></bezel>
+ <bezel name="button_dca_text1" element="button_dca_text"> <bounds x="65" y="37" width="10" height="8"/></bezel>
+ <bezel name="button_aca_text1" element="button_aca_text"> <bounds x="80" y="37" width="10" height="8"/></bezel>
+ <bezel name="button_shift_text1" element="button_shift_text"><bounds x="95" y="35" width="10" height="10"/></bezel>
+
+ <bezel name="button_auto_text1" element="button_auto_text"> <bounds x="5" y="52" width="10" height="8"/></bezel>
+ <bezel name="button_up_text1" element="button_up_text"> <bounds x="20" y="52" width="10" height="8"/></bezel>
+ <bezel name="button_dn_text1" element="button_dn_text"> <bounds x="35" y="52" width="10" height="8"/></bezel>
+ <bezel name="button_int_text1" element="button_int_text"> <bounds x="50" y="52" width="10" height="8"/></bezel>
+ <bezel name="button_sgl_text1" element="button_sgl_text"> <bounds x="65" y="52" width="10" height="8"/></bezel>
+ <bezel name="button_srq_text1" element="button_srq_text"> <bounds x="80" y="52" width="10" height="8"/></bezel>
+ <bezel name="button_local_text1" element="button_local_text"> <bounds x="95" y="52" width="10" height="8"/></bezel>
+
+
+<!-- to match the schematics, we consider Col.0-3 to be driven by P14-17. The value in "inputmask" is the one read as "p1 & 0x0F", i.e. P13-P10. -->
+<!-- col.0 : (nc)|shift|ACA|DCA
+ col.1 : 4W|2W|ACV|DCV
+ col.2 : int|dn|up|auto
+ col.3 : (nc)|loc|srq|sgl -->
+ <bezel element="hl" inputtag="COL.0" inputmask="0x01"><bounds x="65" y="35" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <bezel element="hl" inputtag="COL.0" inputmask="0x02"><bounds x="80" y="35" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <bezel element="hl" inputtag="COL.0" inputmask="0x04"><bounds x="95" y="35" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <!--bezel element="hl" inputtag="COL.0" inputmask="0x08"><bounds x="" y="54" width="10" height="10" /><color alpha="0.2" /></bezel -->
+ <bezel element="hl" inputtag="COL.1" inputmask="0x01"><bounds x="5" y="35" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <bezel element="hl" inputtag="COL.1" inputmask="0x02"><bounds x="20" y="35" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <bezel element="hl" inputtag="COL.1" inputmask="0x04"><bounds x="35" y="35" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <bezel element="hl" inputtag="COL.1" inputmask="0x08"><bounds x="50" y="35" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <bezel element="hl" inputtag="COL.2" inputmask="0x01"><bounds x="5" y="50" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <bezel element="hl" inputtag="COL.2" inputmask="0x02"><bounds x="20" y="50" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <bezel element="hl" inputtag="COL.2" inputmask="0x04"><bounds x="35" y="50" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <bezel element="hl" inputtag="COL.2" inputmask="0x08"><bounds x="50" y="50" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <bezel element="hl" inputtag="COL.3" inputmask="0x01"><bounds x="65" y="50" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <bezel element="hl" inputtag="COL.3" inputmask="0x02"><bounds x="80" y="50" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <bezel element="hl" inputtag="COL.3" inputmask="0x04"><bounds x="95" y="50" width="10" height="10" /><color alpha="0.2" /></bezel>
+ <!--bezel element="hl" inputtag="COL.3" inputmask="0x08"><bounds x="" y="5" width="10" height="10" /><color alpha="0.2" /></bezel -->
+ </view>
+</mamelayout>
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 5cda21c3d53..4607da87a23 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -15314,6 +15314,9 @@ hp2622a //
@source:hp2640.cpp
hp2645 //
+@source:hp3478a.cpp
+hp3478a //
+
@source:hp48.cpp
hp38g //
hp39g //