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 "kernel.h"
  20: #include <termios.h>
  21: #include <minix/callnr.h>
  22: #include <minix/com.h>
  23: #include "protect.h"
  24: #include "tty.h"
  25: #include "proc.h"
  26: 
  27: /* Definitions used by the console driver. */
  28: #define MONO_BASE    0xB0000L   /* base of mono video memory */
  29: #define COLOR_BASE   0xB8000L   /* base of color video memory */
  30: #define MONO_SIZE     0x1000    /* 4K mono video memory */
  31: #define COLOR_SIZE    0x4000    /* 16K color video memory */
  32: #define EGA_SIZE      0x8000    /* EGA & VGA have at least 32K */
  33: #define BLANK_COLOR   0x0700    /* determines cursor color on blank screen */
  34: #define SCROLL_UP          0    /* scroll forward */
  35: #define SCROLL_DOWN        1    /* scroll backward */
  36: #define BLANK_MEM ((u16_t *) 0) /* tells mem_vid_copy() to blank the screen */
  37: #define CONS_RAM_WORDS    80    /* video ram buffer size */
  38: #define MAX_ESC_PARMS      4    /* number of escape sequence params allowed */
  39: 
  40: /* Constants relating to the controller chips. */
  41: #define M_6845         0x3B4    /* port for 6845 mono */
  42: #define C_6845         0x3D4    /* port for 6845 color */
  43: #define INDEX              0    /* 6845's index register */
  44: #define DATA               1    /* 6845's data register */
  45: #define VID_ORG           12    /* 6845's origin register */
  46: #define CURSOR            14    /* 6845's cursor register */
  47: 
  48: /* Beeper. */
  49: #define BEEP_FREQ     0x0533    /* value to put into timer to set beep freq */
  50: #define B_TIME             3    /* length of CTRL-G beep is ticks */
  51: 
  52: /* definitions used for font management */
  53: #define GA_SEQUENCER_INDEX      0x3C4
  54: #define GA_SEQUENCER_DATA       0x3C5
  55: #define GA_GRAPHICS_INDEX       0x3CE
  56: #define GA_GRAPHICS_DATA        0x3CF
  57: #define GA_VIDEO_ADDRESS        0xA0000L
  58: #define GA_FONT_SIZE            8192
  59: 
  60: /* Global variables used by the console driver and assembly support. */
  61: PUBLIC u16_t vid_seg;
  62: PUBLIC vir_bytes vid_off;       /* video ram is found at vid_seg:vid_off */
  63: PUBLIC unsigned vid_size;       /* 0x2000 for color or 0x0800 for mono */
  64: PUBLIC unsigned vid_mask;       /* 0x1FFF for color or 0x07FF for mono */
  65: PUBLIC unsigned blank_color = BLANK_COLOR; /* display code for blank */
  66: 
  67: /* Private variables used by the console driver. */
  68: PRIVATE int vid_port;           /* I/O port for accessing 6845 */
  69: PRIVATE int wrap;               /* hardware can wrap? */
  70: PRIVATE int softscroll;         /* 1 = software scrolling, 0 = hardware */
  71: PRIVATE int beeping;            /* speaker is beeping? */
  72: PRIVATE unsigned font_lines;    /* font lines per character */
  73: PRIVATE unsigned scr_width;     /* # characters on a line */
  74: PRIVATE unsigned scr_lines;     /* # lines on the screen */
  75: PRIVATE unsigned scr_size;      /* # characters on the screen */
  76: 
  77: /* Per console data. */
  78: typedef struct console {
  79:   tty_t *c_tty;                 /* associated TTY struct */
  80:   int c_column;                 /* current column number (0-origin) */
  81:   int c_row;                    /* current row (0 at top of screen) */
  82:   int c_rwords;                 /* number of WORDS (not bytes) in outqueue */
  83:   unsigned c_start;             /* start of video memory of this console */
  84:   unsigned c_limit;             /* limit of this console's video memory */
  85:   unsigned c_org;               /* location in RAM where 6845 base points */
  86:   unsigned c_cur;               /* current position of cursor in video RAM */
  87:   unsigned c_attr;              /* character attribute */
  88:   unsigned c_blank;             /* blank attribute */
  89:   char c_reverse;               /* reverse video */
  90:   char c_esc_state;             /* 0=normal, 1=ESC, 2=ESC[ */
  91:   char c_esc_intro;             /* Distinguishing character following ESC */
  92:   int *c_esc_parmp;             /* pointer to current escape parameter */
  93:   int c_esc_parmv[MAX_ESC_PARMS];       /* list of escape parameters */
  94:   u16_t c_ramqueue[CONS_RAM_WORDS];     /* buffer for video RAM */
  95: } console_t;
  96: 
  97: PRIVATE int nr_cons= 1;         /* actual number of consoles */
  98: PRIVATE console_t cons_table[NR_CONS];
  99: PRIVATE console_t *curcons;     /* currently visible */
 100: 
 101: /* Color if using a color controller. */
 102: #define color   (vid_port == C_6845)
 103: 
 104: /* Map from ANSI colors to the attributes used by the PC */
 105: PRIVATE int ansi_colors[8] = {0, 4, 2, 6, 1, 5, 3, 7};
 106: 
 107: /* Structure used for font management */
 108: struct sequence {
 109:         unsigned short index;
 110:         unsigned char port;
 111:         unsigned char value;
 112: };
 113: 
 114: FORWARD _PROTOTYPE( void cons_write, (struct tty *tp)                   );
 115: FORWARD _PROTOTYPE( void cons_echo, (tty_t *tp, int c)                  );
 116: FORWARD _PROTOTYPE( void out_char, (console_t *cons, int c)             );
 117: FORWARD _PROTOTYPE( void beep, (void)                                   );
 118: FORWARD _PROTOTYPE( void do_escape, (console_t *cons, int c)            );
 119: FORWARD _PROTOTYPE( void flush, (console_t *cons)                       );
 120: FORWARD _PROTOTYPE( void parse_escape, (console_t *cons, int c)         );
 121: FORWARD _PROTOTYPE( void scroll_screen, (console_t *cons, int dir)      );
 122: FORWARD _PROTOTYPE( void set_6845, (int reg, unsigned val)              );
 123: FORWARD _PROTOTYPE( void stop_beep, (timer_t *tmrp)                             );
 124: FORWARD _PROTOTYPE( void cons_org0, (void)                              );
 125: FORWARD _PROTOTYPE( void ga_program, (struct sequence *seq)             );
 126: FORWARD _PROTOTYPE( void cons_ioctl, (tty_t *tp)                        );
 127: 
 128: 
 129: /*===========================================================================*
 130:  *                              cons_write                                   *
 131:  *===========================================================================*/
 132: PRIVATE void cons_write(tp)
 133: register struct tty *tp;        /* tells which terminal is to be used */
 134: {
 135: /* Copy as much data as possible to the output queue, then start I/O.  On
 136:  * memory-mapped terminals, such as the IBM console, the I/O will also be
 137:  * finished, and the counts updated.  Keep repeating until all I/O done.
 138:  */
 139: 
 140:   int count;
 141:   register char *tbuf;
 142:   char buf[64];
 143:   phys_bytes user_phys;
 144:   console_t *cons = tp->tty_priv;
 145: 
 146:   /* Check quickly for nothing to do, so this can be called often without
 147:    * unmodular tests elsewhere.
 148:    */
 149:   if ((count = tp->tty_outleft) == 0 || tp->tty_inhibited) return;
 150: 
 151:   /* Copy the user bytes to buf[] for decent addressing. Loop over the
 152:    * copies, since the user buffer may be much larger than buf[].
 153:    */
 154:   do {
 155:         if (count > sizeof(buf)) count = sizeof(buf);
 156:         user_phys = proc_vir2phys(proc_addr(tp->tty_outproc), tp->tty_out_vir);
 157:         phys_copy(user_phys, vir2phys(buf), (phys_bytes) count);
 158:         tbuf = buf;
 159: 
 160:         /* Update terminal data structure. */
 161:         tp->tty_out_vir += count;
 162:         tp->tty_outcum += count;
 163:         tp->tty_outleft -= count;
 164: 
 165:         /* Output each byte of the copy to the screen.  Avoid calling
 166:          * out_char() for the "easy" characters, put them into the buffer
 167:          * directly.
 168:          */
 169:         do {
 170:                 if ((unsigned) *tbuf < ' ' || cons->c_esc_state > 0
 171:                         || cons->c_column >= scr_width
 172:                         || cons->c_rwords >= buflen(cons->c_ramqueue))
 173:                 {
 174:                         out_char(cons, *tbuf++);
 175:                 } else {
 176:                         cons->c_ramqueue[cons->c_rwords++] =
 177:                                         cons->c_attr | (*tbuf++ & BYTE);
 178:                         cons->c_column++;
 179:                 }
 180:         } while (--count != 0);
 181:   } while ((count = tp->tty_outleft) != 0 && !tp->tty_inhibited);
 182: 
 183:   flush(cons);                  /* transfer anything buffered to the screen */
 184: 
 185:   /* Reply to the writer if all output is finished. */
 186:   if (tp->tty_outleft == 0) {
 187:         tty_reply(tp->tty_outrepcode, tp->tty_outcaller, tp->tty_outproc,
 188:                                                         tp->tty_outcum);
 189:         tp->tty_outcum = 0;
 190:   }
 191: }
 192: 
 193: 
 194: /*===========================================================================*
 195:  *                              cons_echo                                    *
 196:  *===========================================================================*/
 197: PRIVATE void cons_echo(tp, c)
 198: register tty_t *tp;             /* pointer to tty struct */
 199: int c;                          /* character to be echoed */
 200: {
 201: /* Echo keyboard input (print & flush). */
 202:   console_t *cons = tp->tty_priv;
 203: 
 204:   out_char(cons, c);
 205:   flush(cons);
 206: }
 207: 
 208: 
 209: /*===========================================================================*
 210:  *                              out_char                                     *
 211:  *===========================================================================*/
 212: PRIVATE void out_char(cons, c)
 213: register console_t *cons;       /* pointer to console struct */
 214: int c;                          /* character to be output */
 215: {
 216: /* Output a character on the console.  Check for escape sequences first. */
 217:   if (cons->c_esc_state > 0) {
 218:         parse_escape(cons, c);
 219:         return;
 220:   }
 221: 
 222:   switch(c) {
 223:         case 000:               /* null is typically used for padding */
 224:                 return;         /* better not do anything */
 225: 
 226:         case 007:               /* ring the bell */
 227:                 flush(cons);    /* print any chars queued for output */
 228:                 beep();
 229:                 return;
 230: 
 231:         case '\b':              /* backspace */
 232:                 if (--cons->c_column < 0) {
 233:                         if (--cons->c_row >= 0) cons->c_column += scr_width;
 234:                 }
 235:                 flush(cons);
 236:                 return;
 237: 
 238:         case '\n':              /* line feed */
 239:                 if ((cons->c_tty->tty_termios.c_oflag & (OPOST|ONLCR))
 240:                                                 == (OPOST|ONLCR)) {
 241:                         cons->c_column = 0;
 242:                 }
 243:                 /*FALL THROUGH*/
 244:         case 013:               /* CTRL-K */
 245:         case 014:               /* CTRL-L */
 246:                 if (cons->c_row == scr_lines-1) {
 247:                         scroll_screen(cons, SCROLL_UP);
 248:                 } else {
 249:                         cons->c_row++;
 250:                 }
 251:                 flush(cons);
 252:                 return;
 253: 
 254:         case '\r':              /* carriage return */
 255:                 cons->c_column = 0;
 256:                 flush(cons);
 257:                 return;
 258: 
 259:         case '\t':              /* tab */
 260:                 cons->c_column = (cons->c_column + TAB_SIZE) & ~TAB_MASK;
 261:                 if (cons->c_column > scr_width) {
 262:                         cons->c_column -= scr_width;
 263:                         if (cons->c_row == scr_lines-1) {
 264:                                 scroll_screen(cons, SCROLL_UP);
 265:                         } else {
 266:                                 cons->c_row++;
 267:                         }
 268:                 }
 269:                 flush(cons);
 270:                 return;
 271: 
 272:         case 033:               /* ESC - start of an escape sequence */
 273:                 flush(cons);    /* print any chars queued for output */
 274:                 cons->c_esc_state = 1;   /* mark ESC as seen */
 275:                 return;
 276: 
 277:         default:                /* printable chars are stored in ramqueue */
 278:                 if (cons->c_column >= scr_width) {
 279:                         if (!LINEWRAP) return;
 280:                         if (cons->c_row == scr_lines-1) {
 281:                                 scroll_screen(cons, SCROLL_UP);
 282:                         } else {
 283:                                 cons->c_row++;
 284:                         }
 285:                         cons->c_column = 0;
 286:                         flush(cons);
 287:                 }
 288:                 if (cons->c_rwords == buflen(cons->c_ramqueue)) flush(cons);
 289:                 cons->c_ramqueue[cons->c_rwords++] = cons->c_attr | (c & BYTE);
 290:                 cons->c_column++;                        /* next column */
 291:                 return;
 292:   }
 293: }
 294: 
 295: 
 296: /*===========================================================================*
 297:  *                              scroll_screen                                *
 298:  *===========================================================================*/
 299: PRIVATE void scroll_screen(cons, dir)
 300: register console_t *cons;       /* pointer to console struct */
 301: int dir;                        /* SCROLL_UP or SCROLL_DOWN */
 302: {
 303:   unsigned new_line, new_org, chars;
 304: 
 305:   flush(cons);
 306:   chars = scr_size - scr_width;         /* one screen minus one line */
 307: 
 308:   /* Scrolling the screen is a real nuisance due to the various incompatible
 309:    * video cards.  This driver supports software scrolling (Hercules?),
 310:    * hardware scrolling (mono and CGA cards) and hardware scrolling without
 311:    * wrapping (EGA cards).  In the latter case we must make sure that
 312:    *            c_start <= c_org && c_org + scr_size <= c_limit
 313:    * holds, because EGA doesn't wrap around the end of video memory.
 314:    */
 315:   if (dir == SCROLL_UP) {
 316:         /* Scroll one line up in 3 ways: soft, avoid wrap, use origin. */
 317:         if (softscroll) {
 318:                 vid_vid_copy(cons->c_start + scr_width, cons->c_start, chars);
 319:         } else
 320:         if (!wrap && cons->c_org + scr_size + scr_width >= cons->c_limit) {
 321:                 vid_vid_copy(cons->c_org + scr_width, cons->c_start, chars);
 322:                 cons->c_org = cons->c_start;
 323:         } else {
 324:                 cons->c_org = (cons->c_org + scr_width) & vid_mask;
 325:         }
 326:         new_line = (cons->c_org + chars) & vid_mask;
 327:   } else {
 328:         /* Scroll one line down in 3 ways: soft, avoid wrap, use origin. */
 329:         if (softscroll) {
 330:                 vid_vid_copy(cons->c_start, cons->c_start + scr_width, chars);
 331:         } else
 332:         if (!wrap && cons->c_org < cons->c_start + scr_width) {
 333:                 new_org = cons->c_limit - scr_size;
 334:                 vid_vid_copy(cons->c_org, new_org + scr_width, chars);
 335:                 cons->c_org = new_org;
 336:         } else {
 337:                 cons->c_org = (cons->c_org - scr_width) & vid_mask;
 338:         }
 339:         new_line = cons->c_org;
 340:   }
 341:   /* Blank the new line at top or bottom. */
 342:   blank_color = cons->c_blank;
 343:   mem_vid_copy(BLANK_MEM, new_line, scr_width);
 344: 
 345:   /* Set the new video origin. */
 346:   if (cons == curcons) set_6845(VID_ORG, cons->c_org);
 347:   flush(cons);
 348: }
 349: 
 350: 
 351: /*===========================================================================*
 352:  *                              flush                                        *
 353:  *===========================================================================*/
 354: PRIVATE void flush(cons)
 355: register console_t *cons;       /* pointer to console struct */
 356: {
 357: /* Send characters buffered in 'ramqueue' to screen memory, check the new
 358:  * cursor position, compute the new hardware cursor position and set it.
 359:  */
 360:   unsigned cur;
 361:   tty_t *tp = cons->c_tty;
 362: 
 363:   /* Have the characters in 'ramqueue' transferred to the screen. */
 364:   if (cons->c_rwords > 0) {
 365:         mem_vid_copy(cons->c_ramqueue, cons->c_cur, cons->c_rwords);
 366:         cons->c_rwords = 0;
 367: 
 368:         /* TTY likes to know the current column and if echoing messed up. */
 369:         tp->tty_position = cons->c_column;
 370:         tp->tty_reprint = TRUE;
 371:   }
 372: 
 373:   /* Check and update the cursor position. */
 374:   if (cons->c_column < 0) cons->c_column = 0;
 375:   if (cons->c_column > scr_width) cons->c_column = scr_width;
 376:   if (cons->c_row < 0) cons->c_row = 0;
 377:   if (cons->c_row >= scr_lines) cons->c_row = scr_lines - 1;
 378:   cur = cons->c_org + cons->c_row * scr_width + cons->c_column;
 379:   if (cur != cons->c_cur) {
 380:         if (cons == curcons) set_6845(CURSOR, cur);
 381:         cons->c_cur = cur;
 382:   }
 383: }
 384: 
 385: 
 386: /*===========================================================================*
 387:  *                              parse_escape                                 *
 388:  *===========================================================================*/
 389: PRIVATE void parse_escape(cons, c)
 390: register console_t *cons;       /* pointer to console struct */
 391: char c;                         /* next character in escape sequence */
 392: {
 393: /* The following ANSI escape sequences are currently supported.
 394:  * If n and/or m are omitted, they default to 1.
 395:  *   ESC [nA moves up n lines
 396:  *   ESC [nB moves down n lines
 397:  *   ESC [nC moves right n spaces
 398:  *   ESC [nD moves left n spaces
 399:  *   ESC [m;nH" moves cursor to (m,n)
 400:  *   ESC [J clears screen from cursor
 401:  *   ESC [K clears line from cursor
 402:  *   ESC [nL inserts n lines ar cursor
 403:  *   ESC [nM deletes n lines at cursor
 404:  *   ESC [nP deletes n chars at cursor
 405:  *   ESC [n@ inserts n chars at cursor
 406:  *   ESC [nm enables rendition n (0=normal, 4=bold, 5=blinking, 7=reverse)
 407:  *   ESC M scrolls the screen backwards if the cursor is on the top line
 408:  */
 409: 
 410:   switch (cons->c_esc_state) {
 411:     case 1:                     /* ESC seen */
 412:         cons->c_esc_intro = '\0';
 413:         cons->c_esc_parmp = bufend(cons->c_esc_parmv);
 414:         do {
 415:                 *--cons->c_esc_parmp = 0;
 416:         } while (cons->c_esc_parmp > cons->c_esc_parmv);
 417:         switch (c) {
 418:             case '[':   /* Control Sequence Introducer */
 419:                 cons->c_esc_intro = c;
 420:                 cons->c_esc_state = 2;
 421:                 break;
 422:             case 'M':   /* Reverse Index */
 423:                 do_escape(cons, c);
 424:                 break;
 425:             default:
 426:                 cons->c_esc_state = 0;
 427:         }
 428:         break;
 429: 
 430:     case 2:                     /* ESC [ seen */
 431:         if (c >= '0' && c <= '9') {
 432:                 if (cons->c_esc_parmp < bufend(cons->c_esc_parmv))
 433:                         *cons->c_esc_parmp = *cons->c_esc_parmp * 10 + (c-'0');
 434:         } else
 435:         if (c == ';') {
 436:                 if (cons->c_esc_parmp < bufend(cons->c_esc_parmv))
 437:                         cons->c_esc_parmp++;
 438:         } else {
 439:                 do_escape(cons, c);
 440:         }
 441:         break;
 442:   }
 443: }
 444: 
 445: 
 446: /*===========================================================================*
 447:  *                              do_escape                                    *
 448:  *===========================================================================*/
 449: PRIVATE void do_escape(cons, c)
 450: register console_t *cons;       /* pointer to console struct */
 451: char c;                         /* next character in escape sequence */
 452: {
 453:   int value, n;
 454:   unsigned src, dst, count;
 455:   int *parmp;
 456: 
 457:   /* Some of these things hack on screen RAM, so it had better be up to date */
 458:   flush(cons);
 459: 
 460:   if (cons->c_esc_intro == '\0') {
 461:         /* Handle a sequence beginning with just ESC */
 462:         switch (c) {
 463:             case 'M':           /* Reverse Index */
 464:                 if (cons->c_row == 0) {
 465:                         scroll_screen(cons, SCROLL_DOWN);
 466:                 } else {
 467:                         cons->c_row--;
 468:                 }
 469:                 flush(cons);
 470:                 break;
 471: 
 472:             default: break;
 473:         }
 474:   } else
 475:   if (cons->c_esc_intro == '[') {
 476:         /* Handle a sequence beginning with ESC [ and parameters */
 477:         value = cons->c_esc_parmv[0];
 478:         switch (c) {
 479:             case 'A':           /* ESC [nA moves up n lines */
 480:                 n = (value == 0 ? 1 : value);
 481:                 cons->c_row -= n;
 482:                 flush(cons);
 483:                 break;
 484: 
 485:             case 'B':           /* ESC [nB moves down n lines */
 486:                 n = (value == 0 ? 1 : value);
 487:                 cons->c_row += n;
 488:                 flush(cons);
 489:                 break;
 490: 
 491:             case 'C':           /* ESC [nC moves right n spaces */
 492:                 n = (value == 0 ? 1 : value);
 493:                 cons->c_column += n;
 494:                 flush(cons);
 495:                 break;
 496: 
 497:             case 'D':           /* ESC [nD moves left n spaces */
 498:                 n = (value == 0 ? 1 : value);
 499:                 cons->c_column -= n;
 500:                 flush(cons);
 501:                 break;
 502: 
 503:             case 'H':           /* ESC [m;nH" moves cursor to (m,n) */
 504:                 cons->c_row = cons->c_esc_parmv[0] - 1;
 505:                 cons->c_column = cons->c_esc_parmv[1] - 1;
 506:                 flush(cons);
 507:                 break;
 508: 
 509:             case 'J':           /* ESC [sJ clears in display */
 510:                 switch (value) {
 511:                     case 0:     /* Clear from cursor to end of screen */
 512:                         count = scr_size - (cons->c_cur - cons->c_org);
 513:                         dst = cons->c_cur;
 514:                         break;
 515:                     case 1:     /* Clear from start of screen to cursor */
 516:                         count = cons->c_cur - cons->c_org;
 517:                         dst = cons->c_org;
 518:                         break;
 519:                     case 2:     /* Clear entire screen */
 520:                         count = scr_size;
 521:                         dst = cons->c_org;
 522:                         break;
 523:                     default:    /* Do nothing */
 524:                         count = 0;
 525:                         dst = cons->c_org;
 526:                 }
 527:                 blank_color = cons->c_blank;
 528:                 mem_vid_copy(BLANK_MEM, dst, count);
 529:                 break;
 530: 
 531:             case 'K':           /* ESC [sK clears line from cursor */
 532:                 switch (value) {
 533:                     case 0:     /* Clear from cursor to end of line */
 534:                         count = scr_width - cons->c_column;
 535:                         dst = cons->c_cur;
 536:                         break;
 537:                     case 1:     /* Clear from beginning of line to cursor */
 538:                         count = cons->c_column;
 539:                         dst = cons->c_cur - cons->c_column;
 540:                         break;
 541:                     case 2:     /* Clear entire line */
 542:                         count = scr_width;
 543:                         dst = cons->c_cur - cons->c_column;
 544:                         break;
 545:                     default:    /* Do nothing */
 546:                         count = 0;
 547:                         dst = cons->c_cur;
 548:                 }
 549:                 blank_color = cons->c_blank;
 550:                 mem_vid_copy(BLANK_MEM, dst, count);
 551:                 break;
 552: 
 553:             case 'L':           /* ESC [nL inserts n lines at cursor */
 554:                 n = value;
 555:                 if (n < 1) n = 1;
 556:                 if (n > (scr_lines - cons->c_row))
 557:                         n = scr_lines - cons->c_row;
 558: 
 559:                 src = cons->c_org + cons->c_row * scr_width;
 560:                 dst = src + n * scr_width;
 561:                 count = (scr_lines - cons->c_row - n) * scr_width;
 562:                 vid_vid_copy(src, dst, count);
 563:                 blank_color = cons->c_blank;
 564:                 mem_vid_copy(BLANK_MEM, src, n * scr_width);
 565:                 break;
 566: 
 567:             case 'M':           /* ESC [nM deletes n lines at cursor */
 568:                 n = value;
 569:                 if (n < 1) n = 1;
 570:                 if (n > (scr_lines - cons->c_row))
 571:                         n = scr_lines - cons->c_row;
 572: 
 573:                 dst = cons->c_org + cons->c_row * scr_width;
 574:                 src = dst + n * scr_width;
 575:                 count = (scr_lines - cons->c_row - n) * scr_width;
 576:                 vid_vid_copy(src, dst, count);
 577:                 blank_color = cons->c_blank;
 578:                 mem_vid_copy(BLANK_MEM, dst + count, n * scr_width);
 579:                 break;
 580: 
 581:             case '@':           /* ESC [n@ inserts n chars at cursor */
 582:                 n = value;
 583:                 if (n < 1) n = 1;
 584:                 if (n > (scr_width - cons->c_column))
 585:                         n = scr_width - cons->c_column;
 586: 
 587:                 src = cons->c_cur;
 588:                 dst = src + n;
 589:                 count = scr_width - cons->c_column - n;
 590:                 vid_vid_copy(src, dst, count);
 591:                 blank_color = cons->c_blank;
 592:                 mem_vid_copy(BLANK_MEM, src, n);
 593:                 break;
 594: 
 595:             case 'P':           /* ESC [nP deletes n chars at cursor */
 596:                 n = value;
 597:                 if (n < 1) n = 1;
 598:                 if (n > (scr_width - cons->c_column))
 599:                         n = scr_width - cons->c_column;
 600: 
 601:                 dst = cons->c_cur;
 602:                 src = dst + n;
 603:                 count = scr_width - cons->c_column - n;
 604:                 vid_vid_copy(src, dst, count);
 605:                 blank_color = cons->c_blank;
 606:                 mem_vid_copy(BLANK_MEM, dst + count, n);
 607:                 break;
 608: 
 609:             case 'm':           /* ESC [nm enables rendition n */
 610:                 for (parmp = cons->c_esc_parmv; parmp <= cons->c_esc_parmp
 611:                                 && parmp < bufend(cons->c_esc_parmv); parmp++) {
 612:                         if (cons->c_reverse) {
 613:                                 /* Unswap fg and bg colors */
 614:                                 cons->c_attr =   ((cons->c_attr & 0x7000) >> 4) |
 615:                                                 ((cons->c_attr & 0x0700) << 4) |
 616:                                                 ((cons->c_attr & 0x8800));
 617:                         }
 618:                         switch (n = *parmp) {
 619:                             case 0:     /* NORMAL */
 620:                                 cons->c_attr = cons->c_blank = BLANK_COLOR;
 621:                                 cons->c_reverse = FALSE;
 622:                                 break;
 623: 
 624:                             case 1:     /* BOLD  */
 625:                                 /* Set intensity bit */
 626:                                 cons->c_attr |= 0x0800;
 627:                                 break;
 628: 
 629:                             case 4:     /* UNDERLINE */
 630:                                 if (color) {
 631:                                         /* Change white to cyan, i.e. lose red
 632:                                          */
 633:                                         cons->c_attr = (cons->c_attr & 0xBBFF);
 634:                                 } else {
 635:                                         /* Set underline attribute */
 636:                                         cons->c_attr = (cons->c_attr & 0x99FF);
 637:                                 }
 638:                                 break;
 639: 
 640:                             case 5:     /* BLINKING */
 641:                                 /* Set the blink bit */
 642:                                 cons->c_attr |= 0x8000;
 643:                                 break;
 644: 
 645:                             case 7:     /* REVERSE */
 646:                                 cons->c_reverse = TRUE;
 647:                                 break;
 648: 
 649:                             default:    /* COLOR */
 650:                                 if (n == 39) n = 37;    /* set default color */
 651:                                 if (n == 49) n = 40;
 652: 
 653:                                 if (!color) {
 654:                                         /* Don't mess up a monochrome screen */
 655:                                 } else
 656:                                 if (30 <= n && n <= 37) {
 657:                                         /* Foreground color */
 658:                                         cons->c_attr =
 659:                                                 (cons->c_attr & 0xF8FF) |
 660:                                                 (ansi_colors[(n - 30)] << 8);
 661:                                         cons->c_blank =
 662:                                                 (cons->c_blank & 0xF8FF) |
 663:                                                 (ansi_colors[(n - 30)] << 8);
 664:                                 } else
 665:                                 if (40 <= n && n <= 47) {
 666:                                         /* Background color */
 667:                                         cons->c_attr =
 668:                                                 (cons->c_attr & 0x8FFF) |
 669:                                                 (ansi_colors[(n - 40)] << 12);
 670:                                         cons->c_blank =
 671:                                                 (cons->c_blank & 0x8FFF) |
 672:                                                 (ansi_colors[(n - 40)] << 12);
 673:                                 }
 674:                         }
 675:                         if (cons->c_reverse) {
 676:                                 /* Swap fg and bg colors */
 677:                                 cons->c_attr =   ((cons->c_attr & 0x7000) >> 4) |
 678:                                                 ((cons->c_attr & 0x0700) << 4) |
 679:                                                 ((cons->c_attr & 0x8800));
 680:                         }
 681:                 }
 682:                 break;
 683:         }
 684:   }
 685:   cons->c_esc_state = 0;
 686: }
 687: 
 688: 
 689: /*===========================================================================*
 690:  *                              set_6845                                     *
 691:  *===========================================================================*/
 692: PRIVATE void set_6845(reg, val)
 693: int reg;                        /* which register pair to set */
 694: unsigned val;                   /* 16-bit value to set it to */
 695: {
 696: /* Set a register pair inside the 6845.
 697:  * Registers 12-13 tell the 6845 where in video ram to start
 698:  * Registers 14-15 tell the 6845 where to put the cursor
 699:  */
 700:   lock();                       /* try to stop h/w loading in-between value */
 701:   outb(vid_port + INDEX, reg);                  /* set the index register */
 702:   outb(vid_port + DATA, (val>>8) & BYTE);  /* output high byte */
 703:   outb(vid_port + INDEX, reg + 1);              /* again */
 704:   outb(vid_port + DATA, val&BYTE);               /* output low byte */
 705:   unlock();
 706: }
 707: 
 708: 
 709: /*===========================================================================*
 710:  *                              beep                                         *
 711:  *===========================================================================*/
 712: PRIVATE void beep()
 713: {
 714: /* Making a beeping sound on the speaker (output for CRTL-G).
 715:  * This routine works by turning on the bits 0 and 1 in port B of the 8255
 716:  * chip that drive the speaker.
 717:  */
 718: 
 719:   static timer_t tmr_stop_beep;
 720: 
 721:   if (!beeping) {
 722:         outb(TIMER_MODE, 0xB6);         /* timer channel 2, square wave */
 723:         outb(TIMER2, (BEEP_FREQ >> 0) & BYTE);     /* low freq byte */
 724:         outb(TIMER2, (BEEP_FREQ >> 8) & BYTE);     /* high freq byte */
 725:         lock();                 /* guard PORT_B from keyboard intr handler */
 726:         outb(PORT_B, inb(PORT_B) | 3);          /* turn on beep bits */
 727:         unlock();
 728:         beeping = TRUE;
 729:   }
 730:   tmr_settimer(&tmr_stop_beep, TTY, get_uptime()+B_TIME, stop_beep);
 731: }
 732: 
 733: 
 734: /*===========================================================================*
 735:  *                              stop_beep                                    *
 736:  *===========================================================================*/
 737: PRIVATE void stop_beep(tmrp)
 738: timer_t *tmrp;
 739: {
 740: /* Turn off the beeper by turning off bits 0 and 1 in PORT_B. */
 741: 
 742:   lock();                       /* guard PORT_B from keyboard intr handler */
 743:   outb(PORT_B, inb(PORT_B) & ~3);
 744:   beeping = FALSE;
 745:   unlock();
 746: }
 747: 
 748: 
 749: /*===========================================================================*
 750:  *                              scr_init                                     *
 751:  *===========================================================================*/
 752: PUBLIC void scr_init(tp)
 753: tty_t *tp;
 754: {
 755: /* Initialize the screen driver. */
 756:   console_t *cons;
 757:   phys_bytes vid_base;
 758:   u16_t bios_columns, bios_crtbase, bios_fontlines;
 759:   u8_t bios_rows;
 760:   int line;
 761:   unsigned page_size;
 762: 
 763:   /* Associate console and TTY. */
 764:   line = tp - &tty_table[0];
 765:   if (line >= nr_cons) return;
 766:   cons = &cons_table[line];
 767:   cons->c_tty = tp;
 768:   tp->tty_priv = cons;
 769: 
 770:   /* Initialize the keyboard driver. */
 771:   kb_init(tp);
 772: 
 773:   /* Fill in TTY function hooks. */
 774:   tp->tty_devwrite = cons_write;
 775:   tp->tty_echo = cons_echo;
 776:   tp->tty_ioctl = cons_ioctl;
 777: 
 778:   /* Get the BIOS parameters that describe the VDU. */
 779:   phys_copy(0x44AL, vir2phys(&bios_columns), 2L);
 780:   phys_copy(0x463L, vir2phys(&bios_crtbase), 2L);
 781:   phys_copy(0x484L, vir2phys(&bios_rows), 1L);
 782:   phys_copy(0x485L, vir2phys(&bios_fontlines), 2L);
 783: 
 784:   vid_port = bios_crtbase;
 785:   scr_width = bios_columns;
 786:   font_lines = bios_fontlines;
 787:   scr_lines = ega ? bios_rows+1 : 25;
 788: 
 789:   if (color) {
 790:         vid_base = COLOR_BASE;
 791:         vid_size = COLOR_SIZE;
 792:   } else {
 793:         vid_base = MONO_BASE;
 794:         vid_size = MONO_SIZE;
 795:   }
 796:   if (ega) vid_size = EGA_SIZE;
 797:   wrap = !ega;
 798: 
 799:   phys2seg(&vid_seg, &vid_off, vid_base);
 800: 
 801:   vid_size >>= 1;         /* word count */
 802:   vid_mask = vid_size - 1;
 803: 
 804:   /* Size of the screen (number of displayed characters.) */
 805:   scr_size = scr_lines * scr_width;
 806: 
 807:   /* There can be as many consoles as video memory allows. */
 808:   nr_cons = vid_size / scr_size;
 809:   if (nr_cons > NR_CONS) nr_cons = NR_CONS;
 810:   if (nr_cons > 1) wrap = 0;
 811:   page_size = vid_size / nr_cons;
 812:   cons->c_start = line * page_size;
 813:   cons->c_limit = cons->c_start + page_size;
 814:   cons->c_cur = cons->c_org = cons->c_start;
 815:   cons->c_attr = cons->c_blank = BLANK_COLOR;
 816: 
 817:   /* Clear the screen. */
 818:   blank_color = BLANK_COLOR;
 819:   mem_vid_copy(BLANK_MEM, cons->c_start, scr_size);
 820:   select_console(0);
 821:   cons_ioctl(tp);
 822: }
 823: 
 824: 
 825: /*===========================================================================*
 826:  *                              putk                                         *
 827:  *===========================================================================*/
 828: PUBLIC void putk(c)
 829: int c;                          /* character to print */
 830: {
 831: /* This procedure is used by the version of printf() that is linked with
 832:  * the kernel itself.  The one in the library sends a message to FS, which is
 833:  * not what is needed for printing within the kernel.  This version just queues
 834:  * the character and starts the output.
 835:  */
 836: 
 837:   if (c != 0) {
 838:         if (c == '\n') putk('\r');
 839:         out_char(&cons_table[0], (int) c);
 840:   } else {
 841:         flush(&cons_table[0]);
 842:   }
 843: }
 844: 
 845: 
 846: /*===========================================================================*
 847:  *                              toggle_scroll                                *
 848:  *===========================================================================*/
 849: PUBLIC void toggle_scroll()
 850: {
 851: /* Toggle between hardware and software scroll. */
 852: 
 853:   cons_org0();
 854:   softscroll = !softscroll;
 855:   printf("%sware scrolling enabled.\n", softscroll ? "Soft" : "Hard");
 856: }
 857: 
 858: 
 859: /*===========================================================================*
 860:  *                              cons_stop                                    *
 861:  *===========================================================================*/
 862: PUBLIC void cons_stop()
 863: {
 864: /* Prepare for halt or reboot. */
 865:   cons_org0();
 866:   softscroll = 1;
 867:   select_console(0);
 868:   cons_table[0].c_attr = cons_table[0].c_blank = BLANK_COLOR;
 869: }
 870: 
 871: 
 872: /*===========================================================================*
 873:  *                              cons_org0                                    *
 874:  *===========================================================================*/
 875: PRIVATE void cons_org0()
 876: {
 877: /* Scroll video memory back to put the origin at 0. */
 878:   int cons_line;
 879:   console_t *cons;
 880:   unsigned n;
 881: 
 882:   for (cons_line = 0; cons_line < nr_cons; cons_line++) {
 883:         cons = &cons_table[cons_line];
 884:         while (cons->c_org > cons->c_start) {
 885:                 n = vid_size - scr_size;        /* amount of unused memory */
 886:                 if (n > cons->c_org - cons->c_start)
 887:                         n = cons->c_org - cons->c_start;
 888:                 vid_vid_copy(cons->c_org, cons->c_org - n, scr_size);
 889:                 cons->c_org -= n;
 890:         }
 891:         flush(cons);
 892:   }
 893:   select_console(current);
 894: }
 895: 
 896: 
 897: /*===========================================================================*
 898:  *                              select_console                               *
 899:  *===========================================================================*/
 900: PUBLIC void select_console(int cons_line)
 901: {
 902: /* Set the current console to console number 'cons_line'. */
 903: 
 904:   if (cons_line < 0 || cons_line >= nr_cons) return;
 905:   current = cons_line;
 906:   curcons = &cons_table[cons_line];
 907:   set_6845(VID_ORG, curcons->c_org);
 908:   set_6845(CURSOR, curcons->c_cur);
 909: }
 910: 
 911: 
 912: /*===========================================================================*
 913:  *                              con_loadfont                                 *
 914:  *===========================================================================*/
 915: PUBLIC int con_loadfont(user_phys)
 916: phys_bytes user_phys;
 917: {
 918: /* Load a font into the EGA or VGA adapter. */
 919:   static struct sequence seq1[7] = {
 920:         { GA_SEQUENCER_INDEX, 0x00, 0x01 },
 921:         { GA_SEQUENCER_INDEX, 0x02, 0x04 },
 922:         { GA_SEQUENCER_INDEX, 0x04, 0x07 },
 923:         { GA_SEQUENCER_INDEX, 0x00, 0x03 },
 924:         { GA_GRAPHICS_INDEX, 0x04, 0x02 },
 925:         { GA_GRAPHICS_INDEX, 0x05, 0x00 },
 926:         { GA_GRAPHICS_INDEX, 0x06, 0x00 },
 927:   };
 928:   static struct sequence seq2[7] = {
 929:         { GA_SEQUENCER_INDEX, 0x00, 0x01 },
 930:         { GA_SEQUENCER_INDEX, 0x02, 0x03 },
 931:         { GA_SEQUENCER_INDEX, 0x04, 0x03 },
 932:         { GA_SEQUENCER_INDEX, 0x00, 0x03 },
 933:         { GA_GRAPHICS_INDEX, 0x04, 0x00 },
 934:         { GA_GRAPHICS_INDEX, 0x05, 0x10 },
 935:         { GA_GRAPHICS_INDEX, 0x06,    0 },
 936:   };
 937: 
 938:   seq2[6].value= color ? 0x0E : 0x0A;
 939: 
 940:   if (!ega) return(ENOTTY);
 941: 
 942:   lock();
 943:   ga_program(seq1);     /* bring font memory into view */
 944: 
 945:   phys_copy(user_phys, (phys_bytes)GA_VIDEO_ADDRESS, (phys_bytes)GA_FONT_SIZE);
 946: 
 947:   ga_program(seq2);     /* restore */
 948:   unlock();
 949: 
 950:   return(OK);
 951: }
 952: 
 953: 
 954: /*===========================================================================*
 955:  *                              ga_program                                   *
 956:  *===========================================================================*/
 957: PRIVATE void ga_program(seq)
 958: struct sequence *seq;
 959: {
 960:   int len= 7;
 961:   do {
 962:         outb(seq->index, seq->port);
 963:         outb(seq->index+1, seq->value);
 964:         seq++;
 965:   } while (--len > 0);
 966: }
 967: 
 968: 
 969: /*===========================================================================*
 970:  *                              cons_ioctl                                   *
 971:  *===========================================================================*/
 972: PRIVATE void cons_ioctl(tp)
 973: tty_t *tp;
 974: {
 975: /* Set the screen dimensions. */
 976: 
 977:   tp->tty_winsize.ws_row= scr_lines;
 978:   tp->tty_winsize.ws_col= scr_width;
 979:   tp->tty_winsize.ws_xpixel= scr_width * 8;
 980:   tp->tty_winsize.ws_ypixel= scr_lines * font_lines;
 981: }