xref: /freebsd/stand/i386/libi386/vidconsole.c (revision c9083b850aed19fff781fd82084e8308c9ae6df6)
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 __FBSDID("$FreeBSD$");
32 
33 #include <stand.h>
34 #include <bootstrap.h>
35 #include <btxv86.h>
36 #include <machine/psl.h>
37 #include "libi386.h"
38 
39 #if KEYBOARD_PROBE
40 #include <machine/cpufunc.h>
41 
42 static int	probe_keyboard(void);
43 #endif
44 static void	vidc_probe(struct console *cp);
45 static int	vidc_init(int arg);
46 static void	vidc_putchar(int c);
47 static int	vidc_getchar(void);
48 static int	vidc_ischar(void);
49 
50 static int	vidc_started;
51 
52 #ifdef TERM_EMU
53 #define MAXARGS		8
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 int	args[MAXARGS], argc;
69 static int	fg_c, bg_c, curx, cury;
70 static int	esc;
71 #endif
72 
73 
74 struct console vidconsole = {
75     "vidconsole",
76     "internal video/keyboard",
77     0,
78     vidc_probe,
79     vidc_init,
80     vidc_putchar,
81     vidc_getchar,
82     vidc_ischar
83 };
84 
85 static void
86 vidc_probe(struct console *cp)
87 {
88 
89     /* look for a keyboard */
90 #if KEYBOARD_PROBE
91     if (probe_keyboard())
92 #endif
93     {
94 
95 	cp->c_flags |= C_PRESENTIN;
96     }
97 
98     /* XXX for now, always assume we can do BIOS screen output */
99     cp->c_flags |= C_PRESENTOUT;
100 }
101 
102 static int
103 vidc_init(int arg)
104 {
105     int		i;
106 
107     if (vidc_started && arg == 0)
108 	return (0);
109     vidc_started = 1;
110 #ifdef TERM_EMU
111     /* Init terminal emulator */
112     end_term();
113     get_pos(&curx, &cury);
114     curs_move(&curx, &cury, curx, cury);
115     fg_c = DEFAULT_FGCOLOR;
116     bg_c = DEFAULT_BGCOLOR;
117 #endif
118     for (i = 0; i < 10 && vidc_ischar(); i++)
119 	(void)vidc_getchar();
120     return (0);	/* XXX reinit? */
121 }
122 
123 void
124 vidc_biosputchar(int c)
125 {
126 
127     v86.ctl = 0;
128     v86.addr = 0x10;
129     v86.eax = 0xe00 | (c & 0xff);
130     v86.ebx = 0x7;
131     v86int();
132 }
133 
134 static void
135 vidc_rawputchar(int c)
136 {
137     int		i;
138 
139     if (c == '\t')
140 	/* lame tab expansion */
141 	for (i = 0; i < 8; i++)
142 	    vidc_rawputchar(' ');
143     else {
144 #ifndef TERM_EMU
145         vidc_biosputchar(c);
146 #else
147 	/* Emulate AH=0eh (teletype output) */
148 	switch(c) {
149 	case '\a':
150 	    vidc_biosputchar(c);
151 	    return;
152 	case '\r':
153 	    curx = 0;
154 	    curs_move(&curx, &cury, curx, cury);
155 	    return;
156 	case '\n':
157 	    cury++;
158 	    if (cury > 24) {
159 		scroll_up(1, fg_c, bg_c);
160 		cury--;
161 	    } else {
162 		curs_move(&curx, &cury, curx, cury);
163 	    }
164 	    return;
165 	case '\b':
166 	    if (curx > 0) {
167 		curx--;
168 		curs_move(&curx, &cury, curx, cury);
169 		/* write_char(' ', fg_c, bg_c); XXX destructive(!) */
170 		return;
171 	    }
172 	    return;
173 	default:
174 	    write_char(c, fg_c, bg_c);
175 	    curx++;
176 	    if (curx > 79) {
177 		curx = 0;
178 		cury++;
179 	    }
180 	    if (cury > 24) {
181 		curx = 0;
182 		scroll_up(1, fg_c, bg_c);
183 		cury--;
184 	    }
185 	}
186 	curs_move(&curx, &cury, curx, cury);
187 #endif
188     }
189 }
190 
191 #ifdef TERM_EMU
192 
193 /* Get cursor position on the screen. Result is in edx. Sets
194  * curx and cury appropriately.
195  */
196 void
197 get_pos(int *x, int *y)
198 {
199 
200     v86.ctl = 0;
201     v86.addr = 0x10;
202     v86.eax = 0x0300;
203     v86.ebx = 0x0;
204     v86int();
205     *x = v86.edx & 0x00ff;
206     *y = (v86.edx & 0xff00) >> 8;
207 }
208 
209 /* Move cursor to x rows and y cols (0-based). */
210 void
211 curs_move(int *_x, int *_y, int x, int y)
212 {
213 
214     v86.ctl = 0;
215     v86.addr = 0x10;
216     v86.eax = 0x0200;
217     v86.ebx = 0x0;
218     v86.edx = ((0x00ff & y) << 8) + (0x00ff & x);
219     v86int();
220     *_x = x;
221     *_y = y;
222     /* If there is ctrl char at this position, cursor would be invisible.
223      * Make it a space instead.
224      */
225     v86.ctl = 0;
226     v86.addr = 0x10;
227     v86.eax = 0x0800;
228     v86.ebx = 0x0;
229     v86int();
230 #define isvisible(c)	(((c) >= 32) && ((c) < 255))
231     if (!isvisible(v86.eax & 0x00ff)) {
232 	write_char(' ', fg_c, bg_c);
233     }
234 }
235 
236 /* Scroll up the whole window by a number of rows. If rows==0,
237  * clear the window. fg and bg are attributes for the new lines
238  * inserted in the window.
239  */
240 void
241 scroll_up(int rows, int fgcol, int bgcol)
242 {
243 
244     if (rows == 0)
245 	rows = 25;
246     v86.ctl = 0;
247     v86.addr = 0x10;
248     v86.eax = 0x0600 + (0x00ff & rows);
249     v86.ebx = (bgcol << 12) + (fgcol << 8);
250     v86.ecx = 0x0;
251     v86.edx = 0x184f;
252     v86int();
253 }
254 
255 /* Write character and attribute at cursor position. */
256 void
257 write_char(int c, int fgcol, int bgcol)
258 {
259 
260     v86.ctl = 0;
261     v86.addr = 0x10;
262     v86.eax = 0x0900 + (0x00ff & c);
263     v86.ebx = (bgcol << 4) + fgcol;
264     v86.ecx = 0x1;
265     v86int();
266 }
267 
268 /**************************************************************/
269 /*
270  * Screen manipulation functions. They use accumulated data in
271  * args[] and argc variables.
272  *
273  */
274 
275 /* Clear display from current position to end of screen */
276 void
277 CD(void)
278 {
279 
280     get_pos(&curx, &cury);
281     if (curx > 0) {
282 	v86.ctl = 0;
283 	v86.addr = 0x10;
284 	v86.eax = 0x0600;
285 	v86.ebx = (bg_c << 4) + fg_c;
286 	v86.ecx = (cury << 8) + curx;
287 	v86.edx = (cury << 8) + 79;
288 	v86int();
289 	if (++cury > 24) {
290 	    end_term();
291 	    return;
292 	}
293     }
294     v86.ctl = 0;
295     v86.addr = 0x10;
296     v86.eax = 0x0600;
297     v86.ebx = (bg_c << 4) + fg_c;
298     v86.ecx = (cury << 8) + 0;
299     v86.edx = (24 << 8) + 79;
300     v86int();
301     end_term();
302 }
303 
304 /* Absolute cursor move to args[0] rows and args[1] columns
305  * (the coordinates are 1-based).
306  */
307 void
308 CM(void)
309 {
310 
311     if (args[0] > 0)
312 	args[0]--;
313     if (args[1] > 0)
314 	args[1]--;
315     curs_move(&curx, &cury, args[1], args[0]);
316     end_term();
317 }
318 
319 /* Home cursor (left top corner) */
320 void
321 HO(void)
322 {
323 
324     argc = 1;
325     args[0] = args[1] = 1;
326     CM();
327 }
328 
329 /* Clear internal state of the terminal emulation code */
330 void
331 end_term(void)
332 {
333 
334     esc = 0;
335     argc = -1;
336 }
337 
338 /* Gracefully exit ESC-sequence processing in case of misunderstanding */
339 void
340 bail_out(int c)
341 {
342     char buf[16], *ch;
343     int i;
344 
345     if (esc) {
346 	vidc_rawputchar('\033');
347 	if (esc != '\033')
348 	    vidc_rawputchar(esc);
349 	for (i = 0; i <= argc; ++i) {
350 	    sprintf(buf, "%d", args[i]);
351 	    ch = buf;
352 	    while (*ch)
353 		vidc_rawputchar(*ch++);
354 	}
355     }
356     vidc_rawputchar(c);
357     end_term();
358 }
359 
360 static void
361 get_arg(int c)
362 {
363 
364     if (argc < 0)
365 	argc = 0;
366     args[argc] *= 10;
367     args[argc] += c - '0';
368 }
369 
370 /* Emulate basic capabilities of cons25 terminal */
371 void
372 vidc_term_emu(int c)
373 {
374     static int ansi_col[] = {
375 	0, 4, 2, 6, 1, 5, 3, 7,
376     };
377     int t;
378     int i;
379 
380     switch (esc) {
381     case 0:
382 	switch (c) {
383 	case '\033':
384 	    esc = c;
385 	    break;
386 	default:
387 	    vidc_rawputchar(c);
388 	    break;
389 	}
390 	break;
391 
392     case '\033':
393 	switch (c) {
394 	case '[':
395 	    esc = c;
396 	    args[0] = 0;
397 	    argc = -1;
398 	    break;
399 	default:
400 	    bail_out(c);
401 	    break;
402 	}
403 	break;
404 
405     case '[':
406 	switch (c) {
407 	case ';':
408 	    if (argc < 0)	/* XXX */
409 		argc = 0;
410 	    else if (argc + 1 >= MAXARGS)
411 		bail_out(c);
412 	    else
413 		args[++argc] = 0;
414 	    break;
415 	case 'H':
416 	    if (argc < 0)
417 		HO();
418 	    else if (argc == 1)
419 		CM();
420 	    else
421 		bail_out(c);
422 	    break;
423 	case 'J':
424 	    if (argc < 0)
425 		CD();
426 	    else
427 		bail_out(c);
428 	    break;
429 	case 'm':
430 	    if (argc < 0) {
431 		fg_c = DEFAULT_FGCOLOR;
432 		bg_c = DEFAULT_BGCOLOR;
433 	    }
434 	    for (i = 0; i <= argc; ++i) {
435 		switch (args[i]) {
436 		case 0:		/* back to normal */
437 		    fg_c = DEFAULT_FGCOLOR;
438 		    bg_c = DEFAULT_BGCOLOR;
439 		    break;
440 		case 1:		/* bold */
441 		    fg_c |= 0x8;
442 		    break;
443 		case 4:		/* underline */
444 		case 5:		/* blink */
445 		    bg_c |= 0x8;
446 		    break;
447 		case 7:		/* reverse */
448 		    t = fg_c;
449 		    fg_c = bg_c;
450 		    bg_c = t;
451 		    break;
452 		case 22:	/* normal intensity */
453 		    fg_c &= ~0x8;
454 		    break;
455 		case 24:	/* not underline */
456 		case 25:	/* not blinking */
457 		    bg_c &= ~0x8;
458 		    break;
459 		case 30: case 31: case 32: case 33:
460 		case 34: case 35: case 36: case 37:
461 		    fg_c = ansi_col[args[i] - 30];
462 		    break;
463 		case 39:	/* normal */
464 		    fg_c = DEFAULT_FGCOLOR;
465 		    break;
466 		case 40: case 41: case 42: case 43:
467 		case 44: case 45: case 46: case 47:
468 		    bg_c = ansi_col[args[i] - 40];
469 		    break;
470 		case 49:	/* normal */
471 		    bg_c = DEFAULT_BGCOLOR;
472 		    break;
473 		}
474 	    }
475 	    end_term();
476 	    break;
477 	default:
478 	    if (isdigit(c))
479 		get_arg(c);
480 	    else
481 		bail_out(c);
482 	    break;
483 	}
484 	break;
485 
486     default:
487 	bail_out(c);
488 	break;
489     }
490 }
491 #endif
492 
493 static void
494 vidc_putchar(int c)
495 {
496 #ifdef TERM_EMU
497     vidc_term_emu(c);
498 #else
499     vidc_rawputchar(c);
500 #endif
501 }
502 
503 static int
504 vidc_getchar(void)
505 {
506 
507     if (vidc_ischar()) {
508 	v86.ctl = 0;
509 	v86.addr = 0x16;
510 	v86.eax = 0x0;
511 	v86int();
512 	return (v86.eax & 0xff);
513     } else {
514 	return (-1);
515     }
516 }
517 
518 static int
519 vidc_ischar(void)
520 {
521 
522     v86.ctl = V86_FLAGS;
523     v86.addr = 0x16;
524     v86.eax = 0x100;
525     v86int();
526     return (!V86_ZR(v86.efl));
527 }
528 
529 #if KEYBOARD_PROBE
530 
531 #define PROBE_MAXRETRY	5
532 #define PROBE_MAXWAIT	400
533 #define IO_DUMMY	0x84
534 #define IO_KBD		0x060		/* 8042 Keyboard */
535 
536 /* selected defines from kbdio.h */
537 #define KBD_STATUS_PORT 	4	/* status port, read */
538 #define KBD_DATA_PORT		0	/* data port, read/write
539 					 * also used as keyboard command
540 					 * and mouse command port
541 					 */
542 #define KBDC_ECHO		0x00ee
543 #define KBDS_ANY_BUFFER_FULL	0x0001
544 #define KBDS_INPUT_BUFFER_FULL	0x0002
545 #define KBD_ECHO		0x00ee
546 
547 /* 7 microsec delay necessary for some keyboard controllers */
548 static void
549 delay7(void)
550 {
551     /*
552      * I know this is broken, but no timer is available yet at this stage...
553      * See also comments in `delay1ms()'.
554      */
555     inb(IO_DUMMY); inb(IO_DUMMY);
556     inb(IO_DUMMY); inb(IO_DUMMY);
557     inb(IO_DUMMY); inb(IO_DUMMY);
558 }
559 
560 /*
561  * This routine uses an inb to an unused port, the time to execute that
562  * inb is approximately 1.25uS.  This value is pretty constant across
563  * all CPU's and all buses, with the exception of some PCI implentations
564  * that do not forward this I/O address to the ISA bus as they know it
565  * is not a valid ISA bus address, those machines execute this inb in
566  * 60 nS :-(.
567  *
568  */
569 static void
570 delay1ms(void)
571 {
572     int i = 800;
573     while (--i >= 0)
574 	(void)inb(0x84);
575 }
576 
577 /*
578  * We use the presence/absence of a keyboard to determine whether the internal
579  * console can be used for input.
580  *
581  * Perform a simple test on the keyboard; issue the ECHO command and see
582  * if the right answer is returned. We don't do anything as drastic as
583  * full keyboard reset; it will be too troublesome and take too much time.
584  */
585 static int
586 probe_keyboard(void)
587 {
588     int retry = PROBE_MAXRETRY;
589     int wait;
590     int i;
591 
592     while (--retry >= 0) {
593 	/* flush any noise */
594 	while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) {
595 	    delay7();
596 	    inb(IO_KBD + KBD_DATA_PORT);
597 	    delay1ms();
598 	}
599 
600 	/* wait until the controller can accept a command */
601 	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
602 	    if (((i = inb(IO_KBD + KBD_STATUS_PORT))
603                 & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0)
604 		break;
605 	    if (i & KBDS_ANY_BUFFER_FULL) {
606 		delay7();
607 	        inb(IO_KBD + KBD_DATA_PORT);
608 	    }
609 	    delay1ms();
610 	}
611 	if (wait <= 0)
612 	    continue;
613 
614 	/* send the ECHO command */
615 	outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO);
616 
617 	/* wait for a response */
618 	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
619 	     if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)
620 		 break;
621 	     delay1ms();
622 	}
623 	if (wait <= 0)
624 	    continue;
625 
626 	delay7();
627 	i = inb(IO_KBD + KBD_DATA_PORT);
628 #ifdef PROBE_KBD_BEBUG
629         printf("probe_keyboard: got 0x%x.\n", i);
630 #endif
631 	if (i == KBD_ECHO) {
632 	    /* got the right answer */
633 	    return (1);
634 	}
635     }
636 
637     return (0);
638 }
639 #endif /* KEYBOARD_PROBE */
640