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