qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

m0110.c (23443B)


      1 /*
      2 Copyright 2011,2012 Jun WAKO <wakojun@gmail.com>
      3 
      4 This software is licensed with a Modified BSD License.
      5 All of this is supposed to be Free Software, Open Source, DFSG-free,
      6 GPL-compatible, and OK to use in both free and proprietary applications.
      7 Additions and corrections to this file are welcome.
      8 
      9 
     10 Redistribution and use in source and binary forms, with or without
     11 modification, are permitted provided that the following conditions are met:
     12 
     13 * Redistributions of source code must retain the above copyright
     14   notice, this list of conditions and the following disclaimer.
     15 
     16 * Redistributions in binary form must reproduce the above copyright
     17   notice, this list of conditions and the following disclaimer in
     18   the documentation and/or other materials provided with the
     19   distribution.
     20 
     21 * Neither the name of the copyright holders nor the names of
     22   contributors may be used to endorse or promote products derived
     23   from this software without specific prior written permission.
     24 
     25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     26 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     29 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     31 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     32 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     33 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     34 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     35 POSSIBILITY OF SUCH DAMAGE.
     36 */
     37 /* M0110A Support was contributed by skagon@github */
     38 
     39 #include <stdbool.h>
     40 #include <avr/io.h>
     41 #include <avr/interrupt.h>
     42 #include <util/delay.h>
     43 #include "m0110.h"
     44 #include "debug.h"
     45 
     46 static inline uint8_t  raw2scan(uint8_t raw);
     47 static inline uint8_t  inquiry(void);
     48 static inline uint8_t  instant(void);
     49 static inline void     clock_lo(void);
     50 static inline void     clock_hi(void);
     51 static inline bool     clock_in(void);
     52 static inline void     data_lo(void);
     53 static inline void     data_hi(void);
     54 static inline bool     data_in(void);
     55 static inline uint16_t wait_clock_lo(uint16_t us);
     56 static inline uint16_t wait_clock_hi(uint16_t us);
     57 static inline uint16_t wait_data_lo(uint16_t us);
     58 static inline uint16_t wait_data_hi(uint16_t us);
     59 static inline void     idle(void);
     60 static inline void     request(void);
     61 
     62 #define WAIT_US(stat, us, err)  \
     63     do {                        \
     64         if (!wait_##stat(us)) { \
     65             m0110_error = err;  \
     66             goto ERROR;         \
     67         }                       \
     68     } while (0)
     69 
     70 #define WAIT_MS(stat, ms, err)       \
     71     do {                             \
     72         uint16_t _ms = ms;           \
     73         while (_ms) {                \
     74             if (wait_##stat(1000)) { \
     75                 break;               \
     76             }                        \
     77             _ms--;                   \
     78         }                            \
     79         if (_ms == 0) {              \
     80             m0110_error = err;       \
     81             goto ERROR;              \
     82         }                            \
     83     } while (0)
     84 
     85 #define KEY(raw) ((raw)&0x7f)
     86 #define IS_BREAK(raw) (((raw)&0x80) == 0x80)
     87 
     88 uint8_t m0110_error = 0;
     89 
     90 void m0110_init(void) {
     91     idle();
     92     _delay_ms(1000);
     93 
     94     /* Not needed to initialize in fact.
     95         uint8_t data;
     96         m0110_send(M0110_MODEL);
     97         data = m0110_recv();
     98         print("m0110_init model: "); print_hex8(data); print("\n");
     99 
    100         m0110_send(M0110_TEST);
    101         data = m0110_recv();
    102         print("m0110_init test: "); print_hex8(data); print("\n");
    103     */
    104 }
    105 
    106 uint8_t m0110_send(uint8_t data) {
    107     m0110_error = 0;
    108 
    109     request();
    110     WAIT_MS(clock_lo, 250, 1);  // keyboard may block long time
    111     for (uint8_t bit = 0x80; bit; bit >>= 1) {
    112         WAIT_US(clock_lo, 250, 3);
    113         if (data & bit) {
    114             data_hi();
    115         } else {
    116             data_lo();
    117         }
    118         WAIT_US(clock_hi, 200, 4);
    119     }
    120     _delay_us(100);  // hold last bit for 80us
    121     idle();
    122     return 1;
    123 ERROR:
    124     print("m0110_send err: ");
    125     print_hex8(m0110_error);
    126     print("\n");
    127     _delay_ms(500);
    128     idle();
    129     return 0;
    130 }
    131 
    132 uint8_t m0110_recv(void) {
    133     uint8_t data = 0;
    134     m0110_error  = 0;
    135 
    136     WAIT_MS(clock_lo, 250, 1);  // keyboard may block long time
    137     for (uint8_t i = 0; i < 8; i++) {
    138         data <<= 1;
    139         WAIT_US(clock_lo, 200, 2);
    140         WAIT_US(clock_hi, 200, 3);
    141         if (data_in()) {
    142             data |= 1;
    143         }
    144     }
    145     idle();
    146     return data;
    147 ERROR:
    148     print("m0110_recv err: ");
    149     print_hex8(m0110_error);
    150     print("\n");
    151     _delay_ms(500);
    152     idle();
    153     return 0xFF;
    154 }
    155 
    156 /*
    157 Handling for exceptional case of key combinations for M0110A
    158 
    159 Shift and Calc/Arrow key could be operated simultaneously:
    160 
    161     Case Shift   Arrow   Events          Interpret
    162     -------------------------------------------------------------------
    163     1    Down    Down    71, 79, DD      Calc(d)*a *b
    164     2    Down    Up      71, 79, UU      Arrow&Calc(u)*a
    165     3    Up      Down    F1, 79, DD      Shift(u) *c
    166     4    Up      Up      F1, 79, UU      Shift(u) and Arrow&Calc(u)*a
    167 
    168     Case Shift   Calc    Events          Interpret
    169     -------------------------------------------------------------------
    170     5(1) Down    Down    71, 71, 79, DD  Shift(d) and Cacl(d)
    171     6(2) Down    Up      F1, 71, 79, UU  Shift(u) and Arrow&Calc(u)*a
    172     7(1) Up      Down    F1, 71, 79, DD  Shift(u) and Calc(d)
    173     8(4) Up      Up      F1, F1, 79, UU  Shift(ux2) and Arrow&Calc(u)*a
    174 
    175 During Calc key is hold:
    176     Case Shift   Arrow   Events          Interpret
    177     -------------------------------------------------------------------
    178     A(3) ----    Down    F1, 79, DD      Shift(u) *c
    179     B    ----    Up      79, UU          Arrow&Calc(u)*a
    180     C    Down    ----    F1, 71          Shift(u) and Shift(d)
    181     D    Up      ----    F1              Shift(u)
    182     E    Hold    Down    79, DD          Normal
    183     F    Hold    Up      79, UU          Arrow&Calc(u)*a
    184     G(1) Down    Down    F1, 71, 79, DD  Shift(u)*b and Calc(d)*a
    185     H(2) Down    Up      F1, 71, 79, UU  Shift(u) and Arrow&Calc(u)*a
    186     I(3) Up      Down    F1, F1, 79, DD  Shift(ux2) *c
    187     J(4) Up      Up      F1, 79, UU      Shift(u) and Arrow&Calc(u)*a
    188 
    189     Case Shift   Calc    Events          Interpret
    190     -------------------------------------------------------------------
    191     K(1) ----    Down    71, 79, DD      Calc(d)*a
    192     L(4) ----    Up      F1, 79, UU      Shift(u) and Arrow&Calc(u)*a
    193     M(1) Hold    Down    71, 79, DD      Calc(d)*a
    194     N    Hold    Up      79, UU          Arrow&Calc(u)*a
    195 
    196     Where DD/UU indicates part of Keypad Down/Up event.
    197     *a: Impossible to distinguish btween Arrow and Calc event.
    198     *b: Shift(d) event is ignored.
    199     *c: Arrow/Calc(d) event is ignored.
    200 */
    201 uint8_t m0110_recv_key(void) {
    202     static uint8_t keybuf  = 0x00;
    203     static uint8_t keybuf2 = 0x00;
    204     static uint8_t rawbuf  = 0x00;
    205     uint8_t        raw, raw2, raw3;
    206 
    207     if (keybuf) {
    208         raw    = keybuf;
    209         keybuf = 0x00;
    210         return raw;
    211     }
    212     if (keybuf2) {
    213         raw     = keybuf2;
    214         keybuf2 = 0x00;
    215         return raw;
    216     }
    217 
    218     if (rawbuf) {
    219         raw    = rawbuf;
    220         rawbuf = 0x00;
    221     } else {
    222         raw = instant();  // Use INSTANT for better response. Should be INQUIRY ?
    223     }
    224     switch (KEY(raw)) {
    225         case M0110_KEYPAD:
    226             raw2 = instant();
    227             switch (KEY(raw2)) {
    228                 case M0110_ARROW_UP:
    229                 case M0110_ARROW_DOWN:
    230                 case M0110_ARROW_LEFT:
    231                 case M0110_ARROW_RIGHT:
    232                     if (IS_BREAK(raw2)) {
    233                         // Case B,F,N:
    234                         keybuf = (raw2scan(raw2) | M0110_CALC_OFFSET);  // Calc(u)
    235                         return (raw2scan(raw2) | M0110_KEYPAD_OFFSET);  // Arrow(u)
    236                     }
    237                     break;
    238             }
    239             // Keypad or Arrow
    240             return (raw2scan(raw2) | M0110_KEYPAD_OFFSET);
    241             break;
    242         case M0110_SHIFT:
    243             raw2 = instant();
    244             switch (KEY(raw2)) {
    245                 case M0110_SHIFT:
    246                     // Case: 5-8,C,G,H
    247                     rawbuf = raw2;
    248                     return raw2scan(raw);  // Shift(d/u)
    249                     break;
    250                 case M0110_KEYPAD:
    251                     // Shift + Arrow, Calc, or etc.
    252                     raw3 = instant();
    253                     switch (KEY(raw3)) {
    254                         case M0110_ARROW_UP:
    255                         case M0110_ARROW_DOWN:
    256                         case M0110_ARROW_LEFT:
    257                         case M0110_ARROW_RIGHT:
    258                             if (IS_BREAK(raw)) {
    259                                 if (IS_BREAK(raw3)) {
    260                                     // Case 4:
    261                                     print("(4)\n");
    262                                     keybuf2 = raw2scan(raw);                         // Shift(u)
    263                                     keybuf  = (raw2scan(raw3) | M0110_CALC_OFFSET);  // Calc(u)
    264                                     return (raw2scan(raw3) | M0110_KEYPAD_OFFSET);   // Arrow(u)
    265                                 } else {
    266                                     // Case 3:
    267                                     print("(3)\n");
    268                                     return (raw2scan(raw));  // Shift(u)
    269                                 }
    270                             } else {
    271                                 if (IS_BREAK(raw3)) {
    272                                     // Case 2:
    273                                     print("(2)\n");
    274                                     keybuf = (raw2scan(raw3) | M0110_CALC_OFFSET);  // Calc(u)
    275                                     return (raw2scan(raw3) | M0110_KEYPAD_OFFSET);  // Arrow(u)
    276                                 } else {
    277                                     // Case 1:
    278                                     print("(1)\n");
    279                                     return (raw2scan(raw3) | M0110_CALC_OFFSET);  // Calc(d)
    280                                 }
    281                             }
    282                             break;
    283                         default:
    284                             // Shift + Keypad
    285                             keybuf = (raw2scan(raw3) | M0110_KEYPAD_OFFSET);
    286                             return raw2scan(raw);  // Shift(d/u)
    287                             break;
    288                     }
    289                     break;
    290                 default:
    291                     // Shift + Normal keys
    292                     keybuf = raw2scan(raw2);
    293                     return raw2scan(raw);  // Shift(d/u)
    294                     break;
    295             }
    296             break;
    297         default:
    298             // Normal keys
    299             return raw2scan(raw);
    300             break;
    301     }
    302 }
    303 
    304 static inline uint8_t raw2scan(uint8_t raw) { return (raw == M0110_NULL) ? M0110_NULL : ((raw == M0110_ERROR) ? M0110_ERROR : (((raw & 0x80) | ((raw & 0x7F) >> 1)))); }
    305 
    306 static inline uint8_t inquiry(void) {
    307     m0110_send(M0110_INQUIRY);
    308     return m0110_recv();
    309 }
    310 
    311 static inline uint8_t instant(void) {
    312     m0110_send(M0110_INSTANT);
    313     uint8_t data = m0110_recv();
    314     if (data != M0110_NULL) {
    315         dprintf("%02X ", data);
    316     }
    317     return data;
    318 }
    319 
    320 static inline void clock_lo(void) {
    321     M0110_CLOCK_PORT &= ~(1 << M0110_CLOCK_BIT);
    322     M0110_CLOCK_DDR |= (1 << M0110_CLOCK_BIT);
    323 }
    324 static inline void clock_hi(void) {
    325     /* input with pull up */
    326     M0110_CLOCK_DDR &= ~(1 << M0110_CLOCK_BIT);
    327     M0110_CLOCK_PORT |= (1 << M0110_CLOCK_BIT);
    328 }
    329 static inline bool clock_in(void) {
    330     M0110_CLOCK_DDR &= ~(1 << M0110_CLOCK_BIT);
    331     M0110_CLOCK_PORT |= (1 << M0110_CLOCK_BIT);
    332     _delay_us(1);
    333     return M0110_CLOCK_PIN & (1 << M0110_CLOCK_BIT);
    334 }
    335 static inline void data_lo(void) {
    336     M0110_DATA_PORT &= ~(1 << M0110_DATA_BIT);
    337     M0110_DATA_DDR |= (1 << M0110_DATA_BIT);
    338 }
    339 static inline void data_hi(void) {
    340     /* input with pull up */
    341     M0110_DATA_DDR &= ~(1 << M0110_DATA_BIT);
    342     M0110_DATA_PORT |= (1 << M0110_DATA_BIT);
    343 }
    344 static inline bool data_in(void) {
    345     M0110_DATA_DDR &= ~(1 << M0110_DATA_BIT);
    346     M0110_DATA_PORT |= (1 << M0110_DATA_BIT);
    347     _delay_us(1);
    348     return M0110_DATA_PIN & (1 << M0110_DATA_BIT);
    349 }
    350 
    351 static inline uint16_t wait_clock_lo(uint16_t us) {
    352     while (clock_in() && us) {
    353         asm("");
    354         _delay_us(1);
    355         us--;
    356     }
    357     return us;
    358 }
    359 static inline uint16_t wait_clock_hi(uint16_t us) {
    360     while (!clock_in() && us) {
    361         asm("");
    362         _delay_us(1);
    363         us--;
    364     }
    365     return us;
    366 }
    367 static inline uint16_t wait_data_lo(uint16_t us) {
    368     while (data_in() && us) {
    369         asm("");
    370         _delay_us(1);
    371         us--;
    372     }
    373     return us;
    374 }
    375 static inline uint16_t wait_data_hi(uint16_t us) {
    376     while (!data_in() && us) {
    377         asm("");
    378         _delay_us(1);
    379         us--;
    380     }
    381     return us;
    382 }
    383 
    384 static inline void idle(void) {
    385     clock_hi();
    386     data_hi();
    387 }
    388 
    389 static inline void request(void) {
    390     clock_hi();
    391     data_lo();
    392 }
    393 
    394 /*
    395 Primitive M0110 Library for AVR
    396 ==============================
    397 
    398 
    399 Signaling
    400 ---------
    401 CLOCK is always from KEYBOARD. DATA are sent with MSB first.
    402 
    403 1) IDLE: both lines are high.
    404     CLOCK ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    405     DATA  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    406 
    407 2) KEYBOARD->HOST: HOST reads bit on rising edge.
    408     CLOCK ~~~~~~~~~~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~~~~~~~~~
    409     DATA  ~~~~~~~~~~~~X777777X666666X555555X444444X333333X222222X111111X000000X~~~~~~~
    410                       <--> 160us(clock low)
    411                          <---> 180us(clock high)
    412 
    413 3) HOST->KEYBOARD: HOST asserts bit on falling edge.
    414     CLOCK ~~~~~~~~~~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~~~~~~~~~
    415     DATA  ~~~~~~|_____X777777X666666X555555X444444X333333X222222X111111X000000X~~~~~~~
    416                 <----> 840us(request to send by host)                     <---> 80us(hold DATA)
    417                       <--> 180us(clock low)
    418                          <---> 220us(clock high)
    419 
    420 
    421 Protocol
    422 --------
    423 COMMAND:
    424     Inquiry     0x10    get key event with block
    425     Instant     0x12    get key event
    426     Model       0x14    get model number(M0110 responds with 0x09)
    427                         bit 7   1 if another device connected(used when keypad exists?)
    428                         bit4-6  next device model number
    429                         bit1-3  keyboard model number
    430                         bit 0   always 1
    431     Test        0x16    test(ACK:0x7D/NAK:0x77)
    432 
    433 KEY EVENT:
    434     bit 7       key state(0:press 1:release)
    435     bit 6-1     scan code(see below)
    436     bit 0       always 1
    437     To get scan code use this: ((bits&(1<<7)) | ((bits&0x7F))>>1).
    438 
    439     Note: On the M0110A, Keypad keys and Arrow keys are preceded by 0x79.
    440           Moreover, some Keypad keys(=, /, * and +) are preceded by 0x71 on press and 0xF1 on release.
    441 
    442 ARROW KEYS:
    443     Arrow keys and Calc keys(+,*,/,= on keypad) share same byte sequence and preceding byte of
    444     Calc keys(0x71 and 0xF1) means press and release event of SHIFT. This causes a very confusing situation,
    445     it is difficult or impossible to tell Calc key from Arrow key plus SHIFT in some cases.
    446 
    447     Raw key events:
    448             press               release
    449             ----------------    ----------------
    450     Left:         0x79, 0x0D          0x79, 0x8D
    451     Right:        0x79, 0x05          0x79, 0x85
    452     Up:           0x79, 0x1B          0x79, 0x9B
    453     Down:         0x79, 0x11          0x79, 0x91
    454     Pad+:   0x71, 0x79, 0x0D    0xF1, 0x79, 0x8D
    455     Pad*:   0x71, 0x79, 0x05    0xF1, 0x79, 0x85
    456     Pad/:   0x71, 0x79, 0x1B    0xF1, 0x79, 0x9B
    457     Pad=:   0x71, 0x79, 0x11    0xF1, 0x79, 0x91
    458 
    459 
    460 RAW CODE:
    461     M0110A
    462     ,---------------------------------------------------------. ,---------------.
    463     |  `|  1|  2|  3|  4|  5|  6|  7|  8|  9|  0|  -|  =|Bcksp| |Clr|  =|  /|  *|
    464     |---------------------------------------------------------| |---------------|
    465     |Tab  |  Q|  W|  E|  R|  T|  Y|  U|  I|  O|  P|  [|  ]|   | |  7|  8|  9|  -|
    466     |-----------------------------------------------------'   | |---------------|
    467     |CapsLo|  A|  S|  D|  F|  G|  H|  J|  K|  L|  ;|  '|Return| |  4|  5|  6|  +|
    468     |---------------------------------------------------------| |---------------|
    469     |Shift   |  Z|  X|  C|  V|  B|  N|  M|  ,|  ,|  /|Shft|Up | |  1|  2|  3|   |
    470     |---------------------------------------------------------' |-----------|Ent|
    471     |Optio|Mac    |           Space           |  \|Lft|Rgt|Dn | |      0|  .|   |
    472     `---------------------------------------------------------' `---------------'
    473     ,---------------------------------------------------------. ,---------------.
    474     | 65| 25| 27| 29| 2B| 2F| 2D| 35| 39| 33| 3B| 37| 31|   67| |+0F|*11|*1B|*05|
    475     |---------------------------------------------------------| |---------------|
    476     |   61| 19| 1B| 1D| 1F| 23| 21| 41| 45| 3F| 47| 43| 3D|   | |+33|+37|+39|+1D|
    477     |-----------------------------------------------------'   | |---------------|
    478     |    73| 01| 03| 05| 07| 0B| 09| 4D| 51| 4B| 53| 4F|    49| |+2D|+2F|+31|*0D|
    479     |---------------------------------------------------------| |---------------|
    480     |      71| 0D| 0F| 11| 13| 17| 5B| 5D| 27| 5F| 59|  71|+1B| |+27|+29|+2B|   |
    481     |---------------------------------------------------------' |-----------|+19|
    482     |   75|     6F|            63             | 55|+0D|+05|+11| |    +25|+03|   |
    483     `---------------------------------------------------------' `---------------'
    484     + 0x79, 0xDD / 0xF1, 0xUU
    485     * 0x71, 0x79,DD / 0xF1, 0x79, 0xUU
    486 
    487 
    488 MODEL NUMBER:
    489     M0110:           0x09  00001001 : model number 4 (100)
    490     M0110A:          0x0B  00001011 : model number 5 (101)
    491     M0110 & M0120:   ???
    492 
    493 
    494 Scan Code
    495 ---------
    496     m0110_recv_key() function returns following scan codes instead of M0110 raw codes.
    497     Scan codes are 1 byte size and MSB(bit7) is set when key is released.
    498 
    499         scancode = ((raw&0x80) | ((raw&0x7F)>>1))
    500 
    501     M0110                                                          M0120
    502     ,---------------------------------------------------------.    ,---------------.
    503     |  `|  1|  2|  3|  4|  5|  6|  7|  8|  9|  0|  -|  =|Backs|    |Clr|  -|Lft|Rgt|
    504     |---------------------------------------------------------|    |---------------|
    505     |Tab  |  Q|  W|  E|  R|  T|  Y|  U|  I|  O|  P|  [|  ]|  \|    |  7|  8|  9|Up |
    506     |---------------------------------------------------------|    |---------------|
    507     |CapsLo|  A|  S|  D|  F|  G|  H|  J|  K|  L|  ;|  '|Return|    |  4|  5|  6|Dn |
    508     |---------------------------------------------------------|    |---------------|
    509     |Shift   |  Z|  X|  C|  V|  B|  N|  M|  ,|  ,|  /|        |    |  1|  2|  3|   |
    510     `---------------------------------------------------------'    |-----------|Ent|
    511          |Opt|Mac |         Space               |Enter|Opt|        |      0|  .|   |
    512          `------------------------------------------------'        `---------------'
    513     ,---------------------------------------------------------.    ,---------------.
    514     | 32| 12| 13| 14| 15| 17| 16| 1A| 1C| 19| 1D| 1B| 18|   33|    | 47| 4E| 46| 42|
    515     |---------------------------------------------------------|    |---------------|
    516     |   30| 0C| 0D| 0E| 0F| 10| 11| 20| 22| 1F| 23| 21| 1E| 2A|    | 59| 5B| 5C| 4D|
    517     |---------------------------------------------------------|    |---------------|
    518     |    39| 00| 01| 02| 03| 05| 04| 26| 28| 25| 29| 27|    24|    | 56| 57| 58| 48|
    519     |---------------------------------------------------------|    |---------------|
    520     |      38| 06| 07| 08| 09| 0B| 2D| 2E| 2B| 2F| 2C|      38|    | 53| 54| 55|   |
    521     `---------------------------------------------------------'    |-----------| 4C|
    522          | 3A|  37|             31              |   34| 3A|        |     52| 41|   |
    523          `------------------------------------------------'        `---------------'
    524 
    525     International keyboard(See page 22 of "Technical Info for 128K/512K")
    526     ,---------------------------------------------------------.
    527     | 32| 12| 13| 14| 15| 17| 16| 1A| 1C| 19| 1D| 1B| 18|   33|
    528     |---------------------------------------------------------|
    529     |   30| 0C| 0D| 0E| 0F| 10| 11| 20| 22| 1F| 23| 21| 1E| 2A|
    530     |------------------------------------------------------   |
    531     |    39| 00| 01| 02| 03| 05| 04| 26| 28| 25| 29| 27| 24|  |
    532     |---------------------------------------------------------|
    533     |  38| 06| 07| 08| 09| 0B| 2D| 2E| 2B| 2F| 2C| 0A|      38|
    534     `---------------------------------------------------------'
    535          | 3A|  37|             34              |   31| 3A|
    536          `------------------------------------------------'
    537 
    538     M0110A
    539     ,---------------------------------------------------------. ,---------------.
    540     |  `|  1|  2|  3|  4|  5|  6|  7|  8|  9|  0|  -|  =|Bcksp| |Clr|  =|  /|  *|
    541     |---------------------------------------------------------| |---------------|
    542     |Tab  |  Q|  W|  E|  R|  T|  Y|  U|  I|  O|  P|  [|  ]|   | |  7|  8|  9|  -|
    543     |-----------------------------------------------------'   | |---------------|
    544     |CapsLo|  A|  S|  D|  F|  G|  H|  J|  K|  L|  ;|  '|Return| |  4|  5|  6|  +|
    545     |---------------------------------------------------------| |---------------|
    546     |Shift   |  Z|  X|  C|  V|  B|  N|  M|  ,|  ,|  /|Shft|Up | |  1|  2|  3|   |
    547     |---------------------------------------------------------' |-----------|Ent|
    548     |Optio|Mac    |           Space           |  \|Lft|Rgt|Dn | |      0|  .|   |
    549     `---------------------------------------------------------' `---------------'
    550     ,---------------------------------------------------------. ,---------------.
    551     | 32| 12| 13| 14| 15| 17| 16| 1A| 1C| 19| 1D| 1B| 18|   33| | 47| 68| 6D| 62|
    552     |---------------------------------------------------------| |---------------|
    553     |   30| 0C| 0D| 0E| 0F| 10| 11| 20| 22| 1F| 23| 21| 1E|   | | 59| 5B| 5C| 4E|
    554     |-----------------------------------------------------'   | |---------------|
    555     |    39| 00| 01| 02| 03| 05| 04| 26| 28| 25| 29| 27|    24| | 56| 57| 58| 66|
    556     |---------------------------------------------------------| |---------------|
    557     |      38| 06| 07| 08| 09| 0B| 2D| 2E| 2B| 2F| 2C|  38| 4D| | 53| 54| 55|   |
    558     |---------------------------------------------------------' |-----------| 4C|
    559     |   3A|     37|            31             | 2A| 46| 42| 48| |     52| 41|   |
    560     `---------------------------------------------------------' `---------------'
    561 
    562 
    563 References
    564 ----------
    565 Technical Info for 128K/512K and Plus
    566     ftp://ftp.apple.asimov.net/pub/apple_II/documentation/macintosh/Mac%20Hardware%20Info%20-%20Mac%20128K.pdf
    567     ftp://ftp.apple.asimov.net/pub/apple_II/documentation/macintosh/Mac%20Hardware%20Info%20-%20Mac%20Plus.pdf
    568 Protocol:
    569     Page 20 of Tech Info for 128K/512K
    570     http://www.mac.linux-m68k.org/devel/plushw.php
    571 Connector:
    572     Page 20 of Tech Info for 128K/512K
    573     http://www.kbdbabel.org/conn/kbd_connector_macplus.png
    574 Signaling:
    575     http://www.kbdbabel.org/signaling/kbd_signaling_mac.png
    576     http://typematic.blog.shinobi.jp/Entry/14/
    577 M0110 raw scan codes:
    578     Page 22 of Tech Info for 128K/512K
    579     Page 07 of Tech Info for Plus
    580     http://m0115.web.fc2.com/m0110.jpg
    581     http://m0115.web.fc2.com/m0110a.jpg
    582 */