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