summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/kay_kbd.cpp
blob: dcdc9c265b6d017bdba6e6f847d2fc7f167664dd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/******************************************************************************
 *
 *  kay_kbd.cpp
 *
 *  Kaypro Serial Keyboards
 *
 ******************************************************************************/

#include "emu.h"
#include "machine/kay_kbd.h"

#include "cpu/mcs48/mcs48.h"
#include "speaker.h"

#define LOG_GENERAL (1U << 0)
#define LOG_TXD     (1U << 1)

//#define VERBOSE (LOG_GENERAL | LOG_TXD)
#include "logmacro.h"

#define LOGTXD(...) LOGMASKED(LOG_TXD, __VA_ARGS__)


/*
 * The KAYPRO keyboard has roughly the following layout:
 *
 *                                                                  [up] [down] [left] [right]
 *         [ESC] [1!] [2@] [3#] [4$] [5%] [6^] [7&] [8*] [9(] [0)] [-_] [=+] [`~] [BACK SPACE]
 *           [TAB] [qQ] [wW] [eE] [rR] [tT] [yY] [uU] [iI] [oO] [pP] [[{] []}]       [DEL]
 *[CTRL] [CAPS LOCK] [aA] [sS] [dD] [fF] [gG] [hH] [jJ] [kK] [lL] [;:] ['"] [RETURN] [\|]
 *            [SHIFT] [zZ] [xX] [cC] [vV] [bB] [nN] [mM] [,<] [.>] [/?] [SHIFT] [LINE FEED]
 *                      [                 SPACE                     ]
 *
 * [7] [8] [9] [-]
 * [4] [5] [6] [,]
 * [1] [2] [3]
 * [  0  ] [.] [ENTER]
 *
 * Notes on Appearance
 * -------------------
 * The RETURN key is shaped like a backwards "L". The keypad ENTER key is actually
 * oriented vertically, not horizontally. The alpha keys are marked with the uppercase letters
 * only. Other keys with two symbols have the shifted symbol printed above the unshifted symbol.
 * The keypad is actually located to the right of the main keyboard; it is shown separately here
 * as a convenience to users of narrow listing windows. The arrow keys are actually marked with
 * arrow graphics pointing in the appropriate directions. The F and J keys are specially shaped,
 * since they are the "home keys" for touch-typing. The CAPS LOCK key has a build-in red indicator
 * which is lit when CAPS LOCK is pressed once, and extinguished when the key is pressed again.
 */


/*
Kaypro 10 keyboard

This is a TTL-level asynchronous serial keyboard using mask-programmed
MCS-48 MCU.  Serial transmission and reception are handled in software
using the onboard timer/counter interrupt.  The frame format is 1 start
bit, 8 data bits, no parity, and 1 stop bit.

Active components:
* Mitsubishi M5L8049-109P-6 (MCS-48 MCU with 2KiB mask ROM)
* SN74154N (4-to-16 demultiplexer)
* DM7406N (hex inverter with open collector outputs)
* SN75451BP (dual high-speed, high-current line driver with AND inputs)
* 6.000MHz crystal

D0-D3 select the key matrix row, only 0x0..0xE are used (0x0F ignored).

D4 is the CAPS LOCK LED output.

P1 is connected to the key matrix columns.

P2.1, P2.2 and P2.3 are CTRL, CAPS LOCK, and SHIFT inputs.

P2.5 drives the speaker (1.5625kHz tone generated using timer/counter).

P2.7 is the serial data output to the host.

T1 is the serial data input from the host.

The program appears to be able to support "typewriter shift" and
"bitshift" layouts, and both ASCII and Kaypro control codes for the
numeric keypad, depending on how the matrix is wired.  All positions in
the key matrix will produce characters, although it's not clear whether
all of them are designed to serve a purpose - some may just be emergent
behaviour from the decoding algorithm.

The serial rate is generated by reloading the T register with 0xfe and
transfering a bit every 21 interrupts, giving a rate within 1% of the
desired 300 Baud:
6MHz / 15 / 32 / (0x100 - 0xfe) / 21 = 297.62 Baud

This scheme allows only 64 machine cycles between interrupts, and more
than half of thse are consumed by the timer service routine itself.

The program ROM checksum routine is supposed to compute the sum of all
program ROM bytes modulo 256 and the XOR of all program ROM bytes, but
due to bugs it omits the first byte of each page (0x000, 0x100, 0x200,
0x300, 0x400, 0x500, 0x600 and 0x700).

The serial command processing is quite lax in what it accepts:
xxxx xxx1   ignored
xxxx Mx10   short beep, set keyclick mute to M
xxxx M100   long beep, set keyclick mute to M
xxx1 M000   answer back with 0xAA?, set keyclick mute to M
xxx0 M000   set keyclick mute to M

The Kaypro II was sold with a different keyboard using an 8751 (MCS-51)
MCU, but we don't have a dump for it.
*/


DEFINE_DEVICE_TYPE(KAYPRO_10_KEYBOARD, kaypro_10_keyboard_device, "kaypro10kbd", "Kaypro 10 Keyboard")


namespace {

ROM_START(kaypro_10_keyboard)
	ROM_REGION(0x0800, "mcu", 0)
	ROM_LOAD("m5l8049.bin", 0x0000, 0x0800, CRC(dc772f80) SHA1(aa7cd3f476466203294675d56098dff45952b9b0))
ROM_END

INPUT_PORTS_START(kaypro_keyboard_typewriter)
	PORT_START("ROW.0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD)      PORT_CHAR(UCHAR_MAMEKEY(0_PAD))     PORT_NAME("Pad 0")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)          PORT_CHAR('4')  PORT_CHAR('$')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)          PORT_CHAR('h')  PORT_CHAR('H')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)          PORT_CHAR('v')  PORT_CHAR('V')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD)      PORT_CHAR(UCHAR_MAMEKEY(3_PAD))     PORT_NAME("Pad 3")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD)      PORT_CHAR(UCHAR_MAMEKEY(6_PAD))     PORT_NAME("Pad 6")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD)      PORT_CHAR(UCHAR_MAMEKEY(9_PAD))     PORT_NAME("Pad 9")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xf8

	PORT_START("ROW.1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xa5
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)          PORT_CHAR('5')  PORT_CHAR('%')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)          PORT_CHAR('r')  PORT_CHAR('R')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)          PORT_CHAR('b')  PORT_CHAR('B')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD)      PORT_CHAR(UCHAR_MAMEKEY(2_PAD))     PORT_NAME("Pad 2")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD)      PORT_CHAR(UCHAR_MAMEKEY(5_PAD))     PORT_NAME("Pad 5")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD)      PORT_CHAR(UCHAR_MAMEKEY(8_PAD))     PORT_NAME("Pad 8")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xf7

	PORT_START("ROW.2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xa4
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('^')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)          PORT_CHAR('t')  PORT_CHAR('T')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)          PORT_CHAR('n')  PORT_CHAR('N')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD)      PORT_CHAR(UCHAR_MAMEKEY(1_PAD))     PORT_NAME("Pad 1")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD)      PORT_CHAR(UCHAR_MAMEKEY(4_PAD))     PORT_NAME("Pad 4")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD)      PORT_CHAR(UCHAR_MAMEKEY(7_PAD))     PORT_NAME("Pad 7")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xf6

	PORT_START("ROW.3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RALT)       PORT_CHAR(10)                       PORT_NAME("LINE FEED")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)          PORT_CHAR('y')  PORT_CHAR('Y')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)          PORT_CHAR('m')  PORT_CHAR('M')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH)  PORT_CHAR('\\') PORT_CHAR('|')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)        PORT_CHAR(UCHAR_MAMEKEY(DEL))       PORT_NAME("DEL")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xe0
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xf5

	PORT_START("ROW.4")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xa3
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('*')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)          PORT_CHAR('u')  PORT_CHAR('U')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)      PORT_CHAR(',')  PORT_CHAR('<')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)      PORT_CHAR(13)                       PORT_NAME("RETURN")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xd4
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE)  PORT_CHAR(8)                        PORT_NAME("BACK SPACE")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT)      PORT_CHAR(UCHAR_MAMEKEY(RIGHT))

	PORT_START("ROW.5")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)          PORT_CHAR('z')  PORT_CHAR('Z')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)          PORT_CHAR('w')  PORT_CHAR('W')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)          PORT_CHAR('k')  PORT_CHAR('K')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)          PORT_CHAR('s')  PORT_CHAR('S')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR('\'') PORT_CHAR('"')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')  PORT_CHAR('}')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('`')  PORT_CHAR('~')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT)       PORT_CHAR(UCHAR_MAMEKEY(LEFT))

	PORT_START("ROW.6")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE)      PORT_CHAR(' ')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR('(')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)          PORT_CHAR('i')  PORT_CHAR('I')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)       PORT_CHAR('.')  PORT_CHAR('>')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)          PORT_CHAR('l')  PORT_CHAR('L')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)          PORT_CHAR('p')  PORT_CHAR('P')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')  PORT_CHAR('_')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP)         PORT_CHAR(UCHAR_MAMEKEY(UP))

	PORT_START("ROW.7")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xa2
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)          PORT_CHAR('0')  PORT_CHAR(')')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)          PORT_CHAR('o')  PORT_CHAR('O')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)      PORT_CHAR('/')  PORT_CHAR('?')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';')  PORT_CHAR(':')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE)  PORT_CHAR('[')  PORT_CHAR('{')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('=')  PORT_CHAR('+')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN)       PORT_CHAR(UCHAR_MAMEKEY(DOWN))

	PORT_START("ROW.8")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xa1
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2')  PORT_CHAR('@')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)          PORT_CHAR('f')  PORT_CHAR('F')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)          PORT_CHAR('x')  PORT_CHAR('X')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)          PORT_CHAR('a')  PORT_CHAR('A')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)          PORT_CHAR('q')  PORT_CHAR('Q')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)          PORT_CHAR('1')  PORT_CHAR(';')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xf0

	PORT_START("ROW.9")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xa0
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)          PORT_CHAR('e')  PORT_CHAR('E')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)          PORT_CHAR('j')  PORT_CHAR('J')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)          PORT_CHAR('d')  PORT_CHAR('D')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xb0
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)        PORT_CHAR('\t')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC)        PORT_CHAR(UCHAR_MAMEKEY(ESC))
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xfe

	PORT_START("ROW.A")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD)    PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))   PORT_NAME("Pad .")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)          PORT_CHAR('3')  PORT_CHAR('#')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)          PORT_CHAR('g')  PORT_CHAR('G')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)          PORT_CHAR('c')  PORT_CHAR('C')
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD)  PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_NAME("Pad ENTER")
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)   PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD)) PORT_NAME("Pad ,")
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)  PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_NAME("Pad -")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xf9

	PORT_START("ROW.B")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) // 2 for ASCII keypad
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED) // 1 for ASCII keypad
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED) // 0 for "bitshift" layout or ASCII keypad
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED) // 7 for ASCII keypad
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED) // 0 for "bitshift" layout or ASCII keypad
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED) // 9 for ASCII keypad
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) // 8 for ASCII keypad
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // . for ASCII keypad

	PORT_START("ROW.C")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) // 6 & for "bitshift" layout
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED) // 3 # for "bitshift" layout?  identical to "typewriter shift" anyway
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED) // 2 " for "bitshift" layout
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED) // "typewriter shift" 2 @ duplicated here
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED) // 6 for ASCII keypad
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED) // 5 for ASCII keypad
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) // 4 for ASCII keypad
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // 3 for ASCII keypad

	PORT_START("ROW.D")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED) // ` @ for "bitshift" layout
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED) // - = for "bitshift" layout
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED) // : * for "bitshift" layout
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED) // ~ ^ for "bitshift" layout
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED) // "typewriter shift" 6 ^ duplicated here
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED) // 9 ) for "bitshift" layout
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) // 8 ( for "bitshift" layout
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // 7 ' for "bitshift" layout

	PORT_START("ROW.E")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xe0
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0x90
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xa0
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0x00
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xe0
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0xc0
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNKNOWN) // 0x54
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) // ; + for "bitshift" layout

	PORT_START("ROW.F")
	PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNUSED) // not scanned

	PORT_START("MOD")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)           PORT_NAME("CTRL")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK)                             PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_NAME("CAPS LOCK")
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)     PORT_CHAR(UCHAR_SHIFT_1)           PORT_NAME("SHIFT")
INPUT_PORTS_END

INPUT_PORTS_START(kaypro_keyboard_bitshift)
	PORT_INCLUDE(kaypro_keyboard_typewriter)

	PORT_MODIFY("ROW.2")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED) // 6 ^ for "typewriter shift" layout

	PORT_MODIFY("ROW.3")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED) // 7 & for "typewriter shift" layout

	PORT_MODIFY("ROW.4")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED) // 8 * for "typewriter shift" layout

	PORT_MODIFY("ROW.5")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED) // ' " for "typewriter shift" layout
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) // ` ~ for "typewriter shift" layout

	PORT_MODIFY("ROW.6")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED) // 9 ) for "typewriter shift" layout
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) // - _ for "typewriter shift" layout

	PORT_MODIFY("ROW.7")
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED) // ; : for "typewriter shift" layout
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) // = + for "typewriter shift" layout

	PORT_MODIFY("ROW.8")
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED) // 2 @ for "typewriter shift" layout

	PORT_MODIFY("ROW.C")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)          PORT_CHAR('6')  PORT_CHAR('&')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)          PORT_CHAR('2')  PORT_CHAR('"')

	PORT_MODIFY("ROW.D")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)     PORT_CHAR('`')  PORT_CHAR('@')
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)      PORT_CHAR('-')  PORT_CHAR('=')
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)      PORT_CHAR(':')  PORT_CHAR('*')
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)      PORT_CHAR('~')  PORT_CHAR('^')
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)          PORT_CHAR('9')  PORT_CHAR(')')
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)          PORT_CHAR('8')  PORT_CHAR('(')
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)          PORT_CHAR('7')  PORT_CHAR('\'')

	PORT_MODIFY("ROW.E")
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)      PORT_CHAR(';')  PORT_CHAR('+')
INPUT_PORTS_END

} // anonymous namespace


kaypro_10_keyboard_device::kaypro_10_keyboard_device(
		machine_config const &mconfig,
		char const *tag,
		device_t *owner,
		std::uint32_t clock)
	: device_t(mconfig, KAYPRO_10_KEYBOARD, tag, owner, clock)
	, m_mcu(*this, "mcu")
	, m_bell(*this, "bell")
	, m_matrix(*this, "ROW.%X", 0)
	, m_modifiers(*this, "MOD")
	, m_rxd_cb(*this)
	, m_txd(1U)
	, m_bus(0U)
{
}

tiny_rom_entry const *kaypro_10_keyboard_device::device_rom_region() const
{
	return ROM_NAME(kaypro_10_keyboard);
}

MACHINE_CONFIG_START(kaypro_10_keyboard_device::device_add_mconfig)
	MCFG_DEVICE_ADD("mcu", I8049, 6_MHz_XTAL)
	MCFG_MCS48_PORT_P1_IN_CB(READ8(*this, kaypro_10_keyboard_device, p1_r))
	MCFG_MCS48_PORT_P2_IN_CB(READ8(*this, kaypro_10_keyboard_device, p2_r))
	MCFG_MCS48_PORT_P2_OUT_CB(WRITE8(*this, kaypro_10_keyboard_device, p2_w))
	MCFG_MCS48_PORT_T1_IN_CB(READLINE(*this, kaypro_10_keyboard_device, t1_r))
	MCFG_MCS48_PORT_BUS_IN_CB(READ8(*this, kaypro_10_keyboard_device, bus_r))
	MCFG_MCS48_PORT_BUS_OUT_CB(WRITE8(*this, kaypro_10_keyboard_device, bus_w))

	SPEAKER(config, "keyboard").front_center();
	MCFG_DEVICE_ADD("bell", SPEAKER_SOUND)
	MCFG_SOUND_ROUTE(ALL_OUTPUTS, "keyboard", 0.25)
MACHINE_CONFIG_END

ioport_constructor kaypro_10_keyboard_device::device_input_ports() const
{
	(void)&INPUT_PORTS_NAME(kaypro_keyboard_bitshift);
	return INPUT_PORTS_NAME(kaypro_keyboard_typewriter);
}

void kaypro_10_keyboard_device::device_start()
{
	m_rxd_cb.resolve_safe();

	save_item(NAME(m_txd));
	save_item(NAME(m_bus));
}

READ8_MEMBER(kaypro_10_keyboard_device::p1_r)
{
	return m_matrix[m_bus & 0x0f]->read();
}

READ8_MEMBER(kaypro_10_keyboard_device::p2_r)
{
	return m_modifiers->read() | 0xf8U;
}

WRITE8_MEMBER(kaypro_10_keyboard_device::p2_w)
{
	if ((VERBOSE & LOG_TXD) && (0x0014U >= m_mcu->pc()))
	{
		auto const suppressor(machine().disable_side_effects());
		address_space &mcu_ram(m_mcu->space(AS_DATA));
		std::uint8_t const txd_time(mcu_ram.read_byte(0x1cU));
		std::uint8_t const serial_flags(mcu_ram.read_byte(0x1eU));
		bool const txd_active(BIT(serial_flags, 6));
		if ((21U == txd_time) && txd_active)
		{
			std::uint8_t const txd_bit(mcu_ram.read_byte(0x1bU));
			LOGTXD(
					(9U == txd_bit) ? "serial transmit: start bit = %1$u (cycles = %3$u)\n" : "serial transmit: bit %2$u = %1$u (cycles = %3$u)\n",
					BIT(data, 7),
					8U - txd_bit,
					m_mcu->total_cycles());
		}
	}

	m_bell->level_w(BIT(data, 5));
	m_rxd_cb(BIT(data, 7));
}

READ_LINE_MEMBER(kaypro_10_keyboard_device::t1_r)
{
	return m_txd ? 1 : 0;
}

READ8_MEMBER(kaypro_10_keyboard_device::bus_r)
{
	return m_bus;
}

WRITE8_MEMBER(kaypro_10_keyboard_device::bus_w)
{
	if (BIT(m_bus ^ data, 4))
		machine().output().set_value("led_caps_lock", BIT(data, 4));
	m_bus = data;
}