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