Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / boot / pc32 / 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  *      From Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp
28  *
29  * $FreeBSD: src/sys/boot/i386/libi386/vidconsole.c,v 1.12.2.2 2002/02/11 04:50:11 nyan Exp $
30  */
31
32 #include <stand.h>
33 #include <bootstrap.h>
34 #include <btxv86.h>
35 #include <machine/psl.h>
36 #include "libi386.h"
37
38 #if KEYBOARD_PROBE
39 #include <machine/cpufunc.h>
40
41 static int      probe_keyboard(void);
42 #endif
43 static void     vidc_probe(struct console *cp);
44 static int      vidc_init(int arg);
45 static void     vidc_putchar(int c);
46 static int      vidc_getchar(void);
47 static int      vidc_ischar(void);
48
49 static int      vidc_started;
50
51 #ifdef TERM_EMU
52 void            end_term(void);
53 void            bail_out(int c);
54 void            vidc_term_emu(int c);
55 void            get_pos(void);
56 void            curs_move(int x, int y);
57 void            write_char(int c, int fg, int bg);
58 void            scroll_up(int rows, int fg, int bg);
59 int             pow10(int i);
60 void            AB(void);
61 void            AF(void);
62 void            CD(void);
63 void            CM(void);
64 void            HO(void);
65 void            ME(void);
66
67 static int      args[2],argc,br;
68 static int      fg,bg,dig;
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();
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
123 static void
124 vidc_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
133 static void
134 vidc_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  */
195 void
196 get_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). */
208 void
209 curs_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  */
237 void
238 scroll_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. */
251 void
252 write_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 */
263 int
264 pow10(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 */
282 void
283 AB(void){
284         bg_c=args[0];
285         end_term();
286 }
287
288 /* Set foreground color */
289 void
290 AF(void)
291 {
292         fg_c=args[0];
293         end_term();
294 }
295
296 /* Clear display from current position to end of screen */
297 void
298 CD(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  */
316 void
317 CM(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) */
326 void
327 HO(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) */
335 void
336 ME(void)
337 {
338         fg_c=7;
339         bg_c=0;
340         end_term();
341 }
342
343 /* Clear internal state of the terminal emulation code */
344 void
345 end_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 */
355 void
356 bail_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 */
379 void
380 vidc_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
484 static void
485 vidc_putchar(int c)
486 {
487 #ifdef TERM_EMU
488     vidc_term_emu(c);
489 #else
490     vidc_rawputchar(c);
491 #endif
492 }
493
494 static int
495 vidc_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
508 static int
509 vidc_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 */
537 static void
538 delay7(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 address 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  */
558 static void
559 delay1ms(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  */
574 static int
575 probe_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 */