vidconsole.c revision 80203
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: head/sys/boot/pc98/libpc98/vidconsole.c 80203 2001-07-23 12:05:27Z kris $ 30 */ 31 32#include <stand.h> 33#include <bootstrap.h> 34#include <btxv86.h> 35#include <machine/psl.h> 36#ifdef PC98 37#include <machine/cpufunc.h> 38#endif 39#include "libi386.h" 40 41#if KEYBOARD_PROBE 42#include <machine/cpufunc.h> 43 44static int probe_keyboard(void); 45#endif 46static void vidc_probe(struct console *cp); 47static int vidc_init(int arg); 48static void vidc_putchar(int c); 49static int vidc_getchar(void); 50static int vidc_ischar(void); 51 52static int vidc_started; 53 54#ifdef TERM_EMU 55void end_term(void); 56void bail_out(int c); 57void vidc_term_emu(int c); 58void get_pos(void); 59void curs_move(int x, int y); 60void write_char(int c, int fg, int bg); 61void scroll_up(int rows, int fg, int bg); 62int pow10(int i); 63void AB(void); 64void AF(void); 65void CD(void); 66void CM(void); 67void HO(void); 68void ME(void); 69 70static int args[2],argc,br; 71static int fg,bg,dig; 72static int fg_c,bg_c,curx,cury; 73static int esc; 74#endif 75 76#ifdef PC98 77static unsigned short *crtat, *Crtat; 78static int row = 25, col = 80; 79#ifdef TERM_EMU 80unsigned int at2pc98(unsigned int fg_at, unsigned int bg_at); 81#endif 82#endif 83 84struct console vidconsole = { 85 "vidconsole", 86 "internal video/keyboard", 87 0, 88 vidc_probe, 89 vidc_init, 90 vidc_putchar, 91 vidc_getchar, 92 vidc_ischar 93}; 94 95static void 96vidc_probe(struct console *cp) 97{ 98 99 /* look for a keyboard */ 100#if KEYBOARD_PROBE 101 if (probe_keyboard()) 102#endif 103 { 104 105 cp->c_flags |= C_PRESENTIN; 106 } 107 108 /* XXX for now, always assume we can do BIOS screen output */ 109 cp->c_flags |= C_PRESENTOUT; 110} 111 112static int 113vidc_init(int arg) 114{ 115 int i; 116#ifdef PC98 117 int hw_cursor; 118#endif 119 120 if (vidc_started && arg == 0) 121 return(0); 122 vidc_started = 1; 123#ifdef PC98 124 Crtat = (unsigned short *)PTOV(0xA0000); 125 while((inb(0x60) & 0x04) == 0); 126 outb(0x62, 0xe0); 127 while((inb(0x60) & 0x01) == 0); 128 hw_cursor = inb(0x62); 129 hw_cursor |= (inb(0x62) << 8); 130 inb(0x62); 131 inb(0x62); 132 inb(0x62); 133 crtat = Crtat + hw_cursor; 134#endif 135#ifdef TERM_EMU 136 /* Init terminal emulator */ 137 end_term(); 138 get_pos(); 139 curs_move(curx,cury); 140 fg_c=7; 141 bg_c=0; 142#endif 143 for(i = 0; i < 10 && vidc_ischar(); i++) 144 (void)vidc_getchar(); 145 return(0); /* XXX reinit? */ 146} 147 148#ifdef PC98 149static void 150beep(void) 151{ 152 outb(0x37, 6); 153 delay(40000); 154 outb(0x37, 7); 155} 156#endif 157 158#if 0 159static void 160vidc_biosputchar(int c) 161{ 162#ifdef PC98 163 unsigned short *cp; 164 int i, pos; 165 166#ifdef TERM_EMU 167 *crtat = (c == 0x5c ? 0xfc : c); 168 *(crtat + 0x1000) = at2pc98(fg, bg); 169#else 170 switch(c) { 171 case '\b': 172 crtat--; 173 break; 174 case '\r': 175 crtat -= (crtat - Crtat) % col; 176 break; 177 case '\n': 178 crtat += col; 179 break; 180 default: 181 *crtat = (c == 0x5c ? 0xfc : c); 182 *(crtat++ + 0x1000) = 0xe1; 183 break; 184 } 185 186 if (crtat >= Crtat + col * row) { 187 cp = Crtat; 188 for (i = 1; i < row; i++) { 189 bcopy((void *)(cp+col), (void *)cp, col*2); 190 cp += col; 191 } 192 for (i = 0; i < col; i++) { 193 *cp++ = ' '; 194 } 195 crtat -= col; 196 } 197 pos = crtat - Crtat; 198 while((inb(0x60) & 0x04) == 0) {} 199 outb(0x62, 0x49); 200 outb(0x60, pos & 0xff); 201 outb(0x60, pos >> 8); 202#endif 203#else 204 v86.ctl = 0; 205 v86.addr = 0x10; 206 v86.eax = 0xe00 | (c & 0xff); 207 v86.ebx = 0x7; 208 v86int(); 209#endif 210} 211#endif 212 213static void 214vidc_rawputchar(int c) 215{ 216 int i; 217 218 if(c == '\t') 219 /* lame tab expansion */ 220 for (i = 0; i < 8; i++) 221 vidc_rawputchar(' '); 222 else { 223#if !defined(TERM_EMU) && !defined(PC98) 224 vidc_biosputchar(c); 225#else 226 /* Emulate AH=0eh (teletype output) */ 227 switch(c) { 228 case '\a': 229#ifdef PC98 230 beep(); 231#else 232 vidc_biosputchar(c); 233#endif 234 return; 235 case '\r': 236 curx=0; 237 curs_move(curx,cury); 238 return; 239 case '\n': 240 cury++; 241 if(cury>24) { 242 scroll_up(1,fg_c,bg_c); 243 cury--; 244 } else { 245 curs_move(curx,cury); 246 } 247 return; 248 case '\b': 249 if(curx>0) { 250 curx--; 251 curs_move(curx,cury); 252 /* write_char(' ',fg_c,bg_c); XXX destructive(!) */ 253 return; 254 } 255 return; 256 default: 257 write_char(c,fg_c,bg_c); 258 curx++; 259 if(curx>79) { 260 curx=0; 261 cury++; 262 } 263 if(cury>24) { 264 curx=0; 265 scroll_up(1,fg_c,bg_c); 266 cury--; 267 } 268 } 269 curs_move(curx,cury); 270#endif 271 } 272} 273 274#ifdef TERM_EMU 275 276/* Get cursor position on the screen. Result is in edx. Sets 277 * curx and cury appropriately. 278 */ 279void 280get_pos(void) 281{ 282#ifdef PC98 283 int pos = crtat - Crtat; 284 curx = pos % col; 285 cury = pos / col; 286#else 287 v86.ctl = 0; 288 v86.addr = 0x10; 289 v86.eax = 0x0300; 290 v86.ebx = 0x0; 291 v86int(); 292 curx=v86.edx & 0x00ff; 293 cury=(v86.edx & 0xff00)>>8; 294#endif 295} 296 297/* Move cursor to x rows and y cols (0-based). */ 298void 299curs_move(int x, int y) 300{ 301#ifdef PC98 302 int pos; 303 pos = x + y*col; 304 crtat = Crtat + pos; 305 pos = crtat - Crtat; 306 while((inb(0x60) & 0x04) == 0) {} 307 outb(0x62, 0x49); 308 outb(0x60, pos & 0xff); 309 outb(0x60, pos >> 8); 310 curx=x; 311 cury=y; 312#define isvisible(c) (((c)>32) && ((c)<255)) 313 if(!isvisible(*crtat & 0x00ff)) { 314 write_char(' ',fg_c,bg_c); 315 } 316#else 317 v86.ctl = 0; 318 v86.addr = 0x10; 319 v86.eax = 0x0200; 320 v86.ebx = 0x0; 321 v86.edx = ((0x00ff & y)<<8)+(0x00ff & x); 322 v86int(); 323 curx=x; 324 cury=y; 325 /* If there is ctrl char at this position, cursor would be invisible. 326 * Make it a space instead. 327 */ 328 v86.ctl=0; 329 v86.addr = 0x10; 330 v86.eax = 0x0800; 331 v86.ebx= 0x0; 332 v86int(); 333#define isvisible(c) (((c)>32) && ((c)<255)) 334 if(!isvisible(v86.eax & 0x00ff)) { 335 write_char(' ',fg_c,bg_c); 336 } 337#endif 338} 339 340/* Scroll up the whole window by a number of rows. If rows==0, 341 * clear the window. fg and bg are attributes for the new lines 342 * inserted in the window. 343 */ 344void 345scroll_up(int rows, int fgcol, int bgcol) 346{ 347#ifdef PC98 348 unsigned short *cp; 349 int i; 350 351 if(rows==0) rows=25; 352 cp = Crtat; 353 for (i = rows ; i < row; i++) { 354 bcopy((void *)(cp+col), (void *)cp, col*2); 355 cp += col; 356 } 357 for (i = 0; i < col; i++) { 358 *(cp + 0x1000) = at2pc98(fgcol, bgcol); 359 *cp++ = ' '; 360 } 361#else 362 if(rows==0) rows=25; 363 v86.ctl = 0; 364 v86.addr = 0x10; 365 v86.eax = 0x0600+(0x00ff & rows); 366 v86.ebx = (bgcol<<12)+(fgcol<<8); 367 v86.ecx = 0x0; 368 v86.edx = 0x184f; 369 v86int(); 370#endif 371} 372 373/* Write character and attribute at cursor position. */ 374void 375write_char(int c, int fgcol, int bgcol) 376{ 377#ifdef PC98 378 *crtat = (c == 0x5c ? 0xfc : c); 379 *(crtat + 0x1000) = at2pc98(fgcol, bgcol); 380#else 381 v86.ctl=0; 382 v86.addr = 0x10; 383 v86.eax = 0x0900+(0x00ff & c); 384 v86.ebx = (bgcol<<4)+fgcol; 385 v86.ecx = 0x1; 386 v86int(); 387#endif 388} 389 390/* Calculate power of 10 */ 391int 392pow10(int i) 393{ 394 int res=1; 395 396 while(i-->0) { 397 res*=10; 398 } 399 return res; 400} 401 402/**************************************************************/ 403/* 404 * Screen manipulation functions. They use accumulated data in 405 * args[] and argc variables. 406 * 407 */ 408 409/* Set background color */ 410void 411AB(void){ 412 bg_c=args[0]; 413 end_term(); 414} 415 416/* Set foreground color */ 417void 418AF(void) 419{ 420 fg_c=args[0]; 421 end_term(); 422} 423 424/* Clear display from current position to end of screen */ 425void 426CD(void) 427{ 428 get_pos(); 429#ifdef PC98 430 for(;crtat <= Crtat + col*row; crtat++){ 431 *crtat = ' '; 432 *(crtat + 0x1000) = at2pc98(fg_c, bg_c); 433 } 434#else 435 v86.ctl = 0; 436 v86.addr = 0x10; 437 v86.eax = 0x0600; 438 v86.ebx = (bg_c<<4)+fg_c; 439 v86.ecx = v86.edx; 440 v86.edx = 0x184f; 441 v86int(); 442#endif 443 curx=0; 444 curs_move(curx,cury); 445 end_term(); 446} 447 448/* Absolute cursor move to args[0] rows and args[1] columns 449 * (the coordinates are 1-based). 450 */ 451void 452CM(void) 453{ 454 if(args[0]>0) args[0]--; 455 if(args[1]>0) args[1]--; 456 curs_move(args[1],args[0]); 457 end_term(); 458} 459 460/* Home cursor (left top corner) */ 461void 462HO(void) 463{ 464 argc=1; 465 args[0]=args[1]=1; 466 CM(); 467} 468 469/* Exit attribute mode (reset fore/back-ground colors to defaults) */ 470void 471ME(void) 472{ 473 fg_c=7; 474 bg_c=0; 475 end_term(); 476} 477 478/* Clear internal state of the terminal emulation code */ 479void 480end_term(void) 481{ 482 esc=0; 483 argc=-1; 484 fg=bg=br=0; 485 args[0]=args[1]=0; 486 dig=0; 487} 488 489/* Gracefully exit ESC-sequence processing in case of misunderstanding */ 490void 491bail_out(int c) 492{ 493 char buf[6],*ch; 494 495 if(esc) vidc_rawputchar('\033'); 496 if(br) vidc_rawputchar('['); 497 if(argc>-1) { 498 sprintf(buf,"%d",args[0]); 499 ch=buf; 500 while(*ch) vidc_rawputchar(*ch++); 501 502 if(argc>0) { 503 vidc_rawputchar(';'); 504 sprintf(buf,"%d",args[1]); 505 ch=buf; 506 while(*ch) vidc_rawputchar(*ch++); 507 } 508 } 509 vidc_rawputchar(c); 510 end_term(); 511} 512 513/* Emulate basic capabilities of cons25 terminal */ 514void 515vidc_term_emu(int c) 516{ 517 518 if(!esc) { 519 if(c=='\033') { 520 esc=1; 521 } else { 522 vidc_rawputchar(c); 523 } 524 return; 525 } 526 527 /* Do ESC sequences processing */ 528 switch(c) { 529 case '\033': 530 /* ESC in ESC sequence - error */ 531 bail_out(c); 532 break; 533 case '[': 534 /* Check if it's first char after ESC */ 535 if(argc<0) { 536 br=1; 537 } else { 538 bail_out(c); 539 } 540 break; 541 case 'H': 542 /* Emulate \E[H (cursor home) and 543 * \E%d;%dH (cursor absolute move) */ 544 if(br) { 545 switch(argc) { 546 case -1: 547 HO(); 548 break; 549 case 1: 550 if(fg) args[0]+=pow10(dig)*3; 551 if(bg) args[0]+=pow10(dig)*4; 552 CM(); 553 break; 554 default: 555 bail_out(c); 556 } 557 } else bail_out(c); 558 break; 559 case 'J': 560 /* Emulate \EJ (clear to end of screen) */ 561 if(br && argc<0) { 562 CD(); 563 } else bail_out(c); 564 break; 565 case ';': 566 /* perhaps args separator */ 567 if(br && (argc>-1)) { 568 argc++; 569 } else bail_out(c); 570 break; 571 case 'm': 572 /* Change char attributes */ 573 if(br) { 574 switch(argc) { 575 case -1: 576 ME(); 577 break; 578 case 0: 579 if(fg) AF(); 580 else AB(); 581 break; 582 default: 583 bail_out(c); 584 } 585 } else bail_out(c); 586 break; 587 default: 588 if(isdigit(c)) { 589 /* Carefully collect numeric arguments */ 590 /* XXX this is ugly. */ 591 if(br) { 592 if(argc==-1) { 593 argc=0; 594 args[argc]=0; 595 dig=0; 596 /* in case we're in error... */ 597 if(c=='3') { 598 fg=1; 599 return; 600 } 601 if(c=='4') { 602 bg=1; 603 return; 604 } 605 args[argc]=(int)(c-'0'); 606 dig=1; 607 args[argc+1]=0; 608 } else { 609 args[argc]=args[argc]*10+(int)(c-'0'); 610 if(argc==0) dig++; 611 } 612 } else bail_out(c); 613 } else bail_out(c); 614 break; 615 } 616} 617#endif 618 619static void 620vidc_putchar(int c) 621{ 622#ifdef TERM_EMU 623 vidc_term_emu(c); 624#else 625 vidc_rawputchar(c); 626#endif 627} 628 629static int 630vidc_getchar(void) 631{ 632 if (vidc_ischar()) { 633 v86.ctl = 0; 634#ifdef PC98 635 v86.addr = 0x18; 636#else 637 v86.addr = 0x16; 638#endif 639 v86.eax = 0x0; 640 v86int(); 641 return(v86.eax & 0xff); 642 } else { 643 return(-1); 644 } 645} 646 647static int 648vidc_ischar(void) 649{ 650#ifdef PC98 651 v86.ctl = 0; 652 v86.addr = 0x18; 653 v86.eax = 0x100; 654 v86int(); 655 return((v86.ebx >> 8) & 0x1); 656#else 657 v86.ctl = V86_FLAGS; 658 v86.addr = 0x16; 659 v86.eax = 0x100; 660 v86int(); 661 return(!(v86.efl & PSL_Z)); 662#endif 663} 664 665#if KEYBOARD_PROBE 666 667#ifdef PC98 668static int 669probe_keyboard(void) 670{ 671 return (*(u_char *)PTOV(0xA1481) & 0x48); 672} 673#else /* PC98 */ 674#define PROBE_MAXRETRY 5 675#define PROBE_MAXWAIT 400 676#define IO_DUMMY 0x84 677#define IO_KBD 0x060 /* 8042 Keyboard */ 678 679/* selected defines from kbdio.h */ 680#define KBD_STATUS_PORT 4 /* status port, read */ 681#define KBD_DATA_PORT 0 /* data port, read/write 682 * also used as keyboard command 683 * and mouse command port 684 */ 685#define KBDC_ECHO 0x00ee 686#define KBDS_ANY_BUFFER_FULL 0x0001 687#define KBDS_INPUT_BUFFER_FULL 0x0002 688#define KBD_ECHO 0x00ee 689 690/* 7 microsec delay necessary for some keyboard controllers */ 691static void 692delay7(void) 693{ 694 /* 695 * I know this is broken, but no timer is available yet at this stage... 696 * See also comments in `delay1ms()'. 697 */ 698 inb(IO_DUMMY); inb(IO_DUMMY); 699 inb(IO_DUMMY); inb(IO_DUMMY); 700 inb(IO_DUMMY); inb(IO_DUMMY); 701} 702 703/* 704 * This routine uses an inb to an unused port, the time to execute that 705 * inb is approximately 1.25uS. This value is pretty constant across 706 * all CPU's and all buses, with the exception of some PCI implentations 707 * that do not forward this I/O address to the ISA bus as they know it 708 * is not a valid ISA bus address, those machines execute this inb in 709 * 60 nS :-(. 710 * 711 */ 712static void 713delay1ms(void) 714{ 715 int i = 800; 716 while (--i >= 0) 717 (void)inb(0x84); 718} 719 720/* 721 * We use the presence/absence of a keyboard to determine whether the internal 722 * console can be used for input. 723 * 724 * Perform a simple test on the keyboard; issue the ECHO command and see 725 * if the right answer is returned. We don't do anything as drastic as 726 * full keyboard reset; it will be too troublesome and take too much time. 727 */ 728static int 729probe_keyboard(void) 730{ 731 int retry = PROBE_MAXRETRY; 732 int wait; 733 int i; 734 735 while (--retry >= 0) { 736 /* flush any noise */ 737 while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) { 738 delay7(); 739 inb(IO_KBD + KBD_DATA_PORT); 740 delay1ms(); 741 } 742 743 /* wait until the controller can accept a command */ 744 for (wait = PROBE_MAXWAIT; wait > 0; --wait) { 745 if (((i = inb(IO_KBD + KBD_STATUS_PORT)) 746 & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0) 747 break; 748 if (i & KBDS_ANY_BUFFER_FULL) { 749 delay7(); 750 inb(IO_KBD + KBD_DATA_PORT); 751 } 752 delay1ms(); 753 } 754 if (wait <= 0) 755 continue; 756 757 /* send the ECHO command */ 758 outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO); 759 760 /* wait for a response */ 761 for (wait = PROBE_MAXWAIT; wait > 0; --wait) { 762 if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) 763 break; 764 delay1ms(); 765 } 766 if (wait <= 0) 767 continue; 768 769 delay7(); 770 i = inb(IO_KBD + KBD_DATA_PORT); 771#ifdef PROBE_KBD_BEBUG 772 printf("probe_keyboard: got 0x%x.\n", i); 773#endif 774 if (i == KBD_ECHO) { 775 /* got the right answer */ 776 return (0); 777 } 778 } 779 780 return (1); 781} 782#endif 783#endif /* KEYBOARD_PROBE */ 784 785#ifdef TERM_EMU 786#ifdef PC98 787static u_char ibmpc_to_pc98[16] = 788 { 0x01,0x21,0x81,0xa1,0x41,0x61,0xc1,0xe1, 0x09,0x29,0x89,0xa9,0x49,0x69,0xc9,0xe9 }; 789static u_char ibmpc_to_pc98rev[16] = 790 { 0x05,0x25,0x85,0xa5,0x45,0x65,0xc5,0xe5, 0x0d,0x2d,0x8d,0xad,0x4d,0x6d,0xcd,0xed }; 791 792unsigned int 793at2pc98(unsigned int fg_at, unsigned int bg_at) 794{ 795 unsigned int at; 796 797 if (bg_at) { 798 if (bg_at & 0x80) { 799 if (bg_at & 0x70) { 800 /* reverse & blink */ 801 at = ibmpc_to_pc98rev[bg_at >> 4] | 0x02; 802 } else { 803 /* normal & blink */ 804 at = ibmpc_to_pc98[fg_at] | 0x02; 805 } 806 } else { 807 /* reverse */ 808 at = ibmpc_to_pc98rev[bg_at >> 4]; 809 } 810 } else { 811 /* normal */ 812 at = ibmpc_to_pc98[fg_at]; 813 } 814 at |= ((fg_at|bg_at) << 8); 815 return (at); 816} 817#endif 818#endif 819