]> git.donarmstrong.com Git - tmk_firmware.git/blob - protocol/m0110.c
Switch TWI library to use 400kHz
[tmk_firmware.git] / protocol / m0110.c
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
47 static inline uint8_t raw2scan(uint8_t raw);
48 static inline uint8_t inquiry(void);
49 static inline uint8_t instant(void);
50 static inline void clock_lo(void);
51 static inline void clock_hi(void);
52 static inline bool clock_in(void);
53 static inline void data_lo(void);
54 static inline void data_hi(void);
55 static inline bool data_in(void);
56 static inline uint16_t wait_clock_lo(uint16_t us);
57 static inline uint16_t wait_clock_hi(uint16_t us);
58 static inline uint16_t wait_data_lo(uint16_t us);
59 static inline uint16_t wait_data_hi(uint16_t us);
60 static inline void idle(void);
61 static inline void request(void);
62
63
64 #define WAIT_US(stat, us, err) do { \
65     if (!wait_##stat(us)) { \
66         m0110_error = err; \
67         goto ERROR; \
68     } \
69 } while (0)
70
71 #define WAIT_MS(stat, ms, err) 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
89 uint8_t m0110_error = 0;
90
91
92 void m0110_init(void)
93 {
94     uint8_t data;
95     idle();
96     _delay_ms(1000);
97
98     m0110_send(M0110_MODEL);
99     data = m0110_recv();
100     print("m0110_init model: "); phex(data); print("\n");
101
102     m0110_send(M0110_TEST);
103     data = m0110_recv();
104     print("m0110_init test: "); phex(data); print("\n");
105 }
106
107 uint8_t m0110_send(uint8_t data)
108 {
109     m0110_error = 0;
110
111     request();
112     WAIT_MS(clock_lo, 250, 1);  // keyboard may block long time
113     for (uint8_t bit = 0x80; bit; bit >>= 1) {
114         WAIT_US(clock_lo, 250, 3);
115         if (data&bit) {
116             data_hi();
117         } else {
118             data_lo();
119         }
120         WAIT_US(clock_hi, 200, 4);
121     }
122     _delay_us(100); // hold last bit for 80us
123     idle();
124     return 1;
125 ERROR:
126     print("m0110_send err: "); phex(m0110_error); print("\n");
127     _delay_ms(500);
128     idle();
129     return 0;
130 }
131
132 uint8_t m0110_recv(void)
133 {
134     uint8_t data = 0;
135     m0110_error = 0;
136
137     WAIT_MS(clock_lo, 250, 1);  // keyboard may block long time
138     for (uint8_t i = 0; i < 8; i++) {
139         data <<= 1;
140         WAIT_US(clock_lo, 200, 2);
141         WAIT_US(clock_hi, 200, 3);
142         if (data_in()) {
143             data |= 1;
144         }
145     }
146     idle();
147     return data;
148 ERROR:
149     print("m0110_recv err: "); phex(m0110_error); print("\n");
150     _delay_ms(500);
151     idle();
152     return 0xFF;
153 }
154
155 /*
156 Handling for exceptional case of key combinations for M0110A
157
158 Shift and Calc/Arrow key could be operated simultaneously:
159
160     Case Shift   Arrow   Events          Interpret
161     -------------------------------------------------------------------
162     1    Down    Down    71, 79, DD      Calc(d)*a *b
163     2    Down    Up      71, 79, UU      Arrow&Calc(u)*a
164     3    Up      Down    F1, 79, DD      Shift(u) *c
165     4    Up      Up      F1, 79, UU      Shift(u) and Arrow&Calc(u)*a
166
167     Case Shift   Calc    Events          Interpret
168     -------------------------------------------------------------------
169     5(1) Down    Down    71, 71, 79, DD  Shift(d) and Cacl(d)
170     6(2) Down    Up      F1, 71, 79, UU  Shift(u) and Arrow&Calc(u)*a
171     7(1) Up      Down    F1, 71, 79, DD  Shift(u) and Calc(d)
172     8(4) Up      Up      F1, F1, 79, UU  Shift(ux2) and Arrow&Calc(u)*a
173
174 During Calc key is hold:
175     Case Shift   Arrow   Events          Interpret
176     -------------------------------------------------------------------
177     A(3) ----    Down    F1, 79, DD      Shift(u) *c
178     B    ----    Up      79, UU          Arrow&Calc(u)*a
179     C    Down    ----    F1, 71          Shift(u) and Shift(d)
180     D    Up      ----    F1              Shift(u)
181     E    Hold    Down    79, DD          Normal
182     F    Hold    Up      79, UU          Arrow&Calc(u)*a
183     G(1) Down    Down    F1, 71, 79, DD  Shift(u)*b and Calc(d)*a
184     H(2) Down    Up      F1, 71, 79, UU  Shift(u) and Arrow&Calc(u)*a
185     I(3) Up      Down    F1, F1, 79, DD  Shift(ux2) *c
186     J(4) Up      Up      F1, 79, UU      Shift(u) and Arrow&Calc(u)*a
187
188     Case Shift   Calc    Events          Interpret
189     -------------------------------------------------------------------
190     K(1) ----    Down    71, 79, DD      Calc(d)*a
191     L(4) ----    Up      F1, 79, UU      Shift(u) and Arrow&Calc(u)*a
192     M(1) Hold    Down    71, 79, DD      Calc(d)*a
193     N    Hold    Up      79, UU          Arrow&Calc(u)*a
194
195     Where DD/UU indicates part of Keypad Down/Up event.
196     *a: Impossible to distinguish btween Arrow and Calc event.
197     *b: Shift(d) event is ignored.
198     *c: Arrow/Calc(d) event is ignored.
199 */
200 uint8_t m0110_recv_key(void)
201 {
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
305 static inline uint8_t raw2scan(uint8_t raw) {
306     return (raw == M0110_NULL) ?  M0110_NULL : (
307                 (raw == M0110_ERROR) ?  M0110_ERROR : (
308                     ((raw&0x80) | ((raw&0x7F)>>1))
309                 )
310            );
311 }
312
313 static inline uint8_t inquiry(void)
314 {
315     m0110_send(M0110_INQUIRY);
316     return m0110_recv();
317 }
318
319 static inline uint8_t instant(void)
320 {
321     m0110_send(M0110_INSTANT);
322     uint8_t data = m0110_recv();
323     if (data != M0110_NULL) {
324         debug_hex(data); debug(" ");
325     }
326     return data;
327 }
328
329 static inline void clock_lo()
330 {
331     M0110_CLOCK_PORT &= ~(1<<M0110_CLOCK_BIT);
332     M0110_CLOCK_DDR  |=  (1<<M0110_CLOCK_BIT);
333 }
334 static inline void clock_hi()
335 {
336     /* input with pull up */
337     M0110_CLOCK_DDR  &= ~(1<<M0110_CLOCK_BIT);
338     M0110_CLOCK_PORT |=  (1<<M0110_CLOCK_BIT);
339 }
340 static inline bool clock_in()
341 {
342     M0110_CLOCK_DDR  &= ~(1<<M0110_CLOCK_BIT);
343     M0110_CLOCK_PORT |=  (1<<M0110_CLOCK_BIT);
344     _delay_us(1);
345     return M0110_CLOCK_PIN&(1<<M0110_CLOCK_BIT);
346 }
347 static inline void data_lo()
348 {
349     M0110_DATA_PORT &= ~(1<<M0110_DATA_BIT);
350     M0110_DATA_DDR  |=  (1<<M0110_DATA_BIT);
351 }
352 static inline void data_hi()
353 {
354     /* input with pull up */
355     M0110_DATA_DDR  &= ~(1<<M0110_DATA_BIT);
356     M0110_DATA_PORT |=  (1<<M0110_DATA_BIT);
357 }
358 static inline bool data_in()
359 {
360     M0110_DATA_DDR  &= ~(1<<M0110_DATA_BIT);
361     M0110_DATA_PORT |=  (1<<M0110_DATA_BIT);
362     _delay_us(1);
363     return M0110_DATA_PIN&(1<<M0110_DATA_BIT);
364 }
365
366 static inline uint16_t wait_clock_lo(uint16_t us)
367 {
368     while (clock_in()  && us) { asm(""); _delay_us(1); us--; }
369     return us;
370 }
371 static inline uint16_t wait_clock_hi(uint16_t us)
372 {
373     while (!clock_in() && us) { asm(""); _delay_us(1); us--; }
374     return us;
375 }
376 static inline uint16_t wait_data_lo(uint16_t us)
377 {
378     while (data_in() && us)  { asm(""); _delay_us(1); us--; }
379     return us;
380 }
381 static inline uint16_t wait_data_hi(uint16_t us)
382 {
383     while (!data_in() && us)  { asm(""); _delay_us(1); us--; }
384     return us;
385 }
386
387 static inline void idle(void)
388 {
389     clock_hi();
390     data_hi();
391 }
392
393 static inline void request(void)
394 {
395     clock_hi();
396     data_lo();
397 }
398
399
400
401 /*
402 Primitive M0110 Library for AVR
403 ==============================
404
405
406 Signaling
407 ---------
408 CLOCK is always from KEYBOARD. DATA are sent with MSB first.
409
410 1) IDLE: both lines are high.
411     CLOCK ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
412     DATA  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
413
414 2) KEYBOARD->HOST: HOST reads bit on rising edge.
415     CLOCK ~~~~~~~~~~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~~~~~~~~~
416     DATA  ~~~~~~~~~~~~X777777X666666X555555X444444X333333X222222X111111X000000X~~~~~~~
417                       <--> 160us(clock low)
418                          <---> 180us(clock high)
419
420 3) HOST->KEYBOARD: HOST asserts bit on falling edge.
421     CLOCK ~~~~~~~~~~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~~~~~~~~~
422     DATA  ~~~~~~|_____X777777X666666X555555X444444X333333X222222X111111X000000X~~~~~~~
423                 <----> 840us(request to send by host)                     <---> 80us(hold DATA)
424                       <--> 180us(clock low)
425                          <---> 220us(clock high)
426
427
428 Protocol
429 --------
430 COMMAND:
431     Inquiry     0x10    get key event with block
432     Instant     0x12    get key event
433     Model       0x14    get model number(M0110 responds with 0x09)
434                         bit 7   1 if another device connected(used when keypad exists?)
435                         bit4-6  next device model number
436                         bit1-3  keyboard model number
437                         bit 0   always 1
438     Test        0x16    test(ACK:0x7D/NAK:0x77)
439
440 KEY EVENT:
441     bit 7       key state(0:press 1:release)
442     bit 6-1     scan code(see below)
443     bit 0       always 1
444     To get scan code use this: ((bits&(1<<7)) | ((bits&0x7F))>>1).
445
446     Note: On the M0110A, Keypad keys and Arrow keys are preceded by 0x79.
447           Moreover, some Keypad keys(=, /, * and +) are preceded by 0x71 on press and 0xF1 on release.
448
449 ARROW KEYS:
450     Arrow keys and Calc keys(+,*,/,= on keypad) share same byte sequence and preceding byte of
451     Calc keys(0x71 and 0xF1) means press and release event of SHIFT. This causes a very confusing situation,
452     it is difficult or impossible to tell Calc key from Arrow key plus SHIFT in some cases.
453
454     Raw key events:
455             press               release
456             ----------------    ----------------
457     Left:         0x79, 0x0D          0x79, 0x8D
458     Right:        0x79, 0x05          0x79, 0x85
459     Up:           0x79, 0x1B          0x79, 0x9B
460     Down:         0x79, 0x11          0x79, 0x91
461     Pad+:   0x71, 0x79, 0x0D    0xF1, 0x79, 0x8D
462     Pad*:   0x71, 0x79, 0x05    0xF1, 0x79, 0x85
463     Pad/:   0x71, 0x79, 0x1B    0xF1, 0x79, 0x9B
464     Pad=:   0x71, 0x79, 0x11    0xF1, 0x79, 0x91
465
466
467 RAW CODE:
468     M0110A
469     ,---------------------------------------------------------. ,---------------.
470     |  `|  1|  2|  3|  4|  5|  6|  7|  8|  9|  0|  -|  =|Bcksp| |Clr|  =|  /|  *|
471     |---------------------------------------------------------| |---------------|
472     |Tab  |  Q|  W|  E|  R|  T|  Y|  U|  I|  O|  P|  [|  ]|   | |  7|  8|  9|  -|
473     |-----------------------------------------------------'   | |---------------|
474     |CapsLo|  A|  S|  D|  F|  G|  H|  J|  K|  L|  ;|  '|Return| |  4|  5|  6|  +|
475     |---------------------------------------------------------| |---------------|
476     |Shift   |  Z|  X|  C|  V|  B|  N|  M|  ,|  ,|  /|Shft|Up | |  1|  2|  3|   |
477     |---------------------------------------------------------' |-----------|Ent|
478     |Optio|Mac    |           Space           |  \|Lft|Rgt|Dn | |      0|  .|   |
479     `---------------------------------------------------------' `---------------'
480     ,---------------------------------------------------------. ,---------------.
481     | 65| 25| 27| 29| 2B| 2F| 2D| 35| 39| 33| 3B| 37| 31|   67| |+0F|*11|*1B|*05|
482     |---------------------------------------------------------| |---------------|
483     |   61| 19| 1B| 1D| 1F| 23| 21| 41| 45| 3F| 47| 43| 3D|   | |+33|+37|+39|+1D|
484     |-----------------------------------------------------'   | |---------------|
485     |    73| 01| 03| 05| 07| 0B| 09| 4D| 51| 4B| 53| 4F|    49| |+2D|+2F|+31|*0D|
486     |---------------------------------------------------------| |---------------|
487     |      71| 0D| 0F| 11| 13| 17| 5B| 5D| 27| 5F| 59|  71|+1B| |+27|+29|+2B|   |
488     |---------------------------------------------------------' |-----------|+19|
489     |   75|     6F|            63             | 55|+0D|+05|+11| |    +25|+03|   |
490     `---------------------------------------------------------' `---------------'
491     + 0x79, 0xDD / 0xF1, 0xUU
492     * 0x71, 0x79,DD / 0xF1, 0x79, 0xUU
493
494
495 MODEL NUMBER:
496     M0110:           0x09  00001001 : model number 4 (100)
497     M0110A:          0x0B  00001011 : model number 5 (101)
498     M0110 & M0120:   ???
499
500
501 Scan Code
502 ---------
503     m0110_recv_key() function returns following scan codes instead of raw key events.
504     Scan codes are 1 byte long and MSB(bit7) is set when key is released. 
505
506     M0110
507     ,---------------------------------------------------------.
508     |  `|  1|  2|  3|  4|  5|  6|  7|  8|  9|  0|  -|  =|Backs|
509     |---------------------------------------------------------|
510     |Tab  |  Q|  W|  E|  R|  T|  Y|  U|  I|  O|  P|  [|  ]|  \|
511     |---------------------------------------------------------|
512     |CapsLo|  A|  S|  D|  F|  G|  H|  J|  K|  L|  ;|  '|Return|
513     |---------------------------------------------------------|
514     |Shift   |  Z|  X|  C|  V|  B|  N|  M|  ,|  ,|  /|        |
515     `---------------------------------------------------------'
516          |Opt|Mac |         Space               |Enter|Opt|
517          `------------------------------------------------'
518     ,---------------------------------------------------------.
519     | 32| 12| 13| 14| 15| 17| 16| 1A| 1C| 19| 1D| 1B| 18|   33|
520     |---------------------------------------------------------|
521     |   30| 0C| 0D| 0E| 0F| 10| 11| 20| 22| 1F| 23| 21| 1E| 2A|
522     |---------------------------------------------------------|
523     |    39| 00| 01| 02| 03| 05| 04| 26| 28| 25| 29| 27|    24|
524     |---------------------------------------------------------|
525     |      38| 06| 07| 08| 09| 0B| 2D| 2E| 2B| 2F| 2C|      38|
526     `---------------------------------------------------------'
527          | 3A|  37|             31              |   34| 3A|
528          `------------------------------------------------'
529
530     M0110A
531     ,---------------------------------------------------------. ,---------------.
532     |  `|  1|  2|  3|  4|  5|  6|  7|  8|  9|  0|  -|  =|Bcksp| |Clr|  =|  /|  *|
533     |---------------------------------------------------------| |---------------|
534     |Tab  |  Q|  W|  E|  R|  T|  Y|  U|  I|  O|  P|  [|  ]|   | |  7|  8|  9|  -|
535     |-----------------------------------------------------'   | |---------------|
536     |CapsLo|  A|  S|  D|  F|  G|  H|  J|  K|  L|  ;|  '|Return| |  4|  5|  6|  +|
537     |---------------------------------------------------------| |---------------|
538     |Shift   |  Z|  X|  C|  V|  B|  N|  M|  ,|  ,|  /|Shft|Up | |  1|  2|  3|   |
539     |---------------------------------------------------------' |-----------|Ent|
540     |Optio|Mac    |           Space           |  \|Lft|Rgt|Dn | |      0|  .|   |
541     `---------------------------------------------------------' `---------------'
542     ,---------------------------------------------------------. ,---------------.
543     | 32| 12| 13| 14| 15| 17| 16| 1A| 1C| 19| 1D| 1B| 18|   33| | 47| 68| 6D| 62|
544     |---------------------------------------------------------| |---------------|
545     |   30| 0C| 0D| 0E| 0F| 10| 11| 20| 22| 1F| 23| 21| 1E|   | | 59| 5B| 5C| 4E|
546     |-----------------------------------------------------'   | |---------------|
547     |    39| 00| 01| 02| 03| 05| 04| 26| 28| 25| 29| 27|    24| | 56| 57| 58| 66|
548     |---------------------------------------------------------| |---------------|
549     |      38| 06| 07| 08| 09| 0B| 2D| 2E| 2B| 2F| 2C|  38| 4D| | 53| 54| 55|   |
550     |---------------------------------------------------------' |-----------| 4C|
551     |   3A|     37|            31             | 2A| 46| 42| 48| |     52| 41|   |
552     `---------------------------------------------------------' `---------------'
553
554
555 References
556 ----------
557 Technical Info for 128K/512K and Plus
558     ftp://ftp.apple.asimov.net/pub/apple_II/documentation/macintosh/Mac%20Hardware%20Info%20-%20Mac%20128K.pdf
559     ftp://ftp.apple.asimov.net/pub/apple_II/documentation/macintosh/Mac%20Hardware%20Info%20-%20Mac%20Plus.pdf
560 Protocol:
561     Page 20 of Tech Info for 128K/512K
562     http://www.mac.linux-m68k.org/devel/plushw.php
563 Connector:
564     Page 20 of Tech Info for 128K/512K
565     http://www.kbdbabel.org/conn/kbd_connector_macplus.png
566 Signaling:
567     http://www.kbdbabel.org/signaling/kbd_signaling_mac.png
568     http://typematic.blog.shinobi.jp/Entry/14/
569 Scan Codes:
570     Page 22 of Tech Info for 128K/512K
571     Page 07 of Tech Info for Plus
572     http://m0115.web.fc2.com/m0110.jpg
573     http://m0115.web.fc2.com/m0110a.jpg
574 */