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