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