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$");
32
33#include <stand.h>
34#include <bootstrap.h>
35#include <btxv86.h>
36#include <machine/cpufunc.h>
37#include "libi386.h"
38
39#if KEYBOARD_PROBE
40#include <machine/cpufunc.h>
41
42static int	probe_keyboard(void);
43#endif
44static void	vidc_probe(struct console *cp);
45static int	vidc_init(int arg);
46static void	vidc_putchar(int c);
47static int	vidc_getchar(void);
48static int	vidc_ischar(void);
49
50static int	vidc_started;
51
52#ifdef TERM_EMU
53#define MAXARGS		8
54#define DEFAULT_FGCOLOR	7
55#define DEFAULT_BGCOLOR	0
56
57void		end_term(void);
58void		bail_out(int c);
59void		vidc_term_emu(int c);
60void		get_pos(void);
61void		curs_move(int x, int y);
62void		write_char(int c, int fg, int bg);
63void		scroll_up(int rows, int fg, int bg);
64void		CD(void);
65void		CM(void);
66void		HO(void);
67
68static int	args[MAXARGS], argc;
69static int	fg_c, bg_c, curx, cury;
70static int	esc;
71#endif
72
73static unsigned short *crtat, *Crtat;
74static int row = 25, col = 80;
75#ifdef TERM_EMU
76static u_int8_t	ibmpc_to_pc98[256] = {
77	0x01, 0x21, 0x81, 0xa1, 0x41, 0x61, 0xc1, 0xe1,
78	0x09, 0x29, 0x89, 0xa9, 0x49, 0x69, 0xc9, 0xe9,
79	0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
80	0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
81	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
82	0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
83	0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
84	0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
85	0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
86	0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
87	0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
88	0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
89	0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
90	0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
91	0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
92	0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
93
94	0x03, 0x23, 0x83, 0xa3, 0x43, 0x63, 0xc3, 0xe3,
95	0x0b, 0x2b, 0x8b, 0xab, 0x4b, 0x6b, 0xcb, 0xeb,
96	0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
97	0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
98	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
99	0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
100	0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
101	0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
102	0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
103	0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
104	0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
105	0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
106	0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
107	0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
108	0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef,
109	0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef,
110};
111#define	at2pc98(fg_at, bg_at)	ibmpc_to_pc98[((bg_at) << 4) | (fg_at)]
112#endif /* TERM_EMU */
113
114struct console vidconsole = {
115    "vidconsole",
116    "internal video/keyboard",
117    0,
118    vidc_probe,
119    vidc_init,
120    vidc_putchar,
121    vidc_getchar,
122    vidc_ischar
123};
124
125static void
126vidc_probe(struct console *cp)
127{
128
129    /* look for a keyboard */
130#if KEYBOARD_PROBE
131    if (probe_keyboard())
132#endif
133    {
134
135	cp->c_flags |= C_PRESENTIN;
136    }
137
138    /* XXX for now, always assume we can do BIOS screen output */
139    cp->c_flags |= C_PRESENTOUT;
140}
141
142static int
143vidc_init(int arg)
144{
145    int		i, hw_cursor;
146
147    if (vidc_started && arg == 0)
148	return (0);
149    vidc_started = 1;
150    Crtat = (unsigned short *)PTOV(0xA0000);
151    while ((inb(0x60) & 0x04) == 0)
152	;
153    outb(0x62, 0xe0);
154    while ((inb(0x60) & 0x01) == 0)
155	;
156    hw_cursor = inb(0x62);
157    hw_cursor |= (inb(0x62) << 8);
158    inb(0x62);
159    inb(0x62);
160    inb(0x62);
161    crtat = Crtat + hw_cursor;
162#ifdef TERM_EMU
163    /* Init terminal emulator */
164    end_term();
165    get_pos();
166    curs_move(curx, cury);
167    fg_c = DEFAULT_FGCOLOR;
168    bg_c = DEFAULT_BGCOLOR;
169#endif
170    for (i = 0; i < 10 && vidc_ischar(); i++)
171	(void)vidc_getchar();
172    return (0);	/* XXX reinit? */
173}
174
175static void
176beep(void)
177{
178
179	outb(0x37, 6);
180	delay(40000);
181	outb(0x37, 7);
182}
183
184#if 0
185static void
186vidc_biosputchar(int c)
187{
188    unsigned short *cp;
189    int i, pos;
190
191#ifdef TERM_EMU
192    *crtat = (c == 0x5c ? 0xfc : c);
193    *(crtat + 0x1000) = at2pc98(fg, bg);
194#else
195    switch(c) {
196    case '\b':
197        crtat--;
198	break;
199    case '\r':
200        crtat -= (crtat - Crtat) % col;
201	break;
202    case '\n':
203        crtat += col;
204	break;
205    default:
206        *crtat = (c == 0x5c ? 0xfc : c);
207	*(crtat++ + 0x1000) = 0xe1;
208	break;
209    }
210
211    if (crtat >= Crtat + col * row) {
212        cp = Crtat;
213	for (i = 1; i < row; i++) {
214	    bcopy((void *)(cp + col), (void *)cp, col * 2);
215	    cp += col;
216	}
217	for (i = 0; i < col; i++) {
218	    *cp++ = ' ';
219	}
220	crtat -= col;
221    }
222    pos = crtat - Crtat;
223    while ((inb(0x60) & 0x04) == 0) {}
224    outb(0x62, 0x49);
225    outb(0x60, pos & 0xff);
226    outb(0x60, pos >> 8);
227#endif
228}
229#endif
230
231static void
232vidc_rawputchar(int c)
233{
234    int		i;
235
236    if (c == '\t')
237	/* lame tab expansion */
238	for (i = 0; i < 8; i++)
239	    vidc_rawputchar(' ');
240    else {
241	/* Emulate AH=0eh (teletype output) */
242	switch(c) {
243	case '\a':
244	    beep();
245	    return;
246	case '\r':
247	    curx = 0;
248	    curs_move(curx, cury);
249	    return;
250	case '\n':
251	    cury++;
252	    if (cury > 24) {
253		scroll_up(1, fg_c, bg_c);
254		cury--;
255	    } else {
256		curs_move(curx, cury);
257	    }
258	    return;
259	case '\b':
260	    if (curx > 0) {
261		curx--;
262		curs_move(curx, cury);
263		/* write_char(' ', fg_c, bg_c); XXX destructive(!) */
264		return;
265	    }
266	    return;
267	default:
268	    write_char(c, fg_c, bg_c);
269	    curx++;
270	    if (curx > 79) {
271		curx = 0;
272		cury++;
273	    }
274	    if (cury > 24) {
275		curx = 0;
276		scroll_up(1, fg_c, bg_c);
277		cury--;
278	    }
279	}
280	curs_move(curx, cury);
281    }
282}
283
284#ifdef TERM_EMU
285
286/* Get cursor position on the screen. Result is in edx. Sets
287 * curx and cury appropriately.
288 */
289void
290get_pos(void)
291{
292    int pos = crtat - Crtat;
293
294    curx = pos % col;
295    cury = pos / col;
296}
297
298/* Move cursor to x rows and y cols (0-based). */
299void
300curs_move(int x, int y)
301{
302    int pos;
303
304    pos = x + y * col;
305    crtat = Crtat + pos;
306    pos = crtat - Crtat;
307    while((inb(0x60) & 0x04) == 0) {}
308    outb(0x62, 0x49);
309    outb(0x60, pos & 0xff);
310    outb(0x60, pos >> 8);
311    curx = x;
312    cury = y;
313#define isvisible(c)	(((c) >= 32) && ((c) < 255))
314    if (!isvisible(*crtat & 0x00ff)) {
315	write_char(' ', fg_c, bg_c);
316    }
317}
318
319/* Scroll up the whole window by a number of rows. If rows==0,
320 * clear the window. fg and bg are attributes for the new lines
321 * inserted in the window.
322 */
323void
324scroll_up(int rows, int fgcol, int bgcol)
325{
326    unsigned short *cp;
327    int i;
328
329    if (rows == 0)
330	rows = 25;
331    cp = Crtat;
332    for (i = rows; i < row; i++) {
333	bcopy((void *)(cp + col), (void *)cp, col * 2);
334	cp += col;
335    }
336    for (i = 0; i < col; i++) {
337	*(cp + 0x1000) = at2pc98(fgcol, bgcol);
338	*cp++ = ' ';
339    }
340}
341
342/* Write character and attribute at cursor position. */
343void
344write_char(int c, int fgcol, int bgcol)
345{
346
347    *crtat = (c == 0x5c ? 0xfc : (c & 0xff));
348    *(crtat + 0x1000) = at2pc98(fgcol, bgcol);
349}
350
351/**************************************************************/
352/*
353 * Screen manipulation functions. They use accumulated data in
354 * args[] and argc variables.
355 *
356 */
357
358/* Clear display from current position to end of screen */
359void
360CD(void)
361{
362    int pos;
363
364    get_pos();
365    for (pos = 0; crtat + pos <= Crtat + col * row; pos++) {
366	*(crtat + pos) = ' ';
367	*(crtat + pos + 0x1000) = at2pc98(fg_c, bg_c);
368    }
369    end_term();
370}
371
372/* Absolute cursor move to args[0] rows and args[1] columns
373 * (the coordinates are 1-based).
374 */
375void
376CM(void)
377{
378
379    if (args[0] > 0)
380	args[0]--;
381    if (args[1] > 0)
382	args[1]--;
383    curs_move(args[1], args[0]);
384    end_term();
385}
386
387/* Home cursor (left top corner) */
388void
389HO(void)
390{
391
392    argc = 1;
393    args[0] = args[1] = 1;
394    CM();
395}
396
397/* Clear internal state of the terminal emulation code */
398void
399end_term(void)
400{
401
402    esc = 0;
403    argc = -1;
404}
405
406/* Gracefully exit ESC-sequence processing in case of misunderstanding */
407void
408bail_out(int c)
409{
410    char buf[16], *ch;
411    int i;
412
413    if (esc) {
414	vidc_rawputchar('\033');
415	if (esc != '\033')
416	    vidc_rawputchar(esc);
417	for (i = 0; i <= argc; ++i) {
418	    sprintf(buf, "%d", args[i]);
419	    ch = buf;
420	    while (*ch)
421		vidc_rawputchar(*ch++);
422	}
423    }
424    vidc_rawputchar(c);
425    end_term();
426}
427
428static void
429get_arg(int c)
430{
431
432    if (argc < 0)
433	argc = 0;
434    args[argc] *= 10;
435    args[argc] += c - '0';
436}
437
438/* Emulate basic capabilities of cons25 terminal */
439void
440vidc_term_emu(int c)
441{
442    static int ansi_col[] = {
443	0, 4, 2, 6, 1, 5, 3, 7,
444    };
445    int t;
446    int i;
447
448    switch (esc) {
449    case 0:
450	switch (c) {
451	case '\033':
452	    esc = c;
453	    break;
454	default:
455	    vidc_rawputchar(c);
456	    break;
457	}
458	break;
459
460    case '\033':
461	switch (c) {
462	case '[':
463	    esc = c;
464	    args[0] = 0;
465	    argc = -1;
466	    break;
467	default:
468	    bail_out(c);
469	    break;
470	}
471	break;
472
473    case '[':
474	switch (c) {
475	case ';':
476	    if (argc < 0)	/* XXX */
477		argc = 0;
478	    else if (argc + 1 >= MAXARGS)
479		bail_out(c);
480	    else
481		args[++argc] = 0;
482	    break;
483	case 'H':
484	    if (argc < 0)
485		HO();
486	    else if (argc == 1)
487		CM();
488	    else
489		bail_out(c);
490	    break;
491	case 'J':
492	    if (argc < 0)
493		CD();
494	    else
495		bail_out(c);
496	    break;
497	case 'm':
498	    if (argc < 0) {
499		fg_c = DEFAULT_FGCOLOR;
500		bg_c = DEFAULT_BGCOLOR;
501	    }
502	    for (i = 0; i <= argc; ++i) {
503		switch (args[i]) {
504		case 0:		/* back to normal */
505		    fg_c = DEFAULT_FGCOLOR;
506		    bg_c = DEFAULT_BGCOLOR;
507		    break;
508		case 1:		/* bold */
509		    fg_c |= 0x8;
510		    break;
511		case 4:		/* underline */
512		case 5:		/* blink */
513		    bg_c |= 0x8;
514		    break;
515		case 7:		/* reverse */
516		    t = fg_c;
517		    fg_c = bg_c;
518		    bg_c = t;
519		    break;
520		case 30: case 31: case 32: case 33:
521		case 34: case 35: case 36: case 37:
522		    fg_c = ansi_col[args[i] - 30];
523		    break;
524		case 39:	/* normal */
525		    fg_c = DEFAULT_FGCOLOR;
526		    break;
527		case 40: case 41: case 42: case 43:
528		case 44: case 45: case 46: case 47:
529		    bg_c = ansi_col[args[i] - 40];
530		    break;
531		case 49:	/* normal */
532		    bg_c = DEFAULT_BGCOLOR;
533		    break;
534		}
535	    }
536	    end_term();
537	    break;
538	default:
539	    if (isdigit(c))
540		get_arg(c);
541	    else
542		bail_out(c);
543	    break;
544	}
545	break;
546
547    default:
548	bail_out(c);
549	break;
550    }
551}
552#endif
553
554static void
555vidc_putchar(int c)
556{
557#ifdef TERM_EMU
558    vidc_term_emu(c);
559#else
560    vidc_rawputchar(c);
561#endif
562}
563
564static int
565vidc_getchar(void)
566{
567
568    if (vidc_ischar()) {
569	v86.ctl = 0;
570	v86.addr = 0x18;
571	v86.eax = 0x0;
572	v86int();
573	return (v86.eax & 0xff);
574    } else {
575	return (-1);
576    }
577}
578
579static int
580vidc_ischar(void)
581{
582
583    v86.ctl = 0;
584    v86.addr = 0x18;
585    v86.eax = 0x100;
586    v86int();
587    return ((v86.ebx >> 8) & 0x1);
588}
589
590#if KEYBOARD_PROBE
591static int
592probe_keyboard(void)
593{
594    return (*(u_char *)PTOV(0xA1481) & 0x48);
595}
596#endif /* KEYBOARD_PROBE */
597