]> git.friedersdorff.com Git - max/tmk_keyboard.git/blob - converter/ibmpc_usb/ibmpc_usb.c
696bf904f633a4124acc6a95261630c2eea108a7
[max/tmk_keyboard.git] / converter / ibmpc_usb / ibmpc_usb.c
1 /*
2 Copyright 2019 Jun Wako <wakojun@gmail.com>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stdint.h>
19 #include <stdbool.h>
20 #include "print.h"
21 #include "util.h"
22 #include "debug.h"
23 #include "ibmpc.h"
24 #include "host.h"
25 #include "led.h"
26 #include "matrix.h"
27 #include "timer.h"
28 #include "action.h"
29 #include "ibmpc_usb.h"
30 #include "ibmpc.h"
31
32
33 static void matrix_make(uint8_t code);
34 static void matrix_break(uint8_t code);
35
36 static int8_t process_cs1(void);
37 static int8_t process_cs2(void);
38 static int8_t process_cs3(void);
39
40
41 static uint8_t matrix[MATRIX_ROWS];
42 #define ROW(code)      ((code>>3)&0x0F)
43 #define COL(code)      (code&0x07)
44
45 static int16_t read_wait(uint16_t wait_ms)
46 {
47     uint16_t start = timer_read();
48     int16_t code;
49     while ((code = ibmpc_host_recv()) == -1 && timer_elapsed(start) < wait_ms);
50     return code;
51 }
52
53 static uint16_t read_keyboard_id(void)
54 {
55     uint16_t id = 0;
56     int16_t  code = 0;
57
58     // Disable
59     //code = ibmpc_host_send(0xF5);
60
61     // Read ID
62     code = ibmpc_host_send(0xF2);
63     if (code == -1) { id = 0xFFFF; goto DONE; }     // XT or No keyboard
64     if (code != 0xFA) { id = 0xFFFE; goto DONE; }   // Broken PS/2?
65
66     code = read_wait(1000);
67     if (code == -1) { id = 0x0000; goto DONE; }     // AT
68     id = (code & 0xFF)<<8;
69
70     code = read_wait(1000);
71     id |= code & 0xFF;
72
73 DONE:
74     // Enable
75     //code = ibmpc_host_send(0xF4);
76
77     return id;
78 }
79
80 void hook_early_init(void)
81 {
82     ibmpc_host_init();
83     ibmpc_host_enable();
84 }
85
86 void matrix_init(void)
87 {
88     debug_enable = true;
89
90     // initialize matrix state: all keys off
91     for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
92
93     return;
94 }
95
96 /*
97  * keyboard recognition
98  *
99  * 1. Send F2 to get keyboard ID
100  *      a. no ACK(FA): XT keyobard
101  *      b. ACK but no ID: 84-key AT keyboard CodeSet2
102  *      c. ID is AB 83: PS/2 keyboard CodeSet2
103  *      d. ID is BF BF: Terminal keyboard CodeSet3
104  *      e. error on recv: maybe broken PS/2
105  */
106 uint16_t keyboard_id = 0x0000;
107 keyboard_kind_t keyboard_kind = NONE;
108 uint8_t matrix_scan(void)
109 {
110     // scan code reading states
111     static enum {
112         INIT,
113         WAIT_STARTUP,
114         READ_ID,
115         LED_SET,
116         LOOP,
117         END
118     } state = INIT;
119     static uint16_t init_time;
120
121
122     if (ibmpc_error) {
123         xprintf("\nERR: %02X\n", ibmpc_error);
124
125         // when recv error, neither send error nor buffer full
126         if (!(ibmpc_error & (IBMPC_ERR_SEND | IBMPC_ERR_FULL))) {
127             // keyboard init again
128             if (state == LOOP) {
129                 xprintf("init\n");
130                 state = INIT;
131             }
132             // probably XT signal, not AT
133             if (state == WAIT_STARTUP) {
134                 state = READ_ID;
135             }
136         }
137
138         // clear or process error
139         ibmpc_error = IBMPC_ERR_NONE;
140     }
141
142     switch (state) {
143         case INIT:
144             ibmpc_protocol = IBMPC_PROTOCOL_AT;
145             keyboard_kind = NONE;
146             keyboard_id = 0x0000;
147             init_time = timer_read();
148             xprintf("I%u\n", init_time);
149
150             // re-initialize keyboard
151             // XT: hard reset 500ms for IBM XT Type-1 keyboard and clones
152             // XT: soft reset 20ms min(clock Lo)
153             ibmpc_host_disable();   // soft reset: inihibit(clock Lo/Data Hi)
154             IBMPC_RST_LO();
155             wait_ms(500);
156             IBMPC_RST_HIZ();
157             ibmpc_host_enable();    // soft reset: idle(clock Hi/Data Hi)
158
159             // TODO: should in while disabling interrupt?
160             // clear ISR state after protocol recognition
161             ibmpc_host_isr_clear();
162
163             matrix_clear();
164             clear_keyboard();
165
166             state = WAIT_STARTUP;
167             break;
168         case WAIT_STARTUP:
169             // 1) Read and ignore BAT code and ID when power-up
170             // For example, XT/AT sends 'AA' and Terminal sends 'AA BF BF' after BAT
171             // AT 84-key: POR and BAT can take 900-9900ms according to AT TechRef [8] 4-7
172             // AT 101/102-key: POR and BAT can take 450-2500ms according to AT TechRef [8] 4-39
173             // 2) Read and ignore key input by user when signal handling/protocol error occurs
174             // This can happen in case of keyboard hotswap, unstable hardware, signal integrity problem or bug
175
176             if (ibmpc_host_recv() != -1 || timer_elapsed(init_time) > 10000) {
177                 // 500ms max wait for ID after AA TechRef [8] 4-41
178                 // 122-key Terminal 6110345: 1ms wait is enough
179                 wait_ms(100); ibmpc_host_recv();
180                 wait_ms(100); ibmpc_host_recv();
181                 state = READ_ID;
182             }
183
184             // XT's 'AA' can not be handled correctly because protocol is configured as AT at this point.
185             // TODO: Check ISR timeout error for XT AA?
186             break;
187         case READ_ID:
188             xprintf("R%u\n", timer_read());
189             keyboard_id = read_keyboard_id();
190             if (ibmpc_error) {
191                 xprintf("\nERR: %02X\n", ibmpc_error);
192                 ibmpc_error = IBMPC_ERR_NONE;
193             }
194             xprintf("ID: %04X\n", keyboard_id);
195             if (0xAB00 == (keyboard_id & 0xFF00)) {
196                 // CodeSet2 PS/2
197                 keyboard_kind = PC_AT;
198             } else if (0xBF00 == (keyboard_id & 0xFF00)) {
199                 // CodeSet3 Terminal
200                 keyboard_kind = PC_TERMINAL;
201             } else if (0x0000 == keyboard_id) {
202                 // CodeSet2 AT
203                 keyboard_kind = PC_AT;
204             } else if (0xFFFF == keyboard_id) {
205                 // CodeSet1 XT
206                 keyboard_kind = PC_XT;
207             } else if (0xFFFE == keyboard_id) {
208                 // CodeSet2 PS/2 fails to response?
209                 keyboard_kind = PC_AT;
210             } else if (0x00FF == keyboard_id) {
211                 // Mouse is not supported
212                 xprintf("Mouse: not supported\n");
213                 keyboard_kind = NONE;
214             } else {
215                 keyboard_kind = PC_AT;
216             }
217
218             // protocol
219             if (keyboard_kind == PC_XT) {
220                 xprintf("kbd: XT\n");
221                 ibmpc_protocol = IBMPC_PROTOCOL_XT;
222             } else if (keyboard_kind == PC_AT) {
223                 xprintf("kbd: AT\n");
224                 ibmpc_protocol = IBMPC_PROTOCOL_AT;
225             } else if (keyboard_kind == PC_TERMINAL) {
226                 xprintf("kbd: Terminal\n");
227                 ibmpc_protocol = IBMPC_PROTOCOL_AT;
228                 // Set all keys - make/break [3]p.23
229                 ibmpc_host_send(0xF8);
230             } else {
231                 xprintf("kbd: Unknown\n");
232                 ibmpc_protocol = IBMPC_PROTOCOL_AT;
233             }
234
235             // clear ISR state after protocol recognition
236             ibmpc_host_isr_clear();
237
238             state = LED_SET;
239             break;
240         case LED_SET:
241             xprintf("L%u\n", timer_read());
242             led_set(host_keyboard_leds());
243             state = LOOP;
244         case LOOP:
245             switch (keyboard_kind) {
246                 case PC_XT:
247                     if (process_cs1() == -1) state = INIT;
248                     break;
249                 case PC_AT:
250                     if (process_cs2() == -1) state = INIT;
251                     break;
252                 case PC_TERMINAL:
253                     if (process_cs3() == -1) state = INIT;
254                     break;
255                 default:
256                     break;
257             }
258             break;
259         default:
260             break;
261     }
262     return 1;
263 }
264
265 inline
266 bool matrix_is_on(uint8_t row, uint8_t col)
267 {
268     return (matrix[row] & (1<<col));
269 }
270
271 inline
272 uint8_t matrix_get_row(uint8_t row)
273 {
274     return matrix[row];
275 }
276
277 uint8_t matrix_key_count(void)
278 {
279     uint8_t count = 0;
280     for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
281         count += bitpop(matrix[i]);
282     }
283     return count;
284 }
285
286
287 inline
288 static void matrix_make(uint8_t code)
289 {
290     if (!matrix_is_on(ROW(code), COL(code))) {
291         matrix[ROW(code)] |= 1<<COL(code);
292     }
293 }
294
295 inline
296 static void matrix_break(uint8_t code)
297 {
298     if (matrix_is_on(ROW(code), COL(code))) {
299         matrix[ROW(code)] &= ~(1<<COL(code));
300     }
301 }
302
303 void matrix_clear(void)
304 {
305     for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
306 }
307
308 void led_set(uint8_t usb_led)
309 {
310     if (keyboard_kind != PC_AT) return;
311
312     uint8_t ibmpc_led = 0;
313     if (usb_led &  (1<<USB_LED_SCROLL_LOCK))
314         ibmpc_led |= (1<<IBMPC_LED_SCROLL_LOCK);
315     if (usb_led &  (1<<USB_LED_NUM_LOCK))
316         ibmpc_led |= (1<<IBMPC_LED_NUM_LOCK);
317     if (usb_led &  (1<<USB_LED_CAPS_LOCK))
318         ibmpc_led |= (1<<IBMPC_LED_CAPS_LOCK);
319     ibmpc_host_set_led(ibmpc_led);
320 }
321
322
323 /*******************************************************************************
324  * XT: Scan Code Set 1
325  *
326  * See [3], [a]
327  *
328  * E0-escaped scan codes are translated into unused range of the matrix.(54-7F)
329  *
330  *     01-53: Normal codes used in original XT keyboard
331  *     54-7F: Not used in original XT keyboard
332  *
333  *         0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
334  *     50  -   -   -   -   *   *   x   x   x   x   *   *   *   *   *   *
335  *     60  *   *   *   *   x   x   x   x   x   x   x   x   x   x   x   *
336  *     70  x   *   *   x   *   *   x   *   *   x   *   x   *   x   x   *
337  *
338  * -: codes existed in original XT keyboard
339  * *: E0-escaped codes translated
340  * x: Non-espcaped codes(Some are not used in real keyboards probably)
341  *
342  * Codes assigned in range 54-7F:
343  *
344  *     50  -                60  Up*                 70  KANAx
345  *     51  -                61  Left*               71  Insert*
346  *     52  -                62  Down*               72  Delete*
347  *     53  -                63  Right*              73  ROx
348  *     54  PrintScr*        64  F13x                74  Home*
349  *     55  Pause*           65  F14x                75  End*
350  *     56  Euro2x           66  F15x                76  F24x
351  *     57  F11x             67  F16x                77  PageUp*
352  *     58  F12x             68  F17x                78  PageDown*
353  *     59  Keypad=x         69  F18x                79  HENKANx
354  *     5A  LGUI*            6A  F19x                7A  RCTL*
355  *     5B  RGUI*            6B  F20x                7B  MUHENKANx
356  *     5C  APP*             6C  F21x                7C  RALT*
357  *     5D  Mute*            6D  F22x                7D  JPYx
358  *     5E  Volume Down*     6E  F23x                7E  Keypad,x
359  *     5F  Volume Up*       6F  Keypad Enter*       7F  Keypad/ *
360  */
361 static uint8_t cs1_e0code(uint8_t code) {
362     switch(code) {
363         // Original IBM XT keyboard doesn't use E0-codes probably
364         // Some XT compatilble keyobards need these keys?
365         case 0x37: return 0x54; // Print Screen
366         case 0x46: return 0x55; // Ctrl + Pause
367         case 0x5B: return 0x5A; // Left  GUI
368         case 0x5C: return 0x5B; // Right GUI
369         case 0x5D: return 0x5C; // Application
370         case 0x20: return 0x5D; // Mute
371         case 0x2E: return 0x5E; // Volume Down
372         case 0x30: return 0x5F; // Volume Up
373         case 0x48: return 0x60; // Up
374         case 0x4B: return 0x61; // Left
375         case 0x50: return 0x62; // Down
376         case 0x4D: return 0x63; // Right
377         case 0x1C: return 0x6F; // Keypad Enter
378         case 0x52: return 0x71; // Insert
379         case 0x53: return 0x72; // Delete
380         case 0x47: return 0x74; // Home
381         case 0x4F: return 0x75; // End
382         case 0x49: return 0x77; // Page Up
383         case 0x51: return 0x78; // Page Down
384         case 0x1D: return 0x7A; // Right Ctrl
385         case 0x38: return 0x7C; // Right Alt
386         case 0x35: return 0x7F; // Keypad /
387
388         // Shared matrix cell with other keys
389         case 0x5E: return 0x70; // Power (KANA)
390         case 0x5F: return 0x79; // Sleep (HENKAN)
391         case 0x63: return 0x7B; // Wake  (MUHENKAN)
392
393         default:
394            xprintf("!CS1_?!\n");
395            return code;
396     }
397     return 0x00;
398 }
399
400 static int8_t process_cs1(void)
401 {
402     static enum {
403         INIT,
404         E0,
405         // Pause: E1 1D 45, E1 9D C5
406         E1,
407         E1_1D,
408         E1_9D,
409     } state = INIT;
410
411     uint16_t code = ibmpc_host_recv();
412     if (code == -1) {
413         return 0;
414     }
415
416     switch (state) {
417         case INIT:
418             switch (code) {
419                 case 0xE0:
420                     state = E0;
421                     break;
422                 case 0xE1:
423                     state = E1;
424                     break;
425                 default:
426                     if (code < 0x80)
427                         matrix_make(code);
428                     else
429                         matrix_break(code & 0x7F);
430                     break;
431             }
432             break;
433         case E0:
434             switch (code) {
435                 case 0x2A:
436                 case 0xAA:
437                 case 0x36:
438                 case 0xB6:
439                     //ignore fake shift
440                     state = INIT;
441                     break;
442                 default:
443                     if (code < 0x80)
444                         matrix_make(cs1_e0code(code));
445                     else
446                         matrix_break(cs1_e0code(code & 0x7F));
447                     state = INIT;
448                     break;
449             }
450             break;
451         case E1:
452             switch (code) {
453                 case 0x1D:
454                     state = E1_1D;
455                     break;
456                 case 0x9D:
457                     state = E1_9D;
458                     break;
459                 default:
460                     state = INIT;
461                     break;
462             }
463             break;
464         case E1_1D:
465             switch (code) {
466                 case 0x45:
467                     matrix_make(0x55);
468                     break;
469                 default:
470                     state = INIT;
471                     break;
472             }
473             break;
474         case E1_9D:
475             switch (code) {
476                 case 0x45:
477                     matrix_break(0x55);
478                     break;
479                 default:
480                     state = INIT;
481                     break;
482             }
483             break;
484         default:
485             state = INIT;
486     }
487     return 0;
488 }
489
490
491 /*******************************************************************************
492  * AT, PS/2: Scan Code Set 2
493  *
494  * Exceptional Handling
495  * --------------------
496  * Some keys should be handled exceptionally. See [b].
497  *
498  * Scan codes are varied or prefix/postfix'd depending on modifier key state.
499  *
500  * 1) Insert, Delete, Home, End, PageUp, PageDown, Up, Down, Right, Left
501  *     a) when Num Lock is off
502  *     modifiers | make                      | break
503  *     ----------+---------------------------+----------------------
504  *     Ohter     |                    <make> | <break>
505  *     LShift    | E0 F0 12           <make> | <break>  E0 12
506  *     RShift    | E0 F0 59           <make> | <break>  E0 59
507  *     L+RShift  | E0 F0 12  E0 F0 59 <make> | <break>  E0 59 E0 12
508  *
509  *     b) when Num Lock is on
510  *     modifiers | make                      | break
511  *     ----------+---------------------------+----------------------
512  *     Other     | E0 12              <make> | <break>  E0 F0 12
513  *     Shift'd   |                    <make> | <break>
514  *
515  *     Handling: These prefix/postfix codes are ignored.
516  *
517  *
518  * 2) Keypad /
519  *     modifiers | make                      | break
520  *     ----------+---------------------------+----------------------
521  *     Ohter     |                    <make> | <break>
522  *     LShift    | E0 F0 12           <make> | <break>  E0 12
523  *     RShift    | E0 F0 59           <make> | <break>  E0 59
524  *     L+RShift  | E0 F0 12  E0 F0 59 <make> | <break>  E0 59 E0 12
525  *
526  *     Handling: These prefix/postfix codes are ignored.
527  *
528  *
529  * 3) PrintScreen
530  *     modifiers | make         | break
531  *     ----------+--------------+-----------------------------------
532  *     Other     | E0 12  E0 7C | E0 F0 7C  E0 F0 12
533  *     Shift'd   |        E0 7C | E0 F0 7C
534  *     Control'd |        E0 7C | E0 F0 7C
535  *     Alt'd     |           84 | F0 84
536  *
537  *     Handling: These prefix/postfix codes are ignored, and both scan codes
538  *               'E0 7C' and 84 are seen as PrintScreen.
539  *
540  * 4) Pause
541  *     modifiers | make(no break code)
542  *     ----------+--------------------------------------------------
543  *     Other     | E1 14 77 E1 F0 14 F0 77
544  *     Control'd | E0 7E E0 F0 7E
545  *
546  *     Handling: Both code sequences are treated as a whole.
547  *               And we need a ad hoc 'pseudo break code' hack to get the key off
548  *               because it has no break code.
549  *
550  * Notes:
551  * 'Hanguel/English'(F1) and 'Hanja'(F2) have no break code. See [a].
552  * These two Korean keys need exceptional handling and are not supported for now.
553  *
554  */
555 static uint8_t cs2_e0code(uint8_t code) {
556     switch(code) {
557         // E0 prefixed codes translation See [a].
558         case 0x11: return 0x0F; // right alt
559         case 0x14: return 0x17; // right control
560         case 0x1F: return 0x19; // left GUI
561         case 0x27: return 0x1F; // right GUI
562         case 0x2F: return 0x5C; // apps
563         case 0x4A: return 0x60; // keypad /
564         case 0x5A: return 0x62; // keypad enter
565         case 0x69: return 0x27; // end
566         case 0x6B: return 0x53; // cursor left
567         case 0x6C: return 0x2F; // home
568         case 0x70: return 0x39; // insert
569         case 0x71: return 0x37; // delete
570         case 0x72: return 0x3F; // cursor down
571         case 0x74: return 0x47; // cursor right
572         case 0x75: return 0x4F; // cursor up
573         case 0x7A: return 0x56; // page down
574         case 0x7D: return 0x5E; // page up
575         case 0x7C: return 0x6F; // Print Screen
576         case 0x7E: return 0x00; // Control'd Pause
577
578         case 0x21: return 0x65; // volume down
579         case 0x32: return 0x6E; // volume up
580         case 0x23: return 0x7F; // mute
581         case 0x10: return 0x08; // (WWW search)     -> F13
582         case 0x18: return 0x10; // (WWW favourites) -> F14
583         case 0x20: return 0x18; // (WWW refresh)    -> F15
584         case 0x28: return 0x20; // (WWW stop)       -> F16
585         case 0x30: return 0x28; // (WWW forward)    -> F17
586         case 0x38: return 0x30; // (WWW back)       -> F18
587         case 0x3A: return 0x38; // (WWW home)       -> F19
588         case 0x40: return 0x40; // (my computer)    -> F20
589         case 0x48: return 0x48; // (email)          -> F21
590         case 0x2B: return 0x50; // (calculator)     -> F22
591         case 0x34: return 0x08; // (play/pause)     -> F13
592         case 0x3B: return 0x10; // (stop)           -> F14
593         case 0x15: return 0x18; // (previous track) -> F15
594         case 0x4D: return 0x20; // (next track)     -> F16
595         case 0x50: return 0x28; // (media select)   -> F17
596         case 0x5E: return 0x50; // (ACPI wake)      -> F22
597         case 0x3F: return 0x57; // (ACPI sleep)     -> F23
598         case 0x37: return 0x5F; // (ACPI power)     -> F24
599
600         // https://github.com/tmk/tmk_keyboard/pull/636
601         case 0x03: return 0x18; // Help        DEC LK411 -> F15
602         case 0x04: return 0x08; // F13         DEC LK411
603         case 0x0B: return 0x20; // Do          DEC LK411 -> F16
604         case 0x0C: return 0x10; // F14         DEC LK411
605         case 0x0D: return 0x19; // LCompose    DEC LK411 -> LGUI
606         case 0x79: return 0x6D; // KP-         DEC LK411 -> PCMM
607         case 0x83: return 0x28; // F17         DEC LK411
608         default: return (code & 0x7F);
609     }
610 }
611
612 static int8_t process_cs2(void)
613 {
614     // scan code reading states
615     static enum {
616         INIT,
617         F0,
618         E0,
619         E0_F0,
620         // Pause
621         E1,
622         E1_14,
623         E1_F0,
624         E1_F0_14,
625         E1_F0_14_F0,
626     } state = INIT;
627
628     uint16_t code = ibmpc_host_recv();
629     if (code == -1) {
630         return 0;
631     }
632
633     switch (state) {
634         case INIT:
635             switch (code) {
636                 case 0xE0:
637                     state = E0;
638                     break;
639                 case 0xF0:
640                     state = F0;
641                     break;
642                 case 0xE1:
643                     state = E1;
644                     break;
645                 case 0x83:  // F7
646                     matrix_make(0x02);
647                     state = INIT;
648                     break;
649                 case 0x84:  // Alt'd PrintScreen
650                     matrix_make(0x6F);
651                     state = INIT;
652                     break;
653                 case 0x00:  // Overrun [3]p.26
654                     matrix_clear();
655                     xprintf("!CS2_OVERRUN!\n");
656                     state = INIT;
657                     break;
658                 case 0xAA:  // Self-test passed
659                 case 0xFC:  // Self-test failed
660                     // reset or plugin-in new keyboard
661                     state = INIT;
662                     return -1;
663                     break;
664                 default:    // normal key make
665                     if (code < 0x80) {
666                         matrix_make(code);
667                     } else {
668                         matrix_clear();
669                         xprintf("!CS2_INIT!\n");
670                     }
671                     state = INIT;
672             }
673             break;
674         case E0:    // E0-Prefixed
675             switch (code) {
676                 case 0x12:  // to be ignored
677                 case 0x59:  // to be ignored
678                     state = INIT;
679                     break;
680                 case 0xF0:
681                     state = E0_F0;
682                     break;
683                 default:
684                     if (code < 0x80) {
685                         matrix_make(cs2_e0code(code));
686                     } else {
687                         matrix_clear();
688                         xprintf("!CS2_E0!\n");
689                     }
690                     state = INIT;
691             }
692             break;
693         case F0:    // Break code
694             switch (code) {
695                 case 0x83:  // F7
696                     matrix_break(0x02);
697                     state = INIT;
698                     break;
699                 case 0x84:  // Alt'd PrintScreen
700                     matrix_break(0x6F);
701                     state = INIT;
702                     break;
703                 default:
704                     if (code < 0x80) {
705                         matrix_break(code);
706                     } else {
707                         matrix_clear();
708                         xprintf("!CS2_F0!\n");
709                     }
710                     state = INIT;
711             }
712             break;
713         case E0_F0: // Break code of E0-prefixed
714             switch (code) {
715                 case 0x12:  // to be ignored
716                 case 0x59:  // to be ignored
717                     state = INIT;
718                     break;
719                 default:
720                     if (code < 0x80) {
721                         matrix_break(cs2_e0code(code));
722                     } else {
723                         matrix_clear();
724                         xprintf("!CS2_E0_F0!\n");
725                     }
726                     state = INIT;
727             }
728             break;
729         // Pause make: E1 14 77
730         case E1:
731             switch (code) {
732                 case 0x14:
733                     state = E1_14;
734                     break;
735                 case 0xF0:
736                     state = E1_F0;
737                     break;
738                 default:
739                     state = INIT;
740             }
741             break;
742         case E1_14:
743             switch (code) {
744                 case 0x77:
745                     matrix_make(0x00);
746                     state = INIT;
747                     break;
748                 default:
749                     state = INIT;
750             }
751             break;
752         // Pause break: E1 F0 14 F0 77
753         case E1_F0:
754             switch (code) {
755                 case 0x14:
756                     state = E1_F0_14;
757                     break;
758                 default:
759                     state = INIT;
760             }
761             break;
762         case E1_F0_14:
763             switch (code) {
764                 case 0xF0:
765                     state = E1_F0_14_F0;
766                     break;
767                 default:
768                     state = INIT;
769             }
770             break;
771         case E1_F0_14_F0:
772             switch (code) {
773                 case 0x77:
774                     matrix_break(0x00);
775                     state = INIT;
776                     break;
777                 default:
778                     state = INIT;
779             }
780             break;
781         default:
782             state = INIT;
783     }
784     return 0;
785 }
786
787 /*
788  * Terminal: Scan Code Set 3
789  *
790  * See [3], [7]
791  *
792  * Scan code 0x83 and 0x84 are handled exceptioanally to fit into 1-byte range index.
793  */
794 static int8_t process_cs3(void)
795 {
796     static enum {
797         READY,
798         F0,
799     } state = READY;
800
801     uint16_t code = ibmpc_host_recv();
802     if (code == -1) {
803         return 0;
804     }
805
806     switch (state) {
807         case READY:
808             switch (code) {
809                 case 0x00:
810                 case 0xFF:
811                     xprintf("!CS3_%02X!\n", code);
812                     return -1;
813                     break;
814                 case 0xF0:
815                     state = F0;
816                     break;
817                 case 0x83:  // F7
818                     matrix_make(0x02);
819                     break;
820                 case 0x84:  // keypad -
821                     matrix_make(0x7F);
822                     break;
823                 default:    // normal key make
824                     if (code < 0x80) {
825                         matrix_make(code);
826                     } else {
827                         xprintf("!CS3_%02X!\n", code);
828                         return -1;
829                     }
830             }
831             break;
832         case F0:    // Break code
833             switch (code) {
834                 case 0x00:
835                 case 0xFF:
836                     xprintf("!CS3_F0_%02X!\n", code);
837                     state = READY;
838                     return -1;
839                     break;
840                 case 0x83:  // F7
841                     matrix_break(0x02);
842                     state = READY;
843                     break;
844                 case 0x84:  // keypad -
845                     matrix_break(0x7F);
846                     state = READY;
847                     break;
848                 default:
849                     state = READY;
850                     if (code < 0x80) {
851                         matrix_break(code);
852                     } else {
853                         xprintf("!CS3_F0_%02X!\n", code);
854                         return -1;
855                     }
856             }
857             break;
858     }
859     return 0;
860 }
861
862 /*
863  * IBM PC Keyboard Protocol Resources:
864  *
865  * [a] Microsoft USB HID to PS/2 Translation Table - Scan Code Set 1 and 2
866  * http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf
867  *
868  * [b] Microsoft Keyboard Scan Code Specification - Special rules of Scan Code Set 1 and 2
869  * http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc
870  *
871  * [1] PS/2 Reference Manuals - Collection of IBM Personal System/2 documents.
872  * http://www.mcamafia.de/pdf/pdfref.htm
873  *
874  * [2] Keyboard and Auxiliary Device Controller - Signal Timing and Format
875  * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
876  *
877  * [3] Keyboards(101- and 102-key) - Keyboard Layout, Scan Code Set, POR, and Commands.
878  * http://www.mcamafia.de/pdf/ibm_hitrc11.pdf
879  *
880  * [4] IBM PC XT Keyboard Protocol
881  * https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol
882  *
883  * [5] IBM Keyboard Scan Code by John Elliott - 83-key, 84-key, 102-key and 122-key
884  * https://www.seasip.info/VintagePC/index.html
885  *
886  * [6] IBM 1391406 Keyboard - Scan Code Set 2 of 102-key PS/2 keyboard
887  * https://www.seasip.info/VintagePC/ibm_1391406.html
888  *
889  * [7] The IBM 6110344 Keyboard - Scan Code Set 3 of 122-key terminal keyboard
890  * https://www.seasip.info/VintagePC/ibm_6110344.html
891  *
892  * [y] TrackPoint Engineering Specifications for version 3E
893  * https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html
894  *
895  * [z] [Soarer's XT/AT/PS2/Terminal to USB converter]
896  * https://geekhack.org/index.php?topic=17458.0
897  *
898  */