vidconsole.c revision 43561
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 *	$Id: vidconsole.c,v 1.11 1999/01/04 18:45:08 peter Exp $
30 */
31
32#include <stand.h>
33#include <bootstrap.h>
34#include <btxv86.h>
35#include <machine/psl.h>
36#include "libi386.h"
37#ifdef PC98
38#include <machine/cpufunc.h>
39#endif
40
41#if KEYBOARD_PROBE
42#include <machine/cpufunc.h>
43
44static int	probe_keyboard(void);
45#endif
46static void	vidc_probe(struct console *cp);
47static int	vidc_init(int arg);
48static void	vidc_putchar(int c);
49static int	vidc_getchar(void);
50static int	vidc_ischar(void);
51
52static int	vidc_started;
53
54#ifdef TERM_EMU
55void		end_term();
56void		bail_out(int c);
57void		vidc_term_emu(int c);
58void		get_pos(void);
59void		curs_move(int x, int y);
60void		write_char(int c, int fg, int bg);
61void		scroll_up(int rows, int fg, int bg);
62void		AB(void);
63void		AF(void);
64void		CD(void);
65void		CM(void);
66void		HO(void);
67void		ME(void);
68
69static int	args[2],argc,br;
70static int	fg,bg,dig;
71static int	fg_c,bg_c,curx,cury;
72static int	esc;
73#endif
74
75#ifdef PC98
76static unsigned short *crtat, *Crtat;
77static int row = 25, col = 80;
78#ifdef TERM_EMU
79unsigned int at2pc98(unsigned int fg_at, unsigned int bg_at);
80#endif
81#endif
82
83struct console vidconsole = {
84    "vidconsole",
85    "internal video/keyboard",
86    0,
87    vidc_probe,
88    vidc_init,
89    vidc_putchar,
90    vidc_getchar,
91    vidc_ischar
92};
93
94static void
95vidc_probe(struct console *cp)
96{
97
98    /* look for a keyboard */
99#if KEYBOARD_PROBE
100    if (probe_keyboard())
101#endif
102    {
103
104	cp->c_flags |= C_PRESENTIN;
105    }
106
107    /* XXX for now, always assume we can do BIOS screen output */
108    cp->c_flags |= C_PRESENTOUT;
109}
110
111static int
112vidc_init(int arg)
113{
114    int		i;
115#ifdef PC98
116    int		hw_cursor;
117#endif
118
119    if (vidc_started && arg == 0)
120	return;
121    vidc_started = 1;
122#ifdef PC98
123    Crtat = (unsigned short *)PTOV(0xA0000);
124    while((inb(0x60) & 0x04) == 0);
125    outb(0x62, 0xe0);
126    while((inb(0x60) & 0x01) == 0);
127    hw_cursor = inb(0x62);
128    hw_cursor |= (inb(0x62) << 8);
129    inb(0x62);
130    inb(0x62);
131    inb(0x62);
132    crtat = Crtat + hw_cursor;
133#endif
134#ifdef TERM_EMU
135    /* Init terminal emulator */
136    end_term();
137    get_pos();
138    curs_move(curx,cury);
139    fg_c=7;
140    bg_c=0;
141#endif
142    for(i = 0; i < 10 && vidc_ischar(); i++)
143	  (void)vidc_getchar();
144    return(0);	/* XXX reinit? */
145}
146
147static void
148vidc_biosputchar(int c)
149{
150#ifdef PC98
151    unsigned short *cp;
152    int i, pos;
153
154#ifdef TERM_EMU
155    *crtat = (c == 0x5c ? 0xfc : c);
156    *(crtat + 0x1000) = at2pc98(fg, bg);
157#else
158    switch(c) {
159    case '\b':
160        crtat--;
161	break;
162    case '\r':
163        crtat -= (crtat - Crtat) % col;
164	break;
165    case '\n':
166        crtat += col;
167	break;
168    default:
169        *crtat = (c == 0x5c ? 0xfc : c);
170	*(crtat++ + 0x1000) = 0xe1;
171	break;
172    }
173
174    if (crtat >= Crtat + col * row) {
175        cp = Crtat;
176	for (i = 1; i < row; i++) {
177	    bcopy((void *)(cp+col), (void *)cp, col*2);
178	    cp += col;
179	}
180	for (i = 0; i < col; i++) {
181	    *cp++ = ' ';
182	}
183	crtat -= col;
184    }
185    pos = crtat - Crtat;
186    while((inb(0x60) & 0x04) == 0) {}
187    outb(0x62, 0x49);
188    outb(0x60, pos & 0xff);
189    outb(0x60, pos >> 8);
190#endif
191#else
192    v86.ctl = 0;
193    v86.addr = 0x10;
194    v86.eax = 0xe00 | (c & 0xff);
195    v86.ebx = 0x7;
196    v86int();
197#endif
198}
199
200static void
201vidc_rawputchar(int c)
202{
203    int		i;
204
205    if(c == '\t')
206	/* lame tab expansion */
207	for (i = 0; i < 8; i++)
208	    vidc_rawputchar(' ');
209    else {
210#ifndef TERM_EMU
211        vidc_biosputchar(c);
212#else
213	/* Emulate AH=0eh (teletype output) */
214	switch(c) {
215	case '\a':
216		vidc_biosputchar(c);
217		return;
218	case '\r':
219		curx=0;
220		curs_move(curx,cury);
221		return;
222	case '\n':
223		cury++;
224		if(cury>24) {
225			scroll_up(1,fg_c,bg_c);
226			cury--;
227		} else {
228			curs_move(curx,cury);
229		}
230		return;
231	case '\b':
232		if(curx>0) {
233			curx--;
234			curs_move(curx,cury);
235			/* write_char(' ',fg_c,bg_c); XXX destructive(!) */
236			return;
237		}
238		return;
239	default:
240		write_char(c,fg_c,bg_c);
241		curx++;
242		if(curx>79) {
243			curx=0;
244			cury++;
245		}
246		if(cury>24) {
247			curx=0;
248			scroll_up(1,fg_c,bg_c);
249			cury--;
250		}
251	}
252	curs_move(curx,cury);
253#endif
254    }
255}
256
257#ifdef TERM_EMU
258
259/* Get cursor position on the screen. Result is in edx. Sets
260 * curx and cury appropriately.
261 */
262void
263get_pos(void)
264{
265#ifdef PC98
266    int pos = crtat - Crtat;
267    curx = pos % col;
268    cury = pos / col;
269#else
270    v86.ctl = 0;
271    v86.addr = 0x10;
272    v86.eax = 0x0300;
273    v86.ebx = 0x0;
274    v86int();
275    curx=v86.edx & 0x00ff;
276    cury=(v86.edx & 0xff00)>>8;
277#endif
278}
279
280/* Move cursor to x rows and y cols (0-based). */
281void
282curs_move(int x, int y)
283{
284#ifdef PC98
285    int pos;
286    pos = x + y*col;
287    crtat = Crtat + pos;
288    pos = crtat - Crtat;
289    while((inb(0x60) & 0x04) == 0) {}
290    outb(0x62, 0x49);
291    outb(0x60, pos & 0xff);
292    outb(0x60, pos >> 8);
293    curx=x;
294    cury=y;
295#define isvisible(c)	(((c)>32) && ((c)<255))
296    if(!isvisible(*crtat & 0x00ff)) {
297	write_char(' ',fg_c,bg_c);
298    }
299#else
300    v86.ctl = 0;
301    v86.addr = 0x10;
302    v86.eax = 0x0200;
303    v86.ebx = 0x0;
304    v86.edx = ((0x00ff & y)<<8)+(0x00ff & x);
305    v86int();
306    curx=x;
307    cury=y;
308    /* If there is ctrl char at this position, cursor would be invisible.
309     * Make it a space instead.
310     */
311    v86.ctl=0;
312    v86.addr = 0x10;
313    v86.eax = 0x0800;
314    v86.ebx= 0x0;
315    v86int();
316#define isvisible(c)	(((c)>32) && ((c)<255))
317    if(!isvisible(v86.eax & 0x00ff)) {
318	write_char(' ',fg_c,bg_c);
319    }
320#endif
321}
322
323/* Scroll up the whole window by a number of rows. If rows==0,
324 * clear the window. fg and bg are attributes for the new lines
325 * inserted in the window.
326 */
327void
328scroll_up(int rows, int fg, int bg)
329{
330#ifdef PC98
331        unsigned short *cp;
332	int i;
333
334	if(rows==0) rows=25;
335	cp = Crtat;
336	for (i = rows ; i < row; i++) {
337	    bcopy((void *)(cp+col), (void *)cp, col*2);
338	    cp += col;
339	}
340	for (i = 0; i < col; i++) {
341	    *(cp + 0x1000) = at2pc98(fg, bg);
342	    *cp++ = ' ';
343	}
344#else
345	if(rows==0) rows=25;
346	v86.ctl = 0;
347	v86.addr = 0x10;
348	v86.eax = 0x0600+(0x00ff & rows);
349	v86.ebx = (bg<<12)+(fg<<8);
350	v86.ecx = 0x0;
351	v86.edx = 0x184f;
352	v86int();
353#endif
354}
355
356/* Write character and attribute at cursor position. */
357void
358write_char(int c, int fg, int bg)
359{
360#ifdef PC98
361        *crtat = c;
362	*(crtat + 0x1000) = at2pc98(fg, bg);
363#else
364	v86.ctl=0;
365    	v86.addr = 0x10;
366    	v86.eax = 0x0900+(0x00ff & c);
367	v86.ebx = (bg<<4)+fg;
368    	v86.ecx = 0x1;
369    	v86int();
370#endif
371}
372
373/* Calculate power of 10 */
374int
375pow10(int i)
376{
377	int res=1;
378
379	while(i-->0) {
380		res*=10;
381	}
382	return res;
383}
384
385/**************************************************************/
386/*
387 * Screen manipulation functions. They use accumulated data in
388 * args[] and argc variables.
389 *
390 */
391
392/* Set background color */
393void
394AB(void){
395	bg_c=args[0];
396	end_term();
397}
398
399/* Set foreground color */
400void
401AF(void)
402{
403	fg_c=args[0];
404	end_term();
405}
406
407/* Clear display from current position to end of screen */
408void
409CD(void)
410{
411    get_pos();
412#ifdef PC98
413    for(;crtat <= Crtat + col*row; crtat++){
414	*crtat = ' ';
415	*(crtat + 0x1000) = at2pc98(fg_c, bg_c);
416    }
417#else
418    v86.ctl = 0;
419    v86.addr = 0x10;
420    v86.eax = 0x0600;
421    v86.ebx = (bg_c<<4)+fg_c;
422    v86.ecx = v86.edx;
423    v86.edx = 0x184f;
424    v86int();
425#endif
426    curx=0;
427    curs_move(curx,cury);
428    end_term();
429}
430
431/* Absolute cursor move to args[0] rows and args[1] columns
432 * (the coordinates are 1-based).
433 */
434void
435CM(void)
436{
437    if(args[0]>0) args[0]--;
438    if(args[1]>0) args[1]--;
439    curs_move(args[1],args[0]);
440    end_term();
441}
442
443/* Home cursor (left top corner) */
444void
445HO(void)
446{
447	argc=1;
448	args[0]=args[1]=1;
449	CM();
450}
451
452/* Exit attribute mode (reset fore/back-ground colors to defaults) */
453void
454ME(void)
455{
456	fg_c=7;
457	bg_c=0;
458	end_term();
459}
460
461/* Clear internal state of the terminal emulation code */
462void
463end_term(void)
464{
465	esc=0;
466	argc=-1;
467	fg=bg=br=0;
468	args[0]=args[1]=0;
469	dig=0;
470}
471
472/* Gracefully exit ESC-sequence processing in case of misunderstanding */
473void
474bail_out(int c)
475{
476	char buf[6],*ch;
477
478	if(esc) vidc_rawputchar('\033');
479	if(br) vidc_rawputchar('[');
480	if(argc>-1) {
481		sprintf(buf,"%d",args[0]);
482		ch=buf;
483		while(*ch) vidc_rawputchar(*ch++);
484
485		if(argc>0) {
486			vidc_rawputchar(';');
487			sprintf(buf,"%d",args[1]);
488			ch=buf;
489			while(*ch) vidc_rawputchar(*ch++);
490		}
491	}
492	vidc_rawputchar(c);
493	end_term();
494}
495
496/* Emulate basic capabilities of cons25 terminal */
497void
498vidc_term_emu(int c)
499{
500
501    if(!esc) {
502	if(c=='\033') {
503	    esc=1;
504	} else {
505	    vidc_rawputchar(c);
506	}
507	return;
508    }
509
510    /* Do ESC sequences processing */
511    switch(c) {
512    case '\033':
513	/* ESC in ESC sequence - error */
514	bail_out(c);
515	break;
516    case '[':
517	/* Check if it's first char after ESC */
518        if(argc<0) {
519            br=1;
520        } else {
521	    bail_out(c);
522        }
523	break;
524    case 'H':
525	/* Emulate \E[H (cursor home) and
526	 * \E%d;%dH (cursor absolute move) */
527	if(br) {
528	    switch(argc) {
529	    case -1:
530		HO();
531		break;
532	    case 1:
533		if(fg) args[0]+=pow10(dig)*3;
534		if(bg) args[0]+=pow10(dig)*4;
535		CM();
536		break;
537	    default:
538		bail_out(c);
539	    }
540	} else bail_out(c);
541	break;
542    case 'J':
543	/* Emulate \EJ (clear to end of screen) */
544	if(br && argc<0) {
545	    CD();
546	} else bail_out(c);
547	break;
548    case ';':
549	/* perhaps args separator */
550	if(br && (argc>-1)) {
551	    argc++;
552	} else bail_out(c);
553	break;
554    case 'm':
555	/* Change char attributes */
556	if(br) {
557	    switch(argc) {
558	    case -1:
559		ME();
560		break;
561	    case 0:
562		if(fg) AF();
563		else AB();
564		break;
565	    default:
566		bail_out(c);
567	    }
568	} else bail_out(c);
569	break;
570    default:
571	if(isdigit(c)) {
572	    /* Carefully collect numeric arguments */
573	    /* XXX this is ugly. */
574	    if(br) {
575	        if(argc==-1) {
576	     	    argc=0;
577		    args[argc]=0;
578		    dig=0;
579		    /* in case we're in error... */
580		    if(c=='3') {
581			fg=1;
582			return;
583		    }
584		    if(c=='4') {
585			bg=1;
586			return;
587		    }
588	     	    args[argc]=(int)(c-'0');
589		    dig=1;
590	     	    args[argc+1]=0;
591	    	} else {
592		    args[argc]=args[argc]*10+(int)(c-'0');
593		    if(argc==0) dig++;
594	    	}
595	    } else bail_out(c);
596	} else bail_out(c);
597	break;
598    }
599}
600#endif
601
602static void
603vidc_putchar(int c)
604{
605#ifdef TERM_EMU
606    vidc_term_emu(c);
607#else
608    vidc_rawputchar(c);
609#endif
610}
611
612static int
613vidc_getchar(void)
614{
615    if (vidc_ischar()) {
616	v86.ctl = 0;
617#ifdef PC98
618	v86.addr = 0x18;
619#else
620	v86.addr = 0x16;
621#endif
622	v86.eax = 0x0;
623	v86int();
624	return(v86.eax & 0xff);
625    } else {
626	return(-1);
627    }
628}
629
630static int
631vidc_ischar(void)
632{
633#ifdef PC98
634    v86.ctl = 0;
635    v86.addr = 0x18;
636    v86.eax = 0x100;
637    v86int();
638    return((v86.ebx >> 8) & 0x1);
639#else
640    v86.ctl = V86_FLAGS;
641    v86.addr = 0x16;
642    v86.eax = 0x100;
643    v86int();
644    return(!(v86.efl & PSL_Z));
645#endif
646}
647
648#if KEYBOARD_PROBE
649#ifdef PC98
650static int
651probe_keyboard(void)
652{
653    return (*(u_char *)PTOV(0xA1481) & 0x48);
654}
655#else   /* PC98 */
656#define PROBE_MAXRETRY	5
657#define PROBE_MAXWAIT	400
658#define IO_DUMMY	0x84
659#define IO_KBD		0x060		/* 8042 Keyboard */
660
661/* selected defines from kbdio.h */
662#define KBD_STATUS_PORT 	4	/* status port, read */
663#define KBD_DATA_PORT		0	/* data port, read/write
664					 * also used as keyboard command
665					 * and mouse command port
666					 */
667#define KBDC_ECHO		0x00ee
668#define KBDS_ANY_BUFFER_FULL	0x0001
669#define KBDS_INPUT_BUFFER_FULL	0x0002
670#define KBD_ECHO		0x00ee
671
672/* 7 microsec delay necessary for some keyboard controllers */
673static void
674delay7(void)
675{
676    /*
677     * I know this is broken, but no timer is available yet at this stage...
678     * See also comments in `delay1ms()'.
679     */
680    inb(IO_DUMMY); inb(IO_DUMMY);
681    inb(IO_DUMMY); inb(IO_DUMMY);
682    inb(IO_DUMMY); inb(IO_DUMMY);
683}
684
685/*
686 * This routine uses an inb to an unused port, the time to execute that
687 * inb is approximately 1.25uS.  This value is pretty constant across
688 * all CPU's and all buses, with the exception of some PCI implentations
689 * that do not forward this I/O adress to the ISA bus as they know it
690 * is not a valid ISA bus address, those machines execute this inb in
691 * 60 nS :-(.
692 *
693 */
694static void
695delay1ms(void)
696{
697    int i = 800;
698    while (--i >= 0)
699	(void)inb(0x84);
700}
701
702/*
703 * We use the presence/absence of a keyboard to determine whether the internal
704 * console can be used for input.
705 *
706 * Perform a simple test on the keyboard; issue the ECHO command and see
707 * if the right answer is returned. We don't do anything as drastic as
708 * full keyboard reset; it will be too troublesome and take too much time.
709 */
710static int
711probe_keyboard(void)
712{
713    int retry = PROBE_MAXRETRY;
714    int wait;
715    int i;
716
717    while (--retry >= 0) {
718	/* flush any noise */
719	while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) {
720	    delay7();
721	    inb(IO_KBD + KBD_DATA_PORT);
722	    delay1ms();
723	}
724
725	/* wait until the controller can accept a command */
726	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
727	    if (((i = inb(IO_KBD + KBD_STATUS_PORT))
728                & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0)
729		break;
730	    if (i & KBDS_ANY_BUFFER_FULL) {
731		delay7();
732	        inb(IO_KBD + KBD_DATA_PORT);
733	    }
734	    delay1ms();
735	}
736	if (wait <= 0)
737	    continue;
738
739	/* send the ECHO command */
740	outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO);
741
742	/* wait for a response */
743	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
744	     if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)
745		 break;
746	     delay1ms();
747	}
748	if (wait <= 0)
749	    continue;
750
751	delay7();
752	i = inb(IO_KBD + KBD_DATA_PORT);
753#ifdef PROBE_KBD_BEBUG
754        printf("probe_keyboard: got 0x%x.\n", i);
755#endif
756	if (i == KBD_ECHO) {
757	    /* got the right answer */
758	    return (0);
759	}
760    }
761
762    return (1);
763}
764#endif
765#endif /* KEYBOARD_PROBE */
766
767#ifdef TERM_EMU
768#ifdef PC98
769static u_char	ibmpc_to_pc98[16] =
770 { 0x01,0x21,0x81,0xa1,0x41,0x61,0xc1,0xe1, 0x09,0x29,0x89,0xa9,0x49,0x69,0xc9,0xe9 };
771static u_char	ibmpc_to_pc98rev[16] =
772 { 0x05,0x25,0x85,0xa5,0x45,0x65,0xc5,0xe5, 0x0d,0x2d,0x8d,0xad,0x4d,0x6d,0xcd,0xed };
773
774unsigned int
775at2pc98(unsigned int fg_at, unsigned int bg_at)
776{
777    unsigned int at;
778
779    if (bg_at) {
780	if (bg_at & 0x80) {
781	    if (bg_at & 0x70) {
782		/* reverse & blink */
783		at = ibmpc_to_pc98rev[bg_at >> 4] | 0x02;
784	    } else {
785		/* normal & blink */
786		at = ibmpc_to_pc98[fg_at] | 0x02;
787	    }
788	} else {
789	    /* reverse */
790	    at = ibmpc_to_pc98rev[bg_at >> 4];
791	}
792    } else {
793	/* normal */
794	at = ibmpc_to_pc98[fg_at];
795    }
796    at |= ((fg_at|bg_at) << 8);
797    return (at);
798}
799#endif
800#endif
801