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