vidconsole.c revision 42291
121259Swollman/*
221259Swollman * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
321259Swollman * Copyright (c) 1997 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp)
421259Swollman * All rights reserved.
521259Swollman *
621259Swollman * Redistribution and use in source and binary forms, with or without
721259Swollman * modification, are permitted provided that the following conditions
821259Swollman * are met:
921259Swollman * 1. Redistributions of source code must retain the above copyright
1021259Swollman *    notice, this list of conditions and the following disclaimer.
1121259Swollman * 2. Redistributions in binary form must reproduce the above copyright
1221259Swollman *    notice, this list of conditions and the following disclaimer in the
1321259Swollman *    documentation and/or other materials provided with the distribution.
1421259Swollman *
1521259Swollman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1621259Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1721259Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1821259Swollman * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1921259Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2021259Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2121259Swollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2221259Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2321259Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2421259Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2521259Swollman * SUCH DAMAGE.
2621259Swollman *
2721259Swollman * 	From Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp
2821259Swollman *
2921259Swollman *	$Id: vidconsole.c,v 1.10 1998/12/31 13:44:04 abial Exp $
3021259Swollman */
3121259Swollman
3221259Swollman#include <stand.h>
3321259Swollman#include <bootstrap.h>
3450477Speter#include <btxv86.h>
3521259Swollman#include <machine/psl.h>
3621259Swollman#include "libi386.h"
3721259Swollman
3821259Swollman#if KEYBOARD_PROBE
3921259Swollman#include <machine/cpufunc.h>
4021259Swollman
4121259Swollmanstatic int	probe_keyboard(void);
4221259Swollman#endif
4321259Swollmanstatic void	vidc_probe(struct console *cp);
4421259Swollmanstatic int	vidc_init(int arg);
4521259Swollmanstatic void	vidc_putchar(int c);
4621259Swollmanstatic int	vidc_getchar(void);
4721259Swollmanstatic int	vidc_ischar(void);
4821259Swollman
4921259Swollmanstatic int	vidc_started;
5021259Swollman
5121259Swollman#ifdef TERM_EMU
5221259Swollmanvoid		end_term();
5321259Swollmanvoid		bail_out(int c);
5421259Swollmanvoid		vidc_term_emu(int c);
5521259Swollmanvoid		get_pos(void);
5621259Swollmanvoid		curs_move(int x, int y);
5721259Swollmanvoid		write_char(int c, int fg, int bg);
5821259Swollmanvoid		scroll_up(int rows, int fg, int bg);
5921259Swollmanvoid		AB(void);
6021259Swollmanvoid		AF(void);
6121259Swollmanvoid		CD(void);
6221259Swollmanvoid		CM(void);
6321259Swollmanvoid		HO(void);
6421259Swollmanvoid		ME(void);
6521259Swollman
6621259Swollmanstatic int	args[2],argc,br;
6721259Swollmanstatic int	fg,bg,dig;
6821259Swollmanstatic int	fg_c,bg_c,curx,cury;
6921259Swollmanstatic int	esc;
7021259Swollman#endif
7121259Swollman
7221259Swollman
7321259Swollmanstruct console vidconsole = {
7421259Swollman    "vidconsole",
7521259Swollman    "internal video/keyboard",
7621259Swollman    0,
7721259Swollman    vidc_probe,
7869224Sjlemon    vidc_init,
7969152Sjlemon    vidc_putchar,
8069224Sjlemon    vidc_getchar,
8169224Sjlemon    vidc_ischar
8269152Sjlemon};
8360938Sjake
8460938Sjakestatic void
8560938Sjakevidc_probe(struct console *cp)
8672084Sphk{
8721259Swollman
8821259Swollman    /* look for a keyboard */
8921259Swollman#if KEYBOARD_PROBE
9021259Swollman    if (probe_keyboard())
9121259Swollman#endif
9221259Swollman    {
9321259Swollman
9421259Swollman	cp->c_flags |= C_PRESENTIN;
9521259Swollman    }
9621259Swollman
9769152Sjlemon    /* XXX for now, always assume we can do BIOS screen output */
9821259Swollman    cp->c_flags |= C_PRESENTOUT;
9921259Swollman}
10021259Swollman
10121259Swollmanstatic int
10221259Swollmanvidc_init(int arg)
10321259Swollman{
10421259Swollman    int		i;
10521259Swollman
10621259Swollman    if (vidc_started && arg == 0)
10721259Swollman	return;
10860938Sjake    vidc_started = 1;
10921259Swollman#ifdef TERM_EMU
11021259Swollman    /* Init terminal emulator */
11121259Swollman    end_term();
11221259Swollman    get_pos();
11321259Swollman    curs_move(curx,cury);
11421259Swollman    fg_c=7;
11521259Swollman    bg_c=0;
11669152Sjlemon#endif
11721259Swollman    for(i = 0; i < 10 && vidc_ischar(); i++)
11821259Swollman	  (void)vidc_getchar();
11921259Swollman    return(0);	/* XXX reinit? */
12021259Swollman}
12121404Swollman
12221404Swollmanstatic void
12321259Swollmanvidc_biosputchar(int c)
12421259Swollman{
12521259Swollman    v86.ctl = 0;
12621259Swollman    v86.addr = 0x10;
12721259Swollman    v86.eax = 0xe00 | (c & 0xff);
12821259Swollman    v86.ebx = 0x7;
12921259Swollman    v86int();
13021259Swollman}
13121259Swollman
13236735Sdfrstatic void
13321259Swollmanvidc_rawputchar(int c)
13421259Swollman{
13521259Swollman    int		i;
13621259Swollman
13721259Swollman    if(c == '\t')
13821259Swollman	/* lame tab expansion */
13921259Swollman	for (i = 0; i < 8; i++)
14021259Swollman	    vidc_rawputchar(' ');
14121259Swollman    else {
14221259Swollman#ifndef TERM_EMU
14321259Swollman        vidc_biosputchar(c);
14421259Swollman#else
14521404Swollman	/* Emulate AH=0eh (teletype output) */
14621404Swollman	switch(c) {
14721259Swollman	case '\a':
14821259Swollman		vidc_biosputchar(c);
14952904Sshin		return;
15021259Swollman	case '\r':
15169152Sjlemon		curx=0;
15221259Swollman		curs_move(curx,cury);
15321259Swollman		return;
15421259Swollman	case '\n':
15521259Swollman		cury++;
15621259Swollman		if(cury>24) {
15721259Swollman			scroll_up(1,fg_c,bg_c);
15821259Swollman			cury--;
15921259Swollman		} else {
16021259Swollman			curs_move(curx,cury);
16158698Sjlemon		}
16221259Swollman		return;
16321259Swollman	case '\b':
16421259Swollman		if(curx>0) {
16521259Swollman			curx--;
16621259Swollman			curs_move(curx,cury);
16721259Swollman			/* write_char(' ',fg_c,bg_c); XXX destructive(!) */
16821259Swollman			return;
16921259Swollman		}
17021259Swollman		return;
17121259Swollman	default:
17221259Swollman		write_char(c,fg_c,bg_c);
17321259Swollman		curx++;
17421259Swollman		if(curx>79) {
17521259Swollman			curx=0;
17621259Swollman			cury++;
17721259Swollman		}
17853541Sshin		if(cury>24) {
17953541Sshin			curx=0;
18053541Sshin			scroll_up(1,fg_c,bg_c);
18153541Sshin			cury--;
18221259Swollman		}
18321259Swollman	}
18421259Swollman	curs_move(curx,cury);
18521259Swollman#endif
18621259Swollman    }
18721259Swollman}
18821259Swollman
18921259Swollman#ifdef TERM_EMU
19021259Swollman
19121259Swollman/* Get cursor position on the screen. Result is in edx. Sets
19221259Swollman * curx and cury appropriately.
19321259Swollman */
19469152Sjlemonvoid
19569152Sjlemonget_pos(void)
19669152Sjlemon{
19769152Sjlemon    v86.ctl = 0;
19869152Sjlemon    v86.addr = 0x10;
19921259Swollman    v86.eax = 0x0300;
20069152Sjlemon    v86.ebx = 0x0;
20169152Sjlemon    v86int();
20269152Sjlemon    curx=v86.edx & 0x00ff;
20369152Sjlemon    cury=(v86.edx & 0xff00)>>8;
20469152Sjlemon}
20569152Sjlemon
20669152Sjlemon/* Move cursor to x rows and y cols (0-based). */
20769152Sjlemonvoid
20869152Sjlemoncurs_move(int x, int y)
20969152Sjlemon{
21069152Sjlemon    v86.ctl = 0;
21169152Sjlemon    v86.addr = 0x10;
21269152Sjlemon    v86.eax = 0x0200;
21369152Sjlemon    v86.ebx = 0x0;
21469152Sjlemon    v86.edx = ((0x00ff & y)<<8)+(0x00ff & x);
21569152Sjlemon    v86int();
21669152Sjlemon    curx=x;
21769152Sjlemon    cury=y;
21869152Sjlemon    /* If there is ctrl char at this position, cursor would be invisible.
21969152Sjlemon     * Make it a space instead.
22069152Sjlemon     */
22169152Sjlemon    v86.ctl=0;
22269152Sjlemon    v86.addr = 0x10;
22369152Sjlemon    v86.eax = 0x0800;
22469152Sjlemon    v86.ebx= 0x0;
22569152Sjlemon    v86int();
22669152Sjlemon#define isvisible(c)	(((c)>32) && ((c)<255))
22769152Sjlemon    if(!isvisible(v86.eax & 0x00ff)) {
22869152Sjlemon	write_char(' ',fg_c,bg_c);
22969152Sjlemon    }
23069152Sjlemon}
23169152Sjlemon
23269152Sjlemon/* Scroll up the whole window by a number of rows. If rows==0,
23369152Sjlemon * clear the window. fg and bg are attributes for the new lines
23469152Sjlemon * inserted in the window.
23569152Sjlemon */
23669152Sjlemonvoid
23769152Sjlemonscroll_up(int rows, int fg, int bg)
23869152Sjlemon{
23969152Sjlemon	if(rows==0) rows=25;
24069152Sjlemon	v86.ctl = 0;
24169152Sjlemon	v86.addr = 0x10;
24269152Sjlemon	v86.eax = 0x0600+(0x00ff & rows);
24369152Sjlemon	v86.ebx = (bg<<12)+(fg<<8);
24469152Sjlemon	v86.ecx = 0x0;
24569152Sjlemon	v86.edx = 0x184f;
24669152Sjlemon	v86int();
24769152Sjlemon}
24869152Sjlemon
24969152Sjlemon/* Write character and attribute at cursor position. */
25069152Sjlemonvoid
25169152Sjlemonwrite_char(int c, int fg, int bg)
25269152Sjlemon{
25369152Sjlemon	v86.ctl=0;
25469152Sjlemon    	v86.addr = 0x10;
25569152Sjlemon    	v86.eax = 0x0900+(0x00ff & c);
25669152Sjlemon	v86.ebx = (bg<<4)+fg;
25769152Sjlemon    	v86.ecx = 0x1;
25855205Speter    	v86int();
25969152Sjlemon}
26069152Sjlemon
26121259Swollman/* Calculate power of 10 */
26235210Sbdeint
26369152Sjlemonpow10(int i)
26421259Swollman{
26569152Sjlemon	int res=1;
26621259Swollman
26769152Sjlemon	while(i-->0) {
26869152Sjlemon		res*=10;
26969152Sjlemon	}
27069152Sjlemon	return res;
27169152Sjlemon}
27269152Sjlemon
27369152Sjlemon/**************************************************************/
27469152Sjlemon/*
27569152Sjlemon * Screen manipulation functions. They use accumulated data in
27669152Sjlemon * args[] and argc variables.
27769152Sjlemon *
27869152Sjlemon */
27969152Sjlemon
28069152Sjlemon/* Set background color */
28169152Sjlemonvoid
28269152SjlemonAB(void){
28369152Sjlemon	bg_c=args[0];
28469152Sjlemon	end_term();
28569152Sjlemon}
28669152Sjlemon
28769152Sjlemon/* Set foreground color */
28869152Sjlemonvoid
28969152SjlemonAF(void)
29069152Sjlemon{
29169152Sjlemon	fg_c=args[0];
29221259Swollman	end_term();
29321259Swollman}
29449459Sbrian
29549459Sbrian/* Clear display from current position to end of screen */
29649459Sbrianvoid
29749459SbrianCD(void)
29849459Sbrian{
29949459Sbrian    get_pos();
30049459Sbrian    v86.ctl = 0;
30155205Speter    v86.addr = 0x10;
30221259Swollman    v86.eax = 0x0600;
30321259Swollman    v86.ebx = (bg_c<<4)+fg_c;
30421259Swollman    v86.ecx = v86.edx;
30521259Swollman    v86.edx = 0x184f;
30621259Swollman    v86int();
30721259Swollman    curx=0;
30821259Swollman    curs_move(curx,cury);
30921259Swollman    end_term();
31021259Swollman}
31121259Swollman
31221259Swollman/* Absolute cursor move to args[0] rows and args[1] columns
31321259Swollman * (the coordinates are 1-based).
31467334Sjoe */
31521259Swollmanvoid
31660938SjakeCM(void)
31721259Swollman{
31821259Swollman    if(args[0]>0) args[0]--;
31921259Swollman    if(args[1]>0) args[1]--;
32047254Spb    curs_move(args[1],args[0]);
32121259Swollman    end_term();
32221259Swollman}
32321259Swollman
32421259Swollman/* Home cursor (left top corner) */
32528845Sjulianvoid
32628845SjulianHO(void)
32728845Sjulian{
32821259Swollman	argc=1;
32921259Swollman	args[0]=args[1]=1;
33021259Swollman	CM();
33153541Sshin}
33253541Sshin
33353541Sshin/* Exit attribute mode (reset fore/back-ground colors to defaults) */
33421404Swollmanvoid
33552904SshinME(void)
33652904Sshin{
33752904Sshin	fg_c=7;
33853541Sshin	bg_c=0;
33952904Sshin	end_term();
34052904Sshin}
34152904Sshin
34252904Sshin/* Clear internal state of the terminal emulation code */
34360938Sjakevoid
34452904Sshinend_term(void)
34552904Sshin{
34652904Sshin	esc=0;
34752904Sshin	argc=-1;
34852904Sshin	fg=bg=br=0;
34921404Swollman	args[0]=args[1]=0;
35021404Swollman	dig=0;
35121404Swollman}
35221404Swollman
35321404Swollman/* Gracefully exit ESC-sequence processing in case of misunderstanding */
35421404Swollmanvoid
35572084Sphkbail_out(int c)
35621434Swollman{
35721434Swollman	char buf[6],*ch;
35821434Swollman
35921434Swollman	if(esc) vidc_rawputchar('\033');
36021434Swollman	if(br) vidc_rawputchar('[');
36121404Swollman	if(argc>-1) {
36221404Swollman		sprintf(buf,"%d",args[0]);
36355205Speter		ch=buf;
36421259Swollman		while(*ch) vidc_rawputchar(*ch++);
36546568Speter
36646568Speter		if(argc>0) {
36746568Speter			vidc_rawputchar(';');
36846568Speter			sprintf(buf,"%d",args[1]);
36946568Speter			ch=buf;
37046568Speter			while(*ch) vidc_rawputchar(*ch++);
37121259Swollman		}
37221259Swollman	}
37352904Sshin	vidc_rawputchar(c);
37421259Swollman	end_term();
37571791Speter}
37621259Swollman
37721259Swollman/* Emulate basic capabilities of cons25 terminal */
37821259Swollmanvoid
37963090Sarchievidc_term_emu(int c)
38063090Sarchie{
38121259Swollman
38262143Sarchie    if(!esc) {
38321259Swollman	if(c=='\033') {
38421259Swollman	    esc=1;
38562143Sarchie	} else {
38621259Swollman	    vidc_rawputchar(c);
38721259Swollman	}
38853541Sshin	return;
38921434Swollman    }
39021404Swollman
39121259Swollman    /* Do ESC sequences processing */
39221404Swollman    switch(c) {
39345720Speter    case '\033':
39421259Swollman	/* ESC in ESC sequence - error */
39541879Sphk	bail_out(c);
39664651Sarchie	break;
39741879Sphk    case '[':
39821259Swollman	/* Check if it's first char after ESC */
39921259Swollman        if(argc<0) {
40036735Sdfr            br=1;
40121259Swollman        } else {
40221259Swollman	    bail_out(c);
40352904Sshin        }
40421259Swollman	break;
40521259Swollman    case 'H':
40621259Swollman	/* Emulate \E[H (cursor home) and
40721259Swollman	 * \E%d;%dH (cursor absolute move) */
40821259Swollman	if(br) {
40921259Swollman	    switch(argc) {
41021259Swollman	    case -1:
41121259Swollman		HO();
41221259Swollman		break;
41321259Swollman	    case 1:
41421259Swollman		if(fg) args[0]+=pow10(dig)*3;
41521259Swollman		if(bg) args[0]+=pow10(dig)*4;
41621259Swollman		CM();
41721259Swollman		break;
41821259Swollman	    default:
41921259Swollman		bail_out(c);
42053541Sshin	    }
42121434Swollman	} else bail_out(c);
42260889Sarchie	break;
42321434Swollman    case 'J':
42455205Speter	/* Emulate \EJ (clear to end of screen) */
42521259Swollman	if(br && argc<0) {
42621259Swollman	    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