initial commit
[freebsd-arm:freebsd-arm.git] / boot / i386 / libi386 / vidconsole.c
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/psl.h>
37 #include "libi386.h"
38
39 #if KEYBOARD_PROBE
40 #include <machine/cpufunc.h>
41
42 static int      probe_keyboard(void);
43 #endif
44 static void     vidc_probe(struct console *cp);
45 static int      vidc_init(int arg);
46 static void     vidc_putchar(int c);
47 static int      vidc_getchar(void);
48 static int      vidc_ischar(void);
49
50 static int      vidc_started;
51
52 #ifdef TERM_EMU
53 #define MAXARGS         8
54 #define DEFAULT_FGCOLOR 7
55 #define DEFAULT_BGCOLOR 0
56
57 void            end_term(void);
58 void            bail_out(int c);
59 void            vidc_term_emu(int c);
60 void            get_pos(int *x, int *y);
61 void            curs_move(int *_x, int *_y, int x, int y);
62 void            write_char(int c, int fg, int bg);
63 void            scroll_up(int rows, int fg, int bg);
64 void            CD(void);
65 void            CM(void);
66 void            HO(void);
67
68 static int      args[MAXARGS], argc;
69 static int      fg_c, bg_c, curx, cury;
70 static int      esc;
71 #endif
72
73
74 struct console vidconsole = {
75     "vidconsole",
76     "internal video/keyboard",
77     0,
78     vidc_probe,
79     vidc_init,
80     vidc_putchar,
81     vidc_getchar,
82     vidc_ischar
83 };
84
85 static void
86 vidc_probe(struct console *cp)
87 {
88     
89     /* look for a keyboard */
90 #if KEYBOARD_PROBE
91     if (probe_keyboard())
92 #endif
93     {
94         
95         cp->c_flags |= C_PRESENTIN;
96     }
97
98     /* XXX for now, always assume we can do BIOS screen output */
99     cp->c_flags |= C_PRESENTOUT;
100 }
101
102 static int
103 vidc_init(int arg)
104 {
105     int         i;
106
107     if (vidc_started && arg == 0)
108         return (0);
109     vidc_started = 1;
110 #ifdef TERM_EMU
111     /* Init terminal emulator */
112     end_term();
113     get_pos(&curx, &cury);
114     curs_move(&curx, &cury, curx, cury);
115     fg_c = DEFAULT_FGCOLOR;
116     bg_c = DEFAULT_BGCOLOR;
117 #endif
118     for (i = 0; i < 10 && vidc_ischar(); i++)
119         (void)vidc_getchar();
120     return (0); /* XXX reinit? */
121 }
122
123 void
124 vidc_biosputchar(int c)
125 {
126
127     v86.ctl = 0;
128     v86.addr = 0x10;
129     v86.eax = 0xe00 | (c & 0xff);
130     v86.ebx = 0x7;
131     v86int();
132 }
133
134 static void
135 vidc_rawputchar(int c)
136 {
137     int         i;
138
139     if (c == '\t')
140         /* lame tab expansion */
141         for (i = 0; i < 8; i++)
142             vidc_rawputchar(' ');
143     else {
144 #ifndef TERM_EMU
145         vidc_biosputchar(c);
146 #else
147         /* Emulate AH=0eh (teletype output) */
148         switch(c) {
149         case '\a':
150             vidc_biosputchar(c);
151             return;
152         case '\r':
153             curx = 0;
154             curs_move(&curx, &cury, curx, cury);
155             return;
156         case '\n':
157             cury++;
158             if (cury > 24) {
159                 scroll_up(1, fg_c, bg_c);
160                 cury--;
161             } else {
162                 curs_move(&curx, &cury, curx, cury);
163             }
164             return;
165         case '\b':
166             if (curx > 0) {
167                 curx--;
168                 curs_move(&curx, &cury, curx, cury);
169                 /* write_char(' ', fg_c, bg_c); XXX destructive(!) */
170                 return;
171             }
172             return;
173         default:
174             write_char(c, fg_c, bg_c);
175             curx++;
176             if (curx > 79) {
177                 curx = 0;
178                 cury++;
179             }
180             if (cury > 24) {
181                 curx = 0;
182                 scroll_up(1, fg_c, bg_c);
183                 cury--;
184             }
185         }
186         curs_move(&curx, &cury, curx, cury);
187 #endif
188     }
189 }
190
191 #ifdef TERM_EMU
192
193 /* Get cursor position on the screen. Result is in edx. Sets
194  * curx and cury appropriately.
195  */
196 void
197 get_pos(int *x, int *y)
198 {
199
200     v86.ctl = 0;
201     v86.addr = 0x10;
202     v86.eax = 0x0300;
203     v86.ebx = 0x0;
204     v86int();
205     *x = v86.edx & 0x00ff;
206     *y = (v86.edx & 0xff00) >> 8;
207 }
208
209 /* Move cursor to x rows and y cols (0-based). */
210 void
211 curs_move(int *_x, int *_y, int x, int y)
212 {
213
214     v86.ctl = 0;
215     v86.addr = 0x10;
216     v86.eax = 0x0200;
217     v86.ebx = 0x0;
218     v86.edx = ((0x00ff & y) << 8) + (0x00ff & x);
219     v86int();
220     *_x = x;
221     *_y = y;
222     /* If there is ctrl char at this position, cursor would be invisible.
223      * Make it a space instead.
224      */
225     v86.ctl = 0;
226     v86.addr = 0x10;
227     v86.eax = 0x0800;
228     v86.ebx = 0x0;
229     v86int();
230 #define isvisible(c)    (((c) >= 32) && ((c) < 255))
231     if (!isvisible(v86.eax & 0x00ff)) {
232         write_char(' ', fg_c, bg_c);
233     }
234 }
235
236 /* Scroll up the whole window by a number of rows. If rows==0,
237  * clear the window. fg and bg are attributes for the new lines
238  * inserted in the window.
239  */
240 void
241 scroll_up(int rows, int fgcol, int bgcol)
242 {
243
244     if (rows == 0)
245         rows = 25;
246     v86.ctl = 0;
247     v86.addr = 0x10;
248     v86.eax = 0x0600 + (0x00ff & rows);
249     v86.ebx = (bgcol << 12) + (fgcol << 8);
250     v86.ecx = 0x0;
251     v86.edx = 0x184f;
252     v86int();
253 }
254
255 /* Write character and attribute at cursor position. */
256 void
257 write_char(int c, int fgcol, int bgcol)
258 {
259
260     v86.ctl = 0;
261     v86.addr = 0x10;
262     v86.eax = 0x0900 + (0x00ff & c);
263     v86.ebx = (bgcol << 4) + fgcol;
264     v86.ecx = 0x1;
265     v86int();
266 }
267
268 /**************************************************************/
269 /*
270  * Screen manipulation functions. They use accumulated data in
271  * args[] and argc variables.
272  *
273  */
274
275 /* Clear display from current position to end of screen */
276 void
277 CD(void)
278 {
279
280     get_pos(&curx, &cury);
281     if (curx > 0) {
282         v86.ctl = 0;
283         v86.addr = 0x10;
284         v86.eax = 0x0600;
285         v86.ebx = (bg_c << 4) + fg_c;
286         v86.ecx = (cury << 8) + curx;
287         v86.edx = (cury << 8) + 79;
288         v86int();
289         if (++cury > 24) {
290             end_term();
291             return;
292         }
293     }
294     v86.ctl = 0;
295     v86.addr = 0x10;
296     v86.eax = 0x0600;
297     v86.ebx = (bg_c << 4) + fg_c;
298     v86.ecx = (cury << 8) + 0;
299     v86.edx = (24 << 8) + 79;
300     v86int();
301     end_term();
302 }
303
304 /* Absolute cursor move to args[0] rows and args[1] columns
305  * (the coordinates are 1-based).
306  */
307 void
308 CM(void)
309 {
310
311     if (args[0] > 0)
312         args[0]--;
313     if (args[1] > 0)
314         args[1]--;
315     curs_move(&curx, &cury, args[1], args[0]);
316     end_term();
317 }
318
319 /* Home cursor (left top corner) */
320 void
321 HO(void)
322 {
323
324     argc = 1;
325     args[0] = args[1] = 1;
326     CM();
327 }
328
329 /* Clear internal state of the terminal emulation code */
330 void
331 end_term(void)
332 {
333
334     esc = 0;
335     argc = -1;
336 }
337
338 /* Gracefully exit ESC-sequence processing in case of misunderstanding */
339 void
340 bail_out(int c)
341 {
342     char buf[16], *ch;
343     int i;
344
345     if (esc) {
346         vidc_rawputchar('\033');
347         if (esc != '\033')
348             vidc_rawputchar(esc);
349         for (i = 0; i <= argc; ++i) {
350             sprintf(buf, "%d", args[i]);
351             ch = buf;
352             while (*ch)
353                 vidc_rawputchar(*ch++);
354         }
355     }
356     vidc_rawputchar(c);
357     end_term();
358 }
359
360 static void
361 get_arg(int c)
362 {
363
364     if (argc < 0)
365         argc = 0;
366     args[argc] *= 10;
367     args[argc] += c - '0';
368 }
369
370 /* Emulate basic capabilities of cons25 terminal */
371 void
372 vidc_term_emu(int c)
373 {
374     static int ansi_col[] = {
375         0, 4, 2, 6, 1, 5, 3, 7,
376     };
377     int t;
378     int i;
379
380     switch (esc) {
381     case 0:
382         switch (c) {
383         case '\033':
384             esc = c;
385             break;
386         default:
387             vidc_rawputchar(c);
388             break;
389         }
390         break;
391
392     case '\033':
393         switch (c) {
394         case '[':
395             esc = c;
396             args[0] = 0;
397             argc = -1;
398             break;
399         default:
400             bail_out(c);
401             break;
402         }
403         break;
404
405     case '[':
406         switch (c) {
407         case ';':
408             if (argc < 0)       /* XXX */
409                 argc = 0;
410             else if (argc + 1 >= MAXARGS)
411                 bail_out(c);
412             else
413                 args[++argc] = 0;
414             break;
415         case 'H':
416             if (argc < 0)
417                 HO();
418             else if (argc == 1)
419                 CM();
420             else
421                 bail_out(c);
422             break;
423         case 'J':
424             if (argc < 0)
425                 CD();
426             else
427                 bail_out(c);
428             break;
429         case 'm':
430             if (argc < 0) {
431                 fg_c = DEFAULT_FGCOLOR;
432                 bg_c = DEFAULT_BGCOLOR;
433             }
434             for (i = 0; i <= argc; ++i) {
435                 switch (args[i]) {
436                 case 0:         /* back to normal */
437                     fg_c = DEFAULT_FGCOLOR;
438                     bg_c = DEFAULT_BGCOLOR;
439                     break;
440                 case 1:         /* bold */
441                     fg_c |= 0x8;
442                     break;
443                 case 4:         /* underline */
444                 case 5:         /* blink */
445                     bg_c |= 0x8;
446                     break;
447                 case 7:         /* reverse */
448                     t = fg_c;
449                     fg_c = bg_c;
450                     bg_c = t;
451                     break;
452                 case 30: case 31: case 32: case 33:
453                 case 34: case 35: case 36: case 37:
454                     fg_c = ansi_col[args[i] - 30];
455                     break;
456                 case 39:        /* normal */
457                     fg_c = DEFAULT_FGCOLOR;
458                     break;
459                 case 40: case 41: case 42: case 43:
460                 case 44: case 45: case 46: case 47:
461                     bg_c = ansi_col[args[i] - 40];
462                     break;
463                 case 49:        /* normal */
464                     bg_c = DEFAULT_BGCOLOR;
465                     break;
466                 }
467             }
468             end_term();
469             break;
470         default:
471             if (isdigit(c))
472                 get_arg(c);
473             else
474                 bail_out(c);
475             break;
476         }
477         break;
478
479     default:
480         bail_out(c);
481         break;
482     }
483 }
484 #endif
485
486 static void
487 vidc_putchar(int c)
488 {
489 #ifdef TERM_EMU
490     vidc_term_emu(c);
491 #else
492     vidc_rawputchar(c);
493 #endif
494 }
495
496 static int
497 vidc_getchar(void)
498 {
499
500     if (vidc_ischar()) {
501         v86.ctl = 0;
502         v86.addr = 0x16;
503         v86.eax = 0x0;
504         v86int();
505         return (v86.eax & 0xff);
506     } else {
507         return (-1);
508     }
509 }
510
511 static int
512 vidc_ischar(void)
513 {
514
515     v86.ctl = V86_FLAGS;
516     v86.addr = 0x16;
517     v86.eax = 0x100;
518     v86int();
519     return (!(v86.efl & PSL_Z));
520 }
521
522 #if KEYBOARD_PROBE
523
524 #define PROBE_MAXRETRY  5
525 #define PROBE_MAXWAIT   400
526 #define IO_DUMMY        0x84
527 #define IO_KBD          0x060           /* 8042 Keyboard */
528
529 /* selected defines from kbdio.h */
530 #define KBD_STATUS_PORT         4       /* status port, read */
531 #define KBD_DATA_PORT           0       /* data port, read/write 
532                                          * also used as keyboard command
533                                          * and mouse command port 
534                                          */
535 #define KBDC_ECHO               0x00ee
536 #define KBDS_ANY_BUFFER_FULL    0x0001
537 #define KBDS_INPUT_BUFFER_FULL  0x0002
538 #define KBD_ECHO                0x00ee
539
540 /* 7 microsec delay necessary for some keyboard controllers */
541 static void
542 delay7(void)
543 {
544     /* 
545      * I know this is broken, but no timer is available yet at this stage...
546      * See also comments in `delay1ms()'.
547      */
548     inb(IO_DUMMY); inb(IO_DUMMY);
549     inb(IO_DUMMY); inb(IO_DUMMY);
550     inb(IO_DUMMY); inb(IO_DUMMY);
551 }
552
553 /*
554  * This routine uses an inb to an unused port, the time to execute that
555  * inb is approximately 1.25uS.  This value is pretty constant across
556  * all CPU's and all buses, with the exception of some PCI implentations
557  * that do not forward this I/O address to the ISA bus as they know it
558  * is not a valid ISA bus address, those machines execute this inb in
559  * 60 nS :-(.
560  *
561  */
562 static void
563 delay1ms(void)
564 {
565     int i = 800;
566     while (--i >= 0)
567         (void)inb(0x84);
568 }
569
570 /* 
571  * We use the presence/absence of a keyboard to determine whether the internal
572  * console can be used for input.
573  *
574  * Perform a simple test on the keyboard; issue the ECHO command and see
575  * if the right answer is returned. We don't do anything as drastic as
576  * full keyboard reset; it will be too troublesome and take too much time.
577  */
578 static int
579 probe_keyboard(void)
580 {
581     int retry = PROBE_MAXRETRY;
582     int wait;
583     int i;
584
585     while (--retry >= 0) {
586         /* flush any noise */
587         while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) {
588             delay7();
589             inb(IO_KBD + KBD_DATA_PORT);
590             delay1ms();
591         }
592
593         /* wait until the controller can accept a command */
594         for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
595             if (((i = inb(IO_KBD + KBD_STATUS_PORT)) 
596                 & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0)
597                 break;
598             if (i & KBDS_ANY_BUFFER_FULL) {
599                 delay7();
600                 inb(IO_KBD + KBD_DATA_PORT);
601             }
602             delay1ms();
603         }
604         if (wait <= 0)
605             continue;
606
607         /* send the ECHO command */
608         outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO);
609
610         /* wait for a response */
611         for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
612              if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)
613                  break;
614              delay1ms();
615         }
616         if (wait <= 0)
617             continue;
618
619         delay7();
620         i = inb(IO_KBD + KBD_DATA_PORT);
621 #ifdef PROBE_KBD_BEBUG
622         printf("probe_keyboard: got 0x%x.\n", i);
623 #endif
624         if (i == KBD_ECHO) {
625             /* got the right answer */
626             return (0);
627         }
628     }
629
630     return (1);
631 }
632 #endif /* KEYBOARD_PROBE */