vidconsole.c revision 64187
1219820Sjeff/*
2219820Sjeff * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
3219820Sjeff * Copyright (c) 1997 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp)
4219820Sjeff * All rights reserved.
5270710Shselasky *
6219820Sjeff * Redistribution and use in source and binary forms, with or without
7219820Sjeff * modification, are permitted provided that the following conditions
8219820Sjeff * are met:
9219820Sjeff * 1. Redistributions of source code must retain the above copyright
10219820Sjeff *    notice, this list of conditions and the following disclaimer.
11219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
12219820Sjeff *    notice, this list of conditions and the following disclaimer in the
13219820Sjeff *    documentation and/or other materials provided with the distribution.
14219820Sjeff *
15219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16219820Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17219820Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18219820Sjeff * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19219820Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20219820Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21219820Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22219820Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23219820Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24219820Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25219820Sjeff * SUCH DAMAGE.
26219820Sjeff *
27219820Sjeff * 	From Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp
28219820Sjeff *
29219820Sjeff * $FreeBSD: head/sys/boot/i386/libi386/vidconsole.c 64187 2000-08-03 09:14:02Z jhb $
30219820Sjeff */
31219820Sjeff
32219820Sjeff#include <stand.h>
33219820Sjeff#include <bootstrap.h>
34219820Sjeff#include <btxv86.h>
35219820Sjeff#include <machine/psl.h>
36219820Sjeff#include "libi386.h"
37219820Sjeff
38219820Sjeff#if KEYBOARD_PROBE
39219820Sjeff#include <machine/cpufunc.h>
40219820Sjeff
41219820Sjeffstatic int	probe_keyboard(void);
42219820Sjeff#endif
43219820Sjeffstatic void	vidc_probe(struct console *cp);
44219820Sjeffstatic int	vidc_init(int arg);
45219820Sjeffstatic void	vidc_putchar(int c);
46219820Sjeffstatic int	vidc_getchar(void);
47219820Sjeffstatic int	vidc_ischar(void);
48219820Sjeff
49219820Sjeffstatic int	vidc_started;
50219820Sjeff
51219820Sjeff#ifdef TERM_EMU
52219820Sjeffvoid		end_term(void);
53219820Sjeffvoid		bail_out(int c);
54219820Sjeffvoid		vidc_term_emu(int c);
55219820Sjeffvoid		get_pos(void);
56219820Sjeffvoid		curs_move(int x, int y);
57219820Sjeffvoid		write_char(int c, int fg, int bg);
58219820Sjeffvoid		scroll_up(int rows, int fg, int bg);
59219820Sjeffint		pow10(int i);
60219820Sjeffvoid		AB(void);
61219820Sjeffvoid		AF(void);
62219820Sjeffvoid		CD(void);
63219820Sjeffvoid		CM(void);
64219820Sjeffvoid		HO(void);
65219820Sjeffvoid		ME(void);
66219820Sjeff
67219820Sjeffstatic int	args[2],argc,br;
68219820Sjeffstatic int	fg,bg,dig;
69219820Sjeffstatic int	fg_c,bg_c,curx,cury;
70219820Sjeffstatic int	esc;
71219820Sjeff#endif
72219820Sjeff
73219820Sjeff
74219820Sjeffstruct console vidconsole = {
75219820Sjeff    "vidconsole",
76219820Sjeff    "internal video/keyboard",
77219820Sjeff    0,
78219820Sjeff    vidc_probe,
79219820Sjeff    vidc_init,
80219820Sjeff    vidc_putchar,
81219820Sjeff    vidc_getchar,
82219820Sjeff    vidc_ischar
83219820Sjeff};
84219820Sjeff
85219820Sjeffstatic void
86219820Sjeffvidc_probe(struct console *cp)
87219820Sjeff{
88219820Sjeff
89219820Sjeff    /* look for a keyboard */
90219820Sjeff#if KEYBOARD_PROBE
91219820Sjeff    if (probe_keyboard())
92219820Sjeff#endif
93219820Sjeff    {
94219820Sjeff
95219820Sjeff	cp->c_flags |= C_PRESENTIN;
96219820Sjeff    }
97219820Sjeff
98219820Sjeff    /* XXX for now, always assume we can do BIOS screen output */
99219820Sjeff    cp->c_flags |= C_PRESENTOUT;
100219820Sjeff}
101219820Sjeff
102219820Sjeffstatic int
103219820Sjeffvidc_init(int arg)
104219820Sjeff{
105219820Sjeff    int		i;
106219820Sjeff
107219820Sjeff    if (vidc_started && arg == 0)
108219820Sjeff	return(0);
109219820Sjeff    vidc_started = 1;
110219820Sjeff#ifdef TERM_EMU
111219820Sjeff    /* Init terminal emulator */
112219820Sjeff    end_term();
113219820Sjeff    get_pos();
114    curs_move(curx,cury);
115    fg_c=7;
116    bg_c=0;
117#endif
118    for(i = 0; i < 10 && vidc_ischar(); i++)
119	  (void)vidc_getchar();
120    return(0);	/* XXX reinit? */
121}
122
123static void
124vidc_biosputchar(int c)
125{
126    v86.ctl = 0;
127    v86.addr = 0x10;
128    v86.eax = 0xe00 | (c & 0xff);
129    v86.ebx = 0x7;
130    v86int();
131}
132
133static void
134vidc_rawputchar(int c)
135{
136    int		i;
137
138    if(c == '\t')
139	/* lame tab expansion */
140	for (i = 0; i < 8; i++)
141	    vidc_rawputchar(' ');
142    else {
143#ifndef TERM_EMU
144        vidc_biosputchar(c);
145#else
146	/* Emulate AH=0eh (teletype output) */
147	switch(c) {
148	case '\a':
149		vidc_biosputchar(c);
150		return;
151	case '\r':
152		curx=0;
153		curs_move(curx,cury);
154		return;
155	case '\n':
156		cury++;
157		if(cury>24) {
158			scroll_up(1,fg_c,bg_c);
159			cury--;
160		} else {
161			curs_move(curx,cury);
162		}
163		return;
164	case '\b':
165		if(curx>0) {
166			curx--;
167			curs_move(curx,cury);
168			/* write_char(' ',fg_c,bg_c); XXX destructive(!) */
169			return;
170		}
171		return;
172	default:
173		write_char(c,fg_c,bg_c);
174		curx++;
175		if(curx>79) {
176			curx=0;
177			cury++;
178		}
179		if(cury>24) {
180			curx=0;
181			scroll_up(1,fg_c,bg_c);
182			cury--;
183		}
184	}
185	curs_move(curx,cury);
186#endif
187    }
188}
189
190#ifdef TERM_EMU
191
192/* Get cursor position on the screen. Result is in edx. Sets
193 * curx and cury appropriately.
194 */
195void
196get_pos(void)
197{
198    v86.ctl = 0;
199    v86.addr = 0x10;
200    v86.eax = 0x0300;
201    v86.ebx = 0x0;
202    v86int();
203    curx=v86.edx & 0x00ff;
204    cury=(v86.edx & 0xff00)>>8;
205}
206
207/* Move cursor to x rows and y cols (0-based). */
208void
209curs_move(int x, int y)
210{
211    v86.ctl = 0;
212    v86.addr = 0x10;
213    v86.eax = 0x0200;
214    v86.ebx = 0x0;
215    v86.edx = ((0x00ff & y)<<8)+(0x00ff & x);
216    v86int();
217    curx=x;
218    cury=y;
219    /* If there is ctrl char at this position, cursor would be invisible.
220     * Make it a space instead.
221     */
222    v86.ctl=0;
223    v86.addr = 0x10;
224    v86.eax = 0x0800;
225    v86.ebx= 0x0;
226    v86int();
227#define isvisible(c)	(((c)>32) && ((c)<255))
228    if(!isvisible(v86.eax & 0x00ff)) {
229	write_char(' ',fg_c,bg_c);
230    }
231}
232
233/* Scroll up the whole window by a number of rows. If rows==0,
234 * clear the window. fg and bg are attributes for the new lines
235 * inserted in the window.
236 */
237void
238scroll_up(int rows, int fgcol, int bgcol)
239{
240	if(rows==0) rows=25;
241	v86.ctl = 0;
242	v86.addr = 0x10;
243	v86.eax = 0x0600+(0x00ff & rows);
244	v86.ebx = (bgcol<<12)+(fgcol<<8);
245	v86.ecx = 0x0;
246	v86.edx = 0x184f;
247	v86int();
248}
249
250/* Write character and attribute at cursor position. */
251void
252write_char(int c, int fgcol, int bgcol)
253{
254	v86.ctl=0;
255    	v86.addr = 0x10;
256    	v86.eax = 0x0900+(0x00ff & c);
257	v86.ebx = (bgcol<<4)+fgcol;
258    	v86.ecx = 0x1;
259    	v86int();
260}
261
262/* Calculate power of 10 */
263int
264pow10(int i)
265{
266	int res=1;
267
268	while(i-->0) {
269		res*=10;
270	}
271	return res;
272}
273
274/**************************************************************/
275/*
276 * Screen manipulation functions. They use accumulated data in
277 * args[] and argc variables.
278 *
279 */
280
281/* Set background color */
282void
283AB(void){
284	bg_c=args[0];
285	end_term();
286}
287
288/* Set foreground color */
289void
290AF(void)
291{
292	fg_c=args[0];
293	end_term();
294}
295
296/* Clear display from current position to end of screen */
297void
298CD(void)
299{
300    get_pos();
301    v86.ctl = 0;
302    v86.addr = 0x10;
303    v86.eax = 0x0600;
304    v86.ebx = (bg_c<<4)+fg_c;
305    v86.ecx = v86.edx;
306    v86.edx = 0x184f;
307    v86int();
308    curx=0;
309    curs_move(curx,cury);
310    end_term();
311}
312
313/* Absolute cursor move to args[0] rows and args[1] columns
314 * (the coordinates are 1-based).
315 */
316void
317CM(void)
318{
319    if(args[0]>0) args[0]--;
320    if(args[1]>0) args[1]--;
321    curs_move(args[1],args[0]);
322    end_term();
323}
324
325/* Home cursor (left top corner) */
326void
327HO(void)
328{
329	argc=1;
330	args[0]=args[1]=1;
331	CM();
332}
333
334/* Exit attribute mode (reset fore/back-ground colors to defaults) */
335void
336ME(void)
337{
338	fg_c=7;
339	bg_c=0;
340	end_term();
341}
342
343/* Clear internal state of the terminal emulation code */
344void
345end_term(void)
346{
347	esc=0;
348	argc=-1;
349	fg=bg=br=0;
350	args[0]=args[1]=0;
351	dig=0;
352}
353
354/* Gracefully exit ESC-sequence processing in case of misunderstanding */
355void
356bail_out(int c)
357{
358	char buf[6],*ch;
359
360	if(esc) vidc_rawputchar('\033');
361	if(br) vidc_rawputchar('[');
362	if(argc>-1) {
363		sprintf(buf,"%d",args[0]);
364		ch=buf;
365		while(*ch) vidc_rawputchar(*ch++);
366
367		if(argc>0) {
368			vidc_rawputchar(';');
369			sprintf(buf,"%d",args[1]);
370			ch=buf;
371			while(*ch) vidc_rawputchar(*ch++);
372		}
373	}
374	vidc_rawputchar(c);
375	end_term();
376}
377
378/* Emulate basic capabilities of cons25 terminal */
379void
380vidc_term_emu(int c)
381{
382
383    if(!esc) {
384	if(c=='\033') {
385	    esc=1;
386	} else {
387	    vidc_rawputchar(c);
388	}
389	return;
390    }
391
392    /* Do ESC sequences processing */
393    switch(c) {
394    case '\033':
395	/* ESC in ESC sequence - error */
396	bail_out(c);
397	break;
398    case '[':
399	/* Check if it's first char after ESC */
400        if(argc<0) {
401            br=1;
402        } else {
403	    bail_out(c);
404        }
405	break;
406    case 'H':
407	/* Emulate \E[H (cursor home) and
408	 * \E%d;%dH (cursor absolute move) */
409	if(br) {
410	    switch(argc) {
411	    case -1:
412		HO();
413		break;
414	    case 1:
415		if(fg) args[0]+=pow10(dig)*3;
416		if(bg) args[0]+=pow10(dig)*4;
417		CM();
418		break;
419	    default:
420		bail_out(c);
421	    }
422	} else bail_out(c);
423	break;
424    case 'J':
425	/* Emulate \EJ (clear to end of screen) */
426	if(br && argc<0) {
427	    CD();
428	} else bail_out(c);
429	break;
430    case ';':
431	/* perhaps args separator */
432	if(br && (argc>-1)) {
433	    argc++;
434	} else bail_out(c);
435	break;
436    case 'm':
437	/* Change char attributes */
438	if(br) {
439	    switch(argc) {
440	    case -1:
441		ME();
442		break;
443	    case 0:
444		if(fg) AF();
445		else AB();
446		break;
447	    default:
448		bail_out(c);
449	    }
450	} else bail_out(c);
451	break;
452    default:
453	if(isdigit(c)) {
454	    /* Carefully collect numeric arguments */
455	    /* XXX this is ugly. */
456	    if(br) {
457	        if(argc==-1) {
458	     	    argc=0;
459		    args[argc]=0;
460		    dig=0;
461		    /* in case we're in error... */
462		    if(c=='3') {
463			fg=1;
464			return;
465		    }
466		    if(c=='4') {
467			bg=1;
468			return;
469		    }
470	     	    args[argc]=(int)(c-'0');
471		    dig=1;
472	     	    args[argc+1]=0;
473	    	} else {
474		    args[argc]=args[argc]*10+(int)(c-'0');
475		    if(argc==0) dig++;
476	    	}
477	    } else bail_out(c);
478	} else bail_out(c);
479	break;
480    }
481}
482#endif
483
484static void
485vidc_putchar(int c)
486{
487#ifdef TERM_EMU
488    vidc_term_emu(c);
489#else
490    vidc_rawputchar(c);
491#endif
492}
493
494static int
495vidc_getchar(void)
496{
497    if (vidc_ischar()) {
498	v86.ctl = 0;
499	v86.addr = 0x16;
500	v86.eax = 0x0;
501	v86int();
502	return(v86.eax & 0xff);
503    } else {
504	return(-1);
505    }
506}
507
508static int
509vidc_ischar(void)
510{
511    v86.ctl = V86_FLAGS;
512    v86.addr = 0x16;
513    v86.eax = 0x100;
514    v86int();
515    return(!(v86.efl & PSL_Z));
516}
517
518#if KEYBOARD_PROBE
519
520#define PROBE_MAXRETRY	5
521#define PROBE_MAXWAIT	400
522#define IO_DUMMY	0x84
523#define IO_KBD		0x060		/* 8042 Keyboard */
524
525/* selected defines from kbdio.h */
526#define KBD_STATUS_PORT 	4	/* status port, read */
527#define KBD_DATA_PORT		0	/* data port, read/write
528					 * also used as keyboard command
529					 * and mouse command port
530					 */
531#define KBDC_ECHO		0x00ee
532#define KBDS_ANY_BUFFER_FULL	0x0001
533#define KBDS_INPUT_BUFFER_FULL	0x0002
534#define KBD_ECHO		0x00ee
535
536/* 7 microsec delay necessary for some keyboard controllers */
537static void
538delay7(void)
539{
540    /*
541     * I know this is broken, but no timer is available yet at this stage...
542     * See also comments in `delay1ms()'.
543     */
544    inb(IO_DUMMY); inb(IO_DUMMY);
545    inb(IO_DUMMY); inb(IO_DUMMY);
546    inb(IO_DUMMY); inb(IO_DUMMY);
547}
548
549/*
550 * This routine uses an inb to an unused port, the time to execute that
551 * inb is approximately 1.25uS.  This value is pretty constant across
552 * all CPU's and all buses, with the exception of some PCI implentations
553 * that do not forward this I/O adress to the ISA bus as they know it
554 * is not a valid ISA bus address, those machines execute this inb in
555 * 60 nS :-(.
556 *
557 */
558static void
559delay1ms(void)
560{
561    int i = 800;
562    while (--i >= 0)
563	(void)inb(0x84);
564}
565
566/*
567 * We use the presence/absence of a keyboard to determine whether the internal
568 * console can be used for input.
569 *
570 * Perform a simple test on the keyboard; issue the ECHO command and see
571 * if the right answer is returned. We don't do anything as drastic as
572 * full keyboard reset; it will be too troublesome and take too much time.
573 */
574static int
575probe_keyboard(void)
576{
577    int retry = PROBE_MAXRETRY;
578    int wait;
579    int i;
580
581    while (--retry >= 0) {
582	/* flush any noise */
583	while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) {
584	    delay7();
585	    inb(IO_KBD + KBD_DATA_PORT);
586	    delay1ms();
587	}
588
589	/* wait until the controller can accept a command */
590	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
591	    if (((i = inb(IO_KBD + KBD_STATUS_PORT))
592                & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0)
593		break;
594	    if (i & KBDS_ANY_BUFFER_FULL) {
595		delay7();
596	        inb(IO_KBD + KBD_DATA_PORT);
597	    }
598	    delay1ms();
599	}
600	if (wait <= 0)
601	    continue;
602
603	/* send the ECHO command */
604	outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO);
605
606	/* wait for a response */
607	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
608	     if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)
609		 break;
610	     delay1ms();
611	}
612	if (wait <= 0)
613	    continue;
614
615	delay7();
616	i = inb(IO_KBD + KBD_DATA_PORT);
617#ifdef PROBE_KBD_BEBUG
618        printf("probe_keyboard: got 0x%x.\n", i);
619#endif
620	if (i == KBD_ECHO) {
621	    /* got the right answer */
622	    return (0);
623	}
624    }
625
626    return (1);
627}
628#endif /* KEYBOARD_PROBE */
629