summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/kc_keyb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/machine/kc_keyb.c')
-rw-r--r--src/mame/machine/kc_keyb.c614
1 files changed, 614 insertions, 0 deletions
diff --git a/src/mame/machine/kc_keyb.c b/src/mame/machine/kc_keyb.c
new file mode 100644
index 00000000000..740d0dc7fce
--- /dev/null
+++ b/src/mame/machine/kc_keyb.c
@@ -0,0 +1,614 @@
+// license:GPL-2.0+
+// copyright-holders:Kevin Thacker,Sandro Ronco
+/*********************************************************************
+
+ kc_keyb.c
+
+ KC85_2/3/4/5 Keyboard emulation
+
+**********************************************************************
+
+ E-mail from Torsten Paul about the keyboard:
+
+ Torsten.Paul@gmx.de
+
+"> I hope that you will be able to help me so I can add keyboard
+> emulation to it.
+>
+Oh yes the keyboard caused me a lot of trouble too. I still don't
+understand it completely.
+A first dirty but working keyboard support is quite easy because
+of the good and modular system rom. Most programs use the supplied
+routines to read keyboard input so patching the right memory address
+with the keycode that is originally supplied by the interrupt
+routines for keyboard input will work. So if you first want to
+have a simple start to get other things to work you can try this.
+
+ * write keycode (ascii) to address IX+0dh (0x1fd)
+ * set bit 0 at address IX+08h (0x1f8) - simply writing 01h
+ worked for me but some other bits are used too
+ this bit is reset if the key is read by the system
+
+> From the schematics I see that the keyboard is linked to the CTC and PIO,
+> but I don't understand it fully.
+> Please can you tell me more about how the keyboard is scanned?
+>
+The full emulation is quite tricky and I still have some programs that
+behave odd. (A good hint for a correct keyboard emulation is Digger ;-).
+
+Ok, now the technical things:
+
+The keyboard of the KC is driven by a remote control circuit that is
+originally designed for infrared remote control. This one was named
+U807 and I learned there should be a similar chip called SAB 3021
+available but I never found the specs on the web. The SAB 3021 was
+produced by Valvo which doesn't exist anymore (bought by Phillips
+if I remember correctly). If you have more luck finding the specs
+I'm still interested.
+There also was a complementary chip for the recieving side but that
+was not used in the KC unfortunately. They choosed to measure the
+pulses sent by the U807 via PIO and CTC.
+
+Anyway here is what I know about the protocol:
+
+The U807 sends impulses with equal length. The information is
+given by the time between two impulses. Short time means bit is 1
+long time mean bit is 0. The impulses are modulated by a 62.5 kHz
+Signal but that's not interesting for the emulator.
+The timing comes from a base clock of 4 MHz that is divided
+multiple times:
+
+4 MHz / 64 - 62.5 kHz -> used for filtered amplification
+62.5 kHz / 64 - 976 Hz -> 1.024 ms
+976 / 7 - 140 Hz -> 7.2 ms
+976 / 5 - 195 Hz -> 5.1 ms
+
+short - 5.12 ms - 1 bit
+long - 7.168 ms - 0 bit
+
+ +-+ +-+ +-+ +-+
+ | | | | | | | |
+ +--+ +-----+ +-------+ +-----+ +--....
+
+ | | |---0---| |--1--|
+ ^
+ |
+ Startbit = shift key
+
+The keyboard can have 64 keys with an additional key that is used by
+the KC as shift key. This additional key is directly connected to the
+chip and changes the startbit. All other keys are arranged in a matrix.
+
+The chip sends full words of 7 bits (including the startbit) at least
+twice for each keypress with a spacing of 14 * 1.024 ms. If the key
+is still pressed the double word is repeated after 19 * 1.024 ms.
+
+The impulses trigger the pio interrupt line for channel B that
+triggers the time measurement by the CTC channel 3."
+
+
+**********************************************************************
+
+
+ The CTC timer 3 count is initialised at 0x08f and counts down.
+
+ The pulses are connected into PIO /BSTB and generate a interrupt
+ on a positive edge. A pulse will not get through if BRDY from PIO is
+ not true!
+
+ Then the interrupt occurs, the CTC count is read. The time between
+ pulses is therefore measured by the CTC.
+
+ The time is checked and depending on the value the KC detects
+ a 1 or 0 bit sent from the keyboard.
+
+ Summary From code below:
+
+ 0x065<=count<0x078 -> 0 bit "short pulse"
+ 0x042<=count<0x065 -> 1 bit "long pulse"
+ count<0x014 -> ignore
+ count>=0x078 -> ignore
+ 0x014<=count<0x042 -> signal end of code "very long pulse"
+
+ codes are sent bit 0, bit 1, bit 2...bit 7. bit 0 is the state
+ of the shift key.
+
+ Torsten's e-mail indicates "short pulse" for 1 bit, and "long
+ pulse" for 0 bit, but I found this to be incorrect. However,
+ the timings are correct.
+
+
+ Keyboard reading procedure extracted from KC85/4 system rom:
+
+0345 f5 push af
+0346 db8f in a,(#8f) ; get CTC timer 3 count
+0348 f5 push af
+0349 3ea7 ld a,#a7 ; channel 3, enable int, select counter mode, control word
+ ; write, software reset, time constant follows
+034b d38f out (#8f),a
+034d 3e8f ld a,#8f ; time constant
+034f d38f out (#8f),a
+0351 f1 pop af
+
+0352 fb ei
+0353 b7 or a ; count is over
+0354 284d jr z,#03a3 ;
+
+ ;; check count is in range
+0356 fe14 cp #14
+0358 3849 jr c,#03a3 ;
+035a fe78 cp #78
+035c 3045 jr nc,#03a3 ;
+
+ ;; at this point, time must be between #14 and #77 to be valid
+
+ ;; if >=#65, then carry=0, and a 0 bit has been detected
+
+035e fe65 cp #65
+0360 303d jr nc,#039f ; (61)
+
+ ;; if <#65, then a 1 bit has been detected. carry is set with the addition below.
+ ;; a carry will be generated if the count is >#42
+
+ ;; therefore for a 1 bit to be generated, then #42<=time<#65
+ ;; must be true.
+
+0362 c6be add a,#be
+0364 3839 jr c,#039f ; (57)
+
+ ;; this code appears to take the transmitted scan-code
+ ;; and converts it into a useable code by the os???
+0366 e5 push hl
+0367 d5 push de
+ ;; convert hardware scan-code into os code
+0368 dd7e0c ld a,(ix+#0c)
+036b 1f rra
+036c ee01 xor #01
+036e dd6e0e ld l,(ix+#0e)
+0371 dd660f ld h,(ix+#0f)
+0374 1600 ld d,#00
+0376 5f ld e,a
+0377 19 add hl,de
+0378 7e ld a,(hl)
+0379 d1 pop de
+037a e1 pop hl
+
+ ;; shift lock pressed?
+037b ddcb087e bit 7,(ix+#08)
+037f 200a jr nz,#038b
+ ;; no.
+
+ ;; alpha char?
+0381 fe40 cp #40
+0383 3806 jr c,#038b
+0385 fe80 cp #80
+0387 3002 jr nc,#038b
+ ;; yes, it is a alpha char
+ ;; force to lower case
+0389 ee20 xor #20
+
+038b ddbe0d cp (ix+#0d) ;; same as stored?
+038e 201d jr nz,#03ad
+
+ ;; yes - must be held for a certain time before it can repeat?
+0390 f5 push af
+0391 3ae0b7 ld a,(#b7e0)
+0394 ddbe0a cp (ix+#0a)
+0397 3811 jr c,#03aa
+0399 f1 pop af
+
+039a dd340a inc (ix+#0a) ;; incremenent repeat count?
+039d 1804 jr #03a3
+
+ ;; update scan-code received so far
+
+039f ddcb0c1e rr (ix+#0c) ; shift in 0 or 1 bit depending on what has been selected
+
+03a3 db89 in a,(#89) ; used to clear brdy
+03a5 d389 out (#89),a
+03a7 f1 pop af
+03a8 ed4d reti
+
+03aa f1 pop af
+03ab 1808 jr #03b5
+
+;; clear count
+03ad dd360a00 ld (ix+#0a),#00
+
+;; shift lock?
+03b1 fe16 cp #16
+03b3 2809 jr z,#03be
+
+;; store char
+03b5 dd770d ld (ix+#0d),a
+03b8 ddcb08c6 set 0,(ix+#08)
+03bc 18e5 jr #03a3
+
+;; toggle shift lock on/off
+03be dd7e08 ld a,(ix+#08)
+03c1 ee80 xor #80
+03c3 dd7708 ld (ix+#08),a
+;; shift/lock was last key pressed
+03c6 3e16 ld a,#16
+03c8 18eb jr #03b5
+
+03ca b7 or a
+03cb ddcb0846 bit 0,(ix+#08)
+03cf c8 ret z
+03d0 dd7e0d ld a,(ix+#0d)
+03d3 37 scf
+03d4 c9 ret
+03d5 cdcae3 call #e3ca
+03d8 d0 ret nc
+03d9 ddcb0886 res 0,(ix+#08)
+03dd c9 ret
+03de cdcae3 call #e3ca
+03e1 d0 ret nc
+03e2 fe03 cp #03
+03e4 37 scf
+03e5 c8 ret z
+03e6 a7 and a
+03e7 c9 ret
+
+
+ Keyboard reading procedure extracted from KC85/3 rom:
+
+
+019a f5 push af
+019b db8f in a,(#8f)
+019d f5 push af
+019e 3ea7 ld a,#a7
+01a0 d38f out (#8f),a
+01a2 3e8f ld a,#8f
+01a4 d38f out (#8f),a
+01a6 f1 pop af
+01a7 ddcb085e bit 3,(ix+#08)
+01ab ddcb089e res 3,(ix+#08)
+01af 2055 jr nz,#0206 ; (85)
+
+01b1 fe65 cp #65
+01b3 3054 jr nc,#0209
+
+
+ ;; count>=#65 = 0 bit
+ ;; #44<=count<#65 = 1 bit
+ ;; count<#44 = end of code
+
+01b5 fe44 cp #44
+01b7 3053 jr nc,#020c
+01b9 e5 push hl
+01ba d5 push de
+01bb ddcb0c3e srl (ix+#0c)
+01bf dd7e08 ld a,(ix+#08)
+01c2 e680 and #80
+01c4 07 rlca
+01c5 ddae0c xor (ix+#0c)
+01c8 2600 ld h,#00
+01ca dd5e0e ld e,(ix+#0e)
+01cd dd560f ld d,(ix+#0f)
+01d0 6f ld l,a
+01d1 19 add hl,de
+01d2 7e ld a,(hl)
+01d3 d1 pop de
+01d4 e1 pop hl
+01d5 ddbe0d cp (ix+#0d)
+01d8 2811 jr z,#01eb ; (17)
+01da dd770d ld (ix+#0d),a
+01dd ddcb08a6 res 4,(ix+#08)
+01e1 ddcb08c6 set 0,(ix+#08)
+01e5 dd360a00 ld (ix+#0a),#00
+01e9 181b jr #0206 ; (27)
+01eb dd340a inc (ix+#0a)
+01ee ddcb0866 bit 4,(ix+#08)
+01f2 200c jr nz,#0200 ; (12)
+01f4 ddcb0a66 bit 4,(ix+#0a)
+01f8 280c jr z,#0206 ; (12)
+01fa ddcb08e6 set 4,(ix+#08)
+01fe 18e1 jr #01e1 ; (-31)
+0200 ddcb0a4e bit 1,(ix+#0a)
+0204 20db jr nz,#01e1 ; (-37)
+0206 f1 pop af
+0207 ed4d reti
+0209 b7 or a
+020a 1801 jr #020d ; (1)
+020c 37 scf
+020d ddcb0c1e rr (ix+#0c)
+0211 18f3 jr #0206 ; (-13)
+
+*********************************************************************/
+
+
+#include "emu.h"
+#include "kc_keyb.h"
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+const device_type KC_KEYBOARD = &device_creator<kc_keyboard_device>;
+
+//**************************************************************************
+// Input Ports
+//**************************************************************************
+
+// this is a fake keyboard layout. The keys are converted into codes
+// which are transmitted by the keyboard to the base-unit. key code can
+// be calculated as (line*8)+bit_index
+
+static INPUT_PORTS_START( kc_keyboard )
+ // start of keyboard scan-codes
+ // codes 0-7
+ PORT_START("KEY0")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('-') PORT_CHAR('=')
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
+ // codes 8-15
+ PORT_START("KEY1")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('^') PORT_CHAR(0x00AC)
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Clr") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(':') PORT_CHAR('*')
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
+ // codes 16-23
+ PORT_START("KEY2")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('@')
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
+ // codes 24-31
+ PORT_START("KEY3")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Ins") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Brk") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
+ // codes 32-39
+ PORT_START("KEY4")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Stop") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(PAUSE))
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
+ // codes 40-47
+ PORT_START("KEY5")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
+ PORT_BIT(0x08, 0x00, IPT_UNUSED)
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
+ // codes 48-56
+ PORT_START("KEY6")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('-') PORT_CHAR('|')
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR('+') PORT_CHAR(';')
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
+ // codes 56-63
+ PORT_START("KEY7")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
+ PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
+ PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
+ PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
+ // end of keyboard scan-codes
+ PORT_START("SHIFT")
+ // has a single shift key. Mapped here to left and right shift.
+ // shift is connected to the MOC pin of the U807
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
+INPUT_PORTS_END
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// kc_keyboard_device - constructor
+//-------------------------------------------------
+kc_keyboard_device::kc_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, KC_KEYBOARD, "KC Keyboard", tag, owner, clock, "kc_keyboard", __FILE__),
+ m_write_out(*this)
+{
+}
+
+//-------------------------------------------------
+// kc_keyboard_device - destructor
+//-------------------------------------------------
+
+kc_keyboard_device::~kc_keyboard_device()
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void kc_keyboard_device::device_start()
+{
+ // resolve callbacks
+ m_write_out.resolve_safe();
+
+ m_timer_transmit_pulse = timer_alloc(TIMER_TRANSMIT_PULSE);
+
+ m_timer_transmit_pulse->adjust(attotime::from_usec(1024), 0, attotime::from_usec(1024));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void kc_keyboard_device::device_reset()
+{
+ // set initial state
+ m_write_out(CLEAR_LINE);
+
+ m_transmit_buffer.pulse_sent = 0;
+ m_transmit_buffer.pulse_count = 0;
+
+ memset(&m_transmit_buffer, 0, sizeof(m_transmit_buffer));
+}
+
+//-------------------------------------------------
+// input_ports - device-specific input ports
+//-------------------------------------------------
+
+ioport_constructor kc_keyboard_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME( kc_keyboard );
+}
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void kc_keyboard_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case TIMER_TRANSMIT_PULSE:
+ if (m_transmit_buffer.pulse_sent < m_transmit_buffer.pulse_count)
+ {
+ // byte containing pulse state
+ int pulse_byte_count = m_transmit_buffer.pulse_sent>>3;
+ // bit within byte containing pulse state
+ int pulse_bit_count = 7 - (m_transmit_buffer.pulse_sent & 0x07);
+
+ // get current pulse state
+ int pulse_state = (m_transmit_buffer.data[pulse_byte_count]>>pulse_bit_count) & 0x01;
+
+ LOG(("KC keyboard sending pulse: %02x\n", pulse_state));
+
+ // send pulse
+ m_write_out(pulse_state ? ASSERT_LINE : CLEAR_LINE);
+
+ // update counts
+ m_transmit_buffer.pulse_sent++;
+ }
+ else
+ {
+ // if there is nothing to send, rescan the keyboard
+ static const char *const keynames[] = { "KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5", "KEY6", "KEY7" };
+
+ m_transmit_buffer.pulse_sent = 0;
+ m_transmit_buffer.pulse_count = 0;
+
+ // scan all lines (excluding shift)
+ for (int i=0; i<8; i++)
+ {
+ UINT8 keyboard_line_data = ioport(keynames[i])->read();
+
+ // scan through each bit
+ for (int b=0; b<8; b++)
+ {
+ // is pressed?
+ if ((keyboard_line_data & (1<<b)) != 0)
+ {
+ // generate fake code
+ UINT8 code = (i<<3) | b;
+ LOG(("Code: %02x\n",code));
+
+ transmit_scancode(code);
+ }
+ }
+ }
+ }
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// add a pulse
+//-------------------------------------------------
+
+void kc_keyboard_device::add_pulse_to_transmit_buffer(int pulse_state, int pulse_number)
+{
+ if (m_transmit_buffer.pulse_count + 1 < KC_TRANSMIT_BUFFER_LENGTH)
+ {
+ for (int i=0; i<pulse_number; i++)
+ {
+ int pulse_byte_count = m_transmit_buffer.pulse_count>>3;
+ int pulse_bit_count = 7-(m_transmit_buffer.pulse_count & 0x07);
+
+ if (pulse_state)
+ m_transmit_buffer.data[pulse_byte_count] |= (1<<pulse_bit_count);
+ else
+ m_transmit_buffer.data[pulse_byte_count] &= ~(1<<pulse_bit_count);
+
+ m_transmit_buffer.pulse_count++;
+ }
+ }
+}
+
+//-------------------------------------------------
+// fill transmit buffer with pulse for 0 or 1 bit
+//-------------------------------------------------
+
+void kc_keyboard_device::add_bit(int bit)
+{
+ if (bit)
+ add_pulse_to_transmit_buffer(0, 7);
+ else
+ add_pulse_to_transmit_buffer(0, 5);
+
+ // "end of bit" pulse -> end of time for bit
+ add_pulse_to_transmit_buffer(1);
+}
+
+
+//-------------------------------------------------
+// begin pulse transmit
+//-------------------------------------------------
+
+void kc_keyboard_device::transmit_scancode(UINT8 scan_code)
+{
+ // initial pulse -> start of code
+ add_pulse_to_transmit_buffer(1);
+
+ // state of shift key
+ add_bit((ioport("SHIFT")->read() & 0x01)^0x01);
+
+ for (int i=0; i<6; i++)
+ {
+ // each bit in turn
+ add_bit(scan_code & (1<<i));
+ }
+
+ // signal end of scan-code
+ add_pulse_to_transmit_buffer(0, 14);
+
+ add_pulse_to_transmit_buffer(1);
+
+ // back to original state
+ add_pulse_to_transmit_buffer(0);
+}