loader: updates from review
[unleashed.git] / usr / src / boot / sys / boot / i386 / libi386 / vidconsole.c
blobedefda223c8284d8f9ee0b3e8e48348d672e2a46
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.
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.
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.
27 * Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp
30 #include <sys/cdefs.h>
32 #include <stand.h>
33 #include <bootstrap.h>
34 #include <btxv86.h>
35 #include <machine/psl.h>
36 #include "libi386.h"
38 #if KEYBOARD_PROBE
39 #include <machine/cpufunc.h>
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);
49 static int vidc_started;
51 #ifdef TERM_EMU
52 #define MAXARGS 8
53 #define KEYBUFSZ 10
54 #define DEFAULT_FGCOLOR 7
55 #define DEFAULT_BGCOLOR 0
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);
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
75 struct console text = {
76 "text",
77 "internal video/keyboard",
79 vidc_probe,
80 vidc_init,
81 vidc_putchar,
82 vidc_getchar,
83 vidc_ischar,
84 NULL
87 static void
88 vidc_probe(struct console *cp)
91 /* look for a keyboard */
92 #if KEYBOARD_PROBE
93 if (probe_keyboard())
94 #endif
97 cp->c_flags |= C_PRESENTIN;
100 /* XXX for now, always assume we can do BIOS screen output */
101 cp->c_flags |= C_PRESENTOUT;
104 static int
105 vidc_init(struct console *cp, int arg)
107 int i;
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? */
126 void
127 vidc_biosputchar(int c)
130 v86.ctl = 0;
131 v86.addr = 0x10;
132 v86.eax = 0xe00 | (c & 0xff);
133 v86.ebx = 0x7;
134 v86int();
137 static void
138 vidc_rawputchar(int c)
140 int i;
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;
163 if (cury > 24) {
164 scroll_up(1, fg_c, bg_c);
165 cury--;
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);
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);
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++;
198 curs_move(&curx, &cury, curx, cury);
199 #endif
203 #ifdef TERM_EMU
205 /* Get cursor position on the screen. Result is in edx. Sets
206 * curx and cury appropriately.
208 void
209 get_pos(int *x, int *y)
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;
221 /* Move cursor to x rows and y cols (0-based). */
222 void
223 curs_move(int *_x, int *_y, int x, int y)
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.
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);
248 v86.ctl = 0; /* show normal underline cursor */
249 v86.addr = 0x10;
250 v86.eax = 0x0100;
251 v86.ecx = 0x0607;
252 v86int();
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.
259 void
260 scroll_up(int rows, int fgcol, int bgcol)
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();
273 /* Write character and attribute at cursor position. */
274 void
275 write_char(int c, int fgcol, int bgcol)
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();
286 /**************************************************************/
288 * Screen manipulation functions. They use accumulated data in
289 * args[] and argc variables.
293 /* Clear line from current position to end of line */
294 void
295 CL(int direction)
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;
311 v86int();
312 end_term();
313 return;
316 /* Clear display from current position to end of screen */
317 void
318 CD(void)
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;
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();
345 /* Absolute cursor move to args[0] rows and args[1] columns
346 * (the coordinates are 1-based).
348 void
349 CM(void)
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();
360 /* Home cursor (left top corner) */
361 void
362 HO(void)
365 argc = 1;
366 args[0] = args[1] = 1;
367 CM();
370 /* Clear internal state of the terminal emulation code */
371 void
372 end_term(void)
375 esc = 0;
376 argc = -1;
379 /* Gracefully exit ESC-sequence processing in case of misunderstanding */
380 void
381 bail_out(int c)
383 char buf[16], *ch;
384 int i;
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++);
397 vidc_rawputchar(c);
398 end_term();
401 static void
402 get_arg(int c)
405 if (argc < 0)
406 argc = 0;
407 args[argc] *= 10;
408 args[argc] += c - '0';
411 /* Emulate basic capabilities of sun-color terminal */
412 void
413 vidc_term_emu(int c)
415 static int ansi_col[] = {
416 0, 4, 2, 6, 1, 5, 3, 7,
418 int t;
419 int i;
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;
431 break;
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;
444 break;
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);
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;
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;
555 end_term();
556 break;
557 default:
558 if (isdigit(c))
559 get_arg(c);
560 else
561 bail_out(c);
562 break;
564 break;
566 default:
567 bail_out(c);
568 break;
571 #endif
573 static void
574 vidc_putchar(struct console *cp, int c)
576 #ifdef TERM_EMU
577 vidc_term_emu(c);
578 #else
579 vidc_rawputchar(c);
580 #endif
583 static int
584 vidc_getchar(struct console *cp)
586 int i, c;
588 for (i = 0; i < KEYBUFSZ; i++) {
589 if (keybuf[i] != 0) {
590 c = keybuf[i];
591 keybuf[i] = 0;
592 return (c);
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);
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);
626 } else {
627 return (-1);
631 static int
632 vidc_ischar(struct console *cp)
634 int i;
636 for (i = 0; i < KEYBUFSZ; i++) {
637 if (keybuf[i] != 0) {
638 return (1);
642 v86.ctl = V86_FLAGS;
643 v86.addr = 0x16;
644 v86.eax = 0x100;
645 v86int();
646 return (!V86_ZR(v86.efl));
649 #if KEYBOARD_PROBE
651 #define PROBE_MAXRETRY 5
652 #define PROBE_MAXWAIT 400
653 #define IO_DUMMY 0x84
654 #define IO_KBD 0x060 /* 8042 Keyboard */
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
662 #define KBDC_ECHO 0x00ee
663 #define KBDS_ANY_BUFFER_FULL 0x0001
664 #define KBDS_INPUT_BUFFER_FULL 0x0002
665 #define KBD_ECHO 0x00ee
667 /* 7 microsec delay necessary for some keyboard controllers */
668 static void
669 delay7(void)
672 * I know this is broken, but no timer is available yet at this stage...
673 * See also comments in `delay1ms()'.
675 inb(IO_DUMMY); inb(IO_DUMMY);
676 inb(IO_DUMMY); inb(IO_DUMMY);
677 inb(IO_DUMMY); inb(IO_DUMMY);
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 :-(.
689 static void
690 delay1ms(void)
692 int i = 800;
693 while (--i >= 0)
694 (void)inb(0x84);
698 * We use the presence/absence of a keyboard to determine whether the internal
699 * console can be used for input.
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.
705 static int
706 probe_keyboard(void)
708 int retry = PROBE_MAXRETRY;
709 int wait;
710 int i;
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();
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);
729 delay1ms();
731 if (wait <= 0)
732 continue;
734 /* send the ECHO command */
735 outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO);
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();
743 if (wait <= 0)
744 continue;
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);
757 return (0);
759 #endif /* KEYBOARD_PROBE */