1 /* Code and data for the IBM console driver. 2 * 3 * The 6845 video controller used by the IBM PC shares its video memory with 4 * the CPU somewhere in the 0xB0000 memory bank. To the 6845 this memory 5 * consists of 16-bit words. Each word has a character code in the low byte 6 * and a so-called attribute byte in the high byte. The CPU directly modifies 7 * video memory to display characters, and sets two registers on the 6845 that 8 * specify the video origin and the cursor position. The video origin is the 9 * place in video memory where the first character (upper left corner) can 10 * be found. Moving the origin is a fast way to scroll the screen. Some 11 * video adapters wrap around the top of video memory, so the origin can 12 * move without bounds. For other adapters screen memory must sometimes be 13 * moved to reset the origin. All computations on video memory use character 14 * (word) addresses for simplicity and assume there is no wrapping. The 15 * assembly support functions translate the word addresses to byte addresses 16 * and the scrolling function worries about wrapping. 17 */ 18 19 #include <minix/drivers.h> 20 #include <termios.h> 21 #include <assert.h> 22 #include <sys/ioctl.h> 23 #include <sys/video.h> 24 #include <sys/mman.h> 25 #include <sys/termios.h> 26 #include <minix/callnr.h> 27 #include <minix/com.h> 28 #include <minix/sys_config.h> 29 #include <minix/vm.h> 30 #include "tty.h" 31 32 /* Set this to 1 if you want console output duplicated on the first 33 * serial line. 34 */ 35 #define DUP_CONS_TO_SER 0 36 37 /* The clock task should provide an interface for this */ 38 #define TIMER_FREQ 1193182L /* clock frequency for timer in PC and AT */ 39 40 /* Global variables used by the console driver and assembly support. */ 41 static phys_bytes vid_size; /* 0x2000 for color or 0x0800 for mono */ 42 static phys_bytes vid_base; 43 static unsigned vid_mask; /* 0x1FFF for color or 0x07FF for mono */ 44 static unsigned blank_color = BLANK_COLOR; /* display code for blank */ 45 46 /* Private variables used by the console driver. */ 47 static int vid_port; /* I/O port for accessing 6845 */ 48 static int wrap; /* hardware can wrap? */ 49 static int softscroll; /* 1 = software scrolling, 0 = hardware */ 50 static int beeping; /* speaker is beeping? */ 51 static long disable_beep = -1; /* do not use speaker if set to 1 */ 52 static unsigned font_lines; /* font lines per character */ 53 static unsigned scr_width; /* # characters on a line */ 54 static unsigned scr_lines; /* # lines on the screen */ 55 static unsigned scr_size; /* # characters on the screen */ 56 57 /* tells mem_vid_copy() to blank the screen */ 58 #define BLANK_MEM ((vir_bytes) 0) 59 60 static int disabled_vc = -1; /* Virtual console that was active when 61 * disable_console was called. 62 */ 63 static int disabled_sm; /* Scroll mode to be restored when re-enabling 64 * console 65 */ 66 67 static char *console_memory = NULL; 68 static char *font_memory = NULL; 69 70 /* Per console data. */ 71 typedef struct console { 72 tty_t *c_tty; /* associated TTY struct */ 73 int c_column; /* current column number (0-origin) */ 74 int c_row; /* current row (0 at top of screen) */ 75 int c_rwords; /* number of WORDS (not bytes) in outqueue */ 76 unsigned c_start; /* start of video memory of this console */ 77 unsigned c_limit; /* limit of this console's video memory */ 78 unsigned c_org; /* location in RAM where 6845 base points */ 79 unsigned c_cur; /* current position of cursor in video RAM */ 80 unsigned c_attr; /* character attribute */ 81 unsigned c_blank; /* blank attribute */ 82 char c_reverse; /* reverse video */ 83 char c_esc_state; /* 0=normal, 1=ESC, 2=ESC[ */ 84 char c_esc_intro; /* Distinguishing character following ESC */ 85 int *c_esc_parmp; /* pointer to current escape parameter */ 86 int c_esc_parmv[MAX_ESC_PARMS]; /* list of escape parameters */ 87 u16_t c_ramqueue[CONS_RAM_WORDS]; /* buffer for video RAM */ 88 int c_line; /* line no */ 89 } console_t; 90 91 #define UPDATE_CURSOR(ccons, cursor) { \ 92 ccons->c_cur = cursor; \ 93 if(curcons && ccons == curcons) \ 94 set_6845(CURSOR, ccons->c_cur); \ 95 } 96 97 #define UPDATE_ORIGIN(ccons, origin) { \ 98 ccons->c_org = origin; \ 99 if (curcons && ccons == curcons) \ 100 set_6845(VID_ORG, ccons->c_org); \ 101 } 102 103 static int nr_cons= 1; /* actual number of consoles */ 104 static console_t cons_table[NR_CONS]; 105 static console_t *curcons = NULL; /* currently visible */ 106 107 static int shutting_down = FALSE; /* don't allow console switches */ 108 109 /* Color if using a color controller. */ 110 #define color (vid_port == C_6845) 111 112 /* Map from ANSI colors to the attributes used by the PC */ 113 static int ansi_colors[8] = {0, 4, 2, 6, 1, 5, 3, 7}; 114 115 /* Structure used for font management */ 116 struct sequence { 117 unsigned short index; 118 unsigned char port; 119 unsigned char value; 120 }; 121 122 static int cons_write(struct tty *tp, int try); 123 static void cons_echo(tty_t *tp, int c); 124 static void out_char(console_t *cons, int c); 125 static void beep(void); 126 static void do_escape(console_t *cons, int c); 127 static void flush(console_t *cons); 128 static void parse_escape(console_t *cons, int c); 129 static void scroll_screen(console_t *cons, int dir); 130 static void set_6845(int reg, unsigned val); 131 static void stop_beep(int arg); 132 static void cons_org0(void); 133 static void disable_console(void); 134 static void reenable_console(void); 135 static int ga_program(struct sequence *seq); 136 static int cons_ioctl(tty_t *tp, int); 137 static void mem_vid_copy(vir_bytes src, int dst, int count); 138 static void vid_vid_copy(int src, int dst, int count); 139 140 #if 0 141 static void get_6845(int reg, unsigned *val); 142 #endif 143 144 static int video_open(devminor_t minor, int access, endpoint_t user_endpt); 145 static int video_close(devminor_t minor); 146 static int video_ioctl(devminor_t minor, unsigned long request, 147 endpoint_t endpt, cp_grant_id_t grant, int flags, 148 endpoint_t user_endpt, cdev_id_t id); 149 150 static struct chardriver video_tab = { 151 .cdr_open = video_open, 152 .cdr_close = video_close, 153 .cdr_ioctl = video_ioctl 154 }; 155 156 /*===========================================================================* 157 * cons_write * 158 *===========================================================================*/ 159 static int cons_write(tp, try) 160 register struct tty *tp; /* tells which terminal is to be used */ 161 int try; 162 { 163 /* Copy as much data as possible to the output queue, then start I/O. On 164 * memory-mapped terminals, such as the IBM console, the I/O will also be 165 * finished, and the counts updated. Keep repeating until all I/O done. 166 */ 167 168 int count; 169 int result = OK; 170 register char *tbuf; 171 char buf[64]; 172 console_t *cons = tp->tty_priv; 173 174 if (try) return 1; /* we can always write to console */ 175 176 /* Check quickly for nothing to do, so this can be called often without 177 * unmodular tests elsewhere. 178 */ 179 if ((count = tp->tty_outleft) == 0 || tp->tty_inhibited) return 0; 180 181 /* Copy the user bytes to buf[] for decent addressing. Loop over the 182 * copies, since the user buffer may be much larger than buf[]. 183 */ 184 do { 185 if (count > sizeof(buf)) count = sizeof(buf); 186 if (tp->tty_outcaller == KERNEL) { 187 /* We're trying to print on kernel's behalf */ 188 memcpy(buf, (char *) tp->tty_outgrant + tp->tty_outcum, count); 189 } else { 190 if ((result = sys_safecopyfrom(tp->tty_outcaller, 191 tp->tty_outgrant, tp->tty_outcum, 192 (vir_bytes) buf, count)) != OK) { 193 break; 194 } 195 } 196 tbuf = buf; 197 198 /* Update terminal data structure. */ 199 tp->tty_outcum += count; 200 tp->tty_outleft -= count; 201 202 /* Output each byte of the copy to the screen. Avoid calling 203 * out_char() for the "easy" characters, put them into the buffer 204 * directly. 205 */ 206 do { 207 if ((unsigned) *tbuf < ' ' || cons->c_esc_state > 0 208 || cons->c_column >= scr_width 209 || cons->c_rwords >= buflen(cons->c_ramqueue)) 210 { 211 out_char(cons, *tbuf++); 212 } else { 213 #if DUP_CONS_TO_SER 214 if (cons == &cons_table[0]) ser_putc(*tbuf); 215 #endif 216 cons->c_ramqueue[cons->c_rwords++] = 217 cons->c_attr | (*tbuf++ & BYTE); 218 cons->c_column++; 219 } 220 } while (--count != 0); 221 } while ((count = tp->tty_outleft) != 0 && !tp->tty_inhibited); 222 223 flush(cons); /* transfer anything buffered to the screen */ 224 225 /* Reply to the writer if all output is finished or if an error occurred. */ 226 if (tp->tty_outleft == 0 || result != OK) { 227 if (tp->tty_outcaller != KERNEL) 228 chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, 229 result != OK ? result : tp->tty_outcum); 230 tp->tty_outcum = tp->tty_outleft = 0; 231 tp->tty_outcaller = NONE; 232 } 233 234 return 0; 235 } 236 237 /*===========================================================================* 238 * cons_echo * 239 *===========================================================================*/ 240 static void cons_echo(tp, c) 241 register tty_t *tp; /* pointer to tty struct */ 242 int c; /* character to be echoed */ 243 { 244 /* Echo keyboard input (print & flush). */ 245 console_t *cons = tp->tty_priv; 246 247 out_char(cons, c); 248 flush(cons); 249 } 250 251 /*===========================================================================* 252 * out_char * 253 *===========================================================================*/ 254 static void out_char(cons, c) 255 register console_t *cons; /* pointer to console struct */ 256 int c; /* character to be output */ 257 { 258 /* Output a character on the console. Check for escape sequences first. */ 259 if (cons->c_esc_state > 0) { 260 parse_escape(cons, c); 261 return; 262 } 263 264 #if DUP_CONS_TO_SER 265 if (cons == &cons_table[0] && c != '\0') 266 { 267 if (c == '\n') 268 ser_putc('\r'); 269 ser_putc(c); 270 } 271 #endif 272 273 switch(c) { 274 case 000: /* null is typically used for padding */ 275 return; /* better not do anything */ 276 277 case 007: /* ring the bell */ 278 flush(cons); /* print any chars queued for output */ 279 beep(); 280 return; 281 282 case '\b': /* backspace */ 283 if (--cons->c_column < 0) { 284 if (--cons->c_row >= 0) cons->c_column += scr_width; 285 } 286 flush(cons); 287 return; 288 289 case '\n': /* line feed */ 290 if ((cons->c_tty->tty_termios.c_oflag & (OPOST|ONLCR)) 291 == (OPOST|ONLCR)) { 292 cons->c_column = 0; 293 } 294 /*FALL THROUGH*/ 295 case 013: /* CTRL-K */ 296 case 014: /* CTRL-L */ 297 if (cons->c_row == scr_lines-1) { 298 scroll_screen(cons, SCROLL_UP); 299 } else { 300 cons->c_row++; 301 } 302 flush(cons); 303 return; 304 305 case '\r': /* carriage return */ 306 cons->c_column = 0; 307 flush(cons); 308 return; 309 310 case '\t': /* tab */ 311 cons->c_column = (cons->c_column + TAB_SIZE) & ~TAB_MASK; 312 if (cons->c_column > scr_width) { 313 cons->c_column -= scr_width; 314 if (cons->c_row == scr_lines-1) { 315 scroll_screen(cons, SCROLL_UP); 316 } else { 317 cons->c_row++; 318 } 319 } 320 flush(cons); 321 return; 322 323 case 033: /* ESC - start of an escape sequence */ 324 flush(cons); /* print any chars queued for output */ 325 cons->c_esc_state = 1; /* mark ESC as seen */ 326 return; 327 328 default: /* printable chars are stored in ramqueue */ 329 if (cons->c_column >= scr_width) { 330 if (!LINEWRAP) return; 331 if (cons->c_row == scr_lines-1) { 332 scroll_screen(cons, SCROLL_UP); 333 } else { 334 cons->c_row++; 335 } 336 cons->c_column = 0; 337 flush(cons); 338 } 339 if (cons->c_rwords == buflen(cons->c_ramqueue)) flush(cons); 340 cons->c_ramqueue[cons->c_rwords++] = cons->c_attr | (c & BYTE); 341 cons->c_column++; /* next column */ 342 return; 343 } 344 } 345 346 /*===========================================================================* 347 * scroll_screen * 348 *===========================================================================*/ 349 static void scroll_screen(cons, dir) 350 register console_t *cons; /* pointer to console struct */ 351 int dir; /* SCROLL_UP or SCROLL_DOWN */ 352 { 353 unsigned new_line, new_org, chars; 354 355 flush(cons); 356 chars = scr_size - scr_width; /* one screen minus one line */ 357 358 /* Scrolling the screen is a real nuisance due to the various incompatible 359 * video cards. This driver supports software scrolling (Hercules?), 360 * hardware scrolling (mono and CGA cards) and hardware scrolling without 361 * wrapping (EGA cards). In the latter case we must make sure that 362 * c_start <= c_org && c_org + scr_size <= c_limit 363 * holds, because EGA doesn't wrap around the end of video memory. 364 */ 365 if (dir == SCROLL_UP) { 366 /* Scroll one line up in 3 ways: soft, avoid wrap, use origin. */ 367 if (softscroll) { 368 vid_vid_copy(cons->c_start + scr_width, cons->c_start, chars); 369 } else 370 if (!wrap && cons->c_org + scr_size + scr_width >= cons->c_limit) { 371 vid_vid_copy(cons->c_org + scr_width, cons->c_start, chars); 372 UPDATE_ORIGIN(cons, cons->c_start); 373 } else { 374 UPDATE_ORIGIN(cons, (cons->c_org + scr_width) & vid_mask); 375 } 376 new_line = (cons->c_org + chars) & vid_mask; 377 } else { 378 /* Scroll one line down in 3 ways: soft, avoid wrap, use origin. */ 379 if (softscroll) { 380 vid_vid_copy(cons->c_start, cons->c_start + scr_width, chars); 381 } else 382 if (!wrap && cons->c_org < cons->c_start + scr_width) { 383 new_org = cons->c_limit - scr_size; 384 vid_vid_copy(cons->c_org, new_org + scr_width, chars); 385 UPDATE_ORIGIN(cons, new_org); 386 } else { 387 UPDATE_ORIGIN(cons, (cons->c_org - scr_width) & vid_mask); 388 } 389 new_line = cons->c_org; 390 } 391 /* Blank the new line at top or bottom. */ 392 blank_color = cons->c_blank; 393 mem_vid_copy(BLANK_MEM, new_line, scr_width); 394 395 flush(cons); 396 } 397 398 /*===========================================================================* 399 * flush * 400 *===========================================================================*/ 401 static void flush(cons) 402 register console_t *cons; /* pointer to console struct */ 403 { 404 /* Send characters buffered in 'ramqueue' to screen memory, check the new 405 * cursor position, compute the new hardware cursor position and set it. 406 */ 407 unsigned cur; 408 tty_t *tp = cons->c_tty; 409 410 /* Have the characters in 'ramqueue' transferred to the screen. */ 411 if (cons->c_rwords > 0) { 412 mem_vid_copy((vir_bytes) cons->c_ramqueue, cons->c_cur, cons->c_rwords); 413 cons->c_rwords = 0; 414 415 /* TTY likes to know the current column and if echoing messed up. */ 416 tp->tty_position = cons->c_column; 417 tp->tty_reprint = TRUE; 418 } 419 420 /* Check and update the cursor position. */ 421 if (cons->c_column < 0) cons->c_column = 0; 422 if (cons->c_column > scr_width) cons->c_column = scr_width; 423 if (cons->c_row < 0) cons->c_row = 0; 424 if (cons->c_row >= scr_lines) cons->c_row = scr_lines - 1; 425 cur = cons->c_org + cons->c_row * scr_width + cons->c_column; 426 if (cur != cons->c_cur) 427 UPDATE_CURSOR(cons, cur); 428 } 429 430 /*===========================================================================* 431 * parse_escape * 432 *===========================================================================*/ 433 static void parse_escape(cons, c) 434 register console_t *cons; /* pointer to console struct */ 435 char c; /* next character in escape sequence */ 436 { 437 /* The following ANSI escape sequences are currently supported. 438 * If n and/or m are omitted, they default to 1. 439 * ESC [nA moves up n lines 440 * ESC [nB moves down n lines 441 * ESC [nC moves right n spaces 442 * ESC [nD moves left n spaces 443 * ESC [m;nH" moves cursor to (m,n) 444 * ESC [J clears screen from cursor 445 * ESC [K clears line from cursor 446 * ESC [nL inserts n lines ar cursor 447 * ESC [nM deletes n lines at cursor 448 * ESC [nP deletes n chars at cursor 449 * ESC [n@ inserts n chars at cursor 450 * ESC [nm enables rendition n (0=normal, 4=bold, 5=blinking, 7=reverse) 451 * ESC M scrolls the screen backwards if the cursor is on the top line 452 */ 453 454 switch (cons->c_esc_state) { 455 case 1: /* ESC seen */ 456 cons->c_esc_intro = '\0'; 457 cons->c_esc_parmp = bufend(cons->c_esc_parmv); 458 do { 459 *--cons->c_esc_parmp = 0; 460 } while (cons->c_esc_parmp > cons->c_esc_parmv); 461 switch (c) { 462 case '[': /* Control Sequence Introducer */ 463 cons->c_esc_intro = c; 464 cons->c_esc_state = 2; 465 break; 466 case 'M': /* Reverse Index */ 467 do_escape(cons, c); 468 break; 469 default: 470 cons->c_esc_state = 0; 471 } 472 break; 473 474 case 2: /* ESC [ seen */ 475 if (c >= '0' && c <= '9') { 476 if (cons->c_esc_parmp < bufend(cons->c_esc_parmv)) 477 *cons->c_esc_parmp = *cons->c_esc_parmp * 10 + (c-'0'); 478 } else 479 if (c == ';') { 480 if (cons->c_esc_parmp < bufend(cons->c_esc_parmv)) 481 cons->c_esc_parmp++; 482 } else { 483 do_escape(cons, c); 484 } 485 break; 486 } 487 } 488 489 /*===========================================================================* 490 * do_escape * 491 *===========================================================================*/ 492 static void do_escape(cons, c) 493 register console_t *cons; /* pointer to console struct */ 494 char c; /* next character in escape sequence */ 495 { 496 int value, n; 497 unsigned src, dst, count; 498 int *parmp; 499 500 /* Some of these things hack on screen RAM, so it had better be up to date */ 501 flush(cons); 502 503 if (cons->c_esc_intro == '\0') { 504 /* Handle a sequence beginning with just ESC */ 505 switch (c) { 506 case 'M': /* Reverse Index */ 507 if (cons->c_row == 0) { 508 scroll_screen(cons, SCROLL_DOWN); 509 } else { 510 cons->c_row--; 511 } 512 flush(cons); 513 break; 514 515 default: break; 516 } 517 } else 518 if (cons->c_esc_intro == '[') { 519 /* Handle a sequence beginning with ESC [ and parameters */ 520 value = cons->c_esc_parmv[0]; 521 switch (c) { 522 case 'A': /* ESC [nA moves up n lines */ 523 n = (value == 0 ? 1 : value); 524 cons->c_row -= n; 525 flush(cons); 526 break; 527 528 case 'B': /* ESC [nB moves down n lines */ 529 n = (value == 0 ? 1 : value); 530 cons->c_row += n; 531 flush(cons); 532 break; 533 534 case 'C': /* ESC [nC moves right n spaces */ 535 n = (value == 0 ? 1 : value); 536 cons->c_column += n; 537 flush(cons); 538 break; 539 540 case 'D': /* ESC [nD moves left n spaces */ 541 n = (value == 0 ? 1 : value); 542 cons->c_column -= n; 543 flush(cons); 544 break; 545 546 case 'H': /* ESC [m;nH" moves cursor to (m,n) */ 547 cons->c_row = cons->c_esc_parmv[0] - 1; 548 cons->c_column = cons->c_esc_parmv[1] - 1; 549 flush(cons); 550 break; 551 552 case 'J': /* ESC [sJ clears in display */ 553 switch (value) { 554 case 0: /* Clear from cursor to end of screen */ 555 count = scr_size - (cons->c_cur - cons->c_org); 556 dst = cons->c_cur; 557 break; 558 case 1: /* Clear from start of screen to cursor */ 559 count = cons->c_cur - cons->c_org; 560 dst = cons->c_org; 561 break; 562 case 2: /* Clear entire screen */ 563 count = scr_size; 564 dst = cons->c_org; 565 break; 566 default: /* Do nothing */ 567 count = 0; 568 dst = cons->c_org; 569 } 570 blank_color = cons->c_blank; 571 mem_vid_copy(BLANK_MEM, dst, count); 572 break; 573 574 case 'K': /* ESC [sK clears line from cursor */ 575 switch (value) { 576 case 0: /* Clear from cursor to end of line */ 577 count = scr_width - cons->c_column; 578 dst = cons->c_cur; 579 break; 580 case 1: /* Clear from beginning of line to cursor */ 581 count = cons->c_column; 582 dst = cons->c_cur - cons->c_column; 583 break; 584 case 2: /* Clear entire line */ 585 count = scr_width; 586 dst = cons->c_cur - cons->c_column; 587 break; 588 default: /* Do nothing */ 589 count = 0; 590 dst = cons->c_cur; 591 } 592 blank_color = cons->c_blank; 593 mem_vid_copy(BLANK_MEM, dst, count); 594 break; 595 596 case 'L': /* ESC [nL inserts n lines at cursor */ 597 n = value; 598 if (n < 1) n = 1; 599 if (n > (scr_lines - cons->c_row)) 600 n = scr_lines - cons->c_row; 601 602 src = cons->c_org + cons->c_row * scr_width; 603 dst = src + n * scr_width; 604 count = (scr_lines - cons->c_row - n) * scr_width; 605 vid_vid_copy(src, dst, count); 606 blank_color = cons->c_blank; 607 mem_vid_copy(BLANK_MEM, src, n * scr_width); 608 break; 609 610 case 'M': /* ESC [nM deletes n lines at cursor */ 611 n = value; 612 if (n < 1) n = 1; 613 if (n > (scr_lines - cons->c_row)) 614 n = scr_lines - cons->c_row; 615 616 dst = cons->c_org + cons->c_row * scr_width; 617 src = dst + n * scr_width; 618 count = (scr_lines - cons->c_row - n) * scr_width; 619 vid_vid_copy(src, dst, count); 620 blank_color = cons->c_blank; 621 mem_vid_copy(BLANK_MEM, dst + count, n * scr_width); 622 break; 623 624 case '@': /* ESC [n@ inserts n chars at cursor */ 625 n = value; 626 if (n < 1) n = 1; 627 if (n > (scr_width - cons->c_column)) 628 n = scr_width - cons->c_column; 629 630 src = cons->c_cur; 631 dst = src + n; 632 count = scr_width - cons->c_column - n; 633 vid_vid_copy(src, dst, count); 634 blank_color = cons->c_blank; 635 mem_vid_copy(BLANK_MEM, src, n); 636 break; 637 638 case 'P': /* ESC [nP deletes n chars at cursor */ 639 n = value; 640 if (n < 1) n = 1; 641 if (n > (scr_width - cons->c_column)) 642 n = scr_width - cons->c_column; 643 644 dst = cons->c_cur; 645 src = dst + n; 646 count = scr_width - cons->c_column - n; 647 vid_vid_copy(src, dst, count); 648 blank_color = cons->c_blank; 649 mem_vid_copy(BLANK_MEM, dst + count, n); 650 break; 651 652 case 'm': /* ESC [nm enables rendition n */ 653 for (parmp = cons->c_esc_parmv; parmp <= cons->c_esc_parmp 654 && parmp < bufend(cons->c_esc_parmv); parmp++) { 655 if (cons->c_reverse) { 656 /* Unswap fg and bg colors */ 657 cons->c_attr = ((cons->c_attr & 0x7000) >> 4) | 658 ((cons->c_attr & 0x0700) << 4) | 659 ((cons->c_attr & 0x8800)); 660 } 661 switch (n = *parmp) { 662 case 0: /* NORMAL */ 663 cons->c_attr = cons->c_blank = BLANK_COLOR; 664 cons->c_reverse = FALSE; 665 break; 666 667 case 1: /* BOLD */ 668 /* Set intensity bit */ 669 cons->c_attr |= 0x0800; 670 break; 671 672 case 4: /* UNDERLINE */ 673 if (color) { 674 /* Change white to cyan, i.e. lose red 675 */ 676 cons->c_attr = (cons->c_attr & 0xBBFF); 677 } else { 678 /* Set underline attribute */ 679 cons->c_attr = (cons->c_attr & 0x99FF); 680 } 681 break; 682 683 case 5: /* BLINKING */ 684 /* Set the blink bit */ 685 cons->c_attr |= 0x8000; 686 break; 687 688 case 7: /* REVERSE */ 689 cons->c_reverse = TRUE; 690 break; 691 692 default: /* COLOR */ 693 if (n == 39) n = 37; /* set default color */ 694 if (n == 49) n = 40; 695 696 if (!color) { 697 /* Don't mess up a monochrome screen */ 698 } else 699 if (30 <= n && n <= 37) { 700 /* Foreground color */ 701 cons->c_attr = 702 (cons->c_attr & 0xF8FF) | 703 (ansi_colors[(n - 30)] << 8); 704 cons->c_blank = 705 (cons->c_blank & 0xF8FF) | 706 (ansi_colors[(n - 30)] << 8); 707 } else 708 if (40 <= n && n <= 47) { 709 /* Background color */ 710 cons->c_attr = 711 (cons->c_attr & 0x8FFF) | 712 (ansi_colors[(n - 40)] << 12); 713 cons->c_blank = 714 (cons->c_blank & 0x8FFF) | 715 (ansi_colors[(n - 40)] << 12); 716 } 717 } 718 if (cons->c_reverse) { 719 /* Swap fg and bg colors */ 720 cons->c_attr = ((cons->c_attr & 0x7000) >> 4) | 721 ((cons->c_attr & 0x0700) << 4) | 722 ((cons->c_attr & 0x8800)); 723 } 724 } 725 break; 726 } 727 } 728 cons->c_esc_state = 0; 729 } 730 731 /*===========================================================================* 732 * set_6845 * 733 *===========================================================================*/ 734 static void set_6845(reg, val) 735 int reg; /* which register pair to set */ 736 unsigned val; /* 16-bit value to set it to */ 737 { 738 /* Set a register pair inside the 6845. 739 * Registers 12-13 tell the 6845 where in video ram to start 740 * Registers 14-15 tell the 6845 where to put the cursor 741 */ 742 pvb_pair_t char_out[4]; 743 pv_set(char_out[0], vid_port + INDEX, reg); /* set index register */ 744 pv_set(char_out[1], vid_port + DATA, (val>>8) & BYTE); /* high byte */ 745 pv_set(char_out[2], vid_port + INDEX, reg + 1); /* again */ 746 pv_set(char_out[3], vid_port + DATA, val&BYTE); /* low byte */ 747 sys_voutb(char_out, 4); /* do actual output */ 748 } 749 750 #if 0 751 /*===========================================================================* 752 * get_6845 * 753 *===========================================================================*/ 754 static void get_6845(reg, val) 755 int reg; /* which register pair to set */ 756 unsigned *val; /* 16-bit value to set it to */ 757 { 758 char v1, v2; 759 u32_t v; 760 /* Get a register pair inside the 6845. */ 761 sys_outb(vid_port + INDEX, reg); 762 sys_inb(vid_port + DATA, &v); 763 v1 = v; 764 sys_outb(vid_port + INDEX, reg+1); 765 sys_inb(vid_port + DATA, &v); 766 v2 = v; 767 *val = (v1 << 8) | v2; 768 } 769 #endif 770 771 /*===========================================================================* 772 * beep_disabled * 773 *===========================================================================*/ 774 static long beep_disabled(void) 775 { 776 /* Return whether the user requested that beeps not be performed. 777 */ 778 779 /* Perform first-time initialization if necessary. */ 780 if (disable_beep < 0) { 781 disable_beep = 0; /* the default is on */ 782 783 (void) env_parse("nobeep", "d", 0, &disable_beep, 0, 1); 784 } 785 786 return disable_beep; 787 } 788 789 /*===========================================================================* 790 * beep * 791 *===========================================================================*/ 792 static void beep() 793 { 794 /* Making a beeping sound on the speaker (output for CRTL-G). 795 * This routine works by turning on the bits 0 and 1 in port B of the 8255 796 * chip that drive the speaker. 797 */ 798 static minix_timer_t tmr_stop_beep; 799 pvb_pair_t char_out[3]; 800 u32_t port_b_val; 801 802 if (beep_disabled()) return; 803 804 /* Set timer in advance to prevent beeping delay. */ 805 set_timer(&tmr_stop_beep, B_TIME, stop_beep, 0); 806 807 if (!beeping) { 808 /* Set timer channel 2, square wave, with given frequency. */ 809 pv_set(char_out[0], TIMER_MODE, 0xB6); 810 pv_set(char_out[1], TIMER2, (BEEP_FREQ >> 0) & BYTE); 811 pv_set(char_out[2], TIMER2, (BEEP_FREQ >> 8) & BYTE); 812 if (sys_voutb(char_out, 3)==OK) { 813 if (sys_inb(PORT_B, &port_b_val)==OK && 814 sys_outb(PORT_B, (port_b_val|3))==OK) 815 beeping = TRUE; 816 } 817 } 818 } 819 820 /*===========================================================================* 821 * video_open * 822 *===========================================================================*/ 823 static int video_open(devminor_t minor, int UNUSED(access), 824 endpoint_t UNUSED(user_endpt)) 825 { 826 /* Should grant IOPL */ 827 disable_console(); 828 return OK; 829 } 830 831 /*===========================================================================* 832 * video_close * 833 *===========================================================================*/ 834 static int video_close(devminor_t minor) 835 { 836 reenable_console(); 837 return OK; 838 } 839 840 /*===========================================================================* 841 * video_ioctl * 842 *===========================================================================*/ 843 static int video_ioctl(devminor_t minor, unsigned long request, 844 endpoint_t endpt, cp_grant_id_t grant, int flags, 845 endpoint_t user_endpt, cdev_id_t id) 846 { 847 return ENOTTY; 848 } 849 850 /*===========================================================================* 851 * do_video * 852 *===========================================================================*/ 853 void do_video(message *m, int ipc_status) 854 { 855 chardriver_process(&video_tab, m, ipc_status); 856 } 857 858 /*===========================================================================* 859 * beep_x * 860 *===========================================================================*/ 861 void beep_x(freq, dur) 862 unsigned freq; 863 clock_t dur; 864 { 865 /* Making a beeping sound on the speaker. 866 * This routine works by turning on the bits 0 and 1 in port B of the 8255 867 * chip that drive the speaker. 868 */ 869 static minix_timer_t tmr_stop_beep; 870 pvb_pair_t char_out[3]; 871 u32_t port_b_val; 872 873 if (beep_disabled()) return; 874 875 unsigned long ival= TIMER_FREQ / freq; 876 if (ival == 0 || ival > 0xffff) 877 return; /* Frequency out of range */ 878 879 /* Set timer in advance to prevent beeping delay. */ 880 set_timer(&tmr_stop_beep, dur, stop_beep, 0); 881 882 if (!beeping) { 883 /* Set timer channel 2, square wave, with given frequency. */ 884 pv_set(char_out[0], TIMER_MODE, 0xB6); 885 pv_set(char_out[1], TIMER2, (ival >> 0) & BYTE); 886 pv_set(char_out[2], TIMER2, (ival >> 8) & BYTE); 887 if (sys_voutb(char_out, 3)==OK) { 888 if (sys_inb(PORT_B, &port_b_val)==OK && 889 sys_outb(PORT_B, (port_b_val|3))==OK) 890 beeping = TRUE; 891 } 892 } 893 } 894 895 /*===========================================================================* 896 * stop_beep * 897 *===========================================================================*/ 898 static void stop_beep(int arg __unused) 899 { 900 /* Turn off the beeper by turning off bits 0 and 1 in PORT_B. */ 901 u32_t port_b_val; 902 if (sys_inb(PORT_B, &port_b_val)==OK && 903 sys_outb(PORT_B, (port_b_val & ~3))==OK) 904 beeping = FALSE; 905 } 906 907 /*===========================================================================* 908 * scr_init * 909 *===========================================================================*/ 910 void scr_init(tp) 911 tty_t *tp; 912 { 913 /* Initialize the screen driver. */ 914 console_t *cons; 915 u16_t bios_columns, bios_crtbase, bios_fontlines; 916 u8_t bios_rows; 917 int line; 918 int s; 919 static int vdu_initialized = 0; 920 static unsigned page_size; 921 922 /* Associate console and TTY. */ 923 line = tp - &tty_table[0]; 924 if (line >= nr_cons) return; 925 cons = &cons_table[line]; 926 cons->c_tty = tp; 927 cons->c_line = line; 928 tp->tty_priv = cons; 929 930 /* Fill in TTY function hooks. */ 931 tp->tty_devwrite = cons_write; 932 tp->tty_echo = cons_echo; 933 tp->tty_ioctl = cons_ioctl; 934 935 /* Get the BIOS parameters that describe the VDU. */ 936 if (! vdu_initialized++) { 937 938 /* FIXME: How about error checking? What to do on failure??? */ 939 s=sys_readbios(VDU_SCREEN_COLS_ADDR, &bios_columns, 940 VDU_SCREEN_COLS_SIZE); 941 s=sys_readbios(VDU_CRT_BASE_ADDR, &bios_crtbase, 942 VDU_CRT_BASE_SIZE); 943 s=sys_readbios( VDU_SCREEN_ROWS_ADDR, &bios_rows, 944 VDU_SCREEN_ROWS_SIZE); 945 s=sys_readbios(VDU_FONTLINES_ADDR, &bios_fontlines, 946 VDU_FONTLINES_SIZE); 947 948 vid_port = bios_crtbase; 949 scr_width = bios_columns; 950 font_lines = bios_fontlines; 951 scr_lines = bios_rows+1; 952 953 if (color) { 954 vid_base = COLOR_BASE; 955 vid_size = COLOR_SIZE; 956 } else { 957 vid_base = MONO_BASE; 958 vid_size = MONO_SIZE; 959 } 960 vid_size = EGA_SIZE; 961 wrap = 0; 962 963 console_memory = vm_map_phys(SELF, (void *) vid_base, vid_size); 964 965 if(console_memory == MAP_FAILED) 966 panic("Console couldn't map video memory"); 967 968 font_memory = vm_map_phys(SELF, (void *)GA_VIDEO_ADDRESS, GA_FONT_SIZE); 969 970 if(font_memory == MAP_FAILED) 971 panic("Console couldn't map font memory"); 972 973 vid_size >>= 1; /* word count */ 974 vid_mask = vid_size - 1; 975 976 /* Size of the screen (number of displayed characters.) */ 977 scr_size = scr_lines * scr_width; 978 979 /* There can be as many consoles as video memory allows. */ 980 nr_cons = vid_size / scr_size; 981 982 if (nr_cons > NR_CONS) nr_cons = NR_CONS; 983 if (nr_cons > 1) wrap = 0; 984 if (nr_cons < 1) panic("no consoles"); 985 page_size = vid_size / nr_cons; 986 } 987 988 cons->c_start = line * page_size; 989 cons->c_limit = cons->c_start + page_size; 990 cons->c_cur = cons->c_org = cons->c_start; 991 cons->c_attr = cons->c_blank = BLANK_COLOR; 992 993 if (line != 0) { 994 /* Clear the non-console vtys. */ 995 blank_color = BLANK_COLOR; 996 mem_vid_copy(BLANK_MEM, cons->c_start, scr_size); 997 } else { 998 /* Set the cursor of the console vty at the bottom. c_cur 999 * is updated automatically later. 1000 */ 1001 scroll_screen(cons, SCROLL_UP); 1002 cons->c_row = scr_lines - 1; 1003 cons->c_column = 0; 1004 } 1005 select_console(0); 1006 cons_ioctl(tp, 0); 1007 } 1008 1009 /*===========================================================================* 1010 * toggle_scroll * 1011 *===========================================================================*/ 1012 void toggle_scroll() 1013 { 1014 /* Toggle between hardware and software scroll. */ 1015 1016 cons_org0(); 1017 softscroll = !softscroll; 1018 printf("%sware scrolling enabled.\n", softscroll ? "Soft" : "Hard"); 1019 } 1020 1021 /*===========================================================================* 1022 * cons_stop * 1023 *===========================================================================*/ 1024 void cons_stop() 1025 { 1026 /* Prepare for halt or reboot. */ 1027 cons_org0(); 1028 softscroll = 1; 1029 select_console(0); 1030 cons_table[0].c_attr = cons_table[0].c_blank = BLANK_COLOR; 1031 shutting_down = TRUE; 1032 } 1033 1034 /*===========================================================================* 1035 * cons_org0 * 1036 *===========================================================================*/ 1037 static void cons_org0() 1038 { 1039 /* Scroll video memory back to put the origin at 0. */ 1040 int cons_line; 1041 console_t *cons; 1042 unsigned n; 1043 1044 for (cons_line = 0; cons_line < nr_cons; cons_line++) { 1045 cons = &cons_table[cons_line]; 1046 while (cons->c_org > cons->c_start) { 1047 n = vid_size - scr_size; /* amount of unused memory */ 1048 if (n > cons->c_org - cons->c_start) 1049 n = cons->c_org - cons->c_start; 1050 vid_vid_copy(cons->c_org, cons->c_org - n, scr_size); 1051 UPDATE_ORIGIN(cons, cons->c_org - n); 1052 } 1053 flush(cons); 1054 } 1055 select_console(ccurrent); 1056 } 1057 1058 /*===========================================================================* 1059 * disable_console * 1060 *===========================================================================*/ 1061 static void disable_console() 1062 { 1063 if (disabled_vc != -1) 1064 return; 1065 1066 disabled_vc = ccurrent; 1067 disabled_sm = softscroll; 1068 1069 cons_org0(); 1070 softscroll = 1; 1071 select_console(0); 1072 1073 /* Should also disable further output to virtual consoles */ 1074 } 1075 1076 /*===========================================================================* 1077 * reenable_console * 1078 *===========================================================================*/ 1079 static void reenable_console() 1080 { 1081 if (disabled_vc == -1) 1082 return; 1083 1084 softscroll = disabled_sm; 1085 select_console(disabled_vc); 1086 disabled_vc = -1; 1087 } 1088 1089 /*===========================================================================* 1090 * select_console * 1091 *===========================================================================*/ 1092 void select_console(int cons_line) 1093 { 1094 /* Set the current console to console number 'cons_line'. */ 1095 1096 if (shutting_down) return; 1097 1098 if (cons_line < 0 || cons_line >= nr_cons) return; 1099 1100 ccurrent = cons_line; 1101 curcons = &cons_table[cons_line]; 1102 1103 UPDATE_CURSOR(curcons, curcons->c_cur); 1104 UPDATE_ORIGIN(curcons, curcons->c_org); 1105 } 1106 1107 /*===========================================================================* 1108 * con_loadfont * 1109 *===========================================================================*/ 1110 int con_loadfont(endpoint_t endpt, cp_grant_id_t grant) 1111 { 1112 1113 /* Load a font into the EGA or VGA adapter. */ 1114 int r, r2; 1115 static struct sequence seq1[7] = { 1116 { GA_SEQUENCER_INDEX, 0x00, 0x01 }, 1117 { GA_SEQUENCER_INDEX, 0x02, 0x04 }, 1118 { GA_SEQUENCER_INDEX, 0x04, 0x07 }, 1119 { GA_SEQUENCER_INDEX, 0x00, 0x03 }, 1120 { GA_GRAPHICS_INDEX, 0x04, 0x02 }, 1121 { GA_GRAPHICS_INDEX, 0x05, 0x00 }, 1122 { GA_GRAPHICS_INDEX, 0x06, 0x00 }, 1123 }; 1124 static struct sequence seq2[7] = { 1125 { GA_SEQUENCER_INDEX, 0x00, 0x01 }, 1126 { GA_SEQUENCER_INDEX, 0x02, 0x03 }, 1127 { GA_SEQUENCER_INDEX, 0x04, 0x03 }, 1128 { GA_SEQUENCER_INDEX, 0x00, 0x03 }, 1129 { GA_GRAPHICS_INDEX, 0x04, 0x00 }, 1130 { GA_GRAPHICS_INDEX, 0x05, 0x10 }, 1131 { GA_GRAPHICS_INDEX, 0x06, 0 }, 1132 }; 1133 1134 seq2[6].value= color ? 0x0E : 0x0A; 1135 1136 r = ga_program(seq1); /* bring font memory into view */ 1137 if (r != OK) return r; 1138 1139 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) font_memory, GA_FONT_SIZE); 1140 1141 r2 = ga_program(seq2); /* restore */ 1142 1143 return(r != OK ? r : r2); 1144 } 1145 1146 /*===========================================================================* 1147 * ga_program * 1148 *===========================================================================*/ 1149 static int ga_program(seq) 1150 struct sequence *seq; 1151 { 1152 pvb_pair_t char_out[14]; 1153 int i; 1154 for (i=0; i<7; i++) { 1155 pv_set(char_out[2*i], seq->index, seq->port); 1156 pv_set(char_out[2*i+1], seq->index+1, seq->value); 1157 seq++; 1158 } 1159 return sys_voutb(char_out, 14); 1160 } 1161 1162 /*===========================================================================* 1163 * cons_ioctl * 1164 *===========================================================================*/ 1165 static int cons_ioctl(tty_t *tp, int UNUSED(try)) 1166 { 1167 /* Set the screen dimensions. */ 1168 1169 tp->tty_winsize.ws_row= scr_lines; 1170 tp->tty_winsize.ws_col= scr_width; 1171 tp->tty_winsize.ws_xpixel= scr_width * 8; 1172 tp->tty_winsize.ws_ypixel= scr_lines * font_lines; 1173 1174 return 0; 1175 } 1176 1177 #define LIMITINDEX(mask, start, size, ct) { \ 1178 int countlimit = size - start; \ 1179 start &= mask; \ 1180 if(ct > countlimit) ct = countlimit; \ 1181 } 1182 1183 /*===========================================================================* 1184 * mem_vid_copy * 1185 *===========================================================================*/ 1186 static void mem_vid_copy(vir_bytes src, int dst_index, int count) 1187 { 1188 u16_t *src_mem = (u16_t *) src; 1189 while(count > 0) { 1190 int i, subcount = count; 1191 u16_t *dst_mem; 1192 LIMITINDEX(vid_mask, dst_index, vid_size, subcount); 1193 dst_mem = (u16_t *) console_memory + dst_index; 1194 if(!src) 1195 for(i = 0; i < subcount; i++) 1196 *dst_mem++ = blank_color; 1197 else 1198 for(i = 0; i < subcount; i++) 1199 *dst_mem++ = *src_mem++; 1200 count -= subcount; 1201 dst_index += subcount; 1202 } 1203 } 1204 1205 /*===========================================================================* 1206 * vid_vid_copy * 1207 *===========================================================================*/ 1208 static void vid_vid_copy(int src_index, int dst_index, int count) 1209 { 1210 int backwards = 0; 1211 if(src_index < dst_index) 1212 backwards = 1; 1213 while(count > 0) { 1214 int i, subcount = count; 1215 u16_t *dst_mem, *src_mem; 1216 LIMITINDEX(vid_mask, src_index, vid_size, subcount); 1217 LIMITINDEX(vid_mask, dst_index, vid_size, subcount); 1218 src_mem = (u16_t *) console_memory + src_index; 1219 dst_mem = (u16_t *) console_memory + dst_index; 1220 if(backwards) { 1221 src_mem += subcount - 1; 1222 dst_mem += subcount - 1; 1223 for(i = 0; i < subcount; i++) 1224 *dst_mem-- = *src_mem--; 1225 } else { 1226 for(i = 0; i < subcount; i++) 1227 *dst_mem++ = *src_mem++; 1228 } 1229 count -= subcount; 1230 dst_index += subcount; 1231 src_index += subcount; 1232 } 1233 } 1234