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