1 // license:BSD-3-Clause
2 // copyright-holders:Patrick Mackinlay
3 
4 /*
5  * A high level emulation implementation of the Intergraph InterPro keyboard,
6  * largely copied from the sunkbd and psi hle implementations.
7  *
8  * These keyboards have two primary banks of keys. The lower bank consists of
9  * a total of 67 regular keyboard keyswitches plus a numeric keypad with a
10  * further 18 keys. The upper bank consists of 57 membrane-style programmable
11  * function keys in groups of 9, 36 and 12 from left to right.
12  *
13  * The following describes the key labels and positions according to the
14  * standard US English keyboard layout. At least a German keyboard variant is
15  * known to have existed.
16  *
17  * Upper bank keys indicated here with asterisks are printed in white, as are
18  * all the A*, B* and C* keys; all the others are printed in brown.
19  *
20  *    Setup*  Home*   2nd             A1      A2      A3      A4      A5      A6      A7      A8      A9      A10     A11     A12          A13     A14     A15     2nd
21  *    Help    Clear   F*                                                                                                                                           F*
22  *            Screen
23  *
24  *    Find    Insert  Print           B1      B2      B3      B4      B5      B6      B7      B8      B9      B10     B11     B12          B13     B14     B15     B16
25  *            Here    Screen*
26  *                    Remove
27  *
28  *    Select  Prev    Next            C1      C2      C3      C4      C5      C6      C7      C8      C9      C10     C11     C12          C13     C14     C15     C16
29  *            Screen  Screen
30  *
31  *
32  * In between the banks on the right hand side, there is a row of LEDs, the
33  * first three are pictures, rather than the descriptions given here).
34  *
35  *                                                                                                                                         Disk Lock ----- L1 L2 L3 L4
36  *
37  * Lower bank keys have up to 3 labels, in shifted and unshifted positions, and
38  * in red on the front face of the key-cap.
39  *
40  *    Esc     ~       !       @       #       $       %       ^       &       *       (       )       _       +       Back    Delete       PF1     PF2     PF3     PF4
41  *            `       1       2       3       4       5       6       7       8       9       0       -       =       Space                ±       ÷       ×       +
42  *                                                                                                                                         Esc     Num Lk  ScrlLk  Sys
43  *
44  *    Alt     Tab         Q       W       E       R       T       Y       U       I       O       P       {       }                        7       8       9       _
45  *    Mode                                                                                                [       ]
46  *                                                                                                                                         Home    ↑       Pg Up   Prt Sc
47  *
48  *    Ctrl    Caps          A       S       D       F       G       H       J       K       L       :       "       |         Return       4       5       6       ,
49  *            Lock
50  *                                                                                                                                         ←               →       −
51  *
52  *            Shift     >       Z       X       C       V       B       N       M       ,       .       ?       Shift         ▲            1       2       3       =
53  *                      <                                                                                                     ■
54  *                                                                                                                                         End     ↓       Pg Dn
55  *
56  *    Hold       Super-   Line                                                                          Repeat        ◄       ■       ►    0               ◦
57  *    Screen     impose   Feed                                                                                                ▼                            .       Enter
58  *                                                                                                                                         Ins             Del     +
59  *
60  * Alt Mode and Caps Lock keys have locking switches, capturing the key in the
61  * depressed position, as well as physical leds visible on the keycaps
62  * themselves. The keyboard also has two physical buttons on the back face of
63  * the keyboard, circular button labelled Boot, and a square one labelled Reset.
64  *
65  * The keyboard uses a 1200bps serial protocol to communicate with the host,
66  * with 1 start bit, 8 data bits, 1 stop bit, and even parity. The protocol
67  * as far as is known consists of the following sequences:
68  *
69  *    From Host       Purpose      Expected Response
70  *    ---------       -------      -----------------
71  *    0x1b 0x42 <xx>  probably controls several functions:
72  *
73  *              0x01  sound bell?
74  *              0x04  keyclick on
75  *              0x08  membrane click on
76  *              0x20  bell tone loud/soft/none? (seems only loud/none)
77  *
78  *    0x1b 0x44       reset/diag   0xff <status>, where status bits are set to
79  *                                 indicate diagnostic failure source: 0x8=EPROM
80  *                                 checksum, 0x10=RAM error, 0x20=ROM checksum
81  *
82  *    0x1b 0x55       activate scan code keyup/down mode?
83  *
84  * The keyboard has a keyboard click function, and the LED indicators described
85  * earlier, meaning that there are additional commands to enable and disable
86  * these functions.
87  *
88  * The keyboard has at least two operating modes: a simple ASCII mode which is
89  * active after reset, and a scancode mode generating make/break codes with
90  * full support for all the qualifiers and other non-ASCII keys.
91  *
92  * In ASCII mode, the keyboard transmits ASCII codes corresponding to the key
93  * labels for keys which map to the ASCII character set. Modifiers are applied
94  * by the keyboard itself, and do not generate make/break codes of their own.
95  *
96  * The following non-ASCII sequences are recognised in the system software,
97  * and likely correspond to specific keyboard keys or buttons:
98  *
99  *    Sequence    Function
100  *    --------    --------
101  *    <esc> ^L    Reboot, possibly maps to Reboot button
102  *    <esc> ^M    Unknown
103  *    <esc> ^N    Unknown, but operates as a toggle
104  *    <esc> ^U    Unknown
105  *
106  * TODO
107  *   - unmapped keys
108  *   - auto-repeat
109  *   - key click and LED commands
110  *   - alternative layouts
111  *   - scancode make/break mode
112  *
113  */
114 #include "emu.h"
115 #include "hle.h"
116 
117 #include "machine/keyboard.ipp"
118 #include "speaker.h"
119 
120 #define VERBOSE 0
121 #include "logmacro.h"
122 
123 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)")
124 
125 namespace bus { namespace interpro { namespace keyboard {
126 
127 namespace {
128 
129 	u8 const TRANSLATION_TABLE[4][5][16] =
130 	{
131 		// unshifted
132 		{
133 			{ 0x1b, 0x60, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2d, 0x3d, 0x08, 0x7f }, // 0
134 			{ 0x00, 0x09, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6f, 0x70, 0x5b, 0x5d, 0x00, 0x00 }, // 1
135 			{ 0x00, 0x00, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6a, 0x6b, 0x6c, 0x3b, 0x27, 0x5c, 0x0d, 0x00 }, // 2
136 			{ 0x00, 0x3c, 0x7a, 0x78, 0x63, 0x76, 0x62, 0x6e, 0x6d, 0x2c, 0x2e, 0x2f, 0x00, 0x00, 0x00, 0x00 }, // 3
137 			{ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 4
138 		},
139 		// shifted
140 		{
141 			{ 0x1b, 0x7e, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5e, 0x26, 0x2a, 0x28, 0x29, 0x5f, 0x2b, 0x08, 0x7f }, // 0
142 			{ 0x00, 0x09, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4f, 0x50, 0x7b, 0x7d, 0x00, 0x00 }, // 1
143 			{ 0x00, 0x00, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4a, 0x4b, 0x4c, 0x3a, 0x22, 0x7c, 0x0d, 0x00 }, // 2
144 			{ 0x00, 0x3e, 0x5a, 0x58, 0x43, 0x56, 0x42, 0x4e, 0x4d, 0x2c, 0x2e, 0x3f, 0x00, 0x00, 0x00, 0x00 }, // 3
145 			{ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 4
146 		},
147 		// unshifted-control
148 		{
149 			{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0
150 			{ 0x00, 0x00, 0x11, 0x17, 0x05, 0x12, 0x14, 0x19, 0x15, 0x09, 0x0f, 0x10, 0x1b, 0x1d, 0x00, 0x00 }, // 1
151 			{ 0x00, 0x00, 0x01, 0x13, 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, 0x00, 0x00, 0x1c, 0x00, 0x00 }, // 2
152 			{ 0x00, 0x00, 0x1a, 0x18, 0x03, 0x16, 0x02, 0x0e, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 3
153 			{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 4
154 		},
155 		// shifted-control
156 		{
157 			{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0
158 			{ 0x00, 0x00, 0x11, 0x17, 0x05, 0x12, 0x14, 0x19, 0x15, 0x09, 0x0f, 0x10, 0x1b, 0x1d, 0x00, 0x00 }, // 1
159 			{ 0x00, 0x00, 0x01, 0x13, 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, 0x00, 0x00, 0x1c, 0x00, 0x00 }, // 2
160 			{ 0x00, 0x00, 0x1a, 0x18, 0x03, 0x16, 0x02, 0x0e, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 3
161 			{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 4
162 		}
163 	};
164 
165 INPUT_PORTS_START(interpro_en_us)
166 
167 	PORT_START("modifiers")
168 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD)   PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_NAME("Control")   PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
169 	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD)   PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)     PORT_NAME("Shift")     PORT_CHAR(UCHAR_SHIFT_1)
170 	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD)   PORT_CODE(KEYCODE_CAPSLOCK)                             PORT_NAME("Caps Lock") PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
171 
172 	PORT_START("row_0")
173 	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_NAME("Esc")       PORT_CHAR(UCHAR_MAMEKEY(ESC))
174 	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)                             PORT_CHAR('`')  PORT_CHAR('~')
175 	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)                                 PORT_CHAR('1')  PORT_CHAR('!')
176 	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)                                 PORT_CHAR('2')  PORT_CHAR('@')
177 	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)                                 PORT_CHAR('3')  PORT_CHAR('#')
178 	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)                                 PORT_CHAR('4')  PORT_CHAR('$')
179 	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)                                 PORT_CHAR('5')  PORT_CHAR('%')
180 	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)                                 PORT_CHAR('6')  PORT_CHAR('^')
181 	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)                                 PORT_CHAR('7')  PORT_CHAR('&')
182 	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)                                 PORT_CHAR('8')  PORT_CHAR('*')
183 	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)                                 PORT_CHAR('9')  PORT_CHAR('(')
184 	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)                                 PORT_CHAR('0')  PORT_CHAR(')')
185 	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)                             PORT_CHAR('-')  PORT_CHAR('_')
186 	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)                            PORT_CHAR('=')  PORT_CHAR('+')
187 	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_NAME("Backspace") PORT_CHAR(8)
188 	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)        PORT_NAME("Delete")    PORT_CHAR(UCHAR_MAMEKEY(DEL))
189 
190 	PORT_START("row_1")
191 	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED) // "Alt Mode"
192 	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)        PORT_NAME("Tab")       PORT_CHAR(9)
193 	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)                                 PORT_CHAR('q')  PORT_CHAR('Q')
194 	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)                                 PORT_CHAR('w')  PORT_CHAR('W')
195 	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)                                 PORT_CHAR('e')  PORT_CHAR('E')
196 	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)                                 PORT_CHAR('r')  PORT_CHAR('R')
197 	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)                                 PORT_CHAR('t')  PORT_CHAR('T')
198 	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)                                 PORT_CHAR('y')  PORT_CHAR('Y')
199 	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)                                 PORT_CHAR('u')  PORT_CHAR('U')
200 	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)                                 PORT_CHAR('i')  PORT_CHAR('I')
201 	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)                                 PORT_CHAR('o')  PORT_CHAR('O')
202 	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)                                 PORT_CHAR('p')  PORT_CHAR('P')
203 	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)                         PORT_CHAR('[')  PORT_CHAR('{')
204 	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE)                        PORT_CHAR(']')  PORT_CHAR('}')
205 
206 	PORT_START("row_2")
207 	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED) // CTRL
208 	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_UNUSED) // LOCK
209 	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)                                 PORT_CHAR('a')  PORT_CHAR('A')
210 	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)                                 PORT_CHAR('s')  PORT_CHAR('S')
211 	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)                                 PORT_CHAR('d')  PORT_CHAR('D')
212 	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)                                 PORT_CHAR('f')  PORT_CHAR('F')
213 	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)                                 PORT_CHAR('g')  PORT_CHAR('G')
214 	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)                                 PORT_CHAR('h')  PORT_CHAR('H')
215 	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)                                 PORT_CHAR('j')  PORT_CHAR('J')
216 	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)                                 PORT_CHAR('k')  PORT_CHAR('K')
217 	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)                                 PORT_CHAR('l')  PORT_CHAR('L')
218 	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)                             PORT_CHAR(';')  PORT_CHAR(':')
219 	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)                             PORT_CHAR('\'') PORT_CHAR('"')
220 	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)                         PORT_CHAR('\\') PORT_CHAR('|')
221 	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_NAME("Return")    PORT_CHAR(13)
222 
223 	PORT_START("row_3")
224 	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED) // LSHIFT
225 	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2)                        PORT_CHAR('<')  PORT_CHAR('>')
226 	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)                                 PORT_CHAR('z')  PORT_CHAR('Z')
227 	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)                                 PORT_CHAR('x')  PORT_CHAR('X')
228 	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)                                 PORT_CHAR('c')  PORT_CHAR('C')
229 	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)                                 PORT_CHAR('v')  PORT_CHAR('V')
230 	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)                                 PORT_CHAR('b')  PORT_CHAR('B')
231 	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)                                 PORT_CHAR('n')  PORT_CHAR('N')
232 	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)                                 PORT_CHAR('m')  PORT_CHAR('M')
233 	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)                             PORT_CHAR(',')
234 	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)                              PORT_CHAR('.')
235 	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)                             PORT_CHAR('/')  PORT_CHAR('?')
236 	PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_UNUSED) // RSHIFT
237 	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)          PORT_NAME("Up")       PORT_CHAR(UCHAR_MAMEKEY(UP))
238 
239 	PORT_START("row_4")
240 	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_UNUSED) // "Hold Screen"
241 	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_UNUSED) // "Superimpose"
242 	PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_UNUSED) // "Line Feed"
243 	PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)       PORT_NAME("Space")    PORT_CHAR(UCHAR_MAMEKEY(SPACE))
244 	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_UNUSED) // "Repeat"
245 	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)        PORT_NAME("Left")     PORT_CHAR(UCHAR_MAMEKEY(LEFT))
246 	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)        PORT_NAME("Down")     PORT_CHAR(UCHAR_MAMEKEY(DOWN))
247 	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)       PORT_NAME("Right")    PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
248 
249 INPUT_PORTS_END
250 
251 INPUT_PORTS_START(hle_en_us_device)
252 	PORT_INCLUDE(interpro_en_us)
253 INPUT_PORTS_END
254 
255 } // anonymous namespace
256 
hle_device_base(machine_config const & mconfig,device_type type,char const * tag,device_t * owner,u32 clock)257 hle_device_base::hle_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock)
258 	: device_t(mconfig, type, tag, owner, clock)
259 	, device_buffered_serial_interface(mconfig, *this)
260 	, device_interpro_keyboard_port_interface(mconfig, *this)
261 	, device_matrix_keyboard_interface(mconfig, *this, "row_0", "row_1", "row_2", "row_3", "row_4")
262 	, m_click_timer(nullptr)
263 	, m_beeper(*this, "beeper")
264 	, m_make_count(0U)
265 	, m_rx_state(RX_IDLE)
266 	, m_keyclick(0U)
267 	, m_beeper_state(0U)
268 {
269 }
270 
~hle_device_base()271 hle_device_base::~hle_device_base()
272 {
273 }
274 
WRITE_LINE_MEMBER(hle_device_base::input_txd)275 WRITE_LINE_MEMBER(hle_device_base::input_txd)
276 {
277 	device_buffered_serial_interface::rx_w(state);
278 }
279 
device_add_mconfig(machine_config & config)280 void hle_device_base::device_add_mconfig(machine_config &config)
281 {
282 	SPEAKER(config, "bell").front_center();
283 	BEEP(config, m_beeper, ATTOSECONDS_TO_HZ(480 * ATTOSECONDS_PER_MICROSECOND)).add_route(ALL_OUTPUTS, "bell", 1.0);
284 }
285 
device_start()286 void hle_device_base::device_start()
287 {
288 	m_click_timer = timer_alloc(CLICK_TIMER_ID);
289 
290 	save_item(NAME(m_make_count));
291 	save_item(NAME(m_rx_state));
292 	save_item(NAME(m_keyclick));
293 	save_item(NAME(m_beeper_state));
294 }
295 
device_reset()296 void hle_device_base::device_reset()
297 {
298 	// initialise state
299 	clear_fifo();
300 	m_make_count = 0U;
301 	m_rx_state = RX_IDLE;
302 	m_keyclick = 0U;
303 	m_beeper_state = 0x00U;
304 
305 	// configure device_buffered_serial_interface
306 	set_data_frame(START_BIT_COUNT, DATA_BIT_COUNT, PARITY, STOP_BITS);
307 	set_rate(BAUD);
308 	receive_register_reset();
309 	transmit_register_reset();
310 
311 	// no beep
312 	m_click_timer->reset();
313 
314 	// kick the base
315 	reset_key_state();
316 	start_processing(attotime::from_hz(1'200));
317 }
318 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)319 void hle_device_base::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
320 {
321 	switch (id)
322 	{
323 	case CLICK_TIMER_ID:
324 		m_beeper_state &= ~u8(BEEPER_CLICK);
325 		m_beeper->set_state(m_beeper_state ? 1 : 0);
326 		break;
327 
328 	default:
329 		break;
330 	}
331 }
332 
tra_callback()333 void hle_device_base::tra_callback()
334 {
335 	output_rxd(transmit_register_get_data_bit());
336 }
337 
tra_complete()338 void hle_device_base::tra_complete()
339 {
340 	if (fifo_full())
341 		start_processing(attotime::from_hz(1'200));
342 
343 	device_buffered_serial_interface::tra_complete();
344 }
345 
key_make(u8 row,u8 column)346 void hle_device_base::key_make(u8 row, u8 column)
347 {
348 	// we should have stopped processing if we filled the FIFO
349 	assert(!fifo_full());
350 
351 	// send the make code, click if desired
352 	transmit_byte(translate(row, column));
353 	if (m_keyclick)
354 	{
355 		m_beeper_state |= u8(BEEPER_CLICK);
356 		m_beeper->set_state(m_beeper_state ? 1 : 0);
357 		m_click_timer->reset(attotime::from_msec(5));
358 	}
359 
360 	// count keys
361 	++m_make_count;
362 	assert(m_make_count);
363 }
364 
key_break(u8 row,u8 column)365 void hle_device_base::key_break(u8 row, u8 column)
366 {
367 	// we should have stopped processing if we filled the FIFO
368 	assert(!fifo_full());
369 	assert(m_make_count);
370 
371 	--m_make_count;
372 
373 	// check our counting
374 	assert(are_all_keys_up() == !bool(m_make_count));
375 }
376 
transmit_byte(u8 byte)377 void hle_device_base::transmit_byte(u8 byte)
378 {
379 	LOG("transmit_byte 0x%02x\n", byte);
380 	device_buffered_serial_interface::transmit_byte(byte);
381 	if (fifo_full())
382 		stop_processing();
383 }
384 
received_byte(u8 byte)385 void hle_device_base::received_byte(u8 byte)
386 {
387 	LOG("received_byte 0x%02x\n", byte);
388 
389 	switch (m_rx_state)
390 	{
391 	case RX_COMMAND:
392 		switch (byte)
393 		{
394 		case 0x42: // configure flags
395 			m_rx_state = RX_FLAGS;
396 			break;
397 
398 		case 0x44: // reset/diagnostic
399 			transmit_byte(0xff);
400 			transmit_byte(0x00);
401 
402 			m_rx_state = RX_IDLE;
403 			break;
404 
405 		case 0x55:
406 			break;
407 		}
408 		break;
409 
410 	case RX_FLAGS:
411 		// FIXME: this logic is wrong (should decode the various fields), but
412 		// generates bell sounds at the right time for now
413 		switch (byte)
414 		{
415 		case 0x28:
416 			LOG("bell on\n");
417 			m_beeper_state |= u8(BEEPER_BELL);
418 			m_beeper->set_state(m_beeper_state ? 1 : 0);
419 			break;
420 
421 		case 0x29:
422 			LOG("bell off\n");
423 			m_beeper_state &= ~u8(BEEPER_BELL);
424 			m_beeper->set_state(m_beeper_state ? 1 : 0);
425 			break;
426 
427 		default:
428 			break;
429 		}
430 		m_rx_state = RX_IDLE;
431 		break;
432 
433 	case RX_IDLE:
434 		if (byte == 0x1b)
435 			m_rx_state = RX_COMMAND;
436 		break;
437 	}
438 }
439 
hle_en_us_device(machine_config const & mconfig,char const * tag,device_t * owner,u32 clock)440 hle_en_us_device::hle_en_us_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
441 	: hle_device_base(mconfig, INTERPRO_HLE_EN_US_KEYBOARD, tag, owner, clock),
442 	m_modifiers(*this, "modifiers")
443 {
444 }
445 
device_input_ports() const446 ioport_constructor hle_en_us_device::device_input_ports() const
447 {
448 	return INPUT_PORTS_NAME(hle_en_us_device);
449 }
450 
translate(u8 row,u8 column)451 u8 hle_en_us_device::translate(u8 row, u8 column)
452 {
453 	u8 const modifiers(m_modifiers->read());
454 
455 	bool const ctrl(modifiers & 0x01);
456 	bool const shift(bool(modifiers & 0x02) || (bool(modifiers & 0x04)));
457 	bool const ctrl_shift(ctrl && shift);
458 
459 	unsigned const map(ctrl_shift ? 3 : ctrl ? 2 : shift ? 1 : 0);
460 
461 	return TRANSLATION_TABLE[map][row][column];
462 }
463 
464 } } } // namespace bus::interpro::keyboard
465