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