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