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