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