1 /*----------------------------------------------------------------------*\
2 | Module conin to handle key translation |
3 | |
4 | Copyright (c) 2015 by Jon Saxton (Lake Barrine, QLD, Australia) |
5 | |
6 | This file is part of yaze-ag - yet another Z80 emulator by ag. |
7 |----------------------------------------------------------------------|
8 | |
9 | This module implements a state machine which examines keyboard input |
10 | such as that generated by modern PC keyboards with "function keys" |
11 | and other keys labelled with symbols and words suggesting cursor |
12 | movement operations. These keys typically generate multi-byte |
13 | sequences which can be augmented with "shift" keys such as "Ctrl", |
14 | "Alt" and plain "Shift". |
15 | |
16 | These multi-byte sequences are generally not understood by programs |
17 | written for CP/M and it is the job of this module and the others |
18 | which comprise the key translation sub-system to recognise these |
19 | sequences and translate them into sequences which CP/M programs do |
20 | understand. |
21 | |
22 | Note that there are other ways to make use of modern keyboards with- |
23 | out so much code. For example, I have used the following shell |
24 | script to invoke a different emulator which does not have its own |
25 | key translation mechanism: |
26 | |
27 | cd ~/z80pack/cpmsim |
28 | xterm -fa monaco -fs 15 -g 80x40 -xrm \ |
29 | 'xterm*VT100.translations: #override \ |
30 | <Key>Up: string(0x05) \n\ |
31 | <Key>Down: string(0x18) \n\ |
32 | None <Key>Right: string(0x04) \n\ |
33 | None <Key>Left: string(0x13) \n\ |
34 | None <Key>Prior: string(0x12) \n\ |
35 | None <Key>Next: string(0x03) \n\ |
36 | None <Key>Delete: string(0x07)' \ |
37 | -e ./cpm3 |
38 | |
39 | That was certainly better than nothing. The advantages of the key |
40 | translation system built into yaze-ag are at least: |
41 | 1. it is much more comprehensive |
42 | 2. multiple translation tables can be present and any of them |
43 | can be loaded on demand without exiting CP/M |
44 | Some disadvantages are |
45 | 1. it is a lot more complex. |
46 | 2. maintenance demands a basic knowledge of C programming and some |
47 | understanding of how state machines work |
48 | |
49 | Jon Saxton |
50 | Developer of key translation system for yaze-ag |
51 | Lake Barrine, QLD, Australia |
52 \*----------------------------------------------------------------------*/
53
54
55 /*-----------------------------------------------------------------------
56 Yaze-ag is free software; you can redistribute it and/or modify it under
57 the terms of the GNU General Public License as published by the Free
58 Software Foundation; either version 2 of the License, or (at your
59 option) any later version.
60
61 This program is distributed in the hope that it will be useful, but
62 WITHOUT ANY WARRANTY; without even the implied warranty of
63 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
64 General Public License for more details.
65
66 You should have received a copy of the GNU General Public License
67 along with this program; if not, write to the Free Software
68 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
69 ------------------------------------------------------------------------- */
70
71 #include "chan.h"
72 #include "ytypes.h"
73 #include "ktt.h"
74 #include <stdio.h>
75 #include <unistd.h>
76
77 #define SHOW 0
78
utf8_illegal(BYTE b)79 int utf8_illegal(BYTE b)
80 {
81 switch (b)
82 {
83 case 0xC0:
84 case 0xC1:
85 case 0xF5:
86 case 0xFF:
87 return 1;
88 }
89 return 0;
90 }
91
92 extern void bios(int);
93
94 /*----------------------------------------------------------
95 // Things used by the console input routines
96 //---------------------------------------------------------- */
97
98 enum console_input_state
99 {
100 IDLE,
101 ESCAPE,
102 F1234,
103 F1234_SEMI,
104 F1234_SHIFT,
105 CSI,
106 CYGWIN_FK,
107 NUMBER,
108 NUMBER_2,
109 UTF8
110 };
111
112 struct _ci
113 ci = { {0}, 0, 0 };
114
115 extern int serin(int chan);
116 extern int contest();
117
118 #if 0
119 Coded but not used
120 /*------------------------------------------------------------------------
121 // conqueued()
122 //
123 // Returns the number of characters queued for input.
124 //------------------------------------------------------------------------ */
125
126 int conqueued()
127 {
128 return ci.size;
129 }
130 #endif
131
132 /*------------------------------------------------------------------------
133 // conin()
134 //
135 // Console input routine.
136 //
137 // This is implemented as a state machine.
138 //
139 // The idea is to translate cursor control keys as seen by the host
140 // system into something useful for CP/M.
141 //
142 // For most yaze-ag environments the cursor keys generate multi-byte
143 // strings which correspond to output screen control sequences. These
144 // are not understood by CP/M which expects a single keystroke to
145 // yield a single character.
146 //
147 // This function returns an 8-bit unsigned number but bear in mind
148 // that CP/M may limit the range to 7 bits.
149 //
150 //-----------------------------------------------------------------------*/
151
conin()152 BYTE conin()
153 {
154 BYTE ch;
155
156 static WORD fk_map[] =
157 {
158 F1, F2, F3, F4, F5, Ignore, F6,
159 F7, F8, F9, F10, Ignore, F11, F12,
160 F13, F14, Ignore, F15, F16, Ignore, F17, F18, F19,
161 F20, Ignore, Ignore, Ignore, Ignore
162 };
163
164 int
165 fk_no = 0,
166 shift_code = 0,
167 utf8len = 0, /* Initialise just to keep the compiler happy */
168 state = IDLE;
169 ui32
170 key = 0;
171 do
172 {
173 /* If there is something queued up already then deliver it. */
174 if (ci.size > 0)
175 {
176 --ci.size;
177 return ci.queue[ci.index++];
178 }
179 ci.index = ci.size = 0;
180 ch = serin(CHNconin);
181
182 switch (state)
183 {
184 /*------*/
185 case IDLE:
186 /*------*/
187 key = ci.index = ci.size = 0;
188 if (ch == 0x1B && contest())
189 {
190 /* We have an escape quickly followed by something else.
191 // Almost certainly we are seeing a multi-byte sequence. */
192 state = ESCAPE;
193 }
194 else
195 key = ch;
196 if ((ch & 0xC0) == 0xC0)
197 {
198 state = UTF8;
199 if ((ch & 0xE0) == 0xC0)
200 {
201 utf8len = 1;
202 key = ch & 0x1F;
203 }
204 else if ((ch & 0xF0) == 0xE0)
205 {
206 utf8len = 2;
207 key = ch & 0x0F;
208 }
209 else if ((ch & 0xF8) == 0xF0)
210 {
211 utf8len = 3;
212 key = ch & 0x07;
213 }
214 else if ((ch & 0xFC) == 0xF8)
215 {
216 utf8len = 4;
217 key = ch & 0x03;
218 }
219 else if ((ch & 0xFE) == 0xFC)
220 {
221 utf8len = 5;
222 key = ch & 0x01;
223 }
224 else
225 {
226 key = Ignore;
227 state = IDLE;
228 }
229 }
230 break;
231
232 /*-------*/
233 case UTF8:
234 /*-------*/
235 if (utf8_illegal(ch) || (ch & 0xC0) != 0x80)
236 {
237 key = Ignore;
238 state = IDLE;
239 }
240 else
241 {
242 key = (key << 6) | (ch & 0x3F);
243 if (--utf8len == 0)
244 {
245 key |= KT_UNICODE;
246 state = IDLE;
247 }
248 }
249 break;
250
251 /*--------*/
252 case ESCAPE:
253 /*--------*/
254 switch (ch)
255 {
256 case 0x1B: /* Two consecutive ESC */
257 key = KT_ALT;
258 break;
259
260 case '[':
261 /* <esc>[ is probably a CSI but it may be Alt-ESC */
262 if (contest())
263 {
264 state = CSI;
265 fk_no = 0;
266 }
267 else
268 {
269 key = 0x1B | KT_ALT;
270 state = IDLE;
271 }
272 break;
273 case 'O': /* Letter */
274 /* <esc>Ox where x is P, Q, R or S is a common coding for
275 // for F1 through F4. Also when using PuTTY it can be a cursor
276 // movement key such as Ctrl-Up, Ctrl-Down etc. (See below.) */
277 state = F1234;
278 break;
279 default:
280 /* We have previously seen an ESC and the thing
281 // which followed did not complete a CSI. That
282 // implies an Alt chord. */
283 key = ch | KT_ALT;
284 state = IDLE;
285 }
286 break;
287
288 /*-------*/
289 case F1234:
290 /*-------*/
291 /* We may have a function key. F1 to F4 often generate
292 // <esc>Ox where O is an upper case letter (not a zero)
293 // and x is what we just saw. The Home and End keys
294 // generate similar sequences.
295 //
296 // 2015-02-01 jrs on advice from ag ...
297 // When using PuTTY as a terminal emulator, <esc>Ox is generated
298 // by chording an arrow key with Control. In that case, x is
299 // A, B, C or D for Up, Down, Right and Left respectively. */
300 state = IDLE;
301 switch (ch)
302 {
303 case 'H':
304 key |= Home;
305 break;
306 case 'F':
307 key |= End;
308 break;
309 case 'P': /* F1 */
310 case 'Q': /* F2 */
311 case 'R': /* F3 */
312 case 'S': /* F4 */
313 key |= fk_map[ch - 'P'];
314 break;
315 case 'A':
316 key = Up | KT_CONTROL;
317 break;
318 case 'B':
319 key = Down | KT_CONTROL;
320 break;
321 case 'C':
322 key = Right | KT_CONTROL;
323 break;
324 case 'D':
325 key = Left | KT_CONTROL;
326 break;
327 case '1':
328 state = F1234_SEMI;
329 break;
330 default:
331 key = Ignore;
332 }
333 break;
334
335 /*------------*/
336 case F1234_SEMI:
337 /*------------*/
338 /* Expect to see a semicolon */
339 if (ch == ';')
340 state = F1234_SHIFT;
341 else
342 {
343 key = Ignore;
344 state = IDLE;
345 }
346 break;
347 /*-------------*/
348 case F1234_SHIFT:
349 /*-------------*/
350 /* Expect to see a shift code */
351 state = F1234;
352 switch (ch)
353 {
354 case '5':
355 key = KT_CONTROL;
356 break;
357 case '6':
358 key = KT_SHIFT | KT_CONTROL;
359 break;
360 case '4':
361 key = KT_SHIFT | KT_ALT;
362 break;
363 case '2':
364 key |= KT_SHIFT;
365 break;
366 case '3':
367 key |= KT_ALT;
368 break;
369 default:
370 key = Ignore;
371 state = IDLE;
372 }
373 break;
374 /*-----*/
375 case CSI:
376 /*-----*/
377 /* We've seen an ISO 6429 command sequence initiator (CSI)
378 // <esc>[ and what follows should be a cursor movement
379 // control or the start of a function key sequence.
380 // If we get something which we don't recognise then we
381 // discard the entire sequence.
382
383 // Start by assuming we're going to resolve the state
384 // machine */
385 state = IDLE;
386
387 switch (ch)
388 {
389 default:
390 key = Ignore;
391 break;
392 case 'A':
393 key = Up;
394 break;
395 case 'B':
396 key = Down;
397 break;
398 case 'C':
399 key = Right;
400 break;
401 case 'D':
402 key = Left;
403 break;
404 case 'G': /* PuTTY */
405 case 'E': /* 102-key PC keyboard */
406 key = NP5;
407 break;
408 case 'F': /* Cygwin terminal emulator */
409 key = End;
410 break;
411 case 'H': /* Cygwin terminal emulator */
412 key = Home;
413 break;
414 case 'Z': /* PuTTY */
415 key = ReverseTab;
416 break;
417 case '0': /* <esc>[n ... */
418 case '1':
419 case '2':
420 case '3':
421 case '4':
422 case '5':
423 case '6':
424 case '7':
425 case '8':
426 case '9':
427 fk_no = ch - '0';
428 state = NUMBER;
429 break;
430
431 case '[':
432 /* Older versions of Cygwin (using rxvt) presented <esc>[[x
433 // where x is A to E for function keys F1 to F5. These were
434 // special cases. All the other keys followed the <esc>[n~
435 // pattern common to other yaze-ag environments.
436 //
437 // There were logically twenty function keys as far as the
438 // older Cygwin terminal emulator was concerned with F11 to
439 // F20 being generated by using the shift key. F11 and F12
440 // were special cases and generated the same codes as
441 // as shift-F1 and shift-F2 respectively in that F11 gene-
442 // rated the same code as shift-F1.
443 //
444 // Here we just deal with the (unshifted) F1 to F5.
445 // The other cases are handled by the general function
446 // key decoder states. (NUMBER)
447 //
448 // (Note that newer Cygwins use mintty instead of rxvt and
449 // mintty behaves much more like a standard UNIX xterm.) */
450
451 state = CYGWIN_FK;
452 break;
453 }
454 break;
455
456 /*-----------*/
457 case CYGWIN_FK:
458 /*-----------*/
459 switch (ch)
460 {
461 case 'A':
462 case 'B':
463 case 'C':
464 case 'D':
465 case 'E': /* 102-key PC keyboard */
466 key = fk_map[ch - 'A'];
467 break;
468 default:
469 key = Ignore;
470 break;
471 }
472 state = IDLE;
473 break;
474
475 /*--------*/
476 case NUMBER:
477 /*--------*/
478 switch (ch)
479 {
480 case '0':
481 case '1':
482 case '2':
483 case '3':
484 case '4':
485 case '5':
486 case '6':
487 case '7':
488 case '8':
489 case '9':
490 fk_no = fk_no * 10 + ch - '0';
491 break;
492 case ';': /* Number separator */
493 state = NUMBER_2;
494 shift_code = 0;
495 break;
496 case '~': /* End of sequence marker */
497 state = IDLE;
498 switch (fk_no)
499 {
500 case 1:
501 key |= Home;
502 break;
503 case 2:
504 key |= Insert;
505 break;
506 case 3:
507 key |= Delete;
508 break;
509 case 4:
510 key |= End;
511 break;
512 case 5:
513 key |= PageUp;
514 break;
515 case 6:
516 key |= PageDown;
517 break;
518 default:
519 if (10 < fk_no && fk_no < 35)
520 key |= fk_map[fk_no - 11];
521 else
522 key = Ignore;
523 break;
524 }
525 break;
526 default:
527 key = Ignore;
528 state = IDLE;
529 }
530 break;
531
532 /*----------*/
533 case NUMBER_2:
534 /*----------*/
535 /* We have seen esc[n; and we are now looking at a second
536 // number. This usually describes a shift key or a combination
537 // of shift keys.
538 //
539 // fk_no holds the first number. */
540
541 switch (ch)
542 {
543 case '0':
544 case '1':
545 case '2':
546 case '3':
547 case '4':
548 case '5':
549 case '6':
550 case '7':
551 case '8':
552 case '9':
553 shift_code = shift_code * 10 + ch - '0';
554 break;
555 default:
556 /* End of the number sequence. The second number
557 // is always the shift code. What happens after we
558 // deal with that depends on the end-of-sequence
559 // character whe have. */
560 switch (shift_code)
561 {
562 case 5:
563 key |= KT_CONTROL;
564 break;
565 case 6:
566 key |= KT_SHIFT | KT_CONTROL;
567 break;
568 case 4:
569 key |= KT_SHIFT | KT_ALT;
570 break;
571 case 2:
572 key |= KT_SHIFT;
573 break;
574 case 3:
575 key |= KT_ALT;
576 break;
577 default:
578 key = Ignore;
579 break;
580 }
581 switch (ch)
582 {
583 case '~': /* End-of-sequence sentinel */
584 state = IDLE;
585 if (10 < fk_no && fk_no < 35)
586 key |= fk_map[fk_no - 11];
587 else
588 switch (fk_no)
589 {
590 case 2:
591 key |= Insert;
592 break;
593 case 3:
594 key |= Delete;
595 break;
596 case 5:
597 key |= PageUp;
598 break;
599 case 6:
600 key |= PageDown;
601 break;
602 default:
603 key = Ignore;
604 };
605 break;
606 default:
607 switch (fk_no)
608 {
609 case 1:
610 switch (ch)
611 {
612 case 'A':
613 key |= Up;
614 break;
615 case 'B':
616 key |= Down;
617 break;
618 case 'C':
619 key |= Right;
620 break;
621 case 'D':
622 key |= Left;
623 break;
624 case 'F':
625 key |= End;
626 break;
627 case 'H':
628 key |= Home;
629 break;
630 default:
631 key = Ignore;
632 }
633 break;
634 default:
635 key = Ignore;
636 }
637 state = IDLE;
638 break;
639 }
640 }
641 }
642 }
643 while (state != IDLE || key == Ignore || keyTrans(key, &ci));
644
645 /*---------------------------------------------------------------------*\
646 | If we just saw a SysRq keystroke then that usurps everything in the |
647 | queue and we immediately exit to the monitor. This is so we can |
648 | recover from a frozen CP/M session at any time. |
649 \*---------------------------------------------------------------------*/
650
651 if (ci.queue[ci.index] == SysRq)
652 {
653 printf("\r\nSysRq detected\r\n");
654 /* Clear the input buffer so the bios() function doesn't
655 // try to parse it as a command. This means keytest will
656 // report a sysRq as <00> rather than <FF> but that is
657 // a very minor issue indeed. */
658 ci.queue[0] = ci.size = 0;
659 bios(254);
660 }
661
662 /* ci.size should ALWAYS be at least 1 */
663 if (ci.size > 0)
664 {
665 --ci.size;
666 return ci.queue[ci.index++];
667 }
668 return 0; /* Serious error */
669 }
670
671