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