xref: /freebsd/stand/i386/libi386/vidconsole.c (revision 5bf5ca772c6de2d53344a78cf461447cc322ccea)
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 30: case 31: case 32: case 33:
453 		case 34: case 35: case 36: case 37:
454 		    fg_c = ansi_col[args[i] - 30];
455 		    break;
456 		case 39:	/* normal */
457 		    fg_c = DEFAULT_FGCOLOR;
458 		    break;
459 		case 40: case 41: case 42: case 43:
460 		case 44: case 45: case 46: case 47:
461 		    bg_c = ansi_col[args[i] - 40];
462 		    break;
463 		case 49:	/* normal */
464 		    bg_c = DEFAULT_BGCOLOR;
465 		    break;
466 		}
467 	    }
468 	    end_term();
469 	    break;
470 	default:
471 	    if (isdigit(c))
472 		get_arg(c);
473 	    else
474 		bail_out(c);
475 	    break;
476 	}
477 	break;
478 
479     default:
480 	bail_out(c);
481 	break;
482     }
483 }
484 #endif
485 
486 static void
487 vidc_putchar(int c)
488 {
489 #ifdef TERM_EMU
490     vidc_term_emu(c);
491 #else
492     vidc_rawputchar(c);
493 #endif
494 }
495 
496 static int
497 vidc_getchar(void)
498 {
499 
500     if (vidc_ischar()) {
501 	v86.ctl = 0;
502 	v86.addr = 0x16;
503 	v86.eax = 0x0;
504 	v86int();
505 	return (v86.eax & 0xff);
506     } else {
507 	return (-1);
508     }
509 }
510 
511 static int
512 vidc_ischar(void)
513 {
514 
515     v86.ctl = V86_FLAGS;
516     v86.addr = 0x16;
517     v86.eax = 0x100;
518     v86int();
519     return (!V86_ZR(v86.efl));
520 }
521 
522 #if KEYBOARD_PROBE
523 
524 #define PROBE_MAXRETRY	5
525 #define PROBE_MAXWAIT	400
526 #define IO_DUMMY	0x84
527 #define IO_KBD		0x060		/* 8042 Keyboard */
528 
529 /* selected defines from kbdio.h */
530 #define KBD_STATUS_PORT 	4	/* status port, read */
531 #define KBD_DATA_PORT		0	/* data port, read/write
532 					 * also used as keyboard command
533 					 * and mouse command port
534 					 */
535 #define KBDC_ECHO		0x00ee
536 #define KBDS_ANY_BUFFER_FULL	0x0001
537 #define KBDS_INPUT_BUFFER_FULL	0x0002
538 #define KBD_ECHO		0x00ee
539 
540 /* 7 microsec delay necessary for some keyboard controllers */
541 static void
542 delay7(void)
543 {
544     /*
545      * I know this is broken, but no timer is available yet at this stage...
546      * See also comments in `delay1ms()'.
547      */
548     inb(IO_DUMMY); inb(IO_DUMMY);
549     inb(IO_DUMMY); inb(IO_DUMMY);
550     inb(IO_DUMMY); inb(IO_DUMMY);
551 }
552 
553 /*
554  * This routine uses an inb to an unused port, the time to execute that
555  * inb is approximately 1.25uS.  This value is pretty constant across
556  * all CPU's and all buses, with the exception of some PCI implentations
557  * that do not forward this I/O address to the ISA bus as they know it
558  * is not a valid ISA bus address, those machines execute this inb in
559  * 60 nS :-(.
560  *
561  */
562 static void
563 delay1ms(void)
564 {
565     int i = 800;
566     while (--i >= 0)
567 	(void)inb(0x84);
568 }
569 
570 /*
571  * We use the presence/absence of a keyboard to determine whether the internal
572  * console can be used for input.
573  *
574  * Perform a simple test on the keyboard; issue the ECHO command and see
575  * if the right answer is returned. We don't do anything as drastic as
576  * full keyboard reset; it will be too troublesome and take too much time.
577  */
578 static int
579 probe_keyboard(void)
580 {
581     int retry = PROBE_MAXRETRY;
582     int wait;
583     int i;
584 
585     while (--retry >= 0) {
586 	/* flush any noise */
587 	while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) {
588 	    delay7();
589 	    inb(IO_KBD + KBD_DATA_PORT);
590 	    delay1ms();
591 	}
592 
593 	/* wait until the controller can accept a command */
594 	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
595 	    if (((i = inb(IO_KBD + KBD_STATUS_PORT))
596                 & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0)
597 		break;
598 	    if (i & KBDS_ANY_BUFFER_FULL) {
599 		delay7();
600 	        inb(IO_KBD + KBD_DATA_PORT);
601 	    }
602 	    delay1ms();
603 	}
604 	if (wait <= 0)
605 	    continue;
606 
607 	/* send the ECHO command */
608 	outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO);
609 
610 	/* wait for a response */
611 	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
612 	     if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)
613 		 break;
614 	     delay1ms();
615 	}
616 	if (wait <= 0)
617 	    continue;
618 
619 	delay7();
620 	i = inb(IO_KBD + KBD_DATA_PORT);
621 #ifdef PROBE_KBD_BEBUG
622         printf("probe_keyboard: got 0x%x.\n", i);
623 #endif
624 	if (i == KBD_ECHO) {
625 	    /* got the right answer */
626 	    return (1);
627 	}
628     }
629 
630     return (0);
631 }
632 #endif /* KEYBOARD_PROBE */
633