xref: /titanic_53/usr/src/boot/sys/boot/i386/libi386/vidconsole.c (revision 4a5d661a82b942b6538acd26209d959ce98b593a)
1 /*-
2  * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
3  * Copyright (c) 1997 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp)
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * 	Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp
28  */
29 
30 #include <sys/cdefs.h>
31 
32 #include <stand.h>
33 #include <bootstrap.h>
34 #include <btxv86.h>
35 #include <machine/psl.h>
36 #include "libi386.h"
37 
38 #if KEYBOARD_PROBE
39 #include <machine/cpufunc.h>
40 
41 static int	probe_keyboard(void);
42 #endif
43 static void	vidc_probe(struct console *cp);
44 static int	vidc_init(struct console *cp, int arg);
45 static void	vidc_putchar(struct console *cp, int c);
46 static int	vidc_getchar(struct console *cp);
47 static int	vidc_ischar(struct console *cp);
48 
49 static int	vidc_started;
50 
51 #ifdef TERM_EMU
52 #define MAXARGS		8
53 #define	KEYBUFSZ	10
54 #define DEFAULT_FGCOLOR	7
55 #define DEFAULT_BGCOLOR	0
56 
57 void		end_term(void);
58 void		bail_out(int c);
59 void		vidc_term_emu(int c);
60 void		get_pos(int *x, int *y);
61 void		curs_move(int *_x, int *_y, int x, int y);
62 void		write_char(int c, int fg, int bg);
63 void		scroll_up(int rows, int fg, int bg);
64 void		CD(void);
65 void		CM(void);
66 void		HO(void);
67 
68 static unsigned	keybuf[KEYBUFSZ];	/* keybuf for extended codes */
69 static int	args[MAXARGS], argc;
70 static int	fg_c, bg_c, curx, cury;
71 static int	esc;
72 #endif
73 
74 
75 struct console text = {
76     "text",
77     "internal video/keyboard",
78     0,
79     vidc_probe,
80     vidc_init,
81     vidc_putchar,
82     vidc_getchar,
83     vidc_ischar,
84     NULL
85 };
86 
87 static void
vidc_probe(struct console * cp)88 vidc_probe(struct console *cp)
89 {
90 
91     /* look for a keyboard */
92 #if KEYBOARD_PROBE
93     if (probe_keyboard())
94 #endif
95     {
96 
97 	cp->c_flags |= C_PRESENTIN;
98     }
99 
100     /* XXX for now, always assume we can do BIOS screen output */
101     cp->c_flags |= C_PRESENTOUT;
102 }
103 
104 static int
vidc_init(struct console * cp,int arg)105 vidc_init(struct console *cp, int arg)
106 {
107     int		i;
108 
109     if (vidc_started && arg == 0)
110 	return (0);
111     vidc_started = 1;
112 #ifdef TERM_EMU
113     /* Init terminal emulator */
114     end_term();
115     get_pos(&curx, &cury);
116     curs_move(&curx, &cury, curx, cury);
117     fg_c = DEFAULT_FGCOLOR;
118     bg_c = DEFAULT_BGCOLOR;
119     memset(keybuf, 0, KEYBUFSZ);
120 #endif
121     for (i = 0; i < 10 && vidc_ischar(cp); i++)
122 	(void)vidc_getchar(cp);
123     return (0);	/* XXX reinit? */
124 }
125 
126 void
vidc_biosputchar(int c)127 vidc_biosputchar(int c)
128 {
129 
130     v86.ctl = 0;
131     v86.addr = 0x10;
132     v86.eax = 0xe00 | (c & 0xff);
133     v86.ebx = 0x7;
134     v86int();
135 }
136 
137 static void
vidc_rawputchar(int c)138 vidc_rawputchar(int c)
139 {
140     int		i;
141 
142     if (c == '\t')
143 	/* lame tab expansion */
144 	for (i = 0; i < 8; i++)
145 	    vidc_rawputchar(' ');
146     else {
147 #ifndef TERM_EMU
148         vidc_biosputchar(c);
149 #else
150 	/* Emulate AH=0eh (teletype output) */
151 	switch(c) {
152 	case '\a':
153 	    vidc_biosputchar(c);
154 	    return;
155 	case '\r':
156 	    curx = 0;
157 	    break;
158 	case '\n':
159 	    cury++;
160 	    if ((text.c_flags & C_MODERAW) == 0)
161 		curx = 0;
162 
163 	    if (cury > 24) {
164 		scroll_up(1, fg_c, bg_c);
165 		cury--;
166 	    }
167 	    break;
168 	case '\b':
169 	    if (curx > 0)
170 		curx--;
171 	    break;
172 	default:
173 		if (curx > 79) {
174 			curx = 0;
175 			cury++;
176 			curs_move(&curx, &cury, curx, cury);
177 		}
178 		if ((text.c_flags & C_MODERAW) == 0) {
179 			if (cury > 24) {
180 				curx = 0;
181 				scroll_up(1, fg_c, bg_c);
182 				cury--;
183 				curs_move(&curx, &cury, curx, cury);
184 			}
185 		}
186 		write_char(c, fg_c, bg_c);
187 		curx++;
188 		if (text.c_flags & C_MODERAW) {
189 			if (curx > 79) {
190 				curx = 0;
191 				if (cury == 25)
192 					scroll_up(1, fg_c, bg_c);
193 				else
194 					cury++;
195 			}
196 		}
197 	}
198 	curs_move(&curx, &cury, curx, cury);
199 #endif
200     }
201 }
202 
203 #ifdef TERM_EMU
204 
205 /* Get cursor position on the screen. Result is in edx. Sets
206  * curx and cury appropriately.
207  */
208 void
get_pos(int * x,int * y)209 get_pos(int *x, int *y)
210 {
211 
212     v86.ctl = 0;
213     v86.addr = 0x10;
214     v86.eax = 0x0300;
215     v86.ebx = 0x0;
216     v86int();
217     *x = v86.edx & 0x00ff;
218     *y = (v86.edx & 0xff00) >> 8;
219 }
220 
221 /* Move cursor to x rows and y cols (0-based). */
222 void
curs_move(int * _x,int * _y,int x,int y)223 curs_move(int *_x, int *_y, int x, int y)
224 {
225 
226     v86.ctl = 0;
227     v86.addr = 0x10;
228     v86.eax = 0x0200;
229     v86.ebx = 0x0;
230     v86.edx = ((0x00ff & y) << 8) + (0x00ff & x);
231     v86int();
232     if (_x != NULL)
233 	*_x = x;
234     if (_y != NULL)
235 	*_y = y;
236     /* If there is ctrl char at this position, cursor would be invisible.
237      * Make it a space instead.
238      */
239     v86.ctl = 0;
240     v86.addr = 0x10;
241     v86.eax = 0x0800;
242     v86.ebx = 0x0;
243     v86int();
244 #define isvisible(c)	(((c) >= 32) && ((c) < 255))
245     if (!isvisible(v86.eax & 0x00ff)) {
246 	write_char(' ', fg_c, bg_c);
247     }
248     v86.ctl = 0;	/* show normal underline cursor */
249     v86.addr = 0x10;
250     v86.eax = 0x0100;
251     v86.ecx = 0x0607;
252     v86int();
253 }
254 
255 /* Scroll up the whole window by a number of rows. If rows==0,
256  * clear the window. fg and bg are attributes for the new lines
257  * inserted in the window.
258  */
259 void
scroll_up(int rows,int fgcol,int bgcol)260 scroll_up(int rows, int fgcol, int bgcol)
261 {
262     if (rows == 0)
263 	rows = 25;
264     v86.ctl = 0;
265     v86.addr = 0x10;
266     v86.eax = 0x0600 + (0x00ff & rows);
267     v86.ebx = (bgcol << 12) + (fgcol << 8);
268     v86.ecx = 0x0;
269     v86.edx = 0x184f;
270     v86int();
271 }
272 
273 /* Write character and attribute at cursor position. */
274 void
write_char(int c,int fgcol,int bgcol)275 write_char(int c, int fgcol, int bgcol)
276 {
277 
278     v86.ctl = 0;
279     v86.addr = 0x10;
280     v86.eax = 0x0900 + (0x00ff & c);
281     v86.ebx = (bgcol << 4) + fgcol;
282     v86.ecx = 0x1;
283     v86int();
284 }
285 
286 /**************************************************************/
287 /*
288  * Screen manipulation functions. They use accumulated data in
289  * args[] and argc variables.
290  *
291  */
292 
293 /* Clear line from current position to end of line */
294 void
CL(int direction)295 CL(int direction)
296 {
297     v86.ctl = 0;
298     v86.addr = 0x10;
299     v86.eax = 0x0600;
300     v86.ebx = (bg_c << 12) + (fg_c << 8);
301     if (direction == 0) {	/* from cursor to end */
302 	v86.ecx = (cury << 8) + curx;
303 	v86.edx = (cury << 8) + 79;
304     } else if (direction == 1) { /* from beginning to cursor */
305 	v86.ecx = (cury << 8) + 0;
306 	v86.edx = (cury << 8) + curx;
307     } else if (direction == 2) { /* entire line */
308 	v86.ecx = (cury << 8) + 0;
309 	v86.edx = (cury << 8) + 79;
310     }
311     v86int();
312     end_term();
313     return;
314 }
315 
316 /* Clear display from current position to end of screen */
317 void
CD(void)318 CD(void)
319 {
320 
321     get_pos(&curx, &cury);
322     if (curx > 0) {
323 	v86.ctl = 0;
324 	v86.addr = 0x10;
325 	v86.eax = 0x0600;
326 	v86.ebx = (bg_c << 12) + (fg_c << 8);
327 	v86.ecx = (cury << 8) + curx;
328 	v86.edx = (cury << 8) + 79;
329 	v86int();
330 	if (++cury > 24) {
331 	    end_term();
332 	    return;
333 	}
334     }
335     v86.ctl = 0;
336     v86.addr = 0x10;
337     v86.eax = 0x0600;
338     v86.ebx = (bg_c << 12) + (fg_c << 8);
339     v86.ecx = (cury << 8) + 0;
340     v86.edx = (24 << 8) + 79;
341     v86int();
342     end_term();
343 }
344 
345 /* Absolute cursor move to args[0] rows and args[1] columns
346  * (the coordinates are 1-based).
347  */
348 void
CM(void)349 CM(void)
350 {
351 
352     if (args[0] > 0)
353 	args[0]--;
354     if (args[1] > 0)
355 	args[1]--;
356     curs_move(&curx, &cury, args[1], args[0]);
357     end_term();
358 }
359 
360 /* Home cursor (left top corner) */
361 void
HO(void)362 HO(void)
363 {
364 
365     argc = 1;
366     args[0] = args[1] = 1;
367     CM();
368 }
369 
370 /* Clear internal state of the terminal emulation code */
371 void
end_term(void)372 end_term(void)
373 {
374 
375     esc = 0;
376     argc = -1;
377 }
378 
379 /* Gracefully exit ESC-sequence processing in case of misunderstanding */
380 void
bail_out(int c)381 bail_out(int c)
382 {
383     char buf[16], *ch;
384     int i;
385 
386     if (esc) {
387 	vidc_rawputchar('\033');
388 	if (esc != '\033')
389 	    vidc_rawputchar(esc);
390 	for (i = 0; i <= argc; ++i) {
391 	    sprintf(buf, "%d", args[i]);
392 	    ch = buf;
393 	    while (*ch)
394 		vidc_rawputchar(*ch++);
395 	}
396     }
397     vidc_rawputchar(c);
398     end_term();
399 }
400 
401 static void
get_arg(int c)402 get_arg(int c)
403 {
404 
405     if (argc < 0)
406 	argc = 0;
407     args[argc] *= 10;
408     args[argc] += c - '0';
409 }
410 
411 /* Emulate basic capabilities of sun-color terminal */
412 void
vidc_term_emu(int c)413 vidc_term_emu(int c)
414 {
415     static int ansi_col[] = {
416 	0, 4, 2, 6, 1, 5, 3, 7,
417     };
418     int t;
419     int i;
420 
421     switch (esc) {
422     case 0:
423 	switch (c) {
424 	case '\033':
425 	    esc = c;
426 	    break;
427 	default:
428 	    vidc_rawputchar(c);
429 	    break;
430 	}
431 	break;
432 
433     case '\033':
434 	switch (c) {
435 	case '[':
436 	    esc = c;
437 	    args[0] = 0;
438 	    argc = -1;
439 	    break;
440 	default:
441 	    bail_out(c);
442 	    break;
443 	}
444 	break;
445 
446     case '[':
447 	switch (c) {
448 	case ';':
449 	    if (argc < 0)	/* XXX */
450 		argc = 0;
451 	    else if (argc + 1 >= MAXARGS)
452 		bail_out(c);
453 	    else
454 		args[++argc] = 0;
455 	    break;
456 	case 'A':		/* UP = \E[%dA */
457 	    if (argc == 0) {
458 		int x, y;
459 		get_pos(&x, &y);
460 		args[1] = x + 1;
461 		args[0] = y - args[0] + 1;
462 		CM();
463 	    } else
464 		bail_out(c);
465 	    break;
466 	case 'B':		/* DO = \E[%dB */
467 	    if (argc == 0) {
468 		int x, y;
469 		get_pos(&x, &y);
470 		args[1] = x + 1;
471 		args[0] = y + args[0] + 1;
472 		CM();
473 	    } else
474 		bail_out(c);
475 	    break;
476 	case 'C':		/* RI = \E[%dC */
477 	    if (argc == 0) {
478 		int x, y;
479 		get_pos(&x, &y);
480 		args[1] = args[0] + 1;
481 		args[0] = y + 1;
482 		CM();
483 	    } else
484 		bail_out(c);
485 	    break;
486 	case 'H':		/* ho = \E[H */
487 	    if (argc < 0)
488 		HO();
489 	    else if (argc == 1)
490 		CM();
491 	    else
492 		bail_out(c);
493 	    break;
494 	case 'J':		/* cd = \E[J */
495 	    if (argc < 0)
496 		CD();
497 	    else
498 		bail_out(c);
499 	    break;
500 	case 'K':
501 	    if (argc < 0)
502 		CL(0);
503 	    else if (argc == 0)
504 		switch (args[0]) {
505 		case 0:
506 		case 1:
507 		case 2:
508 			CL(args[0]);
509 			break;
510 		default:
511 			bail_out(c);
512 		}
513 	    else
514 		bail_out(c);
515 	    break;
516 	case 'm':
517 	    if (argc < 0) {
518 		fg_c = DEFAULT_FGCOLOR;
519 		bg_c = DEFAULT_BGCOLOR;
520 	    }
521 	    for (i = 0; i <= argc; ++i) {
522 		switch (args[i]) {
523 		case 0:		/* back to normal */
524 		    fg_c = DEFAULT_FGCOLOR;
525 		    bg_c = DEFAULT_BGCOLOR;
526 		    break;
527 		case 1:		/* bold */
528 		    fg_c |= 0x8;
529 		    break;
530 		case 4:		/* underline */
531 		case 5:		/* blink */
532 		    bg_c |= 0x8;
533 		    break;
534 		case 7:		/* reverse */
535 		    t = fg_c;
536 		    fg_c = bg_c;
537 		    bg_c = t;
538 		    break;
539 		case 30: case 31: case 32: case 33:
540 		case 34: case 35: case 36: case 37:
541 		    fg_c = ansi_col[args[i] - 30];
542 		    break;
543 		case 39:	/* normal */
544 		    fg_c = DEFAULT_FGCOLOR;
545 		    break;
546 		case 40: case 41: case 42: case 43:
547 		case 44: case 45: case 46: case 47:
548 		    bg_c = ansi_col[args[i] - 40];
549 		    break;
550 		case 49:	/* normal */
551 		    bg_c = DEFAULT_BGCOLOR;
552 		    break;
553 		}
554 	    }
555 	    end_term();
556 	    break;
557 	default:
558 	    if (isdigit(c))
559 		get_arg(c);
560 	    else
561 		bail_out(c);
562 	    break;
563 	}
564 	break;
565 
566     default:
567 	bail_out(c);
568 	break;
569     }
570 }
571 #endif
572 
573 static void
vidc_putchar(struct console * cp,int c)574 vidc_putchar(struct console *cp, int c)
575 {
576 #ifdef TERM_EMU
577     vidc_term_emu(c);
578 #else
579     vidc_rawputchar(c);
580 #endif
581 }
582 
583 static int
vidc_getchar(struct console * cp)584 vidc_getchar(struct console *cp)
585 {
586     int i, c;
587 
588     for (i = 0; i < KEYBUFSZ; i++) {
589 	if (keybuf[i] != 0) {
590 	    c = keybuf[i];
591 	    keybuf[i] = 0;
592 	    return (c);
593 	}
594     }
595 
596     if (vidc_ischar(cp)) {
597 	v86.ctl = 0;
598 	v86.addr = 0x16;
599 	v86.eax = 0x0;
600 	v86int();
601 	if ((v86.eax & 0xff) != 0) {
602 		return (v86.eax & 0xff);
603 	}
604 
605 	/* extended keys */
606 	switch (v86.eax & 0xff00) {
607 	case 0x4800:	/* up */
608 		keybuf[0] = '[';
609 		keybuf[1] = 'A';
610 		return (0x1b);	/* esc */
611 	case 0x4b00:	/* left */
612 		keybuf[0] = '[';
613 		keybuf[1] = 'D';
614 		return (0x1b);	/* esc */
615 	case 0x4d00:	/* right */
616 		keybuf[0] = '[';
617 		keybuf[1] = 'C';
618 		return (0x1b);	/* esc */
619 	case 0x5000:	/* down */
620 		keybuf[0] = '[';
621 		keybuf[1] = 'B';
622 		return (0x1b);	/* esc */
623 	default:
624 		return (-1);
625 	}
626     } else {
627 	return (-1);
628     }
629 }
630 
631 static int
vidc_ischar(struct console * cp)632 vidc_ischar(struct console *cp)
633 {
634     int i;
635 
636     for (i = 0; i < KEYBUFSZ; i++) {
637 	if (keybuf[i] != 0) {
638 	    return (1);
639 	}
640     }
641 
642     v86.ctl = V86_FLAGS;
643     v86.addr = 0x16;
644     v86.eax = 0x100;
645     v86int();
646     return (!V86_ZR(v86.efl));
647 }
648 
649 #if KEYBOARD_PROBE
650 
651 #define PROBE_MAXRETRY	5
652 #define PROBE_MAXWAIT	400
653 #define IO_DUMMY	0x84
654 #define IO_KBD		0x060		/* 8042 Keyboard */
655 
656 /* selected defines from kbdio.h */
657 #define KBD_STATUS_PORT 	4	/* status port, read */
658 #define KBD_DATA_PORT		0	/* data port, read/write
659 					 * also used as keyboard command
660 					 * and mouse command port
661 					 */
662 #define KBDC_ECHO		0x00ee
663 #define KBDS_ANY_BUFFER_FULL	0x0001
664 #define KBDS_INPUT_BUFFER_FULL	0x0002
665 #define KBD_ECHO		0x00ee
666 
667 /* 7 microsec delay necessary for some keyboard controllers */
668 static void
delay7(void)669 delay7(void)
670 {
671     /*
672      * I know this is broken, but no timer is available yet at this stage...
673      * See also comments in `delay1ms()'.
674      */
675     inb(IO_DUMMY); inb(IO_DUMMY);
676     inb(IO_DUMMY); inb(IO_DUMMY);
677     inb(IO_DUMMY); inb(IO_DUMMY);
678 }
679 
680 /*
681  * This routine uses an inb to an unused port, the time to execute that
682  * inb is approximately 1.25uS.  This value is pretty constant across
683  * all CPU's and all buses, with the exception of some PCI implentations
684  * that do not forward this I/O address to the ISA bus as they know it
685  * is not a valid ISA bus address, those machines execute this inb in
686  * 60 nS :-(.
687  *
688  */
689 static void
delay1ms(void)690 delay1ms(void)
691 {
692     int i = 800;
693     while (--i >= 0)
694 	(void)inb(0x84);
695 }
696 
697 /*
698  * We use the presence/absence of a keyboard to determine whether the internal
699  * console can be used for input.
700  *
701  * Perform a simple test on the keyboard; issue the ECHO command and see
702  * if the right answer is returned. We don't do anything as drastic as
703  * full keyboard reset; it will be too troublesome and take too much time.
704  */
705 static int
probe_keyboard(void)706 probe_keyboard(void)
707 {
708     int retry = PROBE_MAXRETRY;
709     int wait;
710     int i;
711 
712     while (--retry >= 0) {
713 	/* flush any noise */
714 	while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) {
715 	    delay7();
716 	    inb(IO_KBD + KBD_DATA_PORT);
717 	    delay1ms();
718 	}
719 
720 	/* wait until the controller can accept a command */
721 	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
722 	    if (((i = inb(IO_KBD + KBD_STATUS_PORT))
723                 & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0)
724 		break;
725 	    if (i & KBDS_ANY_BUFFER_FULL) {
726 		delay7();
727 	        inb(IO_KBD + KBD_DATA_PORT);
728 	    }
729 	    delay1ms();
730 	}
731 	if (wait <= 0)
732 	    continue;
733 
734 	/* send the ECHO command */
735 	outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO);
736 
737 	/* wait for a response */
738 	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
739 	     if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)
740 		 break;
741 	     delay1ms();
742 	}
743 	if (wait <= 0)
744 	    continue;
745 
746 	delay7();
747 	i = inb(IO_KBD + KBD_DATA_PORT);
748 #ifdef PROBE_KBD_BEBUG
749         printf("probe_keyboard: got 0x%x.\n", i);
750 #endif
751 	if (i == KBD_ECHO) {
752 	    /* got the right answer */
753 	    return (1);
754 	}
755     }
756 
757     return (0);
758 }
759 #endif /* KEYBOARD_PROBE */
760