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