vidconsole.c revision 84276
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 Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp
28 *
29 * $FreeBSD: head/sys/boot/i386/libi386/vidconsole.c 84276 2001-10-01 11:42:25Z yokota $
30 */
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
41static int	probe_keyboard(void);
42#endif
43static void	vidc_probe(struct console *cp);
44static int	vidc_init(int arg);
45static void	vidc_putchar(int c);
46static int	vidc_getchar(void);
47static int	vidc_ischar(void);
48
49static int	vidc_started;
50
51#ifdef TERM_EMU
52void		end_term(void);
53void		bail_out(int c);
54void		vidc_term_emu(int c);
55void		get_pos(void);
56void		curs_move(int x, int y);
57void		write_char(int c, int fg, int bg);
58void		scroll_up(int rows, int fg, int bg);
59int		pow10(int i);
60void		AB(void);
61void		AF(void);
62void		CD(void);
63void		CM(void);
64void		HO(void);
65void		ME(void);
66
67static int	args[2],argc,br;
68static int	fg,bg,dig;
69static int	fg_c,bg_c,curx,cury;
70static int	esc;
71#endif
72
73
74struct 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
85static void
86vidc_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
102static int
103vidc_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();
114    curs_move(curx, cury);
115    fg_c = 7;
116    bg_c = 0;
117#endif
118    for (i = 0; i < 10 && vidc_ischar(); i++)
119	(void)vidc_getchar();
120    return (0);	/* XXX reinit? */
121}
122
123static void
124vidc_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
134static void
135vidc_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);
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);
163	    }
164	    return;
165	case '\b':
166	    if (curx > 0) {
167		curx--;
168		curs_move(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);
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 */
196void
197get_pos(void)
198{
199
200    v86.ctl = 0;
201    v86.addr = 0x10;
202    v86.eax = 0x0300;
203    v86.ebx = 0x0;
204    v86int();
205    curx = v86.edx & 0x00ff;
206    cury = (v86.edx & 0xff00) >> 8;
207}
208
209/* Move cursor to x rows and y cols (0-based). */
210void
211curs_move(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    curx = x;
221    cury = 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 */
240void
241scroll_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. */
256void
257write_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/* Calculate power of 10 */
269int
270pow10(int i)
271{
272    int res = 1;
273
274    while (i-- > 0) {
275	res *= 10;
276    }
277    return res;
278}
279
280/**************************************************************/
281/*
282 * Screen manipulation functions. They use accumulated data in
283 * args[] and argc variables.
284 *
285 */
286
287/* Set background color */
288void
289AB(void)
290{
291
292    bg_c = args[0];
293    end_term();
294}
295
296/* Set foreground color */
297void
298AF(void)
299{
300
301    fg_c = args[0];
302    end_term();
303}
304
305/* Clear display from current position to end of screen */
306void
307CD(void)
308{
309
310    get_pos();
311    v86.ctl = 0;
312    v86.addr = 0x10;
313    v86.eax = 0x0600;
314    v86.ebx = (bg_c << 4) + fg_c;
315    v86.ecx = v86.edx;
316    v86.edx = 0x184f;
317    v86int();
318    curx = 0;
319    curs_move(curx, cury);
320    end_term();
321}
322
323/* Absolute cursor move to args[0] rows and args[1] columns
324 * (the coordinates are 1-based).
325 */
326void
327CM(void)
328{
329
330    if (args[0] > 0)
331	args[0]--;
332    if (args[1] > 0)
333	args[1]--;
334    curs_move(args[1], args[0]);
335    end_term();
336}
337
338/* Home cursor (left top corner) */
339void
340HO(void)
341{
342
343    argc = 1;
344    args[0] = args[1] = 1;
345    CM();
346}
347
348/* Exit attribute mode (reset fore/back-ground colors to defaults) */
349void
350ME(void)
351{
352
353    fg_c = 7;
354    bg_c = 0;
355    end_term();
356}
357
358/* Clear internal state of the terminal emulation code */
359void
360end_term(void)
361{
362
363    esc = 0;
364    argc = -1;
365    fg = bg = br = 0;
366    args[0] = args[1] = 0;
367    dig = 0;
368}
369
370/* Gracefully exit ESC-sequence processing in case of misunderstanding */
371void
372bail_out(int c)
373{
374    char buf[6],*ch;
375
376    if (esc)
377	vidc_rawputchar('\033');
378    if (br)
379	vidc_rawputchar('[');
380    if (argc > -1) {
381	sprintf(buf, "%d", args[0]);
382	ch = buf;
383	while (*ch)
384	    vidc_rawputchar(*ch++);
385
386	if (argc > 0) {
387	    vidc_rawputchar(';');
388	    sprintf(buf, "%d", args[1]);
389	    ch = buf;
390	    while (*ch)
391		vidc_rawputchar(*ch++);
392	}
393    }
394    vidc_rawputchar(c);
395    end_term();
396}
397
398/* Emulate basic capabilities of cons25 terminal */
399void
400vidc_term_emu(int c)
401{
402
403    if (!esc) {
404	if (c == '\033') {
405	    esc = 1;
406	} else {
407	    vidc_rawputchar(c);
408	}
409	return;
410    }
411
412    /* Do ESC sequences processing */
413    switch (c) {
414    case '\033':
415	/* ESC in ESC sequence - error */
416	bail_out(c);
417	break;
418    case '[':
419	/* Check if it's first char after ESC */
420        if (argc < 0) {
421            br = 1;
422        } else {
423	    bail_out(c);
424        }
425	break;
426    case 'H':
427	/* Emulate \E[H (cursor home) and
428	 * \E%d;%dH (cursor absolute move) */
429	if (br) {
430	    switch (argc) {
431	    case -1:
432		HO();
433		break;
434	    case 1:
435		if (fg)
436		    args[0] += pow10(dig)*3;
437		if (bg)
438		    args[0] += pow10(dig)*4;
439		CM();
440		break;
441	    default:
442		bail_out(c);
443	    }
444	} else bail_out(c);
445	break;
446    case 'J':
447	/* Emulate \EJ (clear to end of screen) */
448	if (br && argc < 0) {
449	    CD();
450	} else bail_out(c);
451	break;
452    case ';':
453	/* perhaps args separator */
454	if (br && (argc > -1)) {
455	    argc++;
456	} else bail_out(c);
457	break;
458    case 'm':
459	/* Change char attributes */
460	if (br) {
461	    switch (argc) {
462	    case -1:
463		ME();
464		break;
465	    case 0:
466		if (fg)
467		    AF();
468		else
469		    AB();
470		break;
471	    default:
472		bail_out(c);
473	    }
474	} else bail_out(c);
475	break;
476    default:
477	if (isdigit(c)) {
478	    /* Carefully collect numeric arguments */
479	    /* XXX this is ugly. */
480	    if (br) {
481	        if (argc == -1) {
482	     	    argc = 0;
483		    args[argc] = 0;
484		    dig = 0;
485		    /* in case we're in error... */
486		    if (c == '3') {
487			fg = 1;
488			return;
489		    }
490		    if (c == '4') {
491			bg = 1;
492			return;
493		    }
494	     	    args[argc] = (int)(c - '0');
495		    dig = 1;
496	     	    args[argc + 1] = 0;
497	    	} else {
498		    args[argc] = args[argc]*10 + (int)(c - '0');
499		    if (argc == 0)
500			dig++;
501	    	}
502	    } else bail_out(c);
503	} else bail_out(c);
504	break;
505    }
506}
507#endif
508
509static void
510vidc_putchar(int c)
511{
512#ifdef TERM_EMU
513    vidc_term_emu(c);
514#else
515    vidc_rawputchar(c);
516#endif
517}
518
519static int
520vidc_getchar(void)
521{
522
523    if (vidc_ischar()) {
524	v86.ctl = 0;
525	v86.addr = 0x16;
526	v86.eax = 0x0;
527	v86int();
528	return (v86.eax & 0xff);
529    } else {
530	return (-1);
531    }
532}
533
534static int
535vidc_ischar(void)
536{
537
538    v86.ctl = V86_FLAGS;
539    v86.addr = 0x16;
540    v86.eax = 0x100;
541    v86int();
542    return (!(v86.efl & PSL_Z));
543}
544
545#if KEYBOARD_PROBE
546
547#define PROBE_MAXRETRY	5
548#define PROBE_MAXWAIT	400
549#define IO_DUMMY	0x84
550#define IO_KBD		0x060		/* 8042 Keyboard */
551
552/* selected defines from kbdio.h */
553#define KBD_STATUS_PORT 	4	/* status port, read */
554#define KBD_DATA_PORT		0	/* data port, read/write
555					 * also used as keyboard command
556					 * and mouse command port
557					 */
558#define KBDC_ECHO		0x00ee
559#define KBDS_ANY_BUFFER_FULL	0x0001
560#define KBDS_INPUT_BUFFER_FULL	0x0002
561#define KBD_ECHO		0x00ee
562
563/* 7 microsec delay necessary for some keyboard controllers */
564static void
565delay7(void)
566{
567    /*
568     * I know this is broken, but no timer is available yet at this stage...
569     * See also comments in `delay1ms()'.
570     */
571    inb(IO_DUMMY); inb(IO_DUMMY);
572    inb(IO_DUMMY); inb(IO_DUMMY);
573    inb(IO_DUMMY); inb(IO_DUMMY);
574}
575
576/*
577 * This routine uses an inb to an unused port, the time to execute that
578 * inb is approximately 1.25uS.  This value is pretty constant across
579 * all CPU's and all buses, with the exception of some PCI implentations
580 * that do not forward this I/O address to the ISA bus as they know it
581 * is not a valid ISA bus address, those machines execute this inb in
582 * 60 nS :-(.
583 *
584 */
585static void
586delay1ms(void)
587{
588    int i = 800;
589    while (--i >= 0)
590	(void)inb(0x84);
591}
592
593/*
594 * We use the presence/absence of a keyboard to determine whether the internal
595 * console can be used for input.
596 *
597 * Perform a simple test on the keyboard; issue the ECHO command and see
598 * if the right answer is returned. We don't do anything as drastic as
599 * full keyboard reset; it will be too troublesome and take too much time.
600 */
601static int
602probe_keyboard(void)
603{
604    int retry = PROBE_MAXRETRY;
605    int wait;
606    int i;
607
608    while (--retry >= 0) {
609	/* flush any noise */
610	while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) {
611	    delay7();
612	    inb(IO_KBD + KBD_DATA_PORT);
613	    delay1ms();
614	}
615
616	/* wait until the controller can accept a command */
617	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
618	    if (((i = inb(IO_KBD + KBD_STATUS_PORT))
619                & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0)
620		break;
621	    if (i & KBDS_ANY_BUFFER_FULL) {
622		delay7();
623	        inb(IO_KBD + KBD_DATA_PORT);
624	    }
625	    delay1ms();
626	}
627	if (wait <= 0)
628	    continue;
629
630	/* send the ECHO command */
631	outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO);
632
633	/* wait for a response */
634	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
635	     if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)
636		 break;
637	     delay1ms();
638	}
639	if (wait <= 0)
640	    continue;
641
642	delay7();
643	i = inb(IO_KBD + KBD_DATA_PORT);
644#ifdef PROBE_KBD_BEBUG
645        printf("probe_keyboard: got 0x%x.\n", i);
646#endif
647	if (i == KBD_ECHO) {
648	    /* got the right answer */
649	    return (0);
650	}
651    }
652
653    return (1);
654}
655#endif /* KEYBOARD_PROBE */
656