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