summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author R. Belmont <rb6502@users.noreply.github.com>2017-10-25 15:33:47 -0400
committer GitHub <noreply@github.com>2017-10-25 15:33:47 -0400
commitdab5e961785562070f9943730ef8689fca966bdd (patch)
treeeefa731f40da7b29b89ca05c64cdba78f7a7ce4b
parentc18aec6ee689b35786cc3056be8d469821687e89 (diff)
parenta020b82a7da8e35fef03af376b0f0b8448c64537 (diff)
Merge pull request #2744 from pmackinlay/interpro
interpro: expansion bus/slots and keyboard
-rw-r--r--scripts/src/bus.lua30
-rw-r--r--scripts/target/mame/mess.lua2
-rw-r--r--src/devices/bus/interpro/keyboard/hle.cpp461
-rw-r--r--src/devices/bus/interpro/keyboard/hle.h113
-rw-r--r--src/devices/bus/interpro/keyboard/keyboard.cpp67
-rw-r--r--src/devices/bus/interpro/keyboard/keyboard.h71
-rw-r--r--src/devices/bus/interpro/sr/gt.cpp193
-rw-r--r--src/devices/bus/interpro/sr/gt.h102
-rw-r--r--src/devices/bus/interpro/sr/sr.cpp363
-rw-r--r--src/devices/bus/interpro/sr/sr.h141
-rw-r--r--src/devices/bus/interpro/sr/sr_cards.cpp24
-rw-r--r--src/devices/bus/interpro/sr/sr_cards.h12
12 files changed, 1579 insertions, 0 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua
index 29605ec7213..ad79fd7d66c 100644
--- a/scripts/src/bus.lua
+++ b/scripts/src/bus.lua
@@ -3238,3 +3238,33 @@ if (BUSES["PSI_KEYBOARD"]~=null) then
MAME_DIR .. "src/devices/bus/psi_kbd/hle.h",
}
end
+
+---------------------------------------------------
+--
+--@src/devices/bus/interpro/sr/sr.h,BUSES["INTERPRO_SR"] = true
+---------------------------------------------------
+
+if (BUSES["INTERPRO_SR"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/bus/interpro/sr/sr.cpp",
+ MAME_DIR .. "src/devices/bus/interpro/sr/sr.h",
+ MAME_DIR .. "src/devices/bus/interpro/sr/sr_cards.cpp",
+ MAME_DIR .. "src/devices/bus/interpro/sr/sr_cards.h",
+ MAME_DIR .. "src/devices/bus/interpro/sr/gt.cpp",
+ MAME_DIR .. "src/devices/bus/interpro/sr/gt.h",
+ }
+end
+
+---------------------------------------------------
+--
+--@src/devices/bus/interpro/keyboard/keyboard.h,BUSES["INTERPRO_KEYBOARD"] = true
+---------------------------------------------------
+
+if (BUSES["INTERPRO_KEYBOARD"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/bus/interpro/keyboard/keyboard.cpp",
+ MAME_DIR .. "src/devices/bus/interpro/keyboard/keyboard.h",
+ MAME_DIR .. "src/devices/bus/interpro/keyboard/hle.cpp",
+ MAME_DIR .. "src/devices/bus/interpro/keyboard/hle.h"
+ }
+end
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index 323cf42c674..1c1e01aea54 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -675,6 +675,8 @@ BUSES["HPDIO"] = true
BUSES["IEEE488"] = true
BUSES["IMI7000"] = true
BUSES["INTELLEC4"] = true
+BUSES["INTERPRO_SR"] = true
+BUSES["INTERPRO_KEYBOARD"] = true
BUSES["INTV"] = true
BUSES["INTV_CTRL"] = true
BUSES["IQ151"] = true
diff --git a/src/devices/bus/interpro/keyboard/hle.cpp b/src/devices/bus/interpro/keyboard/hle.cpp
new file mode 100644
index 00000000000..a455f9227e3
--- /dev/null
+++ b/src/devices/bus/interpro/keyboard/hle.cpp
@@ -0,0 +1,461 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * A high level emulation implementation of the Intergraph InterPro keyboard,
+ * largely copied from the sunkbd and psi hle implementations.
+ *
+ * These keyboards have two primary banks of keys. The lower bank consists of
+ * a total of 67 regular keyboard keyswitches plus a numeric keypad with a
+ * further 18 keys. The upper bank consists of 57 membrane-style programmable
+ * function keys in groups of 9, 36 and 12 from left to right.
+ *
+ * The following describes the key labels and positions according to the
+ * standard US English keyboard layout. It's unknown what other variations
+ * existed at this point.
+ *
+ * Upper bank keys indicated here with asterisks are printed in white, as are
+ * all the A*, B* and C* keys; all the others are printed in brown.
+ *
+ * Setup* Home* 2nd A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 2nd
+ * Help Clear F* F*
+ * Screen
+ *
+ * Find Insert Print B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 B16
+ * Here Screen*
+ * Remove
+ *
+ * Select Prev Next C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13 C14 C15 C16
+ * Screen Screen
+ *
+ *
+ * In between the banks on the right hand side, there is a row of LEDs, the
+ * first three are pictures, rather than the descriptions given here).
+ *
+ * Disk Lock ----- L1 L2 L3 L4
+ *
+ * Lower bank keys have up to 3 labels, in shifted and unshifted positions, and
+ * in red on the front face of the key-cap.
+ *
+ * Esc ~ ! @ # $ % ^ & * ( ) _ + Back Delete PF1 PF2 PF3 PF4
+ * ` 1 2 3 4 5 6 7 8 9 0 - = Space ± ÷ × +
+ * Esc Num Lk ScrlLk Sys
+ *
+ * Alt Tab Q W E R T Y U I O P { } 7 8 9 _
+ * Mode [ ]
+ * Home ↑ Pg Up Prt Sc
+ *
+ * Ctrl Caps A S D F G H J K L : " | Return 4 5 6 ,
+ * Lock
+ * ← → −
+ *
+ * Shift > Z X C V B N M , . ? Shift ▲ 1 2 3 =
+ * < ■
+ * End ↓ Pg Dn
+ *
+ * Hold Super- Line Repeat ◄ ■ ► 0 ◦
+ * Screen impose Feed ▼ . Enter
+ * Ins Del +
+ *
+ * Alt Mode and Caps Lock keys have locking switches, capturing the key in the
+ * depressed position, as well as physical leds visible on the keycaps
+ * themselves. The keyboard also has two physical buttons on the back face of
+ * the keyboard, circular button labelled Boot, and a square one labelled Reset.
+ *
+ * The keyboard uses a 1200bps serial protocol to communicate with the host,
+ * with 1 start bit, 8 data bits, 1 stop bit, and even parity. The protocol
+ * as far as is known consists of the following sequences:
+ *
+ * From Host Purpose Expected Response
+ * --------- ------- -----------------
+ * <esc> D Diagnostic 0xff <status>, where status bits are set to
+ * indicate diagnostic failure source: 0x8=EPROM
+ * checksum, 0x10=RAM error, 0x20=ROM checksum
+ *
+ * <esc> B ( Bell On None
+ *
+ * <esc> B ) Bell Off None
+ *
+ * <esc> B <byte> Unknown None
+ *
+ * The keyboard has a keyboard click function, and the LED indicators described
+ * earlier, meaning that there are additional commands to enable and disable
+ * these functions.
+ *
+ * The keyboard transmits ASCII codes corresponding to the keycap labels for
+ * keys which map to the ASCII character set. Modifiers are applied by the
+ * keyboard itself, and do not generate make/break codes of their own.
+ *
+ * The following non-ASCII sequences are recognised in the system software,
+ * and likely correspond to specific keyboard keys or buttons:
+ *
+ * Sequence Function
+ * -------- --------
+ * <esc> ^L Reboot, possibly maps to Reboot button
+ * <esc> ^M Unknown
+ * <esc> ^N Unknown, but operates as a toggle
+ * <esc> ^U Unknown
+ *
+ * TODO
+ * - unmapped keys
+ * - auto-repeat
+ * - key click and LED commands
+ * - alternative layouts
+ *
+ */
+#include "emu.h"
+#include "hle.h"
+
+#include "machine/keyboard.ipp"
+#include "speaker.h"
+
+#define VERBOSE 0
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE_NS(INTERPRO_HLE_EN_US_KEYBOARD, bus::interpro::keyboard, hle_en_us_device, "kbd_hle_en_us", "InterPro Keyboard (HLE, US English)")
+
+namespace bus { namespace interpro { namespace keyboard {
+
+namespace {
+
+ u8 const TRANSLATION_TABLE[4][5][16] =
+ {
+ // unshifted
+ {
+ { 0x1b, 0x60, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2d, 0x3d, 0x08, 0x7f }, // 0
+ { 0x00, 0x09, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6f, 0x70, 0x5b, 0x5d, 0x00, 0x00 }, // 1
+ { 0x00, 0x00, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6a, 0x6b, 0x6c, 0x3b, 0x27, 0x5c, 0x0d, 0x00 }, // 2
+ { 0x00, 0x3c, 0x7a, 0x78, 0x63, 0x76, 0x62, 0x6e, 0x6d, 0x2c, 0x2e, 0x2f, 0x00, 0x00, 0x00, 0x00 }, // 3
+ { 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 4
+ },
+ // shifted
+ {
+ { 0x1b, 0x7e, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5e, 0x26, 0x2a, 0x28, 0x29, 0x5f, 0x2b, 0x08, 0x7f }, // 0
+ { 0x00, 0x09, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4f, 0x50, 0x7b, 0x7d, 0x00, 0x00 }, // 1
+ { 0x00, 0x00, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4a, 0x4b, 0x4c, 0x3a, 0x22, 0x7c, 0x0d, 0x00 }, // 2
+ { 0x00, 0x3e, 0x5a, 0x58, 0x43, 0x56, 0x42, 0x4e, 0x4d, 0x2c, 0x2e, 0x3f, 0x00, 0x00, 0x00, 0x00 }, // 3
+ { 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 4
+ },
+ // unshifted-control
+ {
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0
+ { 0x00, 0x00, 0x11, 0x17, 0x05, 0x12, 0x14, 0x19, 0x15, 0x09, 0x0f, 0x10, 0x1b, 0x1d, 0x00, 0x00 }, // 1
+ { 0x00, 0x00, 0x01, 0x13, 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, 0x00, 0x00, 0x1c, 0x00, 0x00 }, // 2
+ { 0x00, 0x00, 0x1a, 0x18, 0x03, 0x16, 0x02, 0x0e, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 3
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 4
+ },
+ // shifted-control
+ {
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0
+ { 0x00, 0x00, 0x11, 0x17, 0x05, 0x12, 0x14, 0x19, 0x15, 0x09, 0x0f, 0x10, 0x1b, 0x1d, 0x00, 0x00 }, // 1
+ { 0x00, 0x00, 0x01, 0x13, 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, 0x00, 0x00, 0x1c, 0x00, 0x00 }, // 2
+ { 0x00, 0x00, 0x1a, 0x18, 0x03, 0x16, 0x02, 0x0e, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 3
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 4
+ }
+ };
+
+INPUT_PORTS_START(interpro_en_us)
+
+ PORT_START("modifiers")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_NAME("Control") PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_NAME("Shift") PORT_CHAR(UCHAR_SHIFT_1)
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_NAME("Caps Lock") PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
+
+ PORT_START("row_0")
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_NAME("Esc") PORT_CHAR(UCHAR_MAMEKEY(ESC))
+ PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~')
+ PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
+ PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
+ PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
+ PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
+ PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
+ PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
+ PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
+ PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
+ PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
+ PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
+ PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
+ PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
+ PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("Backspace") PORT_CHAR(8)
+ PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_NAME("Delete") PORT_CHAR(UCHAR_MAMEKEY(DEL))
+
+ PORT_START("row_1")
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED) // "Alt Mode"
+ PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_NAME("Tab") PORT_CHAR(9)
+ PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
+ PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
+ PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
+ PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
+ PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
+ PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
+ PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
+ PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
+ PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
+ PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
+ PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
+ PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
+
+ PORT_START("row_2")
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED) // CTRL
+ PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_UNUSED) // LOCK
+ PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
+ PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
+ PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
+ PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
+ PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
+ PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
+ PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
+ PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
+ PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
+ PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
+ PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
+ PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
+ PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_NAME("Return") PORT_CHAR(13)
+
+ PORT_START("row_3")
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED) // LSHIFT
+ PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('<') PORT_CHAR('>')
+ PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
+ PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
+ PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
+ PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
+ PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
+ PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
+ PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
+ PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',')
+ PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.')
+ PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
+ PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_UNUSED) // RSHIFT
+ PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_NAME("Up") PORT_CHAR(UCHAR_MAMEKEY(UP))
+
+ PORT_START("row_4")
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED) // "Hold Screen"
+ PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_UNUSED) // "Superimpose"
+ PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_UNUSED) // "Line Feed"
+ PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Space") PORT_CHAR(UCHAR_MAMEKEY(SPACE))
+ PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNUSED) // "Repeat"
+ PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Left") PORT_CHAR(UCHAR_MAMEKEY(LEFT))
+ PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_NAME("Down") PORT_CHAR(UCHAR_MAMEKEY(DOWN))
+ PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Right") PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
+
+INPUT_PORTS_END
+
+INPUT_PORTS_START(hle_en_us_device)
+ PORT_INCLUDE(interpro_en_us)
+INPUT_PORTS_END
+
+} // anonymous namespace
+
+hle_device_base::hle_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, type, tag, owner, clock)
+ , device_buffered_serial_interface(mconfig, *this)
+ , device_interpro_keyboard_port_interface(mconfig, *this)
+ , device_matrix_keyboard_interface(mconfig, *this, "row_0", "row_1", "row_2", "row_3", "row_4")
+ , m_click_timer(nullptr)
+ , m_beeper(*this, "beeper")
+ , m_make_count(0U)
+ , m_rx_state(RX_IDLE)
+ , m_keyclick(0U)
+ , m_beeper_state(0U)
+{
+}
+
+hle_device_base::~hle_device_base()
+{
+}
+
+WRITE_LINE_MEMBER(hle_device_base::input_txd)
+{
+ device_buffered_serial_interface::rx_w(state);
+}
+
+MACHINE_CONFIG_MEMBER(hle_device_base::device_add_mconfig)
+ MCFG_SPEAKER_STANDARD_MONO("bell")
+ MCFG_SOUND_ADD("beeper", BEEP, ATTOSECONDS_TO_HZ(480 * ATTOSECONDS_PER_MICROSECOND))
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "bell", 1.0)
+MACHINE_CONFIG_END
+
+void hle_device_base::device_start()
+{
+ m_click_timer = timer_alloc(CLICK_TIMER_ID);
+
+ save_item(NAME(m_make_count));
+ save_item(NAME(m_rx_state));
+ save_item(NAME(m_keyclick));
+ save_item(NAME(m_beeper_state));
+}
+
+void hle_device_base::device_reset()
+{
+ // initialise state
+ clear_fifo();
+ m_make_count = 0U;
+ m_rx_state = RX_IDLE;
+ m_keyclick = 0U;
+ m_beeper_state = 0x00U;
+
+ // configure device_buffered_serial_interface
+ set_data_frame(START_BIT_COUNT, DATA_BIT_COUNT, PARITY, STOP_BITS);
+ set_rate(BAUD);
+ receive_register_reset();
+ transmit_register_reset();
+
+ // start with keyboard LEDs off
+ machine().output().set_led_value(LED_DISK, 0);
+ machine().output().set_led_value(LED_LOCK, 0);
+ machine().output().set_led_value(LED_UNKNOWN, 0);
+ machine().output().set_led_value(LED_L1, 0);
+ machine().output().set_led_value(LED_L2, 0);
+ machine().output().set_led_value(LED_L3, 0);
+ machine().output().set_led_value(LED_L4, 0);
+
+ // no beep
+ m_click_timer->reset();
+
+ // kick the base
+ reset_key_state();
+ start_processing(attotime::from_hz(1'200));
+}
+
+void hle_device_base::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case CLICK_TIMER_ID:
+ m_beeper_state &= ~u8(BEEPER_CLICK);
+ m_beeper->set_state(m_beeper_state ? 1 : 0);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void hle_device_base::tra_callback()
+{
+ output_rxd(transmit_register_get_data_bit());
+}
+
+void hle_device_base::tra_complete()
+{
+ if (fifo_full())
+ start_processing(attotime::from_hz(1'200));
+
+ device_buffered_serial_interface::tra_complete();
+}
+
+void hle_device_base::key_make(u8 row, u8 column)
+{
+ // we should have stopped processing if we filled the FIFO
+ assert(!fifo_full());
+
+ // send the make code, click if desired
+ transmit_byte(translate(row, column));
+ if (m_keyclick)
+ {
+ m_beeper_state |= u8(BEEPER_CLICK);
+ m_beeper->set_state(m_beeper_state ? 1 : 0);
+ m_click_timer->reset(attotime::from_msec(5));
+ }
+
+ // count keys
+ ++m_make_count;
+ assert(m_make_count);
+}
+
+void hle_device_base::key_break(u8 row, u8 column)
+{
+ // we should have stopped processing if we filled the FIFO
+ assert(!fifo_full());
+ assert(m_make_count);
+
+ --m_make_count;
+
+ // check our counting
+ assert(are_all_keys_up() == !bool(m_make_count));
+}
+
+void hle_device_base::transmit_byte(u8 byte)
+{
+ LOG("transmit_byte 0x%02x\n", byte);
+ device_buffered_serial_interface::transmit_byte(byte);
+ if (fifo_full())
+ stop_processing();
+}
+
+void hle_device_base::received_byte(u8 byte)
+{
+ LOG("received_byte 0x%02x\n", byte);
+
+ switch (m_rx_state)
+ {
+ case RX_ESC:
+ switch (byte)
+ {
+ case 'B': // bell
+ m_rx_state = RX_BELL;
+ break;
+
+ case 'D': // diagnostic
+ transmit_byte(0xff);
+ transmit_byte(0x00);
+
+ m_rx_state = RX_IDLE;
+ break;
+ }
+ break;
+
+ case RX_BELL:
+ switch (byte)
+ {
+ case '(':
+ LOG("bell on\n");
+ m_beeper_state |= u8(BEEPER_BELL);
+ m_beeper->set_state(m_beeper_state ? 1 : 0);
+ break;
+
+ case ')':
+ LOG("bell off\n");
+ m_beeper_state &= ~u8(BEEPER_BELL);
+ m_beeper->set_state(m_beeper_state ? 1 : 0);
+ break;
+
+ default:
+ // FIXME: boot code sends 0x8, unknown meaning
+ break;
+ }
+ m_rx_state = RX_IDLE;
+ break;
+
+ case RX_IDLE:
+ if (byte == 0x1b)
+ m_rx_state = RX_ESC;
+ break;
+ }
+}
+
+hle_en_us_device::hle_en_us_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : hle_device_base(mconfig, INTERPRO_HLE_EN_US_KEYBOARD, tag, owner, clock),
+ m_modifiers(*this, "modifiers")
+{
+}
+
+ioport_constructor hle_en_us_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME(hle_en_us_device);
+}
+
+u8 hle_en_us_device::translate(u8 row, u8 column)
+{
+ u8 const modifiers(m_modifiers->read());
+
+ bool const ctrl(modifiers & 0x01);
+ bool const shift(bool(modifiers & 0x02) || (bool(modifiers & 0x04)));
+ bool const ctrl_shift(ctrl && shift);
+
+ unsigned const map(ctrl_shift ? 3 : ctrl ? 2 : shift ? 1 : 0);
+
+ return TRANSLATION_TABLE[map][row][column];
+}
+
+} } } // namespace bus::interpro::keyboard
diff --git a/src/devices/bus/interpro/keyboard/hle.h b/src/devices/bus/interpro/keyboard/hle.h
new file mode 100644
index 00000000000..fac0b48d3be
--- /dev/null
+++ b/src/devices/bus/interpro/keyboard/hle.h
@@ -0,0 +1,113 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+#ifndef MAME_DEVICES_INTERPRO_KEYBOARD_HLE_H
+#define MAME_DEVICES_INTERPRO_KEYBOARD_HLE_H
+
+#pragma once
+
+#include "keyboard.h"
+#include "machine/keyboard.h"
+#include "sound/beep.h"
+
+namespace bus { namespace interpro { namespace keyboard {
+
+class hle_device_base
+ : public device_t
+ , public device_buffered_serial_interface<16U>
+ , public device_interpro_keyboard_port_interface
+ , protected device_matrix_keyboard_interface<5U>
+{
+public:
+ virtual ~hle_device_base() override;
+
+ virtual DECLARE_WRITE_LINE_MEMBER(input_txd) override;
+
+protected:
+ // constructor/destructor
+ hle_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock);
+
+ // device overrides
+ virtual void device_add_mconfig(machine_config &config) override;
+ 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;
+
+ // device_buffered_serial_interface overrides
+ virtual void tra_callback() override;
+ virtual void tra_complete() override;
+
+ // device_matrix_keyboard_interface overrides
+ virtual void key_make(u8 row, u8 column) override;
+ virtual void key_break(u8 row, u8 column) override;
+
+ // customised transmit_byte method
+ void transmit_byte(u8 byte);
+ virtual u8 translate(u8 row, u8 column) = 0;
+
+private:
+ enum {
+ CLICK_TIMER_ID = 30'000
+ };
+
+ enum : int {
+ LED_DISK = 0,
+ LED_LOCK,
+ LED_UNKNOWN,
+ LED_L1,
+ LED_L2,
+ LED_L3,
+ LED_L4
+ };
+
+ enum : u8 {
+ BEEPER_BELL = 0x01U,
+ BEEPER_CLICK = 0x02U
+ };
+
+ enum : u8 {
+ RX_IDLE,
+ RX_ESC,
+ RX_BELL
+ };
+
+ enum : u8 {
+ COMMAND_RESET = 0x01U,
+ COMMAND_BELL_ON = 0x02U,
+ COMMAND_BELL_OFF = 0x03U,
+ COMMAND_CLICK_ON = 0x0aU,
+ COMMAND_CLICK_OFF = 0x0bU,
+ COMMAND_LED = 0x0eU,
+ COMMAND_LAYOUT = 0x0fU
+ };
+
+ // device_buffered_serial_interface overrides
+ virtual void received_byte(u8 byte) override;
+
+ emu_timer *m_click_timer;
+ required_device<beep_device> m_beeper;
+
+ u8 m_make_count;
+ u8 m_rx_state;
+
+ u8 m_keyclick;
+ u8 m_beeper_state;
+};
+
+
+class hle_en_us_device : public hle_device_base
+{
+public:
+ hle_en_us_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
+
+ virtual ioport_constructor device_input_ports() const override;
+
+ virtual u8 translate(u8 row, u8 column) override;
+
+ required_ioport m_modifiers;
+};
+
+} } } // namespace bus::interpro::keyboard
+
+DECLARE_DEVICE_TYPE_NS(INTERPRO_HLE_EN_US_KEYBOARD, bus::interpro::keyboard, hle_en_us_device)
+
+#endif // MAME_DEVICES_INTERPRO_KEYBOARD_HLE_H
diff --git a/src/devices/bus/interpro/keyboard/keyboard.cpp b/src/devices/bus/interpro/keyboard/keyboard.cpp
new file mode 100644
index 00000000000..210b509277c
--- /dev/null
+++ b/src/devices/bus/interpro/keyboard/keyboard.cpp
@@ -0,0 +1,67 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#include "emu.h"
+#include "keyboard.h"
+
+#define VERBOSE 0
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(INTERPRO_KEYBOARD_PORT, interpro_keyboard_port_device, "interpro_keyboard_port", "InterPro Keyboard Port")
+
+int const device_interpro_keyboard_port_interface::START_BIT_COUNT;
+int const device_interpro_keyboard_port_interface::DATA_BIT_COUNT;
+device_serial_interface::parity_t const device_interpro_keyboard_port_interface::PARITY;
+device_serial_interface::stop_bits_t const device_interpro_keyboard_port_interface::STOP_BITS;
+int const device_interpro_keyboard_port_interface::BAUD;
+
+interpro_keyboard_port_device::interpro_keyboard_port_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock)
+ : interpro_keyboard_port_device(mconfig, INTERPRO_KEYBOARD_PORT, tag, owner, clock)
+{
+}
+
+interpro_keyboard_port_device::interpro_keyboard_port_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock)
+ , device_slot_interface(mconfig, *this)
+ , m_rxd_handler(*this)
+ , m_dev(nullptr)
+{
+}
+
+interpro_keyboard_port_device::~interpro_keyboard_port_device()
+{
+}
+
+void interpro_keyboard_port_device::device_config_complete()
+{
+ m_dev = dynamic_cast<device_interpro_keyboard_port_interface *>(get_card_device());
+}
+
+void interpro_keyboard_port_device::device_start()
+{
+ m_rxd_handler.resolve_safe();
+
+ save_item(NAME(m_rxd));
+}
+
+WRITE_LINE_MEMBER(interpro_keyboard_port_device::write_txd)
+{
+ if (m_dev)
+ m_dev->input_txd(state);
+}
+
+device_interpro_keyboard_port_interface::device_interpro_keyboard_port_interface(machine_config const &mconfig, device_t &device)
+ : device_slot_card_interface(mconfig, device)
+ , m_port(dynamic_cast<interpro_keyboard_port_device *>(device.owner()))
+{
+}
+
+device_interpro_keyboard_port_interface::~device_interpro_keyboard_port_interface()
+{
+}
+
+#include "hle.h"
+
+SLOT_INTERFACE_START(interpro_keyboard_devices)
+ SLOT_INTERFACE("hle_en_us", INTERPRO_HLE_EN_US_KEYBOARD)
+SLOT_INTERFACE_END
diff --git a/src/devices/bus/interpro/keyboard/keyboard.h b/src/devices/bus/interpro/keyboard/keyboard.h
new file mode 100644
index 00000000000..ca3b1a2eb82
--- /dev/null
+++ b/src/devices/bus/interpro/keyboard/keyboard.h
@@ -0,0 +1,71 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+#ifndef MAME_BUS_INTERPRO_KEYBOARD_KEYBOARD_H
+#define MAME_BUS_INTERPRO_KEYBOARD_KEYBOARD_H
+
+#pragma once
+
+#define MCFG_INTERPRO_KEYBOARD_PORT_ADD(tag, slot_intf, def_slot) \
+ MCFG_DEVICE_ADD(tag, INTERPRO_KEYBOARD_PORT, 0) \
+ MCFG_DEVICE_SLOT_INTERFACE(slot_intf, def_slot, false)
+
+#define MCFG_INTERPRO_KEYBOARD_RXD_HANDLER(cb) \
+ devcb = &interpro_keyboard_port_device::set_rxd_handler(*device, DEVCB_##cb);
+
+class device_interpro_keyboard_port_interface;
+
+class interpro_keyboard_port_device : public device_t, public device_slot_interface
+{
+ friend class device_interpro_keyboard_port_interface;
+
+public:
+ interpro_keyboard_port_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock);
+ virtual ~interpro_keyboard_port_device();
+
+ // static configuration helpers
+ template <class Object> static devcb_base &set_rxd_handler(device_t &device, Object &&cb) { return downcast<interpro_keyboard_port_device &>(device).m_rxd_handler.set_callback(std::forward<Object>(cb)); }
+
+ DECLARE_WRITE_LINE_MEMBER(write_txd);
+
+protected:
+ interpro_keyboard_port_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, uint32_t clock);
+
+ virtual void device_start() override;
+ virtual void device_config_complete() override;
+
+ int m_rxd;
+
+ devcb_write_line m_rxd_handler;
+
+private:
+ device_interpro_keyboard_port_interface *m_dev;
+};
+
+class device_interpro_keyboard_port_interface : public device_slot_card_interface
+{
+ friend class interpro_keyboard_port_device;
+
+public:
+ virtual ~device_interpro_keyboard_port_interface() override;
+
+ virtual DECLARE_WRITE_LINE_MEMBER(input_txd) { }
+
+ DECLARE_WRITE_LINE_MEMBER(output_rxd) { m_port->m_rxd = state; m_port->m_rxd_handler(state); }
+
+protected:
+ device_interpro_keyboard_port_interface(machine_config const &mconfig, device_t &device);
+
+ interpro_keyboard_port_device *m_port;
+
+ static constexpr int START_BIT_COUNT = 1;
+ static constexpr int DATA_BIT_COUNT = 8;
+ static constexpr device_serial_interface::parity_t PARITY = device_serial_interface::PARITY_EVEN;
+ static constexpr device_serial_interface::stop_bits_t STOP_BITS = device_serial_interface::STOP_BITS_1;
+ static constexpr int BAUD = 1'200;
+};
+
+DECLARE_DEVICE_TYPE(INTERPRO_KEYBOARD_PORT, interpro_keyboard_port_device)
+
+SLOT_INTERFACE_EXTERN(interpro_keyboard_devices);
+
+#endif // MAME_BUS_INTERPRO_KEYBOARD_KEYBOARD_H
diff --git a/src/devices/bus/interpro/sr/gt.cpp b/src/devices/bus/interpro/sr/gt.cpp
new file mode 100644
index 00000000000..c4ed9109792
--- /dev/null
+++ b/src/devices/bus/interpro/sr/gt.cpp
@@ -0,0 +1,193 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * An emulation of GT graphics for Intergraph InterPro.
+ *
+ * TODO
+ * - control register (including primary/secondary buffer selection)
+ * - DP8510V BITBLT unit
+ * - custom Bresenham line drawing ASIC
+ * - support GT+, GTII, GTII+
+ * - reset behaviour
+ */
+
+#include "emu.h"
+#include "screen.h"
+
+#include "gt.h"
+
+#define VERBOSE 0
+#include "logmacro.h"
+
+DEVICE_ADDRESS_MAP_START(map, 32, mpcb963_device)
+ AM_RANGE(0x00000000, 0x0000007f) AM_READ(idprom_r)
+ AM_RANGE(0x00000080, 0x0000008f) AM_DEVICE8("ramdac0", bt459_device, map, 0xff)
+ AM_RANGE(0x000000b0, 0x000000b3) AM_READWRITE16(control_r, control_w, 0xffff)
+ //AM_RANGE(0x000000d4, 0x000000d7) AM_READWRITE8(, 0xff) // currently unknown
+ //AM_RANGE(0x000000a0, 0x000000a0) AM_READWRITE8(, 0xff) // currently unknown
+ AM_RANGE(0x00400000, 0x005fffff) AM_READWRITE(vram_r, vram_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map, 32, mpcba79_device)
+ AM_RANGE(0x00000000, 0x0000007f) AM_READ(idprom_r)
+ AM_RANGE(0x00000080, 0x0000008f) AM_DEVICE8("ramdac0", bt459_device, map, 0xff)
+ AM_RANGE(0x00000090, 0x0000009f) AM_DEVICE8("ramdac1", bt459_device, map, 0xff)
+ AM_RANGE(0x000000b0, 0x000000b3) AM_READWRITE16(control_r, control_w, 0xffff)
+ //AM_RANGE(0x000000d4, 0x000000d7) AM_READWRITE8(, 0xff) // currently unknown
+ //AM_RANGE(0x000000a0, 0x000000a0) AM_READWRITE8(, 0xff) // currently unknown
+ AM_RANGE(0x00400000, 0x007fffff) AM_READWRITE(vram_r, vram_w)
+ADDRESS_MAP_END
+
+ROM_START(mpcb963)
+ ROM_REGION(0x80, "idprom", 0)
+ ROM_LOAD32_BYTE("mpcb963a.bin", 0x0, 0x20, CRC(4cf4562d) SHA1(58bcc2afb66168f1d44a0366b6a5ccc4c22e0f32))
+ROM_END
+
+ROM_START(mpcba79)
+ ROM_REGION(0x80, "idprom", 0)
+ ROM_LOAD32_BYTE("mpcba79a.bin", 0x0, 0x20, CRC(b3b98324) SHA1(77b4ed0bbc6ed19646c4536d9976563f78961408))
+ROM_END
+
+DEFINE_DEVICE_TYPE(MPCB963, mpcb963_device, "mpcb963", "2000 Graphics f/1 1Mp Monitor")
+DEFINE_DEVICE_TYPE(MPCBA79, mpcba79_device, "mpcba79", "2000 Graphics f/2 1Mp Monitors")
+
+/*
+ * The raw screen data here is just a guess for now. Real values will need to
+ * be determined to ensure the cursor positions generated by the Bt459 are
+ * aligned properly on the screen.
+ */
+MACHINE_CONFIG_MEMBER(mpcb963_device::device_add_mconfig)
+ MCFG_SCREEN_ADD("screen0", RASTER)
+ MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, GT_XPIXELS, 0, GT_XPIXELS, GT_YPIXELS, 0, GT_YPIXELS)
+ MCFG_SCREEN_UPDATE_DEVICE("", mpcb963_device, screen_update0)
+ MCFG_DEVICE_ADD("ramdac0", BT459, 0)
+MACHINE_CONFIG_END
+
+MACHINE_CONFIG_MEMBER(mpcba79_device::device_add_mconfig)
+ MCFG_SCREEN_ADD("screen0", RASTER)
+ MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, GT_XPIXELS, 0, GT_XPIXELS, GT_YPIXELS, 0, GT_YPIXELS)
+ MCFG_SCREEN_UPDATE_DEVICE("", mpcba79_device, screen_update0)
+ MCFG_DEVICE_ADD("ramdac0", BT459, 0)
+
+ MCFG_SCREEN_ADD("screen1", RASTER)
+ MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, GT_XPIXELS, 0, GT_XPIXELS, GT_YPIXELS, 0, GT_YPIXELS)
+ MCFG_SCREEN_UPDATE_DEVICE("", mpcba79_device, screen_update1)
+ MCFG_DEVICE_ADD("ramdac1", BT459, 0)
+MACHINE_CONFIG_END
+
+gt_device_base::gt_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : sr_card_device_base(mconfig, type, tag, owner, clock)
+{
+}
+
+mpcb963_device::mpcb963_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : gt_device_base(mconfig, MPCB963, tag, owner, clock)
+ , m_screen{ { { *this, "ramdac0" }, {}, true } }
+{
+}
+
+const tiny_rom_entry *mpcb963_device::device_rom_region() const
+{
+ return ROM_NAME(mpcb963);
+}
+
+void mpcb963_device::device_start()
+{
+ gt_device_base::device_start();
+
+ // allocate double-buffered vram
+ m_screen[0].vram.reset(new u8[GT_VRAM_SIZE]);
+
+ save_item(NAME(m_control));
+ save_pointer(NAME(m_screen[0].vram.get()), GT_VRAM_SIZE);
+}
+
+
+WRITE16_MEMBER(mpcb963_device::control_w)
+{
+ LOG("control_w 0x%04x\n", data);
+
+ m_control = data;
+}
+
+READ32_MEMBER(mpcb963_device::vram_r) const
+{
+ return ((u32 *)m_screen[(offset >> 19) & 1].vram.get())[offset & 0x7ffff];
+}
+
+WRITE32_MEMBER(mpcb963_device::vram_w)
+{
+ const gt_screen_t &gt_screen = m_screen[(offset >> 19) & 1];
+
+ ((u32 *)gt_screen.vram.get())[offset & 0x7ffff] = (((u32 *)gt_screen.vram.get())[offset & 0x7ffff] & ~mem_mask) | (data & mem_mask);
+}
+
+u32 mpcb963_device::screen_update0(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ const gt_screen_t &gt_screen = m_screen[0];
+
+ gt_screen.ramdac->screen_update(screen, bitmap, cliprect, &gt_screen.vram[gt_screen.primary ? 0x000000 : GT_BUFFER_SIZE]);
+
+ return 0;
+}
+
+mpcba79_device::mpcba79_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : gt_device_base(mconfig, MPCBA79, tag, owner, clock)
+ , m_screen { { { *this, "ramdac0" }, {}, true }, { { *this, "ramdac1" }, {}, true } }
+{
+}
+
+const tiny_rom_entry *mpcba79_device::device_rom_region() const
+{
+ return ROM_NAME(mpcba79);
+}
+
+void mpcba79_device::device_start()
+{
+ gt_device_base::device_start();
+
+ m_screen[0].vram.reset(new u8[GT_VRAM_SIZE]);
+ m_screen[1].vram.reset(new u8[GT_VRAM_SIZE]);
+
+ save_item(NAME(m_control));
+ save_pointer(NAME(m_screen[0].vram.get()), GT_VRAM_SIZE);
+ save_pointer(NAME(m_screen[1].vram.get()), GT_VRAM_SIZE);
+}
+
+WRITE16_MEMBER(mpcba79_device::control_w)
+{
+ LOG("control_w 0x%04x\n", data);
+
+ m_control = data;
+}
+
+READ32_MEMBER(mpcba79_device::vram_r) const
+{
+ return ((u32 *)m_screen[(offset >> 19) & 1].vram.get())[offset & 0x7ffff];
+}
+
+WRITE32_MEMBER(mpcba79_device::vram_w)
+{
+ const gt_screen_t &gt_screen = m_screen[(offset >> 19) & 1];
+
+ ((u32 *)gt_screen.vram.get())[offset & 0x7ffff] = (((u32 *)gt_screen.vram.get())[offset & 0x7ffff] & ~mem_mask) | (data & mem_mask);
+}
+
+u32 mpcba79_device::screen_update0(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ const gt_screen_t &gt_screen = m_screen[0];
+
+ gt_screen.ramdac->screen_update(screen, bitmap, cliprect, &gt_screen.vram[gt_screen.primary ? 0x000000 : GT_BUFFER_SIZE]);
+
+ return 0;
+}
+
+u32 mpcba79_device::screen_update1(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ const gt_screen_t &gt_screen = m_screen[1];
+
+ gt_screen.ramdac->screen_update(screen, bitmap, cliprect, &gt_screen.vram[gt_screen.primary ? 0x000000 : GT_BUFFER_SIZE]);
+
+ return 0;
+}
diff --git a/src/devices/bus/interpro/sr/gt.h b/src/devices/bus/interpro/sr/gt.h
new file mode 100644
index 00000000000..c572772ccda
--- /dev/null
+++ b/src/devices/bus/interpro/sr/gt.h
@@ -0,0 +1,102 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+#ifndef MAME_BUS_INTERPRO_SR_GT_H
+#define MAME_BUS_INTERPRO_SR_GT_H
+
+#pragma once
+
+#include "sr.h"
+#include "video/bt459.h"
+
+class gt_device_base : public sr_card_device_base
+{
+protected:
+ gt_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
+
+public:
+ static const u32 GT_PIXCLOCK = 80'000'000; // just a guess
+ static const int GT_XPIXELS = 1184;
+ static const int GT_YPIXELS = 884;
+ static const int GT_BUFFER_SIZE = 0x100000; // 1 megabyte
+ static const int GT_VRAM_SIZE = 0x200000; // 1 megabyte double buffered
+
+ enum control_mask
+ {
+ CONTROL_SCREEN = 0x1000, // possibly selects screen 0 or 1?
+ CONTROL_BUSY = 0x8000
+ };
+
+ virtual DECLARE_READ16_MEMBER(control_r) const = 0;
+ virtual DECLARE_WRITE16_MEMBER(control_w) = 0;
+ virtual DECLARE_READ32_MEMBER(vram_r) const = 0;
+ virtual DECLARE_WRITE32_MEMBER(vram_w) = 0;
+
+protected:
+ typedef struct
+ {
+ required_device<bt459_device> ramdac;
+ std::unique_ptr<u8[]> vram;
+
+ bool primary;
+ } gt_screen_t;
+};
+
+class mpcb963_device : public gt_device_base
+{
+public:
+ mpcb963_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ static const int GT_SCREEN_COUNT = 1;
+
+ virtual DECLARE_ADDRESS_MAP(map, 32) override;
+
+ virtual DECLARE_READ16_MEMBER(control_r) const override { return m_control; }
+ virtual DECLARE_WRITE16_MEMBER(control_w) override;
+ virtual DECLARE_READ32_MEMBER(vram_r) const override;
+ virtual DECLARE_WRITE32_MEMBER(vram_w) override;
+
+protected:
+ virtual const tiny_rom_entry *device_rom_region() const override;
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+
+ u32 screen_update0(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+private:
+ u16 m_control;
+
+ gt_screen_t m_screen[GT_SCREEN_COUNT];
+};
+
+class mpcba79_device : public gt_device_base
+{
+public:
+ mpcba79_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ static const int GT_SCREEN_COUNT = 2;
+
+ virtual DECLARE_ADDRESS_MAP(map, 32) override;
+
+ virtual DECLARE_READ16_MEMBER(control_r) const override { return m_control; }
+ virtual DECLARE_WRITE16_MEMBER(control_w) override;
+ virtual DECLARE_READ32_MEMBER(vram_r) const override;
+ virtual DECLARE_WRITE32_MEMBER(vram_w) override;
+
+protected:
+ virtual const tiny_rom_entry *device_rom_region() const override;
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+
+ u32 screen_update0(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ u32 screen_update1(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+private:
+ u16 m_control;
+ gt_screen_t m_screen[GT_SCREEN_COUNT];
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(MPCB963, mpcb963_device)
+DECLARE_DEVICE_TYPE(MPCBA79, mpcba79_device)
+
+#endif // MAME_BUS_INTERPRO_SR_GT_H
diff --git a/src/devices/bus/interpro/sr/sr.cpp b/src/devices/bus/interpro/sr/sr.cpp
new file mode 100644
index 00000000000..d27609d117d
--- /dev/null
+++ b/src/devices/bus/interpro/sr/sr.cpp
@@ -0,0 +1,363 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * An initial very primitive emulation of the SR bus for the Intergraph
+ * InterPro.
+ *
+ * The bus is referred to by several different names at different places
+ * in the system code, such as SR, SR bus, SRX, SRX/C bus, CBUS and some
+ * variations on those. It's likely that SRX is an enhanced version of the
+ * original SR bus implementation, and that CBUS is an alternate name.
+ *
+ * SR bus devices are mapped into the host memory map at a fixed address
+ * range per slot, starting at 0x87000000 and incrementing by 0x08000000.
+ * Within this address range, the first 32 double words provide access to
+ * a 32 byte board signature area, with a format as follows.
+ *
+ * u8 board[8] first four bytes typically zero, next three bytes
+ * contain a board number (in ASCII) from the list
+ * below, last byte contains a revision character
+ * u8 eco[8] engineering change order bits; these are initially
+ * all set, and are cleared starting from the least
+ * significant bit of the first byte onward to indicate
+ * board level changes
+ * u8 feature[8] used for different purposes by different boards
+ * u8 reserved[2] always set to 0xff
+ * u16 family indicate the board type from the list below
+ * u8 footprint[4] the first three bytes contain the magic number 0x55
+ * 0xaa 0x55, the last byte contains the ones-complement
+ * of the sum of the other bytes as a checksum
+ *
+ * The following are the board families taken from the system boot ROM.
+ *
+ * Family Description
+ * ------ -----------
+ * 0x0000 I/O processor
+ * 0x0001 CLIX engine
+ * 0x0002 expansion memory
+ * 0x0003 floating-point engine
+ * 0x0004 pipe-starter
+ * 0x0005 integrated frame buffer
+ * 0x0006 32/CGX
+ * 0x0007 GRE integrated frame buffer
+ * 0x0008 hardcopy/digitizer
+ * 0x0009 plotter DMA I/F
+ * 0x000a A/V mux
+ * 0x000b image subsystem I/F
+ * 0x000c VME adapter
+ * 0x000d I/O processor
+ * 0x000e CLIX engine
+ * 0x000f frame grabber
+ * 0x0010 screen generator subsystem
+ * 0x0011 scanner I/F
+ * 0x0012 convolution filter
+ * 0x0013 non-linear filter
+ * 0x0014 run-length encoder
+ * 0x0015 color setter data board
+ * 0x0016 EDGE I
+ * 0x0017 6000-series system board
+ * 0x0018 32-channel async I/F
+ * 0x0019 SR QUAD SCSI I/F
+ * 0x001a FDDI I/F
+ * 0x001b EDGE II processor
+ * 0x001c EDGE II frame buffer
+ * 0x001d IOI
+ * 0x001e IOI Clix Engine
+ * 0x001f MAYA bus I/F unit
+ * 0x0020 comp/decomp group 4
+ * 0x0021 QWIC/SRX I/F
+ * 0x0022 SRX test board
+ * 0x0023 QWIC bus CPU
+ * 0x0024 2000-series system board
+ * 0x0025 2000-series graphics board
+ * 0x0026 E100/SRX device I/F
+ * 0x0027 IKOS H/W simulator I/F
+ * 0x0028 enhanced VME adapter
+ * 0x0029 QWIC expansion memory
+ * 0x002a image comp/decomp
+ * 0x002b teleconferencing controller
+ * 0x002c Sky 8115-I interface
+ * 0x002d C-bus token ring
+ * 0x002e Screen IV PressFax Subsystem
+ * 0x002f C400E CPU
+ * 0x0030 C-bus exp Ethernet
+ * 0x0031 2400-series CPU
+ * 0x0032 6400-series CPU
+ * 0x0033 6000-series GT Graphics
+ * 0x0034 C4K QWIC bus processor
+ * 0x0038 Visualization Processor
+ * 0x0039 2700-series CPU
+ * 0x0040 6700-series CPU
+ * 0x0041 2800-series CPU
+ * 0x0042 6700-series CPU
+ *
+ * The following are the board names taken from the showconfig.dat file
+ * shipped with the operating system.
+ *
+ * # Description
+ * --- -----------
+ * 004 EDGE-2 Processor f/2 2Mp-FB's
+ * 008 Image Memory VRAM Board
+ * 009 EDGE II Frame Buffer for ImageStation
+ * 010 VI50 Image Processor
+ * 014 SRX Quad-SCSI Controller
+ * 019 C4E CPU Assembly
+ * 030 EDGE-2 Processor f/1 or 2 1Mp-FB's
+ * 031 EDGE-2 Processor f/1 2Mp-FB
+ * 032 CBUS Token Ring
+ * 034 256MB QWIC Bus Expansion Memory
+ * 046 6400 36-MHz Series System Board
+ * 047 2400 Series System Board
+ * 068 2400 Graphics f/1 1Mp Monitor (V-60)
+ * 069 2400 Graphics f/2 1Mp Monitors (V-60)
+ * 070 2400 Graphics f/1 1Mp Monitor (V-76)
+ * 071 2400 Graphics f/2 1Mp Monitors (V-76)
+ * 081 2400 Graphics f/1 2Mp Monitor (V-60/76)
+ * 083 SRX Hard PC Option / 8 Mb
+ * 093 EDGE-2 Plus Processor f/1 or 2 1Mp-FB's
+ * 094 EDGE-2 Plus Processor f/1 2Mp-FB
+ * 095 EDGE-2 Plus Processor f/2 2Mp-FB's
+ * 096 EDGE-2 Plus Frame Buffer f/1Mp Monitor
+ * 098 6400 32-MHz Sapphire System Board
+ * 101 2400 Graphics f/1 1Mp Monitor (V-76)
+ * 100 2500 Series System Board
+ * 102 2400 Graphics f/2 1Mp Monitors (V-76)
+ * 106 C-Bus Series Hard PC Option / 8 Mb
+ * 112 SRX Fast Quad-SCSI Controller
+ * 115 Edge III Processor
+ * 116 Edge III-C Single Ramdac
+ * 126 SRX Enhanced VME Adapter
+ * 127 6700 Series System Board
+ * 128 2700 Series System Board
+ * 129 6800 Series System Board
+ * 135 GT II Graphics f/1 2Mp Monitor (V-60/76)
+ * 136 GT II Graphics f/2 2Mp Monitor (V-60/76)
+ * 144 6800 Series System Board
+ * 145 2800 Series System Board
+ * 217 C-Bus Hard PC Option / 16 Mb
+ * 218 SRX Hard PC Option / 16 Mb
+ * 512 32C Clix Engine w/6MB
+ * 543 Floating Point Engine
+ * 548 80386 I/O Processor
+ * 577 Analog Video Mux/Summer
+ * 588 80186 I/O Processor
+ * 595 Pipe Starter
+ * 604 Digitizer/HardCopy Controller
+ * 605 300 Series Clix Engine w/16MB
+ * 617 300 Series SR VME-Adapter
+ * 633 32MB Expansion Memory
+ * 636 80186 I/O Processor w/CoProcessor
+ * 641 Image Subsystem Interface
+ * 643 8Mb Integrated Frame Buffer f/1Mp
+ * 650 2Mb Integrated Frame Buffer f/1Mp
+ * 652 2Mb Integrated Frame Buffer f/1Mp
+ * 657 32C Clix Engine w/8MB
+ * 663 200 Series SR VME-Adapter
+ * 664 32C Clix Engine w/16MB
+ * 675 SR Plotter-DMA-Interface
+ * 677 32C Clix Engine w/6MB
+ * 686 8Mb Integrated Frame Buffer f/2Mp
+ * 693 Clix Engine w/8MB
+ * 694 Clix Engine w/16MB
+ * 722 SR Frame Grabber NTSC
+ * 730 300/400 Series Clix Engine w/8MB
+ * 732 Clix Engine w/16MB
+ * 739 16MB Expansion Memory
+ * 765 6000 System Board w/16MB
+ * 776 Runlength Encoder
+ * 777 Scanner Interface
+ * 778 Nonlinear Filter
+ * 779 SIP Convolution Filter
+ * 789 32C Clix Engine w/6MB
+ * 792 Digitizer/HardCopy Controller Plus
+ * 796 2Mb Integrated Frame Buffer f/1Mp -T
+ * 799 80386 I/O Processor -T
+ * 801 Clix Engine w/8MB
+ * 819 QWIC Bus Clix Engine w/256K CB CACHE
+ * 820 QWIC System Interface w/64MB ECC
+ * 821 SRX 32 Channel RS232 Controller
+ * 822 300 Series Clix Engine w/16MB ECC
+ * 823 300/400 Series Clix Engine w/8MB ECC
+ * 825 32C Clix Engine w/12MB
+ * 826 FDDI Communications Processor
+ * 828 EDGE-1 Graphics f/1 1Mp Monitor (55K/60)
+ * 837 Analog Video Mux/Summer -T
+ * 838 Floating Point Engine -T
+ * 844 32MB Expansion Memory -T
+ * 849 EDGE-1 Graphics f/1 2Mp Monitor (55K/60)
+ * 851 Clix Engine w/16MB ECC -T
+ * 852 Digitizer/HardCopy Controller Plus -T
+ * 853 Clix Engine w/16MB ECC -T
+ * 883 6000 System Board w/8MB
+ * 887 SR Frame Grabber PAL
+ * 894 Clix Engine w/16MB ECC
+ * 896 EDGE-2/Plus Frame Buffer f/2Mp Monitor (V-60)
+ * 897 EDGE-2 Processor f/1 2Mp-FB
+ * 904 EDGE-1 Graphics f/2 1Mp Monitors (55K/60)
+ * 905 SRX Frame Grabber NTSC
+ * 906 SRX Frame Grabber PAL
+ * 915 64MB QWIC Bus Expansion Memory
+ * 917 Input Output Interface
+ * 932 IOI Clix Engine w/16MB ECC
+ * 956 SRX Enhanced VME-Adapter
+ * 958 VME-Controller f/SRX Interface
+ * 962 2000 System Board
+ * 963 2000 Graphics f/1 1Mp Monitor
+ * 965 EDGE-1 Graphics f/1 1Mp Monitor (66K/72)
+ * 966 EDGE-1 Graphics f/2 1Mp Monitors (66K/72)
+ * 977 6000 System Board w/32MB parity
+ * 978 6000 System Board w/48MB parity
+ * 979 6000 System Board w/64MB parity
+ * 980 6200 System Board w/8MB parity
+ * 981 6200 System Board w/16MB parity
+ * 982 6200 System Board w/32MB parity
+ * 983 6200 System Board w/48MB parity
+ * 984 6200 System Board w/64MB parity
+ * 31275 Raster Data Board
+ * 31277 Screener III A Board
+ * 31277/8 Screener III A+B Board
+ * A59 200 Series Clix Engine w/16MB parity
+ * A61 200 Series Clix Engine w/8MB parity
+ * A63 EDGE-2 Frame Buffer f/1Mp Monitor (55K/60)
+ * A77 200 Series Clix Engine w/16MB
+ * A79 2000 Graphics f/2 1Mp Monitors
+ * A80 QWIC System Interface w/16MB ECC
+ * A81 300 Series Clix Engine w/16MB ECC
+ * A86 SRX Teleconferencing Controller
+ * A95 IOI Clix Engine w/32MB ECC
+ * A96 IOI Clix Engine w/64MB ECC
+ * B13 System Board w/16MB parity -T
+ * B14 System Board w/48MB parity -T
+ * B15 EDGE-1 Graphics f/1 1Mp Monitor (55K/60) -T
+ * B16 EDGE-1 Graphics f/2 1Mp Monitors (55K/60) -T
+ * B17 IOI Clix Engine w/16MB ECC -T
+ * B18 Input Output Interface -T
+ * B20 SRX 32 Channel RS232 Controller -T
+ * B21 FDDI Communications Processor -T
+ * B22 SRX Quad-SCSI Controller -T
+ * b23 JPEG Compression/Decompression I/F
+ * B50 Application-Specific Acceleration Proc. II
+ * B63 IOI Clix Engine w/64MB ECC -T
+ * B67 GT Plus Graphics f/1 1Mp Monitor (V-76)
+ * B68 GT II Graphics f/1 1Mp Monitor (V-76)
+ * B70 GT II Graphics f/2 1Mp Monitors (V-76)
+ * B92 GT II Graphics f/1 2Mp Monitor (V-60/76)
+ * B93 GT II Graphics f/2 2Mp Monitors (V-60/76)
+ * B99 NTSC Frame Grabber for 3000 Series
+ * C01 PAL Frame Grabber for 3000 series
+ * C02 NTSC Frame Grabber for 6000 series
+ * C03 PAL Frame Grabber for 6000 series
+ * c05 25Mhz GTII Graphics f/1 1Mp Monitor
+ * c06 25Mhz GTII Graphics f/2 1Mp Monitors
+ * C41 GTII 60/76Hz Graphics f/1 2Mp Monitor
+ * C42 GTII 60/76Hz Graphics f/2 2Mp Monitor
+ */
+
+#include "emu.h"
+#include "sr.h"
+
+#define VERBOSE 0
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(SR_SLOT, sr_slot_device, "sr_slot", "InterPro SR bus slot")
+
+sr_slot_device::sr_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, SR_SLOT, tag, owner, clock)
+ , device_slot_interface(mconfig, *this)
+ , m_sr_tag(nullptr)
+ , m_sr_slottag(nullptr)
+{
+}
+
+void sr_slot_device::static_set_sr_slot(device_t &device, const char *tag, const char *slottag)
+{
+ sr_slot_device &sr_card = dynamic_cast<sr_slot_device &>(device);
+
+ sr_card.m_sr_tag = tag;
+ sr_card.m_sr_slottag = slottag;
+}
+
+void sr_slot_device::device_start()
+{
+ device_sr_card_interface *dev = dynamic_cast<device_sr_card_interface *>(get_card_device());
+
+ if (dev)
+ device_sr_card_interface::static_set_sr_tag(*dev, m_sr_tag, m_sr_slottag);
+}
+
+DEFINE_DEVICE_TYPE(SR, sr_device, "sr", "InterPro SR bus")
+
+sr_device::sr_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, SR, tag, owner, clock)
+ , m_data_space(nullptr)
+ , m_io_space(nullptr)
+ , m_out_irq0_cb(*this)
+ , m_out_irq1_cb(*this)
+ , m_out_irq2_cb(*this)
+{
+}
+
+void sr_device::device_start()
+{
+ // grab the main memory space from the mmu
+ device_memory_interface *mmu;
+ siblingdevice("mmu")->interface(mmu);
+ m_data_space = &mmu->space(0);
+ m_io_space = &mmu->space(1);
+
+ // resolve callbacks
+ m_out_irq0_cb.resolve_safe();
+ m_out_irq1_cb.resolve_safe();
+ m_out_irq2_cb.resolve_safe();
+
+ // empty the slots
+ m_slot_count = 0;
+ for (device_sr_card_interface *slot : m_slot)
+ slot = nullptr;
+}
+
+void sr_device::device_reset()
+{
+}
+
+device_sr_card_interface::device_sr_card_interface(const machine_config &mconfig, device_t &device)
+ : device_slot_card_interface(mconfig, device)
+ , m_sr(nullptr)
+ , m_sr_tag(nullptr)
+ , m_sr_slottag(nullptr)
+{
+}
+
+device_sr_card_interface::~device_sr_card_interface()
+{
+}
+
+void device_sr_card_interface::static_set_sr_tag(device_t &device, const char *tag, const char *slottag)
+{
+ device_sr_card_interface &sr_card = dynamic_cast<device_sr_card_interface &>(device);
+
+ sr_card.m_sr_tag = tag;
+ sr_card.m_sr_slottag = slottag;
+}
+
+void device_sr_card_interface::set_sr_device()
+{
+ // get a reference to the bus
+ m_sr = dynamic_cast<sr_device *>(device().machine().device(m_sr_tag));
+
+ // install the card in the next available slot
+ m_sr->install_card(*this, &device_sr_card_interface::map);
+}
+
+sr_card_device_base::sr_card_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, const char *idprom_region)
+ : device_t(mconfig, type, tag, owner, clock)
+ , device_sr_card_interface(mconfig, *this)
+ , m_idprom_region(idprom_region)
+{
+}
+
+void sr_card_device_base::device_start()
+{
+ set_sr_device();
+}
diff --git a/src/devices/bus/interpro/sr/sr.h b/src/devices/bus/interpro/sr/sr.h
new file mode 100644
index 00000000000..236b4ea674b
--- /dev/null
+++ b/src/devices/bus/interpro/sr/sr.h
@@ -0,0 +1,141 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_BUS_INTERPRO_SR_SR_H
+#define MAME_BUS_INTERPRO_SR_SR_H
+
+#pragma once
+
+#define MCFG_SR_OUT_IRQ0_CB(_devcb) \
+ devcb = &sr_device::set_out_irq0_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_SR_OUT_IRQ1_CB(_devcb) \
+ devcb = &sr_device::set_out_irq1_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_SR_OUT_IRQ2_CB(_devcb) \
+ devcb = &sr_device::set_out_irq2_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_SR_SLOT_ADD(_srtag, _tag, _slot_intf, _def_slot, _fixed) \
+ MCFG_DEVICE_ADD(_tag, SR_SLOT, 0) \
+ MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, _fixed) \
+ sr_slot_device::static_set_sr_slot(*device, _srtag, _tag);
+
+#define MCFG_SR_SLOT_REMOVE(_tag) \
+ MCFG_DEVICE_REMOVE(_tag)
+
+class sr_slot_device : public device_t, public device_slot_interface
+{
+public:
+ // construction/destruction
+ sr_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ // inline configuration
+ static void static_set_sr_slot(device_t &device, const char *tag, const char *slottag);
+protected:
+ sr_slot_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);
+
+ // device-level overrides
+ virtual void device_start() override;
+
+ // configuration
+ const char *m_sr_tag, *m_sr_slottag;
+};
+
+class device_sr_card_interface;
+
+class sr_device : public device_t
+{
+public:
+ // construction/destruction
+ sr_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ // inline configuration
+ template <class Object> static devcb_base &set_out_irq0_callback(device_t &device, Object &&cb) { return downcast<sr_device &>(device).m_out_irq0_cb.set_callback(std::forward<Object>(cb)); }
+ template <class Object> static devcb_base &set_out_irq1_callback(device_t &device, Object &&cb) { return downcast<sr_device &>(device).m_out_irq1_cb.set_callback(std::forward<Object>(cb)); }
+ template <class Object> static devcb_base &set_out_irq2_callback(device_t &device, Object &&cb) { return downcast<sr_device &>(device).m_out_irq2_cb.set_callback(std::forward<Object>(cb)); }
+
+ static const u32 SR_BASE = 0x87000000;
+ static const u32 SR_SIZE = 0x08000000;
+ static const int SR_COUNT = 16;
+
+ DECLARE_WRITE_LINE_MEMBER(irq0_w) { m_out_irq0_cb(state); }
+ DECLARE_WRITE_LINE_MEMBER(irq1_w) { m_out_irq1_cb(state); }
+ DECLARE_WRITE_LINE_MEMBER(irq2_w) { m_out_irq2_cb(state); }
+
+ // installation function for card devices
+ template <typename T> void install_card(T &device, void (T::*map)(address_map &map))
+ {
+ // record the device in the next free slot
+ m_slot[m_slot_count] = &device;
+
+ // compute slot base address
+ offs_t start = SR_BASE + m_slot_count * SR_SIZE;
+ offs_t end = start + (SR_SIZE - 1);
+
+ // install the device address map
+ m_data_space->install_device(start, end, device, map, 32);
+ m_io_space->install_device(start, end, device, map, 32);
+
+ m_slot_count++;
+ }
+
+protected:
+ sr_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);
+
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // internal state
+ address_space *m_data_space;
+ address_space *m_io_space;
+
+ devcb_write_line m_out_irq0_cb;
+ devcb_write_line m_out_irq1_cb;
+ devcb_write_line m_out_irq2_cb;
+
+private:
+ device_sr_card_interface *m_slot[SR_COUNT];
+ int m_slot_count;
+};
+
+class device_sr_card_interface : public device_slot_card_interface
+{
+public:
+ // construction/destruction
+ virtual ~device_sr_card_interface();
+
+ virtual DECLARE_ADDRESS_MAP(map, 32) = 0;
+
+ void set_sr_device();
+
+ // inline configuration
+ static void static_set_sr_tag(device_t &device, const char *tag, const char *slottag);
+
+protected:
+ device_sr_card_interface(const machine_config &mconfig, device_t &device);
+
+ sr_device *m_sr;
+ const char *m_sr_tag, *m_sr_slottag;
+};
+
+class sr_card_device_base : public device_t, public device_sr_card_interface
+{
+protected:
+ sr_card_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, const char *idprom_region = "idprom");
+
+public:
+ READ32_MEMBER(idprom_r) { return device().memregion(m_idprom_region)->as_u32(offset); }
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+
+private:
+ const char *const m_idprom_region;
+};
+
+DECLARE_DEVICE_TYPE(SR, sr_device)
+DECLARE_DEVICE_TYPE(SR_SLOT, sr_slot_device)
+
+#endif // MAME_BUS_INTERPRO_SR_SR_H
diff --git a/src/devices/bus/interpro/sr/sr_cards.cpp b/src/devices/bus/interpro/sr/sr_cards.cpp
new file mode 100644
index 00000000000..9a626b04a6e
--- /dev/null
+++ b/src/devices/bus/interpro/sr/sr_cards.cpp
@@ -0,0 +1,24 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#include "emu.h"
+#include "sr_cards.h"
+
+// video
+#include "gt.h"
+
+// storage
+
+// sound
+
+// network
+
+// communication ports
+
+// other
+
+
+SLOT_INTERFACE_START(sr_cards)
+ SLOT_INTERFACE("mpcb963", MPCB963)
+ SLOT_INTERFACE("mpcba79", MPCBA79)
+SLOT_INTERFACE_END
diff --git a/src/devices/bus/interpro/sr/sr_cards.h b/src/devices/bus/interpro/sr/sr_cards.h
new file mode 100644
index 00000000000..b7f552173d4
--- /dev/null
+++ b/src/devices/bus/interpro/sr/sr_cards.h
@@ -0,0 +1,12 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_BUS_INTERPRO_SR_SR_CARDS_H
+#define MAME_BUS_INTERPRO_SR_SR_CARDS_H
+
+#pragma once
+
+// supported devices
+SLOT_INTERFACE_EXTERN(sr_cards);
+
+#endif // MAME_BUS_INTERPRO_SR_SR_CARDS_H